Files
astromaddie--HaloCE-VR/source/ai/ai_communication.c
T
Jonas Volman 6bfd956737 Reconcile approved Halo fidelity packets and bounded data credit
Recover transparent geometry submit (+1,285 meaningful code, zero exact losses); reconcile COMMON ownership and Q10 85+EXT1 credit, actions/main data, and separately disclosed denominator corrections. Preserve canonical-only repairs and all held research. C09 is ordinary disclosed cleanup, not a claim of proven scaffolding intent. Clean rebuild, 1,663 tests, parks, audits, provider links and independent EXT1 check pass. Local reconciliation only.
2026-09-29 13:52:37 -07:00

7220 lines
193 KiB
C

/*
AI_COMMUNICATION.C
symbols in this file:
00031040 0150:
_ai_communication_initialize (0000)
00031190 0010:
_ai_communication_dispose (0000)
000311A0 0110:
_ai_communication_initialize_for_new_map (0000)
000312B0 0010:
_ai_communication_dispose_from_old_map (0000)
000312C0 0030:
_ai_communication_get_type_name (0000)
000312F0 0040:
_ai_communication_get_type_by_name (0000)
00031330 0060:
_ai_communication_packet_new (0000)
00031390 0070:
_reply_filter_close (0000)
00031400 0070:
_reply_filter_not_close (0000)
00031470 0050:
_reply_filter_searching (0000)
000314C0 0090:
_reply_filter_same_platoon (0000)
00031550 0020:
_reply_filter_fighting (0000)
00031570 0040:
_reply_filter_fighting_close (0000)
000315B0 00b0:
_reply_filter_same_target (0000)
00031660 0040:
_reply_filter_no_certain_target (0000)
000316A0 0040:
_reply_filter_flee_leader (0000)
000316E0 01a0:
_ai_communication_consider_speech (0000)
00031880 0040:
_actor_communication_team (0000)
000318C0 00b0:
_ai_communication_look_secondary_at_unit (0000)
00031970 0050:
_ai_communication_look_secondary_at_object (0000)
000319C0 0110:
_ai_conversation_status (0000)
00031AD0 0060:
_ai_conversation_line (0000)
00031B30 0090:
_ai_conversation_advance (0000)
00031BC0 0190:
_ai_conversation_finish (0000)
00031D50 0130:
_ai_conversation_new (0000)
00031E80 01b0:
_ai_conversation_line_begin (0000)
00032030 0290:
_ai_conversation_line_perform (0000)
000322C0 0030:
_ai_conversation_line_end (0000)
000322F0 00d0:
_actor_reset_idle_vocalization_timer (0000)
000323C0 00f0:
_actor_communication_update (0000)
000324B0 0080:
_ai_communication_handle_received_looking (0000)
00032530 02a0:
_ai_communication_update_speech_timers (0000)
000327D0 0340:
_ai_communication_get_player_rating (0000)
00032B10 0090:
_ai_conversation_stop (0000)
00032BA0 00d0:
_ai_conversation_actor_deleted (0000)
00032C70 0170:
_ai_conversation_unit_died (0000)
00032DE0 0800:
_ai_conversation_find_participant (0000)
000335E0 02c0:
_ai_communication_started (0000)
000338A0 0210:
_ai_communication_notify (0000)
00033AB0 0390:
_ai_communication_actor_talk_weight (0000)
00033E40 00c0:
_ai_communication_find_specific_actor_to_talk (0000)
00033F00 0120:
_ai_communication_find_global_actor_to_talk (0000)
00034020 06d0:
_ai_conversation_begin (0000)
000346F0 0450:
_ai_communication_find_actor_to_reply_to_player (0000)
00034B40 0630:
_ai_communication_finished (0000)
00035170 0150:
_ai_conversation (0000)
000352C0 0260:
_ai_conversation_update (0000)
00035520 1f80:
_ai_communication_event (0000)
00247808 18c0:
_communication_speech_priorities (0000)
_communication_notification_delays (0010)
_communication_protagonist_default_look_priorities (0030)
_communication_recipient_default_look_priorities (0040)
_communication_player_speaking_priorities (0050)
_communication_unit_prefer_silent_time (0060)
_communication_timer_tolerances (0068)
_communication_play_delays (01a8)
_communication_player_additional_delay (01bc)
_communication_overlap_time_modifier (01c0)
_communication_timeout_low_priority_modifier (01c4)
_communication_repeat_selection_time (01c8)
_communication_player_absolute_range (01cc)
_communication_player_ideal_range_min (01d0)
_communication_player_ideal_range_max (01d4)
_communication_player_ideal_fov (01d8)
_communication_player_rating_low_priority (01dc)
_global_dialogue_table (01e0)
_global_reply_table (1248)
002490C8 0015:
??_C@_0BF@PLOFNBGK@postcombat_celebrate?$AA@ (0000)
002490E0 0018:
??_C@_0BI@BNIOPALA@postcombat_shoot_corpse?$AA@ (0000)
002490F8 0018:
??_C@_0BI@LCABFNHO@postcombat_check_friend?$AA@ (0000)
00249110 0017:
??_C@_0BH@JEELJBED@postcombat_check_enemy?$AA@ (0000)
00249128 0013:
??_C@_0BD@MKOGOGNM@postcombat_triumph?$AA@ (0000)
0024913C 0014:
??_C@_0BE@OGGIDGFC@postcombat_massacre?$AA@ (0000)
00249150 0013:
??_C@_0BD@OKBEBJFP@postcombat_wounded?$AA@ (0000)
00249164 0015:
??_C@_0BF@OFGIJICH@postcombat_unscathed?$AA@ (0000)
0024917C 0011:
??_C@_0BB@LGDDKHGB@postcombat_alone?$AA@ (0000)
00249190 0005:
??_C@_04PMNENHO@leap?$AA@ (0000)
00249198 0008:
??_C@_07KMLHDIFF@falling?$AA@ (0000)
002491A0 0014:
??_C@_0BE@DIJKKGAP@uncover_exclamation?$AA@ (0000)
002491B4 0005:
??_C@_04FLLHOKOG@dive?$AA@ (0000)
002491BC 0006:
??_C@_05IMODGEHH@melee?$AA@ (0000)
002491C4 0009:
??_C@_08MPLMAFLL@surprise?$AA@ (0000)
002491D0 0010:
??_C@_0BA@HBKFDHMB@vehicle_falling?$AA@ (0000)
002491E0 000f:
??_C@_0P@EOJFLCO@vehicle_scared?$AA@ (0000)
002491F0 000f:
??_C@_0P@OJJDOKMB@vehicle_woohoo?$AA@ (0000)
00249200 000d:
??_C@_0N@KIHOLEBN@vehicle_exit?$AA@ (0000)
00249210 000e:
??_C@_0O@NEANEBAA@vehicle_entry?$AA@ (0000)
00249220 0010:
??_C@_0BA@JKBMANAN@hiding_finished?$AA@ (0000)
00249230 000f:
??_C@_0P@BOOLPEN@attempted_flee?$AA@ (0000)
00249240 000a:
??_C@_09LPKHDOHP@flee_idle?$AA@ (0000)
0024924C 0011:
??_C@_0BB@GDNLPHOF@flee_leader_died?$AA@ (0000)
00249260 0011:
??_C@_0BB@GKOPGPMO@shooting_traitor?$AA@ (0000)
00249274 000f:
??_C@_0P@IDHEPNJE@shooting_group?$AA@ (0000)
00249284 0011:
??_C@_0BB@LKALKDPF@shooting_berserk?$AA@ (0000)
00249298 0011:
??_C@_0BB@GHLHMONC@shooting_vehicle?$AA@ (0000)
002492AC 0009:
??_C@_08HFADMLAA@shooting?$AA@ (0000)
002492B8 0016:
??_C@_0BG@IIEIONGD@sighted_friend_player?$AA@ (0000)
002492D0 0008:
??_C@_07LADJDDJF@retreat?$AA@ (0000)
002492D8 0008:
??_C@_07JKODCMP@advance?$AA@ (0000)
002492E0 000e:
??_C@_0O@OAGOMGLF@uncover_start?$AA@ (0000)
002492F0 0015:
??_C@_0BF@HNJCJGJJ@search_group_abandon?$AA@ (0000)
00249308 000f:
??_C@_0P@GDPJMJHJ@search_abandon?$AA@ (0000)
00249318 000e:
??_C@_0O@EKDNJHKG@search_report?$AA@ (0000)
00249328 000d:
??_C@_0N@MDODPJCI@search_query?$AA@ (0000)
00249338 000d:
??_C@_0N@IJIHJIIJ@search_start?$AA@ (0000)
00249348 0010:
??_C@_0BA@KFNIENAA@alert_noncombat?$AA@ (0000)
00249358 0008:
??_C@_07MAJPMGLJ@blocked?$AA@ (0000)
00249360 000d:
??_C@_0N@JEODFMIK@lost_contact?$AA@ (0000)
00249370 000f:
??_C@_0P@JELOHPAB@grenade_danger?$AA@ (0000)
00249380 0010:
??_C@_0BA@GHAABFAJ@grenade_sighted?$AA@ (0000)
00249390 0010:
??_C@_0BA@ILMKIOPH@grenade_startle?$AA@ (0000)
002493A0 0011:
??_C@_0BB@NDMOOLOJ@grenade_throwing?$AA@ (0000)
002493B4 0013:
??_C@_0BD@JHAEBNEM@allegiance_changed?$AA@ (0000)
002493C8 0012:
??_C@_0BC@LJDIENED@found_dead_friend?$AA@ (0000)
002493DC 0011:
??_C@_0BB@HACOIKAB@unexpected_enemy?$AA@ (0000)
002493F0 000c:
??_C@_0M@EJKFHLEL@found_enemy?$AA@ (0000)
002493FC 000e:
??_C@_0O@CDKKCOME@sighted_enemy?$AA@ (0000)
0024940C 0005:
??_C@_04NAFIGEAM@hurt?$AA@ (0000)
00249414 000e:
??_C@_0O@EGHFODNC@killing_spree?$AA@ (0000)
00249424 0006:
??_C@_05PBMMFKGI@death?$AA@ (0000)
0024942C 0004:
??_C@_03MADNOHBP@COV?$AA@ (0000)
00249430 0009:
??_C@_08ONAJMEN@covenant?$AA@ (0000)
0024943C 0004:
??_C@_03KPLLINCC@HUM?$AA@ (0000)
00249440 0006:
??_C@_05OAIPHEDE@human?$AA@ (0000)
00249448 0008:
??_C@_07BLDMICJO@exclaim?$AA@ (0000)
00249450 0005:
??_C@_04EJGCJLIC@yell?$AA@ (0000)
00249458 000c:
??_C@_0M@JHEHDIOO@communicate?$AA@ (0000)
00249464 0005:
??_C@_04HBNBFOKD@talk?$AA@ (0000)
0024946C 0008:
??_C@_07EFCDKMI@chatter?$AA@ (0000)
00249474 0007:
??_C@_06NLCJMHIE@filler?$AA@ (0000)
0024947C 0012:
??_C@_0BC@ONDNPAEP@conversation_data?$AA@ (0000)
00249490 0010:
??_C@_0BA@HJGPPPBN@ai?5conversation?$AA@ (0000)
002494A0 0048:
??_C@_0EI@HLDOMHAD@ai_communication_initialize?3?5una@ (0000)
002494E8 0019:
??_C@_0BJ@DEJOBHIO@ai?5communication?5replies?$AA@ (0000)
00249508 004b:
??_C@_0EL@IJCKEONL@ai_communication_initialize?3?5una@ (0000)
00249554 0025:
??_C@_0CF@ENENAALK@c?3?2halo?2SOURCE?2ai?2ai_communicati@ (0000)
0024957C 001a:
??_C@_0BK@HKKGFHIF@ai?5communication?5dialogue?$AA@ (0000)
00249598 000c:
??_C@_0M@FJGFLLPA@information?$AA@ (0000)
002495A4 003a:
??_C@_0DK@JAPPOFEL@?$CIplay_type?5?$DN?$DN?5_unit_play_speech_@ (0000)
002495E0 000f:
??_C@_0P@LFGNPAFB@spk?$CFd?$DMtol?$CFd?$CL?$CFd?$AA@ (0000)
002495F0 000a:
??_C@_09NBNIDDMC@nospch?9?$CFs?$AA@ (0000)
00249600 0040:
??_C@_0EA@HGHJJMBM@vocalization_type?5?$CG?$CG?5sound_defin@ (0000)
00249640 0021:
??_C@_0CB@JPNJDDBL@?$CFs?3?5told?5to?5advance?5by?5scripting@ (0000)
00249664 0012:
??_C@_0BC@NMDPGBEB@?$CFs?3?5finished?5?$CFs?$CFs?$AA@ (0000)
00249678 000c:
??_C@_0M@PDJPHGJL@prematurely?$AA@ (0000)
00249684 000d:
??_C@_0N@HAEOLDAP@successfully?$AA@ (0000)
00249694 0013:
??_C@_0BD@FOMCINHP@?5?$CIunable?5to?5begin?$CJ?$AA@ (0000)
002496A8 0048:
??_C@_0EI@KMONEJAM@?$CFs?3?5this?5conversation?5is?5already@ (0000)
002496F0 00b5:
??_C@_0LF@IOBNLMGM@?$CIconversation?9?$DOdialogue_indices?$FL@ (0000)
002497A8 000d:
??_C@_0N@IDIKJELG@?$CFs?3?5speak?5?$CFs?$AA@ (0000)
002497B8 004b:
??_C@_0EL@EAKMIEAH@?$CIreply_table_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIrep@ (0000)
00249808 0052:
??_C@_0FC@DCLNMHJF@?$CIdialogue_type_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CId@ (0000)
0024985C 0013:
??_C@_0BD@NKCKMGKJ@?$CFs?5?$CFs?5?$CFd?1?$CFs?3?5?$CFs?5?$CFd?$AA@ (0000)
00249870 0004:
__real@3eb33333 (0000)
00249874 001e:
??_C@_0BO@PGLKMLMB@?$CFs?3?5told?5to?5stop?5by?5scripting?$AA@ (0000)
00249894 0018:
??_C@_0BI@MOODPKBH@?$CFs?3?5unit?5died?0?5aborting?$AA@ (0000)
002498AC 0008:
??_C@_07CEJJIKE@?$CFs?$CI?$CFd?$CJ?5?$AA@ (0000)
002498B4 0012:
??_C@_0BC@LGHGCJKF@no?9dialogue?9match?$AA@ (0000)
002498C8 0010:
??_C@_0BA@LGNJEIF@not?9near?9player?$AA@ (0000)
002498D8 000a:
??_C@_09DFNMPCAJ@selection?$AA@ (0000)
002498E4 0014:
??_C@_0BE@FCFFGOIN@nowhere?9near?9player?$AA@ (0000)
002498F8 0013:
??_C@_0BD@FMPJDKHJ@already?9conversing?$AA@ (0000)
0024990C 000b:
??_C@_0L@FEFLJKBC@wrong?9type?$AA@ (0000)
00249918 000c:
??_C@_0M@CFFMKJFH@?5?5reasons?3?5?$AA@ (0000)
00249924 0031:
??_C@_0DB@PGEHKHFC@?$CFs?3?5didn?8t?5find?5?$CFd?1?$CFs?5in?5?$CFs?5?$CI?$CFd?5@ (0000)
00249958 000b:
??_C@_0L@COMKDGN@?$DMeveryone?$DO?$AA@ (0000)
00249964 0010:
??_C@_0BA@ELKHLFDD@?$DMspecific?5unit?$DO?$AA@ (0000)
00249978 006d:
??_C@_0GN@FHAECEPL@?$CIactor_variant_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIa@ (0000)
002499E8 001c:
??_C@_0BM@CBPILECH@found_variant_index?5?$CB?$DN?5NONE?$AA@ (0000)
00249A08 0056:
??_C@_0FG@DGHHBBJL@change_variant_indices_count?5?$DM?5M@ (0000)
00249A60 0004:
__real@3eaaaaab (0000)
00249A64 0004:
__real@41a20000 (0000)
00249A68 003a:
??_C@_0DK@CMCFHJEF@nearby_unit_count?5?$DM?5MAXIMUM_PART@ (0000)
00249AA4 0028:
??_C@_0CI@ICHMPFMM@conversation_definition?5?$CG?$CG?5conve@ (0000)
00249ACC 0007:
??_C@_06DJHOIPC@?$CFs?3?5?$CFs?$AA@ (0000)
00249AD4 0009:
??_C@_08BAHEMPNM@?5?$FL?$CFd?1?$CFs?$FN?$AA@ (0000)
00249AE0 0006:
??_C@_05DNIIFBMG@?$CFs?5?$CFs?$AA@ (0000)
00249AE8 0008:
??_C@_07GKOCGALF@non?9voc?$AA@ (0000)
00249AF0 000c:
??_C@_0M@DMDFMBAG@unit?5?$CF04X?3?5?$AA@ (0000)
00249AFC 000c:
??_C@_0M@HNMGEIHE@player?5?$CFd?3?5?$AA@ (0000)
00249B08 0005:
??_C@_04PFAOODAA@?$CFs?3?5?$AA@ (0000)
00249B10 0008:
??_C@_07MMOCNKBD@?$CFs?1?$CFs?3?5?$AA@ (0000)
00249B18 0072:
??_C@_0HC@PEHCJGNI@?$CIai_information?9?$DOdialogue_type_i@ (0000)
00249B90 0091:
??_C@_0JB@OPDEEEOP@?$CIconversation?9?$DOdialogue_indices?$FL@ (0000)
00249C24 0030:
??_C@_0DA@LIHNLMLM@?$CFs?3?5cannot?5start?0?5players?5are?5no@ (0000)
00249C54 0028:
??_C@_0CI@LELOFLBG@?$CFs?3?5cannot?5start?0?5nobody?5can?5see@ (0000)
00249C80 0047:
??_C@_0EH@ODDEEICK@?$CFs?3?5participants?5currently?5outsi@ (0000)
00249CC8 0025:
??_C@_0CF@BJPALGNB@?$CFs?3?5could?5not?5find?5participant?5?$CF@ (0000)
00249CF0 0036:
??_C@_0DG@BNDGFHIE@?$CFs?3?5found?5participant?5?$CFd?1?$CFs?5but?5@ (0000)
00249D28 0016:
??_C@_0BG@KFPPJIJM@?$FL?$CFs?50?9player?9chance?$FN?5?$AA@ (0000)
00249D40 0014:
??_C@_0BE@DLAFFNAP@?$FL?$CFs?5rand?$CF?42f?$DO?$CF?42f?$FN?5?$AA@ (0000)
00249D54 0012:
??_C@_0BC@GGBDH@?$FL?$CFs?5found?9actor?$FN?5?$AA@ (0000)
00249D68 0012:
??_C@_0BC@GOGGHGJI@?$FL?$CFs?5?$CFs?9d?9dis?1?$CFd?$FN?5?$AA@ (0000)
00249D7C 000d:
??_C@_0N@EFEDAFNN@?$FL?$CFs?5nobody?$FN?5?$AA@ (0000)
00249D8C 0015:
??_C@_0BF@DPPMFKIF@?$FLscripted?9override?$FN?5?$AA@ (0000)
00249DA4 0011:
??_C@_0BB@HDBKEACA@PLAYER?9REPLY?5?$CFs?5?$AA@ (0000)
00249DB8 0007:
??_C@_06IKJGFCFO@?$DO?$DO?$CFs?$DM?$DM?$AA@ (0000)
00249DC0 0008:
??_C@_07GPMPHPH@nobody?5?$AA@ (0000)
00249DC8 000c:
??_C@_0M@EMMAIOJB@playercant?5?$AA@ (0000)
00249DD4 0009:
??_C@_08LMAMDFAK@u?9?$CFs?9?$CFs?5?$AA@ (0000)
00249DE0 0004:
??_C@_03EKLFPLOI@n?1a?$AA@ (0000)
00249DE4 0004:
??_C@_03CJOLJKCN@dis?$AA@ (0000)
00249DE8 0008:
??_C@_07CPBKJGBP@filter?5?$AA@ (0000)
00249DF0 000a:
??_C@_09PGKELFMI@0?9chance?5?$AA@ (0000)
00249DFC 000d:
??_C@_0N@KOBKEGMJ@rand?9failed?5?$AA@ (0000)
00249E0C 0010:
??_C@_0BA@MDFGCECJ@rand?5?$CF?42f?$DO?$CF?42f?5?$AA@ (0000)
00249E1C 0011:
??_C@_0BB@BILJOOCN@?$CBreply_to_player?$AA@ (0000)
00249E30 0013:
??_C@_0BD@PDIGFKID@override?9scripted?5?$AA@ (0000)
00249E44 000b:
??_C@_0L@HPKHMDON@wrong?9dmg?5?$AA@ (0000)
00249E50 0004:
??_C@_03DKEJHHJP@?$CFs?3?$AA@ (0000)
00249E54 000b:
??_C@_0L@LNGLDCHG@REPLY?5?$CFs?3?5?$AA@ (0000)
00249E60 0047:
??_C@_0EH@NNNDPBEK@?$CFs?3?5could?5not?5start?0?5and?5not?5set@ (0000)
00249EA8 0039:
??_C@_0DJ@ONAPPANJ@?$CFs?3?5can?8t?5begin?5yet?5but?5will?5rem@ (0000)
00249EE4 0017:
??_C@_0BH@MJIAPKFJ@?$CFs?3?5begun?5successfully?$AA@ (0000)
00249F00 0058:
??_C@_0FI@CHGNAOEL@WARNING?3?5too?5many?5executing?5conv@ (0000)
00249F58 0027:
??_C@_0CH@FMECNBHL@?$CFs?3?5script?5tried?5to?5start?5conver@ (0000)
00249F80 001a:
??_C@_0BK@ELJOEOCB@?$CFs?3?5no?5more?5lines?5to?5play?$AA@ (0000)
00249F9C 0030:
??_C@_0DA@PPIFADMI@?$CFs?3?5unable?5to?5begin?0?5and?5no?5poin@ (0000)
00249FCC 0014:
??_C@_0BE@KPFKNBML@?$CFs?3?5trying?5to?5begin?$AA@ (0000)
00249FE0 003c:
??_C@_0DM@GFNDBPMM@selected_possibility?9?$DOpreselecte@ (0000)
0024A01C 001e:
??_C@_0BO@LJOGJGHI@?$FLrnd?$CF?41f?5tot?$CF?41f?5cum?$CF?41f?$EA?$CFd?$FN?5?$AA@ (0000)
0024A03C 0019:
??_C@_0BJ@DMOBOMPI@?$FL?$CFd?1?$CF?41f?5force?5?$CFd?1?$CF?41f?$FN?5?$AA@ (0000)
0024A058 0020:
??_C@_0CA@DKHGIOGG@total_possibility_weight?5?$DO?50?40f?$AA@ (0000)
0024A078 0059:
??_C@_0FJ@IBKDMIEO@ai_communication_event?3?5type?5?$CFd?5@ (0000)
0024A0D8 0043:
??_C@_0ED@GJAAIMME@?$FL?$CFs?1?$CFd?5?$CFs?5del?$CFd?5w?3?$CF?41f?$CFs?5s?$CF?41f?5p@ (0000)
0024A11C 0002:
??_C@_01BIAFAFID@F?$AA@ (0000)
0024A120 0007:
??_C@_06BEJGECAN@PLAYER?$AA@ (0000)
0024A128 0012:
??_C@_0BC@FOAHPEHM@protagonist_actor?$AA@ (0000)
0024A13C 0011:
??_C@_0BB@BOBCLBAE@?$FL?$CFs?1?$CFd?5u?9?$CFs?9?$CFs?$FN?5?$AA@ (0000)
0024A150 0030:
??_C@_0DA@CPDLIKLO@subject_actor?9?$DOdanger_zone?4objec@ (0000)
0024A180 0015:
??_C@_0BF@JKGAIKPI@?$FL?$CFs?1?$CFd?5?$CFs?9d?9dis?1?$CFd?$FN?5?$AA@ (0000)
0024A198 000f:
??_C@_0P@BLNKGMGL@?$FL?$CFs?1?$CFd?5?$CFs?5?$CFs?$FN?5?$AA@ (0000)
0024A1A8 0010:
??_C@_0BA@JGHKCAMO@shout?3?$CF?41f?$DM?$CF?41f?$AA@ (0000)
0024A1B8 000f:
??_C@_0P@NMKONCAD@talk?3?$CF?41f?$DM?$CF?41f?$AA@ (0000)
0024A1C8 000f:
??_C@_0P@JEDLKPOF@chat?3?$CF?41f?$DM?$CF?41f?$AA@ (0000)
0024A1D8 0039:
??_C@_0DJ@KAPPHHBA@communication_priority?5?$DM?5_ai_com@ (0000)
0024A214 000c:
??_C@_0M@EGBKHEDG@?$CFs?9?$CFc?$CFc?$CFc?$CFs?$AA@ (0000)
0024A220 0004:
??_C@_03HOBIBNHG@?9hi?$AA@ (0000)
0024A224 0004:
??_C@_03CPELBCCM@?9lo?$AA@ (0000)
0024A228 007f:
??_C@_0HP@PLIGCELE@?$CIcommunication_priority?5?$DO?5_ai_co@ (0000)
0024A2A8 0055:
??_C@_0FF@NKMCJBLB@?$CIcommunication_team?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIco@ (0000)
0024A300 0009:
??_C@_08ECOGDNI@unteamed?$AA@ (0000)
0024A30C 0013:
??_C@_0BD@NMMLILLH@?$FL?$CFs?1?$CFd?50?9playrat?$FN?5?$AA@ (0000)
0024A320 0012:
??_C@_0BC@OMHOAJOH@?$FL?$CFs?1?$CFd?5nogrp?9?$CFc?$FN?5?$AA@ (0000)
0024A334 0014:
??_C@_0BE@OMABELMA@?$FL?$CFs?1?$CFd?5noplyreply?$FN?5?$AA@ (0000)
0024A348 0013:
??_C@_0BD@GACNBALB@?$FL?$CFs?1?$CFd?5nounit?9?$CFc?$FN?5?$AA@ (0000)
0024A35C 0006:
??_C@_05LLNBKOOD@?$DMerr?$DO?$AA@ (0000)
0024A364 000f:
??_C@_0P@OMGCNPFA@?$FL?$CFs?1?$CFd?5nodmg?$FN?5?$AA@ (0000)
0024A374 0014:
??_C@_0BE@PGBJJNLI@?$FL?$CFs?1?$CFd?5nocausrace?$FN?5?$AA@ (0000)
0024A388 0013:
??_C@_0BD@PHFOKNBE@?$FL?$CFs?1?$CFd?5nosubrace?$FN?5?$AA@ (0000)
0024A39C 0013:
??_C@_0BD@NGEMBLCE@?$FL?$CFs?1?$CFd?5status?9?$CFs?$FN?5?$AA@ (0000)
0024A3B0 0004:
??_C@_03NDPLAFDJ@vis?$AA@ (0000)
0024A3B4 0009:
??_C@_08OLCLKDFA@nodanger?$AA@ (0000)
0024A3C0 0007:
??_C@_06EBKKMJGD@notvis?$AA@ (0000)
0024A3C8 0005:
??_C@_04BLLOBPAA@lost?$AA@ (0000)
0024A3D0 0005:
??_C@_04BBBDNBOK@dead?$AA@ (0000)
0024A3D8 0011:
??_C@_0BB@GKMIFEBC@?$FL?$CFs?1?$CFd?5host?9?$CFs?$FN?5?$AA@ (0000)
0024A3EC 0008:
??_C@_07CEGFBHJP@traitor?$AA@ (0000)
0024A3F4 0006:
??_C@_05ICAMJEGE@enemy?$AA@ (0000)
0024A3FC 0007:
??_C@_06BHBNKPBA@friend?$AA@ (0000)
0024A404 0005:
??_C@_04PJDNGLKJ@self?$AA@ (0000)
0024A410 0048:
??_C@_0EI@GINMJEIA@?$CIdialogue_index?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIdialog@ (0000)
0024A458 0009:
??_C@_08GLNGGDPJ@DISABLED?$AA@ (0000)
0024A464 0007:
??_C@_06DAIKMLH@?$CFs?9?$CFc?5?$AA@ (0000)
0024A46C 002b:
??_C@_0CL@IOMHDOAM@allegiance?5?$CFs?0?5?$CFd?5incidents?5?$CIthr@ (0000)
0024A498 000c:
??_C@_0M@IDKAPGFO@still?5holds?$AA@ (0000)
0024A4A4 0029:
??_C@_0CJ@GKFDOEJ@incident?5between?5teams?5?$CFs?5and?5?$CFs@ (0000)
0024A4D0 0009:
??_C@_08PAHPFNGA@accident?$AA@ (0000)
0024A4DC 0009:
??_C@_08JPJNIAAJ@betrayal?$AA@ (0000)
0024A4E8 000b:
??_C@_0L@MCEFNCAK@unobserved?$AA@ (0000)
0024A4F4 0009:
??_C@_08PJALNDK@observed?$AA@ (0000)
0024A500 0055:
??_C@_0FF@FCIFJAOH@?$CIcommunication_type?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIco@ (0000)
002B7308 0114:
_global_communication_priority_names (0000)
_global_communication_team_names (0020)
_global_communication_type_names (0030)
00319CA8 0010:
_global_dialogue_event_count (0000)
_global_dialogue_events (0004)
_global_reply_event_count (0008)
_global_reply_events (000c)
*/
/* ---------- headers */
#include "cseries.h"
#include "ai_communication.h"
#include "ai.h"
#include "ai_debug.h"
#include "ai_profile.h"
#include "ai_script.h"
#include "actions.h"
#include "actor_definitions.h"
#include "actors.h"
#include "actor_types.h"
#include "ai/ai_globals.h"
#include "ai_scenario_definitions.h"
#include "cseries/errors.h"
#include "encounters.h"
#include "game/game.h"
#include "game/game_allegiance.h"
#include "game/players.h"
#include "main/console.h"
#include "memory/data.h"
#include "physics/collision_usage.h"
#include "physics/collisions.h"
#include "props.h"
#include "saved games/game_state.h"
#include "scenario/scenario.h"
#include "scenario/scenario_definitions.h"
#include "sound/game_sound.h"
#include "tag_files/tag_files.h"
#include "units/dialogue_definitions.h"
#include "units/units.h"
/* ---------- constants */
enum
{
NUMBER_OF_COMMUNICATION_PRIORITIES = 8,
NUMBER_OF_AI_COMMUNICATION_PRIORITIES = NUMBER_OF_COMMUNICATION_PRIORITIES,
MAXIMUM_COMMUNICATION_POSSIBILITIES = 16,
NUMBER_OF_COMMUNICATION_TEAMS = 4,
NUMBER_OF_COMMUNICATION_TYPES = 57,
NUMBER_OF_COMMUNICATION_TIMER_TYPES = 5,
NUMBER_OF_DIALOGUE_USAGES = 105,
NUMBER_OF_REPLY_USAGES = 46,
MAXIMUM_PARTICIPANTS_PER_CONVERSATION = 8,
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT = 6,
MAXIMUM_RECENT_CONVERSATIONS = 16,
_ai_conversation_stop_if_anyone_dies_bit = 0,
_ai_conversation_stop_if_damaged_bit = 1,
_ai_conversation_stop_if_visible_enemy_bit = 2,
_ai_conversation_stop_if_alerted_to_enemy_bit = 3,
_ai_conversation_player_must_be_visible_bit = 4,
_ai_conversation_stop_other_actions_bit = 5,
_ai_conversation_keep_trying_to_play_bit = 6,
_ai_conversation_player_must_be_looking_at_bit = 7,
_ai_conversation_participant_optional_bit = 0,
_ai_conversation_participant_has_alternate_bit = 1,
_ai_conversation_participant_is_alternate_bit = 2,
_ai_conversation_line_addressee_look_back_bit = 0,
_ai_conversation_line_everyone_look_at_speaker_bit = 1,
_ai_conversation_line_everyone_look_at_addressee_bit = 2,
_ai_conversation_line_wait_after_until_told_to_advance_bit = 3,
_ai_conversation_line_wait_until_speaker_nearby_bit = 4,
_ai_conversation_line_wait_until_everyone_nearby_bit = 5,
_ai_conversation_address_player = 1,
_ai_conversation_address_participant = 2,
_ai_conversation_selection_friendly_actor = 0,
_ai_conversation_selection_disembodied = 1,
_ai_conversation_selection_in_player_vehicle = 2,
_ai_conversation_selection_not_in_vehicle = 3,
_ai_conversation_selection_sargeant = 4,
_ai_conversation_selection_any_actor = 5,
_ai_conversation_selection_radio = 6,
_ai_conversation_selection_radio_sargeant = 7,
_find_actor_mode_same_team = 0,
_find_actor_mode_friend = 1,
_find_actor_mode_enemy = 2,
_find_actor_allow_lookup_bit = 0,
_find_actor_near_to_players_bit = 1,
_find_actor_same_vehicle_bit = 2,
_find_actor_allow_subject_bit = 3,
_find_actor_allow_cause_bit = 4,
_actor_mode_braindead = 0,
_actor_mode_asleep = 1,
_actor_mode_alert = 2,
_actor_mode_combat = 3,
_actor_combat_status_definite = 3,
_actor_combat_status_certain = 4,
_actor_combat_status_dangerous = 6,
_ai_information_none = 0,
_ai_information_look_unit = 1,
_ai_information_look_object = 2,
_ai_information_allegiance = 1,
_dialogue_usage_lookup_bit = 0,
_dialogue_usage_force_bit = 1,
_dialogue_usage_immediate_notify_bit = 2,
_dialogue_usage_player_bit = 3,
_dialogue_usage_same_vehicle_bit = 4,
_dialogue_usage_allow_subject_bit = 5,
_dialogue_usage_override_scripted_bit = 6,
_comm_look_direction_subject = 1,
_comm_look_direction_protagonist = 2,
_comm_look_direction_target = 3,
_comm_look_direction_danger = 4,
_comm_protagonist_subject = 0,
_comm_protagonist_cause = 1,
_comm_protagonist_friend = 2,
_comm_protagonist_target = 3,
_comm_protagonist_enemy = 4,
_ai_communication_priority_none = 0,
_ai_communication_priority_communicate = 4,
_ai_communication_priority_shout = 5,
_ai_communication_priority_yell = 6,
_ai_communication_priority_exclaim = 7,
_ai_sound_volume_medium = 1,
_ai_sound_volume_loud = 2,
_ai_sound_volume_shout = 3,
_ai_communication_team_human = 0,
_ai_communication_team_covenant = 1,
NUMBER_OF_AI_COMMUNICATION_TEAMS = 2,
_unit_play_speech_none = 0,
_unit_play_speech_queue = 1,
};
#define COMMUNICATION_CLOSE_DISTANCE 5.0f
/* ---------- macros */
#define ai_print_conversations (ai_debug.print_conversations)
#define ai_conversation_driver_get(index) \
((struct conversation_datum *)datum_get(conversation_data, (index)))
/* ---------- structures */
struct ai_information_packet;
struct dialogue_usage
{
short communication_type;
short communication_priority;
short vocalization_type;
short animation_type;
short protagonist_type;
short protagonist_look_priority;
short recipient_look_direction;
short recipient_look_priority;
real weight;
real repeat_delay;
short flags;
short required_group;
short required_hostility;
short required_enemy_status;
short required_subject_race;
short required_cause_race;
short required_damage;
};
struct reply_usage
{
short original_vocalization_type;
short original_damage_category;
short protagonist_type;
short vocalization_type;
short animation_type;
short communication_priority;
word flags;
real chance;
real player_chance;
real delay_time;
real repeat_delay;
boolean (*reply_filter)(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
};
typedef char dialogue_usage_size_assert[
sizeof(struct dialogue_usage) == 0x28 ? 1 : -1];
typedef char reply_usage_size_assert[
sizeof(struct reply_usage) == 0x24 ? 1 : -1];
typedef char ai_conversation_line_view_current_line_offset_assert[
offsetof(struct conversation_datum, line_index) == 0x48 ? 1 : -1];
struct scenario_conversation_participant_view
{
word pad00;
word flags;
short selection_type;
short actor_type;
short preexisting_object_name_index;
short new_attach_object_name_index;
byte __unknown0C[0x0C];
short dialogue_variants[MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT];
char ai_index_name[32];
long runtime_ai_index;
byte __unknown48[0x0C];
};
struct scenario_conversation_line_view
{
word flags;
short participant_index;
short address_type;
short address_participant_index;
long unknown08;
real delay_time;
byte __unknown10[0x0C];
struct tag_reference dialogue[MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT];
};
struct dialogue_event_status
{
long last_time_spoken;
long disable_until_time;
};
/* Function-local in the original; its construction code establishes this
* 0x38-byte layout. January's assertion strings name the selected record
* `selected_possibility` and its reply field `preselected_reply_actor_index`. */
struct ai_communication_possibility
{
real weight;
boolean interrupts;
boolean is_reply;
short vocalization_type;
short priority;
short animation_type;
short play_type;
short delay_time;
short ai_delay_time;
word pad12;
long protagonist_unit_index;
long protagonist_actor_index;
long recipient_unit_index;
long preselected_reply_actor_index;
short protagonist_look_priority;
short recipient_look_priority;
short look_type;
word pad2A;
long look_unit_index;
long sound_definition_index;
short dialogue_type_index;
word pad36;
};
typedef char ai_communication_possibility_size_assert[
sizeof(struct ai_communication_possibility) == 0x38 ? 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;
};
typedef char ai_conversation_datum_view_size_assert[
sizeof(struct conversation_datum) == 0x64 ? 1 : -1];
typedef char ai_conversation_datum_view_line_index_offset_assert[
offsetof(struct conversation_datum, line_index) == 0x48 ? 1 : -1];
typedef char ai_conversation_datum_view_line_advance_offset_assert[
offsetof(struct conversation_datum, line_advance) == 0x63 ? 1 : -1];
typedef char ai_conversation_datum_header_any_line_spoken_offset_assert[
offsetof(struct conversation_datum, any_line_spoken) == 0x5 ? 1 : -1];
typedef char ai_conversation_datum_header_begun_offset_assert[
offsetof(struct conversation_datum, begun) == 0x6 ? 1 : -1];
typedef char ai_conversation_datum_header_waiting_to_advance_offset_assert[
offsetof(struct conversation_datum, waiting_to_advance) == 0x8 ? 1 : -1];
typedef char ai_conversation_datum_header_told_to_advance_offset_assert[
offsetof(struct conversation_datum, told_to_advance) == 0x9 ? 1 : -1];
typedef char scenario_conversation_definition_view_size_assert[
sizeof(struct ai_conversation) == 0x74 ? 1 : -1];
typedef char scenario_conversation_definition_participants_offset_assert[
offsetof(struct ai_conversation, participants) == 0x50 ? 1 : -1];
typedef char scenario_conversation_definition_lines_offset_assert[
offsetof(struct ai_conversation, lines) == 0x5C ? 1 : -1];
typedef char scenario_conversation_participant_view_size_assert[
sizeof(struct scenario_conversation_participant_view) == 0x54 ? 1 : -1];
typedef char scenario_conversation_line_view_size_assert[
sizeof(struct scenario_conversation_line_view) == 0x7C ? 1 : -1];
typedef char ai_conversation_driver_datum_view_size_assert[
sizeof(struct conversation_datum) == 0x64 ? 1 : -1];
typedef char ai_conversation_driver_participant_bitmask_offset_assert[
offsetof(struct conversation_datum, participant_bitmask) == 0x14 ? 1 : -1];
typedef char ai_conversation_driver_actor_indices_offset_assert[
offsetof(struct conversation_datum, actor_indices) == 0x28 ? 1 : -1];
typedef char ai_conversation_driver_line_flags_offset_assert[
offsetof(struct conversation_datum, line_flags) == 0x4E ? 1 : -1];
typedef char ai_conversation_driver_line_unit_index_offset_assert[
offsetof(struct conversation_datum, line_unit_index) == 0x54 ? 1 : -1];
typedef char ai_conversation_driver_line_address_unit_index_offset_assert[
offsetof(struct conversation_datum, line_address_unit_index) == 0x58 ? 1 : -1];
typedef char recent_conversation_view_size_assert[
sizeof(struct recent_conversation) == 0x10 ? 1 : -1];
typedef char ai_print_conversations_offset_assert[
offsetof(struct ai_debug_state, print_conversations) == 0x9F ? 1 : -1];
typedef char ai_communication_unit_speech_item_size_assert[
sizeof(struct unit_speech_item) == 0x30 ? 1 : -1];
typedef char ai_communication_unit_speech_item_ai_offset_assert[
offsetof(struct unit_speech_item, ai) == 0x10 ? 1 : -1];
typedef char ai_communication_actor_mode_offset_assert[
offsetof(struct actor_datum, state.mode) == 0x6A ? 1 : -1];
typedef char ai_communication_actor_unit_index_offset_assert[
offsetof(struct actor_datum, meta.unit_index) == 0x18 ? 1 : -1];
typedef char ai_communication_actor_idle_combat_offset_assert[
offsetof(struct actor_datum, control.idle_vocalization_combat) == 0x6CC ? 1 : -1];
typedef char ai_communication_actor_idle_timer_offset_assert[
offsetof(struct actor_datum, control.idle_vocalization_timer) == 0x6CE ? 1 : -1];
typedef char ai_communication_actor_iterator_size_assert[
sizeof(struct actor_iterator) == 0x1C ? 1 : -1];
typedef char ai_communication_actor_iterator_index_offset_assert[
offsetof(struct actor_iterator, index) == 0x14 ? 1 : -1];
/* ---------- prototypes */
static boolean reply_filter_close(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_not_close(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_searching(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_same_platoon(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_fighting(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_fighting_close(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_same_target(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_no_certain_target(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static boolean reply_filter_flee_leader(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index);
static short ai_communication_consider_speech(
long unit_index,
short communication_priority,
short speech_priority,
short delay_ticks,
boolean allow_vocalization_lookup,
boolean allow_recent_disabling,
short *vocalization_type,
real *weight,
long *sound_definition_index_reference,
char *debug_string);
static real ai_communication_actor_talk_weight(
long actor_index,
long subject_unit_index,
real_point3d const *subject_point,
long cause_unit_index,
real_point3d const *cause_point,
real stimulus_range,
short ai_communication_type,
short ai_communication_priority,
short unit_speech_priority,
short vocalization_type,
short animation_type,
short flags);
static long ai_communication_find_specific_actor_to_talk(
long ai_index,
long subject_unit_index,
long cause_unit_index,
real maximum_distance,
short communication_type,
short communication_priority,
short speech_priority,
short vocalization_type,
short animation_type,
short flags);
static long ai_communication_find_global_actor_to_talk(
short team_index,
short find_actor_mode,
long subject_unit_index,
long cause_unit_index,
real maximum_distance,
short communication_type,
short communication_priority,
short speech_priority,
short vocalization_type,
short animation_type,
short flags);
static void ai_communication_look_secondary_at_unit(
long actor_index,
short type,
short priority,
long look_unit_index,
long prop_index);
static void ai_communication_look_secondary_at_object(
long actor_index,
short type,
short priority,
long object_index);
static boolean ai_conversation_line_begin(
long conversation_index);
static boolean ai_conversation_line_perform(
long conversation_index);
static void ai_conversation_line_end(
long conversation_index);
static void actor_reset_idle_vocalization_timer(
long actor_index);
static void ai_communication_handle_received_looking(
long actor_index,
long prop_index,
struct ai_information_packet *information);
static void ai_communication_update_speech_timers(
long unit_index,
short priority,
short vocalization_type,
short dialogue_type_index,
short reply_table_index);
boolean sound_scripted_dialog_is_playing(
void);
static boolean ai_conversation_find_participant(
long conversation_index,
short participant_index,
boolean *found_specific_unit_reference,
boolean *try_alternate_reference,
boolean *success_with_better_player_rating_reference,
real *best_distance_reference);
static boolean ai_conversation_begin(
long conversation_index,
boolean *continue_trying);
static long ai_communication_find_actor_to_reply_to_player(
long unit_index,
long target_unit_index,
short vocalization_type,
short damage_category,
real *reply_rating_reference);
static long ai_conversation_new(
short scenario_conversation_index,
boolean scripted);
/* ---------- globals */
short const communication_speech_priorities[NUMBER_OF_COMMUNICATION_PRIORITIES] =
{
0, 3, 3, 3, 4, 5, 5, 8,
};
real const communication_notification_delays[NUMBER_OF_COMMUNICATION_PRIORITIES] =
{
0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.3f,
};
short const communication_protagonist_default_look_priorities
[NUMBER_OF_COMMUNICATION_PRIORITIES] =
{
0, 4, 4, 5, 6, 6, 6, 4,
};
short const communication_recipient_default_look_priorities
[NUMBER_OF_COMMUNICATION_PRIORITIES] =
{
0, 3, 3, 4, 5, 5, 6, 4,
};
short const communication_player_speaking_priorities
[NUMBER_OF_COMMUNICATION_PRIORITIES] =
{
0, 2, 3, 4, 4, 6, 6, 7,
};
short const communication_unit_prefer_silent_time = 60;
real const communication_timer_tolerances
[NUMBER_OF_COMMUNICATION_PRIORITIES][2][NUMBER_OF_COMMUNICATION_TIMER_TYPES] =
{
{
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
},
{
{ 4.0f, 4.5f, 2.5f, 2.0f, 0.0f },
{ 5.0f, 5.0f, 3.0f, 2.5f, 0.0f },
},
{
{ 3.0f, 3.5f, 2.0f, 1.5f, 0.0f },
{ 4.5f, 4.5f, 2.5f, 2.0f, 0.0f },
},
{
{ 0.3f, 1.0f, 1.0f, 1.0f, 0.8f },
{ 1.0f, 2.0f, 1.5f, 1.5f, 0.3f },
},
{
{ 0.0f, 0.5f, 0.5f, 0.5f, 1.3f },
{ 0.2f, 1.0f, 1.0f, 1.0f, 0.8f },
},
{
{ 0.0f, 0.0f, 1.0f, 1.0f, 1.5f },
{ 0.0f, 0.0f, 1.5f, 1.5f, 1.0f },
},
{
{ 0.0f, 0.0f, 0.5f, 0.0f, 1.5f },
{ 0.0f, 0.0f, 0.5f, 0.0f, 1.5f },
},
{
{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f },
},
};
real const communication_play_delays[NUMBER_OF_COMMUNICATION_TIMER_TYPES] =
{
0.0f, 0.5f, 0.8f, 0.5f, 0.8f,
};
short const communication_player_additional_delay = 30;
short const communication_overlap_time_modifier = 45;
short const communication_timeout_low_priority_modifier = 30;
short const communication_repeat_selection_time = 900;
real const communication_player_absolute_range = 30.0f;
real const communication_player_ideal_range_min = 3.0f;
real const communication_player_ideal_range_max = 15.0f;
real const communication_player_ideal_fov = 0.70710677f;
real const communication_player_rating_low_priority = 2.0f;
struct dialogue_usage const global_dialogue_table[NUMBER_OF_DIALOGUE_USAGES] =
{
/* death */
{ 0, 3, 49, -1, 1, 6, 2, 1, 10.0f, 0.0f,
0, -1, 2, -1, -1, -1, -1 },
{ 0, 6, 51, -1, 1, 6, 3, 6, 20.0f, 0.0f,
66, -1, 2, -1, 1, -1, -1 },
{ 0, 2, 53, -1, 1, 1, 2, 1, 10.0f, 0.0f,
8, -1, 3, -1, -1, -1, -1 },
{ 0, 6, 55, -1, 1, 6, 3, 6, 20.0f, 0.0f,
74, -1, 3, -1, 1, -1, -1 },
{ 0, 2, 57, -1, 1, 1, 2, 1, 10.0f, 0.0f,
8, -1, 3, -1, 4, -1, -1 },
{ 0, 2, 59, -1, 1, 1, 2, 1, 40.0f, 0.0f,
8, -1, 3, -1, 8, -1, -1 },
{ 0, 2, 61, -1, 1, 1, 2, 1, 40.0f, 0.0f,
8, -1, 3, -1, 16, -1, -1 },
{ 0, 2, 63, -1, 1, 1, 2, 1, 40.0f, 0.0f,
8, -1, 3, -1, 64, -1, -1 },
{ 0, 2, 65, -1, 1, 1, 2, 1, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, 2 },
{ 0, 2, 67, -1, 1, 1, 2, 1, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, 10 },
{ 0, 2, 66, -1, 1, 1, 2, 1, 10.0f, 0.0f,
0, -1, 3, -1, -1, -1, 11 },
{ 0, 3, 68, -1, 1, 1, 2, 1, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 5 },
{ 0, 3, 69, -1, 1, 1, 2, 1, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 3 },
{ 0, 3, 70, -1, 1, 1, 2, 1, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 4 },
{ 0, 3, 71, -1, 1, 1, 2, 1, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 6 },
{ 0, 2, 72, -1, 1, 1, 2, 1, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 7 },
{ 0, 2, 73, -1, 1, 1, 2, 1, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 12 },
{ 0, 3, 74, -1, 1, 1, 2, 1, 30.0f, 0.0f,
0, -1, 3, -1, -1, -1, 9 },
{ 0, 2, 75, -1, 1, 1, 2, 1, 30.0f, 10.0f,
0, -1, 3, -1, -1, -1, 8 },
{ 0, 2, 96, -1, 2, 1, 3, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
{ 0, 6, 97, -1, 2, 6, 3, 6, 15.0f, 0.0f,
66, -1, -1, -1, 1, -1, -1 },
{ 0, 3, 98, -1, 2, 5, 1, 1, 10.0f, 0.0f,
0, -1, 2, -1, -1, -1, -1 },
{ 0, 4, 99, -1, 2, 6, 3, 5, 20.0f, 0.0f,
66, -1, 2, -1, -1, 1, -1 },
{ 0, 2, 100, -1, 2, 1, 3, 1, 10.0f, 0.0f,
0, -1, 3, -1, -1, -1, -1 },
{ 0, 2, 101, -1, 2, 1, 3, 1, 10.0f, 0.0f,
0, -1, 3, -1, -1, 1, -1 },
{ 0, 2, 102, -1, 2, 1, 3, 1, 10.0f, 0.0f,
0, -1, 3, -1, -1, 4, -1 },
{ 0, 2, 103, -1, 2, 1, 3, 1, 40.0f, 0.0f,
0, -1, 3, -1, -1, 56, -1 },
{ 0, 2, 104, -1, 2, 1, 3, 1, 40.0f, 0.0f,
0, -1, 3, -1, -1, 64, -1 },
{ 0, 5, 105, -1, 2, 6, 3, 6, 30.0f, 0.0f,
2, -1, 4, -1, -1, -1, -1 },
/* killing_spree */
{ 1, 4, 76, -1, 0, 1, 2, 1, 30.0f, 0.0f,
8, -1, -1, -1, -1, -1, -1 },
/* damage */
{ 3, 3, 21, -1, 1, 1, 2, 1, 10.0f, 0.0f,
0, -1, 2, -1, -1, -1, -1 },
{ 3, 3, 22, -1, 1, 1, 2, 1, 10.0f, 0.0f,
2, -1, 2, -1, 1, -1, -1 },
{ 3, 3, 29, -1, 0, 5, 3, 1, 10.0f, 0.0f,
8, -1, 2, -1, -1, -1, -1 },
{ 3, 3, 31, -1, 0, 6, 3, 1, 10.0f, 5.0f,
0, -1, 2, -1, -1, 1, -1 },
/* hurt */
{ 2, 1, 23, -1, 1, 1, 0, 0, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, -1 },
{ 2, 2, 32, -1, 0, 5, 0, 0, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, -1 },
{ 2, 2, 35, -1, 0, 5, 0, 0, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, 2 },
{ 2, 2, 36, -1, 0, 5, 0, 0, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, 11 },
{ 2, 2, 37, -1, 0, 5, 0, 0, 10.0f, 10.0f,
0, -1, 3, -1, -1, -1, 10 },
{ 2, 3, 38, -1, 0, 5, 0, 0, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 5 },
{ 2, 2, 40, -1, 0, 5, 0, 0, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 4 },
{ 2, 3, 41, -1, 0, 5, 0, 0, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 6 },
{ 2, 2, 42, -1, 0, 5, 0, 0, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 7 },
{ 2, 2, 43, -1, 0, 5, 0, 0, 20.0f, 0.0f,
0, -1, 3, -1, -1, -1, 12 },
{ 2, 3, 44, -1, 0, 5, 0, 0, 30.0f, 0.0f,
0, -1, 3, -1, -1, -1, 9 },
{ 2, 2, 45, -1, 0, 5, 0, 0, 30.0f, 0.0f,
0, -1, 3, -1, -1, -1, 8 },
/* sighted_enemy */
{ 4, 5, 108, 3, 0, 6, 3, 6, 10.0f, 10.0f,
0, -1, -1, 0, -1, -1, -1 },
{ 4, 5, 109, 3, 0, 6, 3, 6, 10.0f, 25.0f,
0, -1, -1, 2, -1, -1, -1 },
/* found_enemy */
{ 5, 5, 110, 3, 0, 6, 3, 6, 10.0f, 25.0f,
0, -1, -1, 2, -1, -1, -1 },
/* unexpected_enemy */
{ 6, 3, 111, -1, 0, 6, 3, 4, 10.0f, 8.0f,
1, -1, -1, -1, -1, -1, -1 },
/* found_dead_friend */
{ 7, 5, 112, -1, 0, 6, 3, 6, 10.0f, 20.0f,
0, -1, -1, 4, -1, -1, -1 },
/* allegiance_changed */
{ 8, 6, 113, -1, 0, 6, 3, 6, 10.0f, 0.0f,
68, -1, 4, -1, -1, -1, -1 },
{ 8, 4, 114, -1, 0, 6, 3, 4, 10.0f, 0.0f,
64, -1, 2, -1, -1, -1, -1 },
/* sighted_friend_player */
{ 25, 4, 148, 3, 0, 6, 3, 5, 10.0f, 15.0f,
0, -1, -1, 4, -1, -1, -1 },
/* lost_contact */
{ 13, 4, 161, -1, 0, 0, 2, 1, 10.0f, 20.0f,
0, -1, -1, 3, -1, -1, -1 },
{ 13, 4, 127, -1, 0, 0, 2, 1, 15.0f, 20.0f,
0, 0, -1, 3, -1, -1, -1 },
/* alert_noncombat */
{ 15, 4, 125, -1, 0, 4, 2, 5, 10.0f, 20.0f,
0, -1, -1, 2, -1, -1, -1 },
/* blocked */
{ 14, 2, 129, 1, 0, 4, 2, 1, 10.0f, 0.0f,
0, 1, -1, -1, -1, -1, 0 },
/* search_start */
{ 16, 3, 131, -1, 0, 0, 2, 1, 10.0f, 8.0f,
0, 0, -1, 3, -1, -1, -1 },
/* search_query */
{ 17, 3, 132, -1, 0, 5, 2, 1, 10.0f, 25.0f,
0, 0, -1, 3, -1, -1, -1 },
/* search_report */
{ 18, 3, 134, -1, 0, 0, 2, 1, 10.0f, 25.0f,
0, 0, -1, 3, -1, -1, -1 },
/* search_abandon */
{ 19, 3, 135, -1, 0, 0, 2, 1, 10.0f, 8.0f,
0, 0, -1, 3, -1, -1, -1 },
/* search_group_abandon */
{ 20, 4, 136, -1, 0, 0, 2, 1, 10.0f, 0.0f,
0, 0, -1, 3, -1, -1, -1 },
/* uncover_start */
{ 21, 3, 137, 2, 0, 0, 2, 1, 10.0f, 30.0f,
0, 1, -1, 3, -1, -1, -1 },
/* cover */
{ 24, 2, 143, -1, 0, 0, 2, 1, 10.0f, 30.0f,
0, 1, -1, -1, -1, -1, -1 },
/* advance */
{ 22, 4, 139, 2, 0, 0, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* retreat */
{ 23, 4, 141, -1, 0, 0, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* shooting */
{ 26, 1, 149, -1, 0, 0, 0, 0, 10.0f, 10.0f,
0, -1, -1, -1, -1, -1, -1 },
/* shooting_vehicle */
{ 27, 1, 150, -1, 0, 0, 0, 0, 20.0f, 10.0f,
0, -1, -1, -1, -1, -1, -1 },
/* shooting_berserk */
{ 28, 2, 151, -1, 0, 0, 0, 0, 20.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* shooting_group */
{ 29, 2, 152, -1, 0, 0, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* shooting_traitor */
{ 30, 1, 153, -1, 0, 0, 0, 0, 30.0f, 10.0f,
0, -1, -1, -1, -1, -1, -1 },
/* flee */
{ 31, 3, 156, -1, 0, 0, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* flee_leader_died */
{ 32, 4, 158, -1, 0, 0, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* flee_idle */
{ 33, 3, 2, -1, 0, 0, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* attempted_flee */
{ 34, 2, 159, -1, 0, 4, 2, 5, 10.0f, 10.0f,
0, 1, -1, -1, -1, -1, -1 },
/* hiding_finished */
{ 35, 3, 162, -1, 0, 0, 2, 1, 10.0f, 30.0f,
0, 1, -1, 3, -1, -1, -1 },
/* vehicle_entry */
{ 36, 3, 163, -1, 0, 0, 0, 0, 10.0f, 10.0f,
0, -1, -1, -1, -1, -1, -1 },
/* vehicle_exit */
{ 37, 3, 164, -1, 0, 0, 0, 0, 10.0f, 10.0f,
0, -1, -1, -1, -1, -1, -1 },
/* vehicle_woohoo */
{ 38, 2, 165, 12, 2, 0, 0, 0, 10.0f, 25.0f,
48, -1, -1, -1, -1, -1, -1 },
/* vehicle_scared */
{ 39, 2, 166, 13, 2, 0, 0, 0, 10.0f, 25.0f,
48, -1, -1, -1, -1, -1, -1 },
/* vehicle_falling */
{ 40, 7, 10, 13, 2, 0, 0, 0, 10.0f, 0.0f,
48, -1, -1, -1, -1, -1, -1 },
/* grenade_sighted */
{ 11, 3, 117, -1, 0, 6, 4, 6, 10.0f, 4.0f,
0, 0, 3, -1, -1, -1, -1 },
/* grenade_startle */
{ 10, 3, 116, -1, 0, 6, 4, 6, 10.0f, 4.0f,
0, -1, -1, 4, -1, -1, -1 },
/* grenade_danger */
{ 12, 5, 118, -1, 0, 5, 4, 5, 10.0f, 4.0f,
0, 0, 3, -1, -1, -1, -1 },
{ 12, 5, 120, -1, 0, 5, 4, 5, 10.0f, 0.0f,
0, 0, 2, -1, -1, -1, -1 },
{ 12, 6, 119, -1, 0, 6, 4, 5, 10.0f, 0.0f,
0, -1, 1, -1, -1, -1, -1 },
/* surprise */
{ 41, 7, 177, -1, 0, 6, 2, 4, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* berserk */
{ 42, 7, 178, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* melee */
{ 43, 7, 179, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* grenade_throwing */
{ 9, 7, 115, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* dive */
{ 44, 7, 180, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* leap */
{ 47, 7, 182, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* falling */
{ 46, 7, 10, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* postcombat_alone */
{ 48, 3, 197, -1, 0, 5, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* postcombat_unscathed */
{ 49, 3, 198, -1, 0, 0, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* postcombat_wounded */
{ 50, 3, 199, -1, 0, 0, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* postcombat_massacre */
{ 51, 3, 201, -1, 0, 0, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* postcombat_triumph */
{ 52, 3, 203, -1, 0, 0, 0, 0, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* postcombat_check_enemy */
{ 53, 3, 189, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, 3, -1, -1, -1, -1 },
/* postcombat_check_friend */
{ 54, 3, 190, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, 2, -1, -1, -1, -1 },
/* postcombat_shoot_corpse */
{ 55, 3, 191, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
{ 55, 6, 192, -1, 0, 4, 2, 1, 10.0f, 0.0f,
2, -1, -1, -1, -1, 1, -1 },
/* postcombat_celebrate */
{ 56, 3, 188, -1, 0, 4, 2, 1, 10.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
/* sentinel */
{ -1, -1, -1, -1, -1, -1, -1, -1, 0.0f, 0.0f,
0, -1, -1, -1, -1, -1, -1 },
};
struct reply_usage const global_reply_table[NUMBER_OF_REPLY_USAGES] =
{
{ 76, -1, 2, 92, -1, 2, 0, 0.0f, 1.0f, 0.7f, 30.0f,
reply_filter_close },
{ 53, 2, 2, 81, -1, 2, 0, 0.0f, 0.5f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 11, 2, 82, -1, 2, 0, 0.0f, 0.6f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 10, 2, 83, -1, 2, 0, 0.0f, 0.6f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 5, 2, 84, -1, 2, 0, 0.0f, 0.8f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 3, 2, 85, -1, 2, 0, 0.5f, 0.9f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 4, 2, 86, -1, 2, 0, 0.0f, 1.0f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 6, 2, 87, -1, 2, 0, 0.0f, 1.0f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 7, 2, 88, -1, 2, 0, 0.0f, 0.9f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 12, 2, 89, -1, 2, 0, 0.0f, 0.6f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 9, 2, 90, -1, 2, 0, 0.0f, 0.8f, 0.7f, 60.0f,
reply_filter_close },
{ 53, 8, 2, 91, -1, 2, 0, 0.0f, 1.0f, 0.7f, 60.0f,
reply_filter_close },
{ 55, -1, 2, 56, -1, 4, 1, 1.0f, 1.0f, 0.3f, 0.0f,
reply_filter_close },
{ 57, -1, 2, 58, -1, 2, 0, 0.8f, 0.6f, 0.5f, 30.0f,
reply_filter_close },
{ 59, -1, 2, 60, -1, 2, 0, 0.8f, 0.6f, 0.5f, 20.0f,
reply_filter_close },
{ 61, -1, 2, 62, -1, 2, 0, 0.8f, 0.6f, 0.5f, 20.0f,
reply_filter_close },
{ 63, -1, 2, 64, -1, 2, 0, 0.8f, 0.6f, 0.5f, 20.0f,
reply_filter_close },
{ 53, -1, 2, 54, -1, 2, 0, 0.6f, 0.4f, 0.5f, 30.0f,
reply_filter_close },
{ 53, -1, 2, 80, -1, 2, 0, 0.0f, 0.4f, 0.7f, 40.0f,
reply_filter_close },
{ 23, -1, 2, 24, -1, 1, 0, 0.8f, 0.0f, 0.7f, 20.0f,
NULL },
{ 32, -1, 4, 33, -1, 1, 0, 0.8f, 0.0f, 0.7f, 20.0f,
NULL },
{ 32, -1, 2, 34, -1, 1, 0, 0.8f, 0.0f, 0.7f, 20.0f,
NULL },
{ 51, -1, 2, 52, -1, 4, 1, 1.0f, 0.0f, 0.3f, 0.0f,
reply_filter_close },
{ 49, -1, 2, 50, -1, 3, 0, 0.7f, 0.0f, 0.3f, 20.0f,
reply_filter_close },
{ 29, -1, 3, 30, -1, 2, 0, 0.7f, 0.4f, 0.5f, 20.0f,
NULL },
{ 108, -1, 2, 123, -1, 3, 0, 0.8f, 0.0f, 0.7f, 20.0f,
reply_filter_close },
{ 108, -1, 2, 124, -1, 3, 0, 0.8f, 0.0f, 0.7f, 20.0f,
reply_filter_not_close },
{ 109, -1, 2, 123, -1, 3, 0, 0.8f, 0.0f, 0.7f, 20.0f,
reply_filter_close },
{ 109, -1, 2, 124, -1, 3, 0, 0.8f, 0.0f, 0.7f, 20.0f,
reply_filter_not_close },
{ 110, -1, 2, 123, -1, 3, 0, 0.8f, 0.0f, 0.7f, 20.0f,
reply_filter_close },
{ 110, -1, 2, 124, -1, 3, 0, 0.8f, 0.0f, 0.7f, 20.0f,
reply_filter_not_close },
{ 125, -1, 3, 126, -1, 3, 0, 0.7f, 0.0f, 0.5f, 15.0f,
NULL },
{ 127, -1, 2, 128, -1, 3, 0, 0.7f, 0.0f, 0.5f, 30.0f,
reply_filter_no_certain_target },
{ 129, -1, 3, 130, -1, 3, 0, 0.5f, 0.0f, 0.5f, 20.0f,
NULL },
{ 132, -1, 2, 133, -1, 3, 0, 1.0f, 0.0f, 0.3f, 20.0f,
reply_filter_searching },
{ 137, -1, 2, 138, -1, 3, 0, 1.0f, 0.0f, 0.3f, 20.0f,
reply_filter_same_target },
{ 139, -1, 2, 140, -1, 4, 0, 0.7f, 0.0f, 0.7f, 20.0f,
reply_filter_same_platoon },
{ 141, -1, 2, 142, -1, 4, 0, 0.7f, 0.0f, 0.7f, 20.0f,
reply_filter_same_platoon },
{ 156, -1, 2, 157, -1, 3, 0, 0.5f, 0.0f, 0.7f, 30.0f,
reply_filter_fighting_close },
{ 156, -1, 4, 154, -1, 3, 0, 0.5f, 0.0f, 0.7f, 30.0f,
reply_filter_fighting },
{ 158, -1, 4, 154, -1, 3, 0, 0.5f, 0.0f, 0.7f, 30.0f,
reply_filter_fighting },
{ 159, -1, 2, 160, -1, 3, 0, 0.5f, 0.0f, 0.7f, 20.0f,
reply_filter_flee_leader },
{ 199, -1, 2, 200, -1, 3, 0, 0.8f, 0.0f, 0.5f, 0.0f,
NULL },
{ 201, -1, 2, 202, -1, 3, 0, 0.8f, 0.0f, 0.5f, 0.0f,
NULL },
{ 203, -1, 2, 204, -1, 3, 0, 0.8f, 0.0f, 0.5f, 0.0f,
NULL },
{ -1, -1, -1, -1, -1, -1, 0, 0.0f, 0.0f, 0.0f, 0.0f,
NULL },
};
char const *global_communication_priority_names[NUMBER_OF_COMMUNICATION_PRIORITIES] =
{
"none",
"filler",
"chatter",
"talk",
"communicate",
"shout",
"yell",
"exclaim",
};
char const *global_communication_team_names[NUMBER_OF_COMMUNICATION_TEAMS] =
{
"human",
"HUM",
"covenant",
"COV",
};
char const *global_communication_type_names[NUMBER_OF_COMMUNICATION_TYPES] =
{
"death",
"killing_spree",
"hurt",
"damage",
"sighted_enemy",
"found_enemy",
"unexpected_enemy",
"found_dead_friend",
"allegiance_changed",
"grenade_throwing",
"grenade_startle",
"grenade_sighted",
"grenade_danger",
"lost_contact",
"blocked",
"alert_noncombat",
"search_start",
"search_query",
"search_report",
"search_abandon",
"search_group_abandon",
"uncover_start",
"advance",
"retreat",
"cover",
"sighted_friend_player",
"shooting",
"shooting_vehicle",
"shooting_berserk",
"shooting_group",
"shooting_traitor",
"flee",
"flee_leader_died",
"flee_idle",
"attempted_flee",
"hiding_finished",
"vehicle_entry",
"vehicle_exit",
"vehicle_woohoo",
"vehicle_scared",
"vehicle_falling",
"surprise",
"berserk",
"melee",
"dive",
"uncover_exclamation",
"falling",
"leap",
"postcombat_alone",
"postcombat_unscathed",
"postcombat_wounded",
"postcombat_massacre",
"postcombat_triumph",
"postcombat_check_enemy",
"postcombat_check_friend",
"postcombat_shoot_corpse",
"postcombat_celebrate",
};
short global_dialogue_event_count = 0;
struct dialogue_event_status *global_dialogue_events = NULL;
short global_reply_event_count = 0;
struct dialogue_event_status *global_reply_events = NULL;
short global_communication_table_indices[NUMBER_OF_COMMUNICATION_TYPES];
/* ---------- public code */
void ai_communication_initialize(
void)
{
struct dialogue_usage const *dialogue;
struct reply_usage const *reply;
short *communication_table_index;
short communication_type;
short communication_type_index;
short dialogue_index;
global_dialogue_event_count = 0;
dialogue = global_dialogue_table;
do
{
dialogue++;
global_dialogue_event_count++;
}
while (dialogue->communication_type != NONE);
if (global_dialogue_events == NULL)
{
global_dialogue_events = (struct dialogue_event_status *)game_state_malloc(
"ai communication dialogue",
NULL,
16 * global_dialogue_event_count);
match_vassert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x286,
global_dialogue_events,
"ai_communication_initialize: unable to allocate comm dialogue status table");
}
global_reply_event_count = 0;
reply = global_reply_table;
do
{
reply++;
global_reply_event_count++;
}
while (reply->original_vocalization_type != NONE);
if (global_reply_events == NULL)
{
global_reply_events = (struct dialogue_event_status *)game_state_malloc(
"ai communication replies",
NULL,
16 * global_reply_event_count);
match_vassert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x293,
global_reply_events,
"ai_communication_initialize: unable to allocate comm reply status table");
}
communication_type_index = 0;
communication_table_index = global_communication_table_indices;
while (communication_type_index < NUMBER_OF_COMMUNICATION_TYPES)
{
dialogue_index = 0;
*communication_table_index = NONE;
dialogue = global_dialogue_table;
communication_type = 0;
while (communication_type != NONE)
{
if (communication_type == communication_type_index)
{
*communication_table_index = dialogue_index;
break;
}
dialogue++;
communication_type = dialogue->communication_type;
dialogue_index++;
}
communication_type_index++;
communication_table_index++;
}
conversation_data = game_state_data_new("ai conversation", 8, 100);
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x2A8,
conversation_data);
return;
}
void ai_communication_dispose(
void)
{
return;
}
void ai_communication_initialize_for_new_map(
void)
{
short event_index;
ai_globals->dialogue_triggers_enabled = TRUE;
csmemset(ai_globals->last_chatter_time, 0, sizeof(ai_globals->last_chatter_time));
csmemset(ai_globals->last_talk_time, 0, sizeof(ai_globals->last_talk_time));
csmemset(ai_globals->last_shout_time, 0, sizeof(ai_globals->last_shout_time));
event_index = 0;
if (((long)global_dialogue_event_count << 1) > 0)
{
do
{
global_dialogue_events[event_index].disable_until_time = NONE;
global_dialogue_events[event_index].last_time_spoken = NONE;
event_index = (short)(event_index + 1);
}
while ((long)event_index < (long)global_dialogue_event_count << 1);
}
event_index = 0;
if (((long)global_reply_event_count << 1) > 0)
{
do
{
global_reply_events[event_index].disable_until_time = NONE;
global_reply_events[event_index].last_time_spoken = NONE;
event_index = (short)(event_index + 1);
}
while ((long)event_index < (long)global_reply_event_count << 1);
}
ai_globals->recent_conversation_count = 0;
ai_globals->recent_conversation_next_index = 0;
csmemset(
ai_globals->recent_conversations,
0,
sizeof(ai_globals->recent_conversations));
data_make_valid(conversation_data);
return;
}
void ai_communication_dispose_from_old_map(
void)
{
data_make_invalid(conversation_data);
return;
}
void ai_communication_packet_new(
struct ai_information_packet *information)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x300,
information);
csmemset(information, 0, sizeof(*information));
information->target_unit_index = NONE;
information->communication_type = NONE;
information->dialogue_type_index = NONE;
information->damage_category = NONE;
return;
}
char const *ai_communication_get_type_name(
short communication_type)
{
char const *name = "<error>";
if (communication_type >= 0 &&
communication_type < NUMBER_OF_COMMUNICATION_TYPES)
{
name = global_communication_type_names[communication_type];
}
return name;
}
short ai_communication_get_type_by_name(
char const *name)
{
short communication_type = NONE;
short index;
for (index = 0; index < NUMBER_OF_COMMUNICATION_TYPES; index++)
{
if (csstrcmp(global_communication_type_names[index], name) == 0)
{
communication_type = index;
}
}
return communication_type;
}
static boolean reply_filter_close(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct prop_datum *prop;
long prop_index;
boolean result;
result = FALSE;
if (reply_actor_index != NONE)
{
prop_index = prop_get_base_by_unit_index(
reply_actor_index,
original_unit_index,
TRUE,
TRUE);
if (prop_index != NONE)
{
prop = prop_get(prop_index);
if (prop->distance < COMMUNICATION_CLOSE_DISTANCE &&
(prop->line_of_sight == _ai_line_of_sight_clear ||
prop->line_of_sight == _ai_line_of_sight_occluded))
{
result = TRUE;
}
}
}
return result;
}
static boolean reply_filter_not_close(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct prop_datum *prop;
long prop_index;
boolean result;
result = FALSE;
if (reply_actor_index != NONE)
{
prop_index = prop_get_base_by_unit_index(
reply_actor_index,
original_unit_index,
TRUE,
TRUE);
if (prop_index != NONE)
{
prop = prop_get(prop_index);
if (prop->distance > COMMUNICATION_CLOSE_DISTANCE ||
(prop->line_of_sight != _ai_line_of_sight_clear &&
prop->line_of_sight != _ai_line_of_sight_occluded))
{
result = TRUE;
}
}
}
return result;
}
static boolean reply_filter_searching(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct actor_datum *reply_actor;
boolean result;
result = FALSE;
if (reply_actor_index != NONE)
{
reply_actor = actor_get(reply_actor_index);
switch (reply_actor->state.action)
{
case _actor_action_uncover:
result = (boolean)(reply_actor->state.action_data.uncover.pursuit_location.type ==
_pursuit_location_position);
break;
case _actor_action_search:
result = TRUE;
break;
}
}
return result;
}
static boolean reply_filter_same_platoon(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct unit_datum *original_unit;
struct actor_datum *original_actor;
struct actor_datum *reply_actor;
long original_actor_index;
boolean result;
result = FALSE;
if (reply_filter_close(
original_unit_index,
communication,
reply_actor_index))
{
original_unit = unit_get(original_unit_index);
original_actor_index = original_unit->unit.actor_index;
if (original_actor_index != NONE && reply_actor_index != NONE)
{
original_actor = actor_get(original_actor_index);
reply_actor = actor_get(reply_actor_index);
result = original_actor->meta.encounter_index != NONE &&
original_actor->meta.encounter_index == reply_actor->meta.encounter_index &&
original_actor->meta.platoon_index == reply_actor->meta.platoon_index;
}
}
return result;
}
static boolean reply_filter_fighting(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
return actor_is_fighting(reply_actor_index);
}
static boolean reply_filter_fighting_close(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
boolean result;
result = FALSE;
if (reply_filter_close(
original_unit_index,
communication,
reply_actor_index) &&
actor_is_fighting(reply_actor_index))
{
result = TRUE;
}
return result;
}
static boolean reply_filter_same_target(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct unit_datum *original_unit;
struct actor_datum *original_actor;
struct actor_datum *reply_actor;
struct prop_datum *original_prop;
struct prop_datum *reply_prop;
long original_actor_index;
boolean result;
result = FALSE;
if (reply_filter_close(
original_unit_index,
communication,
reply_actor_index))
{
original_unit = unit_get(original_unit_index);
original_actor_index = original_unit->unit.actor_index;
if (original_actor_index != NONE && reply_actor_index != NONE)
{
original_actor = actor_get(original_actor_index);
reply_actor = actor_get(reply_actor_index);
if (original_actor->target.target_prop_index != NONE &&
reply_actor->target.target_prop_index != NONE)
{
original_prop = prop_get(original_actor->target.target_prop_index);
reply_prop = prop_get(reply_actor->target.target_prop_index);
result = original_prop->unit_index == reply_prop->unit_index;
}
}
}
return result;
}
static boolean reply_filter_no_certain_target(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct actor_datum *reply_actor;
boolean result;
result = FALSE;
if (reply_actor_index != NONE)
{
reply_actor = actor_get(reply_actor_index);
if (reply_actor->state.mode == _actor_mode_combat &&
reply_actor->state.combat_status < _actor_combat_status_certain)
{
result = TRUE;
}
}
return result;
}
static boolean reply_filter_flee_leader(
long original_unit_index,
struct ai_information_packet *communication,
long reply_actor_index)
{
struct actor_datum *reply_actor;
boolean result;
result = FALSE;
if (actor_is_fighting(reply_actor_index))
{
reply_actor = actor_get(reply_actor_index);
if (reply_actor->meta.type == _actor_elite)
{
result = TRUE;
}
}
return result;
}
static short ai_communication_consider_speech(
long unit_index,
short communication_priority,
short speech_priority,
short delay_ticks,
boolean allow_vocalization_lookup,
boolean allow_recent_disabling,
short *vocalization_type,
real *weight,
long *sound_definition_index_reference,
char *debug_string)
{
long last_speech_time;
long elapsed_time;
short play_type;
short timer_tolerance;
short speech_time;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xC1A,
vocalization_type && sound_definition_index_reference && weight);
play_type = unit_test_speech(
unit_index,
speech_priority,
allow_vocalization_lookup,
TRUE,
&last_speech_time,
vocalization_type,
sound_definition_index_reference);
if (play_type == _unit_play_speech_none)
{
if (debug_string)
{
sprintf(
debug_string,
"nospch-%s",
unit_get_speech_priority_name(speech_priority));
}
}
else if (play_type == _unit_play_speech_queue)
{
*weight *= 0.3f;
}
if ((game_connection() != _game_connection_local ||
!ai_debug.communication_unit_repeat_disabled) &&
allow_recent_disabling &&
communication_priority < _ai_communication_priority_shout &&
last_speech_time != NONE)
{
elapsed_time = game_time_get() - last_speech_time;
speech_time = (short)(elapsed_time < 0 ? 0 : elapsed_time);
timer_tolerance = (short)(long)(
communication_timer_tolerances[communication_priority][0][2] * 30.0f +
(real)delay_ticks);
if (speech_time <= timer_tolerance)
{
play_type = _unit_play_speech_none;
*weight = 0.0f;
if (debug_string)
{
sprintf(
debug_string,
"spk%d<tol%d+%d",
speech_time,
delay_ticks,
timer_tolerance - delay_ticks);
}
}
else if (speech_time < timer_tolerance + 60)
{
*weight =
(real)(speech_time - timer_tolerance) *
*weight *
0.016666668f;
}
}
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xC49,
(play_type == _unit_play_speech_none) || (*weight > 0.0f));
return play_type;
}
static real ai_communication_actor_talk_weight(
long actor_index,
long subject_unit_index,
real_point3d const *subject_point,
long cause_unit_index,
real_point3d const *cause_point,
real stimulus_range,
short ai_communication_type,
short ai_communication_priority,
short unit_speech_priority,
short vocalization_type,
short animation_type,
short flags)
{
struct actor_datum *actor;
boolean has_target;
boolean passes;
real weight;
actor = actor_get(actor_index);
has_target = subject_unit_index != NONE || cause_unit_index != NONE;
passes = TRUE;
weight = 10.0f;
if (actor->meta.unit_index == NONE)
{
passes = FALSE;
}
if (actor->state.mode > _actor_mode_asleep && passes)
{
if (has_target)
{
if (subject_unit_index != NONE)
{
real dx;
real dy;
real dz;
real distance_squared;
dx = subject_point->x - actor->input.position.head_position.x;
dy = subject_point->y - actor->input.position.head_position.y;
dz = subject_point->z - actor->input.position.head_position.z;
distance_squared = dx * dx;
distance_squared += dy * dy;
distance_squared += dz * dz;
passes =
distance_squared <
stimulus_range * stimulus_range;
}
else
{
passes = FALSE;
}
if (!passes && cause_unit_index != NONE)
{
passes = distance_squared3d(
&actor->input.position.head_position,
cause_point) <
stimulus_range * stimulus_range;
}
}
if (!passes)
{
goto done;
}
if (TEST_FLAG(flags, _find_actor_near_to_players_bit))
{
real player_rating;
player_rating = ai_communication_get_player_rating(
actor->meta.unit_index,
FALSE,
NULL,
NULL);
if (player_rating == 0.0f)
{
passes = FALSE;
goto done;
}
else
{
weight = player_rating * 5.0f + 10.0f;
}
}
if (TEST_FLAG(flags, _find_actor_same_vehicle_bit) &&
subject_unit_index != NONE)
{
struct unit_datum *subject_unit;
subject_unit = unit_get(subject_unit_index);
if (subject_unit->object.parent_object_index !=
actor->input.vehicle_index)
{
passes = FALSE;
goto done;
}
}
if ((short)animation_type != NONE &&
unit_test_animation_impulse(
actor->meta.unit_index,
animation_type))
{
weight += 5.0f;
}
if (vocalization_type != NONE)
{
short vocalization_lookup_type = vocalization_type;
long sound_definition_index = NONE;
if (ai_communication_consider_speech(
actor->meta.unit_index,
ai_communication_priority,
unit_speech_priority,
0,
TEST_FLAG(flags, _find_actor_allow_lookup_bit),
TRUE,
&vocalization_lookup_type,
&weight,
&sound_definition_index,
NULL) == _unit_play_speech_none)
{
passes = FALSE;
goto done;
}
}
if (has_target)
{
boolean subject_matches;
boolean cause_matches;
subject_matches = FALSE;
cause_matches = FALSE;
if (subject_unit_index != NONE)
{
if (actor->meta.unit_index == subject_unit_index)
{
if (TEST_FLAG(flags, _find_actor_allow_subject_bit))
{
subject_matches = TRUE;
}
else
{
passes = FALSE;
}
}
else
{
long prop_index;
prop_index = prop_get_base_by_unit_index(
actor_index,
subject_unit_index,
TRUE,
FALSE);
if (prop_index != NONE)
{
struct prop_datum *prop;
prop = prop_get(prop_index);
if (prop->distance > stimulus_range)
{
subject_matches = FALSE;
}
else if (prop->state >= _prop_state_becoming_unacknowledged &&
prop->state <= _prop_state_acknowledged)
{
subject_matches = TRUE;
}
else if (!prop->enemy)
{
if (ai_communication_type != 0)
{
subject_matches = TRUE;
}
else if (prop->audibility >= _ai_sound_volume_loud ||
prop->ineffability >= 2 ||
actor_visibility_at_point(
actor_index,
&actor->input.position,
&prop->head_position,
prop->flashlight ?
_prop_lighting_bright :
prop->lighting,
(word)prop->line_of_sight,
TRUE,
FALSE,
actor_get_perception_knowledge(
actor_index,
prop_index)) >= 2)
{
subject_matches = TRUE;
}
}
if (subject_matches)
{
weight +=
(1.0f - prop->distance / stimulus_range) *
10.0f;
}
}
}
}
if (cause_unit_index != NONE)
{
if (actor->meta.unit_index == cause_unit_index)
{
if (TEST_FLAG(flags, _find_actor_allow_cause_bit))
{
cause_matches = TRUE;
}
else
{
passes = FALSE;
}
}
else
{
long prop_index;
prop_index = prop_get_active_by_unit_index(
actor_index,
cause_unit_index);
if (prop_index != NONE)
{
struct prop_datum *prop;
prop = prop_get(prop_index);
if (prop->distance > stimulus_range)
{
cause_matches = FALSE;
}
else if (prop->state >= _prop_state_becoming_unacknowledged &&
prop->state <= _prop_state_acknowledged)
{
cause_matches = TRUE;
}
else if (!prop->enemy)
{
cause_matches = TRUE;
}
if (cause_matches)
{
weight +=
(1.0f - prop->distance / stimulus_range) *
10.0f;
}
}
}
}
if (passes)
{
passes = cause_matches | subject_matches;
}
}
}
else
{
passes = FALSE;
}
done:
return passes ? weight : 0.0f;
}
static long ai_communication_find_specific_actor_to_talk(
long ai_index,
long subject_unit_index,
long cause_unit_index,
real maximum_distance,
short communication_type,
short communication_priority,
short speech_priority,
short vocalization_type,
short animation_type,
short flags)
{
struct ai_script_actor_reference_iterator iterator;
real_point3d subject_point;
real_point3d cause_point;
long best_actor_index = NONE;
real best_weight = 0.0f;
if (ai_index != NONE)
{
if (subject_unit_index != NONE)
{
unit_get_head_position(subject_unit_index, &subject_point);
}
if (cause_unit_index != NONE)
{
unit_get_head_position(cause_unit_index, &cause_point);
}
ai_index_actor_iterator_new(ai_index, &iterator);
while (ai_index_actor_iterator_next(&iterator))
{
real weight = ai_communication_actor_talk_weight(
iterator.actor_index,
subject_unit_index,
&subject_point,
cause_unit_index,
&cause_point,
maximum_distance,
communication_type,
communication_priority,
speech_priority,
vocalization_type,
animation_type,
flags);
if (weight > best_weight)
{
best_actor_index = iterator.actor_index;
best_weight = weight;
}
}
}
return best_actor_index;
}
static long ai_communication_find_global_actor_to_talk(
short team_index,
short find_actor_mode,
long subject_unit_index,
long cause_unit_index,
real maximum_distance,
short communication_type,
short communication_priority,
short speech_priority,
short vocalization_type,
short animation_type,
short flags)
{
struct actor_iterator iterator;
struct actor_datum *actor;
real_point3d subject_point;
real_point3d cause_point;
long best_actor_index = NONE;
real best_weight = 0.0f;
if (subject_unit_index != NONE)
{
unit_get_head_position(subject_unit_index, &subject_point);
}
if (cause_unit_index != NONE)
{
/* BUG (preserved for exact matching): January asks for the subject
* position again here and leaves cause_point uninitialized. A corrected
* build should call unit_get_head_position(cause_unit_index, &cause_point). */
unit_get_head_position(subject_unit_index, &subject_point);
}
actor_iterator_new(&iterator, TRUE);
actor = actor_iterator_next(&iterator);
while (actor)
{
boolean eligible = TRUE;
if (team_index != NONE)
{
boolean enemy = game_team_is_enemy(
team_index,
actor->meta.team_index);
switch (find_actor_mode)
{
case _find_actor_mode_same_team:
eligible = actor->meta.team_index == team_index;
break;
case _find_actor_mode_friend:
eligible = !enemy;
break;
case _find_actor_mode_enemy:
eligible = enemy;
break;
default:
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xDFD,
!"unreachable");
eligible = FALSE;
break;
}
}
if (eligible)
{
real weight = ai_communication_actor_talk_weight(
iterator.index,
subject_unit_index,
&subject_point,
cause_unit_index,
&cause_point,
maximum_distance,
communication_type,
communication_priority,
speech_priority,
vocalization_type,
animation_type,
flags);
if (weight > best_weight)
{
best_actor_index = iterator.index;
best_weight = weight;
}
}
actor = actor_iterator_next(&iterator);
}
return best_actor_index;
}
short actor_communication_team(
long actor_index)
{
struct actor_datum *actor = actor_get(actor_index);
short race = actor_type_get_race(actor->meta.type);
short communication_team = NONE;
if (TEST_FLAG(race, _race_human_bit))
{
communication_team = _ai_communication_team_human;
}
else if (TEST_FLAG(race, _race_covenant_bit))
{
communication_team = _ai_communication_team_covenant;
}
return communication_team;
}
static void ai_communication_look_secondary_at_unit(
long actor_index,
short type,
short priority,
long look_unit_index,
long prop_index)
{
struct direction_specification direction;
short prop_state;
if (actor_index != NONE &&
priority > 0 &&
look_unit_index != NONE &&
unit_try_and_get(look_unit_index))
{
if (prop_index == NONE)
{
prop_index = prop_get_active_by_unit_index(
actor_index,
look_unit_index);
if (prop_index == NONE)
{
goto look_at_point;
}
}
prop_state = prop_get(prop_index)->state;
if (prop_state >= _prop_state_becoming_unacknowledged &&
prop_state <= _prop_state_acknowledged &&
prop_index != NONE)
{
direction.type = _direction_specification_prop;
direction.prop_index = prop_index;
}
else
{
look_at_point:
direction.type = _direction_specification_point;
unit_get_head_position(
look_unit_index,
&direction.point);
}
actor_look_secondary(
actor_index,
type,
priority,
&direction);
}
return;
}
static void ai_communication_look_secondary_at_object(
long actor_index,
short type,
short priority,
long object_index)
{
struct direction_specification direction;
if (actor_index != NONE &&
priority > 0 &&
object_index != NONE &&
object_try_and_get(object_index))
{
direction.type = _direction_specification_object;
direction.object_index = object_index;
actor_look_secondary(
actor_index,
type,
priority,
&direction);
}
return;
}
short ai_conversation_status(
short scenario_conversation_index)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
short status = 0;
data_iterator_new(&iterator, conversation_data);
while ((conversation = (struct conversation_datum *)
data_iterator_next(&iterator)) != NULL)
{
if (conversation->scenario_conversation_index == scenario_conversation_index)
{
short conversation_status;
if (!conversation->begun)
{
conversation_status = 1;
}
else if (!conversation->any_line_spoken)
{
conversation_status = 2;
}
else
{
conversation_status = conversation->waiting_to_advance ? 4 : 3;
}
status = MAX(status, conversation_status);
}
}
if (!status)
{
struct recent_conversation *recent_conversation;
long latest_finish_time = NONE;
short latest_index = NONE;
short index;
for (index = 0; index < ai_globals->recent_conversation_count; index++)
{
if (ai_globals->recent_conversations[index].scenario_conversation_index ==
scenario_conversation_index &&
ai_globals->recent_conversations[index].finish_time > latest_finish_time)
{
latest_index = index;
latest_finish_time = ai_globals->recent_conversations[index].finish_time;
}
}
if (latest_index != NONE)
{
recent_conversation = &ai_globals->recent_conversations[latest_index];
if (recent_conversation->unable_to_begin)
{
status = 5;
}
else
{
status = recent_conversation->finished_successfully ? 6 : 7;
}
}
}
return status;
}
static void actor_reset_idle_vocalization_timer(
long actor_index)
{
struct actor_datum *actor;
struct actor_definition *definition;
struct unit_datum *unit;
boolean in_combat;
short speech_offset;
real delay;
actor = actor_get(actor_index);
definition = actor_definition_get(actor->meta.definition_index);
in_combat = actor_in_combat(actor_index);
speech_offset = 0;
if (actor->meta.unit_index != NONE)
{
unit = unit_get(actor->meta.unit_index);
if (unit->unit.speech.current.priority > 0)
{
speech_offset = unit->unit.speech.sound_timer;
}
}
if (in_combat)
{
real maximum_delay;
real minimum_delay;
maximum_delay = definition->communication.idle_combat_time_upper_bound;
minimum_delay = definition->communication.idle_combat_time_lower_bound;
delay = real_seed_random_range(
get_global_random_seed_address(),
minimum_delay,
maximum_delay);
}
else
{
real maximum_delay;
real minimum_delay;
maximum_delay = definition->communication.idle_noncombat_time_upper_bound;
minimum_delay = definition->communication.idle_noncombat_time_lower_bound;
delay = real_seed_random_range(
get_global_random_seed_address(),
minimum_delay,
maximum_delay);
}
actor->control.idle_vocalization_combat = in_combat;
actor->control.idle_vocalization_timer =
(short)(delay * 30.0f + (real)speech_offset);
return;
}
short ai_conversation_line(
short scenario_conversation_index)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
short line = 999;
data_iterator_new(&iterator, conversation_data);
while ((conversation = (struct conversation_datum *)data_iterator_next(&iterator)) != NULL)
{
if (conversation->scenario_conversation_index == scenario_conversation_index)
{
line = conversation->line_index;
break;
}
}
return line;
}
void ai_conversation_advance(
short scenario_conversation_index)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
data_iterator_new(&iterator, conversation_data);
while ((conversation = (struct conversation_datum *)
data_iterator_next(&iterator)) != NULL)
{
if (conversation->scenario_conversation_index == scenario_conversation_index)
{
if (ai_print_conversations)
{
struct ai_conversation *definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation);
console_printf(
FALSE,
"%s: told to advance by scripting",
definition->name);
}
conversation->told_to_advance = TRUE;
}
}
return;
}
void ai_conversation_finish(
long conversation_index,
boolean unable_to_begin,
boolean success)
{
struct conversation_datum *conversation;
struct conversation_datum *recent_conversation;
struct ai_conversation *definition;
struct actor_datum *actor;
short recent_conversation_index;
long recent_conversation_count;
unsigned long participant_bitmask;
short participant_index;
short actor_action;
if (conversation_index != NONE)
{
conversation = (struct conversation_datum *)datum_get(
conversation_data,
conversation_index);
definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: finished %s%s",
definition->name,
success ? "successfully" : "prematurely",
unable_to_begin ? " (unable to begin)" : "");
}
recent_conversation = (struct conversation_datum *)datum_get(
conversation_data,
conversation_index);
recent_conversation_index =
(word)ai_globals->recent_conversation_next_index;
ai_globals->recent_conversation_next_index =
(short)(recent_conversation_index + 1);
ai_globals->recent_conversation_next_index %=
MAXIMUM_RECENT_CONVERSATIONS;
recent_conversation_count = ai_globals->recent_conversation_count;
if (recent_conversation_count <= recent_conversation_index + 1)
{
recent_conversation_count = recent_conversation_index + 1;
}
ai_globals->recent_conversation_count =
(short)recent_conversation_count;
ai_globals->recent_conversations[recent_conversation_index]
.scenario_conversation_index =
recent_conversation->scenario_conversation_index;
ai_globals->recent_conversations[recent_conversation_index]
.unable_to_begin = unable_to_begin;
ai_globals->recent_conversations[recent_conversation_index]
.finished_successfully = success;
ai_globals->recent_conversations[recent_conversation_index]
.finish_time = game_time_get();
participant_index = 0;
while ((long)participant_index < definition->participants.count)
{
participant_bitmask = conversation->participant_bitmask;
if ((participant_bitmask & FLAG(participant_index)) != 0 &&
conversation->actor_indices[participant_index] != NONE)
{
actor = actor_get(conversation->actor_indices[participant_index]);
actor_action = actor->state.action;
actor->external_orders.conversation_index = NONE;
actor->external_orders.conversation_attention_unit_index = NONE;
if (actor_action == _actor_action_converse)
{
actor->state.action_data.converse.conversation_index = NONE;
}
}
participant_index = (short)(participant_index + 1);
}
datum_delete(conversation_data, conversation_index);
}
return;
}
real ai_communication_get_player_rating(
long unit_index,
boolean test_line_of_sight,
long *unit_index_reference,
real *distance_reference)
{
struct data_iterator iterator;
struct player_datum *player;
real_point3d position;
real_point3d player_position;
/* INFERRED FROM JANUARY'S BYTES - not attested in any surviving source.
* Naming the two endpoints before the vector is taken is what fixes the
* order in which the compiler squares the vector's components: an
* address-of expression and a pointer value are different operand forms
* to this compiler. Measured on this function with everything else held
* constant, squaring order of the components:
*
* &player_position, &position k j i (the untouched source)
* player_head_position, &position k j i
* &player_position, head_position k j i
* player_head_position, head_position k i j <- this spelling
* any of the above plus &vector bound k j i
*
* January squares them k, i, j. Section size, relocation count,
* instruction count and frame size are identical in all five. */
real_point3d const *head_position = &position;
real_point3d const *player_head_position = &player_position;
real_vector3d vector;
real_vector3d aiming_vector;
long closest_unit_index = NONE;
real best_rating = 0.0f;
real closest_distance = REAL_MAX;
boolean any_players = FALSE;
real rating;
unit_get_head_position(unit_index, &position);
data_iterator_new(&iterator, player_data);
while ((player = (struct player_datum *)data_iterator_next(&iterator)) != NULL)
{
if (player->unit_index != NONE)
{
real distance_squared;
any_players = TRUE;
unit_get_head_position(player->unit_index, &player_position);
vector_from_points3d(player_head_position, head_position, &vector);
distance_squared = magnitude_squared3d(&vector);
if (distance_squared <
communication_player_absolute_range * communication_player_absolute_range)
{
boolean clear_line_of_sight = FALSE;
real distance;
if (test_line_of_sight)
{
struct collision_result collision;
real_vector3d line_of_sight_vector;
boolean blocked;
short cluster_index = object_get(object_get_ultimate_parent(unit_index))->
object.location.cluster_index;
short player_cluster_index =
object_get(object_get_ultimate_parent(player->unit_index))->
object.location.cluster_index;
if (cluster_index != NONE &&
player_cluster_index != NONE &&
!scenario_test_pvs(cluster_index, player_cluster_index))
{
continue;
}
ai_profile.meters[_ai_meter_collisions].accumulator++;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xE91,
global_current_collision_user_depth <
MAXIMUM_COLLISION_USER_STACK_DEPTH);
global_current_collision_users[global_current_collision_user_depth++] =
_collision_user_ai_comms;
vector_from_points3d(
player_head_position,
head_position,
&line_of_sight_vector);
blocked = collision_test_vector(
FLAG(_collision_test_front_facing_surfaces_bit) |
FLAG(_collision_test_back_facing_surfaces_bit) |
FLAG(_collision_test_ignore_two_sided_surfaces_bit) |
FLAG(_collision_test_structure_bit),
&player_position,
&line_of_sight_vector,
NONE,
&collision);
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xE97,
global_current_collision_user_depth > 1);
--global_current_collision_user_depth;
clear_line_of_sight = (boolean)(distance_squared <
communication_player_ideal_range_min *
communication_player_ideal_range_min ||
!blocked);
}
rating = 1.0f;
distance = square_root(distance_squared);
if (distance < communication_player_ideal_range_max)
{
if (distance < communication_player_ideal_range_min)
{
rating = 2.0f;
}
else
{
rating = 1.0f +
(communication_player_ideal_range_max - distance) /
(communication_player_ideal_range_max -
communication_player_ideal_range_min);
}
if (clear_line_of_sight)
{
rating += 0.5f;
}
if (distance > _real_epsilon)
{
real facing;
unit_get_aiming_vector(player->unit_index, &aiming_vector);
facing = dot_product3d(&aiming_vector, &vector) / distance;
if (facing > communication_player_ideal_fov)
{
rating += 0.7f - (1.0f - facing) /
(1.0f - communication_player_ideal_fov) * 0.35f;
}
}
}
if (rating > best_rating)
{
best_rating = rating;
closest_unit_index = player->unit_index;
closest_distance = distance;
}
}
}
}
rating = !any_players ? 1.0f : best_rating;
if (distance_reference)
{
*distance_reference = closest_distance;
}
if (unit_index_reference)
{
*unit_index_reference = closest_unit_index;
}
return rating;
}
void ai_conversation_stop(
short scenario_conversation_index)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
data_iterator_new(&iterator, conversation_data);
while ((conversation = (struct conversation_datum *)
data_iterator_next(&iterator)) != NULL)
{
if (conversation->scenario_conversation_index == scenario_conversation_index)
{
if (ai_print_conversations)
{
struct ai_conversation *definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation);
console_printf(
FALSE,
"%s: told to stop by scripting",
definition->name);
}
ai_conversation_finish(iterator.datum_index, FALSE, FALSE);
}
}
return;
}
void actor_communication_update(
long actor_index)
{
struct actor_datum *actor;
boolean in_combat;
actor = actor_get(actor_index);
if (actor->state.mode >= _actor_mode_alert &&
ai_globals->dialogue_triggers_enabled)
{
in_combat = actor_in_combat(actor_index);
if (actor->control.idle_vocalization_timer == 0 ||
actor->control.idle_vocalization_combat != in_combat)
{
actor_reset_idle_vocalization_timer(actor_index);
}
if (actor->control.idle_vocalization_timer > 0 &&
--actor->control.idle_vocalization_timer == 0)
{
long sound_definition_index = NONE;
long vocalization_type = in_combat != FALSE;
short play_type = unit_test_speech(
actor->meta.unit_index,
_unit_speech_idle,
TRUE,
FALSE,
NULL,
(short *)&vocalization_type,
&sound_definition_index);
if (play_type > 0)
{
struct unit_speech_item speech_item;
csmemset(&speech_item, 0, sizeof(speech_item));
speech_item.vocalization_type = (short)vocalization_type;
speech_item.sound_definition_index = sound_definition_index;
speech_item.priority = _unit_speech_idle;
ai_communication_packet_new(&speech_item.ai);
unit_speak(actor->meta.unit_index, play_type, &speech_item);
}
}
}
return;
}
static void ai_communication_handle_received_looking(
long actor_index,
long prop_index,
struct ai_information_packet *information)
{
struct prop_datum *prop;
short type;
if (information->look_type > 0)
{
prop = prop_get(prop_index);
type = _secondary_look_communicated_direction;
if (information->look_type == _ai_information_look_unit &&
information->look_data.unit.unit_index == prop->unit_index)
{
type = _secondary_look_communicating_prop;
}
switch (information->look_type)
{
case _ai_information_look_unit:
ai_communication_look_secondary_at_unit(
actor_index,
type,
information->look_priority,
information->look_data.unit.unit_index,
NONE);
break;
case _ai_information_look_object:
ai_communication_look_secondary_at_object(
actor_index,
type,
information->look_priority,
information->look_data.object.object_index);
break;
}
}
return;
}
static void ai_communication_update_speech_timers(
long unit_index,
short priority,
short vocalization_type,
short dialogue_type_index,
short reply_table_index)
{
struct unit_datum *unit = unit_get(unit_index);
struct actor_datum *actor;
long time;
long notification_time;
if (unit->unit.actor_index == NONE)
{
actor = NULL;
}
else
{
actor = actor_get(unit->unit.actor_index);
}
time = game_time_get();
notification_time = time +
MAX(0, unit->unit.speech.sound_timer - communication_overlap_time_modifier);
unit->unit.speech.last_speech_finished_time = notification_time;
if (actor)
{
short communication_team;
actor_reset_idle_vocalization_timer(unit->unit.actor_index);
communication_team = actor_communication_team(unit->unit.actor_index);
if (communication_team != NONE)
{
if (priority <= _unit_speech_shout)
{
ai_globals->last_chatter_time[communication_team] = MAX(
ai_globals->last_chatter_time[communication_team],
notification_time);
if (priority >= _unit_speech_talk)
{
ai_globals->last_talk_time[communication_team] = MAX(
ai_globals->last_talk_time[communication_team],
notification_time);
}
if (priority >= _unit_speech_shout)
{
ai_globals->last_shout_time[communication_team] = MAX(
ai_globals->last_shout_time[communication_team],
notification_time);
}
if (ai_debug.print_speech_timers)
{
char const *timer_name = priority >= _unit_speech_talk ? "talk" : "chatter";
char const *team_name =
global_communication_team_names[communication_team * 2];
error(
2,
"%s %s %d/%s: %s %d",
team_name,
unit_get_speech_priority_name(priority),
dialogue_type_index,
dialogue_get_vocalization_name(vocalization_type, TRUE),
timer_name,
notification_time - time);
}
}
if (dialogue_type_index != NONE)
{
struct dialogue_event_status *event;
struct dialogue_usage const *usage;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xC9C,
(dialogue_type_index >= 0) && (dialogue_type_index < global_dialogue_event_count));
usage = &global_dialogue_table[dialogue_type_index];
event = &global_dialogue_events[
dialogue_type_index * NUMBER_OF_AI_COMMUNICATION_TEAMS + communication_team];
event->last_time_spoken = time;
if (game_connection() ||
!ai_debug.communication_timeout_disabled)
{
if (usage->repeat_delay > 0.0f)
{
event->disable_until_time = (long)(
usage->repeat_delay * TICKS_PER_SECOND +
notification_time);
}
}
}
if (reply_table_index != NONE)
{
struct dialogue_event_status *event;
struct reply_usage const *usage;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xCAF,
(reply_table_index >= 0) && (reply_table_index < global_reply_event_count));
usage = &global_reply_table[reply_table_index];
event = &global_reply_events[
reply_table_index * NUMBER_OF_AI_COMMUNICATION_TEAMS + communication_team];
event->last_time_spoken = time;
if (game_connection() ||
!ai_debug.communication_timeout_disabled)
{
if (usage->repeat_delay > 0.0f)
{
event->disable_until_time = (long)(
usage->repeat_delay * TICKS_PER_SECOND +
notification_time);
}
}
}
}
}
return;
}
static boolean ai_conversation_find_participant(
long conversation_index,
short participant_index,
boolean *found_specific_unit_reference,
boolean *try_alternate_reference,
boolean *success_with_better_player_rating_reference,
real *best_distance_reference)
{
struct conversation_datum *conversation =
ai_conversation_driver_get(conversation_index);
struct ai_conversation *conversation_definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
struct scenario_conversation_participant_view *participant =
TAG_BLOCK_GET_ELEMENT(
&conversation_definition->participants,
participant_index,
struct scenario_conversation_participant_view);
short selected_variant_index = NONE;
long selected_actor_index = NONE;
real best_player_distance = REAL_MAX;
boolean better_player_rating_found = FALSE;
boolean participant_found = FALSE;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x127A,
conversation_definition && conversation);
if (participant->selection_type == _ai_conversation_selection_disembodied)
{
selected_variant_index = 0;
participant_found = TRUE;
}
else
{
struct actor_iterator actor_iterator;
struct ai_script_actor_reference_iterator ai_actor_iterator;
real_point3d nearby_unit_positions[MAXIMUM_PARTICIPANTS_PER_CONVERSATION];
short rejection_counts[7];
boolean player_selection = FALSE;
boolean use_specific_object = FALSE;
boolean use_ai_iterator = FALSE;
boolean first_participant;
long candidate_object_index = NONE;
real best_rating = 0.0f;
short possible_actor_count = 0;
short nearby_unit_count;
short slot_index;
csmemset(rejection_counts, 0, sizeof(rejection_counts));
if (participant->selection_type == _ai_conversation_selection_radio ||
participant->selection_type == _ai_conversation_selection_radio_sargeant)
{
player_selection = TRUE;
}
nearby_unit_count = 0;
for (slot_index = 0;
slot_index < conversation_definition->participants.count;
slot_index = (short)(slot_index + 1))
{
long actor_index = conversation->actor_indices[slot_index];
if (actor_index != NONE)
{
struct actor_datum *actor = actor_get(actor_index);
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x12AD,
nearby_unit_count < MAXIMUM_PARTICIPANTS_PER_CONVERSATION);
nearby_unit_positions[nearby_unit_count] =
actor->input.position.body_position;
nearby_unit_count = (short)(nearby_unit_count + 1);
}
}
first_participant = nearby_unit_count == 0;
if (participant->preexisting_object_name_index != NONE)
{
candidate_object_index = object_index_from_name_index(
participant->preexisting_object_name_index);
use_specific_object = TRUE;
}
else if (participant->runtime_ai_index != NONE)
{
ai_index_actor_iterator_new(
participant->runtime_ai_index,
&ai_actor_iterator);
use_ai_iterator = TRUE;
}
else
{
actor_iterator_new(&actor_iterator, TRUE);
}
while (TRUE)
{
real candidate_rating = 0.0f;
real candidate_player_distance = REAL_MAX;
struct unit_datum *player_target_unit = NULL;
real player_rating;
boolean selection_valid = TRUE;
struct actor_datum *candidate_actor;
long candidate_actor_index;
long player_target_unit_index;
short actor_variant_index;
if (use_specific_object)
{
struct unit_datum *candidate_unit = unit_try_and_get(
candidate_object_index);
candidate_actor_index = NONE;
candidate_actor = NULL;
if (candidate_unit && candidate_unit->unit.actor_index != NONE)
{
candidate_actor_index = candidate_unit->unit.actor_index;
candidate_actor = actor_get(candidate_actor_index);
}
candidate_object_index = NONE;
}
else if (use_ai_iterator)
{
candidate_actor = ai_index_actor_iterator_next(&ai_actor_iterator);
candidate_actor_index = ai_actor_iterator.actor_index;
}
else
{
candidate_actor = actor_iterator_next(&actor_iterator);
candidate_actor_index = actor_iterator.index;
}
if (candidate_actor == NULL)
{
break;
}
possible_actor_count = (short)(possible_actor_count + 1);
if (candidate_actor->meta.unit_index == NONE)
{
rejection_counts[0]++;
continue;
}
if (candidate_actor->meta.type != participant->actor_type)
{
rejection_counts[1]++;
continue;
}
for (slot_index = 0;
slot_index < conversation_definition->participants.count;
slot_index = (short)(slot_index + 1))
{
if (candidate_actor_index == conversation->actor_indices[slot_index])
{
break;
}
}
if (slot_index < conversation_definition->participants.count)
{
rejection_counts[2]++;
continue;
}
player_rating = ai_communication_get_player_rating(
candidate_actor->meta.unit_index,
first_participant,
&player_target_unit_index,
&candidate_player_distance);
if (player_target_unit_index == NONE)
{
if (!player_selection)
{
rejection_counts[3]++;
better_player_rating_found = TRUE;
continue;
}
}
else
{
candidate_rating = player_rating;
player_target_unit = unit_get(player_target_unit_index);
}
switch (participant->selection_type)
{
case _ai_conversation_selection_friendly_actor:
case _ai_conversation_selection_radio:
if (player_target_unit &&
game_team_is_enemy(
candidate_actor->meta.team_index,
player_target_unit->object.owner_team_index))
{
selection_valid = FALSE;
}
break;
case _ai_conversation_selection_in_player_vehicle:
if (!player_target_unit ||
player_target_unit->object.parent_object_index == NONE ||
candidate_actor->input.vehicle_index !=
player_target_unit->object.parent_object_index)
{
selection_valid = FALSE;
}
else if (candidate_actor->input.vehicle_gunner)
{
candidate_rating += 1.0f;
}
break;
case _ai_conversation_selection_not_in_vehicle:
if (candidate_actor->input.vehicle_index != NONE)
{
selection_valid = FALSE;
}
break;
case _ai_conversation_selection_sargeant:
case _ai_conversation_selection_radio_sargeant:
if (candidate_actor->meta.unique_leader)
{
candidate_rating += 1.5f;
}
break;
case _ai_conversation_selection_disembodied:
case _ai_conversation_selection_any_actor:
default:
break;
}
if (!selection_valid)
{
rejection_counts[4]++;
continue;
}
if (first_participant &&
!player_selection &&
player_rating < 2.0f &&
conversation_definition->run_to_player_dist == 0.0f)
{
rejection_counts[5]++;
better_player_rating_found = TRUE;
continue;
}
if (nearby_unit_count > 0)
{
real nearest_distance_squared = REAL_MAX;
for (slot_index = 0;
slot_index < nearby_unit_count;
slot_index = (short)(slot_index + 1))
{
real distance_squared = distance_squared3d(
&candidate_actor->input.position.body_position,
&nearby_unit_positions[slot_index]);
if (nearest_distance_squared > distance_squared)
{
nearest_distance_squared = distance_squared;
}
}
if (nearest_distance_squared < 20.25f)
{
candidate_rating += 1.0f -
(square_root(nearest_distance_squared) - 1.5f) *
0.33333334f;
}
}
{
struct unit_datum *candidate_unit = unit_get(
candidate_actor->meta.unit_index);
short change_variant_indices[
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT];
short unit_variant_index = candidate_unit->object.variant_number;
short change_variant_indices_count = 0;
short found_variant_index = NONE;
boolean found_variant = FALSE;
short variant_index;
for (variant_index = 0;
variant_index <
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT;
variant_index = (short)(variant_index + 1))
{
short dialogue_variant =
participant->dialogue_variants[variant_index];
if (dialogue_variant == NONE)
{
continue;
}
if (dialogue_variant == unit_variant_index)
{
found_variant_index = variant_index;
found_variant = TRUE;
break;
}
if (dialogue_variant == 0)
{
found_variant_index = variant_index;
found_variant = TRUE;
}
else if (unit_variant_index < 100 && dialogue_variant < 100)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x13AF,
change_variant_indices_count <
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT);
change_variant_indices[change_variant_indices_count++] =
variant_index;
}
}
if (found_variant)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x13B8,
found_variant_index != NONE);
actor_variant_index = found_variant_index;
candidate_rating += 0.7f;
}
else if (change_variant_indices_count > 0)
{
if (change_variant_indices_count == 1)
{
actor_variant_index = change_variant_indices[0];
}
else
{
actor_variant_index = change_variant_indices[
seed_random_range(
get_global_random_seed_address(),
0,
change_variant_indices_count)];
}
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x13C7,
(actor_variant_index >= 0) &&
(actor_variant_index <
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT));
}
else
{
rejection_counts[6]++;
continue;
}
}
if (candidate_rating > best_rating)
{
selected_actor_index = candidate_actor_index;
best_rating = candidate_rating;
best_player_distance = candidate_player_distance;
selected_variant_index = actor_variant_index;
participant_found = TRUE;
}
}
if (!participant_found && ai_print_conversations)
{
char ai_name[256];
char const *object_name = "none";
short name_index = participant->preexisting_object_name_index != NONE ?
participant->preexisting_object_name_index :
participant->new_attach_object_name_index;
if (VALID_INDEX(name_index, global_scenario_get()->object_names.count))
{
object_name = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->object_names,
name_index,
struct scenario_object_name)->name;
}
if (use_specific_object)
{
csstrcpy(ai_name, "<specific unit>");
}
else if (use_ai_iterator)
{
ai_index_to_string(
participant->runtime_ai_index,
global_scenario_get(),
ai_name,
sizeof(ai_name));
}
else
{
csstrcpy(ai_name, "<everyone>");
}
console_printf(
FALSE,
"%s: didn't find %d/%s in %s (%d possible actors)",
conversation_definition->name,
participant_index,
object_name,
ai_name,
possible_actor_count);
if (possible_actor_count > 0)
{
char reason_string[512];
char const *reason_names[7] =
{
"swarm",
"wrong-type",
"already-conversing",
"nowhere-near-player",
"selection",
"not-near-player",
"no-dialogue-match",
};
csstrcpy(reason_string, " reasons: ");
for (slot_index = 0; slot_index < 7; slot_index++)
{
if (rejection_counts[slot_index] > 0)
{
sprintf(
&reason_string[csstrlen(reason_string)],
"%s(%d) ",
reason_names[slot_index],
rejection_counts[slot_index]);
}
}
console_printf(FALSE, reason_string);
}
}
}
if (participant_found)
{
SET_FLAG(conversation->participant_bitmask, participant_index, TRUE);
conversation->actor_indices[participant_index] = selected_actor_index;
conversation->dialogue_indices[participant_index] = selected_variant_index;
if (found_specific_unit_reference && selected_actor_index != NONE)
{
*found_specific_unit_reference = TRUE;
}
}
else if (TEST_FLAG(
participant->flags,
_ai_conversation_participant_has_alternate_bit) &&
try_alternate_reference)
{
*try_alternate_reference = TRUE;
}
if (better_player_rating_found &&
success_with_better_player_rating_reference)
{
*success_with_better_player_rating_reference = TRUE;
}
if (best_distance_reference &&
*best_distance_reference > best_player_distance)
{
*best_distance_reference = best_player_distance;
}
return participant_found;
}
void ai_communication_started(
long unit_index,
short priority,
short vocalization_type,
struct ai_information_packet *information)
{
switch (priority)
{
case _unit_speech_none:
case _unit_speech_idle:
case _unit_speech_pain:
case _unit_speech_involuntary:
case _unit_speech_death:
break;
default:
if (ai_debug.print_vocalizations)
{
char string[1024];
char name[256];
struct unit_datum *unit = unit_get(unit_index);
if (unit->unit.actor_index != NONE)
{
struct actor_datum *actor = actor_get(unit->unit.actor_index);
if (actor->meta.encounter_index == NONE)
{
csstrcpy(name, "<no encounter>");
}
else
{
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);
sprintf(
name,
"%s/%s",
encounter->name,
TAG_BLOCK_GET_ELEMENT(
&encounter->squads,
actor->meta.squad_index,
struct squad_definition)->name);
}
sprintf(
string,
"%s/%s: ",
name,
actor_type_get_name(actor->meta.type));
}
else if (unit->object.name_index != NONE)
{
sprintf(
string,
"%s: ",
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->object_names,
unit->object.name_index,
struct scenario_object_name)->name);
}
else if (unit->unit.player_index != NONE)
{
sprintf(
string,
"player %d: ",
DATUM_INDEX_TO_ABSOLUTE_INDEX(unit->unit.player_index));
}
else
{
sprintf(
string,
"unit %04X: ",
DATUM_INDEX_TO_ABSOLUTE_INDEX(unit_index));
}
sprintf(
temporary,
"%s %s",
unit_get_speech_priority_name(priority),
vocalization_type == NONE ?
"non-voc" :
dialogue_get_vocalization_name(vocalization_type, FALSE));
csstrcat(string, temporary);
if (information &&
information->dialogue_type_index != NONE)
{
sprintf(
temporary,
" [%d/%s]",
information->dialogue_type_index,
ai_communication_get_type_name(
global_dialogue_table[information->dialogue_type_index].communication_type));
csstrcat(string, temporary);
}
console_printf(FALSE, string);
}
if (ai_debug.print_speech)
{
struct unit_datum *unit = unit_get(unit_index);
if (unit->unit.speech.current.priority > _unit_speech_none)
{
char actor_string[512];
char speech_string[512];
error(
2,
"%s: %s",
ai_debug_describe_actor(
unit->unit.actor_index,
unit_index,
FALSE,
actor_string,
sizeof(actor_string)),
unit_describe_speech(
unit_index,
TRUE,
sizeof(speech_string),
speech_string));
}
}
if (!information->updated_dialogue_timers)
{
ai_communication_update_speech_timers(
unit_index,
priority,
vocalization_type,
information->dialogue_type_index,
NONE);
}
break;
}
return;
}
void ai_communication_notify(
long unit_index,
short priority,
short vocalization_type,
struct ai_information_packet *ai_information)
{
long sound_volume;
struct unit_datum *unit;
struct location const *sound_location;
short speaker_team;
long parent_object_index;
real_point3d speaker_head;
struct actor_iterator actors;
struct actor_datum *actor;
long prop_index;
struct prop_datum *prop;
struct actor_position_data sense_position;
switch (ai_information->information_type)
{
case _ai_information_allegiance:
ai_handle_allegiance_broken_notification(
ai_information->information_data.allegiance.team1_index,
ai_information->information_data.allegiance.team2_index,
ai_information->information_data.allegiance.broken);
break;
}
if (ai_information->information_type != 0 ||
ai_information->look_priority > 0)
{
unit = unit_get(unit_index);
sound_location = &unit->object.location;
speaker_team = unit->object.owner_team_index;
sound_volume = _ai_sound_volume_medium;
unit_get_head_position(unit_index, &speaker_head);
if (ai_information->dialogue_type_index != NONE)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x894,
(ai_information->dialogue_type_index >= 0) && (ai_information->dialogue_type_index < global_dialogue_event_count));
if (global_dialogue_table[ai_information->dialogue_type_index].communication_priority >=
_ai_communication_priority_communicate)
{
sound_volume = _ai_sound_volume_shout;
}
}
if (unit->object.parent_object_index != NONE)
{
parent_object_index = object_get_ultimate_parent(unit_index);
sound_location = &object_get(parent_object_index)->object.location;
}
actor_iterator_new(&actors, TRUE);
actor = actor_iterator_next(&actors);
while (actor)
{
if (actor->meta.unit_index != unit_index &&
!game_team_is_enemy(actor->meta.team_index, speaker_team) &&
!(distance_squared3d(
&actor->input.position.head_position,
&speaker_head) > 900.0f))
{
prop_index = prop_get_base_by_unit_index(
actors.index,
unit_index,
TRUE,
TRUE);
if (prop_index != NONE)
{
prop = prop_get(prop_index);
actor_perception_find_sense_position(
actors.index,
&speaker_head,
prop_index,
&sense_position);
if (actor_audibility_at_point(
actors.index,
&sense_position,
&speaker_head,
sound_location,
(short)sound_volume,
1.0f,
prop->line_of_sight) >= _ai_sound_volume_loud)
{
actor_handle_communication(
actors.index,
prop_index,
ai_information);
ai_communication_handle_received_looking(
actors.index,
prop_index,
ai_information);
}
}
}
actor = actor_iterator_next(&actors);
}
}
return;
}
static boolean ai_conversation_begin(
long conversation_index,
boolean *continue_trying)
{
struct conversation_datum *conversation =
ai_conversation_driver_get(conversation_index);
struct ai_conversation *definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
unsigned long better_player_rating_mask = 0;
boolean try_alternate = FALSE;
boolean found_alternate = FALSE;
boolean found_specific_unit = FALSE;
boolean can_begin = TRUE;
boolean keep_trying = FALSE;
real best_distance = REAL_MAX;
boolean participant_not_ready;
boolean participant_missing;
short index;
conversation->participant_bitmask = 0;
csmemset(conversation->actor_indices, NONE, sizeof(conversation->actor_indices));
csmemset(conversation->dialogue_indices, NONE, sizeof(conversation->dialogue_indices));
for (index = 0;
index < definition->participants.count;
index = (short)(index + 1))
{
struct scenario_conversation_participant_view *participant =
TAG_BLOCK_GET_ELEMENT(
&definition->participants,
index,
struct scenario_conversation_participant_view);
if (!TEST_FLAG(
participant->flags,
_ai_conversation_participant_is_alternate_bit))
{
boolean better_player_rating = FALSE;
ai_conversation_find_participant(
conversation_index,
index,
&found_specific_unit,
&try_alternate,
&better_player_rating,
&best_distance);
SET_FLAG(
better_player_rating_mask,
index,
better_player_rating);
}
}
if (try_alternate)
{
for (index = 0;
index < definition->participants.count;
index = (short)(index + 1))
{
struct scenario_conversation_participant_view *participant =
TAG_BLOCK_GET_ELEMENT(
&definition->participants,
index,
struct scenario_conversation_participant_view);
if (!TEST_FLAG(conversation->participant_bitmask, index) &&
TEST_FLAG(
participant->flags,
_ai_conversation_participant_is_alternate_bit))
{
boolean better_player_rating = FALSE;
if (ai_conversation_find_participant(
conversation_index,
index,
&found_specific_unit,
NULL,
&better_player_rating,
&best_distance))
{
found_alternate = TRUE;
}
else
{
SET_FLAG(
better_player_rating_mask,
index,
better_player_rating);
}
}
}
}
participant_not_ready = FALSE;
participant_missing = FALSE;
for (index = 0;
index < definition->participants.count;
index = (short)(index + 1))
{
struct scenario_conversation_participant_view *participant =
TAG_BLOCK_GET_ELEMENT(
&definition->participants,
index,
struct scenario_conversation_participant_view);
if (!TEST_FLAG(
participant->flags,
_ai_conversation_participant_optional_bit) &&
!TEST_FLAG(conversation->participant_bitmask, index) &&
(!TEST_FLAG(
participant->flags,
_ai_conversation_participant_has_alternate_bit) ||
!found_alternate) &&
(!TEST_FLAG(
participant->flags,
_ai_conversation_participant_is_alternate_bit) ||
try_alternate))
{
if (TEST_FLAG(better_player_rating_mask, index))
{
participant_not_ready = TRUE;
}
else
{
participant_missing = TRUE;
}
if (ai_print_conversations)
{
char const *object_name = "none";
short name_index = participant->preexisting_object_name_index != NONE ?
participant->preexisting_object_name_index :
participant->new_attach_object_name_index;
if (VALID_INDEX(name_index, global_scenario_get()->object_names.count))
{
object_name = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->object_names,
name_index,
struct scenario_object_name)->name;
}
if (TEST_FLAG(better_player_rating_mask, index))
{
console_printf(
FALSE,
"%s: found participant %d/%s but not ready to talk yet",
definition->name,
index,
object_name);
}
else
{
console_printf(
FALSE,
"%s: could not find participant %d/%s",
definition->name,
index,
object_name);
}
}
break;
}
}
if (participant_missing)
{
can_begin = FALSE;
}
else if (participant_not_ready)
{
keep_trying = TRUE;
can_begin = FALSE;
}
if (can_begin &&
TEST_FLAG(definition->flags, _ai_conversation_keep_trying_to_play_bit) &&
definition->trigger_distance > 0.0f &&
found_specific_unit &&
best_distance > definition->trigger_distance)
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: participants currently outside trigger-dist %.1f > %.1f, must wait",
definition->name,
best_distance,
definition->trigger_distance);
}
keep_trying = TRUE;
can_begin = FALSE;
}
conversation->triggering_player_unit_index = NONE;
if (can_begin &&
TEST_FLAG(definition->flags, _ai_conversation_player_must_be_visible_bit))
{
if (!found_specific_unit)
{
can_begin = FALSE;
}
else
{
struct data_iterator player_iterator;
struct player_datum *player;
real nearest_player_distance = REAL_MAX;
data_iterator_new(&player_iterator, player_data);
while ((player = (struct player_datum *)data_iterator_next(
&player_iterator)) != NULL)
{
if (player->unit_index != NONE)
{
real nearest_prop_distance = REAL_MAX;
for (index = 0;
index < definition->participants.count;
index = (short)(index + 1))
{
long actor_index = conversation->actor_indices[index];
if (actor_index != NONE)
{
long prop_index = prop_get_active_by_unit_index(
actor_index,
player->unit_index);
if (prop_index != NONE)
{
struct prop_datum *prop = prop_get(prop_index);
if (prop->state >= _prop_state_becoming_unacknowledged &&
prop->state <= _prop_state_acknowledged &&
nearest_prop_distance > prop->distance)
{
nearest_prop_distance = prop->distance;
}
}
}
}
if (nearest_prop_distance < nearest_player_distance)
{
conversation->triggering_player_unit_index =
player->unit_index;
nearest_player_distance = nearest_prop_distance;
}
}
}
if (conversation->triggering_player_unit_index == NONE)
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: cannot start, nobody can see player",
definition->name);
}
can_begin = FALSE;
if (TEST_FLAG(
definition->flags,
_ai_conversation_keep_trying_to_play_bit))
{
keep_trying = TRUE;
}
}
}
}
if (can_begin &&
TEST_FLAG(
definition->flags,
_ai_conversation_player_must_be_looking_at_bit) &&
found_specific_unit)
{
struct data_iterator player_iterator;
struct player_datum *player;
boolean player_can_see_participant = FALSE;
data_iterator_new(&player_iterator, player_data);
while ((player = (struct player_datum *)data_iterator_next(
&player_iterator)) != NULL &&
!player_can_see_participant)
{
if (player->unit_index != NONE)
{
for (index = 0;
index < definition->participants.count;
index = (short)(index + 1))
{
long actor_index = conversation->actor_indices[index];
if (actor_index != NONE)
{
struct actor_datum *actor = actor_get(actor_index);
if (unit_can_see_point(
player->unit_index,
&actor->input.position.head_position,
0.52359879f))
{
player_can_see_participant = TRUE;
break;
}
}
}
}
}
if (!player_can_see_participant)
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: cannot start, players are not looking at us",
definition->name);
}
can_begin = FALSE;
if (TEST_FLAG(
definition->flags,
_ai_conversation_keep_trying_to_play_bit))
{
keep_trying = TRUE;
}
}
}
if (can_begin)
{
for (index = 0;
index < definition->participants.count;
index = (short)(index + 1))
{
if (TEST_FLAG(conversation->participant_bitmask, index) &&
conversation->actor_indices[index] != NONE)
{
struct scenario_conversation_participant_view *participant =
TAG_BLOCK_GET_ELEMENT(
&definition->participants,
index,
struct scenario_conversation_participant_view);
struct actor_datum *actor = actor_get(conversation->actor_indices[index]);
struct unit_datum *unit = unit_get(actor->meta.unit_index);
short attach_name_index = participant->new_attach_object_name_index;
short dialogue_variant;
if (attach_name_index != NONE)
{
object_set_object_index_for_name_index(
attach_name_index,
actor->meta.unit_index);
}
if (TEST_FLAG(
definition->flags,
_ai_conversation_stop_other_actions_bit))
{
struct action_state_data action_data;
if (action_converse_setup(
conversation->actor_indices[index],
conversation_index,
&action_data.converse))
{
actor_action_change(
conversation->actor_indices[index],
_actor_action_converse,
&action_data);
}
}
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x1216,
(conversation->dialogue_indices[index] >= 0) &&
(conversation->dialogue_indices[index] <
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT));
dialogue_variant = participant->dialogue_variants[
conversation->dialogue_indices[index]];
if (unit->object.variant_number != dialogue_variant)
{
unit->object.variant_number = dialogue_variant;
SET_FLAG(
unit->unit.flags,
_unit_must_set_up_dialogue_bit,
FALSE);
}
}
}
conversation->begun = TRUE;
}
else
{
*continue_trying = keep_trying &&
TEST_FLAG(
definition->flags,
_ai_conversation_keep_trying_to_play_bit);
}
return can_begin;
}
static long ai_communication_find_actor_to_reply_to_player(
long unit_index,
long target_unit_index,
short vocalization_type,
short damage_category,
real *reply_rating_reference)
{
char debug_string[1024];
long actor_index = NONE;
real reply_rating = 1.0f;
if (ai_globals->dialogue_triggers_enabled && vocalization_type != NONE)
{
struct reply_usage const *reply;
short reply_table_index;
boolean communication_not_focused = ai_debug.communication_focus_enable;
if (ai_debug.print_communication)
{
sprintf(
debug_string,
"PLAYER-REPLY %s ",
dialogue_get_vocalization_name(vocalization_type, TRUE));
}
if (ai_debug.communication_focus_enable &&
BIT_VECTOR_TEST_FLAG(
ai_debug.communication_focus_vector,
vocalization_type))
{
communication_not_focused = FALSE;
}
reply = global_reply_table;
reply_table_index = 0;
do
{
if (reply->original_vocalization_type == vocalization_type &&
(reply->original_damage_category == NONE ||
reply->original_damage_category == damage_category))
{
short speech_priority =
communication_speech_priorities[reply->communication_priority];
if (ai_debug.communication_focus_enable &&
BIT_VECTOR_TEST_FLAG(
ai_debug.communication_focus_vector,
reply->vocalization_type))
{
communication_not_focused = FALSE;
}
if (sound_scripted_dialog_is_playing() &&
!TEST_FLAG(reply->flags, _dialogue_usage_lookup_bit))
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "[scripted-override] ");
}
}
else if (reply->player_chance > 0.0f)
{
real random_value = real_seed_random(
get_global_random_seed_address());
if ((game_connection() == _game_connection_local &&
ai_debug.communication_random_disabled) ||
random_value < reply->player_chance)
{
switch (reply->protagonist_type)
{
case _comm_protagonist_friend:
{
struct unit_datum *unit = unit_get(unit_index);
actor_index = ai_communication_find_global_actor_to_talk(
unit->object.owner_team_index,
_find_actor_mode_friend,
unit_index,
NONE,
9.0f,
NONE,
reply->communication_priority,
speech_priority,
reply->vocalization_type,
reply->animation_type,
0);
}
break;
case _comm_protagonist_target:
{
struct unit_datum *target_unit = unit_try_and_get(
target_unit_index);
if (target_unit)
{
actor_index = target_unit->unit.actor_index;
}
}
break;
case _comm_protagonist_enemy:
{
struct unit_datum *unit = unit_get(unit_index);
actor_index = ai_communication_find_global_actor_to_talk(
unit->object.owner_team_index,
_find_actor_mode_enemy,
unit_index,
NONE,
9.0f,
NONE,
reply->communication_priority,
speech_priority,
reply->vocalization_type,
reply->animation_type,
0);
}
break;
}
if (actor_index == NONE)
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s nobody] ",
dialogue_get_vocalization_name(
reply->vocalization_type,
TRUE)));
}
}
else
{
short communication_team = actor_communication_team(actor_index);
if (communication_team != NONE)
{
struct dialogue_event_status *event = &global_reply_events[
reply_table_index * NUMBER_OF_AI_COMMUNICATION_TEAMS +
communication_team];
long time = game_time_get();
if (event->last_time_spoken != NONE)
{
reply_rating =
(real)(time - event->last_time_spoken) * 0.0011111111f;
if (reply_rating < 0.0f)
{
reply_rating = 0.0f;
}
else if (reply_rating > 1.0f)
{
reply_rating = 1.0f;
}
}
if ((game_connection() != _game_connection_local ||
!ai_debug.communication_timeout_disabled) &&
event->disable_until_time != NONE)
{
long disable_time = event->disable_until_time - time;
if (disable_time > 0)
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s %s-d-dis/%d] ",
dialogue_get_vocalization_name(
reply->vocalization_type,
TRUE),
global_communication_team_names[
communication_team * 2 + 1],
disable_time));
}
actor_index = NONE;
}
}
}
if (actor_index != NONE && ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s found-actor] ",
dialogue_get_vocalization_name(
reply->vocalization_type,
TRUE)));
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s rand%.2f>%.2f] ",
dialogue_get_vocalization_name(
reply->vocalization_type,
TRUE),
random_value,
reply->player_chance));
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s 0-player-chance] ",
dialogue_get_vocalization_name(
reply->vocalization_type,
TRUE)));
}
}
if (actor_index != NONE)
{
break;
}
}
reply++;
reply_table_index++;
}
while (reply->original_vocalization_type != NONE);
if (ai_debug.print_communication &&
!communication_not_focused)
{
error(2, debug_string);
}
}
if (reply_rating_reference)
{
*reply_rating_reference = reply_rating;
}
return actor_index;
}
void ai_communication_finished(
long unit_index,
short priority,
short vocalization_type,
boolean reply_to_player,
long preselected_reply_actor_index,
struct ai_information_packet *information)
{
if (ai_globals->dialogue_triggers_enabled && vocalization_type != NONE)
{
char consider_string[512];
char debug_string[1024];
struct reply_usage const *reply;
boolean communication_not_focused = FALSE;
boolean any_replies = FALSE;
short reply_table_index;
if (ai_debug.print_communication)
{
sprintf(
debug_string,
"REPLY %s: ",
dialogue_get_vocalization_name(vocalization_type, FALSE));
}
if (ai_debug.communication_focus_enable)
{
communication_not_focused = !BIT_VECTOR_TEST_FLAG(
ai_debug.communication_focus_vector,
vocalization_type);
}
for (reply = global_reply_table, reply_table_index = 0;
reply->original_vocalization_type != NONE;
reply++, reply_table_index++)
{
if (reply->original_vocalization_type == vocalization_type)
{
struct unit_datum *unit = unit_get(unit_index);
struct actor_datum *speaker_actor = unit->unit.actor_index == NONE ?
NULL :
actor_get(unit->unit.actor_index);
long reply_unit_index = NONE;
short speech_priority =
communication_speech_priorities[reply->communication_priority];
any_replies = TRUE;
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"%s:",
dialogue_get_vocalization_name(reply->vocalization_type, FALSE)));
}
if (ai_debug.communication_focus_enable &&
BIT_VECTOR_TEST_FLAG(
ai_debug.communication_focus_vector,
reply->vocalization_type))
{
communication_not_focused = FALSE;
}
if (reply->original_damage_category != NONE &&
reply->original_damage_category != information->damage_category)
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "wrong-dmg ");
}
}
else if (sound_scripted_dialog_is_playing() &&
!TEST_FLAG(reply->flags, _dialogue_usage_lookup_bit))
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "override-scripted ");
}
}
else
{
if (preselected_reply_actor_index != NONE)
{
reply_unit_index =
actor_get(preselected_reply_actor_index)->meta.unit_index;
}
else
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x9E5,
!reply_to_player);
switch (reply->protagonist_type)
{
case _comm_protagonist_target:
{
struct unit_datum *target_unit = unit_try_and_get(
information->target_unit_index);
if (target_unit)
{
reply_unit_index = information->target_unit_index;
}
}
break;
case _comm_protagonist_friend:
{
long reply_actor_index;
if (speaker_actor && speaker_actor->meta.encounter_index != NONE)
{
reply_actor_index = ai_communication_find_specific_actor_to_talk(
DATUM_INDEX_TO_ABSOLUTE_INDEX(speaker_actor->meta.encounter_index),
unit_index,
NONE,
9.0f,
NONE,
reply->communication_priority,
speech_priority,
reply->vocalization_type,
reply->animation_type,
0);
}
else
{
reply_actor_index = ai_communication_find_global_actor_to_talk(
unit->object.owner_team_index,
_find_actor_mode_friend,
unit_index,
NONE,
9.0f,
NONE,
reply->communication_priority,
speech_priority,
reply->vocalization_type,
reply->animation_type,
0);
}
if (reply_actor_index != NONE)
{
reply_unit_index = actor_get(reply_actor_index)->meta.unit_index;
}
}
break;
case _comm_protagonist_enemy:
{
long reply_actor_index = ai_communication_find_global_actor_to_talk(
unit->object.owner_team_index,
_find_actor_mode_enemy,
unit_index,
NONE,
9.0f,
NONE,
reply->communication_priority,
speech_priority,
reply->vocalization_type,
reply->animation_type,
0);
if (reply_actor_index != NONE)
{
reply_unit_index = actor_get(reply_actor_index)->meta.unit_index;
}
}
break;
}
}
if (reply_unit_index != NONE)
{
struct unit_datum *reply_unit = unit_get(reply_unit_index);
if (reply_unit->unit.player_index == NONE)
{
boolean play_reply = reply_to_player;
if (!play_reply)
{
if (reply->chance > 0.0f)
{
real random_value = real_seed_random(
get_global_random_seed_address());
if ((game_connection() == _game_connection_local &&
ai_debug.communication_random_disabled) ||
random_value < reply->chance)
{
play_reply = TRUE;
}
else
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"rand %.2f>%.2f ",
random_value,
reply->chance));
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "0-chance ");
}
}
}
if (play_reply)
{
if (!reply->reply_filter ||
reply->reply_filter(
unit_index,
information,
reply_unit->unit.actor_index))
{
short reply_vocalization_type = reply->vocalization_type;
long sound_definition_index = NONE;
real weight = 1.0f;
short delay_ticks = (short)(long)(reply->delay_time * TICKS_PER_SECOND);
short play_type = ai_communication_consider_speech(
reply_unit_index,
reply->communication_priority,
speech_priority,
delay_ticks,
FALSE,
FALSE,
&reply_vocalization_type,
&weight,
&sound_definition_index,
consider_string);
if (play_type > _unit_play_speech_none)
{
struct unit_speech_item speech;
speech.priority = speech_priority;
speech.vocalization_type = reply_vocalization_type;
speech.sound_definition_index = sound_definition_index;
speech.delay_time = delay_ticks;
speech.ai_notification_delay = (short)(long)(
communication_notification_delays[reply->communication_priority] *
TICKS_PER_SECOND);
speech.pause_time = 24;
speech.ai.target_unit_index = unit_index;
speech.ai.communication_type = NONE;
speech.ai.dialogue_type_index = NONE;
speech.ai.damage_category = NONE;
speech.ai.updated_dialogue_timers = TRUE;
speech.ai.look_priority = 0;
speech.ai.look_type = _ai_information_none;
speech.ai.information_type = _ai_information_none;
csmemset(&speech.ai.information_data, 0, sizeof(speech.ai.information_data));
unit_speak(reply_unit_index, play_type, &speech);
ai_communication_update_speech_timers(
reply_unit_index,
speech_priority,
reply_vocalization_type,
NONE,
reply_table_index);
ai_communication_look_secondary_at_unit(
reply_unit->unit.actor_index,
_secondary_look_communicating_prop,
communication_protagonist_default_look_priorities[
reply->communication_priority],
unit_index,
NONE);
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
strupr(csprintf(
temporary,
">>%s<<",
dialogue_get_vocalization_name(speech.vocalization_type, TRUE))));
}
break;
}
else
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"u-%s-%s ",
weight > 0.0f ? "dis" : "n/a",
consider_string));
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "filter ");
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "rand-failed ");
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "playercant ");
}
}
}
else
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "nobody ");
}
}
}
}
}
if (any_replies &&
ai_debug.print_communication &&
!communication_not_focused)
{
error(2, debug_string);
}
}
return;
}
void ai_conversation_actor_deleted(
long actor_index)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
struct ai_conversation *definition;
short participant_index;
data_iterator_new(&iterator, conversation_data);
conversation = (struct conversation_datum *)
data_iterator_next(&iterator);
while (conversation != NULL)
{
definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
participant_index = 0;
while ((long)participant_index < definition->participants.count)
{
if (conversation->actor_indices[participant_index] == actor_index)
{
if ((definition->flags & FLAG(
_ai_conversation_stop_if_anyone_dies_bit)) != 0)
{
ai_conversation_finish(
iterator.datum_index,
FALSE,
FALSE);
break;
}
conversation->participant_bitmask &= ~FLAG(participant_index);
conversation->actor_indices[participant_index] = NONE;
if (conversation->line_participant_index == participant_index)
{
conversation->line_advance = TRUE;
}
}
participant_index = (short)(participant_index + 1);
}
conversation = (struct conversation_datum *)
data_iterator_next(&iterator);
}
return;
}
void ai_conversation_unit_died(
long unit_index,
boolean deleted)
{
struct data_iterator iterator;
short participant_index;
struct conversation_datum *conversation;
struct ai_conversation *definition;
struct actor_datum *actor;
boolean referenced;
data_iterator_new(&iterator, conversation_data);
conversation = (struct conversation_datum *)
data_iterator_next(&iterator);
if (conversation == NULL)
{
return;
}
while (TRUE)
{
definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
referenced = FALSE;
if (conversation->line_unit_index == unit_index)
{
referenced = TRUE;
conversation->line_advance = TRUE;
conversation->line_unit_index = NONE;
}
if (conversation->line_address_unit_index == unit_index)
{
referenced = TRUE;
conversation->line_address_unit_index = NONE;
}
if (conversation->triggering_player_unit_index == unit_index)
{
referenced = TRUE;
conversation->triggering_player_unit_index = NONE;
}
if (deleted ||
(definition->flags & FLAG(
_ai_conversation_stop_if_anyone_dies_bit)) != 0)
{
participant_index = 0;
if (participant_index < definition->participants.count)
{
do
{
if ((conversation->participant_bitmask &
FLAG(participant_index)) != 0 &&
conversation->actor_indices[participant_index] != NONE)
{
actor = actor_get(
conversation->actor_indices[participant_index]);
if (actor->meta.unit_index == unit_index)
{
referenced = TRUE;
}
if (deleted)
{
if (actor->state.action == _actor_action_converse &&
actor->state.action_data.converse.run_to_unit_index ==
unit_index)
{
actor->state.action_data.converse.run_to_unit_index = NONE;
}
if (actor->external_orders.conversation_attention_unit_index ==
unit_index)
{
actor->external_orders.conversation_attention_unit_index = NONE;
}
}
}
participant_index = (short)(participant_index + 1);
}
while (participant_index < definition->participants.count);
}
if (referenced)
{
break;
}
}
conversation = (struct conversation_datum *)
data_iterator_next(&iterator);
if (conversation == NULL)
{
return;
}
}
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: unit died, aborting",
definition->name);
}
ai_conversation_finish(iterator.datum_index, FALSE, FALSE);
return;
}
static long ai_conversation_new(
short scenario_conversation_index,
boolean scripted)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
long conversation_index = datum_new(conversation_data);
if (conversation_index == NONE && scripted)
{
boolean overwrite_scripted = TRUE;
long overwrite_creation_time = LONG_MAX;
long overwrite_conversation_index = NONE;
data_iterator_new(&iterator, conversation_data);
while ((conversation = (struct conversation_datum *)
data_iterator_next(&iterator)) != NULL)
{
if (conversation->scripted < overwrite_scripted ||
conversation->creation_time < overwrite_creation_time)
{
overwrite_creation_time = conversation->creation_time;
overwrite_conversation_index = iterator.datum_index;
overwrite_scripted = conversation->scripted;
}
}
if (overwrite_conversation_index != NONE)
{
if (ai_print_conversations)
{
struct ai_conversation *definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation);
console_printf(
FALSE,
"%s: this conversation is already running or trying to run, overwrite it",
definition->name);
}
ai_conversation_finish(
overwrite_conversation_index,
FALSE,
FALSE);
conversation_index = datum_new_at_index(
conversation_data,
overwrite_conversation_index);
}
}
if (conversation_index != NONE)
{
conversation = (struct conversation_datum *)datum_get(
conversation_data,
conversation_index);
conversation->scenario_conversation_index = scenario_conversation_index;
conversation->line_index = NONE;
conversation->scripted = scripted;
conversation->creation_time = game_time_get();
}
return conversation_index;
}
static boolean ai_conversation_line_begin(
long conversation_index)
{
struct conversation_datum *conversation;
struct ai_conversation *definition;
struct scenario_conversation_participant_view *participant;
struct scenario_conversation_line_view *line;
struct actor_datum *actor;
short participant_index;
long speaker_actor_index;
boolean result;
conversation = ai_conversation_driver_get(conversation_index);
definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
line = TAG_BLOCK_GET_ELEMENT(
&definition->lines,
conversation->line_index,
struct scenario_conversation_line_view);
participant_index = line->participant_index;
result = FALSE;
if (participant_index >= 0 &&
participant_index < definition->participants.count &&
(conversation->participant_bitmask & FLAG(participant_index)) != 0)
{
participant = TAG_BLOCK_GET_ELEMENT(
&definition->participants,
participant_index,
struct scenario_conversation_participant_view);
speaker_actor_index =
conversation->actor_indices[line->participant_index];
conversation->line_participant_index = line->participant_index;
if (speaker_actor_index == NONE)
{
conversation->line_actor_index = NONE;
conversation->line_unit_index = NONE;
conversation->line_address_unit_index = NONE;
conversation->line_unspatialized = TRUE;
}
else
{
actor = actor_get(speaker_actor_index);
conversation->line_actor_index = speaker_actor_index;
conversation->line_unit_index = actor->meta.unit_index;
conversation->line_address_unit_index = NONE;
switch (line->address_type)
{
case _ai_conversation_address_player:
conversation->line_address_unit_index =
conversation->triggering_player_unit_index;
break;
case _ai_conversation_address_participant:
if (line->address_participant_index >= 0 &&
line->address_participant_index < definition->participants.count &&
conversation->actor_indices[line->address_participant_index] != NONE)
{
actor = actor_get(
conversation->actor_indices[line->address_participant_index]);
conversation->line_address_unit_index = actor->meta.unit_index;
}
break;
}
conversation->line_unspatialized =
participant->selection_type == _ai_conversation_selection_radio ||
participant->selection_type ==
_ai_conversation_selection_radio_sargeant;
}
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x146B,
(conversation->dialogue_indices[line->participant_index] >= 0) &&
(conversation->dialogue_indices[line->participant_index] <
MAXIMUM_DIALOGUE_VARIANTS_PER_CONVERSATION_PARTICIPANT));
conversation->line_sound_index = line->dialogue[
conversation->dialogue_indices[line->participant_index]].index;
conversation->line_delay_timer = (short)(long)(line->delay_time * 30.0f);
conversation->line_flags = line->flags;
conversation->line_advance = FALSE;
conversation->line_finished = FALSE;
conversation->line_spoken = FALSE;
result = TRUE;
}
return result;
}
static boolean ai_conversation_line_perform(
long conversation_index)
{
struct conversation_datum *conversation;
struct ai_conversation *definition;
struct actor_datum *actor;
struct unit_datum *unit;
long sound_definition_index;
long speaking_unit_index;
long speech_sound_definition_index;
long participant_actor_index;
short vocalization_type;
short play_type;
short participant_index;
short flags;
short delay_timer;
boolean busy;
conversation = ai_conversation_driver_get(conversation_index);
definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
if (!conversation->line_advance)
{
if (!conversation->line_spoken)
{
busy = FALSE;
sound_definition_index = conversation->line_sound_index;
if (sound_definition_index != NONE)
{
flags = conversation->line_flags;
if ((flags &
(FLAG(_ai_conversation_line_wait_until_speaker_nearby_bit) |
FLAG(_ai_conversation_line_wait_until_everyone_nearby_bit))) != 0)
{
participant_index = 0;
while ((long)participant_index < definition->participants.count)
{
participant_actor_index =
conversation->actor_indices[participant_index];
if (participant_actor_index != NONE)
{
actor = actor_get(participant_actor_index);
flags = conversation->line_flags;
if (((flags & FLAG(
_ai_conversation_line_wait_until_everyone_nearby_bit)) != 0 ||
((flags & FLAG(
_ai_conversation_line_wait_until_speaker_nearby_bit)) != 0 &&
participant_actor_index == conversation->line_actor_index)) &&
actor->state.action == _actor_action_converse &&
actor->state.action_data.converse.run_to_unit_index != NONE &&
!actor->state.action_data.converse.in_range &&
!actor->state.action_data.converse.failed)
{
busy = TRUE;
}
}
participant_index = (short)(participant_index + 1);
}
}
if (sound_scripted_dialog_is_playing())
{
busy = TRUE;
}
if (!busy)
{
speaking_unit_index = conversation->line_unit_index;
if (speaking_unit_index == NONE ||
conversation->line_unspatialized)
{
scripted_sound_new(
conversation->line_sound_index,
NONE,
1.0f);
}
else
{
speech_sound_definition_index = conversation->line_sound_index;
vocalization_type = NONE;
play_type = unit_test_speech(
speaking_unit_index,
_ai_communication_priority_yell,
FALSE,
TRUE,
NULL,
&vocalization_type,
&speech_sound_definition_index);
if (play_type == TRUE)
{
busy = TRUE;
}
else if (play_type > 0)
{
struct unit_speech_item speech_item;
long line_sound_index;
csmemset(&speech_item, 0, sizeof(speech_item));
line_sound_index = conversation->line_sound_index;
speech_item.vocalization_type = NONE;
speech_item.ai.communication_type = NONE;
speech_item.ai.damage_category = NONE;
speech_item.ai.dialogue_type_index = NONE;
speech_item.ai.target_unit_index =
conversation->line_address_unit_index;
speech_item.priority = _ai_communication_priority_yell;
speech_item.sound_definition_index = line_sound_index;
speech_item.ai.look_priority = TRUE;
speech_item.ai.look_type = _ai_information_look_unit;
speech_item.ai.look_data.unit.unit_index =
conversation->line_unit_index;
speech_item.ai.information_type = _ai_information_none;
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: speak %s",
definition->name,
tag_get_name(line_sound_index));
}
unit_speak(
conversation->line_unit_index,
play_type,
&speech_item);
}
}
}
}
if (!busy)
{
conversation->line_spoken = TRUE;
conversation->any_line_spoken = TRUE;
}
}
if (conversation->line_spoken)
{
if (!conversation->line_finished)
{
if (conversation->line_unit_index == NONE)
{
sound_definition_index = conversation->line_sound_index;
conversation->line_finished =
sound_definition_index == NONE ||
scripted_sound_time(sound_definition_index) == 0;
}
else
{
unit = unit_get(conversation->line_unit_index);
conversation->line_finished =
unit->unit.speech.current.priority !=
_ai_communication_priority_yell;
}
}
if (conversation->line_finished)
{
delay_timer = conversation->line_delay_timer;
if (delay_timer > 0)
{
conversation->line_delay_timer = (short)(delay_timer - 1);
return conversation->line_advance;
}
conversation->line_advance = TRUE;
if ((conversation->line_flags & FLAG(
_ai_conversation_line_wait_after_until_told_to_advance_bit)) != 0)
{
if (!conversation->waiting_to_advance)
{
conversation->waiting_to_advance = TRUE;
conversation->told_to_advance = FALSE;
}
if (conversation->told_to_advance)
{
conversation->waiting_to_advance = FALSE;
}
else
{
conversation->line_advance = FALSE;
}
}
}
}
}
return conversation->line_advance;
}
static void ai_conversation_line_end(
long conversation_index)
{
struct conversation_datum *conversation;
conversation = ai_conversation_driver_get(conversation_index);
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
return;
}
boolean ai_conversation(
short scenario_conversation_index,
boolean scripted)
{
struct scenario *scenario;
long conversation_index;
boolean result;
scenario = global_scenario_get();
result = FALSE;
if (scenario_conversation_index >= 0 &&
scenario_conversation_index < scenario->ai_conversations.count)
{
conversation_index = ai_conversation_new(
scenario_conversation_index,
scripted);
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: script tried to start conversation",
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation)->name);
}
if (conversation_index == NONE)
{
error(
2,
"WARNING: too many executing conversations (ran out of MAXIMUM_CONVERSATIONS_PER_MAP %d)",
128);
}
else
{
boolean continue_trying;
continue_trying = FALSE;
if (ai_conversation_begin(conversation_index, &continue_trying))
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: begun successfully",
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation)->name);
}
result = TRUE;
}
else if (continue_trying)
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: can't begin yet but will remember and keep trying it",
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation)->name);
}
result = TRUE;
}
else
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: could not start, and not set to keep trying... aborting (status 5)",
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
scenario_conversation_index,
struct ai_conversation)->name);
}
ai_conversation_finish(conversation_index, TRUE, FALSE);
}
}
}
return result;
}
void ai_conversation_update(
void)
{
struct data_iterator iterator;
struct conversation_datum *conversation;
long time;
time = game_time_get();
data_iterator_new(&iterator, conversation_data);
conversation = (struct conversation_datum *)data_iterator_next(&iterator);
while (conversation)
{
struct ai_conversation *definition;
definition = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_conversations,
conversation->scenario_conversation_index,
struct ai_conversation);
if (!conversation->begun)
{
boolean continue_trying;
continue_trying = TRUE;
if ((time - conversation->creation_time) % 30 == 0)
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: trying to begin",
definition->name);
}
ai_conversation_begin(iterator.datum_index, &continue_trying);
}
if (!conversation->begun && !continue_trying)
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: unable to begin, and no point in continuing",
definition->name);
}
ai_conversation_finish(iterator.datum_index, TRUE, FALSE);
}
}
if (conversation->begun && !conversation->finished)
{
boolean perform;
perform = conversation->line_index >= 0 &&
conversation->line_index < definition->lines.count;
while (!perform || ai_conversation_line_perform(iterator.datum_index))
{
if (perform)
{
ai_conversation_line_end(iterator.datum_index);
}
conversation->line_index++;
if (conversation->line_index < definition->lines.count)
{
perform = ai_conversation_line_begin(iterator.datum_index);
}
else
{
if (ai_print_conversations)
{
console_printf(
FALSE,
"%s: no more lines to play",
definition->name);
}
conversation->finished = TRUE;
break;
}
}
}
if (conversation->finished)
{
ai_conversation_finish(iterator.datum_index, FALSE, TRUE);
}
else if (conversation->begun)
{
short participant_index;
for (participant_index = 0;
participant_index < definition->participants.count;
participant_index++)
{
if (TEST_FLAG(conversation->participant_bitmask, participant_index) &&
conversation->actor_indices[participant_index] != NONE)
{
struct actor_datum *actor;
long actor_unit_index;
long speaker_unit_index;
actor = actor_get(conversation->actor_indices[participant_index]);
actor->external_orders.conversation_index = iterator.datum_index;
actor_unit_index = actor->meta.unit_index;
actor->external_orders.conversation_attention_unit_index = NONE;
speaker_unit_index = conversation->line_unit_index;
if (actor_unit_index == speaker_unit_index)
{
actor->external_orders.conversation_attention_unit_index =
conversation->line_address_unit_index;
}
else
{
long address_unit_index;
address_unit_index = conversation->line_address_unit_index;
if (actor_unit_index == address_unit_index &&
TEST_FLAG(
conversation->line_flags,
_ai_conversation_line_addressee_look_back_bit))
{
actor->external_orders.conversation_attention_unit_index =
speaker_unit_index;
}
else if (TEST_FLAG(
conversation->line_flags,
_ai_conversation_line_everyone_look_at_speaker_bit))
{
actor->external_orders.conversation_attention_unit_index =
speaker_unit_index;
}
else if (TEST_FLAG(
conversation->line_flags,
_ai_conversation_line_everyone_look_at_addressee_bit))
{
actor->external_orders.conversation_attention_unit_index =
address_unit_index;
}
}
}
}
}
conversation = (struct conversation_datum *)data_iterator_next(&iterator);
}
return;
}
void ai_communication_event(
short communication_type,
long subject_unit_index,
long cause_unit_index,
short hostility,
short damage_type,
short information_type,
struct ai_information_data *information_data)
{
long event_time = game_time_get();
char requirements_string[1024];
char debug_string[1024];
char team_string[256];
char timer_string[512];
char speech_string[512];
short possibility_count = 0;
real total_possibility_weight = 0.0f;
boolean any_forced_possibility = FALSE;
struct unit_datum *subject_unit = NULL;
struct unit_datum *cause_unit = NULL;
long subject_encounter_index = NONE;
struct encounter_datum *subject_encounter = NULL;
long subject_actor_index = NONE;
struct actor_datum *subject_actor = NULL;
long cause_actor_index = NONE;
struct actor_datum *cause_actor = NULL;
long friend_actor_index = NONE;
long other_actor_index = NONE;
short subject_team = NONE;
short cause_team = NONE;
short subject_race = 0;
short cause_race = 0;
boolean find_friend_actor = TRUE;
boolean find_other_actor = TRUE;
boolean subject_comm_groups_enabled[2];
boolean cause_comm_groups_enabled[2];
boolean enemy_status_enabled[6];
boolean hostility_enabled[NUMBER_OF_AI_COMMUNICATION_HOSTILITIES];
short time_since_chatter[NUMBER_OF_AI_COMMUNICATION_TEAMS];
short time_since_talk[NUMBER_OF_AI_COMMUNICATION_TEAMS];
short time_since_shout[NUMBER_OF_AI_COMMUNICATION_TEAMS];
real chatter_seconds[NUMBER_OF_AI_COMMUNICATION_TEAMS];
real talk_seconds[NUMBER_OF_AI_COMMUNICATION_TEAMS];
real shout_seconds[NUMBER_OF_AI_COMMUNICATION_TEAMS];
short speech_disabled_reason[NUMBER_OF_AI_COMMUNICATION_TEAMS][NUMBER_OF_COMMUNICATION_PRIORITIES][2];
short speech_delay[NUMBER_OF_AI_COMMUNICATION_TEAMS][NUMBER_OF_COMMUNICATION_PRIORITIES][2];
boolean speech_disabled[NUMBER_OF_AI_COMMUNICATION_TEAMS][NUMBER_OF_COMMUNICATION_PRIORITIES][2];
struct ai_communication_possibility possibilities[MAXIMUM_COMMUNICATION_POSSIBILITIES];
struct ai_communication_possibility *selected_possibility;
struct dialogue_usage const *usage;
short dialogue_index;
boolean player_involved = FALSE;
boolean suppress_output = FALSE;
boolean any_requirements_failed = FALSE;
boolean any_protagonist_considered = FALSE;
short team_index;
short priority;
short distance_group;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x34E,
(communication_type >= 0) && (communication_type < NUMBER_OF_AI_COMMUNICATION_TYPES));
if (hostility == NONE)
{
hostility = _comm_hostility_none;
}
if (damage_type == NONE)
{
damage_type = 0;
}
for (distance_group = 0; distance_group < 2; distance_group++)
{
subject_comm_groups_enabled[distance_group] = FALSE;
cause_comm_groups_enabled[distance_group] = FALSE;
}
if (subject_unit_index != NONE)
{
subject_unit = unit_get(subject_unit_index);
subject_actor_index = subject_unit->unit.actor_index;
subject_team = subject_unit->object.owner_team_index;
subject_race = (short)ai_get_race_from_team_index(subject_team);
if (subject_actor_index != NONE)
{
subject_actor = actor_get(subject_actor_index);
subject_encounter_index = subject_actor->meta.encounter_index;
subject_race = actor_type_get_race(subject_actor->meta.type);
if (subject_actor->situation.close_friends > 0)
{
subject_comm_groups_enabled[0] = TRUE;
subject_comm_groups_enabled[1] = TRUE;
}
else if (subject_actor->situation.area_friends > 0)
{
subject_comm_groups_enabled[0] = TRUE;
subject_comm_groups_enabled[1] = FALSE;
}
if (subject_encounter_index != NONE)
{
subject_encounter = encounter_get(subject_encounter_index);
}
}
else if (subject_unit->unit.player_index != NONE)
{
subject_race = _race_player;
}
}
if (cause_unit_index != NONE)
{
cause_unit = unit_get(cause_unit_index);
cause_actor_index = cause_unit->unit.actor_index;
cause_team = cause_unit->object.owner_team_index;
cause_race = (short)ai_get_race_from_team_index(cause_team);
if (cause_actor_index != NONE)
{
cause_actor = actor_get(cause_actor_index);
cause_race = actor_type_get_race(cause_actor->meta.type);
if (cause_actor->situation.close_friends > 0)
{
cause_comm_groups_enabled[0] = TRUE;
cause_comm_groups_enabled[1] = TRUE;
}
else if (cause_actor->situation.area_friends > 0)
{
cause_comm_groups_enabled[0] = TRUE;
cause_comm_groups_enabled[1] = FALSE;
}
}
else if (cause_unit->unit.player_index != NONE)
{
cause_race = _race_player;
}
}
if (subject_unit && cause_unit &&
subject_team != cause_team &&
game_team_is_ally(subject_team, cause_team))
{
boolean betrayal = FALSE;
boolean observed = FALSE;
if (communication_type == _ai_communication_death)
{
if (hostility == _comm_hostility_enemy)
{
betrayal = TRUE;
observed = TRUE;
}
else
{
if (subject_encounter)
{
betrayal =
subject_encounter->enemy_traitor ||
subject_encounter->enemy_visible_timer == NONE ||
subject_encounter->enemy_visible_timer >= 270;
}
friend_actor_index = ai_communication_find_global_actor_to_talk(
subject_team,
_find_actor_mode_same_team,
subject_unit_index,
cause_unit_index,
18.0f,
_ai_communication_death,
_ai_communication_priority_yell,
NONE,
NONE,
NONE,
0);
if (friend_actor_index != NONE)
{
find_friend_actor = FALSE;
observed = TRUE;
}
switch (damage_type)
{
case 3:
case 4:
case 9:
if (!betrayal)
{
observed = FALSE;
}
break;
}
if (damage_type == 3)
{
betrayal = FALSE;
}
}
if (betrayal)
{
hostility = _comm_hostility_traitor;
}
if (ai_debug.print_allegiance)
{
console_printf(
FALSE,
"incident between teams %s and %s: %s, %s",
global_game_team_names[subject_team],
global_game_team_names[cause_team],
betrayal ? "betrayal" : "accident",
observed ? "observed" : "unobserved");
}
if (observed)
{
boolean notify_immediately = FALSE;
boolean broken = game_allegiance_incident(
cause_team,
subject_team,
betrayal ? _allegiance_incident_betrayal : _allegiance_incident_accident,
&notify_immediately);
if (notify_immediately)
{
ai_handle_allegiance_broken_notification(
cause_team,
subject_team,
broken);
}
if (ai_debug.print_allegiance && !broken)
{
short incident_threshold;
short incidents = game_allegiance_get_incidents(
cause_team,
subject_team,
&incident_threshold);
console_printf(
FALSE,
"allegiance %s, %d incidents (threshold %d)",
"still holds",
incidents,
incident_threshold == NONE ? 999 : incident_threshold);
}
}
}
if (game_team_is_enemy(subject_team, cause_team))
{
hostility = _comm_hostility_traitor;
}
}
if (ai_debug.print_communication)
{
char hostility_characters[NUMBER_OF_AI_COMMUNICATION_HOSTILITIES] =
{
'n', 's', 'f', 'e', 't'
};
sprintf(
debug_string,
"%s-%c ",
global_communication_type_names[communication_type],
hostility_characters[hostility]);
strupr(debug_string);
csstrcpy(requirements_string, "");
}
if (!subject_actor)
{
short index;
for (index = 0; index < NUMBEROF(enemy_status_enabled); index++)
{
enemy_status_enabled[index] = TRUE;
}
}
else if (!subject_encounter)
{
enemy_status_enabled[0] = !subject_actor->target.any_target_ever;
enemy_status_enabled[1] =
!subject_actor->target.target_really_alive &&
subject_actor->target.since_any_target_visible_timer != NONE;
enemy_status_enabled[2] =
subject_actor->target.target_prop_index == NONE ||
subject_actor->target.since_any_target_visible_timer == NONE ||
subject_actor->target.since_any_target_visible_timer >= 180;
enemy_status_enabled[3] =
subject_actor->state.combat_status < _actor_combat_status_definite &&
(subject_actor->target.since_any_target_visible_timer == NONE ||
subject_actor->target.since_any_target_visible_timer >= 75) &&
(subject_actor->target.target_really_alive || subject_actor->state.combat_status > 0);
enemy_status_enabled[4] = subject_actor->state.combat_status < _actor_combat_status_dangerous;
enemy_status_enabled[5] =
subject_actor->target.target_type >= _actor_target_visible_enemy &&
subject_actor->target.target_really_alive;
}
else
{
enemy_status_enabled[0] = !subject_actor->target.any_target_ever;
enemy_status_enabled[1] =
subject_encounter->enemy_visible_timer != NONE &&
!subject_encounter->enemy_alive;
enemy_status_enabled[2] =
(subject_encounter->enemy_visible_timer == NONE ||
subject_encounter->enemy_visible_timer >= 180) &&
subject_encounter->enemy_alive;
enemy_status_enabled[3] =
subject_actor->state.combat_status < _actor_combat_status_definite &&
(subject_encounter->enemy_visible_timer == NONE ||
subject_encounter->enemy_visible_timer >= 75) &&
(subject_encounter->enemy_alive || subject_actor->state.combat_status > 0);
enemy_status_enabled[4] =
subject_actor->state.combat_status < _actor_combat_status_dangerous &&
(subject_encounter->enemy_visible_timer == NONE ||
subject_encounter->enemy_visible_timer >= 75);
enemy_status_enabled[5] =
subject_encounter->enemy_visible &&
subject_encounter->enemy_alive;
}
csmemset(hostility_enabled, 0, sizeof(hostility_enabled));
if (hostility != NONE)
{
hostility_enabled[hostility] = TRUE;
if (hostility == _comm_hostility_traitor)
{
hostility_enabled[_comm_hostility_enemy] = TRUE;
}
}
csmemset(speech_disabled_reason, NONE, sizeof(speech_disabled_reason));
csmemset(speech_disabled, 0, sizeof(speech_disabled));
csmemset(speech_delay, 0, sizeof(speech_delay));
for (team_index = 0;
team_index < NUMBER_OF_AI_COMMUNICATION_TEAMS;
team_index++)
{
time_since_shout[team_index] = (short)MAX(0,
event_time - ai_globals->last_shout_time[team_index]);
time_since_talk[team_index] = (short)MAX(0,
event_time - ai_globals->last_talk_time[team_index]);
time_since_chatter[team_index] = (short)MAX(0,
event_time - ai_globals->last_chatter_time[team_index]);
shout_seconds[team_index] =
time_since_shout[team_index] * (1.0f / TICKS_PER_SECOND);
talk_seconds[team_index] =
time_since_talk[team_index] * (1.0f / TICKS_PER_SECOND);
chatter_seconds[team_index] =
time_since_chatter[team_index] * (1.0f / TICKS_PER_SECOND);
/* nothing may be spoken at priority none */
speech_disabled[team_index][_ai_communication_priority_none][0] = TRUE;
speech_disabled[team_index][_ai_communication_priority_none][1] = TRUE;
for (priority = 1; priority < _ai_communication_priority_yell; priority++)
{
for (distance_group = 0; distance_group < 2; distance_group++)
{
boolean disabled = FALSE;
short delay = 0;
short reason = NONE;
if (communication_timer_tolerances[priority][distance_group][0] > 0.0f)
{
short remaining = (short)(
communication_timer_tolerances[priority][distance_group][0] * TICKS_PER_SECOND -
time_since_chatter[team_index]);
if (remaining > 0)
{
reason = 0;
disabled = TRUE;
delay = MAX(delay, remaining);
}
}
if (communication_timer_tolerances[priority][distance_group][1] > 0.0f)
{
short remaining = (short)(
communication_timer_tolerances[priority][distance_group][1] * TICKS_PER_SECOND -
time_since_talk[team_index]);
if (remaining > 0)
{
disabled = TRUE;
reason = 1;
delay = MAX(delay, remaining);
}
}
if (communication_timer_tolerances[priority][distance_group][3] > 0.0f)
{
short remaining = (short)(
communication_timer_tolerances[priority][distance_group][3] * TICKS_PER_SECOND -
time_since_shout[team_index]);
if (remaining > 0)
{
disabled = TRUE;
reason = 2;
delay = MAX(delay, remaining);
}
}
/* BUG (original): January reads this slot's stored delay before it
* is written below (still zero from the clear), so a positive
* minimum tolerance re-enables every disabled slot; comparing
* `delay` was probably intended. */
if (disabled &&
communication_timer_tolerances[priority][distance_group][4] > 0.0f &&
speech_delay[team_index][priority][distance_group] <
communication_timer_tolerances[priority][distance_group][4] * TICKS_PER_SECOND)
{
disabled = FALSE;
}
speech_disabled[team_index][priority][distance_group] = disabled;
speech_delay[team_index][priority][distance_group] = delay;
speech_disabled_reason[team_index][priority][distance_group] = reason;
}
}
}
if (!ai_globals->dialogue_triggers_enabled)
{
if (ai_debug.print_communication)
{
csstrcat(debug_string, "DISABLED");
error(2, debug_string);
}
return;
}
dialogue_index = global_communication_table_indices[communication_type];
if (game_connection() == _game_connection_local &&
BIT_VECTOR_TEST_FLAG(ai_debug.communication_suppress_vector, communication_type))
{
dialogue_index = NONE;
suppress_output = TRUE;
}
else
{
suppress_output = BIT_VECTOR_TEST_FLAG(
ai_debug.communication_ignore_vector,
communication_type);
}
if (ai_debug.communication_focus_enable)
{
suppress_output = TRUE;
}
if (dialogue_index != NONE)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x4C3,
(dialogue_index >= 0) && (dialogue_index < global_dialogue_event_count));
for (usage = &global_dialogue_table[dialogue_index];
usage->communication_type == communication_type;
usage++, dialogue_index++)
{
short communication_priority = usage->communication_priority;
if (ai_debug.communication_focus_enable &&
BIT_VECTOR_TEST_FLAG(
ai_debug.communication_focus_vector,
usage->vocalization_type))
{
suppress_output = FALSE;
}
if (usage->required_hostility != NONE &&
!hostility_enabled[usage->required_hostility])
{
if (ai_debug.print_communication)
{
char const *hostility_names[NUMBER_OF_AI_COMMUNICATION_HOSTILITIES] =
{
"none", "self", "friend", "enemy", "traitor"
};
csstrcat(
requirements_string,
csprintf(
temporary,
"[%s/%d host-%s] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
hostility_names[usage->required_hostility]));
}
any_requirements_failed = TRUE;
continue;
}
else if (sound_scripted_dialog_is_playing() &&
usage->communication_priority < _ai_communication_priority_yell &&
!TEST_FLAG(usage->flags, _dialogue_usage_override_scripted_bit))
{
if (ai_debug.print_communication)
{
csstrcat(requirements_string, "[scripted-override] ");
}
any_requirements_failed = TRUE;
continue;
}
else if (usage->required_enemy_status != NONE &&
!enemy_status_enabled[usage->required_enemy_status])
{
if (ai_debug.print_communication)
{
char const *enemy_status_names[NUMBEROF(enemy_status_enabled)] =
{
"never", "dead", "lost", "notvis", "nodanger", "vis"
};
csstrcat(
requirements_string,
csprintf(
temporary,
"[%s/%d status-%s] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
enemy_status_names[usage->required_enemy_status]));
}
any_requirements_failed = TRUE;
continue;
}
else if (usage->required_subject_race != NONE &&
(subject_unit_index == NONE ||
!(usage->required_subject_race & subject_race)))
{
if (ai_debug.print_communication)
{
csstrcat(
requirements_string,
csprintf(
temporary,
"[%s/%d nosubrace] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index));
}
any_requirements_failed = TRUE;
continue;
}
else if (usage->required_cause_race != NONE &&
(cause_unit_index == NONE ||
!(usage->required_cause_race & cause_race)))
{
if (ai_debug.print_communication)
{
csstrcat(
requirements_string,
csprintf(
temporary,
"[%s/%d nocausrace] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index));
}
any_requirements_failed = TRUE;
continue;
}
else if (usage->required_damage != NONE &&
usage->required_damage != damage_type)
{
if (ai_debug.print_communication)
{
csstrcat(
requirements_string,
csprintf(
temporary,
"[%s/%d nodmg] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index));
}
any_requirements_failed = TRUE;
continue;
}
else
{
short speech_priority = communication_speech_priorities[communication_priority];
long protagonist_actor_index = NONE;
long protagonist_unit_index = NONE;
struct actor_datum *protagonist_actor = NULL;
long recipient_unit_index = NONE;
short look_type = _ai_information_none;
long look_unit_index = NONE;
short protagonist_look_priority;
short recipient_look_priority = 0;
boolean *protagonist_groups = NULL;
boolean is_reply = FALSE;
boolean near_player = FALSE;
long reply_actor_index = NONE;
short delay_time;
short ai_delay_time;
short candidate_delay = 0;
real player_rating;
real repeat_rating = 1.0f;
real reply_rating = 1.0f;
csstrcpy(team_string, "<err>");
switch (usage->protagonist_type)
{
case _comm_protagonist_subject:
protagonist_groups = subject_comm_groups_enabled;
protagonist_unit_index = subject_unit_index;
protagonist_actor_index = subject_actor_index;
protagonist_actor = subject_actor;
recipient_unit_index = cause_unit_index;
break;
case _comm_protagonist_cause:
protagonist_groups = cause_comm_groups_enabled;
protagonist_unit_index = cause_unit_index;
protagonist_actor_index = cause_actor_index;
protagonist_actor = cause_actor;
recipient_unit_index = subject_unit_index;
break;
case _comm_protagonist_friend:
recipient_unit_index = cause_unit_index;
if (find_friend_actor)
{
short find_actor_flags = 0;
SET_FLAG(find_actor_flags, _find_actor_allow_lookup_bit,
TEST_FLAG(usage->flags, _dialogue_usage_lookup_bit));
SET_FLAG(find_actor_flags, _find_actor_near_to_players_bit, TRUE);
SET_FLAG(find_actor_flags, _find_actor_same_vehicle_bit,
TEST_FLAG(usage->flags, _dialogue_usage_same_vehicle_bit));
SET_FLAG(find_actor_flags, _find_actor_allow_subject_bit,
TEST_FLAG(usage->flags, _dialogue_usage_allow_subject_bit));
SET_FLAG(find_actor_flags, _find_actor_allow_cause_bit, TRUE);
if (subject_encounter_index != NONE)
{
friend_actor_index = ai_communication_find_specific_actor_to_talk(
DATUM_INDEX_TO_ABSOLUTE_INDEX(subject_encounter_index),
subject_unit_index,
cause_unit_index,
10.0f,
communication_type,
communication_priority,
speech_priority,
usage->vocalization_type,
usage->animation_type,
find_actor_flags);
}
else
{
friend_actor_index = ai_communication_find_global_actor_to_talk(
subject_team,
_find_actor_mode_friend,
subject_unit_index,
cause_unit_index,
10.0f,
communication_type,
communication_priority,
speech_priority,
usage->vocalization_type,
usage->animation_type,
find_actor_flags);
}
find_friend_actor = FALSE;
}
protagonist_actor_index = friend_actor_index;
if (protagonist_actor_index != NONE)
{
protagonist_actor = actor_get(protagonist_actor_index);
protagonist_unit_index = protagonist_actor->meta.unit_index;
}
break;
case _comm_protagonist_enemy:
recipient_unit_index = cause_unit_index;
if (find_other_actor)
{
short find_actor_flags = 0;
SET_FLAG(find_actor_flags, _find_actor_allow_lookup_bit,
TEST_FLAG(usage->flags, _dialogue_usage_lookup_bit));
SET_FLAG(find_actor_flags, _find_actor_near_to_players_bit, TRUE);
SET_FLAG(find_actor_flags, _find_actor_same_vehicle_bit,
TEST_FLAG(usage->flags, _dialogue_usage_same_vehicle_bit));
SET_FLAG(find_actor_flags, _find_actor_allow_subject_bit,
TEST_FLAG(usage->flags, _dialogue_usage_allow_subject_bit));
other_actor_index = ai_communication_find_global_actor_to_talk(
subject_team,
_find_actor_mode_enemy,
subject_unit_index,
cause_unit_index,
12.0f,
communication_type,
communication_priority,
speech_priority,
usage->vocalization_type,
usage->animation_type,
find_actor_flags);
find_other_actor = FALSE;
}
protagonist_actor_index = other_actor_index;
if (protagonist_actor_index != NONE)
{
protagonist_actor = actor_get(protagonist_actor_index);
protagonist_unit_index = protagonist_actor->meta.unit_index;
}
break;
default:
match_vassert("c:\\halo\\SOURCE\\ai\\ai_communication.c", 0x594, FALSE, NULL);
break;
}
{
boolean protagonist_invalid = FALSE;
if (protagonist_unit_index == NONE)
{
protagonist_invalid = TRUE;
}
else
{
struct unit_datum *protagonist_unit = unit_get(protagonist_unit_index);
if (TEST_FLAG(protagonist_unit->object.damage_flags, _object_dead_bit))
{
protagonist_invalid = TRUE;
}
else if (protagonist_unit->object.type == _object_type_vehicle)
{
protagonist_invalid = TRUE;
}
else if (protagonist_unit->unit.player_index != NONE &&
protagonist_unit->unit.actor_index == NONE)
{
if (TEST_FLAG(usage->flags, _dialogue_usage_player_bit))
{
is_reply = TRUE;
player_involved = TRUE;
}
else
{
protagonist_invalid = TRUE;
}
}
}
if (protagonist_actor)
{
if (protagonist_actor->state.mode == _actor_mode_braindead)
{
protagonist_invalid = TRUE;
}
else if (protagonist_actor->state.action == _actor_action_obey &&
!protagonist_actor->state.action_data.obey.allow_communication)
{
protagonist_invalid = TRUE;
}
}
if (protagonist_invalid)
{
if (ai_debug.print_communication)
{
char protagonist_code =
usage->protagonist_type == _comm_protagonist_subject ? 's' :
usage->protagonist_type == _comm_protagonist_cause ? 'c' :
usage->protagonist_type == _comm_protagonist_friend ? 'f' :
usage->protagonist_type == _comm_protagonist_enemy ? 'e' : '?';
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d nounit-%c] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
protagonist_code));
}
any_protagonist_considered = TRUE;
continue;
}
}
if (is_reply)
{
reply_actor_index = ai_communication_find_actor_to_reply_to_player(
protagonist_unit_index,
recipient_unit_index,
usage->vocalization_type,
damage_type,
&reply_rating);
if (protagonist_unit_index == subject_unit_index)
{
find_friend_actor = FALSE;
friend_actor_index = reply_actor_index;
}
if (reply_actor_index == NONE)
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d noplyreply] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index));
}
any_protagonist_considered = TRUE;
continue;
}
}
if (usage->required_group != NONE &&
protagonist_groups &&
!protagonist_groups[usage->required_group])
{
if (ai_debug.print_communication)
{
char group_code =
usage->required_group == 0 ? 'e' :
usage->required_group == 1 ? 't' : '?';
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d nogrp-%c] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
group_code));
}
any_protagonist_considered = TRUE;
continue;
}
if (is_reply)
{
communication_priority =
communication_player_speaking_priorities[communication_priority];
player_rating = 2.0f;
csstrcpy(team_string, "player");
}
else
{
player_rating = ai_communication_get_player_rating(
protagonist_unit_index,
TRUE,
NULL,
NULL);
if (player_rating == 0.0f)
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d 0-playrat] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index));
}
any_protagonist_considered = TRUE;
continue;
}
else
{
short communication_team;
near_player = player_rating < communication_player_rating_low_priority;
communication_team = protagonist_actor_index == NONE ?
NONE : actor_communication_team(protagonist_actor_index);
if (communication_team == NONE)
{
csstrcpy(team_string, "unteamed");
}
else
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x62B,
(communication_team >= 0) && (communication_team < NUMBER_OF_AI_COMMUNICATION_TEAMS));
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x62E,
(communication_priority > _ai_communication_priority_none) && (communication_priority < NUMBER_OF_AI_COMMUNICATION_PRIORITIES));
sprintf(
team_string,
"%s-%c%c%c%s",
global_communication_team_names[2 * communication_team + 1],
global_communication_priority_names[communication_priority][0],
global_communication_priority_names[communication_priority][1],
global_communication_priority_names[communication_priority][2],
near_player ? "-lo" : "-hi");
if (speech_disabled[communication_team][communication_priority][near_player])
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x639,
communication_priority < _ai_communication_priority_yell);
if (ai_debug.print_communication)
{
if (speech_disabled_reason[communication_team][communication_priority][near_player] == 0)
{
sprintf(
timer_string,
"chat:%.1f<%.1f",
chatter_seconds[communication_team],
communication_timer_tolerances[communication_priority][near_player][0]);
}
else if (speech_disabled_reason[communication_team][communication_priority][near_player] == 1)
{
sprintf(
timer_string,
"talk:%.1f<%.1f",
talk_seconds[communication_team],
communication_timer_tolerances[communication_priority][near_player][1]);
}
else if (speech_disabled_reason[communication_team][communication_priority][near_player] == 2)
{
sprintf(
timer_string,
"shout:%.1f<%.1f",
shout_seconds[communication_team],
communication_timer_tolerances[communication_priority][near_player][3]);
}
else
{
sprintf(timer_string, "<err>");
}
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d %s %s] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
team_string,
timer_string));
}
any_protagonist_considered = TRUE;
continue;
}
else
{
candidate_delay = speech_delay[communication_team][communication_priority][near_player];
if (communication_priority < _ai_communication_priority_exclaim)
{
struct dialogue_event_status *event = &global_dialogue_events[
dialogue_index * NUMBER_OF_AI_COMMUNICATION_TEAMS +
communication_team];
if (event->last_time_spoken != NONE)
{
repeat_rating =
(real)(event_time - event->last_time_spoken) *
0.0011111111f;
if (repeat_rating < 0.0f)
{
repeat_rating = 0.0f;
}
else if (repeat_rating > 1.0f)
{
repeat_rating = 1.0f;
}
}
if ((game_connection() != _game_connection_local ||
!ai_debug.communication_timeout_disabled) &&
event->disable_until_time != NONE)
{
long remaining = event->disable_until_time - event_time;
if (near_player)
{
remaining += communication_timeout_low_priority_modifier;
}
if (remaining > 0)
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d %s-d-dis/%d] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
global_communication_team_names[2 * communication_team + 1],
remaining));
}
any_protagonist_considered = TRUE;
continue;
}
}
}
}
}
}
}
delay_time = (short)(
communication_play_delays[usage->protagonist_type] * TICKS_PER_SECOND);
if (subject_race == _race_player && !is_reply)
{
delay_time = (short)(delay_time + communication_player_additional_delay);
}
ai_delay_time = (short)(TEST_FLAG(usage->flags, _dialogue_usage_immediate_notify_bit) ?
0.0f :
communication_notification_delays[communication_priority] * TICKS_PER_SECOND);
delay_time = (short)(delay_time + candidate_delay);
ai_delay_time = (short)(ai_delay_time + candidate_delay);
switch (usage->recipient_look_direction)
{
case _comm_look_direction_subject:
if (subject_unit_index != NONE)
{
look_type = _ai_information_look_unit;
look_unit_index = subject_unit_index;
}
break;
case _comm_look_direction_protagonist:
if (protagonist_unit_index != NONE)
{
look_type = _ai_information_look_unit;
look_unit_index = protagonist_unit_index;
}
break;
case _comm_look_direction_target:
if (recipient_unit_index != NONE)
{
look_type = _ai_information_look_unit;
look_unit_index = recipient_unit_index;
}
break;
case _comm_look_direction_danger:
if (subject_actor_index != NONE)
{
struct actor_datum *subject_actor = actor_get(subject_actor_index);
if (subject_actor->danger_zone.danger_type > _actor_danger_zone_none)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x6B0,
subject_actor->danger_zone.object_index != NONE);
look_type = _ai_information_look_object;
look_unit_index = subject_actor->danger_zone.object_index;
}
}
break;
}
if (look_type != _ai_information_none)
{
recipient_look_priority = usage->recipient_look_priority;
if (recipient_look_priority == NONE ||
recipient_look_priority == TRUE)
{
recipient_look_priority =
communication_recipient_default_look_priorities[communication_priority];
}
}
protagonist_look_priority = usage->protagonist_look_priority;
if (protagonist_look_priority == NONE ||
protagonist_look_priority == TRUE)
{
protagonist_look_priority =
communication_protagonist_default_look_priorities[communication_priority];
}
{
short vocalization_type = usage->vocalization_type;
short animation_type = usage->animation_type;
long sound_definition_index = NONE;
real speech_weight = 1.0f;
real animation_weight = 1.0f;
short play_type = _unit_play_speech_none;
if (!is_reply)
{
play_type = ai_communication_consider_speech(
protagonist_unit_index,
communication_priority,
speech_priority,
delay_time,
TEST_FLAG(usage->flags, _dialogue_usage_lookup_bit),
FALSE,
&vocalization_type,
&speech_weight,
&sound_definition_index,
speech_string);
if (play_type == _unit_play_speech_none)
{
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d u-%s-%s] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
speech_weight > 0.0f ? "dis" : "n/a",
speech_string));
}
any_protagonist_considered = TRUE;
continue;
}
else if (animation_type != NONE)
{
boolean animation_impulse = unit_test_animation_impulse(
protagonist_unit_index,
animation_type);
if (animation_impulse && protagonist_actor_index != NONE)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x6F2,
protagonist_actor);
if (actor_action_class(protagonist_actor_index) == _action_class_transitory)
{
animation_impulse = FALSE;
}
else if (protagonist_actor->state.mode == _actor_mode_asleep)
{
animation_impulse = FALSE;
}
}
if (animation_impulse)
{
animation_weight = 2.0f;
}
}
}
{
real weight = animation_weight * speech_weight * player_rating *
usage->weight * reply_rating * repeat_rating;
if (weight > 0.0f)
{
if (possibility_count >= MAXIMUM_COMMUNICATION_POSSIBILITIES)
{
error(
2,
"ai_communication_event: type %d (%s) overflowed MAXIMUM_COMMUNICATION_POSSIBILITIES (%d)",
communication_type,
global_communication_type_names[communication_type],
MAXIMUM_COMMUNICATION_POSSIBILITIES);
break;
}
possibilities[possibility_count].weight = weight;
possibilities[possibility_count].dialogue_type_index = dialogue_index;
possibilities[possibility_count].is_reply = is_reply;
possibilities[possibility_count].protagonist_unit_index = protagonist_unit_index;
possibilities[possibility_count].protagonist_actor_index = protagonist_actor_index;
possibilities[possibility_count].animation_type = usage->animation_type;
possibilities[possibility_count].recipient_unit_index = recipient_unit_index;
possibilities[possibility_count].preselected_reply_actor_index = reply_actor_index;
possibilities[possibility_count].priority = speech_priority;
possibilities[possibility_count].delay_time = delay_time;
possibilities[possibility_count].ai_delay_time = ai_delay_time;
possibilities[possibility_count].vocalization_type = vocalization_type;
possibilities[possibility_count].sound_definition_index = sound_definition_index;
possibilities[possibility_count].recipient_look_priority = recipient_look_priority;
possibilities[possibility_count].look_type = look_type;
possibilities[possibility_count].look_unit_index = look_unit_index;
possibilities[possibility_count].play_type = play_type;
possibilities[possibility_count].protagonist_look_priority = protagonist_look_priority;
possibilities[possibility_count].interrupts =
TEST_FLAG(usage->flags, _dialogue_usage_force_bit);
if (possibilities[possibility_count].interrupts)
{
any_forced_possibility = TRUE;
}
possibility_count++;
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%s/%d %s del%d w:%.1f%s s%.1f p%.1f%s a%.1f rc%.1f rp%.1f t%.1f] ",
dialogue_get_vocalization_name(usage->vocalization_type, TRUE),
dialogue_index,
team_string,
candidate_delay,
usage->weight,
possibilities[possibility_count - 1].interrupts ? "F" : "",
speech_weight,
player_rating,
is_reply ? "PLAYER" : "",
animation_weight,
repeat_rating,
reply_rating,
possibilities[possibility_count - 1].weight));
}
total_possibility_weight += weight;
any_protagonist_considered = TRUE;
}
}
}
}
}
}
if (ai_debug.print_communication_player && !player_involved)
{
suppress_output = TRUE;
}
if (possibility_count > 0)
{
struct ai_information_packet ai_packet;
selected_possibility = possibilities;
if (any_forced_possibility)
{
real original_weight = total_possibility_weight;
short forced_count = 0;
short index;
total_possibility_weight = 0.0f;
for (index = 0; index < possibility_count; index++)
{
if (possibilities[index].interrupts)
{
forced_count++;
}
else
{
possibilities[index].weight = 0.0f;
}
total_possibility_weight += possibilities[index].weight;
}
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x76E,
total_possibility_weight > 0.0f);
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[%d/%.1f force %d/%.1f] ",
possibility_count,
original_weight,
forced_count,
total_possibility_weight));
}
}
if (possibility_count > 1)
{
real cumulative_weight = 0.0f;
real random_weight = real_seed_random(get_global_random_seed_address()) *
total_possibility_weight;
short index;
for (index = 0; index < possibility_count - 1; index++)
{
cumulative_weight += possibilities[index].weight;
if (cumulative_weight >= random_weight)
{
break;
}
}
selected_possibility = &possibilities[index];
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
csprintf(
temporary,
"[rnd%.1f tot%.1f cum%.1f@%d] ",
random_weight,
total_possibility_weight,
cumulative_weight,
index));
}
}
ai_packet.communication_type = communication_type;
ai_packet.target_unit_index = selected_possibility->recipient_unit_index;
ai_packet.damage_category = damage_type;
ai_packet.dialogue_type_index = selected_possibility->dialogue_type_index;
ai_packet.updated_dialogue_timers = TRUE;
ai_packet.look_priority = selected_possibility->recipient_look_priority;
ai_packet.look_type = selected_possibility->look_type;
ai_packet.look_data.unit.unit_index = selected_possibility->look_unit_index;
ai_packet.information_type = information_type == NONE ?
_ai_information_none : information_type;
if (!information_data)
{
csmemset(
&ai_packet.information_data,
0,
sizeof(ai_packet.information_data));
}
else
{
ai_packet.information_data = *information_data;
}
if (selected_possibility->is_reply)
{
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0x7A7,
selected_possibility->preselected_reply_actor_index != NONE);
ai_communication_started(
selected_possibility->protagonist_unit_index,
selected_possibility->priority,
selected_possibility->vocalization_type,
&ai_packet);
ai_communication_notify(
selected_possibility->protagonist_unit_index,
selected_possibility->priority,
selected_possibility->vocalization_type,
&ai_packet);
ai_communication_finished(
selected_possibility->protagonist_unit_index,
selected_possibility->priority,
selected_possibility->vocalization_type,
TRUE,
selected_possibility->preselected_reply_actor_index,
&ai_packet);
}
else
{
struct unit_speech_item speech;
speech.priority = selected_possibility->priority;
speech.vocalization_type = selected_possibility->vocalization_type;
speech.sound_definition_index = selected_possibility->sound_definition_index;
speech.delay_time = selected_possibility->delay_time;
speech.ai_notification_delay = selected_possibility->ai_delay_time;
speech.pause_time = 24;
speech.ai = ai_packet;
unit_speak(
selected_possibility->protagonist_unit_index,
selected_possibility->play_type,
&speech);
if (selected_possibility->animation_type != NONE)
{
struct unit_datum *speaker_unit = unit_get(selected_possibility->protagonist_unit_index);
real_vector2d alignment_vector;
alignment_vector.i = speaker_unit->object.forward.i;
alignment_vector.j = speaker_unit->object.forward.j;
if (selected_possibility->recipient_unit_index != NONE)
{
real_point3d protagonist_head_position;
real_point3d target_head_position;
real magnitude;
unit_get_head_position(selected_possibility->protagonist_unit_index, &protagonist_head_position);
unit_get_head_position(selected_possibility->recipient_unit_index, &target_head_position);
alignment_vector.i = target_head_position.x - protagonist_head_position.x;
alignment_vector.j = target_head_position.y - protagonist_head_position.y;
magnitude = square_root(
alignment_vector.i * alignment_vector.i + alignment_vector.j * alignment_vector.j);
if (fabs(magnitude) >= _real_epsilon && magnitude != 0.0f)
{
alignment_vector.i /= magnitude;
alignment_vector.j /= magnitude;
}
else
{
alignment_vector.i = speaker_unit->object.forward.i;
alignment_vector.j = speaker_unit->object.forward.j;
}
}
unit_start_animation_impulse(
selected_possibility->protagonist_unit_index,
selected_possibility->animation_type,
&alignment_vector);
}
if (selected_possibility->protagonist_actor_index != NONE)
{
ai_communication_look_secondary_at_unit(
selected_possibility->protagonist_actor_index,
_secondary_look_communicated_direction,
selected_possibility->protagonist_look_priority,
selected_possibility->recipient_unit_index,
NONE);
}
ai_communication_update_speech_timers(
selected_possibility->protagonist_unit_index,
selected_possibility->priority,
selected_possibility->vocalization_type,
selected_possibility->dialogue_type_index,
NONE);
}
if (ai_debug.print_communication)
{
csstrcat(
debug_string,
strupr(csprintf(
temporary,
">>%s<<",
dialogue_get_vocalization_name(selected_possibility->vocalization_type, TRUE))));
if (!suppress_output)
{
error(2, debug_string);
}
}
}
else if (ai_debug.print_communication)
{
if (any_requirements_failed && !any_protagonist_considered)
{
csstrcat(debug_string, requirements_string);
}
if (ai_debug.communication_focus_enable ||
!any_requirements_failed ||
any_protagonist_considered)
{
csstrcat(debug_string, "NONE");
if (!suppress_output)
{
error(2, debug_string);
}
}
}
return;
}
/* ---------- private code */