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:
- 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.
- 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.