stepstead-client/scripts/market_panel.gd
jts 6a1706051d İlk commit: Stepstead Godot istemci (4.6 Mobile)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 11:31:35 +03:00

293 lines
9.4 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends PanelContainer
## Pazar paneli — oyuncu ilanları + NPC vendor. Kod ile kurulur, hud.gd ekler.
## Node adı "MarketPanel" (world.gd blokaj kontrolü).
## Görüntüleme her yerden; ilan verme / satın alma / vendor satışı sadece pazar'da
## (sunucu zaten doğrular, UI da butonları kilitler).
const PANEL_HEIGHT := 640.0
const MARKET_LOCATION := "pazar"
var _tab_listings: Button
var _tab_sell: Button
var _tab_stock: Button
var _list: VBoxContainer
var _scroll: ScrollContainer
var _search: LineEdit
var _info: Label
var _tab := "listings" # "listings" | "sell" | "stock"
func _ready() -> void:
name = "MarketPanel"
visible = false
_build_ui()
func _build_ui() -> void:
set_anchors_preset(Control.PRESET_BOTTOM_WIDE)
offset_top = -PANEL_HEIGHT
offset_left = 8
offset_right = -8
offset_bottom = -8
var sb := StyleBoxFlat.new()
sb.bg_color = Color(0.13, 0.11, 0.08, 0.97)
sb.set_corner_radius_all(10)
add_theme_stylebox_override("panel", sb)
var margin := MarginContainer.new()
margin.add_theme_constant_override("margin_left", 12)
margin.add_theme_constant_override("margin_right", 12)
margin.add_theme_constant_override("margin_top", 8)
margin.add_theme_constant_override("margin_bottom", 8)
add_child(margin)
var vbox := VBoxContainer.new()
vbox.add_theme_constant_override("separation", 6)
margin.add_child(vbox)
var top := HBoxContainer.new()
var title := Label.new()
title.text = "🏪 Pazar"
title.add_theme_font_size_override("font_size", 18)
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
top.add_child(title)
_tab_listings = Button.new()
_tab_listings.text = "📜 İlanlar"
_tab_listings.toggle_mode = true
_tab_listings.button_pressed = true
_tab_listings.pressed.connect(func() -> void: _switch("listings"))
top.add_child(_tab_listings)
_tab_sell = Button.new()
_tab_sell.text = "🎒 Sat"
_tab_sell.toggle_mode = true
_tab_sell.pressed.connect(func() -> void: _switch("sell"))
top.add_child(_tab_sell)
_tab_stock = Button.new()
_tab_stock.text = "🧰 Vendor"
_tab_stock.toggle_mode = true
_tab_stock.pressed.connect(func() -> void: _switch("stock"))
top.add_child(_tab_stock)
var close_btn := Button.new()
close_btn.text = ""
close_btn.pressed.connect(func() -> void: visible = false)
top.add_child(close_btn)
vbox.add_child(top)
_info = Label.new()
_info.add_theme_font_size_override("font_size", 13)
_info.add_theme_color_override("font_color", Color(0.75, 0.7, 0.55))
vbox.add_child(_info)
_search = LineEdit.new()
_search.placeholder_text = "🔍 Ara…"
_search.text_submitted.connect(func(_t: String) -> void: refresh())
vbox.add_child(_search)
_scroll = ScrollContainer.new()
_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
vbox.add_child(_scroll)
_list = VBoxContainer.new()
_list.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_list.add_theme_constant_override("separation", 6)
_scroll.add_child(_list)
func _at_market() -> bool:
return str(GameState.character.get("location_code", "")) == MARKET_LOCATION
func open() -> void:
visible = true
refresh()
func _switch(tab: String) -> void:
_tab = tab
_tab_listings.button_pressed = tab == "listings"
_tab_sell.button_pressed = tab == "sell"
_tab_stock.button_pressed = tab == "stock"
_search.visible = tab == "listings"
refresh()
func refresh() -> void:
_info.text = ("🪙 %d" % int(GameState.character.get("gold", 0))) + \
("" if _at_market() else " • ⚠ İşlem için Pazar'a git (sadece vitrin)")
for c in _list.get_children():
c.queue_free()
if _tab == "listings":
await _render_listings()
elif _tab == "sell":
await _render_sell()
else:
await _render_stock()
func _render_listings() -> void:
var rows: Array = await GameState.market_listings(_search.text.strip_edges())
if rows.is_empty():
_list.add_child(_muted("ık ilan yok."))
return
for r in rows:
if r is Dictionary:
_list.add_child(_listing_row(r))
func _listing_row(r: Dictionary) -> HBoxContainer:
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 10)
var info := VBoxContainer.new()
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
var name_lbl := Label.new()
name_lbl.text = "%s ×%d" % [str(r.get("item_name", "")), int(r.get("qty", 0))]
name_lbl.add_theme_font_size_override("font_size", 15)
info.add_child(name_lbl)
var meta := Label.new()
meta.text = "🪙 %d/adet • %s" % [int(r.get("unit_price", 0)), str(r.get("seller_name", ""))]
meta.add_theme_font_size_override("font_size", 12)
meta.add_theme_color_override("font_color", Color(0.6, 0.58, 0.5))
info.add_child(meta)
row.add_child(info)
var btn := Button.new()
if bool(r.get("mine", false)):
btn.text = "İptal"
btn.pressed.connect(_on_cancel.bind(btn, int(r.get("id", 0))))
else:
var total: int = int(r.get("qty", 0)) * int(r.get("unit_price", 0))
btn.text = "Al (🪙 %d)" % total
if not _at_market():
btn.disabled = true
elif int(GameState.character.get("gold", 0)) < total:
btn.text = "🪙 yetersiz"
btn.disabled = true
else:
btn.pressed.connect(_on_buy.bind(btn, int(r.get("id", 0))))
row.add_child(btn)
return row
func _render_sell() -> void:
var rows: Array = await GameState.vendor_prices()
if rows.is_empty():
_list.add_child(_muted("Envanterin boş."))
return
var at_market := _at_market()
for r in rows:
if r is Dictionary:
_list.add_child(_sell_row(r, at_market))
func _sell_row(r: Dictionary, at_market: bool) -> VBoxContainer:
var box := VBoxContainer.new()
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 8)
var name_lbl := Label.new()
name_lbl.text = "%s ×%d" % [str(r.get("item_name", "")), int(r.get("qty", 0))]
name_lbl.add_theme_font_size_override("font_size", 15)
name_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_child(name_lbl)
var qty_spin := SpinBox.new()
qty_spin.min_value = 1
qty_spin.max_value = int(r.get("qty", 1))
qty_spin.value = 1
row.add_child(qty_spin)
var vend_btn := Button.new()
vend_btn.text = "Vendor 🪙 %d" % int(r.get("unit_price", 0))
vend_btn.disabled = not at_market
vend_btn.pressed.connect(_on_vendor_sell.bind(vend_btn, str(r.get("item_code", "")), qty_spin))
row.add_child(vend_btn)
box.add_child(row)
var row2 := HBoxContainer.new()
row2.add_theme_constant_override("separation", 8)
var spacer := Control.new()
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row2.add_child(spacer)
var price_lbl := Label.new()
price_lbl.text = "İlan fiyatı:"
price_lbl.add_theme_font_size_override("font_size", 12)
row2.add_child(price_lbl)
var price_spin := SpinBox.new()
price_spin.min_value = 1
price_spin.max_value = 1000000
price_spin.value = maxi(1, int(r.get("unit_price", 1)) * 2)
row2.add_child(price_spin)
var list_btn := Button.new()
list_btn.text = "📜 İlan Ver"
list_btn.disabled = not at_market
list_btn.pressed.connect(_on_list.bind(list_btn, str(r.get("item_code", "")), qty_spin, price_spin))
row2.add_child(list_btn)
box.add_child(row2)
return box
func _render_stock() -> void:
var rows: Array = await GameState.vendor_stock()
if rows.is_empty():
_list.add_child(_muted("Vendor stoku yüklenemedi."))
return
var at_market := _at_market()
var gold := int(GameState.character.get("gold", 0))
for r in rows:
if r is Dictionary:
_list.add_child(_stock_row(r, at_market, gold))
func _stock_row(r: Dictionary, at_market: bool, gold: int) -> HBoxContainer:
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 8)
var name_lbl := Label.new()
name_lbl.text = str(r.get("item_name", ""))
name_lbl.add_theme_font_size_override("font_size", 15)
name_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_child(name_lbl)
var qty_spin := SpinBox.new()
qty_spin.min_value = 1
qty_spin.max_value = 10
qty_spin.value = 1
row.add_child(qty_spin)
var price := int(r.get("unit_price", 0))
var btn := Button.new()
btn.text = "Al 🪙 %d" % price
if not at_market:
btn.disabled = true
elif gold < price:
btn.text = "🪙 yetersiz"
btn.disabled = true
else:
btn.pressed.connect(_on_vendor_buy.bind(btn, str(r.get("item_code", "")), qty_spin))
row.add_child(btn)
return row
func _on_vendor_buy(btn: Button, code: String, qty_spin: SpinBox) -> void:
btn.disabled = true
var res: Dictionary = await GameState.vendor_buy(code, int(qty_spin.value))
_report(res)
refresh()
func _on_buy(btn: Button, listing_id: int) -> void:
btn.disabled = true
var res: Dictionary = await GameState.market_buy(listing_id)
_report(res)
refresh()
func _on_cancel(btn: Button, listing_id: int) -> void:
btn.disabled = true
var res: Dictionary = await GameState.market_cancel(listing_id)
_report(res)
refresh()
func _on_vendor_sell(btn: Button, code: String, qty_spin: SpinBox) -> void:
btn.disabled = true
var res: Dictionary = await GameState.vendor_sell(code, int(qty_spin.value))
_report(res)
refresh()
func _on_list(btn: Button, code: String, qty_spin: SpinBox, price_spin: SpinBox) -> void:
btn.disabled = true
var res: Dictionary = await GameState.market_list(code, int(qty_spin.value), int(price_spin.value))
_report(res)
refresh()
func _report(res: Dictionary) -> void:
if not res.ok and res.body is Dictionary and res.body.has("detail"):
_info.text = "" + str(res.body["detail"])
func _muted(text: String) -> Label:
var l := Label.new()
l.text = text
l.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6))
return l