freezy / VisualPinball.Engine

:video_game: Visual Pinball Engine for Unity
https://docs.visualpinball.org
GNU General Public License v3.0
396 stars 62 forks source link

Properly simulate score reels #421

Closed freezy closed 1 year ago

freezy commented 2 years ago

We already have a score reel component, but it's very simplistic. In fact, a lot more is going on in a score reel, and the goal of this issue is to specify it and find a solution to implement it in VPE, as well as expose it through Visual Scripting.

Single Point vs Multi Point

We call single point scoring when a reel only advances by one position. It doesn't matter which reel, so a score changing from 0 to 100 or from 0 to 1000 is both called single point scoring. If multiple reels change one position at the same time (e.g. from 0900 to 1000), we also call single point scoring.

Direct Switch vs Motor-Driven Switches

Moving a reel to the next position is done through a coil. For single scoring elements on the playfield, a switch triggers the appropriate coil directly. For multi scoring, a motor activates the switches.

Please check this excellent website to understand how score motors work.

Blocking

While the motor is running, it blocks additional points during that time. For example, if the score goes from 0 to 500, and while the motor is moving the hundreds from 0 to 5, another 100 is scored, that 100 is discarded. If however a single point is scored on another reel (for example, a 10), it might be accepted (but a 50 wouldn't be accepted since the motor is busy moving the hundreds from 0 to 500).

Resetting

Resetting starts the score motor, pulses the reels until 0 is reached on all, then turns off the score motor. For example, a score of 2041 would cause the following sequence: 3052, 4063, 5074, 6085, 7096, 8007, 9008, 9, 0.

No more than two score banks can be reset a time. On a 4 player game, one and two will reset, followed by three and four.

Side effects

The motor status is globally of interest, since some tables like Volley turn off the bumper and insert lights while the motor is running.

VPE Approach

We want to provide a simple way for creators to do score-reel scoring, but we also want more advanced creators give the possibility to accurately simulate a score reel with a score motor.

The simple way is just adding a number to the score and sending the score to the display. The display (in our case, the score reels, but the same would apply to a segment display as well) would thus receive an integer value and doe what it needs to do autonomously.

For the accurate way, we'll provide a Score Motor Component. This component comes with a (pulse) coil input to turn the motor on, and a configurable number of switch outputs. Inside the component, one can configure how the score motor is set up, i.e. how many cams it features, and the pulse patterns for each switch. (It might be easier to assign a pattern to each switch, instead of cams and the position of the switches on each cam.)

Then, our ScoreReelComponent would become a ICoilDeviceComponent that takes in a coil input which moves the reel to the next position (it might be better to make ScoreReelDisplayComponent a ICoilDeviceComponent, since that would allow easier tracking of the score).

So for advanced mode, one would add the new ScoreMotorComponent, link its switch outputs to the coil inputs of the ScoreREelDisplayComponent through the wire manager, and use the reel- and motor switches in the GLE to update the score, instead of an integer.

Scottacus64 commented 2 years ago

Looks great! A couple of changes/suggestions:

Under blocking, some manufacturers block all scoring while the score motor is running. For example if 50 points were being scored on a Gottlieb Volley, any scores of 10 or 100 would be blocked. On a Bally Capersville if 50 points were being scored a score of 1 or 100 would also be registered by the machine as in your description above. It's a Gottlieb vs Bally thing.

Under Resetting, it could be stated that the reset occurs in one or two groups of five pulses. For 0555 it would take one set of five pulses to get to 0000 but 6983 would take two sets of five to get to 0000.

freezy commented 2 years ago

Yes, so additionally to the motor coil input, we the component should get a coil input for each reel so it can be blocked if necessary.

Scottacus64 commented 2 years ago

Could there be some way to block the score via some sort of check box in the score block that can be checked if the score should be blocked by the score motor? It's been a while so I don't remember how the vpe coding system works.

freezy commented 2 years ago

Yes that would be the other way. Add a "blocking" coil input to the score reel display component and a "motor running" switch to the motor component and hook them together.

Whatever works for you best is fine.

Scottacus64 commented 2 years ago

I started working with jsm174 on the score motor component and re-read through this document. I think that the score motor component of driving the score reels is more complex than it needs to be.

There are all sorts of limitations that an actual EM has that we don't need to burden ourselves with. For example an EM never really knows what the score is, it only "knows" if switches on a score reel are closed or open. We have the luxury of knowing the actual score value and being able to manipulate that data to achieve an accurate representation of an EM machine.

If you play one of my vpx EM machines and watch how the B2S score reels are returned to zero at restart, that is nothing more than a timer pulsing a "score motor sub" that parses out the individual digits of the score, makes a new score value by incrementing each digit by one and then sends an updated score to B2S to drive the reels. You've got this already built into the score reel display component. All that would need to be added is a way to time the steps of driving these reel displays.

Screenshot 2022-08-11 170541

A score motor might look complex but all it really is this: a series of five pulses and complementary blocking periods for these five pulses. If you look at this diagram you'll see that 1D completely overlaps the first pulse of 1A, 2C the second, 4B the third and 4C the fifth. What this means is that if you want to score 30 points on this machine then block the first and second of 1A's pulses and let the last three go to the 10's reel unit to step the appropriate players score. You do this by wiring 1A in series though normally closed switches on 1D and 2C. When 1D opens between 15-30 degrees it prevents 1A's pulse from going any further in the circuit and the same for 2C. For completeness sake, 2B breaks all of the machine's hold switches and the others are all switch stacks that change state once the score motor runs.

20220811_155020

I think that we don't need to burden VPE with this sort of complexity. We can let the score motor component handle the pulsing of a node that adds a set point total to the player's score and then let the score reel display handle the rest. This way both simple and complex approaches would be unified into one score motor block. If you want to score 50 then check all of the boxes. If you want to score 30 on the first, third and fifth pulses then check those as in my diagram. Whatever you do just check the correct number of boxes (ie 2 for 20/200/2000...). The component could also have a "Block all scores" box to let VPE know to block all scoring while the score motor is running or not.

At any rate I hope that my rambling helps carify what goes on in a real EM and what to my thinking is the best way to simulate that. Thanks again for letting me sit in on VPE's development!

jsm174 commented 2 years ago

I started working on a ScoreMotorComponent here. It is very similar to the PinMameMechComponent.

The ScoreMotorComponent exposes switches that can be enabled for a range of degrees or a repeating range of degrees.

Screen Shot 2022-08-14 at 10 59 00 AM

Currently the ScoreMotorComponent exposes a Start Coil and Motor Running Switch as requested in issue.

Screen Shot 2022-08-14 at 11 07 57 AM Screen Shot 2022-08-14 at 11 08 41 AM

So now we need to figure out how to link this component with the score reels given the previous comments.

jsm174 commented 2 years ago

So one thought is to add a reset coil and a zero switch to ScoreReelDisplayComponent. Pulsing the reset coil would advance all the non-zero reels one position. When the score motor completes, we can check the zero switch. If the zero switch is enabled, we are done, otherwise start the score motor again:

score_motor_reset_idea

This appears to work. Some initial thoughts are:

Scottacus64 commented 2 years ago

Are you guys sure that you want to make this system of score reels completely divorced from the player's score? There are a few aspects to an EM like replays, add-a-balls and matching that are dependent some non-trivial circuitry in an EM but are dead easy to do with just knowing the player's score.

freezy commented 2 years ago

So the one thing I missed when writing the issue was that there's a circuit behind the motor switches that contains logic that then triggers the reel coils. So how about we just define the timing how the reels move. Something like that:

This assumes that every reel has the same pattern.

The output of this component are a switch for each reel that can be wired to the reel component (the actual display).

Additionally, this component exposes switches for:

The input is an integer - the score.

Would that cover our needs?

freezy commented 2 years ago

We could also integrate this as an optional config into the reel component directly, then you don't need to explicitly wire the outputs to the reel's inputs.

freezy commented 2 years ago

Alternative UI that is less error-prone. Pattern can be dragged to sort.

jsm174 commented 2 years ago

I've updated the feature/score-motor branch, to generate the interface as described above:

Screen Shot 2022-08-21 at 2 33 04 PM

Since a score reel display is updated by passing in the complete score, I'm not sure how to proceed.

Screen Shot 2022-08-21 at 2 39 32 PM

Ie, to figure out the "increase by", we need to look at score, potential new score, if the score motor is running, etc.

freezy commented 2 years ago

I would say:

  1. For each reel, figure out how many steps it needs to go.
  2. If all are single-scoring or none, do it and exit
  3. Otherwise, apply the pattern defined above

Now, the question is how do we handle scoring while the motor is running. I'm not even sure how new scoring is handled when the motor is not blocking it. Maybe @Scottacus64 can answer that.

In general I'd be in favor of having a simple was of implementing blocking, i.e. a checkbox that says "block new scores when active", and that would ignore incoming scores, and at the end the new scores is emitted through an event, so the GLE can retrieve it.

But I would also implement a "motor start" and "motor end" event, so the GLE can handle the blocking itself.