mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 07:55:11 +08:00
- 为战斗报告、星球、月球、任务通知和舰队任务等 ID 添加随机后缀,避免重复 ID - 在 GM 面板中添加预设管理系统,支持保存和应用建筑、研究、舰船和防御的配置 - 在外交界面添加 NPC 排序功能,支持按声望、星球数量、难度和盟友数量排序 - 修复数据迁移中的重复星球 ID 问题,确保月球与母星关联正确 - 优化 GM 面板的资源最大化功能,基于实际存储容量设置资源 - 为所有支持的语言添加相关翻译文本
604 lines
17 KiB
TypeScript
604 lines
17 KiB
TypeScript
/**
|
||
* 战役系统逻辑
|
||
* 处理任务进度、解锁、完成等核心逻辑
|
||
*/
|
||
|
||
import {
|
||
QuestStatus,
|
||
ObjectiveType,
|
||
type Player,
|
||
type PlayerCampaignProgress,
|
||
type QuestObjective,
|
||
type QuestReward,
|
||
type QuestNotification,
|
||
type CampaignQuestConfig,
|
||
type NPC,
|
||
RelationStatus,
|
||
type BuildingType,
|
||
type TechnologyType,
|
||
type ShipType
|
||
} from '@/types/game'
|
||
import { MAIN_CAMPAIGN, getAllQuests, getQuestById, getQuestsByChapter } from '@/config/campaignConfig'
|
||
import * as resourceLogic from './resourceLogic'
|
||
|
||
/**
|
||
* 初始化玩家战役进度
|
||
*/
|
||
export const initializeCampaignProgress = (player: Player): PlayerCampaignProgress => {
|
||
// 如果已有进度,返回现有进度
|
||
if (player.campaignProgress) {
|
||
return player.campaignProgress
|
||
}
|
||
|
||
// 创建初始进度,第一个任务默认解锁
|
||
const firstQuest = getQuestsByChapter(1)[0]
|
||
const progress: PlayerCampaignProgress = {
|
||
campaignId: MAIN_CAMPAIGN.id,
|
||
currentChapter: 1,
|
||
currentQuestId: firstQuest?.id,
|
||
questProgress: {},
|
||
completedQuests: [],
|
||
unlockedQuests: firstQuest ? [firstQuest.id] : [],
|
||
branchChoices: {}
|
||
}
|
||
|
||
// 初始化第一个任务为可接取状态
|
||
if (firstQuest) {
|
||
progress.questProgress[firstQuest.id] = {
|
||
questId: firstQuest.id,
|
||
status: QuestStatus.Available,
|
||
objectives: {}
|
||
}
|
||
// 初始化目标进度
|
||
const questProgress = progress.questProgress[firstQuest.id]
|
||
if (questProgress) {
|
||
firstQuest.objectives.forEach(obj => {
|
||
questProgress.objectives[obj.id] = {
|
||
current: 0,
|
||
completed: false
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
return progress
|
||
}
|
||
|
||
/**
|
||
* 获取任务状态
|
||
*/
|
||
export const getQuestStatus = (progress: PlayerCampaignProgress, questId: string): QuestStatus => {
|
||
const questProgress = progress.questProgress[questId]
|
||
if (questProgress) {
|
||
return questProgress.status
|
||
}
|
||
|
||
// 检查是否已解锁
|
||
if (progress.unlockedQuests.includes(questId)) {
|
||
return QuestStatus.Available
|
||
}
|
||
|
||
return QuestStatus.Locked
|
||
}
|
||
|
||
/**
|
||
* 开始任务
|
||
*/
|
||
export const startQuest = (player: Player, questId: string): { success: boolean; error?: string } => {
|
||
const progress = player.campaignProgress
|
||
if (!progress) {
|
||
return { success: false, error: 'campaign.errors.notInitialized' }
|
||
}
|
||
|
||
const quest = getQuestById(questId)
|
||
if (!quest) {
|
||
return { success: false, error: 'campaign.errors.questNotFound' }
|
||
}
|
||
|
||
// 检查是否已解锁
|
||
if (!progress.unlockedQuests.includes(questId)) {
|
||
return { success: false, error: 'campaign.errors.questLocked' }
|
||
}
|
||
|
||
// 检查是否已完成
|
||
if (progress.completedQuests.includes(questId)) {
|
||
return { success: false, error: 'campaign.errors.questAlreadyCompleted' }
|
||
}
|
||
|
||
// 初始化或更新任务进度
|
||
if (!progress.questProgress[questId]) {
|
||
progress.questProgress[questId] = {
|
||
questId,
|
||
status: QuestStatus.Active,
|
||
objectives: {},
|
||
startedAt: Date.now()
|
||
}
|
||
}
|
||
const questProgress = progress.questProgress[questId]
|
||
if (questProgress) {
|
||
questProgress.status = QuestStatus.Active
|
||
questProgress.startedAt = Date.now()
|
||
// 初始化目标进度
|
||
quest.objectives.forEach(obj => {
|
||
if (!questProgress.objectives[obj.id]) {
|
||
questProgress.objectives[obj.id] = {
|
||
current: 0,
|
||
completed: false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
progress.currentQuestId = questId
|
||
|
||
return { success: true }
|
||
}
|
||
|
||
/**
|
||
* 检查单个目标的进度
|
||
*/
|
||
export const checkObjectiveProgress = (player: Player, objective: QuestObjective, npcs: NPC[]): number => {
|
||
switch (objective.type) {
|
||
case ObjectiveType.BuildBuilding: {
|
||
// 检查玩家所有星球上该建筑的最高等级
|
||
const targetLevel = objective.targetSecondary as number
|
||
const buildingType = objective.target as BuildingType
|
||
const hasBuilding = player.planets.some(planet => {
|
||
const level = planet.buildings[buildingType] || 0
|
||
return level >= targetLevel
|
||
})
|
||
return hasBuilding ? 1 : 0
|
||
}
|
||
|
||
case ObjectiveType.ResearchTech: {
|
||
const targetLevel = objective.targetSecondary as number
|
||
const techType = objective.target as TechnologyType
|
||
const currentLevel = player.technologies[techType] || 0
|
||
return currentLevel >= targetLevel ? 1 : 0
|
||
}
|
||
|
||
case ObjectiveType.ProduceShips: {
|
||
// 统计所有星球上该类型舰船的总数
|
||
const shipType = objective.target as ShipType
|
||
let totalShips = 0
|
||
player.planets.forEach(planet => {
|
||
totalShips += planet.fleet[shipType] || 0
|
||
})
|
||
return Math.min(totalShips, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.AccumulateResources: {
|
||
// 统计所有星球的资源总量
|
||
let totalResources = 0
|
||
player.planets.forEach(planet => {
|
||
totalResources += planet.resources.metal + planet.resources.crystal + planet.resources.deuterium
|
||
})
|
||
return Math.min(totalResources, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.Colonize: {
|
||
// 统计殖民地数量(不包括母星)
|
||
const colonyCount = player.planets.length - 1
|
||
return Math.min(colonyCount, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.SpyTarget: {
|
||
// 统计侦查报告数量
|
||
const target = objective.target
|
||
if (target === 'any') {
|
||
return Math.min(player.spyReports.length, objective.required)
|
||
} else if (target === 'hostile') {
|
||
// 统计敌对NPC的侦查报告
|
||
const hostileNpcIds = npcs
|
||
.filter(npc => {
|
||
const relation = npc.relations?.[player.id]
|
||
return relation?.status === RelationStatus.Hostile
|
||
})
|
||
.map(npc => npc.id)
|
||
const hostileReports = player.spyReports.filter(report => hostileNpcIds.includes(report.targetPlayerId || ''))
|
||
return Math.min(hostileReports.length, objective.required)
|
||
}
|
||
return 0
|
||
}
|
||
|
||
case ObjectiveType.SendGift: {
|
||
// 从成就统计中获取送礼次数
|
||
return Math.min(player.achievementStats?.giftsSent || 0, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.Expedition: {
|
||
// 从成就统计中获取探险次数(使用 expeditionsTotal)
|
||
return Math.min(player.achievementStats?.expeditionsTotal || 0, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.WinBattles: {
|
||
const target = objective.target
|
||
if (target === 'attack') {
|
||
return Math.min(player.achievementStats?.attacksWon || 0, objective.required)
|
||
} else if (target === 'defense') {
|
||
// 使用 defensesSuccessful 代替 defensesWon
|
||
return Math.min(player.achievementStats?.defensesSuccessful || 0, objective.required)
|
||
}
|
||
const totalWins = (player.achievementStats?.attacksWon || 0) + (player.achievementStats?.defensesSuccessful || 0)
|
||
return Math.min(totalWins, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.RecycleDebris: {
|
||
return Math.min(player.achievementStats?.recyclingMissions || 0, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.ReachRelation: {
|
||
// 检查是否有任何NPC达到指定关系等级
|
||
const targetStatus = objective.target as string
|
||
const hasRelation = npcs.some(npc => {
|
||
const relation = npc.relations?.[player.id]
|
||
return relation?.status === targetStatus
|
||
})
|
||
return hasRelation ? 1 : 0
|
||
}
|
||
|
||
case ObjectiveType.FormAlliance: {
|
||
// 检查是否有盟友(使用 Friendly 作为最高关系等级)
|
||
const hasAlliance = npcs.some(npc => {
|
||
const relation = npc.relations?.[player.id]
|
||
return relation?.status === RelationStatus.Friendly && (relation.reputation || 0) >= 80
|
||
})
|
||
return hasAlliance ? 1 : 0
|
||
}
|
||
|
||
case ObjectiveType.DefeatNPC: {
|
||
// 检查攻击NPC获胜次数
|
||
return Math.min(player.achievementStats?.attacksWon || 0, objective.required)
|
||
}
|
||
|
||
case ObjectiveType.DestroyPlanet: {
|
||
return Math.min(player.achievementStats?.planetDestructions || 0, objective.required)
|
||
}
|
||
|
||
default:
|
||
return 0
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新任务进度
|
||
*/
|
||
export const updateQuestProgress = (
|
||
player: Player,
|
||
questId: string,
|
||
npcs: NPC[]
|
||
): {
|
||
updated: boolean
|
||
completedObjectives: string[]
|
||
questCompleted: boolean
|
||
} => {
|
||
const result = {
|
||
updated: false,
|
||
completedObjectives: [] as string[],
|
||
questCompleted: false
|
||
}
|
||
|
||
const progress = player.campaignProgress
|
||
if (!progress) return result
|
||
|
||
const questProgress = progress.questProgress[questId]
|
||
if (!questProgress || questProgress.status !== QuestStatus.Active) return result
|
||
|
||
const quest = getQuestById(questId)
|
||
if (!quest) return result
|
||
|
||
// 检查每个目标的进度
|
||
quest.objectives.forEach(objective => {
|
||
const objProgress = questProgress.objectives[objective.id]
|
||
if (!objProgress || objProgress.completed) return
|
||
|
||
const newProgress = checkObjectiveProgress(player, objective, npcs)
|
||
if (newProgress !== objProgress.current) {
|
||
objProgress.current = newProgress
|
||
result.updated = true
|
||
|
||
// 检查目标是否完成
|
||
if (newProgress >= objective.required && !objProgress.completed) {
|
||
objProgress.completed = true
|
||
result.completedObjectives.push(objective.id)
|
||
}
|
||
}
|
||
})
|
||
|
||
// 检查任务是否全部完成
|
||
const allCompleted = quest.objectives.every(obj => questProgress.objectives[obj.id]?.completed)
|
||
|
||
if (allCompleted && questProgress.status === QuestStatus.Active) {
|
||
questProgress.status = QuestStatus.Completed
|
||
questProgress.completedAt = Date.now()
|
||
result.questCompleted = true
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* 领取任务奖励
|
||
*/
|
||
export const claimQuestRewards = (
|
||
player: Player,
|
||
questId: string
|
||
): {
|
||
success: boolean
|
||
rewards?: QuestReward
|
||
error?: string
|
||
} => {
|
||
const progress = player.campaignProgress
|
||
if (!progress) {
|
||
return { success: false, error: 'campaign.errors.notInitialized' }
|
||
}
|
||
|
||
const questProgress = progress.questProgress[questId]
|
||
if (!questProgress) {
|
||
return { success: false, error: 'campaign.errors.questNotFound' }
|
||
}
|
||
|
||
if (questProgress.status !== QuestStatus.Completed) {
|
||
return { success: false, error: 'campaign.errors.questNotCompleted' }
|
||
}
|
||
|
||
if (questProgress.rewardsClaimed) {
|
||
return { success: false, error: 'campaign.errors.rewardsAlreadyClaimed' }
|
||
}
|
||
|
||
const quest = getQuestById(questId)
|
||
if (!quest) {
|
||
return { success: false, error: 'campaign.errors.questNotFound' }
|
||
}
|
||
|
||
// 发放奖励
|
||
const rewards = quest.rewards
|
||
const currentPlanet = player.planets[0] // 默认发放到第一个星球
|
||
|
||
if (rewards.resources && currentPlanet) {
|
||
resourceLogic.addResources(currentPlanet.resources, rewards.resources)
|
||
}
|
||
|
||
if (rewards.darkMatter && currentPlanet) {
|
||
currentPlanet.resources.darkMatter += rewards.darkMatter
|
||
}
|
||
|
||
if (rewards.points) {
|
||
// 奖励积分存入 bonusPoints,不会被 calculatePlayerPoints 覆盖
|
||
player.bonusPoints = (player.bonusPoints || 0) + rewards.points
|
||
}
|
||
|
||
if (rewards.ships && currentPlanet) {
|
||
Object.entries(rewards.ships).forEach(([shipType, count]) => {
|
||
const ship = shipType as ShipType
|
||
currentPlanet.fleet[ship] = (currentPlanet.fleet[ship] || 0) + count
|
||
})
|
||
}
|
||
|
||
// 标记奖励已领取
|
||
questProgress.rewardsClaimed = true
|
||
|
||
// 将任务添加到已完成列表
|
||
if (!progress.completedQuests.includes(questId)) {
|
||
progress.completedQuests.push(questId)
|
||
}
|
||
|
||
// 解锁后续任务
|
||
unlockNextQuests(player, questId)
|
||
|
||
return { success: true, rewards }
|
||
}
|
||
|
||
/**
|
||
* 解锁后续任务
|
||
*/
|
||
export const unlockNextQuests = (player: Player, _completedQuestId: string): string[] => {
|
||
const progress = player.campaignProgress
|
||
if (!progress) return []
|
||
|
||
const unlockedQuests: string[] = []
|
||
const allQuests = getAllQuests()
|
||
|
||
allQuests.forEach(quest => {
|
||
// 跳过已解锁的任务
|
||
if (progress.unlockedQuests.includes(quest.id)) return
|
||
|
||
// 检查前置任务是否完成
|
||
if (quest.requiredQuestIds && quest.requiredQuestIds.length > 0) {
|
||
const allRequiredCompleted = quest.requiredQuestIds.every(reqId => progress.completedQuests.includes(reqId))
|
||
|
||
if (allRequiredCompleted) {
|
||
progress.unlockedQuests.push(quest.id)
|
||
unlockedQuests.push(quest.id)
|
||
|
||
// 初始化任务进度
|
||
progress.questProgress[quest.id] = {
|
||
questId: quest.id,
|
||
status: QuestStatus.Available,
|
||
objectives: {}
|
||
}
|
||
const questProgress = progress.questProgress[quest.id]
|
||
if (questProgress) {
|
||
quest.objectives.forEach(obj => {
|
||
questProgress.objectives[obj.id] = {
|
||
current: 0,
|
||
completed: false
|
||
}
|
||
})
|
||
}
|
||
|
||
// 更新当前章节
|
||
if (quest.chapter > progress.currentChapter) {
|
||
progress.currentChapter = quest.chapter
|
||
}
|
||
}
|
||
}
|
||
})
|
||
|
||
return unlockedQuests
|
||
}
|
||
|
||
/**
|
||
* 计算战役总进度百分比
|
||
*/
|
||
export const calculateCampaignProgress = (progress: PlayerCampaignProgress): number => {
|
||
const totalQuests = getAllQuests().length
|
||
const completedCount = progress.completedQuests.length
|
||
return Math.round((completedCount / totalQuests) * 100)
|
||
}
|
||
|
||
/**
|
||
* 计算章节进度百分比
|
||
*/
|
||
export const calculateChapterProgress = (progress: PlayerCampaignProgress, chapterNumber: number): number => {
|
||
const chapterQuests = getQuestsByChapter(chapterNumber)
|
||
const completedCount = chapterQuests.filter(quest => progress.completedQuests.includes(quest.id)).length
|
||
return Math.round((completedCount / chapterQuests.length) * 100)
|
||
}
|
||
|
||
/**
|
||
* 计算单个任务的进度百分比
|
||
*/
|
||
export const calculateQuestProgress = (progress: PlayerCampaignProgress, questId: string): number => {
|
||
const questProgress = progress.questProgress[questId]
|
||
if (!questProgress) return 0
|
||
|
||
const quest = getQuestById(questId)
|
||
if (!quest) return 0
|
||
|
||
// 计算所有目标的平均进度
|
||
const objectives = quest.objectives
|
||
if (objectives.length === 0) return 0
|
||
|
||
let totalProgress = 0
|
||
objectives.forEach(obj => {
|
||
const objProgress = questProgress.objectives[obj.id]
|
||
if (objProgress) {
|
||
if (objProgress.completed) {
|
||
totalProgress += 100
|
||
} else {
|
||
totalProgress += Math.min((objProgress.current / obj.required) * 100, 100)
|
||
}
|
||
}
|
||
})
|
||
|
||
return Math.round(totalProgress / objectives.length)
|
||
}
|
||
|
||
/**
|
||
* 获取当前可进行的任务列表
|
||
*/
|
||
export const getAvailableQuests = (progress: PlayerCampaignProgress): CampaignQuestConfig[] => {
|
||
return getAllQuests().filter(quest => {
|
||
const status = getQuestStatus(progress, quest.id)
|
||
return status === QuestStatus.Available || status === QuestStatus.Active
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取当前激活的任务
|
||
*/
|
||
export const getActiveQuest = (progress: PlayerCampaignProgress): CampaignQuestConfig | undefined => {
|
||
if (!progress.currentQuestId) return undefined
|
||
return getQuestById(progress.currentQuestId)
|
||
}
|
||
|
||
/**
|
||
* 创建任务通知
|
||
*/
|
||
export const createQuestNotification = (
|
||
questId: string,
|
||
eventType: QuestNotification['eventType'],
|
||
messageKey: string,
|
||
messageParams?: Record<string, string | number>,
|
||
rewards?: QuestReward
|
||
): QuestNotification => {
|
||
const quest = getQuestById(questId)
|
||
return {
|
||
id: `quest_notification_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
||
timestamp: Date.now(),
|
||
questId,
|
||
questTitleKey: quest?.titleKey || '',
|
||
eventType,
|
||
messageKey,
|
||
messageParams,
|
||
rewards,
|
||
read: false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 添加任务通知到玩家
|
||
*/
|
||
export const addQuestNotification = (player: Player, notification: QuestNotification): void => {
|
||
if (!player.questNotifications) {
|
||
player.questNotifications = []
|
||
}
|
||
player.questNotifications.unshift(notification)
|
||
|
||
// 限制通知数量
|
||
if (player.questNotifications.length > 50) {
|
||
player.questNotifications = player.questNotifications.slice(0, 50)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 标记通知为已读
|
||
*/
|
||
export const markQuestNotificationRead = (player: Player, notificationId: string): void => {
|
||
const notification = player.questNotifications?.find(n => n.id === notificationId)
|
||
if (notification) {
|
||
notification.read = true
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取未读通知数量
|
||
*/
|
||
export const getUnreadQuestNotificationCount = (player: Player): number => {
|
||
return player.questNotifications?.filter(n => !n.read).length || 0
|
||
}
|
||
|
||
/**
|
||
* 检查并更新所有激活任务的进度
|
||
*/
|
||
export const checkAllActiveQuestsProgress = (player: Player, npcs: NPC[]): QuestNotification[] => {
|
||
const notifications: QuestNotification[] = []
|
||
const progress = player.campaignProgress
|
||
|
||
if (!progress) return notifications
|
||
|
||
// 遍历所有激活的任务
|
||
Object.entries(progress.questProgress).forEach(([questId, questProgress]) => {
|
||
if (questProgress.status !== QuestStatus.Active) return
|
||
|
||
const result = updateQuestProgress(player, questId, npcs)
|
||
|
||
// 为完成的目标创建通知
|
||
result.completedObjectives.forEach(objId => {
|
||
const quest = getQuestById(questId)
|
||
const objective = quest?.objectives.find(o => o.id === objId)
|
||
if (objective) {
|
||
notifications.push(
|
||
createQuestNotification(questId, 'objective_completed', 'campaign.notifications.objectiveCompleted', {
|
||
objective: objective.descriptionKey
|
||
})
|
||
)
|
||
}
|
||
})
|
||
|
||
// 任务完成通知
|
||
if (result.questCompleted) {
|
||
const quest = getQuestById(questId)
|
||
notifications.push(createQuestNotification(questId, 'quest_completed', 'campaign.notifications.questCompleted', {}, quest?.rewards))
|
||
}
|
||
})
|
||
|
||
// 添加通知到玩家
|
||
notifications.forEach(notification => {
|
||
addQuestNotification(player, notification)
|
||
})
|
||
|
||
return notifications
|
||
}
|