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.
675 lines
25 KiB
675 lines
25 KiB
|
3 weeks ago
|
import { DailyShopDataManager } from '../DailyShopDataManager';
|
||
|
|
import type { DailyShopParsed } from '../DailyShopTypes';
|
||
|
|
import { BattleBuffFunctionType, passiveBuffUsesBattleGameCd } from './BattleBuffTypes';
|
||
|
|
|
||
|
|
export { passiveBuffUsesBattleGameCd };
|
||
|
|
import {
|
||
|
|
parseBattleBuffBaseThunderConfig,
|
||
|
|
parseBattleBuffKillPeachConfig,
|
||
|
|
parseFunctionParNums,
|
||
|
|
type BattleBuffBaseThunderConfig,
|
||
|
|
} from './BattleBuffParamParse';
|
||
|
|
|
||
|
|
/** 召神令被动建池结果 */
|
||
|
|
export type BattleBuffHeroSummonRatePoolAdjust = {
|
||
|
|
/** 是否携带 functiontype=8 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重后 functionpar 偏移累加 */
|
||
|
|
offset: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 行军速度被动汇总结果 */
|
||
|
|
export type BattleBuffMoveSpeedAdjust = {
|
||
|
|
/** 是否携带 functiontype=12 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后 functionpar 万分比累加 */
|
||
|
|
bonusWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 兵将血量被动汇总结果 */
|
||
|
|
export type BattleBuffUnitHpAdjust = {
|
||
|
|
/** 是否携带 functiontype=15 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后 functionpar 万分比累加 */
|
||
|
|
bonusWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 攻击单位攻速被动汇总结果 */
|
||
|
|
export type BattleBuffUnitAttackSpeedAdjust = {
|
||
|
|
/** 是否携带 functiontype=27 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后 functionpar 万分比累加 */
|
||
|
|
bonusWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 敌军移速降低被动汇总结果 */
|
||
|
|
export type BattleBuffEnemySlowAdjust = {
|
||
|
|
/** 是否携带 functiontype=23 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后降低比例万分比累加 */
|
||
|
|
slowWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 召唤消耗降低被动汇总结果 */
|
||
|
|
export type BattleBuffSummonCostAdjust = {
|
||
|
|
/** 是否携带 functiontype=26 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后降低比例万分比累加 */
|
||
|
|
reduceWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 基地血量被动汇总结果 */
|
||
|
|
export type BattleBuffBaseHpAdjust = {
|
||
|
|
/** 是否携带 functiontype=6 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后 functionpar 上限值累加 */
|
||
|
|
bonusHp: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 还魂丹被动汇总结果 */
|
||
|
|
export type BattleBuffReviveAdjust = {
|
||
|
|
/** 是否携带 functiontype=14 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后触发概率万分比累加(上限 10000) */
|
||
|
|
probWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 九霄神雷被动汇总结果 */
|
||
|
|
export type BattleBuffBaseThunderAdjust = {
|
||
|
|
/** 是否携带 functiontype=20 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 单局最多触发次数(不同 buffId 累加) */
|
||
|
|
maxTriggers: number;
|
||
|
|
/** 小怪伤害比例万分比(取携带中最大值) */
|
||
|
|
smallDmgWan: number;
|
||
|
|
/** Boss 伤害比例万分比(取携带中最大值) */
|
||
|
|
bossDmgWan: number;
|
||
|
|
/** 触发距离格数(取携带中最大值) */
|
||
|
|
triggerCells: number;
|
||
|
|
/** 生效距离格数(从锚点向出怪口后退,取携带中最大值) */
|
||
|
|
effectCells: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 打断 Boss 施法被动汇总结果 */
|
||
|
|
export type BattleBuffBossInterruptAdjust = {
|
||
|
|
/** 是否携带 functiontype=21 被动 */
|
||
|
|
active: boolean;
|
||
|
|
/** 去重 buffId 后激活概率万分比累加(上限 10000) */
|
||
|
|
probWan: number;
|
||
|
|
/** 去重 buffId 后伤害比例万分比累加 */
|
||
|
|
dmgWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 还魂丹复活后实体 Tag:再次死亡无桃、不再触发还魂 */
|
||
|
|
export const BATTLE_BUFF_PASSIVE_REVIVED_TAG = 'buff_passive_revived';
|
||
|
|
|
||
|
|
/** 读取被动 buff 局内 CD 秒数(functionpar 首段;无 CD 类型返回 0) */
|
||
|
|
export function readBattleBuffPassiveCdSec(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!row) return 0;
|
||
|
|
const ft = Number(row.functiontype) || 0;
|
||
|
|
if (!passiveBuffUsesBattleGameCd(ft)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 被动 buff functionpar 缺少 CD', row.id, row.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「召神令」(functiontype=8) */
|
||
|
|
export function isBattleBuffPassiveHeroSummonRate(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.HeroSummonRate;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取召神令 number 档偏移(functionpar 首段) */
|
||
|
|
export function readBattleBuffHeroSummonRateOffset(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveHeroSummonRate(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 召神令 functionpar 缺少偏移', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
const offset = Math.floor(nums[0]);
|
||
|
|
return offset > 0 ? offset : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中召神令偏移:不同 buffId 叠加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动
|
||
|
|
*/
|
||
|
|
export function resolveCarriedHeroSummonRatePoolAdjust(passives: readonly DailyShopParsed[]): BattleBuffHeroSummonRatePoolAdjust {
|
||
|
|
let offset = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < passives.length; i++) {
|
||
|
|
const row = passives[i].row;
|
||
|
|
if (!isBattleBuffPassiveHeroSummonRate(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
offset += readBattleBuffHeroSummonRateOffset(row);
|
||
|
|
}
|
||
|
|
return { active, offset };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「行军速度」(functiontype=12) */
|
||
|
|
export function isBattleBuffPassiveMoveSpeed(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.MoveSpeed;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取行军速度被动加成万分比(functionpar 首段) */
|
||
|
|
export function readBattleBuffMoveSpeedBonusWan(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveMoveSpeed(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 行军速度 functionpar 缺少万分比', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中行军速度加成:不同 buffId 万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedMoveSpeedBonusWan(passives?: readonly DailyShopParsed[]): BattleBuffMoveSpeedAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let bonusWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveMoveSpeed(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
bonusWan += readBattleBuffMoveSpeedBonusWan(row);
|
||
|
|
}
|
||
|
|
return { active, bonusWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「基地加血」(functiontype=6) */
|
||
|
|
export function isBattleBuffPassiveBaseHp(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.BaseHp;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取基地加血被动上限增加值(functionpar 首段) */
|
||
|
|
export function readBattleBuffBaseHpBonus(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveBaseHp(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 基地加血 functionpar 缺少上限值', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中基地加血:不同 buffId 上限值相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedBaseHpBonus(passives?: readonly DailyShopParsed[]): BattleBuffBaseHpAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let bonusHp = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveBaseHp(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
bonusHp += readBattleBuffBaseHpBonus(row);
|
||
|
|
}
|
||
|
|
return { active, bonusHp };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「还魂丹」(functiontype=14) */
|
||
|
|
export function isBattleBuffPassiveRevive(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.Revive;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取还魂丹被动触发概率万分比(functionpar 首段) */
|
||
|
|
export function readBattleBuffReviveProbWan(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveRevive(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 还魂丹 functionpar 缺少万分比', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中还魂丹触发概率:不同 buffId 万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedReviveProbWan(passives?: readonly DailyShopParsed[]): BattleBuffReviveAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let probWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveRevive(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
probWan += readBattleBuffReviveProbWan(row);
|
||
|
|
}
|
||
|
|
probWan = Math.min(10000, probWan);
|
||
|
|
return { active, probWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 还魂丹:按汇总概率掷骰是否触发原地复活。
|
||
|
|
* @param passives 省略时读 DailyShop 当前携带被动
|
||
|
|
*/
|
||
|
|
export function rollCarriedPassiveRevive(passives?: readonly DailyShopParsed[]): boolean {
|
||
|
|
const adjust = resolveCarriedReviveProbWan(passives);
|
||
|
|
if (!adjust.active || adjust.probWan <= 0) return false;
|
||
|
|
const roll = Math.floor(Math.random() * 10000);
|
||
|
|
return roll < adjust.probWan;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「九霄神雷」(functiontype=20) */
|
||
|
|
export function isBattleBuffPassiveBaseThunder(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.BaseThunder;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取九霄神雷被动配置 */
|
||
|
|
export function readBattleBuffBaseThunderConfig(row: ITableBattlebuff | null | undefined): BattleBuffBaseThunderConfig | null {
|
||
|
|
if (!isBattleBuffPassiveBaseThunder(row)) return null;
|
||
|
|
return parseBattleBuffBaseThunderConfig(row!.functionpar);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中九霄神雷:单局次数不同 buffId 累加;伤害/距离取最大值。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedBaseThunderConfig(passives?: readonly DailyShopParsed[]): BattleBuffBaseThunderAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let maxTriggers = 0;
|
||
|
|
let smallDmgWan = 0;
|
||
|
|
let bossDmgWan = 0;
|
||
|
|
let triggerCells = 0;
|
||
|
|
let effectCells = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveBaseThunder(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const cfg = readBattleBuffBaseThunderConfig(row);
|
||
|
|
if (!cfg) continue;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId > 0) {
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!duplicated) {
|
||
|
|
seenIds.push(buffId);
|
||
|
|
maxTriggers += cfg.maxTriggers;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (cfg.smallDmgWan > smallDmgWan) smallDmgWan = cfg.smallDmgWan;
|
||
|
|
if (cfg.bossDmgWan > bossDmgWan) bossDmgWan = cfg.bossDmgWan;
|
||
|
|
if (cfg.triggerCells > triggerCells) triggerCells = cfg.triggerCells;
|
||
|
|
if (cfg.effectCells > effectCells) effectCells = cfg.effectCells;
|
||
|
|
}
|
||
|
|
return { active, maxTriggers, smallDmgWan, bossDmgWan, triggerCells, effectCells };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「打断 Boss 施法」(functiontype=21) */
|
||
|
|
export function isBattleBuffPassiveBossInterrupt(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.BossInterrupt;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 读取打断 Boss 施法被动参数。
|
||
|
|
* functionpar:激活概率万分比,伤害比例万分比
|
||
|
|
*/
|
||
|
|
export function readBattleBuffBossInterruptConfig(row: ITableBattlebuff | null | undefined): { probWan: number; dmgWan: number } | null {
|
||
|
|
if (!isBattleBuffPassiveBossInterrupt(row)) return null;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 2) {
|
||
|
|
console.log('[BattleBuff] 打断 Boss 施法 functionpar 缺少概率/伤害', row!.id, row!.functionpar);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const probWan = Math.max(0, Math.floor(nums[0]));
|
||
|
|
const dmgWan = Math.max(0, Math.floor(nums[1]));
|
||
|
|
if (probWan <= 0 && dmgWan <= 0) return null;
|
||
|
|
return { probWan, dmgWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中打断 Boss 施法:不同 buffId 概率与伤害万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedBossInterruptConfig(passives?: readonly DailyShopParsed[]): BattleBuffBossInterruptAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let probWan = 0;
|
||
|
|
let dmgWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveBossInterrupt(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
const cfg = readBattleBuffBossInterruptConfig(row);
|
||
|
|
if (!cfg) continue;
|
||
|
|
probWan += cfg.probWan;
|
||
|
|
dmgWan += cfg.dmgWan;
|
||
|
|
}
|
||
|
|
probWan = Math.min(10000, probWan);
|
||
|
|
return { active, probWan, dmgWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 打断 Boss 施法:按汇总概率掷骰是否触发。
|
||
|
|
* @param passives 省略时读 DailyShop 当前携带被动
|
||
|
|
*/
|
||
|
|
export function rollCarriedBossInterrupt(passives?: readonly DailyShopParsed[]): boolean {
|
||
|
|
const adjust = resolveCarriedBossInterruptConfig(passives);
|
||
|
|
if (!adjust.active || adjust.probWan <= 0) return false;
|
||
|
|
const roll = Math.floor(Math.random() * 10000);
|
||
|
|
return roll < adjust.probWan;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「兵将血量」(functiontype=15) */
|
||
|
|
export function isBattleBuffPassiveUnitHp(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.UnitHp;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取兵将血量被动上限加成万分比(functionpar 首段) */
|
||
|
|
export function readBattleBuffUnitHpBonusWan(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveUnitHp(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 兵将血量 functionpar 缺少万分比', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中兵将血量加成:不同 buffId 万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedUnitHpBonusWan(passives?: readonly DailyShopParsed[]): BattleBuffUnitHpAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let bonusWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveUnitHp(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
bonusWan += readBattleBuffUnitHpBonusWan(row);
|
||
|
|
}
|
||
|
|
return { active, bonusWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「攻击单位攻速」(functiontype=27) */
|
||
|
|
export function isBattleBuffPassiveUnitAttackSpeed(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.UnitAttackSpeed;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取攻击单位攻速被动加成万分比(functionpar 首段) */
|
||
|
|
export function readBattleBuffUnitAttackSpeedBonusWan(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveUnitAttackSpeed(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 攻击单位攻速 functionpar 缺少万分比', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中攻击单位攻速加成:不同 buffId 万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedUnitAttackSpeedBonusWan(passives?: readonly DailyShopParsed[]): BattleBuffUnitAttackSpeedAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let bonusWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveUnitAttackSpeed(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
bonusWan += readBattleBuffUnitAttackSpeedBonusWan(row);
|
||
|
|
}
|
||
|
|
return { active, bonusWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「敌军移速降低」(functiontype=23) */
|
||
|
|
export function isBattleBuffPassiveEnemySlow(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.EnemySlow;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取敌军移速降低被动比例万分比(functionpar 首段) */
|
||
|
|
export function readBattleBuffEnemySlowWan(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveEnemySlow(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 敌军移速降低 functionpar 缺少万分比', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中敌军移速降低:不同 buffId 万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedEnemySlowWan(passives?: readonly DailyShopParsed[]): BattleBuffEnemySlowAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let slowWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveEnemySlow(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
slowWan += readBattleBuffEnemySlowWan(row);
|
||
|
|
}
|
||
|
|
return { active, slowWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「召唤消耗降低」(functiontype=26) */
|
||
|
|
export function isBattleBuffPassiveSummonCost(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.SummonCost;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取召唤消耗降低被动比例万分比(functionpar 首段) */
|
||
|
|
export function readBattleBuffSummonCostReduceWan(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!isBattleBuffPassiveSummonCost(row)) return 0;
|
||
|
|
const nums = parseFunctionParNums(row!.functionpar);
|
||
|
|
if (nums.length < 1) {
|
||
|
|
console.log('[BattleBuff] 召唤消耗降低 functionpar 缺少万分比', row!.id, row!.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 汇总携带被动中召唤消耗降低:不同 buffId 万分比相加,相同 buffId 只计一次。
|
||
|
|
* @param passives 当前携带被动;省略时读 DailyShop
|
||
|
|
*/
|
||
|
|
export function resolveCarriedSummonCostReduceWan(passives?: readonly DailyShopParsed[]): BattleBuffSummonCostAdjust {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let reduceWan = 0;
|
||
|
|
let active = false;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveSummonCost(row)) continue;
|
||
|
|
active = true;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId <= 0) continue;
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
reduceWan += readBattleBuffSummonCostReduceWan(row);
|
||
|
|
}
|
||
|
|
reduceWan = Math.min(10000, reduceWan);
|
||
|
|
return { active, reduceWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否被动「击杀额外蟠桃」(functiontype=25) */
|
||
|
|
export function isBattleBuffPassiveKillPeach(row: ITableBattlebuff | null | undefined): boolean {
|
||
|
|
if (!row) return false;
|
||
|
|
return Number(row.functiontype) === BattleBuffFunctionType.KillPeach;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 击杀小怪:按携带被动 functionpar 概率掷骰,累加成功 buff 的蟠桃数量。
|
||
|
|
* 不同 buffId 各自掷骰;相同 buffId 只计一次。
|
||
|
|
* @param passives 省略时读 DailyShop 当前携带被动
|
||
|
|
* @returns 本次击杀额外蟠桃总数;未触发时 0
|
||
|
|
*/
|
||
|
|
export function rollCarriedKillPeachBonus(passives?: readonly DailyShopParsed[]): number {
|
||
|
|
const list = passives ?? DailyShopDataManager.getOwnedPassive();
|
||
|
|
let total = 0;
|
||
|
|
const seenIds: number[] = [];
|
||
|
|
for (let i = 0; i < list.length; i++) {
|
||
|
|
const row = list[i].row;
|
||
|
|
if (!isBattleBuffPassiveKillPeach(row)) continue;
|
||
|
|
const buffId = Number(row.id) || 0;
|
||
|
|
if (buffId > 0) {
|
||
|
|
let duplicated = false;
|
||
|
|
for (let j = 0; j < seenIds.length; j++) {
|
||
|
|
if (seenIds[j] === buffId) {
|
||
|
|
duplicated = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (duplicated) continue;
|
||
|
|
seenIds.push(buffId);
|
||
|
|
}
|
||
|
|
const cfg = parseBattleBuffKillPeachConfig(row.functionpar);
|
||
|
|
if (!cfg) {
|
||
|
|
console.log('[BattleBuff] 击杀额外蟠桃 functionpar 无效', row.id, row.functionpar);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
const roll = Math.floor(Math.random() * 10000);
|
||
|
|
if (roll < cfg.probWan) {
|
||
|
|
total += cfg.count;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return total;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 读取被动 buff 产字 weapon2Id(functionpar 第二段) */
|
||
|
|
export function readBattleBuffPassiveSpawnWeapon2Id(row: ITableBattlebuff | null | undefined): number {
|
||
|
|
if (!row) return 0;
|
||
|
|
const nums = parseFunctionParNums(row.functionpar);
|
||
|
|
if (nums.length < 2) {
|
||
|
|
console.log('[BattleBuff] 被动产字 functionpar 缺少文字 id', row?.id, row?.functionpar);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return Math.max(0, Math.floor(nums[1]));
|
||
|
|
}
|