Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0b0ba56
更新活动 (#763)
wess09 Aug 13, 2026
1f7cff4
fix(campaign): 低心情撤退后切换后续活动任务
a2893005741 Aug 14, 2026
f9c7900
fix(campaign): 修正低心情撤退流程
a2893005741 Aug 14, 2026
cd23d6c
fix(campaign): 完整退出低心情准备页面
a2893005741 Aug 14, 2026
5e786aa
refactor(campaign): 配置化低心情任务切换
a2893005741 Aug 14, 2026
7395fc0
fix(emotion): 预留下一次出击心情消耗
a2893005741 Aug 14, 2026
5968218
fix(map): 兼容非战斗任务撤退
a2893005741 Aug 14, 2026
0c297f6
fix(emotion): 集中情绪状态更新
a2893005741 Aug 14, 2026
857deff
Dev (#775)
Beatrice-betty Aug 15, 2026
d412866
fix(campaign): 主线低心情撤退后切换后续任务
a2893005741 Aug 16, 2026
5a98c4f
fix(withdraw): 移除撤退结算固定等待
a2893005741 Aug 16, 2026
bfb927d
test(campaign): 撤销低心情撤退测试文件
a2893005741 Aug 16, 2026
4a3de48
fix(campaign): 跳过禁用的低心情后继任务
a2893005741 Aug 16, 2026
af503a7
fix(campaign): 非系列战役返回空后继任务
a2893005741 Aug 16, 2026
3497fcf
Merge branch 'wess09:master' into agent/low-emotion-withdraw-task-chain
a2893005741 Aug 17, 2026
aff5d80
fix(webui): 空仪表盘分组不再导致概览异常
a2893005741 Aug 17, 2026
7afb056
chore(git): 忽略本地低心情撤退补丁目录
a2893005741 Aug 17, 2026
9484dbd
chore(assets): 移除不再保留的界面和地图资源
a2893005741 Aug 17, 2026
e28ed78
ci(git-over-cdn): 缺少凭据时跳过未配置发布通道
a2893005741 Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/git-over-cdn-123pan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ on:
description: "Remote folder name under parent folder"
required: false
default: "AzurPilot_master"
push:
branches:
- master
# 123Pan 需要单独的 ClientID/ClientSecret,未配置时不参与日常自动发布。

jobs:
build-and-upload:
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/git-over-cdn-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,35 @@ concurrency:
cancel-in-progress: true

jobs:
check-cloudflare-config:
runs-on: ubuntu-latest
permissions: {}
outputs:
configured: ${{ steps.detect.outputs.configured }}

steps:
- name: Detect publish credentials
id: detect
env:
ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
WORKER_NAME: ${{ secrets.CF_WORKER_NAME }}
run: |
set -euo pipefail
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] || {
[ -n "$ACCOUNT_ID" ] &&
[ -n "$API_TOKEN" ] &&
[ -n "$WORKER_NAME" ];
}; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "Cloudflare Secrets 未配置,跳过本次自动发布。"
fi

build-upload-release:
needs: check-cloudflare-config
if: needs.check-cloudflare-config.outputs.configured == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/git-over-cdn-ssh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ on:
description: "How many old commits to build packs for"
required: false
default: "15"
push:
branches:
- master
# SSH 发布需要独立服务器和私钥,未配置时不参与日常自动发布。

jobs:
build-and-upload:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,4 @@ cursor-helper/**/review/reports/files/

AGENTS.md
RTK.md
/低心情撤退修复补丁/
Binary file removed assets/gui/css/bj.jpg
Binary file not shown.
Binary file removed assets/map_detection/os_globe_map.png
Binary file not shown.
Binary file removed doc/macGUI.png
Binary file not shown.
18 changes: 18 additions & 0 deletions module/campaign/campaign_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ class CampaignBase(CampaignUI, Map, AutoSearchCombat):
"""
FUNCTION_NAME_BASE = 'battle_'
MAP: CampaignMap
low_emotion_withdrawn = False

def handle_combat_low_emotion(self):
"""处理战役中的低心情强制出击提示。

仅在“计算心情消耗”模式下取消提示并撤退;其他心情设置维持原行为。
"""
if self.config.Emotion_Mode != 'calculate':
return super().handle_combat_low_emotion()

if self.handle_popup_cancel('IGNORE_LOW_EMOTION'):
logger.hr('低心情撤退')
logger.info('[低心情] 已取消强制出击,退出战斗准备后撤退当前地图')
self.low_emotion_withdrawn = True
# 不能在战斗准备页内嵌撤退循环;由 CampaignRun 退出此页面后统一撤退。
raise CampaignEnd('LowEmotionWithdraw')

return False

def battle_default(self):
"""默认战斗策略:清除所有敌人。
Expand Down
10 changes: 7 additions & 3 deletions module/campaign/gems_farming.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,18 @@ def handle_exp_info(self):
return False
if super().handle_exp_info():
return True
wait_for_transition = not getattr(self, '_withdraw_result_processing', False)
if self.appear_then_click(EXP_INFO_C, threshold=10):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(EXP_INFO_D):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(OPTS_INFO_D, offset=True, similarity=0.9):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
return False

Expand Down
84 changes: 84 additions & 0 deletions module/campaign/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from module.shop.shop_status import ShopStatus
from module.campaign.campaign_ui import MODE_SWITCH_1
from module.config.config import AzurLaneConfig
from module.config.task_priority import parse_task_priority
from module.exception import CampaignEnd, RequestHumanTakeover, ScriptEnd
from module.handler.fast_forward import map_files, to_map_file_name
from module.logger import logger
Expand Down Expand Up @@ -418,6 +419,83 @@ def after_campaign_run(self):
"""单次战役完成后的扩展钩子。"""
pass

def get_low_emotion_next_campaign_tasks(self, task):
"""从任务优先级配置中获取当前战役系列的后续任务。"""
task_prefix = next(
(
prefix
for prefix in ('Event', 'Main')
if task == prefix or (task.startswith(prefix) and task[len(prefix):].isdigit())
),
None,
)
if task_prefix is None:
return []

campaign_tasks = [
candidate
for candidate in parse_task_priority(self.config.SCHEDULER_PRIORITY)
if candidate == task_prefix
or (candidate.startswith(task_prefix) and candidate[len(task_prefix):].isdigit())
]
try:
return campaign_tasks[campaign_tasks.index(task) + 1:]
except ValueError:
return []

def handle_low_emotion_withdrawal(self):
"""延后因低心情撤退的当前任务,并切换到后续同系列战役图。"""
if not getattr(self.campaign, 'low_emotion_withdrawn', False):
return False

task = self.config.task.command
try:
# 低心情弹窗可能出现在战斗准备页。先退出该页面,回到地图后再撤退。
self.campaign.withdraw(skip_first_screenshot=False)
except CampaignEnd:
pass

emotion = self.campaign.emotion
emotion.update()
if emotion.using_public:
public_fleet = getattr(emotion, 'public_fleet', None)
fleets = [public_fleet] if public_fleet is not None else []
else:
# 初次出击时地图还未初始化,无法可靠判断是哪一队触发弹窗。
# 双舰队配置下保守地延后两队,避免按错误舰队提前重试。
fleets = list(getattr(emotion, 'fleets', []))
if not self.config.FLEET_2:
fleets = fleets[:1]

if not fleets:
logger.critical('[低心情] 未找到舰队心情记录,无法安全延后当前任务')
raise RequestHumanTakeover

# 游戏已显示低心情强制出击弹窗,说明本地记录的心情值已经失真。
# 不能继续用过高的旧值(例如 75)计算,否则会把当前任务排回现在。
for fleet in fleets:
fleet.current = 0
emotion.record()
emotion.show()
recovered = emotion.get_recovered_for_battle(self.campaign._map_battle)

fleet_names = ', '.join(str(fleet.fleet) for fleet in fleets)
logger.info(
f'[低心情] 游戏端低心情,{task} 的 {fleet_names} 队按 0 心情恢复至 {recovered}'
)
self.config.task_delay(target=recovered)

# 后继同系列战役图由用户的任务优先级配置决定,跳过用户禁用的任务。
# 所有后继图不可调用时,最后一张图交由调度器处理。
for next_task in self.get_low_emotion_next_campaign_tasks(task):
if self.config.task_call(next_task, force_call=False):
logger.info(f'[低心情] {task} 已撤退,立即切换到 {next_task}')
self.config.update()
break

self.campaign.low_emotion_withdrawn = False
return True

def handle_commission_notice(self):
"""
检查委托通知。如果发现委托完成,停止当前任务并调用委托处理。
Expand Down Expand Up @@ -511,6 +589,9 @@ def run(self, name, folder='campaign_main', mode='normal', total=0):
self.device.click_record_clear()
try:
self.campaign.run()
except CampaignEnd:
if not getattr(self.campaign, 'low_emotion_withdrawn', False):
raise
except ScriptEnd as e:
logger.hr('脚本结束')
logger.info(str(e))
Expand All @@ -519,6 +600,9 @@ def run(self, name, folder='campaign_main', mode='normal', total=0):
self.config.Scheduler_Enable = False
break

if self.handle_low_emotion_withdrawal():
self.config.task_stop('[低心情] 已撤退并延后当前任务')

# 更新配置
if len(self.campaign.config.modified):
logger.info('[战役-运行] 更新仪表盘配置')
Expand Down
27 changes: 17 additions & 10 deletions module/combat/combat.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,42 +497,43 @@ def handle_battle_status(self, drop=None):
"""
if self.is_combat_executing():
return False
wait_for_transition = not getattr(self, '_withdraw_result_processing', False)
if self.appear(BATTLE_STATUS_S, interval=self.battle_status_click_interval):
if drop:
drop.handle_add(self)
else:
elif wait_for_transition:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_S)
return True
if self.appear(BATTLE_STATUS_A, interval=self.battle_status_click_interval):
logger.warning('[战斗-结算] 战斗评价 A')
if drop:
drop.handle_add(self)
else:
elif wait_for_transition:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_A)
return True
if self.appear(BATTLE_STATUS_B, interval=self.battle_status_click_interval):
logger.warning('[战斗-结算] 战斗评价 B')
if drop:
drop.handle_add(self)
else:
elif wait_for_transition:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_B)
return True
if self.appear(BATTLE_STATUS_C, interval=self.battle_status_click_interval):
logger.warning('[战斗-结算] 战斗评价 C')
if drop:
drop.handle_add(self)
else:
elif wait_for_transition:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_C)
return True
if self.appear(BATTLE_STATUS_D, interval=self.battle_status_click_interval):
logger.warning('[战斗-结算] 战斗评价 D')
if drop:
drop.handle_add(self)
else:
elif wait_for_transition:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_D)
return True
Expand Down Expand Up @@ -587,20 +588,26 @@ def handle_exp_info(self):
"""
if self.is_combat_executing():
return False
wait_for_transition = not getattr(self, '_withdraw_result_processing', False)
if self.appear_then_click(EXP_INFO_S):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(EXP_INFO_A):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(EXP_INFO_B):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(EXP_INFO_C):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(EXP_INFO_D):
self.device.sleep((0.25, 0.5))
if wait_for_transition:
self.device.sleep((0.25, 0.5))
return True

return False
Expand Down
23 changes: 9 additions & 14 deletions module/combat/emotion.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,23 +336,16 @@ def reduce_per_battle_before_entering(self):
def reduce_shipwreck(self):
return 10

def _check_reduce(self, battle):
"""检查战斗带来的情绪减少
def get_recovered_for_battle(self, battle):
"""计算完成下一次战役所需的情绪恢复时间

Returns:
recovered (datetime): 预期恢复时间。
delay (bool): 是否需要延迟。
datetime: 扣除下一次战役的预计情绪后达到控制阈值的时间。
"""
if self.using_public:
reduce = battle * self.reduce_per_battle_before_entering
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
logger.info(f'[情绪-检查] 预期情绪扣减: {reduce}')

self.update()
self.record()
self.show()
recovered = self.public_fleet.get_recovered(reduce)
delay = recovered > current_time()
return recovered, delay
return self.public_fleet.get_recovered(reduce)

method = self.config.Fleet_FleetOrder

Expand All @@ -369,13 +362,15 @@ def _check_reduce(self, battle):

battle = tuple(np.array(battle) * self.reduce_per_battle_before_entering)
logger.info(f'[情绪-检查] 预期情绪扣减: {battle}')
return max([f.get_recovered(b) for f, b in zip(self.fleets, battle)])

def _check_reduce(self, battle):
"""检查战斗带来的情绪减少。"""
self.update()
self.record()
self.show()
recovered = max([f.get_recovered(b) for f, b in zip(self.fleets, battle)])
delay = recovered > current_time()
return recovered, delay
recovered = self.get_recovered_for_battle(battle)
return recovered, recovered > current_time()

def check_reduce(self, battle):
"""进入战役前检查情绪。
Expand Down
Loading
Loading