destroyulator/game/scripts/CubicleHell.gd
Monster Robot Party dd905d908d LANE7: CUBICLE HELL — the tilt loop, the job, and the payslip
The game stopped being a smash sandbox. You work a shift, the work provokes you, a
meter fills, you snap, and you cannot go back to your desk until you have broken
enough things. Then you sit back down and try to earn the rest of your day.

The score is your payslip: WAGES (tasks completed) - DAMAGES (everything you broke).
The load-bearing detail is that rage drains a FLAT amount per object destroyed while
each object bills at its OWN value — so during a meltdown the play is to wreck a lot
of cheap rubbish fast rather than put the sledgehammer through a filing cabinet. You
don't choose whether you lose your temper, you choose how much it costs. FURY (objects
broken while melting down) is tracked separately and deliberately isn't money, so
"I ended the shift owing them $2,400" stays a brag.

Rage.gd — WORKING -> MELTDOWN -> COOLDOWN. During a meltdown the clock STALLS if you
stop destroying; you must actually be breaking things.

RageOverlay.gd — the blood vessel. A vignette that closes in with rage and jumps
inward on every heartbeat, procedural veins that creep further into frame the angrier
you get, and a synthesized lub-dub going 62 -> 172 BPM. Zero assets: the shader is
built in code, the veins are draw_polyline from a seeded RNG, the heartbeat is a
generated WAV. Tops out as a heavy frame rather than a red screen — you have to be
able to see what you're about to destroy.

Tasks.gd — stations you walk to and are LOCKED IN PLACE at, because you're at your
desk and that's the joke. Two so far:
  SPREADSHEET  the selected cell silently drifts one cell partway through entry, with
               no sound and no animation. Commit without noticing and the figure lands
               in the wrong cell and you start again. This is the whole thesis of the
               game in about forty lines.
  PHOTOCOPIER  jams, and wants the right tray out of 1, 2, 2A, 3, 3B.
Snapping ejects you from the station at no penalty — you didn't choose to walk away.

CubicleHell.gd — 5-minute shift, wages vs damages, meltdown count, end-of-shift
payslip with a verdict line.

Also: HUD gets a monospace face (Godot's fallback is proportional, so the spreadsheet
grid and the work-order docket never lined up), and the arcade HUD's headline becomes
net pay, red when negative.

LANES/LANE7-cubicle-hell.md captures the rest of the design: more provocations, the
GAUNTLET mode (find which of many cabinets, pull drawers, destroy files individually,
and work out which tool the level wants without being told), the other three
workplaces, and why doratracyer/floor_plan can't help — it's OpenSCAD SVG->STL for
3D printing, no generation, no room polygons, GPL-3.0. Office.gd is already most of
a parametric floor-plan builder; lifting its numbers into a data spec is the real path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:34:34 +10:00

119 lines
3.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Node
class_name CubicleHell
## CUBICLE HELL — the mode the game is actually about.
##
## You work a shift. The work is designed to provoke you. The meter fills. You snap, and
## for half a minute you are physically unable to go back to your desk until you've
## broken enough things. Then you sit back down and try to earn the rest of your day.
##
## THE SCORE IS YOUR PAYSLIP:
##
## NET = WAGES (tasks completed) DAMAGES (everything you broke)
##
## Rage drains a flat amount per object destroyed, but each object bills at its own
## value — so the play during a meltdown is to smash a lot of cheap rubbish quickly
## rather than put a sledgehammer through a filing cabinet. You don't get to choose
## whether you lose your temper. You choose how much it costs.
##
## FURY is a separate, non-monetary stat: things broken while actually melting down.
## It exists because "I ended the shift owing them money" should still be a brag.
##
## Godot 4.7 GDScript 2.0.
@export var shift_time: float = 300.0 ## a five-minute shift
var rage: Rage
var tasks: Tasks
var time_left: float = 0.0
var running: bool = false
var damages: int = 0 ## $ of property destroyed
var fury: int = 0 ## objects broken during a meltdown
var meltdowns: int = 0
var result: String = ""
signal shift_ended()
func setup(r: Rage, t: Tasks) -> void:
rage = r
tasks = t
if rage != null:
rage.snapped.connect(_on_snapped)
func begin() -> void:
running = true
time_left = shift_time
damages = 0
fury = 0
meltdowns = 0
result = ""
func _on_snapped() -> void:
meltdowns += 1
# you cannot keep filling in the spreadsheet while you're kicking the printer
if tasks != null:
tasks.eject()
## Main calls this for every object destroyed, with what it was worth.
func on_smashed(value: int) -> void:
if not running:
return
damages += value
if rage != null and rage.is_meltdown():
fury += 1
func wages() -> int:
return tasks.wages if tasks != null else 0
func net() -> int:
return wages() - damages
func _process(dt: float) -> void:
if not running:
return
time_left -= dt
if time_left <= 0.0:
time_left = 0.0
_end()
func _end() -> void:
running = false
var n := net()
if n >= 600:
result = "EMPLOYEE OF THE MONTH"
elif n >= 0:
result = "SATISFACTORY"
elif n >= -900:
result = "A LETTER WILL BE PLACED ON YOUR FILE"
else:
result = "SECURITY WILL ESCORT YOU OUT"
shift_ended.emit()
## The line the HUD shows while the shift runs.
func hud_line() -> String:
if not running:
return "SHIFT OVER — %s · net $%d (M cycles mode · R new shift)" % [result, net()]
var m := int(time_left) / 60
var s := int(time_left) % 60
return "CUBICLE HELL %d:%02d · pay $%d damages $%d · %d meltdown%s" % [
m, s, wages(), damages, meltdowns, "" if meltdowns == 1 else "s"]
## End-of-shift payslip, as lines.
func payslip() -> PackedStringArray:
var out := PackedStringArray()
out.append("MONSTER ROBOT PARTY · PAYSLIP")
out.append("")
out.append("%-26s %8d" % ["Tasks completed", tasks.tasks_done if tasks else 0])
out.append("%-26s %8d" % ["Tasks fumbled", tasks.tasks_fumbled if tasks else 0])
out.append("%-26s %8s" % ["Gross wages", "$%d" % wages()])
out.append("")
out.append("%-26s %8d" % ["Meltdowns", meltdowns])
out.append("%-26s %8d" % ["Objects destroyed in rage", fury])
out.append("%-26s %8s" % ["Deductions (damages)", "-$%d" % damages])
out.append("--------------------------------------")
out.append("%-26s %8s" % ["NET", "$%d" % net()])
out.append("")
out.append(result)
return out