From f0804aa587b7ffbdcf9793ceae9b558706b3c924 Mon Sep 17 00:00:00 2001 From: MrBruh <4706866+cybersecurity@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:08:26 +1300 Subject: [PATCH] Extra enemies: per player or static multiplier --- ...ultiplayer_type_select.server_settings.xml | 32 +++++++- port/linux/NETCODE.md | 17 +++-- port/linux/README.md | 4 +- port/linux/game/coop_enemies.c | 73 ++++++++++++++---- port/linux/game/menu_functions.c | 47 +++++++++--- port/linux/src/port_config.c | 20 ++++- tools/port_settings.py | 75 ++++++++++++------- 7 files changed, 203 insertions(+), 65 deletions(-) diff --git a/port/assets/menus/ce/main_menu.multiplayer_type_select.server_settings.xml b/port/assets/menus/ce/main_menu.multiplayer_type_select.server_settings.xml index e76e1eea..456615fb 100644 --- a/port/assets/menus/ce/main_menu.multiplayer_type_select.server_settings.xml +++ b/port/assets/menus/ce/main_menu.multiplayer_type_select.server_settings.xml @@ -20,6 +20,8 @@ + + @@ -93,7 +95,29 @@ - + + + + + + + + + + + + + + + + + + + + + + + @@ -185,6 +209,8 @@ + + @@ -203,6 +229,10 @@ + + + + diff --git a/port/linux/NETCODE.md b/port/linux/NETCODE.md index a5c083f9..806e3c8d 100644 --- a/port/linux/NETCODE.md +++ b/port/linux/NETCODE.md @@ -54,14 +54,15 @@ with ideas from VALORANT's netcode articles, keeping the 30 Hz tick: threads' wake times along with it. The object, device and name syncs bring the clients up to date. - The host's EXTRA ENEMIES (`coop_enemies.c`, `network.coop_enemies`) - give each squad of enemies a level places that percentage of itself more - for each player past the first, in rings about its starting locations - where the ground is open, and never past the actors a level needs for its - own (the actor pool, `halo_port_capacity.h`, holds 1024). Riders a - dropship has no seats for are kept, and placed beside its riders once - they get out. Only the host runs the AI, so the clients see them as the - host's other actors. + The host's EXTRA ENEMIES (`coop_enemies.c`, `network.coop_enemies_mode`) + give each squad of enemies a level places more of itself: PER PLAYER, a + percentage of itself for each player past the first; STATIC MULTIPLIER, + that many times itself for any number of players. They stand in rings + about its starting locations where the ground is open, and never take the + actors a level needs for its own (the actor pool, `halo_port_capacity.h`, + holds 1024). Riders a dropship has no seats for are kept, and placed + beside its riders once they get out. Only the host runs the AI, so the + clients see them as the host's other actors. - **Host authoritative.** The host alone decides damage, deaths, spawns, pickups, scores and the game's objects; clients do not decide them but apply what the host sends. diff --git a/port/linux/README.md b/port/linux/README.md index ebbedd90..bfc82b4b 100644 --- a/port/linux/README.md +++ b/port/linux/README.md @@ -261,7 +261,9 @@ the setting for one start of the game. It has priority over the file. | `network.public_lobby` | `true` | `HALO_NET_PUBLIC_LOBBY` | `true`: the server browser. Public games are listed, and Join Game > Server Browser shows them. `false`: no games are listed or shown. Refer to "Server browser". | | `network.host_public` | `true` | `HALO_NET_HOST_PUBLIC` | `true`: a new game of Create Game > Internet starts as PUBLIC. `false`: it starts as PRIVATE. LISTING in Server Setup changes it for each game. Refer to "Server browser". | | `network.coop_friendly_fire` | `"on"` | `HALO_NET_COOP_FRIENDLY_FIRE` | Whether the players of an online co-op game hurt each other: `"off"`, `"on"`, `"shields_only"` or `"explosives_only"`. FRIENDLY FIRE in co-op's Server Setup writes its choice here. Their AI allies they always can, as in the campaign. | -| `network.coop_enemies` | `50` | `HALO_NET_COOP_ENEMIES` | Online co-op's extra enemies, a percentage from `0` to `200`: for each player past the first, each squad of enemies that a level places gets this much of itself more (`100`: as many again, so four players meet four times the squad). The extra enemies stand around the squad's places, and those that a dropship has no seats for drop out of it after its passengers. EXTRA ENEMIES in co-op's Server Setup writes its choice here. | +| `network.coop_enemies_mode` | `"per_player"` | `HALO_NET_COOP_ENEMIES_MODE` | Online co-op's extra enemies: `"none"`; `"per_player"`, each squad of enemies that a level places grows by `network.coop_enemies` for each player past the first; or `"multiplier"`, each squad is `network.coop_enemies_multiplier` times as large, for any number of players. The extra enemies stand around the squad's places, and those that a dropship has no seats for drop out of it after its passengers. EXTRA ENEMIES in co-op's Server Setup writes its choice here. | +| `network.coop_enemies` | `50` | `HALO_NET_COOP_ENEMIES` | The extra enemies per player, a percentage from `25` to `200`: for each player past the first, each squad of enemies gets this much of itself more (`100`: as many again, so four players meet four times the squad). PER PLAYER in co-op's Server Setup writes its choice here. | +| `network.coop_enemies_multiplier` | `2` | `HALO_NET_COOP_ENEMIES_MULTIPLIER` | The static multiplier of the enemies, `2` to `32`: each squad of enemies is this many times as large. MULTIPLIER in co-op's Server Setup writes its choice here. | | `network.coop_public` | `false` | `HALO_NET_COOP_PUBLIC` | `true`: an online co-op game (Create Game > Internet, a SINGLEPLAYER map) starts as PUBLIC. `false`: it starts as PRIVATE. LISTING in co-op's Server Setup writes its choice here. Refer to "Server browser". | | `network.brokers_file` | `"brokers.txt"` | `HALO_NET_BROKERS_FILE` | The file of the public MQTT brokers that let the machines of an invite find each other, and that carry the listings of the server browser: next to `config.toml`, unless a full path. One `host:port` on each line, up to 4; `#` starts a comment. | | `network.stun_servers` | Google and Cloudflare | `HALO_NET_STUN` | The public STUN servers (`host:port`, with commas between them) that give the internet address of a machine. | diff --git a/port/linux/game/coop_enemies.c b/port/linux/game/coop_enemies.c index 4feb53ee..ade5c80f 100644 --- a/port/linux/game/coop_enemies.c +++ b/port/linux/game/coop_enemies.c @@ -2,12 +2,14 @@ COOP_ENEMIES.C Network co-op's extra enemies: Server Setup's EXTRA ENEMIES -(network.coop_enemies, a percentage). For each player past the first, each -squad of the players' enemies that a level places (encounters.c's -encounter_create) gets that much of its count more: at 100%, two players -meet twice the squad, four players four times it. The extra enemies stand -in rings about the squad's starting locations, on open ground, so they do -not start inside each other. +(network.coop_enemies_mode). PER PLAYER (network.coop_enemies, a +percentage): for each player past the first, each squad of the players' +enemies that a level places (encounters.c's encounter_create) gets that +much of its count more; at 100%, two players meet twice the squad, four +players four times it. STATIC MULTIPLIER (network.coop_enemies_multiplier): +each squad is that many times as large, for any number of players. The +extra enemies stand in rings about the squad's starting locations, on +open ground, so they do not start inside each other. A squad a script loads into a dropship (vehicle_load_magic) can be larger than the dropship's seats. The riders left without a seat are erased, and @@ -39,9 +41,11 @@ Only the host runs the AI, so all of this is the host's. #include "network_coop.h" #include +#include /* port_config.c's */ long config_integer(char const *name); +char const *config_string(char const *name); /* ---------- constants */ @@ -50,8 +54,12 @@ enum /* the actors a level keeps for its own placements, which the extra enemies never take: as many as the Xbox's whole pool */ COOP_ENEMIES_LEVEL_ACTORS = 256, - /* EXTRA ENEMIES' greatest (Server Setup's choices: 0 to 200%) */ + /* EXTRA ENEMIES' amounts, as Server Setup has them: 25 to 200% per + player, or 2 to 32 times (none is its NONE) */ + COOP_ENEMIES_MINIMUM_PERCENT = 25, COOP_ENEMIES_MAXIMUM_PERCENT = 200, + COOP_ENEMIES_MINIMUM_MULTIPLIER = 2, + COOP_ENEMIES_MAXIMUM_MULTIPLIER = 32, /* the places tried for an extra enemy, a ring further out each */ SPREAD_ATTEMPTS = 8, SPREAD_PLACES_PER_RING = 6, @@ -71,6 +79,14 @@ looked along, and the ground looked for below it */ /* a kept rider's place beside a rider who got out, each a little apart */ #define RIDER_SPACING 0.5f +/* EXTRA ENEMIES' choices (network.coop_enemies_mode's values) */ +enum +{ + _coop_enemies_none, + _coop_enemies_per_player, + _coop_enemies_multiplier, +}; + /* ---------- structures */ /* a vehicle a script loaded riders into: its seated ones, and whether they @@ -99,8 +115,11 @@ struct coop_kept_rider static struct { - /* EXTRA ENEMIES, as the game began with it */ + /* EXTRA ENEMIES, as the game began with it: its choice, and the amount + of PER PLAYER (a percentage) and of STATIC MULTIPLIER */ + short mode; short percent; + short multiplier; struct coop_riding_vehicle vehicles[MAXIMUM_RIDING_VEHICLES]; short vehicle_count; struct coop_kept_rider riders[MAXIMUM_KEPT_RIDERS]; @@ -115,6 +134,14 @@ static boolean coop_enemies_host( return game_connection() == _game_connection_network_server && network_coop_active(); } +/* whether this game has extra enemies (it is a co-op host's, with some +chosen) */ +static boolean coop_enemies_on( + void) +{ + return coop_enemies.mode != _coop_enemies_none && coop_enemies_host(); +} + /* the players in the game, every machine's: the network game's (a level places its first enemies before the players' units are made, as it loads: game_initialize_for_new_map) */ @@ -283,8 +310,17 @@ static void coop_enemies_rider_place( void coop_enemies_new_game( void) { + char const *mode = config_string("network.coop_enemies_mode"); + csmemset(&coop_enemies, 0, sizeof(coop_enemies)); - coop_enemies.percent = (short)PIN(config_integer("network.coop_enemies"), 0, COOP_ENEMIES_MAXIMUM_PERCENT); + /* (an amount past Server Setup's, as it shows it: its nearest choice's + end) */ + coop_enemies.percent = (short)PIN(config_integer("network.coop_enemies"), COOP_ENEMIES_MINIMUM_PERCENT, + COOP_ENEMIES_MAXIMUM_PERCENT); + coop_enemies.multiplier = (short)PIN(config_integer("network.coop_enemies_multiplier"), + COOP_ENEMIES_MINIMUM_MULTIPLIER, COOP_ENEMIES_MAXIMUM_MULTIPLIER); + coop_enemies.mode = !strcmp(mode, "per_player") ? _coop_enemies_per_player : + !strcmp(mode, "multiplier") ? _coop_enemies_multiplier : _coop_enemies_none; } void coop_enemies_reset( @@ -302,15 +338,20 @@ short coop_enemies_extra_count( long extra, room; short players; - if (count <= 0 || coop_enemies.percent <= 0 || !coop_enemies_host()) + if (count <= 0 || !coop_enemies_on()) return 0; encounter = encounter_get(encounter_index); if (!game_team_is_enemy(_game_team_player, encounter->team_index)) return 0; - players = coop_enemies_player_count(); - if (players <= 1) - return 0; - extra = ((long)count * coop_enemies.percent * (players - 1) + 50) / 100; + if (coop_enemies.mode == _coop_enemies_multiplier) + extra = (long)count * (coop_enemies.multiplier - 1); + else + { + players = coop_enemies_player_count(); + if (players <= 1) + return 0; + extra = ((long)count * coop_enemies.percent * (players - 1) + 50) / 100; + } room = MAXIMUM_ACTORS - COOP_ENEMIES_LEVEL_ACTORS - actor_data->actual_count; return (short)PIN(extra, 0, MAX(room, 0)); } @@ -349,7 +390,7 @@ void coop_enemies_rider_seated( struct coop_riding_vehicle *vehicle; struct unit_datum *unit; - if (coop_enemies.percent <= 0 || !coop_enemies_host()) + if (!coop_enemies_on()) return; vehicle = coop_enemies_riding_vehicle(vehicle_index, TRUE); unit = unit_get(unit_index); @@ -391,7 +432,7 @@ boolean coop_enemies_rider_unseated( struct coop_kept_rider *rider; struct coop_riding_vehicle *vehicle; - if (coop_enemies.percent <= 0 || !coop_enemies_host() || coop_enemies.rider_count >= MAXIMUM_KEPT_RIDERS) + if (!coop_enemies_on() || coop_enemies.rider_count >= MAXIMUM_KEPT_RIDERS) return FALSE; unit = unit_get(unit_index); vehicle = coop_enemies_riding_vehicle(vehicle_index, FALSE); diff --git a/port/linux/game/menu_functions.c b/port/linux/game/menu_functions.c index 63b52d02..7d098faa 100644 --- a/port/linux/game/menu_functions.c +++ b/port/linux/game/menu_functions.c @@ -2576,10 +2576,23 @@ static char const *const server_settings_gametype_rows[] = /* the most players a co-op game hosted starts with (maximum_players') */ #define COOPERATIVE_DEFAULT_PLAYERS 16 -/* Server Setup's help for co-op's FRIENDLY FIRE, by its choice, and its -EXTRA ENEMIES (its help_strings, tools/port_settings.py) */ +/* Server Setup's help for co-op's FRIENDLY FIRE and EXTRA ENEMIES, by +their choices, and EXTRA ENEMIES' PER PLAYER and MULTIPLIER (its +help_strings, tools/port_settings.py) */ #define COOPERATIVE_FRIENDLY_FIRE_HELP 12 #define COOPERATIVE_EXTRA_ENEMIES_HELP 16 +#define COOPERATIVE_ENEMIES_PER_PLAYER_HELP 19 +#define COOPERATIVE_ENEMIES_MULTIPLIER_HELP 20 + +/* co-op's EXTRA ENEMIES' choices (port_settings.COOP_ENEMIES_MODES, in this +order): its amount's row is the choice's */ +enum +{ + _cooperative_enemies_none, + _cooperative_enemies_per_player, + _cooperative_enemies_multiplier, + NUMBER_OF_COOPERATIVE_ENEMIES_MODES +}; /* co-op's FRIENDLY FIRE's choices (network.coop_friendly_fire's values, port_settings.COOP_FRIENDLY_FIRE_VALUES, in this order) */ @@ -2708,14 +2721,30 @@ static void server_settings_update(struct widget_instance *list) if (help && list->focused_child == named(list, "op_friendly_fire", 0)) help->parameters.text_box.string_list_index = (short)(COOPERATIVE_FRIENDLY_FIRE_HELP + choice); } - /* ... and its EXTRA ENEMIES (network.coop_enemies, which the host - reads as the game begins: coop_enemies.c) */ - visible_set(named(list, "op_extra_enemies", 0), cooperative); - if (cooperative && list->parameters.list.extended_description && - list->focused_child == named(list, "op_extra_enemies", 0)) + /* ... and its EXTRA ENEMIES, with the row of the amount of the one + chosen below it (network.coop_enemies_mode and its amounts, which the + host reads as the game begins: coop_enemies.c) */ { - list->parameters.list.extended_description->parameters.text_box.string_list_index = - COOPERATIVE_EXTRA_ENEMIES_HELP; + struct widget_instance *help = list->parameters.list.extended_description; + short mode = _cooperative_enemies_none; + + if ((spinner = named(list, "extra_enemies_spinner", 0)) != NULL) + { + mode = (short)PIN(spinner->parameters.list.selected_index, 0, + NUMBER_OF_COOPERATIVE_ENEMIES_MODES - 1); + } + visible_set(named(list, "op_extra_enemies", 0), cooperative); + visible_set(named(list, "op_enemies_per_player", 0), cooperative && mode == _cooperative_enemies_per_player); + visible_set(named(list, "op_enemies_multiplier", 0), cooperative && mode == _cooperative_enemies_multiplier); + if (cooperative && help) + { + if (list->focused_child == named(list, "op_extra_enemies", 0)) + help->parameters.text_box.string_list_index = (short)(COOPERATIVE_EXTRA_ENEMIES_HELP + mode); + else if (list->focused_child == named(list, "op_enemies_per_player", 0)) + help->parameters.text_box.string_list_index = COOPERATIVE_ENEMIES_PER_PLAYER_HELP; + else if (list->focused_child == named(list, "op_enemies_multiplier", 0)) + help->parameters.text_box.string_list_index = COOPERATIVE_ENEMIES_MULTIPLIER_HELP; + } } } /* LISTING (an internet game's): PUBLIC, listed in everyone's server diff --git a/port/linux/src/port_config.c b/port/linux/src/port_config.c index 0542d5c5..c7554eac 100644 --- a/port/linux/src/port_config.c +++ b/port/linux/src/port_config.c @@ -266,11 +266,23 @@ static const struct config_setting config_settings[] = "\"on\", \"shields_only\" or \"explosives_only\" (Server Setup's FRIENDLY\n" "FIRE in co-op, which writes its choice here). Their AI allies they\n" "always can, as in the campaign." }, + { "network.coop_enemies_mode", _config_string, "\"per_player\"", "HALO_NET_COOP_ENEMIES_MODE", _environment_value, + _platform_all, + "Online co-op's extra enemies: \"none\", \"per_player\" (each squad of\n" + "enemies grows by coop_enemies for each player past the first) or\n" + "\"multiplier\" (each is coop_enemies_multiplier times as large, for any\n" + "number of players). Server Setup's EXTRA ENEMIES in co-op writes its\n" + "choice here." }, { "network.coop_enemies", _config_integer, "50", "HALO_NET_COOP_ENEMIES", _environment_value, _platform_all, - "Online co-op's extra enemies, a percentage: for each player past the\n" - "first, each squad of enemies a level places gets this much of itself\n" - "more (100: as many again; 0 to 200). Server Setup's EXTRA ENEMIES in\n" - "co-op writes its choice here." }, + "Online co-op's extra enemies per player, a percentage: for each player\n" + "past the first, each squad of enemies a level places gets this much of\n" + "itself more (100: as many again; 25 to 200). Server Setup's PER PLAYER\n" + "in co-op writes its choice here." }, + { "network.coop_enemies_multiplier", _config_integer, "2", "HALO_NET_COOP_ENEMIES_MULTIPLIER", _environment_value, + _platform_all, + "Online co-op's static multiplier of its enemies: each squad of enemies\n" + "a level places is this many times as large (2 to 32). Server Setup's\n" + "MULTIPLIER in co-op writes its choice here." }, { "network.brokers_file", _config_string, "\"brokers.txt\"", "HALO_NET_BROKERS_FILE", _environment_value, _platform_all, "The file of the public MQTT brokers through which the machines of an\n" diff --git a/tools/port_settings.py b/tools/port_settings.py index 8a483aa5..c52e7089 100644 --- a/tools/port_settings.py +++ b/tools/port_settings.py @@ -681,29 +681,42 @@ def _server_settings() -> list: ['', '', '']) rows.append((f"{base}/op_friendly_fire", None, 4)) - # ... and its EXTRA ENEMIES (port/linux/game/coop_enemies.c), below it; - # its choice kept in network.coop_enemies - extra += _widget(f"{base}/op_extra_enemies", [("width", 512), ("height", 28), - ("flags", "pass_unhandled_to_focused_child"), - ("bitmap", "bitmaps/option_bkds"), ("color", "#FF2896FF")], - [f'', - f'']) - extra += _widget(f"{base}/extra_enemies_label", [("type", "text"), ("controller", 1), ("width", 300), - ("height", 22), ("string_list", f"{base}/labels"), - ("string_index", 11), ("font", "ui\\large_ui"), - ("color", "#FF2896FF"), ("text_x", 13), ("text_y", 4)], []) - extra += _widget(f"{base}/extra_enemies_spinner", - [("type", "spinner"), ("top", 2), ("width", 206), ("height", 20), - ("flags", "pass_unhandled_to_focused_child left_right_tabs_items"), - ("strings", "|".join(f"{value}%" for value in COOP_ENEMIES_VALUES)), - ("setting", "network.coop_enemies"), - ("values", "|".join(str(value) for value in COOP_ENEMIES_VALUES)), ("font", "ui\\large_ui"), - ("color", "#FF2896FF"), ("align", "center"), ("text_y", 4), - ("header_bitmap", "bitmaps/arrow_sm_left"), ("footer_bitmap", "bitmaps/arrow_sm_right"), - ("header_bounds", "7 -13 19 -7"), ("footer_bounds", "7 208 19 214")], - ['', '', - '']) + # ... and its EXTRA ENEMIES (port/linux/game/coop_enemies.c), below it: + # NONE, PER PLAYER or STATIC MULTIPLIER, and below that the amount of the + # one chosen (its own row, the other hidden: menu_functions.c's + # server_settings_update); each kept in its network.coop_enemies setting + def spinner_row(key, label_index, strings, setting, values): + nonlocal extra + extra += _widget(f"{base}/op_{key}", [("width", 512), ("height", 28), + ("flags", "pass_unhandled_to_focused_child"), + ("bitmap", "bitmaps/option_bkds"), ("color", "#FF2896FF")], + [f'', + f'']) + extra += _widget(f"{base}/{key}_label", [("type", "text"), ("controller", 1), ("width", 300), + ("height", 22), ("string_list", f"{base}/labels"), + ("string_index", label_index), ("font", "ui\\large_ui"), + ("color", "#FF2896FF"), ("text_x", 13), ("text_y", 4)], []) + extra += _widget(f"{base}/{key}_spinner", + [("type", "spinner"), ("top", 2), ("width", 206), ("height", 20), + ("flags", "pass_unhandled_to_focused_child left_right_tabs_items"), + ("strings", "|".join(strings)), ("setting", setting), + ("values", "|".join(str(value) for value in values)), ("font", "ui\\large_ui"), + ("color", "#FF2896FF"), ("align", "center"), ("text_y", 4), + ("header_bitmap", "bitmaps/arrow_sm_left"), ("footer_bitmap", "bitmaps/arrow_sm_right"), + ("header_bounds", "7 -13 19 -7"), ("footer_bounds", "7 208 19 214")], + ['', + '', + '']) + + spinner_row("extra_enemies", 11, ["NONE", "PER PLAYER", "STATIC MULTIPLIER"], "network.coop_enemies_mode", + COOP_ENEMIES_MODES) rows.append((f"{base}/op_extra_enemies", None, 5)) + spinner_row("enemies_per_player", 12, [f"{value}%" for value in COOP_ENEMIES_PERCENTAGES], + "network.coop_enemies", COOP_ENEMIES_PERCENTAGES) + rows.append((f"{base}/op_enemies_per_player", None, 6)) + spinner_row("enemies_multiplier", 13, [f"{value}X" for value in COOP_ENEMIES_MULTIPLIERS], + "network.coop_enemies_multiplier", COOP_ENEMIES_MULTIPLIERS) + rows.append((f"{base}/op_enemies_multiplier", None, 6)) # the gametype's options for this game (the gametype editor's screens, # editing a copy of the gametype chosen: "port setup edit") for index, (key, screen) in enumerate(SETUP_OPTION_SCREENS): @@ -733,7 +746,8 @@ def _server_settings() -> list: '']) extra += _strings(f"{base}/labels", ["GAME NAME:", "MAXIMUM PLAYERS:", "INVITE LINK:", "GAME TYPE:", "PLAYER OPTIONS:", "ITEM OPTIONS:", "VEHICLE OPTIONS:", "INDICATOR OPTIONS:", - "TEAMPLAY OPTIONS:", "LISTING:", "FRIENDLY FIRE:", "EXTRA ENEMIES:"]) + "TEAMPLAY OPTIONS:", "LISTING:", "FRIENDLY FIRE:", "EXTRA ENEMIES:", + "PER PLAYER:", "MULTIPLIER:"]) extra += _strings(f"{base}/help_strings", [ "", "The name the game shows in the lists of games.\\nEnter changes it.", @@ -753,8 +767,13 @@ def _server_settings() -> list: "Players can be hurt by weapons or explosives\\nfired by the other players.", "Damage from the other players will only reduce\\nshields. Health will be unaffected.", "Players can be hurt by damage from explosives\\nfired by the other players.", - # (EXTRA ENEMIES') + # (EXTRA ENEMIES', by its choice: COOP_ENEMIES_MODES' order) + "Enemy squads are as the campaign has them.", + "Enemy squads grow with the players: by the amount\\nbelow for each player past the first.", + "Enemy squads are the amount below times as large,\\nhowever many players there are.", + # (PER PLAYER's and MULTIPLIER's) "For each player past the first, enemy squads get\\nthis much more of themselves (100%: as many again).", + "Each enemy squad is this many times as large.", ]) lines = _screen(base, spec, rows, ["server settings update"], [''], extra) @@ -770,8 +789,12 @@ MAXIMUM_PLAYERS = [2, 4, 8, 12, 16, 24, 32, 48, 64, 96, 128] # network.coop_friendly_fire's values, as Server Setup's FRIENDLY FIRE shows # them (menu_functions.c's cooperative_friendly_fire_modes, in this order) COOP_FRIENDLY_FIRE_VALUES = ["off", "on", "shields_only", "explosives_only"] -# network.coop_enemies' values, percentages (Server Setup's EXTRA ENEMIES) -COOP_ENEMIES_VALUES = [0, 25, 50, 100, 150, 200] +# Server Setup's EXTRA ENEMIES: network.coop_enemies_mode's values (in +# coop_enemies.c's order), and the amounts of PER PLAYER (network.coop_enemies, +# percentages) and STATIC MULTIPLIER (network.coop_enemies_multiplier) +COOP_ENEMIES_MODES = ["none", "per_player", "multiplier"] +COOP_ENEMIES_PERCENTAGES = [25, 50, 100, 150, 200] +COOP_ENEMIES_MULTIPLIERS = [2, 4, 8, 16, 32] # Server Setup's rows of the gametype's options: the gametype editor's # screens SETUP_OPTION_SCREENS = [