1.0
This commit is contained in:
683
MD说明文件夹/月度任务系统详细实现.md
Normal file
683
MD说明文件夹/月度任务系统详细实现.md
Normal file
@@ -0,0 +1,683 @@
|
||||
# 月度任务系统详细实现文档
|
||||
|
||||
## 📊 功能概述
|
||||
|
||||
月度任务系统是v2.1.1版本的核心新功能,提供钓鱼和竞技场的月度进度跟踪和自动补齐功能。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 核心参数
|
||||
|
||||
```javascript
|
||||
// 月度目标
|
||||
const FISH_TARGET = 320 // 钓鱼月度目标:320次
|
||||
const ARENA_TARGET = 240 // 竞技场月度目标:240次
|
||||
|
||||
// 状态变量
|
||||
const monthLoading = ref(false) // 刷新进度中
|
||||
const fishToppingUp = ref(false) // 钓鱼补齐中
|
||||
const arenaToppingUp = ref(false) // 竞技场补齐中
|
||||
const monthActivity = ref(null) // 月度任务数据
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 进度计算逻辑
|
||||
|
||||
### 1. 时间相关计算
|
||||
|
||||
```javascript
|
||||
// 获取当前日期信息
|
||||
const now = new Date()
|
||||
const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate() // 本月总天数
|
||||
const dayOfMonth = now.getDate() // 当前是本月第几天
|
||||
|
||||
// 剩余天数
|
||||
const remainingDays = computed(() => Math.max(0, daysInMonth - dayOfMonth))
|
||||
|
||||
// 月度进度(当前天数 / 总天数)
|
||||
const monthProgress = computed(() => Math.min(1, Math.max(0, dayOfMonth / daysInMonth)))
|
||||
|
||||
// 月度进度百分比(显示用)
|
||||
const monthPercent = computed(() => Math.min(100, Math.round((dayOfMonth / daysInMonth) * 100)))
|
||||
```
|
||||
|
||||
### 2. 任务进度计算
|
||||
|
||||
```javascript
|
||||
// 从API获取的数据
|
||||
const myMonthInfo = computed(() => monthActivity.value?.myMonthInfo || {})
|
||||
const myArenaInfo = computed(() => monthActivity.value?.myArenaInfo || {})
|
||||
|
||||
// 当前完成数量
|
||||
const fishNum = computed(() => Number(myMonthInfo.value?.['2']?.num || 0))
|
||||
const arenaNum = computed(() => Number(myArenaInfo.value?.num || 0))
|
||||
|
||||
// 完成百分比
|
||||
const fishPercent = computed(() => Math.min(100, Math.round((fishNum.value / FISH_TARGET) * 100)))
|
||||
const arenaPercent = computed(() => Math.min(100, Math.round((arenaNum.value / ARENA_TARGET) * 100)))
|
||||
```
|
||||
|
||||
### 3. 应该完成的数量(补齐目标)
|
||||
|
||||
```javascript
|
||||
// 钓鱼应该完成的数量
|
||||
const fishShouldBe = computed(() => {
|
||||
if (remainingDays.value === 0) {
|
||||
return FISH_TARGET // 最后一天,按满额计算
|
||||
}
|
||||
return Math.min(FISH_TARGET, Math.ceil(monthProgress.value * FISH_TARGET))
|
||||
})
|
||||
|
||||
// 竞技场应该完成的数量
|
||||
const arenaShouldBe = computed(() => {
|
||||
if (remainingDays.value === 0) {
|
||||
return ARENA_TARGET
|
||||
}
|
||||
return Math.min(ARENA_TARGET, Math.ceil(monthProgress.value * ARENA_TARGET))
|
||||
})
|
||||
|
||||
// 需要补齐的数量
|
||||
const fishNeeded = computed(() => Math.max(0, fishShouldBe.value - fishNum.value))
|
||||
const arenaNeeded = computed(() => Math.max(0, arenaShouldBe.value - arenaNum.value))
|
||||
```
|
||||
|
||||
**计算示例**:
|
||||
- 假设今天是10月15日,本月共31天
|
||||
- 月度进度 = 15 / 31 = 48.39%
|
||||
- 钓鱼应该完成 = 320 × 48.39% = 155次(向上取整)
|
||||
- 如果当前完成100次,需要补齐 = 155 - 100 = 55次
|
||||
|
||||
---
|
||||
|
||||
## 🎣 钓鱼补齐功能
|
||||
|
||||
### 实现逻辑
|
||||
|
||||
```javascript
|
||||
const topUpMonthly = (type) => {
|
||||
const isFish = type === 'fish'
|
||||
const target = isFish ? FISH_TARGET : ARENA_TARGET
|
||||
const current = isFish ? fishNum.value : arenaNum.value
|
||||
const shouldBe = isFish ? fishShouldBe.value : arenaShouldBe.value
|
||||
const needed = Math.max(0, shouldBe - current)
|
||||
|
||||
if (needed === 0) {
|
||||
message.info(`${isFish ? '钓鱼' : '竞技场'}已达标,无需补齐`)
|
||||
return
|
||||
}
|
||||
|
||||
if (isFish) {
|
||||
topUpFish(needed)
|
||||
} else {
|
||||
topUpArena(needed)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 钓鱼补齐详细流程
|
||||
|
||||
```javascript
|
||||
const topUpFish = async (needed) => {
|
||||
fishToppingUp.value = true
|
||||
try {
|
||||
// 1. 获取当前资源
|
||||
const roleInfo = tokenStore.gameData?.roleInfo?.role
|
||||
const items = roleInfo?.items || {}
|
||||
|
||||
// 2. 解析鱼竿数量
|
||||
const normalRod = getItemCount(items, 1011) || 0 // 普通鱼竿ID: 1011
|
||||
const goldRod = getItemCount(items, 1012) || 0 // 金鱼竿ID: 1012
|
||||
|
||||
// 3. 计算使用策略(优先使用免费次数)
|
||||
let useFree = 0
|
||||
let useGold = 0
|
||||
|
||||
if (normalRod >= needed) {
|
||||
// 免费次数足够
|
||||
useFree = needed
|
||||
} else {
|
||||
// 免费次数不足,使用金鱼竿
|
||||
useFree = normalRod
|
||||
useGold = needed - normalRod
|
||||
|
||||
if (goldRod < useGold) {
|
||||
message.warning(`金鱼竿不足!需要${useGold}个,仅有${goldRod}个`)
|
||||
// 根据可用金鱼竿调整补齐数量
|
||||
useGold = goldRod
|
||||
}
|
||||
}
|
||||
|
||||
const total = useFree + useGold
|
||||
if (total === 0) {
|
||||
message.error('没有可用的鱼竿')
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 执行钓鱼
|
||||
message.info(`开始钓鱼:使用普通鱼竿${useFree}次,金鱼竿${useGold}次`)
|
||||
|
||||
// 先使用普通鱼竿
|
||||
for (let i = 0; i < useFree; i++) {
|
||||
await tokenStore.sendMessageWithPromise(
|
||||
tokenStore.selectedToken.id,
|
||||
'fishing_fish',
|
||||
{ fishingType: 1 }, // 1=普通鱼竿
|
||||
5000
|
||||
)
|
||||
await sleep(500) // 间隔500ms
|
||||
}
|
||||
|
||||
// 再使用金鱼竿
|
||||
for (let i = 0; i < useGold; i++) {
|
||||
await tokenStore.sendMessageWithPromise(
|
||||
tokenStore.selectedToken.id,
|
||||
'fishing_fish',
|
||||
{ fishingType: 2 }, // 2=金鱼竿
|
||||
5000
|
||||
)
|
||||
await sleep(500)
|
||||
}
|
||||
|
||||
// 5. 完成后刷新进度
|
||||
await sleep(1000)
|
||||
await fetchMonthlyActivity()
|
||||
|
||||
message.success(`钓鱼补齐完成!共完成${total}次钓鱼`)
|
||||
|
||||
} catch (error) {
|
||||
message.error(`钓鱼补齐失败:${error.message}`)
|
||||
} finally {
|
||||
fishToppingUp.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 鱼竿数量解析
|
||||
|
||||
```javascript
|
||||
const getItemCount = (items, itemId) => {
|
||||
if (!items) return 0
|
||||
|
||||
// 支持多种数据结构
|
||||
|
||||
// 1. 数组结构:[{id: 1011, num: 10}, ...]
|
||||
if (Array.isArray(items)) {
|
||||
const found = items.find(it => Number(it.id ?? it.itemId) === itemId)
|
||||
if (!found) return 0
|
||||
return Number(found.num ?? found.count ?? found.quantity ?? 0)
|
||||
}
|
||||
|
||||
// 2. 对象结构:{ '1011': 10 } 或 { '1011': { num: 10 } }
|
||||
const node = items[String(itemId)] ?? items[itemId]
|
||||
if (node == null) return 0
|
||||
|
||||
if (typeof node === 'number') return Number(node)
|
||||
if (typeof node === 'object') {
|
||||
return Number(node.num ?? node.count ?? node.quantity ?? 0)
|
||||
}
|
||||
|
||||
return Number(node) || 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚔️ 竞技场补齐功能
|
||||
|
||||
### 实现逻辑
|
||||
|
||||
```javascript
|
||||
const topUpArena = async (needed) => {
|
||||
arenaToppingUp.value = true
|
||||
try {
|
||||
// 1. 获取当前体力
|
||||
const roleInfo = tokenStore.gameData?.roleInfo?.role
|
||||
const energy = roleInfo?.energy || 0
|
||||
const maxEnergy = roleInfo?.maxEnergy || 100
|
||||
|
||||
// 2. 计算战斗次数(贪心策略)
|
||||
// 假设每次战斗消耗5点体力
|
||||
const ENERGY_PER_BATTLE = 5
|
||||
const possibleBattles = Math.floor(energy / ENERGY_PER_BATTLE)
|
||||
|
||||
if (possibleBattles < needed) {
|
||||
message.warning(`体力不足!需要${needed}次战斗(${needed * ENERGY_PER_BATTLE}体力),当前仅有${energy}体力`)
|
||||
// 可以选择:1) 只打可以打的次数 2) 取消操作
|
||||
// 这里选择打可以打的次数
|
||||
needed = possibleBattles
|
||||
}
|
||||
|
||||
if (needed === 0) {
|
||||
message.error('体力不足,无法进行战斗')
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 执行战斗
|
||||
message.info(`开始竞技场战斗:共${needed}次`)
|
||||
|
||||
let successCount = 0
|
||||
for (let i = 0; i < needed; i++) {
|
||||
try {
|
||||
// 先匹配对手
|
||||
const matchResult = await tokenStore.sendMessageWithPromise(
|
||||
tokenStore.selectedToken.id,
|
||||
'arena_matchopponent',
|
||||
{},
|
||||
5000
|
||||
)
|
||||
|
||||
await sleep(300)
|
||||
|
||||
// 发起战斗
|
||||
const battleResult = await tokenStore.sendMessageWithPromise(
|
||||
tokenStore.selectedToken.id,
|
||||
'arena_battle',
|
||||
{
|
||||
targetRoleId: matchResult?.opponent?.roleId,
|
||||
battleType: 1 // 普通战斗
|
||||
},
|
||||
5000
|
||||
)
|
||||
|
||||
successCount++
|
||||
await sleep(1000) // 战斗间隔
|
||||
|
||||
} catch (error) {
|
||||
console.error(`第${i+1}次战斗失败:`, error)
|
||||
// 继续下一次
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 刷新进度
|
||||
await sleep(1000)
|
||||
await fetchMonthlyActivity()
|
||||
|
||||
message.success(`竞技场补齐完成!成功进行${successCount}次战斗`)
|
||||
|
||||
} catch (error) {
|
||||
message.error(`竞技场补齐失败:${error.message}`)
|
||||
} finally {
|
||||
arenaToppingUp.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 刷新进度功能
|
||||
|
||||
```javascript
|
||||
const fetchMonthlyActivity = async () => {
|
||||
if (!tokenStore.selectedToken) {
|
||||
message.warning('请先选择Token')
|
||||
return
|
||||
}
|
||||
|
||||
const status = tokenStore.getWebSocketStatus(tokenStore.selectedToken.id)
|
||||
if (status !== 'connected') {
|
||||
return
|
||||
}
|
||||
|
||||
monthLoading.value = true
|
||||
try {
|
||||
const tokenId = tokenStore.selectedToken.id
|
||||
const result = await tokenStore.sendMessageWithPromise(
|
||||
tokenId,
|
||||
'activity_get', // 获取月度任务信息的命令
|
||||
{},
|
||||
10000
|
||||
)
|
||||
|
||||
// 解析响应数据(兼容多种格式)
|
||||
const act = result?.activity || result?.body?.activity || result
|
||||
monthActivity.value = act || null
|
||||
|
||||
if (act) {
|
||||
message.success('月度任务进度已更新')
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(`获取月度任务失败:${e.message}`)
|
||||
} finally {
|
||||
monthLoading.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎬 一键完成功能
|
||||
|
||||
### 下拉菜单选项
|
||||
|
||||
```javascript
|
||||
const fishMoreOptions = [
|
||||
{ label: '一键完成', key: 'complete-fish' }
|
||||
]
|
||||
|
||||
const arenaMoreOptions = [
|
||||
{ label: '一键完成', key: 'complete-arena' }
|
||||
]
|
||||
```
|
||||
|
||||
### 一键完成处理
|
||||
|
||||
```javascript
|
||||
const onFishMoreSelect = (key) => {
|
||||
if (key === 'complete-fish') {
|
||||
completeMonthly('fish')
|
||||
}
|
||||
}
|
||||
|
||||
const onArenaMoreSelect = (key) => {
|
||||
if (key === 'complete-arena') {
|
||||
completeMonthly('arena')
|
||||
}
|
||||
}
|
||||
|
||||
const completeMonthly = async (type) => {
|
||||
const isFish = type === 'fish'
|
||||
const target = isFish ? FISH_TARGET : ARENA_TARGET
|
||||
const current = isFish ? fishNum.value : arenaNum.value
|
||||
const remaining = target - current
|
||||
|
||||
if (remaining <= 0) {
|
||||
message.info(`${isFish ? '钓鱼' : '竞技场'}已完成全部目标`)
|
||||
return
|
||||
}
|
||||
|
||||
// 直接补齐到满额
|
||||
if (isFish) {
|
||||
await topUpFish(remaining)
|
||||
} else {
|
||||
await topUpArena(remaining)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI组件代码
|
||||
|
||||
### 月度任务面板
|
||||
|
||||
```vue
|
||||
<div class="status-card monthly-tasks" v-show="activeSection === 'activity'">
|
||||
<div class="card-header">
|
||||
<img src="/icons/1736425783912140.png" alt="月度任务" class="status-icon">
|
||||
<div class="status-info">
|
||||
<h3>月度任务</h3>
|
||||
<p>进度与一键补齐</p>
|
||||
</div>
|
||||
<div class="status-badge" :class="{ active: monthHasData }">
|
||||
<div class="status-dot" />
|
||||
<span v-if="remainingDays > 0">剩余 {{ remainingDays }} 天</span>
|
||||
<span v-else>本月最后一天</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<!-- 钓鱼进度 -->
|
||||
<div class="monthly-row">
|
||||
<div class="row-title">钓鱼进度</div>
|
||||
<div class="row-value">
|
||||
{{ fishNum }} / {{ FISH_TARGET }}({{ fishPercent }}%)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 竞技场进度 -->
|
||||
<div class="monthly-row">
|
||||
<div class="row-title">竞技场进度</div>
|
||||
<div class="row-value">
|
||||
{{ arenaNum }} / {{ ARENA_TARGET }}({{ arenaPercent }}%)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="action-row">
|
||||
<!-- 刷新按钮 -->
|
||||
<button
|
||||
class="action-button secondary"
|
||||
:disabled="monthLoading || fishToppingUp || arenaToppingUp"
|
||||
@click="fetchMonthlyActivity"
|
||||
>
|
||||
{{ monthLoading ? '刷新中...' : '刷新进度' }}
|
||||
</button>
|
||||
|
||||
<!-- 钓鱼补齐 -->
|
||||
<n-button-group>
|
||||
<n-button
|
||||
class="action-button"
|
||||
:disabled="monthLoading || fishToppingUp"
|
||||
@click="topUpMonthly('fish')"
|
||||
>
|
||||
{{ fishToppingUp ? '补齐中...' : '钓鱼补齐' }}
|
||||
</n-button>
|
||||
<n-dropdown
|
||||
:options="fishMoreOptions"
|
||||
trigger="click"
|
||||
@select="onFishMoreSelect"
|
||||
>
|
||||
<n-button :disabled="monthLoading || fishToppingUp">▾</n-button>
|
||||
</n-dropdown>
|
||||
</n-button-group>
|
||||
|
||||
<!-- 竞技场补齐 -->
|
||||
<n-button-group>
|
||||
<n-button
|
||||
class="action-button"
|
||||
:disabled="monthLoading || arenaToppingUp"
|
||||
@click="topUpMonthly('arena')"
|
||||
>
|
||||
{{ arenaToppingUp ? '补齐中...' : '竞技场补齐' }}
|
||||
</n-button>
|
||||
<n-dropdown
|
||||
:options="arenaMoreOptions"
|
||||
trigger="click"
|
||||
@select="onArenaMoreSelect"
|
||||
>
|
||||
<n-button :disabled="monthLoading || arenaToppingUp">▾</n-button>
|
||||
</n-dropdown>
|
||||
</n-button-group>
|
||||
</div>
|
||||
|
||||
<!-- 补齐说明 -->
|
||||
<p class="description muted">
|
||||
补齐规则:让"当前天数比例"和"完成比例"一致;
|
||||
若无剩余天数则按满额({{FISH_TARGET}}/{{ARENA_TARGET}})计算。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📡 相关游戏命令
|
||||
|
||||
### 1. 获取月度任务信息
|
||||
```javascript
|
||||
command: 'activity_get'
|
||||
params: {}
|
||||
response: {
|
||||
activity: {
|
||||
myMonthInfo: {
|
||||
'2': { num: 150 } // 钓鱼完成次数
|
||||
},
|
||||
myArenaInfo: {
|
||||
num: 80 // 竞技场完成次数
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 钓鱼
|
||||
```javascript
|
||||
command: 'fishing_fish'
|
||||
params: {
|
||||
fishingType: 1 // 1=普通鱼竿, 2=金鱼竿
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 竞技场匹配对手
|
||||
```javascript
|
||||
command: 'arena_matchopponent'
|
||||
params: {}
|
||||
response: {
|
||||
opponent: {
|
||||
roleId: 12345,
|
||||
name: '对手名称',
|
||||
power: 1000000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 竞技场战斗
|
||||
```javascript
|
||||
command: 'arena_battle'
|
||||
params: {
|
||||
targetRoleId: 12345,
|
||||
battleType: 1 // 1=普通战斗
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 领取月度任务奖励
|
||||
```javascript
|
||||
command: 'monthlyactivity_receivereward'
|
||||
params: {
|
||||
rewardId: 1 // 奖励ID
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置和优化
|
||||
|
||||
### 1. 错误处理
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// 执行任务
|
||||
} catch (error) {
|
||||
// 1. 记录错误
|
||||
console.error('月度任务执行失败:', error)
|
||||
|
||||
// 2. 用户提示
|
||||
message.error(`操作失败:${error.message}`)
|
||||
|
||||
// 3. 状态恢复
|
||||
fishToppingUp.value = false
|
||||
arenaToppingUp.value = false
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 请求间隔
|
||||
|
||||
```javascript
|
||||
// 避免请求过快
|
||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
// 钓鱼间隔:500ms
|
||||
await sleep(500)
|
||||
|
||||
// 战斗间隔:1000ms
|
||||
await sleep(1000)
|
||||
```
|
||||
|
||||
### 3. 超时设置
|
||||
|
||||
```javascript
|
||||
// 短请求:5秒超时
|
||||
await tokenStore.sendMessageWithPromise(tokenId, cmd, params, 5000)
|
||||
|
||||
// 长请求:10秒超时
|
||||
await tokenStore.sendMessageWithPromise(tokenId, cmd, params, 10000)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 数据流图
|
||||
|
||||
```
|
||||
用户点击"刷新进度"
|
||||
↓
|
||||
fetchMonthlyActivity()
|
||||
↓
|
||||
发送 'activity_get' 命令
|
||||
↓
|
||||
接收响应并解析
|
||||
↓
|
||||
更新 monthActivity
|
||||
↓
|
||||
自动计算各项指标
|
||||
↓
|
||||
UI显示最新进度
|
||||
|
||||
---
|
||||
|
||||
用户点击"钓鱼补齐"
|
||||
↓
|
||||
topUpMonthly('fish')
|
||||
↓
|
||||
计算需要补齐的次数
|
||||
↓
|
||||
topUpFish(needed)
|
||||
↓
|
||||
获取当前鱼竿数量
|
||||
↓
|
||||
计算使用策略
|
||||
↓
|
||||
执行钓鱼操作(循环)
|
||||
↓
|
||||
刷新进度
|
||||
↓
|
||||
完成提示
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 实现检查清单
|
||||
|
||||
集成月度任务系统时,请确保:
|
||||
|
||||
- [ ] 添加月度任务状态变量
|
||||
- [ ] 实现进度计算逻辑
|
||||
- [ ] 实现钓鱼补齐功能
|
||||
- [ ] 实现竞技场补齐功能
|
||||
- [ ] 实现刷新进度功能
|
||||
- [ ] 实现一键完成功能
|
||||
- [ ] 添加物品数量解析函数
|
||||
- [ ] 添加UI组件
|
||||
- [ ] 添加相关游戏命令
|
||||
- [ ] 错误处理和用户提示
|
||||
- [ ] 测试各种边界情况
|
||||
- [ ] 优化请求间隔
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### Q1: 补齐数量不准确
|
||||
**A**: 检查monthProgress的计算,确保使用向上取整(Math.ceil)
|
||||
|
||||
### Q2: 鱼竿数量解析失败
|
||||
**A**: 检查items数据结构,可能需要适配不同的服务端格式
|
||||
|
||||
### Q3: 竞技场体力不足
|
||||
**A**: 在topUpArena中已经处理,会根据当前体力调整战斗次数
|
||||
|
||||
### Q4: 补齐操作卡住
|
||||
**A**: 检查WebSocket连接状态,确保sendMessageWithPromise正常工作
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
月度任务系统是一个完整的自动化功能模块,包含:
|
||||
- ✅ 智能进度计算
|
||||
- ✅ 资源优先级策略
|
||||
- ✅ 自动补齐执行
|
||||
- ✅ 友好的用户界面
|
||||
- ✅ 完善的错误处理
|
||||
|
||||
这个系统大大提升了玩家完成月度任务的效率!
|
||||
|
||||
Reference in New Issue
Block a user