Skip to content

Monster Bash

The Monster Bash Skitso Mod was a landmark VPW collaborative project, developed from December 2020 to February 2022 with 30+ release candidates. Originally a "Skitso mod" of randr/Unclewilly's table, it became the first table released under the VPW brand. Key contributors: Skitso (visuals/project lead), rothbauerw (physics/sounds), iaakki (scripting/flasher code), sixtoe (VR integration), bord1947 (3D mesh work), tomate80 (wire ramp textures), and many others.

Build Notes

Playfield Mesh and Physics Collision

Bord1947 provided a playfield_mesh.obj and a separate phys_plywood.obj -- a physics-only collidable mesh that gives plywood sides of playfield holes a collision surface. The physics mesh should NOT be made visible; it is purely for ball collision.

PWM/Lampz Upgrade Path

MB uses older JP (Jordy Pijpers) fading code for lamp control. Upgrading to PWM requires converting from JP fading to either Lampz (intermediate step) or directly to PWM. Iaakki had a macro to generate Lampz mass-assignments from JP fading code. The table's "epic light shows" make PWM particularly worthwhile but the conversion is non-trivial.

VPX Version Requirements

UpdateMaterial function requires VPX 10.6.1 or newer. Also recommended: SAMBuild r5240.

3D & Art

Skitso's Lighting Methodology

  1. Use normal playfield (no artificial darkening)
  2. Achieve mood with day/night slider, environmental image, and carefully crafted LUT
  3. Go through GI lighting one by one, comparing with PAPA videos for reference on color temperature
  4. Use Pinside/reference images for light obstruction, shadow behavior, and light transfer through plastics
  5. Go through inserts last

Key philosophy: "To have a virtual pinball machine look good is not to make one element more realistic -- it's about the whole big picture looking cohesive."

Insert Lighting Technique

Skitso uses two VPX light objects per insert (for inserts without defined edges like monster heads) -- one for the insert itself and a second to smooth edges and prevent harsh/sharp appearance. This technique requires playfield reflections to be enabled in VPX global settings; disabling PF reflections causes dark squares around the inserts.

LUT (Color Grading) Philosophy

The table uses fading LUTs (multiple ColorGrade images) that change with GI level for darker ambient when lights go out:

If Step >= 7 Then
    Table1.ColorGradeImage = "ColorGrade_8"
Else
    Table1.ColorGradeImage = "ColorGrade_" & (step+1)
End If

Skitso strongly opposed including a LUT selector, preferring a single curated visual vision.

Wire Ramp Texture Improvements

Tomate80 improved wire ramp textures with better contrast between bright and dark parts. New textures added ~6MB to file size but required no model changes -- just better texture rendering with improved lighting/shadows baked in.

Tesla Coil Dome Replacement

Flupper suggested stacking two half-ball flasher domes for the Tesla coil globes. The final implementation uses 4 half-domes (2 per globe) across 4 depth biases with 8 total primitives, using a custom purple texture.

Scripting

FadeDisableLighting for Insert Tray Brightness

Sub FadeDisableLighting(nr, a, alvl)
    Select Case FadingState(nr)
        Case 4
            a.UserValue = a.UserValue - 0.25
            If a.UserValue < 0 Then a.UserValue = 0
            a.BlendDisableLighting = alvl * a.UserValue
        Case 5
            a.UserValue = a.UserValue + 0.5
            If a.UserValue > 1 Then a.UserValue = 1
            a.BlendDisableLighting = alvl * a.UserValue
    End Select
End Sub

Flupper Flasher Dome Implementation

Flupper flasher domes use multiple components: base primitive, lit primitive, flasher object, and light object. Key control: objlit(nr).BlendDisableLighting = 10 * ObjLevel(nr)^2. The ^x exponent controls fading speed -- higher values = faster fade. Different exponents on different components create natural-looking fading:

objflasher(nr).opacity = 1000 * FlasherFlareIntensity * sol19_3lvl^2.5
objlight(nr).IntensityScale = 1 * FlasherLightIntensity * sol19_3lvl^3
objbase(nr).BlendDisableLighting = FlasherOffBrightness + 30 * sol19_3lvl^2 + 0.1
objlit(nr).BlendDisableLighting = 15 * Sol19_3lvl^0.9 + 0.1

Objects nearest the light origin should fade slowest (lowest exponent).

Modulated Solenoid Flashers

Frankenstein flashers were switched from SetModLamp to SolModCallback for modulated output. SetModLamp always starts at max (255) and can't pulse. With SolModCallback, output follows actual solenoid ROM values -- most hits are 150-160, reaching max only at "It's Alive" and Jackpots.

FadeModLamp vs FadeLamp Termination Rules

