Trigger Handling
Post MTG Forge Related Programming Questions Here
Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins
Trigger Handling
by friarsol » 17 Sep 2014, 15:46
Last night I was playing around with Trigger Handling. Basically what I'm going to try to do is the following:
After state is finished being checked, just before a player gets priority each trigger that is "active" will be locked in, and recorded to a list in TriggerHandler. I'll check many of the trigger requirements (host card zone, threshold, etc) before being marked as active. Then when we run the waiting triggers during the next state check, we'll run the second half of the active trigger's requirements (those specific to the runParams). This also should help speed up this portion of the code, since for each trigger we were looping through all of the cards and checking for its triggers, where now we'll just be assigning active triggers once for each check, and then we only loop through those triggers. Basically, since these triggers will already be "locked" then we won't have to worry about LKI information when dealing with the trigger firing.
My first tests are ETB, and LTB/Dies.
So for example, I want to do the following:
Cast Sengir Autocrat
Cast Blood Artist
Cast Damnation.
Autocrat should trigger once on ETB, once on LTB.
Blood Artist should trigger 5 times on Damnation resolution.
If anyone has specific triggers they want me to test while I work on this, please let me know. I'll slowly expand out as I get specific triggers working.
After state is finished being checked, just before a player gets priority each trigger that is "active" will be locked in, and recorded to a list in TriggerHandler. I'll check many of the trigger requirements (host card zone, threshold, etc) before being marked as active. Then when we run the waiting triggers during the next state check, we'll run the second half of the active trigger's requirements (those specific to the runParams). This also should help speed up this portion of the code, since for each trigger we were looping through all of the cards and checking for its triggers, where now we'll just be assigning active triggers once for each check, and then we only loop through those triggers. Basically, since these triggers will already be "locked" then we won't have to worry about LKI information when dealing with the trigger firing.
My first tests are ETB, and LTB/Dies.
So for example, I want to do the following:
Cast Sengir Autocrat
Cast Blood Artist
Cast Damnation.
Autocrat should trigger once on ETB, once on LTB.
Blood Artist should trigger 5 times on Damnation resolution.
If anyone has specific triggers they want me to test while I work on this, please let me know. I'll slowly expand out as I get specific triggers working.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by KrazyTheFox » 17 Sep 2014, 16:47
Sounds good to me and it seems like this'll be halfway there as far as the engine rewrite is concerned. Here's my general outline for handling triggers for that:
Each card will register triggers with the Game object (which is where the new game loop will be). When a GameAction is executed by the Game, it'll check to see if there's any triggers listed for that particular action, then it'll loop through those to figure out which ones should activate, then store the activated triggers in a list.
The Game will then wait until whatever it was doing is finished, then it'll add the triggers to the stack in order.
For example, I cast and resolve a creature that triggers a Soul Attendant's ability. That ability is added to the game's active trigger list. The game will finish the GameEvent that moves the creature, after which it'll add the triggers to the stack and remove them from the active triggers list.
--
As for particular triggers I'd like to see, Life Gain is high on my list since I like running tokens/life gain a lot (there's also those couple life gain decks in quest mode), which activates sometimes 100s of triggers.
Good luck implementing this; it'll be really nice to be able to copy and paste some of it to the new engine code!
Each card will register triggers with the Game object (which is where the new game loop will be). When a GameAction is executed by the Game, it'll check to see if there's any triggers listed for that particular action, then it'll loop through those to figure out which ones should activate, then store the activated triggers in a list.
The Game will then wait until whatever it was doing is finished, then it'll add the triggers to the stack in order.
For example, I cast and resolve a creature that triggers a Soul Attendant's ability. That ability is added to the game's active trigger list. The game will finish the GameEvent that moves the creature, after which it'll add the triggers to the stack and remove them from the active triggers list.
--
As for particular triggers I'd like to see, Life Gain is high on my list since I like running tokens/life gain a lot (there's also those couple life gain decks in quest mode), which activates sometimes 100s of triggers.
Good luck implementing this; it'll be really nice to be able to copy and paste some of it to the new engine code!
-
KrazyTheFox - Programmer
- Posts: 725
- Joined: 18 Mar 2014, 23:51
- Has thanked: 66 times
- Been thanked: 226 times
Re: Trigger Handling
by Marek14 » 17 Sep 2014, 16:52
How would this handle state triggers like Endangered Armodon? The main problem (for me) with these is that it's hard to make a list of them since their wordings are very variable...
Also, with Sidisi and Rakshasa Vizier, it might be time for another look at damage triggers which trigger on something "dealing damage" or "being dealt damage" and which should therefore trigger only once even if there are multiple recipients/sources of damage (Dromad Purebred or Soul Link being an example).
Also, with Sidisi and Rakshasa Vizier, it might be time for another look at damage triggers which trigger on something "dealing damage" or "being dealt damage" and which should therefore trigger only once even if there are multiple recipients/sources of damage (Dromad Purebred or Soul Link being an example).
Re: Trigger Handling
by friarsol » 17 Sep 2014, 17:10
"Always" triggers are already treated a bit differently in the code, since they can only be on the stack one at a time and they don't really "trigger" in the same fashion that other triggers do. They just check for their conditions constantly. I'll put Endangered Armodon on the list (examples of other "Always" triggers are Islandhome creatures). I don't believe any of that should change, an "Always" trigger is active as long as it meets the standard requirements AND that trigger isn't on the stack already.Marek14 wrote:How would this handle state triggers like Endangered Armodon? The main problem (for me) with these is that it's hard to make a list of them since their wordings are very variable...
Also, with Sidisi and Rakshasa Vizier, it might be time for another look at damage triggers which trigger on something "dealing damage" or "being dealt damage" and which should therefore trigger only once even if there are multiple recipients/sources of damage (Dromad Purebred or Soul Link being an example).
Yea if this goes smoothly I can take a look at how the damage is firing right now. Although most (if not all) of this work will be in the core of receiving and running triggers as oppposed to when they fire (which is what the problem that damage triggers have right now).
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by friarsol » 18 Sep 2014, 03:18
Looks like all of the above are working right now (not counting the consolidation of damage). I just noticed an issue with Clones copying triggering, which makes sense since the Clone isn't actually cloned when the triggers are set. I'll see what I can do about that. The code changes aren't that huge, so I'm hoping this will work with all my testing.
I'll try to at least test one of each Trigger type as I play some more matches.
I'll try to at least test one of each Trigger type as I play some more matches.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by friarsol » 18 Sep 2014, 17:44
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by Marek14 » 18 Sep 2014, 18:30
If you test these two, maybe also Homura, Human Ascendant, just in case?
Re: Trigger Handling
by friarsol » 18 Sep 2014, 23:46
Does this work right now at all? I was just testing it and it seems to come back into play flipped (when killing it), but then I goto cast Disenchant on enchantment variety, it flips before I can target it?Marek14 wrote:If you test these two, maybe also Homura, Human Ascendant, just in case?
Edit: Ahh.. I think the script is wrong. If someone can confirm for me that the return to play ability doesn't actually flip properly in a current/snapshot build. I'll just fix the script to work the way it should.
Also, it's good I tested Loyal Cathar, because apparently delayed triggers were broken and I hadn't tested those yet.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by Marek14 » 19 Sep 2014, 04:36
I didn't actually test Homura, but it seemed very similar to those twofriarsol wrote:Does this work right now at all? I was just testing it and it seems to come back into play flipped (when killing it), but then I goto cast Disenchant on enchantment variety, it flips before I can target it?Marek14 wrote:If you test these two, maybe also Homura, Human Ascendant, just in case?
Edit: Ahh.. I think the script is wrong. If someone can confirm for me that the return to play ability doesn't actually flip properly in a current/snapshot build. I'll just fix the script to work the way it should.
Also, it's good I tested Loyal Cathar, because apparently delayed triggers were broken and I hadn't tested those yet.
Re: Trigger Handling
by friarsol » 19 Sep 2014, 16:38
I just tested it on my desktop, the first half seems to work without the script change, but then it tries to retrigger each time the enchantment part dies. I'll commit the script fix when I commit the trigger changes.
Hopefully I can figure out the Clone LTB Trigger crash this weekend and get this all committed.
Hopefully I can figure out the Clone LTB Trigger crash this weekend and get this all committed.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by friarsol » 21 Sep 2014, 03:26
Alright just committed this. I'm sure there's a few things left to tweak, but hopefully it's mostly working for everyone. I tried testing as many of the triggers I could test easily.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Trigger Handling
by friarsol » 05 Oct 2014, 21:52
Just noticed this one today. If Essence of the Wild is in play, and a creature ETB has an ETB trigger you get this:
This is because the Card has changed in the middle of ETB (but the Trigger is already "Active").
I believe all I need to do to fix it is call:
game.getTriggerHandler().clearInstrinsicActiveTriggers(etbCreature) just before it turns into an EotW. Unfortunately, I'm not sure where in the code that inverted cloning happens. Anyone know?
- ETB Crash | Open
- Game-0 > java.lang.RuntimeException: AbilityFactory : getAbility -- no API in Essence of the Wild
at forge.game.ability.AbilityFactory.getAbility(AbilityFactory.java:108)
at forge.game.trigger.TriggerHandler.runSingleTrigger(TriggerHandler.java:438)
at forge.game.trigger.TriggerHandler.runNonStaticTriggersForPlayer(TriggerHandler.java:318)
at forge.game.trigger.TriggerHandler.runWaitingTrigger(TriggerHandler.java:287)
at forge.game.trigger.TriggerHandler.runWaitingTriggers(TriggerHandler.java:251)
at forge.game.zone.MagicStack.unfreezeStack(MagicStack.java:198)
at forge.game.zone.MagicStack.finishResolving(MagicStack.java:639)
at forge.game.zone.MagicStack.resolveStack(MagicStack.java:591)
at forge.game.phase.PhaseHandler.startFirstTurn(PhaseHandler.java:1077)
at forge.game.GameAction.startGame(GameAction.java:1585)
at forge.game.Match.startGame(Match.java:81)
at forge.match.MatchUtil$2.run(MatchUtil.java:232)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
This is because the Card has changed in the middle of ETB (but the Trigger is already "Active").
I believe all I need to do to fix it is call:
game.getTriggerHandler().clearInstrinsicActiveTriggers(etbCreature) just before it turns into an EotW. Unfortunately, I'm not sure where in the code that inverted cloning happens. Anyone know?
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
12 posts
• Page 1 of 1
Who is online
Users browsing this forum: KeithOvart and 90 guests