Replace hand-coded cabinet/lever/icons with real generated art
The user generated actual 3D-rendered assets (cabinet frame, lever, reel-symbol sprite sheet) via an image model - swaps out the StyleBoxFlat-approximated cabinet, Tween-animated marquee lights/glow, and gradient-SVG icons from the last pass for the real thing. The generated PNGs had no real alpha channel (a baked checkerboard pattern standing in for transparency, a known limitation of some image-gen output) - added a flood-fill background remover (BFS from the image border, matching near-white/grey checkerboard tones) to produce genuinely transparent PNGs, then sliced the sprite sheet into 6 individual icons and measured the cabinet's empty reel-window rect as a fraction of the frame image so the real 3x3 reel grid and lever can be positioned precisely over the static art via anchors. Verified with actual rendered screenshots at each step (window alignment, then lever sizing/position) rather than just structural checks, since this is exactly the kind of thing that looks fine in code and wrong on screen.
This commit is contained in:
@@ -3,13 +3,17 @@ extends VBoxContainer
|
||||
const Brand := preload("res://scripts/ui/medmind_style.gd")
|
||||
const STAR_ICON := preload("res://assets/icons/lucide/star.svg")
|
||||
const CLOCK_ICON := preload("res://assets/icons/lucide/clock-3.svg")
|
||||
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 CABINET_FRAME := preload("res://assets/slot_machine/cabinet_frame.png")
|
||||
const LEVER_TEXTURE := preload("res://assets/slot_machine/lever.png")
|
||||
const SLOT_COIN := preload("res://assets/slot_machine/slot_coin.png")
|
||||
const SLOT_GEM := preload("res://assets/slot_machine/slot_gem.png")
|
||||
const SLOT_POTION := preload("res://assets/slot_machine/slot_potion.png")
|
||||
const SLOT_SHIELD := preload("res://assets/slot_machine/slot_shield.png")
|
||||
const SLOT_COIN_STACK := preload("res://assets/slot_machine/slot_coin_stack.png")
|
||||
const SLOT_GIFT := preload("res://assets/slot_machine/slot_gift.png")
|
||||
const REEL_SYMBOLS := [SLOT_COIN, SLOT_GEM, SLOT_POTION, SLOT_SHIELD, SLOT_COIN_STACK, SLOT_GIFT]
|
||||
# fraction-of-image rect of the cabinet's empty reel window, measured against cabinet_frame.png
|
||||
const WINDOW_RECT := Rect2(0.203, 0.246, 0.591, 0.604)
|
||||
const REWARD_ICONS := {
|
||||
"jackpot": {"texture": SLOT_COIN, "modulate": false},
|
||||
"epic": {"texture": SLOT_GEM, "modulate": false},
|
||||
@@ -23,7 +27,7 @@ const REWARD_ICONS := {
|
||||
var main: Control
|
||||
|
||||
var subtitle_label: Label
|
||||
var machine_panel: PanelContainer
|
||||
var machine_root: Control
|
||||
var reel_columns: Array = []
|
||||
var win_up_to_card: PanelContainer
|
||||
var win_up_to_label: Label
|
||||
@@ -55,7 +59,13 @@ func _ready() -> void:
|
||||
body.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
add_child(body)
|
||||
|
||||
_build_machine(body)
|
||||
var left_column := VBoxContainer.new()
|
||||
left_column.add_theme_constant_override("separation", 16)
|
||||
left_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
body.add_child(left_column)
|
||||
|
||||
_build_machine(left_column)
|
||||
_build_controls(left_column)
|
||||
_build_rewards_panel(body)
|
||||
|
||||
status_label = Label.new()
|
||||
@@ -94,48 +104,35 @@ 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)
|
||||
machine_root = Control.new()
|
||||
machine_root.custom_minimum_size = Vector2(560, 560)
|
||||
machine_root.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
machine_root.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
parent.add_child(machine_root)
|
||||
|
||||
var margin := MarginContainer.new()
|
||||
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 frame := TextureRect.new()
|
||||
frame.texture = CABINET_FRAME
|
||||
frame.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
frame.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
frame.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT
|
||||
frame.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
machine_root.add_child(frame)
|
||||
|
||||
var column := VBoxContainer.new()
|
||||
column.add_theme_constant_override("separation", 20)
|
||||
margin.add_child(column)
|
||||
var window := Control.new()
|
||||
window.anchor_left = WINDOW_RECT.position.x
|
||||
window.anchor_top = WINDOW_RECT.position.y
|
||||
window.anchor_right = WINDOW_RECT.position.x + WINDOW_RECT.size.x
|
||||
window.anchor_bottom = WINDOW_RECT.position.y + WINDOW_RECT.size.y
|
||||
window.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
machine_root.add_child(window)
|
||||
|
||||
_build_marquee(column)
|
||||
|
||||
var reel_row := HBoxContainer.new()
|
||||
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", 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 center := CenterContainer.new()
|
||||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
window.add_child(center)
|
||||
|
||||
var reels_grid := HBoxContainer.new()
|
||||
reels_grid.add_theme_constant_override("separation", 18)
|
||||
reel_margin.add_child(reels_grid)
|
||||
reels_grid.add_theme_constant_override("separation", 16)
|
||||
center.add_child(reels_grid)
|
||||
|
||||
reel_columns.clear()
|
||||
for _column_index in range(3):
|
||||
@@ -146,19 +143,31 @@ func _build_machine(parent: Control) -> void:
|
||||
for _row_index in range(3):
|
||||
var icon := TextureRect.new()
|
||||
icon.texture = REEL_SYMBOLS[randi() % REEL_SYMBOLS.size()]
|
||||
icon.custom_minimum_size = Vector2(58, 58)
|
||||
icon.custom_minimum_size = Vector2(66, 66)
|
||||
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())
|
||||
var lever := TextureRect.new()
|
||||
lever.texture = LEVER_TEXTURE
|
||||
lever.anchor_left = 0.80
|
||||
lever.anchor_right = 1.06
|
||||
lever.anchor_top = 0.24
|
||||
lever.anchor_bottom = 0.88
|
||||
lever.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
lever.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT
|
||||
lever.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
machine_root.add_child(lever)
|
||||
|
||||
|
||||
func _build_controls(parent: Control) -> void:
|
||||
var dark := UIThemeService.is_dark_mode()
|
||||
|
||||
win_up_to_card = PanelContainer.new()
|
||||
win_up_to_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false))
|
||||
column.add_child(win_up_to_card)
|
||||
parent.add_child(win_up_to_card)
|
||||
var win_margin := MarginContainer.new()
|
||||
win_margin.add_theme_constant_override("margin_left", 14)
|
||||
win_margin.add_theme_constant_override("margin_right", 14)
|
||||
@@ -174,7 +183,7 @@ func _build_machine(parent: Control) -> void:
|
||||
|
||||
var controls_row := HBoxContainer.new()
|
||||
controls_row.add_theme_constant_override("separation", 12)
|
||||
column.add_child(controls_row)
|
||||
parent.add_child(controls_row)
|
||||
|
||||
cost_card = PanelContainer.new()
|
||||
cost_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false))
|
||||
@@ -240,135 +249,7 @@ func _build_machine(parent: Control) -> void:
|
||||
info_label.add_theme_font_size_override("font_size", 11)
|
||||
info_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
info_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
column.add_child(info_label)
|
||||
|
||||
|
||||
func _build_marquee(column: VBoxContainer) -> void:
|
||||
var row := HBoxContainer.new()
|
||||
row.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
row.add_theme_constant_override("separation", 14)
|
||||
column.add_child(row)
|
||||
row.add_child(_marquee_star())
|
||||
row.add_child(_marquee_glow_label())
|
||||
row.add_child(_marquee_star())
|
||||
|
||||
|
||||
func _marquee_star() -> TextureRect:
|
||||
var star := TextureRect.new()
|
||||
star.texture = STAR_ICON
|
||||
star.modulate = Brand.SKY_BLUE
|
||||
star.custom_minimum_size = Vector2(16, 16)
|
||||
star.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
star.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
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
|
||||
wrap.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
wrap.add_theme_constant_override("separation", 0)
|
||||
|
||||
var knob := PanelContainer.new()
|
||||
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(15)
|
||||
knob_box.corner_detail = 20
|
||||
knob_box.anti_aliasing = false
|
||||
knob.add_theme_stylebox_override("panel", knob_box)
|
||||
wrap.add_child(knob)
|
||||
|
||||
var rod := ColorRect.new()
|
||||
rod.color = Color("64748b")
|
||||
rod.custom_minimum_size = Vector2(6, 140)
|
||||
rod.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
wrap.add_child(rod)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
func _cabinet_box() -> StyleBoxFlat:
|
||||
var box := StyleBoxFlat.new()
|
||||
box.bg_color = Color("0b1220")
|
||||
box.border_color = Brand.SKY_BLUE
|
||||
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
|
||||
|
||||
|
||||
func _reel_screen_box() -> StyleBoxFlat:
|
||||
var box := StyleBoxFlat.new()
|
||||
box.bg_color = Color("e2e8f0")
|
||||
box.border_color = Color("94a3b8")
|
||||
box.set_border_width_all(2)
|
||||
box.set_corner_radius_all(10)
|
||||
return box
|
||||
parent.add_child(info_label)
|
||||
|
||||
|
||||
func _build_rewards_panel(parent: Control) -> void:
|
||||
@@ -636,7 +517,7 @@ func _play_coin_burst(coins_won: int) -> void:
|
||||
var credits_badge: Control = main.get("credits_badge")
|
||||
if credits_badge == null or not is_instance_valid(credits_badge):
|
||||
return
|
||||
var origin := machine_panel.get_global_rect().get_center()
|
||||
var origin := machine_root.get_global_rect().get_center()
|
||||
var target := credits_badge.get_global_rect().get_center()
|
||||
var coin_count := clampi(3 + coins_won / 500, 4, 10)
|
||||
var flight_time := 0.55
|
||||
|
||||
Reference in New Issue
Block a user