diff --git a/src/renderer/components/battle/BotParticipant.vue b/src/renderer/components/battle/BotParticipant.vue index 9abcea07..3cf2ea95 100644 --- a/src/renderer/components/battle/BotParticipant.vue +++ b/src/renderer/components/battle/BotParticipant.vue @@ -20,7 +20,7 @@ import { Icon } from "@iconify/vue"; import robot from "@iconify-icons/mdi/robot"; import { MenuItem } from "primevue/menuitem"; -import { Ref, ref } from "vue"; +import { Ref, ref, toRaw } from "vue"; import LuaOptionsModal from "@/components/battle/LuaOptionsModal.vue"; import TeamParticipant from "@/components/battle/TeamParticipant.vue"; @@ -43,6 +43,10 @@ const actions: MenuItem[] = [ label: "Configure", command: configureBot, }, + { + label: "Duplicate", + command: duplicateBot, + }, { label: "Kick", command: kickBot, @@ -59,6 +63,13 @@ function kickBot() { props.battle.removeBot(props.bot); } +// Duplicates this bot and its settings and gives it a new player id. +function duplicateBot() { + const duplicatedBot = structuredClone(toRaw(props.bot)); + duplicatedBot.playerId = props.battle.contenders.value.length; + props.battle.addBot(duplicatedBot); +} + async function configureBot() { const engineVersion = api.content.engine.installedVersions.find((version) => version.id === props.battle.battleOptions.engineVersion); const ai = engineVersion?.ais.find((ai) => ai.name === props.bot.name); @@ -69,7 +80,7 @@ async function configureBot() { } function setBotOptions(options: Record) { - props.battle.setBotOptions(props.bot.name, options); + props.battle.setBotOptions(props.bot.playerId, options); } diff --git a/src/renderer/model/battle/abstract-battle.ts b/src/renderer/model/battle/abstract-battle.ts index ddeba0e1..664142da 100644 --- a/src/renderer/model/battle/abstract-battle.ts +++ b/src/renderer/model/battle/abstract-battle.ts @@ -90,6 +90,16 @@ export abstract class AbstractBattle { }); } + // Returns a bot by it's PlayerId. Returns undefined if not found.. + public getBotByPlayerId(playerId: number): Bot | undefined { + return this.bots.find((bot) => { + if (bot.playerId === playerId) { + return true; + } + return false; + }); + } + public open() { this.watchStopHandles = [ watch( @@ -137,7 +147,7 @@ export abstract class AbstractBattle { // eslint-disable-next-line @typescript-eslint/no-explicit-any public abstract setGameOptions(options: Record): void; // eslint-disable-next-line @typescript-eslint/no-explicit-any - public abstract setBotOptions(botName: string, options: Record): void; + public abstract setBotOptions(playerId: number, options: Record): void; public abstract addBot(bot: Bot): void; public abstract removeBot(bot: Bot): void; public abstract playerToSpectator(player: User): void; diff --git a/src/renderer/model/battle/offline-battle.ts b/src/renderer/model/battle/offline-battle.ts index 5ca3fe23..55f847b6 100644 --- a/src/renderer/model/battle/offline-battle.ts +++ b/src/renderer/model/battle/offline-battle.ts @@ -72,8 +72,8 @@ export class OfflineBattle extends AbstractBattle { this.fixIds(); } - public setBotOptions(botName: string, options: Record) { - const bot = this.getParticipantByName(botName) as Bot; + public setBotOptions(playerId: number, options: Record) { + const bot = this.getBotByPlayerId(playerId) as Bot; bot.aiOptions = options; } diff --git a/src/renderer/model/battle/spads-battle.ts b/src/renderer/model/battle/spads-battle.ts index d0d479dc..80f502cc 100644 --- a/src/renderer/model/battle/spads-battle.ts +++ b/src/renderer/model/battle/spads-battle.ts @@ -364,7 +364,7 @@ export class SpadsBattle extends AbstractBattle { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setBotOptions(botName: string, options: Record) { + public setBotOptions(playerId: number, options: Record) { console.warn("not implemented: setBotOptions"); // TODO }