diff --git a/pokete.py b/pokete.py index fd647203..16fd0a24 100755 --- a/pokete.py +++ b/pokete.py @@ -20,42 +20,44 @@ import scrap_engine as se import pokete_data as p_data import release -from pokete_classes import animations +from pokete_classes import animations, loops +from pokete_classes.context import Context +from pokete_classes.inv import inv, buy +from pokete_classes.menu import menu +from pokete_classes.periodic_events import MovingGrassEvent, MovingWaterEvent, \ + TreatNPCEvent from pokete_classes.pokestats import PokeStats -from pokete_classes.poke import Poke, upgrade_by_one_lvl +from pokete_classes.poke import Poke from pokete_classes.color import Color -from pokete_classes.ui_elements import ChooseBox, InfoBox, BetterChooseBox +from pokete_classes.save import read_save, save +from pokete_classes.ui.elements import InfoBox from pokete_classes.classes import PlayMap -from pokete_classes.settings import settings, VisSetting, Slider -from pokete_classes.inv_items import invitems, LearnDisc -from pokete_classes.types import types +from pokete_classes.settings import settings from pokete_classes.providers import ProtoFigure -from pokete_classes.buy import Buy, InvBox from pokete_classes.audio import audio from pokete_classes.tss import tss -from pokete_classes.side_loops import LoadingScreen, About, Help -from pokete_classes.input import text_input, ask_bool, ask_text, ask_ok -from pokete_classes.mods import ModError, ModInfo, DummyMods -from pokete_classes.pokete_care import PoketeCare, DummyFigure -from pokete_classes import deck, detail, game, timer, ob_maps as obmp, \ +from pokete_classes.side_loops import loading_screen, Help +from pokete_classes.input import text_input, _ev +from pokete_classes.mods import try_load_mods, loaded_mods +from pokete_classes.pokete_care import DummyFigure, pokete_care +from pokete_classes import deck, detail, timer, ob_maps as obmp, \ movemap as mvp, fightmap as fm # import pokete_classes.generic_map_handler as gmh from pokete_classes.landscape import Meadow, Water, Sand, HighGrass, Poketeball from pokete_classes.doors import ( CenterDoor, Door, DoorToCenter, DoorToShop, ChanceDoor ) -from pokete_classes.learnattack import LearnAttack from pokete_classes.roadmap import RoadMap from pokete_classes.npcs import NPC, Trainer -from pokete_classes.notify import notifier -from pokete_classes.achievements import achievements, AchievementOverview -from pokete_classes.event import _ev -from pokete_classes.hotkeys import ( - get_action, Action, ACTION_DIRECTIONS, hotkeys_save, hotkeys_from_save +from pokete_classes.ui import notifier, ask_bool, ask_text, ask_ok +from pokete_classes.achievements import achievements +from pokete_classes.input import ( + get_action, Action, ACTION_DIRECTIONS, hotkeys_from_save ) from pokete_classes.dex import Dex -from pokete_classes.loops import std_loop -from pokete_classes.periodic_event_manager import PeriodicEventManager +from pokete_classes.game import ( + PeriodicEventManager, PeriodicEvent, MapChangeExeption +) from util import liner, sort_vers from release import SPEED_OF_TIME @@ -324,7 +326,7 @@ def action(self, ob): break elif action.triggers(Action.CANCEL, Action.ACT_3): break - std_loop(box=mvp.movemap) + loops.std(box=mvp.movemap) mvp.movemap.full_show(init=True) @@ -340,7 +342,7 @@ def action(self, ob): mvp.movemap.text(mvp.movemap.bmap.inner.x - mvp.movemap.x + 9, 3, ["Welcome to the Pokete-Shop", "Wanna buy something?"]) - buy() + buy(Context(None, mvp.movemap, mvp.movemap, figure)) mvp.movemap.full_show(init=True) mvp.movemap.text(mvp.movemap.bmap.inner.x - mvp.movemap.x + 9, 3, ["Have a great day!"]) @@ -476,7 +478,7 @@ def set_money(self, money): money, self.__money) self.__money = money for cls in [inv, buy]: - cls.money_label.rechar("$" + str(self.__money)) + cls.money_label.rechar("$" + str(self.__money)) # TODO: Remove cls.box.set_ob(cls.money_label, cls.box.width - 2 - len(cls.money_label.text), 0) @@ -540,291 +542,6 @@ def pos(cls): print(figure.x, figure.y, figure.map.name) -class Inv: - """Inventory to see and manage items in - ARGS: - _map: se.Map this will be shown on""" - - def __init__(self, _map): - self.map = _map - self.box = ChooseBox(_map.height - 3, 35, "Inventory", - f"{Action.REMOVE.mapping}:remove") - self.box2 = InvBox(7, 21, overview=self) - self.money_label = se.Text(f"${figure.get_money()}") - self.desc_label = se.Text(" ") - # adding - self.box.add_ob(self.money_label, - self.box.width - 2 - len(self.money_label.text), 0) - self.box2.add_ob(self.desc_label, 1, 1) - - def resize_view(self): - """Manages recursive view resizing""" - self.box.remove() - self.map.resize_view() - self.box.resize(self.map.height - 3, 35) - self.box.add(self.map, self.map.width - self.box.width, 0) - mvp.movemap.full_show() - - def __call__(self): - """Opens the inventory""" - _ev.clear() - items = self.add() - self.box.resize(self.map.height - 3, 35) - with self.box.add(self.map, self.map.width - 35, 0): - while True: - action = get_action() - if action.triggers(Action.UP, Action.DOWN): - self.box.input(action) - elif action.triggers(Action.CANCEL): - break - elif action.triggers(Action.ACCEPT): - obj = items[self.box.index.index] - self.box2.name_label.rechar(obj.pretty_name) - self.desc_label.rechar(liner(obj.desc, 19)) - self.box2.add(self.map, self.box.x - 19, 3) - while True: - action = get_action() - if ( - action.triggers(Action.CANCEL) - or action.triggers(Action.ACCEPT) - ): - self.box2.remove() - if obj.name == "treat": - if ask_bool( - self.map, - "Do you want to upgrade one of " - "your Poketes by a level?", - self - ): - ex_cond = True - while ex_cond: - index = deck.deck( - mvp.movemap, 6, label="Your deck", - in_fight=True - ) - if index is None: - ex_cond = False - self.map.show(init=True) - break - poke = figure.pokes[index] - break - if not ex_cond: - break - upgrade_by_one_lvl(poke, figure, self.map) - items = self.rem_item(obj.name, items) - ask_ok( - self.map, - f"{poke.name} reached level " - f"{poke.lvl()}!", - self - ) - elif isinstance(obj, LearnDisc): - if ask_bool( - self.map, - f"Do you want to teach " - f"'{obj.attack_dict['name']}'?", - self - ): - ex_cond = True - while ex_cond: - index = deck.deck( - mvp.movemap, 6, label="Your deck", - in_fight=True - ) - if index is None: - ex_cond = False - self.map.show(init=True) - break - poke = figure.pokes[index] - if getattr(types, - obj.attack_dict['types'][0]) \ - in poke.types: - break - ex_cond = ask_bool( - self.map, - "You can't teach " - f"'{obj.attack_dict['name']}' to " - f"'{poke.name}'! \n" - "Do you want to continue?", - self - ) - if not ex_cond: - break - if LearnAttack(poke, self.map, self) \ - (obj.attack_name): - items = self.rem_item(obj.name, items) - if len(items) == 0: - break - break - std_loop(box=self.box2) - self.map.show() - elif action.triggers(Action.REMOVE): - if ask_bool( - self.map, - "Do you really want to throw " - f"{items[self.box.index.index].pretty_name} away?", - self - ): - items = self.rem_item(items[self.box.index.index].name, - items) - if len(items) == 0: - break - std_loop(box=self) - self.map.show() - self.box.remove_c_obs() - - def rem_item(self, name, items): - """Removes an item from the inv - ARGS: - name: Items name - items: List of Items - RETURNS: - List of Items""" - figure.remove_item(name) - for obj in self.box.c_obs: - obj.remove() - self.box.remove_c_obs() - items = self.add() - if not items: - return items - if self.box.index.index >= len(items): - self.box.set_index(len(items) - 1) - return items - - def add(self): - """Adds all items to the box - RETURNS: - List of Items""" - items = [getattr(invitems, i) for i in figure.inv if figure.inv[i] > 0] - self.box.add_c_obs( - [ - se.Text( - f"{i.pretty_name}s : {figure.inv[i.name]}", - state="float" - ) - for i in items - ] - ) - return items - - -class Menu: - """Menu to manage settings and other stuff in - ARGS: - _map: se.Map this will be shown on""" - - def __init__(self, _map): - self.map = _map - self.box = ChooseBox(_map.height - 3, 35, "Menu", overview=_map) - self.playername_label = se.Text("Playername: ", state="float") - self.represent_char_label = se.Text("Char: ", state="float") - self.mods_label = se.Text("Mods", state="float") - self.ach_label = se.Text("Achievements", state="float") - self.about_label = se.Text("About", state="float") - self.save_label = se.Text("Save", state="float") - self.exit_label = se.Text("Exit", state="float") - self.realname_label = se.Text(session_info["user"], state="float") - self.char_label = se.Text(figure.char, state="float") - self.box.add_c_obs([self.playername_label, - self.represent_char_label, - VisSetting("Autosave", "autosave", - {True: "On", False: "Off"}), - VisSetting("Animations", "animations", - {True: "On", False: "Off"}), - VisSetting("Save trainers", "save_trainers", - {True: "On", False: "Off"}), - VisSetting("Audio", "audio", - {True: "On", False: "Off"}), - Slider("Volume", "volume"), - VisSetting("Load mods", "load_mods", - {True: "On", False: "Off"}), - self.mods_label, self.ach_label, - self.about_label, self.save_label, - self.exit_label]) - # adding - self.box.add_ob(self.realname_label, - self.playername_label.rx + self.playername_label.width, - self.playername_label.ry) - self.box.add_ob(self.char_label, - self.represent_char_label.rx - + self.represent_char_label.width, - self.represent_char_label.ry) - - def resize_view(self): - """Manages recursive view resizing""" - self.box.remove() - self.box.overview.resize_view() - self.box.resize(self.map.height - 3, 35) - self.box.add(self.map, self.map.width - self.box.width, 0) - - def __call__(self, pevm): - """Opens the menu""" - self.box.resize(self.map.height - 3, 35) - self.realname_label.rechar(figure.name) - self.char_label.rechar(figure.char) - audio_before = settings("audio").val - volume_before = settings("volume").val - with self.box.add(self.map, self.map.width - self.box.width, 0): - _ev.clear() - while True: - action = get_action() - i = self.box.c_obs[self.box.index.index] - if (strength := action.get_x_strength()) != 0: - if isinstance(i, Slider): - i.change(strength) - elif action.triggers(Action.ACCEPT): - # Fuck python for not having case statements - lxgr - # but it does lmao - Magnus - if i == self.playername_label: - figure.name = text_input(self.realname_label, self.map, - figure.name, 18, 17) - self.map.name_label_rechar(figure.name) - elif i == self.represent_char_label: - inp = text_input(self.char_label, self.map, - figure.char, 18, 1) - # excludes bad unicode: - if ( - len(inp.encode("utf-8")) != 1 - and inp not in ["ä", "ö", "ü", "ß"] - ): - inp = "a" - self.char_label.rechar(inp) - notifier.notify("Error", "Bad character", - "The chosen character has to be a \ -valid single-space character!") - figure.rechar(inp) - elif i == self.mods_label: - ModInfo(mvp.movemap, mods.mod_info)() - elif i == self.save_label: - # When will python3.10 come out? - with InfoBox("Saving....", info="", _map=self.map): - # Shows a box displaying "Saving...." while saving - save() - time.sleep(SPEED_OF_TIME * 1.5) - elif i == self.exit_label: - save() - sys.exit() - elif i == self.about_label: - about() - elif i == self.ach_label: - AchievementOverview()(mvp.movemap) - elif isinstance(i, VisSetting): - i.change() - if ( - audio_before != settings("audio").val - or volume_before != settings("volume").val - ): - audio.switch(figure.map.song) - audio_before = settings("audio").val - volume_before = settings("volume").val - elif action.triggers(Action.UP, Action.DOWN): - self.box.input(action) - elif action.triggers(Action.CANCEL, Action.MENU): - break - std_loop(pevm=pevm, box=self) - self.map.full_show() - - # General use functions ####################### @@ -833,89 +550,7 @@ def autosave(): while True: time.sleep(SPEED_OF_TIME * 300) if settings("autosave").val: - save() - - -def save(): - """Saves all relevant data to savefile""" - _si = { - "user": figure.name, - "represent_char": figure.char, - "ver": VERSION, - "map": figure.map.name, - "oldmap": figure.oldmap.name, - "last_center_map": figure.last_center_map.name, - "x": figure.x, - "y": figure.y, - "achievements": achievements.achieved, - "pokes": {i: poke.dict() for i, poke in enumerate(figure.pokes)}, - "inv": figure.inv, - "money": figure.get_money(), - "settings": settings.to_dict(), - "caught_poketes": list(dict.fromkeys(figure.caught_pokes - + [i.identifier - for i in figure.pokes])), - "visited_maps": figure.visited_maps, - "startup_time": __t, - "hotkeys": hotkeys_save(), - # filters doublicates from figure.used_npcs - "used_npcs": list(dict.fromkeys(figure.used_npcs)), - "pokete_care": pokete_care.dict(), - "time": timer.time.time, - } - with open(SAVEPATH / "pokete.json", "w+") as file: - # writes the data to the save file in a nice format - json.dump(_si, file, indent=4) - logging.info("[General] Saved") - - -def read_save(): - """Reads from savefile - RETURNS: - session_info dict""" - Path(SAVEPATH).mkdir(parents=True, exist_ok=True) - # Default test session_info - _si = { - "user": "DEFAULT", - "represent_char": "a", - "ver": VERSION, - "map": "intromap", - "oldmap": "playmap_1", - "last_center_map": "playmap_1", - "x": 4, - "y": 5, - "achievements": [], - "pokes": { - "0": {"name": "steini", "xp": 50, "hp": "SKIP", - "ap": ["SKIP", "SKIP"]} - }, - "inv": {"poketeball": 15, "healing_potion": 1}, - "settings": { - "load_mods": False}, - "figure.caught_pokes": ["steini"], - "visited_maps": ["playmap_1"], - "startup_time": 0, - "used_npcs": [], - "hotkeys": {}, - "pokete_care": { - "entry": 0, - "poke": None, - }, - "time": 0 - } - - if os.path.exists(SAVEPATH / "pokete.json"): - with open(SAVEPATH / "pokete.json") as _file: - _si = json.load(_file) - elif os.path.exists(HOME / ".cache" / "pokete" / "pokete.json"): - with open(HOME / ".cache" / "pokete" / "pokete.json") as _file: - _si = json.load(_file) - elif os.path.exists(HOME / ".cache" / "pokete" / "pokete.py"): - l_dict = {} - with open(HOME / ".cache" / "pokete" / "pokete.py", "r") as _file: - exec(_file.read(), {"session_info": _si}, l_dict) - _si = json.loads(json.dumps(l_dict["session_info"])) - return _si + save(figure) def reset_terminal(): @@ -940,7 +575,7 @@ def codes(string): """Cheats""" for i in string: if i == "w": - save() + save(figure) elif i == "!": exec(string[string.index("!") + 2:]) return @@ -958,12 +593,8 @@ def codes(string): # Those are adding additional actions to playmaps ################################################# -class ExtraActions: - """Extra actions class to keep track of extra actions""" - - @staticmethod - def playmap_7(): - """Cave animation""" +class Playmap7Event(PeriodicEvent): + def tick(self, tick: int): _map = obmp.ob_maps["playmap_7"] for obj in _map.get_obj("inner_walls").obs \ + [i.main_ob for i in _map.trainers] \ @@ -978,6 +609,11 @@ def playmap_7(): obj.rechar(" ") +extra_actions: dict[str, list[PeriodicEvent]] = { + "playmap_7": [Playmap7Event()] +} + + # main functions ################ @@ -985,7 +621,8 @@ def teleport(poke): """Teleports the player to another towns pokecenter ARGS: poke: The Poke shown in the animation""" - if (obj := roadmap(mvp.movemap, None, choose=True)) is None: + if (obj := RoadMap()(Context(None, mvp.movemap, mvp.movemap, figure), + choose=True)) is None: return if settings("animations").val: animations.transition(mvp.movemap, poke) @@ -1005,7 +642,7 @@ def swap_poke(): ): return port = 65432 - save() + save(figure) do = ask_bool(mvp.movemap, "Do you want to be the host?", mvp.movemap) if (index := deck.deck(mvp.movemap, 6, "Your deck", True)) is None: return @@ -1026,7 +663,7 @@ def swap_poke(): conn.sendall( str.encode( json.dumps( - {"mods": mods.mod_info, + {"mods": loaded_mods.mod_info, "name": figure.name, "poke": figure.pokes[index].dict()}))) else: @@ -1048,17 +685,17 @@ def swap_poke(): return sock.sendall( str.encode( - json.dumps({"mods": mods.mod_info, + json.dumps({"mods": loaded_mods.mod_info, "name": figure.name, "poke": figure.pokes[index].dict()}))) data = sock.recv(1024) decode_data = json.loads(data.decode()) logging.info("[Swap_poke] Recieved %s", decode_data) mod_info = decode_data.get("mods", {}) - if mods.mod_info != mod_info: + if loaded_mods.mod_info != mod_info: ask_ok( mvp.movemap, f"""Conflicting mod versions! -Your mods: {', '.join(i + '-' + mods.mod_info[i] for i in mods.mod_info)} +Your mods: {', '.join(i + '-' + loaded_mods.mod_info[i] for i in loaded_mods.mod_info)} Your partners mods: {', '.join(i + '-' + mod_info[i] for i in mod_info)}""", mvp.movemap ) @@ -1067,13 +704,13 @@ def swap_poke(): decode_data["poke"]["xp"], decode_data["poke"]["hp"]), index) figure.pokes[index].set_ap(decode_data["poke"]["ap"]) - save() # to avoid duping + save(figure) # to avoid duping ask_ok(mvp.movemap, f"You received: {figure.pokes[index].name.capitalize()} at level \ {figure.pokes[index].lvl()} from {decode_data['name']}.", mvp.movemap) -def _game(_map): +def _game(_map: PlayMap): """Game function ARGS: _map: The map that will be shown""" @@ -1091,15 +728,18 @@ def _game(_map): mvp.movemap.set(0, 0) mvp.movemap.bmap = _map mvp.movemap.full_show() - pevm = PeriodicEventManager(_map) + pevm = PeriodicEventManager( + [MovingGrassEvent(_map), MovingWaterEvent(_map), + TreatNPCEvent()] + _map.extra_actions()) + ctx = Context(pevm, mvp.movemap, mvp.movemap, figure) inp_dict = { Action.DECK: [deck.deck, (mvp.movemap, 6, "Your deck")], - Action.MAP: [roadmap, (mvp.movemap, pevm)], - Action.INVENTORY: [inv, ()], - Action.POKEDEX: [pokete_dex, ()], - Action.CLOCK: [timer.clock, (mvp.movemap,)], - Action.MENU: [mvp.movemap.menu, (pevm,)], - Action.HELP: [help_page, ()] + Action.MAP: [RoadMap(), (ctx,)], + Action.INVENTORY: [inv, (ctx,)], + Action.POKEDEX: [Dex(), (ctx,)], + Action.CLOCK: [timer.clock, (ctx,)], + Action.MENU: [menu, (ctx,)], + Action.HELP: [Help(), (ctx,)] } if _map.weather is not None: notifier.notify("Weather", "Info", _map.weather.info) @@ -1123,7 +763,7 @@ def _game(_map): mvp.movemap, "Do you really wish to exit?", mvp.movemap ): - save() + save(figure) sys.exit() elif action.triggers(Action.CONSOLE): inp = text_input(mvp.movemap.code_label, mvp.movemap, ":", @@ -1133,7 +773,7 @@ def _game(_map): mvp.movemap.code_label.outp(figure.map.pretty_name) codes(inp) _ev.clear() - std_loop(pevm=pevm, box=mvp.movemap) + loops.std(pevm=pevm, box=mvp.movemap) for statement, x, y in zip( [ figure.x + 6 > mvp.movemap.x + mvp.movemap.width, @@ -1242,8 +882,7 @@ def gen_maps(): Dict of all PlayMaps""" maps = {} for ob_map, args in p_data.maps.items(): - args["extra_actions"] = (getattr(ExtraActions, args["extra_actions"], - None) + args["extra_actions"] = (extra_actions.get(args["extra_actions"]) if args["extra_actions"] is not None else None) maps[ob_map] = PlayMap(name=ob_map, **args) @@ -1289,7 +928,7 @@ def main(): while True: try: _game(game_map) - except game.MapChangeExeption as err: + except MapChangeExeption as err: game_map = err.map @@ -1572,14 +1211,11 @@ def recogniser(): # resizing screen tss() + loading_screen() # Home global HOME = Path.home() - # loading screen - loading_screen = LoadingScreen(VERSION, CODENAME) - loading_screen() - # readinf savefile session_info = read_save() @@ -1598,21 +1234,7 @@ def recogniser(): settings("load_mods").val = False # Loading mods - if settings("load_mods").val: - try: - import mods - except ModError as mod_err: - error_box = InfoBox(str(mod_err), "Mod-loading Error") - error_box.center_add(loading_screen.map) - loading_screen.map.show() - sys.exit(1) - - for mod in mods.mod_obs: - mod.mod_p_data(p_data) - else: - mods = DummyMods() - logging.info("[General] %d mods are loaded: (%s)", - len(mods.mod_obs), ', '.join(mods.mod_names)) + try_load_mods(loading_screen.map) # validating data p_data.validate() @@ -1639,27 +1261,21 @@ def recogniser(): # Definiton of all additionaly needed obs and maps ############################################################# - mvp.movemap = mvp.Movemap(tss.height - 1, tss.width, Menu) + mvp.movemap = mvp.Movemap(tss.height - 1, tss.width) # A dict that contains all world action functions for Attacks abb_funcs = {"teleport": teleport} # side fn definitions detail.detail = detail.Detail(tss.height - 1, tss.width) - pokete_dex = Dex(figure) - help_page = Help(mvp.movemap) RoadMap.check_maps() - roadmap = RoadMap(figure) deck.deck = deck.Deck(tss.height - 1, tss.width, figure, abb_funcs) - about = About(VERSION, CODENAME, mvp.movemap) - inv = Inv(mvp.movemap) - buy = Buy(figure, mvp.movemap) - pokete_care = PoketeCare.from_dict(session_info.get("pokete_care", { + pokete_care.from_dict(session_info.get("pokete_care", { "entry": 0, "poke": None, })) timer.time = timer.Time(session_info.get("time", 0)) - timer.clock = timer.Clock(timer.time, mvp.movemap) + timer.clock = timer.Clock(timer.time) HighGrass.figure = figure Poketeball.figure = figure _ev.set_emit_fn(timer.time.emit_input) diff --git a/pokete_classes/achievements.py b/pokete_classes/achievements.py index fba893ce..b3ce79f3 100644 --- a/pokete_classes/achievements.py +++ b/pokete_classes/achievements.py @@ -4,12 +4,12 @@ import logging import scrap_engine as se from util import liner -from .hotkeys import ACTION_DIRECTIONS, Action, get_action -from .loops import std_loop, easy_exit_loop -from .ui_elements import BetterChooseBox, LabelBox +from .context import Context +from .input import ACTION_DIRECTIONS, Action, get_action +from .ui.elements import BetterChooseBox, LabelBox +from .ui.notify import notifier from .color import Color -from .notify import notifier -from . import movemap as mvp +from . import loops class Achievement: @@ -75,12 +75,13 @@ def __init__(self, ach, ach_ob, overview): is_ach = ach_ob.is_achieved(ach.identifier) date = [i[-1] for i in ach_ob.achieved if i[0] == ach.identifier][0] if is_ach else "" - label = se.Text("Achieved: ", state="float")\ + label = se.Text("Achieved: ", state="float") \ + se.Text("Yes" if is_ach else "No", esccode=Color.thicc - + (Color.green if is_ach - else Color.grey), state="float")\ - + (se.Text("\nAt: " + date, state="float") if is_ach else se.Text(""))\ + + (Color.green if is_ach + else Color.grey), state="float") \ + + (se.Text("\nAt: " + date, + state="float") if is_ach else se.Text("")) \ + se.Text("\n" + liner(ach.desc, 30), state="float") super().__init__(label, name=ach.title, info=f"{Action.CANCEL.mapping}:close", @@ -93,19 +94,17 @@ class AchievementOverview(BetterChooseBox): def __init__(self): super().__init__( 3, [se.Text(" ")], name="Achievements", - overview=mvp.movemap.menu ) - def __call__(self, _map): - """Input loop - ARGS: - _map: se.Map to show this on""" + def __call__(self, ctx: Context): + """Input loop""" self.set_items(3, [se.Text(i.title, esccode=Color.thicc + Color.green if achievements.is_achieved(i.identifier) else "", state="float") for i in achievements.achievements]) - self.map = _map + self.map = ctx.map + self.overview = ctx.overview with self: while True: action = get_action() @@ -122,10 +121,11 @@ def __call__(self, _map): ach, achievements, self - ).center_add(_map) as achbox: - easy_exit_loop(box=achbox) - std_loop(box=self) - self.map.show() + ).center_add(ctx.map) as achbox: + loops.easy_exit( + box=achbox, pevm=ctx.pevm) + loops.std(box=self, pevm=ctx.pevm) + self.map.full_show() achievements = Achievements() diff --git a/pokete_classes/classes.py b/pokete_classes/classes.py index d0ff8605..04be25b5 100644 --- a/pokete_classes/classes.py +++ b/pokete_classes/classes.py @@ -2,6 +2,7 @@ import scrap_engine as se import pokete_classes.game_map as gm +from .game import PeriodicEvent from .weather import Weather @@ -19,7 +20,8 @@ class PlayMap(gm.GameMap): def __init__(self, height=se.screen_height - 1, width=se.screen_width, trainers=None, name="", pretty_name="", poke_args=None, - w_poke_args=None, extra_actions=None, weather=None, + w_poke_args=None, + extra_actions: list[PeriodicEvent] | None = None, weather=None, song="03 Chibi Ninja.mp3"): super().__init__(height, width, name=name) self.song = song @@ -52,10 +54,11 @@ def get_obj(self, name): name: Name in registry""" return self.registry.get(name, None) - def extra_actions(self): + def extra_actions(self) -> list[PeriodicEvent]: """Executes the extra action""" if self.__extra_actions is not None: - self.__extra_actions() + return self.__extra_actions + return [] class OutP(se.Text): diff --git a/pokete_classes/context.py b/pokete_classes/context.py new file mode 100644 index 00000000..397abee7 --- /dev/null +++ b/pokete_classes/context.py @@ -0,0 +1,21 @@ +from copy import copy + +from .movemap import Movemap +from .game import PeriodicEventManager +from .ui import Overview + + +class Context: + def __init__( + self, pevm: PeriodicEventManager, _map: Movemap, + overview: Overview, figure + ): + self.pevm = pevm + self.map = _map + self.overview = overview + self.figure = figure + + def with_overview(self, overview: Overview) -> "Context": + ctx = copy(self) + ctx.overview = overview + return ctx diff --git a/pokete_classes/deck.py b/pokete_classes/deck.py index 51ef2b99..6f7eac78 100644 --- a/pokete_classes/deck.py +++ b/pokete_classes/deck.py @@ -4,17 +4,16 @@ import scrap_engine as se from pokete_classes import detail import pokete_classes.game_map as gm -from pokete_classes.hotkeys import ( - ACTION_DIRECTIONS, Action, get_action +from .input import ( + ACTION_DIRECTIONS, Action, get_action, _ev ) import pokete_classes.movemap as mvp -from .event import _ev -from .input import ask_bool, ask_ok -from .loops import std_loop +from .ui import ask_bool, ask_ok from .color import Color from .poke import Poke -from .ui_elements import StdFrame2 +from .ui.elements import StdFrame2 from .tss import tss +from . import loops class Deck(detail.Informer): @@ -164,7 +163,7 @@ def __call__(self, overview, p_len, label="Your full deck", in_fight=False): {self.figure.pokes[self.index.index].name}?", self): self.rem_pokes() self.figure.pokes[self.index.index] = Poke("__fallback__", - 10, 0) + 10, 0) self.pokes = self.figure.pokes[:len(self.pokes)] self.add_all() self.index.set( @@ -175,7 +174,7 @@ def __call__(self, overview, p_len, label="Your full deck", in_fight=False): mvp.movemap.balls_label_rechar(self.figure.pokes) elif action.triggers(Action.ACCEPT): if len(self.pokes) == 0 or \ - self.pokes[self.index.index].identifier == "__fallback__": + self.pokes[self.index.index].identifier == "__fallback__": continue if in_fight: if self.pokes[self.index.index].hp > 0: @@ -200,9 +199,9 @@ def __call__(self, overview, p_len, label="Your full deck", in_fight=False): _ev.set(Action.CANCEL.mapping) continue self.submap.full_show(init=True) - std_loop(False, box=self) - if len(self.pokes) > 0 and\ - self.index.y - self.submap.y + 6 > self.submap.height: + loops.std(False, box=self) + if len(self.pokes) > 0 and \ + self.index.y - self.submap.y + 6 > self.submap.height: self.submap.set(self.submap.x, self.submap.y + 1) elif len(self.pokes) > 0 and self.index.y - 1 < self.submap.y: self.submap.set(self.submap.x, self.submap.y - 1) diff --git a/pokete_classes/detail.py b/pokete_classes/detail.py index 4389e499..71b5b5a1 100644 --- a/pokete_classes/detail.py +++ b/pokete_classes/detail.py @@ -3,13 +3,12 @@ import scrap_engine as se import pokete_classes.game_map as gm from util import liner -from .hotkeys import Action, get_action +from .input import Action, get_action, _ev +from .ui.elements import StdFrame2, ChooseBox from .pokestats import PokeStatsInfoBox -from .loops import std_loop -from .event import _ev -from .ui_elements import StdFrame2, ChooseBox from .color import Color from .tss import tss +from . import loops class Informer: @@ -35,8 +34,8 @@ def add(poke, figure, _map, _x, _y, in_deck=True): obj.add(_map, _x + __x, _y + __y) if in_deck and figure.pokes.index(poke) < 6: poke.pball_small.add(_map, round(_map.width / 2) - 1 - if figure.pokes.index(poke) % 2 == 0 - else _map.width - 2, _y) + if figure.pokes.index(poke) % 2 == 0 + else _map.width - 2, _y) for eff in poke.effects: eff.add_label() @@ -178,7 +177,8 @@ def __call__(self, poke, abb=True, overview=None): self.attack_defense.rechar(f"Attack:{self.poke.atc}\ {(4 - len(str(self.poke.atc))) * ' '}Defense:{self.poke.defense}") self.initiative_label.rechar(f"Initiative:{self.poke.initiative}") - for obj, _x, _y in zip([self.poke.desc, self.poke.text_type], [34, 41], [2, 5]): + for obj, _x, _y in zip([self.poke.desc, self.poke.text_type], [34, 41], + [2, 5]): obj.add(self.map, _x, _y) self.add_attack_labels() if (tss.height - 1, tss.width) != (self.map.height, self.map.width): @@ -209,30 +209,31 @@ def __call__(self, poke, abb=True, overview=None): for i in abb_obs ], overview=self - ).center_add(self.map)\ - as box: + ).center_add(self.map) \ + as box: while True: action = get_action() if action.triggers(Action.UP, Action.DOWN): box.input(action) self.map.show() elif action.triggers(Action.ACCEPT): - ret_action = abb_obs[box.index.index].world_action + ret_action = abb_obs[ + box.index.index].world_action _ev.set(Action.CANCEL.mapping) break elif action.triggers(Action.CANCEL): break - std_loop(False, box=box) - std_loop(False, box=self) + loops.std(False, box=box) + loops.std(False, box=self) # This section generates the Text effect for attack labels for atc in self.poke.attack_obs: if len(atc.desc) > int((self.map.width - 3) / 2 - 1): if atc.temp_j == 5: atc.temp_i += 1 atc.temp_j = 0 - if atc.temp_i == len(atc.desc)\ - - int(self.map.width / 2 - 1)\ - + 10: + if atc.temp_i == len(atc.desc) \ + - int(self.map.width / 2 - 1) \ + + 10: atc.temp_i = 0 atc.temp_j = -30 atc.label_desc.rechar(atc.desc[atc.temp_i: diff --git a/pokete_classes/dex.py b/pokete_classes/dex.py index 9e17e1d7..70930b7a 100644 --- a/pokete_classes/dex.py +++ b/pokete_classes/dex.py @@ -1,28 +1,26 @@ """Contains the Pokete dex that gives information about all Poketes""" import scrap_engine as se -from pokete_classes.hotkeys import Action, ACTION_UP_DOWN, get_action import pokete_data as p_data -import pokete_classes.movemap as mvp + from util import liner -from .loops import std_loop, easy_exit_loop +from .context import Context +from .input import Action, ACTION_UP_DOWN, get_action from .poke import Poke from .color import Color from .nature import PokeNature -from .ui_elements import ChooseBox, Box -from .tss import tss +from .ui import Overview +from .ui.elements import ChooseBox, Box +from . import loops -class Dex: - """The Pokete dex that shows stats about all Poketes ever caught - ARGS: - figure: Figure object""" +class Dex(Overview): + """The Pokete dex that shows stats about all Poketes ever caught""" - def __init__(self, figure): - self.box = ChooseBox(mvp.movemap.height - 3, 35, "Poketedex", + def __init__(self): + self.box = ChooseBox(50, 35, "Poketedex", info=f"{Action.CANCEL.mapping}:close") self.detail_box = Box(16, 35, overview=self) - self.figure = figure self.idx = 0 self.obs = [] self.detail_info = se.Text("", state="float") @@ -41,7 +39,7 @@ def rem_c_obs(self): c_ob.remove() self.box.remove_c_obs() - def detail(self, poke): + def detail(self, ctx: Context, poke): """Shows details about the Pokete ARGS: poke: Pokes identifier""" @@ -55,10 +53,10 @@ def detail(self, poke): }[poke.night_active] desc_text = liner(poke.desc.text.replace("\n", " ") + (f"""\n\n Evolves into { - p_data.pokes[poke.evolve_poke]['name'] if - poke.evolve_poke in - self.figure.caught_pokes else '???' - }.""" + p_data.pokes[poke.evolve_poke]['name'] if + poke.evolve_poke in + ctx.figure.caught_pokes else '???' + }.""" if poke.evolve_lvl != 0 else ""), 29) self.detail_box.resize(10 + len(desc_text.split("\n")), 35) self.detail_box.name_label.rechar(poke.name) @@ -73,15 +71,15 @@ def detail(self, poke): Initiative: {poke.initiative} Active: """) + se.Text(active[0], esccode=active[1]) - with self.detail_box.center_add(mvp.movemap): - easy_exit_loop(box=self.detail_box) + with self.detail_box.center_add(self.box.map): + loops.easy_exit(box=self.detail_box, pevm=ctx.pevm) self.detail_box.rem_ob(poke.ico) def resize_view(self): """Manages recursive view resizing""" self.box.remove() - mvp.movemap.resize_view() - self.box.resize(mvp.movemap.height - 3, 35) + self.box.overview.resize_view() + self.box.resize(self.box.map.height - 3, 35) self.rem_c_obs() self.add_c_obs() if len(self.box.c_obs) == 0: @@ -91,23 +89,24 @@ def resize_view(self): self.box.set_index(len(self.box.c_obs) - 1) if self.box.index.index >= len(self.box.c_obs): self.box.set_index(len(self.box.c_obs) - 1) - self.box.add(mvp.movemap, mvp.movemap.width - self.box.width, 0) - mvp.movemap.full_show() + self.box.add(self.box.map, self.box.map.width - self.box.width, 0) + self.box.map.full_show() - def __call__(self): + def __call__(self, ctx: Context): """Opens the dex""" - self.box.resize(mvp.movemap.height - 3, 35) + self.box.overview = ctx.overview + self.box.resize(ctx.map.height - 3, 35) pokes = p_data.pokes self.idx = 0 p_dict = {i[1]: i[-1] for i in sorted([(pokes[j]["types"][0], j, pokes[j]) for j in list(pokes)[1:]])} self.obs = [se.Text(f"{i + 1} \ -{p_dict[poke]['name'] if poke in self.figure.caught_pokes else '???'}", - state="float") +{p_dict[poke]['name'] if poke in ctx.figure.caught_pokes else '???'}", + state="float") for i, poke in enumerate(p_dict)] self.add_c_obs() - with self.box.add(mvp.movemap, mvp.movemap.width - self.box.width, 0): + with self.box.add(ctx.map, ctx.map.width - self.box.width, 0): while True: action = get_action() for event, idx, n_idx, add, idx_2 in zip( @@ -118,8 +117,8 @@ def __call__(self): [-1, 0], ): if action.triggers(event) and self.box.index.index == idx: - if self.box.c_obs[self.box.index.index]\ - != self.obs[idx_2]: + if self.box.c_obs[self.box.index.index] \ + != self.obs[idx_2]: self.rem_c_obs() self.idx += add self.add_c_obs() @@ -127,13 +126,16 @@ def __call__(self): action = get_action() if action.triggers(Action.ACCEPT): if "???" not in self.box.c_obs[self.box.index.index].text: - self.detail(list(p_dict)[self.idx - * (self.box.height - 2) - + self.box.index.index]) + self.detail( + ctx, + list(p_dict)[self.idx + * (self.box.height - 2) + + self.box.index.index], + ) elif action.triggers(*ACTION_UP_DOWN): self.box.input(action) elif action.triggers(Action.CANCEL, Action.POKEDEX): break - std_loop(box=self) - mvp.movemap.show() + loops.std(box=self, pevm=ctx.pevm) + ctx.map.full_show() self.rem_c_obs() diff --git a/pokete_classes/doors.py b/pokete_classes/doors.py index 2d7ff1f0..26c4fc0a 100644 --- a/pokete_classes/doors.py +++ b/pokete_classes/doors.py @@ -2,7 +2,7 @@ import random import scrap_engine as se -from pokete_classes import game, ob_maps as obmp +from . import game, ob_maps as obmp class CenterDoor(se.Object): diff --git a/pokete_classes/fightmap/__init__.py b/pokete_classes/fightmap/__init__.py index 5bb73fe7..c3643add 100644 --- a/pokete_classes/fightmap/__init__.py +++ b/pokete_classes/fightmap/__init__.py @@ -8,19 +8,18 @@ from pokete_classes import animations, ob_maps as obmp, \ deck, game_map as gm from release import SPEED_OF_TIME -from ..hotkeys import Action, get_action +from ..input import Action, get_action from ..audio import audio from ..npcs import Trainer from ..providers import NatureProvider, ProtoFigure -from ..ui_elements import StdFrame2 +from ..ui.elements import StdFrame2 from ..classes import OutP -from ..input import ask_bool +from ..ui import ask_bool from ..achievements import achievements -from ..inv_items import invitems +from ..inv import invitems from ..settings import settings -from ..loops import std_loop from ..tss import tss -from .. import movemap as mvp +from .. import movemap as mvp, loops from .attack import AttackBox from .inv import InvBox @@ -222,22 +221,22 @@ def get_figure_attack(self, figure, enem): if ( not enem.escapable or not ask_bool( - self, - "Do you really want to run away?", - overview=self - ) + self, + "Do you really want to run away?", + overview=self + ) ): continue if ( random.randint(0, 100) < max( - 5, - min( - 50 - ( - figure.curr.initiative - enem.curr.initiative - ), - 95 - ) + 5, + min( + 50 - ( + figure.curr.initiative - enem.curr.initiative + ), + 95 ) + ) ): self.outp.outp("You failed to run away!") time.sleep(SPEED_OF_TIME * 1) @@ -279,7 +278,7 @@ def get_figure_attack(self, figure, enem): self.show(init=True) continue return "" - std_loop(False, box=self) + loops.std(False, box=self) self.show() def fight(self, providers): diff --git a/pokete_classes/fightmap/attack.py b/pokete_classes/fightmap/attack.py index 255026b3..539cabc9 100644 --- a/pokete_classes/fightmap/attack.py +++ b/pokete_classes/fightmap/attack.py @@ -2,10 +2,9 @@ import scrap_engine as se from util import liner -from ..hotkeys import ACTION_UP_DOWN, Action, get_action -from ..ui_elements import ChooseBox, LabelBox -from ..loops import std_loop -from .. import effects +from ..input import ACTION_UP_DOWN, Action, get_action +from ..ui.elements import ChooseBox, LabelBox +from .. import effects, loops class AttackBox(se.Box): @@ -111,7 +110,7 @@ def __call__(self, _map, attack_obs): self.rechar_atk_box(attack_obs) self.map.show() elif action.triggers(Action.ACCEPT) or (0 <= action.get_number() - < len(attack_obs)): + < len(attack_obs)): attack = attack_obs[ self.box.index.index if action.triggers(Action.ACCEPT) else action.get_number() @@ -137,5 +136,5 @@ def __call__(self, _map, attack_obs): self.rechar_atk_box(attack_obs) self.map.show() continue - std_loop(False, box=self) + loops.std(False, box=self) return attack diff --git a/pokete_classes/fightmap/inv.py b/pokete_classes/fightmap/inv.py index af8f3bdf..ef639cd4 100644 --- a/pokete_classes/fightmap/inv.py +++ b/pokete_classes/fightmap/inv.py @@ -1,9 +1,9 @@ """Contains stuff related to fight inventory""" import scrap_engine as se -from ..ui_elements import ChooseBox -from ..loops import std_loop -from ..hotkeys import ACTION_UP_DOWN, Action, get_action +from ..ui.elements import ChooseBox +from ..input import ACTION_UP_DOWN, Action, get_action +from .. import loops class InvBox(ChooseBox): @@ -24,7 +24,7 @@ def __call__(self, _map, items, inv): items: List of InvItems that can be choosen from inv: The Figures inv""" self.add_c_obs([se.Text(f"{i.pretty_name}s : {inv[i.name]}") - for i in items]) + for i in items]) self.set_index(0) self.resize(_map.height - 3, 35) with self.add(_map, _map.width - 35, 0): @@ -39,6 +39,6 @@ def __call__(self, _map, items, inv): elif action.triggers(Action.ACCEPT): item = items[self.index.index] break - std_loop(False, box=self) + loops.std(False, box=self) self.remove_c_obs() return item diff --git a/pokete_classes/game/__init__.py b/pokete_classes/game/__init__.py new file mode 100644 index 00000000..9b4ce481 --- /dev/null +++ b/pokete_classes/game/__init__.py @@ -0,0 +1,2 @@ +from .map_change_exception import MapChangeExeption +from .periodic_event_manager import PeriodicEvent, PeriodicEventManager diff --git a/pokete_classes/game.py b/pokete_classes/game/map_change_exception.py similarity index 100% rename from pokete_classes/game.py rename to pokete_classes/game/map_change_exception.py diff --git a/pokete_classes/game/periodic_event_manager.py b/pokete_classes/game/periodic_event_manager.py new file mode 100644 index 00000000..8098b4c3 --- /dev/null +++ b/pokete_classes/game/periodic_event_manager.py @@ -0,0 +1,26 @@ +"""Contains the pevm""" + + +class PeriodicEvent: + def tick(self, tick: int): + raise NotImplemented() + + +class PeriodicEventManager: + """As the name states: It manages periodic events in the game loop""" + + def __init__(self, events: list[PeriodicEvent], tick=0): + self.events = events + self.tick = tick + + def with_events( + self, + events: list[PeriodicEvent] + ) -> "PeriodicEventManager": + return PeriodicEventManager(self.events + events, self.tick) + + def event(self): + """Executes the events""" + for event in self.events: + event.tick(self.tick) + self.tick += 1 diff --git a/pokete_classes/general.py b/pokete_classes/general.py index f53335f2..61e8dc7b 100644 --- a/pokete_classes/general.py +++ b/pokete_classes/general.py @@ -3,7 +3,7 @@ import logging from pokete_classes import movemap as mvp from .doors import DoorToCenter -from .input import ask_ok +from .ui import ask_ok def check_walk_back(figure, self=None): diff --git a/pokete_classes/input/__init__.py b/pokete_classes/input/__init__.py new file mode 100644 index 00000000..ee30be3b --- /dev/null +++ b/pokete_classes/input/__init__.py @@ -0,0 +1,6 @@ +from .event import _ev +from .hotkeys import ( + Action, ActionList, ACTION_DIRECTIONS, ACTION_UP_DOWN, + get_action, get_mapping, hotkeys_save, hotkeys_from_save +) +from .text import text_input diff --git a/pokete_classes/event.py b/pokete_classes/input/event.py similarity index 100% rename from pokete_classes/event.py rename to pokete_classes/input/event.py diff --git a/pokete_classes/hotkeys.py b/pokete_classes/input/hotkeys.py similarity index 92% rename from pokete_classes/hotkeys.py rename to pokete_classes/input/hotkeys.py index 2f4885bc..a66a219a 100644 --- a/pokete_classes/hotkeys.py +++ b/pokete_classes/input/hotkeys.py @@ -124,22 +124,22 @@ def get_x_strength(self) -> int: '8': ActionList([Action.ACT_8]), '9': ActionList([Action.ACT_9]), - 'a': ActionList([Action.LEFT]), + 'a': ActionList([Action.LEFT]), 'Key.left': ActionList([Action.LEFT]), - 'd': ActionList([Action.RIGHT]), + 'd': ActionList([Action.RIGHT]), 'Key.right': ActionList([Action.RIGHT]), - 'w': ActionList([Action.UP]), + 'w': ActionList([Action.UP]), 'Key.up': ActionList([Action.UP]), - 's': ActionList([Action.DOWN]), + 's': ActionList([Action.DOWN]), 'Key.down': ActionList([Action.DOWN]), 'Key.space': ActionList([Action.ACCEPT]), 'Key.enter': ActionList([Action.ACCEPT]), - 'y': ActionList([Action.ACCEPT, Action.QUICK_ATC_1]), - 'o': ActionList([Action.ACCEPT]), - 'q': ActionList([Action.CANCEL]), - 'n': ActionList([Action.CANCEL]), - 'Key.esc': ActionList([Action.CANCEL]), + 'y': ActionList([Action.ACCEPT, Action.QUICK_ATC_1]), + 'o': ActionList([Action.ACCEPT]), + 'q': ActionList([Action.CANCEL]), + 'n': ActionList([Action.CANCEL]), + 'Key.esc': ActionList([Action.CANCEL]), 'Key.backspace': ActionList([Action.CANCEL]), 'r': ActionList([Action.REMOVE]), @@ -174,7 +174,7 @@ def hotkeys_save(): def hotkeys_from_save(save, _map, version_change): """Sets hotkey_mappings from save""" - from .input import ask_bool + from ..ui import ask_bool global hotkey_mappings if save == {}: diff --git a/pokete_classes/input/text.py b/pokete_classes/input/text.py new file mode 100644 index 00000000..2913667a --- /dev/null +++ b/pokete_classes/input/text.py @@ -0,0 +1,47 @@ +from util import hard_liner +from .event import _ev +from .. import loops + + +def text_input(obj, _map, name, wrap_len, max_len=1000000, box=None): + """Processes text input + ARGS: + obj: The text label that will be rechared + _map: The map this happens on + name: The default value of the label + wrap_len: The len at which the text wraps + max_len: The len at which the text shall end + box: The box this is called for""" + _ev.clear() + obj.rechar(hard_liner(wrap_len, name + "█")) + bname = name + _map.show() + while True: + # Use lower level ev.get() methods because we need + # to handle typed text, not game actions + if _ev.get() in ("Key.enter", "Key.esc"): + _ev.clear() + obj.rechar(hard_liner(wrap_len, name)) + _map.show() + return name + if _ev.get() == "Key.backspace": + if len(name) <= 0: + _ev.clear() + obj.rechar(bname) + _map.show() + return bname + name = name[:-1] + obj.rechar(hard_liner(wrap_len, name + "█")) + _map.show() + _ev.clear() + elif ( + ((i := _ev.get()) not in ["", "exit"] and "Key." not in i) + and len(name) < max_len or i == "Key.space" + ): + if _ev.get() == "Key.space": + _ev.set(" ") + name += str(_ev.get()) + obj.rechar(hard_liner(wrap_len, name + "█")) + _map.show() + _ev.clear() + loops.std(_map.name == "movemap", box=box) diff --git a/pokete_classes/inv/__init__.py b/pokete_classes/inv/__init__.py new file mode 100644 index 00000000..1d81e781 --- /dev/null +++ b/pokete_classes/inv/__init__.py @@ -0,0 +1,3 @@ +from .inv import inv +from .items import invitems +from .buy import buy diff --git a/pokete_classes/inv/box.py b/pokete_classes/inv/box.py new file mode 100644 index 00000000..96f6b1c3 --- /dev/null +++ b/pokete_classes/inv/box.py @@ -0,0 +1,13 @@ +from ..ui.elements import Box +from .. import movemap as mvp + + +class InvBox(Box): + """Box wrapper for inv""" + + def resize_view(self): + """Manages recursive view resizing""" + self.remove() + self.overview.resize_view() + self.add(self.map, self.overview.box.x - 19, 3) + mvp.movemap.full_show() diff --git a/pokete_classes/buy.py b/pokete_classes/inv/buy.py similarity index 51% rename from pokete_classes/buy.py rename to pokete_classes/inv/buy.py index 630e7508..90a650d3 100644 --- a/pokete_classes/buy.py +++ b/pokete_classes/inv/buy.py @@ -1,43 +1,29 @@ """Classes related to buing stuff""" import scrap_engine as se -from pokete_classes.hotkeys import ACTION_UP_DOWN, Action, get_action -from util import liner -from .loops import std_loop -from .ui_elements import Box, ChooseBox -from .inv_items import invitems -from . import movemap as mvp - - -class InvBox(Box): - """Box wrapper for inv""" - def resize_view(self): - """Manages recursive view resizing""" - self.remove() - self.overview.resize_view() - self.add(self.map, self.overview.box.x - 19, 3) - mvp.movemap.full_show() +from pokete_classes.context import Context +from util import liner +from .box import InvBox +from .items import invitems +from ..ui import Overview +from ..input import ACTION_UP_DOWN, Action, get_action +from ..ui.elements import ChooseBox +from .. import movemap as mvp, loops -class Buy: - """Menu to buy items in, is triggered in shop - Args: - figure: Figure object - _map: The se.Map the menu is shown on""" +class Buy(Overview): + """Menu to buy items in, is triggered in shop""" - def __init__(self, figure, _map): - self.map = _map - self.box = ChooseBox(_map.height - 3, 35, "Shop") + def __init__(self): + self.box = ChooseBox(50, 35, "Shop") self.box2 = InvBox(7, 21, overview=self) - self.fig = figure - self.map = _map self.items = [invitems.poketeball, invitems.superball, invitems.healing_potion, invitems.super_potion, invitems.ap_potion] self.box.add_c_obs([se.Text(f"{obj.pretty_name} : {obj.price}$") for obj in self.items]) - self.money_label = se.Text(f"{figure.get_money()}$") + self.money_label = se.Text("0$") self.desc_label = se.Text(" ") # adding self.box.add_ob(self.money_label, @@ -47,18 +33,21 @@ def __init__(self, figure, _map): def resize_view(self): """Manages recursive view resizing""" self.box.remove() - self.map.resize_view() - self.box.resize(self.map.height - 3, 35) - self.box.add(self.map, self.map.width - self.box.width, 0) + self.box.map.resize_view() + self.box.resize(self.box.map.height - 3, 35) + self.box.add(self.box.map, self.box.map.width - self.box.width, 0) mvp.movemap.full_show() - def __call__(self): + def __call__(self, ctx: Context): """Opens the buy menu""" - self.box.resize(self.map.height - 3, 35) - with self.box.add(self.map, self.map.width - 35, 0): - self.box2.add(self.map, self.box.x - 19, 3) + self.money_label.rechar(f"{ctx.figure.get_money()}$") + self.box.set_ob(self.money_label, + self.box.width - 2 - len(self.money_label.text), 0) + self.box.resize(ctx.map.height - 3, 35) + with self.box.add(ctx.map, ctx.map.width - 35, 0): + self.box2.add(ctx.map, self.box.x - 19, 3) self.rechar() - self.map.show() + ctx.map.show() while True: action = get_action() if action.triggers(*ACTION_UP_DOWN): @@ -68,11 +57,11 @@ def __call__(self): break elif action.triggers(Action.ACCEPT): obj = self.items[self.box.index.index] - if self.fig.get_money() - obj.price >= 0: - self.fig.add_money(-obj.price) - self.fig.give_item(obj.name) - std_loop(box=self.box2) - self.map.show() + if ctx.figure.get_money() - obj.price >= 0: + ctx.figure.add_money(-obj.price) + ctx.figure.give_item(obj.name) + loops.std(box=self.box2, pevm=ctx.pevm) + ctx.map.full_show() self.box2.remove() def rechar(self): @@ -82,5 +71,7 @@ def rechar(self): self.desc_label.rechar(liner(obj.desc, 19)) +buy = Buy() + if __name__ == "__main__": print("\033[31;1mDo not execute this!\033[0m") diff --git a/pokete_classes/inv/inv.py b/pokete_classes/inv/inv.py new file mode 100644 index 00000000..9d2f4eca --- /dev/null +++ b/pokete_classes/inv/inv.py @@ -0,0 +1,195 @@ +import scrap_engine as se + +from util import liner +from .items import LearnDisc, invitems +from .. import loops, deck +from .box import InvBox +from ..context import Context +from ..input import Action, get_action, _ev +from ..learnattack import LearnAttack +from ..types import types +from ..poke import upgrade_by_one_lvl +from ..ui import ask_bool, ask_ok, Overview +from ..ui.elements import ChooseBox + + +class Inv(Overview): + """Inventory to see and manage items in""" + + def __init__(self): + self.map = None + self.box = ChooseBox(50, 35, "Inventory", + f"{Action.REMOVE.mapping}:remove") + self.box2 = InvBox(7, 21, overview=self) + self.money_label = se.Text("$0") + self.desc_label = se.Text(" ") + # adding + self.box.add_ob(self.money_label, + self.box.width - 2 - len(self.money_label.text), 0) + self.box2.add_ob(self.desc_label, 1, 1) + + def resize_view(self): + """Manages recursive view resizing""" + self.box.remove() + self.box.overview.resize_view() + self.box.resize(self.map.height - 3, 35) + self.box.add(self.map, self.map.width - self.box.width, 0) + self.map.full_show() + + def set_money(self, figure): + self.money_label.rechar(f"${figure.get_money()}") + self.box.set_ob(self.money_label, + self.box.width - 2 - len(self.money_label.text), 0) + + def __call__(self, ctx: Context): + """Opens the inventory""" + self.map = ctx.map + self.box.overview = ctx.overview + figure = ctx.figure + _ev.clear() + items = self.add(figure) + self.box.resize(self.map.height - 3, 35) + self.set_money(figure) + with self.box.add(self.map, self.map.width - 35, 0): + while True: + action = get_action() + if action.triggers(Action.UP, Action.DOWN): + self.box.input(action) + elif action.triggers(Action.CANCEL): + break + elif action.triggers(Action.ACCEPT): + obj = items[self.box.index.index] + self.box2.name_label.rechar(obj.pretty_name) + self.desc_label.rechar(liner(obj.desc, 19)) + self.box2.add(self.map, self.box.x - 19, 3) + while True: + action = get_action() + if ( + action.triggers(Action.CANCEL) + or action.triggers(Action.ACCEPT) + ): + self.box2.remove() + if obj.name == "treat": + if ask_bool( + self.map, + "Do you want to upgrade one of " + "your Poketes by a level?", + self + ): + ex_cond = True + while ex_cond: + index = deck.deck( + self.map, 6, label="Your deck", + in_fight=True + ) + if index is None: + ex_cond = False + self.map.show(init=True) + break + poke = figure.pokes[index] + break + if not ex_cond: + break + upgrade_by_one_lvl(poke, figure, self.map) + items = self.rem_item(figure, obj.name, + items) + ask_ok( + self.map, + f"{poke.name} reached level " + f"{poke.lvl()}!", + self + ) + elif isinstance(obj, LearnDisc): + if ask_bool( + self.map, + f"Do you want to teach " + f"'{obj.attack_dict['name']}'?", + self + ): + ex_cond = True + while ex_cond: + index = deck.deck( + self.map, 6, label="Your deck", + in_fight=True + ) + if index is None: + ex_cond = False + self.map.show(init=True) + break + poke = figure.pokes[index] + if getattr(types, + obj.attack_dict['types'][0]) \ + in poke.types: + break + ex_cond = ask_bool( + self.map, + "You can't teach " + f"'{obj.attack_dict['name']}' to " + f"'{poke.name}'! \n" + "Do you want to continue?", + self + ) + if not ex_cond: + break + if LearnAttack(poke, self.map, self) \ + (obj.attack_name): + items = self.rem_item(figure, obj.name, + items) + if len(items) == 0: + break + break + loops.std(pevm=ctx.pevm, box=self.box2) + self.map.full_show() + elif action.triggers(Action.REMOVE): + if ask_bool( + self.map, + "Do you really want to throw " + f"{items[self.box.index.index].pretty_name} away?", + self + ): + items = self.rem_item( + figure, items[self.box.index.index].name, + items + ) + if len(items) == 0: + break + loops.std(pevm=ctx.pevm, box=self) + self.map.full_show() + self.box.remove_c_obs() + + def rem_item(self, figure, name, items): + """Removes an item from the inv + ARGS: + name: Items name + items: List of Items + RETURNS: + List of Items""" + figure.remove_item(name) + for obj in self.box.c_obs: + obj.remove() + self.box.remove_c_obs() + items = self.add(figure) + if not items: + return items + if self.box.index.index >= len(items): + self.box.set_index(len(items) - 1) + return items + + def add(self, figure): + """Adds all items to the box + RETURNS: + List of Items""" + items = [getattr(invitems, i) for i in figure.inv if figure.inv[i] > 0] + self.box.add_c_obs( + [ + se.Text( + f"{i.pretty_name}s : {figure.inv[i.name]}", + state="float" + ) + for i in items + ] + ) + return items + + +inv = Inv() diff --git a/pokete_classes/inv_items.py b/pokete_classes/inv/items.py similarity index 100% rename from pokete_classes/inv_items.py rename to pokete_classes/inv/items.py diff --git a/pokete_classes/landscape.py b/pokete_classes/landscape.py index e4dc287b..fe5599fb 100644 --- a/pokete_classes/landscape.py +++ b/pokete_classes/landscape.py @@ -8,7 +8,7 @@ from .color import Color from .general import check_walk_back from .poke import Poke -from .input import ask_ok +from .ui import ask_ok class HighGrass(se.Object): @@ -58,8 +58,6 @@ class Meadow(se.Text): all_grass = [] all_water = [] all_sand = [] - max_tick = 100 - curr_tick = max_tick def __init__(self, string, poke_args): super().__init__(string, ignore=self.esccode + " " + Color.reset, @@ -71,47 +69,6 @@ def __init__(self, string, poke_args): Color.yellow: Meadow.all_sand, }[self.esccode].append(self) - @classmethod - def moving_grass(cls, objs): - """Animation for moving grass - ARGS: - objs: List of Highgrass objects this is done for""" - if cls.curr_tick < cls.max_tick: - cls.curr_tick += 1 - return - cls.curr_tick = 0 - - for obj in objs: - if obj.char == cls.esccode + ";" + Color.reset: - if random.randint(0, 600) == 0: - obj.rechar(Color.thicc + cls.esccode + ";" + Color.reset) - cls.check_figure_redraw(obj) - else: - obj.rechar(cls.esccode + ";" + Color.reset) - cls.check_figure_redraw(obj) - - @classmethod - def moving_water(cls, objs): - """Water animation - ARGS: - objs: The water objects this will happen for""" - for obj in objs: - if random.randint(0, 9) == 0: - if " " not in obj.char: - obj.rechar([i for i in - [Color.lightblue + "~" + Color.reset, - Color.blue + "~" + Color.reset] - if i != obj.char][0]) - cls.check_figure_redraw(obj) - - @staticmethod - def check_figure_redraw(obj): - """Checks whether or not the figure has to be redrawn - ARGS: - obj: The obj that this is checked for""" - if obj.x == HighGrass.figure.x and obj.y == HighGrass.figure.y: - HighGrass.figure.redraw() - class Water(Meadow): """Same as Meadow, but for Water""" diff --git a/pokete_classes/learnattack.py b/pokete_classes/learnattack.py index bab1c521..758069fa 100644 --- a/pokete_classes/learnattack.py +++ b/pokete_classes/learnattack.py @@ -1,15 +1,15 @@ """Contains the LearnAttack class""" import random + import scrap_engine as se import pokete_data as p_data from util import liner -from .hotkeys import Action, get_action -from .loops import std_loop, easy_exit_loop -from .input import ask_bool, ask_ok -from .ui_elements import ChooseBox, Box -from . import detail +from .input import Action, get_action +from .ui import ask_bool, ask_ok +from .ui.elements import ChooseBox, Box from .attack import Attack +from . import detail, loops class AttackInfo(Box): @@ -136,10 +136,10 @@ def __call__(self, attack=None): with AttackInfo( new_attack, self.map, self.box ) as box: - easy_exit_loop(box=box) + loops.easy_exit(box=box) elif action.triggers(Action.CANCEL): return False - std_loop(box=self.box) + loops.std(box=self.box) self.box.remove_c_obs() return True return False diff --git a/pokete_classes/loops.py b/pokete_classes/loops.py index 7276022b..714627c5 100644 --- a/pokete_classes/loops.py +++ b/pokete_classes/loops.py @@ -1,24 +1,29 @@ """Standardized loops components""" import time -from pokete_classes.hotkeys import Action, get_action + import release -from .notify import notifier +from .ui import notifier +from .input import Action, get_action from .tss import tss -def easy_exit_loop(on_mvmp=True, box=None): +def easy_exit(on_mvmp=True, box=None, pevm=None): """Loops until Cancel or Accept is given ARGS: on_mvmp: Indicates if the loop is executed on movemap + pevm: The PeriodicEventManager object, that may be needed to trigger + periodic events in the overlaing loop box: The box this is called for""" while True: if get_action().triggers(*(Action.CANCEL, Action.ACCEPT)): return - std_loop(on_mvmp, box=box) + std(on_mvmp, box=box, pevm=pevm) + if pevm: + box.map.full_show() -def std_loop(on_mvmp=True, pevm=None, box=None): +def std(on_mvmp=True, pevm=None, box=None): """Standard action executed in most loops ARGS: on_mvmp: Indicates if the loop is executed on movemap diff --git a/pokete_classes/menu.py b/pokete_classes/menu.py new file mode 100644 index 00000000..bd662538 --- /dev/null +++ b/pokete_classes/menu.py @@ -0,0 +1,138 @@ +import sys +import time + +import scrap_engine as se +from release import SPEED_OF_TIME +from . import loops +from .achievements import AchievementOverview +from .audio import audio +from .context import Context +from .input import get_action, Action, text_input, _ev +from .mods import ModInfo +from .save import save +from .settings import VisSetting, Slider, settings +from .side_loops import About +from .ui import notifier, Overview +from .ui.elements import ChooseBox, InfoBox + + +class Menu(Overview): + """Menu to manage settings and other stuff in""" + + def __init__(self): + self.map = None + self.box = ChooseBox(50, 35, "Menu") + self.playername_label = se.Text("Playername: ", state="float") + self.represent_char_label = se.Text("Char: ", state="float") + self.mods_label = se.Text("Mods", state="float") + self.ach_label = se.Text("Achievements", state="float") + self.about_label = se.Text("About", state="float") + self.save_label = se.Text("Save", state="float") + self.exit_label = se.Text("Exit", state="float") + self.realname_label = se.Text("", state="float") + self.char_label = se.Text("", state="float") + self.box.add_c_obs([self.playername_label, + self.represent_char_label, + VisSetting("Autosave", "autosave", + {True: "On", False: "Off"}), + VisSetting("Animations", "animations", + {True: "On", False: "Off"}), + VisSetting("Save trainers", "save_trainers", + {True: "On", False: "Off"}), + VisSetting("Audio", "audio", + {True: "On", False: "Off"}), + Slider("Volume", "volume"), + VisSetting("Load mods", "load_mods", + {True: "On", False: "Off"}), + self.mods_label, self.ach_label, + self.about_label, self.save_label, + self.exit_label]) + # adding + self.box.add_ob(self.realname_label, + self.playername_label.rx + self.playername_label.width, + self.playername_label.ry) + self.box.add_ob(self.char_label, + self.represent_char_label.rx + + self.represent_char_label.width, + self.represent_char_label.ry) + + def resize_view(self): + """Manages recursive view resizing""" + self.box.remove() + self.box.overview.resize_view() + self.box.resize(self.map.height - 3, 35) + self.box.add(self.map, self.map.width - self.box.width, 0) + + def __call__(self, ctx: Context): + """Opens the menu""" + self.map = ctx.map + figure = ctx.figure + self.box.overview = ctx.overview + + self.box.resize(self.map.height - 3, 35) + self.realname_label.rechar(figure.name) + self.char_label.rechar(figure.char) + audio_before = settings("audio").val + volume_before = settings("volume").val + with self.box.add(self.map, self.map.width - self.box.width, 0): + _ev.clear() + while True: + action = get_action() + i = self.box.c_obs[self.box.index.index] + if (strength := action.get_x_strength()) != 0: + if isinstance(i, Slider): + i.change(strength) + elif action.triggers(Action.ACCEPT): + # Fuck python for not having case statements - lxgr + # but it does lmao - Magnus + if i == self.playername_label: + figure.name = text_input(self.realname_label, self.map, + figure.name, 18, 17) + self.map.name_label_rechar(figure.name) + elif i == self.represent_char_label: + inp = text_input(self.char_label, self.map, + figure.char, 18, 1) + # excludes bad unicode: + if ( + len(inp.encode("utf-8")) != 1 + and inp not in ["ä", "ö", "ü", "ß"] + ): + inp = "a" + self.char_label.rechar(inp) + notifier.notify("Error", "Bad character", + "The chosen character has to be a \ +valid single-space character!") + figure.rechar(inp) + elif i == self.mods_label: + ModInfo()(ctx.with_overview(self)) + elif i == self.save_label: + # When will python3.10 come out? + with InfoBox("Saving....", info="", _map=self.map): + # Shows a box displaying "Saving...." while saving + save(figure) + time.sleep(SPEED_OF_TIME * 1.5) + elif i == self.exit_label: + save(figure) + sys.exit() + elif i == self.about_label: + About()(ctx.with_overview(self)) + elif i == self.ach_label: + AchievementOverview()(ctx.with_overview(self)) + elif isinstance(i, VisSetting): + i.change() + if ( + audio_before != settings("audio").val + or volume_before != settings("volume").val + ): + audio.switch(figure.map.song) + audio_before = settings("audio").val + volume_before = settings("volume").val + elif action.triggers(Action.UP, Action.DOWN): + self.box.input(action) + elif action.triggers(Action.CANCEL, Action.MENU): + break + loops.std(pevm=ctx.pevm, box=self) + self.map.full_show() + + +menu = Menu() diff --git a/pokete_classes/mods.py b/pokete_classes/mods.py index 4fcbdf4f..c4332553 100644 --- a/pokete_classes/mods.py +++ b/pokete_classes/mods.py @@ -1,9 +1,11 @@ """This file contains all classes related to mods""" +import logging +import sys +import pokete_data as p_data from .side_loops import LoopBox -from .ui_elements import InfoBox +from .ui.elements import InfoBox from .settings import settings -from . import movemap as mvp class DummyMods: @@ -29,26 +31,44 @@ def __init__(self, name, err): class ModInfo(LoopBox): - """Gives information about mods - ARGS: - _map: The se.Map the info is shown on - mod_info: mod_info dict""" + """Gives information about mods""" - def __init__(self, _map, mod_info): + def __init__(self): self.text = f""" -Mods are { {True: 'enabled', False: 'disabled'}[settings("load_mods").val] }! +Mods are { {True: 'enabled', False: 'disabled'}[settings("load_mods").val]}! To load a mod, it has to be placed in '/mods', and mods have to be enabled in the menu. -Currently {len(mod_info)} mod{"s are" if len(mod_info) != 1 else " is"} loaded: - """ + "\n ".join(f"{i}-{mod_info[i]}" for i in mod_info) + "\n" +Currently {len(loaded_mods.mod_info)} mod{"s are" if len(loaded_mods.mod_info) != 1 else " is"} loaded: + """ + "\n ".join(f"{i}-{loaded_mods.mod_info[i]}" for i in + loaded_mods.mod_info) + "\n" super().__init__( InfoBox( - self.text, name="Mods", _map=_map, - overview=mvp.movemap.menu + self.text, name="Mods", ) ) +def try_load_mods(_map): + global loaded_mods + if settings("load_mods").val: + try: + import mods + except ModError as mod_err: + error_box = InfoBox(str(mod_err), "Mod-loading Error") + error_box.center_add(_map) + _map.show() + sys.exit(1) + + for mod in mods.mod_obs: + mod.mod_p_data(p_data) + else: + loaded_mods = DummyMods() + logging.info("[General] %d mods are loaded: (%s)", + len(loaded_mods.mod_obs), ', '.join(loaded_mods.mod_names)) + + +loaded_mods = DummyMods() + if __name__ == "__main__": print("\033[31;1mDo not execute this!\033[0m") diff --git a/pokete_classes/movemap.py b/pokete_classes/movemap.py index 0a099140..833997da 100644 --- a/pokete_classes/movemap.py +++ b/pokete_classes/movemap.py @@ -2,30 +2,26 @@ import time import scrap_engine as se + from util import liner -import pokete_classes.ob_maps as obmp -import pokete_classes.game_map as gm from release import SPEED_OF_TIME -from .loops import std_loop from .classes import OutP from .color import Color -from .event import _ev -from .hotkeys import Action -from .notify import notifier +from .input import _ev, Action +from .ui import notifier, Overview from .tss import tss +from . import loops, ob_maps as obmp, game_map as gm -class Movemap(gm.GameSubmap): +class Movemap(gm.GameSubmap, Overview): """Movemap class to remove bad code ARGS: height: Height of the map - width: Width of the map - menu_cls: The class Menu""" + width: Width of the map""" - def __init__(self, height, width, menu_cls): + def __init__(self, height, width): super().__init__(obmp.ob_maps["playmap_1"], 0, 0, height=height, width=width, name="movemap") - self.menu = menu_cls(self) self.name_label = se.Text("") self.balls_label = se.Text("") self.label_bg = se.Square(" ", self.width, 1, state="float") @@ -92,7 +88,7 @@ def text(self, _x, _y, inp_arr): " " ) ) - std_loop(box=self) + loops.std(box=self) if _ev.get() != "": _ev.clear() break @@ -104,7 +100,7 @@ def text(self, _x, _y, inp_arr): ) ) while _ev.get() == "": - std_loop(box=self) + loops.std(box=self) self.show() self.multitext.remove() _ev.clear() @@ -135,9 +131,10 @@ def balls_label_rechar(self, pokes): ARGS: pokes: The player's Pokes""" self.balls_label.rechar("".join("-" if i >= len(pokes) - or pokes[i].identifier == "__fallback__" + or pokes[ + i].identifier == "__fallback__" else "o" if pokes[i].hp > 0 - else "x" + else "x" for i in range(6)), esccode=Color.thicc) def name_label_rechar(self, name): diff --git a/pokete_classes/nature.py b/pokete_classes/nature.py index 83438068..a305bfa2 100644 --- a/pokete_classes/nature.py +++ b/pokete_classes/nature.py @@ -5,10 +5,10 @@ import scrap_engine as se import pokete_data as p_data from util import liner -from .hotkeys import Action -from .ui_elements import LabelBox +from .input import Action +from .ui.elements import LabelBox from .color import Color -from .loops import easy_exit_loop +from . import loops class Nature: @@ -51,7 +51,7 @@ def get_value(self, name): """Gets one attribute value by its name ARGS: name: The name of the attribute""" - return getattr(self.nature, name)**self.grade + return getattr(self.nature, name) ** self.grade def dict(self): """RETURNS: @@ -92,9 +92,9 @@ def __init__(self, p_n): defense = self.get_amount(p_n.nature.defense) init = self.get_amount(p_n.nature.initiative) text = se.Text(f"Nature: {'very ' if p_n.grade == 2 else ''}") \ - + se.Text(p_n.nature.name, esccode=Color.thicc - + p_n.nature.esccode) \ - + se.Text(liner(f"\n\n That means it has {atc} attack, \ + + se.Text(p_n.nature.name, esccode=Color.thicc + + p_n.nature.esccode) \ + + se.Text(liner(f"\n\n That means it has {atc} attack, \ {defense} defense and {init} initiative points compared to normal Poketes \ of its kind.", 40, pre="")) super().__init__( @@ -116,4 +116,4 @@ def __call__(self, _map, overview): _map: Map to show on""" self.overview = overview with self.center_add(_map): - easy_exit_loop(False, box=self) + loops.easy_exit(False, box=self) diff --git a/pokete_classes/npcs.py b/pokete_classes/npcs.py index 93c50d1a..88647f4d 100644 --- a/pokete_classes/npcs.py +++ b/pokete_classes/npcs.py @@ -6,15 +6,14 @@ import scrap_engine as se import pokete_classes.fightmap as fm from release import SPEED_OF_TIME -from pokete_classes.hotkeys import ACTION_UP_DOWN, Action, get_action -from . import movemap as mvp +from .input import ACTION_UP_DOWN, Action, get_action from .providers import Provider -from .loops import std_loop -from .input import ask_bool -from .inv_items import invitems +from .ui import ask_bool +from .inv import invitems from .settings import settings -from .ui_elements import ChooseBox +from .ui.elements import ChooseBox from .general import check_walk_back +from . import movemap as mvp, loops class NPCTrigger(se.Object): @@ -170,7 +169,7 @@ def chat(self): while True: self.text(q_a["q"]) while get_action() is None: - std_loop(mvp.movemap) + loops.std(mvp.movemap) mvp.movemap.show() if q_a["a"] == {}: break @@ -196,7 +195,7 @@ def chat(self): elif action.triggers(Action.ACCEPT): key = keys[c_b.index.index] break - std_loop(box=c_b) + loops.std(box=c_b) mvp.movemap.show() q_a = q_a["a"][key] @@ -264,8 +263,8 @@ def action(self): return self.pokes = [p for p in self.pokes if p.hp > 0] if self.pokes and (not self.used - or not settings("save_trainers").val) \ - and self.check_walk(self.fig.x, self.fig.y): + or not settings("save_trainers").val) \ + and self.check_walk(self.fig.x, self.fig.y): mvp.movemap.full_show() time.sleep(SPEED_OF_TIME * 0.7) self.exclamate() diff --git a/pokete_classes/periodic_event_manager.py b/pokete_classes/periodic_event_manager.py deleted file mode 100644 index 786ae093..00000000 --- a/pokete_classes/periodic_event_manager.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Contains the pevm""" - -from pokete_classes import timer -from .settings import settings -from .landscape import Meadow -from .npcs import NPC - - -class PeriodicEventManager: - """As the name states: It manages periodic events in the game loop - ARGS: - _map: The PlayMap the game is currently taking place on""" - - def __init__(self, _map): - self.map = _map - self.all_grass_objs = [] - self.all_water_objs = [] - - for meadow in Meadow.all_grass: - if meadow.map == _map: - self.all_grass_objs += meadow.obs - for water in Meadow.all_water: - if water.map == _map: - self.all_water_objs += water.obs - - def event(self): - """Executes the events""" - self.map.extra_actions() - if settings("animations").val: - Meadow.moving_grass(self.all_grass_objs) - Meadow.moving_water(self.all_water_objs) - # Reset treat NPC after 6 minutes - if timer.time.normalized == 6*60: - NPC.get("npc_28").unset_used() diff --git a/pokete_classes/periodic_events.py b/pokete_classes/periodic_events.py new file mode 100644 index 00000000..4dafd717 --- /dev/null +++ b/pokete_classes/periodic_events.py @@ -0,0 +1,63 @@ +import random + +from . import timer +from .color import Color +from .landscape import Meadow, HighGrass +from .npcs import NPC +from .game import PeriodicEvent +from .settings import settings + + +def check_figure_redraw(obj): + """Checks whether or not the figure has to be redrawn + ARGS: + obj: The obj that this is checked for""" + if obj.x == HighGrass.figure.x and obj.y == HighGrass.figure.y: + HighGrass.figure.redraw() + + +class MovingGrassEvent(PeriodicEvent): + max_tick = 100 + + def __init__(self, _map): + self.all_grass_objs = [] + for meadow in Meadow.all_grass: + if meadow.map == _map: + self.all_grass_objs += meadow.obs + + def tick(self, tick: int): + if tick % self.max_tick == 0 and settings("animations").val: + for obj in self.all_grass_objs: + if obj.char == Color.green + ";" + Color.reset: + if random.randint(0, 600) == 0: + obj.rechar( + Color.thicc + Color.green + ";" + Color.reset) + check_figure_redraw(obj) + else: + obj.rechar(Color.green + ";" + Color.reset) + check_figure_redraw(obj) + + +class MovingWaterEvent(PeriodicEvent): + def __init__(self, _map): + self.all_water_objs = [] + for water in Meadow.all_water: + if water.map == _map: + self.all_water_objs += water.obs + + def tick(self, tick: int): + if settings("animations").val: + for obj in self.all_water_objs: + if random.randint(0, 9) == 0: + if " " not in obj.char: + obj.rechar([i for i in + [Color.lightblue + "~" + Color.reset, + Color.blue + "~" + Color.reset] + if i != obj.char][0]) + check_figure_redraw(obj) + + +class TreatNPCEvent(PeriodicEvent): + def tick(self, tick: int): + if timer.time.normalized == 6 * 60: + NPC.get("npc_28").unset_used() diff --git a/pokete_classes/poke.py b/pokete_classes/poke.py index a3cc7219..468af392 100644 --- a/pokete_classes/poke.py +++ b/pokete_classes/poke.py @@ -21,7 +21,7 @@ from .learnattack import LearnAttack from .nature import PokeNature from .achievements import achievements -from .loops import std_loop +from . import loops class Poke: @@ -38,7 +38,7 @@ def __init__(self, poke, _xp, _hp="SKIP", _ap=None, _attacks=None, _effects=None, player=True, shiny=False, nature=None, stats=None): self.nature = PokeNature.random() if nature is None \ - else PokeNature.from_dict(nature) + else PokeNature.from_dict(nature) self.inf = p_data.pokes[poke] self.moves = Moves(self) # Attributes @@ -137,7 +137,9 @@ def set_vars(self): """Updates/sets some vars""" for name in ["atc", "defense", "initiative"]: setattr(self, name, round((self.lvl() + self.inf[name] - + (2 if self.shiny else 0)) * self.nature.get_value(name))) + + ( + 2 if self.shiny else 0)) * self.nature.get_value( + name))) for atc in self.attack_obs: atc.set_ap(atc.max_ap) @@ -171,7 +173,8 @@ def add_xp(self, _xp): self.text_xp.rechar(f"XP:{self.xp - (self.lvl() ** 2 - 1)}/\ {((self.lvl() + 1) ** 2 - 1) - (self.lvl() ** 2 - 1)}") self.text_lvl.rechar(f"Lvl:{self.lvl()}") - logging.info("[Poke][%s] Gained %dxp (curr:%d)", self.name, _xp, self.xp) + logging.info("[Poke][%s] Gained %dxp (curr:%d)", self.name, _xp, + self.xp) if old_lvl < self.lvl(): logging.info("[Poke][%s] Reached lvl. %d", self.name, self.lvl()) return True @@ -211,7 +214,7 @@ def attack(self, attack, enem, fightmap, providers): enem.oldhp = enem.hp self.oldhp = self.hp eff = (1.3 if enem.type.name in attack.type.effective else 0.5 - if enem.type.name in attack.type.ineffective else 1) * w_eff + if enem.type.name in attack.type.ineffective else 1) * w_eff n_hp = round((self.atc * attack.factor / (enem.defense if enem.defense >= 1 else 1)) @@ -256,7 +259,7 @@ def evolve(self, figure, _map): figure: The figure object the poke belongs to _map: The map the evolving happens on""" if not self.player or self.evolve_poke == "" \ - or self.lvl() < self.evolve_lvl: + or self.lvl() < self.evolve_lvl: return False evomap = EvoMap(_map.height, _map.width, _map) new = Poke(self.evolve_poke, self.xp, _attacks=self.attacks, @@ -278,7 +281,7 @@ def evolve(self, figure, _map): round((evomap.height - 8) / 2)) time.sleep(SPEED_OF_TIME * 0.7 - i * 0.09999) evomap.show() - std_loop(box=evomap) + loops.std(box=evomap) self.ico.remove() new.ico.add(evomap, round(evomap.width / 2 - 4), round((evomap.height - 8) / 2)) @@ -295,7 +298,7 @@ def evolve(self, figure, _map): figure.caught_pokes.append(new.identifier) achievements.achieve("first_evolve") logging.info("[Poke] %s evolved into %s", self.name, new.name) - std_loop(box=evomap) + loops.std(box=evomap) del self return True @@ -322,7 +325,7 @@ def wild(cls, poke, _xp): obj.attacks.append(new_attack) while len(obj.attacks) > 4: - obj.attacks.pop(random.randint(0, len(obj.attacks)-1)) + obj.attacks.pop(random.randint(0, len(obj.attacks) - 1)) return cls( poke, @@ -339,7 +342,7 @@ def upgrade_by_one_lvl(poke, figure, _map): poke: The pokete, that will be upgraded figure: The figure object the Pokete belongs to _map: The map the upgrade happens on""" - poke.add_xp((poke.lvl()+1)**2-1 - ((poke.lvl())**2-1)) + poke.add_xp((poke.lvl() + 1) ** 2 - 1 - ((poke.lvl()) ** 2 - 1)) poke.set_vars() poke.learn_attack(_map, _map) poke.evolve(figure, _map) diff --git a/pokete_classes/pokestats.py b/pokete_classes/pokestats.py index 1021a612..83d97aae 100644 --- a/pokete_classes/pokestats.py +++ b/pokete_classes/pokestats.py @@ -3,10 +3,9 @@ from datetime import datetime import scrap_engine as se - -from pokete_classes.hotkeys import Action -from pokete_classes.loops import easy_exit_loop -from pokete_classes.ui_elements import LabelBox +from .input import Action +from .ui.elements import LabelBox +from . import loops class PokeStats: @@ -135,4 +134,4 @@ def __call__(self, _map): ARGS: _map: Map to show on""" with self.center_add(_map): - easy_exit_loop(False, box=self) + loops.easy_exit(False, box=self) diff --git a/pokete_classes/pokete_care.py b/pokete_classes/pokete_care.py index 4aa4d287..b4e58896 100644 --- a/pokete_classes/pokete_care.py +++ b/pokete_classes/pokete_care.py @@ -25,13 +25,11 @@ def __init__(self, entry=0, poke=None): self.entry = entry self.poke = poke - @classmethod - def from_dict(cls, _dict): + def from_dict(self, _dict): """Assembles a PoketeCare from _dict""" - entry = _dict.get("entry", 0) - poke = None if _dict.get("poke") is None else \ + self.entry = _dict.get("entry", 0) + self.poke = None if _dict.get("poke") is None else \ Poke.from_dict(_dict["poke"]) - return cls(entry, poke) def dict(self): """Returns a dict from the object""" @@ -39,3 +37,6 @@ def dict(self): "entry": self.entry, "poke": None if self.poke is None else self.poke.dict(), } + + +pokete_care = PoketeCare() diff --git a/pokete_classes/providers.py b/pokete_classes/providers.py index 83019474..35359545 100644 --- a/pokete_classes/providers.py +++ b/pokete_classes/providers.py @@ -4,7 +4,7 @@ import time from abc import ABC, abstractmethod from pokete_classes import movemap as mvp -from .input import ask_bool +from .ui import ask_bool class Provider(ABC): @@ -69,6 +69,7 @@ class NatureProvider(Provider): """The Natures Provider ARGS: poke: One Pokete""" + def __init__(self, poke): super().__init__([poke], escapable=True, xp_multiplier=1) @@ -126,8 +127,8 @@ def handle_defeat(self, fightmap, winner): bool: whether or not a Pokete was choosen""" if winner.escapable: if ask_bool( - fightmap, "Do you want to choose another Pokete?", - fightmap + fightmap, "Do you want to choose another Pokete?", + fightmap ): success = fightmap.choose_poke(self) if not success: diff --git a/pokete_classes/roadmap.py b/pokete_classes/roadmap.py index 19f1ff58..d7b1f50a 100644 --- a/pokete_classes/roadmap.py +++ b/pokete_classes/roadmap.py @@ -1,14 +1,16 @@ """Contains all classes relevant to show the roadmap""" -import scrap_engine as se +import logging +import scrap_engine as se import pokete_data as p_data import pokete_classes.ob_maps as obmp +from pokete_classes.context import Context +from pokete_classes.game import PeriodicEvent from util import liner -from .hotkeys import ACTION_DIRECTIONS, Action, ActionList, get_action -from .loops import std_loop, easy_exit_loop +from .input import ACTION_DIRECTIONS, Action, ActionList, get_action from .color import Color -from .ui_elements import Box, InfoBox -from . import movemap as mvp +from .ui.elements import Box, InfoBox +from . import loops class RoadMapException(Exception): @@ -71,11 +73,11 @@ def __init__( self.d_next = d_next Station.obs.append(self) - def choose(self): + def choose(self, figure): """Chooses and hightlights the station""" Station.choosen = self self.roadmap.rechar_info( - self.name if self.has_been_visited() else "???") + self.name if self.has_been_visited(figure) else "???") def unchoose(self): """Unchooses the station""" @@ -87,7 +89,7 @@ def blink(self): def un_blink(self): self.rechar(self.text, self.color) - def next(self, inp: ActionList): + def next(self, inp: ActionList, figure): """Chooses the next station in a certain direction ARGS: inp: Action Enum""" @@ -103,20 +105,20 @@ def next(self, inp: ActionList): }[inp] if (n_e := getattr(self, inp + "_next")) != "": self.unchoose() - getattr(self.roadmap, n_e).choose() + getattr(self.roadmap, n_e).choose(figure) - def has_been_visited(self): + def has_been_visited(self, figure): """Returns if the stations map has been visited before""" - return self.associates[0].name in self.roadmap.fig.visited_maps + return self.associates[0].name in figure.visited_maps def is_city(self): """Returns if the station is a city""" return "pokecenter" in p_data.map_data[self.associates[0].name][ "hard_obs"] - def hide_if_visited(self, choose=False): + def hide_if_visited(self, figure, choose=False): self.text = self.base_text - if not self.has_been_visited(): + if not self.has_been_visited(figure): self.color = Color.white for ch in ["A", "P", "$", "C", "#"]: self.text = self.text.replace(ch, " ") @@ -131,15 +133,11 @@ def hide_if_visited(self, choose=False): class RoadMap: - """Map you can see and navigate maps on - ARGS: - fig: Figure object""" + """Map you can see and navigate maps on""" - def __init__(self, fig): - self.fig = fig + def __init__(self): self.box = Box( 17, 61, "Roadmap", f"{Action.CANCEL.mapping}:close", - overview=mvp.movemap ) self.rose = se.Text(""" N ▲ @@ -167,7 +165,7 @@ def __init__(self, fig): setattr(self, sta, obj) @property - def sta(self): + def sta(self) -> Station: """Gives choosen station""" return Station.choosen @@ -180,43 +178,44 @@ def rechar_info(self, name): self.info_label.rechar(name) self.box.add_ob(self.info_label, self.box.width - 2 - len(name), 0) - def __call__(self, _map: se.Submap, pevm, choose=False): + def __call__(self, ctx: Context, choose=False): """Shows the roadmap ARGS: - _map: se.Map this is shown on choose: Bool whether or not this is done to choose a city""" for i in Station.obs: - i.hide_if_visited(choose) + i.hide_if_visited(ctx.figure, choose) [ i for i in Station.obs if ( - self.fig.map - if self.fig.map + ctx.figure.map + if ctx.figure.map not in [obmp.ob_maps[i] for i in ("shopmap", "centermap")] - else self.fig.oldmap + else ctx.figure.oldmap ) in i.associates - ][0].choose() - blinker = Blinker() - with self.box.center_add(_map): + ][0].choose(ctx.figure) + self.box.overview = ctx.overview + blinker = BlinkerEvent(self.sta) + pevm = ctx.pevm.with_events([blinker]) + with self.box.center_add(ctx.map): while True: action = get_action() if action.triggers(*ACTION_DIRECTIONS): - self.sta.next(action) + self.sta.next(action, ctx.figure) elif action.triggers(Action.MAP, Action.CANCEL): break elif ( action.triggers(Action.ACCEPT) and choose - and self.sta.has_been_visited() + and self.sta.has_been_visited(ctx.figure) and self.sta.is_city() ): return self.sta.associates[0] elif ( action.triggers(Action.ACCEPT) and not choose - and self.sta.has_been_visited() + and self.sta.has_been_visited(ctx.figure) ): p_list = ", ".join( set( @@ -234,13 +233,16 @@ def __call__(self, _map: se.Submap, pevm, choose=False): 30, ), self.sta.name, - _map=_map, + _map=ctx.map, overview=self.box, ) as box: - easy_exit_loop(box=box) - std_loop(box=self.box, pevm=pevm) - blinker(self.sta) - _map.full_show() + loops.easy_exit(box=box, pevm=ctx.pevm) + blinker.station = self.sta + loops.std( + box=self.box, + pevm=pevm + ) + ctx.map.full_show() self.sta.unchoose() @staticmethod @@ -256,17 +258,19 @@ def check_maps(): raise RoadMapException(_map) -class Blinker: - def __init__(self): - self.idx = 0 - - def __call__(self, station: Station): - self.idx += 1 - if self.idx == 10: - station.blink() - if self.idx == 20: - station.un_blink() - self.idx = 0 +class BlinkerEvent(PeriodicEvent): + def __init__(self, station: Station): + self.station = station + self.blink = False + + def tick(self, tick: int): + logging.info("yws" + str(self.blink)) + if tick % 10 == 0: + if self.blink: + self.station.blink() + else: + self.station.un_blink() + self.blink = not self.blink if __name__ == "__main__": diff --git a/pokete_classes/save.py b/pokete_classes/save.py new file mode 100644 index 00000000..1efda0dd --- /dev/null +++ b/pokete_classes/save.py @@ -0,0 +1,97 @@ +import json +import logging +import os +from pathlib import Path + +import release +from . import timer +from .achievements import achievements +from .input import hotkeys_save +from .pokete_care import pokete_care +from .settings import settings + +HOME = Path.home() + + +def save(figure): + """Saves all relevant data to savefile""" + _si = { + "user": figure.name, + "represent_char": figure.char, + "ver": release.VERSION, + "map": figure.map.name, + "oldmap": figure.oldmap.name, + "last_center_map": figure.last_center_map.name, + "x": figure.x, + "y": figure.y, + "achievements": achievements.achieved, + "pokes": {i: poke.dict() for i, poke in enumerate(figure.pokes)}, + "inv": figure.inv, + "money": figure.get_money(), + "settings": settings.to_dict(), + "caught_poketes": list(dict.fromkeys(figure.caught_pokes + + [i.identifier + for i in figure.pokes])), + "visited_maps": figure.visited_maps, + "hotkeys": hotkeys_save(), + # filters doublicates from figure.used_npcs + "used_npcs": list(dict.fromkeys(figure.used_npcs)), + "pokete_care": pokete_care.dict(), + "time": timer.time.time, + } + with open(release.SAVEPATH / "pokete.json", "w+") as file: + # writes the data to the save file in a nice format + json.dump(_si, file, indent=4) + logging.info("[General] Saved") + + +def read_save(): + """Reads from savefile + RETURNS: + session_info dict""" + Path(release.SAVEPATH).mkdir(parents=True, exist_ok=True) + # Default test session_info + _si = { + "user": "DEFAULT", + "represent_char": "a", + "ver": release.VERSION, + "map": "intromap", + "oldmap": "playmap_1", + "last_center_map": "playmap_1", + "x": 4, + "y": 5, + "achievements": [], + "pokes": { + "0": {"name": "steini", "xp": 50, "hp": "SKIP", + "ap": ["SKIP", "SKIP"]} + }, + "inv": {"poketeball": 15, "healing_potion": 1}, + "settings": { + "load_mods": False}, + "figure.caught_pokes": ["steini"], + "visited_maps": ["playmap_1"], + "used_npcs": [], + "hotkeys": {}, + "pokete_care": { + "entry": 0, + "poke": None, + }, + "time": 0 + } + + save_file = release.SAVEPATH / "pokete.json" + old_save_file = HOME / ".cache" / "pokete" / "pokete.json" + ancient_save_file = HOME / ".cache" / "pokete" / "pokete.py" + + if os.path.exists(save_file): + with open(save_file) as _file: + _si = json.load(_file) + elif os.path.exists(old_save_file): + with open(old_save_file) as _file: + _si = json.load(_file) + elif os.path.exists(ancient_save_file): + l_dict = {} + with open(ancient_save_file, "r") as _file: + exec(_file.read(), {"session_info": _si}, l_dict) + _si = json.loads(json.dumps(l_dict["session_info"])) + return _si diff --git a/pokete_classes/side_loops.py b/pokete_classes/side_loops.py index 05fcd829..81cd4480 100644 --- a/pokete_classes/side_loops.py +++ b/pokete_classes/side_loops.py @@ -3,38 +3,37 @@ import os import scrap_engine as se import pokete_classes.game_map as gm +import release +from pokete_classes.context import Context from util import liner -from .loops import easy_exit_loop -from .ui_elements import InfoBox -from . import movemap as mvp +from .ui.elements import InfoBox +from . import loops class LoopBox: - """Provides an easy_exit_loop call function + """Provides an loops.easy_exit call function ARGS: box: The box to display""" def __init__(self, box): self.box = box - def __call__(self): + def __call__(self, ctx: Context): """Shows the about text""" + self.box.map = ctx.map + self.box.overview = ctx.overview with self.box: - easy_exit_loop(box=self.box) + loops.easy_exit(box=self.box, pevm=ctx.pevm) class About(LoopBox): - """The about text, that can be triggered in the menu - ARGS: - ver: Version - cname: Codename - _map: se.Map this will be displayed on""" + """The about text, that can be triggered in the menu""" - def __init__(self, ver, cname, _map): + def __init__(self): super().__init__( InfoBox( liner( - f"""Pokete v{ver} -- {cname} + f"""Pokete v{release.VERSION} -- {release.CODENAME} by lxgr-linux This software is licensed under the GPL3, you should have gotten a \ @@ -46,18 +45,14 @@ def __init__(self, ver, cname, _map): 60, pre="" ), name="About", - _map=_map, - overview=mvp.movemap.menu ) ) class Help(LoopBox): - """Helptext that can be displayed by pressing '?' - ARGS: - _map: se.Map this will be displayed on""" + """Helptext that can be displayed by pressing '?'""" - def __init__(self, _map): + def __init__(self): super().__init__( InfoBox( """Controls: @@ -71,19 +66,14 @@ def __init__(self, _map): For more information about how to play this game, check out https://git.io/JRRqe""", name="Help", - _map=_map, - overview=mvp.movemap ) ) class LoadingScreen: - """Loading screen that's shown at game's start - ARGS: - ver: Version - codename: Codename""" + """Loading screen that's shown at game's start""" - def __init__(self, ver, codename): + def __init__(self): width, height = os.get_terminal_size() self.map = gm.GameMap(width=width, height=height - 1) se.Text(r""" _____ _ _ @@ -91,21 +81,23 @@ def __init__(self, ver, codename): | |__) |__ | | _____| |_ ___ | ___/ _ \| |/ / _ \ __/ _ \ | | | (_) | < __/ || __/ -|_| \___/|_|\_\___|\__\___|""", state="float")\ +|_| \___/|_|\_\___|\__\___|""", state="float") \ .add(self.map, int(self.map.width / 2) - 15, int(self.map.height / 2) - 4) - se.Text(f"v{ver}", state="float").add(self.map, - int(self.map.width / 2) - 15, - int(self.map.height / 2) + 2) - se.Text(codename, state="float").add(self.map, - int(self.map.width / 2) + 14 - - len(codename), - int(self.map.height / 2) + 2) + se.Text(f"v{release.VERSION}", state="float").add(self.map, + int(self.map.width / 2) - 15, + int(self.map.height / 2) + 2) + se.Text(release.CODENAME, state="float").add(self.map, + int(self.map.width / 2) + 14 + - len(release.CODENAME), + int(self.map.height / 2) + 2) def __call__(self): """Shows the loading screen""" self.map.show() +loading_screen = LoadingScreen() + if __name__ == "__main__": print("\033[31;1mDo not execute this!\033[0m") diff --git a/pokete_classes/timer.py b/pokete_classes/timer.py index d53ef471..66e9682a 100644 --- a/pokete_classes/timer.py +++ b/pokete_classes/timer.py @@ -4,9 +4,10 @@ import time as time_mod import scrap_engine as se from release import SPEED_OF_TIME -from .hotkeys import Action, get_action -from .ui_elements import Box -from .loops import std_loop +from .context import Context +from .input import Action, get_action +from .ui.elements import Box +from . import loops time = None clock = None @@ -99,24 +100,22 @@ def normalized(self): class Clock(Box): """Clock class to display the current time ARGS: - time_ob: Time object - overview: The overview this happens on""" + time_ob: Time object""" - def __init__(self, time_ob, overview): + def __init__(self, time_ob): self.time = time_ob super().__init__( 9, 28, "Clock", f"{Action.CANCEL.mapping}:close", - overview ) - def __call__(self, _map): - """Shows the clock - ARGS: - _map: The map to show on""" + def __call__(self, ctx: Context): + """Shows the clock""" + d_p = True letter_obs = self.draw_letters(d_p) raw_time = self.time.time - with self.center_add(_map): + self.overview = ctx.overview + with self.center_add(ctx.map): while True: if get_action().triggers(*(Action.CANCEL, Action.CLOCK)): break @@ -124,8 +123,8 @@ def __call__(self, _map): d_p = not d_p letter_obs = self.draw_letters(d_p, letter_obs) raw_time = self.time.time - self.map.show() - std_loop(box=self) + loops.std(box=self, pevm=ctx.pevm) + self.map.full_show() self.__rem_obs(letter_obs) def __rem_obs(self, letter_obs): diff --git a/pokete_classes/tss.py b/pokete_classes/tss.py index e9cf595c..15001346 100644 --- a/pokete_classes/tss.py +++ b/pokete_classes/tss.py @@ -2,8 +2,8 @@ import os import scrap_engine as se -import pokete_classes.game_map as gm -from .ui_elements import StdFrame +from .ui.elements import StdFrame +from . import game_map as gm class ResizeScreen: diff --git a/pokete_classes/ui/__init__.py b/pokete_classes/ui/__init__.py new file mode 100644 index 00000000..4ffdc3dc --- /dev/null +++ b/pokete_classes/ui/__init__.py @@ -0,0 +1,3 @@ +from .notify import notifier +from .input import ask_text, ask_bool, ask_ok +from .overview import Overview diff --git a/pokete_classes/ui/elements/__init__.py b/pokete_classes/ui/elements/__init__.py new file mode 100644 index 00000000..4edde181 --- /dev/null +++ b/pokete_classes/ui/elements/__init__.py @@ -0,0 +1,4 @@ +from .frame import StdFrame, StdFrame2 +from .box import Box +from .choose import ChooseBox, BetterChooseBox +from .boxes import InfoBox, InputBox, LabelBox diff --git a/pokete_classes/ui/elements/box.py b/pokete_classes/ui/elements/box.py new file mode 100644 index 00000000..1c9ffdb7 --- /dev/null +++ b/pokete_classes/ui/elements/box.py @@ -0,0 +1,69 @@ +import scrap_engine as se + +from .frame import StdFrame +from ..overview import Overview + + +class Box(se.Box, Overview): + """Box to show content in + ARGS: + height: The boxes height + width: The boxes width + name: The boxes displayed name + info: Info that will be displayed in the bottom left corner of the box""" + + def __init__(self, height, width, name="", info="", + overview: Overview | None = None): + super().__init__(height, width) + self.overview = overview + self.frame = StdFrame(height, width) + self.inner = se.Square(char=" ", width=width - 2, height=height - 2, + state="float") + self.name_label = se.Text(name, state="float") + self.info_label = se.Text(info, state="float") + # adding + self.add_ob(self.frame, 0, 0) + self.add_ob(self.inner, 1, 1) + self.add_ob(self.name_label, 2, 0) + self.add_ob(self.info_label, 2, self.height - 1) + + def resize_view(self): + """Manages recursive view resizing""" + if self.overview is not None: + self.remove() + self.overview.resize_view() + self.center_add(self.map) + self.map.show() + + def center_add(self, _map): + """Adds the box to the maps center + ARGS: + _map: se.Map the box will be added to""" + self.add(_map, round((_map.width - self.width) / 2), + round((_map.height - self.height) / 2)) + return self + + def resize(self, height, width): + """Resizes the box to a certain size + See se.Box.resize""" + super().resize(height, width) + self.inner.resize(width - 2, height - 2) + self.frame.resize(height, width) + self.set_ob(self.name_label, 2, 0) + self.set_ob(self.info_label, 2, self.height - 1) + + def add(self, _map, x, y): + """Adds the box to a map + See se.Box.add""" + super().add(_map, x, y) + return self + + def __enter__(self): + """Enter dunder for context management""" + self.map.show() + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + """Exit dunder for context management""" + self.remove() + self.map.show() diff --git a/pokete_classes/ui/elements/boxes.py b/pokete_classes/ui/elements/boxes.py new file mode 100644 index 00000000..96332dd3 --- /dev/null +++ b/pokete_classes/ui/elements/boxes.py @@ -0,0 +1,71 @@ +import scrap_engine as se + +from pokete_classes.input import Action +from .box import Box + + +class LabelBox(Box): + """A Box just containing one label + ARGS: + label: The se.Text label + name: The boxes displayed name + info: Info that will be displayed in the bottom left corner of the box""" + + def __init__(self, label, name="", info="", overview=None): + self.label = label + super().__init__( + label.height + 2, label.width + 4, name, info, + overview=overview + ) + self.add_ob(label, 2, 1) + + +class InfoBox(LabelBox): + """Box to display basic text information in + ARGS: + text: String displayed + name: The boxes displayed name + info: Info that will be displayed in the bottom left corner of the box + _map: The se.Map this will be shown on""" + + def __init__( + self, text, name="", + info=f"{Action.CANCEL.mapping}:close", + _map=None, overview=None + ): + super().__init__(se.Text(text), name=name, info=info, overview=overview) + self.map = _map + + def __enter__(self): # Contextmanagement is fucking awesome! + """Enter dunder for contextmanagement""" + self.center_add(self.map) + self.map.show() + return self + + +class InputBox(InfoBox): + """Box that promps the user to input a text + ARGS: + _map: The map the input box should be shown on + infotext: The information text about the input + introtext: The text that introduces the text field + text: The default text in the text field + name: The boxes desplayed name + max_len: Max length of the text""" + + def __init__( + self, infotext, introtext, text, max_len, + name="", _map=None, overview=None + ): + height = len(infotext.split("\n")) + 3 + width = sorted([len(i) for i in infotext.split("\n")] + + [len(introtext) + 1 + max_len])[-1] + 4 + super(LabelBox, self).__init__(height, width, name, overview=overview) + self.map = _map + self.infotext = se.Text(infotext) + self.introtext = se.Text(introtext) + self.text = se.Text(text) + self.add_ob(self.infotext, 2, 1) + self.add_ob(self.introtext, 2, len(infotext.split("\n")) + 1) + self.add_ob(self.text, self.introtext.rx + len(introtext) + 1, + self.introtext.ry) diff --git a/pokete_classes/ui_elements.py b/pokete_classes/ui/elements/choose.py similarity index 52% rename from pokete_classes/ui_elements.py rename to pokete_classes/ui/elements/choose.py index 8c5c77b8..44aa1acf 100644 --- a/pokete_classes/ui_elements.py +++ b/pokete_classes/ui/elements/choose.py @@ -1,10 +1,8 @@ -"""This file contains most of the user interface -elements used in Pokete""" - -import logging import scrap_engine as se -from .hotkeys import ACTION_DIRECTIONS, ACTION_UP_DOWN, Action, ActionList +from .box import Box +from pokete_classes.input import ACTION_DIRECTIONS, ACTION_UP_DOWN, Action, \ + ActionList class BoxIndex(se.Object): @@ -15,95 +13,6 @@ def __init__(self): self.index = 0 -class StdFrame(se.Frame): - """Standardized frame - ARGS: - height: The frames height - width: The frames width""" - - def __init__(self, height, width): - super().__init__(width=width, height=height, - corner_chars=["┌", "┐", "└", "┘"], - horizontal_chars=["─", "─"], - vertical_chars=["│", "│"], state="float") - - -class StdFrame2(se.Frame): - """Standardized frame - ARGS: - height: The frames height - width: The frames width""" - - def __init__(self, height, width, state="solid"): - super().__init__(width=width, height=height, - corner_chars=["_", "_", "|", "|"], - horizontal_chars=["_", "_"], state=state) - - -class Box(se.Box): - """Box to show content in - ARGS: - height: The boxes height - width: The boxes width - name: The boxes displayed name - info: Info that will be displayed in the bottom left corner of the box""" - - def __init__(self, height, width, name="", info="", overview=None): - super().__init__(height, width) - self.overview = overview - self.frame = StdFrame(height, width) - self.inner = se.Square(char=" ", width=width - 2, height=height - 2, - state="float") - self.name_label = se.Text(name, state="float") - self.info_label = se.Text(info, state="float") - # adding - self.add_ob(self.frame, 0, 0) - self.add_ob(self.inner, 1, 1) - self.add_ob(self.name_label, 2, 0) - self.add_ob(self.info_label, 2, self.height - 1) - - def resize_view(self): - """Manages recursive view resizing""" - if self.overview is not None: - self.remove() - self.overview.resize_view() - self.center_add(self.map) - self.map.show() - - def center_add(self, _map): - """Adds the box to the maps center - ARGS: - _map: se.Map the box will be added to""" - self.add(_map, round((_map.width - self.width) / 2), - round((_map.height - self.height) / 2)) - return self - - def resize(self, height, width): - """Resizes the box to a certain size - See se.Box.resize""" - super().resize(height, width) - self.inner.resize(width - 2, height - 2) - self.frame.resize(height, width) - self.set_ob(self.name_label, 2, 0) - self.set_ob(self.info_label, 2, self.height - 1) - - def add(self, _map, x, y): - """Adds the box to a map - See se.Box.add""" - super().add(_map, x, y) - return self - - def __enter__(self): - """Enter dunder for context management""" - self.map.show() - return self - - def __exit__(self, exc_type, exc_value, exc_tb): - """Exit dunder for context management""" - self.remove() - self.map.show() - - class ChooseBox(Box): """Box that contains items you can choose from ARGS: @@ -115,9 +24,9 @@ class ChooseBox(Box): c_obs: List of se.Texts that can be choosen from""" def __init__( - self, height, width, name="", info="", - index_x=2, c_obs=None, overview=None - ): + self, height, width, name="", info="", + index_x=2, c_obs=None, overview=None + ): super().__init__(height, width, name, info, overview=overview) self.index_x = index_x self.index = BoxIndex() @@ -209,9 +118,9 @@ class BetterChooseBox(Box): _map: The map it will be shown on""" def __init__( - self, columns, labels: [se.Text], - name="", _map=None, overview=None - ): + self, columns, labels: [se.Text], + name="", _map=None, overview=None + ): self.nest_label_obs = [] self.set_items(columns, labels, init=True) super().__init__( @@ -263,10 +172,10 @@ def input(self, inp: ActionList): Action.RIGHT: (0, 1), }[inp] self.set_index((self.index[0] + _c[0]) - % len([i for i in self.nest_label_obs if len(i) > - self.index[1]]), + % len([i for i in self.nest_label_obs if len(i) > + self.index[1]]), (self.index[1] + _c[1]) - % len(self.nest_label_obs[self.index[0]])) + % len(self.nest_label_obs[self.index[0]])) def set_items(self, columns, labels: [se.Text], init=False): """Sets the items shown in the box @@ -284,7 +193,6 @@ def set_items(self, columns, labels: [se.Text], init=False): label_obs[i * columns:(i + 1) * columns] for i in range(max(round(len(labels) / columns + 0.49), 1)) ] - logging.info(self.nest_label_obs) if not init: self.resize(3 * len(self.nest_label_obs) + 2, sum(i.width for i in self.nest_label_obs[0]) + 2) @@ -306,74 +214,3 @@ def __enter__(self): self.center_add(self.map) self.map.show() return self - - -class LabelBox(Box): - """A Box just containing one label - ARGS: - label: The se.Text label - name: The boxes displayed name - info: Info that will be displayed in the bottom left corner of the box""" - - def __init__(self, label, name="", info="", overview=None): - self.label = label - super().__init__( - label.height + 2, label.width + 4, name, info, - overview=overview - ) - self.add_ob(label, 2, 1) - - -class InfoBox(LabelBox): - """Box to display basic text information in - ARGS: - text: String displayed - name: The boxes displayed name - info: Info that will be displayed in the bottom left corner of the box - _map: The se.Map this will be shown on""" - - def __init__( - self, text, name="", - info=f"{Action.CANCEL.mapping}:close", - _map=None, overview=None - ): - super().__init__(se.Text(text), name=name, info=info, overview=overview) - self.map = _map - - def __enter__(self): # Contextmanagement is fucking awesome! - """Enter dunder for contextmanagement""" - self.center_add(self.map) - self.map.show() - return self - - -class InputBox(InfoBox): - """Box that promps the user to input a text - ARGS: - _map: The map the input box should be shown on - infotext: The information text about the input - introtext: The text that introduces the text field - text: The default text in the text field - name: The boxes desplayed name - max_len: Max length of the text""" - - def __init__( - self, infotext, introtext, text, max_len, - name="", _map=None, overview=None - ): - height = len(infotext.split("\n")) + 3 - width = sorted([len(i) for i in infotext.split("\n")] - + [len(introtext) + 1 + max_len])[-1] + 4 - super(LabelBox, self).__init__(height, width, name, overview=overview) - self.map = _map - self.infotext = se.Text(infotext) - self.introtext = se.Text(introtext) - self.text = se.Text(text) - self.add_ob(self.infotext, 2, 1) - self.add_ob(self.introtext, 2, len(infotext.split("\n")) + 1) - self.add_ob(self.text, self.introtext.rx + len(introtext) + 1, - self.introtext.ry) - - -if __name__ == "__main__": - print("\033[31;1mDo not execute this!\033[0m") diff --git a/pokete_classes/ui/elements/frame.py b/pokete_classes/ui/elements/frame.py new file mode 100644 index 00000000..0e3fc118 --- /dev/null +++ b/pokete_classes/ui/elements/frame.py @@ -0,0 +1,26 @@ +import scrap_engine as se + + +class StdFrame(se.Frame): + """Standardized frame + ARGS: + height: The frames height + width: The frames width""" + + def __init__(self, height, width): + super().__init__(width=width, height=height, + corner_chars=["┌", "┐", "└", "┘"], + horizontal_chars=["─", "─"], + vertical_chars=["│", "│"], state="float") + + +class StdFrame2(se.Frame): + """Standardized frame + ARGS: + height: The frames height + width: The frames width""" + + def __init__(self, height, width, state="solid"): + super().__init__(width=width, height=height, + corner_chars=["_", "_", "|", "|"], + horizontal_chars=["_", "_"], state=state) diff --git a/pokete_classes/input.py b/pokete_classes/ui/input.py similarity index 55% rename from pokete_classes/input.py rename to pokete_classes/ui/input.py index 6e5c714b..35f25225 100644 --- a/pokete_classes/input.py +++ b/pokete_classes/ui/input.py @@ -1,54 +1,8 @@ """This file contains input wrappers for ui elements""" -from pokete_classes.hotkeys import Action, get_action -from util import hard_liner -from .loops import std_loop -from .ui_elements import InfoBox, InputBox -from .event import _ev - - -def text_input(obj, _map, name, wrap_len, max_len=1000000, box=None): - """Processes text input - ARGS: - obj: The text label that will be rechared - _map: The map this happens on - name: The default value of the label - wrap_len: The len at which the text wraps - max_len: The len at which the text shall end - box: The box this is called for""" - _ev.clear() - obj.rechar(hard_liner(wrap_len, name + "█")) - bname = name - _map.show() - while True: - # Use lower level ev.get() methods because we need - # to handle typed text, not game actions - if _ev.get() in ("Key.enter", "Key.esc"): - _ev.clear() - obj.rechar(hard_liner(wrap_len, name)) - _map.show() - return name - if _ev.get() == "Key.backspace": - if len(name) <= 0: - _ev.clear() - obj.rechar(bname) - _map.show() - return bname - name = name[:-1] - obj.rechar(hard_liner(wrap_len, name + "█")) - _map.show() - _ev.clear() - elif ( - ((i := _ev.get()) not in ["", "exit"] and "Key." not in i) - and len(name) < max_len or i == "Key.space" - ): - if _ev.get() == "Key.space": - _ev.set(" ") - name += str(_ev.get()) - obj.rechar(hard_liner(wrap_len, name + "█")) - _map.show() - _ev.clear() - std_loop(_map.name == "movemap", box=box) +from .elements import InfoBox, InputBox +from ..input import Action, get_action, text_input, _ev +from .. import loops def ask_bool(_map, text, overview=None): @@ -69,7 +23,7 @@ def ask_bool(_map, text, overview=None): if action.triggers(Action.CANCEL): ret = False break - std_loop(_map.name == "movemap", box=box) + loops.std(_map.name == "movemap", box=box) return ret @@ -105,5 +59,5 @@ def ask_ok(_map, text, overview=None): action = get_action() if action.triggers(Action.ACCEPT or action == Action.CANCEL): break - std_loop(_map.name == "movemap", box=box) + loops.std(_map.name == "movemap", box=box) _ev.clear() diff --git a/pokete_classes/notify.py b/pokete_classes/ui/notify.py similarity index 96% rename from pokete_classes/notify.py rename to pokete_classes/ui/notify.py index 751f67f2..3dbe8d3d 100644 --- a/pokete_classes/notify.py +++ b/pokete_classes/ui/notify.py @@ -2,9 +2,9 @@ import scrap_engine as se from util import liner -from .ui_elements import LabelBox -from .color import Color -from .util.object_group import get_nested +from .elements import LabelBox +from .util import get_nested +from ..color import Color class Notification(LabelBox): diff --git a/pokete_classes/ui/overview.py b/pokete_classes/ui/overview.py new file mode 100644 index 00000000..91dde123 --- /dev/null +++ b/pokete_classes/ui/overview.py @@ -0,0 +1,3 @@ +class Overview: + def resize_view(self): + raise NotImplemented diff --git a/pokete_classes/util/object_group.py b/pokete_classes/ui/util.py similarity index 100% rename from pokete_classes/util/object_group.py rename to pokete_classes/ui/util.py diff --git a/pokete_classes/util/__init__.py b/pokete_classes/util/__init__.py deleted file mode 100644 index e69de29b..00000000