Difference between revisions of "Scripting Tutorial"

From Nexus Mods Wiki
Jump to: navigation, search
 
(46 intermediate revisions by 2 users not shown)
Line 24: Line 24:
 
The tutorial is also designed in such a way that you don’t have to discard the result of the previous exercise. The following exercise either expands on the previous one or creates something new from scratch and the results may coexist.
 
The tutorial is also designed in such a way that you don’t have to discard the result of the previous exercise. The following exercise either expands on the previous one or creates something new from scratch and the results may coexist.
  
==1.    New brain, new subbrain, first script ==
+
 
 +
 
 +
== 1. New brain, new subbrain, first script ==
  
 
'''Goal:''' The NPC plays an animation
 
'''Goal:''' The NPC plays an animation
Line 39: Line 41:
 
##**''tags= waveSmall''   
 
##**''tags= waveSmall''   
 
##*Wait with parameters  
 
##*Wait with parameters  
##**''time = -1''       
+
##**''duration = -1''       
  
 
'''Newly introduced nodes:'''
 
'''Newly introduced nodes:'''
  
 
*Root  
 
*Root  
*Sequence
+
*Sequence  
*PlayAnimation
+
*PlayAnimation  
*Wait
+
*Wait  
  
 
'''Extras:'''
 
'''Extras:'''
Line 59: Line 61:
 
##Insert a prefab   
 
