feat: 新增Android平台支持及构建流程

集成Android平台相关目录与配置文件,包含Gradle构建脚本、资源文件、启动图标、Java入口、Proguard规则等,完善.gitignore以排除Android构建产物。更新CI流程,支持自动构建并发布Android APK。移除README中项目结构说明,简化文档。
This commit is contained in:
谦君
2025-12-20 00:48:36 +08:00
parent 20fb2bb6a4
commit 1368bb4445
97 changed files with 7859 additions and 335 deletions

View File

@@ -73,6 +73,21 @@ export const createFleetMission = (
}
}
/**
* 运输任务失败原因
*/
export type TransportFailReason = 'targetNotFound' | 'giftRejected'
/**
* 运输任务结果
*/
export interface TransportResult {
success: boolean
reputationGain?: number
overflow?: Resources
failReason?: TransportFailReason
}
/**
* 处理运输任务到达
*/
@@ -83,7 +98,7 @@ export const processTransportArrival = (
allNpcs?: NPC[],
locale: Locale = 'zh-CN',
storageCapacityBonus: number = 0
): { success: boolean; reputationGain?: number; overflow?: Resources } => {
): TransportResult => {
// 检查是否是赠送任务
if (mission.isGift && mission.giftTargetNpcId && player && allNpcs) {
const targetNpc = allNpcs.find(n => n.id === mission.giftTargetNpcId)
@@ -94,7 +109,7 @@ export const processTransportArrival = (
// 如果礼物被拒绝,资源返还给玩家
if (!giftResult.accepted) {
// 资源保留在cargo中返回时会退还给玩家
return { success: false, reputationGain: undefined }
return { success: false, reputationGain: undefined, failReason: 'giftRejected' }
}
// 礼物被接受,清空货物
@@ -114,10 +129,13 @@ export const processTransportArrival = (
mission.cargo = result.overflow
return { success: true, overflow: result.overflow }
}
// 运输成功,清空货物
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 }
return { success: false, failReason: 'targetNotFound' }
}
/**
@@ -356,6 +374,15 @@ export const canColonize = (player: Player): boolean => {
return currentPlanets < maxPlanets
}
/**
* 殖民任务结果
*/
export interface ColonizeResult {
success: boolean
planet: Planet | null
failReason?: 'positionOccupied' | 'maxColoniesReached'
}
/**
* 处理殖民任务到达
*/
@@ -364,18 +391,18 @@ export const processColonizeArrival = (
targetPlanet: Planet | undefined,
player: Player,
colonyNameTemplate: string = 'Colony'
): Planet | null => {
): ColonizeResult => {
if (targetPlanet) {
// 位置已被占用
mission.status = 'returning'
return null
return { success: false, planet: null, failReason: 'positionOccupied' }
}
// 检查殖民地槽位限制
if (!canColonize(player)) {
// 超出殖民地数量限制,殖民船返回
mission.status = 'returning'
return null
return { success: false, planet: null, failReason: 'maxColoniesReached' }
}
// 创建新殖民地
@@ -431,7 +458,7 @@ export const processColonizeArrival = (
mission.fleet[ShipType.ColonyShip] = (mission.fleet[ShipType.ColonyShip] || 1) - 1
mission.status = 'returning'
return newPlanet
return { success: true, planet: newPlanet }
}
/**
@@ -481,6 +508,20 @@ export const calculateSpyDetectionChance = (attackerSpyLevel: number, defenderSp
return Math.max(0.01, Math.min(0.99, baseChance))
}
/**
* 侦查任务失败原因
*/
export type SpyFailReason = 'targetNotFound'
/**
* 侦查任务结果
*/
export interface SpyResult {
success: boolean
report?: SpyReport
failReason?: SpyFailReason
}
/**
* 处理间谍任务到达
*/
@@ -491,10 +532,10 @@ export const processSpyArrival = (
defender: Player | null,
allNpcs?: NPC[],
locale: Locale = 'zh-CN'
): SpyReport | null => {
): SpyResult => {
if (!targetPlanet) {
mission.status = 'returning'
return null
return { success: false, failReason: 'targetNotFound' }
}
// 获取间谍技术等级
@@ -536,7 +577,21 @@ export const processSpyArrival = (
}
mission.status = 'returning'
return spyReport
return { success: true, report: spyReport }
}
/**
* 部署任务失败原因
*/
export type DeployFailReason = 'targetNotFound' | 'notOwnPlanet'
/**
* 部署任务结果
*/
export interface DeployResult {
success: boolean
overflow?: Partial<Record<ShipType, number>>
failReason?: DeployFailReason
}
/**
@@ -547,10 +602,14 @@ export const processDeployArrival = (
targetPlanet: Planet | undefined,
playerId: string,
technologies: Record<TechnologyType, number>
): { success: boolean; overflow?: Partial<Record<ShipType, number>> } => {
if (!targetPlanet || targetPlanet.ownerId !== playerId) {
): DeployResult => {
if (!targetPlanet) {
mission.status = 'returning'
return { success: false }
return { success: false, failReason: 'targetNotFound' }
}
if (targetPlanet.ownerId !== playerId) {
mission.status = 'returning'
return { success: false, failReason: 'notOwnPlanet' }
}
// 使用安全添加函数,防止舰队仓储溢出
@@ -566,6 +625,21 @@ export const processDeployArrival = (
return { success: true }
}
/**
* 回收任务失败原因
*/
export type RecycleFailReason = 'noDebrisField' | 'debrisEmpty'
/**
* 回收任务结果
*/
export interface RecycleResult {
success: boolean
collectedResources?: Pick<Resources, 'metal' | 'crystal'>
remainingDebris?: Pick<Resources, 'metal' | 'crystal'> | null
failReason?: RecycleFailReason
}
/**
* 处理回收任务到达
*/
@@ -575,10 +649,10 @@ export const processRecycleArrival = (
player?: Player,
allNpcs?: NPC[],
locale: Locale = 'zh-CN'
): { collectedResources: Pick<Resources, 'metal' | 'crystal'>; remainingDebris: Pick<Resources, 'metal' | 'crystal'> | null } | null => {
): RecycleResult => {
if (!debrisField) {
mission.status = 'returning'
return null
return { success: false, failReason: 'noDebrisField' }
}
// 计算回收船的货舱容量
@@ -599,7 +673,7 @@ export const processRecycleArrival = (
// 防止除零如果残骸为0直接返回
if (totalDebris === 0) {
mission.status = 'returning'
return null
return { success: false, failReason: 'debrisEmpty' }
}
// 按比例收集金属和晶体
@@ -625,6 +699,7 @@ export const processRecycleArrival = (
}
return {
success: true,
collectedResources: {
metal: collectedMetal,
crystal: collectedCrystal
@@ -840,42 +915,114 @@ export const calculatePlanetDefensePower = (fleet: Partial<Fleet>, defense: Part
}
/**
* 处理行星毁灭任务到达
* 处理行星/月球毁灭任务到达
* OGame规则
* - 月球销毁概率 = (100 - √diameter) × √deathstars
* - 死星反向销毁概率 = √diameter / 2
* - 两个概率独立判定
*/
/**
* 毁灭任务失败原因
*/
export type DestroyFailReason = 'targetNotFound' | 'ownPlanet' | 'noDeathstar' | 'chanceFailed'
/**
* 销毁任务结果
*/
export interface DestroyResult {
success: boolean // 目标是否被销毁
destructionChance: number // 销毁概率
planetId?: string // 被销毁的星球/月球ID
deathstarsLost: boolean // 死星是否被反向销毁
deathstarDestructionChance: number // 死星反向销毁概率
isMoon: boolean // 目标是否为月球
failReason?: DestroyFailReason // 失败原因
}
export const processDestroyArrival = (
mission: FleetMission,
targetPlanet: Planet | undefined,
attacker: Player
): { success: boolean; destructionChance: number; planetId?: string } | null => {
if (!targetPlanet || targetPlanet.ownerId === attacker.id) {
): DestroyResult => {
if (!targetPlanet) {
mission.status = 'returning'
return null
return {
success: false,
destructionChance: 0,
deathstarsLost: false,
deathstarDestructionChance: 0,
isMoon: false,
failReason: 'targetNotFound'
}
}
if (targetPlanet.ownerId === attacker.id) {
mission.status = 'returning'
return {
success: false,
destructionChance: 0,
deathstarsLost: false,
deathstarDestructionChance: 0,
isMoon: targetPlanet.isMoon || false,
failReason: 'ownPlanet'
}
}
// 检查是否有死星
const deathstarCount = mission.fleet[ShipType.Deathstar] || 0
if (deathstarCount === 0) {
mission.status = 'returning'
return null
return {
success: false,
destructionChance: 0,
deathstarsLost: false,
deathstarDestructionChance: 0,
isMoon: targetPlanet.isMoon || false,
failReason: 'noDeathstar'
}
}
// 计算目标星球的防御力量
const planetaryShieldCount = targetPlanet.defense[DefenseType.PlanetaryShield] || 0
const defensePower = calculatePlanetDefensePower(targetPlanet.fleet, targetPlanet.defense)
// 根据目标类型使用不同的销毁逻辑
if (targetPlanet.isMoon) {
// 月球销毁使用 OGame 公式
const result = moonLogic.tryDestroyMoon(targetPlanet, deathstarCount)
// 计算摧毁概率
const destructionChance = calculateDestructionChance(deathstarCount, planetaryShieldCount, defensePower)
// 如果死星被反向销毁,从任务舰队中移除
if (result.deathstarsDestroyed) {
mission.fleet[ShipType.Deathstar] = 0
}
// 随机判断是否成功
const randomValue = Math.random() * 100
const success = randomValue < destructionChance
mission.status = 'returning'
mission.status = 'returning'
return {
success: result.moonDestroyed,
destructionChance: result.moonDestructionChance,
planetId: result.moonDestroyed ? targetPlanet.id : undefined,
deathstarsLost: result.deathstarsDestroyed,
deathstarDestructionChance: result.deathstarDestructionChance,
isMoon: true,
failReason: result.moonDestroyed ? undefined : 'chanceFailed'
}
} else {
// 行星销毁使用原有逻辑
const planetaryShieldCount = targetPlanet.defense[DefenseType.PlanetaryShield] || 0
const defensePower = calculatePlanetDefensePower(targetPlanet.fleet, targetPlanet.defense)
const destructionChance = calculateDestructionChance(deathstarCount, planetaryShieldCount, defensePower)
return {
success,
destructionChance,
planetId: success ? targetPlanet.id : undefined
const randomValue = Math.random() * 100
const success = randomValue < destructionChance
mission.status = 'returning'
return {
success,
destructionChance,
planetId: success ? targetPlanet.id : undefined,
deathstarsLost: false,
deathstarDestructionChance: 0,
isMoon: false,
failReason: success ? undefined : 'chanceFailed'
}
}
}
@@ -985,17 +1132,17 @@ export const updateFleetMissions = async (
}
case MissionType.Colonize:
const newColony = processColonizeArrival(mission, targetPlanet, attacker)
if (newColony) {
newColonies.push(newColony)
planets.set(targetKey, newColony)
const colonizeResult = processColonizeArrival(mission, targetPlanet, attacker)
if (colonizeResult.success && colonizeResult.planet) {
newColonies.push(colonizeResult.planet)
planets.set(targetKey, colonizeResult.planet)
}
break
case MissionType.Spy:
const spyReport = processSpyArrival(mission, targetPlanet, attacker, defender)
if (spyReport) {
spyReports.push(spyReport)
const spyResult = processSpyArrival(mission, targetPlanet, attacker, defender)
if (spyResult.success && spyResult.report) {
spyReports.push(spyResult.report)
}
break
@@ -1010,7 +1157,7 @@ export const updateFleetMissions = async (
const debrisId = `debris_${mission.targetPosition.galaxy}_${mission.targetPosition.system}_${mission.targetPosition.position}`
const debrisField = debrisFields.get(debrisId)
const recycleResult = processRecycleArrival(mission, debrisField, attacker, allNpcs)
if (recycleResult) {
if (recycleResult.success && recycleResult.collectedResources) {
if (recycleResult.remainingDebris) {
// 更新残骸场
const updatedDebris: DebrisField = {
@@ -1029,7 +1176,7 @@ export const updateFleetMissions = async (
case MissionType.Destroy:
const destroyResult = processDestroyArrival(mission, targetPlanet, attacker)
if (destroyResult && destroyResult.success && destroyResult.planetId) {
if (destroyResult.success && destroyResult.planetId) {
// 星球被摧毁
destroyedPlanetIds.push(destroyResult.planetId)