Reactions the long version

From WiCWiki

Jump to: navigation, search

Contents

Every time anything happens in the game; a unit dies/creates/ends up in battle, a command point change owner, the game starts…everything; the game executes call-back functions. We use lots of things that happen in the game to create single player maps but it would be messy and confusing to spread the code from our python events and place parts of it in different call-back functions therefore we have a system with “reactions”. There are reactions for when unit dies, command point change owners and the game starts. If this sounds a bit confusing…well, it is. But hopefully things will be clearer soon.


You can take a closer look at all the reactions in the wic\python\serverimports.py file. Every reaction starts with the ‘RE_’ prefix.


Actions

An action is a combination of a function and some arguments and is used for reactions, post fill commands etc. An action is often triggered when a reaction is executed.


We seldom or never know exactly when a reaction will trigger its actions and because of that we need to save what will happen when the reaction is triggered. The functions that will be executed we save in actions. A reaction can have several actions and if that is the case, we have to have them in a list. [ Action(aFunction), Action(aFunction) ]. Make sure you don’t have the parentheses after the function because if you have, it will execute the function directly. If the function have parameters, use a comma after the functions name like this, ‘Action(aFunction, anArgument)”. If you use several parameters just separate them with commas like this, Action(”aFunction, anArgument, anArgument”).


Here is a reaction that will execute an action when triggered.

RE_OnEmptyPlatoon( mapvars.pltPlayer, Action( queVillageFail.Execute ) )

Here is a reaction that will execute two actions when triggered.

RE_OnCommandPointTaken( 'CP_Village', TEAM_NATO, [ Action( RemoveReaction, mapvars.reactVillagePlayerDead ) ,Action( queVillageComplete.Execute ) ] )

The difference is that if the reaction shall execute several actions they must be in a list.


You can save the action in a variable to execute later, as well.

myAction = Action( queVillageFail.Execute )

Execute the action.

myAction.Execute()


PostEvent

To trigger a reaction an event must be posted. We post events with the PostEvent function that is used a lot in the API. Every call-back in the game calculates information, adapts the information to the reactions system and sends the result as events for us to use. Sometimes events post from deep inside the code, but you can post your own events to trigger a reaction.


PostEvent( anEventString, *someArguments )


- The first parameter is the ’events name’ the string you post, which must be a string.

- The second parameter is an arbitrary number of arguments, for more info check www.python.org. This means you can send as many arguments you need. The arguments are used for test data if the reaction that listens to the event has tests.


Here is an example where the PostEvent just posts one event, without test data.

PostEvent( 'IntroEnd' )

If you want to post the event with arguments, just add them after the string separated with commas.

PostEvent( 'IntroEnd', 1, ’Kalle’ )


Post events to test a reaction

Because reactions only gets tested and triggered when an event posts, and never if the event the reaction listens to already have happened, it sometimes can lead to strange problematic situations. That something we want to listen to already have happened and the reaction won’t get triggered is a pretty common situation and we risk a game breaker. If you know that something is crucial for the mission you want to make sure the problem is fixed before it occurs. Some examples you can solve with complex reactions which we will cover in coming paragraphs of this chapter. But, sometimes there are no complex reactions so you will have to fix it in another way.


You can after you’ve created the reaction post the event the reaction listens to and can in that way test if it should trigger.


Here is an example where we use the reaction OnEmptyGroup which checks the group (in this case the ‘mapvars.grpDestroyAA1’ group) size. But, if the group is empty when the reaction is created it will never trigger and we have made us a potential game breaker.

The solution in this case is an added ‘PostEvent’ that will test the group size after the reaction is created. If the group is empty it will trigger the OnEmptyGroup reaction and we’ve saved us from a nasty situation.


Create the reaction that will check if the group is empty.

mapvars.reactDestroyAAKilled1 = RE_OnEmptyGroup( mapvars.grpDestroyAA1, Action( mapvars.objDestroyAA.RemoveSubMarker, 1, True ) )

Posts the groups current size to make sure the reaction will trigger either or not the group is empty already.

PostEvent( 'GroupSize', mapvars.grpDestroyAA1, mapvars.grpDestroyAA1.Size( True ) )

Different Reaction Types

There are three different types of reactions. Time based, event based and complex reactions. The complex reactions don’t work like the other two.


Event reactions

