Merge pull request #113 from Zebartin/dev

Add: Event assignments (Space Station Task Force)
This commit is contained in:
LmeSzinc 2023-09-27 22:38:21 +08:00 committed by GitHub
commit 28081361d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 827 additions and 144 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -139,6 +139,7 @@
"Name_3": "The_Invisible_Hand",
"Name_4": "Nine_Billion_Names",
"Duration": 20,
"Event": true,
"Assignment": {}
}
},

View File

@ -200,6 +200,8 @@ def generate_code():
output = os.path.join(path, 'assets')
os.makedirs(output, exist_ok=True)
for prev in iter_folder(output, ext='.py'):
if os.path.basename(prev) == '__init__.py':
continue
os.remove(prev)
for module, module_data in all.items():

View File

@ -1,7 +1,6 @@
import os
import re
import typing as t
from collections import namedtuple
from functools import cached_property
from module.base.code_generator import CodeGenerator
@ -254,14 +253,19 @@ class KeywordExtract:
self.clear_keywords()
def generate_assignment_keywords(self):
KeywordFromFile = namedtuple('KeywordFromFile', ('file', 'class_name', 'output_file'))
for keyword in (
KeywordFromFile('ExpeditionGroup.json', 'AssignmentGroup', './tasks/assignment/keywords/group.py'),
KeywordFromFile('ExpeditionData.json', 'AssignmentEntry', './tasks/assignment/keywords/entry.py')
self.load_keywords(['空间站特派'])
self.write_keywords(
keyword_class='AssignmentEventGroup',
output_file='./tasks/assignment/keywords/event_group.py'
)
for file_name, class_name, output_file in (
('ExpeditionGroup.json', 'AssignmentGroup', './tasks/assignment/keywords/group.py'),
('ExpeditionData.json', 'AssignmentEntry', './tasks/assignment/keywords/entry.py'),
('ActivityExpedition.json', 'AssignmentEventEntry', './tasks/assignment/keywords/event_entry.py'),
):
file = os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', keyword.file)
file = os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', file_name)
self.load_keywords(deep_get(data, 'Name.Hash') for data in read_file(file).values())
self.write_keywords(keyword_class=keyword.class_name, output_file=keyword.output_file)
self.write_keywords(keyword_class=class_name, output_file=output_file)
def generate_map_planes(self):
planes = {

View File

@ -1046,6 +1046,10 @@
20
]
},
"Event": {
"type": "checkbox",
"value": true
},
"Assignment": {
"type": "stored",
"value": {},

View File

@ -176,6 +176,7 @@ Assignment:
Duration:
value: 20
option: [ 4, 8, 12, 20 ]
Event: true
Assignment:
stored: StoredAssignment
order: 3

View File

@ -106,6 +106,7 @@ class GeneratedConfig:
Assignment_Name_3 = 'The_Invisible_Hand' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity
Assignment_Name_4 = 'Nine_Billion_Names' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity
Assignment_Duration = 20 # 4, 8, 12, 20
Assignment_Event = True
Assignment_Assignment = {}
# Group `ItemStorage`

View File

@ -728,6 +728,10 @@
"12": "12 Hours",
"20": "20 Hours"
},
"Event": {
"name": "Complete Event Assignments First",
"help": ""
},
"Assignment": {
"name": "Assign.",
"help": ""

View File

@ -728,6 +728,10 @@
"12": "12 horas",
"20": "20 horas"
},
"Event": {
"name": "Assignment.Event.name",
"help": ""
},
"Assignment": {
"name": "Encargo",
"help": ""

View File

@ -728,6 +728,10 @@
"12": "12",
"20": "20"
},
"Event": {
"name": "Assignment.Event.name",
"help": ""
},
"Assignment": {
"name": "Assignment.Assignment.name",
"help": "Assignment.Assignment.help"

View File

@ -728,6 +728,10 @@
"12": "12小时",
"20": "20小时"
},
"Event": {
"name": "有活动时优先做活动委托",
"help": ""
},
"Assignment": {
"name": "委托",
"help": ""

View File

@ -728,6 +728,10 @@
"12": "12小時",
"20": "20小時"
},
"Event": {
"name": "有活動時優先做活動委託",
"help": ""
},
"Assignment": {
"name": "委託",
"help": ""

View File

@ -375,17 +375,17 @@ class Duration(Ocr):
def timedelta_regex(cls, lang):
regex_str = {
'cn': r'^(?P<prefix>.*?)'
r'((?P<days>\d{1,2})天)?'
r'((?P<hours>\d{1,2})小时)?'
r'((?P<minutes>\d{1,2})分钟)?'
r'((?P<seconds>\d{1,2})秒)?'
r'$',
r'((?P<days>\d{1,2})\s*\s*)?'
r'((?P<hours>\d{1,2})\s*小时\s*)?'
r'((?P<minutes>\d{1,2})\s*分钟\s*)?'
r'((?P<seconds>\d{1,2})\s*秒)?'
r'(?P<suffix>[^天时钟秒]*?)$',
'en': r'^(?P<prefix>.*?)'
r'((?P<days>\d{1,2})\s*d\s*)?'
r'((?P<hours>\d{1,2})\s*h\s*)?'
r'((?P<minutes>\d{1,2})\s*m\s*)?'
r'((?P<seconds>\d{1,2})\s*s)?'
r'$'
r'(?P<suffix>[^dhms]*?)$'
}[lang]
return re.compile(regex_str)

View File

@ -0,0 +1,18 @@
from module.config.server import VALID_LANG
from tasks.assignment.assets.assets_assignment_ui import (
CHARACTER_MATERIALS_CHECK, CHARACTER_MATERIALS_CLICK,
EXP_MATERIALS_CREDITS_CHECK, EXP_MATERIALS_CREDITS_CLICK, GROUP_SEARCH,
SPACE_STATION_TASK_FORCE_CHECK, SPACE_STATION_TASK_FORCE_CLICK,
SYNTHESIS_MATERIALS_CHECK, SYNTHESIS_MATERIALS_CLICK)
for group_button_wrapper in (
SPACE_STATION_TASK_FORCE_CHECK, SPACE_STATION_TASK_FORCE_CLICK,
CHARACTER_MATERIALS_CHECK, CHARACTER_MATERIALS_CLICK,
EXP_MATERIALS_CREDITS_CHECK, EXP_MATERIALS_CREDITS_CLICK,
SYNTHESIS_MATERIALS_CHECK, SYNTHESIS_MATERIALS_CLICK,
):
for lang in VALID_LANG:
button = getattr(group_button_wrapper, lang, None)
if button is None:
continue
button.search = GROUP_SEARCH.button

View File

@ -43,3 +43,20 @@ REDISPATCH = ButtonWrapper(
button=(779, 592, 905, 629),
),
)
REPORT = ButtonWrapper(
name='REPORT',
cn=Button(
file='./assets/cn/assignment/claim/REPORT.png',
area=(537, 80, 742, 128),
search=(517, 60, 762, 148),
color=(102, 90, 68),
button=(537, 80, 742, 128),
),
en=Button(
file='./assets/en/assignment/claim/REPORT.png',
area=(393, 83, 885, 137),
search=(373, 63, 905, 157),
color=(71, 63, 49),
button=(393, 83, 885, 137),
),
)

