Arngrim

Untitled

Nov 8th, 2016
239
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 165.68 KB | None | 0 0
  1. ' Müslifresser Softworx proudly presents...
  2. '
  3. ' B O O M E R A N G
  4. '
  5. ' a programming example (v1.4)
  6. '
  7. '
  8. 'pinball machine design (c)1974 by Bally
  9. 'version for Visual Pinball copyleft 2001 by CYBERYOGI =CO=Windler
  10.  
  11. 'This is seed-ware; with this I spread my knowledge to the mankind though that
  12. 'people can learn to make other great pinball emulations too,to preserve the
  13. 'experience of realizing and playing these beautiful historical games in virtual
  14. 'form and enable everybody to play them again...
  15. '
  16. '
  17. 'SOURCE CODE:
  18. 'This program was mostly written by myself but partly inspirated by the horrible
  19. 'spaghetti-code of an egomanic moron who really doesn't deserve to be mentioned
  20. 'here.
  21. '
  22. 'This source code is a piece of literate programming and should be quite self-
  23. 'explanatory (I hope :-) ) and even be suited as a tutorial example. I hope
  24. 'this will help other people to faithfully port and create many other good
  25. 'pinballs games on Visual Pinball.
  26. '
  27. 'This program is not really object oriented, but I attempted to make it as
  28. 'human-readable as possible. I therefore added lots of comments, gave most
  29. 'variables and functions understandable names and used functions for all code
  30. 'sequences those have a special meaning or are used multiple times. Although
  31. 'it would be conceptionally even more canonical, I did not allways use 1
  32. 'function per light to encapsulate its state (on or off) and the corresponding
  33. 'variable, because most lights are switched at only few places in the code,
  34. 'which stays understandable also without tons of functions.
  35. '
  36. 'I don't know how to do object orientation in Micro$ofts crappy Visual Basic,
  37. 'nor if the dialect of Visual Pinball would supports it at all. The last time
  38. 'I programmed in such a BASIC was on my Amiga in the very slow dialect Amiga
  39. 'BASIC 1.2 (yes, from Micro$oft), which definitely included no object orien-
  40. 'tation. But at least I used functions in the style of "setter methods" for
  41. 'all things those have a side effect (e.g. updating an on-screen counter or
  42. 'playing a sound) to achieve a consistent behaviour. To read values of vari-
  43. 'ables, I otherwise did not use functions in the style of "getter methods",
  44. 'but read them directly, because I hope that this works in this BASIC without
  45. 'nasty multi-threading side effects.
  46. '
  47. 'Notes:This game first jerked a lot on my AMD K6 300MHz because the resting pins
  48. ' of the captive bals were badly placed and surfaces badly aligned, which
  49. ' triggered numerical instabilities of Visual Pinbal those made these
  50. ' balls continuously bounce around and never come entirely to rest. Though
  51. ' accidently the CPU time of a 3er multi-ball play was consumed even when
  52. ' only one ball was moved by the player. Small changes of the pin shape
  53. ' and position reduce this problem dramatically,but any banal modifications
  54. ' of "Wall" objects on the playfield can make it neccessary to re-adjust
  55. ' the resting pins again.On my PC it still runs not perfectly smooth;espe-
  56. ' cially the timing of rhythmic sounds is often quite off.
  57. '
  58. ' Some sounds are based on another simulation of this games,and additional
  59. ' sounds were added from the Visual PinMAME default sample set. (They are
  60. ' likely inaccurate, but at least similar to a mechanical Bally pinball
  61. ' machine. I don't know if there were bells at all in Bally games of that
  62. ' era.) There are still some state wheel sounds fake and the volume ratios
  63. ' are bad,too. The coin sounds were faked by me.Various additional sounds
  64. ' were donated by Frank Menzler (thanks).
  65. ' The solenoid mains hum and buzz sounds were hand-made by me;I hope I
  66. ' didn't exaggerate them.But remember; an EM pinball hardware is NOT one
  67. ' of these gentle,harmless,noiseless 3.3V CMOS highly integrated microchip
  68. ' thingies where you fearlessly can touch contacts with bare finger during
  69. ' operation. Its nothing cute,but it is a loudly rumbling,mechanical,mains
  70. ' operated monstrosity consisting of rattling,revolving contact wheels and
  71. ' lots of green sparks spitting relays - and now don't come in mind to
  72. ' imagine these relays as those 2cm small,airtight sealed acryl plastic
  73. ' boxes with tiny,gold plated contacts inside you may know from your local
  74. ' electronics store - no,these relays are a bunch of finger-long,corroded
  75. ' looking metal leaf switches without any cover or protection,and inside
  76. ' the machine you can smell the odour of slowly toasting metal,pressboard
  77. ' formaldehyd and a little ozon that lets no occasion untried to make the
  78. ' silver balls less shiny and to attack the playfield rubber parts when
  79. ' they are made from pure noble natural latex. (Sorry for these explicit
  80. ' utterances,but there seem to be many PC users around those can't imagine
  81. ' that there was ever anything digital based on boolean logics besides
  82. ' multi-layer SMD mainboard equipped microprocessor operated micro-elec-
  83. ' tronics.)
  84. '
  85. ' In this code I often use the term "lamp" rather then "light" to name
  86. ' objects,because "light" can als be a verb which could sometimes result in
  87. ' confusion. With "light" I usually mean rather the actual state of a
  88. ' lamp or group of lamps than the physical lamp itself.
  89. '
  90. ' This program includes 2 switchable game rule sets - one is the authentic
  91. ' version from a real Bally "Boomerang" machine,the 2nd is a more challen-
  92. ' ging rule set created by me.The reason for this is that I originally in-
  93. ' tended to emulate the original pinball game as close as possible,but by
  94. ' the lack of access to the real machine I had partly defined my own rules.
  95. ' Later I was contacted by Jeff Stuecheli who explained me the original
  96. ' rules and behaviour of his "Boomerang" machine,just to discover that
  97. ' these authentic rules sounded very boring and way too easy to me.Though
  98. ' I decided to implement 2 switchable rule sets to make it possible to keep
  99. ' my old rules and despite provide an authentic emulation of the original
  100. ' game rules by Bally.I hope this doesn't complicate the source code too
  101. ' much;the program switches by a key press between both rule sets using the
  102. ' variable "ruleSet".Its value RS_authentic (=0) selects the authentic
  103. ' Bally rules,while the value RS_enhanced (=1) selects my own rule set.(If
  104. ' you intend to use source code parts for other games without rule selec-
  105. ' tion,you can remove the conditional statements those test the "ruleSet"
  106. ' value and simply use unconditioned statements for your game rules instead.)
  107. '
  108. ' Various options of the game can be adjusted in the "operator settings"
  109. ' section below.Many additional game machine dependant parameters can be
  110. ' changed in the "pinball machine constants" section;some depend on actual
  111. ' given game configurations (number of score displays on the backbox etc.)
  112. ' but others (especially boolean flags) often can be changed easily without
  113. ' modifying anything else. These are especially interesting when some
  114. ' generic behaviour details of a pinball machine are currently unknown and
  115. ' shall be adjusted later when anybody else finds them out.
  116. '
  117. ' On the table a huge decal that simulates the rims of the acryl parts,
  118. ' hinders the view onto the playfield.If you want to learn how this
  119. ' table is built (or just modify it),best first write down its coordinates
  120. ' etc. and remove the thing(resize to 10*10 or so works best and also speeds
  121. ' up rendering time - the original values in "Boomerang" are width 927.313,
  122. ' height 1503.13,rotation 0.) To adapt this program to a different pinball
  123. ' game,I strongly recommend to copy the new parts into this program and not
  124. ' vice versa because Visual Pinball 3 yet supports no export of graphics/
  125. ' sounds and (worse?) it is impossible to copy playfield objects with keeping
  126. ' their names,which makes modifications very awkward.
  127. '
  128. 'TECH INFO: (This is the world first EM pinball emulator!)
  129. '
  130. 'This piece of software is the possibly world first open source attempt to
  131. 'EMULATE the inner working of an electro-mechanical pinball machine. (The
  132. 'commercial PC game Amtex "Royal Flush" also looks like an emulation,but I am
  133. 'not sure.)
  134. '
  135. 'Electro-mechanical pinball machines contain neither ROMs nor anything computer-
  136. 'like,but my idea was to simulate at least the behaviour of the score motor (the
  137. 'camshaft-based main command sequencing unit in such a machine) to reproduce the
  138. 'general timing behaviour of the circuit. When correctly implemented,this
  139. 'automatically should e.g. play all sounds of the mechanism with the right timing
  140. '(except on my slow PC :S ) and also reproduce flaws like that certain targets
  141. 'don't score points when they are hit while the motor is still running.Though
  142. 'every mechanical sound you hear in this game is caused by an action performed
  143. 'by the internal mechanism (and not just because "it sounds cool" to play a pre-
  144. 'recorded sample of the entire reset sequence before the game starts).Listen
  145. 'carefully e.g. to the state wheel reset noises - they really depend on what the
  146. 'previous wheel rotation angle was.
  147. '
  148. '(If you have no clue of what I talk about,then urgently take a look at the
  149. 'great pinball repair site at http://marvin3m.com to learn how a real electro-
  150. 'mechanical pinball machine works internally.)
  151. '
  152. 'This emulation is certainly not very accurate,because I have neither access to
  153. 'pinball schematics nor to the real pinball machine nor I have time to work
  154. 'myself through EM pinball schematics (those are said to be really cryptic).
  155. 'Though I did not emulate every single relay,although most of them certainly
  156. 'have an equivalent in my code.Also the dependencies among the relays are
  157. 'not modelled when unknown or not important,because I don't know how to easily
  158. 'express in BASIC that when e.g. a relay turns a lamp off but a closed switch
  159. 'is wired in parallel it still supplies the lamp,that then the lamp must
  160. 'not turn off. My relays are just binary flags,though everything that shall be
  161. 'done by them must be done by hand in the code whereever their state is changed.
  162. '
  163. 'Certainly I know well about boolean logics in BASIC,but for making a perfect
  164. 'emulation it would be necessary to write somekind of digital circuit simulator
  165. '(like PSPICE) which can be fed with the relay wiring table from the original
  166. 'pinball machine schematics, and then would simulate the states of all relays
  167. 'and voltages on every single line when the states of the input lines change.
  168. 'Such an approach would be generally possible. (In the book "Structure
  169. 'and Interpretation of Computer Programs" by Abelson and Sussman is a Scheme
  170. '(LISP) program listing explained that does something similar.) But I have
  171. 'not the slightest imagination how the network-like wiring dependencies among
  172. 'relays could be described in a non-objectoriented BASIC program without ending
  173. 'up in a terrible chaos of a thousand of arrays.
  174. '
  175. 'The game logics description in an exact circuit emulator would also become a
  176. 'very abstract and badly understandable piece of code that would be almost
  177. 'useless for hobby programmers without schematics. Also because I wanted to
  178. 'write an understandable program that everybody can modify to add game rules
  179. 'or to simulate completely different mechanical pinball games with,I did not
  180. 'try to code such a number table driven monstrosity.
  181. '
  182. 'I saw now that it is possible to use classes in this BASIC and that there
  183. 'is even an "eval" command to implement a thunk concept (executing strings as
  184. 'code),which may be enough to port Abelson and Sussman's digital circuit
  185. 'emulator,but I have no time at the moment.If anybody else wants to implement
  186. 'a physically perfect emulation program - you are welcome to write it!
  187. '
  188. '
  189. 'Notes:I have not much time to answer questions about programming,nor to become
  190. ' coordinator of an open source team.I wrote this because it needed to be
  191. ' done,but now I have to prepare myself again for my thesis(development of
  192. ' a gesture and sing controlled VR music synthesizer program).I hope you
  193. ' can understand this source code without too much e-mail questions.
  194. '
  195. ' And for people those still don't get it: All the "TM" and "®" signs for
  196. ' fancy named features of this souce code etc. are a HOAX to kid capitalism.
  197. ' This is an open source project and free for everybody.Although it is
  198. ' almost unbelievable,some people indeed took this for real and flamed me
  199. ' because they thought it would be a nasty anti-open-source measure.In re-
  200. ' ality registering a trademark is (at least in Germany) a costly bureau-
  201. ' cratic act that takes at least some months and (by my knowledge) costs
  202. ' a fee of multiple hundred (when not thousands) of dollars. Thus trademar-
  203. ' king random words for code features of a free open source program is such
  204. ' a ridiculously absurd idea that I never came in mind that it was even re-
  205. ' motely possible that anybody could take this for real.I am a strict anti-
  206. ' capitalist and fight against any approaches to patent software algorithms
  207. ' or human genes or to trademark commonly used words to beat profit out of
  208. ' them and the like. The only genuine trademarks in my VP games are the
  209. ' ones those were made by the pinball machine manufacturers themselves.
  210. '
  211. '
  212. 'CREDITS:
  213. '
  214. 'Many thanks to Randy Davis,who created Visual Pinball and though made my work
  215. 'and the revival of all these great historical pinball games possible.(What a
  216. 'pity that there is no Linux version...) Also thanks to PeBo,who find out the
  217. 'trick how to simulate a realistic looking mechanical reel score counter,to
  218. 'Kurt Herman for the idea of round voxel graphics 3D targets and thanks to Boris
  219. '(?) for the highscore save and load routine.Thanks to Frank Gigliotti for the
  220. 'tip and picture for the shiny ball.Also thanks to the Visual PinMAME team for
  221. 'some sounds and especially very much thanks to Frank Menzler who donated the
  222. 'great Bally mechanism sound sample set from his nice "Fireball" VP game and
  223. 'also helped me with details about the Bally hardware.Thanks to Jeff Stuecheli
  224. 'for the detailed explanation of the original "Boomerang" game rules.
  225. '
  226. 'No thanks at all to an egomanic moron who refuses any programming help and
  227. 'flamed me instead by his disgustingly self-rightous false pride.(Though
  228. 'never forget - boomerangs always come back... ;-) )
  229. '
  230. '
  231. 'LICENCE: ("Seedware licence v1.1 for Visual Pinball games")
  232. '
  233. 'This is seed-ware; with this I spread my knowledge to the mankind though that
  234. 'people can learn to make other great pinball emulations too,to preserve the
  235. 'experience of realizing and playing these beautiful historical games in virtual
  236. 'form and enable everybody to play them again.
  237. '
  238. 'For video arcade games there is MAME,for home computers and consoles there is
  239. 'MESS and also computerized pinball machines have been emulated with PinMAME,but
  240. 'yet there was no solution for electro-mechanical pinball machines.With this
  241. 'work I hope I can help to close the gap by providing a sort of framework for
  242. 'building other emulations from.
  243. '
  244. 'You are permitted and encouraged to use parts of this source code or other
  245. 'parts of this game in other Visual Pinball tables and publish them for non-
  246. 'commercial purposes, so far you give me (CYBERYOGI =CO=Windler) credits and
  247. 'include the URL of my site (see below) into the derived work.
  248. '
  249. 'You are also permitted to port this code to other programming languages and
  250. 'hardware platforms to hopefully soon get rid of that "MS"-desease it is yet
  251. 'still bound to.If you publish a modified version of a seedware game that was
  252. 'created by someone else,you must use a different file name than the original
  253. 'work to prevent confusion,and you also must include your e-mail adress and a
  254. 'download URL or other description to enable people to obtain the unmodified
  255. 'version of it.
  256. '
  257. 'All source code parts must be documented using code comments (in the style of
  258. 'literate programming) in a way that other programmers can understand them.When
  259. 'you change code parts,then you must also update the documentation to explain
  260. 'the changes.
  261. '
  262. 'If you emulate a pinball machine that has a certain feature (e.g. playfield
  263. 'illumination) but you are too lazy to implement it in your playfield and
  264. 'backbox model,then you must NOT delete the corresponding routines from this
  265. 'source code,but instead implement them as empty,commented dummy routine to
  266. 'permit everybody to easily add the missing features later.
  267. '
  268. 'This program and its components are distributed "as is" and come with absolute-
  269. 'ly no warranties.
  270. '
  271. 'All copyleft and copyright messages of authors and co-authors must be kept in-
  272. 'tact.
  273. '
  274. 'This seedware and its derived works can NOT be sold for profit without my ex-
  275. 'plicit written permission and the permission of all co-authors.
  276. '
  277. 'All new written program code that includes portions of this code or is based
  278. 'on it (so far published) must also be releases as open source under this
  279. 'license to follow the universal law of software and let the seed grow and
  280. 'multiply.
  281. '
  282. '(If you make such works,please inform me by e-mail because I would like to
  283. 'receive a copy of your work too.But never(!) e-mail to me a VPT file itself
  284. 'or other huge data without my prior explicit request,because huge files
  285. 'could result in a mail box crash which could endanger me to loose e-mails.
  286. 'Instead better e-mail me a download URL of your VPT file.)
  287. '
  288. 'When you make a new pinball game derived from this seedware,you have the right
  289. 'to exclude art components of your own work from open source in a way that they
  290. 'may not be modified and/or not removed from your pinball game and/or not in-
  291. 'cluded into different pinball games,so far these components are mainly copyrigh-
  292. 'ted by you and NOT substancially based on the artwork of a given pinball ma-
  293. 'chine.The here described "art components" cover only pictures,text and music
  294. 'elements - they can never cover technical parts of the source code nor technical
  295. 'playfield objects nor sound samples taken from a pinball machine.
  296. '
  297. 'So far you use seedware code to upgrade a given Visual Pinball game that initi-
  298. 'ally was created by 3rd persons (not by you) under a different open source li-
  299. 'cence,then you must mention the game's original author and explain in the merged
  300. 'code that the original parts of the game are not seedware and provide a source
  301. '(e.g. download URL) to enable people (e.g. other programmers) to obtain the un-
  302. 'modified version under its original licence unless the initial creator of the VP
  303. 'game accepts the upgraded version as the new official release of his game and
  304. 'allows to change the new version's licence entirely into seedware.
  305. '
  306. 'It is FORBIDDEN to use parts of seedware for purposes or products those severely
  307. 'glorify brutality or senseless brain destruction(e.g. alcohol,glutamate products
  308. 'or other drugs),cruelty,environmental destruction,machismo or other socially
  309. 'harmful things.
  310. '
  311. 'If you intend to remake a historical pinball game that deals with such a topic,
  312. 'you may possibly get my permission by e-mail, but in this case you must
  313. 'include a visible warning into the game that its topic is ok only within a
  314. 'piece of pinball game history but not in reality.
  315. '
  316. 'The following messages and sayings must not be modified or removed from this
  317. 'game:
  318. '
  319. '-the copyleft message including "by CYBERYOGI =CO=Windler,teachmaster of
  320. ' Logologie - the 1st cyberage-religion!"
  321. '-"ABANDON THE BRUTALITY!"
  322. '-the formula "V.R. HERE!" (hidden on the backdrop picture).
  323. '-the disclaimer text on the intro screen and that I respect shamanic cultures
  324. ' and love this game's artwork.
  325. '-the use of "washers" instead of coins in texts of this game,including:
  326. ' "Washers® - the arcade operator's least beloved foreign currency...".
  327. '
  328. 'The Cyberyoga icon (the green,drawn line being with heart and smily) in this
  329. 'game is copyrighted by me (=CO= Windler) and may not be modified or spread in
  330. 'modified state without my permission. :)-E-8
  331. '
  332. '
  333. ' MAY THE SOFTWARE BE WITH YOU!
  334. '
  335. '*============================================================================*
  336. 'I CYBERYOGI Christian Oliver(=CO=) Windler I
  337. 'I (teachmaster of LOGOLOGIE - the first cyberage-religion!) I
  338. 'I ! I
  339. '*=============================ABANDON=THE=BRUTALITY==========================*
  340. ' {http://www.informatik.fh-hamburg.de/~windle_c/e_index.html}
  341. '
  342.  
  343.  
  344. option explicit
  345. '(This option makes declarations of global variables strictly neccessary - it
  346. 'needs a little more typing work but prevents awful to find bugs by misspelled
  347. 'variable names.Due to all undeclared variables in BASIC are initialized with 0
  348. 'or false before use,without this you otherwise would NEVER get errors by mis-
  349. 'spelled variables those were intended to be anyway false or 0 - only the
  350. 'program would behave wrong whithout any obvious reason."option explicit" is
  351. 'the only protection against this.)
  352.  
  353. On Error Resume Next
  354. ExecuteGlobal GetTextFile("controller.vbs")
  355. If Err Then MsgBox "You need the controller.vbs in order to run this table, available in the vp10 package"
  356. On Error Goto 0
  357.  
  358. Const cGameName = "boomerang_1974"
  359.  
  360. 'operator settings
  361. '=================
  362.  
  363. 'for authentic game rules mode:
  364. const ballsPerGame0 =3 'max. 5 balls (The counter can't display more.)
  365. const specialWinsCredit0=false 'true: hitting the special wins a free credit
  366. const specialShootAgain0=true 'true: hitting the special wins an extra ball
  367. const specialWinsPoints0=false 'amount of points won by hitting a special (false=none)
  368.  
  369. 'for enhanced game rules mode:
  370. const ballsPerGame1 =3 'max. 5 balls
  371. const specialWinsCredit1=true 'true: hitting the special wins a free credit
  372. const specialShootAgain1=false 'true: hitting the special wins an extra ball
  373. const specialWinsPoints1=10000 'amount of points won by hitting a special (false=none)
  374. const topHoleShootAgain =15000 'bonus value to make tophole give a "shoot again" (false=off)
  375. const bonusClearsBumper =true 'true: bonus at topHoleShootAgain value turns center bumper
  376. 'light off.
  377. 'common:
  378. const coinChute1Credits=1 'credits for inserting a small washer
  379. const coinChute2Credits=2 'credits for mid-sized washers
  380. const coinChute3Credits=12 'credits for large washers (=false "removes" a slot)
  381. const maxBonus =15000 'According to rules flyer the machine can only count to 15000.
  382. const specialLitValue =15000 'bonus value to light the captive ball lane specials(false=off)
  383. const knockerEnabled =true 'sounds a loud separate knocker when winning extra credits
  384. const tiltEnabled =true
  385. const tiltSensitivity =4 'higher value = lower sensitivity
  386. const tiltSwingDecay =0.2 'higher value = faster pendulum decay
  387. const initBonusIs1000 =true 'true: each round starts with bonus 1000.
  388. const digitMatchEnabled =true 'true: score digit matching can win free credits
  389. const extraDigitsEnabled =true 'true: small numbers display missing higher score digits
  390. const chimeForExtraDigits =true 'true: a gong is played for extra (overflow) digits
  391. const lossyScoreCounter =false'true: score counter skips recognizing too fast points to
  392. 'simulate mechanical limitations. (This simulates a nasty
  393. 'technical flaw - PLEASE keep this turned off!)
  394. const ballRollNoisesEnabled=true'If you don't like the ball roll noises or the game runs
  395. 'too jerky on a slow PC,set this false to disable.
  396. const highScoreSoundEnabled=true'plays sound effect after highscore
  397. const highScoreFileName ="Boomerang_highscore.txt"
  398. 'name of highscore save file,or false to disable save/load
  399. '-----------------------------------------------------------------------------------------------
  400.  
  401. 'Some general Visual Pinball tips:
  402. '
  403. 'Before you design a Visual Pinball playfield,check that the picture is not
  404. 'distorted;otherwise use a graphics program to remove the distortion.Due to
  405. 'parallax effects higher playfield parts on photos tend to appear closer to
  406. 'rims than low parts.Though e.g. the playfield bottom area(near the outhole)
  407. 'which should be a vertical wall can look like a slanted floor area.Cut this
  408. 'piece out and paste the rest of the photo together again to make the playfield
  409. 'shorter.(Though nudging the ball up again will work better.)
  410. 'In Visual Pinball the "Table" width and height parameters can be used to fix a
  411. 'wrong width to height ratio.Also the relative playfield size in ratio to the
  412. 'ball is determined this way.Adjust these values so that round bumper tops on
  413. 'the photo will match with the shape of a real bumper.Use a Kicker hole to see
  414. 'how large a ball is in ratio to the photo.
  415. '
  416. 'Decals can be used on lamps or to display a high resolution detail on a low-
  417. 'res background to save memory.To scale a decal for use as a playfield part or
  418. 'backbox part,use the following formula:
  419. '
  420. 'scaledLength = areaLength*(decalLength/pictureLength), where
  421. '
  422. ' length = a height or width
  423. ' areaLength = length of the entire playfield or backdrop in the editor
  424. ' decalLength = length of the decal (in pixels)
  425. ' pictureLength= length of the backdrop or playfield picture the decal was
  426. ' cut out from (in pixels)
  427. '
  428. '(When you use a decal with n times the picture resolution,you have to devide
  429. 'the result by n afterwards.)
  430.  
  431. 'In a finished pinball game at least the picture of the flippers,spinners and
  432. 'bumpers should always be removed from the playfield floor photo,because they
  433. 'make the game look really stupid.To stay capable to adjust these objects
  434. 'correctly,set the Table picture to an unretouched "testFloor" version of the
  435. 'picture file while editing the playfield.Use JPEG pictures instead of BMP
  436. 'for all non-transparent textures to reduce the VPT file size.
  437. '
  438. 'When you start making a playfield,never use the "outerWall" object for model-
  439. 'ling playfield "buildings" unless you are entire sure that their height matches
  440. 'the rim and that they indeed consist of a single block that is not separated
  441. 'from the rim,because doing so makes later changes of these buildings extremely
  442. 'difficult and also side colours can not be set separately when everything was
  443. 'made from 1 piece.
  444. '
  445. 'Normal buildings on the playfield should not be made of one solid block but
  446. 'instead consist of at least 2 levels (middle and top) of those the middle
  447. 'level has a lightbulbish beige colour while the top level has the plastic
  448. 'texture on it.In its simplest form this is really easy to do;simply copy the
  449. 'building with cut and paste to the same place,and then change the height
  450. 'of the top part to about from bottom 48 to top 50 and the height of the middle
  451. 'part to about bottom 23 to top 27.Set the top colour of the resulting pane to
  452. 'about RGB 238,205,142.This simple trick makes playfields look much more
  453. 'realistic without much effort.
  454. '
  455. 'It looks even better to also add cylindrical posts between these panes,but
  456. 'don't use excessively many control points for them,because they can slow down
  457. 'Visual Pinball.The modelling of playfield objects those can neither be seen nor
  458. 'hit with the ball should be avoided or kept simple to keep the count of control
  459. 'points low for better performance.
  460. '
  461. 'For further important tips about playfield colour adjustment and lighting see
  462. 'resetLights() and lightCenterBumper().
  463. '
  464. 'For normal playfield "Wall" parts use elasticity 0.3,for fixed rubbers 0.4 and
  465. 'to the free,straight part of long rubbers add a slingshot property with sling-
  466. 'shot force 3 to get correct bouncing behaviour.Also targets should have an
  467. 'elasticity value about 0.35 to simulate the rebounce force of the leaf spring
  468. 'behind them.
  469. '
  470. 'To make a playfield appear realistic,a view angle about "Inclination 31" and
  471. '"Field Of View 66" should be used,because only this way the length of the
  472. 'playfield stays realizable.Many VP games try to make the playfield area on the
  473. 'screen bigger by choosing values those let the front and back edge of the table
  474. 'appear with the same width.But this makes the table look almost square and the
  475. 'spacial depth of the playfield gets completely lost.When in the opposit the
  476. 'front edge of the playfield occupies the entire screen to maximize the width
  477. 'by a very flat angle,the game becomes badly overviewable and looks like watched
  478. 'by a child.(This may remind your childhood pinball memories,but playing is dif-
  479. 'ficult this way.) Also top view should be avoided,because it is very unrealistic
  480. 'and uses only a tiny fraction of the screen for the playfield which makes it
  481. 'look very small and unspectacular.When a player wants to take an undistorted
  482. 'top view look onto the playfield,he should download a pinball flyer photo
  483. 'instead or watch the playfield in the editor. The playfield view angle during
  484. 'game is intended for playing and not for watching it in all details.
  485. 'To simulate a left and right playfield rim makes the machine look much more
  486. 'realistic;unfortunately a Visual Pinball bug displays any objects closely left
  487. 'or right outside the playfield bounds only in black.To define the table size
  488. 'wider would be possible,but this way all table texture pictures would need to
  489. 'be extended with a blank left and right area too.
  490. '
  491. 'The captive ball lanes in this game have a kink in their outer wall - this
  492. 'is a trick to make it more difficult to shoot their ball up to the top,because
  493. 'these lanes are intended as skill shots and though the the kink slows down the
  494. 'captive ball when it isn't hit at a particular angle.
  495. '
  496. 'The way a backglass shall be displayed depends very much on its motive.
  497. 'Important is that the score counters etc. must not become too small.In this
  498. 'game("Boomerang") I devides the backglass landscape into 2 halfs and addeded
  499. 'some missing graphics in the middle to create a rather impressionistic than
  500. 'realistic appearance of the game.But for other backglasses with more central
  501. 'motives this concept can also look really awful (look e.g. at the terribly
  502. 'mutilated graphics of VP "Magnotron" v1.1 by Hugh).For such backglasses the
  503. 'central picture should rather stay in 1 piece,but the score counters etc.
  504. 'in this case should not be displayed simply on the backglass,because this
  505. 'would make them way too small.Important is not that the backglass appearance
  506. 'stays completely authentic,but rather that the athmosphere of the game is
  507. 'kept intact.Way too small displays disturb this much more than a reasonably
  508. 're-arranged backglass layout.
  509. '
  510. 'When you use textboxes,always check how they look at different screen
  511. 'resolutions;due to the font size can slightly vary at different resolutions,
  512. 'sometimes characters or even entire words can disappear when they don't fit
  513. 'into their textbox at certain resolutions.Therefore make textboxes better a
  514. 'bit too large than too narrow.
  515.  
  516.  
  517. 'pinball machine constants: (for code portability)
  518. '--------------------------
  519. const maxCredits=25 'My Bally "Champ" could count credits up to 25(?).
  520. const maxPlayers =4 'player count of this machine
  521. const maxBalls =5 'range of the ball counter on this machine
  522. const matchedScoreDigit=1 'digit that is compared during matching
  523. const bonusStepWidth=1000 'amount by that the bonus ladder advances
  524. const coinMech1Time=500 'delay time for inserted coin (must be >0)
  525. const coinMech2Time=500
  526. const coinMech3Time=500
  527. const spinnerResetTime=7 'speed of the spinnner state wheel reset.
  528. const playerResetTime=14 'speed of the player state wheel reset.
  529. const playerUpResetTime=12 'speed of the player-up state wheel reset.
  530. const ballCounterResetTime=16 'speed of the ball counter state wheel reset.
  531. const creditAddTime=69 'speed of the credit counter wheel
  532. const ballTexture="shinyBall" 'picture for the balls
  533. const knockerKnocksAlways =false 'true: knocker knocks also by insert coin
  534. const smallCoinStartsGame =true 'true: inserting a 1 credit coin behaves like start button press
  535. const overPayBlocksChutes =true 'true: at maximum credit coins are rejected
  536. const startButtonQuitsGame =true 'true: a running game can be reset by start button presses
  537. const highScoreEnabled =true 'when the game contains a highscore display
  538. const shootAgainStaysLit =true 'true: after next ball "shoot again" lamps stay lit
  539. 'until scoring points,else they switch off immediately
  540. const restartWhen0Pts =true 'true: player shoots again when ball scored no points
  541. const tiltClearsPilotLamps =true 'true: tilt clears the playfield pilot lamps (typical for Bally)
  542. const tiltClearsIllumination=true 'true: illumination switches off when tilt
  543. '(this is not authentic Bally,but looks great)
  544. const tiltPreventsExtraBall =true 'true: during tilt no new extra ball can be won
  545. const tiltVoidsExtraBall =false 'true: like above,but a won extra ball is void by tilt
  546. const tiltVoids0PtsRestart =true 'Normally any player can shoot again when the round
  547. 'scored no points. When true a tilt prevents this.
  548. const tiltClearsDoubleBonus =true 'true: counts down bonus ladder faster when tilt
  549. const tiltPreventsAddBonus =true 'true: bonus ladder can't increment during tilt
  550. const noTiltDuringBonus =false 'true prevents tilt during bonus ladder countdown
  551. const credit0UnlightsCounter=true 'true: credit counter light switches off when credit=0
  552. const creditOverpayBounce =false 'true: credit counter reel rattles by overpaying
  553.  
  554. 'score counter mechanism:
  555. const minCounterNr =1 'set this 0 when an additional score counter 0 exists;
  556. 'e.g. as a highscore display
  557. const reelsPerCounter=5 'digits per score counter
  558. const reelSpinTime =55 'normal score counter reel rotation speed (was 55)
  559. const reelCarryTime =5 'faster score counter reel speed for carry (was 5)
  560. const scoreMotorTime =24 'speed with that the score motor advances
  561. '(I don't know if this is mains frequency dependant,because many
  562. 'sound samples in VP games click with different speeds.)
  563. const motorStepWidth =10 'degree with that the simulated score motor wheels
  564. 'turn by each timer step (Never change this unless you
  565. 'adapt the camshaft simulations too!)
  566. const reelChimeForCarry=false 'true: play score chime also for digits flipped by carry
  567. const maxScoreReelNoises=9 'number of sound samples for clicks of multiple reels
  568.  
  569. 'game rule sets: '(These are the values for ruleSet.)
  570. const RS_authentic=0 'the original Bally "Boomerang" rule set (too easy =>boring)
  571. const RS_enhanced =1 'enhanced game rules by =CO=Windler (more challenging)
  572.  
  573.  
  574. 'declarations of global variables:
  575. '---------------------------------
  576. ' Variables defined within procedures stay local. Local variables for internal
  577. ' use within procedures must never be declared here, because this would make
  578. ' them global and though could result in ugly side effect bugs due to a pinball
  579. ' game uses massive multi-threading.Variables become global when they are either
  580. ' declared or first used (assigned) outside all procedures.
  581.  
  582. 'mechanical score counter variables:
  583. dim scoreDigit (4,5) 'holds the digit values;highest index stores all extra digits
  584. dim carry (4,5) 'carries are 1 when 1 will be added to next digit,else 0
  585. dim stuckScoreReel(4,5) 'counting semaphores for modelling stuck counter reels
  586. dim scoreReelTop (4,5)
  587. dim scoreReelMain (4,5)
  588. dim scoreReelLow (4,5)
  589.  
  590. 'These arrays must have the numerical dimension values of (maxPlayers,
  591. 'reelsPerCounter);inserting the constant names directly doesn't work in this
  592. 'crappy Micro$oft BASIC.
  593.  
  594. dim scoreReelBorder(4,2)
  595. 'Array must have dimensions scoreReelBorder(maxPlayers,2).
  596.  
  597. dim scoreReelChime(5)
  598. 'Array must have dimensions of reelsPerCounter.
  599.  
  600. dim scoreReelNoise(9) 'sample name strings for 1 to 10 simultaneous reels clicks
  601. 'Array must have dimensions of maxScoreReelNoises
  602.  
  603. dim scoreAddTimer(4)
  604. dim scoreSpinTimer(4)
  605. dim reelTime(4) 'with this speed the counter reels rotate
  606. dim unaddedScore(4) 'this buffers the remaining score to add
  607. dim scoreIsCounting(4) 'flag signals that a player's counter has not finished counting yet
  608. 'These arrays must have dimensions of maxPlayers.
  609.  
  610. dim scoreResetNumber 'will contain as many "1" as value of reelsPerCounter
  611.  
  612. dim reelClicks 'actual count of counter click noises to play
  613. dim stuckReelsBuzz 'actual count of stuck reels to simulate solenoid buzz
  614.  
  615. 'relays:
  616.  
  617. dim gameOver 'true when game over
  618. dim gameIsResetting '"reset relay", true when the counters etc. are resetting
  619. dim tilt 'true when game in tilt state
  620. dim newRound 'true when bonusses are collected by score motor
  621. dim matching 'true during end game digit matching
  622. dim creditTaken '"credit relay", true when a credit from the counter is
  623. 'used to start the game)
  624. dim illumination 'true when the general illumination(plastics etc.) is lit
  625. dim centerBumperLit
  626. dim doubleBonus
  627. dim specialHit
  628. dim extraBallWon
  629. dim shootAgain 'when the actual round is a shoot again
  630. dim add500 'relays for points to be added by the score motor
  631. dim add3000
  632.  
  633. dim A 'target A..D hit
  634. dim B
  635. dim C
  636. dim D
  637. dim A_lock 'these prevent adding Bonus twice when captive ball rolls back
  638. dim B_lock
  639. dim C_lock
  640. dim D_lock
  641.  
  642. 'other flags:
  643.  
  644. dim reelChimeEnabled 'signals that counter digits play a chime sound
  645. dim playfieldLightsEnabled 'true enables the various pilot lamps on playfield
  646. dim backboxLightsEnabled 'true enables various display lamps in the backbox
  647. dim creditsAddable 'true when credit counter is ready to add new credits
  648. dim ballInTopHole
  649. dim ballInDrain
  650. dim keepMotorRunning 'lets the score motor turn another round
  651. dim leftExtraBallLit
  652. dim rightExtraBallLit
  653. dim rollover1kLit
  654. dim flippersEnabled
  655. dim leftFlipButton 'true when button pressed
  656. dim rightFlipButton 'true when button pressed
  657. dim leftFlipperUp 'true when the flipper is up
  658. dim rightFlipperUp
  659. dim ballBefore10p1 'true when ball lies in front of the center rubbers
  660. dim ballBefore10p2
  661. dim ballBefore10p3
  662. dim ballBefore10p4
  663.  
  664. 'stuck score reel semaphores: (1 per rollover switch with changeable score)
  665.  
  666. dim triggerLeft1kStuck
  667. dim triggerRight1kStuck
  668.  
  669. 'other state variables:
  670. dim scoreMotorAngle 'position of the score motor camshaft wheels
  671. '(0 when motor is at stand-still position)
  672. dim highScore(2) 'highscores for both rule sets
  673. dim rulesSwitchable 'true while rules can be switched (not during game)
  674. dim ruleSet 'the currently active rule set (0=authentic,1=advanced)
  675. dim bonus
  676. dim tiltSwing
  677. dim tiltHasEffect
  678. dim spins
  679. dim ballCount
  680. dim credits
  681. dim specialLit
  682. dim currentPlayer
  683. dim players
  684. dim unaddedCredits
  685. dim matchNumber 'actual pseudo random number of the matcher state wheel
  686. dim knockerActive 'false when inserting coin to disable the separate (loud) knocker
  687. dim ballCreated
  688.  
  689. 'program initialization:
  690. '-----------------------
  691. highScore(RS_authentic)=0
  692. highScore(RS_enhanced )=0
  693. ruleSet=RS_authentic
  694. scoreMotorAngle=0
  695. keepMotorRunning=false
  696. triggerLeft1kStuck =0
  697. triggerRight1kStuck=0
  698. credits=0
  699. unaddedCredits=0
  700. creditsAddable=true
  701. creditTaken=false
  702. knockerActive=true
  703. matching=false
  704. illumination=true 'else setIllumination false will be ignored
  705. tiltSwing=0
  706. tiltHasEffect=false
  707. tilt=false
  708. If B2SOn Then
  709. Controller.B2SSetTilt 33,0
  710. Controller.b2ssetdata 1,1
  711. End If
  712. leftFlipButton =false
  713. rightFlipButton=false
  714. flippersEnabled=false
  715. gameOver=true
  716. players=1
  717. currentPlayer=1
  718. ballCount=1
  719. ballBefore10p1=false
  720. ballBefore10p2=false
  721. ballBefore10p3=false
  722. ballBefore10p4=false
  723.  
  724. ballInDrain=true
  725. ballCreated=false 'no ball on playfield created yet.
  726.  
  727. matchNumber=int(10*rnd())
  728. spins=int(11*rnd())
  729. 'In a real pinball machine these values stem from the previous game.
  730.  
  731.  
  732. sub theTable_init()
  733. LoadEM
  734. ' This is called by the pinball table at program start to generate the 2
  735. ' captive balls etc.
  736. setIllumination false
  737. scoreMotor.interval=scoreMotorTime
  738. clickTimer.interval=2
  739. spinnerReset.interval =spinnerResetTime
  740. playerCounterReset.interval=playerResetTime
  741. playerUpReset.interval =playerUpResetTime
  742. ballCounterReset.interval =ballCounterResetTime
  743. creditAdder.interval =creditAddTime
  744. coinMech1.interval=0
  745. coinMech2.interval=0
  746. coinMech3.interval=0
  747.  
  748. initializeScoreCounters
  749.  
  750. leftCBallKicker.createBall.image=ballTexture
  751. leftCBallKicker.kick 90,6
  752. rightCBallKicker.CreateBall.image=ballTexture
  753. rightCBallKicker.kick -100,6
  754.  
  755. tiltTimer.interval=200
  756. tiltTimer.enabled=tiltEnabled 'true or false
  757.  
  758. showKeySettings
  759. shootAgainText.text=""
  760. If B2SOn Then Controller.B2SSetShootAgain 36, 0
  761. setEndMatchLights(-1)
  762. If B2SOn Then Controller.B2SSetBallInPlay 32, 0
  763. if highScoreFileName<>false then LoadGameStats UserDirectory,highScoreFileName
  764. spinCreditReel
  765. doGameOver
  766. setGameRules ruleSet 'updates the rules display
  767.  
  768. showIntro.enabled=true 'timer to pop up the intro text requester
  769. end sub
  770.  
  771.  
  772. 'Button press and release routines:
  773. '----------------------------------
  774.  
  775. sub theTable_KeyDown(ByVal keycode)
  776.  
  777. ' keysBox.text=keycode 'TEST!!!
  778.  
  779. 'Coin inserted:
  780. if keycode = 2 and coinChute1Credits<>false then coinMech1.enabled=true 'key "5"
  781. if keycode = 3 and coinChute2Credits<>false then coinMech2.enabled=true 'key "6"
  782. if keycode = 4 and coinChute3Credits<>false then coinMech3.enabled=true 'key "7"
  783.  
  784. 'By setting a coinChuteXCredits constant false,a coin slot can be "removed" to
  785. 'simulate a machine with less coin slots.
  786.  
  787. 'Start button pressed:
  788.  
  789. if (keycode = 2) and credits > 0 and not gameIsResetting then
  790. 'A start button press starts a new game after game over or adds a player
  791. 'to an already started game so far it is still playing the 1st ball.For
  792. 'both actions a credit is needed,that will be taken off the credit counter.
  793.  
  794. if startButtonQuitsGame or gameOver or (ballCount=1 and players<maxPlayers) then
  795. 'When startButtonQuitsGame is set false,a running game can be canceled by
  796. 'pressing the start button again when the maximum number of players already
  797. 'has been added or the ball counter has advanced beyond the 1st ball.
  798.  
  799. setRelay creditTaken,true
  800. creditButtonRoutine
  801. end if
  802. end if
  803.  
  804. 'select game rules:
  805. if (keycode=50) and rulesSwitchable then 'key "M"
  806. if ruleSet=RS_authentic then
  807. setGameRules RS_enhanced
  808. else
  809. setGameRules RS_authentic
  810. end if
  811.  
  812. playSoundMonophonic "diverter"
  813. saveGame
  814. end if
  815.  
  816. 'rule sheet: (game rules)
  817. if keycode = 19 then showRuleSheet 'key "R"
  818.  
  819.  
  820. if keycode = plungerKey then
  821. plunger.PullBack
  822. end if
  823.  
  824. if keycode = leftFlipperKey then
  825. leftFlipButton=true
  826. if flippersEnabled then moveLeftFlipper true
  827. if gameOver then setIllumination true
  828. end if
  829.  
  830. if keycode = rightFlipperKey then
  831. rightFlipButton=true
  832. if flippersEnabled then moveRightFlipper true
  833. end if
  834.  
  835. if keycode = LeftTiltKey then
  836. Nudge 90, 2
  837. nudged
  838. end if
  839.  
  840. if keycode = RightTiltKey then
  841. Nudge 270, 2
  842. nudged
  843. end if
  844.  
  845. if keycode = CenterTiltKey then
  846. Nudge 0, 2
  847. nudged
  848. end if
  849. end sub
  850.  
  851.  
  852. sub theTable_KeyUp(ByVal keycode)
  853.  
  854. if keycode = plungerKey then
  855. playSoundMonophonic "plunger"
  856. plunger.fire
  857.  
  858. 'Hint: When you have a combined sound sample of the plunger and ball rolling
  859. ' noise,please split it up into 2 samples and only play the plunger sound
  860. ' when no ball is in the firing chute - it sounds so silly when the plunger
  861. ' always starts a ball rolling noise when there is no ball in the chute.
  862. ' Instead place an overroll trigger in the chute that activates the rolling
  863. ' noise sample only when touched by the ball itself.
  864. end if
  865.  
  866.  
  867. if keycode = leftFlipperKey then
  868. leftFlipButton=false
  869. if flippersEnabled then moveLeftFlipper false
  870. end if
  871.  
  872. if keycode = rightFlipperKey then
  873. rightFlipButton=false
  874. if flippersEnabled then moveRightFlipper false
  875. end if
  876. end sub
  877.  
  878.  
  879. 'target hit routines: (called when a ball hits playfield objects)
  880. '--------------------
  881.  
  882. sub centerBumper_Hit()
  883. playSound SoundFXDOF("Bumper",107,DOFPulse,DOFContactors)
  884. DOF 108, DOFPulse
  885. if centerBumperLit then
  886. ' playSound "Bell100"
  887. addScore 100
  888. end if
  889. end sub
  890.  
  891.  
  892. sub topHoleKicker_Hit()
  893. ' triggered by top hole
  894.  
  895. stopBallRollNoises
  896.  
  897. rollover1kLit = true
  898. setLight lampLeft1k ,true
  899. setLight lampRight1k,true
  900.  
  901. lightCenterBumper true
  902. 'This is just a guess from Bally "Champ",because on the bumper stands "when
  903. 'lit", therefore there must be anything in the game to light it.
  904. '
  905. 'I was told later that on the original pinball machine this bumper stays
  906. 'always lit,but I kept this in my enhanced rule set,because it is much more
  907. 'fun this way.
  908.  
  909. if ruleSet=RS_enhanced and topHoleShootAgain<>false and bonus>=topHoleShootAgain then setExtraBall true 'Shoot Again!
  910.  
  911. 'This rule was created by me from Bally "Champ",because otherwise there would
  912. 'be no "real" extra balls in the game. The outlane lamps basically do not more
  913. 'than a kind of ball saver;they just temporably disable the outlane danger
  914. 'but never give an extra ball without loosing the current ball in play.
  915. '
  916. 'The real pinball machine can instead make the captive ball lane specials
  917. 'earn an extra ball,therefore I use topHoleShootAgain only in my enhanced
  918. 'game rule set.
  919.  
  920. ballInTopHole=true
  921. scoreMotor.enabled=true '3000 points are added by the score motor
  922. end sub
  923.  
  924.  
  925. sub leftSlingshot_slingshot()
  926. ' playSound "bell10"
  927. playSound SoundFXDOF("bigSlingshot",103,DOFPulse,DOFContactors)
  928. DOF 105, DOFPulse
  929. addScore (10)
  930. end sub
  931.  
  932. sub rightSlingshot_slingshot()
  933. ' playSound "bell10"
  934. playSound SoundFXDOF("bigSlingshot",104,DOFPulse,DOFContactors)
  935. DOF 106, DOFPulse
  936. addScore (10)
  937. end sub
  938.  
  939.  
  940. sub spinner1_Spin()
  941. ' playSound "bell100"
  942. playSound "soloff2"
  943. DOF 109, DOFPulse
  944. if centerBumperLit then addScore 100 else addScore 10 'this rule is a guess
  945. incrementSpinner
  946. end sub
  947.  
  948. ' The disc-shaped targets on the playfield are composite objects consisting of
  949. ' multiple vertically intersecting rectangular blocks.The blocks intersect each-
  950. ' other to keep the count of controll points reasonable low,because too many of
  951. ' them can slow down the game by too much CPU load. Unlike round decal props
  952. ' stuck on the backdrop over square targets in other games,these ones are ge-
  953. ' nuine 3 dimensional objects those can be viewed at any angle.The targets have
  954. ' a higher elasticity value than normal walls to simulate the rebounce of the
  955. ' metal leaf spring contact.
  956. '
  957. ' Due to the active part of these targets is as well their horizontal middle
  958. ' stripe as the black center spot(bolt),there are 2 hit subroutines for each
  959. ' target neccessary to recognize hits on both parts.In the below code I
  960. ' therefore each time call the subroutine of the main part of the same target
  961. ' when its center spot is hit,to make maintainance easier.The center spot
  962. ' has always the same name as the target but with the prefix "target2" instead
  963. ' of "target".
  964. '
  965. ' Tip: You can copy these targets into other Visual Pinball games using copy
  966. ' and paste.
  967.  
  968. sub targetCenter1_hit()
  969. msgbox("test")
  970. playSound SoundFXDOF("soloff",112,DOFPulse,DOFTargets)
  971. call hitCenterTarget
  972. end sub
  973. sub target2Center1_hit()
  974. call targetCenter1_hit
  975. end sub
  976.  
  977. sub targetCenter2_hit()
  978. playSound SoundFXDOF("soloff",112,DOFPulse,DOFTargets)
  979. call hitCenterTarget
  980. end sub
  981.  
  982. sub target2Center2_hit()
  983. call targetCenter2_hit
  984. end sub
  985.  
  986. sub targetCenter3_hit()
  987. playSound SoundFXDOF("soloff",113,DOFPulse,DOFTargets)
  988. call hitCenterTarget
  989. end sub
  990.  
  991. sub target2Center3_hit()
  992. call targetCenter3_hit
  993. end sub
  994.  
  995. sub targetCenter4_hit()
  996. playSound SoundFXDOF("soloff",113,DOFPulse,DOFTargets)
  997. call hitCenterTarget
  998. end sub
  999.  
  1000. sub target2Center4_hit()
  1001. call targetCenter4_hit
  1002. end sub
  1003.  
  1004. sub hitCenterTarget()
  1005. addScore (100)
  1006. if ruleSet=RS_authentic or centerBumperLit then incrementSpinner
  1007. end sub
  1008.  
  1009. 'These memorize at which side the rubber bands in the tribal center are hit:
  1010.  
  1011. sub before10p1_hit() : ballBefore10p1=true end sub
  1012. sub before10p1_unhit(): ballBefore10p1=false end sub
  1013.  
  1014. sub before10p2_hit() : ballBefore10p2=true end sub
  1015. sub before10p2_unhit(): ballBefore10p2=false end sub
  1016.  
  1017. sub before10p3_hit() : ballBefore10p3=true end sub
  1018. sub before10p3_unhit(): ballBefore10p3=false end sub
  1019.  
  1020. sub before10p4_hit() : ballBefore10p4=true end sub
  1021. sub before10p4_unhit(): ballBefore10p4=false end sub
  1022.  
  1023.  
  1024. sub rubber10p1_hit()
  1025. if ballBefore10p1 and centerBumperLit then addScore 10
  1026. end sub
  1027. sub rubber10p1_slingshot() call rubber10p1_hit end sub
  1028.  
  1029. sub rubber10p2_hit()
  1030. if ballBefore10p2 and centerBumperLit then addScore 10
  1031. end sub
  1032. sub rubber10p2_slingshot() call rubber10p2_hit end sub
  1033.  
  1034. sub rubber10p3_hit()
  1035. if ballBefore10p3 and centerBumperLit then addScore 10
  1036. end sub
  1037. sub rubber10p3_slingshot() call rubber10p3_hit end sub
  1038.  
  1039. sub rubber10p4_hit()
  1040. if ballBefore10p4 and centerBumperLit then addScore 10
  1041. end sub
  1042. sub rubber10p4_slingshot() call rubber10p4_hit end sub
  1043.  
  1044.  
  1045. sub targetLeftCLaneEnd_hit()
  1046. playSound SoundFXDOF("soloff",112,DOFPulse,DOFTargets)
  1047. call hitSpecial
  1048. end sub
  1049.  
  1050. sub target2LeftCLaneEnd_hit() call targetLeftCLaneEnd_hit end sub
  1051.  
  1052. sub targetRightCLaneEnd_hit()
  1053. playSound SoundFXDOF("soloff",113,DOFPulse,DOFTargets)
  1054. call hitSpecial
  1055. end sub
  1056.  
  1057. sub target2RightCLaneEnd_hit() call targetRightCLaneEnd_hit end sub
  1058.  
  1059.  
  1060. sub hitSpecial()
  1061. ' This lets the player win an extra credit.Per round only one credit can be
  1062. ' won. (This rule is a guess.)
  1063. if specialLit then
  1064.  
  1065. select case ruleSet
  1066. case RS_authentic
  1067. if specialWinsPoints0>0 then addScore specialWinsPoints0
  1068. if specialWinsCredit0 then addCredit
  1069. if specialShootAgain0 then setExtraBall true
  1070. case RS_enhanced
  1071. if specialWinsPoints1>0 then addScore specialWinsPoints1
  1072. if specialWinsCredit1 then addCredit
  1073. if specialShootAgain1 then setExtraBall true
  1074. end select
  1075.  
  1076. setLight lampLeftCLaneEnd ,false
  1077. setLight lampRightCLaneEnd,false
  1078. specialLit=false
  1079. setRelay specialHit,true
  1080. end if
  1081. end sub
  1082.  
  1083.  
  1084. sub targetRight500_hit()
  1085. playSound "soloff"
  1086. ' playSound "bell100-5"
  1087. addScore500
  1088. end sub
  1089. sub target2Right500_hit()
  1090. DOF 115, DOFPulse
  1091. call targetRight500_hit
  1092. end sub
  1093.  
  1094. sub targetLeft500_hit()
  1095. playSound "soloff"
  1096. ' playSound "bell100-5"
  1097. addScore500
  1098. end sub
  1099. sub target2Left500_hit()
  1100. DOF 114, DOFPulse
  1101. call targetLeft500_hit
  1102. end sub
  1103.  
  1104.  
  1105. sub triggerLeft1k_hit()
  1106. DOF 122, DOFOn
  1107. playSound "soloff2"
  1108. addscore 1000
  1109. if rollover1kLit then addScoreDigitStuckBySema 3,triggerLeft1kStuck
  1110. 'This adds 1 to the 4th score counter digit in a way that the digit can get
  1111. 'stuck and hum so long a ball lies on the switch (see reel-a-listic score
  1112. 'counter description below).
  1113.  
  1114. if ruleSet=RS_authentic then incrementBonus
  1115. end sub
  1116.  
  1117. sub triggerLeft1k_unhit()
  1118. DOF 122, DOFOff
  1119. playBallRollSound "ballRoll2"
  1120. releaseScoreDigitBySema 3,triggerLeft1kStuck
  1121. 'This releases the stuck counter digit again.
  1122. end sub
  1123.  
  1124.  
  1125. sub triggerRight1k_hit()
  1126. DOF 123, DOFOn
  1127. addscore 1000
  1128. playSound "soloff2"
  1129. if rollover1kLit then addScoreDigitStuckBySema 3,triggerRight1kStuck
  1130. if ruleSet=RS_authentic then incrementBonus
  1131. end sub
  1132.  
  1133. sub triggerRight1k_unhit()
  1134. DOF 123, DOFOff
  1135. playBallRollSound "ballRoll2"
  1136. releaseScoreDigitBySema 3,triggerRight1kStuck
  1137. end sub
  1138.  
  1139.  
  1140. sub triggerLeftCLaneMid_hit()
  1141. DOF 117, DOFOn
  1142. playSound "soloff2"
  1143. select case ruleSet
  1144. case RS_authentic
  1145. addScoreDigitStuck 3
  1146. incrementBonus
  1147. case RS_enhanced
  1148. addScore500
  1149. end select
  1150. end sub
  1151.  
  1152. sub triggerLeftCLaneMid_unhit()
  1153. DOF 117, DOFOff
  1154. if ruleSet=RS_authentic then releaseScoreDigit 3
  1155. end sub
  1156.  
  1157.  
  1158. sub triggerRightCLaneMid_Hit()
  1159. DOF 120, DOFOn
  1160. playSound "soloff2"
  1161. select case ruleSet
  1162. case RS_authentic
  1163. addScoreDigitStuck 3
  1164. incrementBonus
  1165. case RS_enhanced
  1166. addScore500
  1167. end select
  1168. end sub
  1169.  
  1170. sub triggerRightCLaneMid_unhit()
  1171. DOF 120, DOFOff
  1172. if ruleSet=RS_authentic then releaseScoreDigit 3
  1173. end sub
  1174.  
  1175.  
  1176. sub triggerLeftInLane_hit()
  1177. DOF 125, DOFOn
  1178. addscore 1000
  1179. ' playSound "bell1000"
  1180. playSound "soloff"
  1181. incrementBonus
  1182. addScoreDigitStuck(3)
  1183. end sub
  1184.  
  1185. sub triggerLeftInLane_unhit()
  1186. DOF 125, DOFOff
  1187. releaseScoreDigit(3)
  1188. end sub
  1189.  
  1190.  
  1191. sub triggerRightInLane_hit()
  1192. DOF 126, DOFOn
  1193. addscore 1000
  1194. ' playSound "bell1000"
  1195. playSound "soloff"
  1196. incrementBonus
  1197. addScoreDigitStuck(3)
  1198. end sub
  1199.  
  1200. sub triggerRightInLane_unhit()
  1201. DOF 126, DOFOff
  1202. releaseScoreDigit(3)
  1203. end sub
  1204.  
  1205.  
  1206. sub triggerLeftOutLane_Hit()
  1207. DOF 124, DOFPulse
  1208. playSound "soloff"
  1209. ' playSound "bell100-5"
  1210. checkExtraBall
  1211. if leftExtraBallLit then
  1212. ' lampLeft_XB.State = LightStateOff 'I doubt that the bonus wheel can do this
  1213. setExtraBall true 'Shoot Again!
  1214. end if
  1215. addScore500
  1216. end sub
  1217.  
  1218.  
  1219. sub triggerRightOutLane_Hit()
  1220. DOF 127, DOFPulse
  1221. playSound "soloff"
  1222. ' playSound "bell100-5"
  1223. checkExtraBall
  1224. if rightExtraBallLit then
  1225. ' lampRight_XB.State = LightStateOff 'I doubt that the bonus wheel can do this
  1226. setExtraBall true 'Shoot Again!
  1227. end if
  1228. addScore500
  1229. end sub
  1230.  
  1231.  
  1232. sub checkExtraBall()
  1233. if bonus >= 15000 then
  1234. leftExtraBallLit = true
  1235. rightExtraBallLit = true
  1236. end if
  1237. if bonus = 8000 or bonus = 10000 or bonus = 12000 or bonus = 14000 then
  1238. leftExtraBallLit = true
  1239. end if
  1240. if bonus = 9000 or bonus = 11000 or bonus = 13000 then
  1241. rightExtraBallLit = true
  1242. end if
  1243. end sub
  1244.  
  1245. 'A-B-C-D targets:
  1246.  
  1247. sub target_A_hit()
  1248. playSound SoundFXOF("soloff",114,DOFPulse,DOFTargets)
  1249. addScore 1000
  1250. ' playSound "bell1000"
  1251. if not A then
  1252. setLight lampTarget_A ,false
  1253. setLight lampLeftCLane_A,true
  1254. setRelay A,true
  1255. checkDoubleBonus
  1256. end if
  1257. end sub
  1258. sub target2_A_hit() call target_A_hit end sub
  1259.  
  1260.  
  1261. sub target_B_hit()
  1262. playSound SoundFXDOF("soloff",114,DOFPulse,DOFTargets)
  1263. addScore 1000
  1264. ' playSound "bell1000"
  1265.  
  1266. if not B then
  1267. setLight lampTarget_B ,false
  1268. setLight lampLeftCLane_B,true
  1269. setRelay B,true
  1270. checkDoubleBonus
  1271. end if
  1272. end sub
  1273. sub target2_B_hit() call target_B_hit end sub
  1274.  
  1275.  
  1276. sub target_C_hit()
  1277. playSound SoundFXDOF("soloff",115,DOFPulse,DOFTargets)
  1278. addScore 1000
  1279. ' playSound "bell1000"
  1280.  
  1281. if not C then
  1282. setLight lampTarget_C ,false
  1283. setLight lampRightCLane_C,true
  1284. setRelay C,true
  1285. checkDoubleBonus
  1286. end if
  1287. end sub
  1288. sub target2_C_hit() call target_C_hit end sub
  1289.  
  1290.  
  1291. sub target_D_hit()
  1292. playSound "soloff"
  1293. addScore 1000
  1294. ' playSound "bell1000"
  1295.  
  1296. if not D then
  1297. setLight lampTarget_D ,false
  1298. setLight lampRightCLane_D,true
  1299. setRelay D,true
  1300. checkDoubleBonus
  1301. end if
  1302. end sub
  1303. sub target2_D_hit() call target_D_hit end sub
  1304.  
  1305.  
  1306. sub checkDoubleBonus()
  1307. if A and B and C and D then
  1308. if tiltClearsDoubleBonus and tilt then exit sub
  1309.  
  1310. setRelay doubleBonus,true
  1311. setLight lamp_DB ,true
  1312. end if
  1313. end sub
  1314.  
  1315.  
  1316. sub triggerLeftCLane_A_hit()
  1317. DOF 116, DOFOn
  1318. playSound "soloff2"
  1319. addscore 1000
  1320. select case ruleSet
  1321. case RS_authentic
  1322. addScoreDigitStuck 3
  1323. if A then incrementBonus
  1324. case RS_enhanced
  1325. if A and not A_lock then
  1326. setRelay A_lock,true 'A_lock prevents twice bonus when ball rolls back
  1327. incrementBonus
  1328. end if
  1329. end select
  1330. end sub
  1331.  
  1332. sub triggerLeftCLane_A_unhit()
  1333. DOF 116, DOFOff
  1334. if ruleSet=RS_authentic then releaseScoreDigit 3
  1335. end sub
  1336.  
  1337.  
  1338. sub triggerLeftCLane_B_hit()
  1339. DOF 118, DOFOn
  1340. playSound "soloff2"
  1341. addscore 1000
  1342. select case ruleSet
  1343. case RS_authentic
  1344. addScoreDigitStuck 3
  1345. if B then incrementBonus
  1346. case RS_enhanced
  1347. if B and not B_lock then
  1348. setRelay B_lock,true
  1349. incrementBonus
  1350. end if
  1351. end select
  1352. end sub
  1353.  
  1354. sub triggerLeftCLane_B_unhit()
  1355. DOF 118, DOFOff
  1356. if ruleSet=RS_authentic then releaseScoreDigit 3
  1357. end sub
  1358.  
  1359.  
  1360. sub triggerRightCLane_C_hit()
  1361. DOF 121, DOFOn
  1362. playSound "soloff2"
  1363. addscore 1000
  1364. select case ruleSet
  1365. case RS_authentic
  1366. addScoreDigitStuck 3
  1367. if C then incrementBonus
  1368. case RS_enhanced
  1369. if C and not C_lock then
  1370. setRelay C_lock,true
  1371. incrementBonus
  1372. end if
  1373. end select
  1374. end sub
  1375.  
  1376. sub triggerRightCLane_C_unhit()
  1377. DOF 121, DOFOff
  1378. if ruleSet=RS_authentic then releaseScoreDigit 3
  1379. end sub
  1380.  
  1381.  
  1382. sub triggerRightCLane_D_hit()
  1383. DOF 119, DOFOn
  1384. playSound "soloff2"
  1385. addscore 1000
  1386. select case ruleSet
  1387. case RS_authentic
  1388. addScoreDigitStuck 3
  1389. if D then incrementBonus
  1390. case RS_enhanced
  1391. if D and not D_lock then
  1392. setRelay D_lock,true
  1393. incrementBonus
  1394. end if
  1395. end select
  1396. end sub
  1397.  
  1398. sub triggerRightCLane_D_unhit()
  1399. DOF 119, DOFOff
  1400. if ruleSet=RS_authentic then releaseScoreDigit 3
  1401. end sub
  1402.  
  1403.  
  1404. sub targetLeftCLaneStart_hit()
  1405. ' triggered by left pin of left captive ball lane.
  1406.  
  1407. setRelay A_lock,false
  1408. setRelay B_lock,false
  1409. 'The ball has rolled back and though the triggers are active again.
  1410. end sub
  1411.  
  1412. sub targetRightCLaneStart_hit()
  1413. ' triggered by right pin of right captive ball lane.
  1414.  
  1415. setRelay C_lock,false
  1416. setRelay D_lock,false
  1417. end sub
  1418.  
  1419.  
  1420. 'pseudo-targets: (these only simulate playfield noises)
  1421. '---------------
  1422. 'Note: All ball rolling and -hit noises in this game are only an approximation
  1423. ' simulated with invisible rollover targets.They therefore don't behave
  1424. ' very realistic because volume and timbre neither react on the ball speed
  1425. ' nor hit angle.
  1426. ' For a proper simulation of these sounds it would be neccessary to give
  1427. ' the floor and all other playfield materials a sound property depending on
  1428. ' the actual ball speed and roll direction,which (by my knowledge) is yet
  1429. ' impossible in Visual Pinball because there are no ways to measure the
  1430. ' actual ball rolling speed and direction. At least the volume of hit noises
  1431. ' should depend on the hit speed.
  1432. '
  1433. ' Also the volume of hit/bump noises is unrealistic because there is no way
  1434. ' to determine with how much force an object was actually hit.Though they
  1435. ' either sound at their fixed volume or not at all,instead of sounding louder
  1436. ' when hit harder.The "Hit Treshold" parameter adjusts at which ball speed
  1437. ' a hit event shall cause a (sound) reaction.
  1438. '
  1439. 'TIP: You can digitally re-sample sounds from other Visual Pinball games without
  1440. ' external devices by using the Windoze audio recorder and the freeware
  1441. ' wave loopback driver program "Virtual Audio Cable" by Eugene Muzychenko.
  1442.  
  1443.  
  1444. sub ejectChute_hit() playSound SoundFXDOF("ballin",129,DOFPulse,DOFContactors) end sub
  1445.  
  1446. sub firingChute_hit() playSound "tinRoll":DOF 130, DOFPulse:end sub
  1447. 'should be different?
  1448.  
  1449. sub beforePlunger_hit()
  1450. ' playSound "soloff" 'should click differently?
  1451. stopBallRollNoises
  1452. end sub
  1453.  
  1454. sub beforeLeftCLane_hit() playSound "ballKlick" end sub
  1455. sub beforeRightCLane_hit() playSound "ballKlick" end sub
  1456.  
  1457. sub metal1_hit() playSound "tinHit3" end sub
  1458. sub metal2_hit() playSound "tinHit3" end sub
  1459. sub metal3_hit() playSound "tinHit3" end sub
  1460. sub metal4_hit() playSound "tinHit3" end sub
  1461. sub metal5_hit() playSound "tinHit3" end sub
  1462. sub metal6_hit() playSound "tinHit3" end sub
  1463.  
  1464. sub spinnerMetal1_hit() playSound "tinHit3" end sub
  1465. sub spinnerMetal2_hit() playSound "tinHit3" end sub
  1466.  
  1467. sub protector_hit() playSound "tinHit3" end sub
  1468. sub gateTrigger_hit() playSound "tinHit3" end sub
  1469.  
  1470. sub tin1_hit() playSound "tinHit2":call undropGuideWires end sub
  1471. sub tin2_hit() playSound "tinHit2":call undropGuideWires end sub
  1472.  
  1473. sub damper_hit():stopBallRollNoises:playSound "bump5":end sub
  1474.  
  1475. sub post1_hit() playSound "bump2" end sub
  1476. sub post2_hit() playSound "bump2" end sub
  1477. sub post3_hit() playSound "bump2" end sub
  1478. sub post4_hit() playSound "bump2" end sub
  1479. sub post5_hit() playSound "bump2" end sub
  1480. sub post6_hit() playSound "bump2" end sub
  1481.  
  1482. sub postL1_hit() playSound "bump5" end sub
  1483. sub postL2_hit() playSound "bump5" end sub
  1484. sub postL3_hit() playSound "bump5" end sub
  1485. sub postL4_hit() playSound "bump5" end sub
  1486. sub postR1_hit() playSound "bump5" end sub
  1487. sub postR2_hit() playSound "bump5" end sub
  1488. sub postR3_hit() playSound "bump5" end sub
  1489. sub postR4_hit() playSound "bump5" end sub
  1490.  
  1491. sub leftSlingshot_hit() playSound "bump5" end sub
  1492. sub rightSlingshot_hit() playSound "bump5" end sub
  1493.  
  1494. sub tinRoll1_hit() playBallRollSound "tinRoll" end sub
  1495. sub tinRoll2_hit() playBallRollSound "tinRoll" end sub
  1496.  
  1497. sub roll1_hit() playBallRollSound "ballRoll1" end sub
  1498. sub roll2_hit() playBallRollSound "ballRoll1" end sub
  1499. sub roll3_hit() playBallRollSound "ballRoll1" end sub
  1500. sub roll4_hit() playBallRollSound "ballRoll1" end sub
  1501. sub roll5_hit() playBallRollSound "ballRoll1" end sub
  1502. sub roll6_hit() playBallRollSound "ballRoll1" end sub
  1503. sub roll7_hit() playBallRollSound "ballRoll1" end sub
  1504. sub roll8_hit() playBallRollSound "ballRoll1":call undropGuideWires end sub
  1505.  
  1506. sub rollDown1_hit() playBallRollSound "ballRoll2" end sub
  1507. sub rollDown2_hit() playBallRollSound "ballRoll2" end sub
  1508. sub rollDown3_hit() playBallRollSound "ballRoll2" end sub
  1509. sub rollDown4_hit() playBallRollSound "ballRoll2" end sub
  1510. sub rollDown5_hit() playBallRollSound "ballRoll2" end sub
  1511.  
  1512.  
  1513. sub playBallRollSound(aSoundString)
  1514. ' This plays a sound sample so far ballRollNoisesEnabled=true.
  1515.  
  1516. if ballRollNoisesEnabled then playSound aSoundString
  1517. end sub
  1518.  
  1519.  
  1520. sub stopBallRollNoises()
  1521. ' This routine stops all still sounding ball roll samples.It should be called
  1522. ' at all places where the ball is known to stand still (e.g. in the outhole
  1523. ' or tophole etc.) to prevent confusion of the player.
  1524. '
  1525. ' Note: In a multi-ball game this would need to be modelled more complex (using
  1526. ' counting semaphores?). The easiest modelling there would be to stop these
  1527. ' noises only when all balls stand still.
  1528. '
  1529. ' TIP: A routine like this could also be used in a simulation of an electronic
  1530. ' pinball machine to prevent a certain type of sounds(e.g. speech) to be
  1531. ' played polyphonic when the simulated sound hardware could only play them
  1532. ' monophonic.
  1533.  
  1534. stopSound "tinRoll"
  1535. stopSound "ballRoll1"
  1536. stopSound "ballRoll2"
  1537. end sub
  1538.  
  1539. 'The following prevents the ball getting stuck under a flipper by temporary
  1540. 'dropping the guiding wire below it.Without this trick an ugly buzz was audible
  1541. 'and it ate that much CPU load that it might crash VP on very slow PCs.
  1542.  
  1543. sub leftGuide_hit()
  1544. playSound "tinHit3"
  1545. if not leftFlipperUp then leftGuide.isDropped=true
  1546. end sub
  1547.  
  1548. sub rightGuide_hit()
  1549. playSound "tinHit3"
  1550. if not rightFlipperUp then rightGuide.isDropped=true
  1551. end sub
  1552.  
  1553. sub undropGuideWires()
  1554. ' This restores the guide wires below the flippers so far they were dropped
  1555. ' by a stuck ball.
  1556. leftGuide.isDropped =false
  1557. rightGuide.isDropped=false
  1558. end sub
  1559.  
  1560.  
  1561. 'misc game related routines:
  1562. '---------------------------
  1563.  
  1564. sub drain_hit()
  1565. DOF 128, DOFPulse
  1566. ' This function is called when a ball falls into the outhole.
  1567. '
  1568. ' TIP: The outhole should always be placed outside the viewfield of the player
  1569. ' (except when the real pinball had a visible one) because it looks really
  1570. ' ugly when the ball just disappears below the flippers.It is very easy
  1571. ' to form a mould and place a flat cover tin above it to place the outhole
  1572. ' (and possibly eject hole) away from the player's sight.(Setting the
  1573. ' kicker type "invisible" may safe a little CPU time because it avoids
  1574. ' drawing it.)
  1575. '
  1576. ' When you want to make a visible fall-through hole,then don't call
  1577. ' .destroyBall immediately but start a timer for this with a small delay.
  1578. ' Though the ball stays visible in the hole for a fraction of a second,
  1579. ' which looks much more realistic.
  1580. ' (This game doesn't destroy the ball at all,but re-ejects it like a
  1581. ' real pinball machine.)
  1582.  
  1583. stopBallRollNoises
  1584. playSound "Ballout"
  1585.  
  1586. ballInDrain=true
  1587. 'When points were scorend,then this will add the actual bonus from the bonus
  1588. 'ladder to the score and start the next round. Else the ball will be just
  1589. 're-ejected.
  1590.  
  1591. undropGuideWires
  1592. 'When a ball got stuck under a flipper,this restores the dropped guide wire.
  1593.  
  1594. if noTiltDuringBonus then tiltHasEffect=false
  1595. 'prevents tilt happening while collecting bonus
  1596.  
  1597. scoreMotor.enabled=true
  1598. end sub
  1599.  
  1600.  
  1601. sub ejectBall()
  1602.  
  1603. if ballInDrain then
  1604. if not ballCreated then
  1605. drain.createBall.image=ballTexture
  1606. ballCreated=true
  1607. end if
  1608.  
  1609. 'Like a real pinball machine,this game does not delete and re-create
  1610. 'the ball again and again,but re-uses the same ball (after it is
  1611. 'created a single time).
  1612. 'By a Visual Pinball bug drain.createBall works only when the ball is
  1613. 'kicked out afterward, therefore it can not be done in theTable_init()
  1614. 'without also kicking it out there,which would result in the drain_hit()
  1615. 'noise when the program starts.
  1616. '
  1617. 'Important: Most Visual Pinball games hang/become unplayable when the player
  1618. ' nudges the ball back into the eject hole near the plunger.This
  1619. ' is a very annoying flaw.To prevent this,a hit event on the eject
  1620. ' kicker should always be sensed and cause a re-eject of the ball,
  1621. ' no matter if the game uses a separate eject kicker or the drain
  1622. ' kicker to eject the ball.
  1623.  
  1624. playSound "ballrel" 'eject coil sound
  1625. drain.kick 80,15
  1626. ballInDrain=false
  1627. end if
  1628. end sub
  1629.  
  1630. 'tilt routines:
  1631.  
  1632. sub nudged()
  1633. ' This is a crude approximation of a nudge against a tilt pendulum. Only an
  1634. ' amplitude is simulated - neither sine swinging nor different directions. The
  1635. ' decay of the pendulum is simulated by tiltTimer.
  1636.  
  1637. if tiltEnabled then
  1638. tiltSwing=tiltSwing+1
  1639.  
  1640. if tiltSwing>=tiltSensitivity then
  1641. tiltSwing=tiltSensitivity
  1642. if not gameOver and tiltHasEffect then doTilt
  1643. end if
  1644.  
  1645. end if
  1646. end sub
  1647.  
  1648.  
  1649. sub tiltTimer_timer()
  1650. ' This timer periodically decreases tiltSwing to approximate pendulum decay.
  1651. if tiltSwing>0 then subtractUntil0 tiltSwing,tiltSwingDecay
  1652. end sub
  1653.  
  1654.  
  1655. sub doTilt()
  1656. ' This sets the machine into tilt state.
  1657. '
  1658. ' Hint: For simulating a pinball machine with "tilt disqualifies player"
  1659. ' behaviour it would be necessary to make one tilt relay per player
  1660. ' (i.e. replace tilt variable by an array) and in startNextRound()
  1661. ' instead of calling unTilt() players with a set tilt relay would need
  1662. ' to be skipped by executing the section that calls nextPlayer() again
  1663. ' (using the score motor) until tilt(currentPlayer)=false.This way a
  1664. ' tilted players would be skipped until game over.
  1665.  
  1666. setRelay tilt,true
  1667. tiltText.Text = "TILT"
  1668. setBumpers false
  1669. setFlippers false
  1670. If B2SOn Then
  1671. Controller.B2SSetTilt 33,1
  1672. Controller.b2ssetdata 1,0
  1673. End If
  1674. if tiltClearsPilotLamps then
  1675. setPlayfieldLights false 'typical for Bally
  1676. lightCenterBumper false
  1677. end if
  1678.  
  1679. if tiltClearsIllumination then setIllumination false
  1680. 'this one may be not authentic for Bally,but looks great
  1681.  
  1682. if tiltClearsDoubleBonus then setRelay doubleBonus,false 'counts down faster
  1683. if tiltVoids0PtsRestart then newRound=false
  1684.  
  1685. if tiltVoidsExtraBall then
  1686. setExtraBall false
  1687. setShootAgain false
  1688. '(In setExtraBall() also the win of a new extra ball is disabled during tilt
  1689. ' when const tiltVoidsExtraBall=true.)
  1690. end if
  1691. end sub
  1692.  
  1693.  
  1694. sub unTilt()
  1695. ' When the game was tilted, then this ends tilt state.
  1696.  
  1697. setRelay tilt,false
  1698. tiltText.Text = " "
  1699. setBumpers true
  1700. setFlippers true
  1701. If B2SOn Then
  1702. Controller.B2SSetTilt 33,0
  1703. Controller.b2ssetdata 1,1
  1704. End If
  1705. end sub
  1706.  
  1707. 'extra balls:
  1708.  
  1709. sub setExtraBall(aBoolean)
  1710. ' When aBoolean=true this lets the player win an extra ball (by setting the
  1711. ' extraBallWon relay) and lights the "shoot again" lamps. When aBoolean=false,
  1712. ' the extraBallWon relay is cleared.(The "shoot again" lamps must be cleared
  1713. ' elsewhere by calling setShootAgain false.)
  1714. '
  1715. ' During tilt no extra ball can be won when const tiltPreventsExtraBall or
  1716. ' tiltVoidsExtraBall is set true.
  1717.  
  1718. if aBoolean then
  1719. if not (tilt and (tiltPreventsExtraBall or tiltVoidsExtraBall)) then
  1720. setRelay extraBallWon,true
  1721. setShootAgain true
  1722. end if
  1723. else
  1724. setRelay extraBallWon,false
  1725. end if
  1726. end sub
  1727.  
  1728.  
  1729. sub setShootAgain(aBoolean)
  1730. ' This sets the shootAgain relay and the shootAgain lamps on playfield and
  1731. ' backbox to the value of aBoolean.
  1732. '
  1733. ' In Bally EM pinball machines the "shoot again" lamp on the playfield and the
  1734. ' backbox are wired in parallel and therefore always switch simulataneously.
  1735. '
  1736. ' Unlike other playfield lamps, these lamps stays also lit when
  1737. ' playfieldLightsEnabled=false,because this way also after a tilt the player
  1738. ' can see if he still has a shoot again. (I believe to remember this from Bally
  1739. ' "Champ";is this correct?)
  1740. '
  1741. ' Important: The extraBallWon relay must be set to tell the program that the next
  1742. ' ball shall be a shoot again.The shootAgain relay itself does nothing
  1743. ' except holding its own lamps lit. (I don't know if real Bally machines
  1744. ' handle this differently.)
  1745.  
  1746. setRelay shootAgain,aBoolean
  1747.  
  1748. if aBoolean then
  1749. lampShootAgain.state=lightStateOn
  1750. shootAgainText.Text = "SHOOT AGAIN"
  1751. If B2SOn Then Controller.B2SSetShootAgain 36, 1
  1752. else
  1753. lampShootAgain.state=lightStateOff
  1754. shootAgainText.Text =""
  1755. If B2SOn Then Controller.B2SSetShootAgain 36, 0
  1756. end if
  1757. end sub
  1758.  
  1759.  
  1760. 'move flippers:
  1761.  
  1762. sub moveLeftFlipper(aBoolean)
  1763. ' This moves the left flipper up when aBoolean=true or down when false with the
  1764. ' corresponding sound so far the flipper was not already in that position.
  1765.  
  1766. if aBoolean and not leftFlipperUp then
  1767. leftFlipper.rotateToEnd
  1768. stopSound "leftFlipperDown"
  1769. 'The stopSound reduces CPU load when someone chatters with the flippers and
  1770. 'it also makes it sound more realistic.
  1771. playSound SoundFXDOF("leftFlipperUp",101,DOFOn,DOFFlippers)
  1772. playSound "leftFlipperHum",-1 'endless loop sample of solenoid hum
  1773. leftFlipperUp=true
  1774. elseIf not aBoolean and leftFlipperUp then
  1775. leftFlipper.RotateToStart
  1776. stopSound "leftFlipperUp"
  1777. playSound SoundFXDOF("leftFlipperDown",101,DOFOff,DOFFlippers)
  1778. stopSound "leftFlipperHum"
  1779. leftFlipperUp=false
  1780. end if
  1781. end sub
  1782.  
  1783. sub moveRightFlipper(aBoolean)
  1784. ' This moves the right flipper up when aBoolean=true or down when false with the
  1785. ' corresponding sound so far the flipper was not already in that position.
  1786.  
  1787. if aBoolean and not rightFlipperUp then
  1788. rightFlipper.rotateToEnd
  1789. stopSound "rightFlipperDown"
  1790. playSound SoundFXDOF("rightFlipperUp",102,DOFOn,DOFFlippers)
  1791. playSound "rightFlipperHum",-1 'endless loop sample of solenoid hum
  1792. rightFlipperUp=true
  1793. elseIf not aBoolean and rightFlipperUp then
  1794. rightFlipper.RotateToStart
  1795. stopSound "rightFlipperUp"
  1796. playSound SoundFXDOF("rightFlipperDown",102,DOFOff,DOFFlippers)
  1797. stopSound "rightFlipperHum"
  1798. rightFlipperUp=false
  1799. end if
  1800. end sub
  1801.  
  1802.  
  1803. 'enable/disable playfield stuff:
  1804.  
  1805. sub setFlippers(aBoolean)
  1806. ' This switches the flippers on or off and regards (with correct sound) when
  1807. ' a flipper button is held down.
  1808. ' (In a real pinball machine the flippers react when the button is held and
  1809. ' then the current to the flippers is switched on.)
  1810.  
  1811. flippersEnabled=aBoolean
  1812.  
  1813. if flippersEnabled then
  1814. moveLeftFlipper leftFlipButton
  1815. moveRightFlipper rightFlipButton
  1816. else
  1817. moveLeftFlipper false
  1818. moveRightFlipper false
  1819. end if
  1820. end sub
  1821.  
  1822.  
  1823. sub setBumpers(aBoolean)
  1824. ' This shall turn the bumping of all bumpers on or off.
  1825. ' BUG: this doesn't work yet (a Visual Pinball bug?). Bumpers remain always on.
  1826.  
  1827. 'TIP: Beside slingshots (triangular "bumpers") also the straight part of long
  1828. ' rubber bands(where they don't touch the post) should be set as slingshots
  1829. ' with a low slingshot force(about 2) to get a more realistic ball bounce
  1830. ' behaviour in Visual Pinball.
  1831. ' (Note: In Visual Pinball 3 slingshot areas of "Wall" objects must not be
  1832. ' convex,because otherwise their slingshot part(black line in editor) can
  1833. ' not be hit with the ball.Instead make such areas as multiple small straight
  1834. ' lines. Concave slingshot areas make no problems.)
  1835. '
  1836. ' To simulate bumpers with a non-round or flat top cover (like e.g. in
  1837. ' Interflip "Alaska"),I recommend to use the Visual Pinball bumper only for
  1838. ' the round center part,and add a flat disc of "wall" material to draw the
  1839. ' top. If the top shall be lightable,use the same trick I did with the
  1840. ' playfield plastics.
  1841.  
  1842. if aBoolean=true then
  1843. centerBumper.threshold =1
  1844. leftSlingshot.threshold =1
  1845. rightSlingshot.threshold=1
  1846. else
  1847. centerBumper.threshold =1000
  1848. leftSlingshot.threshold =1000
  1849. rightSlingshot.threshold=1000
  1850. end If
  1851. end sub
  1852.  
  1853. 'coin chutes:
  1854.  
  1855. sub creditButtonRoutine
  1856. ' This routine adds a player to a running game when it is still at the 1st ball,
  1857. ' or otherwise resets the current game and starts a new one with 1 player.
  1858. '
  1859. ' This is called when the start button is pressed and credits are present or
  1860. ' when a valid washer was inserted into a 1-credit coin chute.
  1861.  
  1862. rulesSwitchable=false 'During a game the rules must stay the same.
  1863.  
  1864. if gameOver or ballCount>1 or players=maxPlayers then
  1865. 'starting a new game with 1 player
  1866. resetGame
  1867. else
  1868. if creditTaken then decrementCredit
  1869. 'When this was called by a start button press,a credit is consumed from the
  1870. 'credit counter (decrementCredit also clears the creditTaken relay).
  1871.  
  1872. addPlayer
  1873. 'Players can be added to a running game as long the 1st ball is in play.
  1874. 'After a player lost a ball, it would become unfair to permit this, because
  1875. 'the new player would get less balls for his credit due to these old Bally
  1876. 'machines have only a single ball counter wheel shared among all players.
  1877. end if
  1878. end sub
  1879.  
  1880.  
  1881. 'The following timers delay inserted coins to simulate the coin mech sound
  1882. 'correctly.
  1883. '
  1884. 'Important: The constants coinMechXTime must be >0,because Timer.interval is abused
  1885. ' here as a state variable. The coinMechX timers must be initialized with
  1886. ' .interval=0 to work correctly.
  1887.  
  1888. sub coinMech1_timer()
  1889. if coinMech1.interval=0 then
  1890. coinMech1.interval=coinMech1Time
  1891. playSound "coin1"
  1892. playSound "coinMech1"
  1893. else
  1894. coinMech1.enabled=false
  1895. coinMech1.interval=0
  1896. insertCoin coinChute1Credits
  1897. end if
  1898. end sub
  1899.  
  1900. sub coinMech2_timer()
  1901. if coinMech2.interval=0 then
  1902. coinMech2.interval=coinMech2Time
  1903. playSound "coin1"
  1904. playSound "coinMech1"
  1905. else
  1906. coinMech2.enabled=false
  1907. coinMech2.interval=0
  1908. insertCoin coinChute2Credits
  1909. end if
  1910. end sub
  1911.  
  1912. sub coinMech3_timer()
  1913. if coinMech3.interval=0 then
  1914. coinMech3.interval=coinMech3Time
  1915. playSound "coin1"
  1916. playSound "coinMech1"
  1917. else
  1918. coinMech3.enabled=false
  1919. coinMech3.interval=0
  1920. insertCoin coinChute3Credits
  1921. end if
  1922. end sub
  1923.  
  1924.  
  1925. sub insertCoin(someCredits)
  1926. ' This simulates inserting a coin. The coins are added using the score motor.
  1927. ' Therefore when the score motor is still running (e.g. by a previous coin), the
  1928. ' then the coin is rejected. Coins with a single credit can be inserted at any
  1929. ' time, because these don't need the score motor to be counted.
  1930. '
  1931. ' (A real mechanical pinball machine can not memorize the credits of multiple
  1932. ' inserted coins,therefore a mechanism in the coindoor re-directs inserted coins
  1933. ' to the coin return chute when the counter is still counting. But so far I know,
  1934. ' the machine does not recognize overpaying the credit counter.)
  1935.  
  1936. if creditsAddable then
  1937.  
  1938. select case int(3*rnd())
  1939. case 0: playSound "cashbox2"
  1940. case 1: playSound "cashbox9"
  1941. case 2: playSound "cashbox15"
  1942. 'This plays randomly 1 of multiple different cashbox noises,but they are
  1943. 'anyway badly audible due to the loud rest of the mechanism.
  1944. end select
  1945.  
  1946. setIllumination true 'a coin activates illumination
  1947.  
  1948. if someCredits=1 then
  1949. 'For adding a single credit the score motor is not involved.
  1950.  
  1951. if smallCoinStartsGame then
  1952. 'When true,it behaves much like a start button press,except that no credit
  1953. 'is subtracted from the counter and that a running game can always be reset
  1954. 'by this.
  1955. creditButtonRoutine
  1956. else
  1957. if not knockerKnocksAlways then knockerActive=false
  1958. 'disables the separate(loud) knocker coil
  1959. addCredit
  1960. knockerActive=true
  1961. end if
  1962. else
  1963. if not knockerKnocksAlways then knockerActive=false
  1964. addCredits(someCredits)
  1965. '(The score motor re-enables the separate knocker after credits are added.)
  1966. end if
  1967. else
  1968. 'here the coin was refused by the rejector because creditsAddable=false.
  1969. playSound "coinEject4" 'coin reject sound.
  1970. end if
  1971. end sub
  1972.  
  1973.  
  1974. sub setCoinLockOut(aBoolean)
  1975. ' A real mechanical pinball machine can not memorize the credits of multiple
  1976. ' inserted coins,therefore a latch in the coindoor re-directs inserted coins to
  1977. ' the coin return chute when the counter is still counting or the score motor
  1978. ' is occupied with other stuff.
  1979. '
  1980. ' This routine is called by processAddCredits() to emulate this feature,because it
  1981. ' makes a characteristic noise when the score motor starts and stops.True means
  1982. ' no credits can be added,false means that credits can be added.(Don't call this
  1983. ' routine elsewhere. The value of creditsAddable is also important for the
  1984. ' proper function of the end game matching routine.)
  1985. '
  1986. ' (I am not sure if this is correctly emulated,but I remember that a mechanism
  1987. ' in the coin door of my Bally "Champ" clicked very often during many kinds of
  1988. ' actions the machine did.)
  1989.  
  1990. if not creditsAddable and not aBoolean then
  1991. creditsAddable=true
  1992. playSound "lockOutOn"
  1993. elseIf creditsAddable and aBoolean then
  1994. creditsAddable=false
  1995. playSound "lockOutOff"
  1996. end if
  1997. end sub
  1998.  
  1999. 'switch game rule set:
  2000.  
  2001. sub setGameRules(aValue)
  2002. ' This swithes the game rule set between authentic and enhanced rules and
  2003. ' updates the displays accordingly.
  2004. '
  2005. ' Attention: This should not be called while a game is running,because it
  2006. ' would mess up the saved highscore.
  2007.  
  2008. ruleSet=aValue
  2009.  
  2010. if aValue=RS_authentic then
  2011. rulesBox.text="authentic game rules"
  2012. elseIf aValue=RS_enhanced then
  2013. rulesBox.text="enhanced mode"
  2014. end if
  2015.  
  2016. releaseStuckScoreReels 'so far captive balls can make mess here
  2017. displayHighScore
  2018. updatePlayfieldLights
  2019. end sub
  2020.  
  2021. 'reset:
  2022.  
  2023. sub resetGame
  2024. ' This routine starts the reset sequence so far the gameOver relay is set.
  2025.  
  2026. ' Below are some lines of test code with that you can experiment to see how the
  2027. ' emulated reset routine reacts on unusual conditions.Especially try to
  2028. ' uncomment "ejectBall" and/or "bonus=..." to see what the simulated mechanism
  2029. ' does when the pinball machine was switched off during a game in such a situ-
  2030. ' ation.
  2031. '
  2032. ' setScore 1,12121 'TEST!!!
  2033. ' setScore 2,91010 'TEST!!!
  2034. ' setScore 3,70000 'TEST!!!
  2035. ' setScore 4,12345 'TEST!!!
  2036.  
  2037. ' debugScoreDisplayAlignment 'TEST!!!
  2038. ' bonus=12000 'TEST!!!
  2039. ' ejectBall 'TEST!!!
  2040.  
  2041. if not gameOver then doGameOver
  2042. 'The gameOver relay must be set because it also controlls the reset sequence.
  2043.  
  2044. setRelay newRound,false
  2045. 'This is important to reset the per-round stuff (spinner etc.) when
  2046. 'we start a new game.Also processCollectBonus() needs this to reset
  2047. 'the bonus ladder.
  2048.  
  2049. setIllumination true
  2050. gameIsResetting =true
  2051. reelChimeEnabled=false 'prevents a racket when score reels reset
  2052. scoreMotor.enabled=true
  2053. end sub
  2054.  
  2055.  
  2056. 'general light related routines:
  2057. '-------------------------------
  2058.  
  2059. sub setLight(aLamp,aBoolean)
  2060. ' This sets a lamp on or off depending on the value of aBoolean (useful for
  2061. ' setting a lamp according to a given boolean state variable).
  2062.  
  2063. if aBoolean and playfieldLightsEnabled then
  2064. aLamp.state=lightStateOn
  2065. else
  2066. aLamp.state=lightStateOff
  2067. end if
  2068. end sub
  2069.  
  2070.  
  2071. sub resetLights()
  2072. ' This resets playfield lights and their variables to their default state
  2073. ' when a round begins.
  2074. '
  2075. ' Hint: To make a pinball game look realistic, the 'colourless' lights must not
  2076. ' be white but instead have a quite orange, lightbulbish hue. RGB 254,235,
  2077. ' 175 is a good value to use. Due to the brightness of this colour is lower
  2078. ' than cranking R+G+B fully up to 255 (as white light could do), the entire
  2079. ' playfield and backglass picture needs to be darkened by a few percent with
  2080. ' a graphics program to make the lamps appear bright enough. Usually bright-
  2081. ' ness and contrast need to be reduced by the same amount. The brightness
  2082. ' also should be set in a way that the black parts of a playfield look
  2083. ' really almost black and not just grey.
  2084. '
  2085. ' Generally the key for a realistic looking playfield is the use of lightbulb
  2086. ' light effects. For these transparent round spots of yellowish orange with
  2087. ' fading rims should be painted onto the playfield picture everywhere where
  2088. ' under plastic parts light bulbs can be expected. (RGB 255,204,17 looks
  2089. ' good.)
  2090. '
  2091. ' With my German "Micrografx Picture Publisher 8" version the best looking
  2092. ' painting mode for drawing light spots is "Schicht"(i.e. layer) with a
  2093. ' transparency about 42% and 100% "Verwischen" (fading brush rims).
  2094.  
  2095. setRelay A,false
  2096. setRelay B,false
  2097. setRelay C,false
  2098. setRelay D,false
  2099. setRelay A_lock,false
  2100. setRelay B_lock,false
  2101. setRelay C_lock,false
  2102. setRelay D_lock,false
  2103.  
  2104. rollover1kLit=(ruleSet=RS_authentic) 'test on true or false
  2105. specialLit =false
  2106. setRelay doubleBonus,false
  2107.  
  2108. updatePlayfieldLights
  2109. lightCenterBumper (ruleSet=RS_authentic) 'test on true or false
  2110. 'The center bumper is always lit on the original pinball machine,though
  2111. 'this is standard for the authentic rule set.
  2112. end sub
  2113.  
  2114.  
  2115. sub updatePlayfieldLights()
  2116. ' This routine shall update all the pilot lamps on the playfield according to
  2117. ' their variables. It is ment as an initialization routine and it is also very
  2118. ' good for debugging states of the playfield variables.But it is not a good
  2119. ' idea to update a single changed light by calling this after setting its
  2120. ' variable. When single variables change,by performance reasons instead only its
  2121. ' corresponding lamp should be updated by hand using setLight().
  2122. '
  2123. ' Note: Although possible, this routine should NOT be called by a fast timer to
  2124. ' periodically update lights when the variables change,because this would
  2125. ' waste much CPU time.
  2126. '
  2127. ' When playfieldLightsEnabled=false,this routine switches all lamps off
  2128. ' (implemented in setLight() ). The "shoot again" lamp is independent from this
  2129. ' (see setShootAgain() ).
  2130. '
  2131. ' When playfield lights are initialized,then this routine must be the central
  2132. ' place to do this,instead of setting some lamps here and others there in the
  2133. ' code.
  2134.  
  2135. setLight lampLeftCLaneMid ,true
  2136. setLight lampRightCLaneMid,true
  2137. setLight lampLeft1k ,rollover1kLit
  2138. setLight lampRight1k ,rollover1kLit
  2139. setLight lamp_DB ,doubleBonus
  2140. setLight lampLeftCLaneEnd ,specialLit
  2141. setLight lampRightCLaneEnd,specialLit
  2142.  
  2143. setLight lampTarget_A ,not A
  2144. setLight lampTarget_B ,not B
  2145. setLight lampTarget_C ,not C
  2146. setLight lampTarget_D ,not D
  2147. setLight lampLeftCLane_A ,A
  2148. setLight lampLeftCLane_B ,B
  2149. setLight lampRightCLane_C,C
  2150. setLight lampRightCLane_D,D
  2151.  
  2152. setSpinnerLights spins
  2153. setBonusLights bonus 'this also updates extra ball lights
  2154. end sub
  2155.  
  2156.  
  2157. sub setPlayfieldLights(aBoolean)
  2158. ' This switches the pilot lamps on the playfield on or off.Important is that switching it
  2159. ' on again during a game relies on a complete implementation of updatePlayfieldLights().
  2160.  
  2161. playfieldLightsEnabled=aBoolean
  2162. updatePlayfieldLights
  2163. end sub
  2164.  
  2165.  
  2166. sub setBackboxLights(aBoolean)
  2167. ' This switches various display lamps in the backbox on or off. To make this
  2168. ' work correctly,they must stay dark when they become set and
  2169. ' backboxLightsEnabled=false.
  2170. '
  2171. ' The "game over" lamp is wired independently and therefore not affected by
  2172. ' this.
  2173.  
  2174. backboxLightsEnabled=aBoolean
  2175. updateBackboxLights
  2176. end sub
  2177.  
  2178.  
  2179. sub updateBackboxLights()
  2180. ' This routine shall update various display lamps in the backbox according to
  2181. ' their variables.
  2182.  
  2183. setBallCounterLights ballCount
  2184. setPlayerLights players
  2185. setPlayerUpLights currentPlayer
  2186. end sub
  2187.  
  2188.  
  2189. sub setIllumination(aBoolean)
  2190. ' This switches the general illumination (playfield plastics illumination and
  2191. ' some backbox illumination lights) on or off.
  2192.  
  2193. if illumination=aBoolean then exit sub
  2194.  
  2195. If aBoolean then
  2196. captLaneLLit.isDropped =false
  2197. captLaneRLit.isDropped =false
  2198. bankLLit.isDropped =false
  2199. bankRLit.isDropped =false
  2200. slingshotLLit.isDropped=false
  2201. slingshotRLit.isDropped=false
  2202.  
  2203. lampHead.state =lightStateOn
  2204. lampKang1.state =lightStateOn
  2205. lampKang2.state =lightStateOn
  2206. lampTitle1.state=lightStateOn
  2207. lampTitle2.state=lightStateOn
  2208. 'These are the translucent objects on the backglass.Sorry that they
  2209. 'have an ugly black rim;a Visual Pinball bug draws a black rectangle
  2210. 'around every lit lamp behind a decal.
  2211. else
  2212. captLaneLLit.isDropped =true
  2213. captLaneRLit.isDropped =true
  2214. bankLLit.isDropped =true
  2215. bankRLit.isDropped =true
  2216. slingshotLLit.isDropped=true
  2217. slingshotRLit.isDropped=true
  2218. lampHead.state =lightStateOff
  2219. lampKang1.state =lightStateOff
  2220. lampKang2.state =lightStateOff
  2221. lampTitle1.state=lightStateOff
  2222. lampTitle2.state=lightStateOff
  2223. end if
  2224.  
  2225. setRelay illumination,aBoolean
  2226. end sub
  2227.  
  2228.  
  2229. sub lightCenterBumper(aBoolean)
  2230. ' This switches the light of the center bumper on or off.
  2231.  
  2232. setRelay centerBumperLit,aBoolean
  2233. If aBoolean=true then
  2234. centerBumper.state =lightStateOn
  2235. litCenterFloor.isDropped=false
  2236. else
  2237. centerBumper.state =lightStateOff
  2238. litCenterFloor.isDropped=true
  2239. end if
  2240.  
  2241. 'Note:
  2242. 'To simulate an illuminated floor part, a flat wall object with the illuminated
  2243. 'floor texture is risen or dropped here. Due to even a dropped object has still
  2244. 'a height above the playfield, its bottom height must be set below the playfield
  2245. 'to become capable to sink completely.
  2246.  
  2247. 'Due to unlit bumpers look awfully dark on Visual Pinball, for the center
  2248. 'bumper a white texture with a checkered pixel pattern is used of that
  2249. 'every 2nd pixel its pixels is set to the transparent colour; though the light
  2250. 'of the lit bumper can shine through the transparent pixels while the rest
  2251. 'looks white - very much like light shining through a white gaze curtain at
  2252. 'your window. When the daylight turns dark, you can still see the white of the
  2253. 'curtain by room light from the other side, and not just the black of the night
  2254. 'behind it.
  2255. 'Also here, the lamp of the bumper was set to a lightbulbish orange tint, and
  2256. 'not to the awful xenon-tube white that is default in Visual Pinball.
  2257. '
  2258. 'TIP: Before you "perforate" a picture this way for transparency,I recommend to
  2259. ' resize it to the twice resolution to make it look less gritty.
  2260.  
  2261. 'BUG: Since I added playfield illumination,due to a Visual Pinball bug the spinner
  2262. ' is now partly invisible when the lit floor is not dropped. This seems to be
  2263. ' a Z-buffer rendering problem.I therefore placed a triangle with Can Drop
  2264. ' property directly under the playfield at the spinner in a way that both
  2265. ' spinner posts stand on it. Despite it is invisible,its bare existence seems
  2266. ' to convince the Z-Buffer to display the spinner correctly. (The less control
  2267. ' points an object has,the less CPU time is needed, therefore a triangular
  2268. ' object burdens the CPU the least.)
  2269. '
  2270. ' The surface order recognition algorithm of Visual Pinball seems to be as
  2271. ' lousy as the one in the terrible "Hard Drivin'" (3D car racing) home computer
  2272. ' versions.
  2273. end sub
  2274.  
  2275.  
  2276. 'small counters and state wheel emulations:
  2277. '------------------------------------------
  2278.  
  2279. 'credit counter:
  2280. '
  2281. 'This credit display employs the "reel-a-listic" technology to simulate counter
  2282. 'reel rotation.(See score counter section for more explanations of the used
  2283. 'technical tricks.)
  2284.  
  2285. sub addCredits(anInteger)
  2286. ' This adds multiple credits to the credit counter using the score motor,which
  2287. ' calls addCredit() multiple times.
  2288. '
  2289. ' Warning: This routine must only be called when scoreMotorAngle=0,otherwise
  2290. ' it would count wrong. (Test on "if creditsAddable=true" for this.)
  2291.  
  2292. unaddedCredits=unaddedCredits+anInteger
  2293. scoreMotor.enabled=true
  2294. If unaddedCredits > 0 Then
  2295. DOF 132, DOFOn
  2296. Else
  2297. DOF 132, DOFOff
  2298. End If
  2299. end sub
  2300.  
  2301.  
  2302. sub addCredit()
  2303. ' This adds 1 credit to the credit counter and updates the counter display.
  2304. ' (This routine does not depend on the score motor but is called from there.)
  2305.  
  2306. if credits=maxCredits and not creditOverpayBounce then exit sub
  2307.  
  2308. playSoundMonophonic "creditSolenoid"
  2309.  
  2310. if knockerEnabled and knockerActive then playSound SoundfXDOF("knocker",131,DOFPulse,DOFKnocker):DOF 111, DOFPulse
  2311. 'This triggers a loud separate knocker when the credit was added by anything
  2312. 'else than inserting a coin.
  2313.  
  2314. if credits<maxCredits then credits = credits+1
  2315. 'I increment the credit counter variable already here because between the
  2316. 'creditAdder timer and the scoreMotor timer exists a race condition that in
  2317. 'rare cases (only on slow PCs?) sometimes made the counter count wrong.
  2318. 'For a physically correct behaviour this instead should be done in
  2319. 'creditAdder_timer().
  2320.  
  2321. setCreditDisplay credits
  2322. creditAdder.enabled=true
  2323. If B2SOn Then Controller.B2ssetcredits Credits
  2324. end sub
  2325.  
  2326. sub creditAdder_timer()
  2327. 'This timer updates the credit display after a small delay to simulate a
  2328. 'rotation at the new stable position.It also plays mechanical sounds after
  2329. 'the delay to achieve a more realistic sound timing for the credit counter.
  2330.  
  2331. creditAdder.enabled=false
  2332. playSound "creditCounter" 'tinny knock/clang of the credit wheel
  2333. 'I remember that the credit counter knocks allways,and not only by
  2334. 'replay credits.
  2335.  
  2336. spinCreditReel
  2337. end sub
  2338.  
  2339.  
  2340. sub decrementCredit()
  2341. ' This subtracts 1 credit from the credit counter.
  2342.  
  2343. if credits>0 then
  2344. credits=credits-1
  2345. setCreditDisplay credits
  2346. If B2SOn Then Controller.B2ssetcredits Credits
  2347. creditAdder.enabled=true
  2348.  
  2349. if credits=maxCredits-1 and unaddedCredits=0 and scoreMotorAngle=0 then setCoinLockOut false
  2350. 'When overpaying blocked the coin chutes,this will free them again because
  2351. 'now there will be at least 1 credit less.
  2352. end if
  2353.  
  2354. setRelay creditTaken,false 'credit relay cleared
  2355. saveGame 'save new credit counter value
  2356. end sub
  2357.  
  2358.  
  2359. sub setCreditDisplay(aValue)
  2360. ' This displays on the credit counter reel the number aValue halfway rotated
  2361. ' to the next value.
  2362.  
  2363. dim top
  2364. if aValue<maxCredits then top=aValue+1 else top=""
  2365.  
  2366. creditLow.text =credits 'print bottom number
  2367. creditClr1.text="" 'clear its lower half
  2368. creditTop.text =top 'print top number
  2369. creditClr0.text="" 'clear its upper half
  2370. end sub
  2371.  
  2372.  
  2373. sub spinCreditReel()
  2374. ' This updates the credit display and its lamp.This routine must also be called
  2375. ' during init of this program.
  2376.  
  2377. if credits=0 and credit0UnlightsCounter then
  2378. lampCreditBox.state=lightStateOff
  2379. else
  2380. lampCreditBox.state=lightStateOn
  2381. end if
  2382. 'This darkens credit counter illumination at 0 credit. (I am not sure if
  2383. 'this is correct.)
  2384.  
  2385. creditClr1.text=""
  2386. creditClr0.text=""
  2387. creditTop.text =""
  2388. creditLow.text =""
  2389. creditMid.text =credits
  2390. If B2SOn Then Controller.B2SSetcredits Credits
  2391. end sub
  2392.  
  2393.  
  2394. 'ball count assembly:
  2395.  
  2396. sub ballCounterReset_timer
  2397. ' Enabling this timer resets the ball counter and buzzes with the correct
  2398. ' length to simulate the state wheel mechanism behaviour.
  2399. '
  2400. ' Warning: to function properly,ballCount must be initialized with 1 before use.
  2401.  
  2402. if ballCount>1 then
  2403. ballCount=ballCount-1
  2404. playSound "diverter"
  2405. else
  2406. ballCounterReset.enabled=false
  2407. end if
  2408.  
  2409. setBallCounterLights ballCount
  2410. end sub
  2411.  
  2412.  
  2413. function isBallCounterResetted()
  2414. ' just a useless feature to emulate the score motor driven reset sequence
  2415. if ballCount=1 then isBallCounterResetted=true else isBallCounterResetted=false
  2416. end function
  2417.  
  2418.  
  2419. sub incrementBallCounter()
  2420. ' This lets the ball counter count 1 ball forward and displays the new value.
  2421. '
  2422. ' Note: In these old Bally machines there is only one ball counter wheel for all
  2423. ' players, though the players can never habe different numbers of balls
  2424. ' per game. Due to all players are strictly served in the sequence 1,2,3,4,
  2425. ' 1,2..., it can never happen that a 'later' player has more balls left than
  2426. ' an earlier player.(A 'shoot again' only prevents loosing a ball, but never
  2427. ' adds a new ball to the counter, because otherwise the later players would
  2428. ' get the additional ball too, because the ball counter state wheel can not
  2429. ' distinguish to whom the added ball would belong.)
  2430. '
  2431. ' In the "Boomerang" flyer stands that these machines could be modified to
  2432. ' support "add-a-ball", i.e. that a player can launch multiple of his balls
  2433. ' into play at any time during a game. Due to there is only one ball counter,
  2434. ' I can only imagine that for this the machine was re-wired in a way that
  2435. ' every player finishes all his balls and only after he goes game over, the
  2436. ' next player could play (i.e. after the modification players can't play
  2437. ' their rounds alternatingly anymore, what I find quite stupid.
  2438.  
  2439. if ballCount<=maxBalls+1 then ballCount = ballCount+1
  2440.  
  2441. 'The counter in a real machine can step 1 position further than maxBalls.
  2442.  
  2443. playSound "diverter"
  2444. setBallCounterLights ballCount
  2445. end sub
  2446.  
  2447.  
  2448. sub setBallCounterLights(aValue)
  2449. ' This routine simulates a row of 5 number-shaped lamps on the backbox to
  2450. ' display the ball counter.Use -1 (or 0) to clear all lamps.
  2451.  
  2452. ball1Text.text=" "
  2453. ball2Text.text=" "
  2454. ball3Text.text=" "
  2455. ball4Text.text=" "
  2456. ball5Text.text=" "
  2457. If B2SOn Then Controller.B2SSetBallInPlay 32, 0
  2458. if not backboxLightsEnabled then exit sub
  2459.  
  2460. if aValue=1 then
  2461. ball1Text.text="1"
  2462. elseIf aValue=2 then ball2Text.text="2"
  2463. elseIf aValue=3 then ball3Text.text="3"
  2464. elseIf aValue=4 then ball4Text.text="4"
  2465. elseIf aValue=5 then ball5Text.text="5"
  2466. end if
  2467. If B2SOn Then Controller.B2SSetBallInPlay 32, ballcount
  2468. 'At game over the ball counter advances 1 beyond ballsPerGame (also in a real
  2469. 'Bally machine),but the lamps stay off in this case.
  2470. end sub
  2471.  
  2472. 'player counter assembly: (Bally "coin unit")
  2473.  
  2474. sub playerCounterReset_timer
  2475. ' Enabling this timer resets the player counter and buzzes with the correct
  2476. ' length to simulate the state wheel mechanism behaviour.
  2477. '
  2478. ' Warning: to function properly,players must be initialized with 1 before use.
  2479.  
  2480. if players>1 then
  2481. players=players-1
  2482. playSound "diverter"
  2483. else
  2484. playerCounterReset.enabled=false
  2485. end if
  2486.  
  2487. setPlayerLights players
  2488. end sub
  2489.  
  2490.  
  2491. function isPlayerCounterResetted()
  2492. ' just a useless feature to emulate the score motor driven reset sequence
  2493. if players=1 then isPlayerCounterResetted=true else isPlayerCounterResetted=false
  2494. end function
  2495.  
  2496.  
  2497. sub addPlayer()
  2498. 'This adds a player to the game and updates the player counter display.
  2499.  
  2500. playSound "diverter" 'loud click when counter wheel steps
  2501. if players<maxPlayers then players=players+1
  2502. setPlayerLights players
  2503. end sub
  2504.  
  2505.  
  2506. sub setPlayerLights(aValue)
  2507. ' This sets the player counter display and lights the score counter according to
  2508. ' the number of players.Use -1 (or 0) to clear all lamps.
  2509.  
  2510. dim t
  2511.  
  2512. if backboxLightsEnabled then t=aValue else t=-1
  2513.  
  2514. if t>=1 then
  2515. lamp1CanPlay.state=lightStateOn
  2516. lampScoreP1.state =lightStateOn
  2517. If B2SOn Then Controller.B2SSetCanPlay 31, t
  2518. else
  2519. lamp1CanPlay.state=lightStateOff
  2520. lampScoreP1.state =lightStateOff
  2521. end if
  2522.  
  2523. if t>=2 then
  2524. lamp2CanPlay.state=lightStateOn
  2525. lampScoreP2.state =lightStateOn
  2526. If B2SOn Then Controller.B2SSetCanPlay 31, t
  2527. else
  2528. lamp2CanPlay.state=lightStateOff
  2529. lampScoreP2.state =lightStateOff
  2530. end if
  2531.  
  2532. if t>=3 then
  2533. lamp3CanPlay.state=lightStateOn
  2534. lampScoreP3.state =lightStateOn
  2535. If B2SOn Then Controller.B2SSetCanPlay 31, t
  2536. else
  2537. lamp3CanPlay.state=lightStateOff
  2538. lampScoreP3.state =lightStateOff
  2539. end if
  2540.  
  2541. if t>=4 then
  2542. lamp4CanPlay.state=lightStateOn
  2543. lampScoreP4.state =lightStateOn
  2544. If B2SOn Then Controller.B2SSetCanPlay 31, t
  2545. else
  2546. lamp4CanPlay.state=lightStateOff
  2547. lampScoreP4.state =lightStateOff
  2548. end if
  2549.  
  2550. updateScoreDisplays
  2551. end sub
  2552.  
  2553. 'player-up unit:
  2554.  
  2555. sub playerUpReset_timer
  2556. ' Enabling this timer resets the playerUp counter and buzzes with the correct
  2557. ' length to simulate the state wheel mechanism behaviour.
  2558. '
  2559. ' Warning: to function properly,currentPlayer must be initialized with 1 before
  2560. ' use.
  2561.  
  2562. if currentPlayer>1 then
  2563. currentPlayer=currentPlayer-1
  2564. playSound "diverter"
  2565. else
  2566. playerUpReset.enabled=false
  2567. end if
  2568.  
  2569. setPlayerUpLights currentPlayer
  2570. end sub
  2571.  
  2572.  
  2573. function isPlayerUpResetted()
  2574. ' just a useless feature to emulate the score motor driven reset sequence
  2575. if currentPlayer=1 then isPlayerUpResetted=true else isPlayerUpResetted=false
  2576. end function
  2577.  
  2578.  
  2579. sub nextPlayerUp()
  2580. ' This sets the next player as the current player,depending on the number of
  2581. ' players per game stored in the player counter state wheel.
  2582.  
  2583. playSound "diverter" 'loud click when player wheel steps
  2584.  
  2585. if currentPlayer<players then
  2586. setCurrentPlayer currentPlayer+1
  2587. else
  2588. setCurrentPlayer 1
  2589. 'I don't know if the state wheel should be resetted here or if it just steps
  2590. 'around and uses different contacts for different numbers of players
  2591. 'or anything like that.
  2592. end if
  2593. end sub
  2594.  
  2595.  
  2596. sub setCurrentPlayer(aPlayerNumber)
  2597. ' This sets the player (1 to 4) that is actually playing and lights its lamp
  2598. ' on the backbox.
  2599.  
  2600. if aPlayerNumber<>currentPlayer then
  2601. releaseStuckScoreReels 'so far any are stuck
  2602. currentPlayer=aPlayerNumber
  2603. end if
  2604. setPlayerUpLights currentPlayer
  2605. end sub
  2606.  
  2607.  
  2608. sub setPlayerUpLights(aValue)
  2609. ' This lights the player-up lamp on backbox.Use -1 (or 0) to clear all lamps.
  2610.  
  2611. dim t
  2612.  
  2613. if backboxLightsEnabled then t=aValue else t=-1
  2614. If B2SOn Then Controller.B2SSetPlayerUp 30,0
  2615. if t=1 then lamp1Up.state=lightStateOn:If B2SOn Then Controller.B2SSetPlayerUp 30,t else lamp1Up.state=lightStateOff
  2616. if t=2 then lamp2Up.state=lightStateOn:If B2SOn Then Controller.B2SSetPlayerUp 30,t else lamp2Up.state=lightStateOff
  2617. if t=3 then lamp3Up.state=lightStateOn:If B2SOn Then Controller.B2SSetPlayerUp 30,t else lamp3Up.state=lightStateOff
  2618. if t=4 then lamp4Up.state=lightStateOn:If B2SOn Then Controller.B2SSetPlayerUp 30,t else lamp4Up.state=lightStateOff
  2619. end sub
  2620.  
  2621. 'spinner lights state wheel assembly:
  2622.  
  2623. sub spinnerReset_timer
  2624. ' Enabling this timer resets the spinner state wheel.
  2625. '
  2626. ' Note:I was told that the spinner state wheel of the original pinball machine
  2627. ' can not reset,though I use this only in my enhanced rule set but not
  2628. ' in the authentic rule set one.
  2629.  
  2630. if spins>0 then
  2631. spins=spins-1
  2632. playSound "diverter"
  2633. else
  2634. spinnerReset.enabled=false
  2635. end if
  2636. setSpinnerLights spins
  2637. end sub
  2638.  
  2639.  
  2640. function isSpinnerResetted()
  2641. ' another dummy for emulation
  2642. if spins=0 then isSpinnerResetted=true else isSpinnerResetted=false
  2643. end function
  2644.  
  2645.  
  2646. sub incrementSpinner()
  2647. spins=spins+1
  2648.  
  2649. if spins>10 then
  2650. spins=0
  2651. incrementBonus 'Bonus added and spinner lights go off again
  2652. end if
  2653.  
  2654. if spins=0 and ruleSet=RS_authentic then spins=1
  2655.  
  2656. 'Is this rule correct? (Returning only to lamp 1 would waste a lamp,
  2657. 'and also psychologically it makes more sense when the bonus increases
  2658. 'while all lights zap away.But otherwise the walking dot in authentic
  2659. 'mode looks more correct to me when there stays always a light visible,
  2660. 'similar like a hand on a clock face.)
  2661.  
  2662. if spins=0 or not playfieldLightsEnabled then
  2663. lampSpin1.State = LightStateOff
  2664. lampSpin2.State = LightStateOff
  2665. lampSpin3.State = LightStateOff
  2666. lampSpin4.State = LightStateOff
  2667. lampSpin5.State = LightStateOff
  2668. lampSpin6.State = LightStateOff
  2669. lampSpin7.State = LightStateOff
  2670. lampSpin8.State = LightStateOff
  2671. lampSpin9.State = LightStateOff
  2672. lampSpin10.State = LightStateOff
  2673. else
  2674. setSpinnerLights spins
  2675. end if
  2676. end sub
  2677.  
  2678.  
  2679. sub setSpinnerLights(aValue)
  2680. ' This lights on playfield the spinner lamps for aValue.Use -1 (or 0) to clear
  2681. ' all lamps.
  2682. '
  2683. ' I was told that the original pinball machine only lights 1 light at a time,
  2684. ' but I kept my previous light row implementation in my enhanced rule set be-
  2685. ' cause it looks much better.
  2686.  
  2687. dim t
  2688.  
  2689. if playfieldLightsEnabled then t=aValue else t=-1
  2690.  
  2691. if ruleSet=RS_authentic then
  2692. 'This lights only the lamp with the number t (authentic Bally).
  2693.  
  2694. if t=1 then lampSpin1.State = LightStateOn else lampSpin1.State = LightStateOff
  2695. if t=2 then lampSpin2.State = LightStateOn else lampSpin2.State = LightStateOff
  2696. if t=3 then lampSpin3.State = LightStateOn else lampSpin3.State = LightStateOff
  2697. if t=4 then lampSpin4.State = LightStateOn else lampSpin4.State = LightStateOff
  2698. if t=5 then lampSpin5.State = LightStateOn else lampSpin5.State = LightStateOff
  2699. if t=6 then lampSpin6.State = LightStateOn else lampSpin6.State = LightStateOff
  2700. if t=7 then lampSpin7.State = LightStateOn else lampSpin7.State = LightStateOff
  2701. if t=8 then lampSpin8.State = LightStateOn else lampSpin8.State = LightStateOff
  2702. if t=9 then lampSpin9.State = LightStateOn else lampSpin9.State = LightStateOff
  2703. if t=10 then lampSpin10.State= LightStateOn else lampSpin10.State= LightStateOff
  2704.  
  2705. else
  2706. 'With enhanced game rules this displays a row of t lights (looks better).
  2707.  
  2708. if t>=1 then lampSpin1.State = LightStateOn else lampSpin1.State = LightStateOff
  2709. if t>=2 then lampSpin2.State = LightStateOn else lampSpin2.State = LightStateOff
  2710. if t>=3 then lampSpin3.State = LightStateOn else lampSpin3.State = LightStateOff
  2711. if t>=4 then lampSpin4.State = LightStateOn else lampSpin4.State = LightStateOff
  2712. if t>=5 then lampSpin5.State = LightStateOn else lampSpin5.State = LightStateOff
  2713. if t>=6 then lampSpin6.State = LightStateOn else lampSpin6.State = LightStateOff
  2714. if t>=7 then lampSpin7.State = LightStateOn else lampSpin7.State = LightStateOff
  2715. if t>=8 then lampSpin8.State = LightStateOn else lampSpin8.State = LightStateOff
  2716. if t>=9 then lampSpin9.State = LightStateOn else lampSpin9.State = LightStateOff
  2717. if t =10 then lampSpin10.State= LightStateOn else lampSpin10.State= LightStateOff
  2718. end if
  2719. end sub
  2720.  
  2721. 'bonus ladder assembly: ("bonus unit")
  2722.  
  2723. sub incrementBonus()
  2724. ' This routine increments the bonus ladder by const bonusStepWidth.
  2725.  
  2726. if bonus=maxBonus or tilt and tiltPreventsAddBonus then exit sub
  2727.  
  2728. bonus=bonus+bonusStepWidth
  2729. playSound "diverter" 'loud click sound when bonus wheel steps
  2730.  
  2731. if ruleSet=RS_enhanced and bonusClearsBumper and bonus=topHoleShootAgain then lightCenterBumper false
  2732. 'This unlights the center bumper in enhanced game rule mode when the bonus
  2733. 'reached the topHoleShootAgain value as a hint to shoot the tophole.
  2734. '(I don't do this in setBonusLights() because it shall be triggered only
  2735. 'once and not when the lights get updated by any other reasons.)
  2736.  
  2737. setBonusLights bonus
  2738. end sub
  2739.  
  2740.  
  2741. sub decrementBonus()
  2742. ' This routine steps down the bonus ladder by const bonusStepWidth.
  2743.  
  2744. if bonus>0 then playSound "diverter" 'loud click sound when bonus wheel steps
  2745. subtractUntil0 bonus,bonusStepWidth
  2746. setBonusLights bonus
  2747. end sub
  2748.  
  2749.  
  2750. sub setBonusLights(aValue)
  2751. ' This routine displays the lights of the bonus ladder according to the actual
  2752. ' value of the argument variable. This is an approximation of what the bonus
  2753. ' state wheel in a real mechanical pinball machine does.
  2754. '
  2755. ' Due to both extra ball lights and specials are also controlled by contacts
  2756. ' on the wheel (a guess from Bally "Champ"),these are also set here depending
  2757. ' on the actual bonus value.
  2758. '
  2759. ' (This routine is game rule dependant and though must be alterated for
  2760. ' emulating other pinball machines.)
  2761.  
  2762. dim x,value
  2763.  
  2764. 'set extra ball lamps: (At bonus >=15000 both lamps are lit.)
  2765.  
  2766. if playfieldLightsEnabled then value=aValue else value=0
  2767. 'This disables all bonus lights when playfieldLightsEnabled=false.
  2768. '(Note: As a side effect then also the extraBallLit variables become false.)
  2769.  
  2770. leftExtraBallLit = false
  2771. rightExtraBallLit = false
  2772. lampLeft_XB.State = LightStateOff
  2773. lampRight_XB.State = LightStateOff
  2774.  
  2775. if value=8000 or value=10000 or value=12000 or value=14000 or value>=15000 then
  2776. lampLeft_XB.State = LightStateOn
  2777. leftExtraBallLit = true
  2778. end if
  2779. if value=9000 or value=11000 or value=13000 or value>=15000 then
  2780. lampRight_XB.State = LightStateOn
  2781. rightExtraBallLit = true
  2782. end if
  2783.  
  2784. 'set bonus ladder lamps:
  2785.  
  2786. x=value mod 10000
  2787.  
  2788. lampBonus1k.State = LightStateOff 'all bonus lamps off
  2789. lampBonus2k.State = LightStateOff
  2790. lampBonus3k.State = LightStateOff
  2791. lampBonus4k.State = LightStateOff
  2792. lampBonus5k.State = LightStateOff
  2793. lampBonus6k.State = LightStateOff
  2794. lampBonus7k.State = LightStateOff
  2795. lampBonus8k.State = LightStateOff
  2796. lampBonus9k.State = LightStateOff
  2797. lampBonus10k.State= LightStateOff
  2798.  
  2799. if ruleSet=RS_authentic then
  2800.  
  2801. 'This lights only the lamp with the number x (authentic Bally).
  2802.  
  2803. select case x
  2804. case 1000: lampBonus1k.State=lightStateOn
  2805. case 2000: lampBonus2k.State=lightStateOn
  2806. case 3000: lampBonus3k.State=lightStateOn
  2807. case 4000: lampBonus4k.State=lightStateOn
  2808. case 5000: lampBonus5k.State=lightStateOn
  2809. case 6000: lampBonus6k.State=lightStateOn
  2810. case 7000: lampBonus7k.State=lightStateOn
  2811. case 8000: lampBonus8k.State=lightStateOn
  2812. case 9000: lampBonus9k.State=lightStateOn
  2813. end select
  2814.  
  2815. else
  2816. 'With enhanced game rules this displays a vertical bar of x lights (looks better).
  2817.  
  2818. if x>=1000 then lampBonus1k.State = LightStateOn
  2819. if x>=2000 then lampBonus2k.State = LightStateOn
  2820. if x>=3000 then lampBonus3k.State = LightStateOn
  2821. if x>=4000 then lampBonus4k.State = LightStateOn
  2822. if x>=5000 then lampBonus5k.State = LightStateOn
  2823. if x>=6000 then lampBonus6k.State = LightStateOn
  2824. if x>=7000 then lampBonus7k.State = LightStateOn
  2825. if x>=8000 then lampBonus8k.State = LightStateOn
  2826. if x =9000 then lampBonus9k.State = LightStateOn
  2827. end if
  2828.  
  2829. if value>=10000 then lampBonus10k.State = LightStateOn
  2830.  
  2831. 'set specials:
  2832.  
  2833. if specialLitValue<>false and bonus>=specialLitValue and not specialHit then
  2834. 'light both specials when enough bonus
  2835. lampLeftCLaneEnd.State = LightStateOn
  2836. lampRightCLaneEnd.State = LightStateOn
  2837. specialLit=true
  2838. end if
  2839. end sub
  2840.  
  2841. 'end game digit matching assembly: (Bally "00-90 unit")
  2842.  
  2843. sub advanceMatcher()
  2844. ' This advances the simulated state wheel of the pseudo random number generator
  2845. ' for the end game digit matching feature of the mechanical pinball machine.
  2846.  
  2847. playSound "rflipper" 'quiet click
  2848.  
  2849. if matchNumber>=9 then
  2850. matchNumber=0
  2851. else
  2852. matchNumber=matchNumber+1
  2853. end if
  2854. end sub
  2855.  
  2856.  
  2857. sub setEndMatchLights(aValue)
  2858. ' This lights a number on the 00-90 score end digit matching lamp row on the
  2859. ' backglass. aValue must be between 0 and 9 to light one of the lamps,or -1
  2860. ' to light none.
  2861.  
  2862. dim i,t
  2863.  
  2864. t="" & aValue 'string of aValue
  2865. for i=0 to matchedScoreDigit
  2866. t=t&"0"
  2867. 'concatenates the correct number of zeroes behind the digit
  2868. next
  2869.  
  2870. if aValue=0 then match0Text.text=t else match0Text.text=""
  2871. if aValue=1 then match1Text.text=t else match1Text.text=""
  2872. if aValue=2 then match2Text.text=t else match2Text.text=""
  2873. if aValue=3 then match3Text.text=t else match3Text.text=""
  2874. if aValue=4 then match4Text.text=t else match4Text.text=""
  2875. if aValue=5 then match5Text.text=t else match5Text.text=""
  2876. if aValue=6 then match6Text.text=t else match6Text.text=""
  2877. if aValue=7 then match7Text.text=t else match7Text.text=""
  2878. if aValue=8 then match8Text.text=t else match8Text.text=""
  2879. if aValue=9 then match9Text.text=t else match9Text.text=""
  2880. end sub
  2881.  
  2882. 'relay
  2883. '-----
  2884.  
  2885. sub setRelay(aRelayName,aBoolean)
  2886. ' This sets the boolean variable aRelayName to the value aBoolean and clicks
  2887. ' when the variable had not already the same value to simulate a relay click.
  2888.  
  2889. if not aRelayName and aBoolean then
  2890. aRelayName=true
  2891. playSound "relayOn"
  2892. elseIf aRelayName and not aBoolean then
  2893. aRelayName=false
  2894. playSound "relayOff"
  2895. end if
  2896. end sub
  2897.  
  2898.  
  2899. 'the score motor (main game logics sequencing unit)
  2900. '~~~~~~~~~~~~~~~
  2901.  
  2902. sub scoreMotor_timer()
  2903. ' This simulates the score motor,which is the central main command sequencing
  2904. ' unit in an electro-mechanical Bally pinball machine.
  2905. '
  2906. ' The score motor consists of a motor that rotates a camshaft with multiple
  2907. ' toothed wheels on it.Above each wheel is a stack of leaf switch contacts
  2908. ' those get opened and or closed by the teeth(cams) when the wheels turn.
  2909. ' The camshaft can only rotate a full turn, because only at its 0 position
  2910. ' is a contact that can stop the motor when it has completed a job.
  2911. '
  2912. ' You can roughly compare the assembly with a mechanical wind-up music box
  2913. ' clockwork,which is designed always to plays the melody to the end before
  2914. ' the cylinder stops at fixed position. Like the tines on the cylinder play
  2915. ' their tones in a fixed sequence, also the leaf switch stacks above the
  2916. ' camshaft move in the same sequence no matter what the relays do.
  2917. '
  2918. ' A Bally-specific special feature of the score motor is that all cam wheels
  2919. ' on the camshaft have 2 equal hemispheres with each the full set of cams,
  2920. ' and therefore the capstan turns instead of full 360 degree rotations
  2921. ' physically only 180 degree until it stops. Though what I modelled as a full
  2922. ' logical 360 degree rotation would be technically only a half rotation. To
  2923. ' make adaptions for other score motor brands easier,I despite devided each
  2924. ' rotation into 360 degree.
  2925. '
  2926. '
  2927. ' This subroutine is called by a timer again and again, and though can be
  2928. ' imagined as the inner part of a for-loop with the running variable
  2929. ' scoreMotorAngle. Like the score motor in a real pinball machine,it serves
  2930. ' many different purposes and therefore contradicts the paradigms of modern
  2931. ' software engineering. What it does depending on which boolean variables
  2932. ' (equivalent to relays in a real pinball machine) are actually set.
  2933.  
  2934. if scoreMotorAngle=0 then
  2935. stopSound "motorHum1"
  2936. playSound "motorHum1",-1
  2937. '(re-)starts endless loop sample of Bally score motor hum
  2938. end if
  2939.  
  2940. processBallInTopHole
  2941. processResetSequence
  2942. processCollectBonus
  2943. processEndGameMatching
  2944. processAddCredits
  2945. processAddScore
  2946.  
  2947. 'Note: The order of the above commands is crucial for the correct behaviour
  2948. ' of this emulation,because unlike with real in series wired relay
  2949. ' switches, the if-statements of this BASIC programs can't recognize when
  2950. ' the sequence of testing and setting a boolean variable is reversed.
  2951. ' (The real circuit would behave rather like when all if-statements in the
  2952. ' above routines would be continuously checked simultaneously. A circuit
  2953. ' simulation program based on constraint logics could simulate this,but
  2954. ' I doubt that this can be done in this poor BASIC,and it also would likely
  2955. ' consume even more CPU capacity than this already quite CPU intensive
  2956. ' piece of code takes.)
  2957.  
  2958. scoreMotorAngle=scoreMotorAngle+motorStepWidth
  2959.  
  2960. if scoreMotorAngle>359 then '360 degree shall not happen
  2961. 'now check how to start the next camshaft round:
  2962.  
  2963. if not keepMotorRunning then
  2964. stopSound "motorHum1" 'stops motor rotation sound
  2965. scoreMotor.enabled=false
  2966. else
  2967. keepMotorRunning=false
  2968. end if
  2969.  
  2970. 'The motor stops after one complete round of the camshaft so far the flag
  2971. 'keepMotorRunning isn't set true. Otherwise the motor continues spinning the
  2972. 'next cycle,but keepMotorRunning turns false by this,though the flag each time
  2973. 'needs to be set true again to extend the motor spin time by another round.
  2974.  
  2975. scoreMotorAngle=0
  2976. end if
  2977. end sub
  2978.  
  2979. ' The following boolean functions simulate the cams(teeth) on the wheels rotated
  2980. ' by the score motor. Depending on how many teeth a wheel has and where they
  2981. ' are,its contacts are triggered at different times depending on the actual
  2982. ' camshaft rotation angle. In this simulation the rotation angle is modelled by
  2983. ' the scoreMotorAngle variable.
  2984. '
  2985. ' Note: Important is that the modulo values for the cams can indeed be reached
  2986. ' with the current motorStepWidth setting. Otherwise the function will be
  2987. ' triggered too seldomly or not at all. The reason why I don't use a single
  2988. ' function here is because with multiple functions the exact tooth position
  2989. ' of each wheel can be easier changed by hand. The actual ones are only
  2990. ' guesses. Each function shall correspond to a wheel, but as well the names
  2991. ' as the count of teeth per wheel were choosen by reason and have not
  2992. ' neccessarily equivalents in Bally schematics. I test on
  2993. ' (scoreMotorAngle mod x)=y instead on "...mod x)=0" because this way the
  2994. ' 1st cam of each wheel is simulated slightly(by an angle of y) after the 0
  2995. ' position,which sounds more realistic than when the 1st pulse always starts
  2996. ' already at 0.
  2997. '
  2998. 'When a routine shall wait on a certain cam that may ocure only in the next camshaft
  2999. 'round,then keepMotorRunning must be set true to prevent the motor from stopping
  3000. 'before reaching it.(The real pinball circuit would use parallel wired switches for
  3001. 'this.)
  3002.  
  3003. 'multi-cam wheels: (good for counting)
  3004.  
  3005. function is2StepsCamOn()
  3006. is2StepsCamOn=(scoreMotorAngle=110 or scoreMotorAngle=290)
  3007. 'This one must not come before the 2nd step of is4StepsCamOn() - otherwise
  3008. 'the bonus ladder adds 1000 points to little during double bonus.
  3009. end function
  3010.  
  3011. function is3StepsCamOn()
  3012. is3StepsCamOn=((scoreMotorAngle mod 40)=20 and scoreMotorAngle<140)
  3013. end function
  3014.  
  3015. function is4StepsCamOn()
  3016. is4StepsCamOn=((scoreMotorAngle mod 90)=20 and scoreMotorAngle<=290)
  3017. end function
  3018.  
  3019. function is5StepsCamOn()
  3020. is5StepsCamOn=((scoreMotorAngle mod 50)=20 and scoreMotorAngle<270)
  3021. end function
  3022.  
  3023. function is6StepsCamOn()
  3024. is6StepsCamOn=((scoreMotorAngle mod 50)=20 and scoreMotorAngle>30)
  3025. 'The position of these cams may be inaccurate.There should be 0.5s pause before
  3026. 'the 1st and after the 6th cam.
  3027. end function
  3028.  
  3029. 'single-cam wheels: (good for sequencing)
  3030.  
  3031. function isZeroCamOn()
  3032. ' This is true when the motor stands still or is passing its default position.
  3033. ' Note: To directly test on "scoreMotorAngle=0" in the code may be clearer.
  3034. isZeroCamOn=(scoreMotorAngle=0)
  3035. end function
  3036.  
  3037. function isEarlyCamOn()
  3038. isEarlyCamOn=(scoreMotorAngle=30)
  3039. end function
  3040.  
  3041. function isMiddleCamOn()
  3042. isMiddleCamOn=(scoreMotorAngle=170)
  3043. end function
  3044.  
  3045. function isLateCamOn()
  3046. ' This cam must come later than all others to work correctly.
  3047. isLateCamOn=(scoreMotorAngle=350)
  3048. end function
  3049.  
  3050. function isCamBeforeEnd()
  3051. ' This means that the camshaft is about to reach the 0 position again (where the
  3052. ' score motor will stop when keepMotorRunning=false).Due to after the motor timer
  3053. ' stands still any procedures of the score motor can not be executed anymore until
  3054. ' the motor is started again,this function must be used instead of isZeroCamOn()
  3055. ' to do a job that is intended to be completed when the motor reaches 0 again.
  3056. '
  3057. ' Note: The value of this function may overleap with isLateCamOn().
  3058.  
  3059. isCamBeforeEnd=((scoreMotorAngle+motorStepWidth)>359)
  3060. end function
  3061.  
  3062.  
  3063. ' The following procedures are repeatedly called by the scoreMotor_timer()
  3064. ' rountine and can be imagined as the inner part of a for-loop that has
  3065. ' the running variable scoreMotorAngle.What they do depends on which camshaft
  3066. ' cam is active at the moment, and this depends on the value of scoreMotorAngle.
  3067.  
  3068. sub processAddScore()
  3069. ' This adds points to the score using the score motor. In a real Bally pinball
  3070. ' machine this is neccessary when digits need to be flipped multiple times to
  3071. ' score other point amounts than 10,100,1000 and so on.
  3072.  
  3073. if add500 and is5StepsCamOn then addScore(100)
  3074. if add3000 and is3StepsCamOn then addScore(1000)
  3075.  
  3076. ' These relays are set by the addScore...() procedures,because on a real machine
  3077. ' they can only be set when the score motor is in 0 position. Though a target
  3078. ' only scores points when the motor is not running, because when the relay would
  3079. ' be set in between, less points than the value of the target would be added
  3080. ' depending on the actual score motor position.
  3081. '
  3082. ' For emulating other pinball machines,relays with other values could be added
  3083. ' here.
  3084.  
  3085. if isLateCamOn then
  3086. 'At the end of a camshaft round the score relays have to be reset to prevent
  3087. 'endless counting:
  3088. setRelay add500 ,false
  3089. setRelay add3000,false
  3090. end if
  3091. end sub
  3092.  
  3093.  
  3094. sub processAddCredits()
  3095. ' This adds credits using the score motor. Important is that new credits must only
  3096. ' be added to unaddedCredits when scoreMotorAngle=0.Otherwise it would count wrong.
  3097. '
  3098. ' This routine must be called after processResetSequence() and processCollectBonus()
  3099. ' to set the coin rejector correctly.
  3100.  
  3101. if isZeroCamOn then setCoinLockOut true 'sets creditsAddable=false
  3102.  
  3103. 'New credits can only be added when the score motor camshaft has rotated
  3104. 'entirely to its end(i.e. the motor will stop now so far keepMotorRunning
  3105. '= false) and the credit counter is not occupied with counting old credits
  3106. 'anymore.
  3107.  
  3108. if unaddedCredits>0 then
  3109.  
  3110. 'First I had used different cam wheels for adding different number of credits,
  3111. 'but Frank Menzler told me that credits are always added by a 6 cam wheel of
  3112. 'the score motor.But I don't know how it works in detail that the number of
  3113. 'credits per coin can be adjusted for different coin chutes.
  3114. '
  3115. 'Possibly it uses boolean comparisons among multiple cam wheel switch outputs
  3116. 'to generate an adjustable count of pulses.I just remember that a Bally "Champ"
  3117. 'had tons of jumper plugs to set the credits per coin,and that in some odd
  3118. 'positions (or by bent relay contacts?) it sometimes gave something like 13
  3119. 'credits per coin.
  3120. '
  3121. 'Frank Menzler claimed that the maximum number of credits per coin would be
  3122. '12 because this would be a full physical rotation of the camshaft (i.e. 2
  3123. 'logical rotations in my model).I don't know if for adding more than 6 credits
  3124. 'an additional relay was set or if there was a special cam wheel that marks
  3125. 'by a long cam the current hemisphere of the otherwise perfectly symmetrical
  3126. 'Bally camshaft.
  3127. '
  3128. 'I just simulated the credit counter behaviour phenomenologically by adding
  3129. '1 credit during each cam of the 6 cam wheel and counting down unaddedCredits
  3130. 'to memorize the number of credits to count,but this has likely nothing to
  3131. 'do with the inner working of the real machine.
  3132.  
  3133. if is6StepsCamOn then
  3134. addCredit
  3135. unaddedCredits=unaddedCredits-1
  3136. if unaddedCredits=0 then saveGame 'save new credit counter value
  3137. end if
  3138.  
  3139. if isLateCamOn and unaddedCredits>0 then keepMotorRunning=true
  3140. 'for adding the remaining credits
  3141. end if
  3142.  
  3143. if isCamBeforeEnd and unaddedCredits=0 and not gameIsResetting then
  3144. knockerActive=true 'when it was disabled by inserting a coin
  3145. if not(overPayBlocksChutes and credits=maxCredits) then setCoinLockOut false
  3146. end if
  3147. end sub
  3148.  
  3149.  
  3150. sub processBallInTopHole()
  3151. ' This routine adds 3000 points and ejects the ball from the tophole.
  3152. ' This must be called before processAddScore() to work correctly.
  3153.  
  3154. if not ballInTopHole then exit sub
  3155.  
  3156. if scoreMotorAngle=0 then
  3157. setRelay add3000,true
  3158. 'The relay must only be set at angle 0 to count the correct amount of points.
  3159. else
  3160. keepMotorRunning=true
  3161. end if
  3162.  
  3163. if add3000 and isLateCamOn then
  3164. 'The test for add3000 prevents that the ball will be kicked out without
  3165. 'points when the motor is still running from previous activation.
  3166. playSound SoundfXDOF("Saucer",110,DOFPulse,DOFContactors)
  3167. DOF 111, DOFPulse
  3168. topHoleKicker.Kick (85+(120*rnd())),8
  3169. setRelay ballInTopHole,false
  3170. end if
  3171. end sub
  3172.  
  3173.  
  3174. sub processEndGameMatching()
  3175. ' This simulates the end digit matching feature with that players can win
  3176. ' free credits depending on the last score digits.
  3177. '
  3178. ' I know very little how this works internally. Though this is rather a dummy
  3179. ' than anything emulated. The only thing I know is that a state wheel is
  3180. ' stepped by various events to simulate a random number generator.
  3181. '
  3182. ' This procedure must be called before processAddCredits to work correctly.
  3183.  
  3184. if not digitMatchEnabled or gameIsResetting then exit sub
  3185.  
  3186. dim i,n
  3187.  
  3188. if matching then
  3189.  
  3190. if creditsAddable then
  3191. setEndMatchLights(matchNumber)
  3192. n=0
  3193. for i=1 to players
  3194. if scoreDigit(i,matchedScoreDigit)=matchNumber then n=n+1
  3195. next
  3196.  
  3197. addCredits(n)
  3198. setRelay matching,false
  3199. else
  3200. keepMotorRunning=true
  3201. end if
  3202.  
  3203. else
  3204. if isEarlyCamOn and not gameOver then advanceMatcher
  3205. 'just a guess - I don't know in which situations the random number state wheel
  3206. 'should advance.
  3207. end if
  3208. end sub
  3209.  
  3210.  
  3211. sub processResetSequence()
  3212. ' The following is an attempt to emulate the start-up sequence of a mechanical
  3213. ' Bally pinball machine. I don't know how exact my emulation is.My only source of
  3214. ' information about the reset sequence is based on the great pinball repair site
  3215. ' at http://marvin3m.com and some newsgroup articles.
  3216. '
  3217. ' This procedure must be called before processCollectBonus() to work correctly.
  3218. '
  3219. ' Important:
  3220. '
  3221. ' This is a BALLY reset sequence.EM pinball machines of other manufacturers
  3222. ' process their reset commands in different orders,though if you want to
  3223. ' faithfully emulate machines of other companies(Gottlieb,Chicago Coins etc.),
  3224. ' then the below code must be modified to correctly model their behaviour.
  3225.  
  3226. if not gameIsResetting then exit sub
  3227.  
  3228. dim i
  3229.  
  3230. if gameOver then
  3231. keepMotorRunning=true
  3232.  
  3233. if isZeroCamOn and not isScoreResetted then
  3234. for i=1 to maxPlayers
  3235. scoreDigit(i,reelsPerCounter)=0
  3236. if extraDigitsEnabled then scoreReelMain(i,reelsPerCounter).text=""
  3237. next
  3238. 'this clears higher surplus digits
  3239. end if
  3240.  
  3241. if is5StepsCamOn then
  3242. if not isScoreResetted then
  3243. for i=1 to maxPlayers
  3244. unaddedScore(i)=scoreResetNumber
  3245. scoreAddTimer(i).enabled=true
  3246. next
  3247. '(the reset relay noise is part of the counter clicks)
  3248. else
  3249. playSoundMonophonic "resetClick10Relay"
  3250. 'let the score reset relay continue clicking
  3251. end if
  3252. end if
  3253.  
  3254. 'The score reset mechanism advances all reels of all counter until they
  3255. 'have reached 0. When gameIsResetting is true, the counters don't step
  3256. 'further than 0, and also carries are ignored to speed up resetting.
  3257. '
  3258. 'A real pinball machine does this the same way,and each reel has a switch
  3259. 'in its 0 position,though after all reels are at 0, a signal current can
  3260. 'flow through all the switches to continue the reset sequence.
  3261. '
  3262. 'I don't know if it is correct that I advance all reels of all counters
  3263. 'simultaneously.Possibly a real pinball machines devided them into multiple
  3264. 'groups those are advanced alternatingly (to limit the electric peak current
  3265. 'that would be likely quite high when about 20 solenoid coils switch simul-
  3266. 'taneously?) Also my "Royal Flush" PC game advances the digits in groups
  3267. 'instead,but possibly this is just Gottlieb specific.
  3268.  
  3269. if isScoreResetted then 'after all digits reached 0
  3270.  
  3271. if isBallCounterResetted and isPlayerCounterResetted and isPlayerUpResetted then
  3272.  
  3273. 'Here the machine waits until all state wheels are resetted and that the
  3274. 'ball has returned to the outhole.When it is still in the firing chute,the
  3275. 'machine starts the new game without the need of ejecting a ball.
  3276. '(The nested if-statement keeps the CPU load low in this case,because star-
  3277. 'ting the state wheel reset timers unneccessary wasted much CPU time.)
  3278.  
  3279. if isMiddleCamOn and bonus=0 then
  3280. 'after bonus ladder "resetted" itself by counting down...
  3281.  
  3282. if creditTaken then decrementCredit 'also clears creditTaken relay
  3283. setRelay gameOver,false
  3284. gameOverText.Text = "" 'counters are resetted now.
  3285. If B2SOn Then
  3286. Controller.B2SSetGameOver 35,0
  3287. Controller.B2SSetMatch 34,0
  3288. End If
  3289. displayHighScore 'removes "new" after a previous highscore
  3290. setEndMatchLights -1 'clears end match number display
  3291. setBackboxLights true
  3292. playfieldLightsEnabled=true
  3293. resetLights '(Here the playfield lights get updated.)
  3294. Dim Fuckingcode:fuckingcode=0
  3295. If B2SOn Then
  3296. Controller.B2ssetscore 1, fuckingcode
  3297. Controller.B2ssetscore 2, fuckingcode
  3298. Controller.B2ssetscore 3, fuckingcode
  3299. Controller.B2ssetscore 4, fuckingcode
  3300. End If
  3301. 'The game will now be started by processCollectBonus() because the
  3302. 'gameOver relay was cleared.
  3303. end if
  3304. elseIf isEarlyCamOn then
  3305. 'This resets the state wheels on those we waited above.
  3306.  
  3307. ' setBackboxLights true 'TEST!!! this shows the state wheel resets
  3308. playerCounterReset.enabled=true
  3309. playerUpReset.enabled =true
  3310. ballCounterReset.enabled =true
  3311. end if
  3312. end if
  3313.  
  3314. else
  3315. 'When we are here,after a successful reset the gameOver relay was cleared...
  3316. keepMotorRunning=true
  3317. 'This waits until processCollectBonus() can eject the ball.
  3318. end if
  3319. end sub
  3320.  
  3321.  
  3322. sub processCollectBonus()
  3323. ' This routine is started by the score motor.It steps down the bonus ladder and
  3324. ' adds the bonus to the score so far there is no tilt.
  3325.  
  3326. if not (ballInDrain or gameIsResetting) then exit sub
  3327.  
  3328. 'Reset must count down the bonus even without ball in drain.
  3329.  
  3330. if bonus>0 and not newRound then
  3331. keepMotorRunning=true
  3332.  
  3333. select case ruleSet
  3334. case RS_authentic
  3335. if (not doubleBonus and is2StepsCamOn) or (doubleBonus and is4StepsCamOn) then addScore bonusStepWidth
  3336. if is2StepsCamOn then decrementBonus
  3337.  
  3338. 'This makes the bonus step down with a fixed speed and during double bonus
  3339. 'adds twice as fast const bonusStepWidth to the score.(In "Boomerang"
  3340. 'bonusStepWidth=1000 points.)
  3341.  
  3342. case RS_enhanced
  3343. if is4StepsCamOn then addScore bonusStepWidth
  3344. if (doubleBonus and is2StepsCamOn) or (not doubleBonus and is4StepsCamOn) then decrementBonus
  3345. 'In the enhanced mode I use the old version (counts faster,sounds better).
  3346. end select
  3347.  
  3348. 'is4StepsCamOn comes not before the 2nd step of is4StepsCamOn() - otherwise
  3349. 'we would empty the bonus counter to soon and though add 1x bonusStepWidth to
  3350. 'little(?) during double bonus.
  3351. '
  3352. 'In an old version of this program I added 4x bonusStepWidth per camshaft
  3353. 'round,because otherwise the bonus ladder descended very slowly,but I was
  3354. 'told that the bonus must count faster and not longer during double bonus,
  3355. 'therefore I modified this for the authentic mode.
  3356. '
  3357. 'Is my new speed and working principle assumption more correct?
  3358. else
  3359. if not gameOver then startNextRound
  3360. end if
  3361. end sub
  3362.  
  3363.  
  3364. sub startNextRound()
  3365. ' This routine is called after bonus collecting was finished.
  3366. ' It checks if we have game-over or how the game continues. This routine is
  3367. ' also called at game start by processCollectBonus().
  3368.  
  3369. if isMiddleCamOn and not newRound then
  3370.  
  3371. if not extraBallWon and not gameIsResetting then
  3372. 'Not gameIsResetting prevents mess when the pinball machine comes up and
  3373. 'the ball is still in the firing chute during reset. (E.g. after overpaying
  3374. 'the 1st coin chute.)
  3375.  
  3376. setShootAgain false
  3377. nextPlayer 'this can set game over
  3378. end if
  3379.  
  3380. setExtraBall false
  3381. setRelay specialHit,false
  3382.  
  3383. if gameOver then exit sub
  3384. 'The game over relay would prevent the rest of this procedure.
  3385.  
  3386. if not shootAgainStaysLit then setShootAgain false '("shoot again" lamps off)
  3387.  
  3388. if not ruleSet=RS_authentic then
  3389. spinnerReset.enabled=true
  3390. else
  3391. if spins=0 then spins=1
  3392. 'In authentic mode the spinner never resets,but it can not have the
  3393. 'value 0.
  3394. end if
  3395.  
  3396. unTilt 'also activates flippers & bumpers
  3397. tiltHasEffect=true
  3398. setPlayfieldLights true
  3399. setIllumination true
  3400. resetLights
  3401. if initBonusIs1000 then incrementBonus
  3402.  
  3403. ' setScore 1,96000 'TEST!!!
  3404. ' bonus=13000 'TEST!!!
  3405. ' doubleBonus=true 'TEST!!!
  3406. ' updatePlayfieldLights 'TEST!!!
  3407.  
  3408. setRelay newRound,true
  3409.  
  3410. 'This relay stays true until points are scored. Though as long no points
  3411. 'are scored in a round,the ball will be only re-ejected without collecting
  3412. 'bonus or changing any lights on the playfield. This is known as the
  3413. 'famous "fairplay interlock" feature.It can be disabled by setting const
  3414. 'restartWhen0Pts=false.
  3415. '
  3416. 'Note: The behaviour that even a tilt voids no ball in the above state is
  3417. ' realistic, because it was invented to prevent the loose of 2 balls
  3418. ' when by too much nudging the freshly ejected ball plummeted back into
  3419. ' the eject hole without scoring a single point.
  3420. '
  3421. ' By setting const tiltVoids0PtsRestart=true the behaviour at tilt can
  3422. ' be changed.
  3423. end if
  3424.  
  3425. if isLateCamOn and newRound and (isSpinnerResetted or ruleSet=RS_authentic) then
  3426. '(newRound was set above...)
  3427.  
  3428. setRelay gameIsResetting,false 'this is a guess to exit reset loop
  3429.  
  3430. reelChimeEnabled=true
  3431. releaseStuckScoreReels 'so far any are stuck
  3432. ejectBall 'so far in outhole
  3433. if not restartWhen0Pts then setRelay newRound,false 'fairplay interlock
  3434. end if
  3435. end sub
  3436.  
  3437.  
  3438. sub nextPlayer()
  3439. ' This switches to the next player or does game over depending on how many
  3440. ' balls are left and players in the game.
  3441.  
  3442. if currentPlayer>=players then
  3443. incrementBallCounter
  3444.  
  3445. if (ruleSet=RS_authentic and ballCount>ballsPerGame0) or (ruleSet=RS_enhanced and ballCount>ballsPerGame1) then
  3446. doGameOver
  3447. if digitMatchEnabled then
  3448. setRelay matching,true 'starts end digit matching
  3449. keepMotorRunning=true
  3450. end if
  3451. exit sub
  3452.  
  3453. end if
  3454. end if
  3455.  
  3456. nextPlayerUp 'This increments currentPlayer or sets it 1 when >players.
  3457. end sub
  3458.  
  3459.  
  3460. sub doGameOver()
  3461. setRelay gameOver,true
  3462. gameOverText.text = "GAME OVER"
  3463. If B2SOn Then
  3464. Controller.B2SSetGameOver 35,1
  3465. Controller.B2SSetBallInPlay 32, 0
  3466. End If
  3467. setPlayfieldLights false
  3468. tiltHasEffect=false
  3469. if tilt then untilt 'clears tilt sign
  3470. setIllumination true
  3471. setPlayfieldLights false
  3472. lightCenterBumper false
  3473. setShootAgain false 'so far a running game was canceled by creditButtonRoutine()
  3474. setBackboxLights false
  3475.  
  3476. setBumpers false
  3477. setFlippers false
  3478. releaseStuckScoreReels 'so far any are stuck
  3479. if digitMatchEnabled then setEndMatchLights(matchNumber)
  3480. If B2SOn Then Controller.B2SSetMatch 34,matchnumber
  3481. updateHighscore
  3482. saveGame 'saving highscore etc.
  3483. rulesSwitchable=true
  3484. end sub
  3485.  
  3486.  
  3487. ' The following routines simulate the way a mechanical Bally pinball machine
  3488. ' adds score amounts those multiple times advance digits using the score
  3489. ' motor. Note that these routines only add score when the camshaft is in its 0
  3490. ' position (i.e. e.g. when it is not turning).
  3491.  
  3492. sub addScore500()
  3493. if scoreMotorAngle=0 then setRelay add500,true
  3494. scoreMotor.enabled=true 'rest does the score motor
  3495. end sub
  3496.  
  3497. sub addScore3000()
  3498. if scoreMotorAngle=0 then setRelay add3000,true
  3499. scoreMotor.enabled=true 'rest does the score motor
  3500. end sub
  3501.  
  3502.  
  3503. 'the reel-a-listic(tm) ;-) score counter assembly:
  3504. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  3505. 'This code simulates a great looking,backlighted mechanical reel score counter
  3506. 'assembly;the reels can even get stuck and hum when a ball lies on a playfield
  3507. 'switch. When the counter flows over,the missing score digits don't get lost but
  3508. 'are displayed outside the reel counter using lamp-like looking extra digits(so
  3509. 'far extraDigitsEnabled is set true).The extra digits can display even multi-
  3510. 'digit overflow values - the digit count of them is only limited by the used
  3511. 'textBox width.
  3512.  
  3513. sub initializeScoreCounters()
  3514. ' This initializer must be called at program start before the score counter can
  3515. ' be used.
  3516.  
  3517.  
  3518. set scoreReelTop(1,0)=scoreTopP1D0
  3519. set scoreReelTop(1,1)=scoreTopP1D1
  3520. set scoreReelTop(1,2)=scoreTopP1D2
  3521. set scoreReelTop(1,3)=scoreTopP1D3
  3522. set scoreReelTop(1,4)=scoreTopP1D4
  3523.  
  3524. set scoreReelMain(1,0)=scoreMidP1D0
  3525. set scoreReelMain(1,1)=scoreMidP1D1
  3526. set scoreReelMain(1,2)=scoreMidP1D2
  3527. set scoreReelMain(1,3)=scoreMidP1D3
  3528. set scoreReelMain(1,4)=scoreMidP1D4
  3529. set scoreReelMain(1,5)=scoreP1Extra
  3530.  
  3531. set scoreReelLow(1,0)=scoreLowP1D0
  3532. set scoreReelLow(1,1)=scoreLowP1D1
  3533. set scoreReelLow(1,2)=scoreLowP1D2
  3534. set scoreReelLow(1,3)=scoreLowP1D3
  3535. set scoreReelLow(1,4)=scoreLowP1D4
  3536.  
  3537. set scoreReelBorder(1,0)=scoreClr0P1
  3538. set scoreReelBorder(1,1)=scoreClr1P1
  3539.  
  3540. set scoreReelTop(2,0)=scoreTopP2D0
  3541. set scoreReelTop(2,1)=scoreTopP2D1
  3542. set scoreReelTop(2,2)=scoreTopP2D2
  3543. set scoreReelTop(2,3)=scoreTopP2D3
  3544. set scoreReelTop(2,4)=scoreTopP2D4
  3545.  
  3546. set scoreReelMain(2,0)=scoreMidP2D0
  3547. set scoreReelMain(2,1)=scoreMidP2D1
  3548. set scoreReelMain(2,2)=scoreMidP2D2
  3549. set scoreReelMain(2,3)=scoreMidP2D3
  3550. set scoreReelMain(2,4)=scoreMidP2D4
  3551. set scoreReelMain(2,5)=scoreP2Extra
  3552.  
  3553. set scoreReelLow(2,0)=scoreLowP2D0
  3554. set scoreReelLow(2,1)=scoreLowP2D1
  3555. set scoreReelLow(2,2)=scoreLowP2D2
  3556. set scoreReelLow(2,3)=scoreLowP2D3
  3557. set scoreReelLow(2,4)=scoreLowP2D4
  3558.  
  3559. set scoreReelBorder(2,0)=scoreClr0P2
  3560. set scoreReelBorder(2,1)=scoreClr1P2
  3561.  
  3562. set scoreReelTop(3,0)=scoreTopP3D0
  3563. set scoreReelTop(3,1)=scoreTopP3D1
  3564. set scoreReelTop(3,2)=scoreTopP3D2
  3565. set scoreReelTop(3,3)=scoreTopP3D3
  3566. set scoreReelTop(3,4)=scoreTopP3D4
  3567.  
  3568. set scoreReelMain(3,0)=scoreMidP3D0
  3569. set scoreReelMain(3,1)=scoreMidP3D1
  3570. set scoreReelMain(3,2)=scoreMidP3D2
  3571. set scoreReelMain(3,3)=scoreMidP3D3
  3572. set scoreReelMain(3,4)=scoreMidP3D4
  3573. set scoreReelMain(3,5)=scoreP3Extra
  3574.  
  3575. set scoreReelLow(3,0)=scoreLowP3D0
  3576. set scoreReelLow(3,1)=scoreLowP3D1
  3577. set scoreReelLow(3,2)=scoreLowP3D2
  3578. set scoreReelLow(3,3)=scoreLowP3D3
  3579. set scoreReelLow(3,4)=scoreLowP3D4
  3580.  
  3581. set scoreReelBorder(3,0)=scoreClr0P3
  3582. set scoreReelBorder(3,1)=scoreClr1P3
  3583.  
  3584. set scoreReelTop(4,0)=scoreTopP4D0
  3585. set scoreReelTop(4,1)=scoreTopP4D1
  3586. set scoreReelTop(4,2)=scoreTopP4D2
  3587. set scoreReelTop(4,3)=scoreTopP4D3
  3588. set scoreReelTop(4,4)=scoreTopP4D4
  3589.  
  3590. set scoreReelMain(4,0)=scoreMidP4D0
  3591. set scoreReelMain(4,1)=scoreMidP4D1
  3592. set scoreReelMain(4,2)=scoreMidP4D2
  3593. set scoreReelMain(4,3)=scoreMidP4D3
  3594. set scoreReelMain(4,4)=scoreMidP4D4
  3595. set scoreReelMain(4,5)=scoreP4Extra
  3596.  
  3597. set scoreReelLow(4,0)=scoreLowP4D0
  3598. set scoreReelLow(4,1)=scoreLowP4D1
  3599. set scoreReelLow(4,2)=scoreLowP4D2
  3600. set scoreReelLow(4,3)=scoreLowP4D3
  3601. set scoreReelLow(4,4)=scoreLowP4D4
  3602.  
  3603. set scoreReelBorder(4,0)=scoreClr0P4
  3604. set scoreReelBorder(4,1)=scoreClr1P4
  3605.  
  3606. set scoreAddTimer(1)=scoreAddTimer1
  3607. set scoreAddTimer(2)=scoreAddTimer2
  3608. set scoreAddTimer(3)=scoreAddTimer3
  3609. set scoreAddTimer(4)=scoreAddTimer4
  3610.  
  3611. set scoreSpinTimer(1)=scoreSpinTimer1
  3612. set scoreSpinTimer(2)=scoreSpinTimer2
  3613. set scoreSpinTimer(3)=scoreSpinTimer3
  3614. set scoreSpinTimer(4)=scoreSpinTimer4
  3615.  
  3616. dim i,j
  3617. for i=0 to maxScoreReelNoises
  3618. scoreReelNoise(i)=false
  3619. next
  3620.  
  3621. scoreReelNoise(0)="resetClick10Relay"
  3622. scoreReelNoise(1)="resetClick9"
  3623. scoreReelNoise(2)="resetClick8"
  3624. scoreReelNoise(3)="resetClick7"
  3625. scoreReelNoise(4)="resetClick6"
  3626. scoreReelNoise(5)="resetClick5"
  3627. scoreReelNoise(6)="resetClick4"
  3628. scoreReelNoise(7)="resetClick3"
  3629. scoreReelNoise(8)="resetClick2"
  3630. scoreReelNoise(9)="resetClick1"
  3631.  
  3632. 'These samples shall simulate the click of n simultaneous score counter reels.
  3633. '
  3634. 'Note: These are just samples taken from the n-th counter click of a Bally
  3635. ' "Fireball" reset sequence.I don't know how many counters really turned
  3636. ' at each click.
  3637.  
  3638. for i=0 to reelsPerCounter
  3639. scoreReelChime(i)=false
  3640. next
  3641.  
  3642. scoreReelChime(1)="bell10"
  3643. scoreReelChime(2)="bell100"
  3644. scoreReelChime(3)="bell1000"
  3645.  
  3646. 'Each counter digit can have a chime sound that is played whenever the counter
  3647. 'reel of the digit advances. The chime sound can be disabled by setting
  3648. 'reelChimeEnabled=false. When only for certain digits chime sounds exist,the
  3649. 'remaining array indices of scoreReelChime() must be set false.
  3650. '
  3651. 'Note: This chime emulation needs a little extra CPU time and it may sound
  3652. ' less accurate than a simulation by a long sample of multiple clangs
  3653. ' that is directly started each time a ball hits a trigger. But as a
  3654. ' benefit the number of clangs with the emulation allways exactly matches
  3655. ' the earned score.
  3656.  
  3657. if chimeForExtraDigits then scoreReelChime(reelsPerCounter)="gong"
  3658. 'This sound is played when an extra digit advances by a reel counter overflow.
  3659.  
  3660. for i=minCounterNr to maxPlayers
  3661. scoreAddTimer(i).interval=reelCarryTime
  3662. reelTime(i)=reelSpinTime
  3663.  
  3664. for j=0 to reelsPerCounter 'the last element contains extra digits
  3665. scoreDigit (i,j)=0
  3666. stuckScoreReel(i,j)=0
  3667. next
  3668. next
  3669.  
  3670. scoreResetNumber=0
  3671. for i=0 to reelsPerCounter-1
  3672. scoreResetNumber=scoreResetNumber+(10^i)
  3673. 'Generates a number of reelsPerCounter times "1" (like 11111) which is used
  3674. 'in the counter reset emulation.
  3675. next
  3676.  
  3677. reelChimeEnabled=true
  3678. end sub
  3679.  
  3680.  
  3681. sub addScore(points)
  3682. ' This routine adds points to the score counter of the current player.
  3683. '
  3684. ' (This behaviour reflects quite well the hardware architecture of the
  3685. ' mechanical Bally pinball machine, which can add points also only to the
  3686. ' currently selected player.)
  3687. '
  3688. ' Important:
  3689. ' This routine can basically add any amount of points at any time. A real
  3690. ' mechanical pinball machine counter can only add 1 to any digit and though
  3691. ' can only add point values consisting of 1 and 0 (e.g. 100,1000,110 etc.),
  3692. ' though it needs the score motor to add other values. To emulate the behaviour
  3693. ' correct, therefore routines in the style of addScore500() and addScore3000()
  3694. ' (see there) should be used to get realistic timing. (E.g. while these are
  3695. ' counting,another hit of a target with the same value has no effect.)
  3696.  
  3697. setRelay newRound,false
  3698.  
  3699. 'This relay stays true until points are scored. Though as long no points
  3700. 'are scored in a round,the ball will be only re-ejected without collecting
  3701. 'bonus or changing any lights on the playfield. This is known as the
  3702. 'famous "fairplay interlock" feature.
  3703.  
  3704. if shootAgain and not extraBallWon then setShootAgain false
  3705.  
  3706. 'When we had an extra ball and the next round has begun,the extraBallWon relay
  3707. 'has been cleared,but the shootAgain relay (and though lamps) stay on until
  3708. 'points are scored. (When shootAgainStaysLit=false,it will have been cleared
  3709. 'already when the new round started.)
  3710.  
  3711. if not gameOver and not tilt and currentPlayer>0 then addScoreToPlayer currentPlayer,points
  3712.  
  3713. end sub
  3714.  
  3715. dim ascore
  3716. sub addScoreToPlayer(aPlayer,points)
  3717. ' This adds points to the score counter of a Player.
  3718. if lossyScoreCounter then unaddedScore(aPlayer)=0
  3719. 'A real mechanical score counter can't memorize multiple scores to
  3720. 'add,though when there is too little time left between score events,
  3721. 'the counter skips counting the rest of the previous score.
  3722. '
  3723. 'This is an approximation of this behaviour. (In reality it would
  3724. 'possibly affect only certain digits and digit values.)
  3725.  
  3726. unaddedScore(aPlayer)=unaddedScore(aPlayer)+points
  3727. If B2SOn Then Controller.B2ssetscore (aplayer), getscore(aplayer)+unaddedscore(aplayer)
  3728.  
  3729. scoreAddTimer(aPlayer).enabled=true
  3730.  
  3731. 'The digits of unaddedScore are counted down while the counter adds them
  3732. 'to the score.
  3733. end sub
  3734.  
  3735.  
  3736. sub addScoreDigitStuck(aDigit)
  3737. ' This advances a score digit reel by 1 and locks it until releaseScoreDigit()
  3738. ' for that digit will be called.
  3739. '
  3740. ' Call this procedure when a ball hits a rollover trigger that has a fixed score
  3741. ' value that advances a digit by 1 directly (without the score motor).Call
  3742. ' releaseScoreDigit() for the digit in the _unhit() procedure of the same target
  3743. ' to enable the digit again when the ball leaves the switch.
  3744. ' Important is that the currentPlayer must not be changed so long a digit is
  3745. ' stuck. After a different player is up, releaseStuckScoreReels() should be
  3746. ' called to free any stuck digits (setCurrentPlayer() does this).
  3747. '
  3748. ' (Internally a counting semaphore memorizes how often this procedure is called,
  3749. ' therefore to release the digit,releaseScoreDigit() must be called for it
  3750. ' the same number of times.)
  3751.  
  3752. setRelay newRound,false
  3753. if shootAgain and not extraBallWon then setShootAgain false
  3754.  
  3755. if not gameOver and not tilt and currentPlayer>0 then
  3756. incrementScoreDigit currentPlayer,aDigit,true
  3757. end if
  3758. end sub
  3759.  
  3760.  
  3761. sub releaseScoreDigit(aDigit)
  3762. ' This frees a score digit that was stuck with addScoreDigitStuck().
  3763. releaseScoreReel currentPlayer,aDigit
  3764. end sub
  3765.  
  3766.  
  3767. sub addScoreDigitStuckBySema(aDigit,aSema)
  3768. ' This does the above but additionally uses an external semaphore.
  3769. '
  3770. ' This routine should be used for rollover triggers those score value can
  3771. ' change(e.g. become 0) during the game,because releaseScoreDigit() must
  3772. ' only be called for a digit that was previously stuck by the same rollover
  3773. ' trigger to get realistic behaviour in multi-ball games. Each such rollover
  3774. ' trigger must use an own semaphore (global variable initialized with 0) per
  3775. ' involved digit to work correctly (i.e. use an array when the rollover can
  3776. ' stick different digits depending on its actual score value.)
  3777.  
  3778. if aSema<0 then aSema=0 'would be out of range
  3779. if aSema=0 then addScoreDigitStuck(aDigit)
  3780. aSema=aSema+1
  3781.  
  3782. 'We use here a counting semaphore,because Visual Pinball can(according to the
  3783. 'docs) call the _hit() procedure of a switch multiple times before calling
  3784. '_unhit() when multiple balls touch the same switch simultaneously.
  3785. end sub
  3786.  
  3787.  
  3788. sub releaseScoreDigitBySema(aDigit,aSema)
  3789. ' This does the above but additionally uses an external semaphore
  3790. ' (see addScoreDigitStuckBySema() ).
  3791.  
  3792. if aSema=1 then releaseScoreDigit(aDigit) 'only when sema switches from 1 to 0
  3793. if aSema>=1 then aSema=aSema-1
  3794. end sub
  3795.  
  3796.  
  3797. sub scoreAddTimer1_timer()
  3798. scoreAddTimerRoutine 1
  3799. end sub
  3800.  
  3801. sub scoreAddTimer2_timer()
  3802. scoreAddTimerRoutine 2
  3803. end sub
  3804.  
  3805. sub scoreAddTimer3_timer()
  3806. scoreAddTimerRoutine 3
  3807. end sub
  3808.  
  3809. sub scoreAddTimer4_timer()
  3810. scoreAddTimerRoutine 4
  3811. end sub
  3812.  
  3813. sub scoreAddTimerRoutine(aPlayer)
  3814. ' To add some points to the score counter of a player, the points first need
  3815. ' to be added to the variable unaddedScore(aPlayer) and then the timer
  3816. ' scoreAddTimer(aPlayer) for the player must be enabled to start counting, which
  3817. ' is done in addScore().
  3818. '
  3819. ' This reel counter simulation code is capable to run multiple score displays at
  3820. ' the same time, and they even have independent timers, which enables them to
  3821. ' rotate (and though click) out of phase to each other when started this way.
  3822. '
  3823. ' Notes:
  3824. ' To implement a mechanical highscore display (like in "Kewpie Doll"),the unused
  3825. ' player 0 postion in the arrays could be used.
  3826.  
  3827. ' This routine can not count backwards,because this would need a completely
  3828. ' different carry processing logics. But so far I know,no mechanical pinball
  3829. ' machine uses countdown counters, and also the normal Bally counters could
  3830. ' not do this. If anybody needs such counters, it would certainly be possible to
  3831. ' re-write this code with a backward stepping routine, but it would take likely
  3832. ' twice as much effort.
  3833.  
  3834. if unaddedScore(aPlayer)=0 then
  3835. scoreAddTimer(aPlayer).enabled=false
  3836. scoreAddTimer(aPlayer).interval=reelCarryTime 'makes counter start faster
  3837. scoreIsCounting(aPlayer)=false
  3838. else
  3839. scoreAddTimer(aPlayer).interval=reelSpinTime
  3840. scoreIsCounting(aPlayer)=true
  3841. end if
  3842.  
  3843. 'The scoreAddTimer is not delayed by the carry routine,but it always expects
  3844. 'that the carry routine will be faster. This theoretically might make trouble
  3845. 'on stupidly slow PCs or when the reelCarryTime would be set ridicoulously slow,
  3846. 'but it has the benefit that the counter clicks much more rhythmical this way
  3847. 'and especially the reel chimes sound way better because they don't get out of
  3848. 'sync by the varying reelSpinTime speed.
  3849.  
  3850. dim i,t
  3851. if extraDigitsEnabled then t=reelsPerCounter else t=reelsPerCounter-1
  3852.  
  3853. for i=0 to t
  3854. if getDigit(unaddedScore(aPlayer),i)>0 then
  3855. unaddedScore(aPlayer)=unaddedScore(aPlayer)-(10^i)
  3856. incrementScoreDigit aPlayer,i,false
  3857. end if
  3858. next
  3859.  
  3860. 'This decrements each non-zero digit of unaddedScore(aPlayer) by 1 while it adds the
  3861. '1 to the digit of the score counter.
  3862. end sub
  3863.  
  3864.  
  3865. sub incrementScoreDigit(aPlayer,digit,isStuck)
  3866. ' This increments a score counter reel(a digit) of the current player by 1 and
  3867. ' displays it halfway rotated to its next value. The digits are numbered from
  3868. ' right to left starting with 0.
  3869. '
  3870. ' The trick how this works was discovered by PeBo.It is based on the use of
  3871. ' 3 vertically overleaping text boxes per score display digit. To draw a
  3872. ' counter reel in a halfway rotated position,the old cipher is drawn in the
  3873. ' top text box while the coming cipher is drawn in the lower text box.(The
  3874. ' cipher in the middle text box is erased by overdrawing from the other
  3875. ' text boxes.) To get rid of the top half of the top cipher and the bottom
  3876. ' half of the bottom cipher,2 bar-shaped empty textboxes are drawn over them.
  3877. ' (Printing "" into a textbox displays nothing but overwrites the contents
  3878. ' of other text boxes with blanks at the overleaped area.) Then the
  3879. ' scoreSpinTimer is started,which after a fraction of a second clears the top
  3880. ' and the bottom text box and instead writes the new cipher into the middle
  3881. ' ("main") text box.
  3882. '
  3883. ' To make this counter work correctly,it is crucial that the drawing order of
  3884. ' the overleaping textboxes (changable with "draw in front","draw in back") in
  3885. ' the editor must be the same as intended in the game;otherwise the overdraw-
  3886. ' ing trick fails. The layers should be (from back to front):
  3887. '
  3888. ' 1.counter reel decale,frame decale and backlight (order is not that important)
  3889. ' 2.top and bottom cipher text boxes
  3890. ' 3.main cipher text boxes
  3891. ' 4.upper and lower clearing bar text boxes
  3892. '
  3893. ' Note: The way how the counter reels jerk into an intermediate position and
  3894. ' then further to the next cipher look not that unrealistic. In a real
  3895. ' mechanical pinball counter the reels also don't rotate smoothly,
  3896. ' because they are not turned by a motor,but instead a coil quickly
  3897. ' pulls each reel quickly into the intermediate position by a short
  3898. ' current pulse, and when the pulse ends, a spring makes the reel flip
  3899. ' further to its next stable position.
  3900. '
  3901. ' When the boolean isStuck is set true,the reel will stay in this position and
  3902. ' refuse any further counting until releaseScoreReel() for this digit will be
  3903. ' called to simulate a blocked solenoid like e.g. when a ball laying on a
  3904. ' playfield overroll switch. This is implemented with counting semaphores,
  3905. ' therefore when incrementScoreDigit(...,...,true) is called multiple times,
  3906. ' releaseScoreReel() must be called the same times to enable it again.The
  3907. ' purpose of this is to simulate multiple switches wired in series those e.g.
  3908. ' in a multi-ball game could be blocked by multiple balls simultaneously.
  3909. '
  3910. ' To simulate the counter backlight,all the mentioned textboxes are transparent
  3911. ' and behind them is rectangular lamp. Due to lights in visual pinball look
  3912. ' extremely dark when they are off, between the lamp and the textboxes there
  3913. ' is a white decal,of that every 2nd pixel is black;due to black was set to
  3914. ' its transparent colour, though the light of the lamp can shine trough it like
  3915. ' daylight through a white gaze curtain. (Also see lightCenterBumper().)
  3916. '
  3917. ' Due to a Visual Pinball bug the background of text boxes gets not updated when
  3918. ' a lamp behind it is switched on or off,therefore the routine
  3919. ' updateScoreDisplays() must be called every time these lamps are switched.
  3920.  
  3921. if isStuck then
  3922. stuckScoreReel(aPlayer,digit)=stuckScoreReel(aPlayer,digit)+1
  3923. if stuckScoreReel(aPlayer,digit)=1 then
  3924. if stuckReelsBuzz=0 then playSound "reelStuckBuzz",-1
  3925. 'starts endless loop of solenoid buzz
  3926. stuckReelsBuzz=stuckReelsBuzz+1
  3927. end if
  3928. end if
  3929.  
  3930. if not(stuckScoreReel(aPlayer,digit)=0 or isStuck and stuckScoreReel(aPlayer,digit)=1) then exit sub
  3931.  
  3932. 'This executes the rest of this procedure only when the stuckScoreReel()
  3933. 'semaphore of a digit is 0 or when it is set to 1 the first time.
  3934.  
  3935. if gameIsResetting and scoreDigit(aPlayer,digit)=0 then exit sub
  3936. 'During reset any reels rotate only until 0.
  3937.  
  3938. scoreIsCounting(aPlayer)=true
  3939.  
  3940. 'This flag signals that the counter is still running. As long it is true,
  3941. 'the game should not be switched to a different player.
  3942. '(A real mechanical pinball machine would count wrong when the player is
  3943. 'switched at the wrong moment. My algorithm may prevent this,but I am not
  3944. 'sure if it works reliable,nor would this be realistic.)
  3945.  
  3946. dim top
  3947. top=scoreDigit(aPlayer,digit)
  3948. scoreDigit(aPlayer,digit)=scoreDigit(aPlayer,digit)+1
  3949.  
  3950. if digit<reelsPerCounter then 'when not an extra digit,display it as rotated reel
  3951.  
  3952. if scoreDigit(aPlayer,digit)=10 then
  3953. scoreDigit(aPlayer,digit)=0
  3954. top=9
  3955. if not gameIsResetting then carry(aPlayer,digit)=1
  3956. 'sets a carry for the next digit
  3957. end if
  3958.  
  3959. scoreReelLow(aPlayer,digit).text=scoreDigit(aPlayer,digit) 'print bottom cipher
  3960. scoreReelBorder(aPlayer,1).text ="" 'clear its lower half
  3961. scoreReelTop(aPlayer,digit).text=top 'print top cipher
  3962. scoreReelBorder(aPlayer,0).text ="" 'clear its upper half
  3963.  
  3964. if isStuck then playSound "reelStuck2" else playReelClick
  3965. 'This is the mechanical counter click for a digit,that is played always
  3966. 'when the digit switches.
  3967. end if
  3968.  
  3969. if reelChimeEnabled and scoreReelChime(digit)<>false and aPlayer>0 then
  3970. 'aPlayer=0 can only be a highscore counter that shall remain mute.
  3971. if not(reelChimeForCarry=false and digit>0 and carry(aPlayer,max(digit-1,0))=1) or digit=reelsPerCounter then
  3972. 'so far this digit was not flipped by a carry and reelChimeForCarry=false
  3973. 'or when this is an extra digit.
  3974. if digit = 5 Then
  3975. playSoundMonophonic SoundFXDOF(scoreReelChime(digit),145,DOFPulse,DOFBell)
  3976. else
  3977. playSoundMonophonic SoundFXDOF(scoreReelChime(digit),140 + digit,DOFPulse,DOFChimes)
  3978. end if
  3979.  
  3980. 'This is an optional chime sound for each digit,that can be disabled e.g. when
  3981. 'the counter is in reset mode.
  3982. '
  3983. 'In a real mechanical pinball machine it depends on how the chimes are wired,
  3984. 'whether they sound also by carry. When they are directly connected with the
  3985. 'counter coils, they unavoidably will. When they are instead connected with a
  3986. 'score motor switch or relay that steps the counter forward, then they won't
  3987. 'notice carries and though sound only when the score reel advances by adding
  3988. 'points. The constant reelChimeForCarry makes both behaviours selectable.
  3989. '
  3990. 'The chime for extra digits(digit=reelsPerCounter) always reacts on carries,
  3991. 'because in a proper designed simulation they could anyway only be trigger by
  3992. 'the "carry" from an overflowing score counter,though otherwise it would
  3993. 'sound never.
  3994. end if
  3995. end if
  3996.  
  3997. scoreSpinTimer(aPlayer).interval=reelTime(aPlayer)
  3998. scoreSpinTimer(aPlayer).enabled=true 'the rest does spinScoreReels()
  3999. end sub
  4000.  
  4001.  
  4002. sub scoreSpinTimer1_timer()
  4003. spinScoreReels 1
  4004. end sub
  4005.  
  4006. sub scoreSpinTimer2_timer()
  4007. spinScoreReels 2
  4008. end sub
  4009.  
  4010. sub scoreSpinTimer3_timer()
  4011. spinScoreReels 3
  4012. end sub
  4013.  
  4014. sub scoreSpinTimer4_timer()
  4015. spinScoreReels 4
  4016. end sub
  4017.  
  4018. sub spinScoreReels(aPlayer)
  4019. ' This is called by the scoreSpinTimers completes rotation of the reels to their
  4020. ' stable position.This routine is also called by setScore() to update the
  4021. ' display.
  4022. '
  4023. ' Note: Actually all digits are flipped simultaneously when this counter gets
  4024. ' active. This in some cases can look unrealistic.Better would be the use
  4025. ' of an agenda (time job list) in that all reels write their intended
  4026. ' flip time to flip independently by using a timer that repeatedly
  4027. ' executes the next pending job. An agenda would help a lot also with other
  4028. ' problems of this emulation, but I don't know any easy way to do something
  4029. ' like this in BASIC.
  4030.  
  4031. ' gameOverText.text=getScore(aPlayer) 'TEST!!!
  4032.  
  4033. dim i
  4034. scoreSpinTimer(aPlayer).enabled=false
  4035. for i=0 to reelsPerCounter-1
  4036. if stuckScoreReel(aPlayer,i)=0 then scoreReelMain(aPlayer,i).text=scoreDigit(aPlayer,i)
  4037. 'print cipher in digit middle
  4038. next
  4039.  
  4040. if extraDigitsEnabled then
  4041. 'This displays the missing digits when the score counter overflows in a
  4042. '(sort of) "light ciphers" style:
  4043.  
  4044. if scoreDigit(aPlayer,reelsPerCounter)>0 then
  4045. scoreReelMain(aPlayer,reelsPerCounter).text=scoreDigit(aPlayer,reelsPerCounter)
  4046. else
  4047. scoreReelMain(aPlayer,reelsPerCounter).text=""
  4048. 'prevents a "0" when counter is long enough
  4049. end if
  4050. end if
  4051.  
  4052. carryForward aPlayer
  4053. end sub
  4054.  
  4055.  
  4056. sub carryForward(aPlayer)
  4057. ' This checks if a carry value must ripple upward to a higher digit.
  4058.  
  4059. ' I use here one carry array per counter. This may appear unneccessary,but when
  4060. ' aPlayer is switched at the wrong moment,this may be safer than sharing
  4061. ' a single array for all score counters.
  4062.  
  4063. dim i
  4064. for i=0 to reelsPerCounter-1
  4065. if carry(aPlayer,i)=1 then
  4066. reelTime(aPlayer)=reelCarryTime 'a digit moved by carry shall flip faster
  4067. incrementScoreDigit aPlayer,i+1,false
  4068. 'The highest array cell accumulates all missing higher digits.
  4069. carry(aPlayer,i)=0
  4070. end if
  4071. next
  4072.  
  4073. reelTime(aPlayer)=reelSpinTime
  4074.  
  4075. ' if unaddedScore(aPlayer)>0 then 'a stricter synchronization method (looks jerky)
  4076. ' scoreAddTimer(aPlayer).enabled=true
  4077. ' else
  4078. ' scoreIsCounting(aPlayer)=false
  4079. ' end if
  4080. end sub
  4081.  
  4082.  
  4083. sub releaseScoreReel(aPlayer,digit)
  4084. ' This releases a stuck scoring reel digit of a player
  4085. ' (see IncrementScoreDigit() ).
  4086.  
  4087. if stuckScoreReel(aPlayer,digit)=0 then exit sub
  4088.  
  4089. subtractUntil0 stuckScoreReel(aPlayer,digit),1
  4090.  
  4091. if stuckScoreReel(aPlayer,digit)=0 then
  4092. subtractUntil0 stuckReelsBuzz,1
  4093. if stuckReelsBuzz=0 then stopSound "reelStuckBuzz" 'stops solenoid buzzing
  4094.  
  4095. scoreReelMain(aPlayer,digit).text=scoreDigit(aPlayer,digit)
  4096. playSound "reelRelease"
  4097. end if
  4098. end sub
  4099.  
  4100.  
  4101. sub releaseStuckScoreReels()
  4102. ' This is a safety command that releases all stuck score reels for the case
  4103. ' that anything goes wrong with the counting semaphores.
  4104.  
  4105. dim i,j
  4106.  
  4107. for i=minCounterNr to maxPlayers
  4108. for j=0 to reelsPerCounter
  4109. if stuckScoreReel(i,j)<>0 then
  4110. stuckScoreReel(i,j)=1
  4111. releaseScoreReel i,j
  4112. end if
  4113. next
  4114. next
  4115.  
  4116. stuckReelsBuzz=0
  4117. end sub
  4118.  
  4119.  
  4120. sub updateScoreDisplays()
  4121. ' This must be called after switching the score backlight on or off to fix a
  4122. ' graphical bug in Visual Pinball (lighted areas behind textboxes otherwise
  4123. ' don't get updated).
  4124.  
  4125. dim i,j
  4126.  
  4127. for i=minCounterNr to maxPlayers
  4128. for j=0 to reelsPerCounter-1
  4129. scoreReelLow(i,j).text=scoreReelLow(i,j).text
  4130. scoreReelBorder(i,1).text=""
  4131. scoreReelTop(i,j).text=scoreReelTop(i,j).text
  4132. scoreReelBorder(i,0).text=""
  4133. scoreReelMain(i,j).text=scoreReelMain(i,j).text
  4134. next
  4135. if extraDigitsEnabled then scoreReelMain(i,reelsPerCounter).text=scoreReelMain(i,reelsPerCounter).text
  4136. next
  4137. end sub
  4138.  
  4139.  
  4140. sub setScore(aPlayer,aNumber)
  4141. ' This sets a score counter directly to a value (possibly useful for loading
  4142. ' a highscore into a counter).
  4143.  
  4144. dim i
  4145. for i=0 to reelsPerCounter-1
  4146. scoreDigit(aPlayer,i)=getDigit(aNumber,i)
  4147. carry(aPlayer,i)=0
  4148. next
  4149. scoreDigit(aPlayer,reelsPerCounter)=int(aNumber/(10^reelsPerCounter))
  4150.  
  4151. spinScoreReels aPlayer 'this updates the score display
  4152. end sub
  4153.  
  4154.  
  4155. function getScore(aPlayer)
  4156. ' This returns as an integer the actually displayed score value of a player,
  4157. ' including all missing higher digits.
  4158. '
  4159. ' Note: This routine does NOT care about the still unadded (buffered) score
  4160. ' the counter is about to add.Therefore it must be waited until
  4161. ' scoreIsCounting(aPlayer)=false to get the completed score value of a
  4162. ' player (e.g. for saving highscores etc.).
  4163.  
  4164. dim i,aScore
  4165.  
  4166. aScore=0
  4167. for i=0 to reelsPerCounter
  4168. aScore=aScore+(scoreDigit(aPlayer,i)*10^i)
  4169.  
  4170. next
  4171.  
  4172. getScore=aScore
  4173. end function
  4174.  
  4175.  
  4176. function getDigit(aNumber,n)
  4177. ' This returns the n-th digit from aNumber; aNumber can be a number string or
  4178. ' an integer number.
  4179.  
  4180. if len(aNumber)>n then
  4181. getDigit=mid(aNumber,len(aNumber)-n,1)
  4182. else
  4183. getDigit="0"
  4184. end if
  4185. 'return values look like a function name assignment in this silly BASIC.
  4186. end function
  4187.  
  4188.  
  4189. function isScoreResetted()
  4190. ' This returns true when all score reels of all score counters are 0.
  4191.  
  4192. dim i
  4193. for i=minCounterNr to maxPlayers
  4194. if getScore(i)<>0 then
  4195. isScoreResetted=false
  4196. exit function
  4197. end if
  4198. next
  4199. isScoreResetted=true
  4200. end function
  4201.  
  4202.  
  4203. sub debugScoreDisplayAlignment
  4204. ' This is a test routine that can be used to align the text boxes of the score
  4205. ' counters on the backdrop picture correctly.
  4206. ' (Don't fear, although the result looks nasty, these counter solenoids are
  4207. ' purely virtual and won't be toasted by this procedure. ;-) )
  4208. '
  4209. ' This routine is not intended to be used within a finished game program.
  4210.  
  4211. dim i,j
  4212.  
  4213. for i=1 to maxPlayers
  4214. unaddedScore(i)=0
  4215.  
  4216. for j=0 to reelsPerCounter-1
  4217. scoreDigit(i,j)=8
  4218. carry(i,j)=0
  4219. incrementScoreDigit i,j,true
  4220. 'this sticks all score reels between 8 and 9
  4221. next
  4222. next
  4223. end sub
  4224.  
  4225.  
  4226. 'sound effect routines:
  4227. '----------------------
  4228.  
  4229. sub playReelClick()
  4230. 'This is the mechanical counter click for a score reel,that is played always
  4231. 'when the digit switches.
  4232.  
  4233. reelClicks=reelClicks+1
  4234. clickTimer.enabled=true
  4235. end sub
  4236.  
  4237.  
  4238. sub clickTimer_timer
  4239.  
  4240. dim n
  4241.  
  4242. if reelClicks>0 then
  4243. n=reelClicks
  4244. if n>maxScoreReelNoises then n=maxScoreReelNoises
  4245.  
  4246. if n>3 then n=n-int(3*rnd()) 'a little random makes sound more vivid
  4247. subtractUntil0 reelClicks,n
  4248.  
  4249. playSound scoreReelNoise(n)
  4250.  
  4251. 'This plays now a sample of n simultaneous reel clicks.The remaining clicks
  4252. 'will be played the next times this timer re-starts.
  4253.  
  4254. clickTimer.interval=1+2*rnd()
  4255.  
  4256. 'This adds a little random to the timer delay to prevent phasing and avoid
  4257. 'overdrive distortion of the output during counter reset.
  4258. else
  4259. clickTimer.enabled=false
  4260. end if
  4261. end sub
  4262.  
  4263.  
  4264. sub playSoundMonophonic(aSoundString)
  4265. ' This plays a sound only on a single voice at a time by shutting down all old
  4266. ' still playing samples of the same name.This can dramatically reduce the
  4267. ' polyphony demand when a sound plays in a loop,and though saves a lot of CPU
  4268. ' time and also prevents overdriving the maximum volume.
  4269. '
  4270. ' Attention: This works only well with sounds that start immediately.When the
  4271. ' loop is too fast and there is a delay in the sample then the
  4272. ' main sound won't be audible at all.Don't use this routine when
  4273. ' the same sample is used for multiple playfield objects those shall
  4274. ' be capable to sound independantly,because their sound would cancel
  4275. ' out each other with this routine.A memory wasting workaround would
  4276. ' be to load multiple copies of the same samples under a different
  4277. ' name.
  4278. '
  4279. stopSound aSoundString
  4280. playSound aSoundString
  4281. end sub
  4282.  
  4283. 'highscore routines:
  4284. '-------------------
  4285.  
  4286. sub updateHighScore()
  4287. ' This calculates the new highscore from the scores of all players and displays it.
  4288.  
  4289. if not highScoreEnabled then exit sub
  4290.  
  4291. dim i,t,updated
  4292.  
  4293. updated=false
  4294.  
  4295. for i=1 to maxPlayers
  4296. if getScore(i)>highScore(ruleSet) then
  4297. highScore(ruleSet)=getScore(i)
  4298. updated=true
  4299. end if
  4300. next
  4301.  
  4302. if updated then t="new " else t=""
  4303. t=t+"high score:" & chr(13) & highScore(ruleSet)
  4304. highScoreText.text=t
  4305.  
  4306. if updated then
  4307. if highScoreSoundEnabled then playSound "didge-a-redo"
  4308. 'This sample I found on a very old "Romware" freeware CD-ROM under the
  4309. 'name SHIPHORN.WAV and immediately I thought whow - what a racket!
  4310. 'It's so incredible lo-fi and crackling that it sounds much rather like a
  4311. '(kind of) mechanically played didgerido,though I immediately knew where
  4312. 'to use it... All attempts to de-click it made it sound just boring,thus
  4313. 'I kept it in all its grainy beauty like it is.
  4314. end if
  4315. end sub
  4316.  
  4317.  
  4318. sub displayHighScore()
  4319. ' This displays the actual highscore. (used when a new game starts to remove
  4320. ' the "new" before the word "high score:",or when the game rules get
  4321. ' switched.)
  4322.  
  4323. highScoreText.text="high score:" & chr(13) & highScore(ruleSet)
  4324. end sub
  4325.  
  4326.  
  4327. ' This was copied from Mr.Do!,where it was copied from the brilliant Eight Ball
  4328. ' Deluxe but I(we?) believe credit and thanks should go to boris for the script.
  4329.  
  4330. Sub loadGameStats(dirFile, nameFile)
  4331. Dim fso, ts
  4332. Dim TestFolder, files, file, f
  4333. Dim temp, pos
  4334. Dim prm, value
  4335.  
  4336. Set fso = CreateObject("Scripting.FileSystemObject")
  4337. If Not fso.FolderExists(dirFile) Then Exit Sub
  4338. Set TestFolder = fso.GetFolder(dirFile)
  4339. Set files = TestFolder.Files
  4340.  
  4341. If (files.Count <> 0) Then
  4342. For Each file in files
  4343. If nameFile=file.Name Then
  4344. Set f = fso.GetFile(dirFile & nameFile)
  4345. Set ts = f.OpenAsTextStream(1,0)
  4346. Do While (ts.AtEndOfStream <> True)
  4347. temp = ts.ReadLine
  4348. pos = instr(1,temp,"=")
  4349. If (pos > 1) Then
  4350. prm = Left(temp,pos-1)
  4351. value = mid(temp,pos+1)
  4352. Select Case prm
  4353. case "highscore_authentic"
  4354. highScore(RS_authentic)=CDbl(value)
  4355. case "highscore_enhanced"
  4356. highScore(RS_enhanced) =CDbl(value)
  4357. case "currentRuleSet"
  4358. ruleSet=CDbl(value)
  4359. case "credits"
  4360. credits=CDbl(value)
  4361. case "matchNumber"
  4362. matchNumber=CDbl(value)
  4363. case "spinnerState"
  4364. spins=CDbl(value)
  4365. case "score1"
  4366. setScore 1,CDbl(value)
  4367. case "score2"
  4368. setScore 2,CDbl(value)
  4369. case "score3"
  4370. setScore 3,CDbl(value)
  4371. case "score4"
  4372. setScore 4,CDbl(value)
  4373. End Select
  4374. If B2SOn Then Controller.B2SSetcredits Credits
  4375.  
  4376. End If
  4377. Loop
  4378. ts.close
  4379. Set f = Nothing
  4380. End If
  4381. Next
  4382. End If
  4383. Set fso = Nothing
  4384. End Sub
  4385.  
  4386.  
  4387. Sub saveGameStats(fileName)
  4388. Dim fso, f
  4389.  
  4390. Set fso = CreateObject("Scripting.FileSystemObject")
  4391. If Not fso.FolderExists(UserDirectory) Then Exit Sub
  4392. Set f = fso.CreateTextFile(fileName, True)
  4393. f.writeLine "highscore_authentic=" & highScore(RS_authentic)
  4394. f.writeLine "highscore_enhanced=" & highScore(RS_enhanced)
  4395. f.writeLine "currentRuleSet=" & ruleSet
  4396. f.writeLine "credits=" & credits
  4397. f.writeLine "matchNumber=" & matchNumber
  4398. f.writeLine "spinnerState=" & spins
  4399. f.writeLine "score1=" & getScore(1)
  4400. f.writeLine "score2=" & getScore(2)
  4401. f.writeLine "score3=" & getScore(3)
  4402. f.writeLine "score4=" & getScore(4)
  4403. f.close
  4404. Set f = Nothing
  4405. Set fso = Nothing
  4406. End Sub
  4407.  
  4408.  
  4409. sub saveGame()
  4410. ' This saves the game status so far the highScoreFileName variable is set.
  4411. if highScoreFileName<>false then saveGameStats UserDirectory & "\" & highScoreFileName
  4412. end sub
  4413.  
  4414.  
  4415. 'general arithmetics:
  4416. '--------------------
  4417. function max(value1,value2)
  4418. ' returns the bigger of 2 values
  4419. if value2>value1 then max=value2 else max=value1
  4420. end function
  4421.  
  4422. function min(value1,value2)
  4423. ' returns the smaller of 2 values
  4424. if value2<value1 then min=value2 else min=value1
  4425. end function
  4426.  
  4427.  
  4428. sub subtractUntil0(aVariable,aNumber)
  4429. ' This subtracts aNumber from aVariable so far the result is >0;else aVariable
  4430. ' is set 0 without getting negative in between.
  4431. ' (This is intended to prevents race conditions with counting semaphores and
  4432. ' other state variables those can be simultaneously set and decremented by
  4433. ' different tasks.)
  4434.  
  4435. if aVariable>aNumber then aVariable=aVariable-aNumber else aVariable=0
  4436. end sub
  4437.  
  4438. 'instruction messages:
  4439.  
  4440. sub showIntro_timer()
  4441. ' This displays the intro text requester.
  4442. showIntro.enabled=false
  4443.  
  4444. dim t
  4445.  
  4446.  
  4447. end sub
  4448.  
  4449.  
  4450. sub showRuleSheet()
  4451. ' This displays a brief summary of the game rules.
  4452.  
  4453. dim t
  4454.  
  4455. t= "RULE SHEET: "
  4456. select case ruleSet
  4457. case RS_authentic: t=t+"(authentic Bally)"
  4458. case RS_enhanced : t=t+"(enhanced mode - more challenging)"
  4459. end select
  4460. t=t+chr(13)&chr(13)
  4461. if ruleSet=RS_enhanced then t=t&"Tophole lights center bumper and upper 1k rollovers."& chr(13)
  4462. t=t&"A,B,C,D targets light A,B,C,D rollovers in captive ball lanes."& chr(13)
  4463. t=t&"Hit A+B+C+D targets to win double bonus."& chr(13)
  4464. t=t&"Left & right inlanes, spinner hits and lit "
  4465. if ruleSet=RS_enhanced then t=t&"A,B,C,D "
  4466. t=t&"rollovers advance bonus."& chr(13)
  4467. t=t&"Maximum bonus="& maxBonus &".High bonusses light outlane extra ball & special."& chr(13)
  4468.  
  4469. select case ruleSet
  4470. case RS_authentic
  4471. if specialLitValue<>false then
  4472. if specialShootAgain0 then t=t&"Special wins extra ball."& chr(13)
  4473. if specialWinsCredit0 then t=t&"Special wins 1 free credit."& chr(13)
  4474. if specialWinsPoints0>0 then t=t&"Special scores "&specialWinsPoints0&" points."&chr(13)
  4475. end if
  4476.  
  4477. case RS_enhanced
  4478. if topHoleShootAgain<>false then
  4479. t=t&"At bonus "& topHoleShootAgain &" tophole wins extra ball."& chr(13)
  4480. end if
  4481. if specialLitValue<>false then
  4482. if specialShootAgain1 then t=t&"Special wins extra ball."& chr(13)
  4483. if specialWinsCredit1 then t=t&"Special wins 1 free credit."& chr(13)
  4484. if specialWinsPoints1>0 then t=t&"Special scores "&specialWinsPoints1&" points."&chr(13)
  4485. end if
  4486. end select
  4487.  
  4488. t=t&"A tilt does not disqualify a player."& chr(13)
  4489. t=t&chr(13)
  4490. t=t&"Push credit button to start game or add 2nd,3rd or 4th player."& chr(13)
  4491. t=t&chr(13)
  4492. t=t&"Insert "
  4493. if coinChute1Credits<>false then
  4494. t=t&"small washer for "& coinChute1Credits &" credit"
  4495. if coinChute1Credits>1 then t=t&"s" else t=t&" (starts game)"
  4496. t=t&chr(13)
  4497. end if
  4498. if coinChute2Credits<>false then
  4499. t=t&"mid size washer for "& coinChute2Credits &" credits"& chr(13)
  4500. end if
  4501. if coinChute3Credits<>false then
  4502. t=t&"large washer for "& coinChute3Credits &" credits"& chr(13)
  4503. end if
  4504. t=t&chr(13)
  4505. t=t&"Washers® - the arcade operator's least beloved foreign currency..."
  4506. msgBox t
  4507. end sub
  4508.  
  4509.  
  4510. sub showKeySettings()
  4511. ' This simply fills a static text box with instruction text.
  4512.  
  4513. dim t
  4514.  
  4515. t= "M = Select Game Mode"& chr(13)
  4516. t=t&"5,6,7 = Insert Washer"& chr(13)
  4517. t=t&"S = Credit Button"& chr(13)
  4518. t=t&"R for Rule Sheet"& chr(13)
  4519.  
  4520. keysBox.text=t
  4521. end sub
  4522.  
  4523. Sub LeftFlipper_Collide(parm)
  4524. PlaySound "rubber"
  4525. End Sub
  4526.  
  4527. Sub RightFlipper_Collide(parm)
  4528. PlaySound "rubber"
  4529. End Sub
  4530.  
  4531. Sub Rubbers_Hit(IDX)
  4532. PlaySound "rubber"
  4533. End Sub
  4534.  
  4535. Sub theTable_Exit
  4536. If B2SOn Then Controller.Stop
  4537. End Sub
Add Comment
Please, Sign In to add comment