Referencing a Target Enemy's Attributes / Children (w/ Inherited Object Type) During Damage Calculation

First, some context: I am new to combat, so my decisions might seem a little wacky. I created the game attribute truerandom when I needed a crutch random value. This is being used to determine a player's chance of a critical hit and a player's variance in damage. It also contains the attribute combat with all of the options for combat, which are going to be Normal, Weapon, Ability, and Spell. I'd like to get through Normal attacks before I tackle the rest.

I created an object type for all enemies because I want to set the foundation for all later enemies, for which there is going to be a system in place to randomize encounters. All enemies have the attribute damageset which is a value that my script will edit while calculating: the player's STR (and, later, weapon) modifiers, whether or not the hit an enemy receives is critical, the player's variance in damage, and the damage reduced by the armor an enemy is wearing. The player's attribute STR determines their damage output, and is added to the "damageset," whereas the armor rating of the target's clothing is subtracted from the "damageset" value. At the end of all the calculations, the enemy's health will be subtracted by their modified "damageset" value.

You can see all this at work below. As you can tell, referencing an enemy's attribute is becoming a challenge for me because it is an object type. I made the following code the result of the verb Attack on an enemy:

ShowMenu ("<b>Attack <font color=#6b3ed6>" + this.alias + "</font></b> with:</b>", game.combat, true) {
  if (result = "Normal") {
    game.truerandom = GetRandomInt(1,30)
    if (game.truerandom = 30) {
      msg ("It's a <b><font color =\"darkred\">CRITICAL</font></b> hit!")
      game.truerandom = GetRandomInt(-2,2)
      this.damageset = player.STR + game.truerandom
      foreach (p, GetDirectChildren(this)) {
        if (GetBoolean(p, "worn")) {
          this.damageset = this.damageset - ( p.armor / 2 )
        }
      }
      this.health = this.health - this.damageset * 2
      if (this.damageset = 0) {
        msg (this.alias + " takes no damage!")
      }
      else {
        msg (this.alias + " takes " + this.damageset * 2 + " damage!")
      }
    }
    else {
      game.truerandom = GetRandomInt(-2,2)
      this.damageset = player.STR + game.truerandom
      foreach (p, GetDirectChildren(this)) {
        if (GetBoolean(p, "worn")) {
          this.damageset = this.damageset - ( p.armor / 2 )
          if (this.damageset < 0) {
            this.damageset = 0
          }
        }
      }
      this.health = this.health - this.damageset
      if (this.damageset = 0) {
        msg (this.alias + " takes no damage!")
      }
      else {
        msg (this.alias + " takes " + this.damageset + " damage!")
      }
    }
    msg (this.alias + " has <font color=#cc3300>" + this.health + "</font> HP.")
  }

The error I am running into is a simple one: Error running script: Error compiling expression 'this': Unknown object or variable 'this'

I know the problem is with using this (because the CRITICAL text appears before the error message), but I'm unsure of what else to use to reference the children target that the player selected to attack. I anticipated that, since my code is inherited by all enemies as a verb, using this to refer to them would make sense, thus calling their damageset attribute. I also understand that I'm going to need a better way to get the children of that enemy than GetDirectChildren(this), for similar reasons.

Still learning the language! Thanks in advance!


mrangel can get into much more accurate detail, but the simpliest fix-method is simply to store 'this' (which is storing the direct parent Object of the scripting) within an Attribute (as Attributes bypass all of these scope issues/problems, lol), an example:

<game name="example_game">

  <attr name="start" type="script">

    invoke (example_object_1.example_script_attribute)
    invoke (example_object_2.example_script_attribute)

  </attr>

</game>

<object name="unknown_object">
</object>

<object name="data_object">

  <attr name="example_object_attribute" type="object">unknown_object</attr>

</object>

<object name="example_object_1">

  <inherit name="example_type" />

  <attr name="alias" type="string">rectangle</attr>

</object>

<object name="example_object_2">

  <inherit name="example_type" />

  <attr name="alias" type="string">triangle</attr>

</object>

<type name="example_type">

  <attr name="example_script_attribute" type="script">

    data_object.example_object_attribute = this

    show menu ("blah", Split ("blah;blah;blah", ";"), false) {
      msg (data_object.example_object_attribute.name)
      msg (data_object.example_object_attribute.alias)
    }

  </attr>

</type>

// ----------------------------

output:

example_object_1
rectangle
example_object_2
triangle

(filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)


however... you don't want every single (err multiple: however many objects you give the object type to) Object(s) to have this combat script... which is what you're doing by putting it into an Object Type....

rather you should just have a single combat script belonging within a single Function or within a single Object's Script Attribute + Delegate, and pass in the Objects and whatever data you need into it... no reason for redundant combat code held within every object, you only need one instance of the combat code, and provide it with the needed data (ie: for this battle, the 'orc' monster, for another battle, the 'dragon' monster).


for example...


using delegates + Object's Script Attributes:

<delegate name="combat_delegate" parameters="enemy_object_parameter" />

<game name="example_game">

  <attr name="start" type="script">

    rundelegate (combat_object, "combat_script_attribute", orc)

    on ready {

      player.life = 999 // to restore your life for the dragon battle below, lol

      rundelegate (combat_object, "combat_script_attribute", dragon)

    }

  </attr>

</game>

<object name="room">
</object>

<object name="player">

  <attr name="parent" type="object">room</attr>

  <attr name="life" type="int">999</attr>

</object>

<object name="orc">

  <attr name="parent" type="object">room</attr>

  <attr name="damage" type="int">10</attr>

</object>

<object name="dragon">

  <attr name="parent" type="object">room</attr>

  <attr name="damage" type="int">100</attr>

</object>

<object name="combat_object">

  <attr name="combat_script_attribute" type="combat_delegate">

    <![CDATA[

      if (player.life > 0) {
        player.life = player.life - enemy_object_parameter.damage
        msg (player.life)
        rundelegate (combat_object, "combat_script_attribute", enemy_object_parameter)
      } else {
        msg ("You were killed by the " + enemy_object_parameter.name + "!")
      }

    ]]>

  </attr>

</object>

using Functions:

<game name="example_game">

  <attr name="start" type="script">

    combat_function (orc)

    on ready {

      player.life = 999 // to restore your life for the dragon battle below, lol

      combat_function (dragon)

    }

  </attr>

</game>

<object name="room">
</object>

<object name="player">

  <attr name="parent" type="object">room</attr>

  <attr name="life" type="int">999</attr>

</object>

<object name="orc">

  <attr name="parent" type="object">room</attr>

  <attr name="damage" type="int">10</attr>

</object>

<object name="dragon">

  <attr name="parent" type="object">room</attr>

  <attr name="damage" type="int">100</attr>

</object>

<function name="combat_function" parameters="enemy_object_parameter">

  <![CDATA[

    if (player.life > 0) {
      player.life = player.life - enemy_object_parameter.damage
      msg (player.life)
      combat_function (enemy_object_parameter)
    } else {
      msg ("You were killed by the " + enemy_object_parameter.name + "!")
    }

  ]]>

</function>

That's awesome. I understand your point about redundancy so I'm fixing all that now. I am using functions.

I wasn't aware attributes could be used like that because I thought it might cause some kind of read error (namely that data_object.example_object_attribute.name ~ data_object.example_object_attribute.alias tidbit).

Update: all is going well so far after implementing those changes. I managed to bypass the scope issues caused by ShowMenu in my function by setting the variable to the targeted enemy before invoking the menu, then calling another function with all the code depending on the player's decision. Thanks for all the recommendations!


In case it makes it easier to understand why the scope issues exist, try thinking of ShowMenu as a function to "create some links that will run this script when clicked". The links are displayed, the function that called ShowMenu ends, and all of that function's variables disappear. All that remains is a list of links on the player's screen, and a script that will be run when they're clicked. So that script can't use any variables except result, and any variables created within that script.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums