Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion assets/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,8 @@
"enable_switch_ap100": "是否切换100体爬塔御魂",
"ap100_group_team": "100体组号,队伍号",
"enable_switch_ap100_by_name": "是否通过ocr切换100体御魂",
"ap100_group_team_name": "100体组名,队伍名"
"ap100_group_team_name": "100体组名,队伍名",
"card_star": "星级",
"get_flower": "启用收取等级奖励"

}
14 changes: 13 additions & 1 deletion tasks/KekkaiActivation/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,17 @@
class CardType(str, Enum):
FISH = '斗鱼'
TAIKO = '太鼓'



class CardStar(str, Enum):
SIX = '6星'
FIVE = '5星'
#FOUR = '4星'
#THREE = '3星'
# 四五星识图不稳暂时去掉
def to_int(self) -> int:
return int(self.value[0])


class ActivationScheduler(Scheduler):
priority: int = Field(default=2, description='priority_help')
Expand All @@ -21,6 +31,8 @@ class ActivationScheduler(Scheduler):

class ActivationConfig(BaseModel):
card_type: CardType = Field(default=CardType.TAIKO, description='card_rule_help')
card_star: CardStar = Field(default=CardStar.SIX, description='6星:六星及以下、5星:五星及以下')
swipe_retry_limit: int = Field(default=10, description='最多滑动X次后触发未找到符合条件的卡')
min_taiko_num: int = Field(default=8, description='挂卡太鼓每小时最少收益,低于则不挂卡')
min_fish_num: int = Field(default=16, description='挂卡斗鱼每小时最少收益,低于则不挂卡')
exchange_before: bool = Field(default=True, description='exchange_before_help')
Expand Down
242 changes: 181 additions & 61 deletions tasks/KekkaiActivation/script_task.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# This Python file uses the following encoding: utf-8
# @author runhey
# github https://github.com/runhey
from typing import Dict, List, Tuple, Optional, Any, Union
import numpy as np
import time

import random
Expand All @@ -21,10 +23,9 @@
from tasks.KekkaiUtilize.utils import CardClass
from tasks.KekkaiActivation.assets import KekkaiActivationAssets
from tasks.KekkaiActivation.utils import parse_rule
from tasks.KekkaiActivation.config import ActivationConfig
from tasks.KekkaiActivation.config import ActivationConfig, CardType, CardStar
from tasks.Utils.config_enum import ShikigamiClass
from tasks.GameUi.page import page_main, page_guild
from tasks.KekkaiActivation.config import CardType

""" 结界挂卡 """
class ScriptTask(KU, KekkaiActivationAssets):
Expand Down Expand Up @@ -71,17 +72,121 @@ def dict_card_image(self) -> dict:
@cached_property
def dict_image_card(self) -> dict:
return {v: k for k, v in self.dict_card_image.items()}

@cached_property
def order_targets(self) -> ImageGrid:
rule = self.config.kekkai_activation.activation_config.card_type
if rule == CardType.TAIKO:
return ImageGrid([self.I_CARDS_KAIKO_6, self.I_CARDS_KAIKO_5])
elif rule == CardType.FISH:
return ImageGrid([self.I_CARDS_FISH_6, self.I_CARDS_FISH_5])
else:
logger.error('Unknown utilize rule')
raise ValueError('Unknown utilize rule')
def star_mapping(self) -> dict:
"""根据配置的星级返回目标卡和干扰卡的图片列表"""
card_star = self.config.kekkai_activation.activation_config.card_star
target_star = card_star.to_int() # 获取目标星级数值

# 定义卡片列表
cards = {
CardType.TAIKO: [
(6, self.I_CARDS_KAIKO_6),
(5, self.I_CARDS_KAIKO_5),
(4, self.I_CARDS_KAIKO_4),
(3, self.I_CARDS_KAIKO_3),
],
CardType.FISH: [
(6, self.I_CARDS_FISH_6),
(5, self.I_CARDS_FISH_5),
(4, self.I_CARDS_FISH_4),
(3, self.I_CARDS_FISH_3),
]
}

