mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 07:55:11 +08:00
feat: 新增NPC与外交逻辑,优化UI组件结构
重构并精简了部分UI组件,移除冗余弹窗与详情组件,新增NPC相关逻辑(npcBehaviorLogic、npcGrowthLogic、npcStore等)及外交逻辑(diplomaticLogic、DiplomacyView)。完善分页、标签、复选框等通用UI组件。优化战报弹窗,调整README下载链接为相对路径,修复部分国际化内容。
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import type { FleetMission, Planet, Resources, Fleet, BattleResult, SpyReport, Player, Officer, DebrisField } from '@/types/game'
|
||||
import { ShipType, DefenseType, MissionType, BuildingType, OfficerType } from '@/types/game'
|
||||
import type { FleetMission, Planet, Resources, Fleet, BattleResult, SpyReport, Player, Officer, DebrisField, NPC } from '@/types/game'
|
||||
import { ShipType, DefenseType, MissionType, BuildingType, OfficerType, TechnologyType } from '@/types/game'
|
||||
import { FLEET_STORAGE_CONFIG } from '@/config/gameConfig'
|
||||
import * as battleLogic from './battleLogic'
|
||||
import * as moonLogic from './moonLogic'
|
||||
import * as moonValidation from './moonValidation'
|
||||
import * as diplomaticLogic from './diplomaticLogic'
|
||||
|
||||
/**
|
||||
* 计算两个星球之间的距离
|
||||
@@ -70,15 +71,44 @@ export const createFleetMission = (
|
||||
/**
|
||||
* 处理运输任务到达
|
||||
*/
|
||||
export const processTransportArrival = (mission: FleetMission, targetPlanet: Planet | undefined): void => {
|
||||
export const processTransportArrival = (
|
||||
mission: FleetMission,
|
||||
targetPlanet: Planet | undefined,
|
||||
player?: Player,
|
||||
allNpcs?: NPC[]
|
||||
): { success: boolean; reputationGain?: number } => {
|
||||
// 检查是否是赠送任务
|
||||
if (mission.isGift && mission.giftTargetNpcId && player && allNpcs) {
|
||||
const targetNpc = allNpcs.find(n => n.id === mission.giftTargetNpcId)
|
||||
if (targetNpc) {
|
||||
const giftResult = diplomaticLogic.handleGiftArrival(mission, player, targetNpc)
|
||||
mission.status = 'returning'
|
||||
|
||||
// 如果礼物被拒绝,资源返还给玩家
|
||||
if (!giftResult.accepted) {
|
||||
// 资源保留在cargo中,返回时会退还给玩家
|
||||
return { success: false, reputationGain: undefined }
|
||||
}
|
||||
|
||||
// 礼物被接受,清空货物
|
||||
mission.cargo = { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }
|
||||
return { success: true, reputationGain: giftResult.reputationGain }
|
||||
}
|
||||
}
|
||||
|
||||
// 正常运输任务
|
||||
if (targetPlanet) {
|
||||
targetPlanet.resources.metal += mission.cargo.metal
|
||||
targetPlanet.resources.crystal += mission.cargo.crystal
|
||||
targetPlanet.resources.deuterium += mission.cargo.deuterium
|
||||
targetPlanet.resources.darkMatter += mission.cargo.darkMatter
|
||||
mission.status = 'returning'
|
||||
mission.cargo = { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }
|
||||
return { success: true }
|
||||
}
|
||||
mission.status = 'returning'
|
||||
mission.cargo = { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +133,9 @@ export const processAttackArrival = async (
|
||||
targetPlanet.defense,
|
||||
targetPlanet.resources,
|
||||
attacker.officers,
|
||||
defender?.officers || ({} as Record<OfficerType, Officer>)
|
||||
defender?.officers || ({} as Record<OfficerType, Officer>),
|
||||
attacker.technologies,
|
||||
defender?.technologies || ({} as Record<TechnologyType, number>)
|
||||
)
|
||||
|
||||
// 更新战斗报告ID
|
||||
@@ -177,13 +209,134 @@ export const processAttackArrival = async (
|
||||
return { battleResult, moon, debrisField }
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理NPC攻击玩家星球
|
||||
* 专门用于NPC作为攻击方的情况
|
||||
*/
|
||||
export const processNPCAttackArrival = async (
|
||||
npc: NPC,
|
||||
mission: FleetMission,
|
||||
targetPlanet: Planet,
|
||||
defender: Player,
|
||||
allPlanets: Planet[]
|
||||
): Promise<{ battleResult: BattleResult; moon: Planet | null; debrisField: DebrisField | null } | null> => {
|
||||
// 执行战斗(使用 Worker 进行异步计算)
|
||||
const battleResult = await battleLogic.simulateBattle(
|
||||
mission.fleet, // NPC舰队
|
||||
targetPlanet.fleet, // 玩家舰队
|
||||
targetPlanet.defense, // 玩家防御
|
||||
targetPlanet.resources, // 玩家资源
|
||||
{} as Record<OfficerType, Officer>, // NPC没有军官系统
|
||||
defender.officers || ({} as Record<OfficerType, Officer>), // 玩家军官
|
||||
npc.technologies, // NPC科技等级
|
||||
defender.technologies // 玩家科技等级
|
||||
)
|
||||
|
||||
// 更新战斗报告ID和参与者信息
|
||||
battleResult.id = `battle_${Date.now()}`
|
||||
battleResult.attackerId = npc.id
|
||||
battleResult.defenderId = defender.id
|
||||
battleResult.attackerPlanetId = mission.originPlanetId
|
||||
battleResult.defenderPlanetId = targetPlanet.id
|
||||
battleResult.timestamp = Date.now()
|
||||
|
||||
// 如果NPC获胜,掠夺的资源给NPC的起始星球
|
||||
if (battleResult.winner === 'attacker' && battleResult.plunder) {
|
||||
const npcOriginPlanet = npc.planets.find(p => p.id === mission.originPlanetId)
|
||||
if (npcOriginPlanet) {
|
||||
// NPC获得掠夺的资源(当舰队返回时)
|
||||
mission.cargo = battleResult.plunder
|
||||
}
|
||||
}
|
||||
|
||||
// 更新NPC舰队 - 计算幸存舰船
|
||||
const survivingFleet: Partial<Fleet> = {}
|
||||
Object.entries(mission.fleet).forEach(([shipType, initialCount]) => {
|
||||
const lost = battleResult.attackerLosses[shipType as ShipType] || 0
|
||||
const surviving = initialCount - lost
|
||||
if (surviving > 0) {
|
||||
survivingFleet[shipType as ShipType] = surviving
|
||||
}
|
||||
})
|
||||
mission.fleet = survivingFleet
|
||||
|
||||
// 更新玩家星球舰队和防御
|
||||
Object.entries(battleResult.defenderLosses.fleet).forEach(([shipType, lost]) => {
|
||||
targetPlanet.fleet[shipType as ShipType] = Math.max(0, (targetPlanet.fleet[shipType as ShipType] || 0) - lost)
|
||||
})
|
||||
|
||||
Object.entries(battleResult.defenderLosses.defense).forEach(([defenseType, lost]) => {
|
||||
targetPlanet.defense[defenseType as DefenseType] = Math.max(0, (targetPlanet.defense[defenseType as DefenseType] || 0) - lost)
|
||||
})
|
||||
|
||||
// 防御设施修复(70%概率)
|
||||
const defenseBeforeBattle: Partial<Record<DefenseType, number>> = { ...targetPlanet.defense }
|
||||
Object.entries(battleResult.defenderLosses.defense).forEach(([defenseType, lost]) => {
|
||||
defenseBeforeBattle[defenseType as DefenseType] = (defenseBeforeBattle[defenseType as DefenseType] || 0) + lost
|
||||
})
|
||||
targetPlanet.defense = battleLogic.repairDefense(defenseBeforeBattle, targetPlanet.defense) as Record<DefenseType, number>
|
||||
|
||||
// 扣除玩家星球被掠夺的资源
|
||||
if (battleResult.plunder) {
|
||||
targetPlanet.resources.metal = Math.max(0, targetPlanet.resources.metal - battleResult.plunder.metal)
|
||||
targetPlanet.resources.crystal = Math.max(0, targetPlanet.resources.crystal - battleResult.plunder.crystal)
|
||||
targetPlanet.resources.deuterium = Math.max(0, targetPlanet.resources.deuterium - battleResult.plunder.deuterium)
|
||||
}
|
||||
|
||||
mission.status = 'returning'
|
||||
|
||||
// 尝试生成月球(如果该位置还没有月球)
|
||||
let moon: Planet | null = null
|
||||
const moonCheck = moonValidation.canCreateMoon(allPlanets, targetPlanet.position, battleResult.debrisField)
|
||||
if (moonCheck.canCreate && moonCheck.chance) {
|
||||
if (moonValidation.shouldGenerateMoon(moonCheck.chance)) {
|
||||
moon = moonLogic.tryGenerateMoon(battleResult.debrisField, targetPlanet.position, targetPlanet.id, targetPlanet.ownerId || 'unknown')
|
||||
}
|
||||
}
|
||||
|
||||
// 创建残骸场(如果有残骸)
|
||||
let debrisField: DebrisField | null = null
|
||||
const totalDebris = battleResult.debrisField.metal + battleResult.debrisField.crystal
|
||||
if (totalDebris > 0) {
|
||||
debrisField = {
|
||||
id: `debris_${targetPlanet.position.galaxy}_${targetPlanet.position.system}_${targetPlanet.position.position}`,
|
||||
position: targetPlanet.position,
|
||||
resources: {
|
||||
metal: battleResult.debrisField.metal,
|
||||
crystal: battleResult.debrisField.crystal
|
||||
},
|
||||
createdAt: Date.now()
|
||||
}
|
||||
}
|
||||
return { battleResult, moon, debrisField }
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算玩家最大星球数量
|
||||
* 基于天体物理学技术等级
|
||||
*/
|
||||
export const calculateMaxPlanets = (astrophysicsLevel: number): number => {
|
||||
// 基础1个星球 + 每2级天体物理学增加1个殖民地槽位
|
||||
return 1 + Math.floor(astrophysicsLevel / 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查玩家是否可以殖民新星球
|
||||
*/
|
||||
export const canColonize = (player: Player): boolean => {
|
||||
const astrophysicsLevel = player.technologies[TechnologyType.Astrophysics] || 0
|
||||
const maxPlanets = calculateMaxPlanets(astrophysicsLevel)
|
||||
const currentPlanets = player.planets.length
|
||||
return currentPlanets < maxPlanets
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理殖民任务到达
|
||||
*/
|
||||
export const processColonizeArrival = (
|
||||
mission: FleetMission,
|
||||
targetPlanet: Planet | undefined,
|
||||
playerId: string,
|
||||
player: Player,
|
||||
colonyNameTemplate: string = 'Colony'
|
||||
): Planet | null => {
|
||||
if (targetPlanet) {
|
||||
@@ -192,11 +345,18 @@ export const processColonizeArrival = (
|
||||
return null
|
||||
}
|
||||
|
||||
// 检查殖民地槽位限制
|
||||
if (!canColonize(player)) {
|
||||
// 超出殖民地数量限制,殖民船返回
|
||||
mission.status = 'returning'
|
||||
return null
|
||||
}
|
||||
|
||||
// 创建新殖民地
|
||||
const newPlanet: Planet = {
|
||||
id: `planet_${Date.now()}`,
|
||||
name: `${colonyNameTemplate} ${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}`,
|
||||
ownerId: playerId,
|
||||
ownerId: player.id,
|
||||
position: mission.targetPosition,
|
||||
resources: { metal: 500, crystal: 500, deuterium: 0, darkMatter: 0, energy: 0 },
|
||||
buildings: {} as Record<BuildingType, number>,
|
||||
@@ -205,11 +365,15 @@ export const processColonizeArrival = (
|
||||
[ShipType.HeavyFighter]: 0,
|
||||
[ShipType.Cruiser]: 0,
|
||||
[ShipType.Battleship]: 0,
|
||||
[ShipType.Battlecruiser]: 0,
|
||||
[ShipType.Bomber]: 0,
|
||||
[ShipType.Destroyer]: 0,
|
||||
[ShipType.SmallCargo]: 0,
|
||||
[ShipType.LargeCargo]: 0,
|
||||
[ShipType.ColonyShip]: 0,
|
||||
[ShipType.Recycler]: 0,
|
||||
[ShipType.EspionageProbe]: 0,
|
||||
[ShipType.SolarSatellite]: 0,
|
||||
[ShipType.DarkMatterHarvester]: 0,
|
||||
[ShipType.Deathstar]: 0
|
||||
},
|
||||
@@ -222,6 +386,8 @@ export const processColonizeArrival = (
|
||||
[DefenseType.PlasmaTurret]: 0,
|
||||
[DefenseType.SmallShieldDome]: 0,
|
||||
[DefenseType.LargeShieldDome]: 0,
|
||||
[DefenseType.AntiBallisticMissile]: 0,
|
||||
[DefenseType.InterplanetaryMissile]: 0,
|
||||
[DefenseType.PlanetaryShield]: 0
|
||||
},
|
||||
buildQueue: [],
|
||||
@@ -242,27 +408,104 @@ export const processColonizeArrival = (
|
||||
return newPlanet
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算间谍侦查信息可见度
|
||||
* 根据双方间谍技术等级差异决定显示哪些信息
|
||||
*/
|
||||
export const calculateSpyVisibility = (
|
||||
attackerSpyLevel: number,
|
||||
defenderSpyLevel: number,
|
||||
spyProbeCount: number
|
||||
): {
|
||||
showFleet: boolean
|
||||
showDefense: boolean
|
||||
showBuildings: boolean
|
||||
showTechnologies: boolean
|
||||
} => {
|
||||
// 技术等级差异 (考虑间谍探测器数量加成)
|
||||
const levelDiff = attackerSpyLevel - defenderSpyLevel + Math.floor(spyProbeCount / 5)
|
||||
|
||||
return {
|
||||
showFleet: levelDiff >= -1, // Level 2: Resources + fleet
|
||||
showDefense: levelDiff >= 1, // Level 4: Resources + fleet + defense
|
||||
showBuildings: levelDiff >= 3, // Level 6: Resources + fleet + defense + buildings
|
||||
showTechnologies: levelDiff >= 5 // Level 8: Resources + fleet + defense + buildings + technologies
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算间谍侦查被发现概率
|
||||
*/
|
||||
export const calculateSpyDetectionChance = (attackerSpyLevel: number, defenderSpyLevel: number, spyProbeCount: number): number => {
|
||||
// 基础被发现概率
|
||||
let baseChance = 0.25
|
||||
|
||||
// 技术等级差异影响
|
||||
const levelDiff = defenderSpyLevel - attackerSpyLevel
|
||||
if (levelDiff > 0) {
|
||||
baseChance += levelDiff * 0.1 // 防守方技术高,增加被发现概率
|
||||
} else {
|
||||
baseChance += levelDiff * 0.05 // 攻击方技术高,减少被发现概率
|
||||
}
|
||||
|
||||
// 间谍探测器数量影响
|
||||
baseChance -= spyProbeCount * 0.01 // 每个探测器降低1%被发现概率
|
||||
|
||||
// 限制在 1% - 99% 之间
|
||||
return Math.max(0.01, Math.min(0.99, baseChance))
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理间谍任务到达
|
||||
*/
|
||||
export const processSpyArrival = (mission: FleetMission, targetPlanet: Planet | undefined, playerId: string): SpyReport | null => {
|
||||
export const processSpyArrival = (
|
||||
mission: FleetMission,
|
||||
targetPlanet: Planet | undefined,
|
||||
attacker: Player,
|
||||
defender: Player | null,
|
||||
allNpcs?: NPC[]
|
||||
): SpyReport | null => {
|
||||
if (!targetPlanet) {
|
||||
mission.status = 'returning'
|
||||
return null
|
||||
}
|
||||
|
||||
// 获取间谍技术等级
|
||||
const attackerSpyLevel = attacker.technologies[TechnologyType.EspionageTechnology] || 0
|
||||
const defenderSpyLevel = defender?.technologies[TechnologyType.EspionageTechnology] || 0
|
||||
const spyProbeCount = mission.fleet[ShipType.EspionageProbe] || 0
|
||||
|
||||
// 计算信息可见度
|
||||
const visibility = calculateSpyVisibility(attackerSpyLevel, defenderSpyLevel, spyProbeCount)
|
||||
|
||||
// 计算被发现概率
|
||||
const detectionChance = calculateSpyDetectionChance(attackerSpyLevel, defenderSpyLevel, spyProbeCount)
|
||||
|
||||
// 判断是否被发现
|
||||
const wasDetected = Math.random() < detectionChance
|
||||
|
||||
const spyReport: SpyReport = {
|
||||
id: `spy_${Date.now()}`,
|
||||
timestamp: Date.now(),
|
||||
spyId: playerId,
|
||||
spyId: attacker.id,
|
||||
targetPlanetId: targetPlanet.id,
|
||||
targetPlanetName: targetPlanet.name,
|
||||
targetPosition: { ...targetPlanet.position },
|
||||
targetPlayerId: targetPlanet.ownerId || 'unknown',
|
||||
resources: { ...targetPlanet.resources },
|
||||
fleet: { ...targetPlanet.fleet },
|
||||
defense: { ...targetPlanet.defense },
|
||||
buildings: { ...targetPlanet.buildings },
|
||||
technologies: {},
|
||||
detectionChance: 0.3
|
||||
resources: { ...targetPlanet.resources }, // 资源信息始终可见
|
||||
fleet: visibility.showFleet ? { ...targetPlanet.fleet } : undefined,
|
||||
defense: visibility.showDefense ? { ...targetPlanet.defense } : undefined,
|
||||
buildings: visibility.showBuildings ? { ...targetPlanet.buildings } : undefined,
|
||||
technologies: visibility.showTechnologies && defender ? { ...defender.technologies } : undefined,
|
||||
detectionChance
|
||||
}
|
||||
|
||||
// 如果目标是NPC星球,调用外交逻辑
|
||||
if (allNpcs && targetPlanet.ownerId) {
|
||||
const targetNpc = allNpcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
|
||||
if (targetNpc) {
|
||||
diplomaticLogic.handleSpyReputation(attacker, targetNpc, wasDetected)
|
||||
}
|
||||
}
|
||||
|
||||
mission.status = 'returning'
|
||||
@@ -291,7 +534,9 @@ export const processDeployArrival = (mission: FleetMission, targetPlanet: Planet
|
||||
*/
|
||||
export const processRecycleArrival = (
|
||||
mission: FleetMission,
|
||||
debrisField: DebrisField | undefined
|
||||
debrisField: DebrisField | undefined,
|
||||
player?: Player,
|
||||
allNpcs?: NPC[]
|
||||
): { collectedResources: Pick<Resources, 'metal' | 'crystal'>; remainingDebris: Pick<Resources, 'metal' | 'crystal'> | null } | null => {
|
||||
if (!debrisField) {
|
||||
mission.status = 'returning'
|
||||
@@ -330,6 +575,11 @@ export const processRecycleArrival = (
|
||||
|
||||
mission.status = 'returning'
|
||||
|
||||
// 检查是否在NPC星球位置回收残骸,如果是则降低好感度
|
||||
if (player && allNpcs && collectedAmount > 0) {
|
||||
diplomaticLogic.handleDebrisRecycleReputation(player, debrisField.position, allNpcs)
|
||||
}
|
||||
|
||||
return {
|
||||
collectedResources: {
|
||||
metal: collectedMetal,
|
||||
@@ -348,11 +598,7 @@ export const processRecycleArrival = (
|
||||
/**
|
||||
* 计算行星毁灭概率
|
||||
*/
|
||||
export const calculateDestructionChance = (
|
||||
deathstarCount: number,
|
||||
planetaryShieldCount: number,
|
||||
planetDefensePower: number
|
||||
): number => {
|
||||
export const calculateDestructionChance = (deathstarCount: number, planetaryShieldCount: number, planetDefensePower: number): number => {
|
||||
// 基础摧毁概率:每艘死星 10%
|
||||
let baseChance = deathstarCount * 10
|
||||
|
||||
@@ -372,10 +618,7 @@ export const calculateDestructionChance = (
|
||||
/**
|
||||
* 计算星球总防御力量
|
||||
*/
|
||||
export const calculatePlanetDefensePower = (
|
||||
fleet: Partial<Fleet>,
|
||||
defense: Partial<Record<DefenseType, number>>
|
||||
): number => {
|
||||
export const calculatePlanetDefensePower = (fleet: Partial<Fleet>, defense: Partial<Record<DefenseType, number>>): number => {
|
||||
let totalPower = 0
|
||||
|
||||
// 计算舰队力量
|
||||
@@ -463,7 +706,8 @@ export const updateFleetMissions = async (
|
||||
debrisFields: Map<string, DebrisField>,
|
||||
attacker: Player,
|
||||
defender: Player | null,
|
||||
now: number
|
||||
now: number,
|
||||
allNpcs?: NPC[]
|
||||
): Promise<{
|
||||
completedMissions: string[]
|
||||
battleReports: BattleResult[]
|
||||
@@ -499,7 +743,7 @@ export const updateFleetMissions = async (
|
||||
|
||||
switch (mission.missionType) {
|
||||
case MissionType.Transport:
|
||||
processTransportArrival(mission, targetPlanet)
|
||||
processTransportArrival(mission, targetPlanet, attacker, allNpcs)
|
||||
break
|
||||
|
||||
case MissionType.Attack: {
|
||||
@@ -520,7 +764,7 @@ export const updateFleetMissions = async (
|
||||
}
|
||||
|
||||
case MissionType.Colonize:
|
||||
const newColony = processColonizeArrival(mission, targetPlanet, attacker.id)
|
||||
const newColony = processColonizeArrival(mission, targetPlanet, attacker)
|
||||
if (newColony) {
|
||||
newColonies.push(newColony)
|
||||
planets.set(targetKey, newColony)
|
||||
@@ -528,7 +772,7 @@ export const updateFleetMissions = async (
|
||||
break
|
||||
|
||||
case MissionType.Spy:
|
||||
const spyReport = processSpyArrival(mission, targetPlanet, attacker.id)
|
||||
const spyReport = processSpyArrival(mission, targetPlanet, attacker, defender)
|
||||
if (spyReport) {
|
||||
spyReports.push(spyReport)
|
||||
}
|
||||
@@ -544,7 +788,7 @@ export const updateFleetMissions = async (
|
||||
case MissionType.Recycle:
|
||||
const debrisId = `debris_${mission.targetPosition.galaxy}_${mission.targetPosition.system}_${mission.targetPosition.position}`
|
||||
const debrisField = debrisFields.get(debrisId)
|
||||
const recycleResult = processRecycleArrival(mission, debrisField)
|
||||
const recycleResult = processRecycleArrival(mission, debrisField, attacker, allNpcs)
|
||||
if (recycleResult) {
|
||||
if (recycleResult.remainingDebris) {
|
||||
// 更新残骸场
|
||||
@@ -582,7 +826,17 @@ export const updateFleetMissions = async (
|
||||
}
|
||||
}
|
||||
|
||||
return { completedMissions, battleReports, spyReports, newColonies, newMoons, newDebrisFields, updatedDebrisFields, removedDebrisFieldIds, destroyedPlanetIds }
|
||||
return {
|
||||
completedMissions,
|
||||
battleReports,
|
||||
spyReports,
|
||||
newColonies,
|
||||
newMoons,
|
||||
newDebrisFields,
|
||||
updatedDebrisFields,
|
||||
removedDebrisFieldIds,
|
||||
destroyedPlanetIds
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user