mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:40:44 +00:00
Crowded modifier 😄 (#3023)
## Description: To increase variety a bit more I present: The "crowded" public game modifier :) It basically simulates a crazy youtuber lobby. Cramp a lot of players on a small map 😄 I think its fun, exciting and you actually need skill to manage the chaos. 5% of public games get this modifier, but because we remove the modifier for big maps its more like 2.5% (should be something special) | <img width="321" height="269" alt="Screenshot 2026-01-25 200427" src="https://github.com/user-attachments/assets/7d2e90c1-e6bc-40a8-a19e-a0849612f472" /> | <img width="317" height="264" alt="Screenshot 2026-01-25 200554" src="https://github.com/user-attachments/assets/8b4bd5da-bed1-4743-a107-9ce07fce3040" /> | <img width="317" height="244" alt="Screenshot 2026-01-25 200521" src="https://github.com/user-attachments/assets/16293de3-0fc4-431f-8151-31b4e11040fe" /> | |---|---|---| ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin
This commit is contained in:
@@ -166,6 +166,7 @@
|
||||
"infinite_gold": "Infinite gold",
|
||||
"infinite_troops": "Infinite troops",
|
||||
"compact_map": "Compact Map",
|
||||
"crowded": "Crowded",
|
||||
"max_timer": "Game length (minutes)",
|
||||
"max_timer_placeholder": "Mins",
|
||||
"max_timer_invalid": "Please enter a valid max timer value (1-120 minutes)",
|
||||
@@ -421,6 +422,7 @@
|
||||
"public_game_modifier": {
|
||||
"random_spawn": "Random Spawn",
|
||||
"compact_map": "Compact Map",
|
||||
"crowded": "Crowded",
|
||||
"starting_gold": "5M Starting Gold"
|
||||
},
|
||||
"select_lang": {
|
||||
|
||||
@@ -374,6 +374,9 @@ export class PublicLobby extends LitElement {
|
||||
if (publicGameModifiers.isCompact) {
|
||||
labels.push(translateText("public_game_modifier.compact_map"));
|
||||
}
|
||||
if (publicGameModifiers.isCrowded) {
|
||||
labels.push(translateText("public_game_modifier.crowded"));
|
||||
}
|
||||
if (publicGameModifiers.startingGold) {
|
||||
labels.push(translateText("public_game_modifier.starting_gold"));
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@ export const GameConfigSchema = z.object({
|
||||
.object({
|
||||
isCompact: z.boolean(),
|
||||
isRandomSpawn: z.boolean(),
|
||||
isCrowded: z.boolean(),
|
||||
startingGold: z.number().int().min(0).optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
@@ -211,6 +211,7 @@ export enum GameMapSize {
|
||||
export interface PublicGameModifiers {
|
||||
isCompact: boolean;
|
||||
isRandomSpawn: boolean;
|
||||
isCrowded: boolean;
|
||||
startingGold?: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export class MapPlaylist {
|
||||
|
||||
const modifiers = this.getRandomPublicGameModifiers();
|
||||
const { startingGold } = modifiers;
|
||||
let { isCompact, isRandomSpawn } = modifiers;
|
||||
let { isCompact, isRandomSpawn, isCrowded } = modifiers;
|
||||
|
||||
// Duos, Trios, and Quads should not get random spawn (as it defeats the purpose)
|
||||
if (
|
||||
@@ -108,8 +108,8 @@ export class MapPlaylist {
|
||||
isRandomSpawn = false;
|
||||
}
|
||||
|
||||
// Maps with smallest player count < 50 don't support compact map in team games
|
||||
// The smallest player count is the 3rd number in the player counts array
|
||||
// Maps with smallest player count (third number of calculateMapPlayerCounts) < 50 don't support compact map in team games
|
||||
// (not enough players after 75% player reduction for compact maps)
|
||||
if (
|
||||
mode === GameMode.Team &&
|
||||
!(await this.supportsCompactMapForTeams(map))
|
||||
@@ -117,15 +117,34 @@ export class MapPlaylist {
|
||||
isCompact = false;
|
||||
}
|
||||
|
||||
// Crowded modifier: if the map's biggest player count (first number of calculateMapPlayerCounts) is 60 or lower (small maps),
|
||||
// set player count to 125 (or 60 if compact map is also enabled)
|
||||
let crowdedMaxPlayers: number | undefined;
|
||||
if (isCrowded) {
|
||||
crowdedMaxPlayers = await this.getCrowdedMaxPlayers(map, isCompact);
|
||||
if (crowdedMaxPlayers === undefined) {
|
||||
isCrowded = false;
|
||||
} else {
|
||||
crowdedMaxPlayers = this.adjustForTeams(crowdedMaxPlayers, playerTeams);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the default public game config (from your GameManager)
|
||||
return {
|
||||
donateGold: mode === GameMode.Team,
|
||||
donateTroops: mode === GameMode.Team,
|
||||
gameMap: map,
|
||||
maxPlayers: await this.lobbyMaxPlayers(map, mode, playerTeams, isCompact),
|
||||
maxPlayers:
|
||||
crowdedMaxPlayers ??
|
||||
(await this.lobbyMaxPlayers(map, mode, playerTeams, isCompact)),
|
||||
gameType: GameType.Public,
|
||||
gameMapSize: isCompact ? GameMapSize.Compact : GameMapSize.Normal,
|
||||
publicGameModifiers: { isCompact, isRandomSpawn, startingGold },
|
||||
publicGameModifiers: {
|
||||
isCompact,
|
||||
isRandomSpawn,
|
||||
isCrowded,
|
||||
startingGold,
|
||||
},
|
||||
startingGold,
|
||||
difficulty:
|
||||
playerTeams === HumansVsNations ? Difficulty.Medium : Difficulty.Easy,
|
||||
@@ -209,18 +228,31 @@ export class MapPlaylist {
|
||||
return {
|
||||
isRandomSpawn: Math.random() < 0.1, // 10% chance
|
||||
isCompact: Math.random() < 0.05, // 5% chance
|
||||
isCrowded: Math.random() < 0.05, // 5% chance
|
||||
startingGold: Math.random() < 0.05 ? 5_000_000 : undefined, // 5% chance
|
||||
};
|
||||
}
|
||||
|
||||
// Maps with smallest player count (third number of calculateMapPlayerCounts) < 50 don't support compact map in team games
|
||||
// (not enough players after 75% player reduction for compact maps)
|
||||
private async supportsCompactMapForTeams(map: GameMapType): Promise<boolean> {
|
||||
// Maps with smallest player count < 50 don't support compact map in team games
|
||||
// The smallest player count is the 3rd number in the player counts array
|
||||
const landTiles = await getMapLandTiles(map);
|
||||
const [, , smallest] = this.calculateMapPlayerCounts(landTiles);
|
||||
return smallest >= 50;
|
||||
}
|
||||
|
||||
private async getCrowdedMaxPlayers(
|
||||
map: GameMapType,
|
||||
isCompact: boolean,
|
||||
): Promise<number | undefined> {
|
||||
const landTiles = await getMapLandTiles(map);
|
||||
const [firstPlayerCount] = this.calculateMapPlayerCounts(landTiles);
|
||||
if (firstPlayerCount <= 60) {
|
||||
return isCompact ? 60 : 125;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async lobbyMaxPlayers(
|
||||
map: GameMapType,
|
||||
mode: GameMode,
|
||||
@@ -236,7 +268,15 @@ export class MapPlaylist {
|
||||
if (isCompactMap) {
|
||||
p = Math.max(3, Math.floor(p * 0.25));
|
||||
}
|
||||
if (numPlayerTeams === undefined) return p;
|
||||
return this.adjustForTeams(p, numPlayerTeams);
|
||||
}
|
||||
|
||||
private adjustForTeams(
|
||||
playerCount: number,
|
||||
numPlayerTeams: TeamCountConfig | undefined,
|
||||
): number {
|
||||
if (numPlayerTeams === undefined) return playerCount;
|
||||
let p = playerCount;
|
||||
switch (numPlayerTeams) {
|
||||
case Duos:
|
||||
p -= p % 2;
|
||||
|
||||
@@ -80,7 +80,7 @@ export class TestServerConfig implements ServerConfig {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getRandomPublicGameModifiers(): PublicGameModifiers {
|
||||
return { isCompact: false, isRandomSpawn: false };
|
||||
return { isCompact: false, isRandomSpawn: false, isCrowded: false };
|
||||
}
|
||||
async supportsCompactMapForTeams(): Promise<boolean> {
|
||||
throw new Error("Method not implemented.");
|
||||
|
||||
Reference in New Issue
Block a user