From c47976d927e7a2c3df78edbc17efb6c0bec77f26 Mon Sep 17 00:00:00 2001 From: lxgr-linux Date: Sun, 10 Nov 2024 18:41:40 +0100 Subject: [PATCH] feat(#267): refactored FightItems --- pokete_classes/fight/attack_process.py | 4 +- pokete_classes/fight/fight.py | 9 +- pokete_classes/fight/fight_items.py | 119 ------------------ pokete_classes/fight/items/__init__.py | 13 ++ pokete_classes/fight/items/ap_potion.py | 13 ++ pokete_classes/fight/items/balls.py | 81 ++++++++++++ pokete_classes/fight/items/healing_potions.py | 27 ++++ pokete_classes/fight/items/item.py | 18 +++ pokete_classes/inv/items.py | 2 +- 9 files changed, 160 insertions(+), 126 deletions(-) delete mode 100644 pokete_classes/fight/fight_items.py create mode 100644 pokete_classes/fight/items/__init__.py create mode 100644 pokete_classes/fight/items/ap_potion.py create mode 100644 pokete_classes/fight/items/balls.py create mode 100644 pokete_classes/fight/items/healing_potions.py create mode 100644 pokete_classes/fight/items/item.py diff --git a/pokete_classes/fight/attack_process.py b/pokete_classes/fight/attack_process.py index 549bbea5..d3aba62b 100644 --- a/pokete_classes/fight/attack_process.py +++ b/pokete_classes/fight/attack_process.py @@ -16,7 +16,7 @@ def __init__(self, fightmap: FightMap): self.fightmap: FightMap = fightmap @staticmethod - def get_random_factor(attack, attacker) -> int: + def get_random_factor(attack, attacker) -> float: return random.choices( [0, 0.75, 1, 1.26], weights=[attack.miss_chance @@ -25,7 +25,7 @@ def get_random_factor(attack, attacker) -> int: )[0] @staticmethod - def get_base_effectivity(defender: Poke, attack: Attack) -> int: + def get_base_effectivity(defender: Poke, attack: Attack) -> float: return ( 1.3 if defender.type.name in attack.type.effective else 0.5 if defender.type.name in attack.type.ineffective else 1 diff --git a/pokete_classes/fight/fight.py b/pokete_classes/fight/fight.py index 635e5675..0d73acd4 100644 --- a/pokete_classes/fight/fight.py +++ b/pokete_classes/fight/fight.py @@ -5,9 +5,9 @@ from release import SPEED_OF_TIME from .attack_process import AttackProcess from .fight_decision import Result -from .fight_items import FightItems from .fightmap import FightMap from .providers import Provider +from .items import fight_items from ..achievements import achievements from ..attack import Attack from ..audio import audio @@ -22,7 +22,6 @@ class Fight: def __init__(self): self.fightmap: FightMap = FightMap(tss.height - 1, tss.width) self.providers: list[Provider] = [] - self.fight_items = FightItems() self.attack_process = AttackProcess(self.fightmap) def initial_player_index(self): @@ -99,8 +98,10 @@ def __call__(self, ctx: Context, providers: list[Provider]): return player case Result.ITEM: item: InvItem = attack_result.item_value - fight_func = getattr(self.fight_items, item.func) - match fight_func(self.fightmap, player, enem): + fight_item = fight_items.get(item.func, None) + if fight_item is None: + raise Exception(f"fight_item doesnt exist {item.func}") + match fight_item.use(self.fightmap, player, enem): case 1: continue # This is the sole reason for the while loop on top case 2: diff --git a/pokete_classes/fight/fight_items.py b/pokete_classes/fight/fight_items.py deleted file mode 100644 index 836bd3cf..00000000 --- a/pokete_classes/fight/fight_items.py +++ /dev/null @@ -1,119 +0,0 @@ -import logging -import random -import time - -from pokete_classes import ob_maps as obmp -from pokete_classes.achievements import achievements -from pokete_classes.audio import audio -from pokete_classes.fight import FightMap -from pokete_classes.fight.providers import NatureProvider, Provider -from release import SPEED_OF_TIME -from ..asset_service.service import asset_service - - -class FightItems: - """Contains all fns callable by an item in fight - The methods that can actually be called in fight follow - the following pattern: - ARGS: - obj: The players Provider - enem: The enemys Provider - RETURNS: - 1: To continue the attack round - 2: To win the game - None: To let the enemy attack""" - - @staticmethod - def __throw(fightmap: FightMap, obj, enem: Provider, chance, name): - """Throws a ball - ARGS: - obj: The players Poke object - enem: The enemys Poke object - chance: The balls catch chance - name: The balls name - RETURNS: - 1: The continue the attack round - 2: The win the game - None: To let the enemy attack""" - - if not isinstance(enem, NatureProvider): - fightmap.outp.outp("You can't do that in a duel!") - return 1 - fightmap.outp.rechar(f"You threw a {name.capitalize()}!") - fightmap.fast_change( - [enem.curr.ico, fightmap.deadico1, fightmap.deadico2, - fightmap.pball], enem.curr.ico) - time.sleep(SPEED_OF_TIME * random.choice([1, 2, 3, 4])) - obj.remove_item(name) - catch_chance = 20 if obj.map == obmp.ob_maps["playmap_1"] else 0 - for effect in enem.curr.effects: - catch_chance += effect.catch_chance - if random.choices([True, False], - weights=[(enem.curr.full_hp / enem.curr.hp) - * chance + catch_chance, - enem.curr.full_hp], k=1)[0]: - audio.switch("xDeviruchi - Decisive Battle (End).mp3") - obj.add_poke(enem.curr, caught_with=name) - fightmap.outp.outp(f"You caught {enem.curr.name}!") - time.sleep(SPEED_OF_TIME * 2) - fightmap.pball.remove() - fightmap.clean_up(obj, enem) - obj.balls_label_rechar() - logging.info("[Fighitem][%s] Caught %s", name, enem.curr.name) - achievements.achieve("first_poke") - if all(poke in obj.caught_pokes for poke in - asset_service.get_base_assets().pokes): - achievements.achieve("catch_em_all") - return 2 - fightmap.outp.outp("You missed!") - fightmap.show() - fightmap.pball.remove() - enem.curr.ico.add(fightmap, enem.curr.ico.x, enem.curr.ico.y) - fightmap.show() - logging.info("[Fighitem][%s] Missed", name) - return None - - @staticmethod - def __potion(obj, hp, name): - """Potion function - ARGS: - obj: The players Poke object - hp: The hp that will be given to the Poke - name: The potions name""" - - obj.remove_item(name) - obj.curr.oldhp = obj.curr.hp - obj.curr.hp = min(obj.curr.full_hp, obj.curr.hp + hp) - obj.curr.hp_bar.update(obj.curr.oldhp) - logging.info("[Fighitem][%s] Used", name) - - def heal_potion(self, fightmap: FightMap, obj, _): - """Healing potion function""" - return self.__potion(obj, 5, "healing_potion") - - def super_potion(self, fightmap: FightMap, obj, _): - """Super potion function""" - return self.__potion(obj, 15, "super_potion") - - def poketeball(self, fightmap: FightMap, obj, enem): - """Poketeball function""" - return self.__throw(fightmap, obj, enem, 1, "poketeball") - - def superball(self, fightmap: FightMap, obj, enem): - """Superball function""" - return self.__throw(fightmap, obj, enem, 6, "superball") - - def hyperball(self, fightmap: FightMap, obj, enem): - """Hyperball function""" - return self.__throw(fightmap, obj, enem, 1000, "hyperball") - - @staticmethod - def ap_potion(obj, _): - """AP potion function""" - obj.remove_item("ap_potion") - for atc in obj.curr.attack_obs: - atc.set_ap(atc.max_ap) - logging.info("[Fighitem][ap_potion] Used") - - -fightitems = FightItems() diff --git a/pokete_classes/fight/items/__init__.py b/pokete_classes/fight/items/__init__.py new file mode 100644 index 00000000..413985b6 --- /dev/null +++ b/pokete_classes/fight/items/__init__.py @@ -0,0 +1,13 @@ +from .item import FightItem +from .balls import PoketeBall, SuperBall, HyperBall +from .healing_potions import HealingPotion, SuperPotion +from .ap_potion import ApPotion + +fight_items: dict[str|None, FightItem] = { + "heal_potion": HealingPotion(), + "super_potion": SuperPotion(), + "poketeball": PoketeBall(), + "superball": SuperBall(), + "hyperball": HyperBall(), + "ap_potion": ApPotion() +} diff --git a/pokete_classes/fight/items/ap_potion.py b/pokete_classes/fight/items/ap_potion.py new file mode 100644 index 00000000..08237fe6 --- /dev/null +++ b/pokete_classes/fight/items/ap_potion.py @@ -0,0 +1,13 @@ +import logging +from pokete_classes.fight.fightmap.fightmap import FightMap +from pokete_classes.fight.items.item import FightItem, RoundContinuation +from pokete_classes.fight.providers import Provider + + +class ApPotion(FightItem): + def use(self, fightmap: FightMap, obj, enem: Provider) -> RoundContinuation: + obj.remove_item("ap_potion") + for atc in obj.curr.attack_obs: + atc.set_ap(atc.max_ap) + logging.info("[Fighitem][ap_potion] Used") + return RoundContinuation.ENEMY_ATTACK diff --git a/pokete_classes/fight/items/balls.py b/pokete_classes/fight/items/balls.py new file mode 100644 index 00000000..e525492f --- /dev/null +++ b/pokete_classes/fight/items/balls.py @@ -0,0 +1,81 @@ +from abc import ABC +import logging +import random +import time + +from pokete_classes.achievements import achievements +from pokete_classes.fight.fightmap.fightmap import FightMap +from pokete_classes.fight.providers import Provider +from pokete_classes.audio import audio +from ... import ob_maps as obmp +from pokete_classes.fight.providers import NatureProvider, Provider +from .item import FightItem, RoundContinuation +from ...asset_service.service import asset_service +from release import SPEED_OF_TIME + + +class GenericPokeBall(FightItem, ABC): + def __init__(self, chance:int, name: str): + self.chance = chance + self.name = name + + def use(self, fightmap: FightMap, obj, enem:Provider) -> RoundContinuation: + """Throws a ball + ARGS: + obj: The players Poke object + enem: The enemys Poke object + chance: The balls catch chance + name: The balls name + RETURNS: + 1: The continue the attack round + 2: The win the game + None: To let the enemy attack""" + if not isinstance(enem, NatureProvider): + fightmap.outp.outp("You can't do that in a duel!") + return RoundContinuation.CONTINUE_ATTACK + fightmap.outp.rechar(f"You threw a {self.name.capitalize()}!") + fightmap.fast_change( + [enem.curr.ico, fightmap.deadico1, fightmap.deadico2, + fightmap.pball], enem.curr.ico) + time.sleep(SPEED_OF_TIME * random.choice([1, 2, 3, 4])) + obj.remove_item(self.name) + catch_chance = 20 if obj.map == obmp.ob_maps.get("playmap_1", None) else 0 + for effect in enem.curr.effects: + catch_chance += effect.catch_chance + if random.choices([True, False], + weights=[(enem.curr.full_hp / enem.curr.hp) + * self.chance + catch_chance, + enem.curr.full_hp], k=1)[0]: + audio.switch("xDeviruchi - Decisive Battle (End).mp3") + obj.add_poke(enem.curr, caught_with=self.name) + fightmap.outp.outp(f"You caught {enem.curr.name}!") + time.sleep(SPEED_OF_TIME * 2) + fightmap.pball.remove() + fightmap.clean_up(obj, enem) + obj.balls_label_rechar() + logging.info("[Fighitem][%s] Caught %s", self.name, enem.curr.name) + achievements.achieve("first_poke") + if all(poke in obj.caught_pokes for poke in + asset_service.get_base_assets().pokes): + achievements.achieve("catch_em_all") + return RoundContinuation.EXIT + fightmap.outp.outp("You missed!") + fightmap.show() + fightmap.pball.remove() + enem.curr.ico.add(fightmap, enem.curr.ico.x, enem.curr.ico.y) + fightmap.show() + logging.info("[Fighitem][%s] Missed", self.name) + return RoundContinuation.ENEMY_ATTACK + +class PoketeBall(GenericPokeBall): + def __init__(self): + super().__init__(1, "poketeball") + + +class SuperBall(GenericPokeBall): + def __init__(self): + super().__init__(6, "superball") + +class HyperBall(GenericPokeBall): + def __init__(self): + super().__init__(1000, "hyperball") diff --git a/pokete_classes/fight/items/healing_potions.py b/pokete_classes/fight/items/healing_potions.py new file mode 100644 index 00000000..6f7f15d0 --- /dev/null +++ b/pokete_classes/fight/items/healing_potions.py @@ -0,0 +1,27 @@ +from abc import ABC +import logging +from pokete_classes.fight.fightmap.fightmap import FightMap +from pokete_classes.fight.items.item import FightItem, RoundContinuation +from pokete_classes.fight.providers import Provider + + +class GenericeHealingPotion(FightItem, ABC): + def __init__(self, hp: int, name: str): + self.hp = hp + self.name = name + + def use(self, fightmap: FightMap, obj, enem: Provider) -> RoundContinuation: + obj.remove_item(self.name) + obj.curr.oldhp = obj.curr.hp + obj.curr.hp = min(obj.curr.full_hp, obj.curr.hp + self.hp) + obj.curr.hp_bar.update(obj.curr.oldhp) + logging.info("[Fighitem][%s] Used", self.name) + return RoundContinuation.ENEMY_ATTACK + +class HealingPotion(GenericeHealingPotion): + def __init__(self): + super().__init__(5, "healing_potion") + +class SuperPotion(GenericeHealingPotion): + def __init__(self): + super().__init__(15, "super_potion") diff --git a/pokete_classes/fight/items/item.py b/pokete_classes/fight/items/item.py new file mode 100644 index 00000000..f2afb22d --- /dev/null +++ b/pokete_classes/fight/items/item.py @@ -0,0 +1,18 @@ +from abc import ABC, abstractmethod +from enum import Enum, auto + +from pokete_classes.fight.fightmap.fightmap import FightMap +from pokete_classes.fight.providers import Provider + + +class RoundContinuation(Enum): + CONTINUE_ATTACK = auto() + EXIT = auto() + ENEMY_ATTACK = auto() + + +class FightItem(ABC): + + @abstractmethod + def use(self, fightmap: FightMap, obj, enem:Provider) -> RoundContinuation: + pass diff --git a/pokete_classes/inv/items.py b/pokete_classes/inv/items.py index d4f692bb..d43d7c34 100644 --- a/pokete_classes/inv/items.py +++ b/pokete_classes/inv/items.py @@ -26,7 +26,7 @@ class InvItem: _fn: The associated method name in FightItems""" def __init__( - self, name:str, pretty_name: str, desc: str, price: int | None, _fn=None + self, name:str, pretty_name: str, desc: str, price: int | None, _fn: str | None=None ): self.name: str = name self.pretty_name = pretty_name