293 lines
9.4 KiB
GDScript
293 lines
9.4 KiB
GDScript
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("Açı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
|