The most common types of reactions are the event based. The event based reactions trigger when an event posts. Something that must be considered when creating event reactions are that they are only triggered when something happens, for example a unit dies. We have talked about this before, but it is important to be aware of.


OnCustomEvent

One really useful reaction is the ‘RE_OnCustomEvent’ reaction which is a reaction that listens to and waits for an event string that you post. You use the ‘PostEvent’ function

RE_OnCustomEvent( anEventString, someActions, someTestFunctions = None )

- The first parameter is the string the reaction listens to.

- The second parameter is what action or actions that will be executed.

- As third parameter you can have test functions for when it will trigger. It is none by default, i.e. you don’t need to write anything if you want it to be none. We will cover the test functions later.


Here is an example with an ‘OnCustomEvent’ reaction that waits for the posted ‘Event’ ‘IntroEnd’ to be posted. It will, when triggered, call the function VillageCam.

RE_OnCustomEvent( 'IntroEnd', Action( VillageCam ) )

Here is another example where we have added a test to the reaction. The test is a counter test and here the PostEvent, in this example ‘IntroEnd’ must be posted twice before the reaction will be triggered and call the function ‘VillageEnd’.

RE_OnCustomEvent( 'IntroEnd', Action( VillageCam ),  CounterTest(2) )


Edit Event reactions

When it comes to event based reactions there’s a bunch of things you can edit; add new reactions, remove reactions and add or remove test functions. To be able to do this you must save the reaction in a variable.


Edit Actions

You can add actions you want the reaction to execute when triggered. You can remove them as well.


There are three methods to add actions to a reaction.


If it’s a new, single action you want to add to the reaction, it looks like this. It will add an action regardless of any earlier actions.


reactExemple.AddAction( Action( VillageStart ) )


- The parameter is the action you want to add. It must be an Action().


If you want to add more than one action it looks like this.


reactExemple.AddActions( [ Action( VillageStart ), Action( VillageStart2 ) ] )


- The parameter must be a list with actions.


If there are an action that must be executed last of all you can add it as a single ‘EndAction’. When the reactions are triggered it will execute all the “normal” actions before it executes the end action. It looks like this.


reactExemple.AddEndAction( Action( VillageStart ) )


- The parameter must be an Action().


If you don’t want an action to be executed when the reaction is triggered, use the ‘RemoveAction’ method. It looks like this.


reactExemple.RemoveAction ( Action( VillageStart ) )


- The parameter must be an Action().


Edit the Tests

The tests can, as actions, be edited after the reactions are created. You can add new tests or remove all test functions.


The code for adding a test function to the reaction looks like this.


reactExemple.AddTest( SimpleTest( 1) )


- The parameter must be a ’Test’.


If you don’t need the test anymore or you want to change them we use the ‘RemoveAllTests’ method. As the name imply, it will remove all tests from that reaction.

It don’t have any parameters.


reactExemple.RemoveAllTests()


Complex reactions

If a reaction’s name ends with EX it’s a complex reaction (in all cases but one (RE_OnPlayerEnterContainerEx ) confusingly enough, but sometimes things are weird, get used to it!). Complex reactions work a bit different than ‘normal’ reactions for example you can’t use ‘RemoveReaction’. If you want to remove a complex reaction you must use ‘myComplexReaction.Shutdown()’. Nor can you use ‘RegisterReaction’, you must create a new one after the reaction has been triggered or you have used ‘Shutdown’. We will cover this later. Complex Reactions are really neat. In a bunch of cases you can use them instead of creating tests to avoid some game breakers. We will talk about this soon.

RE_OnCommandPointTakenEx.

This reaction checks if the team you want to check owns one or several command points. There are two reasons for using the complex reaction in this case. If you use a ‘normal reaction’ to check if the team takes a command point it wont trigger if the team owns the command point when the reaction is created but if you use the complex reaction it can (if you use ‘True’ instead of the default ‘False’ as the aTestAtInit parameter) trigger if the team owns it from the beginning or when they take the command point. You can also check if the team owns several command points.

RE_OnCommandPointTakenEx(someCommandPoints, aTeam, someActions, aTestAtInit = False) 

- The first parameter is a list with one or several command points. This must be in a list even though you might just have one command point, else it will crash.

- The second parameter is which team you want to test if they own the command point.

- The third parameter is an action or several actions you want to execute when the reaction triggers.

