import type { NPC, Planet, Player, FleetMission, SpyReport, SpiedNotification, IncomingFleetAlert, Fleet, DebrisField, Resources, TradeOffer, IntelReport, JointAttackInvite, IntelType, AidNotification, AllyDefenseNotification, AttitudeChangeNotification } from '@/types/game' import { MissionType, ShipType, TechnologyType, RelationStatus, NPCAIType } from '@/types/game' // 重新导出类型供外部使用 export type { TradeOffer, IntelReport, JointAttackInvite, IntelType, AidNotification, AllyDefenseNotification, AttitudeChangeNotification } import * as fleetLogic from './fleetLogic' import * as diplomaticLogic from './diplomaticLogic' import * as resourceLogic from './resourceLogic' import { DIPLOMATIC_CONFIG, SHIPS } from '@/config/gameConfig' // ========== 敌对NPC增强行为类型定义 ========== /** * 骚扰任务类型 */ export type HarassmentType = 'spy' | 'raid' | 'intimidate' /** * 报复等级 */ export interface RevengeLevel { attackCount: number fleetMultiplier: number attackInterval: number name: string } /** * 围攻协调信息 */ export interface SiegeCoordination { targetPlanetId: string targetPosition: { galaxy: number; system: number; position: number } coordinatorNpcId: string participantNpcIds: string[] arrivalTime: number startedAt: number } // TradeOffer, IntelReport, JointAttackInvite, IntelType 类型已移至 @/types/game.ts /** * NPC之间的关系 */ export interface NPCRelation { npcId: string targetNpcId: string status: 'ally' | 'neutral' | 'enemy' strength: number // 关系强度 0-100 lastUpdated: number } /** * NPC联盟 */ export interface NPCAlliance { id: string name: string leaderNpcId: string memberNpcIds: string[] enemyNpcIds: string[] createdAt: number } /** * 辅助函数:获取舰队中最慢船只的速度 * 用于计算飞行时间 */ const getFleetMinSpeed = (fleet: Partial): number => { let minSpeed = Infinity for (const [shipType, count] of Object.entries(fleet)) { if ((count || 0) > 0) { const shipConfig = SHIPS[shipType as ShipType] if (shipConfig) { minSpeed = Math.min(minSpeed, shipConfig.speed) } } } return minSpeed === Infinity ? 10000 : minSpeed // 默认使用基础速度 } /** * AI 类型行为修改器 * 根据 NPC 的 AI 类型调整其行为参数 */ export interface AIBehaviorModifier { spyFrequencyMultiplier: number // 侦查频率倍率(越高侦查越频繁) attackFrequencyMultiplier: number // 攻击频率倍率 attackProbabilityMultiplier: number // 攻击概率倍率 fleetSizeMultiplier: number // 出击舰队比例倍率 giftProbabilityMultiplier: number // 赠送礼物概率倍率 revengeMultiplier: number // 反击倾向倍率 defenseFocus: boolean // 是否优先发展防御 willAttackWhenHostile: boolean // 敌对时是否会主动攻击 willSpyWhenHostile: boolean // 敌对时是否会侦查 } /** * 获取 AI 类型的行为修改器 */ export const getAIBehaviorModifier = (aiType?: NPCAIType): AIBehaviorModifier => { switch (aiType) { case NPCAIType.Aggressive: // 侵略型:高频侦查攻击,大舰队出击,不送礼,强烈反击 return { spyFrequencyMultiplier: 1.5, attackFrequencyMultiplier: 1.5, attackProbabilityMultiplier: 1.3, fleetSizeMultiplier: 1.2, giftProbabilityMultiplier: 0, revengeMultiplier: 2.0, defenseFocus: false, willAttackWhenHostile: true, willSpyWhenHostile: true } case NPCAIType.Defensive: // 防守型:低频侦查,几乎不主动攻击,但被攻击后强烈反击 return { spyFrequencyMultiplier: 0.3, attackFrequencyMultiplier: 0.1, attackProbabilityMultiplier: 0.1, fleetSizeMultiplier: 0.5, giftProbabilityMultiplier: 0.5, revengeMultiplier: 3.0, // 反击很强烈 defenseFocus: true, willAttackWhenHostile: false, // 即使敌对也不主动攻击 willSpyWhenHostile: true // 但会侦查监视 } case NPCAIType.Trader: // 商人型:几乎不攻击,高概率送礼,被攻击后轻微反击 return { spyFrequencyMultiplier: 0.2, attackFrequencyMultiplier: 0.05, attackProbabilityMultiplier: 0.05, fleetSizeMultiplier: 0.3, giftProbabilityMultiplier: 3.0, // 高概率送礼 revengeMultiplier: 0.5, // 反击意愿低 defenseFocus: false, willAttackWhenHostile: false, willSpyWhenHostile: false } case NPCAIType.Expansionist: // 扩张型:中等侦查频率,较少攻击,专注发展 return { spyFrequencyMultiplier: 0.7, attackFrequencyMultiplier: 0.4, attackProbabilityMultiplier: 0.5, fleetSizeMultiplier: 0.6, giftProbabilityMultiplier: 1.0, revengeMultiplier: 1.0, defenseFocus: false, willAttackWhenHostile: true, willSpyWhenHostile: true } case NPCAIType.Balanced: default: // 平衡型(默认):标准行为 return { spyFrequencyMultiplier: 1.0, attackFrequencyMultiplier: 1.0, attackProbabilityMultiplier: 1.0, fleetSizeMultiplier: 1.0, giftProbabilityMultiplier: 1.0, revengeMultiplier: 1.0, defenseFocus: false, willAttackWhenHostile: true, willSpyWhenHostile: true } } } /** * 根据 AI 类型调整动态行为配置 */ export const applyAIModifierToConfig = (config: DynamicBehaviorConfig, modifier: AIBehaviorModifier): DynamicBehaviorConfig => { return { ...config, spyInterval: Math.floor(config.spyInterval / modifier.spyFrequencyMultiplier), attackInterval: Math.floor(config.attackInterval / modifier.attackFrequencyMultiplier), attackProbability: Math.min(1.0, config.attackProbability * modifier.attackProbabilityMultiplier), attackFleetSizeRatio: Math.min(1.0, config.attackFleetSizeRatio * modifier.fleetSizeMultiplier) } } /** * NPC行为决策系统 * * 流程: * 1. NPC定期侦查玩家星球 * 2. 玩家收到"被侦查"通知 * 3. 基于侦查结果,NPC决定是否攻击 * 4. NPC发起攻击,玩家收到实时警告 * 5. NPC检测并回收自己星球附近的残骸 * 6. NPC被攻击后会做出防御性反应或反击 */ // 动态行为配置接口 export interface DynamicBehaviorConfig { spyInterval: number attackInterval: number attackProbability: number minSpyProbes: number attackFleetSizeRatio: number maxConcurrentSpyMissions: number // 同时最多多少个侦查任务 maxConcurrentAttackMissions: number // 同时最多多少个攻击任务 } /** * 根据玩家积分计算动态NPC行为配置 * 积分越高,NPC越激进 */ export const calculateDynamicBehavior = (playerPoints: number): DynamicBehaviorConfig => { if (playerPoints < 1000) { // 新手阶段:NPC温和但会主动侦查攻击 return { spyInterval: 300, // 5分钟侦查一次 attackInterval: 300, // 5分钟攻击一次(与侦查同步,侦查完就攻击) attackProbability: 0.4, minSpyProbes: 1, attackFleetSizeRatio: 0.3, // 只派30%舰队 maxConcurrentSpyMissions: 3, maxConcurrentAttackMissions: 2 } } else if (playerPoints < 5000) { // 初级阶段:NPC比较激进 return { spyInterval: 420, // 7分钟侦查一次 attackInterval: 420, // 7分钟攻击一次(与侦查同步) attackProbability: 0.45, minSpyProbes: 2, attackFleetSizeRatio: 0.5, // 派50%舰队 maxConcurrentSpyMissions: 5, maxConcurrentAttackMissions: 3 } } else if (playerPoints < 20000) { // 中级阶段:NPC很激进 return { spyInterval: 360, // 6分钟侦查一次 attackInterval: 360, // 6分钟攻击一次(与侦查同步) attackProbability: 0.55, minSpyProbes: 3, attackFleetSizeRatio: 0.7, // 派70%舰队 maxConcurrentSpyMissions: 8, maxConcurrentAttackMissions: 5 } } else if (playerPoints < 50000) { // 高级阶段:NPC非常激进 return { spyInterval: 300, // 5分钟侦查一次 attackInterval: 300, // 5分钟攻击一次(与侦查同步) attackProbability: 0.65, minSpyProbes: 4, attackFleetSizeRatio: 0.85, // 派85%舰队 maxConcurrentSpyMissions: 10, maxConcurrentAttackMissions: 8 } } else { // 专家阶段:NPC极度激进 return { spyInterval: 240, // 4分钟侦查一次 attackInterval: 240, // 4分钟攻击一次(与侦查同步) attackProbability: 0.8, minSpyProbes: 5, attackFleetSizeRatio: 0.95, // 派95%舰队 maxConcurrentSpyMissions: 15, maxConcurrentAttackMissions: 12 } } } /** * 检查NPC是否应该侦查玩家 */ export const shouldNPCSpyPlayer = (npc: NPC, player: Player, currentTime: number, config: DynamicBehaviorConfig): boolean => { // 新手保护:积分低于1000的玩家不会被侦查 const playerPoints = player.points || 0 if (playerPoints < 1000) { return false } // 获取 AI 行为修改器 const aiModifier = getAIBehaviorModifier(npc.aiType) // 检查外交关系 - 统一使用 npc.relations const relation = npc.relations?.[player.id] // 如果没有关系数据,视为中立,不侦查 if (!relation) { return false } // 友好或中立NPC不侦查 if (relation.status === RelationStatus.Friendly || relation.status === RelationStatus.Neutral) { return false } // 只有敌对NPC才会继续 if (relation.status !== RelationStatus.Hostile) { return false } // 根据 AI 类型判断是否会侦查 if (!aiModifier.willSpyWhenHostile) { return false } // 只有敌对NPC才会到达这里,检查冷却时间(根据 AI 类型调整) const adjustedConfig = applyAIModifierToConfig(config, aiModifier) const lastSpyTime = npc.lastSpyTime || 0 if (currentTime - lastSpyTime < adjustedConfig.spyInterval * 1000) { return false } // 敌对NPC且冷却结束,执行侦查 return true } /** * 检查NPC是否应该攻击玩家 */ export const shouldNPCAttackPlayer = (npc: NPC, player: Player, currentTime: number, config: DynamicBehaviorConfig): boolean => { // 新手保护:积分低于1000的玩家不会被攻击 const playerPoints = player.points || 0 if (playerPoints < 1000) { return false } // 获取 AI 行为修改器 const aiModifier = getAIBehaviorModifier(npc.aiType) // 检查外交关系 - 统一使用 npc.relations const relation = npc.relations?.[player.id] // 如果没有关系数据,视为中立,不攻击 if (!relation) { return false } // 友好或中立NPC不攻击 if (relation.status === RelationStatus.Friendly || relation.status === RelationStatus.Neutral) { return false } // 只有敌对NPC才会继续 if (relation.status !== RelationStatus.Hostile) { return false } // 根据 AI 类型判断是否会主动攻击 if (!aiModifier.willAttackWhenHostile) { return false } // 检查攻击冷却(根据 AI 类型调整) const adjustedConfig = applyAIModifierToConfig(config, aiModifier) const lastAttackTime = npc.lastAttackTime || 0 if (currentTime - lastAttackTime < adjustedConfig.attackInterval * 1000) { return false } // 必须有侦查报告才能攻击 if (!npc.playerSpyReports || Object.keys(npc.playerSpyReports).length === 0) { return false } // 根据 AI 类型的攻击概率决定是否攻击 // 侵略型总是攻击,其他类型按概率 if (aiModifier.attackProbabilityMultiplier < 1.0) { if (Math.random() > adjustedConfig.attackProbability) { return false } } return true } /** * 检查NPC是否应该赠送资源给玩家 */ export const shouldNPCGiftPlayer = (npc: NPC, player: Player, currentTime: number): boolean => { const { NPC_GIFT_CONFIG } = DIPLOMATIC_CONFIG // 检查功能是否启用 if (!NPC_GIFT_CONFIG.ENABLED) { return false } // 获取 AI 行为修改器 const aiModifier = getAIBehaviorModifier(npc.aiType) // 侵略型 NPC 永远不送礼 if (aiModifier.giftProbabilityMultiplier === 0) { return false } // 检查上次赠送时间(商人型间隔更短) const lastGiftTime = (npc as any).lastGiftTime || 0 const giftInterval = NPC_GIFT_CONFIG.CHECK_INTERVAL / aiModifier.giftProbabilityMultiplier if (currentTime - lastGiftTime < giftInterval * 1000) { return false } // 检查好感度 - 统一使用 npc.relations // 商人型 NPC 好感度门槛更低 const relation = npc.relations?.[player.id] const minReputation = npc.aiType === NPCAIType.Trader ? NPC_GIFT_CONFIG.MIN_REPUTATION * 0.5 : NPC_GIFT_CONFIG.MIN_REPUTATION if (!relation || relation.reputation < minReputation) { return false } // 随机概率(根据 AI 类型调整) const giftProbability = NPC_GIFT_CONFIG.GIFT_PROBABILITY * aiModifier.giftProbabilityMultiplier return Math.random() < giftProbability } /** * 计算玩家的总资源产量(每小时) */ const calculatePlayerTotalProduction = (player: Player): { metal: number; crystal: number; deuterium: number } => { let totalMetal = 0 let totalCrystal = 0 let totalDeuterium = 0 // 计算当前激活的军官加成 const bonuses = player.officers ? { resourceProductionBonus: Object.values(player.officers).reduce((sum, officer) => { if (officer.active && officer.expiresAt && officer.expiresAt > Date.now()) { return sum + (officer.bonuses?.resourceProductionBonus || 0) } return sum }, 0), darkMatterProductionBonus: Object.values(player.officers).reduce((sum, officer) => { if (officer.active && officer.expiresAt && officer.expiresAt > Date.now()) { return sum + (officer.bonuses?.darkMatterProductionBonus || 0) } return sum }, 0), energyProductionBonus: Object.values(player.officers).reduce((sum, officer) => { if (officer.active && officer.expiresAt && officer.expiresAt > Date.now()) { return sum + (officer.bonuses?.energyProductionBonus || 0) } return sum }, 0) } : { resourceProductionBonus: 0, darkMatterProductionBonus: 0, energyProductionBonus: 0 } // 遍历所有星球计算总产量 for (const planet of player.planets) { if (planet.isMoon) continue // 月球没有矿场 const production = resourceLogic.calculateResourceProduction(planet, bonuses) totalMetal += production.metal || 0 totalCrystal += production.crystal || 0 totalDeuterium += production.deuterium || 0 } return { metal: totalMetal, crystal: totalCrystal, deuterium: totalDeuterium } } /** * NPC向玩家赠送资源 * 动态计算赠送量:基于玩家每小时产量的4-8倍 * 赠送的资源会从NPC主星球扣除 */ export const giftResourcesToPlayer = (npc: NPC, player: Player): void => { const { NPC_GIFT_CONFIG } = DIPLOMATIC_CONFIG const dynamicConfig = NPC_GIFT_CONFIG.DYNAMIC_GIFT const baseConfig = NPC_GIFT_CONFIG.BASE_GIFT_AMOUNT // 获取NPC主星球 const npcPlanet = npc.planets[0] if (!npcPlanet) return let giftResources = { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 } if (dynamicConfig?.ENABLED) { // 动态计算:根据玩家产量 const playerProduction = calculatePlayerTotalProduction(player) // 随机生成产量倍数(4-8倍) const multiplierRange = dynamicConfig.PRODUCTION_MULTIPLIER const multiplier = multiplierRange.min + Math.random() * (multiplierRange.max - multiplierRange.min) // 计算赠送量(产量 × 倍数) let metalGift = Math.floor(playerProduction.metal * multiplier) let crystalGift = Math.floor(playerProduction.crystal * multiplier) let deuteriumGift = Math.floor(playerProduction.deuterium * multiplier) // 应用最小值保底 metalGift = Math.max(metalGift, dynamicConfig.MIN_AMOUNT.METAL) crystalGift = Math.max(crystalGift, dynamicConfig.MIN_AMOUNT.CRYSTAL) deuteriumGift = Math.max(deuteriumGift, dynamicConfig.MIN_AMOUNT.DEUTERIUM) // 应用最大值封顶 metalGift = Math.min(metalGift, dynamicConfig.MAX_AMOUNT.METAL) crystalGift = Math.min(crystalGift, dynamicConfig.MAX_AMOUNT.CRYSTAL) deuteriumGift = Math.min(deuteriumGift, dynamicConfig.MAX_AMOUNT.DEUTERIUM) giftResources.metal = metalGift giftResources.crystal = crystalGift giftResources.deuterium = deuteriumGift } else { // 使用基础配置(后备方案) giftResources.metal = Math.floor(Math.random() * (baseConfig.METAL.max - baseConfig.METAL.min + 1)) + baseConfig.METAL.min giftResources.crystal = Math.floor(Math.random() * (baseConfig.CRYSTAL.max - baseConfig.CRYSTAL.min + 1)) + baseConfig.CRYSTAL.min giftResources.deuterium = Math.floor(Math.random() * (baseConfig.DEUTERIUM.max - baseConfig.DEUTERIUM.min + 1)) + baseConfig.DEUTERIUM.min } // 根据好感度额外加成:好感度越高,赠送越多 const relation = npc.relations?.[player.id] if (relation && relation.reputation > 60) { // 好感度60-100区间,额外增加0-40% const reputationBonus = 1 + (relation.reputation - 60) / 100 giftResources.metal = Math.floor(giftResources.metal * reputationBonus) giftResources.crystal = Math.floor(giftResources.crystal * reputationBonus) giftResources.deuterium = Math.floor(giftResources.deuterium * reputationBonus) } // 限制赠送量不超过NPC拥有的资源(只赠送NPC实际拥有的部分) giftResources.metal = Math.min(giftResources.metal, Math.floor(npcPlanet.resources.metal)) giftResources.crystal = Math.min(giftResources.crystal, Math.floor(npcPlanet.resources.crystal)) giftResources.deuterium = Math.min(giftResources.deuterium, Math.floor(npcPlanet.resources.deuterium)) // 如果NPC资源不足(所有资源都为0),则不赠送 if (giftResources.metal <= 0 && giftResources.crystal <= 0 && giftResources.deuterium <= 0) { return } // 从NPC星球扣除资源 npcPlanet.resources.metal -= giftResources.metal npcPlanet.resources.crystal -= giftResources.crystal npcPlanet.resources.deuterium -= giftResources.deuterium // 处理赠送 diplomaticLogic.handleNPCGiftToPlayer(npc, player, giftResources) // 更新上次赠送时间 ;(npc as any).lastGiftTime = Date.now() } /** * 检查NPC是否应该协防玩家 * 当玩家受到攻击且NPC是友好状态时触发 */ export const shouldNPCDefendPlayer = (npc: NPC, player: Player, currentTime: number): boolean => { const { ALLY_DEFENSE_CONFIG } = DIPLOMATIC_CONFIG // 检查功能是否启用 if (!ALLY_DEFENSE_CONFIG?.ENABLED) { return false } // 检查好感度 const relation = npc.relations?.[player.id] if (!relation || relation.reputation < ALLY_DEFENSE_CONFIG.MIN_REPUTATION) { return false } // 检查上次协防时间 const lastDefendTime = (npc as any).lastDefendTime || 0 if (currentTime - lastDefendTime < ALLY_DEFENSE_CONFIG.CHECK_INTERVAL * 1000) { return false } // 检查当前协防任务数量 const activeDefenseMissions = npc.fleetMissions?.filter(m => m.missionType === MissionType.Station && m.status === 'outbound').length || 0 if (activeDefenseMissions >= ALLY_DEFENSE_CONFIG.MAX_CONCURRENT_DEFENSES) { return false } // 检查玩家是否有来袭攻击舰队 const incomingAttacks = player.incomingFleetAlerts?.filter( alert => alert.missionType === MissionType.Attack && alert.fleetSize >= ALLY_DEFENSE_CONFIG.MIN_INCOMING_FLEET_SIZE ) || [] if (incomingAttacks.length === 0) { return false } // 根据好感度计算协防概率 const reputationBonus = (relation.reputation - ALLY_DEFENSE_CONFIG.MIN_REPUTATION) / 30 // 70-100区间 const defenseProbability = ALLY_DEFENSE_CONFIG.BASE_DEFENSE_PROBABILITY + reputationBonus * 0.3 return Math.random() < defenseProbability } /** * NPC派舰队协防玩家 */ export const sendDefenseFleet = (npc: NPC, _player: Player, targetPlanet: Planet): FleetMission | null => { const { ALLY_DEFENSE_CONFIG } = DIPLOMATIC_CONFIG // 选择最近且有舰队的NPC星球 const sourcePlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!sourcePlanet) return null // 计算派遣舰队(战斗舰船的30%-60%) const fleetRatio = ALLY_DEFENSE_CONFIG.FLEET_RATIO.min + Math.random() * (ALLY_DEFENSE_CONFIG.FLEET_RATIO.max - ALLY_DEFENSE_CONFIG.FLEET_RATIO.min) const combatShipTypes = [ ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship, ShipType.Battlecruiser, ShipType.Bomber, ShipType.Destroyer ] const defenseFleet: Partial = {} let hasShips = false for (const shipType of combatShipTypes) { const available = sourcePlanet.fleet[shipType] || 0 if (available > 0) { const toSend = Math.floor(available * fleetRatio) if (toSend > 0) { defenseFleet[shipType] = toSend sourcePlanet.fleet[shipType] = available - toSend hasShips = true } } } if (!hasShips) return null // 计算飞行时间 const distance = fleetLogic.calculateDistance(sourcePlanet.position, targetPlanet.position) const minSpeed = getFleetMinSpeed(defenseFleet) const flightTime = fleetLogic.calculateFlightTime(distance, minSpeed) const currentTime = Date.now() const arrivalTime = currentTime + flightTime // 创建协防任务 const mission: FleetMission = { id: `ally_defend_${currentTime}_${npc.id}`, playerId: npc.id, npcId: npc.id, originPlanetId: sourcePlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, fleet: defenseFleet as Fleet, missionType: MissionType.Station, departureTime: currentTime, arrivalTime: arrivalTime, returnTime: arrivalTime + ALLY_DEFENSE_CONFIG.STATION_DURATION, status: 'outbound', cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, isGift: false, isHostile: false // 盟友协防不是敌对任务 } // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) // 更新上次协防时间 ;(npc as any).lastDefendTime = currentTime return mission } /** * 检查并执行NPC协防行为 */ export const checkAndExecuteAllyDefense = (npc: NPC, player: Player, currentTime: number): void => { if (!shouldNPCDefendPlayer(npc, player, currentTime)) { return } // 找到受攻击的玩家星球 const incomingAttacks = player.incomingFleetAlerts?.filter(alert => alert.missionType === MissionType.Attack) || [] if (incomingAttacks.length === 0) return // 选择一个受攻击的星球进行协防 const targetAlert = incomingAttacks[Math.floor(Math.random() * incomingAttacks.length)] if (!targetAlert) return const targetPlanet = player.planets.find(p => p.id === targetAlert.targetPlanetId) if (!targetPlanet) return // 派遣协防舰队 const mission = sendDefenseFleet(npc, player, targetPlanet) if (mission) { // 可以在这里添加通知玩家的逻辑 diplomaticLogic.notifyPlayerOfAllyDefense(npc, player, targetPlanet, mission) } } /** * 选择NPC的最佳攻击来源星球 */ const selectBestNPCPlanet = (npc: NPC, targetPosition: { galaxy: number; system: number; position: number }): Planet | null => { if (npc.planets.length === 0) return null // 选择距离最近且有舰队的星球 let bestPlanet: Planet | null = null let minDistance = Infinity for (const planet of npc.planets) { const distance = fleetLogic.calculateDistance(planet.position, targetPosition) const hasFleet = Object.values(planet.fleet).some(count => (count || 0) > 0) if (hasFleet && distance < minDistance) { minDistance = distance bestPlanet = planet } } return bestPlanet } /** * 计算NPC星球的战斗舰队总攻击力 * 用于判断NPC是否有足够的战斗力来发起有意义的攻击 */ const calculateNPCCombatPower = (npcPlanet: Planet): number => { // 各舰船的攻击力 const shipAttackPower: Record = { [ShipType.LightFighter]: 50, [ShipType.HeavyFighter]: 150, [ShipType.Cruiser]: 400, [ShipType.Battleship]: 1200, [ShipType.Bomber]: 700, [ShipType.Destroyer]: 2500, [ShipType.Battlecruiser]: 1000, [ShipType.Deathstar]: 200000 } let totalPower = 0 for (const [shipType, attack] of Object.entries(shipAttackPower)) { const count = npcPlanet.fleet[shipType as ShipType] || 0 totalPower += count * attack } return totalPower } // 最小战斗力阈值:相当于约10艘轻型战斗机的攻击力 // 这样避免NPC只有几艘小飞机就频繁侦查骚扰玩家 const MIN_COMBAT_POWER_FOR_SPY = 500 /** * 创建NPC侦查任务 */ export const createNPCSpyMission = ( npc: NPC, targetPlanet: Planet, _planets: Planet[], config: DynamicBehaviorConfig ): FleetMission | null => { // 选择NPC的最佳星球作为起点 const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!npcPlanet) { return null } // 检查NPC是否有足够的间谍探测器 const spyProbes = npcPlanet.fleet[ShipType.EspionageProbe] || 0 if (spyProbes < config.minSpyProbes) { return null } // 检查NPC是否有足够的战斗力 // 战斗力太低的话侦查没有意义,也避免频繁骚扰玩家 const combatPower = calculateNPCCombatPower(npcPlanet) if (combatPower < MIN_COMBAT_POWER_FOR_SPY) { return null } // 创建侦查舰队 const fleet: Partial = { [ShipType.EspionageProbe]: config.minSpyProbes } // 从NPC星球扣除舰队 npcPlanet.fleet[ShipType.EspionageProbe] = (npcPlanet.fleet[ShipType.EspionageProbe] || 0) - config.minSpyProbes // 计算飞行时间 const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) const spyProbeSpeed = 100000000 // 间谍探测器速度 const flightTime = fleetLogic.calculateFlightTime(distance, spyProbeSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-spy-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: true, originPlanetId: npcPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType: MissionType.Spy, fleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 更新NPC的上次侦查时间 npc.lastSpyTime = now // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) return mission } /** * 处理NPC侦查到达 * 返回:被侦查通知(给玩家)和侦查报告(给NPC) */ export const processNPCSpyArrival = ( npc: NPC, mission: FleetMission, targetPlanet: Planet, player: Player ): { spiedNotification: SpiedNotification; spyReport: SpyReport } => { // 计算侦查等级(基于NPC的间谍科技) const npcSpyTech = npc.technologies[TechnologyType.EspionageTechnology] || 0 const playerSpyTech = player.technologies[TechnologyType.EspionageTechnology] || 0 const spyProbeCount = mission.fleet[ShipType.EspionageProbe] || 0 // 计算被发现的概率 const detectionChance = Math.max(0, Math.min(100, 50 + (playerSpyTech - npcSpyTech) * 10 - spyProbeCount * 5)) const detected = Math.random() * 100 < detectionChance // 创建NPC的侦查报告 const spyReport: SpyReport = { id: `npc-spy-report-${mission.id}`, timestamp: Date.now(), spyId: npc.id, targetPlanetId: targetPlanet.id, targetPlanetName: targetPlanet.name, targetPosition: targetPlanet.position, targetPlayerId: player.id, resources: { ...targetPlanet.resources }, fleet: npcSpyTech >= 2 ? { ...targetPlanet.fleet } : undefined, defense: npcSpyTech >= 4 ? { ...targetPlanet.defense } : undefined, buildings: npcSpyTech >= 6 ? { ...targetPlanet.buildings } : undefined, technologies: npcSpyTech >= 8 ? { ...player.technologies } : undefined, detectionChance } // 保存到NPC的侦查报告 if (!npc.playerSpyReports) { npc.playerSpyReports = {} } npc.playerSpyReports[targetPlanet.id] = spyReport // 创建被侦查通知(给玩家) const spiedNotification: SpiedNotification = { id: `spied-${mission.id}`, timestamp: Date.now(), npcId: npc.id, npcName: npc.name, targetPlanetId: targetPlanet.id, targetPlanetName: targetPlanet.name, detectionSuccess: detected, read: false } // 舰队返回 mission.status = 'returning' mission.returnTime = Date.now() + (mission.arrivalTime - mission.departureTime) return { spiedNotification, spyReport } } /** * 决定NPC攻击舰队组成 * 基于侦查报告和NPC实力 */ const decideAttackFleet = (_npc: NPC, npcPlanet: Planet, _spyReport: SpyReport, config: DynamicBehaviorConfig): Partial | null => { // 简单策略:派出一定比例的可用舰队 const attackFleet: Partial = {} let hasShips = false // 优先派出战斗舰船 const combatShips = [ ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship, ShipType.Bomber, ShipType.Destroyer, ShipType.Battlecruiser, ShipType.Deathstar ] for (const shipType of combatShips) { const available = npcPlanet.fleet[shipType] || 0 if (available > 0) { // 使用 Math.ceil 确保至少派出1艘(如果有的话) // 但不能超过可用数量 const sendCount = Math.min(available, Math.max(1, Math.floor(available * config.attackFleetSizeRatio))) attackFleet[shipType] = sendCount hasShips = true } } return hasShips ? attackFleet : null } /** * 创建NPC攻击任务 */ export const createNPCAttackMission = ( npc: NPC, targetPlanet: Planet, spyReport: SpyReport, config: DynamicBehaviorConfig ): FleetMission | null => { // 选择NPC的最佳星球作为起点 const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!npcPlanet) { return null } // 决定攻击舰队 const attackFleet = decideAttackFleet(npc, npcPlanet, spyReport, config) if (!attackFleet) { return null } // 从NPC星球扣除舰队 for (const [shipType, count] of Object.entries(attackFleet)) { npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算飞行时间 const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) // 找出舰队中最慢的船速 let minSpeed = Infinity for (const _shipType of Object.keys(attackFleet)) { // 这里简化处理,实际应该从SHIPS配置中获取速度 const baseSpeed = 10000 // 简化 minSpeed = Math.min(minSpeed, baseSpeed) } const flightTime = fleetLogic.calculateFlightTime(distance, minSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-attack-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: true, originPlanetId: npcPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType: MissionType.Attack, fleet: attackFleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 更新NPC的上次攻击时间 npc.lastAttackTime = now // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) return mission } /** * 创建或更新即将到来的舰队警告 */ export const createIncomingFleetAlert = (mission: FleetMission, npc: NPC, targetPlanet: Planet): IncomingFleetAlert => { const fleetSize = Object.values(mission.fleet).reduce((sum, count) => sum + (count || 0), 0) return { id: mission.id, npcId: npc.id, npcName: npc.name, missionType: mission.missionType, targetPlanetId: targetPlanet.id, targetPlanetName: targetPlanet.name, arrivalTime: mission.arrivalTime, fleetSize, read: false } } /** * 更新玩家的即将到来的舰队警告 * 删除已到达或已返回的任务警告 */ export const updateIncomingFleetAlerts = (player: Player, currentTime: number): void => { if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } // 删除已过期的警告(舰队已到达) player.incomingFleetAlerts = player.incomingFleetAlerts.filter(alert => alert.arrivalTime > currentTime) } /** * NPC主动行为主更新函数 * 应该在游戏循环中定期调用 */ export const updateNPCBehavior = ( npc: NPC, player: Player, allPlanets: Planet[], debrisFields: Record, currentTime: number ): void => { // 根据玩家积分计算动态行为配置 const config = calculateDynamicBehavior(player.points) // 1. 检查并回收附近的残骸(优先级最高) const nearbyDebris = findNearbyDebris(npc, debrisFields) if (nearbyDebris.length > 0) { // 检查是否已经有正在执行的回收任务 const activeRecycleMissions = npc.fleetMissions?.filter(m => m.missionType === MissionType.Recycle && m.status === 'outbound') || [] const activeDebrisIds = new Set(activeRecycleMissions.map(m => m.debrisFieldId).filter(Boolean)) // 找到还没有被回收的残骸 const availableDebris = nearbyDebris.filter(d => !activeDebrisIds.has(d.id)) if (availableDebris.length > 0) { // 随机选择一个残骸场进行回收 const targetDebris = availableDebris[Math.floor(Math.random() * availableDebris.length)] if (targetDebris) { createNPCRecycleMission(npc, targetDebris, player, allPlanets) } } } // 2. 检查是否应该反击(优先于普通攻击) if (shouldNPCRevenge(npc, currentTime)) { const revengeMission = createNPCRevengeMission(npc, allPlanets, config) if (revengeMission) { // 找到目标星球创建警告 const targetPlanet = allPlanets.find(p => p.id === revengeMission.targetPlanetId) if (targetPlanet) { const alert = createIncomingFleetAlert(revengeMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } // 反击后跳过普通攻击 return } } // 3. 检查是否应该侦查玩家 if (shouldNPCSpyPlayer(npc, player, currentTime, config)) { // 随机选择一个玩家星球进行侦查 const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) if (playerPlanets.length > 0) { const targetPlanet = playerPlanets[Math.floor(Math.random() * playerPlanets.length)] if (!targetPlanet) { return } const spyMission = createNPCSpyMission(npc, targetPlanet, allPlanets, config) if (spyMission) { // 创建即将到来的舰队警告 const alert = createIncomingFleetAlert(spyMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } } } // 4. 检查是否应该攻击玩家 if (shouldNPCAttackPlayer(npc, player, currentTime, config)) { // 检查是否有最近的侦查报告 if (npc.playerSpyReports && Object.keys(npc.playerSpyReports).length > 0) { // 选择一个侦查过的星球进行攻击 const spyReports = Object.values(npc.playerSpyReports) const recentReport = spyReports[Math.floor(Math.random() * spyReports.length)] // 确保找到了侦查报告 if (!recentReport) { return } // 找到目标星球 const targetPlanet = allPlanets.find(p => p.id === recentReport.targetPlanetId) if (targetPlanet) { const attackMission = createNPCAttackMission(npc, targetPlanet, recentReport, config) if (attackMission) { // 创建即将到来的舰队警告 const alert = createIncomingFleetAlert(attackMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } } } } // 5. 检查是否应该赠送资源给玩家(仅友好NPC) if (shouldNPCGiftPlayer(npc, player, currentTime)) { giftResourcesToPlayer(npc, player) } // 6. 检查是否应该协防玩家(仅友好NPC,玩家被攻击时) checkAndExecuteAllyDefense(npc, player, currentTime) // 7. 更新即将到来的舰队警告(删除过期的) updateIncomingFleetAlerts(player, currentTime) } /** * 带并发限制的NPC行为更新函数 * 防止同时产生过多侦查和攻击任务导致游戏卡顿 */ export const updateNPCBehaviorWithLimit = ( npc: NPC, player: Player, allPlanets: Planet[], debrisFields: Record, currentTime: number, limits: { activeSpyMissions: number activeAttackMissions: number config: DynamicBehaviorConfig } ): { spyCreated: boolean; attackCreated: boolean } => { const { activeSpyMissions, activeAttackMissions, config } = limits let spyCreated = false let attackCreated = false // 1. 检查并回收附近的残骸(优先级最高,不受并发限制) const nearbyDebris = findNearbyDebris(npc, debrisFields) if (nearbyDebris.length > 0) { const activeRecycleMissions = npc.fleetMissions?.filter(m => m.missionType === MissionType.Recycle && m.status === 'outbound') || [] const activeDebrisIds = new Set(activeRecycleMissions.map(m => m.debrisFieldId).filter(Boolean)) const availableDebris = nearbyDebris.filter(d => !activeDebrisIds.has(d.id)) if (availableDebris.length > 0) { const targetDebris = availableDebris[Math.floor(Math.random() * availableDebris.length)] if (targetDebris) { createNPCRecycleMission(npc, targetDebris, player, allPlanets) } } } // 2. 检查是否应该反击(优先于普通攻击,受攻击并发限制) if (activeAttackMissions < config.maxConcurrentAttackMissions && shouldNPCRevenge(npc, currentTime)) { const revengeMission = createNPCRevengeMission(npc, allPlanets, config) if (revengeMission) { const targetPlanet = allPlanets.find(p => p.id === revengeMission.targetPlanetId) if (targetPlanet) { const alert = createIncomingFleetAlert(revengeMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) attackCreated = true } return { spyCreated, attackCreated } } } // 3. 检查是否应该侦查玩家(受侦查并发限制) if (activeSpyMissions < config.maxConcurrentSpyMissions && shouldNPCSpyPlayer(npc, player, currentTime, config)) { const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) if (playerPlanets.length > 0) { const targetPlanet = playerPlanets[Math.floor(Math.random() * playerPlanets.length)] if (targetPlanet) { const spyMission = createNPCSpyMission(npc, targetPlanet, allPlanets, config) if (spyMission) { const alert = createIncomingFleetAlert(spyMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) spyCreated = true } } } } // 4. 检查是否应该攻击玩家(受攻击并发限制) if (activeAttackMissions < config.maxConcurrentAttackMissions && shouldNPCAttackPlayer(npc, player, currentTime, config)) { if (npc.playerSpyReports && Object.keys(npc.playerSpyReports).length > 0) { const spyReports = Object.values(npc.playerSpyReports) const recentReport = spyReports[Math.floor(Math.random() * spyReports.length)] if (recentReport) { const targetPlanet = allPlanets.find(p => p.id === recentReport.targetPlanetId) if (targetPlanet) { const attackMission = createNPCAttackMission(npc, targetPlanet, recentReport, config) if (attackMission) { const alert = createIncomingFleetAlert(attackMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) attackCreated = true } } } } } // 5. 检查是否应该赠送资源给玩家(仅友好NPC,不受并发限制) if (shouldNPCGiftPlayer(npc, player, currentTime)) { giftResourcesToPlayer(npc, player) } // 6. 检查是否应该协防玩家(仅友好NPC,玩家被攻击时,不受并发限制) checkAndExecuteAllyDefense(npc, player, currentTime) // 7. 更新即将到来的舰队警告(删除过期的) updateIncomingFleetAlerts(player, currentTime) return { spyCreated, attackCreated } } // ========== 测试辅助函数 ========== /** * 测试函数:强制NPC立即侦查玩家 * 用于开发和测试 */ export const forceNPCSpyPlayer = (npc: NPC, player: Player, allPlanets: Planet[], targetPlanetIndex = 0): FleetMission | null => { const config = calculateDynamicBehavior(player.points) // 选择目标星球 const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) if (playerPlanets.length === 0) { console.error('[Test] No player planets found') return null } const targetPlanet = playerPlanets[targetPlanetIndex] || playerPlanets[0] if (!targetPlanet) { console.error('[Test] Target planet not found') return null } // 创建侦查任务 const spyMission = createNPCSpyMission(npc, targetPlanet, allPlanets, config) if (spyMission) { // 创建即将到来的舰队警告 const alert = createIncomingFleetAlert(spyMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } else { console.error('[Test] Failed to create spy mission - NPC may not have spy probes') } return spyMission } /** * 测试函数:强制NPC立即攻击玩家 * 需要先有侦查报告 */ export const forceNPCAttackPlayer = (npc: NPC, player: Player, allPlanets: Planet[], targetPlanetIndex = 0): FleetMission | null => { const config = calculateDynamicBehavior(player.points) // 检查是否有侦查报告 if (!npc.playerSpyReports || Object.keys(npc.playerSpyReports).length === 0) { console.error('[Test] No spy reports available - NPC must spy first!') return null } // 选择目标星球 const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) if (playerPlanets.length === 0) { console.error('[Test] No player planets found') return null } const targetPlanet = playerPlanets[targetPlanetIndex] || playerPlanets[0] if (!targetPlanet) { console.error('[Test] Target planet not found') return null } // 获取该星球的侦查报告 const spyReport = npc.playerSpyReports[targetPlanet.id] if (!spyReport) { console.error(`[Test] No spy report for ${targetPlanet.name} - spy this planet first!`) return null } // 创建攻击任务 const attackMission = createNPCAttackMission(npc, targetPlanet, spyReport, config) if (attackMission) { // 创建即将到来的舰队警告 const alert = createIncomingFleetAlert(attackMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } else { console.error('[Test] Failed to create attack mission - NPC may not have ships') } return attackMission } /** * 测试函数:强制NPC先侦查再攻击 * 一步到位的测试函数 */ export const forceNPCSpyAndAttack = ( npc: NPC, player: Player, allPlanets: Planet[], targetPlanetIndex = 0 ): { spyMission: FleetMission | null; attackMission: FleetMission | null } => { // 1. 先侦查 const spyMission = forceNPCSpyPlayer(npc, player, allPlanets, targetPlanetIndex) if (!spyMission) { return { spyMission: null, attackMission: null } } // 2. 模拟侦查到达,立即生成侦查报告 const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) const targetPlanet = playerPlanets[targetPlanetIndex] || playerPlanets[0] if (!targetPlanet) { console.error('[Test] Target planet not found') return { spyMission, attackMission: null } } const { spyReport, spiedNotification } = processNPCSpyArrival(npc, spyMission, targetPlanet, player) // 保存侦查报告到NPC if (!npc.playerSpyReports) { npc.playerSpyReports = {} } npc.playerSpyReports[targetPlanet.id] = spyReport // 添加被侦查通知给玩家 if (!player.spiedNotifications) { player.spiedNotifications = [] } player.spiedNotifications.push(spiedNotification) // 3. 立即发起攻击 const attackMission = forceNPCAttackPlayer(npc, player, allPlanets, targetPlanetIndex) return { spyMission, attackMission } } /** * 测试函数:加速舰队任务到达时间 * 将任务的到达时间设置为现在+指定秒数 */ export const accelerateNPCMission = (npc: NPC, missionId: string, arriveInSeconds = 5, player?: Player): boolean => { if (!npc.fleetMissions) { console.error('[Test] NPC has no fleet missions') return false } const mission = npc.fleetMissions.find(m => m.id === missionId) if (!mission) { console.error('[Test] Mission not found') return false } const now = Date.now() const flightTime = arriveInSeconds * 1000 // 飞行时间(毫秒) // 同时修改 departureTime 和 arrivalTime,保持飞行时间为指定秒数 mission.departureTime = now mission.arrivalTime = now + flightTime // 同时更新对应的 IncomingFleetAlert if (player && player.incomingFleetAlerts) { const alert = player.incomingFleetAlerts.find(a => a.id === missionId) if (alert) { alert.arrivalTime = mission.arrivalTime } } return true } /** * 测试函数:加速所有NPC舰队任务 */ export const accelerateAllNPCMissions = (npc: NPC, arriveInSeconds = 5, player?: Player): number => { if (!npc.fleetMissions) { console.error('[Test] NPC has no fleet missions') return 0 } const now = Date.now() const flightTime = arriveInSeconds * 1000 let count = 0 npc.fleetMissions.forEach(mission => { if (mission.status === 'outbound') { // 同时修改 departureTime 和 arrivalTime mission.departureTime = now mission.arrivalTime = now + flightTime // 同时更新对应的 IncomingFleetAlert if (player && player.incomingFleetAlerts) { const alert = player.incomingFleetAlerts.find(a => a.id === mission.id) if (alert) { alert.arrivalTime = mission.arrivalTime } } count++ } else if (mission.status === 'returning' && mission.returnTime) { // 对于返回任务,保持原来的逻辑 mission.returnTime = now + flightTime count++ } }) return count } /** * 检查NPC星球附近是否有残骸场 * 返回NPC可以回收的残骸场列表 * NPC会主动寻找同一星系内的残骸进行回收 */ export const findNearbyDebris = (npc: NPC, debrisFields: Record): DebrisField[] => { const nearbyDebris: DebrisField[] = [] for (const debris of Object.values(debrisFields)) { // 检查残骸是否在NPC的星球附近(同一星系内) for (const npcPlanet of npc.planets) { if (debris.position.galaxy === npcPlanet.position.galaxy && debris.position.system === npcPlanet.position.system) { // 检查残骸是否有足够资源值得回收(至少1000金属或水晶) if (debris.resources.metal > 1000 || debris.resources.crystal > 1000) { // 计算距离,确保不会太远(最多在同一星系内) const distance = Math.abs(debris.position.position - npcPlanet.position.position) if (distance <= 15) { // 同一星系内最多15个位置 nearbyDebris.push(debris) } } } } } return nearbyDebris } /** * 创建NPC回收残骸任务 */ export const createNPCRecycleMission = (npc: NPC, debris: DebrisField, player: Player, allPlanets: Planet[]): FleetMission | null => { // 找到离残骸最近的NPC星球 let closestPlanet: Planet | null = null let minDistance = Infinity for (const npcPlanet of npc.planets) { if (npcPlanet.position.galaxy === debris.position.galaxy && npcPlanet.position.system === debris.position.system) { const distance = Math.abs(npcPlanet.position.position - debris.position.position) if (distance < minDistance) { minDistance = distance closestPlanet = npcPlanet } } } if (!closestPlanet) { return null } // 检查NPC是否有回收船 const recyclers = closestPlanet.fleet[ShipType.Recycler] || 0 if (recyclers === 0) { return null } // 计算需要多少回收船(每艘回收船容量20000) const totalDebris = debris.resources.metal + debris.resources.crystal const recyclersNeeded = Math.min(Math.ceil(totalDebris / 20000), recyclers) // 创建回收舰队 const fleet: Partial = { [ShipType.Recycler]: recyclersNeeded } // 从NPC星球扣除舰队 closestPlanet.fleet[ShipType.Recycler] = recyclers - recyclersNeeded // 计算飞行时间 const distance = fleetLogic.calculateDistance(closestPlanet.position, debris.position) const recyclerSpeed = 2000 // 回收船基础速度 const flightTime = fleetLogic.calculateFlightTime(distance, recyclerSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-recycle-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: false, originPlanetId: closestPlanet.id, targetPosition: debris.position, debrisFieldId: debris.id, missionType: MissionType.Recycle, fleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) // 检查残骸位置是否有玩家星球,如果有则发送警告 const playerPlanetAtDebris = allPlanets.find( p => p.ownerId === player.id && p.position.galaxy === debris.position.galaxy && p.position.system === debris.position.system && p.position.position === debris.position.position ) if (playerPlanetAtDebris) { // 创建即将到来的舰队警告(非敌对) const alert = createIncomingFleetAlert(mission, npc, playerPlanetAtDebris) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) // 创建NPC活动通知 if (!player.npcActivityNotifications) { player.npcActivityNotifications = [] } player.npcActivityNotifications.push({ id: `npc-activity-${mission.id}`, timestamp: now, npcId: npc.id, npcName: npc.name, activityType: 'recycle', targetPosition: debris.position, targetPlanetId: playerPlanetAtDebris.id, targetPlanetName: playerPlanetAtDebris.name, arrivalTime: mission.arrivalTime, read: false }) } return mission } /** * NPC被攻击后的反应 * 记录攻击者,并根据情况决定是否反击或加强防御 */ export const handleNPCAttacked = (npc: NPC, attackerId: string, attackerPlanetId: string | undefined): void => { // 初始化被攻击记录 if (!npc.attackedBy) { npc.attackedBy = {} } // 记录攻击者和被攻击次数 if (!npc.attackedBy[attackerId]) { npc.attackedBy[attackerId] = { count: 0, lastAttackTime: 0, planetId: attackerPlanetId } } npc.attackedBy[attackerId].count++ npc.attackedBy[attackerId].lastAttackTime = Date.now() if (attackerPlanetId) { npc.attackedBy[attackerId].planetId = attackerPlanetId } // 设置警戒状态(被攻击后1小时内保持警戒) npc.alertUntil = Date.now() + 3600 * 1000 // 如果被同一个玩家攻击超过3次,标记为高优先级反击目标 if (npc.attackedBy[attackerId].count >= 3) { npc.revengeTarget = attackerId } } /** * 检查NPC是否应该反击 */ export const shouldNPCRevenge = (npc: NPC, currentTime: number): boolean => { // 如果没有复仇目标,不反击 if (!npc.revengeTarget || !npc.attackedBy) { return false } // 获取 AI 行为修改器 const aiModifier = getAIBehaviorModifier(npc.aiType) // 商人型 NPC 反击意愿很低,可能选择不反击 if (aiModifier.revengeMultiplier < 1.0 && Math.random() > aiModifier.revengeMultiplier) { return false } const attackRecord = npc.attackedBy[npc.revengeTarget] if (!attackRecord) { return false } // 被攻击后24小时内可以反击(防守型可能更长时间记仇) const revengeWindow = 24 * 3600 * 1000 * aiModifier.revengeMultiplier const timeSinceLastAttack = currentTime - attackRecord.lastAttackTime if (timeSinceLastAttack > revengeWindow) { return false } // 至少等待一段时间后再反击(防守型反应更快) const minWaitTime = aiModifier.defenseFocus ? 300 * 1000 : 600 * 1000 // 防守型5分钟,其他10分钟 if (timeSinceLastAttack < minWaitTime) { return false } return true } /** * 创建NPC反击任务 */ export const createNPCRevengeMission = (npc: NPC, allPlanets: Planet[], config: DynamicBehaviorConfig): FleetMission | null => { if (!npc.revengeTarget || !npc.attackedBy) { return null } const attackRecord = npc.attackedBy[npc.revengeTarget] if (!attackRecord || !attackRecord.planetId) { return null } // 找到攻击者的星球 const targetPlanet = allPlanets.find(p => p.id === attackRecord.planetId) if (!targetPlanet) { return null } // 选择NPC的最佳星球作为起点 const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!npcPlanet) { return null } // 获取 AI 行为修改器 const aiModifier = getAIBehaviorModifier(npc.aiType) // 反击时派出更多舰队(基础多50%,根据 AI 类型调整) // 防守型/侵略型反击更猛烈 const revengeMultiplier = 1.5 * aiModifier.revengeMultiplier const revengeFleet = decideAttackFleet(npc, npcPlanet, {} as SpyReport, { ...config, attackFleetSizeRatio: Math.min(1.0, config.attackFleetSizeRatio * revengeMultiplier) }) if (!revengeFleet) { return null } // 从NPC星球扣除舰队 for (const [shipType, count] of Object.entries(revengeFleet)) { npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算飞行时间 const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) let minSpeed = Infinity for (const _shipType of Object.keys(revengeFleet)) { const baseSpeed = 10000 minSpeed = Math.min(minSpeed, baseSpeed) } const flightTime = fleetLogic.calculateFlightTime(distance, minSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-revenge-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: true, originPlanetId: npcPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType: MissionType.Attack, fleet: revengeFleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) // 清除复仇目标(已经反击) npc.revengeTarget = undefined return mission } /** * NPC状态诊断函数 - 用于调试和了解NPC当前状态 */ // 诊断原因类型,包含翻译键和参数 export interface DiagnosticReason { key: string // 翻译键,如 'friendlyNoAction', 'insufficientProbes' params?: Record // 翻译参数,如 { current: 5, required: 10 } } // 关系状态翻译键类型 export type RelationStatusKey = 'friendly' | 'hostile' | 'neutral' | 'noRelation' | 'noRelationNeutral' export interface NPCDiagnosticInfo { npcId: string npcName: string difficulty: string aiType?: NPCAIType // AI 行为类型 relationStatus: string // 保持原有字段用于显示 relationStatusKey: RelationStatusKey // 翻译键 reputation: number canSpy: boolean canAttack: boolean spyProbes: number totalFleetPower: number lastSpyTime: number lastAttackTime: number timeSinceLastSpy: number timeSinceLastAttack: number nextSpyIn: number nextAttackIn: number attackProbability: number reasons: DiagnosticReason[] // 改为结构化原因数组 } export const diagnoseNPCBehavior = (npcs: NPC[], player: Player, currentTime: number): NPCDiagnosticInfo[] => { const playerPoints = player.points || 0 const config = calculateDynamicBehavior(playerPoints) return npcs.map(npc => { const planet = npc.planets[0] const relation = npc.relations?.[player.id] const reasons: DiagnosticReason[] = [] // 检查外交关系 let canSpy = false // 默认不能侦查,只有敌对NPC才能侦查 let canAttack = false // 默认不能攻击,只有敌对NPC才能攻击 let relationStatus = '' let relationStatusKey: RelationStatusKey = 'noRelation' let reputation = 0 if (relation) { reputation = relation.reputation || 0 if (relation.status === RelationStatus.Friendly) { relationStatus = 'friendly' relationStatusKey = 'friendly' reasons.push({ key: 'friendlyNoAction' }) } else if (relation.status === RelationStatus.Neutral) { relationStatus = 'neutral' relationStatusKey = 'neutral' reasons.push({ key: 'neutralNoAction' }) } else if (relation.status === RelationStatus.Hostile) { relationStatus = 'hostile' relationStatusKey = 'hostile' canSpy = true canAttack = true reasons.push({ key: 'hostileWillAct' }) } } else { // 无关系的NPC视为中立 relationStatus = 'noRelationNeutral' relationStatusKey = 'noRelationNeutral' reasons.push({ key: 'noRelationNeutral' }) } // 检查侦查探测器数量 const spyProbes = planet?.fleet?.[ShipType.EspionageProbe] || 0 if (spyProbes < config.minSpyProbes) { canSpy = false reasons.push({ key: 'insufficientProbes', params: { current: spyProbes, required: config.minSpyProbes } }) } // 计算舰队战力 let totalFleetPower = 0 if (planet?.fleet) { Object.entries(planet.fleet).forEach(([shipType, count]) => { const shipConfig = SHIPS[shipType as ShipType] if (shipConfig) { const power = shipConfig.attack + shipConfig.shield + shipConfig.armor / 10 totalFleetPower += power * (count as number) } }) } if (totalFleetPower === 0) { canAttack = false reasons.push({ key: 'noFleet' }) } // 时间检查 const lastSpyTime = npc.lastSpyTime || 0 const lastAttackTime = npc.lastAttackTime || 0 const timeSinceLastSpy = Math.floor((currentTime - lastSpyTime) / 1000) const timeSinceLastAttack = Math.floor((currentTime - lastAttackTime) / 1000) const nextSpyIn = Math.max(0, config.spyInterval - timeSinceLastSpy) const nextAttackIn = Math.max(0, config.attackInterval - timeSinceLastAttack) if (timeSinceLastSpy < config.spyInterval) { reasons.push({ key: 'spyCooldown', params: { min: Math.floor(nextSpyIn / 60), sec: nextSpyIn % 60 } }) } if (timeSinceLastAttack < config.attackInterval) { reasons.push({ key: 'attackCooldown', params: { min: Math.floor(nextAttackIn / 60), sec: nextAttackIn % 60 } }) } // 检查是否已经侦查过玩家 const hasSpiedPlayer = npc.playerSpyReports && Object.keys(npc.playerSpyReports).length > 0 if (!hasSpiedPlayer && canAttack) { canAttack = false reasons.push({ key: 'notSpiedYet' }) } // 计算实际攻击概率 let actualAttackProbability = config.attackProbability if (relation?.status === RelationStatus.Hostile) { actualAttackProbability = Math.min(config.attackProbability * 2.0, 1.0) } return { npcId: npc.id, npcName: npc.name, difficulty: npc.difficulty, aiType: npc.aiType, relationStatus, relationStatusKey, reputation, canSpy, canAttack, spyProbes, totalFleetPower: Math.floor(totalFleetPower), lastSpyTime, lastAttackTime, timeSinceLastSpy, timeSinceLastAttack, nextSpyIn, nextAttackIn, attackProbability: actualAttackProbability, reasons } }) } // ========== 敌对NPC增强行为系统 ========== /** * 获取NPC的报复等级 * 基于被攻击次数确定报复强度 */ export const getRevengeLevel = (npc: NPC, playerId: string): RevengeLevel => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const levels = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION.LEVELS const memoryDuration = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION.MEMORY_DURATION // 默认等级(用于数组为空或未找到时) const defaultLevel: RevengeLevel = levels[0] || { attackCount: 1, fleetMultiplier: 1.0, attackInterval: 600, name: 'mild' } const attackRecord = npc.attackedBy?.[playerId] if (!attackRecord) { return defaultLevel // 默认最低等级 } // 检查记忆是否过期 const now = Date.now() if (now - attackRecord.lastAttackTime > memoryDuration) { return defaultLevel // 记忆过期,重置为最低等级 } // 根据被攻击次数确定报复等级 const attackCount = attackRecord.count let currentLevel = defaultLevel for (const level of levels) { if (attackCount >= level.attackCount) { currentLevel = level } } return currentLevel } /** * 检查NPC是否应该执行骚扰战术 */ export const shouldNPCHarass = (npc: NPC, player: Player, currentTime: number): boolean => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const harassConfig = HOSTILE_BEHAVIOR_CONFIG.HARASSMENT if (!harassConfig.ENABLED) { return false } // 检查外交关系 const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Hostile) { return false } // 检查好感度是否足够低 if (relation.reputation > harassConfig.TRIGGER_REPUTATION) { return false } // 检查骚扰冷却时间 const lastHarassTime = (npc as any).lastHarassTime || 0 if (currentTime - lastHarassTime < harassConfig.INTERVAL * 1000) { return false } // 概率判断 return Math.random() < harassConfig.PROBABILITY } /** * 选择骚扰类型 */ export const selectHarassmentType = (): HarassmentType => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const types = HOSTILE_BEHAVIOR_CONFIG.HARASSMENT.TYPES const rand = Math.random() if (rand < types.spy) { return 'spy' } else if (rand < types.spy + types.raid) { return 'raid' } else { return 'intimidate' } } /** * 创建骚扰任务 */ export const createHarassmentMission = ( npc: NPC, player: Player, allPlanets: Planet[], harassmentType: HarassmentType ): FleetMission | null => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const harassConfig = HOSTILE_BEHAVIOR_CONFIG.HARASSMENT // 选择随机玩家星球作为目标 const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) if (playerPlanets.length === 0) return null const targetPlanet = playerPlanets[Math.floor(Math.random() * playerPlanets.length)] if (!targetPlanet) return null // 选择NPC最佳星球 const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!npcPlanet) return null let fleet: Partial = {} let missionType: MissionType = MissionType.Attack if (harassmentType === 'spy') { // 侦查骚扰:派出间谍探测器 const probes = npcPlanet.fleet[ShipType.EspionageProbe] || 0 if (probes < 1) return null fleet = { [ShipType.EspionageProbe]: Math.min(probes, 3) } missionType = MissionType.Spy } else { // 袭击或威慑:派出小规模战斗舰队 const fleetRatio = harassConfig.FLEET_SIZE_RATIO.min + Math.random() * (harassConfig.FLEET_SIZE_RATIO.max - harassConfig.FLEET_SIZE_RATIO.min) const combatShips = [ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser] let hasShips = false for (const shipType of combatShips) { const available = npcPlanet.fleet[shipType] || 0 if (available > 0) { const sendCount = Math.max(1, Math.floor(available * fleetRatio)) fleet[shipType] = sendCount hasShips = true } } if (!hasShips) return null // 威慑任务使用部署类型(不攻击,只是展示武力) if (harassmentType === 'intimidate') { missionType = MissionType.Deploy } } // 从NPC星球扣除舰队 for (const [shipType, count] of Object.entries(fleet)) { npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算飞行时间 const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) const fleetMinSpeed = getFleetMinSpeed(fleet) const flightTime = fleetLogic.calculateFlightTime(distance, fleetMinSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-harass-${harassmentType}-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: harassmentType !== 'intimidate', // 威慑不是敌对行动 originPlanetId: npcPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType, fleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 更新骚扰时间 ;(npc as any).lastHarassTime = now // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) return mission } /** * 检查是否可以发起围攻 * 需要多个敌对NPC协调 */ export const canInitiateSiege = (npcs: NPC[], player: Player, currentTime: number): { canSiege: boolean; eligibleNpcs: NPC[] } => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const siegeConfig = HOSTILE_BEHAVIOR_CONFIG.SIEGE if (!siegeConfig.ENABLED) { return { canSiege: false, eligibleNpcs: [] } } // 找出所有符合围攻条件的敌对NPC const eligibleNpcs = npcs.filter(npc => { const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Hostile) { return false } // 检查好感度是否足够低 if (relation.reputation > siegeConfig.TRIGGER_REPUTATION) { return false } // 检查围攻冷却 const lastSiegeTime = (npc as any).lastSiegeTime || 0 if (currentTime - lastSiegeTime < siegeConfig.COOLDOWN) { return false } // 检查是否有足够的舰队 const npcPlanet = npc.planets[0] if (!npcPlanet) return false const combatPower = calculateNPCCombatPower(npcPlanet) return combatPower >= MIN_COMBAT_POWER_FOR_SPY * 10 // 围攻需要较大战力 }) // 检查是否有足够的NPC参与围攻 const canSiege = eligibleNpcs.length >= siegeConfig.MIN_HOSTILE_NPCS return { canSiege, eligibleNpcs } } /** * 发起围攻 * 协调多个NPC同时攻击玩家 */ export const initiateSiege = ( eligibleNpcs: NPC[], player: Player, allPlanets: Planet[] ): { missions: FleetMission[]; coordination: SiegeCoordination } | null => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const siegeConfig = HOSTILE_BEHAVIOR_CONFIG.SIEGE if (eligibleNpcs.length < siegeConfig.MIN_HOSTILE_NPCS) { return null } // 选择目标星球(选择资源最多的) const playerPlanets = allPlanets.filter(p => p.ownerId === player.id) if (playerPlanets.length === 0) return null let targetPlanet = playerPlanets[0] let maxResources = 0 for (const planet of playerPlanets) { const totalResources = planet.resources.metal + planet.resources.crystal + planet.resources.deuterium if (totalResources > maxResources) { maxResources = totalResources targetPlanet = planet } } if (!targetPlanet) return null // 计算统一到达时间(以最远NPC的飞行时间为基准) let maxFlightTime = 0 for (const npc of eligibleNpcs) { const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (npcPlanet) { const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) const flightTime = fleetLogic.calculateFlightTime(distance, 10000) // 使用基准速度 maxFlightTime = Math.max(maxFlightTime, flightTime) } } const now = Date.now() const coordWindow = siegeConfig.COORDINATION_WINDOW const baseArrivalTime = now + maxFlightTime * 1000 + coordWindow const missions: FleetMission[] = [] const participantNpcIds: string[] = [] // 为每个NPC创建攻击任务 for (const npc of eligibleNpcs) { const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!npcPlanet) continue // 计算围攻舰队 const fleetRatio = siegeConfig.FLEET_RATIO.min + Math.random() * (siegeConfig.FLEET_RATIO.max - siegeConfig.FLEET_RATIO.min) const combatShips = [ ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship, ShipType.Bomber, ShipType.Destroyer, ShipType.Battlecruiser ] const siegeFleet: Partial = {} let hasShips = false for (const shipType of combatShips) { const available = npcPlanet.fleet[shipType] || 0 if (available > 0) { const sendCount = Math.max(1, Math.floor(available * fleetRatio)) siegeFleet[shipType] = sendCount hasShips = true } } if (!hasShips) continue // 从NPC星球扣除舰队 for (const [shipType, count] of Object.entries(siegeFleet)) { npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算随机偏移的到达时间(在协调窗口内) const arrivalOffset = Math.random() * coordWindow const arrivalTime = baseArrivalTime + arrivalOffset // 反推出发时间 const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) const siegeMinSpeed = getFleetMinSpeed(siegeFleet) const actualFlightTime = fleetLogic.calculateFlightTime(distance, siegeMinSpeed) const departureTime = arrivalTime - actualFlightTime * 1000 const mission: FleetMission = { id: `npc-siege-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: true, originPlanetId: npcPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType: MissionType.Attack, fleet: siegeFleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: Math.max(now, departureTime), arrivalTime, status: 'outbound' } // 更新NPC的围攻时间 ;(npc as any).lastSiegeTime = now // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) missions.push(mission) participantNpcIds.push(npc.id) } if (missions.length < siegeConfig.MIN_HOSTILE_NPCS) { return null // 没有足够的NPC成功发起围攻 } const coordination: SiegeCoordination = { targetPlanetId: targetPlanet.id, targetPosition: targetPlanet.position, coordinatorNpcId: eligibleNpcs[0]?.id || '', participantNpcIds, arrivalTime: baseArrivalTime, startedAt: now } return { missions, coordination } } /** * 执行报复升级攻击 * 根据报复等级派出不同强度的攻击 */ export const executeEscalatedRevenge = ( npc: NPC, player: Player, allPlanets: Planet[], config: DynamicBehaviorConfig ): FleetMission | null => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const escalationConfig = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION if (!escalationConfig.ENABLED) { return null } // 获取报复等级 const revengeLevel = getRevengeLevel(npc, player.id) // 检查攻击间隔 const lastRevengeTime = npc.lastAttackTime || 0 const now = Date.now() if (now - lastRevengeTime < revengeLevel.attackInterval * 1000) { return null } // 找到攻击者的星球 const attackRecord = npc.attackedBy?.[player.id] if (!attackRecord?.planetId) { return null } const targetPlanet = allPlanets.find(p => p.id === attackRecord.planetId) if (!targetPlanet) { return null } // 选择NPC最佳星球 const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position) if (!npcPlanet) { return null } // 根据报复等级计算舰队规模 const baseFleetRatio = config.attackFleetSizeRatio const escalatedRatio = Math.min(1.0, baseFleetRatio * revengeLevel.fleetMultiplier) const combatShips = [ ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship, ShipType.Bomber, ShipType.Destroyer, ShipType.Battlecruiser, ShipType.Deathstar ] const revengeFleet: Partial = {} let hasShips = false for (const shipType of combatShips) { const available = npcPlanet.fleet[shipType] || 0 if (available > 0) { const sendCount = Math.max(1, Math.floor(available * escalatedRatio)) revengeFleet[shipType] = sendCount hasShips = true } } if (!hasShips) { return null } // 从NPC星球扣除舰队 for (const [shipType, count] of Object.entries(revengeFleet)) { npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算飞行时间 const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position) const revengeMinSpeed = getFleetMinSpeed(revengeFleet) const flightTime = fleetLogic.calculateFlightTime(distance, revengeMinSpeed) const mission: FleetMission = { id: `npc-revenge-escalated-${revengeLevel.name}-${npc.id}-${now}`, playerId: npc.id, npcId: npc.id, isHostile: true, originPlanetId: npcPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType: MissionType.Attack, fleet: revengeFleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 更新攻击时间 npc.lastAttackTime = now // 添加到NPC任务列表 if (!npc.fleetMissions) { npc.fleetMissions = [] } npc.fleetMissions.push(mission) return mission } /** * 呼叫盟友NPC助战 * 在报复等级较高时,可能呼叫盟友一起攻击 */ export const callAlliesForRevenge = ( npc: NPC, allNpcs: NPC[], player: Player, allPlanets: Planet[], _config: DynamicBehaviorConfig ): FleetMission[] => { const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const escalationConfig = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION if (!escalationConfig.CALL_ALLIES) { return [] } // 获取报复等级 const revengeLevel = getRevengeLevel(npc, player.id) // 计算呼叫盟友概率(报复等级越高概率越大) const levelIndex = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION.LEVELS.findIndex(l => l.name === revengeLevel.name) const allyCallProbability = escalationConfig.ALLY_CALL_BASE_PROBABILITY * (levelIndex + 1) if (Math.random() > allyCallProbability) { return [] } // 找到NPC的盟友(同样敌对玩家的NPC) const allyMissions: FleetMission[] = [] const attackRecord = npc.attackedBy?.[player.id] if (!attackRecord?.planetId) { return [] } const targetPlanet = allPlanets.find(p => p.id === attackRecord.planetId) if (!targetPlanet) { return [] } for (const allyNpc of allNpcs) { if (allyNpc.id === npc.id) continue // 跳过自己 // 检查是否是盟友(也敌对玩家) const allyRelation = allyNpc.relations?.[player.id] if (!allyRelation || allyRelation.status !== RelationStatus.Hostile) { continue } // 检查NPC之间是否有盟友关系 const interNpcRelation = allyNpc.allies?.includes(npc.id) || npc.allies?.includes(allyNpc.id) if (!interNpcRelation) { // 即使不是正式盟友,敌对同一玩家的NPC也有概率响应 if (Math.random() > 0.3) continue } // 选择盟友NPC的最佳星球 const allyPlanet = selectBestNPCPlanet(allyNpc, targetPlanet.position) if (!allyPlanet) continue // 派出援军舰队(较小规模) const combatShips = [ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship] const allyFleet: Partial = {} let hasShips = false for (const shipType of combatShips) { const available = allyPlanet.fleet[shipType] || 0 if (available > 0) { const sendCount = Math.max(1, Math.floor(available * 0.3)) // 援军派30% allyFleet[shipType] = sendCount hasShips = true } } if (!hasShips) continue // 从盟友星球扣除舰队 for (const [shipType, count] of Object.entries(allyFleet)) { allyPlanet.fleet[shipType as ShipType] = (allyPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算飞行时间 const distance = fleetLogic.calculateDistance(allyPlanet.position, targetPlanet.position) const allyMinSpeed = getFleetMinSpeed(allyFleet) const flightTime = fleetLogic.calculateFlightTime(distance, allyMinSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-ally-revenge-${allyNpc.id}-${now}`, playerId: allyNpc.id, npcId: allyNpc.id, isHostile: true, originPlanetId: allyPlanet.id, targetPosition: targetPlanet.position, targetPlanetId: targetPlanet.id, missionType: MissionType.Attack, fleet: allyFleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 添加到盟友NPC任务列表 if (!allyNpc.fleetMissions) { allyNpc.fleetMissions = [] } allyNpc.fleetMissions.push(mission) allyMissions.push(mission) } return allyMissions } /** * 增强版NPC行为更新函数 * 包含围攻、骚扰、报复升级等增强行为 */ export const updateHostileNPCBehavior = ( npc: NPC, allNpcs: NPC[], player: Player, allPlanets: Planet[], _debrisFields: Record, currentTime: number, activeSiege?: SiegeCoordination ): { harassMission?: FleetMission siegeMissions?: FleetMission[] revengeMission?: FleetMission allyRevengeMissions?: FleetMission[] newSiegeCoordination?: SiegeCoordination } => { const result: { harassMission?: FleetMission siegeMissions?: FleetMission[] revengeMission?: FleetMission allyRevengeMissions?: FleetMission[] newSiegeCoordination?: SiegeCoordination } = {} const config = calculateDynamicBehavior(player.points) // 检查是否是敌对NPC const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Hostile) { return result } // 1. 检查报复升级(优先级最高) if (npc.revengeTarget === player.id) { const revengeMission = executeEscalatedRevenge(npc, player, allPlanets, config) if (revengeMission) { result.revengeMission = revengeMission // 创建警告 const targetPlanet = allPlanets.find(p => p.id === revengeMission.targetPlanetId) if (targetPlanet) { const alert = createIncomingFleetAlert(revengeMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } // 呼叫盟友助战 const allyMissions = callAlliesForRevenge(npc, allNpcs, player, allPlanets, config) if (allyMissions.length > 0) { result.allyRevengeMissions = allyMissions // 为盟友任务创建警告 for (const allyMission of allyMissions) { const targetPlanet = allPlanets.find(p => p.id === allyMission.targetPlanetId) const allyNpc = allNpcs.find(n => n.id === allyMission.npcId) if (targetPlanet && allyNpc) { const alert = createIncomingFleetAlert(allyMission, allyNpc, targetPlanet) player.incomingFleetAlerts.push(alert) } } } return result // 报复后不执行其他行为 } } // 2. 检查围攻(如果没有正在进行的围攻) if (!activeSiege) { const { canSiege, eligibleNpcs } = canInitiateSiege(allNpcs, player, currentTime) if (canSiege && eligibleNpcs.includes(npc)) { // 概率触发围攻 const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG if (Math.random() < HOSTILE_BEHAVIOR_CONFIG.SIEGE.BASE_PROBABILITY) { const siegeResult = initiateSiege(eligibleNpcs, player, allPlanets) if (siegeResult) { result.siegeMissions = siegeResult.missions result.newSiegeCoordination = siegeResult.coordination // 为所有围攻任务创建警告 for (const mission of siegeResult.missions) { const targetPlanet = allPlanets.find(p => p.id === mission.targetPlanetId) const attackerNpc = allNpcs.find(n => n.id === mission.npcId) if (targetPlanet && attackerNpc) { const alert = createIncomingFleetAlert(mission, attackerNpc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } } return result // 围攻后不执行其他行为 } } } } // 3. 检查骚扰战术 if (shouldNPCHarass(npc, player, currentTime)) { const harassmentType = selectHarassmentType() const harassMission = createHarassmentMission(npc, player, allPlanets, harassmentType) if (harassMission) { result.harassMission = harassMission // 创建警告 const targetPlanet = allPlanets.find(p => p.id === harassMission.targetPlanetId) if (targetPlanet) { const alert = createIncomingFleetAlert(harassMission, npc, targetPlanet) if (!player.incomingFleetAlerts) { player.incomingFleetAlerts = [] } player.incomingFleetAlerts.push(alert) } } } return result } // ========== 中立NPC行为系统 ========== /** * 检查中立NPC是否应该提议贸易 */ export const shouldNPCOfferTrade = (npc: NPC, player: Player, currentTime: number): boolean => { const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const tradeConfig = NEUTRAL_BEHAVIOR_CONFIG.TRADE if (!tradeConfig.ENABLED) { return false } // 检查玩家当前贸易提议数量是否已达上限 const maxOffers = (tradeConfig as any).MAX_PENDING_OFFERS || 30 if ((player.tradeOffers?.length || 0) >= maxOffers) { return false } // 检查外交关系 const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Neutral) { return false } // 检查好感度范围 if (relation.reputation < tradeConfig.MIN_REPUTATION || relation.reputation > tradeConfig.MAX_REPUTATION) { return false } // 检查贸易冷却 const lastTradeTime = (npc as any).lastTradeOfferTime || 0 if (currentTime - lastTradeTime < tradeConfig.CHECK_INTERVAL * 1000) { return false } // 概率判断 return Math.random() < tradeConfig.PROBABILITY } /** * 创建贸易提议 */ export const createTradeOffer = (npc: NPC, player: Player): TradeOffer | null => { const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const tradeConfig = NEUTRAL_BEHAVIOR_CONFIG.TRADE // 获取NPC主星球 const npcPlanet = npc.planets[0] if (!npcPlanet) return null // 计算玩家总产量 const playerProduction = calculatePlayerTotalProduction(player) // 随机决定贸易量 const tradeMultiplier = tradeConfig.TRADE_AMOUNT.min + Math.random() * (tradeConfig.TRADE_AMOUNT.max - tradeConfig.TRADE_AMOUNT.min) // 随机选择交易类型 const tradeTypes: Array<{ offer: 'metal' | 'crystal' | 'deuterium'; request: 'metal' | 'crystal' | 'deuterium'; rate: number }> = [ { offer: 'crystal', request: 'metal', rate: tradeConfig.EXCHANGE_RATES.metalToCrystal }, { offer: 'deuterium', request: 'metal', rate: tradeConfig.EXCHANGE_RATES.metalToDeuterium }, { offer: 'deuterium', request: 'crystal', rate: tradeConfig.EXCHANGE_RATES.crystalToDeuterium } ] const selectedTrade = tradeTypes[Math.floor(Math.random() * tradeTypes.length)] if (!selectedTrade) return null // 计算交易量(基于玩家产量和NPC资源) const baseAmount = playerProduction[selectedTrade.request] * tradeMultiplier const npcHas = Math.floor(npcPlanet.resources[selectedTrade.offer] || 0) // 验证基础数值有效性 if (!isFinite(baseAmount) || baseAmount <= 0 || !isFinite(npcHas) || npcHas <= 0) { return null } // NPC提供的资源量 const offerAmount = Math.min(Math.floor(baseAmount / selectedTrade.rate), npcHas) if (!isFinite(offerAmount) || offerAmount <= 0) return null // 玩家需要支付的资源量(含手续费) const requestAmount = Math.floor(offerAmount * selectedTrade.rate * (1 + tradeConfig.FEE_PERCENTAGE / 100)) if (!isFinite(requestAmount) || requestAmount <= 0) return null const now = Date.now() const offer: TradeOffer = { id: `trade-${npc.id}-${now}`, npcId: npc.id, npcName: npc.name, timestamp: now, offeredResources: { type: selectedTrade.offer, amount: offerAmount }, requestedResources: { type: selectedTrade.request, amount: requestAmount }, expiresAt: now + 30 * 60 * 1000, // 30分钟后过期 status: 'pending' } // 更新NPC的贸易时间 ;(npc as any).lastTradeOfferTime = now return offer } /** * 处理贸易接受 */ export const acceptTradeOffer = ( offer: TradeOffer, npc: NPC, player: Player, playerPlanet: Planet ): { success: boolean; reason?: string } => { const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const tradeConfig = NEUTRAL_BEHAVIOR_CONFIG.TRADE // 检查贸易是否过期 if (Date.now() > offer.expiresAt) { offer.status = 'expired' return { success: false, reason: 'errors.tradeExpired' } } // 检查玩家是否有足够资源 const playerHas = playerPlanet.resources[offer.requestedResources.type] if (playerHas < offer.requestedResources.amount) { return { success: false, reason: 'errors.insufficientResources' } } // 获取NPC主星球 const npcPlanet = npc.planets[0] if (!npcPlanet) { return { success: false, reason: 'errors.npcPlanetNotFound' } } // 检查NPC是否还有足够资源 const npcHas = npcPlanet.resources[offer.offeredResources.type] if (npcHas < offer.offeredResources.amount) { return { success: false, reason: 'errors.npcInsufficientResources' } } // 执行交易 // 从玩家扣除 playerPlanet.resources[offer.requestedResources.type] -= offer.requestedResources.amount // 给玩家资源 playerPlanet.resources[offer.offeredResources.type] += offer.offeredResources.amount // 从NPC扣除 npcPlanet.resources[offer.offeredResources.type] -= offer.offeredResources.amount // 给NPC资源 npcPlanet.resources[offer.requestedResources.type] += offer.requestedResources.amount // 增加好感度 const relation = npc.relations?.[player.id] if (relation) { relation.reputation = Math.min(DIPLOMATIC_CONFIG.MAX_REPUTATION, relation.reputation + tradeConfig.REPUTATION_GAIN) relation.lastUpdated = Date.now() } offer.status = 'accepted' return { success: true } } /** * 检查中立NPC是否应该摇摆态度 */ export const shouldNPCSwingAttitude = ( npc: NPC, player: Player, currentTime: number ): { shouldSwing: boolean; direction?: 'friendly' | 'hostile' } => { const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const swingConfig = NEUTRAL_BEHAVIOR_CONFIG.SWING if (!swingConfig.ENABLED) { return { shouldSwing: false } } // 检查外交关系 const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Neutral) { return { shouldSwing: false } } // 检查摇摆冷却 const lastSwingCheck = (npc as any).lastSwingCheck || 0 if (currentTime - lastSwingCheck < swingConfig.CHECK_INTERVAL * 1000) { return { shouldSwing: false } } // 更新检查时间 ;(npc as any).lastSwingCheck = currentTime // 检查是否达到摇摆阈值 if (relation.reputation >= swingConfig.SWING_THRESHOLD.toFriendly) { // 好感度足够高,可能变友好 const probability = swingConfig.BASE_PROBABILITY * (relation.reputation / 20) if (Math.random() < probability) { return { shouldSwing: true, direction: 'friendly' } } } else if (relation.reputation <= swingConfig.SWING_THRESHOLD.toHostile) { // 好感度足够低,可能变敌对 const probability = swingConfig.BASE_PROBABILITY * (Math.abs(relation.reputation) / 20) if (Math.random() < probability) { return { shouldSwing: true, direction: 'hostile' } } } return { shouldSwing: false } } /** * 执行NPC态度摇摆 */ export const executeNPCSwing = (npc: NPC, player: Player, direction: 'friendly' | 'hostile'): void => { const relation = npc.relations?.[player.id] if (!relation) return if (direction === 'friendly') { relation.status = RelationStatus.Friendly relation.reputation = Math.max(relation.reputation, DIPLOMATIC_CONFIG.FRIENDLY_THRESHOLD + 5) } else { relation.status = RelationStatus.Hostile relation.reputation = Math.min(relation.reputation, DIPLOMATIC_CONFIG.HOSTILE_THRESHOLD - 5) } relation.lastUpdated = Date.now() } /** * 中立NPC观察附近事件并调整好感度 */ export const observeNearbyEvents = (neutralNpc: NPC, eventNpc: NPC, player: Player, eventType: 'attack' | 'help'): void => { const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const observeConfig = NEUTRAL_BEHAVIOR_CONFIG.OBSERVE if (!observeConfig.ENABLED) return // 检查是否是中立NPC const relation = neutralNpc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Neutral) return // 检查是否在同一星系(如果配置要求) if (observeConfig.SAME_GALAXY_ONLY) { const neutralPlanet = neutralNpc.planets[0] const eventPlanet = eventNpc.planets[0] if (!neutralPlanet || !eventPlanet) return if (neutralPlanet.position.galaxy !== eventPlanet.position.galaxy) return } // 根据事件类型调整好感度 if (eventType === 'attack') { relation.reputation = Math.max(DIPLOMATIC_CONFIG.MIN_REPUTATION, relation.reputation + observeConfig.ATTACK_NEARBY_PENALTY) } else if (eventType === 'help') { relation.reputation = Math.min(DIPLOMATIC_CONFIG.MAX_REPUTATION, relation.reputation + observeConfig.HELP_NEARBY_BONUS) } relation.lastUpdated = Date.now() } // ========== 友好NPC增强行为系统 ========== /** * 检查友好NPC是否应该分享情报 */ export const shouldNPCShareIntel = (npc: NPC, player: Player, currentTime: number): boolean => { const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const intelConfig = FRIENDLY_BEHAVIOR_CONFIG.INTEL_SHARING if (!intelConfig.ENABLED) { return false } // 检查外交关系 const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Friendly) { return false } // 检查好感度 if (relation.reputation < intelConfig.MIN_REPUTATION) { return false } // 检查分享冷却 const lastIntelTime = (npc as any).lastIntelShareTime || 0 if (currentTime - lastIntelTime < intelConfig.INTERVAL * 1000) { return false } // 概率判断 return Math.random() < intelConfig.PROBABILITY } /** * 选择情报类型 */ export const selectIntelType = (): IntelType => { const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const types = FRIENDLY_BEHAVIOR_CONFIG.INTEL_SHARING.INTEL_TYPES const rand = Math.random() if (rand < types.enemyFleet) { return 'enemyFleet' } else if (rand < types.enemyFleet + types.enemyResources) { return 'enemyResources' } else { return 'enemyMovement' } } /** * 创建情报报告 */ export const createIntelReport = (friendlyNpc: NPC, allNpcs: NPC[], player: Player): IntelReport | null => { // 找到一个敌对NPC作为情报目标 const hostileNpcs = allNpcs.filter(n => { const relation = n.relations?.[player.id] return relation && relation.status === RelationStatus.Hostile }) if (hostileNpcs.length === 0) return null const targetNpc = hostileNpcs[Math.floor(Math.random() * hostileNpcs.length)] if (!targetNpc) return null const targetPlanet = targetNpc.planets[0] if (!targetPlanet) return null const intelType = selectIntelType() const now = Date.now() const report: IntelReport = { id: `intel-${friendlyNpc.id}-${now}`, fromNpcId: friendlyNpc.id, fromNpcName: friendlyNpc.name, timestamp: now, targetNpcId: targetNpc.id, targetNpcName: targetNpc.name, intelType, data: {}, read: false } // 根据情报类型填充数据 switch (intelType) { case 'enemyFleet': report.data.fleet = { ...targetPlanet.fleet } break case 'enemyResources': report.data.resources = { ...targetPlanet.resources } break case 'enemyMovement': // 检查NPC是否有正在进行的任务 const activeMission = targetNpc.fleetMissions?.find(m => m.status === 'outbound') if (activeMission) { report.data.movement = { targetPosition: activeMission.targetPosition, arrivalTime: activeMission.arrivalTime, missionType: activeMission.missionType } } else { // 没有活动任务,改为分享舰队信息 report.intelType = 'enemyFleet' report.data.fleet = { ...targetPlanet.fleet } } break } // 更新NPC的情报分享时间 ;(friendlyNpc as any).lastIntelShareTime = now return report } /** * 检查友好NPC是否应该邀请联合攻击 */ export const shouldNPCInviteJointAttack = (npc: NPC, player: Player, currentTime: number): boolean => { const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const jointConfig = FRIENDLY_BEHAVIOR_CONFIG.JOINT_ATTACK if (!jointConfig.ENABLED) { return false } // 检查外交关系 const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Friendly) { return false } // 检查好感度 if (relation.reputation < jointConfig.MIN_REPUTATION) { return false } // 检查邀请冷却 const lastInviteTime = (npc as any).lastJointAttackInvite || 0 if (currentTime - lastInviteTime < jointConfig.INTERVAL * 1000) { return false } // 概率判断 return Math.random() < jointConfig.PROBABILITY } /** * 创建联合攻击邀请 */ export const createJointAttackInvite = (friendlyNpc: NPC, allNpcs: NPC[], player: Player): JointAttackInvite | null => { const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const jointConfig = FRIENDLY_BEHAVIOR_CONFIG.JOINT_ATTACK // 找到合适的攻击目标 let targetNpc: NPC | null = null if (jointConfig.PREFER_COMMON_ENEMY) { // 优先选择共同敌人(对友好NPC也敌对的NPC) const commonEnemies = allNpcs.filter(n => { const playerRelation = n.relations?.[player.id] const npcRelation = n.relations?.[friendlyNpc.id] return playerRelation?.status === RelationStatus.Hostile && npcRelation?.status === RelationStatus.Hostile }) if (commonEnemies.length > 0) { targetNpc = commonEnemies[Math.floor(Math.random() * commonEnemies.length)] || null } } // 如果没有共同敌人,选择任意敌对NPC if (!targetNpc) { const hostileNpcs = allNpcs.filter(n => { const relation = n.relations?.[player.id] return relation && relation.status === RelationStatus.Hostile }) if (hostileNpcs.length > 0) { targetNpc = hostileNpcs[Math.floor(Math.random() * hostileNpcs.length)] || null } } if (!targetNpc) return null const targetPlanet = targetNpc.planets[0] if (!targetPlanet) return null // 计算NPC承诺的舰队 const npcPlanet = friendlyNpc.planets[0] if (!npcPlanet) return null const fleetRatio = jointConfig.NPC_FLEET_RATIO.min + Math.random() * (jointConfig.NPC_FLEET_RATIO.max - jointConfig.NPC_FLEET_RATIO.min) const combatShips = [ ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship, ShipType.Bomber, ShipType.Destroyer ] const committedFleet: Partial = {} let hasShips = false for (const shipType of combatShips) { const available = npcPlanet.fleet[shipType] || 0 if (available > 0) { const commitCount = Math.floor(available * fleetRatio) if (commitCount > 0) { committedFleet[shipType] = commitCount hasShips = true } } } if (!hasShips) return null const now = Date.now() const invite: JointAttackInvite = { id: `joint-attack-${friendlyNpc.id}-${now}`, fromNpcId: friendlyNpc.id, fromNpcName: friendlyNpc.name, timestamp: now, targetNpcId: targetNpc.id, targetNpcName: targetNpc.name, targetPlanetId: targetPlanet.id, targetPosition: targetPlanet.position, npcFleetCommitment: committedFleet, expectedLootRatio: jointConfig.PLAYER_LOOT_RATIO, expiresAt: now + 2 * 60 * 60 * 1000, // 2小时后过期 status: 'pending' } // 更新NPC的邀请时间 ;(friendlyNpc as any).lastJointAttackInvite = now return invite } /** * 检查友好NPC是否应该提供资源援助 */ export const shouldNPCProvideAid = (npc: NPC, player: Player, currentTime: number): boolean => { const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const aidConfig = FRIENDLY_BEHAVIOR_CONFIG.RESOURCE_AID if (!aidConfig.ENABLED) { return false } // 检查外交关系 const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Friendly) { return false } // 检查好感度 if (relation.reputation < aidConfig.MIN_REPUTATION) { return false } // 检查援助冷却 const lastAidTime = (npc as any).lastAidTime || 0 if (currentTime - lastAidTime < aidConfig.CHECK_INTERVAL * 1000) { return false } // 检查玩家是否资源紧张 const playerProduction = calculatePlayerTotalProduction(player) const lowResourceThreshold = { metal: playerProduction.metal * aidConfig.TRIGGER_LOW_RESOURCES_HOURS, crystal: playerProduction.crystal * aidConfig.TRIGGER_LOW_RESOURCES_HOURS, deuterium: playerProduction.deuterium * aidConfig.TRIGGER_LOW_RESOURCES_HOURS } // 检查玩家总资源 let totalPlayerResources = { metal: 0, crystal: 0, deuterium: 0 } for (const planet of player.planets) { totalPlayerResources.metal += planet.resources.metal totalPlayerResources.crystal += planet.resources.crystal totalPlayerResources.deuterium += planet.resources.deuterium } // 如果玩家资源充足,不援助 if ( totalPlayerResources.metal > lowResourceThreshold.metal && totalPlayerResources.crystal > lowResourceThreshold.crystal && totalPlayerResources.deuterium > lowResourceThreshold.deuterium ) { return false } // 概率判断 return Math.random() < aidConfig.PROBABILITY } /** * 执行资源援助 */ export const executeResourceAid = (npc: NPC, player: Player): Resources | null => { const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG const aidConfig = FRIENDLY_BEHAVIOR_CONFIG.RESOURCE_AID // 获取NPC主星球 const npcPlanet = npc.planets[0] if (!npcPlanet) return null // 计算援助量 const playerProduction = calculatePlayerTotalProduction(player) const aidAmount = { metal: Math.floor(playerProduction.metal * aidConfig.AID_AMOUNT_HOURS), crystal: Math.floor(playerProduction.crystal * aidConfig.AID_AMOUNT_HOURS), deuterium: Math.floor(playerProduction.deuterium * aidConfig.AID_AMOUNT_HOURS), darkMatter: 0, energy: 0 } // 限制为NPC实际拥有的资源 aidAmount.metal = Math.min(aidAmount.metal, Math.floor(npcPlanet.resources.metal * 0.5)) aidAmount.crystal = Math.min(aidAmount.crystal, Math.floor(npcPlanet.resources.crystal * 0.5)) aidAmount.deuterium = Math.min(aidAmount.deuterium, Math.floor(npcPlanet.resources.deuterium * 0.5)) // 如果援助量太少,不援助 if (aidAmount.metal <= 0 && aidAmount.crystal <= 0 && aidAmount.deuterium <= 0) { return null } // 从NPC扣除 npcPlanet.resources.metal -= aidAmount.metal npcPlanet.resources.crystal -= aidAmount.crystal npcPlanet.resources.deuterium -= aidAmount.deuterium // 给玩家主星球 const playerMainPlanet = player.planets[0] if (playerMainPlanet) { playerMainPlanet.resources.metal += aidAmount.metal playerMainPlanet.resources.crystal += aidAmount.crystal playerMainPlanet.resources.deuterium += aidAmount.deuterium } // 更新援助时间 ;(npc as any).lastAidTime = Date.now() return aidAmount } /** * 更新中立NPC行为 */ export const updateNeutralNPCBehavior = ( npc: NPC, _allNpcs: NPC[], player: Player, currentTime: number ): { tradeOffer?: TradeOffer swingDirection?: 'friendly' | 'hostile' } => { const result: { tradeOffer?: TradeOffer swingDirection?: 'friendly' | 'hostile' } = {} // 检查是否是中立NPC const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Neutral) { return result } // 1. 检查贸易提议 if (shouldNPCOfferTrade(npc, player, currentTime)) { const offer = createTradeOffer(npc, player) if (offer) { result.tradeOffer = offer } } // 2. 检查态度摇摆 const swingResult = shouldNPCSwingAttitude(npc, player, currentTime) if (swingResult.shouldSwing && swingResult.direction) { executeNPCSwing(npc, player, swingResult.direction) result.swingDirection = swingResult.direction } return result } /** * 更新友好NPC行为 */ export const updateFriendlyNPCBehavior = ( npc: NPC, allNpcs: NPC[], player: Player, currentTime: number ): { intelReport?: IntelReport jointAttackInvite?: JointAttackInvite aidProvided?: Resources } => { const result: { intelReport?: IntelReport jointAttackInvite?: JointAttackInvite aidProvided?: Resources } = {} // 检查是否是友好NPC const relation = npc.relations?.[player.id] if (!relation || relation.status !== RelationStatus.Friendly) { return result } // 1. 检查情报分享 if (shouldNPCShareIntel(npc, player, currentTime)) { const report = createIntelReport(npc, allNpcs, player) if (report) { result.intelReport = report } } // 2. 检查联合攻击邀请 if (shouldNPCInviteJointAttack(npc, player, currentTime)) { const invite = createJointAttackInvite(npc, allNpcs, player) if (invite) { result.jointAttackInvite = invite } } // 3. 检查资源援助 if (shouldNPCProvideAid(npc, player, currentTime)) { const aid = executeResourceAid(npc, player) if (aid) { result.aidProvided = aid } } return result } /** * 综合NPC行为更新函数 * 根据NPC与玩家的关系状态调用相应的行为更新 */ export const updateAllNPCBehaviors = ( npcs: NPC[], player: Player, allPlanets: Planet[], debrisFields: Record, currentTime: number, activeSiege?: SiegeCoordination ): { hostileResults: Map> neutralResults: Map> friendlyResults: Map> newSiegeCoordination?: SiegeCoordination } => { const hostileResults = new Map>() const neutralResults = new Map>() const friendlyResults = new Map>() let newSiegeCoordination: SiegeCoordination | undefined for (const npc of npcs) { const relation = npc.relations?.[player.id] if (relation?.status === RelationStatus.Hostile) { // 敌对NPC const result = updateHostileNPCBehavior(npc, npcs, player, allPlanets, debrisFields, currentTime, activeSiege) hostileResults.set(npc.id, result) if (result.newSiegeCoordination) { newSiegeCoordination = result.newSiegeCoordination } } else if (relation?.status === RelationStatus.Neutral) { // 中立NPC const result = updateNeutralNPCBehavior(npc, npcs, player, currentTime) neutralResults.set(npc.id, result) } else if (relation?.status === RelationStatus.Friendly) { // 友好NPC const result = updateFriendlyNPCBehavior(npc, npcs, player, currentTime) friendlyResults.set(npc.id, result) } } return { hostileResults, neutralResults, friendlyResults, newSiegeCoordination } } // ========== NPC联盟系统 ========== /** * 获取NPC之间的AI类型兼容性分数 */ export const getAITypeCompatibility = (type1?: NPCAIType, type2?: NPCAIType): number => { const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG const compatibility = NPC_ALLIANCE_CONFIG.INTER_NPC_RELATIONS.TYPE_COMPATIBILITY // 默认类型为平衡型 const t1 = type1 || NPCAIType.Balanced const t2 = type2 || NPCAIType.Balanced // 商人型与任何类型都友好 if (t1 === NPCAIType.Trader || t2 === NPCAIType.Trader) { return compatibility.trader_any } // 侵略型与防守型互为敌人 if ((t1 === NPCAIType.Aggressive && t2 === NPCAIType.Defensive) || (t1 === NPCAIType.Defensive && t2 === NPCAIType.Aggressive)) { return compatibility.aggressive_defensive } // 扩张型互相竞争 if (t1 === NPCAIType.Expansionist && t2 === NPCAIType.Expansionist) { return compatibility.expansionist_expansionist } // 平衡型保持中立 if (t1 === NPCAIType.Balanced || t2 === NPCAIType.Balanced) { return compatibility.balanced_any } // 默认中立 return 0 } /** * 初始化NPC之间的关系 */ export const initializeInterNPCRelations = (npcs: NPC[]): void => { const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG if (!NPC_ALLIANCE_CONFIG.ENABLED) return const config = NPC_ALLIANCE_CONFIG.INTER_NPC_RELATIONS for (const npc of npcs) { if (!npc.allies) npc.allies = [] if (!npc.enemies) npc.enemies = [] const npcPlanet = npc.planets[0] if (!npcPlanet) continue for (const otherNpc of npcs) { if (npc.id === otherNpc.id) continue const otherPlanet = otherNpc.planets[0] if (!otherPlanet) continue // 计算关系分数 let relationScore = 0 // 1. AI类型兼容性 const typeCompatibility = getAITypeCompatibility(npc.aiType, otherNpc.aiType) relationScore += typeCompatibility * 50 // 将-1到1映射到-50到50 // 2. 同一星系加成 if (npcPlanet.position.galaxy === otherPlanet.position.galaxy) { relationScore += config.SAME_GALAXY_ALLY_BONUS * 100 // 30点加成 } // 3. 随机因素(如果是mixed模式) if (config.INIT_MODE === 'mixed' || config.INIT_MODE === 'random') { relationScore += (Math.random() - 0.5) * 40 // -20到20的随机值 } // 根据分数决定关系 if (relationScore > 30) { // 盟友 if (!npc.allies.includes(otherNpc.id)) { npc.allies.push(otherNpc.id) } } else if (relationScore < -30) { // 敌人 if (!npc.enemies.includes(otherNpc.id)) { npc.enemies.push(otherNpc.id) } } // 否则保持中立 } } } /** * 传播连坐效应 * 当玩家攻击/帮助NPC时,影响其盟友的好感度 */ export const propagateGuiltByAssociation = ( targetNpc: NPC, allNpcs: NPC[], player: Player, eventType: 'attack' | 'help', depth: number = 0 ): void => { const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG const guiltConfig = NPC_ALLIANCE_CONFIG.GUILT_BY_ASSOCIATION if (!guiltConfig.ENABLED) return if (depth >= guiltConfig.MAX_PROPAGATION_DEPTH) return // 获取目标NPC的盟友 const allies = targetNpc.allies || [] for (const allyId of allies) { const allyNpc = allNpcs.find(n => n.id === allyId) if (!allyNpc) continue // 获取盟友与玩家的关系 const relation = allyNpc.relations?.[player.id] if (!relation) continue // 计算好感度变化 let reputationChange = eventType === 'attack' ? guiltConfig.ALLY_REPUTATION_PENALTY : guiltConfig.ALLY_REPUTATION_BONUS // 如果启用强度乘数,根据传播深度减弱效果 if (guiltConfig.STRENGTH_MULTIPLIER) { reputationChange = Math.floor(reputationChange * Math.pow(0.5, depth)) } // 应用好感度变化 relation.reputation = Math.max( DIPLOMATIC_CONFIG.MIN_REPUTATION, Math.min(DIPLOMATIC_CONFIG.MAX_REPUTATION, relation.reputation + reputationChange) ) relation.lastUpdated = Date.now() // 更新关系状态 if (relation.reputation < DIPLOMATIC_CONFIG.HOSTILE_THRESHOLD) { relation.status = RelationStatus.Hostile } else if (relation.reputation > DIPLOMATIC_CONFIG.FRIENDLY_THRESHOLD) { relation.status = RelationStatus.Friendly } else { relation.status = RelationStatus.Neutral } // 递归传播到盟友的盟友 propagateGuiltByAssociation(allyNpc, allNpcs, player, eventType, depth + 1) } } /** * NPC盟友互助:当NPC被攻击时,盟友可能响应 */ export const checkAllyMutualDefense = ( attackedNpc: NPC, attackerPlayerId: string, allNpcs: NPC[], allPlanets: Planet[], currentTime: number ): FleetMission[] => { const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG const defenseConfig = NPC_ALLIANCE_CONFIG.MUTUAL_DEFENSE if (!defenseConfig.ENABLED) return [] const defenseMissions: FleetMission[] = [] const allies = attackedNpc.allies || [] // 找到攻击者的星球 const attackerPlanet = allPlanets.find(p => p.ownerId === attackerPlayerId) if (!attackerPlanet) return [] for (const allyId of allies) { const allyNpc = allNpcs.find(n => n.id === allyId) if (!allyNpc) continue // 检查响应冷却 const lastDefenseTime = (allyNpc as any).lastMutualDefenseTime || 0 if (currentTime - lastDefenseTime < defenseConfig.COOLDOWN * 1000) { continue } // 概率检查 if (Math.random() > defenseConfig.RESPONSE_PROBABILITY) { continue } // 选择盟友最佳星球 const allyPlanet = selectBestNPCPlanet(allyNpc, attackerPlanet.position) if (!allyPlanet) continue // 计算响应舰队 const fleetRatio = defenseConfig.FLEET_RATIO.min + Math.random() * (defenseConfig.FLEET_RATIO.max - defenseConfig.FLEET_RATIO.min) const combatShips = [ ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship, ShipType.Bomber, ShipType.Destroyer ] const defenseFleet: Partial = {} let hasShips = false for (const shipType of combatShips) { const available = allyPlanet.fleet[shipType] || 0 if (available > 0) { const sendCount = Math.max(1, Math.floor(available * fleetRatio)) defenseFleet[shipType] = sendCount hasShips = true } } if (!hasShips) continue // 从盟友星球扣除舰队 for (const [shipType, count] of Object.entries(defenseFleet)) { allyPlanet.fleet[shipType as ShipType] = (allyPlanet.fleet[shipType as ShipType] || 0) - (count as number) } // 计算飞行时间 const distance = fleetLogic.calculateDistance(allyPlanet.position, attackerPlanet.position) const defenseMinSpeed = getFleetMinSpeed(defenseFleet) const flightTime = fleetLogic.calculateFlightTime(distance, defenseMinSpeed) const now = Date.now() const mission: FleetMission = { id: `npc-mutual-defense-${allyNpc.id}-${now}`, playerId: allyNpc.id, npcId: allyNpc.id, isHostile: true, originPlanetId: allyPlanet.id, targetPosition: attackerPlanet.position, targetPlanetId: attackerPlanet.id, missionType: MissionType.Attack, fleet: defenseFleet, cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }, departureTime: now, arrivalTime: now + flightTime * 1000, status: 'outbound' } // 更新响应时间 ;(allyNpc as any).lastMutualDefenseTime = now // 添加到盟友NPC任务列表 if (!allyNpc.fleetMissions) { allyNpc.fleetMissions = [] } allyNpc.fleetMissions.push(mission) defenseMissions.push(mission) } return defenseMissions } /** * NPC之间的资源共享 */ export const executeNPCResourceSharing = (npcs: NPC[], currentTime: number): void => { const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG const sharingConfig = NPC_ALLIANCE_CONFIG.RESOURCE_SHARING if (!sharingConfig.ENABLED) return for (const npc of npcs) { // 检查共享冷却 const lastShareTime = (npc as any).lastResourceShareTime || 0 if (currentTime - lastShareTime < sharingConfig.INTERVAL * 1000) { continue } const npcPlanet = npc.planets[0] if (!npcPlanet) continue const allies = npc.allies || [] if (allies.length === 0) continue // 计算可共享资源 const shareAmount = { metal: Math.floor(npcPlanet.resources.metal * sharingConfig.SHARE_RATIO), crystal: Math.floor(npcPlanet.resources.crystal * sharingConfig.SHARE_RATIO), deuterium: Math.floor(npcPlanet.resources.deuterium * sharingConfig.SHARE_RATIO) } // 平均分配给所有盟友 const perAlly = { metal: Math.floor(shareAmount.metal / allies.length), crystal: Math.floor(shareAmount.crystal / allies.length), deuterium: Math.floor(shareAmount.deuterium / allies.length) } if (perAlly.metal <= 0 && perAlly.crystal <= 0 && perAlly.deuterium <= 0) { continue } // 从NPC扣除 npcPlanet.resources.metal -= perAlly.metal * allies.length npcPlanet.resources.crystal -= perAlly.crystal * allies.length npcPlanet.resources.deuterium -= perAlly.deuterium * allies.length // 分配给盟友 for (const allyId of allies) { const allyNpc = npcs.find(n => n.id === allyId) if (!allyNpc) continue const allyPlanet = allyNpc.planets[0] if (!allyPlanet) continue allyPlanet.resources.metal += perAlly.metal allyPlanet.resources.crystal += perAlly.crystal allyPlanet.resources.deuterium += perAlly.deuterium } // 更新共享时间 ;(npc as any).lastResourceShareTime = currentTime } } /** * 获取NPC的所有盟友信息 */ export const getNPCAllies = (npc: NPC, allNpcs: NPC[]): NPC[] => { const allies: NPC[] = [] const allyIds = npc.allies || [] for (const allyId of allyIds) { const allyNpc = allNpcs.find(n => n.id === allyId) if (allyNpc) { allies.push(allyNpc) } } return allies } /** * 获取NPC的所有敌人信息 */ export const getNPCEnemies = (npc: NPC, allNpcs: NPC[]): NPC[] => { const enemies: NPC[] = [] const enemyIds = npc.enemies || [] for (const enemyId of enemyIds) { const enemyNpc = allNpcs.find(n => n.id === enemyId) if (enemyNpc) { enemies.push(enemyNpc) } } return enemies } /** * 增强版NPC被攻击处理 * 包含连坐效应和盟友互助 */ export const handleNPCAttackedWithAlliance = ( npc: NPC, attackerId: string, attackerPlanetId: string | undefined, allNpcs: NPC[], player: Player, allPlanets: Planet[], currentTime: number ): FleetMission[] => { // 基础被攻击处理 handleNPCAttacked(npc, attackerId, attackerPlanetId) // 传播连坐效应 propagateGuiltByAssociation(npc, allNpcs, player, 'attack') // 检查盟友互助 const defenseMissions = checkAllyMutualDefense(npc, attackerId, allNpcs, allPlanets, currentTime) return defenseMissions } /** * 更新NPC联盟系统 * 在游戏循环中调用 */ export const updateNPCAllianceSystem = (npcs: NPC[], currentTime: number): void => { const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG if (!NPC_ALLIANCE_CONFIG.ENABLED) return // 执行NPC之间的资源共享 executeNPCResourceSharing(npcs, currentTime) }