In the JP fading system, lamp routines with "Mod" suffix (NFadeLm, Flashm, FadeDisableLightingM) do NOT terminate the fading cycle. The last routine in a sequence MUST use the non-Mod version:

' Correct order:
FadeDisableLighting 81, cfeatures2d, 0.85
Flash 81, l81ref  ' This terminates the fade

Dynamic Ball Shadows

Array-based implementation performs better than collections (especially in VR):

  • Max 2 simultaneous shadow sources recommended
  • Limit DynamicSources to lights that actually cast visible shadows
  • Shadow intensity: 0.95-0.98 range
  • When adding GI bulbs near each other, only include 1-2 to prevent flickering
  • Dynamic shadows have the biggest performance impact of any feature -- first thing to disable for performance

VR Mode Detection

' Wrong (logic error - always true):
If VRRoom = 0 or DesktopMode = 0 Then

' Correct - show in cabinet only:
If VRRoom <> 0 or DesktopMode Then
    ' VR or Desktop code
Else
    ' Cabinet-only code
End If

VR can run from cabinet mode, so don't assume VR = desktop.

Flasher Fading Performance Optimization

Optimize by adjusting timer interval and fade equation:

  • Original: timer=30, equation 0.9 * objlevel = 23 fading steps
  • Optimized: timer=35, equation 0.8 * objlevel = 14 fading steps
  • Fewer steps = fewer semi-transparent texture overlaps = better GPU performance

Key insight: overlapping semi-transparent textures are the main performance bottleneck, not script execution.

Troubleshooting

VR Swimming Lights

Semi-transparent objects set to static rendering cause "swimming" lights when you move your head in VR. Fix: untick "Static Rendering" (set to Active). For Monster Bash, setting the playfield opacity to 1.0 instead of 0.999 and using a cutout for the Creature area resolved the issue.

Dracula Mechanic Array Bounds Error

When the Dracula mechanic sends invalid position values, add bounds checking:

If mechpos < 5 then mechpos = 5
If mechpos > 85 then mechpos = 85

Target Bouncer Causing Ball Jumps

Target bouncer gives z-velocity on first hit, then a second target amplifies it. Fix: add "ceiling" walls above target areas to prevent balls from jumping too high.

Stuck Balls from Overlapping Geometry

Ramps made of walls (collidable) AND a primitive mesh create a "hill" at ramp exits. Fix: modify the primitive to match the ramp exit shape and delete the redundant walls.

VPX 10.8 Normal Map Rendering Regression

VPX 10.8 broke rendering with funky textures on Frankenstein and other objects. Root causes: normal mapping issues in both DirectX and OpenGL, and reliance on a long-standing VPX bug where texture transparency was ignored if material opacity was 1.0. Niwak pushed an automatic fix to VPX that corrects old tables on load.

Pop Bumper GI Washout

Pop bumper area appeared washed out in orange light. User fix: find rollover lane guide lamps in GITopRight collection, raise falloff power from 4 to 10, lower falloff range from 150 to 50.

Collidable Decorative Primitives

Small decorative primitives (nuts around Frank, slingshot nuts) were accidentally set as collidable. Always audit small decorative prims to ensure they're non-collidable.

Game Knowledge

Phantom Flip Feature

Monster Bash's Phantom Flip uses eddy sensors under the playfield above each flipper. The ROM calculates ball speed from inlane switch to sensor timing. Fresh NVRam = no phantom flips until enough data collected. On real machines, this feature is notoriously unreliable. Tournament mode disables phantom flips. Can be disabled in ROM settings (F1 menu).

Best Practices

Table Release Process

  • 90-day "bro code" waiting period before mods are published
  • Modders must credit original team and link to original file
  • VPU Patching System (by Dazz) creates small diff patches requiring the original download
  • Table went through RC1-RC10 testing cycle with dedicated testers before release

POV Configuration

  • Z-scale should always be 1
  • Maintain perspective for POV-dependent lighting/model tricks
  • Minimize egg-shaped balls (X:Y scale near 1:1)
  • Some tables have POV-dependent flasher effects that break with altered POV

VR Performance (Order of Impact)

  1. Collidable prims (most impact)
  2. Big/complex textures
  3. Complex/high-poly prims in viewport
  4. Semi-transparent texture overlaps (flashers)
  5. Everything renders twice (once per eye)

Set VR far plane to 2000-3000 to limit render distance.

Display Calibration

Many complaints about table darkness stem from uncalibrated displays. Old/faded LCDs, low brightness settings, and incorrect GPU driver color settings (limited vs full range) significantly affect perceived brightness.

Resources

  • PAPA tutorial video (physics reference): Monster Bash at PAPA HQ
  • VPU Patching System: VPU Patcher
  • Rubberizer script adds negligible performance overhead and doesn't affect ball spin