mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 15:00:27 +02:00
Split-screen co-op
This commit is contained in:
1 parent
d578f88bd0
commit
193cbf59c7
16 files changed
+377
-31
No files matched your search
@@ -51,7 +51,7 @@ until it is redrawn.
|
||||
| `ce/shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__6.png` | 512x256 | `shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__6.png` |
|
||||
| `ce/shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__8.png` | 512x256 | `shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__8.png` |
|
||||
|
||||
13 of 154 frames are placeholders.
|
||||
13 of 155 frames are placeholders.
|
||||
|
||||
The PC version's `ui\gamespy` and `ui\ticker` fonts are drawn with `ui\small_ui`, which
|
||||
the Xbox's map has.
|
||||
@@ -10,7 +10,7 @@ do (save a setting, join a game) does not happen; the screens open and close as
|
||||
|
||||
| Function | Uses | |
|
||||
| --- | --- | --- |
|
||||
| `common button bar update` | 24 | PC function |
|
||||
| `common button bar update` | 25 | PC function |
|
||||
| `direct ip connect init` | 1 | PC function |
|
||||
| `direct ip connect update` | 1 | PC function |
|
||||
| `direct ip edit field` | 4 | PC function |
|
||||
|
||||
@@ -309,6 +309,9 @@
|
||||
<bitmap name="main_menu/solo_level_select/header_load_level">
|
||||
<frame png="ce/shell/main_menu/solo_level_select/header_load_level__0.png" width="512" height="64"/>
|
||||
</bitmap>
|
||||
<bitmap name="main_menu/multiplayer_type_select/coop/header_player_2">
|
||||
<frame png="ce/port/main_menu/multiplayer_type_select/coop/header_player_2.png" width="512" height="64"/>
|
||||
</bitmap>
|
||||
<bitmap name="main_menu/multiplayer_type_select/join_game/header_direct_link">
|
||||
<frame png="ce/port/main_menu/multiplayer_type_select/join_game/header_direct_link.png" width="512" height="64"/>
|
||||
</bitmap>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's multiplayer screens, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/multiplayer_type_select/multiplayer_type_coop_item" type="text" left="51" width="232" height="32" bitmap="bitmaps/list_item_bkd" string_list="main_menu/multiplayer_type_select/multiplayer_options" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="5">
|
||||
<on event="a" run="port coop begin" open="main_menu/multiplayer_type_select/coop/player_2_profile_screen" branch="true"/>
|
||||
<on event="start" run="port coop begin" open="main_menu/multiplayer_type_select/coop/player_2_profile_screen" branch="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/player_2_profile_screen" width="640" height="480" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/gradient">
|
||||
<child widget="main_menu/new_select/sel_list_desc_bkd"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/player_2_profile_list"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/header_player_2"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/player_2_help" x="20" y="416"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/header_player_2" controller="1" left="35" top="11" width="363" height="59" bitmap="main_menu/multiplayer_type_select/coop/header_player_2">
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/player_2_profile_list" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/profile_manager/player_profile_extended_desc">
|
||||
<data input="3wide player profile list update"/>
|
||||
<on event="created" run="port coop player 2 list initialize"/>
|
||||
<on event="deleted" run="player profile list dispose"/>
|
||||
<on event="custom_activation" run="port coop player 2" open="main_menu/solo_level_select/solo_level_select_screen" branch="true"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_0" x="20" y="73"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_1" x="20" y="103"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_2" x="20" y="133"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_3" x="20" y="163"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_4" x="20" y="193"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_5" x="20" y="223"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_6" x="20" y="253"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_7" x="20" y="283"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_8" x="20" y="313"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_9" x="20" y="343"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/list_item_10" x="20" y="373"/>
|
||||
<child widget="main_menu/multiplayer_type_select/coop/player_2_button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_0" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd_top" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_1" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_2" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_3" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_4" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_5" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_6" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_7" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_8" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_9" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/list_item_10" width="390" height="28" bitmap="bitmaps/sel_list_item_bkd" font="ui\large_ui" color="#FF2896FF" align="center" text_y="3">
|
||||
<on event="a" run="single prev cl item activated"/>
|
||||
<on event="start" run="single prev cl item activated"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/new_select/list_item_text" x="25"/>
|
||||
<child widget="main_menu/new_select/list_item_arrows"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/player_2_help" type="text" width="350" height="24" text="Player 2: choose with your controller." font="ui\small_ui" color="#FF2896FF" text_y="5" text_flags="no_focus_test">
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/coop/player_2_button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<data input="common button bar update"/>
|
||||
<child widget="main_menu/profile_manager/profile_manager_button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_back" x="510" y="1"/>
|
||||
</widget>
|
||||
</menus>
|
||||
@@ -59,13 +59,14 @@
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_create_item" x="-32" y="210"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_create_internet_item" y="243"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_create_lan_item" y="276"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_gametypes_item" y="342"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_coop_item" y="309"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_gametypes_item" y="375"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/multiplayer_type_select_screen" width="640" height="480" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/gradient">
|
||||
<on event="created" run="mp type menu initialize"/>
|
||||
<child widget="main_menu/multiplayer_type_select/header_multiplayer"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_select_list"/>
|
||||
<child widget="main_menu/blueline" x="64" y="325"/>
|
||||
<child widget="main_menu/blueline" x="64" y="358"/>
|
||||
</widget>
|
||||
</menus>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
@@ -374,6 +374,7 @@
|
||||
<string text="Join a game by its invite link,\nor one a Discord invite\nreached."/>
|
||||
<string text="Host a game on the Internet:\nplayers join by its invite\nlink or Discord."/>
|
||||
<string text="Host a game on your LAN\nonly."/>
|
||||
<string text="Play the campaign with a\nfriend in split screen:\nplayer 2 on a gamepad."/>
|
||||
<string text="Set all the attributes for your \nmultiplayer gametypes and\nkeep them for future use."/>
|
||||
</strings>
|
||||
<strings name="main_menu/multiplayer_type_select/multiplayer_options">
|
||||
@@ -384,6 +385,7 @@
|
||||
<string text="DIRECT LINK"/>
|
||||
<string text="EDIT GAMETYPES"/>
|
||||
<string text="SERVER BROWSER"/>
|
||||
<string text="CO-OP CAMPAIGN"/>
|
||||
</strings>
|
||||
<strings name="main_menu/multiplayer_type_select/server_settings/cap_server_settings_options">
|
||||
<string text="Enter a name for your server."/>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"ce/main_menu.gametype_select.xml",
|
||||
"ce/main_menu.multiplayer_type_select.connected.4way_profile_select.xml",
|
||||
"ce/main_menu.multiplayer_type_select.connected.xml",
|
||||
"ce/main_menu.multiplayer_type_select.coop.xml",
|
||||
"ce/main_menu.multiplayer_type_select.direct_ip.xml",
|
||||
"ce/main_menu.multiplayer_type_select.join_game.filters.xml",
|
||||
"ce/main_menu.multiplayer_type_select.join_game.port.xml",
|
||||
@@ -50,6 +51,7 @@
|
||||
"ce/shell.xml",
|
||||
"ce/strings.xml",
|
||||
"ce/port/blank__0.png",
|
||||
"ce/port/main_menu/multiplayer_type_select/coop/header_player_2.png",
|
||||
"ce/port/main_menu/multiplayer_type_select/join_game/header_direct_link.png",
|
||||
"ce/port/main_menu/multiplayer_type_select/join_game/header_server_browser.png",
|
||||
"ce/port/main_menu/multiplayer_type_select/lobby/header_lobby.png",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="64" viewBox="0 0 512 64">
|
||||
<defs>
|
||||
<filter id="soft" filterUnits="userSpaceOnUse" x="-60" y="-60" width="632" height="184">
|
||||
<feGaussianBlur stdDeviation="14.23 13.06"/>
|
||||
</filter>
|
||||
<mask id="cut" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="64">
|
||||
<rect width="512" height="59" fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g mask="url(#cut)">
|
||||
<rect x="29.08" y="28.05" width="323.93" height="40.95" fill="#021931" fill-opacity="0.893" filter="url(#soft)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 563 B |
+12
-3
@@ -95,8 +95,10 @@ The keyboard and the mouse are a control scheme of their own for the player
|
||||
of controller 1: each action has up to two keys or mouse buttons, which
|
||||
Settings > Controls Setup (or `[controls]` in `config.toml`) changes. The
|
||||
game adds the input of the first gamepad to controller 1. The other
|
||||
gamepads operate controllers 2 to 4. The profile's button layout (Settings >
|
||||
Gamepads) is the gamepads' only.
|
||||
gamepads operate controllers 2 to 4. In co-op with only one gamepad, that
|
||||
gamepad is controller 2 (player 2) and the keyboard and mouse stay
|
||||
controller 1. The profile's button layout (Settings > Gamepads) is the
|
||||
gamepads' only.
|
||||
|
||||
| Action | Keys and buttons (default) |
|
||||
| --- | --- |
|
||||
@@ -157,6 +159,13 @@ change. The screens open, close and move between each other as the PC
|
||||
version's. `display.menus = "xbox"` gives the Xbox's menus, which start every
|
||||
kind of game.
|
||||
|
||||
Multiplayer > CO-OP CAMPAIGN is the Xbox's cooperative play, which the PC
|
||||
version does not have: two players on this computer play the campaign in
|
||||
split screen. Player 1 is the player who chose it, on the current profile.
|
||||
Player 2 then chooses a profile with their own controller (a gamepad), and
|
||||
New Game's levels are those either profile has reached. A co-op game does
|
||||
not continue a saved game of one player.
|
||||
|
||||
In a multiplayer game, the pause menu (escape) has SETTINGS, which opens
|
||||
the profile's settings while the game goes on, and for the host END GAME.
|
||||
END GAME ends the game as its time limit does: the players stay, and after
|
||||
@@ -671,7 +680,7 @@ Other changes:
|
||||
| `networking/`, `game/`, `interface/`, `bungie_net/network/` and the pools of objects, effects and sounds | The system link limits and the memory for them. |
|
||||
| `game/`, `objects/`, `units/`, `networking/` | The distributed netcode. Refer to `NETCODE.md`. |
|
||||
| `cache/cache_files.c` | When a map's tags load and unload, the port finds the bitmaps that the high-res HUD replaces (`game/hud_hires_tags.c`), and adds the tags of the menus to the menus' map (`game/menu_tags.c`). |
|
||||
| `interface/ui_widget.c`, `interface/ui_widget_event_handler_functions.c`, `interface/ui_widget_game_data_input_functions.c` | The main menu is the PC version's from `port/assets/menus` (`display.menus`); the menus' widgets can call the port's functions (`game/menu_functions.c`) and send the PC version's custom activation event; the widgets' memory is 256 KB, not 16 KB. |
|
||||
| `interface/ui_widget.c`, `interface/ui_widget_event_handler_functions.c`, `interface/ui_widget_game_data_input_functions.c` | The main menu is the PC version's from `port/assets/menus` (`display.menus`); the menus' widgets can call the port's functions (`game/menu_functions.c`) and send the PC version's custom activation event; the widgets' memory is 256 KB, not 16 KB; the main menu and Multiplayer clear co-op's controllers, so that a gamepad going back to controller 1 is not a controller unplugged. |
|
||||
| `input/input_abstraction.c`, `game/player_control.c`, `game/players.c`, `game/player_queues_new.c`, `units/units.h` | The keyboard and mouse's actions (`src/xinput_sdl.c`, `include/halo_keyboard.h`) join controller 1's game controls; their reload key reloads on its own, and their action key only acts (a control flag of the port's, sent with the player's action, stops the reload the controller's X falls back to). |
|
||||
| `sound/sound_manager.c`, `interface/hud.c`, `game/game_engine.c` | The music's and the other sounds' volumes; the HUD's and the scoreboard's settings are read again when Settings changes them. |
|
||||
| `interface/hud.c` | In multiplayer, players' names are drawn above their heads (`display.player_names`, `display.player_name_scale`). |
|
||||
|
||||
@@ -51,6 +51,11 @@ the one last used, else the first) and the game's saved game in it:
|
||||
The Xbox's functions of those names take the Xbox's widgets (a spinner of
|
||||
levels, the difficulty list itself), so ours run instead (menu_tags.c).
|
||||
|
||||
Co-op (the Xbox's Cooperative Play, Multiplayer's CO-OP CAMPAIGN): "port coop
|
||||
begin" makes two players, player 1 on its profile; "port coop player 2"
|
||||
gives player 2 the profile chosen and the controller that chose it; then the
|
||||
campaign's New Game and difficulty start the game for both.
|
||||
|
||||
The rest are the PC version's, which its menus (port/assets/menus/ce)
|
||||
name and the Xbox's has not: they do nothing yet, and succeed, so that what
|
||||
their handlers open opens.
|
||||
@@ -660,7 +665,8 @@ static void rows_update(struct widget_instance *list, short count, void (*row_te
|
||||
}
|
||||
|
||||
/* player 1's profile, read again (the active one, else the one last used,
|
||||
else the first), on the controller; FALSE if there is none */
|
||||
else the first), on the controller (in co-op, on the one that chose co-op:
|
||||
coop_begin); FALSE if there is none */
|
||||
static boolean campaign_profile(short controller, struct player_profile *profile)
|
||||
{
|
||||
long profile_index = player_ui_get_active_player_profile_index(0);
|
||||
@@ -679,39 +685,52 @@ static boolean campaign_profile(short controller, struct player_profile *profile
|
||||
}
|
||||
}
|
||||
player_ui_set_active_player_profile(0, profile_index, profile);
|
||||
player_ui_set_single_player_local_player_controller(0, controller);
|
||||
if (player_spawn_count < 2)
|
||||
player_ui_set_single_player_local_player_controller(0, controller);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* the levels the profile has reached (as the Xbox's list has them: those
|
||||
it has played, the one after the last it finished, the first) and finished,
|
||||
and its saved game's */
|
||||
and its saved game's; in co-op, those either player's has reached and
|
||||
finished, and no saved game (a game of one player's does not go on with
|
||||
two: game_state.c's game_state_header_valid) */
|
||||
static void campaign_levels_read(struct player_profile const *profile)
|
||||
{
|
||||
char const *map_name;
|
||||
short highest_level, highest_difficulty, difficulty, level;
|
||||
struct player_profile player2;
|
||||
short highest_level, highest_difficulty, difficulty, level, player;
|
||||
|
||||
player_profile_get_highest_completed_solo_level((struct player_profile *)profile, &highest_level,
|
||||
&highest_difficulty);
|
||||
for (level = 0; level < NUMBER_OF_SINGLE_PLAYER_LEVELS; level++)
|
||||
memset(&campaign.levels[level], 0, sizeof(campaign.levels[level]));
|
||||
if (player_spawn_count >= 2)
|
||||
player_ui_get_active_player_profile(1, &player2);
|
||||
for (player = 0; player < (player_spawn_count >= 2 ? 2 : 1); player++)
|
||||
{
|
||||
byte flags = profile->single_player_map_flags[level];
|
||||
short marker;
|
||||
struct player_profile const *reader = player == 0 ? profile : &player2;
|
||||
|
||||
campaign.levels[level].available = flags || level == highest_level + 1 || level == 0;
|
||||
for (marker = 0; marker < 3; marker++)
|
||||
campaign.levels[level].finished[marker] = (flags >> (marker + 1)) & 1;
|
||||
player_profile_get_highest_completed_solo_level((struct player_profile *)reader, &highest_level,
|
||||
&highest_difficulty);
|
||||
for (level = 0; level < NUMBER_OF_SINGLE_PLAYER_LEVELS; level++)
|
||||
{
|
||||
byte flags = reader->single_player_map_flags[level];
|
||||
short marker;
|
||||
|
||||
campaign.levels[level].available |= flags || level == highest_level + 1 || level == 0;
|
||||
for (marker = 0; marker < 3; marker++)
|
||||
campaign.levels[level].finished[marker] |= (flags >> (marker + 1)) & 1;
|
||||
}
|
||||
}
|
||||
if (!ui_widget_port_saved_game(&map_name, &campaign.saved_level, &difficulty))
|
||||
if (player_spawn_count >= 2 || !ui_widget_port_saved_game(&map_name, &campaign.saved_level, &difficulty))
|
||||
campaign.saved_level = NONE;
|
||||
(void)map_name;
|
||||
}
|
||||
|
||||
/* plays the map, at the difficulty (a saved game in it goes on: main.c's
|
||||
main_new_map, if its difficulty is this one) */
|
||||
static void campaign_start(char const *map_name, short difficulty, short controller)
|
||||
{
|
||||
player_ui_set_single_player_local_player_controller(0, controller);
|
||||
if (player_spawn_count < 2)
|
||||
player_ui_set_single_player_local_player_controller(0, controller);
|
||||
main_set_map_name(map_name);
|
||||
main_set_difficulty(difficulty);
|
||||
game_connection_set(0);
|
||||
@@ -1397,6 +1416,82 @@ static boolean profile_delete(void)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ---------- Co-op: the campaign for two players on this machine, in split
|
||||
screen (the Xbox's Cooperative Play, which the PC version has not):
|
||||
Multiplayer's CO-OP CAMPAIGN ("port coop begin"), player 2's profile, chosen
|
||||
with player 2's controller ("port coop player 2"), then New Game's levels
|
||||
(those either has reached) and difficulty. The main menu and Multiplayer
|
||||
go back to one player (main_menu_initialize, multiplayer_type_menu_initialize) */
|
||||
|
||||
/* (xinput_sdl.c) whether two players play on this machine: then one gamepad
|
||||
is player 2's, not the keyboard's player 1's */
|
||||
unsigned char pc_menu_coop_players(void)
|
||||
{
|
||||
return player_spawn_count >= 2;
|
||||
}
|
||||
|
||||
/* "port coop begin": two players, player 1 on its profile (campaign_profile)
|
||||
and the controller that chose co-op */
|
||||
static boolean coop_begin(short controller)
|
||||
{
|
||||
struct player_profile profile;
|
||||
|
||||
player_spawn_count = 1;
|
||||
player_ui_reset_single_player_local_player_controllers();
|
||||
if (!campaign_profile(controller, &profile))
|
||||
return campaign_fail();
|
||||
player_spawn_count = 2;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* "port coop player 2 list initialize": on player 2's profile, else the first
|
||||
that is not player 1's */
|
||||
static boolean coop_player2_list_initialize(struct widget_instance *list)
|
||||
{
|
||||
long player1 = player_ui_get_active_player_profile_index(0);
|
||||
long player2 = player_ui_get_active_player_profile_index(1);
|
||||
short index;
|
||||
|
||||
profile_list_read(TRUE);
|
||||
profile_list.chosen = 0;
|
||||
for (index = profile_list.count - 1; index >= 0; index--)
|
||||
{
|
||||
if (profile_list.indices[index] != player1)
|
||||
profile_list.chosen = index;
|
||||
}
|
||||
for (index = 0; index < profile_list.count && player2 != NONE; index++)
|
||||
{
|
||||
if (profile_list.indices[index] == player2)
|
||||
profile_list.chosen = index;
|
||||
}
|
||||
profile_list.first = (short)PIN(profile_list.chosen - PROFILE_ROWS / 2, 0,
|
||||
MAX(0, profile_list.count + 1 - PROFILE_ROWS));
|
||||
focus_row(list, (short)(profile_list.chosen - profile_list.first));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* "port coop player 2": the profile chosen made player 2's, on the controller
|
||||
that chose it, which must not be player 1's */
|
||||
static boolean coop_player2_choose(short controller)
|
||||
{
|
||||
struct player_profile profile;
|
||||
|
||||
if (player_spawn_count < 2 || profile_list.chosen >= profile_list.count ||
|
||||
!player_profile_get(profile_list.indices[profile_list.chosen], &profile))
|
||||
{
|
||||
return campaign_fail();
|
||||
}
|
||||
if (controller == player_ui_get_single_player_local_player_controller(0))
|
||||
{
|
||||
display_error_text_deferred(L"Player 2 chooses their\r\nprofile with their own\r\ncontroller.",
|
||||
NONE);
|
||||
return campaign_fail();
|
||||
}
|
||||
player_ui_set_single_player_local_player_controller(1, controller);
|
||||
player_ui_set_active_player_profile(1, profile_list.indices[profile_list.chosen], &profile);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ---------- Controls Setup: the keyboard and mouse's controls
|
||||
(port/linux/include/halo_keyboard.h), shown a group at a time, two
|
||||
bindings each */
|
||||
@@ -4022,6 +4117,18 @@ boolean pc_menu_event_function_invoke(
|
||||
{
|
||||
return profile_choose(controller);
|
||||
}
|
||||
else if (!strcmp(name, "port coop begin"))
|
||||
{
|
||||
return coop_begin(controller);
|
||||
}
|
||||
else if (!strcmp(name, "port coop player 2 list initialize"))
|
||||
{
|
||||
return coop_player2_list_initialize(widget);
|
||||
}
|
||||
else if (!strcmp(name, "port coop player 2"))
|
||||
{
|
||||
return coop_player2_choose(controller);
|
||||
}
|
||||
else if (!strcmp(name, "request del player profile"))
|
||||
{
|
||||
return profile_delete_request();
|
||||
|
||||
@@ -341,6 +341,7 @@ static char const *const port_function_names[] =
|
||||
"port lobby preview join",
|
||||
"port setup edit",
|
||||
"port pause end game",
|
||||
"port coop begin", "port coop player 2 list initialize", "port coop player 2",
|
||||
/* (the gametype editor's: the Xbox's walk their rows by place, which the
|
||||
PC version's screens changed) */
|
||||
"mp profile begin editing", "mp profile save changes", "request del playlist profile", "final del playlist profile",
|
||||
|
||||
@@ -54,6 +54,9 @@ drive the controller.
|
||||
|
||||
/* main/console.c */
|
||||
extern unsigned char console_is_active(void);
|
||||
/* port/linux/game/menu_functions.c: two players on this machine (the
|
||||
campaign's co-op) */
|
||||
extern unsigned char pc_menu_coop_players(void);
|
||||
|
||||
/* ---------- device tables */
|
||||
|
||||
@@ -634,6 +637,16 @@ static int sdl_gamepads(SDL_Gamepad *gamepads[PORT_COUNT])
|
||||
return found;
|
||||
}
|
||||
|
||||
/* the gamepad of a port: the first shares port 0 with the keyboard, but for
|
||||
two players with one gamepad (co-op), port 1 has it (the keyboard's player
|
||||
is 1, the gamepad's 2) */
|
||||
static SDL_Gamepad *port_gamepad(SDL_Gamepad *gamepads[PORT_COUNT], int count, int port)
|
||||
{
|
||||
if (count == 1 && pc_menu_coop_players())
|
||||
return port == 1 ? gamepads[0] : NULL;
|
||||
return port < count ? gamepads[port] : NULL;
|
||||
}
|
||||
|
||||
static SHORT stick(Sint16 value, BOOL flip)
|
||||
{
|
||||
int result = flip ? -(int)value - 1 : value;
|
||||
@@ -717,9 +730,12 @@ static DWORD connected_gamepads(void)
|
||||
DWORD mask = XDEVICE_PORT0_MASK;
|
||||
int port;
|
||||
|
||||
/* the first pad shares port 0 with the keyboard */
|
||||
for (port = 1; port < count; port++)
|
||||
mask |= 1UL << port;
|
||||
/* the first pad shares port 0 with the keyboard (port_gamepad) */
|
||||
for (port = 1; port < PORT_COUNT; port++)
|
||||
{
|
||||
if (port_gamepad(gamepads, count, port))
|
||||
mask |= 1UL << port;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
@@ -813,7 +829,7 @@ DWORD WINAPI XInputGetState(HANDLE device, PXINPUT_STATE state)
|
||||
else
|
||||
keyboard_controls(&input, &state->Gamepad);
|
||||
}
|
||||
if (count > 0)
|
||||
if (port_gamepad(gamepads, count, 0))
|
||||
sdl_gamepad_state(gamepads[0], &state->Gamepad);
|
||||
test_input_gamepad(&state->Gamepad);
|
||||
if (abs(state->Gamepad.sThumbRX) > STICK_AIMING_DEFLECTION ||
|
||||
@@ -824,9 +840,9 @@ DWORD WINAPI XInputGetState(HANDLE device, PXINPUT_STATE state)
|
||||
pthread_mutex_unlock(&mouse_lock);
|
||||
}
|
||||
}
|
||||
else if (port < count)
|
||||
else if (port_gamepad(gamepads, count, port))
|
||||
{
|
||||
sdl_gamepad_state(gamepads[port], &state->Gamepad);
|
||||
sdl_gamepad_state(port_gamepad(gamepads, count, port), &state->Gamepad);
|
||||
}
|
||||
|
||||
if (memcmp(&state->Gamepad, &controllers[port].previous, sizeof(state->Gamepad)))
|
||||
@@ -850,11 +866,11 @@ DWORD WINAPI XInputSetState(HANDLE device, PXINPUT_FEEDBACK feedback)
|
||||
if (port < 0)
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
count = sdl_gamepads(gamepads);
|
||||
if (port < count)
|
||||
if (port_gamepad(gamepads, count, port))
|
||||
{
|
||||
/* the game refreshes the motors every frame; rumble a little longer
|
||||
than that so they do not stutter */
|
||||
SDL_RumbleGamepad(gamepads[port], feedback->Rumble.wLeftMotorSpeed,
|
||||
SDL_RumbleGamepad(port_gamepad(gamepads, count, port), feedback->Rumble.wLeftMotorSpeed,
|
||||
feedback->Rumble.wRightMotorSpeed, 100);
|
||||
}
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
@@ -2029,6 +2029,10 @@ static boolean multiplayer_type_menu_initialize(
|
||||
boolean *widget_deleted)
|
||||
{
|
||||
player_spawn_count = 1;
|
||||
/* port: and no co-op player's controller, which one player with one
|
||||
gamepad gives back to the keyboard's (xinput_sdl.c's port_gamepad): its
|
||||
going is not a controller unplugged (input_abstraction.c) */
|
||||
player_ui_reset_single_player_local_player_controllers();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2315,6 +2319,8 @@ static boolean main_menu_initialize(
|
||||
dispose_global_network_game_server();
|
||||
network_game_accept_remote_connections(FALSE);
|
||||
player_spawn_count = 1;
|
||||
/* port: as multiplayer_type_menu_initialize */
|
||||
player_ui_reset_single_player_local_player_controllers();
|
||||
player_ui_end_editing_profile();
|
||||
if (!ui_main_menu_music_active())
|
||||
ui_start_main_menu_music();
|
||||
|
||||
+6
-1
@@ -70,9 +70,14 @@ LEFT_OUT = {
|
||||
"ui\\shell\\main_menu\\multiplayer_type_select\\checking_updates_screen_join",
|
||||
}
|
||||
# children moved: (parent, child) to (x, y); the main menu's Quit up into
|
||||
# Credits' place
|
||||
# Credits' place; Multiplayer's Edit Gametypes and its line down a row, for
|
||||
# Co-op (port_settings.WIDGET_PATCHES)
|
||||
CHILD_OFFSETS = {
|
||||
("ui\\shell\\main_menu\\main_menu_select_list", "ui\\shell\\main_menu\\main_menu_item_quit_game"): (192, 391),
|
||||
("ui\\shell\\main_menu\\multiplayer_type_select\\multiplayer_type_select_list",
|
||||
"ui\\shell\\main_menu\\multiplayer_type_select\\multiplayer_type_gametypes_item"): (0, 375),
|
||||
("ui\\shell\\main_menu\\multiplayer_type_select\\multiplayer_type_select_screen",
|
||||
"ui\\shell\\main_menu\\blueline"): (64, 358),
|
||||
# the browser's rows up under the column titles, into the place of the
|
||||
# scroll up button (hidden: the list does not scroll)
|
||||
**{("ui\\shell\\main_menu\\multiplayer_type_select\\join_game\\join_game_items_list",
|
||||
|
||||
+63
-1
@@ -350,13 +350,14 @@ def LOADOUT_HELPS(slot: str) -> list:
|
||||
|
||||
STRING_OVERRIDES.update({
|
||||
f"{MT}/multiplayer_options": ["JOIN GAME", "CREATE GAME", "INTERNET", "LAN", "DIRECT LINK", "EDIT GAMETYPES",
|
||||
"SERVER BROWSER"],
|
||||
"SERVER BROWSER", "CO-OP CAMPAIGN"],
|
||||
f"{MT}/multiplayer_option_descriptions": [
|
||||
"Browse the games on the\\nInternet.",
|
||||
"Join a multiplayer game on\\nyour LAN.",
|
||||
"Join a game by its invite link,\\nor one a Discord invite\\nreached.",
|
||||
"Host a game on the Internet:\\nplayers join by its invite\\nlink or Discord.",
|
||||
"Host a game on your LAN\\nonly.",
|
||||
"Play the campaign with a\\nfriend in split screen:\\nplayer 2 on a gamepad.",
|
||||
"Set all the attributes for your \\nmultiplayer gametypes and\\nkeep them for future use.",
|
||||
],
|
||||
"main_menu/gametype_select/var_gametype_banks": ["STANDARD", "CUSTOM"],
|
||||
@@ -418,6 +419,10 @@ WIDGET_PATCHES = {
|
||||
f'<on event="start" run="mp type set mode" open="{MT}/join_game/join_game_screen"/>',
|
||||
'<on event="left_mouse" run="mouse emit accept event"/>',
|
||||
]},
|
||||
# (Co-op, after Create Game's: _coop)
|
||||
f"{MT}/multiplayer_type_select_list": {"insert_before": {
|
||||
f"{MT}/multiplayer_type_gametypes_item": [f'<child widget="{MT}/multiplayer_type_coop_item" y="309"/>'],
|
||||
}},
|
||||
f"{MT}/join_game/header_join_game": {"children": [
|
||||
f'<child widget="{MT}/join_game/header_server_browser"/>',
|
||||
f'<child widget="{MT}/join_game/header_direct_link"/>',
|
||||
@@ -455,6 +460,7 @@ TITLES = {
|
||||
f"{MT}/join_game/header_server_browser": "SERVER BROWSER",
|
||||
f"{MT}/join_game/header_direct_link": "DIRECT LINK",
|
||||
f"{MT}/lobby/header_lobby": "GAME LOBBY",
|
||||
f"{MT}/coop/header_player_2": "PLAYER 2 PROFILE",
|
||||
}
|
||||
|
||||
|
||||
@@ -760,6 +766,61 @@ def _lobby() -> list:
|
||||
return lines
|
||||
|
||||
|
||||
def _coop() -> list:
|
||||
"""Co-op, the campaign for two players on this machine in split screen
|
||||
(the Xbox's Cooperative Play): Multiplayer's CO-OP CAMPAIGN, then player
|
||||
2's profile, chosen with player 2's controller (its rows take any
|
||||
controller's presses, as the shared rows do only controller 1's), then
|
||||
New Game's levels and difficulty (menu_functions.c's coop_begin)"""
|
||||
base = f"{MT}/coop"
|
||||
lines = _widget(f"{MT}/multiplayer_type_coop_item",
|
||||
[("type", "text"), ("left", 51), ("width", 232), ("height", 32), ("bitmap", "bitmaps/list_item_bkd"),
|
||||
("string_list", f"{MT}/multiplayer_options"), ("string_index", 7), ("font", "ui\\large_ui"),
|
||||
("color", "#FF2896FF"), ("text_x", 13), ("text_y", 5)],
|
||||
[f'<on event="a" run="port coop begin" open="{base}/player_2_profile_screen" branch="true"/>',
|
||||
f'<on event="start" run="port coop begin" open="{base}/player_2_profile_screen" branch="true"/>',
|
||||
'<on event="left_mouse" run="mouse emit accept event"/>'])
|
||||
lines += _widget(f"{base}/player_2_profile_screen", [("width", 640), ("height", 480),
|
||||
("flags", "pass_unhandled_to_focused_child"),
|
||||
("bitmap", "bitmaps/gradient")],
|
||||
['<child widget="main_menu/new_select/sel_list_desc_bkd"/>',
|
||||
f'<child widget="{base}/player_2_profile_list"/>',
|
||||
f'<child widget="{base}/header_player_2"/>',
|
||||
f'<child widget="{base}/player_2_help" x="20" y="416"/>'])
|
||||
lines += _header(f"{base}/header_player_2", f"{base}/header_player_2")
|
||||
rows = [f'<child widget="{base}/list_item_{index}" x="20" y="{73 + 30 * index}"/>' for index in range(11)]
|
||||
lines += _widget(f"{base}/player_2_profile_list",
|
||||
[("type", "column_list"), ("width", 640), ("height", 480),
|
||||
("flags", "pass_unhandled_to_focused_child up_down_tabs_children"),
|
||||
("description", "main_menu/profile_manager/player_profile_extended_desc")],
|
||||
['<data input="3wide player profile list update"/>',
|
||||
'<on event="created" run="port coop player 2 list initialize"/>',
|
||||
'<on event="deleted" run="player profile list dispose"/>',
|
||||
'<on event="custom_activation" run="port coop player 2" '
|
||||
'open="main_menu/solo_level_select/solo_level_select_screen" branch="true"/>',
|
||||
*rows, f'<child widget="{base}/player_2_button_bar" y="414"/>'])
|
||||
for index in range(11):
|
||||
lines += _widget(f"{base}/list_item_{index}",
|
||||
[("width", 390), ("height", 28),
|
||||
("bitmap", "bitmaps/sel_list_item_bkd_top" if index == 0 else "bitmaps/sel_list_item_bkd"),
|
||||
("font", "ui\\large_ui"), ("color", "#FF2896FF"), ("align", "center"), ("text_y", 3)],
|
||||
['<on event="a" run="single prev cl item activated"/>',
|
||||
'<on event="start" run="single prev cl item activated"/>',
|
||||
'<on event="left_mouse" run="mouse emit accept event"/>',
|
||||
'<child widget="main_menu/new_select/list_item_text" x="25"/>',
|
||||
'<child widget="main_menu/new_select/list_item_arrows"/>'])
|
||||
lines += _widget(f"{base}/player_2_help", [("type", "text"), ("width", 350), ("height", 24),
|
||||
("text", "Player 2: choose with your controller."),
|
||||
("font", "ui\\small_ui"), ("color", "#FF2896FF"), ("text_y", 5),
|
||||
("text_flags", "no_focus_test")], [])
|
||||
lines += _widget(f"{base}/player_2_button_bar", [("type", "column_list"), ("width", 640), ("height", 28),
|
||||
("flags", "pass_unhandled_to_focused_child left_right_tabs_items")],
|
||||
['<data input="common button bar update"/>',
|
||||
'<child widget="main_menu/profile_manager/profile_manager_button_ok" x="380" y="1"/>',
|
||||
'<child widget="common_button_back" x="510" y="1"/>'])
|
||||
return lines
|
||||
|
||||
|
||||
def _item_options_extras() -> list:
|
||||
"""Item Options' rows of the port's: the map's weapons (YES or NO), and
|
||||
the loadout, CATEGORY (the weapon set) or CUSTOM (each player's primary
|
||||
@@ -801,6 +862,7 @@ def multiplayer_files() -> dict:
|
||||
f"{MT}/join_game".replace("/", ".") + ".port.xml": head + _join_game_extras() + ["</menus>", ""],
|
||||
f"{MT}/server_settings".replace("/", ".") + ".xml": head + _server_settings() + ["</menus>", ""],
|
||||
f"{MT}/lobby".replace("/", ".") + ".xml": head + _lobby() + ["</menus>", ""],
|
||||
f"{MT}/coop".replace("/", ".") + ".xml": head + _coop() + ["</menus>", ""],
|
||||
"main_menu/settings_select/multiplayer_setup/item_options_edit".replace("/", ".") + ".port.xml": head + _item_options_extras() + ["</menus>", ""],
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user