class_name TeamScreen extends Control #page "Equipe" : liste des 6 emplacements a gauche, fiche detaillee du pokemon a droite. #manette d'abord (navigation par focus), clavier et souris fonctionnent aussi. # #usage : # var screen := TeamScreen.new() # canvas_layer.add_child(screen) # screen.setup(GameData.player_team) # screen.closed.connect(screen.queue_free) signal closed const ControllerIconTextureScript = preload("res://addons/controller_icons/objects/ControllerIconTexture.gd") const STAT_LINES : Array = [ [PokemonNature.Stat.ATTACK, "Attaque"], [PokemonNature.Stat.DEFENSE, "Défense"], [PokemonNature.Stat.SP_ATTACK, "Att. Spé."], [PokemonNature.Stat.SP_DEFENSE, "Déf. Spé."], [PokemonNature.Stat.SPEED, "Vitesse"], ] var _team : PokemonTeam var _slot_buttons : Array[Button] = [] var _hp_bars : Array[ProgressBar] = [] var _hp_fills : Array[StyleBoxFlat] = [] var _info_sprite : TextureRect var _info_label : Label var _empty_label : Label var _prev_mouse_mode : Input.MouseMode = Input.MOUSE_MODE_VISIBLE var _mouse_saved : bool = false func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS #fonctionne meme si le jeu est en pause _build_ui() _prev_mouse_mode = Input.mouse_mode _mouse_saved = true Input.mouse_mode = Input.MOUSE_MODE_VISIBLE func _exit_tree() -> void: _restore_mouse() func setup(team : PokemonTeam) -> void: if !is_node_ready(): await ready _team = team _team.team_changed.connect(_refresh) _refresh() _focus_first_member.call_deferred() func _unhandled_input(event : InputEvent) -> void: if !visible: return if event.is_action_pressed(&"ui_cancel"): _close() get_viewport().set_input_as_handled() #---------- construction de l'interface ---------- func _build_ui() -> void: set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) #le node est deja dans l'arbre : il faut aussi remettre les offsets a zero var bg := ColorRect.new() bg.color = Color(0, 0, 0, 0.8) bg.set_anchors_preset(Control.PRESET_FULL_RECT) add_child(bg) var margin := MarginContainer.new() margin.set_anchors_preset(Control.PRESET_FULL_RECT) for side in ["left", "top", "right", "bottom"]: margin.add_theme_constant_override("margin_" + side, 40) add_child(margin) var root := VBoxContainer.new() root.size_flags_horizontal = Control.SIZE_EXPAND_FILL root.add_theme_constant_override("separation", 12) margin.add_child(root) var title := Label.new() title.text = "Équipe" title.add_theme_font_size_override("font_size", 28) root.add_child(title) var columns := HBoxContainer.new() columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL columns.size_flags_vertical = Control.SIZE_EXPAND_FILL columns.add_theme_constant_override("separation", 24) root.add_child(columns) #liste des emplacements var list_col := VBoxContainer.new() list_col.add_theme_constant_override("separation", 6) columns.add_child(list_col) for i in PokemonTeam.MAX_SIZE: _add_slot(list_col, i) #fiche detaillee var info_panel := PanelContainer.new() info_panel.custom_minimum_size = Vector2(520, 0) #un label avec retour a la ligne n'a pas de largeur minimale : on la garantit ici info_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL columns.add_child(info_panel) var info_margin := MarginContainer.new() for side in ["left", "top", "right", "bottom"]: info_margin.add_theme_constant_override("margin_" + side, 16) info_panel.add_child(info_margin) var info_box := HBoxContainer.new() info_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL info_box.size_flags_vertical = Control.SIZE_EXPAND_FILL info_box.add_theme_constant_override("separation", 20) info_margin.add_child(info_box) _info_sprite = TextureRect.new() _info_sprite.custom_minimum_size = Vector2(160, 160) _info_sprite.expand_mode = TextureRect.EXPAND_IGNORE_SIZE _info_sprite.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED _info_sprite.size_flags_vertical = Control.SIZE_SHRINK_BEGIN info_box.add_child(_info_sprite) _info_label = Label.new() _info_label.custom_minimum_size = Vector2(300, 0) #evite que le texte s'ecrase a une lettre par ligne _info_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL _info_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART info_box.add_child(_info_label) _empty_label = Label.new() _empty_label.text = "Aucun Pokémon dans l'équipe pour le moment." _empty_label.hide() root.add_child(_empty_label) #aide des touches var hints := HBoxContainer.new() root.add_child(hints) _make_hint(hints, &"ui_cancel", "Retour", _close) func _add_slot(parent : Control, index : int) -> void: var b := Button.new() b.custom_minimum_size = Vector2(380, 88) b.alignment = HORIZONTAL_ALIGNMENT_LEFT b.expand_icon = true b.clip_text = true b.add_theme_constant_override("icon_max_width", 64) b.pressed.connect(_update_info.bind(index)) b.focus_entered.connect(_update_info.bind(index)) b.mouse_entered.connect(_on_slot_hovered.bind(index)) parent.add_child(b) _slot_buttons.append(b) #barre de PV en bas du bouton var bar := ProgressBar.new() bar.show_percentage = false bar.custom_minimum_size = Vector2(0, 8) bar.mouse_filter = Control.MOUSE_FILTER_IGNORE bar.anchor_left = 0.0 bar.anchor_right = 1.0 bar.anchor_top = 1.0 bar.anchor_bottom = 1.0 bar.offset_left = 8.0 bar.offset_right = -8.0 bar.offset_top = -14.0 bar.offset_bottom = -6.0 var fill := StyleBoxFlat.new() fill.bg_color = Color(0.3, 0.8, 0.35) bar.add_theme_stylebox_override("fill", fill) b.add_child(bar) _hp_bars.append(bar) _hp_fills.append(fill) func _make_hint(parent : Control, action : StringName, text : String, callback : Callable) -> Button: #icone qui s'adapte au peripherique (addon controller_icons) + texte ; cliquable a la souris var b := Button.new() b.flat = true b.focus_mode = Control.FOCUS_NONE b.text = text var tex : Texture2D = ControllerIconTextureScript.new() tex.set("path", String(action)) b.icon = tex b.expand_icon = true b.add_theme_constant_override("icon_max_width", 32) b.pressed.connect(callback) parent.add_child(b) return b #---------- affichage ---------- func _refresh() -> void: if _team == null: return for i in _slot_buttons.size(): var b : Button = _slot_buttons[i] var bar : ProgressBar = _hp_bars[i] var p : PokemonInstance = _team.get_pokemon(i) if p == null: b.icon = null b.text = "—" b.modulate = Color(1, 1, 1, 0.4) b.focus_mode = Control.FOCUS_NONE bar.hide() continue var max_hp : int = p.get_max_hp() b.focus_mode = Control.FOCUS_ALL b.modulate = Color(1.0, 0.6, 0.6) if p.is_fainted() else Color.WHITE b.icon = p.species.get_menu_sprite(false) b.text = "%s Niv.%d\nPV %d/%d%s" % [p.get_display_name(), p.level, p.current_hp, max_hp, " (K.O.)" if p.is_fainted() else ""] bar.show() bar.max_value = max_hp bar.value = p.current_hp var ratio : float = float(p.current_hp) / float(max_hp) _hp_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)) _empty_label.visible = _team.is_empty() #garde la fiche a jour pour l'emplacement qui a le focus var focused : int = _focused_index() if focused != -1: _update_info(focused) elif _team.is_empty(): _update_info(-1) func _update_info(index : int) -> void: var p : PokemonInstance = _team.get_pokemon(index) if _team and index >= 0 else null if p == null: _info_sprite.texture = null _info_label.text = "" return _info_sprite.texture = p.species.get_menu_sprite(false) var lines : Array[String] = [] lines.append("%s Niv.%d" % [p.get_display_name(), p.level]) lines.append("Nature : %s" % String(PokemonNature.Nature.keys()[p.nature]).capitalize()) if p.ability != null: lines.append("Talent : %s" % p.ability.get_display_name()) if p.can_be_mounted(): lines.append("Peut être monté") lines.append("") lines.append("PV : %d / %d" % [p.current_hp, p.get_max_hp()]) for line in STAT_LINES: lines.append("%s : %d" % [line[1], p.get_stat(line[0])]) lines.append("") lines.append("Capacités") for slot in p.active_moves: lines.append(" %s PP %d/%d" % [slot.move.get_display_name(), slot.current_pp, slot.max_pp]) _info_label.text = "\n".join(lines) #---------- focus ---------- func _focused_index() -> int: var focus_owner : Control = get_viewport().gui_get_focus_owner() return _slot_buttons.find(focus_owner) if focus_owner is Button else -1 func _focus_first_member() -> void: for b in _slot_buttons: if b.focus_mode == Control.FOCUS_ALL: b.grab_focus() return func _on_slot_hovered(index : int) -> void: if _slot_buttons[index].focus_mode == Control.FOCUS_ALL: _slot_buttons[index].grab_focus() #---------- fermeture ---------- func _close() -> void: _restore_mouse() hide() closed.emit() func _restore_mouse() -> void: if !_mouse_saved: return Input.mouse_mode = _prev_mouse_mode _mouse_saved = false