Guest User

BadCats 1.2 vpmflips Debug script

a guest
Jan 7th, 2018
485
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
VBScript 164.38 KB | None | 0 0
  1. '**********************
  2. '  Bad Cats(1989)
  3. ' VPX table by unclewilly,Clark Kent, Dark
  4. ' version 1.0
  5. '**********************
  6.  
  7. Option Explicit
  8. Randomize
  9. Dim FlipLag
  10. FlipLag = 0  'Enable/Disable FlipperLag Fix  0 is disable 1 is enable
  11. Dim xx, DNS
  12. Dns = table1.NightDay
  13.  
  14. If DNS <= 5 or DNS >= 75 then
  15.     For each xx in aGiLights:xx.intensity = xx.intensity *(1-(DNS/100)):Next
  16.     For each xx in aAllFlashers:xx.opacity = xx.opacity *(1-(DNS/100)):Next
  17.     For each xx in AllLamps:xx.intensity = xx.intensity *(1-(DNS/100)):Next
  18.     For each xx in TargetDropGi:xx.intensity = xx.intensity *(1-(DNS/100)):Next
  19. else
  20.     If DNS <= 40 or DNS >= 80 Then
  21.         For each xx in aGiLights:xx.intensity = xx.intensity *(.5-(DNS/100)):Next
  22.         For each xx in aAllFlashers:xx.opacity = xx.opacity *(.5-(DNS/100)):Next
  23.         For each xx in AllLamps:xx.intensity = xx.intensity *(.5-(DNS/100)):Next
  24.     For each xx in TargetDropGi:xx.intensity = xx.intensity *(.5-(DNS/100)):Next
  25.     else
  26.         For each xx in aGiLights:xx.intensity = xx.intensity *(.7-(DNS/100)):Next
  27.         For each xx in aAllFlashers:xx.opacity = xx.opacity *(.7-(DNS/100)):Next
  28.         For each xx in AllLamps:xx.intensity = xx.intensity *(.7-(DNS/100)):Next
  29.     For each xx in TargetDropGi:xx.intensity = xx.intensity *(.7-(DNS/100)):Next
  30.     end if
  31. end if
  32.  
  33.  
  34.  
  35. solGI 0
  36.  
  37. On Error Resume Next
  38. ExecuteGlobal GetTextFile("controller.vbs")
  39. If Err Then MsgBox "You need the controller.vbs in order to run this table, available in the vp10 package"
  40. On Error Goto 0
  41.  
  42.  
  43.  
  44.  
  45.  
  46. 'LoadVPM "01550000", "S11.vbs", 3.26
  47. 'Last Updated in VBS v3.36
  48.  
  49.  
  50.  
  51.  
  52. LoadController("VPM")
  53. 'Option Explicit
  54. LoadCore
  55. Private Sub LoadCore
  56.     On Error Resume Next
  57.     If VPBuildVersion < 0 Or Err Then
  58.         Dim fso : Set fso = CreateObject("Scripting.FileSystemObject") : Err.Clear
  59.         'ExecuteGlobal fso.OpenTextFile("core.vbs", 1).ReadAll    : If Err Then MsgBox "Can't open ""core.vbs""" : Exit Sub
  60.         ExecuteGlobal fso.OpenTextFile("VPMKeys.vbs", 1).ReadAll : If Err Then MsgBox "Can't open ""vpmkeys.vbs""" : Exit Sub
  61.     Else
  62.         'ExecuteGlobal GetTextFile("core.vbs")    : If Err Then MsgBox "Can't open ""core.vbs"""    : Exit Sub
  63.         ExecuteGlobal GetTextFile("VPMKeys.vbs") : If Err Then MsgBox "Can't open ""vpmkeys.vbs""" : Exit Sub
  64.     End If
  65. End Sub
  66.  
  67. 'Option Explicit
  68. Const VPinMAMEDriverVer = 3.56
  69. '=======================
  70. ' VPinMAME driver core.
  71. '=======================
  72. ' New in 3.56 (Update by )
  73. '
  74. ' New in 3.55 (Update by nFozzy)
  75. ' - Prevent 'object not a collection' errors if vpmNudge.TiltObj isn't set
  76. ' - Support for double leaf flipper switches
  77. '   - For now, keybinds for these staged flippers are defined in VPMKeys.vbs. By default they are set to LeftFlipperKey and RightFlipperKey, disabling them.
  78. '   - Adapting older tables requires vpmFlips: Create upper flipper subs and point SolCallback(sULFlipper) and SolCallback(sURFlipper) to them.
  79. '   - This may break compatibility with some older WPC tables that use the 'cSingleLFlip' method (More info in WPC.vbs), note that close to no 'modern' (e.g. VP8/VP9/VPX) table uses this anyway
  80. ' - Integrated FastFlips, (new object vpmFlips): Low latency flipper response for games with pre-solid state flippers
  81. '   - Ensure 'vpmInit me' is called in the table init section
  82. '   - UseSolenoids = 2 enables and auto sets the game-on solenoid (based on GameOnSolenoid in the system .vbs script)
  83. '   - Important info on supported WPC games is documented in WPC.vbs
  84. '   - Pre-solid-state flipper games (except Zaccaria and LTD) should work perfectly. This includes Bally/Williams WPCs up to Terminator 2 / Party Zone
  85. '   - Data East / early Segas will work perfectly, unless they have ROM-controlled flipper effects
  86. '   - Fliptronics and WPC-S games (Addams Family through Jack Bot / WHO Dunnit) will work with caveats (no ROM controlled flipper effects, beware stuck balls. More info in WPC.vbs)
  87. '   - Sega Whitestar (Apollo 13 / Goldeneye / etc), WPC95 (Congo / AFM / etc), and Capcom and everything onward will not work
  88. '   - There's also a debug test command which may be useful if it's not working properly. Open the debug window (Accessible from the VP-escape menu, press the ">" button to bring up the text field) and type in 'vpmFlips.DebugTest'
  89. '
  90. ' New in 3.54 (Update by mfuegemann & nFozzy & Ninuzzu/Tom Tower & Toxie)
  91. ' - Added UltraDMD_Options.vbs to configure Ultra DMD based tables globally (see the file itself for detailed descriptions)
  92. ' - Added sam.vbs
  93. ' - Added Class1812.vbs
  94. ' - Added inder_centaur.vbs
  95. ' - Restore basic functionality of cvpmDropTarget.CreateEvents for drop targets with an animation time (e.g. VP10 and newer)
  96. ' - Minor cleanups and code unifications for all machines
  97. ' - Add keyConfigurations to VPMKeys.vbs for Taito and also remap the hardcoded keycode '13' to keySoundDiag
  98. '
  99. ' New in 3.53 (Update by Toxie)
  100. ' - Add more key mappings to help dialog
  101. '
  102. ' New in 3.52 (Update by DJRobX & Toxie)
  103. ' - Change default interval of the PinMAME timer to -1 (frame-sync'ed) if VP10.2 (or newer) is running
  104. ' - Add modulated solenoids to support ROM controlled fading flashers:
  105. '   To use, add "UseVPMModSol=True" to the table script
  106. '   Also use SolModCallback instead of SolCallback to receive level changes as input: It will be a level from 0 to 255.
  107. '   Just continue to use SolCallback if you only care about boolean values though, as it will only fire if level changes from on to off.
  108. '   Note: vpmInit MUST BE CALLED or VPM will not switch modes (if you are only getting 0 and 1 from SolModCallback then that may be the issue)
  109. '
  110. ' New in 3.51 (Update by mfuegemann & Arngrim & Toxie)
  111. ' - gts1.vbs dip fix
  112. ' - Add comments to cvpmDropTarget.CreateEvents: do not use this anymore in VP10 and above, as drop targets have an animation time nowadays
  113. ' - Change default interval of the PinMAME timer to 3 if VP10 (or newer) is running, and leave it at 1 for everything else
  114. ' - Fix missing SlingshotThreshold() when using VP8.X
  115. ' - (Controller.vbs changes)
  116. '   - now its allowed to have each toy to be set to 0 (sound effect), 1 (DOF) or 2 (both)
  117. '   - new DOF types: DOFFlippers, DOFTargets, DOFDropTargets
  118. '   - all values are now stored in the registry (HKEY_CURRENT_USER\SOFTWARE\Visual Pinball\Controller\), and can also be changed from within VP10.2 and above
  119. '   - InitializeOptions call added to the controller init, for tables that want to use this functionality during gameplay (options menu via F6)
  120. '
  121. ' New in 3.50 (Update by Toxie & mfuegemann & Arngrim)
  122. ' - Added MAC.vbs & IronBalls.vbs & Lancelot.vbs & Antar.vbs
  123. ' - (Core changes)
  124. '   - Increased NVOffset limit from 10 to 32
  125. '   - Use temporary variables for Switch() calls to workaround current PROC issues
  126. '   - Controller.vbs user-folder detection fix, and add simple PROC usage via LoadPROC (see Controller.vbs for details)
  127. '   - Add UseVPMNVRAM = true to the table script (place before LoadVPM, or otherwise calling core.vbs)
  128. '     to make changed content of NVRAM available (since last update) via the NVRAMCallback (delivers a three dimensional array with: location, new value, old value)
  129. '     (requires VPM 2.7 or newer)
  130. '
  131. ' New in 3.49 (Update by Arngrim)
  132. ' - Add new Controller.vbs to abstract DOF, B2S, VPM and EM controller loading, usage and sound/effect handling,
  133. '   see Controller.vbs header on how to use it exactly
  134. '
  135. ' New in 3.48 (Update by JimmyFingers)
  136. ' - (Core changes)
  137. '   - Changed vpmNudge.TiltObj handling to use Bumper.Threshold / Wall.SlingshotThreshold temporary value changes rather than force / SlingshotStrength changes to disable tiltobj array objects
  138. '   - There existed a bug in VP since at least the 9.x versions where the Wall.SlingshotStrength value being set by scripting during game play did change the value but the slingshot behaviour / "Slingshot Force" (from the editor) of the wall object did not change (i.e. did not have an effect); As a result the attempted disabling of bumpers and slingshots after a tilt event on supported games (that can send a relay for vpmNudge.SolGameOn ) would only work for the bumper objects
  139. '   - Using thresholds instead also now has added benefit by not actually triggering the related _Hit or _Slingshot routines so animations, sound processing, and other potential nested subroutine calls will also not activate resulting in a better tilt simulation
  140. '   Note: NudgePlugin option .vbs files were also updated as they contain and are reassigned the vpmNudge routines when invoked
  141. '
  142. ' New in 3.47 (Update by Toxie)
  143. ' - (Core changes)
  144. '   - Add UseVPMColoredDMD = true to the table script (place before LoadVPM, or otherwise calling core.vbs)
  145. '     to automatically pass the raw colored DMD data (RGB from 0..255) from VPM to VP (see VP10+ for details on how to display it)
  146. '
  147. ' New in 3.46 (Update by KieferSkunk)
  148. ' - (Core changes)
  149. '   - Added two new classes: cvpmTrough and cvpmSaucer
  150. '     - cvpmTrough takes over for cvpmBallStack in non-Saucer mode.
  151. '       - Can handle any number of balls (no more "out of bounds" errors with lots of balls)
  152. '       - Accurately simulates ball movement and switch interaction in a real trough
  153. '     - cvpmSaucer takes over for cvpmBallStack in Saucer mode.
  154. '     - cvpmBallStack is now considered "legacy" - kept for compatibility with existing tables.  (No changes)
  155. '   - Updated vbsdoc.html with these new classes.
  156. '   - Added two helper functions, vpMin(a, b) and vpMax(a, b).
  157. '     - These each take two numbers (or strings) and return the lower or higher of the two (respectively).
  158. '
  159. ' New in 3.45 (Update by KieferSkunk)
  160. ' - (Core changes)
  161. '   - Rewrote cvpmDictionary as a wrapper around Microsoft's Scripting.Dictionary object.
  162. '     This provides two major benefits:
  163. '     (1) Improved performance: Keys are stored by hash/reference, not by index, and existence checks and key location are now O(1) instead of O(N) operations.
  164. '     (2) Keys and Items can now both be primitive types or objects.  You can use integers, strings, etc. as keys, and you can use any object as an Item.
  165. '         Note: The only restriction is that a Key cannot be a Scripting.Dictionary or an Array.
  166. '   - cvpmTurnTable now smoothly changes speeds and directions.  You can adjust the following properties to change the turntable's behavior:
  167. '     - MaxSpeed: Sets new maximum spin speed.  If motor is on, turntable will smoothly accelerate to new speed.
  168. '     - SpinUp: Sets new spin-up rate.  If currently accelerating, turntable will accelerate at the new rate.
  169. '     - SpinDown: Sets new spin-down rate.  If currently slowing to a stop, turntable will decelerate at the new rate.
  170. '     - SpinCW: True for clockwise rotation, False for counter-clockwise.  If motor is on, switching this will smoothly reverse the turntable's direction.
  171. '
  172. ' New in 3.44 (Update by Toxie)
  173. ' - (Core changes)
  174. '   - Added ability to define default ball mass (in VP Units) inside table script.
  175. '       Defaults to 1 unit if undefined. Example...
  176. '           Const BallMass = 2 '(place before LoadVPM, or otherwise calling core.vbs)
  177. '       Note that this should be used if changing the ball size via BallSize,
  178. '       as the mass is of course proportional to the radius of the ball: m=k*r^3.
  179. '       One can also use the diameter/size like in VP, so BallMass=k*BallSize^3 with k=1/125000.
  180. '       Example: BallSize = 55, so BallMass = (55*55*55)/125000 = 1.331.
  181. '   - Add UseVPMDMD = true to the table script (place before LoadVPM, or otherwise calling core.vbs)
  182. '     to automatically pass the raw DMD data (levels from 0..100) from VPM to VP (see VP10+ for details on how to display it)
  183. '   - Add toggleKeyCoinDoor in VPMKeys.vbs to choose between a real coindoor setup (e.g. cabinets) and the 'classic' on/off behaviour (e.g desktops/keyboards)
  184. '   - Add inverseKeyCoinDoor in VPMKeys.vbs to in addition choose between the behaviour of a real coindoor switch (key pressed = closed, key not pressed = open)
  185. '     or the inverted behaviour (key pressed = open, key not pressed = closed)
  186. '   - Increase maximum number of balls/conMaxBalls to 13 and conStackSw to 8 (for Apollo 13), use InitSw8() then instead of InitSw()
  187. '   - Deprecate vpmSolFlip2, as VP10 does not feature speed on flippers anymore
  188. '
  189. ' New in 3.43 (Update by Koadic)
  190. ' - (Core Changes)
  191. '   - Minor adjustment to vbs loading via LoadScript to account for files in nonstandard locations
  192. '   - Fix minor bugs when loading some tables
  193.  
  194. ' New in 3.42 (Update by Koadic)
  195. ' - (Core Changes)
  196. '   - Minor adjustment to vpmInit to unpause controller before stopping controller
  197. '
  198. ' New in 3.41 (Update by Koadic)
  199. ' - (Core Changes)
  200. '   - Modified vpmInit routine:
  201. '       Added creation of _Exit routine to vpmInit to perform Controller.Stop (will retroactively effect all tables using vpmInit call)
  202. '       Modified vpmInit to create _Paused, _UnPaused, and _Exit separately, so if any don't exit, they will be created individually
  203. '       Modified Error handling to fix bug where vmpInit might throw "Invalid procedure call or argument" error
  204. '        and cause table not to work due to improper Table_Init scripting.
  205. '   - Added 2 functions: CheckScript(file) and LoadScript(file) that can return True/False as well as the latter loading the script if true.
  206. '      These check for existance in either the Tables and Scripts directory and can return a boolean value as well as the LoadScript autoloading
  207. '       the file, as opposed to my previous methods only checking the local folder containing the table being run.
  208. '      CheckScript(file) checks for existance, and if found returns a True value
  209. '      LoadScript(file) checks for existance, and if found, loads specified file (via ExecuteGlobal GetTextFile(file)) and returns a True value
  210. '       Examples:
  211. '           If LoadScript("thefile.vbs") Then DoThisOtherThing  ' If Loadscript found 'thefile' and loaded it (returned true) then do this other thing
  212. '           LoadScript("somefile.vbs")                          ' Checks for 'somefile' and loads it if it exists
  213. '   - Reworked CheckLEDWiz routine into generic LoadScript(file) routine to allow for better detection of script in the VP tables
  214. '      or scripts directory, not just current directory containing the table.
  215. '   - Added ability to load NudgePlugIn.vbs and if found, it will be loaded and replace current default nudging class.
  216. '       - This detection and autoloading can allow for 'on demand' replacement of other core components as well in the future.
  217. '   - Added ability to load GlobalPlugIn.vbs containing any custom scripting the user wants loaded with the core.vbs (instead of modifying the core)
  218. ' -(Other Additions)
  219. '   - Updated B2BCollision.vbs with vpmBallCreate method and renamed new file to B2B.vbs (to maintain compatiblity with tables using old file).
  220. '
  221. ' New in 3.40 (Update by Koadic)
  222. ' - (Core Changes)
  223. '   - Modified NVOffset routine to allow use of alternative controllers (like dB2S B2S.Server)
  224. ' New in 3.39 (Update by Koadic)
  225. ' - (Core Changes)
  226. '   - Hopefully fixed bug introduced in 3.37 when using a VP version older than 9.0.10
  227. ' New in 3.38 (Update by Koadic)
  228. ' - (Core Changes)
  229. '   - Added automatic detection of ledcontrol.vbs and enabling for LedWiz use, allowing concurrent use by both users and non users of an LedWiz
  230. ' New in 3.37 (Update by Koadic)
  231. ' - (Core Changes)
  232. '   - Added ability to define default ballsize (in VP Units) inside table script.
  233. '       Defaults to 50 vp units if undefined. Example...
  234. '           Const BallSize = 47 '(place before LoadVPM, or otherwise calling core.vbs) 
  235. ' New in 3.36 (update courtesy of Koadic)
  236. ' - (Core Changes)
  237. '   - Added VPMVol routine for allowing setting of Global VPM Volume (normally adjustable from '~' key, but otherwise unsaveable without this)
  238. ' - (System VBS Alterations)
  239. '   - Added keyVPMVolume in VPMKeys.vbs, set to use the F12 key
  240. '   - Added call to VPMVol routine in each system's .vbs file, allowing end-user to access the new routine
  241. ' New in 3.35 (Update courtesy of Koadic)
  242. ' - (Core Changes)
  243. '   - Added NVOffset routine for allowing use of multiple nvram files per romset name
  244. ' New in 3.34 (Update by Destruk)
  245. ' - (System VBS Additions)
  246. '   - Added Play2.vbs
  247. ' New in 3.33 (Update by Destruk)
  248. ' - (System VBS Additions)
  249. '   - Added LTD.vbs
  250. ' New in 3.32 (Update by Destruk)
  251. ' - (System VBS Alterations)
  252. '   - Added Playmatic Replay setting switches
  253. ' New in 3.31 (Update by Destruk)
  254. ' - (System VBS Additions)
  255. '   - Added play1.vbs
  256. ' New in 3.30 (Update by Destruk)
  257. ' - (System VBS Additions)
  258. '   - Added zacproto.vbs
  259. ' New in 3.29 (Update by Noah)
  260. ' - (System VBS Additions)
  261. '   - Added jvh.vbs and ali.vbs by Destruk for Jac van Ham and Allied Leisure
  262. ' Corrected VPBuild Number for slingshots/bumpers and ball decals - Seeker
  263. ' New in 3.27 (Update by PD)
  264. ' - (System VBS Additions)
  265. '   - Added gts1.vbs by Inkochnito for Gottlieb System 1
  266. ' New in 3.26 (Update by PD)
  267. ' - (Core Changes)
  268. '    - Added "GICallback2" function to support Steve Ellenoff's new support in VPM for Dimming GI in WMS games
  269. '      GICallback returns numeric values 0-8 instead of a boolean 0 or 1 (on/off) like GICallback does.
  270. '      Existing tables will need to be altered to support dimming levels and need to use GICallback2 instead.
  271. '      The old GICallback is left intact so older tables are not broken by the new code
  272. '
  273. ' New in 3.25 (release 2) (Update by PD)
  274. ' - (Core Changes)
  275. '    - Restored former flipper speed due to complaints about some tables having BTTF problem returned and a resolution
  276. '      of arguments over the settings
  277. '    - New Optional Flipper Code Added (vpmSolFlip2) that let's you specify both up and down-swing speeds in the script
  278. '      plus the ability to turn flipper sounds on or off for that call
  279. '      Format: vpmSolFlip2 (Flip1obj, Flip2obj, UpSpeed, DownSpeed, SoundOn, Enable)
  280. '
  281. ' New in 3.24 (Update by PD)
  282. ' - (Core Changes)
  283. '    - Altered flipper code so the upswing defaults to your downswing (i.e. VBS no longer adds a different value)
  284. '      (This change was done due to arguments over issues now resolved)
  285. '    - I have decreased the return strength setting to be very low, though.  So any downswing hits (say from a ball
  286. '      heading to the trough) won't get hit with any real power.  So, assuming you have a reasonably fast upswing,
  287. '      you won't get any balls through the flipper and any balls hit by the underside won't get pegged anymore, which
  288. '      is a more realistic behavior.
  289. '
  290. ' New in 3.23 (Update by PD)
  291. ' - (System.vbs Additions)
  292. '    - SlamtTilt definitions added to AlvinG and Capcom systems
  293. '    - High Score Reset Switch Added to Williams System7 (S7.vbs)
  294. '    - Sleic.vbs system added (courtesy of Destruk)
  295. '    - Peper.vbs system added (courtesy of Destruk)
  296. '    - Juegos.vbs system added (courtesy of Destruk)
  297. '
  298. ' New in 3.22 (Update by PD)
  299. ' - (Core Changes)
  300. '   - Outhole switch handling updated so it resets correctly with an F3 reset.  
  301. '     This affects mostly Gottlieb System3 games (Thanks Racerxme for pointing this out)
  302. '   - Flipper handling modified to have a low return strength setting so any balls under such flippers
  303. '     won't get hit hard.  This allows the higher 'flipper fix' return speed without the associated hard hit issue.
  304. ' - (System.vbs Additions)
  305. '   -Inder.vbs test switches updated (Thanks Peter)
  306. '   -Bally.vbs swSoundDiag value changed to -6 (Thanks Racerxme)
  307. '
  308. ' New in 3.21 (Update by PD)
  309. ' -(Core Changes)
  310. '   - Attemped bug fix in the Impulse Plunger object that could cause weak plunges sometimes on full pulls
  311. '  
  312. ' -(System.vbs Additions)
  313. '   -Zac1.vbs has the program enable switch added to it (Thanks TomB)
  314. '   -GamePlan.vbs has the accounting reset switch added to it (Thanks Incochnito)
  315. '
  316. ' -(Other Additions)
  317. '   -PD Light System VBS file updated to V5.5 (adds fading reel pop bumper handler and checklight function)
  318. '
  319. ' New in 3.20 (Update by PD)
  320. ' -(System.vbs Additions)
  321. '   -Apparently Atari2.vbs uses 81/83 for the flipper switches and Atar1.vbs uses 82/84 so this repairs
  322. '    the Atari2.vbs file.
  323. '
  324. ' New in 3.19 (Update by PD)
  325. ' -(System.vbs Additions)
  326. '   - Fixed the swLLFlip and swLRFlip switch numbers in the Atari1.vbs, Atari2.vbs and Atari.vbs files
  327. '     SolFlipper should now work with Atari tables using the updated file
  328. '
  329. ' New in 3.18 (Update by PD)
  330. ' -(System.vbs Additions)
  331. '   - Added Atari1.vbs and Atari2.vbs files (Thanks to Inkochnito).  
  332. '     -The old Atari.vbs file is now obsolete, but included for backwards compatability with any existing tables
  333. '      that may have used it. New Tables should use the appropriate Atari1.vbs or Atari2.vbs files.
  334. '
  335. ' New in 3.17 (Update by PD)
  336. ' -(System.vbs Additions)
  337. '   -Fixed wrong switch definition in Sys80.vbs for the self-test switch.  The operator menus should work now.
  338. '    (Thanks to Inkochnito for pointing it out).
  339. '   -Added inder.vbs, nuova.vbs, spinball.vbs and mrgame.vbs files (Thanks to Destruk)
  340. '
  341. ' New in 3.16 (Update by PD)
  342. ' -(System.vbs Additions)
  343. '   -Added "BeginModal" and "EndModal" statements to each system (required for latest versions of VP ( >V6.1) to
  344. '    avoid problems during the VPM "F3" reset.
  345. ' -(Other Additions)
  346. '   - PDLightSystem Core updated to version 5.4
  347. '
  348. ' New in 3.15 (Update by PD)
  349. ' -(Core Additions)
  350. '   - Added a new higher resolution Impulse Plunger Object
  351. '      (It uses a trigger to plunge the ball.  It can be a variable Manual Plunger or function as an Automatic Plunger)
  352. '      (It also features random variance options and optional pull / plunge sounds)
  353. '
  354. ' -(System.vbs Additions)
  355. '   - Fixed wrong switch number for Tilt & Slam Tilt in Sega.vbs
  356. '   - Added Master CPU Enter switch to S7.vbs for Dip Switch control in Williams System7
  357. '
  358. ' -(Other Additions)
  359. '   - Added PDLightSystem.vbs (V5.3) file to archive
  360. '     (open it with a text editor to see how to use it; it's called separately like the core file)
  361. '
  362. ' New in 3.14 (Update by PD)
  363. ' -(System.vbs Additions)
  364. '   - Added latest Zac1.vbs and Zac2.vbs files to archive
  365. '
  366. ' New in 3.13 (Update by PD)
  367. ' -(Core Additions)
  368. '   - Added Destruk's code to "Add" or "Remove" a ball from the table when "B" is pressed.
  369. '   - Added "AutoplungeS" call which is the same as "Autoplunger" except it will play a specified sound when fired
  370. '
  371. ' -(System.vbs Additions)
  372. '   - Taito.vbs updated to fix service menu keys and default dip switch menu added
  373. '   - Dip Switch / Option Menu "class" code added to all table VBS scripts to ease menu coding for table authors
  374. '   - Fixed some labeling errors and organization and added a "Last Updated" version comment at the start of each file
  375. '
  376. ' New in 3.12
  377. '   - Made flipper return speed a constant conFlipRetSpeed
  378. '   - set conFlipRetSpeed to 0.137 to reduce ball thru flipper problem
  379. '
  380. ' New in 3.11
  381. '   - Added a short delay between balls in the ballstacks to ensure
  382. '     that the game registers the switches as off when balls are rolling
  383. '     in the trough. All balls should probably move at the same time but it is
  384. '     a bit tricky to implement without changing a lot of code.
  385. '   - Removed support for the wshltdlg.dll since funtionality is in VPM now
  386. ' New in 3.10
  387. '   - Public release
  388. ' Put this at the top of the table file
  389. 'LoadVPM "02000000", "xxx.VBS", 3.15
  390. 'Const cGameName    = "xxxx" ' PinMAME short game name
  391. 'Const UseSolenoids = True
  392. 'Const UseLamps     = True
  393. ''Standard sound
  394. 'Const SSolenoidOn  = "SolOn"       'Solenoid activates
  395. 'Const SSolenoidOff = "SolOff"      'Solenoid deactivates
  396. 'Const SFlipperOn   = "FlipperUp"   'Flipper activated
  397. 'Const SFlipperOff  = "FlipperDown" 'Flipper deactivated
  398. 'Const SCoin        = "Quarter"     'Coin inserted
  399. ''Callbacks
  400. 'Set LampCallback   = GetRef("UpdateMultipleLamps")
  401. 'Set GICallback     = GetRef("UpdateGI")  ' Original GI Callback (returns boolean on and off values only)
  402. 'Set GICallback2    = GetRef("UpdateGI")  ' New GI Callback supports Newer VPM Dimming GI and returns values numeric 0-8)
  403. 'Set MotorCallback  = GetRef("UpdateMotors")
  404. '
  405. 'Sub LoadVPM(VPMver, VBSfile, VBSver)
  406. '   On Error Resume Next
  407. '       If ScriptEngineMajorVersion < 5 Then MsgBox "VB Script Engine 5.0 or higher required"
  408. '       ExecuteGlobal GetTextFile(VBSfile)
  409. '       If Err Then MsgBox "Unable to open " & VBSfile & ". Ensure that it is in the same folder as this table. " & vbNewLine & Err.Description : Err.Clear
  410. '       Set Controller = CreateObject("VPinMAME.Controller")
  411. '       If Err Then MsgBox "Can't Load VPinMAME." & vbNewLine & Err.Description
  412. '       If VPMver>"" Then If Controller.Version < VPMver Or Err Then MsgBox "VPinMAME ver " & VPMver & " required." : Err.Clear
  413. '       If VPinMAMEDriverVer < VBSver Or Err Then MsgBox VBSFile & " ver " & VBSver & " or higher required."
  414. 'End Sub
  415. '
  416. 'Sub Table_KeyDown(ByVal keycode)
  417. '   If vpmKeyDown(keycode) Then Exit Sub
  418. '   If keycode = PlungerKey Then Plunger.Pullback
  419. 'End Sub
  420. 'Sub Table_KeyUp(ByVal keycode)
  421. '   If vpmKeyUp(keycode) Then Exit Sub
  422. '   If keycode = PlungerKey Then Plunger.Fire
  423. 'End Sub
  424. '
  425. 'Const cCredits  = ""
  426. 'Sub Table_Init
  427. '   vpmInit Me
  428. '   On Error Resume Next
  429. '       With Controller
  430. '           .GameName = cGameName
  431. '           If Err Then MsgBox "Can't start Game " & cGameName & vbNewLine & Err.Description : Exit Sub
  432. '           .SplashInfoLine = cCredits
  433. '           .HandleMechanics = 0
  434. '           .ShowDMDOnly = True : .ShowFrame = False : .ShowTitle = False
  435. '           .Run : If Err Then MsgBox Err.Description
  436. '       End With
  437. '   On Error Goto 0
  438. '' Nudging
  439. '   vpmNudge.TiltSwitch = swTilt
  440. '   vpmNudge.Sensitivity = 5
  441. '   vpmNudge.TiltObj = Array(Bumper1,Bumper2,LeftslingShot,RightslingShot)
  442. '' Map switches and lamps
  443. '   vpmCreateEvents colSwObjects ' collection of triggers etc
  444. '   vpmMapLights    colLamps     ' collection of all lamps
  445. '' Trough handler
  446. '   Set bsTrough = New cvpmBallStack
  447. '   bsTrough.InitNoTrough BallRelease, swOuthole, 90, 2
  448. '   'or
  449. '   bsTrough.InitSw swOuthole,swTrough1,swTrough2,0,0,0,0
  450. '---------------------------------------------------------------
  451. Dim Controller   ' VPinMAME Controller Object
  452. Dim vpmTimer     ' Timer Object
  453. Dim vpmNudge     ' Nudge handler Object
  454. Dim Lights(200)  ' Put all lamps in an array for easier handling
  455. ' If more than one lamp is connected, fill this with an array of each light
  456. Dim vpmMultiLights() : ReDim vpmMultiLights(0)
  457. Private gNextMechNo : gNextMechNo = 0 ' keep track of created mech handlers (would be nice with static members)
  458.  
  459. ' Callbacks
  460. Dim SolCallback(68) ' Solenoids (parsed at Runtime)
  461. Dim SolModCallback(68) ' Solenoid modulated callbacks (parsed at Runtime)
  462. Dim SolPrevState(68) ' When modulating solenoids are in use, needed to keep positive value levels from changing boolean state
  463. Dim LampCallback    ' Called after lamps are updated
  464. Dim GICallback      ' Called for each changed GI String
  465. Dim GICallback2     ' Called for each changed GI String
  466. Dim MotorCallback   ' Called after solenoids are updated
  467. Dim vpmCreateBall   ' Called whenever a vpm class needs to create a ball
  468. Dim BSize:If IsEmpty(Eval("BallSize"))=true Then BSize=25 Else BSize = BallSize/2
  469. Dim BMass:If IsEmpty(Eval("BallMass"))=true Then BMass=1 Else BMass = BallMass
  470. Dim UseDMD:If IsEmpty(Eval("UseVPMDMD"))=true Then UseDMD=false Else UseDMD = UseVPMDMD
  471. Dim UseModSol:If IsEmpty(Eval("UseVPMModSol"))=true Then UseModSol=false Else UseModSol = UseVPMModSol
  472. Dim UseColoredDMD:If IsEmpty(Eval("UseVPMColoredDMD"))=true Then UseColoredDMD=false Else UseColoredDMD = UseVPMColoredDMD
  473. Dim UseNVRAM:If IsEmpty(Eval("UseVPMNVRAM"))=true Then UseNVRAM=false Else UseNVRAM = UseVPMNVRAM
  474. Dim NVRAMCallback
  475.  
  476. ' Assign Null Default Sub so script won't error if only one is defined in a script (should redefine in your script)
  477. Set GICallback = GetRef("NullSub")
  478. Set GICallback2 = GetRef("NullSub")
  479.  
  480. ' Game specific info
  481. Dim ExtraKeyHelp    ' Help string for game specific keys
  482. Dim vpmShowDips     ' Show DIPs function
  483. '-----------------------------------------------------------------------------
  484. ' These helper functions require the following objects on the table:
  485. '   PinMAMETimer   : Timer object
  486. '   PulseTimer     : Timer object
  487.  
  488.  
  489.  
  490. '   Beta Class (move me)
  491. ' cvpmFlips (Object = vpmFlips) low latency flipper handling for games with pre-solid state flippers (fastflips)
  492. '   (Public) .Solenoid          - Integer, Flipper Solenoid number. vpmInit sets this to GameOnSolenoid from (whatever system .vbs)
  493. '   (Public) .TiltSol           - Boolean, control flipper enabled / tilts. This is handled automatically in the main loop if 'UseSolenoids' is used.
  494. '   (Public) .TiltObjects       - Boolean, Automatically calls vpmnudge.SolGameOn to disable slingshots and bumpers in vpmnudge.TiltObj array when tilted. (Default True)
  495. '   (Public) .Delay             - Integer input. Adds a buffer to the flipper solenoid response. Hack for some fliptronic / WPC-S WPCs that may have solenoid jitter. (Default 0)
  496. '   (Public) .CallbackL         - String input. Points to Left Flipper callback sub.  If UseSolenoids = 2, vpmInit automatically steals SolCallback(sLLFlipper).
  497. '   (Public) .CallbackR         - String input. Points to Right Flipper callback sub. If UseSolenoids = 2, vpmInit automatically steals SolCallback(sLRFlipper).
  498. '   (Public) .CallbackUL        - String input. Upper Left Flipper callback sub.     If UseSolenoids = 2, vpmInit automatically steals SolCallback(sULFlipper).
  499. '   (Public) .CallbackUR        - String input. Upper Right Flipper callback sub.    If UseSolenoids = 2, vpmInit automatically steals SolCallback(sURFlipper).
  500. '   -debug commands-
  501. '   (Public) .DebugOn           - Boolean, debug always-on flippers
  502. '   (Public) .DebugTest         - Reports any discovered errors in debug box
  503. '   (Public) .PrintSols         - Print any enabled solenoids in the debug box (Debug for finding tilt solenoids)
  504.  
  505. ' Available classes:
  506. ' ------------------
  507. ' cvpmTimer (Object = vpmTimer)
  508. '   (Public)  .PulseSwitch   - pulse switch and call callback after delay (default)
  509. '   (Public)  .PulseSw       - pulse switch
  510. '   (Public)  .AddTimer      - call callback after delay
  511. '   (Public)  .Reset         - Re-set all ballStacks
  512. '   (Friend)  .InitTimer     - initialise fast or slow timer
  513. '   (Friend)  .EnableUpdate  - Add/remove automatic update for an instance
  514. '   (Private) .Update        - called from slow timer
  515. '   (Private) .FastUpdate    - called from fast timer
  516. '   (Friend)  .AddResetObj   - Add object that needs to catch reset
  517. '
  518. ' cvpmTrough (Create as many as needed)
  519. '   (Public) .IsTrough         - Get or Set whether this trough is the default trough (first trough sets this by default)
  520. '   (Public) .Size             - Get or Set total number of balls trough can hold
  521. '   (Public) .EntrySw          - Set switch number for trough entry (if any) - eg. Outhole
  522. '   (Public) .AddSw            - Assign a switch at a specific slot
  523. '   (Public) .InitSwitches     - Set trough switches using an array, from exit slot back toward entrance.
  524. '   (Public) .InitExit         - Setup exit kicker, force and direction
  525. '   (Public) .InitExitVariance - Modify exit kick direction and force (+/-, min force = 1)
  526. '   (Public) .InitEntrySounds  - Sounds to play when a ball enters the trough
  527. '   (Public) .InitExitSounds   - Sounds to play when the exit kicker fires
  528. '   (Public) .CreateEvents     - Auto-generate hit events for VP entry kicker(s) associated with this trough
  529. '   (Public) .MaxBallsPerKick  - Set maximum number of balls to kick out (default 1)
  530. '   (Public) .MaxSlotsPerKick  - Set maximum slots from which to get balls when kicking out (default 1)
  531. '   (Public) .Balls            - Get current balls in trough, or set initial number of balls in trough
  532. '   (Public) .BallsPending     - Get number of balls waiting in trough entry
  533. '   (Public) .Reset            - Reset and update all trough switches
  534. '   (Friend) .Update           - Called from vpmTimer to update ball positions and switches
  535. '   (Public) .AddBall          - Add a ball to the trough from a kicker.  If kicker is the exit kicker, stacks ball at exit.
  536. '   (Public) .SolIn            - Solenoid handler for entry solenoid
  537. '   (Public) .SolOut           - Solenoid handler for exit solenoid
  538. '
  539. ' cvpmSaucer (Create as many as needed)
  540. '   (Public) .InitKicker       - Setup main kicker, switch, exit direction and force (including Z force)
  541. '   (Public) .InitExitVariance - Modify kick direction and force (+/-, min force = 1)
  542. '   (Public) .InitAltKick      - Set alternate direction and force (including Z force) - for saucers with two kickers
  543. '   (Public) .InitSounds       - Sounds to play when a ball enters the saucer or the kicker fires
  544. '   (Public) .CreateEvents     - Auto-generate hit event for VP kicker(s) associated with this saucer
  545. '   (Public) .AddBall          - Add a ball to the saucer from a kicker.
  546. '   (Public) .HasBall          - True if the saucer is occupied.
  547. '   (Public) .solOut           - Fire the primary exit kicker.  Ejects ball if one is present.
  548. '   (Public) .solOutAlt        - Fire the secondary exit kicker.  Ejects ball with alternate forces if present.
  549. '
  550. ' cvpmBallStack (DEPRECATED, but create as many as needed)
  551. '   (Public) .InitSw        - init switches used in stack
  552. '   (Public) .InitSaucer    - init saucer
  553. '   (Public) .InitNoTrough  - init a single ball, no trough handler
  554. '   (Public) .InitKick      - init exit kicker
  555. '   (Public) .InitAltKick   - init second kickout direction
  556. '   (Public) .CreateEvents  - Create addball events for kickers
  557. '   (Public) .KickZ         - Z axis kickout angle (radians)
  558. '   (Public) .KickBalls     - Maximum number of balls kicked out at the same time
  559. '   (Public) .KickForceVar  - Initial ExitKicker Force value varies by this much (+/-, minimum force = 1)
  560. '   (Public) .KickAngleVar  - ExitKicker Angle value varies by this much (+/-)
  561. '   (Public) .BallColour    - Set ball colour
  562. '   (Public) .TempBallImage  - Set ball image for next ball only
  563. '   (Public) .TempBallColour - Set ball colour for next ball only
  564. '   (Public) .BallImage     - Set ball image
  565. '   (Public) .InitAddSnd    - Sounds when ball enters stack
  566. '   (Public) .InitEntrySnd  - Sounds for Entry kicker
  567. '   (Public) .InitExitSnd   - Sounds for Exit kicker
  568. '   (Public) .AddBall       - add ball in "kicker" to stack
  569. '   (Public) .SolIn         - Solenoid handler for entry solenoid
  570. '   (Public) .EntrySol_On   - entry solenoid fired
  571. '   (Public) .SolOut        - Solenoid handler for exit solenoid
  572. '   (Public) .SolOutAlt     - Solenoid handler for exit solenoid 2nd direction
  573. '   (Public) .ExitSol_On    - exit solenoid fired
  574. '   (Public) .ExitAltSol_On - 2nd exit solenoid fired
  575. '   (Public) .Balls         - get/set number of balls in stack (default)
  576. '   (Public) .BallsPending  - get number of balls waiting to come in to stack
  577. '   (Public) .IsTrough      - Specify that this is the main ball trough
  578. '   (Public) .Reset         - reset and update all ballstack switches
  579. '   (Friend) .Update        - Update ball positions (from vpmTimer class)
  580. '  Obsolete
  581. '   (Public) .SolExit       - exit solenoid handler
  582. '   (Public) .SolEntry      - Entry solenoid handler
  583. '   (Public) .InitProxy     - Init proxy switch
  584.  
  585. ' cvpmNudge (Object = vpmNudge)
  586. '   Hopefully we can add a real pendulum simulator in the future
  587. '   (Public)  .TiltSwitch   - set tilt switch
  588. '   (Public)  .Senitivity   - Set tiltsensitivity (0-10)
  589. '   (Public)  .TiltObj      - Set objects affected by tilt
  590. '   (Public)  .DoNudge dir,power  - Nudge table
  591. '   (Public)  .SolGameOn    - Game On solenoid handler
  592. '   (Private) .Update       - Handle tilting
  593. '
  594. ' cvpmDropTarget (create as many as needed)
  595. '   (Public)  .InitDrop     - initialise DropTarget bank
  596. '   (Public)  .CreateEvents - Create Hit events
  597. '   (Public)  .InitSnd      - sound to use for targets
  598. '   (Public)  .AnyUpSw      - Set AnyUp switch
  599. '   (Public)  .AllDownSw    - Set all down switch
  600. '   (Public)  .AllDown      - All targets down?
  601. '   (Public)  .Hit          - A target had been hit
  602. '   (Public)  .SolHit       - Solenoid handler for dropping a target
  603. '   (Public)  .SolUnHit     - Solenoid handler for raising a target
  604. '   (Public)  .SolDropDown  - Solenoid handler for Bank down
  605. '   (Public)  .SolDropUp    - Solenoid handler for Bank reset
  606. '   (Public)  .DropSol_On   - Reset target bank
  607. '   (Friend)  .SetAllDn     - check alldown & anyup switches
  608. '
  609. ' cvpmMagnet (create as many as needed)
  610. '   (Public)  .InitMagnet   - initialise magnet
  611. '   (Public)  .CreateEvents - Create Hit/Unhit events
  612. '   (Public)  .Solenoid     - Set solenoid that controls magnet
  613. '   (Public)  .GrabCenter   - Magnet grabs ball at center
  614. '   (Public)  .MagnetOn     - Turn magnet on and off
  615. '   (Public)  .X            - Move magnet
  616. '   (Public)  .Y            - Move magnet
  617. '   (Public)  .Strength     - Change strength
  618. '   (Public)  .Size         - Change magnet reach
  619. '   (Public)  .AddBall      - A ball has come within range
  620. '   (Public)  .RemoveBall   - A ball is out of reach for the magnet
  621. '   (Public)  .Balls        - Balls currently within magnets reach
  622. '   (Public)  .AttractBall  - attract ball to magnet
  623. '   (Private) .Update       - update all balls (called from timer)
  624. '   (Private) .Reset        - handle emulation reset
  625. '  Obsolete
  626. '   (Public)  .Range        - Change magnet reach
  627.  
  628. ' cvpmTurnTable (create as many as needed)
  629. '   (Public)  .InitTurnTable - initialise turntable
  630. '   (Public)  .CreateEvents  - Create Hit/Unhit events
  631. '   (Public)  .MaxSpeed      - Maximum speed
  632. '   (Public)  .SpinUp        - Speedup acceleration
  633. '   (Public)  .SpinDown      - Retardation
  634. '   (Public)  .Speed         - Current speed
  635. '   (Public)  .MotorOn       - Motor On/Off
  636. '   (Public)  .SpinCW        - Control direction
  637. '   (Public)  .SolMotorState - Motor on/off solenoid handler
  638. '   (Public)  .AddBall       - A ball has come withing range
  639. '   (Public)  .RemoveBall    - A ball is out of reach for the magnet
  640. '   (Public)  .Balls         - Balls currently within magnets reach
  641. '   (Public)  .AffectBall    - affect a ball
  642. '   (Private) .Update        - update all balls (called from timer)
  643. '   (Private) .Reset         - handle emulation reset
  644.  
  645. ' cvpmMech (create as many as needed)
  646. '   (Public)  .Sol1, Sol2    - Controlling solenoids
  647. '   (Public)  .MType         - type of mechanics
  648. '   (Public)  .Length, Steps
  649. '   (Public)  .Acc, Ret      - Acceleration, retardation
  650. '   (Public)  .AddSw         - Automatically controlled switches
  651. '   (Public)  .AddPulseSw    - Automatically pulsed switches
  652. '   (Public)  .Callback      - Update graphics function
  653. '   (Public)  .Start         - Start mechanics handler
  654. '   (Public)  .Position      - Current position
  655. '   (Public)  .Speed         - Current Speed
  656. '   (Private) .Update
  657. '   (Private) .Reset
  658. '
  659. ' cvpmCaptiveBall (create as many as needed)
  660. '   (Public)  .InitCaptive   - Initialise captive balls
  661. '   (Public)  .CreateEvents  - Create events for captive ball
  662. '   (Public)  .ForceTrans    - Amount of force tranferred to captive ball (0-1)
  663. '   (Public)  .MinForce      - Minimum force applied to the ball
  664. '   (Public)  .NailedBalls   - Number of "nailed" balls infront of captive ball
  665. '   (Public)  .RestSwitch    - Switch activated when ball is in rest position
  666. '   (Public)  .Start         - Create moving ball etc.
  667. '   (Public)  .TrigHit       - trigger in front of ball hit (or unhit)
  668. '   (Public)  .BallHit       - Wall in front of ball hit
  669. '   (Public)  .BallReturn    - Captive ball has returned to kicker
  670. '   (Private) .Reset
  671. '
  672. ' cvpmVLock (create as many as needed)
  673. '   (Public)  .InitVLock     - Initialise the visible ball stack
  674. '   (Public)  .ExitDir       - Balls exit angle (like kickers)
  675. '   (Public)  .ExitForce     - Force of balls kicked out
  676. '   (Public)  .KickForceVar  - Vary kickout force
  677. '   (Public)  .InitSnd       - Sounds to make on kickout
  678. '   (Public)  .Balls         - Number of balls in Lock
  679. '   (Public)  .SolExit       - Solenoid event
  680. '   (Public)  .CreateEvents  - Create events needed
  681. '   (Public)  .TrigHit       - called from trigger hit event
  682. '   (Public)  .TrigUnhit     - called from trigger unhit event
  683. '   (Public)  .KickHit       - called from kicier hit event
  684. '
  685. ' cvpmDips (create as many as needed) => (Dip Switch And/Or Table Options Menu)
  686. '   (Public)  .AddForm       - create a form (AKA dialogue)
  687. '   (Public)  .AddChk        - add a chckbox
  688. '   (Public)  .AddChkExtra   -   -  "" -     for non-dip settings
  689. '   (Public)  .AddFrame      - add a frame with checkboxes or option buttons
  690. '   (Public)  .AddFrameExtra -  - "" - for non-dip settings
  691. '   (Public)  .AddLabel      - add a label (text string)
  692. '   (Public)  .ViewDips      - Show form
  693. '   (Public)  .ViewDipsExtra -  - "" -  with non-dip settings
  694. '
  695. ' cvpmImpulseP (create as many as needed) => (Impulse Plunger Object using a Trigger to Plunge Manual/Auto)
  696. '   (Public)  .InitImpulseP - Initialise Impulse Plunger Object (Trigger, Plunger Power, Time to Full Plunge [0 = Auto])
  697. '   (Public)  .CreateEvents - Create Hit/Unhit events
  698. '   (Public)  .Strength     - Change plunger strength
  699. '   (Public)  .Time         - Change plunger time (in seconds) to full plunger strength (0 = Auto Plunger)
  700. '   (Public)  .Pullback     - Pull the plunger back
  701. '   (Public)  .Fire     - Fires / Releases the Plunger (Manual or Auto depending on Timing Value given)
  702. '   (Public)  .AutoFire     - Fires / Releases the Plunger at Maximum Strength +/- Random variation (i.e. Instant Auto)
  703. '   (Public)  .Switch       - Switch Number to activate when ball is sitting on plunger trigger (if any)
  704. '   (Public)  .Random       - Sets the multiplier level of random variance to add (0 = No Variance / Default)
  705. '   (Public)  .InitEntrySnd - Plays Sound as Plunger is Pulled Back
  706. '   (Public)  .InitExitSnd  - Plays Sound as Plunger is Fired (WithBall,WithoutBall)
  707. '
  708. ' Generic solenoid handlers:
  709. ' --------------------------
  710. ' vpmSolFlipper flipObj1, flipObj2          - "flips flippers". Set unused to Nothing
  711. ' vpmSolFlip2   flipObj1, flipObj2, flipSpeedUp, flipSpeedDn, sndOn).  Set unused to Nothing
  712. ' vpmSolDiverter divObj, sound              - open/close diverter (flipper) with/without sound
  713. ' vpmSolWall wallObj, sound                 - Raise/Drop wall with/without sound
  714. ' vpmSolToggleWall wall1, wall2, sound      - Toggle between two walls
  715. ' vpmSolToggleObj obj1,obj2,sound           - Toggle any objects
  716. ' vpmSolAutoPlunger plungerObj, var, enabled    - Autoplunger/kickback
  717. ' vpmSolAutoPlungeS plungerObj, sound, var, enabled - Autoplunger/kickback With Specified Sound To Play
  718. ' vpmSolGate obj, sound                     - Open/close gate
  719. ' vpmSolSound sound                         - Play sound only
  720. ' vpmFlasher flashObj                       - Flashes flasher
  721. '
  722. ' Generating events:
  723. ' ------------------
  724. ' vpmCreateEvents
  725. ' cpmCreateLights
  726. '
  727. ' Variables declared (to be filled in):
  728. ' ---------------------------------------
  729. ' SolCallback()  - handler for each solenoid
  730. ' Lights()       - Lamps
  731. '
  732. ' Constants used (must be defined):
  733. ' ---------------------------------
  734. ' UseSolenoids   - Update solenoids
  735. ' MotorCallback  - Called once every update for mechanics or custom sol handler
  736. ' UseLamps       - Update lamps
  737. ' LampCallback   - Sub to call after lamps are updated
  738. '                  (or every update if UseLamps is false)
  739. ' GICallback     - Sub to call to update GI strings
  740. ' GICallback2    - Sub to call to update GI strings
  741. ' SFlipperOn     - Flipper activate sound
  742. ' SFlipperOff    - Flipper deactivate sound
  743. ' SSolenoidOn    - Solenoid activate sound
  744. ' SSolenoidOff   - Solenoid deactivate sound
  745. ' SCoin          - Coin Sound
  746. ' ExtraKeyHelp   - Game specific keys in help window
  747. '
  748. ' Exported variables:
  749. ' -------------------
  750. ' vpmTimer      - Timer class for PulseSwitch etc
  751. ' vpmNudge      - Class for table nudge handling
  752. '-----------------------------------------------------
  753. Private Function PinMAMEInterval
  754.         If VPBuildVersion >= 10200 Then
  755.                 PinMAMEInterval = -1 ' VP10.2 introduced special frame-sync'ed timers
  756.        Else
  757.             If VPBuildVersion >= 10000 Then
  758.                 PinMAMEInterval = 3  ' as old VP9 timers pretended to run at 1000Hz but actually did only a max of 100Hz (e.g. corresponding nowadays to interval=10), we do something inbetween for VP10+ by default
  759.            Else
  760.                 PinMAMEInterval = 1
  761.             End If
  762.         End If
  763. End Function
  764.  
  765. Private Const conStackSw    = 8  ' Stack switches
  766. Private Const conMaxBalls   = 13 ' Because of Apollo 13
  767. Private Const conMaxTimers  = 20 ' Spinners can generate a lot of timers
  768. Private Const conTimerPulse = 40 ' Timer runs at 25Hz
  769. Private Const conFastTicks  = 4  ' Fast is 4 times per timer pulse
  770. Private Const conMaxSwHit   = 5  ' Don't stack up more than 5 events for each switch
  771.  
  772. ' DEPRECATED Flipper constants:
  773. Private Const conFlipRetStrength = 0.01  ' Flipper return strength
  774. Private Const conFlipRetSpeed    = 0.137 ' Flipper return speed
  775.  
  776. Function CheckScript(file) 'Checks Tables and Scripts directories for specified vbs file, and if it exitst, will load it.
  777.     CheckScript = False
  778.   On Error Resume Next
  779.     Dim TablesDirectory:TablesDirectory = Left(UserDirectory,InStrRev(UserDirectory,"\",InStrRev(UserDirectory,"\")-1))&"Tables\"
  780.     Dim ScriptsDirectory:ScriptsDirectory = Left(UserDirectory,InStrRev(UserDirectory,"\",InStrRev(UserDirectory,"\")-1))&"Scripts\"
  781.     dim check:Set check = CreateObject("Scripting.FileSystemObject")
  782.     If check.FileExists(tablesdirectory & file) Or check.FileExists(scriptsdirectory & file) Or check.FileExists(file) Then CheckScript = True
  783.   On Error Goto 0
  784. End Function
  785.  
  786. Function LoadScript(file) 'Checks Tables and Scripts directories for specified vbs file, and if it exitst, will load it.
  787.     LoadScript = False
  788.   On Error Resume Next
  789.     If CheckScript(file) Then ExecuteGlobal GetTextFile(file):LoadScript = True
  790.   On Error Goto 0
  791. End Function
  792.  
  793. ' Dictionary
  794. ' At one point, Microsoft had made Scripting.Dictionary "unsafe for scripting", but it's
  795. ' been a long time since that was true.  So now, to maintain compatibility with all tables
  796. ' and scripts that use cvpmDictionary, this class is now a simple wrapper around Microsoft's
  797. ' more efficient implementation.
  798. Class cvpmDictionary
  799.     Private mDict
  800.     Private Sub Class_Initialize : Set mDict = CreateObject("Scripting.Dictionary") : End Sub
  801.  
  802.     ' DEPRECATED: MS Dictionaries are not index-based.  Use "Exists" method instead.
  803.     Private Function FindKey(aKey)
  804.         Dim ii, key : FindKey = -1
  805.         If mDict.Count > 0 Then
  806.             ii = 0
  807.             For Each key In mDict.Keys
  808.                 If key = aKey Then FindKey = ii : Exit Function
  809.             Next
  810.         End If
  811.     End Function
  812.  
  813.     Public Property Get Count : Count = mDict.Count : End Property
  814.  
  815.     Public Property Get Item(aKey)
  816.         Item = Empty
  817.         If mDict.Exists(aKey) Then
  818.             If IsObject(mDict(aKey)) Then
  819.                 Set Item = mDict(aKey)
  820.             Else
  821.                 Item = mDict(aKey)
  822.             End If
  823.         End If
  824.     End Property
  825.  
  826.     Public Property Let Item(aKey, aData)
  827.         If IsObject(aData) Then
  828.             Set mDict(aKey) = aData
  829.         Else
  830.             mDict(aKey) = aData
  831.         End If
  832.     End Property
  833.  
  834.     Public Property Set Key(aKey)
  835.         ' This function is (and always has been) a no-op.  Previous definition
  836.         ' just looked up aKey in the keys list, and if found, set the key to itself.
  837.     End Property
  838.  
  839.     Public Sub Add(aKey, aItem)
  840.         If IsObject(aItem) Then
  841.             Set mDict(aKey) = aItem
  842.         Else
  843.             mDict(aKey) = aItem
  844.         End If
  845.     End Sub
  846.  
  847.     Public Sub Remove(aKey)      : mDict.Remove(aKey)          : End Sub
  848.     Public Sub      RemoveAll    : mDict.RemoveAll             : End Sub
  849.     Public Function Exists(aKey) : Exists = mDict.Exists(aKey) : End Function
  850.     Public Function Items        : Items  = mDict.Items        : End Function
  851.     Public Function Keys         : Keys   = mDict.Keys         : End Function
  852. End Class
  853.  
  854. '--------------------
  855. '       Timer
  856. '--------------------
  857. Class cvpmTimer
  858.     Private mQue, mNow, mTimers
  859.     Private mSlowUpdates, mFastUpdates, mResets, mFastTimer
  860.  
  861.     Private Sub Class_Initialize
  862.         ReDim mQue(conMaxTimers) : mNow = 0 : mTimers = 0
  863.         Set mSlowUpdates = New cvpmDictionary
  864.         Set mFastUpdates = New cvpmDictionary
  865.         Set mResets      = New cvpmDictionary
  866.     End Sub
  867.  
  868.     Public Sub InitTimer(aTimerObj, aFast)
  869.         If aFast Then
  870.             Set mFastTimer = aTimerObj
  871.             aTimerObj.TimerInterval = conTimerPulse \ conFastTicks
  872.             aTimerObj.TimerEnabled = False
  873.             vpmBuildEvent aTimerObj, "Timer", "vpmTimer.FastUpdate"
  874.         Else
  875.             aTimerObj.Interval = conTimerPulse : aTimerObj.Enabled = True
  876.             vpmBuildEvent aTimerObj, "Timer", "vpmTimer.Update"
  877.         End If
  878.     End Sub
  879.  
  880.     Sub EnableUpdate(aClass, aFast, aEnabled)
  881.         On Error Resume Next
  882.         If aFast Then
  883.             If aEnabled Then mFastUpdates.Add aClass, 0 : Else mFastUpdates.Remove aClass
  884.             mFastTimer.TimerEnabled = mFastUpdates.Count > 0
  885.         Else
  886.             If aEnabled Then mSlowUpdates.Add aClass, 0 : Else mSlowUpdates.Remove aClass
  887.         End If
  888.     End Sub
  889.  
  890.     Public Sub Reset
  891.         Dim obj : For Each obj In mResets.Keys : obj.Reset : Next
  892.     End Sub
  893.  
  894.     Public Sub FastUpdate
  895.         Dim obj : For Each obj In mFastUpdates.Keys : obj.Update : Next
  896.     End Sub
  897.  
  898.     Public Sub Update
  899.         Dim ii, jj, sw, obj, mQuecopy
  900.  
  901.         For Each obj In mSlowUpdates.Keys : obj.Update : Next
  902.         If mTimers = 0 Then Exit Sub
  903.         mNow = mNow + 1 : ii = 1
  904.  
  905.         Do While ii <= mTimers
  906.             If mQue(ii)(0) <= mNow Then
  907.                 If mQue(ii)(1) = 0 Then
  908.                     If isObject(mQue(ii)(3)) Then
  909.                         Call mQue(ii)(3)(mQue(ii)(2))
  910.                     ElseIf varType(mQue(ii)(3)) = vbString Then
  911.                         If mQue(ii)(3) > "" Then Execute mQue(ii)(3) & " " & mQue(ii)(2) & " "
  912.                     End If
  913.                     mTimers = mTimers - 1
  914.                     For jj = ii To mTimers : mQue(jj) = mQue(jj+1) : Next : ii = ii - 1
  915.                 ElseIf mQue(ii)(1) = 1 Then
  916.                     mQuecopy = mQue(ii)(2)
  917.                     Controller.Switch(mQuecopy) = False
  918.                     mQue(ii)(0) = mNow + mQue(ii)(4) : mQue(ii)(1) = 0
  919.                 Else '2
  920.                     mQuecopy = mQue(ii)(2)
  921.                     Controller.Switch(mQuecopy) = True
  922.                     mQue(ii)(1) = 1
  923.                 End If
  924.             End If
  925.             ii = ii + 1
  926.         Loop
  927.     End Sub
  928.  
  929.     Public Sub AddResetObj(aObj)  : mResets.Add aObj, 0 : End Sub
  930.  
  931.     Public Sub PulseSw(aSwNo) : PulseSwitch aSwNo, 0, 0 : End Sub
  932.  
  933.     Public Default Sub PulseSwitch(aSwNo, aDelay, aCallback)
  934.         Dim ii, count, last
  935.         count = 0
  936.         For ii = 1 To mTimers
  937.             If mQue(ii)(1) > 0 And mQue(ii)(2) = aSwNo Then count = count + 1 : last = ii
  938.         Next
  939.         If count >= conMaxSwHit Or mTimers = conMaxTimers Then Exit Sub
  940.         mTimers = mTimers + 1 : mQue(mTimers) = Array(mNow, 2, aSwNo, aCallback, aDelay\conTimerPulse)
  941.         If count Then mQue(mTimers)(0) = mQue(last)(0) + mQue(last)(1)
  942.     End Sub
  943.  
  944.     Public Sub AddTimer(aDelay, aCallback)
  945.         If mTimers = conMaxTimers Then Exit Sub
  946.         mTimers = mTimers + 1
  947.         mQue(mTimers) = Array(mNow + aDelay \ conTimerPulse, 0, 0, aCallback)
  948.     End Sub
  949.    
  950.     Public Sub AddTimer2(aDelay, aCallback, aID)
  951.         If mTimers = conMaxTimers Then Exit Sub
  952.         mTimers = mTimers + 1
  953.         mQue(mTimers) = Array(mNow + aDelay \ conTimerPulse, 0, aID, aCallback)
  954.     End Sub
  955. End Class
  956.  
  957. '--------------------
  958. '     Trough
  959. '--------------------
  960. Class cvpmTrough
  961.     ' Takes over for older cvpmBallStack in "trough mode".  Theory of operation:
  962.    ' A trough can hold up to N balls, and has N*2 "slots".  A ball effectively takes
  963.    ' up two slots, so no two adjacent slots (0 and 1) can be occupied at the same time.
  964.    ' Switches are assigned to even slots only, which means that as balls move through
  965.    ' the trough, each switch is allowed to flip between open and closed.
  966.    ' Slot 0 is the exit, and can have additional balls "stacked" on it, simulating balls
  967.    ' falling onto the exit kicker instead of coming in from the entrance.  Extra balls
  968.    ' can be queued up at the entrance, and will enter the trough only if there's room
  969.    ' for them.
  970.  
  971.     Private mSlot(), mSw(), mEntrySw
  972.     Private mBallsInEntry, mMaxBallsPerKick, mStackExitBalls
  973.     Private mExitKicker, mExitDir, mExitForce, mDirVar, mForceVar
  974.     Private mSounds
  975.  
  976.     ' If you want to see what the trough is doing internally, add a TextBox to your table
  977.    ' named "DebugBox" (recommend Courier New or FixedSys at a small font size) and set
  978.    ' this variable to true via .isDebug = True.
  979.     Private mDebug
  980.  
  981.     Private Sub Class_Initialize
  982.         Dim ii
  983.  
  984.         ReDim mSw(conMaxBalls), mSlot(conMaxBalls * 2)
  985.         For ii = 0 to UBound(mSlot) : mSlot(ii) = 0 : Next   ' All slots empty to start
  986.        For ii = 0 to UBound(mSw)   : mSw(ii) = 0   : Next   ' All switches unassigned to start.
  987.        mEntrySw = 0
  988.  
  989.         Set mExitKicker = Nothing
  990.         mExitDir = 0 : mExitForce = 1 : mDirVar = 0 : mForceVar = 0
  991.         mBallsInEntry = 0 : mMaxBallsPerKick = 1 : mStackExitBalls = 1
  992.  
  993.         Set mSounds = New cvpmDictionary
  994.  
  995.         mDebug = False
  996.  
  997.         If Not IsObject(vpmTrough) Then Set vpmTrough = Me
  998.     End Sub
  999.  
  1000.     Public Property Let IsTrough(aYes)
  1001.         If aYes Then
  1002.             Set vpmTrough = Me
  1003.         ElseIf Me Is vpmTrough Then
  1004.             Set vpmTrough = Nothing
  1005.         End If
  1006.     End Property
  1007.  
  1008.     Public Property Get IsTrough
  1009.         IsTrough = (Me Is vpmTrough)
  1010.     End Property
  1011.  
  1012.     ' Initialization
  1013.  
  1014.     Public Property Let isDebug(enabled) : mDebug = enabled : End Property
  1015.  
  1016.     Public Property Let Size(aSize)
  1017.         Dim oldSize, newSize, ii
  1018.         oldSize = UBound(mSw)
  1019.         newSize = vpMax(1, aSize)
  1020.  
  1021.         ReDim Preserve mSlot(newSize * 2)
  1022.         ReDim Preserve mSw(newSize)
  1023.         For ii = oldSize+1 To newSize : mSw(ii) = 0 : Next
  1024.         For ii = (oldSize*2) + 1 to (newSize*2) : mSlot(ii) = 0 : Next
  1025.     End Property
  1026.     Public Property Get Size : Size = UBound(mSw) : End Property
  1027.  
  1028.     ' Set EntrySw = 0 if you want balls to just fall into the trough automatically.
  1029.    ' Set it to a real switch number to indicate that a ball is occupying an entry kicker.
  1030.    ' The ROM in the controller is then responsible for kicking the ball into the trough.
  1031.    Public Property Let EntrySw(swNo) : mEntrySw = swNo : End Property
  1032.  
  1033.     ' Assign switches, starting from slot 0 and going to entrance.
  1034.    ' This sub allows you to pass in as many switches as you wish.
  1035.    Public Sub InitSwitches(switchArray)
  1036.         If Not IsArray(switchArray) Then
  1037.             Err.Raise 17, "cvpmTrough.InitSwitches: Input must be an array."
  1038.         End If
  1039.  
  1040.         Dim ii
  1041.         For ii = 0 to UBound(mSw)
  1042.             If ii > UBound(switchArray) Then
  1043.                 mSw(ii) = 0
  1044.             Else
  1045.                 mSw(ii) = switchArray(ii)
  1046.             End If
  1047.         Next
  1048.     End Sub
  1049.  
  1050.     ' Alternative: Assign a switch to a specific slot.
  1051.    Public Sub AddSw(slotNo, swNo)
  1052.         If slotNo < 0 OR slotNo > UBound(mSw) Then Exit Sub
  1053.         mSw(slotNo) = swNo
  1054.     End Sub
  1055.  
  1056.     ' MaxBallsPerKick: Kick up to N balls total per exit kick.  Balls are only kicked from Slot 0.
  1057.    ' StackExitBalls: Automatically stack up to N balls in Slot 0 regardless of where they came from.
  1058.  
  1059.     ' Example: Subway where exit kicker is on the same level as the trough and a ball can
  1060.    ' come in from the exit: StackExitBalls = 1, MaxBallsPerKick = 2.  If Slot 0 has 1
  1061.    ' ball and Slot 1 is occupied, only one ball will be kicked.  If Slot 0 has 2 or more
  1062.    ' balls, it'll kick out 2 balls.
  1063.  
  1064.     ' Example: Twilight Zone Slot Kicker: Kicker is below trough, so if a ball is in the
  1065.    ' exit chute, another ball can fall into the chute as well whether it came in from the
  1066.    ' exit (Slot Machine) or any other entrance (Piano, Camera).  In both cases, the kicker
  1067.    ' will eject 2 balls at once.  Set StackExitBalls = 2, maxBallsPerKick = 2 to simulate.
  1068.  
  1069.     Public Property Let MaxBallsPerKick(n) : mMaxBallsPerKick = vpMax(1, n) : End Property
  1070.     Public Property Let StackExitBalls(n) : mStackExitBalls = vpMax(1, n) : End Property
  1071.  
  1072.     Public Sub InitExit(aKicker, aDir, aForce)
  1073.         If TypeName(aKicker) <> "Kicker" Then
  1074.             Err.Raise 17, "cvpmTrough.InitExit: Cannot use object of type '" & TypeName(aKicker) & "'."
  1075.         End If
  1076.  
  1077.         Set mExitKicker = aKicker
  1078.         mExitDir = aDir
  1079.         mExitForce = vpMax(1, aForce)
  1080.     End Sub
  1081.  
  1082.     Public Sub InitExitVariance(aDirVar, aForceVar)
  1083.         mDirVar = aDirVar
  1084.         mForceVar = aForceVar
  1085.     End Sub
  1086.  
  1087.     ' Setup sounds
  1088.    Public Sub InitEntrySounds(addSound, entrySoundEmpty, entrySoundBall)
  1089.         mSounds.Item("add") = addSound
  1090.         mSounds.Item("entry") = entrySoundEmpty
  1091.         mSounds.Item("entryBall") = entrySoundBall
  1092.     End Sub
  1093.  
  1094.     Public Sub InitExitSounds(exitSoundEmpty, exitSoundBall)
  1095.         mSounds.Item("exit") = exitSoundEmpty
  1096.         mSounds.Item("exitBall") = exitSoundBall
  1097.     End Sub
  1098.  
  1099.     ' Start trough with this many balls
  1100.    Public Property Let Balls(numBalls)
  1101.         Dim ii, ballsAdded
  1102.  
  1103.         ' First clear all slots.
  1104.        For ii = 0 to UBound(mSlot) : mSlot(ii) = 0 : Next
  1105.  
  1106.         ' Now put a ball in each even-numbered slot up to the number requested.
  1107.        ' First, stack exit slot.  (Note, we may get a negative number. vpMin/vpMax prevent that.)
  1108.        mSlot(0) = vpMax(0, vpMin(mStackExitBalls, numBalls))
  1109.         ballsAdded = mSlot(0)
  1110.  
  1111.         ' Fill remaining slots.
  1112.        For ii = 1 to vpMin(numBalls - mSlot(0), UBound(mSw))
  1113.             mSlot(ii*2) = 1
  1114.             ballsAdded = ballsAdded + 1
  1115.         Next
  1116.  
  1117.         ' If we asked to put more balls in the trough than it can handle, queue up the rest.
  1118.        mBallsInEntry = vpMax(0, numBalls-ballsAdded)
  1119.  
  1120.         UpdateTroughSwitches
  1121.     End Property
  1122.  
  1123.     Public Property Get Balls
  1124.         Balls = 0
  1125.         Dim ii : For ii = 0 to UBound(mSlot) : Balls = Balls + mSlot(ii) : Next
  1126.     End Property
  1127.  
  1128.     Public Property Get BallsPending : BallsPending = mBallsInEntry : End Property
  1129.  
  1130.     ' Auto-generate events for any entry kickers (eg. outhole, TZ Camera and Piano, etc.)
  1131.    ' Accepts a single kicker, an Array, or a Collection.
  1132.     Public Sub CreateEvents(aName, aKicker)
  1133.         Dim obj, tmp
  1134.         If Not vpmCheckEvent(aName, Me) Then Exit Sub
  1135.         vpmSetArray tmp, aKicker
  1136.         For Each obj In tmp
  1137.             If isObject(obj) Then
  1138.                 vpmBuildEvent obj, "Hit", aName & ".AddBall Me"
  1139.             Else
  1140.                 vpmBuildEvent mKicker, "Hit", aName & ".AddBall Me"
  1141.             End If
  1142.         Next
  1143.     End Sub
  1144.  
  1145.     ' VPM Update management
  1146.  
  1147.     Private Property Let NeedUpdate(aEnabled) : vpmTimer.EnableUpdate Me, False, aEnabled : End Property
  1148.  
  1149.     Public Sub Reset
  1150.         Dim mEntrySwcopy
  1151.         UpdateTroughSwitches
  1152.         If mEntrySw Then
  1153.             mEntrySwcopy = mEntrySw
  1154.             Controller.Switch(mEntrySwcopy) = (mBallsInEntry > 0)
  1155.         End If
  1156.     End Sub
  1157.  
  1158.     Public Sub Update
  1159.         NeedUpdate = AdvanceBalls
  1160.         UpdateTroughSwitches
  1161.     End Sub
  1162.  
  1163.     ' Switch and slot management
  1164.  
  1165.     Private Sub setSw(slotNo, enabled)
  1166.         Dim mSwcopy
  1167.         If mSw(slotNo) Then
  1168.             mSwcopy = mSw(slotNo)
  1169.             Controller.Switch(mSwcopy) = enabled
  1170.         End If
  1171.     End Sub
  1172.  
  1173.     Private Sub UpdateTroughSwitches
  1174.         Dim ii, mSwcopy
  1175.         For ii = 0 to UBound(mSw)
  1176.             If mSw(ii) Then
  1177.                 mSwcopy = mSw(ii)
  1178.                 Controller.Switch(mSwcopy) = (mSlot(ii*2) > 0)
  1179.             End If
  1180.         Next
  1181.         If mDebug Then UpdateDebugBox
  1182.     End Sub
  1183.  
  1184.     Private Sub UpdateDebugBox   ' Requires a textbox named DebugBox
  1185.         Dim str, ii, mSwcopy
  1186.         str = "Entry: " & mBallsInEntry & " (sw" & mEntrySw & " = "
  1187.         If mEntrySw > 0 Then
  1188.             mSwcopy = mEntrySw
  1189.             str = str & Controller.Switch(mSwcopy)
  1190.         Else
  1191.             str = str & "n/a"
  1192.         End If
  1193.         str = str & ")" & vbNewLine
  1194.  
  1195.         str = str & "["
  1196.         For ii = UBound(mSlot) To 0 Step -1 : str = str & mSlot(ii) : Next
  1197.         str = str & "]" & vbNewLine
  1198.  
  1199.         str = str & "["
  1200.         For ii = UBound(mSlot) To 0 Step -1
  1201.             If ii Mod 2 = 0 Then
  1202.                 If mSw(ii\2) Then
  1203.                     mSwcopy = mSw(ii\2)
  1204.                     If Controller.Switch(mSwcopy) Then
  1205.                         str = str & "1"
  1206.                     Else
  1207.                         str = str & "0"
  1208.                     End If
  1209.                 Else
  1210.                     str = str & "-"
  1211.                 End If
  1212.             Else
  1213.                 str = str & " "
  1214.             End If
  1215.         Next
  1216.         str = str & "]"
  1217.  
  1218.         DebugBox.Text = str
  1219.     End Sub
  1220.  
  1221.     Private Function AdvanceBalls
  1222.         Dim ii, canMove, maxSlot
  1223.         maxSlot = UBound(mSlot)
  1224.         AdvanceBalls = False
  1225.  
  1226.         ' Move balls through slots, one slot at a time.
  1227.        For ii = 0 to maxSlot
  1228.             If mSlot(ii) Then               ' Ball in this slot.
  1229.                 canMove = False
  1230.  
  1231.                 ' Can this ball move?  (Slot 0 = no)
  1232.                If ii = 0 Then
  1233.                     ' Slot 0 never moves (except when ejected)
  1234.                    canMove = False
  1235.                 ElseIf ii = 1 Then
  1236.                     ' Slot 1 automatically moves to Slot 0
  1237.                     canMove = True
  1238.                 ElseIf ii = 2 Then
  1239.                     ' Slot 2 moves if the number of balls in slot 0 is less than the stack target.
  1240.                     canMove = (mSlot(0) < mStackExitBalls)
  1241.                 Else
  1242.                     ' Only move if there is no ball in ii-1 or ii-2.
  1243.                     canMove = (mSlot(ii-2) = 0) AND (mSlot(ii-1) = 0)
  1244.                 End If
  1245.  
  1246.                 If canMove Then
  1247.                     mSlot(ii) = mSlot(ii) - 1
  1248.                     mSlot(ii-1) = mSlot(ii-1) + 1
  1249.                     AdvanceBalls = True       ' Mark balls as having moved.
  1250.                End If
  1251.             End If
  1252.         Next
  1253.  
  1254.         ' If balls are supposed to fall into the trough without going through a kicker,
  1255.        ' see if any balls are pending and try to add one automatically if so.
  1256.        If mBallsInEntry > 0 AND mEntrySw <= 0 Then
  1257.             AdvanceBalls = AddBallAtEntrance OR AdvanceBalls
  1258.         End If
  1259.     End Function
  1260.  
  1261.     ' Ball management
  1262.  
  1263.     Private Function AddBallAtEntrance
  1264.         Dim mSwcopy
  1265.         Dim maxSlot : maxSlot = UBound(mSlot)
  1266.         AddBallAtEntrance = False
  1267.  
  1268.         ' Only add a ball if there's room for it at the entrance.
  1269.        ' If the trough is full (or the entrance is occupied), the ball will remain
  1270.        ' in the entry queue.  In a kicker-gated trough, the entry switch will remain
  1271.        ' pressed down, usually resulting in the machine retrying the load.  In a fall-in
  1272.        ' trough, the ball will just remain queued until the entrance opens up.
  1273.        If mSlot(maxSlot) = 0 AND mSlot(maxSlot-1) = 0 Then
  1274.             mSlot(maxSlot) = 1
  1275.             mBallsInEntry = vpMax(0, mBallsInEntry - 1)
  1276.             If mBallsInEntry = 0 AND mEntrySw Then
  1277.                 mSwcopy = mEntrySw
  1278.                 Controller.Switch(mSwcopy) = False
  1279.             End If
  1280.             AddBallAtEntrance = True
  1281.         End If
  1282.     End Function
  1283.  
  1284.     Public Sub AddBall(aKicker)
  1285.         Dim mSwcopy
  1286.         Dim addDone : addDone = False
  1287.         If IsObject(aKicker) Then
  1288.             aKicker.DestroyBall
  1289.             If aKicker Is mExitKicker Then
  1290.                 ' Ball fell in from exit.  Stack it up on Slot 0.
  1291.                mSlot(0) = mSlot(0) + 1
  1292.                 NeedUpdate = True
  1293.                 UpdateTroughSwitches
  1294.                 addDone = True
  1295.             End If
  1296.         End If
  1297.  
  1298.         If Not addDone Then
  1299.             ' Ball came in from entrance.  Queue it up for entry.
  1300.            mBallsInEntry = mBallsInEntry + 1
  1301.         If mEntrySw > 0 Then
  1302.             mSwcopy = mEntrySw
  1303.             ' Trough has an entry kicker.  Ball will not enter trough
  1304.             ' until the entry solenoid is fired.
  1305.             Controller.Switch(mSwcopy) = True
  1306.             End If
  1307.             NeedUpdate = True
  1308.         End If
  1309.  
  1310.         PlaySound mSounds.Item("add")
  1311.     End Sub
  1312.  
  1313.     ' Use solCallback(solNo) on the trough entry kicker solenoid.
  1314.    Public Sub solIn(aEnabled)
  1315.         If aEnabled Then
  1316.             If mBallsInEntry > 0 Then
  1317.                 NeedUpdate = AddBallAtEntrance
  1318.                 PlaySound mSounds.Item("entryBall")
  1319.             Else
  1320.                 PlaySound mSounds.Item("entry")
  1321.             End If
  1322.         End If
  1323.     End Sub
  1324.     Public Sub EntrySol_On : solIn(true) : End Sub
  1325.  
  1326.     ' Use solCallback(solNo) on the trough exit kicker solenoid.
  1327.     Public Sub solOut(aEnabled)
  1328.         Dim iiBall, kDir, kForce, kBaseDir, ballsEjected
  1329.         ballsEjected = 0
  1330.  
  1331.         If aEnabled Then
  1332.             For iiBall = 0 to (mMaxBallsPerKick - 1)
  1333.                 kDir = (mExitDir + (Rnd - 0.5) * mDirVar)
  1334.                 kForce = vpMax(1, mExitForce + (Rnd - 0.5) * mForceVar * (0.8 * iiBall))   ' Dampen force a bit on subsequent balls.
  1335.  
  1336.                 If mSlot(0) > 0 Then
  1337.                     ' Remove ball from this slot.
  1338.                     mSlot(0) = mSlot(0) - 1
  1339.                     If isObject(mExitKicker) Then
  1340.                         vpmTimer.AddTimer ballsEjected*200, "vpmCreateBall(" & mExitKicker.Name & ").Kick " &_
  1341.                             CInt(kDir) & "," & Replace(kForce,",",".") & ", 0 '"
  1342.                     End If
  1343.  
  1344.                     ballsEjected = ballsEjected + 1
  1345.                 End If
  1346.             Next
  1347.  
  1348.             If ballsEjected > 0 Then
  1349.                 PlaySound mSounds.Item("exitBall")
  1350.                 UpdateTroughSwitches
  1351.                 NeedUpdate = True
  1352.             Else
  1353.                 PlaySound mSounds.Item("exit")
  1354.             End If
  1355.         End If
  1356.     End Sub
  1357.     Public Sub ExitSol_On : solOut(true) : End Sub
  1358. End Class
  1359.  
  1360. '--------------------
  1361. '     Saucer
  1362. '--------------------
  1363. Class cvpmSaucer
  1364.     ' Takes over for older cvpmBallStack in "saucer mode".
  1365.  
  1366.     Private mSw, mKicker, mExternalKicker
  1367.     Private mDir(1), mForce(1), mZForce(1), mDirVar, mForceVar
  1368.     Private mSounds
  1369.  
  1370.     Private Sub Class_Initialize
  1371.         mSw = 0
  1372.  
  1373.         mKicker = 0
  1374.         mExternalKicker = 0
  1375.         mDir(0) = 0 : mForce(0) = 1 : mZForce(0) = 0
  1376.         mDir(1) = 0 : mForce(1) = 1 : mZForce(1) = 0
  1377.         mDirVar = 0 : mForceVar = 0
  1378.  
  1379.         Set mSounds = New cvpmDictionary
  1380.     End Sub
  1381.  
  1382.     ' Initialization
  1383.  
  1384.     Public Sub InitKicker(aKicker, aSw, aDir, aForce, aZForce)
  1385.         If TypeName(aKicker) <> "Kicker" Then
  1386.             Err.Raise 17, "cvpmSaucer.InitKicker: Cannot use object of type '" & TypeName(aKicker) & "'."
  1387.         End If
  1388.  
  1389.         Set mKicker = aKicker
  1390.         mSw = aSw
  1391.         mDir(0) = aDir
  1392.         mForce(0) = vpMax(1, aForce)
  1393.         mZForce(0) = vpMax(0, aZForce)
  1394.     End Sub
  1395.  
  1396.     Public Sub InitExitVariance(aDirVar, aForceVar)
  1397.         mDirVar = aDirVar
  1398.         mForceVar = aForceVar
  1399.     End Sub
  1400.  
  1401.     ' Alternate kick params (simulates a saucer with two kickers)
  1402.    Public Sub InitAltKick(aDir, aForce, aZForce)
  1403.         mDir(1) = aDir
  1404.         mForce(1) = vpMax(1, aForce)
  1405.         mZForce(1) = vpMax(0, aZForce)
  1406.     End Sub
  1407.  
  1408.     ' Setup sounds
  1409.    Public Sub InitSounds(addSound, exitSoundEmpty, exitSoundBall)
  1410.         mSounds.Item("add") = addSound
  1411.         mSounds.Item("exit") = exitSoundEmpty
  1412.         mSounds.Item("exitBall") = exitSoundBall
  1413.     End Sub
  1414.  
  1415.     ' Generate hit event for the kicker(s) associated with this saucer.
  1416.    ' Accepts a single kicker, an Array, or a Collection.
  1417.     Public Sub CreateEvents(aName, aKicker)
  1418.         Dim obj, tmp
  1419.         If Not vpmCheckEvent(aName, Me) Then Exit Sub
  1420.         vpmSetArray tmp, aKicker
  1421.         For Each obj In tmp
  1422.             If isObject(obj) Then
  1423.                 vpmBuildEvent obj, "Hit", aName & ".AddBall Me"
  1424.             Else
  1425.                 vpmBuildEvent mKicker, "Hit", aName & ".AddBall Me"
  1426.             End If
  1427.         Next
  1428.     End Sub
  1429.  
  1430.     ' Ball management
  1431.  
  1432.     Public Sub AddBall(aKicker)
  1433.         Dim mSwcopy
  1434.         If isObject(aKicker) Then
  1435.             If aKicker Is mKicker Then
  1436.                 mKicker.Enabled = False
  1437.                 mExternalKicker = 0
  1438.             Else
  1439.                 aKicker.Enabled = False
  1440.                 Set mExternalKicker = aKicker
  1441.             End If
  1442.         Else
  1443.             mKicker.Enabled = False
  1444.             mExternalKicker = 0
  1445.         End If
  1446.  
  1447.         If mSw Then
  1448.             mSwcopy = mSw
  1449.             Controller.Switch(mSwcopy) = True
  1450.         End If
  1451.         PlaySound mSounds.Item("add")
  1452.     End Sub
  1453.  
  1454.     Public Property Get HasBall
  1455.         HasBall = False
  1456.         If IsObject(mExternalKicker) Then
  1457.             HasBall = True
  1458.         Else
  1459.             HasBall = Not mKicker.Enabled
  1460.         End If
  1461.     End Property
  1462.  
  1463.     ' SolCallback solNo, "mySaucer.solOut"
  1464.    Public Sub solOut(aEnabled) : If aEnabled Then KickOut 0 : End If : End Sub
  1465.     Public Sub ExitSol_On : KickOut 0 : End Sub
  1466.  
  1467.     ' SolCallback solNo, "mySaucer.solOutAlt"
  1468.    Public Sub solOutAlt(aEnabled) : If aEnabled Then KickOut 1 : End If : End Sub
  1469.     Public Sub ExitAltSol_On : KickOut 1 : End Sub
  1470.  
  1471.     Private Sub KickOut(kickIndex)
  1472.         Dim mSwcopy
  1473.         If HasBall Then
  1474.             Dim kDir, kForce, kZForce
  1475.  
  1476.             kDir = mDir(kickIndex) + (Rnd - 0.5)*mDirVar
  1477.             kForce = vpMax(1, mForce(kickIndex) + (Rnd - 0.5)*mForceVar)
  1478.             kZForce = mZForce(kickIndex)
  1479.  
  1480.             If IsObject(mExternalKicker) Then
  1481.                 ' Transfer ball to internal kicker and remove relationship
  1482.                vpmCreateBall mKicker
  1483.                 mExternalKicker.DestroyBall
  1484.                 mExternalKicker.Enabled = True
  1485.             Else
  1486.                 mKicker.Enabled = True
  1487.             End If
  1488.  
  1489.             mKicker.Kick kDir, kForce, kZForce
  1490.             If mSw Then
  1491.                 mSwcopy = mSw
  1492.                 Controller.Switch(mSwcopy) = False
  1493.             End If
  1494.             PlaySound mSounds.Item("exitBall")
  1495.         Else
  1496.             PlaySound mSounds.Item("exit")
  1497.         End If
  1498.     End Sub
  1499. End Class
  1500.  
  1501. '--------------------
  1502. '     BallStack (DEPRECATED/LEGACY)
  1503. '     Known issues:
  1504. '     - Adding more balls than conMaxBalls will crash the script.
  1505. '     - If there are more balls in trough than are ever used in a game (eg. Bride of Pinbot),
  1506. '       one or more trough switches will be permanently stuck down and may result in a ROM test report.
  1507. '     - Trough does not handle stacking balls at exit.
  1508. '     - Saucer mode is essentially a hack on top of the trough logic.
  1509. '--------------------
  1510. Class cvpmBallStack
  1511.     Private mSw(), mEntrySw, mBalls, mBallIn, mBallPos(), mSaucer, mBallsMoving
  1512.     Private mInitKicker, mExitKicker, mExitDir, mExitForce
  1513.     Private mExitDir2, mExitForce2
  1514.     Private mEntrySnd, mEntrySndBall, mExitSnd, mExitSndBall, mAddSnd
  1515.     Public KickZ, KickBalls, KickForceVar, KickAngleVar
  1516.  
  1517.     Private Sub Class_Initialize
  1518.         ReDim mSw(conStackSw), mBallPos(conMaxBalls)
  1519.         mBallIn = 0 : mBalls = 0 : mExitKicker = 0 : mInitKicker = 0 : mBallsMoving = False
  1520.         KickBalls = 1 : mSaucer = False : mExitDir = 0 : mExitForce = 0
  1521.         mExitDir2 = 0 : mExitForce2 = 0 : KickZ = 0 : KickForceVar = 0 : KickAngleVar = 0
  1522.         mAddSnd = 0 : mEntrySnd = 0 : mEntrySndBall = 0 : mExitSnd = 0 : mExitSndBall = 0
  1523.         vpmTimer.AddResetObj Me
  1524.     End Sub
  1525.  
  1526.     Private Property Let NeedUpdate(aEnabled) : vpmTimer.EnableUpdate Me, False, aEnabled : End Property
  1527.  
  1528.     Private Function SetSw(aNo, aStatus)
  1529.                 Dim mSwcopy
  1530.                 SetSw = False
  1531.                 If HasSw(aNo) Then
  1532.                     mSwcopy = mSw(aNo)
  1533.                     Controller.Switch(mSwcopy) = aStatus
  1534.                     SetSw = True
  1535.                 End If
  1536.     End Function
  1537.  
  1538.     Private Function HasSw(aNo)
  1539.         HasSw = False : If aNo <= conStackSw Then If mSw(aNo) Then HasSw = True
  1540.     End Function
  1541.  
  1542.     Public Sub Reset
  1543.         Dim mSwcopy
  1544.         Dim ii : If mBalls Then For ii = 1 to mBalls : SetSw mBallPos(ii), True : Next
  1545.         If mEntrySw And mBallIn > 0 Then
  1546.             mSwcopy = mEntrySw
  1547.             Controller.Switch(mSwcopy) = True
  1548.         End If
  1549.     End Sub
  1550.  
  1551.     Public Sub Update
  1552.         Dim BallQue, ii, mSwcopy
  1553.         NeedUpdate = False : BallQue = 1
  1554.         For ii = 1 To mBalls
  1555.             If mBallpos(ii) > BallQue Then ' next slot available
  1556.                 NeedUpdate = True
  1557.                 If HasSw(mBallPos(ii)) Then ' has switch
  1558.                     mSwcopy = mSw(mBallPos(ii))
  1559.                     If Controller.Switch(mSwcopy) Then
  1560.                         SetSw mBallPos(ii), False
  1561.                     Else
  1562.                         mBallPos(ii) = mBallPos(ii) - 1
  1563.                         SetSw mBallPos(ii), True
  1564.                     End If
  1565.                 Else ' no switch. Move ball to first switch or occupied slot
  1566.                     Do
  1567.                         mBallPos(ii) = mBallPos(ii) - 1
  1568.                     Loop Until SetSw(mBallPos(ii), True) Or mBallPos(ii) = BallQue
  1569.                 End If
  1570.             End If
  1571.             BallQue = mBallPos(ii) + 1
  1572.         Next
  1573.     End Sub
  1574.  
  1575.     Public Sub AddBall(aKicker)
  1576.         Dim mSwcopy
  1577.         If isObject(aKicker) Then
  1578.             If mSaucer Then
  1579.                 If aKicker Is mExitKicker Then
  1580.                     mExitKicker.Enabled = False : mInitKicker = 0
  1581.                 Else
  1582.                     aKicker.Enabled = False : Set mInitKicker = aKicker
  1583.                 End If
  1584.             Else
  1585.                 aKicker.DestroyBall
  1586.             End If
  1587.         ElseIf mSaucer Then
  1588.             mExitKicker.Enabled = False : mInitKicker = 0
  1589.         End If
  1590.         If mEntrySw Then
  1591.             mSwcopy = mEntrySw
  1592.             Controller.Switch(mSwcopy) = True : mBallIn = mBallIn + 1
  1593.         Else
  1594.             mBalls = mBalls + 1 : mBallPos(mBalls) = conStackSw + 1 : NeedUpdate = True
  1595.         End If
  1596.         PlaySound mAddSnd
  1597.     End Sub
  1598.  
  1599.     ' A bug in the script engine forces the "End If" at the end
  1600.     Public Sub SolIn(aEnabled)     : If aEnabled Then KickIn        : End If : End Sub
  1601.     Public Sub SolOut(aEnabled)    : If aEnabled Then KickOut False : End If : End Sub
  1602.     Public Sub SolOutAlt(aEnabled) : If aEnabled Then KickOut True  : End If : End Sub
  1603.     Public Sub EntrySol_On   : KickIn        : End Sub
  1604.     Public Sub ExitSol_On    : KickOut False : End Sub
  1605.     Public Sub ExitAltSol_On : KickOut True  : End Sub
  1606.  
  1607.     Private Sub KickIn
  1608.         Dim mSwcopy
  1609.         If mBallIn Then PlaySound mEntrySndBall Else PlaySound mEntrySnd : Exit Sub
  1610.         mBalls = mBalls + 1 : mBallIn = mBallIn - 1 : mBallPos(mBalls) = conStackSw + 1 : NeedUpdate = True
  1611.         If mEntrySw And mBallIn = 0 Then
  1612.             mSwcopy = mEntrySw
  1613.             Controller.Switch(mSwcopy) = False
  1614.         End If
  1615.     End Sub
  1616.  
  1617.     Private Sub KickOut(aAltSol)
  1618.         Dim ii,jj, kForce, kDir, kBaseDir
  1619.         If mBalls Then PlaySound mExitSndBall Else PlaySound mExitSnd : Exit Sub
  1620.         If aAltSol Then kForce = mExitForce2 : kBaseDir = mExitDir2 Else kForce = mExitForce : kBaseDir = mExitDir
  1621.         kForce = kForce + (Rnd - 0.5)*KickForceVar
  1622.         If mSaucer Then
  1623.             SetSw 1, False : mBalls = 0 : kDir = kBaseDir + (Rnd - 0.5)*KickAngleVar
  1624.             If isObject(mInitKicker) Then
  1625.                 vpmCreateBall mExitKicker : mInitKicker.Destroyball : mInitKicker.Enabled = True
  1626.             Else
  1627.                 mExitKicker.Enabled = True
  1628.             End If
  1629.             mExitKicker.Kick kDir, kForce, KickZ
  1630.         Else
  1631.             For ii = 1 To kickballs
  1632.                 If mBalls = 0 Or mBallPos(1) <> ii Then Exit For ' No more balls
  1633.                 For jj = 2 To mBalls ' Move balls in array
  1634.                     mBallPos(jj-1) = mBallPos(jj)
  1635.                 Next
  1636.                 mBallPos(mBalls) = 0 : mBalls = mBalls - 1 : NeedUpdate = True
  1637.                 SetSw ii, False
  1638.                 If isObject(mExitKicker) Then
  1639.                     If kForce < 1 Then kForce = 1
  1640.                     kDir = kBaseDir + (Rnd - 0.5)*KickAngleVar
  1641.                     vpmTimer.AddTimer (ii-1)*200, "vpmCreateBall(" & mExitKicker.Name & ").Kick " &_
  1642.                       CInt(kDir) & "," & Replace(kForce,",",".") & "," & Replace(KickZ,",",".") & " '"
  1643.                 End If
  1644.                 kForce = kForce * 0.8
  1645.             Next
  1646.         End If
  1647.     End Sub
  1648.  
  1649.     Public Sub InitSaucer(aKicker, aSw, aDir, aPower)
  1650.         InitKick aKicker, aDir, aPower : mSaucer = True
  1651.         If aSw Then mSw(1) = aSw Else mSw(1) = aKicker.TimerInterval
  1652.     End Sub
  1653.  
  1654.     Public Sub InitNoTrough(aKicker, aSw, aDir, aPower)
  1655.         InitKick aKicker, aDir, aPower : Balls = 1
  1656.         If aSw Then mSw(1) = aSw Else mSw(1) = aKicker.TimerInterval
  1657.         If Not IsObject(vpmTrough) Then Set vpmTrough = Me
  1658.     End Sub
  1659.  
  1660.     Public Sub InitSw(aEntry, aSw1, aSw2, aSw3, aSw4, aSw5, aSw6, aSw7)
  1661.         mEntrySw = aEntry : mSw(1) = aSw1 : mSw(2) = aSw2 : mSw(3) = aSw3 : mSw(4) = aSw4
  1662.         mSw(5) = aSw5 : mSw(6) = aSw6 : mSw(7) = aSw7 : mSw(8) = 0
  1663.         If Not IsObject(vpmTrough) Then Set vpmTrough = Me
  1664.     End Sub
  1665.  
  1666.     Public Sub InitSw8(aEntry, aSw1, aSw2, aSw3, aSw4, aSw5, aSw6, aSw7, aSw8)
  1667.         mEntrySw = aEntry : mSw(1) = aSw1 : mSw(2) = aSw2 : mSw(3) = aSw3 : mSw(4) = aSw4
  1668.         mSw(5) = aSw5 : mSw(6) = aSw6 : mSw(7) = aSw7 : mSw(8) = aSw8
  1669.         If Not IsObject(vpmTrough) Then Set vpmTrough = Me
  1670.     End Sub
  1671.  
  1672.     Public Sub InitKick(aKicker, aDir, aForce)
  1673.         Set mExitKicker = aKicker : mExitDir = aDir : mExitForce = aForce
  1674.     End Sub
  1675.  
  1676.     Public Sub CreateEvents(aName, aKicker)
  1677.         Dim obj, tmp
  1678.         If Not vpmCheckEvent(aName, Me) Then Exit Sub
  1679.         vpmSetArray tmp, aKicker
  1680.         For Each obj In tmp
  1681.             If isObject(obj) Then
  1682.                 vpmBuildEvent obj, "Hit", aName & ".AddBall Me"
  1683.             Else
  1684.                 vpmBuildEvent mExitKicker, "Hit", aName & ".AddBall Me"
  1685.             End If
  1686.         Next
  1687.     End Sub
  1688.  
  1689.     Public Property Let IsTrough(aIsTrough)
  1690.         If aIsTrough Then
  1691.             Set vpmTrough = Me
  1692.         ElseIf IsObject(vpmTrough) Then
  1693.             If vpmTrough Is Me Then vpmTrough = 0
  1694.         End If
  1695.     End Property
  1696.  
  1697.     Public Property Get IsTrough : IsTrough = vpmTrough Is Me : End Property
  1698.  
  1699.     Public Sub InitAltKick(aDir, aForce)
  1700.         mExitDir2 = aDir : mExitForce2 = aForce
  1701.     End Sub
  1702.  
  1703.     Public Sub InitEntrySnd(aBall, aNoBall) : mEntrySndBall = aBall : mEntrySnd = aNoBall : End Sub
  1704.     Public Sub InitExitSnd(aBall, aNoBall)  : mExitSndBall = aBall  : mExitSnd = aNoBall  : End Sub
  1705.     Public Sub InitAddSnd(aSnd) : mAddSnd = aSnd : End Sub
  1706.  
  1707.     Public Property Let Balls(aBalls)
  1708.         Dim ii
  1709.         For ii = 1 To conStackSw
  1710.             SetSw ii, False : mBallPos(ii) = conStackSw + 1
  1711.         Next
  1712.         If mSaucer And aBalls > 0 And mBalls = 0 Then vpmCreateBall mExitKicker
  1713.         mBalls = aBalls : NeedUpdate = True
  1714.     End Property
  1715.  
  1716.     Public Default Property Get Balls : Balls = mBalls         : End Property
  1717.     Public Property Get BallsPending  : BallsPending = mBallIn : End Property
  1718.  
  1719.     ' Obsolete stuff
  1720.     Public Sub SolEntry(aSnd1, aSnd2, aEnabled)
  1721.         If aEnabled Then mEntrySndBall = aSnd1 : mEntrySnd = aSnd2 : KickIn
  1722.     End Sub
  1723.     Public Sub SolExit(aSnd1, aSnd2, aEnabled)
  1724.         If aEnabled Then mExitSndBall = aSnd1 : mExitSnd = aSnd2 : KickOut False
  1725.     End Sub
  1726.     Public Sub InitProxy(aProxyPos, aSwNo) : End Sub
  1727.     Public TempBallColour, TempBallImage, BallColour
  1728.     Public Property Let BallImage(aImage) : vpmBallImage = aImage : End Property
  1729. End Class
  1730.  
  1731. '--------------------
  1732. '       Nudge
  1733. '--------------------
  1734. class cvpmNudge
  1735.     Private mCount, mSensitivity, mNudgeTimer, mSlingBump, mForce
  1736.     Public TiltSwitch
  1737.  
  1738.     Private Sub Class_Initialize
  1739.         mCount = 0 : TiltSwitch = 0 : mSensitivity = 5 : vpmTimer.AddResetObj Me
  1740.     End sub
  1741.  
  1742.     Private Property Let NeedUpdate(aEnabled) : vpmTimer.EnableUpdate Me, False, aEnabled : End Property
  1743.  
  1744.     Public Property Let TiltObj(aSlingBump)
  1745.         Dim ii
  1746.         ReDim mForce(vpmSetArray(mSlingBump, aSlingBump))
  1747.         For ii = 0 To UBound(mForce)
  1748.             If TypeName(mSlingBump(ii)) = "Bumper" Then mForce(ii) = mSlingBump(ii).Threshold
  1749.             If vpmVPVer >= 90 and TypeName(mSlingBump(ii)) = "Wall" Then mForce(ii) = mSlingBump(ii).SlingshotThreshold
  1750.         Next
  1751.     End Property
  1752.  
  1753.     Public Property Let Sensitivity(aSens) : mSensitivity = (10-aSens)+1 : End property
  1754.  
  1755.     Public Sub DoNudge(ByVal aDir, ByVal aForce)
  1756.         aDir = aDir + (Rnd-0.5)*15*aForce : aForce = (0.6+Rnd*0.8)*aForce
  1757.         Nudge aDir, aForce
  1758.         If TiltSwitch = 0 Then Exit Sub ' If no switch why care
  1759.         mCount = mCount + aForce * 1.2
  1760.         If mCount > mSensitivity + 10 Then mCount = mSensitivity + 10
  1761.         If mCount >= mSensitivity Then vpmTimer.PulseSw TiltSwitch
  1762.         NeedUpdate = True
  1763.     End sub
  1764.  
  1765.     Public Sub Update
  1766.         If mCount > 0 Then
  1767.             mNudgeTimer = mNudgeTimer + 1
  1768.             If mNudgeTimer > 1000\conTimerPulse Then
  1769.                 If mCount > mSensitivity+1 Then mCount = mCount - 1 : vpmTimer.PulseSw TiltSwitch
  1770.                 mCount = mCount - 1 : mNudgeTimer = 0
  1771.             End If
  1772.         Else
  1773.             mCount = 0 : NeedUpdate = False
  1774.         End If
  1775.     End Sub
  1776.  
  1777.     Public Sub Reset : mCount = 0 : End Sub
  1778.  
  1779.     Public Sub SolGameOn(aEnabled)
  1780.         if IsEmpty(mForce) then exit sub 'prevent errors if vpmNudge.TiltObj isn't set
  1781.         Dim obj, ii
  1782.         If aEnabled Then
  1783.             ii = 0
  1784.             For Each obj In mSlingBump
  1785.                 If TypeName(obj) = "Bumper" Then obj.Threshold = mForce(ii)
  1786.                 If vpmVPVer >= 90 and TypeName(obj) = "Wall" Then obj.SlingshotThreshold = mForce(ii)
  1787.                 ii = ii + 1
  1788.             Next
  1789.         Else
  1790.             For Each obj In mSlingBump
  1791.                 If TypeName(obj) = "Bumper" Then obj.Threshold = 100
  1792.                 If vpmVPVer >= 90 and TypeName(obj) = "Wall" Then obj.SlingshotThreshold = 100
  1793.             Next
  1794.         End If
  1795.     End Sub
  1796. End Class
  1797.  
  1798. '--------------------
  1799. '    DropTarget
  1800. '--------------------
  1801. Class cvpmDropTarget
  1802.     Private mDropObj, mDropSw(), mDropSnd, mRaiseSnd, mSwAnyUp, mSwAllDn, mAllDn, mLink
  1803.  
  1804.     Private Sub Class_Initialize
  1805.         mDropSnd = 0 : mRaiseSnd = 0 : mSwAnyUp = 0 : mSwAllDn = 0 : mAllDn = False : mLink = Empty
  1806.     End sub
  1807.  
  1808.     Private Sub CheckAllDn(ByVal aStatus)
  1809.         Dim obj
  1810.         If Not IsEmpty(mLink) Then
  1811.             If aStatus Then
  1812.                 For Each obj In mLink : aStatus = aStatus And obj.AllDown : Next
  1813.             End If
  1814.             For Each obj In mLink: obj.SetAllDn aStatus : Next
  1815.         End If
  1816.         SetAllDn aStatus
  1817.     End Sub
  1818.  
  1819.     Public Sub SetAllDn(aStatus)
  1820.         Dim mSwcopy
  1821.         If mSwAllDn Then
  1822.             mSwcopy = mSwAllDn
  1823.             Controller.Switch(mSwcopy) = aStatus
  1824.         End If
  1825.         If mSwAnyUp Then
  1826.             mSwcopy = mSwAnyUp
  1827.             Controller.Switch(mSwcopy) = Not aStatus
  1828.         End If
  1829.     End Sub
  1830.  
  1831.     Public Sub InitDrop(aWalls, aSw)
  1832.         Dim obj, obj2, ii
  1833.         ' Fill in switch number
  1834.         On Error Resume Next : ReDim mDropSw(0)
  1835.         If IsArray(aSw) Then
  1836.             ReDim mDropSw(UBound(aSw))
  1837.             For ii = 0 To UBound(aSw) : mDropSw(ii) = aSw(ii) : Next
  1838.         ElseIf aSw = 0 Or Err Then
  1839.             On Error Goto 0
  1840.             If vpmIsArray(aWalls) Then
  1841.                 ii = 0 : If IsArray(aWalls) Then ReDim mDropSw(UBound(aWalls)) Else ReDim mDropSw(aWalls.Count-1)
  1842.                 For Each obj In aWalls
  1843.                     If vpmIsArray(obj) Then
  1844.                         For Each obj2 In obj
  1845.                             If obj2.HasHitEvent Then mDropSw(ii) = obj2.TimerInterval : Exit For
  1846.                         Next
  1847.                     Else
  1848.                         mDropSw(ii) = obj.TimerInterval
  1849.                     End If
  1850.                     ii = ii + 1
  1851.                 Next
  1852.             Else
  1853.                 mDropSw(0) = aWalls.TimerInterval
  1854.             End If
  1855.         Else
  1856.             mDropSw(0) = aSw
  1857.         End If
  1858.         ' Copy walls
  1859.         vpmSetArray mDropObj, aWalls
  1860.     End Sub
  1861.  
  1862.     Public Sub CreateEvents(aName)
  1863.         Dim ii, obj1, obj2
  1864.         If Not vpmCheckEvent(aName, Me) Then Exit Sub
  1865.         ii = 1
  1866.         For Each obj1 In mDropObj
  1867.             If vpmIsArray(obj1) Then
  1868.                 For Each obj2 In obj1
  1869.                     if TypeName(obj2) = "HitTarget" Then    'if object in array is a Target, use .Dropped
  1870.                         vpmBuildEvent obj2, "Dropped", aName & ".Hit " & ii 'Droptarget_Dropped : DTbank.Hit 1 : End Sub
  1871.                     else
  1872.                         If obj2.HasHitEvent Then vpmBuildEvent obj2, "Hit", aName & ".Hit " & ii
  1873.                     End If
  1874.                 Next
  1875.             Else
  1876.                 if TypeName(obj1) = "HitTarget" Then    'if object in array is a Target, use .Dropped
  1877.                     vpmBuildEvent obj1, "Dropped", aName & ".Hit " & ii
  1878.                 else
  1879.                     vpmBuildEvent obj1, "Hit", aName & ".Hit " & ii
  1880.                 End If
  1881.             End If
  1882.             ii = ii + 1
  1883.         Next
  1884.     End Sub
  1885.  
  1886.     Public Property Let AnyUpSw(aSwAnyUp)
  1887.         Dim mSwcopy
  1888.         mSwAnyUp = aSwAnyUp
  1889.         mSwcopy = mSwAnyUp
  1890.         Controller.Switch(mSwcopy) = True
  1891.     End Property
  1892.     Public Property Let AllDownSw(aSwAllDn) : mSwAllDn = aSwAllDn : End Property
  1893.     Public Property Get AllDown : AllDown = mAllDn : End Property
  1894.     Public Sub InitSnd(aDrop, aRaise) : mDropSnd = aDrop : mRaiseSnd = aRaise : End Sub
  1895.     Public Property Let LinkedTo(aLink)
  1896.         If IsArray(aLink) Then mLink = aLink Else mLink = Array(aLink)
  1897.     End Property
  1898.  
  1899.     Public Sub Hit(aNo)
  1900.         Dim ii, mSwcopy
  1901.         vpmSolWall mDropObj(aNo-1), mDropSnd, True
  1902.         mSwcopy = mDropSw(aNo-1)
  1903.         Controller.Switch(mSwcopy) = True
  1904.         For Each ii In mDropSw
  1905.             mSwcopy = ii
  1906.             If Not Controller.Switch(mSwcopy) Then Exit Sub
  1907.         Next
  1908.         mAllDn = True : CheckAllDn True
  1909.     End Sub
  1910.  
  1911.     Public Sub SolHit(aNo, aEnabled) : If aEnabled Then Hit aNo : End If : End Sub
  1912.  
  1913.     Public Sub SolUnhit(aNo, aEnabled)
  1914.         Dim mSwcopy
  1915.         Dim ii : If Not aEnabled Then Exit Sub
  1916.         PlaySound mRaiseSnd : vpmSolWall mDropObj(aNo-1), False, False
  1917.         mSwcopy = mDropSw(aNo-1)
  1918.         Controller.Switch(mSwcopy) = False
  1919.         mAllDn = False : CheckAllDn False
  1920.     End Sub
  1921.  
  1922.     Public Sub SolDropDown(aEnabled)
  1923.         Dim mSwcopy
  1924.         Dim ii : If Not aEnabled Then Exit Sub
  1925.         PlaySound mDropSnd
  1926.         For Each ii In mDropObj : vpmSolWall ii, False, True : Next
  1927.         For Each ii In mDropSw  : mSwcopy = ii : Controller.Switch(mSwcopy) = True : Next
  1928.         mAllDn = True : CheckAllDn True
  1929.     End Sub
  1930.  
  1931.     Public Sub SolDropUp(aEnabled)
  1932.         Dim mSwcopy
  1933.         Dim ii : If Not aEnabled Then Exit Sub
  1934.         PlaySound mRaiseSnd
  1935.         For Each ii In mDropObj : vpmSolWall ii, False, False : Next
  1936.         For Each ii In mDropSw  : mSwcopy = ii : Controller.Switch(mSwcopy) = False : Next
  1937.         mAllDn = False : CheckAllDn False
  1938.     End Sub
  1939.  
  1940.     Public Sub DropSol_On : SolDropUp True : End Sub
  1941. End Class
  1942.  
  1943. '--------------------
  1944. '       Magnet
  1945. '--------------------
  1946. Class cvpmMagnet
  1947.     Private mEnabled, mBalls, mTrigger
  1948.     Public X, Y, Strength, Size, GrabCenter, Solenoid
  1949.  
  1950.     Private Sub Class_Initialize
  1951.         Size = 1 : Strength = 0 : Solenoid = 0 : mEnabled = False
  1952.         Set mBalls = New cvpmDictionary
  1953.     End Sub
  1954.  
  1955.     Private Property Let NeedUpdate(aEnabled) : vpmTimer.EnableUpdate Me, True, aEnabled : End Property
  1956.  
  1957.     Public Sub InitMagnet(aTrigger, aStrength)
  1958.         Dim tmp
  1959.         If vpmIsArray(aTrigger) Then Set tmp = aTrigger(0) Else Set tmp = aTrigger
  1960.         X = tmp.X : Y = tmp.Y : Size = tmp.Radius : vpmTimer.InitTimer tmp, True
  1961.         If IsArray(aTrigger) Then mTrigger = aTrigger Else Set mTrigger = aTrigger
  1962.         Strength = aStrength : GrabCenter = aStrength > 14
  1963.     End Sub
  1964.  
  1965.     Public Sub CreateEvents(aName)
  1966.         If vpmCheckEvent(aName, Me) Then
  1967.             vpmBuildEvent mTrigger, "Hit", aName & ".AddBall ActiveBall"
  1968.             vpmBuildEvent mTrigger, "UnHit", aName & ".RemoveBall ActiveBall"
  1969.         End If
  1970.     End Sub
  1971.  
  1972.     Public Property Let MagnetOn(aEnabled) : mEnabled = aEnabled : End Property
  1973.     Public Property Get MagnetOn
  1974.         If Solenoid > 0 Then MagnetOn = Controller.Solenoid(Solenoid) Else MagnetOn = mEnabled
  1975.     End Property
  1976.  
  1977.     Public Sub AddBall(aBall)
  1978.         With mBalls
  1979.             If .Exists(aBall) Then .Item(aBall) = .Item(aBall) + 1 Else .Add aBall, 1 : NeedUpdate = True
  1980.         End With
  1981.     End Sub
  1982.  
  1983.     Public Sub RemoveBall(aBall)
  1984.         With mBalls
  1985.             If .Exists(aBall) Then .Item(aBall) = .Item(aBall) - 1 : If .Item(aBall) <= 0 Then .Remove aBall
  1986.             NeedUpdate = (.Count > 0)
  1987.         End With
  1988.     End Sub
  1989.  
  1990.     Public Property Get Balls : Balls = mBalls.Keys : End Property
  1991.  
  1992.     Public Sub Update
  1993.         Dim obj
  1994.         If MagnetOn Then
  1995.             On Error Resume Next
  1996.             For Each obj In mBalls.Keys
  1997.                 If obj.X < 0 Or Err Then mBalls.Remove obj Else AttractBall obj
  1998.             Next
  1999.             On Error Goto 0
  2000.         End If
  2001.     End Sub
  2002.  
  2003.     Public Sub AttractBall(aBall)
  2004.         Dim dX, dY, dist, force, ratio
  2005.         dX = aBall.X - X : dY = aBall.Y - Y : dist = Sqr(dX*dX + dY*dY)
  2006.         If dist > Size Or dist < 1 Then Exit Sub 'Just to be safe
  2007.         If GrabCenter And dist < 20 Then
  2008.             aBall.VelX = 0 : aBall.VelY = 0 : aBall.X = X : aBall.Y = Y
  2009.         Else
  2010.             ratio = dist / (1.5*Size)
  2011.             force = Strength * exp(-0.2/ratio)/(ratio*ratio*56) * 1.5
  2012.             aBall.VelX = (aBall.VelX - dX * force / dist) * 0.985
  2013.             aBall.VelY = (aBall.VelY - dY * force / dist) * 0.985
  2014.         End if
  2015.     End Sub
  2016.     ' obsolete
  2017.     Public Property Let Range(aSize) : Size = aSize : End Property
  2018.     Public Property Get Range        : Range = Size : End Property
  2019. End Class
  2020.  
  2021. '--------------------
  2022. '     Turntable
  2023. '--------------------
  2024. Class cvpmTurntable
  2025.     Private mX, mY, mSize, mTrigger, mBalls, mSpinUp, mSpinDown
  2026.     Private mMotorOn, mSpinCW
  2027.     Private mMaxSpeed, mTargetSpeed, mCurrentAccel
  2028.     Public Speed
  2029.  
  2030.     Private Sub Class_Initialize
  2031.         Set mBalls = New cvpmDictionary
  2032.         mMotorOn = False : mSpinCW = True : Speed = 0 : mSpinUp = 10 : mSpinDown = 4
  2033.         AdjustTargets
  2034.     End Sub
  2035.  
  2036.     Private Property Let NeedUpdate(aEnabled) : vpmTimer.EnableUpdate Me, True, aEnabled : End Property
  2037.  
  2038.     Public Sub InitTurntable(aTrigger, aMaxSpeed)
  2039.         mX = aTrigger.X : mY = aTrigger.Y : mSize = aTrigger.Radius : vpmTimer.InitTimer aTrigger, True
  2040.         mMaxSpeed = aMaxSpeed : Set mTrigger = aTrigger
  2041.         AdjustTargets
  2042.     End Sub
  2043.  
  2044.     Public Sub CreateEvents(aName)
  2045.         If vpmCheckEvent(aName, Me) Then
  2046.             vpmBuildEvent mTrigger, "Hit", aName & ".AddBall ActiveBall"
  2047.             vpmBuildEvent mTrigger, "UnHit", aName & ".RemoveBall ActiveBall"
  2048.         End If
  2049.     End Sub
  2050.  
  2051.     Public Sub SolMotorState(aCW, aMotorOn)
  2052.         mSpinCW = aCW
  2053.         mMotorOn = aMotorOn
  2054.         AdjustTargets
  2055.     End Sub
  2056.  
  2057.     Private Sub AdjustTargets
  2058.         If mMotorOn Then
  2059.             mTargetSpeed = MaxSpeed
  2060.             mCurrentAccel = SpinUp
  2061.             If Not mSpinCW Then mTargetSpeed = -MaxSpeed
  2062.         Else
  2063.             mTargetSpeed = 0
  2064.             mCurrentAccel = SpinDown
  2065.         End If
  2066.  
  2067.         NeedUpdate = mBalls.Count Or SpinUp Or SpinDown
  2068.     End Sub
  2069.  
  2070.     Public Property Let MaxSpeed(newSpeed) : mMaxSpeed = newSpeed : AdjustTargets : End Property
  2071.     Public Property Let SpinUp(newRate) : mSpinUp = newRate : AdjustTargets : End Property
  2072.     Public Property Let SpinDown(newRate) : mSpinDown = newRate : AdjustTargets : End Property
  2073.  
  2074.     Public Property Get MaxSpeed : MaxSpeed = mMaxSpeed : End Property
  2075.     Public Property Get SpinUp : SpinUp = mSpinup : End Property
  2076.     Public Property Get SpinDown : SpinDown = mSpinDown : End Property
  2077.  
  2078.     Public Property Let MotorOn(aEnabled) : SolMotorState mSpinCW, aEnabled : End Property
  2079.     Public Property Let SpinCW(aCW) : SolMotorState aCW, mMotorOn : End Property
  2080.  
  2081.     Public Property Get MotorOn : MotorOn = mMotorOn : End Property
  2082.     Public Property Get SpinCW : SpinCW = mSpinCW : End Property
  2083.  
  2084.     Public Sub AddBall(aBall)
  2085.         On Error Resume Next : mBalls.Add aBall,0 : NeedUpdate = True
  2086.     End Sub
  2087.     Public Sub RemoveBall(aBall)
  2088.         On Error Resume Next
  2089.         mBalls.Remove aBall : NeedUpdate = mBalls.Count Or SpinUp Or SpinDown
  2090.     End Sub
  2091.     Public Property Get Balls : Balls = mBalls.Keys : End Property
  2092.  
  2093.     Public Sub Update
  2094.         If Speed > mTargetSpeed Then
  2095.             Speed = Speed - mCurrentAccel/100
  2096.             If Speed < mTargetSpeed Then Speed = mTargetSpeed : NeedUpdate = mBalls.Count
  2097.         ElseIf Speed < mTargetSpeed Then
  2098.             Speed = Speed + mCurrentAccel/100
  2099.             If Speed > mTargetSpeed Then Speed = mTargetSpeed : NeedUpdate = mBalls.Count
  2100.         End If
  2101.  
  2102.         If Speed Then
  2103.             Dim obj
  2104.             On Error Resume Next
  2105.             For Each obj In mBalls.Keys
  2106.                 If obj.X < 0 Or Err Then mBalls.Remove obj Else AffectBall obj
  2107.             Next
  2108.             On Error Goto 0
  2109.         End If
  2110.     End Sub
  2111.  
  2112.     Public Sub AffectBall(aBall)
  2113.         Dim dX, dY, dist
  2114.         dX = aBall.X - mX : dY = aBall.Y - mY : dist = Sqr(dX*dX + dY*dY)
  2115.         If dist > mSize Or dist < 1 Or Speed = 0 Then Exit Sub
  2116.         aBall.VelX = aBall.VelX - (dY * Speed / 8000)
  2117.         aBall.VelY = aBall.VelY + (dX * Speed / 8000)
  2118.     End Sub
  2119. End Class
  2120.  
  2121. '--------------------
  2122. '     Mech
  2123. '--------------------
  2124. Const vpmMechLinear    = &H00
  2125. Const vpmMechNonLinear = &H01
  2126. Const vpmMechCircle    = &H00
  2127. Const vpmMechStopEnd   = &H02
  2128. Const vpmMechReverse   = &H04
  2129. Const vpmMechOneSol    = &H00
  2130. Const vpmMechOneDirSol = &H10
  2131. Const vpmMechTwoDirSol = &H20
  2132. Const vpmMechStepSol   = &H40
  2133. Const vpmMechSlow      = &H00
  2134. Const vpmMechFast      = &H80
  2135. Const vpmMechStepSw    = &H00
  2136. Const vpmMechLengthSw  = &H100
  2137.  
  2138. Class cvpmMech
  2139.     Public Sol1, Sol2, MType, Length, Steps, Acc, Ret
  2140.     Private mMechNo, mNextSw, mSw(), mLastPos, mLastSpeed, mCallback
  2141.  
  2142.     Private Sub Class_Initialize
  2143.         ReDim mSw(10)
  2144.         gNextMechNo = gNextMechNo + 1 : mMechNo = gNextMechNo : mNextSw = 0 : mLastPos = 0 : mLastSpeed = 0
  2145.         MType = 0 : Length = 0 : Steps = 0 : Acc = 0 : Ret = 0 : vpmTimer.addResetObj Me
  2146.     End Sub
  2147.  
  2148.     Public Sub AddSw(aSwNo, aStart, aEnd)
  2149.         mSw(mNextSw) = Array(aSwNo, aStart, aEnd, 0)
  2150.         mNextSw = mNextSw + 1
  2151.     End Sub
  2152.  
  2153.     Public Sub AddPulseSwNew(aSwNo, aInterval, aStart, aEnd)
  2154.         If Controller.Version >= "01200000" Then
  2155.             mSw(mNextSw) = Array(aSwNo, aStart, aEnd, aInterval)
  2156.         Else
  2157.             mSw(mNextSw) = Array(aSwNo, -aInterval, aEnd - aStart + 1, 0)
  2158.         End If
  2159.         mNextSw = mNextSw + 1
  2160.     End Sub
  2161.  
  2162.     Public Sub Start
  2163.         Dim sw, ii
  2164.         With Controller
  2165.             .Mech(1) = Sol1 : .Mech(2) = Sol2 : .Mech(3) = Length
  2166.             .Mech(4) = Steps : .Mech(5) = MType : .Mech(6) = Acc : .Mech(7) = Ret
  2167.             ii = 10
  2168.             For Each sw In mSw
  2169.                 If IsArray(sw) Then
  2170.                     .Mech(ii) = sw(0) : .Mech(ii+1) = sw(1)
  2171.                     .Mech(ii+2) = sw(2) : .Mech(ii+3) = sw(3)
  2172.                     ii = ii + 10
  2173.                 End If
  2174.             Next
  2175.             .Mech(0) = mMechNo
  2176.         End With
  2177.         If IsObject(mCallback) Then mCallBack 0, 0, 0 : mLastPos = 0 : vpmTimer.EnableUpdate Me, False, True
  2178.     End Sub
  2179.  
  2180.     Public Property Get Position : Position = Controller.GetMech(mMechNo) : End Property
  2181.     Public Property Get Speed    : Speed = Controller.GetMech(-mMechNo)   : End Property
  2182.     Public Property Let Callback(aCallBack) : Set mCallback = aCallBack : End Property
  2183.  
  2184.     Public Sub Update
  2185.         Dim currPos, speed
  2186.         currPos = Controller.GetMech(mMechNo)
  2187.         speed = Controller.GetMech(-mMechNo)
  2188.         If currPos < 0 Or (mLastPos = currPos And mLastSpeed = speed) Then Exit Sub
  2189.         mCallBack currPos, speed, mLastPos : mLastPos = currPos : mLastSpeed = speed
  2190.     End Sub
  2191.  
  2192.     Public Sub Reset : Start : End Sub
  2193.     ' Obsolete
  2194.     Public Sub AddPulseSw(aSwNo, aInterval, aLength) : AddSw aSwNo, -aInterval, aLength : End Sub
  2195. End Class
  2196.  
  2197. '--------------------
  2198. '   Captive Ball
  2199. '--------------------
  2200. Class cvpmCaptiveBall
  2201.     Private mBallKicked, mBallDir, mBallCos, mBallSin, mTrigHit
  2202.     Private mTrig, mWall, mKickers, mVelX, mVelY, mKickNo
  2203.     Public ForceTrans, MinForce, RestSwitch, NailedBalls
  2204.  
  2205.     Private Sub Class_Initialize
  2206.         mBallKicked = False : ForceTrans = 0.5 : mTrigHit = False : MinForce = 3 : NailedBalls = 0
  2207.         vpmTimer.addResetObj Me
  2208.     End Sub
  2209.  
  2210.     Public Sub InitCaptive(aTrig, aWall, aKickers, aBallDir)
  2211.         Set mTrig = aTrig : Set mWall = aWall
  2212.         mKickNo = vpmSetArray(mKickers, aKickers)
  2213.         mBallDir = aBallDir : mBallCos = Cos(aBallDir * 3.1415927/180) : mBallSin = Sin(aBallDir * 3.1415927/180)
  2214.     End Sub
  2215.  
  2216.     Public Sub Start
  2217.         Dim mSwcopy
  2218.         vpmCreateBall mKickers(mKickNo + (mKickNo <> NailedBalls))
  2219.         If RestSwitch Then
  2220.             mSwcopy = RestSwitch
  2221.             Controller.Switch(mSwcopy) = True
  2222.         End If
  2223.     End Sub
  2224.  
  2225.     Public Sub TrigHit(aBall)
  2226.         mTrigHit = IsObject(aBall) : If mTrigHit Then mVelX = aBall.VelX : mVelY = aBall.VelY
  2227.     End Sub
  2228.  
  2229.     Public Sub Reset
  2230.         Dim mSwcopy
  2231.         If RestSwitch Then
  2232.             mSwcopy = RestSwitch
  2233.             Controller.Switch(mSwcopy) = True
  2234.         End If
  2235.     End Sub
  2236.  
  2237.     Public Sub BallHit(aBall)
  2238.         Dim dX, dY, force, mSwcopy
  2239.         If mBallKicked Then Exit Sub ' Ball is not here
  2240.         If mTrigHit Then mTrigHit = False Else mVelX = aBall.VelX : mVelY = aBall.VelY
  2241.         dX = aBall.X - mKickers(0).X : dY = aBall.Y - mKickers(0).Y
  2242.         force = -ForceTrans * (dY * mVelY + dX * mVelX) * (dY * mBallCos + dX * mBallSin) / (dX*dX + dY*dY)
  2243.         If force < 1 Then Exit Sub
  2244.         If force < MinForce Then force = MinForce
  2245.         If mKickNo <> NailedBalls Then
  2246.             vpmCreateBall mKickers(mKickNo)
  2247.             mKickers(mKickNo-1).DestroyBall
  2248.         End If
  2249.         mKickers(mKickNo).Kick mBallDir, force : mBallKicked = True
  2250.         If RestSwitch Then
  2251.             mSwcopy = RestSwitch
  2252.             Controller.Switch(mSwcopy) = False
  2253.         End If
  2254.     End Sub
  2255.  
  2256.     Public Sub BallReturn(aKicker)
  2257.         Dim mSwcopy
  2258.         If mKickNo <> NailedBalls Then vpmCreateBall mKickers(mKickNo-1) : aKicker.DestroyBall
  2259.         mBallKicked = False
  2260.         If RestSwitch Then
  2261.             mSwcopy = RestSwitch
  2262.             Controller.Switch(mSwcopy) = True
  2263.         End If
  2264.     End Sub
  2265.  
  2266.     Public Sub CreateEvents(aName)
  2267.         If vpmCheckEvent(aName, Me) Then
  2268.             If Not mTrig Is Nothing Then
  2269.                 vpmBuildEvent mTrig, "Hit", aName & ".TrigHit ActiveBall"
  2270.                 vpmBuildEvent mTrig, "UnHit", aName & ".TrigHit 0"
  2271.             End If
  2272.             vpmBuildEvent mWall, "Hit", aName & ".BallHit ActiveBall"
  2273.             vpmBuildEvent mKickers(mKickNo), "Hit", aName & ".BallReturn Me"
  2274.         End If
  2275.     End Sub
  2276.     ' Obsolete
  2277.     Public BallImage, BallColour
  2278. End Class
  2279.  
  2280. '--------------------
  2281. '   Visible Locks
  2282. '--------------------
  2283. Class cvpmVLock
  2284.     Private mTrig, mKick, mSw(), mSize, mBalls, mGateOpen, mRealForce, mBallSnd, mNoBallSnd
  2285.     Public ExitDir, ExitForce, KickForceVar
  2286.  
  2287.     Private Sub Class_Initialize
  2288.         mBalls = 0 : ExitDir = 0 : ExitForce = 0 : KickForceVar = 0 : mGateOpen = False
  2289.         vpmTimer.addResetObj Me
  2290.     End Sub
  2291.  
  2292.     Public Sub InitVLock(aTrig, aKick, aSw)
  2293.         Dim ii
  2294.         mSize = vpmSetArray(mTrig, aTrig)
  2295.         If vpmSetArray(mKick, aKick) <> mSize Then MsgBox "cvpmVLock: Unmatched kick+trig" : Exit Sub
  2296.         On Error Resume Next
  2297.         ReDim mSw(mSize)
  2298.         If IsArray(aSw) Then
  2299.             For ii = 0 To UBound(aSw) : mSw(ii) = aSw(ii) : Next
  2300.         ElseIf aSw = 0 Or Err Then
  2301.             For ii = 0 To mSize: mSw(ii) = mTrig(ii).TimerInterval : Next
  2302.         Else
  2303.             mSw(0) = aSw
  2304.         End If
  2305.     End Sub
  2306.  
  2307.     Public Sub InitSnd(aBall, aNoBall) : mBallSnd = aBall : mNoBallSnd = aNoBall : End Sub
  2308.     Public Sub CreateEvents(aName)
  2309.         Dim ii
  2310.         If Not vpmCheckEvent(aName, Me) Then Exit Sub
  2311.         For ii = 0 To mSize
  2312.             vpmBuildEvent mTrig(ii), "Hit", aName & ".TrigHit ActiveBall," & ii+1
  2313.             vpmBuildEvent mTrig(ii), "Unhit", aName & ".TrigUnhit ActiveBall," & ii+1
  2314.             vpmBuildEvent mKick(ii), "Hit", aName & ".KickHit " & ii+1
  2315.         Next
  2316.     End Sub
  2317.  
  2318.     Public Sub SolExit(aEnabled)
  2319.         Dim ii, mSwcopy
  2320.         mGateOpen = aEnabled
  2321.         If Not aEnabled Then Exit Sub
  2322.         If mBalls > 0 Then PlaySound mBallSnd : Else PlaySound mNoBallSnd : Exit Sub
  2323.         For ii = 0 To mBalls-1
  2324.             mKick(ii).Enabled = False
  2325.             If mSw(ii) Then
  2326.                 mSwcopy = mSw(ii)
  2327.                 Controller.Switch(mSwcopy) = False
  2328.             End If
  2329.         Next
  2330.         If ExitForce > 0 Then ' Up
  2331.             mRealForce = ExitForce + (Rnd - 0.5)*KickForceVar : mKick(mBalls-1).Kick ExitDir, mRealForce
  2332.         Else ' Down
  2333.             mKick(0).Kick 0, 0
  2334.         End If
  2335.     End Sub
  2336.  
  2337.     Public Sub Reset
  2338.         Dim mSwcopy
  2339.         Dim ii : If mBalls = 0 Then Exit Sub
  2340.         For ii = 0 To mBalls-1
  2341.             If mSw(ii) Then
  2342.                 mSwcopy = mSw(ii)
  2343.                 Controller.Switch(mSwcopy) = True
  2344.             End If
  2345.         Next
  2346.     End Sub
  2347.  
  2348.     Public Property Get Balls : Balls = mBalls : End Property
  2349.  
  2350.     Public Property Let Balls(aBalls)
  2351.         Dim mSwcopy
  2352.         Dim ii : mBalls = aBalls
  2353.         For ii = 0 To mSize
  2354.             mSwcopy = mSw(ii)
  2355.             If ii >= aBalls Then
  2356.                 mKick(ii).DestroyBall : If mSwcopy Then Controller.Switch(mSwcopy) = False
  2357.             Else
  2358.                 vpmCreateBall mKick(ii) : If mSwcopy Then Controller.Switch(mSwcopy) = True
  2359.             End If
  2360.         Next
  2361.     End Property
  2362.  
  2363.     Public Sub TrigHit(aBall, aNo)
  2364.         Dim mSwcopy
  2365.         aNo = aNo - 1
  2366.         If mSw(aNo) Then
  2367.             mSwcopy = mSw(aNo)
  2368.             Controller.Switch(mSwcopy) = True
  2369.         End If
  2370.         If aBall.VelY < -1 Then Exit Sub ' Allow small upwards speed
  2371.         If aNo = mSize Then mBalls = mBalls + 1
  2372.         If mBalls > aNo Then mKick(aNo).Enabled = Not mGateOpen
  2373.     End Sub
  2374.  
  2375.     Public Sub TrigUnhit(aBall, aNo)
  2376.         Dim mSwcopy
  2377.         aNo = aNo - 1
  2378.         If mSw(aNo) Then
  2379.             mSwcopy = mSw(aNo)
  2380.             Controller.Switch(mSwcopy) = False
  2381.         End If
  2382.         If aBall.VelY > -1 Then
  2383.             If aNo = 0 Then mBalls = mBalls - 1
  2384.             If aNo < mSize Then mKick(aNo+1).Kick 0, 0
  2385.         Else
  2386.             If aNo = mSize Then mBalls = mBalls - 1
  2387.             If aNo > 0 Then mKick(aNo-1).Kick ExitDir, mRealForce
  2388.         End If
  2389.     End Sub
  2390.  
  2391.     Public Sub KickHit(aNo) : mKick(aNo-1).Enabled = False : End Sub
  2392. End Class
  2393.  
  2394. '--------------------
  2395. '   View Dips
  2396. '--------------------
  2397. Class cvpmDips
  2398.     Private mLWF, mChkCount, mOptCount, mItems()
  2399.  
  2400.     Private Sub Class_Initialize
  2401.         ReDim mItems(100)
  2402.     End Sub
  2403.  
  2404.     Private Sub addChkBox(aType, aLeft, aTop, aWidth, aNames)
  2405.         Dim ii, obj
  2406.         If Not isObject(mLWF) Then Exit Sub
  2407.         For ii = 0 To UBound(aNames) Step 2
  2408.             Set obj = mLWF.AddCtrl("chkBox", 10+aLeft, 5+aTop+ii*7, aWidth, 14, aNames(ii))
  2409.             mChkCount = mChkCount + 1 : mItems(mChkCount+mOptCount) = Array(aType, obj, mChkCount, aNames(ii+1), aNames(ii+1))
  2410.         Next
  2411.     End Sub
  2412.  
  2413.     Private Sub addOptBox(aType, aLeft, aTop, aWidth, aHeading, aMask, aNames)
  2414.         Dim ii, obj
  2415.         If Not isObject(mLWF) Then Exit Sub
  2416.         mLWF.AddCtrl "Frame", 10+aLeft, 5+aTop, 10+aWidth, 7*UBound(aNames)+25, aHeading
  2417.         If aMask Then
  2418.             For ii = 0 To UBound(aNames) Step 2
  2419.                 Set obj = mLWF.AddCtrl("OptBtn", 10+aLeft+5, 5+aTop+ii*7+14, aWidth, 14, aNames(ii))
  2420.                 mOptCount = mOptCount + 1 : mItems(mChkCount+mOptCount) = Array(aType+2,obj,mOptCount,aNames(ii+1),aMask)
  2421.             Next
  2422.         Else
  2423.             addChkBox aType, 5+aLeft, 15+aTop, aWidth, aNames
  2424.         End If
  2425.     End Sub
  2426.  
  2427.     Public Sub addForm(ByVal aWidth, aHeight, aName)
  2428.         If aWidth < 80 Then aWidth = 80
  2429.         On Error Resume Next
  2430.         Set mLWF = CreateObject("VPinMAME.WSHDlg") : If Err Then Exit Sub
  2431.         With mLWF
  2432.             .x = -1 : .y = -1 ' : .w = aWidth : .h = aHeight+60
  2433.             .Title = aName : .AddCtrl "OKBtn", -1, -1, 70, 25, "&Ok"
  2434.         End With
  2435.         mChkCount = 0 : mOptCount = 0
  2436.     End Sub
  2437.  
  2438.     Public Sub addChk(aLeft, aTop, aWidth, aNames)
  2439.         addChkBox 0, aLeft, aTop, aWidth, aNames
  2440.     End Sub
  2441.     Public Sub addChkExtra(aLeft, aTop, aWidth, aNames)
  2442.         addChkBox 1, aLeft, aTop, aWidth, aNames
  2443.     End Sub
  2444.     Public Sub addFrame(aLeft, aTop, aWidth, aHeading, aMask, aNames)
  2445.         addOptBox 0, aLeft, aTop, aWidth, aHeading, aMask, aNames
  2446.     End Sub
  2447.     Public Sub addFrameExtra(aLeft, aTop, aWidth, aHeading, aMask, aNames)
  2448.         addOptBox 1, aLeft, aTop, aWidth, aHeading, aMask, aNames
  2449.     End Sub
  2450.  
  2451.     Public Sub addLabel(aLeft, aTop, aWidth, aHeight, aCaption)
  2452.         If Not isObject(mLWF) Then Exit Sub
  2453.         mLWF.AddCtrl "Label", 10+aLeft, 5+aTop, aWidth, aHeight, aCaption
  2454.     End Sub
  2455.  
  2456.     Public Sub viewDips : viewDipsExtra 0 : End Sub
  2457.     Public Function viewDipsExtra(aExtra)
  2458.         Dim dips(1), ii, useDip
  2459.         If Not isObject(mLWF) Then Exit Function
  2460.         With Controller
  2461.             dips(0) = .Dip(0) + .Dip(1)*256 + .Dip(2)*65536 + (.Dip(3) And &H7f)*&H1000000
  2462.             If .Dip(3) And &H80 Then dips(0) = dips(0) Or &H80000000 'workaround for overflow error
  2463.         End With
  2464.         useDip = False : dips(1) = aExtra
  2465.         For ii = 1 To mChkCount + mOptCount
  2466.             mItems(ii)(1).Value = -((dips(mItems(ii)(0) And &H01) And mItems(ii)(4)) = mItems(ii)(3))
  2467.             If (mItems(ii)(0) And &H01) = 0 Then useDip = True
  2468.         Next
  2469.         mLWF.Show GetPlayerHWnd
  2470.         dips(0) = 0 : dips(1) = 0
  2471.         For ii = 1 To mChkCount + mOptCount
  2472.             If mItems(ii)(1).Value Then dips(mItems(ii)(0) And &H01) = dips(mItems(ii)(0) And &H01) Or mItems(ii)(3)
  2473.         Next
  2474.         If useDip Then
  2475.             With Controller
  2476.                 .Dip(0) =  (dips(0) And 255)
  2477.                 .Dip(1) = ((dips(0) And 65280)\256) And 255
  2478.                 .Dip(2) = ((dips(0) And &H00ff0000)\65536) And 255
  2479.                 .Dip(3) = ((dips(0) And &Hff000000)\&H01000000) And 255
  2480.             End With
  2481.         End If
  2482.         viewDipsExtra = dips(1)
  2483.     End Function
  2484. End Class
  2485.  
  2486. '--------------------
  2487. '   Impulse Plunger
  2488. '--------------------
  2489. Class cvpmImpulseP
  2490.     Private mEnabled, mBalls, mTrigger, mEntrySnd, mExitSnd, MExitSndBall
  2491.     Public X, Y, Strength, Res, Size, Solenoid, IMPowerOut, Time, mCount, Pull, IMPowerTrans, cFactor, Auto, RandomOut, SwitchNum, SwitchOn, BallOn
  2492.  
  2493.     Private Sub Class_Initialize
  2494.         Size = 1 : Strength = 0 : Solenoid = 0 : Res = 1 : IMPowerOut = 0 : Time = 0 : mCount = 0 : mEnabled = False
  2495.         Pull = 0 : IMPowerTrans = 0 : Auto = False : RandomOut = 0 : SwitchOn = 0 : SwitchNum = 0 : BallOn = 0
  2496.         Set mBalls = New cvpmDictionary
  2497.     End Sub
  2498.  
  2499.     Private Property Let NeedUpdate(aEnabled) : vpmTimer.EnableUpdate Me, True, aEnabled : End Property
  2500.  
  2501.     Public Sub InitImpulseP(aTrigger, aStrength, aTime)
  2502.         Dim tmp
  2503.         If vpmIsArray(aTrigger) Then Set tmp = aTrigger(0) Else Set tmp = aTrigger
  2504.         X = tmp.X : Y = tmp.Y : Size = tmp.Radius : vpmTimer.InitTimer tmp, True
  2505.         If IsArray(aTrigger) Then mTrigger = aTrigger Else Set mTrigger = aTrigger
  2506.         Strength = aStrength
  2507.         Res = 500
  2508.         Time = aTime
  2509.         If aTime = 0 Then
  2510.             Auto = True
  2511.         Else
  2512.             cFactor = (Res / Time) / 100
  2513.             Auto = False
  2514.         End If
  2515.     End Sub
  2516.  
  2517.     Public Sub CreateEvents(aName)
  2518.         If vpmCheckEvent(aName, Me) Then
  2519.             vpmBuildEvent mTrigger, "Hit", aName & ".AddBall ActiveBall"
  2520.             vpmBuildEvent mTrigger, "UnHit", aName & ".RemoveBall ActiveBall"
  2521.         End If
  2522.     End Sub
  2523.  
  2524.  
  2525.     Public Property Let PlungeOn(aEnabled) : mEnabled = aEnabled : End Property
  2526.     Public Property Get PlungeOn
  2527.         If Solenoid > 0 Then PlungeOn = Controller.Solenoid(Solenoid) Else PlungeOn = mEnabled
  2528.     End Property
  2529.  
  2530.     Public Sub AddBall(aBall)
  2531.         Dim mSwcopy
  2532.         With mBalls
  2533.             If .Exists(aBall) Then .Item(aBall) = .Item(aBall) + 1 Else .Add aBall, 1 : NeedUpdate = True
  2534.         End With
  2535.         If SwitchOn = True Then
  2536.             mSwcopy = SwitchNum
  2537.             Controller.Switch(mSwcopy) = 1
  2538.         End If
  2539.         BallOn = 1
  2540.     End Sub
  2541.  
  2542.     Public Sub RemoveBall(aBall)
  2543.         Dim mSwcopy
  2544.         With mBalls
  2545.             If .Exists(aBall) Then .Item(aBall) = .Item(aBall) - 1 : If .Item(aBall) <= 0 Then .Remove aBall
  2546.             NeedUpdate = (.Count > 0)
  2547.         End With
  2548.         If SwitchOn = True Then
  2549.             mSwcopy = SwitchNum
  2550.             Controller.Switch(mSwcopy) = 0
  2551.         End If
  2552.         BallOn = 0
  2553.     End Sub
  2554.  
  2555.     Public Property Get Balls : Balls = mBalls.Keys : End Property
  2556.  
  2557.     Public Sub Update
  2558.         Dim obj
  2559.         If pull = 1 and mCount < Res Then
  2560.             mCount = mCount + cFactor
  2561.             IMPowerTrans = mCount
  2562.             NeedUpdate = True
  2563.         Else
  2564.             IMPowerTrans = mCount
  2565.             NeedUpdate = False
  2566.         End If
  2567.         If PlungeOn Then
  2568.             On Error Resume Next
  2569.             For Each obj In mBalls.Keys
  2570.                 If obj.X < 0 Or Err Then : mBalls.Remove obj : Else : PlungeBall obj : End If
  2571.             Next
  2572.             On Error Goto 0
  2573.         End If
  2574.     End Sub
  2575.  
  2576.     Public Sub PlungeBall(aBall)
  2577.             aBall.VelY = IMPowerOut
  2578.     End Sub
  2579.  
  2580.     Public Sub Random(aInput) ' Random Output Varience
  2581.         RandomOut = aInput
  2582.     End Sub
  2583.  
  2584.     Public Sub Fire           ' Resets System and Transfer Power Value
  2585.         If Auto = True Then
  2586.         IMPowerOut = -Strength + ((Rnd) * RandomOut)
  2587.         Else
  2588.         IMPowerOut = -Strength * (IMPowerTrans + ((Rnd-0.5) * cFactor * RandomOut)) / Res
  2589.         End If
  2590.         PlungeOn = True
  2591.         Update
  2592.         PlungeOn = False
  2593.         Pull = 0 : IMPowerOut = 0 : IMPowerTrans = 0 : mCount = 0
  2594.         If BallOn = 1 Then : PlaySound mExitSndBall : Else : PlaySound mExitSnd : End If
  2595.     End Sub
  2596.  
  2597.     Public Sub AutoFire   ' Auto-Fire Specific Call (so you don't have to change timing)
  2598.         IMPowerOut = -Strength + ((Rnd) * RandomOut)
  2599.         PlungeOn = True
  2600.         Update
  2601.         PlungeOn = False
  2602.         Pull = 0 : IMPowerOut = 0 : IMPowerTrans = 0 : mCount = 0
  2603.         If BallOn = 1 Then : PlaySound mExitSndBall : Else : PlaySound mExitSnd : End If
  2604.     End Sub
  2605.    
  2606.     Public Sub Pullback     ' Pull Plunger
  2607.         Pull = 0 : IMPowerOut = 0 : IMPowerTrans = 0 : mCount = 0 ' reinitialize to be sure
  2608.         Pull = 1 : NeedUpdate = True
  2609.             PlaySound mEntrySnd
  2610.     End Sub
  2611.    
  2612.     Public Sub Switch(aSw)
  2613.         SwitchOn = True
  2614.         SwitchNum = aSw
  2615.     End Sub
  2616.    
  2617.     Public Sub InitEntrySnd(aNoBall) : mEntrySnd = aNoBall : End Sub
  2618.     Public Sub InitExitSnd(aBall, aNoBall)  : mExitSndBall = aBall  : mExitSnd = aNoBall  : End Sub
  2619. End Class
  2620.  
  2621. Set vpmTimer = New cvpmTimer
  2622. If LoadScript("NudgePlugIn.vbs") Then Set vpmNudge = New cvpmNudge2 Else Set vpmNudge = New cvpmNudge
  2623.  
  2624. '-------------
  2625. 'cvpmFlips (FastFlips)
  2626. '-------------
  2627. dim vpmFlips : set vpmFlips = New cvpmFlips : vpmFlips.Name = "vpmFlips"
  2628.  
  2629. '*************************************************
  2630. Sub InitVpmFlips() 'Called from vpmInit
  2631.     if not UseSolenoids > 1 then exit sub
  2632.     On Error Resume Next
  2633.         if UseSolenoids > 2 then vpmFlips.Solenoid = UseSolenoids else vpmFlips.Solenoid = GameOnSolenoid End If
  2634.     On Error Goto 0
  2635.     vpmFlips.DebugTestInit = True
  2636.  
  2637.     if not IsEmpty(SolCallback(sLLFlipper)) then vpmFlips.CallBackL = SolCallback(sLLFlipper)   'Lower Flippers
  2638.     if not IsEmpty(SolCallback(sLRFlipper)) then vpmFlips.CallBackR = SolCallback(sLRFlipper)
  2639.     if not IsEmpty(SolCallback(sULFlipper)) then vpmFlips.CallBackUL = SolCallback(sULFlipper)  'Upper Flippers
  2640.     if not IsEmpty(SolCallback(sURFlipper)) then vpmFlips.CallBackUR = SolCallback(sURFlipper)
  2641. End Sub
  2642. Function NullFunction(aEnabled):End Function    '1 argument null function
  2643.  
  2644. Class cvpmFlips
  2645.     Public TiltObjects, DebugOn, Name, Delay
  2646.     private SubL, SubUL, SubR, SubUR, FlippersEnabled,  LagCompensation, FlipState(3), Sol  'set private
  2647.    
  2648.     Private Sub Class_Initialize()
  2649.         dim x : for x = 0 to 3 : flipstate(x) = False : Next
  2650.         Delay = 0 : FlippersEnabled = False : DebugOn = False : LagCompensation = False : Sol = 0 : TiltObjects = True
  2651.         SubL = "NullFunction": SubR = "NullFunction" : SubUL = "NullFunction": SubUR = "NullFunction"
  2652.     End Sub
  2653.  
  2654.     public DebugTestKeys, DebugTestInit
  2655.     Public Sub DebugTest
  2656.         dim e
  2657.         if UseSolenoids = 1 then debug.print "debugtest: " & "UseSolenoids = 1, change to 2 or greater and restart to enable!" : Exit Sub
  2658.         dim a : if usesolenoids = 2 and usesolenoids <> solenoid then a = "(Auto)"
  2659.         if solenoid = 0 then a = " (undefined solenoid, automatic tilt is disabled)" : e = True
  2660.         debug.print "--DebugTest--" & vbnewline & "Game:(" & cGameName & ") Flipper Sol=" & Solenoid & a & " Flippers on?: " & FlippersEnabled
  2661.         if not DebugTestInit then debug.print "Init error! Please add 'vpmInit me' to the table1_init section of the table script!" : exit sub
  2662.         if not FlippersEnabled and Controller.Solenoid(solenoid) and not DebugOn then debug.print "Tiltsol problem, flippers should be on right now!" : e = True
  2663.  
  2664.         if IsEmpty(keyStagedFlipperL) then debug.print "keyStagedFlipperL is empty or undefined (check vpmkeys.vbs)" : e = True
  2665.         if IsEmpty(keyStagedFlipperR) then debug.print "keyStagedFlipperR is empty or undefined (check vpmkeys.vbs)" : e = True
  2666.         if IsEmpty(DebugTestKeys) then debug.print "no flipper key inputs detected (press some buttons, or check system vbs!)"
  2667.         if e then debug.print "--errors detected!--"
  2668.     End Sub
  2669.     'debug for finding sols 'vpmFlips.PrintSols
  2670.     Public Sub PrintSols() : Dim x, sols: sols=controller.solenoids: for x= 0 to uBound(sols) : if sols(x) then debug.print x & ":" & sols(x) end if : Next : End Sub   'debug for finding sols
  2671.  
  2672.     'set callbacks
  2673.     Public Property Let CallBackL(aInput)  : SubL  = aInput : SolCallback(sLLFlipper) = Empty: End Property 'execute
  2674.     Public Property Let CallBackUL(aInput) : SubUL = aInput : SolCallback(sULFlipper) = Empty: End Property
  2675.     Public Property Let CallBackR(aInput)  : SubR  = aInput : SolCallback(sLRFlipper) = Empty: End Property
  2676.     Public Property Let CallBackUR(aInput) : SubUR = aInput : SolCallback(sURFlipper) = Empty: End Property
  2677.    
  2678.     'Automatically decouple flipper solcallback script lines (only if both are pointing to the same sub) thanks gtxjoe
  2679.     Private Sub Decouple(aSolType, aInput)  : If StrComp(SolCallback(aSolType),aInput,1) = 0 then SolCallback(aSolType) = Empty End If : End Sub
  2680.     Public Property Let Solenoid(aInput) : if not IsEmpty(aInput) then Sol = aInput : end if : End Property 'set solenoid
  2681.     Public Property Get Solenoid : Solenoid = sol : End Property
  2682.    
  2683.     'call callbacks
  2684.     Public Sub FlipL(aEnabled)
  2685.         DebugTestKeys = True
  2686.         FlipState(0) = aEnabled 'track flipper button states: the game-on sol flips immediately if the button is held down (1.1)
  2687.         If not FlippersEnabled and not DebugOn then Exit Sub
  2688.         execute subL & " " & aEnabled
  2689.     End Sub
  2690.  
  2691.     Public Sub FlipR(aEnabled)
  2692.         DebugTestKeys = True
  2693.         FlipState(1) = aEnabled
  2694.         If not FlippersEnabled and not DebugOn then Exit Sub
  2695.         execute subR & " " & aEnabled
  2696.     End Sub
  2697.  
  2698.     Public Sub FlipUL(aEnabled)
  2699.         FlipState(2) = aEnabled
  2700.         If not FlippersEnabled and not DebugOn then Exit Sub
  2701.         execute subUL & " " & aEnabled
  2702.     End Sub
  2703.  
  2704.     Public Sub FlipUR(aEnabled)
  2705.         FlipState(3) = aEnabled
  2706.         If not FlippersEnabled and not DebugOn then Exit Sub
  2707.         execute subUR & " " & aEnabled
  2708.     End Sub
  2709.    
  2710.     Public Sub TiltSol(aEnabled)    'Handle solenoid / Delay (if delayinit)
  2711.         If delay > 0 and not aEnabled then  'handle delay
  2712.             vpmtimer.addtimer Delay, Name & ".FireDelay" & "'"
  2713.             LagCompensation = True
  2714.         else
  2715.             If Delay > 0 then LagCompensation = False
  2716.             EnableFlippers(aEnabled)
  2717.         end If
  2718.     End Sub
  2719.    
  2720.     Sub FireDelay() : If LagCompensation then EnableFlippers False End If : End Sub
  2721.    
  2722.     Public Sub EnableFlippers(aEnabled) 'private
  2723.         If aEnabled then execute SubL & " " & FlipState(0) : execute SubR & " " & FlipState(1) : execute subUL & " " & FlipState(2) : execute subUR & " " & FlipState(3)
  2724.         FlippersEnabled = aEnabled
  2725.         If TiltObjects then vpmnudge.solgameon aEnabled
  2726.         If Not aEnabled then
  2727.             execute subL & " " & False
  2728.             execute subR & " " & False
  2729.             execute subUL & " " & False
  2730.             execute subUR & " " & False
  2731.         End If
  2732.     End Sub
  2733. End Class
  2734.  
  2735.  
  2736. '---------------------------
  2737. ' Check VP version running
  2738. '---------------------------
  2739. Private Function vpmCheckVPVer
  2740.     On Error Resume Next
  2741.     ' a bug in VBS?: Err object is not cleared on Exit Function
  2742.     If VPBuildVersion < 0 Or Err Then vpmCheckVPVer = 50 : Err.Clear : Exit Function
  2743.     If VPBuildVersion > 2806 and VPBuildVersion < 9999 Then
  2744.         vpmCheckVPVer = 63
  2745.     ElseIf VPBuildVersion > 2721 and VPBuildVersion < 9999 Then
  2746.         vpmCheckVPVer = 61
  2747.     ElseIf VPBuildVersion >= 900 and VPBuildVersion <= 999 Then
  2748.         vpmCheckVPVer = 90
  2749.     ElseIf VPBuildVersion >= 10000 Then
  2750.         vpmCheckVPVer = 100
  2751.     Else
  2752.         vpmCheckVPVer = 60
  2753.     End If
  2754. End Function
  2755. Private vpmVPVer : vpmVPVer = vpmCheckVPVer()
  2756. '--------------------
  2757. ' Initialise timers
  2758. '--------------------
  2759. Sub PulseTimer_Init   : vpmTimer.InitTimer Me, False : End Sub
  2760. Sub PinMAMETimer_Init : Me.Interval = PinMAMEInterval : Me.Enabled = True : End Sub
  2761.  
  2762. '---------------------------------------------
  2763. ' Init function called from Table_Init event
  2764. '---------------------------------------------
  2765. Public Sub vpmInit(aTable)
  2766.     Set vpmTable = aTable
  2767.     If vpmVPVer >= 60 Then
  2768.       On Error Resume Next
  2769.         If Not IsObject(GetRef(aTable.name & "_Paused")) Or Err Then Err.Clear : vpmBuildEvent aTable, "Paused", "Controller.Pause = True"
  2770.         If Not IsObject(GetRef(aTable.name & "_UnPaused")) Or Err Then Err.Clear : vpmBuildEvent aTable, "UnPaused", "Controller.Pause = False"
  2771.         If Not IsObject(GetRef(aTable.name & "_Exit")) Or Err Then Err.Clear : vpmBuildEvent aTable, "Exit", "Controller.Pause = False:Controller.Stop"
  2772.     End If
  2773.     if UseModSol Then
  2774.         If Controller.Version >= 02080000 Then
  2775.           Controller.SolMask(2)=1
  2776.         Else
  2777.           MsgBox "Modulated Flashers/Solenoids not supported with this Visual PinMAME version (2.8 or newer is required)"
  2778.         End If
  2779.     End If
  2780.     InitVpmFlips
  2781. End Sub
  2782.  
  2783. ' Exit function called in Table_Exit event
  2784. Public Sub vpmExit : End Sub
  2785. '------------------------------------------------------
  2786. ' All classes call this function to create a ball
  2787. ' Assign vpmCreateBall if you want a custom function
  2788. '------------------------------------------------------
  2789. Private Function vpmDefCreateBall(aKicker)
  2790.     If Not IsEmpty(vpmBallImage) Then aKicker.Createball.Image = vpmBallImage Else aKicker.Createball : End If
  2791.     Set vpmDefCreateBall = aKicker
  2792. End Function
  2793.  
  2794. Private Function vpmDefCreateBall2(aKicker)
  2795.     If Not IsEmpty(vpmBallImage) Then aKicker.Createsizedball(BSize).Image = vpmBallImage Else aKicker.Createsizedball(BSize) : End If
  2796.     Set vpmDefCreateBall2 = aKicker
  2797. End Function
  2798.  
  2799. Private Function vpmDefCreateBall3(aKicker)
  2800.     If Not IsEmpty(vpmBallImage) Then
  2801.         aKicker.CreateSizedBallWithMass(BSize,BMass).Image = vpmBallImage
  2802.     Else
  2803.         aKicker.CreateSizedBallWithMass BSize,BMass ' for whatever reason it doesn't work if using ()
  2804.     End If
  2805.     Set vpmDefCreateBall3 = aKicker
  2806. End Function
  2807.  
  2808. If VPBuildVersion >= 10000 Then
  2809.     Set vpmCreateBall = GetRef("vpmDefCreateBall3")
  2810. ElseIf VPBuildVersion > 909 And vpmVPVer >= 90 Then
  2811.         Set vpmCreateBall = GetRef("vpmDefCreateBall2")
  2812. Else
  2813.     Set vpmCreateBall = GetRef("vpmDefCreateBall")
  2814. End If
  2815.  
  2816. Private vpmTrough ' Default Trough. Used to clear up missing balls
  2817. Private vpmTable  ' Table object
  2818.  
  2819. '-------------------
  2820. ' Main Loop
  2821. '------------------
  2822. Private Const CHGNO = 0
  2823. Private Const CHGSTATE = 1
  2824. Private vpmTrueFalse : vpmTrueFalse = Array(" True", " False"," True")
  2825.  
  2826. Sub vpmDoSolCallback(aNo, aEnabled)
  2827.     If SolCallback(aNo) <> "" Then Execute SolCallback(aNo) & vpmTrueFalse(aEnabled+1)
  2828. End Sub
  2829.  
  2830. Sub vpmDoLampUpdate(aNo, aEnabled)
  2831.     On Error Resume Next : Lights(aNo).State = Abs(aEnabled)
  2832. End Sub
  2833.  
  2834. Sub PinMAMETimer_Timer
  2835.     Dim ChgLamp,ChgSol,ChgGI, ii, tmp, idx, nsol, solon
  2836.     Dim DMDp
  2837.     Dim ChgNVRAM
  2838.  
  2839.     'Me.Enabled = False 'this was supposed to be some kind of weird mutex, disable it
  2840.  
  2841.     On Error Resume Next
  2842.         If UseDMD Then
  2843.             DMDp = Controller.RawDmdPixels
  2844.             If Not IsEmpty(DMDp) Then
  2845.                 DMDWidth = Controller.RawDmdWidth
  2846.                 DMDHeight = Controller.RawDmdHeight
  2847.                 DMDPixels = DMDp
  2848.             End If
  2849.         ElseIf UseColoredDMD Then
  2850.             DMDp = Controller.RawDmdColoredPixels
  2851.             If Not IsEmpty(DMDp) Then
  2852.                 DMDWidth = Controller.RawDmdWidth
  2853.                 DMDHeight = Controller.RawDmdHeight
  2854.                 DMDColoredPixels = DMDp
  2855.             End If
  2856.         End If
  2857.         If UseNVRAM Then
  2858.             If isObject(NVRAMCallback) Then
  2859.                 ChgNVRAM = Controller.ChangedNVRAM 'Controller.NVRAM would deliver everything of the NVRAM all the time as 1D array
  2860.                 If(Not IsEmpty(ChgNVRAM)) Then NVRAMCallback ChgNVRAM
  2861.             End If
  2862.         End If
  2863.         If UseLamps Then ChgLamp = Controller.ChangedLamps Else LampCallback
  2864.         If UseSolenoids Then ChgSol = Controller.ChangedSolenoids
  2865.         If isObject(GICallback) or isObject(GICallback2) Then ChgGI = Controller.ChangedGIStrings
  2866.         MotorCallback
  2867.     On Error Goto 0
  2868.     If Not IsEmpty(ChgLamp) Then
  2869.         On Error Resume Next
  2870.             For ii = 0 To UBound(ChgLamp)
  2871.                 idx = chgLamp(ii, 0)
  2872.                 If IsArray(Lights(idx)) Then
  2873.                     For Each tmp In Lights(idx) : tmp.State = ChgLamp(ii, 1) : Next
  2874.                 Else
  2875.                     Lights(idx).State = ChgLamp(ii, 1)
  2876.                 End If
  2877.             Next
  2878.             For Each tmp In vpmMultiLights
  2879.                 For ii = 1 To UBound(tmp) : tmp(ii).State = tmp(0).State : Next
  2880.             Next
  2881.             LampCallback
  2882.         On Error Goto 0
  2883.     End If
  2884.     If Not IsEmpty(ChgSol) Then
  2885.         For ii = 0 To UBound(ChgSol)
  2886.             nsol = ChgSol(ii, 0)
  2887.             tmp = SolCallback(nsol)
  2888.             solon = ChgSol(ii, 1)
  2889.             If solon > 1 Then solon = 1
  2890.             If UseModSol Then
  2891.                 If solon <> SolPrevState(nsol) Then
  2892.                     SolPrevState(nsol) = solon
  2893.                     If tmp <> "" Then Execute tmp & vpmTrueFalse(solon+1)
  2894.                 End If
  2895.                 tmp = SolModCallback(nsol)
  2896.                 If tmp <> "" Then Execute tmp & " " & ChgSol(ii, 1)
  2897.             Else
  2898.                 If tmp <> "" Then Execute tmp & vpmTrueFalse(solon+1)
  2899.             End If
  2900.         if UseSolenoids > 1 then if nsol = vpmFlips.Solenoid then vpmFlips.TiltSol cbool(solon)
  2901.         Next
  2902.     End If
  2903.     If Not IsEmpty(ChgGI) Then
  2904.         For ii = 0 To UBound(ChgGI)
  2905.             GICallback ChgGI(ii, 0), CBool(ChgGI(ii, 1))
  2906.             GICallback2 ChgGI(ii, 0), ChgGI(ii, 1)
  2907.         Next
  2908.     End If
  2909.  
  2910.     'Me.Enabled = True 'this was supposed to be some kind of weird mutex, disable it
  2911. End Sub
  2912.  
  2913. '
  2914. ' Private helper functions
  2915. '
  2916. Private Sub vpmPlaySound(aEnabled, aSound)
  2917.     If VarType(aSound) = vbString Then
  2918.         If aEnabled Then StopSound aSound : PlaySound aSound
  2919.     ElseIf aSound Then
  2920.         If aEnabled Then PlaySound SSolenoidOn Else PlaySound SSolenoidOff
  2921.     End If
  2922. End Sub
  2923.  
  2924. Private Sub vpmToggleObj(aObj, aEnabled)
  2925.     Dim mSwcopy
  2926.     Select Case TypeName(aObj)
  2927.         Case "Wall"                        aObj.IsDropped = aEnabled
  2928.         Case "Bumper", "Light"             aObj.State     = Abs(aEnabled)
  2929.         Case "Kicker", "Trigger", "Timer"  aObj.Enabled   = aEnabled
  2930.         Case "Gate"                        aObj.Open      = aEnabled
  2931.         Case "Integer"                     mSwcopy = aObj : Controller.Switch(mSwcopy) = aEnabled
  2932.         Case Else MsgBox "vpmToggleObj: Unhandled Object " & TypeName(aObj)
  2933.     End Select
  2934. End Sub
  2935.  
  2936. Private Function vpmCheckEvent(aName, aObj)
  2937.     vpmCheckEvent = True
  2938.     On Error Resume Next
  2939.     If Not Eval(aName) Is aObj Or Err Then MsgBox "CreateEvents: Wrong name " & aName : vpmCheckEvent = False
  2940. End Function
  2941.  
  2942. Private Sub vpmBuildEvent(aObj, aEvent, aTask)
  2943.     Dim obj, str
  2944.     str = "_" & aEvent & " : " & aTask & " : End Sub"
  2945.     If vpmIsArray(aObj) Then
  2946.         For Each obj In aObj : ExecuteGlobal "Sub " & obj.Name & str : Next
  2947.     Else
  2948.         ExecuteGlobal "Sub " & aObj.Name & str
  2949.     End If
  2950. End Sub
  2951.  
  2952. Private Function vpmIsCollection(aObj)
  2953.     vpmIsCollection =  TypeName(aObj) = "Collection" Or TypeName(aObj) = "ICollection"
  2954. End Function
  2955. Private Function vpmIsArray(aObj)
  2956.     vpmIsArray = IsArray(aObj) Or vpmIsCollection(aObj)
  2957. End Function
  2958.  
  2959. Private Function vpmSetArray(aTo, aFrom)
  2960.     If IsArray(aFrom) Then
  2961.         aTo = aFrom : vpmSetArray = UBound(aFrom)
  2962.     ElseIf vpmIsCollection(aFrom) Then
  2963.         Set aTo = aFrom : vpmSetArray = aFrom.Count - 1
  2964.     Else
  2965.         aTo = Array(aFrom) : vpmSetArray = 0
  2966.     End If
  2967. End Function
  2968.  
  2969. Sub vpmCreateEvents(aHitObjs)
  2970.     Dim obj
  2971.     For Each obj In aHitObjs
  2972.         Select Case TypeName(obj)
  2973.             Case "Trigger"
  2974.                 vpmBuildEvent obj, "Hit", "Controller.Switch(" & Obj.TimerInterval & ") = True"
  2975.                 vpmBuildEvent obj, "UnHit", "Controller.Switch(" & Obj.TimerInterval & ") = False"
  2976.             Case "Wall"
  2977.                 If obj.HasHitEvent Then
  2978.                     vpmBuildEvent obj, "Hit", "vpmTimer.PulseSw " & Obj.TimerInterval
  2979.                 Else
  2980.                     vpmBuildEvent obj, "SlingShot", "vpmTimer.PulseSw " & Obj.TimerInterval
  2981.                 End If
  2982.             Case "Bumper", "Gate"
  2983.                 vpmBuildEvent obj, "Hit","vpmTimer.PulseSw " & Obj.TimerInterval
  2984.             Case "Spinner"
  2985.                 vpmBuildEvent obj, "Spin","vpmTimer.PulseSw " & Obj.TimerInterval
  2986.         End Select
  2987.     Next
  2988. End Sub
  2989.  
  2990. Sub vpmMapLights(aLights)
  2991.     Dim obj, str, ii, idx
  2992.     For Each obj In aLights
  2993.         idx = obj.TimerInterval
  2994.         If IsArray(Lights(idx)) Then
  2995.             str = "Lights(" & idx & ") = Array("
  2996.             For Each ii In Lights(idx) : str = str & ii.Name & "," : Next
  2997.             ExecuteGlobal str & obj.Name & ")"
  2998.         ElseIf IsObject(Lights(idx)) Then
  2999.             Lights(idx) = Array(Lights(idx),obj)
  3000.         Else
  3001.             Set Lights(idx) = obj
  3002.         End If
  3003.     Next
  3004. End Sub
  3005.  
  3006. Function vpmMoveBall(aBall, aFromKick, aToKick)
  3007.     With aToKick.CreateBall
  3008.         If TypeName(aBall) = "IBall" Then
  3009.             .Color = aBall.Color   : .Image = aBall.Image
  3010.             If vpmVPVer >= 60 Then
  3011.                 .FrontDecal = aBall.FrontDecal : .BackDecal = aBall.BackDecal
  3012. '               .UserValue = aBall.UserValue
  3013.             End If
  3014.         End If
  3015.     End With
  3016.     aFromKick.DestroyBall : Set vpmMoveBall = aToKick
  3017. End Function
  3018.  
  3019. Sub vpmAddBall
  3020. Dim Answer
  3021.     If IsObject(vpmTrough) Then
  3022.             Answer=MsgBox("Click YES to Add a ball to the Trough, NO Removes a ball from the Trough",vbYesNoCancel + vbQuestion)
  3023.         If Answer = vbYes Then vpmTrough.AddBall 0
  3024.         If Answer = vbNo Then vpmTrough.Balls=vpmTrough.Balls-1
  3025.     End If
  3026. End Sub
  3027.  
  3028. '----------------------------
  3029. ' Generic solenoid handlers
  3030. '----------------------------
  3031. ' ----- Flippers ------
  3032. Sub vpmSolFlipper(aFlip1, aFlip2, aEnabled)
  3033.     Dim oldStrength, oldSpeed ' only for pre-VP10
  3034.     If aEnabled Then
  3035.         PlaySound SFlipperOn : aFlip1.RotateToEnd : If Not aFlip2 Is Nothing Then aFlip2.RotateToEnd
  3036.     Else
  3037.         PlaySound SFlipperOff
  3038.         If VPBuildVersion < 10000 Then
  3039.             oldStrength = aFlip1.Strength : aFlip1.Strength = conFlipRetStrength
  3040.             oldSpeed = aFlip1.Speed : aFlip1.Speed = conFlipRetSpeed
  3041.         End If
  3042.         aFlip1.RotateToStart
  3043.         If VPBuildVersion < 10000 Then
  3044.             aFlip1.Strength = oldStrength
  3045.             aFlip1.Speed = oldSpeed
  3046.         End If
  3047.         If Not aFlip2 Is Nothing Then
  3048.             If VPBuildVersion < 10000 Then
  3049.                 oldStrength = aFlip2.Strength : aFlip2.Strength = conFlipRetStrength
  3050.                 oldSpeed = aFlip2.Speed : aFlip2.Speed = conFlipRetSpeed
  3051.             End If
  3052.             aFlip2.RotateToStart
  3053.             If VPBuildVersion < 10000 Then
  3054.                 aFlip2.Strength = oldStrength
  3055.                 aFlip2.Speed = oldSpeed
  3056.             End If
  3057.         End If
  3058.     End If
  3059. End Sub
  3060.  
  3061. ' ----- Flippers With Speed Control ------
  3062. Sub vpmSolFlip2(aFlip1, aFlip2, aFlipSpeedUp, aFlipSpeedDn, aSnd, aEnabled) ' DEPRECATED, as VP10 does not feature speed on flippers anymore
  3063.     Dim oldStrength, oldSpeed
  3064.     If aEnabled Then
  3065.         If aSnd = true then : PlaySound SFlipperOn : End If
  3066.         If Not aFlipSpeedUp = 0 Then
  3067.             aFlip1.Speed = aFlipSpeedUp
  3068.             aFlip1.RotateToEnd
  3069.         Else
  3070.             aFlip1.RotateToEnd
  3071.         End If
  3072.         If Not aFlip2 Is Nothing Then
  3073.             If Not aFlipSpeedUp = 0 Then
  3074.                 aFlip2.Speed = aFlipSpeedUp
  3075.                 aFlip2.RotateToEnd
  3076.             Else
  3077.                 aFlip2.RotateToEnd
  3078.             End If
  3079.         End If
  3080.     Else
  3081.         If aSnd = true then : PlaySound SFlipperOff : End If
  3082.         oldStrength = aFlip1.Strength
  3083.         aFlip1.Strength = conFlipRetStrength
  3084.         oldSpeed = aFlip1.Speed
  3085.         If Not aFlipSpeedDn = 0 Then
  3086.             aFlip1.Speed = aFlipSpeedDn
  3087.         Else
  3088.             aFlip1.Speed = conFlipRetSpeed
  3089.         End If
  3090.         aFlip1.RotateToStart : aFlip1.Strength = oldStrength : aFlip1.Speed = oldSpeed
  3091.         If Not aFlip2 Is Nothing Then
  3092.             oldStrength = aFlip2.Strength
  3093.             oldSpeed = aFlip2.Speed
  3094.             If Not aFlipSpeedDn = 0 Then
  3095.                 aFlip2.Speed = aFlipSpeedDn
  3096.             Else
  3097.                 aFlip2.Speed = conFlipRetSpeed
  3098.             End If
  3099.             aFlip2.Strength = conFlipRetStrength
  3100.             aFlip2.RotateToStart : aFlip2.Strength = oldStrength : aFlip2.Speed = oldSpeed
  3101.         End If
  3102.     End If
  3103. End Sub
  3104.  
  3105. ' ------ Diverters ------
  3106. Sub vpmSolDiverter(aDiv, aSound, aEnabled)
  3107.     If aEnabled Then aDiv.RotateToEnd : Else aDiv.RotateToStart
  3108.     vpmPlaySound aEnabled, aSound
  3109. End sub
  3110.  
  3111. ' ------ Walls ------
  3112. Sub vpmSolWall(aWall, aSound, aEnabled)
  3113.     Dim obj
  3114.     If vpmIsArray(aWall) Then
  3115.         For Each obj In aWall : obj.IsDropped = aEnabled : Next
  3116.     Else
  3117.         aWall.IsDropped = aEnabled
  3118.     End If
  3119.     vpmPlaySound aEnabled, aSound
  3120. End Sub
  3121.  
  3122. Sub vpmSolToggleWall(aWall1, aWall2, aSound, aEnabled)
  3123.     Dim obj
  3124.     If vpmIsArray(aWall1) Then
  3125.         For Each obj In aWall1 : obj.IsDropped = aEnabled : Next
  3126.     Else
  3127.         aWall1.IsDropped = aEnabled
  3128.     End If
  3129.     If vpmIsArray(aWall2) Then
  3130.         For Each obj In aWall2 : obj.IsDropped = Not aEnabled : Next
  3131.     Else
  3132.         aWall2.IsDropped = Not aEnabled
  3133.     End If
  3134.     vpmPlaySound aEnabled, aSound
  3135. End Sub
  3136.  
  3137. ' ------- Autoplunger ------
  3138. Sub vpmSolAutoPlunger(aPlung, aVar, aEnabled)
  3139.     Dim oldFire
  3140.     If aEnabled Then
  3141.         oldFire = aPlung.FireSpeed : aPlung.FireSpeed = oldFire * (100-aVar*(2*Rnd-1))/100
  3142.         PlaySound SSolenoidOn : aPlung.Fire : aPlung.FireSpeed = oldFire
  3143.     Else
  3144.         aPlung.Pullback
  3145.     End If
  3146. End Sub
  3147.  
  3148. ' --------Autoplunger with Specified Sound To Play ---------
  3149. Sub vpmSolAutoPlungeS(aPlung, aSound, aVar, aEnabled)
  3150.     Dim oldFire
  3151.     If aEnabled Then
  3152.         oldFire = aPlung.FireSpeed : aPlung.FireSpeed = oldFire * (100-aVar*(2*Rnd-1))/100
  3153.         PlaySound aSound : aPlung.Fire : aPlung.FireSpeed = oldFire
  3154.     Else
  3155.         aPlung.Pullback
  3156.     End If
  3157. End Sub
  3158.  
  3159. ' --------- Gate -----------
  3160. Sub vpmSolGate(aGate, aSound, aEnabled)
  3161.     Dim obj
  3162.     If vpmIsArray(aGate) Then
  3163.         For Each obj In aGate : obj.Open = aEnabled : Next
  3164.     Else
  3165.         aGate.Open = aEnabled
  3166.     End If
  3167.     vpmPlaySound aEnabled, aSound
  3168. End Sub
  3169.  
  3170. ' ------ Sound Only -------
  3171. Sub vpmSolSound(aSound, aEnabled)
  3172.     If aEnabled Then StopSound aSound : PlaySound aSound
  3173. End Sub
  3174.  
  3175. ' ------- Flashers --------
  3176. Sub vpmFlasher(aFlash, aEnabled)
  3177.     Dim obj
  3178.     If vpmIsArray(aFlash) Then
  3179.         For Each obj In aFlash : obj.State = Abs(aEnabled) : Next
  3180.     Else
  3181.         aFlash.State = Abs(aEnabled)
  3182.     End If
  3183. End Sub
  3184.  
  3185. '---- Generic object toggle ----
  3186. Sub vpmSolToggleObj(aObj1, aObj2, aSound, aEnabled)
  3187.     Dim obj
  3188.     If vpmIsArray(aObj1) Then
  3189.         If IsArray(aObj1(0)) Then
  3190.             For Each obj In aObj1(0) : vpmToggleObj obj, aEnabled     : Next
  3191.             For Each obj In aObj1(1) : vpmToggleObj obj, Not aEnabled : Next
  3192.         Else
  3193.             For Each obj In aObj1    : vpmToggleObj obj, aEnabled     : Next
  3194.         End If
  3195.     ElseIf Not aObj1 Is Nothing Then
  3196.         vpmToggleObj aObj1, aEnabled
  3197.     End If
  3198.     If vpmIsArray(aObj2) Then
  3199.         If IsArray(aObj2(0)) Then
  3200.             For Each obj In aObj2(0) : vpmToggleObj obj, Not aEnabled : Next
  3201.             For Each obj In aObj2(1) : vpmToggleObj obj, aEnabled     : Next
  3202.         Else
  3203.             For Each obj In aObj2    : vpmToggleObj obj, Not aEnabled : Next
  3204.         End If
  3205.     ElseIf Not aObj2 Is Nothing Then
  3206.         vpmToggleObj aObj2, Not aEnabled
  3207.     End If
  3208.     vpmPlaySound aEnabled, aSound
  3209. End Sub
  3210.  
  3211. '
  3212. ' Stubs to allow older games to still work
  3213. ' These will be removed one day
  3214. '
  3215. Sub SolFlipper(f1,f2,e) : vpmSolFlipper f1,f2,e : End Sub
  3216. Sub SolDiverter(d,s,e) : vpmSolDiverter d,s,e : End Sub
  3217. Sub SolSound(s,e) : vpmSolSound s,e : End Sub
  3218. Sub Flasher(f,e) : vpmFlasher f,e : End Sub
  3219. Sub SolMagnet(m,e) : vpmSolMagnet m,e : End Sub
  3220. Sub SolAutoPlunger(p,e) : vpmSolAutoPlunger p,0,e : End Sub
  3221. Function KeyDownHandler(ByVal k) : KeyDownHandler = vpmKeyDown(k) : End Function
  3222. Function KeyUpHandler(ByVal k) : KeyUpHandler = vpmKeyUp(k) : End Function
  3223. Function KeyName(ByVal k) : KeyName = vpmKeyName(k) : End Function
  3224. Sub vpmSolMagnet(m,e) : m.Enabled = e : If Not e Then m.Kick 180,1 : End If : End Sub
  3225. Dim vpmBallImage : vpmBallImage = Empty ' Default ball properties
  3226. Dim vpmBallColour
  3227.  
  3228. '-- Flipper solenoids (all games)
  3229. Const sLRFlipper = 46
  3230. Const sLLFlipper = 48
  3231. Const sURFlipper = 34
  3232. Const sULFlipper = 36
  3233.  
  3234. ' Convert keycode to readable string
  3235. Private keyNames1, keyNames2
  3236. keyNames1 = Array("Escape","1","2","3","4","5","6","7","8","9","0","Minus '-'",_
  3237. "Equals '='","Backspace","Tab","Q","W","E","R","T","Y","U","I","O","P","[","]",_
  3238. "Enter","Left Ctrl","A","S","D","F","G","H","J","K","L",";","'","`","Left Shift",_
  3239. "\","Z","X","C","V","B","N","M",",",".","/","Right Shift","*","Left Menu","Space",_
  3240. "Caps Lock","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","NumLock","ScrlLock",_
  3241. "Numpad 7","Numpad 8","Numpad 9","Numpad -","Numpad 4","Numpad 5","Numpad 6",_
  3242. "Numpad +","Numpad 1","Numpad 2","Numpad 3","Numpad 0","Numpad .","?","?","?",_
  3243. "F11","F12","F13","F14","F15")
  3244. keyNames2 = Array("Pause","?","Home","Up","PageUp","?","Left","?","Right","?",_
  3245. "End","Down","PageDown","Insert","Delete")
  3246.  
  3247. Function vpmKeyName(ByVal aKeycode)
  3248.     If aKeyCode-1 <= UBound(keyNames1) Then
  3249.         vpmKeyName = keyNames1(aKeyCode-1)
  3250.     ElseIf aKeyCode >= 197 And aKeyCode <= 211 Then
  3251.         vpmKeyName = keyNames2(aKeyCode-197)
  3252.     ElseIf aKeyCode = 184 Then
  3253.         vpmKeyName = "R.Alt"
  3254.     Else
  3255.         vpmKeyName = "?"
  3256.     End If
  3257. End Function
  3258.  
  3259. Private vpmSystemHelp
  3260. Private Sub vpmShowHelp
  3261.     Dim szKeyMsg
  3262.     szKeyMsg = "The following keys are defined: "                  & vbNewLine &_
  3263.                "(American keyboard layout)"                        & vbNewLine &_
  3264.         vbNewLine & "Visual PinMAME keys:"                         & vbNewLine &_
  3265.         vpmKeyName(keyShowOpts)   & vbTab & "Game options..."      & vbNewLine &_
  3266.         vpmKeyName(keyShowKeys)   & vbTab & "Keyboard settings..." & vbNewLine &_
  3267.         vpmKeyName(keyReset)      & vbTab & "Reset emulation"      & vbNewLine &_
  3268.         vpmKeyName(keyFrame)      & vbTab & "Toggle Display lock"  & vbNewLine &_
  3269.         vpmKeyName(keyDoubleSize) & vbTab & "Toggle Display size"  & vbNewLine
  3270.     If IsObject(vpmShowDips) Then
  3271.             szKeyMsg = szKeyMsg & vpmKeyName(keyShowDips)   & vbTab & "Show DIP Switch / Option Menu" & vbNewLine
  3272.         End If
  3273.     If IsObject(vpmTrough) Then
  3274.         szKeyMsg = szKeyMsg & vpmKeyName(keyAddBall) & vbTab & "Add / Remove Ball From Table" & vbNewLine
  3275.     End If
  3276.     szKeyMsg = szKeyMsg & vpmKeyName(keyBangBack) & vbTab & "Bang Back" & vbNewLine &_
  3277.         vbNewLine & vpmSystemHelp & vbNewLine
  3278.     If ExtraKeyHelp <> "" Then
  3279.         szKeyMsg = szKeyMsg & vbNewLine & "Game Specific keys:" &_
  3280.             vbNewLine & ExtraKeyHelp & vbNewLine
  3281.     End If
  3282.     szKeyMsg = szKeyMsg & vbNewLine & "Visual Pinball keys:"     & vbNewLine &_
  3283.         vpmKeyName(LeftFlipperKey)  & vbTab & "Left Flipper"     & vbNewLine &_
  3284.         vpmKeyName(RightFlipperKey) & vbTab & "Right Flipper"    & vbNewLine &_
  3285.         vpmKeyName(LeftMagnaSave)   & vbTab & "Left Magna Save"  & vbNewLine &_
  3286.         vpmKeyName(RightMagnaSave)  & vbTab & "Right Magna Save" & vbNewLine &_
  3287.         vpmKeyName(PlungerKey)      & vbTab & "Launch Ball"      & vbNewLine &_
  3288.         vpmKeyName(StartGameKey)    & vbTab & "Start Button"     & vbNewLine &_
  3289.         vpmKeyName(AddCreditKey)    & vbTab & "Insert Coin 1"    & vbNewLine &_
  3290.         vpmKeyName(AddCreditKey2)   & vbTab & "Insert Coin 2"    & vbNewLine &_
  3291.         vpmKeyName(ExitGame)        & vbTab & "Exit Game"        & vbNewLine &_
  3292.         vpmKeyName(MechanicalTilt)  & vbTab & "Mechanical Tilt"  & vbNewLine &_
  3293.         vpmKeyName(LeftTiltKey)     & vbTab & "Nudge from Left"  & vbNewLine &_
  3294.         vpmKeyName(RightTiltKey)    & vbTab & "Nudge from Right" & vbNewLine &_
  3295.         vpmKeyName(CenterTiltKey)   & vbTab & "Nudge forward"    & vbNewLine
  3296.     MsgBox szKeyMsg,vbOkOnly,"Keyboard Settings..."
  3297. End Sub
  3298.  
  3299. Private Sub NullSub(no,enabled)
  3300. 'Place Holder Sub
  3301. End Sub
  3302.  
  3303. 'added thanks to Koadic
  3304. Sub NVOffset(version) ' version 2 for dB2S compatibility
  3305.     Dim check,nvcheck,v,vv,nvpath,rom
  3306.     Set check = CreateObject("Scripting.FileSystemObject")
  3307.     Set nvcheck = CreateObject("WScript.Shell")
  3308.     nvpath = nvcheck.RegRead("HKCU\Software\Freeware\Visual PinMame\globals\nvram_directory") & "\"
  3309.     rom = controller.gamename
  3310.     For v=1 to 32 'check up to 32 possible versions using same rom, it's overkill, but could be changed to a lower number (requested for 32 NFL variations)
  3311.         If check.FileExists(nvpath & rom & " v" & v & ".txt") Then vv=v : exit For : End If
  3312.         vv=0
  3313.     Next
  3314.     If vv=version or version = 0 Then
  3315.         Exit Sub
  3316.     ElseIf vv=0 Then
  3317.         check.CreateTextFile nvpath & rom & " v" & version & ".txt", True
  3318.         Exit Sub
  3319.     Else
  3320.         check.moveFile nvpath & rom & " v" & vv & ".txt", nvpath & rom & " v" & version & ".txt"
  3321.         If check.FileExists(nvpath & rom & ".nv") Then
  3322.             check.copyFile nvpath & rom & ".nv", nvpath & rom & " v" & vv & ".nv", True
  3323.         End If
  3324.         If check.FileExists(nvpath & rom & " v" & version & ".nv") Then
  3325.             check.copyFile nvpath & rom & " v" & version & ".nv", nvpath & rom & ".nv", True
  3326.         End If
  3327.     End If
  3328. End Sub
  3329.  
  3330. Sub VPMVol
  3331.     dim VolPM,VolPMNew
  3332.     VolPM = Controller.Games(controller.GameName).Settings.Value("volume")
  3333.     VolPMNew = InputBox ("Enter desired VPinMame Volume Level (-32 to 0)","VPinMame Volume",VolPM)
  3334.     If VolPMNew = "" Then Exit Sub
  3335.     If VolPMNew <=0 and VolPMNew >= -32 Then
  3336.         Controller.Games(controller.GameName).Settings.Value("volume")= round(VolPMNew)
  3337.         msgbox "The Visual PinMAME Global Volume is now set to " & round(VolPMNew) & "db." & VbNewLine & VbNewLine & "Please reset Visual PinMAME (F3) to apply."
  3338.     Else
  3339.         msgbox "Entered value is out of range. Entry must be in the range of negative 32 to 0." & VbNewLine & VbNewLine & "Visual PinMAME Global Volume will remain set at " & VolPM & "."
  3340.     End If
  3341. End Sub
  3342.  
  3343. ' Simple min/max functions
  3344. Function vpMin(a, b) : If a < b Then vpMin = a Else vpMin = b : End If : End Function
  3345. Function vpMax(a, b) : If a > b Then vpMax = a Else vpMax = b : End If : End Function
  3346.  
  3347. LoadScript("ledcontrol.vbs"):Err.Clear  ' Checks for existance of ledcontrol.vbs and loads it if found, if found but no ledwiz installed, clear error to allow loading of table
  3348.  
  3349. LoadScript("GlobalPlugIn.vbs")          ' Checks for existance of GlobalPlugIn.vbs and loads it if found, useful for adding
  3350.                                         ' custom scripting that can be used for all tables instead of altering the core.vbs
  3351.  
  3352.  
  3353. '-------------------------
  3354. ' S11 Data
  3355. '-------------------------
  3356. ' Flipper Solenoid
  3357. Const GameOnSolenoid = 23
  3358. ' Cabinet switches
  3359. Const swAdvance        = -7
  3360. Const swUpDown         = -6
  3361. Const swCPUDiag        = -5
  3362. Const swSoundDiag      = -4
  3363. Const swTilt           =  1
  3364. Const swBallRollTilt   =  2
  3365. Const swStartButton    =  3
  3366. Const swCoin3          =  4
  3367. Const swCoin2          =  5
  3368. Const swCoin1          =  6
  3369. Const swSlamTilt       =  7
  3370. Const swHiScoreReset   =  8
  3371.  
  3372. Const swLRFlip         = 82
  3373. Const swLLFlip         = 84
  3374.  
  3375. ' Help Window
  3376. vpmSystemHelp = "Williams System 9/11 keys:" & vbNewLine &_
  3377.   vpmKeyName(keyInsertCoin1)  & vbTab & "Insert Coin #1"   & vbNewLine &_
  3378.   vpmKeyName(keyInsertCoin2)  & vbTab & "Insert Coin #2"   & vbNewLine &_
  3379.   vpmKeyName(keyInsertCoin3)  & vbTab & "Insert Coin #3"   & vbNewLine &_
  3380.   vpmKeyName(keyHiscoreReset) & vbTab & "Hiscore Reset"    & vbNewLine &_
  3381.   vpmKeyName(keyAdvance)      & vbTab & "Advance"          & vbNewLine &_
  3382.   vpmKeyName(keyUpDown)       & vbTab & "Up/Down"          & vbNewLine &_
  3383.   vpmKeyName(keyCPUDiag)      & vbTab & "Cpu Diagnostic"   & vbNewLine &_
  3384.   vpmKeyName(keySoundDiag)    & vbTab & "Sound Diagnostic" & vbNewLine &_
  3385.   vpmKeyName(keySlamDoorHit)  & vbTab & "Slam Tilt"
  3386.  
  3387. ' Dip Switch / Options Menu
  3388. Private Sub s11ShowDips
  3389.     If Not IsObject(vpmDips) Then ' First time
  3390.         Set vpmDips = New cvpmDips
  3391.         With vpmDips
  3392.             .AddForm 150, 45, "DIP Switches"
  3393.             .AddChk  0,0,100, Array("Germany", &H0001)
  3394.         End With
  3395.     End If
  3396.     vpmDips.ViewDips
  3397. End Sub
  3398. Set vpmShowDips = GetRef("s11ShowDips")
  3399. Private vpmDips
  3400.  
  3401. ' Keyboard handlers
  3402. Function vpmKeyDown(ByVal keycode)
  3403.     On Error Resume Next
  3404.     vpmKeyDown = True ' Assume we handle the key
  3405.     With Controller
  3406.         Select Case keycode
  3407.             Case LeftFlipperKey  .Switch(swLLFlip) = True : vpmKeyDown = False :  vpmFlips.FlipL True : if keycode = keyStagedFlipperL then vpmFlips.FlipUL True
  3408.             Case RightFlipperKey .Switch(swLRFlip) = True : vpmKeyDown = False :  vpmFlips.FlipR True : if keycode = keyStagedFlipperR then vpmFlips.FlipUR True
  3409.             Case keyStagedFlipperL vpmFlips.FlipUL True
  3410.             Case keyStagedFlipperR vpmFlips.FlipUR True
  3411.             Case keyInsertCoin1  vpmTimer.AddTimer 750,"vpmTimer.PulseSw swCoin1'" : Playsound SCoin
  3412.             Case keyInsertCoin2  vpmTimer.AddTimer 750,"vpmTimer.PulseSw swCoin2'" : Playsound SCoin
  3413.             Case keyInsertCoin3  vpmTimer.AddTimer 750,"vpmTimer.PulseSw swCoin3'" : Playsound SCoin
  3414.             Case StartGameKey    .Switch(swStartButton)  = True
  3415.             Case keyUpDown       .Switch(swUpDown)       = Not .Switch(swUpDown)
  3416.             Case keyAdvance      .Switch(swAdvance)      = True
  3417.             Case keyCPUDiag      .Switch(swCPUDiag)      = True
  3418.             Case keySoundDiag    .Switch(swSoundDiag)    = True
  3419.             Case keyHiscoreReset .Switch(swHiscoreReset) = True
  3420.             Case keySlamDoorHit  .Switch(swSlamTilt)     = True
  3421.             Case keyBangBack     vpmNudge.DoNudge 0, 6
  3422.             Case LeftTiltKey     vpmNudge.DoNudge 75, 2
  3423.             Case RightTiltKey    vpmNudge.DoNudge 285, 2
  3424.             Case CenterTiltKey   vpmNudge.DoNudge 0, 2
  3425.             Case keyVPMVolume    vpmVol
  3426.             Case Else            vpmKeyDown = False
  3427.         End Select
  3428.     End With
  3429.     On Error Goto 0
  3430. End Function
  3431.  
  3432. Function vpmKeyUp(ByVal keycode)
  3433.     On Error Resume Next
  3434.     vpmKeyUp = True ' Assume we handle the key
  3435.     With Controller
  3436.         Select Case keycode
  3437.             Case LeftFlipperKey  .Switch(swLLFlip) = False : vpmKeyUp = False :  vpmFlips.FlipL False : if keycode = keyStagedFlipperL then vpmFlips.FlipUL False
  3438.             Case RightFlipperKey .Switch(swLRFlip) = False : vpmKeyUp = False :  vpmFlips.FlipR False : if keycode = keyStagedFlipperR then vpmFlips.FlipUR False
  3439.             Case keyStagedFlipperL vpmFlips.FlipUL False
  3440.             Case keyStagedFlipperR vpmFlips.FlipUR False
  3441.             Case StartGameKey    .Switch(swStartButton)  = False
  3442.             Case keyAdvance      .Switch(swAdvance)      = False
  3443.             Case keyCPUDiag      .Switch(swCPUDiag)      = False
  3444.             Case keySoundDiag    .Switch(swSoundDiag)    = False
  3445.             Case keyHiscoreReset .Switch(swHiscoreReset) = False
  3446.             Case keySlamDoorHit  .Switch(swSlamTilt)     = False
  3447.             Case keyShowOpts     .Pause = True : .ShowOptsDialog GetPlayerHWnd : .Pause = False
  3448.             Case keyShowKeys     .Pause = True : vpmShowHelp : .Pause = False
  3449.             Case keyShowDips     If IsObject(vpmShowDips) Then .Pause = True : vpmShowDips : .Pause = False
  3450.             Case keyAddBall      .Pause = True : vpmAddBall  : .Pause = False
  3451.             Case keyReset        .Stop : BeginModal : .Run : vpmTimer.Reset : EndModal
  3452.             Case keyFrame        .LockDisplay = Not .LockDisplay
  3453.             Case keyDoubleSize   .DoubleSize  = Not .DoubleSize
  3454.             Case Else            vpmKeyUp = False
  3455.         End Select
  3456.     End With
  3457.     On Error Goto 0
  3458. End Function
  3459.  
  3460.  
  3461.  
  3462. Dim bsTrough, bsDog, bsTrash, dtBird, dtMilk
  3463. Const cGameName = "bcats_l5"
  3464.  
  3465. Const UseSolenoids = 2
  3466. Const UseLamps = 0
  3467. Const UseGI = 0
  3468. Const UseSync = 1
  3469. Const HandleMech = 0
  3470.  
  3471. 'Standard Sounds
  3472. Const SSolenoidOn = "fx_Solenoid"
  3473. Const SSolenoidOff = ""
  3474. Const SCoin = "fx_Coin"
  3475.  
  3476. '************
  3477. ' Table init.
  3478. '************
  3479. dim HiddenVar
  3480. If Table1.ShowDT = False then
  3481.     HiddenVar = 1
  3482. Else
  3483.     HiddenVar = 0
  3484. end If
  3485.  
  3486. Sub Table1_Init
  3487.     vpmInit me
  3488.  
  3489.     With Controller
  3490.         .GameName = cGameName
  3491.         If Err Then MsgBox "Can't start Game" & cGameName & vbNewLine & Err.Description:Exit Sub
  3492.         .SplashInfoLine = "BadCats, Williams 1989" & vbNewLine & "VPX table by unclewilly v.1.0"
  3493.         .Games(cGameName).Settings.Value("rol") = 0 '1= rotated display, 0= normal
  3494.        .HandleMechanics = 0
  3495.         .HandleKeyboard = 0
  3496.         .ShowDMDOnly = 1
  3497.         .ShowFrame = 0
  3498.         .ShowTitle = 0
  3499.         .Hidden = HiddenVar
  3500.         If Err Then MsgBox Err.Description
  3501.     End With
  3502.     On Error Goto 0
  3503.     Controller.Run
  3504.  
  3505.     'Nudging
  3506.    vpmNudge.TiltSwitch = swTilt
  3507.     vpmNudge.Sensitivity = 2
  3508.     'vpmNudge.TiltObj = Array(sw60, sw61, sw62, LeftSlingshot, RightSlingShot)
  3509.  
  3510.     'Trough
  3511.    Set bsTrough = New cvpmBallStack
  3512.     With bsTrough
  3513.         .InitSw 0, 10, 0, 0, 0, 0, 0, 0
  3514.         .InitKick ballrelease, 90, 4
  3515.         .InitEntrySnd "fx_Solenoid", "fx_Solenoid"
  3516.         .InitExitSnd SoundFX("fx_ballrel", DOFContactors), SoundFX("fx_Solenoid", DOFContactors)
  3517.         .IsTrough = True
  3518.         .Balls = 1
  3519.     End With
  3520.  
  3521.     'Dog House hole
  3522.    Set bsDog = New cvpmBallStack
  3523.     With bsDog
  3524.         .InitSw 0, 22, 0, 0, 0, 0, 0, 0
  3525.         .InitKick Ralfie, 180, 22
  3526.         .InitEntrySnd "fx_kicker_enter", "fx_Solenoid"
  3527.         .InitExitSnd SoundFX("fx_kicker", DOFContactors), SoundFX("fx_Solenoid", DOFContactors)
  3528.         .IsTrough = False
  3529.     End With
  3530.  
  3531.     'Trash hole
  3532.    Set bsTrash = New cvpmBallStack
  3533.     With bsTrash
  3534.         .InitSaucer Bin, 24, 79, 22
  3535.         .KickForceVar = 2
  3536.         .KickAngleVar = 2
  3537.         .InitExitSnd SoundFX("fx_kicker", DOFContactors), SoundFX("fx_kicker", DOFContactors)
  3538.         .InitAddSnd SoundFX("fx_kicker_enter", DOFContactors)
  3539.         .CreateEvents "bsTrash", Bin
  3540.     End With
  3541.  
  3542.     'Droptargets
  3543.    set dtBird = new cvpmdroptarget
  3544.     With dtBird
  3545.         .InitDrop Array(sw25,sw26,sw27,sw28,sw29), Array(25, 26, 27, 28, 29)
  3546.         .Initsnd SoundFX("fx_droptarget", DOFContactors), SoundFX("fx_resetdrop", DOFContactors)
  3547.     End With
  3548.  
  3549.     set dtMilk = new cvpmdroptarget
  3550.     With dtMilk
  3551.         .InitDrop Array(sw37,sw38,sw39), Array(37, 38, 39)
  3552.         .Initsnd SoundFX("fx_droptarget", DOFContactors), SoundFX("fx_resetdrop", DOFContactors)
  3553.     End With
  3554.  
  3555.     ' Seafood Wheel
  3556.    Dim mSFWheelMech
  3557.     Set mSFWheelMech = New cvpmMech
  3558.     With mSFWheelMech
  3559.         .MType = vpmMechStepSol + vpmMechCircle + vpmMechLinear + vpmMechFast
  3560.         .Sol1 = 16
  3561.         .Sol2 = 15
  3562.         .Length = 200
  3563.         .Steps = 200
  3564.         .AddSw 44, 0, 99
  3565.         .Callback = GetRef("UpdateWheel")
  3566.         .Start
  3567.     End With
  3568.  
  3569. 'Init VariTarget
  3570.    sw19w21.IsDropped = 1
  3571.     sw19w31.IsDropped = 1
  3572.     sw19w41.IsDropped = 1
  3573.  
  3574.     'Main Timer init
  3575.    PinMAMETimer.Interval = PinMAMEInterval
  3576.     PinMAMETimer.Enabled = 1
  3577.  
  3578.  
  3579. If Table1.ShowDT = False then
  3580.     For each xx in SideRails:xx.Visible = False:Next
  3581. End If
  3582. End Sub
  3583.  
  3584. Sub table1_Paused:Controller.Pause = 1:End Sub
  3585. Sub table1_unPaused:Controller.Pause = 0:End Sub
  3586. Sub table1_exit:Controller.Stop:End Sub
  3587. '**********
  3588. ' Keys
  3589. '**********
  3590.  
  3591. Sub table1_KeyDown(ByVal Keycode)
  3592.         If keycode = LeftFlipperKey Then
  3593.             If FlipLag = 1 then flipnf 0, 1
  3594.         end if
  3595.         If keycode = RightFlipperKey Then
  3596.             If FlipLag = 1 then flipnf 1, 1
  3597.         end if
  3598.     If keycode = LeftTiltKey Then Nudge 90, 5:PlaySound SoundFX("fx_nudge", 0), 0, 1, -0.1, 0.25
  3599.     If keycode = RightTiltKey Then Nudge 270, 5:PlaySound SoundFX("fx_nudge", 0), 0, 1, 0.1, 0.25
  3600.     If keycode = CenterTiltKey Then Nudge 0, 6:PlaySound SoundFX("fx_nudge", 0), 0, 1, 0, 0.25
  3601.     If keycode = PlungerKey Then PlaySound "fx_PlungerPull", 0, 1, 0.1, 0.05:Plunger.Pullback
  3602.     If vpmKeyDown(keycode) Then Exit Sub
  3603. End Sub
  3604.  
  3605. Sub table1_KeyUp(ByVal Keycode)
  3606.         If keycode = LeftFlipperKey Then
  3607.             If FlipLag = 1 then flipnf 0, 0
  3608.         end if
  3609.         If keycode = RightFlipperKey Then
  3610.             If FlipLag = 1 then flipnf 1, 0
  3611.         end if
  3612.     If keycode = PlungerKey Then PlaySound "fx_plunger", 0, 1, 0.1, 0.05:Plunger.Fire
  3613.     If vpmKeyUp(keycode) Then Exit Sub
  3614. End Sub
  3615.  
  3616. '*********
  3617. ' Switches
  3618. '*********
  3619.  
  3620. 'Slings & Rubbers
  3621. Dim LStep, RStep
  3622.  
  3623. Sub LeftSlingShot_Slingshot
  3624.     PlaySound SoundFX("fx_slingshot", DOFContactors), 0, 1, -0.05, 0.05
  3625.     LeftSling4.Visible = 1
  3626.     Lemk.RotX = 26
  3627.     LStep = 0
  3628.     vpmTimer.PulseSw 63
  3629.     LeftSlingShot.TimerEnabled = 1
  3630. End Sub
  3631.  
  3632. Sub LeftSlingShot_Timer
  3633.     Select Case LStep
  3634.         Case 1:LeftSLing4.Visible = 0:LeftSLing3.Visible = 1:Lemk.RotX = 14
  3635.         Case 2:LeftSLing3.Visible = 0:LeftSLing2.Visible = 1:Lemk.RotX = 2
  3636.         Case 3:LeftSLing2.Visible = 0:Lemk.RotX = -10:LeftSlingShot.TimerEnabled = 0
  3637.     End Select
  3638.     LStep = LStep + 1
  3639. End Sub
  3640.  
  3641. Sub RightSlingShot_Slingshot
  3642.     PlaySound SoundFX("fx_slingshot", DOFContactors), 0, 1, 0.05, 0.05
  3643.     RightSling4.Visible = 1
  3644.     Remk.RotX = 26
  3645.     RStep = 0
  3646.     vpmTimer.PulseSw 64
  3647.     RightSlingShot.TimerEnabled = 1
  3648. End Sub
  3649.  
  3650. Sub RightSlingShot_Timer
  3651.     Select Case RStep
  3652.         Case 1:RightSLing4.Visible = 0:RightSLing3.Visible = 1:Remk.RotX = 14
  3653.         Case 2:RightSLing3.Visible = 0:RightSLing2.Visible = 1:Remk.RotX = 2
  3654.         Case 3:RightSLing2.Visible = 0:Remk.RotX = -10:RightSlingShot.TimerEnabled = 0
  3655.     End Select
  3656.     RStep = RStep + 1
  3657. End Sub
  3658.  
  3659. 'Rubbers
  3660.  
  3661. Sub sw40_Hit():PlaySound "fx_Rubber", 0, 1, -0.1, 0.15::vpmTimer.PulseSw 40:End Sub
  3662. Sub sw33_Hit():PlaySound "fx_Rubber", 0, 1, 0.1, 0.15::vpmTimer.PulseSw 33:End Sub
  3663. Sub sw34_Hit():PlaySound "fx_Rubber", 0, 1, 0.1, 0.15::vpmTimer.PulseSw 34:End Sub
  3664.  
  3665.  
  3666. ' Bumpers
  3667. Sub sw60_Hit:vpmTimer.PulseSw 60:PlaySound SoundFX("fx_bumper", DOFContactors), 0, 1, -0.1, 0.15:End Sub
  3668. Sub sw61_Hit:vpmTimer.PulseSw 61:PlaySound SoundFX("fx_bumper", DOFContactors), 0, 1, 0.1, 0.15:End Sub
  3669. Sub sw62_Hit:vpmTimer.PulseSw 62:PlaySound SoundFX("fx_bumper", DOFContactors), 0, 1, 0, 0.15:End Sub
  3670.  
  3671.  
  3672. 'Rollover & Ramp Switches
  3673. Sub sw11_Hit:Controller.Switch(11) = 1:PlaySound "fx_sensor", 0, 1, -0.1, 0.15:End Sub
  3674. Sub sw11_UnHit:Controller.Switch(11) = 0:End Sub
  3675.  
  3676. Sub sw12_Hit:Controller.Switch(12) = 1:PlaySound "fx_sensor", 0, 1, 0, 0.15:End Sub
  3677. Sub sw12_UnHit:Controller.Switch(12) = 0:End Sub
  3678.  
  3679. Sub sw13_Hit:Controller.Switch(13) = 1:PlaySound "fx_sensor", 0, 1, 0, 0.15:End Sub
  3680. Sub sw13_UnHit:Controller.Switch(13) = 0:End Sub
  3681.  
  3682. Sub sw14_Hit:Controller.Switch(14) = 1:PlaySound "fx_sensor", 0, 1, 0, 0.15:End Sub
  3683. Sub sw14_UnHit:Controller.Switch(14) = 0:End Sub
  3684.  
  3685. Sub sw36_Hit:Controller.Switch(36) = 1:PlaySound "fx_sensor", 0, 1, -0.1, 0.15:End Sub
  3686. Sub sw36_UnHit:Controller.Switch(36) = 0:End Sub
  3687.  
  3688. Sub sw30_Hit:Controller.Switch(30) = 1:PlaySound "fx_sensor", 0, 1, -0.1, 0.15:End Sub
  3689. Sub sw30_UnHit:Controller.Switch(30) = 0:End Sub
  3690.  
  3691. Sub sw31_Hit:Controller.Switch(31) = 1:PlaySound "fx_sensor", 0, 1, 0.1, 0.15:End Sub
  3692. Sub sw31_UnHit:Controller.Switch(31) = 0:End Sub
  3693.  
  3694. Sub sw35_Hit:Controller.Switch(35) = 1:PlaySound "fx_sensor", 0, 1, -0.1, 0.15:End Sub
  3695. Sub sw35_UnHit:Controller.Switch(35) = 0:End Sub
  3696.  
  3697. Sub sw41_Hit:Controller.Switch(41) = 1:sw41.Timerenabled = 1:PlaySound "fx_sensor", 0, 1, 0.1, 0.15:End Sub
  3698. Sub sw41_UnHit:Controller.Switch(41) = 0:End Sub
  3699. dim sw41Dir
  3700. sw41Dir = -1
  3701. Sub sw41_Timer()
  3702.     If sw41P.ObjRotZ = 60 then sw41Dir = 5
  3703.     If sw41P.ObjRotZ = 90 then sw41Dir = -5
  3704.     sw41P.ObjRotZ = sw41P.ObjRotZ + sw41Dir
  3705.     If sw41P.ObjRotZ = 90 then sw41.timerenabled = 0
  3706. End Sub
  3707.  
  3708. Sub sw43_Hit:Controller.Switch(43) = 1:sw43.Timerenabled = 1:PlaySound "fx_sensor", 0, 1, 0.1, 0.15:End Sub
  3709. Sub sw43_UnHit:Controller.Switch(43) = 0:End Sub
  3710. dim sw43Dir
  3711. sw43Dir = -1
  3712. Sub sw43_Timer()
  3713.     If sw43P.ObjRotZ = 60 then sw43Dir = 5
  3714.     If sw43P.ObjRotZ = 90 then sw43Dir = -5
  3715.     sw43P.ObjRotZ = sw43P.ObjRotZ + sw43Dir
  3716.     If sw43P.ObjRotZ = 90 then sw43.timerenabled = 0
  3717. End Sub
  3718.  
  3719. 'Ramp Gates
  3720. Sub sw16_Hit:vpmTimer.PulseSw 16:End Sub
  3721.  
  3722. Sub sw23_Hit:vpmTimer.PulseSw 23:End Sub
  3723.  
  3724. ' Linear Fish Target
  3725.  
  3726. Sub sw19_1_Hit:vpmTimer.PulseSw 19:PlaySound SoundFX("fx_target", DOFContactors), 0, 1, -0.1, 0.15:Fisht.transY = -50:sw19w11.IsDropped = 1:sw19w21.IsDropped = 0:End Sub
  3727.  
  3728. Sub sw19_2_Hit:vpmTimer.PulseSw 19:PlaySound SoundFX("fx_target", DOFContactors), 0, 1, -0.1, 0.15:Fisht.transY = -95:sw19w21.IsDropped = 1:sw19w31.IsDropped = 0:End Sub
  3729.  
  3730. Sub sw19_3_Hit:vpmTimer.PulseSw 19:PlaySound SoundFX("fx_target", DOFContactors), 0, 1, -0.1, 0.15:Fisht.transY = -135:sw19w31.IsDropped = 1:sw19w41.IsDropped = 0:End Sub
  3731.  
  3732. Sub sw19_4_Hit:vpmTimer.PulseSw 19:PlaySound SoundFX("fx_target", DOFContactors), 0, 1, -0.1, 0.15:End Sub
  3733.  
  3734. Sub sw19_1_UnHit
  3735.     If ActiveBall.VelY > 0 Then
  3736.        
  3737.         sw19w11.IsDropped = 0
  3738.         sw19w21.IsDropped = 1
  3739.     End If
  3740. End Sub
  3741.  
  3742. Sub sw19_2_UnHit
  3743.     If ActiveBall.VelY > 0 Then
  3744.  
  3745.         sw19w21.IsDropped = 0
  3746.         sw19w31.IsDropped = 1
  3747.     End If
  3748. End Sub
  3749.  
  3750. Sub sw19_3_UnHit
  3751.     If ActiveBall.VelY > 0 Then
  3752.  
  3753.         sw19w31.IsDropped = 0
  3754.         sw19w41.IsDropped = 1
  3755.     End If
  3756. End Sub
  3757.  
  3758. Sub sw19_4_UnHit
  3759. End Sub
  3760.  
  3761. Sub Fanimation_UnHit():FTTimer.Enabled = 1:End Sub
  3762.  
  3763. Sub FTTimer_Timer()
  3764.     If FishT.TransY < 0 Then
  3765.         FishT.TransY =FishT.TransY + 5
  3766.     Else
  3767.         FTTimer.enabled = 0
  3768.     end If
  3769. End Sub
  3770.  
  3771. 'Droptargets VPX
  3772. Sub sw25_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, -0.1, 0.15:End Sub 'hit event only for the sound
  3773. Sub sw25_Dropped:dtBird.hit 1:If GIState=1 then:sw25l.State = 1:end if: sw25.Image = "BirdTD": End Sub
  3774.  
  3775. Sub sw26_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, -0.1, 0.15:End Sub 'hit event only for the sound
  3776. Sub sw26_Dropped:dtBird.hit 2:If GIState=1 then:sw26l.State = 1:end if:sw26.Image = "BirdTD": End Sub
  3777.  
  3778. Sub sw27_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, -0.1, 0.15:End Sub 'hit event only for the sound
  3779. Sub sw27_Dropped:dtBird.hit 3:If GIState=1 then:sw27l.State = 1:end if:sw27.Image = "BirdTD": End Sub
  3780.  
  3781. Sub sw28_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, -0.1, 0.15:End Sub 'hit event only for the sound
  3782. Sub sw28_Dropped:dtBird.hit 4:If GIState=1 then:sw28l.State = 1:end if:sw28.Image = "BirdTD": End Sub
  3783.  
  3784. Sub sw29_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, -0.1, 0.15:End Sub 'hit event only for the sound
  3785. Sub sw29_Dropped:dtBird.hit 5:sw29.Image = "BirdTD": End Sub
  3786.  
  3787. Sub sw37_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, 0.1, 0.15:End Sub 'hit event only for the sound
  3788. Sub sw37_Dropped:dtMilk.hit 1:If GIState=1 then:sw37l.State = 1:end if:sw37.Image = "MilkD": End Sub
  3789.  
  3790. Sub sw38_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, 0.1, 0.15:End Sub 'hit event only for the sound
  3791. Sub sw38_Dropped:dtMilk.hit 2:If GIState=1 then:sw38l.State = 1:end if:sw38.Image = "MilkD": End Sub
  3792.  
  3793. Sub sw39_Hit:PlaySound SoundFX("fx_droptarget", DOFContactors), 0, 1, 0.1, 0.15:End Sub 'hit event only for the sound
  3794. Sub sw39_Dropped:dtMilk.hit 3:If GIState=1 then:sw39l.State = 1:end if:sw39.Image = "MilkD": End Sub
  3795.  
  3796. ' Drain & holes
  3797. Sub Bin_Hit():BsTrash.AddBall 0:End Sub
  3798. Sub Drain_Hit:Playsound "fx_drain":bsTrough.AddBall Me:End Sub
  3799. Sub Ralfie_Hit:Playsound "fx_kicker_enter", 0, 1, 0.05, 0.05:bsDog.AddBall Me:End Sub
  3800.  
  3801. '  Ramp Helpers
  3802. Sub LHelp_Hit():Playsound "fx_balldrop", 0, 1, -0.05, 0.05:end Sub
  3803.  
  3804. Sub RHelp_Hit():Playsound "fx_balldrop", 0, 1, 0.05, 0.05:end Sub
  3805.  
  3806. Sub WireRampSound_Hit():Playsound "WireRamp", 0, 1, 0, 0.35:end Sub
  3807. '***********
  3808. ' Solenoids
  3809. '***********
  3810. ' from pacdudes script
  3811. SolCallback(1) = "bsTrough.SolIn"
  3812. SolCallback(1) = "bsTrough.SolOut"
  3813. SolCallback(2) = "vpmsolsound SoundFX(""fx_knocker"",DOFKnocker),"
  3814. SolCallback(3) = "SolDogOut"
  3815. SolCallback(4) = "solMT"  'dtMilk.SolDropUp
  3816. SolCallback(5) = "bsTrash.SolOut"
  3817. SolCallback(6) = "SolBT"    'dtBird.SolDropUp
  3818. SolCallback(10)= "SolGIBlink"
  3819. SolCallBack(23)= "SolGION"  'check to see if 10 works
  3820.  
  3821. SolCallback(15) = "SolSFW1"
  3822. SolCallback(16) = "SolSFW"
  3823. 'Flashers
  3824. SolCallback(25) = "flash125"
  3825. SolCallback(26) = "flash126"
  3826. SolCallback(27) = "flash127"
  3827. SolCallback(28) = "flash128"
  3828. SolCallback(29) = "flash129"
  3829. SolCallback(30) = "flash130"
  3830. SolCallback(31) = "flash131"
  3831. SolCallback(32) = "flash132"
  3832. 'Solenoid Subs
  3833.  
  3834. Sub SolDogOut(enabled)
  3835.     If Enabled Then
  3836.         bsDog.ExitSol_On
  3837.         SetLamp 190, 0
  3838.     End If
  3839. End Sub
  3840.  
  3841. Sub SolSFW(enabled)
  3842.   If enabled Then
  3843.     SetLamp 190, 1
  3844.   Else
  3845.     SetLamp 190, 0
  3846.   end If
  3847.  
  3848.  
  3849.  
  3850. end Sub
  3851.  
  3852. Sub SolSFW1(enabled)
  3853.  
  3854.   If enabled Then
  3855.     SetLamp 190, 1
  3856.   Else
  3857.     SetLamp 190, 0
  3858.   end If
  3859.  
  3860. end Sub
  3861.  
  3862. Sub solMT(enabled)
  3863.   If enabled Then
  3864.     dtMilk.DropSol_On
  3865.     For each xx in MTGi:xx.State = 0:next
  3866.     For each xx in MT:xx.Image = "Milk":next
  3867.   Else
  3868.   end If
  3869. end Sub
  3870.  
  3871. Sub solBT(enabled)
  3872.   If enabled Then
  3873.     dtBird.DropSol_On
  3874.     For each xx in BTGi:xx.State = 0:next
  3875.     For each xx in BT:xx.Image = "BirdT":next
  3876.   Else
  3877.   end If
  3878. end Sub
  3879.  
  3880. Sub Flash127(enabled)
  3881.   If enabled Then
  3882.     Setlamp 127, 1
  3883.   Else
  3884.     SetLamp 127, 0
  3885.   end If
  3886. end Sub
  3887.  
  3888. Sub Flash125(enabled)
  3889.   If enabled Then
  3890.     Setlamp 125, 1
  3891.   Else
  3892.     SetLamp 125, 0
  3893.   end If
  3894. end Sub
  3895.  
  3896. Sub Flash126(enabled)
  3897.   If enabled Then
  3898.     Setlamp 126, 1
  3899.   Else
  3900.     SetLamp 126, 0
  3901.   end If
  3902. end Sub
  3903.  
  3904. Sub Flash128(enabled)
  3905.   If enabled Then
  3906.     Setlamp 128, 1
  3907.   Else
  3908.     SetLamp 128, 0
  3909.   end If
  3910. end Sub
  3911.  
  3912. Sub Flash129(enabled)
  3913.   If enabled Then
  3914.     Setlamp 129, 1
  3915.   Else
  3916.     SetLamp 129, 0
  3917.   end If
  3918. end Sub
  3919.  
  3920. Sub Flash130(enabled)
  3921.   If enabled Then
  3922.     Setlamp 130, 1
  3923.   Else
  3924.     SetLamp 130, 0
  3925.   end If
  3926. end Sub
  3927.  
  3928. Sub Flash131(enabled)
  3929.   If enabled Then
  3930.     Setlamp 131, 1
  3931.   Else
  3932.     SetLamp 131, 0
  3933.   end If
  3934. end Sub
  3935.  
  3936. Sub Flash132(enabled)
  3937.   If enabled Then
  3938.     Setlamp 132, 1
  3939.   Else
  3940.     SetLamp 132, 0
  3941.   end If
  3942. end Sub
  3943.  
  3944. Sub ACRelay(enabled)
  3945.     vpmNudge.SolGameOn enabled
  3946. End Sub
  3947.  
  3948. '**************
  3949. ' Flipper Subs
  3950. '**************
  3951.  
  3952. SolCallback(sLRFlipper) = "SolRFlipper"
  3953. SolCallback(sLLFlipper) = "SolLFlipper"
  3954.  
  3955. Sub SolLFlipper(Enabled)
  3956.     If Enabled Then
  3957.     If FlipLag = 0 then
  3958.         PlaySound SoundFX("fx_flipperup", DOFContactors), 0, 1, -0.1, 0.25
  3959.         LeftFlipper.RotateToEnd
  3960.     end If
  3961.     Else
  3962.     If FlipLag = 0 then
  3963.         PlaySound SoundFX("fx_flipperdown", DOFContactors), 0, 1, -0.1, 0.25
  3964.         LeftFlipper.RotateToStart
  3965.     end if
  3966.     End If
  3967. End Sub
  3968.  
  3969. Sub SolRFlipper(Enabled)
  3970.     If Enabled Then
  3971.     If FlipLag = 0 then
  3972.         PlaySound SoundFX("fx_flipperup", DOFContactors), 0, 1, 0.1, 0.25
  3973.         RightFlipper.RotateToEnd
  3974.     end if
  3975.     Else
  3976.     If FlipLag = 0 then
  3977.         PlaySound SoundFX("fx_flipperdown", DOFContactors), 0, 1, 0.1, 0.25
  3978.         RightFlipper.RotateToStart
  3979.     end if
  3980.     End If
  3981. End Sub
  3982.  
  3983. Sub LeftFlipper_Collide(parm)
  3984.     PlaySound "fx_rubber_flipper", 0, parm / 10, -0.1, 0.15
  3985. End Sub
  3986.  
  3987. Sub RightFlipper_Collide(parm)
  3988.     PlaySound "fx_rubber_flipper", 0, parm / 10, 0.1, 0.15
  3989. End Sub
  3990.  
  3991. 'SeaFoodWheel Based on jp's script based on cyclone script
  3992. Dim SFWSpin
  3993. SFWSpin = 0
  3994. Sub UpdateWheel(aNewPos, aSpeed, aLastPos)
  3995.     ' 360/200= 1.8
  3996.    If aNewPos <> aLastPos then
  3997.         SFWheel.ObjRotZ = aNewPos * 1.8
  3998.     End If
  3999. End Sub
  4000.  
  4001. '*********
  4002. ' Special Flippers
  4003. '*********
  4004. dim FlippersEnabled
  4005.  
  4006.  
  4007.  
  4008. sub flipnf(LR, DU)
  4009.     if LR = 0 Then        'left flipper
  4010.        if DU = 1 then
  4011.             If FlippersEnabled = True then
  4012.                 leftflipper.rotatetoend
  4013.                 LeftFlipperSound 1
  4014.             end if
  4015.             controller.Switch(swLLFlip) = True
  4016.         Elseif DU = 0 then
  4017.             If FlippersEnabled = True then
  4018.                 leftflipper.rotatetoStart
  4019.                 LeftFlipperSound 0
  4020.             end if
  4021.             controller.Switch(swLLFlip) = False                            
  4022.         end if
  4023.     elseif LR = 1 then        ''right flipper
  4024.        if DU = 1 then
  4025.             If FlippersEnabled = True then
  4026.                 RightFlipper.rotatetoend
  4027.                 RightFlipperSound 1
  4028.             end if
  4029.             controller.Switch(swLRFlip) = True
  4030.         Elseif DU = 0 then
  4031.             If FlippersEnabled = True then
  4032.                 RightFlipper.rotatetoStart
  4033.                 RightFlipperSound 0
  4034.             end if
  4035.             controller.Switch(swLRFlip) = False                        
  4036.         end if
  4037.     end if
  4038. end sub
  4039.  
  4040. sub LeftFlipperSound(updown)'called along with the flipper, so feel free to add stuff, EOStorque tweaks, animation updates, upper flippers, whatever.
  4041.    if updown = 1 Then
  4042.         PlaySound SoundFX("fx_flipperup", DOFContactors), 0, 1, -0.1, 0.25    'flip
  4043.    Else
  4044.         PlaySound SoundFX("fx_flipperdown", DOFContactors), 0, 1, -0.1, 0.25'return
  4045.    end if
  4046. end sub
  4047. sub RightFlipperSound(updown)
  4048.     if updown = 1 Then
  4049.         PlaySound SoundFX("fx_flipperup", DOFContactors), 0, 1, 0.1, 0.25    'flip
  4050.    Else
  4051.         PlaySound SoundFX("fx_flipperdown", DOFContactors), 0, 1, 0.1, 0.25'return
  4052.    end if
  4053. end sub
  4054. '************GI Subs
  4055.  
  4056.  Dim GIActive,GIState
  4057.  GIActive=0:GIState=0
  4058.  Sub SolGION(enabled)
  4059.     FlippersEnabled = Enabled
  4060.     If enabled then
  4061.         GIActive=1:SolGI 1
  4062.     else
  4063.         GIActive=0:SolGI 0
  4064.         if leftflipper.startangle > leftflipper.endangle Then
  4065.             if leftflipper.currentangle < leftflipper.startangle then leftflipper.rotatetostart : leftflippersound 0 : end if
  4066.         elseif leftflipper.startangle < leftflipper.endangle Then
  4067.             if leftflipper.currentangle > leftflipper.startangle then leftflipper.rotatetostart : leftflippersound 0 : end If
  4068.         end If
  4069.         if rightflipper.startangle > rightflipper.endangle Then
  4070.             if rightflipper.currentangle < rightflipper.startangle then rightflipper.rotatetostart : rightflippersound 0 : end if
  4071.         elseif rightflipper.startangle < rightflipper.endangle Then
  4072.             if rightflipper.currentangle > rightflipper.startangle then rightflipper.rotatetostart : rightflippersound 0 : end If
  4073.         end If
  4074.     end if
  4075.  End Sub
  4076.  
  4077.  Sub SolGIBlink(enabled)
  4078.     If GIActive=1 then:SolGI Not enabled:end if
  4079.  End Sub
  4080.  
  4081.  'GI Lights
  4082.  
  4083.   Sub SolGI(Enabled)
  4084.     If enabled then
  4085.     Playsound "fx_relay_on"                             'ninuzzu - added relay click sound
  4086.     Table1.ColorGradeImage = "ColorGrade_8"             'ninuzzu - added LUT color grade---->this will light the whole table when GI is on
  4087.     For each xx in aGiLights:xx.State = 1:next
  4088.     If Sw28.IsDropped = 1 then: sw28l.State = 1: End if
  4089.     If Sw27.IsDropped = 1 then: sw27l.State = 1: End if
  4090.     If Sw26.IsDropped = 1 then: sw26l.State = 1: End if
  4091.     If Sw25.IsDropped = 1 then: sw25l.State = 1: End if
  4092.     If Sw39.IsDropped = 1 then: sw39l.State = 1: End if
  4093.     If Sw38.IsDropped = 1 then: sw38l.State = 1: End if
  4094.     If Sw37.IsDropped = 1 then: sw37l.State = 1: End if
  4095.     GIState=1
  4096.     SetLamp 190, 0
  4097.     else
  4098.     Playsound "fx_relay_off"                            'ninuzzu - added relay click sound
  4099.         Table1.ColorGradeImage = "ColorGrade_1"         'ninuzzu - added LUT color grade---->this will darken the whole table when GI is off
  4100.     For each xx in aGiLights:xx.State = 0:next
  4101.     For each xx in TargetDropGi:xx.State = 0:next
  4102.     GIState=0
  4103.     end if
  4104.  End Sub
  4105.  
  4106. '******************************************************
  4107. '        JP's VP10 Fading Lamps & Flashers
  4108. '  very reduced, mostly for rom activated flashers
  4109. ' if you need to turn a light on or off then use:
  4110. '   LightState(lightnumber) = 0 or 1
  4111. '        Based on PD's Fading Light System
  4112. '******************************************************
  4113.  
  4114. Dim LightState(200), FlashSpeedUp(200), FlashSpeedDown(200), FlashMin(200), FlashMax(200), FlashLevel(200)
  4115.  
  4116. InitFlashers() ' turn off the lights and flashers and reset them to the default parameters
  4117.  
  4118. LampTimer.Interval = 50 'lamp fading speed
  4119. LampTimer.Enabled = 1
  4120.  
  4121. Sub LampTimer_timer()
  4122.     Dim chgLamp, x
  4123.     chgLamp = Controller.ChangedLamps
  4124.     If Not IsEmpty(chgLamp) Then
  4125.         For x = 0 To UBound(chgLamp)
  4126.             LightState(chgLamp(x, 0) ) = chgLamp(x, 1) 'light state as set by the rom
  4127.        Next
  4128.     End If
  4129.     ' Lights & Flashers
  4130.    LightX 1, l1
  4131.     LightX 2, l2
  4132.     LightX 3, l3
  4133.     LightX 4, l4
  4134.     LightX 5, l5
  4135.     LightX 6, l6
  4136.     LightX 7, l7
  4137.     LightX 8, l8
  4138.     Flash 9, l9
  4139.     Flash 10, l10
  4140.     Flash 11, l11
  4141.     Flash 12, l12
  4142.     Flash 13, l13
  4143.     LightX 14, l14
  4144.     LightX 15, l15
  4145.     LightX 16, l16
  4146.     Flash 17, l17
  4147.     Flash 18, l18
  4148.     Flash 19, l19
  4149.  
  4150.     LightX 21, l21
  4151.     LightX 22, l22
  4152.     LightX 23, l23
  4153.     LightX 24, l24
  4154.     LightX 25, l25
  4155.     LightX 26, l26
  4156.     LightX 27, l27
  4157.     LightX 28, l28
  4158.     LightX 29, l29
  4159.     LightX 30, l30
  4160.     LightX 31, l31
  4161.     LightX 33, l33
  4162.     LightX 34, l34
  4163.     LightX 35, l35
  4164.     LightX 36, l36
  4165.     LightX 37, l37
  4166.     LightX 38, l38
  4167.     LightX 39, l39
  4168.     LightX 40, l40
  4169.     LightX 41, l41
  4170.     LightX 42, l42
  4171.     LightX 43, l43
  4172.     LightX 44, l44
  4173.     LightX 45, l45
  4174.     LightX 46, l46
  4175.     LightX 47, l47
  4176.     LightX 48, l48
  4177.     LightX 49, l49
  4178.     LightX 50, l50
  4179.     LightX 51, l51
  4180.     LightX 52, l52
  4181.     LightX 53, l53
  4182. '    LightX 54, l54 'Lamp shed backglass
  4183. '    LightX 55, l55  'bbq bg
  4184. '    LightX 56, l56  'candle bg
  4185. '    LightX 57, l57  '57-64 bg jackpot 1000000 - 8000000
  4186. '    LightX 58, l58
  4187. '    LightX 59, l59
  4188. '    LightX 60, l60
  4189. '    Flashm 61, Diode3
  4190. '    Flash 62, Diode4
  4191. '    LightX 63, l63
  4192. '    LightXm 64, l69a
  4193.     LightXm 125, f25a
  4194.     LightX 125, f25
  4195.     LightXm 126, f26a
  4196.     LightXm 126, f26
  4197.     Flash 126, f26b
  4198.     LightXm 127, f27
  4199.     lightXm 127, f27a
  4200.     Flash 127, f27b
  4201.     LightXm 128, f28a
  4202.     LightXm 128, f28
  4203.     Flash 128, f28b
  4204.     LightXm 129, f29a
  4205.     LightXm 129, f29
  4206.     Flash 129, f29b
  4207.     LightXm 130, f30
  4208.     Flash 130, f30a
  4209.     LightXm 131, f31a
  4210.     LightXm 131, f31
  4211.     Flash 131, f31b
  4212.     LightXm 132, f32
  4213.     Flash 132, f32a
  4214.  
  4215.     Flash 190, SFWL
  4216. End Sub
  4217.  
  4218. Sub SetLamp(nr, value)
  4219.     If value <> LightState(nr) Then
  4220.         LightState(nr) = value
  4221.     End If
  4222. End Sub
  4223.  
  4224. ' div lamp subs
  4225.  
  4226. Sub InitFlashers()
  4227.     Dim x
  4228.     For x = 0 to 200
  4229.         LightState(x) = 0        ' light state: 0=off, 1=on, -1=no change (on or off)
  4230.        FlashSpeedUp(x) = 0.5    ' Fade Speed Up
  4231.        FlashSpeedDown(x) = 0.25 ' Fade Speed Down
  4232.        FlashMax(x) = 1          ' the maximum intensity when on, usually 1
  4233.        FlashMin(x) = 0          ' the minimum intensity when off, usually 0
  4234.        FlashLevel(x) = 0        ' the intensity/fading of the flashers
  4235.    Next
  4236. End Sub
  4237.  
  4238. ' VPX Lights, just turn them on or off
  4239.  
  4240. Sub LightX(nr, object)
  4241.     Select Case LightState(nr)
  4242.         Case 0, 1:object.state = LightState(nr):LightState(nr) = -1
  4243.     End Select
  4244. End Sub
  4245.  
  4246. Sub LightXm(nr, object) 'multiple lights
  4247.    Select Case LightState(nr)
  4248.         Case 0, 1:object.state = LightState(nr)
  4249.     End Select
  4250. End Sub
  4251.  
  4252. ' VPX Flashers, changes the intensity
  4253.  
  4254. Sub Flash(nr, object)
  4255.     Select Case LightState(nr)
  4256.         Case 0 'off
  4257.            FlashLevel(nr) = FlashLevel(nr) - FlashSpeedDown(nr)
  4258.             If FlashLevel(nr) < FlashMin(nr) Then
  4259.                 FlashLevel(nr) = FlashMin(nr)
  4260.                 LightState(nr) = -1 'completely off, so stop the fading loop
  4261.            End if
  4262.             Object.IntensityScale = FlashLevel(nr)
  4263.         Case 1 ' on
  4264.            FlashLevel(nr) = FlashLevel(nr) + FlashSpeedUp(nr)
  4265.             If FlashLevel(nr) > FlashMax(nr) Then
  4266.                 FlashLevel(nr) = FlashMax(nr)
  4267.                 LightState(nr) = -1 'completely on, so stop the fading loop
  4268.            End if
  4269.             Object.IntensityScale = FlashLevel(nr)
  4270.     End Select
  4271. End Sub
  4272.  
  4273. Sub Flashm(nr, object) 'multiple flashers, it just sets the intensity
  4274.    Object.IntensityScale = FlashLevel(nr)
  4275. End Sub
  4276.  
  4277. ' *********************************************************************
  4278. '                      Supporting Ball & Sound Functions
  4279. ' *********************************************************************
  4280.  
  4281. Function Vol(ball) ' Calculates the Volume of the sound based on the ball speed
  4282.    Vol = Csng(BallVel(ball) ^2 / 500)
  4283. End Function
  4284.  
  4285. Function Pan(ball) ' Calculates the pan for a ball based on the X position on the table. "table1" is the name of the table
  4286.    Dim tmp
  4287.     tmp = ball.x * 2 / table1.width-1
  4288.     If tmp > 0 Then
  4289.         Pan = Csng(tmp ^10)
  4290.     Else
  4291.         Pan = Csng(-((- tmp) ^10) )
  4292.     End If
  4293. End Function
  4294.  
  4295. Function Pitch(ball) ' Calculates the pitch of the sound based on the ball speed
  4296.    Pitch = BallVel(ball) * 20
  4297. End Function
  4298.  
  4299. Function BallVel(ball) 'Calculates the ball speed
  4300.    BallVel = INT(SQR((ball.VelX ^2) + (ball.VelY ^2) ) )
  4301. End Function
  4302.  
  4303. '*****************************************
  4304. '      JP's VP10 Rolling Sounds
  4305. '*****************************************
  4306.  
  4307. 'Const tnob = 5 ' total number of balls in this table is 4, but always use a higher number here because of the timing
  4308.  
  4309. Const tnob = 1 'ninuzzu - why 5 balls? Bad Cats has only one ball
  4310. ReDim rolling(tnob)
  4311. InitRolling
  4312.  
  4313. Sub InitRolling
  4314.     Dim i
  4315.     For i = 0 to tnob
  4316.         rolling(i) = False
  4317.     Next
  4318. End Sub
  4319.  
  4320. Sub RollingUpdate()
  4321.     Dim BOT, b, ballpitch
  4322.     BOT = GetBalls
  4323.  
  4324.     ' stop the sound of deleted balls
  4325.    For b = UBound(BOT) + 1 to tnob
  4326.         rolling(b) = False
  4327.         StopSound("fx_ballrolling" & b)
  4328.     Next
  4329.  
  4330.     ' exit the sub if no balls on the table
  4331.    If UBound(BOT) = -1 Then Exit Sub
  4332.  
  4333.     ' play the rolling sound for each ball
  4334.    For b = 0 to UBound(BOT)
  4335.         If BallVel(BOT(b) ) > 1 Then
  4336.             If BOT(b).z < 30 Then
  4337.                 ballpitch = Pitch(BOT(b) )
  4338.             Else
  4339.                 ballpitch = Pitch(BOT(b) ) * 100
  4340.             End If
  4341.             rolling(b) = True
  4342.             PlaySound("fx_ballrolling" & b), -1, Vol(BOT(b) ), Pan(BOT(b) ), 0, ballpitch, 1, 0
  4343.         Else
  4344.             If rolling(b) = True Then
  4345.                 StopSound("fx_ballrolling" & b)
  4346.                 rolling(b) = False
  4347.             End If
  4348.         End If
  4349.     Next
  4350. End Sub
  4351.  
  4352. '**********************
  4353. ' Ball Collision Sound
  4354. '**********************
  4355.  
  4356. Sub OnBallBallCollision(ball1, ball2, velocity)
  4357.     PlaySound("fx_collide"), 0, Csng(velocity) ^2 / 500, Pan(ball1), 0, Pitch(ball1), 0, 0
  4358. End Sub
  4359.  
  4360. '******************
  4361. ' RealTime Updates
  4362. '******************
  4363.  
  4364. Set MotorCallback = GetRef("RealTimeUpdates")
  4365.  
  4366. Sub RealTimeUpdates
  4367.     RollingUpdate
  4368.     BallShadowUpdate                                'ninuzzu - added ballshadow routine
  4369.     FlipperL.RotZ=LeftFlipper.currentangle          'ninuzzu - move flipper primitive in sync with VP flipper object
  4370.     FlipperR.RotZ=RightFlipper.currentangle         'ninuzzu - move flipper primitive in sync with VP flipper object
  4371.     FlipperLSh.RotZ=LeftFlipper.currentangle        'ninuzzu - move flipper shadow primitive in sync with VP flipper object
  4372.     FlipperRSh.RotZ=RightFlipper.currentangle       'ninuzzu - move flipper shadow primitive in sync with VP flipper object
  4373.  
  4374. End Sub
  4375.  
  4376. '*****************************************
  4377. '   Ball Shadow
  4378. '*****************************************
  4379.  
  4380. Dim BallShadow
  4381. BallShadow = Array (BallShadow1)                                                                        'ninuzzu - let's create an array of primitives, the number of primitives is equal to tnob
  4382. Dim ShadowSFW
  4383. ShadowSFW = 0  
  4384.  
  4385. Sub shadowTrig_Hit: ShadowSFW = 1: End Sub                                                                                              'ninuzzu- so in this case only one primitive, for 3 ball it will be BallShadow = Array (BallShadow1,BallShadow2,BallShadow3)
  4386. Sub shadowTrig_UnHit: ShadowSFW = 0: End Sub
  4387.  
  4388. Sub BallShadowUpdate()
  4389.     Dim BOT, b
  4390.     BOT = GetBalls                                                                                      'ninuzzu- this will return an array , the balls array, this is updated in real time
  4391.  
  4392.     ' render the shadow for each ball                      
  4393.     For b = 0 to UBound(BOT)                                                                            'ninuzzu - now let's link the ball array with the array of primitives; so for each ball in the array, do this
  4394.         If BOT(b).X < Table1.Width/2 Then
  4395.             BallShadow(b).X = ((BOT(b).X) - (Ballsize/6) + ((BOT(b).X - (Table1.Width/2))/7)) + 10      'ninuzzu - the shadow array will move left or right depending on the ball X position in the table
  4396.         Else
  4397.             BallShadow(b).X = ((BOT(b).X) + (Ballsize/6) + ((BOT(b).X - (Table1.Width/2))/7)) - 10
  4398.         End If
  4399.         BallShadow(b).Y = BOT(b).Y + 20                                                                 'ninuzzu - the shadow Y is at ball Y + 20 units lower
  4400.         BallShadow(b).Z = 1                                                                             'ninuzzu - the shadow Z is 1
  4401.    
  4402.         If (BOT(b).Z > 20 and ShadowSFW = 0)  Then                                                                          'ninuzzu - if the ball is falling through a hole, e.g. a subway, the shadow is not visible.
  4403.             BallShadow(b).visible = 1
  4404.         Else
  4405.             BallShadow(b).visible = 0
  4406.         End If
  4407.     Next
  4408. End Sub
  4409.  
  4410. '******************************
  4411. ' Diverse Collection Hit Sounds
  4412. '******************************
  4413.  
  4414. Sub aMetal_Wires_Hit(idx):PlaySound "fx_metalhit", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
  4415. Sub aRubber_Bands_Hit(idx):PlaySound "fx_rubber_band", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
  4416. Sub aRubber_Posts_Hit(idx):PlaySound "fx_rubber", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
  4417. Sub aRubber_Pins_Hit(idx):PlaySound "fx_postrubber", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
  4418. Sub aPlastics_Hit(idx):PlaySound "fx_PlasticHit", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
  4419. Sub aGates_Hit(idx):PlaySound "fx_Gate", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
  4420. Sub aWoods_Hit(idx):PlaySound "fx_Woodhit", 0, Vol(ActiveBall), pan(ActiveBall), 0, Pitch(ActiveBall), 0, 0:End Sub
Add Comment
Please, Sign In to add comment