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,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Alliance service for EmpireCore.
|
|
3
|
+
|
|
4
|
+
Provides high-level APIs for:
|
|
5
|
+
- Alliance chat (send messages, get history)
|
|
6
|
+
- Alliance help (help members, help all, request help)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import TYPE_CHECKING, Callable
|
|
12
|
+
|
|
13
|
+
from empire_core.protocol.models import (
|
|
14
|
+
AllianceChatLogRequest,
|
|
15
|
+
AllianceChatLogResponse,
|
|
16
|
+
AllianceChatMessageRequest,
|
|
17
|
+
AllianceChatMessageResponse,
|
|
18
|
+
AskHelpRequest,
|
|
19
|
+
ChatLogEntry,
|
|
20
|
+
HelpAllRequest,
|
|
21
|
+
HelpAllResponse,
|
|
22
|
+
HelpMemberRequest,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from .base import BaseService, register_service
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@register_service("alliance")
|
|
32
|
+
class AllianceService(BaseService):
|
|
33
|
+
"""
|
|
34
|
+
Service for alliance operations.
|
|
35
|
+
|
|
36
|
+
Accessible via client.alliance after auto-registration.
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
client = EmpireClient(...)
|
|
40
|
+
client.login()
|
|
41
|
+
|
|
42
|
+
# Send chat message
|
|
43
|
+
client.alliance.send_chat("Hello alliance!")
|
|
44
|
+
|
|
45
|
+
# Help all members
|
|
46
|
+
client.alliance.help_all()
|
|
47
|
+
|
|
48
|
+
# Subscribe to incoming messages
|
|
49
|
+
def on_message(response: AllianceChatMessageResponse):
|
|
50
|
+
print(f"{response.player_name}: {response.decoded_text}")
|
|
51
|
+
|
|
52
|
+
client.alliance.on_chat_message(on_message)
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, client) -> None:
|
|
56
|
+
super().__init__(client)
|
|
57
|
+
self._chat_callbacks: list[Callable[[AllianceChatMessageResponse], None]] = []
|
|
58
|
+
|
|
59
|
+
# Register internal handler for chat messages
|
|
60
|
+
self.on_response("acm", self._handle_chat_message)
|
|
61
|
+
|
|
62
|
+
# =========================================================================
|
|
63
|
+
# Chat Operations
|
|
64
|
+
# =========================================================================
|
|
65
|
+
|
|
66
|
+
def send_chat(self, message: str) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Send a message to alliance chat.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
message: The message text to send (will be auto-encoded)
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
client.alliance.send_chat("Hello alliance!")
|
|
75
|
+
client.alliance.send_chat("Special chars work: 100% safe!")
|
|
76
|
+
"""
|
|
77
|
+
request = AllianceChatMessageRequest.create(message)
|
|
78
|
+
self.send(request)
|
|
79
|
+
|
|
80
|
+
def get_chat_log(self, timeout: float = 5.0) -> list[ChatLogEntry]:
|
|
81
|
+
"""
|
|
82
|
+
Get alliance chat history.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
timeout: Timeout in seconds to wait for response
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
List of ChatLogEntry objects
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
history = client.alliance.get_chat_log()
|
|
92
|
+
for entry in history:
|
|
93
|
+
print(f"{entry.player_name}: {entry.decoded_text}")
|
|
94
|
+
"""
|
|
95
|
+
request = AllianceChatLogRequest()
|
|
96
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
97
|
+
|
|
98
|
+
if isinstance(response, AllianceChatLogResponse):
|
|
99
|
+
return response.chat_log
|
|
100
|
+
|
|
101
|
+
return []
|
|
102
|
+
|
|
103
|
+
def on_chat_message(self, callback: Callable[[AllianceChatMessageResponse], None]) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Register a callback for incoming alliance chat messages.
|
|
106
|
+
|
|
107
|
+
The callback will be called whenever a chat message is received,
|
|
108
|
+
including messages from other players and confirmations of your own.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
callback: Function that receives AllianceChatMessageResponse
|
|
112
|
+
|
|
113
|
+
Example:
|
|
114
|
+
def on_message(msg: AllianceChatMessageResponse):
|
|
115
|
+
print(f"[{msg.player_name}] {msg.decoded_text}")
|
|
116
|
+
|
|
117
|
+
client.alliance.on_chat_message(on_message)
|
|
118
|
+
"""
|
|
119
|
+
self._chat_callbacks.append(callback)
|
|
120
|
+
|
|
121
|
+
def _handle_chat_message(self, response) -> None:
|
|
122
|
+
"""Internal handler for chat message responses."""
|
|
123
|
+
if isinstance(response, AllianceChatMessageResponse):
|
|
124
|
+
for callback in self._chat_callbacks:
|
|
125
|
+
try:
|
|
126
|
+
callback(response)
|
|
127
|
+
except Exception:
|
|
128
|
+
pass # Silently ignore callback errors
|
|
129
|
+
|
|
130
|
+
# =========================================================================
|
|
131
|
+
# Help Operations
|
|
132
|
+
# =========================================================================
|
|
133
|
+
|
|
134
|
+
def help_all(self) -> HelpAllResponse | None:
|
|
135
|
+
"""
|
|
136
|
+
Help all alliance members who need help.
|
|
137
|
+
|
|
138
|
+
Sends a single request that helps all pending help requests
|
|
139
|
+
(heal, repair, recruit).
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
HelpAllResponse with helped_count, or None on failure
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
response = client.alliance.help_all()
|
|
146
|
+
if response:
|
|
147
|
+
print(f"Helped {response.helped_count} members")
|
|
148
|
+
"""
|
|
149
|
+
request = HelpAllRequest()
|
|
150
|
+
response = self.send(request, wait=True, timeout=5.0)
|
|
151
|
+
|
|
152
|
+
if isinstance(response, HelpAllResponse):
|
|
153
|
+
return response
|
|
154
|
+
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
def help_member_heal(self, player_id: int, castle_id: int) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Help heal a specific member's wounded soldiers.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
player_id: The player's ID
|
|
163
|
+
castle_id: The castle ID with wounded soldiers
|
|
164
|
+
"""
|
|
165
|
+
request = HelpMemberRequest.heal(player_id, castle_id)
|
|
166
|
+
self.send(request)
|
|
167
|
+
|
|
168
|
+
def help_member_repair(self, player_id: int, castle_id: int) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Help repair a specific member's building.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
player_id: The player's ID
|
|
174
|
+
castle_id: The castle ID with damaged building
|
|
175
|
+
"""
|
|
176
|
+
request = HelpMemberRequest.repair(player_id, castle_id)
|
|
177
|
+
self.send(request)
|
|
178
|
+
|
|
179
|
+
def help_member_recruit(self, player_id: int, castle_id: int) -> None:
|
|
180
|
+
"""
|
|
181
|
+
Help a specific member with soldier recruitment.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
player_id: The player's ID
|
|
185
|
+
castle_id: The castle ID recruiting soldiers
|
|
186
|
+
"""
|
|
187
|
+
request = HelpMemberRequest.recruit(player_id, castle_id)
|
|
188
|
+
self.send(request)
|
|
189
|
+
|
|
190
|
+
def request_heal_help(self, castle_id: int) -> None:
|
|
191
|
+
"""
|
|
192
|
+
Request heal help from alliance for a castle.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
castle_id: The castle ID with wounded soldiers
|
|
196
|
+
"""
|
|
197
|
+
request = AskHelpRequest.heal(castle_id)
|
|
198
|
+
self.send(request)
|
|
199
|
+
|
|
200
|
+
def request_repair_help(self, castle_id: int, building_id: int) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Request repair help from alliance for a building.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
castle_id: The castle ID
|
|
206
|
+
building_id: The building ID that needs repair
|
|
207
|
+
"""
|
|
208
|
+
request = AskHelpRequest.repair(castle_id, building_id)
|
|
209
|
+
self.send(request)
|
|
210
|
+
|
|
211
|
+
def request_recruit_help(self, castle_id: int) -> None:
|
|
212
|
+
"""
|
|
213
|
+
Request recruit help from alliance for a castle.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
castle_id: The castle ID recruiting soldiers
|
|
217
|
+
"""
|
|
218
|
+
request = AskHelpRequest.recruit(castle_id)
|
|
219
|
+
self.send(request)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
__all__ = ["AllianceService"]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base service class and registration decorator.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Callable, Type, TypeVar
|
|
8
|
+
|
|
9
|
+
from empire_core.protocol.models import BaseRequest, BaseResponse, parse_response
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from empire_core.client.client import EmpireClient
|
|
13
|
+
|
|
14
|
+
# Registry of service classes
|
|
15
|
+
_service_registry: dict[str, Type["BaseService"]] = {}
|
|
16
|
+
|
|
17
|
+
T = TypeVar("T", bound="BaseService")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def register_service(name: str) -> Callable[[Type[T]], Type[T]]:
|
|
21
|
+
"""
|
|
22
|
+
Decorator to register a service class.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
@register_service("alliance")
|
|
26
|
+
class AllianceService(BaseService):
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
The service will be accessible as client.alliance
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def decorator(cls: Type[T]) -> Type[T]:
|
|
33
|
+
_service_registry[name] = cls
|
|
34
|
+
cls._service_name = name
|
|
35
|
+
return cls
|
|
36
|
+
|
|
37
|
+
return decorator
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_registered_services() -> dict[str, Type["BaseService"]]:
|
|
41
|
+
"""Get all registered service classes."""
|
|
42
|
+
return _service_registry.copy()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BaseService:
|
|
46
|
+
"""
|
|
47
|
+
Base class for all services.
|
|
48
|
+
|
|
49
|
+
Services provide high-level APIs for game domains and use
|
|
50
|
+
protocol models for type-safe request/response handling.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
_service_name: str = ""
|
|
54
|
+
|
|
55
|
+
def __init__(self, client: "EmpireClient") -> None:
|
|
56
|
+
self.client = client
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def zone(self) -> str:
|
|
60
|
+
"""Get the game zone from client config."""
|
|
61
|
+
return self.client.config.default_zone
|
|
62
|
+
|
|
63
|
+
def send(self, request: BaseRequest, wait: bool = False, timeout: float = 5.0) -> BaseResponse | None:
|
|
64
|
+
"""
|
|
65
|
+
Send a request to the server.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
request: The request model to send
|
|
69
|
+
wait: Whether to wait for a response
|
|
70
|
+
timeout: Timeout in seconds when waiting
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
The parsed response if wait=True, otherwise None
|
|
74
|
+
"""
|
|
75
|
+
packet = request.to_packet(zone=self.zone)
|
|
76
|
+
self.client.connection.send(packet)
|
|
77
|
+
|
|
78
|
+
if wait:
|
|
79
|
+
command = request.get_command()
|
|
80
|
+
try:
|
|
81
|
+
response_packet = self.client.connection.wait_for(command, timeout=timeout)
|
|
82
|
+
if response_packet and isinstance(response_packet.payload, dict):
|
|
83
|
+
return parse_response(command, response_packet.payload)
|
|
84
|
+
except Exception:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def on_response(self, command: str, handler: Callable[[BaseResponse], None]) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Register a handler for a specific response type.
|
|
92
|
+
|
|
93
|
+
Handlers are registered with the client for efficient routing.
|
|
94
|
+
Only commands with registered handlers will be parsed.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
command: The command code to handle (e.g., "acm")
|
|
98
|
+
handler: Callback function that receives the parsed response
|
|
99
|
+
"""
|
|
100
|
+
self.client._register_handler(command, handler)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
"BaseService",
|
|
105
|
+
"register_service",
|
|
106
|
+
"get_registered_services",
|
|
107
|
+
]
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Castle service for EmpireCore.
|
|
3
|
+
|
|
4
|
+
Provides high-level APIs for:
|
|
5
|
+
- Castle management (list, select, rename, relocate)
|
|
6
|
+
- Resource information
|
|
7
|
+
- Production rates
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from empire_core.protocol.models import (
|
|
13
|
+
CastleInfo,
|
|
14
|
+
DetailedCastleInfo,
|
|
15
|
+
GetCastlesRequest,
|
|
16
|
+
GetCastlesResponse,
|
|
17
|
+
GetDetailedCastleRequest,
|
|
18
|
+
GetDetailedCastleResponse,
|
|
19
|
+
GetProductionRequest,
|
|
20
|
+
GetProductionResponse,
|
|
21
|
+
GetResourcesRequest,
|
|
22
|
+
GetResourcesResponse,
|
|
23
|
+
ProductionRates,
|
|
24
|
+
RenameCastleRequest,
|
|
25
|
+
RenameCastleResponse,
|
|
26
|
+
ResourceAmount,
|
|
27
|
+
SelectCastleRequest,
|
|
28
|
+
SelectCastleResponse,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from .base import BaseService, register_service
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@register_service("castle")
|
|
35
|
+
class CastleService(BaseService):
|
|
36
|
+
"""
|
|
37
|
+
Service for castle operations.
|
|
38
|
+
|
|
39
|
+
Accessible via client.castle after auto-registration.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
client = EmpireClient(...)
|
|
43
|
+
client.login()
|
|
44
|
+
|
|
45
|
+
# Get all castles
|
|
46
|
+
castles = client.castle.get_all()
|
|
47
|
+
for c in castles:
|
|
48
|
+
print(f"{c.castle_name} at ({c.x}, {c.y})")
|
|
49
|
+
|
|
50
|
+
# Select a castle
|
|
51
|
+
client.castle.select(castle_id=12345)
|
|
52
|
+
|
|
53
|
+
# Get resources
|
|
54
|
+
resources = client.castle.get_resources(castle_id=12345)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# =========================================================================
|
|
58
|
+
# Castle List Operations
|
|
59
|
+
# =========================================================================
|
|
60
|
+
|
|
61
|
+
def get_all(self, timeout: float = 5.0) -> list[CastleInfo]:
|
|
62
|
+
"""
|
|
63
|
+
Get list of all player's castles.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
timeout: Timeout in seconds to wait for response
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
List of CastleInfo objects
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
castles = client.castle.get_all()
|
|
73
|
+
for c in castles:
|
|
74
|
+
print(f"{c.castle_name} (ID: {c.castle_id}) at ({c.x}, {c.y})")
|
|
75
|
+
"""
|
|
76
|
+
request = GetCastlesRequest()
|
|
77
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
78
|
+
|
|
79
|
+
if isinstance(response, GetCastlesResponse):
|
|
80
|
+
return response.castles
|
|
81
|
+
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
def get_details(self, castle_id: int, timeout: float = 5.0) -> DetailedCastleInfo | None:
|
|
85
|
+
"""
|
|
86
|
+
Get detailed information about a specific castle.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
castle_id: The castle ID
|
|
90
|
+
timeout: Timeout in seconds
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
DetailedCastleInfo with buildings, resources, etc., or None
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
details = client.castle.get_details(12345)
|
|
97
|
+
if details:
|
|
98
|
+
print(f"Buildings: {len(details.buildings)}")
|
|
99
|
+
print(f"Population: {details.population}/{details.max_population}")
|
|
100
|
+
"""
|
|
101
|
+
request = GetDetailedCastleRequest(CID=castle_id)
|
|
102
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
103
|
+
|
|
104
|
+
if isinstance(response, GetDetailedCastleResponse):
|
|
105
|
+
return response.castle
|
|
106
|
+
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
# =========================================================================
|
|
110
|
+
# Castle Selection
|
|
111
|
+
# =========================================================================
|
|
112
|
+
|
|
113
|
+
def select(self, castle_id: int, timeout: float = 5.0) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Select/jump to a castle (makes it the active castle).
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
castle_id: The castle ID to select
|
|
119
|
+
timeout: Timeout in seconds
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
True if successful, False otherwise
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
if client.castle.select(12345):
|
|
126
|
+
print("Castle selected!")
|
|
127
|
+
"""
|
|
128
|
+
request = SelectCastleRequest(CID=castle_id)
|
|
129
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
130
|
+
|
|
131
|
+
if isinstance(response, SelectCastleResponse):
|
|
132
|
+
return response.error_code == 0
|
|
133
|
+
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
# =========================================================================
|
|
137
|
+
# Castle Modification
|
|
138
|
+
# =========================================================================
|
|
139
|
+
|
|
140
|
+
def rename(self, castle_id: int, new_name: str, timeout: float = 5.0) -> bool:
|
|
141
|
+
"""
|
|
142
|
+
Rename a castle.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
castle_id: The castle ID
|
|
146
|
+
new_name: The new castle name
|
|
147
|
+
timeout: Timeout in seconds
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
True if successful, False otherwise
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
if client.castle.rename(12345, "My Fortress"):
|
|
154
|
+
print("Castle renamed!")
|
|
155
|
+
"""
|
|
156
|
+
request = RenameCastleRequest(CID=castle_id, CN=new_name)
|
|
157
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
158
|
+
|
|
159
|
+
if isinstance(response, RenameCastleResponse):
|
|
160
|
+
return response.error_code == 0
|
|
161
|
+
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
# =========================================================================
|
|
165
|
+
# Resource Operations
|
|
166
|
+
# =========================================================================
|
|
167
|
+
|
|
168
|
+
def get_resources(self, castle_id: int, timeout: float = 5.0) -> ResourceAmount | None:
|
|
169
|
+
"""
|
|
170
|
+
Get current resources for a castle.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
castle_id: The castle ID
|
|
174
|
+
timeout: Timeout in seconds
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
ResourceAmount with wood, stone, food, coins, or None
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
resources = client.castle.get_resources(12345)
|
|
181
|
+
if resources:
|
|
182
|
+
print(f"Wood: {resources.wood}")
|
|
183
|
+
print(f"Stone: {resources.stone}")
|
|
184
|
+
"""
|
|
185
|
+
request = GetResourcesRequest(CID=castle_id)
|
|
186
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
187
|
+
|
|
188
|
+
if isinstance(response, GetResourcesResponse):
|
|
189
|
+
return response.resources
|
|
190
|
+
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
def get_production(
|
|
194
|
+
self, castle_id: int, timeout: float = 5.0
|
|
195
|
+
) -> tuple[ProductionRates | None, ProductionRates | None]:
|
|
196
|
+
"""
|
|
197
|
+
Get production and consumption rates for a castle.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
castle_id: The castle ID
|
|
201
|
+
timeout: Timeout in seconds
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Tuple of (production_rates, consumption_rates), either may be None
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
production, consumption = client.castle.get_production(12345)
|
|
208
|
+
if production:
|
|
209
|
+
print(f"Wood/hr: {production.wood}")
|
|
210
|
+
print(f"Food/hr: {production.food}")
|
|
211
|
+
"""
|
|
212
|
+
request = GetProductionRequest(CID=castle_id)
|
|
213
|
+
response = self.send(request, wait=True, timeout=timeout)
|
|
214
|
+
|
|
215
|
+
if isinstance(response, GetProductionResponse):
|
|
216
|
+
return response.production, response.consumption
|
|
217
|
+
|
|
218
|
+
return None, None
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
__all__ = ["CastleService"]
|
|
File without changes
|