Four lanes built in parallel onto the data-driven spine: - Rules.gd (509) — score with a decaying combo multiplier, 3 balls, ball save, escalating drop-bank bonuses, lit rollover lanes that level up when swept, saucer-lock multiball, two-warning tilt, end-of-ball bonus, per-table high scores in user://pinball_scores.cfg. - Juice.gd (784) — 12 procedural voices synthesized at boot, no assets. The spinner ratchet is timed to Table.gd bleeding spin_left at 6.0/s so the ticking stops exactly when the blade does. Pooled voices/lights/sparks, quadratic-falloff shake, and a damped spring for nudge. Palette-tinted lights with a value floor, because a light the colour of a near-black saucer illuminates nothing. - Hud.gd (935) — score that ROLLS UP rather than snapping, combo, award callouts, plunger meter, tilt banner, ball save, game over panel. - dev/probe_tables.gd (480) — the gate: builds every table and checks parts, part overlaps, playfield bounds, flipper swing, that the ball actually DRAINS, and that everything settles. The probe immediately found three real bugs the flipper autotest could not see: 1. The drain sat 3 cm PAST the playfield lip, so on four of five tables the ball rolled off the end into the void and the trigger never fired. A table you cannot lose a ball on is not a table. Moved inside the slab. 2. Inlane posts were level with the slingshots and interpenetrated them on three tables — on a real table the posts sit below. Moved down-table. 3. THE DIG's counter ramp ran through the crate bank, and a 15 deg yaw over a 28 cm ramp swings its far end 7 cm sideways, which then walked it through the left rail. Less yaw, tucked in, crates slid right. PROBE_TABLES: PASS 5/5. AUTOTEST: ALL TABLES OK. Still open, and it is an ENGINE finding not a table one: godot-box3d does not enforce HingeJoint3D angular limits — flippers swing 147-160 deg against a 62 deg limit. The probe reports it as a NOTE per table. Worth an upstream issue alongside the motor-sign inversion already in the README. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
353 lines
19 KiB
GDScript
353 lines
19 KiB
GDScript
extends RefCounted
|
|
class_name Tables
|
|
|
|
## The tables, as data. Each is a Table spec (see Table.gd for the part vocabulary).
|
|
##
|
|
## These are ORIGINAL layouts, not recreations of the Microsoft table vendored next door —
|
|
## that decomp is here as a reference and a build target, not something to trace.
|
|
##
|
|
## A table is a Dictionary: name, palette, size, and a flat list of parts. That means a new
|
|
## table is authorable in minutes, tables can be diffed, and dev/probe_tables.gd can build
|
|
## every one of them headless and assert the playfield is sane.
|
|
|
|
const ORDER := ["neon", "grotto", "foundry", "orbital", "thedig"]
|
|
|
|
static func get_table(id: String) -> Dictionary:
|
|
match id:
|
|
"grotto": return grotto()
|
|
"foundry": return foundry()
|
|
"orbital": return orbital()
|
|
"thedig": return thedig()
|
|
return neon()
|
|
|
|
static func next_id(id: String) -> String:
|
|
var i := ORDER.find(id)
|
|
return ORDER[(i + 1) % ORDER.size()] if i >= 0 else ORDER[0]
|
|
|
|
# ================================================================ TABLE 1
|
|
## NEON ARCADE — the friendly one. Wide open lower field, a three-bank of drop targets,
|
|
## two pop bumpers up top and a spinner lane on the left. Learn the flippers here.
|
|
static func neon() -> Dictionary:
|
|
var parts: Array = []
|
|
# --- the lower field: flippers, slings, outlane guides ---
|
|
parts.append({"kind": "flipper", "id": "flip_l", "at": Vector3(-0.062, 0.022, 0.400),
|
|
"side": -1, "length": 0.078, "limit_lo": -32.0, "limit_hi": 30.0, "torque": 7.0})
|
|
parts.append({"kind": "flipper", "id": "flip_r", "at": Vector3(0.062, 0.022, 0.400),
|
|
"side": 1, "length": 0.078, "limit_lo": -30.0, "limit_hi": 32.0, "torque": 7.0})
|
|
parts.append({"kind": "sling", "id": "sling_l", "at": Vector3(-0.125, 0.0, 0.320),
|
|
"dir": Vector3(1, 0, -0.55), "yaw": deg_to_rad(-32.0), "kick": 0.135, "points": 120})
|
|
parts.append({"kind": "sling", "id": "sling_r", "at": Vector3(0.125, 0.0, 0.320),
|
|
"dir": Vector3(-1, 0, -0.55), "yaw": deg_to_rad(32.0), "kick": 0.135, "points": 120})
|
|
# outlane / inlane dividers — the guides that decide whether a drain was fair
|
|
parts.append({"kind": "wall", "id": "guide_l", "at": Vector3(-0.175, 0.0, 0.380),
|
|
"size": Vector3(0.012, 0.05, 0.150), "yaw": deg_to_rad(9.0)})
|
|
parts.append({"kind": "wall", "id": "guide_r", "at": Vector3(0.175, 0.0, 0.380),
|
|
"size": Vector3(0.012, 0.05, 0.150), "yaw": deg_to_rad(-9.0)})
|
|
parts.append({"kind": "post", "id": "post_l", "at": Vector3(-0.100, 0.0, 0.366)})
|
|
parts.append({"kind": "post", "id": "post_r", "at": Vector3(0.100, 0.0, 0.366)})
|
|
|
|
# --- mid field: the drop-target bank and a pair of standing targets ---
|
|
for i in 3:
|
|
parts.append({"kind": "drop", "id": "drop_%d" % i, "bank": "neon",
|
|
"at": Vector3(-0.130 + i * 0.034, 0.0, 0.055), "points": 500})
|
|
parts.append({"kind": "target", "id": "targ_l", "at": Vector3(-0.196, 0.0, -0.070),
|
|
"yaw": deg_to_rad(24.0), "points": 300})
|
|
parts.append({"kind": "target", "id": "targ_r", "at": Vector3(0.196, 0.0, -0.070),
|
|
"yaw": deg_to_rad(-24.0), "points": 300})
|
|
|
|
# --- the spinner lane, left side, fed by a hard left flipper shot ---
|
|
parts.append({"kind": "spinner", "id": "spin_main", "at": Vector3(-0.155, 0.0, 0.170),
|
|
"yaw": deg_to_rad(12.0), "points": 90})
|
|
parts.append({"kind": "wall", "id": "spin_guide", "at": Vector3(-0.205, 0.0, 0.170),
|
|
"size": Vector3(0.012, 0.05, 0.180), "yaw": deg_to_rad(6.0)})
|
|
|
|
# --- top: two pops and a saucer between them ---
|
|
parts.append({"kind": "bumper", "id": "pop_l", "at": Vector3(-0.075, 0.0, -0.250),
|
|
"kick": 0.165, "points": 250})
|
|
parts.append({"kind": "bumper", "id": "pop_r", "at": Vector3(0.075, 0.0, -0.250),
|
|
"kick": 0.165, "points": 250})
|
|
parts.append({"kind": "bumper", "id": "pop_t", "at": Vector3(0.0, 0.0, -0.335),
|
|
"kick": 0.165, "points": 250})
|
|
parts.append({"kind": "saucer", "id": "saucer_top", "at": Vector3(0.0, 0.0, -0.150),
|
|
"eject": Vector3(0.1, 0, 1), "hold": 0.85, "power": 0.20, "points": 2500})
|
|
|
|
# --- the N-E-O-N rollover lanes across the top arch ---
|
|
var glyphs := ["N", "E", "O", "N"]
|
|
for i in glyphs.size():
|
|
parts.append({"kind": "rollover", "id": "lane_%d" % i, "glyph": glyphs[i],
|
|
"at": Vector3(-0.105 + i * 0.070, 0.0, -0.430), "points": 150})
|
|
|
|
# --- the right ramp: the money shot ---
|
|
parts.append({"kind": "ramp", "id": "ramp_r", "at": Vector3(0.120, 0.0, 0.230),
|
|
"yaw": deg_to_rad(-14.0), "length": 0.30, "width": 0.052, "rise": 0.06,
|
|
"points": 1500})
|
|
|
|
return {
|
|
"name": "NEON ARCADE",
|
|
"subtitle": "wide open. learn the flippers here.",
|
|
"width": 0.52, "length": 1.10,
|
|
"palette": {
|
|
"field": Color(0.10, 0.09, 0.20), "rail": Color(0.62, 0.64, 0.72),
|
|
"flipper": Color(0.98, 0.30, 0.55), "bumper": Color(0.35, 0.95, 0.90),
|
|
"sling": Color(0.55, 0.95, 0.45), "target": Color(0.98, 0.55, 0.20),
|
|
"drop": Color(0.98, 0.88, 0.30), "spinner": Color(0.80, 0.90, 1.00),
|
|
"lane": Color(0.45, 0.60, 1.00), "ramp": Color(0.30, 0.75, 0.95),
|
|
"wall": Color(0.30, 0.32, 0.45), "post": Color(0.90, 0.92, 1.00),
|
|
"saucer": Color(0.08, 0.10, 0.18),
|
|
},
|
|
"parts": parts,
|
|
}
|
|
|
|
# ================================================================ TABLE 2
|
|
## THE GROTTO — tight and mean. Narrower field, a five-bank guarding the only ramp, and
|
|
## the outlanes are wide open, so a bad shot is punished immediately.
|
|
static func grotto() -> Dictionary:
|
|
var parts: Array = []
|
|
parts.append({"kind": "flipper", "id": "flip_l", "at": Vector3(-0.058, 0.022, 0.410),
|
|
"side": -1, "length": 0.072, "limit_lo": -34.0, "limit_hi": 28.0, "torque": 6.5})
|
|
parts.append({"kind": "flipper", "id": "flip_r", "at": Vector3(0.058, 0.022, 0.410),
|
|
"side": 1, "length": 0.072, "limit_lo": -28.0, "limit_hi": 34.0, "torque": 6.5})
|
|
parts.append({"kind": "sling", "id": "sling_l", "at": Vector3(-0.118, 0.0, 0.330),
|
|
"dir": Vector3(1, 0, -0.7), "yaw": deg_to_rad(-38.0), "kick": 0.155, "points": 150})
|
|
parts.append({"kind": "sling", "id": "sling_r", "at": Vector3(0.118, 0.0, 0.330),
|
|
"dir": Vector3(-1, 0, -0.7), "yaw": deg_to_rad(38.0), "kick": 0.155, "points": 150})
|
|
# no inlane posts — the outlanes are open, which is the whole personality of this table
|
|
|
|
# a five-bank straight across the middle: it BLOCKS the ramp until you clear it
|
|
for i in 5:
|
|
parts.append({"kind": "drop", "id": "gd_%d" % i, "bank": "gate",
|
|
"at": Vector3(-0.096 + i * 0.048, 0.0, 0.010), "points": 400})
|
|
parts.append({"kind": "ramp", "id": "ramp_c", "at": Vector3(0.0, 0.0, -0.040),
|
|
"yaw": 0.0, "length": 0.26, "width": 0.048, "rise": 0.07, "points": 3000})
|
|
|
|
parts.append({"kind": "spinner", "id": "spin_l", "at": Vector3(-0.170, 0.0, 0.130),
|
|
"yaw": deg_to_rad(16.0), "points": 120})
|
|
parts.append({"kind": "spinner", "id": "spin_r", "at": Vector3(0.170, 0.0, 0.130),
|
|
"yaw": deg_to_rad(-16.0), "points": 120})
|
|
parts.append({"kind": "bumper", "id": "pop_a", "at": Vector3(-0.060, 0.0, -0.290),
|
|
"kick": 0.180, "points": 300})
|
|
parts.append({"kind": "bumper", "id": "pop_b", "at": Vector3(0.060, 0.0, -0.290),
|
|
"kick": 0.180, "points": 300})
|
|
parts.append({"kind": "saucer", "id": "saucer_l", "at": Vector3(-0.150, 0.0, -0.220),
|
|
"eject": Vector3(0.4, 0, 1), "hold": 1.1, "power": 0.22, "points": 4000})
|
|
parts.append({"kind": "saucer", "id": "saucer_r", "at": Vector3(0.150, 0.0, -0.220),
|
|
"eject": Vector3(-0.4, 0, 1), "hold": 1.1, "power": 0.22, "points": 4000})
|
|
for i in 3:
|
|
parts.append({"kind": "rollover", "id": "glane_%d" % i, "glyph": "DIG".substr(i, 1),
|
|
"at": Vector3(-0.085 + i * 0.085, 0.0, -0.420), "points": 200})
|
|
parts.append({"kind": "post", "id": "gp_l", "at": Vector3(-0.100, 0.0, 0.190), "radius": 0.010})
|
|
parts.append({"kind": "post", "id": "gp_r", "at": Vector3(0.100, 0.0, 0.190), "radius": 0.010})
|
|
|
|
return {
|
|
"name": "THE GROTTO",
|
|
"subtitle": "open outlanes. the bank guards the ramp.",
|
|
"width": 0.46, "length": 1.10,
|
|
"palette": {
|
|
"field": Color(0.06, 0.14, 0.13), "rail": Color(0.45, 0.55, 0.52),
|
|
"flipper": Color(0.20, 0.85, 0.75), "bumper": Color(0.95, 0.60, 0.25),
|
|
"sling": Color(0.30, 0.70, 0.95), "target": Color(0.90, 0.35, 0.45),
|
|
"drop": Color(0.85, 0.95, 0.55), "spinner": Color(0.70, 0.95, 0.90),
|
|
"lane": Color(0.30, 0.85, 0.70), "ramp": Color(0.95, 0.75, 0.35),
|
|
"wall": Color(0.14, 0.26, 0.24), "post": Color(0.75, 0.90, 0.86),
|
|
"saucer": Color(0.04, 0.10, 0.09),
|
|
},
|
|
"parts": parts,
|
|
}
|
|
|
|
# ================================================================ TABLE 3
|
|
## THE FOUNDRY — the long one. A tall upper field stacked with pops, twin ramps, and a
|
|
## target array you have to pick apart. Slower, more deliberate, higher ceiling.
|
|
static func foundry() -> Dictionary:
|
|
var parts: Array = []
|
|
parts.append({"kind": "flipper", "id": "flip_l", "at": Vector3(-0.064, 0.022, 0.430),
|
|
"side": -1, "length": 0.082, "limit_lo": -30.0, "limit_hi": 32.0, "torque": 8.0})
|
|
parts.append({"kind": "flipper", "id": "flip_r", "at": Vector3(0.064, 0.022, 0.430),
|
|
"side": 1, "length": 0.082, "limit_lo": -32.0, "limit_hi": 30.0, "torque": 8.0})
|
|
# an upper-left third flipper, fed by the left ramp — the skill shot of this table
|
|
parts.append({"kind": "flipper", "id": "flip_u", "at": Vector3(-0.090, 0.022, -0.120),
|
|
"side": -1, "length": 0.062, "limit_lo": -28.0, "limit_hi": 26.0, "torque": 6.0})
|
|
parts.append({"kind": "sling", "id": "sling_l", "at": Vector3(-0.132, 0.0, 0.350),
|
|
"dir": Vector3(1, 0, -0.5), "yaw": deg_to_rad(-30.0), "kick": 0.130, "points": 100})
|
|
parts.append({"kind": "sling", "id": "sling_r", "at": Vector3(0.132, 0.0, 0.350),
|
|
"dir": Vector3(-1, 0, -0.5), "yaw": deg_to_rad(30.0), "kick": 0.130, "points": 100})
|
|
parts.append({"kind": "post", "id": "fp_l", "at": Vector3(-0.112, 0.0, 0.345)})
|
|
parts.append({"kind": "post", "id": "fp_r", "at": Vector3(0.112, 0.0, 0.345)})
|
|
|
|
# twin ramps flanking the centre
|
|
parts.append({"kind": "ramp", "id": "ramp_l", "at": Vector3(-0.135, 0.0, 0.190),
|
|
"yaw": deg_to_rad(12.0), "length": 0.28, "width": 0.050, "rise": 0.065, "points": 2000})
|
|
parts.append({"kind": "ramp", "id": "ramp_r", "at": Vector3(0.135, 0.0, 0.190),
|
|
"yaw": deg_to_rad(-12.0), "length": 0.28, "width": 0.050, "rise": 0.065, "points": 2000})
|
|
|
|
# a 2x3 target array in the centre you pick apart shot by shot
|
|
for row in 2:
|
|
for col in 3:
|
|
parts.append({"kind": "target", "id": "arr_%d_%d" % [row, col],
|
|
"at": Vector3(-0.058 + col * 0.058, 0.0, 0.020 - row * 0.075),
|
|
"width": 0.030, "points": 350})
|
|
# a four-bank up top
|
|
for i in 4:
|
|
parts.append({"kind": "drop", "id": "fd_%d" % i, "bank": "smelt",
|
|
"at": Vector3(-0.075 + i * 0.050, 0.0, -0.230), "points": 600})
|
|
|
|
parts.append({"kind": "spinner", "id": "spin_c", "at": Vector3(0.0, 0.0, 0.150),
|
|
"points": 150})
|
|
parts.append({"kind": "bumper", "id": "pop_1", "at": Vector3(-0.100, 0.0, -0.330),
|
|
"kick": 0.170, "points": 300})
|
|
parts.append({"kind": "bumper", "id": "pop_2", "at": Vector3(0.0, 0.0, -0.390),
|
|
"kick": 0.170, "points": 300})
|
|
parts.append({"kind": "bumper", "id": "pop_3", "at": Vector3(0.100, 0.0, -0.330),
|
|
"kick": 0.170, "points": 300})
|
|
parts.append({"kind": "saucer", "id": "saucer_deep", "at": Vector3(0.170, 0.0, -0.130),
|
|
"eject": Vector3(-0.6, 0, 1), "hold": 1.2, "power": 0.24, "points": 5000})
|
|
for i in 4:
|
|
parts.append({"kind": "rollover", "id": "flane_%d" % i, "glyph": "CAST".substr(i, 1),
|
|
"at": Vector3(-0.120 + i * 0.080, 0.0, -0.455), "points": 175})
|
|
|
|
return {
|
|
"name": "THE FOUNDRY",
|
|
"subtitle": "twin ramps, an upper flipper, and a long way up.",
|
|
"width": 0.54, "length": 1.20,
|
|
"palette": {
|
|
"field": Color(0.16, 0.10, 0.08), "rail": Color(0.58, 0.52, 0.46),
|
|
"flipper": Color(0.95, 0.55, 0.15), "bumper": Color(0.98, 0.85, 0.35),
|
|
"sling": Color(0.85, 0.40, 0.20), "target": Color(0.75, 0.80, 0.90),
|
|
"drop": Color(0.95, 0.45, 0.25), "spinner": Color(0.90, 0.85, 0.75),
|
|
"lane": Color(0.98, 0.70, 0.30), "ramp": Color(0.65, 0.68, 0.75),
|
|
"wall": Color(0.26, 0.18, 0.14), "post": Color(0.85, 0.80, 0.72),
|
|
"saucer": Color(0.10, 0.06, 0.05),
|
|
},
|
|
"parts": parts,
|
|
}
|
|
|
|
# ================================================================ TABLE 4
|
|
## ORBITAL — everything funnels to the centre. Two saucers flank a long central ramp, and
|
|
## the pops sit in a tight cluster at the top so a ball that gets up there RATTLES.
|
|
static func orbital() -> Dictionary:
|
|
var parts: Array = []
|
|
parts.append({"kind": "flipper", "id": "flip_l", "at": Vector3(-0.060, 0.022, 0.420),
|
|
"side": -1, "length": 0.076, "limit_lo": -32.0, "limit_hi": 30.0, "torque": 7.5})
|
|
parts.append({"kind": "flipper", "id": "flip_r", "at": Vector3(0.060, 0.022, 0.420),
|
|
"side": 1, "length": 0.076, "limit_lo": -30.0, "limit_hi": 32.0, "torque": 7.5})
|
|
parts.append({"kind": "sling", "id": "sling_l", "at": Vector3(-0.124, 0.0, 0.340),
|
|
"dir": Vector3(1, 0, -0.6), "yaw": deg_to_rad(-34.0), "kick": 0.140, "points": 130})
|
|
parts.append({"kind": "sling", "id": "sling_r", "at": Vector3(0.124, 0.0, 0.340),
|
|
"dir": Vector3(-1, 0, -0.6), "yaw": deg_to_rad(34.0), "kick": 0.140, "points": 130})
|
|
parts.append({"kind": "post", "id": "op_l", "at": Vector3(-0.098, 0.0, 0.384)})
|
|
parts.append({"kind": "post", "id": "op_r", "at": Vector3(0.098, 0.0, 0.384)})
|
|
|
|
# the long central ramp — the spine of the table
|
|
parts.append({"kind": "ramp", "id": "ramp_c", "at": Vector3(0.0, 0.0, 0.180),
|
|
"length": 0.34, "width": 0.054, "rise": 0.075, "points": 3500})
|
|
# orbit lanes hugging the ramp, each with a spinner in it
|
|
parts.append({"kind": "spinner", "id": "orb_l", "at": Vector3(-0.148, 0.0, 0.120),
|
|
"yaw": deg_to_rad(14.0), "points": 130})
|
|
parts.append({"kind": "spinner", "id": "orb_r", "at": Vector3(0.148, 0.0, 0.120),
|
|
"yaw": deg_to_rad(-14.0), "points": 130})
|
|
parts.append({"kind": "wall", "id": "orb_gl", "at": Vector3(-0.196, 0.0, 0.120),
|
|
"size": Vector3(0.012, 0.05, 0.220), "yaw": deg_to_rad(5.0)})
|
|
parts.append({"kind": "wall", "id": "orb_gr", "at": Vector3(0.196, 0.0, 0.120),
|
|
"size": Vector3(0.012, 0.05, 0.220), "yaw": deg_to_rad(-5.0)})
|
|
|
|
# twin saucers = the multiball locks, deliberately easy to SEE and hard to HIT
|
|
parts.append({"kind": "saucer", "id": "lock_l", "at": Vector3(-0.165, 0.0, -0.100),
|
|
"eject": Vector3(0.5, 0, 1), "hold": 1.0, "power": 0.21, "points": 3000})
|
|
parts.append({"kind": "saucer", "id": "lock_r", "at": Vector3(0.165, 0.0, -0.100),
|
|
"eject": Vector3(-0.5, 0, 1), "hold": 1.0, "power": 0.21, "points": 3000})
|
|
|
|
# a tight four-pop cluster at the top: get up here and the ball goes mad
|
|
parts.append({"kind": "bumper", "id": "pop_n", "at": Vector3(0.0, 0.0, -0.400), "kick": 0.185, "points": 300})
|
|
parts.append({"kind": "bumper", "id": "pop_s", "at": Vector3(0.0, 0.0, -0.280), "kick": 0.185, "points": 300})
|
|
parts.append({"kind": "bumper", "id": "pop_w", "at": Vector3(-0.068, 0.0, -0.340), "kick": 0.185, "points": 300})
|
|
parts.append({"kind": "bumper", "id": "pop_e", "at": Vector3(0.068, 0.0, -0.340), "kick": 0.185, "points": 300})
|
|
|
|
for i in 3:
|
|
parts.append({"kind": "drop", "id": "od_%d" % i, "bank": "airlock",
|
|
"at": Vector3(-0.058 + i * 0.058, 0.0, -0.185), "points": 550})
|
|
var g := ["O", "R", "B"]
|
|
for i in g.size():
|
|
parts.append({"kind": "rollover", "id": "olane_%d" % i, "glyph": g[i],
|
|
"at": Vector3(-0.090 + i * 0.090, 0.0, -0.470), "points": 175})
|
|
|
|
return {
|
|
"name": "ORBITAL",
|
|
"subtitle": "everything funnels to the centre.",
|
|
"width": 0.50, "length": 1.16,
|
|
"palette": {
|
|
"field": Color(0.04, 0.05, 0.12), "rail": Color(0.60, 0.66, 0.78),
|
|
"flipper": Color(0.30, 0.70, 1.00), "bumper": Color(0.95, 0.95, 0.55),
|
|
"sling": Color(0.60, 0.45, 0.95), "target": Color(0.90, 0.90, 0.95),
|
|
"drop": Color(0.40, 0.95, 0.85), "spinner": Color(0.85, 0.90, 1.00),
|
|
"lane": Color(0.55, 0.70, 1.00), "ramp": Color(0.75, 0.78, 0.88),
|
|
"wall": Color(0.10, 0.14, 0.26), "post": Color(0.88, 0.92, 1.00),
|
|
"saucer": Color(0.02, 0.03, 0.08),
|
|
},
|
|
"parts": parts,
|
|
}
|
|
|
|
# ================================================================ TABLE 5
|
|
## THE DIG — the record shop table, and the one that belongs to this house. The bank is a
|
|
## row of crates you flip through, the ramp is the counter, and the spinner is a turntable.
|
|
## Same universe as the shop in Destroyulator; the theme is the only thing borrowed.
|
|
static func thedig() -> Dictionary:
|
|
var parts: Array = []
|
|
parts.append({"kind": "flipper", "id": "flip_l", "at": Vector3(-0.063, 0.022, 0.405),
|
|
"side": -1, "length": 0.080, "limit_lo": -31.0, "limit_hi": 31.0, "torque": 7.2})
|
|
parts.append({"kind": "flipper", "id": "flip_r", "at": Vector3(0.063, 0.022, 0.405),
|
|
"side": 1, "length": 0.080, "limit_lo": -31.0, "limit_hi": 31.0, "torque": 7.2})
|
|
parts.append({"kind": "sling", "id": "sling_l", "at": Vector3(-0.128, 0.0, 0.325),
|
|
"dir": Vector3(1, 0, -0.55), "yaw": deg_to_rad(-33.0), "kick": 0.138, "points": 125})
|
|
parts.append({"kind": "sling", "id": "sling_r", "at": Vector3(0.128, 0.0, 0.325),
|
|
"dir": Vector3(-1, 0, -0.55), "yaw": deg_to_rad(33.0), "kick": 0.138, "points": 125})
|
|
parts.append({"kind": "post", "id": "dp_l", "at": Vector3(-0.102, 0.0, 0.371)})
|
|
parts.append({"kind": "post", "id": "dp_r", "at": Vector3(0.102, 0.0, 0.371)})
|
|
|
|
# THE CRATES: a six-bank you flip through one sleeve at a time. Clearing it is
|
|
# "finding the record", which is the entire fantasy of a record shop.
|
|
for i in 6:
|
|
parts.append({"kind": "drop", "id": "crate_%d" % i, "bank": "crates",
|
|
"at": Vector3(-0.112 + i * 0.050, 0.0, 0.070), "points": 450})
|
|
|
|
# the turntable: a spinner you can whip, dead centre
|
|
parts.append({"kind": "spinner", "id": "deck", "at": Vector3(0.0, 0.0, -0.020),
|
|
"points": 160})
|
|
# the counter — a ramp up to the register
|
|
parts.append({"kind": "ramp", "id": "counter", "at": Vector3(-0.163, 0.0, 0.210),
|
|
"yaw": deg_to_rad(7.0), "length": 0.28, "width": 0.050, "rise": 0.062,
|
|
"points": 2200})
|
|
# listening booth: capture the ball, hold it a good long while
|
|
parts.append({"kind": "saucer", "id": "booth", "at": Vector3(0.160, 0.0, -0.150),
|
|
"eject": Vector3(-0.5, 0, 1), "hold": 1.4, "power": 0.22, "points": 4500})
|
|
|
|
# the wall of rare sleeves — a target array that pays
|
|
for i in 4:
|
|
parts.append({"kind": "target", "id": "rare_%d" % i,
|
|
"at": Vector3(-0.175 + i * 0.030, 0.0, -0.250), "yaw": deg_to_rad(18.0),
|
|
"width": 0.026, "points": 400})
|
|
parts.append({"kind": "bumper", "id": "pop_a", "at": Vector3(0.080, 0.0, -0.330),
|
|
"kick": 0.170, "points": 275})
|
|
parts.append({"kind": "bumper", "id": "pop_b", "at": Vector3(0.150, 0.0, -0.395),
|
|
"kick": 0.170, "points": 275})
|
|
parts.append({"kind": "bumper", "id": "pop_c", "at": Vector3(0.010, 0.0, -0.400),
|
|
"kick": 0.170, "points": 275})
|
|
var g := ["D", "I", "G"]
|
|
for i in g.size():
|
|
parts.append({"kind": "rollover", "id": "dlane_%d" % i, "glyph": g[i],
|
|
"at": Vector3(-0.100 + i * 0.090, 0.0, -0.455), "points": 180})
|
|
|
|
return {
|
|
"name": "THE DIG",
|
|
"subtitle": "flip the crates. find the record.",
|
|
"width": 0.52, "length": 1.12,
|
|
"palette": {
|
|
"field": Color(0.14, 0.07, 0.13), "rail": Color(0.58, 0.55, 0.60),
|
|
"flipper": Color(1.00, 0.36, 0.62), "bumper": Color(0.98, 0.80, 0.30),
|
|
"sling": Color(0.50, 0.85, 0.70), "target": Color(0.85, 0.60, 0.95),
|
|
"drop": Color(0.35, 0.65, 0.95), "spinner": Color(0.20, 0.20, 0.24),
|
|
"lane": Color(1.00, 0.50, 0.70), "ramp": Color(0.55, 0.38, 0.24),
|
|
"wall": Color(0.24, 0.12, 0.22), "post": Color(0.92, 0.88, 0.95),
|
|
"saucer": Color(0.08, 0.04, 0.08),
|
|
},
|
|
"parts": parts,
|
|
}
|