First commit 🎉
This commit is contained in:
commit
43ea213f9b
728 changed files with 37080 additions and 0 deletions
179
addons/popochiu/engine/audio_manager/audio_cue.gd
Normal file
179
addons/popochiu/engine/audio_manager/audio_cue.gd
Normal file
|
@ -0,0 +1,179 @@
|
|||
@tool
|
||||
class_name PopochiuAudioCue
|
||||
extends Resource
|
||||
## Used to play audio files with extra properties.
|
||||
##
|
||||
## You can set the pitch (with random values), volume, and audio bus, as well as specify whether
|
||||
## it loops, and whether it is 2D positioned.
|
||||
|
||||
|
||||
## The audio file to play.
|
||||
@export var audio: AudioStream
|
||||
## Whether the audio file will loop when played.
|
||||
@export var loop := false : set = set_loop
|
||||
## Whether this audio cue uses a 2D position.
|
||||
@export var is_2d := false
|
||||
## Whether the audio can be played simultaneously with other instances of itself. Especially useful
|
||||
## for audio cues set in a loop (where [member loop] is [code]true[/code]).
|
||||
@export var can_play_simultaneous := true
|
||||
## The pitch value (in semitones) to use when playing the audio file.
|
||||
@export var pitch := 0.0
|
||||
## The volume to use when playing the audio file.
|
||||
@export var volume := 0.0
|
||||
## The range of values to use for randomly changing the pitch of the audio file when played.
|
||||
@export var rnd_pitch := Vector2.ZERO
|
||||
## The range of values to use to randomly changing the volume of the audio file when played.
|
||||
@export var rnd_volume := Vector2.ZERO
|
||||
## Maximum distance from which the audio file is still hearable. This only works if [member is_2d]
|
||||
## is [code]true[/code].
|
||||
@export var max_distance := 2000
|
||||
## The audio bus in which the audio file will be played.
|
||||
@export var bus := "Master"
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Plays this audio cue with a fade that lasts [param duration] seconds. If [param wait_to_end] is
|
||||
## set to [code]true[/code], the function will wait for the audio to finish. You can specify the
|
||||
## starting volume with [param from] and the target volume with [param to], as well as the
|
||||
## [param position_2d] of the [AudioStreamPlayer] or [AudioStreamPlayer2D] that will play the audio
|
||||
## file.
|
||||
func fade(
|
||||
duration := 1.0, wait_to_end := false, from := -80.0, to := INF, position_2d := Vector2.ZERO
|
||||
) -> void:
|
||||
if wait_to_end:
|
||||
await PopochiuUtils.e.am.play_fade_cue(resource_name, duration, from, to, position_2d, true)
|
||||
else:
|
||||
PopochiuUtils.e.am.play_fade_cue(resource_name, duration, from, to, position_2d)
|
||||
|
||||
|
||||
## Plays this audio cue with a fade that lasts [param duration] seconds. If [param wait_to_end] is
|
||||
## set to [code]true[/code], the function will wait for the audio to finish. You can specify the
|
||||
## starting volume with [param from] and the target volume with [param to], as well as the
|
||||
## [param position_2d] of the [AudioStreamPlayer] or [AudioStreamPlayer2D] that will play the audio
|
||||
## file.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_fade(
|
||||
duration := 1.0, wait_to_end := false, from := -80.0, to := INF, position_2d := Vector2.ZERO
|
||||
) -> Callable:
|
||||
return func ():
|
||||
if wait_to_end:
|
||||
await fade(duration, wait_to_end, from, to, position_2d)
|
||||
else:
|
||||
fade(duration, wait_to_end, from, to, position_2d)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
## Stops the audio cue, with an optional fade effect lasting [param fade_duration] seconds.
|
||||
func stop(fade_duration := 0.0) -> void:
|
||||
PopochiuUtils.e.am.stop(resource_name, fade_duration)
|
||||
|
||||
|
||||
## Stops the audio cue, with an optional fade effect lasting [param fade_duration] seconds.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_stop(fade_duration := 0.0) -> Callable:
|
||||
return func ():
|
||||
stop(fade_duration)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
## Changes the [member AudioStreamPlayer.pitch_scale] of the [AudioStreamPlayer] playing the audio
|
||||
## file associated with this audio cue to [param pitch]. If the audio was played with a 2D position,
|
||||
## then [member AudioStreamPlayer2D.pitch_scale] will be affected.
|
||||
func change_stream_pitch(pitch := 0.0) -> void:
|
||||
PopochiuUtils.e.am.change_cue_pitch(resource_name, pitch)
|
||||
|
||||
|
||||
## Changes the [member AudioStreamPlayer.volume_db] of the [AudioStreamPlayer] playing the audio
|
||||
## file associated with this audio cue to [param volume]. If the audio was played with a 2D
|
||||
## position, then [member AudioStreamPlayer2D.volume_db] will be affected.
|
||||
func change_stream_volume(volume := 0.0) -> void:
|
||||
PopochiuUtils.e.am.change_cue_volume(resource_name, volume)
|
||||
|
||||
|
||||
## Returns the value of [member AudioStreamPlayer.pitch_scale] to be applied to the
|
||||
## [AudioStreamPlayer] playing the audio file associated with this audio cue. If the audio was
|
||||
## played with a 2D position, then [member AudioStreamPlayer2D.volume_db] will be affected.
|
||||
func get_pitch_scale() -> float:
|
||||
var p := PopochiuUtils.a.semitone_to_pitch(pitch)
|
||||
|
||||
if rnd_pitch != Vector2.ZERO:
|
||||
p = _get_rnd_pitch()
|
||||
|
||||
return p
|
||||
|
||||
|
||||
## Returns the playback position of this audio cue.
|
||||
func get_cue_playback_position() -> float:
|
||||
return PopochiuUtils.e.am.get_cue_playback_position(resource_name)
|
||||
|
||||
|
||||
## Maps [param values] to the properties of this audio cue. This is used by TabAudio when changing
|
||||
## the script of the audio cue to one of the types: [AudioCueSound] or [AudioCueMusic].
|
||||
func set_values(values: Dictionary) -> void:
|
||||
resource_name = values.resource_name
|
||||
audio = values.audio
|
||||
loop = values.loop
|
||||
is_2d = values.is_2d
|
||||
pitch = values.pitch
|
||||
volume = values.volume
|
||||
rnd_pitch = values.rnd_pitch
|
||||
rnd_volume = values.rnd_volume
|
||||
max_distance = values.max_distance
|
||||
bus = values.bus
|
||||
|
||||
|
||||
## Returns the properties of this audio cue as a [Dictionary]. This is used by TabAudio when
|
||||
## changing the script of the audio cue to one of the types: [AudioCueSound] or [AudioCueMusic].
|
||||
func get_values() -> Dictionary:
|
||||
return {
|
||||
resource_name = resource_name,
|
||||
audio = audio,
|
||||
loop = loop,
|
||||
is_2d = is_2d,
|
||||
pitch = pitch,
|
||||
volume = volume,
|
||||
rnd_pitch = rnd_pitch,
|
||||
rnd_volume = rnd_volume,
|
||||
max_distance = max_distance,
|
||||
bus = bus
|
||||
}
|
||||
|
||||
|
||||
## Returns [code]true[/code] if playing.
|
||||
func is_playing() -> bool:
|
||||
return PopochiuUtils.a.is_playing_cue(resource_name)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_loop(value: bool) -> void:
|
||||
loop = value
|
||||
|
||||
if not audio: return
|
||||
|
||||
match audio.get_class():
|
||||
'AudioStreamOggVorbis', 'AudioStreamMP3':
|
||||
audio.loop = value
|
||||
'AudioStreamWAV':
|
||||
(audio as AudioStreamWAV).loop_mode = (
|
||||
AudioStreamWAV.LOOP_FORWARD if value else AudioStreamWAV.LOOP_DISABLED
|
||||
)
|
||||
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _get_rnd_pitch() -> float:
|
||||
randomize()
|
||||
return PopochiuUtils.a.semitone_to_pitch(pitch + randf_range(rnd_pitch.x, rnd_pitch.y))
|
||||
|
||||
|
||||
func _get_rnd_volume() -> float:
|
||||
randomize()
|
||||
return volume + randf_range(rnd_volume.x, rnd_volume.y)
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/audio_manager/audio_cue.gd.uid
Normal file
1
addons/popochiu/engine/audio_manager/audio_cue.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://btinlvcono726
|
|
@ -0,0 +1,2 @@
|
|||
extends Resource
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://7s3126auywka
|
23
addons/popochiu/engine/audio_manager/audio_cue_music.gd
Normal file
23
addons/popochiu/engine/audio_manager/audio_cue_music.gd
Normal file
|
@ -0,0 +1,23 @@
|
|||
@tool
|
||||
class_name AudioCueMusic
|
||||
extends PopochiuAudioCue
|
||||
## A specific type of [PopochiuAudioCue] designed for playing music.
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Plays this audio cue. It can fade for [param fade_duration] seconds, and you can change the track
|
||||
## starting position in seconds with [param music_position].
|
||||
func play(fade_duration := 0.0, music_position := 0.0) -> void:
|
||||
PopochiuUtils.e.am.play_music_cue(resource_name, fade_duration, music_position)
|
||||
|
||||
|
||||
## Plays this audio cue. It can fade for [param fade_duration] seconds, and you can change the track
|
||||
## starting position in seconds with [param music_position].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_play(fade_duration := 0.0, music_position := 0.0) -> Callable:
|
||||
return func ():
|
||||
await play(fade_duration, music_position)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bccg7ywc586ng
|
31
addons/popochiu/engine/audio_manager/audio_cue_sound.gd
Normal file
31
addons/popochiu/engine/audio_manager/audio_cue_sound.gd
Normal file
|
@ -0,0 +1,31 @@
|
|||
@tool
|
||||
class_name AudioCueSound
|
||||
extends PopochiuAudioCue
|
||||
## A specific type of [PopochiuAudioCue] designed for playing sounds.
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Plays this audio cue. If [param wait_to_end] is set to [code]true[/code], the function will pause
|
||||
## until the audio clip finishes. You can play the file from a specific [param position_2d] in the
|
||||
## scene if [member PopochiuAudioCue.is_2d] is [code]true[/code].
|
||||
func play(wait_to_end := false, position_2d := Vector2.ZERO) -> void:
|
||||
if wait_to_end:
|
||||
await PopochiuUtils.e.am.play_sound_cue(resource_name, position_2d, true)
|
||||
else:
|
||||
PopochiuUtils.e.am.play_sound_cue(resource_name, position_2d)
|
||||
|
||||
|
||||
## Plays this audio cue. If [param wait_to_end] is set to [code]true[/code], the function will pause
|
||||
## until the audio clip finishes. You can play the file from a specific [param position_2d] in the
|
||||
## scene if [member PopochiuAudioCue.is_2d] is [code]true[/code].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_play(wait_to_end := false, position_2d := Vector2.ZERO) -> Callable:
|
||||
return func ():
|
||||
if wait_to_end:
|
||||
await play(true, position_2d)
|
||||
else:
|
||||
play(false, position_2d)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cqbmt4xruu8eo
|
403
addons/popochiu/engine/audio_manager/audio_manager.gd
Normal file
403
addons/popochiu/engine/audio_manager/audio_manager.gd
Normal file
|
@ -0,0 +1,403 @@
|
|||
class_name PopochiuAudioManager
|
||||
extends Node
|
||||
## Handles playing audio using [PopochiuAudioCue]s.
|
||||
##
|
||||
## It plays sound effects and music using [AudioStreamPlayer] or [AudioStreamPlayer2D], creating
|
||||
## these nodes at runtime if needed. By default, it has 6 nodes for positional streams and 5 for
|
||||
## playing non-positional streams.[br][br]
|
||||
## The [b]PopochiuAudioManager[/b] is loaded as a child of [Popochiu] when the game starts.
|
||||
|
||||
## Used to mark stream players created at runtime that should be [method Node.free] when they are
|
||||
## no longer needed.
|
||||
const TEMP_PLAYER := "temporal"
|
||||
## Specifies the path where the volume configuration for the audio buses used in the game is stored.
|
||||
const SETTINGS_PATH = "user://audio_settings.save"
|
||||
|
||||
## Used to convert the value of the pitch set on [member PopochiuAudioCue.pitch] to the
|
||||
## corresponding value needed for the [code]pitch_scale[/code] property of the audio stream players.
|
||||
var twelfth_root_of_two := pow(2, (1.0 / 12))
|
||||
## Stores the volume values for each of the audio buses used by the game.
|
||||
var volume_settings := {}
|
||||
|
||||
var _mx_cues := {}
|
||||
var _sfx_cues := {}
|
||||
var _vo_cues := {}
|
||||
var _ui_cues := {}
|
||||
var _active := {}
|
||||
var _all_in_one := {}
|
||||
# Serves as a map that stores an AudioStreamPlayer/AudioStreamPlayer2D and the tween used to fade
|
||||
# its volume
|
||||
var _fading_sounds := {}
|
||||
var _dflt_volumes := {}
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
for bus_idx in range(AudioServer.get_bus_count()):
|
||||
var bus_name = AudioServer.get_bus_name(bus_idx)
|
||||
volume_settings[bus_name] = AudioServer.get_bus_volume_db(bus_idx)
|
||||
|
||||
for arr in ["mx_cues", "sfx_cues", "vo_cues", "ui_cues"]:
|
||||
for rp in PopochiuResources.get_data_value("audio", arr, []):
|
||||
var ac: PopochiuAudioCue = load(rp)
|
||||
|
||||
self["_%s" % arr][ac.resource_name] = ac
|
||||
_all_in_one[ac.resource_name] = ac
|
||||
_dflt_volumes[ac.resource_name] = ac.volume
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Searches for the [PopochiuAudioCue] identified by [param cue_name] among the cues that are NOT
|
||||
## part of the music group and plays it. You can set a [param position_2d] to play it positionally.
|
||||
## If [param wait_to_end] is set to [code]true[/code], the function will pause until the audio
|
||||
## finishes.
|
||||
func play_sound_cue(cue_name := "", position_2d := Vector2.ZERO, wait_to_end = null) -> Node:
|
||||
var stream_player: Node = null
|
||||
|
||||
if _all_in_one.has(cue_name.to_lower()):
|
||||
var cue: PopochiuAudioCue = _all_in_one[cue_name.to_lower()]
|
||||
stream_player = _play(cue, position_2d)
|
||||
else:
|
||||
PopochiuUtils.print_error("Sound not found: " + cue_name)
|
||||
|
||||
if wait_to_end != null:
|
||||
await get_tree().process_frame
|
||||
|
||||
return null
|
||||
|
||||
if wait_to_end == true and stream_player:
|
||||
await stream_player.finished
|
||||
elif wait_to_end == false:
|
||||
await get_tree().process_frame
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
## Searches for the [PopochiuAudioCue] identified by [param cue_name] among the cues that are part
|
||||
## of the music group and plays it. It can fade for [param fade_duration] seconds, and you can start
|
||||
## playing it from a given [param music_position] in seconds.
|
||||
func play_music_cue(cue_name: String, fade_duration := 0.0, music_position := 0.0) -> Node:
|
||||
var stream_player: Node = null
|
||||
|
||||
if _active.has(cue_name):
|
||||
return _active[cue_name].players[0]
|
||||
|
||||
if _mx_cues.has(cue_name.to_lower()):
|
||||
var cue: PopochiuAudioCue = _mx_cues[cue_name.to_lower()]
|
||||
# NOTE: fixes #27 AudioCues were losing the volume set in editor when
|
||||
# played with a fade
|
||||
cue.volume = _dflt_volumes[cue_name.to_lower()]
|
||||
if fade_duration > 0.0:
|
||||
stream_player = _fade_in(
|
||||
cue,
|
||||
Vector2.ZERO,
|
||||
fade_duration,
|
||||
-80.0,
|
||||
cue.volume,
|
||||
music_position
|
||||
)
|
||||
else:
|
||||
stream_player = _play(cue, Vector2.ZERO, music_position)
|
||||
else:
|
||||
PopochiuUtils.print_error("Music not found: " + cue_name)
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
## Plays the [PopochiuAudioCue] identified by [param cue_name] using a fade that will last
|
||||
## [param duration] seconds. Specify the starting volume with [param from] and the target volume
|
||||
## with [param to]. You can play the audio positionally with [param position_2d] and wait for it
|
||||
## to finish if [param wait_to_end] is set to [code]true[/code].
|
||||
func play_fade_cue(
|
||||
cue_name := "",
|
||||
duration := 1.0,
|
||||
from := -80.0,
|
||||
to := INF,
|
||||
position_2d := Vector2.ZERO,
|
||||
wait_to_end = null
|
||||
) -> Node:
|
||||
var stream_player: Node = null
|
||||
|
||||
if _all_in_one.has(cue_name.to_lower()):
|
||||
var cue: PopochiuAudioCue = _all_in_one[cue_name.to_lower()]
|
||||
stream_player = _fade_in(cue, position_2d, duration, from, to if to != INF else cue.volume)
|
||||
else:
|
||||
PopochiuUtils.print_error("Sound to fade not found " + cue_name)
|
||||
|
||||
if wait_to_end != null:
|
||||
await get_tree().process_frame
|
||||
|
||||
return null
|
||||
|
||||
if wait_to_end == true and stream_player:
|
||||
await stream_player.finished
|
||||
elif wait_to_end == false:
|
||||
await get_tree().process_frame
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
## Stops the [PopochiuAudioCue] identified by [param cue_name]. It can use a fade effect that will
|
||||
## last [param fade_duration] seconds.
|
||||
func stop(cue_name: String, fade_duration := 0.0) -> void:
|
||||
if _active.has(cue_name):
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
|
||||
if is_instance_valid(stream_player):
|
||||
if fade_duration > 0.0:
|
||||
_fade_sound(
|
||||
cue_name, fade_duration, stream_player.volume_db, -80.0
|
||||
)
|
||||
else:
|
||||
stream_player.stop()
|
||||
# Always emit the signal since it won't be emitted if the audio
|
||||
# file haven't reach the end yet
|
||||
stream_player.finished.emit()
|
||||
else:
|
||||
_active.erase(cue_name)
|
||||
|
||||
|
||||
## Returns the playback position of the [PopochiuAudioCue] identified by [param cue_name].
|
||||
func get_cue_playback_position(cue_name: String) -> float:
|
||||
if not _active.has(cue_name): return -1.0
|
||||
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
|
||||
if is_instance_valid(stream_player):
|
||||
return stream_player.get_playback_position()
|
||||
|
||||
return -1.0
|
||||
|
||||
|
||||
## Changes the [code]pitch_scale[/code] of the [PopochiuAudioCue] identified by [param cue_name] to
|
||||
## the value set (in semitones) in [param pitch].
|
||||
func change_cue_pitch(cue_name: String, pitch := 0.0) -> void:
|
||||
if not _active.has(cue_name): return
|
||||
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
stream_player.set_pitch_scale(_semitone_to_pitch(pitch))
|
||||
|
||||
|
||||
## Changes the [code]volume_db[/code] of the [PopochiuAudioCue] identified by [param cue_name] to
|
||||
## the value set in [param volume].
|
||||
func change_cue_volume(cue_name: String, volume := 0.0) -> void:
|
||||
if not _active.has(cue_name): return
|
||||
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
stream_player.volume_db = volume
|
||||
|
||||
|
||||
## Sets [param value] as the volume of the audio bus identified with [param bus_name].
|
||||
func set_bus_volume_db(bus_name: String, value: float) -> void:
|
||||
if volume_settings.has(bus_name):
|
||||
volume_settings[bus_name] = value
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index(bus_name), volume_settings[bus_name]
|
||||
)
|
||||
|
||||
save_sound_settings()
|
||||
|
||||
|
||||
## Saves in the file at [constant SETTINGS_PATH] the volume values of all the audio buses.
|
||||
func save_sound_settings():
|
||||
var file = FileAccess.open(SETTINGS_PATH, FileAccess.WRITE)
|
||||
|
||||
if file == null:
|
||||
PopochiuUtils.print_error("Error opening file: " + SETTINGS_PATH)
|
||||
else:
|
||||
file.store_var(volume_settings)
|
||||
file.close()
|
||||
|
||||
|
||||
## Loads the volume values stored at [constant SETTINGS_PATH] for all the audio buses.
|
||||
func load_sound_settings():
|
||||
var file = FileAccess.open(SETTINGS_PATH, FileAccess.READ)
|
||||
|
||||
if file:
|
||||
volume_settings = file.get_var(true)
|
||||
file.close()
|
||||
for bus_idx in range(AudioServer.get_bus_count()):
|
||||
var bus_name = AudioServer.get_bus_name(bus_idx)
|
||||
if volume_settings.has(bus_name):
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index(bus_name),
|
||||
volume_settings[bus_name]
|
||||
)
|
||||
else:
|
||||
volume_settings[bus_name] = AudioServer.get_bus_volume_db(bus_idx)
|
||||
|
||||
|
||||
## Returns [code]true[/code] if the [PopochiuAudioCue] identified by [param cue_name] is playing.
|
||||
func is_playing_cue(cue_name: String) -> bool:
|
||||
return get_cue_playback_position(cue_name) > -1
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
# Calculates the [code]pitch_scale[/code] value of [param pitch], which is in semitones.
|
||||
func _semitone_to_pitch(pitch: float) -> float:
|
||||
return pow(twelfth_root_of_two, pitch)
|
||||
|
||||
|
||||
# Plays the sound and assigns it to a free AudioStreamPlayer, or creates one if there are no more.
|
||||
func _play(
|
||||
cue: PopochiuAudioCue, position := Vector2.ZERO, from_position := 0.0
|
||||
) -> Node:
|
||||
var player: Node = null
|
||||
|
||||
if cue.is_2d:
|
||||
player = _get_free_stream($Positional)
|
||||
|
||||
if not is_instance_valid(player):
|
||||
player = AudioStreamPlayer2D.new()
|
||||
player.set_meta(TEMP_PLAYER, true)
|
||||
$Active.add_child(player)
|
||||
|
||||
(player as AudioStreamPlayer2D).stream = cue.audio
|
||||
(player as AudioStreamPlayer2D).pitch_scale = cue.get_pitch_scale()
|
||||
(player as AudioStreamPlayer2D).volume_db = cue.volume
|
||||
(player as AudioStreamPlayer2D).max_distance = cue.max_distance
|
||||
(player as AudioStreamPlayer2D).position = position
|
||||
else:
|
||||
player = _get_free_stream($Generic)
|
||||
|
||||
if not is_instance_valid(player):
|
||||
player = AudioStreamPlayer.new()
|
||||
player.set_meta(TEMP_PLAYER, true)
|
||||
$Active.add_child(player)
|
||||
|
||||
(player as AudioStreamPlayer).stream = cue.audio
|
||||
(player as AudioStreamPlayer).pitch_scale = cue.get_pitch_scale()
|
||||
(player as AudioStreamPlayer).volume_db = cue.volume
|
||||
|
||||
var cue_name: String = cue.resource_name
|
||||
|
||||
player.bus = cue.bus
|
||||
player.play(from_position)
|
||||
|
||||
if not player.finished.is_connected(_on_audio_stream_player_finished):
|
||||
player.finished.connect(_on_audio_stream_player_finished.bind(player, cue_name, 0))
|
||||
|
||||
if _active.has(cue_name):
|
||||
_active[cue_name].players.append(player)
|
||||
|
||||
# NOTE: Stop the previous stream player created to play the audio cue
|
||||
# that is in loop to avoid having more than one sound playing.
|
||||
if not _active[cue_name].can_play_simultaneous:
|
||||
stop(cue_name)
|
||||
else:
|
||||
_active[cue_name] = {
|
||||
players = [player],
|
||||
loop = cue.loop,
|
||||
can_play_simultaneous = cue.can_play_simultaneous
|
||||
}
|
||||
|
||||
return player
|
||||
|
||||
|
||||
func _get_free_stream(group: Node):
|
||||
return _reparent(group, $Active, 0)
|
||||
|
||||
|
||||
# Reassigns the [AudioStreamPlayer] to its original group when it finishes so it can be available
|
||||
# for being used again.
|
||||
func _on_audio_stream_player_finished(
|
||||
stream_player: Node, cue_name: String, _debug_idx: int
|
||||
) -> void:
|
||||
if stream_player.has_meta(TEMP_PLAYER):
|
||||
stream_player.queue_free()
|
||||
elif stream_player is AudioStreamPlayer:
|
||||
_reparent($Active, $Generic, stream_player.get_index())
|
||||
else:
|
||||
_reparent($Active, $Positional, stream_player.get_index())
|
||||
|
||||
if _active.has(cue_name):
|
||||
var players: Array = _active[cue_name].players
|
||||
for idx in players.size():
|
||||
if players[idx].get_instance_id() == stream_player.get_instance_id():
|
||||
players.remove_at(idx)
|
||||
break
|
||||
|
||||
if players.is_empty():
|
||||
_active.erase(cue_name)
|
||||
|
||||
if not stream_player.finished.is_connected(_on_audio_stream_player_finished):
|
||||
stream_player.finished.connect(_on_audio_stream_player_finished)
|
||||
|
||||
|
||||
func _reparent(source: Node, target: Node, child_idx: int) -> Node:
|
||||
if not is_instance_valid(source) or source.get_children().is_empty():
|
||||
return null
|
||||
|
||||
var node_to_reparent: Node = source.get_child(child_idx)
|
||||
|
||||
if not is_instance_valid(node_to_reparent):
|
||||
return null
|
||||
|
||||
node_to_reparent.reparent(target)
|
||||
|
||||
return node_to_reparent
|
||||
|
||||
|
||||
func _fade_in(
|
||||
cue: PopochiuAudioCue,
|
||||
position: Vector2,
|
||||
duration := 1.0,
|
||||
from := -80.0,
|
||||
to := 0.0,
|
||||
from_position := 0.0
|
||||
) -> Node:
|
||||
if cue.audio.get_instance_id() in _fading_sounds:
|
||||
from = _fading_sounds[cue.audio.get_instance_id()].stream.volume_db
|
||||
|
||||
var tween: Tween = _fading_sounds[cue.audio.get_instance_id()].tween
|
||||
# Stop the tween only of the sound that is fading
|
||||
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
_fading_sounds[cue.audio.get_instance_id()].finished.emit()
|
||||
_fading_sounds.erase(cue.audio.get_instance_id())
|
||||
|
||||
cue.volume = from
|
||||
|
||||
var stream_player: Node = _play(cue, position, from_position)
|
||||
|
||||
if stream_player:
|
||||
_fade_sound(cue.resource_name, duration, from, to)
|
||||
else:
|
||||
cue.volume = to
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
func _fade_sound(cue_name: String, duration = 1, from = 0, to = 0) -> void:
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
|
||||
if _fading_sounds.has(stream_player.stream.get_instance_id()):
|
||||
_fading_sounds[stream_player.stream.get_instance_id()].tween.kill()
|
||||
|
||||
var t := create_tween().set_ease(Tween.EASE_IN_OUT)
|
||||
t.finished.connect(_fadeout_finished.bind(stream_player, t))
|
||||
t.tween_property(stream_player, "volume_db", to, duration).from(from)
|
||||
|
||||
if from > to :
|
||||
_fading_sounds[stream_player.stream.get_instance_id()] = {
|
||||
stream = stream_player,
|
||||
tween = t
|
||||
}
|
||||
|
||||
|
||||
func _fadeout_finished(stream_player: Node, tween: Tween) -> void:
|
||||
if stream_player.stream.get_instance_id() in _fading_sounds :
|
||||
_fading_sounds.erase(stream_player.stream.get_instance_id())
|
||||
stream_player.stop()
|
||||
tween.finished.disconnect(_fadeout_finished)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bngbmwj65yxut
|
34
addons/popochiu/engine/audio_manager/audio_manager.tscn
Normal file
34
addons/popochiu/engine/audio_manager/audio_manager.tscn
Normal file
|
@ -0,0 +1,34 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://ciufytcstpkcr"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/audio_manager/audio_manager.gd" id="1_3dbpw"]
|
||||
|
||||
[node name="AudioManager" type="Node"]
|
||||
script = ExtResource("1_3dbpw")
|
||||
|
||||
[node name="Positional" type="Node" parent="."]
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D2" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D3" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D4" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D5" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D6" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="Generic" type="Node" parent="."]
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer2" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer3" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer4" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer5" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="Active" type="Node" parent="."]
|
168
addons/popochiu/engine/cursor/cursor.gd
Normal file
168
addons/popochiu/engine/cursor/cursor.gd
Normal file
|
@ -0,0 +1,168 @@
|
|||
class_name PopochiuCursor
|
||||
extends CanvasLayer
|
||||
|
||||
# TODO: Deprecate this? I'll leave it here while we merge the refactor for the
|
||||
# creation popups because in those the Cursor.Type enum is used.
|
||||
enum Type {
|
||||
NONE,
|
||||
ACTIVE,
|
||||
DOWN,
|
||||
IDLE,
|
||||
LEFT,
|
||||
LOOK,
|
||||
RIGHT,
|
||||
SEARCH,
|
||||
TALK,
|
||||
UP,
|
||||
USE,
|
||||
WAIT,
|
||||
}
|
||||
|
||||
@export var is_pixel_perfect := false
|
||||
|
||||
var is_blocked := false
|
||||
|
||||
@onready var main_cursor: AnimatedSprite2D = $MainCursor
|
||||
@onready var secondary_cursor: Sprite2D = $SecondaryCursor
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"Cursor", self)
|
||||
|
||||
|
||||
func _ready():
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
|
||||
|
||||
# Connect to autoload signals
|
||||
PopochiuUtils.e.ready.connect(show_cursor)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
var texture_size := (main_cursor.sprite_frames.get_frame_texture(
|
||||
main_cursor.animation,
|
||||
main_cursor.frame
|
||||
) as Texture2D).get_size()
|
||||
|
||||
var mouse_position: Vector2 = main_cursor.get_global_mouse_position()
|
||||
|
||||
if is_pixel_perfect:
|
||||
# Thanks to @whyshchuck
|
||||
main_cursor.position = Vector2i(mouse_position)
|
||||
secondary_cursor.position = Vector2i(mouse_position)
|
||||
else:
|
||||
main_cursor.position = mouse_position
|
||||
secondary_cursor.position = mouse_position
|
||||
|
||||
if main_cursor.position.x < 1.0:
|
||||
main_cursor.position.x = 1.0
|
||||
elif main_cursor.position.x > PopochiuUtils.e.width - 2.0:
|
||||
main_cursor.position.x = PopochiuUtils.e.width - 2.0
|
||||
|
||||
if main_cursor.position.y < 1.0:
|
||||
main_cursor.position.y = 1.0
|
||||
elif main_cursor.position.y > PopochiuUtils.e.height - 2.0:
|
||||
main_cursor.position.y = PopochiuUtils.e.height - 2.0
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
func show_cursor(anim_name := "normal", ignore_block := false) -> void:
|
||||
if not ignore_block and is_blocked: return
|
||||
|
||||
if (
|
||||
not anim_name.is_empty()
|
||||
and not main_cursor.sprite_frames.has_animation(anim_name)
|
||||
):
|
||||
PopochiuUtils.print_error("Cursor has no animation: %s" % anim_name)
|
||||
return
|
||||
|
||||
main_cursor.play(anim_name)
|
||||
main_cursor.show()
|
||||
secondary_cursor.hide()
|
||||
|
||||
|
||||
func set_secondary_cursor_texture(texture: Texture2D, ignore_block := false) -> void:
|
||||
if not ignore_block and is_blocked: return
|
||||
|
||||
secondary_cursor.texture = texture
|
||||
|
||||
if PopochiuUtils.e.settings.scale_gui:
|
||||
# Scale the cursor based the relation of the texture size compared to the main cursor
|
||||
# texture size
|
||||
secondary_cursor.scale = Vector2.ONE * ceil(
|
||||
float(texture.get_height()) / float(get_cursor_height())
|
||||
)
|
||||
|
||||
secondary_cursor.show()
|
||||
|
||||
|
||||
func remove_secondary_cursor_texture() -> void:
|
||||
secondary_cursor.texture = null
|
||||
|
||||
if PopochiuUtils.e.settings.scale_gui:
|
||||
secondary_cursor.scale = PopochiuUtils.e.scale
|
||||
|
||||
secondary_cursor.hide()
|
||||
|
||||
|
||||
func toggle_visibility(is_visible: bool) -> void:
|
||||
main_cursor.visible = is_visible
|
||||
secondary_cursor.visible = is_visible
|
||||
|
||||
|
||||
func block() -> void:
|
||||
is_blocked = true
|
||||
|
||||
|
||||
func unblock() -> void:
|
||||
is_blocked = false
|
||||
|
||||
|
||||
func scale_cursor(factor: Vector2) -> void:
|
||||
secondary_cursor.scale = factor
|
||||
main_cursor.scale = factor
|
||||
|
||||
|
||||
func get_position() -> Vector2:
|
||||
return secondary_cursor.position
|
||||
|
||||
|
||||
func replace_frames(new_node: AnimatedSprite2D) -> void:
|
||||
main_cursor.sprite_frames = new_node.sprite_frames
|
||||
main_cursor.offset = new_node.offset
|
||||
|
||||
|
||||
func hide_main_cursor() -> void:
|
||||
main_cursor.hide()
|
||||
|
||||
|
||||
func show_main_cursor() -> void:
|
||||
main_cursor.show()
|
||||
|
||||
|
||||
func hide_secondary_cursor() -> void:
|
||||
secondary_cursor.hide()
|
||||
|
||||
|
||||
func show_secondary_cursor() -> void:
|
||||
secondary_cursor.show()
|
||||
|
||||
|
||||
func get_type_name(idx: int) -> String:
|
||||
return Type.keys()[idx].to_snake_case()
|
||||
|
||||
|
||||
func get_cursor_height() -> int:
|
||||
var height := 0
|
||||
|
||||
if main_cursor.visible:
|
||||
height = main_cursor.sprite_frames.get_frame_texture(main_cursor.animation, 0).get_height()
|
||||
elif secondary_cursor.visible:
|
||||
height = secondary_cursor.texture.get_height()
|
||||
|
||||
return height
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/cursor/cursor.gd.uid
Normal file
1
addons/popochiu/engine/cursor/cursor.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://cus5ystyu850m
|
211
addons/popochiu/engine/cursor/cursor.tscn
Normal file
211
addons/popochiu/engine/cursor/cursor.tscn
Normal file
|
@ -0,0 +1,211 @@
|
|||
[gd_scene load_steps=22 format=3 uid="uid://c2pqh7ajiuiy0"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bl3ecai6lvat1" path="res://addons/popochiu/engine/cursor/sprites/cursor.png" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/cursor/cursor.gd" id="1_n3epl"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="10"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(0, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="11"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(32, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="12"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(64, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="15"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(96, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="16"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(128, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="4"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(192, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="5"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(224, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="18"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(256, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="17"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(288, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="9"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(160, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="2"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(320, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="3"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(352, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="14"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(384, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="19"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(416, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="6"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(448, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="7"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(480, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="13"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(512, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="8"]
|
||||
atlas = ExtResource("1")
|
||||
region = Rect2(544, 0, 32, 32)
|
||||
|
||||
[sub_resource type="SpriteFrames" id="1"]
|
||||
animations = [{
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("10")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("11")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("12")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("12")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("11")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"active",
|
||||
"speed": 10.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("15")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("16")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"down",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("4")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("5")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"left",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("18")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"look",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("17")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"none",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("9")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"normal",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("2")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("3")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"right",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("14")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"search",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("19")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"talk",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("6")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("7")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"up",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("13")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"use",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("8")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"wait",
|
||||
"speed": 5.0
|
||||
}]
|
||||
|
||||
[node name="CursorLayer" type="CanvasLayer"]
|
||||
layer = 100
|
||||
script = ExtResource("1_n3epl")
|
||||
|
||||
[node name="MainCursor" type="AnimatedSprite2D" parent="."]
|
||||
texture_filter = 1
|
||||
sprite_frames = SubResource("1")
|
||||
animation = &"normal"
|
||||
|
||||
[node name="SecondaryCursor" type="Sprite2D" parent="."]
|
||||
texture_filter = 1
|
211
addons/popochiu/engine/cursor/cursor_animations.tres
Normal file
211
addons/popochiu/engine/cursor/cursor_animations.tres
Normal file
|
@ -0,0 +1,211 @@
|
|||
[gd_resource type="SpriteFrames" load_steps=22 format=3 uid="uid://d0ny3mket72mf"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bl3ecai6lvat1" path="res://addons/popochiu/engine/cursor/sprites/cursor.png" id="1_qk45r"]
|
||||
[ext_resource type="Texture2D" uid="uid://bocign602kxhh" path="res://addons/popochiu/engine/objects/gui/templates/simple_click/images/simple_click_cursor.png" id="2_05hav"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="10"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(0, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="11"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(32, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="12"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(64, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="15"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(96, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="16"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(128, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_t3qtx"]
|
||||
atlas = ExtResource("2_05hav")
|
||||
region = Rect2(576, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="4"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(192, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="5"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(224, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="18"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(256, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="17"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(288, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="9"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(160, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="2"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(320, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="3"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(352, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="14"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(384, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="19"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(416, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="6"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(448, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="7"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(480, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="13"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(512, 0, 32, 32)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="8"]
|
||||
atlas = ExtResource("1_qk45r")
|
||||
region = Rect2(544, 0, 32, 32)
|
||||
|
||||
[resource]
|
||||
animations = [{
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("10")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("11")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("12")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("12")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("11")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"active",
|
||||
"speed": 10.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("15")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("16")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"down",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("AtlasTexture_t3qtx")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"gui",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("4")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("5")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"left",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("18")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"look",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("17")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"none",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("9")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"normal",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("2")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("3")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"right",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("14")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"search",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("19")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"talk",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("6")
|
||||
}, {
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("7")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"up",
|
||||
"speed": 4.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("13")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"use",
|
||||
"speed": 5.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": SubResource("8")
|
||||
}],
|
||||
"loop": false,
|
||||
"name": &"wait",
|
||||
"speed": 5.0
|
||||
}]
|
BIN
addons/popochiu/engine/cursor/sprites/cursor.png
Normal file
BIN
addons/popochiu/engine/cursor/sprites/cursor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
34
addons/popochiu/engine/cursor/sprites/cursor.png.import
Normal file
34
addons/popochiu/engine/cursor/sprites/cursor.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bl3ecai6lvat1"
|
||||
path="res://.godot/imported/cursor.png-46ea11cf9adf0d4df6d3fbe5a77d49cf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/popochiu/engine/cursor/sprites/cursor.png"
|
||||
dest_files=["res://.godot/imported/cursor.png-46ea11cf9adf0d4df6d3fbe5a77d49cf.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
82
addons/popochiu/engine/helpers/popochiu_characters_helper.gd
Normal file
82
addons/popochiu/engine/helpers/popochiu_characters_helper.gd
Normal file
|
@ -0,0 +1,82 @@
|
|||
class_name PopochiuCharactersHelper
|
||||
extends RefCounted
|
||||
## Helper class to handle things related to [PopochiuCharacters] but that may not be the
|
||||
## responsibility of [PopochiuICharacter].
|
||||
|
||||
static var char_pattern := r'(?<character>.+?)'
|
||||
static var emo_pattern := r'(?:\((?<emotion>\w+)\))?'
|
||||
static var time_pattern := r'(?:\[(?<time>\d+)\])?'
|
||||
static var line_pattern := r':\s*(?<line>.+)'
|
||||
static var emo_or_time_pattern := r'(%s%s|%s%s)?' % [
|
||||
emo_pattern, time_pattern, time_pattern, emo_pattern
|
||||
]
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Defines the [PopochiuCharacter] that will be controlled by players.
|
||||
static func define_player() -> void:
|
||||
var pc := PopochiuUtils.c.get_character(PopochiuResources.get_data_value("setup", "pc", ""))
|
||||
|
||||
# If there is no explicitly configured Player-controlled Character (PC), select the first
|
||||
# PopochiuCharacter on the list of characters to be the default PC
|
||||
if not pc:
|
||||
var characters := PopochiuResources.get_section_keys("characters")
|
||||
if not characters.is_empty():
|
||||
pc = PopochiuUtils.c.get_character(characters[0])
|
||||
|
||||
if pc:
|
||||
PopochiuUtils.c.player = pc
|
||||
|
||||
|
||||
## Evals [param text] to know if it is a wait inside a dialog or if it is a [PopochiuCharacter]
|
||||
## saying something. This is used when calling [method E.queue].
|
||||
static func execute_string(text: String) -> void:
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
return
|
||||
|
||||
var regex = RegEx.new()
|
||||
regex.compile(r'^\.+$')
|
||||
var result = regex.search(text)
|
||||
|
||||
if result:
|
||||
# A shortcut to wait X seconds
|
||||
await PopochiuUtils.e.wait(0.25 * pow(2, result.get_string(0).count(".") - 1))
|
||||
elif ":" in text:
|
||||
await _trigger_dialog_line(text)
|
||||
else:
|
||||
await PopochiuUtils.g.show_system_text(text)
|
||||
|
||||
PopochiuUtils.e.auto_continue_after = -1.0
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
static func _trigger_dialog_line(text: String) -> void:
|
||||
var regex = RegEx.new()
|
||||
regex.compile(r'^%s%s%s$' % [char_pattern, emo_or_time_pattern, line_pattern])
|
||||
var result := regex.search(text)
|
||||
var character_name := result.get_string("character")
|
||||
var emotion := result.get_string("emotion")
|
||||
var change_time := result.get_string("time")
|
||||
var dialogue_line := result.get_string("line")
|
||||
|
||||
var character := PopochiuUtils.c.get_character(character_name)
|
||||
|
||||
if not character:
|
||||
PopochiuUtils.print_warning("Character %s not found to play dialog line." % character_name)
|
||||
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
return
|
||||
|
||||
if emotion:
|
||||
character.emotion = emotion
|
||||
|
||||
if change_time:
|
||||
PopochiuUtils.e.auto_continue_after = float(change_time)
|
||||
|
||||
await character.say(dialogue_line)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://clpl8h74blwby
|
44
addons/popochiu/engine/interfaces/i_audio.gd
Normal file
44
addons/popochiu/engine/interfaces/i_audio.gd
Normal file
|
@ -0,0 +1,44 @@
|
|||
class_name PopochiuIAudio
|
||||
extends Node
|
||||
## Provides access to the [PopochiuAudioCue]s in the game. Access with [b]A[/b] (e.g.
|
||||
## [code]A.sfx_woosh.play()[/code]).
|
||||
##
|
||||
## Interface class that can be used to access all the audio cues in the game in order to play
|
||||
## sound effects and music.[br][br]
|
||||
## Use examples:[br]
|
||||
## [codeblock]
|
||||
## func _on_click() -> void:
|
||||
## await A.sfx_tv_on.play()
|
||||
## await E.queue([
|
||||
## A.mx_toon_town.queue_play(),
|
||||
## A.vo_scream.queue_play(true), # Wait for the audio to finish
|
||||
## A.sfx_boing.queue_play(),
|
||||
## ])
|
||||
## A.mx_house.play()
|
||||
## [/codeblock]
|
||||
|
||||
## Used to convert the value of the pitch set on [member PopochiuAudioCue.pitch] to the
|
||||
## corresponding value needed for the [code]pitch_scale[/code] property of the audio stream players.
|
||||
var twelfth_root_of_two := pow(2, (1.0 / 12))
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"A", self)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Transforms [param pitch] to a value that can be used to modify the
|
||||
## [member AudioStreamPlayer.pitch_scale] or [member AudioStreamPlayer2D.pitch_scale].
|
||||
func semitone_to_pitch(pitch: float) -> float:
|
||||
return pow(twelfth_root_of_two, pitch)
|
||||
|
||||
|
||||
## Returns [code]true[/code] if the [PopochiuAudioCue] identified by [param cue_name] is playing.
|
||||
func is_playing_cue(cue_name: String) -> bool:
|
||||
return PopochiuUtils.e.am.is_playing_cue(cue_name)
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/interfaces/i_audio.gd.uid
Normal file
1
addons/popochiu/engine/interfaces/i_audio.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b0sr3r3nlem0t
|
176
addons/popochiu/engine/interfaces/i_character.gd
Normal file
176
addons/popochiu/engine/interfaces/i_character.gd
Normal file
|
@ -0,0 +1,176 @@
|
|||
class_name PopochiuICharacter
|
||||
extends Node
|
||||
## Provides access to the [PopochiuCharacter]s in the game. Access with [b]C[/b] (e.g.
|
||||
## [code]C.player.say("What a wonderful plugin")[/code]).
|
||||
##
|
||||
## Use it to manipulate Characters. Its script is [b]i_character.gd[/b].[br][br]
|
||||
##
|
||||
## Some things you can do with it:[br][br]
|
||||
## [b]•[/b] Access the Player-controlled Character (PC) directly [code]C.player[/code].[br]
|
||||
## [b]•[/b] Access any character (with autocompletion based on its name).[br]
|
||||
## [b]•[/b] Make characters move or say something.[br][br]
|
||||
##
|
||||
## Example:
|
||||
## [codeblock]
|
||||
## func on_click() -> void:
|
||||
## await C.walk_to_clicked() # Make the PC move to the clicked object
|
||||
## await C.face_clicked() # Make the PC look at the clicked object
|
||||
## await C.player.say("It's a three-headed monkey!!!") # The PC says something
|
||||
## await C.Popsy.say("Don't tell me...") # Another character says something
|
||||
## [/codeblock]
|
||||
|
||||
|
||||
## Emitted when [param character] says [param message].
|
||||
signal character_spoke(character: PopochiuCharacter, message: String)
|
||||
|
||||
## Access to the [PopochiuCharacter] that is the current Player-controlled Character (PC).
|
||||
var player: PopochiuCharacter : set = set_player
|
||||
## Access to the [PopochiuCharacter] that is owning the camera.
|
||||
var camera_owner: PopochiuCharacter
|
||||
## Stores data about the state of each [PopochiuCharacter] in the game. The key of each entry is the
|
||||
## [member PopochiuCharacter.script_name] of the character.
|
||||
var characters_states := {}
|
||||
|
||||
var _characters := {}
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"C", self)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Makes the Player-controlled Character (PC) move (NON-BLOCKING) to the
|
||||
## [member PopochiuClickable.walk_to_point] position of the last clicked [PopochiuClickable] (i.e. a
|
||||
## [PopochiuProp], a [PopochiuHotspot], or another [PopochiuCharacter]) in the room. You can set an
|
||||
## [param offset] relative to the target position.
|
||||
func walk_to_clicked(offset := Vector2.ZERO) -> void:
|
||||
await player.walk_to_clicked(offset)
|
||||
|
||||
|
||||
## Makes the Player-controlled Character (PC) move (NON-BLOCKING) to the
|
||||
## [member PopochiuClickable.walk_to_point] position of the last clicked [PopochiuClickable] (i.e. a
|
||||
## [PopochiuProp], a [PopochiuHotspot], or another [PopochiuCharacter]) in the room. You can set an
|
||||
## [param offset] relative to the target position.
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to_clicked(offset := Vector2.ZERO) -> Callable:
|
||||
return func (): await walk_to_clicked(offset)
|
||||
|
||||
|
||||
## Similar to [method walk_to_clicked] but BLOCKING the GUI to prevent players from clicking other
|
||||
## objects or any point in the room.
|
||||
func walk_to_clicked_blocking(offset := Vector2.ZERO) -> void:
|
||||
await player.walk_to_clicked_blocking(offset)
|
||||
|
||||
|
||||
## Similar to [method walk_to_clicked] but BLOCKING the GUI to prevent players from clicking other
|
||||
## objects or any point in the room.
|
||||
func queue_walk_to_clicked_blocking(offset := Vector2.ZERO) -> Callable:
|
||||
return func (): await walk_to_clicked_blocking(offset)
|
||||
|
||||
|
||||
## Makes the Player-controlled Character (PC) look at the last clicked [PopochiuClickable].
|
||||
func face_clicked() -> void:
|
||||
await player.face_clicked()
|
||||
|
||||
|
||||
## Makes the Player-controlled Character (PC) look at the last clicked [PopochiuClickable].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_clicked() -> Callable:
|
||||
return func (): await face_clicked()
|
||||
|
||||
|
||||
## Makes the camera follow [param c].
|
||||
func change_camera_owner(c: PopochiuCharacter) -> void:
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
camera_owner = c
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
return
|
||||
|
||||
camera_owner = c
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
## Makes the camera follow [param c].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_change_camera_owner(c: PopochiuCharacter) -> Callable:
|
||||
return func (): await change_camera_owner(c)
|
||||
|
||||
|
||||
## Returns the instance of the [PopochiuCharacter] identified with [param script_name]. If the
|
||||
## character doesn't exists, then [code]null[/code] is returned.[br][br]
|
||||
## This method is used by [b]res://game/autoloads/c.gd[/b] to load the instance of each character
|
||||
## (present in that script as a variable for code autocompletion) in runtime.
|
||||
func get_runtime_character(script_name: String) -> PopochiuCharacter:
|
||||
var character: PopochiuCharacter = null
|
||||
|
||||
if _characters.has(script_name):
|
||||
character = _characters[script_name]
|
||||
else:
|
||||
PopochiuUtils.print_error("Character %s is not in the room" % script_name)
|
||||
|
||||
return character
|
||||
|
||||
|
||||
## Returns [code]true[/code] if [param script_name] is equal to [code]player[/code] or exist in
|
||||
## [member characters].
|
||||
func is_valid_character(script_name: String) -> bool:
|
||||
var is_valid := false
|
||||
|
||||
if script_name.to_lower() == "player":
|
||||
is_valid = true
|
||||
else:
|
||||
is_valid = _characters.has(script_name)
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
## Gets a [PopochiuCharacter] identified with [param script_name]. If the instance doesn't exist in
|
||||
## [member characters], then one is created, added to the array, and returned.
|
||||
func get_character(script_name: String) -> PopochiuCharacter:
|
||||
var character: PopochiuCharacter = null
|
||||
|
||||
if script_name.is_empty():
|
||||
return character
|
||||
|
||||
if (
|
||||
script_name.to_lower() == "player"
|
||||
or (is_instance_valid(player) and player.script_name.to_lower() == script_name)
|
||||
):
|
||||
character = player
|
||||
elif _characters.has(script_name):
|
||||
character = _characters[script_name]
|
||||
else:
|
||||
# If the character doesn't exist, try to instantiate it from the list of characters (Resource)
|
||||
# in popochiu_data.cfg
|
||||
character = get_instance(script_name)
|
||||
|
||||
if character:
|
||||
_characters[character.script_name] = character
|
||||
set(character.script_name, character)
|
||||
|
||||
return character
|
||||
|
||||
|
||||
## Gets the instance of the [PopochiuCharacter] identified with [param script_name].
|
||||
func get_instance(script_name: String) -> PopochiuCharacter:
|
||||
var tres_path: String = PopochiuResources.get_data_value("characters", script_name, "")
|
||||
|
||||
if not tres_path:
|
||||
PopochiuUtils.print_error("Character [b]%s[/b] doesn't exist in the project" % script_name)
|
||||
return null
|
||||
|
||||
return load(load(tres_path).scene).instantiate()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_player(value: PopochiuCharacter) -> void:
|
||||
player = value
|
||||
camera_owner = value
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/interfaces/i_character.gd.uid
Normal file
1
addons/popochiu/engine/interfaces/i_character.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b4102ianyo7fo
|
158
addons/popochiu/engine/interfaces/i_dialog.gd
Normal file
158
addons/popochiu/engine/interfaces/i_dialog.gd
Normal file
|
@ -0,0 +1,158 @@
|
|||
class_name PopochiuIDialog
|
||||
extends Node
|
||||
## Provides access to the [PopochiuDialog]s in the game. Access with [b]D[/b] (e.g.
|
||||
## [code]D.AskAboutLoom.start()[/code]).
|
||||
##
|
||||
## Use it to work with branching dialogs and listen to options selection. Its script is
|
||||
## [b]i_dialog.gd[/b].[br][br]
|
||||
##
|
||||
## Some things you can do with it:[br][br]
|
||||
## [b]•[/b] Start a branching dialog.[br]
|
||||
## [b]•[/b] Know when a dialog has finished, or an option in the current list of options is
|
||||
## selected.[br]
|
||||
## [b]•[/b] Create a list of options on the fly.[br][br]
|
||||
##
|
||||
## Example:
|
||||
## [codeblock]
|
||||
## func on_click() -> void:
|
||||
## # Create a dialog with 3 options
|
||||
## var opt: PopochiuDialogOption = await D.show_inline_dialog([
|
||||
## "Ask Popsy something", "Give Popsy a hug", "Do nothing"
|
||||
## ])
|
||||
##
|
||||
## # The options IDs will go from 0 to the size - 1 of the array passed to D.show_inline_dialog
|
||||
## match opt.id:
|
||||
## "0": # "Ask Popsy something" was selected
|
||||
## D.ChatWithPopsy.start() # Start the ChatWithPopsy dialog
|
||||
## "1": # "Give Popsy a hug" was selected
|
||||
## await C.walk_to_clicked()
|
||||
## await C.player.play_hug()
|
||||
## "2": # "Do nothing" was selected
|
||||
## await C.player.say("Maybe later...")
|
||||
## [/codeblock]
|
||||
|
||||
## Emitted when [param dlg] starts.
|
||||
signal dialog_started(dlg: PopochiuDialog)
|
||||
## Emitted when an [param opt] is selected in the current dialog.
|
||||
signal option_selected(opt: PopochiuDialogOption)
|
||||
## Emitted when [param dlg] finishes.
|
||||
signal dialog_finished(dlg: PopochiuDialog)
|
||||
## Emitted when the list of available [param options] in the current dialog is requested.
|
||||
signal dialog_options_requested(options: Array[PopochiuDialogOption])
|
||||
## Emitted when an inline dialog is created based on a list of [param options].
|
||||
signal inline_dialog_requested(options: Array)
|
||||
|
||||
## Whether a dialog is playing.
|
||||
var active := false
|
||||
## Stores data about the state of each [PopochiuDialog] in the game. The key of each entry is the
|
||||
## [member PopochiuDialog.script_name] of the dialog.
|
||||
var trees := {}
|
||||
## Provides access to the dialog that is currently playing.
|
||||
var current_dialog: PopochiuDialog = null : set = set_current_dialog
|
||||
## Provides access to the currently selected option in the dialog that is currently playing.
|
||||
var selected_option: PopochiuDialogOption = null
|
||||
## Provides access to the branching dialog that was played before the current one. I.e. Could be
|
||||
## used to return to the previous dialog after exhausting the options in the currently playing one.
|
||||
var prev_dialog: PopochiuDialog = null
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"D", self)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Displays a list of [param options], similar to a branching dialog, and returns the selected
|
||||
## [PopochiuDialogOption].
|
||||
func show_inline_dialog(options: Array) -> PopochiuDialogOption:
|
||||
active = true
|
||||
|
||||
if current_dialog:
|
||||
PopochiuUtils.d.option_selected.disconnect(current_dialog._on_option_selected)
|
||||
|
||||
inline_dialog_requested.emit(options)
|
||||
|
||||
var pdo: PopochiuDialogOption = await option_selected
|
||||
|
||||
if current_dialog:
|
||||
PopochiuUtils.d.option_selected.connect(current_dialog._on_option_selected)
|
||||
else:
|
||||
active = false
|
||||
PopochiuUtils.g.unblock()
|
||||
|
||||
return pdo
|
||||
|
||||
|
||||
## Halts the currently playing [PopochiuDialog].
|
||||
func finish_dialog() -> void:
|
||||
dialog_finished.emit(current_dialog)
|
||||
|
||||
|
||||
## Makes the Player-controlled Character (PC) to say the selected option in a branching dialog.
|
||||
func say_selected() -> void:
|
||||
await PopochiuUtils.c.player.say(selected_option.text)
|
||||
|
||||
|
||||
## Transforms any text to gibberish preserving bbcode tags
|
||||
func create_gibberish(input_string: String) -> String:
|
||||
var output_text := ""
|
||||
var bbcode := false
|
||||
var letters := [
|
||||
"a","e","i","o","u",
|
||||
"y","b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","z"
|
||||
]
|
||||
for chr in input_string:
|
||||
if(chr == "["):
|
||||
bbcode = true
|
||||
elif(chr == "]"):
|
||||
output_text += chr
|
||||
bbcode = false
|
||||
continue
|
||||
|
||||
if (!bbcode):
|
||||
if (chr != " "):
|
||||
output_text += letters[randi_range(0,letters.size()-1)]
|
||||
else:
|
||||
output_text += " "
|
||||
else:
|
||||
output_text += chr
|
||||
return output_text
|
||||
|
||||
|
||||
## @deprecated
|
||||
## Now it is [method get_instance].
|
||||
func get_dialog_instance(script_name: String) -> PopochiuDialog:
|
||||
return get_instance(script_name)
|
||||
|
||||
|
||||
## Gets the instance of the [PopochiuDialog] identified with [param script_name].
|
||||
func get_instance(script_name: String) -> PopochiuDialog:
|
||||
var tres_path: String = PopochiuResources.get_data_value("dialogs", script_name, "")
|
||||
|
||||
if not tres_path:
|
||||
PopochiuUtils.print_error("Dialog [b]%s[/b] doesn't exist in the project" % script_name)
|
||||
return null
|
||||
|
||||
return load(tres_path)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_current_dialog(value: PopochiuDialog) -> void:
|
||||
current_dialog = value
|
||||
active = true
|
||||
|
||||
await self.dialog_finished
|
||||
|
||||
# Save the state of the dialog
|
||||
trees[current_dialog.script_name] = current_dialog
|
||||
|
||||
active = false
|
||||
current_dialog = null
|
||||
selected_option = null
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/interfaces/i_dialog.gd.uid
Normal file
1
addons/popochiu/engine/interfaces/i_dialog.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dtjt7q7s51g0e
|
167
addons/popochiu/engine/interfaces/i_graphic_interface.gd
Normal file
167
addons/popochiu/engine/interfaces/i_graphic_interface.gd
Normal file
|
@ -0,0 +1,167 @@
|
|||
class_name PopochiuIGraphicInterface
|
||||
extends Node
|
||||
## Provides access to the in-game [PopochiuGraphicInterface] (GUI). Access with [b]G[/b] (e.g.
|
||||
## [code]G.block()[/code]).[br][br]
|
||||
##
|
||||
## Use it to manage the GUI. Its script is [b]i_graphic_interface.gd[/b].[br][br]
|
||||
##
|
||||
## Some things you can do with it:[br][br]
|
||||
##
|
||||
## [b]•[/b] Show messages in the middle of the screen (like a narrator or a game message).[br]
|
||||
## [b]•[/b] Show info about hovered objects in the game.[br]
|
||||
## [b]•[/b] Show, hide, block or unblock the GUI.[br][br]
|
||||
##
|
||||
## Examples:
|
||||
## [codeblock]
|
||||
## G.show_info('Click this to open the main menu')
|
||||
## G.display('There are no actions set for this object')
|
||||
## G.hide_interface()
|
||||
## G.connect('inventory_shown', self, '_play_inventory_sfx')
|
||||
## [/codeblock]
|
||||
|
||||
## Emitted when [method block] is called. [PopochiuGraphicInterface] connects to this signal in
|
||||
## order to block the GUI.
|
||||
signal blocked
|
||||
## Emitted when [method unblock] is called. [PopochiuGraphicInterface] connects to this signal in
|
||||
## order to unblock the GUI.
|
||||
signal unblocked
|
||||
## Emitted when [method hide_interface] is called. [PopochiuGraphicInterface] connects to this
|
||||
## signal in order to hide the GUI.
|
||||
signal hidden
|
||||
## Emitted when [method show_interface] is called. [PopochiuGraphicInterface] connects to this
|
||||
## signal in order to show the GUI.
|
||||
signal shown
|
||||
## Emitted when the cursor enters (hover) a [param clickable].
|
||||
signal mouse_entered_clickable(clickable: PopochiuClickable)
|
||||
## Emitted when the cursor exits a [param clickable].
|
||||
signal mouse_exited_clickable(clickable: PopochiuClickable)
|
||||
## Emitted when the cursor enters (hover) a [param inventory_item].
|
||||
signal mouse_entered_inventory_item(inventory_item: PopochiuInventoryItem)
|
||||
## Emitted when the cursor exits a [param inventory_item].
|
||||
signal mouse_exited_inventory_item(inventory_item: PopochiuInventoryItem)
|
||||
## Emitted when a [PopochiuCharacter] begins to say a dialogue line.
|
||||
signal dialog_line_started
|
||||
## Emitted when a [PopochiuCharacter] finishes saying a dialogue line.
|
||||
signal dialog_line_finished
|
||||
## Emitted when [method show_hover_text] so the GUI can show [param message] in the hover text.
|
||||
## I.e. when a [PopochiuClickable] is hovered.
|
||||
signal hover_text_shown(message: String)
|
||||
## Emitted when [method show_system_text] so the GUI can show [param message] as a system text.
|
||||
signal system_text_shown(message: String)
|
||||
## Emitted when the system text disappears after a click on the screen.
|
||||
signal system_text_hidden
|
||||
## Emitted when the [PopochiuPopup] identified by [member PopochiuPopup.script_name] is opened.
|
||||
signal popup_requested(script_name: StringName)
|
||||
# NOTE: Maybe add some signals for clicking objects and items
|
||||
#signal clicked_clickable(clickable: PopochiuClickable)
|
||||
#signal clicked_inventory_item(inventory_item: PopochiuInventoryItem)
|
||||
## Emitted when the dialog options of the running [PopochiuDialog] are shown.
|
||||
signal dialog_options_shown
|
||||
## Emitted when a game is loaded and the GUI has shown (or not shown) a notification to the player.
|
||||
signal load_feedback_finished
|
||||
|
||||
## Whether the GUI is blocked or not.
|
||||
var is_blocked := false
|
||||
## Provides access to the identifier of the GUI template used by the game.
|
||||
var template := ""
|
||||
## Provides access to the [PopochiuGraphicInterface] of the GUI template used by the game.
|
||||
var gui: PopochiuGraphicInterface
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"G", self)
|
||||
|
||||
|
||||
func _ready():
|
||||
template = PopochiuResources.get_data_value("ui", "template", "")
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Displays [param msg] at the center of the screen, useful for narration, instructions, or warnings
|
||||
## to players. Temporarily blocks the GUI until players click anywhere on the game window, causing
|
||||
## the text to disappear.
|
||||
func show_system_text(msg: String) -> void:
|
||||
# NOTE: Not sure if this logic should happen here. Perhaps it could trigger a signal to which
|
||||
# the in-game graphic interface connects, allowing it to handle the logic.
|
||||
if not PopochiuUtils.e.playing_queue and gui.popups_stack.is_empty():
|
||||
block()
|
||||
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
await get_tree().process_frame
|
||||
|
||||
return
|
||||
|
||||
system_text_shown.emit(PopochiuUtils.e.get_text(msg))
|
||||
await system_text_hidden
|
||||
|
||||
if not PopochiuUtils.e.playing_queue and gui.popups_stack.is_empty():
|
||||
unblock()
|
||||
|
||||
|
||||
## Displays [param msg] at the center of the screen, useful for narration, instructions, or warnings
|
||||
## to players. Temporarily blocks the GUI until players click anywhere on the game window, causing
|
||||
## the text to disappear.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_show_system_text(msg: String) -> Callable:
|
||||
return func (): await show_system_text(msg)
|
||||
|
||||
|
||||
## Displays [param msg] in the game window without blocking interactions. Used to show players the
|
||||
## name of objects where the cursor is positioned (i.e., a [PopochiuClickable]). It could also be
|
||||
## used to show players what will happen if they use the left click or right click.
|
||||
func show_hover_text(msg := '') -> void:
|
||||
hover_text_shown.emit(msg)
|
||||
|
||||
|
||||
## Causes the in-game graphic interface (GUI) to be blocked. This prevents players from interacting
|
||||
## with the game elements.
|
||||
func block() -> void:
|
||||
is_blocked = true
|
||||
blocked.emit()
|
||||
|
||||
|
||||
## Causes the in-game graphic interface (GUI) to be unblocked.
|
||||
func unblock(wait := false) -> void:
|
||||
is_blocked = false
|
||||
|
||||
if wait:
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
|
||||
if is_blocked: return
|
||||
|
||||
unblocked.emit()
|
||||
|
||||
|
||||
## Makes the in-game graphic interface (GUI) to hide.
|
||||
func hide_interface() -> void:
|
||||
hidden.emit()
|
||||
|
||||
|
||||
## Makes the in-game graphic interface (GUI) to hide.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_hide_interface() -> Callable:
|
||||
return func(): hide_interface()
|
||||
|
||||
|
||||
## Makes the in-game graphic interface (GUI) to show.
|
||||
func show_interface() -> void:
|
||||
shown.emit()
|
||||
|
||||
|
||||
## Makes the in-game graphic interface (GUI) to show.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_show_interface() -> Callable:
|
||||
return func(): show_interface()
|
||||
|
||||
|
||||
## Returns the name of the cursor texture to show.
|
||||
func get_cursor_name() -> String:
|
||||
if not is_instance_valid(gui): return ""
|
||||
|
||||
return gui.get_cursor_name()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bv4x1t3ugm6vk
|
203
addons/popochiu/engine/interfaces/i_inventory.gd
Normal file
203
addons/popochiu/engine/interfaces/i_inventory.gd
Normal file
|
@ -0,0 +1,203 @@
|
|||
class_name PopochiuIInventory
|
||||
extends Node
|
||||
## Provides access to the [PopochiuInventoryItem]s in the game. Access with [b]I[/b] (e.g.,
|
||||
## [code]I.Key.add()[/code]).
|
||||
##
|
||||
## Use it to manage the inventory. Its script is [b]i_inventory.gd[/b].[br][br]
|
||||
##
|
||||
## Some things you can do with it:[br][br]
|
||||
## [b]•[/b] Add and remove items in the inventory.[br]
|
||||
## [b]•[/b] Change the cursor to the appearance of an inventory item.[br]
|
||||
## [b]•[/b] Detect when an item has been added or removed.[br][br]
|
||||
##
|
||||
## Examples:
|
||||
## [codeblock]
|
||||
## # Add the DeckOfCards item to the inventory.
|
||||
## I.DeckOfCards.add_now(false)
|
||||
##
|
||||
## # Add the Key item to the inventory and make it the selected one.
|
||||
## I.Key.add_as_active()
|
||||
##
|
||||
## # Remove the Card item from the inventory. Inside an E.run([])
|
||||
## I.Card.remove()
|
||||
##
|
||||
## # Add the ToyCar item after some dialog lines
|
||||
## E.queue([
|
||||
## "Player: Oh, is the toy car I need",
|
||||
## I.ToyCar.queue_add(),
|
||||
## "Player: Now I will be able to enter the private club",
|
||||
## ])
|
||||
## [/codeblock]
|
||||
|
||||
## Emitted when [param item] is added to the inventory. [param animate] can be utilized by the GUI
|
||||
## to display an animation of the item entering the inventory.
|
||||
signal item_added(item: PopochiuInventoryItem, animate: bool)
|
||||
## Emitted when the [param item] has completed entering the inventory, signifying the end of the GUI
|
||||
## animation.
|
||||
signal item_add_done(item: PopochiuInventoryItem)
|
||||
## Emitted when [param item] is removed from the inventory. [param animate] can be employed by the
|
||||
## GUI to display an animation of the item leaving the inventory.
|
||||
signal item_removed(item: PopochiuInventoryItem, animate: bool)
|
||||
## Emitted when the [param item] has completed leaving the inventory, indicating the end of the GUI
|
||||
## animation.
|
||||
signal item_remove_done(item: PopochiuInventoryItem)
|
||||
## Emitted when [param item] is replaced in the inventory by [param new_item]. Useful for handling
|
||||
## inventory item combinations.
|
||||
signal item_replaced(item: PopochiuInventoryItem, new_item: PopochiuInventoryItem)
|
||||
## Emitted when an item replacement has finished.
|
||||
signal item_replace_done
|
||||
## Emitted when the [param item] has finished leaving the inventory (i.e. when the GUI animation
|
||||
## is complete).
|
||||
signal item_discarded(item: PopochiuInventoryItem)
|
||||
## Emitted when [param item] is selected in the inventory.
|
||||
signal item_selected(item: PopochiuInventoryItem)
|
||||
## Emitted when the inventory is about to be displayed. You can specify the duration it remains
|
||||
## visible with [param time] in seconds.
|
||||
signal inventory_show_requested(time: float)
|
||||
## Emitted once the animation that displays the inventory has finished.
|
||||
signal inventory_shown
|
||||
## Emitted when you want to hide the inventory. [param use_anim] can be used to determine whether or
|
||||
## not to use an animation in the GUI.
|
||||
signal inventory_hide_requested(use_anim: bool)
|
||||
|
||||
## Provides access to the inventory item that is currently selected.
|
||||
var active: PopochiuInventoryItem : set = set_active
|
||||
## Provides access to the inventory item that was clicked.
|
||||
var clicked: PopochiuInventoryItem
|
||||
# ---- Used for saving/loading the game ------------------------------------------------------------
|
||||
## [Array] containing instances of the currently held [PopochiuInventoryItem]s.
|
||||
var items := []
|
||||
## Stores data about the state of each [PopochiuInventoryItem] in the game. The key of each entry is
|
||||
## the [member PopochiuInventoryItem.script_name] of the item.
|
||||
var items_states := {}
|
||||
# ------------------------------------------------------------ Used for saving/loading the game ----
|
||||
|
||||
var _item_instances := {}
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"I", self)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Removes all the items that are currently in the inventory. If [param in_bg] is [code]true[/code],
|
||||
## then the items are removed without calling [method PopochiuInventoryItem.discard] for each item.
|
||||
func clean_inventory(in_bg := false) -> void:
|
||||
items.clear()
|
||||
|
||||
for instance in _item_instances:
|
||||
var pii: PopochiuInventoryItem = _item_instances[instance]
|
||||
|
||||
if not pii.in_inventory: continue
|
||||
if not in_bg: await pii.discard()
|
||||
|
||||
pii.remove(!in_bg)
|
||||
|
||||
|
||||
## Displays the inventory for a duration of [param time] seconds.
|
||||
func show_inventory(time := 1.0) -> void:
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
inventory_show_requested.emit(time)
|
||||
|
||||
await self.inventory_shown
|
||||
|
||||
|
||||
## Displays the inventory for a duration of [param time] seconds.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_show_inventory(time := 1.0) -> Callable:
|
||||
return func (): await show_inventory(time)
|
||||
|
||||
|
||||
## Hides the inventory. If [param use_anim] is set to [code]true[/code], a GUI animation is applied.
|
||||
func hide_inventory(use_anim := true) -> void:
|
||||
inventory_hide_requested.emit(use_anim)
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
## Hides the inventory. If [param use_anim] is set to [code]true[/code], a GUI animation is applied.
|
||||
## [br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_hide_inventory(use_anim := true) -> Callable:
|
||||
return func (): await hide_inventory(use_anim)
|
||||
|
||||
|
||||
## Returns the instance of the [PopochiuInventoryItem] identified with [param item_name]. If the
|
||||
## item doesn't exists, then [code]null[/code] is returned.[br][br]
|
||||
## This method is used by [b]res://game/autoloads/i.gd[/b] to load the instace of each item
|
||||
## (present in that script as a variable for code autocompletion) in runtime.
|
||||
func get_item_instance(item_name: String) -> PopochiuInventoryItem:
|
||||
var item: PopochiuInventoryItem = null
|
||||
|
||||
if _item_instances.has(item_name):
|
||||
item = _item_instances[item_name]
|
||||
else:
|
||||
# If the item is not in the list of items, then try to instantiate it
|
||||
item = get_instance(item_name)
|
||||
|
||||
if item:
|
||||
_item_instances[item.script_name] = item
|
||||
set(item.script_name, item)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
## Gets the instance of the [PopochiuInventoryItem] identified with [param script_name].
|
||||
func get_instance(script_name: String) -> PopochiuInventoryItem:
|
||||
var tres_path: String = PopochiuResources.get_data_value("inventory_items", script_name, "")
|
||||
|
||||
if not tres_path:
|
||||
PopochiuUtils.print_error(
|
||||
"Inventory item [b]%s[/b] doesn't exist in the project" % script_name
|
||||
)
|
||||
return null
|
||||
|
||||
return load(load(tres_path).scene).instantiate()
|
||||
|
||||
|
||||
## Sets the cursor to use the texture of [param item].
|
||||
func set_active_item(item: PopochiuInventoryItem = null) -> void:
|
||||
if is_instance_valid(item):
|
||||
active = item
|
||||
else:
|
||||
active = null
|
||||
|
||||
|
||||
## Verifies if the item identified as [param item_name] is in the inventory.
|
||||
func is_item_in_inventory(item_name: String) -> bool:
|
||||
var i: PopochiuInventoryItem = get_item_instance(item_name)
|
||||
return is_instance_valid(i) and i.in_inventory
|
||||
|
||||
|
||||
## Checks whether the inventory has reached its limit.
|
||||
func is_full() -> bool:
|
||||
return (
|
||||
PopochiuUtils.e.settings.inventory_limit > 0
|
||||
and PopochiuUtils.e.settings.inventory_limit == items.size()
|
||||
)
|
||||
|
||||
|
||||
## Deselects the [member active] item.
|
||||
func deselect_active() -> void:
|
||||
active = null
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_active(value: PopochiuInventoryItem) -> void:
|
||||
if is_instance_valid(active):
|
||||
active.unselected.emit()
|
||||
|
||||
active = value
|
||||
|
||||
item_selected.emit(active)
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/interfaces/i_inventory.gd.uid
Normal file
1
addons/popochiu/engine/interfaces/i_inventory.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b3yk4isvjtpx3
|
354
addons/popochiu/engine/interfaces/i_room.gd
Normal file
354
addons/popochiu/engine/interfaces/i_room.gd
Normal file
|
@ -0,0 +1,354 @@
|
|||
class_name PopochiuIRoom
|
||||
extends Node
|
||||
## Provides access to the [PopochiuRoom]s in the game. Access with [b]R[/b] (e.g.
|
||||
## [code]R.House.get_prop("Drawer")[/code]).
|
||||
##
|
||||
## Use it to access props, hotspots, regions and walkable areas in the current room; or to access to
|
||||
## data from other rooms. Its script is [b]i_room.gd[/b].[br][br]
|
||||
##
|
||||
## Some things you can do with it:[br][br]
|
||||
## [b]•[/b] Access objects inside the current room.[br]
|
||||
## [b]•[/b] Access the state of any room.[br]
|
||||
## [b]•[/b] Move to another room.[br][br]
|
||||
##
|
||||
## Examples:
|
||||
## [codeblock]
|
||||
## R.get_prop("Scissors").modulate.a = 1.0 # Get Scissors prop and make it visible
|
||||
## R.Outside.state.is_raining # Access the is_raining property in the Outside room
|
||||
## [/codeblock]
|
||||
|
||||
## Provides access to the current [PopochiuRoom].
|
||||
var current: PopochiuRoom = null : set = set_current
|
||||
## Stores the state of each [PopochiuRoom] in the game. The key of each room is its
|
||||
## [member PopochiuRoom.script_name], and each value is a [Dictionary] with its properties and the
|
||||
## data of all its [PopochiuProp]s, [PopochiuHotspot]s, [PopochiuWalkableArea]s, [PopochiuRegion]s,
|
||||
## and some data related with the [PopochiuCharacter]s in it. For more info about the data stored,
|
||||
## check the documentation for [PopochiuRoomData].
|
||||
var rooms_states := {}
|
||||
|
||||
var _room_instances := {}
|
||||
var _use_transition_on_room_change := true
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _init() -> void:
|
||||
Engine.register_singleton(&"R", self)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Retrieves the [PopochiuProp] with a [member PopochiuClickable.script_name] matching
|
||||
## [param prop_name].
|
||||
func get_prop(prop_name: String) -> PopochiuProp:
|
||||
return current.get_prop(prop_name)
|
||||
|
||||
|
||||
## Retrieves the [PopochiuHotspot] with a [member PopochiuClickable.script_name] matching
|
||||
## [param hotspot_name].
|
||||
func get_hotspot(hotspot_name: String) -> PopochiuHotspot:
|
||||
return current.get_hotspot(hotspot_name)
|
||||
|
||||
|
||||
## Retrieves the [PopochiuRegion] with a [member PopochiuRegion.script_name] matching
|
||||
## [param region_name].
|
||||
func get_region(region_name: String) -> PopochiuRegion:
|
||||
return current.get_region(region_name)
|
||||
|
||||
|
||||
## Retrieves the [PopochiuWalkableArea] with a [member PopochiuWalkableArea.script_name] matching
|
||||
## [param walkable_area_name].
|
||||
func get_walkable_area(walkable_area_name: String) -> PopochiuWalkableArea:
|
||||
return current.get_walkable_area(walkable_area_name)
|
||||
|
||||
|
||||
## Retrieves the [Marker2D] with a [member Node.name] matching [param marker_name].
|
||||
func get_marker(marker_name: String) -> Marker2D:
|
||||
return current.get_marker(marker_name)
|
||||
|
||||
|
||||
## Retrieves the [b]global position[/b] of the [Marker2D] with a [member Node.name] matching
|
||||
## [param marker_name].
|
||||
func get_marker_position(marker_name: String) -> Vector2:
|
||||
return current.get_marker_position(marker_name)
|
||||
|
||||
|
||||
## Returns all the [PopochiuProp]s in the room.
|
||||
func get_props() -> Array:
|
||||
return get_tree().get_nodes_in_group("props")
|
||||
|
||||
|
||||
## Returns all the [PopochiuHotspot]s in the room.
|
||||
func get_hotspots() -> Array:
|
||||
return get_tree().get_nodes_in_group("hotspots")
|
||||
|
||||
|
||||
## Returns all the [PopochiuRegion]s in the room.
|
||||
func get_regions() -> Array:
|
||||
return get_tree().get_nodes_in_group("regions")
|
||||
|
||||
|
||||
## Returns all the [PopochiuWalkableArea]s in the room.
|
||||
func get_walkable_areas() -> Array:
|
||||
return get_tree().get_nodes_in_group("walkable_areas")
|
||||
|
||||
|
||||
## Returns all the [Marker2D]s in the room.
|
||||
func get_markers() -> Array:
|
||||
return current.get_markers()
|
||||
|
||||
|
||||
## Returns the instance of the [PopochiuRoom] identified with [param script_name]. If the room
|
||||
## doesn't exists, then [code]null[/code] is returned.[br][br]
|
||||
## This method is used by [b]res://game/autoloads/r.gd[/b] to load the instance of each room (present
|
||||
## in that script as a variable for code autocompletion) in runtime.
|
||||
func get_runtime_room(script_name: String) -> PopochiuRoom:
|
||||
var room: PopochiuRoom = null
|
||||
|
||||
if _room_instances.has(script_name):
|
||||
room = _room_instances[script_name]
|
||||
else:
|
||||
room = get_instance(script_name)
|
||||
|
||||
if room:
|
||||
_room_instances[room.script_name] = room
|
||||
|
||||
return room
|
||||
|
||||
|
||||
## Gets the instance of the [PopochiuRoom] identified with [param script_name].
|
||||
func get_instance(script_name: String) -> PopochiuRoom:
|
||||
# Fix #328 by returning the instance of the current room if it matches the instance that the
|
||||
# plugin is looking for
|
||||
if is_instance_valid(current) and current.script_name == script_name:
|
||||
return current
|
||||
|
||||
var tres_path: String = PopochiuResources.get_data_value("rooms", script_name, "")
|
||||
|
||||
if not tres_path:
|
||||
PopochiuUtils.print_error("Room [b]%s[/b] doesn't exist in the project" % script_name)
|
||||
return null
|
||||
|
||||
return load(load(tres_path).scene).instantiate()
|
||||
|
||||
|
||||
## Clears all the [PopochiuRoom] instances to free memory and orphan children.
|
||||
func clear_instances() -> void:
|
||||
for r in _room_instances:
|
||||
(_room_instances[r] as PopochiuRoom).free()
|
||||
|
||||
_room_instances.clear()
|
||||
|
||||
|
||||
## Loads the room with [param script_name]. [param use_transition] can be used to trigger a [i]fade
|
||||
## out[/i] animation before loading the room, and a [i]fade in[/i] animation once it is ready.
|
||||
## If [param store_state] is [code]true[/code] the state of the room will be stored in memory.
|
||||
## [param ignore_change] is used internally by Popochiu to know if it's the first time the room is
|
||||
## loaded when starting the game.
|
||||
func goto_room(
|
||||
script_name := "",
|
||||
use_transition := true,
|
||||
store_state := true,
|
||||
ignore_change := false
|
||||
) -> void:
|
||||
if not PopochiuUtils.e.in_room: return
|
||||
|
||||
PopochiuUtils.e.in_room = false
|
||||
PopochiuUtils.g.block()
|
||||
|
||||
_use_transition_on_room_change = use_transition
|
||||
# Never fade the TL in, if we are entering the first room at game start
|
||||
if use_transition and Engine.get_process_frames() > 0:
|
||||
PopochiuUtils.e.tl.play_transition(PopochiuUtils.e.tl.FADE_IN)
|
||||
await PopochiuUtils.e.tl.transition_finished
|
||||
elif Engine.get_process_frames() > 0:
|
||||
PopochiuUtils.e.tl.show_curtain()
|
||||
|
||||
# Prevent the GUI from showing info coming from the previous room
|
||||
PopochiuUtils.g.show_hover_text()
|
||||
PopochiuUtils.cursor.show_cursor()
|
||||
|
||||
if is_instance_valid(PopochiuUtils.c.player) and Engine.get_process_frames() > 0:
|
||||
PopochiuUtils.c.player.last_room = current.script_name
|
||||
|
||||
# Store the room state
|
||||
if store_state:
|
||||
rooms_states[current.script_name] = current.state
|
||||
current.state.save_children_states()
|
||||
|
||||
# Remove PopochiuCharacter nodes from the room so they are not deleted
|
||||
if Engine.get_process_frames() > 0:
|
||||
current.exit_room()
|
||||
|
||||
# Reset camera config
|
||||
PopochiuUtils.e.camera.restore_default_limits()
|
||||
|
||||
if ignore_change:
|
||||
return
|
||||
|
||||
var rp: String = PopochiuResources.get_data_value("rooms", script_name, "")
|
||||
if rp.is_empty():
|
||||
PopochiuUtils.print_error(
|
||||
"Can't go to room [b]%s[/b] because it doesn't exist" % script_name
|
||||
)
|
||||
return
|
||||
|
||||
if Engine.get_process_frames() == 0:
|
||||
await get_tree().process_frame
|
||||
|
||||
clear_instances()
|
||||
|
||||
PopochiuUtils.e.clear_hovered()
|
||||
PopochiuUtils.e.get_tree().change_scene_to_file(load(rp).scene)
|
||||
|
||||
|
||||
## Called once the loaded [param room] is "ready" ([method Node._ready]).
|
||||
func room_readied(room: PopochiuRoom) -> void:
|
||||
if not is_instance_valid(current):
|
||||
current = room
|
||||
|
||||
# When running from the Editor the first time, use goto_room
|
||||
if Engine.get_process_frames() == 0:
|
||||
await get_tree().process_frame
|
||||
PopochiuUtils.e.in_room = true
|
||||
|
||||
# Calling this will make the camera be set to its default values and will store the state of
|
||||
# the main room (the last parameter will prevent Popochiu from changing the scene to the
|
||||
# same that is already loaded).
|
||||
# Also, use the transition layer to fade in the room, if the setting is enabled.
|
||||
await goto_room(room.script_name, PopochiuUtils.e.settings.show_tl_in_first_room, true, true)
|
||||
|
||||
# Make the camera be ready for the room
|
||||
current.setup_camera()
|
||||
|
||||
# Update the core state
|
||||
if PopochiuUtils.e.loaded_game:
|
||||
PopochiuUtils.c.player = PopochiuUtils.c.get_character(PopochiuUtils.e.loaded_game.player.id)
|
||||
else:
|
||||
current.state.visited = true
|
||||
current.state.visited_times += 1
|
||||
current.state.visited_first_time = current.state.visited_times == 1
|
||||
|
||||
# Add the PopochiuCharacter instances to the room
|
||||
if (rooms_states[room.script_name]["characters"] as Dictionary).is_empty():
|
||||
# Store the initial state of the characters in the room
|
||||
current.state.save_characters()
|
||||
|
||||
current.clean_characters()
|
||||
|
||||
# Load the state of characters in the room
|
||||
for chr_script_name: String in rooms_states[room.script_name]["characters"]:
|
||||
var chr_dic: Dictionary = rooms_states[room.script_name]["characters"][chr_script_name]
|
||||
var chr: PopochiuCharacter = PopochiuUtils.c.get_character(chr_script_name)
|
||||
|
||||
if not chr: continue
|
||||
|
||||
chr.position = Vector2(chr_dic.x, chr_dic.y)
|
||||
chr._looking_dir = chr_dic.facing
|
||||
chr.visible = chr_dic.visible
|
||||
chr.modulate = Color.from_string(chr_dic.modulate, Color.WHITE)
|
||||
chr.self_modulate = Color.from_string(chr_dic.self_modulate, Color.WHITE)
|
||||
chr.light_mask = chr_dic.light_mask
|
||||
chr.baseline = chr_dic.baseline
|
||||
|
||||
if chr_dic.has("walk_to_point"):
|
||||
chr.walk_to_point = PopochiuUtils.unpack_vector_2(chr_dic.walk_to_point)
|
||||
|
||||
if chr_dic.has("look_at_point"):
|
||||
chr.look_at_point = PopochiuUtils.unpack_vector_2(chr_dic.look_at_point)
|
||||
|
||||
current.add_character(chr)
|
||||
|
||||
# If the room must have the player character but it is not part of its $Characters node, then
|
||||
# add the PopochiuCharacter to the room
|
||||
if (
|
||||
current.has_player
|
||||
and is_instance_valid(PopochiuUtils.c.player)
|
||||
and not current.has_character(PopochiuUtils.c.player.script_name)
|
||||
):
|
||||
current.add_character(PopochiuUtils.c.player)
|
||||
# Place the PC in the middle of the room
|
||||
PopochiuUtils.c.player.position = Vector2(PopochiuUtils.e.width, PopochiuUtils.e.height) / 2.0
|
||||
await PopochiuUtils.c.player.idle()
|
||||
|
||||
# Load the state of Props, Hotspots, Regions and WalkableAreas
|
||||
for type in PopochiuResources.ROOM_CHILDREN:
|
||||
for script_name in rooms_states[room.script_name][type]:
|
||||
var node: Node2D = current.callv(
|
||||
"get_" + type.trim_suffix("s"),
|
||||
[(script_name as String).to_pascal_case()]
|
||||
)
|
||||
|
||||
if not is_instance_valid(node):
|
||||
# Fix #320 by ignoring the object if it doesn't exist inside the Room
|
||||
continue
|
||||
|
||||
var node_dic: Dictionary =\
|
||||
rooms_states[room.script_name][type][script_name]
|
||||
|
||||
for property in node_dic:
|
||||
if not PopochiuResources.has_property(node, property): continue
|
||||
|
||||
node[property] = node_dic[property]
|
||||
|
||||
for c in get_tree().get_nodes_in_group("PopochiuClickable"):
|
||||
c.room = current
|
||||
|
||||
await current._on_room_entered()
|
||||
|
||||
if PopochiuUtils.e.loaded_game:
|
||||
PopochiuUtils.c.player.global_position = Vector2(
|
||||
PopochiuUtils.e.loaded_game.player.position.x,
|
||||
PopochiuUtils.e.loaded_game.player.position.y
|
||||
)
|
||||
|
||||
if _use_transition_on_room_change:
|
||||
PopochiuUtils.e.tl.play_transition(PopochiuUtils.e.tl.FADE_OUT)
|
||||
await PopochiuUtils.e.tl.transition_finished
|
||||
|
||||
await PopochiuUtils.e.wait(0.3)
|
||||
else:
|
||||
PopochiuUtils.e.tl.hide_curtain()
|
||||
await get_tree().process_frame
|
||||
|
||||
if not current.hide_gui:
|
||||
PopochiuUtils.g.unblock()
|
||||
|
||||
if PopochiuUtils.e.hovered:
|
||||
PopochiuUtils.g.mouse_entered_clickable.emit(PopochiuUtils.e.hovered)
|
||||
|
||||
PopochiuUtils.e.in_room = true
|
||||
|
||||
if PopochiuUtils.e.loaded_game:
|
||||
PopochiuUtils.e.game_loaded.emit(PopochiuUtils.e.loaded_game)
|
||||
await PopochiuUtils.g.load_feedback_finished
|
||||
|
||||
PopochiuUtils.e.loaded_game = {}
|
||||
|
||||
# This enables the room to listen input events
|
||||
current.is_current = true
|
||||
await current._on_room_transition_finished()
|
||||
|
||||
# Fix #219: Update visited_first_time state once _on_room_transition_finished() finishes
|
||||
current.state.visited_first_time = false
|
||||
|
||||
|
||||
func store_states() -> void:
|
||||
# Store the default state of rooms in the game
|
||||
for room_tres in PopochiuResources.get_section("rooms"):
|
||||
var res: PopochiuRoomData = load(room_tres)
|
||||
rooms_states[res.script_name] = res
|
||||
res.save_children_states()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_current(value: PopochiuRoom) -> void:
|
||||
if not value.is_inside_tree():
|
||||
goto_room(value.script_name)
|
||||
else:
|
||||
current = value
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/interfaces/i_room.gd.uid
Normal file
1
addons/popochiu/engine/interfaces/i_room.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://ban86wtmbkxi2
|
979
addons/popochiu/engine/objects/character/popochiu_character.gd
Normal file
979
addons/popochiu/engine/objects/character/popochiu_character.gd
Normal file
|
@ -0,0 +1,979 @@
|
|||
@tool
|
||||
@icon('res://addons/popochiu/icons/character.png')
|
||||
class_name PopochiuCharacter
|
||||
extends PopochiuClickable
|
||||
## Any object that can move, walk, navigate rooms, or have an inventory.
|
||||
|
||||
## Determines when to flip the [b]$Sprite2D[/b] child.
|
||||
enum FlipsWhen {
|
||||
## The [b]$Sprite2D[/b] child is not flipped.
|
||||
NONE,
|
||||
## The [b]$Sprite2D[/b] child is flipped when the character is looking to the right.
|
||||
LOOKING_RIGHT,
|
||||
## The [b]$Sprite2D[/b] child is flipped when the character is looking to the left.
|
||||
LOOKING_LEFT
|
||||
}
|
||||
## Determines the direction the character is facing
|
||||
enum Looking {
|
||||
RIGHT,
|
||||
## The character is facing down-right [code](x, y)[/code].
|
||||
DOWN_RIGHT,
|
||||
## The character is facing down [code](0, y)[/code].
|
||||
DOWN,
|
||||
## The character is facing down-left [code](-x, y)[/code].
|
||||
DOWN_LEFT,
|
||||
## The character is facing left [code](-x, 0)[/code].
|
||||
LEFT,
|
||||
## The character is facing up-left [code](-x, -y)[/code].
|
||||
UP_LEFT,
|
||||
## The character is facing up [code](0, -y)[/code].
|
||||
UP,
|
||||
## The character is facing up-right [code](x, -y)[/code].
|
||||
UP_RIGHT
|
||||
## The character is facing right [code](x, 0)[/code].
|
||||
}
|
||||
|
||||
## Emitted when a [param character] starts moving from [param start] to [param end]. [PopochiuRoom]
|
||||
## connects to this signal in order to make characters move inside them from one point to another.
|
||||
signal started_walk_to(character: PopochiuCharacter, start: Vector2, end: Vector2)
|
||||
## Emitted when the character is forced to stop while walking.
|
||||
signal stopped_walk
|
||||
## Emitted when the character reaches the ending position when moving from one point to another.
|
||||
signal move_ended
|
||||
## Emitted when the animation to grab things has finished.
|
||||
signal grab_done
|
||||
|
||||
## Empty string constant to perform type checks (String is not nullable in GDScript. See #381, #382).
|
||||
const EMPTY_STRING = ""
|
||||
|
||||
## The [Color] in which the dialogue lines of the character are rendered.
|
||||
@export var text_color := Color.WHITE
|
||||
## Depending on its value, the [b]$Sprite2D[/b] child will be flipped horizontally depending on
|
||||
## which way the character is facing. If the value is [constant NONE], then the
|
||||
## [b]$Sprite2D[/b] child won't be flipped.
|
||||
@export var flips_when: FlipsWhen = FlipsWhen.NONE
|
||||
## Array of [Dictionary] where each element has
|
||||
## [code]{ emotion: String, variations: Array[PopochiuAudioCue] }[/code].
|
||||
## You can use this to define which [PopochiuAudioCue]s to play when the character speaks using a
|
||||
## specific emotion.
|
||||
@export var voices := []: set = set_voices
|
||||
## Whether the character should follow the player-controlled character (PC) when it moves through
|
||||
## the room.
|
||||
@export var follow_player := false: set = set_follow_player
|
||||
## The offset between the player-controlled character (PC) and this character when it follows the
|
||||
## former one.
|
||||
@export var follow_player_offset := Vector2(20, 0)
|
||||
## Array of [Dictionary] where each element has [code]{ emotion: String, avatar: Texture }[/code].
|
||||
## You can use this to define which [Texture] to use as avatar for the character when it speaks
|
||||
## using a specific emotion.
|
||||
@export var avatars := []: set = set_avatars
|
||||
## The speed at which the character will move in pixels per frame.
|
||||
@export var walk_speed := 200.0
|
||||
## Whether the character can or not move.
|
||||
@export var can_move := true
|
||||
## Whether the character ignores or not walkable areas. If [code]true[/code], the character will
|
||||
## move to any point in the room clicked by players without taking into account the walkable areas
|
||||
## in it.
|
||||
@export var ignore_walkable_areas := false
|
||||
## Whether the character will move only when the frame changes on its animation.
|
||||
@export var anti_glide_animation: bool = false
|
||||
## Used by the GUI to calculate where to render the dialogue lines said by the character when it
|
||||
## speaks.
|
||||
@export var dialog_pos: Vector2
|
||||
# This category is used by the Aseprite Importer in order to allow the creation of a section in the
|
||||
# Inspector for the character.
|
||||
@export_category("Aseprite")
|
||||
|
||||
## The stored position of the character. Used when [member anti_glide_animation] is
|
||||
## [code]true[/code].
|
||||
var position_stored = null
|
||||
## Stores the [member PopochiuRoom.script_name] of the previously visited [PopochiuRoom].
|
||||
var last_room := EMPTY_STRING
|
||||
## The suffix text to add to animation names.
|
||||
var anim_suffix := EMPTY_STRING
|
||||
## Whether the character is or not moving through the room.
|
||||
var is_moving := false
|
||||
## The current emotion used by the character.
|
||||
var emotion := EMPTY_STRING
|
||||
##
|
||||
var on_scaling_region: Dictionary = {}
|
||||
## Stores the default walk speed defined in [member walk_speed]. Used by [PopochiuRoom] when scaling
|
||||
## the character if it is inside a [PopochiuRegion] that modifies the scale.
|
||||
var default_walk_speed := 0
|
||||
## Stores the default scale. Used by [PopochiuRoom] when scaling the character if it is inside a
|
||||
## [PopochiuRegion] that modifies the scale.
|
||||
var default_scale := Vector2.ONE
|
||||
# Holds the direction the character is looking at.
|
||||
# Initialized to DOWN.
|
||||
var _looking_dir: int = Looking.DOWN
|
||||
# Holds a suffixes fallback list for the animations to play.
|
||||
# Initialized to the suffixes corresponding to the DOWN direction.
|
||||
var _animation_suffixes: Array = ['_d', '_dr', '_dl', '_r', '_l', EMPTY_STRING]
|
||||
# Holds the last PopochiuClickable that the character reached.
|
||||
var _last_reached_clickable: PopochiuClickable = null
|
||||
# Holds the animation that's currently selected in the character's AnimationPlayer.
|
||||
var _current_animation: String = "null"
|
||||
# Holds the last animation category requested for the character (idle, walk, talk, grab, ...).
|
||||
var _last_requested_animation_label: String = "null"
|
||||
# Holds the direction the character was looking at when the current animation was requested.
|
||||
var _last_requested_animation_dir: int = -1
|
||||
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
|
||||
# Array of the animation suffixes to search for
|
||||
# based on the angle the character is facing.
|
||||
var _valid_animation_suffixes = [
|
||||
['_r', '_l', '_dr', '_dl', '_d'], # 0 - 22.5 degrees
|
||||
['_dr', '_dl', '_r' , '_l', '_d'], # 22.5 - 45 degrees
|
||||
['_dr', '_dl', '_d' , '_r', '_l'], # 45 - 67.5 degrees
|
||||
['_d', '_dr', '_dl', '_r', '_l'], # 67.5 - 90 degrees
|
||||
['_d', '_dl', '_dr', '_l', '_r'], # 90 - 112.5 degrees
|
||||
['_dl', '_dr', '_d', '_l', '_r'], # 112.5 - 135 degrees
|
||||
['_dl', '_dr', '_l', '_r', '_d'], # 135 - 157.5 degrees
|
||||
['_l', '_r', '_dl', '_dr', '_d'], # 157.5 - 180 degrees
|
||||
['_l', '_r', '_ul', '_ur', '_u'], # 180 - 202.5 degrees
|
||||
['_ul', '_ur', '_l', '_r', '_u'], # 202.5 - 225 degrees
|
||||
['_ul', '_ur', '_u', '_l', '_r'], # 225 - 247.5 degrees
|
||||
['_u', '_ul', '_ur', '_l', '_r'], # 247.5 - 270 degrees
|
||||
['_u', '_ur', '_ul', '_r', '_l'], # 270 - 292.5 degrees
|
||||
['_ur', '_ul', '_u', '_r', '_l'], # 292.5 - 315 degrees
|
||||
['_ur', '_ul', '_r', '_l', '_u'], # 315 - 337.5 degrees
|
||||
['_r', '_l', '_ur', '_ul', '_u']] # 337.5 - 360 degrees
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready():
|
||||
super()
|
||||
|
||||
default_walk_speed = walk_speed
|
||||
default_scale = Vector2(scale)
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
hide_helpers()
|
||||
set_process(true)
|
||||
else:
|
||||
set_process(follow_player)
|
||||
|
||||
for child in get_children():
|
||||
if not child is Sprite2D:
|
||||
continue
|
||||
child.frame_changed.connect(_update_position)
|
||||
|
||||
move_ended.connect(_on_move_ended)
|
||||
|
||||
|
||||
func _get_property_list():
|
||||
return [
|
||||
{
|
||||
name = "popochiu_placeholder",
|
||||
type = TYPE_NIL,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Use it to play the idle animation of the character.
|
||||
## [i]Virtual[/i].
|
||||
func _play_idle() -> void:
|
||||
play_animation('idle')
|
||||
|
||||
|
||||
## Use it to play the walk animation of the character.
|
||||
## [i]Virtual[/i].
|
||||
func _play_walk(target_pos: Vector2) -> void:
|
||||
# Set the default parameters for play_animation()
|
||||
var animation_label = 'walk'
|
||||
var animation_fallback = 'idle'
|
||||
|
||||
play_animation(animation_label, animation_fallback)
|
||||
|
||||
|
||||
## Use it to play the talk animation of the character.
|
||||
## [i]Virtual[/i].
|
||||
func _play_talk() -> void:
|
||||
play_animation('talk')
|
||||
|
||||
|
||||
## Use it to play the grab animation of the character.
|
||||
## [i]Virtual[/i].
|
||||
func _play_grab() -> void:
|
||||
play_animation('grab')
|
||||
|
||||
|
||||
func _on_move_ended() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Puts the character in the idle state by playing its idle animation, then waits for
|
||||
## [code]0.2[/code] seconds.
|
||||
## If the character has a [b]$Sprite2D[/b] child, it makes it flip based on the [member flips_when]
|
||||
## value.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_idle() -> Callable:
|
||||
return func(): await idle()
|
||||
|
||||
|
||||
## Puts the character in the idle state by playing its idle animation, then waits for
|
||||
## [code]0.2[/code] seconds.
|
||||
## If the character has a [b]$Sprite2D[/b] child, it makes it flip based on the [member flips_when]
|
||||
## value.
|
||||
func idle() -> void:
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
_flip_left_right(
|
||||
_looking_dir in [Looking.LEFT, Looking.DOWN_LEFT, Looking.UP_LEFT],
|
||||
_looking_dir in [Looking.RIGHT, Looking.DOWN_RIGHT, Looking.UP_RIGHT]
|
||||
)
|
||||
|
||||
# Call the virtual that plays the idle animation
|
||||
_play_idle()
|
||||
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
|
||||
## Makes the character move to [param target_pos] and plays its walk animation.
|
||||
## If the character has a [b]$Sprite2D[/b] child, it makes it flip based on the [member flips_when]
|
||||
## value.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk(target_pos: Vector2) -> Callable:
|
||||
return func(): await walk(target_pos)
|
||||
|
||||
|
||||
## Makes the character move to [param target_pos] and plays its walk animation.
|
||||
## If the character has a [b]$Sprite2D[/b] child, it makes it flip based on the [member flips_when]
|
||||
## value.
|
||||
func walk(target_pos: Vector2) -> void:
|
||||
is_moving = true
|
||||
_last_reached_clickable = null
|
||||
|
||||
# The ROOM will take care of moving the character
|
||||
# and face her in the correct direction from here
|
||||
|
||||
_flip_left_right(
|
||||
target_pos.x < position.x,
|
||||
target_pos.x > position.x
|
||||
)
|
||||
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
is_moving = false
|
||||
await get_tree().process_frame
|
||||
|
||||
position = target_pos
|
||||
PopochiuUtils.e.camera.position = target_pos
|
||||
await get_tree().process_frame
|
||||
|
||||
return
|
||||
|
||||
# Call the virtual that plays the walk animation
|
||||
_play_walk(target_pos)
|
||||
|
||||
# Trigger the signal for the room to start moving the character
|
||||
started_walk_to.emit(self, position, target_pos)
|
||||
await move_ended
|
||||
|
||||
is_moving = false
|
||||
|
||||
|
||||
func turn_towards(target_pos: Vector2) -> void:
|
||||
_flip_left_right(
|
||||
target_pos.x < position.x,
|
||||
target_pos.x > position.x
|
||||
)
|
||||
face_direction(target_pos)
|
||||
_play_walk(target_pos)
|
||||
|
||||
## Makes the character stop moving and emits [signal stopped_walk].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_stop_walking() -> Callable:
|
||||
return func(): await stop_walking()
|
||||
|
||||
|
||||
## Makes the character stop moving and emits [signal stopped_walk].
|
||||
func stop_walking() -> void:
|
||||
is_moving = false
|
||||
|
||||
stopped_walk.emit()
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
## Makes the character to look up by setting [member _looking_dir] to [constant UP] and waits until
|
||||
## [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_up() -> Callable:
|
||||
return func(): await face_up()
|
||||
|
||||
|
||||
## Makes the character to look up by setting [member _looking_dir] to [constant UP] and waits until
|
||||
## [method idle] finishes.
|
||||
func face_up() -> void:
|
||||
face_direction(position + Vector2.UP)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look up and right by setting [member _looking_dir] to [constant UP_RIGHT]
|
||||
## and waits until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_up_right() -> Callable:
|
||||
return func(): await face_up_right()
|
||||
|
||||
|
||||
## Makes the character to look up and right by setting [member _looking_dir] to [constant UP_RIGHT]
|
||||
## and waits until [method idle] finishes.
|
||||
func face_up_right() -> void:
|
||||
face_direction(position + Vector2.UP + Vector2.RIGHT)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look right by setting [member _looking_dir] to [constant RIGHT] and waits
|
||||
## until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_right() -> Callable:
|
||||
return func(): await face_right()
|
||||
|
||||
|
||||
## Makes the character to look right by setting [member _looking_dir] to [constant RIGHT] and waits
|
||||
## until [method idle] finishes.
|
||||
func face_right() -> void:
|
||||
face_direction(position + Vector2.RIGHT)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look down and right by setting [member _looking_dir] to
|
||||
## [constant DOWN_RIGHT] and waits until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_down_right() -> Callable:
|
||||
return func(): await face_down_right()
|
||||
|
||||
|
||||
## Makes the character to look down and right by setting [member _looking_dir] to
|
||||
## [constant DOWN_RIGHT] and waits until [method idle] finishes.
|
||||
func face_down_right() -> void:
|
||||
face_direction(position + Vector2.DOWN + Vector2.RIGHT)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look down by setting [member _looking_dir] to [constant DOWN] and waits
|
||||
## until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_down() -> Callable:
|
||||
return func(): await face_down()
|
||||
|
||||
|
||||
## Makes the character to look down by setting [member _looking_dir] to [constant DOWN] and waits
|
||||
## until [method idle] finishes.
|
||||
func face_down() -> void:
|
||||
face_direction(position + Vector2.DOWN)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look down and left by setting [member _looking_dir] to
|
||||
## [constant DOWN_LEFT] and waits until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_down_left() -> Callable:
|
||||
return func(): await face_down_left()
|
||||
|
||||
|
||||
## Makes the character to look down and left by setting [member _looking_dir] to
|
||||
## [constant DOWN_LEFT] and waits until [method idle] finishes.
|
||||
func face_down_left() -> void:
|
||||
face_direction(position + Vector2.DOWN + Vector2.LEFT)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look left by setting [member _looking_dir] to [constant LEFT] and waits
|
||||
## until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_left() -> Callable:
|
||||
return func(): await face_left()
|
||||
|
||||
|
||||
## Makes the character to look left by setting [member _looking_dir] to [constant LEFT] and waits
|
||||
## until [method idle] finishes.
|
||||
func face_left() -> void:
|
||||
face_direction(position + Vector2.LEFT)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character to look up and left by setting [member _looking_dir] to [constant UP_LEFT]
|
||||
## and waits until [method idle] finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_up_left() -> Callable:
|
||||
return func(): await face_up_left()
|
||||
|
||||
|
||||
## Makes the character to look up and left by setting [member _looking_dir] to [constant UP_LEFT]
|
||||
## and waits until [method idle] finishes.
|
||||
func face_up_left() -> void:
|
||||
face_direction(position + Vector2.UP + Vector2.LEFT)
|
||||
await idle()
|
||||
|
||||
|
||||
## Makes the character face in the direction of the last clicked [PopochiuClickable], which is
|
||||
## stored in [member Popochiu.clicked].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_face_clicked() -> Callable:
|
||||
return func(): await face_clicked()
|
||||
|
||||
|
||||
## Makes the character face in the direction of the last clicked [PopochiuClickable], which is
|
||||
## stored in [member Popochiu.clicked].
|
||||
func face_clicked() -> void:
|
||||
var global_lap = PopochiuUtils.e.clicked.to_global(PopochiuUtils.e.clicked.look_at_point)
|
||||
|
||||
_flip_left_right(
|
||||
global_lap.x < global_position.x,
|
||||
global_lap.x > global_position.x
|
||||
)
|
||||
|
||||
await face_direction(global_lap)
|
||||
|
||||
|
||||
## Calls [method _play_talk] and emits [signal character_spoke] sending itself as parameter, and the
|
||||
## [param dialog] line to show on screen. You can specify the emotion to use with [param emo]. If an
|
||||
## [AudioCue] is defined for the emotion, it is played. Once the talk animation finishes, the
|
||||
## characters return to its idle state.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_say(dialog: String, emo := EMPTY_STRING) -> Callable:
|
||||
return func(): await say(dialog, emo)
|
||||
|
||||
|
||||
## Calls [method _play_talk] and emits [signal character_spoke] sending itself as parameter, and the
|
||||
## [param dialog] line to show on screen. You can specify the emotion to use with [param emo]. If an
|
||||
## [AudioCue] is defined for the emotion, it is played. Once the talk animation finishes, the
|
||||
## characters return to its idle state.
|
||||
func say(dialog: String, emo := EMPTY_STRING) -> void:
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
if not emo.is_empty():
|
||||
emotion = emo
|
||||
|
||||
# Call the virtual that plays the talk animation
|
||||
_play_talk()
|
||||
|
||||
var vo_name := _get_vo_cue(emotion)
|
||||
if not vo_name.is_empty() and PopochiuUtils.a.get(vo_name):
|
||||
PopochiuUtils.a[vo_name].play(false, global_position)
|
||||
|
||||
PopochiuUtils.c.character_spoke.emit(self, dialog)
|
||||
|
||||
await PopochiuUtils.g.dialog_line_finished
|
||||
|
||||
# Stop the voice if it is still playing (feature #202)
|
||||
# Fix: Check if the vo_name is valid in order to stop it
|
||||
if not vo_name.is_empty() and PopochiuUtils.a[vo_name].is_playing():
|
||||
PopochiuUtils.a[vo_name].stop(0.3)
|
||||
|
||||
emotion = EMPTY_STRING
|
||||
idle()
|
||||
|
||||
|
||||
## Calls [method _play_grab] and waits until the [signal grab_done] is emitted, then goes back to
|
||||
## [method idle].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_grab() -> Callable:
|
||||
return func(): await grab()
|
||||
|
||||
|
||||
## Calls [method _play_grab] and waits until the [signal grab_done] is emitted, then goes back to
|
||||
## [method idle].
|
||||
func grab() -> void:
|
||||
if PopochiuUtils.e.cutscene_skipped:
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
# Call the virtual that plays the grab animation
|
||||
_play_grab()
|
||||
|
||||
await grab_done
|
||||
|
||||
idle()
|
||||
|
||||
|
||||
## Calls [method PopochiuClickable.hide_helpers].
|
||||
func hide_helpers() -> void:
|
||||
super()
|
||||
# TODO: add visibility logic for dialog_pos gizmo
|
||||
|
||||
|
||||
## Calls [method PopochiuClickable.show_helpers].
|
||||
func show_helpers() -> void:
|
||||
super()
|
||||
# TODO: add visibility logic for dialog_pos gizmo
|
||||
|
||||
|
||||
## Makes the character walk to [param pos].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to(pos: Vector2) -> Callable:
|
||||
return func(): await walk_to(pos)
|
||||
|
||||
|
||||
## Makes the character walk to [param pos].
|
||||
func walk_to(pos: Vector2) -> void:
|
||||
await walk(PopochiuUtils.r.current.to_global(pos))
|
||||
|
||||
|
||||
## Makes the character walk to the last clicked [PopochiuClickable], which is stored in
|
||||
## [member Popochiu.clicked]. You can set an [param offset] relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to_clicked(offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await walk_to_clicked(offset)
|
||||
|
||||
|
||||
## Makes the character walk (NON-BLOCKING) to the last clicked [PopochiuClickable], which is stored
|
||||
## in [member Popochiu.clicked]. You can set an [param offset] relative to the target position.
|
||||
func walk_to_clicked(offset := Vector2.ZERO) -> void:
|
||||
var clicked_id: String = PopochiuUtils.e.clicked.script_name
|
||||
|
||||
if PopochiuUtils.e.clicked == _last_reached_clickable:
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
await _walk_to_node(PopochiuUtils.e.clicked, offset)
|
||||
_last_reached_clickable = PopochiuUtils.e.clicked
|
||||
|
||||
# Check if the action was cancelled
|
||||
if not PopochiuUtils.e.clicked or clicked_id != PopochiuUtils.e.clicked.script_name:
|
||||
await PopochiuUtils.e.await_stopped
|
||||
|
||||
|
||||
## Makes the character walk (BLOCKING the GUI) to the last clicked [PopochiuClickable], which is
|
||||
## stored in [member Popochiu.clicked]. You can set an [param offset] relative to the target position.
|
||||
func walk_to_clicked_blocking(offset := Vector2.ZERO) -> void:
|
||||
PopochiuUtils.g.block()
|
||||
|
||||
await _walk_to_node(PopochiuUtils.e.clicked, offset)
|
||||
|
||||
PopochiuUtils.g.unblock()
|
||||
|
||||
|
||||
## Makes the character walk (BLOCKING the GUI) to the last clicked [PopochiuClickable], which is
|
||||
## stored in [member Popochiu.clicked]. You can set an [param offset] relative to the target position.
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to_clicked_blocking(offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await walk_to_clicked_blocking(offset)
|
||||
|
||||
|
||||
## Makes the character walk to the [PopochiuProp] (in the current room) which
|
||||
## [member PopochiuClickable.script_name] is equal to [param id]. You can set an [param offset]
|
||||
## relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to_prop(id: String, offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await walk_to_prop(id, offset)
|
||||
|
||||
|
||||
## Makes the character walk to the [PopochiuProp] (in the current room) which
|
||||
## [member PopochiuClickable.script_name] is equal to [param id]. You can set an [param offset]
|
||||
## relative to the target position.
|
||||
func walk_to_prop(id: String, offset := Vector2.ZERO) -> void:
|
||||
await _walk_to_node(PopochiuUtils.r.current.get_prop(id), offset)
|
||||
|
||||
|
||||
## Makes the character teleport (disappear at one location and instantly appear at another) to the
|
||||
## [PopochiuProp] (in the current room) which [member PopochiuClickable.script_name] is equal to
|
||||
## [param id]. You can set an [param offset] relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_teleport_to_prop(id: String, offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await teleport_to_prop(id, offset)
|
||||
|
||||
|
||||
## Makes the character teleport (disappear at one location and instantly appear at another) to the
|
||||
## [PopochiuProp] (in the current room) which [member PopochiuClickable.script_name] is equal to
|
||||
## [param id]. You can set an [param offset] relative to the target position.
|
||||
func teleport_to_prop(id: String, offset := Vector2.ZERO) -> void:
|
||||
await _teleport_to_node(PopochiuUtils.r.current.get_prop(id), offset)
|
||||
|
||||
|
||||
## Makes the character walk to the [PopochiuHotspot] (in the current room) which
|
||||
## [member PopochiuClickable.script_name] is equal to [param id]. You can set an [param offset]
|
||||
## relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to_hotspot(id: String, offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await walk_to_hotspot(id, offset)
|
||||
|
||||
|
||||
## Makes the character walk to the [PopochiuHotspot] (in the current room) which
|
||||
## [member PopochiuClickable.script_name] is equal to [param id]. You can set an [param offset]
|
||||
## relative to the target position.
|
||||
func walk_to_hotspot(id: String, offset := Vector2.ZERO) -> void:
|
||||
await _walk_to_node(PopochiuUtils.r.current.get_hotspot(id), offset)
|
||||
|
||||
|
||||
## Makes the character teleport (disappear at one location and instantly appear at another) to the
|
||||
## [PopochiuHotspot] (in the current room) which [member PopochiuClickable.script_name] is equal to
|
||||
## [param id]. You can set an [param offset] relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_teleport_to_hotspot(id: String, offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await teleport_to_hotspot(id, offset)
|
||||
|
||||
|
||||
## Makes the character teleport (disappear at one location and instantly appear at another) to the
|
||||
## [PopochiuHotspot] (in the current room) which [member PopochiuClickable.script_name] is equal to
|
||||
## [param id]. You can set an [param offset] relative to the target position.
|
||||
func teleport_to_hotspot(id: String, offset := Vector2.ZERO) -> void:
|
||||
await _teleport_to_node(PopochiuUtils.r.current.get_hotspot(id), offset)
|
||||
|
||||
|
||||
## Makes the character walk to the [Marker2D] (in the current room) which [member Node.name] is
|
||||
## equal to [param id]. You can set an [param offset] relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_walk_to_marker(id: String, offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await walk_to_marker(id, offset)
|
||||
|
||||
|
||||
## Makes the character walk to the [Marker2D] (in the current room) which [member Node.name] is
|
||||
## equal to [param id]. You can set an [param offset] relative to the target position.
|
||||
func walk_to_marker(id: String, offset := Vector2.ZERO) -> void:
|
||||
await _walk_to_node(PopochiuUtils.r.current.get_marker(id), offset)
|
||||
|
||||
|
||||
## Makes the character teleport (disappear at one location and instantly appear at another) to the
|
||||
## [Marker2D] (in the current room) which [member Node.name] is equal to [param id]. You can set an
|
||||
## [param offset] relative to the target position.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_teleport_to_marker(id: String, offset := Vector2.ZERO) -> Callable:
|
||||
return func(): await teleport_to_marker(id, offset)
|
||||
|
||||
|
||||
## Makes the character teleport (disappear at one location and instantly appear at another) to the
|
||||
## [Marker2D] (in the current room) which [member Node.name] is equal to [param id]. You can set an
|
||||
## [param offset] relative to the target position.
|
||||
func teleport_to_marker(id: String, offset := Vector2.ZERO) -> void:
|
||||
await _teleport_to_node(PopochiuUtils.r.current.get_marker(id), offset)
|
||||
|
||||
|
||||
## Sets [member emotion] to [param new_emotion] when in a [method Popochiu.queue].
|
||||
func queue_set_emotion(new_emotion: String) -> Callable:
|
||||
return func(): emotion = new_emotion
|
||||
|
||||
|
||||
## Sets [member ignore_walkable_areas] to [param new_value] when in a [method Popochiu.queue].
|
||||
func queue_ignore_walkable_areas(new_value: bool) -> Callable:
|
||||
return func(): ignore_walkable_areas = new_value
|
||||
|
||||
|
||||
## Plays the [param animation_label] animation. You can specify a fallback animation to play with
|
||||
## [param animation_fallback] in case the former one doesn't exists.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_play_animation(
|
||||
animation_label: String, animation_fallback := 'idle', blocking := false
|
||||
) -> Callable:
|
||||
return func(): await play_animation(animation_label, animation_fallback)
|
||||
|
||||
|
||||
## Plays the [param animation_label] animation. You can specify a fallback animation to play with
|
||||
## [param animation_fallback] in case the former one doesn't exists.
|
||||
func play_animation(animation_label: String, animation_fallback := 'idle'):
|
||||
if (animation_label != _last_requested_animation_label) or (_looking_dir != _last_requested_animation_dir):
|
||||
if not has_node("AnimationPlayer"):
|
||||
PopochiuUtils.print_error(
|
||||
"Can't play character animation. Required AnimationPlayer not found in character %s" %
|
||||
[script_name]
|
||||
)
|
||||
return
|
||||
|
||||
if animation_player.get_animation_list().is_empty():
|
||||
return
|
||||
|
||||
# Search for a valid animation corresponding to animation_label
|
||||
_current_animation = _get_valid_oriented_animation(animation_label)
|
||||
# If is not present, do the same for the the fallback animation.
|
||||
if _current_animation.is_empty():
|
||||
_current_animation = _get_valid_oriented_animation(animation_fallback)
|
||||
# In neither are available, exit and throw an error to check for the presence of the animations.
|
||||
if _current_animation.is_empty(): # Again!
|
||||
PopochiuUtils.print_error(
|
||||
"Neither the requested nor the fallback animation could be found for character %s.\
|
||||
Requested:%s - Fallback: %s" % [script_name, animation_label, animation_fallback]
|
||||
)
|
||||
return
|
||||
# Cache the the _current_animation context to avoid re-searching for it.
|
||||
_last_requested_animation_label = animation_label
|
||||
_last_requested_animation_dir = _looking_dir
|
||||
# Play the animation in the best available orientation
|
||||
animation_player.play(_current_animation)
|
||||
# If the playing is blocking, wait for the animation to finish
|
||||
await animation_player.animation_finished
|
||||
|
||||
# Go back to idle state
|
||||
_play_idle()
|
||||
|
||||
|
||||
## Makes the animation that is currently playing to stop. Works only if it is looping and is not an
|
||||
## idle animation. The animation stops when the current loop finishes.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_stop_animation():
|
||||
return func(): await stop_animation()
|
||||
|
||||
|
||||
## Makes the animation that is currently playing to stop. Works only if it is looping and is not an
|
||||
## idle animation. The animation stops when the current loop finishes.
|
||||
func stop_animation():
|
||||
# If the animation is not looping or is an idle one, do nothing
|
||||
if (
|
||||
animation_player.get_animation(
|
||||
animation_player.current_animation
|
||||
).loop_mode == Animation.LOOP_NONE
|
||||
or animation_player.current_animation == 'idle'
|
||||
or animation_player.current_animation.begins_with('idle_')
|
||||
):
|
||||
return
|
||||
|
||||
# Save the loop mode, wait for the anim to be over as designed, then restore the mode
|
||||
var animation: Animation = animation_player.get_animation(animation_player.current_animation)
|
||||
var animation_loop_mode := animation.loop_mode
|
||||
animation.loop_mode = Animation.LOOP_NONE
|
||||
await animation_player.animation_finished
|
||||
|
||||
_play_idle()
|
||||
animation.loop_mode = animation_loop_mode
|
||||
|
||||
|
||||
## Immediately stops the animation that is currently playing by changing to the idle animation.
|
||||
## [br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_halt_animation():
|
||||
return func(): halt_animation()
|
||||
|
||||
|
||||
## Immediately stops the animation that is currently playing by changing to the idle animation.
|
||||
func halt_animation():
|
||||
_play_idle()
|
||||
|
||||
|
||||
## Pauses the animation that is currently playing.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_pause_animation():
|
||||
return func(): pause_animation()
|
||||
|
||||
|
||||
## Pauses the animation that is currently playing.
|
||||
func pause_animation():
|
||||
animation_player.pause()
|
||||
|
||||
|
||||
## Resumes the current animation (that was previously paused).[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_resume_animation():
|
||||
return func(): resume_animation()
|
||||
|
||||
|
||||
## Resumes the current animation (that was previously paused).
|
||||
func resume_animation():
|
||||
animation_player.play()
|
||||
|
||||
|
||||
## Makes the character look in the direction of [param destination]. The result is one of the values
|
||||
## defined by [enum Looking].
|
||||
func face_direction(destination: Vector2):
|
||||
# Determine the direction the character is facing.
|
||||
# Remember: Y coordinates have opposite sign in Godot.
|
||||
# This means that negative angles are up movements.
|
||||
# Set the direction using the _looking property.
|
||||
# We cannot use the face_* functions because they
|
||||
# set the state as IDLE.
|
||||
|
||||
# Based on the character facing direction, define a set of
|
||||
# animation suffixes in reference order.
|
||||
# Notice how we seek for opposite directions for left and
|
||||
# right. Flipping is done in other functions. We just define
|
||||
# a preference order for animations when available.
|
||||
|
||||
# Get the vector from the origin to the destination.
|
||||
var angle = wrapf(rad_to_deg((destination - position).angle()) , 0, 360)
|
||||
# The angle calculation uses 16 angles rather than 8 for greater accuracy
|
||||
# in choosing the facing direction fallback animations.
|
||||
var _looking_angle := int(angle / 22.5) % 16
|
||||
# Selecting the animation suffixes for the current facing direction.
|
||||
# Note that we add a fallback empty string to the list, in case the only
|
||||
# available animation is the base one ('walk', 'talk', etc).
|
||||
_animation_suffixes = _valid_animation_suffixes[_looking_angle] + [EMPTY_STRING]
|
||||
# The 16 directions used for animation suffixes are simplified to 8 general directions
|
||||
_looking_dir = int(angle / 45) % 8
|
||||
|
||||
|
||||
## Returns the [Texture] of the avatar defined for the [param emo] emotion.
|
||||
## Returns [code]null[/code] if no avatar is found. If there is an avatar defined for the
|
||||
## [code]""[/code] emotion, that one is returned by default.
|
||||
func get_avatar_for_emotion(emo := EMPTY_STRING) -> Texture:
|
||||
var texture: Texture = null
|
||||
|
||||
while not texture and not avatars.is_empty():
|
||||
for dic in avatars:
|
||||
if dic.emotion.is_empty():
|
||||
texture = dic.avatar
|
||||
elif dic.emotion == emo:
|
||||
texture = dic.avatar
|
||||
break
|
||||
|
||||
return texture
|
||||
|
||||
|
||||
## Returns the [code]y[/code] value of the dialog_pos [Vector2] that defines the
|
||||
## position of the dialog lines said by the character when it talks.
|
||||
func get_dialog_pos() -> float:
|
||||
return dialog_pos.y
|
||||
|
||||
|
||||
func update_position() -> void:
|
||||
position = (
|
||||
position_stored
|
||||
if position_stored
|
||||
else position
|
||||
)
|
||||
|
||||
|
||||
## Updates the scale depending on the properties of the scaling region where it is located.
|
||||
func update_scale():
|
||||
if on_scaling_region:
|
||||
var polygon_range = (
|
||||
on_scaling_region["polygon_bottom_y"] - on_scaling_region["polygon_top_y"]
|
||||
)
|
||||
var scale_range = (
|
||||
on_scaling_region["scale_bottom"] - on_scaling_region["scale_top"]
|
||||
)
|
||||
|
||||
var position_from_the_top_of_region = (
|
||||
position.y - on_scaling_region["polygon_top_y"]
|
||||
)
|
||||
|
||||
var scale_for_position = (
|
||||
on_scaling_region["scale_top"] + (
|
||||
scale_range / polygon_range * position_from_the_top_of_region
|
||||
)
|
||||
)
|
||||
scale.x = [
|
||||
[scale_for_position, on_scaling_region["scale_min"]].max(),
|
||||
on_scaling_region["scale_max"]
|
||||
].min()
|
||||
scale.y = [
|
||||
[scale_for_position, on_scaling_region["scale_min"]].max(),
|
||||
on_scaling_region["scale_max"]
|
||||
].min()
|
||||
walk_speed = default_walk_speed / default_scale.x * scale_for_position
|
||||
else:
|
||||
scale = default_scale
|
||||
walk_speed = default_walk_speed
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_voices(value: Array) -> void:
|
||||
voices = value
|
||||
|
||||
for idx in value.size():
|
||||
if not value[idx]:
|
||||
var arr: Array[AudioCueSound] = []
|
||||
|
||||
voices[idx] = {
|
||||
emotion = EMPTY_STRING,
|
||||
variations = arr
|
||||
}
|
||||
elif not value[idx].variations.is_empty():
|
||||
if value[idx].variations[-1] == null:
|
||||
value[idx].variations[-1] = AudioCueSound.new()
|
||||
|
||||
|
||||
func set_follow_player(value: bool) -> void:
|
||||
follow_player = value
|
||||
|
||||
if not Engine.is_editor_hint():
|
||||
set_process(follow_player)
|
||||
|
||||
|
||||
func set_avatars(value: Array) -> void:
|
||||
avatars = value
|
||||
|
||||
for idx in value.size():
|
||||
if not value[idx]:
|
||||
avatars[idx] = {
|
||||
emotion = EMPTY_STRING,
|
||||
avatar = Texture.new(),
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _translate() -> void:
|
||||
if Engine.is_editor_hint() or not is_inside_tree(): return
|
||||
description = PopochiuUtils.e.get_text(_description_code)
|
||||
|
||||
|
||||
func _get_vo_cue(emotion := EMPTY_STRING) -> String:
|
||||
for v in voices:
|
||||
if v.emotion.to_lower() == emotion.to_lower():
|
||||
var cue_name := EMPTY_STRING
|
||||
|
||||
if not v.variations.is_empty():
|
||||
if not v.has('not_played') or v.not_played.is_empty():
|
||||
v['not_played'] = range(v.variations.size())
|
||||
|
||||
var idx: int = (v['not_played'] as Array).pop_at(
|
||||
PopochiuUtils.get_random_array_idx(v['not_played'])
|
||||
)
|
||||
|
||||
cue_name = v.variations[idx].resource_name
|
||||
|
||||
return cue_name
|
||||
return EMPTY_STRING
|
||||
|
||||
|
||||
func _get_valid_oriented_animation(animation_label):
|
||||
# The list of prefixes is in order of preference
|
||||
# Eg. walk_dl, walk_l, walk
|
||||
# Scan the AnimationPlayer and return the first that matches.
|
||||
for suffix in _animation_suffixes:
|
||||
var animation = "%s%s" % [animation_label, suffix]
|
||||
if animation_player.has_animation(animation):
|
||||
return animation
|
||||
|
||||
return EMPTY_STRING
|
||||
|
||||
|
||||
func _walk_to_node(node: Node2D, offset: Vector2) -> void:
|
||||
if not is_instance_valid(node):
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
await walk(
|
||||
node.to_global(node.walk_to_point if node is PopochiuClickable else Vector2.ZERO) + offset
|
||||
)
|
||||
|
||||
|
||||
# Instantly move to the node position
|
||||
func _teleport_to_node(node: Node2D, offset: Vector2) -> void:
|
||||
if not is_instance_valid(node):
|
||||
await get_tree().process_frame
|
||||
return
|
||||
|
||||
position = node.to_global(
|
||||
node.walk_to_point if node is PopochiuClickable else Vector2.ZERO
|
||||
) + offset
|
||||
|
||||
|
||||
func _update_position():
|
||||
PopochiuUtils.r.current.update_characters_position(self)
|
||||
|
||||
# Flips sprites depending on user preferences: requires two boolean conditions
|
||||
# as arguments for flipping left [param left_cond] or right [param right_cond]
|
||||
func _flip_left_right(left_cond: bool, right_cond: bool) -> void:
|
||||
if has_node('Sprite2D'):
|
||||
$Sprite2D.flip_h = false
|
||||
match flips_when:
|
||||
FlipsWhen.LOOKING_LEFT:
|
||||
$Sprite2D.flip_h = left_cond
|
||||
FlipsWhen.LOOKING_RIGHT:
|
||||
$Sprite2D.flip_h = right_cond
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://vq4ev5brs6us
|
|
@ -0,0 +1,18 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cwtpncpx24dm7"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/character/popochiu_character.gd" id="1_2xmr4"]
|
||||
|
||||
[node name="Character" type="Area2D"]
|
||||
script = ExtResource("1_2xmr4")
|
||||
popochiu_placeholder = null
|
||||
interaction_polygon = PackedVector2Array(-10, -10, 10, -10, 10, 10, -10, 10)
|
||||
|
||||
[node name="InteractionPolygon" type="CollisionPolygon2D" parent="."]
|
||||
polygon = PackedVector2Array(-10, -10, 10, -10, 10, 10, -10, 10)
|
||||
|
||||
[node name="ScalingPolygon" type="CollisionPolygon2D" parent="."]
|
||||
polygon = PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
|
@ -0,0 +1,42 @@
|
|||
@icon('res://addons/popochiu/icons/character.png')
|
||||
class_name PopochiuCharacterData
|
||||
extends Resource
|
||||
## This class is used to store information when saving and loading the game. It also ensures that
|
||||
## the data remains throughout the game's execution.
|
||||
|
||||
## The identifier of the object used in scripts.
|
||||
@export var script_name := ''
|
||||
## The path to the scene file to be used when adding the character to the game during runtime.
|
||||
@export_file("*.tscn") var scene := ''
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the game is saved.
|
||||
## [i]Virtual[/i].
|
||||
func _on_save() -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
## Called when the game is loaded. The structure of [param data] is the same returned by
|
||||
## [method _on_save].
|
||||
## [i]Virtual[/i].
|
||||
func _on_load(_data: Dictionary) -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Use this to store custom data when saving the game. The returned [Dictionary] must contain only
|
||||
## JSON supported types: [bool], [int], [float], [String].
|
||||
func on_save() -> Dictionary:
|
||||
return _on_save()
|
||||
|
||||
|
||||
## Called when the game is loaded. [param data] will have the same structure you defined for the
|
||||
## returned [Dictionary] by [method _on_save].
|
||||
func on_load(data: Dictionary) -> void:
|
||||
_on_load(data)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://id2ek58lklcx
|
419
addons/popochiu/engine/objects/clickable/popochiu_clickable.gd
Normal file
419
addons/popochiu/engine/objects/clickable/popochiu_clickable.gd
Normal file
|
@ -0,0 +1,419 @@
|
|||
@tool
|
||||
class_name PopochiuClickable
|
||||
extends Area2D
|
||||
## Handles an Area2D that reacts to mouse events.
|
||||
##
|
||||
## Is the base class for [PopochiuProp], [PopochiuHotspot] and [PopochiuCharacter].
|
||||
## It has a property to determine when the object should render in front or back to other, another
|
||||
## property that can be used to define the position to which characters will move to when moving
|
||||
## to the item, and tow [CollisionPolygon2D] which are used to handle players interaction and
|
||||
## handle scaling.
|
||||
|
||||
## Used to allow devs to define the cursor type for the clickable.
|
||||
const CURSOR := preload("res://addons/popochiu/engine/cursor/cursor.gd")
|
||||
|
||||
## The identifier of the object used in scripts.
|
||||
@export var script_name := ""
|
||||
## The text shown to players when the cursor hovers the object.
|
||||
@export var description := ""
|
||||
## Whether the object will listen to interactions.
|
||||
@export var clickable := true: set = set_clickable
|
||||
## The [code]y[/code] position of the baseline relative to the center of the object.
|
||||
@export var baseline := 0
|
||||
## The [Vector2] position where characters will move when approaching the object.
|
||||
@export var walk_to_point := Vector2.ZERO
|
||||
## The [Vector2] position where characters will turn looking at the object.
|
||||
@export var look_at_point := Vector2.ZERO
|
||||
## The cursor to use when the mouse hovers the object.
|
||||
@export var cursor: CURSOR.Type = CURSOR.Type.NONE
|
||||
## Whether the object will be rendered always above other objects in the room.
|
||||
@export var always_on_top := false
|
||||
## Stores the vertices to assign to the [b]InteractionPolygon[/b] child during runtime. This is used
|
||||
## by [PopochiuRoom] to store the info in its [code].tscn[/code].
|
||||
@export var interaction_polygon := PackedVector2Array()
|
||||
## Stores the position to assign to the [b]InteractionPolygon[/b] child during runtime. This is used
|
||||
## by [PopochiuRoom] to store the info in its [code].tscn[/code].
|
||||
@export var interaction_polygon_position := Vector2.ZERO
|
||||
|
||||
## The [PopochiuRoom] to which the object belongs.
|
||||
var room: Node2D = null: set = set_room
|
||||
## The number of times this object has been left-clicked.
|
||||
var times_clicked := 0
|
||||
## The number of times this object has been double-clicked.
|
||||
var times_double_clicked := 0
|
||||
## The number of times this object has been right-clicked.
|
||||
var times_right_clicked := 0
|
||||
## The number of times this object has been middle-clicked.
|
||||
var times_middle_clicked := 0
|
||||
# NOTE: Don't know if this will make sense, or if this object should emit a signal about the click
|
||||
# (command execution).
|
||||
## Stores the last [enum MouseButton] pressed on this object.
|
||||
var last_click_button := -1
|
||||
|
||||
# Used for setting the double click delay. Windows default is 500 milliseconds.
|
||||
var _double_click_delay: float = 0.2
|
||||
# Used for tracking if a double click has occurred.
|
||||
var _has_double_click: bool = false
|
||||
|
||||
@onready var _description_code := description
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready():
|
||||
add_to_group("PopochiuClickable")
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
hide_helpers()
|
||||
|
||||
# Add interaction polygon to the proper group
|
||||
if (get_node_or_null("InteractionPolygon") != null):
|
||||
get_node("InteractionPolygon").add_to_group(
|
||||
PopochiuEditorHelper.POPOCHIU_OBJECT_POLYGON_GROUP
|
||||
)
|
||||
|
||||
# Ignore assigning the polygon when:
|
||||
if (
|
||||
get_node_or_null("InteractionPolygon") == null # there is no InteractionPolygon node
|
||||
or not get_parent() is Node2D # editing it in the .tscn file of the object directly
|
||||
or self is PopochiuCharacter # avoid resetting the polygon for characters (see issue #158))
|
||||
):
|
||||
return
|
||||
|
||||
if interaction_polygon.is_empty():
|
||||
interaction_polygon = get_node("InteractionPolygon").polygon
|
||||
interaction_polygon_position = get_node("InteractionPolygon").position
|
||||
else:
|
||||
get_node("InteractionPolygon").polygon = interaction_polygon
|
||||
get_node("InteractionPolygon").position = interaction_polygon_position
|
||||
|
||||
# If we are in the editor, we're done
|
||||
return
|
||||
|
||||
# When the game is running...
|
||||
# Update the node's polygon when:
|
||||
if (
|
||||
get_node_or_null("InteractionPolygon") # there is an InteractionPolygon node
|
||||
and not self is PopochiuCharacter # avoids resetting the polygon (see issue #158)
|
||||
):
|
||||
get_node("InteractionPolygon").polygon = interaction_polygon
|
||||
get_node("InteractionPolygon").position = interaction_polygon_position
|
||||
|
||||
visibility_changed.connect(_toggle_input)
|
||||
|
||||
# Ignore this object if it is a temporary one (its name has *)
|
||||
if clickable and not "*" in name:
|
||||
# Connect to own signals
|
||||
mouse_entered.connect(_on_mouse_entered)
|
||||
mouse_exited.connect(_on_mouse_exited)
|
||||
# Fix #183 by listening only to inputs in this CollisionObject2D
|
||||
input_event.connect(_on_input_event)
|
||||
|
||||
# Connect to singleton signals
|
||||
PopochiuUtils.e.language_changed.connect(_translate)
|
||||
|
||||
_translate()
|
||||
|
||||
|
||||
func _notification(event: int) -> void:
|
||||
if event == NOTIFICATION_EDITOR_PRE_SAVE:
|
||||
interaction_polygon = get_node("InteractionPolygon").polygon
|
||||
interaction_polygon_position = get_node("InteractionPolygon").position
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the room this node belongs to has been added to the tree.
|
||||
## [i]Virtual[/i].
|
||||
func _on_room_set() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the node is clicked.
|
||||
## [i]Virtual[/i].
|
||||
func _on_click() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the node is double clicked.
|
||||
## [i]Virtual[/i].
|
||||
func _on_double_click() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the node is right clicked.
|
||||
## [i]Virtual[/i].
|
||||
func _on_right_click() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the node is middle clicked.
|
||||
## [i]Virtual[/i].
|
||||
func _on_middle_click() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the node is clicked and there is an inventory item selected.
|
||||
## [i]Virtual[/i].
|
||||
func _on_item_used(item: PopochiuInventoryItem) -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Used by the plugin to hide the visual helpers that show the interaction polygon
|
||||
## in the 2D Canvas Editor when this node is unselected in the Scene panel.
|
||||
func hide_helpers() -> void:
|
||||
if get_node_or_null("InteractionPolygon"):
|
||||
$InteractionPolygon.hide()
|
||||
|
||||
|
||||
## Used by the plugin to make visible the visual helpers that show the interaction polygon
|
||||
## in the 2D Canvas Editor when this node is unselected in the Scene panel.
|
||||
func show_helpers() -> void:
|
||||
if get_node_or_null("InteractionPolygon"):
|
||||
$InteractionPolygon.show()
|
||||
|
||||
|
||||
## Hides this Node.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_disable() -> Callable:
|
||||
return func(): await disable()
|
||||
|
||||
|
||||
## Hides this Node.
|
||||
func disable() -> void:
|
||||
self.visible = false
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
## Shows this Node.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_enable() -> Callable:
|
||||
return func(): await enable()
|
||||
|
||||
|
||||
## Shows this Node.
|
||||
func enable() -> void:
|
||||
self.visible = true
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
## Returns the [member description] of the node using [method Object.tr] if
|
||||
## [member PopochiuSettings.use_translations] is [code]true[/code]. Otherwise, it returns just the
|
||||
## value of [member description].
|
||||
func get_description() -> String:
|
||||
if Engine.is_editor_hint():
|
||||
if description.is_empty():
|
||||
description = name
|
||||
return description
|
||||
return PopochiuUtils.e.get_text(description)
|
||||
|
||||
|
||||
## Called when the object is left clicked.
|
||||
func on_click() -> void:
|
||||
await _on_click()
|
||||
|
||||
|
||||
## Called when the object is double clicked.
|
||||
func on_double_click() -> void:
|
||||
_reset_double_click()
|
||||
await _on_double_click()
|
||||
|
||||
|
||||
## Called when the object is right clicked.
|
||||
func on_right_click() -> void:
|
||||
await _on_right_click()
|
||||
|
||||
|
||||
## Called when the object is middle clicked.
|
||||
func on_middle_click() -> void:
|
||||
await _on_middle_click()
|
||||
|
||||
|
||||
## Called when an [param item] is used on this object.
|
||||
func on_item_used(item: PopochiuInventoryItem) -> void:
|
||||
await _on_item_used(item)
|
||||
# after item has been used return to normal state
|
||||
PopochiuUtils.i.active = null
|
||||
|
||||
|
||||
## Triggers the proper GUI command for the clicked mouse button identified with [param button_idx],
|
||||
## which can be [enum MouseButton].MOUSE_BUTTON_LEFT, [enum MouseButton].MOUSE_BUTTON_RIGHT or
|
||||
## [enum MouseButton].MOUSE_BUTTON_MIDDLE.
|
||||
func handle_command(button_idx: int) -> void:
|
||||
var command: String = PopochiuUtils.e.get_current_command_name().to_snake_case()
|
||||
var prefix := "on_%s"
|
||||
var suffix := "click"
|
||||
|
||||
match button_idx:
|
||||
MOUSE_BUTTON_RIGHT:
|
||||
suffix = "right_" + suffix
|
||||
MOUSE_BUTTON_MIDDLE:
|
||||
suffix = "middle_" + suffix
|
||||
|
||||
if not command.is_empty():
|
||||
var command_method := suffix.replace("click", command)
|
||||
|
||||
if has_method(prefix % command_method):
|
||||
suffix = command_method
|
||||
|
||||
PopochiuUtils.e.add_history({
|
||||
action = suffix if command.is_empty() else command,
|
||||
target = description
|
||||
})
|
||||
|
||||
await call(prefix % suffix)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_clickable(value: bool) -> void:
|
||||
clickable = value
|
||||
input_pickable = clickable
|
||||
|
||||
|
||||
func set_room(value: Node2D) -> void:
|
||||
room = value
|
||||
|
||||
_on_room_set()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _on_mouse_entered() -> void:
|
||||
if PopochiuUtils.e.hovered and is_instance_valid(PopochiuUtils.e.hovered) and (
|
||||
PopochiuUtils.e.hovered.get_parent() == self
|
||||
or get_index() < PopochiuUtils.e.hovered.get_index()
|
||||
):
|
||||
PopochiuUtils.e.add_hovered(self, true)
|
||||
return
|
||||
|
||||
PopochiuUtils.e.add_hovered(self)
|
||||
|
||||
PopochiuUtils.g.mouse_entered_clickable.emit(self)
|
||||
|
||||
|
||||
func _on_mouse_exited() -> void:
|
||||
last_click_button = -1
|
||||
|
||||
if PopochiuUtils.e.remove_hovered(self):
|
||||
PopochiuUtils.g.mouse_exited_clickable.emit(self)
|
||||
|
||||
|
||||
func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int):
|
||||
if PopochiuUtils.g.is_blocked or not PopochiuUtils.e.hovered or PopochiuUtils.e.hovered != self:
|
||||
return
|
||||
|
||||
if _is_double_click_or_tap(event):
|
||||
times_double_clicked += 1
|
||||
PopochiuUtils.e.clicked = self
|
||||
on_double_click()
|
||||
|
||||
return
|
||||
|
||||
if not await _is_click_or_touch_pressed(event): return
|
||||
|
||||
var event_index := PopochiuUtils.get_click_or_touch_index(event)
|
||||
PopochiuUtils.e.clicked = self
|
||||
last_click_button = event_index
|
||||
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
match event_index:
|
||||
MOUSE_BUTTON_LEFT:
|
||||
if PopochiuUtils.i.active:
|
||||
await on_item_used(PopochiuUtils.i.active)
|
||||
else:
|
||||
await handle_command(event_index)
|
||||
times_clicked += 1
|
||||
MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE:
|
||||
if PopochiuUtils.i.active: return
|
||||
|
||||
await handle_command(event_index)
|
||||
|
||||
if event_index == MOUSE_BUTTON_RIGHT:
|
||||
times_right_clicked += 1
|
||||
elif event_index == MOUSE_BUTTON_MIDDLE:
|
||||
times_middle_clicked += 1
|
||||
|
||||
PopochiuUtils.e.clicked = null
|
||||
|
||||
|
||||
func _toggle_input() -> void:
|
||||
if clickable:
|
||||
input_pickable = visible
|
||||
|
||||
|
||||
func _translate() -> void:
|
||||
if (
|
||||
Engine.is_editor_hint()
|
||||
or not is_inside_tree()
|
||||
or not PopochiuUtils.e.settings.use_translations
|
||||
):
|
||||
return
|
||||
|
||||
description = PopochiuUtils.e.get_text("%s-%s" % [get_tree().current_scene.name, _description_code])
|
||||
|
||||
|
||||
# ---- @anthonyirwin82 -----------------------------------------------------------------------------
|
||||
# NOTE: Temporarily duplicating PopochiuUtils functions here with an added delay for double click.
|
||||
# Having delay in the PopochiuUtils class that other gui code calls introduced unwanted issues.
|
||||
# This is a temporary work around until a more permanent solution is found.
|
||||
|
||||
# Checks if [param event] is an [InputEventMouseButton] or [InputEventScreenTouch] event.
|
||||
func _is_click_or_touch(event: InputEvent) -> bool:
|
||||
if (
|
||||
(event is InputEventMouseButton and not event.double_click)
|
||||
or (event is InputEventScreenTouch and not event.double_tap)
|
||||
):
|
||||
# This delay is need to prevent a single click being detected before double click
|
||||
await PopochiuUtils.e.wait(_double_click_delay)
|
||||
|
||||
if not _has_double_click:
|
||||
return (event is InputEventMouseButton or event is InputEventScreenTouch)
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# Checks if [param event] is an [InputEventMouseButton] or [InputEventScreenTouch] event and if it
|
||||
# is pressed.
|
||||
func _is_click_or_touch_pressed(event: InputEvent) -> bool:
|
||||
# Fix #183 by including `event is InputEventScreenTouch` validation
|
||||
if not _has_double_click:
|
||||
return await _is_click_or_touch(event) and event.pressed
|
||||
else:
|
||||
return false
|
||||
|
||||
|
||||
# Checks if [param event] is a double click or double tap event.
|
||||
func _is_double_click_or_tap(event: InputEvent) -> bool:
|
||||
if (
|
||||
(event is InputEventMouseButton and event.double_click)
|
||||
or (event is InputEventScreenTouch and event.double_tap)
|
||||
):
|
||||
_has_double_click = true
|
||||
|
||||
if event is InputEventMouseButton:
|
||||
return event.double_click
|
||||
elif event is InputEventScreenTouch:
|
||||
return event.double_tap
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# Resets the double click status to false by default
|
||||
func _reset_double_click(double_click: bool = false) -> void:
|
||||
# this delay is needed to prevent single click being detected after double click event
|
||||
await PopochiuUtils.e.wait(_double_click_delay)
|
||||
_has_double_click = double_click
|
||||
# ----------------------------------------------------------------------------- @anthonyirwin82 ----
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://df27veamriamt
|
165
addons/popochiu/engine/objects/dialog/popochiu_dialog.gd
Normal file
165
addons/popochiu/engine/objects/dialog/popochiu_dialog.gd
Normal file
|
@ -0,0 +1,165 @@
|
|||
@tool
|
||||
@icon('res://addons/popochiu/icons/dialog.png')
|
||||
class_name PopochiuDialog
|
||||
extends Resource
|
||||
## A class for branching dialogs. The dialog options can be used to trigger events.
|
||||
|
||||
## The identifier of the object used in scripts.
|
||||
@export var script_name := ''
|
||||
## The array of [PopochiuDialogOption] to show on screen when the dialog is running.
|
||||
@export var options: Array[PopochiuDialogOption] = [] : set = set_options
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the dialog starts. [b]You have to use an [code]await[/code] in this method in order
|
||||
## to make the dialog to work properly[/b].
|
||||
## [i]Virtual[/i].
|
||||
func _on_start() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the [param opt] dialog option is clicked. The [member PopochiuDialogOption.id] in
|
||||
## [param opt] can be used to check which was the selected option.
|
||||
## [i]Virtual[/i].
|
||||
func _option_selected(opt: PopochiuDialogOption) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the game is saved.
|
||||
## [i]Virtual[/i].
|
||||
func _on_save() -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
## Called when the game is loaded. The structure of [param data] is the same returned by
|
||||
## [method _on_save].
|
||||
## [i]Virtual[/i].
|
||||
func _on_load(_data: Dictionary) -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Starts this dialog, then [method _on_start] is called.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_start() -> Callable:
|
||||
return func (): await start()
|
||||
|
||||
|
||||
## Starts this dialog, then [method _on_start] is called.
|
||||
func start() -> void:
|
||||
if PopochiuUtils.d.current_dialog == self:
|
||||
return
|
||||
|
||||
# Start this dialog
|
||||
PopochiuUtils.d.current_dialog = self
|
||||
await _start()
|
||||
|
||||
|
||||
## Stops the dialog (which makes the menu with the options to disappear).[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_stop() -> Callable:
|
||||
return func (): await stop()
|
||||
|
||||
|
||||
## Stops the dialog (which makes the menu with the options to disappear).
|
||||
func stop() -> void:
|
||||
PopochiuUtils.d.finish_dialog()
|
||||
|
||||
|
||||
## Enables each [PopochiuDialogOption] which [member PopochiuDialogOption.id] matches each of the
|
||||
## values in the [param ids] array.
|
||||
func turn_on_options(ids: Array) -> void:
|
||||
for id in ids:
|
||||
var opt: PopochiuDialogOption = get_option(id)
|
||||
if opt: opt.turn_on()
|
||||
|
||||
|
||||
## Disables each [PopochiuDialogOption] which [member PopochiuDialogOption.id] matches each of the
|
||||
## values in the [param ids] array.
|
||||
func turn_off_options(ids: Array) -> void:
|
||||
for id in ids:
|
||||
var opt: PopochiuDialogOption = get_option(id)
|
||||
if opt: opt.turn_off()
|
||||
|
||||
|
||||
## Disables [b]forever[/b] each [PopochiuDialogOption] which [member PopochiuDialogOption.id]
|
||||
## matches each of the values in the [param ids] array.
|
||||
func turn_off_forever_options(ids: Array) -> void:
|
||||
for id in ids:
|
||||
var opt: PopochiuDialogOption = get_option(id)
|
||||
if opt: opt.turn_off_forever()
|
||||
|
||||
|
||||
## Use this to save custom data when saving the game. The returned [Dictionary] must contain only
|
||||
## JSON supported types: [bool], [int], [float], [String].
|
||||
func on_save() -> Dictionary:
|
||||
return _on_save()
|
||||
|
||||
|
||||
## Called when the game is loaded. [param data] will have the same structure you defined for the
|
||||
## returned [Dictionary] by [method _on_save].
|
||||
func on_load(data: Dictionary) -> void:
|
||||
_on_load(data)
|
||||
|
||||
|
||||
## Returns the dilog option which [member PopochiuDialogOption.id] matches [param opt_id].
|
||||
func get_option(opt_id: String) -> PopochiuDialogOption:
|
||||
for o in options:
|
||||
if (o as PopochiuDialogOption).id == opt_id:
|
||||
return o
|
||||
return null
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_options(value: Array[PopochiuDialogOption]) -> void:
|
||||
options = value
|
||||
|
||||
for idx in value.size():
|
||||
if not value[idx]:
|
||||
var new_opt: PopochiuDialogOption = PopochiuDialogOption.new()
|
||||
var id := 'Opt%d' % options.size()
|
||||
|
||||
new_opt.id = id
|
||||
new_opt.text = 'Option %d' % options.size()
|
||||
options[idx] = new_opt
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _start() -> void:
|
||||
PopochiuUtils.g.block()
|
||||
PopochiuUtils.d.dialog_started.emit(self)
|
||||
|
||||
await _on_start()
|
||||
|
||||
_show_options()
|
||||
|
||||
await PopochiuUtils.d.dialog_finished
|
||||
|
||||
PopochiuUtils.g.unblock()
|
||||
PopochiuUtils.d.option_selected.disconnect(_on_option_selected)
|
||||
|
||||
|
||||
func _show_options() -> void:
|
||||
if not PopochiuUtils.d.active: return
|
||||
|
||||
PopochiuUtils.d.dialog_options_requested.emit(options)
|
||||
|
||||
if not PopochiuUtils.d.option_selected.is_connected(_on_option_selected):
|
||||
PopochiuUtils.d.option_selected.connect(_on_option_selected)
|
||||
|
||||
|
||||
func _on_option_selected(opt: PopochiuDialogOption) -> void:
|
||||
opt.used = true
|
||||
opt.used_times += 1
|
||||
PopochiuUtils.d.selected_option = opt
|
||||
|
||||
_option_selected(opt)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://osh8vbx7f1ah
|
|
@ -0,0 +1,67 @@
|
|||
@tool
|
||||
class_name PopochiuDialogOption
|
||||
extends Resource
|
||||
## Each of the options in a [PopochiuDialog].
|
||||
|
||||
## The identifier of the option. Use it when scripting.
|
||||
@export var id := "" : set = set_id
|
||||
## The text to show on screen for the option.
|
||||
@export var text := ""
|
||||
## The icon to show on screen for the option.
|
||||
#@export var icon: Texture = null
|
||||
## Whether this option is visible.
|
||||
@export var visible := true
|
||||
## Whether this option is disabled. If [code]true[/code], the option won´t be rendered.
|
||||
@export var disabled := false
|
||||
## Whether this option should be [b]always[/b] rendered as not previously selected.
|
||||
@export var always_on := false
|
||||
|
||||
## Stores the same value of the [member id] property.
|
||||
var script_name := ""
|
||||
## Whether the option was already been selected. If [code]true[/code], then the option's
|
||||
## [member text] will be shown different in the options menu, so players know they already clicked
|
||||
## the option.
|
||||
var used := false
|
||||
## The number of times this options has been clicked.
|
||||
var used_times := 0
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the option is selected.
|
||||
## [i]Virtual[/i].
|
||||
func _on_selected() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Makes the option visible. Won´t work if the option is [member disabled].
|
||||
func turn_on() -> void:
|
||||
if disabled: return
|
||||
|
||||
visible = true
|
||||
used = false
|
||||
|
||||
|
||||
## Makes the option invisible.
|
||||
func turn_off() -> void:
|
||||
visible = false
|
||||
|
||||
|
||||
## Disables the option by making [member disable] [code]true[/code].
|
||||
func turn_off_forever() -> void:
|
||||
disabled = true
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_id(value: String) -> void:
|
||||
id = value
|
||||
|
||||
script_name = id
|
||||
resource_name = id
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cclk5bj1l4p4p
|
|
@ -0,0 +1,144 @@
|
|||
class_name PopochiuDialogMenu
|
||||
extends Control
|
||||
@warning_ignore("return_value_discarded")
|
||||
@warning_ignore("unused_signal")
|
||||
|
||||
signal shown
|
||||
|
||||
@export var option_scene: PackedScene
|
||||
## Max height of the menu in pixels. If visible options make the menu to exceed this value, it will
|
||||
## enable a vertical scroll bar.
|
||||
@export var max_height := 49
|
||||
@export_category("Option buttons")
|
||||
@export var normal_font_color: Color = Color("706deb")
|
||||
@export var normal_used_font_color: Color = Color("2e2c9b")
|
||||
@export var hover_font_color: Color = Color("ffffff")
|
||||
@export var hover_used_font_color: Color = Color("b2b2b2")
|
||||
@export var pressed_font_color: Color = Color("a9ff9f")
|
||||
@export var pressed_used_font_color: Color = Color("56ac4d")
|
||||
|
||||
var current_options := []
|
||||
|
||||
@onready var panel_container: PanelContainer = $PanelContainer
|
||||
@onready var dialog_options_container: VBoxContainer = %DialogOptionsContainer
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
for child in dialog_options_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
panel_container.custom_minimum_size = Vector2.ZERO
|
||||
|
||||
# Connect to own signals
|
||||
gui_input.connect(_clicked)
|
||||
|
||||
# Connect to autoloads signals
|
||||
PopochiuUtils.d.dialog_options_requested.connect(_create_options.bind(true))
|
||||
PopochiuUtils.d.inline_dialog_requested.connect(_create_inline_options)
|
||||
PopochiuUtils.d.dialog_finished.connect(remove_options)
|
||||
|
||||
hide()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _clicked(event: InputEvent) -> void:
|
||||
if PopochiuUtils.get_click_or_touch_index(event) == MOUSE_BUTTON_LEFT:
|
||||
accept_event()
|
||||
|
||||
|
||||
# Creates an Array of PopochiuDialogOption to show dialog tree options created
|
||||
# during execution, (those that are created after calling D.show_inline_dialog)
|
||||
func _create_inline_options(opts: Array) -> void:
|
||||
var tmp_opts := []
|
||||
for idx in opts.size():
|
||||
var new_opt: PopochiuDialogOption = PopochiuDialogOption.new()
|
||||
|
||||
new_opt.id = str(idx)
|
||||
new_opt.text = opts[idx]
|
||||
|
||||
tmp_opts.append(new_opt)
|
||||
|
||||
_create_options(tmp_opts, true)
|
||||
|
||||
|
||||
func _create_options(options := [], autoshow := false) -> void:
|
||||
remove_options()
|
||||
|
||||
if options.is_empty():
|
||||
if not current_options.is_empty():
|
||||
show_options()
|
||||
return
|
||||
|
||||
current_options = options.duplicate(true)
|
||||
|
||||
for dialog_option: PopochiuDialogOption in options:
|
||||
var dialog_menu_option := option_scene.instantiate()
|
||||
dialog_menu_option.normal_color = normal_font_color
|
||||
dialog_menu_option.normal_used_color = normal_used_font_color
|
||||
dialog_menu_option.hover_color = hover_font_color
|
||||
dialog_menu_option.hover_used_color = hover_used_font_color
|
||||
dialog_menu_option.pressed_color = pressed_font_color
|
||||
dialog_menu_option.pressed_used_color = pressed_used_font_color
|
||||
dialog_options_container.add_child(dialog_menu_option)
|
||||
|
||||
dialog_menu_option.option = dialog_option
|
||||
dialog_menu_option.pressed.connect(_on_option_clicked)
|
||||
|
||||
if dialog_option.disabled or not dialog_option.visible:
|
||||
dialog_menu_option.hide()
|
||||
else:
|
||||
dialog_menu_option.show()
|
||||
|
||||
if autoshow: show_options()
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
|
||||
# Fix: Height and position of the dialog menu was wrong when changing the amount of options to
|
||||
# show.
|
||||
var options_height := 0
|
||||
var visible_options := 0
|
||||
for opt in dialog_options_container.get_children():
|
||||
if not opt.visible: continue
|
||||
|
||||
options_height += opt.size.y
|
||||
visible_options += 1
|
||||
|
||||
options_height += (
|
||||
dialog_options_container.get_theme_constant("separation") * (visible_options - 1)
|
||||
)
|
||||
|
||||
panel_container.size.y = min(options_height, max_height)
|
||||
panel_container.position.y = PopochiuUtils.e.height - panel_container.size.y
|
||||
|
||||
|
||||
func remove_options(_dialog: PopochiuDialog = null) -> void:
|
||||
if not current_options.is_empty():
|
||||
current_options.clear()
|
||||
|
||||
for btn in dialog_options_container.get_children():
|
||||
btn.queue_free()
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
size.y = 0
|
||||
dialog_options_container.size.y = 0
|
||||
|
||||
|
||||
func show_options() -> void:
|
||||
PopochiuUtils.g.block()
|
||||
PopochiuUtils.g.dialog_options_shown.emit()
|
||||
|
||||
show()
|
||||
shown.emit()
|
||||
|
||||
|
||||
func _on_option_clicked(opt: PopochiuDialogOption) -> void:
|
||||
PopochiuUtils.g.unblock()
|
||||
|
||||
hide()
|
||||
PopochiuUtils.d.option_selected.emit(opt)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://c6el34etfenet
|
|
@ -0,0 +1,39 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://dhsfl8ot4j5fj"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/dialog_menu/dialog_menu.gd" id="1_3dp72"]
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_cvsnu"]
|
||||
[ext_resource type="PackedScene" uid="uid://dcta4urojglil" path="res://addons/popochiu/engine/objects/gui/components/dialog_menu/dialog_menu_option/dialog_menu_option.tscn" id="3_0jfsm"]
|
||||
|
||||
[node name="DialogMenu" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_cvsnu")
|
||||
script = ExtResource("1_3dp72")
|
||||
option_scene = ExtResource("3_0jfsm")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
custom_minimum_size = Vector2(0, 53)
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -53.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
|
||||
layout_mode = 2
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="DialogOptionsContainer" type="VBoxContainer" parent="PanelContainer/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 8
|
||||
theme_override_constants/separation = 1
|
|
@ -0,0 +1,92 @@
|
|||
class_name PopochiuDialogMenuOption
|
||||
extends PanelContainer
|
||||
|
||||
signal pressed(node: PopochiuDialogOption)
|
||||
|
||||
var option: PopochiuDialogOption : set = set_dialog_option
|
||||
var text := "" : set = set_text
|
||||
var used := false : set = set_used
|
||||
var normal_color := Color.WHITE
|
||||
var normal_used_color := normal_color
|
||||
var hover_color := Color.WHITE
|
||||
var hover_used_color := hover_color
|
||||
var pressed_color := Color.WHITE
|
||||
var pressed_used_color := pressed_color
|
||||
|
||||
var _state := "normal" : set = set_state
|
||||
|
||||
@onready var rich_text_label: RichTextLabel = %RichTextLabel
|
||||
@onready var handler: Button = %Handler
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
handler.pressed.connect(_on_pressed)
|
||||
handler.mouse_entered.connect(_on_mouse_entered)
|
||||
handler.mouse_exited.connect(_on_mouse_exited)
|
||||
handler.button_down.connect(_on_button_down)
|
||||
handler.button_up.connect(_on_button_up)
|
||||
|
||||
_update_font_color()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_dialog_option(value: PopochiuDialogOption) -> void:
|
||||
option = value
|
||||
if PopochiuConfig.should_dialog_options_be_gibberish():
|
||||
text = PopochiuUtils.d.create_gibberish(option.text)
|
||||
else:
|
||||
text = option.text
|
||||
|
||||
used = option.used and not option.always_on
|
||||
|
||||
|
||||
func set_text(value: String) -> void:
|
||||
text = value
|
||||
rich_text_label.text = value
|
||||
|
||||
|
||||
func set_used(value: bool) -> void:
|
||||
used = value
|
||||
_update_font_color()
|
||||
|
||||
|
||||
func set_state(value: String) -> void:
|
||||
_state = value
|
||||
_update_font_color()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _on_pressed() -> void:
|
||||
_state = "pressed"
|
||||
pressed.emit(option)
|
||||
|
||||
|
||||
func _on_mouse_entered() -> void:
|
||||
_state = "hover"
|
||||
|
||||
|
||||
func _on_mouse_exited() -> void:
|
||||
_state = "normal"
|
||||
|
||||
|
||||
func _on_button_down() -> void:
|
||||
_state = "pressed"
|
||||
|
||||
|
||||
func _on_button_up() -> void:
|
||||
_state = "hover" if handler.is_hovered() else "normal"
|
||||
|
||||
|
||||
func _update_font_color() -> void:
|
||||
rich_text_label.add_theme_color_override(
|
||||
"default_color",
|
||||
get("%s_color" % (_state + ("_used" if used else "")))
|
||||
)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://blkbqigv64y7w
|
|
@ -0,0 +1,40 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://dcta4urojglil"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_y0k4l"]
|
||||
[ext_resource type="FontFile" uid="uid://dixh1egf7k2fb" path="res://addons/popochiu/engine/objects/gui/fonts/monkeyisland_1991.ttf" id="2_5iw2f"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/dialog_menu/dialog_menu_option/dialog_menu_option.gd" id="2_nywcv"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_un1pr"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_we03b"]
|
||||
|
||||
[node name="DialogMenuOption" type="PanelContainer"]
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 24.0
|
||||
grow_horizontal = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_y0k4l")
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_un1pr")
|
||||
script = ExtResource("2_nywcv")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "A [wave]veeeeeeeeeeeeeery[/wave] long line option that should wrap so devs can have long options......"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="Handler" type="Button" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_fonts/font = ExtResource("2_5iw2f")
|
||||
theme_override_font_sizes/font_size = 12
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_we03b")
|
||||
flat = true
|
||||
alignment = 0
|
||||
text_overrun_behavior = 4
|
|
@ -0,0 +1,17 @@
|
|||
extends PopochiuDialogText
|
||||
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _modify_size(msg: String, _target_position: Vector2) -> void:
|
||||
var _size := await _calculate_size(msg)
|
||||
|
||||
# Define size and position (before calculating overflow)
|
||||
rich_text_label.size.y = _size.y
|
||||
rich_text_label.position.y = get_meta(DFLT_POSITION).y - (_size.y - get_meta(DFLT_SIZE).y)
|
||||
|
||||
|
||||
func _append_text(msg: String, props: Dictionary) -> void:
|
||||
rich_text_label.text = "[center][color=%s]%s[/color][/center]" % [props.color.to_html(), msg]
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://mnqgd41iws6y
|
|
@ -0,0 +1,58 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://bpl3qjbxonfpb"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/dialog_caption/dialog_caption.gd" id="1_17s2p"]
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_xoamn"]
|
||||
[ext_resource type="Texture2D" uid="uid://h156lkhxk5tl" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/images/ico_continue.png" id="3_etbl2"]
|
||||
|
||||
[node name="DialogCaption" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_xoamn")
|
||||
script = ExtResource("1_17s2p")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="."]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(16, 16)
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 20.0
|
||||
offset_top = -20.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = -4.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
size_flags_horizontal = 0
|
||||
mouse_filter = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 4
|
||||
bbcode_enabled = true
|
||||
text = "[center]A [shake]caption[/shake] dialog text[/center]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
meta_underlined = false
|
||||
|
||||
[node name="ContinueIcon" type="TextureProgressBar" parent="RichTextLabel"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -16.0
|
||||
offset_right = 16.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
value = 100.0
|
||||
fill_mode = 2
|
||||
texture_progress = ExtResource("3_etbl2")
|
|
@ -0,0 +1,47 @@
|
|||
extends PopochiuDialogText
|
||||
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _modify_size(msg: String, target_position: Vector2) -> void:
|
||||
var _size := await _calculate_size(msg)
|
||||
|
||||
# Define size and position (before calculating overflow)
|
||||
rich_text_label.size = _size
|
||||
rich_text_label.position = target_position - rich_text_label.size / 2.0
|
||||
rich_text_label.position.y -= rich_text_label.size.y / 2.0
|
||||
# Calculate overflow and reposition
|
||||
if rich_text_label.position.x < 0.0:
|
||||
rich_text_label.position.x = limit_margin
|
||||
elif rich_text_label.position.x + rich_text_label.size.x + continue_icon_size.x > _x_limit:
|
||||
rich_text_label.position.x = (
|
||||
_x_limit - limit_margin - rich_text_label.size.x - continue_icon_size.x
|
||||
)
|
||||
if rich_text_label.position.y < 0.0:
|
||||
rich_text_label.position.y = limit_margin
|
||||
elif rich_text_label.position.y + rich_text_label.size.y > _y_limit:
|
||||
rich_text_label.position.y = _y_limit - limit_margin - rich_text_label.size.y
|
||||
|
||||
|
||||
func _set_default_label_size(lbl: Label) -> void:
|
||||
lbl.size.y = get_meta(DFLT_SIZE).y
|
||||
|
||||
|
||||
func _append_text(msg: String, props: Dictionary) -> void:
|
||||
var center: float = floor(rich_text_label.position.x + (rich_text_label.size.x / 2))
|
||||
if center == props.position.x:
|
||||
rich_text_label.text = "[center]%s[/center]" % msg
|
||||
elif center < props.position.x:
|
||||
rich_text_label.text = "[right]%s[/right]" % msg
|
||||
else:
|
||||
rich_text_label.text = msg
|
||||
|
||||
|
||||
func _get_icon_from_position() -> float:
|
||||
return rich_text_label.size.y / 2.0 - 1.0
|
||||
|
||||
|
||||
func _get_icon_to_position() -> float:
|
||||
return rich_text_label.size.y / 2.0 + 3.0
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cp4khma0rcc33
|
|
@ -0,0 +1,47 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://bn7o13nv11ka1"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/dialog_overhead/dialog_overhead.gd" id="1_a5mdx"]
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="2_t336a"]
|
||||
[ext_resource type="Texture2D" uid="uid://h156lkhxk5tl" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/images/ico_continue.png" id="3_67j10"]
|
||||
|
||||
[node name="DialogOverhead" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("2_t336a")
|
||||
script = ExtResource("1_a5mdx")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="."]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(16, 16)
|
||||
layout_mode = 0
|
||||
offset_right = 153.0
|
||||
offset_bottom = 16.0
|
||||
size_flags_horizontal = 0
|
||||
mouse_filter = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 4
|
||||
bbcode_enabled = true
|
||||
text = "An [shake]overhead[/shake] dialog text"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
meta_underlined = false
|
||||
|
||||
[node name="ContinueIcon" type="TextureProgressBar" parent="RichTextLabel"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_right = 16.0
|
||||
offset_bottom = 16.0
|
||||
grow_horizontal = 0
|
||||
value = 100.0
|
||||
fill_mode = 2
|
||||
texture_progress = ExtResource("3_67j10")
|
|
@ -0,0 +1,45 @@
|
|||
extends PopochiuDialogText
|
||||
|
||||
@onready var left_avatar_container: PanelContainer = %LeftAvatarContainer
|
||||
@onready var left_avatar: TextureRect = %LeftAvatar
|
||||
@onready var right_avatar_container: PanelContainer = %RightAvatarContainer
|
||||
@onready var right_avatar: TextureRect = %RightAvatar
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
super()
|
||||
|
||||
# Connect to singletons signals
|
||||
PopochiuUtils.c.character_spoke.connect(_update_avatar)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _update_avatar(chr: PopochiuCharacter, _msg := '') -> void:
|
||||
if not rich_text_label.visible:
|
||||
return
|
||||
|
||||
left_avatar_container.modulate.a = 0.0
|
||||
left_avatar.texture = null
|
||||
right_avatar_container.modulate.a = 0.0
|
||||
right_avatar.texture = null
|
||||
|
||||
var char_pos: Vector2 = PopochiuUtils.get_screen_coords_for(chr).floor() / (
|
||||
PopochiuUtils.e.scale if PopochiuUtils.e.settings.scale_gui else Vector2.ONE
|
||||
)
|
||||
|
||||
if char_pos.x <= PopochiuUtils.e.half_width:
|
||||
left_avatar_container.modulate.a = 1.0
|
||||
left_avatar.texture = chr.get_avatar_for_emotion(chr.emotion)
|
||||
else:
|
||||
right_avatar_container.modulate.a = 1.0
|
||||
right_avatar.texture = chr.get_avatar_for_emotion(chr.emotion)
|
||||
|
||||
|
||||
func _set_default_size() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bad80j2xrbimk
|
|
@ -0,0 +1,106 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://33wmak2jumqm"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/dialog_portrait/dialog_portrait.gd" id="1_ejue5"]
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_x1xka"]
|
||||
[ext_resource type="Texture2D" uid="uid://h156lkhxk5tl" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/images/ico_continue.png" id="4_lqu2o"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8fbbc"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mjjs7"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 2.0
|
||||
bg_color = Color(0, 0, 0, 0.705882)
|
||||
|
||||
[node name="DialogPortrait" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_x1xka")
|
||||
script = ExtResource("1_ejue5")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -48.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_8fbbc")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 2
|
||||
|
||||
[node name="LeftAvatarContainer" type="PanelContainer" parent="PanelContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="LeftAvatar" type="TextureRect" parent="PanelContainer/HBoxContainer/LeftAvatarContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
custom_minimum_size = Vector2(48, 48)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 2
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="TextContainer" type="PanelContainer" parent="PanelContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_mjjs7")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="PanelContainer/HBoxContainer/TextContainer"]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(16, 16)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
bbcode_enabled = true
|
||||
text = "A [shake]portrait[/shake] dialog text."
|
||||
scroll_active = false
|
||||
meta_underlined = false
|
||||
|
||||
[node name="ContinueIcon" type="TextureProgressBar" parent="PanelContainer/HBoxContainer/TextContainer/RichTextLabel"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -16.0
|
||||
offset_top = -16.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
mouse_filter = 2
|
||||
value = 100.0
|
||||
fill_mode = 2
|
||||
texture_progress = ExtResource("4_lqu2o")
|
||||
|
||||
[node name="RightAvatarContainer" type="PanelContainer" parent="PanelContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="RightAvatar" type="TextureRect" parent="PanelContainer/HBoxContainer/RightAvatarContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
custom_minimum_size = Vector2(48, 48)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 2
|
||||
stretch_mode = 3
|
|
@ -0,0 +1,301 @@
|
|||
class_name PopochiuDialogText
|
||||
extends Control
|
||||
## Show dialogue texts char by char using a [RichTextLabel].
|
||||
##
|
||||
## An invisible [Label] is used to calculate the width of the [RichTextLabel] node.
|
||||
|
||||
signal animation_finished
|
||||
signal text_show_started
|
||||
signal text_show_finished
|
||||
|
||||
const DFLT_SIZE := "dflt_size"
|
||||
const DFLT_POSITION := "dflt_position"
|
||||
|
||||
@export var wrap_width := 200.0
|
||||
@export var limit_margin := 4.0
|
||||
|
||||
var tween: Tween = null
|
||||
var continue_icon_tween: Tween = null
|
||||
|
||||
var _secs_per_character := 1.0
|
||||
var _is_waiting_input := false
|
||||
var _auto_continue := false
|
||||
var _dialog_pos := Vector2.ZERO
|
||||
var _x_limit := 0.0
|
||||
var _y_limit := 0.0
|
||||
|
||||
@onready var rich_text_label: RichTextLabel = %RichTextLabel
|
||||
@onready var continue_icon: TextureProgressBar = %ContinueIcon
|
||||
@onready var continue_icon_size := continue_icon.texture_progress.get_size()
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
# Set the default values
|
||||
rich_text_label.text = ""
|
||||
|
||||
set_meta(DFLT_SIZE, rich_text_label.size)
|
||||
set_meta(DFLT_POSITION, rich_text_label.position)
|
||||
|
||||
modulate.a = 0.0
|
||||
_secs_per_character = PopochiuUtils.e.text_speed
|
||||
_x_limit = PopochiuUtils.e.width / (
|
||||
PopochiuUtils.e.scale.x if PopochiuUtils.e.settings.scale_gui else 1.0
|
||||
)
|
||||
_y_limit = PopochiuUtils.e.height / (
|
||||
PopochiuUtils.e.scale.y if PopochiuUtils.e.settings.scale_gui else 1.0
|
||||
)
|
||||
|
||||
# Connect to singletons events
|
||||
PopochiuUtils.e.text_speed_changed.connect(change_speed)
|
||||
PopochiuUtils.c.character_spoke.connect(_show_dialogue)
|
||||
|
||||
continue_icon.hide()
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if (
|
||||
not PopochiuUtils.get_click_or_touch_index(event) in [MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT]
|
||||
or modulate.a == 0.0
|
||||
):
|
||||
return
|
||||
|
||||
accept_event()
|
||||
if rich_text_label.visible_ratio == 1.0:
|
||||
disappear()
|
||||
else:
|
||||
stop()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
func play_text(props: Dictionary) -> void:
|
||||
var msg: String = PopochiuUtils.e.get_text(props.text)
|
||||
_is_waiting_input = false
|
||||
_dialog_pos = props.position
|
||||
|
||||
if PopochiuConfig.should_talk_gibberish():
|
||||
msg = PopochiuUtils.d.create_gibberish(msg)
|
||||
|
||||
# Call the virtual method that modifies the size of the RichTextLabel in case the dialog style
|
||||
# requires it.
|
||||
await _modify_size(msg, props.position)
|
||||
|
||||
# Assign the text and align mode
|
||||
msg = "[color=%s]%s[/color]" % [props.color.to_html(), msg]
|
||||
_append_text(msg, props)
|
||||
|
||||
if _secs_per_character > 0.0:
|
||||
# The text will appear with an animation
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
tween = create_tween()
|
||||
tween.tween_property(
|
||||
rich_text_label, "visible_ratio",
|
||||
1,
|
||||
_secs_per_character * rich_text_label.get_total_character_count()
|
||||
).from(0.0)
|
||||
tween.finished.connect(_wait_input)
|
||||
else:
|
||||
_wait_input()
|
||||
|
||||
modulate.a = 1.0
|
||||
|
||||
|
||||
func stop() ->void:
|
||||
if modulate.a == 0.0:
|
||||
return
|
||||
|
||||
if _is_waiting_input:
|
||||
_notify_completion()
|
||||
else:
|
||||
# Skip tweens
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
rich_text_label.visible_ratio = 1.0
|
||||
_wait_input()
|
||||
|
||||
|
||||
func disappear() -> void:
|
||||
if modulate.a == 0.0: return
|
||||
|
||||
_auto_continue = false
|
||||
modulate.a = 0.0
|
||||
_is_waiting_input = false
|
||||
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
rich_text_label.clear()
|
||||
rich_text_label.text = ""
|
||||
_set_default_size()
|
||||
|
||||
continue_icon.hide()
|
||||
continue_icon.modulate.a = 1.0
|
||||
|
||||
if is_instance_valid(continue_icon_tween) and continue_icon_tween.is_running():
|
||||
continue_icon_tween.kill()
|
||||
|
||||
set_process_input(false)
|
||||
text_show_finished.emit()
|
||||
PopochiuUtils.g.dialog_line_finished.emit()
|
||||
|
||||
|
||||
func change_speed() -> void:
|
||||
_secs_per_character = PopochiuUtils.e.text_speed
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _show_dialogue(chr: PopochiuCharacter, msg := "") -> void:
|
||||
if not visible: return
|
||||
|
||||
play_text({
|
||||
text = msg,
|
||||
color = chr.text_color,
|
||||
position = PopochiuUtils.get_screen_coords_for(chr, chr.dialog_pos).floor() / (
|
||||
PopochiuUtils.e.scale if PopochiuUtils.e.settings.scale_gui else Vector2.ONE
|
||||
),
|
||||
})
|
||||
|
||||
PopochiuUtils.g.dialog_line_started.emit()
|
||||
|
||||
set_process_input(true)
|
||||
text_show_started.emit()
|
||||
|
||||
|
||||
func _modify_size(_msg: String, _target_position: Vector2) -> void:
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
## Creates a RichTextLabel to calculate the resulting size of this node once the whole text is shown.
|
||||
func _calculate_size(msg: String) -> Vector2:
|
||||
var rt := RichTextLabel.new()
|
||||
rt.add_theme_font_override("normal_font", get_theme_font("normal_font"))
|
||||
rt.bbcode_enabled = true
|
||||
rt.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
rt.text = msg
|
||||
rt.size = get_meta(DFLT_SIZE)
|
||||
rich_text_label.add_child(rt)
|
||||
|
||||
# Create a Label to check if the text exceeds the wrap_width
|
||||
var lbl := Label.new()
|
||||
lbl.add_theme_font_override("normal_font", get_theme_font("normal_font"))
|
||||
|
||||
_set_default_label_size(lbl)
|
||||
|
||||
lbl.text = rt.get_parsed_text()
|
||||
rich_text_label.add_child(lbl)
|
||||
|
||||
rt.clear()
|
||||
rt.text = ""
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
var _size := lbl.size
|
||||
|
||||
if _size.x > wrap_width:
|
||||
# This node will have the width of the wrap_width
|
||||
_size.x = wrap_width
|
||||
rt.fit_content = true
|
||||
rt.size.x = _size.x
|
||||
rt.text = msg
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
_size = rt.size
|
||||
else:
|
||||
# This node will have the width of the text
|
||||
_size.y = get_meta(DFLT_SIZE).y
|
||||
|
||||
var characters_count := lbl.get_total_character_count()
|
||||
|
||||
lbl.free()
|
||||
rt.free()
|
||||
|
||||
return _size
|
||||
|
||||
|
||||
func _set_default_label_size(lbl: Label) -> void:
|
||||
lbl.size = get_meta(DFLT_SIZE)
|
||||
|
||||
|
||||
func _append_text(msg: String, _props: Dictionary) -> void:
|
||||
rich_text_label.append_text(msg)
|
||||
|
||||
|
||||
func _wait_input() -> void:
|
||||
_is_waiting_input = true
|
||||
|
||||
if is_instance_valid(tween) and tween.finished.is_connected(_wait_input):
|
||||
tween.finished.disconnect(_wait_input)
|
||||
|
||||
if PopochiuUtils.e.auto_continue_after >= 0.0:
|
||||
_auto_continue = true
|
||||
await get_tree().create_timer(PopochiuUtils.e.auto_continue_after + 0.2).timeout
|
||||
|
||||
if _auto_continue:
|
||||
_continue(true)
|
||||
else:
|
||||
_show_icon()
|
||||
|
||||
|
||||
func _show_icon() -> void:
|
||||
if is_instance_valid(continue_icon_tween) and continue_icon_tween.is_running():
|
||||
continue_icon_tween.kill()
|
||||
|
||||
continue_icon_tween = create_tween()
|
||||
|
||||
if not PopochiuUtils.e.settings.auto_continue_text:
|
||||
# For manual continuation: make the icon jump
|
||||
continue_icon.value = 100.0
|
||||
continue_icon_tween.tween_property(
|
||||
continue_icon, "position:y", _get_icon_to_position(), 0.8
|
||||
).from(_get_icon_from_position()).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
||||
continue_icon_tween.set_loops()
|
||||
else:
|
||||
# For automatic continuation: Make the icon appear as a progress bar indicating the time
|
||||
# players have to read before auto-continuing.
|
||||
continue_icon.value = 0.0
|
||||
continue_icon.position.y = size.y / 2.0
|
||||
|
||||
continue_icon_tween.tween_property(
|
||||
continue_icon, "value",
|
||||
100.0, 3.0,
|
||||
).from_current().set_ease(Tween.EASE_OUT)
|
||||
continue_icon_tween.finished.connect(_continue)
|
||||
|
||||
continue_icon_tween.pause()
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
continue_icon_tween.play()
|
||||
continue_icon.show()
|
||||
|
||||
|
||||
func _get_icon_from_position() -> float:
|
||||
return rich_text_label.size.y - continue_icon.size.y + 2.0
|
||||
|
||||
|
||||
func _get_icon_to_position() -> float:
|
||||
return rich_text_label.size.y - continue_icon.size.y - 1.0
|
||||
|
||||
|
||||
func _notify_completion() -> void:
|
||||
disappear()
|
||||
animation_finished.emit()
|
||||
|
||||
|
||||
func _continue(forced_continue := false) -> void:
|
||||
if PopochiuUtils.e.settings.auto_continue_text or forced_continue:
|
||||
disappear()
|
||||
|
||||
|
||||
func _set_default_size() -> void:
|
||||
rich_text_label.size = get_meta(DFLT_SIZE)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://1kyr8r56atd8
|
|
@ -0,0 +1,38 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://cymfte4xe3tnw"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_mrmwc"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/dialog_text.gd" id="2_adpg7"]
|
||||
[ext_resource type="Texture2D" uid="uid://h156lkhxk5tl" path="res://addons/popochiu/engine/objects/gui/components/dialog_text/images/ico_continue.png" id="3_fqlmr"]
|
||||
|
||||
[node name="DialogText" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_mrmwc")
|
||||
script = ExtResource("2_adpg7")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
offset_right = 74.0
|
||||
offset_bottom = 12.0
|
||||
text = "Dialog text"
|
||||
fit_content = true
|
||||
|
||||
[node name="ContinueIcon" type="TextureProgressBar" parent="RichTextLabel"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_right = 16.0
|
||||
offset_bottom = 16.0
|
||||
grow_horizontal = 0
|
||||
value = 100.0
|
||||
fill_mode = 2
|
||||
texture_progress = ExtResource("3_fqlmr")
|
Binary file not shown.
After Width: | Height: | Size: 173 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://h156lkhxk5tl"
|
||||
path="res://.godot/imported/ico_continue.png-1c56d7b39fdf03dafd5aacca996d24e0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/popochiu/engine/objects/gui/components/dialog_text/images/ico_continue.png"
|
||||
dest_files=["res://.godot/imported/ico_continue.png-1c56d7b39fdf03dafd5aacca996d24e0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
|
@ -0,0 +1,39 @@
|
|||
extends Control
|
||||
class_name PopochiuHoverText
|
||||
|
||||
@export var hide_during_dialogs := false
|
||||
|
||||
@onready var label: RichTextLabel = $RichTextLabel
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
label.text = ""
|
||||
|
||||
# Connect to autoloads' signals
|
||||
PopochiuUtils.g.hover_text_shown.connect(_show_text)
|
||||
PopochiuUtils.g.dialog_line_started.connect(_on_dialog_line_started)
|
||||
PopochiuUtils.g.dialog_line_finished.connect(_on_dialog_line_finished)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
func _show_text(txt := "") -> void:
|
||||
label.text = "[center]%s[/center]" % txt
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _on_dialog_line_started() -> void:
|
||||
if hide_during_dialogs:
|
||||
hide()
|
||||
|
||||
|
||||
func _on_dialog_line_finished() -> void:
|
||||
if hide_during_dialogs:
|
||||
show()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cb8ngs7ei1rao
|
|
@ -0,0 +1,29 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://esorelppu4hw"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_s7kd8"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/hover_text/hover_text.gd" id="2_r2ckj"]
|
||||
|
||||
[node name="HoverText" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_s7kd8")
|
||||
script = ExtResource("2_r2ckj")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 12.0
|
||||
grow_horizontal = 2
|
||||
mouse_filter = 2
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 4
|
||||
bbcode_enabled = true
|
||||
text = "[center]text for hovered objects[/center]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
|
@ -0,0 +1,183 @@
|
|||
extends Control
|
||||
|
||||
@export var always_visible := false
|
||||
@export var hide_when_gui_is_blocked := false
|
||||
## Defines the height in pixels of the zone where moving the mouse in the top of the screen will
|
||||
## make the bar to show. Note: This value will be affected by the Experimental Scale GUI checkbox
|
||||
## in Project Settings > Popochiu > GUI.
|
||||
@export var input_zone_height := 4
|
||||
|
||||
var is_disabled := false
|
||||
var tween: Tween = null
|
||||
|
||||
var _is_hidden := true
|
||||
|
||||
@onready var panel_container: PanelContainer = %PanelContainer
|
||||
@onready var box: Container = %Box
|
||||
@onready var hidden_y := panel_container.position.y - panel_container.size.y
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready():
|
||||
if not always_visible:
|
||||
panel_container.position.y = hidden_y
|
||||
|
||||
# Connect to singletons signals
|
||||
PopochiuUtils.g.blocked.connect(_on_gui_blocked)
|
||||
PopochiuUtils.g.unblocked.connect(_on_gui_unblocked)
|
||||
PopochiuUtils.i.item_added.connect(_add_item)
|
||||
PopochiuUtils.i.item_removed.connect(_remove_item)
|
||||
PopochiuUtils.i.item_replaced.connect(_replace_item)
|
||||
PopochiuUtils.i.inventory_show_requested.connect(_show_and_hide)
|
||||
PopochiuUtils.i.inventory_hide_requested.connect(_close)
|
||||
|
||||
# Check if there are already items in the inventory (set manually in the scene)
|
||||
for ii in box.get_children():
|
||||
if ii is PopochiuInventoryItem:
|
||||
ii.in_inventory = true
|
||||
ii.selected.connect(_change_cursor)
|
||||
|
||||
set_process_input(not always_visible)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not event is InputEventMouseMotion: return
|
||||
|
||||
var rect := panel_container.get_rect()
|
||||
rect.size += Vector2(0.0, input_zone_height)
|
||||
if PopochiuUtils.e.settings.scale_gui:
|
||||
rect = Rect2(
|
||||
panel_container.get_rect().position * PopochiuUtils.e.scale,
|
||||
panel_container.get_rect().size * PopochiuUtils.e.scale
|
||||
)
|
||||
|
||||
if _is_hidden and rect.has_point(get_global_mouse_position()):
|
||||
_open()
|
||||
elif not _is_hidden and not rect.has_point(get_global_mouse_position()):
|
||||
_close()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _open() -> void:
|
||||
if always_visible: return
|
||||
if not is_disabled and panel_container.position.y != hidden_y: return
|
||||
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
tween = create_tween().set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_OUT)
|
||||
tween.tween_property(
|
||||
panel_container, "position:y", 0.0, 0.5
|
||||
).from(hidden_y if not is_disabled else panel_container.position.y)
|
||||
|
||||
_is_hidden = false
|
||||
|
||||
|
||||
func _close() -> void:
|
||||
if always_visible: return
|
||||
await get_tree().process_frame
|
||||
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
tween = create_tween()
|
||||
tween.tween_property(
|
||||
panel_container, "position:y",
|
||||
hidden_y if not is_disabled else hidden_y - 3.5,
|
||||
0.2
|
||||
).from(0.0).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
||||
|
||||
_is_hidden = true
|
||||
|
||||
|
||||
func _on_tween_finished() -> void:
|
||||
_is_hidden = panel_container.position.y == hidden_y
|
||||
|
||||
|
||||
func _change_cursor(item: PopochiuInventoryItem) -> void:
|
||||
PopochiuUtils.i.set_active_item(item)
|
||||
|
||||
|
||||
func _on_gui_blocked() -> void:
|
||||
set_process_input(false)
|
||||
|
||||
if hide_when_gui_is_blocked:
|
||||
hide()
|
||||
|
||||
|
||||
func _on_gui_unblocked() -> void:
|
||||
set_process_input(true)
|
||||
|
||||
if hide_when_gui_is_blocked:
|
||||
show()
|
||||
|
||||
|
||||
func _add_item(item: PopochiuInventoryItem, animate := true) -> void:
|
||||
box.add_child(item)
|
||||
|
||||
item.expand_mode = TextureRect.EXPAND_FIT_WIDTH
|
||||
item.custom_minimum_size.y = box.size.y
|
||||
|
||||
item.selected.connect(_change_cursor)
|
||||
|
||||
if not always_visible and animate:
|
||||
# Show the inventory for a while and hide after a couple of seconds so players can see the
|
||||
# item being added to the inventory
|
||||
set_process_input(false)
|
||||
|
||||
_open()
|
||||
await get_tree().create_timer(2.0).timeout
|
||||
|
||||
# The mouse not being on the inventory can close the inventory prior to the 2 seconds
|
||||
# expiring. This check fixes this. Bug 350.
|
||||
if not _is_hidden:
|
||||
_close()
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
|
||||
set_process_input(true)
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
|
||||
PopochiuUtils.i.item_add_done.emit(item)
|
||||
|
||||
|
||||
func _remove_item(item: PopochiuInventoryItem, animate := true) -> void:
|
||||
item.selected.disconnect(_change_cursor)
|
||||
box.remove_child(item)
|
||||
|
||||
if not always_visible:
|
||||
PopochiuUtils.cursor.show_cursor()
|
||||
PopochiuUtils.g.show_hover_text()
|
||||
|
||||
if animate:
|
||||
_close()
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
PopochiuUtils.i.item_remove_done.emit(item)
|
||||
|
||||
|
||||
func _replace_item(item: PopochiuInventoryItem, new_item: PopochiuInventoryItem) -> void:
|
||||
item.replace_by(new_item)
|
||||
await get_tree().process_frame
|
||||
|
||||
PopochiuUtils.i.item_replace_done.emit()
|
||||
|
||||
|
||||
func _show_and_hide(time := 1.0) -> void:
|
||||
set_process_input(false)
|
||||
_open()
|
||||
await tween.finished
|
||||
await PopochiuUtils.e.wait(time)
|
||||
|
||||
_close()
|
||||
await tween.finished
|
||||
|
||||
set_process_input(true)
|
||||
PopochiuUtils.i.inventory_shown.emit()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cnso60yytp7ot
|
|
@ -0,0 +1,27 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://ciar5j7qm85bc"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/inventory_bar/inventory_bar.gd" id="1"]
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_aynoo"]
|
||||
|
||||
[node name="InventoryBar" type="Control" groups=["popochiu_gui_component"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("1_aynoo")
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 0
|
||||
offset_right = 36.0
|
||||
offset_bottom = 24.0
|
||||
|
||||
[node name="Box" type="HBoxContainer" parent="PanelContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
Binary file not shown.
After Width: | Height: | Size: 249 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://s4phlorab5ds"
|
||||
path="res://.godot/imported/inventory_grid_down_button.png-ffac1afa54860747ab14c6a1f8220bae.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/popochiu/engine/objects/gui/components/inventory_grid/images/inventory_grid_down_button.png"
|
||||
dest_files=["res://.godot/imported/inventory_grid_down_button.png-ffac1afa54860747ab14c6a1f8220bae.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
Binary file not shown.
After Width: | Height: | Size: 246 B |
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dfa511cxg31g5"
|
||||
path="res://.godot/imported/inventory_grid_up_button.png-dd40c7fcb22a7dfc4741c7c1c2d05d54.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/popochiu/engine/objects/gui/components/inventory_grid/images/inventory_grid_up_button.png"
|
||||
dest_files=["res://.godot/imported/inventory_grid_up_button.png-dd40c7fcb22a7dfc4741c7c1c2d05d54.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
|
@ -0,0 +1,226 @@
|
|||
@tool
|
||||
class_name PopochiuInventoryGrid
|
||||
extends HBoxContainer
|
||||
|
||||
const EMPTY_SLOT := "[Empty Slot]00"
|
||||
|
||||
@export var slot_scene: PackedScene = null : set = set_slot_scene
|
||||
@export var columns := 4 : set = set_columns
|
||||
@export var visible_rows := 2 : set = set_visible_rows
|
||||
@export var number_of_slots := 16 : set = set_number_of_slots
|
||||
@export var h_separation := 0 : set = set_h_separation
|
||||
@export var v_separation := 0 : set = set_v_separation
|
||||
@export var show_arrows := true : set = set_show_arrows
|
||||
@export var scroll_with_mouse_wheel := true
|
||||
|
||||
var rows := 0
|
||||
var max_scroll := 0.0
|
||||
|
||||
var slot_size: float = 0.0
|
||||
|
||||
@onready var scroll_container: ScrollContainer = $ScrollContainer
|
||||
@onready var box: GridContainer = %Box
|
||||
@onready var scroll_buttons: VBoxContainer = $ScrollButtons
|
||||
@onready var up: TextureButton = %Up
|
||||
@onready var down: TextureButton = %Down
|
||||
@onready var gap_size: int = box.get_theme_constant("v_separation")
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
_update_box()
|
||||
return
|
||||
|
||||
scroll_container.mouse_filter = (
|
||||
Control.MOUSE_FILTER_PASS if scroll_with_mouse_wheel else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
|
||||
_update_box()
|
||||
_calculate_rows_and_scroll()
|
||||
# _check_starting_items()
|
||||
|
||||
# Connect to child signals
|
||||
up.pressed.connect(_on_up_pressed)
|
||||
down.pressed.connect(_on_down_pressed)
|
||||
scroll_container.get_v_scroll_bar().value_changed.connect(_on_scroll)
|
||||
|
||||
# Connect to singletons signals
|
||||
PopochiuUtils.i.item_added.connect(_add_item)
|
||||
PopochiuUtils.i.item_removed.connect(_remove_item)
|
||||
PopochiuUtils.i.item_replaced.connect(_replace_item)
|
||||
|
||||
_check_scroll_buttons()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_visible_rows(value: int) -> void:
|
||||
visible_rows = value
|
||||
_update_box()
|
||||
|
||||
|
||||
func set_columns(value: int) -> void:
|
||||
columns = value
|
||||
_update_box()
|
||||
|
||||
|
||||
func set_slot_scene(value: PackedScene) -> void:
|
||||
slot_scene = value
|
||||
_update_box()
|
||||
|
||||
|
||||
func set_number_of_slots(value: int) -> void:
|
||||
number_of_slots = value
|
||||
_update_box()
|
||||
|
||||
|
||||
func set_h_separation(value: int) -> void:
|
||||
h_separation = value
|
||||
_update_box()
|
||||
|
||||
|
||||
func set_v_separation(value: int) -> void:
|
||||
v_separation = value
|
||||
_update_box()
|
||||
|
||||
|
||||
func set_show_arrows(value: bool) -> void:
|
||||
show_arrows = value
|
||||
|
||||
if is_instance_valid(scroll_buttons):
|
||||
scroll_buttons.visible = value
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _update_box() -> void:
|
||||
if not is_instance_valid(box): return
|
||||
|
||||
box.columns = columns
|
||||
box.add_theme_constant_override("h_separation", h_separation)
|
||||
box.add_theme_constant_override("v_separation", v_separation)
|
||||
|
||||
# Fix: remove the child immediately (instead of calling queue_free()), and do not await for
|
||||
# a process frame cause it can cause an issue when adding items marked as "Start with it".
|
||||
for child in box.get_children():
|
||||
child.free()
|
||||
|
||||
for idx in number_of_slots:
|
||||
var slot := slot_scene.instantiate()
|
||||
box.add_child(slot)
|
||||
|
||||
slot.name = EMPTY_SLOT
|
||||
slot_size = slot.size.y
|
||||
|
||||
scroll_container.custom_minimum_size = Vector2(
|
||||
(columns * (slot_size + h_separation)) - h_separation,
|
||||
(visible_rows * (slot_size + v_separation)) - v_separation
|
||||
)
|
||||
|
||||
|
||||
## Calculate the number of rows in the box and the max scroll
|
||||
func _calculate_rows_and_scroll() -> void:
|
||||
var visible_slots := 0
|
||||
for slot in box.get_children():
|
||||
if slot.visible:
|
||||
visible_slots += 1
|
||||
@warning_ignore("integer_division")
|
||||
rows = visible_slots / box.columns
|
||||
max_scroll = ((slot_size + gap_size) * int(rows / 2))
|
||||
|
||||
|
||||
## Check if there are inventory items in the scene tree and add them to the
|
||||
## Inventory interface class (I).
|
||||
func _check_starting_items() -> void:
|
||||
for slot in box.get_children():
|
||||
if (slot.get_child_count() > 0
|
||||
and slot.get_child(0) is PopochiuInventoryItem
|
||||
):
|
||||
PopochiuUtils.i.items.append(slot.get_child(0).script_name)
|
||||
slot.name = slot.get_child(0).script_name
|
||||
else:
|
||||
slot.name = EMPTY_SLOT
|
||||
|
||||
|
||||
func _on_up_pressed() -> void:
|
||||
scroll_container.scroll_vertical -= (slot_size + gap_size) + 1
|
||||
_check_scroll_buttons()
|
||||
|
||||
|
||||
func _on_down_pressed() -> void:
|
||||
scroll_container.scroll_vertical += (slot_size + gap_size) + 1
|
||||
_check_scroll_buttons()
|
||||
|
||||
|
||||
func _add_item(item: PopochiuInventoryItem, _animate := true) -> void:
|
||||
var slot := box.get_child(PopochiuUtils.i.items.size() - 1)
|
||||
slot.name = "[%s]" % item.script_name
|
||||
slot.add_child(item)
|
||||
|
||||
item.expand_mode = TextureRect.EXPAND_FIT_WIDTH
|
||||
|
||||
if slot.has_method("get_content_height"):
|
||||
item.custom_minimum_size.y = slot.get_content_height()
|
||||
else:
|
||||
item.custom_minimum_size.y = slot.size.y
|
||||
|
||||
box.set_meta(item.script_name, slot)
|
||||
|
||||
item.selected.connect(_change_cursor)
|
||||
_check_scroll_buttons()
|
||||
|
||||
# Common call to all inventories. Should be in the class from where inventory panels will
|
||||
# inherit from
|
||||
await get_tree().process_frame
|
||||
|
||||
PopochiuUtils.i.item_add_done.emit(item)
|
||||
|
||||
|
||||
func _remove_item(item: PopochiuInventoryItem, _animate := true) -> void:
|
||||
item.selected.disconnect(_change_cursor)
|
||||
|
||||
box.get_meta(item.script_name).remove_child(item)
|
||||
box.get_meta(item.script_name).name = EMPTY_SLOT
|
||||
|
||||
_check_scroll_buttons()
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
PopochiuUtils.i.item_remove_done.emit(item)
|
||||
|
||||
|
||||
func _replace_item(
|
||||
item: PopochiuInventoryItem, new_item: PopochiuInventoryItem
|
||||
) -> void:
|
||||
item.replace_by(new_item)
|
||||
box.remove_meta(item.script_name)
|
||||
box.set_meta(new_item.script_name, new_item.get_parent())
|
||||
|
||||
_check_scroll_buttons()
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
PopochiuUtils.i.item_replace_done.emit()
|
||||
|
||||
|
||||
func _change_cursor(item: PopochiuInventoryItem) -> void:
|
||||
PopochiuUtils.i.set_active_item(item)
|
||||
|
||||
|
||||
## Checks if the UP and DOWN buttons should be enabled
|
||||
func _check_scroll_buttons() -> void:
|
||||
up.disabled = scroll_container.scroll_vertical == 0
|
||||
down.disabled = (
|
||||
scroll_container.scroll_vertical >= max_scroll
|
||||
or not (PopochiuUtils.i.items.size() > box.columns * visible_rows)
|
||||
)
|
||||
|
||||
|
||||
func _on_scroll(_value: float) -> void:
|
||||
_check_scroll_buttons()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://5iimjg0vmeqf
|
|
@ -0,0 +1,91 @@
|
|||
[gd_scene load_steps=14 format=3 uid="uid://cn2b5v8k1lseo"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/inventory_grid/inventory_grid.gd" id="1_0y5i1"]
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_kaq1v"]
|
||||
[ext_resource type="Texture2D" uid="uid://dfa511cxg31g5" path="res://addons/popochiu/engine/objects/gui/components/inventory_grid/images/inventory_grid_up_button.png" id="2_6y3so"]
|
||||
[ext_resource type="PackedScene" uid="uid://db6csk6i4f3un" path="res://addons/popochiu/engine/objects/gui/components/inventory_grid/inventory_grid_slot.tscn" id="3_ma7vn"]
|
||||
[ext_resource type="Texture2D" uid="uid://s4phlorab5ds" path="res://addons/popochiu/engine/objects/gui/components/inventory_grid/images/inventory_grid_down_button.png" id="3_yoinj"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_7tbkh"]
|
||||
atlas = ExtResource("2_6y3so")
|
||||
region = Rect2(0, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_yafup"]
|
||||
atlas = ExtResource("2_6y3so")
|
||||
region = Rect2(32, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_8r5xk"]
|
||||
atlas = ExtResource("2_6y3so")
|
||||
region = Rect2(16, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_73nr4"]
|
||||
atlas = ExtResource("2_6y3so")
|
||||
region = Rect2(48, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_mcgs4"]
|
||||
atlas = ExtResource("3_yoinj")
|
||||
region = Rect2(0, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_kw4h2"]
|
||||
atlas = ExtResource("3_yoinj")
|
||||
region = Rect2(32, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_pi25e"]
|
||||
atlas = ExtResource("3_yoinj")
|
||||
region = Rect2(16, 0, 16, 16)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_pejpn"]
|
||||
atlas = ExtResource("3_yoinj")
|
||||
region = Rect2(48, 0, 16, 16)
|
||||
|
||||
[node name="InventoryGrid" type="HBoxContainer" groups=["popochiu_gui_component"]]
|
||||
offset_right = 152.0
|
||||
offset_bottom = 50.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 0
|
||||
theme = ExtResource("1_kaq1v")
|
||||
script = ExtResource("1_0y5i1")
|
||||
slot_scene = ExtResource("3_ma7vn")
|
||||
|
||||
[node name="ScrollButtons" type="VBoxContainer" parent="."]
|
||||
custom_minimum_size = Vector2(24, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Up" type="TextureButton" parent="ScrollButtons"]
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
texture_normal = SubResource("AtlasTexture_7tbkh")
|
||||
texture_pressed = SubResource("AtlasTexture_yafup")
|
||||
texture_hover = SubResource("AtlasTexture_8r5xk")
|
||||
texture_disabled = SubResource("AtlasTexture_73nr4")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="Down" type="TextureButton" parent="ScrollButtons"]
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
texture_normal = SubResource("AtlasTexture_mcgs4")
|
||||
texture_pressed = SubResource("AtlasTexture_kw4h2")
|
||||
texture_hover = SubResource("AtlasTexture_pi25e")
|
||||
texture_disabled = SubResource("AtlasTexture_pejpn")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="."]
|
||||
custom_minimum_size = Vector2(100, 50)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
scroll_vertical_custom_step = 27.0
|
||||
horizontal_scroll_mode = 3
|
||||
vertical_scroll_mode = 3
|
||||
|
||||
[node name="Box" type="GridContainer" parent="ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 0
|
||||
theme_override_constants/v_separation = 0
|
||||
columns = 4
|
|
@ -0,0 +1,17 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://db6csk6i4f3un"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fjc4l"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
bg_color = Color(0.180392, 0.172549, 0.607843, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(1, 1, 1, 1)
|
||||
|
||||
[node name="Slot" type="PanelContainer"]
|
||||
custom_minimum_size = Vector2(31, 25)
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_fjc4l")
|
|
@ -0,0 +1,25 @@
|
|||
extends "../popochiu_popup.gd"
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the popup is opened. At this point it is not visible yet.
|
||||
func _open() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the popup is closed. The node hides after calling this method.
|
||||
func _close() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when OK is pressed.
|
||||
func _on_ok() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when CANCEL or X (top-right corner) are pressed.
|
||||
func _on_cancel() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bed7ao6h80o80
|
|
@ -0,0 +1,82 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://d06okn5i6id2g"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_ck2qk"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/confirmation_popup/confirmation_popup.gd" id="2_2ngwd"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="3_jqrxu"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_n4smw"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_4phlw"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_x73gy"]
|
||||
|
||||
[node name="ConfirmationPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_ck2qk")
|
||||
script = ExtResource("2_2ngwd")
|
||||
script_name = &"ConfirmationPopup"
|
||||
title = "Confirmation"
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_x73gy")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("3_jqrxu")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Confirmation"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_n4smw")
|
||||
texture_pressed = ExtResource("4_4phlw")
|
||||
texture_hover = ExtResource("4_4phlw")
|
||||
|
||||
[node name="Question" type="Label" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Yes or No?"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "ok"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "cancel"
|
|
@ -0,0 +1,13 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://b6dm3xvvr7dvi"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://dixh1egf7k2fb" path="res://addons/popochiu/engine/objects/gui/fonts/monkeyisland_1991.ttf" id="1_o6vvl"]
|
||||
|
||||
[node name="DialogLine" type="RichTextLabel"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_fonts/normal_font = ExtResource("1_o6vvl")
|
||||
bbcode_enabled = true
|
||||
text = "Popochiu: Hi!"
|
||||
fit_content = true
|
|
@ -0,0 +1,13 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://2mnjw3qsi8hc"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://dixh1egf7k2fb" path="res://addons/popochiu/engine/objects/gui/fonts/monkeyisland_1991.ttf" id="1_i1qxr"]
|
||||
|
||||
[node name="InteractionLine" type="RichTextLabel"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_fonts/normal_font = ExtResource("1_i1qxr")
|
||||
bbcode_enabled = true
|
||||
text = "Player [color=edf171]click[/color] Toy car"
|
||||
fit_content = true
|
|
@ -0,0 +1,57 @@
|
|||
@tool
|
||||
extends PopochiuPopup
|
||||
|
||||
@export var dialog_line: PackedScene = null
|
||||
@export var interaction_line: PackedScene = null
|
||||
|
||||
@onready var lines_list: VBoxContainer = find_child("LinesList")
|
||||
@onready var empty: Label = %Empty
|
||||
@onready var lines_scroll: ScrollContainer = %LinesScroll
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
super()
|
||||
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
for c in lines_list.get_children():
|
||||
(c as Control).queue_free()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
func _open() -> void:
|
||||
if PopochiuUtils.e.history.is_empty():
|
||||
empty.show()
|
||||
lines_scroll.hide()
|
||||
else:
|
||||
empty.hide()
|
||||
lines_scroll.show()
|
||||
|
||||
for data in PopochiuUtils.e.history:
|
||||
var lbl: RichTextLabel
|
||||
|
||||
if data.has("character"):
|
||||
lbl = dialog_line.instantiate()
|
||||
lbl.text = "[color=%s]%s:[/color] %s" % [
|
||||
(data.character as PopochiuCharacter).text_color.to_html(false),
|
||||
(data.character as PopochiuCharacter).description,
|
||||
data.text
|
||||
]
|
||||
else:
|
||||
lbl = interaction_line.instantiate()
|
||||
lbl.text = "[color=edf171]%s[/color] [color=a9ff9f]%s[/color]" % [
|
||||
data.action, data.target
|
||||
]
|
||||
|
||||
lines_list.add_child(lbl)
|
||||
|
||||
|
||||
func _close() -> void:
|
||||
for c in lines_list.get_children():
|
||||
(c as Control).queue_free()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://beng6nsf3gmgy
|
|
@ -0,0 +1,115 @@
|
|||
[gd_scene load_steps=12 format=3 uid="uid://dfrsiyyqncspo"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_ktant"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/history_popup/history_popup.gd" id="2_bdgcw"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_6f0nr"]
|
||||
[ext_resource type="PackedScene" uid="uid://b6dm3xvvr7dvi" path="res://addons/popochiu/engine/objects/gui/components/popups/history_popup/components/dialog_line.tscn" id="3_8deqt"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_e8jii"]
|
||||
[ext_resource type="PackedScene" uid="uid://2mnjw3qsi8hc" path="res://addons/popochiu/engine/objects/gui/components/popups/history_popup/components/interaction_line.tscn" id="4_ndekf"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="5_odsj4"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_xipc1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pm06l"]
|
||||
bg_color = Color(1, 1, 1, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gr6a3"]
|
||||
bg_color = Color(0.768627, 0.423529, 0.443137, 1)
|
||||
border_width_left = 8
|
||||
border_color = Color(0.388235, 0.607843, 1, 1)
|
||||
|
||||
[sub_resource type="Theme" id="Theme_cqgnt"]
|
||||
VScrollBar/styles/grabber = SubResource("StyleBoxFlat_pm06l")
|
||||
VScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_pm06l")
|
||||
VScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_pm06l")
|
||||
VScrollBar/styles/scroll = SubResource("StyleBoxFlat_gr6a3")
|
||||
VScrollBar/styles/scroll_focus = SubResource("StyleBoxFlat_gr6a3")
|
||||
|
||||
[node name="HistoryPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_ktant")
|
||||
script = ExtResource("2_bdgcw")
|
||||
dialog_line = ExtResource("3_8deqt")
|
||||
interaction_line = ExtResource("4_ndekf")
|
||||
script_name = &"HistoryPopup"
|
||||
title = "History"
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_xipc1")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
custom_minimum_size = Vector2(256, 144)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("5_odsj4")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "History"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_6f0nr")
|
||||
texture_pressed = ExtResource("4_e8jii")
|
||||
texture_hover = ExtResource("4_e8jii")
|
||||
|
||||
[node name="Empty" type="Label" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
text = "Nothing to show yet"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="LinesScroll" type="ScrollContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme = SubResource("Theme_cqgnt")
|
||||
|
||||
[node name="LinesList" type="VBoxContainer" parent="Overlay/PanelContainer/VBoxContainer/LinesScroll"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "ok"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "cancel"
|
|
@ -0,0 +1,139 @@
|
|||
@tool
|
||||
class_name PopochiuPopup
|
||||
extends Control
|
||||
## The base popup node used by Popochiu GUIs.
|
||||
|
||||
@export var closes_by_clicking_out := true
|
||||
@export var script_name: StringName = ""
|
||||
@export var title := "" : set = set_title
|
||||
|
||||
@onready var lbl_title: Label = %Title
|
||||
@onready var btn_ok: Button = %Ok
|
||||
@onready var btn_cancel: Button = %Cancel
|
||||
@onready var btn_close: TextureButton = %Close
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
if not title.is_empty():
|
||||
lbl_title.text = title
|
||||
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
# Connect to own signals
|
||||
$Overlay.gui_input.connect(_check_click)
|
||||
|
||||
# Connect to child signals
|
||||
btn_ok.pressed.connect(on_ok_pressed)
|
||||
btn_cancel.pressed.connect(on_cancel_pressed)
|
||||
btn_close.pressed.connect(on_cancel_pressed)
|
||||
|
||||
# Connect to singleton signals
|
||||
PopochiuUtils.g.popup_requested.connect(_on_popup_requested)
|
||||
|
||||
close()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the popup is opened. At this point it is not visible yet.
|
||||
func _open() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the popup is closed. The node hides after calling this method.
|
||||
func _close() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when OK is pressed.
|
||||
func _on_ok() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when CANCEL or X (top-right corner) are pressed.
|
||||
func _on_cancel() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Shows the popup scaling it and blocking interactions with the graphic interface.
|
||||
func open() -> void:
|
||||
_open()
|
||||
|
||||
PopochiuUtils.g.block()
|
||||
PopochiuUtils.cursor.show_cursor("gui", true)
|
||||
# Never open a popup on top of another
|
||||
if not PopochiuUtils.e.gui.popups_stack.is_empty():
|
||||
PopochiuUtils.e.gui.popups_stack.back().hide()
|
||||
PopochiuUtils.e.gui.popups_stack.append(self)
|
||||
|
||||
show()
|
||||
|
||||
|
||||
## Closes the popup unlocking interactions with the graphic interface.
|
||||
func close() -> void:
|
||||
PopochiuUtils.e.gui.popups_stack.erase(self)
|
||||
hide()
|
||||
|
||||
if PopochiuUtils.e.gui.popups_stack.is_empty():
|
||||
PopochiuUtils.g.unblock()
|
||||
PopochiuUtils.cursor.unblock()
|
||||
else:
|
||||
# Idempotent call, no need to check the mode
|
||||
PopochiuUtils.e.gui.popups_stack.back().show()
|
||||
|
||||
_close()
|
||||
|
||||
|
||||
## Called when the OK button is pressed. It closes the popup afterwards.
|
||||
func on_ok_pressed() -> void:
|
||||
_on_ok()
|
||||
close()
|
||||
|
||||
|
||||
## Called when the CANCEL button is pressed. It closes the popup afterwards.
|
||||
func on_cancel_pressed() -> void:
|
||||
_on_cancel()
|
||||
close()
|
||||
|
||||
|
||||
## Called when the X (top-right corner) button is pressed. It closes the popup
|
||||
## afterwards.
|
||||
func on_close_pressed() -> void:
|
||||
_on_cancel()
|
||||
close()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_title(value: String) -> void:
|
||||
title = value
|
||||
|
||||
if is_instance_valid(lbl_title):
|
||||
lbl_title.text = title
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
## Checks if the overlay area of the popup was clicked in order to close it.
|
||||
func _check_click(event: InputEvent) -> void:
|
||||
if (
|
||||
PopochiuUtils.get_click_or_touch_index(event) == MOUSE_BUTTON_LEFT
|
||||
and closes_by_clicking_out
|
||||
):
|
||||
_on_cancel()
|
||||
close()
|
||||
|
||||
|
||||
func _on_popup_requested(popup_script_name: StringName) -> void:
|
||||
if popup_script_name == script_name:
|
||||
open()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bmlrdxcv748wb
|
|
@ -0,0 +1,69 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://c51xplyeuk787"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_4hfpy"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup.gd" id="2_yuv82"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_5rk5h"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="3_co4j2"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_4odj8"]
|
||||
|
||||
[node name="PopochiuPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_4hfpy")
|
||||
script = ExtResource("2_yuv82")
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("3_co4j2")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Title"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_5rk5h")
|
||||
texture_pressed = ExtResource("4_4odj8")
|
||||
texture_hover = ExtResource("4_4odj8")
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "ok"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "cancel"
|
|
@ -0,0 +1,12 @@
|
|||
[gd_resource type="StyleBoxFlat" format=3 uid="uid://dbajakvkltfaj"]
|
||||
|
||||
[resource]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 2.0
|
||||
bg_color = Color(0, 0, 0, 0.705882)
|
||||
expand_margin_left = 2.0
|
||||
expand_margin_top = 2.0
|
||||
expand_margin_right = 2.0
|
||||
expand_margin_bottom = 2.0
|
|
@ -0,0 +1,26 @@
|
|||
@tool
|
||||
extends PopochiuPopup
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the popup is opened. At this point it is not visible yet.
|
||||
func _open() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the popup is closed. The node hides after calling this method.
|
||||
func _close() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when OK is pressed.
|
||||
func _on_ok() -> void:
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
## Called when CANCEL or X (top-right corner) are pressed.
|
||||
func _on_cancel() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bqvdywnh28ces
|
|
@ -0,0 +1,84 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://bnjo044fkdcq7"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_3nwvu"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/quit_popup/quit_popup.gd" id="2_nkwwk"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="3_828u5"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_r2fp8"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_mra7q"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_il3mr"]
|
||||
|
||||
[node name="QuitPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_3nwvu")
|
||||
script = ExtResource("2_nkwwk")
|
||||
script_name = &"QuitPopup"
|
||||
title = "Quit game"
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_il3mr")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
custom_minimum_size = Vector2(256, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("3_828u5")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Quit game"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_r2fp8")
|
||||
texture_pressed = ExtResource("4_mra7q")
|
||||
texture_hover = ExtResource("4_mra7q")
|
||||
|
||||
[node name="Question" type="Label" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Are you sure you want to quit?"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "OK"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Cancel
|
||||
"
|
|
@ -0,0 +1,134 @@
|
|||
@tool
|
||||
extends PopochiuPopup
|
||||
|
||||
signal slot_selected
|
||||
|
||||
const SELECTION_COLOR := Color("edf171")
|
||||
const OVERWRITE_COLOR := Color("c46c71")
|
||||
|
||||
var _current_slot: Button = null
|
||||
var _slot_name := ""
|
||||
var _prev_text := ""
|
||||
var _slot := 0
|
||||
|
||||
@onready var slots: VBoxContainer = %Slots
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
super()
|
||||
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
btn_ok.disabled = true
|
||||
|
||||
var saves: Dictionary = PopochiuUtils.e.get_saves_descriptions()
|
||||
|
||||
for btn: Button in slots.get_children():
|
||||
btn.set_meta("has_save", false)
|
||||
|
||||
if saves.has(btn.get_index() + 1):
|
||||
btn.text = saves[btn.get_index() + 1]
|
||||
btn.set_meta("has_save", true)
|
||||
else:
|
||||
btn.disabled = true
|
||||
|
||||
btn.pressed.connect(_select_slot.bind(btn))
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
func _open() -> void:
|
||||
btn_ok.disabled = true
|
||||
_slot = 0
|
||||
|
||||
if _current_slot:
|
||||
_current_slot.text = _prev_text
|
||||
_current_slot.button_pressed = false
|
||||
|
||||
_current_slot = null
|
||||
_prev_text = ""
|
||||
|
||||
|
||||
func _close() -> void:
|
||||
if not _slot: return
|
||||
|
||||
slot_selected.emit()
|
||||
|
||||
if _slot_name:
|
||||
PopochiuUtils.e.save_game(_slot, _slot_name)
|
||||
else:
|
||||
PopochiuUtils.e.load_game(_slot)
|
||||
|
||||
|
||||
func _on_ok() -> void:
|
||||
_slot = _current_slot.get_index() + 1
|
||||
|
||||
if _slot_name:
|
||||
_prev_text = _current_slot.text
|
||||
_current_slot.set_meta("has_save", true)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
func open_save() -> void:
|
||||
_show_save()
|
||||
|
||||
|
||||
func open_load() -> void:
|
||||
_show_load()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _show_save(slot_text := "") -> void:
|
||||
lbl_title.text = "Choose a slot to save the game"
|
||||
_slot_name = slot_text
|
||||
|
||||
if _slot_name.is_empty():
|
||||
_slot_name = _format_date(Time.get_datetime_dict_from_system())
|
||||
|
||||
for btn in slots.get_children():
|
||||
btn.disabled = false
|
||||
|
||||
open()
|
||||
|
||||
|
||||
func _show_load() -> void:
|
||||
lbl_title.text = "Choose the slot to load"
|
||||
_slot_name = ""
|
||||
|
||||
for btn in slots.get_children():
|
||||
btn.disabled = !(btn as Button).get_meta("has_save")
|
||||
|
||||
open()
|
||||
|
||||
|
||||
func _select_slot(btn: Button) -> void:
|
||||
if _slot_name:
|
||||
if _current_slot:
|
||||
_current_slot.text = _prev_text
|
||||
_current_slot.button_pressed = false
|
||||
|
||||
_current_slot = btn
|
||||
_prev_text = _current_slot.text
|
||||
_current_slot.text = _slot_name
|
||||
else:
|
||||
if _current_slot:
|
||||
_current_slot.button_pressed = false
|
||||
|
||||
_current_slot = btn
|
||||
_prev_text = _current_slot.text
|
||||
|
||||
btn_ok.disabled = false
|
||||
|
||||
|
||||
func _format_date(date: Dictionary) -> String:
|
||||
return "%d/%02d/%02d %02d:%02d:%02d" % [
|
||||
date.year, date.month, date.day, date.hour, date.minute, date.second
|
||||
]
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bmwrmsc6w6ss6
|
|
@ -0,0 +1,100 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://cndputybyj57n"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_llqb8"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/save_and_load_popup/save_and_load_popup.gd" id="2_m1ot6"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="3_inruf"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_n4oyj"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_jqolx"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_yg86r"]
|
||||
|
||||
[node name="SaveAndLoadPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_llqb8")
|
||||
script = ExtResource("2_m1ot6")
|
||||
script_name = &"SaveAndLoadPopup"
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_yg86r")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
custom_minimum_size = Vector2(192, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("3_inruf")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Choose a slot to..."
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_n4oyj")
|
||||
texture_pressed = ExtResource("4_jqolx")
|
||||
texture_hover = ExtResource("4_jqolx")
|
||||
|
||||
[node name="Slots" type="VBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
alignment = 1
|
||||
|
||||
[node name="BtnSlot1" type="Button" parent="Overlay/PanelContainer/VBoxContainer/Slots"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "slot 1"
|
||||
|
||||
[node name="BtnSlot2" type="Button" parent="Overlay/PanelContainer/VBoxContainer/Slots"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "slot 2"
|
||||
|
||||
[node name="BtnSlot3" type="Button" parent="Overlay/PanelContainer/VBoxContainer/Slots"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "slot 3"
|
||||
|
||||
[node name="BtnSlot4" type="Button" parent="Overlay/PanelContainer/VBoxContainer/Slots"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "slot 4"
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "OK"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
|
@ -0,0 +1,16 @@
|
|||
@tool
|
||||
extends PopochiuPopup
|
||||
|
||||
@onready var sound_volumes: GridContainer = %SoundVolumes
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
func _open() -> void:
|
||||
sound_volumes.update_sliders()
|
||||
|
||||
|
||||
func _on_cancel() -> void:
|
||||
sound_volumes.restore_last_volumes()
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://chx7nkjk6cwvt
|
|
@ -0,0 +1,80 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://dwxm2p1iyhpx6"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_rwmoe"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/sound_settings_popup/sound_settings_popup.gd" id="2_qt85i"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_knqsp"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="3_nqnfj"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_v7y6x"]
|
||||
[ext_resource type="PackedScene" uid="uid://drx0r8w00ivck" path="res://addons/popochiu/engine/objects/gui/components/sound_volumes/sound_volumes.tscn" id="5_lh576"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6mo8y"]
|
||||
|
||||
[node name="SoundSettingsPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_rwmoe")
|
||||
script = ExtResource("2_qt85i")
|
||||
script_name = &"SoundSettingsPopup"
|
||||
title = "Sound Settings"
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_6mo8y")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("3_nqnfj")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Sound Settings"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_knqsp")
|
||||
texture_pressed = ExtResource("4_v7y6x")
|
||||
texture_hover = ExtResource("4_v7y6x")
|
||||
|
||||
[node name="SoundVolumes" parent="Overlay/PanelContainer/VBoxContainer" instance=ExtResource("5_lh576")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "ok"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "cancel"
|
|
@ -0,0 +1,40 @@
|
|||
@tool
|
||||
extends PopochiuPopup
|
||||
|
||||
@onready var text_speed: HSlider = %TextSpeed
|
||||
@onready var dialog_style: OptionButton = %DialogStyle
|
||||
@onready var continue_mode: CheckButton = %ContinueMode
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
super()
|
||||
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
text_speed.value = 0.1 - PopochiuUtils.e.text_speed
|
||||
dialog_style.selected = PopochiuUtils.e.settings.dialog_style
|
||||
continue_mode.button_pressed = PopochiuUtils.e.settings.auto_continue_text
|
||||
|
||||
# Connect to child signals
|
||||
text_speed.value_changed.connect(_on_text_speed_changed)
|
||||
dialog_style.item_selected.connect(_on_dialog_style_selected)
|
||||
continue_mode.toggled.connect(_on_continue_mode_toggled)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _on_text_speed_changed(value: float) -> void:
|
||||
PopochiuUtils.e.text_speed = 0.1 - value
|
||||
|
||||
|
||||
func _on_dialog_style_selected(idx: int) -> void:
|
||||
PopochiuUtils.e.current_dialog_style = dialog_style.get_item_id(idx)
|
||||
|
||||
|
||||
func _on_continue_mode_toggled(toggled_on: bool) -> void:
|
||||
PopochiuUtils.e.settings.auto_continue_text = toggled_on
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://s6343c1jdmgy
|
|
@ -0,0 +1,151 @@
|
|||
[gd_scene load_steps=13 format=3 uid="uid://de68lx1xqv7fb"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dpequqav4rjaf" path="res://addons/popochiu/engine/objects/gui/resources/base_gui_theme.tres" id="1_7sq5d"]
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/gui/components/popups/text_settings_popup/text_settings_popup.gd" id="2_nmcxa"]
|
||||
[ext_resource type="StyleBox" uid="uid://dbajakvkltfaj" path="res://addons/popochiu/engine/objects/gui/components/popups/popochiu_popup_panel_container.tres" id="3_coq7y"]
|
||||
[ext_resource type="Texture2D" uid="uid://cmxrewai8t2lm" path="res://addons/popochiu/engine/objects/gui/resources/images/close.png" id="3_rckox"]
|
||||
[ext_resource type="Texture2D" uid="uid://p32i25numi5e" path="res://addons/popochiu/engine/objects/gui/resources/images/close_highlight.png" id="4_gfs8i"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bh5os"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_b5jxw"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rinqp"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_vprmd"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_372y7"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_t5skw"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5015x"]
|
||||
|
||||
[node name="TextSettingsPopup" type="Control" groups=["popochiu_gui_popup"]]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_7sq5d")
|
||||
script = ExtResource("2_nmcxa")
|
||||
script_name = &"SierraTextPopup"
|
||||
title = "Text options"
|
||||
|
||||
[node name="Overlay" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_bh5os")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Overlay"]
|
||||
custom_minimum_size = Vector2(160, 140)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = ExtResource("3_coq7y")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Overlay/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Text options"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Close" type="TextureButton" parent="Overlay/PanelContainer/VBoxContainer/HeaderContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
texture_normal = ExtResource("3_rckox")
|
||||
texture_pressed = ExtResource("4_gfs8i")
|
||||
texture_hover = ExtResource("4_gfs8i")
|
||||
|
||||
[node name="BodyContainer" type="VBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TextSpeedContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer/TextSpeedContainer"]
|
||||
layout_mode = 2
|
||||
text = "Typing speed"
|
||||
|
||||
[node name="TextSpeed" type="HSlider" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer/TextSpeedContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
max_value = 0.1
|
||||
step = 0.01
|
||||
|
||||
[node name="DialogStyleContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer/DialogStyleContainer"]
|
||||
layout_mode = 2
|
||||
text = "Style"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="DialogStyle" type="OptionButton" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer/DialogStyleContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
selected = 0
|
||||
item_count = 3
|
||||
popup/item_0/text = "Above character"
|
||||
popup/item_1/text = "Portrait"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Caption"
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="ContinueModeContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer/ContinueModeContainer"]
|
||||
layout_mode = 2
|
||||
text = "Autoadvance"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ContinueMode" type="CheckButton" parent="Overlay/PanelContainer/VBoxContainer/BodyContainer/ContinueModeContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 10
|
||||
size_flags_vertical = 0
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_b5jxw")
|
||||
theme_override_styles/disabled = SubResource("StyleBoxEmpty_rinqp")
|
||||
theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_vprmd")
|
||||
theme_override_styles/hover = SubResource("StyleBoxEmpty_372y7")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxEmpty_t5skw")
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_5015x")
|
||||
icon_alignment = 2
|
||||
|
||||
[node name="FooterContainer" type="HBoxContainer" parent="Overlay/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "OK"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Overlay/PanelContainer/VBoxContainer/FooterContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Back"
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue