mirror of
https://github.com/astromaddie/HaloCE-VR.git
synced 2026-10-20 02:00:14 +02:00
8082 lines
215 KiB
C
8082 lines
215 KiB
C
/*
|
|
GAME_ENGINE.C
|
|
|
|
symbols in this file:
|
|
00096780 0020:
|
|
_game_globals_get_weapon (0000)
|
|
000967A0 0070:
|
|
_game_engine_get_team_score (0000)
|
|
00096810 0020:
|
|
_linear_to_non_linear_alpha (0000)
|
|
00096830 0020:
|
|
_game_engine_dispose (0000)
|
|
00096850 0040:
|
|
_initialize_player_multiplayer_data (0000)
|
|
00096890 0120:
|
|
_game_engine_build_lighting (0000)
|
|
000969B0 0020:
|
|
_game_engine_dispose_from_old_map (0000)
|
|
000969D0 0020:
|
|
_game_engine_game_ending (0000)
|
|
000969F0 0020:
|
|
_game_engine_game_starting (0000)
|
|
00096A10 0040:
|
|
_players_in_game (0000)
|
|
00096A50 0020:
|
|
_game_engine_statistics_append (0000)
|
|
00096A70 0020:
|
|
_game_engine_handle_client_message (0000)
|
|
00096A90 0020:
|
|
_game_engine_handle_server_message (0000)
|
|
00096AB0 0030:
|
|
_sort_statistic_buffer (0000)
|
|
00096AE0 0050:
|
|
_sort_statistic_buffer_ranking (0000)
|
|
00096B30 0010:
|
|
_is_place_tied (0000)
|
|
00096B40 0010:
|
|
_place_get_position (0000)
|
|
00096B50 0050:
|
|
_get_place_string (0000)
|
|
00096BA0 0070:
|
|
_drawline (0000)
|
|
00096C10 0020:
|
|
_get_selected_color (0000)
|
|
00096C30 0050:
|
|
_get_postgame_hilite_colors (0000)
|
|
00096C80 0060:
|
|
_find_closest_player_callback (0000)
|
|
00096CE0 0020:
|
|
_game_engine_post_rasterize_objects (0000)
|
|
00096D00 0030:
|
|
_can_delete_item (0000)
|
|
00096D30 0100:
|
|
_game_engine_update_purge (0000)
|
|
00096E30 00a0:
|
|
_update_weapon_inventory (0000)
|
|
00096ED0 0150:
|
|
_game_engine_update_weapons (0000)
|
|
00097020 00b0:
|
|
_item_collection_get_total (0000)
|
|
000970D0 0050:
|
|
_game_engine_load_stage (0000)
|
|
00097120 0030:
|
|
_game_engine_playlist_begin (0000)
|
|
00097150 0060:
|
|
_game_engine_get_current_stage (0000)
|
|
000971B0 0030:
|
|
_game_engine_end_game (0000)
|
|
000971E0 0020:
|
|
_game_engine_allow_pick_up (0000)
|
|
00097200 0050:
|
|
_game_engine_player_damaged_player (0000)
|
|
00097250 00a0:
|
|
_game_engine_player_is_odd_man_out (0000)
|
|
000972F0 0040:
|
|
_game_engine_player_is_out_of_lives (0000)
|
|
00097330 0100:
|
|
_game_engine_should_spawn_player (0000)
|
|
00097430 0070:
|
|
_game_engine_player_get_team_index (0000)
|
|
000974A0 0040:
|
|
_game_engine_prespawn_player_update (0000)
|
|
000974E0 0010:
|
|
_game_engine_running (0000)
|
|
000974F0 0020:
|
|
_game_engine_can_score (0000)
|
|
00097510 0020:
|
|
_game_engine_force_single_screen (0000)
|
|
00097530 0030:
|
|
_get_blink_alpha (0000)
|
|
00097560 0010:
|
|
_game_engine_press_start_to_begin (0000)
|
|
00097570 00f0:
|
|
_nearby_vehicle (0000)
|
|
00097660 0100:
|
|
_game_engine_rasterize_message (0000)
|
|
00097760 00e0:
|
|
_game_engine_picking_up (0000)
|
|
00097840 0080:
|
|
_goal_matches_player (0000)
|
|
000978C0 00a0:
|
|
_game_engine_player_get_custom_motion_sensor_positions (0000)
|
|
00097960 0030:
|
|
_get_flag_definition_index (0000)
|
|
00097990 0030:
|
|
_get_ball_definition_index (0000)
|
|
000979C0 0040:
|
|
_game_engine_switch_to_postgame (0000)
|
|
00097A00 0010:
|
|
_game_engine_get_variant (0000)
|
|
00097A10 0020:
|
|
_game_engine_get_goal_in_use (0000)
|
|
00097A30 0060:
|
|
_game_engine_get_goal_position (0000)
|
|
00097A90 0080:
|
|
_game_engine_set_goal_position (0000)
|
|
00097B10 0020:
|
|
_game_engine_clear_goal_position (0000)
|
|
00097B30 00d0:
|
|
_game_engine_render_nav_points (0000)
|
|
00097C00 0020:
|
|
_game_engine_infinite_grenades_internal (0000)
|
|
00097C20 0030:
|
|
_game_engine_infinite_grenades (0000)
|
|
00097C50 0020:
|
|
_game_engine_has_teams (0000)
|
|
00097C70 0030:
|
|
_game_engine_display_team_indicators (0000)
|
|
00097CA0 0030:
|
|
_game_engine_has_shield (0000)
|
|
00097CD0 0060:
|
|
_weapon_definition_index_to_list_index (0000)
|
|
00097D30 0030:
|
|
_list_index_to_weapon_definition_index (0000)
|
|
00097D60 00c0:
|
|
_game_engine_remap_vehicle (0000)
|
|
00097E20 0190:
|
|
_game_engine_remap_weapon (0000)
|
|
00097FB0 0070:
|
|
_game_engine_man_out (0000)
|
|
00098020 0030:
|
|
_game_engine_state_message (0000)
|
|
00098050 0090:
|
|
_game_engine_player_get_change_color (0000)
|
|
000980E0 0070:
|
|
_game_engine_player_has_flag (0000)
|
|
00098150 0050:
|
|
_game_engine_player_depower_active_camo (0000)
|
|
000981A0 00e0:
|
|
_get_place_name (0000)
|
|
00098280 0170:
|
|
_game_engine_get_place (0000)
|
|
000983F0 0040:
|
|
_game_engine_hud_draw_motion_sensor (0000)
|
|
00098430 0020:
|
|
_game_engine_test_flag (0000)
|
|
00098450 0020:
|
|
_game_engine_test_trait (0000)
|
|
00098470 00a0:
|
|
_netgame_flag_verify_no_team_duplicates (0000)
|
|
00098510 0070:
|
|
_netgame_flag_verify_team_range (0000)
|
|
00098580 0070:
|
|
_game_engine_playlist_next (0000)
|
|
000985F0 0090:
|
|
_build_game_variant_slayer (0000)
|
|
00098680 0090:
|
|
_build_game_variant_slayer_pro (0000)
|
|
00098710 0090:
|
|
_build_game_variant_elimination (0000)
|
|
000987A0 0090:
|
|
_build_game_variant_phantoms (0000)
|
|
00098830 0090:
|
|
_build_game_variant_endurance (0000)
|
|
000988C0 0090:
|
|
_build_game_variant_rockets (0000)
|
|
00098950 0090:
|
|
_build_game_variant_snipers (0000)
|
|
000989E0 0090:
|
|
_build_game_variant_team_slayer (0000)
|
|
00098A70 0090:
|
|
_build_game_variant_oddball (0000)
|
|
00098B00 0090:
|
|
_build_game_variant_team_oddball (0000)
|
|
00098B90 0090:
|
|
_build_game_variant_reverse_tag (0000)
|
|
00098C20 00a0:
|
|
_build_game_variant_accumulation (0000)
|
|
00098CC0 00a0:
|
|
_build_game_variant_juggernaut (0000)
|
|
00098D60 00a0:
|
|
_build_game_variant_stalker (0000)
|
|
00098E00 0080:
|
|
_build_game_variant_king (0000)
|
|
00098E80 0090:
|
|
_build_game_variant_king_pro (0000)
|
|
00098F10 0080:
|
|
_build_game_variant_crazy_king (0000)
|
|
00098F90 0090:
|
|
_build_game_variant_team_king (0000)
|
|
00099020 0090:
|
|
_build_game_variant_ctf (0000)
|
|
000990B0 0090:
|
|
_build_game_variant_ctf_pro (0000)
|
|
00099140 0090:
|
|
_build_game_variant_invasion (0000)
|
|
000991D0 0090:
|
|
_build_game_variant_iron_ctf (0000)
|
|
00099260 0090:
|
|
_build_game_variant_race (0000)
|
|
000992F0 0090:
|
|
_build_game_variant_rally (0000)
|
|
00099380 0090:
|
|
_build_game_variant_team_race (0000)
|
|
00099410 0090:
|
|
_build_game_variant_team_rally (0000)
|
|
000994A0 0030:
|
|
_game_engine_override_map_name (0000)
|
|
000994D0 0020:
|
|
_game_engine_override_game_variant (0000)
|
|
000994F0 01a0:
|
|
_rasterize_in_game_score_draw_line (0000)
|
|
00099690 0060:
|
|
_game_engine_hud_draw_messages (0000)
|
|
000996F0 0080:
|
|
_game_engine_player_has_stealth_weapon (0000)
|
|
00099770 0110:
|
|
_game_engine_weapon_fired (0000)
|
|
00099880 0030:
|
|
_test_any_gamepad_button (0000)
|
|
000998B0 00c0:
|
|
_ticks_to_unicode_time_string (0000)
|
|
00099970 0060:
|
|
_game_engine_flag_reset (0000)
|
|
000999D0 01b0:
|
|
_game_engine_variant_cleanup (0000)
|
|
00099B80 0010:
|
|
_game_engine_allow_pause (0000)
|
|
00099B90 0270:
|
|
_game_engine_predict_resources (0000)
|
|
00099E00 0020:
|
|
_game_engine_draw_object_in_motion_sensor (0000)
|
|
00099E20 0020:
|
|
_game_engine_allow_dynamic_lighting (0000)
|
|
00099E40 0020:
|
|
_game_engine_allow_integrated_lights (0000)
|
|
00099E60 0030:
|
|
_game_engine_force_autopickup (0000)
|
|
00099E90 0060:
|
|
_game_engine_initialize (0000)
|
|
00099EF0 0100:
|
|
_multiple_teams_alive (0000)
|
|
00099FF0 00f0:
|
|
_team_has_players (0000)
|
|
0009A0E0 0020:
|
|
_game_engine_should_end_game (0000)
|
|
0009A100 0080:
|
|
_adjust_score_for_ranking (0000)
|
|
0009A180 0230:
|
|
_populate_statistic_buffer (0000)
|
|
0009A3B0 0080:
|
|
_game_engine_get_player_place (0000)
|
|
0009A430 0060:
|
|
_postgame_statistic_get_rating (0000)
|
|
0009A490 01f0:
|
|
_select_players_to_display (0000)
|
|
0009A680 01c0:
|
|
_find_closest_player_index (0000)
|
|
0009A840 0100:
|
|
_internal_rasterize_target_name (0000)
|
|
0009A940 0920:
|
|
_internal_rasterize_score (0000)
|
|
0009B260 0080:
|
|
_random_item (0000)
|
|
0009B2E0 0020:
|
|
_game_engine_get_type (0000)
|
|
0009B300 00a0:
|
|
_match_game_type (0000)
|
|
0009B3A0 0150:
|
|
_game_engine_update_item_spawn (0000)
|
|
0009B4F0 0070:
|
|
_game_engine_update_player_no_shield (0000)
|
|
0009B560 0060:
|
|
_game_engine_update_player_always_invis (0000)
|
|
0009B5C0 00e0:
|
|
_game_engine_update_non_deterministic (0000)
|
|
0009B6A0 0040:
|
|
_multiplayer_message_internal (0000)
|
|
0009B6E0 0090:
|
|
_multiplayer_message (0000)
|
|
0009B770 0010:
|
|
_game_show_score_one_player (0000)
|
|
0009B780 0060:
|
|
_game_show_score_team (0000)
|
|
0009B7E0 00d0:
|
|
_game_show_score_you_ally_enemy (0000)
|
|
0009B8B0 0080:
|
|
_game_show_score_extended (0000)
|
|
0009B930 0020:
|
|
_game_show_score (0000)
|
|
0009B950 0110:
|
|
_find_netgame_flags (0000)
|
|
0009BA60 0040:
|
|
_find_netgame_flag (0000)
|
|
0009BAA0 0130:
|
|
_handle_custom_starting_equipment (0000)
|
|
0009BBD0 0150:
|
|
_game_engine_postspawn_player_update (0000)
|
|
0009BD20 00d0:
|
|
_game_engine_get_damage_multiplier (0000)
|
|
0009BDF0 03e0:
|
|
_game_engine_update_teleporter (0000)
|
|
0009C1D0 0170:
|
|
_game_engine_get_distance_rating_for_spawn (0000)
|
|
0009C340 0120:
|
|
_game_engine_get_friendly_bonus (0000)
|
|
0009C460 00b0:
|
|
_default_starting_location_rate_function (0000)
|
|
0009C510 0060:
|
|
_game_engine_get_starting_location_rating (0000)
|
|
0009C570 0220:
|
|
_game_engine_get_variant_by_name (0000)
|
|
0009C790 0130:
|
|
_game_engine_remap_equipment (0000)
|
|
0009C8C0 0070:
|
|
_game_engine_remap_object_definition (0000)
|
|
0009C930 0140:
|
|
_game_engine_get_state_message (0000)
|
|
0009CA70 00c0:
|
|
_game_engine_did_player_win_default (0000)
|
|
0009CB30 0030:
|
|
_game_engine_did_player_win (0000)
|
|
0009CB60 0080:
|
|
_game_engine_did_team_win (0000)
|
|
0009CBE0 0040:
|
|
_netgame_flag_verify_team_exists (0000)
|
|
0009CC20 0060:
|
|
_netgame_verify_spawn_points (0000)
|
|
0009CC80 0070:
|
|
_netgame_verify_equipment (0000)
|
|
0009CCF0 0280:
|
|
_game_engine_verify_current_map (0000)
|
|
0009CF70 0010:
|
|
_game_engine_playlist_initialize (0000)
|
|
0009CF80 0080:
|
|
_game_engine_initialize_for_new_map (0000)
|
|
0009D000 0140:
|
|
_game_engine_player_added (0000)
|
|
0009D140 0480:
|
|
_game_engine_generate_title_string (0000)
|
|
0009D5C0 09e0:
|
|
_game_engine_post_rasterize_post_game (0000)
|
|
0009DFA0 02f0:
|
|
_game_engine_update (0000)
|
|
0009E290 0340:
|
|
_game_engine_player_killed (0000)
|
|
0009E5D0 00a0:
|
|
_game_engine_nonplayer_post_rasterize (0000)
|
|
0009E670 0350:
|
|
_game_engine_rasterize_in_game_score (0000)
|
|
0009E9C0 0140:
|
|
_game_engine_post_rasterize_in_game (0000)
|
|
0009EB00 0060:
|
|
_game_engine_post_rasterize (0000)
|
|
0025B190 0008:
|
|
__real@3ffe666660000000 (0000)
|
|
0025B198 0002:
|
|
??_C@_11LOCGONAA@?$AA?$AA@ (0000)
|
|
0025B19C 0019:
|
|
??_C@_0BJ@IAIEOKNM@ui?2multiplayer_game_text?$AA@ (0000)
|
|
0025B1B8 001d:
|
|
??_C@_0BN@BLHEDFPC@weapon_is_flag?$CIweapon_index?$CJ?$AA@ (0000)
|
|
0025B1D8 0022:
|
|
??_C@_0CC@HKAICMNO@c?3?2halo?2SOURCE?2game?2game_engine?4@ (0000)
|
|
0025B1FC 003c:
|
|
??_C@_0DM@INOFLLBD@?$CIitem?9?$DOobject?4scale?5?$DO?$DN?50?45f?$CJ?5?$CG?$CG?5@ (0000)
|
|
0025B238 0014:
|
|
??_C@_0BE@PEGHDBAP@NULL?5?$CB?$DN?5game_engine?$AA@ (0000)
|
|
0025B24C 0014:
|
|
??_C@_0BE@CKCLKIJA@variant?5?$CG?$CG?5map_name?$AA@ (0000)
|
|
0025B260 001a:
|
|
??_C@_0BK@EKPIBKAB@dead_player_index?5?$CB?$DN?5NONE?$AA@ (0000)
|
|
0025B27C 000c:
|
|
??_C@_0M@CLNODINK@game_engine?$AA@ (0000)
|
|
0025B288 0008:
|
|
__real@3f53104b57cf969e (0000)
|
|
0025B290 0007:
|
|
??_C@_06IEOJBDIK@object?$AA@ (0000)
|
|
0025B298 0097:
|
|
??_C@_0JH@PNHLNBOL@?$CBallow_pick_up?5?$HM?$HM?5?$CBTEST_FLAG?$CIwea@ (0000)
|
|
0025B330 001a:
|
|
??_C@_0BK@LOIEOAAE@global_goal?$FLindex?$FN?4in_use?$AA@ (0000)
|
|
0025B34C 0004:
|
|
__real@3f2147ae (0000)
|
|
0025B350 0015:
|
|
??_C@_0BF@MOJDLNAG@NONE?5?$CB?$DN?5lookup_index?$AA@ (0000)
|
|
0025B368 001d:
|
|
??_C@_0BN@OIBHHACP@place?4place?5?$DM?5maximum_places?$AA@ (0000)
|
|
0025B388 002c:
|
|
??_C@_0CM@IEEKPKHB@?$CI?$CBall_tied?5?$HM?$HM?5?$CItied?$CJ?$CJ?5?$HM?$HM?5?$CI1?5?$DN?$DN?5g@ (0000)
|
|
0025B3B4 001e:
|
|
??_C@_0BO@HPNHAHOG@levels?2test?2carousel?2carousel?$AA@ (0000)
|
|
0025B3D4 000c:
|
|
??_C@_1M@CDCAHPKB@?$AA?$CF?$AAs?$AA?3?$AA?$CF?$AAs?$AA?$AA@ (0000)
|
|
0025B3E0 0008:
|
|
??_C@_17PMDFIHDK@?$AA0?$AA?$CF?$AAd?$AA?$AA@ (0000)
|
|
0025B3E8 0006:
|
|
??_C@_15KNBIKKIN@?$AA?$CF?$AAd?$AA?$AA@ (0000)
|
|
0025B3F0 0004:
|
|
??_C@_13HOIJIPNN@?$AA?5?$AA?$AA@ (0000)
|
|
0025B3F8 0046:
|
|
??_C@_0EG@GDPJDEHO@NETGAME?5CODE?5FAILURE?3?5game_engin@ (0000)
|
|
0025B440 001b:
|
|
??_C@_0BL@FBPHGMEB@player?9?$DOteam_index?5?$CB?$DN?5NONE?$AA@ (0000)
|
|
0025B45C 0013:
|
|
??_C@_0BD@JDPLED@player_index?$CB?$DNNONE?$AA@ (0000)
|
|
0025B470 002b:
|
|
??_C@_0CL@DLMKKJM@player_count?5?$DM?5MULTIPLAYER_MAXIM@ (0000)
|
|
0025B49C 0022:
|
|
??_C@_0CC@FPLNHLNP@place?$DMMULTIPLAYER_MAXIMUM_PLAYER@ (0000)
|
|
0025B4C0 0013:
|
|
??_C@_0BD@EGHPGJBP@found?5local?5player?$AA@ (0000)
|
|
0025B4D4 001d:
|
|
??_C@_0BN@LMCPNAFN@player_count?$DN?$CFd?0?5maxcount?$DN?$CFd?$AA@ (0000)
|
|
0025B4F8 0008:
|
|
__real@3fc0bf25a0000000 (0000)
|
|
0025B500 0015:
|
|
??_C@_0BF@IHIEPAI@NONE?5?$CB?$DN?5player_index?$AA@ (0000)
|
|
0025B518 0016:
|
|
??_C@_0BG@OJNPNGFL@failed?5to?5teleport?5?$CFd?$AA@ (0000)
|
|
0025B530 0008:
|
|
__real@3fe3333340000000 (0000)
|
|
0025B538 000a:
|
|
??_C@_09DOOCKEDN@team_king?$AA@ (0000)
|
|
0025B544 0005:
|
|
??_C@_04PJOEONHN@king?$AA@ (0000)
|
|
0025B54C 0008:
|
|
??_C@_07LIOCHKOH@ironctf?$AA@ (0000)
|
|
0025B554 0004:
|
|
??_C@_03JHHHHEKD@ctf?$AA@ (0000)
|
|
0025B558 0008:
|
|
??_C@_07JHGHBFJP@oddball?$AA@ (0000)
|
|
0025B560 000d:
|
|
??_C@_0N@JBNPINGA@accumulation?$AA@ (0000)
|
|
0025B570 000d:
|
|
??_C@_0N@CPCFMJOB@team_oddball?$AA@ (0000)
|
|
0025B580 0008:
|
|
??_C@_07IAHNGGND@stalker?$AA@ (0000)
|
|
0025B588 000c:
|
|
??_C@_0M@MLMHALFL@elimination?$AA@ (0000)
|
|
0025B594 000c:
|
|
??_C@_0M@NOCMPHHF@team_slayer?$AA@ (0000)
|
|
0025B5A0 0007:
|
|
??_C@_06CBFFIGEC@slayer?$AA@ (0000)
|
|
0025B5A8 0006:
|
|
??_C@_05GCEFBECL@rally?$AA@ (0000)
|
|
0025B5B0 000a:
|
|
??_C@_09KMEIIIPA@team_race?$AA@ (0000)
|
|
0025B5BC 0005:
|
|
??_C@_04GLEOMBLA@race?$AA@ (0000)
|
|
0025B5C4 0004:
|
|
__real@3f0ccccd (0000)
|
|
0025B5C8 003b:
|
|
??_C@_0DL@MBPNEOKF@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B604 003b:
|
|
??_C@_0DL@FDFHGCGI@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B640 003e:
|
|
??_C@_0DO@GDPJBCBM@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B680 003d:
|
|
??_C@_0DN@GLOHFEFE@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B6C0 003a:
|
|
??_C@_0DK@COLEDOMN@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B700 0047:
|
|
??_C@_0EH@GJGBEMDH@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B748 0047:
|
|
??_C@_0EH@BMGHBGKH@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B790 004a:
|
|
??_C@_0EK@DHDLIGMN@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B7E0 0049:
|
|
??_C@_0EJ@FACIONE@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B830 004f:
|
|
??_C@_0EP@CGHPHLAM@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B880 004f:
|
|
??_C@_0EP@MJLNBADC@NETGAME?5MAP?5FAILURE?3?5failed?5to?5f@ (0000)
|
|
0025B8D0 0039:
|
|
??_C@_0DJ@MPJKFIIJ@NETGAME?5MAP?5FAILURE?3?5duplicate?5r@ (0000)
|
|
0025B90C 0031:
|
|
??_C@_0DB@KDFIELID@NETGAME?5MAP?5FAILURE?3?5missing?5rac@ (0000)
|
|
0025B940 0034:
|
|
??_C@_0DE@KIBANADL@NETGAME?5MAP?5FAILURE?3?5missing?5odd@ (0000)
|
|
0025B974 0031:
|
|
??_C@_0DB@IKODHNKI@NETGAME?5MAP?5FAILURE?3?5missing?5hil@ (0000)
|
|
0025B9A8 0035:
|
|
??_C@_0DF@EHBELDKM@NETGAME?5MAP?5FAILURE?3?5ctf?5flag?5ou@ (0000)
|
|
0025B9E0 0032:
|
|
??_C@_0DC@DCAAPLNK@NETGAME?5MAP?5FAILURE?3?5duplicate?5c@ (0000)
|
|
0025BA14 0030:
|
|
??_C@_0DA@LKKJMHEH@NETGAME?5MAP?5FAILURE?3?5missing?5ctf@ (0000)
|
|
0025BA48 0057:
|
|
??_C@_0FH@ODDMDCIB@failed?5to?5initialize?5custome?5gam@ (0000)
|
|
0025BAA0 000d:
|
|
??_C@_0N@MABFIMPF@title_string?$AA@ (0000)
|
|
0025BAB0 001e:
|
|
??_C@_1BO@NNCACCCJ@?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?$CF?$AAd?$AA?$AA@ (0000)
|
|
0025BAD0 001a:
|
|
??_C@_1BK@JPAKMPFP@?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?$CF?$AAd?$AA?$AA@ (0000)
|
|
0025BAEC 0016:
|
|
??_C@_1BG@EEDCCJKF@?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?$CF?$AAd?$AA?$AA@ (0000)
|
|
0025BB04 0012:
|
|
??_C@_1BC@DGONEHBJ@?$AA?5?$AA?7?$AA?5?$AA?7?$AA?5?$AA?7?$AA?$CF?$AAs?$AA?$AA@ (0000)
|
|
0025BB18 000e:
|
|
??_C@_1O@HAEHHN@?$AA?5?$AA?7?$AA?5?$AA?7?$AA?$CF?$AAs?$AA?$AA@ (0000)
|
|
0025BB28 000a:
|
|
??_C@_19GCIMBEIF@?$AA?5?$AA?7?$AA?$CF?$AAs?$AA?$AA@ (0000)
|
|
0025BB34 0026:
|
|
??_C@_1CG@MDCDIADO@?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?$AA@ (0000)
|
|
0025BB5C 0012:
|
|
??_C@_0BC@FNEEDLLE@ambient_computers?$AA@ (0000)
|
|
0025BB70 0012:
|
|
??_C@_0BC@GOFPLFEH@ambient_machinery?$AA@ (0000)
|
|
0025BB84 000f:
|
|
??_C@_0P@PLBDAHNN@ambient_nature?$AA@ (0000)
|
|
0025BB94 0014:
|
|
??_C@_1BE@EFJKLKFF@?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?7?$AA?$CF?$AAs?$AA?$AA@ (0000)
|
|
0025BBA8 001b:
|
|
??_C@_0BL@NPJONDMM@NONE?5?$CB?$DN?5local_player_index?$AA@ (0000)
|
|
002DE3E0 0020:
|
|
_debug_player_color (0000)
|
|
0043E498 047c:
|
|
_global_goal (0000)
|
|
_global_variant (0400)
|
|
_game_engine (0468)
|
|
_global_autogenerate_list (0474)
|
|
_global_autogenerate_count (0478)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "cseries_windows.h"
|
|
#include "errors.h"
|
|
#include "aim_assist.h"
|
|
#include "game_engine.h"
|
|
#include "game_engine_list.h"
|
|
#include "game_engine_place.h"
|
|
|
|
#include "bitmaps/bitmap_group.h"
|
|
#include "bitmaps/bitmap_group_lookup.h"
|
|
#include "effects/player_effects.h"
|
|
#include "game_allegiance.h"
|
|
#include "game_globals.h"
|
|
#include "interface/interface.h"
|
|
#include "interface/hud.h"
|
|
#include "interface/hud_definitions.h"
|
|
#include "interface/hud_messaging.h"
|
|
#include "interface/player_ui.h"
|
|
#include "interface/terminal.h"
|
|
#include "interface/ui_widget.h"
|
|
#include "input/input.h"
|
|
#include "items/equipment_definitions.h"
|
|
#include "items/item_definitions.h"
|
|
#include "items/weapon_definitions.h"
|
|
#include "items/weapons.h"
|
|
#include "main/console.h"
|
|
#include "main/main.h"
|
|
#include "math/integer_math.h"
|
|
#include "networking/network_game_globals.h"
|
|
#include "networking/network_server_manager.h"
|
|
#include "objects.h"
|
|
#include "objects/damage_effect_definitions.h"
|
|
#include "physics/collision_features.h"
|
|
#include "player_rumble.h"
|
|
#include "players.h"
|
|
#include "players_runtime.h"
|
|
#include "rasterizer/rasterizer.h"
|
|
#include "rasterizer/rasterizer_console_vars.h"
|
|
#include "render/render.h"
|
|
#include "saved games/player_profile.h"
|
|
#include "scenario/scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "sound/sound_classes.h"
|
|
#include "text/draw_string.h"
|
|
#include "text/font_group.h"
|
|
#include "text/text_group.h"
|
|
#include "text/unicode.h"
|
|
#include "units/bipeds.h"
|
|
#include "units/units.h"
|
|
|
|
/* network_game_globals.c's */
|
|
boolean network_game_distributed_client(void);
|
|
/* port/linux/game/network_distributed.c's */
|
|
void network_distributed_player_killed(long *killing_player_index, long *killing_object_index,
|
|
long dead_player_index, boolean *friendly_fire);
|
|
/* port/linux/game/network_damage.c's */
|
|
boolean network_damage_killer_score(long player_index, long *score);
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
/* port: the native builds' session limit (halo_port_limits.h) */
|
|
MULTIPLAYER_MAXIMUM_PLAYERS = HALO_PORT_MAXIMUM_NETWORK_PLAYERS,
|
|
/* ui\multiplayer_game_text only has strings for the first 16 places */
|
|
NUMBER_OF_PLACE_STRINGS = 16,
|
|
};
|
|
|
|
/* scenario_starting_equipment.flags (HCEX names) */
|
|
enum
|
|
{
|
|
_netgame_starting_equipment_flag_no_grenades_bit = 0,
|
|
_netgame_starting_equipment_flag_plasma_greandes_bit, /* (sic) HCEX spelling */
|
|
};
|
|
|
|
/* game_engine_globals.flags and universal_variant.flags bits used only by this file
|
|
(the lower bits are declared with their siblings in game_engine.h) */
|
|
enum
|
|
{
|
|
_game_engine_9_or_more_players_bit = 3,
|
|
_game_engine_game_over_sound_disabled_bit,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_game_variant_generic_starting_equipment_bit = 5,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_game_engine_all = 12,
|
|
_game_engine_all_non_team,
|
|
_game_engine_all_normal,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_multiplayer_sound_game_over = 0x1,
|
|
_multiplayer_sound_double_kill = 0xE,
|
|
_multiplayer_sound_triple_kill,
|
|
_multiplayer_sound_killtacular_kill,
|
|
_multiplayer_sound_running_riot,
|
|
_multiplayer_sound_killing_spree,
|
|
_multiplayer_sound_teleporter_activate = 0x1B,
|
|
_multiplayer_sound_countdown_for_respawn = 0x1D,
|
|
_multiplayer_sound_respawn = 0x1F,
|
|
};
|
|
|
|
enum game_engine_mode
|
|
{
|
|
game_engine_mode_active = 0,
|
|
game_engine_mode_postgame_delay,
|
|
game_engine_mode_postgame_rasterize_delay,
|
|
game_engine_mode_postgame_rasterize,
|
|
};
|
|
|
|
enum postgame_statistic
|
|
{
|
|
_postgame_statistic_ranking = 0,
|
|
_postgame_statistic_score,
|
|
_postgame_statistic_kills,
|
|
_postgame_statistic_assists,
|
|
_postgame_statistic_deaths,
|
|
NUMBER_OF_POSTGAME_STATISTICS,
|
|
};
|
|
|
|
enum game_engine_weapons
|
|
{
|
|
_game_engine_weapons_normal = 0,
|
|
_game_engine_weapons_pistols,
|
|
_game_engine_weapons_assault_rifles,
|
|
_game_engine_weapons_plasma_weapons,
|
|
_game_engine_weapons_sniping,
|
|
_game_engine_weapons_no_sniping,
|
|
_game_engine_weapons_rocket_launchers,
|
|
_game_engine_weapons_shotguns,
|
|
_game_engine_weapons_short_range,
|
|
_game_engine_weapons_human,
|
|
_game_engine_weapons_no_grenades,
|
|
NUMBER_OF_GAME_ENGINE_WEAPON_SETS,
|
|
};
|
|
|
|
enum goal_radar
|
|
{
|
|
_radar_motion_tracker = 0,
|
|
_radar_nav_point,
|
|
_radar_none,
|
|
};
|
|
|
|
enum game_engine_vehicles
|
|
{
|
|
_game_engine_vehicles_default = 0,
|
|
_game_engine_vehicles_none,
|
|
_game_engine_vehicles_warthog,
|
|
_game_engine_vehicles_ghost,
|
|
_game_engine_vehicles_tank,
|
|
NUMBER_OF_GAME_ENGINE_VEHICLE_SETS,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_weapon_list_assault_rifle = 0,
|
|
_weapon_list_flamethrower,
|
|
_weapon_list_gravity_rifle,
|
|
_weapon_list_needler,
|
|
_weapon_list_pistol,
|
|
_weapon_list_plasma_pistol,
|
|
_weapon_list_plasma_rifle,
|
|
_weapon_list_rocket_launcher,
|
|
_weapon_list_shotgun,
|
|
_weapon_list_sniper_rifle,
|
|
_weapon_list_ball,
|
|
_weapon_list_flag,
|
|
_weapon_list_frag_grenade,
|
|
_weapon_list_plasma_grenade,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
#define hud_globals_definition_get(index) \
|
|
((struct hud_globals_definition *)tag_get('hudg', (index)))
|
|
|
|
#define item_collection_definition_get(index) ((struct item_collection_definition *)tag_get(ITEM_COLLECTION_DEFINITION_TAG, index))
|
|
|
|
/* ---------- structures */
|
|
|
|
struct netgame_goal
|
|
{
|
|
real_point3d position;
|
|
boolean in_use;
|
|
long player_index;
|
|
short team_index;
|
|
long ignore_player_index;
|
|
short nav_index;
|
|
};
|
|
|
|
struct game_engine_globals
|
|
{
|
|
unsigned long flags;
|
|
long next_team_index;
|
|
real postgame_timer;
|
|
real postgame_progress;
|
|
long postgame_state;
|
|
real hud_message_timers[MAXIMUM_LOCAL_PLAYERS];
|
|
};
|
|
|
|
struct game_engine_stage
|
|
{
|
|
char map_name[64];
|
|
struct game_variant variant;
|
|
};
|
|
|
|
struct statistic_buffer
|
|
{
|
|
long player_index;
|
|
long score;
|
|
long custom;
|
|
long kills;
|
|
long deaths;
|
|
long assists;
|
|
long place;
|
|
};
|
|
|
|
typedef char verify_statistic_buffer_size[
|
|
sizeof(struct statistic_buffer) == 0x1C ? 1 : -1];
|
|
typedef char verify_netgame_goal_size[sizeof(struct netgame_goal) == 0x20 ? 1 : -1];
|
|
typedef char verify_game_engine_globals_postgame_timer_offset[
|
|
offsetof(struct game_engine_globals, postgame_timer) == 0x8 ? 1 : -1];
|
|
typedef char verify_game_engine_globals_next_team_index_offset[
|
|
offsetof(struct game_engine_globals, next_team_index) == 0x4 ? 1 : -1];
|
|
typedef char verify_game_engine_globals_postgame_progress_offset[
|
|
offsetof(struct game_engine_globals, postgame_progress) == 0xC ? 1 : -1];
|
|
typedef char verify_game_engine_globals_postgame_state_offset[
|
|
offsetof(struct game_engine_globals, postgame_state) == 0x10 ? 1 : -1];
|
|
typedef char verify_game_engine_globals_hud_message_timers_offset[
|
|
offsetof(struct game_engine_globals, hud_message_timers) == 0x14 ? 1 : -1];
|
|
typedef char verify_game_engine_globals_size[
|
|
sizeof(struct game_engine_globals) == 0x24 ? 1 : -1];
|
|
typedef char verify_game_engine_stage_variant_offset[
|
|
offsetof(struct game_engine_stage, variant) == 0x40 ? 1 : -1];
|
|
typedef char verify_game_engine_stage_size[
|
|
sizeof(struct game_engine_stage) == 0xA8 ? 1 : -1];
|
|
|
|
/* ---------- prototypes */
|
|
|
|
void game_engine_playlist_next(
|
|
long parameter0,
|
|
long parameter1,
|
|
long playlist_type);
|
|
|
|
static void game_engine_build_lighting(
|
|
void);
|
|
|
|
static boolean is_place_tied(
|
|
struct statistic_buffer const *entry);
|
|
|
|
int __cdecl sort_statistic_buffer(
|
|
void const *entry0_pointer,
|
|
void const *entry1_pointer);
|
|
|
|
int __cdecl sort_statistic_buffer_ranking(
|
|
void const *entry0_pointer,
|
|
void const *entry1_pointer);
|
|
|
|
static void drawline(
|
|
wchar_t const *string,
|
|
long row_index,
|
|
short justification);
|
|
|
|
static boolean game_engine_infinite_grenades_internal(
|
|
void);
|
|
|
|
static long select_players_to_display(
|
|
enum postgame_statistic statistic,
|
|
long player_index,
|
|
struct statistic_buffer *output,
|
|
long maximum_count);
|
|
|
|
static void netgame_flag_verify_no_team_duplicates(
|
|
short flag_type,
|
|
char const *error_message);
|
|
|
|
static void netgame_flag_verify_team_range(
|
|
short flag_type,
|
|
short minimum_index,
|
|
short maximum_index,
|
|
char const *error_message);
|
|
|
|
static void netgame_verify_spawn_points(
|
|
short game_type,
|
|
short unused_team_index,
|
|
short minimum_count,
|
|
char const *error_message);
|
|
|
|
static void netgame_verify_equipment(
|
|
short game_type,
|
|
char const *error_message);
|
|
|
|
boolean multiple_teams_alive(
|
|
void);
|
|
|
|
static void game_engine_post_rasterize_in_game(
|
|
void);
|
|
|
|
static void game_engine_rasterize_in_game_score(
|
|
long player_index,
|
|
real alpha);
|
|
|
|
static void game_engine_predict_resources(
|
|
void);
|
|
|
|
static void game_engine_verify_current_map(
|
|
void);
|
|
|
|
void game_engine_post_rasterize_post_game(
|
|
void);
|
|
|
|
static boolean internal_rasterize_score(
|
|
long player_index,
|
|
long message,
|
|
long message_data,
|
|
wchar_t *buffer,
|
|
long buffer_size);
|
|
|
|
static void game_engine_update_player_no_shield(
|
|
long player_index);
|
|
|
|
static void game_engine_update_teleporter(
|
|
long player_index);
|
|
|
|
static void game_engine_update_weapons(
|
|
void);
|
|
|
|
static void game_engine_update_item_spawn(
|
|
void);
|
|
|
|
boolean team_has_players(
|
|
long team_index);
|
|
|
|
static struct statistic_buffer game_engine_get_player_place(
|
|
long player_index);
|
|
|
|
long postgame_statistic_get_rating(
|
|
long player_index,
|
|
enum postgame_statistic statistic,
|
|
boolean inverse);
|
|
|
|
long populate_statistic_buffer(
|
|
struct statistic_buffer *statistic_buffer,
|
|
enum postgame_statistic statistic,
|
|
boolean inverse);
|
|
|
|
static boolean find_closest_player_callback(
|
|
long object_index,
|
|
void *custom_data);
|
|
|
|
static long find_closest_player_index(
|
|
long player_index);
|
|
|
|
static void internal_rasterize_target_name(
|
|
long player_index);
|
|
|
|
static long adjust_score_for_ranking(
|
|
long score,
|
|
long player_index);
|
|
|
|
/* ---------- globals */
|
|
|
|
short debug_player_color = NONE;
|
|
|
|
/* port: slayer's kill-in-order target uses the player's absolute index as
|
|
its goal index, so every player needs a goal slot */
|
|
struct netgame_goal global_goal[MAX(32, MULTIPLAYER_MAXIMUM_PLAYERS)] = { 0 };
|
|
struct game_variant global_variant = { 0 };
|
|
struct game_engine *game_engine = NULL;
|
|
|
|
extern struct game_engine_globals game_engine_globals;
|
|
extern struct game_engine_stage global_stage;
|
|
extern long timeout_for_endgame_sound;
|
|
|
|
/* whether a client has had the host's game type state this game (what
|
|
changes in the first, from this machine's own start, it only takes: the
|
|
game types show what changes in the next) */
|
|
static boolean game_engine_network_state_read = FALSE;
|
|
|
|
/* ---------- public code */
|
|
|
|
long game_globals_get_weapon(
|
|
struct game_globals *game_globals,
|
|
long weapon_list_index)
|
|
{
|
|
struct tag_reference *weapon = TAG_BLOCK_GET_ELEMENT(
|
|
&game_globals->weapon_list,
|
|
weapon_list_index,
|
|
struct tag_reference);
|
|
long weapon_definition_index = weapon->index;
|
|
|
|
return weapon_definition_index;
|
|
}
|
|
|
|
long game_engine_get_team_score(
|
|
long team_index)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (player)
|
|
{
|
|
if (player->team_index == team_index)
|
|
return game_engine->get_player_score(iterator.datum_index, TRUE);
|
|
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
real linear_to_non_linear_alpha(
|
|
real linear_alpha)
|
|
{
|
|
real non_linear_alpha = (real)pow((double)linear_alpha, 1.9f);
|
|
|
|
return non_linear_alpha;
|
|
}
|
|
|
|
static void initialize_player_multiplayer_data(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
player->state_message = NONE;
|
|
player->state_message_player_index = NONE;
|
|
player->speed_multiplier = 1.0f;
|
|
player->teleporter_index = NONE;
|
|
player->player_display_index = NONE;
|
|
csmemset(
|
|
&player->statistics.multiplayer_statistics,
|
|
0,
|
|
sizeof(long));
|
|
|
|
return;
|
|
}
|
|
|
|
/* port: English ordinal for a zero-based place past the string list's 16
|
|
("17th", "22nd", "111th"; "tied for 17th" when tied), in a static buffer */
|
|
static wchar_t *place_ordinal_string(
|
|
long place,
|
|
boolean tied)
|
|
{
|
|
static wchar_t string[32];
|
|
long number = place + 1;
|
|
wchar_t const *suffix = L"th";
|
|
|
|
if (number % 100 < 11 || number % 100 > 13)
|
|
{
|
|
switch (number % 10)
|
|
{
|
|
case 1:
|
|
suffix = L"st";
|
|
break;
|
|
case 2:
|
|
suffix = L"nd";
|
|
break;
|
|
case 3:
|
|
suffix = L"rd";
|
|
break;
|
|
}
|
|
}
|
|
|
|
usnprintf(
|
|
string,
|
|
NUMBEROF(string),
|
|
tied ? L"tied for %d%s" : L"%d%s",
|
|
number,
|
|
suffix);
|
|
string[NUMBEROF(string) - 1] = 0;
|
|
|
|
return string;
|
|
}
|
|
|
|
static wchar_t *get_place_string(
|
|
struct statistic_buffer *entry)
|
|
{
|
|
long string_index = PIN(entry->place & 0x7F, 0, 15);
|
|
long string_list_index;
|
|
|
|
/* port: places past the 16th are spelled out instead of clamped (the
|
|
caller's format string marks a tie) */
|
|
if ((entry->place & 0x7F) >= NUMBER_OF_PLACE_STRINGS)
|
|
return place_ordinal_string(entry->place & 0x7F, FALSE);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
return unicode_string_list_get_string(
|
|
string_list_index,
|
|
string_index + 36);
|
|
|
|
return L"";
|
|
}
|
|
|
|
static long game_engine_did_team_win(
|
|
long team_index)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
long result = FALSE;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (player)
|
|
{
|
|
if (player->team_index == team_index)
|
|
{
|
|
result = FALSE;
|
|
if (game_engine)
|
|
{
|
|
if (game_engine->did_player_win)
|
|
result =
|
|
game_engine->did_player_win(iterator.datum_index);
|
|
else
|
|
result = game_engine_did_player_win_default(
|
|
iterator.datum_index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static void game_engine_generate_title_string(
|
|
wchar_t *title_string,
|
|
long player_index)
|
|
{
|
|
wchar_t *secondary_string;
|
|
wchar_t life_string[128];
|
|
wchar_t score_string[256];
|
|
long string_list_index;
|
|
wchar_t *format_string;
|
|
|
|
player_get(player_index);
|
|
secondary_string = L"";
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x36E,
|
|
title_string);
|
|
if (global_variant.universal_variant.lives > 0)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
long remaining_lives =
|
|
global_variant.universal_variant.lives - player->statistics.deaths;
|
|
|
|
switch (remaining_lives)
|
|
{
|
|
case 0:
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
secondary_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x34);
|
|
}
|
|
else
|
|
secondary_string = L"";
|
|
break;
|
|
|
|
case 1:
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
secondary_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x35);
|
|
}
|
|
else
|
|
secondary_string = L"";
|
|
break;
|
|
|
|
default:
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
format_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x36);
|
|
}
|
|
else
|
|
format_string = L"";
|
|
|
|
usnprintf(
|
|
life_string,
|
|
NUMBEROF(life_string),
|
|
format_string,
|
|
remaining_lives);
|
|
life_string[NUMBEROF(life_string) - 1] = 0;
|
|
secondary_string = life_string;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (game_engine_globals.postgame_state == game_engine_mode_postgame_delay)
|
|
{
|
|
long did_player_win = game_engine_did_player_win(player_index);
|
|
boolean has_teams = game_engine_has_teams();
|
|
wchar_t *outcome_string;
|
|
|
|
switch (did_player_win)
|
|
{
|
|
case NONE:
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
outcome_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x37);
|
|
}
|
|
else
|
|
outcome_string = L"";
|
|
ustrncpy(title_string, outcome_string, 80);
|
|
break;
|
|
|
|
case FALSE:
|
|
if (has_teams)
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
outcome_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x38);
|
|
}
|
|
else
|
|
outcome_string = L"";
|
|
ustrncpy(title_string, outcome_string, 80);
|
|
}
|
|
else
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
outcome_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x39);
|
|
}
|
|
else
|
|
outcome_string = L"";
|
|
ustrncpy(title_string, outcome_string, 80);
|
|
}
|
|
break;
|
|
|
|
case TRUE:
|
|
if (has_teams)
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
outcome_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x3A);
|
|
}
|
|
else
|
|
outcome_string = L"";
|
|
ustrncpy(title_string, outcome_string, 80);
|
|
}
|
|
else
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
outcome_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x3B);
|
|
}
|
|
else
|
|
outcome_string = L"";
|
|
ustrncpy(title_string, outcome_string, 80);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (game_engine_has_teams())
|
|
{
|
|
wchar_t team0_name[8];
|
|
wchar_t team1_name[8];
|
|
long team0_score;
|
|
long team1_score;
|
|
|
|
game_engine->format_team_name(
|
|
0,
|
|
team0_name);
|
|
game_engine->format_team_name(
|
|
1,
|
|
team1_name);
|
|
team0_score = game_engine_get_team_score(0);
|
|
team1_score = game_engine_get_team_score(1);
|
|
|
|
if (team0_score > team1_score)
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
format_string =
|
|
unicode_string_list_get_string(string_list_index, 0x3C);
|
|
}
|
|
else
|
|
format_string = L"";
|
|
|
|
usnprintf(
|
|
title_string,
|
|
80,
|
|
format_string,
|
|
team0_name,
|
|
team1_name,
|
|
secondary_string);
|
|
}
|
|
else if (team0_score < team1_score)
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
format_string =
|
|
unicode_string_list_get_string(string_list_index, 0x3D);
|
|
}
|
|
else
|
|
format_string = L"";
|
|
|
|
usnprintf(
|
|
title_string,
|
|
80,
|
|
format_string,
|
|
team1_name,
|
|
team0_name,
|
|
secondary_string);
|
|
}
|
|
else
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
format_string =
|
|
unicode_string_list_get_string(string_list_index, 0x3E);
|
|
}
|
|
else
|
|
format_string = L"";
|
|
|
|
usnprintf(
|
|
title_string,
|
|
80,
|
|
format_string,
|
|
team1_name,
|
|
secondary_string);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct statistic_buffer entry;
|
|
|
|
entry = game_engine_get_player_place(player_index);
|
|
game_engine->format_player_score(player_index, score_string);
|
|
|
|
if (is_place_tied(&entry))
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
format_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x3F);
|
|
}
|
|
else
|
|
format_string = L"";
|
|
|
|
usnprintf(
|
|
title_string,
|
|
80,
|
|
format_string,
|
|
get_place_string(&entry),
|
|
score_string,
|
|
secondary_string);
|
|
}
|
|
else
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
{
|
|
format_string = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x40);
|
|
}
|
|
else
|
|
format_string = L"";
|
|
|
|
usnprintf(
|
|
title_string,
|
|
80,
|
|
format_string,
|
|
get_place_string(&entry),
|
|
score_string,
|
|
secondary_string);
|
|
}
|
|
}
|
|
|
|
title_string[79] = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
static void rasterize_in_game_score_draw_line(
|
|
wchar_t const *string,
|
|
boolean brighten,
|
|
real_argb_color *color,
|
|
long row_index)
|
|
{
|
|
rectangle2d bounds = render.camera.window_bounds;
|
|
short narrow_tab_stops[3];
|
|
short wide_tab_stops[3];
|
|
short *tab_stops;
|
|
boolean splitscreen;
|
|
long font_index;
|
|
|
|
splitscreen = local_player_count() > 1;
|
|
font_index = hud_get_font_index();
|
|
narrow_tab_stops[0] = 80;
|
|
narrow_tab_stops[1] = 125;
|
|
narrow_tab_stops[2] = 200;
|
|
wide_tab_stops[0] = 130;
|
|
wide_tab_stops[1] = 195;
|
|
wide_tab_stops[2] = 315;
|
|
|
|
if (bounds.x1 - bounds.x0 > 320)
|
|
tab_stops = wide_tab_stops;
|
|
else
|
|
tab_stops = narrow_tab_stops;
|
|
|
|
if (row_index)
|
|
draw_string_set_tab_stops(tab_stops, 3);
|
|
else
|
|
draw_string_set_tab_stops(NULL, 0);
|
|
|
|
offset_rectangle2d(
|
|
&bounds,
|
|
-render.camera.viewport_bounds.x0,
|
|
-render.camera.viewport_bounds.y0);
|
|
|
|
if (font_index != NONE)
|
|
{
|
|
struct font_header *font = font_definition_get(font_index);
|
|
long row_offset = 4 * (splitscreen == FALSE) + 4;
|
|
long line_height = font->leading_height;
|
|
|
|
if (!splitscreen)
|
|
line_height += font->descending_height;
|
|
line_height += font->ascending_height;
|
|
|
|
if (brighten)
|
|
{
|
|
color->red += 0.4f;
|
|
color->green += 0.4f;
|
|
color->blue += 0.4f;
|
|
|
|
if (color->red > 1.0f)
|
|
color->red = 1.0f;
|
|
if (color->green > 1.0f)
|
|
color->green = 1.0f;
|
|
if (color->blue > 1.0f)
|
|
color->blue = 1.0f;
|
|
}
|
|
|
|
row_index += row_offset;
|
|
bounds.y0 = (short)(row_index * line_height);
|
|
bounds.y1 = (short)((row_index + 1) * line_height);
|
|
draw_string_set_draw_mode(font_index, NONE, 0, 0, color);
|
|
rasterizer_draw_unicode_string(&bounds, NULL, NULL, 0, string);
|
|
}
|
|
|
|
draw_string_set_tab_stops(NULL, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
long populate_statistic_buffer(
|
|
struct statistic_buffer *statistic_buffer,
|
|
enum postgame_statistic statistic,
|
|
boolean inverse)
|
|
{
|
|
long player_count = 0;
|
|
boolean invert = statistic == _postgame_statistic_deaths ? !inverse : inverse;
|
|
struct data_iterator player_iterator;
|
|
struct player_datum *player;
|
|
long statistic_index;
|
|
|
|
data_iterator_new(&player_iterator, player_data);
|
|
while ((player = (struct player_datum *)data_iterator_next(&player_iterator)) != NULL)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x2C8,
|
|
player_count < MULTIPLAYER_MAXIMUM_PLAYERS);
|
|
if (player_count < MULTIPLAYER_MAXIMUM_PLAYERS)
|
|
{
|
|
statistic_buffer[player_count].player_index = player_iterator.datum_index;
|
|
player_count++;
|
|
}
|
|
}
|
|
|
|
for (statistic_index = 0; statistic_index < player_count; statistic_index++)
|
|
{
|
|
long player_index = statistic_buffer[statistic_index].player_index;
|
|
|
|
player = player_get(player_index);
|
|
switch (statistic)
|
|
{
|
|
case _postgame_statistic_ranking:
|
|
statistic_buffer[statistic_index].custom = 0;
|
|
if (game_engine->get_player_score)
|
|
{
|
|
statistic_buffer[statistic_index].custom = adjust_score_for_ranking(
|
|
game_engine->get_player_score(player_index, FALSE),
|
|
player_index);
|
|
}
|
|
statistic_buffer[statistic_index].kills = player->statistics.kills[0];
|
|
statistic_buffer[statistic_index].assists = player->statistics.assists[0];
|
|
statistic_buffer[statistic_index].deaths = player->statistics.deaths;
|
|
break;
|
|
|
|
case _postgame_statistic_score:
|
|
statistic_buffer[statistic_index].score = 0;
|
|
if (game_engine->get_player_score)
|
|
{
|
|
statistic_buffer[statistic_index].score = adjust_score_for_ranking(
|
|
game_engine->get_player_score(player_index, FALSE),
|
|
player_index);
|
|
}
|
|
break;
|
|
|
|
case _postgame_statistic_kills:
|
|
statistic_buffer[statistic_index].score = player->statistics.kills[0];
|
|
break;
|
|
|
|
case _postgame_statistic_assists:
|
|
statistic_buffer[statistic_index].score = player->statistics.assists[0];
|
|
break;
|
|
|
|
case _postgame_statistic_deaths:
|
|
statistic_buffer[statistic_index].score = player->statistics.deaths;
|
|
break;
|
|
|
|
default:
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x2FD,
|
|
!"unreachable");
|
|
break;
|
|
}
|
|
|
|
if (invert)
|
|
statistic_buffer[statistic_index].score = -statistic_buffer[statistic_index].score;
|
|
}
|
|
|
|
if (statistic == _postgame_statistic_ranking)
|
|
{
|
|
qsort(
|
|
statistic_buffer,
|
|
player_count,
|
|
sizeof(*statistic_buffer),
|
|
sort_statistic_buffer_ranking);
|
|
}
|
|
else
|
|
{
|
|
qsort(
|
|
statistic_buffer,
|
|
player_count,
|
|
sizeof(*statistic_buffer),
|
|
sort_statistic_buffer);
|
|
}
|
|
|
|
for (statistic_index = 0; statistic_index < player_count; statistic_index++)
|
|
{
|
|
if (statistic_index != 0 &&
|
|
sort_statistic_buffer_ranking(
|
|
&statistic_buffer[statistic_index - 1],
|
|
&statistic_buffer[statistic_index]) == 0)
|
|
{
|
|
statistic_buffer[statistic_index - 1].place |= 0x80000000;
|
|
statistic_buffer[statistic_index].place = statistic_buffer[statistic_index - 1].place;
|
|
}
|
|
else
|
|
{
|
|
statistic_buffer[statistic_index].place = statistic_index;
|
|
}
|
|
}
|
|
|
|
return player_count;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static long select_players_to_display(
|
|
enum postgame_statistic statistic,
|
|
long player_index,
|
|
struct statistic_buffer *output,
|
|
long maximum_count)
|
|
{
|
|
struct statistic_buffer statistic_buffer[MULTIPLAYER_MAXIMUM_PLAYERS];
|
|
long player_count = populate_statistic_buffer(statistic_buffer, statistic, FALSE);
|
|
boolean debug = rasterizer_debug_options.pad3 == 'E';
|
|
long local_player_count = 0;
|
|
long statistic_index;
|
|
|
|
if (debug)
|
|
{
|
|
terminal_printf(
|
|
global_real_argb_white,
|
|
"player_count=%d, maxcount=%d",
|
|
player_count,
|
|
maximum_count);
|
|
}
|
|
|
|
for (statistic_index = 0; statistic_index < player_count; statistic_index++)
|
|
{
|
|
struct player_datum *player = player_get(statistic_buffer[statistic_index].player_index);
|
|
|
|
if (player->local_player_index != NONE)
|
|
local_player_count++;
|
|
}
|
|
|
|
if (player_count > maximum_count)
|
|
{
|
|
long outside_range_count = 0;
|
|
struct statistic_buffer outside_range[MAXIMUM_LOCAL_PLAYERS];
|
|
long outside_range_index;
|
|
|
|
for (statistic_index = maximum_count; statistic_index < player_count; statistic_index++)
|
|
{
|
|
struct player_datum *player = player_get(statistic_buffer[statistic_index].player_index);
|
|
|
|
if (player && player->local_player_index != NONE)
|
|
{
|
|
if (debug)
|
|
{
|
|
terminal_printf(
|
|
global_real_argb_white,
|
|
"found local player");
|
|
}
|
|
|
|
outside_range[outside_range_count] = statistic_buffer[statistic_index];
|
|
outside_range_count++;
|
|
}
|
|
}
|
|
|
|
for (outside_range_index = 0; outside_range_index < outside_range_count; outside_range_index++)
|
|
{
|
|
long insertion_index;
|
|
|
|
for (insertion_index = maximum_count - 1; insertion_index >= 0; insertion_index--)
|
|
{
|
|
struct player_datum *player = player_get(statistic_buffer[insertion_index].player_index);
|
|
|
|
if (player->local_player_index == NONE)
|
|
{
|
|
csmemmove(
|
|
&statistic_buffer[insertion_index],
|
|
&statistic_buffer[insertion_index + 1],
|
|
maximum_count * sizeof(struct statistic_buffer) -
|
|
(insertion_index + 1) * sizeof(struct statistic_buffer));
|
|
statistic_buffer[maximum_count - 1] = outside_range[outside_range_index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
csmemcpy(
|
|
output,
|
|
statistic_buffer,
|
|
MIN(maximum_count, player_count) *
|
|
sizeof(struct statistic_buffer));
|
|
return MIN(maximum_count, player_count);
|
|
}
|
|
|
|
static void game_engine_rasterize_in_game_score(
|
|
long player_index,
|
|
real alpha)
|
|
{
|
|
wchar_t row_string[256];
|
|
wchar_t score_string[256];
|
|
struct statistic_buffer entries[6];
|
|
wchar_t title_string[80];
|
|
real_argb_color text_color;
|
|
real_argb_color team_colors[2];
|
|
real_argb_color color;
|
|
boolean has_teams = game_engine_has_teams();
|
|
boolean is_current_player;
|
|
long entry_count;
|
|
long entry_index;
|
|
long string_list_index;
|
|
wchar_t *column_name;
|
|
wchar_t *score_name;
|
|
|
|
game_engine_generate_title_string(title_string, player_index);
|
|
entry_count = select_players_to_display(
|
|
_postgame_statistic_ranking,
|
|
player_index,
|
|
entries,
|
|
NUMBEROF(entries));
|
|
|
|
color.alpha = alpha;
|
|
color.red = 0.7f;
|
|
color.green = 0.7f;
|
|
color.blue = 0.7f;
|
|
rasterize_in_game_score_draw_line(title_string, FALSE, &color, 0);
|
|
|
|
color.red = 0.5f;
|
|
color.green = 0.5f;
|
|
color.blue = 0.5f;
|
|
color.alpha = alpha;
|
|
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
column_name = unicode_string_list_get_string(string_list_index, 0x43);
|
|
else
|
|
column_name = L"";
|
|
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
score_name = unicode_string_list_get_string(string_list_index, 0x44);
|
|
else
|
|
score_name = L"";
|
|
|
|
game_engine->format_score_name(score_string);
|
|
usprintf(row_string, L"\t%s\t%s\t%s", column_name, score_name, score_string);
|
|
rasterize_in_game_score_draw_line(row_string, FALSE, &color, 1);
|
|
|
|
for (entry_index = 0; entry_index < entry_count; entry_index++)
|
|
{
|
|
long entry_player_index = entries[entry_index].player_index;
|
|
struct player_datum *player =
|
|
player_try_and_get(entry_player_index);
|
|
wchar_t *status_string;
|
|
wchar_t *place_string;
|
|
real_argb_color *row_color;
|
|
|
|
is_current_player = player_index == entry_player_index;
|
|
if (player)
|
|
{
|
|
color = *hud_get_text_color(&text_color);
|
|
player = player_get(entry_player_index);
|
|
|
|
team_colors[0].alpha = alpha;
|
|
team_colors[0].red = 0.6f;
|
|
team_colors[0].green = 0.3f;
|
|
team_colors[0].blue = 0.3f;
|
|
team_colors[1].alpha = alpha;
|
|
team_colors[1].red = 0.3f;
|
|
team_colors[1].green = 0.3f;
|
|
team_colors[1].blue = 0.6f;
|
|
color.alpha = alpha;
|
|
|
|
game_engine->format_player_score(
|
|
entry_player_index,
|
|
score_string);
|
|
|
|
if (game_engine_player_is_out_of_lives(entry_player_index))
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
status_string =
|
|
unicode_string_list_get_string(string_list_index, 0x8A);
|
|
else
|
|
status_string = L"";
|
|
}
|
|
else if (player->quit_out_of_game)
|
|
{
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
status_string =
|
|
unicode_string_list_get_string(string_list_index, 0x8B);
|
|
else
|
|
status_string = L"";
|
|
}
|
|
else
|
|
status_string = score_string;
|
|
|
|
place_string = get_place_string(&entries[entry_index]);
|
|
|
|
usprintf(
|
|
row_string,
|
|
L"\t%s\t%s\t%s",
|
|
place_string,
|
|
player->name,
|
|
status_string);
|
|
|
|
if (has_teams)
|
|
row_color = &team_colors[PIN(player->team_index, 0, 1)];
|
|
else
|
|
row_color = &color;
|
|
|
|
rasterize_in_game_score_draw_line(
|
|
row_string,
|
|
is_current_player,
|
|
row_color,
|
|
entry_index + 2);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void drawline(
|
|
wchar_t const *string,
|
|
long row_index,
|
|
short justification)
|
|
{
|
|
rectangle2d bounds = render.camera.window_bounds;
|
|
long line_height = 18;
|
|
long line_spacing = 8;
|
|
|
|
offset_rectangle2d(
|
|
&bounds,
|
|
-render.camera.viewport_bounds.x0,
|
|
-render.camera.viewport_bounds.y0);
|
|
bounds.y0 = (short)(row_index * line_height);
|
|
bounds.y1 = (short)((row_index + 1) * line_height + line_spacing);
|
|
draw_string_set_format(NONE, justification, 0);
|
|
rasterizer_draw_unicode_string(&bounds, NULL, NULL, 0, string);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_post_rasterize_post_game(
|
|
void)
|
|
{
|
|
/* port: sized like every buffer handed to populate_statistic_buffer */
|
|
struct statistic_buffer entries[MULTIPLAYER_MAXIMUM_PLAYERS];
|
|
wchar_t score_string[256];
|
|
wchar_t row_string[256];
|
|
short tab_stops[6];
|
|
real_argb_color winner_color;
|
|
real_argb_color normal_color;
|
|
real_argb_color hilite_color;
|
|
long font_index;
|
|
long entry_count;
|
|
long entry_index;
|
|
|
|
if (!game_engine)
|
|
return;
|
|
|
|
tab_stops[0] = 50;
|
|
tab_stops[1] = 125;
|
|
tab_stops[2] = 250;
|
|
tab_stops[3] = 350;
|
|
tab_stops[4] = 410;
|
|
tab_stops[5] = 500;
|
|
|
|
font_index = hud_globals->messaging.single_player_font.index;
|
|
get_postgame_hilite_colors(
|
|
&winner_color,
|
|
&normal_color,
|
|
&hilite_color);
|
|
draw_string_set_draw_mode(
|
|
font_index,
|
|
NONE,
|
|
2,
|
|
8,
|
|
&winner_color);
|
|
draw_string_set_color(&winner_color);
|
|
draw_string_set_format(NONE, 0, 0);
|
|
|
|
{
|
|
struct hud_globals_definition *hud_definition =
|
|
hud_globals_definition_get(
|
|
interface_get_tag_index(_interface_hud_globals));
|
|
rectangle2d bounds;
|
|
|
|
bounds.x0 = 0;
|
|
bounds.y0 = 0;
|
|
bounds.x1 = 640;
|
|
bounds.y1 = 480;
|
|
if (bitmap_group_try_and_get_bitmap(hud_definition->carnage_report_bitmap.index, 0))
|
|
{
|
|
draw_bitmap_in_rect(
|
|
bitmap_group_try_and_get_bitmap(hud_definition->carnage_report_bitmap.index, 0),
|
|
&bounds,
|
|
&bounds,
|
|
NULL,
|
|
NONE,
|
|
NULL,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
if (global_variant.universal_variant.teams)
|
|
{
|
|
short team_tab_stops[6] = { 50, 200, 300, 350, 410, 500 };
|
|
long team_order[2] = { 0, 1 };
|
|
wchar_t const *team_formats[2];
|
|
long string_list_index;
|
|
long team_row;
|
|
long red_team_won;
|
|
|
|
red_team_won = game_engine_did_team_win(0);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
team_formats[0] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x41) :
|
|
L"";
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
team_formats[1] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x42) :
|
|
L"";
|
|
|
|
if (!red_team_won)
|
|
{
|
|
team_order[0] = 1;
|
|
team_order[1] = 0;
|
|
}
|
|
|
|
draw_string_set_tab_stops(team_tab_stops, NUMBEROF(team_tab_stops));
|
|
for (team_row = 0; team_row < 2; team_row++)
|
|
{
|
|
long team_index = team_order[team_row];
|
|
|
|
game_engine->format_team_name(
|
|
team_index,
|
|
score_string);
|
|
usnprintf(
|
|
row_string,
|
|
NUMBEROF(row_string),
|
|
team_formats[team_index],
|
|
score_string);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, team_row + 4, 0);
|
|
}
|
|
}
|
|
|
|
{
|
|
long string_list_index;
|
|
wchar_t const *column_strings[5];
|
|
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
column_strings[0] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x43) :
|
|
L"";
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
column_strings[1] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x44) :
|
|
L"";
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
column_strings[2] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x45) :
|
|
L"";
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
column_strings[3] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x46) :
|
|
L"";
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
column_strings[4] =
|
|
string_list_index != NONE ?
|
|
unicode_string_list_get_string(string_list_index, 0x47) :
|
|
L"";
|
|
|
|
game_engine->format_score_name(score_string);
|
|
usnprintf(
|
|
row_string,
|
|
NUMBEROF(row_string),
|
|
L"\t%s\t%s\t%s\t%s\t%s\t%s",
|
|
column_strings[0],
|
|
column_strings[1],
|
|
score_string,
|
|
column_strings[2],
|
|
column_strings[3],
|
|
column_strings[4]);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
draw_string_set_tab_stops(tab_stops, NUMBEROF(tab_stops));
|
|
drawline(row_string, 7, 0);
|
|
}
|
|
|
|
entry_count = select_players_to_display(_postgame_statistic_ranking, NONE, entries, 12);
|
|
for (entry_index = 0; entry_index < entry_count; entry_index++)
|
|
{
|
|
long player_index = entries[entry_index].player_index;
|
|
long draw_row = entry_index + 8;
|
|
struct player_datum *player = player_get(player_index);
|
|
real_argb_color team_colors[2];
|
|
wchar_t const *place_string;
|
|
|
|
if (player->local_player_index != NONE)
|
|
draw_string_set_color(&normal_color);
|
|
else
|
|
draw_string_set_color(&winner_color);
|
|
draw_string_set_tab_stops(tab_stops, NUMBEROF(tab_stops));
|
|
place_string = get_place_string(&entries[entry_index]);
|
|
usnprintf(row_string, NUMBEROF(row_string), L" \t%s", place_string);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, draw_row, 0);
|
|
draw_string_set_color(&winner_color);
|
|
|
|
if (global_variant.universal_variant.teams)
|
|
{
|
|
long team_index = player->team_index;
|
|
|
|
team_colors[0].red = 0.8f;
|
|
team_colors[0].green = 0.4f;
|
|
team_colors[0].blue = 0.4f;
|
|
team_colors[0].alpha = 1.0f;
|
|
team_colors[1].red = 0.4f;
|
|
team_colors[1].green = 0.4f;
|
|
team_colors[1].blue = 0.8f;
|
|
team_colors[1].alpha = 1.0f;
|
|
draw_string_set_color(
|
|
&team_colors[PIN(team_index, 0, 1)]);
|
|
}
|
|
usnprintf(row_string, NUMBEROF(row_string), L" \t \t%s", player->name);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, draw_row, 0);
|
|
draw_string_set_color(&winner_color);
|
|
|
|
if (!postgame_statistic_get_rating(player_index, _postgame_statistic_score, FALSE))
|
|
draw_string_set_color(&hilite_color);
|
|
game_engine->format_player_score(
|
|
player_index,
|
|
score_string);
|
|
usnprintf(row_string, NUMBEROF(row_string), L" \t \t \t%s", score_string);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, draw_row, 0);
|
|
draw_string_set_color(&winner_color);
|
|
|
|
if (!postgame_statistic_get_rating(player_index, _postgame_statistic_kills, FALSE))
|
|
draw_string_set_color(&hilite_color);
|
|
usnprintf(
|
|
row_string,
|
|
NUMBEROF(row_string),
|
|
L" \t \t \t \t%d",
|
|
(long)player->statistics.kills[0]);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, draw_row, 0);
|
|
draw_string_set_color(&winner_color);
|
|
|
|
if (!postgame_statistic_get_rating(player_index, _postgame_statistic_assists, FALSE))
|
|
draw_string_set_color(&hilite_color);
|
|
usnprintf(
|
|
row_string,
|
|
NUMBEROF(row_string),
|
|
L" \t \t \t \t \t%d",
|
|
(long)player->statistics.assists[0]);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, draw_row, 0);
|
|
draw_string_set_color(&winner_color);
|
|
|
|
if (!postgame_statistic_get_rating(player_index, _postgame_statistic_deaths, FALSE))
|
|
draw_string_set_color(&hilite_color);
|
|
usnprintf(
|
|
row_string,
|
|
NUMBEROF(row_string),
|
|
L" \t \t \t \t \t \t%d",
|
|
(long)player->statistics.deaths);
|
|
row_string[NUMBEROF(row_string) - 1] = 0;
|
|
drawline(row_string, draw_row, 0);
|
|
draw_string_set_tab_stops(tab_stops, NUMBEROF(tab_stops));
|
|
}
|
|
|
|
{
|
|
real_argb_color prompt_color;
|
|
long string_list_index;
|
|
wchar_t const *prompt;
|
|
struct network_game_server *server;
|
|
rectangle2d bounds;
|
|
|
|
bounds = render.camera.window_bounds;
|
|
prompt_color = winner_color;
|
|
prompt_color.alpha = game_engine_globals.postgame_progress;
|
|
bounds.y0 = 410;
|
|
bounds.x0 = 70;
|
|
offset_rectangle2d(
|
|
&bounds,
|
|
-render.camera.viewport_bounds.x0,
|
|
-render.camera.viewport_bounds.y0);
|
|
draw_string_set_tab_stops(NULL, 0);
|
|
draw_string_set_color(&prompt_color);
|
|
|
|
server = global_network_game_server_get();
|
|
if (server)
|
|
{
|
|
bounds.x0 = 380;
|
|
string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
prompt =
|
|
unicode_string_list_get_string(string_list_index, 0x48);
|
|
else
|
|
prompt = L"";
|
|
draw_string_and_hack_in_icons(
|
|
&bounds,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
prompt,
|
|
FALSE);
|
|
return;
|
|
}
|
|
|
|
bounds.x0 = 520;
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
prompt = unicode_string_list_get_string(string_list_index, 0x49);
|
|
else
|
|
prompt = L"";
|
|
draw_string_and_hack_in_icons(
|
|
&bounds,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
prompt,
|
|
FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static long find_closest_player_index(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
long best_object_index = NONE;
|
|
|
|
if (player->local_player_index != NONE &&
|
|
player_control_get_autoaim_level(player->local_player_index) > 0.0f)
|
|
{
|
|
best_object_index =
|
|
player_control_get_target_object_index(player->local_player_index);
|
|
}
|
|
|
|
if (best_object_index == NONE)
|
|
{
|
|
long object_indices[32];
|
|
real_point3d camera_position;
|
|
real_vector3d facing_direction;
|
|
long object_count;
|
|
long object_index;
|
|
|
|
unit_get_camera_position(player->unit_index, &camera_position);
|
|
player_control_get_facing_direction(
|
|
player->local_player_index,
|
|
&facing_direction);
|
|
object_count = find_objects_from_point_vector(
|
|
&camera_position,
|
|
&facing_direction,
|
|
find_closest_player_callback,
|
|
&player_index,
|
|
NUMBEROF(object_indices),
|
|
object_indices);
|
|
|
|
for (object_index = 0; object_index < object_count; object_index++)
|
|
{
|
|
long candidate_object_index = object_indices[object_index];
|
|
struct unit_datum *candidate = unit_get(candidate_object_index);
|
|
real distance_squared = distance_squared3d(
|
|
&camera_position,
|
|
&candidate->object.position);
|
|
real_point3d target_position;
|
|
real_vector3d target_direction;
|
|
real target_distance;
|
|
real target_angle;
|
|
|
|
if ((candidate->unit.active_camouflage < 1.0f ||
|
|
player->player_display_index == player_index_from_unit_index(candidate_object_index)) &&
|
|
autoaim_compute_target(
|
|
object_indices[object_index],
|
|
&camera_position,
|
|
&facing_direction,
|
|
player->unit_index,
|
|
&target_position,
|
|
&target_direction,
|
|
&target_distance,
|
|
&target_angle) &&
|
|
fabs(target_angle) < 0.13083334267139435 &&
|
|
distance_squared < 400.0f &&
|
|
distance_squared < 900.0f)
|
|
{
|
|
best_object_index = object_indices[object_index];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_object_index != NONE)
|
|
best_object_index = player_index_from_unit_index(best_object_index);
|
|
|
|
return best_object_index;
|
|
}
|
|
|
|
long game_engine_remap_equipment(
|
|
long equipment_definition_index)
|
|
{
|
|
struct equipment_definition *equipment;
|
|
long weapon_list_index;
|
|
boolean remap_equipment;
|
|
|
|
if (equipment_definition_index == NONE)
|
|
equipment = NULL;
|
|
else
|
|
equipment = equipment_definition_get(equipment_definition_index);
|
|
|
|
weapon_list_index =
|
|
weapon_definition_index_to_list_index(equipment_definition_index);
|
|
remap_equipment = weapon_list_index == _weapon_list_frag_grenade ||
|
|
weapon_list_index == _weapon_list_plasma_grenade;
|
|
if (!remap_equipment)
|
|
{
|
|
if (equipment)
|
|
{
|
|
if (equipment->equipment.powerup_type == _equipment_powerup_overshield)
|
|
{
|
|
if (TEST_FLAG(global_variant.universal_variant.flags, _game_variant_no_shields_bit))
|
|
return NONE;
|
|
}
|
|
else if (equipment->equipment.powerup_type == _equipment_powerup_active_camouflage)
|
|
{
|
|
if (TEST_FLAG(global_variant.universal_variant.flags, _game_variant_always_invisible_bit))
|
|
return NONE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (global_variant.universal_variant.weapon_set)
|
|
{
|
|
case _game_engine_weapons_plasma_weapons:
|
|
weapon_list_index = _weapon_list_plasma_grenade;
|
|
break;
|
|
|
|
case _game_engine_weapons_human:
|
|
weapon_list_index = _weapon_list_frag_grenade;
|
|
break;
|
|
|
|
case _game_engine_weapons_no_grenades:
|
|
weapon_list_index = NONE;
|
|
break;
|
|
}
|
|
|
|
if (!TEST_FLAG(game_engine_globals.flags, _game_engine_5_or_more_players_bit))
|
|
{
|
|
if (game_engine_infinite_grenades_internal())
|
|
{
|
|
weapon_list_index = NONE;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(game_engine_globals.flags, _game_engine_9_or_more_players_bit))
|
|
{
|
|
if (real_seed_random(get_global_random_seed_address()) > 0.3f)
|
|
weapon_list_index = NONE;
|
|
}
|
|
else if (TEST_FLAG(game_engine_globals.flags, _game_engine_5_or_more_players_bit))
|
|
{
|
|
if (real_seed_random(get_global_random_seed_address()) > 0.55f)
|
|
weapon_list_index = NONE;
|
|
}
|
|
|
|
}
|
|
|
|
if (remap_equipment)
|
|
return list_index_to_weapon_definition_index(weapon_list_index);
|
|
|
|
return equipment_definition_index;
|
|
}
|
|
|
|
int __cdecl sort_statistic_buffer(
|
|
void const *entry0_pointer,
|
|
void const *entry1_pointer)
|
|
{
|
|
struct statistic_buffer const *entry0 = entry0_pointer;
|
|
struct statistic_buffer const *entry1 = entry1_pointer;
|
|
long result = 0;
|
|
|
|
if (entry0->score > entry1->score)
|
|
result = -1;
|
|
else if (entry1->score > entry0->score)
|
|
result = 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
int __cdecl sort_statistic_buffer_ranking(
|
|
void const *entry0_pointer,
|
|
void const *entry1_pointer)
|
|
{
|
|
struct statistic_buffer const *entry0 = entry0_pointer;
|
|
struct statistic_buffer const *entry1 = entry1_pointer;
|
|
long result = 0;
|
|
|
|
if (entry0->custom > entry1->custom)
|
|
result = -1;
|
|
else if (entry1->custom > entry0->custom)
|
|
result = 1;
|
|
else if (entry0->kills > entry1->kills)
|
|
result = -1;
|
|
else if (entry1->kills > entry0->kills)
|
|
result = 1;
|
|
else if (entry0->deaths > entry1->deaths)
|
|
result = 1;
|
|
else if (entry1->deaths > entry0->deaths)
|
|
result = -1;
|
|
else if (entry0->assists > entry1->assists)
|
|
result = -1;
|
|
else if (entry1->assists > entry0->assists)
|
|
result = 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean is_place_tied(
|
|
struct statistic_buffer const *entry)
|
|
{
|
|
boolean result =
|
|
(entry->place & 0x80000000) > 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long place_get_position(
|
|
struct statistic_buffer const *entry)
|
|
{
|
|
return entry->place & ~FLAG(31);
|
|
}
|
|
|
|
static void get_selected_color(
|
|
real_argb_color *color)
|
|
{
|
|
color->red = 0.98f;
|
|
color->green = 0.96f;
|
|
color->blue = 0.96f;
|
|
color->alpha = 1.0f;
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean can_delete_item(
|
|
long weapon_index)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (weapon_try_and_get(weapon_index) &&
|
|
weapon_is_flag(weapon_index))
|
|
{
|
|
result = FALSE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean multiplayer_message_internal(
|
|
long player_index,
|
|
long message,
|
|
long message_data,
|
|
wchar_t *buffer,
|
|
long buffer_size)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (game_engine->format_message)
|
|
{
|
|
result = game_engine->format_message(
|
|
player_index,
|
|
message,
|
|
message_data,
|
|
buffer,
|
|
buffer_size);
|
|
}
|
|
|
|
if (!result)
|
|
{
|
|
result = internal_rasterize_score(
|
|
player_index,
|
|
message,
|
|
message_data,
|
|
buffer,
|
|
buffer_size);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static void multiplayer_message(
|
|
long player_index,
|
|
long message,
|
|
long message_data)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (player->local_player_index != NONE)
|
|
{
|
|
wchar_t buffer[1024];
|
|
|
|
if (multiplayer_message_internal(
|
|
player_index,
|
|
message,
|
|
message_data,
|
|
buffer,
|
|
NUMBEROF(buffer)))
|
|
{
|
|
buffer[NUMBEROF(buffer) - 1] = 0;
|
|
hud_print_message(player->local_player_index, buffer);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_show_score_one_player(
|
|
long player_index,
|
|
long message,
|
|
long message_data)
|
|
{
|
|
if (message != NONE)
|
|
multiplayer_message(player_index, message, message_data);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_show_score_team(
|
|
long team_index,
|
|
long score)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (player)
|
|
{
|
|
if (player->team_index == team_index)
|
|
{
|
|
game_show_score_one_player(iterator.datum_index, score, NONE);
|
|
}
|
|
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_show_score_you_ally_enemy(
|
|
long player_index,
|
|
long you_score,
|
|
long ally_score,
|
|
long enemy_score,
|
|
long other_player_index)
|
|
{
|
|
struct player_datum *local_player;
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
long score;
|
|
|
|
local_player = player_get(player_index);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xB1A,
|
|
NONE != player_index);
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (player)
|
|
{
|
|
if (iterator.datum_index == player_index)
|
|
{
|
|
if (you_score != NONE)
|
|
{
|
|
game_show_score_one_player(
|
|
iterator.datum_index,
|
|
you_score,
|
|
other_player_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (game_team_is_enemy(
|
|
(short)local_player->team_index,
|
|
(short)player->team_index))
|
|
{
|
|
score = enemy_score;
|
|
}
|
|
else
|
|
score = ally_score;
|
|
|
|
if (score != NONE)
|
|
{
|
|
game_show_score_one_player(
|
|
iterator.datum_index,
|
|
score,
|
|
other_player_index);
|
|
}
|
|
}
|
|
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_show_score_extended(
|
|
long player_index,
|
|
long score,
|
|
long team_index)
|
|
{
|
|
if (player_index != NONE)
|
|
{
|
|
game_show_score_one_player(player_index, score, team_index);
|
|
}
|
|
else
|
|
{
|
|
struct data_iterator iterator;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while (data_iterator_next(&iterator))
|
|
{
|
|
game_show_score_one_player(
|
|
iterator.datum_index,
|
|
score,
|
|
team_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
long players_in_game(
|
|
void)
|
|
{
|
|
struct data_iterator iterator;
|
|
long player_count = 0;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while (data_iterator_next(&iterator))
|
|
player_count++;
|
|
|
|
return player_count;
|
|
}
|
|
|
|
static void game_engine_press_start_to_begin(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static boolean goal_matches_player(
|
|
struct player_datum *player,
|
|
long player_index,
|
|
long goal_index)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (game_engine)
|
|
{
|
|
struct netgame_goal *goal = &global_goal[goal_index];
|
|
|
|
if (game_engine->player_can_see_goal)
|
|
{
|
|
if (goal->in_use)
|
|
result = game_engine->player_can_see_goal(player_index, goal_index);
|
|
}
|
|
else if (
|
|
goal->in_use &&
|
|
(goal->player_index == NONE || player_index == goal->player_index) &&
|
|
(goal->team_index == NONE || player->team_index == goal->team_index) &&
|
|
(goal->ignore_player_index == NONE || player_index != goal->ignore_player_index))
|
|
{
|
|
result = TRUE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean nearby_vehicle(
|
|
long player_index,
|
|
struct player_starting_location const *starting_location)
|
|
{
|
|
struct location location;
|
|
long object_indices[16];
|
|
short object_count;
|
|
short object_index;
|
|
|
|
player_get(player_index);
|
|
scenario_location_from_point(&location, &starting_location->position);
|
|
object_count = objects_in_sphere(
|
|
0,
|
|
0x11F,
|
|
&location,
|
|
&starting_location->position,
|
|
0.1f,
|
|
object_indices,
|
|
NUMBEROF(object_indices));
|
|
|
|
for (object_index = 0; object_index < object_count; object_index++)
|
|
{
|
|
struct object_datum *object = object_get(object_indices[object_index]);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xEE9,
|
|
object);
|
|
|
|
if (object->object.type == _object_type_vehicle)
|
|
{
|
|
struct vehicle_datum *vehicle =
|
|
(struct vehicle_datum *)object_get_and_verify_type(
|
|
object_indices[object_index],
|
|
_object_mask_vehicle);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xEEE,
|
|
vehicle);
|
|
|
|
if (vehicle)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static real game_engine_get_friendly_bonus(
|
|
long player_index,
|
|
real_point3d const *position)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
struct data_iterator iterator;
|
|
struct player_datum *other_player;
|
|
real rating = 0.0f;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (other_player)
|
|
{
|
|
if (player->team_index == other_player->team_index &&
|
|
other_player->unit_index != NONE)
|
|
{
|
|
real_point3d origin;
|
|
real distance;
|
|
|
|
object_get_origin(other_player->unit_index, &origin);
|
|
distance = distance3d(&origin, position);
|
|
if (distance >= 1.0f && distance <= 6.0f)
|
|
{
|
|
rating += (real)pow(
|
|
(double)(1.0f - (distance - 1.0f) * 0.2f),
|
|
(double)0.6f);
|
|
}
|
|
}
|
|
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
if (rating > 3.0f)
|
|
rating = 3.0f;
|
|
|
|
return rating * 3.0f + 1.0f;
|
|
}
|
|
|
|
static real default_starting_location_rate_function(
|
|
long player_index,
|
|
struct player_starting_location const *starting_location)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
real rating = 1.0f;
|
|
|
|
if (game_engine_running() &&
|
|
game_engine_test_flag(0) &&
|
|
player->team_index != starting_location->team_index)
|
|
{
|
|
rating = 0.0f;
|
|
}
|
|
|
|
if (rating > 0.0f)
|
|
{
|
|
rating *= game_engine_get_distance_rating_for_spawn(
|
|
player_index,
|
|
&starting_location->position);
|
|
}
|
|
|
|
if (game_engine_running() && rating > 0.0f && game_engine_has_teams())
|
|
{
|
|
rating *= game_engine_get_friendly_bonus(
|
|
player_index,
|
|
&starting_location->position);
|
|
}
|
|
|
|
if (game_engine_running() && game_engine->starting_location_rating)
|
|
{
|
|
rating *= game_engine->starting_location_rating(
|
|
player_index,
|
|
starting_location);
|
|
}
|
|
|
|
return rating;
|
|
}
|
|
|
|
static boolean test_any_gamepad_button(
|
|
long button_index)
|
|
{
|
|
boolean result = FALSE;
|
|
long gamepad_index;
|
|
|
|
for (gamepad_index = 0; gamepad_index < 4; gamepad_index++)
|
|
{
|
|
struct gamepad_state const *gamepad_state =
|
|
input_get_gamepad_state((short)gamepad_index);
|
|
|
|
if (gamepad_state && gamepad_state->buttons[button_index])
|
|
{
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean match_game_type(
|
|
long game_type,
|
|
long count,
|
|
short const *game_types)
|
|
{
|
|
boolean result;
|
|
long index;
|
|
|
|
if (game_engine)
|
|
{
|
|
result = FALSE;
|
|
for (index = 0; index < count; index++)
|
|
{
|
|
short entry = game_types[index];
|
|
|
|
result = result | (entry == game_type);
|
|
if (entry == _game_engine_all)
|
|
{
|
|
result = result | TRUE;
|
|
}
|
|
else if (entry == _game_engine_all_non_team)
|
|
{
|
|
result = result | (game_type != game_engine_ctf);
|
|
}
|
|
else if (entry == _game_engine_all_normal)
|
|
{
|
|
result = result | (game_type != game_engine_ctf && game_type != game_engine_race);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = TRUE;
|
|
for (index = 0; index < count; index++)
|
|
result = result & (game_types[index] == game_engine_none);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean game_engine_infinite_grenades_internal(
|
|
void)
|
|
{
|
|
boolean infinite_grenades = FALSE;
|
|
|
|
if (!TEST_FLAG(game_engine_globals.flags, _game_engine_5_or_more_players_bit))
|
|
infinite_grenades = TEST_FLAG(global_variant.universal_variant.flags, _game_variant_infinite_grenades_bit);
|
|
|
|
return infinite_grenades;
|
|
}
|
|
|
|
static boolean find_closest_player_callback(
|
|
long object_index,
|
|
void *custom_data)
|
|
{
|
|
long excluded_player = *(long *)custom_data;
|
|
boolean result = FALSE;
|
|
struct object_datum *object = object_get(object_index);
|
|
|
|
if (!TEST_FLAG(object->object.flags, _object_invisible_bit) &&
|
|
TEST_FLAG(_object_mask_biped, object->object.type) &&
|
|
!TEST_FLAG(object->object.damage_flags, _object_dead_bit) &&
|
|
player_index_from_unit_index(object_index) != excluded_player)
|
|
{
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void game_engine_update_purge(
|
|
void)
|
|
{
|
|
long cutoff_time = game_time_get() - 900;
|
|
|
|
/* (a client of the distributed netcode removes items when the host does,
|
|
port/linux/game/network_distributed.c) */
|
|
if (!network_game_distributed_client())
|
|
{
|
|
struct object_iterator item_iterator;
|
|
|
|
object_iterator_new(&item_iterator, _object_mask_item, 0);
|
|
while (object_iterator_next(&item_iterator))
|
|
{
|
|
struct item_datum *item = item_get(item_iterator.index);
|
|
|
|
if (item->item.last_owned_time < cutoff_time &&
|
|
!TEST_FLAG(item->item.flags, _item_attached_to_unit_bit))
|
|
{
|
|
long item_index = item_iterator.index;
|
|
if (can_delete_item(item_index))
|
|
object_delete(item_iterator.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
struct object_iterator biped_iterator;
|
|
|
|
object_iterator_new(&biped_iterator, _object_mask_biped, 0);
|
|
while (object_iterator_next(&biped_iterator))
|
|
{
|
|
struct object_datum *object;
|
|
|
|
cutoff_time = 900;
|
|
object = object_get(biped_iterator.index);
|
|
|
|
if (object->object.idle_ticks > (short)cutoff_time &&
|
|
TEST_FLAG(object->object.damage_flags, _object_dead_bit))
|
|
{
|
|
object_delete(biped_iterator.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_flag_reset(
|
|
long weapon_index,
|
|
real_point3d const *position)
|
|
{
|
|
if (weapon_index != NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
object_set_position(weapon_index, position, global_forward3d, global_up3d);
|
|
object_reset(weapon_index);
|
|
SET_FLAG(weapon->weapon.flags, _weapon_runtime_game_engine_active_bit, FALSE);
|
|
weapon->item.last_owned_time = game_time_get();
|
|
weapon->item.ignore_object_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ticks_to_unicode_time_string(
|
|
long ticks,
|
|
unsigned long character_count,
|
|
wchar_t *string)
|
|
{
|
|
long total_seconds;
|
|
long minutes;
|
|
long seconds;
|
|
wchar_t minute_string[64];
|
|
wchar_t second_string[64];
|
|
|
|
total_seconds = ticks / 30;
|
|
minutes = total_seconds / 60;
|
|
seconds = total_seconds - minutes * 60;
|
|
|
|
if (minutes == 0)
|
|
usnprintf(minute_string, NUMBEROF(minute_string), L" ");
|
|
else
|
|
usnprintf(minute_string, NUMBEROF(minute_string), L"%d", minutes);
|
|
|
|
if (seconds <= 9)
|
|
usnprintf(second_string, NUMBEROF(second_string), L"0%d", seconds);
|
|
else
|
|
usnprintf(second_string, NUMBEROF(second_string), L"%d", seconds);
|
|
|
|
usnprintf(string, character_count, L"%s:%s", minute_string, second_string);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_playlist_initialize(
|
|
void)
|
|
{
|
|
game_engine_playlist_next(0, 0, 2);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_playlist_begin(
|
|
void)
|
|
{
|
|
main_set_multiplayer_map_name(global_stage.map_name);
|
|
game_set_game_variant(&global_stage.variant);
|
|
|
|
if (!network_game_is_active())
|
|
main_reset_map();
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_get_current_stage(
|
|
struct game_variant *variant,
|
|
char *map_name)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\game\\game_engine.c", 0x918, variant && map_name);
|
|
|
|
csmemcpy(variant, &global_stage.variant, sizeof(*variant));
|
|
csstrncpy(map_name, global_stage.map_name, sizeof(global_stage.map_name)-1);
|
|
map_name[sizeof(global_stage.map_name)-1] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
long list_index_to_weapon_definition_index(
|
|
long weapon_list_index)
|
|
{
|
|
long weapon_definition_index = NONE;
|
|
|
|
if (weapon_list_index!=NONE)
|
|
{
|
|
struct game_globals *game_globals = scenario_get_game_globals();
|
|
|
|
weapon_definition_index = game_globals_get_weapon(game_globals, weapon_list_index);
|
|
}
|
|
|
|
return weapon_definition_index;
|
|
}
|
|
|
|
long weapon_definition_index_to_list_index(
|
|
long weapon_definition_index)
|
|
{
|
|
struct game_globals *game_globals = scenario_get_game_globals();
|
|
struct tag_reference *weapons = game_globals->weapon_list.count ?
|
|
TAG_BLOCK_GET_ELEMENT(&game_globals->weapon_list, 0, struct tag_reference) :
|
|
NULL;
|
|
long result = NONE;
|
|
long weapon_list_index;
|
|
|
|
for (weapon_list_index = 0;
|
|
weapon_list_index < game_globals->weapon_list.count;
|
|
weapon_list_index++)
|
|
{
|
|
long definition_index = weapons[weapon_list_index].index;
|
|
|
|
if (weapon_definition_index == definition_index)
|
|
{
|
|
result = weapon_list_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void game_engine_state_message(
|
|
long player_index,
|
|
long state_message,
|
|
long state_message_player_index)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
player->state_message = state_message;
|
|
player->state_message_player_index = state_message_player_index;
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_player_depower_active_camo(
|
|
long player_index)
|
|
{
|
|
if (player_index!=NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (player->unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(player->unit_index);
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_active_camouflaged_bit))
|
|
unit->unit.active_camouflage = 0.5f;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
real get_blink_alpha(
|
|
void)
|
|
{
|
|
long phase = system_milliseconds()%2700;
|
|
|
|
return sin(phase * (3.14159265358979/2700.0));
|
|
}
|
|
|
|
void game_engine_rasterize_message(
|
|
wchar_t const *message,
|
|
real alpha)
|
|
{
|
|
rectangle2d bounds;
|
|
real_argb_color color;
|
|
long font_index;
|
|
long terminal_font_index;
|
|
|
|
if (local_player_count())
|
|
font_index = hud_globals->messaging.multi_player_font.index;
|
|
else
|
|
font_index = hud_globals->messaging.single_player_font.index;
|
|
|
|
bounds = render.camera.window_bounds;
|
|
|
|
terminal_font_index = interface_get_tag_index(_interface_font_terminal);
|
|
draw_string_set_draw_mode(
|
|
terminal_font_index,
|
|
NONE,
|
|
0,
|
|
0,
|
|
global_real_argb_white);
|
|
|
|
color.alpha = alpha;
|
|
color.red = 0.45882353f;
|
|
color.green = 0.7294118f;
|
|
color.blue = 1.0f;
|
|
|
|
offset_rectangle2d(
|
|
&bounds,
|
|
-render.camera.viewport_bounds.x0,
|
|
-render.camera.viewport_bounds.y0);
|
|
|
|
bounds.y1 = (short)((5 * bounds.y0 + bounds.y1) / 6 + 9);
|
|
bounds.y0 = bounds.y1 - 15;
|
|
|
|
draw_string_set_draw_mode(font_index, NONE, 2, 8, &color);
|
|
draw_string_set_color(&color);
|
|
rasterizer_draw_unicode_string(&bounds, NULL, NULL, 0, message);
|
|
draw_string_set_format(NONE, 0, 0);
|
|
draw_string_set_tab_stops(NULL, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_post_rasterize_in_game(
|
|
void)
|
|
{
|
|
long local_player_index;
|
|
long player_index;
|
|
struct player_datum *player;
|
|
struct gamepad_state const *gamepad;
|
|
real fade;
|
|
|
|
local_player_index = render.local_player_index;
|
|
player_index = local_player_get_player_index(local_player_index);
|
|
player = player_get(player_index);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x770,
|
|
NONE != local_player_index);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x771,
|
|
NULL != game_engine);
|
|
|
|
if (game_engine && player)
|
|
internal_rasterize_target_name(player_index);
|
|
|
|
gamepad = input_get_gamepad_state(local_player_index);
|
|
fade = game_engine_globals.hud_message_timers[local_player_index];
|
|
if ((!gamepad ||
|
|
!gamepad->buttons[_gamepad_binary_button_back]) &&
|
|
game_engine_globals.postgame_state != game_engine_mode_postgame_delay)
|
|
{
|
|
/* a frame is no longer a tick (render_interpolation.c): fade in half
|
|
a second, not in 15 frames */
|
|
fade -= 0.06666667f * main_get_seconds_elapsed() * TICKS_PER_SECOND;
|
|
}
|
|
else
|
|
{
|
|
fade += 0.06666667f * main_get_seconds_elapsed() * TICKS_PER_SECOND;
|
|
}
|
|
|
|
fade = PIN(fade, 0.0f, 1.0f);
|
|
if (fade > 0.0f)
|
|
{
|
|
real alpha = linear_to_non_linear_alpha(fade);
|
|
|
|
game_engine_rasterize_in_game_score(player_index, alpha);
|
|
}
|
|
|
|
game_engine_globals.hud_message_timers[local_player_index] = fade;
|
|
|
|
return;
|
|
}
|
|
|
|
long game_engine_player_get_team_index(
|
|
long player_index)
|
|
{
|
|
long team_index = 1;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\game\\game_engine.c", 0xC11, game_engine);
|
|
|
|
if (!game_engine->team_index_override)
|
|
/* port: 0 or 1 (another machine's player has no local player: -1) */
|
|
team_index = PIN(player_get(player_index)->local_player_index % 2, 0, 1);
|
|
|
|
return team_index;
|
|
}
|
|
|
|
void game_engine_update_player_always_invis(
|
|
long player_index)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
if ((TEST_FLAG(global_variant.universal_variant.flags, _game_variant_always_invisible_bit) ||
|
|
game_engine_test_trait(player_index, 1)) &&
|
|
player_get(player_index)->unit_index!=NONE)
|
|
{
|
|
player_handle_powerup_minor(player_index, 0, 15);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_player_has_flag(
|
|
long player_index)
|
|
{
|
|
boolean has_flag = FALSE;
|
|
|
|
if (player_index!=NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (player->unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(player->unit_index);
|
|
long weapon_index;
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; weapon_index++)
|
|
{
|
|
long weapon_object_index = unit->unit.weapon_object_indices[weapon_index];
|
|
|
|
if (weapon_object_index!=NONE && weapon_is_flag(weapon_object_index))
|
|
{
|
|
has_flag = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return has_flag;
|
|
}
|
|
|
|
void game_show_score(
|
|
long player_index,
|
|
long score)
|
|
{
|
|
game_show_score_extended(player_index, score, NONE);
|
|
|
|
return;
|
|
}
|
|
|
|
void get_postgame_hilite_colors(
|
|
real_argb_color *winner_color,
|
|
real_argb_color *normal_color,
|
|
real_argb_color *hilite_color)
|
|
{
|
|
winner_color->red = 0.45882353f;
|
|
winner_color->green = 0.7294118f;
|
|
winner_color->blue = 1.0f;
|
|
winner_color->alpha = 1.0f;
|
|
|
|
normal_color->red = 1.0f;
|
|
normal_color->green = 1.0f;
|
|
normal_color->blue = 0.0f;
|
|
normal_color->alpha = 1.0f;
|
|
|
|
get_selected_color(hilite_color);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_running(
|
|
void)
|
|
{
|
|
boolean running = game_engine!=NULL;
|
|
|
|
return running;
|
|
}
|
|
|
|
/* port: whether the game is over and its scores are shown (where the
|
|
host's button starts the next, game_engine_update) */
|
|
boolean game_engine_showing_postgame(
|
|
void)
|
|
{
|
|
return game_engine != NULL && game_engine_globals.postgame_state == game_engine_mode_postgame_rasterize;
|
|
}
|
|
|
|
boolean game_engine_force_single_screen(
|
|
void)
|
|
{
|
|
boolean force_single_screen = FALSE;
|
|
|
|
if (game_engine &&
|
|
game_engine_globals.postgame_state>=game_engine_mode_postgame_rasterize_delay &&
|
|
game_engine_globals.postgame_state<=game_engine_mode_postgame_rasterize)
|
|
{
|
|
force_single_screen = TRUE;
|
|
}
|
|
|
|
return force_single_screen;
|
|
}
|
|
|
|
void game_engine_dispose(
|
|
void)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
if (game_engine->dispose)
|
|
game_engine->dispose();
|
|
|
|
game_engine = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_update_player_no_shield(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player;
|
|
|
|
if (game_engine_has_shield(player_index))
|
|
return;
|
|
|
|
player = player_get(player_index);
|
|
if (player->unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(player->unit_index);
|
|
|
|
unit->object.shield_vitality = 0.0f;
|
|
unit->object.maximum_shield_vitality = 0.0f;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_build_lighting(
|
|
void)
|
|
{
|
|
long player_count = 0;
|
|
long object_count = 0;
|
|
|
|
{
|
|
struct data_iterator iterator;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while (data_iterator_next(&iterator))
|
|
player_count++;
|
|
}
|
|
|
|
if (global_variant.universal_variant.vehicle_set != _game_engine_vehicles_none)
|
|
{
|
|
if (global_variant.game_engine_index == game_engine_race)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
short flag_index;
|
|
|
|
for (flag_index = 0;
|
|
flag_index < scenario->netgame_flags.count;
|
|
flag_index++)
|
|
{
|
|
struct scenario_netgame_flag *flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
flag_index,
|
|
struct scenario_netgame_flag);
|
|
|
|
if (flag->type == _netgame_flag_race_vehicle)
|
|
object_count++;
|
|
}
|
|
|
|
if (object_count > player_count)
|
|
object_count = player_count;
|
|
}
|
|
else
|
|
{
|
|
struct object_iterator object_iterator;
|
|
|
|
object_iterator_new(
|
|
&object_iterator,
|
|
_object_mask_vehicle,
|
|
0);
|
|
while (object_iterator_next(&object_iterator))
|
|
object_count++;
|
|
}
|
|
}
|
|
|
|
if ((player_count > 8 && object_count >= 2) ||
|
|
player_count >= 13)
|
|
{
|
|
SET_FLAG(game_engine_globals.flags, _game_engine_disable_dynamic_light_bit, TRUE);
|
|
}
|
|
|
|
if (player_count > 4 ||
|
|
object_count >= 4 ||
|
|
TEST_FLAG(game_engine_globals.flags, _game_engine_disable_dynamic_light_bit))
|
|
{
|
|
SET_FLAG(game_engine_globals.flags, _game_engine_disable_integrated_lights_bit, TRUE);
|
|
}
|
|
|
|
if (player_count >= 5)
|
|
SET_FLAG(game_engine_globals.flags, _game_engine_5_or_more_players_bit, TRUE);
|
|
|
|
if (player_count >= 9)
|
|
SET_FLAG(game_engine_globals.flags, _game_engine_9_or_more_players_bit, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_dispose_from_old_map(
|
|
void)
|
|
{
|
|
if (game_engine && game_engine->dispose_from_old_map)
|
|
game_engine->dispose_from_old_map();
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_game_ending(
|
|
void)
|
|
{
|
|
if (game_engine && game_engine->game_ending)
|
|
game_engine->game_ending();
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_game_starting(
|
|
void)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
if (game_engine->game_starting)
|
|
game_engine->game_starting();
|
|
|
|
game_engine_build_lighting();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_statistics_append(
|
|
long statistic)
|
|
{
|
|
if (game_engine && game_engine->statistics_append)
|
|
game_engine->statistics_append(statistic);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_handle_client_message(
|
|
void *message)
|
|
{
|
|
if (game_engine && game_engine->handle_client_message)
|
|
game_engine->handle_client_message(message);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_handle_server_message(
|
|
void *message)
|
|
{
|
|
if (game_engine && game_engine->handle_server_message)
|
|
game_engine->handle_server_message(message);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_post_rasterize_objects(
|
|
void)
|
|
{
|
|
if (game_engine && game_engine->post_rasterize_objects)
|
|
game_engine->post_rasterize_objects();
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_post_rasterize(
|
|
void)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
switch (game_engine_globals.postgame_state)
|
|
{
|
|
case game_engine_mode_active:
|
|
case game_engine_mode_postgame_delay:
|
|
game_engine_post_rasterize_in_game();
|
|
break;
|
|
case game_engine_mode_postgame_rasterize_delay:
|
|
case game_engine_mode_postgame_rasterize:
|
|
game_engine_post_rasterize_post_game();
|
|
break;
|
|
default:
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x7B7,
|
|
!"unreachable");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_nonplayer_post_rasterize(
|
|
void)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
switch (game_engine_globals.postgame_state)
|
|
{
|
|
case game_engine_mode_active:
|
|
case game_engine_mode_postgame_delay:
|
|
game_engine_press_start_to_begin();
|
|
break;
|
|
|
|
case game_engine_mode_postgame_rasterize_delay:
|
|
case game_engine_mode_postgame_rasterize:
|
|
{
|
|
rectangle2d window_bounds;
|
|
long local_player_index;
|
|
|
|
game_engine_post_rasterize_post_game();
|
|
window_bounds.x0 = 0;
|
|
window_bounds.x1 = 640;
|
|
window_bounds.y0 = 0;
|
|
window_bounds.y1 = 480;
|
|
|
|
for (local_player_index = 0;
|
|
local_player_index < MAXIMUM_LOCAL_PLAYERS;
|
|
local_player_index++)
|
|
{
|
|
render_ui_widgets_postgame(
|
|
(short)local_player_index,
|
|
&window_bounds);
|
|
rumble_player_clear((short)local_player_index);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xE3B,
|
|
!"unreachable");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_update_teleporter(
|
|
long player_index)
|
|
{
|
|
static int blocked_message_delay = 0;
|
|
static long fade_function = 0;
|
|
static long screen_flash_type = 6;
|
|
static real max_intensity = 1.0f;
|
|
static real alpha = 0.5f;
|
|
static real red = 0.35f;
|
|
static real green = 1.0f;
|
|
static real blue = 0.35f;
|
|
static real duration = 1.0f;
|
|
struct scenario *scenario = global_scenario_get();
|
|
struct player_datum *player = player_get(player_index);
|
|
struct unit_datum *unit;
|
|
struct scenario_netgame_flag *source_flag;
|
|
struct scenario_netgame_flag *destination_flag;
|
|
real_vector3d forward;
|
|
long source_flag_index;
|
|
long destination_flag_index;
|
|
|
|
if (player->unit_index == NONE)
|
|
return;
|
|
|
|
unit = unit_get(player->unit_index);
|
|
if (player->teleporter_index != NONE)
|
|
{
|
|
struct scenario_netgame_flag *previous_flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
player->teleporter_index,
|
|
struct scenario_netgame_flag);
|
|
|
|
if (distance_squared3d(&previous_flag->position, &unit->object.position) > 1.0f)
|
|
player->teleporter_index = NONE;
|
|
}
|
|
|
|
source_flag_index = NONE;
|
|
find_netgame_flags(
|
|
&unit->object.position,
|
|
0.5f,
|
|
0.0f,
|
|
_netgame_flag_teleporter_source,
|
|
NONE,
|
|
1,
|
|
&source_flag_index);
|
|
if (source_flag_index == NONE ||
|
|
source_flag_index == player->teleporter_index)
|
|
{
|
|
return;
|
|
}
|
|
|
|
source_flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
source_flag_index,
|
|
struct scenario_netgame_flag);
|
|
destination_flag_index = NONE;
|
|
find_netgame_flags(
|
|
NULL,
|
|
0.0f,
|
|
0.0f,
|
|
_netgame_flag_teleporter_target,
|
|
source_flag->team_index,
|
|
1,
|
|
&destination_flag_index);
|
|
if (destination_flag_index != NONE)
|
|
{
|
|
destination_flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
destination_flag_index,
|
|
struct scenario_netgame_flag);
|
|
|
|
{
|
|
struct collision_feature_list features;
|
|
struct player_datum *unit_player;
|
|
real_point3d position;
|
|
real height;
|
|
real radius;
|
|
|
|
unit = unit_get(player->unit_index);
|
|
forward = unit->object.forward;
|
|
unit_player = player_get(player_index);
|
|
biped_get_physics_pill(
|
|
unit_player->unit_index,
|
|
&position,
|
|
&height,
|
|
&radius);
|
|
position = destination_flag->position;
|
|
{
|
|
struct collision_plane point_test_result;
|
|
|
|
if (collision_get_features_in_sphere(
|
|
0x200380,
|
|
&position,
|
|
height + radius * 2.0f,
|
|
height,
|
|
radius,
|
|
NONE,
|
|
&features) &&
|
|
collision_features_test_point(
|
|
&features,
|
|
&position,
|
|
&point_test_result))
|
|
{
|
|
if (point_test_result.object_index != NONE)
|
|
{
|
|
if (TEST_FLAG(
|
|
_object_mask_unit,
|
|
object_get(
|
|
point_test_result.object_index)
|
|
->object.type))
|
|
{
|
|
struct unit_datum *blocking_unit = unit_get(
|
|
point_test_result.object_index);
|
|
if (blocking_unit->unit.player_index != NONE)
|
|
{
|
|
struct player_datum *blocking_player =
|
|
player_get(blocking_unit->unit.player_index);
|
|
blocking_player->is_blocking_teleporter = TRUE;
|
|
blocking_player->telefrag_timeout++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (blocked_message_delay > 0)
|
|
{
|
|
blocked_message_delay--;
|
|
return;
|
|
}
|
|
|
|
blocked_message_delay = 120;
|
|
{
|
|
long string_list_index =
|
|
tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
wchar_t const *message;
|
|
|
|
if (string_list_index != NONE)
|
|
message = unicode_string_list_get_string(
|
|
string_list_index,
|
|
0x65);
|
|
else
|
|
message = L"";
|
|
hud_print_message(
|
|
unit_get_local_player_index(unit_player->unit_index),
|
|
message);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (player->local_player_index != NONE)
|
|
{
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_teleporter_activate);
|
|
if (player->local_player_index != NONE)
|
|
{
|
|
struct screen_flash_definition screen_flash = { 0 };
|
|
|
|
screen_flash.fade_function = fade_function;
|
|
screen_flash.type = screen_flash_type;
|
|
screen_flash.duration = duration;
|
|
screen_flash.priority = 2;
|
|
screen_flash.max_intensity = max_intensity;
|
|
screen_flash.zero_scale_factor = 0.0f;
|
|
screen_flash.screen_flash_color.alpha = alpha;
|
|
screen_flash.screen_flash_color.red = red;
|
|
screen_flash.screen_flash_color.green = green;
|
|
screen_flash.screen_flash_color.blue = blue;
|
|
player_effect_screen_flash(
|
|
player_index,
|
|
&screen_flash,
|
|
1.0f);
|
|
}
|
|
}
|
|
|
|
{
|
|
real angle =
|
|
arctangent(forward.j, forward.i) +
|
|
destination_flag->facing -
|
|
source_flag->facing;
|
|
|
|
forward.i = cosine(angle);
|
|
forward.j = sine(angle);
|
|
normalize3d(&forward);
|
|
object_set_position(
|
|
player->unit_index,
|
|
&destination_flag->position,
|
|
&forward,
|
|
NULL);
|
|
if (player->local_player_index != NONE)
|
|
player_control_set_facing(player->local_player_index, &forward);
|
|
|
|
player->teleporter_index = find_netgame_flag(
|
|
&unit->object.position,
|
|
1.0f,
|
|
0.0f,
|
|
_netgame_flag_teleporter_source,
|
|
NONE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
console_printf(
|
|
FALSE,
|
|
"failed to teleport %d",
|
|
source_flag->team_index);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_update(
|
|
void)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
game_engine_update_multiplayer_sound();
|
|
game_engine_update_purge();
|
|
game_engine_update_weapons();
|
|
game_engine_update_item_spawn();
|
|
|
|
{
|
|
struct data_iterator player_iterator;
|
|
|
|
data_iterator_new(&player_iterator, player_data);
|
|
while (data_iterator_next(&player_iterator))
|
|
{
|
|
game_engine_update_player_no_shield(player_iterator.datum_index);
|
|
game_engine_update_player_always_invis(player_iterator.datum_index);
|
|
game_engine_update_teleporter(player_iterator.datum_index);
|
|
|
|
/* (on a client of the distributed netcode each game type
|
|
skips what the host decides, whose scores, flags, balls and
|
|
hills it has: game_engine_read_network_state) */
|
|
if (game_engine->player_update_each_tick)
|
|
game_engine->player_update_each_tick(player_iterator.datum_index);
|
|
}
|
|
}
|
|
|
|
if (game_engine->update)
|
|
game_engine->update();
|
|
|
|
switch (game_engine_globals.postgame_state)
|
|
{
|
|
case game_engine_mode_active:
|
|
/* (a client of the distributed netcode ends the game when the host
|
|
has) */
|
|
if (
|
|
!network_game_distributed_client() &&
|
|
game_engine_should_end_game())
|
|
{
|
|
game_engine_end_game();
|
|
}
|
|
break;
|
|
|
|
case game_engine_mode_postgame_delay:
|
|
if (game_engine_globals.postgame_timer <= 2.0f &&
|
|
!TEST_FLAG(game_engine_globals.flags, _game_engine_game_over_sound_disabled_bit))
|
|
{
|
|
sound_class_set_gain("", 0.0f, 30);
|
|
sound_class_set_gain("ambient_nature", 0.2f, 30);
|
|
sound_class_set_gain("ambient_machinery", 0.2f, 30);
|
|
sound_class_set_gain("ambient_computers", 0.2f, 30);
|
|
SET_FLAG(game_engine_globals.flags, _game_engine_game_over_sound_disabled_bit, TRUE);
|
|
}
|
|
|
|
game_engine_globals.postgame_timer -= 1.0f / TICKS_PER_SECOND;
|
|
if (game_engine_globals.postgame_timer <= 0.0f)
|
|
{
|
|
struct network_game_server *server;
|
|
|
|
game_engine_globals.postgame_progress = 0.0f;
|
|
game_engine_globals.postgame_state = game_engine_mode_postgame_rasterize_delay;
|
|
game_engine_globals.postgame_timer = 5.0f;
|
|
|
|
{
|
|
struct data_iterator player_iterator;
|
|
struct player_datum *player;
|
|
|
|
data_iterator_new(&player_iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&player_iterator);
|
|
while (player)
|
|
{
|
|
if (player->unit_index != NONE)
|
|
unit_kill(player->unit_index);
|
|
if (player->local_player_index != NONE)
|
|
rumble_player_clear(player->local_player_index);
|
|
|
|
player = (struct player_datum *)data_iterator_next(&player_iterator);
|
|
}
|
|
}
|
|
|
|
{
|
|
struct object_iterator object_iterator;
|
|
|
|
object_iterator_new(
|
|
&object_iterator,
|
|
_object_mask_vehicle,
|
|
0);
|
|
while (object_iterator_next(&object_iterator))
|
|
{
|
|
object_delete(object_iterator.index);
|
|
}
|
|
}
|
|
|
|
server = global_network_game_server_get();
|
|
if (server)
|
|
network_game_server_switch_to_postgame(
|
|
global_network_game_server_get());
|
|
}
|
|
break;
|
|
|
|
case game_engine_mode_postgame_rasterize_delay:
|
|
case game_engine_mode_postgame_rasterize:
|
|
break;
|
|
|
|
default:
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xA03,
|
|
!"unreachable");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
enum game_engine_message
|
|
{
|
|
_game_engine_message_welcome = 0,
|
|
_game_engine_message_killed_by_unknown,
|
|
_game_engine_message_killed_by_biped,
|
|
_game_engine_message_killed_by_vehicle,
|
|
_game_engine_message_killed_by_player,
|
|
_game_engine_message_killed_by_friendly_fire,
|
|
_game_engine_message_killed_by_self,
|
|
_game_engine_message_double_kill,
|
|
_game_engine_message_killed_enemy,
|
|
_game_engine_message_triple_kill,
|
|
_game_engine_message_multi_kill,
|
|
_game_engine_message_five_kills_in_row,
|
|
_game_engine_message_ten_kills_in_a_row,
|
|
_game_engine_message_killed_friendly,
|
|
_game_engine_message_multi_kill_with_score,
|
|
_game_engine_message_triple_kill_with_score,
|
|
_game_engine_message_double_kill_with_score,
|
|
_game_engine_message_ten_kills_in_a_row_with_score,
|
|
_game_engine_message_five_kills_in_row_with_score,
|
|
_game_engine_message_killed_enemy_with_score,
|
|
_game_engine_message_winner,
|
|
_game_engine_message_team_winner,
|
|
_game_engine_message_show_score,
|
|
_game_engine_message_odd_man_out,
|
|
_game_engine_message_out_of_lives,
|
|
_game_engine_message_respawn_timer,
|
|
_game_engine_message_waiting_for_space_to_clear,
|
|
_game_engine_message_player_quit_self,
|
|
_game_engine_message_quit,
|
|
_game_engine_message_press_back_for_score,
|
|
_game_engine_message_time_left,
|
|
NUMBER_OF_GAME_ENGINE_MESSAGES,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_equipment_created_at_rest_bit = 0,
|
|
};
|
|
|
|
void game_engine_player_killed(
|
|
long killing_player_index,
|
|
long killing_object_index,
|
|
long dead_player_index,
|
|
boolean friendly_fire)
|
|
{
|
|
struct player_datum *dead_player = player_get(dead_player_index);
|
|
boolean player_kill;
|
|
boolean same_player;
|
|
boolean valid_players;
|
|
long message = NONE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xA31,
|
|
dead_player_index != NONE);
|
|
|
|
if (!game_engine)
|
|
return;
|
|
|
|
/* the distributed netcode: a client's copy of a death has the host's
|
|
killer (port/linux/game/network_distributed.c) */
|
|
network_distributed_player_killed(&killing_player_index, &killing_object_index, dead_player_index,
|
|
&friendly_fire);
|
|
/* the host's kill of a player who quit, ahead of this client's clock
|
|
(game_update_quit_players has not come to its time yet) */
|
|
if (network_game_distributed_client() && dead_player->quit_out_of_game_time != NONE &&
|
|
killing_player_index == dead_player_index)
|
|
{
|
|
dead_player->quit_out_of_game = TRUE;
|
|
}
|
|
dead_player->death_time = game_time_get();
|
|
if (game_engine->player_killed_player)
|
|
{
|
|
game_engine->player_killed_player(
|
|
killing_player_index,
|
|
killing_object_index,
|
|
dead_player_index,
|
|
friendly_fire);
|
|
}
|
|
|
|
same_player = killing_player_index == dead_player_index;
|
|
if (killing_player_index != NONE && dead_player_index != NONE)
|
|
valid_players = TRUE;
|
|
else
|
|
valid_players = FALSE;
|
|
player_kill = !friendly_fire && valid_players && !same_player;
|
|
|
|
dead_player->respawn_timer =
|
|
dead_player->respawn_penalty + global_variant.universal_variant.respawn_time;
|
|
if (global_variant.universal_variant.respawn_time_growth > 0)
|
|
{
|
|
dead_player->respawn_penalty += global_variant.universal_variant.respawn_time_growth;
|
|
dead_player->respawn_penalty =
|
|
MIN(
|
|
dead_player->respawn_penalty,
|
|
global_variant.universal_variant.respawn_time_growth * 5);
|
|
if (player_kill && killing_player_index != NONE)
|
|
{
|
|
struct player_datum *killing_player =
|
|
player_get(killing_player_index);
|
|
killing_player->respawn_penalty -= global_variant.universal_variant.respawn_time_growth;
|
|
killing_player->respawn_penalty =
|
|
MAX(killing_player->respawn_penalty, 0);
|
|
}
|
|
}
|
|
|
|
if (!player_kill)
|
|
dead_player->respawn_timer += global_variant.universal_variant.suicide_penalty;
|
|
|
|
dead_player->respawn_timer = MAX(dead_player->respawn_timer, 90);
|
|
dead_player = player_get(dead_player_index);
|
|
|
|
if (!dead_player->quit_out_of_game)
|
|
{
|
|
if (killing_player_index == NONE)
|
|
{
|
|
if (killing_object_index == NONE)
|
|
message = _game_engine_message_killed_by_unknown;
|
|
else
|
|
{
|
|
struct object_datum *object = object_get(killing_object_index);
|
|
|
|
unit_try_and_get(killing_object_index);
|
|
switch (object->object.type)
|
|
{
|
|
case _object_type_biped:
|
|
message = _game_engine_message_killed_by_biped;
|
|
break;
|
|
case _object_type_vehicle:
|
|
message = _game_engine_message_killed_by_vehicle;
|
|
break;
|
|
default:
|
|
message = _game_engine_message_killed_by_unknown;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (killing_player_index == dead_player_index)
|
|
message = _game_engine_message_killed_by_self;
|
|
else
|
|
message =
|
|
_game_engine_message_killed_by_player + (friendly_fire != FALSE);
|
|
|
|
game_show_score_extended(
|
|
dead_player_index,
|
|
message,
|
|
killing_player_index);
|
|
}
|
|
else
|
|
{
|
|
struct data_iterator iterator;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while (data_iterator_next(&iterator))
|
|
{
|
|
multiplayer_message(
|
|
iterator.datum_index,
|
|
_game_engine_message_quit,
|
|
dead_player_index);
|
|
}
|
|
}
|
|
|
|
if (message == _game_engine_message_killed_by_friendly_fire)
|
|
{
|
|
if (killing_player_index != NONE)
|
|
{
|
|
multiplayer_message(
|
|
killing_player_index,
|
|
_game_engine_message_killed_friendly,
|
|
dead_player_index);
|
|
}
|
|
else
|
|
{
|
|
struct data_iterator iterator;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while (data_iterator_next(&iterator))
|
|
{
|
|
multiplayer_message(
|
|
iterator.datum_index,
|
|
_game_engine_message_killed_friendly,
|
|
dead_player_index);
|
|
}
|
|
}
|
|
}
|
|
else if (message == _game_engine_message_killed_by_player)
|
|
{
|
|
struct player_datum *killing_player =
|
|
player_get(killing_player_index);
|
|
|
|
if (killing_player->statistics.multiple_kills >= 4)
|
|
{
|
|
message = _game_engine_message_multi_kill;
|
|
}
|
|
else if (killing_player->statistics.multiple_kills == 3)
|
|
message = _game_engine_message_triple_kill;
|
|
else if (killing_player->statistics.multiple_kills == 2)
|
|
message = _game_engine_message_double_kill;
|
|
else if (killing_player->statistics.kills_in_a_row == 5)
|
|
message = _game_engine_message_five_kills_in_row;
|
|
else
|
|
{
|
|
message = (killing_player->statistics.kills_in_a_row % 5)
|
|
? _game_engine_message_killed_enemy
|
|
: _game_engine_message_ten_kills_in_a_row;
|
|
}
|
|
|
|
game_show_score_extended(
|
|
killing_player_index,
|
|
message,
|
|
dead_player_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_update_non_deterministic(
|
|
real delta_seconds)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
switch (game_engine_globals.postgame_state)
|
|
{
|
|
case game_engine_mode_postgame_rasterize_delay:
|
|
rumble_clear_all_now();
|
|
game_engine_globals.postgame_timer -= delta_seconds;
|
|
if (game_engine_globals.postgame_timer <= 0.0f)
|
|
game_engine_globals.postgame_state = game_engine_mode_postgame_rasterize;
|
|
break;
|
|
|
|
case game_engine_mode_postgame_rasterize:
|
|
rumble_clear_all_now();
|
|
game_engine_globals.postgame_progress += delta_seconds;
|
|
if (game_engine_globals.postgame_progress > 1.0f)
|
|
game_engine_globals.postgame_progress = 1.0f;
|
|
|
|
if (test_any_gamepad_button(0) || test_any_gamepad_button(12))
|
|
{
|
|
if (global_network_game_server_get())
|
|
network_game_server_reset_to_pregame(
|
|
global_network_game_server_get());
|
|
}
|
|
else if (test_any_gamepad_button(1) || test_any_gamepad_button(13))
|
|
{
|
|
network_game_abort();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_display_team_indicators(
|
|
void)
|
|
{
|
|
boolean display = FALSE;
|
|
|
|
if (game_engine)
|
|
display = TEST_FLAG(global_variant.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit) && global_variant.universal_variant.teams;
|
|
|
|
return display;
|
|
}
|
|
|
|
boolean game_engine_can_score(
|
|
void)
|
|
{
|
|
boolean can_score = TRUE;
|
|
|
|
if (game_engine)
|
|
can_score = game_engine_globals.postgame_state==game_engine_mode_active;
|
|
|
|
return can_score;
|
|
}
|
|
|
|
boolean game_engine_infinite_grenades(
|
|
long player_index)
|
|
{
|
|
boolean infinite_grenades = FALSE;
|
|
|
|
if (game_engine_running() && player_index!=NONE)
|
|
infinite_grenades = game_engine_infinite_grenades_internal();
|
|
|
|
return infinite_grenades;
|
|
}
|
|
|
|
boolean game_engine_has_shield(
|
|
long player_index)
|
|
{
|
|
boolean has_shield = TRUE;
|
|
|
|
if (game_engine && player_index!=NONE)
|
|
has_shield = !TEST_FLAG(global_variant.universal_variant.flags, _game_variant_no_shields_bit);
|
|
|
|
return has_shield;
|
|
}
|
|
|
|
boolean game_engine_draw_object_in_motion_sensor(
|
|
long unit_index)
|
|
{
|
|
boolean draw_object = TRUE;
|
|
|
|
if (game_engine)
|
|
draw_object = TEST_FLAG(global_variant.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit);
|
|
|
|
return draw_object;
|
|
}
|
|
|
|
boolean game_engine_hud_draw_motion_sensor(
|
|
long player_index)
|
|
{
|
|
boolean draw_motion_sensor = TRUE;
|
|
|
|
if (game_engine)
|
|
{
|
|
boolean default_draw_motion_sensor;
|
|
|
|
draw_motion_sensor =
|
|
TEST_FLAG(global_variant.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit);
|
|
default_draw_motion_sensor = global_variant.universal_variant.goal_radar == _radar_motion_tracker;
|
|
|
|
if (global_variant.game_engine_index == game_engine_slayer && !global_variant.game_engine_variant.slayer.kill_in_order)
|
|
default_draw_motion_sensor = FALSE;
|
|
|
|
draw_motion_sensor |= default_draw_motion_sensor;
|
|
}
|
|
|
|
return draw_motion_sensor;
|
|
}
|
|
|
|
boolean game_engine_player_has_stealth_weapon(
|
|
long player_index)
|
|
{
|
|
boolean has_stealth_weapon = FALSE;
|
|
|
|
if (player_index != NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
if (player->unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(player->unit_index);
|
|
long current_weapon_index = unit->unit.current_weapon_index;
|
|
|
|
if (current_weapon_index != NONE)
|
|
{
|
|
long weapon_index =
|
|
unit->unit.weapon_object_indices[current_weapon_index];
|
|
if (weapon_index != NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
if (weapon->definition_index != NONE)
|
|
{
|
|
struct weapon_definition *weapon_definition =
|
|
weapon_definition_get(weapon->definition_index);
|
|
has_stealth_weapon = TEST_FLAG(
|
|
weapon_definition->weapon.flags,
|
|
_weapon_does_not_depower_active_camo_bit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return has_stealth_weapon;
|
|
}
|
|
|
|
void game_engine_weapon_fired(
|
|
long player_index)
|
|
{
|
|
if (game_engine && player_index != NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
long unit_index = player->unit_index;
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long weapon_index = unit_inventory_get_weapon(
|
|
unit_index,
|
|
unit_get(unit_index)->unit.current_weapon_index);
|
|
real active_camouflage_decrease = 0.1f;
|
|
|
|
if (game_engine_player_has_stealth_weapon(player_index))
|
|
{
|
|
active_camouflage_decrease = 0.0f;
|
|
}
|
|
else if (weapon_index != NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition =
|
|
weapon_definition_get(weapon->definition_index);
|
|
|
|
if (0.0f != weapon_definition->weapon.active_camo_ding)
|
|
active_camouflage_decrease =
|
|
weapon_definition->weapon.active_camo_ding;
|
|
}
|
|
|
|
if (unit->unit.active_camouflage >= 0.05f)
|
|
{
|
|
unit->unit.active_camouflage -= active_camouflage_decrease;
|
|
unit->unit.cause_for_camo_regrowth = 1;
|
|
unit->unit.active_camouflage =
|
|
MAX(0.05f, unit->unit.active_camouflage);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
wchar_t *get_place_name(
|
|
struct game_engine_place place)
|
|
{
|
|
long lookup_index;
|
|
long string_list_index;
|
|
|
|
/* port: a session can have more places than the string list names */
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x1316,
|
|
place.place < MULTIPLAYER_MAXIMUM_PLAYERS,
|
|
"place.place < maximum_places");
|
|
|
|
if (TEST_FLAG(place.flags, _place_two_groups) && TEST_FLAG(place.flags, _place_tied))
|
|
lookup_index = 35;
|
|
else if (TEST_FLAG(place.flags, _place_two_groups) && place.place == 0)
|
|
lookup_index = 33;
|
|
else if (TEST_FLAG(place.flags, _place_two_groups) && place.place == 1)
|
|
lookup_index = 34;
|
|
else if (TEST_FLAG(place.flags, _place_all_tied))
|
|
lookup_index = 32;
|
|
/* port: places past the 16th are spelled out; a tie is marked like the
|
|
string list's tied places */
|
|
else if (place.place >= NUMBER_OF_PLACE_STRINGS)
|
|
return place_ordinal_string(place.place, TEST_FLAG(place.flags, _place_tied));
|
|
else
|
|
{
|
|
lookup_index = place.place;
|
|
if (TEST_FLAG(place.flags, _place_tied))
|
|
lookup_index += 16;
|
|
}
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x1331,
|
|
lookup_index != NONE,
|
|
"NONE != lookup_index");
|
|
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
return unicode_string_list_get_string(
|
|
string_list_index,
|
|
lookup_index + 0x66);
|
|
|
|
return L"";
|
|
}
|
|
|
|
long find_netgame_flags(
|
|
real_point3d const *position,
|
|
real radius,
|
|
real height,
|
|
short type,
|
|
short team_index,
|
|
long maximum_count,
|
|
long *flag_indices)
|
|
{
|
|
real radius_squared = radius * radius;
|
|
long found_count = 0;
|
|
short flag_index;
|
|
struct scenario *scenario;
|
|
|
|
scenario = global_scenario_get();
|
|
|
|
for (flag_index = 0; flag_index < scenario->netgame_flags.count; flag_index++)
|
|
{
|
|
struct scenario_netgame_flag *flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
flag_index,
|
|
struct scenario_netgame_flag);
|
|
|
|
if (type != NONE && type != flag->type)
|
|
continue;
|
|
|
|
if (team_index != NONE && team_index != flag->team_index)
|
|
continue;
|
|
|
|
if (position)
|
|
{
|
|
if (radius >= 0.0f &&
|
|
distance_squared3d(&flag->position, position) > radius_squared)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (height > 0.0f &&
|
|
fabs(flag->position.z - position->z) > height)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (found_count < maximum_count)
|
|
flag_indices[found_count++] = flag_index;
|
|
}
|
|
|
|
return found_count;
|
|
}
|
|
|
|
long find_netgame_flag(
|
|
real_point3d const *position,
|
|
real radius,
|
|
real height,
|
|
short type,
|
|
short team_index)
|
|
{
|
|
long flag_index = NONE;
|
|
|
|
find_netgame_flags(
|
|
position,
|
|
radius,
|
|
height,
|
|
type,
|
|
team_index,
|
|
1,
|
|
&flag_index);
|
|
|
|
return flag_index;
|
|
}
|
|
|
|
/* playlist data: in the Aug-15-2001 build the playlist functions were the only users of these two globals;
|
|
this build no longer reads them. their definition point here, with the playlist code, is inferred (the
|
|
Aug-2001 users and the later builds' layout), not attested */
|
|
void *global_autogenerate_list = NULL;
|
|
long global_autogenerate_count = 0;
|
|
|
|
void game_engine_playlist_next(
|
|
long parameter0,
|
|
long parameter1,
|
|
long playlist_type)
|
|
{
|
|
char *map_name;
|
|
struct game_variant variant;
|
|
|
|
csstrcpy(global_stage.map_name, "levels\\test\\carousel\\carousel");
|
|
map_name = main_get_multiplayer_map_name();
|
|
if (map_name && map_name[0])
|
|
{
|
|
csstrncpy(global_stage.map_name, map_name, sizeof(global_stage.map_name) - 1);
|
|
global_stage.map_name[sizeof(global_stage.map_name) - 1] = 0;
|
|
}
|
|
|
|
if (player_ui_game_variant_specified(&variant))
|
|
csmemcpy(&global_stage.variant, &variant, sizeof(global_stage.variant));
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_should_end_game(
|
|
void)
|
|
{
|
|
boolean should_end_game = FALSE;
|
|
|
|
if (game_engine && !multiple_teams_alive())
|
|
should_end_game = TRUE;
|
|
|
|
return should_end_game;
|
|
}
|
|
|
|
void game_engine_clear_goal_position(
|
|
short goal_index)
|
|
{
|
|
/* port: a goal of the table only */
|
|
if (!VALID_INDEX(goal_index, NUMBEROF(global_goal)))
|
|
return;
|
|
csmemset(&global_goal[goal_index], 0, sizeof(struct netgame_goal));
|
|
|
|
return;
|
|
}
|
|
|
|
long get_flag_definition_index(
|
|
void)
|
|
{
|
|
struct game_globals *game_globals;
|
|
struct game_globals_multiplayer_information *multiplayer_information;
|
|
|
|
global_scenario_get();
|
|
game_globals = scenario_get_game_globals();
|
|
multiplayer_information = TAG_BLOCK_GET_ELEMENT(
|
|
&game_globals->multiplayer_information,
|
|
0,
|
|
struct game_globals_multiplayer_information);
|
|
|
|
return multiplayer_information->flag.index;
|
|
}
|
|
|
|
long get_ball_definition_index(
|
|
void)
|
|
{
|
|
struct game_globals *game_globals;
|
|
struct game_globals_multiplayer_information *multiplayer_information;
|
|
|
|
global_scenario_get();
|
|
game_globals = scenario_get_game_globals();
|
|
multiplayer_information = TAG_BLOCK_GET_ELEMENT(
|
|
&game_globals->multiplayer_information,
|
|
0,
|
|
struct game_globals_multiplayer_information);
|
|
|
|
return multiplayer_information->ball.index;
|
|
}
|
|
|
|
void game_engine_override_map_name(
|
|
char const *map_name)
|
|
{
|
|
if (map_name && map_name[0])
|
|
csstrncpy(global_stage.map_name, map_name, sizeof(global_stage.map_name)-1);
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_override_game_variant(
|
|
struct game_variant const *variant)
|
|
{
|
|
if (variant)
|
|
csmemcpy(&global_stage.variant, variant, sizeof(global_stage.variant));
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_switch_to_postgame(
|
|
void)
|
|
{
|
|
if (game_engine_globals.postgame_state==game_engine_mode_active)
|
|
{
|
|
if (global_network_game_server_get())
|
|
{
|
|
game_engine_globals.postgame_state = game_engine_mode_postgame_delay;
|
|
game_engine_globals.postgame_timer = 7.0f;
|
|
}
|
|
else
|
|
{
|
|
game_engine_globals.postgame_state = game_engine_mode_postgame_rasterize;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_load_stage(
|
|
char const *map_name)
|
|
{
|
|
if (!map_name || csstrcmp(global_stage.map_name, map_name))
|
|
main_set_multiplayer_map_name(global_stage.map_name);
|
|
|
|
game_set_game_variant(&global_stage.variant);
|
|
if (!network_game_is_active())
|
|
main_reset_map();
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_end_game(
|
|
void)
|
|
{
|
|
if (game_engine_globals.postgame_state==game_engine_mode_active)
|
|
{
|
|
game_engine_globals.postgame_state = game_engine_mode_postgame_delay;
|
|
game_engine_globals.postgame_timer = 7.0f;
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_game_over);
|
|
ui_widgets_close_all();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_player_damaged_player(
|
|
long damaging_player_index,
|
|
long dead_player_index,
|
|
boolean damage_type)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\game\\game_engine.c", 0xA20, dead_player_index != NONE);
|
|
|
|
/* (a client of the distributed netcode replaying the host's damage has
|
|
the host's game type state, game_engine_read_network_state) */
|
|
if (network_game_distributed_client())
|
|
return;
|
|
if (game_engine && game_engine->player_damaged_player)
|
|
game_engine->player_damaged_player(damaging_player_index, dead_player_index, damage_type);
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean game_engine_player_is_odd_man_out(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
boolean result = FALSE;
|
|
|
|
if (global_variant.universal_variant.odd_man_out && player->unit_index==NONE)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *other_player;
|
|
|
|
result = TRUE;
|
|
data_iterator_new(&iterator, player_data);
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (other_player)
|
|
{
|
|
if (other_player->unit_index==NONE &&
|
|
other_player!=player &&
|
|
(other_player->death_time>player->death_time ||
|
|
(other_player->death_time==player->death_time &&
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index)<
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(iterator.datum_index))))
|
|
{
|
|
result = FALSE;
|
|
}
|
|
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean game_engine_player_is_out_of_lives(
|
|
long player_index)
|
|
{
|
|
boolean out_of_lives = FALSE;
|
|
|
|
if (global_variant.universal_variant.lives>0)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (player->unit_index==NONE && player->statistics.deaths>=global_variant.universal_variant.lives)
|
|
out_of_lives = TRUE;
|
|
}
|
|
|
|
return out_of_lives;
|
|
}
|
|
|
|
short game_engine_player_get_custom_motion_sensor_positions(
|
|
long player_index,
|
|
real_point2d *positions,
|
|
byte *goal_indices,
|
|
short maximum_count)
|
|
{
|
|
short count = 0;
|
|
|
|
if (game_engine && global_variant.universal_variant.goal_radar == _radar_motion_tracker && player_index != NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
long goal_index;
|
|
|
|
/* port: every goal, one per player in the native builds' sessions */
|
|
for (goal_index = 0; goal_index < (long)NUMBEROF(global_goal); goal_index++)
|
|
{
|
|
struct netgame_goal *goal = &global_goal[goal_index];
|
|
|
|
if (goal_matches_player(player, player_index, goal_index) && count < maximum_count)
|
|
{
|
|
goal_indices[count] = goal_index;
|
|
positions[count].x = goal->position.x;
|
|
positions[count].y = goal->position.y;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void game_engine_render_nav_points(
|
|
short local_player_index)
|
|
{
|
|
if (game_engine &&
|
|
global_variant.universal_variant.goal_radar == _radar_nav_point &&
|
|
local_player_index != NONE)
|
|
{
|
|
long player_index = local_player_get_player_index(local_player_index);
|
|
|
|
if (player_index != NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
long goal_index;
|
|
|
|
if (player->unit_index != NONE)
|
|
{
|
|
real_point3d head_position;
|
|
|
|
unit_get_head_position(player->unit_index, &head_position);
|
|
/* port: every goal, one per player in the native builds' sessions */
|
|
for (goal_index = 0; goal_index < (long)NUMBEROF(global_goal); goal_index++)
|
|
{
|
|
if (goal_matches_player(player, player_index, goal_index))
|
|
{
|
|
short render_type = hud_get_nav_point_render_type(
|
|
local_player_index,
|
|
&head_position,
|
|
&global_goal[goal_index].position,
|
|
NONE);
|
|
|
|
custom_render_nav_point(
|
|
local_player_index,
|
|
&global_goal[goal_index].position,
|
|
global_goal[goal_index].nav_index,
|
|
render_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_hud_draw_messages(
|
|
long player_index)
|
|
{
|
|
boolean draw_messages = TRUE;
|
|
|
|
if (game_engine && player_index!=NONE)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (player->local_player_index!=NONE &&
|
|
game_engine_globals.hud_message_timers[player->local_player_index]>0.0f)
|
|
{
|
|
draw_messages = FALSE;
|
|
}
|
|
}
|
|
|
|
return draw_messages;
|
|
}
|
|
|
|
boolean game_engine_force_autopickup(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
boolean force_autopickup = FALSE;
|
|
|
|
if (game_engine && global_variant.game_engine_index==game_engine_ctf && weapon_is_flag(weapon_index))
|
|
force_autopickup = TRUE;
|
|
|
|
return force_autopickup;
|
|
}
|
|
|
|
boolean game_engine_allow_pick_up(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
boolean allow_pick_up = TRUE;
|
|
|
|
if (game_engine && game_engine->allow_pick_up)
|
|
allow_pick_up = game_engine->allow_pick_up(unit_index, weapon_index);
|
|
|
|
return allow_pick_up;
|
|
}
|
|
|
|
boolean game_engine_picking_up(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
boolean allow_pick_up = TRUE;
|
|
|
|
if (game_engine)
|
|
{
|
|
struct weapon_datum *weapon = weapon_try_and_get(weapon_index);
|
|
|
|
if (weapon && weapon_is_flag(weapon_index))
|
|
{
|
|
if (TEST_FLAG(weapon->weapon.flags, _weapon_runtime_game_engine_active_bit))
|
|
{
|
|
SET_FLAG(weapon->weapon.flags, _weapon_runtime_game_engine_active_bit, FALSE);
|
|
|
|
if (game_engine->weapon_dropped)
|
|
game_engine->weapon_dropped(weapon_index);
|
|
}
|
|
|
|
SET_FLAG(weapon->weapon.flags, _weapon_runtime_game_engine_active_bit, TRUE);
|
|
|
|
if (game_engine->picking_up)
|
|
{
|
|
long player_index = player_index_from_unit_index(unit_index);
|
|
|
|
allow_pick_up = game_engine->picking_up(
|
|
weapon_index,
|
|
player_index);
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xF58,
|
|
!allow_pick_up ||
|
|
!TEST_FLAG(weapon->weapon.flags, _weapon_must_be_readied_bit) ||
|
|
!unit_has_weapon_with_flag(unit_index, _weapon_must_be_readied_bit));
|
|
}
|
|
}
|
|
|
|
return allow_pick_up;
|
|
}
|
|
|
|
boolean game_engine_test_flag(
|
|
long flag)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (game_engine && game_engine->test_flag)
|
|
result = game_engine->test_flag(flag);
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean game_engine_test_trait(
|
|
long trait,
|
|
long value)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (game_engine && game_engine->test_trait)
|
|
result = game_engine->test_trait(trait, value);
|
|
|
|
return result;
|
|
}
|
|
|
|
void game_engine_prespawn_player_update(
|
|
long player_index)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
if (game_engine->prespawn_player_update)
|
|
game_engine->prespawn_player_update(player_index);
|
|
else
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
/* port: 0 or 1 (another machine's player has no local player: -1) */
|
|
player->team_index = PIN(player->local_player_index % 2, 0, 1);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static struct statistic_buffer game_engine_get_player_place(
|
|
long player_index)
|
|
{
|
|
struct statistic_buffer statistic_buffer[MULTIPLAYER_MAXIMUM_PLAYERS];
|
|
long place = 0;
|
|
|
|
populate_statistic_buffer(statistic_buffer, _postgame_statistic_ranking, FALSE);
|
|
while (TRUE)
|
|
{
|
|
if (statistic_buffer[place].player_index == player_index)
|
|
break;
|
|
|
|
place++;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x362,
|
|
place<MULTIPLAYER_MAXIMUM_PLAYERS);
|
|
}
|
|
|
|
return statistic_buffer[place];
|
|
}
|
|
|
|
struct game_variant *build_game_variant_king(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.score_to_win = 2;
|
|
result.flags = 1;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_index = game_engine_king;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.king.moving_hill = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_crazy_king(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.score_to_win = 2;
|
|
result.game_engine_variant.king.moving_hill = TRUE;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_king;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_slayer(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_slayer;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.universal_variant.goal_radar = _radar_motion_tracker;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 15;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.slayer.no_death_bonus = FALSE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = FALSE;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
result.flags = 1;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_team_slayer(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.respawn_time = 300;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.game_engine_index = game_engine_slayer;
|
|
result.universal_variant.teams = TRUE;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.universal_variant.goal_radar = _radar_motion_tracker;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 50;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.slayer.no_death_bonus = FALSE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = FALSE;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_elimination(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.game_engine_index = game_engine_slayer;
|
|
result.universal_variant.lives = 1;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.universal_variant.goal_radar = _radar_motion_tracker;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 25;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.slayer.no_death_bonus = FALSE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = FALSE;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_phantoms(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.game_engine_index = game_engine_slayer;
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.game_engine_variant.slayer.no_death_bonus = TRUE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = TRUE;
|
|
result.game_engine_variant.slayer.kill_in_order = TRUE;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 10;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_endurance(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.game_engine_index = game_engine_slayer;
|
|
result.universal_variant.odd_man_out = TRUE;
|
|
result.universal_variant.respawn_time_growth = 300;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.goal_radar = _radar_motion_tracker;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 5;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.score_to_win = 10;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.slayer.no_death_bonus = FALSE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = FALSE;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_rockets(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, TRUE);
|
|
result.game_engine_index = game_engine_slayer;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 25;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_rocket_launchers;
|
|
result.game_engine_variant.slayer.no_death_bonus = FALSE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = FALSE;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_snipers(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, TRUE);
|
|
result.game_engine_index = game_engine_slayer;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 150;
|
|
result.universal_variant.score_to_win = 15;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_sniping;
|
|
result.game_engine_variant.slayer.no_death_bonus = FALSE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = FALSE;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_oddball(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_none;
|
|
result.game_engine_variant.oddball.ball_spawn_count = 1;
|
|
result.game_engine_variant.oddball.ball_spawn_delay = TRUE;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_oddball;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 2;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.oddball.random_start = FALSE;
|
|
result.game_engine_variant.oddball.oddball_ball_type = 0;
|
|
result.game_engine_variant.oddball.trait_with_ball = 0;
|
|
result.game_engine_variant.oddball.trait_without_ball = 0;
|
|
result.game_engine_variant.oddball.speed_with_ball = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_team_oddball(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result;
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, TRUE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.teams = TRUE;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_none;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.game_engine_variant.oddball.ball_spawn_count = 1;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.flags = 1;
|
|
result.game_engine_variant.oddball.ball_spawn_delay = FALSE;
|
|
result.game_engine_variant.oddball.random_start = FALSE;
|
|
result.game_engine_variant.oddball.oddball_ball_type = 0;
|
|
result.game_engine_variant.oddball.trait_with_ball = 0;
|
|
result.game_engine_variant.oddball.trait_without_ball = 0;
|
|
result.game_engine_variant.oddball.speed_with_ball = 0;
|
|
result.game_engine_index = game_engine_oddball;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.respawn_time = 300;
|
|
result.universal_variant.score_to_win = 2;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_reverse_tag(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_none;
|
|
result.game_engine_variant.oddball.ball_spawn_count = 1;
|
|
result.game_engine_variant.oddball.ball_spawn_delay = TRUE;
|
|
result.game_engine_variant.oddball.oddball_ball_type = 1;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_oddball;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 2;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.oddball.random_start = FALSE;
|
|
result.game_engine_variant.oddball.trait_with_ball = 0;
|
|
result.game_engine_variant.oddball.trait_without_ball = 0;
|
|
result.game_engine_variant.oddball.speed_with_ball = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_accumulation(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_none;
|
|
result.game_engine_variant.oddball.oddball_ball_type = 1;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_oddball;
|
|
result.universal_variant.goal_radar = _radar_none;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 5;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.oddball.ball_spawn_count = 16;
|
|
result.game_engine_variant.oddball.ball_spawn_delay = FALSE;
|
|
result.game_engine_variant.oddball.random_start = FALSE;
|
|
result.game_engine_variant.oddball.trait_with_ball = 0;
|
|
result.game_engine_variant.oddball.trait_without_ball = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_juggernaut(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.oddball.ball_spawn_count = 1;
|
|
result.game_engine_variant.oddball.trait_with_ball = 2;
|
|
result.flags = 1;
|
|
result.game_engine_variant.oddball.oddball_ball_type = 2;
|
|
result.game_engine_index = game_engine_oddball;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 10;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.oddball.ball_spawn_delay = FALSE;
|
|
result.game_engine_variant.oddball.random_start = FALSE;
|
|
result.game_engine_variant.oddball.trait_without_ball = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_stalker(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.respawn_time = 150;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.game_engine_variant.oddball.ball_spawn_count = 1;
|
|
result.game_engine_index = game_engine_oddball;
|
|
result.game_engine_variant.oddball.trait_with_ball = 1;
|
|
result.game_engine_variant.oddball.trait_without_ball = 3;
|
|
result.flags = 1;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.oddball.oddball_ball_type = 2;
|
|
result.game_engine_variant.oddball.speed_with_ball = 2;
|
|
result.universal_variant.goal_radar = _radar_motion_tracker;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 10;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.oddball.ball_spawn_delay = FALSE;
|
|
result.game_engine_variant.oddball.random_start = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_king_pro(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, TRUE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.score_to_win = 2;
|
|
result.flags = 1;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_index = game_engine_king;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 300;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.suicide_penalty = 450;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.king.moving_hill = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_team_king(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.score_to_win = 2;
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.universal_variant.teams = TRUE;
|
|
result.game_engine_variant.king.moving_hill = TRUE;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_king;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 300;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_slayer_pro(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_slayer;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, TRUE);
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.slayer.no_death_bonus = TRUE;
|
|
result.game_engine_variant.slayer.no_kill_penalty = TRUE;
|
|
result.flags = 1;
|
|
result.universal_variant.goal_radar = _radar_motion_tracker;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 25;
|
|
result.universal_variant.suicide_penalty = 450;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.slayer.kill_in_order = FALSE;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_ctf(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_ctf;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.teams = TRUE;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 300;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 3;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.ctf.assault = FALSE;
|
|
result.game_engine_variant.ctf.flag_at_home_to_score = FALSE;
|
|
result.game_engine_variant.ctf.flag_must_reset = FALSE;
|
|
result.game_engine_variant.ctf.reset_on_capture = FALSE;
|
|
result.game_engine_variant.ctf.single_flag_time = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_ctf_pro(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_ctf;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, TRUE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.teams = TRUE;
|
|
result.game_engine_variant.ctf.flag_at_home_to_score = TRUE;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 300;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 3;
|
|
result.universal_variant.suicide_penalty = 450;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.ctf.assault = FALSE;
|
|
result.game_engine_variant.ctf.flag_must_reset = FALSE;
|
|
result.game_engine_variant.ctf.reset_on_capture = FALSE;
|
|
result.game_engine_variant.ctf.single_flag_time = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_invasion(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_ctf;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.teams = TRUE;
|
|
result.game_engine_variant.ctf.assault = TRUE;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 5;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 3;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.ctf.flag_at_home_to_score = FALSE;
|
|
result.game_engine_variant.ctf.flag_must_reset = FALSE;
|
|
result.game_engine_variant.ctf.reset_on_capture = FALSE;
|
|
result.game_engine_variant.ctf.single_flag_time = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_iron_ctf(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_ctf;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.teams = TRUE;
|
|
result.game_engine_variant.ctf.flag_must_reset = TRUE;
|
|
result.flags = 1;
|
|
result.universal_variant.health = 2.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 450;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 3;
|
|
result.universal_variant.suicide_penalty = 150;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_tank;
|
|
result.game_engine_variant.ctf.assault = FALSE;
|
|
result.game_engine_variant.ctf.flag_at_home_to_score = FALSE;
|
|
result.game_engine_variant.ctf.reset_on_capture = FALSE;
|
|
result.game_engine_variant.ctf.single_flag_time = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_race(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_race;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 3;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.race.race_type = 0;
|
|
result.game_engine_variant.race.team_scoring = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_rally(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.race.race_type = 2;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_race;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 15;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.teams = FALSE;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.race.team_scoring = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_team_race(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.teams = TRUE;
|
|
result.flags = 1;
|
|
result.game_engine_index = game_engine_race;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.score_to_win = 3;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.game_engine_variant.race.race_type = 0;
|
|
result.game_engine_variant.race.team_scoring = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
struct game_variant *build_game_variant_team_rally(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant result = { 0 };
|
|
|
|
result.game_engine_index = game_engine_race;
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_draw_object_in_motion_sensor_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_allow_friendly_navpoints_bit, TRUE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_infinite_grenades_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_no_shields_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_always_invisible_bit, FALSE);
|
|
SET_FLAG(result.universal_variant.flags, _game_variant_generic_starting_equipment_bit, FALSE);
|
|
result.universal_variant.goal_radar = _radar_nav_point;
|
|
result.universal_variant.score_to_win = 5;
|
|
result.universal_variant.teams = TRUE;
|
|
result.universal_variant.vehicle_set = _game_engine_vehicles_warthog;
|
|
result.flags = 1;
|
|
result.game_engine_variant.race.race_type = 2;
|
|
result.universal_variant.health = 1.0f;
|
|
result.universal_variant.lives = 0;
|
|
result.universal_variant.odd_man_out = FALSE;
|
|
result.universal_variant.respawn_time = 0;
|
|
result.universal_variant.respawn_time_growth = 0;
|
|
result.universal_variant.suicide_penalty = 300;
|
|
result.universal_variant.weapon_set = _game_engine_weapons_normal;
|
|
result.game_engine_variant.race.team_scoring = 0;
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
long postgame_statistic_get_rating(
|
|
long player_index,
|
|
enum postgame_statistic statistic,
|
|
boolean inverse)
|
|
{
|
|
struct statistic_buffer statistic_buffer[MULTIPLAYER_MAXIMUM_PLAYERS];
|
|
long player_count = 0;
|
|
long rating;
|
|
long statistic_index;
|
|
|
|
player_count = populate_statistic_buffer(statistic_buffer, statistic, inverse);
|
|
rating = 0;
|
|
if (statistic_buffer[0].player_index != player_index)
|
|
{
|
|
for (statistic_index = 1; statistic_index < player_count; statistic_index++)
|
|
{
|
|
if (statistic_buffer[statistic_index - 1].score != statistic_buffer[statistic_index].score)
|
|
rating++;
|
|
|
|
if (statistic_buffer[statistic_index].player_index == player_index)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rating;
|
|
}
|
|
|
|
long game_engine_did_player_win(
|
|
long player_index)
|
|
{
|
|
long result = 0;
|
|
|
|
if (game_engine)
|
|
{
|
|
if (game_engine->did_player_win)
|
|
result = game_engine->did_player_win(player_index);
|
|
else
|
|
result = game_engine_did_player_win_default(player_index);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean multiple_teams_alive(
|
|
void)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (players_in_game() <= 1)
|
|
return TRUE;
|
|
else
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
long team_index = NONE;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (player)
|
|
{
|
|
if (!player->quit_out_of_game &&
|
|
(player->unit_index != NONE ||
|
|
(!game_engine_player_is_odd_man_out(iterator.datum_index) &&
|
|
!game_engine_player_is_out_of_lives(
|
|
iterator.datum_index))))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x1BE,
|
|
player->team_index != NONE);
|
|
|
|
if (player->team_index != team_index)
|
|
{
|
|
if (team_index != NONE)
|
|
{
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
|
|
team_index = player->team_index;
|
|
}
|
|
}
|
|
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean team_has_players(
|
|
long team_index)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (players_in_game() <= 1)
|
|
{
|
|
result = TRUE;
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (player)
|
|
{
|
|
if (!player->quit_out_of_game &&
|
|
(player->unit_index != NONE ||
|
|
(!game_engine_player_is_odd_man_out(iterator.datum_index) &&
|
|
!game_engine_player_is_out_of_lives(
|
|
iterator.datum_index))))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x1F7,
|
|
player->team_index != NONE);
|
|
|
|
/* Preserve the retail first-eligible-player behavior. */
|
|
if (player->team_index == team_index)
|
|
result = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
}
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
long game_engine_did_player_win_default(
|
|
long player_index)
|
|
{
|
|
long result = 0;
|
|
|
|
if (global_variant.universal_variant.teams)
|
|
{
|
|
long team0_score = game_engine_get_team_score(0);
|
|
long team1_score = game_engine_get_team_score(1);
|
|
long winning_team_index;
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (multiple_teams_alive())
|
|
winning_team_index = team0_score == team1_score ? NONE : (team0_score > team1_score ? 0 : 1);
|
|
else
|
|
winning_team_index = team_has_players(0) ? 0 : 1;
|
|
|
|
if (winning_team_index == NONE)
|
|
result = NONE;
|
|
else if (player->team_index == winning_team_index)
|
|
result = TRUE;
|
|
else
|
|
result = FALSE;
|
|
}
|
|
else
|
|
{
|
|
struct statistic_buffer entry;
|
|
|
|
entry = game_engine_get_player_place(player_index);
|
|
if (is_place_tied(&entry) &&
|
|
!place_get_position(&entry))
|
|
result = NONE;
|
|
else if (!place_get_position(&entry))
|
|
result = TRUE;
|
|
else
|
|
result = FALSE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static long game_engine_get_type(
|
|
void)
|
|
{
|
|
long game_engine_type = NONE;
|
|
|
|
if (game_engine)
|
|
game_engine_type = game_engine->type;
|
|
|
|
return game_engine_type;
|
|
}
|
|
|
|
struct game_variant *game_engine_get_variant(
|
|
void)
|
|
{
|
|
return &global_variant;
|
|
}
|
|
|
|
struct game_variant *game_engine_get_variant_by_name(
|
|
struct game_variant *variant,
|
|
char const *name)
|
|
{
|
|
struct game_variant temporary;
|
|
struct game_variant result;
|
|
|
|
csmemset(&result, 0, sizeof(result));
|
|
|
|
if (csstrcmp(name, "race") == 0)
|
|
result = *build_game_variant_race(&temporary);
|
|
else if (csstrcmp(name, "team_race") == 0)
|
|
result = *build_game_variant_team_race(&temporary);
|
|
else if (csstrcmp(name, "rally") == 0)
|
|
result = *build_game_variant_rally(&temporary);
|
|
else if (csstrcmp(name, "slayer") == 0)
|
|
result = *build_game_variant_slayer(&temporary);
|
|
else if (csstrcmp(name, "team_slayer") == 0)
|
|
result = *build_game_variant_team_slayer(&temporary);
|
|
else if (csstrcmp(name, "elimination") == 0)
|
|
result = *build_game_variant_elimination(&temporary);
|
|
else if (csstrcmp(name, "stalker") == 0)
|
|
result = *build_game_variant_stalker(&temporary);
|
|
else if (csstrcmp(name, "team_oddball") == 0)
|
|
result = *build_game_variant_team_oddball(&temporary);
|
|
else if (csstrcmp(name, "accumulation") == 0)
|
|
result = *build_game_variant_accumulation(&temporary);
|
|
else if (csstrcmp(name, "oddball") == 0)
|
|
result = *build_game_variant_oddball(&temporary);
|
|
else if (csstrcmp(name, "ctf") == 0)
|
|
result = *build_game_variant_ctf(&temporary);
|
|
else if (csstrcmp(name, "ironctf") == 0)
|
|
result = *build_game_variant_iron_ctf(&temporary);
|
|
else if (csstrcmp(name, "king") == 0)
|
|
result = *build_game_variant_king(&temporary);
|
|
else if (csstrcmp(name, "team_king") == 0)
|
|
result = *build_game_variant_team_king(&temporary);
|
|
|
|
*variant = result;
|
|
|
|
return variant;
|
|
}
|
|
|
|
boolean game_engine_get_goal_in_use(
|
|
short goal_index)
|
|
{
|
|
return global_goal[goal_index].in_use;
|
|
}
|
|
|
|
real_point3d *game_engine_get_goal_position(
|
|
real_point3d *position,
|
|
short index)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0xFE2,
|
|
global_goal[index].in_use);
|
|
*position = global_goal[index].position;
|
|
|
|
return position;
|
|
}
|
|
|
|
void game_engine_set_goal_position(
|
|
short goal_index,
|
|
real_point3d const *position,
|
|
real height,
|
|
char const *name,
|
|
long player_index,
|
|
short team_index,
|
|
long ignore_player_index)
|
|
{
|
|
/* port: a goal of the table only */
|
|
if (!VALID_INDEX(goal_index, NUMBEROF(global_goal)))
|
|
return;
|
|
global_goal[goal_index].ignore_player_index = ignore_player_index;
|
|
global_goal[goal_index].nav_index = find_nav_point(name);
|
|
global_goal[goal_index].in_use = TRUE;
|
|
global_goal[goal_index].position = *position;
|
|
global_goal[goal_index].team_index = team_index;
|
|
global_goal[goal_index].position.z += height + 0.63f;
|
|
global_goal[goal_index].player_index = player_index;
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_man_out(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
boolean man_out = player->quit_out_of_game ||
|
|
game_engine_player_is_out_of_lives(player_index) ||
|
|
game_engine_player_is_odd_man_out(player_index);
|
|
|
|
return man_out;
|
|
}
|
|
|
|
real_rgb_color *game_engine_player_get_change_color(
|
|
real_rgb_color *change_color,
|
|
long player_index)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
real_rgb_color result;
|
|
|
|
if (global_variant.universal_variant.teams)
|
|
{
|
|
if (player->team_index == 0)
|
|
result = *global_real_rgb_red;
|
|
else
|
|
result = *global_real_rgb_blue;
|
|
}
|
|
else
|
|
{
|
|
long color_index = player->network_player_data.primary_color_index;
|
|
|
|
if (debug_player_color != NONE)
|
|
color_index = debug_player_color;
|
|
|
|
result = player_profile_get_rgb_color(color_index);
|
|
}
|
|
|
|
*change_color = result;
|
|
|
|
return change_color;
|
|
}
|
|
|
|
boolean game_engine_has_teams(
|
|
void)
|
|
{
|
|
boolean has_teams = FALSE;
|
|
|
|
if (game_engine)
|
|
has_teams = global_variant.universal_variant.teams;
|
|
|
|
return has_teams;
|
|
}
|
|
|
|
boolean game_engine_allow_pause(
|
|
void)
|
|
{
|
|
return game_engine_globals.postgame_state==game_engine_mode_active;
|
|
}
|
|
|
|
boolean game_engine_allow_dynamic_lighting(
|
|
void)
|
|
{
|
|
boolean allow_dynamic_lighting = TRUE;
|
|
|
|
if (game_engine)
|
|
allow_dynamic_lighting = !TEST_FLAG(game_engine_globals.flags, _game_engine_disable_dynamic_light_bit);
|
|
|
|
return allow_dynamic_lighting;
|
|
}
|
|
|
|
boolean game_engine_allow_integrated_lights(
|
|
long object_index)
|
|
{
|
|
boolean allow_integrated_lights = TRUE;
|
|
|
|
if (game_engine)
|
|
allow_integrated_lights = !TEST_FLAG(game_engine_globals.flags, _game_engine_disable_integrated_lights_bit);
|
|
|
|
return allow_integrated_lights;
|
|
}
|
|
|
|
void game_engine_variant_cleanup(
|
|
struct game_variant *variant)
|
|
{
|
|
struct game_variant original = *variant;
|
|
|
|
variant->human_readable_game_description[NUMBEROF(variant->human_readable_game_description) - 1] = 0;
|
|
variant->game_engine_index = PIN(variant->game_engine_index, 1, 5);
|
|
variant->universal_variant.teams = !!variant->universal_variant.teams;
|
|
variant->universal_variant.odd_man_out = !!variant->universal_variant.odd_man_out;
|
|
variant->universal_variant.respawn_time_growth = MAX(variant->universal_variant.respawn_time_growth, 0);
|
|
variant->universal_variant.respawn_time = MAX(variant->universal_variant.respawn_time, 0);
|
|
variant->universal_variant.suicide_penalty = MAX(variant->universal_variant.suicide_penalty, 0);
|
|
variant->universal_variant.lives = MAX(variant->universal_variant.lives, 0);
|
|
variant->universal_variant.health = PIN(variant->universal_variant.health, 0.25f, 4.0f);
|
|
variant->universal_variant.weapon_set = PIN(variant->universal_variant.weapon_set, 0, NUMBER_OF_GAME_ENGINE_WEAPON_SETS - 1);
|
|
variant->universal_variant.vehicle_set = PIN(variant->universal_variant.vehicle_set, 0, NUMBER_OF_GAME_ENGINE_VEHICLE_SETS - 1);
|
|
|
|
switch (variant->game_engine_index)
|
|
{
|
|
case game_engine_ctf:
|
|
variant->game_engine_variant.ctf.assault = !!variant->game_engine_variant.ctf.assault;
|
|
variant->game_engine_variant.ctf.reset_on_capture = !!variant->game_engine_variant.ctf.reset_on_capture;
|
|
variant->game_engine_variant.ctf.flag_must_reset = !!variant->game_engine_variant.ctf.flag_must_reset;
|
|
variant->universal_variant.teams = TRUE;
|
|
variant->game_engine_variant.ctf.flag_at_home_to_score = !!variant->game_engine_variant.ctf.flag_at_home_to_score;
|
|
variant->game_engine_variant.ctf.single_flag_time = FLOOR(variant->game_engine_variant.ctf.single_flag_time, 0);
|
|
break;
|
|
|
|
case game_engine_slayer:
|
|
variant->game_engine_variant.slayer.no_death_bonus = !!variant->game_engine_variant.slayer.no_death_bonus;
|
|
variant->game_engine_variant.slayer.no_kill_penalty = !!variant->game_engine_variant.slayer.no_kill_penalty;
|
|
variant->game_engine_variant.slayer.kill_in_order = !!variant->game_engine_variant.slayer.kill_in_order;
|
|
break;
|
|
|
|
case game_engine_oddball:
|
|
/* port: the balls' arrays and goals hold no more */
|
|
variant->game_engine_variant.oddball.ball_spawn_count =
|
|
PIN(variant->game_engine_variant.oddball.ball_spawn_count, 0, MAXIMUM_ODDBALLS);
|
|
break;
|
|
}
|
|
|
|
if (csmemcmp(&original, variant, sizeof(original)) != 0)
|
|
{
|
|
struct game_variant cleaned;
|
|
|
|
csmemcpy(&cleaned, &original, sizeof(cleaned));
|
|
csmemcpy(&cleaned, variant, sizeof(cleaned));
|
|
error(
|
|
_error_silent,
|
|
"NETGAME CODE FAILURE: game_engine_variant_cleanup changed the variant");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_predict_resources(
|
|
void)
|
|
{
|
|
struct game_globals *game_globals;
|
|
struct game_globals_multiplayer_information *multiplayer_information;
|
|
struct game_globals_vehicle *vehicle;
|
|
long weapon_indices[10];
|
|
long weapon_index;
|
|
|
|
game_globals = scenario_get_game_globals();
|
|
multiplayer_information = TAG_BLOCK_GET_ELEMENT(
|
|
&game_globals->multiplayer_information,
|
|
0,
|
|
struct game_globals_multiplayer_information);
|
|
|
|
switch (global_variant.universal_variant.vehicle_set)
|
|
{
|
|
case _game_engine_vehicles_warthog:
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
&multiplayer_information->vehicles,
|
|
0,
|
|
struct game_globals_vehicle);
|
|
object_definition_predict(vehicle->vehicle.index);
|
|
break;
|
|
|
|
case _game_engine_vehicles_ghost:
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
&multiplayer_information->vehicles,
|
|
1,
|
|
struct game_globals_vehicle);
|
|
object_definition_predict(vehicle->vehicle.index);
|
|
break;
|
|
|
|
case _game_engine_vehicles_tank:
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
&multiplayer_information->vehicles,
|
|
2,
|
|
struct game_globals_vehicle);
|
|
object_definition_predict(vehicle->vehicle.index);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
struct tag_block *vehicles = &multiplayer_information->vehicles;
|
|
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
vehicles,
|
|
0,
|
|
struct game_globals_vehicle);
|
|
object_definition_predict(vehicle->vehicle.index);
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
vehicles,
|
|
1,
|
|
struct game_globals_vehicle);
|
|
object_definition_predict(vehicle->vehicle.index);
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
vehicles,
|
|
2,
|
|
struct game_globals_vehicle);
|
|
object_definition_predict(vehicle->vehicle.index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
object_definition_predict(list_index_to_weapon_definition_index(_weapon_list_frag_grenade));
|
|
object_definition_predict(list_index_to_weapon_definition_index(_weapon_list_plasma_grenade));
|
|
|
|
if (global_variant.game_engine_index == game_engine_oddball)
|
|
object_definition_predict(list_index_to_weapon_definition_index(_weapon_list_ball));
|
|
|
|
if (global_variant.game_engine_index == game_engine_ctf)
|
|
object_definition_predict(list_index_to_weapon_definition_index(_weapon_list_flag));
|
|
|
|
weapon_indices[0] = list_index_to_weapon_definition_index(0);
|
|
weapon_indices[1] = list_index_to_weapon_definition_index(1);
|
|
weapon_indices[2] = list_index_to_weapon_definition_index(2);
|
|
weapon_indices[3] = list_index_to_weapon_definition_index(3);
|
|
weapon_indices[4] = list_index_to_weapon_definition_index(4);
|
|
weapon_indices[5] = list_index_to_weapon_definition_index(5);
|
|
weapon_indices[6] = list_index_to_weapon_definition_index(6);
|
|
weapon_indices[7] = list_index_to_weapon_definition_index(7);
|
|
weapon_indices[8] = list_index_to_weapon_definition_index(8);
|
|
weapon_indices[9] = list_index_to_weapon_definition_index(9);
|
|
|
|
for (weapon_index = 0; weapon_index < 10; weapon_index++)
|
|
{
|
|
object_definition_predict(
|
|
game_engine_remap_weapon(weapon_indices[weapon_index]));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_initialize(
|
|
struct game_variant *variant)
|
|
{
|
|
csmemset(&game_engine_globals, 0, sizeof(game_engine_globals));
|
|
game_engine_globals.postgame_state = game_engine_mode_active;
|
|
|
|
if (variant && variant->game_engine_index)
|
|
{
|
|
global_variant = *variant;
|
|
game_engine_variant_cleanup(&global_variant);
|
|
/* port: the cleaned variant's (the one given may be any number) */
|
|
game_engine = game_engines[global_variant.game_engine_index];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_initialize_for_new_map(
|
|
void)
|
|
{
|
|
if (game_engine)
|
|
{
|
|
game_engine_verify_current_map();
|
|
game_engine_intialize_queued_sounds();
|
|
csmemset(global_goal, 0, sizeof(global_goal));
|
|
game_engine_globals.next_team_index = 0;
|
|
timeout_for_endgame_sound = 0;
|
|
game_engine_network_state_read = FALSE;
|
|
|
|
if (game_engine->initialize_for_new_map &&
|
|
!game_engine->initialize_for_new_map())
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"failed to initialize custome game engine for new map, reverting to default game engine");
|
|
|
|
if (game_engine)
|
|
{
|
|
if (game_engine->dispose)
|
|
game_engine->dispose();
|
|
|
|
game_engine = NULL;
|
|
}
|
|
}
|
|
|
|
game_engine_predict_resources();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_player_added(
|
|
long player_index)
|
|
{
|
|
initialize_player_multiplayer_data(player_index);
|
|
|
|
if (game_engine)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
long *next_team_index = &game_engine_globals.next_team_index;
|
|
|
|
if (global_variant.universal_variant.teams)
|
|
{
|
|
if (global_network_game_client_get())
|
|
{
|
|
player->team_index =
|
|
PIN((signed char)player->network_player_data.team_index % 2, 0, 1);
|
|
}
|
|
else
|
|
{
|
|
player->network_player_data.team_index =
|
|
(char)*next_team_index;
|
|
player->team_index =
|
|
(signed char)*next_team_index;
|
|
*next_team_index = (*next_team_index + 1) % 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* port: a free-for-all team is the player's own slot, which stays
|
|
below the player limit and is the same on every machine */
|
|
long team_index = DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index);
|
|
|
|
player->network_player_data.team_index =
|
|
(char)team_index;
|
|
player->team_index =
|
|
(signed char)team_index;
|
|
}
|
|
|
|
if (player_index != NONE)
|
|
{
|
|
multiplayer_message(player_index, 0, NONE);
|
|
}
|
|
else
|
|
{
|
|
struct data_iterator iterator;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while (data_iterator_next(&iterator))
|
|
multiplayer_message(iterator.datum_index, 0, NONE);
|
|
}
|
|
|
|
if (game_engine->player_added)
|
|
game_engine->player_added(player_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
real game_engine_get_distance_rating_for_spawn(
|
|
long player_index,
|
|
real_point3d const *position)
|
|
{
|
|
boolean has_teams = game_engine ? global_variant.universal_variant.teams : FALSE;
|
|
struct player_datum *player;
|
|
struct data_iterator iterator;
|
|
struct player_datum *other_player;
|
|
real rating;
|
|
|
|
player = player_get(player_index);
|
|
rating = 1.0f;
|
|
data_iterator_new(&iterator, player_data);
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (other_player)
|
|
{
|
|
if (other_player->unit_index!=NONE)
|
|
{
|
|
real_point3d origin;
|
|
real distance;
|
|
|
|
object_get_origin(other_player->unit_index, &origin);
|
|
distance = distance3d(&origin, position);
|
|
|
|
if (!has_teams ||
|
|
other_player->team_index!=player->team_index ||
|
|
!(distance>0.25f))
|
|
{
|
|
if (distance<0.25f)
|
|
rating = 0.0f;
|
|
else if (distance<1.0f)
|
|
rating *= 0.1f;
|
|
|
|
if (other_player->team_index!=player->team_index)
|
|
{
|
|
if (distance<2.0f)
|
|
rating = 0.0f;
|
|
else if (!(distance>5.0f))
|
|
rating = (distance-2.0f)*rating*0.33333334f;
|
|
}
|
|
}
|
|
}
|
|
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
return rating;
|
|
}
|
|
|
|
|
|
real game_engine_get_starting_location_rating(
|
|
long player_index,
|
|
struct player_starting_location const *starting_location)
|
|
{
|
|
if (!match_game_type(game_engine_get_type(), 4, starting_location->game_types))
|
|
return 0.0f;
|
|
|
|
if (nearby_vehicle(player_index, starting_location))
|
|
return 0.0f;
|
|
|
|
return default_starting_location_rate_function(player_index, starting_location);
|
|
}
|
|
|
|
real game_engine_get_damage_multiplier(
|
|
long damaging_player_index,
|
|
long damaged_player_index)
|
|
{
|
|
real result = 1.0f;
|
|
|
|
if (game_engine)
|
|
result /= PIN(global_variant.universal_variant.health, 0.25f, 4.0f);
|
|
|
|
if (damaging_player_index != NONE &&
|
|
damaged_player_index != NONE)
|
|
{
|
|
if (game_engine_test_trait(damaging_player_index, 2))
|
|
{
|
|
result *= 1.5f;
|
|
}
|
|
|
|
if (game_engine_test_trait(damaged_player_index, 3))
|
|
{
|
|
result *= 0.5f;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
long game_engine_remap_vehicle(
|
|
long vehicle_definition_index)
|
|
{
|
|
long result = vehicle_definition_index;
|
|
|
|
if (game_engine)
|
|
{
|
|
struct game_globals *game_globals;
|
|
struct game_globals_multiplayer_information *multiplayer_information;
|
|
struct tag_block *vehicles;
|
|
struct game_globals_vehicle *vehicle0;
|
|
struct game_globals_vehicle *vehicle1;
|
|
struct game_globals_vehicle *vehicle2;
|
|
struct game_globals_vehicle *vehicle;
|
|
|
|
game_globals = scenario_get_game_globals();
|
|
multiplayer_information = TAG_BLOCK_GET_ELEMENT(
|
|
&game_globals->multiplayer_information,
|
|
0,
|
|
struct game_globals_multiplayer_information);
|
|
vehicle0 = TAG_BLOCK_GET_ELEMENT(
|
|
&multiplayer_information->vehicles,
|
|
0,
|
|
struct game_globals_vehicle);
|
|
vehicles = &multiplayer_information->vehicles;
|
|
vehicle1 = TAG_BLOCK_GET_ELEMENT(vehicles, 1, struct game_globals_vehicle);
|
|
vehicle2 = TAG_BLOCK_GET_ELEMENT(vehicles, 2, struct game_globals_vehicle);
|
|
|
|
if (result != vehicle0->vehicle.index &&
|
|
result != vehicle1->vehicle.index &&
|
|
result != vehicle2->vehicle.index)
|
|
{
|
|
result = NONE;
|
|
}
|
|
|
|
switch (global_variant.universal_variant.vehicle_set)
|
|
{
|
|
case _game_engine_vehicles_none:
|
|
result = NONE;
|
|
break;
|
|
|
|
case _game_engine_vehicles_warthog:
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
vehicles,
|
|
0,
|
|
struct game_globals_vehicle);
|
|
if (vehicle->vehicle.index != result)
|
|
result = NONE;
|
|
break;
|
|
|
|
case _game_engine_vehicles_ghost:
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
vehicles,
|
|
1,
|
|
struct game_globals_vehicle);
|
|
if (vehicle->vehicle.index != result)
|
|
result = NONE;
|
|
break;
|
|
|
|
case _game_engine_vehicles_tank:
|
|
vehicle = TAG_BLOCK_GET_ELEMENT(
|
|
vehicles,
|
|
2,
|
|
struct game_globals_vehicle);
|
|
if (vehicle->vehicle.index != result)
|
|
result = NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
long game_engine_remap_weapon(
|
|
long weapon_definition_index)
|
|
{
|
|
long weapon_list_index =
|
|
weapon_definition_index_to_list_index(weapon_definition_index);
|
|
|
|
if (weapon_list_index == _weapon_list_ball ||
|
|
weapon_list_index == _weapon_list_flag ||
|
|
weapon_list_index == NONE)
|
|
{
|
|
return weapon_definition_index;
|
|
}
|
|
|
|
if (weapon_list_index == _weapon_list_flamethrower)
|
|
weapon_list_index = _weapon_list_rocket_launcher;
|
|
|
|
switch (global_variant.universal_variant.weapon_set)
|
|
{
|
|
case _game_engine_weapons_pistols:
|
|
switch (weapon_list_index)
|
|
{
|
|
case _weapon_list_needler:
|
|
case _weapon_list_pistol:
|
|
case _weapon_list_plasma_rifle:
|
|
case _weapon_list_rocket_launcher:
|
|
weapon_list_index = _weapon_list_plasma_pistol;
|
|
break;
|
|
|
|
case _weapon_list_plasma_pistol:
|
|
default:
|
|
weapon_list_index = _weapon_list_pistol;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case _game_engine_weapons_assault_rifles:
|
|
switch (weapon_list_index)
|
|
{
|
|
case _weapon_list_needler:
|
|
case _weapon_list_pistol:
|
|
case _weapon_list_plasma_rifle:
|
|
case _weapon_list_rocket_launcher:
|
|
weapon_list_index = _weapon_list_plasma_rifle;
|
|
break;
|
|
|
|
case _weapon_list_plasma_pistol:
|
|
default:
|
|
weapon_list_index = _weapon_list_assault_rifle;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case _game_engine_weapons_plasma_weapons:
|
|
if (weapon_list_index >= _weapon_list_needler && weapon_list_index <= _weapon_list_plasma_pistol)
|
|
weapon_list_index = _weapon_list_plasma_pistol;
|
|
else
|
|
weapon_list_index = _weapon_list_plasma_rifle;
|
|
break;
|
|
|
|
case _game_engine_weapons_sniping:
|
|
if (weapon_list_index != _weapon_list_pistol && weapon_list_index != _weapon_list_sniper_rifle)
|
|
weapon_list_index = _weapon_list_sniper_rifle;
|
|
break;
|
|
|
|
case _game_engine_weapons_no_sniping:
|
|
switch (weapon_list_index)
|
|
{
|
|
case _weapon_list_pistol:
|
|
weapon_list_index = _weapon_list_assault_rifle;
|
|
break;
|
|
|
|
case _weapon_list_sniper_rifle:
|
|
weapon_list_index = _weapon_list_shotgun;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case _game_engine_weapons_rocket_launchers:
|
|
weapon_list_index = _weapon_list_rocket_launcher;
|
|
break;
|
|
|
|
case _game_engine_weapons_shotguns:
|
|
weapon_list_index = _weapon_list_shotgun;
|
|
break;
|
|
|
|
case _game_engine_weapons_short_range:
|
|
switch (weapon_list_index)
|
|
{
|
|
case _weapon_list_assault_rifle:
|
|
case _weapon_list_needler:
|
|
case _weapon_list_pistol:
|
|
case _weapon_list_sniper_rifle:
|
|
weapon_list_index = _weapon_list_shotgun;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case _game_engine_weapons_human:
|
|
switch (weapon_list_index)
|
|
{
|
|
case _weapon_list_needler:
|
|
case _weapon_list_plasma_rifle:
|
|
weapon_list_index = _weapon_list_assault_rifle;
|
|
break;
|
|
|
|
case _weapon_list_plasma_pistol:
|
|
weapon_list_index = _weapon_list_pistol;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return list_index_to_weapon_definition_index(weapon_list_index);
|
|
}
|
|
|
|
long game_engine_remap_object_definition(
|
|
long definition_index)
|
|
{
|
|
short object_type;
|
|
|
|
if (!game_engine_running() || definition_index==NONE)
|
|
{
|
|
return definition_index;
|
|
}
|
|
|
|
object_type = object_definition_get(definition_index)->object.type;
|
|
if (object_type==_object_type_vehicle)
|
|
{
|
|
return game_engine_remap_vehicle(definition_index);
|
|
}
|
|
if (object_type==_object_type_weapon)
|
|
{
|
|
return game_engine_remap_weapon(definition_index);
|
|
}
|
|
if (object_type==_object_type_equipment)
|
|
{
|
|
return game_engine_remap_equipment(definition_index);
|
|
}
|
|
|
|
return definition_index;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
static void netgame_flag_verify_no_team_duplicates(
|
|
short flag_type,
|
|
char const *error_message)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
short flag_index;
|
|
|
|
for (flag_index = 0;
|
|
flag_index < scenario->netgame_flags.count;
|
|
flag_index++)
|
|
{
|
|
struct scenario_netgame_flag *flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
flag_index,
|
|
struct scenario_netgame_flag);
|
|
short duplicate_index;
|
|
|
|
if (flag_type != flag->type)
|
|
continue;
|
|
|
|
for (duplicate_index = flag_index + 1;
|
|
duplicate_index < scenario->netgame_flags.count;
|
|
duplicate_index++)
|
|
{
|
|
struct scenario_netgame_flag *duplicate = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
duplicate_index,
|
|
struct scenario_netgame_flag);
|
|
|
|
if (flag_type == duplicate->type &&
|
|
duplicate->team_index == flag->team_index)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
error_message,
|
|
duplicate->team_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void update_weapon_inventory(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x80A,
|
|
weapon_index != NONE);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x80B,
|
|
weapon_is_flag(weapon_index));
|
|
|
|
weapon = weapon_get(weapon_index);
|
|
if (weapon->object.parent_object_index == NONE &&
|
|
!TEST_FLAG(weapon->item.flags, _item_attached_to_unit_bit) &&
|
|
TEST_FLAG(
|
|
weapon->weapon.flags,
|
|
_weapon_runtime_game_engine_active_bit))
|
|
{
|
|
weapon->weapon.flags &=
|
|
~FLAG(_weapon_runtime_game_engine_active_bit);
|
|
|
|
if (game_engine->weapon_dropped)
|
|
game_engine->weapon_dropped(weapon_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_update_weapons(
|
|
void)
|
|
{
|
|
struct object_iterator iterator;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x828,
|
|
NULL != game_engine);
|
|
|
|
object_iterator_new(
|
|
&iterator,
|
|
_object_mask_item,
|
|
0);
|
|
while (object_iterator_next(&iterator))
|
|
{
|
|
struct item_datum *item = item_get(iterator.index);
|
|
|
|
if (TEST_FLAG(item->item.flags, _item_attached_to_unit_bit))
|
|
{
|
|
item->object.scale = 1.f;
|
|
}
|
|
else
|
|
{
|
|
struct item_definition *definition =
|
|
item_definition_get(item->definition_index);
|
|
|
|
item->object.scale =
|
|
definition->item.scale != 0.f
|
|
? definition->item.scale
|
|
: 1.f;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x83E,
|
|
(item->object.scale >= 0.5f) &&
|
|
(item->object.scale <= 3.f));
|
|
}
|
|
|
|
/*
|
|
* Slot 0x38 is populated by the CTF and oddball engines. It receives
|
|
* each objective weapon during the per-tick item scan.
|
|
*/
|
|
if (game_engine->objective_weapon_update)
|
|
{
|
|
struct weapon_datum *weapon = weapon_try_and_get(iterator.index);
|
|
|
|
if (weapon && weapon_is_flag(iterator.index))
|
|
{
|
|
update_weapon_inventory(iterator.index);
|
|
game_engine->objective_weapon_update(
|
|
iterator.index,
|
|
weapon);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void netgame_flag_verify_team_range(
|
|
short flag_type,
|
|
short minimum_index,
|
|
short maximum_index,
|
|
char const *error_message)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
short flag_index;
|
|
|
|
for (flag_index = 0;
|
|
flag_index < scenario->netgame_flags.count;
|
|
flag_index++)
|
|
{
|
|
struct scenario_netgame_flag *flag = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_flags,
|
|
flag_index,
|
|
struct scenario_netgame_flag);
|
|
|
|
if (flag_type == flag->type &&
|
|
(flag->team_index < minimum_index ||
|
|
flag->team_index > maximum_index))
|
|
{
|
|
error(
|
|
_error_silent,
|
|
error_message,
|
|
flag->team_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void netgame_verify_equipment(
|
|
short game_type,
|
|
char const *error_message)
|
|
{
|
|
long matching_count = 0;
|
|
struct scenario *scenario = global_scenario_get();
|
|
short equipment_index;
|
|
|
|
for (equipment_index = 0;
|
|
equipment_index < scenario->netgame_equipment.count;
|
|
equipment_index++)
|
|
{
|
|
struct scenario_netgame_equipment *equipment =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_equipment,
|
|
equipment_index,
|
|
struct scenario_netgame_equipment);
|
|
|
|
if (match_game_type(
|
|
game_type,
|
|
4,
|
|
equipment->game_type))
|
|
{
|
|
matching_count++;
|
|
}
|
|
}
|
|
|
|
if (matching_count == 0)
|
|
error(_error_silent, error_message);
|
|
|
|
return;
|
|
}
|
|
|
|
static void netgame_flag_verify_team_exists(
|
|
short flag_type,
|
|
short flag_index,
|
|
char const *error_message)
|
|
{
|
|
if (find_netgame_flag(
|
|
NULL,
|
|
0.f,
|
|
0.f,
|
|
flag_type,
|
|
flag_index) == NONE)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
error_message,
|
|
flag_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void game_engine_verify_current_map(
|
|
void)
|
|
{
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_ctf_flag,
|
|
0,
|
|
"NETGAME MAP FAILURE: missing ctf flag [team %d]");
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_ctf_flag,
|
|
1,
|
|
"NETGAME MAP FAILURE: missing ctf flag [team %d]");
|
|
|
|
netgame_flag_verify_no_team_duplicates(
|
|
_netgame_flag_ctf_flag,
|
|
"NETGAME MAP FAILURE: duplicate ctf flag [team %d]");
|
|
netgame_flag_verify_team_range(
|
|
_netgame_flag_ctf_flag,
|
|
0,
|
|
1,
|
|
"NETGAME MAP FAILURE: ctf flag out of range [team %d]");
|
|
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_hill,
|
|
0,
|
|
"NETGAME MAP FAILURE: missing hill flag [team %d]");
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_hill,
|
|
1,
|
|
"NETGAME MAP FAILURE: missing hill flag [team %d]");
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_oddball_ball_spawn,
|
|
0,
|
|
"NETGAME MAP FAILURE: missing oddball flag [team %d]");
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_oddball_ball_spawn,
|
|
1,
|
|
"NETGAME MAP FAILURE: missing oddball flag [team %d]");
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_race_track,
|
|
0,
|
|
"NETGAME MAP FAILURE: missing race flag [team %d]");
|
|
netgame_flag_verify_team_exists(
|
|
_netgame_flag_race_track,
|
|
1,
|
|
"NETGAME MAP FAILURE: missing race flag [team %d]");
|
|
|
|
netgame_flag_verify_no_team_duplicates(
|
|
_netgame_flag_race_track,
|
|
"NETGAME MAP FAILURE: duplicate race track flag [team %d]");
|
|
|
|
/* BUG (preserved for exact matching): January passes team index zero for
|
|
* both CTF checks, and netgame_verify_spawn_points never reads that
|
|
* formal parameter.
|
|
* A corrected build should filter starting locations by an authoritatively
|
|
* recovered team-index field before reporting per-team counts.
|
|
*/
|
|
netgame_verify_spawn_points(
|
|
game_engine_ctf,
|
|
0,
|
|
4,
|
|
"NETGAME MAP FAILURE: failed to find enough spawn points for ctf team 0 (%d/%d)");
|
|
netgame_verify_spawn_points(
|
|
game_engine_ctf,
|
|
0,
|
|
4,
|
|
"NETGAME MAP FAILURE: failed to find enough spawn points for ctf team 1 (%d/%d)");
|
|
netgame_verify_spawn_points(
|
|
game_engine_slayer,
|
|
0,
|
|
4,
|
|
"NETGAME MAP FAILURE: failed to find enough spawn points for slayer %d/%d");
|
|
netgame_verify_spawn_points(
|
|
game_engine_oddball,
|
|
0,
|
|
4,
|
|
"NETGAME MAP FAILURE: failed to find enough spawn points for oddball %d/%d");
|
|
netgame_verify_spawn_points(
|
|
game_engine_king,
|
|
0,
|
|
4,
|
|
"NETGAME MAP FAILURE: failed to find enough spawn points for king %d/%d");
|
|
netgame_verify_spawn_points(
|
|
game_engine_race,
|
|
0,
|
|
4,
|
|
"NETGAME MAP FAILURE: failed to find enough spawn points for race %d/%d");
|
|
|
|
netgame_verify_equipment(
|
|
game_engine_ctf,
|
|
"NETGAME MAP FAILURE: failed to find any equipment for ctf");
|
|
netgame_verify_equipment(
|
|
game_engine_slayer,
|
|
"NETGAME MAP FAILURE: failed to find any equipment for slayer");
|
|
netgame_verify_equipment(
|
|
game_engine_oddball,
|
|
"NETGAME MAP FAILURE: failed to find any equipment for oddball");
|
|
netgame_verify_equipment(
|
|
game_engine_king,
|
|
"NETGAME MAP FAILURE: failed to find any equipment for king");
|
|
netgame_verify_equipment(
|
|
game_engine_race,
|
|
"NETGAME MAP FAILURE: failed to find any equipment for race");
|
|
|
|
return;
|
|
}
|
|
|
|
static void internal_rasterize_target_name(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player;
|
|
long target_player_index;
|
|
|
|
player = player_get(player_index);
|
|
target_player_index = NONE;
|
|
if (player->local_player_index != NONE && player->unit_index != NONE)
|
|
{
|
|
target_player_index = find_closest_player_index(player_index);
|
|
if (target_player_index == player_index)
|
|
target_player_index = NONE;
|
|
}
|
|
|
|
/* This is drawn once a frame, several frames per tick
|
|
(render_interpolation.c): the hold time counts ticks, as it did on the
|
|
Xbox. */
|
|
{
|
|
static long last_game_times[MAXIMUM_NUMBER_OF_LOCAL_PLAYERS];
|
|
long *last_game_time = NULL;
|
|
|
|
if (player->local_player_index >= 0 &&
|
|
player->local_player_index < MAXIMUM_NUMBER_OF_LOCAL_PLAYERS)
|
|
{
|
|
last_game_time = &last_game_times[player->local_player_index];
|
|
}
|
|
if (!last_game_time || *last_game_time != game_time_get())
|
|
{
|
|
if (last_game_time)
|
|
*last_game_time = game_time_get();
|
|
if (player->player_display_index != target_player_index)
|
|
{
|
|
if (player->player_display_count > 0)
|
|
player->player_display_count--;
|
|
if (player->player_display_count == 0)
|
|
player->player_display_index = target_player_index;
|
|
}
|
|
else if (player->player_display_count < 15)
|
|
{
|
|
player->player_display_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (player->player_display_index != NONE)
|
|
{
|
|
struct player_datum *target_player = player_get(player->player_display_index);
|
|
wchar_t target_name[12] = { 0 };
|
|
long hold_time = player->player_display_count;
|
|
real alpha;
|
|
|
|
if (hold_time >= 10)
|
|
hold_time = 10;
|
|
ustrncpy(target_name, target_player->name, NUMBEROF(target_name) - 1);
|
|
target_name[NUMBEROF(target_name) - 1] = 0;
|
|
alpha = linear_to_non_linear_alpha(hold_time * 0.1f) * 0.5f;
|
|
game_engine_rasterize_message(target_name, alpha);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean internal_rasterize_score(
|
|
long player_index,
|
|
long message,
|
|
long message_data,
|
|
wchar_t *buffer,
|
|
long buffer_size)
|
|
{
|
|
struct player_datum *player;
|
|
struct player_datum *other_player;
|
|
long score;
|
|
long string_list_index;
|
|
wchar_t const *format;
|
|
boolean result;
|
|
|
|
result = TRUE;
|
|
player = player_get(player_index);
|
|
score = 0;
|
|
|
|
if (game_engine_test_flag(1))
|
|
{
|
|
switch (message)
|
|
{
|
|
case _game_engine_message_multi_kill:
|
|
message = _game_engine_message_multi_kill_with_score;
|
|
break;
|
|
case _game_engine_message_triple_kill:
|
|
message = _game_engine_message_triple_kill_with_score;
|
|
break;
|
|
case _game_engine_message_double_kill:
|
|
message = _game_engine_message_double_kill_with_score;
|
|
break;
|
|
case _game_engine_message_ten_kills_in_a_row:
|
|
message = _game_engine_message_ten_kills_in_a_row_with_score;
|
|
break;
|
|
case _game_engine_message_five_kills_in_row:
|
|
message = _game_engine_message_five_kills_in_row_with_score;
|
|
break;
|
|
case _game_engine_message_killed_enemy:
|
|
message = _game_engine_message_killed_enemy_with_score;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (message >= _game_engine_message_multi_kill_with_score &&
|
|
message <= _game_engine_message_killed_enemy_with_score)
|
|
{
|
|
/* port: a client of the distributed netcode replaying the host's
|
|
killing blow: the score the host's game type gave its killer
|
|
(its own has it only once the game type's state comes) */
|
|
if (!network_damage_killer_score(player_index, &score))
|
|
score = game_engine->get_player_score(player_index, TRUE);
|
|
}
|
|
}
|
|
|
|
switch (message)
|
|
{
|
|
case _game_engine_message_welcome:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x4A);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name);
|
|
break;
|
|
case _game_engine_message_killed_by_unknown:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x4B);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name);
|
|
break;
|
|
case _game_engine_message_killed_by_biped:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x4C);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name);
|
|
break;
|
|
case _game_engine_message_killed_by_vehicle:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x4D);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name);
|
|
break;
|
|
case _game_engine_message_killed_by_player:
|
|
other_player = player_get(message_data);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x4E);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name,
|
|
other_player->name);
|
|
break;
|
|
case _game_engine_message_killed_by_friendly_fire:
|
|
other_player = player_get(message_data);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x4F);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name,
|
|
other_player->name);
|
|
break;
|
|
case _game_engine_message_quit:
|
|
other_player = player_get(message_data);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x50);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
other_player->name);
|
|
break;
|
|
case _game_engine_message_killed_by_self:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x51);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
player->name);
|
|
break;
|
|
case _game_engine_message_killed_friendly:
|
|
other_player = player_get(message_data);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x52);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
other_player->name);
|
|
break;
|
|
case _game_engine_message_multi_kill:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x53);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_killtacular_kill);
|
|
break;
|
|
case _game_engine_message_triple_kill:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x54);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_triple_kill);
|
|
break;
|
|
case _game_engine_message_double_kill:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x55);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_double_kill);
|
|
break;
|
|
case _game_engine_message_ten_kills_in_a_row:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x56);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_running_riot);
|
|
break;
|
|
case _game_engine_message_five_kills_in_row:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x57);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_killing_spree);
|
|
break;
|
|
case _game_engine_message_killed_enemy:
|
|
other_player = player_get(message_data);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x58);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
other_player->name);
|
|
break;
|
|
case _game_engine_message_multi_kill_with_score:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x59);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
score);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_killtacular_kill);
|
|
break;
|
|
case _game_engine_message_triple_kill_with_score:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x5A);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
score);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_triple_kill);
|
|
break;
|
|
case _game_engine_message_double_kill_with_score:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x5B);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
score);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_double_kill);
|
|
break;
|
|
case _game_engine_message_ten_kills_in_a_row_with_score:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x5C);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
score);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_running_riot);
|
|
break;
|
|
case _game_engine_message_five_kills_in_row_with_score:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x5D);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
score);
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_killing_spree);
|
|
break;
|
|
case _game_engine_message_killed_enemy_with_score:
|
|
other_player = player_get(message_data);
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x5E);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
other_player->name,
|
|
score);
|
|
break;
|
|
case _game_engine_message_odd_man_out:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x5F);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
break;
|
|
case _game_engine_message_out_of_lives:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x60);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
break;
|
|
case _game_engine_message_respawn_timer:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x61);
|
|
else
|
|
format = L"";
|
|
usnprintf(
|
|
buffer,
|
|
buffer_size,
|
|
format,
|
|
message_data);
|
|
break;
|
|
case _game_engine_message_waiting_for_space_to_clear:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x62);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
break;
|
|
case _game_engine_message_player_quit_self:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x63);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
break;
|
|
case _game_engine_message_press_back_for_score:
|
|
string_list_index = tag_loaded('ustr', "ui\\multiplayer_game_text");
|
|
if (string_list_index != NONE)
|
|
format = unicode_string_list_get_string(string_list_index, 0x64);
|
|
else
|
|
format = L"";
|
|
ustrncpy(
|
|
buffer,
|
|
format,
|
|
buffer_size);
|
|
break;
|
|
default:
|
|
result = FALSE;
|
|
break;
|
|
}
|
|
|
|
buffer[buffer_size - 1] = 0;
|
|
return result;
|
|
}
|
|
|
|
static long adjust_score_for_ranking(
|
|
long score,
|
|
long player_index)
|
|
{
|
|
struct player_datum *player;
|
|
long result = 0;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x29D,
|
|
player_index!=NONE);
|
|
|
|
player = player_get(player_index);
|
|
if (score < -1000)
|
|
score = -1000;
|
|
score += 1000;
|
|
if (player->statistics.deaths <
|
|
global_variant.universal_variant.lives)
|
|
{
|
|
result = 0x40000000;
|
|
}
|
|
if (!player->quit_out_of_game)
|
|
result |= 0x20000000;
|
|
|
|
return result | score;
|
|
}
|
|
|
|
static long item_collection_get_total(
|
|
struct tag_block const *permutations)
|
|
{
|
|
struct item_permutation_definition const *permutation =
|
|
(struct item_permutation_definition const *)permutations->address;
|
|
long permutation_count = permutations->count;
|
|
long result = 0;
|
|
long permutation_index;
|
|
|
|
for (permutation_index = 0;
|
|
permutation_index < permutation_count;
|
|
permutation_index++)
|
|
{
|
|
result = (long)(
|
|
(real)result + permutation[permutation_index].weight);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static long random_item(
|
|
long item_collection_index)
|
|
{
|
|
struct item_collection_definition *definition =
|
|
item_collection_definition_get(item_collection_index);
|
|
struct tag_block *permutations = &definition->permutations;
|
|
long permutation_count = permutations->count;
|
|
long remaining_weight = seed_random_range(
|
|
get_global_random_seed_address(),
|
|
0,
|
|
(short)item_collection_get_total(permutations));
|
|
struct item_permutation_definition const *permutation =
|
|
permutations->address;
|
|
long permutation_index = 0;
|
|
|
|
while (permutation_index < permutation_count)
|
|
{
|
|
remaining_weight =
|
|
(long)((real)remaining_weight -
|
|
permutation[permutation_index].weight);
|
|
if (remaining_weight < 0)
|
|
return permutation[permutation_index].item.index;
|
|
|
|
permutation_index++;
|
|
}
|
|
|
|
return NONE;
|
|
}
|
|
|
|
|
|
static void game_engine_update_item_spawn(
|
|
void)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
short equipment_index;
|
|
|
|
/* a client of the distributed netcode has the host's items
|
|
(port/linux/game/network_distributed.c) */
|
|
if (network_game_distributed_client())
|
|
return;
|
|
|
|
for (equipment_index = 0;
|
|
equipment_index < scenario->netgame_equipment.count;
|
|
equipment_index++)
|
|
{
|
|
struct scenario_netgame_equipment *equipment =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->netgame_equipment,
|
|
equipment_index,
|
|
struct scenario_netgame_equipment);
|
|
if (match_game_type(
|
|
game_engine_get_type(),
|
|
NUMBEROF(equipment->game_type),
|
|
equipment->game_type))
|
|
{
|
|
long respawn_period = 30 * TICKS_PER_SECOND;
|
|
short respawn_time = equipment->spawn_time;
|
|
|
|
if (respawn_time == 0)
|
|
{
|
|
if (equipment->item_collection.index != NONE)
|
|
{
|
|
struct item_collection_definition *item_collection =
|
|
item_collection_definition_get(
|
|
equipment->item_collection.index);
|
|
|
|
respawn_time = item_collection->spawn_time;
|
|
if (respawn_time != 0)
|
|
respawn_period = respawn_time * TICKS_PER_SECOND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
respawn_period = respawn_time * TICKS_PER_SECOND;
|
|
}
|
|
|
|
if (game_time_get() % respawn_period == 0)
|
|
{
|
|
long definition_index =
|
|
random_item(equipment->item_collection.index);
|
|
struct object_placement_data placement_data;
|
|
long object_index;
|
|
|
|
object_placement_data_new(
|
|
&placement_data,
|
|
definition_index,
|
|
NONE);
|
|
placement_data.position = equipment->position;
|
|
object_index = object_new(&placement_data);
|
|
if (object_index != NONE)
|
|
{
|
|
struct item_datum *item = item_get(object_index);
|
|
|
|
object_set_garbage(object_index, FALSE);
|
|
if (TEST_FLAG(
|
|
equipment->flags,
|
|
_equipment_created_at_rest_bit))
|
|
{
|
|
SET_FLAG(item->object.flags, _object_at_rest_bit, TRUE);
|
|
}
|
|
item->item.last_owned_time +=
|
|
respawn_period - 30 * TICKS_PER_SECOND;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void handle_custom_starting_equipment(
|
|
long unit_index,
|
|
long *fragmentation_grenade_count,
|
|
long *plasma_grenade_count)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
long starting_equipment_index;
|
|
|
|
for (starting_equipment_index = 0;
|
|
starting_equipment_index < scenario->scenario_starting_equipment.count;
|
|
starting_equipment_index++)
|
|
{
|
|
struct scenario_starting_equipment *starting_equipment = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->scenario_starting_equipment,
|
|
starting_equipment_index,
|
|
struct scenario_starting_equipment);
|
|
|
|
if (match_game_type(
|
|
game_engine_get_type(),
|
|
4,
|
|
starting_equipment->game_type))
|
|
{
|
|
boolean first_weapon = TRUE;
|
|
long item_collection_index;
|
|
|
|
/* January visits the first five of the six item collections */
|
|
for (item_collection_index = 0;
|
|
item_collection_index < 5;
|
|
item_collection_index++)
|
|
{
|
|
if (starting_equipment->item_collection[item_collection_index].index != NONE)
|
|
{
|
|
long definition_index = random_item(
|
|
starting_equipment->item_collection[item_collection_index].index);
|
|
long weapon_index;
|
|
struct object_placement_data placement_data;
|
|
|
|
object_placement_data_new(
|
|
&placement_data,
|
|
definition_index,
|
|
NONE);
|
|
weapon_index = object_new(&placement_data);
|
|
if (weapon_index != NONE)
|
|
{
|
|
struct object_datum *weapon = object_get_and_verify_type(
|
|
weapon_index,
|
|
_object_mask_item);
|
|
|
|
if (!first_weapon &&
|
|
unit_has_weapon_definition_index(
|
|
unit_index,
|
|
weapon->definition_index))
|
|
{
|
|
object_delete(weapon_index);
|
|
}
|
|
else
|
|
{
|
|
unit_add_weapon_to_inventory(
|
|
unit_index,
|
|
weapon_index,
|
|
first_weapon ? 2 : 0);
|
|
first_weapon = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(starting_equipment->flags, _netgame_starting_equipment_flag_no_grenades_bit))
|
|
{
|
|
*fragmentation_grenade_count = 0;
|
|
*plasma_grenade_count = 0;
|
|
}
|
|
|
|
if (TEST_FLAG(starting_equipment->flags, _netgame_starting_equipment_flag_plasma_greandes_bit))
|
|
{
|
|
*plasma_grenade_count += *fragmentation_grenade_count;
|
|
*fragmentation_grenade_count = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_engine_postspawn_player_update(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player;
|
|
long unit_index;
|
|
struct game_globals_grenade *fragmentation_grenade;
|
|
struct game_globals_grenade *plasma_grenade;
|
|
long fragmentation_grenade_count;
|
|
long plasma_grenade_count;
|
|
|
|
if (!game_engine)
|
|
return;
|
|
|
|
if (game_engine->player_update)
|
|
{
|
|
game_engine->player_update(player_index);
|
|
return;
|
|
}
|
|
|
|
player = player_get(player_index);
|
|
unit_index = player->unit_index;
|
|
fragmentation_grenade = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->grenades,
|
|
_unit_grenade_human_fragmentation,
|
|
struct game_globals_grenade);
|
|
plasma_grenade = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->grenades,
|
|
_unit_grenade_covenant_plasma,
|
|
struct game_globals_grenade);
|
|
plasma_grenade_count = plasma_grenade->maximum_count;
|
|
fragmentation_grenade_count = fragmentation_grenade->maximum_count;
|
|
|
|
if (TEST_FLAG(game_engine_globals.flags, _game_engine_9_or_more_players_bit))
|
|
{
|
|
fragmentation_grenade_count = 1;
|
|
plasma_grenade_count = 1;
|
|
}
|
|
else if (TEST_FLAG(game_engine_globals.flags, _game_engine_5_or_more_players_bit))
|
|
{
|
|
fragmentation_grenade_count = 2;
|
|
plasma_grenade_count = 2;
|
|
}
|
|
|
|
{
|
|
long starting_fragmentation_grenade_count =
|
|
fragmentation_grenade_count;
|
|
long starting_plasma_grenade_count = 0;
|
|
|
|
if (!TEST_FLAG(global_variant.universal_variant.flags, _game_variant_generic_starting_equipment_bit))
|
|
{
|
|
handle_custom_starting_equipment(
|
|
unit_index,
|
|
&starting_fragmentation_grenade_count,
|
|
&starting_plasma_grenade_count);
|
|
}
|
|
|
|
if (game_engine_infinite_grenades_internal())
|
|
{
|
|
starting_plasma_grenade_count =
|
|
plasma_grenade_count;
|
|
starting_fragmentation_grenade_count =
|
|
fragmentation_grenade_count;
|
|
}
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
struct unit_datum *unit = object_get_and_verify_type(
|
|
unit_index,
|
|
_object_mask_unit);
|
|
|
|
switch (global_variant.universal_variant.weapon_set)
|
|
{
|
|
case _game_engine_weapons_plasma_weapons:
|
|
starting_plasma_grenade_count +=
|
|
starting_fragmentation_grenade_count;
|
|
starting_fragmentation_grenade_count = 0;
|
|
break;
|
|
case _game_engine_weapons_human:
|
|
starting_fragmentation_grenade_count +=
|
|
starting_plasma_grenade_count;
|
|
starting_plasma_grenade_count = 0;
|
|
break;
|
|
case _game_engine_weapons_no_grenades:
|
|
if (!game_engine_infinite_grenades_internal())
|
|
{
|
|
starting_fragmentation_grenade_count = 0;
|
|
starting_plasma_grenade_count = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
starting_fragmentation_grenade_count = MIN(
|
|
starting_fragmentation_grenade_count,
|
|
fragmentation_grenade_count);
|
|
starting_plasma_grenade_count = MIN(
|
|
starting_plasma_grenade_count,
|
|
plasma_grenade_count);
|
|
unit->unit.grenade_counts[
|
|
_unit_grenade_human_fragmentation] =
|
|
(char)starting_fragmentation_grenade_count;
|
|
unit->unit.grenade_counts[
|
|
_unit_grenade_covenant_plasma] =
|
|
(char)starting_plasma_grenade_count;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean game_engine_get_state_message(
|
|
long player_index,
|
|
wchar_t *message,
|
|
long message_character_count)
|
|
{
|
|
struct player_datum *player;
|
|
long state_message_parameter;
|
|
long state_message;
|
|
long respawn_timer;
|
|
boolean result = FALSE;
|
|
|
|
if (game_engine)
|
|
{
|
|
player = player_get(player_index);
|
|
|
|
if (player->state_message >= _game_engine_message_odd_man_out &&
|
|
player->state_message <=
|
|
_game_engine_message_waiting_for_space_to_clear)
|
|
{
|
|
player->state_message = NONE;
|
|
}
|
|
|
|
if (player->unit_index == NONE)
|
|
{
|
|
state_message_parameter = 0;
|
|
if (player->quit_out_of_game == TRUE)
|
|
state_message = _game_engine_message_player_quit_self;
|
|
else if (game_engine_player_is_out_of_lives(player_index))
|
|
state_message = _game_engine_message_out_of_lives;
|
|
else if (game_engine_player_is_odd_man_out(player_index))
|
|
state_message = _game_engine_message_odd_man_out;
|
|
else
|
|
{
|
|
respawn_timer = player->respawn_timer;
|
|
if (respawn_timer > 0)
|
|
{
|
|
state_message = _game_engine_message_respawn_timer;
|
|
state_message_parameter = respawn_timer / TICKS_PER_SECOND;
|
|
}
|
|
else
|
|
state_message =
|
|
_game_engine_message_waiting_for_space_to_clear;
|
|
}
|
|
|
|
result = multiplayer_message_internal(
|
|
player_index,
|
|
state_message,
|
|
state_message_parameter,
|
|
message,
|
|
message_character_count);
|
|
}
|
|
else if (game_time_get() < 15 * TICKS_PER_SECOND)
|
|
{
|
|
result = multiplayer_message_internal(
|
|
player_index,
|
|
_game_engine_message_press_back_for_score,
|
|
NONE,
|
|
message,
|
|
message_character_count);
|
|
}
|
|
else if (player->state_message != NONE)
|
|
{
|
|
result = multiplayer_message_internal(
|
|
player_index,
|
|
player->state_message,
|
|
player->state_message_player_index,
|
|
message,
|
|
message_character_count);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* port: a distributed client's players spawn when the host spawns them
|
|
(network_player_attach_unit), but their respawn countdown runs here as it
|
|
does on the host (game_engine_should_spawn_player), for the hud's count and
|
|
its sounds: it stops a tick short, where the host's unit ends it */
|
|
void game_engine_client_respawn_countdown(
|
|
long player_index)
|
|
{
|
|
struct player_datum *player;
|
|
|
|
if (!game_engine)
|
|
return;
|
|
player = player_get(player_index);
|
|
if (player->quit_out_of_game == TRUE ||
|
|
player->statistics.deaths == 0 ||
|
|
player->respawn_timer <= 1 ||
|
|
game_engine_player_is_out_of_lives(player_index) ||
|
|
game_engine_player_is_odd_man_out(player_index) ||
|
|
game_engine_globals.postgame_state == game_engine_mode_postgame_rasterize ||
|
|
game_engine_globals.postgame_state == game_engine_mode_postgame_rasterize_delay)
|
|
{
|
|
return;
|
|
}
|
|
if (player->local_player_index != NONE &&
|
|
(player->respawn_timer == 90 || player->respawn_timer == 60 || player->respawn_timer == 30))
|
|
{
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_countdown_for_respawn);
|
|
}
|
|
player->respawn_timer--;
|
|
if (player->local_player_index != NONE && player->respawn_timer == 1)
|
|
game_engine_play_multiplayer_sound(_multiplayer_sound_respawn);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean game_engine_should_spawn_player(
|
|
long player_index)
|
|
{
|
|
boolean should_spawn = FALSE;
|
|
|
|
if (game_engine)
|
|
{
|
|
struct player_datum *player = player_get(player_index);
|
|
|
|
if (player->quit_out_of_game == TRUE)
|
|
{
|
|
should_spawn = FALSE;
|
|
}
|
|
else if (player->statistics.deaths == 0)
|
|
{
|
|
should_spawn = TRUE;
|
|
}
|
|
else if (game_engine_player_is_out_of_lives(player_index))
|
|
{
|
|
should_spawn = FALSE;
|
|
}
|
|
else if (game_engine_player_is_odd_man_out(player_index))
|
|
{
|
|
should_spawn = FALSE;
|
|
}
|
|
else if (game_engine_globals.postgame_state ==
|
|
game_engine_mode_postgame_rasterize ||
|
|
game_engine_globals.postgame_state ==
|
|
game_engine_mode_postgame_rasterize_delay)
|
|
{
|
|
should_spawn = FALSE;
|
|
}
|
|
else
|
|
{
|
|
long respawn_timer = player->respawn_timer;
|
|
|
|
if (respawn_timer > 0)
|
|
{
|
|
if (player->local_player_index != NONE)
|
|
{
|
|
if (respawn_timer == 90)
|
|
{
|
|
game_engine_play_multiplayer_sound(
|
|
_multiplayer_sound_countdown_for_respawn);
|
|
}
|
|
else if (respawn_timer == 60)
|
|
{
|
|
game_engine_play_multiplayer_sound(
|
|
_multiplayer_sound_countdown_for_respawn);
|
|
}
|
|
else if (respawn_timer == 30)
|
|
{
|
|
game_engine_play_multiplayer_sound(
|
|
_multiplayer_sound_countdown_for_respawn);
|
|
}
|
|
else if (respawn_timer == 1)
|
|
{
|
|
game_engine_play_multiplayer_sound(
|
|
_multiplayer_sound_respawn);
|
|
}
|
|
}
|
|
|
|
player->respawn_timer--;
|
|
should_spawn = player->respawn_timer == 0;
|
|
}
|
|
else
|
|
{
|
|
should_spawn = TRUE;
|
|
}
|
|
}
|
|
|
|
if (should_spawn &&
|
|
game_time_get() > 3 &&
|
|
game_time_get() % 32 !=
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index) % 32)
|
|
{
|
|
should_spawn = FALSE;
|
|
}
|
|
}
|
|
|
|
return should_spawn;
|
|
}
|
|
|
|
struct game_engine_place game_engine_get_place(
|
|
long player_index,
|
|
enum get_score_type score_type)
|
|
{
|
|
struct player_datum *player;
|
|
long group_count;
|
|
boolean tied;
|
|
boolean all_tied;
|
|
struct game_engine_place result;
|
|
|
|
player = player_get(player_index);
|
|
all_tied = TRUE;
|
|
tied = FALSE;
|
|
group_count = 1;
|
|
result.place = 0;
|
|
|
|
if (game_engine->get_player_score)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *other_player;
|
|
/* port: free-for-all team indices run up to the player limit */
|
|
unsigned long team_mask[BIT_VECTOR_SIZE_IN_LONGS(MULTIPLAYER_MAXIMUM_PLAYERS)] = { 0 };
|
|
long score = game_engine->get_player_score(player_index, score_type);
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
other_player = (struct player_datum *)data_iterator_next(&iterator);
|
|
while (other_player)
|
|
{
|
|
boolean different_player;
|
|
|
|
if (score_type == _get_score_team)
|
|
{
|
|
different_player =
|
|
other_player->team_index != player->team_index;
|
|
}
|
|
else
|
|
different_player = iterator.datum_index != player_index;
|
|
|
|
if (different_player &&
|
|
score_type == _get_score_team)
|
|
{
|
|
/* a team outside the mask (none yet) counts on its own */
|
|
if (VALID_INDEX(other_player->team_index, MULTIPLAYER_MAXIMUM_PLAYERS))
|
|
{
|
|
if (BIT_VECTOR_TEST_FLAG(team_mask, other_player->team_index))
|
|
different_player = FALSE;
|
|
else
|
|
BIT_VECTOR_SET_FLAG(team_mask, other_player->team_index, TRUE);
|
|
}
|
|
}
|
|
|
|
if (different_player)
|
|
{
|
|
long other_score = game_engine->get_player_score(
|
|
iterator.datum_index,
|
|
score_type);
|
|
|
|
group_count++;
|
|
if (other_score != score)
|
|
all_tied = FALSE;
|
|
|
|
if (other_score > score)
|
|
result.place++;
|
|
else if (other_score == score)
|
|
tied = TRUE;
|
|
}
|
|
|
|
other_player =
|
|
(struct player_datum *)data_iterator_next(&iterator);
|
|
}
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\game\\game_engine.c",
|
|
0x1377,
|
|
!all_tied || tied || group_count == 1,
|
|
"(!all_tied || (tied)) || (1 == group_count)");
|
|
}
|
|
|
|
all_tied &= tied;
|
|
result.flags = score_type != _get_score_team
|
|
? 0
|
|
: FLAG(_place_team);
|
|
SET_FLAG(result.flags, _place_tied, tied);
|
|
SET_FLAG(
|
|
result.flags,
|
|
_place_all_tied,
|
|
all_tied);
|
|
SET_FLAG(
|
|
result.flags,
|
|
_place_two_groups,
|
|
group_count == 2);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void netgame_verify_spawn_points(
|
|
short game_type,
|
|
short unused_team_index,
|
|
short minimum_count,
|
|
char const *error_message)
|
|
{
|
|
short starting_location_count;
|
|
long matching_count;
|
|
short starting_location_index;
|
|
|
|
starting_location_count = player_get_starting_location_count();
|
|
matching_count = 0;
|
|
for (starting_location_index = 0;
|
|
starting_location_index < starting_location_count;
|
|
starting_location_index++)
|
|
{
|
|
struct player_starting_location *starting_location =
|
|
player_get_starting_location(starting_location_index);
|
|
|
|
if (match_game_type(
|
|
game_type,
|
|
4,
|
|
starting_location->game_types))
|
|
{
|
|
matching_count++;
|
|
}
|
|
}
|
|
|
|
if (matching_count < minimum_count)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
error_message,
|
|
matching_count,
|
|
minimum_count);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
long game_engine_slayer_write_network_state(byte *buffer, long size);
|
|
boolean game_engine_slayer_read_network_state(byte const *buffer, long size, boolean first);
|
|
long game_engine_ctf_write_network_state(byte *buffer, long size);
|
|
boolean game_engine_ctf_read_network_state(byte const *buffer, long size, boolean first);
|
|
long game_engine_oddball_write_network_state(byte *buffer, long size);
|
|
boolean game_engine_oddball_read_network_state(byte const *buffer, long size, boolean first);
|
|
long game_engine_king_write_network_state(byte *buffer, long size);
|
|
boolean game_engine_king_read_network_state(byte const *buffer, long size, boolean first);
|
|
long game_engine_race_write_network_state(byte *buffer, long size);
|
|
boolean game_engine_race_read_network_state(byte const *buffer, long size, boolean first);
|
|
|
|
/* the distributed netcode (port/linux/game/network_damage.c): a player's
|
|
score as the game type has it (with its killing blows' messages), 0 for
|
|
none */
|
|
long game_engine_network_player_score(
|
|
long player_index)
|
|
{
|
|
if (!game_engine || !game_engine->get_player_score)
|
|
return 0;
|
|
return game_engine->get_player_score(player_index, TRUE);
|
|
}
|
|
|
|
/* the distributed netcode (port/linux/game/network_distributed.c): the
|
|
current game type's state (scores, and what else every machine must agree
|
|
on), which the host sends its clients; the size written, 0 for none */
|
|
long game_engine_write_network_state(
|
|
byte *buffer,
|
|
long size)
|
|
{
|
|
long postgame_state;
|
|
long written;
|
|
|
|
if (!game_engine || size < (long)sizeof(postgame_state))
|
|
return 0;
|
|
/* whether the game is over, then the game type's */
|
|
postgame_state = game_engine_globals.postgame_state;
|
|
csmemcpy(buffer, &postgame_state, sizeof(postgame_state));
|
|
buffer += sizeof(postgame_state);
|
|
size -= sizeof(postgame_state);
|
|
switch (game_engine_get_type())
|
|
{
|
|
case game_engine_ctf: written = game_engine_ctf_write_network_state(buffer, size); break;
|
|
case game_engine_slayer: written = game_engine_slayer_write_network_state(buffer, size); break;
|
|
case game_engine_oddball: written = game_engine_oddball_write_network_state(buffer, size); break;
|
|
case game_engine_king: written = game_engine_king_write_network_state(buffer, size); break;
|
|
case game_engine_race: written = game_engine_race_write_network_state(buffer, size); break;
|
|
default: written = 0; break;
|
|
}
|
|
return sizeof(postgame_state) + written;
|
|
}
|
|
|
|
/* a client: the host's */
|
|
void game_engine_read_network_state(
|
|
byte const *buffer,
|
|
long size)
|
|
{
|
|
long postgame_state;
|
|
boolean first;
|
|
boolean read;
|
|
|
|
if (!game_engine || size < (long)sizeof(postgame_state))
|
|
return;
|
|
csmemcpy(&postgame_state, buffer, sizeof(postgame_state));
|
|
buffer += sizeof(postgame_state);
|
|
size -= sizeof(postgame_state);
|
|
first = !game_engine_network_state_read;
|
|
switch (game_engine_get_type())
|
|
{
|
|
case game_engine_ctf: read = game_engine_ctf_read_network_state(buffer, size, first); break;
|
|
case game_engine_slayer: read = game_engine_slayer_read_network_state(buffer, size, first); break;
|
|
case game_engine_oddball: read = game_engine_oddball_read_network_state(buffer, size, first); break;
|
|
case game_engine_king: read = game_engine_king_read_network_state(buffer, size, first); break;
|
|
case game_engine_race: read = game_engine_race_read_network_state(buffer, size, first); break;
|
|
default: read = TRUE; break;
|
|
}
|
|
/* (a state the game type refused is not had: the first it takes is,
|
|
whose events it only takes) */
|
|
if (!read)
|
|
return;
|
|
game_engine_network_state_read = TRUE;
|
|
/* the game ended on the host (after what ended it is shown, as the host
|
|
shows it) */
|
|
if (postgame_state != 0 && game_engine_globals.postgame_state == 0)
|
|
game_engine_end_game();
|
|
}
|