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)