extends Node class_name Modes ## Which game you're playing. M cycles. ## ## CUBICLE HELL ...... the main one. Work a shift, get provoked, snap, wreck things, ## go back to your desk. Scored as wages minus damages. ## THE GAUNTLET ...... find which cabinet holds one client's file and destroy all of ## it, against a clock, with a hidden perfect tool. ## TOTAL DESTRUCTION . no job, no pay. Wreck the whole site. Scored on time. ## AGAINST THE CLOCK . fixed timer, maximise damage VALUE. The exact inverse of ## Cubicle Hell, where the expensive things are the goal rather ## than the mistake — same level, opposite instinct. ## ## Modes own only their rules. The level, the rage meter, the tasks and the destruction ## all exist regardless; a mode decides which of them are switched on and how the number ## at the top of the screen is computed. ## ## Godot 4.7 GDScript 2.0. enum M { CUBICLE_HELL, GAUNTLET, TOTAL, CLOCK } const NAMES := ["CUBICLE HELL", "THE GAUNTLET", "TOTAL DESTRUCTION", "AGAINST THE CLOCK"] const ORDER := [M.CUBICLE_HELL, M.GAUNTLET, M.TOTAL, M.CLOCK] @export var clock_time: float = 120.0 var mode: int = M.CUBICLE_HELL var running := false var time_left := 0.0 var result := "" # TOTAL DESTRUCTION var _to_break := 0 var _broken := 0 var _elapsed := 0.0 # AGAINST THE CLOCK var damage_value := 0 signal changed(mode: int) func cycle() -> void: mode = ORDER[(ORDER.find(mode) + 1) % ORDER.size()] changed.emit(mode) func set_mode(m: int) -> void: mode = m changed.emit(mode) func name_of() -> String: return NAMES[mode] ## Called by Main after a level is populated, with how many smashables exist. func begin(total_bodies: int) -> void: running = true result = "" _elapsed = 0.0 _broken = 0 damage_value = 0 _to_break = total_bodies time_left = clock_time func on_smashed(value: int) -> void: if not running: return _broken += 1 damage_value += value if mode == M.TOTAL and _broken >= _to_break: running = false result = "SITE CLEARED in %s" % _mmss(_elapsed) func _process(dt: float) -> void: if not running: return match mode: M.TOTAL: _elapsed += dt M.CLOCK: time_left -= dt if time_left <= 0.0: time_left = 0.0 running = false result = "$%s of damage" % _comma(damage_value) func progress() -> float: if mode != M.TOTAL or _to_break <= 0: return 0.0 return clampf(float(_broken) / float(_to_break), 0.0, 1.0) func hud_line() -> String: match mode: M.TOTAL: if not running: return "TOTAL DESTRUCTION — %s (R again · M mode)" % result return "TOTAL DESTRUCTION %s · %d / %d (%d%%)" % [ _mmss(_elapsed), _broken, _to_break, int(progress() * 100.0)] M.CLOCK: if not running: return "AGAINST THE CLOCK — %s (R again · M mode)" % result return "AGAINST THE CLOCK %02d:%02d · $%s · the expensive things" % [ int(time_left) / 60, int(time_left) % 60, _comma(damage_value)] return "" func headline() -> String: match mode: M.TOTAL: return "%d%%" % int(progress() * 100.0) M.CLOCK: return "$%s" % _comma(damage_value) return "" func _mmss(t: float) -> String: return "%d:%02d" % [int(t) / 60, int(t) % 60] func _comma(n: int) -> String: var s := str(absi(n)) var out := "" var c := 0 for i in range(s.length() - 1, -1, -1): out = s[i] + out c += 1 if c % 3 == 0 and i > 0: out = "," + out return ("-" if n < 0 else "") + out