İlk commit: Stepstead Godot istemci (4.6 Mobile)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jts 2026-07-11 11:31:35 +03:00
commit 6a1706051d
346 changed files with 14415 additions and 0 deletions

293
scripts/market_panel.gd Normal file
View file

@ -0,0 +1,293 @@
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