Castle¶
Castles, buildings, resources and transfers behind client.castle.
castle.service¶
Your castles: resources, buildings, the construction queue, and sending goods and troops.
It covers:
- Castle management (list, join, rename)
- Resources and production
- Buildings and the construction queue
- Sending resources, support and units
- Tax collection
Action methods return True when the server accepted the action and False
when it rejected it with an error code; transport failures (timeout,
disconnect) raise. Query methods raise on any failure, and so do
start_tax and collect_tax, which return their replies.
Methods that take one of your castles send its kingdom from the castle
list the server sent at login (client.state.get_castles()), as the
client does, and raise UnknownCastleError for an id not in it, or
AmbiguousCastleError for an id listed in several of your kingdoms.
Building methods act on the castle joined last (CastleService.join)
and name buildings by object id, a BuildingRow.object_id from
join(...).buildings; upgrade_building, upgrade_defense and
finish_construction take the castle and join it themselves. Their typed replies (BuildResponse and the
others) can be read with client.request.
Classes:
| Name | Description |
|---|---|
CastleService |
Your castles, their buildings and resources, and sending goods and troops. |
CastleService
¶
Bases: BaseService
Your castles, their buildings and resources, and sending goods and troops.
Reached as client.castle.
Usage
client = EmpireClient(...)
client.login()
# Get all castles
castles = client.castle.get_all()
for c in castles:
print(f"{c.castle_name} at ({c.x}, {c.y})")
# Join a castle and read its buildings
castle = client.castle.join(castle_id=12345)
# Get resources
resources = client.castle.get_resources(castle_id=12345)
Methods:
| Name | Description |
|---|---|
get_all |
Get list of all player's castles. |
get_details |
Get resources and stationed units for one castle. |
select |
Select/jump to a castle (makes it the active castle). |
join |
Join a castle, making it the active castle, and return its state. |
join_area |
Join an area by its position ( |
rename |
Rename one of your castles. |
get_resources |
Get one of your castles' resources. |
get_production |
Get the joined castle's production area: production, storage, population and the rest. |
get_build_queue |
Get the joined castle's construction slots. |
build |
Place a new building in the joined castle. |
upgrade_building |
Upgrade a building in one of your castles. |
move_building |
Move a building in the joined castle. |
sell_decoration |
Sell a decoration placed in the joined castle. |
destroy_building |
Start taking a building in the joined castle down. |
finish_construction |
Finish a building's running build or upgrade at once. |
skip_construction_time |
Shorten a building's running construction with a minute skip. |
upgrade_defense |
Upgrade a wall, gate or tower of one of your castles, by its object id. |
repair_building |
Repair a damaged building in the joined castle. |
repair_all |
Repair every damaged building in the joined castle at once, for rubies; see "Spending |
buy_expansion |
Buy an expansion of the joined castle's grounds, with resources (NORMAL) or rubies (PREMIUM). |
open_treasure_chest |
Open a treasure chest found on an expansion of the joined castle. |
collect_mine |
Collect what a mine in the joined castle has produced. |
collect_resource_cart |
Collect the joined castle's resource cart of one resource. |
get_tax_info |
Read the tax collection status. |
start_tax |
Start a tax collection. |
collect_tax |
Collect the tax. |
send_resources |
Send resources from one of your castles to a castle on the map, by carriage. |
get_market_info |
List every castle's market carriages, those not on the road, and its resources. |
send_support |
Send support troops from a castle to a target location. |
send_troops |
Send troops from one of your areas to another of your own, where they stay. |
get_travel_info |
Get the pre-calculation of troops sent from one of your areas to another, before |
transfer_units_to_kingdom |
Send units from one of your castles to another kingdom. |
transfer_goods_to_kingdom |
Send goods from one of your castles to your castle in another kingdom. |
skip_kingdom_transfer_time |
Shorten the units or goods on their way to a kingdom with a minute skip item. |
get_horses |
The horses one of your castles can send movements with, by wod id. |
get_all
¶
get_all(timeout: float = 5.0) -> list[CastleInfo]
get_details
¶
get_details(castle_id: int, timeout: float = 5.0) -> DetailedCastleInfo
Get resources and stationed units for one castle.
The server answers with every castle of yours; the one asked for is picked out here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, from |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnknownCastleError
|
The reply lists no castle with that id |
AmbiguousCastleError
|
The reply lists the id in several kingdoms |
select
¶
Select/jump to a castle (makes it the active castle).
Use join for the castle's state the server sends back.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, a |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnknownCastleError
|
|
AmbiguousCastleError
|
|
Client: C2SJoinCastleVO sent with the castle's objectId and
kingdomID (JoinAreaAndSavePositionCommand.execute, bundle line 100892)
join
¶
join(castle_id: int, timeout: float = 5.0) -> SelectCastleResponse
Join a castle, making it the active castle, and return its state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, a |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnknownCastleError
|
|
AmbiguousCastleError
|
|
CommandError
|
The server refused the join. |
Client: C2SJoinCastleVO sent with the castle's objectId and
kingdomID (JoinAreaAndSavePositionCommand.execute, bundle line 100892)
join_area
¶
join_area(x: int, y: int, kingdom_id: Kingdom = GREEN, timeout: float = 5.0) -> SelectCastleResponse
Join an area by its position (jaa), making it the active area, and return its state.
The client joins this way the objects it may visit that are not
castles: outposts, capitals, metropolises and faction camps that are
not destroyed, and a kingdom castle just after its first naming. It
joins main and kingdom castles otherwise by id, as join does.
It never joins anything else (see JoinAreaRequest).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
Map x of the area |
required |
y
|
int
|
Map y of the area |
required |
kingdom_id
|
Kingdom
|
The kingdom it lies in |
GREEN
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
CommandError
|
The server refused the join; a live server answered INVALID_POSITION (6) for objects the client never joins, such as NPC camps and kings towers |
Client: JoinAreaAndSavePositionCommand.execute (bundle line 100892),
JAACommand.executeCommand (bundle line 130190)
rename
¶
rename(castle_id: int, new_name: str, *, is_initial_name: bool = False, spend_rubies: bool = False, timeout: float = 5.0) -> bool
Rename one of your castles.
Looks the castle up in the castle list for the type and kingdom the
server needs, so this costs one extra round trip. Pass
is_initial_name=True to name a newly acquired castle, such as a
monument or laboratory, instead of renaming one. A rename costs
PlayerConst.CHANGE_CASTLE_NAME_C2 (2500) rubies unless a premium account runs;
see "Spending rubies" in the guides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, from |
required |
new_name
|
str
|
The new name |
required |
is_initial_name
|
bool
|
Name a newly acquired castle instead of renaming one, which is free |
False
|
spend_rubies
|
bool
|
Allow a rename while no premium account is known to run |
False
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
A rename, no premium account is known to run, and |
UnknownCastleError
|
|
AmbiguousCastleError
|
|
Client: CastleRenameCastleDialog.showLoaded and sendCommand (bundle lines 57219, 57231)
price a rename, sent with 1; CastleNameCastleDialog.sendCommand (bundle line 32589) sends
the first name with 0 and shows no price; CHANGE_CASTLE_NAME_C2 (dll line 19599)
get_resources
¶
get_resources(castle_id: int, timeout: float = 5.0) -> CastleResources
Get one of your castles' resources.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, a |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnknownCastleError
|
|
AmbiguousCastleError
|
|
Client: C2SGetCastleResourcesVO sent with the castle's objectId and
kingdomID (CastleTransferResourcesDialog.onSelectCastle, bundle line 38076)
get_production
¶
get_production(timeout: float = 5.0) -> CastleProductionArea
get_build_queue
¶
get_build_queue(timeout: float = 5.0) -> ConstructionList
build
¶
build(wod_id: int, x: int, y: int, rotation: int = 0, *, spend_rubies: bool = False, private_offer_id: int = -1, district_object_id: int = -1, timeout: float = 5.0) -> bool
Place a new building in the joined castle.
Spends rubies when the building has a ruby price (BuildingDef.rubies_to_build at your
level), and with spend_rubies on missing resources; see "Spending rubies" in the guides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wod_id
|
int
|
The building type's wod id |
required |
x
|
int
|
Castle grid x, -1 when placing into a district |
required |
y
|
int
|
Castle grid y, -1 when placing into a district |
required |
rotation
|
int
|
Rotation |
0
|
spend_rubies
|
bool
|
Allow a ruby price, and pay the missing resources with rubies |
False
|
private_offer_id
|
int
|
The private offer the purchase uses, -1 for none |
-1
|
district_object_id
|
int
|
Object id of the district to place into, -1 for none |
-1
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
The building has a ruby price, or is not in the game data, and |
GameDataNotLoadedError
|
|
Client: IsoServerCommands.buyObjectFromShop (bundle line 63766); ABasicBuildingVO.costC2
(bundle lines 17950-17953)
upgrade_building
¶
upgrade_building(castle_id: int, object_id: int, *, spend_rubies: bool = False, private_offer_id: int = -1, timeout: float = 5.0) -> bool
Upgrade a building in one of your castles.
Joins the castle first, as the client is inside it, and prices the building from the
join's reply: the level it upgrades to, by BuildingDef.rubies_to_build at your level,
needs spend_rubies when it has a ruby price; see "Spending rubies" in the guides.
spend_rubies also pays missing resources with rubies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, a |
required |
object_id
|
int
|
The building's object id |
required |
spend_rubies
|
bool
|
Allow a ruby price, and pay the missing resources with rubies |
False
|
private_offer_id
|
int
|
The prime sale offer the upgrade uses, -1 for none |
-1
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
The next level has a ruby price, or the building or its next level is not
known, and |
GameDataNotLoadedError
|
|
UnknownCastleError / AmbiguousCastleError / CommandError
|
see |
Client: RingMenuButtonUpgradeBuilding.onClick (bundle line 50092),
IsoServerCommands.upgradeBuilding (bundle line 63774); the price is the next level's
costC2, BuildingCostHelper.getUpgradeCostList (bundle lines 41106-41116) through
AShopVO.getCost (bundle line 31766) and ABasicBuildingVO.costC2 (bundle lines 17950-17953)
move_building
¶
Move a building in the joined castle.
sell_decoration
¶
Sell a decoration placed in the joined castle.
destroy_building
¶
Start taking a building in the joined castle down.
finish_construction
¶
finish_construction(castle_id: int, object_id: int, *, spend_rubies: bool = False, timeout: float = 5.0) -> bool
Finish a building's running build or upgrade at once.
Joins the castle first, as the client is inside it, and works out the time left from the
join's reply as the client does. With at most FREE_SKIP_TIME (240) seconds left it
finishes for free, sent as a free skip; with more left, or the time left not known (a
repair or tear-down, an unknown building or player level), it costs rubies; see "Spending
rubies" in the guides. A subscription that lengthens the free time is not checked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, a |
required |
object_id
|
int
|
The building's object id |
required |
spend_rubies
|
bool
|
Allow finishing a construction that costs rubies |
False
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
More than 240 seconds are left, or the time left is not known, and
|
GameDataNotLoadedError
|
the game data cannot be loaded |
UnknownCastleError / AmbiguousCastleError / CommandError
|
see |
Client: IsoServerCommands.fastCompleteBuilding (bundle line 63773) sends FS 1 when
CastleSpecialEventData.hasSkipForFree (bundle line 139853), which calculateSkipCosts
(bundle line 139849) decides from getTimeLeftForBuilding (bundle line 17967);
FREE_SKIP_TIME (dll line 18962)
skip_construction_time
¶
Shorten a building's running construction with a minute skip.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
object_id
|
int
|
The building's object id |
required |
minute_skip
|
Currency | str
|
The minute skip to use, |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Client: BuildingMinuteSkipProperties.getMinuteSkipCommand (bundle line 50021),
CastleMinuteSkipDialog.showLoaded (bundle line 7671)
upgrade_defense
¶
upgrade_defense(castle_id: int, object_id: int, *, spend_rubies: bool = False, private_offer_id: int = -1, timeout: float = 5.0) -> bool
Upgrade a wall, gate or tower of one of your castles, by its object id.
Joins the castle first, as the client is inside it, and prices the building from the
join's reply: the level it upgrades to, by BuildingDef.rubies_to_build at your level,
needs spend_rubies when it has a ruby price; see "Spending rubies" in the guides.
spend_rubies also pays missing resources with rubies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, a |
required |
object_id
|
int
|
The wall's, gate's or tower's object id |
required |
spend_rubies
|
bool
|
Allow a ruby price, and pay the missing resources with rubies |
False
|
private_offer_id
|
int
|
The private offer the upgrade uses, -1 for none |
-1
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
The next level has a ruby price, or the object or its next level is not
known, and |
GameDataNotLoadedError
|
|
UnknownCastleError / AmbiguousCastleError / CommandError
|
see |
Client: C2SIsoUpgradeDefenceVO, sent with PWR 1 by
CastleResourceWaitDialogProperties.getResourceSkipCommand (bundle line 35220); the price
as for upgrade_building
repair_building
¶
repair_building(object_id: int, *, spend_rubies: bool = False, private_offer_id: int = -1, timeout: float = 5.0) -> bool
Repair a damaged building in the joined castle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
object_id
|
int
|
The building's object id |
required |
spend_rubies
|
bool
|
Pay the missing resources with rubies; see "Spending rubies" in the guides |
False
|
private_offer_id
|
int
|
The private offer the repair uses, -1 for none |
-1
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Client: IsoServerCommands.repairBuilding (bundle line 63770); with rubies,
CastleResourceWaitDialogProperties.getResourceSkipCommand (bundle line 35221)
repair_all
¶
Repair every damaged building in the joined castle at once, for rubies; see "Spending rubies" in the guides.
The price follows the damage's total repair time and is not worked out here, so this
always needs spend_rubies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spend_rubies
|
bool
|
Allow the rubies it costs; without it nothing is sent |
False
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Client: CastleRepairTipDialog (bundle lines 97414, 97430) prices it with
AreaData.getTotalRepairCostC2 (bundle line 131460), ConstructionConst.getRepairAllCostsC2
(dll lines 18966-18969)
buy_expansion
¶
buy_expansion(x: int, y: int, rotation: int = 0, expansion_type: ExpansionType = NORMAL, *, spend_rubies: bool = False, timeout: float = 5.0) -> bool
Buy an expansion of the joined castle's grounds, with resources (NORMAL) or rubies (PREMIUM).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
Castle grid x of the expansion |
required |
y
|
int
|
Castle grid y of the expansion |
required |
rotation
|
int
|
Rotation |
0
|
expansion_type
|
ExpansionType
|
NORMAL or PREMIUM; PREMIUM costs rubies, see "Spending rubies" in the guides |
NORMAL
|
spend_rubies
|
bool
|
Allow a PREMIUM expansion |
False
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Client: CastleExpansionVE (bundle line 86849) needs userHasC2ForBuy for a premium
expansion before sendBuyRequestToServer (bundle line 86854)
open_treasure_chest
¶
Open a treasure chest found on an expansion of the joined castle.
collect_mine
¶
Collect what a mine in the joined castle has produced.
collect_resource_cart
¶
collect_resource_cart(cart_type: ResourceCartType, timeout: float = 5.0) -> bool
Collect the joined castle's resource cart of one resource.
get_tax_info
¶
start_tax
¶
start_tax(tax_type: int, *, spend_rubies: bool = False, timeout: float = 5.0) -> StartTaxResponse
Start a tax collection.
Tax types 0 to 6 collect for TAX_DURATIONS[tax_type] seconds. Type 0
is free and types 1 to 4 cost a tenth of their income in coins. Types 5
and 6 cost TAX_RUBY_COSTS rubies unless a premium account, a VIP
level or the tax research waives it; this method refuses them unless
spend_rubies is True, and does not check for a waiver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tax_type
|
int
|
The tax type, 0 to 6 |
required |
spend_rubies
|
bool
|
Allow types 5 and 6, which may spend rubies |
False
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
StartTaxResponse
|
The reply: coins and rubies after, and the tax status. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
Client: C2SStartCollectTaxVO (bundle line 91088), sent by
CastleCollectTaxElement.onStartTaxCollection (bundle line 91069), which
shows a ruby cost when CollectTaxElementVO.hasC2CostWithoutPremium
(bundle line 91050); TaxConst (dll lines 19771-19790)
collect_tax
¶
collect_tax(timeout: float = 5.0) -> CollectTaxResponse
Collect the tax.
Collecting while the collection runs brings the share of its income earned so far; the client asks first when that is nothing.
Returns:
| Type | Description |
|---|---|
CollectTaxResponse
|
The reply: the coins collected, coins and rubies after, and the tax status. |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
Client: C2SCollectTaxVO (bundle line 41083), sent by
CastleCollectTaxDialog.collectTax (bundle line 91027) after
onCollectTaxClick (bundle line 91015); TaxConst.getCollectedMoney
(dll line 19784); TXCCommand (bundle line 128747)
send_resources
¶
send_resources(source_castle_id: int, target_x: int, target_y: int, goods: dict[Resource, int], *, horse_booster_id: int = -1, feathers: bool = False, slowdown: int = 0, spend_rubies: bool = False, timeout: float = 5.0) -> bool
Send resources from one of your castles to a castle on the map, by carriage.
A horse that costs rubies, unless paid with feathers, and a slowdown spend rubies; see "Spending rubies" in the guides.
Nothing is sent when the goods fail a check the client makes before sending: an amount that is not a positive int (the client drops zeros and refuses a send of nothing; this raises on a zero instead), goods from more than one of the dialog's tabs (classic, kingdom, mead), or, once the player is known and has no legend level, anything but wood, stone and food (the client then shows only that tab). The client sends one tab's goods per send, and the server refuses a send that mixes tabs with INVALID_PARAMETER_VALUE (seen live for wood with coal and coal with honey).
Not checked, since they depend on the target or on data this call does not have: the target owner's level for kingdom resources and legend level for mead goods, at most 2 goods (wood and stone) to a monument or laboratory, the carriage capacity and the castle's stock.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_castle_id
|
int
|
The castle the carriages leave from, one of yours |
required |
target_x
|
int
|
Map x of the target castle |
required |
target_y
|
int
|
Map y of the target castle |
required |
goods
|
dict[Resource, int]
|
Amount per resource, all from one tab, such as
|
required |
horse_booster_id
|
int
|
The horse's wod id, -1 for none; sent as -1 whenever feathers are used, as the client does |
-1
|
feathers
|
bool
|
Pay for the horse with feathers |
False
|
slowdown
|
int
|
Seconds to delay the arrival by; costs rubies |
0
|
spend_rubies
|
bool
|
Allow a horse or slowdown that costs rubies |
False
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnsendableGoodsError
|
The goods fail one of the checks above |
UnknownCastleError
|
|
AmbiguousCastleError
|
|
ValueError
|
The horse or slowdown costs rubies and |
GameDataNotLoadedError
|
A horse is picked without feathers and the game data cannot be loaded |
Client: CastlePostSendGoodsDialog.sendGoods (bundle line 33378), which
sends castleList.getKingdomIdByCastleId of the source castle as KID;
CastleSendGoodsDialog.sendGoodsCastle (bundle line 27202), which refuses
a zero sum; CollectableParser._createGoodsList (bundle line 40530),
which drops zero amounts; CastleSendGoodsComponent.rewardList (bundle
line 44340), which sends one tab's goods, and setTabVisibility (bundle
line 44261), which shows the kingdom and mead tabs only to a legend. Not
mirrored: applySpecialLevelRestrictions (bundle line 44289) and
isBoosterArea (bundle line 44356).
get_market_info
¶
get_market_info(timeout: float = 5.0) -> list[MarketCastle]
send_support
¶
send_support(source_castle_id: int, target_x: int, target_y: int, units: WodAmountMapping | Sequence[WodAmount], commander_id: int, wait_time: int = 12, use_premium_commander: bool = False, horse_booster_id: int = -1, feathers: bool = False, slowdown: int = 0, timeout: float = 5.0, *, spend_rubies: bool = False) -> bool
Send support troops from a castle to a target location.
The server refuses a support to your own area with NO_SELF_DESTRUCTION
(92); move troops between your own areas with send_troops.
A support led by the premium commander is refused before sending when it
may cost rubies, unless spend_rubies is True; see
client.commanders.premium_send. So is a horse that costs rubies, unless
paid with feathers, and a slowdown; see "Spending rubies" in the guides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_castle_id
|
int
|
The castle the troops leave from, one of yours: |
required |
target_x
|
int
|
Target X coordinate |
required |
target_y
|
int
|
Target Y coordinate |
required |
units
|
WodAmountMapping | Sequence[WodAmount]
|
The units, then any tools: |
required |
commander_id
|
int
|
Commander to lead the support, a |
required |
wait_time
|
int
|
Station duration in hours (0-12, default: 12) |
12
|
use_premium_commander
|
bool
|
Lead with the premium commander ( |
False
|
horse_booster_id
|
int
|
Type of horses for speed bonus (-1 = none, default: -1); sent as -1 whenever feathers are used, as the client does |
-1
|
feathers
|
bool
|
Pay for the movement with feathers |
False
|
slowdown
|
int
|
Movement slowdown modifier (0 = none, default: 0); costs rubies |
0
|
timeout
|
float
|
Timeout in seconds |
5.0
|
spend_rubies
|
bool
|
Send even when the premium commander, the horse or the slowdown costs rubies |
False
|
Raises:
| Type | Description |
|---|---|
PremiumCommanderCostError
|
The premium commander leads, may cost rubies,
and |
ValueError
|
The horse or slowdown costs rubies and |
GameDataNotLoadedError
|
The premium commander leads, VIP time runs and the game data cannot be loaded; or a horse is picked without feathers before it was called |
send_troops
¶
send_troops(source_x: int, source_y: int, target_x: int, target_y: int, units: WodAmountMapping | Sequence[WodAmount], commander_id: int, *, kingdom_id: Kingdom = GREEN, use_premium_commander: bool = False, spend_rubies: bool = False, horse_booster_id: int = -1, feathers: bool = False, slowdown: int = 0, timeout: float = 5.0) -> bool
Send troops from one of your areas to another of your own, where they stay.
This is how the client moves troops between your own areas; a support
(send_support) to your own area is refused with
NO_SELF_DESTRUCTION (92). While the troops are heading out they can be
recalled with client.movements.recall.
Targets the client offers: your castle (main or kingdom castle), your
outposts not under conquer control, your villages and resource isles,
your king's towers, monuments and laboratories, and your faction camps.
Sources: your castles and outposts (not occupied ones), villages,
resource isles, king's towers, monuments and laboratories. Both must
sit in the same kingdom; to move troops between kingdoms use
transfer_units_to_kingdom.
Rubies as for send_support: the premium commander, a horse that costs
rubies unless paid with feathers, and a slowdown need spend_rubies.
Client: CastleTroopSupportData.sendTroops (bundle line 38420); the
targets from the canBeTroupsSended overrides (CastleMapobjectVO
bundle line 18917, OutpostMapobjectVO 18819, KingstowerMapobjectVO
19075, FactionCampMapobjectVO 21530, VillageMapobjectVO 22673,
UpgradableLandmarkMapobjectVO 42648), the sources from
CastleStartAttackDialog.fillCastleList (bundle line 14725)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_x
|
int
|
Map x of the area the troops leave from |
required |
source_y
|
int
|
Map y of the area the troops leave from |
required |
target_x
|
int
|
Map x of the area they go to |
required |
target_y
|
int
|
Map y of the area they go to |
required |
units
|
WodAmountMapping | Sequence[WodAmount]
|
The units, then any tools: |
required |
commander_id
|
int
|
Commander to lead them, a |
required |
kingdom_id
|
Kingdom
|
The kingdom both areas sit in |
GREEN
|
use_premium_commander
|
bool
|
Lead with the premium commander ( |
False
|
spend_rubies
|
bool
|
Send even when the premium commander, the horse or the slowdown costs rubies |
False
|
horse_booster_id
|
int
|
The horse's wod id, -1 for none; sent as -1 whenever feathers are used, as the client does |
-1
|
feathers
|
bool
|
Pay for the horse with feathers |
False
|
slowdown
|
int
|
Seconds to delay the arrival by; costs rubies |
0
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
PremiumCommanderCostError
|
The premium commander leads, may cost rubies,
and |
ValueError
|
The horse or slowdown costs rubies and |
GameDataNotLoadedError
|
The premium commander leads, VIP time runs and the game data cannot be loaded; or a horse is picked without feathers before it was called |
get_travel_info
¶
get_travel_info(source_x: int, source_y: int, target_x: int, target_y: int, *, kingdom_id: Kingdom = GREEN, timeout: float = 5.0) -> GetTravelInfoResponse
Get the pre-calculation of troops sent from one of your areas to another, before send_troops.
This is what the client asks for when it opens the dialog to send
troops: the target's map row and owner, and your units and tools at the
source. The commanders it carries update client.state. The travel
time and cost are not in the reply; the client works them out from the
units picked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_x
|
int
|
Map x of the area the troops would leave from |
required |
source_y
|
int
|
Map y of the area the troops would leave from |
required |
target_x
|
int
|
Map x of the area they would go to |
required |
target_y
|
int
|
Map y of the area they would go to |
required |
kingdom_id
|
Kingdom
|
The kingdom both areas sit in |
GREEN
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
CommandError
|
The server refused it, |
Client: C2STroopSupportInfoVO (bundle line 72087), sent by
CastleStartAttackDialog (bundle line 14831); STICommand (bundle line 129095)
transfer_units_to_kingdom
¶
transfer_units_to_kingdom(source_castle_id: int, target_kingdom_id: Kingdom, units: WodAmountMapping | Sequence[WodAmount], *, target_castle_id: int = -1, timeout: float = 5.0) -> bool
Send units from one of your castles to another kingdom.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_castle_id
|
int
|
The castle the units leave from, one of yours |
required |
target_kingdom_id
|
Kingdom
|
The kingdom to send them to |
required |
units
|
WodAmountMapping | Sequence[WodAmount]
|
The units, |
required |
target_castle_id
|
int
|
Object id of a picked target castle, -1 for none |
-1
|
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnknownCastleError
|
|
AmbiguousCastleError
|
|
Client: CastleTransferTroopsToKingdomProperties.getUnitTransferCommand
(bundle line 37448) sends the source castle's kingdomID as SKID
transfer_goods_to_kingdom
¶
transfer_goods_to_kingdom(source_castle_id: int, target_kingdom_id: Kingdom, goods: dict[Resource, int], *, timeout: float = 5.0) -> bool
Send goods from one of your castles to your castle in another kingdom.
The goods pass the checks of send_resources, since the client
sends them from the same send component: positive amounts, one tab per
send, classic goods only below legend level. The source castle may not
sit in the target kingdom or in Berimond, which the client leaves out
of its castle picker, and the target kingdom must hold a castle of
yours, which the client sends the goods to. The travel tax and the
target's storage are left to the server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_castle_id
|
int
|
The castle the goods leave from, one of yours |
required |
target_kingdom_id
|
Kingdom
|
The kingdom to send them to |
required |
goods
|
dict[Resource, int]
|
Amount per resource, all from one tab, such as |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
UnsendableGoodsError
|
The goods fail one of the checks of |
ValueError
|
The source castle sits in the target kingdom or in Berimond, or no castle of yours sits in the target kingdom |
UnknownCastleError
|
|
AmbiguousCastleError
|
|
Client: CastleTransferResourcesDialog.sendResources (bundle line 38082) sends the
picked castle's kingdomID as SKID; its castle picker leaves out
CastleTransferResToKingdomProperties.exceptingKingdomIDs (bundle line 55533), and
targetInitialized (bundle line 55553) needs getMainCastleByKingdomID of the
target kingdom; CastleSendGoodsComponent.setTabVisibility (bundle line 44261)
skip_kingdom_transfer_time
¶
skip_kingdom_transfer_time(kingdom_id: Kingdom, transfer_type: KingdomTransferType, minute_skip: Currency | str, timeout: float = 5.0) -> bool
Shorten the units or goods on their way to a kingdom with a minute skip item.
Each kingdom has at most one units transfer and one goods transfer on
the way; client.state.get_kingdoms() lists them. The full skip the
client also offers (kst) costs rubies and is not offered here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kingdom_id
|
Kingdom
|
The kingdom the transfer goes to |
required |
transfer_type
|
KingdomTransferType
|
|
required |
minute_skip
|
Currency | str
|
The minute skip to use, |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Client: KingdomUnitsTravelMinuteSkipProperties.getMinuteSkipCommand (bundle line 37497),
KingdomGoodsTravelMinuteSkipProperties.getMinuteSkipCommand (bundle line 55567);
CastleMinuteSkipDialog.showLoaded (bundle line 7671); MSKCommand (bundle line 125795)
get_horses
¶
get_horses(castle_id: int) -> list[HorseStats] | None
The horses one of your castles can send movements with, by wod id.
Read from the login data's gpc section and its pushes; nothing is
sent. A horse whose is_instant_spy_horse is set can be paid with
rubies or, sent as feathers=True, with feathers; the client shows it
twice, once per payment. An id missing from the game data is left out,
as the client leaves it out.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
castle_id
|
int
|
One of your castles, |
required |
Returns:
| Type | Description |
|---|---|
list[HorseStats] | None
|
The horses sorted by wod id, or None when no |
Raises:
| Type | Description |
|---|---|
GameDataNotLoadedError
|
the game data cannot be loaded |
UnknownCastleError
|
|
AmbiguousCastleError
|
|
Client: CastleHorsesVO.parseParamObject (bundle line 139177),
CastlePermanentCastleData.getCastleByWorldAreaId (bundle line 139145)
castle.models.actions¶
Castle actions.
Commands:
- jca: Join a castle (answered as jaa)
- jaa: Join an area by its position
- arc: Rename castle
- rst: Relocate castle
Classes:
| Name | Description |
|---|---|
SelectCastleRequest |
Join a castle, making it the session's active castle. |
JoinAreaRequest |
Join an area by its position rather than by castle id. |
SelectCastleResponse |
The joined area's state, the |
RenameCastleRequest |
Rename a castle. |
RenameCastleResponse |
Response to castle rename. |
RelocateCastleRequest |
Start a relocation to a new position. |
RelocateCastleResponse |
Response to castle relocation. |
SelectCastleRequest
pydantic-model
¶
Bases: BaseRequest
Join a castle, making it the session's active castle.
Command: jca (answered as 'jaa') Payload: {"CID": castle_id, "KID": kingdom_id}
Castle-scoped reads such as gui answer for the joined castle.
Client: C2SJoinCastleVO (bundle line 5841); its MY_CASTLE is -1
Fields:
JoinAreaRequest
pydantic-model
¶
Bases: BaseRequest
Join an area by its position rather than by castle id.
Command: jaa Payload: {"PX": x, "PY": y, "KID": kingdom_id}
The client joins a map object this way only when it may visit it and it
is not a castle: JoinAreaAndSavePositionCommand sends jca for a
CastleMapobjectVO (main and kingdom castles) and jaa for anything
else. Its callers pass either your own areas from the castle lists or,
from a double click on the world map or the ring menu's visit button,
an object whose canBeVisited is true: outposts, capitals and
metropolises, and faction camps that are not destroyed. Every other
object (NPC camps, dungeons, kings towers, villages, monuments and the
rest) says false, and the client never joins it; a live server answered
INVALID_POSITION (6) for NPC camps and a kings tower. ARCCommand
also joins a kingdom castle by position after its first naming, and a
live server accepted that too. Joining another
player's outpost is what the client allows; whether the server accepts it
is unverified.
Client: C2SJoinAreaVO (bundle line 56128), whose key order the fields
follow; sent by JoinAreaAndSavePositionCommand.execute (bundle line
100892) and ARCCommand.executeCommand (bundle line 124969);
CastleWorldMapScreen.onDoubleClickCastle (bundle line 39601) and
ButtonVisitCastleComponent (bundle line 110159) check
canBeVisited, on InteractiveMapobjectVO (bundle line 3678),
OutpostMapobjectVO (bundle line 18829), CastleMapobjectVO
(bundle line 18924) and FactionCampMapobjectVO (bundle line 21538)
Fields:
accepts_reply
¶
Whether a jaa reply is for this area: its KID and, when sent, the joined area's row position.
Client: JAACommand (bundle line 130190) switches to the reply's KID;
AreaFactory.parseAreaInfo (bundle line 130226) reads the area from
gca.A with WorldmapObjectFactory.parseWorldMapArea (bundle line
5343), whose parser for every area type takes the position from A[1]
and A[2] (BasicMapobjectVO, InteractiveMapobjectVO,
CapitalMapobjectVO, MetropolMapobjectVO, FactionCampMapobjectVO
and the rest, bundle lines 3631-76548). A treasure camp (T 8) is the
exception: its area is not read from A, so its reply is not checked
for position.
SelectCastleResponse
pydantic-model
¶
Bases: BaseResponse
The joined area's state, the jaa the server answers a join with.
Command: jaa, the answer to both jca and a jaa by position; the
client reads both with the same code.
Payload: {"KID": kingdom_id, "T": area_type, "gca": {...}, "grc": {...}, "gpa": {...}, ...}
A block that cannot be read is None, so it does not cost the rest. The
other blocks (spl0 to spl3, gui, uap, hin, sin,
abpi, crai) are kept as sent.
Client: JAACommand.executeCommand (bundle line 130190),
AreaDataUpdater.parseJAA (bundle line 131496)
Fields:
-
error_code(int) -
kingdom_id(int) -
area_type(MapItemType | None) -
buildings(CastleBuildings | None) -
resources(CastleResources | None) -
production_area(CastleProductionArea | None) -
slum_level(SlumLevel | None) -
area_booster(AreaBooster | None) -
mines(MineStatusList | None) -
resource_carts(ResourceCartInfo | None)
Validators:
-
_area_type→area_type -
_buildings→buildings -
_resources→resources -
_production_area→production_area -
_slum_level→slum_level -
_area_booster→area_booster -
_mines→mines -
_resource_carts→resource_carts
area_type
pydantic-field
¶
area_type: MapItemType | None = None
Wire key T
The joined area's type; None for one MapItemType lacks
buildings
pydantic-field
¶
buildings: CastleBuildings | None = None
Wire key gca
The area's buildings; None when the reply has none
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The area's resources; None when the reply has none
production_area
pydantic-field
¶
production_area: CastleProductionArea | None = None
Wire key gpa
The area's production area; None when the reply has none
slum_level
pydantic-field
¶
slum_level: SlumLevel | None = None
Wire key csl
The area's slum level; None when the reply has none
area_booster
pydantic-field
¶
area_booster: AreaBooster | None = None
Wire key gab
The area's builder discount; None when the reply has none
mines
pydantic-field
¶
mines: MineStatusList | None = None
Wire key gsm
The area's mines; None when the reply has none
resource_carts
pydantic-field
¶
resource_carts: ResourceCartInfo | None = None
Wire key rci
The area's resource carts; None when the reply has none
RenameCastleRequest
pydantic-model
¶
Bases: BaseRequest
Rename a castle.
Command: arc Payload: {"CID": castle_id, "P": 0 or 1, "KID": kingdom_id, "AT": area_type, "N": name}
The keys follow the client's order: the constructor initialises CID, P, KID and AT before it sets N, which it encodes as it encodes any text it sends.
Client: C2SRenameCastleVO (bundle line 42424)
Fields:
RenameCastleResponse
pydantic-model
¶
Bases: BaseResponse
Response to castle rename.
Command: arc Payload: {"CID": castle_id, "KID": kingdom_id, "P": 0 or 1}
Client: ARCCommand.executeCommand (bundle line 124966), which looks the
castle up by CID and KID, and joins a kingdom castle's area again after a
first naming (P 0)
Fields:
-
error_code(int) -
castle_id(int) -
kingdom_id(Kingdom) -
is_rename(int)
RelocateCastleRequest
pydantic-model
¶
Bases: BaseRequest
Start a relocation to a new position.
Command: rst Payload: {"PX": x, "PY": y}
The client sends only the position: no castle id and no kingdom.
Client: C2SStartRelocationVO (bundle line 109634), sent by
CastleRelocateDialog.onClick (bundle line 109619)
Fields:
RelocateCastleResponse
pydantic-model
¶
Bases: BaseResponse
Response to castle relocation.
Command: rst
Client: RSTCommand.executeCommand (bundle line 126817)
Fields:
castle.models.buildings¶
Building commands.
Every command acts on the castle joined with jca (client.castle.select)
and names a building by its object id, a BuildingRow.object_id from the
castle's CastleBuildings (client.castle.join).
Commands:
- ebu: Build
- eup: Upgrade a building
- emo: Move a building
- sbd: Sell a decoration
- edo: Take a building down
- fco: Finish a construction at once
- msb: Shorten a construction with a minute skip
- eud: Upgrade the wall, gate or a tower
- rbu: Repair a building
- ira: Repair every building
- ebe: Buy a castle expansion
- etc: Open an expansion's treasure chest
Classes:
| Name | Description |
|---|---|
BuildRequest |
Place a new building in the joined castle. |
BuildResponse |
The new building. |
UpgradeBuildingRequest |
Upgrade a building in the joined castle. |
UpgradeBuildingResponse |
The upgraded buildings. |
MoveBuildingRequest |
Move a building in the joined castle. |
MoveBuildingResponse |
The moved building. |
SellBuildingRequest |
Sell a decoration placed in the joined castle. |
SellBuildingResponse |
The sold decoration. |
DestroyBuildingRequest |
Start taking a building in the joined castle down. |
DestroyBuildingResponse |
The building being taken down. |
FastCompleteRequest |
Finish a building's running construction at once: for free with |
FastCompleteResponse |
The finished building. |
TimeSkipBuildingRequest |
Shorten a building's running construction with a minute skip. |
TimeSkipBuildingResponse |
The construction slots after a minute skip. |
UpgradeWallRequest |
Upgrade the joined castle's wall, gate or one of its towers. |
UpgradeWallResponse |
The upgraded wall, gate or tower. |
RepairBuildingRequest |
Repair a damaged building in the joined castle. |
RepairBuildingResponse |
The building being repaired. |
RepairAllRequest |
Repair every damaged building in the joined castle at once. |
RepairAllResponse |
The repaired buildings. |
BuyExtensionRequest |
Buy an expansion of the joined castle's grounds. |
BuyExtensionResponse |
The castle after the expansion. |
CollectExtensionGiftRequest |
Open a treasure chest found on an expansion of the joined castle. |
CollectExtensionGiftResponse |
The opened chest. |
BuildRequest
pydantic-model
¶
Bases: BaseRequest
Place a new building in the joined castle.
Command: ebu Payload: {"WID": wod_id, "X": x, "Y": y, "R": rotation, "PWR": 0 or 1, "PO": offer_id, "DOID": district_id}
Keys follow the client's order: the constructor initialises PWR before PO. To place a building into a district the client sends X and Y as -1 and the district's object id as DOID.
Client: C2SIsoBuyObjectVO (bundle line 31942), sent by
IsoServerCommands.buyObjectFromShop (bundle line 63766)
Fields:
-
wod_id(int) -
x(int) -
y(int) -
rotation(int) -
pay_with_rubies(bool) -
private_offer_id(int) -
district_object_id(int)
BuildResponse
pydantic-model
¶
Bases: _BuildingReply
The new building.
Command: ebu Payload: {"NO": row, "grc": {..}, "scl": {..}, "gcu": {..}, "sin": .., "fbe": ..}
sin and fbe are kept as sent.
Client: EBUCommand.executeCommand (bundle line 122760),
AreaDataUpdater.parseEBU (bundle line 131512)
Fields:
-
error_code(int) -
building(BuildingRow | None) -
resources(CastleResources | None) -
construction_list(ConstructionList | None) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The castle's resources after
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots after
UpgradeBuildingRequest
pydantic-model
¶
Bases: BaseRequest
Upgrade a building in the joined castle.
Command: eup Payload: {"OID": object_id, "PWR": 0 or 1, "PO": offer_id}
Keys follow the client's order: the constructor initialises PWR before PO.
Client: C2SIsoUpgradeObjectVO (bundle line 31971)
Fields:
UpgradeBuildingResponse
pydantic-model
¶
Bases: _BuildingReply
The upgraded buildings.
Command: eup Payload: {"O": [row, ...], "grc": {..}, "scl": {..}, "gcu": {..}}
Client: EUPCommand.executeCommand (bundle line 122860),
AreaDataUpdater.parseEUP (bundle line 131516)
Fields:
-
error_code(int) -
buildings(list[BuildingRow]) -
resources(CastleResources | None) -
construction_list(ConstructionList | None) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The castle's resources after
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots after
MoveBuildingRequest
pydantic-model
¶
Bases: BaseRequest
Move a building in the joined castle.
Command: emo Payload: {"OID": object_id, "X": x, "Y": y, "R": rotation}
Client: C2SIsoMoveObjectVO (bundle line 63796), sent by
IsoServerCommands.moveObject (bundle line 63776) for a building outside
a district
Fields:
MoveBuildingResponse
pydantic-model
¶
Bases: _BuildingReply
The moved building.
Command: emo Payload: {"MO": row}
Client: EMOCommand.executeCommand (bundle line 122815), which also
reads the reply to a failed move, AreaDataUpdater.parseEMO (bundle line 131518)
Fields:
-
error_code(int) -
building(BuildingRow | None)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
building
pydantic-field
¶
building: BuildingRow | None = None
Wire key MO
The building where it now stands
SellBuildingRequest
pydantic-model
¶
Bases: BaseRequest
Sell a decoration placed in the joined castle.
Command: sbd Payload: {"OID": object_id}
Client: C2SellBuildingDeco (bundle line 77740), sent from the sell
dialog of a placed decoration (bundle line 28587)
Fields:
SellBuildingResponse
pydantic-model
¶
Bases: _BuildingReply
The sold decoration.
Command: sbd Payload: {"OID": object_id, "gcu": {..}}
Client: SBDCommand.executeCommand (bundle line 131722),
AreaDataUpdater.parseSBD (bundle line 131522)
Fields:
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
DestroyBuildingRequest
pydantic-model
¶
Bases: BaseRequest
Start taking a building in the joined castle down.
Command: edo Payload: {"OID": object_id}
Client: C2SIsoDisassembleObjectVO (bundle line 41065)
Fields:
DestroyBuildingResponse
pydantic-model
¶
Bases: _BuildingReply
The building being taken down.
Command: edo Payload: {"O": row, "scl": {..}}
Client: EDOCommand.executeCommand (bundle line 122781),
AreaDataUpdater.parseEDO (bundle line 131513), which reads the reply
itself as the row
Fields:
-
error_code(int) -
building(BuildingRow | None) -
construction_list(ConstructionList | None)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots after
FastCompleteRequest
pydantic-model
¶
Bases: BaseRequest
Finish a building's running construction at once: for free with FS 1, else for rubies.
Command: fco Payload: {"OID": object_id, "FS": 0 or 1}
Client: C2SIsoFastCompleteObjectVO (bundle line 31951), sent by
IsoServerCommands.fastCompleteBuilding (bundle line 63773) with FS 1 when
CastleSpecialEventData.hasSkipForFree (bundle line 139853)
Fields:
FastCompleteResponse
pydantic-model
¶
Bases: _BuildingReply
The finished building.
Command: fco Payload: {"O": row, "gcu": {..}}
Client: FCOCommand.executeCommand (bundle line 122908),
AreaDataUpdater.parseFCO (bundle line 131519)
Fields:
-
error_code(int) -
building(BuildingRow | None) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
TimeSkipBuildingRequest
pydantic-model
¶
Bases: BaseRequest
Shorten a building's running construction with a minute skip.
Command: msb Payload: {"OID": object_id, "MST": minute_skip}
Keys follow the client's order: the constructor initialises OID and sets MST after it.
Client: C2SMinuteSkipBuildingVO (bundle line 81363), built by
BuildingMinuteSkipProperties.getMinuteSkipCommand (bundle line 50021);
CastleMinuteSkipDialog.onScrollItemClick passes the currency's
jsonKey (bundle line 7722)
Fields:
-
object_id(int) -
minute_skip(EnumOrStr['Currency'])
TimeSkipBuildingResponse
pydantic-model
¶
Bases: _BuildingReply
The construction slots after a minute skip.
Command: msb Payload: {"scl": {..}}
Client: MSBCommand.executeCommand (bundle line 125767)
Fields:
-
error_code(int) -
construction_list(ConstructionList | None)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots after
UpgradeWallRequest
pydantic-model
¶
Bases: BaseRequest
Upgrade the joined castle's wall, gate or one of its towers.
Command: eud Payload: {"OID": object_id, "PO": offer_id, "PWR": 0 or 1}
Client: C2SIsoUpgradeDefenceVO (bundle line 41075)
Fields:
UpgradeWallResponse
pydantic-model
¶
Bases: _BuildingReply
The upgraded wall, gate or tower.
Command: eud Payload: {"N": row, "grc": {..}, "gcu": {..}}
Client: EUDCommand.executeCommand (bundle line 122845),
AreaDataUpdater.parseEUD (bundle line 131515)
Fields:
-
error_code(int) -
building(BuildingRow | None) -
resources(CastleResources | None) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The castle's resources after
RepairBuildingRequest
pydantic-model
¶
Bases: BaseRequest
Repair a damaged building in the joined castle.
Command: rbu Payload: {"OID": object_id, "PO": offer_id, "PWR": 0 or 1}
Client: C2SIsoRepairBuildingVO (bundle line 31961)
Fields:
RepairBuildingResponse
pydantic-model
¶
Bases: _BuildingReply
The building being repaired.
Command: rbu Payload: {"O": row, "scl": {..}, "grc": {..}, "gcu": {..}}
Client: RBUCommand.executeCommand (bundle line 123162),
AreaDataUpdater.parseRBU (bundle line 131507)
Fields:
-
error_code(int) -
building(BuildingRow | None) -
construction_list(ConstructionList | None) -
resources(CastleResources | None) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots after
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The castle's resources after
RepairAllRequest
pydantic-model
¶
Bases: BaseRequest
Repair every damaged building in the joined castle at once.
Command: ira Payload: {}
Client: C2SIsoRepairAllVO (bundle line 37983)
RepairAllResponse
pydantic-model
¶
Bases: _BuildingReply
The repaired buildings.
Command: ira Payload: {"gpa": {..}, "B": [row, ...], "gcu": {..}}
Client: IRACommand.executeCommand (bundle line 123079),
AreaDataUpdater.parseIRA (bundle line 131508)
Fields:
-
error_code(int) -
production_area(CastleProductionArea | None) -
buildings(list[BuildingRow]) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
production_area
pydantic-field
¶
production_area: CastleProductionArea | None = None
Wire key gpa
The castle's production area after
BuyExtensionRequest
pydantic-model
¶
Bases: BaseRequest
Buy an expansion of the joined castle's grounds.
Command: ebe Payload: {"X": x, "Y": y, "R": rotation, "CT": expansion_type}
Client: C2SIsoBuyExpansionVO (bundle line 63787), sent by
IsoServerCommands.buyExpansion (bundle line 63769) with the
expansion's IsoExpansionEnum id
Fields:
-
x(int) -
y(int) -
rotation(int) -
expansion_type(ExpansionType)
expansion_type
pydantic-field
¶
expansion_type: ExpansionType = ExpansionType.NORMAL
Wire key CT
Pay with resources (NORMAL) or rubies (PREMIUM)
BuyExtensionResponse
pydantic-model
¶
Bases: _BuildingReply
The castle after the expansion.
Command: ebe Payload: {"gca": {..}, "grc": {..}, "scl": {..}, "gcu": {..}, "sin": ..}
sin is kept as sent.
Client: EBECommand.executeCommand (bundle line 122743),
AreaDataUpdater.parseEBE (bundle line 131511)
Fields:
-
error_code(int) -
castle_buildings(CastleBuildings | None) -
resources(CastleResources | None) -
construction_list(ConstructionList | None) -
currencies(CurrencyBlock)
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
castle_buildings
pydantic-field
¶
castle_buildings: CastleBuildings | None = None
Wire key gca
The castle's buildings after
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The castle's resources after
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots after
CollectExtensionGiftRequest
pydantic-model
¶
Bases: BaseRequest
Open a treasure chest found on an expansion of the joined castle.
Command: etc Payload: {"OID": object_id}
Client: C2SExtensionTreasureChestVO (bundle line 86720), sent by
CastleTreasureChestBuildingDialog (bundle line 86704)
Fields:
CollectExtensionGiftResponse
pydantic-model
¶
Bases: _BuildingReply
The opened chest.
Command: etc Payload: {"RID": reward_id, "OID": object_id}
Client: AreaDataUpdater.parseETC (bundle line 131517);
CastleTreasureChestBuildingDialog.onEtcArrived (bundle line 86706)
reads RID through int()
Fields:
Validators:
-
_resources -
_production_area -
_castle_buildings -
_construction_list -
_row -
_rows
castle.models.castles¶
The player's castle list.
Commands:
- gcl: Get castles list
Classes:
| Name | Description |
|---|---|
PlayerCastle |
One row of a castle list's |
GetCastlesRequest |
Get a player's castle list. |
CastleInfo |
One of the player's locations: a castle list entry and its map row. |
GetCastlesResponse |
The player's castle list. |
PlayerCastle
pydantic-model
¶
Bases: BasePayload
One row of a castle list's AI entries, read by field position as its
area type's client parser reads it.
- Castles, outposts, kingdom castles and faction camps
(
InteractiveMapobjectVO.parseAreaInfo): object id 3, owner 4, keep, wall, gate, tower and moat 5 to 9 throughint()with keep, wall and gate at least 1, name 10, attack cooldown 11, sabotage cooldown 12, seconds since the last spy 13, outpost type 14, occupier 15, kingdom 16, equipment skin 17 and temporary sabotage protection 19 (on when it is 1); 11 to 19 throughint(), so one the row lacks reads as 0. - Capitals and metropolises: as above, but the levels as sent, 13 as sent, the occupier at 14 and the skin at 15, both as sent, and no outpost type.
- Kings towers: object id 3, owner 4, kingdom 5, seconds since the last spy 6, name 7.
- Monuments: object id 3, owner 4, monument type 5, level 6, kingdom 7, seconds since the last spy 8, name 9.
- Laboratories: object id 3, owner 4, level 5, kingdom 6, seconds since the last spy 7, name 8.
- Faction capitals: owner 3, and no object id, name or kingdom.
The client stores the row's kingdom as sent; one that is not a Kingdom
reads as the kingdom the row is listed under. An occupier the row lacks
reads as -1 here, where the client's int() would read 0: a
deliberate leniency, so a short row does not look occupied.
Client: WorldmapObjectFactory.parseWorldMapArea (bundle line 5343),
InteractiveMapobjectVO.parseAreaInfo (bundle line 3631),
CastleMapobjectVO.parseAreaInfo (bundle line 18910),
FactionCampMapobjectVO.parseAreaInfo (bundle line 21526),
CapitalMapobjectVO.parseAreaInfo (bundle line 18729),
MetropolMapobjectVO.parseAreaInfo (bundle line 21609),
KingstowerMapobjectVO.parseAreaInfo (bundle line 19055),
MonumentMapobjectVO.parseAreaInfo (bundle line 21652),
LaboratoryMapobjectVO.parseAreaInfo (bundle line 25900),
FactionCapitalMapobjectVO.parseAreaInfo (bundle line 22764)
Fields:
-
kingdom(Kingdom) -
location_id(int | None) -
x(int) -
y(int) -
castle_type(MapItemType) -
owner_id(int) -
name(str | None) -
occupier_id(int) -
keep_level(int | None) -
wall_level(int | None) -
gate_level(int | None) -
tower_level(int | None) -
moat_level(int | None) -
landmark_level(int | None) -
monument_type(int | None) -
attack_cooldown_seconds(int | None) -
sabotage_cooldown_seconds(int | None) -
seconds_since_spy(int | None) -
outpost_type(int | None) -
equipment_skin_id(int | None) -
has_sabotage_protection(bool)
location_id
pydantic-field
¶
location_id: int | None = None
The castle's object id; None for a type with none
landmark_level
pydantic-field
¶
landmark_level: int | None = None
A monument's or laboratory's level; None for any other type
monument_type
pydantic-field
¶
monument_type: int | None = None
A monument's type; None for any other type
attack_cooldown_seconds
pydantic-field
¶
attack_cooldown_seconds: int | None = None
Seconds until it can be attacked again; None for a type with none
sabotage_cooldown_seconds
pydantic-field
¶
sabotage_cooldown_seconds: int | None = None
Seconds until it can be sabotaged again; None for a type with none
seconds_since_spy
pydantic-field
¶
seconds_since_spy: int | None = None
Seconds since it was last spied on; None for a type with none
outpost_type
pydantic-field
¶
outpost_type: int | None = None
The outpost type; None for a type with none
equipment_skin_id
pydantic-field
¶
equipment_skin_id: int | None = None
Unique id of the castle skin equipped; None for a type with none
has_sabotage_protection
pydantic-field
¶
has_sabotage_protection: bool = False
Whether a temporary sabotage protection is on
is_occupied
property
¶
is_occupied: bool
Whether someone occupies this castle; the client's isOccupied is an occupier id above -1.
from_list
classmethod
¶
Read a gcl.C[].AI[].AI row; kingdom is the block it is listed under.
Raises:
| Type | Description |
|---|---|
ValueError
|
The row is not a list, its area type is not one a
castle list holds, or it is too short for its type's layout.
|
GetCastlesRequest
pydantic-model
¶
Bases: BaseRequest
Get a player's castle list.
Command: gcl Payload: {"PID": player_id}
The client always sends the logged-in player's id. Without one the
payload is {}, which the server answers with your castle list.
Client: C2SGetCastleListVO (bundle line 60768); its
MY_CASTLELIST is -1, but no caller sends it
Fields:
CastleInfo
pydantic-model
¶
Bases: BasePayload
One of the player's locations: a castle list entry and its map row.
Entry: {"AI": [row], "OGT": .., "OGC": .., "AOT": .., "CAT": .., "TA": ..}
The row's values are read by its area type's layout, the field positions
PlayerCastle lists. OGT, OGC, AOT, CAT and TA
are the entry's keys, read through int() (OGT and OGC only when
they are not 0), so one the entry lacks reads as 0. KID is the row's own
kingdom when it carries one (InteractiveMapobjectVO.parseAreaInfo, bundle
line 3637), else the block the entry is listed under. A faction capital's row carries no object
id, so it has no CastleInfo.
Client: CastleListVO.parseCastleList (bundle line 13698), which reads
the row with WorldmapObjectFactory.parseWorldMapArea
Fields:
-
castle_id(int) -
castle_name(str) -
x(int) -
y(int) -
kingdom_id(Kingdom) -
castle_type(MapItemType) -
owner_id(int) -
occupier_id(int) -
keep_level(int | None) -
wall_level(int | None) -
gate_level(int | None) -
tower_level(int | None) -
moat_level(int | None) -
landmark_level(int | None) -
monument_type(int | None) -
attack_cooldown_seconds(int | None) -
sabotage_cooldown_seconds(int | None) -
seconds_since_spy(int | None) -
outpost_type(int | None) -
equipment_skin_id(int | None) -
has_sabotage_protection(bool) -
open_gate_seconds(int) -
open_gate_counter(int) -
abandon_outpost_seconds(int) -
cancel_abandon_seconds(int) -
no_abandon_seconds(int)
kingdom_id
pydantic-field
¶
Wire key KID
The castle's kingdom: the row's own, else the block it is listed under
landmark_level
pydantic-field
¶
landmark_level: int | None = None
A monument's or laboratory's level; None for any other type
monument_type
pydantic-field
¶
monument_type: int | None = None
A monument's type; None for any other type
attack_cooldown_seconds
pydantic-field
¶
attack_cooldown_seconds: int | None = None
Seconds until it can be attacked again; None for a type with none
sabotage_cooldown_seconds
pydantic-field
¶
sabotage_cooldown_seconds: int | None = None
Seconds until it can be sabotaged again; None for a type with none
seconds_since_spy
pydantic-field
¶
seconds_since_spy: int | None = None
Seconds since it was last spied on; None for a type with none
outpost_type
pydantic-field
¶
outpost_type: int | None = None
The outpost type; None for a type with none
equipment_skin_id
pydantic-field
¶
equipment_skin_id: int | None = None
Unique id of the castle skin equipped; None for a type with none
has_sabotage_protection
pydantic-field
¶
has_sabotage_protection: bool = False
Whether a temporary sabotage protection is on
open_gate_seconds
pydantic-field
¶
open_gate_seconds: int = 0
Wire key OGT
Seconds the gate stays open
open_gate_counter
pydantic-field
¶
open_gate_counter: int = 0
Wire key OGC
How often the gate has been opened
abandon_outpost_seconds
pydantic-field
¶
abandon_outpost_seconds: int = 0
Wire key AOT
Seconds until the outpost is abandoned; not positive when it is not
cancel_abandon_seconds
pydantic-field
¶
cancel_abandon_seconds: int = 0
Wire key CAT
Seconds left to cancel abandoning the outpost
no_abandon_seconds
pydantic-field
¶
no_abandon_seconds: int = 0
Wire key TA
Seconds before the outpost may be abandoned again
is_occupied
property
¶
is_occupied: bool
Whether someone occupies it; the client's isOccupied is an occupier id above -1.
from_entry
classmethod
¶
from_entry(entry: dict[str, Any], kingdom: Kingdom = GREEN) -> CastleInfo | None
Parse a gcl.C[].AI[] entry; its AI row is read by its area type (see PlayerCastle).
None for a row the client reads but that names no castle: a faction capital, which has no object id, or a faction camp that is not on the map.
GetCastlesResponse
pydantic-model
¶
Bases: BaseResponse
The player's castle list.
Command: gcl Payload: {"PID": player_id, "C": [{"KID": kingdom, "AI": [{"AI": [row...], "AOT": .., "TA": ..}, ...]}, ...]}
Entries are flattened across kingdoms into castles.
Client: CastleListVO.parseCastleList (bundle line 13698), which gdi
uses for its gcl block too.
Fields:
-
error_code(int) -
player_id(int) -
castles(list[CastleInfo])
Validators:
-
_flatten_kingdoms
castle.models.collect¶
Mines and resource carts, and collecting from them.
Commands:
- cmr: Collect a mine
- gsm: The joined castle's mines (pushed)
- rcc: Collect a resource cart
- rci: The joined castle's resource carts (pushed)
Classes:
| Name | Description |
|---|---|
CollectMineResourcesRequest |
Collect what a mine in the joined castle has produced. |
MineStatus |
One mine's state, an entry of the |
MineStatusList |
The joined castle's mines: the |
CollectMineResourcesResponse |
The mines after collecting. |
CollectResourceCartRequest |
Collect the joined castle's resource cart of one resource. |
ResourceCart |
One resource cart, an entry of the |
ResourceCartInfo |
The joined castle's resource carts, wood, stone and food: the |
CollectResourceCartResponse |
The collected cart. |
CollectMineResourcesRequest
pydantic-model
¶
Bases: BaseRequest
Collect what a mine in the joined castle has produced.
Command: cmr Payload: {"OID": object_id}
Client: C2SCollectMineResources (bundle line 84598), sent by
AMineBuildingVE.onMouseClick (bundle line 51410)
Fields:
MineStatus
pydantic-model
¶
Bases: BasePayload
One mine's state, an entry of the gsm block's M.
Client: CastleMineData.parse_GSM (bundle line 50363) and
triggerInfoFlash (bundle line 50371)
Fields:
-
object_id(int) -
remaining_collection_amount(int) -
next_collect_seconds(int) -
coins(int | None) -
rubies(int | None)
MineStatusList
pydantic-model
¶
Bases: BaseResponse
The joined castle's mines: the gsm push, and the block of that name in
the cmr and jaa replies.
Command: gsm Payload: {"M": [{"OID": .., "RC": .., "NC": ..}, ...]}
Client: GSMCommand.executeCommand (bundle line 125752),
CastleMineData.parse_GSM (bundle line 50363)
Fields:
-
error_code(int) -
mines(list[MineStatus])
Validators:
-
_mines→mines
CollectMineResourcesResponse
pydantic-model
¶
Bases: BaseResponse
The mines after collecting.
Command: cmr Payload: {"gsm": {"M": [{"OID": .., "RC": .., "NC": .., "C1": ..}, ...]}, "gcu": {...}}
Client: CMRCommand.executeCommand (bundle line 125737)
Fields:
-
error_code(int) -
mines(MineStatusList | None) -
currencies(CurrencyBlock)
Validators:
-
_block→mines
CollectResourceCartRequest
pydantic-model
¶
Bases: BaseRequest
Collect the joined castle's resource cart of one resource.
Command: rcc Payload: {"RT": cart_type}
Client: C2SIsoResourceCartCollectVO (bundle line 87566), sent by
ResourceCartSurroundingsVE.onMouseClick (bundle line 87542)
Fields:
ResourceCart
pydantic-model
¶
Bases: BasePayload
One resource cart, an entry of the rci block's RC.
Client: ResourceCartData.parseRciItem (bundle line 81053)
Fields:
-
cart_type(ResourceCartType | None) -
amount(int) -
remaining_seconds(int | float | None)
cart_type
pydantic-field
¶
cart_type: ResourceCartType | None = None
Wire key RT
The cart's resource; None for an unknown index
ResourceCartInfo
pydantic-model
¶
Bases: BaseResponse
The joined castle's resource carts, wood, stone and food: the rci push,
and the block of that name in the rcc and jaa replies.
Command: rci Payload: {"RC": [{"RT": .., "A": .., "RS": ..}, ...]}
The client reads the first three entries, one cart each.
Client: RCICommand.executeCommand (bundle line 123196),
CastleResourceCartsData.parse_RCI (bundle line 28798)
Fields:
-
error_code(int) -
carts(list[ResourceCart])
Validators:
-
_carts→carts
CollectResourceCartResponse
pydantic-model
¶
Bases: BaseResponse
The collected cart.
Command: rcc Payload: {"RT": cart_type, "A": amount, "grc": {...}, "rci": {"RC": [...]}}
Client: RCCCommand.executeCommand (bundle line 123181),
CastleResourceCartsData.parse_RCC (bundle line 28796)
Fields:
-
error_code(int) -
cart_type(ResourceCartType | None) -
amount(int) -
resources(CastleResources | None) -
carts(ResourceCartInfo | None)
Validators:
cart_type
pydantic-field
¶
cart_type: ResourceCartType | None = None
Wire key RT
The collected cart's resource; None for an unknown index
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The castle's resources after
castle.models.details¶
Detailed castle info and the production area block.
Commands:
- dcl: Get detailed castle info
Classes:
| Name | Description |
|---|---|
GetDetailedCastleRequest |
Get resources, units and production data for every castle the player owns. |
ResourceProduction |
Hourly production per resource; the client reads |
StorageCapacity |
Storage capacity per resource ( |
ProductionBonus |
Production bonus per resource in percent ( |
SafeAmount |
The block's |
CastleProductionArea |
A castle's production area, the |
DetailedCastleInfo |
One castle's entry in the detailed castle list. |
GetDetailedCastleResponse |
Detail for every castle the player owns. |
GetDetailedCastleRequest
pydantic-model
¶
Bases: BaseRequest
Get resources, units and production data for every castle the player owns.
Command: dcl Payload: {"CD": 1}
The client's constructor defaults CD to 1, and some dialogs send 0;
what it selects is not traced, so this sends the default.
Client: C2SGetDetailedCastleListVO (bundle line 7922)
Fields:
ResourceProduction
pydantic-model
¶
StorageCapacity
pydantic-model
¶
Bases: _ProductionAreaSection
Storage capacity per resource (MR<key>), a number as sent; anything else reads as 0.
Fields:
-
wood(int | float) -
stone(int | float) -
food(int | float) -
coal(int | float) -
oil(int | float) -
glass(int | float) -
iron(int | float) -
aquamarine(int | float) -
honey(int | float) -
mead(int | float) -
beef(int | float)
Validators:
ProductionBonus
pydantic-model
¶
SafeAmount
pydantic-model
¶
Bases: _ProductionAreaSection
The block's SAFE_<key> amounts per resource.
The server sends them, but the client never reads them, so what they measure is not confirmed.
Fields:
-
wood(float) -
stone(float) -
food(float) -
coal(float) -
oil(float) -
glass(float) -
iron(float) -
aquamarine(float) -
honey(float) -
mead(float) -
beef(float)
Validators:
CastleProductionArea
pydantic-model
¶
Bases: BasePayload
A castle's production area, the gpa block.
The per-resource sections are aliased key by key and validated from the
same block, so each wire key appears once in this module. The whole-number
fields are read through int(), as the client reads them; the other
numbers read anything that is not a number as 0, as the client's
arithmetic does (MP is 0 unless it is truthy).
Client: AreaDataUpdater.parseGPA (bundle line 131506),
AreaDataCommonInfo.parseGPA (bundle line 130993),
AreaDataStorageItem.parseGPA (bundle line 131380),
AreaDataMorality.parseGPA (bundle line 131272),
DetailedCastleVO.parseGpaData (bundle line 140979)
Fields:
-
population(int) -
neutral_deco_points(int) -
sickness(int) -
riot(int) -
guards(int) -
build_speed_percent(int) -
metropolis_food_bonus(float) -
unit_capacity(int) -
auxiliary_capacity(int) -
morale(int) -
faction_buff(float) -
food_consumption_delta(float) -
food_consumption_reduction_percent(int) -
mead_consumption_delta(float) -
mead_consumption_reduction_percent(int) -
beef_consumption_delta(float) -
beef_consumption_reduction_percent(int) -
barracks_speed(float) -
workshop_speed(float) -
defense_workshop_speed(float) -
hospital_speed(float) -
production(ResourceProduction) -
storage_capacity(StorageCapacity) -
production_bonus_percent(ProductionBonus) -
safe_amount(SafeAmount)
Validators:
-
_number→faction_buff,food_consumption_delta,mead_consumption_delta,beef_consumption_delta,barracks_speed,workshop_speed,defense_workshop_speed,hospital_speed -
_truthy_number→metropolis_food_bonus -
_sections_see_the_whole_block
build_speed_percent
pydantic-field
¶
build_speed_percent: int = 100
Wire key BDB
Construction speed in percent
metropolis_food_bonus
pydantic-field
¶
metropolis_food_bonus: float = 0.0
Wire key MP
A metropolis's food production bonus
auxiliary_capacity
pydantic-field
¶
auxiliary_capacity: int = 0
Wire key AUS
How many auxiliaries the castle holds
faction_buff
pydantic-field
¶
faction_buff: float = 0.0
Wire key RFPPA
Faction strength balance, 0 to 1: below 0.5 boosts red faction morale, above 0.5 blue
food_consumption_delta
pydantic-field
¶
food_consumption_delta: float = 0.0
Wire key DFC
Food consumption per hour, times 10
food_consumption_reduction_percent
pydantic-field
¶
food_consumption_reduction_percent: int = 100
Wire key FCR
Food consumption in percent of the base
mead_consumption_delta
pydantic-field
¶
mead_consumption_delta: float = 0.0
Wire key DMEADC
Mead consumption per hour, times 10
mead_consumption_reduction_percent
pydantic-field
¶
mead_consumption_reduction_percent: int = 100
Wire key MEADCR
Mead consumption in percent of the base
beef_consumption_delta
pydantic-field
¶
beef_consumption_delta: float = 0.0
Wire key DBEEFC
Beef consumption per hour, times 10
beef_consumption_reduction_percent
pydantic-field
¶
beef_consumption_reduction_percent: int = 100
Wire key BEEFCR
Beef consumption in percent of the base
workshop_speed
pydantic-field
¶
workshop_speed: float = 0.0
Wire key RS2
Siege workshop production speed
defense_workshop_speed
pydantic-field
¶
defense_workshop_speed: float = 0.0
Wire key RS3
Defense workshop production speed
production_bonus_percent
pydantic-field
¶
production_bonus_percent: ProductionBonus
Production bonus in percent, 100 for none
DetailedCastleInfo
pydantic-model
¶
Bases: BasePayload
One castle's entry in the detailed castle list.
The client reads AID, D, the 11 resources (through int()
wherever it uses them), MC, AC, SHI, gpa and the flags
B, WS, DW and H (through Boolean()); KID is the
kingdom block the entry is listed under. The server also sends OGT,
AOT, HI and TU, which the client does not read from this reply:
it reads hospital and travelling units from gui (CastleMilitaryData.parse_GUI,
bundle line 138833). Each unit array reads as an inventory: an id sent twice adds up
(UnitInventoryDictionary, bundle lines 5533-5538).
Client: CastleUserCastleListDetailed.parseData (bundle line 140906),
DetailedCastleVO.parseData (bundle line 140973)
Fields:
-
castle_id(int) -
kingdom_id(int) -
wood(int) -
stone(int) -
food(int) -
coal(int) -
oil(int) -
glass(int) -
iron(int) -
aquamarine(int) -
honey(int) -
mead(int) -
beef(int) -
defense_value(int) -
has_barracks(bool) -
has_siege_workshop(bool) -
has_defense_workshop(bool) -
has_hospital(bool) -
market_carriages(int) -
open_gate_seconds(int) -
abandon_outpost_seconds(int) -
units(WodAmounts) -
stronghold_units(WodAmounts) -
hospital_units(WodAmounts) -
travelling_units(WodAmounts) -
production_area(CastleProductionArea | None)
Validators:
-
_area→production_area
has_siege_workshop
pydantic-field
¶
has_siege_workshop: bool = False
Wire key WS
Whether it has a siege workshop
has_defense_workshop
pydantic-field
¶
has_defense_workshop: bool = False
Wire key DW
Whether it has a defense workshop
market_carriages
pydantic-field
¶
market_carriages: int = 0
Wire key MC
The castle's market carriages
open_gate_seconds
pydantic-field
¶
open_gate_seconds: int = 0
Wire key OGT
Seconds the gate stays open
abandon_outpost_seconds
pydantic-field
¶
abandon_outpost_seconds: int = -1
Wire key AOT
Seconds until the outpost is abandoned, -1 when it is not
stronghold_units
pydantic-field
¶
Wire key SHI
Units in the stronghold (safe house)
hospital_units
pydantic-field
¶
Wire key HI
Wounded units in the hospital; the client reads them from gui, not from this reply
travelling_units
pydantic-field
¶
Wire key TU
Units on their way in (gui's in_production); the client reads them from gui, not from this reply
production_area
pydantic-field
¶
production_area: CastleProductionArea | None = None
Wire key gpa
The castle's production area; None when the entry has none
GetDetailedCastleResponse
pydantic-model
¶
Bases: BaseResponse
Detail for every castle the player owns.
Command: dcl Payload: {"PID": player_id, "C": [{"KID": kingdom, "AI": [{"AID": castle_id, "W": .., "AC": [..], "gpa": {..}}]}]}
Client: DCLCommand.executeCommand (bundle line 129299)
Fields:
Validators:
-
_flatten_kingdoms
castle
¶
castle(castle_id: int) -> DetailedCastleInfo | None
The listed castle with this id, or None.
Raises:
| Type | Description |
|---|---|
AmbiguousCastleError
|
the id is listed in several kingdoms |
castle.models.kingdoms¶
Your kingdoms: which are unlocked, and the units and goods on their way to one.
Commands:
- kpi: kingdom info, a login section of
gbd, a reply, and inside the transfer replies
Classes:
| Name | Description |
|---|---|
KingdomUnlock |
One kingdom's unlock, warehouse and slum, an entry of a |
KingdomTransfer |
Units or goods on their way to a kingdom, an entry of a |
KingdomInfoResponse |
Your kingdoms' unlocks, and the units and goods on their way to them. |
KingdomUnlock
pydantic-model
¶
Bases: TimedPayload
One kingdom's unlock, warehouse and slum, an entry of a kpi's UL.
Client: CastleKingdomVO.parseUnlockInfo (bundle line 134696); hasContor read at bundle line 50209
Fields:
-
received_at(float) -
kingdom_id(EnumOrInt[Kingdom]) -
is_unlocked(bool) -
has_warehouse(bool) -
slum_level(ClientInt) -
paid_wood(ClientNumber) -
paid_stone(ClientNumber) -
paid_food(ClientNumber) -
paid_coins(ClientNumber) -
reset_seconds(ClientNumber) -
reward_set(ClientNumber)
Validators:
-
_kingdom→kingdom_id -
_flag→is_unlocked,has_warehouse
kingdom_id
pydantic-field
¶
Wire key KID
The kingdom; -1 when not sent
has_warehouse
pydantic-field
¶
has_warehouse: bool = False
Wire key C
Whether the kingdom has a warehouse, which its resource villages need
paid_wood
pydantic-field
¶
paid_wood: ClientNumber = 0
Wire key SPW
Wood paid towards the next slum level
paid_stone
pydantic-field
¶
paid_stone: ClientNumber = 0
Wire key SPS
Stone paid towards the next slum level
paid_food
pydantic-field
¶
paid_food: ClientNumber = 0
Wire key SPF
Food paid towards the next slum level
paid_coins
pydantic-field
¶
paid_coins: ClientNumber = 0
Wire key SPC1
Coins paid towards the next slum level
reset_seconds
pydantic-field
¶
reset_seconds: ClientNumber = 0
Wire key KRS
Seconds until the kingdom resets when the values were read; 0 for none
reward_set
pydantic-field
¶
reward_set: ClientNumber = 0
Wire key CRS
The kingdom's reward set; 0 for none
KingdomTransfer
pydantic-model
¶
Bases: TimedPayload
Units or goods on their way to a kingdom, an entry of a kpi's UT or RT.
The units come as [wod_id, amount] rows, which the client keeps as they are and names by
their unit (toolTipText); here each is a UNITS collectable, its item the unit or tool.
Client: CastleKingdomUnitTransferVO.fillFromParamObject and toolTipText (bundle lines 134663,
134675), CastleKingdomGoodsTransferVO.fillFromParamObject (bundle line 134640), which reads the
goods with CollectableParserS2CParamList
Fields:
-
received_at(float) -
kingdom_id(EnumOrInt[Kingdom]) -
seconds(ClientInt) -
units(tuple[Collectable, ...]) -
goods(CollectableRows)
Validators:
-
_kingdom→kingdom_id -
_units→units
KingdomInfoResponse
pydantic-model
¶
Bases: BaseResponse
Your kingdoms' unlocks, and the units and goods on their way to them.
A kpi updates only the kingdoms it lists; the client keeps the others as they were, and
client.state.get_kingdoms() does the same. The transfers are replaced whole. The Berimond
faction block (fki) belongs to the faction event and is not read here.
Command: kpi, as a login section of gbd, a reply, and inside the kgt, kst, kut, msk
and fjf replies.
Client: KPICommand (bundle line 124684), CastleKingdomData.parse_KPI (bundle line 134539)
Fields:
-
error_code(int) -
kingdoms(tuple[KingdomUnlock, ...]) -
unit_transfers(tuple[KingdomTransfer, ...]) -
goods_transfers(tuple[KingdomTransfer, ...])
Validators:
-
_kingdoms→kingdoms
kingdoms
pydantic-field
¶
kingdoms: tuple[KingdomUnlock, ...] = ()
Wire key UL
The kingdoms this packet lists
unit_transfers
pydantic-field
¶
unit_transfers: tuple[KingdomTransfer, ...] = ()
Wire key UT
Units on their way to another kingdom
goods_transfers
pydantic-field
¶
goods_transfers: tuple[KingdomTransfer, ...] = ()
Wire key RT
Goods on their way to another kingdom
kingdom
¶
kingdom(kingdom_id: Kingdom | int) -> KingdomUnlock | None
One kingdom, None when it is not listed.
castle.models.market¶
Sending resources between castles, and the market overview.
Commands:
- crm: Send resources to a castle
- cmi: List your castles' carriages and resources
Classes:
| Name | Description |
|---|---|
CreateMarketMovementRequest |
Send resources from one of your castles to a castle on the map, by carriage. |
CreateMarketMovementResponse |
The carriage on its way. |
MarketInfoRequest |
List your castles' market carriages and resources. |
MarketCastle |
One castle in the market overview. |
MarketInfoResponse |
Your castles' market carriages and resources. |
ResourceAmounts
module-attribute
¶
ResourceAmounts = Annotated[dict[Resource, int], BeforeValidator(_resource_rows), PlainSerializer(lambda amounts: [[resource.value, amount] for resource, amount in amounts.items()])]
Resources by amount, sent as [[resource_key, amount], ...] in insertion order.
Client: CollectableParserC2SCosts.createCostsListForServer (bundle line 62921) adds up a repeated
item (combineDuplicatedItems) and then writes [itemType.serverKey, amount] per item
CreateMarketMovementRequest
pydantic-model
¶
Bases: BaseRequest
Send resources from one of your castles to a castle on the map, by carriage.
Command: crm Payload: {"KID": kingdom_id, "SID": source_castle_id, "TX": x, "TY": y, "HBW": horse, "PTT": 0 or 1, "SD": slowdown, "G": [[resource_key, amount], ...]}
Keys follow the client's order: the constructor initialises KID to SD and
sets G after them. KID is the source castle's kingdom. A horse paid
with feathers is sent as HBW -1 with PTT 1. The client merges
repeated resources in G into one entry each.
Client: C2SCreateMarketMovementVO (bundle line 68348), sent by
CastlePostSendGoodsDialog.sendGoods (bundle line 33377); G is
built by CollectableParserC2SCosts.createCostsListForServer (bundle line 62921)
Fields:
-
kingdom_id(Kingdom) -
source_castle_id(int) -
target_x(int) -
target_y(int) -
horse_booster_id(int) -
feathers(int) -
slowdown(int) -
goods(ResourceAmounts)
kingdom_id
pydantic-field
¶
Wire key KID
The source castle's kingdom
source_castle_id
pydantic-field
¶
source_castle_id: int
Wire key SID
One of your castles, CastleInfo.castle_id from client.castle.get_all() or Castle.id from client.state.get_castles()
horse_booster_id
pydantic-field
¶
horse_booster_id: int = -1
Wire key HBW
The horse booster's wod id, -1 for none or when paid with feathers
goods
pydantic-field
¶
goods: ResourceAmounts
Wire key G
The amount of each resource to send, sent as [key, amount] pairs such as ['W', 1000]
CreateMarketMovementResponse
pydantic-model
¶
Bases: BaseResponse
The carriage on its way.
Command: crm Payload: {"gcu": {...}, "grc": {...}, "O": [owner, ...], "A": movement}
O (the owners involved) and A (the new movement) are kept as sent.
Client: CRMCommand.executeCommand (bundle line 125981)
Fields:
-
error_code(int) -
currencies(CurrencyBlock) -
resources(CastleResources | None)
Validators:
-
_block→resources
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The joined castle's resources after
MarketInfoRequest
pydantic-model
¶
Bases: BaseRequest
List your castles' market carriages and resources.
Command: cmi Payload: {"S": scope, "KID": kingdom_id}
The client always sends {"S": 1, "KID": -1}: every kingdom.
Client: C2SMarketInfoVO (bundle line 27012)
Fields:
scope
pydantic-field
¶
scope: MarketScope = MarketScope.ALL_KINGDOMS
Wire key S
Which castles to list
MarketCastle
pydantic-model
¶
Bases: BasePayload
One castle in the market overview.
AE (the castle's area effects) is kept as sent.
Client: CastleTradeVO.fillFromParamObject (bundle line 140345), which
reads every number through int()
Fields:
MarketInfoResponse
pydantic-model
¶
Bases: BaseResponse
Your castles' market carriages and resources.
Command: cmi Payload: {"C": [{"KID": .., "CID": .., "TC": .., "AC": .., "W": .., ...}, ...]}
Client: CMICommand.executeCommand (bundle line 128944),
CastleTradeInfoVO.fillCastleList (bundle line 140325)
Fields:
-
error_code(int) -
castles(list[MarketCastle])
Validators:
-
_castles→castles
castle.models.objects¶
A castle's buildings and its construction queue.
Commands:
- scl: Show the construction list
Classes:
| Name | Description |
|---|---|
BuildingRow |
One placed object in a castle: a building, wall, gate, tower, ground or |
ConstructionList |
A castle's construction slots, the |
FieldEfficiency |
Resource field efficiency per resource ( |
PlacedConstructionItem |
A construction item on a building: one |
BuildingConstructionItems |
The construction items on one building: one |
CastleBuildings |
A castle's buildings, the |
ShowConstructionListRequest |
Get the joined castle's construction slots. |
ShowConstructionListResponse |
The joined castle's construction slots. |
Functions:
| Name | Description |
|---|---|
building_or_none |
A reply's building row, or None when it has none or it cannot be read. |
building_rows |
Each readable row of a list of building rows. |
Attributes:
| Name | Type | Description |
|---|---|---|
FREE_SLOT |
|
|
LOCKED_SLOT |
|
BuildingRow
pydantic-model
¶
Bases: BasePayload
One placed object in a castle: a building, wall, gate, tower, ground or fixed object, read by field position.
- 0 wod id, 1 object id, 2 x, 3 y, 4 rotation (1 for a building inside a
district); each through
int() - 5 seconds of the running construction done, 6 state, 7 hit points, 8 the construction boost at the start, times 100, 9 efficiency, 10 damage type (0 when absent)
- 11 onwards: values the building's type reads, such as a unit-producing building's production speed
- 14 the district the building sits in, 15 its slot there
- 16 the upgrade target's wod id, -1 when absent
A row may come wrapped as {"O": [...]}. Where the client reads an
absent 1 to 4, 14 or 15 as 0, so does this. Fields 5 to 10 are read only
by the building classes (ABasicBuildingVO), which the client picks by
the wod id's group and name; this reads them whenever the row reaches
index 5 and leaves them None otherwise, which is not verified to match
the client's class for every wod id.
Client: IsoHelperData.createIsoObjectVOByServer (bundle line 63049),
AVisualVO.parseServerObject (bundle line 17801),
AIsoObjectVO.parseServerObject (bundle line 11265),
ABasicBuildingVO.parseServerObject (bundle lines 17866-17868, index 5 is _constructionCompletionInSec),
AUnitProductionBuildingVO.parseServerObject (bundle line 36254)
Fields:
-
wod_id(int) -
object_id(int) -
x(int) -
y(int) -
rotation(int) -
construction_completed_seconds(int | None) -
state(BuildingState | None) -
hit_points(int | None) -
construction_boost_at_start(float | None) -
efficiency(int | None) -
damage_type(int | None) -
district_id(int) -
district_slot_id(int) -
upgrade_target_wod_id(int)
object_id
pydantic-field
¶
object_id: int = 0
The object's id in its castle, which building commands name
construction_completed_seconds
pydantic-field
¶
construction_completed_seconds: int | None = None
Seconds of the running construction done when the row was sent
construction_boost_at_start
pydantic-field
¶
construction_boost_at_start: float | None = None
The construction speed boost when the construction started, as a factor
district_id
pydantic-field
¶
district_id: int = 0
Object id of the district the building sits in, 0 for none
upgrade_target_wod_id
pydantic-field
¶
upgrade_target_wod_id: int = -1
Wod id the building is upgrading to, -1 for none
from_list
classmethod
¶
from_list(data: Any) -> BuildingRow
Read a building row, bare or wrapped as {"O": [...]}.
Raises:
| Type | Description |
|---|---|
ValueError
|
It is not a row. |
ConstructionSlot
pydantic-model
¶
Bases: BasePayload
One construction slot of a castle.
Client: ConstructionSlotVO (bundle lines 131242-131250), filled by
AreaDataConstructionList.parseList (bundle line 131170)
Fields:
object_id
pydantic-field
¶
object_id: int
Object id of the building under construction there; FREE_SLOT (-1) or LOCKED_SLOT (-2)
is_waiting
pydantic-field
¶
is_waiting: bool = False
A waiting slot: its position is at or past the castle's construction slots
ConstructionList
pydantic-model
¶
Bases: BasePayload
A castle's construction slots, the scl block.
Client: AreaDataConstructionList.parseSCL / parseList (bundle
lines 131173, 131166), which read each OIDL entry through int()
into a ConstructionSlotVO at its position
Fields:
-
slots(tuple[ConstructionSlot, ...]) -
slot_count(int | float)
Validators:
-
_slots
FieldEfficiency
pydantic-model
¶
PlacedConstructionItem
pydantic-model
¶
Bases: BasePayload
A construction item on a building: one CIL entry.
Client: ABasicBuildingVO.parseConstructionItems (bundle lines 18005-18010), which puts the item at
its slot type's first index plus int(S) and gives a temporary item the RS seconds left
Fields:
-
construction_item_id(EnumOrInt['ConstructionItem']) -
slot(ClientInt) -
remaining_seconds(ClientNumber)
construction_item_id
pydantic-field
¶
construction_item_id: EnumOrInt['ConstructionItem']
Wire key CID
The construction item
slot
pydantic-field
¶
slot: ClientInt = 0
Wire key S
Its slot among the slots of its item's slot type
remaining_seconds
pydantic-field
¶
remaining_seconds: ClientNumber = 0
Wire key RS
Seconds a temporary item has left; 0 for a permanent one
BuildingConstructionItems
pydantic-model
¶
Bases: BasePayload
The construction items on one building: one CI entry.
Client: ABasicBuildingVO.parseConstructionItems (bundle lines 18000-18006), which takes the entry
whose OID is the building's
Fields:
-
object_id(ClientInt) -
items(tuple[PlacedConstructionItem, ...])
Validators:
-
_readable→items
items
pydantic-field
¶
items: tuple[PlacedConstructionItem, ...] = ()
Wire key CIL
Its construction items
CastleBuildings
pydantic-model
¶
Bases: BasePayload
A castle's buildings, the gca block.
The block's A (the castle's map row) and O (its owner) are kept as sent.
Client: AreaDataUpdater.parseGCA (bundle line 131497),
IsoDataObjectGroupInnerBuilding.parseGCA (bundle line 80810),
IsoDataObjectGroupDefence.parseGCA (bundle line 80220),
IsoDataObjectGroupGround.parseGCA (bundle line 80771),
IsoDataObjectGroupFixedPosition.parseGCA (bundle line 80743),
AreaDataStorageItem.parseGCA (bundle line 131381), AreaDataUpdater.parseConstructionItems
(bundle line 131524)
Fields:
-
buildings(list[BuildingRow]) -
walls(list[BuildingRow]) -
gates(list[BuildingRow]) -
towers(list[BuildingRow]) -
grounds(list[BuildingRow]) -
fixed_positions(list[BuildingRow]) -
construction_list(ConstructionList | None) -
construction_items(tuple[BuildingConstructionItems, ...]) -
field_efficiency(FieldEfficiency)
Validators:
-
_rows -
_construction_list→construction_list -
_construction_items→construction_items -
_efficiency_sees_the_block
fixed_positions
pydantic-field
¶
fixed_positions: list[BuildingRow]
Wire key FP
Objects at fixed positions
construction_list
pydantic-field
¶
construction_list: ConstructionList | None = None
Wire key scl
The construction slots; None when the block has none
construction_items
pydantic-field
¶
construction_items: tuple[BuildingConstructionItems, ...] = ()
Wire key CI
The construction items on each building that has any
field_efficiency
pydantic-field
¶
field_efficiency: FieldEfficiency
Resource field efficiency per resource
ShowConstructionListRequest
pydantic-model
¶
Bases: BaseRequest
Get the joined castle's construction slots.
Command: scl Payload: {}
Client: C2SShowConstructionListVO (bundle line 63754)
ShowConstructionListResponse
pydantic-model
¶
Bases: ConstructionList, BaseResponse
The joined castle's construction slots.
Command: scl Payload: {"OIDL": [object_id, ...], "SSC": slot_count}
Client: SCLCommand.executeCommand (bundle line 123374)
Fields:
-
error_code(int) -
slots(tuple[ConstructionSlot, ...]) -
slot_count(int | float)
Validators:
-
_slots
block_or_none
¶
A reply's block read as model, or None when it is not an object or cannot be read.
building_or_none
¶
building_or_none(value: Any) -> BuildingRow | None
A reply's building row, or None when it has none or it cannot be read.
building_rows
¶
building_rows(value: Any) -> list[BuildingRow]
Each readable row of a list of building rows.
The client throws on a null or malformed entry; here one costs only itself.
castle.models.permanent¶
What each castle has unlocked: its units and its horses.
Commands:
- gpc: Permanent castle data, a login section and a push
Classes:
| Name | Description |
|---|---|
CastleUnitUnlocks |
The units and tools a castle can recruit, and those it has still locked. |
PermanentCastle |
One castle's unlocked units and horses. |
PermanentCastleDataResponse |
Every castle's unlocked units and horses. |
CastleUnitUnlocks
pydantic-model
¶
Bases: EmpireModel
The units and tools a castle can recruit, and those it has still locked.
Client: CastleUnitsVO.parseParamObject (bundle lines 139200-139207) looks each id up among
the units rows and keeps the soldiers, auxiliaries and tools
Fields:
-
unlocked_units(tuple[UnitOrTool, ...]) -
locked_units(tuple[UnitOrTool, ...])
Validators:
-
_ids→unlocked_units,locked_units
PermanentCastle
pydantic-model
¶
Bases: EmpireModel
One castle's unlocked units and horses.
Client: CastlePermanentCastleVO.parseParamObject (bundle line 139158),
CastleHorsesVO.parseParamObject (bundle line 139177)
Fields:
-
castle_id(ClientInt) -
kingdom_id(Kingdom) -
units(CastleUnitUnlocks) -
horse_ids(list[EnumOrInt['Horse']])
Validators:
-
_kingdom→kingdom_id -
_units→units -
_horses→horse_ids
units
pydantic-field
¶
units: CastleUnitUnlocks
Wire key U
Units and tools unlocked and locked there
PermanentCastleDataResponse
pydantic-model
¶
Bases: BaseResponse
Every castle's unlocked units and horses.
Command: gpc, as a login section of gbd and as a push
Payload: {"A": [{"AID": castle_id, "KID": kingdom, "U": {"U": [wod_id, ...], "L": [wod_id, ...]},
"UH": [wod_id, ...]}, ...]}
The client never asks for it: there is no C2S constant for gpc. An
update names only the castles it changes; castles it leaves out keep
what they had.
Client: GPCCommand.executeCommand (bundle line 123044),
CastlePermanentCastleData.parseGPC (bundle line 139128), read from
gbd (bundle line 129381)
Fields:
Validators:
-
_castles→castles
castle.models.resources¶
A castle's resources and production.
Commands:
- grc: Get a castle's resources
- gpa: Get the joined castle's production area
Classes:
| Name | Description |
|---|---|
CastleResources |
A castle's resources, the |
GetResourcesRequest |
Get one of your castles' resources. |
GetResourcesResponse |
A castle's resources. |
GetProductionRequest |
Get the joined castle's production area. |
GetProductionResponse |
The joined castle's production area. |
CastleResources
pydantic-model
¶
Bases: BasePayload
A castle's resources, the grc block.
Client: CastleResourcesVO.parseGRC (bundle line 131537), which reads
AID and KID through int(); AreaDataStorageItem.parseGRC
(bundle line 131382) reads each amount through int()
Fields:
-
castle_id(ClientInt) -
kingdom_id(ClientInt) -
wood(ClientInt) -
stone(ClientInt) -
food(ClientInt) -
coal(ClientInt) -
oil(ClientInt) -
glass(ClientInt) -
iron(ClientInt) -
aquamarine(ClientInt) -
honey(ClientInt) -
mead(ClientInt) -
beef(ClientInt)
GetResourcesRequest
pydantic-model
¶
Bases: BaseRequest
Get one of your castles' resources.
Command: grc Payload: {"AID": castle_id, "KID": kingdom_id}
Client: C2SGetCastleResourcesVO (bundle line 19925), sent for a
castle picked in the resource transfer dialog (bundle line 38076)
Fields:
GetResourcesResponse
pydantic-model
¶
Bases: CastleResources, BaseResponse
A castle's resources.
Command: grc Payload: {"AID": castle_id, "KID": kingdom_id, "W": .., "S": .., "F": .., ...}
Client: GRCCommand.exec (bundle line 123063), which hands the reply to
AreaDataUpdater.parseGRC (bundle line 131501)
Fields:
-
error_code(int) -
castle_id(ClientInt) -
kingdom_id(ClientInt) -
wood(ClientInt) -
stone(ClientInt) -
food(ClientInt) -
coal(ClientInt) -
oil(ClientInt) -
glass(ClientInt) -
iron(ClientInt) -
aquamarine(ClientInt) -
honey(ClientInt) -
mead(ClientInt) -
beef(ClientInt)
GetProductionRequest
pydantic-model
¶
Bases: BaseRequest
Get the joined castle's production area.
Command: gpa Payload: {}
The server answers for the castle joined with jca.
Client: C2SGetCastleProductionDataVO (bundle line 25082)
GetProductionResponse
pydantic-model
¶
Bases: CastleProductionArea, BaseResponse
The joined castle's production area.
Command: gpa
Client: GPACommand.exec (bundle line 123031), which hands the reply to
AreaDataUpdater.parseGPA (bundle line 131506)
Fields:
-
error_code(int) -
population(int) -
neutral_deco_points(int) -
sickness(int) -
riot(int) -
guards(int) -
build_speed_percent(int) -
metropolis_food_bonus(float) -
unit_capacity(int) -
auxiliary_capacity(int) -
morale(int) -
faction_buff(float) -
food_consumption_delta(float) -
food_consumption_reduction_percent(int) -
mead_consumption_delta(float) -
mead_consumption_reduction_percent(int) -
beef_consumption_delta(float) -
beef_consumption_reduction_percent(int) -
barracks_speed(float) -
workshop_speed(float) -
defense_workshop_speed(float) -
hospital_speed(float) -
production(ResourceProduction) -
storage_capacity(StorageCapacity) -
production_bonus_percent(ProductionBonus) -
safe_amount(SafeAmount)
Validators:
-
_number→faction_buff,food_consumption_delta,mead_consumption_delta,beef_consumption_delta,barracks_speed,workshop_speed,defense_workshop_speed,hospital_speed -
_truthy_number→metropolis_food_bonus -
_sections_see_the_whole_block
castle.models.support¶
Sending support to a castle, and troops between your own areas.
Commands:
- cds: Send support
- cat: Send troops to another of your own areas
- sti: Travel pre-calculation for troops sent to another of your own areas
Classes:
| Name | Description |
|---|---|
SendSupportRequest |
Send support troops to a castle. |
SendSupportResponse |
The support on its way. |
SendTroopsRequest |
Send troops from one of your areas to another of your own areas, where they stay. |
SendTroopsResponse |
The troops on their way. |
GetTravelInfoRequest |
Ask for the pre-calculation of troops sent from one of your areas to another of your own. |
TravelTargetArea |
The target's map row and its owner, the |
TravelUnits |
Your units and tools at the source, the |
GetTravelInfoResponse |
The pre-calculation of troops sent between your own areas. |
SendSupportRequest
pydantic-model
¶
Bases: BaseRequest
Send support troops to a castle.
Command: cds Payload: { "SID": source_castle_id, "TX": target_x, "TY": target_y, "LID": commander_id, "WT": wait_time, "HBW": horse_booster_id (-1 when PTT is set), "BPC": use_premium_commander, "PTT": feathers, "SD": slowdown, "A": [[unit_id, count], ...] }
The client sends no kingdom id. Fields follow its key order. BPC is
1 only when the premium commander (LID -14,
TravelConst.COMMANDER_PREMIUM) leads the army, which uses one of the
player's premium commanders or, when none are left, costs rubies
(CastlePostAttackDialog.startAttack sends sendMovement(0) for any
other commander). PTT 1 pays for the movement with feathers.
Client: C2SCreateDefenceSupportMovementVO (bundle line 133893), built
by CastleAttackData.sendSupport (bundle line 133855)
Fields:
-
source_castle_id(int) -
target_x(int) -
target_y(int) -
commander_id(int) -
wait_time(int) -
horse_booster_id(int) -
use_premium_commander(int) -
feathers(int) -
slowdown(int) -
units(WodAmountSlots)
source_castle_id
pydantic-field
¶
source_castle_id: int
Wire key SID
One of your castles, CastleInfo.castle_id from client.castle.get_all() or Castle.id from client.state.get_castles()
commander_id
pydantic-field
¶
commander_id: int
Wire key LID
A Commander.commander_id from client.commanders.get_commanders(); 0 is the free starting commander, -14 the premium one
horse_booster_id
pydantic-field
¶
horse_booster_id: int = -1
Wire key HBW
The horse booster's wod id, -1 for none or when paid with feathers
use_premium_commander
pydantic-field
¶
use_premium_commander: int = 0
Wire key BPC
1 when the premium commander leads
units
pydantic-field
¶
Wire key A
The units, then any tools, one pair per filled slot
accepts_reply
¶
Whether a cds reply is the movement this sent: its target area (A.M.TA) is TX/TY.
Client: CDSCommand (bundle line 125939) reads the new movement from A,
whose TA is the target area (BasicMapmovementVO). An army heading home
targets your own castle and is not taken.
SendSupportResponse
pydantic-model
¶
Bases: BaseResponse
The support on its way.
Command: cds Payload: {"gcu": {...}, "O": [owner, ...], "A": movement}
O (the owners involved) and A (the new movement) are kept as sent.
Client: CDSCommand.executeCommand (bundle line 125939)
Fields:
SendTroopsRequest
pydantic-model
¶
Bases: BaseRequest
Send troops from one of your areas to another of your own areas, where they stay.
Command: cat Payload: { "SX": source_x, "SY": source_y, "TX": target_x, "TY": target_y, "KID": kingdom_id, "LID": commander_id, "WT": 0, "HBW": horse_booster_id (-1 when PTT is set), "BPC": use_premium_commander, "PTT": feathers, "SD": slowdown, "A": [[wod_id, amount], ...] }
Fields follow the client's key order: the constructor initialises SX to SD
and sets A after them. The source is named by its map position, not its
id. KID is the target's kingdom, and the source must sit in the same
one. A lists the units, then the tools. The client always sends WT
0: only a support sets a wait time. A support (cds) to your own area is
refused with NO_SELF_DESTRUCTION (92); this is the command the client uses
for it.
Client: C2SCreateArmyTravelMovementVO (bundle line 99772), built by
CastleTroopSupportData.sendTroops (bundle line 38420) from
CastlePostAttackDialog.sendTroops (bundle line 38373); the army from
CastleTroopSupportVO.getArmy (bundle line 99737)
Fields:
-
source_x(int) -
source_y(int) -
target_x(int) -
target_y(int) -
kingdom_id(Kingdom) -
commander_id(int) -
wait_time(int) -
horse_booster_id(int) -
use_premium_commander(int) -
feathers(int) -
slowdown(int) -
units(WodAmountSlots)
kingdom_id
pydantic-field
¶
Wire key KID
The kingdom both areas sit in
commander_id
pydantic-field
¶
commander_id: int
Wire key LID
A Commander.commander_id from client.commanders.get_commanders(); 0 is the free starting commander, -14 the premium one
horse_booster_id
pydantic-field
¶
horse_booster_id: int = -1
Wire key HBW
The horse booster's wod id, -1 for none or when paid with feathers
use_premium_commander
pydantic-field
¶
use_premium_commander: int = 0
Wire key BPC
1 when the premium commander leads
units
pydantic-field
¶
Wire key A
The units, then any tools, one pair per filled slot
accepts_reply
¶
Whether a cat reply is the movement this sent: its target area (A.M.TA) is TX/TY.
Client: CATCommand (bundle line 125924) reads the new movement from A,
whose TA is the target area (BasicMapmovementVO). An army heading home
targets its source and is not taken.
SendTroopsResponse
pydantic-model
¶
Bases: BaseResponse
The troops on their way.
Command: cat Payload: {"O": [owner, ...], "A": movement, "gcu": {...}}
O (the owners involved) and A (the new movement) are kept as sent.
Client: CATCommand.executeCommand (bundle line 125924)
Fields:
GetTravelInfoRequest
pydantic-model
¶
Bases: BaseRequest
Ask for the pre-calculation of troops sent from one of your areas to another of your own.
Command: sti Payload: {"SX": source_x, "SY": source_y, "TX": target_x, "TY": target_y, "KID": kingdom_id}
Keys follow the client's order: the constructor initialises SX, SY, TX, TY
and KID, though it takes the target first. KID is the target's kingdom.
Client: C2STroopSupportInfoVO (bundle line 72087), sent by
CastleStartAttackDialog (bundle line 14831)
Fields:
TravelTargetArea
pydantic-model
¶
Bases: BasePayload
The target's map row and its owner, the gaa block of an sti reply.
Unlike an attack pre-calculation's, OI is one owner record, not a
list: the client reads it with parseOwnerInfo, which takes no record
without an OID.
Client: CastleTroopSupportVO.fillFromParamObject (bundle line 99736),
CastleOtherPlayerData.parseOwnerInfo (bundle line 138996)
Fields:
Validators:
-
_owner_record→owner
TravelUnits
pydantic-model
¶
Bases: BasePayload
Your units and tools at the source, the gui block of an sti reply.
Client: CastleTroopSupportVO.fillFromParamObject (bundle line 99736) reads I
into a UnitInventoryDictionary and SHI into a StrongholdUnitInventory
Fields:
-
units(WodAmounts) -
stronghold(WodAmounts)
GetTravelInfoResponse
pydantic-model
¶
Bases: BaseResponse
The pre-calculation of troops sent between your own areas.
Command: sti Payload:
{"SCID": source_castle_id, "KID": ..,
"gaa": {"AI": [target map row], "OI": {owner record}},
"gui": {"I": [[wod_id, count], ...], "SHI": [[wod_id, count], ...]},
"AE": [[effect_id, [value], source_tag], ...],
"gli": {"C": [...], "B": [...]}}
gli (your commanders) updates client.state; AE, the area
effects on the movement, is read as an attack pre-calculation's is. The
reply carries no travel time or cost: the client works them out from the
distance and the units picked. A source that is not one of your castles is refused with
NOT_IN_OWNED_CASTLE.
Client: STICommand.executeCommand (bundle line 129099), which passes
gli to CastleLordData.parse_GLI; CastleTroopSupportData.parse_STI
(bundle line 38417); CastleTroopSupportVO.fillFromParamObject (bundle
line 99736), whose base CastleFightScreenVO.fillFromParamObject reads
AE (bundle line 30501)
Fields:
-
error_code(int) -
source_castle_id(int) -
kingdom_id(Kingdom) -
target_area(TravelTargetArea) -
units(TravelUnits) -
area_effects(CommanderEffects)
source_castle_id
pydantic-field
¶
source_castle_id: int = 0
Wire key SCID
The castle the troops would leave from
kingdom_id
pydantic-field
¶
Wire key KID
The source castle's kingdom
target_area
pydantic-field
¶
target_area: TravelTargetArea
Wire key gaa
The target's map row and owner
area_effects
pydantic-field
¶
area_effects: CommanderEffects
Wire key AE
Area effects on the movement
castle.models.tax¶
Tax collection.
Commands:
- txi: Read the tax collection status
- txs: Start a tax collection
- txc: Collect the tax
Classes:
| Name | Description |
|---|---|
TaxInfo |
The tax collection status, the |
GetTaxInfoRequest |
Read the tax collection status. |
TaxInfoResponse |
The tax collection status: the |
StartTaxRequest |
Start a tax collection of one tax type. |
StartTaxResponse |
The tax status after starting a collection. |
CollectTaxRequest |
Collect the tax, also cutting a running collection short. |
CollectTaxResponse |
The collected tax. |
Attributes:
| Name | Type | Description |
|---|---|---|
TAX_DURATIONS |
tuple[int, ...]
|
Seconds each tax type collects for, by tax type 0 to 6. |
TAX_RUBY_COSTS |
tuple[int, ...]
|
Rubies each tax type costs to start before any waiver, by tax type. |
TAX_DURATIONS
module-attribute
¶
Seconds each tax type collects for, by tax type 0 to 6.
Client: TaxConst.COLLECTOR_DURATION (dll line 19772)
TAX_RUBY_COSTS
module-attribute
¶
Rubies each tax type costs to start before any waiver, by tax type.
Types 5 and 6 cost rubies unless a premium account, a VIP level or the tax research waives it; types 1 to 4 cost a tenth of their income in coins and type 0 is free.
Client: TaxConst.START_COST_C2, START_COST_C1_PERC,
getCollectorC2Costs (dll lines 19773-19779)
TaxInfo
pydantic-model
¶
Bases: BasePayload
The tax collection status, the TX of a txi block.
Client: TaxInfoVO.fillFromParamObject (bundle line 29538)
Fields:
-
tax_type(int) -
remaining_seconds(int) -
expected_income(int) -
population(int) -
is_boosted(bool) -
vip_bonus(int)
Validators:
-
_boosted→is_boosted
tax_type
pydantic-field
¶
tax_type: int = 0
Wire key TT
The running or finished collection's tax type, 0 to 6; -1 for none
remaining_seconds
pydantic-field
¶
remaining_seconds: int = 0
Wire key RT
Seconds until the collection is done, as of the reply
expected_income
pydantic-field
¶
expected_income: int = 0
Wire key EM
Coins the collection brings before boosts
status
property
¶
status: TaxStatus
Whether a collection runs or waits to be collected, as of the reply.
The client keeps the end time, the reply's time plus RT, and counts down to it: a
collection is collecting while the remaining time is 0 or more, so one with RT 0 is
still collecting as of the reply, and ready to collect once the end time has passed.
Client: TaxInfoVO.taxState and remainingCollectionTimeInSeconds (bundle lines
29542, 29544), fillFromParamObject (bundle line 29540)
GetTaxInfoRequest
pydantic-model
¶
Bases: BaseRequest
Read the tax collection status.
Command: txi Payload: {}
Client: C2SGetTaxInfoVO (bundle line 37186), sent by
CastleCollectTaxStartDialog.showLoaded (bundle line 52962) and
CastleCollectTaxDialog.showLoaded (bundle line 91005)
TaxInfoResponse
pydantic-model
¶
Bases: BaseResponse
The tax collection status: the txi reply, also nested in txs and txc.
Command: txi Payload: {"TX": {"TT": .., "RT": .., "EM": .., "PO": .., "IB": .., "VB": ..}}
Client: TXICommand.executeCommand (bundle line 128792),
CastleTaxData.parse_TXI (bundle line 112797)
Fields:
-
error_code(int) -
tax(TaxInfo)
StartTaxRequest
pydantic-model
¶
Bases: BaseRequest
Start a tax collection of one tax type.
Command: txs Payload: {"TT": tax_type, "TX": 3}
Client: C2SStartCollectTaxVO (bundle line 91088), sent by
CastleCollectTaxElement.onStartTaxCollection (bundle line 91069)
Fields:
StartTaxResponse
pydantic-model
¶
Bases: BaseResponse
The tax status after starting a collection.
Command: txs Payload: {"gcu": {...}, "txi": {"TX": {...}}}
Client: TXSCommand.executeCommand (bundle line 128807)
Fields:
-
error_code(int) -
currencies(CurrencyBlock) -
tax_info(TaxInfoResponse | None)
currencies
pydantic-field
¶
currencies: CurrencyBlock = None
Wire key gcu
Coins and rubies after the start
tax_info
pydantic-field
¶
tax_info: TaxInfoResponse | None = None
Wire key txi
The tax status after the start; None when the reply has none
CollectTaxRequest
pydantic-model
¶
Bases: BaseRequest
Collect the tax, also cutting a running collection short.
Command: txc Payload: {"TR": 29}
Client: C2SCollectTaxVO (bundle line 41083), sent by
IsoServerCommands.collectTax (bundle line 63777) and
CastleCollectTaxDialog.collectTax (bundle line 91027)
Fields:
CollectTaxResponse
pydantic-model
¶
Bases: BaseResponse
The collected tax.
Command: txc Payload: {"gcu": {...}, "txi": {"TX": {...}}, "CT": coins}
Client: TXCCommand.executeCommand (bundle line 128747)
Fields:
-
error_code(int) -
currencies(CurrencyBlock) -
tax_info(TaxInfoResponse | None) -
collected(int)
currencies
pydantic-field
¶
currencies: CurrencyBlock = None
Wire key gcu
Coins and rubies after collecting
tax_info
pydantic-field
¶
tax_info: TaxInfoResponse | None = None
Wire key txi
The tax status after collecting; None when the reply has none
castle.models.transfers¶
Moving units and goods to another kingdom.
Commands:
- kut: Transfer units from a castle to another kingdom
- kgt: Transfer goods from a castle to another kingdom
- msk: Shorten a transfer to another kingdom with a minute skip
Classes:
| Name | Description |
|---|---|
KingdomUnitTransferRequest |
Send units from one of your castles to another kingdom. |
KingdomUnitTransferResponse |
The transfer's result. |
KingdomGoodsTransferRequest |
Send goods from one of your castles to your castle in another kingdom. |
KingdomGoodsTransferResponse |
The transfer's result. |
MinuteSkipKingdomTransferRequest |
Shorten the units or goods on their way to a kingdom with a minute skip. |
MinuteSkipKingdomTransferResponse |
The transfers after a minute skip. |
KingdomUnitTransferRequest
pydantic-model
¶
Bases: BaseRequest
Send units from one of your castles to another kingdom.
Command: kut Payload: {"SCID": source_castle_id, "SKID": source_kingdom, "TKID": target_kingdom, "CID": target_castle_id, "A": [[wod_id, amount], ...]}
Keys follow the client's order: the constructor initialises SCID, SKID,
TKID and CID before it sets A. CID is the picked target castle's
object id, -1 when none is picked.
Client: C2SKingdomUnitTransferVO (bundle line 95305), built by
CastleTransferTroopsToKingdomProperties.getUnitTransferCommand
(bundle line 37448) with the units as getAsWodAmountArray pairs
Fields:
-
source_castle_id(int) -
source_kingdom_id(Kingdom) -
target_kingdom_id(Kingdom) -
target_castle_id(int) -
units(WodAmountSlots)
source_castle_id
pydantic-field
¶
source_castle_id: int
Wire key SCID
One of your castles, CastleInfo.castle_id from client.castle.get_all() or Castle.id from client.state.get_castles()
source_kingdom_id
pydantic-field
¶
Wire key SKID
The source castle's kingdom
target_kingdom_id
pydantic-field
¶
target_kingdom_id: Kingdom
Wire key TKID
The kingdom to send the units to
KingdomUnitTransferResponse
pydantic-model
¶
Bases: BaseResponse
The transfer's result.
Command: kut Payload: {"gcu": {...}, "gui": {...}, "kpi": {...}}
gui (the source castle's units after) is kept as sent; kpi updates client.state.get_kingdoms().
Client: KUTCommand.executeCommand (bundle line 124732)
Fields:
KingdomGoodsTransferRequest
pydantic-model
¶
Bases: BaseRequest
Send goods from one of your castles to your castle in another kingdom.
Command: kgt Payload: {"SCID": source_castle_id, "SKID": source_kingdom, "TKID": target_kingdom, "G": [[resource_key, amount], ...]}
Keys follow the client's order: the constructor initialises SCID, SKID and
TKID before it sets G. G is built as the market's is, a repeated
resource merged into one entry.
Client: C2SKingdomGoodsTransferVO (bundle line 98402), built by
CastleTransferResToKingdomProperties.getSendResourcesCommand (bundle line 55548)
from CastleTransferResourcesDialog.sendResources (bundle line 38082), which
builds G with createCostsListForServer (bundle line 38086)
Fields:
-
source_castle_id(int) -
source_kingdom_id(Kingdom) -
target_kingdom_id(Kingdom) -
goods(ResourceAmounts)
source_castle_id
pydantic-field
¶
source_castle_id: int
Wire key SCID
One of your castles, CastleInfo.castle_id from client.castle.get_all() or Castle.id from client.state.get_castles()
source_kingdom_id
pydantic-field
¶
Wire key SKID
The source castle's kingdom
target_kingdom_id
pydantic-field
¶
target_kingdom_id: Kingdom
Wire key TKID
The kingdom to send the goods to
goods
pydantic-field
¶
goods: ResourceAmounts
Wire key G
The amount of each resource to send, sent as [key, amount] pairs such as ['W', 1000]
KingdomGoodsTransferResponse
pydantic-model
¶
Bases: BaseResponse
The transfer's result.
Command: kgt Payload: {"gcu": {...}, "grc": {...}, "kpi": {...}}
gcu and kpi update client.state (client.state.get_kingdoms() lists the goods on
their way); grc is the joined castle's resources after, kept here.
Client: KGTCommand.executeCommand (bundle line 124658)
Fields:
-
error_code(int) -
currencies(CurrencyBlock) -
resources(CastleResources | None)
Validators:
-
_block→resources
resources
pydantic-field
¶
resources: CastleResources | None = None
Wire key grc
The joined castle's resources after
MinuteSkipKingdomTransferRequest
pydantic-model
¶
Bases: BaseRequest
Shorten the units or goods on their way to a kingdom with a minute skip.
Command: msk Payload: {"MST": minute_skip, "KID": "kingdom_id", "TT": "transfer_type"}
Keys follow the client's order. KID is the kingdom the transfer goes
to; the client sends KID and TT through toString().
Client: C2SMinuteSkipKingdomTransferVO (bundle line 54609), built by
KingdomUnitsTravelMinuteSkipProperties.getMinuteSkipCommand (bundle line 37497)
and KingdomGoodsTravelMinuteSkipProperties.getMinuteSkipCommand (bundle line 55567);
CastleMinuteSkipDialog.onScrollItemClick passes the currency's jsonKey (bundle line 7722)
Fields:
-
minute_skip(EnumOrStr['Currency']) -
kingdom_id(Kingdom) -
transfer_type(KingdomTransferType)
minute_skip
pydantic-field
¶
minute_skip: EnumOrStr['Currency']
Wire key MST
The minute skip used, Currency.SKIP_1_MINUTE to SKIP_24_HOURS; sent as its key
transfer_type
pydantic-field
¶
transfer_type: KingdomTransferType
Wire key TT
Whether the transfer carries units or goods
MinuteSkipKingdomTransferResponse
pydantic-model
¶
Bases: BaseResponse
The transfers after a minute skip.
Command: msk Payload: {"kpi": {...}}
kpi updates client.state.get_kingdoms().
Client: MSKCommand.executeCommand (bundle line 125799)
Fields:
castle.models.updates¶
Castle pushes: units received, building XP, changed buildings, slum level and area booster.
Commands (all pushed by the server):
- rue: Units received in a castle
- fbe: A building finished
- cbx: A building gave XP
- gdb: Damaged buildings of the joined castle
- gcb: Buildings of the joined castle whose efficiency changed
- csl: The joined castle's slum level
- gab: The joined castle's area booster
Classes:
| Name | Description |
|---|---|
UnitsReceived |
Units that finished and arrived in one of your castles: the castle's new count of that unit. |
BuildingXP |
XP a building in the joined castle gave. |
BuildingFinished |
A building in the joined castle finished, and the XP it gave. |
DamagedBuildings |
Buildings of the joined castle that were damaged, as their rows are now. |
BuildingEfficiencyChanged |
Buildings of the joined castle whose efficiency changed, as their rows are now. |
SlumLevel |
The joined castle's slum level. |
AreaBooster |
The joined castle's builder discount. |
UnitsReceived
pydantic-model
¶
Bases: BaseResponse
Units that finished and arrived in one of your castles: the castle's new count of that unit.
Command: rue Payload: {"AID": castle_id, "SID": kingdom_id, "WID": wod_id, "NUA": amount}
Client: RUECommand.executeCommand (bundle line 125646),
CastleUserCastleListDetailed.parse_rue (bundle line 140918),
CastleMilitaryData.parse_rue (bundle line 138834)
Fields:
-
error_code(int) -
castle_id(ClientInt) -
kingdom_id(ClientInt) -
wod_id(UnitOrTool) -
amount(ClientInt)
BuildingXP
pydantic-model
¶
Bases: BaseResponse
XP a building in the joined castle gave.
Command: cbx Payload: {"OID": object_id, "XP": xp}
Client: CBXCommand.executeCommand (bundle line 123345),
AreaDataUpdater.parseCBX (bundle line 131521),
IsoUpdaterData.onGainedBuildingPoints (bundle line 130308)
Fields:
BuildingFinished
pydantic-model
¶
Bases: BuildingXP
A building in the joined castle finished, and the XP it gave.
Command: fbe Payload: {"OID": object_id, "XP": xp}
Client: FBECommand.exec (bundle line 122879), which reads it as cbx
Fields:
DamagedBuildings
pydantic-model
¶
Bases: BaseResponse
Buildings of the joined castle that were damaged, as their rows are now.
Command: gdb Payload: {"B": [row, ...]}
Client: GDBCommand.executeCommand (bundle line 122998),
AreaDataUpdater.parseGDB (bundle line 131510),
IsoUpdaterData.updateMultipleObjectInfos (bundle line 130259)
Fields:
Validators:
-
_rows→buildings
BuildingEfficiencyChanged
pydantic-model
¶
Bases: DamagedBuildings
Buildings of the joined castle whose efficiency changed, as their rows are now.
Command: gcb Payload: {"B": [row, ...]}
Client: GCBCommand.executeCommand (bundle line 122968),
AreaDataUpdater.parseGCB (bundle line 131509)
Fields:
Validators:
-
_rows→buildings
SlumLevel
pydantic-model
¶
Bases: BaseResponse
The joined castle's slum level.
Command: csl Payload: {"SL": level}
Client: CSLCommand.executeCommand (bundle line 122715),
AreaDataSlum.parseCSL (bundle line 131305)
Fields:
-
error_code(int) -
level(int)
AreaBooster
pydantic-model
¶
Bases: BaseResponse
The joined castle's builder discount.
Command: gab Payload: {"B": discount}
Client: GABCommand.executeCommand (bundle line 122923),
AreaDataCommonInfo.parseGAB (bundle line 130992)
Fields:
-
error_code(int) -
builder_discount(int | float)
castle.models.upkeep¶
Pushes that keep the castle screen current: construction item expiry and the resource citizen.
Commands:
- nec: when the next construction item expires, a login section of
gbdand a push - irc: the resource the citizen in your castle carries, a push
Classes:
| Name | Description |
|---|---|
ConstructionItemExpiryResponse |
When the next of your construction items expires, and when one last expired. |
ResourcePoolResponse |
The resource the citizen walking in your castle carries, to collect by clicking it. |
ConstructionItemExpiryResponse
pydantic-model
¶
Bases: TimedResponse
When the next of your construction items expires, and when one last expired.
The push's CI (the joined castle's construction items) is not read here.
Command: nec, as a login section of gbd and a push.
Client: NECCommand (bundle line 123294), ConstructionItemData.parse_NEC and
hasNewExpiredItems (bundle lines 22976, 22992)
Fields:
-
received_at(float) -
error_code(int) -
next_expiry_seconds(ClientNumber) -
last_expired_at(ClientNumber)
next_expiry_seconds
pydantic-field
¶
next_expiry_seconds: ClientNumber = -1
Wire key NCRS
Seconds until the next construction item expires when the values were read, -1 for none
last_expired_at
pydantic-field
¶
last_expired_at: ClientNumber = -1
Wire key LECT
When a construction item last expired, the server's timestamp; -1 none
ResourcePoolResponse
pydantic-model
¶
Bases: BaseResponse
The resource the citizen walking in your castle carries, to collect by clicking it.
Command: irc, a push.
Client: IRCCommand (bundle line 123090), CastleResourcePoolData.parseIRC (bundle line 139623), which
keeps the first collectable of G and sets its amount to 1000 when EG is set. The client drops it
when you join a castle (JAACommand, bundle line 130194), as client.state does, and also when the
citizen carrying it is collected or leaves (registerNewMovementAsOwner, bundle line 139626), which
the state does not follow, so it can outlast the citizen until the next irc.
Fields:
Validators:
-
_extra→has_extra_goods
castle.models.wishing_well¶
The ruby wishing well.
Commands:
- rww: the wishing well's level and time, a login section of
gbdand a reply
Classes:
| Name | Description |
|---|---|
WishingWellResponse |
The ruby wishing well: its level and whether it can be started, runs, or waits to be collected. |
WishingWellResponse
pydantic-model
¶
Bases: TimedResponse
The ruby wishing well: its level and whether it can be started, runs, or waits to be collected.
Command: rww, as a login section of gbd and a successful reply; a reply refused for lack of
resources is not read.
Client: RWWCommand (bundle line 126903), CastleRubyWishingWellData.parse_RWW (bundle line 21255),
isReadyToStart, isReadyToCollect, isRunning and getRemainingSecondsCalculated
(bundle lines 21248-21262)
Fields:
seconds
pydantic-field
¶
seconds: ClientNumber = -1
Wire key RT
Seconds left when the values were read: -1 ready to start, 0 ready to collect
is_ready_to_start
property
¶
is_ready_to_start: bool
Whether the wishing well can be started, as last read.