You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
4.7 KiB
149 lines
4.7 KiB
|
3 weeks ago
|
import type { BattleSession } from 'db://assets/mx/module/battle';
|
||
|
|
import {
|
||
|
|
AttributeComp,
|
||
|
|
BattleTeamId,
|
||
|
|
TeamComp,
|
||
|
|
TagComp,
|
||
|
|
EntityTag,
|
||
|
|
} from 'db://assets/mx/module/battle';
|
||
|
|
import type { EntityId } from 'db://assets/mx/module/ecs';
|
||
|
|
import { TableNames } from '../../game/ConfigTableData';
|
||
|
|
import { heroCharacterIds, isComboHero } from '../config/BattleHeroTable';
|
||
|
|
import { Weapon2TileType } from 'db://assets/mx/module/merge';
|
||
|
|
|
||
|
|
function _parseBaseAttr(raw: string): Array<{ type: number; value: number }> {
|
||
|
|
if (!raw) return [];
|
||
|
|
const out: Array<{ type: number; value: number }> = [];
|
||
|
|
for (const seg of raw.split(';')) {
|
||
|
|
const part = seg.trim();
|
||
|
|
if (!part) continue;
|
||
|
|
const [typeStr, valueStr] = part.split(',');
|
||
|
|
const type = Number(typeStr);
|
||
|
|
const value = Number(valueStr);
|
||
|
|
if (!Number.isFinite(type) || !Number.isFinite(value)) continue;
|
||
|
|
out.push({ type, value });
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 全局词条:已删表字段,恒 false */
|
||
|
|
export function isGlobalSkillEntry(_entry: ITableSkill): boolean {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 物化到某武器塔:词条表已删,恒空 */
|
||
|
|
export function entriesAffectingWeapon(
|
||
|
|
_skills: readonly ITableSkill[],
|
||
|
|
_weapon1Id: number,
|
||
|
|
): ITableSkill[] {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 汇总已选词条 base_attr(词条已删,恒 0) */
|
||
|
|
export function sumSelectedBaseAttrWan(
|
||
|
|
skills: readonly ITableSkill[],
|
||
|
|
attrType: number,
|
||
|
|
): number {
|
||
|
|
let sum = 0;
|
||
|
|
for (const entry of skills) {
|
||
|
|
const raw = (entry as ITableSkill & { base_attr?: string }).base_attr ?? '';
|
||
|
|
for (const { type, value } of _parseBaseAttr(raw)) {
|
||
|
|
if (type === attrType) sum += value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return sum;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 万分比加成 → 权重乘数:1000 → ×1.1 */
|
||
|
|
export function wanToWeightMul(wan: number): number {
|
||
|
|
if (wan <= 0) return 1;
|
||
|
|
return 1 + wan / 10000;
|
||
|
|
}
|
||
|
|
|
||
|
|
const GLOBAL_SLOW_SOURCE = 'global_skill_slow';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 怪物减速:base_attr type8,万分比减速量(1000=减速10%→移速×0.9)。
|
||
|
|
* 对单个敌人写入持久 Mul(无 remain)。
|
||
|
|
*/
|
||
|
|
export function applyGlobalMonsterSlowToEntity(
|
||
|
|
session: BattleSession,
|
||
|
|
entityId: EntityId,
|
||
|
|
slowWan: number,
|
||
|
|
): void {
|
||
|
|
if (slowWan <= 0) return;
|
||
|
|
const world = session.world;
|
||
|
|
if (!world.isAlive(entityId)) return;
|
||
|
|
if (!_isEnemy(world, entityId)) return;
|
||
|
|
if (!world.getComponent(entityId, AttributeComp)) {
|
||
|
|
world.addComponent(entityId, new AttributeComp());
|
||
|
|
}
|
||
|
|
const attrs = session.attributes;
|
||
|
|
const attr = world.getComponent(entityId, AttributeComp);
|
||
|
|
if (attr) {
|
||
|
|
attr.modifiers = attr.modifiers.filter((m) => m.source !== GLOBAL_SLOW_SOURCE);
|
||
|
|
}
|
||
|
|
const speedMul = Math.max(0, 1 - slowWan / 10000);
|
||
|
|
attrs.addModifier(entityId, {
|
||
|
|
attr: 'move_speed_mul',
|
||
|
|
op: 'Mul',
|
||
|
|
value: speedMul,
|
||
|
|
source: GLOBAL_SLOW_SOURCE,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 对场上全部敌人应用全局减速(选技能后立刻生效) */
|
||
|
|
export function applyGlobalMonsterSlowToAllEnemies(
|
||
|
|
session: BattleSession,
|
||
|
|
skills: readonly ITableSkill[],
|
||
|
|
): void {
|
||
|
|
const slowWan = sumSelectedBaseAttrWan(skills, 8);
|
||
|
|
if (slowWan <= 0) return;
|
||
|
|
session.world.forEachAlive((id) => {
|
||
|
|
applyGlobalMonsterSlowToEntity(session, id, slowWan);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function _isEnemy(world: BattleSession['world'], id: EntityId): boolean {
|
||
|
|
const team = world.getComponent(id, TeamComp);
|
||
|
|
if (team && BattleTeamId.isEnemyTeam(team.team)) return true;
|
||
|
|
const tags = world.getComponent(id, TagComp);
|
||
|
|
return !!tags?.tags.includes(EntityTag.Enemy);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Weapon2 是否属于英雄(herotype===2 的 characters 成员) */
|
||
|
|
export function isHeroWeapon2Id(weapon2Id: number): boolean {
|
||
|
|
const id = Number(weapon2Id);
|
||
|
|
if (!Number.isFinite(id) || id <= 0) return false;
|
||
|
|
const list = gg.data.table.getTableList<ITableHero>(TableNames.Hero) ?? [];
|
||
|
|
for (const w1 of list) {
|
||
|
|
if (!isComboHero(w1)) continue;
|
||
|
|
for (const g of heroCharacterIds(w1)) {
|
||
|
|
if (Number(g) === id) return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 背包刷新:按已选全局词条修正 Weapon2 基础权重。
|
||
|
|
* type10 英雄概率、type11 格子概率:基础 weight ×(1+万分比)。
|
||
|
|
*/
|
||
|
|
export function applyBagRefreshWeightBonus(
|
||
|
|
baseWeight: number,
|
||
|
|
weapon2Type: number,
|
||
|
|
weapon2Id: number,
|
||
|
|
skills: readonly ITableSkill[],
|
||
|
|
): number {
|
||
|
|
if (baseWeight <= 0 || !skills.length) return baseWeight;
|
||
|
|
let wan = 0;
|
||
|
|
if (Number(weapon2Type) === Weapon2TileType.Grid) {
|
||
|
|
wan += sumSelectedBaseAttrWan(skills, 11);
|
||
|
|
}
|
||
|
|
if (isHeroWeapon2Id(weapon2Id)) {
|
||
|
|
wan += sumSelectedBaseAttrWan(skills, 10);
|
||
|
|
}
|
||
|
|
if (wan <= 0) return baseWeight;
|
||
|
|
return baseWeight * wanToWeightMul(wan);
|
||
|
|
}
|