diff --git a/src/arena/Constuructors/MagicConstructor.ts b/src/arena/Constuructors/MagicConstructor.ts index b3f2b66..39c5d72 100644 --- a/src/arena/Constuructors/MagicConstructor.ts +++ b/src/arena/Constuructors/MagicConstructor.ts @@ -94,7 +94,7 @@ export abstract class Magic extends AffectableAction { */ getCost(initiator: Player): void { const costValue = +initiator.stats.val(this.costType) - this.cost; - console.log(initiator, 'MP:', costValue); + console.log(initiator.id, 'MP:', costValue); if (costValue >= 0) { initiator.stats.set(this.costType, costValue); } else { diff --git a/src/arena/magics/__snapshots__/fireBall.test.ts.snap b/src/arena/magics/__snapshots__/fireBall.test.ts.snap index 5205d36..a7a7b26 100644 --- a/src/arena/magics/__snapshots__/fireBall.test.ts.snap +++ b/src/arena/magics/__snapshots__/fireBall.test.ts.snap @@ -30,11 +30,11 @@ aperiam 🔥 💔-1.21/-5.59 📖10 ]" exports[`fireBall should hit 6 targets 1`] = ` [ 8, - -5.56, - 8, - 8, - 8, - 8, + -0.72, + 6.79, + 7.09, + 6.8, + 6.79, 8, 8, 8, @@ -43,13 +43,13 @@ exports[`fireBall should hit 6 targets 1`] = ` ] `; -exports[`fireBall should hit 6 targets 2`] = `120`; +exports[`fireBall should hit 6 targets 2`] = `117`; exports[`fireBall should hit 6 targets 3`] = ` "*asperiores* сотворил _Огненный шар_ на *magni* нанеся 8.72 урона -\\[ magni 🔥 💔-8.72/-5.56 📖80 -magni 🔥 💔-1.21/-1.93 📖10 -magni 🔥 💔-1.21/-3.14 📖10 -magni 🔥 💔-1.21/-4.35 📖10 -magni 🔥 💔-1.21/-5.56 📖10 ]" +\\[ magni 🔥 💔-8.72/-0.72 📖80 +labore 🔥 💔-1.21/6.79 📖10 +iure 🔥 💔-0.91/7.09 📖7 +recusandae 🔥 💔-1.2/6.8 📖10 +ipsam 🔥 💔-1.21/6.79 📖10 ]" `; diff --git a/src/arena/magics/__snapshots__/fireRain.test.ts.snap b/src/arena/magics/__snapshots__/fireRain.test.ts.snap new file mode 100644 index 0000000..d265bf5 --- /dev/null +++ b/src/arena/magics/__snapshots__/fireRain.test.ts.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`fireRain should hit clan targets 1`] = ` +[ + 8, + 2.59, + 2.59, + 3.93, + 2.63, + 2.59, + 3.97, + 8, + 8, + 8, + 8, +] +`; + +exports[`fireRain should hit clan targets 2`] = `234`; + +exports[`fireRain should hit clan targets 3`] = ` +"*asperiores* сотворил _Огненный дождь_ на *magni* нанеся 4.03 урона +\\[ magni 🔥 💔-4.03/2.59 📖40 +labore 🔥 💔-5.41/2.59 📖43 +iure 🔥 💔-4.07/3.93 📖33 +recusandae 🔥 💔-5.37/2.63 📖43 +ipsam 🔥 💔-5.41/2.59 📖43 +dignissimos 🔥 💔-4.03/3.97 📖32 ]" +`; + +exports[`fireRain should hit single target if target has no clan 1`] = ` +[ + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 2.57, + 8, + 8, +] +`; + +exports[`fireRain should hit single target if target has no clan 2`] = `51`; + +exports[`fireRain should hit single target if target has no clan 3`] = ` +"*explicabo* сотворил _Огненный дождь_ на *aperiam* нанеся 5.43 урона +\\[ aperiam 🔥 💔-5.43/2.57 📖51 ]" +`; diff --git a/src/arena/magics/fireBall.test.ts b/src/arena/magics/fireBall.test.ts index 0666917..9da100c 100644 --- a/src/arena/magics/fireBall.test.ts +++ b/src/arena/magics/fireBall.test.ts @@ -24,8 +24,8 @@ describe('fireBall', () => { await TestUtils.createClan(charIds[1], { players: charIds.slice(0, 6), }); - - await Promise.all(charIds.map(CharacterService.getCharacterById)); + TestUtils.resetCharacterCache(); + await Promise.all([initiator.id, ...charIds].map(CharacterService.getCharacterById)); game = new GameService([initiator.id, ...charIds]); game.players.players.forEach((player, index) => { diff --git a/src/arena/magics/fireRain.test.ts b/src/arena/magics/fireRain.test.ts new file mode 100644 index 0000000..cccd558 --- /dev/null +++ b/src/arena/magics/fireRain.test.ts @@ -0,0 +1,68 @@ +import casual from 'casual'; +import { times } from 'lodash'; +import CharacterService from '@/arena/CharacterService'; +import GameService from '@/arena/GameService'; +import { profsData } from '@/data/profs'; +import TestUtils from '@/utils/testUtils'; +import fireRain from './fireRain'; + +// npm t src/arena/magics/fireRain.test.ts + +describe('fireRain', () => { + let game: GameService; + + beforeAll(() => { + casual.seed(1); + }); + + beforeEach(async () => { + const harks = { ...profsData.m.hark, wis: 20 }; + const initiator = await TestUtils.createCharacter({ prof: 'm', magics: { fireRain: 3 }, harks }); + const chars = await Promise.all(times(10, () => TestUtils.createCharacter())); + const charIds = chars.map(({ id }) => id); + + await TestUtils.createClan(charIds[1], { + players: charIds.slice(0, 6), + }); + + TestUtils.resetCharacterCache(); + await Promise.all([initiator.id, ...charIds].map(CharacterService.getCharacterById)); + + game = new GameService([initiator.id, ...charIds]); + game.players.players.forEach((player, index) => { + player.resists.fire = index % 3 ? 1 : 0.75; + }); + }); + + beforeEach(() => { + jest.spyOn(global.Math, 'random').mockReturnValue(0.15); + }); + + afterEach(() => { + jest.spyOn(global.Math, 'random').mockRestore(); + }); + + it('should hit clan targets', () => { + game.players.players[0].proc = 1; + + fireRain.cast(game.players.players[0], game.players.players[1], game); + + expect( + game.players.players.map((player) => player.stats.val('hp')), + ).toMatchSnapshot(); + expect(game.players.players[0].stats.val('exp')).toMatchSnapshot(); + expect(TestUtils.normalizeRoundHistory(game.getRoundResults())).toMatchSnapshot(); + }); + + it('should hit single target if target has no clan', () => { + game.players.players[0].proc = 1; + + fireRain.cast(game.players.players[0], game.players.players[8], game); + + expect( + game.players.players.map((player) => player.stats.val('hp')), + ).toMatchSnapshot(); + expect(game.players.players[0].stats.val('exp')).toMatchSnapshot(); + expect(TestUtils.normalizeRoundHistory(game.getRoundResults())).toMatchSnapshot(); + }); +}); diff --git a/src/arena/magics/fireRain.ts b/src/arena/magics/fireRain.ts index 346efc9..ccf82be 100644 --- a/src/arena/magics/fireRain.ts +++ b/src/arena/magics/fireRain.ts @@ -1,13 +1,14 @@ -import { DmgMagic } from '../Constuructors/DmgMagicConstructor'; +import { AoeDmgMagic } from '../Constuructors/AoeDmgMagicConstructor'; +import type GameService from '../GameService'; +import type { Player } from '../PlayersService'; /** * Огненный дождь * Основное описание магии общее требовани есть в конструкторе */ -class FireRain extends DmgMagic { +class FireRain extends AoeDmgMagic { constructor() { super({ - // @ts-expect-error не используется name: 'fireRain', displayName: 'Огненный дождь', desc: 'Обрушивает на команду противника огненный дождь', @@ -25,13 +26,44 @@ class FireRain extends DmgMagic { }); } + getTargets(): Player[] { + const { target, game } = this.params; + + if (!target.clan) { + return []; + } + + const targets = game.players.getPlayersByClan(target.clan?.id) + .filter(({ alive }) => alive) + .filter(({ id }) => id !== target.id); + + return targets; + } + /** * Основная функция запуска магии - * @todo */ - run(): void { - // @ts-expect-error @todo - return this; + run(initiator: Player, target: Player, game: GameService): void { + const effect = this.effectVal(); + target.stats.down('hp', effect); + + const targets = this.getTargets(); + + targets.forEach((target) => { + this.runAoe(initiator, target, game); + }); + } + + runAoe(initiator: Player, target: Player, game: GameService) { + const effect = this.effectVal({ initiator, target, game }); + target.stats.down('hp', effect); + + this.status.expArr.push({ + id: target.id, + name: target.nick, + val: effect, + hp: target.stats.val('hp'), + }); } } diff --git a/src/arena/magics/index.ts b/src/arena/magics/index.ts index 86506a8..1aebc56 100644 --- a/src/arena/magics/index.ts +++ b/src/arena/magics/index.ts @@ -8,6 +8,7 @@ export { default as dispel } from './dispel'; export { default as eclipse } from './eclipse'; export { default as entangle } from './entangle'; export { default as frostTouch } from './frostTouch'; +export { default as fireRain } from './fireRain'; export { default as glitch } from './glitch'; export { default as lightHeal } from './lightHeal'; export { default as madness } from './madness'; diff --git a/src/utils/testUtils.ts b/src/utils/testUtils.ts index d85ce5d..1bac4cc 100644 --- a/src/utils/testUtils.ts +++ b/src/utils/testUtils.ts @@ -3,12 +3,13 @@ import type { AnyKeys } from 'mongoose'; import CharacterService from '@/arena/CharacterService'; import { ClanService } from '@/arena/ClanService'; import type { HistoryItem } from '@/arena/HistoryService'; -import { formatMessage } from '@/arena/LogService/utils'; import { profsList, profsData, type Prof } from '@/data/profs'; import { type Char, CharModel } from '@/models/character'; import { type Clan, ClanModel } from '@/models/clan'; import { InventoryModel } from '@/models/inventory'; import { type Item, ItemModel } from '@/models/item'; +import { formatMessage } from '@/arena/LogService/utils'; +import arena from '@/arena'; const functions = casual.functions(); @@ -48,6 +49,10 @@ export default class TestUtils { return CharModel.findById(id); } + static resetCharacterCache() { + arena.characters = {}; + } + static async createClan(charId: string, params?: AnyKeys) { const players = params?.players?.concat([charId]) ?? [charId]; const created = await ClanModel.create({