Making Bots Respond To Dynamite Planted Events
From Bots-United Wiki
THIS PAGE IS STILL UNDER SOME CONSTRUCTION!
If you already know all there is to know about fake func_constructs, jump down to #Trigger_the_fake_entity_in_the_script
Contents[hide] |
Discussion
In FritzBot ET when an Engineer plants dynamite at a dynamite action, the information is soon passed on to the defending engineer bots who may chose to come to defuse the dynamite. But getting other bots to come along to protect the engineers is a problem. You can always set up camps nearby but these camp actions have to compete for the bots along with all the other active actions elsewhere in the map. And if there are higher priority actions available then the responding engineers will likely be alone.
Now if the defuse action is very important to the map then being able to temporarily disable the competing actions can be of great value. Other classes of nearby bots will then respond to fill the support camps and roams and help fight the other team's engineers who remain to defend their planted dynamite. And likewise the other team's nearby bots might come to their engineers aid.
Method
Now since there is no test available in the aiscript for the "dynamite planted" case, then the work shifts to the map script file. ET provides special "dynamited" and "defused" events for those trigger_objective_info (TOI) entities that target func_constructibles. For TOIs that target plain vanilla one-time dynamitable entities, these events are not supposed to work but tests show that the "dynamited" event may in fact work. The lack of the "defused" event for these non func_constructible cases does create a minor problem which is discussed below.
Once again the communication from the map script file to the aiscript will use a helper fake func_constructible and faketoi. The func_constructible can be 'construct'ed multiple times so it is used rather than a fake func_explosive.
Create the fake entities
First we have to add the create commands to the top of the game_manager spawn section;
game_manager { spawn { // entity to tell aiscript that Gen A has been planted with dynamite create { objflags "2" scriptName "Dynohelper_ent_obj" classname "trigger_objective_info" targetname "Dynohelper_ent_obj" target "Dynohelper_ent" origin "2749 -1533 -55" //////////////////////// mins "-63 -10 0" maxs "63 10 128" health 1000000 spawnflags 17 // AXIS_OBJECTIVE(1) | CUSTOMIMAGE track "Dynohelper_ent" shortname "Dynohelper_ent" eflags 65536 customaxisimage "gfx/limbo/cm_radar_maindoor" model "*16" } create { classname "func_constructible" spawnflags "4" scriptname "Dynohelper_ent" targetname "Dynohelper_ent" origin "2749 -1533 -55" mins "-63 -10 0" maxs "63 10 128" eflags 65536 svflags 1 model "*24" } ...
Trigger the fake entity in the script
Next use a BSP reader to find the name of the generator's trigger_objective_info's scriptname (generator_toi in our example). (There is a BSP reader in the utility menu of Hobbit's waypoint editor).
Now find the TOI's scriptname inside the map script file. If (as in our example) it does not occur in the file, create one with the minimum spawn and death section.
And this generator Gen A's trigger_objective_info (TOI) has to have a dynamited event script sub-block so in our example we code-in the new script block below;
generator_toi { spawn { //add to as needed } dynamited { wm_announce "protect GEN A! Defuse the dynamite!!!" construct Dynohelper_ent //tell the aiscript } defused //NOTE this announcement won't happen as Gen A is not a func_constructible { wm_announce "GEN A Defused!" } death { //add to as needed } }
Note the call inside the dynamited event to "construct Dynohelper_ent". This call to construct the target of our fake func_construct will trigger the corresponding action test in the aiscript.
Add the associated actions to the waypoints
But first we have to add an action-node to the waypoint nav file. I prefer to place faketoi actions hovering immediately above the place where they are logically used. This makes it easier to remember what they are for when flying through the map. But they can be placed anywhere in the map within 512 scaled-inches of a normal node.
Now the trick to adding a fake construct associated action to the waypoints is to add an action that will never attract the attention of the bots. The way we do this is to set the original goal_num to 999 and to never activate the action in the aiscript. That way it is never an active action for the bots to try to do. Nonetheless the fake construct action in the map script will still be able to trigger its action test in the aiscript.
Find the fake entity number
Before we add the associated action we need to know the entity number of the fake_construct.
To find the entity number of the faketoi use the command /faketois in the console. For this to work the 4 lines in bold of the create block at the top of this article must be uncommented as shown. The output of /faketois is a list of all the scripted entities prefixed by their entity numbers in square brackets[ ]. For our fake construct example there will be 2 entities listed one of which will appear as;
[nnn]trigger_objective_info -- Dynohelper_ent_obj
Where nnn is a 2 or 3 digit number.
Place the associated action
So in the game waypoint editor, fly to the place you want the action to be and open the console to add the action_minor_construct;
action_add 2 2 1 nnn 1 0 999
This will show up as a yellow spindle with no spokes (the radius of just 1 is a visual clue to remind you that it is not a normal action). The fourth parameter (nnn) is the entity number that we just found with /faketois.
Correct all other waypoint action entnums
One of the awkward things about scripted entities is they displace most other entities' numbers that are in the BSP file. Consequently all the already waypointed objective actions (build, dyno, satchel, flag, steal), fixed mg42 camp actions, script movers (tank truck...), and nodes with matching entnum values for capture flags have to be verified and corrected. Failure to do so means FritzBot ET may crash.
Action tests in the aiscript
For a one time destructible add an action test for the associated action (#127 in our example).
action 127 //dynamite planted at a func_destructible { //if_construct_built_true 127 (the test has no effect, just here for clarity) deactivateAction_group 9 //focus the bots in the area near the dynamite //since the script file can not tell us if the dynamite got defused //re-activate the group after a suitable delay wait 35 if_action_false 69 //did the dynamite not blow up? activateAction_group 9 }
Note, because ET does not support the defuse event for func_destructible map objects, we have to code the action test using some assumptions. One of those assumption that may prove wrong is that dynamite only gets planted every minute or so. So should a second bundle of dynamite get planted before the 35 seconds of the wait 35 has expired then action group 9 will be re-activated early.
And to be on the safe side we add a 'deactivateAction_group 9' to the action test for action 69 (in our example).
action 69 //Gen A blows the door { ... deactivateAction_group 9 //in case it got wrongly re-activated ... }
BTW for whatever reason the if_construct_built_true/if_construct_built_false tests do not appear to work properly on these fake constructs actions.
Defuse actions for constructibles
... MORE TO DO
Script file final clean up
Finally, cleanup any unused or unneeded code in the script file that you may have added for debugging. Add a revision comment at the top of the script file and don't forget to comment out the 4 properties no longer needed in the trigger_objective_info of your fake entity (otherwise an icon will appear in the command map).
// Original Map by.... // // Revision: Fake entity pair added for Fritzbot ET dynamite planted trigger to the aiscript // MyHandle, Month DD, Year game_manager { spawn { // entity to tell aiscript that Gen A has been planted with dynamite create { objflags "2" scriptName "Dynohelper_ent_obj" classname "trigger_objective_info" targetname "Dynohelper_ent_obj" target "Dynohelper_ent" origin "2749 -1533 -55" //////////////////////// mins "-63 -10 0" maxs "63 10 128" health 1000000 // spawnflags 17 // AXIS_OBJECTIVE(1) | CUSTOMIMAGE track "Dynohelper_ent" shortname "Dynohelper_ent" // eflags 65536 // customaxisimage "gfx/limbo/cm_radar_maindoor" // model "*16" } ...
Limitations
Since dynamite has a 30 second timer and it may take the bots 2-3 seconds to chose to return to a nearby camp, this method really should only be used where the bots you want to respond are 10-15 seconds away (although you could also use it to get far away bots back to defensive positions that will need to be manned if the dynamite blows up). The method does require between one and two action tests in the aiscript so a map already at the maximum number of action tests can not incorporate this method. And scripted entities do put greater demands on the PC than those compiled in the BSP file so be on the lookout for any stability problems that may indicate a need for more memory or power.
Other Ideas For Dynamited Event
This comes thanks to a strategy suggestion from xgzoq.
Often, a map has an objective (like a dynamite) that can be approached from both sides, or a map with 2 obstacles separating the attacking team from the match objectives. Typically the waypoints would split the defense and split the attack. The bots then randomly chose which way and eventually a situation occurs where the numbers and classes favor the attackers. But a human team might cooperate more and mass attack one objective or side more, switching to the alternate option when lack of success leads to frustration. So how to simulate a team frustration in bots?
Since we can detect when dynamite is planted, we can count these events. Then compare that number to a value that represents say a maximum bot patience level.
For example add a fake func_explosive and an accumulator to the game_manager.
game_manager { spawn { . . . create //a one-time flag to change strategy { scriptName "backsidenow" classname "func_explosive" targetname "backsidenow" origin "1850 -150 650" mins "-63 -10 0" maxs "63 10 128" health 1000000 spawnflags 1 // EXPLOSIVE_START_INVIS(1) eflags 65536 // EF_FAKEBMODEL svflags 1 // SVF_NOCLIENT } . . . //dynamite countdown globalaccum 7 random 3 globalaccum 7 inc 1 //1 to 3 plants before switching to a different strategy //printglobalaccum 7 //debug
Then decrement this accumulator in the dynamite event and conditionally test it;
maindoor1_trig //example taken from v1rocket_b2, trigger_objective_info scriptname for main gate. { spawn { //And to save using a globalaccum, use a local accum initialized here instead //accum 7 random 3 //accum 7 inc 1 //1..3 //printaccum 7 //debug } dynamited { togglespeaker maindooralarm globalaccum 7 inc -1 //change main gate strategy? TomTom globalaccum 7 abort_if_not_equal 0 //must only alertentity func_explosives one-time print "Attack Plan Baker NOW!" wm_teamvoiceannounce 2 sound/chat/allies/27a.wav // 'where to?' alertentity backsidenow //construct helper_ent... //uncomment if func_explosive not targeted by its own toi //(e.g. if piggybacked multiple func_explosives on a single fake construct-toi pair helper_ent... } }
And finally restart the map, open the console, and use /faketois to find the ent number of the new func_explosive (backsidenow in the example) and add an action_event_explode to the .nav file using that ent number. Set the action as active forever, and goal = 0. Then test if_action_true in the aiscript for this action so as to activate the other strategy groups.