1. Introduction
The NPC weapon scripts implement an NPC holding a pistol-type firearm. With this system, the NPC can identify NPCs on an opposing team, move towards them and shoot at them. Similarly, the NPC can receive shots from other NPCs and non-NPC avatars, and potentially be "killed". NPCs are rezzed by an object which contains an NPC appearance notecard.
2. Construction
The following objects are involved:
2.1 Rezzer object
This object creates the NPCs, and may be rezzed by the RezMela system.
One script serves to rez the NPCs (the NPC rezzer script), but an NPC is rezzed for each non-root prim. The script may also be in a non-root prim. NPC notecards should exist in the prim that contains the script; there may be many of these, and rezzed NPCs will be created from them at random.
The name of the rezzer object needs to contain the name of the faction (team, side) that the NPCs belong to, and the name of the opposing faction, that the NPC will attack. The name must be in the format: "<name>: <faction1>/<faction2>", where <name> is any string, <faction1> is the NPC's own faction, and <faction2> is the opposing faction. For example, if the object is named "NPC rezzer: Red/Blue", the NPCs will fight on the Red side and attack any NPCs on the Blue side.
NPCs will have their names set to the faction name (as forename) followed by a pseudo-random three-digit number (as surname) - for example, "Blue 371". This information is used to determine the faction of other NPCs.
If the description of the object is "debug" when the script initiates, the rezzer will operate in a mode that is suitable for testing and creating NPC appearance notecards, and will not delete itself when RezMela clears NPCs with the "rezDelNPCs" signal. See (5.1) for details.
2.2 NPC weapon
This is a pistol, attached to the NPC's right hand, which contains the following:
2.3 NPC weapon's bullet & bullet dummy
The main bullet should be a small sphere, set to physical, and should contain the main bullet script.
The bullet dummy is fired at the same time as the main bullet. Its name must be exactly "BulletDummy" for collision filtering in targets to work. It is an invisible box prim, sized <3, 0.1, 0.1>, path cut from 0.125 to 0.625 and with maximum square hollow. This shape was found to be best suited for the purpose: to generate a collision event in targets such as buildings, etc, which contain the "damageable objects" script (see here). The script inside this object is primarily there to set 100% buoyancy, temp-on-rez, phantom and die-at-edge status, and to kill the object on impact with anything else.
2.4 Server
This is simply an object containing the server code; its construction is immaterial.
3. Usage
This section describes the way the system works in roughly sequential order.
3.1 Rezzing
The server is rezzed once only, when the scene is first created. NPC rezzers can be rezzed at that time also, and at any other time.
When the NPC rezzer is rezzed, it creates NPCs according to its construction as described in (2.1). After brief handshaking between the NPC and the rezzer to establish each others' identities and inform the NPC of its opposing faction (see (4.)), the NPC will operate autonomously.
On being rezzed, the secondary weapon script receives the current deadlist (see 5.2) from the server.
3.2 NPC behaviour
When idle, the NPC will alternate at random between being inert and searching a target. When searching, the main weapon script requests the secondary script to search for the nearest target - ie the nearest NPC whose name shows them to be an enemy, and which does not appear in the deadlist.
If there is no target, and the NPC is more than 8m away from its originating rezzer, the NPC will move towards a random point near to the rezzer (within 6m).
If there is a target nearby, the NPC main script looks at the distance to that target. If it's more than 30m away, the NPC will run closer to the target for a random time and then search again, moving closer again if necessary. During the repeated search, it may decide on a new target, or acquire a new position for a moving target.
Once within range, the NPC will then take aim. The aiming animation is played (see 5.1) and the NPC walks slightly towards the target. This short walk helps to ensure that the NPC is facing the right way. If the target has become too close (< 5m), the NPC will move away and then repeat this.
If the NPC is the right distance away, the weapon is fired at the target and the NPC stops moving and returns to the idle state.
At all times, the secondary script listens for "death" messages, to keep the deadlist up-to-date. If the dead NPC is the script's owner, the NPC has been hit and is "killed".
When killed, the secondary script triggers the shout animation, stops the NPC from moving and plays a random death animation from the object contents. From then on, there will be no further function from that NPC's scripts.
3.3 Projectiles
Firing the weapon involves the creation near-simultaneously of two objects: the main bullet and the dummy bullet (see 2.3). These are rezzed at the same point and with the same velocity and rotation. There should be no collision between them because the dummy object is phantom. Both objects have 100% buoyancy, so are unaffected by gravity. They will travel together in a straight line until they hit a target.
If the target is an avatar, the main bullet will detect the collision and send a "death" signal to be picked up by the the target (to initiate their death function) and by other NPCs and the server (to update their deadlists).
3.4 Clearing away bodies
Every two seconds, the server removes NPCs that have been dead for more than ten seconds. It will remove them from its own deadlist, although the deadlist carried by NPCs will not reflect the removal.
4. Communications
The following messages are sent and received:
After being created, an NPC will broadcast (from the secondary weapon script using llRegionSay) on channel 29904047 the string "R" followed by the NPC's UUID.
Upon receiving the "R" message, the NPC rezzer will respond via llRegionSayTo with "F" followed by the name of the NPC's opposing faction.
The server will also receive the "R" message and will respond with "L" followed by the deadlist (see 5.3).
On collision with an NPC, the main bullet script sends a chat message on channel 29904047 with llRegionSay consisting of "D" (for "dead") followed by the UUID of the struck NPC. This may be picked up by the NPC's weapon's secondary script, which will initiate a death for that NPC, as well as by other NPCs' scripts and the server.
5. Miscellaneous points
5.1 NPC rezzer debug mode
If the NPC rezzer has "debug" as its description, it can be used for testing and to create avatar notecards. It will not create NPCs on being rezzed, but instead will do so when short-clicked. Long-clicking will cause the clicking avatar's appearance to be saved in a notecard called "NPC avatar data". In addition, it will not delete itself when RezMela clears NPCs with the "rezDelNPCs" signal.
5.2 Aiming animation
Certain built-in animations cause the viewer to ensure that the animated avatar is rotated to match its server-side rotation (normally, there is only a loose connection between the two). This page on the Second Life Wiki lists those animations (the key word there is "target" or "targeting"). It's important to use this to avoid the appearance of the bullet heading towards the target when the avatar is facing a somewhat different direction.
5.2 Deadlist
This is an internal list of the partial UUIDs of deceased NPCs. It is kept by both the weapon secondary scripts and the server. The list enables the weapon's targeting logic to ignore the corpses of previously-killed NPCs - from the system point of view, there is no difference between a dead soldier and a living one.
Because an NPC when rezzed has no knowledge of which NPCs in the field are dead, the server keeps track, and communication between a newly-rezzed NPC and the server will ensure that the NPC is brought up-to-date. From then on, it acquires its own list of deaths.
The string list of dead NPCs consists of the first 5 characters of dead NPCs' UUIDs, each followed by "|". For example, "99fda|a2a41|4767a|" is a list of three partial UUIDs.
The reasons for this are (from the program notes):
We don't need the full UUID, just enough of it to be pretty sure of uniqueness - we'd need half a million NPCs before we had an even chance of a duplicate.
We can squeeze them all into a single 1024-char-max string for messaging (1024 / 6 > 170 potential casualties).
It's cheaper and faster to transmit than using a list. We separate each with "|" so that UUID fragments are separated from each another.
5.3 "NPC rezzer timeout"
If you see this error (via llOwnerSay) from the NPC rezzer, it indicates a lack of response from the NPCs it has rezzed, specifically the "R" signal. The most likely cause is that the NPC is not set up correctly - perhaps they've not been issued with a weapon, or the weapon isn't working for some reason.