Merge pull request #113 from Zebartin/dev
Add: Event assignments (Space Station Task Force)
BIN
assets/cn/assignment/claim/REPORT.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
assets/cn/assignment/dispatch/ASSIGNMENT_START.SEARCH.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
assets/cn/assignment/dispatch/CHARACTER_SUPPORT_LIST.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
assets/cn/assignment/ui/CHARACTER_MATERIALS_CHECK.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
assets/cn/assignment/ui/CHARACTER_MATERIALS_CLICK.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
assets/cn/assignment/ui/SPACE_STATION_TASK_FORCE_CHECK.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
assets/cn/assignment/ui/SPACE_STATION_TASK_FORCE_CLICK.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
BIN
assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/en/assignment/claim/REPORT.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
assets/en/assignment/dispatch/ASSIGNMENT_START.SEARCH.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/en/assignment/dispatch/CHARACTER_SUPPORT_LIST.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
assets/en/assignment/ui/CHARACTER_MATERIALS_CHECK.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/en/assignment/ui/CHARACTER_MATERIALS_CLICK.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
assets/en/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/en/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
assets/en/assignment/ui/SPACE_STATION_TASK_FORCE_CHECK.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
assets/en/assignment/ui/SPACE_STATION_TASK_FORCE_CLICK.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/en/assignment/ui/SYNTHESIS_MATERIALS_CHECK.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/en/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png
Normal file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 5.3 KiB |
BIN
assets/share/assignment/dispatch/CHARACTER_SUPPORT.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
assets/share/assignment/dispatch/CHARACTER_SUPPORT_SELECTED.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 39 KiB |
BIN
assets/share/assignment/dispatch/EMPTY_SLOT_SUPPORT.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 10 KiB |
BIN
assets/share/assignment/ui/GROUP_SEARCH.png
Normal file
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -139,6 +139,7 @@
|
||||
"Name_3": "The_Invisible_Hand",
|
||||
"Name_4": "Nine_Billion_Names",
|
||||
"Duration": 20,
|
||||
"Event": true,
|
||||
"Assignment": {}
|
||||
}
|
||||
},
|
||||
|
@ -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():
|
||||
|
@ -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 = {
|
||||
|
@ -1046,6 +1046,10 @@
|
||||
20
|
||||
]
|
||||
},
|
||||
"Event": {
|
||||
"type": "checkbox",
|
||||
"value": true
|
||||
},
|
||||
"Assignment": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
|
@ -176,6 +176,7 @@ Assignment:
|
||||
Duration:
|
||||
value: 20
|
||||
option: [ 4, 8, 12, 20 ]
|
||||
Event: true
|
||||
Assignment:
|
||||
stored: StoredAssignment
|
||||
order: 3
|
||||
|
@ -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`
|
||||
|
@ -728,6 +728,10 @@
|
||||
"12": "12 Hours",
|
||||
"20": "20 Hours"
|
||||
},
|
||||
"Event": {
|
||||
"name": "Complete Event Assignments First",
|
||||
"help": ""
|
||||
},
|
||||
"Assignment": {
|
||||
"name": "Assign.",
|
||||
"help": ""
|
||||
|
@ -728,6 +728,10 @@
|
||||
"12": "12 horas",
|
||||
"20": "20 horas"
|
||||
},
|
||||
"Event": {
|
||||
"name": "Assignment.Event.name",
|
||||
"help": ""
|
||||
},
|
||||
"Assignment": {
|
||||
"name": "Encargo",
|
||||
"help": ""
|
||||
|
@ -728,6 +728,10 @@
|
||||
"12": "12",
|
||||
"20": "20"
|
||||
},
|
||||
"Event": {
|
||||
"name": "Assignment.Event.name",
|
||||
"help": ""
|
||||
},
|
||||
"Assignment": {
|
||||
"name": "Assignment.Assignment.name",
|
||||
"help": "Assignment.Assignment.help"
|
||||
|
@ -728,6 +728,10 @@
|
||||
"12": "12小时",
|
||||
"20": "20小时"
|
||||
},
|
||||
"Event": {
|
||||
"name": "有活动时优先做活动委托",
|
||||
"help": ""
|
||||
},
|
||||
"Assignment": {
|
||||
"name": "委托",
|
||||
"help": ""
|
||||
|
@ -728,6 +728,10 @@
|
||||
"12": "12小時",
|
||||
"20": "20小時"
|
||||
},
|
||||
"Event": {
|
||||
"name": "有活動時優先做活動委託",
|
||||
"help": ""
|
||||
},
|
||||
"Assignment": {
|
||||
"name": "委託",
|
||||
"help": ""
|
||||
|
@ -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)
|
||||
|
||||
|
18
tasks/assignment/assets/__init__.py
Normal 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
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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__()
|
||||
|
221
tasks/assignment/keywords/event_entry.py
Normal 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',
|
||||
)
|
14
tasks/assignment/keywords/event_group.py
Normal 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',
|
||||
)
|
@ -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
|
||||
]
|
||||
|
@ -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
|
||||
|
||||
|