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