class_name Campaign extends RefCounted ## Small campaign/session progression model. Venue rules stay in VenueScenario; ## this only owns order, completed shifts, difficulty and the next venue. const ORDER := ["hollywood_building", "joke_shop", "tv_studio"] var index := 0 var shift_number := 1 var completed: Array[String] = [] var difficulty := "normal" var complete := false var last_grade := "" func setup(data: Dictionary = {}) -> void: index = clampi(int(data.get("index", 0)), 0, ORDER.size()) shift_number = maxi(1, int(data.get("shift_number", 1))) difficulty = str(data.get("difficulty", "normal")) completed.clear() for item in data.get("completed", []): var value := str(item) if value in ORDER and value not in completed: completed.append(value) complete = bool(data.get("complete", index >= ORDER.size())) last_grade = str(data.get("last_grade", "")) func next_venue() -> String: if complete or index >= ORDER.size(): return "" return ORDER[index] func summary() -> Dictionary: return { "index": index, "next_venue": next_venue(), "shift_number": shift_number, "difficulty": difficulty, "complete": complete, "completed": completed.duplicate(), "last_grade": last_grade, } ## Record one finished shift. Idempotent per venue for a single campaign run. func record_shift(scenario_id: String, review: Dictionary = {}) -> bool: if complete: return false if scenario_id != next_venue(): return false if scenario_id not in completed: completed.append(scenario_id) var rep: Dictionary = review.get("reputation", {}) last_grade = str(rep.get("grade", last_grade)) index += 1 shift_number = index + 1 if index >= ORDER.size(): complete = true return true func set_difficulty(value: String) -> void: if value in ["easy", "normal", "hard"]: difficulty = value func reset() -> void: setup({})