extends Control
# =========================
# ์ํ(์์/๊ฑด๋ฌผ/์์ฐ/์ฉ๋)
# =========================
var resources: Dictionary = {
"gold": 0,
"wood": 0,
"stone": 0,
"food": 0
}
# ๊ฑด๋ฌผ ๋ ๋ฒจ (์์ฐ ๊ฑด๋ฌผ 3๊ฐ)
var building_level: Dictionary = {
"lumbermill": 0,
"quarry": 0,
"farm": 0
}
# ๊ฑด๋ฌผ ์ ๊ทธ๋ ์ด๋ ๋น์ฉ (๊ฐ ๋น์ฉ์ ์์ ๋์ ๋๋ฆฌ๋ก)
var building_cost: Dictionary = {
"lumbermill": {"gold": 10, "wood": 0, "stone": 0, "food": 0},
"quarry":ย ย ย {"gold": 10, "wood": 0, "stone": 0, "food": 0},
"farm":ย ย ย ย {"gold": 10, "wood": 0, "stone": 0, "food": 0}
}
# ์์ ์ ์ฅ ์ฉ๋(์ต๋์น)
var capacity: Dictionary = {
"gold": 999999,
"wood": 200,
"stone": 200,
"food": 200
}
# ์ด๋น ์์ฐ๋
var production_per_sec: Dictionary = {
"gold": 0,
"wood": 0,
"stone": 0,
"food": 0
}
# ===== ์ฐฝ๊ณ (์ฉ๋) =====
var storage_level: int = 0
const BASE_CAPACITY: Dictionary = {
"gold": 999999,
"wood": 200,
"stone": 200,
"food": 200
}
const STORAGE_BONUS: Dictionary = {
"wood": 50,
"stone": 50,
"food": 50
}
var storage_cost: Dictionary = {"gold": 0, "wood": 50, "stone": 50, "food": 0}
# ์คํ๋ผ์ธ ๊ด๋ จ
@onready var offline_popup_label: Label = $MarginContainer/VBoxContainer/OfflinePopupLabel
# UI ๋ผ๋ฒจ
@onready var gold_label: Labelย = $MarginContainer/VBoxContainer/ResourceBox/GoldLabel
@onready var wood_label: Labelย = $MarginContainer/VBoxContainer/ResourceBox/WoodLabel
@onready var stone_label: Label = $MarginContainer/VBoxContainer/ResourceBox/StoneLabel
@onready var food_label: Labelย = $MarginContainer/VBoxContainer/ResourceBox/FoodLabel
# ๊ฑด๋ฌผ ๋ฒํผ/๋น์ฉ ๋ผ๋ฒจ
@onready var lumber_btn: Button = $MarginContainer/VBoxContainer/BuildingBox/LumberMillBox/LumberMillButton
@onready var lumber_cost_label: Label = $MarginContainer/VBoxContainer/BuildingBox/LumberMillBox/LumberMillCostLabel
@onready var quarry_btn: Button = $MarginContainer/VBoxContainer/BuildingBox/QuarryBox/QuarryButton
@onready var quarry_cost_label: Label = $MarginContainer/VBoxContainer/BuildingBox/QuarryBox/QuarryCostLabel
@onready var farm_btn: Button = $MarginContainer/VBoxContainer/BuildingBox/FarmBox/FarmButton
@onready var farm_cost_label: Label = $MarginContainer/VBoxContainer/BuildingBox/FarmBox/FarmCostLabel
@onready var storage_btn: Button = $MarginContainer/VBoxContainer/BuildingBox/StorageBox/StorageButton
@onready var storage_cost_label: Label = $MarginContainer/VBoxContainer/BuildingBox/StorageBox/StorageCostLabel
# ์ ์ฅ
const SAVE_PATH := "user://save.json"
const SAVE_VERSION := 1
func _ready() -> void:
load_game()
recalc_capacity()
recalc_production()
update_ui()
# =========================
# UI ๊ฐฑ์
# =========================
func update_ui() -> void:
# ์์ ๋ผ๋ฒจ
gold_label.textย = "Gold:ย %d / %d" % [int(resources["gold"]),ย int(capacity["gold"])]
wood_label.textย = "Wood:ย %d / %d" % [int(resources["wood"]),ย int(capacity["wood"])]
stone_label.text = "Stone: %d / %d" % [int(resources["stone"]), int(capacity["stone"])]
food_label.textย = "Food:ย %d / %d" % [int(resources["food"]),ย int(capacity["food"])]
# ๋ฒํผ ํ ์คํธ(๋ ๋ฒจ)
lumber_btn.text = "LumberMill Lv.%d" % int(building_level["lumbermill"])
quarry_btn.text = "Quarry Lv.%d" % int(building_level["quarry"])
farm_btn.textย ย = "Farm Lv.%d" % int(building_level["farm"])
# ๋น์ฉ ํ์
lumber_cost_label.text = "Cost: " + format_cost(building_cost["lumbermill"])
quarry_cost_label.text = "Cost: " + format_cost(building_cost["quarry"])
farm_cost_label.textย ย = "Cost: " + format_cost(building_cost["farm"])
# ๊ตฌ๋งค ๊ฐ๋ฅ/๋ถ๊ฐ
lumber_btn.disabled = not can_afford(building_cost["lumbermill"])
quarry_btn.disabled = not can_afford(building_cost["quarry"])
farm_btn.disabledย ย = not can_afford(building_cost["farm"])
# ---- ์ฐฝ๊ณ UI ----
if is_instance_valid(storage_btn):
storage_btn.text = "Storage Lv.%d" % storage_level
storage_btn.disabled = not can_afford(storage_cost)
if is_instance_valid(storage_cost_label):
storage_cost_label.text = "Cost: " + format_cost(storage_cost)
func format_cost(cost: Dictionary) -> String:
var parts: Array[String] = []
for k in ["gold","wood","stone","food"]:
var v := int(cost.get(k, 0))
if v > 0:
parts.append("%s %d" % [k.capitalize(), v])
if parts.is_empty():
return "Free"
return ", ".join(parts)
# =========================
# ์์ฐ/์ฉ๋/ํด๋จํ
# =========================
func clamp_resource(key: String) -> void:
if not resources.has(key): return
if not capacity.has(key): return
resources[key] = clampi(int(resources[key]), 0, int(capacity[key]))
func add_resource(key: String, amount: int) -> void:
if not resources.has(key): return
resources[key] = int(resources[key]) + int(amount)
clamp_resource(key)
func recalc_production() -> void:
# ์์ฐ ๊ณต์(์ผ๋จ ๋จ์):
# - LumberMill ๋ ๋ฒจ๋น wood +1/s
# - Quarry ๋ ๋ฒจ๋น stone +1/s
# - Farm ๋ ๋ฒจ๋น food +1/s
production_per_sec["wood"]ย = int(building_level["lumbermill"]) * 1
production_per_sec["stone"] = int(building_level["quarry"]) * 1
production_per_sec["food"]ย = int(building_level["farm"]) * 1
# gold๋ ์ง๊ธ์ ์์ฐ ์์(์ํ๋ฉด ๋์ค์ "์ธ๊ธ" ๊ฐ์ ๊ฑด๋ฌผ๋ก ์ถ๊ฐ)
production_per_sec["gold"] = 0
func recalc_capacity() -> void:
for k in BASE_CAPACITY.keys():
if k == "gold":
capacity[k] = int(BASE_CAPACITY[k])
else:
capacity[k] = int(BASE_CAPACITY[k]) + storage_level * int(STORAGE_BONUS[k])
clamp_resource(String(k))
# =========================
# ํ์ด๋จธ: ์ด๋น ์์ฐ
# =========================
func _on_timer_timeout() -> void:
for k in production_per_sec.keys():
add_resource(String(k), int(production_per_sec[k]))
update_ui()
# ์ํ๋ฉด ์ฃผ๊ธฐ ์ ์ฅ:
# save_game()
# =========================
# ๊ฑด๋ฌผ ์ ๊ทธ๋ ์ด๋(๋ฒํผ 3๊ฐ)
# =========================
func _on_lumbermill_pressed() -> void:
try_upgrade_building("lumbermill")
func _on_quarry_pressed() -> void:
try_upgrade_building("quarry")
func _on_farm_pressed() -> void:
try_upgrade_building("farm")
func try_upgrade_building(id: String) -> void:
var cost: Dictionary = building_cost.get(id, {})
if not can_afford(cost):
return
pay_cost(cost)
building_level[id] = int(building_level[id]) + 1
# ๋น์ฉ ์ฆ๊ฐ ๊ท์น(์์): gold๋ 1.6๋ฐฐ+5, ํด๋น ์์๋ ์กฐ๊ธ์ฉ ์๊ตฌ
grow_building_cost(id)
recalc_production()
update_ui()
save_game()
func can_afford(cost: Dictionary) -> bool:
for k in cost.keys():
var need := int(cost.get(k, 0))
if need <= 0:
continue
if int(resources.get(k, 0)) < need:
return false
return true
func pay_cost(cost: Dictionary) -> void:
for k in cost.keys():
var need := int(cost.get(k, 0))
if need <= 0:
continue
resources[k] = int(resources.get(k, 0)) - need
clamp_resource(String(k))
func grow_building_cost(id: String) -> void:
var lv := int(building_level[id])
var c: Dictionary = building_cost[id]
# gold ๊ธฐ๋ณธ ์ฆ๊ฐ
c["gold"] = int(max(1, int(c.get("gold", 10)) * 1.6)) + 5
# ํด๋น ๊ฑด๋ฌผ๊ณผ ๊ด๋ จ ์์๋ ์กฐ๊ธ ์๊ตฌ(์์)
if id == "lumbermill":
c["wood"] = int(lv * 3)
elif id == "quarry":
c["stone"] = int(lv * 3)
elif id == "farm":
c["food"] = int(lv * 3)
# ๋์ ๋๋ฆฌ ๋ฐ์
building_cost[id] = c
# =========================
# ์ ์ฅ/๋ถ๋ฌ์ค๊ธฐ + ์คํ๋ผ์ธ
# =========================
func make_save_data() -> Dictionary:
return {
"v": SAVE_VERSION,
"resources": resources,
"building_level": building_level,
"building_cost": building_cost,
"storage_level": storage_level,
"storage_cost": storage_cost,
"last_play_time": int(Time.get_unix_time_from_system())
}
func apply_save_data(data: Dictionary) -> void:
# resources
var saved_resources: Dictionary = data.get("resources", {})
if typeof(saved_resources) == TYPE_DICTIONARY:
for key in resources.keys():
resources[key] = int(saved_resources.get(key, resources[key]))
clamp_resource(String(key))
# building_level
var saved_lv: Dictionary = data.get("building_level", {})
if typeof(saved_lv) == TYPE_DICTIONARY:
for key in building_level.keys():
building_level[key] = int(saved_lv.get(key, building_level[key]))
# building_cost
var saved_cost: Dictionary = data.get("building_cost", {})
if typeof(saved_cost) == TYPE_DICTIONARY:
for key in building_cost.keys():
var saved_entry = saved_cost.get(key, {})
if typeof(saved_entry) == TYPE_DICTIONARY:
building_cost[key] = saved_entry.duplicate(true)
storage_level = int(data.get("storage_level", storage_level))
var saved_sc: Dictionary = data.get("storage_cost", {})
if typeof(saved_sc) == TYPE_DICTIONARY:
for k in storage_cost.keys():
storage_cost[k] = int(saved_sc.get(k, storage_cost[k]))
recalc_capacity()
recalc_production()
func save_game() -> void:
var data := make_save_data()
var file := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file == null:
push_error("Save failed: cannot open file")
return
file.store_string(JSON.stringify(data))
file.close()
func load_game() -> void:
if not FileAccess.file_exists(SAVE_PATH):
return
var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
if file == null:
push_error("Load failed: cannot open file")
return
var text := file.get_as_text()
file.close()
var parsed: Variant = JSON.parse_string(text)
if typeof(parsed) != TYPE_DICTIONARY:
push_error("Load failed: invalid json")
return
var data: Dictionary = parsed as Dictionary
apply_save_data(data)
apply_offline_earnings(data)
func apply_offline_earnings(data: Dictionary) -> void:
var last_time: int = int(data.get("last_play_time", 0))
if last_time <= 0:
return
var now: int = int(Time.get_unix_time_from_system())
var offline_seconds: int = maxi(0, now - last_time)
# ๊ธฐ๋ณธ ์ํ(์ง๊ธ์ 8์๊ฐ)
var max_seconds: int = 8 * 60 * 60
offline_seconds = mini(offline_seconds, max_seconds)
if offline_seconds <= 0:
return
# ์คํ๋ผ์ธ ๋์ ์์ฐ ์ ์ฉ(wood/stone/food)
var earned: Dictionary = {"wood":0,"stone":0,"food":0}
for k in ["wood","stone","food"]:
var gain := offline_seconds * int(production_per_sec[k])
if gain > 0:
earned[k] = gain
add_resource(k, gain)
show_offline_popup_multi(offline_seconds, earned)
func show_offline_popup_multi(seconds: int, earned: Dictionary) -> void:
if offline_popup_label == null:
return
var parts: Array[String] = []
for k in ["wood","stone","food"]:
var v := int(earned.get(k, 0))
if v > 0:
parts.append("+" + str(v) + " " + k.capitalize())
if parts.is_empty():
return
offline_popup_label.text = "์คํ๋ผ์ธ " + format_duration(seconds) + " ๋์ " + ", ".join(parts)
offline_popup_label.visible = true
offline_popup_label.modulate.a = 1.0
await get_tree().create_timer(1.2).timeout
var t := create_tween()
t.tween_property(offline_popup_label, "modulate:a", 0.0, 0.6)
await t.finished
offline_popup_label.visible = false
offline_popup_label.modulate.a = 1.0
func format_duration(total_seconds: int) -> String:
var s: int = maxi(0, total_seconds)
var h: int = s / 3600
var m: int = (s % 3600) / 60
var sec: int = s % 60
if h > 0:
return str(h) + "์๊ฐ " + str(m) + "๋ถ"
elif m > 0:
return str(m) + "๋ถ " + str(sec) + "์ด"
else:
return str(sec) + "์ด"
func _on_storage_button_pressed() -> void:
try_upgrade_storage()
func try_upgrade_storage() -> void:
if not can_afford(storage_cost):
return
pay_cost(storage_cost)
storage_level += 1
# ๋น์ฉ ์ฆ๊ฐ ๊ท์น(์์): wood/stone 1.5๋ฐฐ + 20, gold๋ ์กฐ๊ธ์ฉ ์๊ตฌ
grow_storage_cost()
recalc_capacity()
update_ui()
save_game()
func grow_storage_cost() -> void:
storage_cost["wood"]ย = int(max(1, int(storage_cost.get("wood", 50)) * 1.5)) + 20
storage_cost["stone"] = int(max(1, int(storage_cost.get("stone", 50)) * 1.5)) + 20
storage_cost["gold"]ย = int(max(0, int(storage_cost.get("gold", 0)) * 1.4)) + (storage_level * 5)
์ ํ๊ณ ์๋์ง๋ ๋ชจ๋ฅด๊ฒ ๋๋ฐ ์์ฆ ๊ฒ๋ค์ ๋๋ฌด ๊ณผ๊ธ์ ๋๋ง ๋๋ฝ๊ฒ ์ฌํ ๊ฒ ๊ฐ์์ ๋ด ๋ก๋ง์ด ๊ฐ๋ํ ๊ฒ์ ๋ง๋ค์ด๋ณด๋ ค๊ณ ํ๋ค
var ๋ง๊ณ son ํด๋ผ
์ฐจ์ด๊ฐ ๋ญ์?
๊นํ์จ