How to target the first enemy in a room?

For reference, previous version of enemy targeter (Which can only target the last enemy in a room)
https://textadventures.co.uk/forum/quest/topic/dpwwos2no0i6wg_cji0yig/how-to-use-timer-to-detect-the-last-enemys-attribute-in-the-room

As usual, I appreciate both coding solutions and little-coding creative walkaround solutions because I am not that good at coding

This is for a future game, if you see this topic too late but still have answers, please do answer


pseudo code (untested)
on enter all rooms script
and on combat teammates' deaths
and on enemies death

placeorder = 1
foreach (enemy, GetDirectChildren (game.pov.parent)) {
  if (HasAttribute(enemy, "enemy")) {
    enemy.position = placeorder
    placeorder = placeorder+1
  }
}

on every 1 turn in turn script (player uses lightning blade)

foreach (enemy, GetDirectChildren (game.pov.parent)) {
  if (HasAttribute(enemy, "enemy")) {
    if (enemy.position = 1) {
      enemy.hp = enemy.hp - player.atk
    }
  }
}

on every 1 turn in turn script (player uses laser beam (area of effect))

foreach (enemy, GetDirectChildren (game.pov.parent)) {
  if (HasAttribute(enemy, "enemy")) {
    if (enemy.position <= 3 ) {
      enemy.hp = enemy.hp - player.atk
    }
  }
}

The problem with my pseudo code is my code tends to be messy and it always turns out that there are simpler and shorter codes to directly target the enemy's position
Also, the game gets more and more complex

  1. The player may find combat teammates which follows the player everywhere until the player click 'say goodbye' verb, this means the enemy will need to target the player's and combat teammates' positions as well
  2. There may be a strategic button to change player's team formation, same for the enemy, for example, player by default is always position 1, but what if you hire a paladin, you would want to him to be blocking most of the enemies' attacks right, so it would be better if he is at position 1, how do I do this stragetic button?
  3. It is very messy to equip weapons and armour to each combat teammates, so what I would be doing is probably to level them up to a maximum of level 10, so at different levels, they can possibly become powerful aoe attacker which needs to be protected, or they can become healer and can be placed anywhere
  4. Powerful game reversals may happen: Player resurrects one of his teammate, this changes the positions once again. Enemy calls in reinforcement, this adds more position number which needs to be targeted

I am sure my pseudo code can solve all of these issues, but imagine, how much code I need to write compared to a proper coding that directly targets the enemy's position or "placement order in a room"

====================================================================

Congratulations! you have arrived at question 2,
your prize reward is a digital hug

As this game is going to be more complex and huger than even before, there are many concerns on how the fighting, travelling rooms, solving puzzles and buying shop items should work out

As you can see the combat have been changed into turn scripts from using of timers in the previous game,
but is every 1 turn script or auto combat too fast for the player?
If you had tried my previous game, you will know that the enemies can chase you to any rooms you enter,
so what if the player is just walking back to a marketplace to buy items, what if the player had already defeated enemies in a room which respawns annoyingly and that the player only wants to solve the puzzle in the room

As far as I know, the way turn scripts works, is no matter what button the player presses, the the turn script will fire, so if there are enemies in the room, players are going to find it especially difficult to just purchase shop items or solve dungeon puzzles

How do I set the combat turn script not to fire when player is not interested in combat or is sneaking pass overleveled enemies?
How do create a verb button "auto battle" that sets the combat turn script to automatic without the player requiring to press any button? (This is for highly confident player that have powerful stats and is just exploring around for full completion of maps)

==================================================================

Part 3 - Game's description

This is just an explanation on why the game have combat teammates
different combat teammates can deal different elemental attacks or provided elemental resistance buffs to player and other teammates
This makes the game more interesting as the player now not only have to collect spells and weapons, he will need to find and swap teammates for different dungeons with different elemental enemies

Because in our previous game, after many playtests, we know that there are either too powerful or too weak players because of how well they play and understand the game, especially in the level up stats, this creates a problem whereby the weak players lose to most enemies and the strong players kills everything like boring rats

=============================================

question 4

Elemental attacks and combat teammates might be akin to jrpg,
this already makes the game coding a huge volcano,
which is why I am skipping parrying,
but if you have a simple idea on introducing back my timer parrying into turn script parrying,
I am all ears for it

====================================================

question 5

