class_name TopBarView extends RefCounted const BitmapFontLabelScript := preload("res://scripts/core/bitmap_font_label.gd") const NumberFormatterScript := preload("res://scripts/core/number_formatter.gd") const StaminaRulesScript := preload("res://scripts/domain/rules/stamina_rules.gd") const PowerFontTexturePath := "res://assets/egret/fonts/building_lv_font.png" const PowerFontPath := "res://assets/egret/fonts/building_lv_font.json" const TopShowTexture := preload("res://assets/egret/menu2/btn_top_show.png") const TopHideTexture := preload("res://assets/egret/menu2/btn_top_hide.png") const TOP_MENU_EXPANDED_Y := 1.0 const TOP_MENU_COLLAPSED_Y := -130.0 const DEFAULT_BAR_FILL_MAX_WIDTH := 223.0 var _owner: Control var _top_layer: Control var _top_button_group: Control var _top_hide: TextureRect var _label_name: Label var _label_level: Label var _label_gold: Label var _label_diamond: Label var _label_score: Label var _label_exp: Label var _label_stamina: Label var _exp_fill: NinePatchRect var _stamina_fill: NinePatchRect var _avatar: TextureRect var _game_config var _style_label: Callable var _style_menu_title: Callable var _score_bitmap: BitmapFontLabel var _exp_tween: Tween var _stamina_tween: Tween var _top_menu_tween: Tween var _top_menu_expanded := true var _initial_rendered := false var _exp_fill_max_width := DEFAULT_BAR_FILL_MAX_WIDTH var _stamina_fill_max_width := DEFAULT_BAR_FILL_MAX_WIDTH func setup(bindings: Dictionary, style_label: Callable, style_menu_title: Callable) -> void: _owner = bindings.get("owner") as Control _top_layer = bindings.get("top_layer") as Control _top_button_group = bindings.get("top_button_group") as Control _top_hide = bindings.get("top_hide") as TextureRect _label_name = bindings.get("label_name") as Label _label_level = bindings.get("label_level") as Label _label_gold = bindings.get("label_gold") as Label _label_diamond = bindings.get("label_diamond") as Label _label_score = bindings.get("label_score") as Label _label_exp = bindings.get("label_exp") as Label _label_stamina = bindings.get("label_stamina") as Label _exp_fill = bindings.get("exp_fill") as NinePatchRect _stamina_fill = bindings.get("stamina_fill") as NinePatchRect if _exp_fill != null and _exp_fill.size.x > 0.0: _exp_fill_max_width = _exp_fill.size.x if _stamina_fill != null and _stamina_fill.size.x > 0.0: _stamina_fill_max_width = _stamina_fill.size.x _avatar = bindings.get("avatar") as TextureRect _game_config = bindings.get("game_config") _style_label = style_label _style_menu_title = style_menu_title _apply_label_theme() _setup_power_bitmap_label() _apply_top_menu_toggle_visual() func toggle_top_button_group() -> bool: if _top_button_group == null or _owner == null: return _top_menu_expanded if _top_menu_tween != null: _top_menu_tween.kill() _top_menu_expanded = not _top_menu_expanded _apply_top_menu_toggle_visual() _top_button_group.visible = true var target_y := TOP_MENU_EXPANDED_Y if _top_menu_expanded else TOP_MENU_COLLAPSED_Y _top_menu_tween = _owner.create_tween() _top_menu_tween.tween_property(_top_button_group, "position:y", target_y, 0.25).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) if not _top_menu_expanded: _top_menu_tween.finished.connect(_finish_collapse_top_menu) return _top_menu_expanded func set_visitor_mode(enabled: bool) -> void: if _top_button_group != null: _top_button_group.visible = not enabled and _top_menu_expanded if _top_hide != null: _top_hide.visible = not enabled func render(player_data: Dictionary, own_player_data: Dictionary = {}) -> void: if _label_name == null: return var nickname := str(player_data.get("nickname", player_data.get("username", ""))) _label_name.text = nickname.left(10) _label_level.text = "LV.%s" % int(player_data.get("level", 1)) _label_gold.text = NumberFormatterScript.compact(int(player_data.get("gold", 0))) _label_diamond.text = NumberFormatterScript.compact(int(player_data.get("gem", 0))) var score_text := NumberFormatterScript.compact(int(player_data.get("score", 0))) _label_score.text = score_text if _score_bitmap != null: _score_bitmap.set_text(score_text) var exp_value := int(player_data.get("exp", 0)) var exp_max := _get_level_exp_max(int(player_data.get("level", 1))) _label_exp.text = "%s/%s" % [exp_value, exp_max] var ratio := clampf(float(exp_value) / float(maxi(exp_max, 1)), 0.0, 1.0) _set_exp_width(_exp_fill_max_width * ratio, _initial_rendered) var stamina_source := own_player_data if not own_player_data.is_empty() else player_data var stamina := StaminaRulesScript.snapshot(stamina_source) var stamina_value := int(stamina.get("value", 20)) var stamina_max := maxi(int(stamina.get("max", 20)), 1) if _label_stamina != null: _label_stamina.text = "%s/%s" % [stamina_value, stamina_max] var stamina_ratio := clampf(float(stamina_value) / float(stamina_max), 0.0, 1.0) _set_stamina_width(_stamina_fill_max_width * stamina_ratio, _initial_rendered) _initial_rendered = true _apply_avatar(str(player_data.get("avatar", "1"))) func _apply_label_theme() -> void: if not _style_label.is_valid(): return _style_label.call(_label_name, 20, Color.html("#fffeef"), 1, Color.html("#7f5523")) _style_label.call(_label_level, 20, Color.WHITE, 1, Color.html("#724f2e")) _style_label.call(_label_gold, 18, Color.html("#fffbe5"), 1, Color.html("#724f2e")) _style_label.call(_label_diamond, 18, Color.html("#fffbe5"), 1, Color.html("#724f2e")) _style_label.call(_label_score, 24, Color.html("#fff05a"), 2, Color.html("#8a4b18")) _style_label.call(_label_exp, 16, Color.html("#b0ff94"), 1, Color.html("#724f2e")) _style_label.call(_label_stamina, 16, Color.html("#fff9d7"), 1, Color.html("#8a4b18")) func _setup_power_bitmap_label() -> void: if _top_layer == null or _label_score == null: return _label_score.visible = false _score_bitmap = BitmapFontLabelScript.new() _score_bitmap.name = "PowerBitmapLabel" _score_bitmap.position = Vector2(244.0, 8.0) _score_bitmap.letter_spacing = -3.0 _score_bitmap.mouse_filter = Control.MOUSE_FILTER_IGNORE _score_bitmap.setup(PowerFontTexturePath, PowerFontPath) _top_layer.add_child(_score_bitmap) func _set_exp_width(width: float, animated: bool) -> void: if _exp_fill == null: return width = clampf(width, 0.0, _exp_fill_max_width) if _exp_tween != null: _exp_tween.kill() if width <= 0.0: _exp_fill.size.x = 0.0 _exp_fill.visible = false return if not animated: _exp_fill.visible = true _exp_fill.size.x = width return if _owner == null: _exp_fill.visible = true _exp_fill.size.x = width return _exp_fill.visible = true _exp_tween = _owner.create_tween() _exp_tween.tween_property(_exp_fill, "size:x", width, 0.24).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) func _set_stamina_width(width: float, animated: bool) -> void: if _stamina_fill == null: return width = clampf(width, 0.0, _stamina_fill_max_width) if _stamina_tween != null: _stamina_tween.kill() if width <= 0.0: _stamina_fill.size.x = 0.0 _stamina_fill.visible = false return if not animated: _stamina_fill.visible = true _stamina_fill.size.x = width return if _owner == null: _stamina_fill.visible = true _stamina_fill.size.x = width return _stamina_fill.visible = true _stamina_tween = _owner.create_tween() _stamina_tween.tween_property(_stamina_fill, "size:x", width, 0.24).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) func _apply_top_menu_toggle_visual() -> void: if _top_hide == null: return _top_hide.texture = TopShowTexture if _top_menu_expanded else TopHideTexture func _finish_collapse_top_menu() -> void: if _top_button_group != null and not _top_menu_expanded: _top_button_group.visible = false func _apply_avatar(avatar_id: String) -> void: if _avatar == null: return if avatar_id.is_empty() or avatar_id.contains("http"): avatar_id = "1" var resource_path := "res://assets/egret/head/head%s.png" % avatar_id if not ResourceLoader.exists(resource_path): resource_path = "res://assets/egret/head/head1.png" _avatar.texture = load(resource_path) func _get_level_exp_max(level: int) -> int: if _game_config != null and _game_config.has_method("get_player_exp_max"): return int(_game_config.get_player_exp_max(level + 1)) return mini(level + 1, 60)