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/.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()