VPX Scripting — Part 1 (Teacher’s Pet)

Sample schematic from an EM pinball table.
For EM pinball tables, the game state and logic are encoded in the wiring, relay states, cams, switches and electric motors. In VPX this logic is implemented in the script of the table.

13 Apr 2024

Background

I hope you are already familiar with Virtual Pinball, version 10 (or version X — VPX, for short). I talked about VPX earlier on my blog, but if you’re here I hope you have more than a passing familiarity with it by now and are curious as to how the script part of a VPX table works — because that’s what I’m going to discuss. In later installments I can talk a little about artwork and the graphical aspect of a VPX table, but we’re going to start with the script. And we’re going to start with the script of a VPX table that has already been created.

I’ll say up front that I am new to VPX and new to scripting VPX pinball tables. I’m also new to Visual Basic — the language the tables appear to be scripted in. So when I discuss VPX scripting and Visual Basic it will be as I understand them. I don’t want to come across as authoritative — I am describing what I found and believe to be true, how I got things to work for me.

I have been programming both as a hobby and professionally for over four decades though so perhaps that kind of background will at least allow me to suggest some good coding practices that may not be specific to VPX or Visual Basic but more to programming in general.

Loserman76

But to again temper your expectations, I should also add that I have only really delved into the VPX tables by a user who went by the name Loserman76. I refer to him in the past tense because he passed away in 2021.

Loserman76 avatar.

Nonetheless, Loserman76 (his real name was Jeff Whitehead) leaves behind quite a legacy. He authored over 200 VPX tables. His focus was on the older, electro-mechanical (EM) tables, so if you’ve ever downloaded or played a VPX table from the 1970’s or earlier there is a a decent chance it was one of his.

If you read my previous post about Visual Pinball then you know I got into it for the virtual cabinet experience. I wanted the big horizontal display for the playfield, the ability to shove the cabinet to nudge the virtual pinball, and what VPX calls “surround-sound feedback” (SSF) that gives you a very visceral feel of the ball being hit by the flippers, bumpers, kick-out holes.

I found though that Loserman76’s table were fairly tame in the haptic feedback department. Instead of a whack, the flippers on his tables just made the gentle buzz of a serial solenoid. Otherwise, I totally enjoyed his tables but with my cabinet strapped with multiple transducers I had really come to be spoiled by tables with more dynamic audio.

At some point it occurred to me to look into the script of a pinball table whose sounds I liked and compare what I found there with what I found in one of Loserman76’s tables. What followed then was a bit of surgery — remove some code from here, paste it into there. From these early experiments I came to know a little of my way around VPX scripting and wish to share that with you here.

I wrestled a bit with who my target audience should be. I decided, as you will find, that I should not assume that you have a programming background, but that you know your way around a computer and the usual tools for editing text, etc. and that of course you are familiar with pinball in general and VPX specifically. I will try to explain things to those new to programming but I’ll also try not to dwell on every issue.

I think what I really hope to accomplish is to remove any anxiety you might have about getting into a VPX script and changing things, learning. If you take it slowly, you’ll likely be surprised at how much you know and understand a few months from now.

Teacher’s Pet

For this series I am going to use the VPX table, Teacher’s Pet that Loserman76 created years ago. If you want to follow along, you can download the table as well. If you just want to follow along the script without having to grab the table first, I put Loserman76’s original script to Teacher’s Pet on Github.

Teacher’s Pet VPX table seen in desktop-mode.
Loserman76’s VPX version of Williams 1965 Teacher’s Pet.

Teacher’s Pet was a pinball table released by the Williams company in 1965. This puts it clearly in the electro-mechanical (EM) era for pinball and therefore an obvious table for Loserman76 to recreate (and an obvious draw for me as well). This must be a popular table with the EM community because, at the time I checked on the Pinside web site’s EM 100 list, it was voted to be among the top 10 of EM tables.

If you do choose to pull down the table and follow along, play the table a few times and pay particular attention to the sounds as that is where we are going to begin our exploration of VPX table scripting.

VPX Editor

If you’ve launched VPX to play a pinball table you’ve seen the kind of editor-view. It looks like a pinball construction set with a palette of tools that appear to allow you to add bumpers, walls — things like that.

Teacher’s Pet open in VPX.
Teacher’s Pet open in the VPX editor. Looks like fun, right?

We’ll poke around in the graphical editor perhaps in a future post, but for now we can ignore the above and focus instead on the little scroll-looking button in the Toolbar palette in the upper left.

The Script button in VPX.
The Script button.

Clicking on this brings up the script of the table.

The script open for Teacher’s Pet.
The script window for Teacher’s Pet.

There are over 3500 lines of code in the script to Teacher’s Pet, but don’t let that freak you out in the slightest. The code is organized in sections, most of which we can completely ignore. I’ll begin then in mapping out the sections so you can begin to feel a little more comfortable moving around in the script file.

