From 47da949b6d84ba016ccf4e4d50d07250e054de8b Mon Sep 17 00:00:00 2001 From: Hexugory Date: Thu, 30 Mar 2023 17:48:40 -0500 Subject: [PATCH] homicide is now real --- src/battle.ts | 74 +++++++++++++++++++++++++++++----------- src/characters.ts | 5 +-- src/slash/battletest.ts | 39 +++++++++++++++++++++ src/slash/commandlist.ts | 4 ++- 4 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 src/slash/battletest.ts diff --git a/src/battle.ts b/src/battle.ts index 693a7ce..3f72fed 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -1,8 +1,14 @@ -import { Message, TextChannel } from "discord.js"; -import { Character, CHARACTERS, PotencyStatus, Target } from "./characters"; +import { Message, TextBasedChannel } from "discord.js"; +import { Character, CHARACTERS, Effect, PotencyStatus, Target } from "./characters"; import { Player } from "./models/player"; import { Unit } from "./models/unit"; +export enum BattleState { + Ongoing, + Team1Win, + Team2Win +} + interface StatusEffects { poison: number regeneration: number @@ -15,6 +21,7 @@ interface PotencyEffects { resistanceChange: PotencyStatus[] accuracyChange: PotencyStatus[] speedChange: PotencyStatus[] + damageChange: PotencyStatus[] } class BattleUnit { @@ -41,13 +48,14 @@ class BattleUnit { potencyEffects: PotencyEffects = { resistanceChange: [], accuracyChange: [], - speedChange: [] + speedChange: [], + damageChange: [] } active = true } export class Battle { - constructor (channel: TextChannel, player1: Player, player2: Player, team1: [Unit, Unit, Unit], team2: [Unit, Unit, Unit]) { + constructor (channel: TextBasedChannel, player1: Player, player2: Player, team1: [Unit, Unit, Unit], team2: [Unit, Unit, Unit]) { this.channel = channel; this.player1 = player1; this.player2 = player2; @@ -56,9 +64,15 @@ export class Battle { this.units = this.team1.concat(this.team2) as [BattleUnit, BattleUnit, BattleUnit, BattleUnit, BattleUnit, BattleUnit]; } - simulateRound () { + simulateRound (): BattleState { + this.log = ''; this.tickStatuses(this.units); this.checkAlive(this.units); + this.useSkills(this.units); + + if (!this.team1.filter(e => {return e.active})[0]) return BattleState.Team2Win; + else if (!this.team2.filter(e => {return e.active})[0]) return BattleState.Team1Win; + else return BattleState.Ongoing; } tickStatuses (units: BattleUnit[]) { @@ -94,7 +108,7 @@ export class Battle { for (let i = 0; i < units.length; i++) { const rolls = [] let modifiedSpeed = units[i].speed - for (const change of units[i].potencyEffects.accuracyChange) { + for (const change of units[i].potencyEffects.speedChange) { modifiedSpeed -= change.potency; } for (let o = 0; o < modifiedSpeed; o++) { @@ -106,8 +120,12 @@ export class Battle { order.sort((a, b) => {return b[1] - a[1]}); + console.debug(order); + for (const initiative of order) { - const unit = units[initiative[1]]; + const unit = units[initiative[0]]; + if (!unit.active) continue; + const skill = unit.character.skills[Math.floor(Math.random()*unit.character.skills.length)]; const team = initiative[0] < 3 ? 1 : 2 const target = team === 1 ? this.team2[Math.floor(Math.random()*this.team2.length)] : this.team1[Math.floor(Math.random()*this.team1.length)]; @@ -121,35 +139,52 @@ export class Battle { for (const effect of skill.effects) { switch (effect.target){ case Target.Self: - this.runEffect([unit]); + this.runEffect(effect, unit, [unit]); break; case Target.Team: - this.runEffect(team === 1 ? this.team1 : this.team2); + this.runEffect(effect, unit, team === 1 ? this.team1 : this.team2); break; case Target.OneTeammate: - this.runEffect([allyTarget]); + this.runEffect(effect, unit, [allyTarget]); break; case Target.OneOpponent: - this.runEffect([target]); + this.runEffect(effect, unit, [target]); break; case Target.AllOpponents: - this.runEffect(team === 1 ? this.team2 : this.team1); + this.runEffect(effect, unit, team === 1 ? this.team2 : this.team1); break; case Target.TrueRandomOpponent: - this.runEffect([team === 1 ? this.team2[Math.floor(Math.random()*this.team2.length)] : this.team1[Math.floor(Math.random()*this.team1.length)]]); + this.runEffect(effect, unit, [team === 1 ? this.team2[Math.floor(Math.random()*this.team2.length)] : this.team1[Math.floor(Math.random()*this.team1.length)]]); break; } } + this.checkAlive(this.units); + } + } + + runEffect (effect: Effect, self: BattleUnit, targets: BattleUnit[]) { + if (effect.damage) { + for (const target of targets) { + let damage = effect.damage; + for (const statChange of self.potencyEffects.damageChange) { + Math.round(damage *= 1+(statChange.potency/100)); + } + for (const statChange of target.potencyEffects.resistanceChange) { + Math.round(damage *= 1-(statChange.potency/100)); + } + + this.appendLog(`${target.character.nameShort} took ${damage} damage!`); + target.health -= damage; + } } } - runEffect (targets: BattleUnit[]) { - - } - checkAlive (units: BattleUnit[]) { for (const unit of units) { - if (unit.health <= 0) unit.active = false; + if (!unit.active) continue; + console.debug(unit.health); + if (unit.health > 0) continue; + unit.active = false; this.appendLog(`${unit.character.nameShort} has been defeated!`); } } @@ -157,13 +192,14 @@ export class Battle { appendLog (str: string) { if (this.log) this.log += '\n'; this.log += str; + console.debug(str); return this.log; } message?: Message log = "" - channel: TextChannel + channel: TextBasedChannel player1: Player player2: Player team1: [BattleUnit, BattleUnit, BattleUnit] diff --git a/src/characters.ts b/src/characters.ts index 62e34b6..622efd5 100644 --- a/src/characters.ts +++ b/src/characters.ts @@ -34,16 +34,17 @@ const Effect = z.object({ resistanceChange: z.optional(PotencyStatus), accuracyChange: z.optional(PotencyStatus), speedChange: z.optional(PotencyStatus), + damageChange: z.optional(PotencyStatus), function: z.optional(z.string()) }).strict(); -type Effect = z.infer; +export type Effect = z.infer; const Skill = z.object({ name: z.string(), accuracy: z.optional(z.number().int()), effects: z.array(Effect) }).strict(); -type Skill = z.infer; +export type Skill = z.infer; const Character = z.object({ id: z.number().int(), diff --git a/src/slash/battletest.ts b/src/slash/battletest.ts new file mode 100644 index 0000000..7a61c77 --- /dev/null +++ b/src/slash/battletest.ts @@ -0,0 +1,39 @@ +import { ApplicationCommandOptionData, ApplicationCommandOptionType, CommandInteraction } from "discord.js" +import { Battle, BattleState } from "../battle" +import { CBClient } from "../cbclient" +import { CHARACTERS } from "../characters" +import { Player } from "../models/player" +import { Unit } from "../models/unit" +import { createArgumentsObject, SlashCommand } from "./slash" + +export class BattleTestCommand implements SlashCommand { + name = 'battletest' + description = 'shhh' + permission = [] + ownerOnly = false + guildOnly = false + guildID = "739645806100873328" //for testing + args: ApplicationCommandOptionData[] = [] + + async execute(int: CommandInteraction) { + const client = int.client as CBClient; + const you = await client.findOrCreatePlayer(int.user.id); + const units = (await Unit.findAll()); + const battle = new Battle(int.channel!, you, you, units.slice(0, 3) as [Unit, Unit, Unit], units.slice(3, 6) as [Unit, Unit, Unit]); + + await int.reply({ + content: 'gottem' + }); + let state: BattleState = BattleState.Ongoing; + while (state === BattleState.Ongoing) { + state = battle.simulateRound(); + await int.channel?.send(battle.log); + } + switch (state) { + case BattleState.Team1Win: + return await int.channel?.send('gamer 1 wins'); + case BattleState.Team2Win: + return await int.channel?.send('gamer 2 wins'); + } + } +}; diff --git a/src/slash/commandlist.ts b/src/slash/commandlist.ts index b747a54..5d1b7b4 100644 --- a/src/slash/commandlist.ts +++ b/src/slash/commandlist.ts @@ -1,3 +1,4 @@ +import { BattleTestCommand } from "./battletest"; import { InitCommand } from "./init"; import { RandomCaseCommand } from "./randomcase"; import { RollCommand } from "./roll"; @@ -6,5 +7,6 @@ import { SlashCommand } from "./slash"; export const SlashCommandList: SlashCommand[] = [ new RandomCaseCommand(), new InitCommand(), - new RollCommand() + new RollCommand(), + new BattleTestCommand() ]; \ No newline at end of file