Source code for scripts.engine.core.state
from __future__ import annotations
import datetime
import json
import logging
import os
from typing import Tuple
import scripts.engine.core.matter
from scripts.engine.core import utility, world
from scripts.engine.internal import library
from scripts.engine.internal.constant import GameState, MAX_SAVES, SAVE_PATH, VERSION
from scripts.engine.internal.data import store
__all__ = [
    "get_previous",
    "get_internal_clock",
    "get_active_skill",
    "get_current",
    "set_active_skill",
    "update_clock",
    "set_new",
    "save_game",
    "dump_save_game",
    "load_game",
]
_save = {}
################### GET ##############################
[docs]def get_previous() -> GameState:
    """
    Get the previous game state
    """
    return store.previous_game_state 
[docs]def get_internal_clock():
    """
    Get the internal clock
    """
    return store.internal_clock 
[docs]def get_active_skill() -> str:
    """
    Get the active skill. Used for targeting mode.
    """
    return store.active_skill 
[docs]def get_active_skill_target() -> Tuple[int, int]:
    """
    Get the active skill target. Used for targeting mode.
    """
    return store.active_skill_target 
[docs]def get_skill_target_valid() -> bool:
    """
    Get the validity of the current target. Used for targeting mode.
    """
    return store.skill_target_valid 
[docs]def get_current() -> GameState:
    """
    Get the current game state
    """
    return store.current_game_state 
################### MANAGING STATE ###################
[docs]def initialise_engine():
    """
    Initialise engine resources.
    N.B. Must be called before using the rest of the engine.
    """
    # load modules that have module level instances the engine needs
    import scripts.engine.core.system
    import scripts.engine.core.ui
    import scripts.engine.internal.data
    import scripts.engine.internal.debug
    import scripts.engine.internal.library
    # register any Actions that exist within the engine
    from scripts.engine.internal.action import DelayedSkill, Projectile, register_action
    register_action(Projectile)
    register_action(DelayedSkill) 
[docs]def update_clock() -> float:
    """
    Tick the internal clock. Manages the frame rate. Returns delta time.
    """
    return store.internal_clock.tick(library.VIDEO_CONFIG.fps_limit) / 1000.0 
[docs]def set_new(new_game_state: GameState):
    """
    Set the current game state
    """
    store.previous_game_state = store.current_game_state
    store.current_game_state = new_game_state
    prev_name = utility.value_to_member(store.previous_game_state, GameState)
    curr_name = utility.value_to_member(store.current_game_state, GameState)
    log_string = f"game_state updated from {prev_name} to {curr_name}"
    logging.info(log_string) 
[docs]def set_active_skill(skill_name: str):
    """
    Set the active skill. Used for targeting mode.
    """
    store.active_skill = skill_name 
[docs]def set_active_skill_target(skill_target: Tuple[int, int]):
    """
    Set the active skill target. Used for targeting mode.
    """
    store.active_skill_target = skill_target 
[docs]def set_skill_target_valid(skill_target_valid: bool):
    """
    Set the validity of the current target. Used for targeting mode.
    """
    store.skill_target_valid = skill_target_valid 
##################### SAVE AND LOAD #######################
[docs]def save_game():
    """
    Serialise the game data to an internal container
    """
    global _save
    # clear existing save data
    _save = {}
    # get the info needed
    full_save_path = SAVE_PATH
    save = {}
    # Prevent FileNotFoundError
    if not os.path.isdir(full_save_path):
        os.makedirs(full_save_path)
    # add data to dict
    save["version"] = VERSION
    save["world"] = world.serialise()
    save["store"] = store.serialise()
    # prep filename
    player = scripts.engine.core.matter.get_player()
    name = scripts.engine.core.matter.get_name(player)
    name = name.replace(" ", "_")  # clean name
    date_and_time = datetime.datetime.utcnow().strftime("%Y%m%d@%H%M%S")
    save_name_prefix = f"{name}"
    new_save_name = f"{save_name_prefix}_{date_and_time}"
    # clear old saves
    existing_saves = []
    # get existing save files and check if they match
    for save_name in os.listdir(str(full_save_path)):
        if save_name_prefix in save_name:
            existing_saves.append(save_name)
    existing_saves = sorted(existing_saves)
    while len(existing_saves) > MAX_SAVES - 1:  # -1 to handle the offset
        save_name = existing_saves.pop(0)
        os.remove(str(full_save_path / save_name))
    # update save data
    _save[new_save_name] = save 
[docs]def dump_save_game():
    """
    Export the save game data, if it exists, to an external json file
    """
    global _save
    for save_name, save_values in _save.items():
        # write to json
        str_path = str(SAVE_PATH / save_name) + ".json"
        with open(str_path, "w") as file:
            json.dump(save_values, file, indent=4)
            logging.info("Save file dumped.") 
[docs]def load_game(filename: str):
    """
    Deserialise the game data from a file. Filename does not include path to save folder.
    """
    # read from json
    str_path = str(SAVE_PATH / filename) + ".json"
    with open(str_path, "r") as file:
        save = json.load(file)
    # check the version
    if save["version"] != VERSION:
        logging.warning(f"Loading data from a previous version, {save['version']}.")
    # deserialise data
    store.deserialise(save["store"])
    new_world = world.deserialise(save["world"])
    # set the data as the default world
    world.move_world(new_world)
    logging.info(f"Game loaded from {filename}.")