Jump to content

small ai script question


Guest wrdaniel

Recommended Posts

Guest wrdaniel

hi all,

 

i want to edit some of the default scripts to suit my style of playing. but i have some small questions which i have not found answered in the web.

 

1) If i get a TRUE in one block the script runs this block and starts from the beginning, right?

 

2) AttackReevaluate attacks the "Enemy" for a defined period and then starts the script from the beginning, but the Action_List is not empty?

 

i used the normal "arang" script and the problem was that the character startet shooting at a group of monsters but the monsters came closer and she shooted with the bow until she was dead. she didnt switch to melee or run away.

 

at the bottom theres a part of the script. so she started with "block2" and opend fire at a monster more then "10" away. but she never came back to block1 cause she continued fighting with the bow also in melee range.

 

that means as long the current enemy is not dead she stays with the AttackReevaluate?

 

maybe someone has the time to explain it a bit for me ;)

 

tnx wrdaniel

 

...[block1]
IF
ActionListEmpty()
AttackedBy([ANYONE],DEFAULT)
Range(LastAttackerOf(Myself),4)
THEN
RESPONSE #100
	EquipMostDamagingMelee()
	AttackReevaluate(LastAttackerOf(Myself),30)
END

... [block2]

IF
ActionListEmpty()
See(NearestEnemyOf(Myself))
!Range(NearestEnemyOf(Myself),10)
THEN
RESPONSE #100
	EquipRanged()
	AttackReevaluate(NearestEnemyOf(Myself),30)
END

...

Link to comment

When triggers return true the actions in the block with true triggers will be executed and the script will restart, yes. Using Continue() as an action will allow the script to not restart and look for another true block to execute, but there are some cautions that accompany that (i.e. don't use it to garnish scripts as you would croutons on a salad ;) )

 

Unless I'm misremembering, AttackReevaluate() is not interruptable - though that shouldn't cause the problems you're having. AttackOneRound() is probably closer to what you're trying to do here, though I'd have to see more of the script to know for sure :D

 

/edit

 

I don't really think you need the ActionListEmpty triggers at all.

Link to comment
Guest wrdaniel

the code quoted above is part of the default script "arang2" from BG2. i was just wondering, cause the ingame text describes the script as "runs away", "switches to melee", etc. - but as long a player is fighting nothing happens.

Link to comment

My recent testing seems to suggest AttackReevaluate() will in fact continue attacking the same enemy without looking for other script actions. So you might experiment with AttackOneRound() as Nythrun suggests. Assuming there are other script blocks to look for. If not, it probably doesn't make much difference.

Link to comment

If you're looking for decent a.i. scripts, the player scripts that ship with BG2 are about the worst model around ;)

 

You might want to browse some of the shorter scripts in SimDing0's quest pack or some other such mod to get a better feeling for what works in the system.

Link to comment
Guest wrdaniel

thanks for the replies. i think i will stay with the "AttackOneRound" for my rangers. That way they always change to the clostest enemies and hit the ones advancing towards them.

 

i dont need perfect scripts for every spell and every enemy, just a little bit of intelligence. why fight someone 20 feet away if you get stabbed in the back again and again ;)

 

the main reason for looking into the scripts was, that i wanted to make the thief search traps if theres no battle while walking around. and that works ok now. :D

 

My recent testing seems to suggest AttackReevaluate() will in fact continue attacking the same enemy without looking for other script actions

 

it stops for scripts without the "ActionListEmpty()" command.

 

wrdaniel

Link to comment

In BG2, all the attack actions are interruptible. To understand the differences, you need to understand a bit about how the engine handles scripts. When the engine goes to process the scripts, it starts evaluating each block from the top down; when a block is found to be true, the engine begins executing the actions (queueing them up if necessary) and restarts script evaluation. If the engine goes through each preceding block and they all evaluate to false, if it then reaches the block with the actions that it last executed and they are still being executed, the engine does nothing (it doesn't attempt to re-execute the actions but instead stops and restarts processing yet again).

 

Given this, AttackOneRound() is used as a pseudo-persistent action. When issued, the character will attack the target for a single round (this is interruptible, so if the engine finds something else for the character to do, it will stop attacking and start performing the new actions). Unlike the other two actions, this action expires after one round (note that thanks to personal initiative, this may not always be six seconds; it's heavily dependent on when in the character's personal round the action is executed), and any subsequent actions in the same block should succeed once the time is up. If the engine returns to this block, the character will restart combat for another round, potentially with a different target (depending on the triggers for the block and the specified target).

 

Attack() is similar to AttackOneRound() except that there is no influenceable limit to how long the character will attack the target. Unless the target becomes invalid or dies, the character will continue attempting to attack the target as long as the engine doesn't find anything else for it to do (if no preceding blocks in the script are true). Unlike AttackOneRound(), the Attack() action never expires, and any actions coming after it the same block won't be executed unless the target dies (there's a hardcoded cutoff where the engine will try to see if a newer target can be found, say if you run the current target into a room and close the door, and any subsequent actions may be performed if the attacker manages to acquire a new target, but I haven't ever tested it).

 

AttackReevaluate() is the most complex of the three. It is similar to both, and the character will attack the target, and script processing restarts as expected. If no preceding blocks in the script are found to be true (the character continues to attack), this action allows you to specify a reevaluation period (in AI updates); depending on the time specified, the game will periodically check to see if restarting the action will yield a new target -- given the engine's behavior where it will never attempt to re-perform actions from the same block if it's still executing actions from that block, you can see how it can be difficult to script combat if you can't ever get the character to respond to changes in the current state of the game. This action is smarter than AttackOneRound(), and the game won't switch targets if the script runner is in melee combat (the current target is within 4 feet, give or take), and if the new target is found to be the same as the current target, the game doesn't attempt to mess with the character's current actions (unlike AttackOneRound(), it won't ever restart combat unless it finds a new target). Like Attack(), subsequent actions in the same block will never be executed unless the current target dies or becomes invalid (this is tricky; although you can control how often the engine should try to look for newer, better targets, the current target has to become invalid *before* the script runner switches to the new target for the subsequent actions to execute).

Link to comment

AttackReevaluate() is shafted. devSin's description is how it's supposed to work, but heavy testing in the past by xyx, Sarkyn, Quitch, myself and many other AI scripters showed that it behaves like a simple Attack(). I never use it.

 

@Nythrun:

 

ActionListEmpty() is a necessity at the top of a script block. For the life of me now I cannot remember just why, but I can guarantee that if it's missing, the script block will fail to fire more often than it fires.

 

Just updated my sig; I cannot think why I didn't add the new tagline before.

 

-Y-

Link to comment

Oh no, if you've got scripting info, you don't get away that easily :)

 

ActionListEmpty() is a restrictive trigger - adding it to a block shouldn't make the block return true more often, only less often. If it's at the top of every block, how do you have an action queue?

 

Can you point me to a script where AttackReevaluate() is used and behaves like Attack()? I don't use this one much, but that's been mostly out of a desire to keep script blocks trim :D

Link to comment
Oh no, if you've got scripting info, you don't get away that easily :)

 

