extends Node class_name GameMode ## The rules layer. Owned by Main, fed the live records + the player's GrabController. ## Two modes: ## FreePlay ..... just smash the store, no timer (default). ## FindMisfiled . exactly ONE disc is in the wrong sleeve. Extract it and press F ## before the 60s timer runs out. The frantic dig leaves a mess — and ## the mess is scored, because the mess is the point. ## ## Main drives transitions (it owns the world reset + the input keys); GameMode owns ## the planting, the clock, the win/lose call, and the scoreboard line. ## ## Godot 4.7 GDScript 2.0. enum Mode { FREE_PLAY, FIND_MISFILED } const GENRES: Array[String] = ["jazz", "techno", "punk", "soul", "funk", "dub"] const ROUND_TIME := 60.0 var mode: int = Mode.FREE_PLAY var _records: Array[Record] = [] var _grab: GrabController = null var _active := false # a FindMisfiled round is running var _time_left := 0.0 var _mess := 0 # bodies smashed during the round (the score) var _result := "" # win/lose banner, "" while playing var _misfiled: Record = null func setup(grab: GrabController) -> void: _grab = grab # ---------------------------------------------------------------- transitions ## FreePlay: no timer, every disc back in its right sleeve. func begin_free_play(records: Array[Record]) -> void: mode = Mode.FREE_PLAY _records = records _active = false _result = "" _misfiled = null for r in _records: if is_instance_valid(r): r.set_genres(r.sleeve_genre, r.sleeve_genre) ## FindMisfiled: plant one mismatch, start the clock. func begin_find_misfiled(records: Array[Record]) -> void: mode = Mode.FIND_MISFILED _records = records _active = true _time_left = ROUND_TIME _mess = 0 _result = "" _plant() ## Put exactly one disc in the wrong sleeve. Prints the plant (acceptance requires it). func _plant() -> void: var valid: Array[Record] = [] for r in _records: if is_instance_valid(r): r.set_genres(r.sleeve_genre, r.sleeve_genre) # reset any prior mismatch valid.append(r) if valid.is_empty(): print("[FindMisfiled] no records to plant into") return _misfiled = valid[randi() % valid.size()] var wrong := _other_genre(_misfiled.sleeve_genre) _misfiled.set_genres(_misfiled.sleeve_genre, wrong) print("[FindMisfiled] planted: a %s disc hidden inside a %s sleeve (of %d records)" % [ wrong, _misfiled.sleeve_genre, valid.size()]) func _other_genre(g: String) -> String: var pool := GENRES.filter(func(x): return x != g) if pool.is_empty(): return g return pool[randi() % pool.size()] # ---------------------------------------------------------------- events ## Main forwards Smashable.smashed here so the mess is scored during a round. func on_smashed() -> void: if _active: _mess += 1 ## F (report). Win only if the disc in hand is genuinely misfiled (disc != sleeve genre). func report() -> void: if mode != Mode.FIND_MISFILED or not _active: return var held: Record = _grab.held_record() if _grab != null else null if held != null and held.has_disc_in_hand() and held.disc_genre != held.sleeve_genre: _win(held) else: # a wrong call just nudges the banner; no hard penalty, keep it playful _result = "NOT THAT ONE — keep digging" func _win(held: Record) -> void: _active = false _result = "FOUND IT! the %s disc was in a %s sleeve · mess %d · %d records disturbed" % [ held.disc_genre, held.sleeve_genre, _mess, disturbed_count()] func _lose(_reason: String) -> void: _active = false var where := "somewhere in the crates" if is_instance_valid(_misfiled): where = "a %s sleeve" % _misfiled.sleeve_genre _result = "TIME'S UP — the misfiled disc was in %s · mess %d" % [where, _mess] # ---------------------------------------------------------------- bookkeeping func disturbed_count() -> int: var n := 0 for r in _records: if is_instance_valid(r) and r.was_disturbed(): n += 1 return n func _process(delta: float) -> void: if _active and mode == Mode.FIND_MISFILED: _time_left -= delta if _time_left <= 0.0: _time_left = 0.0 _lose("timeout") ## The scoreboard line Main appends to the HUD. func hud_line() -> String: if mode == Mode.FREE_PLAY: return "MODE: FreePlay (1 FreePlay · 2 Find-the-Misfiled-Disc)" if _active: return "MODE: Find Misfiled %02d s mess %d · pull discs, F to report the wrong one" % [ int(ceil(_time_left)), _mess] return "MODE: Find Misfiled %s (2 to replay · 1 FreePlay)" % _result