- The fourth parameter is if you want to check if the team owns the command point when the reaction is created. It is ‘False’ by default and ‘False’ won’t execute the ‘owner test’ when created so if you want the test to be used, use true.


Here is an example where the OnCommandPointTakenEx reaction which will trigger when NATO takes the command point ’CP_Village’


RE_OnCommandPointTakenEx( [ 'CP_Village' ], TEAM_NATO, [ Action( RemoveReaction, mapvars.reactVillagePlayerDead ) ,Action( queVillageComplete.Execute ) ], True )


The first parameter is the command point ’CP_Village’ (which is in a list even though it’s just one, remember?). After that is the team, in this case NATO. The third parameter is a list with actions. The last parameter in this case is aTestAtInit variable which is ‘True’ in this case which means it will test if the team owns the command point when the reaction is created and will in that case trigger directly.

RE_OnCommandPointFortifiedEX.

If you want to check if the team have built fortifications on several perimeter points, not necessarily perimeter points that belong to the same command point, it’s much easier to use the complex ‘RE_OnCommandPointFortifiedEX’ reaction than to use several ‘normal’ reactions. You can decide if the reaction should trigger on when the right fortification type is built on all perimeter points or how many fortifications there are.

Like with RE_OnCommandPointTakenEx you can test if there already are fortifications when the reaction is created and in that case trigger the reaction instantly.

RE_OnCommandPointFortifiedEx(somePerimeterPoints, aTeam, aLevel, someActions, aExactLevelCheck = False, aTestAtInit = False )


- The first parameter is the perimeter point (NB, if you use only one, it mustn’t be in a list, unlike in the OnCommantPointTakenEX reaction, standardizations are for chickens) or a list with several perimeter points.

- The second parameter is which team you want to test if they’ve built fortifications.

- The third parameter is the level or number fortifications there should be on all perimeter points to trigger the reaction. If it is what level, this applies; 1 = anti infantry, 2 = anti tank and 3 = anti air.

- The fourth parameter is an action or several actions you want to execute when the reaction triggers.

- The fifth parameter is if it should be quantity or type that will trigger the reaction. By default it is ‘False’ and that means the reaction will trigger on quantity.

- The sixth parameter is if you want to check if the team already have built fortifications when the reaction is created. It is ‘False’ by default and ‘False’ won’t execute the test when created.


Here is an example where the OnCommandPointFortifiedEx which will trigger if NATO has built 2 fortifications on both Mansions perimeter points.

RE_OnCommandPointFortifiedEx( [ 'PP_Mansion1', 'PP_Mansion2' ], TEAM_NATO, 2, Action( queMansionUpdate.Execute ), False, True )  

As first parameter we use the Mansions command points two perimeter points, ‘'PP_Mansion1' and 'PP_Mansion2', in a list. The only thing we care about in this case is that there are at least two fortifications on each perimeter point; we don’t care about type in this case, hence the ‘False’ as parameter five. To prevent one possible game breaker we check if there are fortifications already when we create the reaction, with ‘True’ as parameter six.

RE_OnCustomEventEX.

If you want to trigger if several events have been posted or trigger when one out of several events has been posted, you should use the RE_OnCustomEventEx reaction.

By adding tests to, or change tests type for the reaction, you can create a pretty advanced reaction.


RE_OnCustomEventEx(someEvents, someActions, someTests = None, aTestType = COMPLEX_TEST_AND, aAddFirst = False )

- The first parameter is one or several event strings. If you use several they should be in a list.

- The second parameter is an action or several actions you want to execute when the reaction triggers.

- The third parameter is none, one or several tests. Default is ‘None’ which means no tests.

- The fourth parameter is which test type. Default is COMPLEX_TEST_AND.

- The fifth parameter is if you want to add the reaction first in the list of reactions. Default is ‘False’ which means it won’t be added first.


Here’s an example where the reaction will trigger the action to execute the function ‘MakeAllUnitsInvulnerable’ with false as parameter (which means the units won’t be invulnerable no more). This will happen after the objective camera, in this case, is finished or skipped.

RE_OnCustomEventEx( ['END_OF_CUTSCENE', 'SKIP_CUTSCENE'], Action( MakeAllUnitsInvulnerable, False ), None, COMPLEX_TEST_OR, True )

