Merge pull request #9 from whoamikyo/master

Fix: [EN] Relogin is now working
This commit is contained in:
LmeSzinc 2020-05-31 16:34:00 +08:00 committed by GitHub
commit aa12392a28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 642 additions and 54 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ config/*.ini
!alas_kr.pyw
dev_tools/debug_tools
.idea
/screenshots
# Created by .ignore support plugin (hsz.mobi)

View File

@ -2,7 +2,7 @@
Alas, an Azur Lane automation tool with GUI (For CN server, can support other server).
![gui](doc/README.assets/gui.png)
![gui](doc/README.assets/gui_en.png)
## Features

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -28,8 +28,8 @@ MAP.map_data = '''
class Config:
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = False
MAP_HAS_MOVABLE_ENEMY = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_SIREN_COUNT = 1

View File

@ -24,8 +24,8 @@ A6, B6, C6, D6, E6, F6, G6, H6, \
= MAP.flatten()
class Config:
SUBMARINE = 1
FLEET_BOSS = 1
SUBMARINE = 0
FLEET_BOSS = 0
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False

View File

@ -25,9 +25,18 @@ A6, B6, C6, D6, E6, F6, G6, H6, I6, \
A7, B7, C7, D7, E7, F7, G7, H7, I7, \
= MAP.flatten()
MAP.spawn_data = [
{'battle': 0, 'enemy': 2, 'siren': 2},
{'battle': 1, 'enemy': 1},
{'battle': 2, 'enemy': 2},
{'battle': 3, 'enemy': 1},
{'battle': 4, 'enemy': 1},
{'battle': 5, 'boss': 1},
]
class Config:
SUBMARINE = 1
FLEET_BOSS = 1
SUBMARINE = 0
FLEET_BOSS = 0
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False
@ -56,3 +65,14 @@ class Config:
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
if self.clear_siren():
return True
if self.clear_enemy(scale=(2, 3)):
return True
return self.battle_default()
def battle_5(self):
return self.clear_boss()

View File

@ -17,18 +17,71 @@ MAP.map_data = '''
SP -- -- -- ++ -- -- ME --
SP -- MS -- -- ME -- -- --
'''
A1, B1, C1, D1, E1, F1, G1, H1, I1, \
A2, B2, C2, D2, E2, F2, G2, H2, I2, \
A3, B3, C3, D3, E3, F3, G3, H3, I3, \
A4, B4, C4, D4, E4, F4, G4, H4, I4, \
A5, B5, C5, D5, E5, F5, G5, H5, I5, \
A6, B6, C6, D6, E6, F6, G6, H6, I6, \
A7, B7, C7, D7, E7, F7, G7, H7, I7, \
A8, B8, C8, D8, E8, F8, G8, H8, I8, \
A9, B9, C9, D9, E9, F9, G9, H9, I9, \
= MAP.flatten()
MAP.spawn_data = [
{'battle': 0, 'enemy': 2, 'siren': 2},
{'battle': 1, 'enemy': 1},
{'battle': 2, 'enemy': 2},
{'battle': 3, 'enemy': 1},
{'battle': 4, 'enemy': 1},
{'battle': 5, 'boss': 1},
]
class Config:
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = False
MAP_HAS_MOVABLE_ENEMY = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = False
MAP_SIREN_COUNT = 2
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_GRID_CENTER_TOLERANCE = 0.3
MAP_SIREN_TEMPLATE = ['1', '2', '3', 'DD']
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 50
MID_DIFF_RANGE_H = (45, 70)
MID_DIFF_RANGE_V = (97 - 3, 97 + 3)
TRUST_EDGE_LINES = True
VANISH_POINT_RANGE = ((540, 740), (-4000, -2000))
DISTANCE_POINT_X_RANGE = ((-2000, -1000),)
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (80, 255 - 40),
'width': (0.9, 10),
'prominence': 10,
'distance': 35,
'wlen': 100,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 40, 255),
'prominence': 10,
'distance': 50,
'wlen': 1000
}
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
if self.clear_siren():
return True
if self.clear_enemy(scale=(2, 3)):
return True
return self.battle_default()
def battle_5(self):
return self.clear_boss()

View File

@ -13,10 +13,22 @@ MAP.map_data = '''
-- ME -- -- ++ ME -- ME --
SP -- -- ME ++ -- ME -- MB
'''
A1, B1, C1, D1, E1, F1, G1, H1, I1, \
A2, B2, C2, D2, E2, F2, G2, H2, I2, \
A3, B3, C3, D3, E3, F3, G3, H3, I3, \
A4, B4, C4, D4, E4, F4, G4, H4, I4, \
A5, B5, C5, D5, E5, F5, G5, H5, I5, \
= MAP.flatten()
MAP.spawn_data = [
{'battle': 0, 'enemy': 2, 'siren': 2},
{'battle': 1, 'enemy': 1},
{'battle': 2, 'enemy': 2},
{'battle': 3, 'enemy': 1},
{'battle': 4, 'boss': 1},
]
class Config:
SUBMARINE = 1
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True
@ -44,3 +56,14 @@ class Config:
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
if self.clear_siren():
return True
if self.clear_enemy(scale=(2, 3)):
return True
return self.battle_default()
def battle_4(self):
return self.clear_boss()

View File

@ -29,8 +29,8 @@ MAP.map_data = '''
class Config:
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = False
MAP_HAS_MOVABLE_ENEMY = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
# MAP_HAS_DYNAMIC_RED_BORDER = False
MAP_SIREN_COUNT = 2

View File

@ -17,8 +17,8 @@ MAP.map_data = '''
class Config:
SUBMARINE = 1
FLEET_BOSS = 2
SUBMARINE = 0
FLEET_BOSS = 0
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False

View File

@ -36,8 +36,8 @@ class Config:
SUBMARINE = 0
FLEET_BOSS = 0
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = False
MAP_HAS_MOVABLE_ENEMY = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = False
MAP_SIREN_COUNT = 3

View File

@ -7,7 +7,7 @@ from module.logger import logger
MAP = CampaignMap('d3')
MAP.shape = 'I9'
MAP.map_data = '''
SP -- ++ ++ -- MS -- ME --
SP -- ++ ++ -- -- -- ME --
-- ME ++ ++ -- ME -- -- --
-- -- -- -- -- -- ME ME --
ME -- ME ME -- ME ++ ++ ++
@ -17,27 +17,51 @@ MAP.map_data = '''
SP -- -- -- ++ -- -- ME --
SP -- MS -- -- ME -- -- --
'''
A1, B1, C1, D1, E1, F1, G1, H1, I1, \
A2, B2, C2, D2, E2, F2, G2, H2, I2, \
A3, B3, C3, D3, E3, F3, G3, H3, I3, \
A4, B4, C4, D4, E4, F4, G4, H4, I4, \
A5, B5, C5, D5, E5, F5, G5, H5, I5, \
A6, B6, C6, D6, E6, F6, G6, H6, I6, \
A7, B7, C7, D7, E7, F7, G7, H7, I7, \
A8, B8, C8, D8, E8, F8, G8, H8, I8, \
A9, B9, C9, D9, E9, F9, G9, H9, I9, \
= MAP.flatten()
MAP.spawn_data = [
{'battle': 0, 'enemy': 2, 'siren': 2},
{'battle': 1, 'enemy': 1},
{'battle': 2, 'enemy': 2, 'siren': 1},
{'battle': 3, 'enemy': 1},
{'battle': 4, 'enemy': 1},
{'battle': 5},
{'battle': 6, 'boss': 1},
]
class Config:
SUBMARINE = 1
FLEET_BOSS = 2
POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_SIREN_COUNT = 3
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_GRID_CENTER_TOLERANCE = 0.3
MAP_SIREN_TEMPLATE = ['1', '2', '3', 'DD']
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 50
MID_DIFF_RANGE_H = (45, 70)
MID_DIFF_RANGE_V = (97 - 3, 97 + 3)
TRUST_EDGE_LINES = True
VANISH_POINT_RANGE = ((540, 740), (-4000, -2000))
DISTANCE_POINT_X_RANGE = ((-2000, -1000),)
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (80, 255 - 40),
'width': (0.9, 10),
'prominence': 10,
'distance': 35,
'wlen': 100,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 40, 255),
@ -50,3 +74,22 @@ class Config:
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
if self.clear_siren():
return True
if self.clear_enemy(scale=(2, 3)):
return True
return self.battle_default()
def battle_5(self):
if self.clear_enemy(scale=(1,)):
return True
if self.clear_enemy(scale=(2,)):
return True
return self.battle_default()
def battle_6(self):
return self.fleet_2.clear_boss()

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

443
doc/development_en.md Normal file
View File

@ -0,0 +1,443 @@
**Translation under progress**
# Participate in development
- [How to add a button] (#How to add a button)
- [How to adapt to a new map] (#How to adapt to a new map)
## How to add a asset
Assets are stored in `./asset` directory, and is defined in each module `asset.py` file.
For example, the button `BATTLE_PREPARATION`
![BATTLE_PREPARATION](development.assets/BATTLE_PREPARATION_en.png)
In `asset.py` it is this:
```
BATTLE_PREPARATION = Button(area={'cn': (1043, 607, 1241, 667), 'en': (1045, 607, 1241, 667)}, color={'cn': (234, 179, 97), 'en': (235, 182, 102)}, button={'cn': (1043, 607, 1241, 667), 'en': (1045, 607, 1241, 667)}, file={'cn': './assets/cn/combat/BATTLE_PREPARATION.png', 'en': './assets/en/combat/BATTLE_PREPARATION.png'})
```
Note that all `asset.py` are generate with `./dev_tools/button_extract.py`, not to manually modify.
Suppose we want to add a `CONFIRM` button, which appears in the submarine signal scanning.
1. **Screenshot**
![screenshot](development.assets/ss.png)
the best way to take screenshots its using `adb exec-out screencap -p > screenshot.png`
If you get some output error, maybe you need to add your emulator port like that `adb -s 127.0.0.1:5555 exec-out screencap -p >screenshot.png`
2. **Copy the picture to the corresponding directory under `./asset`**, and change the file name, for example `EQUIP_CONFIRM.png`
3. **Drag to Photoshop**, here takes Photoshop CS6 as an example
4. **Use the selection tool (M) to select the button area**
5. **Play action**
In the first operation, you can follow the steps below to add actions.
In the menu bar `WINDOW`, click on `ACTION` option or ALT+F9
Before adding actions, it is best to back-up the current picture, because the next operation that needs to be recorded is irreversible.
* In the action window, click the icon of the new action, and name it according to your preferences, for example button_image. Click `Begin Recording`, note that the gray circle turns red, which means that action recording has started
* Right-click in the picture area, click `Inverse` or `SHIFT+CTRL+I`
* In the menu bar `Edit`, click `Fill` or `SHIFT+F5`
* In the pop-up fill window, fill content use `Black`, fill mode selection `normal`, opacity select 100, click `Ok`
* In the menu bar `File`, click `Save`
* In the menu bar `File`, click `Close`
* In the action window, click the stop recording icon, the action recording will stop.
After the recording is completed, you will get the following actions
```
button_image
Inverse
Fill
Using: Black
Opacity: 100%
Mode: normal
Save
Close
```
When adding assets in the future, you can directly click the button of the playback action to complete the image processing.
6. **(Optional) Add attributes to overlay pictures**
A asset has three attributes:
- area, the area identified by the asset
- color, the color of the asset
- button, click area after the asset appears
If you place `SEARCH_CONFIRM.BUTTON.png` in the same folder, and process it with the method mentioned above. The `button` attribute of `SEARCH_CONFIRM.BUTTON.png` will over-write `button` attribute of `SEARCH_CONFIRM.png`
This is a very useful feature, because the script usually needs to analyze the elements that appear in the screenshot and click on the button. The location that needs to be analyzed and the location that needs to be clicked may not be in the same position.
7. **Run button_extract.py under ./dev_tools**
button_extract.py will automatically extract the attributes of the button, eliminating the trouble of manual input.
```
python -m dev_tools.button_extract
```
8. **Use button**
Inherit the ModuleBase class under module.base.base, you can call the following methods:
- appear()
- appear_then_click()
- wait_until_appear()
- wait_until_appear_then_click()
- wait_until_disappear()
Optional parameters of the method:
- offset (bool, int, tuple): The default is 0, when it is 0, the average color of the button on the screenshot will be used to identify the button
After input, it means that the button refers to the preset area offset range, and then use the template matching to identify the button
- interval (int): The trigger interval of the button, the default is 0.
Represents the trigger interval of the button. When there is an animation after the button is clicked, this parameter can prevent the button from being frequently clicked.
- screenshot (bool): Save the screenshot after the button appears
- genre (str): the name of the subdirectory where the screenshot is saved
## How to adapt to a new map
The following example is adapted to the simple version of 7-2. The complete logic is in `campaign/campaign_main/7_2.py`
### Define the map
1. **New file**
In `./campaign`if it's a new event, create a new directory, as recommended name the directory `event_<eventdate>_<server>`, such as `event_20200326_cn `or `event_20200521_en`.
Create a new .py file, the file name is the map name, lowercase, starting with a letter, such as sp3, d3.
2. **Import入**
```
from module.campaign.campaign_base import CampaignBase
from module.map.map_base import CampaignMap
```
3. ```
MAP = CampaignMap()
```
4. **(Optional) Set the map size**
When not filled, it is generated according to the sea area information
```
MAP.shape = 'H5'
```
5. **Set sea area information**
In Azur Lane WIKI you can see the map data, if it is a new map, WIKI may take 1 day or 2 to update...
![wiki_7_2](development.assets/wiki_7_2.jpg)
```
MAP.map_data = '''
ME ++ ME -- ME ME -- SP
MM ++ ++ MM -- -- ME --
ME -- ME MB ME -- ME MM
-- ME -- MM -- ME ++ ++
SP -- ME ME -- ME ++ ++
'''
```
The map information must contain at least the location of land and sea. There are still some difficulties in identifying land and sea
If the Spawn point (SP) is relatively remote, you must also include it
```
MAP.map_data = '''
-- ++ ++ -- -- -- -- SP
-- ++ -- -- -- -- -- --
-- -- -- -- -- -- -- --
-- -- -- -- -- -- ++ ++
SP -- -- -- -- -- ++ ++
'''
```
The meaning of these symbols is in `module/map/grid_info.py`.
| print_name | property_name | description |
|------------|----------------|-------------------------|
| ++ | is_land | fleet can't go to land |
| -- | is_sea | sea |
| __ | | submarine spawn point |
| SP | is_spawn_point | fleet may spawns here |
| ME | may_enemy | enemy may spawns here |
| MB | may_boss | boss may spawns here |
| MM | may_mystery | mystery may spawns here |
| MA | may_ammo | fleet can get ammo here |
| MS | may_siren | Siren/Elite enemy spawn |
6. **(Optional) Set sea weights**
The higher the value, the less you want the fleet to go. When not set, all are 10.
```
MAP.weight_data = '''
40 30 30 30 30 30 30 30
20 20 20 20 20 20 20 20
10 10 10 10 10 10 10 10
20 20 20 20 20 20 20 20
30 30 30 30 30 30 30 30
'''
```
7. **(Optional) Set the camera position*
```
MAP.camera_data = ['D3']
```
When not set, it will be generated according to the map size and camera field of view.
Manual setting can speed up the map scanning speed. For example, in 7-2, the automatically generated camera position is D2 D3 E2 E3. In fact, you only need to point the camera at D3 to see the entire map (A1 does not matter)..
8. **(Optional) Set enemy refresh information**
```
MAP.spawn_data = [
{'battle': 0, 'enemy': 3},
{'battle': 1, 'enemy': 2, 'mystery': 1},
{'battle': 2, 'enemy': 2, 'mystery': 1},
{'battle': 3, 'enemy': 1, 'mystery': 2},
{'battle': 4, 'enemy': 1},
{'battle': 5, 'boss': 1},
]
```
Here is a logic of what will be refreshed after each battle.This information is not on WIKI and needs to be collected manually.
In theory, it can be run without filling in. The function of the enemys refresh information is to correct the recognition errors and missing, and to capture the camera movement when the BOSS is refreshed.
9. **Expand the map**
```
A1, B1, C1, D1, E1, F1, G1, H1, \
A2, B2, C2, D2, E2, F2, G2, H2, \
A3, B3, C3, D3, E3, F3, G3, H3, \
A4, B4, C4, D4, E4, F4, G4, H4, \
A5, B5, C5, D5, E5, F5, G5, H5, \
= MAP.flatten()
```
This is to prepare for the subsequent writing of the enemy logic, which can be generated using the following code
```
shape = 'H5'
def location2node(location):
return chr(location[0] + 64 + 1) + str(location[1] + 1)
def node2location(node):
return ord(node[0]) % 32 - 1, int(node[1]) - 1
shape = node2location(shape.upper())
for y in range(shape[1]+1):
text = ', '.join([location2node((x, y)) for x in range(shape[0]+1)]) + ', \\'
print(text)
print(' = MAP.flatten()')
```
The py file can be found at `dev_tools/Flatten_generator.py`
10. **(Optional) Define map elements**
Such as defining roadblocks
```
ROAD_MAIN = RoadGrids([A3, [C3, B4, C5], [F1, G2, G3]])
```
### Set map parameters
The map parameters will override the default parameters and user parameters, with the highest priority.
If you don't know how to set it, just skip it. But it needs to be defined.
```
class Config:
pass
```
Refer to module.config.config for properties that can be set
For example, for the microlayer D3, because the map adds sea fog, different grid recognition parameters are required (the default parameters are for 7-2).
```
class Config:
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (100, 220),
'width': 1,
'prominence': 10,
'distance': 35,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 80, 255),
'prominence': 2,
'distance': 50,
'wlen': 1000
}
```
There should be the following settings for enable siren recognition.
```
class Config:
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_SIREN_COUNT = 2
```
If only the least sea area information is entered, or there is no enemy refresh information, it should be noted that the map information is missing, and it will run in `wasteland mode` at this time
```
class Config:
POOR_MAP_DATA = True
```
For Crimson Echoes, the camera position was raised, the grid was reduced to 0.66.
```
class Config:
MAP_GRID_CENTER_TOLERANCE = 0.3
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 50
EDGE_LINES_HOUGHLINES_THRESHOLD = 50
CAMERA_SWIPE_MULTIPLY_X = 200 * 0.7
CAMERA_SWIPE_MULTIPLY_Y = 140 * 0.7
COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.
MID_DIFF_RANGE_H = (45, 70)
MID_DIFF_RANGE_V = (97 - 3, 97 + 3)
TRUST_EDGE_LINES = True
VANISH_POINT_RANGE = ((540, 740), (-4000, -2000))
DISTANCE_POINT_X_RANGE = ((-2000, -1000),)
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (80, 255 - 40),
'width': (0.9, 10),
'prominence': 10,
'distance': 35,
'wlen': 100,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 40, 255),
'prominence': 10,
'distance': 50,
'wlen': 1000
}
```
### Write search logic
- Run at default
```
class Campaign(CampaignBase):
MAP = MAP
```
- Simple logic
Write logic according to the first few battles, when missing, use the logic of the previous battle
Generally speaking, the logic of each battle should start with a question mark and clear roadblocks, then clear potential roadblocks, and finally return to the default battle logic
```
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
self.clear_all_mystery(nearby=False)
if self.clear_roadblocks([ROAD_MAIN], strongest=True):
return True
if self.clear_potential_roadblocks([ROAD_MAIN], strongest=True):
return True
if self.clear_enemy(strongest=True, weight=True):
return True
return self.battle_default()
def battle_5(self):
self.clear_all_mystery(nearby=False)
if self.clear_roadblocks([ROAD_MAIN]):
return True
return self.fleet_2.brute_clear_boss()
```
To be continued
## How to support other server/language
### GUI
Copy `./module/config/argparser.py` to `argparser_xx.py` and change the argment.
Create a dictionary in `./module/config/dictionary.py` that translate your language to english.
Copy `alas_cn.py` to `alas_xx.py` and import `argparser_xx.py` . Then, edit server name.
> Format of .pyw file name: <sctipt_name>_<server_name>.pyw
>
> Script name is used to load ini file under `./config`, For example, alas_cn.pyw and alas_en.pyw both loads `./config/alas.ini`, but in different languages.
### Assets
Copy folder `./assets/cn` to `./assets/<your server>`, and replace the image. This will cost a lot of time to find, crop and test. Fortunately, if a image does not contain any charactors, it may works in all servers.
After replacing an image, don't forget to run `./dev_tools/button_extract.py`
### Class methods
Some method may be different in different servers. This decoractor is use to calls different function with a same name according to config (AzurLaneConfig instance).
```
from module.base.decorator import Config
from module.base.base import ModuleBase
class AnotherModule(ModuleBase):
@Config.when(SERVER='en')
def function(self):
# This method will be called only in EN server
pass
@Config.when(SERVER=None)
def function(self):
# This method will be called in other server
pass
```
### Other
There area also some modules diffcult to change: the commission module.
In `./module/reward/commission.py`, I use [cnocr](https://github.com/breezedeus/cnocr) to recognize commission name in chinese, it may not works well in other languages.

View File

@ -162,7 +162,7 @@ class CampaignOcr:
self.stage_entrance = {}
buttons = self.campaign_extract_name_image(image)
ocr = Ocr(buttons, lang='stage', letter=(255, 255, 255), back=(102, 102, 102), threshold=180)
ocr = Ocr(buttons, lang='stage', letter=(255, 255, 255), back=(102, 102, 102), threshold=120)
result = ocr.ocr(image)
if not isinstance(result, list):
result = [result]

View File

@ -12,6 +12,8 @@ AUTOMATION_SWITCH = Button(area={'cn': (821, 106, 907, 136), 'en': (823, 109, 90
BATTLE_PREPARATION = Button(area={'cn': (1043, 607, 1241, 667), 'en': (1045, 607, 1241, 667)}, color={'cn': (234, 179, 97), 'en': (235, 182, 102)}, button={'cn': (1043, 607, 1241, 667), 'en': (1045, 607, 1241, 667)}, file={'cn': './assets/cn/combat/BATTLE_PREPARATION.png', 'en': './assets/en/combat/BATTLE_PREPARATION.png'})
BATTLE_PREPARATION_WITH_OVERLAY = Button(area={'cn': (1058, 622, 1226, 652), 'en': (1058, 622, 1226, 652)}, color={'cn': (96, 74, 39), 'en': (96, 74, 39)}, button={'cn': (1058, 622, 1226, 652), 'en': (1058, 622, 1226, 652)}, file={'cn': './assets/cn/combat/BATTLE_PREPARATION_WITH_OVERLAY.png', 'en': './assets/en/combat/BATTLE_PREPARATION_WITH_OVERLAY.png'})
BATTLE_STATUS_A = Button(area={'cn': (622, 266, 732, 288), 'en': (622, 266, 732, 288)}, color={'cn': (235, 227, 111), 'en': (235, 227, 111)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_A.png', 'en': './assets/en/combat/BATTLE_STATUS_A.png'})
BATTLE_STATUS_C = Button(area={'cn': (431, 257, 514, 329), 'en': (431, 257, 514, 329)}, color={'cn': (169, 161, 164), 'en': (169, 161, 164)}, button={'cn': (431, 257, 514, 329), 'en': (431, 257, 514, 329)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_C.png', 'en': './assets/en/combat/BATTLE_STATUS_C.png'})
BATTLE_STATUS_D = Button(area={'cn': (431, 257, 514, 329), 'en': (431, 257, 514, 329)}, color={'cn': (169, 161, 164), 'en': (169, 161, 164)}, button={'cn': (431, 257, 514, 329), 'en': (431, 257, 514, 329)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_D.png', 'en': './assets/en/combat/BATTLE_STATUS_D.png'})
BATTLE_STATUS_S = Button(area={'cn': (633, 297, 722, 320), 'en': (629, 297, 722, 318)}, color={'cn': (233, 241, 127), 'en': (233, 241, 127)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_S.png', 'en': './assets/en/combat/BATTLE_STATUS_S.png'})
COMBAT_AUTO = Button(area={'cn': (136, 573, 167, 604), 'en': (136, 573, 167, 604)}, color={'cn': (229, 242, 255), 'en': (229, 242, 255)}, button={'cn': (136, 573, 167, 604), 'en': (136, 573, 167, 604)}, file={'cn': './assets/cn/combat/COMBAT_AUTO.png', 'en': './assets/en/combat/COMBAT_AUTO.png'})
COMBAT_AUTO_SWITCH = Button(area={'cn': (18, 38, 36, 56), 'en': (16, 31, 140, 62)}, color={'cn': (179, 198, 235), 'en': (105, 137, 189)}, button={'cn': (18, 38, 36, 56), 'en': (16, 31, 140, 62)}, file={'cn': './assets/cn/combat/COMBAT_AUTO_SWITCH.png', 'en': './assets/en/combat/COMBAT_AUTO_SWITCH.png'})

View File

@ -53,7 +53,7 @@ def update_config_from_template(config, file):
sidebar_title='Function',
terminal_font_family='Consolas',
language='english',
default_size=(1000, 720),
default_size=(1110, 720),
navigation='SIDEBAR',
tabbed_groups=True,
show_success_modal=False,
@ -119,7 +119,7 @@ def main(ini_name=''):
# 选择关卡
stage = setting_parser.add_argument_group('Level settings', 'Need to run once to save options')
stage.add_argument('--enable_stop_condition', default=default('--enable_stop_condition'), choices=['yes', 'no'])
stage.add_argument('--enable_fast_forward', default=default('--enable_fast_forward'), choices=['yes', 'no'])
stage.add_argument('--enable_fast_forward', default=default('--enable_fast_forward'), choices=['yes', 'no'], help='Enable or disable clearing mode')
stop = stage.add_argument_group('Stop condition', 'After triggering, it will not stop immediately. It will complete the current attack first, and fill in 0 if it is not needed.')
stop.add_argument('--if_count_greater_than', default=default('--if_count_greater_than'), help='The previous setting will be used, and the number\n of deductions will be deducted after completion of the attack until it is cleared.')
@ -133,7 +133,7 @@ def main(ini_name=''):
fleet.add_argument('--enable_fleet_control', default=default('--enable_fleet_control'), choices=['yes', 'no'])
fleet.add_argument('--enable_map_fleet_lock', default=default('--enable_map_fleet_lock'), choices=['yes', 'no'])
f1 = fleet.add_argument_group('Road Fleet', 'Players can choose a formation before battle. Though it has no effect appearance-wise, the formations applies buffs to certain stats.\nLine Ahead: Increases Firepower and Torpedo by 15%, but reduces Evasion by 10% (Applies only to Vanguard fleet)\nDouble Line: Increases Evasion by 30%, but decreases Firepower and Torpedo by 5% (Applies only to Vanguard fleet)\nDiamond: Increases Anti-Air by 20% (no penalties, applies to entire fleet)')
f1 = fleet.add_argument_group('Mob Fleet', 'Players can choose a formation before battle. Though it has no effect appearance-wise, the formations applies buffs to certain stats.\nLine Ahead: Increases Firepower and Torpedo by 15%, but reduces Evasion by 10% (Applies only to Vanguard fleet)\nDouble Line: Increases Evasion by 30%, but decreases Firepower and Torpedo by 5% (Applies only to Vanguard fleet)\nDiamond: Increases Anti-Air by 20% (no penalties, applies to entire fleet)')
f1.add_argument('--fleet_index_1', default=default('--fleet_index_1'), choices=['1', '2', '3', '4', '5', '6'])
f1.add_argument('--fleet_formation_1', default=default('--fleet_formation_1'), choices=['Line Ahead', 'Double Line', 'Diamond'])
f1.add_argument('--fleet_step_1', default=default('--fleet_step_1'), choices=['1', '2', '3', '4', '5', '6'], help='In event map, fleet has limit on moving, so fleet_step is how far can a fleet goes in one operation, if map cleared, it will be ignored')
@ -143,7 +143,7 @@ def main(ini_name=''):
f2.add_argument('--fleet_formation_2', default=default('--fleet_formation_2'), choices=['Line Ahead', 'Double Line', 'Diamond'])
f2.add_argument('--fleet_step_2', default=default('--fleet_step_2'), choices=['1', '2', '3', '4', '5', '6'], help='In event map, fleet has limit on moving, so fleet_step is how far can a fleet goes in one operation, if map cleared, it will be ignored')
f3 = fleet.add_argument_group('Alternate Road Fleet')
f3 = fleet.add_argument_group('Alternate Mob Fleet')
f3.add_argument('--fleet_index_3', default=default('--fleet_index_3'), choices=['do_not_use', '1', '2', '3', '4', '5', '6'])
f3.add_argument('--fleet_formation_3', default=default('--fleet_formation_3'), choices=['Line Ahead', 'Double Line', 'Diamond'])
f3.add_argument('--fleet_step_3', default=default('--fleet_step_3'), choices=['1', '2', '3', '4', '5', '6'], help='In event map, fleet has limit on moving, so fleet_step is how far can a fleet goes in one operation, if map cleared, it will be ignored')
@ -152,7 +152,7 @@ def main(ini_name=''):
f4.add_argument('--combat_auto_mode', default=default('--combat_auto_mode'), choices=['combat_auto', 'combat_manual', 'stand_still_in_the_middle'])
# 潜艇设置
submarine = setting_parser.add_argument_group('Submarine settings', 'Only supported: hunt_only, do_not_use every_combat')
submarine = setting_parser.add_argument_group('Submarine settings', 'Only supported: hunt_only, do_not_use and every_combat')
submarine.add_argument('--fleet_index_4', default=default('--fleet_index_4'), choices=['do_not_use', '1', '2'])
submarine.add_argument('--submarine_mode', default=default('--submarine_mode'), choices=['do_not_use', 'hunt_only', 'every_combat', 'when_no_ammo', 'when_boss_combat', 'when_boss_combat_boss_appear'])
@ -161,7 +161,7 @@ def main(ini_name=''):
emotion.add_argument('--enable_emotion_reduce', default=default('--enable_emotion_reduce'), choices=['yes', 'no'])
emotion.add_argument('--ignore_low_emotion_warn', default=default('--ignore_low_emotion_warn'), choices=['yes', 'no'])
e1 = emotion.add_argument_group('Road Fleet')
e1 = emotion.add_argument_group('Mob Fleet')
e1.add_argument('--emotion_recover_1', default=default('--emotion_recover_1'), choices=['not_in_dormitory', 'dormitory_floor_1', 'dormitory_floor_2'])
e1.add_argument('--emotion_control_1', default=default('--emotion_control_1'), choices=['keep_high_emotion', 'avoid_green_face', 'avoid_yellow_face', 'avoid_red_face'])
e1.add_argument('--hole_fleet_married_1', default=default('--hole_fleet_married_1'), choices=['yes', 'no'])
@ -171,7 +171,7 @@ def main(ini_name=''):
e2.add_argument('--emotion_control_2', default=default('--emotion_control_2'), choices=['keep_high_emotion', 'avoid_green_face', 'avoid_yellow_face', 'avoid_red_face'])
e2.add_argument('--hole_fleet_married_2', default=default('--hole_fleet_married_2'), choices=['yes', 'no'])
e3 = emotion.add_argument_group('Alternate Road Fleet', 'Will be used when the home team triggers mood control')
e3 = emotion.add_argument_group('Alternate Mob Fleet', 'Will be used when the first team triggers mood control')
e3.add_argument('--emotion_recover_3', default=default('--emotion_recover_3'), choices=['not_in_dormitory', 'dormitory_floor_1', 'dormitory_floor_2'])
e3.add_argument('--emotion_control_3', default=default('--emotion_control_3'), choices=['keep_high_emotion', 'avoid_green_face', 'avoid_yellow_face', 'avoid_red_face'])
e3.add_argument('--hole_fleet_married_3', default=default('--hole_fleet_married_3'), choices=['yes', 'no'])
@ -182,7 +182,7 @@ def main(ini_name=''):
hp.add_argument('--enable_low_hp_withdraw', default=default('--enable_low_hp_withdraw'), choices=['yes', 'no'])
hp_balance = hp.add_argument_group('HP Balance', '')
hp_balance.add_argument('--scout_hp_difference_threshold', default=default('--scout_hp_difference_threshold'), help='When the difference in HP volume is greater than the threshold, transpose')
hp_balance.add_argument('--scout_hp_weights', default=default('--scout_hp_weights'), help='Pioneer flesh should be modified when there is a difference, the format 1000,1000,1000')
hp_balance.add_argument('--scout_hp_weights', default=default('--scout_hp_weights'), help='Should be repaired when there is a difference in Vanguard, format 1000,1000,1000')
hp_add = hp.add_argument_group('Emergency repair', '')
hp_add.add_argument('--emergency_repair_single_threshold', default=default('--emergency_repair_single_threshold'), help='Used when single shipgirl is below the threshold')
hp_add.add_argument('--emergency_repair_hole_threshold', default=default('--emergency_repair_hole_threshold'), help='Used when all front rows or all back rows are below the threshold')
@ -250,23 +250,23 @@ def main(ini_name=''):
priority4.add_argument('--urgent_part', default=default('--urgent_part'), help='Support Vila Vela Island, support terror Banner')
priority4.add_argument('--urgent_cube', default=default('--urgent_cube'), help='Rescue merchant ship, enemy attack')
priority4.add_argument('--urgent_book', default=default('--urgent_book'), help='Support Tuhaoer Island, support Moe Island')
priority4.add_argument('--urgent_box', default=default('--urgent_box'), help='BIW equipment transportation, NYB equipment research and development')
priority4.add_argument('--urgent_gem', default=default('--urgent_gem'), help='BIW VIP guard, NYB patrol guard')
priority4.add_argument('--urgent_ship', default=default('--urgent_ship'), help='Small ship watching ceremony, alliance ship watching ceremony')
priority4.add_argument('--urgent_box', default=default('--urgent_box'), help='BIW Gear Transport, NYB Gear Transport')
priority4.add_argument('--urgent_gem', default=default('--urgent_gem'), help='BIW VIP Escort, NYB VIP Escort')
priority4.add_argument('--urgent_ship', default=default('--urgent_ship'), help='Small Launch Ceremony, Fleet Launch Ceremony, Alliance Launch Ceremony')
reward_tactical = reward_parser.add_argument_group('Classroom', 'Only support continuation of skill books, not new skills')
reward_tactical.add_argument('--enable_tactical_reward', default=default('--enable_tactical_reward'), choices=['yes', 'no'])
reward_tactical.add_argument('--tactical_night_range', default=default('--tactical_night_range'), help='Format 23:30-06:30')
reward_tactical.add_argument('--tactical_book_tier', default=default('--tactical_book_tier'), choices=['3', '2', '1'], help='Skill book that uses at most T a few \nT3 is a gold book, T2 is a purple book, T1 is a blue book')
reward_tactical.add_argument('--tactical_book_tier', default=default('--tactical_book_tier'), choices=['3', '2', '1'], help='Wich skill book will use first\nT3 is a gold book, T2 is a purple book, T1 is a blue book')
reward_tactical.add_argument('--tactical_exp_first', default=default('--tactical_exp_first'), choices=['yes', 'no'], help='Choose Yes, give priority to the 150% bonus \nSelect No, give priority to the skills book with the same rarity')
reward_tactical.add_argument('--tactical_book_tier_night', default=default('--tactical_book_tier_night'), choices=['3', '2', '1'])
reward_tactical.add_argument('--tactical_exp_first_night', default=default('--tactical_exp_first_night'), choices=['yes', 'no'])
# ==========emulator==========
emulator_parser = subs.add_parser('emulator')
emulator = emulator_parser.add_argument_group('Emulator', 'Need to run once to save the options, it will check whether the game is started \nIf the game is started, trigger a collection')
emulator = emulator_parser.add_argument_group('Emulator', 'Need to run once to save the options, it will check whether the game is started \nIf the game has not started, it will be started')
emulator.add_argument('--serial', default=default('--serial'), help='Bluestacks 127.0.0.1:5555 \nNox 127.0.0.1:62001')
emulator.add_argument('--package_name', default=default('--package_name'), help='')
emulator.add_argument('--package_name', default='com.YoStarEN.AzurLane', help='')
debug = emulator_parser.add_argument_group('Debug settings', '')
debug.add_argument('--enable_error_log_and_screenshot_save', default=default('--enable_error_log_and_screenshot_save'), choices=['yes', 'no'])
@ -274,7 +274,7 @@ def main(ini_name=''):
adb = emulator_parser.add_argument_group('ADB settings', '')
adb.add_argument('--use_adb_screenshot', default=default('--use_adb_screenshot'), choices=['yes', 'no'], help='It is recommended to enable it to reduce CPU usage')
adb.add_argument('--use_adb_control', default=default('--use_adb_control'), choices=['yes', 'no'], help='Suggest to close, can speed up the click speed')
adb.add_argument('--use_adb_control', default=default('--use_adb_control'), choices=['yes', 'no'], help='Recommended off, can speed up the click speed')
adb.add_argument('--combat_screenshot_interval', default=default('--combat_screenshot_interval'), help='Slow down the screenshot speed during battle and reduce CPU')
# ==========每日任务==========
@ -296,7 +296,7 @@ def main(ini_name=''):
daily_task.add_argument('--daily_equipment', default=default('--daily_equipment'), help='Change equipment before playing, unload equipment after playing, do not need to fill in 0 \ncomma, such as 3, 1, 0, 1, 1, 0')
# 困难设置
hard = daily_parser.add_argument_group('Difficult setting', 'Need to turn on weekly mode, only support 10-4 temporarily')
hard = daily_parser.add_argument_group('Difficult setting', 'Need to turn on weekly mode, only support 10-1, 10-2 and 10-4 temporarily')
hard.add_argument('--hard_campaign', default=default('--hard_campaign'), help='For example 10-4')
hard.add_argument('--hard_fleet', default=default('--hard_fleet'), choices=['1', '2'])
hard.add_argument('--hard_equipment', default=default('--hard_equipment'), help='Change equipment before playing, unload equipment after playing, do not need to fill in 0 \ncomma, such as 3, 1, 0, 1, 1, 0')
@ -311,14 +311,15 @@ def main(ini_name=''):
exercise.add_argument('--exercise_equipment', default=default('--exercise_equipment'), help='Change equipment before playing, unload equipment after playing, do not need to fill in 0 \ncomma, such as 3, 1, 0, 1, 1, 0')
# ==========event_daily_ab==========
event_ab_parser = subs.add_parser('event_daily_ab')
event_ab_parser = subs.add_parser('event_daily_bonus')
event_name = event_ab_parser.add_argument_group('Choose an event', 'bonus for first clear each day')
event_name.add_argument('--event_name_ab', default=default('--event_name_ab'), choices=event_folder, help='There a dropdown menu with many options')
# event_name.add_argument('--enable_hard_bonus', default=default('--enable_hard_bonus'), choices=['yes', 'no'], help='Will enable Daily bonus for Event hard maps') # Trying implement all event maps
# ==========main==========
main_parser = subs.add_parser('main')
# 选择关卡
stage = main_parser.add_argument_group('Choose a level', 'The main line chart strikes, currently only supports the first six chapters and 7-2')
stage = main_parser.add_argument_group('Choose a level', 'Main campaign, currently only supports the first six chapters and 7-2')
stage.add_argument('--main_stage', default=default('--main_stage'), help='E.g 7-2')
# ==========event==========
@ -356,9 +357,9 @@ def main(ini_name=''):
# ==========c124_leveling==========
c_12_4_parser = subs.add_parser('c12-4_leveling')
c_12_4 = c_12_4_parser.add_argument_group('12-4 Search enemy settings', 'Need to ensure that the team has a certain strength')
c_12_4.add_argument('--non_s3_enemy_enter_tolerance', default=default('--non_s3_enemy_enter_tolerance'), choices=['0', '1', '2'], help='Endure how many battles to enter without big')
c_12_4.add_argument('--non_s3_enemy_enter_tolerance', default=default('--non_s3_enemy_enter_tolerance'), choices=['0', '1', '2'], help='Avoid enemy too strong')
c_12_4.add_argument('--non_s3_enemy_withdraw_tolerance', default=default('--non_s3_enemy_withdraw_tolerance'), choices=['0', '1', '2', '10'], help='How many battles will be fought after there is no large scale')
c_12_4.add_argument('--ammo_pick_up_124', default=default('--ammo_pick_up_124'), choices=['2', '3', '4', '5'], help='How much ammunition to pick after the war')
c_12_4.add_argument('--ammo_pick_up_124', default=default('--ammo_pick_up_124'), choices=['2', '3', '4', '5'], help='How much ammunition to pick after the battle')
args = parser.parse_args()

View File

@ -28,7 +28,7 @@ dic_true_eng_to_eng = {
'reward': 'reward',
'emulator': 'emulator',
'daily': 'daily',
'event_daily_ab': 'event_daily_ab',
'event_daily_bonus': 'event_daily_ab',
'main': 'main',
'event': 'event',
'semi_auto': 'semi_auto',
@ -162,7 +162,7 @@ dic_true_eng_to_eng = {
'no': 'no',
'Line Ahead': 'formation_1',
'Double Line': 'formation_2',
'formation_3': 'formation_3',
'Diamond': 'formation_3',
'combat_auto': 'combat_auto',
'combat_manual': 'combat_manual',
'stand_still_in_the_middle': 'stand_still_in_the_middle',

View File

@ -3,6 +3,8 @@ from module.campaign.run import CampaignRun
RECORD_SINCE = (0,)
CAMPAIGN_NAME = ['a1', 'a2', 'a3', 'b1', 'b2', 'b3']
# CAMPAIGN_ALL = ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3', 'd1', 'd2', 'd3'] # Trying implement all event maps
class CampaignAB(CampaignRun):

View File

@ -20,10 +20,10 @@ INFO_BAR_2 = Button(area={'cn': (194, 234, 1086, 236), 'en': (194, 234, 1086, 23
INFO_BAR_3 = Button(area={'cn': (194, 171, 1086, 173), 'en': (194, 171, 1086, 173)}, color={'cn': (107, 158, 255), 'en': (107, 158, 255)}, button={'cn': (194, 171, 1086, 173), 'en': (194, 171, 1086, 173)}, file={'cn': './assets/cn/handler/INFO_BAR_3.png', 'en': './assets/en/handler/INFO_BAR_3.png'})
IN_MAP = Button(area={'cn': (749, 654, 921, 707), 'en': (748, 652, 922, 702)}, color={'cn': (213, 124, 124), 'en': (211, 124, 124)}, button={'cn': (749, 654, 921, 707), 'en': (748, 652, 922, 702)}, file={'cn': './assets/cn/handler/IN_MAP.png', 'en': './assets/en/handler/IN_MAP.png'})
IN_STAGE = Button(area={'cn': (122, 16, 172, 39), 'en': (120, 18, 208, 40)}, color={'cn': (149, 167, 207), 'en': (104, 118, 157)}, button={'cn': (122, 16, 172, 39), 'en': (120, 18, 208, 40)}, file={'cn': './assets/cn/handler/IN_STAGE.png', 'en': './assets/en/handler/IN_STAGE.png'})
LOGIN_ANNOUNCE = Button(area={'cn': (1160, 45, 1227, 90), 'en': (1160, 45, 1227, 90)}, color={'cn': (174, 61, 56), 'en': (174, 61, 56)}, button={'cn': (1160, 45, 1227, 90), 'en': (1160, 45, 1227, 90)}, file={'cn': './assets/cn/handler/LOGIN_ANNOUNCE.png', 'en': './assets/en/handler/LOGIN_ANNOUNCE.png'})
LOGIN_ANNOUNCE = Button(area={'cn': (1160, 45, 1227, 90), 'en': (1159, 44, 1228, 91)}, color={'cn': (174, 61, 56), 'en': (193, 79, 73)}, button={'cn': (1160, 45, 1227, 90), 'en': (1159, 44, 1228, 91)}, file={'cn': './assets/cn/handler/LOGIN_ANNOUNCE.png', 'en': './assets/en/handler/LOGIN_ANNOUNCE.png'})
LOGIN_CHECK = Button(area={'cn': (77, 655, 154, 711), 'en': (77, 655, 154, 711)}, color={'cn': (33, 36, 33), 'en': (33, 36, 33)}, button={'cn': (416, 294, 534, 400), 'en': (416, 294, 534, 400)}, file={'cn': './assets/cn/handler/LOGIN_CHECK.png', 'en': './assets/en/handler/LOGIN_CHECK.png'})
LOGIN_GAME_UPDATE = Button(area={'cn': (700, 471, 873, 529), 'en': (700, 471, 873, 529)}, color={'cn': (238, 170, 78), 'en': (238, 170, 78)}, button={'cn': (700, 471, 873, 529), 'en': (700, 471, 873, 529)}, file={'cn': './assets/cn/handler/LOGIN_GAME_UPDATE.png', 'en': './assets/en/handler/LOGIN_GAME_UPDATE.png'})
LOGIN_RETURN_SIGN = Button(area={'cn': (1, 7, 104, 47), 'en': (1, 7, 104, 47)}, color={'cn': (158, 214, 229), 'en': (158, 214, 229)}, button={'cn': (1, 7, 104, 47), 'en': (1, 7, 104, 47)}, file={'cn': './assets/cn/handler/LOGIN_RETURN_SIGN.png', 'en': './assets/en/handler/LOGIN_RETURN_SIGN.png'})
LOGIN_RETURN_SIGN = Button(area={'cn': (1, 7, 104, 47), 'en': (1195, 524, 1272, 547)}, color={'cn': (158, 214, 229), 'en': (202, 202, 203)}, button={'cn': (1, 7, 104, 47), 'en': (1195, 524, 1272, 547)}, file={'cn': './assets/cn/handler/LOGIN_RETURN_SIGN.png', 'en': './assets/en/handler/LOGIN_RETURN_SIGN.png'})
MAP_AIR_RAID = Button(area={'cn': (350, 447, 1280, 472), 'en': (350, 447, 1280, 472)}, color={'cn': (154, 43, 46), 'en': (154, 43, 46)}, button={'cn': (350, 447, 1280, 472), 'en': (350, 447, 1280, 472)}, file={'cn': './assets/cn/handler/MAP_AIR_RAID.png', 'en': './assets/en/handler/MAP_AIR_RAID.png'})
MAP_AMBUSH = Button(area={'cn': (261, 433, 1280, 449), 'en': (261, 433, 1280, 449)}, color={'cn': (161, 41, 43), 'en': (161, 41, 43)}, button={'cn': (261, 433, 1280, 449), 'en': (261, 433, 1280, 449)}, file={'cn': './assets/cn/handler/MAP_AMBUSH.png', 'en': './assets/en/handler/MAP_AMBUSH.png'})
MAP_AMBUSH_EVADE = Button(area={'cn': (325, 393, 1280, 395), 'en': (325, 393, 1280, 395)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255)}, button={'cn': (979, 444, 1152, 502), 'en': (979, 444, 1152, 502)}, file={'cn': './assets/cn/handler/MAP_AMBUSH_EVADE.png', 'en': './assets/en/handler/MAP_AMBUSH_EVADE.png'})
@ -34,7 +34,7 @@ MAP_GREEN = Button(area={'cn': (195, 260, 349, 292), 'en': (201, 259, 341, 290)}
MAP_STAR_1 = Button(area={'cn': (245, 377, 254, 384), 'en': (232, 381, 240, 389)}, color={'cn': (251, 233, 143), 'en': (252, 234, 146)}, button={'cn': (245, 377, 254, 384), 'en': (232, 381, 240, 389)}, file={'cn': './assets/cn/handler/MAP_STAR_1.png', 'en': './assets/en/handler/MAP_STAR_1.png'})
MAP_STAR_2 = Button(area={'cn': (532, 377, 540, 384), 'en': (518, 382, 526, 389)}, color={'cn': (251, 233, 144), 'en': (252, 234, 144)}, button={'cn': (532, 377, 540, 384), 'en': (518, 382, 526, 389)}, file={'cn': './assets/cn/handler/MAP_STAR_2.png', 'en': './assets/en/handler/MAP_STAR_2.png'})
MAP_STAR_3 = Button(area={'cn': (818, 377, 827, 384), 'en': (804, 382, 812, 389)}, color={'cn': (251, 233, 143), 'en': (252, 234, 144)}, button={'cn': (818, 377, 827, 384), 'en': (804, 382, 812, 389)}, file={'cn': './assets/cn/handler/MAP_STAR_3.png', 'en': './assets/en/handler/MAP_STAR_3.png'})
MAP_WALK_OUT_OF_STEP = Button(area={'cn': (654, 312, 704, 335), 'en': (654, 312, 704, 335)}, color={'cn': (109, 113, 120), 'en': (109, 113, 120)}, button={'cn': (654, 312, 704, 335), 'en': (654, 312, 704, 335)}, file={'cn': './assets/cn/handler/MAP_WALK_OUT_OF_STEP.png', 'en': './assets/en/handler/MAP_WALK_OUT_OF_STEP.png'})
MAP_WALK_OUT_OF_STEP = Button(area={'cn': (654, 312, 704, 335), 'en': (654, 312, 704, 335)}, color={'cn': (109, 113, 120), 'en': (112, 113, 120)}, button={'cn': (654, 312, 704, 335), 'en': (654, 312, 704, 335)}, file={'cn': './assets/cn/handler/MAP_WALK_OUT_OF_STEP.png', 'en': './assets/en/handler/MAP_WALK_OUT_OF_STEP.png'})
MYSTERY_ITEM = Button(area={'cn': (589, 294, 691, 427), 'en': (589, 294, 691, 427)}, color={'cn': (144, 127, 83), 'en': (144, 127, 83)}, button={'cn': (589, 294, 691, 427), 'en': (589, 294, 691, 427)}, file={'cn': './assets/cn/handler/MYSTERY_ITEM.png', 'en': './assets/en/handler/MYSTERY_ITEM.png'})
POPUP_CANCEL = Button(area={'cn': (404, 493, 576, 550), 'en': (403, 481, 577, 541)}, color={'cn': (166, 169, 172), 'en': (170, 172, 174)}, button={'cn': (404, 493, 576, 550), 'en': (403, 481, 577, 541)}, file={'cn': './assets/cn/handler/POPUP_CANCEL.png', 'en': './assets/en/handler/POPUP_CANCEL.png'})
POPUP_CONFIRM = Button(area={'cn': (704, 493, 876, 550), 'en': (703, 481, 877, 541)}, color={'cn': (94, 144, 204), 'en': (107, 152, 207)}, button={'cn': (704, 493, 876, 550), 'en': (703, 481, 877, 541)}, file={'cn': './assets/cn/handler/POPUP_CONFIRM.png', 'en': './assets/en/handler/POPUP_CONFIRM.png'})

View File

@ -12,10 +12,10 @@ COMMISSION_NOTICE_AT_CAMPAIGN = Button(area={'cn': (1054, 647, 1061, 654), 'en':
COMMISSION_START = Button(area={'cn': (1028, 322, 1156, 383), 'en': (1031, 333, 1147, 383)}, color={'cn': (229, 175, 113), 'en': (227, 169, 108)}, button={'cn': (1028, 322, 1156, 383), 'en': (1031, 333, 1147, 383)}, file={'cn': './assets/cn/reward/COMMISSION_START.png', 'en': './assets/en/reward/COMMISSION_START.png'})
COMMISSION_STOP_SCROLLING = Button(area={'cn': (115, 236, 179, 487), 'en': (115, 236, 179, 487)}, color={'cn': (50, 55, 74), 'en': (50, 55, 74)}, button={'cn': (115, 236, 179, 487), 'en': (115, 236, 179, 487)}, file={'cn': './assets/cn/reward/COMMISSION_STOP_SCROLLING.png', 'en': './assets/en/reward/COMMISSION_STOP_SCROLLING.png'})
COMMISSION_URGENT = Button(area={'cn': (35, 231, 68, 281), 'en': (28, 221, 76, 283)}, color={'cn': (215, 188, 124), 'en': (169, 138, 95)}, button={'cn': (35, 231, 68, 281), 'en': (28, 221, 76, 283)}, file={'cn': './assets/cn/reward/COMMISSION_URGENT.png', 'en': './assets/en/reward/COMMISSION_URGENT.png'})
EXP_INFO_S_REWARD = Button(area={'cn': (498, 140, 557, 154), 'en': (498, 140, 557, 154)}, color={'cn': (233, 241, 127), 'en': (105, 119, 139)}, button={'cn': (498, 140, 557, 154), 'en': (498, 140, 557, 154)}, file={'cn': './assets/cn/reward/EXP_INFO_S_REWARD.png', 'en': './assets/en/reward/EXP_INFO_S_REWARD.png'})
MISSION_MULTI = Button(area={'cn': (1041, 8, 1101, 39), 'en': (1041, 8, 1101, 39)}, color={'cn': (226, 192, 142), 'en': (226, 192, 142)}, button={'cn': (1041, 8, 1101, 39), 'en': (1041, 8, 1101, 39)}, file={'cn': './assets/cn/reward/MISSION_MULTI.png', 'en': './assets/en/reward/MISSION_MULTI.png'})
EXP_INFO_S_REWARD = Button(area={'cn': (498, 140, 557, 154), 'en': (477, 132, 521, 144)}, color={'cn': (233, 241, 127), 'en': (233, 241, 126)}, button={'cn': (498, 140, 557, 154), 'en': (477, 132, 521, 144)}, file={'cn': './assets/cn/reward/EXP_INFO_S_REWARD.png', 'en': './assets/en/reward/EXP_INFO_S_REWARD.png'})
MISSION_MULTI = Button(area={'cn': (1041, 8, 1101, 39), 'en': (1041, 8, 1101, 39)}, color={'cn': (226, 192, 142), 'en': (221, 179, 96)}, button={'cn': (1041, 8, 1101, 39), 'en': (1041, 8, 1101, 39)}, file={'cn': './assets/cn/reward/MISSION_MULTI.png', 'en': './assets/en/reward/MISSION_MULTI.png'})
MISSION_NOTICE = Button(area={'cn': (940, 670, 945, 681), 'en': (940, 670, 945, 681)}, color={'cn': (183, 83, 66), 'en': (183, 83, 66)}, button={'cn': (940, 670, 945, 681), 'en': (940, 670, 945, 681)}, file={'cn': './assets/cn/reward/MISSION_NOTICE.png', 'en': './assets/en/reward/MISSION_NOTICE.png'})
MISSION_SINGLE = Button(area={'cn': (1093, 118, 1179, 177), 'en': (1093, 118, 1179, 177)}, color={'cn': (115, 155, 218), 'en': (115, 155, 218)}, button={'cn': (1093, 118, 1179, 177), 'en': (1093, 118, 1179, 177)}, file={'cn': './assets/cn/reward/MISSION_SINGLE.png', 'en': './assets/en/reward/MISSION_SINGLE.png'})
MISSION_SINGLE = Button(area={'cn': (1093, 118, 1179, 177), 'en': (1093, 118, 1179, 177)}, color={'cn': (115, 155, 218), 'en': (106, 147, 215)}, button={'cn': (1093, 118, 1179, 177), 'en': (1093, 118, 1179, 177)}, file={'cn': './assets/cn/reward/MISSION_SINGLE.png', 'en': './assets/en/reward/MISSION_SINGLE.png'})
OIL = Button(area={'cn': (162, 64, 182, 91), 'en': (162, 64, 182, 91)}, color={'cn': (71, 72, 71), 'en': (71, 72, 71)}, button={'cn': (162, 64, 182, 91), 'en': (162, 64, 182, 91)}, file={'cn': './assets/cn/reward/OIL.png', 'en': './assets/en/reward/OIL.png'})
REWARD_1 = Button(area={'cn': (383, 285, 503, 297), 'en': (383, 283, 503, 294)}, color={'cn': (238, 168, 81), 'en': (241, 187, 120)}, button={'cn': (383, 285, 503, 297), 'en': (383, 283, 503, 294)}, file={'cn': './assets/cn/reward/REWARD_1.png', 'en': './assets/en/reward/REWARD_1.png'})
REWARD_2 = Button(area={'cn': (383, 404, 503, 444), 'en': (382, 403, 504, 445)}, color={'cn': (233, 165, 67), 'en': (236, 177, 92)}, button={'cn': (383, 404, 503, 444), 'en': (382, 403, 504, 445)}, file={'cn': './assets/cn/reward/REWARD_2.png', 'en': './assets/en/reward/REWARD_2.png'})

View File

@ -37,7 +37,7 @@ dictionary_en = {
'extra_drill': ['Sailing', 'Defense Patrol', 'Buoy'],
'extra_part': ['veinprotectoncommisionll', 'Forestprtectoncommisionl', 'Forestprotectoncommisionll'],
'extra_cube': ['Exercise'],
'extra_oil': ['Large-saleoilExtractionlll', 'FleetCargoTransport', 'Large-saleoilExtractianl', 'Large-saleoilExtractionl', 'Large-saleoilExtractiaonll'],
'extra_oil': ['oilextraction', 'FleetCargoTransport', 'oilExtractianl', '', 'oilExtractiaonll'],
'extra_book': ['LargeMerchantEscort'],
'urgent_drill': ['Cargo Defense', 'Scouts', 'Force', 'Elites', 'FrontierDefensePatrol'],
'urgent_part': ['Lavella', 'Maui', 'Rendova', 'AidingWongbanna'],
@ -181,7 +181,7 @@ class Commission:
Returns:
timedelta: datetime.timedelta instance.
"""
string = string.replace('D', '0') # Poor OCR
string = string.replace('D', '0').replace(' ', '').replace('-', '') # Poor OCR
result = re.search('(\d+):(\d+):(\d+)', string)
if not result:
logger.warning(f'Invalid time string: {string}')

View File

@ -12,7 +12,7 @@ CAMPAIGN_GOTO_EXERCISE = Button(area={'cn': (1166, 648, 1248, 703), 'en': (1150,
COMMISSION_CHECK = Button(area={'cn': (122, 16, 175, 39), 'en': (120, 14, 301, 41)}, color={'cn': (157, 173, 210), 'en': (98, 112, 150)}, button={'cn': (122, 16, 175, 39), 'en': (120, 14, 301, 41)}, file={'cn': './assets/cn/ui/COMMISSION_CHECK.png', 'en': './assets/en/ui/COMMISSION_CHECK.png'})
DAILY_CHECK = Button(area={'cn': (23, 656, 67, 698), 'en': (23, 656, 67, 698)}, color={'cn': (84, 139, 210), 'en': (84, 139, 210)}, button={'cn': (23, 656, 67, 698), 'en': (23, 656, 67, 698)}, file={'cn': './assets/cn/ui/DAILY_CHECK.png', 'en': './assets/en/ui/DAILY_CHECK.png'})
EVENT_CHECK = Button(area={'cn': (123, 63, 206, 109), 'en': (123, 63, 206, 109)}, color={'cn': (88, 104, 138), 'en': (88, 104, 138)}, button={'cn': (123, 63, 206, 109), 'en': (123, 63, 206, 109)}, file={'cn': './assets/cn/ui/EVENT_CHECK.png', 'en': './assets/en/ui/EVENT_CHECK.png'})
EVENT_LIST_CHECK = Button(area={'cn': (123, 15, 232, 39), 'en': (123, 15, 232, 39)}, color={'cn': (151, 166, 206), 'en': (151, 166, 206)}, button={'cn': (123, 15, 232, 39), 'en': (123, 15, 232, 39)}, file={'cn': './assets/cn/ui/EVENT_LIST_CHECK.png', 'en': './assets/en/ui/EVENT_LIST_CHECK.png'})
EVENT_LIST_CHECK = Button(area={'cn': (123, 15, 232, 39), 'en': (123, 15, 223, 39)}, color={'cn': (151, 166, 206), 'en': (109, 122, 163)}, button={'cn': (123, 15, 232, 39), 'en': (123, 15, 223, 39)}, file={'cn': './assets/cn/ui/EVENT_LIST_CHECK.png', 'en': './assets/en/ui/EVENT_LIST_CHECK.png'})
EXERCISE_CHECK = Button(area={'cn': (1065, 340, 1204, 382), 'en': (1078, 343, 1190, 382)}, color={'cn': (129, 166, 220), 'en': (138, 174, 225)}, button={'cn': (1065, 340, 1204, 382), 'en': (1078, 343, 1190, 382)}, file={'cn': './assets/cn/ui/EXERCISE_CHECK.png', 'en': './assets/en/ui/EXERCISE_CHECK.png'})
FLEET_CHECK = Button(area={'cn': (1044, 641, 1243, 702), 'en': (1042, 640, 1244, 703)}, color={'cn': (237, 186, 112), 'en': (236, 187, 113)}, button={'cn': (1044, 641, 1243, 702), 'en': (1042, 640, 1244, 703)}, file={'cn': './assets/cn/ui/FLEET_CHECK.png', 'en': './assets/en/ui/FLEET_CHECK.png'})
GOTO_MAIN = Button(area={'cn': (1230, 17, 1253, 45), 'en': (1230, 17, 1253, 45)}, color={'cn': (112, 132, 159), 'en': (112, 132, 159)}, button={'cn': (1228, 18, 1255, 49), 'en': (1228, 18, 1255, 49)}, file={'cn': './assets/cn/ui/GOTO_MAIN.png', 'en': './assets/en/ui/GOTO_MAIN.png'})