Trigger Attachments. If you maps has them, please read this.

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Trigger Attachments. If you maps has them, please read this.

Veqryn
Administrator
I am thinking of re-writing how Triggers work.

Depending on how you coded your map xml, this might affect you or not.

One of my goals, is to NOT affect your map.  I don't want anyone to have to re-write their map because of my changes.

However, I only know how I coded my maps, I don't know what kinds of crazy things you guys did.


HOW I CODED MY XML TRIGGERS:
Each of the triggers in my maps does only 1 thing.  
If I need to do 2 things, I made 2 different triggers.

So for example, I may want to do 2 things: give a player a tech, and switch their production frontier.
I want this to happen on this condition: I have conquered Moscow for the first time.

What I do in my maps:
ConditionAttachment_Germans_control_Moscow -> has the condition of direct ownership of Moscow by Germany

TriggerAttachment_Germans_ReceiveTechX -> has the condition above, has 'uses=1', and has 'tech' equal to 'tech X'

TriggerAttachment_Germans_ProductionFrontier_changedTo_Blah -> has the condition above, has 'uses=1', and has 'frontier' equal to 'German_Blah_production'


Therefore, when the condition is true, I will have two separate triggers firing.  This is the BEST way of doing things right now.



However, the engine currently allows this following method.  I consider this method to be BAD PRACTICE.  See below:

ConditionAttachment_Germans_control_Moscow -> has the condition of direct ownership of Moscow by Germany

TriggerAttachment_Germans_ReceiveTechX_and_ProductionFrontier_changedTo_Blah -> has the condition above, has 'uses=1', and has 'tech' equal to 'tech X', and has 'frontier' equal to 'German_Blah_production'


So, my question is, DOES ANYONE USE THIS 'BAD' WAY OF CODING THEIR TRIGGERS?


I want to change the way triggers work, and it will NOT change the "GOOD" way, but it will change how the current "BAD" way is implemented when you actually play.



Here is a brief overview of why, which you don't need to read if you don't want to:
One thing I want to point out, that makes any refactoring of the Trigger attachment difficult is this:
The current way of "activating" a trigger, does NOT activate the FULL trigger.
Instead, it activates only a part of the trigger.

For example, take this:

in Trigger Attachment: public static void triggerTechChange(PlayerID player, IDelegateBridge aBridge, final String beforeOrAfter, final String stepName)
This is called from exactly two places: the TechActivationDelegate, and the BaseDelegate.
In the TechActivationDelegate, we are activating this trigger IF this trigger has no "when" variable set.
In BaseDelegate, we are activating this trigger IF the trigger has a "when" set.

Now, inside this, we go and get the triggers that HAVE tech changes: Set<TriggerAttachment> trigs = getTriggers(player, data, techMatch(beforeOrAfter, stepName));

This is done by using this match:
            public boolean match(TriggerAttachment t)
            {
                return t.getUses() != 0 && whenOrDefaultMatch(beforeOrAfter, stepName).match(t) && !t.getTech().isEmpty();
            }

Which means, if the Trigger has uses still (either uses is >0 or uses is -1 which means infinite), and it has a "when" either null or matching the target's "when", and the "t.getTech()" is not empty.

Basically, the "t.getTech()" is what determines that this trigger is going to fire or not.  Any trigger in the entire map xml that has t.getTech() set to something, will come up for firing.  Any trigger that doesn't have t.getTech() set, will be ignored.

