class_name LuneHeroCard extends Button ## The hero in the bottom bar: portrait, name, level, experience progress, HP, role, the ## three item slots and the talent state. A click opens the hero window (details, items, ## talents) or, without a hero, the recruiting window. No MOBA action bar: heroes act alone. var hero_id: String = "" var _portrait: LunePortraitRect var _name: Label var _level: Label var _role_glyph: LuneGlyph var _role: Label var _xp: LuneHpBar var _xp_text: Label var _hp_glyph: LuneGlyph var _hp: Label var _hp_bar: LuneHpBar var _slots: Array[LuneGlyph] = [] var _slot_boxes: Array[PanelContainer] = [] var _talent: LuneStatusBadge var _empty: VBoxContainer var _filled: VBoxContainer var _faces: Array[LunePortraitRect] = [] var data: Dictionary = {} var _signature: String = "" var _empty_title: Label var _empty_price: Label var _empty_coin: LuneGlyph var _faces_row: HBoxContainer func _init() -> void: theme_type_variation = "HudCard" focus_mode = Control.FOCUS_ALL mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND custom_minimum_size = Vector2(320, 132) var row := HBoxContainer.new() row.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) row.offset_left = 8 row.offset_right = -8 row.offset_top = 8 row.offset_bottom = -8 row.add_theme_constant_override("separation", 10) row.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(row) _portrait = LunePortraitRect.new() _portrait.custom_minimum_size = Vector2(88, 0) row.add_child(_portrait) _filled = VBoxContainer.new() _filled.add_theme_constant_override("separation", 2) _filled.size_flags_horizontal = Control.SIZE_EXPAND_FILL _filled.mouse_filter = Control.MOUSE_FILTER_IGNORE row.add_child(_filled) _name = LuneUiTheme.label("", "HudName", _filled) _name.clip_text = true _name.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS var role_row := HBoxContainer.new() role_row.add_theme_constant_override("separation", 5) role_row.mouse_filter = Control.MOUSE_FILTER_IGNORE _filled.add_child(role_row) LuneGlyph.make("star", LuneUiTheme.HERO, 13.0, role_row) _level = LuneUiTheme.label("", "HudValue", role_row) _role_glyph = LuneGlyph.make("tank", LuneUiTheme.MUTED, 14.0, role_row) _role = LuneUiTheme.label("", "HudMuted", role_row) var xp_row := HBoxContainer.new() xp_row.add_theme_constant_override("separation", 6) xp_row.mouse_filter = Control.MOUSE_FILTER_IGNORE _filled.add_child(xp_row) _xp = LuneHpBar.new() _xp.custom_minimum_size = Vector2(60, 6) _xp.size_flags_horizontal = Control.SIZE_EXPAND_FILL _xp.size_flags_vertical = Control.SIZE_SHRINK_CENTER _xp.fill = LuneUiTheme.BRONZE_LIGHT xp_row.add_child(_xp) _xp_text = LuneUiTheme.label("", "HudMuted", xp_row) var hp_row := HBoxContainer.new() hp_row.add_theme_constant_override("separation", 5) hp_row.mouse_filter = Control.MOUSE_FILTER_IGNORE _filled.add_child(hp_row) _hp_glyph = LuneGlyph.make("heart", LuneUiTheme.DANGER.lightened(.2), 13.0, hp_row) _hp = LuneUiTheme.label("", "HudText", hp_row) _hp_bar = LuneHpBar.new() _hp_bar.custom_minimum_size = Vector2(50, 5) _hp_bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL _hp_bar.size_flags_vertical = Control.SIZE_SHRINK_CENTER hp_row.add_child(_hp_bar) var item_row := HBoxContainer.new() item_row.add_theme_constant_override("separation", 4) item_row.mouse_filter = Control.MOUSE_FILTER_IGNORE _filled.add_child(item_row) for index: int in range(LuneContent.ITEM_SLOTS): var box := PanelContainer.new() box.add_theme_stylebox_override("panel", LuneUiTheme.slot_style()) box.custom_minimum_size = Vector2(26, 26) box.mouse_filter = Control.MOUSE_FILTER_IGNORE item_row.add_child(box) var glyph := LuneGlyph.new() glyph.custom_minimum_size = Vector2(18, 18) box.add_child(glyph) _slots.append(glyph) _slot_boxes.append(box) _talent = LuneStatusBadge.new() item_row.add_child(_talent) _empty = VBoxContainer.new() _empty.add_theme_constant_override("separation", 3) _empty.size_flags_horizontal = Control.SIZE_EXPAND_FILL _empty.alignment = BoxContainer.ALIGNMENT_CENTER _empty.mouse_filter = Control.MOUSE_FILTER_IGNORE row.add_child(_empty) _empty_title = LuneUiTheme.label("HELD ANWERBEN", "HudName", _empty) var faces := HBoxContainer.new() _faces_row = faces faces.add_theme_constant_override("separation", 4) faces.mouse_filter = Control.MOUSE_FILTER_IGNORE _empty.add_child(faces) for hero: String in LuneContent.HEROES: var face := LunePortraitRect.new() face.custom_minimum_size = Vector2(38, 38) faces.add_child(face) _faces.append(face) var price := HBoxContainer.new() price.add_theme_constant_override("separation", 4) price.mouse_filter = Control.MOUSE_FILTER_IGNORE _empty.add_child(price) _empty_coin = LuneGlyph.make("coin", LuneUiTheme.GOLD, 14.0, price) _empty_price = LuneUiTheme.label("%d · ein Held pro Spieler" % int(LuneContent.HEROES.values()[0].cost), "HudMuted", price) LuneTooltip.attach(self, _tip) ## hero: own hero dictionary (draft in planning) or {}; live: the combat unit or null. func apply(hero: Dictionary, model: LuneHudModel, live: LuneUnit) -> void: var has_hero: bool = not hero.is_empty() if live != null and has_hero: _hp.text = ("%s / %s" % [LuneUiTheme.number(live.hp), LuneUiTheme.number(live.max_hp)]) if live.alive else "gefallen" _hp_bar.fraction = live.hp / maxf(1.0, live.max_hp) if live.alive else 0.0 _hp_bar.fill = LuneHpBar.health_color(_hp_bar.fraction, LuneUiTheme.GOOD) _portrait.crossed = not live.alive var signature: String = "%s|%s|%d|%s" % [str(hero), live != null, model.own_gold(), model.planning()] if signature == _signature: return _signature = signature _filled.visible = has_hero _empty.visible = not has_hero if not has_hero: hero_id = "" data = {} _portrait.show_model("w04") _portrait.dimmed = true var index: int = 0 for id: String in LuneContent.HEROES: _faces[index].show_model(str(LuneContent.HEROES[id].model)) index += 1 # In combat nobody can recruit: say so instead of offering it. var planning: bool = model.planning() _empty_title.text = "HELD ANWERBEN" if planning else "KEIN HELD" _empty_price.text = ("%d · ein Held pro Spieler" % int(LuneContent.HEROES.values()[0].cost)) if planning else "Anwerben in der Planung" _empty_coin.visible = planning _faces_row.visible = planning return hero_id = str(hero.hero_id) var spec: Dictionary = LuneContent.HEROES[hero_id] var definition: Dictionary = model.hero_definition(hero) data = {"hero": hero, "definition": definition} _portrait.dimmed = false _portrait.crossed = live != null and not live.alive _portrait.show_model(str(spec.model)) _name.text = str(spec.name) var xp: int = int(hero.xp) var level: int = LuneContent.level_for(xp) _level.text = "STUFE %d" % level _role_glyph.glyph = LuneStats.role_glyph(str(definition.role)) _role.text = LuneStats.role_name(str(definition.role)) var low: int = LuneContent.LEVEL_XP[level - 1] var top: bool = level >= LuneContent.LEVEL_XP.size() var high: int = LuneContent.LEVEL_XP[mini(level, LuneContent.LEVEL_XP.size() - 1)] _xp.fraction = 1.0 if top else float(xp - low) / maxf(1.0, float(high - low)) _xp_text.text = "EP %d / %d" % [xp, high] if not top else "EP %d · max" % xp _hp_bar.visible = live != null if live == null: _hp.text = "%s Leben" % LuneUiTheme.number(definition.max_hp) var items: Array = hero.items for index: int in range(_slots.size()): var item_id: String = str(items[index]) if index < items.size() else "" _slots[index].glyph = item_id _slots[index].color = LuneItemSlot.color_of(item_id) var talents: Array = hero.talents var open_tier: int = talents.size() if open_tier < LuneContent.TALENT_LEVELS.size() and level >= LuneContent.TALENT_LEVELS[open_tier]: _talent.set_state("TALENT!", LuneUiTheme.HERO, "star", true) _talent.visible = model.planning() elif open_tier < LuneContent.TALENT_LEVELS.size(): _talent.set_state("TALENT AB %d" % LuneContent.TALENT_LEVELS[open_tier], LuneUiTheme.MUTED) _talent.visible = true else: _talent.visible = false func _tip() -> Dictionary: if data.is_empty(): return {"title": "Held anwerben", "glyph": "crown", "glyph_color": LuneUiTheme.HERO, "text": "Drei Helden stehen zur Wahl; jeder Spieler führt einen. Helden sammeln Erfahrung, steigen bis Stufe 10 auf, tragen drei Items und wählen zwei Talente.", "note": "Klick: Heldenauswahl öffnen"} var hero: Dictionary = data.hero var spec: Dictionary = LuneContent.HEROES[hero.hero_id] var names: PackedStringArray = [] for item_id: String in hero.items: names.append(LuneContent.ITEMS[item_id].name) return {"title": "%s · Stufe %d" % [spec.name, LuneContent.level_for(int(hero.xp))], "glyph": "crown", "glyph_color": LuneUiTheme.HERO, "subtitle": str(spec.title), "stats": LuneStats.rows(data.definition), "text": "Items: " + (", ".join(names) if not names.is_empty() else "keine"), "note": "Klick: Werte, Items und Talente · H: Kamera zum Helden"}