mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 13:00:25 +02:00
Integrate coherent B3, P2/K17 prerequisites, completed K1-K16 + S, and the independent profile/effects rulings. Five gains and two explicitly approved B3 debits: net +3 strict functions, +2770 meaningful/+2800 padded bytes (+2774 progress credit). No additional losses; Halo objects 394 to 396 and data unchanged. Clean rebuild, identical 8252-row stable sweep, 68 valid parks, admission/provider checks, 467-unit warning comparison, unchanged fake scan, and 1318 tests passed. Preserve canonical helper recoveries and unrelated work. Full evidence and disclosures in docs/object_matching_logs/astra_five_function_reconciliation_20260928.md.
4797 lines
121 KiB
C
4797 lines
121 KiB
C
/*
|
|
AI_SCRIPT.C
|
|
|
|
symbols in this file:
|
|
00042690 0090:
|
|
_encounter_get_platoon (0000)
|
|
00042720 0010:
|
|
_ai_script_initialize (0000)
|
|
00042730 0010:
|
|
_ai_script_dispose (0000)
|
|
00042740 0010:
|
|
_ai_script_initialize_for_new_map (0000)
|
|
00042750 0010:
|
|
_ai_script_dispose_from_old_map (0000)
|
|
00042760 0130:
|
|
_ai_index_from_string (0000)
|
|
00042890 00f0:
|
|
_ai_index_to_string (0000)
|
|
00042980 0120:
|
|
_ai_index_platoon_iterator_new (0000)
|
|
00042AA0 0070:
|
|
_ai_index_platoon_iterator_next (0000)
|
|
00042B10 0100:
|
|
_ai_index_squad_iterator_new (0000)
|
|
00042C10 00e0:
|
|
_ai_index_squad_iterator_next (0000)
|
|
00042CF0 00d0:
|
|
_ai_index_actor_iterator_new (0000)
|
|
00042DC0 0070:
|
|
_ai_index_actor_iterator_next (0000)
|
|
00042E30 00a0:
|
|
_object_list_from_ai_reference (0000)
|
|
00042ED0 0220:
|
|
_ai_scripting_attach_unit (0000)
|
|
000430F0 0040:
|
|
_ai_scripting_attach_units (0000)
|
|
00043130 0060:
|
|
_ai_scripting_detach_unit (0000)
|
|
00043190 0090:
|
|
_ai_scripting_detach_units (0000)
|
|
00043220 0090:
|
|
_ai_scripting_place (0000)
|
|
000432B0 0060:
|
|
_ai_scripting_kill_internal (0000)
|
|
00043310 0060:
|
|
_ai_scripting_kill (0000)
|
|
00043370 0060:
|
|
_ai_scripting_kill_silent (0000)
|
|
000433D0 0090:
|
|
_ai_scripting_erase (0000)
|
|
00043460 0030:
|
|
_ai_scripting_erase_all (0000)
|
|
00043490 0020:
|
|
_ai_scripting_deselect (0000)
|
|
000434B0 0040:
|
|
_ai_scripting_select (0000)
|
|
000434F0 0110:
|
|
_ai_scripting_spawn_actor (0000)
|
|
00043600 0080:
|
|
_ai_scripting_set_respawn (0000)
|
|
00043680 0080:
|
|
_ai_scripting_set_deaf (0000)
|
|
00043700 0080:
|
|
_ai_scripting_set_blind (0000)
|
|
00043780 00d0:
|
|
_ai_scripting_magically_see_unit (0000)
|
|
00043850 0040:
|
|
_ai_scripting_magically_see_units (0000)
|
|
00043890 0090:
|
|
_ai_scripting_timer_start (0000)
|
|
00043920 00a0:
|
|
_ai_scripting_timer_expire (0000)
|
|
000439C0 02d0:
|
|
_ai_scripting_count_internal (0000)
|
|
00043C90 0020:
|
|
_ai_scripting_swarm_count (0000)
|
|
00043CB0 0020:
|
|
_ai_scripting_nonswarm_count (0000)
|
|
00043CD0 0020:
|
|
_ai_scripting_living_count (0000)
|
|
00043CF0 0040:
|
|
_ai_scripting_living_fraction (0000)
|
|
00043D30 0030:
|
|
_ai_scripting_strength (0000)
|
|
00043D60 0060:
|
|
_ai_scripting_is_attacking (0000)
|
|
00043DC0 0090:
|
|
_ai_scripting_attack (0000)
|
|
00043E50 0090:
|
|
_ai_scripting_defend (0000)
|
|
00043EE0 0090:
|
|
_ai_scripting_maneuver (0000)
|
|
00043F70 00a0:
|
|
_ai_scripting_maneuver_enable (0000)
|
|
00044010 0430:
|
|
_code_00044010 (0000)
|
|
00044440 0550:
|
|
_code_00044440 (0000)
|
|
00044990 00a0:
|
|
_ai_scripting_migrate (0000)
|
|
00044A30 00f0:
|
|
_code_00044a30 (0000)
|
|
00044B20 0110:
|
|
_ai_scripting_migrate_by_unit (0000)
|
|
00044C30 00e0:
|
|
_ai_scripting_migrate_and_speak (0000)
|
|
00044D10 00f0:
|
|
_ai_scripting_allegiance (0000)
|
|
00044E00 0050:
|
|
_ai_scripting_allegiance_remove (0000)
|
|
00044E50 0050:
|
|
_ai_scripting_allegiance_broken (0000)
|
|
00044EA0 0050:
|
|
_ai_scripting_vehicle_candidate_qsort (0000)
|
|
00044EF0 0060:
|
|
_ai_scripting_going_to_vehicle (0000)
|
|
00044F50 00a0:
|
|
_ai_scripting_exit_vehicle (0000)
|
|
00044FF0 00a0:
|
|
_ai_scripting_braindead (0000)
|
|
00045090 0100:
|
|
_ai_scripting_braindead_by_unit (0000)
|
|
00045190 00a0:
|
|
_ai_scripting_ignore (0000)
|
|
00045230 00a0:
|
|
_ai_scripting_prefer_target (0000)
|
|
000452D0 0120:
|
|
_code_000452d0 (0000)
|
|
000453F0 0060:
|
|
_ai_scripting_teleport_starting_location_if_unsupported (0000)
|
|
00045450 0060:
|
|
_ai_scripting_teleport_starting_location (0000)
|
|
000454B0 0090:
|
|
_ai_scripting_try_to_fight_nothing (0000)
|
|
00045540 00d0:
|
|
_ai_scripting_try_to_fight (0000)
|
|
00045610 0090:
|
|
_ai_scripting_try_to_fight_player (0000)
|
|
000456A0 00a0:
|
|
_ai_scripting_allow_charge (0000)
|
|
00045740 00c0:
|
|
_ai_scripting_command_list (0000)
|
|
00045800 00a0:
|
|
_ai_scripting_command_list_by_unit (0000)
|
|
000458A0 0090:
|
|
_ai_scripting_command_list_advance (0000)
|
|
00045930 0070:
|
|
_ai_scripting_command_list_advance_by_unit (0000)
|
|
000459A0 0050:
|
|
_code_000459a0 (0000)
|
|
000459F0 0250:
|
|
_ai_scripting_command_list_status (0000)
|
|
00045C40 00d0:
|
|
_ai_scripting_free (0000)
|
|
00045D10 00d0:
|
|
_ai_scripting_free_units (0000)
|
|
00045DE0 00e0:
|
|
_ai_scripting_attach_free (0000)
|
|
00045EC0 00b0:
|
|
_ai_scripting_force_active (0000)
|
|
00045F70 00d0:
|
|
_ai_scripting_force_active_by_unit (0000)
|
|
00046040 00d0:
|
|
_ai_scripting_set_return_state (0000)
|
|
00046110 00a0:
|
|
_ai_scripting_set_current_state (0000)
|
|
000461B0 0080:
|
|
_ai_scripting_assess_status (0000)
|
|
00046230 00a0:
|
|
_ai_scripting_status (0000)
|
|
000462D0 0010:
|
|
_ai_scripting_reconnect (0000)
|
|
000462E0 0090:
|
|
_ai_scripting_playfight (0000)
|
|
00046370 01f0:
|
|
_ai_scripting_vehicle_encounter (0000)
|
|
00046560 00a0:
|
|
_ai_scripting_find_vehicle_enterable (0000)
|
|
00046600 0040:
|
|
_ai_scripting_vehicle_enterable_distance (0000)
|
|
00046640 0050:
|
|
_ai_scripting_vehicle_enterable_team (0000)
|
|
00046690 0050:
|
|
_ai_scripting_vehicle_enterable_actor_type (0000)
|
|
000466E0 00a0:
|
|
_ai_scripting_vehicle_enterable_actors (0000)
|
|
00046780 00a0:
|
|
_ai_scripting_vehicle_enterable_disable (0000)
|
|
00046820 0070:
|
|
_ai_scripting_look_at_object (0000)
|
|
00046890 0050:
|
|
_ai_scripting_stop_looking (0000)
|
|
000468E0 00a0:
|
|
_ai_scripting_automatic_migration_target (0000)
|
|
00046980 0080:
|
|
_ai_scripting_follow_target_disable (0000)
|
|
00046A00 0080:
|
|
_ai_scripting_follow_target_players (0000)
|
|
00046A80 0090:
|
|
_ai_scripting_follow_target_unit (0000)
|
|
00046B10 00b0:
|
|
_ai_scripting_follow_target_ai (0000)
|
|
00046BC0 0080:
|
|
_ai_scripting_follow_distance (0000)
|
|
00046C40 0070:
|
|
_ai_scripting_conversation (0000)
|
|
00046CB0 0060:
|
|
_ai_scripting_conversation_stop (0000)
|
|
00046D10 0060:
|
|
_ai_scripting_conversation_advance (0000)
|
|
00046D70 0010:
|
|
_ai_scripting_conversation_line (0000)
|
|
00046D80 0010:
|
|
_ai_scripting_conversation_status (0000)
|
|
00046D90 00b0:
|
|
_ai_scripting_link_activation (0000)
|
|
00046E40 0090:
|
|
_ai_scripting_berserk (0000)
|
|
00046ED0 0070:
|
|
_ai_scripting_set_team (0000)
|
|
00046F40 00a0:
|
|
_ai_scripting_allow_dormant (0000)
|
|
00046FE0 00d0:
|
|
_ai_scripting_magically_see_encounter (0000)
|
|
000470B0 00a0:
|
|
_ai_scripting_magically_see_players (0000)
|
|
00047150 0010:
|
|
_ai_scripting_retreat (0000)
|
|
00047160 0150:
|
|
_ai_scripting_go_to_vehicle_internal (0000)
|
|
000472B0 0080:
|
|
_ai_scripting_go_to_vehicle (0000)
|
|
00047330 0080:
|
|
_ai_scripting_go_to_vehicle_override (0000)
|
|
000473B0 0170:
|
|
_ai_scripting_renew (0000)
|
|
0024BE60 0001:
|
|
_ai_script_squad_separator (0000)
|
|
0024BE68 004d:
|
|
??_C@_0EN@GCPCCILO@platoon_absolute_index?$DO?$DN0?5?$CG?$CG?5pla@ (0000)
|
|
0024BEB8 006b:
|
|
??_C@_0GL@DJKDNA@platoon_index?$DO?$DN0?5?$CG?$CG?5platoon_inde@ (0000)
|
|
0024BF24 0020:
|
|
??_C@_0CA@NFDLKDJF@ai_string?5?$CG?$CG?5ai_index_reference?$AA@ (0000)
|
|
0024BF44 001e:
|
|
??_C@_0BO@MOPGHLE@c?3?2halo?2SOURCE?2ai?2ai_script?4c?$AA@ (0000)
|
|
0024BF64 0009:
|
|
??_C@_08EAMFHCBD@iterator?$AA@ (0000)
|
|
0024BF70 0042:
|
|
??_C@_0EC@OOBEBCBB@ai_attach?3?5could?5not?5find?5a?5squa@ (0000)
|
|
0024BFB8 0058:
|
|
??_C@_0FI@EBLCLMKD@ai_attach?3?5no?5actor?5variant?5spec@ (0000)
|
|
0024C010 001d:
|
|
??_C@_0BN@JAFMEMLM@?$CFs?3?5ai_attach_unit?50x?$CF04X?5?$CFs?$AA@ (0000)
|
|
0024C030 001a:
|
|
??_C@_0BK@DKGEFPIC@?$CFs?3?5ai_detach_unit?50x?$CF04X?$AA@ (0000)
|
|
0024C04C 0010:
|
|
??_C@_0BA@GMNPJHGI@?$CFs?3?5ai_place?5?$CFs?$AA@ (0000)
|
|
0024C05C 000f:
|
|
??_C@_0P@BLMJLDDB@?$CFs?3?5ai_kill?5?$CFs?$AA@ (0000)
|
|
0024C06C 0016:
|
|
??_C@_0BG@OLKJPCEP@?$CFs?3?5ai_kill_silent?5?$CFs?$AA@ (0000)
|
|
0024C084 0010:
|
|
??_C@_0BA@OAGPLBNE@?$CFs?3?5ai_erase?5?$CFs?$AA@ (0000)
|
|
0024C094 0011:
|
|
??_C@_0BB@DAJIABOO@?$CFs?3?5ai_erase_all?$AA@ (0000)
|
|
0024C0A8 0016:
|
|
??_C@_0BG@MJMIDNGO@?$CFs?3?5ai_spawn_actor?5?$CFs?$AA@ (0000)
|
|
0024C0C0 0019:
|
|
??_C@_0BJ@EEKIPIPP@?$CFs?3?5ai_set_respawn?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C0DC 0004:
|
|
??_C@_03MCADLMAF@off?$AA@ (0000)
|
|
0024C0E0 0003:
|
|
??_C@_02LIELOMNJ@on?$AA@ (0000)
|
|
0024C0E4 0016:
|
|
??_C@_0BG@NGLFOKP@?$CFs?3?5ai_set_deaf?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C0FC 0017:
|
|
??_C@_0BH@HLGNJJOK@?$CFs?3?5ai_set_blind?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C114 0024:
|
|
??_C@_0CE@JALKNAAG@?$CFs?3?5ai_magically_see_unit?5?$CFs?50x?$CF@ (0000)
|
|
0024C138 0016:
|
|
??_C@_0BG@LFPOGIIF@?$CFs?3?5ai_timer_start?5?$CFs?$AA@ (0000)
|
|
0024C150 0017:
|
|
??_C@_0BH@JJJMMONM@?$CFs?3?5ai_timer_expire?5?$CFs?$AA@ (0000)
|
|
0024C168 003d:
|
|
??_C@_0DN@EGOGAENI@?$CIcount_type?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIcount_type@ (0000)
|
|
0024C1A8 0011:
|
|
??_C@_0BB@JNDCBAGE@?$CFs?3?5ai_attack?5?$CFs?$AA@ (0000)
|
|
0024C1BC 0011:
|
|
??_C@_0BB@DELPBLFI@?$CFs?3?5ai_defend?5?$CFs?$AA@ (0000)
|
|
0024C1D0 0013:
|
|
??_C@_0BD@CBLKAFAE@?$CFs?3?5ai_maneuver?5?$CFs?$AA@ (0000)
|
|
0024C1E4 001d:
|
|
??_C@_0BN@CEFNBAKE@?$CFs?3?5ai_maneuver_enable?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C204 0038:
|
|
??_C@_0DI@LFBKAFPO@?5?5?$CFs?5?9?$DO?5?$CFs?5?$CIno?5matching?5squads?5f@ (0000)
|
|
0024C23C 0025:
|
|
??_C@_0CF@LALAHEHA@?5?5?$CFs?5?9?$DO?5?$CFs?5?$CIno?5matching?5types?5fo@ (0000)
|
|
0024C264 001a:
|
|
??_C@_0BK@CDOPAPNJ@?5?5?$CFs?5?9?$DO?5?$CFs?5?$CIsame?9type?5?$CFs?$CJ?$AA@ (0000)
|
|
0024C280 001b:
|
|
??_C@_0BL@GFNCBEPH@?5?5?$CFs?5?9?$DO?5?$CFs?5?$CIsame?9actor?5?$CFs?$CJ?$AA@ (0000)
|
|
0024C2A0 005c:
|
|
??_C@_0FM@JEMPJMEH@?$CIfound_squad_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIfou@ (0000)
|
|
0024C2FC 001d:
|
|
??_C@_0BN@BJIJNCFF@?5?5?$CFs?5?9?$DO?5?$CFs?5?$CIsame?9variant?5?$CFs?$CJ?$AA@ (0000)
|
|
0024C31C 000f:
|
|
??_C@_0P@HILOFCFK@?5?5?$CFs?5unchanged?$AA@ (0000)
|
|
0024C330 008c:
|
|
??_C@_0IM@CIJDMBDM@?$CItarget_squad_indices?$FLcurrent_sq@ (0000)
|
|
0024C3C0 0053:
|
|
??_C@_0FD@PAODIOCF@?$CIcurrent_squad_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIc@ (0000)
|
|
0024C414 0009:
|
|
??_C@_08DLMOHGLB@squad?5?$CFs?$AA@ (0000)
|
|
0024C420 0063:
|
|
??_C@_0GD@DHCPGCGK@?$CIsource_iterator?4squad_index?5?$DO?$DN?5@ (0000)
|
|
0024C484 0015:
|
|
??_C@_0BF@OECKHMKF@?$CFs?3?5ai_migrate?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C49C 0005:
|
|
??_C@_04NPEDKLDA@unit?$AA@ (0000)
|
|
0024C4A4 0026:
|
|
??_C@_0CG@IJGCILMG@?$CFs?3?5ai_migrate_by_unit?5?$DMsome?5guy@ (0000)
|
|
0024C4D0 0050:
|
|
??_C@_0FA@GAKJCPBM@ai_migrate_and_speak?3?5unknown?5sp@ (0000)
|
|
0024C520 0022:
|
|
??_C@_0CC@JKLCPGKI@?$CFs?3?5ai_migrate_and_speak?5?$CFs?5?$CFs?5?$CF@ (0000)
|
|
0024C544 0018:
|
|
??_C@_0BI@ONAJBOCN@?$CFs?3?5ai_allegiance?5?$CFd?5?$CFd?$AA@ (0000)
|
|
0024C55C 001f:
|
|
??_C@_0BP@ICGEEHN@?$CFs?3?5ai_allegiance_remove?5?$CFd?5?$CFd?$AA@ (0000)
|
|
0024C57C 0017:
|
|
??_C@_0BH@HFKAOKKA@?$CFs?3?5ai_exit_vehicle?5?$CFs?$AA@ (0000)
|
|
0024C594 0017:
|
|
??_C@_0BH@NDIAEFNI@?$CFs?3?5ai_braindead?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C5AC 0028:
|
|
??_C@_0CI@LMFNPANN@?$CFs?3?5ai_braindead_by_unit?5?$DMsome?5g@ (0000)
|
|
0024C5D4 0020:
|
|
??_C@_0CA@GOECGNLE@?$CFs?3?5ai_disregard?5?$DMsome?5guys?$DO?5?$CFs?$AA@ (0000)
|
|
0024C5F4 0024:
|
|
??_C@_0CE@HDJGLAIB@?$CFs?3?5ai_prefer_target?5?$DMsome?5guys?$DO@ (0000)
|
|
0024C618 0034:
|
|
??_C@_0DE@FNEPLHAJ@?$CFs?3?5ai_teleport_starting_locatio@ (0000)
|
|
0024C64C 0025:
|
|
??_C@_0CF@JAILDLKJ@?$CFs?3?5ai_teleport_starting_locatio@ (0000)
|
|
0024C674 001f:
|
|
??_C@_0BP@MEILCKDB@?$CFs?3?5ai_try_to_fight_nothing?5?$CFs?$AA@ (0000)
|
|
0024C694 001a:
|
|
??_C@_0BK@BDNNJAGD@?$CFs?3?5ai_try_to_fight?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C6B0 001e:
|
|
??_C@_0BO@OIEPGOJB@?$CFs?3?5ai_try_to_fight_player?5?$CFs?$AA@ (0000)
|
|
0024C6D0 001a:
|
|
??_C@_0BK@MBMJKENK@?$CFs?3?5ai_allow_charge?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C6EC 0006:
|
|
??_C@_05LAPONLG@false?$AA@ (0000)
|
|
0024C6F4 0005:
|
|
??_C@_04LOAJBDKD@true?$AA@ (0000)
|
|
0024C6FC 001a:
|
|
??_C@_0BK@ECBKDHID@?$CFs?3?5ai_command_list?5?$CFs?5?$CFd?$AA@ (0000)
|
|
0024C718 0026:
|
|
??_C@_0CG@KDJJNHNP@?$CFs?3?5ai_command_list_by_unit?5?$DMuni@ (0000)
|
|
0024C740 001f:
|
|
??_C@_0BP@PAICMAGO@?$CFs?3?5ai_command_list_advance?5?$CFs?$AA@ (0000)
|
|
0024C760 0030:
|
|
??_C@_0DA@BIDMGCPJ@?$CFs?3?5ai_command_list_advance_by_u@ (0000)
|
|
0024C790 000f:
|
|
??_C@_0P@MPAKFNGN@?$CFs?3?5ai_free?5?$CFs?$AA@ (0000)
|
|
0024C7A0 001f:
|
|
??_C@_0BP@HEGBFLAM@?$CFs?3?5ai_free_units?5?$DMsome?5units?$DO?$AA@ (0000)
|
|
0024C7C0 0036:
|
|
??_C@_0DG@OFCIAFCL@?$CFs?3?5ai_attach_free?5?$CFs?5cannot?5be?5@ (0000)
|
|
0024C7F8 001d:
|
|
??_C@_0BN@IEADAMKD@?$CFs?3?5ai_attach_free?50x?$CF04X?5?$CFs?$AA@ (0000)
|
|
0024C818 001a:
|
|
??_C@_0BK@NGFIDLOP@?$CFs?3?5ai_force_active?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C838 0063:
|
|
??_C@_0GD@LKDLLFKG@ai_force_active_by_unit?3?5unit?5is@ (0000)
|
|
0024C89C 002b:
|
|
??_C@_0CL@INBDPDM@?$CFs?3?5ai_force_active_by_unit?5?$DMsom@ (0000)
|
|
0024C8C8 001e:
|
|
??_C@_0BO@GELBKNIM@?$CFs?3?5ai_set_return_state?5?$CFs?5?$CFd?$AA@ (0000)
|
|
0024C8E8 001f:
|
|
??_C@_0BP@OMBCPLPA@?$CFs?3?5ai_set_current_state?5?$CFs?5?$CFd?$AA@ (0000)
|
|
0024C908 0011:
|
|
??_C@_0BB@DKMLPAFC@?$CFs?3?5ai_status?5?$CFs?$AA@ (0000)
|
|
0024C91C 0017:
|
|
??_C@_0BH@LGGEBDHB@?$CFs?3?5ai_playfight?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024C934 0028:
|
|
??_C@_0CI@NHKIHJII@?$CFs?3?5ai_vehicle_encounter?5?$DMsome?5u@ (0000)
|
|
0024C95C 003e:
|
|
??_C@_0DO@IHGIOMGJ@ai_vehicle_enterable?3?5too?5many?5e@ (0000)
|
|
0024C99C 0031:
|
|
??_C@_0DB@BDJKMKHA@?$CFs?3?5ai_vehicle_enterable_distanc@ (0000)
|
|
0024C9D0 0030:
|
|
??_C@_0DA@FDMBDBPG@?$CFs?3?5ai_vehicle_enterable_team?5?$DMs@ (0000)
|
|
0024CA00 0036:
|
|
??_C@_0DG@LLMHMJE@?$CFs?3?5ai_vehicle_enterable_actor_t@ (0000)
|
|
0024CA38 0043:
|
|
??_C@_0ED@KEKJMIKK@ai_vehicle_enterable_actors?3?5too@ (0000)
|
|
0024CA7C 0032:
|
|
??_C@_0DC@OHFPMGGD@?$CFs?3?5ai_vehicle_enterable_actors?5@ (0000)
|
|
0024CAB0 0030:
|
|
??_C@_0DA@KCDIODEC@?$CFs?3?5ai_vehicle_enterable_disable@ (0000)
|
|
0024CAE0 0030:
|
|
??_C@_0DA@MEBODMJO@?$CFs?3?5ai_look_at_object?5?$DMsome?5unit@ (0000)
|
|
0024CB10 0020:
|
|
??_C@_0CA@PDOKPGNA@?$CFs?3?5ai_stop_looking?5?$DMsome?5unit?$DO?$AA@ (0000)
|
|
0024CB30 0028:
|
|
??_C@_0CI@LKLFKNDG@?$CFs?3?5ai_automatic_migration_targe@ (0000)
|
|
0024CB58 0020:
|
|
??_C@_0CA@HJOJOKEG@?$CFs?3?5ai_follow_target_disable?5?$CFs?$AA@ (0000)
|
|
0024CB78 0020:
|
|
??_C@_0CA@LPFMOJGF@?$CFs?3?5ai_follow_target_players?5?$CFs?$AA@ (0000)
|
|
0024CB98 0029:
|
|
??_C@_0CJ@PMNFIMPA@?$CFs?3?5ai_follow_target_unit?5?$CFs?5?$DMso@ (0000)
|
|
0024CBC4 001e:
|
|
??_C@_0BO@NGDADOD@?$CFs?3?5ai_follow_target_ai?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024CBE4 001f:
|
|
??_C@_0BP@FMPPICBE@?$CFs?3?5ai_follow_distance?5?$CFs?5?$CF?41f?$AA@ (0000)
|
|
0024CC04 0017:
|
|
??_C@_0BH@IGJNNOIC@?$CFs?3?5ai_conversation?5?$CFs?$AA@ (0000)
|
|
0024CC1C 001c:
|
|
??_C@_0BM@HGJNKNLN@?$CFs?3?5ai_conversation_stop?5?$CFs?$AA@ (0000)
|
|
0024CC38 001f:
|
|
??_C@_0BP@IBOEPNGC@?$CFs?3?5ai_conversation_advance?5?$CFs?$AA@ (0000)
|
|
0024CC58 006a:
|
|
??_C@_0GK@GMJOKPPN@ai_link_activation?3?5cannot?5link?5@ (0000)
|
|
0024CCC4 001d:
|
|
??_C@_0BN@OAHKKHJB@?$CFs?3?5ai_link_activation?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024CCE4 0012:
|
|
??_C@_0BC@GFJPNDGO@?$CFs?3?5ai_berserk?5?$CFs?$AA@ (0000)
|
|
0024CCF8 001b:
|
|
??_C@_0BL@MKHHLLAC@?$CFs?3?5ai_allow_dormant?5?$CFs?5?$CFs?$AA@ (0000)
|
|
0024CD14 0025:
|
|
??_C@_0CF@EKNEFNDI@?$CFs?3?5ai_magically_see_encounter?5?$CF@ (0000)
|
|
0024CD3C 0020:
|
|
??_C@_0CA@ENGIEHJO@?$CFs?3?5ai_magically_see_players?5?$CFs?$AA@ (0000)
|
|
0024CD5C 0022:
|
|
??_C@_0CC@LGDPOIEJ@?$CFs?3?5ai_go_to_vehicle?5?$CFs?50x?$CF04X?5?$CF@ (0000)
|
|
0024CD80 002b:
|
|
??_C@_0CL@HNAPEBL@?$CFs?3?5ai_go_to_vehicle_override?5?$CFs@ (0000)
|
|
0024CDAC 0010:
|
|
??_C@_0BA@GHFHPDND@?$CFs?3?5ai_renew?5?$CFs?$AA@ (0000)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "ai/ai.h"
|
|
#include "ai/actions.h"
|
|
#include "ai/actor_definitions.h"
|
|
#include "ai/actor_types.h"
|
|
#include "ai/actors.h"
|
|
#include "ai/ai_communication.h"
|
|
#include "ai/ai_debug.h"
|
|
#include "ai/ai_scenario_definitions.h"
|
|
#include "ai/encounters.h"
|
|
#include "ai/props.h"
|
|
#include "ai/ai_script.h"
|
|
#include "cseries/errors.h"
|
|
#include "cache/cache_files.h"
|
|
#include "game/game.h"
|
|
#include "game/game_allegiance.h"
|
|
#include "game/players.h"
|
|
#include "hs/hs.h"
|
|
#include "hs/object_lists.h"
|
|
#include "memory/data.h"
|
|
#include "objects/objects.h"
|
|
#include "scenario/scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "units/bipeds.h"
|
|
#include "units/units.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
_ai_count_living = 0,
|
|
_ai_count_swarm,
|
|
_ai_count_nonswarm,
|
|
NUMBER_OF_AI_COUNT_TYPES,
|
|
};
|
|
|
|
enum ai_reference_type
|
|
{
|
|
_ai_reference_type_encounter = 0,
|
|
_ai_reference_type_platoon,
|
|
_ai_reference_type_squad,
|
|
NUMBER_OF_AI_REFERENCE_TYPES,
|
|
};
|
|
|
|
enum
|
|
{
|
|
MAXIMUM_ACTIVATION_LINK_INDICES_PER_ENCOUNTER = 3,
|
|
MAXIMUM_AI_ENTERABLE_VEHICLES = 32,
|
|
MAXIMUM_AI_INDICES_PER_ENTERABLE_VEHICLE = 6,
|
|
};
|
|
|
|
/* actor default states (actors.c keeps this enum file-local too) */
|
|
enum actor_default_state
|
|
{
|
|
actor_default_state_none = 0,
|
|
actor_default_state_asleep,
|
|
actor_default_state_alert,
|
|
actor_default_state_moving_repeat_position,
|
|
actor_default_state_moving_loop,
|
|
actor_default_state_moving_loop_back_and_forth,
|
|
actor_default_state_moving_loop_randomly,
|
|
actor_default_state_moving_randomly,
|
|
actor_default_state_guarding,
|
|
actor_default_state_guarding_at_guard_point,
|
|
actor_default_state_searching,
|
|
actor_default_state_fleeing,
|
|
number_of_actor_default_states,
|
|
};
|
|
|
|
/* actor_state_data.mode (actors.c keeps this enum file-local too) */
|
|
enum
|
|
{
|
|
_actor_mode_braindead = 0,
|
|
_actor_mode_alert = 2,
|
|
_actor_mode_combat,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_actor_combat_status_none = 0,
|
|
};
|
|
|
|
/* ai unit effect types (ai.h does not yet declare these) */
|
|
enum
|
|
{
|
|
_ai_unit_effect_bump = 0,
|
|
_ai_unit_effect_shooting,
|
|
_ai_unit_effect_death_scream,
|
|
_ai_unit_effect_magic_sight,
|
|
NUMBER_OF_AI_UNIT_EFFECTS,
|
|
};
|
|
|
|
/* actor_external_orders.desired_target_type (actors.h does not yet declare these) */
|
|
enum
|
|
{
|
|
_desired_target_none = 0,
|
|
_desired_target_ai,
|
|
_desired_target_player,
|
|
NUMBER_OF_DESIRED_TARGET_TYPES,
|
|
};
|
|
|
|
/* encounter_datum.follow_target_type (encounters.h does not yet declare these) */
|
|
enum
|
|
{
|
|
_follow_target_none = 0,
|
|
_follow_target_players,
|
|
_follow_target_unit,
|
|
_follow_target_ai,
|
|
NUMBER_OF_FOLLOW_TARGET_TYPES,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
/* ---------- structures */
|
|
|
|
struct ai_script_vehicle_enterable_data
|
|
{
|
|
long vehicle_index;
|
|
real radius;
|
|
short team_bitmask;
|
|
short actor_type_bitmask;
|
|
short ai_indices_count;
|
|
word __pad0E;
|
|
long ai_indices[MAXIMUM_AI_INDICES_PER_ENTERABLE_VEHICLE];
|
|
};
|
|
|
|
struct ai_script_globals_data
|
|
{
|
|
boolean ai_active;
|
|
boolean ai_initialized_for_map;
|
|
byte __unknown2[0x3B4];
|
|
short enterable_vehicle_count;
|
|
struct ai_script_vehicle_enterable_data enterable_vehicles[MAXIMUM_AI_ENTERABLE_VEHICLES];
|
|
};
|
|
|
|
typedef char ai_script_squad_iterator_size_assert[
|
|
sizeof(struct ai_script_squad_iterator) == 0x14 ? 1 : -1];
|
|
|
|
struct actor_iterator
|
|
{
|
|
struct data_iterator encounter_iterator;
|
|
boolean iterated_encounterless_list;
|
|
boolean active_only;
|
|
byte pad[2];
|
|
long index;
|
|
long next_index;
|
|
};
|
|
|
|
struct encounter_actor_iterator
|
|
{
|
|
long encounter_index;
|
|
long index;
|
|
long next_index;
|
|
};
|
|
|
|
struct ai_script_vehicle_candidate
|
|
{
|
|
long actor_index;
|
|
real distance_squared;
|
|
boolean already_going_to_vehicle;
|
|
byte pad[3];
|
|
};
|
|
|
|
struct ai_script_conversation_definition
|
|
{
|
|
char name[32];
|
|
byte unknown[84];
|
|
};
|
|
|
|
typedef char ai_script_actor_iterator_size_assert[
|
|
sizeof(struct actor_iterator) == 0x1C ? 1 : -1];
|
|
typedef char ai_script_actor_reference_iterator_size_assert[
|
|
sizeof(struct ai_script_actor_reference_iterator) == 0x18 ? 1 : -1];
|
|
typedef char ai_script_actor_reference_iterator_actor_index_offset_assert[
|
|
offsetof(struct ai_script_actor_reference_iterator, actor_index) == 0x10 ? 1 : -1];
|
|
typedef char ai_script_vehicle_enterable_size_assert[
|
|
sizeof(struct ai_script_vehicle_enterable) == 0xC ? 1 : -1];
|
|
typedef char ai_script_vehicle_enterable_radius_offset_assert[
|
|
offsetof(struct ai_script_vehicle_enterable, radius) == 0x4 ? 1 : -1];
|
|
typedef char ai_script_vehicle_enterable_team_offset_assert[
|
|
offsetof(struct ai_script_vehicle_enterable, team_bitmask) == 0x8 ? 1 : -1];
|
|
typedef char ai_script_vehicle_enterable_actor_type_offset_assert[
|
|
offsetof(struct ai_script_vehicle_enterable, actor_type_bitmask) == 0xA ? 1 : -1];
|
|
typedef char ai_script_vehicle_enterable_data_size_assert[
|
|
sizeof(struct ai_script_vehicle_enterable_data) == 0x28 ? 1 : -1];
|
|
typedef char ai_script_globals_enterable_vehicle_count_offset_assert[
|
|
offsetof(struct ai_script_globals_data, enterable_vehicle_count) == 0x3B6 ? 1 : -1];
|
|
typedef char ai_script_globals_enterable_vehicles_offset_assert[
|
|
offsetof(struct ai_script_globals_data, enterable_vehicles) == 0x3B8 ? 1 : -1];
|
|
typedef char ai_script_platoon_iterator_size_assert[
|
|
sizeof(struct ai_script_platoon_iterator) == 0xC ? 1 : -1];
|
|
typedef char ai_script_vehicle_candidate_size_assert[
|
|
sizeof(struct ai_script_vehicle_candidate) == 0xC ? 1 : -1];
|
|
typedef char ai_script_vehicle_candidate_distance_offset_assert[
|
|
offsetof(struct ai_script_vehicle_candidate, distance_squared) == 0x4 ? 1 : -1];
|
|
typedef char ai_script_vehicle_candidate_state_offset_assert[
|
|
offsetof(struct ai_script_vehicle_candidate, already_going_to_vehicle) == 0x8 ? 1 : -1];
|
|
/* ---------- prototypes */
|
|
|
|
static long ai_scripting_assess_status(
|
|
long actor_index);
|
|
static void ai_scripting_teleport_starting_location_private(
|
|
long ai_reference,
|
|
boolean only_if_unsupported);
|
|
static void ai_scripting_kill_internal(
|
|
long ai_reference,
|
|
boolean silent);
|
|
static long ai_scripting_count_internal(
|
|
long ai_reference,
|
|
short count_type,
|
|
long *original_count_reference,
|
|
real *strength_reference);
|
|
static int ai_scripting_vehicle_candidate_qsort(
|
|
void const *candidate0,
|
|
void const *candidate1);
|
|
static void ai_scripting_go_to_vehicle_internal(
|
|
long ai_reference,
|
|
long unit_index,
|
|
char const *seat_substring_name,
|
|
boolean override_previous_vehicles);
|
|
static short ai_scripting_migrate_find_target_squad(
|
|
long source_encounter_index,
|
|
short source_squad_index,
|
|
struct actor_definition *source_actor_definition,
|
|
struct actor_variant_definition *source_variant_definition,
|
|
boolean same_encounter,
|
|
long target_ai_reference,
|
|
char const *debug_description);
|
|
static void ai_scripting_migrate_internal(
|
|
long source_ai_reference,
|
|
long target_ai_reference,
|
|
boolean generate_stimulus,
|
|
boolean advancing_stimulus);
|
|
static void ai_scripting_migrate_by_unit_internal(
|
|
long unit_index,
|
|
long target_ai_reference,
|
|
boolean generate_stimulus,
|
|
boolean advancing_stimulus);
|
|
static short ai_scripting_command_list_status_internal(
|
|
long actor_index,
|
|
long unit_index,
|
|
short command_list_index,
|
|
struct obey_individual_simple_control *simple_control,
|
|
struct obey_individual_complex_control *complex_control);
|
|
|
|
/* ---------- globals */
|
|
|
|
extern struct ai_script_globals_data *ai_globals;
|
|
|
|
char const ai_script_squad_separator = '/';
|
|
|
|
/* ---------- public code */
|
|
|
|
void ai_script_initialize(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void ai_script_dispose(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void ai_script_initialize_for_new_map(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void ai_script_dispose_from_old_map(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void ai_index_to_string(
|
|
long ai_reference,
|
|
struct scenario *scenario,
|
|
char *buffer,
|
|
long buffer_size)
|
|
{
|
|
if (ai_reference == NONE)
|
|
{
|
|
csstrncpy(buffer, "none", buffer_size);
|
|
}
|
|
else
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_encounters,
|
|
ai_reference & UNSIGNED_SHORT_MAX,
|
|
struct encounter_definition);
|
|
|
|
switch ((unsigned long)ai_reference >> 30)
|
|
{
|
|
case _ai_reference_type_encounter:
|
|
_snprintf(buffer, buffer_size, "%s", encounter_definition->name);
|
|
break;
|
|
case _ai_reference_type_platoon:
|
|
_snprintf(
|
|
buffer,
|
|
buffer_size,
|
|
"%s/%s",
|
|
encounter_definition->name,
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->platoons,
|
|
(ai_reference >> 16) & UNSIGNED_CHAR_MAX,
|
|
struct platoon_definition)->name);
|
|
break;
|
|
case _ai_reference_type_squad:
|
|
_snprintf(
|
|
buffer,
|
|
buffer_size,
|
|
"%s/%s",
|
|
encounter_definition->name,
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
(ai_reference >> 16) & UNSIGNED_CHAR_MAX,
|
|
struct squad_definition)->name);
|
|
break;
|
|
default:
|
|
csstrncpy(buffer, "<error>", buffer_size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean ai_index_from_string(
|
|
struct scenario *scenario,
|
|
char const *ai_string,
|
|
long *ai_index_reference)
|
|
{
|
|
long ai_reference = NONE;
|
|
boolean success;
|
|
char const *separator;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 87, ai_string && ai_index_reference);
|
|
|
|
if (!_stricmp(ai_string, "none"))
|
|
{
|
|
success = TRUE;
|
|
}
|
|
else
|
|
{
|
|
separator = strrchr(ai_string, ai_script_squad_separator);
|
|
if (!separator)
|
|
{
|
|
long encounter_index = scenario_get_encounter_by_name(scenario, ai_string);
|
|
|
|
if (encounter_index != NONE)
|
|
ai_reference = encounter_index & UNSIGNED_SHORT_MAX;
|
|
}
|
|
else
|
|
{
|
|
long encounter_name_length = separator - ai_string;
|
|
|
|
if (encounter_name_length <= TAG_STRING_LENGTH)
|
|
{
|
|
char encounter_name[TAG_STRING_LENGTH+1];
|
|
long encounter_index;
|
|
|
|
csstrncpy(encounter_name, ai_string, encounter_name_length);
|
|
encounter_name[encounter_name_length] = 0;
|
|
|
|
encounter_index = scenario_get_encounter_by_name(scenario, encounter_name);
|
|
if (encounter_index != NONE)
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_encounters,
|
|
encounter_index,
|
|
struct encounter_definition);
|
|
long squad_index = encounter_definition_get_squad_by_name(
|
|
encounter_definition,
|
|
separator + 1);
|
|
|
|
if (squad_index != NONE)
|
|
{
|
|
ai_reference = (_ai_reference_type_squad << 30) |
|
|
((squad_index & UNSIGNED_CHAR_MAX) << 16) |
|
|
(encounter_index & UNSIGNED_SHORT_MAX);
|
|
}
|
|
else
|
|
{
|
|
long platoon_index = encounter_definition_get_platoon_by_name(
|
|
encounter_definition,
|
|
separator + 1);
|
|
|
|
if (platoon_index != NONE)
|
|
{
|
|
ai_reference = (_ai_reference_type_platoon << 30) |
|
|
((platoon_index & UNSIGNED_CHAR_MAX) << 16) |
|
|
(encounter_index & UNSIGNED_SHORT_MAX);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
success = ai_reference != NONE;
|
|
}
|
|
|
|
*ai_index_reference = ai_reference;
|
|
|
|
return success;
|
|
}
|
|
|
|
void ai_index_squad_iterator_new(
|
|
long ai_reference,
|
|
struct ai_script_squad_iterator *iterator)
|
|
{
|
|
struct scenario *scenario = global_scenario_try_and_get();
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 282, iterator);
|
|
|
|
iterator->encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
|
|
if (scenario &&
|
|
ai_globals->ai_initialized_for_map &&
|
|
VALID_INDEX(iterator->encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
iterator->encounter_index & UNSIGNED_SHORT_MAX,
|
|
struct encounter_definition);
|
|
|
|
switch ((unsigned long)ai_reference >> 30)
|
|
{
|
|
case _ai_reference_type_encounter:
|
|
case _ai_reference_type_platoon:
|
|
iterator->squad_index = NONE;
|
|
iterator->next_squad_index = 0;
|
|
iterator->last_squad_index = encounter_definition->squads.count - 1;
|
|
if (((unsigned long)ai_reference >> 30) == _ai_reference_type_encounter)
|
|
iterator->required_platoon_index = NONE;
|
|
else
|
|
iterator->required_platoon_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
break;
|
|
case _ai_reference_type_squad:
|
|
{
|
|
short squad_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
|
|
if (VALID_INDEX(squad_index, encounter_definition->squads.count))
|
|
{
|
|
iterator->squad_index = NONE;
|
|
iterator->last_squad_index = squad_index;
|
|
iterator->next_squad_index = squad_index;
|
|
iterator->required_platoon_index = NONE;
|
|
}
|
|
else
|
|
{
|
|
iterator->encounter_index = NONE;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
iterator->encounter_index = NONE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iterator->encounter_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct squad_datum *ai_index_squad_iterator_next(
|
|
struct ai_script_squad_iterator *iterator)
|
|
{
|
|
struct squad_datum *squad = NULL;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 351, iterator);
|
|
|
|
if (iterator->encounter_index != NONE)
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(iterator->encounter_index);
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
iterator->encounter_index & UNSIGNED_SHORT_MAX,
|
|
struct encounter_definition);
|
|
|
|
while (iterator->next_squad_index <= iterator->last_squad_index)
|
|
{
|
|
struct squad_definition *squad_definition;
|
|
|
|
iterator->squad_index = iterator->next_squad_index;
|
|
iterator->next_squad_index++;
|
|
squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
iterator->squad_index,
|
|
struct squad_definition);
|
|
|
|
if (iterator->required_platoon_index == NONE ||
|
|
squad_definition->platoon_index == iterator->required_platoon_index)
|
|
{
|
|
squad = encounter_get_squad(encounter, iterator->squad_index);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return squad;
|
|
}
|
|
|
|
void ai_index_platoon_iterator_new(
|
|
long ai_reference,
|
|
struct ai_script_platoon_iterator *iterator)
|
|
{
|
|
struct scenario *scenario = global_scenario_try_and_get();
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 191, iterator);
|
|
|
|
iterator->encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
|
|
if (scenario &&
|
|
ai_globals->ai_initialized_for_map &&
|
|
VALID_INDEX(iterator->encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
iterator->encounter_index & UNSIGNED_SHORT_MAX,
|
|
struct encounter_definition);
|
|
|
|
switch ((unsigned long)ai_reference >> 30)
|
|
{
|
|
case _ai_reference_type_encounter:
|
|
iterator->platoon_index = 0;
|
|
iterator->last_platoon_index = encounter_definition->platoons.count - 1;
|
|
break;
|
|
case _ai_reference_type_platoon:
|
|
case _ai_reference_type_squad:
|
|
if (((unsigned long)ai_reference >> 30) == _ai_reference_type_squad)
|
|
{
|
|
long squad_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
|
|
if (VALID_INDEX(squad_index, encounter_definition->squads.count))
|
|
{
|
|
iterator->platoon_index = TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
squad_index,
|
|
struct squad_definition)->platoon_index;
|
|
}
|
|
else
|
|
{
|
|
iterator->platoon_index = NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iterator->platoon_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
}
|
|
|
|
if (VALID_INDEX(iterator->platoon_index, encounter_definition->platoons.count))
|
|
iterator->last_platoon_index = iterator->platoon_index;
|
|
else
|
|
iterator->encounter_index = NONE;
|
|
break;
|
|
default:
|
|
iterator->encounter_index = NONE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iterator->encounter_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct platoon_datum *ai_index_platoon_iterator_next(
|
|
struct ai_script_platoon_iterator *iterator)
|
|
{
|
|
struct platoon_datum *platoon = NULL;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 265, iterator);
|
|
|
|
if (iterator->encounter_index != NONE &&
|
|
iterator->platoon_index <= iterator->last_platoon_index)
|
|
{
|
|
platoon = encounter_get_platoon(
|
|
encounter_get(iterator->encounter_index),
|
|
iterator->platoon_index);
|
|
iterator->platoon_index++;
|
|
}
|
|
|
|
return platoon;
|
|
}
|
|
|
|
void ai_index_actor_iterator_new(
|
|
long ai_reference,
|
|
struct ai_script_actor_reference_iterator *iterator)
|
|
{
|
|
struct scenario *scenario = global_scenario_try_and_get();
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 384, iterator);
|
|
|
|
iterator->encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
|
|
if (scenario &&
|
|
ai_globals->ai_initialized_for_map &&
|
|
VALID_INDEX(iterator->encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
iterator->encounter_index & UNSIGNED_SHORT_MAX,
|
|
struct encounter_definition);
|
|
|
|
iterator->platoon_index = NONE;
|
|
iterator->squad_index = NONE;
|
|
|
|
switch ((unsigned long)ai_reference >> 30)
|
|
{
|
|
case _ai_reference_type_encounter:
|
|
break;
|
|
case _ai_reference_type_platoon:
|
|
iterator->platoon_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
break;
|
|
case _ai_reference_type_squad:
|
|
iterator->squad_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
break;
|
|
default:
|
|
iterator->encounter_index = NONE;
|
|
break;
|
|
}
|
|
|
|
if (iterator->encounter_index != NONE)
|
|
{
|
|
encounter_actor_iterator_new(
|
|
(struct encounter_actor_iterator *)&iterator->actor_encounter_index,
|
|
iterator->encounter_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iterator->encounter_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct actor_datum *ai_index_actor_iterator_next(
|
|
struct ai_script_actor_reference_iterator *iterator)
|
|
{
|
|
struct actor_datum *actor;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 442, iterator);
|
|
|
|
do
|
|
{
|
|
actor = encounter_actor_iterator_next(
|
|
(struct encounter_actor_iterator *)&iterator->actor_encounter_index);
|
|
}
|
|
while (actor &&
|
|
((iterator->squad_index != NONE &&
|
|
iterator->squad_index != actor->meta.squad_index) ||
|
|
(iterator->platoon_index != NONE &&
|
|
iterator->platoon_index != actor->meta.platoon_index)));
|
|
|
|
return actor;
|
|
}
|
|
|
|
long object_list_from_ai_reference(
|
|
long ai_reference)
|
|
{
|
|
long object_list_index = NONE;
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
object_list_index = object_list_new();
|
|
if (object_list_index != NONE)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
long swarm_unit_index;
|
|
|
|
if (actor->meta.unit_index != NONE)
|
|
object_list_add(object_list_index, actor->meta.unit_index);
|
|
|
|
for (swarm_unit_index = actor->meta.swarm_unit_index;
|
|
swarm_unit_index != NONE;)
|
|
{
|
|
struct unit_datum *unit = unit_get(swarm_unit_index);
|
|
|
|
object_list_add(object_list_index, swarm_unit_index);
|
|
swarm_unit_index = unit->unit.swarm_next_unit_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return object_list_index;
|
|
}
|
|
|
|
void ai_scripting_reconnect(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
short ai_scripting_conversation_line(
|
|
short conversation_index)
|
|
{
|
|
return ai_conversation_line(conversation_index);
|
|
}
|
|
|
|
short ai_scripting_conversation_status(
|
|
short conversation_index)
|
|
{
|
|
return ai_conversation_status(conversation_index);
|
|
}
|
|
|
|
void ai_scripting_retreat(
|
|
long ai_reference)
|
|
{
|
|
ai_scripting_maneuver(ai_reference);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_maneuver(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_maneuver %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_platoon_iterator iterator;
|
|
struct platoon_datum *platoon;
|
|
|
|
ai_index_platoon_iterator_new(ai_reference, &iterator);
|
|
for (platoon = ai_index_platoon_iterator_next(&iterator);
|
|
platoon;
|
|
platoon = ai_index_platoon_iterator_next(&iterator))
|
|
{
|
|
platoon->maneuvering = TRUE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_attack(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_attack %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_platoon_iterator iterator;
|
|
struct platoon_datum *platoon;
|
|
|
|
ai_index_platoon_iterator_new(ai_reference, &iterator);
|
|
for (platoon = ai_index_platoon_iterator_next(&iterator);
|
|
platoon;
|
|
platoon = ai_index_platoon_iterator_next(&iterator))
|
|
{
|
|
platoon->defending = FALSE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_defend(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_defend %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_platoon_iterator iterator;
|
|
struct platoon_datum *platoon;
|
|
|
|
ai_index_platoon_iterator_new(ai_reference, &iterator);
|
|
for (platoon = ai_index_platoon_iterator_next(&iterator);
|
|
platoon;
|
|
platoon = ai_index_platoon_iterator_next(&iterator))
|
|
{
|
|
platoon->defending = TRUE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_maneuver_enable(
|
|
long ai_reference,
|
|
boolean enable)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_maneuver_enable %s %s", hs_runtime_get_executing_thread_name(), ai_name, enable ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_platoon_iterator iterator;
|
|
struct platoon_datum *platoon;
|
|
|
|
ai_index_platoon_iterator_new(ai_reference, &iterator);
|
|
for (platoon = ai_index_platoon_iterator_next(&iterator);
|
|
platoon;
|
|
platoon = ai_index_platoon_iterator_next(&iterator))
|
|
{
|
|
platoon->maneuver_disable = !enable;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_berserk(
|
|
long ai_reference,
|
|
boolean berserk)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_berserk %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
actor_berserk(iterator.actor_index, berserk);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_playfight(
|
|
long ai_reference,
|
|
boolean playfight)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_playfight %s %s", hs_runtime_get_executing_thread_name(), ai_name, playfight ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_get(ai_reference & UNSIGNED_SHORT_MAX)->playfighting = playfight;
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_braindead(
|
|
long ai_reference,
|
|
boolean braindead)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_braindead %s %s", hs_runtime_get_executing_thread_name(), ai_name, braindead ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
actor_braindead(iterator.actor_index, braindead);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_allow_charge(
|
|
long ai_reference,
|
|
boolean allow)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_allow_charge %s %s", hs_runtime_get_executing_thread_name(), ai_name, allow ? "true" : "false");
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
actor->external_orders.disable_charging = !allow;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_allow_dormant(
|
|
long ai_reference,
|
|
boolean allow)
|
|
{
|
|
struct ai_script_squad_iterator iterator;
|
|
struct squad_datum *squad;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_allow_dormant %s %s", hs_runtime_get_executing_thread_name(), ai_name, allow ? "true" : "false");
|
|
}
|
|
|
|
ai_index_squad_iterator_new(ai_reference, &iterator);
|
|
for (squad = ai_index_squad_iterator_next(&iterator);
|
|
squad;
|
|
squad = ai_index_squad_iterator_next(&iterator))
|
|
{
|
|
squad->disable_dormant = !allow;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_timer_start(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_timer_start %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_squad_iterator iterator;
|
|
struct squad_datum *squad;
|
|
|
|
ai_index_squad_iterator_new(ai_reference, &iterator);
|
|
for (squad = ai_index_squad_iterator_next(&iterator);
|
|
squad;
|
|
squad = ai_index_squad_iterator_next(&iterator))
|
|
{
|
|
squad->delay_timer_started = TRUE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_timer_expire(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_timer_expire %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_squad_iterator iterator;
|
|
struct squad_datum *squad;
|
|
|
|
ai_index_squad_iterator_new(ai_reference, &iterator);
|
|
for (squad = ai_index_squad_iterator_next(&iterator);
|
|
squad;
|
|
squad = ai_index_squad_iterator_next(&iterator))
|
|
{
|
|
encounter_squad_timer_expire(iterator.encounter_index, iterator.squad_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_follow_target_disable(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_follow_target_disable %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_get(ai_reference & UNSIGNED_SHORT_MAX)->follow_target_type = _follow_target_none;
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_automatic_migration_target(
|
|
long ai_reference,
|
|
boolean automatic)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_automatic_migration_target %s %s", hs_runtime_get_executing_thread_name(), ai_name, automatic ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_squad_iterator iterator;
|
|
struct squad_datum *squad;
|
|
|
|
ai_index_squad_iterator_new(ai_reference, &iterator);
|
|
for (squad = ai_index_squad_iterator_next(&iterator);
|
|
squad;
|
|
squad = ai_index_squad_iterator_next(&iterator))
|
|
{
|
|
squad->automatic_migration_target = automatic;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_follow_target_players(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_follow_target_players %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_get(ai_reference & UNSIGNED_SHORT_MAX)->follow_target_type = _follow_target_players;
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_follow_distance(
|
|
long ai_reference,
|
|
real distance)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_follow_distance %s %.1f", hs_runtime_get_executing_thread_name(), ai_name, distance);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_get(ai_reference & UNSIGNED_SHORT_MAX)->follow_target_distance = distance;
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_follow_target_unit(
|
|
long ai_reference,
|
|
long unit_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_follow_target_unit %s <some unit>", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(ai_reference & UNSIGNED_SHORT_MAX);
|
|
|
|
if (unit_index == NONE)
|
|
{
|
|
encounter->follow_target_type = _follow_target_none;
|
|
}
|
|
else
|
|
{
|
|
encounter->follow_target_type = _follow_target_unit;
|
|
encounter->follow_target_unit_index = unit_index;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_follow_target_ai(
|
|
long ai_reference,
|
|
long target_ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
char target_ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
/* BUG (preserved for exact matching): January formats ai_reference again instead of
|
|
* target_ai_reference, so the log prints the first name twice. A corrected build should
|
|
* pass target_ai_reference here.
|
|
*/
|
|
ai_index_to_string(ai_reference, global_scenario_get(), target_ai_name, sizeof(target_ai_name));
|
|
error(_error_silent, "%s: ai_follow_target_ai %s %s", hs_runtime_get_executing_thread_name(), ai_name, target_ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(ai_reference & UNSIGNED_SHORT_MAX);
|
|
|
|
if (target_ai_reference == NONE)
|
|
{
|
|
encounter->follow_target_type = _follow_target_none;
|
|
}
|
|
else
|
|
{
|
|
encounter->follow_target_type = _follow_target_ai;
|
|
encounter->follow_target_ai_index = target_ai_reference;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_force_active(
|
|
long ai_reference,
|
|
boolean force)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_force_active %s %s", hs_runtime_get_executing_thread_name(), ai_name, force ? "true" : "false");
|
|
}
|
|
|
|
if (ai_globals->ai_initialized_for_map && ai_reference != NONE)
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
struct scenario *scenario = global_scenario_get();
|
|
|
|
if (VALID_INDEX(encounter_index, scenario->ai_encounters.count))
|
|
encounter_get(encounter_index)->force_active = force;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_deselect(
|
|
void)
|
|
{
|
|
if (ai_globals->ai_initialized_for_map)
|
|
{
|
|
ai_debug_select_actor(NONE, NONE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_attach_units(
|
|
long object_list_index,
|
|
long ai_reference)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
ai_scripting_attach_unit(unit_index, ai_reference);
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_magically_see_units(
|
|
long ai_reference,
|
|
long object_list_index)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
ai_scripting_magically_see_unit(ai_reference, unit_index);
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_set_respawn(
|
|
long ai_reference,
|
|
boolean respawn)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_set_respawn %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
respawn ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_set_respawn(ai_reference & UNSIGNED_SHORT_MAX, respawn);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_set_deaf(
|
|
long ai_reference,
|
|
boolean deaf)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_set_deaf %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
deaf ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_set_deaf(ai_reference & UNSIGNED_SHORT_MAX, deaf);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_set_blind(
|
|
long ai_reference,
|
|
boolean blind)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_set_blind %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
blind ? "on" : "off");
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
encounter_set_blind(ai_reference & UNSIGNED_SHORT_MAX, blind);
|
|
|
|
return;
|
|
}
|
|
|
|
struct ai_script_vehicle_enterable *ai_scripting_find_vehicle_enterable(
|
|
long vehicle_index)
|
|
{
|
|
struct ai_script_vehicle_enterable_data *vehicle_enterable = NULL;
|
|
|
|
if (vehicle_index != NONE)
|
|
{
|
|
short vehicle_enterable_count = ai_globals->enterable_vehicle_count;
|
|
short vehicle_enterable_index;
|
|
|
|
for (vehicle_enterable_index = 0;
|
|
vehicle_enterable_index < vehicle_enterable_count;
|
|
vehicle_enterable_index++)
|
|
{
|
|
if (ai_globals->enterable_vehicles[vehicle_enterable_index].vehicle_index == vehicle_index)
|
|
break;
|
|
}
|
|
|
|
if (vehicle_enterable_index >= MAXIMUM_AI_ENTERABLE_VEHICLES)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"ai_vehicle_enterable: too many enterable vehicles (max is %d)",
|
|
MAXIMUM_AI_ENTERABLE_VEHICLES);
|
|
}
|
|
else
|
|
{
|
|
vehicle_enterable = &ai_globals->enterable_vehicles[vehicle_enterable_index];
|
|
if (vehicle_enterable_index >= vehicle_enterable_count)
|
|
{
|
|
csmemset(vehicle_enterable, 0, sizeof(*vehicle_enterable));
|
|
vehicle_enterable->vehicle_index = vehicle_index;
|
|
vehicle_enterable->radius = 8.0f;
|
|
ai_globals->enterable_vehicle_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (struct ai_script_vehicle_enterable *)vehicle_enterable;
|
|
}
|
|
|
|
void ai_scripting_vehicle_enterable_distance(
|
|
long unit_index,
|
|
real distance)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_vehicle_enterable_distance <some vehicle>",
|
|
hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct ai_script_vehicle_enterable *vehicle_enterable =
|
|
ai_scripting_find_vehicle_enterable(unit_index);
|
|
if (vehicle_enterable)
|
|
vehicle_enterable->radius = distance;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_vehicle_enterable_team(
|
|
long unit_index,
|
|
short team_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_vehicle_enterable_team <some vehicle> %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
team_index);
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct ai_script_vehicle_enterable *vehicle_enterable =
|
|
ai_scripting_find_vehicle_enterable(unit_index);
|
|
if (vehicle_enterable)
|
|
vehicle_enterable->team_bitmask |= 1 << team_index;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_vehicle_enterable_actor_type(
|
|
long unit_index,
|
|
short actor_type)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_vehicle_enterable_actor_type <some vehicle> %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
actor_type);
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct ai_script_vehicle_enterable *vehicle_enterable =
|
|
ai_scripting_find_vehicle_enterable(unit_index);
|
|
if (vehicle_enterable)
|
|
vehicle_enterable->actor_type_bitmask |= 1 << actor_type;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_vehicle_enterable_actors(
|
|
long unit_index,
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_vehicle_enterable_actors <some vehicle> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name);
|
|
}
|
|
|
|
if (unit_index != NONE && ai_reference != NONE)
|
|
{
|
|
struct ai_script_vehicle_enterable_data *vehicle_enterable =
|
|
(struct ai_script_vehicle_enterable_data *)ai_scripting_find_vehicle_enterable(unit_index);
|
|
|
|
if (vehicle_enterable)
|
|
{
|
|
if (vehicle_enterable->ai_indices_count < MAXIMUM_AI_INDICES_PER_ENTERABLE_VEHICLE)
|
|
{
|
|
vehicle_enterable->ai_indices[vehicle_enterable->ai_indices_count++] = ai_reference;
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"ai_vehicle_enterable_actors: too many groups of actors (max is %d)",
|
|
MAXIMUM_AI_INDICES_PER_ENTERABLE_VEHICLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_vehicle_enterable_disable(
|
|
long unit_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_vehicle_enterable_disable <some vehicle>",
|
|
hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
short vehicle_enterable_index;
|
|
|
|
for (vehicle_enterable_index = 0;
|
|
vehicle_enterable_index < ai_globals->enterable_vehicle_count;
|
|
vehicle_enterable_index++)
|
|
{
|
|
if (ai_globals->enterable_vehicles[vehicle_enterable_index].vehicle_index == unit_index)
|
|
{
|
|
ai_globals->enterable_vehicle_count--;
|
|
if (vehicle_enterable_index < ai_globals->enterable_vehicle_count)
|
|
{
|
|
ai_globals->enterable_vehicles[vehicle_enterable_index] =
|
|
ai_globals->enterable_vehicles[ai_globals->enterable_vehicle_count];
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_detach_unit(
|
|
long unit_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_detach_unit 0x%04X",
|
|
hs_runtime_get_executing_thread_name(),
|
|
unit_index & UNSIGNED_SHORT_MAX);
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
long actor_index = unit_get(unit_index)->unit.actor_index;
|
|
if (actor_index != NONE)
|
|
actor_delete(actor_index, FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_detach_units(
|
|
long object_list_index)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
ai_scripting_detach_unit(unit_index);
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_kill(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_kill %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name);
|
|
}
|
|
|
|
ai_scripting_kill_internal(ai_reference, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_kill_silent(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_kill_silent %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name);
|
|
}
|
|
|
|
ai_scripting_kill_internal(ai_reference, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
short ai_scripting_swarm_count(
|
|
long ai_reference)
|
|
{
|
|
return ai_scripting_count_internal(ai_reference, _ai_count_swarm, NULL, NULL);
|
|
}
|
|
|
|
short ai_scripting_nonswarm_count(
|
|
long ai_reference)
|
|
{
|
|
return ai_scripting_count_internal(ai_reference, _ai_count_nonswarm, NULL, NULL);
|
|
}
|
|
|
|
short ai_scripting_living_count(
|
|
long ai_reference)
|
|
{
|
|
return ai_scripting_count_internal(ai_reference, _ai_count_living, NULL, NULL);
|
|
}
|
|
|
|
real ai_scripting_living_fraction(
|
|
long ai_reference)
|
|
{
|
|
real result = 0.0f;
|
|
long original_count;
|
|
long count = ai_scripting_count_internal(
|
|
ai_reference,
|
|
_ai_count_living,
|
|
&original_count,
|
|
NULL);
|
|
|
|
if (original_count > 0)
|
|
result = (real)count / (real)original_count;
|
|
|
|
return result;
|
|
}
|
|
|
|
real ai_scripting_strength(
|
|
long ai_reference)
|
|
{
|
|
real strength = 0.0f;
|
|
ai_scripting_count_internal(ai_reference, _ai_count_living, NULL, &strength);
|
|
return strength;
|
|
}
|
|
|
|
boolean ai_scripting_is_attacking(
|
|
long ai_reference)
|
|
{
|
|
struct ai_script_platoon_iterator iterator;
|
|
struct platoon_datum *platoon;
|
|
boolean attacking = FALSE;
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
ai_index_platoon_iterator_new(ai_reference, &iterator);
|
|
|
|
platoon = ai_index_platoon_iterator_next(&iterator);
|
|
while (platoon)
|
|
{
|
|
if (!platoon->defending)
|
|
{
|
|
attacking = TRUE;
|
|
break;
|
|
}
|
|
|
|
platoon = ai_index_platoon_iterator_next(&iterator);
|
|
}
|
|
}
|
|
|
|
return attacking;
|
|
}
|
|
|
|
short ai_scripting_going_to_vehicle(
|
|
long unit_index)
|
|
{
|
|
struct actor_iterator iterator;
|
|
struct actor_datum *actor;
|
|
short count = 0;
|
|
|
|
actor_iterator_new(&iterator, TRUE);
|
|
actor = actor_iterator_next(&iterator);
|
|
while (actor)
|
|
{
|
|
if (actor->state.action == _actor_action_vehicle &&
|
|
actor->state.action_data.vehicle.vehicle_index == unit_index)
|
|
{
|
|
count++;
|
|
}
|
|
|
|
actor = actor_iterator_next(&iterator);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static short ai_scripting_migrate_find_target_squad(
|
|
long source_encounter_index,
|
|
short source_squad_index,
|
|
struct actor_definition *source_actor_definition,
|
|
struct actor_variant_definition *source_variant_definition,
|
|
boolean same_encounter,
|
|
long target_ai_reference,
|
|
char const *debug_description)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
struct encounter_definition *target_encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(target_ai_reference),
|
|
struct encounter_definition);
|
|
struct ai_script_squad_iterator iterator;
|
|
struct squad_datum *squad;
|
|
short first_squad_index = NONE;
|
|
short matching_type_index = NONE;
|
|
short matching_actor_index = NONE;
|
|
short matching_variant_index = NONE;
|
|
short matching_squad_index = NONE;
|
|
short found_squad_index = NONE;
|
|
|
|
ai_index_squad_iterator_new(target_ai_reference, &iterator);
|
|
for (squad = ai_index_squad_iterator_next(&iterator);
|
|
squad;
|
|
squad = ai_index_squad_iterator_next(&iterator))
|
|
{
|
|
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&target_encounter_definition->squads,
|
|
iterator.squad_index,
|
|
struct squad_definition);
|
|
struct actor_variant_definition *actor_variant_definition = NULL;
|
|
struct actor_definition *actor_definition = NULL;
|
|
short actor_palette_index = squad_definition->actor_palette_index;
|
|
|
|
if (VALID_INDEX(actor_palette_index, scenario->ai_actor_palette.count))
|
|
{
|
|
struct tag_reference *actor_palette_entry = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_actor_palette,
|
|
actor_palette_index,
|
|
struct tag_reference);
|
|
|
|
if (actor_palette_entry->index != NONE &&
|
|
tag_get_group_tag(actor_palette_entry->index) == ACTOR_VARIANT_DEFINITION_TAG)
|
|
{
|
|
actor_variant_definition = actor_variant_definition_get(actor_palette_entry->index);
|
|
if (actor_variant_definition->actor_reference.index != NONE)
|
|
{
|
|
actor_definition = actor_definition_get(
|
|
actor_variant_definition->actor_reference.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matching_squad_index == NONE &&
|
|
same_encounter &&
|
|
source_squad_index == iterator.squad_index)
|
|
{
|
|
matching_squad_index = iterator.squad_index;
|
|
}
|
|
if (matching_variant_index == NONE &&
|
|
source_variant_definition &&
|
|
actor_variant_definition &&
|
|
source_variant_definition == actor_variant_definition)
|
|
{
|
|
matching_variant_index = iterator.squad_index;
|
|
}
|
|
if (matching_actor_index == NONE &&
|
|
source_actor_definition &&
|
|
actor_definition &&
|
|
source_actor_definition == actor_definition)
|
|
{
|
|
matching_actor_index = iterator.squad_index;
|
|
}
|
|
if (matching_type_index == NONE &&
|
|
source_actor_definition &&
|
|
actor_definition &&
|
|
source_actor_definition->type == actor_definition->type)
|
|
{
|
|
matching_type_index = iterator.squad_index;
|
|
}
|
|
if (first_squad_index == NONE)
|
|
{
|
|
first_squad_index = iterator.squad_index;
|
|
}
|
|
}
|
|
|
|
if (matching_squad_index != NONE)
|
|
{
|
|
found_squad_index = matching_squad_index;
|
|
if (ai_debug.print_migration)
|
|
{
|
|
error(_error_silent, " %s unchanged", debug_description);
|
|
}
|
|
}
|
|
else if (matching_variant_index != NONE)
|
|
{
|
|
found_squad_index = matching_variant_index;
|
|
if (ai_debug.print_migration)
|
|
{
|
|
struct squad_definition *target_squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&target_encounter_definition->squads,
|
|
found_squad_index,
|
|
struct squad_definition);
|
|
char const *source_variant_name = "<error>";
|
|
|
|
if (source_encounter_index != NONE)
|
|
{
|
|
struct encounter_definition *source_encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(source_encounter_index),
|
|
struct encounter_definition);
|
|
|
|
if (source_squad_index != NONE)
|
|
{
|
|
struct squad_definition *source_squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&source_encounter_definition->squads,
|
|
source_squad_index,
|
|
struct squad_definition);
|
|
|
|
if (VALID_INDEX(
|
|
source_squad_definition->actor_palette_index,
|
|
scenario->ai_actor_palette.count))
|
|
{
|
|
struct tag_reference *actor_palette_entry = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_actor_palette,
|
|
source_squad_definition->actor_palette_index,
|
|
struct tag_reference);
|
|
|
|
if (actor_palette_entry->index != NONE)
|
|
{
|
|
source_variant_name = tag_get_name(actor_palette_entry->index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
error(
|
|
_error_silent,
|
|
" %s -> %s (same-variant %s)",
|
|
debug_description,
|
|
target_squad_definition->name,
|
|
source_variant_name);
|
|
}
|
|
}
|
|
else if (matching_actor_index != NONE)
|
|
{
|
|
found_squad_index = matching_actor_index;
|
|
if (ai_debug.print_migration)
|
|
{
|
|
struct squad_definition *target_squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&target_encounter_definition->squads,
|
|
found_squad_index,
|
|
struct squad_definition);
|
|
char const *actor_name = "<error>";
|
|
|
|
if (source_variant_definition)
|
|
{
|
|
long actor_definition_index = source_variant_definition->actor_reference.index;
|
|
|
|
actor_name = actor_definition_index == NONE ?
|
|
"<none>" :
|
|
tag_get_name(actor_definition_index);
|
|
}
|
|
error(
|
|
_error_silent,
|
|
" %s -> %s (same-actor %s)",
|
|
debug_description,
|
|
target_squad_definition->name,
|
|
actor_name);
|
|
}
|
|
}
|
|
else if (matching_type_index != NONE)
|
|
{
|
|
found_squad_index = matching_type_index;
|
|
if (ai_debug.print_migration)
|
|
{
|
|
struct squad_definition *target_squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&target_encounter_definition->squads,
|
|
found_squad_index,
|
|
struct squad_definition);
|
|
|
|
error(
|
|
_error_silent,
|
|
" %s -> %s (same-type %s)",
|
|
debug_description,
|
|
target_squad_definition->name,
|
|
actor_type_get_name(source_actor_definition->type));
|
|
}
|
|
}
|
|
else if (first_squad_index != NONE)
|
|
{
|
|
found_squad_index = first_squad_index;
|
|
if (ai_debug.print_migration)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
" %s -> %s (no matching types found)",
|
|
debug_description,
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&target_encounter_definition->squads,
|
|
found_squad_index,
|
|
struct squad_definition)->name);
|
|
}
|
|
}
|
|
else if (target_encounter_definition->squads.count > 0)
|
|
{
|
|
found_squad_index = 0;
|
|
if (ai_debug.print_migration)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
" %s -> %s (no matching squads found, going to squad 0)",
|
|
debug_description,
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&target_encounter_definition->squads,
|
|
found_squad_index,
|
|
struct squad_definition)->name);
|
|
}
|
|
}
|
|
|
|
if (found_squad_index != NONE)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1532,
|
|
(found_squad_index >= 0) && (found_squad_index < target_encounter_definition->squads.count));
|
|
}
|
|
|
|
return found_squad_index;
|
|
}
|
|
|
|
static void ai_scripting_migrate_internal(
|
|
long source_ai_reference,
|
|
long target_ai_reference,
|
|
boolean generate_stimulus,
|
|
boolean advancing_stimulus)
|
|
{
|
|
if (source_ai_reference != NONE && target_ai_reference != NONE)
|
|
{
|
|
long source_encounter_index = source_ai_reference & UNSIGNED_SHORT_MAX;
|
|
long target_encounter_index = target_ai_reference & UNSIGNED_SHORT_MAX;
|
|
|
|
if (source_encounter_index != NONE && target_encounter_index != NONE)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
struct encounter_datum *source_encounter = encounter_get(source_encounter_index);
|
|
struct encounter_datum *target_encounter = encounter_get(target_encounter_index);
|
|
struct encounter_definition *source_encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(source_encounter_index),
|
|
struct encounter_definition);
|
|
struct encounter_definition *target_encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(target_encounter_index),
|
|
struct encounter_definition);
|
|
boolean same_encounter = source_encounter_index == target_encounter_index;
|
|
short target_squad_indices[MAXIMUM_SQUADS_PER_ENCOUNTER];
|
|
|
|
csmemset(target_squad_indices, NONE, sizeof(target_squad_indices));
|
|
|
|
{
|
|
struct ai_script_squad_iterator source_iterator;
|
|
struct squad_datum *squad;
|
|
|
|
ai_index_squad_iterator_new(source_ai_reference, &source_iterator);
|
|
for (squad = ai_index_squad_iterator_next(&source_iterator);
|
|
squad;
|
|
squad = ai_index_squad_iterator_next(&source_iterator))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1571,
|
|
(source_iterator.squad_index >= 0) && (source_iterator.squad_index < MAXIMUM_SQUADS_PER_ENCOUNTER));
|
|
if (squad->current_count > 0 || source_encounter->is_prevehicle_encounter)
|
|
{
|
|
struct squad_definition *source_squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&source_encounter_definition->squads,
|
|
source_iterator.squad_index,
|
|
struct squad_definition);
|
|
struct actor_definition *source_actor_definition = NULL;
|
|
struct actor_variant_definition *source_variant_definition = NULL;
|
|
|
|
if (VALID_INDEX(
|
|
source_squad_definition->actor_palette_index,
|
|
scenario->ai_actor_palette.count))
|
|
{
|
|
struct tag_reference *actor_palette_entry = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_actor_palette,
|
|
source_squad_definition->actor_palette_index,
|
|
struct tag_reference);
|
|
|
|
if (actor_palette_entry->index != NONE &&
|
|
tag_get_group_tag(actor_palette_entry->index) == ACTOR_VARIANT_DEFINITION_TAG)
|
|
{
|
|
source_variant_definition = actor_variant_definition_get(
|
|
actor_palette_entry->index);
|
|
if (source_variant_definition->actor_reference.index != NONE)
|
|
{
|
|
source_actor_definition = actor_definition_get(
|
|
source_variant_definition->actor_reference.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
sprintf(temporary, "squad %s", source_squad_definition->name);
|
|
target_squad_indices[source_iterator.squad_index] = ai_scripting_migrate_find_target_squad(
|
|
source_encounter_index,
|
|
source_iterator.squad_index,
|
|
source_actor_definition,
|
|
source_variant_definition,
|
|
same_encounter,
|
|
target_ai_reference,
|
|
temporary);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
struct encounter_actor_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
encounter_actor_iterator_new(&iterator, source_encounter_index);
|
|
for (actor = encounter_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = encounter_actor_iterator_next(&iterator))
|
|
{
|
|
short current_squad_index = actor->meta.squad_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1617,
|
|
(current_squad_index >= 0) && (current_squad_index < MAXIMUM_SQUADS_PER_ENCOUNTER));
|
|
if (target_squad_indices[current_squad_index] != NONE &&
|
|
(!same_encounter || target_squad_indices[current_squad_index] != current_squad_index))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1629,
|
|
(target_squad_indices[current_squad_index] >= 0) && (target_squad_indices[current_squad_index] < target_encounter_definition->squads.count));
|
|
actor_change_encounter(
|
|
iterator.index,
|
|
target_encounter_index,
|
|
target_squad_indices[current_squad_index]);
|
|
if (generate_stimulus)
|
|
{
|
|
actor_stimulus_maneuvering(
|
|
iterator.index,
|
|
advancing_stimulus,
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (source_encounter->is_prevehicle_encounter)
|
|
{
|
|
struct actor_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
actor_iterator_new(&iterator, FALSE);
|
|
for (actor = actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = actor_iterator_next(&iterator))
|
|
{
|
|
if (DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.prevehicle_encounter_index) ==
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(source_encounter_index))
|
|
{
|
|
short current_squad_index = actor->meta.prevehicle_squad_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1655,
|
|
(current_squad_index >= 0) && (current_squad_index < MAXIMUM_SQUADS_PER_ENCOUNTER));
|
|
if (target_squad_indices[current_squad_index] != NONE &&
|
|
(!same_encounter || target_squad_indices[current_squad_index] != current_squad_index))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1667,
|
|
(target_squad_indices[current_squad_index] >= 0) && (target_squad_indices[current_squad_index] < target_encounter_definition->squads.count));
|
|
actor->meta.prevehicle_encounter_index = target_encounter_index;
|
|
actor->meta.prevehicle_squad_index = target_squad_indices[current_squad_index];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!same_encounter)
|
|
{
|
|
/* BUG (original): January tests the reference type of the already-masked encounter
|
|
* index (test edi,0xC0000000), so the source flag is also cleared for platoon and squad
|
|
* migrations (the later HCEA build clears it unconditionally). A corrected build would
|
|
* presumably test the type of source_ai_reference instead.
|
|
*/
|
|
if (((unsigned long)source_encounter_index >> 30) == _ai_reference_type_encounter)
|
|
{
|
|
source_encounter->is_prevehicle_encounter = FALSE;
|
|
}
|
|
target_encounter->is_prevehicle_encounter = TRUE;
|
|
}
|
|
}
|
|
|
|
{
|
|
struct encounter_actor_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
encounter_actor_iterator_new(&iterator, NONE);
|
|
for (actor = encounter_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = encounter_actor_iterator_next(&iterator))
|
|
{
|
|
if (DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.disconnected_encounter_index) ==
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(source_encounter_index))
|
|
{
|
|
short current_squad_index = actor->meta.disconnected_squad_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1700,
|
|
(current_squad_index >= 0) && (current_squad_index < MAXIMUM_SQUADS_PER_ENCOUNTER));
|
|
if (target_squad_indices[current_squad_index] != NONE &&
|
|
(!same_encounter || target_squad_indices[current_squad_index] != current_squad_index))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1712,
|
|
(target_squad_indices[current_squad_index] >= 0) && (target_squad_indices[current_squad_index] < target_encounter_definition->squads.count));
|
|
actor->meta.disconnected_encounter_index = target_encounter_index;
|
|
actor->meta.disconnected_squad_index = target_squad_indices[current_squad_index];
|
|
if (!same_encounter &&
|
|
target_encounter_definition->runtime_structure_bsp_reference_index ==
|
|
global_structure_bsp_index_get())
|
|
{
|
|
encounterless_detach_actor(iterator.index);
|
|
encounter_attach_actor(
|
|
iterator.index,
|
|
actor->meta.disconnected_encounter_index,
|
|
actor->meta.disconnected_squad_index,
|
|
TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (source_encounter->team_index != target_encounter->team_index)
|
|
{
|
|
ai_update_team_status();
|
|
}
|
|
encounters_update_dirty_status();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_migrate(
|
|
long source_ai_reference,
|
|
long target_ai_reference)
|
|
{
|
|
if (ai_debug.print_migration || ai_debug.print_scripting)
|
|
{
|
|
char source_name[512];
|
|
char target_name[512];
|
|
|
|
ai_index_to_string(
|
|
source_ai_reference,
|
|
global_scenario_get(),
|
|
source_name,
|
|
sizeof(source_name));
|
|
ai_index_to_string(
|
|
target_ai_reference,
|
|
global_scenario_get(),
|
|
target_name,
|
|
sizeof(target_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_migrate %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
source_name,
|
|
target_name);
|
|
}
|
|
|
|
ai_scripting_migrate_internal(
|
|
source_ai_reference,
|
|
target_ai_reference,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
static void ai_scripting_migrate_by_unit_internal(
|
|
long unit_index,
|
|
long target_ai_reference,
|
|
boolean generate_stimulus,
|
|
boolean advancing_stimulus)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long actor_index = unit->unit.actor_index != NONE ?
|
|
unit->unit.actor_index :
|
|
unit->unit.swarm_actor_index;
|
|
|
|
if (actor_index != NONE && target_ai_reference != NONE)
|
|
{
|
|
struct actor_datum *actor = actor_get(actor_index);
|
|
struct actor_definition *actor_definition = actor_definition_get(actor->meta.definition_index);
|
|
struct actor_variant_definition *actor_variant_definition =
|
|
actor_variant_definition_get(actor->meta.variant_definition_index);
|
|
long target_encounter_index = target_ai_reference & UNSIGNED_SHORT_MAX;
|
|
boolean same_encounter = DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.encounter_index) ==
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(target_encounter_index);
|
|
short target_squad_index = ai_scripting_migrate_find_target_squad(
|
|
actor->meta.encounter_index,
|
|
actor->meta.squad_index,
|
|
actor_definition,
|
|
actor_variant_definition,
|
|
same_encounter,
|
|
target_ai_reference,
|
|
"unit");
|
|
|
|
if (target_squad_index != NONE &&
|
|
(!same_encounter || target_squad_index != actor->meta.squad_index))
|
|
{
|
|
actor_change_encounter(actor_index, target_encounter_index, target_squad_index);
|
|
if (generate_stimulus)
|
|
{
|
|
actor_stimulus_maneuvering(actor_index, advancing_stimulus, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_migrate_by_unit(
|
|
long object_list_index,
|
|
long target_ai_reference)
|
|
{
|
|
if (ai_debug.print_migration || ai_debug.print_scripting)
|
|
{
|
|
char target_name[512];
|
|
|
|
ai_index_to_string(
|
|
target_ai_reference,
|
|
global_scenario_get(),
|
|
target_name,
|
|
sizeof(target_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_migrate_by_unit <some guys> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
target_name);
|
|
}
|
|
|
|
if (object_list_index != NONE && target_ai_reference != NONE)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit)
|
|
{
|
|
long child_object_index;
|
|
|
|
ai_scripting_migrate_by_unit_internal(
|
|
unit_index,
|
|
target_ai_reference,
|
|
FALSE,
|
|
FALSE);
|
|
child_object_index = unit->object.first_child_object_index;
|
|
while (child_object_index != NONE)
|
|
{
|
|
struct object_datum *child_object = object_get(child_object_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, child_object->object.type))
|
|
{
|
|
ai_scripting_migrate_by_unit_internal(
|
|
child_object_index,
|
|
target_ai_reference,
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
child_object_index = child_object->object.next_object_index;
|
|
}
|
|
}
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
ai_update_team_status();
|
|
encounters_update_dirty_status();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_migrate_and_speak(
|
|
long source_ai_reference,
|
|
long target_ai_reference,
|
|
char const *speech_type)
|
|
{
|
|
boolean advancing_stimulus;
|
|
|
|
if (ai_debug.print_migration || ai_debug.print_scripting)
|
|
{
|
|
char source_name[512];
|
|
char target_name[512];
|
|
|
|
ai_index_to_string(
|
|
source_ai_reference,
|
|
global_scenario_get(),
|
|
source_name,
|
|
sizeof(source_name));
|
|
ai_index_to_string(
|
|
target_ai_reference,
|
|
global_scenario_get(),
|
|
target_name,
|
|
sizeof(target_name));
|
|
/* BUG (preserved for exact matching): the format has four %s conversions
|
|
* but January supplies only three values, so the fourth conversion reads
|
|
* past the end of the argument list.
|
|
* Evidence: January's .rdata holds the 34-byte literal
|
|
* "%s: ai_migrate_and_speak %s %s %s", which has four conversions, while
|
|
* its call site cleans 0x14 bytes - five dwords, i.e. only three values -
|
|
* and loads speech_type only afterwards, at +0x7d. The same defect
|
|
* survives unrepaired in the 2020 build.
|
|
* Consequence: with January's prologue the fourth slot lands on the saved
|
|
* EDI home, so vsprintf dereferences the caller's entry EDI as a char *.
|
|
* The branch is gated on ai_debug.print_migration or
|
|
* ai_debug.print_scripting. Both default to false, but they are exposed as
|
|
* the HaloScript globals ai_print_migration and ai_print_scripting, so a
|
|
* scenario script or the debug console can reach this path at runtime.
|
|
* A corrected build should pass speech_type as the fourth value.
|
|
*/
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_migrate_and_speak %s %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
source_name,
|
|
target_name);
|
|
}
|
|
|
|
if (_stricmp(speech_type, "advance") == 0)
|
|
{
|
|
advancing_stimulus = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (_stricmp(speech_type, "retreat") != 0)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"ai_migrate_and_speak: unknown speech type '%s' (must be 'advance' or 'retreat')",
|
|
speech_type);
|
|
}
|
|
advancing_stimulus = FALSE;
|
|
}
|
|
|
|
ai_scripting_migrate_internal(
|
|
source_ai_reference,
|
|
target_ai_reference,
|
|
TRUE,
|
|
advancing_stimulus);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_allegiance_remove(
|
|
short team1_index,
|
|
short team2_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_allegiance_remove %d %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
team1_index,
|
|
team2_index);
|
|
}
|
|
|
|
if (team1_index != NONE && team2_index != NONE)
|
|
game_allegiance_remove(team1_index, team2_index);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean ai_scripting_allegiance_broken(
|
|
short team1_index,
|
|
short team2_index)
|
|
{
|
|
boolean broken = FALSE;
|
|
|
|
if (team1_index != NONE && team2_index != NONE)
|
|
{
|
|
broken =
|
|
game_team_is_ally(team1_index, team2_index) &&
|
|
game_team_is_enemy(team1_index, team2_index);
|
|
}
|
|
|
|
return broken;
|
|
}
|
|
|
|
boolean ai_scripting_conversation(
|
|
short conversation_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
struct scenario *scenario;
|
|
char const *conversation_name;
|
|
|
|
scenario = global_scenario_get();
|
|
conversation_name = "<error>";
|
|
if (VALID_INDEX(conversation_index, scenario->ai_conversations.count))
|
|
{
|
|
conversation_name = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_conversations,
|
|
conversation_index,
|
|
struct ai_script_conversation_definition)->name;
|
|
}
|
|
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_conversation %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
conversation_name);
|
|
}
|
|
|
|
return ai_conversation(conversation_index, TRUE);
|
|
}
|
|
|
|
void ai_scripting_conversation_stop(
|
|
short conversation_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
struct scenario *scenario;
|
|
char const *conversation_name;
|
|
|
|
scenario = global_scenario_get();
|
|
conversation_name = "<error>";
|
|
if (VALID_INDEX(conversation_index, scenario->ai_conversations.count))
|
|
{
|
|
conversation_name = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_conversations,
|
|
conversation_index,
|
|
struct ai_script_conversation_definition)->name;
|
|
}
|
|
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_conversation_stop %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
conversation_name);
|
|
}
|
|
|
|
ai_conversation_stop(conversation_index);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_conversation_advance(
|
|
short conversation_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
struct scenario *scenario;
|
|
char const *conversation_name;
|
|
|
|
scenario = global_scenario_get();
|
|
conversation_name = "<error>";
|
|
if (VALID_INDEX(conversation_index, scenario->ai_conversations.count))
|
|
{
|
|
conversation_name = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_conversations,
|
|
conversation_index,
|
|
struct ai_script_conversation_definition)->name;
|
|
}
|
|
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_conversation_advance %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
conversation_name);
|
|
}
|
|
|
|
ai_conversation_advance(conversation_index);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_stop_looking(
|
|
long unit_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_stop_looking <some unit>",
|
|
hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
long actor_index = unit_get(unit_index)->unit.actor_index;
|
|
if (actor_index != NONE)
|
|
actor_look_secondary_stop(actor_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_go_to_vehicle(
|
|
long ai_reference,
|
|
long unit_index,
|
|
char const *seat_substring_name)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_go_to_vehicle %s 0x%04X %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
unit_index & UNSIGNED_SHORT_MAX,
|
|
seat_substring_name);
|
|
}
|
|
|
|
ai_scripting_go_to_vehicle_internal(ai_reference, unit_index, seat_substring_name, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_go_to_vehicle_override(
|
|
long ai_reference,
|
|
long unit_index,
|
|
char const *seat_substring_name)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
ai_index_to_string(
|
|
ai_reference,
|
|
global_scenario_get(),
|
|
ai_name,
|
|
sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_go_to_vehicle_override %s 0x%04X %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
unit_index & UNSIGNED_SHORT_MAX,
|
|
seat_substring_name);
|
|
}
|
|
|
|
ai_scripting_go_to_vehicle_internal(ai_reference, unit_index, seat_substring_name, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_erase_all(
|
|
void)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(_error_silent, "%s: ai_erase_all", hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
ai_erase(NONE, NONE, NONE, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_select(
|
|
long ai_reference)
|
|
{
|
|
if (ai_globals->ai_initialized_for_map)
|
|
{
|
|
long encounter_index;
|
|
|
|
if (ai_reference == NONE)
|
|
encounter_index = NONE;
|
|
else
|
|
encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
|
|
ai_debug_select_encounter(encounter_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
short ai_scripting_status(
|
|
long ai_reference)
|
|
{
|
|
short status = 0;
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_status %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
while (ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
short actor_status = ai_scripting_assess_status(iterator.actor_index);
|
|
|
|
status = MAX(status, actor_status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void ai_scripting_spawn_actor(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_spawn_actor %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_globals->ai_initialized_for_map && ai_reference != NONE)
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
short squad_index = ((unsigned long)ai_reference >> 30) == _ai_reference_type_squad ?
|
|
((unsigned long)ai_reference >> 16) & UNSIGNED_CHAR_MAX : NONE;
|
|
|
|
if (squad_index == NONE &&
|
|
((unsigned long)ai_reference >> 30) == _ai_reference_type_platoon)
|
|
{
|
|
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 search_index;
|
|
|
|
for (search_index = 0;
|
|
search_index < encounter_definition->squads.count;
|
|
search_index++)
|
|
{
|
|
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
search_index,
|
|
struct squad_definition);
|
|
|
|
if (squad_definition->platoon_index == (((unsigned long)ai_reference >> 16) & UNSIGNED_CHAR_MAX))
|
|
{
|
|
squad_index = search_index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (squad_index != NONE)
|
|
encounter_spawn_actor(encounter_index, squad_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_vehicle_encounter(
|
|
long unit_index,
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_vehicle_encounter <some unit> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name);
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct unit_datum *vehicle = unit_get(unit_index);
|
|
short target_encounter_index = NONE;
|
|
short target_squad_index = NONE;
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
short encounter_index = ai_reference;
|
|
|
|
if (VALID_INDEX(encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
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 = 0;
|
|
|
|
switch ((unsigned long)ai_reference >> 30)
|
|
{
|
|
case _ai_reference_type_encounter:
|
|
break;
|
|
case _ai_reference_type_platoon:
|
|
{
|
|
short platoon_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
|
|
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);
|
|
|
|
if (squad_definition->platoon_index == platoon_index)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (squad_index >= encounter_definition->squads.count)
|
|
{
|
|
squad_index = 0;
|
|
}
|
|
break;
|
|
}
|
|
case _ai_reference_type_squad:
|
|
squad_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
break;
|
|
default:
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 3154, !"unreachable");
|
|
break;
|
|
}
|
|
|
|
if (VALID_INDEX(squad_index, encounter_definition->squads.count))
|
|
{
|
|
target_encounter_index = ai_reference;
|
|
target_squad_index = squad_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (target_encounter_index != NONE && target_squad_index != NONE)
|
|
{
|
|
short previous_encounter_index = vehicle->unit.fake_encounter_index;
|
|
|
|
if (previous_encounter_index != NONE)
|
|
{
|
|
struct encounter_actor_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
encounter_actor_iterator_new(&iterator, previous_encounter_index);
|
|
for (actor = encounter_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = encounter_actor_iterator_next(&iterator))
|
|
{
|
|
if (actor->input.vehicle_index == unit_index)
|
|
{
|
|
actor_change_encounter(
|
|
iterator.index,
|
|
target_encounter_index,
|
|
target_squad_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vehicle->unit.fake_encounter_index = target_encounter_index;
|
|
vehicle->unit.fake_squad_index = target_squad_index;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_attach_unit(
|
|
long unit_index,
|
|
long ai_reference)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, scenario, ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_attach_unit 0x%04X %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
unit_index & UNSIGNED_SHORT_MAX,
|
|
ai_name);
|
|
}
|
|
|
|
if (ai_globals->ai_initialized_for_map &&
|
|
unit_index != NONE &&
|
|
ai_reference != NONE)
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
|
|
if (VALID_INDEX(encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
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 squad_index = 0;
|
|
|
|
if (((unsigned long)ai_reference >> 30) == _ai_reference_type_squad)
|
|
{
|
|
squad_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
}
|
|
else if (((unsigned long)ai_reference >> 30) == _ai_reference_type_platoon)
|
|
{
|
|
long platoon_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
long search_index;
|
|
|
|
for (search_index = 0;
|
|
search_index < encounter_definition->squads.count;
|
|
search_index++)
|
|
{
|
|
if (TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
search_index,
|
|
struct squad_definition)->platoon_index == platoon_index)
|
|
{
|
|
squad_index = search_index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VALID_INDEX(squad_index, encounter_definition->squads.count))
|
|
{
|
|
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
squad_index,
|
|
struct squad_definition);
|
|
|
|
if (squad_definition->actor_palette_index != NONE)
|
|
{
|
|
struct tag_reference *actor_palette_entry = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_actor_palette,
|
|
squad_definition->actor_palette_index,
|
|
struct tag_reference);
|
|
|
|
if (actor_palette_entry->index != NONE)
|
|
{
|
|
long actor_definition_index = actor_variant_definition_get(
|
|
actor_palette_entry->index)->actor_reference.index;
|
|
|
|
if (actor_definition_index != NONE)
|
|
{
|
|
actor_create_for_unit(
|
|
TEST_FLAG(
|
|
actor_definition_get(actor_definition_index)->flags,
|
|
_actor_definition_swarm_actor_bit),
|
|
unit_index,
|
|
actor_palette_entry->index,
|
|
encounter_index,
|
|
squad_index,
|
|
FALSE,
|
|
NONE,
|
|
TEST_FLAG(
|
|
encounter_definition->flags,
|
|
_encounter_braindead_bit),
|
|
squad_definition->initial_state,
|
|
squad_definition->default_state,
|
|
NONE,
|
|
0);
|
|
encounters_update_dirty_status();
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
error(
|
|
_error_silent,
|
|
"ai_attach: no actor variant specified for %s/%s, cannot create actor to attach to biped",
|
|
encounter_definition->name,
|
|
squad_definition->name);
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"ai_attach: could not find a squad in encounter %s to attach actor",
|
|
encounter_definition->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_allegiance(
|
|
short team_index0,
|
|
short team_index1)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_allegiance %d %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
team_index0,
|
|
team_index1);
|
|
}
|
|
|
|
if (team_index0 != NONE && team_index1 != NONE)
|
|
{
|
|
short special_team_index = NONE;
|
|
short incident_decay_time = NONE;
|
|
short incident_threshold = NONE;
|
|
boolean requires_communication = FALSE;
|
|
boolean suspicious = FALSE;
|
|
|
|
if (team_index0 == _game_team_player)
|
|
special_team_index = team_index1;
|
|
else if (team_index1 == _game_team_player)
|
|
special_team_index = team_index0;
|
|
|
|
switch (special_team_index)
|
|
{
|
|
case _game_team_human:
|
|
case _game_team_sentinel:
|
|
{
|
|
short incident_decay_times[NUMBER_OF_GAME_DIFFICULTY_LEVELS] =
|
|
{300, 450, 1200, 2700};
|
|
|
|
suspicious = TRUE;
|
|
incident_threshold = 5;
|
|
incident_decay_time = incident_decay_times[game_difficulty_level_get()];
|
|
requires_communication = special_team_index == _game_team_human;
|
|
break;
|
|
}
|
|
}
|
|
|
|
game_allegiance_create(
|
|
team_index0,
|
|
suspicious && team_index0 == special_team_index,
|
|
team_index1,
|
|
suspicious && team_index1 == special_team_index,
|
|
incident_threshold,
|
|
incident_decay_time,
|
|
requires_communication);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_renew(
|
|
long ai_reference)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_renew %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
if (actor->meta.unit_index != NONE)
|
|
{
|
|
struct actor_variant_definition *actor_variant_definition =
|
|
actor_variant_definition_get(actor->meta.variant_definition_index);
|
|
struct unit_datum *unit = unit_get(actor->meta.unit_index);
|
|
|
|
unit->object.body_vitality = unit->object.maximum_body_vitality > 0.0f ? 1.0f : 0.0f;
|
|
unit->object.shield_vitality = unit->object.maximum_shield_vitality > 0.0f ? 1.0f : 0.0f;
|
|
|
|
if (actor_variant_definition->grenade_combat.grenade_type != NONE)
|
|
{
|
|
short desired_grenade_count = random_range(
|
|
actor_variant_definition->items.grenades_lower_bound,
|
|
actor_variant_definition->items.grenades_upper_bound + 1);
|
|
short grenade_count = unit_get_grenade_count(
|
|
actor->meta.unit_index,
|
|
unit_get_current_grenade_type(actor->meta.unit_index));
|
|
|
|
if (grenade_count < desired_grenade_count)
|
|
{
|
|
unit_add_grenade_type_to_inventory(
|
|
actor->meta.unit_index,
|
|
actor_variant_definition->grenade_combat.grenade_type,
|
|
desired_grenade_count - grenade_count);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_braindead_by_unit(
|
|
long object_list_index,
|
|
boolean braindead)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_braindead_by_unit <some guys> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
braindead ? "on" : "off");
|
|
}
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit)
|
|
{
|
|
long child_object_index;
|
|
|
|
if (unit->unit.actor_index != NONE)
|
|
{
|
|
actor_braindead(unit->unit.actor_index, braindead);
|
|
}
|
|
else if (unit->unit.swarm_actor_index != NONE)
|
|
{
|
|
actor_braindead(unit->unit.swarm_actor_index, braindead);
|
|
}
|
|
|
|
child_object_index = unit->object.first_child_object_index;
|
|
while (child_object_index != NONE)
|
|
{
|
|
struct object_datum *child_object = object_get(child_object_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, child_object->object.type))
|
|
{
|
|
struct unit_datum *child = (struct unit_datum *)child_object;
|
|
|
|
if (child->unit.actor_index != NONE)
|
|
{
|
|
actor_braindead(child->unit.actor_index, braindead);
|
|
}
|
|
else if (child->unit.swarm_actor_index != NONE)
|
|
{
|
|
actor_braindead(child->unit.swarm_actor_index, braindead);
|
|
}
|
|
}
|
|
|
|
child_object_index = child_object->object.next_object_index;
|
|
}
|
|
}
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_force_active_by_unit(
|
|
long unit_index,
|
|
boolean force)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_force_active_by_unit <some unit> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
force ? "true" : "false");
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
long actor_index = unit_get(unit_index)->unit.actor_index;
|
|
|
|
if (actor_index != NONE)
|
|
{
|
|
struct actor_datum *actor = actor_get(actor_index);
|
|
|
|
if (actor->meta.encounterless)
|
|
{
|
|
actor->meta.force_active = force;
|
|
}
|
|
else
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
actor->meta.encounter_index & UNSIGNED_SHORT_MAX,
|
|
struct encounter_definition);
|
|
|
|
error(
|
|
_error_silent,
|
|
"ai_force_active_by_unit: unit is a member of encounter %s/%s, you must use ai_force_active instead",
|
|
encounter_definition->name,
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
actor->meta.squad_index,
|
|
struct squad_definition)->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_attach_free(
|
|
long unit_index,
|
|
long actor_variant_definition_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_attach_free 0x%04X %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
unit_index & UNSIGNED_SHORT_MAX,
|
|
actor_variant_definition_index == NONE ?
|
|
"<error>" :
|
|
tag_name_strip_path(tag_get_name(actor_variant_definition_index)));
|
|
}
|
|
|
|
if (ai_globals->ai_initialized_for_map &&
|
|
unit_index != NONE &&
|
|
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)
|
|
{
|
|
if (TEST_FLAG(
|
|
actor_definition_get(actor_definition_index)->flags,
|
|
_actor_definition_swarm_actor_bit))
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_attach_free %s cannot be used for swarm actors",
|
|
hs_runtime_get_executing_thread_name(),
|
|
tag_name_strip_path(tag_get_name(actor_variant_definition_index)));
|
|
}
|
|
else
|
|
{
|
|
actor_create_for_unit(
|
|
FALSE,
|
|
unit_index,
|
|
actor_variant_definition_index,
|
|
NONE,
|
|
NONE,
|
|
FALSE,
|
|
NONE,
|
|
FALSE,
|
|
actor_default_state_alert,
|
|
actor_default_state_none,
|
|
NONE,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_try_to_fight(
|
|
long ai_reference,
|
|
long target_ai_reference)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
char target_ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
ai_index_to_string(target_ai_reference, global_scenario_get(), target_ai_name, sizeof(target_ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_try_to_fight %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
target_ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
actor->external_orders.desired_target_type = _desired_target_ai;
|
|
actor->external_orders.desired_target_ai_index = target_ai_reference;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_set_return_state(
|
|
long ai_reference,
|
|
short default_state)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_set_return_state %s %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
default_state);
|
|
}
|
|
|
|
if (VALID_INDEX(default_state, number_of_actor_default_states))
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
short action_class = actor_action_class(iterator.actor_index);
|
|
|
|
actor->state.default_state = default_state;
|
|
|
|
if (actor->state.combat_status == _actor_combat_status_none &&
|
|
(action_class == _action_class_noncombat ||
|
|
action_class == _action_class_passive ||
|
|
action_class == _action_class_transitory))
|
|
{
|
|
actor_action_set_default_state(iterator.actor_index, NONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_magically_see_unit(
|
|
long ai_reference,
|
|
long unit_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_magically_see_unit %s 0x%04X",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
unit_index & UNSIGNED_SHORT_MAX);
|
|
}
|
|
|
|
if (ai_reference != NONE && unit_index != NONE)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
long prop_index;
|
|
|
|
if (actor->meta.encounter_index != NONE)
|
|
encounter_force_activate(actor->meta.encounter_index);
|
|
|
|
prop_index = prop_get_base_by_unit_index(iterator.actor_index, unit_index, TRUE, FALSE);
|
|
if (prop_index != NONE)
|
|
actor_handle_unit_effect(iterator.actor_index, prop_index, _ai_unit_effect_magic_sight);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_magically_see_encounter(
|
|
long ai_reference,
|
|
long target_ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
char target_ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
ai_index_to_string(target_ai_reference, global_scenario_get(), target_ai_name, sizeof(target_ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_magically_see_encounter %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
target_ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE && target_ai_reference != NONE)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(target_ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
if (actor->meta.unit_index != NONE)
|
|
{
|
|
ai_scripting_magically_see_unit(ai_reference, actor->meta.unit_index);
|
|
}
|
|
else if (actor->meta.swarm_unit_index != NONE)
|
|
{
|
|
ai_scripting_magically_see_unit(ai_reference, actor->meta.swarm_unit_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_link_activation(
|
|
long ai_reference,
|
|
long link_ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
char link_ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
ai_index_to_string(link_ai_reference, global_scenario_get(), link_ai_name, sizeof(link_ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_link_activation %s %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
link_ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE && link_ai_reference != NONE)
|
|
{
|
|
if (!encounter_link_activation(ai_reference & UNSIGNED_SHORT_MAX, link_ai_reference))
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"ai_link_activation: cannot link to another encounter, MAXIMUM_ACTIVATION_LINK_INDICES_PER_ENCOUNTER is %d",
|
|
MAXIMUM_ACTIVATION_LINK_INDICES_PER_ENCOUNTER);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_free(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_free %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
2769,
|
|
actor->meta.encounter_index != NONE);
|
|
|
|
actor_flush_position_indices(iterator.actor_index);
|
|
encounter_detach_actor(iterator.actor_index, FALSE);
|
|
encounterless_attach_actor(iterator.actor_index);
|
|
}
|
|
|
|
encounters_update_dirty_status();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_free_units(
|
|
long object_list_index)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
short freed_count = 0;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_free_units <some units>",
|
|
hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit &&
|
|
unit->unit.actor_index != NONE &&
|
|
actor_get(unit->unit.actor_index)->meta.encounter_index != NONE)
|
|
{
|
|
actor_flush_position_indices(unit->unit.actor_index);
|
|
encounter_detach_actor(unit->unit.actor_index, FALSE);
|
|
encounterless_attach_actor(unit->unit.actor_index);
|
|
freed_count++;
|
|
}
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
if (freed_count > 0)
|
|
encounters_update_dirty_status();
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_exit_vehicle(
|
|
long ai_reference)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_exit_vehicle %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
if (actor->input.vehicle_index != NONE && actor->meta.unit_index != NONE)
|
|
unit_try_and_exit_seat(actor->meta.unit_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_magically_see_players(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_magically_see_players %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
for (player = data_iterator_next(&iterator);
|
|
player;
|
|
player = data_iterator_next(&iterator))
|
|
{
|
|
ai_scripting_magically_see_unit(ai_reference, player->unit_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_set_current_state(
|
|
long ai_reference,
|
|
short current_state)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[512];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_set_current_state %s %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
current_state);
|
|
}
|
|
|
|
if (VALID_INDEX(current_state, number_of_actor_default_states))
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
while (ai_index_actor_iterator_next(&iterator))
|
|
actor_action_set_default_state(iterator.actor_index, current_state);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_command_list(
|
|
long ai_reference,
|
|
short command_list_index)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_command_list %s %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name,
|
|
command_list_index);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
while (ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
struct action_state_data action_data;
|
|
|
|
if (action_obey_command_list_setup(
|
|
iterator.actor_index,
|
|
command_list_index,
|
|
&action_data.obey))
|
|
{
|
|
actor_action_change(iterator.actor_index, _actor_action_obey, &action_data);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_command_list_by_unit(
|
|
long unit_index,
|
|
short command_list_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_command_list_by_unit <unit> %d",
|
|
hs_runtime_get_executing_thread_name(),
|
|
command_list_index);
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit && unit->unit.actor_index != NONE)
|
|
{
|
|
struct actor_datum *actor = actor_get(unit->unit.actor_index);
|
|
struct action_state_data action_data;
|
|
|
|
if (action_obey_command_list_setup(
|
|
unit->unit.actor_index,
|
|
command_list_index,
|
|
&action_data.obey))
|
|
{
|
|
actor_action_change(unit->unit.actor_index, _actor_action_obey, &action_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_try_to_fight_nothing(
|
|
long ai_reference)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_try_to_fight_nothing %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
actor->external_orders.desired_target_type = _desired_target_none;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_try_to_fight_player(
|
|
long ai_reference)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_try_to_fight_player %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
actor->external_orders.desired_target_type = _desired_target_player;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_ignore(
|
|
long object_list_index,
|
|
boolean ignore)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_disregard <some guys> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ignore ? "on" : "off");
|
|
}
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit)
|
|
SET_FLAG(unit->unit.flags, _unit_ignored_by_actors_bit, ignore);
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_prefer_target(
|
|
long object_list_index,
|
|
boolean prefer)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_prefer_target <some guys> %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
prefer ? "on" : "off");
|
|
}
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit)
|
|
SET_FLAG(unit->unit.flags, _unit_preferred_target_bit, prefer);
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void ai_scripting_teleport_starting_location_private(
|
|
long ai_reference,
|
|
boolean only_if_unsupported)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
for (actor = ai_index_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = ai_index_actor_iterator_next(&iterator))
|
|
{
|
|
if (actor->meta.unit_index == NONE)
|
|
continue;
|
|
if (only_if_unsupported &&
|
|
(actor->input.vehicle_index != NONE ||
|
|
biped_approximate_surface_index(actor->meta.unit_index, NULL) != NONE))
|
|
{
|
|
continue;
|
|
}
|
|
if (actor->meta.encounter_index == NONE)
|
|
continue;
|
|
|
|
{
|
|
struct encounter_definition *encounter = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.encounter_index),
|
|
struct encounter_definition);
|
|
struct squad_definition *squad = TAG_BLOCK_GET_ELEMENT(
|
|
&encounter->squads,
|
|
actor->meta.squad_index,
|
|
struct squad_definition);
|
|
short starting_location_index = encounter_get_actor_starting_location(
|
|
actor->meta.encounter_index,
|
|
actor->meta.squad_index,
|
|
TRUE);
|
|
|
|
if (starting_location_index != NONE)
|
|
{
|
|
struct actor_starting_location const *starting_location = TAG_BLOCK_GET_ELEMENT(
|
|
&squad->starting_locations,
|
|
starting_location_index,
|
|
struct actor_starting_location);
|
|
real_vector3d forward;
|
|
|
|
vector3d_from_angle(&forward, starting_location->facing);
|
|
object_set_position(actor->meta.unit_index, &starting_location->position, &forward, NULL);
|
|
object_reset(actor->meta.unit_index);
|
|
actor_move_halt(iterator.actor_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_teleport_starting_location_if_unsupported(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_teleport_starting_location_if_unsupported %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name);
|
|
}
|
|
|
|
ai_scripting_teleport_starting_location_private(ai_reference, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_teleport_starting_location(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_teleport_starting_location %s",
|
|
hs_runtime_get_executing_thread_name(),
|
|
ai_name);
|
|
}
|
|
|
|
ai_scripting_teleport_starting_location_private(ai_reference, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_command_list_advance(
|
|
long ai_reference)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_command_list_advance %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
while (ai_index_actor_iterator_next(&iterator))
|
|
action_obey_advance_command_list(iterator.actor_index);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_erase(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_erase %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
long squad_index = ((unsigned long)ai_reference >> 30) == _ai_reference_type_squad ?
|
|
(ai_reference >> 16) & UNSIGNED_CHAR_MAX : NONE;
|
|
long platoon_index = ((unsigned long)ai_reference >> 30) == _ai_reference_type_platoon ?
|
|
(ai_reference >> 16) & UNSIGNED_CHAR_MAX : NONE;
|
|
|
|
ai_erase(ai_reference & UNSIGNED_SHORT_MAX, platoon_index, squad_index, FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_place(
|
|
long ai_reference)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
char ai_name[256];
|
|
|
|
ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name));
|
|
error(_error_silent, "%s: ai_place %s", hs_runtime_get_executing_thread_name(), ai_name);
|
|
}
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
long squad_index = ((unsigned long)ai_reference >> 30) == _ai_reference_type_squad ?
|
|
(ai_reference >> 16) & UNSIGNED_CHAR_MAX : NONE;
|
|
long platoon_index = ((unsigned long)ai_reference >> 30) == _ai_reference_type_platoon ?
|
|
(ai_reference >> 16) & UNSIGNED_CHAR_MAX : NONE;
|
|
|
|
encounter_create(ai_reference & UNSIGNED_SHORT_MAX, platoon_index, squad_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_look_at_object(
|
|
long unit_index,
|
|
long object_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_look_at_object <some unit> <some object>",
|
|
hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
if (unit_index != NONE && object_index != NONE)
|
|
{
|
|
long actor_index = unit_get(unit_index)->unit.actor_index;
|
|
|
|
if (actor_index != NONE)
|
|
{
|
|
struct direction_specification direction;
|
|
|
|
direction.type = _direction_specification_object;
|
|
direction.object_index = object_index;
|
|
|
|
actor_look_secondary(
|
|
actor_index,
|
|
_secondary_look_scripted,
|
|
_secondary_look_priority_default,
|
|
&direction);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_set_team(
|
|
long ai_reference,
|
|
short team_index)
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
struct encounter_actor_iterator iterator;
|
|
struct actor_datum *actor;
|
|
|
|
encounter_get(encounter_index)->team_index = team_index;
|
|
|
|
encounter_actor_iterator_new(&iterator, encounter_index);
|
|
for (actor = encounter_actor_iterator_next(&iterator);
|
|
actor;
|
|
actor = encounter_actor_iterator_next(&iterator))
|
|
{
|
|
actor_set_team(iterator.index, team_index);
|
|
}
|
|
|
|
ai_update_team_status();
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_scripting_command_list_advance_by_unit(
|
|
long unit_index)
|
|
{
|
|
if (ai_debug.print_scripting)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"%s: ai_command_list_advance_by_unit <some unit>",
|
|
hs_runtime_get_executing_thread_name());
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit)
|
|
{
|
|
if (unit->unit.actor_index != NONE)
|
|
{
|
|
action_obey_advance_command_list(unit->unit.actor_index);
|
|
}
|
|
else if (unit->unit.swarm_actor_index != NONE)
|
|
{
|
|
action_obey_advance_command_list(unit->unit.swarm_actor_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static short ai_scripting_command_list_status_internal(
|
|
long actor_index,
|
|
long unit_index,
|
|
short command_list_index,
|
|
struct obey_individual_simple_control *simple_control,
|
|
struct obey_individual_complex_control *complex_control)
|
|
{
|
|
struct ai_command_list_definition *command_list = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_command_lists,
|
|
command_list_index,
|
|
struct ai_command_list_definition);
|
|
struct ai_command_definition *command = NULL;
|
|
short status;
|
|
|
|
if (simple_control->current_command_index < command_list->commands.count)
|
|
{
|
|
command = TAG_BLOCK_GET_ELEMENT(
|
|
&command_list->commands,
|
|
simple_control->current_command_index,
|
|
struct ai_command_definition);
|
|
}
|
|
|
|
if (!command)
|
|
{
|
|
status = 1;
|
|
}
|
|
else if (TEST_FLAG(simple_control->metadata_flags, _obey_metadata_waiting_for_advance_notification_bit))
|
|
{
|
|
status = 2;
|
|
}
|
|
else
|
|
{
|
|
status = 3;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
short ai_scripting_command_list_status(
|
|
long object_list_index)
|
|
{
|
|
short result = 0;
|
|
long game_time = game_time_get();
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit)
|
|
{
|
|
struct actor_datum *actor = NULL;
|
|
short status = 0;
|
|
|
|
if (unit->unit.actor_index != NONE)
|
|
{
|
|
actor = actor_get(unit->unit.actor_index);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
2688,
|
|
!actor->meta.swarm);
|
|
if (actor->state.action == _actor_action_obey)
|
|
{
|
|
status = ai_scripting_command_list_status_internal(
|
|
unit->unit.actor_index,
|
|
unit_index,
|
|
actor->state.action_data.obey.command_list_index,
|
|
&actor->state.action_data.obey.simple_control,
|
|
&actor->state.action_data.obey.complex_control);
|
|
}
|
|
}
|
|
else if (unit->unit.swarm_actor_index != NONE)
|
|
{
|
|
actor = actor_get(unit->unit.swarm_actor_index);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
2701,
|
|
actor->meta.swarm);
|
|
if (actor->state.action == _actor_action_obey &&
|
|
actor->meta.swarm_cache_index != NONE)
|
|
{
|
|
struct swarm_datum *swarm = swarm_get(actor->meta.swarm_cache_index);
|
|
short swarm_unit_index = 0;
|
|
|
|
while (swarm_unit_index < swarm->unit_count &&
|
|
swarm->unit_indices[swarm_unit_index] != unit_index)
|
|
{
|
|
swarm_unit_index++;
|
|
}
|
|
|
|
if (swarm_unit_index < swarm->unit_count)
|
|
{
|
|
struct swarm_component_datum *component = swarm_component_get(
|
|
swarm->component_indices[swarm_unit_index]);
|
|
|
|
if (TEST_FLAG(component->flags, _swarm_component_obey_bit))
|
|
{
|
|
status = ai_scripting_command_list_status_internal(
|
|
unit->unit.swarm_actor_index,
|
|
unit_index,
|
|
actor->state.action_data.obey.command_list_index,
|
|
&component->obey,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!status &&
|
|
actor &&
|
|
actor->state.last_command_list_time != NONE &&
|
|
actor->state.last_command_list_time + 150 >= game_time)
|
|
{
|
|
status = 1;
|
|
}
|
|
result = MAX(result, status);
|
|
}
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
static int ai_scripting_vehicle_candidate_qsort(
|
|
void const *candidate0,
|
|
void const *candidate1)
|
|
{
|
|
struct ai_script_vehicle_candidate const *vehicle0 = candidate0;
|
|
struct ai_script_vehicle_candidate const *vehicle1 = candidate1;
|
|
|
|
if (vehicle0->already_going_to_vehicle != vehicle1->already_going_to_vehicle)
|
|
return vehicle0->already_going_to_vehicle ? 1 : -1;
|
|
|
|
if (vehicle0->distance_squared < vehicle1->distance_squared)
|
|
return -1;
|
|
|
|
if (vehicle0->distance_squared > vehicle1->distance_squared)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ai_scripting_go_to_vehicle_internal(
|
|
long ai_reference,
|
|
long unit_index,
|
|
char const *seat_substring_name,
|
|
boolean override_previous_vehicles)
|
|
{
|
|
struct unit_datum *vehicle;
|
|
real_point3d vehicle_origin;
|
|
short seat_indices[16];
|
|
struct ai_script_vehicle_candidate candidates[64];
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
struct actor_datum *actor;
|
|
short candidate_count;
|
|
short available_seat_count;
|
|
short candidate_index;
|
|
|
|
vehicle = unit_try_and_get(unit_index);
|
|
if (ai_reference == NONE || vehicle == NULL)
|
|
return;
|
|
|
|
candidate_count = 0;
|
|
object_get_origin(unit_index, &vehicle_origin);
|
|
available_seat_count = vehicle_scripting_find_available_seats(
|
|
unit_index,
|
|
seat_substring_name,
|
|
NONE,
|
|
seat_indices,
|
|
NUMBEROF(seat_indices));
|
|
if (available_seat_count <= 0)
|
|
return;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
actor = ai_index_actor_iterator_next(&iterator);
|
|
while (actor)
|
|
{
|
|
if ((word)candidate_count < NUMBEROF(candidates))
|
|
{
|
|
real dx;
|
|
real dy;
|
|
real dz;
|
|
|
|
candidates[candidate_count].actor_index = iterator.actor_index;
|
|
dx = vehicle_origin.x - actor->input.position.body_position.x;
|
|
dy = vehicle_origin.y - actor->input.position.body_position.y;
|
|
dz = vehicle_origin.z - actor->input.position.body_position.z;
|
|
|
|
candidates[candidate_count].distance_squared =
|
|
dy * dy + (dx * dx + dz * dz);
|
|
candidates[candidate_count].already_going_to_vehicle =
|
|
actor->state.action == _actor_action_vehicle;
|
|
candidate_count++;
|
|
}
|
|
|
|
actor = ai_index_actor_iterator_next(&iterator);
|
|
}
|
|
|
|
qsort(candidates, candidate_count, sizeof(candidates[0]), ai_scripting_vehicle_candidate_qsort);
|
|
|
|
for (candidate_index = 0; candidate_index < candidate_count; candidate_index++)
|
|
{
|
|
if (candidates[candidate_index].already_going_to_vehicle &&
|
|
!override_previous_vehicles)
|
|
{
|
|
break;
|
|
}
|
|
|
|
actor_action_try_to_enter_vehicle(
|
|
candidates[candidate_index].actor_index,
|
|
unit_index,
|
|
NULL,
|
|
NONE,
|
|
available_seat_count,
|
|
seat_indices);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static long ai_scripting_assess_status(
|
|
long actor_index)
|
|
{
|
|
struct actor_datum *actor = actor_get(actor_index);
|
|
|
|
if (!actor->meta.active)
|
|
return 0;
|
|
|
|
if (actor->state.mode < _actor_mode_combat)
|
|
return 1;
|
|
|
|
if (!actor->state.combat_status)
|
|
return 2;
|
|
|
|
if (actor->target.target_type < _actor_target_definite_orphan)
|
|
return 3;
|
|
|
|
if (actor->target.target_type < _actor_target_visible_enemy)
|
|
return 4;
|
|
|
|
if (actor->orders.combat.shoot_at_target || actor->orders.combat.throw_grenade)
|
|
return 6;
|
|
|
|
return 5;
|
|
}
|
|
|
|
static void ai_scripting_kill_internal(
|
|
long ai_reference,
|
|
boolean silent)
|
|
{
|
|
struct ai_script_actor_reference_iterator iterator;
|
|
|
|
if (ai_reference == NONE)
|
|
return;
|
|
|
|
ai_index_actor_iterator_new(ai_reference, &iterator);
|
|
while (ai_index_actor_iterator_next(&iterator))
|
|
actor_kill(iterator.actor_index, silent, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
static long ai_scripting_count_internal(
|
|
long ai_reference,
|
|
short count_type,
|
|
long *original_count_reference,
|
|
real *strength_reference)
|
|
{
|
|
long count = 0;
|
|
long original_count = 0;
|
|
real strength = 0.0f;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\ai_script.c",
|
|
1029,
|
|
(count_type >= 0) && (count_type < NUMBER_OF_AI_COUNT_TYPES));
|
|
|
|
if (ai_reference != NONE)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
|
|
switch ((unsigned long)ai_reference >> 30)
|
|
{
|
|
case 0:
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
if (VALID_INDEX(encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(encounter_index);
|
|
|
|
switch (count_type)
|
|
{
|
|
case _ai_count_living:
|
|
count = encounter->current_count;
|
|
break;
|
|
case _ai_count_swarm:
|
|
count = encounter->current_swarm_count;
|
|
break;
|
|
case _ai_count_nonswarm:
|
|
count = FLOOR(encounter->current_count - encounter->current_swarm_count, 0);
|
|
break;
|
|
default:
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 1060, !"unreachable");
|
|
break;
|
|
}
|
|
|
|
original_count = encounter->original_count;
|
|
strength = encounter->current_strength_fraction;
|
|
}
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
if (VALID_INDEX(encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(encounter_index);
|
|
short platoon_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
|
|
if (VALID_INDEX(platoon_index, encounter->platoon_count))
|
|
{
|
|
struct platoon_datum *platoon = encounter_get_platoon(encounter, platoon_index);
|
|
|
|
switch (count_type)
|
|
{
|
|
case _ai_count_living:
|
|
count = platoon->current_count;
|
|
break;
|
|
case _ai_count_swarm:
|
|
count = platoon->current_swarm_count;
|
|
break;
|
|
case _ai_count_nonswarm:
|
|
count = FLOOR(platoon->current_count - platoon->current_swarm_count, 0);
|
|
break;
|
|
default:
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 1096, !"unreachable");
|
|
break;
|
|
}
|
|
|
|
original_count = platoon->original_count;
|
|
strength = platoon->current_strength_fraction;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
long encounter_index = ai_reference & UNSIGNED_SHORT_MAX;
|
|
if (VALID_INDEX(encounter_index, scenario->ai_encounters.count))
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(encounter_index);
|
|
short squad_index = (ai_reference >> 16) & UNSIGNED_CHAR_MAX;
|
|
|
|
if (VALID_INDEX(squad_index, encounter->squad_count))
|
|
{
|
|
struct squad_datum *squad = encounter_get_squad(encounter, squad_index);
|
|
|
|
switch (count_type)
|
|
{
|
|
case _ai_count_living:
|
|
count = squad->current_count;
|
|
break;
|
|
case _ai_count_swarm:
|
|
count = squad->current_swarm_count;
|
|
break;
|
|
case _ai_count_nonswarm:
|
|
count = FLOOR(squad->current_count - squad->current_swarm_count, 0);
|
|
break;
|
|
default:
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 1133, !"unreachable");
|
|
break;
|
|
}
|
|
|
|
original_count = squad->original_count;
|
|
strength = squad->current_strength_fraction;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_script.c", 1143, !"unreachable");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (original_count_reference)
|
|
*original_count_reference = original_count;
|
|
if (strength_reference)
|
|
*strength_reference = strength;
|
|
|
|
return count;
|
|
}
|