Generate real glossy reel-symbol art and close the visual gap further

Hand-authored 6 gradient-shaded SVG icons (gold coin, purple gem,
green potion, blue RZ shield, coin stack, gift box) to replace the
flat monochrome placeholder icons in the reels and reward list - no
image-generation tool is available here, so this is genuine vector
art with radial/linear gradients and highlight layers rather than a
literal asset match to the mockup's renders.

Combined with real Godot techniques: a layered-label glow effect on
the "RZ" marquee, chase-lit marquee bulbs down both sides of the
reel screen (looping Tweens), a much more pronounced arch silhouette
and neon border on the cabinet, and an overall scale-up (bigger
reels, marquee, lever) so the machine reads as the dominant element
instead of a cramped placeholder. Verified with an actual rendered
screenshot (not just structural checks) at each iteration.
This commit is contained in:
hz
2026-07-30 01:54:45 -05:00
parent bcb411bed8
commit 1bc7fbfb3c
13 changed files with 468 additions and 54 deletions
+116 -54
View File
@@ -2,20 +2,22 @@ extends VBoxContainer
const Brand := preload("res://scripts/ui/medmind_style.gd")
const STAR_ICON := preload("res://assets/icons/lucide/star.svg")
const SHIELD_ICON := preload("res://assets/icons/lucide/shield.svg")
const CLOCK_ICON := preload("res://assets/icons/lucide/clock-3.svg")
const RESET_ICON := preload("res://assets/icons/lucide/rotate-ccw.svg")
const GEM_ICON := preload("res://assets/icons/lucide/gem.svg")
const FLASK_ICON := preload("res://assets/icons/lucide/flask-conical.svg")
const REEL_SYMBOLS := [STAR_ICON, SHIELD_ICON, CLOCK_ICON, RESET_ICON, GEM_ICON, FLASK_ICON]
const ICON_BY_REWARD_KEY := {
"jackpot": "star",
"epic": "gem",
"great": "shield",
"exam_token": "clock-3",
"qbank_reset": "rotate-ccw",
"nice": "star",
"good_luck": "star",
const SLOT_COIN := preload("res://assets/icons/slots/slot_coin.svg")
const SLOT_GEM := preload("res://assets/icons/slots/slot_gem.svg")
const SLOT_POTION := preload("res://assets/icons/slots/slot_potion.svg")
const SLOT_SHIELD := preload("res://assets/icons/slots/slot_shield.svg")
const SLOT_COIN_STACK := preload("res://assets/icons/slots/slot_coin_stack.svg")
const SLOT_GIFT := preload("res://assets/icons/slots/slot_gift.svg")
const REEL_SYMBOLS := [SLOT_COIN, SLOT_GEM, SLOT_POTION, SLOT_SHIELD, SLOT_COIN_STACK, SLOT_GIFT]
const REWARD_ICONS := {
"jackpot": {"texture": SLOT_COIN, "modulate": false},
"epic": {"texture": SLOT_GEM, "modulate": false},
"great": {"texture": SLOT_SHIELD, "modulate": false},
"exam_token": {"texture": CLOCK_ICON, "modulate": true},
"qbank_reset": {"texture": SLOT_GIFT, "modulate": false},
"nice": {"texture": SLOT_COIN_STACK, "modulate": false},
"good_luck": {"texture": SLOT_COIN, "modulate": false, "tint": true},
}
var main: Control
@@ -94,60 +96,64 @@ func _build_header() -> void:
func _build_machine(parent: Control) -> void:
var dark := UIThemeService.is_dark_mode()
machine_panel = PanelContainer.new()
machine_panel.custom_minimum_size = Vector2(560, 0)
machine_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
machine_panel.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
machine_panel.add_theme_stylebox_override("panel", _cabinet_box())
parent.add_child(machine_panel)
var margin := MarginContainer.new()
margin.add_theme_constant_override("margin_left", 24)
margin.add_theme_constant_override("margin_right", 24)
margin.add_theme_constant_override("margin_top", 22)
margin.add_theme_constant_override("margin_bottom", 22)
margin.add_theme_constant_override("margin_left", 32)
margin.add_theme_constant_override("margin_right", 32)
margin.add_theme_constant_override("margin_top", 30)
margin.add_theme_constant_override("margin_bottom", 28)
machine_panel.add_child(margin)
var column := VBoxContainer.new()
column.add_theme_constant_override("separation", 16)
column.add_theme_constant_override("separation", 20)
margin.add_child(column)
_build_marquee(column)
var reel_row := HBoxContainer.new()
reel_row.add_theme_constant_override("separation", 16)
reel_row.add_theme_constant_override("separation", 20)
reel_row.alignment = BoxContainer.ALIGNMENT_CENTER
column.add_child(reel_row)
reel_row.add_child(_build_light_strip(7))
var reel_screen := PanelContainer.new()
reel_screen.add_theme_stylebox_override("panel", _reel_screen_box())
reel_row.add_child(reel_screen)
var reel_margin := MarginContainer.new()
reel_margin.add_theme_constant_override("margin_left", 16)
reel_margin.add_theme_constant_override("margin_right", 16)
reel_margin.add_theme_constant_override("margin_top", 14)
reel_margin.add_theme_constant_override("margin_bottom", 14)
reel_margin.add_theme_constant_override("margin_left", 22)
reel_margin.add_theme_constant_override("margin_right", 22)
reel_margin.add_theme_constant_override("margin_top", 18)
reel_margin.add_theme_constant_override("margin_bottom", 18)
reel_screen.add_child(reel_margin)
var reels_grid := HBoxContainer.new()
reels_grid.add_theme_constant_override("separation", 12)
reels_grid.add_theme_constant_override("separation", 18)
reel_margin.add_child(reels_grid)
reel_columns.clear()
for _column_index in range(3):
var reel_col := VBoxContainer.new()
reel_col.add_theme_constant_override("separation", 8)
reel_col.add_theme_constant_override("separation", 10)
reels_grid.add_child(reel_col)
var cells: Array = []
for _row_index in range(3):
var icon := TextureRect.new()
icon.texture = STAR_ICON
icon.modulate = Brand.AMBER
icon.custom_minimum_size = Vector2(34, 34)
icon.texture = REEL_SYMBOLS[randi() % REEL_SYMBOLS.size()]
icon.custom_minimum_size = Vector2(58, 58)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
reel_col.add_child(icon)
cells.append(icon)
reel_columns.append(cells)
reel_row.add_child(_build_light_strip(7))
reel_row.add_child(_build_lever())
win_up_to_card = PanelContainer.new()
@@ -240,15 +246,10 @@ func _build_machine(parent: Control) -> void:
func _build_marquee(column: VBoxContainer) -> void:
var row := HBoxContainer.new()
row.alignment = BoxContainer.ALIGNMENT_CENTER
row.add_theme_constant_override("separation", 12)
row.add_theme_constant_override("separation", 14)
column.add_child(row)
row.add_child(_marquee_star())
var mark := Label.new()
mark.text = "RZ"
mark.add_theme_font_override("font", Brand.font)
mark.add_theme_font_size_override("font_size", 34)
mark.add_theme_color_override("font_color", Brand.SKY_BLUE)
row.add_child(mark)
row.add_child(_marquee_glow_label())
row.add_child(_marquee_star())
@@ -262,6 +263,65 @@ func _marquee_star() -> TextureRect:
return star
func _marquee_glow_label() -> Control:
var wrap := Control.new()
wrap.custom_minimum_size = Vector2(150, 70)
var halo_specs := [
{"size": 74, "alpha": 0.22, "offset": Vector2(-4, -4)},
{"size": 74, "alpha": 0.22, "offset": Vector2(4, 4)},
{"size": 64, "alpha": 0.4, "offset": Vector2.ZERO},
]
for spec in halo_specs:
var glow := Label.new()
glow.text = "RZ"
glow.add_theme_font_override("font", Brand.font)
glow.add_theme_font_size_override("font_size", spec["size"])
glow.add_theme_color_override("font_color", Color(Brand.SKY_BLUE, spec["alpha"]))
glow.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
glow.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
glow.set_anchors_preset(Control.PRESET_FULL_RECT)
glow.position += spec["offset"]
wrap.add_child(glow)
var mark := Label.new()
mark.text = "RZ"
mark.add_theme_font_override("font", Brand.font)
mark.add_theme_font_size_override("font_size", 46)
mark.add_theme_color_override("font_color", Brand.SKY_BLUE)
mark.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
mark.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
mark.set_anchors_preset(Control.PRESET_FULL_RECT)
wrap.add_child(mark)
return wrap
func _build_light_strip(count: int) -> VBoxContainer:
var strip := VBoxContainer.new()
strip.add_theme_constant_override("separation", 14)
strip.alignment = BoxContainer.ALIGNMENT_CENTER
for index in range(count):
var bulb := PanelContainer.new()
bulb.custom_minimum_size = Vector2(11, 11)
var bulb_box := StyleBoxFlat.new()
bulb_box.bg_color = Color("fef3c7")
bulb_box.set_corner_radius_all(6)
bulb_box.corner_detail = 12
bulb_box.anti_aliasing = false
bulb.add_theme_stylebox_override("panel", bulb_box)
strip.add_child(bulb)
_animate_bulb(bulb, index)
return strip
func _animate_bulb(bulb: Control, index: int) -> void:
bulb.modulate.a = 0.3
var tween := create_tween()
tween.set_loops()
tween.tween_interval(index * 0.12)
tween.tween_property(bulb, "modulate:a", 1.0, 0.4).set_trans(Tween.TRANS_SINE)
tween.tween_property(bulb, "modulate:a", 0.3, 0.4).set_trans(Tween.TRANS_SINE)
tween.tween_interval(1.4)
func _build_lever() -> Control:
var wrap := VBoxContainer.new()
wrap.alignment = BoxContainer.ALIGNMENT_BEGIN
@@ -269,11 +329,11 @@ func _build_lever() -> Control:
wrap.add_theme_constant_override("separation", 0)
var knob := PanelContainer.new()
knob.custom_minimum_size = Vector2(22, 22)
knob.custom_minimum_size = Vector2(30, 30)
knob.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
var knob_box := StyleBoxFlat.new()
knob_box.bg_color = Brand.ROYAL_BLUE
knob_box.set_corner_radius_all(11)
knob_box.set_corner_radius_all(15)
knob_box.corner_detail = 20
knob_box.anti_aliasing = false
knob.add_theme_stylebox_override("panel", knob_box)
@@ -281,7 +341,7 @@ func _build_lever() -> Control:
var rod := ColorRect.new()
rod.color = Color("64748b")
rod.custom_minimum_size = Vector2(5, 90)
rod.custom_minimum_size = Vector2(6, 140)
rod.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
wrap.add_child(rod)
@@ -292,13 +352,13 @@ func _cabinet_box() -> StyleBoxFlat:
var box := StyleBoxFlat.new()
box.bg_color = Color("0b1220")
box.border_color = Brand.SKY_BLUE
box.set_border_width_all(3)
box.corner_radius_top_left = 48
box.corner_radius_top_right = 48
box.corner_radius_bottom_left = 16
box.corner_radius_bottom_right = 16
box.shadow_color = Color(Brand.SKY_BLUE, 0.35)
box.shadow_size = 10
box.set_border_width_all(4)
box.corner_radius_top_left = 90
box.corner_radius_top_right = 90
box.corner_radius_bottom_left = 20
box.corner_radius_bottom_right = 20
box.shadow_color = Color(Brand.SKY_BLUE, 0.45)
box.shadow_size = 18
return box
@@ -445,15 +505,19 @@ func _build_reward_row(dark: bool, reward: Dictionary) -> HBoxContainer:
row.add_theme_constant_override("separation", 10)
var icon_panel := PanelContainer.new()
icon_panel.custom_minimum_size = Vector2(32, 32)
icon_panel.custom_minimum_size = Vector2(36, 36)
icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER
icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 16))
icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 18))
var icon := TextureRect.new()
icon.texture = load("res://assets/icons/lucide/%s.svg" % str(ICON_BY_REWARD_KEY.get(key, "star")))
icon.custom_minimum_size = Vector2(16, 16)
var spec: Dictionary = REWARD_ICONS.get(key, {"texture": SLOT_COIN, "modulate": false})
icon.texture = spec.get("texture")
icon.custom_minimum_size = Vector2(22, 22)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.modulate = accent
if spec.get("modulate", false):
icon.modulate = accent
elif spec.get("tint", false):
icon.modulate = Color("94a3b8")
icon_panel.add_child(icon)
row.add_child(icon_panel)
@@ -563,8 +627,7 @@ func _spin_one_reel(cells: Array, duration: float) -> void:
elapsed += interval
interval = minf(interval * 1.18, 0.22)
for cell in cells:
cell.texture = STAR_ICON
cell.modulate = Brand.AMBER
cell.texture = SLOT_COIN
func _play_coin_burst(coins_won: int) -> void:
@@ -580,8 +643,7 @@ func _play_coin_burst(coins_won: int) -> void:
var stagger := 0.035
for index in range(coin_count):
var coin := TextureRect.new()
coin.texture = STAR_ICON
coin.modulate = Brand.AMBER
coin.texture = SLOT_COIN
coin.custom_minimum_size = Vector2(16, 16)
coin.size = Vector2(16, 16)
coin.top_level = true