<li><aclass="backlink"title="Click to do a full-text search for this title"href="./Scripting_ScriptsAndEntities?action=fullsearch&value=linkto%3A%22Scripting+ScriptsAndEntities%22&context=180">Scripting ScriptsAndEntities</a></li>
<spanclass="anchor"id="line-2"></span><spanclass="anchor"id="line-3"></span><pclass="line867"><imgclass="attachment"src="./Scripting_ScriptsAndEntities?action=AttachFile&do=get&target=repairbot1.jpg"title="attachment:repairbot1.jpg"/><spanclass="anchor"id="line-4"></span><spanclass="anchor"id="line-5"></span><pclass="line867"><strong>Introduction</strong><spanclass="anchor"id="line-6"></span><spanclass="anchor"id="line-7"></span><pclass="line867"><aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bots</a> opened up some fun options for scripted behavior in Quake 4. They serve more as an ambient set piece than an actual enemy and can add a lot of life to a scene. Rather than scripting this ambience for each scene, we created a common entity-based script function that we can call on any <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a>, allowing it to randomly move from one point to the next, ‘repairing’ all the way. <spanclass="anchor"id="line-8"></span><spanclass="anchor"id="line-9"></span><pclass="line862">In this guide we won’t cover the use of the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> script (though you can find implementation info in the .script file comments and some game levels and probably will still know how to use it by the time we’re done), but rather use it as a detailed example of how we used a common script to create a flexible system that can be added to a map with no (additional) scripting required. <spanclass="anchor"id="line-10"></span><spanclass="anchor"id="line-11"></span><pclass="line874">This guide assumes you are familiar with the basics of the scripting system and the level editor. <spanclass="anchor"id="line-12"></span><spanclass="anchor"id="line-13"></span><pclass="line867"><strong>The Function</strong><spanclass="anchor"id="line-14"></span><spanclass="anchor"id="line-15"></span><pclass="line862">Before we cover anything, let’s touch on the order of events within the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> function: <spanclass="anchor"id="line-16"></span><spanclass="anchor"id="line-17"></span><ul><li><pclass="line862">Spawn <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a>. <spanclass="anchor"id="line-18"></span></li><li>Select a random target from an available list. <spanclass="anchor"id="line-19"></span></li><li><pclass="line862">Check if another <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> is using that position. <spanclass="anchor"id="line-20"></span></li><li>If the position is occupied, select a new random target until finding one that is unoccupied. <spanclass="anchor"id="line-21"></span></li><li><pclass="line862">Flag the target as occupied so no other <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bots</a> can select it. <spanclass="anchor"id="line-22"></span></li><li>Move to the target. <spanclass="anchor"id="line-23"></span></li><li>Look at a specific point of interest <spanclass="anchor"id="line-24"></span></li><li>Perform the ‘repair’ action <spanclass="anchor"id="line-25"></span></li><li>Mark the current target as unoccupied <spanclass="anchor"id="line-26"></span></li><li>Select a new target and repeat <spanclass="anchor"id="line-27"></span></li><li><pclass="line862">If at any point the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> dies, set the current target as unoccupied and break the function <spanclass="anchor"id="line-28"></span><spanclass="anchor"id="line-29"></span></li></ul><pclass="line874">This ends up looking more complicated than it really is, so even if you’re not that familiar with the script system, don’t worry – if it makes you feel any better, this was my first script for the game! <spanclass="anchor"id="line-30"></span><spanclass="anchor"id="line-31"></span><pclass="line874">Through the next two sections (Entities and Script), we’ll look at how we achieve each of these event
<spanclass="anchor"id="line-94"></span></pre><spanclass="anchor"id="line-95"></span><spanclass="anchor"id="line-96"></span><pclass="line862">You’ll recognize this as the script function set on the <ahref="./Entity_FuncSpawner">func_spawner</a>. It might seem redundant to just thread another function, but this is actually extremely important – by threading the second function, we ensure that all the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bots</a> running this function simultaneously can run parallel with no issues. <spanclass="anchor"id="line-97"></span><spanclass="anchor"id="line-98"></span><pclass="line867"><spanclass="anchor"id="line-99"></span><pre>void botActions( entity botName ) {
<spanclass="anchor"id="line-100"></span></pre><spanclass="anchor"id="line-101"></span><spanclass="anchor"id="line-102"></span><pclass="line862">When the function starts, the only entity it knows is our <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a>, threaded from randomBots. <spanclass="anchor"id="line-103"></span><spanclass="anchor"id="line-104"></span><pclass="line867"><spanclass="anchor"id="line-105"></span><pre>// Define the entities that will be used for random target selection
<spanclass="anchor"id="line-113"></span></pre><spanclass="anchor"id="line-114"></span><spanclass="anchor"id="line-115"></span><pclass="line862">This is the single most important line in this script. With this one line, we’ve already made the difference between a static script and one that can be used anywhere. By pulling a keyvalue from an entity that is already a local variable, we know that any <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> can gain access to any list of entities we choose. <spanclass="anchor"id="line-116"></span><spanclass="anchor"id="line-117"></span><pclass="line862">We’ve also opened our script wide open for gaining information from other entities. Now that the list <ahref="./Entity_TargetNull">target_null</a> is stored, we have access to every entity that may be specified on the list as well as any keys or targets those entities may store. By keeping our entities as local variables to the function rather than relying on scripting them by name, we’ve already ensured that this function can be used anywhere with any entity list provided it follows the structure we established originally. <spanclass="anchor"id="line-118"></span><spanclass="anchor"id="line-119"></span><pclass="line874">Let’s keep looking at ways we can access and fiddle with this information. <spanclass="anchor"id="line-120"></span><spanclass="anchor"id="line-121"></span><pclass="line867"><spanclass="anchor"id="line-122"></span><pre> // Perform these actions as long as the Repair Bot lives.
<spanclass="anchor"id="line-124"></span></pre><spanclass="anchor"id="line-125"></span><spanclass="anchor"id="line-126"></span><pclass="line862">We’ll drop this in a while loop –<aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bots</a> are somewhat flimsy, so this will help us break out if one should come across an untimely end, and also end the function if the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> is removed from the map by other means. Quick Aside: If you’re using entities that don’t explode (particularly any that don’t burn out such as the marines), you’ll want to use isLivingEntity instead – that one includes an extra check for health, as dead/ragdoll creatures may still count as ‘valid’ and cause problems. <spanclass="anchor"id="line-127"></span><spanclass="anchor"id="line-128"></span><pclass="line867"><spanclass="anchor"id="line-129"></span><pre> // Select a new move target at random from the list of target_nulls
<spanclass="anchor"id="line-131"></span></pre><spanclass="anchor"id="line-132"></span><spanclass="anchor"id="line-133"></span><pclass="line874">Now we’re ready to set our repairTarget keyvalue. Remember that previously we referred to our available move points as repairTargets – same applies here. Using the qListRandom function, we’re able to select an entity at random from our list of available entities. <spanclass="anchor"id="line-134"></span><spanclass="anchor"id="line-135"></span><pclass="line862">We could also get away with repairTarget.randomTarget(), but qListRandom (a handy <aclass="nonexistent"href="./ScriptUtility">ScriptUtility</a>) does some extra checks for us and adds handling in the event that one of our list entities is removed. <spanclass="anchor"id="line-136"></span><spanclass="anchor"id="line-137"></span><pclass="line867"><spanclass="anchor"id="line-138"></span><pre> // Check against the previous target, and select a new one if the old target repeats itself.
<spanclass="anchor"id="line-143"></span></pre><spanclass="anchor"id="line-144"></span><spanclass="anchor"id="line-145"></span><pclass="line862">Now it’s time to bring the “occupied” key into the mix. We know that we don’t want two <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bots</a> to overlap at the same point, so before we do anything we check that key. As long as the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> finds repairTargets that are occupied, it will select a new target until it finds one with the “occupied” key set to 0. <spanclass="anchor"id="line-146"></span><spanclass="anchor"id="line-147"></span><pclass="line867"><spanclass="anchor"id="line-148"></span><pre> // We found a valid target! Pull the action target from the move target.
<spanclass="anchor"id="line-150"></span></pre><spanclass="anchor"id="line-151"></span><spanclass="anchor"id="line-152"></span><pclass="line874">Once we have a target, we can use the getTarget function to pull the associated entity into the script. Using targets instead of keys can be quicker in setting up your entities (thanks to a handy Editor Shortcut), but remember that getTarget takes a float parameter to specify which target to select. getTarget(0) will select the “target” entity, but getTarget(4) will select the “target4” entity. Be very careful with this if storing more than one target per entity or if you’ve changed targets when it might be easy to accidentally get a “target1” key. <spanclass="anchor"id="line-153"></span><spanclass="anchor"id="line-154"></span><pclass="line867"><spanclass="anchor"id="line-155"></span><pre> // Set target as occupied to prevent other repair bots from using it
<spanclass="anchor"id="line-157"></span></pre><spanclass="anchor"id="line-158"></span><spanclass="anchor"id="line-159"></span><pclass="line862">In the case of the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> function, I used a threaded function to toggle occupied status – this was a result of some debugging for an alternate setup and it worked, so it was never changed. In most cases, though, a simple entity.setKey should be appropriate. <spanclass="anchor"id="line-160"></span><spanclass="anchor"id="line-161"></span><pclass="line874">Now that we have all our entities set up properly, let’s see how it all comes together for the meat of the function: <spanclass="anchor"id="line-162"></span><spanclass="anchor"id="line-163"></span><pclass="line867"><spanclass="anchor"id="line-164"></span><pre> // Identify action type (repair target or kill target).
<spanclass="anchor"id="line-187"></span></pre><spanclass="anchor"id="line-188"></span><spanclass="anchor"id="line-189"></span><pclass="line874">You’ll notice first off that this entire chunk of script is an if conditional checking to see if repairAction is a valid entity. By not including targets for two of the repairTargets, I was able to include two very different behaviors at certain points. <spanclass="anchor"id="line-190"></span><spanclass="anchor"id="line-191"></span><pclass="line874">Also note the abundance of isValidEntity checks. These are very important when using any version of a ‘wait’ function on entities that can be killed, and at the very least spare you a lot of console warnings getting dumped (which also would prevent the <spanclass="anchor"id="line-192"></span><spanclass="anchor"id="line-193"></span><pclass="line862">Otherwise the behavior is exactly as we outlined. The <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> moves to the repairTarget point, looks at the repairAction entity, performs the action specified on repairTarget, and then sets its repairTarget position back to unoccupied status. By some careful entity and script setup, we can get away with writing this chunk of script once. Imagine, in contrast, writing this for each <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> and each repairTarget in sequence. <spanclass="anchor"id="line-194"></span><spanclass="anchor"id="line-195"></span><pclass="line867"><spanclass="anchor"id="line-196"></span><pre> // Move targets with no target are kill zones which should remove the repair bot. Handle those here.
<spanclass="anchor"id="line-212"></span></pre><spanclass="anchor"id="line-213"></span><spanclass="anchor"id="line-214"></span><pclass="line862">Now we see the alternate behavior I specified for the two repairTarget positions that did not include repairAction targets. These points were hidden out of view and designed to keep fresh <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bots</a> cycling into the scene – also allowing the spawner to be turned off and eventually leave the zone empty. <spanclass="anchor"id="line-215"></span><spanclass="anchor"id="line-216"></span><pclass="line862">It’s another straightforward function here, but instead of performing an action, the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> moves to the repairTarget, sets the repairTarget as unoccupied, and removes itself. The removeUpdateSpawner function in this case was very important, as simply removing an entity caused the <ahref="./Entity_FuncSpawner">func_spawner</a> to ‘forget’ to spawn more and killing them resulted in (as I’m sure you can imagine by my humorous commentary) an awkward explosion. <spanclass="anchor"id="line-217"></span><spanclass="anchor"id="line-218"></span><pclass="line867"><spanclass="anchor"id="line-219"></span><pre> // Define the old target for comparison against the new target.
<spanclass="anchor"id="line-223"></span></pre><spanclass="anchor"id="line-224"></span><spanclass="anchor"id="line-225"></span><pclass="line862">This last line was from a previous version that also checked to make sure the <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> selected a new target and did not repeat the same target twice. In practice, it ended up not being a large concern, but you could run similar checks and comparisons by storing your variables in a similar fashion. <spanclass="anchor"id="line-226"></span><spanclass="anchor"id="line-227"></span><pclass="line862">That concludes our examination of the common <aclass="nonexistent"href="./Entity_MonsterRepairBot">Repair Bot</a> script! Hopefully you’ve seen not only how to pull information into the script for flexible use, but also why much of this function was set up as it was. While this is more of a commentary on an existing example rather than a tutorial, hopefully you can take the information demonstrated here and put it to good use in your own sequences. Good luck! <spanclass="anchor"id="line-228"></span><spanclass="anchor"id="bottom"></span></div><pid="pageinfo"class="info"lang="en"dir="ltr">Scripting ScriptsAndEntities (last edited 2005-11-02 17:15:42 by <spantitle="AndrewWeldon @ 67.129.250.254[67.129.250.254]"><aclass="nonexistent"href="./AndrewWeldon"title="AndrewWeldon @ 67.129.250.254[67.129.250.254]">AndrewWeldon</a></span>)</p>
<li><ahref="http://moinmoin.wikiwikiweb.de/">MoinMoin Powered</a></li><li><ahref="http://www.python.org/">Python Powered</a></li><li><ahref="http://validator.w3.org/check?uri=referer">Valid HTML 4.01</a></li>