class_name TeamHUD extends Control #element de HUD : barre de l'equipe en bas a gauche ; le pokemon selectionne est agrandi. #la selection est stockee dans GameData (GameData.get_selected_pokemon()), #pour que le lancer (ThrowState...) sache quel pokemon envoyer. # #usage : ajouter TeamHUD.new() a ton HUD (CanvasLayer), ou attacher ce script a un Control plein ecran. const ControllerIconTextureScript = preload("res://addons/controller_icons/objects/ControllerIconTexture.gd") #actions creees automatiquement si elles n'existent pas (croix gauche/droite, fleches du clavier) @export var prev_action : StringName = &"hud_team_prev" @export var next_action : StringName = &"hud_team_next" @export var slot_size : float = 68.0 @export var selected_slot_size : float = 90.0 var _root_box : MarginContainer var _name_label : Label var _prev_icon : TextureRect var _next_icon : TextureRect var _slot_panels : Array[PanelContainer] = [] var _slot_styles : Array[StyleBoxFlat] = [] var _slot_sprites : Array[TextureRect] = [] var _slot_bars : Array[ProgressBar] = [] var _slot_fills : Array[StyleBoxFlat] = [] var _signatures : Array = [] #evite de reappliquer l'affichage d'un emplacement qui n'a pas change func _ready() -> void: _ensure_actions() _build_ui() #---------- actions d'entree ---------- func _ensure_actions() -> void: _ensure_action(prev_action, [_joy_event(JOY_BUTTON_DPAD_LEFT), _key_event(KEY_LEFT)]) _ensure_action(next_action, [_joy_event(JOY_BUTTON_DPAD_RIGHT), _key_event(KEY_RIGHT)]) func _ensure_action(action : StringName, events : Array[InputEvent]) -> void: if InputMap.has_action(action): return InputMap.add_action(action) for e in events: InputMap.action_add_event(action, e) func _joy_event(button : JoyButton) -> InputEvent: var e := InputEventJoypadButton.new() e.button_index = button return e func _key_event(key : Key) -> InputEvent: var e := InputEventKey.new() e.physical_keycode = key return e func _unhandled_input(event : InputEvent) -> void: #n'est pas appele quand le jeu est en pause (menu pause ouvert) if event.is_action_pressed(prev_action): GameData.cycle_selection(-1) get_viewport().set_input_as_handled() elif event.is_action_pressed(next_action): GameData.cycle_selection(1) get_viewport().set_input_as_handled() #---------- construction ---------- func _build_ui() -> void: set_anchors_preset(Control.PRESET_FULL_RECT) mouse_filter = Control.MOUSE_FILTER_IGNORE _root_box = MarginContainer.new() _root_box.set_anchors_preset(Control.PRESET_BOTTOM_LEFT) _root_box.grow_vertical = Control.GROW_DIRECTION_BEGIN #grandit vers le haut _root_box.mouse_filter = Control.MOUSE_FILTER_IGNORE for side in ["left", "bottom"]: _root_box.add_theme_constant_override("margin_" + side, 32) add_child(_root_box) var column := VBoxContainer.new() column.mouse_filter = Control.MOUSE_FILTER_IGNORE column.add_theme_constant_override("separation", 6) _root_box.add_child(column) _name_label = Label.new() _name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE _name_label.add_theme_font_size_override("font_size", 20) _name_label.add_theme_constant_override("outline_size", 6) _name_label.add_theme_color_override("font_outline_color", Color.BLACK) column.add_child(_name_label) var row := HBoxContainer.new() row.mouse_filter = Control.MOUSE_FILTER_IGNORE row.add_theme_constant_override("separation", 8) column.add_child(row) _prev_icon = _make_hint_icon(prev_action) row.add_child(_prev_icon) var slots_row := HBoxContainer.new() slots_row.mouse_filter = Control.MOUSE_FILTER_IGNORE slots_row.add_theme_constant_override("separation", 6) row.add_child(slots_row) for i in PokemonTeam.MAX_SIZE: _add_slot(slots_row) _signatures.append(null) _next_icon = _make_hint_icon(next_action) row.add_child(_next_icon) func _add_slot(parent : Control) -> void: var panel := PanelContainer.new() panel.mouse_filter = Control.MOUSE_FILTER_IGNORE panel.size_flags_vertical = Control.SIZE_SHRINK_END #les petits restent alignes en bas, le selectionne depasse vers le haut var style := StyleBoxFlat.new() style.bg_color = Color(0, 0, 0, 0.55) style.set_corner_radius_all(10) style.set_border_width_all(2) style.set_content_margin_all(6) panel.add_theme_stylebox_override("panel", style) parent.add_child(panel) var box := VBoxContainer.new() box.mouse_filter = Control.MOUSE_FILTER_IGNORE box.add_theme_constant_override("separation", 3) panel.add_child(box) var sprite := TextureRect.new() sprite.mouse_filter = Control.MOUSE_FILTER_IGNORE sprite.expand_mode = TextureRect.EXPAND_IGNORE_SIZE sprite.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED sprite.size_flags_vertical = Control.SIZE_EXPAND_FILL box.add_child(sprite) var bar := ProgressBar.new() bar.mouse_filter = Control.MOUSE_FILTER_IGNORE bar.show_percentage = false bar.custom_minimum_size = Vector2(0, 6) var fill := StyleBoxFlat.new() fill.bg_color = Color(0.3, 0.8, 0.35) bar.add_theme_stylebox_override("fill", fill) box.add_child(bar) _slot_panels.append(panel) _slot_styles.append(style) _slot_sprites.append(sprite) _slot_bars.append(bar) _slot_fills.append(fill) func _make_hint_icon(action : StringName) -> TextureRect: #icone de touche qui s'adapte au peripherique (addon controller_icons) var tr := TextureRect.new() tr.mouse_filter = Control.MOUSE_FILTER_IGNORE var tex : Texture2D = ControllerIconTextureScript.new() tex.set("path", String(action)) tr.texture = tex tr.custom_minimum_size = Vector2(32, 32) tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED tr.size_flags_vertical = Control.SIZE_SHRINK_CENTER return tr #---------- mise a jour (l'equipe change souvent : on lit l'etat chaque frame, sans signaux a rebrancher) ---------- func _process(_delta : float) -> void: var team : PokemonTeam = GameData.player_team GameData.validate_selection() var selected : int = GameData.selected_index _root_box.visible = !team.is_empty() var able_count : int = 0 for i in _slot_panels.size(): var p : PokemonInstance = team.get_pokemon(i) if p == null: _slot_panels[i].visible = false _signatures[i] = null continue _slot_panels[i].visible = true if !p.is_fainted(): able_count += 1 var sig : Array = [p.get_instance_id(), p.current_hp, p.get_max_hp(), i == selected] if _signatures[i] == sig: continue _signatures[i] = sig _apply_slot(i, p, i == selected) #les icones de touches n'apparaissent que s'il y a un choix a faire _prev_icon.visible = able_count > 1 _next_icon.visible = able_count > 1 var selected_pokemon : PokemonInstance = team.get_pokemon(selected) var label_text : String = "" if selected_pokemon: label_text = "%s Niv.%d" % [selected_pokemon.get_display_name(), selected_pokemon.level] if _name_label.text != label_text: _name_label.text = label_text func _apply_slot(i : int, p : PokemonInstance, is_selected : bool) -> void: var px : float = selected_slot_size if is_selected else slot_size _slot_panels[i].custom_minimum_size = Vector2(px, px) _slot_panels[i].modulate = Color(1.0, 0.55, 0.55) if p.is_fainted() else Color.WHITE _slot_sprites[i].texture = p.species.get_menu_sprite(false) _slot_styles[i].border_color = Color(1.0, 0.9, 0.4) if is_selected else Color(1, 1, 1, 0.25) _slot_styles[i].set_border_width_all(3 if is_selected else 2) var max_hp : int = p.get_max_hp() _slot_bars[i].max_value = max_hp _slot_bars[i].value = p.current_hp var ratio : float = float(p.current_hp) / float(max_hp) _slot_fills[i].bg_color = Color(0.3, 0.8, 0.35) if ratio > 0.5 else (Color(0.95, 0.7, 0.2) if ratio > 0.2 else Color(0.9, 0.25, 0.25))