Something I am likely to repeat a few times, Loserman76’s tables are very similar to each other when you look at the script. There is a boilerplate “Loserman76 table”. For that reason, I can describe the layout and structure of a Loserman76 script and you will find it applies more or less to all of his tables.

Visual Studio Code

The built in script-editor in VPX is functional: has syntax highlighting, etc. And with a single mouse click you can of course launch VPX and see your script changes in the pinball table play out immediately. But for making significant changes to the script there is no reason for you to feel you have to remain in VPX. The script is after all just text, so you can Select All, then Copy and finally Paste into a new document using whatever code editor you prefer.

I’ll be using Visual Studio Code (or just VSCode for short). I like it because it is cross-platform (I happen to use Macs for everything except playing VPX, ha ha), it is free, and it is a decent code editor.

The script open in Visual Studio Code.
Using Visual Studio Code to edit the script for Teacher’s Pet.

Above is how the script for Teacher’s Pet looks in VSCode. When I was in Windows I had used Notepad and pasted the script from Teacher’s Pet into a new .txt document. By default you don’t get the syntax highlighting that you see above in VSCode. You have to tell the app what kind of file this is. You can see in the lower right corner that it is treating the script as a Plain Text file. Click on the Plain Text label.

VSC treating the script as Plain Text.

Near the top of the document you will be presented with a search field — I typed in vb, short for Visual Basic.

Entering VB to tell Visual Studio Code to treat the file as a Visual Basic file.
Typing vb is the quickest way to winnow the results down to the one we want: Visual Basic.

We now get syntax highlighting similar to what you will see in VPX’s editor: comments are in green, strings are in red, etc. — the specific colors are not the same between VPX and VSCode, but the idea is the same.

If you change the file extension of your text file from .txt to .bas (for Basic) then VSCode will default to Visual Basic syntax highlighting. But this change causes complications in Windows so I have avoided it.

Begin Script Overview

Comments at the top of the script.

If you’re completely new to all of this, we’ll start simple. The top 6 lines of the script are displayed in green (in both VPX and VSCode). These are called comments and are completely ignored by the interpreter — by VPX itself. Every programming language I have used (and HTML for that matter) also has a way to add comments that are meant only for humans. In this example, the comments serve to inform you, dear reader, the name of the table, who created it, and who helped with the artwork.

In Visual Basic (and by extension VPX scripts) any text following an apostrophe character (the single quote character) is treated as part of a comment and ignored by VPX. But comments need not merely serve to give credit to the author of the pinball table, they can be used anywhere in the script — for example, to explain how part of a particular subroutine works.

In fact it is generally considered good programming practice to document your code but you won’t find a lot of comments here. (I suppose it is like flossing, we all mean to do it with more regularity.)

Someone experienced with VPX scripts probably won’t need a lot of the hand-holding they would get with well commented code. And there are ways that a good programmer can sort of “document” code fairly well without resorting to comments.

Globals follow at the top of the script.
The next 40 lines of the Teacher’s Pet script.

Starting on Line 17 in Teacher’s Pet and continuing to Line 164 are loads of Consts and Dims. These are the global constants (Const) and variables (Dim) that may be accessed anywhere in the code that follows.

The code preceding Line 17 is pretty much standard boilerplate code you see at the top of all VPX tables. You can ignore it.

The names of a few of the constants like ShadowFlippersOn is an example of what I call “self documenting” because there is no comment really needed here since the constant name explains what it does. Thoughtful table creators, like Loserman76, were encouraging players to tweak the table by the self-explanatory variable name and also by putting it so conveniently at the top of the file. Just change True to False to turn off the flipper shadows.

Subroutines

We are all the way down to Line 166 before we see our first function or subroutine. It is the subroutine Table1_init().

Table_init(), the first subroutine.

As the name implies this subroutine is called automatically for us by VPX when our pinball table is first loaded. All objects in VPX have a name and, by convention, or at least in Loserman76’s tables, Table1 is usually the name of the object that represents the entire pinball table. It’s _init subroutine is only called once but is called first. Most of what you will see in this subroutine concerns initializing the state of the table, initializing the variables we saw at the top of the file.

I’m trying to keep this high level, trying not to go too far into the weeds. So I will not dive into everything that is happening in every subroutine in the table (I don’t even know myself having not taken the time to follow every thread). But I do want you to get a flavor for what the script kind of looks like so it will look less intimidating.

Lets scrub down to the next subroutine, Table1_exit().

Table_init(), the first subroutine.

At this point you can see a pattern. This subroutine is called for us by VPX when the table (Table1) is about to exit. Even the names of the subroutines that are being called within suggest we are leaving: savehs (a subroutine that saves the high scores) … save this, stop that…

The subroutine above is small enough that you can see now that all subroutines begin with they keyword Sub and end with End Sub. It’s nice when the programmer indents the contents between the Sub and End Sub but the interpreter in VPX doesn’t really care.

Events