result = {'target_images': {}, 'higher_images': {}}

for card_type, card_list in cards.items():
# 目标卡:星级 ≤ 目标星级
result['target_images'][card_type] = [
card for star, card in card_list if star <= target_star
]
# 干扰卡:星级 > 目标星级
result['higher_images'][card_type] = [
card for star, card in card_list if star > target_star
]

return result

def _select_best_card_from_results(
self,
filtered_results: List[Any],
min_card_num: int,
ocr_count: int
) -> Optional[RuleClick]:
"""
从过滤后的OCR结果中选择最佳卡片
:param filtered_results: 过滤后的OCR结果列表
:param min_card_num: 最低收益阈值
:param ocr_count: 当前OCR计数
:return: 选中的RuleClick对象,或None
"""
logger.info(f"第{ocr_count}次滑动后识别到卡: {[result.ocr_text for result in filtered_results]}")

# 提取数字并按数字排序
numeric_results = []
for result in filtered_results:
numbers = [int(num) for num in re.findall(r'\d+', result.ocr_text)]
if numbers:
if numbers[0] < min_card_num:
logger.debug(f"卡牌收益 {numbers[0]} 低于阈值 {min_card_num},跳过")
continue
numeric_results.append((numbers[0], result))

if numeric_results:
# 按数字大到小排序
sorted_results = [result for _, result in sorted(numeric_results, key=lambda x: x[0], reverse=True)]
max_result = sorted_results[0]

return self._create_click_target(max_result)

return None


def _create_click_target(self, ocr_result: Any) -> RuleClick:
"""
创建点击目标
:param ocr_result: OCR结果对象
:return: RuleClick对象
"""
box = ocr_result.box
x_min = self.O_CHECK_CARD_NUMBER.roi[0] + box[0][0]
y_min = self.O_CHECK_CARD_NUMBER.roi[1] + box[0][1]
width = box[1][0] - box[0][0]
height = box[2][1] - box[1][1]
roi = int(x_min), int(y_min), int(width), int(height)

return RuleClick(roi_front=roi, roi_back=roi, name="tmpclick")

def _perform_swipe_action(self, current_ocr_count: int, swipe_limit: int, reason: str = "未找到符合条件的卡") -> Tuple[bool, int]:
"""
执行滑动操作

:param current_ocr_count: 当前OCR计数
:param swipe_limit: 滑动次数限制
:param reason: 滑动原因,用于日志记录
:return: (是否成功, 新的OCR计数)
"""
new_ocr_count = current_ocr_count + 1

# 检查是否达到滑动上限
if new_ocr_count >= swipe_limit:
logger.warning(f'已达到滑动次数上限({swipe_limit}),{reason}')
return False, new_ocr_count

# 执行滑动操作
logger.info(f"{reason},准备第{new_ocr_count}次滑动")
duration = 2
safe_pos_x = random.randint(200, 400)
safe_pos_y = random.randint(580, 600)
p1 = (safe_pos_x, safe_pos_y)
p2 = (safe_pos_x, safe_pos_y - 410)
logger.info('Swipe %s -> %s, %sS ' % (point2str(*p1), point2str(*p2), duration))
self.device.swipe_adb(p1, p2, duration=duration)
time.sleep(1)
self.screenshot() # 确保截图更新
return True, new_ocr_count

