import { DailyShopDataManager } from '../DailyShopDataManager'; import type { DailyShopParsed } from '../DailyShopTypes'; import { BattleBuffFunctionType, passiveBuffUsesBattleGameCd } from './BattleBuffTypes'; export { passiveBuffUsesBattleGameCd }; import { parseBattleBuffBaseThunderConfig, parseBattleBuffKillPeachConfig, parseBattleBuffTreasureShovelPeachRange, 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; } /** 是否被动「掘宝仙铲」(functiontype=10) */ export function isBattleBuffPassiveTreasureShovel(row: ITableBattlebuff | null | undefined): boolean { if (!row) return false; return Number(row.functiontype) === BattleBuffFunctionType.TreasureShovel; } /** * 是否携带掘宝仙铲被动(铲子变金铲、挖格产桃)。 * @param passives 省略时读 DailyShop 当前携带被动 */ export function hasCarriedTreasureShovel(passives?: readonly DailyShopParsed[]): boolean { const list = passives ?? DailyShopDataManager.getOwnedPassive(); for (let i = 0; i < list.length; i++) { if (isBattleBuffPassiveTreasureShovel(list[i].row)) return true; } return false; } /** * 掘宝仙铲:按携带被动 functionpar 随机本次挖格蟠桃数量。 * @param passives 省略时读 DailyShop 当前携带被动 * @returns 正整数;未携带或配置无效时 0 */ export function rollCarriedTreasureShovelPeachCount(passives?: readonly DailyShopParsed[]): number { const list = passives ?? DailyShopDataManager.getOwnedPassive(); for (let i = 0; i < list.length; i++) { const row = list[i].row; if (!isBattleBuffPassiveTreasureShovel(row)) continue; const range = parseBattleBuffTreasureShovelPeachRange(row.functionpar); if (!range) { console.log('[BattleBuff] 掘宝仙铲 functionpar 无效', row.id, row.functionpar); return 0; } const span = range.max - range.min + 1; return range.min + Math.floor(Math.random() * span); } return 0; } /** 读取被动 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])); }