Documentation and Optimization Part 1 (#2)
Authored-by: Dennis Ploeger <develop@dieploegers.de>
This commit is contained in:
@@ -1,63 +1,42 @@
|
||||
# Plugin script to initialize Escoria
|
||||
tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
# Autoloads to instantiate
|
||||
const autoloads = {
|
||||
"escoria": "res://addons/escoria-core/game/escoria.tscn",
|
||||
"esctypes": "res://addons/escoria-core/game/core-scripts/escoria_types.gd"
|
||||
}
|
||||
|
||||
# Custom types to generate outside of Classes
|
||||
const custom_types = [
|
||||
{
|
||||
"type_name": "ESCBackground",
|
||||
"parent_type": "Sprite",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/escbackground.gd"
|
||||
},
|
||||
{
|
||||
"type_name": "ESCItem",
|
||||
"parent_type": "Area2D",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/escitem.gd"
|
||||
},
|
||||
{
|
||||
"type_name": "ESCItemsInventory",
|
||||
"parent_type": "GridContainer",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/items_inventory.gd"
|
||||
},
|
||||
{
|
||||
"type_name": "ESCInventoryItem",
|
||||
"parent_type": "TextureButton",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/escinventoryitem.gd"
|
||||
},
|
||||
{
|
||||
"type_name": "ESCPlayer",
|
||||
"parent_type": "KinematicBody2D",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/escplayer.gd"
|
||||
},
|
||||
{
|
||||
"type_name": "ESCRoom",
|
||||
"parent_type": "Node2D",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/escroom.gd"
|
||||
},
|
||||
{
|
||||
"type_name": "ESCTerrain",
|
||||
"parent_type": "Navigation2D",
|
||||
"script_res": "res://addons/escoria-core/game/core-scripts/escterrain.gd"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
# Setup Escoria
|
||||
func _enter_tree():
|
||||
add_autoloads()
|
||||
for key in autoloads.keys():
|
||||
add_autoload_singleton(key, autoloads[key])
|
||||
|
||||
for custom_type in custom_types:
|
||||
add_custom_type(custom_type.type_name, custom_type.parent_type,
|
||||
load(custom_type.script_res), null)
|
||||
|
||||
# Prepare settings
|
||||
set_escoria_main_settings()
|
||||
set_escoria_debug_settings()
|
||||
set_escoria_ui_settings()
|
||||
set_escoria_internal_settings()
|
||||
set_escoria_sound_settings()
|
||||
|
||||
|
||||
set_escoria_platform_settings()
|
||||
|
||||
|
||||
# Prepare the settings in the Escoria UI category
|
||||
func set_escoria_ui_settings():
|
||||
if !ProjectSettings.has_setting("escoria/ui/tooltip_follows_mouse"):
|
||||
ProjectSettings.set_setting("escoria/ui/tooltip_follows_mouse", true)
|
||||
@@ -110,8 +89,21 @@ func set_escoria_ui_settings():
|
||||
"hint_string": "*.tscn, *.scn"
|
||||
}
|
||||
ProjectSettings.add_property_info(game_scene_property_info)
|
||||
|
||||
if !ProjectSettings.has_setting("escoria/ui/items_autoregister_path"):
|
||||
ProjectSettings.set_setting(
|
||||
"escoria/ui/items_autoregister_path",
|
||||
"res://game/items/escitems/"
|
||||
)
|
||||
var game_scene_property_info = {
|
||||
"name": "escoria/ui/items_autoregister_path",
|
||||
"type": TYPE_STRING,
|
||||
"hint": PROPERTY_HINT_DIR
|
||||
}
|
||||
ProjectSettings.add_property_info(game_scene_property_info)
|
||||
|
||||
|
||||
# Prepare the settings in the Escoria main category
|
||||
func set_escoria_main_settings():
|
||||
if !ProjectSettings.has_setting("escoria/main/game_start_script"):
|
||||
ProjectSettings.set_setting("escoria/main/game_start_script", "")
|
||||
@@ -132,7 +124,7 @@ func set_escoria_main_settings():
|
||||
ProjectSettings.add_property_info(force_quit_property_info)
|
||||
|
||||
ProjectSettings.set_setting("application/run/main_scene", "res://addons/escoria-core/game/main_scene.tscn")
|
||||
|
||||
|
||||
if not ProjectSettings.has_setting("escoria/main/command_directories"):
|
||||
ProjectSettings.set_setting("escoria/main/command_directories", [
|
||||
"res://addons/escoria-core/game/core-scripts/esc/commands"
|
||||
@@ -142,7 +134,7 @@ func set_escoria_main_settings():
|
||||
"type": TYPE_ARRAY,
|
||||
})
|
||||
|
||||
|
||||
|
||||
if !ProjectSettings.has_setting("escoria/main/text_lang"):
|
||||
ProjectSettings.set_setting("escoria/main/text_lang", TranslationServer.get_locale())
|
||||
var text_lang_property_info = {
|
||||
@@ -151,7 +143,7 @@ func set_escoria_main_settings():
|
||||
"hint": PROPERTY_HINT_NONE
|
||||
}
|
||||
ProjectSettings.add_property_info(text_lang_property_info)
|
||||
|
||||
|
||||
if !ProjectSettings.has_setting("escoria/main/voice_lang"):
|
||||
ProjectSettings.set_setting("escoria/main/voice_lang", TranslationServer.get_locale())
|
||||
var voice_lang_property_info = {
|
||||
@@ -160,13 +152,9 @@ func set_escoria_main_settings():
|
||||
"hint": PROPERTY_HINT_NONE
|
||||
}
|
||||
ProjectSettings.add_property_info(voice_lang_property_info)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Prepare the settings in the Escoria debug category
|
||||
func set_escoria_debug_settings():
|
||||
if !ProjectSettings.has_setting("escoria/debug/terminate_on_warnings"):
|
||||
ProjectSettings.set_setting("escoria/debug/terminate_on_warnings", false)
|
||||
@@ -190,6 +178,7 @@ func set_escoria_debug_settings():
|
||||
ProjectSettings.add_property_info(property_info)
|
||||
|
||||
|
||||
# Prepare the settings in the Escoria internal category
|
||||
func set_escoria_internal_settings():
|
||||
if !ProjectSettings.has_setting("escoria/internals/save_data"):
|
||||
ProjectSettings.set_setting("escoria/internals/save_data", "")
|
||||
@@ -202,6 +191,7 @@ func set_escoria_internal_settings():
|
||||
ProjectSettings.add_property_info(save_data_property_info)
|
||||
|
||||
|
||||
# Prepare the settings in the Escoria sound settings
|
||||
func set_escoria_sound_settings():
|
||||
if !ProjectSettings.has_setting("escoria/sound/master_volume"):
|
||||
ProjectSettings.set_setting("escoria/sound/master_volume", 1)
|
||||
@@ -212,7 +202,7 @@ func set_escoria_sound_settings():
|
||||
"hint_string": "0,1"
|
||||
}
|
||||
ProjectSettings.add_property_info(master_data_property_info)
|
||||
|
||||
|
||||
if !ProjectSettings.has_setting("escoria/sound/music_volume"):
|
||||
ProjectSettings.set_setting("escoria/sound/music_volume", 1)
|
||||
var music_data_property_info = {
|
||||
@@ -242,37 +232,26 @@ func set_escoria_sound_settings():
|
||||
"hint_string": "0,1"
|
||||
}
|
||||
ProjectSettings.add_property_info(speech_data_property_info)
|
||||
|
||||
|
||||
|
||||
# Defines platform-specific parameters. Those are the ones that must be re-set for each platform export.
|
||||
# Prepare the settings in the Escoria platform category and may need special
|
||||
# setting per build
|
||||
func set_escoria_platform_settings():
|
||||
# Skip cache - certain platforms (esp. mobile) lack memory for caching scenes
|
||||
# If true, all generic scenes (UI, inventory, etc) will be loaded as any other scene.
|
||||
# Skip cache - certain platforms (esp. mobile) lack memory for caching
|
||||
# scenes.
|
||||
# If set to true, all generic scenes (UI, inventory, etc) will be loaded
|
||||
# as any other scene.
|
||||
if !ProjectSettings.has_setting("escoria/platform/skip_cache"):
|
||||
ProjectSettings.set_setting("escoria/platform/skip_cache", false)
|
||||
|
||||
|
||||
if !ProjectSettings.has_setting("escoria/platform/skip_cache.mobile"):
|
||||
ProjectSettings.set_setting("escoria/platform/skip_cache.mobile", true)
|
||||
|
||||
func add_autoloads():
|
||||
for key in autoloads.keys():
|
||||
add_autoload_singleton(key, autoloads[key])
|
||||
|
||||
func remove_autoloads():
|
||||
# Uninstall plugin
|
||||
func _exit_tree():
|
||||
for key in autoloads.keys():
|
||||
if ProjectSettings.has_setting(key):
|
||||
remove_autoload_singleton(key)
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
remove_custom_type("ESCBackground")
|
||||
remove_custom_type("ESCItem")
|
||||
remove_custom_type("ESCInventoryItem")
|
||||
remove_custom_type("ESCItemsInventory")
|
||||
remove_custom_type("ESCPlayer")
|
||||
remove_custom_type("ESCRoom")
|
||||
remove_custom_type("ESCTerrain")
|
||||
|
||||
remove_autoloads()
|
||||
for custom_type in custom_types:
|
||||
remove_custom_type(custom_type.type_name)
|
||||
|
||||
|
||||
|
||||
438
addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd
Normal file
438
addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd
Normal file
@@ -0,0 +1,438 @@
|
||||
# Node that performs the moving (walk, teleport, terrain scaling...) actions on
|
||||
# its parent node.
|
||||
tool
|
||||
extends Node
|
||||
class_name ESCMovable
|
||||
|
||||
|
||||
# Tasks carried out by this walkable node
|
||||
# NONE - The node is inactive
|
||||
# WALK - The node walks the parent somewhere
|
||||
# SLIDE - The node slides the parent somewhere
|
||||
enum MovableTask {
|
||||
NONE,
|
||||
WALK,
|
||||
SLIDE
|
||||
}
|
||||
|
||||
|
||||
# Character path through the scene as calculated by the Pathfinder
|
||||
var walk_path: Array = []
|
||||
|
||||
# Current active walk path entry
|
||||
var path_ofs: int
|
||||
|
||||
# The destination where the character should be moving to
|
||||
var walk_destination: Vector2
|
||||
|
||||
# The walk context currently carried out by this movable node
|
||||
var walk_context: ESCWalkContext = null
|
||||
|
||||
# Wether the character was moved at all
|
||||
var moved: bool
|
||||
|
||||
# Angle degrees to the last position (TODO is that correct?)
|
||||
var last_deg : int
|
||||
|
||||
# Direction of the last position (TODO is that correct?)
|
||||
var last_dir : int
|
||||
|
||||
# Scale of the last position (TODO is that correct?)
|
||||
var last_scale : Vector2
|
||||
|
||||
# TODO Isn't this actually the flip state of the current animation?
|
||||
var pose_scale : int
|
||||
|
||||
|
||||
var _orig_speed: float = 0.0
|
||||
|
||||
|
||||
# Shortcut variable that references the node's parent
|
||||
onready var parent = get_parent()
|
||||
|
||||
# If character misses an animation, bypass it and proceed.
|
||||
onready var bypass_missing_animation = false
|
||||
|
||||
|
||||
# Currenly running task
|
||||
onready var task = MovableTask.NONE
|
||||
|
||||
|
||||
# Add the signal "arrived" to the parent node, which is emitted when
|
||||
# the destination position was reached
|
||||
func _ready() -> void:
|
||||
parent.add_user_signal("arrived")
|
||||
|
||||
|
||||
# Main processing loop
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - delta: Time that has passed since the last call to this function
|
||||
func _process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if task == MovableTask.WALK or \
|
||||
task == MovableTask.SLIDE:
|
||||
var pos = parent.get_position()
|
||||
var old_pos = pos
|
||||
var next
|
||||
if walk_path.size() > 1:
|
||||
next = walk_path[path_ofs + 1]
|
||||
else:
|
||||
next = walk_path[path_ofs]
|
||||
|
||||
var dist = parent.speed * delta * pow(last_scale.x, 2) * \
|
||||
parent.terrain.player_speed_multiplier
|
||||
if walk_context.fast:
|
||||
dist *= parent.terrain.player_doubleclick_speed_multiplier
|
||||
var dir = (next - pos).normalized()
|
||||
|
||||
# TODO comment what this is all about
|
||||
dir = dir * (dir.x * dir.x + dir.y * dir.y * parent.v_speed_damp)
|
||||
|
||||
var new_pos
|
||||
if pos.distance_to(next) < dist:
|
||||
new_pos = next
|
||||
path_ofs += 1
|
||||
else:
|
||||
new_pos = pos + dir * dist
|
||||
|
||||
if path_ofs >= walk_path.size() - 1:
|
||||
walk_stop(walk_destination)
|
||||
return
|
||||
|
||||
pos = new_pos
|
||||
|
||||
var angle = (old_pos.angle_to_point(pos))
|
||||
parent.set_position(pos)
|
||||
|
||||
if task == MovableTask.WALK:
|
||||
last_deg = escoria.utils.get_deg_from_rad(angle)
|
||||
last_dir = _get_dir_deg(last_deg, parent.animations)
|
||||
|
||||
var current_animation = ""
|
||||
if parent.animation_sprite != null:
|
||||
current_animation = parent.animation_sprite.animation
|
||||
# elif animation != null:
|
||||
# current_animation = animation.current_animation
|
||||
|
||||
# FIXME This is obviously wrong as bypass_missing_animation is
|
||||
# always false
|
||||
bypass_missing_animation = false
|
||||
if !bypass_missing_animation:
|
||||
var animation_to_play = \
|
||||
parent.animations.directions[last_dir][0]
|
||||
if current_animation != animation_to_play:
|
||||
if parent.animation_sprite.frames.has_animation(
|
||||
animation_to_play
|
||||
):
|
||||
parent.animation_sprite.play(animation_to_play)
|
||||
else:
|
||||
bypass_missing_animation = true
|
||||
current_animation = animation_to_play
|
||||
escoria.logger.report_warnings(
|
||||
"movable.gd:_process()",
|
||||
[
|
||||
"Character %s has no animation %s "
|
||||
% [parent.global_id, animation_to_play],
|
||||
"Bypassing missing animation and " +
|
||||
"proceed movement."
|
||||
],
|
||||
true
|
||||
)
|
||||
|
||||
pose_scale = parent.animations.directions[last_dir][1]
|
||||
|
||||
update_terrain()
|
||||
else:
|
||||
moved = false
|
||||
set_process(false)
|
||||
|
||||
|
||||
# Teleports this item to the target position.
|
||||
# TODO angle is only used for logging and has no further use, so it probably
|
||||
# can be removed
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - target: Vector2, Position2d or ESCItem
|
||||
func teleport(target, angle : Object = null) -> void:
|
||||
if typeof(target) == TYPE_VECTOR2 :
|
||||
escoria.logger.info(
|
||||
"Object %s teleported at position %s with angle" %
|
||||
[parent.global_id, str(target)],
|
||||
[angle]
|
||||
)
|
||||
parent.position = target
|
||||
elif target is Position2D:
|
||||
escoria.logger.info(
|
||||
"Object %s teleported at position %s with angle" %
|
||||
[parent.global_id, str(target.position)],
|
||||
[angle]
|
||||
)
|
||||
parent.position = target.position
|
||||
elif typeof(target) == TYPE_OBJECT:
|
||||
# FIXME this is better written as target is ESCItem if that's
|
||||
# the only case here
|
||||
# if target.get("interact_positions") != null:
|
||||
# parent.position = target.interact_positions.default
|
||||
# else:
|
||||
# parent.position = target.position
|
||||
parent.position = target.get_interact_position()
|
||||
escoria.logger.info("Object " + target.name + " teleported at position "
|
||||
+ str(parent.position) + " with angle ", str(angle))
|
||||
else:
|
||||
escoria.logger.report_errors("escitem.gd:teleport()",
|
||||
["Target to teleport to is null or unusable (" + target + ")"])
|
||||
|
||||
|
||||
# Walk to a given position
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - pos: Position to walk to
|
||||
# - p_walk_context: Walk context to use
|
||||
func walk_to(pos : Vector2, p_walk_context: ESCWalkContext = null) -> void:
|
||||
if not parent.terrain:
|
||||
walk_stop(parent.get_position())
|
||||
return
|
||||
|
||||
if task == MovableTask.WALK:
|
||||
if walk_context.target_object == p_walk_context.target_object \
|
||||
or walk_context.target_position == p_walk_context.target_position:
|
||||
walk_context.fast = p_walk_context.fast
|
||||
|
||||
walk_context = p_walk_context
|
||||
|
||||
if task == MovableTask.NONE:
|
||||
task = MovableTask.WALK
|
||||
|
||||
walk_path = parent.terrain.get_simple_path(parent.get_position(), pos, true)
|
||||
|
||||
if walk_path.size() == 0:
|
||||
task = MovableTask.NONE
|
||||
walk_stop(parent.get_position())
|
||||
set_process(false)
|
||||
return
|
||||
moved = true
|
||||
walk_destination = walk_path[walk_path.size()-1]
|
||||
path_ofs = 0
|
||||
task = MovableTask.WALK
|
||||
set_process(true)
|
||||
|
||||
|
||||
# FIXME this function doesn't seem to be used anywhere
|
||||
func walk(target_pos, p_speed, context = null) -> void:
|
||||
if p_speed:
|
||||
_orig_speed = parent.speed
|
||||
parent.speed = p_speed
|
||||
walk_to(target_pos, context)
|
||||
|
||||
|
||||
# We have finished walking. Set the idle pose and complete
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - pos: Final target position
|
||||
func walk_stop(pos: Vector2) -> void:
|
||||
parent.position = pos
|
||||
# parent.interact_status = parent.INTERACT_STATES.INTERACT_NONE
|
||||
walk_path = []
|
||||
|
||||
if _orig_speed > 0:
|
||||
parent.speed = _orig_speed
|
||||
_orig_speed = 0.0
|
||||
|
||||
task = MovableTask.NONE
|
||||
moved = false
|
||||
set_process(false)
|
||||
|
||||
# If we're heading to an object and reached its interaction position,
|
||||
# orient towards the defined interaction direction set on the object
|
||||
# (if any)
|
||||
if walk_context.target_object and \
|
||||
walk_context.target_object.node.player_orients_on_arrival and \
|
||||
walk_context.target_object.interactive:
|
||||
var orientation = walk_context.target_object.node.interaction_direction
|
||||
last_dir = orientation
|
||||
parent.animation_sprite.play(
|
||||
parent.animations.idles[orientation][0]
|
||||
)
|
||||
pose_scale = parent.animations.idles[orientation][1]
|
||||
else:
|
||||
parent.animation_sprite.play(parent.animations.idles[last_dir][0])
|
||||
pose_scale = parent.animations.idles[last_dir][1]
|
||||
|
||||
update_terrain()
|
||||
|
||||
yield(parent.animation_sprite, "animation_finished")
|
||||
if walk_context.target_object:
|
||||
escoria.logger.debug(
|
||||
"%s arrived at %s" % [
|
||||
parent.global_id,
|
||||
walk_context.target_object.global_id
|
||||
]
|
||||
)
|
||||
else:
|
||||
escoria.logger.debug(
|
||||
"%s arrived at %s" % [
|
||||
parent.global_id,
|
||||
walk_context.target_position
|
||||
]
|
||||
)
|
||||
parent.emit_signal("arrived", walk_context)
|
||||
|
||||
|
||||
# Update the sprite scale and lighting
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - on_event_finished_name: Used if this function is called from an ESC event
|
||||
func update_terrain(on_event_finished_name = null) -> void:
|
||||
if !parent.terrain or parent.terrain == null \
|
||||
or !is_instance_valid(parent.terrain):
|
||||
return
|
||||
if on_event_finished_name != null and on_event_finished_name != "setup":
|
||||
return
|
||||
if parent.get("is_exit"):
|
||||
return
|
||||
if parent.get("dont_apply_terrain_scaling"):
|
||||
return
|
||||
|
||||
var pos = parent.position
|
||||
if pos.y <= VisualServer.CANVAS_ITEM_Z_MAX:
|
||||
parent.z_index = pos.y
|
||||
else:
|
||||
parent.z_index = VisualServer.CANVAS_ITEM_Z_MAX
|
||||
|
||||
var factor = parent.terrain.get_terrain(pos)
|
||||
var scal = parent.terrain.get_scale_range(factor)
|
||||
if scal != parent.get_scale():
|
||||
last_scale = scal
|
||||
parent.scale = last_scale
|
||||
|
||||
var color = parent.terrain.get_light(pos)
|
||||
parent.modulate = color
|
||||
|
||||
# Do not flip the entire character, because that would conflict
|
||||
# with shadows that expect to be siblings of $texture
|
||||
# TODO Make the character sprite not rely on the node name
|
||||
if pose_scale == -1 and parent.get_node("sprite").scale.x > 0:
|
||||
parent.get_node("sprite").scale.x *= pose_scale
|
||||
parent.collision.scale.x *= pose_scale
|
||||
elif pose_scale == 1 and parent.get_node("sprite").scale.x < 0:
|
||||
parent.get_node("sprite").scale.x *= -1
|
||||
parent.collision.scale.x *= -1
|
||||
|
||||
|
||||
# Get the player direction index based on rotation angles
|
||||
#
|
||||
# FIXME: This function doesn't seem to be used anymore
|
||||
# #### Parameters
|
||||
#
|
||||
# - angle: The rotation angle
|
||||
# - animations: The list of character animations
|
||||
func _get_dir(angle : float, animations) -> int:
|
||||
var deg = escoria.utils.get_deg_from_rad(angle)
|
||||
return _get_dir_deg(deg, animations)
|
||||
|
||||
|
||||
# Get the player direction index based on degrees
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - deg: Degrees
|
||||
# - animations: Player animations script
|
||||
func _get_dir_deg(deg: int, animations: Script) -> int:
|
||||
# We turn the angle by -90° because angle_to_point gives the angle
|
||||
# against X axis, not Y
|
||||
deg = wrapi(deg - 90, 0, 360)
|
||||
var dir = -1
|
||||
var i = 0
|
||||
|
||||
for arr_angle_zone in animations.dir_angles:
|
||||
if is_angle_in_interval(deg, arr_angle_zone):
|
||||
dir = i
|
||||
break
|
||||
else:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# It's an error to have the animations misconfigured
|
||||
if dir == -1:
|
||||
escoria.logger.report_errors(
|
||||
"escitem.gd:_get_dir_deg()",
|
||||
["No direction found for " + str(deg)]
|
||||
)
|
||||
|
||||
return dir
|
||||
|
||||
|
||||
# Returns true if given angle is inside the interval given by a starting_angle
|
||||
# and the size.
|
||||
# TODO Refactor to make this stuff understandable :D
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - angle: Angle to test
|
||||
# - interval : Array of size 2, containing the starting angle, and the size of
|
||||
# interval
|
||||
# eg: [90, 40] corresponds to angle between 90° and 130°
|
||||
func is_angle_in_interval(angle: float, interval : Array) -> bool:
|
||||
angle = wrapi(angle, 0, 360)
|
||||
if angle == 0:
|
||||
angle = 360
|
||||
var start_angle = wrapi(interval[0], 0, 360)
|
||||
var angle_area = interval[1]
|
||||
var end_angle = wrapi(interval[0] + angle_area, 0, 360)
|
||||
|
||||
if ((angle >= 270 and angle <= 360) \
|
||||
or (angle >= 0 and angle <= 90)) \
|
||||
and wrapi(angle + 180, 0, 360) > wrapi(interval[0] + 180, 0, 360) \
|
||||
and wrapi(angle + 180, 0, 360) <= wrapi(
|
||||
interval[0] + angle_area + 180, 0, 360
|
||||
):
|
||||
return true
|
||||
elif wrapi(angle, 0, 360) > start_angle \
|
||||
and wrapi(angle, 0, 360) <= end_angle:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
# Sets character's angle and plays according animation.
|
||||
#
|
||||
# TODO: depending on current angle and current angle, the character may
|
||||
# directly turn around with no "progression". We may enhance this by
|
||||
# calculating successive directions to turn the character to, so that he
|
||||
# doesn't switch to opposite direction too fast.
|
||||
# For example, if character looks WEST and set_angle(EAST) is called, we may
|
||||
# want the character to first turn SOUTHWEST, then SOUTH, then SOUTHEAST and
|
||||
# finally EAST, all more or less fast.
|
||||
# Whatever the implementation, this should be activated using "parameter
|
||||
# "immediate" set to false.
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - deg int angle to set the character
|
||||
# - immediate bool (currently unused, see TODO below)
|
||||
# If true, direction is switched immediately. Else, successive animations are
|
||||
# used so that the character turns to target angle.
|
||||
func set_angle(deg : int, immediate = true) -> void:
|
||||
if deg < 0 or deg > 360:
|
||||
escoria.logger.report_errors(
|
||||
"movable.gd:set_angle()",
|
||||
["Invalid degree to turn to " + str(deg)]
|
||||
)
|
||||
moved = true
|
||||
last_deg = deg
|
||||
last_dir = _get_dir_deg(deg, parent.animations)
|
||||
|
||||
# The character may have a state animation from before, which would be
|
||||
# resumed, so we immediately force the correct idle animation
|
||||
if parent.animation_sprite.animation != \
|
||||
parent.animations.idles[last_dir][0]:
|
||||
parent.animation_sprite.play(parent.animations.idles[last_dir][0])
|
||||
pose_scale = parent.animations.idles[last_dir][1]
|
||||
update_terrain()
|
||||
@@ -1,4 +0,0 @@
|
||||
extends Node
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
@@ -1,334 +0,0 @@
|
||||
tool
|
||||
extends Node
|
||||
class_name Movable
|
||||
|
||||
"""
|
||||
This class performs the moving (walk, teleport, terrain scaling...) actions on
|
||||
the parent node.
|
||||
"""
|
||||
|
||||
|
||||
onready var parent = get_parent()
|
||||
|
||||
# If character misses an animation, bypass it and proceed.
|
||||
onready var bypass_missing_animation = false
|
||||
|
||||
var walk_path : Array = []
|
||||
var walk_destination : Vector2
|
||||
var walk_context
|
||||
var moved : bool
|
||||
var path_ofs : float
|
||||
|
||||
var last_deg : int
|
||||
var last_dir : int
|
||||
var last_scale : Vector2
|
||||
var pose_scale : int
|
||||
|
||||
|
||||
|
||||
func _ready():
|
||||
parent.add_user_signal("arrived")
|
||||
|
||||
func _process(time):
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if parent.task == parent.PLAYER_TASKS.WALK or parent.task == parent.PLAYER_TASKS.SLIDE:
|
||||
var pos = parent.get_position()
|
||||
var old_pos = pos
|
||||
var next
|
||||
if walk_path.size() > 1:
|
||||
next = walk_path[path_ofs + 1]
|
||||
else:
|
||||
next = walk_path[path_ofs]
|
||||
|
||||
var dist = parent.speed * time * pow(last_scale.x, 2) * \
|
||||
parent.terrain.player_speed_multiplier
|
||||
if walk_context and "fast" in walk_context and walk_context.fast:
|
||||
dist *= parent.terrain.player_doubleclick_speed_multiplier
|
||||
var dir = (next - pos).normalized()
|
||||
|
||||
# assume that x^2 + y^2 == 1, apply v_speed_damp the y axis
|
||||
#printt("dir before", dir)
|
||||
dir = dir * (dir.x * dir.x + dir.y * dir.y * parent.v_speed_damp)
|
||||
#printt("dir after", dir, dist)
|
||||
|
||||
var new_pos
|
||||
if pos.distance_to(next) < dist:
|
||||
new_pos = next
|
||||
path_ofs += 1
|
||||
else:
|
||||
new_pos = pos + dir * dist
|
||||
|
||||
if path_ofs >= walk_path.size() - 1:
|
||||
walk_stop(walk_destination)
|
||||
return
|
||||
|
||||
pos = new_pos
|
||||
|
||||
var angle = (old_pos.angle_to_point(pos))
|
||||
parent.set_position(pos)
|
||||
|
||||
if parent.task == parent.PLAYER_TASKS.WALK:
|
||||
last_deg = escoria.utils._get_deg_from_rad(angle)
|
||||
last_dir = _get_dir_deg(last_deg, parent.animations)
|
||||
|
||||
var current_animation = ""
|
||||
if parent.animation_sprite != null:
|
||||
current_animation = parent.animation_sprite.animation
|
||||
# elif animation != null:
|
||||
# current_animation = animation.current_animation
|
||||
|
||||
bypass_missing_animation = false
|
||||
if !bypass_missing_animation:
|
||||
var animation_to_play = parent.animations.directions[last_dir][0]
|
||||
if current_animation != animation_to_play:
|
||||
if parent.animation_sprite.frames.has_animation(animation_to_play):
|
||||
parent.animation_sprite.play(animation_to_play)
|
||||
else:
|
||||
bypass_missing_animation = true
|
||||
current_animation = animation_to_play
|
||||
escoria.logger.report_warnings("movable.gd:_process()",
|
||||
["Character " + parent.global_id + " has no animation " + animation_to_play,
|
||||
"Bypassing missing animation and proceed movement."], true)
|
||||
|
||||
pose_scale = parent.animations.directions[last_dir][1]
|
||||
|
||||
update_terrain()
|
||||
else:
|
||||
moved = false
|
||||
set_process(false)
|
||||
|
||||
|
||||
func teleport(target, angle : Object = null) -> void:
|
||||
"""
|
||||
Teleports the item on target position.
|
||||
target can be Vector2 or Object
|
||||
"""
|
||||
if typeof(target) == TYPE_VECTOR2 :
|
||||
escoria.logger.info("Object " + parent.global_id + " teleported at position " +
|
||||
str(target) + " with angle", [angle])
|
||||
parent.position = target
|
||||
elif target is Position2D:
|
||||
escoria.logger.info("Object " + parent.global_id + " teleported at position " +
|
||||
str(target.position) + " with angle", [angle])
|
||||
parent.position = target.position
|
||||
elif typeof(target) == TYPE_OBJECT:
|
||||
# if target.get("interact_positions") != null:
|
||||
# parent.position = target.interact_positions.default #.global_position
|
||||
# else:
|
||||
# parent.position = target.position
|
||||
parent.position = target.get_interact_position()
|
||||
escoria.logger.info("Object " + target.name + " teleported at position "
|
||||
+ str(parent.position) + " with angle ", str(angle))
|
||||
else:
|
||||
escoria.logger.report_errors("escitem.gd:teleport()", ["Target to teleport to is null or unusable (" + target + ")"])
|
||||
|
||||
# PUBLIC FUNCTION
|
||||
func walk_to(pos : Vector2, p_walk_context = null):
|
||||
if not parent.terrain:
|
||||
return walk_stop(parent.get_position())
|
||||
|
||||
if parent.task == parent.PLAYER_TASKS.WALK:
|
||||
if walk_context.has("target_object") and p_walk_context.has("target_object"):
|
||||
if walk_context["target_object"] == p_walk_context["target_object"]:
|
||||
walk_context["fast"] = p_walk_context["fast"]
|
||||
return true
|
||||
elif walk_context.has("target") and p_walk_context.has("target"):
|
||||
if walk_context["target"] == p_walk_context["target"]:
|
||||
walk_context["fast"] = p_walk_context["fast"]
|
||||
return true
|
||||
else:
|
||||
pass
|
||||
if parent.task == parent.PLAYER_TASKS.NONE:
|
||||
parent.task = parent.PLAYER_TASKS.WALK
|
||||
walk_path = parent.terrain.get_terrain_path(parent.get_position(), pos)
|
||||
walk_context = p_walk_context
|
||||
if walk_path.size() == 0:
|
||||
parent.task = parent.PLAYER_TASKS.NONE
|
||||
walk_stop(parent.get_position())
|
||||
set_process(false)
|
||||
return
|
||||
moved = true
|
||||
walk_destination = walk_path[walk_path.size()-1]
|
||||
if parent.terrain.is_solid(pos):
|
||||
walk_destination = walk_path[walk_path.size()-1]
|
||||
path_ofs = 0.0
|
||||
parent.task = parent.PLAYER_TASKS.WALK
|
||||
set_process(true)
|
||||
|
||||
# PRIVATE FUNCTION
|
||||
func walk(target_pos, p_speed, context = null):
|
||||
if p_speed:
|
||||
parent.orig_speed = parent.speed
|
||||
parent.speed = p_speed
|
||||
walk_to(target_pos, context)
|
||||
|
||||
# PRIVATE FUNCTION
|
||||
func walk_stop(pos):
|
||||
parent.position = pos
|
||||
# parent.interact_status = parent.INTERACT_STATES.INTERACT_NONE
|
||||
walk_path = []
|
||||
|
||||
if parent.orig_speed:
|
||||
parent.speed = parent.orig_speed
|
||||
parent.orig_speed = 0.0
|
||||
|
||||
parent.task = parent.PLAYER_TASKS.NONE
|
||||
moved = false
|
||||
set_process(false)
|
||||
if parent.params_queue != null && !parent.params_queue.empty():
|
||||
if parent.animations.dir_angles.size() > 0:
|
||||
if parent.arams_queue[0].interact_angle == -1:
|
||||
escoria.tools.resolve_angle_to(parent.params_queue[0])
|
||||
else:
|
||||
last_dir = _get_dir_deg(parent.params_queue[0].interact_angle, parent.animations)
|
||||
parent.animation_sprite.play(parent.animations.idles[last_dir][0])
|
||||
pose_scale = parent.animations.idles[last_dir][1]
|
||||
update_terrain()
|
||||
else:
|
||||
parent.animation_sprite.play(parent.animations.idles[last_dir][0])
|
||||
pose_scale = parent.animations.idles[last_dir][1]
|
||||
get_tree().call_group_flags(SceneTree.GROUP_CALL_DEFAULT, "game", "interact", parent.params_queue)
|
||||
# Clear params queue to prevent the same action from being triggered again
|
||||
parent.params_queue = []
|
||||
else:
|
||||
|
||||
# If we're heading to an object and reached its interaction position,
|
||||
# orient towards the defined interaction direction set on the object (if any)
|
||||
if walk_context.has("target_object") \
|
||||
and walk_context.target_object.player_orients_on_arrival \
|
||||
and escoria.object_manager.get_object(
|
||||
walk_context.target_object.global_id
|
||||
).interactive:
|
||||
var orientation = walk_context["target_object"].interaction_direction
|
||||
last_dir = orientation
|
||||
parent.animation_sprite.play(parent.animations.idles[orientation][0])
|
||||
pose_scale = parent.animations.idles[orientation][1]
|
||||
|
||||
else:
|
||||
parent.animation_sprite.play(parent.animations.idles[last_dir][0])
|
||||
pose_scale = parent.animations.idles[last_dir][1]
|
||||
update_terrain()
|
||||
|
||||
yield(parent.animation_sprite, "animation_finished")
|
||||
escoria.logger.info(parent.global_id + " arrived at " + str(walk_context))
|
||||
parent.emit_signal("arrived", walk_context)
|
||||
|
||||
|
||||
func update_terrain(on_event_finished_name = null):
|
||||
if !parent.terrain or parent.terrain == null or !is_instance_valid(parent.terrain):
|
||||
return
|
||||
if on_event_finished_name != null and on_event_finished_name != "setup":
|
||||
return
|
||||
if parent.get("is_exit"):
|
||||
return
|
||||
if parent.get("dont_apply_terrain_scaling"):
|
||||
return
|
||||
|
||||
var pos = parent.position
|
||||
parent.z_index = pos.y if pos.y <= VisualServer.CANVAS_ITEM_Z_MAX else VisualServer.CANVAS_ITEM_Z_MAX
|
||||
|
||||
var color
|
||||
if parent.terrain_is_scalenodes:
|
||||
last_scale = parent.terrain.get_terrain(pos)
|
||||
parent.scale = last_scale
|
||||
elif parent.check_maps:
|
||||
color = parent.terrain.get_terrain(pos)
|
||||
var scal = parent.terrain.get_scale_range(color.b)
|
||||
if scal != parent.get_scale():
|
||||
last_scale = scal
|
||||
parent.scale = last_scale
|
||||
|
||||
# Do not flip the entire character, because that would conflict
|
||||
# with shadows that expect to be siblings of $texture
|
||||
if pose_scale == -1 and parent.get_node("sprite").scale.x > 0:
|
||||
parent.get_node("sprite").scale.x *= pose_scale
|
||||
parent.collision.scale.x *= pose_scale
|
||||
elif pose_scale == 1 and parent.get_node("sprite").scale.x < 0:
|
||||
parent.get_node("sprite").scale.x *= -1
|
||||
parent.collision.scale.x *= -1
|
||||
|
||||
# if parent.check_maps:
|
||||
# color = parent.terrain.get_light(pos)
|
||||
#
|
||||
# if color:
|
||||
# for s in sprites:
|
||||
# s.set_modulate(color)
|
||||
|
||||
func _get_dir(angle : float, animations) -> int:
|
||||
var deg = escoria.utils._get_deg_from_rad(angle)
|
||||
return _get_dir_deg(deg, animations)
|
||||
|
||||
|
||||
func _get_dir_deg(deg : int, animations) -> int:
|
||||
# We turn the angle by -90° because angle_to_point gives the angle against X axis, not Y
|
||||
deg = wrapi(deg - 90, 0, 360)
|
||||
var dir = -1
|
||||
var i = 0
|
||||
|
||||
for arr_angle_zone in animations.dir_angles:
|
||||
if is_angle_in_interval(deg, arr_angle_zone):
|
||||
dir = i
|
||||
break
|
||||
else:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# It's an error to have the animations misconfigured
|
||||
if dir == -1:
|
||||
escoria.logger.report_errors("escitem.gd:_get_dir_deg()", ["No direction found for " + str(deg)])
|
||||
|
||||
return dir
|
||||
|
||||
"""
|
||||
Returns true if given angle is inside the interval given by a starting_angle and the size.
|
||||
@param angle : Angle to test
|
||||
@param: interval : Array of size 2, containing the starting angle, and the size of interval
|
||||
eg: [90, 40] corresponds to angle between 90° and 130°
|
||||
"""
|
||||
func is_angle_in_interval(angle: float, interval : Array) -> bool:
|
||||
angle = wrapi(angle, 0, 360)
|
||||
if angle == 0:
|
||||
angle = 360
|
||||
var start_angle = wrapi(interval[0], 0, 360)
|
||||
var angle_area = interval[1]
|
||||
var end_angle = wrapi(interval[0] + angle_area, 0, 360)
|
||||
|
||||
if (angle >= 270 and angle <= 360) or (angle >= 0 and angle <= 90):
|
||||
if wrapi(angle+180, 0, 360) > wrapi(interval[0]+ 180, 0, 360) \
|
||||
&& wrapi(angle+180, 0, 360) <= wrapi(interval[0] + angle_area + 180, 0, 360):
|
||||
return true
|
||||
else:
|
||||
if wrapi(angle, 0, 360) > start_angle && wrapi(angle, 0, 360) <= end_angle:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
"""
|
||||
Sets character's angle and plays according animation.
|
||||
- deg int angle to set the character
|
||||
- immediate bool (currently unused, see TODO below)
|
||||
If true, direction is switched immediately. Else, successive animations are
|
||||
used so that the character turns to target angle.
|
||||
|
||||
TODO: depending on current angle and current angle, the character may directly turn around
|
||||
with no "progression". We may enhance this by calculating successive directions to turn the
|
||||
character to, so that he doesn't switch to opposite direction too fast.
|
||||
For example, if character looks WEST and set_angle(EAST) is called, we may want the character
|
||||
to first turn SOUTHWEST, then SOUTH, then SOUTHEAST and finally EAST, all more or less fast.
|
||||
Whatever the implementation, this should be activated using "parameter "immediate" set to false.
|
||||
"""
|
||||
func set_angle(deg : int, immediate = true):
|
||||
if deg < 0 or deg > 360:
|
||||
escoria.logger.report_errors("movable.gd:set_angle()", ["Invalid degree to turn to " + str(deg)])
|
||||
moved = true
|
||||
last_deg = deg
|
||||
last_dir = _get_dir_deg(deg, parent.animations)
|
||||
|
||||
# The character may have a state animation from before, which would be
|
||||
# resumed, so we immediately force the correct idle animation
|
||||
if parent.animation_sprite.animation != parent.animations.idles[last_dir][0]:
|
||||
parent.animation_sprite.play(parent.animations.idles[last_dir][0])
|
||||
pose_scale = parent.animations.idles[last_dir][1]
|
||||
update_terrain()
|
||||
@@ -1,9 +1,7 @@
|
||||
# `teleport object1 object2 [angle]`
|
||||
# `teleport object1 object2
|
||||
#
|
||||
# Sets the position of object1 to the position of object2. By default,
|
||||
# object2's interact_angle is used to turn object1, but angle will override
|
||||
# this. Useful for doors and such with an interact_angle you don't always want
|
||||
# to adhere to when re-entering a room.
|
||||
# Sets the position of object1 to the position of object2.
|
||||
# FIXME re-add the angle parameter here
|
||||
#
|
||||
# @ESC
|
||||
extends ESCBaseCommand
|
||||
@@ -14,8 +12,8 @@ class_name TeleportCommand
|
||||
func configure() -> ESCCommandArgumentDescriptor:
|
||||
return ESCCommandArgumentDescriptor.new(
|
||||
2,
|
||||
[TYPE_STRING, TYPE_STRING, TYPE_INT],
|
||||
[null, null, null]
|
||||
[TYPE_STRING, TYPE_STRING],
|
||||
[null, null]
|
||||
)
|
||||
|
||||
|
||||
@@ -42,9 +40,8 @@ func validate(arguments: Array):
|
||||
|
||||
# Run the command
|
||||
func run(command_params: Array) -> int:
|
||||
escoria.object_manager.get_object(command_params[0]).node\
|
||||
(escoria.object_manager.get_object(command_params[0]).node as ESCPlayer)\
|
||||
.teleport(
|
||||
escoria.object_manager.get_object(command_params[1]).node,
|
||||
command_params[2]
|
||||
escoria.object_manager.get_object(command_params[1]).node
|
||||
)
|
||||
return ESCExecution.RC_OK
|
||||
|
||||
@@ -19,7 +19,10 @@ func load_command(command_name: String) -> ESCBaseCommand:
|
||||
):
|
||||
if ResourceLoader.exists("%s/%s.gd" % [command_directory, command_name]):
|
||||
registry[command_name] = load(
|
||||
"%s/%s.gd" % [command_directory, command_name]
|
||||
"%s/%s.gd" % [
|
||||
command_directory.trim_suffix("/"),
|
||||
command_name
|
||||
]
|
||||
).new()
|
||||
return registry[command_name]
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ const RESERVED_GLOBALS = [
|
||||
export(Dictionary) var _globals = {}
|
||||
|
||||
|
||||
|
||||
func _init():
|
||||
set_global("ESC_LAST_SCENE", "", true)
|
||||
|
||||
|
||||
# Check if a global was registered
|
||||
#
|
||||
# #### Parameters
|
||||
|
||||
@@ -82,7 +82,7 @@ func is_valid() -> bool:
|
||||
var command_found = false
|
||||
for base_path in ProjectSettings.get("escoria/main/command_directories"):
|
||||
var command_path = "%s/%s.gd" % [
|
||||
base_path,
|
||||
base_path.trim_suffix("/"),
|
||||
self.name
|
||||
]
|
||||
if ResourceLoader.exists(command_path):
|
||||
|
||||
@@ -1,49 +1,53 @@
|
||||
# ESCBackground's purpose is to display a background image and receive input
|
||||
# events on the background. More precisely, the TextureRect under ESCBackground
|
||||
# does not receive events itself - if it did, it would also eat all events like
|
||||
# hotspot focusing and such. Instead, we set the TextureRect mouse filter to
|
||||
# MOUSE_FILTER_IGNORE, and we use an Area2D node to receive the input events.
|
||||
#
|
||||
# If ESCBackground doesn't contain a texture, it is important that its rect_size
|
||||
# is set over the whole scene, because its rect_size is then used to create the
|
||||
# Area2D node under it. If the rect_size is wrongly set, the background may
|
||||
# receive no input.
|
||||
tool
|
||||
extends TextureRect
|
||||
class_name ESCBackground
|
||||
|
||||
func get_class():
|
||||
return "ESCBackground"
|
||||
|
||||
# The background was double clicked
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: The position where the player clicked
|
||||
signal double_left_click_on_bg(position)
|
||||
signal left_click_on_bg(position)
|
||||
signal right_click_on_bg(position)
|
||||
signal mouse_moved
|
||||
|
||||
# The background was left clicked
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: The position where the player clicked
|
||||
signal left_click_on_bg(position)
|
||||
|
||||
# The background was right clicked
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: The position where the player clicked
|
||||
signal right_click_on_bg(position)
|
||||
|
||||
|
||||
# The ESC script connected to this background
|
||||
export(String, FILE, "*.esc") var esc_script = ""
|
||||
|
||||
# Actual size of the scene
|
||||
var size : Vector2
|
||||
|
||||
"""
|
||||
ESCBackground purpose is to display a background image and receive input events
|
||||
on the background. More precisely, the TextureRect under ESCBackground does not
|
||||
receive events itself - if it did, it would also eat all events like hotspot
|
||||
focusing and such. Instead, we set the TextureRect mouse filter to
|
||||
MOUSE_FILTER_IGNORE, and we use an Area2D node to receive the input events.
|
||||
|
||||
If ESCBackground doesn't contain a texture, it is important that its rect_size
|
||||
is set over the whole scene, because its rect_size is then used to create the
|
||||
Area2D node under it. If the rect_size is wrongly set, the background may
|
||||
receive no input.
|
||||
"""
|
||||
|
||||
# PRIVATE VARS
|
||||
var area : Area2D
|
||||
var actual_click_position : Vector2
|
||||
|
||||
# Godot doesn't do doubleclicks so we must
|
||||
var last_lmb_dt = 0
|
||||
var waiting_dblclick = null # null or [pos, event]
|
||||
|
||||
# Create the underlying Area2D as an input device
|
||||
func _enter_tree():
|
||||
# Use size of background texture to calculate collision shape if any
|
||||
var size
|
||||
if get_texture():
|
||||
size = get_texture().get_size()
|
||||
else:
|
||||
size = rect_size
|
||||
|
||||
area = Area2D.new()
|
||||
var area = Area2D.new()
|
||||
var shape = RectangleShape2D.new()
|
||||
|
||||
var sid = area.create_shape_owner(area)
|
||||
@@ -53,24 +57,34 @@ func _enter_tree():
|
||||
transform.origin = size / 2
|
||||
area.shape_owner_set_transform(sid, transform)
|
||||
|
||||
# Set extents of RectangleShape2D to cover entire Sprite
|
||||
# Set extents of RectangleShape2D to cover entire TextureRect
|
||||
shape.set_extents(size / 2)
|
||||
area.shape_owner_add_shape(sid, shape)
|
||||
|
||||
# Handle inputs to the Area2D ourself
|
||||
area.connect("input_event", self, "manage_input")
|
||||
|
||||
add_child(area)
|
||||
|
||||
# Disable mouse filter events and connect our own events to the ESC input
|
||||
# manager
|
||||
func _ready():
|
||||
mouse_filter = MOUSE_FILTER_IGNORE
|
||||
area.connect("input_event", self, "manage_input")
|
||||
connect("gui_input", self, "manage_input_texturerect")
|
||||
|
||||
|
||||
if !Engine.is_editor_hint():
|
||||
connect("left_click_on_bg", escoria.inputs_manager, "_on_left_click_on_bg")
|
||||
connect("right_click_on_bg", escoria.inputs_manager, "_on_right_click_on_bg")
|
||||
connect("double_left_click_on_bg", escoria.inputs_manager, "_on_double_left_click_on_bg")
|
||||
# connect("mouse_moved_on_bg", escoria.inputs_manager, "_on_mouse_moved_on_bg")
|
||||
|
||||
func manage_input(_viewport, event, _shape_idx):
|
||||
|
||||
# Manage inputs reaching the Area2D and emit the events to the input manager
|
||||
# TODO: Don't change private variables here, use an event for BUTTON_WHEEL
|
||||
#
|
||||
# #### Parameters
|
||||
# - _viewport: (not used)
|
||||
# - event: Event received
|
||||
# - _shape_idx: (not used)
|
||||
func manage_input(_viewport, event, _shape_idx) -> void:
|
||||
if event.is_action_pressed("switch_action_verb"):
|
||||
if event.button_index == BUTTON_WHEEL_UP:
|
||||
escoria.inputs_manager._on_mousewheel_action(-1)
|
||||
@@ -87,21 +101,11 @@ func manage_input(_viewport, event, _shape_idx):
|
||||
emit_signal("left_click_on_bg", p)
|
||||
if event.button_index == BUTTON_RIGHT:
|
||||
emit_signal("right_click_on_bg", p)
|
||||
# elif event is InputEventMouseMotion:
|
||||
# emit_signal("mouse_moved_on_bg")
|
||||
|
||||
|
||||
|
||||
func manage_input_texturerect(event):
|
||||
if event is InputEventMouseButton and event.is_pressed():
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("left_click_on_bg", event.position)
|
||||
if event.button_index == BUTTON_RIGHT:
|
||||
emit_signal("right_click_on_bg", event.position)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
# Calculate the actual area taken by this background depending on its
|
||||
# Texture or set size
|
||||
# **Returns** The correct area size
|
||||
func get_full_area_rect2() -> Rect2:
|
||||
var area_rect2 : Rect2 = Rect2()
|
||||
var pos = get_global_position()
|
||||
251
addons/escoria-core/game/core-scripts/esc_game.gd
Normal file
251
addons/escoria-core/game/core-scripts/esc_game.gd
Normal file
@@ -0,0 +1,251 @@
|
||||
# A base class for ESC game scenes
|
||||
# An extending class can be used in the project settings and is responsible
|
||||
# for managing very basic game features and controls
|
||||
tool
|
||||
extends Node2D
|
||||
class_name ESCGame
|
||||
|
||||
|
||||
# Editor debug modes
|
||||
# NONE - No debugging
|
||||
# MOUSE_TOOLTIP_LIMITS - Visualize the tooltip limits
|
||||
enum EDITOR_GAME_DEBUG_DISPLAY {
|
||||
NONE,
|
||||
MOUSE_TOOLTIP_LIMITS
|
||||
}
|
||||
|
||||
|
||||
# The safe margin around tooltips
|
||||
export(float) var mouse_tooltip_margin = 50.0
|
||||
|
||||
|
||||
# A reference to the node handling tooltips
|
||||
var tooltip_node : Object
|
||||
|
||||
|
||||
# Which (if any) debug mode for the editor is used
|
||||
export(EDITOR_GAME_DEBUG_DISPLAY) var editor_debug_mode = \
|
||||
EDITOR_GAME_DEBUG_DISPLAY.NONE setget _set_editor_debug_mode
|
||||
|
||||
|
||||
# Handle debugging visualizations
|
||||
func _draw():
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
if editor_debug_mode == EDITOR_GAME_DEBUG_DISPLAY.NONE:
|
||||
return
|
||||
|
||||
if editor_debug_mode == EDITOR_GAME_DEBUG_DISPLAY.MOUSE_TOOLTIP_LIMITS:
|
||||
var mouse_limits : Rect2 = get_viewport_rect().grow(-mouse_tooltip_margin)
|
||||
print(mouse_limits)
|
||||
|
||||
# Draw lines for tooltip limits
|
||||
draw_rect(mouse_limits, ColorN("red"), false, 10.0)
|
||||
|
||||
|
||||
# Called when the player left clicks on the background
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: Position clicked
|
||||
func left_click_on_bg(position: Vector2) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when the player right clicks on the background
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: Position clicked
|
||||
func right_click_on_bg(position: Vector2) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when the player double clicks on the background
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: Position clicked
|
||||
func left_double_click_on_bg(position: Vector2) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an element in the scene was focused
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - element_id: Global id of the element focused
|
||||
func element_focused(element_id: String) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when no element is focused anymore
|
||||
# (Needs to be overridden, if supported)
|
||||
func element_unfocused() -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an item was left clicked
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_global_id: Global id of the item that was clicked
|
||||
# - event: The received input event
|
||||
func left_click_on_item(item_global_id: String, event: InputEvent) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an item was right clicked
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_global_id: Global id of the item that was clicked
|
||||
# - event: The received input event
|
||||
func right_click_on_item(item_global_id: String, event: InputEvent) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an item was double clicked
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_global_id: Global id of the item that was clicked
|
||||
# - event: The received input event
|
||||
func left_double_click_on_item(
|
||||
item_global_id: String,
|
||||
event: InputEvent
|
||||
) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an inventory item was left clicked
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: Global id of the inventory item was clicked
|
||||
# - event: The received input event
|
||||
func left_click_on_inventory_item(
|
||||
inventory_item_global_id: String,
|
||||
event: InputEvent
|
||||
) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an inventory item was right clicked
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: Global id of the inventory item was clicked
|
||||
# - event: The received input event
|
||||
func right_click_on_inventory_item(
|
||||
inventory_item_global_id: String,
|
||||
event: InputEvent
|
||||
) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an inventory item was double clicked
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: Global id of the inventory item was clicked
|
||||
# - event: The received input event
|
||||
func left_double_click_on_inventory_item(
|
||||
inventory_item_global_id: String,
|
||||
event: InputEvent
|
||||
) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when an inventory item was focused
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: Global id of the inventory item that was focused
|
||||
func inventory_item_focused(inventory_item_global_id : String) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when no inventory item is focused anymore
|
||||
# (Needs to be overridden, if supported)
|
||||
func inventory_item_unfocused() -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when the inventory was opened
|
||||
# (Needs to be overridden, if supported)
|
||||
func open_inventory():
|
||||
pass
|
||||
|
||||
|
||||
# Called when the inventory was closed
|
||||
# (Needs to be overridden, if supported)
|
||||
func close_inventory():
|
||||
pass
|
||||
|
||||
|
||||
# Called when the mousewheel was used
|
||||
# (Needs to be overridden, if supported)
|
||||
#
|
||||
# #### Parameter
|
||||
#
|
||||
# - direction: The direction in which the mouse wheel was rotated
|
||||
func mousewheel_action(direction : int):
|
||||
pass
|
||||
|
||||
|
||||
# Called when the UI should be hidden
|
||||
# (Needs to be overridden, if supported)
|
||||
func hide_ui():
|
||||
pass
|
||||
|
||||
|
||||
# Called when the UI should be shown
|
||||
# (Needs to be overridden, if supported)
|
||||
func show_ui():
|
||||
pass
|
||||
|
||||
|
||||
# Function is called if Project setting escoria/ui/tooltip_follows_mouse = true
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_position: Position of the mouse
|
||||
func update_tooltip_following_mouse_position(p_position : Vector2):
|
||||
var corrected_position = p_position
|
||||
|
||||
# clamp TOP
|
||||
if tooltip_node.tooltip_distance_to_edge_top(p_position) <= mouse_tooltip_margin:
|
||||
corrected_position.y = mouse_tooltip_margin
|
||||
|
||||
# clamp BOTTOM
|
||||
if tooltip_node.tooltip_distance_to_edge_bottom(p_position + tooltip_node.rect_size) <= mouse_tooltip_margin:
|
||||
corrected_position.y = escoria.game_size.y - mouse_tooltip_margin - tooltip_node.rect_size.y
|
||||
|
||||
# clamp LEFT
|
||||
if tooltip_node.tooltip_distance_to_edge_left(p_position - tooltip_node.rect_size/2) <= mouse_tooltip_margin:
|
||||
corrected_position.x = mouse_tooltip_margin
|
||||
|
||||
# clamp RIGHT
|
||||
if tooltip_node.tooltip_distance_to_edge_right(p_position + tooltip_node.rect_size/2) <= mouse_tooltip_margin:
|
||||
corrected_position.x = escoria.game_size.x - mouse_tooltip_margin - tooltip_node.rect_size.x
|
||||
|
||||
tooltip_node.anchor_right = 0.2
|
||||
tooltip_node.rect_position = corrected_position + tooltip_node.offset_from_cursor
|
||||
|
||||
|
||||
# Set the Editor debug mode
|
||||
func _set_editor_debug_mode(p_editor_debug_mode : int) -> void:
|
||||
editor_debug_mode = p_editor_debug_mode
|
||||
update()
|
||||
93
addons/escoria-core/game/core-scripts/esc_inventory_item.gd
Normal file
93
addons/escoria-core/game/core-scripts/esc_inventory_item.gd
Normal file
@@ -0,0 +1,93 @@
|
||||
# The inventory representation of an ESC item if pickable
|
||||
extends TextureButton
|
||||
class_name ESCInventoryItem
|
||||
|
||||
|
||||
# Signal emitted when the item was left clicked
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_id: Global ID of the clicked item
|
||||
signal mouse_left_inventory_item(item_id)
|
||||
|
||||
# Signal emitted when the item was right clicked
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_id: Global ID of the clicked item
|
||||
signal mouse_right_inventory_item(item_id)
|
||||
|
||||
# Signal emitted when the item was double clicked
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_id: Global ID of the clicked item
|
||||
signal mouse_double_left_inventory_item(item_id)
|
||||
|
||||
# Signal emitted when the item was focused
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item_id: Global ID of the clicked item
|
||||
signal inventory_item_focused(item_id)
|
||||
|
||||
# Signal emitted when the item is not focused anymore
|
||||
signal inventory_item_unfocused()
|
||||
|
||||
|
||||
# Global ID of the ESCItem that uses this ESCInventoryItem
|
||||
# Will be set by ESCItem automatically
|
||||
var global_id
|
||||
|
||||
|
||||
# Connect input handlers
|
||||
func _ready():
|
||||
connect("gui_input", self, "_on_inventory_item_gui_input")
|
||||
connect("mouse_entered", self, "_on_inventory_item_mouse_enter")
|
||||
connect("mouse_exited", self, "_on_inventory_item_mouse_exit")
|
||||
|
||||
|
||||
# Handle the gui input and emit the respective signals
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - event: The event received
|
||||
func _on_inventory_item_gui_input(event : InputEvent):
|
||||
if event.is_action_pressed("switch_action_verb"):
|
||||
if event.button_index == BUTTON_WHEEL_UP:
|
||||
escoria.inputs_manager._on_mousewheel_action(-1)
|
||||
elif event.button_index == BUTTON_WHEEL_DOWN:
|
||||
escoria.inputs_manager._on_mousewheel_action(1)
|
||||
if event is InputEventMouseButton:
|
||||
# var p = get_global_mouse_position()
|
||||
if event.doubleclick:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal(
|
||||
"mouse_double_left_inventory_item",
|
||||
global_id,
|
||||
event
|
||||
)
|
||||
else:
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal(
|
||||
"mouse_left_inventory_item",
|
||||
global_id,
|
||||
event
|
||||
)
|
||||
if event.button_index == BUTTON_RIGHT:
|
||||
emit_signal(
|
||||
"mouse_right_inventory_item",
|
||||
global_id,
|
||||
event
|
||||
)
|
||||
|
||||
|
||||
# Handle mouse entering the item and send the respecitve signal
|
||||
func _on_inventory_item_mouse_enter():
|
||||
emit_signal("inventory_item_focused", global_id)
|
||||
|
||||
|
||||
# Handle mouse leaving the item and send the respecitve signal
|
||||
func _on_inventory_item_mouse_exit():
|
||||
emit_signal("inventory_item_unfocused")
|
||||
349
addons/escoria-core/game/core-scripts/esc_item.gd
Normal file
349
addons/escoria-core/game/core-scripts/esc_item.gd
Normal file
@@ -0,0 +1,349 @@
|
||||
# ESCItem is a Sprite that defines an item, potentially interactive
|
||||
tool
|
||||
extends Area2D
|
||||
class_name ESCItem
|
||||
|
||||
|
||||
# Emitted when the mouse has entered this item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - items: The inventory item node
|
||||
signal mouse_entered_item(item)
|
||||
|
||||
# Emitted when the mouse has exited this item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - items: The inventory item node
|
||||
signal mouse_exited_item(item)
|
||||
|
||||
# Emitted when the item was left cliced
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - global_id: ID of this item
|
||||
signal mouse_left_clicked_item(global_id)
|
||||
|
||||
# Emitted when the item was double cliced
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - global_id: ID of this item
|
||||
signal mouse_double_left_clicked_item(global_id)
|
||||
|
||||
# Emitted when the item was right cliced
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - global_id: ID of this item
|
||||
signal mouse_right_clicked_item(global_id)
|
||||
|
||||
# Emitted when the item walked to a destination
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - walk_context: The walk context of the command
|
||||
signal arrived(walk_context)
|
||||
|
||||
|
||||
# The global ID of this item
|
||||
export(String) var global_id
|
||||
|
||||
# The ESC script for this item
|
||||
export(String, FILE, "*.esc") var esc_script
|
||||
|
||||
# If true, the ESC script may have an ":exit_scene" event to manage scene changes
|
||||
export(bool) var is_exit
|
||||
|
||||
# If true, object is considered as trigger. Allows using :trigger_in and
|
||||
# :trigger_out verbs in ESC scripts.
|
||||
export(bool) var is_trigger
|
||||
|
||||
# The verb used for the trigger in ESC events
|
||||
export(String) var trigger_in_verb = "trigger_in"
|
||||
|
||||
# The verb used for the trigger out ESC events
|
||||
export(String) var trigger_out_verb = "trigger_out"
|
||||
|
||||
# If true, the player can interact with this item
|
||||
export(bool) var is_interactive = true
|
||||
|
||||
# If true, player orients towards 'interaction_direction' as
|
||||
# player character arrives.
|
||||
export(bool) var player_orients_on_arrival = true
|
||||
|
||||
# Let the player turn to this direction when the player arrives at the
|
||||
# item
|
||||
export(int) var interaction_direction
|
||||
|
||||
# The name for the tooltip of this item
|
||||
export(String) var tooltip_name
|
||||
|
||||
# Default action to use if object is not in the inventory
|
||||
export(String) var default_action
|
||||
|
||||
# Default action to use if object is in the inventory
|
||||
export(String) var default_action_inventory
|
||||
|
||||
# If action used by player is in this list, the game will wait for a second
|
||||
# click on another item to combine objects together (typical
|
||||
# `USE <X> WITH <Y>`, `GIVE <X> TO <Y>`)
|
||||
export(PoolStringArray) var combine_if_action_used_among = []
|
||||
|
||||
# If true, combination must be done in the way it is written in ESC script
|
||||
# ie. :use ON_ITEM
|
||||
# If false, combination will be tried in the other way.
|
||||
export(bool) var combine_is_one_way = false
|
||||
|
||||
# If true, then the object must have been picked up before using it.
|
||||
# A false value is useful for items in the background, such as buttons.
|
||||
export(bool) var use_from_inventory_only = false
|
||||
|
||||
# Scene based on ESCInventoryItem used in inventory for the object if it is
|
||||
# picked up, that displays and handles the item
|
||||
export(PackedScene) var inventory_item_scene_file : PackedScene
|
||||
|
||||
# Color used for dialogs
|
||||
export(Color) var dialog_color = ColorN("white")
|
||||
|
||||
# If true, terrain scaling will not be applied and
|
||||
# node will remain at the scale set in the scene.
|
||||
export(bool) var dont_apply_terrain_scaling = false
|
||||
|
||||
# Speed of this item ifmovable
|
||||
export(int) var speed : int = 300
|
||||
|
||||
# Speed damp of this item if movable
|
||||
export(float) var v_speed_damp : float = 1.0
|
||||
|
||||
# Animations script (for walking, idling...)
|
||||
export(Script) var animations
|
||||
|
||||
# The movable subnode
|
||||
var movable: ESCMovable = null
|
||||
|
||||
# Reference to the animation node (null if none was found)
|
||||
var animation_sprite = null
|
||||
|
||||
# Reference to the current terrain
|
||||
var terrain: ESCTerrain
|
||||
|
||||
# Reference to this items collision shape node
|
||||
var collision: Node
|
||||
|
||||
# The representation of this item in the scene. Will
|
||||
# be loaded, if inventory_item_scene_file is set.
|
||||
var inventory_item: ESCInventoryItem = null setget ,_get_inventory_item
|
||||
|
||||
|
||||
# Add the movable node, connect signals, detect child nodes
|
||||
# and register this item
|
||||
func _ready():
|
||||
movable = ESCMovable.new()
|
||||
|
||||
add_child(movable)
|
||||
|
||||
_detect_children()
|
||||
|
||||
connect("mouse_entered", self, "_on_mouse_entered")
|
||||
connect("mouse_exited", self, "_on_mouse_exited")
|
||||
connect("input_event", self, "manage_input")
|
||||
|
||||
# Register and connect all elements to Escoria backoffice.
|
||||
if not Engine.is_editor_hint():
|
||||
escoria.event_manager.connect("event_finished", self, "_update_terrain")
|
||||
|
||||
escoria.object_manager.register_object(
|
||||
ESCObject.new(
|
||||
global_id,
|
||||
self
|
||||
),
|
||||
true
|
||||
)
|
||||
|
||||
terrain = escoria.room_terrain
|
||||
|
||||
if !is_trigger:
|
||||
connect("mouse_entered_item", escoria.inputs_manager, "_on_mouse_entered_item")
|
||||
connect("mouse_exited_item", escoria.inputs_manager, "_on_mouse_exited_item")
|
||||
connect("mouse_left_clicked_item", escoria.inputs_manager, "_on_mouse_left_clicked_item")
|
||||
connect("mouse_double_left_clicked_item", escoria.inputs_manager, "_on_mouse_left_double_clicked_item")
|
||||
connect("mouse_right_clicked_item", escoria.inputs_manager, "_on_mouse_right_clicked_item")
|
||||
else:
|
||||
connect("area_entered", self, "element_entered")
|
||||
connect("area_exited", self, "element_exited")
|
||||
connect("body_entered", self, "element_entered")
|
||||
connect("body_exited", self, "element_exited")
|
||||
|
||||
# If object can be in the inventory, set default_action_inventory to same as
|
||||
# default_action, if default_action_inventory is not set
|
||||
if use_from_inventory_only and default_action_inventory.empty():
|
||||
default_action_inventory = default_action
|
||||
|
||||
# Perform a first terrain scaling if we have to.
|
||||
if !is_exit or dont_apply_terrain_scaling:
|
||||
movable.last_scale = scale
|
||||
movable.update_terrain()
|
||||
|
||||
|
||||
# Return the animation player node
|
||||
func get_animation_player():
|
||||
return animation_sprite
|
||||
|
||||
|
||||
# Return the position the player needs to walk to to interact with this
|
||||
# item. That can either be a direct Position2D child or a collision shape
|
||||
#
|
||||
# **Returns** The interaction position
|
||||
func get_interact_position() -> Vector2:
|
||||
var interact_position = null
|
||||
for c in get_children():
|
||||
if c is Position2D:
|
||||
if c.get_owner() == self:
|
||||
continue
|
||||
interact_position = c.global_position
|
||||
|
||||
if interact_position == null and collision != null:
|
||||
interact_position = collision.global_position
|
||||
|
||||
return interact_position
|
||||
|
||||
|
||||
# Manage mouse button clicks on this item by sending out signals
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - _viewport: not used
|
||||
# - event: Triggered event
|
||||
# - _shape_idx: not used
|
||||
func manage_input(
|
||||
_viewport : Viewport,
|
||||
event : InputEvent,
|
||||
_shape_idx : int
|
||||
) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
|
||||
if event.doubleclick:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("mouse_double_left_clicked_item", self, event)
|
||||
else:
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("mouse_left_clicked_item", self, event)
|
||||
elif event.button_index == BUTTON_RIGHT:
|
||||
emit_signal("mouse_right_clicked_item", self, event)
|
||||
|
||||
|
||||
# React to the mouse entering the item by emitting the respective signal
|
||||
func _on_mouse_entered():
|
||||
emit_signal("mouse_entered_item", self)
|
||||
|
||||
|
||||
# React to the mouse exiting the item by emitting the respective signal
|
||||
func _on_mouse_exited():
|
||||
emit_signal("mouse_exited_item", self)
|
||||
|
||||
|
||||
# Another item (e.g. the player) has entered this item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - body: Other object that has entered the item
|
||||
func element_entered(body):
|
||||
if body is ESCBackground or body.get_parent() is ESCBackground:
|
||||
return
|
||||
escoria.do("trigger_in", [global_id, body.global_id, trigger_in_verb])
|
||||
|
||||
|
||||
# Another item (e.g. the player) has exited this element
|
||||
# #### Parameters
|
||||
#
|
||||
# - body: Other object that has entered the item
|
||||
func element_exited(body):
|
||||
if body is ESCBackground or body.get_parent() is ESCBackground:
|
||||
return
|
||||
escoria.do("trigger_out", [global_id, body.global_id, trigger_out_verb])
|
||||
|
||||
|
||||
# Use the movable node to teleport this item to the target item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - target: Target item to teleport to
|
||||
func teleport(target: Node) -> void:
|
||||
movable.teleport(target)
|
||||
|
||||
|
||||
# Use the movable node to make the item walk to the given position
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - pos: Position to walk to
|
||||
# - p_walk_context: Walk context to use
|
||||
func walk_to(pos : Vector2, p_walk_context: ESCWalkContext = null) -> void:
|
||||
movable.walk_to(pos, p_walk_context)
|
||||
|
||||
|
||||
# Set the moving speed
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - speed_value: Set the new speed
|
||||
func set_speed(speed_value : int) -> void:
|
||||
speed = speed_value
|
||||
|
||||
|
||||
# Set the angle
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# Set the angle
|
||||
func set_angle(deg : int, immediate = true):
|
||||
movable.set_angle(deg, immediate)
|
||||
|
||||
|
||||
# Play the talking animation
|
||||
func start_talking():
|
||||
if animation_sprite.is_playing():
|
||||
animation_sprite.stop()
|
||||
animation_sprite.play(animations.speaks[movable.last_dir][0])
|
||||
|
||||
|
||||
# Stop playing the talking animation
|
||||
func stop_talking():
|
||||
if animation_sprite.is_playing():
|
||||
animation_sprite.stop()
|
||||
animation_sprite.play(animations.idles[movable.last_dir][0])
|
||||
|
||||
|
||||
# Detect the child nodes and set respective references
|
||||
func _detect_children() -> void:
|
||||
# Detect animation player
|
||||
for n in get_children():
|
||||
if n is AnimatedSprite:
|
||||
animation_sprite = n
|
||||
continue
|
||||
if n is AnimationPlayer:
|
||||
animation_sprite = n
|
||||
continue
|
||||
|
||||
# Initialize collision variable.
|
||||
for c in get_children():
|
||||
if c is CollisionShape2D or c is CollisionPolygon2D:
|
||||
collision = c
|
||||
|
||||
|
||||
# Upate the terrain when an event finished
|
||||
func _update_terrain(rc: int, event_name: String) -> void:
|
||||
movable.update_terrain(event_name)
|
||||
|
||||
|
||||
# Get inventory item from the inventory item scene
|
||||
# **Returns** The inventory item of this ESCitem
|
||||
func _get_inventory_item() -> ESCInventoryItem:
|
||||
if not inventory_item and inventory_item_scene_file:
|
||||
inventory_item = inventory_item_scene_file.instance()
|
||||
inventory_item.global_id = self.global_id
|
||||
return inventory_item
|
||||
|
||||
20
addons/escoria-core/game/core-scripts/esc_player.gd
Normal file
20
addons/escoria-core/game/core-scripts/esc_player.gd
Normal file
@@ -0,0 +1,20 @@
|
||||
# A playable character
|
||||
# TODO
|
||||
# - Currently the sprite node needs to be named "sprite". This is bad.
|
||||
# - Animation management doesn't allow using AnimationPlayer yet. Need to find
|
||||
# the best solution to manage both AnimatedSprite and AnimationPlayer.
|
||||
tool
|
||||
extends ESCItem
|
||||
class_name ESCPlayer
|
||||
|
||||
|
||||
# The node that references the camera position
|
||||
export(NodePath) var camera_position_node
|
||||
|
||||
|
||||
# Return the camera position if a camera_position_node exists or the
|
||||
# global position of the player
|
||||
func get_camera_pos():
|
||||
if camera_position_node and get_node(camera_position_node):
|
||||
return get_node(camera_position_node).global_position
|
||||
return global_position
|
||||
@@ -1,6 +1,6 @@
|
||||
# A cache for resources
|
||||
extends Object
|
||||
class_name ResourceCache
|
||||
class_name ESCResourceCache
|
||||
|
||||
var thread : Thread
|
||||
var mutex : Mutex
|
||||
@@ -1,41 +1,62 @@
|
||||
# A room in an Escora based game
|
||||
tool
|
||||
extends Node2D
|
||||
class_name ESCRoom
|
||||
|
||||
func get_class():
|
||||
return "ESCRoom"
|
||||
|
||||
export(String) var global_id = ""
|
||||
export(String, FILE, "*.esc") var esc_script = ""
|
||||
export(PackedScene) var player_scene
|
||||
export(Array, Rect2) var camera_limits : Array = [Rect2()] setget set_camera_limits
|
||||
var player
|
||||
onready var game = $game
|
||||
|
||||
### EDITOR TOOLS ###
|
||||
enum EDITOR_ROOM_DEBUG_DISPLAY {
|
||||
# Debugging displays for a room
|
||||
# NONE: No debug display
|
||||
# CAMERA_LIMITS: Display the camera limits
|
||||
enum EditorRoomDebugDisplay {
|
||||
NONE,
|
||||
CAMERA_LIMITS
|
||||
}
|
||||
export(EDITOR_ROOM_DEBUG_DISPLAY) var editor_debug_mode = EDITOR_ROOM_DEBUG_DISPLAY.NONE setget set_editor_debug_mode
|
||||
onready var camera_limits_colors : Array = [
|
||||
ColorN("red"), ColorN("blue"), ColorN("green")
|
||||
]
|
||||
### END EDITOR TOOLS ###
|
||||
|
||||
|
||||
# The global id of this room
|
||||
export(String) var global_id = ""
|
||||
|
||||
# The ESC script of this room
|
||||
export(String, FILE, "*.esc") var esc_script = ""
|
||||
|
||||
# The player inside this scene
|
||||
export(PackedScene) var player_scene
|
||||
|
||||
# The camera limits available in this room
|
||||
export(Array, Rect2) var camera_limits : Array = [Rect2()] setget set_camera_limits
|
||||
|
||||
# The editor debug display mode
|
||||
export(int) var editor_debug_mode = EditorRoomDebugDisplay.NONE setget set_editor_debug_mode
|
||||
|
||||
|
||||
# The player scene instance
|
||||
var player
|
||||
|
||||
|
||||
# The game scene instance
|
||||
var game
|
||||
|
||||
|
||||
# Start the random number generator when the camera limits should be displayed
|
||||
func _enter_tree():
|
||||
randomize()
|
||||
if editor_debug_mode == EditorRoomDebugDisplay.CAMERA_LIMITS:
|
||||
randomize()
|
||||
|
||||
|
||||
# Sanitize camera limits, add player node and set the global id to the
|
||||
# name of this node if it's not set manually
|
||||
func _ready():
|
||||
if camera_limits.empty():
|
||||
camera_limits.push_back(Rect2())
|
||||
if camera_limits.size() == 1 and camera_limits[0].has_no_area():
|
||||
camera_limits[0] = Rect2(0, 0, $background.rect_size.x, $background.rect_size.y)
|
||||
camera_limits[0] = \
|
||||
Rect2(0, 0, $background.rect_size.x, $background.rect_size.y)
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
game = $game
|
||||
|
||||
if player_scene:
|
||||
player = player_scene.instance()
|
||||
add_child(player)
|
||||
@@ -59,12 +80,18 @@ func _ready():
|
||||
|
||||
if global_id.empty():
|
||||
global_id = name
|
||||
|
||||
|
||||
|
||||
# Draw the camera limits visualization if enabled
|
||||
func _draw():
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
if editor_debug_mode == EDITOR_ROOM_DEBUG_DISPLAY.NONE:
|
||||
if editor_debug_mode == EditorRoomDebugDisplay.NONE:
|
||||
return
|
||||
|
||||
var camera_limits_colors : Array = [
|
||||
ColorN("red"), ColorN("blue"), ColorN("green")
|
||||
]
|
||||
|
||||
# If there are more camera limits than colors defined for them, add more.
|
||||
if camera_limits.size() > camera_limits_colors.size():
|
||||
@@ -79,12 +106,22 @@ func _draw():
|
||||
draw_string(default_font, Vector2(camera_limits[i].position.x + 30,
|
||||
camera_limits[i].position.y + 30), str(i), camera_limits_colors[i])
|
||||
|
||||
return
|
||||
|
||||
func set_camera_limits(p_camera_limits : Array) -> void:
|
||||
# Set the camera limits
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_camera_limits: An array of Rect2Ds as camera limits
|
||||
func set_camera_limits(p_camera_limits: Array) -> void:
|
||||
camera_limits = p_camera_limits
|
||||
update()
|
||||
|
||||
func set_editor_debug_mode(p_editor_debug_mode : int) -> void:
|
||||
|
||||
# Set the editor debug mode
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_editor_debug_mode: The debug mode to set for the room
|
||||
func set_editor_debug_mode(p_editor_debug_mode: int) -> void:
|
||||
editor_debug_mode = p_editor_debug_mode
|
||||
update()
|
||||
228
addons/escoria-core/game/core-scripts/esc_terrain.gd
Normal file
228
addons/escoria-core/game/core-scripts/esc_terrain.gd
Normal file
@@ -0,0 +1,228 @@
|
||||
# A walkable Terrains
|
||||
tool
|
||||
extends Navigation2D
|
||||
class_name ESCTerrain
|
||||
|
||||
|
||||
# Visualize scales or the lightmap for debugging purposes
|
||||
enum DebugMode {
|
||||
NONE
|
||||
SCALES
|
||||
LIGHTMAP
|
||||
}
|
||||
|
||||
|
||||
# Scaling texture
|
||||
export(Texture) var scales setget _set_scales
|
||||
|
||||
# Minimum scaling
|
||||
export(float) var scale_min = 0.3
|
||||
|
||||
# Maximum scaling
|
||||
export(float) var scale_max = 1.0
|
||||
|
||||
# Lightmap texture
|
||||
export(Texture) var lightmap setget _set_lightmap
|
||||
|
||||
# The scaling factor for the scale and light maps
|
||||
export(Vector2) var bitmaps_scale = Vector2(1,1) setget _set_bm_scale
|
||||
|
||||
# Multiplier applied to the player speed on this terrain
|
||||
export(float) var player_speed_multiplier = 1.0
|
||||
|
||||
# Multiplier how much faster the player will walk when fast mode is on
|
||||
# (double clicked)
|
||||
export(float) var player_doubleclick_speed_multiplier = 1.5
|
||||
|
||||
# Additional modulator to the lightmap texture
|
||||
export(Color) var lightmap_modulate = Color(1, 1, 1, 1)
|
||||
|
||||
# Currently selected debug visualize mode
|
||||
export(int, "None", "Scales", "Lightmap") var debug_mode = DebugMode.NONE \
|
||||
setget _set_debug_mode
|
||||
|
||||
|
||||
# The currently activ navigation polygon
|
||||
var current_active_navigation_instance: NavigationPolygonInstance = null
|
||||
|
||||
|
||||
# Currently visualized texture for debug mode
|
||||
var _texture = null
|
||||
|
||||
# The image from the lightmap texture
|
||||
var _lightmap_data
|
||||
|
||||
# Prohibits multiple calls to update_texture
|
||||
var _texture_in_update = false
|
||||
|
||||
|
||||
# Set a reference to the active navigation polygon, register to Escoria
|
||||
# and update the texture
|
||||
func _ready():
|
||||
var navigation_enabled_found = false
|
||||
for n in get_children():
|
||||
if n is NavigationPolygonInstance:
|
||||
if n.enabled:
|
||||
if navigation_enabled_found:
|
||||
escoria.logger.report_errors(
|
||||
"ESCTerrain:_ready()",
|
||||
[
|
||||
"Multiple NavigationPolygonInstances enabled " + \
|
||||
"at the same time."
|
||||
]
|
||||
)
|
||||
navigation_enabled_found = true
|
||||
current_active_navigation_instance = n
|
||||
|
||||
if !Engine.is_editor_hint():
|
||||
escoria.room_terrain = self
|
||||
_update_texture()
|
||||
|
||||
|
||||
# Return the Color of the lightmap pixel for the specified position
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - pos: Position to calculate lightmap for
|
||||
# **Returns** The color of the given point
|
||||
func get_light(pos: Vector2) -> Color:
|
||||
if not lightmap or lightmap.get_data().is_empty():
|
||||
return Color(1, 1, 1, 1)
|
||||
var c = _get_color(_lightmap_data, pos)
|
||||
return _get_color(_lightmap_data, pos) * lightmap_modulate
|
||||
|
||||
|
||||
# Calculate the scale inside the scale range for a given scale factor
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - factor: The factor for the scaling according to the scale map
|
||||
# **Returns** The scaling
|
||||
func get_scale_range(factor: float) -> Vector2:
|
||||
factor = scale_min + (scale_max - scale_min) * factor
|
||||
return Vector2(factor, factor)
|
||||
|
||||
|
||||
# Get the terrain scale factor for a given position
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - pos: The position to calculate for
|
||||
# **Returns** The scale factor for the given position
|
||||
func get_terrain(pos: Vector2) -> float:
|
||||
if scales == null || scales.get_data().is_empty():
|
||||
return 1.0
|
||||
return _get_color(scales.get_data(), pos).v
|
||||
|
||||
|
||||
# Small helper to get the color of an image at a position
|
||||
func _get_color(image: Image, pos: Vector2) -> Color:
|
||||
image.lock()
|
||||
var color=image.get_pixel(pos.x, pos.y)
|
||||
image.unlock()
|
||||
return color
|
||||
|
||||
|
||||
# Set the bitmap scaling
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_scale: Scale to set
|
||||
func _set_bm_scale(p_scale: Vector2):
|
||||
bitmaps_scale = p_scale
|
||||
_update_texture()
|
||||
|
||||
|
||||
# Set the lightmap texture
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_lightmap: Lightmap texture to set
|
||||
func _set_lightmap(p_lightmap: Texture):
|
||||
var need_init = (lightmap != p_lightmap) or (lightmap and not _lightmap_data)
|
||||
|
||||
lightmap = p_lightmap
|
||||
|
||||
# It's bad enough a new copy is created when reading a pixel, we don't
|
||||
# also need to get the data for every read to make yet another copy
|
||||
if need_init:
|
||||
if _lightmap_data:
|
||||
_lightmap_data.unlock()
|
||||
_lightmap_data = lightmap.get_data()
|
||||
_lightmap_data.lock()
|
||||
|
||||
_update_texture()
|
||||
|
||||
|
||||
# Set the scales texture
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_scales: Scale texture to set
|
||||
func _set_scales(p_scales: Texture):
|
||||
scales = p_scales
|
||||
_update_texture()
|
||||
|
||||
|
||||
# Set the debug mode
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_mode: Debug mode to set
|
||||
func _set_debug_mode(p_mode: int):
|
||||
debug_mode = p_mode
|
||||
_update_texture()
|
||||
|
||||
|
||||
# Update the debug texture, if it is dirty
|
||||
func _update_texture():
|
||||
if _texture_in_update:
|
||||
return
|
||||
_texture_in_update = true
|
||||
call_deferred("_do_update_texture")
|
||||
|
||||
|
||||
# Update the texture and optionally set the debug texture
|
||||
func _do_update_texture():
|
||||
_texture_in_update = false
|
||||
if !is_inside_tree() or !Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if debug_mode == DebugMode.NONE:
|
||||
update()
|
||||
return
|
||||
|
||||
_texture = ImageTexture.new()
|
||||
if debug_mode == DebugMode.SCALES:
|
||||
if scales != null:
|
||||
_texture = scales
|
||||
elif debug_mode == DebugMode.LIGHTMAP:
|
||||
if lightmap != null:
|
||||
_texture = lightmap
|
||||
|
||||
update()
|
||||
|
||||
|
||||
# Draw debugging visualizations
|
||||
func _draw():
|
||||
if _texture == null or \
|
||||
not Engine.is_editor_hint() or \
|
||||
debug_mode == DebugMode.NONE:
|
||||
if current_active_navigation_instance:
|
||||
current_active_navigation_instance.visible = true
|
||||
return
|
||||
|
||||
var scale_vect = bitmaps_scale
|
||||
|
||||
if current_active_navigation_instance:
|
||||
current_active_navigation_instance.visible = false
|
||||
|
||||
var src = Rect2(0, 0, _texture.get_width(), _texture.get_height())
|
||||
var dst = Rect2(
|
||||
0,
|
||||
0,
|
||||
_texture.get_width() * scale_vect.x,
|
||||
_texture.get_height() * scale_vect.y
|
||||
)
|
||||
|
||||
draw_texture_rect_region(_texture, dst, src)
|
||||
24
addons/escoria-core/game/core-scripts/esc_walk_context.gd
Normal file
24
addons/escoria-core/game/core-scripts/esc_walk_context.gd
Normal file
@@ -0,0 +1,24 @@
|
||||
# The walk context describes the target of a walk command and if that command
|
||||
# should be executed fast
|
||||
extends Object
|
||||
class_name ESCWalkContext
|
||||
|
||||
|
||||
# Target object that the walk command tries to reach
|
||||
var target_object: ESCObject = null
|
||||
|
||||
# The target position
|
||||
var target_position: Vector2 = Vector2()
|
||||
|
||||
# Wether to move fast
|
||||
var fast: bool
|
||||
|
||||
|
||||
func _init(
|
||||
p_target_object: ESCObject,
|
||||
p_target_position: Vector2,
|
||||
p_fast: bool
|
||||
):
|
||||
target_object = p_target_object
|
||||
target_position = p_target_position
|
||||
fast = p_fast
|
||||
@@ -1,129 +0,0 @@
|
||||
tool
|
||||
extends Node2D
|
||||
class_name ESCGame
|
||||
|
||||
func get_class():
|
||||
return "ESCGame"
|
||||
|
||||
|
||||
export(float) var mouse_tooltip_margin = 50.0
|
||||
var tooltip_node : Object
|
||||
|
||||
### EDITOR TOOLS ###
|
||||
enum EDITOR_GAME_DEBUG_DISPLAY {
|
||||
NONE,
|
||||
MOUSE_TOOLTIP_LIMITS
|
||||
}
|
||||
export(EDITOR_GAME_DEBUG_DISPLAY) var editor_debug_mode = EDITOR_GAME_DEBUG_DISPLAY.NONE setget set_editor_debug_mode
|
||||
|
||||
|
||||
|
||||
func set_editor_debug_mode(p_editor_debug_mode : int) -> void:
|
||||
editor_debug_mode = p_editor_debug_mode
|
||||
update()
|
||||
|
||||
func _draw():
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
if editor_debug_mode == EDITOR_GAME_DEBUG_DISPLAY.NONE:
|
||||
return
|
||||
|
||||
if editor_debug_mode == EDITOR_GAME_DEBUG_DISPLAY.MOUSE_TOOLTIP_LIMITS:
|
||||
var mouse_limits : Rect2 = get_viewport_rect().grow(-mouse_tooltip_margin)
|
||||
print(mouse_limits)
|
||||
|
||||
# Draw lines for tooltip limits
|
||||
draw_rect(mouse_limits, ColorN("red"), false, 10.0)
|
||||
return
|
||||
|
||||
|
||||
## BACKGROUND ##
|
||||
func left_click_on_bg(position : Vector2) -> void:
|
||||
pass
|
||||
|
||||
func right_click_on_bg(position : Vector2) -> void:
|
||||
pass
|
||||
|
||||
func left_double_click_on_bg(position : Vector2) -> void:
|
||||
pass
|
||||
|
||||
## ITEM/HOTSPOT FOCUS ##
|
||||
func element_focused(element_id : String) -> void:
|
||||
pass
|
||||
|
||||
func element_unfocused() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## ITEMS ##
|
||||
func left_click_on_item(item_global_id : String, event : InputEvent) -> void:
|
||||
pass
|
||||
|
||||
func right_click_on_item(item_global_id : String, event : InputEvent) -> void:
|
||||
pass
|
||||
|
||||
func left_double_click_on_item(item_global_id : String, event : InputEvent) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## INVENTORY ##
|
||||
func left_click_on_inventory_item(inventory_item_global_id : String, event : InputEvent) -> void:
|
||||
pass
|
||||
|
||||
func right_click_on_inventory_item(inventory_item_global_id : String, event : InputEvent) -> void:
|
||||
pass
|
||||
|
||||
func left_double_click_on_inventory_item(inventory_item_global_id : String, event : InputEvent) -> void:
|
||||
pass
|
||||
|
||||
func inventory_item_focused(inventory_item_global_id : String) -> void:
|
||||
pass
|
||||
|
||||
func inventory_item_unfocused() -> void:
|
||||
pass
|
||||
|
||||
func open_inventory():
|
||||
pass
|
||||
|
||||
func close_inventory():
|
||||
pass
|
||||
|
||||
## MOUSEWHEEL ACTION ##
|
||||
func mousewheel_action(direction : int):
|
||||
pass
|
||||
|
||||
|
||||
## UI SPECIFICS
|
||||
func hide_ui():
|
||||
pass
|
||||
|
||||
func show_ui():
|
||||
pass
|
||||
|
||||
|
||||
|
||||
## FUNCTIONS BELOW THIS POINT DON'T NEED TO BE REIMPLEMENTED BY USER
|
||||
## (Although they can be, if required)
|
||||
|
||||
# This function is called if Project setting escoria/ui/tooltip_follows_mouse = true
|
||||
func update_tooltip_following_mouse_position(p_position : Vector2):
|
||||
var corrected_position = p_position
|
||||
|
||||
# clamp TOP
|
||||
if tooltip_node.tooltip_distance_to_edge_top(p_position) <= mouse_tooltip_margin:
|
||||
corrected_position.y = mouse_tooltip_margin
|
||||
|
||||
# clamp BOTTOM
|
||||
if tooltip_node.tooltip_distance_to_edge_bottom(p_position + tooltip_node.rect_size) <= mouse_tooltip_margin:
|
||||
corrected_position.y = escoria.game_size.y - mouse_tooltip_margin - tooltip_node.rect_size.y
|
||||
|
||||
# clamp LEFT
|
||||
if tooltip_node.tooltip_distance_to_edge_left(p_position - tooltip_node.rect_size/2) <= mouse_tooltip_margin:
|
||||
corrected_position.x = mouse_tooltip_margin
|
||||
|
||||
# clamp RIGHT
|
||||
if tooltip_node.tooltip_distance_to_edge_right(p_position + tooltip_node.rect_size/2) <= mouse_tooltip_margin:
|
||||
corrected_position.x = escoria.game_size.x - mouse_tooltip_margin - tooltip_node.rect_size.x
|
||||
|
||||
tooltip_node.anchor_right = 0.2
|
||||
tooltip_node.rect_position = corrected_position + tooltip_node.offset_from_cursor
|
||||
@@ -1,45 +0,0 @@
|
||||
extends TextureButton
|
||||
class_name ESCInventoryItem
|
||||
|
||||
func get_class():
|
||||
return "ESCInventoryItem"
|
||||
|
||||
export(String) var global_id
|
||||
|
||||
signal mouse_left_inventory_item(item_id)
|
||||
signal mouse_right_inventory_item(item_id)
|
||||
signal mouse_double_left_inventory_item(item_id)
|
||||
signal inventory_item_focused(item_id)
|
||||
signal inventory_item_unfocused()
|
||||
|
||||
|
||||
func _ready():
|
||||
connect("gui_input", self, "_on_inventory_item_gui_input")
|
||||
connect("mouse_entered", self, "_on_inventory_item_mouse_enter")
|
||||
connect("mouse_exited", self, "_on_inventory_item_mouse_exit")
|
||||
|
||||
func _on_inventory_item_gui_input(event : InputEvent):
|
||||
if event.is_action_pressed("switch_action_verb"):
|
||||
if event.button_index == BUTTON_WHEEL_UP:
|
||||
escoria.inputs_manager._on_mousewheel_action(-1)
|
||||
elif event.button_index == BUTTON_WHEEL_DOWN:
|
||||
escoria.inputs_manager._on_mousewheel_action(1)
|
||||
if event is InputEventMouseButton:
|
||||
# var p = get_global_mouse_position()
|
||||
if event.doubleclick:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("mouse_double_left_inventory_item", global_id, event)
|
||||
else:
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("mouse_left_inventory_item", global_id, event)
|
||||
if event.button_index == BUTTON_RIGHT:
|
||||
emit_signal("mouse_right_inventory_item", global_id, event)
|
||||
|
||||
func _on_inventory_item_mouse_enter():
|
||||
# Notify UI that item is focused (room.game.ui.Label UI)
|
||||
emit_signal("inventory_item_focused", global_id)
|
||||
|
||||
func _on_inventory_item_mouse_exit():
|
||||
# Notify UI that item is unfocused (room.game.ui.Label UI)
|
||||
emit_signal("inventory_item_unfocused")
|
||||
@@ -1,269 +0,0 @@
|
||||
tool
|
||||
extends Area2D
|
||||
class_name ESCItem
|
||||
|
||||
"""
|
||||
ESCItem is a Sprite that defines an item, potentially interactive
|
||||
"""
|
||||
|
||||
signal mouse_entered_item(item)
|
||||
signal mouse_exited_item(item)
|
||||
signal mouse_left_clicked_item(global_id)
|
||||
signal mouse_double_left_clicked_item(global_id)
|
||||
signal mouse_right_clicked_item(global_id)
|
||||
|
||||
var Movable
|
||||
var MovableScript = load("res://addons/escoria-core/game/core-scripts/behaviors/movable.gd")
|
||||
|
||||
export(String) var global_id
|
||||
export(String, FILE, "*.esc") var esc_script
|
||||
|
||||
# If true, the ESC script may have an ":exit_scene" event to manage scene changes
|
||||
export(bool) var is_exit
|
||||
|
||||
# is_trigger If true, object is considered as trigger. Allows using :trigger_in and
|
||||
# :trigger_out verbs in ESC scripts.
|
||||
export(bool) var is_trigger
|
||||
export(String) var trigger_in_verb = "trigger_in"
|
||||
export(String) var trigger_out_verb = "trigger_out"
|
||||
|
||||
# is_interactive : If true, object is not "focusable".
|
||||
export(bool) var is_interactive = true
|
||||
|
||||
# player_orients_on_arrival : If true, player orients towards 'interaction_direction' as
|
||||
# player character arrives.
|
||||
export(bool) var player_orients_on_arrival = true
|
||||
|
||||
export(ESCPlayer.Directions) var interaction_direction
|
||||
export(String) var tooltip_name
|
||||
|
||||
# Default action to use if object is not in the inventory
|
||||
export(String) var default_action
|
||||
# Default action to use if object is in the inventory
|
||||
export(String) var default_action_inventory
|
||||
|
||||
# If action used by player is in the list, game will wait for a second click on another item
|
||||
# to combine objects together (typical USE <X> WITH <Y>, GIVE <X> TO <Y>)
|
||||
export(PoolStringArray) var combine_if_action_used_among = []
|
||||
# If true, combination must be done in the way it is written in ESC script
|
||||
# ie. :use ON_ITEM
|
||||
# If false, combination will be tried in the other way.
|
||||
export(bool) var combine_is_one_way = false
|
||||
# If use_from_inventory_only is true, then the object must have been picked up before using it.
|
||||
# A false value is useful for items in the background, such as buttons.
|
||||
export(bool) var use_from_inventory_only = false
|
||||
# Scene used in inventory for the object if it is picked up
|
||||
export(PackedScene) var inventory_item_scene_file : PackedScene
|
||||
|
||||
# Color used for dialogs
|
||||
export(Color) var dialog_color = ColorN("white")
|
||||
|
||||
|
||||
# Detected interact position set by a Position2D node OUTSIDE OF THE SCENE.
|
||||
# You have to add a child to the INSTANCED SCENE, IN THE ROOM SCENE.
|
||||
#var interact_positions : Dictionary = { "default": null}
|
||||
|
||||
# Animation node (null if none was found)
|
||||
var animation_sprite
|
||||
|
||||
# Animations script (for walking, idling...)
|
||||
export(Script) var animations
|
||||
|
||||
# TERRAIN
|
||||
var terrain : ESCTerrain
|
||||
# If the terrain node type is scalenodes
|
||||
var terrain_is_scalenodes : bool
|
||||
var check_maps = true
|
||||
var collision
|
||||
# If dont_apply_terrain_scaling is true, terrain scaling will not be applied and
|
||||
# node will remain at the scale set in the scene.
|
||||
export(bool) var dont_apply_terrain_scaling = false
|
||||
|
||||
export(int) var speed : int = 300
|
||||
export(float) var v_speed_damp : float = 1.0
|
||||
var orig_speed : float
|
||||
|
||||
enum PLAYER_TASKS {
|
||||
NONE,
|
||||
WALK,
|
||||
SLIDE
|
||||
}
|
||||
onready var task = PLAYER_TASKS.NONE # type PLAYER_TASKS
|
||||
var params_queue : Array
|
||||
|
||||
|
||||
# PRIVATE VARS
|
||||
# Size of the item
|
||||
var size : Vector2
|
||||
var last_deg : int
|
||||
var last_dir : int
|
||||
|
||||
|
||||
func _ready():
|
||||
# Adds movable behavior
|
||||
Movable = Node.new()
|
||||
Movable.set_script(MovableScript)
|
||||
add_child(Movable)
|
||||
|
||||
for n in get_children():
|
||||
if n is AnimatedSprite:
|
||||
animation_sprite = n
|
||||
continue
|
||||
if n is AnimationPlayer:
|
||||
animation_sprite = n
|
||||
continue
|
||||
|
||||
connect("mouse_entered", self, "_on_mouse_entered")
|
||||
connect("mouse_exited", self, "_on_mouse_exited")
|
||||
connect("input_event", self, "manage_input")
|
||||
|
||||
# Initialize collision variable.
|
||||
for c in get_children():
|
||||
if c is CollisionShape2D or c is CollisionPolygon2D:
|
||||
collision = c
|
||||
|
||||
# Register and connect all elements to Escoria backoffice.
|
||||
if !Engine.is_editor_hint():
|
||||
escoria.object_manager.register_object(
|
||||
ESCObject.new(
|
||||
global_id,
|
||||
self
|
||||
),
|
||||
true
|
||||
)
|
||||
terrain = escoria.room_terrain
|
||||
|
||||
if !is_trigger:
|
||||
connect("mouse_entered_item", escoria.inputs_manager, "_on_mouse_entered_item")
|
||||
connect("mouse_exited_item", escoria.inputs_manager, "_on_mouse_exited_item")
|
||||
connect("mouse_left_clicked_item", escoria.inputs_manager, "_on_mouse_left_clicked_item")
|
||||
connect("mouse_double_left_clicked_item", escoria.inputs_manager, "_on_mouse_left_double_clicked_item")
|
||||
connect("mouse_right_clicked_item", escoria.inputs_manager, "_on_mouse_right_clicked_item")
|
||||
else:
|
||||
connect("area_entered", self, "element_entered")
|
||||
connect("area_exited", self, "element_exited")
|
||||
connect("body_entered", self, "element_entered")
|
||||
connect("body_exited", self, "element_exited")
|
||||
|
||||
# If object can be in the inventory, set default_action_inventory to same as
|
||||
# default_action, if default_action_inventory is not set
|
||||
if use_from_inventory_only and default_action_inventory.empty():
|
||||
default_action_inventory = default_action
|
||||
|
||||
# Perform a first terrain scaling if we have to.
|
||||
if !is_exit or dont_apply_terrain_scaling:
|
||||
Movable.last_scale = scale
|
||||
Movable.update_terrain()
|
||||
|
||||
|
||||
func get_animation_player():
|
||||
if animation_sprite == null:
|
||||
for n in get_children():
|
||||
if n is AnimationPlayer:
|
||||
animation_sprite = n
|
||||
return animation_sprite
|
||||
|
||||
|
||||
#"""
|
||||
#Initialize the interact_position attribute by searching for a Position2D
|
||||
#node in children nodes.
|
||||
#If any is found, the first one is used as interaction position with this hotspot.
|
||||
#If none is found, we use the CollisionShape2D or CollisionPolygon2D child node's
|
||||
#position instead.
|
||||
#"""
|
||||
#func init_interact_position_with_node():
|
||||
# interact_positions.default = null
|
||||
# for c in get_children():
|
||||
# if c is Position2D:
|
||||
# # If the position2D node is part of the hotspot, it means it is not an interact position
|
||||
# # but a dialog position for example. Interact position node must be set in the room scene.
|
||||
# if c.get_owner() == self:
|
||||
# continue
|
||||
# var globalpos = c.global_position
|
||||
# interact_positions.default = c.global_position
|
||||
#
|
||||
# if c is CollisionShape2D or c is CollisionPolygon2D:
|
||||
# collision = c
|
||||
# if interact_positions.default == null:
|
||||
# interact_positions.default = c.global_position
|
||||
|
||||
func get_interact_position() -> Vector2:
|
||||
var interact_position = null
|
||||
for c in get_children():
|
||||
if c is Position2D:
|
||||
# If the position2D node is part of the hotspot, it means it is not an interact position
|
||||
# but a dialog position for example. Interact position node must be set in the room scene.
|
||||
if c.get_owner() == self:
|
||||
continue
|
||||
interact_position = c.global_position
|
||||
|
||||
if c is CollisionShape2D or c is CollisionPolygon2D:
|
||||
if interact_position == null:
|
||||
interact_position = c.global_position
|
||||
|
||||
return interact_position
|
||||
|
||||
|
||||
|
||||
func manage_input(viewport : Viewport, event : InputEvent, shape_idx : int):
|
||||
if event is InputEventMouseButton:
|
||||
|
||||
if event.doubleclick:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("mouse_double_left_clicked_item", self, event)
|
||||
else:
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
emit_signal("mouse_left_clicked_item", self, event)
|
||||
elif event.button_index == BUTTON_RIGHT:
|
||||
emit_signal("mouse_right_clicked_item", self, event)
|
||||
|
||||
|
||||
func _on_mouse_entered():
|
||||
emit_signal("mouse_entered_item", self)
|
||||
|
||||
func _on_mouse_exited():
|
||||
emit_signal("mouse_exited_item", self)
|
||||
|
||||
################################################################################
|
||||
|
||||
# TRIGGER functions
|
||||
|
||||
func element_entered(body):
|
||||
if body is ESCBackground or body.get_parent() is ESCBackground:
|
||||
return
|
||||
escoria.do("trigger_in", [global_id, body.global_id, trigger_in_verb])
|
||||
|
||||
|
||||
func element_exited(body):
|
||||
if body is ESCBackground or body.get_parent() is ESCBackground:
|
||||
return
|
||||
escoria.do("trigger_out", [global_id, body.global_id, trigger_out_verb])
|
||||
|
||||
################################################################################
|
||||
|
||||
# MOVING OBJECT functions
|
||||
|
||||
func teleport(target, angle : Object = null) -> void:
|
||||
Movable.teleport(target, angle)
|
||||
|
||||
func walk_to(pos : Vector2, p_walk_context = null) -> void:
|
||||
Movable.walk_to(pos, p_walk_context)
|
||||
|
||||
func set_speed(speed_value : int) -> void:
|
||||
speed = speed_value
|
||||
|
||||
################################################################################
|
||||
|
||||
# TALKATIVE object functions
|
||||
|
||||
func start_talking():
|
||||
# if animation_sprite.is_playing():
|
||||
# animation_sprite.stop()
|
||||
# animation_sprite.play(animations.speaks[last_dir][0])
|
||||
pass
|
||||
|
||||
func stop_talking():
|
||||
# if animation_sprite.is_playing():
|
||||
# animation_sprite.stop()
|
||||
pass
|
||||
@@ -1,12 +0,0 @@
|
||||
extends Node
|
||||
|
||||
const OBJ_DEFAULT_STATE = "default"
|
||||
|
||||
enum EVENT_LEVEL_STATE {
|
||||
RETURN, # 0
|
||||
YIELD, # 1
|
||||
BREAK, # 2
|
||||
REPEAT, # 3
|
||||
CALL, # 4
|
||||
JUMP # 5
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
tool
|
||||
extends KinematicBody2D
|
||||
class_name ESCPlayer
|
||||
|
||||
func get_class():
|
||||
return "ESCPlayer"
|
||||
|
||||
"""
|
||||
TODO
|
||||
- Currently the sprite node needs to be named "sprite". This is bad.
|
||||
- Animation management doesn't allow using AnimationPlayer yet. Need to find
|
||||
the best solution to manage both AnimatedSprite and AnimationPlayer.
|
||||
"""
|
||||
|
||||
var Movable : Node
|
||||
var MovableScript = load("res://addons/escoria-core/game/core-scripts/behaviors/movable.gd")
|
||||
|
||||
export var global_id : String
|
||||
|
||||
var params_queue : Array
|
||||
var terrain : ESCTerrain
|
||||
|
||||
# If the terrain node type is scalenodes
|
||||
var terrain_is_scalenodes : bool
|
||||
var check_maps = true
|
||||
|
||||
export(int) var speed : int = 300
|
||||
export(float) var v_speed_damp : float = 1.0
|
||||
var orig_speed : float
|
||||
|
||||
enum PLAYER_TASKS {
|
||||
NONE,
|
||||
WALK,
|
||||
SLIDE
|
||||
}
|
||||
onready var task = PLAYER_TASKS.NONE # type PLAYER_TASKS
|
||||
|
||||
|
||||
enum Directions {
|
||||
NORTH = 0, # 0
|
||||
NORTHEAST = 1, # 1
|
||||
EAST = 2, # 2
|
||||
SOUTHEAST = 3, # 3
|
||||
SOUTH = 4, # 4
|
||||
SOUTHWEST = 5, # 5
|
||||
WEST = 6, # 6
|
||||
NORTHWEST = 7, # 7
|
||||
TOP = 0,
|
||||
TOP_RIGHT = 1
|
||||
RIGHT = 2,
|
||||
BOTTOM_RIGHT = 3,
|
||||
BOTTOM = 4,
|
||||
BOTTOM_LEFT = 5,
|
||||
LEFT = 6,
|
||||
TOP_LEFT = 7,
|
||||
}
|
||||
|
||||
# Animations script (for walking, idling...)
|
||||
export(Script) var animations
|
||||
|
||||
# AnimatedSprite node (if any)
|
||||
var animation_sprite
|
||||
# AnimationPlayer node (if any)
|
||||
## NOT USED YET
|
||||
#var animation
|
||||
var collision
|
||||
|
||||
# Dialogs parameters
|
||||
export(NodePath) var dialog_position_node
|
||||
export(Color) var dialog_color = ColorN("white")
|
||||
|
||||
# Camera parameters
|
||||
export(NodePath) var camera_position_node
|
||||
|
||||
|
||||
func _ready():
|
||||
# Adds movable behavior
|
||||
Movable = Node.new()
|
||||
Movable.set_script(MovableScript)
|
||||
add_child(Movable)
|
||||
|
||||
|
||||
# Connect the player to the event_done signal, so we can react to a finished
|
||||
# ":setup" event. In this case, we need to run update_terrain()
|
||||
escoria.event_manager.connect("event_finished", self, "update_terrain")
|
||||
|
||||
# assert(is_angle_in_interval(0, [340,40])) # true
|
||||
# assert(is_angle_in_interval(359, [340,40])) # true
|
||||
# assert(is_angle_in_interval(1, [340,40])) # true
|
||||
# assert(!is_angle_in_interval(90, [340,40])) # false
|
||||
#
|
||||
# assert(is_angle_in_interval(90, [70,40])) #true
|
||||
# assert(!is_angle_in_interval(180, [70,40])) #false
|
||||
#
|
||||
# assert(is_angle_in_interval(179, [160, 40])) #true
|
||||
# assert(is_angle_in_interval(180, [160, 40])) #true
|
||||
# assert(is_angle_in_interval(181, [160, 40])) #true
|
||||
# assert(!is_angle_in_interval(0, [160, 40])) #false
|
||||
#
|
||||
# assert(is_angle_in_interval(270, [250, 40])) # true
|
||||
# assert(!is_angle_in_interval(270, [70,40])) #false
|
||||
|
||||
for n in get_children():
|
||||
if n is AnimatedSprite:
|
||||
animation_sprite = n
|
||||
|
||||
# for sprite_child in n.get_children():
|
||||
# if sprite_child is AnimationPlayer:
|
||||
# animation = sprite_child
|
||||
# break
|
||||
|
||||
if n is CollisionShape2D or n is CollisionPolygon2D:
|
||||
collision = n
|
||||
|
||||
animation_sprite.connect("animation_finished", self, "anim_finished")
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
terrain = escoria.room_terrain
|
||||
Movable.last_scale = scale
|
||||
|
||||
# set_process(true)
|
||||
|
||||
|
||||
func _process(time):
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
$debug.text = str(z_index)
|
||||
|
||||
|
||||
func anim_finished():
|
||||
pass
|
||||
|
||||
|
||||
func get_camera_pos():
|
||||
if camera_position_node and get_node(camera_position_node):
|
||||
return get_node(camera_position_node).global_position
|
||||
return global_position
|
||||
|
||||
|
||||
func get_animations_list() -> PoolStringArray:
|
||||
return animation_sprite.get_sprite_frames().get_animation_names()
|
||||
|
||||
|
||||
func start_talking():
|
||||
if animation_sprite.is_playing():
|
||||
animation_sprite.stop()
|
||||
animation_sprite.play(animations.speaks[Movable.last_dir][0])
|
||||
|
||||
func stop_talking():
|
||||
if animation_sprite.is_playing():
|
||||
animation_sprite.stop()
|
||||
animation_sprite.play(animations.idles[Movable.last_dir][0])
|
||||
|
||||
|
||||
func teleport(target, angle : Object = null) -> void:
|
||||
Movable.teleport(target, angle)
|
||||
|
||||
|
||||
func walk_to(pos : Vector2, p_walk_context = null):
|
||||
return Movable.walk_to(pos, p_walk_context)
|
||||
|
||||
|
||||
func set_angle(deg : int, immediate = true):
|
||||
Movable.set_angle(deg, immediate)
|
||||
|
||||
|
||||
func set_speed(speed_value : int) -> void:
|
||||
speed = speed_value
|
||||
|
||||
func update_terrain(rc: int, event_name: String) -> void:
|
||||
Movable.update_terrain(event_name)
|
||||
@@ -1,63 +0,0 @@
|
||||
tool
|
||||
extends "res://addons/escoria-core/game/core-scripts/escterrain_base.gd"
|
||||
class_name ESCTerrain
|
||||
|
||||
func get_class():
|
||||
return "ESCTerrain"
|
||||
|
||||
export var scale_min = 0.3
|
||||
export var scale_max = 1.0
|
||||
|
||||
var current_active_navigation_instance : NavigationPolygonInstance
|
||||
|
||||
func _ready():
|
||||
var navigation_enabled_found = false
|
||||
for n in get_children():
|
||||
if n is NavigationPolygonInstance:
|
||||
if n.enabled:
|
||||
if navigation_enabled_found:
|
||||
escoria.logger.report_errors("escterrain.gd:_ready()", ["Multiple NavigationPolygonInstances enabled at the same time."])
|
||||
navigation_enabled_found = true
|
||||
current_active_navigation_instance = n
|
||||
|
||||
if !Engine.is_editor_hint():
|
||||
escoria.room_terrain = self
|
||||
#path = ImagePathFinder.new()
|
||||
_update_texture()
|
||||
|
||||
func get_scale_range(r):
|
||||
r = scale_min + (scale_max - scale_min) * r
|
||||
return Vector2(r, r)
|
||||
|
||||
func get_terrain(pos):
|
||||
if scales == null || scales.get_data().is_empty():
|
||||
return Color(1, 1, 1, 1)
|
||||
return get_pixel(pos, scales.get_data())
|
||||
|
||||
func get_pixel(pos, p_image):
|
||||
if pos.x + 1 >= p_image.get_width() || pos.y + 1 >= p_image.get_height() || pos.x < 0 || pos.y < 0:
|
||||
return Color(1.0, 0.0, 0.0)
|
||||
|
||||
# `get_pixel()` is slow; this is accurate enough
|
||||
# without interpolating neighboring pixels and accounting for fractions
|
||||
p_image.lock()
|
||||
var pixel = p_image.get_pixel(pos.x, pos.y)
|
||||
p_image.unlock()
|
||||
return pixel
|
||||
|
||||
func _draw():
|
||||
if typeof(texture) == typeof(null):
|
||||
return
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
if debug_mode == 0:
|
||||
return
|
||||
var scale_vect = bitmaps_scale
|
||||
|
||||
var src = Rect2(0, 0, texture.get_width(), texture.get_height())
|
||||
var dst = Rect2(0, 0, texture.get_width() * scale_vect.x, texture.get_height() * scale_vect.y)
|
||||
|
||||
draw_texture_rect_region(texture, dst, src)
|
||||
#draw_texture(texture, Vector2(0, 0))
|
||||
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
tool
|
||||
extends Navigation2D
|
||||
|
||||
export(Texture) var scales setget set_scales,get_scales
|
||||
export var bitmaps_scale = Vector2(1,1) setget set_bm_scale,get_bm_scale
|
||||
export(Texture) var lightmap setget set_lightmap,get_lightmap
|
||||
var lightmap_data
|
||||
|
||||
#warning-ignore:unused_class_variable
|
||||
export var player_speed_multiplier = 1.0 # Override player speed in current scene
|
||||
#warning-ignore:unused_class_variable
|
||||
export var player_doubleclick_speed_multiplier = 1.5 # Make the player move faster when doubleclicked
|
||||
export var lightmap_modulate = Color(1, 1, 1, 1)
|
||||
export(int, "None", "Scales", "Lightmap") var debug_mode = 1 setget debug_mode_updated
|
||||
|
||||
var texture
|
||||
var img_area
|
||||
var _texture_dirty = false
|
||||
|
||||
func set_bm_scale(p_scale):
|
||||
bitmaps_scale = p_scale
|
||||
_update_texture()
|
||||
|
||||
func get_bm_scale():
|
||||
return bitmaps_scale
|
||||
|
||||
func set_lightmap(p_lightmap):
|
||||
var need_init = (lightmap != p_lightmap) or (lightmap and not lightmap_data)
|
||||
|
||||
lightmap = p_lightmap
|
||||
|
||||
# It's bad enough a new copy is created when reading a pixel, we don't
|
||||
# also need to get the data for every read to make yet another copy
|
||||
if need_init:
|
||||
if lightmap_data:
|
||||
lightmap_data.unlock()
|
||||
lightmap_data = lightmap.get_data()
|
||||
lightmap_data.lock()
|
||||
|
||||
_update_texture()
|
||||
|
||||
func get_lightmap():
|
||||
return lightmap
|
||||
|
||||
func set_scales(p_scales):
|
||||
scales = p_scales
|
||||
_update_texture()
|
||||
|
||||
func get_scales():
|
||||
return scales
|
||||
|
||||
func debug_mode_updated(p_mode):
|
||||
debug_mode = p_mode
|
||||
_update_texture()
|
||||
|
||||
func _update_texture():
|
||||
if _texture_dirty:
|
||||
return
|
||||
|
||||
_texture_dirty = true
|
||||
call_deferred("_do_update_texture")
|
||||
|
||||
func _do_update_texture():
|
||||
_texture_dirty = false
|
||||
if !is_inside_tree():
|
||||
return
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if debug_mode == 0:
|
||||
update()
|
||||
return
|
||||
|
||||
texture = ImageTexture.new()
|
||||
if debug_mode == 1:
|
||||
if scales != null:
|
||||
#texture.create_from_image(scales)
|
||||
texture = scales
|
||||
else:
|
||||
if lightmap != null:
|
||||
#texture.create_from_image(lightmap)
|
||||
texture = lightmap
|
||||
|
||||
update()
|
||||
|
||||
|
||||
|
||||
func make_local(pos):
|
||||
pos = pos - get_position()
|
||||
pos = pos * 1.0 / get_scale()
|
||||
pos = get_closest_point(pos)
|
||||
return pos
|
||||
|
||||
func make_global(pos):
|
||||
pos = pos * get_scale()
|
||||
pos = pos + get_position()
|
||||
return pos
|
||||
|
||||
func get_terrain_path(p_src, p_dest):
|
||||
# printt("get path ", p_src, p_dest)
|
||||
p_src = make_local(p_src)
|
||||
p_dest = make_local(p_dest)
|
||||
|
||||
var r_path = get_simple_path(p_src, p_dest, true)
|
||||
r_path = Array(r_path)
|
||||
for i in range(0, r_path.size()):
|
||||
r_path[i] = make_global(r_path[i])
|
||||
return r_path
|
||||
|
||||
func is_solid(pos):
|
||||
pos = pos - get_position()
|
||||
pos = pos * 1.0 / get_scale()
|
||||
|
||||
var closest = get_closest_point(pos)
|
||||
return pos == closest
|
||||
|
||||
func _color_mul(a, b):
|
||||
var c = Color()
|
||||
c.r = a.r * b.r
|
||||
c.g = a.g * b.g
|
||||
c.b = a.b * b.b
|
||||
c.a = a.a * b.a
|
||||
return c
|
||||
|
||||
func get_light(pos):
|
||||
if not lightmap or lightmap.get_data().is_empty():
|
||||
return
|
||||
|
||||
return _color_mul(get_pixel(pos, lightmap_data), lightmap_modulate)
|
||||
|
||||
func get_pixel(pos, p_image):
|
||||
p_image.lock()
|
||||
|
||||
pos = make_local(pos)
|
||||
pos = pos * 1.0 / bitmaps_scale
|
||||
|
||||
if pos.x + 1 >= p_image.get_width() || pos.y + 1 >= p_image.get_height() || pos.x < 0 || pos.y < 0:
|
||||
return Color()
|
||||
|
||||
var ll = p_image.get_pixel(pos.x, pos.y)
|
||||
var ndif = Vector2()
|
||||
ndif.x = pos.x - floor(pos.x)
|
||||
ndif.y = pos.y - floor(pos.y)
|
||||
var ur
|
||||
|
||||
img_area = Rect2(0, 0, p_image.get_width(), p_image.get_height())
|
||||
|
||||
var lr = ll
|
||||
if ndif.x > 0 && img_area.has_point(Vector2(pos.x+1, pos.y)):
|
||||
lr = p_image.get_pixel(pos.x+1, pos.y)
|
||||
#if lr.a < 128:
|
||||
# lr = ll
|
||||
ur = lr
|
||||
|
||||
var ul = ll
|
||||
if ndif.y > 0 && img_area.has_point(Vector2(pos.x, pos.y+1)):
|
||||
ul = p_image.get_pixel(pos.x, pos.y+1)
|
||||
#if ul.a < 128:
|
||||
# ul = ll
|
||||
ur = ul
|
||||
|
||||
if ndif.x > 0 && ndif.y > 0 && img_area.has_point(Vector2(pos.x+1, pos.y+1)):
|
||||
var pix = p_image.get_pixel(pos.x+1, pos.y+1)
|
||||
#if pix.a > 128:
|
||||
ur = pix
|
||||
|
||||
var bottom = ll.linear_interpolate(lr, ndif.x)
|
||||
var top
|
||||
if ur != null:
|
||||
top = ul.linear_interpolate(ur, ndif.x)
|
||||
else:
|
||||
top = ul
|
||||
|
||||
var final = bottom.linear_interpolate(top, ndif.y)
|
||||
|
||||
p_image.unlock()
|
||||
return final
|
||||
@@ -1,92 +0,0 @@
|
||||
tool
|
||||
|
||||
extends "terrain_base.gd"
|
||||
|
||||
const DIST_EPSILON = 0.000001
|
||||
|
||||
var scale_nodes = []
|
||||
|
||||
onready var scale_min = $"scale_min"
|
||||
onready var scale_max = $"scale_max"
|
||||
|
||||
func debug_mode_updated(p_mode):
|
||||
debug_mode = p_mode
|
||||
|
||||
._update_texture()
|
||||
|
||||
func _do_update_texture():
|
||||
_texture_dirty = false
|
||||
if !is_inside_tree():
|
||||
return
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if debug_mode == 0:
|
||||
update()
|
||||
return
|
||||
|
||||
texture = ImageTexture.new()
|
||||
|
||||
if lightmap != null:
|
||||
#texture.create_from_image(lightmap)
|
||||
texture = lightmap
|
||||
|
||||
update()
|
||||
|
||||
static func sort_by_y(a, b):
|
||||
return a.global_position.y < b.global_position.y
|
||||
|
||||
# Return a "scale range" immediately based on the interpolated scale size
|
||||
func get_terrain(pos):
|
||||
# printt("Called", pos)
|
||||
var prev
|
||||
var next
|
||||
var prev_target
|
||||
var node_target
|
||||
for i in range(1, scale_nodes.size()):
|
||||
prev = scale_nodes[i - 1]
|
||||
next = scale_nodes[i]
|
||||
|
||||
if prev.global_position.y < pos.y and pos.y < next.global_position.y:
|
||||
# printt("1:", prev.global_position.y, " < ", pos.y, " and ", pos.y, " < ", next.global_position.y)
|
||||
prev_target = prev.target_scale.y
|
||||
node_target = next.target_scale.y
|
||||
break
|
||||
|
||||
var nodes_dist = next.global_position.y - prev.global_position.y
|
||||
if nodes_dist < DIST_EPSILON:
|
||||
nodes_dist = DIST_EPSILON
|
||||
var interp_dist = (pos.y - prev.global_position.y) / nodes_dist
|
||||
|
||||
var y_1 = Vector2(0, prev_target)
|
||||
var y_2 = Vector2(0, node_target)
|
||||
|
||||
var interp = y_1.linear_interpolate(y_2, interp_dist)
|
||||
|
||||
return Vector2(interp.y, interp.y)
|
||||
|
||||
func get_pixel(pos, p_image):
|
||||
if pos.x + 1 >= p_image.get_width() || pos.y + 1 >= p_image.get_height() || pos.x < 0 || pos.y < 0:
|
||||
return Color(1.0, 0.0, 0.0)
|
||||
|
||||
# `get_pixel()` is slow; this is accurate enough
|
||||
# without interpolating neighboring pixels and accounting for fractions
|
||||
return p_image.get_pixel(pos.x, pos.y)
|
||||
|
||||
func _draw():
|
||||
if not texture:
|
||||
return
|
||||
|
||||
if debug_mode == 0:
|
||||
return
|
||||
|
||||
draw_texture(texture, Vector2(0, 0))
|
||||
|
||||
func _ready():
|
||||
for c in get_children():
|
||||
if c is preload("scalenode.gd"):
|
||||
scale_nodes.push_back(c)
|
||||
|
||||
scale_nodes.sort_custom(self, "sort_by_y")
|
||||
scale_nodes.push_front(scale_min)
|
||||
scale_nodes.push_back(scale_max)
|
||||
@@ -1,9 +0,0 @@
|
||||
extends Node
|
||||
|
||||
|
||||
func get_inventory_item(item_id : String) -> ESCInventoryItem:
|
||||
for c in get_children():
|
||||
if c.global_id == item_id:
|
||||
if c.inventory_item_scene_file:
|
||||
return c.inventory_item_scene_file.instance()
|
||||
return null
|
||||
@@ -1,5 +0,0 @@
|
||||
extends Position2D
|
||||
|
||||
#warning-ignore:unused_class_variable
|
||||
export(Vector2) var target_scale = Vector2(1.0, 1.0)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
extends Position2D
|
||||
|
||||
#warning-ignore:unused_class_variable
|
||||
export(Vector2) var target_scale = Vector2(1.0, 1.0)
|
||||
@@ -1,6 +1,15 @@
|
||||
# A set of common utilities
|
||||
extends Object
|
||||
class_name ESCUtils
|
||||
|
||||
# Helpers to deal with player's and items' angles
|
||||
func _get_deg_from_rad(rad_angle : float):
|
||||
|
||||
# Convert radians to degrees
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - rad_angle: Angle in radians
|
||||
# **Returns** Degrees
|
||||
func get_deg_from_rad(rad_angle : float):
|
||||
var deg = rad2deg(rad_angle)
|
||||
if deg >= 360.0:
|
||||
deg = clamp(deg, 0.0, 360.0)
|
||||
@@ -1,14 +1,24 @@
|
||||
# The escorie main script
|
||||
extends Node
|
||||
|
||||
# Scripts
|
||||
onready var main = $main
|
||||
onready var inputs_manager = $inputs_manager
|
||||
onready var utils = load("res://addons/escoria-core/game/core-scripts/utils/utils.gd").new()
|
||||
onready var save_data = load("res://addons/escoria-core/game/core-scripts/save_data/save_data.gd").new()
|
||||
|
||||
# Current game state
|
||||
# * DEFAULT: Common game function
|
||||
# * DIALOG: Game is playing a dialog
|
||||
# * WAIT: Game is waiting
|
||||
enum GAME_STATE {
|
||||
DEFAULT,
|
||||
DIALOG,
|
||||
WAIT
|
||||
}
|
||||
|
||||
|
||||
# Logger used
|
||||
var logger: ESCLogger
|
||||
|
||||
# Several utilities
|
||||
var utils: ESCUtils
|
||||
|
||||
# The inventory manager instance
|
||||
var inventory_manager: ESCInventoryManager
|
||||
|
||||
@@ -30,30 +40,24 @@ var object_manager: ESCObjectManager
|
||||
# ESC command registry instance
|
||||
var command_registry: ESCCommandRegistry
|
||||
|
||||
var resource_cache: ResourceCache
|
||||
# Resource cache handler
|
||||
var resource_cache: ESCResourceCache
|
||||
|
||||
# INSTANCES
|
||||
# Instance of the main menu
|
||||
var main_menu_instance
|
||||
|
||||
## Dialog player instantiator. This instance is called directly for dialogs.
|
||||
var dialog_player
|
||||
## Inventory scene
|
||||
var inventory
|
||||
|
||||
# Game variables
|
||||
# Terrain of the current room
|
||||
var room_terrain
|
||||
|
||||
enum GAME_STATE {
|
||||
DEFAULT,
|
||||
DIALOG,
|
||||
WAIT
|
||||
}
|
||||
onready var current_state = GAME_STATE.DEFAULT
|
||||
# Dialog player instantiator. This instance is called directly for dialogs.
|
||||
var dialog_player
|
||||
|
||||
onready var game_size = get_viewport().size
|
||||
# Inventory scene
|
||||
var inventory
|
||||
|
||||
# These are settings that the player can affect and save/load later
|
||||
var settings : Dictionary
|
||||
|
||||
# These are default settings
|
||||
var settings_default : Dictionary = {
|
||||
# Text language
|
||||
@@ -64,7 +68,7 @@ var settings_default : Dictionary = {
|
||||
"speech_enabled": ProjectSettings.get_setting("escoria/sound/speech_enabled"),
|
||||
# Master volume (max is 1.0)
|
||||
"master_volume": ProjectSettings.get_setting("escoria/sound/master_volume"),
|
||||
# Music volume (max is 1.0)
|
||||
# Music volume (max is 1.0)
|
||||
"music_volume": ProjectSettings.get_setting("escoria/sound/music_volume"),
|
||||
# Sound effects volume (max is 1.0)
|
||||
"sfx_volume": ProjectSettings.get_setting("escoria/sound/sfx_volume"),
|
||||
@@ -79,8 +83,26 @@ var settings_default : Dictionary = {
|
||||
}
|
||||
|
||||
|
||||
# The current state of the game
|
||||
onready var current_state = GAME_STATE.DEFAULT
|
||||
|
||||
# The game resolution
|
||||
onready var game_size = get_viewport().size
|
||||
|
||||
# The main scene
|
||||
onready var main = $main
|
||||
|
||||
# The escoria inputs manager
|
||||
onready var inputs_manager = $inputs_manager
|
||||
|
||||
# Savegame management
|
||||
onready var save_data = load("res://addons/escoria-core/game/core-scripts/save_data/save_data.gd").new()
|
||||
|
||||
|
||||
# Initialize various objects
|
||||
func _init():
|
||||
self.logger = ESCLogger.new()
|
||||
self.utils = ESCUtils.new()
|
||||
self.inventory_manager = ESCInventoryManager.new()
|
||||
self.action_manager = ESCActionManager.new()
|
||||
self.event_manager = ESCEventManager.new()
|
||||
@@ -89,10 +111,11 @@ func _init():
|
||||
self.object_manager = ESCObjectManager.new()
|
||||
self.command_registry = ESCCommandRegistry.new()
|
||||
self.esc_compiler = ESCCompiler.new()
|
||||
self.resource_cache = ResourceCache.new()
|
||||
self.resource_cache = ESCResourceCache.new()
|
||||
self.resource_cache.start()
|
||||
|
||||
|
||||
# Load settings
|
||||
func _ready():
|
||||
save_data.start()
|
||||
save_data.check_settings()
|
||||
@@ -101,8 +124,6 @@ func _ready():
|
||||
escoria._on_settings_loaded(escoria.settings)
|
||||
|
||||
|
||||
##################################################################################
|
||||
|
||||
# Called by Main menu "start new game"
|
||||
func new_game():
|
||||
var script = self.esc_compiler.load_esc_file(
|
||||
@@ -112,19 +133,21 @@ func new_game():
|
||||
var rc = yield(event_manager, "event_finished")
|
||||
while rc[1] != "start":
|
||||
rc = yield(event_manager, "event_finished")
|
||||
|
||||
|
||||
if rc[0] != ESCExecution.RC_OK:
|
||||
self.logger.report_errors(
|
||||
"Start event of the start script returned unsuccessful: %d" % rc[0],
|
||||
[]
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
"""
|
||||
Generic action function that runs an action on an element of the room (eg player walk)
|
||||
action: type of the action ()
|
||||
"""
|
||||
|
||||
# Run a generic action
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - action: type of the action to run
|
||||
# - params: Parameters for the action
|
||||
func do(action : String, params : Array = []) -> void:
|
||||
if current_state == GAME_STATE.DEFAULT:
|
||||
match action:
|
||||
@@ -134,10 +157,10 @@ func do(action : String, params : Array = []) -> void:
|
||||
# Check moving object.
|
||||
if not self.object_manager.has(params[0]):
|
||||
self.logger.report_errors(
|
||||
"escoria.gd:do()",
|
||||
"escoria.gd:do()",
|
||||
[
|
||||
"Walk action requested on inexisting object: %s "\
|
||||
% params[0]
|
||||
"Walk action requested on inexisting " + \
|
||||
"object: %s " % params[0]
|
||||
]
|
||||
)
|
||||
return
|
||||
@@ -151,14 +174,18 @@ func do(action : String, params : Array = []) -> void:
|
||||
var is_fast : bool = false
|
||||
if params.size() > 2 and params[2] == true:
|
||||
is_fast = true
|
||||
var walk_context = {"fast": is_fast, "target": target_position}
|
||||
var walk_context = ESCWalkContext.new(
|
||||
null,
|
||||
target_position,
|
||||
is_fast
|
||||
)
|
||||
moving_obj.walk_to(target_position, walk_context)
|
||||
|
||||
# Walk to object from its id
|
||||
elif params[1] is String:
|
||||
if not self.object_manager.has(params[1]):
|
||||
self.logger.report_errors(
|
||||
"escoria.gd:do()",
|
||||
"escoria.gd:do()",
|
||||
[
|
||||
"Walk action requested TOWARDS " +\
|
||||
"inexisting object: %s" % params[1]
|
||||
@@ -168,31 +195,45 @@ func do(action : String, params : Array = []) -> void:
|
||||
|
||||
var object = self.object_manager.get_object(params[1])
|
||||
if object:
|
||||
var target_position : Vector2 = object.node.interact_position
|
||||
var target_position : Vector2 = \
|
||||
object.node.interact_position
|
||||
var is_fast : bool = false
|
||||
if params.size() > 2 and params[2] == true:
|
||||
is_fast = true
|
||||
var walk_context = {"fast": is_fast, "target_object" : object}
|
||||
var walk_context = ESCWalkContext.new(
|
||||
object,
|
||||
Vector2(),
|
||||
is_fast
|
||||
)
|
||||
|
||||
moving_obj.walk_to(target_position, walk_context)
|
||||
|
||||
"item_left_click":
|
||||
if params[0] is String:
|
||||
self.logger.info("escoria.do() : item_left_click on item ", [params[0]])
|
||||
self.logger.info(
|
||||
"escoria.do() : item_left_click on item ",
|
||||
[params[0]]
|
||||
)
|
||||
var item = self.object_manager.get_object(params[0])
|
||||
ev_left_click_on_item(item, params[1])
|
||||
_ev_left_click_on_item(item, params[1])
|
||||
|
||||
"item_right_click":
|
||||
if params[0] is String:
|
||||
self.logger.info("escoria.do() : item_right_click on item ", [params[0]])
|
||||
self.logger.info(
|
||||
"escoria.do() : item_right_click on item ",
|
||||
[params[0]]
|
||||
)
|
||||
var item = self.object_manager.get_object(params[0])
|
||||
ev_left_click_on_item(item, params[1], true)
|
||||
_ev_left_click_on_item(item, params[1], true)
|
||||
|
||||
"trigger_in":
|
||||
var trigger_id = params[0]
|
||||
var object_id = params[1]
|
||||
var trigger_in_verb = params[2]
|
||||
self.logger.info("escoria.do() : trigger_in " + trigger_id + " by " + object_id)
|
||||
self.logger.info("escoria.do() : trigger_in %s by %s" % [
|
||||
trigger_id,
|
||||
object_id
|
||||
])
|
||||
self.event_manager.queue_event(
|
||||
object_manager.get_object(trigger_id).events[
|
||||
trigger_in_verb
|
||||
@@ -203,7 +244,10 @@ func do(action : String, params : Array = []) -> void:
|
||||
var trigger_id = params[0]
|
||||
var object_id = params[1]
|
||||
var trigger_out_verb = params[2]
|
||||
self.logger.info("escoria.do() : trigger_out " + trigger_id + " by " + object_id)
|
||||
self.logger.info("escoria.do() : trigger_out %s by %s" % [
|
||||
trigger_id,
|
||||
object_id
|
||||
])
|
||||
self.event_manager.queue_event(
|
||||
object_manager.get_object(trigger_id).events[
|
||||
trigger_out_verb
|
||||
@@ -211,26 +255,27 @@ func do(action : String, params : Array = []) -> void:
|
||||
)
|
||||
|
||||
_:
|
||||
self.logger.report_warnings("escoria.gd:do()",
|
||||
self.logger.report_warnings("escoria.gd:do()",
|
||||
["Action received:", action, "with params ", params])
|
||||
elif current_state == GAME_STATE.WAIT:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
# PRIVATE
|
||||
func ev_left_click_on_item(obj, event, default_action = false):
|
||||
"""
|
||||
Event occurring when an object/item is left clicked
|
||||
obj : object that was left clicked
|
||||
event :
|
||||
"""
|
||||
# Event handler when an object/item was clicked
|
||||
# FIXME this method is way to complex
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - ob: Object that was left clicked
|
||||
# - event: Input event that was received
|
||||
# - default_action: Run the inventory default action
|
||||
func _ev_left_click_on_item(obj, event, default_action = false):
|
||||
if obj is String:
|
||||
obj = object_manager.get_object(obj)
|
||||
self.logger.info(obj.global_id + " left-clicked with event ", [event])
|
||||
|
||||
var need_combine = false
|
||||
# Check if current_action and current_tool are already set
|
||||
# Check if current_action and current_tool are already set
|
||||
if self.action_manager.current_action:
|
||||
if self.action_manager.current_tool:
|
||||
if self.action_manager.current_action in self.action_manager\
|
||||
@@ -251,23 +296,24 @@ func ev_left_click_on_item(obj, event, default_action = false):
|
||||
self.action_manager.current_tool = obj
|
||||
|
||||
|
||||
# Don't interact after player movement towards object (because object is inactive for example)
|
||||
# Don't interact after player movement towards object
|
||||
# (because object is inactive for example)
|
||||
var dont_interact = false
|
||||
var destination_position : Vector2 = main.current_scene.player.global_position
|
||||
var destination_position : Vector2 = main.current_scene.player.\
|
||||
global_position
|
||||
|
||||
# Create walk context
|
||||
var walk_context = {"fast": event.doubleclick, "target_object" : obj.node}
|
||||
var walk_context = ESCWalkContext.new(
|
||||
obj,
|
||||
Vector2(),
|
||||
event.doubleclick
|
||||
)
|
||||
|
||||
# If object not in inventory, player walks towards it
|
||||
if not inventory_manager.inventory_has(obj.global_id):
|
||||
var clicked_object_has_interact_position = false
|
||||
|
||||
if object_manager.get_object(obj.global_id).interactive:
|
||||
# if obj.interact_positions.default != null:
|
||||
# destination_position = obj.interact_positions.default#.global_position
|
||||
# clicked_object_has_interact_position = true
|
||||
# else:
|
||||
# destination_position = obj.position
|
||||
if obj.node.get_interact_position() != null:
|
||||
destination_position = obj.node.get_interact_position()
|
||||
clicked_object_has_interact_position = true
|
||||
@@ -277,20 +323,25 @@ func ev_left_click_on_item(obj, event, default_action = false):
|
||||
destination_position = event.position
|
||||
dont_interact = true
|
||||
|
||||
# Use ESC for this?
|
||||
var is_already_walking = main.current_scene.player.walk_to(destination_position, walk_context)
|
||||
main.current_scene.player.walk_to(
|
||||
destination_position,
|
||||
walk_context
|
||||
)
|
||||
|
||||
# Wait for the player to arrive before continuing with action.
|
||||
var context = yield(main.current_scene.player, "arrived")
|
||||
var context: ESCWalkContext = yield(
|
||||
main.current_scene.player,
|
||||
"arrived"
|
||||
)
|
||||
|
||||
self.logger.info("Context arrived: ", [context])
|
||||
if context.has("target_object") and walk_context.has("target_object"):
|
||||
if (context.target_object.global_id != walk_context.target_object.global_id) \
|
||||
or (context.target_object.global_id == walk_context.target_object.global_id and is_already_walking):
|
||||
dont_interact = true
|
||||
elif context.has("target") and walk_context.has("target"):
|
||||
if (context.target.global_id != walk_context.target.global_id) \
|
||||
or (context.target.global_id == walk_context.target.global_id and is_already_walking):
|
||||
dont_interact = true
|
||||
|
||||
if context.target_object and \
|
||||
context.target_object.global_id != walk_context.\
|
||||
target_object.global_id:
|
||||
dont_interact = true
|
||||
elif context.target_position != walk_context.target_position:
|
||||
dont_interact = true
|
||||
|
||||
# If no interaction should happen after player has arrived, leave immediately.
|
||||
if dont_interact:
|
||||
@@ -318,22 +369,24 @@ func ev_left_click_on_item(obj, event, default_action = false):
|
||||
# If apply_interact, perform combine between items
|
||||
if need_combine:
|
||||
self.action_manager.activate(
|
||||
self.action_manager.current_action,
|
||||
self.action_manager.current_tool,
|
||||
self.action_manager.current_action,
|
||||
self.action_manager.current_tool,
|
||||
obj
|
||||
)
|
||||
|
||||
else:
|
||||
self.action_manager.activate(
|
||||
self.action_manager.current_action,
|
||||
self.action_manager.current_action,
|
||||
obj
|
||||
)
|
||||
|
||||
# else:
|
||||
## escoria.fallback("")
|
||||
# pass
|
||||
|
||||
func _on_settings_loaded(p_settings : Dictionary):
|
||||
|
||||
# Apply the loaded settings
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# * p_settings: Loaded settings
|
||||
func _on_settings_loaded(p_settings : Dictionary) -> void:
|
||||
escoria.logger.info("******* settings loaded", p_settings)
|
||||
if p_settings != null:
|
||||
settings = p_settings
|
||||
@@ -346,9 +399,18 @@ func _on_settings_loaded(p_settings : Dictionary):
|
||||
|
||||
# TODO Apply globally
|
||||
# AudioServer.set_fx_global_volume_scale(settings.sfx_volume)
|
||||
AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear2db(settings.master_volume))
|
||||
AudioServer.set_bus_volume_db(AudioServer.get_bus_index("SFX"), linear2db(settings.sfx_volume))
|
||||
AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Music"), linear2db(settings.music_volume))
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index("Master"),
|
||||
linear2db(settings.master_volume)
|
||||
)
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index("SFX"),
|
||||
linear2db(settings.sfx_volume)
|
||||
)
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index("Music"),
|
||||
linear2db(settings.music_volume)
|
||||
)
|
||||
TranslationServer.set_locale(settings.text_lang)
|
||||
# music_volume_changed()
|
||||
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
# Escoria inputs manager
|
||||
# Catches, handles and distributes input events for the game
|
||||
tool
|
||||
extends Node
|
||||
|
||||
|
||||
# A LIFO stack of hovered items
|
||||
onready var hover_stack : Array = []
|
||||
|
||||
# The global id fo the topmost item from the hover_stack
|
||||
onready var hotspot_focused : String = ""
|
||||
|
||||
|
||||
func _ready():
|
||||
set_process_input(true)
|
||||
|
||||
|
||||
func _input(event):
|
||||
# Input event handler
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - event: Godot input event received
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("esc_show_debug_prompt"):
|
||||
escoria.main.get_node("layers/debug_layer/esc_prompt_popup").popup()
|
||||
|
||||
@@ -21,57 +28,97 @@ func _input(event):
|
||||
if event is InputEventMouseMotion:
|
||||
escoria.main.current_scene.game.update_tooltip_following_mouse_position(event.position)
|
||||
|
||||
###################################################################################
|
||||
|
||||
func _on_left_click_on_bg(position : Vector2):
|
||||
# The background was clicked with the LMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: Position of the click
|
||||
func _on_left_click_on_bg(position: Vector2) -> void:
|
||||
if hotspot_focused.empty():
|
||||
escoria.logger.info("Left click on background at ", [str(position)])
|
||||
escoria.main.current_scene.game.left_click_on_bg(position)
|
||||
|
||||
|
||||
func _on_double_left_click_on_bg(position : Vector2):
|
||||
# The background was double-clicked with the LMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: Position of the click
|
||||
func _on_double_left_click_on_bg(position: Vector2) -> void:
|
||||
if hotspot_focused.empty():
|
||||
escoria.logger.info("Double left click on background at ", [str(position)])
|
||||
escoria.main.current_scene.game.left_double_click_on_bg(position)
|
||||
|
||||
|
||||
func _on_right_click_on_bg(position : Vector2):
|
||||
# The background was clicked with the RMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - position: Position of the click
|
||||
func _on_right_click_on_bg(position: Vector2) -> void:
|
||||
if hotspot_focused.empty():
|
||||
escoria.logger.info("Right click on background at ", [str(position)])
|
||||
escoria.main.current_scene.game.right_click_on_bg(position)
|
||||
|
||||
##################################################################################
|
||||
|
||||
func _on_mouse_left_click_inventory_item(inventory_item_global_id, event : InputEvent) -> void:
|
||||
# An inventory item was clicked with the LMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: The global id of the clicked inventory item
|
||||
# - event: The input event received
|
||||
func _on_mouse_left_click_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
|
||||
escoria.logger.info("Inventory item left clicked ", [inventory_item_global_id])
|
||||
escoria.main.current_scene.game.left_click_on_inventory_item(inventory_item_global_id, event)
|
||||
|
||||
|
||||
func _on_mouse_right_click_inventory_item(inventory_item_global_id, event : InputEvent) -> void:
|
||||
# An inventory item was clicked with the RMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: The global id of the clicked inventory item
|
||||
# - event: The input event received
|
||||
func _on_mouse_right_click_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
|
||||
escoria.logger.info("Inventory item right clicked ", [inventory_item_global_id])
|
||||
escoria.main.current_scene.game.right_click_on_inventory_item(inventory_item_global_id, event)
|
||||
|
||||
|
||||
func _on_mouse_double_left_click_inventory_item(inventory_item_global_id, event : InputEvent) -> void:
|
||||
# An inventory item was doublce-clicked with the LMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: The global id of the clicked inventory item
|
||||
# - event: The input event received
|
||||
func _on_mouse_double_left_click_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
|
||||
escoria.logger.info("Inventory item double left clicked ", [inventory_item_global_id])
|
||||
escoria.main.current_scene.game.left_double_click_on_inventory_item(inventory_item_global_id, event)
|
||||
|
||||
|
||||
func _on_mouse_entered_inventory_item(inventory_item_global_id) -> void:
|
||||
# The mouse entered an inventory item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - inventory_item_global_id: The global id of the inventory item that is hovered
|
||||
func _on_mouse_entered_inventory_item(inventory_item_global_id: String) -> void:
|
||||
escoria.logger.info("Inventory item focused ", [inventory_item_global_id])
|
||||
escoria.main.current_scene.game.inventory_item_focused(inventory_item_global_id)
|
||||
|
||||
|
||||
# The mouse exited an inventory item
|
||||
func _on_mouse_exited_inventory_item() -> void:
|
||||
escoria.logger.info("Inventory item unfocused")
|
||||
escoria.main.current_scene.game.inventory_item_unfocused()
|
||||
|
||||
|
||||
##################################################################################
|
||||
|
||||
func _on_mouse_entered_item(item : ESCItem) -> void:
|
||||
# The mouse entered an Escoria item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item: The Escoria item hovered
|
||||
func _on_mouse_entered_item(item: ESCItem) -> void:
|
||||
escoria.logger.info("Item focused : ", [item.global_id])
|
||||
clean_hover_stack()
|
||||
_clean_hover_stack()
|
||||
|
||||
if !hover_stack.empty():
|
||||
if item.z_index > hover_stack.back().z_index:
|
||||
@@ -85,9 +132,14 @@ func _on_mouse_entered_item(item : ESCItem) -> void:
|
||||
escoria.main.current_scene.game.element_focused(item.global_id)
|
||||
|
||||
|
||||
# The mouse exited an Escoria item
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item: The Escoria item hovered
|
||||
func _on_mouse_exited_item(item : ESCItem) -> void:
|
||||
escoria.logger.info("Item unfocused : ", [item.global_id])
|
||||
hover_stack_pop(item)
|
||||
_hover_stack_pop(item)
|
||||
if hover_stack.empty():
|
||||
hotspot_focused = ""
|
||||
escoria.main.current_scene.game.element_unfocused()
|
||||
@@ -95,40 +147,62 @@ func _on_mouse_exited_item(item : ESCItem) -> void:
|
||||
hotspot_focused = hover_stack.back().global_id
|
||||
escoria.main.current_scene.game.element_focused(hotspot_focused)
|
||||
|
||||
|
||||
|
||||
# An Escoria item was clicked with the LMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item: The Escoria item clicked
|
||||
# - event: The input event from the click
|
||||
func _on_mouse_left_clicked_item(item : ESCItem, event : InputEvent) -> void:
|
||||
if hover_stack.empty() or hover_stack.back() == item:
|
||||
escoria.logger.info("Item left clicked", [item.global_id, event])
|
||||
escoria.main.current_scene.game.left_click_on_item(item.global_id, event)
|
||||
|
||||
|
||||
# An Escoria item was double-clicked with the LMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item: The Escoria item clicked
|
||||
# - event: The input event from the click
|
||||
func _on_mouse_left_double_clicked_item(item : ESCItem, event : InputEvent) -> void:
|
||||
escoria.logger.info("Item left double clicked", [item.global_id, event])
|
||||
escoria.main.current_scene.game.left_double_click_on_item(item.global_id, event)
|
||||
|
||||
|
||||
# An Escoria item was clicked with the RMB
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - item: The Escoria item clicked
|
||||
# - event: The input event from the click
|
||||
func _on_mouse_right_clicked_item(item : ESCItem, event : InputEvent) -> void:
|
||||
escoria.logger.info("Item right clicked", [item.global_id, event])
|
||||
escoria.main.current_scene.game.right_click_on_item(item.global_id, event)
|
||||
|
||||
|
||||
##################################################################################
|
||||
|
||||
# The mousewheel was turned
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - direction: The direction the wheel was turned. 1 = up, -1 = down
|
||||
func _on_mousewheel_action(direction : int):
|
||||
escoria.main.current_scene.game.mousewheel_action(direction)
|
||||
|
||||
|
||||
##################################################################################
|
||||
# Event when the pause menu was requested
|
||||
func _on_pause_menu_requested():
|
||||
escoria.main.current_scene.game.pause_game()
|
||||
|
||||
func clean_hover_stack():
|
||||
|
||||
# Clean the hover stack
|
||||
func _clean_hover_stack():
|
||||
for e in hover_stack:
|
||||
if e == null or !is_instance_valid(e):
|
||||
hover_stack.erase(e)
|
||||
|
||||
func hover_stack_pop(item):
|
||||
|
||||
# Remove the given item from the stack
|
||||
func _hover_stack_pop(item):
|
||||
hover_stack.erase(item)
|
||||
|
||||
################################################################################
|
||||
|
||||
func _on_pause_menu_requested():
|
||||
escoria.main.current_scene.game.pause_game()
|
||||
|
||||
@@ -1,43 +1,55 @@
|
||||
# Escoria main room handling and scene switcher
|
||||
extends Node
|
||||
|
||||
# This script is basically the scene-switcher.
|
||||
|
||||
# Global id of the last scene the player was before current scene
|
||||
var last_scene_global_id
|
||||
# Current scene room being displayed
|
||||
var current_scene
|
||||
var last_scene_global_id: String
|
||||
|
||||
# Current scene room being displayed
|
||||
var current_scene: Node
|
||||
|
||||
# The Escoria context currently in wait state
|
||||
var wait_level
|
||||
|
||||
# FIXME Document this variable
|
||||
var screen_ofs = Vector2(0, 0)
|
||||
|
||||
# ESCBackgroundMusic node
|
||||
# Reference to the ESCBackgroundMusic node
|
||||
onready var bg_music = $bg_music
|
||||
|
||||
# Reference to the scene transition node
|
||||
onready var scene_transition = $layers/curtain/scene_transition
|
||||
|
||||
|
||||
# Set the new current scene
|
||||
# Connect the wait timer event
|
||||
func _ready() -> void:
|
||||
$layers/wait_timer.connect("timeout", self, "_on_wait_finished")
|
||||
|
||||
|
||||
# Set current scene
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - p_scene: Current scene to set
|
||||
func set_scene(p_scene: Node):
|
||||
# - p_scene: Scene to set
|
||||
func set_scene(p_scene: Node) -> void:
|
||||
if !p_scene:
|
||||
escoria.logger.report_errors("main", ["Trying to set empty scene"])
|
||||
|
||||
if current_scene != null:
|
||||
clear_scene()
|
||||
|
||||
|
||||
add_child(p_scene)
|
||||
move_child(p_scene, 0)
|
||||
|
||||
|
||||
current_scene = p_scene
|
||||
check_game_scene_methods()
|
||||
|
||||
set_camera_limits()
|
||||
|
||||
|
||||
func clear_scene():
|
||||
# Cleanup the current scene
|
||||
func clear_scene() -> void:
|
||||
if current_scene == null:
|
||||
return
|
||||
|
||||
@@ -48,14 +60,18 @@ func clear_scene():
|
||||
current_scene.free()
|
||||
current_scene = null
|
||||
|
||||
func wait(params : Array, level):
|
||||
wait_level = level
|
||||
$layers/wait_timer.set_wait_time(float(params[0]))
|
||||
$layers/wait_timer.set_one_shot(true)
|
||||
$layers/wait_timer.start()
|
||||
|
||||
# Triggered, when the wait has finished
|
||||
func _on_wait_finished() -> void:
|
||||
escoria.esc_level_runner.finished(wait_level)
|
||||
|
||||
|
||||
func set_camera_limits(camera_limit_id : int = 0):
|
||||
# Set the camera limits
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# * camera_limits_id: The id of the room's camera limits to set
|
||||
func set_camera_limits(camera_limit_id : int = 0) -> void:
|
||||
var limits = {}
|
||||
var scene_camera_limits = current_scene.camera_limits[camera_limit_id]
|
||||
if scene_camera_limits.size.x == 0 and scene_camera_limits.size.y == 0:
|
||||
@@ -65,10 +81,14 @@ func set_camera_limits(camera_limit_id : int = 0):
|
||||
area = child.get_full_area_rect2()
|
||||
break
|
||||
|
||||
# if the background is smaller than the viewport, we want the camera to stick centered on the background
|
||||
if area.size.x == 0 or area.size.y == 0 or area.size < get_viewport().size:
|
||||
escoria.logger.report_warning("main.gd:set_camera_limits()",
|
||||
"No limit area! Using viewport.")
|
||||
# if the background is smaller than the viewport, we want the camera
|
||||
# to stick centered on the background
|
||||
if area.size.x == 0 or area.size.y == 0 \
|
||||
or area.size < get_viewport().size:
|
||||
escoria.logger.report_warning(
|
||||
"main.gd:set_camera_limits()",
|
||||
"No limit area! Using viewport."
|
||||
)
|
||||
area.size = get_viewport().size
|
||||
|
||||
escoria.logger.info("Setting camera limits from scene ", [area])
|
||||
@@ -82,21 +102,25 @@ func set_camera_limits(camera_limit_id : int = 0):
|
||||
else:
|
||||
limits = {
|
||||
"limit_left": scene_camera_limits.position.x,
|
||||
"limit_right": scene_camera_limits.position.x + scene_camera_limits.size.x,
|
||||
"limit_right": scene_camera_limits.position.x + \
|
||||
scene_camera_limits.size.x,
|
||||
"limit_top": scene_camera_limits.position.y,
|
||||
"limit_bottom": scene_camera_limits.position.y + scene_camera_limits.size.y + screen_ofs.y * 2,
|
||||
"limit_bottom": scene_camera_limits.position.y + \
|
||||
scene_camera_limits.size.y + screen_ofs.y * 2,
|
||||
"set_default": true,
|
||||
}
|
||||
escoria.logger.info("Setting camera limits from parameter ", [scene_camera_limits])
|
||||
escoria.logger.info(
|
||||
"Setting camera limits from parameter ",
|
||||
[scene_camera_limits]
|
||||
)
|
||||
|
||||
current_scene.game.get_node("camera").set_limits(limits)
|
||||
current_scene.game.get_node("camera").set_offset(screen_ofs * 2)
|
||||
|
||||
|
||||
"""
|
||||
The game.tscn scene's root node script MUST implement the following methods.
|
||||
If they do not exist, stop immediately. Implement them, even if empty
|
||||
"""
|
||||
# Sanity check that the game.tscn scene's root node script MUST
|
||||
# implement the following methods. If they do not exist, stop immediately.
|
||||
# Implement them, even if empty
|
||||
func check_game_scene_methods():
|
||||
assert(current_scene.game.has_method("left_click_on_bg"))
|
||||
assert(current_scene.game.has_method("right_click_on_bg"))
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
extends Node
|
||||
|
||||
# Main_scene is the entry point for Godot Engine.
|
||||
# This scene sets up the main menu scene to load.
|
||||
extends Node
|
||||
|
||||
|
||||
# Start the main menu
|
||||
func _ready():
|
||||
var main_menu_path = ProjectSettings.get_setting("escoria/ui/main_menu_scene")
|
||||
var main_menu_path = ProjectSettings.get_setting(
|
||||
"escoria/ui/main_menu_scene"
|
||||
)
|
||||
var main_menu_scene = load(main_menu_path).instance()
|
||||
# get_tree().get_root().call_deferred("add_child", main_menu_scene)
|
||||
escoria.call_deferred("add_child", main_menu_scene)
|
||||
|
||||
@@ -177,7 +177,7 @@ func _process(_delta):
|
||||
if typeof(target) == TYPE_VECTOR2 or typeof(target) == TYPE_ARRAY:
|
||||
self.global_position = resolve_target_pos()
|
||||
elif "moved" in target and target.moved \
|
||||
or "moved" in target.Movable and target.Movable.moved:
|
||||
or "moved" in target.movable and target.movable.moved:
|
||||
self.global_position = resolve_target_pos()
|
||||
|
||||
func _ready():
|
||||
|
||||
@@ -46,23 +46,30 @@ func add_new_item_by_id(item_id : String) -> void:
|
||||
item_id = item_id.rsplit("i/", false)[0]
|
||||
if not items_ids_in_inventory.has(item_id):
|
||||
if not escoria.object_manager.has(item_id):
|
||||
escoria.logger.report_errors(
|
||||
"inventory_ui.gd:add_new_item_by_id()",
|
||||
[
|
||||
"Item global id '%s' does not exist." % item_id,
|
||||
"Check item's id in ESCORIA_ALL_ITEMS scene."
|
||||
]
|
||||
)
|
||||
if not all_items.get_inventory_item(item_id):
|
||||
escoria.logger.report_errors(
|
||||
"inventory_ui.gd:add_new_item_by_id()",
|
||||
[
|
||||
"Item global id '%s' doesn't have a " +\
|
||||
"corresponding inventory item." % item_id,
|
||||
"Check item's id in ESCORIA_ALL_ITEMS scene."
|
||||
]
|
||||
)
|
||||
var item_inventory_button = all_items.get_inventory_item(item_id).duplicate()
|
||||
var inventory_file = "%s/%s.tscn" % [
|
||||
ProjectSettings.get_setting(
|
||||
"escoria/ui/items_autoregister_path"
|
||||
).trim_suffix("/"),
|
||||
item_id
|
||||
]
|
||||
if ResourceLoader.exists(inventory_file):
|
||||
escoria.object_manager.register_object(
|
||||
ESCObject.new(
|
||||
item_id,
|
||||
ResourceLoader.load(inventory_file).instance()
|
||||
)
|
||||
)
|
||||
else:
|
||||
escoria.logger.report_errors(
|
||||
"inventory_ui.gd:add_new_item_by_id()",
|
||||
[
|
||||
"Item global id '%s' does not exist." % item_id,
|
||||
"Check item's id in ESCORIA_ALL_ITEMS scene."
|
||||
]
|
||||
)
|
||||
var item_inventory_button = (
|
||||
escoria.object_manager.get_object(item_id).node as ESCItem
|
||||
).inventory_item.duplicate()
|
||||
items_ids_in_inventory[item_id] = item_inventory_button
|
||||
get_node(inventory_ui_container).add_item(item_inventory_button)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/escinventoryitem.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/esc_inventory_item.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/escoria-core/game/assets/images/no_image.png" type="Texture" id=2]
|
||||
|
||||
[node name="inventory_item" type="TextureButton"]
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/items_inventory.gd" type="Script" id=1]
|
||||
|
||||
[node name="items_inventory" type="GridContainer"]
|
||||
columns = 5
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/escterrain.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/escbackground.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/escitem.gd" type="Script" id=3]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/escroom.gd" type="Script" id=4]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/esc_terrain.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/esc_background.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/esc_item.gd" type="Script" id=3]
|
||||
[ext_resource path="res://addons/escoria-core/game/core-scripts/esc_room.gd" type="Script" id=4]
|
||||
|
||||
[sub_resource type="NavigationPolygon" id=1]
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ var result_angles = []
|
||||
func _ready():
|
||||
# Find player animations
|
||||
$player_animations.add_item("")
|
||||
for anim_name in $player.get_animations_list():
|
||||
for anim_name in $player.get_animation_player.get_sprite_frames().get_animation_names():
|
||||
$player_animations.add_item(anim_name)
|
||||
|
||||
# Set initial angles
|
||||
|
||||
Reference in New Issue
Block a user