We listens to two events; ’END_OF_CUTSCENE’ or ’SKIP_CUTSCENE’. We want to execute the function MakeAllUnitsInvulnerable when either one of the two events posts, hence the Test type COMPLEX_TEST_OR instead of COMPLEX_TEST_AND (which is the default and had resulted in, in this case, that both events must be posted before it will trigger the reaction). Because it is important that we turn of the immortality as fast as possible, we want the reaction to be executed first in the reaction list. We do this by changing the last parameters default ‘False’ to ‘True’. We will cover more about test types later.


If there were no Complex reactions

As we mentioned before the complex reactions are really neat. If we would like to do the same thing as ‘OnCommandPointTakenEX’ by using an event reaction you would have to add your own test if the team had the command point already. It would look something like this.

if GetCommandPointOwner( 'CP_Village' ) == TEAM_NATO:
    queVillageComplete.Execute                  
    RemoveReaction( mapvars.reactVillagePlayerDead )
else:
    RE_OnCommandPointTaken( 'CP_Village', TEAM_NATO, [ Action( RemoveReaction, mapvars.reactVillagePlayerDead ) ,Action( queVillageComplete.Execute ) ] )


The first thing you have to do is to check if NATO is the owner of ‘CP_Village’. If it is true, execute the queue and remove the reaction immediately to remove the chance of react on the wrong thing. If it is not true, create a reaction that listens to when NATO takes the command point and execute the queue and remove the reaction.


Some event reactions don’t have a Complex reaction version, so in some cases you have to do this anyway.

Timer reactions

As the name indicates timer reactions is about time. There are two kinds of timer reactions; Delay and Repeat.


Delay.

Delays are really handy some times. If there is something you want to happen for example 3 seconds from now you should use a delay.

The code looks like this.


Delay( aDelay, someActions )


- The first parameter is how long, in seconds, the delay should be, in other words, in how many seconds it will trigger.

- The second parameter is the action or the list of actions that will be triggered.


Here is an example with a delay that will trigger the function LastStand2 in allunits.py, in 3 seconds.


Delay( 3, Action( allunits.LastStand2 ) )



Repeat.

The other timer reaction is a repeat reaction. It will trigger its actions at regular intervals.


It might be good to know that the delay will affect the first time this reaction will trigger its actions as well.


Repeat( aDelay, someActions )


- The first parameter are how long, in seconds, before it will trigger the first time and how far between the actions will be triggered.

- The second parameter is the action or the list of actions that will be triggered.


Here is an example with a repeat that every third second will trigger the function

‘LastStand2’ from the allunits.py file.


Repeat( 3, allunits.LastStand2 )


Edit timer reactions.

If you save a timer reaction in a variable you can change it later. If it’s a repeat timer you can change the time that affects the intervals as well.


To be able to change the reaction, we’ll save it in a variable.


reactDelay = Delay( 3, Action( allunits.LastStand2 ) )


If you want to get or change the repeat time, use the ‘myRepeatTime’ member variable.


reactDelay.myRepeatTime


If you want to know how many seconds remaining until the reaction will trigger, use the ‘GetTimeLeft’ method.


timeLeftToTrigger = reactDelay.GetTimeLeft()


Remove and Register Reactions

If you save a reaction in a variable you can remove it when you don’t need it anymore. If the reaction have triggered or been removed you can add it again.


This is an example where the reaction will be triggered when the command point 'CP_Village' is taken.


reactExemple = RE_OnCommandPointTakenEx( [ 'CP_Village' ], TEAM_NATO,) Action( queVillageComplete.Execute ) , True )


Sometimes we must save a reaction in a variable and remove it. For example if you have an objective that will trigger an action queue when you complete it and another queue when you failed it is important to remove the reaction that didn’t trigger, especially if there is a couple of message boxes in the queue else there is an chance you will fail the objective right after you completed it and that is not fun.

RemoveReaction.

If you don’t have any use for the reaction any more or don’t want it to trigger at the moment you should remove it from the reaction manager.

We use the function called ‘RemoveReaction’. It won’t remove the reaction for real just set it as inactive.


RemoveReaction( reactExemple )


-The parameter is the variable with the saved reaction.


RegisterReaction

If you want to add the reaction to the reaction manager again after it’s been triggered or you have removed it, you use the function called ‘RegisterReaction’.


RegisterReaction( reactExemple, aFirstInList = False )


