Compare commits

...

2 Commits

Author SHA1 Message Date
DragonSpirit 5d9b0ce738 add kickable tooltips 2026-04-23 14:44:21 +03:00
DragonSpirit 5f61c26563 ui items slots 2026-04-23 14:40:55 +03:00
7 changed files with 206 additions and 0 deletions
+18
View File
@@ -20,6 +20,7 @@ var damage_modifier: float = 1.8
@onready var mesh_node: MeshInstance3D = $BoulderMesh
var boulder_mat: StandardMaterial3D
var _tooltip: Label3D
const COLOR_IDLE := Color(0.32, 0.28, 0.22)
const COLOR_IMPACT := Color(1.0, 1.0, 1.0)
@@ -28,6 +29,23 @@ func _ready() -> void:
add_to_group("kickable")
boulder_mat = mesh_node.material_override.duplicate() as StandardMaterial3D
mesh_node.material_override = boulder_mat
_tooltip = Label3D.new()
_tooltip.text = "Boulder\nRock+Rock\nVery heavy. High damage"
_tooltip.billboard = BaseMaterial3D.BILLBOARD_ENABLED
_tooltip.font_size = 28
_tooltip.outline_size = 6
_tooltip.position = Vector3(0, 1.4, 0)
_tooltip.modulate = Color(1.0, 0.95, 0.8)
_tooltip.visible = false
add_child(_tooltip)
func _process(_delta: float) -> void:
if dead or state != State.IDLE:
_tooltip.visible = false
return
var players := get_tree().get_nodes_in_group("player")
_tooltip.visible = not players.is_empty() and \
(players[0] as Node3D).global_position.distance_to(global_position) < 2.5
func apply_collision_damage(dmg: float) -> void:
_take_damage(dmg)
+18
View File
@@ -22,6 +22,7 @@ var damage_modifier: float = 0.0
@onready var mesh_node: MeshInstance3D = $EssenceMesh
var essence_mat: StandardMaterial3D
var _tooltip: Label3D
func _ready() -> void:
add_to_group("kickable")
@@ -30,6 +31,23 @@ func _ready() -> void:
var tw := create_tween().set_loops()
tw.tween_property(self, "position:y", 0.35, 0.6)
tw.tween_property(self, "position:y", 0.15, 0.6)
_tooltip = Label3D.new()
_tooltip.text = "Magic Essence\nKick into\nEnchanting Table → Sphere"
_tooltip.billboard = BaseMaterial3D.BILLBOARD_ENABLED
_tooltip.font_size = 28
_tooltip.outline_size = 6
_tooltip.position = Vector3(0, 1.2, 0)
_tooltip.modulate = Color(0.85, 0.7, 1.0)
_tooltip.visible = false
add_child(_tooltip)
func _process(_delta: float) -> void:
if dead or state != State.IDLE:
_tooltip.visible = false
return
var players := get_tree().get_nodes_in_group("player")
_tooltip.visible = not players.is_empty() and \
(players[0] as Node3D).global_position.distance_to(global_position) < 2.5
func apply_collision_damage(_dmg: float) -> void:
pass
+18
View File
@@ -22,6 +22,7 @@ var damage_modifier: float = 0.9
@onready var mesh_node: MeshInstance3D = $IronMesh
var iron_mat: StandardMaterial3D
var _tooltip: Label3D
const COLOR_IDLE := Color(0.55, 0.58, 0.62)
const COLOR_IMPACT := Color(1.0, 1.0, 1.0)
@@ -30,6 +31,23 @@ func _ready() -> void:
add_to_group("kickable")
iron_mat = mesh_node.material_override.duplicate() as StandardMaterial3D
mesh_node.material_override = iron_mat
_tooltip = Label3D.new()
_tooltip.text = "Iron\nHeavy weapon\n+Leather → Iron Shield\nForge → Metal Plate"
_tooltip.billboard = BaseMaterial3D.BILLBOARD_ENABLED
_tooltip.font_size = 28
_tooltip.outline_size = 6
_tooltip.position = Vector3(0, 1.1, 0)
_tooltip.modulate = Color(1.0, 0.95, 0.8)
_tooltip.visible = false
add_child(_tooltip)
func _process(_delta: float) -> void:
if dead or state != State.IDLE:
_tooltip.visible = false
return
var players := get_tree().get_nodes_in_group("player")
_tooltip.visible = not players.is_empty() and \
(players[0] as Node3D).global_position.distance_to(global_position) < 2.5
func apply_collision_damage(dmg: float) -> void:
_take_damage(dmg)
+18
View File
@@ -14,9 +14,27 @@ var fly_vel: Vector3 = Vector3.ZERO
var damage_modifier: float = 0.0
@onready var mesh_node: MeshInstance3D = $LeatherMesh
var _tooltip: Label3D
func _ready() -> void:
add_to_group("kickable")
_tooltip = Label3D.new()
_tooltip.text = "Leather\n+Stick → Boots\n+Rock → Leather Armor\n+Iron → Iron Shield"
_tooltip.billboard = BaseMaterial3D.BILLBOARD_ENABLED
_tooltip.font_size = 28
_tooltip.outline_size = 6
_tooltip.position = Vector3(0, 1.0, 0)
_tooltip.modulate = Color(1.0, 0.95, 0.8)
_tooltip.visible = false
add_child(_tooltip)
func _process(_delta: float) -> void:
if state != State.IDLE:
_tooltip.visible = false
return
var players := get_tree().get_nodes_in_group("player")
_tooltip.visible = not players.is_empty() and \
(players[0] as Node3D).global_position.distance_to(global_position) < 2.5
func apply_collision_damage(_dmg: float) -> void:
pass
+98
View File
@@ -48,6 +48,11 @@ var progress_bg: ColorRect
var upgrade_panel: Panel
var gameover_panel: Panel
# Equipment slots
var equip_fills: Array[ColorRect] = []
var equip_labels: Array[Label] = []
var _equip_prev_tiers: Array[int] = [-1, -1, -1]
func _ready() -> void:
_spawn_level()
_create_camera()
@@ -460,6 +465,8 @@ func _make_hud() -> void:
# Tier display
tier_label = _label(Vector2(12, 148), "", 17)
_make_equipment_slots()
func _make_upgrade_panel() -> void:
upgrade_panel = Panel.new()
upgrade_panel.process_mode = Node.PROCESS_MODE_ALWAYS
@@ -539,6 +546,96 @@ func _make_gameover_panel() -> void:
restart_btn.connect("pressed", _restart)
vbox.add_child(restart_btn)
# ─── Equipment slots ──────────────────────────────────────────────────────────
const _EQUIP_SLOT_SIZE := 54
const _EQUIP_SLOT_GAP := 8
const _EQUIP_SLOT_PAD := 3
const _EQUIP_START_X := 12
const _EQUIP_START_Y := 170
const _EQUIP_EMPTY_COL := Color(0.10, 0.10, 0.16)
const _EQUIP_BORDER_COL := Color(0.30, 0.30, 0.42)
# [slot_index][tier] = [fill_color, label_text]
const _EQUIP_DATA := [
[ # shield
[Color(0.10, 0.10, 0.16), ""],
[Color(0.55, 0.38, 0.18), "Wood\nShield"],
[Color(0.55, 0.58, 0.62), "Iron\nShield"],
],
[ # armor (toughness_tier)
[Color(0.10, 0.10, 0.16), ""],
[Color(0.76, 0.47, 0.18), "Leather\nArmor"],
[Color(0.58, 0.68, 1.00), "Metal\nArmor"],
],
[ # boots (kick_tier)
[Color(0.10, 0.10, 0.16), ""],
[Color(0.76, 0.47, 0.18), "Leather\nBoots"],
[Color(0.58, 0.68, 1.00), "Plate\nBoots"],
[Color(0.75, 0.18, 1.00), "Magic\nBoots"],
],
]
func _make_equipment_slots() -> void:
var titles := ["Shield", "Armor", "Boots"]
for i in range(3):
var x := _EQUIP_START_X + i * (_EQUIP_SLOT_SIZE + _EQUIP_SLOT_GAP)
var y := _EQUIP_START_Y
var title := _label(Vector2(x, y), titles[i], 12)
title.size.x = _EQUIP_SLOT_SIZE
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
var border := ColorRect.new()
border.position = Vector2(x, y + 16)
border.size = Vector2(_EQUIP_SLOT_SIZE, _EQUIP_SLOT_SIZE)
border.color = _EQUIP_BORDER_COL
canvas.add_child(border)
var fill := ColorRect.new()
fill.position = Vector2(x + _EQUIP_SLOT_PAD, y + 16 + _EQUIP_SLOT_PAD)
fill.size = Vector2(_EQUIP_SLOT_SIZE - _EQUIP_SLOT_PAD * 2, _EQUIP_SLOT_SIZE - _EQUIP_SLOT_PAD * 2)
fill.color = _EQUIP_EMPTY_COL
canvas.add_child(fill)
equip_fills.append(fill)
var lbl := Label.new()
lbl.position = fill.position
lbl.size = fill.size
lbl.text = ""
lbl.add_theme_font_size_override("font_size", 11)
lbl.add_theme_color_override("font_color", Color.WHITE)
lbl.add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.85))
lbl.add_theme_constant_override("shadow_offset_x", 1)
lbl.add_theme_constant_override("shadow_offset_y", 1)
lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
lbl.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
canvas.add_child(lbl)
equip_labels.append(lbl)
func _update_equipment_slots() -> void:
if not is_instance_valid(player):
return
var tiers := [
player.get("shield_tier") as int,
player.get("toughness_tier") as int,
player.get("kick_tier") as int,
]
for i in range(3):
var tier: int = tiers[i]
if tier == _equip_prev_tiers[i]:
continue
_equip_prev_tiers[i] = tier
var data: Array = _EQUIP_DATA[i]
var entry: Array = data[clampi(tier, 0, data.size() - 1)]
equip_fills[i].color = entry[0] as Color
equip_labels[i].text = entry[1] as String
if tier > 0:
var tw := create_tween()
tw.tween_property(equip_fills[i], "color", Color.WHITE, 0.06)
tw.tween_property(equip_fills[i], "color", entry[0] as Color, 0.22)
# ─── UI helpers ───────────────────────────────────────────────────────────────
func _label(pos: Vector2, text: String, size: int) -> Label:
@@ -577,3 +674,4 @@ func _update_tier_label() -> void:
var st: int = player.get("shield_tier")
var shield_str := "-" if st == 0 else str(st)
tier_label.text = "Kick: %d Tough: %d Shield: %s" % [kt, tt, shield_str]
_update_equipment_slots()
+18
View File
@@ -20,6 +20,7 @@ var damage_modifier: float = 1.25
@onready var mesh_node: MeshInstance3D = $RockMesh
var rock_mat: StandardMaterial3D
var _tooltip: Label3D
const COLOR_IDLE := Color(0.45, 0.38, 0.30)
const COLOR_IMPACT := Color(1.0, 1.0, 1.0)
@@ -28,6 +29,23 @@ func _ready() -> void:
add_to_group("kickable")
rock_mat = mesh_node.material_override.duplicate() as StandardMaterial3D
mesh_node.material_override = rock_mat
_tooltip = Label3D.new()
_tooltip.text = "Rock\n+Rock → Boulder\n+Stick → Wood Shield"
_tooltip.billboard = BaseMaterial3D.BILLBOARD_ENABLED
_tooltip.font_size = 28
_tooltip.outline_size = 6
_tooltip.position = Vector3(0, 1.1, 0)
_tooltip.modulate = Color(1.0, 0.95, 0.8)
_tooltip.visible = false
add_child(_tooltip)
func _process(_delta: float) -> void:
if dead or state != State.IDLE:
_tooltip.visible = false
return
var players := get_tree().get_nodes_in_group("player")
_tooltip.visible = not players.is_empty() and \
(players[0] as Node3D).global_position.distance_to(global_position) < 2.5
func can_merge_with(_other: Node3D, _speed: float) -> bool:
return false
+18
View File
@@ -20,6 +20,7 @@ var damage_modifier: float = 0.6
@onready var mesh_node: MeshInstance3D = $StickMesh
var stick_mat: StandardMaterial3D
var _tooltip: Label3D
const COLOR_IDLE := Color(0.55, 0.38, 0.18)
const COLOR_IMPACT := Color(1.0, 1.0, 1.0)
@@ -28,6 +29,23 @@ func _ready() -> void:
add_to_group("kickable")
stick_mat = mesh_node.material_override.duplicate() as StandardMaterial3D
mesh_node.material_override = stick_mat
_tooltip = Label3D.new()
_tooltip.text = "Stick\n+Leather → Boots\n+Stick → Stick Armor\n+Rock → Wood Shield"
_tooltip.billboard = BaseMaterial3D.BILLBOARD_ENABLED
_tooltip.font_size = 28
_tooltip.outline_size = 6
_tooltip.position = Vector3(0, 1.2, 0)
_tooltip.modulate = Color(1.0, 0.95, 0.8)
_tooltip.visible = false
add_child(_tooltip)
func _process(_delta: float) -> void:
if dead or state != State.IDLE:
_tooltip.visible = false
return
var players := get_tree().get_nodes_in_group("player")
_tooltip.visible = not players.is_empty() and \
(players[0] as Node3D).global_position.distance_to(global_position) < 2.5
func apply_collision_damage(dmg: float) -> void:
_take_damage(dmg)