Skip to content

Commit

Permalink
feat: new item sets (#341)
Browse files Browse the repository at this point in the history
* wip items sets

* add modifiers

* enable all magics
  • Loading branch information
kyvg authored Nov 21, 2024
1 parent 28694eb commit 4a5585d
Show file tree
Hide file tree
Showing 34 changed files with 491 additions and 244 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"start": "NODE_ENV=production bun src/fwo.ts",
"dev": "bun src/cli/dev.ts",
"test": "bun src/cli/test.ts -i",
"generate:items": "bun src/cli/generateItems.ts",
"reset-harks": "bun src/cli/resetHarks.ts",
"lint": "biome lint",
"format": "biome format",
Expand Down
17 changes: 17 additions & 0 deletions src/arena/ActionService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import arena from '@/arena';

export type Action = keyof typeof arena.actions;

export class ActionService {
static isAction(maybeAction: string): maybeAction is Action {
return maybeAction in arena.actions;
}

static isBaseAction(action: string) {
if (ActionService.isAction(action)) {
return arena.actions[action].lvl === 0;
}

return false;
}
}
1 change: 1 addition & 0 deletions src/arena/Constuructors/BaseAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export abstract class BaseAction {
orderType: OrderType;
actionType: ActionType;
effectType?: DamageType;
lvl: number;

context: BaseActionContext;
params: BaseActionParams;
Expand Down
4 changes: 2 additions & 2 deletions src/arena/Constuructors/MagicConstructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ export abstract class Magic extends AffectableAction {
const { initiator, target } = this.params;
const initiatorMagicLvl = initiator.magics[this.name];
const imc = initiator.modifiers.castChance; // мод шанс прохождения
const castChance = initiator.castChance?.[this.name] ?? 0; // мод action'а
const failChance = target.failChance?.[this.name] ?? 0;
const castChance = initiator.getCastChance(this.name); // мод action'а
const failChance = target.getFailChance(this.name);
let chance = this.chance[initiatorMagicLvl - 1];
if (typeof chance === 'string') {
chance = MiscService.dice(chance);
Expand Down
39 changes: 26 additions & 13 deletions src/arena/Constuructors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ export type OrderType = 'all' | 'any' | 'enemy' | 'self' | 'team' | 'teamExceptS
export type AOEType = 'target' | 'team';
export type DamageType = 'acid' | 'fire' | 'lighting' | 'frost' | 'physical' | 'clear';
export type BreaksMessage =
'NO_INITIATOR' |
'NO_TARGET' |
'NO_MANA' |
'NO_ENERGY' |
'CHANCE_FAIL' |
'GOD_FAIL' |
'HEAL_FAIL' |
'SKILL_FAIL' |
'PHYS_FAIL' |
'NO_WEAPON';
| 'NO_INITIATOR'
| 'NO_TARGET'
| 'NO_MANA'
| 'NO_ENERGY'
| 'CHANCE_FAIL'
| 'GOD_FAIL'
| 'HEAL_FAIL'
| 'SKILL_FAIL'
| 'PHYS_FAIL'
| 'NO_WEAPON';

export type ExpArr = {
initiator: Player;
Expand Down Expand Up @@ -51,14 +51,27 @@ export type SuccessArgs = {
effect: number;
hp: number;
expArr: ExpArr;
weapon: Item | undefined
weapon: Item | undefined;
effectType?: DamageType;
orderType: OrderType;
affects?: SuccessArgs[];
msg?: CustomMessageFn;
}
};

export type ActionType = 'magic' | 'dmg-magic' | 'dmg-magic-long' | 'aoe-dmg-magic' | 'magic-long' | 'skill' | 'phys' | 'heal-magic' | 'heal' | 'protect' | 'dodge' | 'passive';
export type ActionType =
| 'magic'
| 'dmg-magic'
| 'dmg-magic-long'
| 'aoe-dmg-magic'
| 'magic-long'
| 'skill'
| 'phys'
| 'heal-magic'
| 'heal'
| 'protect'
| 'dodge'
| 'passive'
| 'regeneration';

export interface FailArgs {
actionType: ActionType;
Expand Down
69 changes: 69 additions & 0 deletions src/arena/EngineService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import arena from '@/arena';
import { type Action, ActionService } from '@/arena/ActionService';
import type Game from '@/arena/GameService';
import config from '@/arena/config';
import _ from 'lodash';

type Stages = Readonly<(Action | Readonly<Action[]>)[]>;

/**
* @param stages массив строк
* @param game объект игры
* @return измененный объект игры
*/
function runStage(stages: Stages, game: Game) {
const ordersByAction = Object.groupBy(game.orders.ordersList, ({ action }) => action);
stages.forEach((stage) => {
if (typeof stage !== 'string') {
runStage(stage, game);
return;
}

console.log('stage run:', stage);
if (!ActionService.isAction(stage)) {
console.log('stage fail (unknown action):', stage);
return;
}
const action = arena.actions[stage];
const orders = ordersByAction[stage];
if (!orders) {
return;
}

orders.forEach((order) => {
const initiator = game.players.getById(order.initiator);
const target = game.players.getById(order.target);
if (initiator && target) {
// FIXME прокидывать проценты в action вместо initiator
initiator.setProc(order.proc / 100);
action.cast(initiator, target, game);
} else {
console.log('stage fail (no player):', initiator?.id, target?.id);
}
});

if ('castLong' in action) {
action.castLong(game);
}
});
return game;
}

/**
* Engine
* Модуль обработки боя
* @todo wip
*/

/**
* @param game Объект игры
*/
export function engine(game: Game): Game | undefined {
try {
return runStage(config.stages, game);
} catch (e) {
console.debug(e);
} finally {
console.info('engine done');
}
}
2 changes: 1 addition & 1 deletion src/arena/GameService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Profs } from '../data';
import * as channelHelper from '../helpers/channelHelper';
import type { Game } from '../models/game';
import type { LongItem } from './Constuructors/LongMagicConstructor';
import { engine } from './engineService';
import { engine } from './EngineService';
import { HistoryService, type HistoryItem } from './HistoryService';
import { LogService } from './LogService';
import type * as magics from './magics';
Expand Down
51 changes: 45 additions & 6 deletions src/arena/InventoryService.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import _ from 'lodash';
import { getItems, addItem, removeItem, putOnItem, putOffItem } from '@/api/inventory';
import { addItem, getItems, putOffItem, putOnItem, removeItem } from '@/api/inventory';
import arena from '@/arena';
import ValidationError from '@/arena/errors/ValidationError';
import type { Char } from '@/models/character';
import type { InventoryDocument } from '@/models/inventory';
import type { Inventory } from '@/schemas/inventory';
import { assignWithSum } from '@/utils/assignWithSum';
import { itemAttributesSchema, type ItemAttributes } from '@/schemas/item/itemAttributesSchema';
import type { Item } from '@/schemas/item';
import { type ItemAttributes, itemAttributesSchema } from '@/schemas/item/itemAttributesSchema';
import type { ItemSet } from '@/schemas/itemSet';
import type { Modifiers } from '@/schemas/shared/modifiers';
import { assignWithSum } from '@/utils/assignWithSum';
import _ from 'lodash';

export class InventoryService {
harksFromItems: ItemAttributes;
Expand All @@ -23,10 +25,43 @@ export class InventoryService {
return inventory.map(({ code }) => arena.items[code]);
}

static getItemsSetsByInventory(inventory: InventoryDocument[], full = true): ItemSet[] {
const codes = new Set(inventory.map(({ code }) => code));
return Object.values(arena.itemsSets).filter(({ items }) => {
if (full) {
return items.every((item) => codes.has(item));
}

return items.some((item) => codes.has(item));
});
}

static getItemSetsModifiersByInventory(inventory: InventoryDocument[]): Modifiers[] {
const itemsSets = InventoryService.getItemsSetsByInventory(inventory, false);
const codes = new Set(inventory.map(({ code }) => code));
const modifiers: Modifiers[] = [];

for (const itemSet of itemsSets) {
const itemsCount = itemSet.items.reduce((sum, item) => (codes.has(item) ? sum + 1 : sum), 0);

for (const modifier of itemSet.modifiers) {
if (modifier.itemsRequired <= itemsCount) {
modifiers.push(modifier);
}
}
}

return modifiers;
}

get attributes() {
return this.harksFromItems.attributes;
}

get modifiers() {
return InventoryService.getItemSetsModifiersByInventory(this.inventory);
}

getItem(itemId) {
return this.inventory.find((item) => item._id.equals(itemId));
}
Expand Down Expand Up @@ -88,7 +123,7 @@ export class InventoryService {
await this.updateHarkFromItems();

const items = this.getEquippedItems().filter(
(i) => !this.hasRequiredAttributes(arena.items[i.code]),
({ code }) => !this.hasRequiredAttributes(arena.items[code]),
);
if (items.length) {
const putOffItems = items.map((i) => putOffItem({ charId: this.char.id, itemId: i.id }));
Expand All @@ -110,7 +145,11 @@ export class InventoryService {
const itemAttriributes = itemAttributesSchema.array().parse(items);
const harksFromItems = itemAttriributes.reduce(assignWithSum, itemAttributesSchema.parse({}));

this.harksFromItems = harksFromItems;
const itemsSets = InventoryService.getItemsSetsByInventory(this.getEquippedItems());
const itemsSetsAttriributes = itemAttributesSchema.array().parse(itemsSets);
const harksFromItemsSets = itemsSetsAttriributes.reduce(assignWithSum, harksFromItems);

this.harksFromItems = harksFromItemsSets;
return harksFromItems;
}

Expand Down
10 changes: 5 additions & 5 deletions src/arena/OrderService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import arena from '@/arena';
import { ActionService } from '@/arena/ActionService';
import { RoundStatus } from '@/arena/RoundService';
import OrderError from '@/arena/errors/OrderError';
import _ from 'lodash';
import * as actions from './actions';
import OrderError from './errors/OrderError';
import { RoundStatus } from './RoundService';
import arena from './index';

export interface Order {
initiator: string;
Expand All @@ -29,7 +29,7 @@ export default class Orders {
* @desc Проверка доступно ли действие для персонажа
*/
static isValidAct({ initiator, action }: Order): boolean {
if (actions[action]) {
if (ActionService.isBaseAction(action)) {
return true;
}
const { skills, magics } = arena.characters[initiator];
Expand Down
40 changes: 16 additions & 24 deletions src/arena/PlayersService/Player.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import arena from '@/arena';
import type { Action } from '@/arena/ActionService';
import type { CharacterService } from '@/arena/CharacterService';
import FlagsConstructor from '@/arena/Constuructors/FlagsConstructor';
import type { DamageType } from '@/arena/Constuructors/types';
import type * as magics from '@/arena/magics';
import type { Stats } from '@/arena/StatsService';
import StatsService from '@/arena/StatsService';
import type { Prof } from '@/data/profs';
import type { Clan } from '@/models/clan';
import { PlayerWeapon } from './PlayerWeapon';
import { convertItemModifiers } from './utils';

export type Resists = Record<DamageType, number>;

export interface Chance {
fail?: Partial<Record<keyof typeof magics, number>>
cast?: Partial<Record<keyof typeof magics, number>>
fail: Partial<Record<Action, number>>;
cast: Partial<Record<Action, number>>;
}

/**
Expand All @@ -38,9 +39,7 @@ export default class Player {
stats: StatsService;
flags: FlagsConstructor;
modifiers: {
magics: {
chance: Chance;
}
chance: Chance;
castChance: number;
};

Expand All @@ -62,17 +61,10 @@ export default class Player {
this.favoriteMagics = params.favoriteMagicList;
this.stats = new StatsService(params.dynamicAttributes);
this.flags = new FlagsConstructor();
// @todo закладка для вычисляемых статов
this.modifiers = {
magics: {
chance: {
fail: {},
cast: {},
},
},
chance: convertItemModifiers(params.inventory.modifiers),
castChance: 0,
}; // Объект
// модификаторов
}; // Объект модификаторов
this.resists = {}; // Объект резистов
this.skills = params.skills || {}; // Обькт доступных скилов
this.magics = params.magics || {}; // объект изученых магий
Expand All @@ -89,12 +81,12 @@ export default class Player {
return new Player(arena.characters[charId]);
}

get failChance(): Chance['fail'] {
return this.modifiers.magics.chance.fail ?? {};
getFailChance(action: Action) {
return this.modifiers.chance.fail[action] ?? 0;
}

get castChance(): Chance['cast'] {
return this.modifiers.magics.chance.cast ?? {};
getCastChance(action: Action) {
return this.modifiers.chance.cast[action] ?? 0;
}

/**
Expand All @@ -118,16 +110,16 @@ export default class Player {
}

/**
* Возвращает убийцу игрока если он записан
*/
* Возвращает убийцу игрока если он записан
*/
getKiller(): string {
return this.flags.isDead;
}

/**
* Устанавливает убийцу игрока
* @param player записывает id убийцы
*/
* Устанавливает убийцу игрока
* @param player записывает id убийцы
*/
setKiller(player: Player): void {
this.flags.isDead = player.id;
}
Expand Down
Loading

0 comments on commit 4a5585d

Please sign in to comment.