View File

@ -8,14 +8,14 @@ ASSIGNMENT_START = ButtonWrapper(
cn=Button(
file='./assets/cn/assignment/dispatch/ASSIGNMENT_START.png',
area=(581, 321, 699, 349),
search=(561, 301, 719, 369),
search=(573, 299, 707, 412),
color=(93, 84, 66),
button=(581, 321, 699, 349),
),
en=Button(
file='./assets/en/assignment/dispatch/ASSIGNMENT_START.png',
area=(679, 323, 784, 347),
search=(659, 303, 804, 367),
search=(669, 297, 794, 416),
color=(93, 83, 65),
button=(679, 323, 784, 347),
),
@ -24,10 +24,10 @@ ASSIGNMENT_STARTED_CHECK = ButtonWrapper(
name='ASSIGNMENT_STARTED_CHECK',
share=Button(
file='./assets/share/assignment/dispatch/ASSIGNMENT_STARTED_CHECK.png',
area=(1174, 297, 1211, 514),
search=(1154, 277, 1231, 534),
color=(86, 81, 78),
button=(1174, 297, 1211, 514),
area=(542, 412, 1156, 422),
search=(522, 392, 1176, 442),
color=(232, 230, 226),
button=(542, 412, 1156, 422),
),
)
CHARACTER_1 = ButtonWrapper(
@ -87,6 +87,43 @@ CHARACTER_LIST = ButtonWrapper(
button=(91, 163, 136, 180),
),
)
CHARACTER_SUPPORT = ButtonWrapper(
name='CHARACTER_SUPPORT',
share=Button(
file='./assets/share/assignment/dispatch/CHARACTER_SUPPORT.png',
area=(103, 212, 435, 302),
search=(83, 192, 455, 322),
color=(69, 65, 80),
button=(103, 212, 435, 302),
),
)
CHARACTER_SUPPORT_LIST = ButtonWrapper(
name='CHARACTER_SUPPORT_LIST',
cn=Button(
file='./assets/cn/assignment/dispatch/CHARACTER_SUPPORT_LIST.png',
area=(91, 166, 171, 186),
search=(71, 146, 191, 206),
color=(147, 146, 143),
button=(91, 166, 171, 186),
),
en=Button(
file='./assets/en/assignment/dispatch/CHARACTER_SUPPORT_LIST.png',
area=(90, 167, 267, 189),
search=(70, 147, 287, 209),
color=(169, 168, 165),
button=(90, 167, 267, 189),
),
)
CHARACTER_SUPPORT_SELECTED = ButtonWrapper(
name='CHARACTER_SUPPORT_SELECTED',
share=Button(
file='./assets/share/assignment/dispatch/CHARACTER_SUPPORT_SELECTED.png',
area=(190, 270, 266, 295),
search=(170, 250, 286, 315),
color=(39, 39, 39),
button=(190, 270, 266, 295),
),
)
CONFIRM_ASSIGNMENT = ButtonWrapper(
name='CONFIRM_ASSIGNMENT',
cn=Button(
@ -149,8 +186,18 @@ EMPTY_SLOT = ButtonWrapper(
share=Button(
file='./assets/share/assignment/dispatch/EMPTY_SLOT.png',
area=(1075, 562, 1110, 597),
search=(1054, 542, 1220, 616),
search=(873, 542, 1136, 608),
color=(200, 200, 195),
button=(1075, 562, 1110, 597),
),
)
EMPTY_SLOT_SUPPORT = ButtonWrapper(
name='EMPTY_SLOT_SUPPORT',
share=Button(
file='./assets/share/assignment/dispatch/EMPTY_SLOT_SUPPORT.png',
area=(1152, 561, 1187, 592),
search=(1132, 541, 1207, 612),
color=(203, 202, 198),
button=(1152, 561, 1187, 592),
),
)

View File

@ -3,14 +3,38 @@ from module.base.button import Button, ButtonWrapper
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.button_extract ```
CHARACTER_MATERIALS = ButtonWrapper(
name='CHARACTER_MATERIALS',
share=Button(
file='./assets/share/assignment/ui/CHARACTER_MATERIALS.png',
area=(146, 91, 255, 124),
search=(126, 71, 275, 144),
color=(213, 213, 208),
button=(146, 91, 255, 124),
CHARACTER_MATERIALS_CHECK = ButtonWrapper(
name='CHARACTER_MATERIALS_CHECK',
cn=Button(
file='./assets/cn/assignment/ui/CHARACTER_MATERIALS_CHECK.png',
area=(346, 97, 421, 117),
search=(326, 77, 441, 137),
color=(177, 176, 173),
button=(346, 97, 421, 117),
),
en=Button(
file='./assets/en/assignment/ui/CHARACTER_MATERIALS_CHECK.png',
area=(339, 88, 429, 126),
search=(319, 68, 449, 146),
color=(193, 192, 189),
button=(339, 88, 429, 126),
),
)
CHARACTER_MATERIALS_CLICK = ButtonWrapper(
name='CHARACTER_MATERIALS_CLICK',
cn=Button(
file='./assets/cn/assignment/ui/CHARACTER_MATERIALS_CLICK.png',
area=(347, 97, 421, 117),
search=(327, 77, 441, 137),
color=(60, 60, 60),
button=(347, 97, 421, 117),
),
en=Button(
file='./assets/en/assignment/ui/CHARACTER_MATERIALS_CLICK.png',
area=(339, 88, 429, 127),
search=(319, 68, 449, 147),
color=(49, 49, 49),
button=(339, 88, 429, 127),
),
)
DISPATCHED = ButtonWrapper(
@ -40,14 +64,58 @@ ENTRY_LOADED = ButtonWrapper(
button=(467, 235, 498, 619),
),
)
EXP_MATERIALS_CREDITS = ButtonWrapper(
name='EXP_MATERIALS_CREDITS',
EXP_MATERIALS_CREDITS_CHECK = ButtonWrapper(
name='EXP_MATERIALS_CREDITS_CHECK',
cn=Button(
file='./assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png',
area=(514, 97, 614, 117),
search=(494, 77, 634, 137),
color=(178, 177, 174),
button=(514, 97, 614, 117),
),
en=Button(
file='./assets/en/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png',
area=(529, 88, 599, 126),
search=(509, 68, 619, 146),
color=(202, 201, 198),
button=(529, 88, 599, 126),
),
)
EXP_MATERIALS_CREDITS_CLICK = ButtonWrapper(
name='EXP_MATERIALS_CREDITS_CLICK',
cn=Button(
file='./assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png',
area=(514, 97, 614, 117),
search=(494, 77, 634, 137),
color=(61, 60, 60),
button=(514, 97, 614, 117),
),
en=Button(
file='./assets/en/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png',
area=(528, 88, 599, 127),
search=(508, 68, 619, 147),
color=(42, 42, 42),
button=(528, 88, 599, 127),
),
)
GROUP_SEARCH = ButtonWrapper(
name='GROUP_SEARCH',
share=Button(
file='./assets/share/assignment/ui/EXP_MATERIALS_CREDITS.png',
area=(310, 85, 447, 134),
search=(290, 65, 467, 154),
color=(214, 214, 210),
button=(310, 85, 447, 134),
file='./assets/share/assignment/ui/GROUP_SEARCH.png',
area=(111, 76, 835, 140),
search=(91, 56, 855, 160),
color=(82, 79, 77),
button=(111, 76, 835, 140),
),
)
OCR_ASSIGNMENT_ENTRY_LIST = ButtonWrapper(
name='OCR_ASSIGNMENT_ENTRY_LIST',
share=Button(
file='./assets/share/assignment/ui/OCR_ASSIGNMENT_ENTRY_LIST.png',
area=(133, 139, 494, 620),
search=(113, 119, 514, 640),
color=(201, 199, 193),
button=(133, 139, 494, 620),
),
)
OCR_ASSIGNMENT_LIMIT = ButtonWrapper(
@ -60,16 +128,6 @@ OCR_ASSIGNMENT_LIMIT = ButtonWrapper(
button=(1095, 95, 1180, 119),
),
)
OCR_ASSIGNMENT_LIST = ButtonWrapper(
name='OCR_ASSIGNMENT_LIST',
share=Button(
file='./assets/share/assignment/ui/OCR_ASSIGNMENT_LIST.png',
area=(133, 139, 494, 620),
search=(113, 119, 514, 640),
color=(201, 199, 193),
button=(133, 139, 494, 620),
),
)
OCR_ASSIGNMENT_TIME = ButtonWrapper(
name='OCR_ASSIGNMENT_TIME',
share=Button(
@ -80,13 +138,71 @@ OCR_ASSIGNMENT_TIME = ButtonWrapper(
button=(605, 564, 886, 589),
),
)
SYNTHESIS_MATERIALS = ButtonWrapper(
name='SYNTHESIS_MATERIALS',
share=Button(
file='./assets/share/assignment/ui/SYNTHESIS_MATERIALS.png',
area=(521, 91, 603, 128),
search=(501, 71, 623, 148),
color=(208, 208, 203),
button=(521, 91, 603, 128),
SPACE_STATION_TASK_FORCE_CHECK = ButtonWrapper(
name='SPACE_STATION_TASK_FORCE_CHECK',
cn=Button(
file='./assets/cn/assignment/ui/SPACE_STATION_TASK_FORCE_CHECK.png',
area=(157, 97, 249, 117),
search=(137, 77, 269, 137),
color=(181, 181, 178),
button=(157, 97, 249, 117),
),
en=Button(
file='./assets/en/assignment/ui/SPACE_STATION_TASK_FORCE_CHECK.png',
area=(176, 88, 239, 126),
search=(156, 68, 259, 146),
color=(198, 197, 194),
button=(176, 88, 239, 126),
),
)
SPACE_STATION_TASK_FORCE_CLICK = ButtonWrapper(
name='SPACE_STATION_TASK_FORCE_CLICK',
cn=Button(
file='./assets/cn/assignment/ui/SPACE_STATION_TASK_FORCE_CLICK.png',
area=(157, 97, 249, 117),
search=(137, 77, 269, 137),
color=(71, 70, 68),
button=(157, 97, 249, 117),
),
en=Button(
file='./assets/en/assignment/ui/SPACE_STATION_TASK_FORCE_CLICK.png',
area=(176, 88, 239, 126),
search=(156, 68, 259, 146),
color=(60, 58, 56),
button=(176, 88, 239, 126),
),
)
SYNTHESIS_MATERIALS_CHECK = ButtonWrapper(
name='SYNTHESIS_MATERIALS_CHECK',
cn=Button(
file='./assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CHECK.png',
area=(708, 97, 783, 117),
search=(688, 77, 803, 137),
color=(180, 179, 176),
button=(708, 97, 783, 117),
),
en=Button(
file='./assets/en/assignment/ui/SYNTHESIS_MATERIALS_CHECK.png',
area=(703, 88, 790, 126),
search=(683, 68, 810, 146),
color=(189, 188, 185),
button=(703, 88, 790, 126),
),
)
SYNTHESIS_MATERIALS_CLICK = ButtonWrapper(
name='SYNTHESIS_MATERIALS_CLICK',
cn=Button(
file='./assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png',
area=(709, 97, 783, 117),
search=(689, 77, 803, 137),
color=(68, 66, 65),
button=(709, 97, 783, 117),
),
en=Button(
file='./assets/en/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png',
area=(702, 88, 790, 126),
search=(682, 68, 810, 146),
color=(61, 59, 58),
button=(702, 88, 790, 126),
),
)

View File

@ -1,17 +1,10 @@
from datetime import datetime
from module.logger import logger
from module.ocr.ocr import Duration
from tasks.assignment.assets.assets_assignment_claim import CLAIM
from tasks.assignment.assets.assets_assignment_ui import (
DISPATCHED,
OCR_ASSIGNMENT_TIME,
)
from tasks.assignment.claim import AssignmentClaim
from tasks.assignment.keywords import (
AssignmentEntry,
KEYWORDS_ASSIGNMENT_GROUP,
)
from tasks.assignment.keywords import (KEYWORDS_ASSIGNMENT_GROUP,
AssignmentEntry, AssignmentEventGroup)
from tasks.assignment.ui import AssignmentStatus
from tasks.base.page import page_assignment, page_menu
from tasks.battle_pass.keywords import KEYWORD_BATTLE_PASS_QUEST
from tasks.daily.keywords import KEYWORDS_DAILY_QUEST
@ -19,7 +12,7 @@ from tasks.daily.synthesize import SynthesizeUI
class Assignment(AssignmentClaim, SynthesizeUI):
def run(self, assignments: list[AssignmentEntry] = None, duration: int = None):
def run(self, assignments: list[AssignmentEntry] = None, duration: int = None, event_first: bool = None):
self.config.update_battle_pass_quests()
self.config.update_daily_quests()
@ -35,14 +28,26 @@ class Assignment(AssignmentClaim, SynthesizeUI):
'There are duplicate assignments in config, check it out')
if duration is None:
duration = self.config.Assignment_Duration
if event_first is None:
event_first = self.config.Assignment_Event
self.dispatched = dict()
self.has_new_dispatch = False
self.ensure_scroll_top(page_menu)
self.ui_ensure(page_assignment)
event_ongoing = next((
g for g in self._iter_groups()
if isinstance(g, AssignmentEventGroup)
), None)
if event_first and event_ongoing is not None:
undispatched = assignments
remain = self._check_all()
remain = self._dispatch_event(remain)
else:
# Iterate in user-specified order, return undispatched ones
undispatched = list(self._check_inlist(assignments, duration))
remain = self._check_all()
undispatched = [x for x in undispatched if x not in self.dispatched]
# There are unchecked assignments
if remain > 0:
for assignment in undispatched[:remain]:
@ -53,7 +58,8 @@ class Assignment(AssignmentClaim, SynthesizeUI):
f'{", ".join([x.name for x in undispatched[remain:]])}')
elif remain > len(undispatched):
self._dispatch_remain(duration, remain - len(undispatched))
# Refresh dashboard before return
_ = self._limit_status
# Scheduler
logger.attr('has_new_dispatch', self.has_new_dispatch)
with self.config.multi_set():
@ -61,7 +67,8 @@ class Assignment(AssignmentClaim, SynthesizeUI):
quests = self.config.stored.BattlePassTodayQuest.load_quests()
if self.has_new_dispatch:
if KEYWORD_BATTLE_PASS_QUEST.Dispatch_1_assignments in quests:
logger.info('Achieved battle pass quest Dispatch_1_assignments')
logger.info(
'Achieved battle pass quest Dispatch_1_assignments')
self.config.task_call('BattlePass')
# Check daily
quests = self.config.stored.DailyQuest.load_quests()
@ -93,21 +100,25 @@ class Assignment(AssignmentClaim, SynthesizeUI):
f'User specified assignments: {", ".join([x.name for x in assignments])}')
_, remain, _ = self._limit_status
for assignment in assignments:
if assignment in self.dispatched:
continue
if remain <= 0:
yield assignment
continue
logger.hr('Assignment inlist', level=2)
logger.info(f'Check assignment inlist: {assignment}')
self.goto_entry(assignment)
if self.appear(CLAIM):
status = self._check_assignment_status()
if status == AssignmentStatus.CLAIMABLE:
self.claim(assignment, duration, should_redispatch=True)
continue
if self.appear(DISPATCHED):
self.dispatched[assignment] = datetime.now() + Duration(
OCR_ASSIGNMENT_TIME).ocr_single_line(self.device.image)
if status == AssignmentStatus.DISPATCHED:
self.dispatched[assignment] = datetime.now() + \
self._get_assignment_time()
continue
if remain > 0:
# General assignments must be dispatchable here
self.dispatch(assignment, duration)
remain -= 1
else:
yield assignment
def _check_all(self):
"""
@ -118,28 +129,31 @@ class Assignment(AssignmentClaim, SynthesizeUI):
Break when a dispatchable assignment is encountered
"""
logger.hr('Assignment check all', level=1)
_, remain, total = self._limit_status
if total == len(self.dispatched):
current, remain, _ = self._limit_status
if current == len(self.dispatched):
return remain
for group in self._iter_groups():
self.goto_group(group)
entries = self._iter_entries()
for _ in range(len(group.entries)):
assignment = next(entries)
insight = False
for assignment in self._iter_entries():
if assignment in self.dispatched:
continue
logger.hr('Assignment all', level=2)
logger.info(f'Check assignment all: {assignment}')
self.goto_entry(assignment)
if self.appear(CLAIM):
self.goto_entry(assignment, insight)
status = self._check_assignment_status()
if status == AssignmentStatus.CLAIMABLE:
self.claim(assignment, None, should_redispatch=False)
current -= 1
remain += 1
insight = True # Order of entries change after claiming
continue
if self.appear(DISPATCHED):
self.dispatched[assignment] = datetime.now() + Duration(
OCR_ASSIGNMENT_TIME).ocr_single_line(self.device.image)
if total == len(self.dispatched):
if status == AssignmentStatus.DISPATCHED:
self.dispatched[assignment] = datetime.now() + \
self._get_assignment_time()
if current == len(self.dispatched):
return remain
insight = False # Order of entries does not change here
continue
break
return remain
@ -174,3 +188,29 @@ class Assignment(AssignmentClaim, SynthesizeUI):
remain -= 1
if remain <= 0:
return
def _dispatch_event(self, remain: int):
if remain <= 0:
return remain
logger.hr('Assignment dispatch event', level=1)
for group in self._iter_groups():
if not isinstance(group, AssignmentEventGroup):
continue
self.goto_group(group)
for assignment in self._iter_entries():
if assignment in self.dispatched:
continue
logger.hr('Assignment event', level=2)
logger.info(f'Check assignment event: {assignment}')
# Order of entries does not change during iteration
self.goto_entry(assignment, insight=False)
status = self._check_assignment_status()
# Should only be dispatchable or locked after _check_all
if status == AssignmentStatus.DISPATCHABLE:
self.dispatch(assignment, None)
remain -= 1
if remain <= 0:
return remain
continue
break
return remain

View File

@ -41,7 +41,7 @@ class AssignmentClaim(AssignmentDispatch):
"""
Pages:
in: CLAIM
out: REDISPATCH
out: REPORT
"""
skip_first_screenshot = True
while 1:
@ -50,7 +50,9 @@ class AssignmentClaim(AssignmentDispatch):
else:
self.device.screenshot()
# End
if self.appear(REDISPATCH):
# Neither CLOSE_REPORT nor REDISPATCH is shown
# If it is an EVENT assignment
if self.appear(REPORT):
logger.info('Assignment report appears')
break
# Claim rewards
@ -63,7 +65,7 @@ class AssignmentClaim(AssignmentDispatch):
should_redispatch (bool): determined by user config and duration in report
Pages:
in: CLOSE_REPORT and REDISPATCH
in: REPORT
out: page_assignment
"""
click_button = REDISPATCH if should_redispatch else CLOSE_REPORT
@ -78,7 +80,8 @@ class AssignmentClaim(AssignmentDispatch):
logger.info('Assignment report is closed')
break
# Close report
if self.appear_then_click(click_button, interval=2):
if self.appear(REPORT, interval=2):
self.device.click(click_button)
continue
def _is_duration_expected(self, duration: int) -> bool:

View File

@ -1,12 +1,37 @@
from datetime import datetime, timedelta
from module.base.base import ModuleBase
from module.base.timer import Timer
from module.config.stored.classes import now
from module.logger import logger
from module.ui.switch import Switch
from tasks.assignment.assets.assets_assignment_dispatch import *
from tasks.assignment.assets.assets_assignment_ui import DISPATCHED
from tasks.assignment.keywords import *
from tasks.assignment.ui import AssignmentSwitch, AssignmentUI
from tasks.assignment.ui import AssignmentUI
class AssignmentSwitch(Switch):
def __init__(self, name, active_color: tuple[int, int, int], is_selector=True):
super().__init__(name, is_selector)
self.active_color = active_color
def get(self, main: ModuleBase):
"""
Use image_color_count instead to determine whether the button is selected/active
Args:
main (ModuleBase):
Returns:
str: state name or 'unknown'.
"""
for data in self.state_list:
if main.image_color_count(data['check_button'], self.active_color):
return data['state']
return 'unknown'
ASSIGNMENT_DURATION_SWITCH = AssignmentSwitch(
'AssignmentDurationSwitch',
@ -22,20 +47,24 @@ class AssignmentDispatch(AssignmentUI):
dispatched: dict[AssignmentEntry, datetime] = dict()
has_new_dispatch: bool = False
def dispatch(self, assignment: AssignmentEntry, duration: int):
def dispatch(self, assignment: AssignmentEntry, duration: int | None):
"""
Dispatch assignment.
Should be called only when limit is checked
Args:
assignment (AssignmentEntry):
duration (int): user specified duration
duration (int | None): user specified duration, None for event assignments
Pages:
in: EMPTY_SLOT
out: DISPATCHED
"""
self._select_characters()
if isinstance(assignment, AssignmentEventEntry):
self._select_support()
duration = self._get_assignment_time().total_seconds() / 3600
else:
self._select_duration(duration)
self._confirm_assignment()
self._wait_until_assignment_started()
@ -63,7 +92,8 @@ class AssignmentDispatch(AssignmentUI):
logger.info('Characters are all selected')
break
# Ensure character list
if not self.appear(CHARACTER_LIST):
# Search EMPTY_SLOT to load offset
if not self.appear(CHARACTER_LIST) and self.appear(EMPTY_SLOT):
if self.interval_is_reached(CHARACTER_LIST, interval=2):
self.interval_reset(CHARACTER_LIST, interval=2)
self.device.click(EMPTY_SLOT)
@ -78,6 +108,30 @@ class AssignmentDispatch(AssignmentUI):
if not self.image_color_count(CHARACTER_2_SELECTED, (240, 240, 240)):
self.device.click(CHARACTER_2)
def _select_support(self):
skip_first_screenshot = True
self.interval_clear(
(CHARACTER_SUPPORT_LIST, CHARACTER_SUPPORT_SELECTED), interval=2)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
# End
if self.match_color(CHARACTER_SUPPORT_SELECTED):
logger.info('Support character is selected')
break
# Ensure support list
if not self.appear(CHARACTER_SUPPORT_LIST):
if self.interval_is_reached(CHARACTER_SUPPORT_LIST, interval=2):
self.interval_reset(CHARACTER_SUPPORT_LIST, interval=2)
self.device.click(EMPTY_SLOT_SUPPORT)
continue
# Select
if self.interval_is_reached(CHARACTER_SUPPORT_SELECTED, interval=2):
self.interval_reset(CHARACTER_SUPPORT_SELECTED, interval=2)
self.device.click(CHARACTER_SUPPORT)
def _select_duration(self, duration: int):
if duration not in {4, 8, 12, 20}:
logger.warning(
@ -133,6 +187,6 @@ class AssignmentDispatch(AssignmentUI):
else:
self.device.screenshot()
# End
if self.appear(ASSIGNMENT_STARTED_CHECK):
if self.match_color(ASSIGNMENT_STARTED_CHECK):
logger.info('Assignment started')
break

View File

@ -1,6 +1,8 @@
import tasks.assignment.keywords.entry as KEYWORDS_ASSIGNMENT_ENTRY
import tasks.assignment.keywords.group as KEYWORDS_ASSIGNMENT_GROUP
from tasks.assignment.keywords.classes import AssignmentEntry, AssignmentGroup
import tasks.assignment.keywords.event_entry as KEYWORDS_ASSIGNMENT_EVENT_ENTRY
import tasks.assignment.keywords.event_group as KEYWORDS_ASSIGNMENT_EVENT_GROUP
from tasks.assignment.keywords.classes import *
KEYWORDS_ASSIGNMENT_GROUP.Character_Materials.entries = (
KEYWORDS_ASSIGNMENT_ENTRY.Nine_Billion_Names,
@ -23,10 +25,37 @@ KEYWORDS_ASSIGNMENT_GROUP.Synthesis_Materials.entries = (
KEYWORDS_ASSIGNMENT_ENTRY.Legend_of_the_Puppet_Master,
KEYWORDS_ASSIGNMENT_ENTRY.The_Wages_of_Humanity,
)
KEYWORDS_ASSIGNMENT_EVENT_GROUP.Space_Station_Task_Force.entries = (
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Repulsion_Bridge_Errors,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Meal_Delivery_Robot_Check_Up,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Noise_Complaint,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Interior_Temperature_Modulator,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Researcher_Health_Reports,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Confidential_Investigation,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Borrowed_Equipment,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Booking_System,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Non_Digital_Documents,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Drip_Feed_Errors,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Pet_Movement_Route_Planning,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Curio_Distribution,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Super_Urgent_Waiting_Online,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Ventilation_Problem,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Unstable_Connection,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Chronology_Checks,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Supply_Chain_Management,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Malicious_Occupation_of_Public_Space,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Uniform_Material,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Virus_Re_creation_Report,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Abnormal_Signal,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Flexible_Working_Approval,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Lighting_Issue,
)
for group in (
KEYWORDS_ASSIGNMENT_GROUP.Character_Materials,
KEYWORDS_ASSIGNMENT_GROUP.EXP_Materials_Credits,
KEYWORDS_ASSIGNMENT_GROUP.Synthesis_Materials,
KEYWORDS_ASSIGNMENT_EVENT_GROUP.Space_Station_Task_Force,
):
for entry in group.entries:
assert entry.group is None

View File

@ -15,6 +15,19 @@ class AssignmentGroup(Keyword):
class AssignmentEntry(Keyword):
instances: ClassVar = {}
group: AssignmentGroup = None
def __hash__(self) -> int:
return super().__hash__()
@dataclass(repr=False)
class AssignmentEventGroup(AssignmentGroup):
instances: ClassVar = {}
@dataclass(repr=False)
class AssignmentEventEntry(AssignmentEntry):
instances: ClassVar = {}
def __hash__(self) -> int:
return super().__hash__()

View File

@ -0,0 +1,221 @@
from .classes import AssignmentEventEntry
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Repulsion_Bridge_Errors = AssignmentEventEntry(
id=1,
name='Repulsion_Bridge_Errors',
cn='斥力桥报错',
cht='斥力橋錯誤',
en='Repulsion Bridge Errors',
jp='斥力ブリッジエラー',
es='Reporte de error del puente de rechazo',
)
Meal_Delivery_Robot_Check_Up = AssignmentEventEntry(
id=2,
name='Meal_Delivery_Robot_Check_Up',
cn='送餐机器人检修',
cht='送餐機器人檢修',
en='Meal-Delivery Robot Check-Up',
jp='配膳ロボット点検修理',
es='Mantenimiento de los robots de reparto de comida',
)
Noise_Complaint = AssignmentEventEntry(
id=3,
name='Noise_Complaint',
cn='噪音投诉问题',
cht='噪音投訴問題',
en='Noise Complaint',
jp='騒音苦情問題',
es='Quejas por ruidos',
)
Interior_Temperature_Modulator = AssignmentEventEntry(
id=4,
name='Interior_Temperature_Modulator',
cn='室内温度调节器',
cht='室內溫度調節器',
en='Interior Temperature Modulator',
jp='室内温度調節器',
es='Regulador de temperatura ambiental',
)
Researcher_Health_Reports = AssignmentEventEntry(
id=5,
name='Researcher_Health_Reports',
cn='科员的体检报告',
cht='組員的體檢報告',
en="Researchers' Health Reports",
jp='スタッフの健康診断報告',
es='Informe médico de los investigadores',
)
Confidential_Investigation = AssignmentEventEntry(
id=6,
name='Confidential_Investigation',
cn='秘密调查行动',
cht='秘密調查行動',
en='Confidential Investigation',
jp='秘密裏の調査',
es='Investigación encubierta',
)
Borrowed_Equipment = AssignmentEventEntry(
id=7,
name='Borrowed_Equipment',
cn='实验器械借用',
cht='實驗器械借用',
en='Borrowed Equipment',
jp='実験機器借用',
es='Préstamo de instrumentos de laboratorio',
)
Booking_System = AssignmentEventEntry(
id=8,
name='Booking_System',
cn='会议室预约系统',
cht='會議室預約系統',
en='Booking System',
jp='会議室予約システム',
es='Sistema de reserva de las salas de reuniones',
)
Non_Digital_Documents = AssignmentEventEntry(
id=9,
name='Non_Digital_Documents',
cn='非电子版文件',
cht='非電子版文件',
en='Non-Digital Documents',
jp='非デジタル版ファイル',
es='Documentos no electrónicos',
)
Drip_Feed_Errors = AssignmentEventEntry(
id=10,
name='Drip_Feed_Errors',
cn='液滴系统报错',
cht='液滴系統錯誤',
en='Drip-Feed Errors',
jp='水やりシステムエラー',
es='Reporte de error del sistema de goteo',
)
Pet_Movement_Route_Planning = AssignmentEventEntry(
id=11,
name='Pet_Movement_Route_Planning',
cn='宠物行动路线规划',
cht='寵物行動路線規劃',
en='Pet Movement Route Planning',
jp='ペットの行動ルート規制',
es='Planificación de las rutas de paseo de mascotas',
)
Food_Improvement_Plan = AssignmentEventEntry(
id=12,
name='Food_Improvement_Plan',
cn='餐饮优化方案',
cht='餐飲改良方案',
en='Food Improvement Plan',
jp='飲食優良化法案',
es='Programa de mejora de comida',
)
Curio_Distribution = AssignmentEventEntry(
id=13,
name='Curio_Distribution',
cn='奇物借用问题',
cht='奇物借用問題',
en='Curio Distribution',
jp='奇物借用問題',
es='Problemas con el préstamo de objetos raros',
)
Super_Urgent_Waiting_Online = AssignmentEventEntry(
id=14,
name='Super_Urgent_Waiting_Online',
cn='来活人很急在线等',
cht='急,線上等',
en='Super Urgent, Waiting Online',
jp='緊急助っ人求むオンラインにて待つ',
es='Muy urgente, esperando en línea',
)
Ventilation_Problem = AssignmentEventEntry(
id=15,
name='Ventilation_Problem',
cn='空气流通问题',
cht='空氣流通問題',
en='Ventilation Problem',
jp='換気問題',
es='Problemas con la circulación del aire',
)
Unstable_Connection = AssignmentEventEntry(
id=16,
name='Unstable_Connection',
cn='连接不稳定问题',
cht='連線不穩定問題',
en='Unstable Connection',
jp='接続不安定問題',
es='Conexión inestable',
)
Chronology_Checks = AssignmentEventEntry(
id=17,
name='Chronology_Checks',
cn='编年史校对',
cht='編年史校對',
en='Chronology Checks',
jp='編年史校正',
es='Corrección de registros',
)
Supply_Chain_Management = AssignmentEventEntry(
id=18,
name='Supply_Chain_Management',
cn='物流供应链管理',
cht='物流供應鏈管理',
en='Supply Chain Management',
jp='物流供給路線管理',
es='Gestión de pedidos',
)
Malicious_Occupation_of_Public_Space = AssignmentEventEntry(
id=19,
name='Malicious_Occupation_of_Public_Space',
cn='公共区域被恶意侵占',
cht='公共區域被惡意侵佔',
en='Malicious Occupation of Public Space',
jp='公共区域の悪意による独占',
es='Invasión de espacios públicos',
)
Uniform_Material = AssignmentEventEntry(
id=20,
name='Uniform_Material',
cn='科室服装面料',
cht='科室服裝材質',
en='Uniform Material',
jp='スタッフ制服の素材',
es='Material del uniforme',
)
Virus_Re_creation_Report = AssignmentEventEntry(
id=21,
name='Virus_Re_creation_Report',
cn='病毒溯源报告',
cht='病毒溯源報告',
en='Virus Re-creation Report',
jp='ウイルス根源報告',
es='Informe de rastreo de virus',
)
Abnormal_Signal = AssignmentEventEntry(
id=22,
name='Abnormal_Signal',
cn='舱段信号异常',
cht='艙段訊號異常',
en='Abnormal Signal',
jp='部分の信号異常',
es='Mala señal en las cabinas',
)
Flexible_Working_Approval = AssignmentEventEntry(
id=23,
name='Flexible_Working_Approval',
cn='轮休审批流程',
cht='輪休審批流程',
en='Flexible Working Approval',
jp='交代休み審査フロー',
es='Aprobación de días de descanso',
)
Lighting_Issue = AssignmentEventEntry(
id=24,
name='Lighting_Issue',
cn='灯光照明问题',
cht='燈光照明問題',
en='Lighting Issue',
jp='照明の色問題',
es='Problemas de iluminación',
)

View File

@ -0,0 +1,14 @@
from .classes import AssignmentEventGroup
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Space_Station_Task_Force = AssignmentEventGroup(
id=1,
name='Space_Station_Task_Force',
cn='空间站特派',
cht='太空站特派',
en='Space Station Task Force',
jp='ステーション特派',
es='Comando de la Estación Espacial',
)

View File

@ -1,39 +1,27 @@
import re
from collections.abc import Iterator
from datetime import timedelta
from enum import Enum
from functools import cached_property
from typing import Iterator
from module.base.base import ModuleBase
from module.base.timer import Timer
from module.exception import ScriptError
from module.logger import logger
from module.ocr.ocr import DigitCounter, Duration, Ocr
from tasks.dungeon.ui import DungeonTabSwitch as Switch
from module.ui.draggable_list import DraggableList
from module.ui.switch import Switch
from tasks.assignment.assets.assets_assignment_claim import CLAIM
from tasks.assignment.assets.assets_assignment_dispatch import EMPTY_SLOT
from tasks.assignment.assets.assets_assignment_ui import *
from tasks.assignment.keywords import *
from tasks.base.ui import UI
class AssignmentSwitch(Switch):
def __init__(self, name, active_color: tuple[int, int, int], is_selector=True):
super().__init__(name, is_selector)
self.active_color = active_color
def get(self, main: ModuleBase):
"""
Use image_color_count instead to determine whether the button is selected/active
Args:
main (ModuleBase):
Returns:
str: state name or 'unknown'.
"""
for data in self.state_list:
if main.image_color_count(data['check_button'], self.active_color):
return data['state']
return 'unknown'
class AssignmentStatus(Enum):
CLAIMABLE = 0
DISPATCHED = 1
DISPATCHABLE = 2
LOCKED = 3
class AssignmentOcr(Ocr):
@ -50,6 +38,10 @@ class AssignmentOcr(Ocr):
(KEYWORDS_ASSIGNMENT_ENTRY.Akashic_Records.name, '阿[未][夏复]记录'),
(KEYWORDS_ASSIGNMENT_ENTRY.Legend_of_the_Puppet_Master.name, '^师传说'),
(KEYWORDS_ASSIGNMENT_ENTRY.The_Wages_of_Humanity.name, '[赠]养人类'),
],
'en': [
(KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan.name,
'Food\s*[I]{0}mprovement Plan'),
]
}
@ -63,7 +55,15 @@ class AssignmentOcr(Ocr):
def filter_detected(self, result) -> bool:
# Drop duration rows
res = Duration.timedelta_regex(self.lang).search(result.ocr_text)
return not bool(res.group('seconds'))
if res.group('hours') or res.group('seconds'):
return False
# Locked event assignments
locked_pattern = {
'cn': '解锁$',
'en': 'Locked$',
}[self.lang]
res = re.search(locked_pattern, result.ocr_text)
return not res
def after_process(self, result: str):
result = super().after_process(result)
@ -74,38 +74,57 @@ class AssignmentOcr(Ocr):
if matched is None:
return result
keyword_lang = self.lang
matched = getattr(KEYWORDS_ASSIGNMENT_ENTRY, matched.lastgroup)
for keyword_class in (
KEYWORDS_ASSIGNMENT_ENTRY,
KEYWORDS_ASSIGNMENT_EVENT_ENTRY,
):
matched = getattr(keyword_class, matched.lastgroup, None)
if matched is not None:
break
else:
raise ScriptError(f'No keyword found for {matched.lastgroup}')
matched = getattr(matched, keyword_lang)
logger.attr(name=f'{self.name} after_process',
text=f'{result} -> {matched}')
return matched
ASSIGNMENT_TOP_SWITCH = AssignmentSwitch(
'AssignmentTopSwitch',
(240, 240, 240)
ASSIGNMENT_GROUP_SWITCH = Switch(
'AssignmentGroupSwitch',
is_selector=True
)
ASSIGNMENT_TOP_SWITCH.add_state(
ASSIGNMENT_GROUP_SWITCH.add_state(
KEYWORDS_ASSIGNMENT_EVENT_GROUP.Space_Station_Task_Force,
check_button=SPACE_STATION_TASK_FORCE_CHECK,
click_button=SPACE_STATION_TASK_FORCE_CLICK
)
ASSIGNMENT_GROUP_SWITCH.add_state(
KEYWORDS_ASSIGNMENT_GROUP.Character_Materials,
check_button=CHARACTER_MATERIALS
check_button=CHARACTER_MATERIALS_CHECK,
click_button=CHARACTER_MATERIALS_CLICK
)
ASSIGNMENT_TOP_SWITCH.add_state(
ASSIGNMENT_GROUP_SWITCH.add_state(
KEYWORDS_ASSIGNMENT_GROUP.EXP_Materials_Credits,
check_button=EXP_MATERIALS_CREDITS
check_button=EXP_MATERIALS_CREDITS_CHECK,
click_button=EXP_MATERIALS_CREDITS_CLICK
)
ASSIGNMENT_TOP_SWITCH.add_state(
ASSIGNMENT_GROUP_SWITCH.add_state(
KEYWORDS_ASSIGNMENT_GROUP.Synthesis_Materials,
check_button=SYNTHESIS_MATERIALS
check_button=SYNTHESIS_MATERIALS_CHECK,
click_button=SYNTHESIS_MATERIALS_CLICK
)
ASSIGNMENT_ENTRY_LIST = DraggableList(
'AssignmentEntryList',
keyword_class=AssignmentEntry,
keyword_class=[AssignmentEntry, AssignmentEventEntry],
ocr_class=AssignmentOcr,
search_button=OCR_ASSIGNMENT_LIST,
search_button=OCR_ASSIGNMENT_ENTRY_LIST,
check_row_order=False,
active_color=(40, 40, 40)
)
ASSIGNMENT_ENTRY_LIST.known_rows = [
kw for kc in ASSIGNMENT_ENTRY_LIST.keyword_class
for kw in kc.instances.values()
]
class AssignmentUI(UI):
@ -119,14 +138,17 @@ class AssignmentUI(UI):
self.device.screenshot()
self.goto_group(KEYWORDS_ASSIGNMENT_GROUP.Character_Materials)
"""
if ASSIGNMENT_GROUP_SWITCH.get(self) == group:
return
logger.hr('Assignment group goto', level=3)
if ASSIGNMENT_TOP_SWITCH.set(group, main=self):
if ASSIGNMENT_GROUP_SWITCH.set(group, self):
self._wait_until_entry_loaded()
def goto_entry(self, entry: AssignmentEntry):
def goto_entry(self, entry: AssignmentEntry, insight: bool = True):
"""
Args:
entry (AssignmentEntry):
insight (bool): skip ocr to save time if insight is False
Examples:
self = AssignmentUI('src')
@ -143,7 +165,24 @@ class AssignmentUI(UI):
raise ScriptError(err_msg)
else:
self.goto_group(entry.group)
ASSIGNMENT_ENTRY_LIST.select_row(entry, self)
ASSIGNMENT_ENTRY_LIST.select_row(entry, self, insight=insight)
def _wait_until_group_loaded(self):
skip_first_screenshot = True
timeout = Timer(2, count=3).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
logger.warning('Wait group loaded timeout')
break
if self.image_color_count(GROUP_SEARCH, (40, 40, 40), count=25000) and \
self.image_color_count(GROUP_SEARCH, (240, 240, 240), count=8000):
logger.info('Group loaded')
break
def _wait_until_entry_loaded(self):
skip_first_screenshot = True
@ -175,8 +214,42 @@ class AssignmentUI(UI):
self.config.stored.Assignment.set(0, 0)
return current, remain, total
def _check_assignment_status(self) -> AssignmentStatus:
skip_first_screenshot = True
timeout = Timer(2, count=3).start()
ret = AssignmentStatus.LOCKED
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
logger.info(
'Check assignment status timeout, assume LOCKED'
)
break
if self.appear(CLAIM):
ret = AssignmentStatus.CLAIMABLE
break
if self.appear(DISPATCHED):
ret = AssignmentStatus.DISPATCHED
break
if self.appear(EMPTY_SLOT):
ret = AssignmentStatus.DISPATCHABLE
break
logger.attr('AssignmentStatus', ret.name)
return ret
def _get_assignment_time(self) -> timedelta:
return Duration(OCR_ASSIGNMENT_TIME).ocr_single_line(self.device.image)
def _iter_groups(self) -> Iterator[AssignmentGroup]:
for state in ASSIGNMENT_TOP_SWITCH.state_list:
self._wait_until_group_loaded()
for state in ASSIGNMENT_GROUP_SWITCH.state_list:
check = state['check_button']
click = state['click_button']
if self.appear(check) or self.appear(click):
yield state['state']
def _iter_entries(self) -> Iterator[AssignmentEntry]:
@ -184,5 +257,8 @@ class AssignmentUI(UI):
Iterate entries from top to bottom
"""
ASSIGNMENT_ENTRY_LIST.load_rows(main=self)
for button in ASSIGNMENT_ENTRY_LIST.cur_buttons:
yield button.matched_keyword
# Freeze ocr results here
yield from [
button.matched_keyword
for button in ASSIGNMENT_ENTRY_LIST.cur_buttons
]

View File

@ -61,6 +61,8 @@ class Login(UI):
# Additional
if self.handle_popup_single():
continue
if self.handle_popup_confirm():
continue
if self.ui_additional():
continue