Moves Slot Machine into the sidebar's account section (alongside RZ Shop/Settings/Log out) instead of being reachable only from the shop - removes the shop's promo banner and the screen's "Back to Shop" link accordingly. Also closes a chunk of the visual gap against the mockup: a proper cabinet-style panel (arched top corners, glowing border), a light "screen" panel holding a real 3x3 reel grid instead of 3 single icons, a decorative lever, an "RZ" marquee, and the cost/spin/auto-spin controls each in their own bordered box. Added two new vendored icons (gem, flask-conical) for reel variety and reward-list icons.
550 lines
20 KiB
GDScript
550 lines
20 KiB
GDScript
extends VBoxContainer
|
|
|
|
const Brand := preload("res://scripts/ui/medmind_style.gd")
|
|
const ICON_BY_SKU := {
|
|
"exam_token_1": "clock-3",
|
|
"qbank_reset_1": "rotate-ccw",
|
|
}
|
|
const FEATURES_BY_SKU := {
|
|
"exam_token_1": ["Full 6-block exam", "Timed & scored", "Performance analytics"],
|
|
"qbank_reset_1": ["Reset eligibility", "Keep your stats", "Smart spaced retention"],
|
|
}
|
|
const POPULAR_SKUS := ["exam_token_1"]
|
|
const TAB_DEFS := [
|
|
{"label": "Featured", "icon": "star", "active": true},
|
|
{"label": "Tokens", "icon": "clipboard-list", "active": false},
|
|
{"label": "Boosts", "icon": "activity", "active": false},
|
|
{"label": "Content", "icon": "book-open", "active": false},
|
|
{"label": "Customization", "icon": "settings", "active": false},
|
|
{"label": "Bundles", "icon": "clipboard-plus", "active": false},
|
|
]
|
|
|
|
var main: Control
|
|
var subtitle_label: Label
|
|
var summary_card: PanelContainer
|
|
var summary_icon_panel: PanelContainer
|
|
var summary_icon: TextureRect
|
|
var balance_value_label: Label
|
|
var coin_caption_label: Label
|
|
var inventory_label: Label
|
|
var tab_controls: Array = []
|
|
var section_heading: Label
|
|
var section_subtitle: Label
|
|
var products_grid: HFlowContainer
|
|
var status_label: Label
|
|
var confirm_dialog: ConfirmationDialog
|
|
var footer_card: PanelContainer
|
|
var footer_icon_panel: PanelContainer
|
|
var footer_icon: TextureRect
|
|
var footer_heading: Label
|
|
var footer_description: Label
|
|
var pending_sku := ""
|
|
|
|
|
|
func _ready() -> void:
|
|
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
add_theme_constant_override("separation", 18)
|
|
|
|
_build_header()
|
|
_build_summary_card()
|
|
_build_tabs()
|
|
|
|
section_heading = Label.new()
|
|
section_heading.text = "Featured"
|
|
section_heading.add_theme_font_override("font", Brand.font)
|
|
section_heading.add_theme_font_size_override("font_size", 18)
|
|
add_child(section_heading)
|
|
section_subtitle = Label.new()
|
|
section_subtitle.text = "Hand-picked items to supercharge your Step 3 prep."
|
|
section_subtitle.add_theme_font_override("font", Brand.font)
|
|
section_subtitle.add_theme_font_size_override("font_size", 12)
|
|
section_subtitle.add_theme_color_override("font_color", Brand.muted_color(UIThemeService.is_dark_mode()))
|
|
add_child(section_subtitle)
|
|
|
|
products_grid = HFlowContainer.new()
|
|
products_grid.add_theme_constant_override("h_separation", 14)
|
|
products_grid.add_theme_constant_override("v_separation", 14)
|
|
add_child(products_grid)
|
|
|
|
status_label = Label.new()
|
|
status_label.add_theme_font_override("font", Brand.font)
|
|
status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
add_child(status_label)
|
|
|
|
confirm_dialog = ConfirmationDialog.new()
|
|
confirm_dialog.confirmed.connect(_confirm_purchase)
|
|
add_child(confirm_dialog)
|
|
|
|
_build_footer()
|
|
|
|
CreditsService.credits_changed.connect(_refresh)
|
|
CreditsService.credits_failed.connect(_show_error)
|
|
CreditsService.store_operation_completed.connect(_show_message)
|
|
UIThemeService.theme_changed.connect(func(_dark: bool): _refresh_theme_controls(); _refresh())
|
|
_refresh()
|
|
|
|
|
|
func on_show(_payload: Variant = null) -> void:
|
|
CreditsService.refresh()
|
|
_refresh()
|
|
|
|
|
|
func _build_header() -> void:
|
|
var title := Label.new()
|
|
title.text = "RZ Coin Shop"
|
|
title.add_theme_font_override("font", Brand.font)
|
|
title.add_theme_font_size_override("font_size", 30)
|
|
add_child(title)
|
|
subtitle_label = Label.new()
|
|
subtitle_label.text = "Spend your RZ Coins on premium content and boosts."
|
|
subtitle_label.add_theme_font_override("font", Brand.font)
|
|
subtitle_label.add_theme_font_size_override("font_size", 14)
|
|
subtitle_label.add_theme_color_override("font_color", Brand.muted_color(UIThemeService.is_dark_mode()))
|
|
add_child(subtitle_label)
|
|
|
|
|
|
func _build_summary_card() -> void:
|
|
var dark := UIThemeService.is_dark_mode()
|
|
summary_card = PanelContainer.new()
|
|
summary_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.AMBER))
|
|
summary_card.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
|
add_child(summary_card)
|
|
|
|
var margin := MarginContainer.new()
|
|
margin.add_theme_constant_override("margin_left", 16)
|
|
margin.add_theme_constant_override("margin_right", 16)
|
|
margin.add_theme_constant_override("margin_top", 12)
|
|
margin.add_theme_constant_override("margin_bottom", 12)
|
|
summary_card.add_child(margin)
|
|
|
|
var row := HBoxContainer.new()
|
|
row.add_theme_constant_override("separation", 14)
|
|
margin.add_child(row)
|
|
|
|
summary_icon_panel = PanelContainer.new()
|
|
summary_icon_panel.custom_minimum_size = Vector2(40, 40)
|
|
summary_icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
|
summary_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.AMBER, 20))
|
|
summary_icon = TextureRect.new()
|
|
summary_icon.texture = load("res://assets/icons/lucide/star.svg")
|
|
summary_icon.custom_minimum_size = Vector2(22, 22)
|
|
summary_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
summary_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
summary_icon.modulate = Brand.AMBER
|
|
summary_icon_panel.add_child(summary_icon)
|
|
row.add_child(summary_icon_panel)
|
|
|
|
var copy := VBoxContainer.new()
|
|
copy.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
|
copy.add_theme_constant_override("separation", 0)
|
|
balance_value_label = Label.new()
|
|
balance_value_label.add_theme_font_override("font", Brand.font)
|
|
balance_value_label.add_theme_font_size_override("font_size", 22)
|
|
balance_value_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
|
copy.add_child(balance_value_label)
|
|
coin_caption_label = Label.new()
|
|
coin_caption_label.text = "RZ Coins"
|
|
coin_caption_label.add_theme_font_override("font", Brand.font)
|
|
coin_caption_label.add_theme_font_size_override("font_size", 12)
|
|
coin_caption_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
copy.add_child(coin_caption_label)
|
|
inventory_label = Label.new()
|
|
inventory_label.add_theme_font_override("font", Brand.font)
|
|
inventory_label.add_theme_font_size_override("font_size", 12)
|
|
inventory_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
copy.add_child(inventory_label)
|
|
row.add_child(copy)
|
|
|
|
|
|
func _build_tabs() -> void:
|
|
var dark := UIThemeService.is_dark_mode()
|
|
tab_controls.clear()
|
|
|
|
var tabs_row := HFlowContainer.new()
|
|
tabs_row.add_theme_constant_override("h_separation", 20)
|
|
tabs_row.add_theme_constant_override("v_separation", 8)
|
|
add_child(tabs_row)
|
|
|
|
for tab_def_variant in TAB_DEFS:
|
|
var tab_def: Dictionary = tab_def_variant
|
|
var tab := VBoxContainer.new()
|
|
tab.add_theme_constant_override("separation", 6)
|
|
tabs_row.add_child(tab)
|
|
|
|
var content := HBoxContainer.new()
|
|
content.add_theme_constant_override("separation", 6)
|
|
tab.add_child(content)
|
|
|
|
var icon := TextureRect.new()
|
|
icon.texture = load("res://assets/icons/lucide/%s.svg" % str(tab_def.get("icon", "star")))
|
|
icon.custom_minimum_size = Vector2(16, 16)
|
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
content.add_child(icon)
|
|
|
|
var label := Label.new()
|
|
label.text = str(tab_def.get("label", ""))
|
|
label.add_theme_font_override("font", Brand.font)
|
|
label.add_theme_font_size_override("font_size", 14)
|
|
content.add_child(label)
|
|
|
|
var underline := ColorRect.new()
|
|
underline.custom_minimum_size = Vector2(0, 2)
|
|
tab.add_child(underline)
|
|
|
|
var active := bool(tab_def.get("active", false))
|
|
tab_controls.append({"icon": icon, "label": label, "underline": underline, "active": active})
|
|
|
|
var divider := ColorRect.new()
|
|
divider.custom_minimum_size = Vector2(0, 1)
|
|
add_child(divider)
|
|
tab_controls.append({"divider": divider})
|
|
|
|
_apply_tab_theme(dark)
|
|
|
|
|
|
func _apply_tab_theme(dark: bool) -> void:
|
|
for entry_variant in tab_controls:
|
|
var entry: Dictionary = entry_variant
|
|
if entry.has("divider"):
|
|
(entry["divider"] as ColorRect).color = Color("2a365a") if dark else Brand.LIGHT_BORDER
|
|
continue
|
|
var active := bool(entry["active"])
|
|
var color := Brand.ROYAL_BLUE if active else Brand.muted_color(dark)
|
|
(entry["icon"] as TextureRect).modulate = color
|
|
(entry["label"] as Label).add_theme_color_override("font_color", color)
|
|
(entry["underline"] as ColorRect).color = Brand.ROYAL_BLUE if active else Color(0, 0, 0, 0)
|
|
|
|
|
|
func _build_footer() -> void:
|
|
var dark := UIThemeService.is_dark_mode()
|
|
footer_card = PanelContainer.new()
|
|
footer_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false))
|
|
add_child(footer_card)
|
|
|
|
var margin := MarginContainer.new()
|
|
margin.add_theme_constant_override("margin_left", 16)
|
|
margin.add_theme_constant_override("margin_right", 16)
|
|
margin.add_theme_constant_override("margin_top", 12)
|
|
margin.add_theme_constant_override("margin_bottom", 12)
|
|
footer_card.add_child(margin)
|
|
|
|
var row := HBoxContainer.new()
|
|
row.add_theme_constant_override("separation", 14)
|
|
margin.add_child(row)
|
|
|
|
footer_icon_panel = PanelContainer.new()
|
|
footer_icon_panel.custom_minimum_size = Vector2(36, 36)
|
|
footer_icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
|
footer_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.ROYAL_BLUE, 18))
|
|
footer_icon = TextureRect.new()
|
|
footer_icon.texture = load("res://assets/icons/lucide/activity.svg")
|
|
footer_icon.custom_minimum_size = Vector2(18, 18)
|
|
footer_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
footer_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
footer_icon.modulate = Brand.ROYAL_BLUE
|
|
footer_icon_panel.add_child(footer_icon)
|
|
row.add_child(footer_icon_panel)
|
|
|
|
var copy := VBoxContainer.new()
|
|
copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
copy.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
|
copy.add_theme_constant_override("separation", 2)
|
|
footer_heading = Label.new()
|
|
footer_heading.text = "How to earn RZ Coins"
|
|
footer_heading.add_theme_font_override("font", Brand.font)
|
|
footer_heading.add_theme_font_size_override("font_size", 15)
|
|
footer_heading.add_theme_color_override("font_color", Brand.text_color(dark))
|
|
copy.add_child(footer_heading)
|
|
footer_description = Label.new()
|
|
footer_description.text = "Answer questions correctly on the first try, within the time limit."
|
|
footer_description.add_theme_font_override("font", Brand.font)
|
|
footer_description.add_theme_font_size_override("font_size", 12)
|
|
footer_description.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
footer_description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
copy.add_child(footer_description)
|
|
row.add_child(copy)
|
|
|
|
|
|
func _refresh_theme_controls() -> void:
|
|
var dark := UIThemeService.is_dark_mode()
|
|
subtitle_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
summary_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.AMBER))
|
|
summary_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.AMBER, 20))
|
|
balance_value_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
|
coin_caption_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
inventory_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
_apply_tab_theme(dark)
|
|
section_subtitle.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
footer_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false))
|
|
footer_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.ROYAL_BLUE, 18))
|
|
footer_heading.add_theme_color_override("font_color", Brand.text_color(dark))
|
|
footer_description.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
|
|
|
|
func _refresh() -> void:
|
|
var dark := UIThemeService.is_dark_mode()
|
|
balance_value_label.text = _format_thousands(CreditsService.credits)
|
|
inventory_label.text = "%d exam token(s) · %d QBank reset token(s)" % [
|
|
CreditsService.exam_tokens, CreditsService.qbank_reset_tokens]
|
|
for child in products_grid.get_children():
|
|
child.queue_free()
|
|
for product_variant in CreditsService.products:
|
|
if typeof(product_variant) != TYPE_DICTIONARY:
|
|
continue
|
|
var product: Dictionary = product_variant
|
|
var sku := str(product.get("sku", ""))
|
|
var price := int(product.get("coinPrice", 0))
|
|
products_grid.add_child(_build_item_card(
|
|
dark,
|
|
ICON_BY_SKU.get(sku, "star"),
|
|
Brand.ROYAL_BLUE,
|
|
str(product.get("name", "Store item")),
|
|
str(product.get("description", "")),
|
|
FEATURES_BY_SKU.get(sku, []),
|
|
POPULAR_SKUS.has(sku),
|
|
price,
|
|
"Buy",
|
|
CreditsService.credits < price,
|
|
_ask_purchase.bind(sku, str(product.get("name", "item")), price)))
|
|
if CreditsService.qbank_reset_tokens > 0:
|
|
products_grid.add_child(_build_item_card(
|
|
dark,
|
|
"rotate-ccw",
|
|
Brand.SUCCESS,
|
|
"Use QBank Reset Token",
|
|
"Question eligibility resets; ELO, history, bookmarks, notes, reviews, and earning limits are preserved.",
|
|
FEATURES_BY_SKU.get("qbank_reset_1", []),
|
|
false,
|
|
-1,
|
|
"Use token",
|
|
false,
|
|
_ask_reset))
|
|
|
|
|
|
func _build_item_card(
|
|
dark: bool,
|
|
icon_name: String,
|
|
accent: Color,
|
|
title: String,
|
|
description_text: String,
|
|
features: Array,
|
|
popular: bool,
|
|
price: int,
|
|
button_text: String,
|
|
button_disabled: bool,
|
|
on_press: Callable
|
|
) -> PanelContainer:
|
|
var card := _new_card(dark)
|
|
var content: VBoxContainer = card.get_child(0).get_child(0)
|
|
|
|
var top_row := HBoxContainer.new()
|
|
content.add_child(top_row)
|
|
top_row.add_child(_card_icon(dark, icon_name, accent))
|
|
var badge_spacer := Control.new()
|
|
badge_spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
top_row.add_child(badge_spacer)
|
|
if popular:
|
|
top_row.add_child(_popular_badge())
|
|
|
|
var name_label := Label.new()
|
|
name_label.text = title
|
|
name_label.add_theme_font_override("font", Brand.font)
|
|
name_label.add_theme_font_size_override("font_size", 16)
|
|
name_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
|
content.add_child(name_label)
|
|
|
|
var description := Label.new()
|
|
description.text = description_text
|
|
description.add_theme_font_override("font", Brand.font)
|
|
description.add_theme_font_size_override("font_size", 12)
|
|
description.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
description.custom_minimum_size = Vector2(190, 0)
|
|
content.add_child(description)
|
|
|
|
if not features.is_empty():
|
|
var bullets := VBoxContainer.new()
|
|
bullets.add_theme_constant_override("separation", 4)
|
|
content.add_child(bullets)
|
|
for feature_text in features:
|
|
bullets.add_child(_feature_row(dark, accent, str(feature_text)))
|
|
|
|
var divider := ColorRect.new()
|
|
divider.custom_minimum_size = Vector2(0, 1)
|
|
divider.color = Color("2a365a") if dark else Brand.LIGHT_BORDER
|
|
content.add_child(divider)
|
|
|
|
var footer_row := HBoxContainer.new()
|
|
footer_row.add_theme_constant_override("separation", 8)
|
|
content.add_child(footer_row)
|
|
if price >= 0:
|
|
footer_row.add_child(_price_tag(dark, price))
|
|
var spacer := Control.new()
|
|
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
footer_row.add_child(spacer)
|
|
var button := Button.new()
|
|
button.text = button_text
|
|
button.custom_minimum_size = Vector2(64, 34)
|
|
button.add_theme_font_override("font", Brand.font)
|
|
_style_buy_button(button)
|
|
button.disabled = button_disabled
|
|
button.pressed.connect(on_press)
|
|
footer_row.add_child(button)
|
|
|
|
return card
|
|
|
|
|
|
func _new_card(dark: bool) -> PanelContainer:
|
|
var card := PanelContainer.new()
|
|
card.custom_minimum_size = Vector2(230, 0)
|
|
card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.ROYAL_BLUE))
|
|
var margin := MarginContainer.new()
|
|
margin.add_theme_constant_override("margin_left", 16)
|
|
margin.add_theme_constant_override("margin_right", 16)
|
|
margin.add_theme_constant_override("margin_top", 14)
|
|
margin.add_theme_constant_override("margin_bottom", 14)
|
|
card.add_child(margin)
|
|
var content := VBoxContainer.new()
|
|
content.add_theme_constant_override("separation", 8)
|
|
margin.add_child(content)
|
|
return card
|
|
|
|
|
|
func _card_icon(dark: bool, icon_name: String, accent: Color) -> PanelContainer:
|
|
var icon_panel := PanelContainer.new()
|
|
icon_panel.custom_minimum_size = Vector2(48, 48)
|
|
icon_panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
|
icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 24))
|
|
var icon := TextureRect.new()
|
|
icon.texture = load("res://assets/icons/lucide/%s.svg" % icon_name)
|
|
icon.custom_minimum_size = Vector2(24, 24)
|
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
icon.modulate = accent
|
|
icon_panel.add_child(icon)
|
|
return icon_panel
|
|
|
|
|
|
func _popular_badge() -> PanelContainer:
|
|
var badge := PanelContainer.new()
|
|
badge.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
|
var box := StyleBoxFlat.new()
|
|
box.bg_color = Brand.ROYAL_BLUE
|
|
box.set_corner_radius_all(6)
|
|
box.content_margin_left = 8
|
|
box.content_margin_right = 8
|
|
box.content_margin_top = 3
|
|
box.content_margin_bottom = 3
|
|
badge.add_theme_stylebox_override("panel", box)
|
|
var label := Label.new()
|
|
label.text = "Popular"
|
|
label.add_theme_font_override("font", Brand.font)
|
|
label.add_theme_font_size_override("font_size", 11)
|
|
label.add_theme_color_override("font_color", Brand.DARK_TEXT)
|
|
badge.add_child(label)
|
|
return badge
|
|
|
|
|
|
func _feature_row(dark: bool, accent: Color, text: String) -> HBoxContainer:
|
|
var row := HBoxContainer.new()
|
|
row.add_theme_constant_override("separation", 6)
|
|
var icon := TextureRect.new()
|
|
icon.texture = load("res://assets/icons/lucide/circle-check.svg")
|
|
icon.custom_minimum_size = Vector2(14, 14)
|
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
icon.modulate = accent
|
|
row.add_child(icon)
|
|
var label := Label.new()
|
|
label.text = text
|
|
label.add_theme_font_override("font", Brand.font)
|
|
label.add_theme_font_size_override("font_size", 12)
|
|
label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
|
row.add_child(label)
|
|
return row
|
|
|
|
|
|
func _price_tag(dark: bool, price: int) -> HBoxContainer:
|
|
var row := HBoxContainer.new()
|
|
row.add_theme_constant_override("separation", 4)
|
|
var icon := TextureRect.new()
|
|
icon.texture = load("res://assets/icons/lucide/star.svg")
|
|
icon.custom_minimum_size = Vector2(14, 14)
|
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
icon.modulate = Brand.AMBER
|
|
row.add_child(icon)
|
|
var label := Label.new()
|
|
label.text = _format_thousands(price)
|
|
label.add_theme_font_override("font", Brand.font)
|
|
label.add_theme_font_size_override("font_size", 14)
|
|
label.add_theme_color_override("font_color", Brand.text_color(dark))
|
|
row.add_child(label)
|
|
return row
|
|
|
|
|
|
func _style_buy_button(button: Button) -> void:
|
|
var normal := StyleBoxFlat.new()
|
|
normal.bg_color = Brand.ROYAL_BLUE
|
|
normal.set_corner_radius_all(8)
|
|
var hover := StyleBoxFlat.new()
|
|
hover.bg_color = Brand.SKY_BLUE
|
|
hover.set_corner_radius_all(8)
|
|
var disabled := StyleBoxFlat.new()
|
|
disabled.bg_color = Color(Brand.ROYAL_BLUE, 0.35)
|
|
disabled.set_corner_radius_all(8)
|
|
button.add_theme_stylebox_override("normal", normal)
|
|
button.add_theme_stylebox_override("hover", hover)
|
|
button.add_theme_stylebox_override("pressed", normal)
|
|
button.add_theme_stylebox_override("disabled", disabled)
|
|
button.add_theme_color_override("font_color", Brand.DARK_TEXT)
|
|
button.add_theme_color_override("font_hover_color", Brand.DARK_TEXT)
|
|
button.add_theme_color_override("font_disabled_color", Color(Brand.DARK_TEXT, 0.7))
|
|
|
|
|
|
func _ask_purchase(sku: String, product_name: String, price: int) -> void:
|
|
pending_sku = sku
|
|
confirm_dialog.title = "Confirm purchase"
|
|
confirm_dialog.dialog_text = "Buy %s for %d Rz Coins?" % [product_name, price]
|
|
confirm_dialog.popup_centered()
|
|
|
|
|
|
func _confirm_purchase() -> void:
|
|
if pending_sku == "__reset__":
|
|
CreditsService.reset_qbank()
|
|
elif not pending_sku.is_empty():
|
|
CreditsService.purchase(pending_sku)
|
|
pending_sku = ""
|
|
|
|
|
|
func _ask_reset() -> void:
|
|
pending_sku = "__reset__"
|
|
confirm_dialog.title = "Reset QBank eligibility"
|
|
confirm_dialog.dialog_text = "Make all QBank questions eligible again? ELO, answer history, bookmarks, notes, reviews, and the rolling coin cap will be preserved."
|
|
confirm_dialog.popup_centered()
|
|
|
|
|
|
func _show_error(message: String) -> void:
|
|
status_label.text = message
|
|
status_label.add_theme_color_override("font_color", Brand.MAGENTA)
|
|
|
|
|
|
func _show_message(message: String) -> void:
|
|
status_label.text = message
|
|
status_label.add_theme_color_override("font_color", Brand.SUCCESS)
|
|
_refresh()
|
|
|
|
|
|
func _format_thousands(value: int) -> String:
|
|
var digits := str(absi(value))
|
|
var grouped := ""
|
|
var count := 0
|
|
for index in range(digits.length() - 1, -1, -1):
|
|
grouped = digits[index] + grouped
|
|
count += 1
|
|
if count % 3 == 0 and index != 0:
|
|
grouped = "," + grouped
|
|
return ("-" if value < 0 else "") + grouped
|