##Insert a prefab   
  
  {{#ev:youtube|uIE90btCr7U}}
+
  {{#ev:youtube|uIE90btCr7U}}  
  
==2.    Links, behavior call, movement ==
+
== 2. Links, behavior call, movement ==
  
 
'''Goal:''' NPC calls a behavior from the SmartArea which makes him move to a tagPoint and play the aimation
 
'''Goal:''' NPC calls a behavior from the SmartArea which makes him move to a tagPoint and play the aimation
Line 73: Line 75:
 
##Save the XML in file ''Data/Libs/AI/_test/test_ST_sa.xml''  
 
##Save the XML in file ''Data/Libs/AI/_test/test_ST_sa.xml''  
 
##Definition of the behavior:  
 
##Definition of the behavior:  
##*Find the tagPoint via GraphSearch  
+
###Find the tagPoint via GraphSearch  
##*Move to the tagPoint  
+
###Move to the tagPoint  
##*Play the animation from previous exercise     
+
###Play the animation from previous exercise     
 
#Change the NPCs subbrain tree definition:  
 
#Change the NPCs subbrain tree definition:  
 
##Find the SmartArea via GraphSearch  
 
##Find the SmartArea via GraphSearch  
Line 84: Line 86:
 
##AI -> Request a full NMN rebuild  
 
##AI -> Request a full NMN rebuild  
 
##cvar ''wh_ai_RecastDebugDraw=1'' shows the generated navmesh   
 
##cvar ''wh_ai_RecastDebugDraw=1'' shows the generated navmesh   
 +
 +
'''Newly introduced nodes:'''
 +
 +
*GraphSearch
 +
*LinkTagFilter
 +
*Move
 +
*CallBehavior
  
 
'''Extras:'''
 
'''Extras:'''
Line 89: Line 98:
 
#Add breakpoints to all the nodes and step through the execution, observe the behavior  
 
#Add breakpoints to all the nodes and step through the execution, observe the behavior  
  
  {{#ev:youtube|v1pBGpriZgQ}}
+
  {{#ev:youtube|v1pBGpriZgQ}}  
  
== 3.    Advanced Move, Dynamic node values, Distance tests, Gates ==
+
== 3. Advanced Move, Dynamic node values, Distance tests, Gates ==
  
 
'''Goal:''' NPC will keep moving towards the player
 
'''Goal:''' NPC will keep moving towards the player
Line 105: Line 114:
 
#Edit the NPC’s subbrain so that it calls the ''moveToPlayer'' behavior  
 
#Edit the NPC’s subbrain so that it calls the ''moveToPlayer'' behavior  
  
''VIDEO COMING SOON''
+
'''Newly introduced nodes:'''
 +
 
 +
*Parallel
 +
*Expression
 +
*Loop
 +
*DistanceGate
 +
*VarOperation (Reinit)
 +
*Success
  
== 4.    Guard behavior, Loops, Randomization, Save/Load ==
+
  {{#ev:youtube|FsZcGUXIUIQ}}  
 +
 
 +
== 4. Guard behavior, Loops, Randomization, Save/Load ==
  
 
'''Goal:''' 2 behaviors. NPC walks in a loop. NPC keeps walking between random tagPoints. Both behaviors support save/load.
 
'''Goal:''' 2 behaviors. NPC walks in a loop. NPC keeps walking between random tagPoints. Both behaviors support save/load.
Line 119: Line 137:
 
##*If there is another tagPoint linked from the previous tagPoint, go to it  
 
##*If there is another tagPoint linked from the previous tagPoint, go to it  
 
##*Wait at each point for several seconds       
 
##*Wait at each point for several seconds       
#Edit the NPC’s subbrain so that it calls the ''patrol ''behavior. Test the behavior.
+
#Edit the NPC’s subbrain so that it calls the ''patrol ''behavior. Test the behavior.  
 
#Add Save/Load Support to the ''patrol'' behavior:  
 
#Add Save/Load Support to the ''patrol'' behavior:  
#*<span class="marker">Info: Saves are created using Ctrl+F5. All AI trees are restarted upon loading so it your responsibility to reconstruct them properly. The only variables which are saved are Brain-scope variables (defined in Brain, accessible from all subbrains) and Persitent variables (that's a variable property, false by default). The rest is reinitialezed upon loading.</span>  
+
#*<span class="marker">Info: Saves are created using Ctrl+F5. All AI trees are restarted upon loading so it your responsibility to reconstruct them properly. The only variables which are saved are Brain-scope variables (defined in Brain, accessible from all subbrains) and Persitent variables (that's a variable property, false by default). The rest is reinitialezed upon loading. Links, unless specified otherwise in ''LinkTagDefinitions.xml'', are persistent</span>  
 
#*You will need at least one persistent variable, you will need some IfCondition tests, and you must pay attention to the order of execution of GraphSearch and Move nodes.   
 
#*You will need at least one persistent variable, you will need some IfCondition tests, and you must pay attention to the order of execution of GraphSearch and Move nodes.   
 
#Create a new behavior on the SA, name it ''randomPatrol''  
 
#Create a new behavior on the SA, name it ''randomPatrol''  
 
#Definition of ''randomPatrol'':  
 
#Definition of ''randomPatrol'':  
##The NPC finds a random tagPoint via GraphSearch (it must not be the point where the NPC is standing now)
+
##The NPC finds a random tagPoint via GraphSearch (it must not be the point where the NPC is standing now)  
 
##The NPC goes to the random tagPoint, waits there for a few seconds  
 
##The NPC goes to the random tagPoint, waits there for a few seconds  
##Loop it
+
##Loop it  
#Create links between the NPC and each tagPoint. You can add a new set of tagPoints or you can reuse the same points used by ''patrol''
+
#Create links between the NPC and each tagPoint. You can add a new set of tagPoints or you can reuse the same points used by ''patrol''  
#Edit the NPC’s subbrain so that it calls the ''randomPatrol'' behavior. Test the behavior.
+
#Edit the NPC’s subbrain so that it calls the ''randomPatrol'' behavior. Test the behavior.  
#Add Save/Load Support to the ''randomPatrol'' behavior.
+
#Add Save/Load Support to the ''randomPatrol'' behavior.  
 +
 
 +
'''Newly introduced nodes:'''
 +
 
 +
*IfCondition
 +
*Nodalyzer
 +
*NegationOp
 +
*WUIDFilter
 +
*VarOperation (PushBack)
  
 
'''Extras:'''
 
'''Extras:'''
Line 139: Line 165:
 
#*Find and store all tagPoints in an array. Then, in a loop, always select a random array member via RandomItem node or via NumericalOperation node using “rand” function.   
 
#*Find and store all tagPoints in an array. Then, in a loop, always select a random array member via RandomItem node or via NumericalOperation node using “rand” function.   
  
''VIDEO COMING SOON''
+
&nbsp; {{#ev:youtube|YIbO7wj575s}} &nbsp;
  
== 5.&nbsp;&nbsp; &nbsp;2 NPCs with the same brain calling different behaviors, link data ==
+
== 5. Link data, Custom link types, Dynamic links ==
  
'''Goal:''' NPCs select their behavior based on link data. First NPC executes''randomPatrol'' behavior, the other NPC executes patrol behavior.
+
'''Goal:''' NPCs select their behavior based on link data. One NPC executes ''patrol'' behavior. 2 other NPCs execute ''randomPatrol'' behavior and utilize a simple reservation system via dynamic links.
  
1.&nbsp;&nbsp; &nbsp;Create a new link definition in LinkTagDefinitions.xml. Define it as the tagname ''work_ST''&nbsp; which can carry data of the type string. (Note: New type definitions require the editor the be restarted)<br/> 2.&nbsp;&nbsp; &nbsp;Place another NPC in the level, name it ''test_ST_npc2''
+
#Create a new link definition in ''LinkTagDefinitions.xml''. Define it as name ''work_ST'' which can carry data of the type ''string''. (Note: New type definitions require the editor the be restarted)  
 +
#Place another 2 NPCs in the level, name them ''test_ST_npc2'' and ''test_ST_npc3''
 +
#*Give them souls of the same names
 +
#*Give the NPCs the same brain as that of ''test_ST_npc1'' (brain ''test_ST_npc_brain'') 
 +
#Create a link from the npc1 to the SA, and name it ''work_ST[(‘patrol’)]''. Note: ‘patrol’ is the data here.
 +
#Create a links from the npc2 and npc3 to the SA, and name each ''work_ST[(‘randomPatrol’)]''.
 +
#Edit the npc subbrain definition so that:
 +
##An NPC finds the SA
 +
##Stores the link data in a variable
 +
##Calls a behavior of the name stored in the variable 
 +
#Change the link networks required by both behavior so that the SA is the starting reference point, not he NPCs
 +
#*Change the behaviors so that they have proper Origins in their GraphSearches (automatically created variable ''__area.id'') 
 +
#2 NPCs are now executing the ''randomPatrol'' behavior and thus are at risk of going to the same spot and clipping through one another. Create a simple reservation systems using link ''reserved''. Change the definition of ''randomPatrol'':
 +
#*When searching for next tagPoint:
 +
#**The NPC excludes the tagPoint it's standing on, and those which are reserved.
 +
#**The NPC does a reservation cleanup - deletes any ''reserved'' link from the area to a tagPoint.
 +
#**Creates a ''reserved'' link from the area to the tagPoint it found.   
  
*Give it a soul of the same name
+
'''Newly introduced nodes:'''
*Give the NPC the same brain as the ''test_ST_npc1'' (brain ''test_ST_npc_brain'')
 
  
3.&nbsp;&nbsp; &nbsp;Create a link from the npc1 to the SA, and name it ''work_ST[(‘patrol’)]''. Note: ‘patrol’ is the data here.<br/> 4.&nbsp;&nbsp; &nbsp;Create a link from the npc2 to the SA, and name it ''work_ST[(‘randomPatrol’)]''. &nbsp;<br/> 5.&nbsp;&nbsp; &nbsp;Edit the subbrain definition so that:
+
*AddLink
 +
*RemoveLink
 +
*Semaphore
  
*The NPC finds the SA
+
&nbsp; {{#ev:youtube|cFFzlYI1tNU}} &nbsp;
*Stores the link data in a variable
 
*Calls a behavior as determined by the data. &nbsp;  
 
  
6.&nbsp;&nbsp; &nbsp;Change the links between NPCs and tagPoints so that each NPC can execute its behavior properly, as they are defined in the previous exercises (that is, the NPC is always the starting point for the search)
+
== 6. Messages ==
  
''VIDEO UPCOMING SOON''
+
'''Goal:''' The NPC doing the patrol behavior reacts to player entering the SmartArea by inspecting the player's original position.
  
== 6.&nbsp;&nbsp; &nbsp;Universally defined behaviors ==
+
#Create a new custom type in ''..\Data\Libs\AI\TypeDefinitions.xml''
 +
#*name the type:subtype ''test_ST:intruder''
 +
#*with a sole member "intruder" of the type ''common:wuid''
 +
#*<font color="black"><span style="background:#ff9900">Note: Adding new types requires editor restart</span></font> 
 +
#Create a new mailbox template definition
 +
#*Name it ''intruderST''
 +
#*Make it accept the new type ''test_ST:intruder'' 
 +
#Add a brain to the SA
 +
#*name the brain ''test_ST_sa''
 +
#*give it all the types of trees an SA-type brain can handle: OnUpdate/OnEnter/OnLeave/OnRequest/OnRelease
 +
#*save the tree definitions in ''_test\test_ST_sa.xml'' (full path ''..\Data\Libs\AI\_test\test_ST_sa.xml'')
 +
#*<font color="black"><span style="background:#ff9900">Add the node Wait=-1 to onUpdate tree and Success node to other trees. They will fail if left empty.</span></font> 
 +
#SA’s onEnter tree definition:
 +
#*when player entered the SA send a message ''test_ST:intruder'' with intruder variable carrying the WUID of the intruder (equal to global variable ''__player'' in this case) to an NPC doing the patrol behavior. 
 +
#Edit the patrol behavior:
 +
#*Add the new mailbox (''intruderST'') to this behavior
 +
#*Create a new variable ''mode'' of the type string. Set the default value to “patrolling”. Mode “patrolling” will represent the old patrol logic from previous exercises. Value “inspecting” will represent the new mode, where NPC reacts to an intruder. 
 +
#Create the “inspecting” logic:
 +
##Move to the intruder, run if the intruder is far
 +
##After the movement, test if the intruder is still close
 +
##*If the intruder is close: 
 +
###Play animation ADLG_Emphasis with tag "angry"
 +
###Then keep turning to the player for a period of time using TurnBody. 
 +
##*If the intruder is far: 
 +
###Play animation ''LookingAround'' (no tags) for several seconds 
 +
##Then set mode back to “patrolling” 
 +
#Create a new logic for switching between the behavior modes:
 +
#*Use a ProcessMessage or ReadMessage node to process the messages sent from the SA. Plug it in parallel to the rest of the logic. Don’t forget a Loop; you need to be able to catch and process multiple messages. The reaction to the message is a change of the mode to “inspecting”
 +
#*Using ContinuousSwitch, create a logic which can react to a change in mode variable and switch to the proper mode tree.
 +
#*When returning to “patrolling” the NPC must find the currently nearest TagPoint and continue patrolling from there. 
  
'''Goal: '''Multiple NPCs can execute the same behavior without the need of creating additional extensive link networks.
+
'''Newly introduced nodes:'''
  
1.&nbsp;&nbsp; &nbsp;Add another NPC ''test_ST_npc3'' to the level, give it a soul and the same brain.<br/> 2.&nbsp;&nbsp; &nbsp;Create a link between the NPC and the SA with link ''work_ST[(‘randomPatrol’)]''. &nbsp;<br/> 3.&nbsp;&nbsp; &nbsp;Change the link network required for the ''randomPatrol'' behavior so that the links no longer start from the NPC but from the SA.<br/> 4.&nbsp;&nbsp; &nbsp;Edit the ''randomPatrol'' definition so that:
+
*SendMessageToNPC
 
+
*InstantSendMessageToNPC
*The GraphSearch starts from the SA (automatically created variable ''__area.id'')
+
*MultiSendMessageToNPC
 +
*InstantMultiSendMessageToNPC
 +
*ReadMessage
 +
*ProcessMessage
 +
*ContinuousSwitch
 +
*Switch
 +
*DistanceCondition
 +
*Selector
 +
*Fail
 +
*AnimationEndWait
 +
*StopAnimation
 +
*TurnBody
 +
*CategoryFilter
 +
*RangeSorter
  
 
'''Extras:'''
 
'''Extras:'''
  
1.&nbsp;&nbsp; &nbsp;Redefine the ''patrol'' behavior and its links in a similar way. Temporarily force the NPC3 to call the ''patrol'' behavior, to test it.
+
#Change the logic so that the NPC goes to the nearest tagPoint when switching back to “patrolling” mode
 +
#Make the whole behavior save/load-proof. &nbsp;
  
''VIDEO UPCOMING SOON''
+
&nbsp; {{#ev:youtube|BqQaEASe8VQ}} &nbsp;
  
== 7.&nbsp;&nbsp; &nbsp;Dynamic links ==
+
== 7. Synchronization, External Locks, AreaPresence ==
  
'''Goal:''' Implementation of a simple reservation system. The 2 NPCs executing randomPatrol will never go to the same spot at the same time.
+
Goal: 2 NPCs placed in an area keep synchronizing and wave at each other at the same time in random intervals. Other entities can enter and leave the area and the 2 NPCs react to these events.
  
1.&nbsp;&nbsp; &nbsp;2 NPCs are now executing the ''randomPatrol'' behavior and thus are at risk of going to the same spot and clipping through one another. &nbsp;<br/> 2.&nbsp;&nbsp; &nbsp;Utilize the pre-defined autoreverse link-pair ''reserver/reservee''. Change the definition of ''randomPatrol'':
+
#Create a regular square-shaped triggerArea
 +
#Create 2 new NPCs ''test_ST_waveMaster'' and ''test_ST_waveSlave''. Place them inside the triggerArea.
 +
#*They will call behaviors named ''waveMaster'' and ''waveSlave'' (resp.) from the SmartArea. 
 +
#Add 2 tag points for each NPC - ''wavePoint'' and ''hidePoint''
 +
#*Place the points inside the triggerArea
 +
#*The wavePoints should be placed about 2 meters apart and must be facing one another 
 +
#Add a new behavior to the SA, name it ''waveMaster''. Defintion of the behavior:
 +
##Find the two points - ''wavePoint'' and ''hidePoint''
 +
##Feature a variable ''mode'' with 2 possible values ''"wave"'' and ''"hide"'' and create a control structure which reacts to changes in mode value (ContinuousSwitch)
 +
##''hide'' mode definition:
 +
##*Go to hidePoint and wait 
 +
##''wave'' mode definition:
 +
##*Move to wavepoint and align with it
 +
##*Loop
 +
##**Random Wait between loop repetitions
 +
##**Wait for synchronization. Synchronize node settings:
 +
##***LockManagerType = Local
 +
##***LockCount = 2 
 +
##**When Synchronized, play animation ''GreetingsUpperBody'' with tag ''waveSmall''   
 +
##Have a parallel control script which changes the mode values:
 +
##*Looped ExternalLock ''stalkerEntered'' (LockmanagerType=Local). When open, set ''mode="hide"'' and lock the ExternalLock.
 +
##*Looped ExternalLock ''stalkerLeft'' (LockmanagerType=Local). When open, set ''mode="wave"'' and lock the ExternalLock.   
 +
#Add new behavior to the SA, name it ''waveSlave''. The defition is the same as ''waveMaster'' with some exceptions:
 +
#*The NPC uses its own pair of points ''wavePoint'' and ''hidePoint''
 +
#*The ''wave'' mode lacks random delays between loop repetitions
 +
#*ExternalLocks are named ''playerEntered'' and ''playerLeft'' 
 +
#Create a new NPC and name it ''test_ST_stalker''. Place it outside the triggerArea
 +
#*He will call a behavior called ''stalker'' from the SmartArea.  
 +
#Create 2 points for the stalker NPC, which will be used by the behavior.  
 +
#*Place them outside the triggerArea
 +
#*Direct trajectory between the points must run across the triggerArea 
 +
#Add a new behavior to the SA, name it ''stalker''. The NPC will be crossing the triggArea in random intervals. Definition of the behavior:
 +
##Find the 2 stalker points
 +
##Go between them in a loop
 +
##At each stop, play the animation ''LookingAround'' for random amount of time 
 +
#Expand the unused onUpdate tree of SmartArea and turn into an area-intruder observing system:  
 +
##Find the triggerArea
 +
##Store player and stalker NPC in an array
 +
##Using AreaPresence node create a script which unlocks the ExternalLocks in ''waveMaster'' or ''waveSlave'' behaviors when the stalker NPC or player enters or leaves the triggerArea. 
  
*When searching for next tagPoint:
+
'''Newly introduced nodes:'''
**The NPC excludes tagPoints from which the link ''reservee'' exists. &nbsp;
 
**The NPC does a reservation cleanup - deletes any ''reserver'' link from the NPC to a tagPoint (if such link exists).
 
**Creates a ''reserver'' link from the NPC to the tagPoint it selected. 
 
<div>''VIDEO COMING SOON''</div>
 
== 8.&nbsp;&nbsp; &nbsp;Save/load ==
 
 
 
'''Goal:''' Both behaviors (''patrol'' and ''randomPatrol'') are save/load-proof. The NPCs resume their walking to the exact same tagPoint they selected before saving and loading.
 
 
 
<span class="marker">Notes: Trees and brains are always restarted after loading. Links, unless specified in ''LinkTagDefinitions.xml'', are persistent.</span>
 
 
 
<br/> 1.&nbsp;&nbsp; &nbsp;Change the definitions of both behavior:
 
 
 
*Change the variable, which stores the destination, to a persistent one.
 
*Only search for a destination, if the destination variable is empty (TRUE when compared to global variable ''__null'')
 
*When the NPC reaches destination, reset the destination variable.
 
 
 
''VIDEO COMING SOON''
 
 
 
== 9.&nbsp;&nbsp; &nbsp;Messages ==
 
 
 
'''Goal:''' The NPC doing the patrol behavior reacts to player entering the SmartArea by inspecting the players original position.
 
 
 
1.&nbsp;&nbsp; &nbsp;Create a new custom type in ''..\Data\Libs\AI\TypeDefinitions.xml''
 
 
 
*name the type ''test_ST:intrude''r (that is type and subtype)
 
*with one member intruder of the type ''common:wuid''
 
*(Note: Adding new types requires editor restart)
 
 
 
2.&nbsp;&nbsp; &nbsp;Create a new mailbox template definition
 
 
 
*Name it ''intruderST''
 
*Make it accept the new type ''test_ST:intruder''
 
 
 
3.&nbsp;&nbsp; &nbsp;Add a brain to the SA
 
 
 
*name the brain ''test_ST_sa''
 
*give it all the types of trees and SA-type brain can handle:&nbsp; OnUpdate/OnEnter/OnLeave/OnRequest/OnRelease
 
*save the trees definitions in ''_test\test_ST_sa.xml'' (full path ''..\Data\Libs\AI\_test\test_ST_sa.xml'')
 
*Add the node Wait=-1 to onUpdate tree and Success node to other trees if there is no script yet. Otherwise they will keep failing.
 
 
 
4.&nbsp;&nbsp; &nbsp;SA’s onEnter tree definition:
 
 
 
*when player entered the SA, send all NPCs, who are executing the patrol behavior, a message ''test_ST:intruder'' with intruder ''variable'' carrying the WUID of the intruder (equal to global variable ''__player'' in this case). &nbsp;
 
 
 
5.&nbsp;&nbsp; &nbsp;Edit the patrol behavior:
 
  
*Add the new mailbox (''intruderST'') to this behavior &nbsp;
+
*Synchronize
*Create a new variable mode of the type string. Set the default value to “patrolling”. “patrolling” will represent the old patrolling logic from previous exercises. Value “inspecting” will represent the new mode, where NPC reacts to an intruder.
+
*ExternalLock
*Create the “inspecting” logic:
+
*SetExternalLock
**Move to ''__player''
+
*AreaPresence
**Play animation ''LookingAround'' (no tags) for several seconds
+
*ExactMove
**Then set mode back to “patrolling” 
 
*Create a new logic for switching between the behavior modes:
 
**Use a ProcessMessage or ReadMessage node to process the messages sent from the SA. Plug it in parallel to the rest of the logic. Don’t forget a Loop; you need to be able to catch and process multiple messages. The reaction to the message is a change of the mode to “inspecting”
 
**Using ContinuousSwitch create a logic which can react to a change in mode variable and switch to the proper mode tree.
 
**When returning to “patrolling” the NPC must head back to a tagPoint it intended to go to prior to intruder induced interruption. 
 
  
 
'''Extras:'''
 
'''Extras:'''
  
1.&nbsp;&nbsp; &nbsp;Change the logic so that the NPC goes to the nearest tagPoint when switching back to “patrolling” mode<br/> 2.&nbsp;&nbsp; &nbsp;Make the whole behavior save/load-proof. &nbsp;
+
#Make the whole script save/load proof
 
+
#Add a second waveSlave NPC and using Sempahore ensure that out of the 3 wave NPCs only 2 at max can synchronize and play the animation.
''VIDEO UPCOMING SOON''
+
#Instead of aligning with the wavePoints in the waveSlave and waveMaster behaviors prior to synchronization, ensure that the NPCs turn to the NPC they Synchronize with, and that the waveSlave NPC turns to the master only after synchronization.  
 +
#Add another stalker NPC and change the logic of the SmartArea's onUpdate tree so that the master only goes to hiding when both stalkers are inside the area.  
  
== 10.&nbsp;&nbsp; &nbsp;Synchronization ==
+
&nbsp; {{#ev:youtube|57EuqdIOdvA}} &nbsp;
  
Goal: 2 NPCs keep synchronizing and wave at each other at the same time in random intervals.<br/> 1.&nbsp;&nbsp; &nbsp;Create 2 new NPCs ''test_ST_npc4'' and ''test_ST_npc5''. They should stay face to face about 2 meters apart.<br/> 2.&nbsp;&nbsp; &nbsp;Add new behavior to the SA, name it ''waveMaster''.<br/> 1.&nbsp;&nbsp; &nbsp;Add a loop and in random intervals (5-20 seconds) run a Synchronize node:
 
  
*Lock Name = ''testST_Wave'' &nbsp;
 
*LockManagerType = ''Global''
 
*Lock Count = 2
 
  
2.&nbsp;&nbsp; &nbsp;When the synchronize node opens it executes waving animation (see exercise I)<br/> 3.&nbsp;&nbsp; &nbsp;Add new behavior to the SA, name it ''waveSlave''.
 
  
*Add a loop and immediately run a Synchronize node. Set it up identically to the one in ''waveMaster'' behavior.
 
*When the synchronize node opens it also executes the waving animation
 
  
4.&nbsp;&nbsp; &nbsp;Make each NPCs call one of the behaviors.
 
  
'''Extras:'''
 
  
1.&nbsp;&nbsp; &nbsp;Change the logic so that the NPC goes to the nearest tagPoint when switching back to “patrolling” mode<br/> 2.&nbsp;&nbsp; &nbsp;Try to do the same with node-pair ExternalLock a SetExternalLock<br/> 3.&nbsp;&nbsp; &nbsp;Edit the waveSlave behavior:
 
  
*The NPC keeps turning to player (node TurnBody in a loop) and only when the Synchronization happens the NPC turns back to “master” and waves at him. Then returns back to turning to player. &nbsp;
+
== 8. Items, AI LODing, cleanups ==
  
4.&nbsp;&nbsp; &nbsp;Add another NPC with waveSlave behavior. Make sure that only one of these “slave” NPCs react to synchronization by limiting the waving animation via Semaphore node.
+
'''Goal:''' NPCs picks and drops an npc tool, then picks and pockets an apple, a shield, a sword which it also equips, and a book. Then the NPC proceeds to read the book, and observes if the player dropped any item. When that happens the NPC closes the book, draws a weapon, picks the item, and then returns back to reading.
  
''VIDEO COMING SOON''
+
#Place 5 itemSlots in the level. Each will receive one of these items:
 +
#*any [[Database_Documentation|npc_tool]] (eg. iron hoe)
 +
#*an apple
 +
#*any shield
 +
#*any sword
 +
#*any book (eg. necronomicon_part2) 
 +
#Place a tagPoint in the level not too far from the slots. Name it ''readSpot''.
 +
#Create an MBT tree for inclusion. Name it ''reading''. Store the implementation in the SA’s xml file.<br/> Definition of ''reading'':
 +
#*The tree expects ''t_book (type common:wuid)'' variable from the parent tree.
 +
#**Regardless, test for the existence of t_book manually (VariableExistsGate) and copy its value to internal bookItem variable. 
 +
#*Plug the following under a FuseBox node with OneCleanup=true.
 +
#**Child tree:
 +
#**#Clean NPC’s hands from any items.
 +
#**#Instantly put the book in right hand and make the item invisible
 +
#**#PlayAnimation ''ReadingBookIn'' with tag ''book''. In reaction to animation event ''Attach'', make the book item visible.
 +
#**#Play looped animation ''ReadingBook'' with tag ''book'' and wait for interruption. 
 +
#**Cleanup tree:
 +
#**#Play animation ''ReadingBookOut'', tag ''book''
 +
#**#Put the item in inventory instantly as a reaction to the animation event ''Detach''
 +
#**#'''Warning:''' FuseBox OnSuccess and OnFail trees are guaranteed to be executed when a subtree ENDs and take precedence over any other script. By placing anything long-lasting in the cleanup trees you may make the NPC seem stuck and unresponsive.   
 +
#*Safeguard every PlayAnimation node using LODGuardian, LODLock, or LODCheck nodes. 
 +
#Add a new behavior ''pickItems'' to the SA:
 +
##Find the items
 +
##Go and pick up the npc tool using safePickItem behavior from the slot.
 +
##*(The npc_tools require the itemSlot to be a SmartObject of type so_slot)
 +
##*'''Warning:''' you must never put items managed by so_slot SmartObjects into inventory or stashes. Pick, place, and drop are the only safe operations.
 +
##*Drop or place back the npc tool 
 +
##Go and pick up the apple with right hand and put the item into inventory
 +
##Go and pick up the shield with left hand and put the item into inventory
 +
##Go and pick up the sword with right hand, equip it, and holster it
 +
##Go and pick up the book with right hand and put it in inventory
 +
##Establish 2 modes ''read'' (default value) and ''pickStuff''
 +
##Control script for switching between the modes:
 +
###Use OnInventoryEvent.
 +
###Whenever the player drops an item mark it with a link and change the mode to ''pickStuff'' 
 +
##Definition of ''pickStuff'' mode:
 +
##*Use Fusebox
 +
##*Child tree:
 +
##*#Loop
 +
##*#Find a dropped item
 +
##*#Draw a weapon
 +
##*#Pick the item and put it into inventory
 +
##*#End the loop if you don't find any item 
 +
##*Cleanup tree:
 +
##*#Holster the weapon and change the mode to ''read''   
 +
##Definition of ''read'' mode:
 +
###Go to readPoint
 +
###Include the ''reading'' tree   
 +
#Jump in the game and drop some items when the NPC is in the reading phase (you can use the command wh_pl_MagicBox to give the player some more items)
  
== 11.&nbsp;&nbsp; &nbsp;AreaPresence ==
+
'''Newly introduced nodes:'''
 
 
'''Goal:''' NPCs inside an area will run a desired behavior when there’s a specified set of entities inside the area.
 
 
 
1.&nbsp;&nbsp; &nbsp;Place a TriggerArea in the level. Name it ''triggerArea''. Create a link from the SA to the''triggerArea''.<br/> 2.&nbsp;&nbsp; &nbsp;Add a new''thereAndBackAgain'' behavior to the SA:
 
 
 
*Walking between 2 points. One inside and one outside the TriggerArea. Wait at each point for 5-20 seconds. &nbsp;
 
 
 
3.&nbsp;&nbsp; &nbsp;Add a new ''welcomeTraveller'' behavior to the SA:
 
 
 
*I turn to the player and wave at him.
 
 
 
4.&nbsp;&nbsp; &nbsp;Place 4 new NPCs in the level. Place them outside the triggerArea. You will probably need a new NPC brain definition for them.<br/> 5.&nbsp;&nbsp; &nbsp;Make all the NPCs execute the''thereAndBackAgain'' behavior by default<br/> 6.&nbsp;&nbsp; &nbsp;Edit the OnUpdate tree of the SA:
 
 
 
*Find the TriggerArea
 
*Run the AreaPresence node and process its events. &nbsp;
 
*When there’s the player and at least 2 NPCs inside the triggerArea, make the NPCs inside the triggerArea to switch to behavior welcomeTraveller. They will then return to ''thereAndBackAgain''. &nbsp;
 
*The next round of ''welcomeTraveller'' execution will only be possible if the player re-enters the area again. &nbsp;
 
 
 
'''Extras: &nbsp;'''
 
 
 
1.&nbsp;&nbsp; &nbsp;Make the whole logic save/load-proof
 
 
 
''VIDEO COMING SOON''
 
 
 
== 12.&nbsp;&nbsp; &nbsp;Items, IncludeTree, animation slave objects, behavior cleanups ==
 
 
 
'''Goal:''' NPCs picks up a book, a sword, and a shield, and equips the weapons. Then the NPC reads the book. If the player drops an item on the floor the NPC closes the book, draws a weapon, picks the item, and then returns back to reading.
 
 
 
1.&nbsp;&nbsp; &nbsp;Place 2 itemSlots in the level. Each will receive one of these items:
 
 
 
*any book (a document type item with the book model)
 
*any sword
 
 
 
2.&nbsp;&nbsp; &nbsp;Place a tagPoint in the level not too far from the slots. Name it ''readSpot''.<br/> 3.&nbsp;&nbsp; &nbsp;Create an MBT tree for inclusion. Name it ''readBook''. Store the definition in the SA’s xml file. Definition of ''readBook'':
 
 
 
1. Plug everything under a FuseBox node with OneCleanup=true.<br/> 2. Phase 1 (intro):
 
 
 
*Tree expects ''t_book (type common:wuid)'' variable from the parent tree. &nbsp;
 
*Cleanup NPC’s hands from any items.
 
*Go to the ''readSpot'' and align with its orientation
 
*Instantly pick the book from inventory and hide the book item (SetVisibility)
 
*Play animation ''readingBookIn'', tag book
 
*React to animation event Attach, make the book visible when it happens.
 
 
 
<br/> 3. Phase 2 (reading) is a loop<br/> &nbsp;
 
  
*Play animation ''readingBook'', tag book
+
*ExistPath
 
+
*IncludeTree
4. Phase 3 is a cleanup tree. Plug it under a FuseBox as On Success subtree.
+
*HadCheck
 
+
*DoPickUp (also notice the Instant version)
&nbsp;
+
*DoPlace (also notice the Instant version)
 
+
*PutItemInInventory (also notice the Instant version)
*1.&nbsp;&nbsp; &nbsp;Play animation ''reading book out'', tag ''book''¨
+
*PutItemInHand (also notice the Instant version)
*2.&nbsp;&nbsp; &nbsp;Put the item in inventory as a reaction to the animation event ''Detach''
+
*IsWeaponDrawn
*<span class="marker">3.&nbsp;&nbsp; &nbsp;Warning: FuseBox OnSuccess and/or OnFail trees are guaranteed to be executed when a subtree ENDs and take precedence over any other script. By placing anything long lasting in the cleanup trees you may make the NPC seem stuck and unresponsive.</span>
+
*DrawWeapon (also notice the Instant version)
 
+
*HolsterWeapon (also notice the Instant version)
4.&nbsp;&nbsp; &nbsp;Add a new behavior ''pickItems'' to the SA:
+
*OnInventoryEvent
 
+
*SetVisibility
*Find the items
+
*AnimationEventCatch
*Go and pick up the book with right hand and put it in inventory
+
*FuseBox
*Go and pick up the sword in right hand, put it into the inventory and equip it
+
*SuppressFailure
*Then in a loop, do these 2 in parallel:
+
*VariableExistsGate
**include the tree ''readBook''
+
*LODGuardian
**Watch if the player dropped any item. 
+
*LODCheck
*If the player dropped an item interruption occurs. The interruption will work in this way:
+
*LODLock
**''readBook'' tree will be terminated from above. ''readbook'' must cleanup itself.
+
*[https://horbuchkostenlos.de/ hörbuch kostenlos]
**The NPC will draw a weapon, run to the dropped item, picks it up and puts it in the inventory. Repeats for all dropped objects.
 
**If there isn’t any other dropped item, returns back to ''readBook''. 
 
 
 
5.&nbsp;&nbsp; &nbsp;Jump in the game and run the command wh_pl_MagicBox to give the player some items so that you can drop them for the NPC.
 
  
 
'''Extras:'''
 
'''Extras:'''
  
1.&nbsp;&nbsp; &nbsp;Remake the picking of the item by calling the correct behavior ''safePickItem'' from the slot<br/> 2.&nbsp;&nbsp; &nbsp;Make the whole script save/load-proof
+
#Make the whole script save/load-proof  
 
+
#Implement AI LOD for all the other behavior from previous exercises.
''VIDEO COMING SOON''
+
#Handle situations, where the player picks the items before the NPC does. With the exception of the book the behavior must be able to cope any of the items missing.
  
{{KCDNavigation}}
+
&nbsp; {{#ev:youtube|5obdAAstplI}} &nbsp;
  
 
[[Category:Kingdom Come Deliverance]] [[Category:Tutorials]]
 
[[Category:Kingdom Come Deliverance]] [[Category:Tutorials]]

Latest revision as of 07:38, 2 August 2022

Script training


This tutorial is designed to allow any newcomer, with basic programming knowledge, to master all the core principles and systems for scripting in Kingdom Come: Deliverance.


Warning: This tutorial DOES NOT cover how to create an actual functioning modification. As it relies on a newly created level completely independent from the main level (rataje.cry) it allows you to use all features, some of which are limited or completely non-modifiable when creating a modification for the vanilla game. Please follow the documented guidelines for what and how is modifiable.


The tutorial goes through 3 major phases

  1. Basics principles of scripting (via basic NPC scripting)
  2. More complex interactions between NPCs and an NPC and the world
  3. Quest scripting

How to use this tutorial:
Each exercise, marked by a Roman numeral, is or will be accompanied by a video counterpart which shows you how to actually do the exercise. 

However, try to come up with you own solution first. Each exercise lists the nodes you will need. Later (= more complex) exercises DO NOT list the most basic nodes (like GraphSearch, Move, Sequence, Parallel, Success etc.) which are integral part of almost any script.

Each exercise will feature a link for downloading one(!) correct solution - a set of XML files with correct MBT trees, typically behavior definitions. Some scripts may have several possible good solutions often with different downsides and advantages over other. Take your time to explore alternative solution and their different characteristics. Still, even if you have the correct XML files you have to properly setup your level and entities and some basic logic (like behavior calling in the NPC brain). The videos will assist you with that every time.

Save the level and script trees often.

Each exercise ends with an implicit running of the game mode. Simply press Ctrl+G and you will be dropped to the level. Exiting the game mode is done via Shift+Esc.

The tutorial is also designed in such a way that you don’t have to discard the result of the previous exercise. The following exercise either expands on the previous one or creates something new from scratch and the results may coexist.

 

1. New brain, new subbrain, first script

Goal: The NPC plays an animation

  1. Create a new level
  2. Insert entity NPC, name it test_ST_npc1
  3. Create a new soul of the same name and give it to the NPC
  4. Create a new brain for this NPC, and name it test_ST_npc_brain
  5. Create a new subbrain in this brain, and name it test_ST_npc1_mainSB
    Save it in a XML file under folder Data/Libs/AI/_test
    Set it AlwaysActive = true.
  6. Add this set of nodes to the newly created subbrain tree
    1. Nodes in Sequence
      • PlayAnimation with parameter
        • animation = GreetingsUpperBody
        • tags= waveSmall
      • Wait with parameters
        • duration = -1

Newly introduced nodes:

  • Root
  • Sequence
  • PlayAnimation
  • Wait

Extras:

  1. Try to do the following:
    1. Insert a brush
    2. Insert a GeomEntity
    3. Paint some vegetation
    4. Paint some terrain texture
    5. Modify the terrain geometry
    6. Apply a texture to primitive GeomEntity (e.g. sphere)
    7. Insert a prefab
 
 

2. Links, behavior call, movement

Goal: NPC calls a behavior from the SmartArea which makes him move to a tagPoint and play the aimation

  1. Place a tagPoint in the level
  2. Create a link with name destination from the NPC test_ST_npc1 to the tagPoint
  3. Place a SmartArea in the level, name ittest_ST_sa
  4. Create a link with name sa from the NPC to the SmartArea
  5. Create a SmartArea template with nametest_ST_sa
    SmartArea can hold behavior definitions even if the SA has no brain
  6. Create a behavior moveToDestination, held by the SmartArea
    1. Save the XML in file Data/Libs/AI/_test/test_ST_sa.xml
    2. Definition of the behavior:
      1. Find the tagPoint via GraphSearch
      2. Move to the tagPoint
      3. Play the animation from previous exercise
  7. Change the NPCs subbrain tree definition:
    1. Find the SmartArea via GraphSearch
    2. CallBehavior node with parameter BehaviorName= moveToDestination
    3. Wait -1
  8. Generate navmesh  
    1. Place a NavigationArea in the level. Cover the whole level.  
    2. AI -> Request a full NMN rebuild
    3. cvar wh_ai_RecastDebugDraw=1 shows the generated navmesh

Newly introduced nodes:

  • GraphSearch
  • LinkTagFilter
  • Move
  • CallBehavior

Extras:

  1. Add breakpoints to all the nodes and step through the execution, observe the behavior
 
 

3. Advanced Move, Dynamic node values, Distance tests, Gates

Goal: NPC will keep moving towards the player

  1. Create a new behavior on the SmartArea, name it moveToPlayer
  2. Definition of moveToPlayer:
    1. The NPC will test how close to the player he is and will set the Move speed to ‘Walk’ or ‘Run’ depending on the distance.
    2. The Move node will:
      • recalculate the path every 200ms (see Move node documentation)
      • stop the NPC 2 meters from the target (player)
    3. Then play the hand-waving animation
    4. Resume the movement if the player gets too far again
  3. Edit the NPC’s subbrain so that it calls the moveToPlayer behavior

Newly introduced nodes:

  • Parallel
  • Expression
  • Loop
  • DistanceGate
  • VarOperation (Reinit)
  • Success
 
 

4. Guard behavior, Loops, Randomization, Save/Load

Goal: 2 behaviors. NPC walks in a loop. NPC keeps walking between random tagPoints. Both behaviors support save/load.

  1. Add several tagPoints in the level and create looped path for the NPC to follow by creating links between the tagPoints
  2. Create a new behavior on the SA, name it patrol:
  3. Definition of patrol:
    1. The NPC will find the initial tagPoint
    2. Move to the initial tagPoint
    3. Loop
      • If there is another tagPoint linked from the previous tagPoint, go to it
      • Wait at each point for several seconds  
  4. Edit the NPC’s subbrain so that it calls the patrol behavior. Test the behavior.
  5. Add Save/Load Support to the patrol behavior:
    • Info: Saves are created using Ctrl+F5. All AI trees are restarted upon loading so it your responsibility to reconstruct them properly. The only variables which are saved are Brain-scope variables (defined in Brain, accessible from all subbrains) and Persitent variables (that's a variable property, false by default). The rest is reinitialezed upon loading. Links, unless specified otherwise in LinkTagDefinitions.xml, are persistent
    • You will need at least one persistent variable, you will need some IfCondition tests, and you must pay attention to the order of execution of GraphSearch and Move nodes.
  6. Create a new behavior on the SA, name it randomPatrol
  7. Definition of randomPatrol:
    1. The NPC finds a random tagPoint via GraphSearch (it must not be the point where the NPC is standing now)
    2. The NPC goes to the random tagPoint, waits there for a few seconds
    3. Loop it
  8. Create links between the NPC and each tagPoint. You can add a new set of tagPoints or you can reuse the same points used by patrol
  9. Edit the NPC’s subbrain so that it calls the randomPatrol behavior. Test the behavior.
  10. Add Save/Load Support to the randomPatrol behavior.

Newly introduced nodes:

  • IfCondition
  • Nodalyzer
  • NegationOp
  • WUIDFilter
  • VarOperation (PushBack)

Extras:

  1. The patrol can be done in an alternative way. Try it.
    • The tagPoints are linked the same way. But instead of finding the next tagPoint at every step, store the whole loop in an array, and loop through that array. You will need a bit advanced GraphSearch setting. You can then loop through the array using Loop, For, ForEach, While. Try them all.
      Notice: There is a danger you will either not load all the tagPoints or, since the tagPoints are looped, the GraphSearch might attempt to create an infinite array, which would lead to program freeze or crash. To prevent this either limit the GraphSearch depth search or create a test which prevents duplicate array members. The latter can be achieved many ways. One of them is a terminator self-link on the last object.
  2. Try another solution for randomPatrol:
    • Find and store all tagPoints in an array. Then, in a loop, always select a random array member via RandomItem node or via NumericalOperation node using “rand” function.
 
 

5. Link data, Custom link types, Dynamic links

Goal: NPCs select their behavior based on link data. One NPC executes patrol behavior. 2 other NPCs execute randomPatrol behavior and utilize a simple reservation system via dynamic links.

  1. Create a new link definition in LinkTagDefinitions.xml. Define it as name work_ST which can carry data of the type string. (Note: New type definitions require the editor the be restarted)
  2. Place another 2 NPCs in the level, name them test_ST_npc2 and test_ST_npc3
    • Give them souls of the same names
    • Give the NPCs the same brain as that of test_ST_npc1 (brain test_ST_npc_brain)
  3. Create a link from the npc1 to the SA, and name it work_ST[(‘patrol’)]. Note: ‘patrol’ is the data here.
  4. Create a links from the npc2 and npc3 to the SA, and name each work_ST[(‘randomPatrol’)].
  5. Edit the npc subbrain definition so that:
    1. An NPC finds the SA
    2. Stores the link data in a variable
    3. Calls a behavior of the name stored in the variable
  6. Change the link networks required by both behavior so that the SA is the starting reference point, not he NPCs
    • Change the behaviors so that they have proper Origins in their GraphSearches (automatically created variable __area.id)
  7. 2 NPCs are now executing the randomPatrol behavior and thus are at risk of going to the same spot and clipping through one another. Create a simple reservation systems using link reserved. Change the definition of randomPatrol:
    • When searching for next tagPoint:
      • The NPC excludes the tagPoint it's standing on, and those which are reserved.
      • The NPC does a reservation cleanup - deletes any reserved link from the area to a tagPoint.
      • Creates a reserved link from the area to the tagPoint it found.

Newly introduced nodes:

  • AddLink
  • RemoveLink
  • Semaphore
 
 

6. Messages

Goal: The NPC doing the patrol behavior reacts to player entering the SmartArea by inspecting the player's original position.

  1. Create a new custom type in ..\Data\Libs\AI\TypeDefinitions.xml
    • name the type:subtype test_ST:intruder
    • with a sole member "intruder" of the type common:wuid
    • Note: Adding new types requires editor restart
  2. Create a new mailbox template definition
    • Name it intruderST
    • Make it accept the new type test_ST:intruder
  3. Add a brain to the SA
    • name the brain test_ST_sa
    • give it all the types of trees an SA-type brain can handle: OnUpdate/OnEnter/OnLeave/OnRequest/OnRelease
    • save the tree definitions in _test\test_ST_sa.xml (full path ..\Data\Libs\AI\_test\test_ST_sa.xml)
    • Add the node Wait=-1 to onUpdate tree and Success node to other trees. They will fail if left empty.
  4. SA’s onEnter tree definition:
    • when player entered the SA send a message test_ST:intruder with intruder variable carrying the WUID of the intruder (equal to global variable __player in this case) to an NPC doing the patrol behavior.
  5. Edit the patrol behavior:
    • Add the new mailbox (intruderST) to this behavior
    • Create a new variable mode of the type string. Set the default value to “patrolling”. Mode “patrolling” will represent the old patrol logic from previous exercises. Value “inspecting” will represent the new mode, where NPC reacts to an intruder.
  6. Create the “inspecting” logic:
    1. Move to the intruder, run if the intruder is far
    2. After the movement, test if the intruder is still close
      • If the intruder is close:
      1. Play animation ADLG_Emphasis with tag "angry"
      2. Then keep turning to the player for a period of time using TurnBody.
      • If the intruder is far:
      1. Play animation LookingAround (no tags) for several seconds
    3. Then set mode back to “patrolling”
  7. Create a new logic for switching between the behavior modes:
    • Use a ProcessMessage or ReadMessage node to process the messages sent from the SA. Plug it in parallel to the rest of the logic. Don’t forget a Loop; you need to be able to catch and process multiple messages. The reaction to the message is a change of the mode to “inspecting”
    • Using ContinuousSwitch, create a logic which can react to a change in mode variable and switch to the proper mode tree.
    • When returning to “patrolling” the NPC must find the currently nearest TagPoint and continue patrolling from there.

Newly introduced nodes:

  • SendMessageToNPC
  • InstantSendMessageToNPC
  • MultiSendMessageToNPC
  • InstantMultiSendMessageToNPC
  • ReadMessage
  • ProcessMessage
  • ContinuousSwitch
  • Switch
  • DistanceCondition
  • Selector
  • Fail
  • AnimationEndWait
  • StopAnimation
  • TurnBody
  • CategoryFilter
  • RangeSorter

Extras:

  1. Change the logic so that the NPC goes to the nearest tagPoint when switching back to “patrolling” mode
  2. Make the whole behavior save/load-proof.  
 
 

7. Synchronization, External Locks, AreaPresence

Goal: 2 NPCs placed in an area keep synchronizing and wave at each other at the same time in random intervals. Other entities can enter and leave the area and the 2 NPCs react to these events.

  1. Create a regular square-shaped triggerArea
  2. Create 2 new NPCs test_ST_waveMaster and test_ST_waveSlave. Place them inside the triggerArea.
    • They will call behaviors named waveMaster and waveSlave (resp.) from the SmartArea.
  3. Add 2 tag points for each NPC - wavePoint and hidePoint
    • Place the points inside the triggerArea
    • The wavePoints should be placed about 2 meters apart and must be facing one another
  4. Add a new behavior to the SA, name it waveMaster. Defintion of the behavior:
    1. Find the two points - wavePoint and hidePoint
    2. Feature a variable mode with 2 possible values "wave" and "hide" and create a control structure which reacts to changes in mode value (ContinuousSwitch)
    3. hide mode definition:
      • Go to hidePoint and wait
    4. wave mode definition:
      • Move to wavepoint and align with it
      • Loop
        • Random Wait between loop repetitions
        • Wait for synchronization. Synchronize node settings:
          • LockManagerType = Local
          • LockCount = 2
        • When Synchronized, play animation GreetingsUpperBody with tag waveSmall
    5. Have a parallel control script which changes the mode values:
      • Looped ExternalLock stalkerEntered (LockmanagerType=Local). When open, set mode="hide" and lock the ExternalLock.
      • Looped ExternalLock stalkerLeft (LockmanagerType=Local). When open, set mode="wave" and lock the ExternalLock.
  5. Add new behavior to the SA, name it waveSlave. The defition is the same as waveMaster with some exceptions:
    • The NPC uses its own pair of points wavePoint and hidePoint
    • The wave mode lacks random delays between loop repetitions
    • ExternalLocks are named playerEntered and playerLeft
  6. Create a new NPC and name it test_ST_stalker. Place it outside the triggerArea
    • He will call a behavior called stalker from the SmartArea.
  7. Create 2 points for the stalker NPC, which will be used by the behavior.
    • Place them outside the triggerArea
    • Direct trajectory between the points must run across the triggerArea
  8. Add a new behavior to the SA, name it stalker. The NPC will be crossing the triggArea in random intervals. Definition of the behavior:
    1. Find the 2 stalker points
    2. Go between them in a loop
    3. At each stop, play the animation LookingAround for random amount of time
  9. Expand the unused onUpdate tree of SmartArea and turn into an area-intruder observing system:
    1. Find the triggerArea
    2. Store player and stalker NPC in an array
    3. Using AreaPresence node create a script which unlocks the ExternalLocks in waveMaster or waveSlave behaviors when the stalker NPC or player enters or leaves the triggerArea.

Newly introduced nodes:

  • Synchronize
  • ExternalLock
  • SetExternalLock
  • AreaPresence
  • ExactMove

Extras:

  1. Make the whole script save/load proof
  2. Add a second waveSlave NPC and using Sempahore ensure that out of the 3 wave NPCs only 2 at max can synchronize and play the animation.
  3. Instead of aligning with the wavePoints in the waveSlave and waveMaster behaviors prior to synchronization, ensure that the NPCs turn to the NPC they Synchronize with, and that the waveSlave NPC turns to the master only after synchronization.
  4. Add another stalker NPC and change the logic of the SmartArea's onUpdate tree so that the master only goes to hiding when both stalkers are inside the area.
 
 





8. Items, AI LODing, cleanups

Goal: NPCs picks and drops an npc tool, then picks and pockets an apple, a shield, a sword which it also equips, and a book. Then the NPC proceeds to read the book, and observes if the player dropped any item. When that happens the NPC closes the book, draws a weapon, picks the item, and then returns back to reading.

  1. Place 5 itemSlots in the level. Each will receive one of these items:
    • any npc_tool (eg. iron hoe)
    • an apple
    • any shield
    • any sword
    • any book (eg. necronomicon_part2)
  2. Place a tagPoint in the level not too far from the slots. Name it readSpot.
  3. Create an MBT tree for inclusion. Name it reading. Store the implementation in the SA’s xml file.
    Definition of reading:
    • The tree expects t_book (type common:wuid) variable from the parent tree.
      • Regardless, test for the existence of t_book manually (VariableExistsGate) and copy its value to internal bookItem variable.
    • Plug the following under a FuseBox node with OneCleanup=true.
      • Child tree:
        1. Clean NPC’s hands from any items.
        2. Instantly put the book in right hand and make the item invisible
        3. PlayAnimation ReadingBookIn with tag book. In reaction to animation event Attach, make the book item visible.
        4. Play looped animation ReadingBook with tag book and wait for interruption.
      • Cleanup tree:
        1. Play animation ReadingBookOut, tag book
        2. Put the item in inventory instantly as a reaction to the animation event Detach
        3. Warning: FuseBox OnSuccess and OnFail trees are guaranteed to be executed when a subtree ENDs and take precedence over any other script. By placing anything long-lasting in the cleanup trees you may make the NPC seem stuck and unresponsive.
    • Safeguard every PlayAnimation node using LODGuardian, LODLock, or LODCheck nodes.
  4. Add a new behavior pickItems to the SA:
    1. Find the items
    2. Go and pick up the npc tool using safePickItem behavior from the slot.
      • (The npc_tools require the itemSlot to be a SmartObject of type so_slot)
      • Warning: you must never put items managed by so_slot SmartObjects into inventory or stashes. Pick, place, and drop are the only safe operations.
      • Drop or place back the npc tool
    3. Go and pick up the apple with right hand and put the item into inventory
    4. Go and pick up the shield with left hand and put the item into inventory
    5. Go and pick up the sword with right hand, equip it, and holster it
    6. Go and pick up the book with right hand and put it in inventory
    7. Establish 2 modes read (default value) and pickStuff
    8. Control script for switching between the modes:
      1. Use OnInventoryEvent.
      2. Whenever the player drops an item mark it with a link and change the mode to pickStuff
    9. Definition of pickStuff mode:
      • Use Fusebox
      • Child tree:
        1. Loop
        2. Find a dropped item
        3. Draw a weapon
        4. Pick the item and put it into inventory
        5. End the loop if you don't find any item
      • Cleanup tree:
        1. Holster the weapon and change the mode to read
    10. Definition of read mode:
      1. Go to readPoint
      2. Include the reading tree
  5. Jump in the game and drop some items when the NPC is in the reading phase (you can use the command wh_pl_MagicBox to give the player some more items)

Newly introduced nodes:

  • ExistPath
  • IncludeTree
  • HadCheck
  • DoPickUp (also notice the Instant version)
  • DoPlace (also notice the Instant version)
  • PutItemInInventory (also notice the Instant version)
  • PutItemInHand (also notice the Instant version)
  • IsWeaponDrawn
  • DrawWeapon (also notice the Instant version)
  • HolsterWeapon (also notice the Instant version)
  • OnInventoryEvent
  • SetVisibility
  • AnimationEventCatch
  • FuseBox
  • SuppressFailure
  • VariableExistsGate
  • LODGuardian
  • LODCheck
  • LODLock
  • hörbuch kostenlos

Extras:

  1. Make the whole script save/load-proof
  2. Implement AI LOD for all the other behavior from previous exercises.
  3. Handle situations, where the player picks the items before the NPC does. With the exception of the book the behavior must be able to cope any of the items missing.