İlk commit: Stepstead Godot istemci (4.6 Mobile)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
6a1706051d
346 changed files with 14415 additions and 0 deletions
210
scripts/location_panel.gd
Normal file
210
scripts/location_panel.gd
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
extends PanelContainer
|
||||
## Lokasyon paneli — bulunduğun yerin kaynakları (topla butonları), buradaki oyuncular.
|
||||
## Kod ile kurulur, world.gd HUD'a ekler. Node adı "LocationPanel".
|
||||
|
||||
const PANEL_HEIGHT := 620.0
|
||||
|
||||
signal city_requested
|
||||
|
||||
var _city_btn: Button
|
||||
var _title: Label
|
||||
var _desc: Label
|
||||
var _players: Label
|
||||
var _list: VBoxContainer
|
||||
var _scroll: ScrollContainer
|
||||
var _header: TextureRect
|
||||
|
||||
func _ready() -> void:
|
||||
name = "LocationPanel"
|
||||
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.10, 0.12, 0.10, 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", 14)
|
||||
margin.add_theme_constant_override("margin_right", 14)
|
||||
margin.add_theme_constant_override("margin_top", 10)
|
||||
margin.add_theme_constant_override("margin_bottom", 10)
|
||||
add_child(margin)
|
||||
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 6)
|
||||
margin.add_child(vbox)
|
||||
|
||||
# Lokasyon başlık görseli (assets/locations/<code>.webp — varsa)
|
||||
_header = TextureRect.new()
|
||||
_header.custom_minimum_size = Vector2(0, 190)
|
||||
_header.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
_header.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
|
||||
_header.clip_contents = true
|
||||
_header.visible = false
|
||||
vbox.add_child(_header)
|
||||
|
||||
var top := HBoxContainer.new()
|
||||
_title = Label.new()
|
||||
_title.add_theme_font_size_override("font_size", 20)
|
||||
_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(_title)
|
||||
_city_btn = Button.new()
|
||||
_city_btn.text = "🏙 Şehir Haritası"
|
||||
_city_btn.visible = false
|
||||
_city_btn.pressed.connect(func() -> void:
|
||||
visible = false
|
||||
city_requested.emit())
|
||||
top.add_child(_city_btn)
|
||||
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)
|
||||
|
||||
_desc = Label.new()
|
||||
_desc.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_desc.add_theme_font_size_override("font_size", 13)
|
||||
_desc.add_theme_color_override("font_color", Color(0.7, 0.72, 0.65))
|
||||
vbox.add_child(_desc)
|
||||
|
||||
_players = Label.new()
|
||||
_players.add_theme_font_size_override("font_size", 13)
|
||||
_players.add_theme_color_override("font_color", Color(0.55, 0.75, 0.95))
|
||||
vbox.add_child(_players)
|
||||
|
||||
_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 open() -> void:
|
||||
visible = true
|
||||
await refresh()
|
||||
|
||||
func refresh() -> void:
|
||||
var res: Dictionary = await GameState.load_location()
|
||||
if not res.ok or not (res.body is Dictionary):
|
||||
_title.text = "Lokasyon yüklenemedi"
|
||||
return
|
||||
var loc: Dictionary = res.body
|
||||
_title.text = "📍 %s" % str(loc.get("name", ""))
|
||||
_city_btn.visible = str(loc.get("type", "")) in ["city", "village"]
|
||||
_desc.text = str(loc.get("description", ""))
|
||||
var img_path := "res://assets/locations/%s.webp" % str(loc.get("code", ""))
|
||||
if ResourceLoader.exists(img_path):
|
||||
_header.texture = load(img_path)
|
||||
_header.visible = true
|
||||
else:
|
||||
_header.visible = false
|
||||
var names: Array = loc.get("players_here", [])
|
||||
_players.text = "👥 Burada: %s" % ", ".join(names) if not names.is_empty() else "👥 Burada başka kimse yok"
|
||||
|
||||
for c in _list.get_children():
|
||||
c.queue_free()
|
||||
_list.add_child(_camp_row(loc))
|
||||
var resources: Array = loc.get("resources", [])
|
||||
if resources.is_empty():
|
||||
var l := Label.new()
|
||||
l.text = "Burada toplanacak kaynak yok."
|
||||
l.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6))
|
||||
_list.add_child(l)
|
||||
return
|
||||
var bal := int(GameState.character.get("step_balance", 0))
|
||||
for r in resources:
|
||||
if r is Dictionary:
|
||||
_list.add_child(_resource_row(r, bal))
|
||||
|
||||
const SKILL_TR := {"mining": "Madencilik", "fishing": "Balıkçılık", "foraging": "Toplama",
|
||||
"woodcutting": "Ağaç Kesme", "hunting": "Avcılık"}
|
||||
|
||||
func _resource_row(r: Dictionary, bal: int) -> 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 = str(r.get("item_name", ""))
|
||||
name_lbl.add_theme_font_size_override("font_size", 15)
|
||||
info.add_child(name_lbl)
|
||||
var meta := Label.new()
|
||||
var skill: String = str(r.get("skill", ""))
|
||||
meta.text = "%s • %d👣 • +%dXP" % [SKILL_TR.get(skill, skill), int(r.get("step_cost", 0)), int(r.get("xp", 0))]
|
||||
meta.add_theme_font_size_override("font_size", 12)
|
||||
meta.add_theme_color_override("font_color", Color(0.6, 0.62, 0.58))
|
||||
info.add_child(meta)
|
||||
row.add_child(info)
|
||||
|
||||
var btn := Button.new()
|
||||
var cd := int(r.get("cooldown_remaining", 0))
|
||||
var tool_ok: bool = bool(r.get("tool_ok", true))
|
||||
var cost := int(r.get("step_cost", 0))
|
||||
if not tool_ok:
|
||||
btn.text = "🔒 %s gerekli" % str(r.get("tool_needed", "Tool"))
|
||||
btn.disabled = true
|
||||
elif cd > 0:
|
||||
btn.text = "⏳ %ds" % cd
|
||||
btn.disabled = true
|
||||
elif bal < cost:
|
||||
btn.text = "👣 yetersiz"
|
||||
btn.disabled = true
|
||||
else:
|
||||
btn.text = "Topla"
|
||||
var code := str(r.get("item_code", ""))
|
||||
btn.pressed.connect(_on_gather.bind(btn, code))
|
||||
row.add_child(btn)
|
||||
return row
|
||||
|
||||
const CAMP_ITEM_TR := {"log_oak": "Meşe Odunu", "stone_small": "Küçük Taş", "vine": "Sarmaşık"}
|
||||
|
||||
func _camp_row(loc: Dictionary) -> HBoxContainer:
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 10)
|
||||
var lbl := Label.new()
|
||||
lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
lbl.add_theme_font_size_override("font_size", 13)
|
||||
if bool(loc.get("has_camp", false)):
|
||||
lbl.text = "⛺ Kampın var — toplama maliyeti -%25"
|
||||
lbl.add_theme_color_override("font_color", Color(0.6, 0.85, 0.6))
|
||||
row.add_child(lbl)
|
||||
return row
|
||||
var cost_items: Dictionary = loc.get("camp_cost_items", {})
|
||||
var parts: Array[String] = []
|
||||
for code in cost_items:
|
||||
parts.append("%d %s" % [int(cost_items[code]), str(CAMP_ITEM_TR.get(code, code))])
|
||||
lbl.text = "⛺ Kamp: %s + %d👣 (-%%25 maliyet)" % [", ".join(parts), int(loc.get("camp_step_cost", 100))]
|
||||
lbl.add_theme_color_override("font_color", Color(0.75, 0.7, 0.55))
|
||||
row.add_child(lbl)
|
||||
var btn := Button.new()
|
||||
btn.text = "Kur"
|
||||
btn.pressed.connect(_on_build_camp.bind(btn))
|
||||
row.add_child(btn)
|
||||
return row
|
||||
|
||||
func _on_build_camp(btn: Button) -> void:
|
||||
btn.disabled = true
|
||||
var res: Dictionary = await GameState.build_camp()
|
||||
if not res.ok and res.body is Dictionary and res.body.has("detail"):
|
||||
print("[camp fail] ", str(res.body["detail"]))
|
||||
await refresh()
|
||||
|
||||
func _on_gather(btn: Button, code: String) -> void:
|
||||
btn.disabled = true
|
||||
var res: Dictionary = await GameState.gather(code)
|
||||
if not res.ok:
|
||||
var msg := "Toplanamadı"
|
||||
if res.body is Dictionary and res.body.has("detail"):
|
||||
msg = str(res.body["detail"])
|
||||
print("[gather fail] ", msg)
|
||||
await refresh()
|
||||
Loading…
Add table
Add a link
Reference in a new issue