Now, we take this list of triggers that has t.getTech() set, and see if any of them have their conditions match:
    for (TriggerAttachment t : trigs)
        {
            boolean met = isMet(t, data);
            if (met)
            {

Then, if they are met, we actually perform JUST the tech-activation PART of the trigger.  We do not perform all the parts of the trigger, just the tech activation:
                t.use(aBridge);
                for (PlayerID aPlayer : t.getPlayers())
                {
                    for (TechAdvance ta : t.getTech())
                    {
                        if (ta.hasTech(TechAttachment.get(aPlayer))
                                    || !TechAdvance.getTechAdvances(data, aPlayer).contains(ta))
                            continue;
                        aBridge.getHistoryWriter().startEvent(MyFormatter.attachmentNameToText(t.getName()) + ": " + aPlayer.getName() + " activates " + ta);
                        TechTracker.addAdvance(aPlayer, aBridge, ta);
                    }
                }


In other words, if there is a trigger that does 2 things: gives the player a new technology, and switches their production frontier to something else, then that trigger will fire in 2 different places, with each part firing once.
So, lets say this trigger has the following things:
TriggerAttachment_TechX_and_FrontierBlah
condition - "we do not have technology X"
trigger tech activation - "give technology X to player"
trigger production frontier switch - "switch production frontier to 'blah'"

Now, lets say that this trigger is called in the following order:
(we start without tech X)
triggerTechChange(...)
triggerProductionChange(...)


What will happen is as follows:

triggerTechChange will find all triggers with a tech change as part of the trigger.  This list will include our triggerattachment, TriggerAttachment_TechX_and_FrontierBlah.
triggerTechChange will look at the conditions of TriggerAttachment_TechX_and_FrontierBlah
TriggerAttachment_TechX_and_FrontierBlah's condition is true, because we do not have "tech X".
triggerTechChange will fire the tech change of TriggerAttachment_TechX_and_FrontierBlah, giving the player "tech X".
triggerTechChange returns, and is done

triggerProductionChange starts, and finds all triggers with a production change.  This list will include our triggerattachment, TriggerAttachment_TechX_and_FrontierBlah.
triggerProductionChange will look at the conditions of TriggerAttachment_TechX_and_FrontierBlah
TriggerAttachment_TechX_and_FrontierBlah's condition is FALSE, because we now have "tech X".
triggerProductionChange will return, having done nothing.


At this point, you might be asking yourself: "this sounds like a stupid way of doing things"
If so, I'm tempted to agree with you.

So, before we "fix" or "refactor" TriggerAttachment, we need to agree on what SHOULD be done.

Should we maintain the above behavior, since it is how the engine currently works, and some maps might be affected.
Or should we have the engine only look to fire Triggers once, and then when it tests the conditions, it should fire all the triggers who's conditions are true, including all the different parts of the trigger?

Whatever we do decide, we need to make sure we can still "fire" the individual parts of the trigger separately.  In other words, we need to be able to fire the "techChange" separately from the "productionChange".  The reason being that the "productionChange" generally occurs at the beginning of the turn, while the "techChange" occurs at the end of the turn.  If they both fired at the same time, this would change a lot of maps and I can not allow that (for example, it would give a player the tech at the start of their turn, which would impact that turn).
Thanks,
Veqryn
.
Please contribute to the TripleA 2013 donation drive:
http://tripleadev.1671093.n2.nabble.com/2013-TripleA-Donation-Drive-tp7583455.html
Reply | Threaded
Open this post in threaded view
|

Re: Trigger Attachments. If you maps has them, please read this.

Zim Xero
I have numerous triggers like the following:

                <attatchment name="triggerAttachmentWild35" attatchTo="Wild" javaClass="games.strategy.triplea.attatchments.TriggerAttachment" type="player">
                <option name="trigger" value="conditionAttachmentWcap35"/>
                <option name="placement" value="Woolly Bay:longship" count="1"/>
                <option name="placement" value="Densac Gulf:longship" count="1"/>
                <option name="placement" value="Clatspur Range:longship" count="1"/>
                <option name="placement" value="Solnor Corner:longship" count="1"/>
                <option name="placement" value="Meno Bay:longship" count="1"/>
                <option name="placement" value="Vast Swamp:longship" count="1"/>
                <option name="placement" value="Tilva Strait:longship" count="1"/>
                <option name="placement" value="Sea Barons:longship" count="1"/>
                <option name="placement" value="Fjords:longship" count="1"/>
                  <option name="placement" value="Hraak Bay:longship" count="1"/>
                <option name="placement" value="Icy Sea:longship" count="1"/>
                <option name="placement" value="Land of Black Ice:longship" count="1"/>
                <option name="placement" value="Yatil East:longship" count="1"/>
                <option name="placement" value="Vesve Forest:longship" count="1"/>
                <option name="placement" value="Whyestil Lake:longship" count="1"/>
                <option name="placement" value="Hellfurnace Mountains:longship" count="1"/>
                <option name="placement" value="Barrier Peaks:longship" count="1"/>
                <option name="placement" value="Corusk Fjord:longship" count="1"/>

                <option name="placement" value="Tundra:warlord" count="1"/>
                <option name="placement" value="Jotsplat:warlord" count="1"/>
                <option name="placement" value="Glot:warlord" count="1"/>
                <option name="placement" value="Marner:warlord" count="1"/>
                <option name="placement" value="Terlep West:warlord" count="1"/>
                <option name="placement" value="Ratik Pass:warlord" count="1"/>
                <option name="placement" value="Amedio South:warlord" count="1"/>
                <option name="placement" value="Hool Marshes:warlord" count="1"/>
                <option name="placement" value="Tors:warlord" count="1"/>
                  <option name="placement" value="Flen:warlord" count="1"/>
                <option name="placement" value="Thornward:warlord" count="1"/>
                <option name="placement" value="Tusman Hills South:warlord" count="1"/>
                <option name="placement" value="Ull:warlord" count="1"/>
                <option name="placement" value="Plains of the Paynims:warlord" count="1"/>
                <option name="placement" value="Yecha Hills:warlord" count="1"/>
                <option name="placement" value="Lake Quag:warlord" count="1"/>
                <option name="placement" value="Sepia Uplands:warlord" count="1"/>
                <option name="placement" value="Vesve South:warlord" count="1"/>
                <option name="placement" value="Warfields:warlord" count="1"/>
                <option name="placement" value="Reyhu:warlord" count="1"/>
                <option name="placement" value="Abbor Alz East:warlord" count="1"/>
                <option name="placement" value="Chathold:warlord" count="1"/></attatchment>


None of my triggers combine two different effect types, but most are a list of placements or purchases.
'thats the way it is' makes it neither desireable nor inevitable
Reply | Threaded
Open this post in threaded view
|

Re: Trigger Attachments. If you maps has them, please read this.

Veqryn
Administrator
so long as there is only 1 effect from the trigger, (in your case, "placement"), then your trigger would be considered a "well-coded" trigger
Please contribute to the TripleA 2013 donation drive:
http://tripleadev.1671093.n2.nabble.com/2013-TripleA-Donation-Drive-tp7583455.html