from __future__ import annotations
import logging
from typing import Optional, Type
from snecs.typedefs import EntityID
import scripts.engine.core.matter
from scripts.engine.core import hourglass, state, utility, world
from scripts.engine.core.component import Knowledge, Position
from scripts.engine.core.ui import ui
from scripts.engine.internal import debug, library
from scripts.engine.internal.action import Skill
from scripts.engine.internal.constant import (
Direction,
DirectionType,
GameState,
InputIntent,
InputIntentType,
TargetingMethod,
UIElement,
)
from scripts.engine.internal.event import EndTurnEvent, event_hub
from scripts.engine.world_objects.tile import Tile
from scripts.nqp import command
from scripts.nqp.processors.targeting import targeting
from scripts.nqp.ui_elements.camera import camera
from scripts.nqp.ui_elements.dungen_viewer import DungenViewer
__all__ = ["process_intent"]
[docs]def process_intent(intent: InputIntentType, game_state: GameState = None):
"""
Process the intent in the context of the game state. Intents are game state sensitive. If no game state is
passed the current one will be obtained.
"""
_process_stateless_intents(intent)
if not game_state:
game_state = state.get_current()
if game_state == GameState.GAME_MAP:
_process_game_map_intents(intent)
elif game_state == GameState.TARGETING:
_process_targeting_mode_intents(intent)
elif game_state == GameState.MENU:
_process_menu_intents(intent)
[docs]def _process_stateless_intents(intent: InputIntentType):
"""
Process intents that don't rely on game state.
"""
## Activate Debug
if intent == InputIntent.DEBUG_TOGGLE:
# F1
if debug.is_fps_visible():
debug.set_fps_visibility(False)
else:
debug.set_fps_visibility(True)
## Refresh Library Data
elif intent == InputIntent.REFRESH_DATA:
# TODO - move to a command in the console.
library.refresh_library()
## Activate data editor
# TODO - have this trigger dev console and move skill editor to a command in the console.
elif intent == InputIntent.DUNGEON_DEV_VIEW:
# F5
if ui.element_is_visible(UIElement.DUNGEN_VIEWER):
ui.set_element_visibility(UIElement.DUNGEN_VIEWER, False)
state.set_new(state.get_previous())
else:
if ui.has_element(UIElement.DUNGEN_VIEWER):
dungen_viewer = ui.get_element(UIElement.DUNGEN_VIEWER)
else:
dungen_viewer = DungenViewer(command.get_element_rect(UIElement.DUNGEN_VIEWER), ui.get_gui_manager())
ui.register_element(UIElement.DUNGEN_VIEWER, dungen_viewer)
ui.set_element_visibility(UIElement.DUNGEN_VIEWER, True)
dungen_viewer.refresh_viewer()
state.set_new(GameState.MENU)
elif intent == InputIntent.DEV_TOGGLE:
# F2
pass
elif intent == InputIntent.BURST_PROFILE:
# F3
debug.enable_profiling(120)
elif intent == InputIntent.TOGGLE_UI:
# F6
# toggle message log
if ui.has_element(UIElement.MESSAGE_LOG):
if ui.element_is_visible(UIElement.MESSAGE_LOG):
ui.set_element_visibility(UIElement.MESSAGE_LOG, False)
else:
ui.set_element_visibility(UIElement.MESSAGE_LOG, True)
# toggle skill bar
if ui.has_element(UIElement.SKILL_BAR):
if ui.element_is_visible(UIElement.SKILL_BAR):
ui.set_element_visibility(UIElement.SKILL_BAR, False)
else:
ui.set_element_visibility(UIElement.SKILL_BAR, True)
elif intent == InputIntent.TEST:
# F12
breakpoint()
[docs]def _process_game_map_intents(intent: InputIntentType):
"""
Process intents for the player turn game state.
"""
player = scripts.engine.core.matter.get_player()
position = scripts.engine.core.matter.get_entitys_component(player, Position)
possible_move_intents = [InputIntent.DOWN, InputIntent.UP, InputIntent.LEFT, InputIntent.RIGHT]
possible_skill_intents = [
InputIntent.SKILL0,
InputIntent.SKILL1,
InputIntent.SKILL2,
InputIntent.SKILL3,
InputIntent.SKILL4,
InputIntent.SKILL5,
]
## Player movement
if intent in possible_move_intents and position:
direction = _get_pressed_direction(intent)
target_tile = world.get_tile((position.x, position.y))
move = scripts.engine.core.matter.get_known_skill(player, "Move")
if direction in move.target_directions:
# ensure it's actually the player's turn by checking the event queue for an unprocessed end turn event
if not event_hub.peek(EndTurnEvent):
_process_skill_use(player, move, target_tile, direction)
## Use a skill
elif intent in possible_skill_intents and position:
skill_name = _get_pressed_skills_name(intent)
current_tile = world.get_tile((position.x, position.y))
if skill_name:
# is skill ready to use
if scripts.engine.core.matter.can_use_skill(player, skill_name):
skill = scripts.engine.core.matter.get_known_skill(player, skill_name)
if skill:
# if auto targeting use the skill
if skill.targeting_method == TargetingMethod.AUTO:
# pass centre as it doesnt matter, the skill will pick the right direction
_process_skill_use(player, skill, current_tile, Direction.CENTRE)
elif skill.targeting_method in [TargetingMethod.TILE, TargetingMethod.DIRECTION]:
# trigger targeting overlay
state.set_new(GameState.TARGETING)
state.set_active_skill(skill_name)
## Show actor info - we're in GAMEMAP so it cant be visible
elif intent == InputIntent.ACTOR_INFO_TOGGLE:
# show
state.set_new(GameState.MENU)
ui.set_element_visibility(UIElement.ACTOR_INFO, True)
elif intent == InputIntent.EXIT:
command.exit_game()
elif intent == InputIntent.LEFT_CLICKED:
targeting.process_click()
[docs]def _process_targeting_mode_intents(intent):
"""
Process intents for the player turn game state.
"""
player = scripts.engine.core.matter.get_player()
position = scripts.engine.core.matter.get_entitys_component(player, Position)
active_skill_name = state.get_active_skill()
skill = scripts.engine.core.matter.get_known_skill(player, active_skill_name)
possible_skill_intents = [
InputIntent.SKILL0,
InputIntent.SKILL1,
InputIntent.SKILL2,
InputIntent.SKILL3,
InputIntent.SKILL4,
InputIntent.SKILL5,
]
## Cancel use
if intent == InputIntent.CANCEL:
# go back to previous state
state.set_new(state.get_previous())
elif intent == InputIntent.LEFT_CLICKED:
targeting.process_click()
## Select new skill
elif intent in possible_skill_intents:
pressed_skill_name = _get_pressed_skills_name(intent)
if pressed_skill_name:
# if skill pressed doesn't match skill already being targeted
if pressed_skill_name != active_skill_name:
# reactivate targeting mode with the new skill
if scripts.engine.core.matter.can_use_skill(player, pressed_skill_name):
state.set_active_skill(pressed_skill_name)
## Use skill
elif intent.upper() in utility.get_class_members(Direction):
direction = _get_pressed_direction(intent)
if position and skill and direction:
outermost = position.get_outermost(direction)
tile = world.get_tile((outermost[0] + direction[0], outermost[1] + direction[1]))
if skill.targeting_method == TargetingMethod.DIRECTION:
if not state.get_skill_target_valid():
tile = None
else:
tile = world.get_tile(state.get_active_skill_target())
if tile:
# we already checked if we could use the skill before activating the targeting mode
_process_skill_use(player, skill, tile, direction)
# resume previous state
state.set_new(state.get_previous())
# ui.update_targeting_overlay(False)
def _process_menu_intents(intent):
## Exit current menu
if intent == InputIntent.ACTOR_INFO_TOGGLE:
state.set_new(state.get_previous())
if ui.has_element(UIElement.ACTOR_INFO):
ui.set_element_visibility(UIElement.ACTOR_INFO, False)
################## HELPER FUNCTIONS ############################
[docs]def _process_skill_use(player: EntityID, skill: Type[Skill], target_tile: Tile, direction: DirectionType):
"""
Process the use of specified skill. Wrapper for actions needed to handle a full skill use. Assumed
'can_use_skill' already completed.
"""
# get players starting position for camera updates
pos = scripts.engine.core.matter.get_entitys_component(player, Position)
start_pos = pos.x, pos.y
if scripts.engine.core.matter.use_skill(player, skill, target_tile, direction):
scripts.engine.core.matter.pay_resource_cost(player, skill.resource_type, skill.resource_cost)
scripts.engine.core.matter.set_skill_on_cooldown(player, skill.__class__.__name__, skill.base_cooldown)
hourglass.end_turn(player, skill.time_cost)
# update camera if position changes
try:
if (pos.x, pos.y) != start_pos:
# ui.get_element(UIElement.CAMERA).set_target((pos.x, pos.y))
camera.set_target((pos.x, pos.y))
except KeyError:
logging.warning("Process skill use: tried to call camera but not init`d.")
[docs]def _get_pressed_direction(intent: InputIntentType) -> DirectionType:
"""
Get the value of the directions pressed. Returns as (x, y). Values are ints between -1 and 1.
"""
if intent == InputIntent.UP:
direction = Direction.UP
elif intent == InputIntent.UP_RIGHT:
direction = Direction.UP_RIGHT
elif intent == InputIntent.UP_LEFT:
direction = Direction.UP_LEFT
elif intent == InputIntent.RIGHT:
direction = Direction.RIGHT
elif intent == InputIntent.LEFT:
direction = Direction.LEFT
elif intent == InputIntent.DOWN:
direction = Direction.DOWN
elif intent == InputIntent.DOWN_RIGHT:
direction = Direction.DOWN_RIGHT
elif intent == InputIntent.DOWN_LEFT:
direction = Direction.DOWN_LEFT
else:
direction = Direction.CENTRE
return direction
[docs]def _get_pressed_skills_name(intent: InputIntentType) -> Optional[str]:
"""
Get the pressed skill number. Returns value of skill number pressed. If not found returns None.
"""
player = scripts.engine.core.matter.get_player()
skill_name = None
if player:
skills = scripts.engine.core.matter.get_entitys_component(player, Knowledge)
if skills:
skill_order = skills.skill_order
try:
if intent == InputIntent.SKILL0:
skill_name = skill_order[0]
elif intent == InputIntent.SKILL1:
skill_name = skill_order[1]
elif intent == InputIntent.SKILL2:
skill_name = skill_order[2]
elif intent == InputIntent.SKILL3:
skill_name = skill_order[3]
elif intent == InputIntent.SKILL4:
skill_name = skill_order[4]
except IndexError:
logging.warning(f"_get_pressed_skills_name: Tried to use skill {intent} but no skill in that slot.")
return skill_name