mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-30 06:42:11 +00:00
4d5bb7a835
## Description: 1. Using the wording `"Nation"`, `"FakeHuman"` and `"NPC"` at the same time is confusing. So I renamed every mention of `"FakeHuman"` and `"NPC"` in the entire project to `"Nation"`. Just like they are called ingame. 2. `BotBehavior.ts` was originally intended for sharing the logic between nations and bots. But at the moment, the logic there isn't really shared and it's basically just about attacking. So I renamed `BotBehavior.ts` to `AiAttackBehavior.ts`. I use "Ai" to indicate that this file is used by bots AND nations. 3. Moved `execuction/utils/AllianceBehavior.ts` to `execuction/nation/NationAllianceBehavior.ts` to make sure everybody understands that this file is not about alliances in general. It's just about nations and how they handle alliances. 4. Removed `difficultyModifier` from `DefaultConfig`. It's unused and I think we usually want to finetune the difficulty instead of using that method. 5. Added `assertNever` in all `switch (difficulty)` default cases. ## 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
105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import { PseudoRandom } from "../PseudoRandom";
|
|
import { simpleHash } from "../Util";
|
|
import { PlayerInfo, PlayerType, Team } from "./Game";
|
|
|
|
export function assignTeams(
|
|
players: PlayerInfo[],
|
|
teams: Team[],
|
|
maxTeamSize: number = getMaxTeamSize(players.length, teams.length),
|
|
): Map<PlayerInfo, Team | "kicked"> {
|
|
const result = new Map<PlayerInfo, Team | "kicked">();
|
|
const teamPlayerCount = new Map<Team, number>();
|
|
|
|
// Group players by clan
|
|
const clanGroups = new Map<string, PlayerInfo[]>();
|
|
const noClanPlayers: PlayerInfo[] = [];
|
|
|
|
// Sort players into clan groups or no-clan list
|
|
for (const player of players) {
|
|
if (player.clan) {
|
|
if (!clanGroups.has(player.clan)) {
|
|
clanGroups.set(player.clan, []);
|
|
}
|
|
clanGroups.get(player.clan)!.push(player);
|
|
} else {
|
|
noClanPlayers.push(player);
|
|
}
|
|
}
|
|
|
|
// Sort clans by size (largest first)
|
|
const sortedClans = Array.from(clanGroups.entries()).sort(
|
|
(a, b) => b[1].length - a[1].length,
|
|
);
|
|
|
|
// First, assign clan players
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
for (const [_, clanPlayers] of sortedClans) {
|
|
// Try to keep the clan together on the team with fewer players
|
|
let team: Team | null = null;
|
|
let teamSize = 0;
|
|
for (const t of teams) {
|
|
const p = teamPlayerCount.get(t) ?? 0;
|
|
if (team !== null && teamSize <= p) continue;
|
|
teamSize = p;
|
|
team = t;
|
|
}
|
|
|
|
if (team === null) continue;
|
|
|
|
for (const player of clanPlayers) {
|
|
if (teamSize < maxTeamSize) {
|
|
teamSize++;
|
|
result.set(player, team);
|
|
} else {
|
|
result.set(player, "kicked");
|
|
}
|
|
}
|
|
teamPlayerCount.set(team, teamSize);
|
|
}
|
|
|
|
// Then, assign non-clan players to balance teams
|
|
let nationPlayers = noClanPlayers.filter(
|
|
(player) => player.playerType === PlayerType.Nation,
|
|
);
|
|
if (nationPlayers.length > 0) {
|
|
// Shuffle only nations to randomize their team assignment
|
|
const random = new PseudoRandom(simpleHash(nationPlayers[0].id));
|
|
nationPlayers = random.shuffleArray(nationPlayers);
|
|
}
|
|
const otherPlayers = noClanPlayers.filter(
|
|
(player) => player.playerType !== PlayerType.Nation,
|
|
);
|
|
|
|
for (const player of otherPlayers.concat(nationPlayers)) {
|
|
let team: Team | null = null;
|
|
let teamSize = 0;
|
|
for (const t of teams) {
|
|
const p = teamPlayerCount.get(t) ?? 0;
|
|
if (team !== null && teamSize <= p) continue;
|
|
teamSize = p;
|
|
team = t;
|
|
}
|
|
if (team === null) continue;
|
|
teamPlayerCount.set(team, teamSize + 1);
|
|
result.set(player, team);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function assignTeamsLobbyPreview(
|
|
players: PlayerInfo[],
|
|
teams: Team[],
|
|
nationCount: number,
|
|
): Map<PlayerInfo, Team | "kicked"> {
|
|
const maxTeamSize = getMaxTeamSize(
|
|
players.length + nationCount,
|
|
teams.length,
|
|
);
|
|
return assignTeams(players, teams, maxTeamSize);
|
|
}
|
|
|
|
export function getMaxTeamSize(numPlayers: number, numTeams: number): number {
|
|
return Math.ceil(numPlayers / numTeams);
|
|
}
|