"""Bedwars."""
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, model_validator
from asyncpixel.models.utils import safe_divide
# Amount of levels to prestige
LEVELS_PER_PRESTIGE = 100
# The exp required to level up once
LEVEL_COST = 5000
# The exp required to level up to the first few levels after a prestige
EASY_LEVEL_COSTS = {1: 500, 2: 1000, 3: 2000, 4: 3500}
# The exp required to level up past the easy levels
EASY_EXP = sum(EASY_LEVEL_COSTS.values())
# The amount of easy levels
EASY_LEVELS = len(EASY_LEVEL_COSTS)
# The exp required to prestige
PRESTIGE_EXP = EASY_EXP + (100 - EASY_LEVELS) * LEVEL_COST
def bedwars_level_from_exp(exp: int) -> float:
"""Bedwars level/star.
Args:
exp (int): the player's bedwars experience
Returns:
float: bedwars level + progress towards next level
"""
levels = (exp // PRESTIGE_EXP) * LEVELS_PER_PRESTIGE
exp %= PRESTIGE_EXP
# The first few levels have different costs
for i in range(1, EASY_LEVELS + 1):
cost = EASY_LEVEL_COSTS[i]
if exp >= cost:
levels += 1
exp -= cost
else:
# We can't afford the next level, so we have found the level we are at
break
levels += exp // LEVEL_COST
exp %= LEVEL_COST
next_level = (levels + 1) % LEVELS_PER_PRESTIGE
# The cost of the next level
if next_level in EASY_LEVEL_COSTS:
next_level_cost = EASY_LEVEL_COSTS[next_level]
else:
next_level_cost = LEVEL_COST
return levels + exp / next_level_cost
[docs]class BedwarsGame(BaseModel):
"""Bedwars GameMode stats.
Args:
items_purchased (int): Total Items purchased. Defaults to 0.
diamond_resources_collected (int): Total diamonds collected. Defaults to 0.
games_played (int): Number of games played. Defaults to 0.
losses (int): Games lost for this Game Mode. Defaults to 0.
gold_resources_collected (int): Total gold collected. Defaults to 0.
void_deaths (int): Deaths to the void Defaults to 0.
deaths (int): Total deaths. Defaults to 0.
winstreak (int): Current winstreak. Defaults to 0.
beds_lost (int): Total beds lost. Defaults to 0.
final_deaths (int): Total final deaths. Defaults to 0.
entity_attack_deaths (int): Number of deaths to an entity. Defaults to 0.
beds_broken (int): Beds broken. Defaults to 0.
entity_attack_final_deaths (int): Number of deaths to an entity. Defaults to 0.
fall_deaths (int): Total deaths from fall damage. Defaults to 0.
magic_deaths (int): Total deaths to magic. Defaults to 0.
permanent_items_purchased (int): Number of items that are not lost
in the game purchased. Defaults to 0.
void_kills (int): Kills using the void. Defaults to 0.
kills (int): Total Total kills. Defaults to 0.
wins (int): Total games won. Defaults to 0.
void_final_deaths (int): Total final deaths to the void. Defaults to 0.
final_kills (int): Total final kills. Defaults to 0.
"""
items_purchased: int = Field(0, alias="items_purchased_bedwars")
diamond_resources_collected: int = Field(0, alias="diamond_resources_collected_bedwars")
games_played: int = Field(0, alias="games_played_bedwars")
losses: int = Field(0, alias="losses_bedwars")
iron_resources_collected: int = Field(0, alias="iron_resources_collected_bedwars")
resources_collected: int = Field(0, alias="resources_collected_bedwars")
gold_resources_collected: int = Field(0, alias="gold_resources_collected_bedwars")
void_deaths: int = Field(0, alias="void_deaths_bedwars")
deaths: int = Field(0, alias="deaths_bedwars")
winstreak: int = Field(0, alias="winstreak")
beds_lost: int = Field(0, alias="beds_lost_bedwars")
final_deaths: int = Field(0, alias="final_deaths_bedwars")
entity_attack_deaths: int = Field(0, alias="entity_attack_deaths_bedwars")
beds_broken: int = Field(0, alias="beds_broken_bedwars")
entity_attack_final_deaths: int = Field(0, alias="entity_attack_final_deaths_bedwars")
fall_deaths: int = Field(0, alias="fall_deaths_bedwars")
magic_deaths: int = Field(0, alias="magic_deaths_bedwars")
permanent_items_purchased: int = Field(0, alias="permanent _items_purchased_bedwars")
void_kills: int = Field(0, alias="void_kills_bedwars")
kills: int = Field(0, alias="kills_bedwars")
wins: int = Field(0, alias="wins_bedwars")
void_final_deaths: int = Field(0, alias="void_final_deaths_bedwars")
final_kills: int = Field(0, alias="final_kills_bedwars")
@property
def win_per_lose(self) -> float:
"""Wins per losses.
Returns:
float: ratio between game wins and game losses.
"""
return safe_divide(self.wins, self.losses)
@property
def beds_broken_per_lost(self) -> float:
"""Beds broken per Beds lost.
Returns:
float: ratio between beds broken and lost.
"""
return safe_divide(self.beds_broken, self.beds_lost)
@property
def final_kills_per_final_death(self) -> float:
"""Final kills per final death.
Returns:
float: ratio between final kills and final deaths.
"""
return safe_divide(self.final_kills, self.final_deaths)
[docs]class Bedwars(BaseModel):
"""Bedwars Stats.
Args:
kills (int): Total kills across all Bedwars gamemodes. Defaults to 0.
wins (int): Total wins across all Bedwars gamemodes. Defaults to 0.
coins (int): Total coins collected. Defaults to 0.
games_played (int): Number of bedwars games played. Defaults to 0.
final_deaths (int): Number of final deaths across all Bedwars gamemodes.
Defaults to 0.
deaths (int): Number of deaths across all Bedwars gamemodes. Defaults to 0.
final_kills (int): Number of final kills across all Bedwars gamemodes.
Defaults to 0.
losses (int): Total bedwars games lost. Defaults to 0.
beds_lost (int): Number of beds lost across all Bedwars gamemodes.
Defaults to 0.
beds_broken (int): Number of beds broken across all Bedwars gamemodes.
Defaults to 0.
winstreak (int): Current winstreak. Defaults to 0.
experience (int): Total bedwars experience. Defaults to 0.
singles (Optional[BedwarsGame]): Stats for the singles Gamemode.
doubles (Optional[BedwarsGame]): Stats for the doubles Gamemode.
triples (Optional[BedwarsGame]): Stats for the triples Gamemode.
quads (Optional[BedwarsGame]): Stats for the quads Gamemode.
four_v_four (Optional[BedwarsGame]): Stats for the four vs four Gamemode.
quads_ultimate (Optional[BedwarsGame]): Stats for the quads ultimate Gamemode.
doubles_ultimate (Optional[BedwarsGame]): Stats for the doubles
ultimate Gamemode.
castle (Optional[BedwarsGame]): Stats for the castle Gamemode.
entity_attack_deaths (int): Deaths to an entity. Defaults to 0.
entity_attack_final_deaths (int): Final deaths to an entity. Defaults to 0.
fall_deaths (int): Overall fall deaths. Defaults to 0.
fall_final_deaths (int): Overall final fall deaths Defaults to 0.
projectile_deaths (int): Projectile deaths. Defaults to 0.
suffocation_deaths (int): Overall suffocation deaths. Defaults to 0.
magic_deaths (int): Overall magic deaths. Defaults to 0.
entity_explosion_deaths (int): Overall explosion deaths. Defaults to 0.
magic_final_deaths (int): Overall magic final deaths. Defaults to 0.
void_deaths (int): Overall void deaths. Defaults to 0.
void_final_death (int): Overall void final deaths. Defaults to 0.
fire_tick_final_death (int): Overall fire final deaths. Defaults to 0.
void_kills (int): Overall void kills. Defaults to 0.
entity_attack_kills (int): Overall entity attack kills. Defaults to 0.
entity_attack_final_kills (int): Overall entity attack final kills.
Defaults to 0.
projectile_final_kills (int): Overall projectile final kills. Defaults to 0.
entity_explosion_kills (int): Overall entity explosion kills. Defaults to 0.
projectile_kills (int): Overall projectile kills. Defaults to 0.
fall_final_kills (int): Overall final fall kills. Defaults to 0.
fall_kills (int): Overall fall kills. Defaults to 0.
resources_collected (int): Overall resources collected. Defaults to 0.
iron_resources_collected (int): Overall iron resources collected. Defaults to 0.
diamond_resources_collected (int): Overall diamond resources collected.
Defaults to 0.
emerald_resources_collected (int): Overall emerald resources collected.
Defaults to 0.
items_purchased (int): Overall items purchased. Defaults to 0.
permanent_items_purchased (int): Overall permanent items purchased.
Defaults to 0.
bedwars_box_rares (int): Bedwars boxes that are rare. Defaults to 0.
bedwars_box (int): Total Bedwars boxes. Defaults to 0.
chest_history_new (List[str]): Chest history. Defaults to [].
bedwars_box_commons (int): Total common Bedwars boxes. Defaults to 0.
spray_glyph_field (str): Current spray glyph. Defaults to "".
active_island_topper (str): Current island topper. Defaults to "".
active_projectile_trail (str): Current projectile trail. Defaults to "".
bedwars_easter_boxes (int): Bedwars easters boxes. Defaults to 0.
Bedwars_opened_chests (int): Total chests opened in Bedwars. Defaults to 0.
Bedwars_opened_rares (int): Total rare chests opened in Bedwars.
Defaults to 0.
Bedwars_opened_commons (int): Total common chests opened in Bedwars.
Defaults to 0.
active_npc_skin (str): Current active NPC skin. Defaults to "".
favourites_2 (str): Favourites. Defaults to "".
Bedwars_opened_epics (int): Total epic chests opened in Bedwars.
Defaults to 0.
active_death_cry (str): Equipped active death cry. Defaults to "".
active_kill_effect (str): Equipped kill effect. Defaults to "".
active_sprays (str): Equipped spray. Defaults to "".
active_glyph (str): Active glyph. Defaults to "".
selected_ultimate (str): Selected Ultimate. Defaults to "".
"""
kills: int = Field(0, alias="kills_bedwars")
wins: int = Field(0, alias="wins_bedwars")
coins: int = Field(0, alias="coins")
games_played: int = Field(0, alias="games_played_bedwars")
final_deaths: int = Field(0, alias="final_deaths_bedwars")
deaths: int = Field(0, alias="deaths_bedwars")
final_kills: int = Field(0, alias="final_kills_bedwars")
losses: int = Field(0, alias="losses_bedwars")
beds_lost: int = Field(0, alias="beds_lost_bedwars")
beds_broken: int = Field(0, alias="beds_broken_bedwars")
winstreak: int = Field(0, alias="winstreak")
experience: int = Field(0, alias="Experience")
singles: Optional[BedwarsGame] = None
doubles: Optional[BedwarsGame] = None
triples: Optional[BedwarsGame] = None
quads: Optional[BedwarsGame] = None
four_v_four: Optional[BedwarsGame] = None
quads_ultimate: Optional[BedwarsGame] = None
doubles_ultimate: Optional[BedwarsGame] = None
castle: Optional[BedwarsGame] = None
# Deaths
entity_attack_deaths: int = Field(0, alias="entity_attack_deaths_bedwars")
entity_attack_final_deaths: int = Field(0, alias="entity_attack_final_deaths_bedwars")
fall_deaths: int = Field(0, alias="fall_deaths_bedwars")
fall_final_deaths: int = Field(0, alias="fall_final_deaths_bedwars")
projectile_deaths: int = Field(0, alias="projectile_deaths_bedwars")
suffocation_deaths: int = Field(0, alias="suffocation_deaths_bedwars")
magic_deaths: int = Field(0, alias="magic_deaths_bedwars")
entity_explosion_deaths: int = Field(0, alias="entity_explosion_deaths_bedwars")
magic_final_deaths: int = Field(0, alias="magic_final_deaths_bedwars")
void_deaths: int = Field(0, alias="void_deaths_bedwars")
void_final_death: int = Field(0, alias="void_final_deaths_bedwars")
fire_tick_final_death: int = Field(0, alias="fire_tick_final_deaths_bedwars")
# Kills
void_kills: int = Field(0, alias="void_kills_bedwars")
entity_attack_kills: int = Field(0, alias="entity_attack_kills_bedwars")
entity_attack_final_kills: int = Field(0, alias="entity_attack_final_kills_bedwars")
projectile_final_kills: int = Field(0, alias="projectile_final_kills_bedwars")
entity_explosion_kills: int = Field(0, alias="entity_explosion_kills_bedwars")
projectile_kills: int = Field(0, alias="projectile_kills_bedwars")
fall_final_kills: int = Field(0, alias="fall_final_kills_bedwars")
fall_kills: int = Field(0, alias="fall_kills_bedwars")
# Resources
resources_collected: int = Field(0, alias="resources_collected_bedwars")
iron_resources_collected: int = Field(0, alias="iron_resources_collected_bedwars")
diamond_resources_collected: int = Field(0, alias="diamond_resources_collected_bedwars")
emerald_resources_collected: int = Field(0, alias="emerald_resources_collected_bedwars")
# Purchases
items_purchased: int = Field(0, alias="_items_purchased_bedwars")
permanent_items_purchased: int = Field(0, alias="permanent_items_purchased_bedwars")
# Other
bedwars_box_rares: int = Field(0, alias="bedwars_box_rares")
bedwars_box: int = Field(0, alias="bedwars_box")
chest_history_new: List[str] = Field([], alias="chest_history_new")
bedwars_box_commons: int = Field(0, alias="bedwars_box_commons")
spray_glyph_field: str = Field("", alias="spray_glyph_field")
active_island_topper: str = Field("", alias="activeIslandTopper")
active_projectile_trail: str = Field("", alias="activeProjectileTrail")
bedwars_easter_boxes: int = Field(0, alias="beds_lost_bedwars")
Bedwars_opened_chests: int = Field(0, alias="Bedwars_openedChests")
Bedwars_opened_rares: int = Field(0, alias="Bedwars_openedRares")
Bedwars_opened_commons: int = Field(0, alias="Bedwars_openedCommons")
active_npc_skin: str = Field("", alias="activeNPCSkin")
favourites_2: str = Field("", alias="favourites_2")
Bedwars_opened_epics: int = Field(0, alias="Bedwars_openedEpics")
active_death_cry: str = Field("", alias="activeDeathCry")
active_kill_effect: str = Field("", alias="activeKillEffect")
active_sprays: str = Field("", alias="activeSprays")
active_glyph: str = Field("", alias="activeGlyph")
selected_ultimate: str = Field("", alias="selected_ultimate")
@property
def final_kills_per_final_death(self) -> float:
"""Final kills per final death.
Returns:
float: ratio between final kills and final deaths.
"""
return safe_divide(self.final_kills, self.final_deaths)
@property
def beds_broken_per_lost(self) -> float:
"""Beds broken per Beds lost.
Returns:
float: ratio between beds broken and beds lost.
"""
return safe_divide(self.beds_broken, self.beds_lost)
@property
def win_lose(self) -> float:
"""Wins per losses.
Returns:
float: ratio between game wins and game losses.
"""
return safe_divide(self.wins, self.losses)
@property
def level(self) -> float:
"""Bedwars level/star.
Returns:
float: bedwars level + progress towards next level
"""
return bedwars_level_from_exp(self.experience)
[docs] @model_validator(mode="before")
@classmethod
def traverse_sources(cls, values: Dict[str, Any]) -> Dict[str, Any]:
"""Traverse all sources."""
out = values.copy()
for name, field in values.items():
if name.startswith("eight_one_"):
if "singles" not in out:
out["singles"] = {}
out["singles"][name[10:]] = field
elif name.startswith("eight_two_"):
if "doubles" not in out:
out["doubles"] = {}
out["doubles"][name[10:]] = field
elif name.startswith("four_three_"):
if "triples" not in out:
out["triples"] = {}
out["triples"][name[11:]] = field
elif name.startswith("four_four_"):
if "quads" not in out:
out["quads"] = {}
out["quads"][name[10:]] = field
elif name.startswith("two_four_"):
if "four_v_four" not in out:
out["four_v_four"] = {}
out["four_v_four"][name[9:]] = field
elif name.startswith("quads_ultimate_"): # pragma: no cover
if "quads_ultimate" not in out:
out["quads_ultimate"] = {}
out["quads_ultimate"][name[15:]] = field
elif name.startswith("ultimate_"): # pragma: no cover
if "doubles_ultimate" not in out:
out["doubles_ultimate"] = {}
out["doubles_ultimate"][name[9:]] = field
elif name.startswith("castle_"):
if "castle" not in out:
out["castle"] = {}
out["castle"][name[7:]] = field
return out