Game Data
The following provides a detailed how-to for use of static game data that is loaded into clashy.py.
All game data has been retrieved from https://coc.guide and is freely available.
Why?
While the API provides all data about a player’s current troops and spells, often people want to know “does this player have a maxed Barbarian for their TH level?” or “how much does it cost to upgrade all a player’s spells to max?”.
This then results in an often clumsy and hard-to-maintain dictionary, csv, json or other form of static data being made and utilised. The integration of these game files into clashy.py means there’s less need to duplicate this process, and upgrading/updating code should be as easy as upgrading versions of clashy.py whenever an update lands.
Other benefits include integration into existing Troop and other data models, meaning the process for getting
a troop’s upgrade cost for a player is the exact same as getting the troop’s name, or level. But more on that later.
Additionally, speed and memory consumption has been prioritised and optimised where possible.
Initialising the Client
There are a few options you can choose from as to how clashy.py will handle when to inject game data and when not to. Although all efforts have been made to minimise overheads, some may prefer to only use game data when they choose to.
Always
If you want clashy.py to always inject game metadata, regardless of when or where you call
Client.get_player(), including in events, use this option.Game metadata will be loaded on startup using this option, which means you can use
Client.parse_army_link()etc. without issue.
client = coc.login('email', 'password', load_game_data=coc.LoadGameData(always=True))
Default
In this option, clashy.py will always inject game metadata when using
Client.get_player(), however will not load game metadata for any events dispatched. Instead see Loading Game Data Manually for how to load it when using events.Game metadata will be loaded on startup using this option, which means you can use
Client.parse_army_link()etc. without issue.
client = coc.login('email', 'password', load_game_data=coc.LoadGameData(default=True))
Startup Only
With this option, clashy.py will load game metadata on startup, but will never automatically load it into Player objects. This means you must manually call
Player.load_game_data()to load game data for a player.Using this option means game metadata is loaded on startup, which means
Client.parse_army_link()etc. will will work fine.
client = coc.login('email', 'password', load_game_data=coc.LoadGameData(startup_only=True))
Never
With this option, game metadata is not loaded on startup, and will never be injected into player objects. This means that you cannot use
Client.parse_army_link()methods, nor thePlayer.load_game_data()to manually load the data.
client = coc.login('email', 'password', load_game_data=coc.LoadGameData(never=True))
Loading Game Data Manually
The preferred way of telling clashy.py that you want to load game data for player requests is either through setting
load_game_metadata to Always or Default, or by passing load_game_data=True into your Client.get_player() call.
player = await client.get_player("#tag", load_game_data=True)
Alternatively, if you initialised the client with load_game_data set to Always or Default and don’t wish to
load game data, you can set it to False:
player = await client.get_player("#tag", load_game_data=False)
By default, however, it is set to whatever you initialised at Client startup.
If you’re using the EventsClient and don’t have load_game_metadata set to Always, your player events
won’t have metadata loaded. This is easy to fix, with the Player.load_game_data() call:
@client.event
@coc.PlayerEvents.troop_change()
async def player_troop_upgraded(cached_player, player, troop):
player.load_game_data()
print("Player {} upgraded {}, which costs {} to upgrade again.", player.name, troop.name, troop.upgrade_cost)
Warning
This is not designed for regular use. Please don’t use it where there’s an alternative. It manually injects troop data into the existing objects which takes longer than creating them at the start of the call. The example given is the only use-case I can think of at present.
Accessing Game Data
When you retrieve a player with game data loaded, all Troop, Spell, Hero, Pet,
and Equipment objects will have their game data attributes populated based on their current level.
All attributes return their appropriate type (int, bool, TimeDelta, etc.)
player = await client.get_player("#2pp")
barb = player.get_troop("Barbarian")
print(barb.dps) # prints 56
print(barb.hitpoints) # prints 129
You can dynamically change the level to see stats at different levels:
player = await client.get_player("#2pp")
barb = player.get_troop("Barbarian")
barb.level += 1 # set the level to be 1 higher
print(barb.dps) # prints stats for the new level
print(barb.hitpoints) # prints stats for the new level
To iterate over all levels of a troop:
player = await client.get_player("#2pp")
barb = player.get_troop("Barbarian")
for level in range(1, barb.max_level + 1):
barb.level = level
print(f"Level {level}: {barb.dps} DPS, {barb.hitpoints} HP, costs {barb.upgrade_cost} to upgrade")
Available Attributes
Game data provides many attributes for troops, spells, heroes, pets, and equipment. Common level-varying attributes include:
dps- Damage per secondhitpoints- Health pointsupgrade_cost- Cost to upgrade to the next levelupgrade_time- Time required to upgraderequired_townhall- Townhall level requiredrequired_lab_level- Laboratory level required (troops/spells)
See the API documentation for Troop, Spell, Hero, Pet, and Equipment
for complete attribute lists