HumanBreak/src/game/state/hero.ts

345 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { logger } from '@motajs/common';
import { EventEmitter } from 'eventemitter3';
import { cloneDeep } from 'lodash-es';
import { HeroSkill, NightSpecial } from '../mechanism/misc';
/**
* 获取勇士在某一点的属性
* @param name 要获取的勇士属性
* @param floorId 勇士所在楼层
*/
export function getHeroStatusOn(name: 'all', floorId?: FloorIds): HeroStatus;
export function getHeroStatusOn(
name: (keyof HeroStatus)[],
floorId?: FloorIds
): Partial<HeroStatus>;
export function getHeroStatusOn<K extends keyof HeroStatus>(
name: K,
floorId?: FloorIds
): HeroStatus[K];
export function getHeroStatusOn(
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
floorId?: FloorIds
) {
// @ts-ignore
return getHeroStatusOf(core.status.hero, name, floorId);
}
/**
* 获取一定状态下的勇士在某一点的属性
* @param status 勇士的状态
* @param name 要获取的勇士属性
* @param floorId 勇士所在楼层
*/
export function getHeroStatusOf(
status: Partial<HeroStatus>,
name: 'all',
floorId?: FloorIds
): HeroStatus;
export function getHeroStatusOf(
status: Partial<HeroStatus>,
name: (keyof HeroStatus)[],
floorId?: FloorIds
): Partial<HeroStatus>;
export function getHeroStatusOf<K extends keyof HeroStatus>(
status: Partial<HeroStatus>,
name: K,
floorId?: FloorIds
): HeroStatus[K];
export function getHeroStatusOf(
status: DeepPartial<HeroStatus>,
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
floorId?: FloorIds
) {
return getRealStatus(status, name, floorId);
}
function getRealStatus(
status: DeepPartial<HeroStatus>,
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
floorId: FloorIds = core.status.floorId
): any {
const { getSkillLevel } = Mota.Plugin.require('skillTree_g');
if (name instanceof Array) {
const res: any = {};
name.forEach(v => {
res[v] = getRealStatus(status, v, floorId);
});
return res;
}
if (name === 'all') {
const res: any = {};
for (const [key, value] of Object.entries(core.status.hero)) {
if (typeof value === 'number') {
res[key] = getRealStatus(
status,
key as keyof HeroStatus,
floorId
);
} else {
res[key] = value;
}
}
return res;
}
let s = (status[name] ?? core.status.hero[name]) as number;
if (s === null || s === void 0) {
throw new ReferenceError(
`Wrong hero status property name is delivered: ${name}`
);
}
if (typeof s !== 'number') return s;
// 永夜、极昼
if (name === 'atk' || name === 'def') {
s += NightSpecial.getNight(floorId);
}
const enabled = HeroSkill.getEnabled();
// 技能
if (enabled === HeroSkill.Blade) {
const level = getSkillLevel(2);
if (name === 'atk') {
s *= 1 + 0.1 * level;
} else if (name === 'def') {
s *= 1 - 0.1 * level;
}
} else if (enabled === HeroSkill.Shield) {
const level = getSkillLevel(10);
if (name === 'atk') {
s *= 1 - 0.1 * level;
} else if (name === 'def') {
s *= 1 + 0.1 * level;
}
}
// buff
s *= core.status.hero.buff[name] ?? 1;
s = Math.floor(s);
return s;
}
export interface IHeroStatusDefault {
atk: number;
def: number;
hp: number;
}
interface HeroStateEvent {
set: [key: string | number | symbol, value: any];
}
type HeroStatusCalculate = (
hero: HeroState<any>,
key: string | number | symbol,
value: any
) => any;
export class HeroState<
T extends object = IHeroStatusDefault
> extends EventEmitter<HeroStateEvent> {
readonly status: T;
readonly computedStatus: T;
readonly buffable: Set<keyof T> = new Set();
readonly buffMap: Map<keyof T, number> = new Map();
private static cal: HeroStatusCalculate = (_0, _1, value) => value;
constructor(init: T) {
super();
this.status = init;
this.computedStatus = cloneDeep(init);
}
/**
* 设置某个属性的值
* @param key 要设置的属性
* @param value 属性值
* @returns 是否设置成功
*/
setStatus<K extends keyof T>(key: K, value: T[K]): boolean {
this.status[key] = value;
this.emit('set', key, value);
return this.refreshStatus(key);
}
/**
* 增加或减少一个属性的值,只对数字型的属性有效
* @param key 要修改的属性
* @param value 属性的增量
* @returns 是否设置成功
*/
addStatus<K extends SelectKey<T, number>>(key: K, value: number): boolean {
if (typeof this.status[key] !== 'number') {
logger.warn(14, String(key));
return false;
}
return this.setStatus<K>(key, (this.status[key] + value) as T[K]);
}
/**
* 获取某个属性的原始值
* @param key 要获取的属性
* @returns 属性的值
*/
getStatus<K extends keyof T>(key: K): T[K] {
return this.status[key];
}
/**
* 获取一个属性计算后的值也就是2.x所说的勇士真实属性
* @param key 要获取的属性值
*/
getComputedStatus<K extends keyof T>(key: K): T[K] {
return this.computedStatus[key];
}
/**
* 标记某个属性为可以被buff加成
*/
markBuffable(key: SelectKey<T, number>): void {
if (typeof this.status[key] !== 'number') {
logger.warn(12, String(key));
return;
}
this.buffable.add(key);
this.buffMap.set(key, 1);
}
/**
* 设置某个属性的buff值
* @param key 要设置buff的属性
* @param value buff值
* @returns 是否设置成功
*/
setBuff(key: SelectKey<T, number>, value: number): boolean {
if (!this.buffable.has(key) || typeof this.status[key] !== 'number') {
logger.warn(13, String(key));
return false;
}
this.buffMap.set(key, value);
return this.refreshStatus(key);
}
/**
* 增加或减少一个buff值
* @param key 要修改的buff属性
* @param value buff增量
* @returns 是否修改成功
*/
addBuff(key: SelectKey<T, number>, value: number): boolean {
if (!this.buffable.has(key) || typeof this.status[key] !== 'number') {
logger.warn(13, String(key));
return false;
}
return this.setBuff(key, this.buffMap.get(key)! + value);
}
/**
* 刷新某个或所有属性,重新进行计算
* @param key 要刷新的属性名,不填表示刷新所有属性
* @returns 是否计算成功
*/
refreshStatus(key?: keyof T): boolean {
if (key === void 0) {
for (const [key, value] of Object.entries(this.status)) {
// @ts-ignore
this.computedStatus[key] = HeroState.cal(this, key, value);
}
return true;
}
this.computedStatus[key] = HeroState.cal(this, key, this.status[key]);
return true;
}
/**
* 计算所有可以buff加成的属性
* @returns 是否计算成功
*/
refreshBuffable(): boolean {
for (const key of this.buffable) {
this.computedStatus[key] = HeroState.cal(
this,
key,
this.status[key]
);
}
return true;
}
/**
* 复写属性计算函数,默认函数不进行计算,直接将原属性返回
* @param fn 计算函数传入两个参数key表示属性名value表示属性值返回值表示计算结果
*/
static overrideCalculate(fn: HeroStatusCalculate) {
this.cal = fn;
}
}
interface IHeroItem {
items: Map<AllIdsOf<'items'>, number>;
/**
* 设置勇士拥有的物品数量
* @param item 物品id
* @param value 物品数量
* @returns 是否设置成功
*/
setItem(item: AllIdsOf<'items'>, value: number): boolean;
/**
* 增加或减少勇士拥有的物品数量
* @param item 物品id
* @param value 物品数量增量
* @returns 是否设置成功
*/
addItem(item: AllIdsOf<'items'>, value: number): boolean;
/**
* 使用一个物品
* @param item 物品id
* @returns 是否使用成功
*/
useItem(item: AllIdsOf<'items'>, x?: number, y?: number): boolean;
/**
* 获得一个物品
* @param item 物品id
* @param num 获得的数量
*/
getItem(item: AllIdsOf<'items'>, num: number): void;
/**
* 获得一个物品
* @param item 物品id
* @param x 物品所在x坐标
* @param y 物品所在y坐标
* @param floorId 物品所在楼层
* @param num 获得的数量
*/
getItem(
item: AllIdsOf<'items'>,
x: number,
y: number,
floorId?: FloorIds,
num?: number
): void;
/**
* 获取某个物品的数量
* @param item 物品id
*/
itemCount(item: AllIdsOf<'items'>): number;
/**
* 判断勇士是否拥有这个物品
* @param item 物品id
*/
hasItem(item: AllIdsOf<'items'>): boolean;
}