Files
Cesarus85--mjolnir-vr/source/ai/encounters.c
T

1942 lines
57 KiB
C

/*
ENCOUNTERS.C
symbols in this file:
00047520 00f0:
_encounters_initialize (0000)
00047610 0010:
_encounters_dispose (0000)
00047620 0020:
_encounters_dispose_from_old_map (0000)
00047640 04b0:
_encounter_compute_activation_cluster_bit_vector (0000)
00047AF0 01b0:
_encounter_detach_actor (0000)
00047CA0 0110:
_encounter_attach_unit (0000)
00047DB0 00b0:
_encounterless_attach_actor (0000)
00047E60 0140:
_encounterless_detach_actor (0000)
00047FA0 0060:
_encounter_get_by_name (0000)
00048000 0030:
_encounter_iterator_new (0000)
00048030 0040:
_encounter_iterator_next (0000)
00048070 0050:
_encounter_actor_iterator_new (0000)
000480C0 0040:
_encounter_actor_iterator_next (0000)
00048100 0080:
_encounter_actor_iterator_prev (0000)
00048180 0040:
_actor_iterator_new (0000)
000481C0 00a0:
_actor_iterator_next (0000)
00048260 0050:
_code_00048260 (0000)
000482B0 00f0:
_code_000482b0 (0000)
000483A0 00a0:
_encounter_modify_pursuit_desires (0000)
00048440 0280:
_encounter_determine_pursuit_availability (0000)
000486C0 00d0:
_code_000486c0 (0000)
00048790 0290:
_code_00048790 (0000)
00048A20 0080:
_code_00048a20 (0000)
00048AA0 0060:
_encounterless_activate (0000)
00048B00 0050:
_code_00048b00 (0000)
00048B50 00c0:
_code_00048b50 (0000)
00048C10 00a0:
_encounter_link_activation (0000)
00048CB0 00a0:
_code_00048cb0 (0000)
00048D50 03d0:
_code_00048d50 (0000)
00049120 01b0:
_encounter_stand_down (0000)
000492D0 0090:
_code_000492d0 (0000)
00049360 0070:
_code_00049360 (0000)
000493D0 0030:
_encounter_set_blind (0000)
00049400 0030:
_encounter_set_deaf (0000)
00049430 00b0:
_encounter_squad_timer_expire (0000)
000494E0 0100:
_code_000494e0 (0000)
000495E0 0290:
_code_000495e0 (0000)
00049870 00a0:
_encounters_initialize_for_new_map (0000)
00049910 00d0:
_encounters_unit_died (0000)
000499E0 0140:
_encounter_verify_firing_position_owner_actor_indices (0000)
00049B20 0130:
_encounter_build_firing_position_owner_actor_indices (0000)
00049C50 0100:
_encounter_mark_examined_pursuit_position (0000)
00049D50 00b0:
_encounter_pursuit_position_already_examined (0000)
00049E00 02e0:
_encounter_get_actor_starting_location (0000)
0004A0E0 0030:
_encounter_force_activate (0000)
0004A110 0030:
_encounter_force_deactivate (0000)
0004A140 0110:
_code_0004a140 (0000)
0004A250 07c0:
_code_0004a250 (0000)
0004AA10 0170:
_code_0004aa10 (0000)
0004AB80 0120:
_encounter_spawn_actor (0000)
0004ACA0 0050:
_encounter_set_respawn (0000)
0004ACF0 02c0:
_code_0004acf0 (0000)
0004AFB0 0140:
_code_0004afb0 (0000)
0004B0F0 0780:
_code_0004b0f0 (0000)
0004B870 0220:
_encounter_attach_actor (0000)
0004BA90 0470:
_encounter_update_status (0000)
0004BF00 0080:
_encounters_update_dirty_status (0000)
0004BF80 02f0:
_encounter_create (0000)
0004C270 01c0:
_code_0004c270 (0000)
0004C430 00c0:
_encounters_create_for_new_map (0000)
0004C4F0 0100:
_encounters_update (0000)
0024CDBC 0018:
_global_empty_possibility (0000)
_global_post_combat_translation_table (0010)
0024CDD4 000d:
??_C@_0N@BNPPEOCF@pursuit_data?$AA@ (0000)
0024CDE4 000b:
??_C@_0L@BAANOOMI@ai?5pursuit?$AA@ (0000)
0024CDF0 000e:
??_C@_0O@CIMAAJHO@platoon_array?$AA@ (0000)
0024CE00 0008:
??_C@_07GONBCAAG@platoon?$AA@ (0000)
0024CE08 000c:
??_C@_0M@EHCGPGM@squad_array?$AA@ (0000)
0024CE14 0006:
??_C@_05BDFMLHFG@squad?$AA@ (0000)
0024CE1C 000f:
??_C@_0P@MECKMOOF@encounter_data?$AA@ (0000)
0024CE2C 001f:
??_C@_0BP@EGKKNGMH@c?3?2halo?2SOURCE?2ai?2encounters?4c?$AA@ (0000)
0024CE50 0058:
??_C@_0FI@BMEIILPH@?$CImove_position?9?$DOcluster_index?5?$DO?$DN@ (0000)
0024CEA8 005c:
??_C@_0FM@DFHIGNCO@?$CIfiring_position?9?$DOcluster_index?5@ (0000)
0024CF04 003a:
??_C@_0DK@EFAAMOOO@?$CIcluster_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIcluster@ (0000)
0024CF40 0031:
??_C@_0DB@KHGAFALI@structure_bsp?9?$DOclusters?4count?5?$DM?$DN@ (0000)
0024CF74 001c:
??_C@_0BM@ONCBDDBG@platoon?9?$DOoriginal_count?5?$DO?50?$AA@ (0000)
0024CF90 001a:
??_C@_0BK@NALIBEDP@squad?9?$DOoriginal_count?5?$DO?50?$AA@ (0000)
0024CFAC 001e:
??_C@_0BO@FMKIINLL@encounter?9?$DOoriginal_count?5?$DO?50?$AA@ (0000)
0024CFCC 001d:
??_C@_0BN@DOEKPEEH@?$CKactor_index_reference?$CB?$DNNONE?$AA@ (0000)
0024CFEC 0025:
??_C@_0CF@FBFKAFPA@actor?9?$DOmeta?4unit_index?5?$DN?$DN?5unit_i@ (0000)
0024D014 002f:
??_C@_0CP@NCMOENFD@actor?9?$DOmeta?4encounter_index?5?$DN?$DN?5e@ (0000)
0024D044 001b:
??_C@_0BL@OIOHEOEO@?$CBactor?9?$DOmeta?4encounterless?$AA@ (0000)
0024D060 0022:
??_C@_0CC@PHCDCJEH@actor?9?$DOmeta?4encounter_index?$DN?$DNNON@ (0000)
0024D084 0022:
??_C@_0CC@LJBHAGND@actor?9?$DOmeta?4platoon_index?5?$DN?$DN?5NON@ (0000)
0024D0A8 0020:
??_C@_0CA@HNKMILAG@actor?9?$DOmeta?4squad_index?5?$DN?$DN?5NONE?$AA@ (0000)
0024D0C8 0024:
??_C@_0CE@BMJAJPHM@actor?9?$DOmeta?4encounter_index?5?$DN?$DN?5N@ (0000)
0024D0F0 005d:
??_C@_0FN@LOCOBBFO@WARNING?3?5too?5many?5actors?5searchi@ (0000)
0024D150 003d:
??_C@_0DN@ECMJNADN@?$CFs?5?$CF04X?3?5coord?5?$CFd?5current?5?$CFd?1?$CFd?1@ (0000)
0024D190 0029:
??_C@_0CJ@GDIDEEDO@overflowed?5MAXIMUM_PLATOONS_PER_@ (0000)
0024D1C0 0047:
??_C@_0EH@JLNGBIIA@encounter_definition?9?$DOplatoons?4c@ (0000)
0024D208 0027:
??_C@_0CH@CNGIJGEJ@overflowed?5MAXIMUM_SQUADS_PER_MA@ (0000)
0024D230 0043:
??_C@_0ED@JFKFDEJF@encounter_definition?9?$DOsquads?4cou@ (0000)
0024D278 0063:
??_C@_0GD@GOGHLEAJ@?$CIlink_encounter_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CI@ (0000)
0024D2DC 0036:
??_C@_0DG@EPEOFOPA@parent_prop?9?$DOorphan_prop_index?5?$DN@ (0000)
0024D314 001a:
??_C@_0BK@GDHJJECC@?$CBencounter?9?$DOenemy_visible?$AA@ (0000)
0024D330 001c:
??_C@_0BM@IJGMLGCM@?$CFs?1?$CFs?3?5delay?5timer?5finished?$AA@ (0000)
0024D34C 0026:
??_C@_0CG@HJILMOOD@?$CFs?1?$CFs?3?5delay?5timer?5started?5?$CI?$CF?41f@ (0000)
0024D374 0011:
??_C@_0BB@HBMEBMNH@survivors?5?$CFd?5?$DN?50?$AA@ (0000)
0024D388 0012:
??_C@_0BC@FNKOIBFL@survivors?5?$CFd?5?$DM?$DN?51?$AA@ (0000)
0024D39C 0021:
??_C@_0CB@HJKPDMEB@survivors?5?$CFd?5?$DM?$DN?575?$CF?$CF?5of?5total?5?$CFd@ (0000)
0024D3C0 0021:
??_C@_0CB@NHMKHBJA@survivors?5?$CFd?5?$DM?$DN?550?$CF?$CF?5of?5total?5?$CFd@ (0000)
0024D3E4 0021:
??_C@_0CB@INABNAMN@survivors?5?$CFd?5?$DM?$DN?525?$CF?$CF?5of?5total?5?$CFd@ (0000)
0024D408 0018:
??_C@_0BI@IGDFHOAI@survivors?5?$CFd?5?$DM?5total?5?$CFd?$AA@ (0000)
0024D420 0015:
??_C@_0BF@FJJAHOMB@strength?5?$CF?42f?5?$DM?525?$CF?$CF?$AA@ (0000)
0024D438 0015:
??_C@_0BF@NMGOFCOD@strength?5?$CF?42f?5?$DM?550?$CF?$CF?$AA@ (0000)
0024D450 0015:
??_C@_0BF@JBHAPBLB@strength?5?$CF?42f?5?$DM?575?$CF?$CF?$AA@ (0000)
0024D468 004a:
??_C@_0EK@PHDJOLGD@owner_actor_indices?$FLactor?9?$DOfirin@ (0000)
0024D4B8 0093:
??_C@_0JD@GAKCCOHE@actor?9?$DOfiring_positions?4current_@ (0000)
0024D550 005a:
??_C@_0FK@DNNFPKMF@firing_position_owner_actor_indi@ (0000)
0024D5B0 0084:
??_C@_0IE@GJHEMMJI@?$CIpursuit?9?$DOnext_actor_index_index@ (0000)
0024D634 0038:
??_C@_0DI@GONNFCHM@pursuit?9?$DOfiring_position_index?5?$DN@ (0000)
0024D66C 0014:
??_C@_0BE@MIHACDDA@found_index?5?$CB?$DN?5NONE?$AA@ (0000)
0024D680 0035:
??_C@_0DF@FCNHHGJA@BIT_VECTOR_TEST_FLAG?$CIsquad?9?$DOunus@ (0000)
0024D6B8 0063:
??_C@_0GD@GIHDEKAB@?$CIselected_behavior_index?5?$DO?$DN?50?$CJ?5?$CG@ (0000)
0024D720 009d:
??_C@_0JN@COMBHAJN@?$CIprimary_postcombat_behaviors?$FLpr@ (0000)
0024D7C0 0004:
__real@3b888889 (0000)
0024D7C8 006c:
??_C@_0GM@MFIMGOEE@WARNING?3?5cannot?5spawn?5actors?5in?5@ (0000)
0024D834 0022:
??_C@_0CC@EIGEOIGF@?$CFs?1?$CFs?3?5randomly?5selected?5to?5spaw@ (0000)
0024D858 002b:
??_C@_0CL@DJPLPKCC@?$CFs?1?$CFs?3?5current?5?$CFd?5?$DM?5max?5?$CFd?5?9?$DO?5de@ (0000)
0024D884 002f:
??_C@_0CP@JPOKCDIH@?$CFs?1?$CFs?3?5unable?5to?5spawn?0?5out?5of?5s@ (0000)
0024D8B4 002e:
??_C@_0CO@IIDKDHP@?$CFs?1?$CFs?3?5current?5?$CFd?5?$DM?5min?5?$CFd?5?9?$DO?5sp@ (0000)
0024D8E4 0018:
??_C@_0BI@NJPCKEMP@?$CFs?1?$CFs?5triggered?5?$CFs?5rule?$AA@ (0000)
0024D8FC 000a:
??_C@_09MEJNCKEO@attacking?$AA@ (0000)
0024D908 000a:
??_C@_09GNBACBHC@defending?$AA@ (0000)
0024D914 0021:
??_C@_0CB@JPLEGLHP@?$CFs?1?$CFs?5triggered?5maneuvering?5rule@ (0000)
0024D938 0030:
??_C@_0DA@LKCBJOBO@?$CFs?3?5current?5?$CFs?1?$CF?41f?5best?5?$CFs?1?$CF?41f@ (0000)
0024D968 0004:
__real@c47a0000 (0000)
0024D96C 0005:
??_C@_04NEBKMGJO@stay?$AA@ (0000)
0024D974 0008:
??_C@_07JBBGNPIP@migrate?$AA@ (0000)
0024D980 004d:
??_C@_0EN@MMBDPONO@WARNING?3?5squad?5?$CFs?1?$CFs?5has?5an?5inva@ (0000)
0024D9D0 004b:
??_C@_0EL@LEBBGDPA@WARNING?3?5actor?5changing?5to?5encou@ (0000)
0024DA1C 0035:
??_C@_0DF@OEDIHECK@?$CBencounter?9?$DOenemy_visible?5?$CG?$CG?5?$CBen@ (0000)
0024DA54 002e:
??_C@_0CO@DFDAGGAE@?$CFs?1?$CFs?3?5?$CFd?5current?5?$CFd?5leaders?0?5cr@ (0000)
0024DA84 000a:
??_C@_09PONLBLAH@no?5leader?$AA@ (0000)
0024DA90 000b:
??_C@_0L@JLIMGEDN@new?5leader?$AA@ (0000)
0024DA9C 000c:
??_C@_0M@ELGJNJDG@ai_place?5?$CFs?$AA@ (0000)
0024DAA8 000f:
??_C@_0P@IGLHCPJF@ai_place?5?$CFs?1?$CFs?$AA@ (0000)
*/
/* ---------- headers */
#include "cseries.h"
#include "ai/actor_activation.h"
#include "ai/actor_iterators.h"
#include "encounters.h"
#include "actors.h"
#include "actor_definitions.h"
#include "actor_iterators.h"
#include "actor_placement.h"
#include "actor_types.h"
#include "ai.h"
#include "ai_debug.h"
#include "ai_script.h"
#include "ai_scenario_definitions.h"
#include "props.h"
#include "cseries/errors.h"
#include "editor/editor_stubs.h"
#include "game/game.h"
#include "game/game_allegiance.h"
#include "game/game_engine.h"
#include "game/players.h"
#include "math/integer_math.h"
#include "memory/data.h"
#include "saved games/game_state.h"
#include "scenario/scenario.h"
#include "scenario/scenario_definitions.h"
#include "structures/structure_bsp_definitions.h"
#include "units/units.h"
#include <stddef.h>
/* ---------- constants */
enum
{
MAXIMUM_ENCOUNTERS = 128,
MAXIMUM_EXAMINED_PURSUIT_POSITIONS_PER_MAP = 256,
NUMBER_OF_ACTOR_INDICES_PER_EXAMINED_PURSUIT_POSITION = 6,
};
enum
{
MAXIMUM_LINK_ENCOUNTERS_PER_ENCOUNTER = 3,
MAXIMUM_FIRING_POSITIONS_PER_ENCOUNTER = 512,
NUMBER_OF_POST_COMBAT_POSSIBILITIES = 2,
ENCOUNTER_REMAIN_ACTIVE_TIME = 150,
ENCOUNTER_UPDATE_INTERVAL = TICKS_PER_SECOND/2,
};
// encounter_definition.searching
enum
{
_encounter_searching_normal = 0,
_encounter_searching_never,
_encounter_searching_forever,
NUMBER_OF_ENCOUNTER_SEARCHING_TYPES,
};
// actor_type_definition.when_to_search_at_target/when_to_pursue/when_to_search_pursuit
enum
{
_actor_pursuit_always = 0,
_actor_pursuit_not_when_group,
_actor_pursuit_never,
};
// encounter_definition.flags (TU-local until ai_scenario_definitions.h names them)
enum
{
_encounter_not_initially_created_bit = 0,
_encounter_respawn_enabled_bit,
_encounter_blind_bit,
_encounter_deaf_bit,
};
// squad_definition.unique_leader_type (TU-local until ai_scenario_definitions.h names them)
enum
{
_unique_leader_type_normal = 0,
_unique_leader_type_none,
_unique_leader_type_random,
_unique_leader_type_sergeant_johnson,
_unique_leader_type_sergeant_lehto,
NUMBER_OF_UNIQUE_LEADER_TYPES,
};
enum
{
_ai_reference_squad_bit = 15,
};
/* ---------- macros */
#define pursuit_get(index) ((struct pursuit_datum *)datum_get(pursuit_data, (index)))
/* ---------- structures */
struct pursuit_datum
{
short identifier;
short firing_position_index;
long last_examined_time;
short actor_count;
short next_actor_index_index;
long actor_indices[NUMBER_OF_ACTOR_INDICES_PER_EXAMINED_PURSUIT_POSITION];
long next_pursuit_index;
};
struct post_combat_possibility
{
long actor_index;
real weight;
long prop_index;
long unit_index;
};
struct encounter_iterator
{
struct data_iterator data;
long index;
boolean active_only;
};
struct encounter_actor_iterator
{
long encounter_index;
long index;
long next_index;
};
struct actor_iterator
{
struct data_iterator encounter_iterator;
boolean iterated_encounterless_list;
boolean active_only;
byte pad[2];
long index;
long next_index;
};
/* `ai_profile` is owned by ai_profile.c. Encounters only owns the January
* encounter-update counter at +0x94, so keep the rest opaque here. */
struct ai_profile_encounter_data
{
byte __unknown0[0x94];
short encounter_update_count;
};
struct encounter_ai_globals_prefix
{
boolean ai_active;
boolean ai_initialized_for_map;
boolean ai_has_control_data;
boolean time_given_this_frame;
short last_highest_service_timer;
short current_highest_service_timer;
long first_encounterless_actor_index;
};
typedef char encounter_iterator_size_assert[
sizeof(struct encounter_iterator) == 0x18 ? 1 : -1];
typedef char encounter_iterator_index_offset_assert[
offsetof(struct encounter_iterator, index) == 0x10 ? 1 : -1];
typedef char encounter_iterator_active_only_offset_assert[
offsetof(struct encounter_iterator, active_only) == 0x14 ? 1 : -1];
typedef char encounter_actor_iterator_size_assert[
sizeof(struct encounter_actor_iterator) == 0xC ? 1 : -1];
typedef char encounter_actor_iterator_index_offset_assert[
offsetof(struct encounter_actor_iterator, index) == 0x4 ? 1 : -1];
typedef char encounter_actor_iterator_next_index_offset_assert[
offsetof(struct encounter_actor_iterator, next_index) == 0x8 ? 1 : -1];
typedef char actor_iterator_size_assert[
sizeof(struct actor_iterator) == 0x1C ? 1 : -1];
typedef char actor_iterator_iterated_encounterless_offset_assert[
offsetof(struct actor_iterator, iterated_encounterless_list) == 0x10 ? 1 : -1];
typedef char actor_iterator_active_only_offset_assert[
offsetof(struct actor_iterator, active_only) == 0x11 ? 1 : -1];
typedef char actor_iterator_index_offset_assert[
offsetof(struct actor_iterator, index) == 0x14 ? 1 : -1];
typedef char actor_iterator_next_index_offset_assert[
offsetof(struct actor_iterator, next_index) == 0x18 ? 1 : -1];
typedef char encounter_ai_globals_initialized_offset_assert[
offsetof(struct encounter_ai_globals_prefix, ai_initialized_for_map) == 0x1 ? 1 : -1];
typedef char encounter_ai_globals_encounterless_actor_offset_assert[
offsetof(struct encounter_ai_globals_prefix, first_encounterless_actor_index) == 0x8 ? 1 : -1];
typedef char encounter_datum_active_offset_assert[
offsetof(struct encounter_datum, active) == 0xD ? 1 : -1];
typedef char encounter_datum_status_dirty_offset_assert[
offsetof(struct encounter_datum, status_dirty) == 0x28 ? 1 : -1];
typedef char encounter_datum_first_actor_index_offset_assert[
offsetof(struct encounter_datum, first_actor_index) == 0x14 ? 1 : -1];
typedef char encounter_datum_blind_offset_assert[
offsetof(struct encounter_datum, blind) == 0x40 ? 1 : -1];
typedef char encounter_datum_deaf_offset_assert[
offsetof(struct encounter_datum, deaf) == 0x41 ? 1 : -1];
typedef char actor_datum_next_actor_index_offset_assert[
offsetof(struct actor_datum, meta.next_actor_index) == 0x2C ? 1 : -1];
typedef char pursuit_datum_size_assert[
sizeof(struct pursuit_datum) == 0x28 ? 1 : -1];
/* ---------- prototypes */
static void encounter_update_squads(
long encounter_index);
void encounter_update_respawn(
long encounter_index);
void encounter_update_platoons(
long encounter_index);
void encounter_update_follow(
long encounter_index);
void encounter_control_actors(
long encounter_index);
static void encounter_clear_pursuit(
long encounter_index);
static boolean encounter_place_actor(
long encounter_index,
short squad_index,
short initial_variant,
boolean spawning);
static void encounterless_deactivate(
long actor_index);
static void encounters_test_activation(
void);
static long encounter_find_pursuit(
long encounter_index,
short firing_position_index,
long history_start_time,
boolean force_create);
static boolean encounter_activate(
long encounter_index);
static void encounter_deactivate(
long encounter_index);
static void encounter_update_timers(
long encounter_index);
static short squad_get_actor_type(
struct squad_definition *squad_definition);
/* ---------- globals */
extern struct encounter_ai_globals_prefix *ai_globals;
extern struct ai_profile_encounter_data ai_profile;
struct data_array *encounter_data;
struct platoon_datum *platoon_array;
struct squad_datum *squad_array;
struct data_array *pursuit_data;
/* ---------- public code */
void encounters_initialize(
void)
{
encounter_data = game_state_data_new("encounter", MAXIMUM_ENCOUNTERS, sizeof(struct encounter_datum));
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 110, encounter_data);
squad_array = (struct squad_datum *)game_state_malloc("squad", "squad", MAXIMUM_SQUADS_PER_MAP * sizeof(struct squad_datum));
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 113, squad_array);
platoon_array = (struct platoon_datum *)game_state_malloc("platoon", "platoon", MAXIMUM_PLATOONS_PER_MAP * sizeof(struct platoon_datum));
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 116, platoon_array);
pursuit_data = game_state_data_new("ai pursuit", MAXIMUM_EXAMINED_PURSUIT_POSITIONS_PER_MAP, sizeof(struct pursuit_datum));
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 119, pursuit_data);
return;
}
void encounters_dispose(
void)
{
return;
}
void encounters_dispose_from_old_map(
void)
{
data_make_invalid(encounter_data);
data_make_invalid(pursuit_data);
return;
}
void encounter_iterator_new(
struct encounter_iterator *iterator,
boolean active_only)
{
if (ai_globals->ai_initialized_for_map)
{
data_iterator_new(&iterator->data, encounter_data);
iterator->active_only = active_only;
}
return;
}
struct encounter_datum *encounter_iterator_next(
struct encounter_iterator *iterator)
{
struct encounter_datum *result = NULL;
if (ai_globals->ai_initialized_for_map)
{
do
{
result = (struct encounter_datum *)data_iterator_next(&iterator->data);
} while (result && iterator->active_only && !result->active);
iterator->index = iterator->data.datum_index;
}
return result;
}
void encounter_actor_iterator_new(
struct encounter_actor_iterator *iterator,
long encounter_index)
{
if (!ai_globals->ai_initialized_for_map)
return;
iterator->encounter_index = encounter_index;
iterator->index = NONE;
if (encounter_index == NONE)
iterator->next_index = ai_globals->first_encounterless_actor_index;
else
iterator->next_index = encounter_get(encounter_index)->first_actor_index;
return;
}
struct actor_datum *encounter_actor_iterator_next(
struct encounter_actor_iterator *iterator)
{
struct actor_datum *result = NULL;
if (ai_globals->ai_initialized_for_map)
{
long next_index = iterator->next_index;
iterator->index = next_index;
if (next_index != NONE)
{
result = actor_get(next_index);
iterator->next_index = result->meta.next_actor_index;
}
}
return result;
}
void encounter_set_blind(
long encounter_index,
boolean blind)
{
if (ai_globals->ai_initialized_for_map)
encounter_get(encounter_index)->blind = blind;
return;
}
void encounter_set_deaf(
long encounter_index,
boolean deaf)
{
if (ai_globals->ai_initialized_for_map)
encounter_get(encounter_index)->deaf = deaf;
return;
}
void actor_iterator_new(
struct actor_iterator *iterator,
boolean active_only)
{
if (!ai_globals->ai_initialized_for_map)
return;
data_iterator_new(&iterator->encounter_iterator, encounter_data);
iterator->iterated_encounterless_list = FALSE;
iterator->next_index = NONE;
iterator->index = NONE;
iterator->active_only = active_only;
return;
}
void encounterless_activate(
long actor_index)
{
struct actor_datum *actor = actor_get(actor_index);
match_assert(
"c:\\halo\\SOURCE\\ai\\encounters.c",
0x720,
actor->meta.encounterless);
actor->meta.encounterless_active_timer = 90;
actor_set_active(actor_index, TRUE);
return;
}
void encounters_update_dirty_status(
void)
{
struct encounter_iterator iterator;
struct encounter_datum *encounter;
encounter_iterator_new(&iterator, FALSE);
while ((encounter = encounter_iterator_next(&iterator)) != NULL)
{
if (encounter->status_dirty)
encounter_update_status(iterator.index);
}
return;
}
long encounter_get_by_name(
char const *encounter_name)
{
long encounter_index = NONE;
struct scenario *scenario = global_scenario_get();
if (scenario)
{
long i;
for (i = 0; i < scenario->ai_encounters.count; ++i)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&scenario->ai_encounters, i, struct encounter_definition);
if (csstrncmp(encounter_definition->name, encounter_name, TAG_STRING_LENGTH+1) == 0)
{
encounter_index = i;
break;
}
}
}
return encounter_index;
}
struct actor_datum *encounter_actor_iterator_prev(
struct encounter_actor_iterator *iterator)
{
struct actor_datum *result = NULL;
if (ai_globals->ai_initialized_for_map)
{
long actor_index = encounter_get(iterator->encounter_index)->first_actor_index;
long previous_index = NONE;
while (actor_index != iterator->index && actor_index != NONE)
{
previous_index = actor_index;
result = actor_get(actor_index);
actor_index = result->meta.next_actor_index;
}
if (actor_index != iterator->index)
return NULL;
iterator->next_index = actor_index;
iterator->index = previous_index;
}
return result;
}
struct actor_datum *actor_iterator_next(
struct actor_iterator *iterator)
{
struct actor_datum *result = NULL;
if (ai_globals->ai_initialized_for_map)
{
if (iterator->next_index == NONE)
{
struct encounter_datum *encounter;
do
{
encounter = (struct encounter_datum *)data_iterator_next(&iterator->encounter_iterator);
if (!encounter)
break;
if (!iterator->active_only || encounter->active)
iterator->next_index = encounter->first_actor_index;
} while (iterator->next_index == NONE);
if (!encounter && !iterator->iterated_encounterless_list)
{
iterator->next_index = ai_globals->first_encounterless_actor_index;
iterator->iterated_encounterless_list = TRUE;
}
}
do
{
long next_index = iterator->next_index;
iterator->index = next_index;
if (next_index == NONE)
break;
result = actor_get(next_index);
iterator->next_index = result->meta.next_actor_index;
if (iterator->active_only && !result->meta.active)
result = NULL;
} while (!result);
}
return result;
}
void encounter_modify_pursuit_desires(
long encounter_index,
short squad_index,
boolean *pursue_tenacious,
short *group_pursuit_restriction,
boolean *group_pursuit_controller,
short *desired_target_search,
short *desired_pursuit,
short *desired_pursuit_search)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
short searching = encounter_definition->searching;
if (TEST_FLAG(squad_definition->flags, _squad_never_search_bit))
searching = _encounter_searching_never;
switch (searching)
{
case _encounter_searching_never:
*group_pursuit_restriction = 1;
*desired_pursuit = _actor_pursuit_never;
*desired_pursuit_search = _actor_pursuit_never;
break;
case _encounter_searching_forever:
*pursue_tenacious = TRUE;
*desired_target_search = _actor_pursuit_always;
*desired_pursuit = _actor_pursuit_always;
*desired_pursuit_search = _actor_pursuit_always;
*group_pursuit_controller = FALSE;
break;
}
return;
}
boolean encounter_link_activation(
long encounter_index,
short link_encounter_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
boolean result = FALSE;
short i;
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 1916, (link_encounter_index >= 0) && (link_encounter_index < global_scenario_get()->ai_encounters.count));
for (i = 0; i < encounter->link_encounter_count; ++i)
{
if (encounter->link_encounter_indices[i] == link_encounter_index)
{
result = TRUE;
break;
}
}
if (!result && encounter->link_encounter_count < MAXIMUM_LINK_ENCOUNTERS_PER_ENCOUNTER)
{
encounter->link_encounter_indices[encounter->link_encounter_count] = link_encounter_index;
encounter->link_encounter_count++;
result = TRUE;
}
return result;
}
void encounterless_attach_actor(
long actor_index)
{
if (ai_globals->ai_initialized_for_map)
{
struct actor_datum *actor = actor_get(actor_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 758, actor->meta.encounter_index==NONE);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 759, !actor->meta.encounterless);
actor->meta.next_actor_index = ai_globals->first_encounterless_actor_index;
ai_globals->first_encounterless_actor_index = actor_index;
actor->meta.encounterless = TRUE;
actor->meta.encounterless_active_timer = actor->meta.active ? 90 : 0;
actor_flush_position_indices(actor_index);
}
return;
}
boolean encounter_mark_examined_pursuit_position(
long encounter_index,
long actor_index,
short firing_position_index,
long history_start_time)
{
boolean marked = FALSE;
long pursuit_index = encounter_find_pursuit(encounter_index, firing_position_index, history_start_time, TRUE);
if (pursuit_index != NONE)
{
struct pursuit_datum *pursuit = pursuit_get(pursuit_index);
short i;
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 1031, pursuit->firing_position_index == firing_position_index);
for (i = 0; i < NUMBER_OF_ACTOR_INDICES_PER_EXAMINED_PURSUIT_POSITION; ++i)
{
if (pursuit->actor_indices[i] == actor_index)
break;
}
if (i >= NUMBER_OF_ACTOR_INDICES_PER_EXAMINED_PURSUIT_POSITION)
{
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 1044, (pursuit->next_actor_index_index >= 0) && (pursuit->next_actor_index_index < NUMBER_OF_ACTOR_INDICES_PER_EXAMINED_PURSUIT_POSITION));
pursuit->actor_indices[pursuit->next_actor_index_index] = actor_index;
pursuit->next_actor_index_index = (pursuit->next_actor_index_index+1) % NUMBER_OF_ACTOR_INDICES_PER_EXAMINED_PURSUIT_POSITION;
pursuit->actor_count++;
marked = TRUE;
}
pursuit->last_examined_time = game_time_get();
}
return marked;
}
void encounter_force_activate(
long encounter_index)
{
encounter_get(encounter_index)->remain_active_timer = ENCOUNTER_REMAIN_ACTIVE_TIME;
encounter_activate(encounter_index);
return;
}
void encounter_force_deactivate(
long encounter_index)
{
encounter_get(encounter_index)->remain_active_timer = 0;
encounter_deactivate(encounter_index);
return;
}
void encounter_set_respawn(
long encounter_index,
boolean respawn)
{
if (ai_globals->ai_initialized_for_map)
{
encounter_get(encounter_index)->respawn_enabled = respawn;
encounter_force_activate(encounter_index);
}
return;
}
void encounter_squad_timer_expire(
long encounter_index,
short squad_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
struct squad_datum *squad = encounter_get_squad(encounter, squad_index);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
squad->delay_timer = 0;
if (TEST_FLAG(squad_definition->flags, _squad_magic_sight_after_timer_bit))
{
ai_scripting_magically_see_players(
DATUM_INDEX_NEW(
DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index),
FLAG(_ai_reference_squad_bit) | (squad_index & UNSIGNED_CHAR_MAX)));
}
if (ai_debug.print_rules)
console_printf(FALSE, "%s/%s: delay timer finished", encounter_definition->name, squad_definition->name);
return;
}
void encounters_create_for_new_map(
void)
{
struct scenario *scenario = global_scenario_get();
struct encounter_iterator iterator;
struct encounter_datum *encounter;
encounter_iterator_new(&iterator, FALSE);
while ((encounter = encounter_iterator_next(&iterator)) != NULL)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&scenario->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(iterator.index), struct encounter_definition);
boolean create = !TEST_FLAG(encounter_definition->flags, _encounter_not_initially_created_bit);
if (DATUM_INDEX_TO_ABSOLUTE_INDEX(ai_debug.selected_squad_index) == DATUM_INDEX_TO_ABSOLUTE_INDEX(iterator.index) || create)
{
encounter_create(iterator.index, NONE, NONE);
}
}
return;
}
void encounters_unit_died(
long unit_index)
{
struct unit_datum *unit = unit_get(unit_index);
if (unit->object.owner_team_index != NONE)
{
struct encounter_iterator iterator;
struct encounter_datum *encounter;
encounter_iterator_new(&iterator, TRUE);
while ((encounter = encounter_iterator_next(&iterator)) != NULL)
{
if (game_team_is_enemy(encounter->team_index, unit->object.owner_team_index) &&
encounter->enemy_target && !encounter->stand_down && !encounter->post_combat)
{
encounter->enemies_defeated++;
}
}
}
return;
}
void encounter_attach_unit(
long encounter_index,
long unit_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
struct unit_datum *unit = unit_get(unit_index);
if (unit->unit.swarm_actor_index != NONE)
{
struct actor_datum *actor = actor_get(unit->unit.swarm_actor_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 603, actor->meta.encounter_index == encounter_index);
}
else
{
struct actor_datum *actor = actor_get(unit->unit.actor_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 609, actor->meta.encounter_index == encounter_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 610, actor->meta.unit_index == unit_index);
}
if (!game_engine_running() && encounter->team_index == 0)
{
encounter->team_index = unit->object.owner_team_index;
if (encounter->first_actor_index != NONE)
ai_update_team_status();
}
return;
}
boolean encounter_spawn_actor(
long encounter_index,
short squad_index)
{
if (ai_globals->ai_initialized_for_map && encounter_place_actor(encounter_index, squad_index, 0, TRUE))
{
struct encounter_datum *encounter = encounter_get(encounter_index);
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
struct squad_datum *squad = encounter_get_squad(encounter, squad_index);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
encounter->current_count++;
squad->current_count++;
if (squad_definition->respawn_total_count > 0)
squad->respawn_actors_left--;
encounter->respawn_delay_ticks = real_random_range(encounter_definition->respawn_time_lower_bound, encounter_definition->respawn_time_upper_bound) * TICKS_PER_SECOND;
squad->respawn_delay_ticks = real_random_range(squad_definition->respawn_time_lower_bound, squad_definition->respawn_time_upper_bound) * TICKS_PER_SECOND;
}
/* Original January and HCEA behavior: the successful placement is not reported to the caller;
* this routine returns FALSE on every path. */
return FALSE;
}
void encounter_verify_firing_position_owner_actor_indices(
long encounter_index)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
long owner_actor_indices[MAXIMUM_FIRING_POSITIONS_PER_ENCOUNTER];
struct encounter_actor_iterator iterator;
struct actor_datum *actor;
csmemset(owner_actor_indices, NONE, encounter_definition->firing_positions.count*sizeof(long));
encounter_actor_iterator_new(&iterator, encounter_index);
while ((actor = encounter_actor_iterator_next(&iterator)) != NULL)
{
if (actor->firing_positions.current_position_index != NONE)
{
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 302, actor->firing_positions.current_position_index>=0 && actor->firing_positions.current_position_index < encounter_definition->firing_positions.count);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 303, owner_actor_indices[actor->firing_positions.current_position_index]==NONE);
owner_actor_indices[actor->firing_positions.current_position_index] = iterator.index;
}
}
return;
}
void encounter_build_firing_position_owner_actor_indices(
long encounter_index,
long *firing_position_owner_actor_indices)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
struct encounter_actor_iterator iterator;
struct actor_datum *actor;
csmemset(firing_position_owner_actor_indices, NONE, encounter_definition->firing_positions.count*sizeof(long));
encounter_actor_iterator_new(&iterator, encounter_index);
while ((actor = encounter_actor_iterator_next(&iterator)) != NULL)
{
if (actor->firing_positions.current_position_index != NONE)
{
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 332, actor->firing_positions.current_position_index>=0 && actor->firing_positions.current_position_index < encounter_definition->firing_positions.count);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 333, firing_position_owner_actor_indices[actor->firing_positions.current_position_index]==NONE);
firing_position_owner_actor_indices[actor->firing_positions.current_position_index] = iterator.index;
}
}
return;
}
void encounterless_detach_actor(
long actor_index)
{
if (ai_globals->ai_initialized_for_map)
{
struct actor_datum *actor = actor_get(actor_index);
long *actor_index_reference;
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 782, actor->meta.encounterless);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 783, actor->meta.encounter_index == NONE);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 784, actor->meta.squad_index == NONE);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 785, actor->meta.platoon_index == NONE);
for (actor_index_reference = &ai_globals->first_encounterless_actor_index;
*actor_index_reference != actor_index;
actor_index_reference = &actor_get(*actor_index_reference)->meta.next_actor_index)
{
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 796, *actor_index_reference!=NONE);
}
*actor_index_reference = actor->meta.next_actor_index;
actor->meta.encounterless = FALSE;
actor->meta.next_actor_index = NONE;
actor->meta.force_active = FALSE;
}
return;
}
void encounter_detach_actor(
long actor_index,
boolean died)
{
if (ai_globals->ai_initialized_for_map)
{
struct actor_datum *actor = actor_get(actor_index);
if (actor->meta.encounter_index != NONE)
{
struct encounter_datum *encounter = encounter_get(actor->meta.encounter_index);
long *actor_index_reference;
for (actor_index_reference = &encounter->first_actor_index;
*actor_index_reference != actor_index;
actor_index_reference = &actor_get(*actor_index_reference)->meta.next_actor_index)
{
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 544, *actor_index_reference!=NONE);
}
*actor_index_reference = actor->meta.next_actor_index;
if (!died)
{
struct squad_datum *squad = encounter_get_squad(encounter, actor->meta.squad_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 556, encounter->original_count > 0);
encounter->original_count--;
if (actor->meta.unique_leader)
{
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 561, encounter->unique_leader_count > 0);
encounter->unique_leader_count--;
}
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 565, squad->original_count > 0);
squad->original_count--;
if (actor->meta.platoon_index != NONE)
{
struct platoon_datum *platoon = encounter_get_platoon(encounter, actor->meta.platoon_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 573, platoon->original_count > 0);
platoon->original_count--;
}
}
actor->meta.next_actor_index = NONE;
actor->meta.encounter_index = NONE;
actor->meta.platoon_index = NONE;
actor->meta.squad_index = NONE;
encounter->status_dirty = TRUE;
}
}
return;
}
void encounter_stand_down(
long encounter_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
struct encounter_actor_iterator iterator;
struct actor_datum *actor;
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 2431, !encounter->enemy_visible);
encounter->stand_down = TRUE;
encounter->enemies_defeated = 0;
encounter_clear_pursuit(encounter_index);
encounter_actor_iterator_new(&iterator, encounter_index);
while ((actor = encounter_actor_iterator_next(&iterator)) != NULL)
{
struct prop_iterator prop_iterator;
struct prop_datum *prop;
prop_iterator_new(&prop_iterator, iterator.index);
while ((prop = prop_iterator_next(&prop_iterator)) != NULL)
{
if (prop->state >= _prop_state_uninspected_orphan && prop->state <= _prop_state_inspected_orphan &&
prop->enemy && prop_iterator.index != actor->target.target_prop_index)
{
struct prop_datum *parent_prop;
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 2458, prop->parent_prop_index != NONE);
parent_prop = prop_get(prop->parent_prop_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 2463, parent_prop->orphan_prop_index == prop_iterator.index);
parent_prop->orphan_prop_index = NONE;
actor_switch_props(iterator.index, prop_iterator.index, NONE);
prop_delete(iterator.index, prop_iterator.index);
}
}
}
return;
}
void encounter_attach_actor(
long actor_index,
long encounter_index,
short squad_index,
boolean has_previous_team)
{
if (ai_globals->ai_initialized_for_map)
{
struct actor_datum *actor = actor_get(actor_index);
struct actor_definition *actor_definition = actor_definition_get(actor->meta.definition_index); // January fetches the definition but never reads it
struct encounter_datum *encounter = encounter_get(encounter_index);
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
struct squad_datum *squad = encounter_get_squad(encounter, squad_index);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
short platoon_index = squad_definition->platoon_index;
boolean activated = FALSE;
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 650, actor->meta.encounter_index==NONE);
actor->meta.disconnected_encounter_index = NONE;
actor->meta.disconnected_squad_index = NONE;
actor->meta.next_actor_index = encounter->first_actor_index;
encounter->first_actor_index = actor_index;
if (platoon_index < 0 || platoon_index >= encounter_definition->platoons.count)
platoon_index = NONE;
actor->meta.encounter_index = encounter_index;
actor->meta.squad_index = squad_index;
actor->meta.platoon_index = platoon_index;
if (actor->meta.active && !actor->meta.dormant)
{
encounter_get(encounter_index)->remain_active_timer = ENCOUNTER_REMAIN_ACTIVE_TIME;
activated = encounter_activate(encounter_index);
}
if (!activated)
{
actor_set_active(actor_index, encounter->active);
if (encounter->active)
actor_set_dormant(actor_index, FALSE);
}
if (actor->meta.unit_index != NONE)
encounter_attach_unit(encounter_index, actor->meta.unit_index);
if (actor->meta.team_index != encounter->team_index)
{
if (has_previous_team)
{
if (encounter->current_count == 0)
{
encounter->team_index = actor->meta.team_index;
ai_update_team_status();
}
else
{
error(_error_silent, "WARNING: actor changing to encounter %s/%s is being forced to change teams", encounter_definition->name, squad_definition->name);
actor_set_team(actor_index, encounter->team_index);
}
}
else
{
actor_set_team(actor_index, encounter->team_index);
}
}
encounter->original_count++;
squad->original_count++;
if (actor->meta.unique_leader)
encounter->unique_leader_count++;
if (platoon_index != NONE)
{
struct platoon_datum *platoon = encounter_get_platoon(encounter, platoon_index);
actor->external_orders.defending = platoon->defending;
actor->emotions.currently_defending = platoon->defending;
platoon->original_count++;
}
encounter->status_dirty = TRUE;
}
return;
}
void encounter_create(
long encounter_index,
short desired_platoon_index,
short desired_squad_index)
{
if (ai_globals->ai_initialized_for_map)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
boolean all_squads = desired_platoon_index == NONE && desired_squad_index == NONE;
short squad_index;
if (ai_debug.print_placement)
{
if (desired_platoon_index != NONE)
{
console_printf(FALSE, "ai_place %s/%s", encounter_definition->name,
TAG_BLOCK_GET_ELEMENT(&encounter_definition->platoons, desired_platoon_index, struct platoon_definition)->name);
}
else if (desired_squad_index != NONE)
{
console_printf(FALSE, "ai_place %s/%s", encounter_definition->name,
TAG_BLOCK_GET_ELEMENT(&encounter_definition->squads, desired_squad_index, struct squad_definition)->name);
}
else
{
console_printf(FALSE, "ai_place %s", encounter_definition->name);
}
}
for (squad_index = 0; squad_index < encounter_definition->squads.count; ++squad_index)
{
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
short initial_variant = 0;
short actor_count;
short actor_type;
short i;
if (!all_squads && squad_index != desired_squad_index)
{
if (squad_definition->platoon_index == NONE || squad_definition->platoon_index != desired_platoon_index)
continue;
}
switch (game_difficulty_level_get())
{
case _game_difficulty_level_easy:
case _game_difficulty_level_normal:
actor_count = squad_definition->min_count;
break;
case _game_difficulty_level_hard:
actor_count = (squad_definition->max_count + squad_definition->min_count) / 2;
break;
case _game_difficulty_level_impossible:
actor_count = squad_definition->max_count;
break;
default:
match_vassert("c:\\halo\\SOURCE\\ai\\encounters.c", 1730, FALSE, NULL);
}
actor_type = squad_get_actor_type(squad_definition);
switch (squad_definition->unique_leader_type)
{
case _unique_leader_type_normal:
{
struct encounter_datum *encounter = encounter_get(encounter_index);
boolean create_leader = FALSE;
if (actor_type == _actor_marine)
{
if (encounter->unique_leader_count == 0)
create_leader = encounter->original_count + actor_count >= 4;
else if (encounter->unique_leader_count == 1)
create_leader = encounter->original_count + actor_count >= 10;
}
if (ai_debug.print_placement)
{
console_printf(FALSE, "%s/%s: %d current %d leaders, create %d -> %s",
encounter_definition->name, squad_definition->name,
encounter->original_count, encounter->unique_leader_count, actor_count,
create_leader ? "new leader" : "no leader");
}
if (!create_leader)
break;
}
// fall through
case _unique_leader_type_random:
if (actor_type == _actor_marine)
initial_variant = 100 + random_range(0, 2);
break;
case _unique_leader_type_sergeant_johnson:
if (actor_type == _actor_marine)
initial_variant = 100;
break;
case _unique_leader_type_sergeant_lehto:
if (actor_type == _actor_marine)
initial_variant = 101;
break;
case _unique_leader_type_none:
break;
}
for (i = 0; i < actor_count; ++i)
{
encounter_place_actor(encounter_index, squad_index, initial_variant, FALSE);
initial_variant = 0;
}
}
encounter_update_status(encounter_index);
encounters_test_activation();
}
return;
}
void encounters_update(
void)
{
long time = game_time_get();
short phase;
struct encounter_iterator iterator;
struct encounter_datum *encounter;
if (time % TICKS_PER_SECOND == 0)
{
encounters_update_dirty_status();
encounters_test_activation();
}
phase = time % ENCOUNTER_UPDATE_INTERVAL;
encounter_iterator_new(&iterator, TRUE);
while ((encounter = encounter_iterator_next(&iterator)) != NULL)
{
long encounter_index = iterator.index;
ai_profile.encounter_update_count++;
if (DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index) % ENCOUNTER_UPDATE_INTERVAL == phase)
{
encounter_update_status(encounter_index);
encounter_update_timers(encounter_index);
encounter_update_respawn(encounter_index);
encounter_update_squads(encounter_index);
encounter_update_platoons(encounter_index);
encounter_update_follow(encounter_index);
encounter_control_actors(encounter_index);
}
}
return;
}
/* ---------- private code */
static void encounter_clear_pursuit(
long encounter_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
long pursuit_index;
for (pursuit_index = encounter->first_pursuit_index; pursuit_index != NONE; pursuit_index = encounter->first_pursuit_index)
{
encounter->first_pursuit_index = pursuit_get(pursuit_index)->next_pursuit_index;
datum_delete(pursuit_data, pursuit_index);
}
return;
}
static long encounter_find_pursuit(
long encounter_index,
short firing_position_index,
long history_start_time,
boolean force_create)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
long pursuit_index = encounter->first_pursuit_index;
boolean reset = FALSE;
while (pursuit_index != NONE)
{
struct pursuit_datum *pursuit = pursuit_get(pursuit_index);
if (pursuit->firing_position_index == firing_position_index)
{
if (pursuit->last_examined_time < history_start_time)
reset = TRUE;
break;
}
pursuit_index = pursuit->next_pursuit_index;
}
if (pursuit_index == NONE)
{
if (force_create)
{
pursuit_index = datum_new(pursuit_data);
if (pursuit_index != NONE)
{
struct pursuit_datum *pursuit = pursuit_get(pursuit_index);
pursuit->firing_position_index = firing_position_index;
pursuit->next_pursuit_index = encounter->first_pursuit_index;
encounter->first_pursuit_index = pursuit_index;
reset = TRUE;
}
else
{
error(_error_silent, "WARNING: too many actors searching, exceeded MAXIMUM_EXAMINED_PURSUIT_POSITIONS_PER_MAP (%d)", MAXIMUM_EXAMINED_PURSUIT_POSITIONS_PER_MAP);
}
}
}
if (reset)
{
struct pursuit_datum *pursuit = pursuit_get(pursuit_index);
pursuit->last_examined_time = NONE;
pursuit->actor_count = 0;
pursuit->next_actor_index_index = 0;
csmemset(pursuit->actor_indices, NONE, sizeof(pursuit->actor_indices));
if (!force_create)
return NONE;
}
return pursuit_index;
}
static boolean encounter_activate(
long encounter_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
if (encounter_definition->runtime_structure_bsp_reference_index == NONE ||
encounter_definition->runtime_structure_bsp_reference_index == global_structure_bsp_index)
{
if (!encounter->active)
{
struct encounter_actor_iterator iterator;
encounter_actor_iterator_new(&iterator, encounter_index);
while (encounter_actor_iterator_next(&iterator))
actor_set_active(iterator.index, TRUE);
}
encounter->last_active_time = game_time_get();
encounter->active = TRUE;
}
return encounter->active;
}
static void encounter_deactivate(
long encounter_index)
{
struct encounter_actor_iterator iterator;
struct actor_datum *actor;
encounter_get(encounter_index)->active = FALSE;
encounter_clear_pursuit(encounter_index);
encounter_actor_iterator_new(&iterator, encounter_index);
while ((actor = encounter_actor_iterator_next(&iterator)) != NULL)
{
if (actor->meta.active)
actor_set_active(iterator.index, FALSE);
actor_verify_activation(iterator.index);
}
return;
}
static short squad_get_actor_type(
struct squad_definition *squad_definition)
{
struct scenario *scenario = global_scenario_get();
short actor_palette_index = squad_definition->actor_palette_index;
short actor_type = _actor_none;
if (actor_palette_index >= 0 && actor_palette_index < scenario->ai_actor_palette.count)
{
long actor_variant_definition_index = TAG_BLOCK_GET_ELEMENT(
&scenario->ai_actor_palette, actor_palette_index, struct tag_reference)->index;
if (actor_variant_definition_index != NONE)
{
long actor_definition_index = actor_variant_definition_get(actor_variant_definition_index)->actor_reference.index;
if (actor_definition_index != NONE)
actor_type = actor_definition_get(actor_definition_index)->type;
}
}
return actor_type;
}
static boolean encounter_post_combat_add_possibility(
struct post_combat_possibility *possibility_array,
long actor_index,
real weight,
long prop_index,
long unit_index)
{
boolean added = FALSE;
short i, j;
for (i = 0; i < NUMBER_OF_POST_COMBAT_POSSIBILITIES; ++i)
{
if (weight > possibility_array[i].weight)
{
for (j = NUMBER_OF_POST_COMBAT_POSSIBILITIES-1; j > i; --j)
possibility_array[j] = possibility_array[j-1];
possibility_array[i].weight = weight;
possibility_array[i].actor_index = actor_index;
possibility_array[i].prop_index = prop_index;
possibility_array[i].unit_index = unit_index;
added = TRUE;
}
}
return added;
}
static boolean encounter_place_actor(
long encounter_index,
short squad_index,
short initial_variant,
boolean spawning)
{
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
short starting_location_index = encounter_get_actor_starting_location(encounter_index, squad_index, spawning);
if (starting_location_index != NONE)
{
struct actor_starting_location *starting_location = TAG_BLOCK_GET_ELEMENT(
&squad_definition->starting_locations, starting_location_index, struct actor_starting_location);
short actor_palette_index = squad_definition->actor_palette_index;
if (starting_location->actor_variant_index != NONE)
actor_palette_index = starting_location->actor_variant_index;
if (actor_palette_index >= 0 && actor_palette_index < global_scenario_get()->ai_actor_palette.count)
{
struct tag_reference *actor_palette_entry = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_actor_palette, actor_palette_index, struct tag_reference);
if (actor_palette_entry->index != NONE)
{
struct actor_variant_definition *actor_variant_definition = actor_variant_definition_get(actor_palette_entry->index);
boolean upgrade_major = FALSE;
if (actor_variant_definition->major_upgrade_reference.index != NONE)
{
boolean random = FALSE;
real chance = 0.f;
ai_get_major_upgrade_chance(squad_definition->major_upgrade, &upgrade_major, &random, &chance);
if (random)
upgrade_major = ai_consider_major_upgrade(encounter_index, squad_index, chance);
}
return actor_place(actor_palette_entry->index, encounter_index, squad_index, starting_location, upgrade_major, initial_variant) != NONE;
}
}
else
{
error(_error_silent, "WARNING: cannot spawn actors in %s/%s because the actor variant specified for this squad is NONE or invalid", encounter_definition->name, squad_definition->name);
}
}
return FALSE;
}
static void encounter_update_timers(
long encounter_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
if (encounter->enemy_visible)
encounter->enemy_visible_timer = 0;
else if (encounter->enemy_visible_timer != NONE)
encounter->enemy_visible_timer += ENCOUNTER_UPDATE_INTERVAL;
if (encounter->enemy_alive)
encounter->enemy_alive_timer = 0;
else if (encounter->enemy_alive_timer != NONE)
encounter->enemy_alive_timer += ENCOUNTER_UPDATE_INTERVAL;
if (encounter->post_combat && encounter->post_combat_delay)
{
if (encounter->post_combat_delay_timer > ENCOUNTER_UPDATE_INTERVAL)
encounter->post_combat_delay_timer -= ENCOUNTER_UPDATE_INTERVAL;
else
encounter->post_combat_delay_timer = 0;
}
return;
}
static void encounter_update_squads(
long encounter_index)
{
struct encounter_datum *encounter = encounter_get(encounter_index);
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
short squad_index;
for (squad_index = 0; squad_index < encounter->squad_count; ++squad_index)
{
struct squad_datum *squad = encounter_get_squad(encounter, squad_index);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads, squad_index, struct squad_definition);
if (squad->delay_timer > 0 && !TEST_FLAG(squad_definition->flags, _squad_delay_forever_bit))
{
if (squad->delay_timer_started)
{
if (squad->delay_timer > ENCOUNTER_UPDATE_INTERVAL)
squad->delay_timer -= ENCOUNTER_UPDATE_INTERVAL;
else
encounter_squad_timer_expire(encounter_index, squad_index);
}
else
{
squad->delay_timer_started = TEST_FLAG(squad_definition->flags, _squad_timer_starts_immediately_bit) ||
encounter->current_in_combat_count > 0;
if (squad->delay_timer_started && ai_debug.print_rules)
{
console_printf(FALSE, "%s/%s: delay timer started (%.1f sec)",
encounter_definition->name, squad_definition->name, squad_definition->squad_delay_timer);
}
}
}
}
return;
}
static void encounterless_deactivate(
long actor_index)
{
struct actor_datum *actor = actor_get(actor_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 1838, actor->meta.encounterless);
actor->meta.encounterless_active_timer = 0;
actor_set_active(actor_index, FALSE);
return;
}
static void encounters_test_activation(
void)
{
struct structure_bsp *structure_bsp = global_structure_bsp_get();
unsigned long const *combined_pvs = players_get_combined_pvs();
unsigned long activation_cluster_bit_vector[BIT_VECTOR_SIZE_IN_LONGS(MAXIMUM_CLUSTERS_PER_STRUCTURE)];
struct data_iterator iterator;
struct encounter_datum *encounter;
struct actor_datum *actor;
long actor_index;
for (actor_index = ai_globals->first_encounterless_actor_index; actor_index != NONE; actor_index = actor->meta.next_actor_index)
{
actor = actor_get(actor_index);
match_assert("c:\\halo\\SOURCE\\ai\\encounters.c", 2234, actor->meta.encounterless);
if (actor->meta.swarm)
{
actor->meta.dormant_desire = TRUE;
if (actor->meta.swarm_cache_index == NONE)
{
long unit_index = actor->meta.swarm_unit_index;
while (unit_index != NONE)
{
struct unit_datum *unit = unit_get(unit_index);
short cluster_index = object_get(object_get_ultimate_parent(unit_index))->object.location.cluster_index;
if (cluster_index != NONE && BIT_VECTOR_TEST_FLAG(combined_pvs, cluster_index))
{
actor->meta.dormant_desire = FALSE;
break;
}
unit_index = unit->unit.swarm_next_unit_index;
}
}
else
{
struct swarm_datum *swarm = swarm_get(actor->meta.swarm_cache_index);
short i;
for (i = 0; i < swarm->unit_count; ++i)
{
short cluster_index = object_get(object_get_ultimate_parent(swarm->unit_indices[i]))->object.location.cluster_index;
if (cluster_index != NONE && BIT_VECTOR_TEST_FLAG(combined_pvs, cluster_index))
{
actor->meta.dormant_desire = FALSE;
break;
}
}
}
}
else
{
short cluster_index = object_get(object_get_ultimate_parent(actor->meta.unit_index))->object.location.cluster_index;
if (cluster_index == NONE)
actor->meta.dormant_desire = TRUE;
else
actor->meta.dormant_desire = !BIT_VECTOR_TEST_FLAG(combined_pvs, cluster_index);
}
if (!actor->meta.dormant_desire || actor->meta.force_active || game_in_editor() || ai_debug.force_all_active)
{
encounterless_activate(actor_index);
}
else if (actor->meta.encounterless_active_timer > TICKS_PER_SECOND)
{
actor->meta.encounterless_active_timer -= TICKS_PER_SECOND;
}
else
{
actor->meta.encounterless_active_timer = 0;
encounterless_deactivate(actor_index);
}
actor_verify_activation(actor_index);
}
data_iterator_new(&iterator, encounter_data);
while ((encounter = (struct encounter_datum *)data_iterator_next(&iterator)) != NULL)
{
long encounter_index = iterator.datum_index;
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters, DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index), struct encounter_definition);
boolean force_active = encounter->force_active || game_in_editor() || encounter->respawn_delay_ticks > 0 || ai_debug.force_all_active;
boolean active = FALSE;
if (encounter_definition->runtime_structure_bsp_reference_index == NONE ||
encounter_definition->runtime_structure_bsp_reference_index == global_structure_bsp_index)
{
encounter_compute_activation_cluster_bit_vector(encounter_index, TRUE, MAXIMUM_CLUSTERS_PER_STRUCTURE, combined_pvs, activation_cluster_bit_vector);
active = bit_vector_and(structure_bsp->clusters.count, combined_pvs, activation_cluster_bit_vector, NULL) || force_active;
}
if (active)
{
encounter->remain_active_timer = ENCOUNTER_REMAIN_ACTIVE_TIME;
encounter_activate(encounter_index);
}
else if (encounter->active && encounter->remain_active_timer > TICKS_PER_SECOND)
{
encounter->remain_active_timer -= TICKS_PER_SECOND;
}
else
{
boolean link_active = FALSE;
short i;
for (i = 0; i < encounter->link_encounter_count; ++i)
{
if (encounter_get(encounter->link_encounter_indices[i])->remain_active_timer > 0)
link_active = TRUE;
}
encounter->remain_active_timer = 0;
if (link_active)
encounter_activate(encounter_index);
else
encounter_deactivate(encounter_index);
}
}
return;
}