mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 16:05:12 +08:00
docs: 新增西班牙语和日语README并优化多语言文档
新增README-ES.md(西班牙语)和README-JA.md(日语)文档,完善多语言README互链。优化各语言README徽章、技术栈、外链格式及语言切换区,提升文档一致性与可读性。
This commit is contained in:
@@ -1,9 +1,102 @@
|
||||
import type { NPC, Planet, Player, FleetMission, SpyReport, SpiedNotification, IncomingFleetAlert, Fleet, DebrisField } from '@/types/game'
|
||||
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<Fleet>): 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 类型调整其行为参数
|
||||
@@ -342,27 +435,133 @@ export const shouldNPCGiftPlayer = (npc: NPC, player: Player, currentTime: numbe
|
||||
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
|
||||
|
||||
// 随机生成赠送资源量
|
||||
const giftResources = {
|
||||
metal:
|
||||
Math.floor(Math.random() * (NPC_GIFT_CONFIG.GIFT_AMOUNT.METAL.max - NPC_GIFT_CONFIG.GIFT_AMOUNT.METAL.min + 1)) +
|
||||
NPC_GIFT_CONFIG.GIFT_AMOUNT.METAL.min,
|
||||
crystal:
|
||||
Math.floor(Math.random() * (NPC_GIFT_CONFIG.GIFT_AMOUNT.CRYSTAL.max - NPC_GIFT_CONFIG.GIFT_AMOUNT.CRYSTAL.min + 1)) +
|
||||
NPC_GIFT_CONFIG.GIFT_AMOUNT.CRYSTAL.min,
|
||||
deuterium:
|
||||
Math.floor(Math.random() * (NPC_GIFT_CONFIG.GIFT_AMOUNT.DEUTERIUM.max - NPC_GIFT_CONFIG.GIFT_AMOUNT.DEUTERIUM.min + 1)) +
|
||||
NPC_GIFT_CONFIG.GIFT_AMOUNT.DEUTERIUM.min,
|
||||
// 获取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)
|
||||
|
||||
@@ -370,6 +569,161 @@ export const giftResourcesToPlayer = (npc: NPC, player: Player): void => {
|
||||
;(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<Fleet> = {}
|
||||
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的最佳攻击来源星球
|
||||
*/
|
||||
@@ -795,7 +1149,10 @@ export const updateNPCBehavior = (
|
||||
giftResourcesToPlayer(npc, player)
|
||||
}
|
||||
|
||||
// 6. 更新即将到来的舰队警告(删除过期的)
|
||||
// 6. 检查是否应该协防玩家(仅友好NPC,玩家被攻击时)
|
||||
checkAndExecuteAllyDefense(npc, player, currentTime)
|
||||
|
||||
// 7. 更新即将到来的舰队警告(删除过期的)
|
||||
updateIncomingFleetAlerts(player, currentTime)
|
||||
}
|
||||
|
||||
@@ -898,7 +1255,10 @@ export const updateNPCBehaviorWithLimit = (
|
||||
giftResourcesToPlayer(npc, player)
|
||||
}
|
||||
|
||||
// 6. 更新即将到来的舰队警告(删除过期的)
|
||||
// 6. 检查是否应该协防玩家(仅友好NPC,玩家被攻击时,不受并发限制)
|
||||
checkAndExecuteAllyDefense(npc, player, currentTime)
|
||||
|
||||
// 7. 更新即将到来的舰队警告(删除过期的)
|
||||
updateIncomingFleetAlerts(player, currentTime)
|
||||
|
||||
return { spyCreated, attackCreated }
|
||||
@@ -1552,3 +1912,1875 @@ export const diagnoseNPCBehavior = (npcs: NPC[], player: Player, currentTime: nu
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 敌对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<Fleet> = {}
|
||||
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<Fleet> = {}
|
||||
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<Fleet> = {}
|
||||
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<Fleet> = {}
|
||||
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<string, DebrisField>,
|
||||
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<Fleet> = {}
|
||||
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<string, DebrisField>,
|
||||
currentTime: number,
|
||||
activeSiege?: SiegeCoordination
|
||||
): {
|
||||
hostileResults: Map<string, ReturnType<typeof updateHostileNPCBehavior>>
|
||||
neutralResults: Map<string, ReturnType<typeof updateNeutralNPCBehavior>>
|
||||
friendlyResults: Map<string, ReturnType<typeof updateFriendlyNPCBehavior>>
|
||||
newSiegeCoordination?: SiegeCoordination
|
||||
} => {
|
||||
const hostileResults = new Map<string, ReturnType<typeof updateHostileNPCBehavior>>()
|
||||
const neutralResults = new Map<string, ReturnType<typeof updateNeutralNPCBehavior>>()
|
||||
const friendlyResults = new Map<string, ReturnType<typeof updateFriendlyNPCBehavior>>()
|
||||
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<Fleet> = {}
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user