ActionListEmpty() is a restrictive trigger - adding it to a block shouldn't make the block return true more often, only less often. If it's at the top of every block, how do you have an action queue?

 

Can you point me to a script where AttackReevaluate() is used and behaves like Attack()? I don't use this one much, but that's been mostly out of a desire to keep script blocks trim :D

 

ActionListEmpty does not need to be at the top of every block, especially on a .cre script.

 

For Player AI its main purpose is to leave control of the character in the players hands. Your action list is not empty if the player is adding commands to the action list through the game menu (go here, cast this spell, attack that, etc.,.)

 

You do not want it at the top of block that you want to fire regardless of what the player says, for instance, you may want your character to always stop and pop a potion of neutralize poison if you character gets poisened.

 

Use of ALE in Player Scripts is one of those taste things. E.g., GBluchers BG2/TOB scripts don't use ALE much. He would rather have the scripts handle everything.

 

The eSeries on the other hand makes extensive use of ALE for non-priority script blocks so that the player retains control of the character 95% of the time.

 

I don't remember all the details fo the testing. But eveyone uses Attack() because AttackReevaluate() and AttackOneRound() do not net you your full complement of attacks per round. E.g., if you should get 3 attacks per round, with AR and A1R you might be 3, 2, or 1 at times.

Link to comment
You're still going to lose attacks the first round with Attack() if you move or use items or do anything else that resets personal initiative.

 

Sounds reasonable/plausible. I didn't do the testing myself, so I can't speak to all the possibilties.

 

For the easier battles, I typically leave the characters on remote control (when using the eSeries), so that wouldn't happen, unless the script decides to do anything except attack ,which is possible because of an interupt timer that should break the character out of the Attack() if something more important than attacking comes along.

 

Shame that the testing isn't archived anywhere, then.

 

Agreed. There is a certain 'modder' that I would kick in the nads over that if I every met them in person.

 

Not that it couldn't be repeated with all the Feedback tools turned on. Probably a most tedious endeavor though.

Link to comment
ActionListEmpty does not need to be at the top of every block, especially on a .cre script.

 

For Player AI its main purpose is to leave control of the character in the players hands. Your action list is not empty if the player is adding commands to the action list through the game menu (go here, cast this spell, attack that, etc.,.)

 

You do not want it at the top of block that you want to fire regardless of what the player says, for instance, you may want your character to always stop and pop a potion of neutralize poison if you character gets poisened.

The reminder of 'why' is much appreciated! It has been so long since I considered it that I had completely forgotten the reason.

There is a certain 'modder' that I would kick in the nads over that if I every met them in person.

I bet I can run faster than you - at least, in that race. I did try to save that archive but... :)

 

@Nythrun: I've tended to concentrate on player AI scripting, like Cirerrek, but for pre-ToB games. I've done very little cre AI scripting; the one time I started to convert my party AI scripts to enemy AI, I realised that if I succeeded I'd make the game near-unplayable because the enemy would probably be a lot smarter than the player.

 

That said, I'm quite willing to look at any AI scripting problems you may have.

 

[EDIT]: I forgot to add that the AI code is available with the script packages.

 

-Y-

Link to comment

If it's used right, AttackReevaluate() works fine (and if you compare the behavior with an Attack() action, you should definitely see the difference). It may be the melee thing (the engine won't switch targets if the current target is too close) that they don't like.

 

It's not an issue in most scripts since you shouldn't have that much to do by the time you get to the hittin'. Potentially losing attacks doesn't really bother me either (once they start attacking, they're not going to stop unless you stop them, which returns to the first point); it's a tradeoff between being able to run away from an enemy and having them maniacally chase you down (completely ignoring all the valid targets along the way) or simply switching targets before their round is up if you puss out.

 

Moot point since almost everybody uses Attack() with their own timers now, as cirerrek says.

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...