Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement preaffect interface in protect class #308

Merged
merged 3 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 18 additions & 51 deletions src/arena/Constuructors/PhysConstructor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const { floatNumber } = require('../../utils/floatNumber');
const MiscService = require('../MiscService');
const { dodge, parry, disarm } = require('../skills');
const { isSuccessResult } = require('./utils');

/**
Expand Down Expand Up @@ -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;
Expand All @@ -40,7 +39,7 @@ class PhysConstructor { /**
/**
* @type {import('./PreAffect').PreAffect[]}
* */
this.preAffects = preAffects;
this.preAffects = [];
}

/**
Expand All @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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();
}

/**
Expand Down Expand Up @@ -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;
5 changes: 4 additions & 1 deletion src/arena/Constuructors/PreAffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 3 additions & 1 deletion src/arena/Constuructors/types.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -88,7 +89,8 @@ export type SuccessArgs =
SkillNext |
PhysNext |
HealMagicNext |
HealNext;
HealNext |
ProtectNext;

export type ActionType = SuccessArgs['actionType'];

Expand Down
2 changes: 2 additions & 0 deletions src/arena/LogService/utils/format-cause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}*`;
}
Expand Down
2 changes: 2 additions & 0 deletions src/arena/LogService/utils/format-exp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down
110 changes: 84 additions & 26 deletions src/arena/actions/protect.ts
Original file line number Diff line number Diff line change
@@ -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<BaseNext, 'exp'> & {
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<PreAffect['check']>): ReturnType<PreAffect['check']> {
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,
};
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/fwo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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());
Expand Down
14 changes: 14 additions & 0 deletions src/utils/registerAffects.ts
Original file line number Diff line number Diff line change
@@ -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();
};
Loading