From 49296d3c3e5f273e4ab6e66b4c1d9009b522a9ac Mon Sep 17 00:00:00 2001 From: Evgeny Kirpichyov <47474371+kyvg@users.noreply.github.com> Date: Tue, 26 Dec 2023 10:40:15 +0200 Subject: [PATCH] implement preaffect interface in protect class (#308) --- src/arena/Constuructors/PhysConstructor.js | 69 ++++--------- src/arena/Constuructors/PreAffect.ts | 5 +- src/arena/Constuructors/types.ts | 4 +- src/arena/LogService/utils/format-cause.ts | 2 + src/arena/LogService/utils/format-exp.ts | 2 + src/arena/actions/protect.ts | 110 ++++++++++++++++----- src/fwo.ts | 3 + src/utils/registerAffects.ts | 14 +++ 8 files changed, 130 insertions(+), 79 deletions(-) create mode 100644 src/utils/registerAffects.ts diff --git a/src/arena/Constuructors/PhysConstructor.js b/src/arena/Constuructors/PhysConstructor.js index 8f7ad0da..bbce4c3e 100644 --- a/src/arena/Constuructors/PhysConstructor.js +++ b/src/arena/Constuructors/PhysConstructor.js @@ -1,6 +1,5 @@ const { floatNumber } = require('../../utils/floatNumber'); const MiscService = require('../MiscService'); -const { dodge, parry, disarm } = require('../skills'); const { isSuccessResult } = require('./utils'); /** @@ -30,7 +29,7 @@ class PhysConstructor { /** * * @param {import('./PreAffect').PreAffect[]} preAffects */ - constructor(atkAct, preAffects = [dodge, parry, disarm]) { + constructor(atkAct) { this.name = atkAct.name; this.displayName = atkAct.displayName; this.desc = atkAct.desc; @@ -40,7 +39,7 @@ class PhysConstructor { /** /** * @type {import('./PreAffect').PreAffect[]} * */ - this.preAffects = preAffects; + this.preAffects = []; } /** @@ -57,10 +56,11 @@ class PhysConstructor { /** this.status = { hit: 0, exp: 0 }; try { this.fitsCheck(); + this.calculateHit(); this.checkPreAffects(); this.isBlurredMind(); - this.protectCheck(); - // тут должен идти просчет дефа + this.applyHit(); + this.checkPostEffect(); this.checkTargetIsDead(); this.next(); @@ -76,7 +76,7 @@ class PhysConstructor { /** if (this.params.game.flags.global.isEclipsed) throw this.breaks('ECLIPSE'); this.preAffects.forEach((preAffect) => { - const result = preAffect.check(this.params); + const result = preAffect.check(this.params, { value: this.status.hit }); if (result && isSuccessResult(result)) { throw this.breaks(result.message, result); @@ -116,32 +116,24 @@ class PhysConstructor { /** * Если проверка провалена, выставляем флаг isHited, означающий что * атака прошла */ - protectCheck() { - const { initiator, target } = this.params; - const atc = initiator.stats.val('patk') * initiator.proc; - const prt = target.flags.isProtected.length > 0 ? target.stats.val('pdef') : 0.1; - const at = floatNumber(Math.round(atc / prt)); - console.log('at', at); - const r = MiscService.rndm('1d100'); - // const c = Math.round(Math.sqrt(at) + (10 * at) + 5); - // new formula for phys attack chance - const c = 20 * at + 50; - const result = c > r; - console.log('left', c, 'right', r, 'result', result); + calculateHit() { + const { initiator } = this.params; + const initiatorHitParam = initiator.stats.val('hit'); const hitval = MiscService.randInt( initiatorHitParam.min, initiatorHitParam.max, ); this.status.hit = floatNumber(hitval * initiator.proc); - if (result) { - this.params.target.flags.isHited = { - initiator: initiator.nick, val: this.status.hit, - }; - this.run(); - } else { - throw this.protectorsGetExp(); - } + } + + applyHit() { + const { initiator } = this.params; + + this.params.target.flags.isHited = { + initiator: initiator.nick, val: this.status.hit, + }; + this.run(); } /** @@ -224,31 +216,6 @@ class PhysConstructor { /** initiator.stats.mode('up', 'exp', this.status.exp); } } - - /** - * Функция выдающая exp для каждого протектора в зависимости от его защиты - * @todo тут скорее всего бага, которая будет давать по 5 раз всем защищающим. - * Экспу за протект нужно выдавать в отдельном action'е который будет идти - * за протектом - */ - protectorsGetExp() { - if (!this.params) return; - const { initiator, target, game } = this.params; - const pdef = target.stats.val('pdef'); // общий показатель защиты цели - /** @type {import('./types').ExpArr} */ - const expArr = target.flags.isProtected.map((flag) => { - const defender = game.players.getById(flag.initiator); - if (defender.id === initiator.id || !game.isPlayersAlly(defender, target)) { - return { name: defender.nick, exp: 0 }; - } - const protect = Math.floor(flag.val * 100) / pdef; - const exp = Math.round(this.status.hit * 0.8 * protect); - defender.stats.up('exp', exp); - return { name: defender.nick, exp }; - }); - - return { ...this.breaks('DEF'), expArr }; - } } module.exports = PhysConstructor; diff --git a/src/arena/Constuructors/PreAffect.ts b/src/arena/Constuructors/PreAffect.ts index f21f5122..1f6bf013 100644 --- a/src/arena/Constuructors/PreAffect.ts +++ b/src/arena/Constuructors/PreAffect.ts @@ -3,5 +3,8 @@ import type { Player } from '../PlayersService'; import type { SuccessArgs } from './types'; export interface PreAffect { - check(params: { initiator: Player, target: Player, game: GameService}): SuccessArgs | void + check( + params: { initiator: Player, target: Player, game: GameService}, + status: { value: number } + ): SuccessArgs | void } diff --git a/src/arena/Constuructors/types.ts b/src/arena/Constuructors/types.ts index 23aaab3f..73d5ac5c 100644 --- a/src/arena/Constuructors/types.ts +++ b/src/arena/Constuructors/types.ts @@ -1,5 +1,6 @@ /* eslint-disable no-use-before-define */ import type { Item } from '../../models/item'; +import type { ProtectNext } from '../actions/protect'; import type GameService from '../GameService'; import type { Player } from '../PlayersService'; import type { AoeDmgMagicNext } from './AoeDmgMagicConstructor'; @@ -88,7 +89,8 @@ export type SuccessArgs = SkillNext | PhysNext | HealMagicNext | - HealNext; + HealNext | + ProtectNext; export type ActionType = SuccessArgs['actionType']; diff --git a/src/arena/LogService/utils/format-cause.ts b/src/arena/LogService/utils/format-cause.ts index 3ae99077..d42bf84d 100644 --- a/src/arena/LogService/utils/format-cause.ts +++ b/src/arena/LogService/utils/format-cause.ts @@ -4,6 +4,8 @@ export function formatCause(cause: SuccessArgs) { switch (cause.actionType) { case 'skill': return `_${cause.action}_ *${cause.initiator}*: 📖${cause.exp}`; + case 'protect': + return `_${cause.action}_ ${cause.expArr.map(({ name, exp }) => `*${name}*: 📖${exp}`)}`; default: return `_${cause.action}_ *${cause.initiator}*`; } diff --git a/src/arena/LogService/utils/format-exp.ts b/src/arena/LogService/utils/format-exp.ts index 47d14bb4..9e64831d 100644 --- a/src/arena/LogService/utils/format-exp.ts +++ b/src/arena/LogService/utils/format-exp.ts @@ -24,6 +24,8 @@ export function formatExp(args: SuccessArgs): string { } case 'heal': return expBrackets(args.expArr.map(({ name, exp, val }) => `${name}: 💖${val}/${args.hp} 📖${exp}`).join(', ')); + case 'protect': + return expBrackets(args.expArr.map(({ name, exp }) => `${name}: 📖${exp}`).join(', ')); case 'phys': return expBrackets(`💔-${args.dmg}/${args.hp} 📖${args.exp}`); case 'skill': diff --git a/src/arena/actions/protect.ts b/src/arena/actions/protect.ts index d8ead974..8b2e7025 100644 --- a/src/arena/actions/protect.ts +++ b/src/arena/actions/protect.ts @@ -1,41 +1,99 @@ -import type { OrderType } from '../Constuructors/types'; -import type Game from '../GameService'; -import type { Player } from '../PlayersService'; +import { PreAffect } from '@/arena/Constuructors/PreAffect'; +import type { BaseNext, ExpArr, OrderType } from '@/arena/Constuructors/types'; +import type Game from '@/arena/GameService'; +import MiscService from '@/arena/MiscService'; +import type { Player } from '@/arena/PlayersService'; +import { floatNumber } from '@/utils/floatNumber'; + +export type ProtectNext = Omit & { + actionType: 'protect' + expArr: ExpArr; +} /** * Класс защиты */ +class Protect implements PreAffect { + name = 'protect'; + displayName = 'Защита'; + desc = 'Защита от физических атак'; + lvl = 0; + orderType: OrderType = 'all'; -class Protect { - name: string; - displayName: string; - desc: string; - lvl: number; - orderType: OrderType; /** - * Изменяем протект цели, создаем custom run + * Каст протекта + * @param initiator Объект кастера + * @param target Объект цели + * @param [game] Объект игры */ - constructor() { - this.name = 'protect'; - this.displayName = 'Защита'; - this.desc = 'Защита от физических атак'; - this.lvl = 0; - this.orderType = 'all'; + cast(initiator: Player, target: Player, _game: Game) { + const protectValue = initiator.stats.val('pdef') * initiator.proc; + + target.stats.up('pdef', protectValue); + target.flags.isProtected.push({ + initiator: initiator.id, val: protectValue, + }); + } + + check(...[params, status]: Parameters): ReturnType { + const { initiator, target, game } = params; + const attackValue = initiator.stats.val('patk') * initiator.proc; + const protectValue = target.flags.isProtected.length > 0 ? target.stats.val('pdef') : 0.1; + const ratio = floatNumber(Math.round(attackValue / protectValue)); + console.log('at', ratio); + + const randomValue = MiscService.rndm('1d100'); + const chance = 20 * ratio + 50; + const result = chance > randomValue; + console.log('chance', chance, 'random', randomValue, 'result', result); + + if (!result) { + return this.getSuccessResult({ initiator, target, game }, status.value); + } } /** - * Каст протекта - * @param {player} initiator Объект кастера - * @param {player} target Объект цели - * @param {game} [game] Объект игры + * Функция выдающая exp для каждого протектора в зависимости от его защиты + * @todo тут скорее всего бага, которая будет давать по 5 раз всем защищающим. + * Экспу за протект нужно выдавать в отдельном action'е который будет идти + * за протектом */ - // eslint-disable-next-line class-methods-use-this - cast(initiator: Player, target: Player, _game: Game) { - const tect = initiator.stats.val('pdef') * initiator.proc; - target.stats.up('pdef', tect); - target.flags.isProtected.push({ - initiator: initiator.id, val: tect, + getSuccessResult( + params: { initiator: Player; target: Player; game: Game; }, + expMultiplier: number, + ): ProtectNext { + const { initiator, target, game } = params; + const pdef = target.stats.val('pdef'); // общий показатель защиты цели + + const expArr: ExpArr = target.flags.isProtected.map((flag) => { + const defender = game.players.getById(flag.initiator) as Player; + + if (defender.id === initiator.id || !game.isPlayersAlly(defender, target)) { + return { + id: defender.id, + name: defender.nick, + exp: 0, + }; + } + + const protect = Math.floor(flag.val * 100) / pdef; + const exp = Math.round(expMultiplier * 0.8 * protect); + defender.stats.up('exp', exp); + + return { + id: defender.id, + name: defender.nick, + exp, + }; }); + + return { + expArr, + action: this.displayName, + actionType: 'protect', + target: target.nick, + initiator: initiator.nick, + }; } } diff --git a/src/fwo.ts b/src/fwo.ts index b6b2eb1c..a481869f 100644 --- a/src/fwo.ts +++ b/src/fwo.ts @@ -12,6 +12,7 @@ import * as middlewares from './middlewares'; import { connect } from './models'; import { ItemModel } from './models/item'; import { stage } from './scenes/stage'; +import { registerAffects } from './utils/registerAffects'; interface BotSession extends Scenes.SceneSession { character: Char; @@ -34,6 +35,8 @@ arena.magics = magics; arena.skills = skills; arena.actions = actions; +registerAffects(); + bot.use(session()); bot.use(stage.middleware()); bot.use(middlewares.chatMiddleware()); diff --git a/src/utils/registerAffects.ts b/src/utils/registerAffects.ts new file mode 100644 index 00000000..c65c8b2f --- /dev/null +++ b/src/utils/registerAffects.ts @@ -0,0 +1,14 @@ +import arena from '@/arena'; + +const registerAttackAffects = () => { + arena.actions.attack.preAffects = [ + arena.actions.protect, + arena.skills.dodge, + arena.skills.parry, + arena.skills.disarm, + ]; +}; + +export const registerAffects = () => { + registerAttackAffects(); +};