Update Hint system
This commit is contained in:
parent
fc5afc8ca5
commit
d230674e02
9
data/hints/cancel_throw.tres
Normal file
9
data/hints/cancel_throw.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[gd_resource type="Resource" script_class="Hint" format=3 uid="uid://bow3d0e0fcto7"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="1_3q5e6"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_3q5e6")
|
||||
action = &"player_throw_cancel"
|
||||
label = "Annuler le lancer"
|
||||
metadata/_custom_type_script = "uid://b1rvj44fyaea4"
|
||||
10
data/hints/dismount.tres
Normal file
10
data/hints/dismount.tres
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[gd_resource type="Resource" script_class="Hint" format=3 uid="uid://cfl2q6f3sjy1v"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="1_dm3jb"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_dm3jb")
|
||||
action = &"player_dismount"
|
||||
label = "Descendre"
|
||||
priority = 10
|
||||
metadata/_custom_type_script = "uid://b1rvj44fyaea4"
|
||||
9
data/hints/jump.tres
Normal file
9
data/hints/jump.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[gd_resource type="Resource" script_class="Hint" format=3 uid="uid://cdx4qndy5xwto"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="1_ch8ap"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_ch8ap")
|
||||
action = &"player_jump"
|
||||
label = "Sauter"
|
||||
metadata/_custom_type_script = "uid://b1rvj44fyaea4"
|
||||
10
data/hints/mount.tres
Normal file
10
data/hints/mount.tres
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[gd_resource type="Resource" script_class="Hint" format=3 uid="uid://c18ntr4wb56m2"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="1_aaq06"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_aaq06")
|
||||
action = &"player_interact"
|
||||
label = "Grimper"
|
||||
priority = 10
|
||||
metadata/_custom_type_script = "uid://b1rvj44fyaea4"
|
||||
9
data/hints/switch_target.tres
Normal file
9
data/hints/switch_target.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[gd_resource type="Resource" script_class="Hint" format=3 uid="uid://c4fqcdrf6il7v"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="1_gggbv"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_gggbv")
|
||||
action = &"player_aim_switch_target"
|
||||
label = "Changer de cible"
|
||||
metadata/_custom_type_script = "uid://b1rvj44fyaea4"
|
||||
10
data/hints/talk.tres
Normal file
10
data/hints/talk.tres
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[gd_resource type="Resource" script_class="Hint" format=3 uid="uid://4dptmdqsnh0c"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="1_j40kh"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_j40kh")
|
||||
action = &"player_interact"
|
||||
label = "Parler"
|
||||
priority = 10
|
||||
metadata/_custom_type_script = "uid://b1rvj44fyaea4"
|
||||
41
game/interaction/interactable.gd
Normal file
41
game/interaction/interactable.gd
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
class_name Interactable
|
||||
extends Area3D
|
||||
|
||||
signal interacted(player : PlayerCharacter)
|
||||
|
||||
@export var hint : Hint #talk.tres, etc.
|
||||
@export var enabled : bool = true:
|
||||
set(value):
|
||||
enabled = value
|
||||
_refresh()
|
||||
|
||||
var _player : PlayerCharacter = null
|
||||
|
||||
func _ready() -> void:
|
||||
assert(hint != null, "Interactable : aucun Hint assigne sur %s" % get_path())
|
||||
body_entered.connect(_on_body_entered)
|
||||
body_exited.connect(_on_body_exited)
|
||||
_refresh()
|
||||
|
||||
func _on_body_entered(body : Node3D) -> void:
|
||||
if !(body is PlayerCharacter): return
|
||||
_player = body
|
||||
_refresh()
|
||||
|
||||
func _on_body_exited(body : Node3D) -> void:
|
||||
if body != _player: return
|
||||
_player = null
|
||||
_refresh()
|
||||
|
||||
func _refresh() -> void:
|
||||
var active : bool = enabled and _player != null
|
||||
set_physics_process(active)
|
||||
if active: Hints.show_hints(self, [hint])
|
||||
else: Hints.clear_hints(self)
|
||||
|
||||
func _physics_process(_delta : float) -> void:
|
||||
if Input.is_action_just_pressed(hint.action):
|
||||
interacted.emit(_player)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Hints.clear_hints(self)
|
||||
1
game/interaction/interactable.gd.uid
Normal file
1
game/interaction/interactable.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b7favvk6hxvru
|
||||
|
|
@ -12,17 +12,23 @@ signal mounted(sent : SentPokemon)
|
|||
signal dismounted(sent : SentPokemon)
|
||||
signal candidate_changed(sent : SentPokemon) #pokemon montable a portee (null = aucun) : pour afficher "A : Monter" dans le HUD
|
||||
|
||||
@export var mount_action : StringName = &"player_interact" #A (manette) / E (clavier) : cree automatiquement s'il n'existe pas
|
||||
@export var dismount_action : StringName = &"player_dismount" #Y (manette) / F (clavier) : cree automatiquement s'il n'existe pas
|
||||
#@export var mount_action : StringName = &"player_interact" #A (manette) / E (clavier) : cree automatiquement s'il n'existe pas
|
||||
#@export var dismount_action : StringName = &"player_dismount" #Y (manette) / F (clavier) : cree automatiquement s'il n'existe pas
|
||||
@export var mount_range : float = 2.5 #distance horizontale max entre le joueur et le pokemon
|
||||
@export var max_height_diff : float = 2.0
|
||||
@export var blocked_states : PackedStringArray = ["DodgeState", "CrouchState"] #etats refuses pendant la monte
|
||||
@export var swallow_jump_on_mount : bool = true #si A sert aussi a sauter : l'appui qui monte ne declenche pas de saut
|
||||
@export var camera_follow_speed : float = 8.0 #vitesse a laquelle la camera monte a hauteur de selle
|
||||
|
||||
@export_group("Hints HUD")
|
||||
@export var mount_hint_text : String = "Grimper"
|
||||
@export var hint_priority : int = 10
|
||||
@export_group("Hints")
|
||||
@export var mount_hint : Hint #mount.tres
|
||||
@export var dismount_hint : Hint #dismount.tres
|
||||
@export var jump_hint : Hint #jump.tres
|
||||
|
||||
var mount_action : StringName:
|
||||
get: return mount_hint.action
|
||||
var dismount_action : StringName:
|
||||
get: return dismount_hint.action
|
||||
|
||||
const GROUNDED_STATES : Array[String] = ["Idle", "Walk", "Run"] #monter / descendre n'est possible que depuis ces etats
|
||||
|
||||
|
|
@ -55,8 +61,18 @@ func _ready() -> void:
|
|||
key.physical_keycode = KEY_F
|
||||
InputMap.action_add_event(dismount_action, key))
|
||||
|
||||
func _refresh_hints() -> void:
|
||||
#une seule source de verite pour les hints de la monture
|
||||
var list : Array[Hint] = []
|
||||
if mount != null:
|
||||
if _can_switch_now(): list.append(dismount_hint) #pas de descente en plein vol
|
||||
list.append(jump_hint)
|
||||
elif candidate != null:
|
||||
list.append(mount_hint)
|
||||
Hints.show_hints(self, list)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Hints.clear_context(self)
|
||||
Hints.clear_hints(self)
|
||||
|
||||
func _ensure_action(action : StringName, add_events : Callable) -> void:
|
||||
if InputMap.has_action(action): return
|
||||
|
|
@ -69,8 +85,7 @@ func is_mounted() -> bool:
|
|||
func _physics_process(_delta : float) -> void:
|
||||
if mount != null:
|
||||
var can_dismount : bool = _can_switch_now()
|
||||
if can_dismount: Hints.set_context(self, dismount_action, "", hint_priority)
|
||||
else: Hints.clear_context(self)
|
||||
_refresh_hints()
|
||||
if Input.is_action_just_pressed(dismount_action) and can_dismount:
|
||||
dismount()
|
||||
return
|
||||
|
|
@ -133,8 +148,7 @@ func _set_candidate(s : SentPokemon) -> void:
|
|||
if s == candidate: return
|
||||
candidate = s
|
||||
candidate_changed.emit(s)
|
||||
if s != null: Hints.set_context(self, mount_action, mount_hint_text, hint_priority)
|
||||
else: Hints.clear_context(self)
|
||||
_refresh_hints()
|
||||
|
||||
#---------- monter / descendre ----------
|
||||
|
||||
|
|
@ -145,7 +159,7 @@ func mount_up(s : SentPokemon) -> void:
|
|||
s.rider = player
|
||||
s.recalled.connect(_on_mount_recalled)
|
||||
_set_candidate(null)
|
||||
Hints.set_context(self, dismount_action, "", hint_priority)
|
||||
_refresh_hints()
|
||||
|
||||
player.is_mounted = true
|
||||
player.enter_mount_stats(profile.mount_speed, profile.mount_accel, profile.mount_deccel,
|
||||
|
|
@ -161,10 +175,10 @@ func dismount() -> void:
|
|||
var s : SentPokemon = mount
|
||||
mount = null
|
||||
profile = null
|
||||
_refresh_hints()
|
||||
|
||||
player.is_mounted = false
|
||||
player.exit_mount_stats()
|
||||
Hints.clear_context(self)
|
||||
_release_blocked_states_next_frame()
|
||||
|
||||
visual_root.position -= _seat_applied
|
||||
|
|
|
|||
|
|
@ -7,14 +7,20 @@
|
|||
[ext_resource type="Script" uid="uid://dy4wrpvpep1jq" path="res://game/player/states/state_machine.gd" id="3_o4naa"]
|
||||
[ext_resource type="Script" uid="uid://bf6hjwfa74ttj" path="res://game/player/states/idle_state.gd" id="4_38lxl"]
|
||||
[ext_resource type="Script" uid="uid://2agliol5dulc" path="res://game/player/states/walk_state.gd" id="4_lg056"]
|
||||
[ext_resource type="Script" uid="uid://b1rvj44fyaea4" path="res://game/ui/hints/hint.gd" id="6_7qhxe"]
|
||||
[ext_resource type="Script" uid="uid://dr5p8ygj25w6x" path="res://game/player/states/jump_state.gd" id="6_lwgph"]
|
||||
[ext_resource type="Resource" uid="uid://cdx4qndy5xwto" path="res://data/hints/jump.tres" id="7_56w3b"]
|
||||
[ext_resource type="Script" uid="uid://dn5idrvu68k83" path="res://game/player/states/aim_state.gd" id="7_kxr4l"]
|
||||
[ext_resource type="Script" uid="uid://dpcfffs3vt5co" path="res://game/player/states/run_state.gd" id="8_j4mf1"]
|
||||
[ext_resource type="Script" uid="uid://c3jh22p4w3xco" path="res://game/player/states/throw_state.gd" id="8_k0r6x"]
|
||||
[ext_resource type="Script" uid="uid://bwojkp7yrwswt" path="res://game/player/states/dodge_state.gd" id="9_6abew"]
|
||||
[ext_resource type="Script" uid="uid://d0577tj0c23bu" path="res://game/player/states/crouch_state.gd" id="10_mi63v"]
|
||||
[ext_resource type="Resource" uid="uid://c4fqcdrf6il7v" path="res://data/hints/switch_target.tres" id="11_lapey"]
|
||||
[ext_resource type="Resource" uid="uid://bow3d0e0fcto7" path="res://data/hints/cancel_throw.tres" id="12_iqmup"]
|
||||
[ext_resource type="Script" uid="uid://c820hnwuwpmlg" path="res://game/player/input/recall_input.gd" id="13_siyau"]
|
||||
[ext_resource type="Script" uid="uid://c4vkkdl0an244" path="res://game/mount/mount_rider.gd" id="14_dkijf"]
|
||||
[ext_resource type="Resource" uid="uid://c18ntr4wb56m2" path="res://data/hints/mount.tres" id="20_lapey"]
|
||||
[ext_resource type="Resource" uid="uid://cfl2q6f3sjy1v" path="res://data/hints/dismount.tres" id="21_iqmup"]
|
||||
|
||||
[sub_resource type="Curve" id="Curve_kxr4l"]
|
||||
_limits = [0.0, 10.0, 0.0, 1.0]
|
||||
|
|
@ -81,28 +87,32 @@ initial_state = NodePath("IdleState")
|
|||
|
||||
[node name="IdleState" type="Node" parent="StateMachine" unique_id=1417869633]
|
||||
script = ExtResource("4_38lxl")
|
||||
hints = Array[StringName]([&"player_jump"])
|
||||
hints = Array[ExtResource("6_7qhxe")]([ExtResource("7_56w3b")])
|
||||
|
||||
[node name="WalkState" type="Node" parent="StateMachine" unique_id=1524475809]
|
||||
script = ExtResource("4_lg056")
|
||||
hints = Array[StringName]([&"player_jump"])
|
||||
hints = Array[ExtResource("6_7qhxe")]([ExtResource("7_56w3b")])
|
||||
|
||||
[node name="JumpState" type="Node" parent="StateMachine" unique_id=711160985]
|
||||
script = ExtResource("6_lwgph")
|
||||
hints = Array[StringName]([&"player_jump"])
|
||||
hints = Array[ExtResource("6_7qhxe")]([ExtResource("7_56w3b")])
|
||||
|
||||
[node name="AimState" type="Node" parent="StateMachine" unique_id=296560188]
|
||||
script = ExtResource("7_kxr4l")
|
||||
switch_hint = ExtResource("11_lapey")
|
||||
cancel_hint = ExtResource("12_iqmup")
|
||||
|
||||
[node name="ThrowState" type="Node" parent="StateMachine" unique_id=1159570489]
|
||||
script = ExtResource("8_k0r6x")
|
||||
cancel_hint = ExtResource("12_iqmup")
|
||||
|
||||
[node name="RunState" type="Node" parent="StateMachine" unique_id=1345421775]
|
||||
script = ExtResource("8_j4mf1")
|
||||
hints = Array[StringName]([&"player_jump"])
|
||||
hints = Array[ExtResource("6_7qhxe")]([ExtResource("7_56w3b")])
|
||||
|
||||
[node name="DodgeState" type="Node" parent="StateMachine" unique_id=232179400]
|
||||
script = ExtResource("9_6abew")
|
||||
keep_previous_hints = true
|
||||
|
||||
[node name="CrouchState" type="Node" parent="StateMachine" unique_id=381419367]
|
||||
script = ExtResource("10_mi63v")
|
||||
|
|
@ -115,3 +125,6 @@ script = ExtResource("13_siyau")
|
|||
|
||||
[node name="MountRider" type="Node" parent="." unique_id=639128028]
|
||||
script = ExtResource("14_dkijf")
|
||||
mount_hint = ExtResource("20_lapey")
|
||||
dismount_hint = ExtResource("21_iqmup")
|
||||
jump_hint = ExtResource("7_56w3b")
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ var state_name : String = "Aim"
|
|||
var play_char : CharacterBody3D
|
||||
|
||||
@export var aim_action : StringName = &"player_aim_camera"
|
||||
@export var switch_target_action : StringName = &"player_aim_switch_target"
|
||||
#@export var switch_target_action : StringName = &"player_aim_switch_target"
|
||||
@export var unlock_action : StringName = &"player_aim_unlock"
|
||||
@export var throw_action : StringName = &"player_throw" #RT / clic gauche (cree automatiquement s'il n'existe pas)
|
||||
@export var cancel_action : StringName = &"player_throw_cancel" #annule la charge d'un lancer (cree par ThrowState)
|
||||
#@export var cancel_action : StringName = &"player_throw_cancel" #annule la charge d'un lancer (cree par ThrowState)
|
||||
|
||||
@export_group("Targeting")
|
||||
@export var throw_range : float = 20.0
|
||||
|
|
@ -24,6 +24,15 @@ var play_char : CharacterBody3D
|
|||
@export var aim_move_speed : float = 3.0
|
||||
@export var aim_move_accel : float = 8.0
|
||||
|
||||
@export_group("Hints")
|
||||
@export var switch_hint : Hint #switch_target.tres
|
||||
@export var cancel_hint : Hint #cancel_throw.tres (la meme ressource que ThrowState)
|
||||
|
||||
var switch_target_action : StringName:
|
||||
get: return switch_hint.action
|
||||
var cancel_action : StringName:
|
||||
get: return cancel_hint.action
|
||||
|
||||
var auto_lock_blocked : bool = false #true apres un deverrouillage manuel, tant que la cible est encore au centre
|
||||
var dodging_from_aim : bool = false #true quand on quitte Aim pour rouler, pour ne pas relacher la cible verrouillee
|
||||
var came_from_crouch : bool = false #true si Aim a ete declenche depuis CrouchState, pour y retourner en sortant
|
||||
|
|
@ -239,7 +248,7 @@ func release_target() -> void:
|
|||
play_char.aim_target = null
|
||||
play_char.cam_holder.set_lock_target(null)
|
||||
|
||||
func get_hints() -> Array:
|
||||
var list : Array[StringName] = [switch_target_action]
|
||||
if charging_throw: list.append(cancel_action)
|
||||
func get_hints() -> Array[Hint]:
|
||||
var list : Array[Hint] = [switch_hint]
|
||||
if charging_throw: list.append(cancel_hint)
|
||||
return list
|
||||
|
|
|
|||
|
|
@ -14,6 +14,3 @@ func enter(play_char_ref : CharacterBody3D) -> void:
|
|||
play_char.move_deccel = profile.mount_deccel
|
||||
# instancier profile.mount_scene_override ou profile.species.model_scene,
|
||||
# le positionner, y attacher play_char selon seat_offset...
|
||||
|
||||
func get_hints() -> Array:
|
||||
return [{"action": &"player_dismount", "text": "Descendre"}] # remplace par le nom réel de ton action
|
||||
|
|
|
|||
|
|
@ -3,25 +3,22 @@ extends Node
|
|||
class_name State
|
||||
|
||||
signal transitioned #it's normal if this signal isn't used in this script, because it's purpose is to be used in the scripts that extends from the State class
|
||||
signal hints_changed #à émettre quand get_hints() change pendant que l'état est actif
|
||||
signal hints_changed #a emettre quand get_hints() change alors que l'etat est actif
|
||||
|
||||
@export var hints : Array[StringName] = [] #hints simples, réglables dans l'inspecteur
|
||||
@export var hints : Array[Hint] = [] #cas simple : se regle dans l'inspecteur
|
||||
@export var keep_previous_hints : bool = false #etats transitoires (Dodge...) : on ne touche pas aux hints affiches
|
||||
|
||||
func enter(_char_reference : CharacterBody3D) -> void:
|
||||
#enter state
|
||||
pass
|
||||
|
||||
func exit() -> void:
|
||||
#exit state
|
||||
pass
|
||||
|
||||
func update(_delta : float) -> void:
|
||||
#process update
|
||||
pass
|
||||
|
||||
func physics_update(_delta : float) -> void:
|
||||
#physics_process update
|
||||
pass
|
||||
|
||||
func get_hints() -> Array:
|
||||
return hints #à surcharger seulement si les hints dépendent d'une condition
|
||||
func get_hints() -> Array[Hint]:
|
||||
return hints #a surcharger seulement quand les hints dependent d'une condition
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func _ready() -> void:
|
|||
if child is State:
|
||||
states[child.name.to_lower()] = child
|
||||
child.transitioned.connect(on_state_child_transition)
|
||||
child.hints_changed.connect(_on_hints_changed.bind(child))
|
||||
child.hints_changed.connect(_on_state_hints_changed.bind(child))
|
||||
|
||||
#if initial state, transition to it
|
||||
if initial_state:
|
||||
|
|
@ -81,11 +81,12 @@ func set_state_blocked(state_name : String, blocked : bool) -> void:
|
|||
if blocked: blocked_states[state_name.to_lower()] = true
|
||||
else: blocked_states.erase(state_name.to_lower())
|
||||
|
||||
func _on_hints_changed(state : State) -> void:
|
||||
func _on_state_hints_changed(state : State) -> void:
|
||||
if state == curr_state: publish_hints()
|
||||
|
||||
func publish_hints() -> void:
|
||||
if !curr_state: return
|
||||
var actions = curr_state.get_hints()
|
||||
if actions != null: Hints.set_state_hints(actions) #null = on garde les hints précédents (si tu as gardé cette option pour Dodge)
|
||||
|
||||
if curr_state and !curr_state.keep_previous_hints:
|
||||
Hints.show_hints(self, curr_state.get_hints())
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Hints.clear_hints(self)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ var play_char : CharacterBody3D
|
|||
|
||||
@export var aim_action : StringName = &"player_aim_camera"
|
||||
@export var throw_action : StringName = &"player_throw" #RT / clic gauche : maintenir pour charger, relacher pour lancer
|
||||
@export var cancel_action : StringName = &"player_throw_cancel" #B / clic droit (cree automatiquement s'il n'existe pas)
|
||||
#@export var cancel_action : StringName = &"player_throw_cancel" #B / clic droit (cree automatiquement s'il n'existe pas)
|
||||
|
||||
@export var cancel_hint : Hint #cancel_throw.tres
|
||||
var cancel_action : StringName: #l'action vient du Hint : une seule definition
|
||||
get: return cancel_hint.action
|
||||
|
||||
@export_group("Charge")
|
||||
@export var charge_move_speed : float = 3.0 #vitesse de deplacement pendant que RT est maintenue
|
||||
|
|
@ -211,6 +215,7 @@ func update_reticle() -> void:
|
|||
|
||||
func throw_ball() -> void:
|
||||
_thrown = true
|
||||
hints_changed.emit()
|
||||
AimReticle.hide_instance()
|
||||
|
||||
var from : Vector3 = play_char.global_position + Vector3.UP * hand_height
|
||||
|
|
@ -295,7 +300,7 @@ func _release_aim() -> void:
|
|||
play_char.cam_holder.set_lock_target(null)
|
||||
play_char.cam_holder.cam_aimed = false
|
||||
|
||||
func get_hints() -> Array:
|
||||
var list : Array[StringName] = []
|
||||
if !_thrown: list.append(cancel_action) #plus d'annulation possible une fois la balle partie
|
||||
func get_hints() -> Array[Hint]:
|
||||
var list : Array[Hint] = []
|
||||
if !_thrown: list.append(cancel_hint) #plus d'annulation possible une fois la balle partie
|
||||
return list
|
||||
|
|
|
|||
6
game/ui/hints/hint.gd
Normal file
6
game/ui/hints/hint.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class_name Hint
|
||||
extends Resource
|
||||
|
||||
@export var action : StringName #action de l'Input Map : sert à la fois à l'icône et au code d'input
|
||||
@export var label : String #libellé affiché (passe par tr() dans le HUD)
|
||||
@export var priority : int = 0 #le plus haut passe en premier ; à action égale, le plus prioritaire gagne
|
||||
1
game/ui/hints/hint.gd.uid
Normal file
1
game/ui/hints/hint.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b1rvj44fyaea4
|
||||
11
game/ui/hints/hint_row.gd
Normal file
11
game/ui/hints/hint_row.gd
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class_name HintRow
|
||||
extends HBoxContainer
|
||||
|
||||
@onready var icon : TextureRect = %Icon
|
||||
@onready var label : Label = %Label
|
||||
|
||||
func setup(hint : Hint) -> void:
|
||||
var tex := ControllerIconTexture.new()
|
||||
tex.path = String(hint.action)
|
||||
icon.texture = tex
|
||||
label.text = tr(hint.label)
|
||||
1
game/ui/hints/hint_row.gd.uid
Normal file
1
game/ui/hints/hint_row.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bul0y1cx8h6m3
|
||||
18
game/ui/hints/hint_row.tscn
Normal file
18
game/ui/hints/hint_row.tscn
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[gd_scene format=3 uid="uid://dfxmt1caqfh1o"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bul0y1cx8h6m3" path="res://game/ui/hints/hint_row.gd" id="1_ra04h"]
|
||||
|
||||
[node name="HintRow" type="HBoxContainer" unique_id=342863396]
|
||||
theme_override_constants/separation = 6
|
||||
script = ExtResource("1_ra04h")
|
||||
|
||||
[node name="Icon" type="TextureRect" parent="." unique_id=273257005]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(40, 40)
|
||||
layout_mode = 2
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Label" type="Label" parent="." unique_id=1767574240]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
|
@ -2,57 +2,40 @@ extends Node
|
|||
|
||||
signal changed
|
||||
|
||||
#catalogue unique : action -> libellé affiché
|
||||
const LABELS : Dictionary = {
|
||||
&"player_jump": "Sauter",
|
||||
&"player_dismount": "Descendre",
|
||||
&"player_aim_switch_target": "Changer de cible",
|
||||
&"player_aim_unlock": "Libérer la cible",
|
||||
&"player_throw_cancel": "Annuler le lancer",
|
||||
}
|
||||
var _layers : Dictionary = {} #source (objet ou StringName) -> Array[Hint]
|
||||
|
||||
var _state_actions : Array = [] # [action, ...]
|
||||
var _context_hints : Dictionary = {} # clé (objet ou StringName) -> {action, text, priority}
|
||||
|
||||
func label_for(action : StringName) -> String:
|
||||
return LABELS.get(action, String(action)) #sans entrée : le nom de l'action, bien visible
|
||||
|
||||
#---------- hints d'état (appelés par la StateMachine) ----------
|
||||
|
||||
func set_state_hints(actions : Array) -> void:
|
||||
_state_actions = actions
|
||||
func show_hints(source : Variant, list : Array) -> void:
|
||||
var clean : Array = list.filter(func(h) -> bool: return h != null) #un Hint non assigne est ignore
|
||||
if clean.is_empty():
|
||||
clear_hints(source)
|
||||
return
|
||||
if _layers.get(source) == clean: return
|
||||
_layers[source] = clean
|
||||
changed.emit()
|
||||
|
||||
#---------- hints de contexte (monture, PNJ, etc.) ----------
|
||||
#text vide : le libellé vient du catalogue. À renseigner quand le sens dépend du contexte (Grimper / Parler sur la même action)
|
||||
func clear_hints(source : Variant) -> void:
|
||||
if _layers.erase(source): changed.emit()
|
||||
|
||||
func set_context(source : Variant, action : StringName, text : String = "", priority : int = 0) -> void:
|
||||
var h : Dictionary = {"action": action, "text": text if text != "" else label_for(action), "priority": priority}
|
||||
if _context_hints.get(source) == h: return
|
||||
_context_hints[source] = h
|
||||
changed.emit()
|
||||
|
||||
func clear_context(source : Variant) -> void:
|
||||
if _context_hints.erase(source): changed.emit()
|
||||
|
||||
#---------- lecture (par le HUD) ----------
|
||||
|
||||
func get_visible() -> Array:
|
||||
for key in _context_hints.keys():
|
||||
if typeof(key) == TYPE_OBJECT and !is_instance_valid(key): _context_hints.erase(key)
|
||||
func get_visible() -> Array[Hint]:
|
||||
#aplatit toutes les sources, en gardant l'ordre d'arrivee pour departager les priorites egales
|
||||
var entries : Array = [] #[hint, ordre]
|
||||
for source in _layers.keys():
|
||||
if typeof(source) == TYPE_OBJECT and !is_instance_valid(source):
|
||||
_layers.erase(source) #source detruite sans avoir appele clear_hints
|
||||
continue
|
||||
for h in _layers[source]:
|
||||
entries.append([h, entries.size()])
|
||||
|
||||
var ctx : Array = _context_hints.values()
|
||||
ctx.sort_custom(func(a, b): return a.priority > b.priority)
|
||||
entries.sort_custom(func(a : Array, b : Array) -> bool:
|
||||
if a[0].priority != b[0].priority: return a[0].priority > b[0].priority
|
||||
return a[1] < b[1])
|
||||
|
||||
#le contexte passe avant l'état ; une même action n'apparaît qu'une fois
|
||||
var result : Array = []
|
||||
#une action = un seul hint (le plus prioritaire)
|
||||
var result : Array[Hint] = []
|
||||
var used : Dictionary = {}
|
||||
for h in ctx:
|
||||
for e in entries:
|
||||
var h : Hint = e[0]
|
||||
if used.has(h.action): continue
|
||||
used[h.action] = true
|
||||
result.append(h)
|
||||
for action in _state_actions:
|
||||
if used.has(action): continue
|
||||
used[action] = true
|
||||
result.append({"action": action, "text": label_for(action)})
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
extends MarginContainer
|
||||
|
||||
const ROW_SCENE : PackedScene = preload("res://game/ui/hints/hint_row.tscn")
|
||||
|
||||
@onready var rows : HBoxContainer = $HBoxContainer
|
||||
|
||||
var _refresh_queued : bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
Hints.changed.connect(refresh)
|
||||
Hints.changed.connect(_queue_refresh)
|
||||
refresh()
|
||||
|
||||
func _queue_refresh() -> void:
|
||||
#plusieurs changements dans la meme frame (transition d'etat + monture...) : une seule reconstruction
|
||||
if _refresh_queued: return
|
||||
_refresh_queued = true
|
||||
refresh.call_deferred()
|
||||
|
||||
func refresh() -> void:
|
||||
_refresh_queued = false
|
||||
for c in rows.get_children():
|
||||
rows.remove_child(c)
|
||||
c.queue_free()
|
||||
for h in Hints.get_visible():
|
||||
rows.add_child(make_row(h.action, h.text))
|
||||
|
||||
func make_row(action : StringName, text : String) -> HBoxContainer:
|
||||
var row := HBoxContainer.new()
|
||||
|
||||
var tex := ControllerIconTexture.new()
|
||||
tex.path = String(action) #même chose que le champ "path" dans ta scène
|
||||
|
||||
var icon := TextureRect.new()
|
||||
icon.texture = tex
|
||||
icon.custom_minimum_size = Vector2(40, 40)
|
||||
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
row.add_child(icon)
|
||||
|
||||
var label := Label.new()
|
||||
label.text = text
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
row.add_child(label)
|
||||
return row
|
||||
for hint in Hints.get_visible():
|
||||
var row : HintRow = ROW_SCENE.instantiate()
|
||||
rows.add_child(row)
|
||||
row.setup(hint)
|
||||
|
|
|
|||
Loading…
Reference in a new issue