mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:10:42 +00:00
Option to disable alliances + 2 new modifiers for variety 😄 (#3392)
## Description:
Rex had this idea: "It would be funny to have an option in private
lobbies to disable alliances."
I added it as an option.
Now people can choose to live in constant fear of their neighbors 😆
Also added two new public game modifiers for variety (only for the
special rotation):
- Alliances disabled (low probability)
- x2 gold multiplier (low probability)
Would be nice to squeeze this into v30, last minute?
## 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:
@@ -193,6 +193,7 @@
|
||||
"infinite_gold": "Infinite gold",
|
||||
"infinite_troops": "Infinite troops",
|
||||
"compact_map": "Compact Map",
|
||||
"disable_alliances": "Disable alliances",
|
||||
"max_timer": "Game length (minutes)",
|
||||
"max_timer_placeholder": "Mins",
|
||||
"max_timer_invalid": "Please enter a valid max timer value (1-120 minutes)",
|
||||
@@ -414,6 +415,7 @@
|
||||
"infinite_troops": "Infinite troops",
|
||||
"donate_troops": "Donate troops",
|
||||
"compact_map": "Compact Map",
|
||||
"disable_alliances": "Disable alliances",
|
||||
"enables_title": "Enable Settings",
|
||||
"player": "Player",
|
||||
"players": "Players",
|
||||
@@ -431,9 +433,12 @@
|
||||
"teams_Trios": "Trios (teams of 3)",
|
||||
"teams_Quads": "Quads (teams of 4)",
|
||||
"teams_Humans Vs Nations": "Humans vs Nations",
|
||||
"starting_gold": "Starting gold",
|
||||
"crowded": "Crowded modifier",
|
||||
"hard_nations": "Hard Nations",
|
||||
"gold_multiplier": "Gold multiplier",
|
||||
"gold_multiplier_placeholder": "2.0x",
|
||||
"starting_gold": "Starting Gold (Millions)",
|
||||
"starting_gold_placeholder": "5",
|
||||
"leave_confirmation": "Are you sure you want to leave the lobby?"
|
||||
},
|
||||
"team_colors": {
|
||||
@@ -478,7 +483,9 @@
|
||||
"compact_map": "Compact Map",
|
||||
"crowded": "Crowded",
|
||||
"hard_nations": "Hard Nations",
|
||||
"starting_gold": "{amount}M Starting Gold"
|
||||
"starting_gold": "{amount}M Starting Gold",
|
||||
"gold_multiplier": "x{amount} Gold Multiplier",
|
||||
"disable_alliances": "Alliances Disabled"
|
||||
},
|
||||
"select_lang": {
|
||||
"title": "Select Language"
|
||||
|
||||
@@ -71,6 +71,7 @@ export class HostLobbyModal extends BaseModal {
|
||||
@state() private goldMultiplierValue: number | undefined = undefined;
|
||||
@state() private startingGold: boolean = false;
|
||||
@state() private startingGoldValue: number | undefined = undefined;
|
||||
@state() private disableAlliances: boolean = false;
|
||||
@state() private lobbyId = "";
|
||||
@state() private lobbyUrlSuffix = "";
|
||||
@state() private clients: ClientInfo[] = [];
|
||||
@@ -174,16 +175,16 @@ export class HostLobbyModal extends BaseModal {
|
||||
.onKeyDown=${this.handleSpawnImmunityDurationKeyDown}
|
||||
></toggle-input-card>`,
|
||||
html`<toggle-input-card
|
||||
.labelKey=${"single_modal.gold_multiplier"}
|
||||
.labelKey=${"host_modal.gold_multiplier"}
|
||||
.checked=${this.goldMultiplier}
|
||||
.inputId=${"gold-multiplier-value"}
|
||||
.inputMin=${0.1}
|
||||
.inputMax=${1000}
|
||||
.inputStep=${"any"}
|
||||
.inputValue=${this.goldMultiplierValue}
|
||||
.inputAriaLabel=${translateText("single_modal.gold_multiplier")}
|
||||
.inputAriaLabel=${translateText("host_modal.gold_multiplier")}
|
||||
.inputPlaceholder=${translateText(
|
||||
"single_modal.gold_multiplier_placeholder",
|
||||
"host_modal.gold_multiplier_placeholder",
|
||||
)}
|
||||
.defaultInputValue=${2}
|
||||
.minValidOnEnable=${0.1}
|
||||
@@ -192,16 +193,16 @@ export class HostLobbyModal extends BaseModal {
|
||||
.onKeyDown=${this.handleGoldMultiplierValueKeyDown}
|
||||
></toggle-input-card>`,
|
||||
html`<toggle-input-card
|
||||
.labelKey=${"single_modal.starting_gold"}
|
||||
.labelKey=${"host_modal.starting_gold"}
|
||||
.checked=${this.startingGold}
|
||||
.inputId=${"starting-gold-value"}
|
||||
.inputMin=${0.1}
|
||||
.inputMax=${1000}
|
||||
.inputStep=${"any"}
|
||||
.inputValue=${this.startingGoldValue}
|
||||
.inputAriaLabel=${translateText("single_modal.starting_gold")}
|
||||
.inputAriaLabel=${translateText("host_modal.starting_gold")}
|
||||
.inputPlaceholder=${translateText(
|
||||
"single_modal.starting_gold_placeholder",
|
||||
"host_modal.starting_gold_placeholder",
|
||||
)}
|
||||
.defaultInputValue=${5}
|
||||
.minValidOnEnable=${0.1}
|
||||
@@ -294,6 +295,10 @@ export class HostLobbyModal extends BaseModal {
|
||||
labelKey: "host_modal.compact_map",
|
||||
checked: this.compactMap,
|
||||
},
|
||||
{
|
||||
labelKey: "host_modal.disable_alliances",
|
||||
checked: this.disableAlliances,
|
||||
},
|
||||
],
|
||||
inputCards,
|
||||
},
|
||||
@@ -457,6 +462,7 @@ export class HostLobbyModal extends BaseModal {
|
||||
this.goldMultiplierValue = undefined;
|
||||
this.startingGold = false;
|
||||
this.startingGoldValue = undefined;
|
||||
this.disableAlliances = false;
|
||||
|
||||
this.leaveLobbyOnClose = true;
|
||||
}
|
||||
@@ -533,6 +539,10 @@ export class HostLobbyModal extends BaseModal {
|
||||
case "host_modal.compact_map":
|
||||
this.handleCompactMapChange(checked);
|
||||
break;
|
||||
case "host_modal.disable_alliances":
|
||||
this.disableAlliances = checked;
|
||||
this.putGameConfig();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -795,6 +805,7 @@ export class HostLobbyModal extends BaseModal {
|
||||
this.startingGold === true && this.startingGoldValue !== undefined
|
||||
? Math.round(this.startingGoldValue * 1_000_000)
|
||||
: undefined,
|
||||
disableAlliances: this.disableAlliances || undefined,
|
||||
} satisfies Partial<GameConfig>,
|
||||
},
|
||||
bubbles: true,
|
||||
|
||||
@@ -56,6 +56,7 @@ const DEFAULT_OPTIONS = {
|
||||
startingGold: false,
|
||||
startingGoldValue: undefined as number | undefined,
|
||||
disabledUnits: [] as UnitType[],
|
||||
disableAlliances: false,
|
||||
} as const;
|
||||
|
||||
@customElement("single-player-modal")
|
||||
@@ -90,6 +91,7 @@ export class SinglePlayerModal extends BaseModal {
|
||||
@state() private disabledUnits: UnitType[] = [
|
||||
...DEFAULT_OPTIONS.disabledUnits,
|
||||
];
|
||||
@state() private disableAlliances: boolean = DEFAULT_OPTIONS.disableAlliances;
|
||||
|
||||
private mapLoader = terrainMapFileLoader;
|
||||
|
||||
@@ -313,6 +315,10 @@ export class SinglePlayerModal extends BaseModal {
|
||||
labelKey: "single_modal.compact_map",
|
||||
checked: this.compactMap,
|
||||
},
|
||||
{
|
||||
labelKey: "single_modal.disable_alliances",
|
||||
checked: this.disableAlliances,
|
||||
},
|
||||
],
|
||||
inputCards,
|
||||
},
|
||||
@@ -383,6 +389,7 @@ export class SinglePlayerModal extends BaseModal {
|
||||
this.gameMode !== DEFAULT_OPTIONS.gameMode ||
|
||||
this.goldMultiplier !== DEFAULT_OPTIONS.goldMultiplier ||
|
||||
this.startingGold !== DEFAULT_OPTIONS.startingGold ||
|
||||
this.disableAlliances !== DEFAULT_OPTIONS.disableAlliances ||
|
||||
this.disabledUnits.length > 0
|
||||
);
|
||||
}
|
||||
@@ -409,6 +416,7 @@ export class SinglePlayerModal extends BaseModal {
|
||||
this.goldMultiplierValue = DEFAULT_OPTIONS.goldMultiplierValue;
|
||||
this.startingGold = DEFAULT_OPTIONS.startingGold;
|
||||
this.startingGoldValue = DEFAULT_OPTIONS.startingGoldValue;
|
||||
this.disableAlliances = DEFAULT_OPTIONS.disableAlliances;
|
||||
}
|
||||
|
||||
protected onOpen(): void {
|
||||
@@ -488,6 +496,9 @@ export class SinglePlayerModal extends BaseModal {
|
||||
case "single_modal.compact_map":
|
||||
this.handleCompactMapChange(checked);
|
||||
break;
|
||||
case "single_modal.disable_alliances":
|
||||
this.disableAlliances = checked;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -696,6 +707,7 @@ export class SinglePlayerModal extends BaseModal {
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
...(this.disableAlliances ? { disableAlliances: true } : {}),
|
||||
},
|
||||
lobbyCreatedAt: Date.now(), // ms; server should be authoritative in MP
|
||||
},
|
||||
|
||||
@@ -168,6 +168,23 @@ export function getActiveModifiers(
|
||||
formattedValue: `${millions}M`,
|
||||
});
|
||||
}
|
||||
if (modifiers.goldMultiplier) {
|
||||
result.push({
|
||||
labelKey: "host_modal.gold_multiplier",
|
||||
badgeKey: "public_game_modifier.gold_multiplier",
|
||||
badgeParams: {
|
||||
amount: modifiers.goldMultiplier,
|
||||
},
|
||||
value: modifiers.goldMultiplier,
|
||||
formattedValue: `x${modifiers.goldMultiplier}`,
|
||||
});
|
||||
}
|
||||
if (modifiers.isAlliancesDisabled) {
|
||||
result.push({
|
||||
labelKey: "host_modal.disable_alliances",
|
||||
badgeKey: "public_game_modifier.disable_alliances",
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,8 @@ export const GameConfigSchema = z.object({
|
||||
isCrowded: z.boolean(),
|
||||
isHardNations: z.boolean(),
|
||||
startingGold: z.number().int().min(0).optional(),
|
||||
goldMultiplier: z.number().min(0.1).max(1000).optional(),
|
||||
isAlliancesDisabled: z.boolean(),
|
||||
})
|
||||
.optional(),
|
||||
nations: z
|
||||
@@ -230,6 +232,7 @@ export const GameConfigSchema = z.object({
|
||||
infiniteTroops: z.boolean(),
|
||||
instantBuild: z.boolean(),
|
||||
disableNavMesh: z.boolean().optional(),
|
||||
disableAlliances: z.boolean().optional(),
|
||||
randomSpawn: z.boolean(),
|
||||
maxPlayers: z.number().optional(),
|
||||
maxTimerValue: z.number().int().min(1).max(120).optional(), // In minutes
|
||||
|
||||
@@ -74,6 +74,7 @@ export interface Config {
|
||||
donateTroops(): boolean;
|
||||
instantBuild(): boolean;
|
||||
disableNavMesh(): boolean;
|
||||
disableAlliances(): boolean;
|
||||
isRandomSpawn(): boolean;
|
||||
numSpawnPhaseTurns(): number;
|
||||
userSettings(): UserSettings;
|
||||
|
||||
@@ -240,6 +240,9 @@ export class DefaultConfig implements Config {
|
||||
disableNavMesh(): boolean {
|
||||
return this._gameConfig.disableNavMesh ?? false;
|
||||
}
|
||||
disableAlliances(): boolean {
|
||||
return this._gameConfig.disableAlliances ?? false;
|
||||
}
|
||||
isRandomSpawn(): boolean {
|
||||
return this._gameConfig.randomSpawn;
|
||||
}
|
||||
|
||||
@@ -243,6 +243,8 @@ export interface PublicGameModifiers {
|
||||
isCrowded: boolean;
|
||||
isHardNations: boolean;
|
||||
startingGold?: number;
|
||||
goldMultiplier?: number;
|
||||
isAlliancesDisabled: boolean;
|
||||
}
|
||||
|
||||
export interface UnitInfo {
|
||||
|
||||
@@ -477,6 +477,9 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
canSendAllianceRequest(other: Player): boolean {
|
||||
if (this.mg.config().disableAlliances()) {
|
||||
return false;
|
||||
}
|
||||
if (other === this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -157,6 +157,9 @@ export class GameServer {
|
||||
if (gameConfig.startingGold !== undefined) {
|
||||
this.gameConfig.startingGold = gameConfig.startingGold;
|
||||
}
|
||||
if (gameConfig.disableAlliances !== undefined) {
|
||||
this.gameConfig.disableAlliances = gameConfig.disableAlliances;
|
||||
}
|
||||
}
|
||||
|
||||
private isKicked(clientID: ClientID): boolean {
|
||||
|
||||
@@ -103,7 +103,9 @@ type ModifierKey =
|
||||
| "isCrowded"
|
||||
| "isHardNations"
|
||||
| "startingGold"
|
||||
| "startingGoldHigh";
|
||||
| "startingGoldHigh"
|
||||
| "goldMultiplier"
|
||||
| "isAlliancesDisabled";
|
||||
|
||||
// Each entry represents one "ticket" in the pool. More tickets = higher chance of selection.
|
||||
const SPECIAL_MODIFIER_POOL: ModifierKey[] = [
|
||||
@@ -113,6 +115,8 @@ const SPECIAL_MODIFIER_POOL: ModifierKey[] = [
|
||||
...Array<ModifierKey>(1).fill("isHardNations"),
|
||||
...Array<ModifierKey>(8).fill("startingGold"),
|
||||
...Array<ModifierKey>(1).fill("startingGoldHigh"),
|
||||
...Array<ModifierKey>(1).fill("goldMultiplier"),
|
||||
...Array<ModifierKey>(1).fill("isAlliancesDisabled"),
|
||||
];
|
||||
|
||||
// Modifiers that cannot be active at the same time.
|
||||
@@ -197,6 +201,7 @@ export class MapPlaylist {
|
||||
isCrowded,
|
||||
isHardNations,
|
||||
startingGold,
|
||||
isAlliancesDisabled: false,
|
||||
},
|
||||
startingGold,
|
||||
difficulty: isHardNations ? Difficulty.Hard : Difficulty.Medium,
|
||||
@@ -263,7 +268,14 @@ export class MapPlaylist {
|
||||
undefined,
|
||||
poolCountReduction,
|
||||
);
|
||||
let { isCrowded, startingGold, isCompact, isRandomSpawn } = poolResult;
|
||||
let {
|
||||
isCrowded,
|
||||
startingGold,
|
||||
isCompact,
|
||||
isRandomSpawn,
|
||||
goldMultiplier,
|
||||
isAlliancesDisabled,
|
||||
} = poolResult;
|
||||
let isHardNations =
|
||||
hardNationsFromIndependentRoll ?? poolResult.isHardNations;
|
||||
|
||||
@@ -280,7 +292,9 @@ export class MapPlaylist {
|
||||
!isRandomSpawn &&
|
||||
!isCompact &&
|
||||
!isHardNations &&
|
||||
startingGold === undefined
|
||||
startingGold === undefined &&
|
||||
goldMultiplier === undefined &&
|
||||
!isAlliancesDisabled
|
||||
) {
|
||||
excludedModifiers.push("isCrowded");
|
||||
const fallback = this.getRandomSpecialGameModifiers(
|
||||
@@ -288,7 +302,13 @@ export class MapPlaylist {
|
||||
1,
|
||||
poolCountReduction,
|
||||
);
|
||||
({ isRandomSpawn, isCompact, startingGold } = fallback);
|
||||
({
|
||||
isRandomSpawn,
|
||||
isCompact,
|
||||
startingGold,
|
||||
goldMultiplier,
|
||||
isAlliancesDisabled,
|
||||
} = fallback);
|
||||
isHardNations =
|
||||
hardNationsFromIndependentRoll ?? fallback.isHardNations;
|
||||
}
|
||||
@@ -321,8 +341,12 @@ export class MapPlaylist {
|
||||
isCrowded,
|
||||
isHardNations,
|
||||
startingGold,
|
||||
goldMultiplier,
|
||||
isAlliancesDisabled,
|
||||
},
|
||||
startingGold,
|
||||
goldMultiplier,
|
||||
disableAlliances: isAlliancesDisabled,
|
||||
difficulty: isHardNations ? Difficulty.Hard : Difficulty.Medium,
|
||||
infiniteGold: false,
|
||||
infiniteTroops: false,
|
||||
@@ -482,6 +506,7 @@ export class MapPlaylist {
|
||||
playerTeams === HumansVsNations
|
||||
? Math.random() < HARD_NATIONS_HVN_PROBABILITY
|
||||
: Math.random() < 0.025, // 2.5% chance
|
||||
isAlliancesDisabled: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -530,6 +555,8 @@ export class MapPlaylist {
|
||||
: selected.has("startingGold")
|
||||
? 5_000_000
|
||||
: undefined,
|
||||
goldMultiplier: selected.has("goldMultiplier") ? 2 : undefined,
|
||||
isAlliancesDisabled: selected.has("isAlliancesDisabled"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ export class TestServerConfig implements ServerConfig {
|
||||
isRandomSpawn: false,
|
||||
isCrowded: false,
|
||||
isHardNations: false,
|
||||
isAlliancesDisabled: false,
|
||||
};
|
||||
}
|
||||
async supportsCompactMapForTeams(): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user