empire-core 0.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- empire_core/__init__.py +36 -0
- empire_core/_archive/actions.py +511 -0
- empire_core/_archive/automation/__init__.py +24 -0
- empire_core/_archive/automation/alliance_tools.py +266 -0
- empire_core/_archive/automation/battle_reports.py +196 -0
- empire_core/_archive/automation/building_queue.py +242 -0
- empire_core/_archive/automation/defense_manager.py +124 -0
- empire_core/_archive/automation/map_scanner.py +370 -0
- empire_core/_archive/automation/multi_account.py +296 -0
- empire_core/_archive/automation/quest_automation.py +94 -0
- empire_core/_archive/automation/resource_manager.py +380 -0
- empire_core/_archive/automation/target_finder.py +153 -0
- empire_core/_archive/automation/tasks.py +224 -0
- empire_core/_archive/automation/unit_production.py +719 -0
- empire_core/_archive/cli.py +68 -0
- empire_core/_archive/client_async.py +469 -0
- empire_core/_archive/commands.py +201 -0
- empire_core/_archive/connection_async.py +228 -0
- empire_core/_archive/defense.py +156 -0
- empire_core/_archive/events/__init__.py +35 -0
- empire_core/_archive/events/base.py +153 -0
- empire_core/_archive/events/manager.py +85 -0
- empire_core/accounts.py +190 -0
- empire_core/client/__init__.py +0 -0
- empire_core/client/client.py +459 -0
- empire_core/config.py +87 -0
- empire_core/exceptions.py +42 -0
- empire_core/network/__init__.py +0 -0
- empire_core/network/connection.py +378 -0
- empire_core/protocol/__init__.py +0 -0
- empire_core/protocol/models/__init__.py +339 -0
- empire_core/protocol/models/alliance.py +186 -0
- empire_core/protocol/models/army.py +444 -0
- empire_core/protocol/models/attack.py +229 -0
- empire_core/protocol/models/auth.py +216 -0
- empire_core/protocol/models/base.py +403 -0
- empire_core/protocol/models/building.py +455 -0
- empire_core/protocol/models/castle.py +317 -0
- empire_core/protocol/models/chat.py +150 -0
- empire_core/protocol/models/defense.py +300 -0
- empire_core/protocol/models/map.py +269 -0
- empire_core/protocol/packet.py +104 -0
- empire_core/services/__init__.py +31 -0
- empire_core/services/alliance.py +222 -0
- empire_core/services/base.py +107 -0
- empire_core/services/castle.py +221 -0
- empire_core/state/__init__.py +0 -0
- empire_core/state/manager.py +398 -0
- empire_core/state/models.py +215 -0
- empire_core/state/quest_models.py +60 -0
- empire_core/state/report_models.py +115 -0
- empire_core/state/unit_models.py +75 -0
- empire_core/state/world_models.py +269 -0
- empire_core/storage/__init__.py +1 -0
- empire_core/storage/database.py +237 -0
- empire_core/utils/__init__.py +0 -0
- empire_core/utils/battle_sim.py +172 -0
- empire_core/utils/calculations.py +170 -0
- empire_core/utils/crypto.py +8 -0
- empire_core/utils/decorators.py +69 -0
- empire_core/utils/enums.py +111 -0
- empire_core/utils/helpers.py +252 -0
- empire_core/utils/response_awaiter.py +153 -0
- empire_core/utils/troops.py +93 -0
- empire_core-0.7.3.dist-info/METADATA +197 -0
- empire_core-0.7.3.dist-info/RECORD +67 -0
- empire_core-0.7.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit production and army management automation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import IntEnum
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from empire_core.state.models import Castle
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from empire_core.client.client import EmpireClient
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UnitType(IntEnum):
|
|
20
|
+
"""Common unit type IDs."""
|
|
21
|
+
|
|
22
|
+
VETERANSABERSLASHER = 5
|
|
23
|
+
VETERANSLINGSHOTMARKSMAN = 6
|
|
24
|
+
OGERMACE_2 = 7
|
|
25
|
+
OGERCROSSBOW_2 = 8
|
|
26
|
+
ELITERANKREWARDMELEE = 9
|
|
27
|
+
ELITERANKREWARDRANGE = 10
|
|
28
|
+
ELITEFLAMETHROWER = 11
|
|
29
|
+
ELITEARROWTHROWER = 12
|
|
30
|
+
AUXILIARYMELEE = 13
|
|
31
|
+
AUXILIARYRANGE = 14
|
|
32
|
+
BERIMONDREWARDMELEE = 18
|
|
33
|
+
BERIMONDREWARDRANGE = 19
|
|
34
|
+
ELITEBERIMONDREWARDMELEE = 20
|
|
35
|
+
ELITEBERIMONDREWARDRANGE = 21
|
|
36
|
+
VALKYRIEMILEE = 22
|
|
37
|
+
VALKYRIERANGE = 23
|
|
38
|
+
SAMURAIATTACKERMELEE = 34
|
|
39
|
+
SAMURAIATTACKERRANGE = 35
|
|
40
|
+
SAMURAIDEFENDERMELEE = 36
|
|
41
|
+
SAMURAIDEFENDERRANGE = 37
|
|
42
|
+
SAMURAIDEFENDERMELEENPC = 38
|
|
43
|
+
SAMURAIDEFENDERRANGENPC = 39
|
|
44
|
+
RENEGADESKELETONSPEERMAN = 40
|
|
45
|
+
RENEGADESKELETONBOWMAN = 41
|
|
46
|
+
HALLOWEENMELEE = 42
|
|
47
|
+
HALLOWEENRANGE = 43
|
|
48
|
+
WINTERATTACKERMELEE = 48
|
|
49
|
+
ELITETINOSWOLVES = 49
|
|
50
|
+
WINTERATTACKERRANGE = 50
|
|
51
|
+
ELITEWINTERATTACKERMELEE = 51
|
|
52
|
+
ELITEWINTERATTACKERRANGE = 52
|
|
53
|
+
RANKREWARDMELEEUSA = 58
|
|
54
|
+
RANKREWARDRANGEUSA = 59
|
|
55
|
+
OGERMACE = 68
|
|
56
|
+
OGERCROSSBOW = 74
|
|
57
|
+
ELITEKINGSMACE = 75
|
|
58
|
+
ELITEKINGSCROSSBOWMAN = 76
|
|
59
|
+
RENEGADEOGERMACE = 78
|
|
60
|
+
RENEGADEOGERCROSSBOW = 79
|
|
61
|
+
ELITESPRINGATTACKERMELEE = 83
|
|
62
|
+
ELITESPRINGATTACKERRANGE = 84
|
|
63
|
+
ELITESPRINGDEFENDERMELEE = 85
|
|
64
|
+
ELITESPRINGDEFENDERRANGE = 86
|
|
65
|
+
SHADOWRANKREWARDMELEE = 92
|
|
66
|
+
SHADOWRANKREWARDRANGE = 93
|
|
67
|
+
STPATRICKSDEFENDERMELEE = 100
|
|
68
|
+
STPATRICKSDEFENDERRANGE = 101
|
|
69
|
+
EASTERDEFENDERMELEE = 102
|
|
70
|
+
EASTERDEFENDERRANGE = 103
|
|
71
|
+
ALIENREROLLEDEFENDERMELEE = 146
|
|
72
|
+
ALIENREROLLEDEFENDERRANGE = 147
|
|
73
|
+
RELICAXE = 148
|
|
74
|
+
RELICSHORTBOW = 149
|
|
75
|
+
RELICHAMMER = 150
|
|
76
|
+
RELICLONGBOW = 151
|
|
77
|
+
MAYAMELEE = 183
|
|
78
|
+
MAYARANGE = 184
|
|
79
|
+
MAYAELITEMELEEE = 185
|
|
80
|
+
MAYAELITERANGE = 186
|
|
81
|
+
RENEGADEMAYAMELEE = 187
|
|
82
|
+
RENEGADEMAYARANGE = 188
|
|
83
|
+
RENEGADEMAYAELITEMELEEE = 189
|
|
84
|
+
RENEGADEMAYAELITERANGE = 190
|
|
85
|
+
CORRUPTEDASSASSIN = 191
|
|
86
|
+
CORRUPTEDCROSSBOWMAN = 192
|
|
87
|
+
CORRUPTEDELITEHALBERDIER = 193
|
|
88
|
+
CORRUPTEDELITELONGBOWMAN = 194
|
|
89
|
+
MEADSHIELDMAIDEN = 195
|
|
90
|
+
MEADSHIELDMAIDEN_2 = 196
|
|
91
|
+
MEADSHIELDMAIDEN_3 = 197
|
|
92
|
+
MEADSHIELDMAIDEN_4 = 198
|
|
93
|
+
MEADSHIELDMAIDEN_5 = 199
|
|
94
|
+
MEADSHIELDMAIDEN_6 = 200
|
|
95
|
+
MEADSHIELDMAIDEN_7 = 201
|
|
96
|
+
MEADSHIELDMAIDEN_8 = 202
|
|
97
|
+
MEADSHIELDMAIDEN_9 = 203
|
|
98
|
+
MEADSHIELDMAIDEN_10 = 204
|
|
99
|
+
MEADRANGER = 205
|
|
100
|
+
MEADRANGER_2 = 206
|
|
101
|
+
MEADRANGER_3 = 207
|
|
102
|
+
MEADRANGER_4 = 208
|
|
103
|
+
MEADRANGER_5 = 209
|
|
104
|
+
MEADRANGER_6 = 210
|
|
105
|
+
MEADRANGER_7 = 211
|
|
106
|
+
MEADRANGER_8 = 212
|
|
107
|
+
MEADRANGER_9 = 213
|
|
108
|
+
MEADRANGER_10 = 214
|
|
109
|
+
MEADSHIELDMAIDEN_11 = 215
|
|
110
|
+
MEADRANGER_11 = 216
|
|
111
|
+
MEADMACE = 217
|
|
112
|
+
MEADMACE_2 = 218
|
|
113
|
+
MEADMACE_3 = 219
|
|
114
|
+
MEADMACE_4 = 220
|
|
115
|
+
MEADMACE_5 = 221
|
|
116
|
+
MEADMACE_6 = 222
|
|
117
|
+
MEADMACE_7 = 223
|
|
118
|
+
MEADMACE_8 = 224
|
|
119
|
+
MEADMACE_9 = 225
|
|
120
|
+
MEADMACE_10 = 226
|
|
121
|
+
MEADMACE_11 = 227
|
|
122
|
+
MEADBOW = 228
|
|
123
|
+
MEADBOW_2 = 229
|
|
124
|
+
MEADBOW_3 = 230
|
|
125
|
+
MEADBOW_4 = 231
|
|
126
|
+
MEADBOW_5 = 232
|
|
127
|
+
MEADBOW_6 = 233
|
|
128
|
+
MEADBOW_7 = 234
|
|
129
|
+
MEADBOW_8 = 235
|
|
130
|
+
MEADBOW_9 = 236
|
|
131
|
+
MEADBOW_10 = 237
|
|
132
|
+
MEADBOW_11 = 238
|
|
133
|
+
ELITEHALBERD = 308
|
|
134
|
+
ELITETWOHANDEDSWORD = 309
|
|
135
|
+
ELITELONGBOWMAN = 311
|
|
136
|
+
ELITEHEAVYCROSSBOWMAN = 312
|
|
137
|
+
SWORDMAN = 601
|
|
138
|
+
SPEERMAN = 602
|
|
139
|
+
MACE = 603
|
|
140
|
+
HALBERD = 604
|
|
141
|
+
TWOHANDEDSWORD = 605
|
|
142
|
+
ARCHER = 606
|
|
143
|
+
CROSSBOWMAN = 607
|
|
144
|
+
BOWMAN = 608
|
|
145
|
+
HEAVYCROSSBOWMAN = 609
|
|
146
|
+
LONGBOWMAN = 610
|
|
147
|
+
SHADOWMACE = 612
|
|
148
|
+
SHADOWCROSSBOWMAN = 613
|
|
149
|
+
MILITIA = 620
|
|
150
|
+
SWORDSMAN = 621
|
|
151
|
+
SPEARMAN = 622
|
|
152
|
+
KNIGHT = 623
|
|
153
|
+
LIGHT_CAVALRY = 640
|
|
154
|
+
HEAVY_CAVALRY = 641
|
|
155
|
+
LANCER = 642
|
|
156
|
+
PEASANT = 652
|
|
157
|
+
BATTERING_RAM = 650
|
|
158
|
+
CATAPULT = 651
|
|
159
|
+
SIEGE_TOWER = 652
|
|
160
|
+
EVENTKNIGHT = 655
|
|
161
|
+
EVENTCROSSBOWMAN = 656
|
|
162
|
+
NATIVEMELEE = 657
|
|
163
|
+
NATIVERANGE = 658
|
|
164
|
+
PRINCENOOB = 659
|
|
165
|
+
MANTLET = 660
|
|
166
|
+
WALL_DEFENDER = 661
|
|
167
|
+
SKELETONSPEERMAN = 662
|
|
168
|
+
SKELETONBOWMAN = 663
|
|
169
|
+
KINGSCROSSBOWMAN = 664
|
|
170
|
+
SOLDIERS = 665
|
|
171
|
+
SHADOWTWOHANDEDSWORD = 667
|
|
172
|
+
SHADOWHEAVYCROSSBOWMAN = 668
|
|
173
|
+
SPY = 670
|
|
174
|
+
AXEVIKING = 670
|
|
175
|
+
COMMANDER = 680
|
|
176
|
+
BOWVIKING = 671
|
|
177
|
+
KINGSMACE = 672
|
|
178
|
+
DRAGONCLAWS = 673
|
|
179
|
+
DRAGONJAW = 674
|
|
180
|
+
DESERTMELEE = 675
|
|
181
|
+
DESERTRANGE = 676
|
|
182
|
+
ICEMELEE = 677
|
|
183
|
+
ICERANGE = 678
|
|
184
|
+
FIREMELEE = 679
|
|
185
|
+
FIRERANGE = 680
|
|
186
|
+
MARAUDER = 684
|
|
187
|
+
FIREDEVIL = 685
|
|
188
|
+
KINGSSPEERMAN = 686
|
|
189
|
+
KINGSBOWMAN = 687
|
|
190
|
+
DESERTEVENTMELEE = 688
|
|
191
|
+
DESERTEVENTRANGE = 689
|
|
192
|
+
ICEEVENTMELEE = 690
|
|
193
|
+
ICEEVENTRANGE = 691
|
|
194
|
+
FIREEVENTMELEE = 692
|
|
195
|
+
FIREEVENTRANGE = 693
|
|
196
|
+
COWHALBERD = 698
|
|
197
|
+
COWBOWMAN = 699
|
|
198
|
+
BLUEMELEE = 710
|
|
199
|
+
BLUERANGE = 711
|
|
200
|
+
REDMELEE = 712
|
|
201
|
+
REDRANGE = 713
|
|
202
|
+
RANKREWARDMELEE = 714
|
|
203
|
+
RANKREWARDRANGE = 715
|
|
204
|
+
PIRATESPEERMAN = 716
|
|
205
|
+
PIRATEBOWMAN = 717
|
|
206
|
+
TENTACLE = 718
|
|
207
|
+
OCTOPUSHEAD = 719
|
|
208
|
+
TINOSWOLVES = 720
|
|
209
|
+
CONAN = 721
|
|
210
|
+
SLINGSHOTMARKSMAN = 727
|
|
211
|
+
RENEGADEPIKEMAN = 728
|
|
212
|
+
RENEGADESPEARTHROWER = 729
|
|
213
|
+
CRUSADEMILEE = 753
|
|
214
|
+
CRUSADERANGE = 754
|
|
215
|
+
RENEGADEPIRATEMILEE = 759
|
|
216
|
+
RENEGADEPIRATERANGE = 760
|
|
217
|
+
ELITEMARAUDER = 765
|
|
218
|
+
ELITEFIREDEVIL = 766
|
|
219
|
+
RENEGADENATIVEMELEE = 767
|
|
220
|
+
RENEGADENATIVERANGE = 768
|
|
221
|
+
SAMURAIDEFENDERMELEENPC_2 = 820
|
|
222
|
+
SAMURAIDEFENDERMELEENPC_3 = 821
|
|
223
|
+
SAMURAIDEFENDERMELEENPC_4 = 822
|
|
224
|
+
SAMURAIDEFENDERMELEENPC_5 = 823
|
|
225
|
+
SAMURAIDEFENDERMELEENPC_6 = 824
|
|
226
|
+
SAMURAIDEFENDERMELEENPC_7 = 825
|
|
227
|
+
SAMURAIDEFENDERMELEENPC_8 = 826
|
|
228
|
+
SAMURAIDEFENDERMELEENPC_9 = 827
|
|
229
|
+
SAMURAIDEFENDERMELEENPC_10 = 828
|
|
230
|
+
SAMURAIDEFENDERMELEENPC_11 = 829
|
|
231
|
+
SAMURAIDEFENDERRANGENPC_2 = 830
|
|
232
|
+
SAMURAIDEFENDERRANGENPC_3 = 831
|
|
233
|
+
SAMURAIDEFENDERRANGENPC_4 = 832
|
|
234
|
+
SAMURAIDEFENDERRANGENPC_5 = 833
|
|
235
|
+
SAMURAIDEFENDERRANGENPC_6 = 834
|
|
236
|
+
SAMURAIDEFENDERRANGENPC_7 = 835
|
|
237
|
+
SAMURAIDEFENDERRANGENPC_8 = 836
|
|
238
|
+
SAMURAIDEFENDERRANGENPC_9 = 837
|
|
239
|
+
SAMURAIDEFENDERRANGENPC_10 = 838
|
|
240
|
+
SAMURAIDEFENDERRANGENPC_11 = 839
|
|
241
|
+
SAMURAIATTACKERMELEENPC = 860
|
|
242
|
+
SAMURAIATTACKERMELEENPC_2 = 861
|
|
243
|
+
SAMURAIATTACKERMELEENPC_3 = 862
|
|
244
|
+
SAMURAIATTACKERMELEENPC_4 = 863
|
|
245
|
+
SAMURAIATTACKERMELEENPC_5 = 864
|
|
246
|
+
SAMURAIATTACKERMELEENPC_6 = 865
|
|
247
|
+
SAMURAIATTACKERMELEENPC_7 = 866
|
|
248
|
+
SAMURAIATTACKERMELEENPC_8 = 867
|
|
249
|
+
SAMURAIATTACKERMELEENPC_9 = 868
|
|
250
|
+
SAMURAIATTACKERMELEENPC_10 = 869
|
|
251
|
+
SAMURAIATTACKERRANGENPC = 870
|
|
252
|
+
SAMURAIATTACKERRANGENPC_2 = 871
|
|
253
|
+
SAMURAIATTACKERRANGENPC_3 = 872
|
|
254
|
+
SAMURAIATTACKERRANGENPC_4 = 873
|
|
255
|
+
SAMURAIATTACKERRANGENPC_5 = 874
|
|
256
|
+
SAMURAIATTACKERRANGENPC_6 = 875
|
|
257
|
+
SAMURAIATTACKERRANGENPC_7 = 876
|
|
258
|
+
SAMURAIATTACKERRANGENPC_8 = 877
|
|
259
|
+
SAMURAIATTACKERRANGENPC_9 = 878
|
|
260
|
+
SAMURAIATTACKERRANGENPC_10 = 879
|
|
261
|
+
PIKEMAN = 900
|
|
262
|
+
PIKEMAN_2 = 901
|
|
263
|
+
PIKEMAN_3 = 902
|
|
264
|
+
PIKEMAN_4 = 903
|
|
265
|
+
PIKEMAN_5 = 904
|
|
266
|
+
PIKEMAN_6 = 905
|
|
267
|
+
PIKEMAN_7 = 906
|
|
268
|
+
PIKEMAN_8 = 907
|
|
269
|
+
PIKEMAN_9 = 908
|
|
270
|
+
PIKEMAN_10 = 909
|
|
271
|
+
SPEARTHROWER = 910
|
|
272
|
+
SPEARTHROWER_2 = 911
|
|
273
|
+
SPEARTHROWER_3 = 912
|
|
274
|
+
SPEARTHROWER_4 = 913
|
|
275
|
+
SPEARTHROWER_5 = 914
|
|
276
|
+
SPEARTHROWER_6 = 915
|
|
277
|
+
SPEARTHROWER_7 = 916
|
|
278
|
+
SPEARTHROWER_8 = 917
|
|
279
|
+
SPEARTHROWER_9 = 918
|
|
280
|
+
SPEARTHROWER_10 = 919
|
|
281
|
+
VETERANSABERSLASHER_2 = 940
|
|
282
|
+
VETERANSABERSLASHER_3 = 941
|
|
283
|
+
VETERANSABERSLASHER_4 = 942
|
|
284
|
+
VETERANSABERSLASHER_5 = 943
|
|
285
|
+
VETERANSABERSLASHER_6 = 944
|
|
286
|
+
VETERANSABERSLASHER_7 = 945
|
|
287
|
+
VETERANSABERSLASHER_8 = 946
|
|
288
|
+
VETERANSABERSLASHER_9 = 947
|
|
289
|
+
VETERANSABERSLASHER_10 = 948
|
|
290
|
+
VETERANSABERSLASHER_11 = 949
|
|
291
|
+
VETERANSLINGSHOTMARKSMAN_2 = 950
|
|
292
|
+
VETERANSLINGSHOTMARKSMAN_3 = 951
|
|
293
|
+
VETERANSLINGSHOTMARKSMAN_4 = 952
|
|
294
|
+
VETERANSLINGSHOTMARKSMAN_5 = 953
|
|
295
|
+
VETERANSLINGSHOTMARKSMAN_6 = 954
|
|
296
|
+
VETERANSLINGSHOTMARKSMAN_7 = 955
|
|
297
|
+
VETERANSLINGSHOTMARKSMAN_8 = 956
|
|
298
|
+
VETERANSLINGSHOTMARKSMAN_9 = 957
|
|
299
|
+
VETERANSLINGSHOTMARKSMAN_10 = 958
|
|
300
|
+
VETERANSLINGSHOTMARKSMAN_11 = 959
|
|
301
|
+
ELITESPRINGATTACKERMELEE_2 = 960
|
|
302
|
+
ELITESPRINGATTACKERRANGE_2 = 961
|
|
303
|
+
RENEGADEPIRATEMILEE_2 = 962
|
|
304
|
+
RENEGADEPIRATERANGE_2 = 963
|
|
305
|
+
QUICKATTACK = 1337
|
|
306
|
+
ELITEHALBERD_2 = 2000
|
|
307
|
+
ELITEHALBERD_3 = 2001
|
|
308
|
+
ELITEHALBERD_4 = 2002
|
|
309
|
+
ELITEHALBERD_5 = 2003
|
|
310
|
+
ELITEHALBERD_6 = 2004
|
|
311
|
+
ELITEHALBERD_7 = 2005
|
|
312
|
+
ELITEHALBERD_8 = 2006
|
|
313
|
+
ELITEHALBERD_9 = 2007
|
|
314
|
+
ELITEHALBERD_10 = 2008
|
|
315
|
+
ELITEHALBERD_11 = 2009
|
|
316
|
+
ELITETWOHANDEDSWORD_2 = 2010
|
|
317
|
+
ELITETWOHANDEDSWORD_3 = 2011
|
|
318
|
+
ELITETWOHANDEDSWORD_4 = 2012
|
|
319
|
+
ELITETWOHANDEDSWORD_5 = 2013
|
|
320
|
+
ELITETWOHANDEDSWORD_6 = 2014
|
|
321
|
+
ELITETWOHANDEDSWORD_7 = 2015
|
|
322
|
+
ELITETWOHANDEDSWORD_8 = 2016
|
|
323
|
+
ELITETWOHANDEDSWORD_9 = 2017
|
|
324
|
+
ELITETWOHANDEDSWORD_10 = 2018
|
|
325
|
+
ELITETWOHANDEDSWORD_11 = 2019
|
|
326
|
+
RELICAXE_2 = 2020
|
|
327
|
+
RELICAXE_3 = 2021
|
|
328
|
+
RELICAXE_4 = 2022
|
|
329
|
+
RELICAXE_5 = 2023
|
|
330
|
+
RELICAXE_6 = 2024
|
|
331
|
+
RELICAXE_7 = 2025
|
|
332
|
+
RELICAXE_8 = 2026
|
|
333
|
+
RELICAXE_9 = 2027
|
|
334
|
+
RELICAXE_10 = 2028
|
|
335
|
+
RELICAXE_11 = 2029
|
|
336
|
+
RELICHAMMER_2 = 2030
|
|
337
|
+
RELICHAMMER_3 = 2031
|
|
338
|
+
RELICHAMMER_4 = 2032
|
|
339
|
+
RELICHAMMER_5 = 2033
|
|
340
|
+
RELICHAMMER_6 = 2034
|
|
341
|
+
RELICHAMMER_7 = 2035
|
|
342
|
+
RELICHAMMER_8 = 2036
|
|
343
|
+
RELICHAMMER_9 = 2037
|
|
344
|
+
RELICHAMMER_10 = 2038
|
|
345
|
+
RELICHAMMER_11 = 2039
|
|
346
|
+
ELITELONGBOWMAN_2 = 2040
|
|
347
|
+
ELITELONGBOWMAN_3 = 2041
|
|
348
|
+
ELITELONGBOWMAN_4 = 2042
|
|
349
|
+
ELITELONGBOWMAN_5 = 2043
|
|
350
|
+
ELITELONGBOWMAN_6 = 2044
|
|
351
|
+
ELITELONGBOWMAN_7 = 2045
|
|
352
|
+
ELITELONGBOWMAN_8 = 2046
|
|
353
|
+
ELITELONGBOWMAN_9 = 2047
|
|
354
|
+
ELITELONGBOWMAN_10 = 2048
|
|
355
|
+
ELITELONGBOWMAN_11 = 2049
|
|
356
|
+
ELITEHEAVYCROSSBOWMAN_2 = 2050
|
|
357
|
+
ELITEHEAVYCROSSBOWMAN_3 = 2051
|
|
358
|
+
ELITEHEAVYCROSSBOWMAN_4 = 2052
|
|
359
|
+
ELITEHEAVYCROSSBOWMAN_5 = 2053
|
|
360
|
+
ELITEHEAVYCROSSBOWMAN_6 = 2054
|
|
361
|
+
ELITEHEAVYCROSSBOWMAN_7 = 2055
|
|
362
|
+
ELITEHEAVYCROSSBOWMAN_8 = 2056
|
|
363
|
+
ELITEHEAVYCROSSBOWMAN_9 = 2057
|
|
364
|
+
ELITEHEAVYCROSSBOWMAN_10 = 2058
|
|
365
|
+
ELITEHEAVYCROSSBOWMAN_11 = 2059
|
|
366
|
+
RELICSHORTBOW_2 = 2060
|
|
367
|
+
RELICSHORTBOW_3 = 2061
|
|
368
|
+
RELICSHORTBOW_4 = 2062
|
|
369
|
+
RELICSHORTBOW_5 = 2063
|
|
370
|
+
RELICSHORTBOW_6 = 2064
|
|
371
|
+
RELICSHORTBOW_7 = 2065
|
|
372
|
+
RELICSHORTBOW_8 = 2066
|
|
373
|
+
RELICSHORTBOW_9 = 2067
|
|
374
|
+
RELICSHORTBOW_10 = 2068
|
|
375
|
+
RELICSHORTBOW_11 = 2069
|
|
376
|
+
RELICLONGBOW_2 = 2070
|
|
377
|
+
RELICLONGBOW_3 = 2071
|
|
378
|
+
RELICLONGBOW_4 = 2072
|
|
379
|
+
RELICLONGBOW_5 = 2073
|
|
380
|
+
RELICLONGBOW_6 = 2074
|
|
381
|
+
RELICLONGBOW_7 = 2075
|
|
382
|
+
RELICLONGBOW_8 = 2076
|
|
383
|
+
RELICLONGBOW_9 = 2077
|
|
384
|
+
RELICLONGBOW_10 = 2078
|
|
385
|
+
RELICLONGBOW_11 = 2079
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@dataclass
|
|
389
|
+
class RecruitmentTask:
|
|
390
|
+
"""A unit recruitment task."""
|
|
391
|
+
|
|
392
|
+
castle_id: int
|
|
393
|
+
unit_type: int
|
|
394
|
+
count: int
|
|
395
|
+
priority: int = 0
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@dataclass
|
|
399
|
+
class UnitProductionTarget:
|
|
400
|
+
"""Target unit counts for a castle."""
|
|
401
|
+
|
|
402
|
+
castle_id: int
|
|
403
|
+
targets: Dict[int, int] = field(default_factory=dict) # unit_type -> target_count
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@dataclass
|
|
407
|
+
class ArmyStatus:
|
|
408
|
+
"""Current army status for a castle."""
|
|
409
|
+
|
|
410
|
+
castle_id: int
|
|
411
|
+
units: Dict[int, int] = field(default_factory=dict) # unit_type -> count
|
|
412
|
+
total_units: int = 0
|
|
413
|
+
in_production: Dict[int, int] = field(default_factory=dict)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class UnitManager:
|
|
417
|
+
"""
|
|
418
|
+
Manages unit production and army composition.
|
|
419
|
+
|
|
420
|
+
Features:
|
|
421
|
+
- Production queue management
|
|
422
|
+
- Auto-recruitment based on targets
|
|
423
|
+
- Army composition recommendations
|
|
424
|
+
- Multi-castle coordination
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
def __init__(self, client: "EmpireClient"):
|
|
428
|
+
self.client = client
|
|
429
|
+
self.queue: List[RecruitmentTask] = []
|
|
430
|
+
self.targets: Dict[int, UnitProductionTarget] = {} # castle_id -> targets
|
|
431
|
+
self._auto_recruit_enabled = False
|
|
432
|
+
self._running = False
|
|
433
|
+
|
|
434
|
+
@property
|
|
435
|
+
def castles(self) -> Dict[int, Castle]:
|
|
436
|
+
"""Get player's castles."""
|
|
437
|
+
player = self.client.state.local_player
|
|
438
|
+
if player:
|
|
439
|
+
return player.castles
|
|
440
|
+
return {}
|
|
441
|
+
|
|
442
|
+
def set_target(self, castle_id: int, unit_type: int, count: int):
|
|
443
|
+
"""
|
|
444
|
+
Set target unit count for a castle.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
castle_id: Castle ID
|
|
448
|
+
unit_type: Unit type ID
|
|
449
|
+
count: Target number of units
|
|
450
|
+
"""
|
|
451
|
+
if castle_id not in self.targets:
|
|
452
|
+
self.targets[castle_id] = UnitProductionTarget(castle_id=castle_id)
|
|
453
|
+
|
|
454
|
+
self.targets[castle_id].targets[unit_type] = count
|
|
455
|
+
logger.info(f"Set target: Castle {castle_id}, Unit {unit_type}, Count {count}")
|
|
456
|
+
|
|
457
|
+
def set_army_composition(
|
|
458
|
+
self,
|
|
459
|
+
castle_id: int,
|
|
460
|
+
composition: Dict[int, int],
|
|
461
|
+
):
|
|
462
|
+
"""
|
|
463
|
+
Set complete army composition target.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
castle_id: Castle ID
|
|
467
|
+
composition: Dict of {unit_type: count}
|
|
468
|
+
"""
|
|
469
|
+
self.targets[castle_id] = UnitProductionTarget(
|
|
470
|
+
castle_id=castle_id,
|
|
471
|
+
targets=composition.copy(),
|
|
472
|
+
)
|
|
473
|
+
logger.info(f"Set army composition for castle {castle_id}")
|
|
474
|
+
|
|
475
|
+
def clear_targets(self, castle_id: Optional[int] = None):
|
|
476
|
+
"""Clear production targets."""
|
|
477
|
+
if castle_id:
|
|
478
|
+
self.targets.pop(castle_id, None)
|
|
479
|
+
else:
|
|
480
|
+
self.targets.clear()
|
|
481
|
+
|
|
482
|
+
def add_task(
|
|
483
|
+
self,
|
|
484
|
+
castle_id: int,
|
|
485
|
+
unit_type: int,
|
|
486
|
+
count: int,
|
|
487
|
+
priority: int = 0,
|
|
488
|
+
) -> RecruitmentTask:
|
|
489
|
+
"""Add a recruitment task to the queue."""
|
|
490
|
+
task = RecruitmentTask(
|
|
491
|
+
castle_id=castle_id,
|
|
492
|
+
unit_type=unit_type,
|
|
493
|
+
count=count,
|
|
494
|
+
priority=priority,
|
|
495
|
+
)
|
|
496
|
+
self.queue.append(task)
|
|
497
|
+
self._sort_queue()
|
|
498
|
+
logger.info(f"Added recruitment: {count}x Unit {unit_type} in Castle {castle_id}")
|
|
499
|
+
return task
|
|
500
|
+
|
|
501
|
+
def remove_task(self, castle_id: int, unit_type: int) -> bool:
|
|
502
|
+
"""Remove a task from the queue."""
|
|
503
|
+
for i, task in enumerate(self.queue):
|
|
504
|
+
if task.castle_id == castle_id and task.unit_type == unit_type:
|
|
505
|
+
self.queue.pop(i)
|
|
506
|
+
return True
|
|
507
|
+
return False
|
|
508
|
+
|
|
509
|
+
def clear_queue(self, castle_id: Optional[int] = None):
|
|
510
|
+
"""Clear recruitment queue."""
|
|
511
|
+
if castle_id:
|
|
512
|
+
self.queue = [t for t in self.queue if t.castle_id != castle_id]
|
|
513
|
+
else:
|
|
514
|
+
self.queue.clear()
|
|
515
|
+
|
|
516
|
+
def get_army_status(self, castle_id: int) -> Optional[ArmyStatus]:
|
|
517
|
+
"""Get current army status for a castle."""
|
|
518
|
+
# Get from state manager
|
|
519
|
+
army = self.client.state.armies.get(castle_id)
|
|
520
|
+
if not army:
|
|
521
|
+
return ArmyStatus(castle_id=castle_id)
|
|
522
|
+
|
|
523
|
+
units = army.units.copy()
|
|
524
|
+
total = sum(units.values())
|
|
525
|
+
|
|
526
|
+
return ArmyStatus(
|
|
527
|
+
castle_id=castle_id,
|
|
528
|
+
units=units,
|
|
529
|
+
total_units=total,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
def get_deficit(self, castle_id: int) -> Dict[int, int]:
|
|
533
|
+
"""
|
|
534
|
+
Get unit deficit compared to targets.
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
Dict of {unit_type: deficit_count}
|
|
538
|
+
"""
|
|
539
|
+
deficit: Dict[int, int] = {}
|
|
540
|
+
target = self.targets.get(castle_id)
|
|
541
|
+
if not target:
|
|
542
|
+
return deficit
|
|
543
|
+
|
|
544
|
+
status = self.get_army_status(castle_id)
|
|
545
|
+
current_units = status.units if status else {}
|
|
546
|
+
|
|
547
|
+
for unit_type, target_count in target.targets.items():
|
|
548
|
+
current = current_units.get(unit_type, 0)
|
|
549
|
+
if current < target_count:
|
|
550
|
+
deficit[unit_type] = target_count - current
|
|
551
|
+
|
|
552
|
+
return deficit
|
|
553
|
+
|
|
554
|
+
def calculate_recruitment_tasks(self, castle_id: Optional[int] = None) -> List[RecruitmentTask]:
|
|
555
|
+
"""
|
|
556
|
+
Calculate recruitment tasks needed to meet targets.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
castle_id: Specific castle or None for all
|
|
560
|
+
|
|
561
|
+
Returns:
|
|
562
|
+
List of RecruitmentTask objects
|
|
563
|
+
"""
|
|
564
|
+
tasks = []
|
|
565
|
+
castle_ids = [castle_id] if castle_id else list(self.targets.keys())
|
|
566
|
+
|
|
567
|
+
for cid in castle_ids:
|
|
568
|
+
deficit = self.get_deficit(cid)
|
|
569
|
+
for unit_type, count in deficit.items():
|
|
570
|
+
if count > 0:
|
|
571
|
+
tasks.append(
|
|
572
|
+
RecruitmentTask(
|
|
573
|
+
castle_id=cid,
|
|
574
|
+
unit_type=unit_type,
|
|
575
|
+
count=count,
|
|
576
|
+
)
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
return tasks
|
|
580
|
+
|
|
581
|
+
async def execute_task(self, task: RecruitmentTask) -> bool:
|
|
582
|
+
"""Execute a recruitment task."""
|
|
583
|
+
try:
|
|
584
|
+
success = await self.client.recruit_units(
|
|
585
|
+
castle_id=task.castle_id,
|
|
586
|
+
unit_id=task.unit_type,
|
|
587
|
+
count=task.count,
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
if success:
|
|
591
|
+
# Remove from queue
|
|
592
|
+
self.queue = [
|
|
593
|
+
t for t in self.queue if not (t.castle_id == task.castle_id and t.unit_type == task.unit_type)
|
|
594
|
+
]
|
|
595
|
+
logger.info(f"Started recruitment: {task.count}x Unit {task.unit_type} in Castle {task.castle_id}")
|
|
596
|
+
|
|
597
|
+
return bool(success)
|
|
598
|
+
except Exception as e:
|
|
599
|
+
logger.error(f"Recruitment failed: {e}")
|
|
600
|
+
return False
|
|
601
|
+
|
|
602
|
+
async def process_queue(self) -> int:
|
|
603
|
+
"""Process the recruitment queue."""
|
|
604
|
+
if not self.queue:
|
|
605
|
+
return 0
|
|
606
|
+
|
|
607
|
+
executed = 0
|
|
608
|
+
for task in self.queue[:]: # Copy to allow modification
|
|
609
|
+
success = await self.execute_task(task)
|
|
610
|
+
if success:
|
|
611
|
+
executed += 1
|
|
612
|
+
await asyncio.sleep(0.5) # Rate limit
|
|
613
|
+
|
|
614
|
+
return executed
|
|
615
|
+
|
|
616
|
+
async def auto_recruit(self) -> int:
|
|
617
|
+
"""
|
|
618
|
+
Automatically recruit units to meet targets.
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
Number of recruitment tasks executed
|
|
622
|
+
"""
|
|
623
|
+
# Calculate needed recruitments
|
|
624
|
+
tasks = self.calculate_recruitment_tasks()
|
|
625
|
+
|
|
626
|
+
if not tasks:
|
|
627
|
+
logger.debug("No recruitment needed")
|
|
628
|
+
return 0
|
|
629
|
+
|
|
630
|
+
# Execute tasks
|
|
631
|
+
executed = 0
|
|
632
|
+
for task in tasks:
|
|
633
|
+
success = await self.execute_task(task)
|
|
634
|
+
if success:
|
|
635
|
+
executed += 1
|
|
636
|
+
await asyncio.sleep(0.5)
|
|
637
|
+
|
|
638
|
+
logger.info(f"Auto-recruit: executed {executed} tasks")
|
|
639
|
+
return executed
|
|
640
|
+
|
|
641
|
+
async def start_auto_recruit(self, interval: int = 120):
|
|
642
|
+
"""
|
|
643
|
+
Start automatic recruitment.
|
|
644
|
+
|
|
645
|
+
Args:
|
|
646
|
+
interval: Check interval in seconds
|
|
647
|
+
"""
|
|
648
|
+
self._auto_recruit_enabled = True
|
|
649
|
+
self._running = True
|
|
650
|
+
|
|
651
|
+
logger.info(f"Auto-recruit started (interval: {interval}s)")
|
|
652
|
+
|
|
653
|
+
while self._running and self._auto_recruit_enabled:
|
|
654
|
+
try:
|
|
655
|
+
await self.auto_recruit()
|
|
656
|
+
except Exception as e:
|
|
657
|
+
logger.error(f"Auto-recruit error: {e}")
|
|
658
|
+
|
|
659
|
+
await asyncio.sleep(interval)
|
|
660
|
+
|
|
661
|
+
def stop_auto_recruit(self):
|
|
662
|
+
"""Stop automatic recruitment."""
|
|
663
|
+
self._auto_recruit_enabled = False
|
|
664
|
+
self._running = False
|
|
665
|
+
logger.info("Auto-recruit stopped")
|
|
666
|
+
|
|
667
|
+
def get_summary(self) -> Dict[str, Any]:
|
|
668
|
+
"""Get recruitment summary."""
|
|
669
|
+
return {
|
|
670
|
+
"queue_length": len(self.queue),
|
|
671
|
+
"castles_with_targets": len(self.targets),
|
|
672
|
+
"total_deficit": sum(sum(self.get_deficit(cid).values()) for cid in self.targets),
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
def recommend_composition(self, focus: str = "balanced", size: int = 500) -> Dict[int, int]:
|
|
676
|
+
"""
|
|
677
|
+
Recommend army composition.
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
focus: "balanced", "attack", "defense", "farming"
|
|
681
|
+
size: Total army size
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
Dict of {unit_type: count}
|
|
685
|
+
"""
|
|
686
|
+
if focus == "attack":
|
|
687
|
+
return {
|
|
688
|
+
UnitType.SWORDSMAN: int(size * 0.3),
|
|
689
|
+
UnitType.KNIGHT: int(size * 0.2),
|
|
690
|
+
UnitType.CROSSBOWMAN: int(size * 0.2),
|
|
691
|
+
UnitType.HEAVY_CAVALRY: int(size * 0.2),
|
|
692
|
+
UnitType.CATAPULT: int(size * 0.1),
|
|
693
|
+
}
|
|
694
|
+
elif focus == "defense":
|
|
695
|
+
return {
|
|
696
|
+
UnitType.SPEARMAN: int(size * 0.3),
|
|
697
|
+
UnitType.CROSSBOWMAN: int(size * 0.3),
|
|
698
|
+
UnitType.WALL_DEFENDER: int(size * 0.2),
|
|
699
|
+
UnitType.MANTLET: int(size * 0.2),
|
|
700
|
+
}
|
|
701
|
+
elif focus == "farming":
|
|
702
|
+
return {
|
|
703
|
+
UnitType.MILITIA: int(size * 0.5),
|
|
704
|
+
UnitType.SWORDSMAN: int(size * 0.3),
|
|
705
|
+
UnitType.BOWMAN: int(size * 0.2),
|
|
706
|
+
}
|
|
707
|
+
else: # balanced
|
|
708
|
+
return {
|
|
709
|
+
UnitType.SWORDSMAN: int(size * 0.25),
|
|
710
|
+
UnitType.SPEARMAN: int(size * 0.15),
|
|
711
|
+
UnitType.CROSSBOWMAN: int(size * 0.2),
|
|
712
|
+
UnitType.KNIGHT: int(size * 0.15),
|
|
713
|
+
UnitType.LIGHT_CAVALRY: int(size * 0.15),
|
|
714
|
+
UnitType.CATAPULT: int(size * 0.1),
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
def _sort_queue(self):
|
|
718
|
+
"""Sort queue by priority."""
|
|
719
|
+
self.queue.sort(key=lambda t: t.priority, reverse=True)
|