from __future__ import annotations
import csv
import logging
import os
import pygame
import yaml
from nqp.core.constants import ASSET_PATH, DATA_PATH, Flags, SceneType, WorldState
from nqp.core.utility import scene_to_scene_type
from nqp.ui_elements.generic.ui_input_box import UIInputBox
__all__ = ["DevConsole"]
[docs]class DevConsole(UIInputBox):
[docs] def __init__(self, game):
size = pygame.Vector2(100, 30)
pos = pygame.Vector2(10, 10)
super().__init__(game, size, pos)
[docs] def update(self, delta_time: float):
super().update(delta_time)
if self.focused:
# pressed enter
if self._game.input.states["typing_enter"]:
self._game.input.states["typing_enter"] = False
self._handle_dev_command()
self._game.debug.toggle_dev_console_visibility()
[docs] def draw(self, surface: pygame.Surface, offset=(0, 0)):
super().draw(surface)
def _handle_dev_command(self):
"""
Handle the command in the dev console. Expected format is "[command] [value]".
"""
command = self.font.text
confirmation_message = ""
if command[:5] == "event":
event_id = command[6:] # +1 position to account for space
# check active scene
if (SceneType.MAIN_MENU, SceneType.RUN_SETUP) not in self._game.scene_stack:
confirmation_message = self._switch_to_event(event_id)
elif command[:7] == "godmode":
# check active scene
if SceneType.WORLD in self._game.scene_stack:
confirmation_message = self._toggle_godmode()
elif command[:17] == "create_unit_data":
# check active scene
if SceneType.MAIN_MENU in self._game.scene_stack:
confirmation_message = self._add_unit_data_for_each_asset_folder()
elif command[:13] == "load-unit-csv":
# check active scene
if self._game.main_menu in self._game.scene_stack:
confirmation_message = self._load_unit_csv()
elif command[:7] == "gallery":
# check active scene
if SceneType.MAIN_MENU in self._game.scene_stack:
confirmation_message = self._switch_to_gallery()
elif command[:11] == "data_editor":
# check active scene
if SceneType.MAIN_MENU in self._game.scene_stack:
confirmation_message = self._switch_to_data_editor()
elif command[:13] == "combat_result":
result = command[14:] # +1 position to account for space
# check active scene
if SceneType.WORLD in self._game.scene_stack and self._game.world.state == WorldState.COMBAT:
confirmation_message = self._process_combat_result(result)
# update result
if confirmation_message != "":
active_scene = self._game.scene_stack[0]
active_scene.ui.set_instruction_text(confirmation_message, True)
def _add_unit_data_for_each_asset_folder(self) -> str:
"""
Add a placeholder unit data for every unit asset folder.
"""
count = 0
unit_dict = list(self._game.data.units.values())[0]
logging.debug(f"Creating unit data...")
for unit_name in os.listdir(ASSET_PATH / "units"):
unit_data_path = DATA_PATH / "units" / unit_name
# skip system files and templates
if unit_name[1:] == "_":
continue
# check if data already exists
if os.path.isfile(f"{unit_data_path}.yaml"):
continue
# it doesnt exist, create the data
unit_dict["type"] = unit_name
with open(f"{unit_data_path}.yaml", "w") as file:
yaml.dump(unit_dict, file, Dumper=yaml.SafeDumper)
logging.debug(f"-> Created {unit_name} yaml.")
count += 1
if count > 0:
confirmation_message = f"{count} unit data created."
else:
confirmation_message = ""
logging.debug(f"All required unit data created. {count} created.")
return confirmation_message
def _toggle_godmode(self) -> str:
"""
Turns godmode on or off.
"""
if self._game.memory.check_for_flag(Flags.GODMODE):
self._game.memory.remove_flag(Flags.GODMODE)
state = "off"
logging.debug(f"Turned godmode off.")
else:
self._game.memory.add_flag(Flags.GODMODE)
state = "on"
logging.debug(f"Turned godmode on.")
# add cheat flag
if not self._game.memory.check_for_flag(Flags.CHEATED):
self._game.memory.add_flag(Flags.CHEATED)
confirmation_message = f"God mode turned {state}."
return confirmation_message
def _switch_to_event(self, event_id: str) -> str:
"""
Change the scene and load a specific event.
"""
# validate event
if event_id in self._game.memory.event_deck.keys():
# load event
self._game.event.load_event(event_id)
self._game.event.ui.rebuild_ui()
self._game.active_scene = self._game.event
confirmation_message = f"Loaded event {event_id}."
return confirmation_message
else:
logging.warning(f"DevConsole: {event_id} not found.")
def _switch_to_gallery(self) -> str:
self._game.dev_gallery.previous_scene_type = scene_to_scene_type(self._game.active_scene)
self._game.dev_gallery.ui.rebuild_ui()
self._game.active_scene = self._game.dev_gallery
confirmation_message = f"Loaded gallery."
return confirmation_message
def _switch_to_data_editor(self):
self._game.dev_unit_data.previous_scene_type = scene_to_scene_type(self._game.active_scene)
self._game.dev_unit_data.ui.rebuild_ui()
self._game.active_scene = self._game.dev_unit_data
confirmation_message = f"Loaded data editor."
return confirmation_message
def _load_unit_csv(self):
"""
Load the unit csv into the unit data files.
"""
existing_units = list(self._game.data.units.keys())
num_updated = 0
num_created = 0
logging.debug(f"Loading unit csv...")
# load the data
with open("units.csv", "r") as csv_file:
csv_reader = csv.DictReader(csv_file)
for row in csv_reader:
str_path = str(DATA_PATH / "units" / f"{row['type']}.yaml")
# check if unit file already exists
if row["type"] in existing_units:
# open existing data
with open(str_path, "r") as unit_data:
data = yaml.load(unit_data, Loader=yaml.SafeLoader)
# update data
data["health"] = int(row["health"])
data["mundane_defence"] = int(row["mundane_defence"])
data["magic_defence"] = int(row["magic_defence"])
data["attack"] = int(row["attack"])
data["range"] = int(row["range"])
data["attack_speed"] = float(row["attack_speed"])
data["move_speed"] = int(row["move_speed"])
data["ammo"] = int(row["ammo"])
data["count"] = int(row["count"])
data["tier"] = int(row["tier"])
data["faction"] = str(row["faction"])
data["damage_type"] = str(row["damage_type"])
data["crit_chance"] = int(row["crit_chance"])
data["penetration"] = int(row["penetration"])
data["regen"] = int(row["regen"])
data["dodge"] = int(row["dodge"])
# delete previous file
os.remove(str_path)
# create new file
with open(str_path, "w") as unit_data:
yaml.dump(data, unit_data, Dumper=yaml.SafeDumper)
num_updated += 1
else:
data = row.copy()
# add needed values not held in csv
data["size"] = 1
data["weight"] = 2
data["gold_cost"] = 0
# create new file
with open(str_path, "w") as unit_data:
yaml.dump(data, unit_data, Dumper=yaml.SafeDumper)
num_created += 1
self._game.data.load_all_data()
confirmation_message = f"Updated {num_updated} unit details and created {num_created} units. Data reloaded."
logging.debug(f"-> {confirmation_message})")
return confirmation_message
def _process_combat_result(self, result: str) -> str:
"""
Set the result of the current combat. Result should be 'win' or 'lose'.
"""
# FIXME - combat is no longer a scene
if result == "win":
logging.debug(f"Skipped to combat victory.")
self._game.combat.end_combat()
self._game.combat.process_victory()
confirmation_message = "Combat won."
elif result == "lose":
logging.debug(f"Skipped to combat defeat.")
self._game.combat.end_combat()
self._game.combat.process_defeat()
confirmation_message = "Combat lost."
else:
confirmation_message = f"Result type ({result}) not recognised."
return confirmation_message