def run_activation(self, _config: ActivationConfig) -> bool:
"""
Expand Down Expand Up @@ -236,66 +341,81 @@ def screening_card(self, rule: str):
continue

def check_card_num(self):
rule = self.config.kekkai_activation.activation_config.card_type
con = self.config.kekkai_activation.activation_config
rule = con.card_type
card_star = con.card_star
swipe_limit = con.swipe_retry_limit

if rule == CardType.TAIKO:
min_card_num = self.config.kekkai_activation.activation_config.min_taiko_num
min_card_num = con.min_taiko_num
check_card = "勾玉"
elif rule == CardType.FISH:
min_card_num = self.config.kekkai_activation.activation_config.min_fish_num
min_card_num = con.min_fish_num
check_card = "体力"
else:
logger.error('Unknown utilize rule')
raise ValueError('Unknown utilize rule')


# 获取目标卡和干扰卡图片列表
star_mapping = self.star_mapping
target_images = star_mapping['target_images'].get(rule, [])
higher_images = star_mapping['higher_images'].get(rule, [])

ocr_count = 0
while 1:
has_ever_seen_target = False

while True:
self.screenshot()
results = self.O_CHECK_CARD_NUMBER.detect_and_ocr(self.device.image)
ocr_count += 1
# 第一步:筛选出包含 "体力或者勾玉" 的结果
filtered_results = [result for result in results if check_card in result.ocr_text]
logger.info(f"识别到卡: {[result.ocr_text for result in filtered_results]}")

# 第二步:提取数字并按数字排序
numeric_results = []
for result in filtered_results:
# 使用正则表达式提取所有数字
numbers = [int(num) for num in re.findall(r'\d+', result.ocr_text)]
if numbers: # 如果提取到数字
if numbers[0] < min_card_num:

# 如果从未见过目标卡,检查当前页面是否有目标卡
if not has_ever_seen_target:
for target_image in target_images:
if self.appear(target_image):
has_ever_seen_target = True
logger.info(f'首次检测到目标卡: {target_image.name}')
break

if has_ever_seen_target:
# 检查是否有干扰卡(适用于所有有干扰卡的模式)
has_higher = False
for higher_image in higher_images:
if self.appear(higher_image):
has_higher = True
logger.info(f'检测到干扰卡: {higher_image.name}')
break

if has_higher:
logger.info(f'{card_star.value}模式下检测到干扰卡,跳过当前页')
success, ocr_count = self._perform_swipe_action(
ocr_count, swipe_limit, "跳过当前页"
)
if not success:
return None
continue
numeric_results.append((numbers[0], result)) # 按第一个数字排序

if numeric_results:
# 按数字大到小排序
sorted_results = [result for _, result in sorted(numeric_results, key=lambda x: x[0], reverse=True)]
max_result = sorted_results[0] # 获取数字最大的结果对象

box = max_result.box # 获取边界框坐标
x_min = self.O_CHECK_CARD_NUMBER.roi[0] + box[0][0]
y_min = self.O_CHECK_CARD_NUMBER.roi[1] + box[0][1]
width = box[1][0] - box[0][0]
height = box[2][1] - box[1][1]
roi = int(x_min), int(y_min), int(width), int(height)

target = RuleClick(roi_front=roi, roi_back=roi, name="tmpclick")
logger.info(f"选择挂卡: [{max_result.ocr_text}] {roi}")
else:
# 没有找到目标卡,执行常规滑动
success, ocr_count = self._perform_swipe_action(ocr_count, swipe_limit, "未找到目标卡,滑动")
if not success:
return None
continue

return target
else:
if ocr_count > 3:
logger.error('多次未找到符合条件的结果, 退出')
return None
logger.warning("未找到符合条件的结果, 准备往上滑动")
duration = 2
safe_pos_x = random.randint(200, 400)
safe_pos_y = random.randint(580, 600)
p1 = (safe_pos_x, safe_pos_y)
p2 = (safe_pos_x, safe_pos_y - 410)
logger.info('Swipe %s -> %s, %sS ' % (point2str(*p1), point2str(*p2), duration))
self.device.swipe_adb(p1, p2, duration=duration)
time.sleep(1)
continue
# 常规OCR识别逻辑
results = self.O_CHECK_CARD_NUMBER.detect_and_ocr(self.device.image)

# 执行常规OCR检查
filtered_results = [result for result in results if check_card in result.ocr_text]

# 从结果中选择最佳卡片
result = self._select_best_card_from_results(filtered_results, min_card_num, ocr_count)
if result:
logger.info(f"选择挂卡: [{result.name}]")
return result

# 使用滑动函数处理常规滑动
success, ocr_count = self._perform_swipe_action(ocr_count, swipe_limit)
if not success:
return None
continue

def _card_not_found(self):
# 获取配置引用
Expand Down
10 changes: 6 additions & 4 deletions tasks/TalismanPass/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ class TalismanPassAssets:
# 领取全部
I_TP_GET_ALL = RuleImage(roi_front=(903,599,70,71), roi_back=(903,599,70,71), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_tp_get_all.png")
# 任务 的右上方红点
I_RED_POINT_TASK = RuleImage(roi_front=(1226,312,23,24), roi_back=(1226,312,23,24), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_task.png")
I_RED_POINT_TASK = RuleImage(roi_front=(1218,286,45,45), roi_back=(1218,286,45,45), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_task.png")
# 今日 的右上方红点
I_RED_POINT_DAY = RuleImage(roi_front=(632,157,23,26), roi_back=(632,157,23,26), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_day.png")
I_RED_POINT_DAY = RuleImage(roi_front=(633,153,35,35), roi_back=(633,153,35,35), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_day.png")
# 本周 的右上方红点
I_RED_POINT_WEEK = RuleImage(roi_front=(795,156,24,25), roi_back=(795,156,24,25), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_week.png")
I_RED_POINT_WEEK = RuleImage(roi_front=(795,153,35,35), roi_back=(795,153,35,35), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_week.png")
# 本月 的右上方红点(截点用)
I_RED_POINT_MONTH = RuleImage(roi_front=(966,162,17,17), roi_back=(966,162,17,17), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_month.png")
# 等级奖励
I_RED_POINT_LEVEL = RuleImage(roi_front=(1222,174,21,22), roi_back=(1214,165,40,43), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_level.png")
I_RED_POINT_LEVEL = RuleImage(roi_front=(1215,154,45,45), roi_back=(1215,154,45,45), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_red_point_level.png")
# 选择一号奖励
I_TP_LEVEL_1 = RuleImage(roi_front=(203,435,122,59), roi_back=(203,435,122,59), threshold=0.8, method="Template matching", file="./tasks/TalismanPass/tp/tp_tp_level_1.png")
# 选择二号奖励
Expand Down
1 change: 1 addition & 0 deletions tasks/TalismanPass/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class LevelReward(str, Enum):
THREE = '体力/樱饼'

class TalismanConfig(BaseModel):
get_flower: bool = Field(default=False, description='收取花合战等级奖励')
level_reward: LevelReward = Field(default=LevelReward.TWO)
harvest_soul: bool = Field(default=False, description='收获1500签御魂')

Expand Down
10 changes: 8 additions & 2 deletions tasks/TalismanPass/script_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ def run(self):
self.goto_page(page_daily)
con: TalismanConfig = self.config.talisman_pass.talisman

# 收取全部奖励
# 收取任务全部奖励
if self.in_task():
self.get_all()
# 收取花合战等级奖励
self.get_flower(con.level_reward)
if con.get_flower:
self.get_flower(con.level_reward)
# 收取1500签御魂
if con.harvest_soul:
self.goto_page(page_main)
Expand Down Expand Up @@ -91,6 +92,11 @@ def in_task(self) -> bool:
self.screenshot()
if self.appear(self.I_TP_GOTO) or self.appear(self.I_TP_EXP):
return True
if self.appear(self.I_RED_POINT_TASK):
self.click(self.I_RED_POINT_TASK)
logger.info('Appear task reward')
return True
logger.info('No any task reward')
return False

def harvest_soul(self):
Expand Down
Loading