Skip to content

Congo

VPW mod featuring nFozzy flipper physics corrections, modulated flasher implementation, playfield mesh for proper ball physics, and VR room with cabinet mode options.

Build Guide

Version history shows progressive improvements from v003 (remapped ramps) through v1.8 (fixed bumper hit threshold). Key versions: v016 (organized table, added VR room), v027 (added playfield mesh), RC3 (fixed GI control, added Rubberizer/TargetBouncer), RC8 (released on VPU).

Scripting

nFozzy Flipper Angle Must Be Negative

Right flipper angle was set to a positive value (238) instead of the correct negative value (-122). This broke the flipper physics code -- live catch, nudge, and flipper tricks were all non-functional.

Fix: Use negative angles for right flippers. Additionally, CheckLiveCatch was never called anywhere and had to be added to flipper_collide subs.

SolFlash Timer Pattern for Flasher Fading

Standard flasher fading pattern using a dedicated timer per flasher solenoid (from Hook):

Sub SolFlash12(enabled)
    if Enabled then
        FlashLevel12 = 1
        Flasherf12_timer
    end if
End Sub

sub Flasherf12_timer
    If not Flasherf12.TimerEnabled Then
        Flasherf12.TimerEnabled = True
    End If
    flashx3 = FlashLevel12*FlashLevel12*FlashLevel12
    Flasherf12.opacity = 110 * flashx3
    f12a1.IntensityScale = 3 * flashx3
    f12a2.IntensityScale = 3 * flashx3
    FlashLevel12 = FlashLevel12 * 0.93 - 0.01
    If FlashLevel12 < 0 Then
        Flasherf12.TimerEnabled = False
    End If
end sub

Uses cubic exponential (FlashLevel^3) for brightness calculation. Each solenoid-controlled flasher should have its own timer.

Modulated Solenoid Flasher with Bi-directional Fading

Advanced flasher code for modulated solenoids that fade between levels (used for Amy flasher, sol17). Supports ramping up fast and fading down smoothly. The timer uses FlashLevel17^5 for opacity and FlashLevel17^3 for IntensityScale.

SolCallback vs SolModCallback

SolCallback gives binary 0 or 1 from a solenoid. SolModCallback provides a fine-grained 0-255 value from modulated solenoids. Not all tables/solenoids support SolModCallback. Best practice: allocate a dedicated timer for each flasher solenoid with fading equations.

Rubberizer: Standup Target Bounce Enhancement

Standup targets in VPX feel lifeless compared to real machines where they bounce significantly. The "Rubberizer" is a script option that applies a random Z-velocity multiplier on target hit to boost bounce. Makes gameplay more realistic but slightly harder to control.

CabinetMode Script Pattern

Three-tier cabinet mode implementation:

'Cabinet mode 0 - Default (rails visible, normal sideblades)
'Cabinet mode 1 - Hide rails, scale side panels higher
'Cabinet mode 2 - Hide rails and side panels entirely
Const CabinetMode = 0

If CabinetMode = 1 Then
    PinCab_Rails.visible = 0
    PinCab_Blades.Size_y = 2000
ElseIf CabinetMode = 2 Then
    PinCab_Rails.visible = 0
    PinCab_Blades.visible = 0
Else
    PinCab_Rails.visible = 1
    PinCab_Blades.Size_y = 1000
End If

Mode 2 (no rails or blades) works well on physical cabinets which already have real sidewalls.

LUT Changer Implementation with Failsafe

LUT position save files can become corrupted (saving an empty file prevents table loading). Implemented a failsafe mechanism to handle corrupted LUT save files. Include a variety of LUT options, as the preferred LUT differs between cabinet and desktop play. Use a 1:1/neutral LUT as default rather than a stylized one.

3D & Art

Backfacing Transparent Rendering for Ramps

Enabling "Render Backfacing Transparent" on plastic ramp primitives significantly improves their appearance. However, this setting completely breaks ramps in VR.

Solution: Toggle via script at game init:

If VRRoom = 0 Then
    Prim_Ramp1.BackfacesEnabled = True
    Prim_Ramp2.BackfacesEnabled = True
    Prim_Ramp3.BackfacesEnabled = True
