mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-19 13:00:28 +02:00
The touch controls stay off in VR alongside the chat's on-screen keyboard (xinput_sdl.c); the join codes and the online list beside the server browser's listed hosts (p2p.c). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3701 lines
97 KiB
C
3701 lines
97 KiB
C
/*
|
|
MAIN.C
|
|
|
|
symbols in this file:
|
|
000EF6F0 0010:
|
|
_main_get_seconds_elapsed (0000)
|
|
000EF700 0080:
|
|
_gamepad_button_is_down (0000)
|
|
000EF780 0010:
|
|
_game_connection_set (0000)
|
|
000EF790 0010:
|
|
_game_connection (0000)
|
|
000EF7A0 0010:
|
|
_main_disallow_persistent_storage (0000)
|
|
000EF7B0 0060:
|
|
_main_set_map_name (0000)
|
|
000EF810 0010:
|
|
_main_defer_map_map_change (0000)
|
|
000EF820 0030:
|
|
_main_set_multiplayer_map_name (0000)
|
|
000EF850 0010:
|
|
_main_get_map_name (0000)
|
|
000EF860 0010:
|
|
_main_get_multiplayer_map_name (0000)
|
|
000EF870 0020:
|
|
_main_set_difficulty (0000)
|
|
000EF890 0010:
|
|
_main_get_difficulty (0000)
|
|
000EF8A0 0040:
|
|
_sort_desired_local_player_controllers (0000)
|
|
000EF8E0 01d0:
|
|
_code_000ef8e0 (0000)
|
|
000EFAB0 0020:
|
|
_main_reset_map (0000)
|
|
000EFAD0 0020:
|
|
_main_revert_map (0000)
|
|
000EFAF0 0020:
|
|
_main_skip_cinematic (0000)
|
|
000EFB10 0010:
|
|
_main_save_map_nonsafe (0000)
|
|
000EFB20 0010:
|
|
_main_saving_map (0000)
|
|
000EFB30 0010:
|
|
_main_save_cancel (0000)
|
|
000EFB40 0040:
|
|
_main_save_map_safe (0000)
|
|
000EFB80 0010:
|
|
_main_won_map (0000)
|
|
000EFB90 0010:
|
|
_main_lost_map (0000)
|
|
000EFBA0 0020:
|
|
_main_respawn (0000)
|
|
000EFBC0 0020:
|
|
_main_save_core (0000)
|
|
000EFBE0 0050:
|
|
_main_save_core_name (0000)
|
|
000EFC30 0020:
|
|
_main_load_core (0000)
|
|
000EFC50 0020:
|
|
_main_load_core_at_startup (0000)
|
|
000EFC70 0050:
|
|
_main_load_core_name (0000)
|
|
000EFCC0 0050:
|
|
_main_load_core_name_at_startup (0000)
|
|
000EFD10 0060:
|
|
_main_switch_structure_bsp (0000)
|
|
000EFD70 0030:
|
|
_main_skip (0000)
|
|
000EFDA0 0040:
|
|
_main_queue_map_name (0000)
|
|
000EFDE0 0050:
|
|
_code_000efde0 (0000)
|
|
000EFE30 0020:
|
|
_main_goto_main_menu (0000)
|
|
000EFE50 0050:
|
|
_main_menu_precache_resources (0000)
|
|
000EFEA0 0020:
|
|
_main_menu_unload (0000)
|
|
000EFEC0 0010:
|
|
_main_menu_ensure_player_queues_exist (0000)
|
|
000EFED0 0010:
|
|
_main_menu_fade_active (0000)
|
|
000EFEE0 0010:
|
|
_main_menu_switch_to_single_player (0000)
|
|
000EFEF0 0010:
|
|
_main_set_game_connection_to_film_playback (0000)
|
|
000EFF00 0170:
|
|
_main_get_solo_level_from_name (0000)
|
|
000F0070 0010:
|
|
_main_get_current_solo_level (0000)
|
|
000F0080 0020:
|
|
_main_get_solo_level_name (0000)
|
|
000F00A0 0010:
|
|
_main_run_demos (0000)
|
|
000F00B0 0070:
|
|
_code_000f00b0 (0000)
|
|
000F0120 01f0:
|
|
_compute_window_bounds (0000)
|
|
000F0310 0040:
|
|
_main_get_window_count (0000)
|
|
000F0350 00d0:
|
|
_code_000f0350 (0000)
|
|
000F0420 0180:
|
|
_code_000f0420 (0000)
|
|
000F05A0 0020:
|
|
_code_000f05a0 (0000)
|
|
000F05C0 0030:
|
|
_code_000f05c0 (0000)
|
|
000F05F0 00b0:
|
|
_code_000f05f0 (0000)
|
|
000F06A0 0020:
|
|
_code_000f06a0 (0000)
|
|
000F06C0 00e0:
|
|
_code_000f06c0 (0000)
|
|
000F07A0 0030:
|
|
_code_000f07a0 (0000)
|
|
000F07D0 0040:
|
|
_code_000f07d0 (0000)
|
|
000F0810 0040:
|
|
_code_000f0810 (0000)
|
|
000F0850 0060:
|
|
_code_000f0850 (0000)
|
|
000F08B0 0020:
|
|
_code_000f08b0 (0000)
|
|
000F08D0 0020:
|
|
_code_000f08d0 (0000)
|
|
000F08F0 0040:
|
|
_code_000f08f0 (0000)
|
|
000F0930 0010:
|
|
_code_000f0930 (0000)
|
|
000F0940 0220:
|
|
_code_000f0940 (0000)
|
|
000F0B60 0050:
|
|
_code_000f0b60 (0000)
|
|
000F0BB0 0030:
|
|
_code_000f0bb0 (0000)
|
|
000F0BE0 0010:
|
|
_main_framerate_throttle_enabled (0000)
|
|
000F0BF0 05a0:
|
|
_code_000f0bf0 (0000)
|
|
000F1190 0210:
|
|
_main_rasterizer_throttle (0000)
|
|
000F13A0 0020:
|
|
_code_000f13a0 (0000)
|
|
000F13C0 0020:
|
|
_main_taking_screenshot (0000)
|
|
000F13E0 00b0:
|
|
_main_movie_start (0000)
|
|
000F1490 0020:
|
|
_main_movie_stop (0000)
|
|
000F14B0 0010:
|
|
_main_stop_time (0000)
|
|
000F14C0 0010:
|
|
_main_start_time (0000)
|
|
000F14D0 0010:
|
|
_main_crash (0000)
|
|
000F14E0 0010:
|
|
_main_print_version (0000)
|
|
000F14F0 00c0:
|
|
_main_vertical_blank_interrupt_handler (0000)
|
|
000F15B0 0070:
|
|
_main_save_current_solo_map (0000)
|
|
000F1620 00c0:
|
|
_main_load_last_solo_map (0000)
|
|
000F16E0 0040:
|
|
_main_save_map_no_timeout (0000)
|
|
000F1720 00e0:
|
|
_main_load_ui_scenario (0000)
|
|
000F1800 0090:
|
|
_main_menu_load (0000)
|
|
000F1890 0020:
|
|
_main_roll_credits (0000)
|
|
000F18B0 0130:
|
|
_main_pregame_render (0000)
|
|
000F19E0 01b0:
|
|
_set_window_camera_values (0000)
|
|
000F1B90 0090:
|
|
_main_present_frame (0000)
|
|
000F1C20 00c0:
|
|
_code_000f1c20 (0000)
|
|
000F1CE0 0050:
|
|
_code_000f1ce0 (0000)
|
|
000F1D30 01f0:
|
|
_code_000f1d30 (0000)
|
|
000F1F20 02a0:
|
|
_main_framerate_render (0000)
|
|
000F21C0 02a0:
|
|
_halt_and_catch_fire (0000)
|
|
000F2460 0040:
|
|
_main_loop_of_death (0000)
|
|
000F24A0 01c0:
|
|
_code_000f24a0 (0000)
|
|
000F2660 0670:
|
|
_main_loop (0000)
|
|
002795A8 003a:
|
|
??_C@_0DK@GMPCKHNN@button_index?$DO?$DN0?5?$CG?$CG?5button_index?$DM@ (0000)
|
|
002795E4 001b:
|
|
??_C@_0BL@EKABHJPH@c?3?2halo?2SOURCE?2main?2main?4c?$AA@ (0000)
|
|
00279600 0037:
|
|
??_C@_0DH@DGKBOONK@?$CIgamepad_index?$DO?$DN0?$CJ?5?$CG?$CG?5?$CIgamepad_i@ (0000)
|
|
00279638 0013:
|
|
??_C@_0BD@OOOKAGFE@j?$DMMAXIMUM_GAMEPADS?$AA@ (0000)
|
|
00279650 0049:
|
|
??_C@_0EJ@DIAKEACK@?$CIdesired_controllers?$FLi?$FN?$DO?$DN0?$CJ?5?$CG?$CG?5?$CI@ (0000)
|
|
0027969C 002c:
|
|
??_C@_0CM@EOEJOPGF@game_connection?$CI?$CJ?5?$DN?$DN?5_game_conne@ (0000)
|
|
002796C8 0009:
|
|
??_C@_08FLLKDKH@core?4bin?$AA@ (0000)
|
|
002796D4 003b:
|
|
??_C@_0DL@LKJEADLD@warning?0?5core?5file?5name?5will?5be?5@ (0000)
|
|
00279710 002c:
|
|
??_C@_0CM@BPJFOCNH@tried?5to?5switch?5to?5invalid?5struc@ (0000)
|
|
0027973C 002c:
|
|
??_C@_0CM@IOHNOMHF@tried?5to?5switch?5to?5current?5struc@ (0000)
|
|
00279768 0030:
|
|
??_C@_0DA@FGIPPFOE@cannot?5skip?5more?5than?515?5frames?5@ (0000)
|
|
00279798 0029:
|
|
??_C@_0CJ@GNBMIDNC@scenario?9?$DOtype?$DN?$DN_scenario_type_m@ (0000)
|
|
002797C4 000e:
|
|
??_C@_0O@OJNPMJKK@num_players?$DO0?$AA@ (0000)
|
|
002797D4 0039:
|
|
??_C@_0DJ@LGIIGICN@horizontal_index?$DO?$DN0?5?$CG?$CG?5horizonta@ (0000)
|
|
00279810 0033:
|
|
??_C@_0DD@NNOBFMAG@vertical_index?$DO?$DN0?5?$CG?$CG?5vertical_in@ (0000)
|
|
00279844 0019:
|
|
??_C@_0BJ@LDIFOEOI@player_index?$DMnum_players?$AA@ (0000)
|
|
00279860 0017:
|
|
??_C@_0BH@PGKHPIFN@main_new_map?$CI?$CJ?5failed?4?$AA@ (0000)
|
|
00279878 0014:
|
|
??_C@_0BE@HPJEDLPC@game_load?$CI?$CJ?5failed?4?$AA@ (0000)
|
|
00279890 0041:
|
|
??_C@_0EB@NLDLOJCN@manual?5skipping?5doesn?8t?5work?5out@ (0000)
|
|
002798D4 000c:
|
|
??_C@_0M@NBLPJHAH@unsafe?5save?$AA@ (0000)
|
|
002798E0 0017:
|
|
??_C@_0BH@JAFGAFKC@gave?5up?5trying?5to?5save?$AA@ (0000)
|
|
002798F8 001f:
|
|
??_C@_0BP@JOHDPJOL@?$DLcore_load_name_at_startup?5?$CFs?6?$AA@ (0000)
|
|
00279918 0004:
|
|
??_C@_03CCGKKFHG@a?$CLt?$AA@ (0000)
|
|
0027991C 000d:
|
|
??_C@_0N@HMFDKLFI@map_name?5?$CFs?6?$AA@ (0000)
|
|
0027992C 0003:
|
|
??_C@_02BKFDOEMK@wt?$AA@ (0000)
|
|
00279930 000f:
|
|
??_C@_0P@JFNDGJMB@d?3?2?$CFs_init?4txt?$AA@ (0000)
|
|
00279940 001e:
|
|
??_C@_0BO@EFIKKGLE@?$CFs_slow_?$CFd_?$CFd_?$CFd_?$CFd_?$CFd_?$CFd?4bin?$AA@ (0000)
|
|
00279960 0008:
|
|
__real@3fa26e978d4fdf3c (0000)
|
|
00279968 0013:
|
|
??_C@_0BD@BANPCAEP@?5des?5?$CFd?5targ?$CF6I64d?$AA@ (0000)
|
|
0027997C 000d:
|
|
??_C@_0N@ONKBMNKN@?5MAINTAIN?5?$CFd?$AA@ (0000)
|
|
0027998C 000d:
|
|
??_C@_0N@MLBLHBF@?5RESTORE?5?5?$CFd?$AA@ (0000)
|
|
0027999C 000d:
|
|
??_C@_0N@KHJNGILI@?5FAILDOWN?5?$CFd?$AA@ (0000)
|
|
002799AC 000d:
|
|
??_C@_0N@KAJPDIPB@?$CI?$CFs?$CF2d?1?$CF2d?$CJ?5?$AA@ (0000)
|
|
002799BC 0003:
|
|
??_C@_02LEBOADDI@dn?$AA@ (0000)
|
|
002799C0 000c:
|
|
??_C@_0M@FONKJGHM@?$CIok?5?5?5?$CF2d?$CJ?5?$AA@ (0000)
|
|
002799CC 0006:
|
|
??_C@_05MPKPLMMK@ignor?$AA@ (0000)
|
|
002799D4 0009:
|
|
??_C@_08NGBFCLJE@?$CI?$CFs?$CF2d?$CJ?5?$AA@ (0000)
|
|
002799E0 0006:
|
|
??_C@_05KGKFECIJ@fail?5?$AA@ (0000)
|
|
002799E8 003a:
|
|
??_C@_0DK@KGLGNIMB@last?$CF6I64d?5init?$CF6I64d?5achv?$CF6I64d@ (0000)
|
|
00279A24 0019:
|
|
??_C@_0BJ@GBFFHJIM@?$CF6I64d?$CItarg?$CF6I64d?5?$CFs?$CF2d?$CJ?$AA@ (0000)
|
|
00279A40 0009:
|
|
??_C@_08EEAEDAMO@LAPSED?5?5?$AA@ (0000)
|
|
00279A4C 0009:
|
|
??_C@_08KMBEHCPI@SYNCED?5?5?$AA@ (0000)
|
|
00279A58 0009:
|
|
??_C@_08PLAGFMEO@THROTTLE?$AA@ (0000)
|
|
00279A68 004a:
|
|
??_C@_0EK@OBDHCGCK@stuck?5waiting?5for?5VBLANK?5callbac@ (0000)
|
|
00279AB4 0006:
|
|
??_C@_05PAONDCEJ@movie?$AA@ (0000)
|
|
00279ABC 0019:
|
|
??_C@_0BJ@MFOKCFOP@main_globals?4movie?$DN?$DNNULL?$AA@ (0000)
|
|
00279AD8 002a:
|
|
??_C@_0CK@IJIKLLCA@chucky?5was?5here?$CB?5?5NULL?5belongs?5t@ (0000)
|
|
00279B04 0031:
|
|
??_C@_0DB@HPIOCFDH@halobeta?5xbox?501?401?414?42342?5Jan?5@ (0000)
|
|
00279B38 0038:
|
|
??_C@_0DI@IKBOMHNJ@Couldn?8t?5create?5a?5file?5to?5write?5@ (0000)
|
|
00279B70 0011:
|
|
??_C@_0BB@HAHOOKLD@z?3?2last_solo?4txt?$AA@ (0000)
|
|
00279B84 0028:
|
|
??_C@_0CI@KPDFMEMF@?$CBmain_globals?4main_menu_scenario@ (0000)
|
|
00279BAC 000d:
|
|
??_C@_0N@CBIELCEF@levels?2ui?2ui?$AA@ (0000)
|
|
00279BBC 0023:
|
|
??_C@_0CD@IMDNHNHI@congratulations?0?5you?5won?5the?5gam@ (0000)
|
|
00279BE0 0014:
|
|
??_C@_0BE@BDDOCJAN@movie?2frame?$CF06d?4tga?$AA@ (0000)
|
|
00279BF4 0019:
|
|
??_C@_0BJ@CKCGDFO@error?5opening?5saved?5film?$AA@ (0000)
|
|
00279C10 0015:
|
|
??_C@_0BF@LPIHHLNO@?$CFdscreenshot?$CFd?$CFd?4tif?$AA@ (0000)
|
|
00279C28 003b:
|
|
??_C@_0DL@LMMABMNB@halobeta?5xbox?501?401?414?42342?5buil@ (0000)
|
|
00279C64 001f:
|
|
??_C@_0BP@HEAODFIJ@old?5tags?2internal?5system?5plain?$AA@ (0000)
|
|
00279C84 0012:
|
|
??_C@_0BC@FLAPBDJB@end?5of?5saved?5film?$AA@ (0000)
|
|
00279C98 0018:
|
|
??_C@_0BI@HBMJOOEK@the?5game?5host?5went?5down?$AA@ (0000)
|
|
00307818 0034:
|
|
_global_difficulty_level (0000)
|
|
_player_spawn_count (0004)
|
|
_global_frame_rate_throttle (0006)
|
|
_global_screenshot_size (0008)
|
|
00455750 0a63:
|
|
_bss_00455750 (0000)
|
|
_debug_force_frame_rate_update (06d0)
|
|
_debug_no_drawing (06d1)
|
|
_debug_game_save (06d2)
|
|
_debug_frame_rate (06d3)
|
|
_display_framerate (06d4)
|
|
_display_vblank_deltas (06d5)
|
|
_display_precache_progress (06d6)
|
|
_global_screenshot_count (06d8)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "errors.h"
|
|
#include "cseries/profile.h"
|
|
#include "cseries/cseries_windows.h"
|
|
#include "main.h"
|
|
#include "real_math.h"
|
|
#include "game.h"
|
|
#include "game_engine.h"
|
|
#include "camera/camera_scripting.h"
|
|
#include "game/cheats.h"
|
|
#include "game/player_control_runtime.h"
|
|
#include "game/players.h"
|
|
#include "game/local_players.h"
|
|
#include "game/player_queues_new.h"
|
|
#include "integer_math.h"
|
|
#include "input.h"
|
|
#include "input/input_abstraction.h"
|
|
#include "shell.h"
|
|
#include "event_manager.h"
|
|
#include "telnet_console.h"
|
|
#include "ui_widget.h"
|
|
#include "editor_stubs.h"
|
|
#include "console.h"
|
|
#include "debug_keys.h"
|
|
#include "scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "cache/cache_files.h"
|
|
#include "cache/predicted_resources.h"
|
|
#include "bitmaps/bitmap_group.h"
|
|
#include "bitmaps/bitmaps.h"
|
|
#include "bitmaps/tiff_file.h"
|
|
#include "interface/hud.h"
|
|
#include "interface/hud_definitions.h"
|
|
#include "interface/attract_mode.h"
|
|
#include "interface/interface.h"
|
|
#include "interface/marketing_and_strategic_business_development.h"
|
|
#include "interface/player_ui.h"
|
|
#include "interface/terminal.h"
|
|
#include "saved games/player_profile.h"
|
|
#include "saved games/game_state.h"
|
|
#include "sound/sound_manager.h"
|
|
#include "rasterizer/rasterizer.h"
|
|
#include "rasterizer/rasterizer_console_vars.h"
|
|
#include "bink/bink_playback.h"
|
|
#include "main/d3d_intimacy.h"
|
|
#include "networking/network_game_globals.h"
|
|
#include "networking/network_game_manager.h"
|
|
#include "networking/network_server_manager.h" /* port: a co-op game's level won */
|
|
#include "network_coop.h" /* port: port/linux/game/network_coop.c */
|
|
#include "network_voice.h" /* port: port/linux/game/network_voice.c */
|
|
#include "network_chat.h" /* port: port/linux/game/network_chat.c */
|
|
#include "map_share.h" /* port: port/linux/game/map_share.c */
|
|
#include "profile_sections.h" /* port: port/linux/include/profile_sections.h */
|
|
#ifdef HALO_PROFILE
|
|
#include "profile_trace.h" /* port: port/linux/src/profile_trace.c */
|
|
#endif
|
|
#ifdef HALO_WEB
|
|
#include <emscripten.h> /* port: the browser runs the main loop (port/web/README.md) */
|
|
#endif
|
|
#include "camera/director.h"
|
|
#include "camera/observer.h"
|
|
#include "cutscene/cinematics.h"
|
|
#include "effects/player_effects.h"
|
|
#include "physics/collision_usage.h"
|
|
#include "physics/collision_debug.h"
|
|
#include "hs/hs.h"
|
|
#include "render/render.h"
|
|
#include "text/draw_string.h"
|
|
#include "text/font_group.h"
|
|
#include "tag_files/files.h"
|
|
#ifdef HALO_ANDROID
|
|
#include "halo_vr.h"
|
|
#endif
|
|
#include "custom_edition_cache.h" /* port: custom_edition_level_name */
|
|
#include "view_fov.h" /* port: display.fov, before the projection and culling */
|
|
|
|
#if defined(HALO_WINDOWS) || defined(HALO_ANDROID) || defined(__linux__)
|
|
#define HALO_NATIVE_BUILD_INFO 1
|
|
#ifndef HALO_BUILD_NUMBER
|
|
#define HALO_BUILD_NUMBER 0
|
|
#endif
|
|
#ifndef HALO_BUILD_FLAVOR
|
|
#define HALO_BUILD_FLAVOR "local"
|
|
#endif
|
|
#endif
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
_bitmap_format_x8r8g8b8 = 10,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_text_justification_left = 0,
|
|
_text_justification_right,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
/* ---------- structures */
|
|
|
|
/*
|
|
struct _main_globals
|
|
{
|
|
long long last_time_clocks; // 0x0
|
|
unsigned long last_time_msec; // 0x8
|
|
long long last_render_clocks; // 0x10
|
|
long long last_vblank_index; // 0x18
|
|
long long last_initial_vblank_index; // 0x20
|
|
long long last_achievable_vblank_index; // 0x28
|
|
long long last_present_vblank_index; // 0x30
|
|
unsigned char did_time_overflow_occur; // 0x38
|
|
float seconds_elapsed; // 0x3C
|
|
short connection; // 0x40
|
|
unsigned short screenshot_identifier; // 0x42
|
|
bitmap_data *movie; // 0x44
|
|
long recording_start_tick; // 0x48
|
|
long recording_stop_tick; // 0x4C
|
|
long recording_frame_index; // 0x50
|
|
float recording_dt; // 0x54
|
|
unsigned char reset_map; // 0x58
|
|
unsigned char rename_map; // 0x59
|
|
unsigned char revert_map; // 0x5A
|
|
unsigned char skip_cinematic; // 0x5B
|
|
unsigned char save_map; // 0x5C
|
|
unsigned char save_map_safely; // 0x5D
|
|
unsigned char save_map_timeout; // 0x5E
|
|
unsigned char saving_map; // 0x5F
|
|
long ticks_until_next_save_check; // 0x60
|
|
long ticks_unable_to_save; // 0x64
|
|
unsigned long map_change_load_timer; // 0x68
|
|
short safe_intervals; // 0x6C
|
|
unsigned char won_map; // 0x6E
|
|
unsigned char lost_map; // 0x6F
|
|
unsigned char respawn; // 0x70
|
|
unsigned char save_core; // 0x71
|
|
unsigned char load_core; // 0x72
|
|
unsigned char load_core_at_startup; // 0x73
|
|
short switch_to_structure_bsp_index; // 0x74
|
|
unsigned char main_menu_scenario_loaded; // 0x76
|
|
unsigned char want_to_be_at_main_menu; // 0x77
|
|
unsigned char run_xdemos; // 0x78
|
|
unsigned char fade_to_dashboard; // 0x79
|
|
unsigned char exit_to_dashboard; // 0x7A
|
|
unsigned char want_to_exit; // 0x7B
|
|
long idle_timeout; // 0x7C
|
|
long idle_last_interesting; // 0x80
|
|
long idle_last_activity; // 0x84
|
|
unsigned char playback_last_recording; // 0x88
|
|
unsigned char halt_time_scale; // 0x89
|
|
unsigned char restart_time; // 0x8A
|
|
unsigned char load_last_solo_level; // 0x8B
|
|
unsigned char cutscene_skip; // 0x8C
|
|
short skip_ticks; // 0x8E
|
|
short loss_timer; // 0x90
|
|
short respawn_timer; // 0x92
|
|
unsigned char queue_map; // 0x94
|
|
unsigned char pad0[3]; // 0x95
|
|
unsigned char solo_try_and_load_from_persistent_storage; // 0x98
|
|
char soloplayer_map_name[256]; // 0x99
|
|
char multiplayer_map_name[256]; // 0x199
|
|
char queued_map_name[256]; // 0x299
|
|
unsigned char want_to_connect; // 0x399
|
|
char connect_address[32]; // 0x39A
|
|
char connect_password[9]; // 0x3BA
|
|
short vblank_interval_current; // 0x3C4
|
|
short vblank_interval_minimum; // 0x3C6
|
|
unsigned char vblank_interval_held; // 0x3C8
|
|
short vblank_failure_count[6]; // 0x3CA
|
|
long long vblank_last_failure_time[6]; // 0x3D8
|
|
unsigned long *vblank_flip_counter; // 0x408
|
|
short vblank_flip_delta_next_index; // 0x40C
|
|
short vblank_flip_deltas[15]; // 0x40E
|
|
};
|
|
*/
|
|
|
|
struct _main_globals
|
|
{
|
|
unsigned long frame_start_milliseconds;
|
|
byte reserved04[4];
|
|
union
|
|
{
|
|
__int64 rasterizer_target_index;
|
|
struct
|
|
{
|
|
unsigned long rasterizer_frame_index;
|
|
unsigned long rasterizer_vertical_blank_index;
|
|
};
|
|
};
|
|
__int64 rasterizer_initial_index;
|
|
__int64 rasterizer_throttle_start_index;
|
|
__int64 rasterizer_throttle_end_index;
|
|
real seconds_elapsed;
|
|
short connection;
|
|
unsigned short screenshot_identifier;
|
|
struct bitmap_data *movie;
|
|
long recording_start_tick;
|
|
long recording_stop_tick;
|
|
long recording_frame_index;
|
|
real recording_dt;
|
|
boolean reset_map;
|
|
boolean defer_map_change;
|
|
boolean revert_map;
|
|
boolean skip_cinematic;
|
|
boolean saving_map;
|
|
boolean save_map_safely;
|
|
boolean save_map_timeout;
|
|
boolean save_map_completed;
|
|
long ticks_until_next_save_check;
|
|
long ticks_unable_to_save;
|
|
unsigned long map_change_load_timer;
|
|
short safe_intervals;
|
|
boolean won_map;
|
|
boolean lost_map;
|
|
boolean respawn;
|
|
boolean save_core;
|
|
boolean load_core;
|
|
boolean load_core_at_startup;
|
|
short switch_to_structure_bsp_index;
|
|
boolean main_menu_scenario_loaded;
|
|
boolean want_to_be_at_main_menu;
|
|
boolean run_xdemos;
|
|
boolean playback_last_recording;
|
|
boolean halt_time_scale;
|
|
boolean restart_time;
|
|
boolean load_last_solo_level;
|
|
boolean skip;
|
|
short skip_ticks;
|
|
short loss_timer;
|
|
short respawn_timer;
|
|
boolean queue_map;
|
|
byte __unknown121[0x3];
|
|
boolean allow_persistent_storage;
|
|
char soloplayer_map_name[256];
|
|
char multiplayer_map_name[256];
|
|
char queued_map_name[256];
|
|
char core_name[64];
|
|
byte reserved3B5;
|
|
short vblank_interval_current;
|
|
short vblank_interval_requested;
|
|
boolean vblank_interval_held;
|
|
byte reserved3BB;
|
|
short vblank_failure_counts[6];
|
|
__int64 vblank_last_failure_indices[6];
|
|
volatile unsigned int *d3d_flip_count;
|
|
short vblank_flip_delta_index;
|
|
short vblank_flip_deltas[15];
|
|
char vblank_debug_string[0x200];
|
|
byte reserved61C[4];
|
|
};
|
|
|
|
typedef char main_hud_globals_font_tag_index_offset_assert[
|
|
offsetof(struct hud_globals_definition, messaging.single_player_font.index) == 0x54 ? 1 : -1];
|
|
|
|
typedef char main_globals_size_assert[
|
|
sizeof(struct _main_globals) == 0x620 ? 1 : -1];
|
|
typedef char main_globals_frame_start_milliseconds_offset_assert[
|
|
offsetof(struct _main_globals, frame_start_milliseconds) == 0x00 ? 1 : -1];
|
|
typedef char main_globals_rasterizer_target_index_offset_assert[
|
|
offsetof(struct _main_globals, rasterizer_target_index) == 0x08 ? 1 : -1];
|
|
typedef char main_globals_rasterizer_initial_index_offset_assert[
|
|
offsetof(struct _main_globals, rasterizer_initial_index) == 0x10 ? 1 : -1];
|
|
typedef char main_globals_rasterizer_throttle_start_index_offset_assert[
|
|
offsetof(struct _main_globals, rasterizer_throttle_start_index) == 0x18 ? 1 : -1];
|
|
typedef char main_globals_rasterizer_throttle_end_index_offset_assert[
|
|
offsetof(struct _main_globals, rasterizer_throttle_end_index) == 0x20 ? 1 : -1];
|
|
typedef char main_globals_seconds_elapsed_offset_assert[
|
|
offsetof(struct _main_globals, seconds_elapsed) == 0x28 ? 1 : -1];
|
|
typedef char main_globals_connection_offset_assert[
|
|
offsetof(struct _main_globals, connection) == 0x2C ? 1 : -1];
|
|
typedef char main_globals_movie_offset_assert[
|
|
offsetof(struct _main_globals, movie) == 0x30 ? 1 : -1];
|
|
typedef char main_globals_defer_map_change_offset_assert[
|
|
offsetof(struct _main_globals, defer_map_change) == 0x45 ? 1 : -1];
|
|
typedef char main_globals_reset_map_offset_assert[
|
|
offsetof(struct _main_globals, reset_map) == 0x44 ? 1 : -1];
|
|
typedef char main_globals_revert_map_offset_assert[
|
|
offsetof(struct _main_globals, revert_map) == 0x46 ? 1 : -1];
|
|
typedef char main_globals_skip_cinematic_offset_assert[
|
|
offsetof(struct _main_globals, skip_cinematic) == 0x47 ? 1 : -1];
|
|
typedef char main_globals_saving_map_offset_assert[
|
|
offsetof(struct _main_globals, saving_map) == 0x48 ? 1 : -1];
|
|
typedef char main_globals_save_map_safely_offset_assert[
|
|
offsetof(struct _main_globals, save_map_safely) == 0x49 ? 1 : -1];
|
|
typedef char main_globals_won_map_offset_assert[
|
|
offsetof(struct _main_globals, won_map) == 0x5A ? 1 : -1];
|
|
typedef char main_globals_halt_time_scale_offset_assert[
|
|
offsetof(struct _main_globals, halt_time_scale) == 0x66 ? 1 : -1];
|
|
typedef char main_globals_respawn_timer_offset_assert[
|
|
offsetof(struct _main_globals, respawn_timer) == 0x6E ? 1 : -1];
|
|
typedef char main_globals_loss_timer_offset_assert[
|
|
offsetof(struct _main_globals, loss_timer) == 0x6C ? 1 : -1];
|
|
typedef char main_globals_allow_persistent_storage_offset_assert[
|
|
offsetof(struct _main_globals, allow_persistent_storage) == 0x74 ? 1 : -1];
|
|
typedef char main_globals_soloplayer_map_name_offset_assert[
|
|
offsetof(struct _main_globals, soloplayer_map_name) == 0x75 ? 1 : -1];
|
|
typedef char main_globals_multiplayer_map_name_offset_assert[
|
|
offsetof(struct _main_globals, multiplayer_map_name) == 0x175 ? 1 : -1];
|
|
typedef char main_globals_core_name_offset_assert[
|
|
offsetof(struct _main_globals, core_name) == 0x375 ? 1 : -1];
|
|
typedef char main_globals_vblank_interval_current_offset_assert[
|
|
offsetof(struct _main_globals, vblank_interval_current) == 0x3B6 ? 1 : -1];
|
|
typedef char main_globals_vblank_interval_requested_offset_assert[
|
|
offsetof(struct _main_globals, vblank_interval_requested) == 0x3B8 ? 1 : -1];
|
|
typedef char main_globals_vblank_interval_held_offset_assert[
|
|
offsetof(struct _main_globals, vblank_interval_held) == 0x3BA ? 1 : -1];
|
|
typedef char main_globals_vblank_failure_counts_offset_assert[
|
|
offsetof(struct _main_globals, vblank_failure_counts) == 0x3BC ? 1 : -1];
|
|
typedef char main_globals_vblank_last_failure_indices_offset_assert[
|
|
offsetof(struct _main_globals, vblank_last_failure_indices) == 0x3C8 ? 1 : -1];
|
|
typedef char main_globals_d3d_flip_count_offset_assert[
|
|
offsetof(struct _main_globals, d3d_flip_count) == 0x3F8 ? 1 : -1];
|
|
typedef char main_globals_vblank_flip_delta_index_offset_assert[
|
|
offsetof(struct _main_globals, vblank_flip_delta_index) == 0x3FC ? 1 : -1];
|
|
typedef char main_globals_vblank_flip_deltas_offset_assert[
|
|
offsetof(struct _main_globals, vblank_flip_deltas) == 0x3FE ? 1 : -1];
|
|
typedef char main_globals_vblank_debug_string_offset_assert[
|
|
offsetof(struct _main_globals, vblank_debug_string) == 0x41C ? 1 : -1];
|
|
|
|
struct game_options
|
|
{
|
|
unsigned long flags;
|
|
short code_version;
|
|
short difficulty;
|
|
unsigned long random_seed;
|
|
char map_name[256];
|
|
};
|
|
|
|
typedef char game_options_size_assert[
|
|
sizeof(struct game_options) == 0x10C ? 1 : -1];
|
|
|
|
#pragma pack(push, 1)
|
|
struct _screenshot_and_framerate_globals
|
|
{
|
|
short count;
|
|
byte reserved002[6];
|
|
struct render_window windows[MAXIMUM_WINDOWS + 1];
|
|
real framerate_samples[8];
|
|
word framerate_flags;
|
|
char framerate_sample_index;
|
|
boolean framerate_active;
|
|
char framerate_counter;
|
|
boolean framerate_reset;
|
|
boolean halt_recursion_lock;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
typedef char screenshot_and_framerate_globals_size_assert[
|
|
sizeof(struct _screenshot_and_framerate_globals) == 0x38B ? 1 : -1];
|
|
|
|
void network_test_update(boolean main_menu_loaded, real seconds);
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static long sort_desired_local_player_controllers(
|
|
short const *a,
|
|
short const *b);
|
|
static void main_setup_connection(
|
|
void);
|
|
static void main_initialize_time(
|
|
void);
|
|
static void main_skip_private(
|
|
void);
|
|
static void main_update_time(
|
|
void);
|
|
static void main_frame_rate_debug(
|
|
void);
|
|
|
|
static void main_new_map(
|
|
struct game_options *options);
|
|
static void main_game_render(
|
|
double time_delta_since_tick_sec);
|
|
|
|
/* ---------- globals */
|
|
|
|
short global_difficulty_level = 1;
|
|
short player_spawn_count = 1;
|
|
boolean global_frame_rate_throttle = TRUE;
|
|
short global_screenshot_size = 1;
|
|
|
|
static char const *scenario_paths[10] =
|
|
{
|
|
"levels\\a10\\a10",
|
|
"levels\\a30\\a30",
|
|
"levels\\a50\\a50",
|
|
"levels\\b30\\b30",
|
|
"levels\\b40\\b40",
|
|
"levels\\c10\\c10",
|
|
"levels\\c20\\c20",
|
|
"levels\\c40\\c40",
|
|
"levels\\d20\\d20",
|
|
"levels\\d40\\d40"
|
|
};
|
|
|
|
static struct _main_globals main_globals = { 0 };
|
|
/* Keep the original globals layout; these clocks belong to pending requests. */
|
|
static long main_loss_last_tick;
|
|
static long main_respawn_last_tick;
|
|
boolean debug_force_frame_rate_update = FALSE;
|
|
boolean debug_no_drawing = FALSE;
|
|
boolean debug_game_save = FALSE;
|
|
boolean debug_frame_rate = FALSE;
|
|
boolean display_framerate = FALSE;
|
|
boolean display_vblank_deltas = FALSE;
|
|
boolean display_precache_progress = FALSE;
|
|
struct _screenshot_and_framerate_globals global_screenshot_count = { 0 };
|
|
boolean debug_render_freeze;
|
|
|
|
/* ---------- public code */
|
|
|
|
real main_get_seconds_elapsed(
|
|
void)
|
|
{
|
|
return main_globals.seconds_elapsed;
|
|
}
|
|
|
|
boolean gamepad_button_is_down(
|
|
short button_index)
|
|
{
|
|
short gamepad_index;
|
|
boolean result = FALSE;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\main\\main.c", 245,
|
|
button_index>=0 && button_index<NUMBER_OF_GAMEPAD_BUTTONS);
|
|
gamepad_index = 0;
|
|
|
|
do
|
|
{
|
|
if (input_has_gamepad(gamepad_index))
|
|
break;
|
|
|
|
gamepad_index++;
|
|
}
|
|
while (gamepad_index < MAXIMUM_GAMEPADS);
|
|
|
|
if (gamepad_index < MAXIMUM_GAMEPADS)
|
|
{
|
|
struct gamepad_state const *gamepad = input_get_gamepad_state(gamepad_index);
|
|
|
|
result = gamepad->buttons[button_index] > 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void create_local_players(
|
|
void)
|
|
{
|
|
short used_controllers[MAXIMUM_GAMEPADS];
|
|
short default_controllers[MAXIMUM_GAMEPADS];
|
|
short desired_controllers[MAXIMUM_GAMEPADS];
|
|
long i;
|
|
long j;
|
|
long player;
|
|
short gamepad_index;
|
|
|
|
if (main_globals.main_menu_scenario_loaded)
|
|
{
|
|
player = player_new(0, NONE, 0, NULL);
|
|
local_player_set_player_index(
|
|
0,
|
|
player);
|
|
}
|
|
else
|
|
{
|
|
csmemset(used_controllers, NONE, sizeof(used_controllers));
|
|
csmemset(desired_controllers, NONE, sizeof(desired_controllers));
|
|
default_controllers[0] = 0;
|
|
default_controllers[1] = 1;
|
|
default_controllers[2] = 2;
|
|
default_controllers[3] = 3;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
741,
|
|
game_connection() == _game_connection_local);
|
|
for (i = 0; i < player_spawn_count; i++)
|
|
{
|
|
gamepad_index =
|
|
player_ui_get_single_player_local_player_controller(i);
|
|
desired_controllers[i] = gamepad_index;
|
|
if (desired_controllers[i] == NONE)
|
|
desired_controllers[i] = default_controllers[i];
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
755,
|
|
(desired_controllers[i]>=0) && (desired_controllers[i]<MAXIMUM_GAMEPADS));
|
|
|
|
if (used_controllers[desired_controllers[i]] != NONE)
|
|
{
|
|
for (j = 0; j < MAXIMUM_GAMEPADS; j++)
|
|
{
|
|
if (used_controllers[j] == NONE)
|
|
{
|
|
desired_controllers[i] = (short)j;
|
|
used_controllers[(short)j] = (short)j;
|
|
break;
|
|
}
|
|
}
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
768,
|
|
j<MAXIMUM_GAMEPADS);
|
|
}
|
|
else
|
|
{
|
|
used_controllers[desired_controllers[i]] = desired_controllers[i];
|
|
}
|
|
}
|
|
|
|
qsort(
|
|
desired_controllers,
|
|
MAXIMUM_GAMEPADS,
|
|
sizeof(*desired_controllers),
|
|
(int (__cdecl *)(void const *, void const *))sort_desired_local_player_controllers);
|
|
for (i = 0; i < player_spawn_count; i++)
|
|
{
|
|
gamepad_index = desired_controllers[i];
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
784,
|
|
(gamepad_index>=0) && (gamepad_index<MAXIMUM_GAMEPADS));
|
|
player = player_new(0, NONE, gamepad_index, NULL);
|
|
local_player_set_player_index(
|
|
gamepad_index,
|
|
player);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void game_connection_set(
|
|
short connection)
|
|
{
|
|
main_globals.connection = connection;
|
|
return;
|
|
}
|
|
|
|
short game_connection(
|
|
void)
|
|
{
|
|
return main_globals.connection;
|
|
}
|
|
|
|
void main_disallow_persistent_storage(
|
|
void)
|
|
{
|
|
main_globals.allow_persistent_storage = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_defer_map_map_change(
|
|
void)
|
|
{
|
|
main_globals.defer_map_change = FALSE;
|
|
return;
|
|
}
|
|
|
|
char *main_get_map_name(
|
|
void)
|
|
{
|
|
return main_globals.soloplayer_map_name;
|
|
}
|
|
|
|
char *main_get_multiplayer_map_name(
|
|
void)
|
|
{
|
|
return main_globals.multiplayer_map_name;
|
|
}
|
|
|
|
short main_get_difficulty(
|
|
void)
|
|
{
|
|
return global_difficulty_level;
|
|
}
|
|
|
|
boolean main_saving_map(
|
|
void)
|
|
{
|
|
return main_globals.saving_map;
|
|
}
|
|
|
|
void main_save_cancel(
|
|
void)
|
|
{
|
|
main_globals.saving_map = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_won_map(
|
|
void)
|
|
{
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.won_map = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_lost_map(
|
|
void)
|
|
{
|
|
if (!main_globals.lost_map)
|
|
{
|
|
main_globals.loss_timer = 0;
|
|
main_loss_last_tick = game_time_get();
|
|
}
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.lost_map = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_stop_time(
|
|
void)
|
|
{
|
|
main_globals.halt_time_scale = FALSE;
|
|
main_globals.restart_time = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_start_time(
|
|
void)
|
|
{
|
|
main_globals.restart_time = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_reset_map(
|
|
void)
|
|
{
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.reset_map = TRUE;
|
|
main_globals.lost_map = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_revert_map(
|
|
void)
|
|
{
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.revert_map = TRUE;
|
|
main_globals.lost_map = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_skip_cinematic(
|
|
void)
|
|
{
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.skip_cinematic = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_save_map_nonsafe(
|
|
void)
|
|
{
|
|
main_globals.saving_map = TRUE;
|
|
main_globals.save_map_safely = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_respawn(
|
|
boolean in_multiplayer)
|
|
{
|
|
if (!main_globals.respawn)
|
|
{
|
|
main_globals.respawn_timer = 0;
|
|
main_respawn_last_tick = game_time_get();
|
|
}
|
|
main_globals.respawn = TRUE;
|
|
if (in_multiplayer)
|
|
main_globals.respawn_timer = 92;
|
|
return;
|
|
}
|
|
|
|
void main_save_core(
|
|
void)
|
|
{
|
|
main_globals.save_core = TRUE;
|
|
csstrcpy(main_globals.core_name, "core.bin");
|
|
return;
|
|
}
|
|
|
|
void main_save_core_name(
|
|
char const *core_name)
|
|
{
|
|
match_vwarn("c:\\halo\\SOURCE\\main\\main.c", 0x3a5, csstrlen(core_name) < 64, "warning, core file name will be truncated to 63 characters");
|
|
csstrncpy(main_globals.core_name, core_name, 63);
|
|
main_globals.save_core = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_load_core(
|
|
void)
|
|
{
|
|
main_globals.load_core = TRUE;
|
|
csstrcpy(main_globals.core_name, "core.bin");
|
|
return;
|
|
}
|
|
|
|
void main_load_core_at_startup(
|
|
void)
|
|
{
|
|
main_globals.load_core_at_startup = TRUE;
|
|
csstrcpy(main_globals.core_name, "core.bin");
|
|
return;
|
|
}
|
|
|
|
void main_load_core_name(
|
|
char const *core_name)
|
|
{
|
|
match_vwarn("c:\\halo\\SOURCE\\main\\main.c", 0x3c9, csstrlen(core_name) < 64, "warning, core file name will be truncated to 63 characters");
|
|
csstrncpy(main_globals.core_name, core_name, 63);
|
|
main_globals.load_core = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_load_core_name_at_startup(
|
|
char const *core_name)
|
|
{
|
|
match_vwarn("c:\\halo\\SOURCE\\main\\main.c", 0x3d7, csstrlen(core_name) < 64, "warning, core file name will be truncated to 63 characters");
|
|
csstrncpy(main_globals.core_name, core_name, 63);
|
|
main_globals.load_core_at_startup = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_switch_structure_bsp(
|
|
short structure_bsp_index)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
if (structure_bsp_index < 0 || structure_bsp_index >= scenario->structure_bsp_references.count)
|
|
{
|
|
console_warning("tried to switch to invalid structure-bsp %d", structure_bsp_index);
|
|
return;
|
|
}
|
|
if (structure_bsp_index == global_structure_bsp_index)
|
|
{
|
|
console_warning("tried to switch to current structure-bsp %d", structure_bsp_index);
|
|
return;
|
|
}
|
|
main_globals.switch_to_structure_bsp_index = structure_bsp_index;
|
|
hud_load(TRUE);
|
|
return;
|
|
}
|
|
|
|
void main_goto_main_menu(
|
|
void)
|
|
{
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.want_to_be_at_main_menu = TRUE;
|
|
return;
|
|
}
|
|
|
|
static void main_queue_map_private(
|
|
void)
|
|
{
|
|
real progress;
|
|
if (cache_files_precache_in_progress() && cache_files_precache_map_status(&progress) == 1)
|
|
cache_files_precache_map_end();
|
|
if (!cache_files_precache_in_progress())
|
|
{
|
|
cache_files_precache_map_begin(main_globals.queued_map_name, FALSE);
|
|
main_globals.queue_map = FALSE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void main_menu_precache_resources(
|
|
void)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
if (scenario)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\main\\main.c", 0x46d, scenario->type==_scenario_type_main_menu);
|
|
predicted_resources_precache(&scenario->predicted_ui_resources);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void main_menu_unload(
|
|
void)
|
|
{
|
|
ui_stop_main_menu_music();
|
|
main_menu_active(FALSE);
|
|
main_globals.main_menu_scenario_loaded = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_menu_load(
|
|
void)
|
|
{
|
|
if (!main_globals.main_menu_scenario_loaded)
|
|
main_load_ui_scenario(FALSE);
|
|
main_screen_shell_load();
|
|
main_menu_precache_resources();
|
|
update_server_delete();
|
|
update_server_new();
|
|
update_server_start();
|
|
game_time_dispose_from_old_map();
|
|
game_time_initialize_for_new_map();
|
|
game_time_start();
|
|
hs_runtime_dispose_from_old_map();
|
|
hs_runtime_initialize_for_new_map();
|
|
main_globals.want_to_be_at_main_menu = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
static void screenshot_record(
|
|
struct bitmap_data *screen,
|
|
struct file_reference *file)
|
|
{
|
|
char const *error_message = tiff_export(file, screen);
|
|
|
|
if (error_message)
|
|
error(_error_silent, error_message);
|
|
return;
|
|
}
|
|
|
|
void set_window_camera_values(
|
|
struct render_window *window,
|
|
struct observer_result const *observer)
|
|
{
|
|
if (observer)
|
|
{
|
|
window->rasterizer_camera.position = observer->position;
|
|
window->rasterizer_camera.forward = observer->forward;
|
|
window->rasterizer_camera.up = observer->up;
|
|
/* port: display.fov's view, the local render view only (view_fov.c) */
|
|
window->rasterizer_camera.vertical_field_of_view =
|
|
render_fov_vertical(window->local_player_index, 2.0f * arctangent(
|
|
0.75f * render_camera_get_adjusted_field_of_view_tangent(
|
|
observer->field_of_view),
|
|
1.0f));
|
|
|
|
if (window->local_player_index != NONE &&
|
|
#ifdef HALO_ANDROID
|
|
/* VR: no camera shake, the head alone moves the view */
|
|
!halo_vr_active() &&
|
|
#endif
|
|
!console_is_active() &&
|
|
!game_time_get_paused() &&
|
|
director_get_perspective(window->local_player_index) !=
|
|
_director_perspective_neutral)
|
|
{
|
|
real_matrix4x3 effect_matrix;
|
|
real_matrix4x3 view_matrix;
|
|
|
|
player_effect_get_camera_effect_matrix(
|
|
window->local_player_index,
|
|
&effect_matrix);
|
|
matrix4x3_from_point_and_vectors(
|
|
&view_matrix,
|
|
&observer->position,
|
|
&observer->forward,
|
|
&observer->up);
|
|
matrix4x3_multiply(&view_matrix, &effect_matrix, &view_matrix);
|
|
matrix4x3_to_point_and_vectors(
|
|
&view_matrix,
|
|
&window->rasterizer_camera.position,
|
|
&window->rasterizer_camera.forward,
|
|
&window->rasterizer_camera.up);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
window->rasterizer_camera.position = *global_origin3d;
|
|
window->rasterizer_camera.forward = *global_forward3d;
|
|
window->rasterizer_camera.up = *global_up3d;
|
|
window->rasterizer_camera.vertical_field_of_view =
|
|
2.0f * arctangent(
|
|
0.75f * render_camera_get_adjusted_field_of_view_tangent(
|
|
DEGREES_TO_RADIANS(80.0f)),
|
|
1.0f);
|
|
}
|
|
|
|
window->rasterizer_camera.mirrored = FALSE;
|
|
window->rasterizer_camera.z_near = rasterizer_globals.near_clip_distance;
|
|
window->rasterizer_camera.z_far = rasterizer_globals.far_clip_distance;
|
|
if (!debug_render_freeze)
|
|
window->render_camera = window->rasterizer_camera;
|
|
|
|
return;
|
|
}
|
|
|
|
void main_present_frame(
|
|
void)
|
|
{
|
|
struct file_reference reference;
|
|
char path[512];
|
|
|
|
render_frame_present(NULL, main_globals.movie);
|
|
if (global_screenshot_count.count <= 0 && main_globals.movie)
|
|
{
|
|
_snprintf(
|
|
path,
|
|
NUMBEROF(path),
|
|
"movie\\frame%06d.tga",
|
|
main_globals.recording_frame_index++);
|
|
file_reference_create_from_path(&reference, path, FALSE);
|
|
screenshot_record(main_globals.movie, &reference);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void main_menu_ensure_player_queues_exist(
|
|
void)
|
|
{
|
|
update_server_delete();
|
|
update_server_new();
|
|
update_server_start();
|
|
return;
|
|
}
|
|
|
|
boolean main_menu_fade_active(
|
|
void)
|
|
{
|
|
return main_globals.map_change_load_timer != 0;
|
|
}
|
|
|
|
void main_menu_switch_to_single_player(
|
|
void)
|
|
{
|
|
main_globals.defer_map_change = TRUE;
|
|
return;
|
|
}
|
|
|
|
void main_set_game_connection_to_film_playback(
|
|
void)
|
|
{
|
|
main_globals.playback_last_recording = TRUE;
|
|
return;
|
|
}
|
|
|
|
short main_get_solo_level_from_name(
|
|
char const *name)
|
|
{
|
|
char lower_name[128] = { 0 };
|
|
short level;
|
|
|
|
/* port: a Custom Edition map (custom_maps\a30) is never one of the
|
|
campaign's levels, whatever its name holds
|
|
(port/linux/game/custom_edition_cache.c) */
|
|
if (custom_edition_level_name(name))
|
|
return NONE;
|
|
csstrncpy(lower_name, name, NUMBEROF(lower_name) - 1);
|
|
lower_name[NUMBEROF(lower_name) - 1] = 0;
|
|
strlwr(lower_name);
|
|
|
|
if (strstr(lower_name, "a10"))
|
|
level = 0;
|
|
else if (strstr(lower_name, "a30"))
|
|
level = 1;
|
|
else if (strstr(lower_name, "a50"))
|
|
level = 2;
|
|
else if (strstr(lower_name, "b30"))
|
|
level = 3;
|
|
else if (strstr(lower_name, "b40"))
|
|
level = 4;
|
|
else if (strstr(lower_name, "c10"))
|
|
level = 5;
|
|
else if (strstr(lower_name, "c20"))
|
|
level = 6;
|
|
else if (strstr(lower_name, "c40"))
|
|
level = 7;
|
|
else if (strstr(lower_name, "d20"))
|
|
level = 8;
|
|
else
|
|
level = strstr(lower_name, "d40") ? 9 : NONE;
|
|
|
|
return level;
|
|
}
|
|
|
|
short main_get_current_solo_level(
|
|
void)
|
|
{
|
|
return main_get_solo_level_from_name(main_globals.soloplayer_map_name);
|
|
}
|
|
|
|
char const *main_get_solo_level_name(
|
|
short level)
|
|
{
|
|
char const *result = NULL;
|
|
|
|
if (level >= 0 && level < NUMBEROF(scenario_paths))
|
|
result = scenario_paths[level];
|
|
|
|
return result;
|
|
}
|
|
|
|
void main_run_demos(
|
|
void)
|
|
{
|
|
main_globals.run_xdemos = TRUE;
|
|
return;
|
|
}
|
|
|
|
static void compute_subframe_counts(
|
|
long num_players,
|
|
long *horizontal_count,
|
|
long *vertical_count)
|
|
{
|
|
long horizontal = 1;
|
|
long vertical = 1;
|
|
boolean split_horizontally = TRUE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
1308,
|
|
num_players>0);
|
|
while (horizontal*vertical<num_players)
|
|
{
|
|
boolean add_column = split_horizontally ? horizontal<vertical : horizontal<=vertical;
|
|
|
|
if (add_column)
|
|
{
|
|
horizontal++;
|
|
}
|
|
else
|
|
{
|
|
horizontal = 1;
|
|
vertical++;
|
|
}
|
|
}
|
|
|
|
*horizontal_count = horizontal;
|
|
*vertical_count = vertical;
|
|
return;
|
|
}
|
|
|
|
void compute_window_bounds(
|
|
long player_index,
|
|
long num_players,
|
|
rectangle2d *pixel_bounds,
|
|
rectangle2d *safe_frame_bounds)
|
|
{
|
|
long vertical_count;
|
|
long horizontal_count;
|
|
long horizontal_index;
|
|
long vertical_index;
|
|
long subframe_width;
|
|
long subframe_height;
|
|
short safe_frame_inset;
|
|
boolean first_player_spans_two_columns;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
1359,
|
|
player_index<num_players);
|
|
safe_frame_inset = num_players>1 ? 4 : 0;
|
|
first_player_spans_two_columns = FALSE;
|
|
compute_subframe_counts(
|
|
num_players,
|
|
&horizontal_count,
|
|
&vertical_count);
|
|
|
|
if (horizontal_count*vertical_count>num_players)
|
|
{
|
|
if (!player_index)
|
|
first_player_spans_two_columns = TRUE;
|
|
else
|
|
player_index++;
|
|
}
|
|
|
|
vertical_index = player_index/horizontal_count;
|
|
horizontal_index = player_index-vertical_index*horizontal_count;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
1390,
|
|
vertical_index>=0 && vertical_index<vertical_count);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\main\\main.c",
|
|
1391,
|
|
horizontal_index>=0 && horizontal_index<horizontal_count);
|
|
|
|
subframe_height =
|
|
(rasterizer_globals.reserved04.frame_bounds.y1-
|
|
rasterizer_globals.reserved04.frame_bounds.y0)/vertical_count;
|
|
subframe_width =
|
|
(rasterizer_globals.reserved04.frame_bounds.x1-
|
|
rasterizer_globals.reserved04.frame_bounds.x0)/horizontal_count;
|
|
subframe_width *= first_player_spans_two_columns ? 2 : 1;
|
|
|
|
safe_frame_bounds->x0 =
|
|
subframe_width*horizontal_index+
|
|
rasterizer_globals.reserved04.frame_bounds.x0;
|
|
safe_frame_bounds->x1 =
|
|
subframe_width*(horizontal_index+1)+
|
|
rasterizer_globals.reserved04.frame_bounds.x0;
|
|
safe_frame_bounds->y0 =
|
|
subframe_height*vertical_index+
|
|
rasterizer_globals.reserved04.frame_bounds.y0;
|
|
safe_frame_bounds->y1 =
|
|
subframe_height*(vertical_index+1)+
|
|
rasterizer_globals.reserved04.frame_bounds.y0;
|
|
*pixel_bounds = *safe_frame_bounds;
|
|
|
|
safe_frame_bounds->x0 += horizontal_index*safe_frame_inset;
|
|
safe_frame_bounds->x1 -= (horizontal_index==0)*safe_frame_inset;
|
|
safe_frame_bounds->y0 += vertical_index*safe_frame_inset;
|
|
safe_frame_bounds->y1 -= (vertical_index==0)*safe_frame_inset;
|
|
|
|
if (!horizontal_index)
|
|
pixel_bounds->x0 = rasterizer_globals.reserved04.screen_bounds.x0;
|
|
if ((first_player_spans_two_columns!=FALSE)+horizontal_index+1==horizontal_count)
|
|
pixel_bounds->x1 = rasterizer_globals.reserved04.screen_bounds.x1;
|
|
if (!vertical_index)
|
|
pixel_bounds->y0 = rasterizer_globals.reserved04.screen_bounds.y0;
|
|
if (vertical_index+1==vertical_count)
|
|
pixel_bounds->y1 = rasterizer_globals.reserved04.screen_bounds.y1;
|
|
|
|
return;
|
|
}
|
|
|
|
short main_get_window_count(
|
|
void)
|
|
{
|
|
boolean single_window = game_engine_force_single_screen() || cinematic_in_progress();
|
|
|
|
return single_window ? 1 : PIN(local_player_count(), 1, MAXIMUM_WINDOWS);
|
|
}
|
|
|
|
#ifdef HALO_PROFILE
|
|
/* port: whether the main menu's map is the one loaded (profile_console.c: a
|
|
recording of debug.profile_record_when = "game" waits for another) */
|
|
boolean main_menu_is_loaded(
|
|
void)
|
|
{
|
|
return main_globals.main_menu_scenario_loaded;
|
|
}
|
|
#endif
|
|
|
|
static void main_new_map(
|
|
struct game_options *options)
|
|
{
|
|
input_flush();
|
|
if (game_load(options))
|
|
{
|
|
game_initialize_for_new_map();
|
|
}
|
|
else
|
|
{
|
|
error(_error_immediate, "game_load() failed.");
|
|
}
|
|
|
|
if (!errors_handle())
|
|
{
|
|
create_local_players();
|
|
game_time_start();
|
|
}
|
|
else
|
|
{
|
|
error(_error_immediate, "main_new_map() failed.");
|
|
}
|
|
game_initial_pulse();
|
|
|
|
main_globals.reset_map = FALSE;
|
|
main_globals.defer_map_change = FALSE;
|
|
main_globals.revert_map = FALSE;
|
|
main_globals.skip_cinematic = FALSE;
|
|
main_globals.saving_map = FALSE;
|
|
main_globals.won_map = FALSE;
|
|
main_globals.lost_map = FALSE;
|
|
main_globals.respawn = FALSE;
|
|
main_globals.save_core = FALSE;
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
main_globals.load_core = main_globals.load_core_at_startup;
|
|
main_globals.load_core_at_startup = FALSE;
|
|
|
|
if (main_globals.allow_persistent_storage)
|
|
game_state_try_and_load_from_persistent_storage();
|
|
ui_widgets_disable_pause_game(30);
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_change_map_name(
|
|
void)
|
|
{
|
|
if (main_globals.main_menu_scenario_loaded == TRUE)
|
|
{
|
|
if (!main_globals.map_change_load_timer)
|
|
{
|
|
if (ui_main_menu_music_active() == TRUE)
|
|
{
|
|
main_globals.map_change_load_timer =
|
|
main_globals.frame_start_milliseconds + 1000;
|
|
main_screen_shell_begin_fade(1000);
|
|
ui_widgets_inhibit_processing(TRUE);
|
|
ui_widgets_set_fade_value(0.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned long remaining_milliseconds =
|
|
main_globals.map_change_load_timer - main_globals.frame_start_milliseconds;
|
|
|
|
ui_widgets_set_fade_value(
|
|
1.0f - (real)remaining_milliseconds * 0.001f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
main_globals.map_change_load_timer = 0;
|
|
}
|
|
|
|
if (main_globals.frame_start_milliseconds >= main_globals.map_change_load_timer)
|
|
{
|
|
ui_widgets_set_fade_value(-1.0f);
|
|
ui_stop_main_menu_music();
|
|
main_menu_active(FALSE);
|
|
main_globals.main_menu_scenario_loaded = FALSE;
|
|
ui_widgets_inhibit_processing(FALSE);
|
|
|
|
if (game_in_progress() && main_globals.connection == _game_connection_local)
|
|
{
|
|
struct game_options options;
|
|
short local_player_index;
|
|
|
|
game_options_new(&options);
|
|
csstrncpy(
|
|
options.map_name,
|
|
main_globals.soloplayer_map_name,
|
|
NUMBEROF(options.map_name) - 1);
|
|
options.map_name[NUMBEROF(options.map_name) - 1] = 0;
|
|
options.difficulty = global_difficulty_level;
|
|
game_dispose_from_old_map();
|
|
game_precache_new_map(options.map_name, TRUE);
|
|
game_unload();
|
|
main_new_map(&options);
|
|
|
|
for (local_player_index = 0;
|
|
local_player_index < player_spawn_count;
|
|
local_player_index++)
|
|
{
|
|
player_profile_save_last_level_played(local_player_index);
|
|
}
|
|
}
|
|
|
|
main_globals.map_change_load_timer = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void main_set_multiplayer_map_name(
|
|
char const *map_name)
|
|
{
|
|
csstrncpy(main_globals.multiplayer_map_name, map_name, NUMBEROF(main_globals.multiplayer_map_name) - 1);
|
|
main_globals.multiplayer_map_name[NUMBEROF(main_globals.multiplayer_map_name) - 1] = 0;
|
|
cache_files_give_time_to_precache(main_globals.multiplayer_map_name);
|
|
return;
|
|
}
|
|
|
|
void main_set_difficulty(
|
|
short difficulty)
|
|
{
|
|
if (difficulty >= _game_difficulty_level_easy && difficulty < NUMBER_OF_GAME_DIFFICULTY_LEVELS)
|
|
global_difficulty_level = difficulty;
|
|
return;
|
|
}
|
|
|
|
static long sort_desired_local_player_controllers(
|
|
short const *a,
|
|
short const *b)
|
|
{
|
|
short value_a = *a;
|
|
short value_b = *b;
|
|
if (value_a == NONE && value_b != value_a)
|
|
return 1;
|
|
if (value_a != NONE && value_b == NONE)
|
|
return -1;
|
|
if (value_a > value_b)
|
|
return 1;
|
|
return (value_a >= value_b) - 1;
|
|
}
|
|
|
|
void main_save_map_safe(
|
|
void)
|
|
{
|
|
if (!main_globals.saving_map || main_globals.save_map_timeout)
|
|
{
|
|
main_globals.saving_map = TRUE;
|
|
main_globals.save_map_safely = TRUE;
|
|
main_globals.save_map_timeout = TRUE;
|
|
main_globals.ticks_until_next_save_check = 0;
|
|
main_globals.ticks_unable_to_save = 0;
|
|
main_globals.safe_intervals = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void main_save_map_no_timeout(
|
|
void)
|
|
{
|
|
if (!main_globals.saving_map || main_globals.save_map_timeout)
|
|
{
|
|
main_globals.saving_map = TRUE;
|
|
main_globals.save_map_safely = TRUE;
|
|
main_globals.ticks_until_next_save_check = 0;
|
|
main_globals.ticks_unable_to_save = 0;
|
|
main_globals.safe_intervals = 0;
|
|
}
|
|
main_globals.save_map_timeout = FALSE;
|
|
return;
|
|
}
|
|
|
|
void main_skip(
|
|
short ticks)
|
|
{
|
|
if (ticks <= 15)
|
|
{
|
|
main_globals.skip_ticks = ticks;
|
|
main_globals.skip = TRUE;
|
|
}
|
|
else
|
|
{
|
|
error(_error_silent, "cannot skip more than 15 frames (half a second)");
|
|
}
|
|
return;
|
|
}
|
|
|
|
void main_queue_map_name(
|
|
char const *map_name)
|
|
{
|
|
if (map_name)
|
|
{
|
|
csstrncpy(main_globals.queued_map_name, map_name, NUMBEROF(main_globals.queued_map_name) - 1);
|
|
main_globals.queue_map = TRUE;
|
|
}
|
|
else
|
|
{
|
|
main_globals.queued_map_name[0] = 0;
|
|
main_globals.queue_map = FALSE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
boolean main_taking_screenshot(
|
|
void)
|
|
{
|
|
return global_screenshot_count.count > 0 || main_globals.movie != NULL;
|
|
}
|
|
|
|
void main_movie_start(
|
|
real frames_per_second)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\main\\main.c", 2715, main_globals.movie==NULL);
|
|
|
|
main_globals.movie = bitmap_2d_new(640, 480, 0, 10);
|
|
if (main_globals.movie)
|
|
{
|
|
directory_create_or_delete_contents("movie");
|
|
main_globals.recording_frame_index = 0;
|
|
if (frames_per_second > 0.0001f)
|
|
main_globals.recording_dt = 1.0f / frames_per_second;
|
|
else
|
|
main_globals.recording_dt = 1.0f / 30.0f;
|
|
game_time_set_speed(1.0f);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void main_movie_stop(
|
|
void)
|
|
{
|
|
if (main_globals.movie)
|
|
{
|
|
bitmap_delete(main_globals.movie);
|
|
main_globals.movie = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void main_crash(
|
|
char const *str)
|
|
{
|
|
/* BUG (original, deliberate): the "crash" script command ("crashes (for debugging).")
|
|
* faults on purpose by storing this literal through the null pointer; the August and
|
|
* September 2001 builds (debug and retail) and January all emit this one store. */
|
|
*(char **)NULL = "chucky was here! NULL belongs to me!!!!!";
|
|
return;
|
|
}
|
|
|
|
#ifdef HALO_NATIVE_BUILD_INFO
|
|
/* The original Xbox version string describes the map format, not this port.
|
|
Keep it on Xbox; native builds name the binary that is actually running. */
|
|
static void main_native_build_label(char *label, size_t capacity)
|
|
{
|
|
char const *platform;
|
|
|
|
#ifdef HALO_ANDROID
|
|
platform = "Android";
|
|
#elif defined(HALO_WINDOWS)
|
|
platform = "Windows";
|
|
#else
|
|
platform = "Linux";
|
|
#endif
|
|
if (HALO_BUILD_NUMBER > 0)
|
|
_snprintf(label, capacity - 1, "OpenCE %s | build %d (%s)",
|
|
platform, HALO_BUILD_NUMBER, HALO_BUILD_FLAVOR);
|
|
else
|
|
_snprintf(label, capacity - 1, "OpenCE %s | local build (%s)",
|
|
platform, HALO_BUILD_FLAVOR);
|
|
label[capacity - 1] = 0;
|
|
}
|
|
|
|
/* The 2 KB error buffer keeps recent lines at its end. Put the newest first
|
|
so the failure is visible even if older messages run off the screen. */
|
|
static char const *main_native_error_tail(char const *messages)
|
|
{
|
|
enum { MAX_LINES = 8, MAX_LINE_BYTES = 110 };
|
|
static char recent[MAX_LINES * (MAX_LINE_BYTES + 5) + 1];
|
|
char const *lines[MAX_LINES];
|
|
size_t lengths[MAX_LINES];
|
|
char const *cursor;
|
|
char const *start;
|
|
size_t length;
|
|
size_t copied;
|
|
size_t used = 0;
|
|
unsigned int count = 0;
|
|
unsigned int index;
|
|
|
|
for (cursor = messages; *cursor; )
|
|
{
|
|
start = cursor;
|
|
while (*cursor && *cursor != '\r' && *cursor != '\n')
|
|
cursor++;
|
|
length = cursor - start;
|
|
while (*cursor == '\r' || *cursor == '\n')
|
|
cursor++;
|
|
if (!length || (length >= sizeof("[...too many errors to print...]") - 1 &&
|
|
!strncmp(start, "[...too many errors to print...]",
|
|
sizeof("[...too many errors to print...]") - 1)))
|
|
continue;
|
|
if (count == MAX_LINES)
|
|
{
|
|
for (index = 1; index < MAX_LINES; index++)
|
|
{
|
|
lines[index - 1] = lines[index];
|
|
lengths[index - 1] = lengths[index];
|
|
}
|
|
count--;
|
|
}
|
|
lines[count] = start;
|
|
lengths[count++] = length;
|
|
}
|
|
if (!count)
|
|
return "No recent messages. See debug.txt for details.\r\n";
|
|
for (index = count; index > 0; index--)
|
|
{
|
|
length = lengths[index - 1];
|
|
copied = length < MAX_LINE_BYTES ? length : MAX_LINE_BYTES;
|
|
memcpy(recent + used, lines[index - 1], copied);
|
|
used += copied;
|
|
if (copied < length)
|
|
{
|
|
memcpy(recent + used, "...", 3);
|
|
used += 3;
|
|
}
|
|
recent[used++] = '\r';
|
|
recent[used++] = '\n';
|
|
}
|
|
recent[used] = 0;
|
|
return recent;
|
|
}
|
|
#endif
|
|
|
|
void main_print_version(
|
|
void)
|
|
{
|
|
#ifdef HALO_NATIVE_BUILD_INFO
|
|
char label[96];
|
|
|
|
main_native_build_label(label, sizeof(label));
|
|
console_printf(FALSE, "%s | compiled %s %s", label, __DATE__, __TIME__);
|
|
#else
|
|
console_printf(FALSE, "halobeta xbox 01.01.14.2342 Jan 14 2002 12:49:20");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void main_vertical_blank_interrupt_handler(
|
|
unsigned long context)
|
|
{
|
|
rasterizer_globals.frame_and_vertical_blank_index++;
|
|
if (!main_globals.d3d_flip_count)
|
|
{
|
|
rasterizer_globals.previous_frame_and_vertical_blank_index =
|
|
rasterizer_globals.frame_and_vertical_blank_index;
|
|
}
|
|
else if (*main_globals.d3d_flip_count != rasterizer_globals.d3d_flip_count)
|
|
{
|
|
main_globals.vblank_flip_deltas[main_globals.vblank_flip_delta_index] =
|
|
(short)((short)rasterizer_globals.frame_and_vertical_blank_index -
|
|
(short)rasterizer_globals.previous_frame_and_vertical_blank_index);
|
|
main_globals.vblank_flip_delta_index =
|
|
(short)((main_globals.vblank_flip_delta_index + 1) % 15);
|
|
rasterizer_globals.previous_frame_and_vertical_blank_index =
|
|
rasterizer_globals.frame_and_vertical_blank_index;
|
|
rasterizer_globals.d3d_flip_count = *main_globals.d3d_flip_count;
|
|
}
|
|
|
|
input_vertical_blank_interrupt();
|
|
return;
|
|
}
|
|
|
|
void main_save_current_solo_map(
|
|
char const *map_name)
|
|
{
|
|
FILE *file;
|
|
|
|
if ((short)main_get_solo_level_from_name(map_name) != NONE)
|
|
{
|
|
file = fopen("z:\\last_solo.txt", "w");
|
|
if (file)
|
|
{
|
|
fwrite(map_name, 1, csstrlen(map_name) + 1, file);
|
|
fclose(file);
|
|
}
|
|
else
|
|
{
|
|
error(_error_silent, "Couldn't create a file to write the current solo map to");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void main_load_last_solo_map(
|
|
void)
|
|
{
|
|
char map_name[256];
|
|
boolean valid_map_name = FALSE;
|
|
|
|
if (main_globals.load_last_solo_level && !bink_playback_active())
|
|
{
|
|
FILE *file;
|
|
|
|
file = fopen("z:\\last_solo.txt", "r");
|
|
|
|
if (file)
|
|
{
|
|
long character_count;
|
|
|
|
character_count = fread(map_name, 1, 255, file);
|
|
fclose(file);
|
|
map_name[MIN(character_count, 255)] = 0;
|
|
valid_map_name = main_get_solo_level_from_name(map_name) != NONE;
|
|
}
|
|
|
|
if (valid_map_name)
|
|
main_set_map_name(map_name);
|
|
else
|
|
main_set_map_name(scenario_paths[0]);
|
|
main_globals.defer_map_change = FALSE;
|
|
main_globals.load_last_solo_level = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void main_load_ui_scenario(
|
|
boolean precache_resources)
|
|
{
|
|
struct game_options options;
|
|
|
|
game_precache_new_map("levels\\ui\\ui", TRUE);
|
|
match_assert("c:\\halo\\SOURCE\\main\\main.c", 1092,
|
|
!main_globals.main_menu_scenario_loaded);
|
|
game_options_new(&options);
|
|
csstrncpy(options.map_name, "levels\\ui\\ui", NUMBEROF(options.map_name) - 1);
|
|
options.map_name[NUMBEROF(options.map_name) - 1] = 0;
|
|
game_precache_new_map(options.map_name, TRUE);
|
|
game_dispose_from_old_map();
|
|
game_unload();
|
|
game_engine_dispose();
|
|
game_set_game_variant(NULL);
|
|
main_globals.main_menu_scenario_loaded = TRUE;
|
|
main_new_map(&options);
|
|
director_script_camera(TRUE);
|
|
scripted_camera_set(0, 0, NONE);
|
|
main_menu_active(TRUE);
|
|
main_globals.load_last_solo_level = TRUE;
|
|
if (precache_resources)
|
|
main_menu_precache_resources();
|
|
|
|
return;
|
|
}
|
|
|
|
void main_roll_credits(
|
|
void)
|
|
{
|
|
error(_error_silent, "congratulations, you won the game!");
|
|
main_menu_load();
|
|
game_end_credits_start();
|
|
return;
|
|
}
|
|
|
|
void main_pregame_render(
|
|
void)
|
|
{
|
|
/* Name, type and function scope from the 2003 PC demo PDB and the HCEX PDB (static local
|
|
struct render_window window of main_pregame_render). Neither PDB records the block: placing it
|
|
at the top of the function is unattested. January corroborates: .bss +0, the 0xAC-byte window
|
|
padded to 0xB0 before main_globals, referenced only by this function. */
|
|
static struct render_window window;
|
|
|
|
collision_log_continue_period(TRUE);
|
|
sound_render();
|
|
{
|
|
real_point3d position = { 0.0f, 0.0f, 0.0f };
|
|
real_vector3d forward = { 0.0f, 0.0f, 1.0f };
|
|
real_vector3d up = { 0.0f, 1.0f, 0.0f };
|
|
|
|
window.local_player_index = NONE;
|
|
window.console_window = TRUE;
|
|
window.rasterizer_camera.position = position;
|
|
window.rasterizer_camera.forward = forward;
|
|
window.rasterizer_camera.up = up;
|
|
window.rasterizer_camera.mirrored = FALSE;
|
|
window.rasterizer_camera.vertical_field_of_view =
|
|
2.0f * arctangent(
|
|
0.75f * render_camera_get_adjusted_field_of_view_tangent(
|
|
DEGREES_TO_RADIANS(80.0f)),
|
|
1.0f);
|
|
compute_window_bounds(
|
|
0,
|
|
1,
|
|
&window.rasterizer_camera.viewport_bounds,
|
|
&window.rasterizer_camera.window_bounds);
|
|
window.rasterizer_camera.z_near = 0.01f;
|
|
window.rasterizer_camera.z_far = 1.0f;
|
|
window.render_camera = window.rasterizer_camera;
|
|
render_frame_pregame(
|
|
&window,
|
|
main_globals.movie);
|
|
}
|
|
collision_log_end_period();
|
|
|
|
return;
|
|
}
|
|
|
|
/* port: a network co-op host reverts to its last saved state with its
|
|
clock kept going forward, and the clients follow through the co-op syncs
|
|
(network_coop.c). FALSE without a saved state, which would reset the map
|
|
on the host alone. */
|
|
static boolean main_coop_host_revert(
|
|
void)
|
|
{
|
|
long now = game_time_get();
|
|
|
|
if (!game_state_port_saved_game_valid())
|
|
return FALSE;
|
|
game_state_revert();
|
|
network_coop_reverted(now);
|
|
ui_widgets_disable_pause_game(30);
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean main_coop_host(
|
|
void)
|
|
{
|
|
return game_connection() == _game_connection_network_server && network_coop_active();
|
|
}
|
|
|
|
static void main_revert_map_private(
|
|
void)
|
|
{
|
|
/* (a network client never reverts on its own: its game is the host's) */
|
|
if (main_coop_host())
|
|
main_coop_host_revert();
|
|
else if (game_connection() != _game_connection_network_client)
|
|
{
|
|
game_state_revert();
|
|
ui_widgets_disable_pause_game(30);
|
|
}
|
|
main_globals.revert_map = FALSE;
|
|
return;
|
|
}
|
|
|
|
static void main_skip_cinematic_private(
|
|
void)
|
|
{
|
|
/* port: only a local game or a network co-op host reverts to skip; a
|
|
network game's other machines would be left out of step */
|
|
boolean skippable = cinematic_can_be_skipped();
|
|
boolean skipped = FALSE;
|
|
|
|
if (skippable && main_coop_host())
|
|
{
|
|
skipped = main_coop_host_revert();
|
|
if (skipped)
|
|
network_coop_skip_done();
|
|
}
|
|
else if (skippable && game_connection() == _game_connection_local)
|
|
{
|
|
game_state_revert();
|
|
ui_widgets_disable_pause_game(30);
|
|
skipped = TRUE;
|
|
}
|
|
if (skipped)
|
|
main_globals.revert_map = FALSE;
|
|
else if (main_coop_host())
|
|
{
|
|
error(_error_silent, "co-op: cutscene not skipped (skippable %d, saved state %d)",
|
|
skippable, game_state_port_saved_game_valid());
|
|
}
|
|
main_globals.skip_cinematic = FALSE;
|
|
return;
|
|
}
|
|
|
|
static void main_skip_private(
|
|
void)
|
|
{
|
|
if (main_globals.skip_ticks && cinematic_in_progress())
|
|
{
|
|
real saved_speed = game_time_get_speed();
|
|
|
|
game_time_set_speed(1.0f);
|
|
while (main_globals.skip_ticks-- > 0)
|
|
game_time_update(1.0f / TICKS_PER_SECOND);
|
|
game_time_set_speed(saved_speed);
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"manual skipping doesn't work outside of cinemtatic start/stop...");
|
|
}
|
|
|
|
main_globals.skip_ticks = 0;
|
|
main_globals.skip = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_saving_map_private(
|
|
void)
|
|
{
|
|
game_state_save();
|
|
hud_autosave(FALSE);
|
|
main_globals.save_map_completed = FALSE;
|
|
return;
|
|
}
|
|
|
|
static void main_save_map_private(
|
|
void)
|
|
{
|
|
if (!game_time_get_paused())
|
|
{
|
|
boolean save_map_safely = main_globals.save_map_safely;
|
|
boolean save_map = FALSE;
|
|
|
|
if (save_map_safely)
|
|
{
|
|
if (main_globals.ticks_unable_to_save++ >= 240 &&
|
|
main_globals.save_map_timeout)
|
|
{
|
|
if (debug_game_save)
|
|
console_printf(FALSE, "gave up trying to save");
|
|
main_globals.saving_map = FALSE;
|
|
return;
|
|
}
|
|
else if (main_globals.ticks_until_next_save_check-- <= 0)
|
|
{
|
|
if (game_safe_to_save())
|
|
{
|
|
if (main_globals.safe_intervals++ >= 3)
|
|
save_map = TRUE;
|
|
}
|
|
else
|
|
{
|
|
main_globals.safe_intervals = 0;
|
|
}
|
|
main_globals.ticks_until_next_save_check = 10;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (debug_game_save)
|
|
console_printf(FALSE, "unsafe save");
|
|
save_map = TRUE;
|
|
}
|
|
|
|
if (save_map)
|
|
{
|
|
/* port: remember where the players are, for network co-op respawns */
|
|
players_note_checkpoint();
|
|
hud_autosave(TRUE);
|
|
main_globals.save_map_completed = TRUE;
|
|
main_globals.saving_map = FALSE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_switch_to_structure_bsp_private(
|
|
void)
|
|
{
|
|
scenario_switch_structure_bsp(main_globals.switch_to_structure_bsp_index);
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
hud_load(0);
|
|
return;
|
|
}
|
|
|
|
/* The original post-increment test expires on its 92nd 30 Hz update.
|
|
Render-only frames must not advance it. Saturate so blocked co-op
|
|
respawns can retry indefinitely without overflowing the short counter. */
|
|
static boolean main_death_timer_expired(
|
|
short *timer,
|
|
long *last_tick,
|
|
boolean advance)
|
|
{
|
|
long current_tick = game_time_get();
|
|
unsigned long elapsed_ticks = 0;
|
|
|
|
if (current_tick < *last_tick)
|
|
{
|
|
/* A checkpoint/core load can move the simulation clock backwards. */
|
|
*timer = 0;
|
|
}
|
|
else
|
|
{
|
|
elapsed_ticks = (unsigned long)current_tick - (unsigned long)*last_tick;
|
|
}
|
|
*last_tick = current_tick;
|
|
|
|
if (!advance)
|
|
return FALSE;
|
|
|
|
*timer = (short)MIN(92, (unsigned long)*timer + elapsed_ticks);
|
|
return *timer >= 92;
|
|
}
|
|
|
|
static void main_lost_map_private(
|
|
void)
|
|
{
|
|
if (main_death_timer_expired(
|
|
&main_globals.loss_timer, &main_loss_last_tick,
|
|
!game_time_get_paused()))
|
|
{
|
|
main_globals.lost_map = FALSE;
|
|
main_globals.loss_timer = 0;
|
|
/* port: in network co-op, everyone dying respawns the players where
|
|
they were at the last checkpoint, without a revert. A mission the
|
|
scripts failed (game_lost, with players still alive: d40's timer,
|
|
a50's Keyes) does revert, or its failure cutscene would never end. */
|
|
if (game_connection() != _game_connection_network_server)
|
|
game_state_revert();
|
|
else if (players_are_all_dead() || !main_coop_host_revert())
|
|
players_respawn_at_checkpoint();
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void main_respawn_private(
|
|
void)
|
|
{
|
|
if (main_death_timer_expired(
|
|
&main_globals.respawn_timer, &main_respawn_last_tick,
|
|
!game_time_get_paused() && !cinematic_in_progress()) &&
|
|
players_respawn_coop())
|
|
{
|
|
main_globals.respawn = FALSE;
|
|
main_globals.respawn_timer = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void main_load_core_private(
|
|
void)
|
|
{
|
|
game_state_load_core(main_globals.core_name);
|
|
main_globals.load_core = FALSE;
|
|
return;
|
|
}
|
|
|
|
static void main_save_core_private(
|
|
void)
|
|
{
|
|
game_state_save_core(main_globals.core_name);
|
|
main_globals.save_core = FALSE;
|
|
return;
|
|
}
|
|
|
|
static void main_run_demos_private(
|
|
void)
|
|
{
|
|
main_globals.run_xdemos = FALSE;
|
|
xbox_demos_launch();
|
|
return;
|
|
}
|
|
|
|
static void main_frame_rate_debug(
|
|
void)
|
|
{
|
|
char sample_index;
|
|
|
|
if (global_screenshot_count.framerate_reset)
|
|
{
|
|
if (debug_frame_rate)
|
|
{
|
|
sample_index = global_screenshot_count.framerate_sample_index;
|
|
}
|
|
else
|
|
{
|
|
global_screenshot_count.framerate_reset = FALSE;
|
|
csmemset(
|
|
global_screenshot_count.framerate_samples,
|
|
0,
|
|
sizeof(global_screenshot_count.framerate_samples));
|
|
sample_index = 0;
|
|
global_screenshot_count.framerate_flags = 0;
|
|
global_screenshot_count.framerate_sample_index = sample_index;
|
|
global_screenshot_count.framerate_active = FALSE;
|
|
global_screenshot_count.framerate_counter = 0;
|
|
global_screenshot_count.framerate_reset = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sample_index = global_screenshot_count.framerate_sample_index;
|
|
}
|
|
|
|
if (debug_frame_rate)
|
|
{
|
|
global_screenshot_count.framerate_samples[sample_index] =
|
|
main_globals.seconds_elapsed;
|
|
SET_FLAG(
|
|
global_screenshot_count.framerate_flags,
|
|
sample_index,
|
|
main_globals.seconds_elapsed > 1.08/TICKS_PER_SECOND);
|
|
|
|
sample_index++;
|
|
sample_index = (char)(sample_index % (long)NUMBEROF(global_screenshot_count.framerate_samples));
|
|
global_screenshot_count.framerate_sample_index = sample_index;
|
|
global_screenshot_count.framerate_reset = TRUE;
|
|
|
|
if (global_screenshot_count.framerate_active)
|
|
{
|
|
if (global_screenshot_count.framerate_sample_index == 0)
|
|
{
|
|
if (global_screenshot_count.framerate_flags == 0)
|
|
{
|
|
if (++global_screenshot_count.framerate_counter >= 60)
|
|
{
|
|
global_screenshot_count.framerate_counter = 0;
|
|
global_screenshot_count.framerate_active = FALSE;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
global_screenshot_count.framerate_counter = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (global_screenshot_count.framerate_flags == 0xFF)
|
|
{
|
|
SYSTEMTIME system_time;
|
|
char core_name[256];
|
|
char init_file_name[260];
|
|
char const *scenario_name =
|
|
tag_name_strip_path(tag_get_name(global_scenario_index));
|
|
FILE *file;
|
|
|
|
GetSystemTime(&system_time);
|
|
/* port: snprintf, here and below (a map's tag's name may be any
|
|
length) */
|
|
snprintf(
|
|
core_name,
|
|
sizeof(core_name),
|
|
"%s_slow_%d_%d_%d_%d_%d_%d.bin",
|
|
scenario_name,
|
|
system_time.wMonth,
|
|
system_time.wDay,
|
|
system_time.wYear,
|
|
system_time.wHour,
|
|
system_time.wMinute,
|
|
system_time.wSecond);
|
|
game_state_save_core(core_name);
|
|
snprintf(init_file_name, sizeof(init_file_name), "d:\\%s_init.txt", scenario_name);
|
|
|
|
file = fopen(init_file_name, "r");
|
|
if (!file)
|
|
{
|
|
file = fopen(init_file_name, "wt");
|
|
fprintf(file, "map_name %s\n", scenario_name);
|
|
}
|
|
else
|
|
{
|
|
fclose(file);
|
|
file = fopen(init_file_name, "a+t");
|
|
}
|
|
|
|
fprintf(file, ";core_load_name_at_startup %s\n", core_name);
|
|
fflush(file);
|
|
fclose(file);
|
|
global_screenshot_count.framerate_active = TRUE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_won_map_private(
|
|
void)
|
|
{
|
|
short level;
|
|
short local_player_index;
|
|
|
|
/* port: when a network co-op level is won, the round ends for everyone as
|
|
in multiplayer, back to the lobby, and the next round is the campaign's
|
|
next level (The Maw's: The Pillar of Autumn). A level not in the campaign
|
|
repeats. */
|
|
if (game_connection() == _game_connection_network_server && network_coop_active())
|
|
{
|
|
struct network_game *game = network_game_get_game();
|
|
|
|
main_globals.won_map = FALSE;
|
|
level = game ? main_get_solo_level_from_name(game->map.name) : NONE;
|
|
player_profile_save_level_completed(0);
|
|
network_game_server_port_cooperative_won(level != NONE ?
|
|
main_get_solo_level_name((level + 1) % NUMBER_OF_SINGLE_PLAYER_LEVELS) : NULL);
|
|
return;
|
|
}
|
|
main_globals.want_to_be_at_main_menu = TRUE;
|
|
main_globals.won_map = FALSE;
|
|
level = main_get_solo_level_from_name(main_globals.soloplayer_map_name);
|
|
/* port: a level not in the campaign (a Custom Edition map's) has no next
|
|
one, rather than the first */
|
|
level = level == NONE ? NONE : level + 1;
|
|
if (level >= 10)
|
|
level = NONE;
|
|
for (local_player_index = 0; local_player_index < player_spawn_count; local_player_index++)
|
|
player_profile_save_level_completed(local_player_index);
|
|
ui_set_next_level(level);
|
|
return;
|
|
}
|
|
|
|
void main_set_map_name(
|
|
char const *map_name)
|
|
{
|
|
main_globals.want_to_be_at_main_menu = FALSE;
|
|
csstrncpy(main_globals.soloplayer_map_name, map_name, NUMBEROF(main_globals.soloplayer_map_name) - 1);
|
|
main_globals.soloplayer_map_name[NUMBEROF(main_globals.soloplayer_map_name) - 1] = 0;
|
|
main_globals.allow_persistent_storage = TRUE;
|
|
if ((game_in_editor() || game_in_progress()) && main_globals.connection == _game_connection_local)
|
|
main_globals.defer_map_change = TRUE;
|
|
return;
|
|
}
|
|
|
|
static void main_exit(
|
|
void)
|
|
{
|
|
#ifdef HALO_PROFILE
|
|
/* port: the profiling build's recording written out before the game goes */
|
|
profile_trace_shutdown();
|
|
#endif
|
|
switch (main_globals.connection)
|
|
{
|
|
case _game_connection_network_server:
|
|
dispose_global_network_game_client();
|
|
dispose_global_network_game_server();
|
|
break;
|
|
case _game_connection_network_client:
|
|
dispose_global_network_game_client();
|
|
break;
|
|
}
|
|
|
|
game_dispose_from_old_map();
|
|
game_dispose();
|
|
debug_keys_dispose();
|
|
console_dispose();
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_reset_time(
|
|
void)
|
|
{
|
|
main_globals.frame_start_milliseconds = system_milliseconds();
|
|
main_globals.rasterizer_target_index = rasterizer_globals.vertical_blank_index;
|
|
return;
|
|
}
|
|
|
|
static boolean main_framerate_throttle_enabled(
|
|
void)
|
|
{
|
|
return rasterizer_globals.framerate_throttle;
|
|
}
|
|
|
|
/* The native ports draw a frame whenever the display can show one, paced
|
|
by vsync, and frames fall between the 30 Hz ticks
|
|
(port/linux/game/render_interpolation.c): no vertical blank throttle, and
|
|
the frame is timed with the performance counter. */
|
|
static void main_update_time_unthrottled(
|
|
void)
|
|
{
|
|
static LARGE_INTEGER previous_counter;
|
|
LARGE_INTEGER counter;
|
|
LARGE_INTEGER frequency;
|
|
real seconds_elapsed = 0.0f;
|
|
|
|
QueryPerformanceCounter(&counter);
|
|
QueryPerformanceFrequency(&frequency);
|
|
if (previous_counter.QuadPart && frequency.QuadPart)
|
|
{
|
|
seconds_elapsed = (real)((double)(counter.QuadPart - previous_counter.QuadPart) /
|
|
(double)frequency.QuadPart);
|
|
}
|
|
previous_counter = counter;
|
|
|
|
if (main_globals.movie)
|
|
{
|
|
seconds_elapsed = main_globals.recording_dt;
|
|
}
|
|
else
|
|
{
|
|
seconds_elapsed = PIN(seconds_elapsed, 0.0f, 1.0f);
|
|
if (main_globals.connection == _game_connection_local)
|
|
{
|
|
if (debug_force_frame_rate_update)
|
|
seconds_elapsed = CEILING(seconds_elapsed, 0.03333333507180214f);
|
|
else
|
|
seconds_elapsed = CEILING(seconds_elapsed, 0.06666667014360428f);
|
|
}
|
|
}
|
|
|
|
main_globals.frame_start_milliseconds = system_milliseconds();
|
|
main_globals.seconds_elapsed = seconds_elapsed;
|
|
profile_seconds_elapsed(seconds_elapsed);
|
|
main_globals.rasterizer_initial_index =
|
|
rasterizer_globals.frame_and_vertical_blank_index;
|
|
}
|
|
|
|
static void main_update_time(
|
|
void)
|
|
{
|
|
unsigned long end_milliseconds;
|
|
long milliseconds_elapsed;
|
|
short requested_rate;
|
|
short requested_interval;
|
|
short elapsed_game_ticks;
|
|
short slot;
|
|
boolean framerate_throttle;
|
|
__int64 target_index;
|
|
__int64 minimum_target_index;
|
|
short short_target_index;
|
|
real seconds_elapsed;
|
|
|
|
if (halo_interpolation_enabled())
|
|
{
|
|
main_update_time_unthrottled();
|
|
return;
|
|
}
|
|
end_milliseconds = system_milliseconds();
|
|
minimum_target_index = MAX(
|
|
main_globals.rasterizer_target_index,
|
|
main_globals.rasterizer_throttle_end_index);
|
|
target_index = minimum_target_index;
|
|
framerate_throttle = main_framerate_throttle_enabled();
|
|
|
|
if (framerate_throttle)
|
|
{
|
|
csstrcpy(main_globals.vblank_debug_string, "");
|
|
if (global_frame_rate_throttle && rasterizer_globals.framerate_throttle_target >= 0)
|
|
{
|
|
requested_interval = (short)(
|
|
60 / (rasterizer_globals.framerate_throttle_target ? rasterizer_globals.framerate_throttle_target : 30));
|
|
main_globals.vblank_interval_requested = requested_interval;
|
|
|
|
if (rasterizer_globals.framerate_throttle_debug)
|
|
{
|
|
short best_interval;
|
|
short current_interval;
|
|
|
|
elapsed_game_ticks = game_time_get_elapsed();
|
|
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
"last%6I64d init%6I64d achv%6I64d pres%6I64d g%d cur%d... ",
|
|
main_globals.rasterizer_target_index,
|
|
main_globals.rasterizer_initial_index,
|
|
main_globals.rasterizer_throttle_start_index,
|
|
main_globals.rasterizer_throttle_end_index,
|
|
elapsed_game_ticks,
|
|
main_globals.vblank_interval_current);
|
|
|
|
best_interval = 5;
|
|
short_target_index = (short)minimum_target_index;
|
|
for (slot = 5; slot > 0; slot--)
|
|
{
|
|
short displayed_failure_count;
|
|
short target_age;
|
|
short slot_bucket;
|
|
boolean ignore_failure;
|
|
char const *label;
|
|
__int64 last_failure_index;
|
|
__int64 target_age_raw;
|
|
|
|
ignore_failure = FALSE;
|
|
displayed_failure_count = main_globals.vblank_failure_counts[slot];
|
|
if (displayed_failure_count >= 99)
|
|
displayed_failure_count = 99;
|
|
|
|
last_failure_index = main_globals.vblank_last_failure_indices[slot];
|
|
target_age_raw = short_target_index - last_failure_index;
|
|
if (target_age_raw < 99)
|
|
target_age = (short)target_age_raw;
|
|
else
|
|
target_age = 99;
|
|
|
|
slot_bucket = (short)((slot + 1) / 2);
|
|
current_interval = main_globals.vblank_interval_current;
|
|
if ((short)((current_interval + 1) / 2) > slot_bucket &&
|
|
(short)(current_interval / 2) <= slot_bucket &&
|
|
elapsed_game_ticks > slot_bucket)
|
|
{
|
|
ignore_failure = TRUE;
|
|
}
|
|
|
|
if (main_globals.rasterizer_throttle_start_index >
|
|
main_globals.rasterizer_initial_index + slot)
|
|
{
|
|
if (!ignore_failure)
|
|
{
|
|
long failure_count;
|
|
|
|
failure_count = main_globals.vblank_failure_counts[slot];
|
|
main_globals.vblank_failure_counts[slot] =
|
|
(short)(failure_count + 1);
|
|
main_globals.vblank_last_failure_indices[slot] = minimum_target_index;
|
|
}
|
|
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
"(%s%2d) ",
|
|
ignore_failure ? "ignor" : "fail ",
|
|
displayed_failure_count);
|
|
}
|
|
else
|
|
{
|
|
if (minimum_target_index >= last_failure_index + 15)
|
|
{
|
|
main_globals.vblank_failure_counts[slot] = 0;
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
"(ok %2d) ",
|
|
target_age);
|
|
}
|
|
else
|
|
{
|
|
label = main_globals.vblank_failure_counts[slot] >= 4 ? "dn" : "wt";
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
"(%s%2d/%2d) ",
|
|
label,
|
|
displayed_failure_count,
|
|
target_age);
|
|
}
|
|
}
|
|
|
|
if (slot >= main_globals.vblank_interval_requested &&
|
|
main_globals.vblank_failure_counts[slot] < 4)
|
|
{
|
|
best_interval = slot;
|
|
}
|
|
}
|
|
|
|
if (best_interval == 0)
|
|
requested_rate = 999;
|
|
else
|
|
requested_rate = (short)(60 / best_interval);
|
|
|
|
if (main_globals.vblank_interval_current < best_interval)
|
|
{
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
" FAILDOWN %d",
|
|
requested_rate);
|
|
}
|
|
else if (best_interval < main_globals.vblank_interval_current)
|
|
{
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
" RESTORE %d",
|
|
requested_rate);
|
|
}
|
|
else
|
|
{
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
" MAINTAIN %d",
|
|
requested_rate);
|
|
}
|
|
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
" des %d targ%6I64d",
|
|
best_interval,
|
|
minimum_target_index + best_interval);
|
|
main_globals.vblank_interval_current = best_interval;
|
|
}
|
|
else
|
|
{
|
|
main_globals.vblank_interval_current = requested_interval;
|
|
}
|
|
|
|
target_index += main_globals.vblank_interval_current;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
milliseconds_elapsed = (long)(real)(
|
|
(long)end_milliseconds - (long)main_globals.frame_start_milliseconds);
|
|
main_globals.vblank_interval_held = FALSE;
|
|
if (milliseconds_elapsed < 33)
|
|
{
|
|
profile_idle_start();
|
|
if (global_frame_rate_throttle)
|
|
Sleep(33 - milliseconds_elapsed);
|
|
profile_idle_end();
|
|
}
|
|
else
|
|
{
|
|
profile_lapsed_msec(milliseconds_elapsed - 33);
|
|
}
|
|
}
|
|
|
|
end_milliseconds = system_milliseconds();
|
|
if ((__int64)rasterizer_globals.frame_and_vertical_blank_index > target_index)
|
|
target_index = (__int64)rasterizer_globals.frame_and_vertical_blank_index;
|
|
|
|
if (framerate_throttle)
|
|
{
|
|
seconds_elapsed =
|
|
(real)(target_index - (__int64)main_globals.rasterizer_target_index) *
|
|
0.01666666753590107f;
|
|
}
|
|
else
|
|
{
|
|
seconds_elapsed =
|
|
(real)(end_milliseconds - main_globals.frame_start_milliseconds) *
|
|
0.001000000047497451f;
|
|
}
|
|
|
|
if (main_globals.movie)
|
|
{
|
|
seconds_elapsed = main_globals.recording_dt;
|
|
}
|
|
else
|
|
{
|
|
seconds_elapsed = PIN(seconds_elapsed, 0.0f, 1.0f);
|
|
if (main_globals.connection == _game_connection_local)
|
|
{
|
|
if (debug_force_frame_rate_update)
|
|
seconds_elapsed = CEILING(seconds_elapsed, 0.03333333507180214f);
|
|
else
|
|
seconds_elapsed = CEILING(seconds_elapsed, 0.06666667014360428f);
|
|
}
|
|
}
|
|
|
|
main_globals.frame_start_milliseconds = end_milliseconds;
|
|
main_globals.rasterizer_target_index = target_index;
|
|
main_globals.seconds_elapsed = seconds_elapsed;
|
|
profile_seconds_elapsed(seconds_elapsed);
|
|
main_globals.rasterizer_initial_index =
|
|
rasterizer_globals.frame_and_vertical_blank_index;
|
|
|
|
return;
|
|
}
|
|
|
|
void main_rasterizer_throttle(
|
|
void)
|
|
{
|
|
__int64 entry_index;
|
|
__int64 target_index;
|
|
__int64 end_index;
|
|
__int64 index_delta;
|
|
__int64 elapsed_index;
|
|
unsigned long start_milliseconds;
|
|
short lapsed_frames;
|
|
boolean did_throttle;
|
|
boolean synchronized;
|
|
char const *description;
|
|
|
|
entry_index = rasterizer_globals.frame_and_vertical_blank_index;
|
|
did_throttle = FALSE;
|
|
main_globals.rasterizer_throttle_start_index =
|
|
rasterizer_globals.frame_and_vertical_blank_index + 1;
|
|
if (rasterizer_globals.framerate_throttle && !halo_interpolation_enabled())
|
|
{
|
|
target_index = main_globals.rasterizer_target_index;
|
|
target_index--;
|
|
if ((__int64)rasterizer_globals.frame_and_vertical_blank_index < target_index)
|
|
{
|
|
start_milliseconds = system_milliseconds();
|
|
did_throttle = TRUE;
|
|
profile_idle_start();
|
|
while ((__int64)rasterizer_globals.frame_and_vertical_blank_index < target_index)
|
|
{
|
|
/* port: a sleep between looks, not only while a map
|
|
precaches: the Xbox spun here, which on a port holds a core
|
|
busy for most of each frame of a game held at 30 frames a
|
|
second (display.interpolation off). A millisecond is well
|
|
inside a 33 ms frame (Windows sets its timer to 1 ms:
|
|
win32_posix.c) */
|
|
Sleep(1);
|
|
if (system_milliseconds() > start_milliseconds + 1000)
|
|
{
|
|
console_warning(
|
|
"stuck waiting for VBLANK callback! disabling rasterizer framerate control");
|
|
rasterizer_globals.framerate_throttle = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
profile_idle_end();
|
|
}
|
|
}
|
|
|
|
end_index = rasterizer_globals.frame_and_vertical_blank_index + 1;
|
|
synchronized =
|
|
main_globals.vblank_interval_current == main_globals.vblank_interval_requested;
|
|
main_globals.rasterizer_throttle_end_index = end_index;
|
|
index_delta = end_index - main_globals.rasterizer_target_index;
|
|
lapsed_frames = (short)PIN(index_delta, 0, 0x7FFF);
|
|
|
|
if (did_throttle)
|
|
{
|
|
elapsed_index = rasterizer_globals.frame_and_vertical_blank_index - entry_index;
|
|
description = "THROTTLE";
|
|
}
|
|
else
|
|
{
|
|
elapsed_index = lapsed_frames;
|
|
description = lapsed_frames == 0 ? "SYNCED " : "LAPSED ";
|
|
}
|
|
|
|
_snprintf(
|
|
main_globals.vblank_debug_string + csstrlen(main_globals.vblank_debug_string),
|
|
sizeof(main_globals.vblank_debug_string) - csstrlen(main_globals.vblank_debug_string),
|
|
"%6I64d(targ%6I64d %s%2d)",
|
|
entry_index,
|
|
main_globals.rasterizer_target_index,
|
|
description,
|
|
elapsed_index);
|
|
main_globals.vblank_interval_held =
|
|
main_globals.vblank_interval_current > 0 && lapsed_frames == 0;
|
|
profile_lapsed_frames(
|
|
lapsed_frames,
|
|
synchronized,
|
|
main_globals.vblank_debug_string);
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_setup_connection(
|
|
void)
|
|
{
|
|
struct game_options options;
|
|
|
|
if (main_globals.playback_last_recording)
|
|
main_globals.want_to_be_at_main_menu = FALSE;
|
|
|
|
if (main_globals.want_to_be_at_main_menu)
|
|
{
|
|
main_menu_load();
|
|
return;
|
|
}
|
|
|
|
if (main_globals.playback_last_recording)
|
|
{
|
|
main_globals.connection = _game_connection_film_playback;
|
|
error(_error_silent, "error opening saved film");
|
|
main_globals.want_to_be_at_main_menu = TRUE;
|
|
main_menu_load();
|
|
}
|
|
else
|
|
{
|
|
main_globals.connection = _game_connection_local;
|
|
game_options_new(&options);
|
|
csstrncpy(
|
|
options.map_name,
|
|
main_globals.soloplayer_map_name,
|
|
NUMBEROF(options.map_name) - 1);
|
|
options.map_name[NUMBEROF(options.map_name) - 1] = 0;
|
|
options.difficulty = global_difficulty_level;
|
|
game_precache_new_map(options.map_name, TRUE);
|
|
game_dispose_from_old_map();
|
|
main_new_map(&options);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_initialize_time(
|
|
void)
|
|
{
|
|
main_globals.frame_start_milliseconds = system_milliseconds();
|
|
main_globals.rasterizer_target_index = 0;
|
|
rasterizer_set_vblank_callback(main_vertical_blank_interrupt_handler);
|
|
main_globals.vblank_flip_delta_index = 0;
|
|
csmemset(main_globals.vblank_flip_deltas, 0, sizeof(main_globals.vblank_flip_deltas));
|
|
main_globals.d3d_flip_count = d3d_find_flipcount();
|
|
return;
|
|
}
|
|
|
|
static void main_reset_map_private(
|
|
void)
|
|
{
|
|
if (!game_time_get_paused())
|
|
{
|
|
scenario_switch_structure_bsp(0);
|
|
game_dispose_from_old_map();
|
|
input_flush();
|
|
game_initialize_for_new_map();
|
|
create_local_players();
|
|
game_time_start();
|
|
game_initial_pulse();
|
|
ui_widgets_disable_pause_game(30);
|
|
|
|
main_globals.reset_map = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void screenshot_render(
|
|
struct render_window *windows)
|
|
{
|
|
char path[512];
|
|
struct file_reference reference;
|
|
point2d screenshot_page_index;
|
|
point2d screenshot_index;
|
|
struct bitmap_data *bitmap;
|
|
long screenshot_width;
|
|
long screenshot_height;
|
|
|
|
global_screenshot_size = PIN(global_screenshot_size, 1, 3);
|
|
screenshot_width =
|
|
rasterizer_globals.reserved04.screen_bounds.x1 -
|
|
rasterizer_globals.reserved04.screen_bounds.x0;
|
|
screenshot_height =
|
|
rasterizer_globals.reserved04.screen_bounds.y1 -
|
|
rasterizer_globals.reserved04.screen_bounds.y0;
|
|
bitmap = bitmap_2d_new(
|
|
global_screenshot_size * screenshot_width,
|
|
global_screenshot_size * screenshot_height,
|
|
0,
|
|
_bitmap_format_x8r8g8b8);
|
|
|
|
if (bitmap && bitmap->base_address)
|
|
{
|
|
console_printf(TRUE, "");
|
|
console_close();
|
|
|
|
for (screenshot_page_index.y = 0;
|
|
screenshot_page_index.y < global_screenshot_count.count;
|
|
screenshot_page_index.y++)
|
|
{
|
|
for (screenshot_page_index.x = 0;
|
|
screenshot_page_index.x < global_screenshot_count.count;
|
|
screenshot_page_index.x++)
|
|
{
|
|
for (screenshot_index.y = 0;
|
|
screenshot_index.y < global_screenshot_size;
|
|
screenshot_index.y++)
|
|
{
|
|
for (screenshot_index.x = 0;
|
|
screenshot_index.x < global_screenshot_size;
|
|
screenshot_index.x++)
|
|
{
|
|
if (global_screenshot_count.count > 1 ||
|
|
global_screenshot_size > 1)
|
|
{
|
|
render_frame(
|
|
windows,
|
|
1,
|
|
&screenshot_page_index,
|
|
&screenshot_index,
|
|
bitmap,
|
|
0.0f);
|
|
render_frame_present(&screenshot_index, bitmap);
|
|
}
|
|
else
|
|
{
|
|
render_frame(windows, 1, NULL, NULL, bitmap, 0.0f);
|
|
render_frame_present(NULL, bitmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
sprintf(
|
|
path,
|
|
"%dscreenshot%d%d.tif",
|
|
main_globals.screenshot_identifier,
|
|
screenshot_page_index.y,
|
|
screenshot_page_index.x);
|
|
file_reference_create_from_path(&reference, path, FALSE);
|
|
screenshot_record(bitmap, &reference);
|
|
}
|
|
}
|
|
|
|
main_globals.screenshot_identifier++;
|
|
bitmap_delete(bitmap);
|
|
}
|
|
|
|
global_screenshot_count.count = 0;
|
|
return;
|
|
}
|
|
|
|
void main_framerate_render(
|
|
void)
|
|
{
|
|
if (display_framerate)
|
|
{
|
|
long font_tag_index;
|
|
|
|
font_tag_index = hud_globals->messaging.single_player_font.index;
|
|
if (font_tag_index != NONE)
|
|
{
|
|
real frame_seconds;
|
|
real frame_rate_real;
|
|
long frame_rate;
|
|
rectangle2d bounds;
|
|
/* room for three digits under C99 snprintf, which (unlike MSVC's
|
|
_snprintf) keeps a byte of the count for the terminator */
|
|
char frame_rate_string[8];
|
|
|
|
bounds = render.camera.window_bounds;
|
|
/* The native ports draw at the display's refresh rate, up to
|
|
hundreds of frames a second: show frames per second averaged over
|
|
half a second, counting each frame once (split screen draws this
|
|
once per view), with no 100 cap. */
|
|
{
|
|
static unsigned long counted_frame_start = 0;
|
|
static real window_seconds = 0.0f;
|
|
static long window_frames = 0;
|
|
static long average_frame_rate = 0;
|
|
|
|
if (main_globals.frame_start_milliseconds != counted_frame_start)
|
|
{
|
|
counted_frame_start = main_globals.frame_start_milliseconds;
|
|
window_seconds += main_globals.seconds_elapsed;
|
|
window_frames++;
|
|
if (window_seconds >= 0.5f)
|
|
{
|
|
average_frame_rate = fast_ftol(window_frames / window_seconds);
|
|
window_seconds = 0.0f;
|
|
window_frames = 0;
|
|
}
|
|
}
|
|
frame_rate = PIN(average_frame_rate, 0, 999);
|
|
(void)frame_seconds;
|
|
(void)frame_rate_real;
|
|
}
|
|
|
|
_snprintf(
|
|
frame_rate_string,
|
|
NUMBEROF(frame_rate_string) - 1,
|
|
"%d",
|
|
(short)frame_rate);
|
|
frame_rate_string[NUMBEROF(frame_rate_string) - 1] = 0;
|
|
bounds.x0 = (short)(bounds.x1 - 50);
|
|
bounds.y0 = (short)(bounds.y1 - 50);
|
|
draw_string_set_format(NONE, _text_justification_left, 0);
|
|
draw_string_set_color(
|
|
(short)frame_rate >= 30 ? global_real_argb_green : global_real_argb_red);
|
|
draw_string_set_font(font_tag_index);
|
|
rasterizer_draw_string(&bounds, NULL, NULL, 0, frame_rate_string);
|
|
}
|
|
}
|
|
|
|
if (display_vblank_deltas)
|
|
{
|
|
long font_tag_index;
|
|
|
|
font_tag_index = hud_globals->messaging.single_player_font.index;
|
|
if (font_tag_index != NONE)
|
|
{
|
|
short index;
|
|
rectangle2d bounds;
|
|
char delta_string[4];
|
|
|
|
bounds = render.camera.window_bounds;
|
|
bounds.x0 = (short)(bounds.x1 - 50);
|
|
bounds.y0 = (short)(bounds.y1 - 50);
|
|
index = (short)((main_globals.vblank_flip_delta_index + 14) % 15);
|
|
while (index != main_globals.vblank_flip_delta_index)
|
|
{
|
|
bounds.y0 -= 20;
|
|
bounds.y1 -= 20;
|
|
_snprintf(
|
|
delta_string,
|
|
NUMBEROF(delta_string) - 1,
|
|
"%d",
|
|
main_globals.vblank_flip_deltas[index]);
|
|
delta_string[NUMBEROF(delta_string) - 1] = 0;
|
|
draw_string_set_format(NONE, _text_justification_right, 0);
|
|
draw_string_set_font(font_tag_index);
|
|
draw_string_set_color(
|
|
main_globals.vblank_flip_deltas[index] == 2
|
|
? global_real_argb_white
|
|
: global_real_argb_red);
|
|
rasterizer_draw_string(&bounds, NULL, NULL, 0, delta_string);
|
|
index = (short)((index + 14) % 15);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (display_precache_progress && cache_files_precache_in_progress())
|
|
{
|
|
long font_tag_index;
|
|
|
|
font_tag_index = hud_globals->messaging.single_player_font.index;
|
|
if (font_tag_index != NONE)
|
|
{
|
|
real progress;
|
|
long progress_percent;
|
|
rectangle2d bounds;
|
|
char progress_string[4];
|
|
|
|
if (cache_files_precache_map_status(&progress) == 0)
|
|
{
|
|
real progress_percent_real;
|
|
|
|
bounds = render.camera.window_bounds;
|
|
progress_percent_real = progress * 100.0f;
|
|
progress_percent = fast_ftol(progress_percent_real);
|
|
_snprintf(
|
|
progress_string,
|
|
NUMBEROF(progress_string) - 1,
|
|
"%d",
|
|
(short)progress_percent);
|
|
progress_string[NUMBEROF(progress_string) - 1] = 0;
|
|
bounds.x0 = (short)(bounds.x1 - 50);
|
|
bounds.y0 = (short)(bounds.y1 - 100);
|
|
draw_string_set_format(NONE, _text_justification_left, 0);
|
|
draw_string_set_color(global_real_argb_purple);
|
|
draw_string_set_font(font_tag_index);
|
|
rasterizer_draw_string(&bounds, NULL, NULL, 0, progress_string);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void halt_and_catch_fire(
|
|
void)
|
|
{
|
|
short gamepad_index;
|
|
long font_tag_index;
|
|
struct scenario *scenario;
|
|
struct rasterizer_frame_begin_parameters frame_parameters;
|
|
struct rasterizer_window_begin_parameters window_parameters;
|
|
#ifdef HALO_NATIVE_BUILD_INFO
|
|
char banner[256];
|
|
char label[96];
|
|
#endif
|
|
|
|
#ifdef HALO_WEB
|
|
/* port: a browser shows only what a frame of its own main loop drew, and
|
|
this screen never returns to it: the game stops, and the page shows the
|
|
error from the log, where it already is (port/web/README.md) */
|
|
emscripten_cancel_main_loop();
|
|
abort();
|
|
#endif
|
|
if (!global_screenshot_count.halt_recursion_lock)
|
|
{
|
|
scenario = global_scenario_try_and_get();
|
|
global_screenshot_count.halt_recursion_lock = TRUE;
|
|
for (gamepad_index = 0; gamepad_index < MAXIMUM_GAMEPADS; gamepad_index++)
|
|
{
|
|
if (input_has_gamepad(gamepad_index))
|
|
{
|
|
input_set_gamepad_rumbler_state(
|
|
gamepad_index,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
|
|
if (scenario)
|
|
{
|
|
font_tag_index = interface_get_tag_index(_interface_font_terminal);
|
|
}
|
|
if (!scenario || font_tag_index == NONE)
|
|
{
|
|
font_tag_index = tag_loaded(
|
|
FONT_GROUP_TAG,
|
|
"old tags\\internal system plain");
|
|
}
|
|
#ifdef HALO_NATIVE_BUILD_INFO
|
|
main_native_build_label(label, sizeof(label));
|
|
_snprintf(banner, sizeof(banner) - 1,
|
|
"%s\r\nCompiled: %s %s\r\nFull log: debug.txt (game data folder)\r\nRecent messages (newest first):",
|
|
label, __DATE__, __TIME__);
|
|
banner[sizeof(banner) - 1] = 0;
|
|
#endif
|
|
|
|
while (TRUE)
|
|
{
|
|
rasterizer_reset_state();
|
|
csmemset(&frame_parameters, 0, sizeof(frame_parameters));
|
|
rasterizer_frame_begin(&frame_parameters);
|
|
rasterizer_windows_begin();
|
|
|
|
csmemset(&window_parameters, 0, sizeof(window_parameters));
|
|
window_parameters.camera.position = *global_origin3d;
|
|
window_parameters.camera.forward = *global_forward3d;
|
|
window_parameters.camera.up = *global_up3d;
|
|
window_parameters.camera.mirrored = FALSE;
|
|
window_parameters.camera.vertical_field_of_view =
|
|
2.0f * arctangent(
|
|
0.75f * render_camera_get_adjusted_field_of_view_tangent(
|
|
DEGREES_TO_RADIANS(80.0f)),
|
|
1.0f);
|
|
window_parameters.camera.z_near = rasterizer_globals.near_clip_distance;
|
|
window_parameters.camera.viewport_bounds.x0 = 0;
|
|
window_parameters.camera.viewport_bounds.x1 = (short)halo_screen_width();
|
|
window_parameters.camera.viewport_bounds.y0 = 0;
|
|
window_parameters.camera.viewport_bounds.y1 = 480;
|
|
window_parameters.camera.z_far = rasterizer_globals.far_clip_distance;
|
|
render_camera_build_frustum(
|
|
&window_parameters.camera,
|
|
NULL,
|
|
&window_parameters.frustum,
|
|
TRUE);
|
|
window_parameters.rasterizer_target = 0;
|
|
window_parameters.fog.atmospheric_color = *global_real_rgb_blue;
|
|
window_parameters.fog.atmospheric_maximum_distance = 0.0f;
|
|
window_parameters.fog.atmospheric_minimum_distance = 0.0f;
|
|
window_parameters.fog.planar_mode = 0;
|
|
render.camera = window_parameters.camera;
|
|
rasterizer_window_begin(&window_parameters);
|
|
|
|
if (font_tag_index != NONE)
|
|
{
|
|
point2d cursor = { 0, 0 };
|
|
rectangle2d bounds = rasterizer_globals.reserved04.frame_bounds;
|
|
|
|
draw_string_set_draw_mode(
|
|
font_tag_index,
|
|
NONE,
|
|
0,
|
|
0,
|
|
global_real_argb_white);
|
|
draw_string_set_tab_stops(NULL, 0);
|
|
draw_string_set_color(global_real_argb_white);
|
|
rasterizer_draw_string(
|
|
&bounds,
|
|
NULL,
|
|
&cursor,
|
|
-4,
|
|
#ifdef HALO_NATIVE_BUILD_INFO
|
|
banner);
|
|
#else
|
|
"halobeta xbox 01.01.14.2342 built at: Jan 14 2002 12:49:20");
|
|
#endif
|
|
bounds.y0 = cursor.y - 1;
|
|
rasterizer_draw_string(
|
|
&bounds,
|
|
NULL,
|
|
&cursor,
|
|
-4,
|
|
#ifdef HALO_NATIVE_BUILD_INFO
|
|
main_native_error_tail(error_get()));
|
|
#else
|
|
error_get());
|
|
#endif
|
|
}
|
|
|
|
rasterizer_transparent_geometry_draw(TRUE);
|
|
rasterizer_transparent_geometry_draw(FALSE);
|
|
rasterizer_debug_draw();
|
|
rasterizer_window_end();
|
|
rasterizer_windows_end();
|
|
rasterizer_frame_end();
|
|
rasterizer_present(NULL, NULL);
|
|
input_update();
|
|
}
|
|
}
|
|
|
|
exit(0);
|
|
return;
|
|
}
|
|
|
|
void main_loop_of_death(
|
|
void)
|
|
{
|
|
while (TRUE)
|
|
{
|
|
input_frame_begin();
|
|
input_update();
|
|
shell_idle();
|
|
event_manager_update();
|
|
telnet_console_process();
|
|
process_ui_widgets();
|
|
main_pregame_render();
|
|
main_rasterizer_throttle();
|
|
main_present_frame();
|
|
input_frame_end();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void main_game_render(
|
|
double time_delta_since_tick_sec)
|
|
{
|
|
boolean force_single_screen;
|
|
long window_index;
|
|
struct render_window *window;
|
|
struct observer_result const *observer;
|
|
long player_window_count;
|
|
long window_count;
|
|
short last_local_player_index;
|
|
|
|
lock_global_random_seed();
|
|
collision_log_continue_period(TRUE);
|
|
sound_render();
|
|
force_single_screen = game_engine_force_single_screen();
|
|
last_local_player_index = NONE;
|
|
|
|
window_count = PIN(local_player_count(), 1, MAXIMUM_LOCAL_PLAYERS);
|
|
player_window_count = window_count;
|
|
if (force_single_screen || cinematic_in_progress())
|
|
{
|
|
window_count = 1;
|
|
player_window_count = 1;
|
|
}
|
|
|
|
for (window_index = 0; window_index < player_window_count; window_index++)
|
|
{
|
|
window = &global_screenshot_count.windows[window_index];
|
|
observer = NULL;
|
|
|
|
compute_window_bounds(
|
|
window_index,
|
|
player_window_count,
|
|
&window->rasterizer_camera.viewport_bounds,
|
|
&window->rasterizer_camera.window_bounds);
|
|
if (force_single_screen)
|
|
{
|
|
window->local_player_index = NONE;
|
|
}
|
|
else if (window_index < window_count)
|
|
{
|
|
if (!rasterizer_debug_options.force_all_player_views_to_default_player ||
|
|
last_local_player_index == NONE)
|
|
{
|
|
if (game_connection() == _game_connection_film_playback)
|
|
{
|
|
last_local_player_index = 0;
|
|
}
|
|
else
|
|
{
|
|
last_local_player_index = local_player_get_next(last_local_player_index);
|
|
}
|
|
}
|
|
|
|
window->local_player_index = last_local_player_index;
|
|
observer = observer_get_camera(window->local_player_index);
|
|
observer = render_interpolation_camera(window->local_player_index, observer);
|
|
}
|
|
else
|
|
{
|
|
window->local_player_index = NONE;
|
|
}
|
|
|
|
set_window_camera_values(window, observer);
|
|
window->console_window = FALSE;
|
|
}
|
|
|
|
window = &global_screenshot_count.windows[player_window_count];
|
|
compute_window_bounds(
|
|
0,
|
|
1,
|
|
&window->rasterizer_camera.viewport_bounds,
|
|
&window->rasterizer_camera.window_bounds);
|
|
window->local_player_index = NONE;
|
|
window->console_window = TRUE;
|
|
set_window_camera_values(window, NULL);
|
|
|
|
if (global_screenshot_count.count <= 0)
|
|
{
|
|
render_frame(
|
|
global_screenshot_count.windows,
|
|
player_window_count + 1,
|
|
NULL,
|
|
NULL,
|
|
main_globals.movie,
|
|
(real)time_delta_since_tick_sec);
|
|
}
|
|
else
|
|
{
|
|
screenshot_render(global_screenshot_count.windows);
|
|
}
|
|
|
|
collision_log_end_period();
|
|
unlock_global_random_seed();
|
|
return;
|
|
}
|
|
|
|
/* port: the main loop's steps, timed in the profiling build */
|
|
PROFILE_SECTION(main_input_section, "input")
|
|
PROFILE_SECTION(main_platform_events_section, "platform_events")
|
|
PROFILE_SECTION(main_network_start_frame_section, "network_start_frame")
|
|
PROFILE_SECTION(main_update_time_section, "main_update_time")
|
|
PROFILE_SECTION(main_ui_update_section, "ui_update")
|
|
PROFILE_SECTION(main_network_end_frame_section, "network_end_frame")
|
|
PROFILE_SECTION(main_game_time_update_section, "game_time_update")
|
|
PROFILE_SECTION(main_non_deterministic_update_section, "non_deterministic_update")
|
|
PROFILE_SECTION(main_throttle_section, "throttle")
|
|
PROFILE_SECTION(main_present_section, "present")
|
|
|
|
#ifdef HALO_WEB
|
|
/* port: in a browser, the page's frames call the main loop one iteration at a
|
|
time, and what an iteration drew shows once it returns (port/web/README.md) */
|
|
static boolean main_loop_iteration(void);
|
|
|
|
static void main_loop_web_frame(
|
|
void)
|
|
{
|
|
if (!main_loop_iteration())
|
|
{
|
|
emscripten_cancel_main_loop();
|
|
error(_error_silent, "end of saved film");
|
|
main_exit();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void main_loop(
|
|
void)
|
|
{
|
|
#ifndef HALO_WEB
|
|
boolean render_frame;
|
|
long connection;
|
|
#endif
|
|
|
|
if (!game_in_editor())
|
|
{
|
|
csstrncpy(main_globals.soloplayer_map_name, "levels\\b30\\b30", NUMBEROF(main_globals.soloplayer_map_name)-1);
|
|
main_globals.soloplayer_map_name[NUMBEROF(main_globals.soloplayer_map_name)-1] = '\0';
|
|
}
|
|
|
|
main_globals.want_to_be_at_main_menu = !game_in_editor();
|
|
main_globals.switch_to_structure_bsp_index = NONE;
|
|
main_globals.halt_time_scale = TRUE;
|
|
|
|
console_initialize();
|
|
debug_keys_initialize();
|
|
game_initialize();
|
|
console_startup();
|
|
main_setup_connection();
|
|
main_initialize_time();
|
|
|
|
#ifdef HALO_WEB
|
|
emscripten_set_main_loop(main_loop_web_frame, 0, TRUE);
|
|
}
|
|
|
|
static boolean main_loop_iteration(
|
|
void)
|
|
{
|
|
boolean render_frame;
|
|
long connection;
|
|
|
|
do
|
|
#else
|
|
while (TRUE)
|
|
#endif
|
|
{
|
|
if (!game_in_editor())
|
|
{
|
|
if (main_globals.switch_to_structure_bsp_index!=NONE)
|
|
{
|
|
main_switch_to_structure_bsp_private();
|
|
}
|
|
|
|
if (main_globals.lost_map)
|
|
{
|
|
main_lost_map_private();
|
|
}
|
|
|
|
if (main_globals.won_map)
|
|
{
|
|
main_won_map_private();
|
|
}
|
|
|
|
if (main_globals.respawn)
|
|
{
|
|
main_respawn_private();
|
|
}
|
|
|
|
if (main_globals.save_map_completed)
|
|
{
|
|
main_saving_map_private();
|
|
}
|
|
|
|
if (main_globals.defer_map_change)
|
|
{
|
|
main_change_map_name();
|
|
}
|
|
|
|
if (main_globals.revert_map)
|
|
{
|
|
main_revert_map_private();
|
|
}
|
|
|
|
if (main_globals.skip_cinematic)
|
|
{
|
|
main_skip_cinematic_private();
|
|
}
|
|
|
|
if (main_globals.reset_map)
|
|
{
|
|
main_reset_map_private();
|
|
}
|
|
|
|
if (main_globals.save_core)
|
|
{
|
|
main_save_core_private();
|
|
}
|
|
|
|
if (main_globals.load_core)
|
|
{
|
|
main_load_core_private();
|
|
}
|
|
|
|
if (main_globals.want_to_be_at_main_menu)
|
|
{
|
|
main_menu_load();
|
|
}
|
|
|
|
if (main_globals.load_last_solo_level)
|
|
{
|
|
main_load_last_solo_map();
|
|
}
|
|
|
|
if (main_globals.run_xdemos)
|
|
{
|
|
main_run_demos_private();
|
|
}
|
|
|
|
if (main_globals.skip)
|
|
{
|
|
main_skip_private();
|
|
}
|
|
|
|
if (main_globals.queue_map)
|
|
{
|
|
main_queue_map_private();
|
|
}
|
|
}
|
|
else if (main_globals.reset_map)
|
|
{
|
|
main_reset_map_private();
|
|
}
|
|
|
|
profile_frame_start();
|
|
profile_scope_enter(main_input_section)
|
|
input_frame_begin();
|
|
input_update();
|
|
input_abstraction_update();
|
|
profile_scope_exit(main_input_section)
|
|
profile_scope_enter(main_platform_events_section)
|
|
shell_idle();
|
|
event_manager_update();
|
|
telnet_console_process();
|
|
profile_scope_exit(main_platform_events_section)
|
|
|
|
if (!shell_application_is_paused())
|
|
{
|
|
render_frame = TRUE;
|
|
|
|
/* automated system link tests (port/linux/game/network_test.c) */
|
|
network_test_update(main_globals.main_menu_scenario_loaded, main_globals.seconds_elapsed);
|
|
/* port: voice chat, in the lobby and in game (port/linux/game/network_voice.c) */
|
|
network_voice_update();
|
|
/* port: map sharing, in the lobby and in game (port/linux/game/map_share.c) */
|
|
map_share_update();
|
|
/* port: network games' text chat (port/linux/game/network_chat.c) */
|
|
network_chat_update();
|
|
profile_scope_enter(main_network_start_frame_section)
|
|
connection = main_globals.connection;
|
|
if (connection==_game_connection_network_client)
|
|
{
|
|
if (!network_game_client_start_frame())
|
|
{
|
|
display_error_when_main_menu_loaded(6);
|
|
error(_error_silent, "the game host went down");
|
|
network_game_abort();
|
|
}
|
|
}
|
|
else if (connection==_game_connection_network_server)
|
|
{
|
|
if (!network_game_client_start_frame())
|
|
{
|
|
display_error_when_main_menu_loaded(1);
|
|
error(_error_silent, "the game host went down");
|
|
network_game_abort();
|
|
}
|
|
else if (!network_game_server_start_frame())
|
|
{
|
|
display_error_when_main_menu_loaded(1);
|
|
error(_error_silent, "the game host went down");
|
|
network_game_abort();
|
|
}
|
|
}
|
|
else if (connection==_game_connection_film_playback)
|
|
{
|
|
profile_scope_exit(main_network_start_frame_section)
|
|
break;
|
|
}
|
|
profile_scope_exit(main_network_start_frame_section)
|
|
|
|
profile_scope(main_update_time_section, main_update_time();)
|
|
profile_scope_enter(main_ui_update_section)
|
|
process_ui_widgets();
|
|
bink_playback_update();
|
|
profile_scope_exit(main_ui_update_section)
|
|
|
|
/* port: not the Xbox debug keyboard's End and Escape, which stop
|
|
the movie and restart the map: this keyboard reaches the game only
|
|
through the console and the menus' text boxes, whose End and Escape
|
|
they are (port/linux/src/xinput_sdl.c) */
|
|
if (editor_should_exit())
|
|
{
|
|
main_movie_stop();
|
|
|
|
if (!game_engine_running())
|
|
{
|
|
main_reset_map();
|
|
}
|
|
}
|
|
|
|
if (game_in_progress())
|
|
{
|
|
terminal_update();
|
|
|
|
if (!console_update() || main_globals.connection!=_game_connection_local)
|
|
{
|
|
/* port: not while a chat line is typed (its K plays an animation) */
|
|
if (!network_chat_typing())
|
|
debug_keys_update();
|
|
cheats_update();
|
|
player_control_update((real)main_globals.halt_time_scale*main_globals.seconds_elapsed);
|
|
|
|
connection = main_globals.connection;
|
|
profile_scope_enter(main_network_end_frame_section)
|
|
if (connection>_game_connection_local && connection<=_game_connection_network_server && !network_game_client_end_frame())
|
|
{
|
|
display_error_when_main_menu_loaded(1);
|
|
network_game_abort();
|
|
}
|
|
profile_scope_exit(main_network_end_frame_section)
|
|
|
|
#ifdef HALO_ANDROID
|
|
halo_vr_phase(HALO_VR_PHASE_INPUT, 0);
|
|
#endif
|
|
profile_scope(main_game_time_update_section,
|
|
game_time_update((real)main_globals.halt_time_scale*main_globals.seconds_elapsed);)
|
|
#ifdef HALO_ANDROID
|
|
halo_vr_phase(HALO_VR_PHASE_TICKS, game_time_get_elapsed());
|
|
#endif
|
|
|
|
render_frame = main_globals.main_menu_scenario_loaded ||
|
|
(main_globals.halt_time_scale &&
|
|
(game_time_get_paused() || game_time_get_elapsed()>0 || game_time_get_speed()<1.0f));
|
|
/* frames between ticks too (render_interpolation.c) */
|
|
if (halo_interpolation_enabled())
|
|
render_frame = main_globals.main_menu_scenario_loaded || main_globals.halt_time_scale;
|
|
render_frame &= !game_engine_running() || game_time_get()>=3;
|
|
|
|
profile_scope_enter(main_non_deterministic_update_section)
|
|
collision_log_continue_period(1);
|
|
director_update((real)main_globals.halt_time_scale*main_globals.seconds_elapsed);
|
|
observer_update((real)main_globals.halt_time_scale*main_globals.seconds_elapsed);
|
|
collision_log_end_period();
|
|
game_engine_update_non_deterministic((real)main_globals.halt_time_scale*main_globals.seconds_elapsed);
|
|
#ifdef HALO_ANDROID
|
|
halo_vr_phase(HALO_VR_PHASE_CAMERA, 0);
|
|
#endif
|
|
profile_scope_exit(main_non_deterministic_update_section)
|
|
}
|
|
|
|
if (main_globals.saving_map)
|
|
{
|
|
main_save_map_private();
|
|
}
|
|
|
|
if (render_frame && !debug_no_drawing)
|
|
{
|
|
profile_render_start();
|
|
render_interpolation_frame_begin();
|
|
main_game_render((double)main_globals.seconds_elapsed);
|
|
render_interpolation_frame_end();
|
|
profile_render_end();
|
|
#ifdef HALO_ANDROID
|
|
halo_vr_phase(HALO_VR_PHASE_DRAW, 0);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
profile_render_start();
|
|
main_pregame_render();
|
|
profile_render_end();
|
|
}
|
|
|
|
profile_scope(main_throttle_section, main_rasterizer_throttle();)
|
|
|
|
if (render_frame && !debug_no_drawing)
|
|
{
|
|
profile_scope(main_present_section, main_present_frame();)
|
|
}
|
|
}
|
|
|
|
input_frame_end();
|
|
profile_frame_end();
|
|
main_frame_rate_debug();
|
|
|
|
if (main_globals.restart_time)
|
|
{
|
|
main_globals.restart_time = FALSE;
|
|
main_reset_time();
|
|
main_globals.halt_time_scale = TRUE;
|
|
}
|
|
#ifdef HALO_WEB
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
#ifdef HALO_WEB
|
|
while (FALSE);
|
|
/* (a film played to its end) */
|
|
return FALSE;
|
|
#else
|
|
|
|
error(_error_silent, "end of saved film");
|
|
main_exit();
|
|
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
/* ---------- private code */
|