- The first parameter is the variable with the reaction you want to add again.

- The second parameter is if the reaction should be added as number one in the reaction list or not. It is false by default.


RegisterReaction( reactExemple )


RepeatingReaction If you don’t want a reaction to be removed after it has been triggered you don’t need to use ‘RegisterReaction’ for each time but can set the reaction to be a repeating reaction.


reactExemple.myRepeating = True


Or you can do it in one line when the reaction is created.


reactExemple = RE_OnCustomEvent( 'IntroEnd', Action( VillageCam ) ).myRepeating = True


Change the myRepeating variable to false if you don’t want it to be a repeating reaction anymore.

reactExemple.myRepeating = False



Reaction Tests.

OnCustomEvent and OnCustomEventEx reactions can have none, one or several test functions. If you use several tests they must be in a list. You can decide how the tests shall be used, if all should be true to trigger or if it is enough with one. We’ll cover more about test functions later.


There are six test functions you can use that already exist. We’ll cover the most common four here.


SimpleTest.

Compare the value from the post event with the test functions value and test if they are the same.

SimpleTest( aValue, anIndex = 0 )

- The first parameter is the value you want to compare with the PostEvents test value. It can be anything that can be equal (‘==’) to the PostEvents value, an integer, a float or a string, for example.

- The second parameter is used if you have more than one value in your ‘PostEvent’ and in that case which value it should be compared to.


By default this is 0, which means the first value so if you just have one, you don’t have to write anything.


Here is an example where the reaction waits for ’introEnd’ to be posted with an argument, in this case 1.

RE_OnCustomEvent( 'IntroEnd', Action( VillageCam ), SimpleTest( 1) )

To trigger this reaction you must post the PostEvent with 1 as a value.

PostEvent( 'IntroEnd', 1 )


ComplexTest.


The Complex test will check if multiple values from the ’PostEvent’ is equal to the tests values. The value on the first index spot in ‘PostEvent’ will be compared to the value on the first index spot in the test function and so on. All comparision must be equal else the reaction won’t trigger.

ComplexTest( *someValues )

- The one and only parameter is the value or values. The ‘ComplexTest’ can be called with an arbitrary number of arguments. The value can be anything that can be equal (‘==’) to the PostEvents values.


Here is an example where the reaction waits for ‘introEnd’ to be posted with three values which will be compared.

RE_OnCustomEvent( 'IntroEnd', Action( VillageCam ), ComplexTest ( 1, 2.42, ‘Hello’) )

To trigger this reaction you must, in this case, post the PostEvent with 1, 2.42 and ‘Hello’ as values.

PostEvent( 'IntroEnd', 1, 2.42 ,’Hello’ )

CounterTest.

The Counter test will decrease the counter for each time the event is posted. When it reaches less than one it will trigger the reaction.

CounterTest( aCounter )

-The parameter is how many times the ‘PostEvent’ must be posted before it will trigger.

Here is an example where the reaction waits for ‘introEnd’ to be posted two times.

RE_OnCustomEvent( 'IntroEnd', Action( VillageCam ), CounterTest (2) )

To trigger this reaction the post event must be posted twice.

PostEvent( 'IntroEnd' )
PostEvent( 'IntroEnd' )


CustomTest.

The custom test executes a function each time the event posts. If the test returns ‘True’ the reaction will be triggered. If the test returns ‘False’ the reaction will not be removed but keep on listening. If the event posts again the test will be executed as before and the whole process will loop until it returns ‘True’.

CustomTest( aFunction, *someArguments )

- The first parameter is the name of the function.

- The second parameter is the functions possible parameters.

Test types.

There are four different options how the reaction will use the tests. By default all tests must be true, i.e. REACTION_TEST_AND.

REACTION_TEST_AND

This will trigger the reaction if all tests are true. This is the default option.

REACTION_TEST_OR

This will trigger the reaction if some test is ‘True’.

REACTION_TEST_XOR

This will trigger the reaction if one and only one test is ‘True’.

REACTION_TEST_NOT 

This will trigger the reaction if all tests are false.


If you want to change the test type you’ll use the member variable myTestType.

reactExemple.myTestType = REACTION_TEST_OR


Extra Chapter 1: Our Python files and Event Structure < > Extra Chapter 3: The Objective Class
Personal tools
User Created Content