Jump to content

triggers


fuzzie

Recommended Posts

The last (ok, optimistic, but I can hope) hugely broken thing in the GemRB scripting code is the trigger code.

 

At the moment, GemRB just keeps some objects around in every scriptable object (actor, door, etc) for things like LastAttacker, checks them, and every time a trigger references one of those, it is set to be wiped after the action queue for that actor is processed. This is obviously a huge source of bugs. And is terrible. Oh, despair. :undecided:

 

It's pretty difficult to tell exactly what's going on from just poking at the original engine, too.

 

ToBEx's source (much love!) is more helpful; I see that there's a trigger list there as well as a list of objects (and our friend labelled as the attack type), and it says that triggers are checked with comparison for 0x0-style triggers and in a more complicated manner for 0x4-style triggers.

 

Can anyone shed more light into this? I haven't found any good clues in the forum history. In particular it would be helpful if anyone had any idea how (a message? a flag? always?) and when (end of frame? if flagged? in the message execution?) the triggers are removed.

Link to comment

I only have a preliminary understanding, but I think trigger opcodes 0x0XXX check if a SetTrigger has been made on the scripted object. Used for things like AttackedBy(), HitBy(), Heard(), etc. They check the object specifiers [EA.GENERAL.CLASS.all that] match that in the script. If so, it returns true. This is because whenever a scripted object is attacked for example, the attacker sends a MessageAddTrigger that will be attached to the target.

 

If you take an excerpt from CGameSprite class (scriptable object):

	Object oAttacker; //42h, for AttackedBy() trigger, for LastAttackerOf triggerid

DWORD nType; //56h
//ASTYLE.IDS from AttackedBy()
//DAMAGES.IDS from HitBy()

Object oCommander; //5ah, for ReceivedOrder() trigger, for LastCommandedBy triggerid
Object oProtector; //6eh, for ProtectedBy triggerid
Object oProtectee; //82h, for ProtectorOf triggerid
Object oTargetor; //96h, for LastTargetedBy triggerid
Object oHitter; //aah, for HitBy() trigger, for LastHitter triggerid
Object oHelpShouter; //beh, for Help() trigger, for LastHelp triggerid
Object oTriggerer; //d2h, for trigger in SVTRIOBJ.IDS, for LastTrigger triggerid
Object oSeeer; //e6h, for LastSeenBy triggerid
Object oTalker; //fah, for Said() trigger, for LastTalkedToBy triggerid
Object oShouter; //10eh, for Heard() trigger, for LastHeardBy triggerid
Object oSummoned; //122h, for Summoned() trigger, for LastSummonerOf triggerid

Let's say A attacks B. I presume that when A is given an action to attack B, it will send a MessageAddTrigger, so that B.oAttacker is set to the object specifiers of A. If B has a script that AttackedBy(id), id will be checked against B.oAttacker. If the object specifiers are the same, then the trigger will be true.

 

I am not sure when these triggers are cleared.

 

 

Trigger opcodes 0x4XXX, parse independently. They check everything else in the game.

 

Take with a bunch of salt, of course!

Link to comment

I don't think stored objects are ever cleared (is that what is asked here?). A lot of hardcoded stuff will set them, so they change frequently, but I think the current value persists until the next update. Maybe through master area transitions? But I don't think it would matter even if they got cleared then.

 

So they init to null on game load (or new object load), and then accrue values throughout the session until the next reload.

Link to comment

Huh, i always assumed triggers get cleared eventually. But found no code (didn't look for it intentionally).

So, i guess, if there is no evidence triggers are ever cleared (unless processed), then i don't have to look for this :undecided:

 

This stuff is difficult to test, because there is no way to know if a trigger is set without clearing it.

Link to comment
This stuff is difficult to test, because there is no way to know if a trigger is set without clearing it.
I ran some quick tests using a debugger. It appears the triggers are truly never cleared. It can only be overriden by a new trigger.

 

Overridden? What does that mean?

 

My current understanding is that there could be 2 AttackedBy triggers on the queue.

Link to comment

I think maybe you two are being confused by the triggers vs the objects? The objects are not so interesting. They are indeed kept around; I already had to add a bunch of hacks to GemRB there to make SoA completable, otherwise scripts are (obviously) left using incorrectly-cleared objects.

 

But there's no clear way to see how/when the triggers in the list are cleared. I'm pretty sure HitBy doesn't return true for the entire life of an object, and I'm also pretty sure it doesn't stay true for hours if it's not checked before then.

 

My current plan is to make *all* triggers go via a message-type queue (things like PartyRested can go there too, dammit) and clear triggers before message proocessing, but I haven't looked into that beyond trying it quickly and checking that it's no more broken than GemRB's current system.

 

(Sorry for my delayed response, I suppose replying to devSin's post up there would have avoided any confusion.)

Link to comment
I think maybe you two are being confused by the triggers vs the objects? The objects are not so interesting. They are indeed kept around; I already had to add a bunch of hacks to GemRB there to make SoA completable, otherwise scripts are (obviously) left using incorrectly-cleared objects.
Yes, you are right. I was thinking of the objects. I haven't really looked much at trigger lists stored in scripted objects.
Link to comment

The trigger list (0x272) is cleared after every script round, if there are some actions to be executed for the active AI object. (And this makes sense, think about the HotKey() trigger.)

 

Player controlled characters do some additional clearing after every action the player gives. (like attack, move, dialogue, guard, cast spell etc.)

This is done in an indirect way, not sure if I got this correctly. Here goes: There is a counter (0x35FC) that is set to 75 (multiple of 15 -> AI updates?) after any of the above actions and this counter is decremented quite often (every AI update?). As long as this counter is greater 0, the AI object clears the trigger list but updates all the last_attacker, last_help, etc. objects before doing that.

 

There is an additional counter (0x35FE) that does basically the same, but it is only set in dialogue, when the transition has some actions.

 

In a nutshell: Trigger list is cleared after the AI object gets something to do.

Link to comment

Archived

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

×
×
  • Create New...