Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
npc_behaviour [2017/01/25 09:32] – removed stohrendorf | trs:npc_behaviour [2017/11/17 11:19] (current) – stohrendorf | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | {{indexmenu_n> | ||
+ | |||
+ | ====== Non-Player Character Behaviour ====== | ||
+ | |||
+ | ===== Overview ===== | ||
+ | |||
+ | All the Tomb Raider game physics and entity behaviour is hardcoded, with each type ID being associated with some specific sort of behaviour (as Lara, as a boat, as a tiger, as a door, as a boulder, as a lock, etc.). That is, each model refer two internal engine routines — // | ||
+ | |||
+ | Several entity types may share the same collisional and/or control routines — for example, there is one generic collisional routine for almost all enemies, another generic routine for doors, and another one for // | ||
+ | |||
+ | Lara is unique player character, so she has a large set of both //control// and // | ||
+ | |||
+ | < | ||
+ | In original Tomb Raider source code, notation for collisional and state routines follows two different schemes. For Lara, collisional and control routines are called '' | ||
+ | |||
+ | For other entity types, there is more generic scheme: collisional routines are called '' | ||
+ | </ | ||
+ | |||
+ | ===== Entity Scripting ===== | ||
+ | |||
+ | Despite the existence of //script files//, here is no any scripting for entity behaviour, like in most contemporary games. This hardcoding makes it difficult to port the earlier Tomb Raider scenarios to the engines of the later games, which could be desirable with their improved hardware support. While textures, models, and animations can be ported, behaviour cannot be. | ||
+ | |||
+ | However, there is a small change in TR4 and TR5 which indicates that specific entity behaviour can be altered — it’s called //OCB//. It was briefly described in [[trs: | ||
+ | |||
+ | Sometimes OCB is interpreted as a “packed” field with several values incorporated — like teeth spike OCB contain information about their horizontal and vertical orientation, | ||
+ | |||
+ | <note tip> | ||
+ | For list of valid entity OCBs in TR4, you may refer to TRLE User’s Manual, although it was written in a big rush, and thus it lacks many existing OCBs for many entities. There are also fan-made OCB lists which are much more comprehensive. | ||
+ | |||
+ | As for TR5, no proper OCB list exists for its entity types, so it may be considered a big unknown. | ||
+ | </ | ||
+ | |||
+ | However, OCB can’t be seriously called “scripting”, | ||
+ | |||
+ | < | ||
+ | Recent patches for TR4 game engine (used to play custom levels), like //TREP// and //TRNG//, feature some kind of basic scripting functionality. However, there’s still no sign of //real scripting language// in them, and such scripting is basically specifying pre-defined variables to alter entity behaviour, just like OCB does. | ||
+ | </ | ||
+ | |||
+ | ===== Pathfinding ===== | ||
+ | |||
+ | Despite the lack of scripting, the Tomb Raider series does have navigation hints for the Non-Player Characters; those entities that move freely across the maps under the command of the game AI. NPCs find their way in a level by checking “FloorData” collisional functions in the same way Lara does, and also with the help of special data structures which are used for proper pathfinding. | ||
+ | |||
+ | ==== Data Structures ==== | ||
+ | |||
+ | TR engines use three different structures to assist pathfinding. These are //boxes, overlaps, and zones//. Most sectors point to some //box//, the main exceptions being horizontal-portal sectors. Several neighbour sectors may point to the same box. A box is a horizontal rectangle, with corners and height specified; each box also has a pointer into the list of // | ||
+ | Several neighbour sectors may point to the same box. Each box also has a pointer into the list of // | ||
+ | |||
+ | This selection is done with the help of the //zones//. These structures of 6 (TR1) or 10 (TR2-TR5) '' | ||
+ | |||
+ | {{anchor: | ||
+ | === Boxes === | ||
+ | |||
+ | A box is a horizontal rectangle, with corners and height specified. | ||
+ | |||
+ | This is presumably the way //TRLE// creates boxes for each level: | ||
+ | |||
+ | * For each sector, extend one axis until a height difference is reached. | ||
+ | * Then extend this row (or column) perpendicular until another height difference is reached. This is a rectangle with the same height and it //defines a box//. | ||
+ | * Do the same with the other axis first, and you get another box. | ||
+ | * Repeat this process for every sector, maybe extending into neighbor rooms through the portals. | ||
+ | * Make sure that there are no any duplicate boxes. | ||
+ | |||
+ | There are two variations of box structure — one for TR1 and another for TR2 and any other game version. | ||
+ | |||
+ | {{anchor: | ||
+ | <code cpp> | ||
+ | struct tr_box | ||
+ | { | ||
+ | uint32_t Zmin; // Horizontal dimensions in global units | ||
+ | uint32_t Zmax; | ||
+ | uint32_t Xmin; | ||
+ | uint32_t Xmax; | ||
+ | | ||
+ | uint16_t OverlapIndex; | ||
+ | }; | ||
+ | </ | ||
+ | {{anchor: | ||
+ | <code cpp> | ||
+ | struct tr2_box | ||
+ | { | ||
+ | uint8_t Zmin; // Horizontal dimensions in sectors | ||
+ | uint8_t Zmax; | ||
+ | uint8_t Xmin; | ||
+ | uint8_t Xmax; | ||
+ | int16_t TrueFloor; | ||
+ | int16_t OverlapIndex; | ||
+ | }; | ||
+ | </ | ||
+ | The '' | ||
+ | |||
+ | === Overlaps === | ||
+ | |||
+ | This is a set of lists of neighbouring boxes for each box, each member being a '' | ||
+ | |||
+ | Overlaps must be parsed in serial manner, as with // | ||
+ | |||
+ | === Zones === | ||
+ | |||
+ | This is a set of '' | ||
+ | |||
+ | {{anchor: | ||
+ | <code cpp> | ||
+ | struct tr_zone | ||
+ | { | ||
+ | uint16_t GroundZone1_Normal; | ||
+ | uint16_t GroundZone2_Normal; | ||
+ | uint16_t FlyZone_Normal; | ||
+ | uint16_t GroundZone1_Alternate; | ||
+ | uint16_t GroundZone2_Alternate; | ||
+ | uint16_t FlyZone_Alternate; | ||
+ | }; | ||
+ | </ | ||
+ | TR2-5 have similar breakdowns, though they have 4 ground zones: | ||
+ | |||
+ | {{anchor: | ||
+ | <code cpp> | ||
+ | struct tr2_zone | ||
+ | { | ||
+ | uint16_t GroundZone1_Normal; | ||
+ | uint16_t GroundZone2_Normal; | ||
+ | uint16_t GroundZone3_Normal; | ||
+ | uint16_t GroundZone4_Normal; | ||
+ | uint16_t FlyZone_Normal; | ||
+ | uint16_t GroundZone1_Alternate; | ||
+ | uint16_t GroundZone2_Alternate; | ||
+ | uint16_t GroundZone3_Alternate; | ||
+ | uint16_t GroundZone4_Alternate; | ||
+ | uint16_t FlyZone_Alternate; | ||
+ | }; | ||
+ | </ | ||
+ | The ground zones are for NPCs that travel on the ground, while the fly zones are for flying or swimming NPCs. | ||
+ | |||
+ | ==== Moods ==== | ||
+ | |||
+ | Each mobile NPC in TR may be in a certain //mood//. A mood defines the way creature behaves. The way NPCs change their mood is based on several conditions, mainly on certain enemy implementation. | ||
+ | |||
+ | Most obvious example of mood change is wolf, which can sleep, walk slowly, chase Lara and flee. This change of mood is based on Lara’s position — if Lara is found in any of the box that NPC can reach, mood is changed to //chase//, which means that // | ||
+ | |||
+ | There are another examples of mood changes. In TR3, monkeys are calm unless Lara shoots them. However, this is not the case for level 2 (Temple Ruins), where monkeys are hardcoded to chase Lara on start-up. | ||
+ | |||
+ | {{anchor: | ||
+ | ==== Pathfinding algorithm ==== | ||
+ | |||
+ | For most of TR NPCs, when they are in //chase// mood, pathfinding (and eventual attack) may be broken down to several steps: | ||
+ | |||
+ | * Collect all boxes that are in the same zone as the NPC. | ||
+ | * Find a path using // | ||
+ | * Calculate a random point in the intersection of the current box and the next box on the waypoint. | ||
+ | * If NPC is finally shortly at arriving at Lara, try to predict the direction in which Lara is running (with different lookahead distances for different types of NPCs). | ||
+ | * Perform an attack based on aforementioned calculations. | ||
+ | |||
+ | {{anchor: | ||
+ | ===== AI Objects ===== | ||
+ | |||
+ | Since TR3, in addition to pathfinding data structures, there are now special //AI objects//, which are used in a node-like manner, defining specific action, like wandering between two points, guarding specific point or running to specific place in case Lara is around. For example, MP Guards in TR3’s “Area 51” may patrol specific area when they are limited by special // | ||
+ | |||
+ | < | ||
+ | Not every NPC is “taught” to work with AI objects — usually, | ||
+ | </ | ||
+ | |||
+ | Specific set of AI objects and their respective entity type IDs are different across game versions, but types themselves largely remained unchanged from TR3 to TR5. Here are they: | ||
+ | |||
+ | * **AI_GUARD** — Makes the enemy stay on his current position and turn his head, looking left and right, with a 180 degree field of view — so his “viewing cone” is continuously changed, based on current look direction. When Lara gets into his “viewing cone”, default entity behaviour is engaged — for example, MP guards will chase Lara and try to beat her. | ||
+ | * **AI_AMBUSH** — Makes the enemy run to a designated square by dropping an AI_AMBUSH object on the same sector with him, and another AI_AMBUSH on the sector where he should run to. He will do that only if he spots Lara (hence the name). After reaching second AI_AMBUSH point, enemy will switch to default behaviour. Best example is MP Guard in “Area 51” who locks out first secret, if you won’t manage to kill him in time after he noticed you. | ||
+ | * **AI_PATROL1** and **AI_PATROL2** — Makes the enemy patrol specific path between AI_PATROL1 and AI_PATROL2 locations. To make it work, AI_PATROL1 object must be in the same sector with enemy, and AI_PATROL2 must be in the point to which enemy must go. After reaching AI_PATROL2 point, enemy will return to AI_PATROL1 point, and vice versa. It’s also possible to specify another “starting point” for enemy by dropping extra AI_PATROL1 object — then enemy will go to this secondary AI_PATROL1 object just after activation. If enemy spots Lara, he will switch to default behaviour. | ||
+ | * **AI_MODIFY** — When placed in the same sector with // | ||
+ | * **AI_FOLLOW** — Used primarily with friendly NPCs, and makes them wait for Lara and then “lead” her to specific point. For such behaviour, one AI_FOLLOW object must be placed in the same sector as NPC, and second AI_FOLLOW object must be placed on target point. If Lara shoots NPC affected with AI_FOLLOW behaviour, he will abandon it and become hostile. | ||
+ | |||
+ | < | ||
+ | If there is a HEAVYTRIGGER under an AI_AMBUSH or AI_PATROL object, the enemy will activate it only when he gets there. | ||
+ | </ | ||
+ | |||
+ | {{: | ||
+ | |||
+ | As for **LARA_START_POS** AI object, it is used to modify Lara’s level starting position, according to prior //end level trigger action// configuration. That is, at start Lara will be teleported to any existing **LARA_START_POS** AI object with same OCB value as in '' | ||
+ | |||
+ | ==== AI Object IDs ==== | ||
+ | |||
+ | Here are all AI Object type IDs in each TR version which has them: | ||
+ | |||
+ | ^ ^TR3 | ||
+ | |AI_GUARD | ||
+ | |AI_AMBUSH | ||
+ | |AI_PATROL1 | ||
+ | |AI_PATROL2 | ||
+ | |AI_MODIFY | ||
+ | |AI_FOLLOW | ||
+ | |AI_X1 | ||
+ | |AI_X2 | ||
+ | |LARA_START_POS| | ||
+ | |||
+ | ==== AI Data Block in TR4-5 ==== | ||
+ | |||
+ | Beginning with TR4, AI objects are //not kept along with other entities//. Instead, they have their own structure, which is basically simplified [[trs: | ||
+ | |||
+ | The format of AI object structure as follows: | ||
+ | |||
+ | {{anchor: | ||
+ | <code cpp> | ||
+ | struct tr4_ai_object | ||
+ | { | ||
+ | uint16_t TypeID | ||
+ | uint16_t Room; // Room where AI object is placed | ||
+ | | ||
+ | | ||
+ | uint16_t Flags; | ||
+ | | ||
+ | }; | ||
+ | </ | ||