End If

VPX Lighting Formula

VPX lighting calculation: environment image * day/night slider * emission scale value (assuming the two ancient VPX lights in lighting settings are switched off).

Ramp Geometry Cleanup in Blender

When preparing VPX-exported ramp geometry in Blender: (1) Merge vertices by distance to connect unmerged polygons, (2) Remove internal faces that mess up materials, (3) Add a bevel modifier before subdivision, (4) Add subdivision modifier to smooth out normal/triangle issues.

Ramp Texture Technique

For ramp textures, copy the Blender render output multiple times in Photoshop to reduce transparency and get a better refraction effect. VR ramps don't need color -- a glossy, dirty texture with no color works best in VR because the environment provides the color information.

Playfield Mesh for Proper Ball Physics

A playfield_mesh primitive imported at position 0,0 with size 100 replaces the flat playfield for ball physics. The name must be exactly playfield_mesh. Areas with no faces become transparent (automatic holes for kickers/scoops).

Critical: The mesh is rendered statically, so DL (Disable Lighting) must be set in the editor before game load. Misaligning the mesh even slightly causes insert lights to appear offset.

GI Lighting Style Philosophy

Two competing lighting philosophies in VPW:

  1. Skitso's "dark ambient" approach - table illuminated solely by its own lights as if in a dim arcade. Uses multiple GI lamps per bulb (3-4) for natural indirect lighting and soft shadows.
  2. Brighter environmental approach with shinyenvironment for proper metal reflections, which flattens the GI lighting but works better in bright rooms and VR.

Neither is wrong - LUT selection and environment image significantly affect the final result.

Environmental Image Selection

The NF HDR environment image makes all metals appear flat grey with no metallic reflections, especially in VR. shinyenvironment3blur4 provides proper metal reflections and is the reliable default.

Troubleshooting

Playfield Mesh Misalignment Breaks Insert Positions

After adding a playfield mesh, all inserts appeared misaligned from their light positions. Root cause: the playfield mesh was accidentally nudged from 0,0 during kicker adjustments.

Fix: Ensure playfield_mesh primitive is exactly at 0,0.

Warped Playfield Image Causing Physics Problems

Congo's VPX table was built on a warped playfield image that was not dimensionally correct. This caused balls to get stuck under the top-left flipper during multiball and general physics oddities. A corrected, non-warped playfield was eventually provided by nFozzy but required realigning all table elements.

Kickback Using Plunger Instead of Impulse Plunger

Congo's kickback used a regular plunger instead of an impulse plunger, causing the kickback to sometimes fail (ball drains instead of being kicked out). Also occasionally double-fires.

Fix: Replace with impulse plunger for more reliable behavior.

Flipper Area Playfield Mesh Lines Cause Ball Hovering

The ball sometimes hovers halfway up a flipper until you flip. This is caused by playfield mesh geometry lines intersecting the flipper area.

Fix: Avoid creating playfield mesh polygon edges in the flipper area.

Ball Drop Sounds Not Triggering from Ramps

Ball drop sounds require specific VelZ and Z-height conditions in the CorMaterial sub. Congo had issues: (1) A hidden wall on layer 11 blocked the ball exit from the wire ramp. (2) The DropCount throttle mechanism was implemented incorrectly. (3) VelZ values ranged from -4 to -14 on ramp drops.

Resources

WPC ROM Volume Maximum Causes Audio Distortion

Setting WPC ROM volume to maximum (31) causes audio distortion, especially noticeable with bumper sounds.

Fix: Set volume to 30 instead of 31. VPW tables also include a VolumeDial script option to reduce mechanical/table sounds volume independently.

Anti Ball Stretch Requires Monitor Aspect Ratio Setting

Checking "Anti Ball Stretch" in VPX is not sufficient alone - you must also enter your monitor's aspect ratio (e.g., 16:9) in the video settings. This keeps the ball round regardless of POV settings.

LUT Dynamic Range - Can Reduce But Not Expand

LUTs can reduce dynamic range (desaturate, compress) but cannot expand it. Better to have full saturation/detail in the base rendering and use LUTs to pull it down to taste.