previously, our game is yolo, yes, the player have only one life,
I am intending to switch back to "on player's death, respawn at room1",
so how should I handle the combat teammates' deaths?
Respawning at room1 sounds weird, respawning at their original found location is too much trouble to code and for the player refind his teammates

Also, if players and combat teammates can keep respawning, this makes killing enemies a breeze,
we can't reset the enemies' hp to max whenever the player enters a room, because it is possible that the player is not attacking an enemy dungeon room but rather, it happens because the enemies are chasing the player, therefore the enemies should not restore to max hp on all occassions (on enter all rooms script)

How should we balance this small wall cracking issue which might bring down the whole house?


I am sure my pseudo code can solve all of these issues, but imagine, how much code I need to write compared to a proper coding that directly targets the enemy's position or "placement order in a room"

You can directly put your pseudo code into the start of your combat turn script rather than
everywhere: (on enter all rooms script, and on combat teammates' deaths, on enemies death, on Player resurrects one of his teammate, (this changes the positions once again), on enemy calls in reinforcement (this adds more position number which needs to be targeted)

placeorder = 1
foreach (enemy, GetDirectChildren (game.pov.parent)) {
  if (HasAttribute(enemy, "enemy")) {
    enemy.position = placeorder
    placeorder = placeorder+1
  }
}

Because your combat turn script fires every turn, it will automatically ensure the enemy's position is always updated,
of course, there will be attack miss cases like enemy calls in reinforcement because their position wasn't updated yet, but usually these attack miss cases usually lasts for one turn only

============================================================================

  1. There may be a strategic button to change player's team formation, same for the enemy, for example, player by default is always position 1, but what if you hire a paladin, you would want to him to be blocking most of the enemies' attacks right, so it would be better if he is at position 1, how do I do this stragetic button?
  2. It is very messy to equip weapons and armour to each combat teammates, so what I would be doing is probably to level them up to a maximum of level 10, so at different levels, they can possibly become powerful aoe attacker which needs to be protected, or they can become healer and can be placed anywhere

The code should be similar to the enemy's place order function

placeorder = 1
foreach (ally, GetDirectChildren (game.pov)) {
  if (HasAttribute(ally, "ally")) {
    ally.position = placeorder
    placeorder = placeorder+1
  }
}

This function automatically sets the combat teammates according to how the player drop and take these allies, so yes, rather than have the allies follow you, they now sits in your inventory instead

So what about the order of the player's position?
Simple, you simply create a pseudo "player" object, perhaps call it Isaac, then the player can choose to drop Isaac and take him to set himself into the correct fighting position

======================================================================

How do create a verb button "auto battle" that sets the combat turn script to automatic without the player requiring to press any button? (This is for highly confident player that have powerful stats and is just exploring around for full completion of maps)

for (attacktimes, 1, player.battlespeed, 1) {
  combatfunction
}

This code is very simple, the player can adjust his battlespeed from 1 to 10,
if he sets it to 5, it means every turnscript fire, the player and enemies attack a total of 5 times

=================================================================

As far as I know, the way turn scripts works, is no matter what button the player presses, the the turn script will fire, so if there are enemies in the room, players are going to find it especially difficult to just purchase shop items or solve dungeon puzzles

Although we can simply allow battlespeed to 0 to stop combat every turn script fire, the game wouldn't feel anxious and scary, enemies chasing you but can't attack you is just plain boring

Therefore we modify the enemy chase function by simply adding one line "enemy.parent.peace"
Basically in rooms where you dun want battles to occur, simply add an attribute to that room named "peace"

Even though the combat turn scripts still fire, since the enemies can't chase you into peaceful zones, you won't be attacked anymore

foreach (enemy, AllObjects()) {
  if (HasAttribute(enemy, "enemy") and HasAttribute(enemy, "aggro")) {
    if (enemy.aggro=true and enemy.hp>0 and enemy.parent.peace) {
      if (RandomChance(50)) {
        msg (enemy.alias1+ " catched up to you")
        MoveObject (enemy, game.pov.parent)
        if (RandomChance(50)) {
          msg (enemy.alias1+ " strikes you for "+enemy.atk+" damage")
          player.hp = player.hp-(enemy.atk)
        }
      }
      else {
        enemy.aggro = false
      }
    }
  }
}

Because in our previous game, after many playtests, we know that there are either too powerful or too weak players because of how well they play and understand the game, especially in the level up stats, this creates a problem whereby the weak players lose to most enemies and the strong players kills everything like boring rats

Besides adding elemental attacks and resistance which are newbie friendly tactics, we can also suppress the power of all stats, when the stat reaches a too high number, its effectiveness starts to drop sharply and becomes exponentially useless, forcing the players to invest in other stats, this stops any stat from becoming overpowered while making all varieties of player builds into more similar and predictable level of power

=============================================================

but if you have a simple idea on introducing back my timer parrying into turn script parrying,
I am all ears for it

Previously in timer parrying, you can see the enemy's attack before he does that attack, in our new game, it will of course not happen due to how much coding that had taken, so instead, the player will have to fight with the enemies a few rounds and see whether the enemies are mostly a physical melee attacker, if so, the player can swap attack to parrying to block all those attacks receiving 0 damage and after a set amount of times deal a heavy parry atk damage to the parried enemies

Obviously this looks too overpowered, so the revelation is that when the player is in parry mode, he receives tripled damage from enemy's attacks that are not physical melee damage
And to make things more tricky, all enemies with physical melee attacks will have at least a 25% to do non-physical melee attacks like fireball spell
So even if the player is very informed of the enemies attack patterns, there is still a 25% of parry failure leading to taking in tripled incoming enemy damage

===================================================

so how should I handle the combat teammates' deaths?
Respawning at room1 sounds weird, respawning at their original found location is too much trouble to code and for the player refind his teammates

When the combat teammates are knockedout, they simply stay in the player's inventory,
set the ally's attribute "knockedout" = true
and basically adjust all combat turn scripts with requiring of ally.knockedout=false
in order to participate in battle

====================================================================

Also, if players and combat teammates can keep respawning, this makes killing enemies a breeze,
we can't reset the enemies' hp to max whenever the player enters a room, because it is possible that the player is not attacking an enemy dungeon room but rather, it happens because the enemies are chasing the player, therefore the enemies should not restore to max hp on all occassions (on enter all rooms script)

Rather than setting the enemies' hp to max whenever the player enters a room,
instead, set all alive enemies' hp to max whenever players and/or combat teammates respawn


Thanks for your answer. I got it!


Thanks for your answer. I got it!

I dunno wat u talking bout,
charlesdaven is an advertisement bot


I'm wondering if there is a benefit to this placeorder system rather than just maintaining a list of enemies.

Looking at the code in the first post, I wonder if it might be more efficient to modify it like:

pseudo code (untested)
on enter all rooms script
and on combat teammates' deaths
and on enemies death

game.enemyorder = FilterByNotAttribute (GetDirectChildren (game.pov.parent), "enemy", null)

(this creates an objectlist of all objects which have an enemy attribute)

on every 1 turn in turn script (player uses lightning blade)

if (ListCount (game.enemyorder) > 0) {
  enemy = ListItem (game.enemyorder, 0)
  enemy.hp = enemy.hp - player.atk
}

(in this case, rather than searching every object in the room again to find the one in the first place, you just look at the list you already made)

on every 1 turn in turn script (player uses laser beam (area of effect))

for (i, 0, 2) {
  if (ListCount (game.enemyorder) > i) {
    enemy = ListItem (game.enemyorder, i)
    enemy.hp = enemy.hp - player.atk
  }
}

(that's just based on your first post… I wrote a response already and apparently didn't finish it)

As to the question about enemies/allies wanting to change positions, like putting your tanks at the front, that's simple enough. Just give them an attribute like preferred_combat_position which says where they want to be - low numbers for the front, and high for the back.

Then your list-making function becomes:

game.enemyorder = FilterByNotAttribute (GetDirectChildren (game.pov.parent), "enemy", null)
game.enemyorder = ObjectListSort (game.enemyorder, "preferred_combat_position")

In this case, enemies/allies with the same preference will be put in standard order (the order they appear in the game file for objects that existed from the start; or the order they were created for cloned objects)


I'm wondering if there is a benefit to this placeorder system rather than just maintaining a list of enemies.

The point is only to keep the enemies' position updated every turn script, and your coding does so

Because the ally might resurrect combat teammates while the enemies can call in reinforcement
halfway in the battle

(that's just based on your first post… I wrote a response already and apparently didn't post it)

You might have accidentally typed in an advertising keyword by copying and paste my message,
in this forum thread, I had a few times "You can't post that here" error,
I keep on changing my words until my posts finally got through

I personally save my message before I create a forum post, but yeah, it is tedious


Looking at the code in the first post, I wonder if it might be more efficient to modify it like:

I often run into system out of memory, so my coding is definitely whacky,
I asked the bot about your coding, and it agrees yours is better and complained my coding were

  1. Redundant Code: The code to find enemies would need to be repeated in every place where an action might affect enemies, which can lead to redundancy and harder-to-maintain code.
  2. Error-Prone: Managing enemy interactions without a central list can lead to inconsistencies, especially if objects change frequently.

But perhaps, because I am not familiar with

  1. FilterByNotAttribute
  2. ListCount

I find it hard to visualize what you are doing as compared to my coding even after asking the bot to explain,

why is

game.enemyorder = FilterByNotAttribute (GetDirectChildren (game.pov.parent), "enemy", null)

faster than

placeorder = 1
foreach (enemy, GetDirectChildren (game.pov.parent)) {
  if (HasAttribute(enemy, "enemy")) {
    enemy.position = placeorder
    placeorder = placeorder+1
  }
}

I was expecting this code to fail, as it sounds like it is deducting all enemies' hp, but when tested, it did only reduced the first enemy's hp, even though the coding works, I have no idea why

if (ListCount (game.enemyorder) > 0) {
  enemy = ListItem (game.enemyorder, 0)
  enemy.hp = enemy.hp - player.atk
}

The below is my imagination for your code
Because you have never set the enemy's position to 1,
how the heck does the code knows you are asking it to choose the
first item in the list?

foreach (enemy, GetDirectChildren (game.pov.parent)) {
  if (HasAttribute(enemy, "enemy")) {
   i̶f̶ ̶(̶e̶n̶e̶m̶y̶.̶p̶o̶s̶i̶t̶i̶o̶n̶ ̶=̶ ̶1̶)̶ ̶{̶
      enemy.hp = enemy.hp - player.atk
    }
  }
}

Or perhaps, you can ignore my elementary questions,
I just need time for my brain to adjust


demo 1 (This is just early testing, it does not feature any significant breakthrough yet)

<!--Saved by Quest 5.8.6836.13983-->
<asl version="580">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="test9">
    <gameid>1379648f-eef8-49c6-974e-a9ba68783226</gameid>
    <version>1.0</version>
    <firstpublished>2024</firstpublished>
    <roomenter type="script">
    </roomenter>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <atk type="int">1</atk>
    </object>
    <exit alias="south" to="room1">
      <inherit name="southdirection" />
    </exit>
  </object>
  <object name="room1">
    <inherit name="editor_room" />
    <exit alias="north" to="room">
      <inherit name="northdirection" />
    </exit>
    <exit alias="south" to="room2">
      <inherit name="southdirection" />
    </exit>
    <object name="goblin">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
    <object name="goblin1">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
  </object>
  <object name="room2">
    <inherit name="editor_room" />
    <exit alias="north" to="room1">
      <inherit name="northdirection" />
    </exit>
    <exit alias="south" to="room3">
      <inherit name="southdirection" />
    </exit>
    <object name="goblin2">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
  </object>
  <object name="room3">
    <inherit name="editor_room" />
    <exit alias="north" to="room2">
      <inherit name="northdirection" />
    </exit>
    <exit alias="south" to="room4">
      <inherit name="southdirection" />
    </exit>
    <object name="goblin3">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
    <object name="goblin4">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
    <object name="goblin5">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
  </object>
  <object name="room4">
    <inherit name="editor_room" />
    <exit alias="north" to="room3">
      <inherit name="northdirection" />
    </exit>
    <exit alias="south" to="room5">
      <inherit name="southdirection" />
    </exit>
    <object name="goblin6">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
    <object name="goblin7">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
    <object name="goblin8">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
    <object name="goblin9">
      <inherit name="editor_object" />
      <enemy type="string"></enemy>
      <hp type="int">7</hp>
    </object>
  </object>
  <object name="room5">
    <inherit name="editor_room" />
    <exit alias="north" to="room4">
      <inherit name="northdirection" />
    </exit>
    <exit alias="south" to="room6">
      <inherit name="southdirection" />
    </exit>
  </object>
  <object name="room6">
    <inherit name="editor_room" />
    <exit alias="north" to="room5">
      <inherit name="northdirection" />
    </exit>
    <exit alias="south" to="room7">
      <inherit name="southdirection" />
    </exit>
  </object>
  <object name="room7">
    <inherit name="editor_room" />
    <exit alias="north" to="room6">
      <inherit name="northdirection" />
    </exit>
  </object>
  <object name="room8">
    <inherit name="editor_room" />
  </object>
  <object name="room9">
    <inherit name="editor_room" />
  </object>
  <object name="room10">
    <inherit name="editor_room" />
  </object>
  <turnscript>
    <script><![CDATA[
      game.enemyorder = FilterByNotAttribute (GetDirectChildren (game.pov.parent), "enemy", null)
      msg (""+game.enemyorder)
      if (ListCount (game.enemyorder) > 0) {
        enemy = ListItem (game.enemyorder, 0)
        enemy.hp = enemy.hp - player.atk
        if (enemy.hp<1) {
          RemoveObject (enemy)
        }
        foreach (enemy, GetDirectChildren (game.pov.parent)) {
          if (HasAttribute(enemy, "enemy")) {
            msg (enemy.name+": "+enemy.hp+" hp")
          }
        }
      }
    ]]></script>
    <enabled />
  </turnscript>
</asl>

As to the question about enemies/allies wanting to change positions, like putting your tanks at the front, that's simple enough. Just give them an attribute like preferred_combat_position which says where they want to be - low numbers for the front, and high for the back.

I know this code will work, but I can't visualize it so I am asking is the player able to change the order for the combat teammates' positions?
Or do I have to create an enter "position number" recorder for the player to manually adjust the allies position?


In this case, enemies/allies with the same preference will be put in standard order (the order they appear in the game file for objects that existed from the start; or the order they were created for cloned objects)

If I am not wrong, this means two enemies with "preferred_combat_position" = 1 will not crash the quest app, and it will sort it out automatically based on coding order in quest file


You might have accidentally typed in an advertising keyword by copying and paste my message,

No, I mean that I had a tab open with half a reply written. I must have got distracted while writing it.

But perhaps, because I am not familiar with

  1. FilterByNotAttribute
  2. ListCount

The use of FilterByNotAttribute here is is a little weird. Basically, FilterByNotAttribute (GetDirectChildren (game.pov.parent), "enemy", null) gives you an objectlist containing all objects in the current room which have an enemy attribute with a value that isn't null.

More often, you would use FilterByAttribute to find an attribute with a specific value. However, because an attribute that doesn't exist has the special value null, that means that we can search for values that are not null to find objects that have a specific attribute. Does that make sense?

ListCount is much simpler - it tells you how many objects are in a list. In the example, we check if ListCount is greater than zero to make sure there are actually enemies in the list. In the example where we're looking for the first three enemies, it checks each time. So for example if there are 3 enemies present, ListCount (game.enemyorder) will be 3, and you can access the enemies by using ListItem (game.enemyorder, 0), ListItem (game.enemyorder, 1), and ListItem (game.enemyorder, 2).

Does that make sense? I'm not sure if you're used to working with lists like this.

why is [...] faster than [...]

It isn't really – it's doing the same thing, just storing the results differently.

The difference is that your code stores a number in an attribute on each enemy. While mine stores a list of enemies in a single attribute.

When you need to find the enemy in a given position, your code is looking at every object in the room again, checking them all to see if they have the right number; you have another loop. But my code just uses ListItem to extract the first enemy from the list.

I was expecting this code to fail, as it sounds like it is deducting all enemies' hp, but when tested, it did only reduced the first enemy's hp, even though the coding works, I have no idea why

It can't touch all enemies; it doesn't have a foreach.
It has the line enemy = ListItem (game.enemyorder, 0) - which sets the enemy variable to be the first object in the list game.enemyorder.

This is a lot faster than using foreach, because it only need to look at one object, not all of them.

I know this code will work, but I can't visualize it so I am asking is the player able to change the order for the combat teammates' positions?
Or do I have to create an enter "position number" recorder for the player to manually adjust the allies position?

I would probably allow the player to choose "front", "middle", or "back", and set the attribute to 1, 2, or 3 … but that's just a preference. You can do it however you want.


charlesdaven

I apologize, you are a human


You sound like a bot this time.


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

Support

Forums