So far we have seen a pair of subroutines that are called for you automatically by VPX when a table is beginning and when a table is about to go away. Although I didn’t step through to confirm this for you we have also seen signs of other subroutines in the script that are not directly called by VPX but which instead are called internally.

The earlier example of savehs() is such a subroutine. We saw where it was called from Table1_exit(). If you search the script you will find where the subroutine is implemented. It does not have the rigid naming convention like ObjectName_init() that we saw earlier. In the case of savehs(), the person that wrote that subroutine just came up with the name (had it been me I would have made the name longer, more descriptive).

There are however standard subroutines that a VPX script needs to implement so that the table can work properly. The “key event” subroutines are just such an example. The first one appears beginning on Line 322.

Table1_KeyDown() subroutine.

The “key” in this case is either a flipper button, the start button, etc. “Down” means the user just pressed the key/button down (as opposed to “up”, having released the key — that comes later). Which key/button was pressed? It will be the value of keycode (you can ignore the ByVal for now). VPX has defined constants for all the standard keys/buttons. We’ll look at one next.

Line 338, if keycode = PlungerKey contains one of the keycode constants. As you probably figured out, everything between If and End If will be executed when the user presses the plunger key.

In the next installment when we look at pinball sounds we will revisit this function since plungers and flippers make noise. But I think for now we’ll scroll on to Line 483.

Table1_KeyUp() subroutine.

The code above follows Table1_KeyDown and demonstrates there is a symmetry: each key down event probably has a corresponding key up event indicating that the player released a key/button (and that is when this function is called). That’s how you would know when a flipper was released for example (and can stop the “buzz” sound from playing).

Hits

Drain_Hit() subroutine.

Just as there is a naming convention in VPX for subroutines to “init” and “exit” an object, there are also subroutines that have the pattern ObjectName_Hit() that are called when the object is hit by the pinball.

The “drain” in pinball is the name for the area on the playfield where you lose your ball … often when it slips between your flippers. This table has an object named Drain that serves this purpose and when the player loses their ball to the drain, Drain_Hit() is called.

The “hit” subroutines stand in contrast to the “event” ones we saw earlier that were user- initiated as well as the “init/exit” subroutines that were state driven. These are called as a result of the ball careening around the pinball table hitting things. These make playfield noises as well so we’ll also look more closely at these routines in a future post.

What follows in the Teacher’s Pet script are a lot of ObjectName_Hit() subroutines. There are hit subroutines for bumpers, targets, triggers… There are some interesting subroutines with _timer in their name too but those will have to wait for a future post.

There are also a scattering of somewhat esoteric subroutines that deal specifically with the logic/gameplay of this table. CheckTeacher on Line 883 is an obvious one. If I had organized the subroutines in this table I might have put these subroutines somewhere else, kept all the _hit subroutines together by themselves.

Eventually by Line 1779 you get to the savehs subroutine we saw a reference to earlier. It is followed by loadhs. These are the kind of save/restore routines we expect to find (VPX doesn’t automatically save your high score for you). So long as the high scores seem to work, there is no need to make any changes to these routines.

Scrolling on, more housekeeping routines follow.

Sound Routines

Finally we get to the section of the script that I’ll have a lot more to say about in a coming installment, the core sound playback routines.

Positional sound code begins.
A comment banner in the VPX script makes finding this section of code easier when scrolling.

My only reason for calling these functions out in this post is to let you know where they live. Surprisingly too, although I was a bit hand-wavy, we are already 2700 lines into the script. Lets scroll on though to the next section of the script.

Object Sounds routines.

What follows Line 2920 in the script for Teacher’s Pet are the “object sounds”. They have the XXX_Hit routine name that we have seen before but in this case the “XXX” is not an object but something called a “collection”. Without getting into what a Collection is, we can nonetheless see that each of these routines cause a sound to be played. Also the names of the subroutines suggest a material like metal, plastic or a common playfield object like a gate.

You can scroll past these routines until about Line 3000. From that line on, the rest of the script is concerned primarily with handling the high scores and table options. Like we’ve seen earlier this code too can be left alone.

Summary

Hey, that’s it. That was 3500+ lines of code and the complete script for a VPX pinball table.

Let me summarize what I think was important. First, the script is in Visual Basic, is easy to get to and edit, and can be edited in your favorite code editor.

Chart showing the percentage of the script for various functions.

Once you get past the global variables and constants, it is a lot of subroutines and functions. The subroutines fall in a couple of categories: init and exit subroutines that VPX calls when an object is newly created or about to be destroyed, user-initiated events for key down and key up, hit routines that are called when objects are hit by the pinball, and then any other subroutines you care to create to organize the code and make it easier to understand or more “portable”.

Most of the routines for reading/saving high scores, handling the entering of the player’s initials are fairly standard across many tables and don’t really need to be changed, can be left alone.

In coming posts we’ll take a deeper dive into playing sounds in VPX. And at that point we’ll be doing a little surgery on Teacher’s Pet.