Compare commits
3 Commits
f872e30d1f
...
d104728e64
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d104728e64 | ||
|
|
2a1e7686f0 | ||
|
|
78c12a21dc |
BIN
assets/en/auto_mission/LAYOUT.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/en/auto_mission/PRESET1_ON.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/en/auto_mission/PRESET2_ON.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/en/auto_mission/PRESET3_ON.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/en/auto_mission/PRESET4_ON.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/en/auto_mission/PRESET_FIRST_ITEM.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
assets/en/auto_mission/PRESET_LIST.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
assets/en/auto_mission/PRESET_WINDOW.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/en/auto_mission/RECEIVED_CHEST.BUTTON.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/en/auto_mission/RECEIVED_CHEST.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/zht/auto_mission/LAYOUT.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/zht/auto_mission/PRESET1_OFF.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/zht/auto_mission/PRESET1_ON.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/zht/auto_mission/PRESET2_OFF.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/zht/auto_mission/PRESET2_ON.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/zht/auto_mission/PRESET3_OFF.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/zht/auto_mission/PRESET3_ON.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/zht/auto_mission/PRESET4_OFF.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/zht/auto_mission/PRESET4_ON.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/zht/auto_mission/PRESET_FIRST_ITEM.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
assets/zht/auto_mission/PRESET_LIST.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/zht/auto_mission/PRESET_WINDOW.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/zht/auto_mission/RECEIVED_CHEST.BUTTON.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/zht/auto_mission/RECEIVED_CHEST.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
@ -57,24 +57,25 @@
|
||||
"ServerUpdate": "04:00"
|
||||
},
|
||||
"Formation": {
|
||||
"burst1": 1,
|
||||
"burst2": 4,
|
||||
"pierce1": 2,
|
||||
"pierce2": 4,
|
||||
"mystic1": 3,
|
||||
"mystic2": 4
|
||||
"burst1": "1-1",
|
||||
"burst2": "1-2",
|
||||
"pierce1": "1-3",
|
||||
"pierce2": "1-4",
|
||||
"mystic1": "1-5",
|
||||
"mystic2": "2-1",
|
||||
"Substitute": false
|
||||
},
|
||||
"ManualBoss": {
|
||||
"Enable": false
|
||||
},
|
||||
"Normal": {
|
||||
"Enable": false,
|
||||
"Area": 4,
|
||||
"Area": null,
|
||||
"Completion": "clear"
|
||||
},
|
||||
"Hard": {
|
||||
"Enable": false,
|
||||
"Area": 6,
|
||||
"Area": null,
|
||||
"Completion": "clear"
|
||||
}
|
||||
},
|
||||
|
||||
@ -274,64 +274,32 @@
|
||||
},
|
||||
"Formation": {
|
||||
"burst1": {
|
||||
"type": "select",
|
||||
"value": 1,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
"type": "input",
|
||||
"value": "1-1"
|
||||
},
|
||||
"burst2": {
|
||||
"type": "select",
|
||||
"value": 4,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
"type": "input",
|
||||
"value": "1-2"
|
||||
},
|
||||
"pierce1": {
|
||||
"type": "select",
|
||||
"value": 2,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
"type": "input",
|
||||
"value": "1-3"
|
||||
},
|
||||
"pierce2": {
|
||||
"type": "select",
|
||||
"value": 4,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
"type": "input",
|
||||
"value": "1-4"
|
||||
},
|
||||
"mystic1": {
|
||||
"type": "select",
|
||||
"value": 3,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
"type": "input",
|
||||
"value": "1-5"
|
||||
},
|
||||
"mystic2": {
|
||||
"type": "select",
|
||||
"value": 4,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
"type": "input",
|
||||
"value": "2-1"
|
||||
},
|
||||
"Substitute": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"ManualBoss": {
|
||||
@ -346,8 +314,8 @@
|
||||
"value": false
|
||||
},
|
||||
"Area": {
|
||||
"type": "input",
|
||||
"value": 4
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
},
|
||||
"Completion": {
|
||||
"type": "select",
|
||||
@ -364,8 +332,8 @@
|
||||
"value": false
|
||||
},
|
||||
"Area": {
|
||||
"type": "input",
|
||||
"value": 6
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
},
|
||||
"Completion": {
|
||||
"type": "select",
|
||||
|
||||
@ -243,38 +243,31 @@ TacticalChallengeShop:
|
||||
"15": false
|
||||
|
||||
Formation:
|
||||
burst1:
|
||||
value: 1
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
burst2:
|
||||
value: 4
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
pierce1:
|
||||
value: 2
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
pierce2:
|
||||
value: 4
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
mystic1:
|
||||
value: 3
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
mystic2:
|
||||
value: 4
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
burst1: 1-1
|
||||
burst2: 1-2
|
||||
pierce1: 1-3
|
||||
pierce2: 1-4
|
||||
mystic1: 1-5
|
||||
mystic2: 2-1
|
||||
Substitute: false
|
||||
|
||||
ManualBoss:
|
||||
Enable: false
|
||||
|
||||
Normal:
|
||||
Enable: false
|
||||
Area: 4
|
||||
Area:
|
||||
value: null
|
||||
type: textarea
|
||||
Completion:
|
||||
value: clear
|
||||
option: [ clear, three_stars]
|
||||
|
||||
Hard:
|
||||
Enable: false
|
||||
Area: 6
|
||||
Area:
|
||||
value: null
|
||||
type: textarea
|
||||
Completion:
|
||||
value: clear
|
||||
option: [ clear, three_stars, three_stars_chest]
|
||||
|
||||
@ -160,24 +160,25 @@ class GeneratedConfig:
|
||||
TacticalChallengeShop_15 = False
|
||||
|
||||
# Group `Formation`
|
||||
Formation_burst1 = 1 # 1, 2, 3, 4
|
||||
Formation_burst2 = 4 # 1, 2, 3, 4
|
||||
Formation_pierce1 = 2 # 1, 2, 3, 4
|
||||
Formation_pierce2 = 4 # 1, 2, 3, 4
|
||||
Formation_mystic1 = 3 # 1, 2, 3, 4
|
||||
Formation_mystic2 = 4 # 1, 2, 3, 4
|
||||
Formation_burst1 = '1-1'
|
||||
Formation_burst2 = '1-2'
|
||||
Formation_pierce1 = '1-3'
|
||||
Formation_pierce2 = '1-4'
|
||||
Formation_mystic1 = '1-5'
|
||||
Formation_mystic2 = '2-1'
|
||||
Formation_Substitute = False
|
||||
|
||||
# Group `ManualBoss`
|
||||
ManualBoss_Enable = False
|
||||
|
||||
# Group `Normal`
|
||||
Normal_Enable = False
|
||||
Normal_Area = 4
|
||||
Normal_Area = None
|
||||
Normal_Completion = 'clear' # clear, three_stars
|
||||
|
||||
# Group `Hard`
|
||||
Hard_Enable = False
|
||||
Hard_Area = 6
|
||||
Hard_Area = None
|
||||
Hard_Completion = 'clear' # clear, three_stars, three_stars_chest
|
||||
|
||||
# Group `ItemStorage`
|
||||
|
||||
@ -827,55 +827,35 @@
|
||||
"Formation": {
|
||||
"_info": {
|
||||
"name": "Formation",
|
||||
"help": "Select the unit for each type"
|
||||
"help": "AAS will choose the unit from the Preset List in the Layout after entering the Unit Formation Page. The format is preset-row, for example 1-1 means choose preset 1 row 1. Please make sure the presets are set to their original names (1,2,3,4). It is highly recommended that you set them up to be unique."
|
||||
},
|
||||
"burst1": {
|
||||
"name": "Explosive 1",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": ""
|
||||
},
|
||||
"burst2": {
|
||||
"name": "Explosive 2",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": ""
|
||||
},
|
||||
"pierce1": {
|
||||
"name": "Piercing 1",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": ""
|
||||
},
|
||||
"pierce2": {
|
||||
"name": "Piercing 2",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": ""
|
||||
},
|
||||
"mystic1": {
|
||||
"name": "Mystic 1",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": ""
|
||||
},
|
||||
"mystic2": {
|
||||
"name": "Mystic 2",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": ""
|
||||
},
|
||||
"Substitute": {
|
||||
"name": "Find Alternatives",
|
||||
"help": "In case you set the preset for some types to be the same and the stage requires all of those types, AAS will try to find alternatives. For example, if you set Explosive 1 and Explosive 2 to be the same and the stage requires both of them, AAS will replace Explosive 2 with Piercing 1."
|
||||
}
|
||||
},
|
||||
"ManualBoss": {
|
||||
@ -899,7 +879,7 @@
|
||||
},
|
||||
"Area": {
|
||||
"name": "Area",
|
||||
"help": ""
|
||||
"help": "Currently only between 4 and 16. Use > to connect multiple areas. Example: 6 > 7 > 8"
|
||||
},
|
||||
"Completion": {
|
||||
"name": "Completion level",
|
||||
@ -919,7 +899,7 @@
|
||||
},
|
||||
"Area": {
|
||||
"name": "Area",
|
||||
"help": ""
|
||||
"help": "Currently only between 6 and 16. Use > to connect multiple areas. Example: 6 > 7 > 8"
|
||||
},
|
||||
"Completion": {
|
||||
"name": "Completion level",
|
||||
|
||||
@ -831,51 +831,31 @@
|
||||
},
|
||||
"burst1": {
|
||||
"name": "Formation.burst1.name",
|
||||
"help": "Formation.burst1.help",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": "Formation.burst1.help"
|
||||
},
|
||||
"burst2": {
|
||||
"name": "Formation.burst2.name",
|
||||
"help": "Formation.burst2.help",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": "Formation.burst2.help"
|
||||
},
|
||||
"pierce1": {
|
||||
"name": "Formation.pierce1.name",
|
||||
"help": "Formation.pierce1.help",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": "Formation.pierce1.help"
|
||||
},
|
||||
"pierce2": {
|
||||
"name": "Formation.pierce2.name",
|
||||
"help": "Formation.pierce2.help",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": "Formation.pierce2.help"
|
||||
},
|
||||
"mystic1": {
|
||||
"name": "Formation.mystic1.name",
|
||||
"help": "Formation.mystic1.help",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": "Formation.mystic1.help"
|
||||
},
|
||||
"mystic2": {
|
||||
"name": "Formation.mystic2.name",
|
||||
"help": "Formation.mystic2.help",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
"help": "Formation.mystic2.help"
|
||||
},
|
||||
"Substitute": {
|
||||
"name": "Formation.Substitute.name",
|
||||
"help": "Formation.Substitute.help"
|
||||
}
|
||||
},
|
||||
"ManualBoss": {
|
||||
|
||||
@ -13,8 +13,6 @@ class ScrollSelect:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
targetind : int
|
||||
Index of the target level
|
||||
window_starty:
|
||||
Y-coordinate of the upper edge of the window
|
||||
first_item_endy:
|
||||
@ -31,7 +29,7 @@ class ScrollSelect:
|
||||
Whether to click on clickx and the last row after the sliding ends
|
||||
"""
|
||||
|
||||
def __init__(self, window_button, first_item_button, expected_button, clickx, swipeoffsetx=-100,
|
||||
def __init__(self, window_button, first_item_button, expected_button, clickx, swipeoffsetx=-100, responsey=40,
|
||||
finalclick=True) -> None:
|
||||
# TODO: Actually, only concerned about the height of one element, completely displaying the Y of the first button, completely displaying the Y of the bottom button, the number of complete elements that the window can contain, the height of the last element in the window, and the left offset and response distance.
|
||||
self.window_starty = window_button.area[1]
|
||||
@ -42,7 +40,7 @@ class ScrollSelect:
|
||||
self.clickx = clickx
|
||||
self.expected_button = expected_button
|
||||
self.swipeoffsetx = swipeoffsetx
|
||||
self.responsey = 40
|
||||
self.responsey = responsey
|
||||
self.finalclick = finalclick
|
||||
|
||||
def compute_swipe(self, main, x1, y1, distance, responsey):
|
||||
@ -59,7 +57,7 @@ class ScrollSelect:
|
||||
main.device.swipe((x1, y1), (x1, int(y1 - (distance + responsey - 4 * (1 + distance / 100)))),
|
||||
duration=1 + distance / 100)
|
||||
|
||||
def select_location(self, main, target_index) -> None:
|
||||
def select_index(self, main, target_index, clickoffsety=0) -> None:
|
||||
click_coords = main.device.click_methods.get(main.config.Emulator_ControlMethod, main.device.click_adb)
|
||||
logger.info("Scroll and select the {}-th level".format(target_index + 1))
|
||||
self.scroll_right_up(main, scrollx=self.clickx + self.swipeoffsetx)
|
||||
@ -110,8 +108,10 @@ class ScrollSelect:
|
||||
self.responsey)
|
||||
if self.finalclick:
|
||||
# Click on the last row
|
||||
clicky = (self.window_endy - self.itemheight // 2) + clickoffsety
|
||||
logger.info(clicky)
|
||||
self.run_until(main,
|
||||
lambda: click_coords(self.clickx, self.window_endy - self.itemheight // 2),
|
||||
lambda: click_coords(self.clickx, clicky),
|
||||
lambda: main.appear(self.expected_button)
|
||||
)
|
||||
|
||||
@ -183,6 +183,24 @@ ENTER_SUB = ButtonWrapper(
|
||||
button=(553, 490, 712, 538),
|
||||
),
|
||||
)
|
||||
LAYOUT = ButtonWrapper(
|
||||
name='LAYOUT',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/LAYOUT.png',
|
||||
area=(1179, 461, 1226, 504),
|
||||
search=(1159, 441, 1246, 524),
|
||||
color=(189, 198, 210),
|
||||
button=(1179, 461, 1226, 504),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/LAYOUT.png',
|
||||
area=(1179, 461, 1226, 504),
|
||||
search=(1159, 441, 1246, 524),
|
||||
color=(189, 198, 210),
|
||||
button=(1179, 461, 1226, 504),
|
||||
),
|
||||
)
|
||||
MISSION_COMPLETE = ButtonWrapper(
|
||||
name='MISSION_COMPLETE',
|
||||
jp=None,
|
||||
@ -327,6 +345,180 @@ ONE_STAR = ButtonWrapper(
|
||||
button=(148, 349, 183, 390),
|
||||
),
|
||||
)
|
||||
PRESET1_OFF = ButtonWrapper(
|
||||
name='PRESET1_OFF',
|
||||
jp=None,
|
||||
en=None,
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET1_OFF.png',
|
||||
area=(52, 143, 197, 181),
|
||||
search=(32, 123, 217, 201),
|
||||
color=(253, 253, 254),
|
||||
button=(52, 143, 197, 181),
|
||||
),
|
||||
)
|
||||
PRESET1_ON = ButtonWrapper(
|
||||
name='PRESET1_ON',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET1_ON.png',
|
||||
area=(54, 146, 200, 182),
|
||||
search=(34, 126, 220, 202),
|
||||
color=(48, 77, 115),
|
||||
button=(54, 146, 200, 182),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET1_ON.png',
|
||||
area=(54, 146, 200, 182),
|
||||
search=(34, 126, 220, 202),
|
||||
color=(48, 77, 115),
|
||||
button=(54, 146, 200, 182),
|
||||
),
|
||||
)
|
||||
PRESET2_OFF = ButtonWrapper(
|
||||
name='PRESET2_OFF',
|
||||
jp=None,
|
||||
en=None,
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET2_OFF.png',
|
||||
area=(214, 144, 355, 180),
|
||||
search=(194, 124, 375, 200),
|
||||
color=(252, 253, 253),
|
||||
button=(214, 144, 355, 180),
|
||||
),
|
||||
)
|
||||
PRESET2_ON = ButtonWrapper(
|
||||
name='PRESET2_ON',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET2_ON.png',
|
||||
area=(211, 147, 355, 182),
|
||||
search=(191, 127, 375, 202),
|
||||
color=(50, 78, 116),
|
||||
button=(211, 147, 355, 182),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET2_ON.png',
|
||||
area=(211, 147, 355, 182),
|
||||
search=(191, 127, 375, 202),
|
||||
color=(50, 78, 116),
|
||||
button=(211, 147, 355, 182),
|
||||
),
|
||||
)
|
||||
PRESET3_OFF = ButtonWrapper(
|
||||
name='PRESET3_OFF',
|
||||
jp=None,
|
||||
en=None,
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET3_OFF.png',
|
||||
area=(369, 143, 516, 186),
|
||||
search=(349, 123, 536, 206),
|
||||
color=(251, 252, 252),
|
||||
button=(369, 143, 516, 186),
|
||||
),
|
||||
)
|
||||
PRESET3_ON = ButtonWrapper(
|
||||
name='PRESET3_ON',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET3_ON.png',
|
||||
area=(365, 146, 517, 184),
|
||||
search=(345, 126, 537, 204),
|
||||
color=(49, 78, 116),
|
||||
button=(365, 146, 517, 184),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET3_ON.png',
|
||||
area=(365, 146, 517, 184),
|
||||
search=(345, 126, 537, 204),
|
||||
color=(49, 78, 116),
|
||||
button=(365, 146, 517, 184),
|
||||
),
|
||||
)
|
||||
PRESET4_OFF = ButtonWrapper(
|
||||
name='PRESET4_OFF',
|
||||
jp=None,
|
||||
en=None,
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET4_OFF.png',
|
||||
area=(527, 146, 675, 183),
|
||||
search=(507, 126, 695, 203),
|
||||
color=(252, 252, 253),
|
||||
button=(527, 146, 675, 183),
|
||||
),
|
||||
)
|
||||
PRESET4_ON = ButtonWrapper(
|
||||
name='PRESET4_ON',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET4_ON.png',
|
||||
area=(528, 148, 676, 178),
|
||||
search=(508, 128, 696, 198),
|
||||
color=(50, 78, 117),
|
||||
button=(528, 148, 676, 178),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET4_ON.png',
|
||||
area=(528, 148, 676, 178),
|
||||
search=(508, 128, 696, 198),
|
||||
color=(50, 78, 117),
|
||||
button=(528, 148, 676, 178),
|
||||
),
|
||||
)
|
||||
PRESET_FIRST_ITEM = ButtonWrapper(
|
||||
name='PRESET_FIRST_ITEM',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET_FIRST_ITEM.png',
|
||||
area=(28, 184, 1252, 397),
|
||||
search=(8, 164, 1272, 417),
|
||||
color=(214, 224, 231),
|
||||
button=(28, 184, 1252, 397),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET_FIRST_ITEM.png',
|
||||
area=(28, 184, 1252, 397),
|
||||
search=(8, 164, 1272, 417),
|
||||
color=(214, 224, 231),
|
||||
button=(28, 184, 1252, 397),
|
||||
),
|
||||
)
|
||||
PRESET_LIST = ButtonWrapper(
|
||||
name='PRESET_LIST',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET_LIST.png',
|
||||
area=(556, 85, 726, 120),
|
||||
search=(536, 65, 746, 140),
|
||||
color=(197, 205, 213),
|
||||
button=(556, 85, 726, 120),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET_LIST.png',
|
||||
area=(567, 83, 708, 120),
|
||||
search=(547, 63, 728, 140),
|
||||
color=(177, 187, 197),
|
||||
button=(567, 83, 708, 120),
|
||||
),
|
||||
)
|
||||
PRESET_WINDOW = ButtonWrapper(
|
||||
name='PRESET_WINDOW',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/PRESET_WINDOW.png',
|
||||
area=(28, 184, 1252, 649),
|
||||
search=(8, 164, 1272, 669),
|
||||
color=(215, 226, 233),
|
||||
button=(28, 184, 1252, 649),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/PRESET_WINDOW.png',
|
||||
area=(28, 184, 1252, 649),
|
||||
search=(8, 164, 1272, 669),
|
||||
color=(215, 226, 233),
|
||||
button=(28, 184, 1252, 649),
|
||||
),
|
||||
)
|
||||
RANK = ButtonWrapper(
|
||||
name='RANK',
|
||||
jp=None,
|
||||
@ -345,6 +537,24 @@ RANK = ButtonWrapper(
|
||||
button=(540, 628, 738, 689),
|
||||
),
|
||||
)
|
||||
RECEIVED_CHEST = ButtonWrapper(
|
||||
name='RECEIVED_CHEST',
|
||||
jp=None,
|
||||
en=Button(
|
||||
file='./assets/en/auto_mission/RECEIVED_CHEST.png',
|
||||
area=(502, 188, 774, 226),
|
||||
search=(482, 168, 794, 246),
|
||||
color=(198, 208, 217),
|
||||
button=(547, 487, 738, 547),
|
||||
),
|
||||
zht=Button(
|
||||
file='./assets/zht/auto_mission/RECEIVED_CHEST.png',
|
||||
area=(569, 192, 704, 226),
|
||||
search=(549, 172, 724, 246),
|
||||
color=(177, 187, 197),
|
||||
button=(549, 488, 736, 543),
|
||||
),
|
||||
)
|
||||
REWARD_ACQUIRED = ButtonWrapper(
|
||||
name='REWARD_ACQUIRED',
|
||||
jp=None,
|
||||
|
||||
@ -7,60 +7,34 @@ from module.base.timer import Timer
|
||||
from module.exception import RequestHumanTakeover
|
||||
from module.logger import logger
|
||||
from tasks.item.data_update import DataUpdate
|
||||
from module.base.decorator import cached_property
|
||||
|
||||
import re
|
||||
|
||||
class AutoMissionStatus(Enum):
|
||||
AP = 0 # Calculate AP and decide to terminate Auto-Mission module or not
|
||||
NAVIGATE = 1 # Navigate to the area and select mode
|
||||
ENTER = 2 # Enter the first stage in the stage list
|
||||
CHECK = 3 # Check stages and find a stage that requires to be completed
|
||||
START = 4 # Start the stage
|
||||
FORMATION = 5 # Select units based on the types required by the stage
|
||||
FIGHT = 6 # Fight the stage
|
||||
STAGES_DATA = 1
|
||||
NAVIGATE = 2 # Navigate to the area and select mode
|
||||
ENTER = 3 # Enter the first stage in the stage list
|
||||
CHECK = 4 # Check stages and find a stage that requires to be completed
|
||||
START = 5 # Start the stage
|
||||
FORMATION = 6 # Select units based on the types required by the stage
|
||||
FIGHT = 7 # Fight the stage
|
||||
END = 8
|
||||
FINISH = -1 # Indicate termination of Auto-Mission module
|
||||
|
||||
class AutoMission(AutoMissionUI, Mission):
|
||||
@property
|
||||
def mission_info(self) -> list:
|
||||
valid = True
|
||||
mode = ("N", "H")
|
||||
enable = (self.config.Normal_Enable, self.config.Hard_Enable)
|
||||
area = (self.config.Normal_Area, self.config.Hard_Area)
|
||||
stages_data = [None, None]
|
||||
completion_level = (self.config.Normal_Completion, self.config.Hard_Completion)
|
||||
for index in range(2):
|
||||
if enable[index]:
|
||||
stages_data[index] = self.get_stages_data(mode[index], area[index])
|
||||
valid = valid if self.check_formation(mode[index], area[index], stages_data[index]) else False
|
||||
if valid:
|
||||
info = zip(mode, area, stages_data, completion_level)
|
||||
return list(filter(lambda x: x[2], info))
|
||||
|
||||
def check_formation(self, mode, area, stages_data):
|
||||
mode_name = "Normal" if mode == "N" else "Hard"
|
||||
if stages_data:
|
||||
for stage, info in stages_data.items():
|
||||
if "start" in info:
|
||||
types = info["start"]
|
||||
list_unit = []
|
||||
list_type = []
|
||||
for type in types:
|
||||
list_type.append(type)
|
||||
unit = self.type_to_unit[type]
|
||||
if unit in list_unit:
|
||||
logger.error(f"Mission {mode_name} {area} requires {list_type} but they are both set to unit {unit}")
|
||||
return False
|
||||
list_unit.append(unit)
|
||||
if list_unit and list_unit[0] > unit:
|
||||
logger.error(f"Mission {mode_name} {area} requires {list_type} but they are set to units {list_unit} respectively.\
|
||||
Due to Auto-Mission's implementation, the first unit's index must be smaller than the second unit's index.")
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
def __init__(self, config, device):
|
||||
super().__init__(config, device)
|
||||
self.task = None
|
||||
self.previous_mode = None
|
||||
self.previous_area = None
|
||||
self._stage = None
|
||||
self.stages_data = None
|
||||
self.default_type_to_preset = self.get_default_type_to_preset()
|
||||
self.current_type_to_preset = None
|
||||
|
||||
@cached_property
|
||||
def type_to_unit(self):
|
||||
return {
|
||||
def get_default_type_to_preset(self):
|
||||
type_to_preset = {
|
||||
"burst1": self.config.Formation_burst1,
|
||||
"burst2": self.config.Formation_burst2,
|
||||
"pierce1": self.config.Formation_pierce1,
|
||||
@ -68,6 +42,83 @@ class AutoMission(AutoMissionUI, Mission):
|
||||
"mystic1": self.config.Formation_mystic1,
|
||||
"mystic2": self.config.Formation_mystic2
|
||||
}
|
||||
valid = True
|
||||
|
||||
for type, preset in type_to_preset.items():
|
||||
preset_list = []
|
||||
if isinstance(preset, str):
|
||||
preset = re.sub(r'[ \t\r\n]', '', preset)
|
||||
preset = preset.split("-")
|
||||
if len(preset) == 2:
|
||||
column = preset[0]
|
||||
row = preset[1]
|
||||
if (column.isdigit() and 1 <= int(column) <= 4) and (row.isdigit() and 1 <= int(row) <= 5):
|
||||
preset_list = [int(num) for num in preset]
|
||||
if not preset_list:
|
||||
logger.error(f"Failed to read {type}'s preset settings")
|
||||
valid = False
|
||||
continue
|
||||
type_to_preset[type] = preset_list
|
||||
|
||||
if not valid:
|
||||
raise RequestHumanTakeover
|
||||
return type_to_preset
|
||||
|
||||
def validate_area(self, mode, area_input):
|
||||
area_list = []
|
||||
if isinstance(area_input, str):
|
||||
area_input = re.sub(r'[ \t\r\n]', '', area_input)
|
||||
area_input = (re.sub(r'[>﹥›˃ᐳ❯]', '>', area_input)).split('>')
|
||||
# tried to convert to set to remove duplicates but doesn't maintain order
|
||||
[area_list.append(x) for x in area_input if x not in area_list]
|
||||
elif isinstance(area_input, int):
|
||||
area_list = [str(area_input)]
|
||||
|
||||
if area_list and len([x for x in area_list if x.isdigit()]) == len(area_list):
|
||||
return area_list
|
||||
|
||||
mode_name = "Normal" if mode == "N" else "H"
|
||||
logger.error(f"Failed to read Mission {mode_name}'s area settings")
|
||||
return None
|
||||
|
||||
def find_alternative(self, type, preset_list):
|
||||
if not self.config.Formation_Substitute:
|
||||
return None
|
||||
|
||||
alternatives_dictionary = {
|
||||
'pierce1': ['pierce2', 'burst1', 'burst2', 'mystic1', 'mystic2'],
|
||||
'pierce2': ['burst1', 'burst2', 'mystic1', 'mystic2'],
|
||||
'burst1': ['burst2', 'pierce1', 'pierce2', 'mystic1', 'mystic2'],
|
||||
'burst2': ['pierce1', 'pierce2', 'mystic1', 'mystic2'],
|
||||
'mystic1': ['mystic2', 'burst1', 'burst2', 'pierce1', 'pierce2'],
|
||||
'mystic2': ['burst1', 'burst2', 'pierce1', 'pierce2'],
|
||||
}
|
||||
alternatives = alternatives_dictionary[type]
|
||||
for alternative in alternatives:
|
||||
alternative_preset = self.default_type_to_preset[alternative]
|
||||
if alternative_preset not in preset_list:
|
||||
preset_list.append(alternative_preset)
|
||||
logger.warning(f"{type} was replaced by {alternative}")
|
||||
return preset_list
|
||||
logger.error(f"Unable to find replacements for {type}")
|
||||
return None
|
||||
|
||||
@property
|
||||
def mission_info(self) -> list:
|
||||
valid = True
|
||||
mode = ("N", "H")
|
||||
enable = (self.config.Normal_Enable, self.config.Hard_Enable)
|
||||
area = (self.config.Normal_Area, self.config.Hard_Area)
|
||||
area_list = [None, None]
|
||||
completion_level = (self.config.Normal_Completion, self.config.Hard_Completion)
|
||||
for index in range(2):
|
||||
if enable[index]:
|
||||
area_list[index] = self.validate_area(mode[index], area[index])
|
||||
valid = valid if area_list[index] else False
|
||||
if valid:
|
||||
info = zip(mode, area_list, completion_level)
|
||||
return list(filter(lambda x: x[1], info))
|
||||
return None
|
||||
|
||||
@property
|
||||
def current_mode(self):
|
||||
@ -75,7 +126,7 @@ class AutoMission(AutoMissionUI, Mission):
|
||||
|
||||
@property
|
||||
def current_area(self):
|
||||
return self.task[0][1]
|
||||
return int(self.task[0][1][0])
|
||||
|
||||
@property
|
||||
def current_stage(self):
|
||||
@ -85,20 +136,67 @@ class AutoMission(AutoMissionUI, Mission):
|
||||
def current_stage(self, value):
|
||||
self._stage = value
|
||||
|
||||
@property
|
||||
def current_stages_data(self):
|
||||
return self.task[0][2]
|
||||
|
||||
@property
|
||||
def current_completion_level(self):
|
||||
return self.task[0][3]
|
||||
return self.task[0][2]
|
||||
|
||||
@property
|
||||
def current_count(self):
|
||||
return 1
|
||||
|
||||
def update_stages_data(self):
|
||||
if [self.previous_mode, self.previous_area] != [self.current_mode, self.current_area]:
|
||||
self.stages_data = self.get_stages_data(self.current_mode, self.current_area)
|
||||
if self.stages_data:
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_current_type_to_preset(self):
|
||||
if [self.previous_mode, self.previous_area] == [self.current_mode, self.current_area]:
|
||||
self.current_type_to_preset = None
|
||||
return True
|
||||
|
||||
mode_name = "Normal" if self.current_mode == "N" else "Hard"
|
||||
use_alternative = False
|
||||
for stage, info in self.stages_data.items():
|
||||
if "start" not in info:
|
||||
continue
|
||||
|
||||
list_preset = []
|
||||
list_type = []
|
||||
for type in info["start"]:
|
||||
preset = self.default_type_to_preset[type]
|
||||
list_type.append(type)
|
||||
|
||||
if preset not in list_preset:
|
||||
list_preset.append(preset)
|
||||
continue
|
||||
logger.error(f"Mission {mode_name} {self.current_area} requires {list_type} but they are both set to preset {preset}")
|
||||
list_preset = self.find_alternative(type, list_preset)
|
||||
use_alternative = True
|
||||
if list_preset:
|
||||
continue
|
||||
return False
|
||||
|
||||
if use_alternative:
|
||||
d = {}
|
||||
for index in range(len(list_type)):
|
||||
type, preset = list_type[index], list_preset[index]
|
||||
d[type] = preset
|
||||
self.current_type_to_preset = d
|
||||
else:
|
||||
self.current_type_to_preset = self.default_type_to_preset
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def update_task(self):
|
||||
self.task.pop(0)
|
||||
self.previous_mode = self.current_mode
|
||||
self.previous_area = self.current_area
|
||||
area_list = self.task[0][1]
|
||||
area_list.pop(0)
|
||||
if not area_list:
|
||||
self.task.pop(0)
|
||||
|
||||
def handle_auto_mission(self, status):
|
||||
match status:
|
||||
@ -106,42 +204,49 @@ class AutoMission(AutoMissionUI, Mission):
|
||||
if self.task:
|
||||
self.realistic_count = self.get_realistic_count()
|
||||
if self.realistic_count != 0:
|
||||
return AutoMissionStatus.NAVIGATE
|
||||
return AutoMissionStatus.STAGES_DATA
|
||||
return AutoMissionStatus.FINISH
|
||||
|
||||
case AutoMissionStatus.STAGES_DATA:
|
||||
if self.update_stages_data() and self.update_current_type_to_preset():
|
||||
return AutoMissionStatus.NAVIGATE
|
||||
return AutoMissionStatus.END
|
||||
|
||||
case AutoMissionStatus.NAVIGATE:
|
||||
switch = SWITCH_NORMAL if self.current_mode == "N" else SWITCH_HARD
|
||||
self.navigate(self.previous_mode, self.current_mode)
|
||||
if self.select_area(self.current_area) and self.select_mode(switch):
|
||||
return AutoMissionStatus.ENTER
|
||||
raise RequestHumanTakeover
|
||||
|
||||
|
||||
case AutoMissionStatus.ENTER:
|
||||
if self.wait_mission_info(self.current_mode, open_task=True):
|
||||
return AutoMissionStatus.CHECK
|
||||
raise RequestHumanTakeover
|
||||
|
||||
case AutoMissionStatus.CHECK:
|
||||
self.current_stage = self.check_stages(*self.task[0])
|
||||
self.current_stage = self.check_stages(self.current_mode, self.current_area, self.stages_data, self.current_completion_level)
|
||||
if self.current_stage:
|
||||
return AutoMissionStatus.START
|
||||
self.update_task()
|
||||
return AutoMissionStatus.AP
|
||||
return AutoMissionStatus.END
|
||||
|
||||
case AutoMissionStatus.START:
|
||||
self.start_stage(self.current_stage)
|
||||
return AutoMissionStatus.FORMATION
|
||||
|
||||
case AutoMissionStatus.FORMATION:
|
||||
self.formation(self.current_stage, self.type_to_unit)
|
||||
self.formation(self.current_stage, self.current_type_to_preset)
|
||||
return AutoMissionStatus.FIGHT
|
||||
|
||||
case AutoMissionStatus.FIGHT:
|
||||
self.fight(self.current_stage, manual_boss=self.config.ManualBoss_Enable)
|
||||
# Return to the previous region to prevent map unlock card recognition
|
||||
self.select_area(self.current_area - 1)
|
||||
self.update_ap()
|
||||
self.previous_mode = self.current_mode
|
||||
self.previous_area = self.current_area
|
||||
return AutoMissionStatus.AP
|
||||
|
||||
case AutoMissionStatus.END:
|
||||
self.update_task()
|
||||
return AutoMissionStatus.AP
|
||||
|
||||
case AutoMissionStatus.FINISH:
|
||||
@ -153,8 +258,6 @@ class AutoMission(AutoMissionUI, Mission):
|
||||
return status
|
||||
|
||||
def run(self):
|
||||
self.previous_mode = None
|
||||
self._stage = None
|
||||
self.task = self.valid_task
|
||||
if self.task:
|
||||
action_timer = Timer(0.5, 1)
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from module.ui.switch import Switch
|
||||
from module.ui.scroll_select import ScrollSelect
|
||||
from module.ocr.ocr import Digit
|
||||
from tasks.base.ui import UI
|
||||
from tasks.base.assets.assets_base_page import MISSION_CHECK
|
||||
from tasks.auto_mission.assets.assets_auto_mission import *
|
||||
from tasks.auto_mission.stage import StageState
|
||||
|
||||
SCROLL_SELECT = ScrollSelect(PRESET_WINDOW, PRESET_FIRST_ITEM, MOBILIZE, clickx=1150, responsey=60, swipeoffsetx=-600)
|
||||
PRESETS = [PRESET1_ON, PRESET2_ON, PRESET3_ON, PRESET4_ON]
|
||||
|
||||
SWITCH_UNIT1 = Switch('Unit1_Switch')
|
||||
SWITCH_UNIT1.add_state('on', UNIT1_ON)
|
||||
SWITCH_UNIT1.add_state('off', UNIT1_OFF)
|
||||
@ -74,8 +78,21 @@ class Copilot(UI):
|
||||
return True
|
||||
|
||||
"""Formation methods"""
|
||||
def choose_unit(self, type, type_to_unit):
|
||||
unit_index = type_to_unit[type] - 1
|
||||
def choose_from_preset(self, type, type_to_preset):
|
||||
preset, row = type_to_preset[type]
|
||||
preset_index = preset - 1
|
||||
row_index = row - 1
|
||||
self.select_then_check(LAYOUT, PRESET_LIST)
|
||||
#self.set_switch(PRESET_SWITCHES[preset_index])
|
||||
PRESET = PRESETS[preset_index]
|
||||
while not self.match_color(PRESET, threshold=50):
|
||||
self.device.screenshot()
|
||||
self.click_with_interval(PRESET, interval=1)
|
||||
clickoffsety = [85, 85, 0, -120, 0]
|
||||
SCROLL_SELECT.select_index(main=self, target_index=row_index, clickoffsety=clickoffsety[row_index])
|
||||
|
||||
def choose_unit(self, unit):
|
||||
unit_index = unit - 1
|
||||
unit_switch = UNIT_SWITCHES[unit_index]
|
||||
self.set_switch(unit_switch)
|
||||
|
||||
@ -86,16 +103,23 @@ class Copilot(UI):
|
||||
return True
|
||||
self.click(*start_coords, interval=2)
|
||||
|
||||
def formation(self, stage, type_to_unit):
|
||||
def formation(self, stage, type_to_preset):
|
||||
if stage.state == StageState.SUB:
|
||||
# Select a unit to start the battle
|
||||
self.choose_unit(stage.formation_info, type_to_unit)
|
||||
self.choose_unit(1)
|
||||
if type_to_preset:
|
||||
type = stage.formation_info
|
||||
self.choose_from_preset(type, type_to_preset)
|
||||
self.click_with_interval(MOBILIZE, interval=1)
|
||||
else:
|
||||
unit = 1
|
||||
for type, start_coords in stage.formation_start_info:
|
||||
self.goto_formation_page(start_coords)
|
||||
self.choose_unit(type, type_to_unit)
|
||||
self.choose_unit(unit)
|
||||
if type_to_preset:
|
||||
self.choose_from_preset(type, type_to_preset)
|
||||
self.select_then_check(MOBILIZE, MISSION_INFO)
|
||||
unit += 1
|
||||
|
||||
"""Fight methods"""
|
||||
def begin_mission(self):
|
||||
@ -122,7 +146,20 @@ class Copilot(UI):
|
||||
force_index = self.get_force()
|
||||
self.sleep(1)
|
||||
return force_index
|
||||
|
||||
|
||||
def handle_all_mission_popup(self):
|
||||
self.sleep(2)
|
||||
while not self.match_color(MISSION_INFO):
|
||||
self.device.screenshot()
|
||||
if self.match_color(MISSION_INFO):
|
||||
break
|
||||
if self.appear_then_click(MISSION_INFO_POPUP):
|
||||
continue
|
||||
if self.appear_then_click(MOVE_UNIT):
|
||||
continue
|
||||
if self.appear_then_click(RECEIVED_CHEST):
|
||||
continue
|
||||
|
||||
def handle_mission_popup(self, button, skip_first_screenshot=True):
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
@ -146,9 +183,10 @@ class Copilot(UI):
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
if not self.match_color(END_PHASE):
|
||||
self.handle_mission_popup(END_PHASE_POPUP)
|
||||
self.handle_all_mission_popup()
|
||||
break
|
||||
self.appear_then_click(END_PHASE)
|
||||
self.sleep(2)
|
||||
|
||||
def wait_over(self):
|
||||
#self.sleep(2)
|
||||
@ -220,6 +258,8 @@ class Copilot(UI):
|
||||
if 'wait-over' in act:
|
||||
self.wait_over()
|
||||
self.sleep(2)
|
||||
if i != len(actions) - 1:
|
||||
self.handle_all_mission_popup()
|
||||
|
||||
logger.warning("Actions completed, waiting to enter the battle...")
|
||||
|
||||
|
||||
@ -23,7 +23,8 @@ class AutoMissionUI(Copilot):
|
||||
# Get stage_data data from the module
|
||||
return stage_data
|
||||
except ModuleNotFoundError:
|
||||
logger.error(f"Exploration not supported for area {area}, under development...")
|
||||
mode_name = "Normal" if mode == "N" else "Hard"
|
||||
logger.error(f"Exploration not supported for Mission {mode_name} area {area}, under development...")
|
||||
return None
|
||||
|
||||
def wait_mission_info(self, mode, open_task=False, max_retry=99999):
|
||||
|
||||
@ -4,10 +4,10 @@ from module.base.decorator import Config
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import DigitCounter
|
||||
from module.ui.scroll_select import ScrollSelect
|
||||
from tasks.base.assets.assets_base_page import SCHEDULE_CHECK
|
||||
from tasks.base.ui import UI
|
||||
from tasks.schedule.assets.assets_schedule import *
|
||||
from tasks.schedule.scroll_select import ScrollSelect
|
||||
|
||||
SCROLL_SELECT = ScrollSelect(window_button=SCROLL, first_item_button=FIRST_ITEM, expected_button=LOCATIONS, clickx=1116)
|
||||
xs = np.linspace(299, 995, 3, dtype=int)
|
||||
@ -51,7 +51,7 @@ class ScheduleUI(UI):
|
||||
return False
|
||||
|
||||
def enter_location(self, location):
|
||||
SCROLL_SELECT.select_location(self, location)
|
||||
SCROLL_SELECT.select_index(main=self, target_index=location)
|
||||
if not self.appear(LOCATIONS):
|
||||
logger.error("Unable to navigate to page for location {}".format(location + 1))
|
||||
return False
|
||||
|
||||