github-actions[bot]
Automated sync from GitHub
71e71e8
Raw
History Blame Contribute Delete
16.1 kB
import datetime
from typing import Any
from peewee import *
import resources.Environment as Env
from src.model.BaseModel import BaseModel, db_obj
from src.model.enums.GameStatus import GameStatus
from src.model.enums.crew.CrewChestSpendingReason import CrewChestSpendingReason
from src.model.enums.crew.CrewLevelUpgradeType import CrewLevelUpgradeType
from src.model.enums.crew.CrewRole import CrewRole
from src.model.enums.devil_fruit.DevilFruitAbilityType import DevilFruitAbilityType
class Crew(BaseModel):
"""
Crew class
"""
name: str | CharField = CharField(max_length=Env.CREW_NAME_MAX_LENGTH.get_int())
can_accept_new_members: bool | BooleanField = BooleanField(default=True)
is_active: bool | BooleanField = BooleanField(default=True)
disband_date: datetime.datetime | DateTimeField = DateTimeField(null=True)
chest_amount: int | BigIntegerField = BigIntegerField(default=0)
total_gained_chest_amount: int | BigIntegerField = BigIntegerField(default=0)
from_reset_chest_amount: int | BigIntegerField = BigIntegerField(default=0)
level: int | IntegerField = IntegerField(default=1)
max_abilities: int | IntegerField = IntegerField(default=Env.CREW_MAX_ABILITIES.get_int())
can_promote_first_mate: bool = BooleanField(default=True)
can_promote_captain: bool = BooleanField(default=True)
max_members: int | IntegerField = IntegerField(default=Env.CREW_MAX_MEMBERS.get_int())
description: str | CharField = CharField(
max_length=Env.CREW_DESCRIPTION_MAX_LENGTH.get_int(), null=True
)
required_bounty: int | BigIntegerField = BigIntegerField(default=0)
allow_view_in_search: bool | BooleanField = BooleanField(default=True)
allow_join_from_search: bool | BooleanField = BooleanField(default=True)
is_full: bool | BooleanField = BooleanField(default=False)
allow_davy_back_fight_request: bool | BooleanField = BooleanField(default=True)
auto_accept_davy_back_fight: bool | BooleanField = BooleanField(default=False)
auto_accept_join: bool | BooleanField = BooleanField(default=True)
# Backref
crew_abilities = None
crew_chest_spending_records = None
crew_join_requests = None
crew_member_chest_contributions = None
davy_back_fight_participants = None
davy_back_fights_challengers = None
davy_back_fights_opponents = None
crew_members = None
class Meta:
db_table = "crew"
def get_average_bounty(self) -> int:
"""
Returns the average bounty of the crew
:return: The average bounty
"""
from src.model.User import User
return self.crew_members.select(fn.Avg(User.bounty)).scalar()
def get_average_location_level(self) -> int:
"""
Returns the average location level of the crew
:return: The average location level
"""
from src.model.User import User
return self.crew_members.select(fn.Avg(User.location_level)).scalar()
def get_members(self, limit: int = None, page: int = 1) -> list:
"""
Returns the crew members ordered by join date
:param limit: The number of members to return. If None, returns all members
:param page: The page to return. If None, returns the first page
:return: The crew member
"""
from src.model.User import User
if limit is not None:
return (
self.crew_members.select()
.order_by(User.crew_join_date.asc())
.paginate(page, limit)
)
return self.crew_members.select().order_by(User.crew_join_date.asc())
def get_members_order_by_davy_back_fight_priority(self) -> list:
"""
Returns the crew members ordered by Davy Back Fight priority
:return: The crew members
"""
return sorted(
self.get_members(),
key=lambda x: (
x.crew_davy_back_fight_priority is None,
x.crew_davy_back_fight_priority,
x.crew_join_date,
),
)
@staticmethod
def get_by_name_if_active(name: str) -> "Crew":
"""
Returns the crew by name, case-insensitive
:param name: The name
:return: The crew
"""
return Crew.get_or_none((Crew.name**name) & (Crew.is_active == True))
@staticmethod
def logical_get(crew_id: int) -> "Crew":
"""
Returns the Crew if it exists and is active
:param crew_id: The crew id
:return: The crew
"""
return Crew.get_or_none((Crew.id == crew_id) & (Crew.is_active == True))
def get_captain(self) -> Any:
"""
Returns the crew captain
:return: The crew captain
"""
from src.model.User import User
return self.crew_members.select().where(User.crew_role == CrewRole.CAPTAIN).get()
def get_first_mate(self) -> Any:
"""
Returns the crew first mate
:return: The crew first mate
"""
from src.model.User import User
return (
self.crew_members.select().where(User.crew_role == CrewRole.FIRST_MATE).get_or_none()
)
def has_first_mate(self) -> bool:
"""
Returns True if the crew has a first mate
:return: True if the crew has a first mate
"""
return self.get_first_mate() is not None
def get_member_count(self) -> int:
"""
Returns the number of crew members
:return: The number of crew members
"""
return self.crew_members.select().count()
def get_chest_contributions(self) -> list:
"""
Returns the crew members ordered by contribution
:return: The crew members
"""
from src.model.CrewMemberChestContribution import CrewMemberChestContribution
return self.crew_member_chest_contributions.select().order_by(
CrewMemberChestContribution.amount.desc()
)
def get_powerup_price(self, reason: CrewChestSpendingReason) -> int:
"""
Returns the next powerup price
:param reason: The reason
:return: The next powerup price
"""
# Not level up, price is current level * BASE_PRICE
if reason is not CrewChestSpendingReason.LEVEL_UP:
return Env.CREW_POWERUP_BASE_PRICE.get_int() * self.level
# Level up, price is double the previous
return Env.CREW_POWERUP_BASE_PRICE.get_int() * (2 ** (self.level - 1))
def get_powerup_price_formatted(self, reason: CrewChestSpendingReason) -> str:
"""
Returns the next powerup price formatted
:param reason: The reason
:return: The next powerup price formatted
"""
from src.utils.string_utils import get_belly_formatted
return get_belly_formatted(self.get_powerup_price(reason))
def get_crew_chest_formatted(self) -> str:
"""
Returns the crew chest formatted
:return: The crew chest formatted
"""
from src.utils.string_utils import get_belly_formatted
return get_belly_formatted(self.chest_amount)
def get_active_abilities(self) -> list:
"""
Returns the crew active abilities
:return: The crew active abilities
"""
from src.model.CrewAbility import CrewAbility
return self.crew_abilities.select().where(
CrewAbility.expiration_date > datetime.datetime.now()
)
def get_active_abilities_types(self) -> list[DevilFruitAbilityType]:
"""
Returns the crew active abilities types
:return: The crew active abilities types
"""
return list(
set([crew_ability.get_ability_type() for crew_ability in self.get_active_abilities()])
)
def get_active_ability(self, ability_type: DevilFruitAbilityType) -> list:
"""
Returns the crew active ability of the given type
:param ability_type: The ability type
:return: The crew active ability
"""
from src.model.CrewAbility import CrewAbility
return self.crew_abilities.select().where(
(CrewAbility.expiration_date > datetime.datetime.now())
& (CrewAbility.ability_type == ability_type.value)
)
def get_next_level_upgrade_type(self) -> CrewLevelUpgradeType:
"""
Returns the next level upgrade type
:return: The next level upgrade type
"""
return CrewLevelUpgradeType.get_by_level(self.level + 1)
def get_upgrade_type_count(self, upgrade_type: CrewLevelUpgradeType) -> int:
"""
Returns the number of upgrades of the given type
:param upgrade_type: The upgrade type
:return: The number of upgrades
"""
if upgrade_type is CrewLevelUpgradeType.MEMBER:
return self.max_members
return self.max_abilities
def level_up(self):
"""
Level up the crew
:return: None
"""
# Increase max members or abilities
upgrade_type = self.get_next_level_upgrade_type()
if upgrade_type is CrewLevelUpgradeType.MEMBER:
self.max_members += 1
self.can_accept_new_members = True
self.set_is_full()
elif upgrade_type is CrewLevelUpgradeType.ABILITY:
self.max_abilities += 1
self.level += 1
self.save()
@staticmethod
def reset_level() -> None:
"""
Reset the crew level
:return: None
"""
# Reset level
conditions: list[tuple[bool, int]] = [
# If level / 2 is higher than allowed after reset, set to max
(
(Crew.level / 2) > Env.CREW_MAX_LEVEL_AFTER_RESET.get_int(),
Env.CREW_MAX_LEVEL_AFTER_RESET.get_int(),
),
# If level / 2 is lower than 1 after reset, set to 1
((Crew.level / 2) < 1, 1),
]
# Else, set to level / 2
case_stmt = Case(None, conditions, Crew.level / 2)
Crew.update(level=case_stmt).execute()
# Reset max allowed members and abilities
Crew.update(
max_members=(Env.CREW_MAX_MEMBERS.get_int() + fn.ROUND((Crew.level - 1) / 2)),
max_abilities=(Env.CREW_MAX_ABILITIES.get_int() + fn.FLOOR((Crew.level - 1) / 2)),
).execute()
# Reset is full attribute
Crew.set_is_full_for_all_crews()
def get_name_escaped(self) -> str:
"""
Returns the crew name escaped
:return: The crew name escaped
"""
from src.service.message_service import escape_valid_markdown_chars
return escape_valid_markdown_chars(self.name)
def get_description_escaped(self) -> str:
"""
Returns the crew description escaped
:return: The crew description escaped
"""
from src.service.message_service import escape_valid_markdown_chars
return escape_valid_markdown_chars(self.description)
def get_required_bounty_formatted(self) -> str:
"""
Returns the crew required bounty formatted
:return: The crew required bounty formatted
"""
from src.utils.string_utils import get_belly_formatted
return get_belly_formatted(self.required_bounty)
def set_is_full(self) -> None:
"""
Set the crew as full
:return: None
"""
if self.get_member_count() >= self.max_members:
self.is_full = True
else:
self.is_full = False
self.save()
# Also set Davy Back Fight priority
self.set_davy_back_fight_priority()
@staticmethod
def set_is_full_for_all_crews() -> None:
"""
Set the crew as full for all crews
:return: None
"""
raw_query = (
"update crew"
" set is_full = IF(((select count(*) from user where crew_id = crew.id) <"
" crew.max_members), 0, 1)"
" where true;"
)
db_obj.get_db().execute_sql(raw_query)
def get_active_davy_back_fight(self) -> Any:
"""
Returns the current Davy Back Fight
:return: The current Davy Back Fight
"""
from src.model.DavyBackFight import DavyBackFight
return (
DavyBackFight.select()
.where(
((DavyBackFight.challenger_crew == self) | (DavyBackFight.opponent_crew == self))
& (
(DavyBackFight.status == GameStatus.COUNTDOWN_TO_START)
| (DavyBackFight.status == GameStatus.IN_PROGRESS)
)
)
.get_or_none()
)
def get_in_progress_davy_back_fight(self) -> Any:
"""
Returns the in progress Davy Back Fight
:return: The in progress Davy Back Fight
"""
from src.model.DavyBackFight import DavyBackFight
return (
DavyBackFight.select()
.where(
((DavyBackFight.challenger_crew == self) | (DavyBackFight.opponent_crew == self))
& (DavyBackFight.status == GameStatus.IN_PROGRESS)
)
.get_or_none()
)
def get_pending_davy_back_fight(self) -> Any:
"""
Returns the pending Davy Back Fight as challenger
:return: The pending Davy Back Fight
"""
from src.model.DavyBackFight import DavyBackFight
return (
self.davy_back_fights_challengers.select()
.where(DavyBackFight.status == GameStatus.AWAITING_OPPONENT_CONFIRMATION)
.get_or_none()
)
def get_penalty_davy_back_fight(self) -> Any:
"""
Returns the penalty Davy Back Fight
:return: The penalty Davy Back Fight
"""
from src.model.DavyBackFight import DavyBackFight
return (
DavyBackFight.select()
.where(
(
(
(DavyBackFight.challenger_crew == self)
& (DavyBackFight.status == GameStatus.LOST)
)
| (
(DavyBackFight.opponent_crew == self)
& (DavyBackFight.status == GameStatus.WON)
)
)
& (DavyBackFight.penalty_end_date > datetime.datetime.now())
)
.get_or_none()
)
def get_name_with_deeplink(self, add_level: bool = False):
"""
Returns the crew name with deeplink
:param add_level: Whether to add the level to the name
:return: The crew name with deeplink
"""
from src.service.crew_service import get_crew_name_with_deeplink
return get_crew_name_with_deeplink(self, add_level=add_level)
def has_active_davy_back_fight(self) -> bool:
"""
Returns True if the crew has an active Davy Back Fight
:return: True if the crew has an active Davy Back Fight
"""
return self.get_active_davy_back_fight() is not None
def has_penalty_davy_back_fight(self) -> bool:
"""
Returns True if the crew has a penalty Davy Back Fight
:return: True if the crew has a penalty Davy Back Fight
"""
return self.get_penalty_davy_back_fight() is not None
def set_davy_back_fight_priority(self, should_reset: bool = False):
"""
Set the crew members Davy Back Fight priority
:param should_reset: Whether to reset the priority
:return: None
"""
from src.model.User import User
# Sorting only by join date
members: list[User] = (
self.get_members()
if should_reset
else self.get_members_order_by_davy_back_fight_priority()
)
for i, member in enumerate(members):
member.crew_davy_back_fight_priority = i + 1
member.save()
Crew.create_table()