Snapshot of the stern-event-agent work (as of 2026-10-12): - gamelog.c: passive PAD mode logging bus events (game/ball/bonus/tilt), all named shots and all-players' scores to /dump/mode.log - pad_mode_runtime.c: the SDK runtime + a GAMELOG section (passive stock-mode and award watching, gl_award_seq()/gl_last_award_id()), a per-edge switch logger to /dump/switch.log (switch_names.inc), and a passive Insider Connected gate; docs/runtime-patch.diff shows the exact changes vs the upstream runtime - event_probe.c / event_read.py: event-bus measurement tooling for identifying bus event ids on another game - ports/king_kong_le-0.97.port: 121 verified sites, 55 named shots, 6 bus events (emulator-proven by the SDK repo authors) Deployed and verified on the machine: events, shots, stock modes and awards. The /dump/switch.log feature is implemented but NOT yet deployed or verified, and the extra-ball award id (default 13) is unconfirmed. See README.md for build/install/log usage and unfinished work. Co-Authored-By: Claude Code <noreply@anthropic.com>
686 lines
53 KiB
C
686 lines
53 KiB
C
/* pad_mode.h - write a game mode of your own for a Stern Spike 2 pinball machine, in C.
|
|
*
|
|
* Read MODE_SDK.md first, then template_mode.c, which uses almost every call below.
|
|
*
|
|
* WHAT A MODE IS. A few functions the game calls: once at boot, 60 times a second, on
|
|
* every shot, and when the ball drains. Your mode decides when it starts, what scores
|
|
* while it runs, and what the display, speakers and lights do - using the GAME'S OWN
|
|
* scoring, sound, light and display code through the calls in this header, so it
|
|
* behaves like a mode the game shipped with.
|
|
*
|
|
* HOW IT GETS INTO THE GAME. Your .c files are compiled together with
|
|
* pad_mode_runtime.c into one shared object (build_mode.sh), which the machine preloads
|
|
* into the game. At boot the runtime reads the PORT for the game on the card
|
|
* (ports/<game>-<version>.port): where each game function is in THIS build, plus that
|
|
* build's shot names, scenes and callouts. Nothing in this header is an address, so the
|
|
* same mode source runs on any game and version that has a port.
|
|
*
|
|
* THE SAFETY GATE. Before hooking anything the runtime checks, for every function the
|
|
* port names, the first two machine instructions against what the port says they
|
|
* should be. If the core ones do not match - a port for another game or version - it
|
|
* hooks NOTHING, calls none of your code, logs why, and the game runs stock. An address
|
|
* outside the game's own code is never read, and the core is checked before the rest.
|
|
*
|
|
* THE RULES THAT KEEP A GAME RUNNING (MODE_SDK.md explains each):
|
|
* 1. Never block. Every callback runs on a game thread; return quickly.
|
|
* 2. No malloc, no files beyond pm_trigger/pm_log, no libc beyond pm_snprintf.
|
|
* The object is built -nostdlib: there is no allocator inside it.
|
|
* 3. Keep state in static variables. Stack is fine for small buffers.
|
|
* 4. One of our modes runs at a time: call pm_begin() before you start, pm_end()
|
|
* when you finish, and do not start if pm_begin() says no.
|
|
* 5. A call that returns failure (0 / NULL) means this game's port lacks that
|
|
* capability, or the thing asked for is not there. Carry on without it.
|
|
*/
|
|
#ifndef PAD_MODE_H
|
|
#define PAD_MODE_H
|
|
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
|
|
/* ---- a mode: define one of these and register it ----------------------------------
|
|
* Every callback is optional (leave it 0). All of them run on the game's own threads -
|
|
* which ones is logged at every boot (MODE_SDK.md, "What is measured").
|
|
*
|
|
* static const struct pm_mode my_mode = {
|
|
* .name = "RAMP FRENZY", .init = on_init, .tick = on_tick,
|
|
* .shot = on_shot, .ball_end = on_ball_end,
|
|
* };
|
|
* PM_REGISTER(my_mode);
|
|
*/
|
|
struct pm_mode {
|
|
const char *name; /* shown in the log; keep it short */
|
|
void (*init)(void); /* once, on the game's first tick (the game is up; its
|
|
scenes may still be loading - see pm_node) */
|
|
void (*tick)(void); /* 60 times a second, on the game's scheduler thread */
|
|
void (*shot)(uint64_t shot); /* every shot the game dispatches - see pm_shot() */
|
|
void (*ball_end)(void); /* the ball in play drained */
|
|
void (*event)(unsigned id); /* one of the game's EVENTS fired (ball start, multiball
|
|
start...): see pm_event(). Called from the tick, within
|
|
a tick of the game's own broadcast. */
|
|
};
|
|
|
|
#define PM_REGISTER(m) \
|
|
static const struct pm_mode *const pm__register_##m \
|
|
__attribute__((used, section("pm_modes"))) = &(m)
|
|
|
|
/* ---- what this game can do ----------------------------------------------------------
|
|
* A port lists only what was found and checked for its build. Ask before relying on a
|
|
* capability, or just call - a missing one fails safely. */
|
|
#define PM_CAN_CALLOUT 0x0001u /* pm_callout, pm_callout_nth */
|
|
#define PM_CAN_LIGHTS 0x0002u /* pm_lights */
|
|
#define PM_CAN_SCREENS 0x0004u /* pm_node, pm_text, pm_show, pm_set_text */
|
|
#define PM_CAN_CLIPS 0x0008u /* pm_clip */
|
|
#define PM_CAN_OWN_SOUND 0x0010u /* pm_callout_own_sound */
|
|
#define PM_CAN_MESSAGES 0x0020u /* pm_message_set / pm_message_restore */
|
|
#define PM_CAN_AWARD_SCREEN 0x0040u /* pm_award_screen */
|
|
#define PM_CAN_EVENTS 0x0080u /* .event callbacks, pm_event */
|
|
#define PM_CAN_SWITCH_SHOTS 0x0200u /* shots from switches: the port's `switch` lines (see shots) */
|
|
|
|
int pm_can(unsigned what); /* 1 if EVERY bit in `what` is available */
|
|
const char *pm_game(void); /* the port's game, e.g. "godzilla_pro" */
|
|
const char *pm_version(void); /* the port's version, e.g. "1.15" */
|
|
|
|
/* ---- the game, right now ----------------------------------------------------------- */
|
|
int pm_in_game(void); /* a game is being played (not attract, not a menu) */
|
|
unsigned pm_player(void); /* the player up, 1-4; 0 when no game */
|
|
uint64_t pm_score(unsigned player); /* that player's score */
|
|
|
|
/* ---- shots --------------------------------------------------------------------------
|
|
* A SHOT is a 64-bit mask the game hands every mode when a playfield switch means
|
|
* something: one bit per shot (a ramp, a target...). A single switch can dispatch twice -
|
|
* first 0x1 ("a playfield switch was hit"), then its own shot bit - so test bits, never
|
|
* compare the whole mask. On a title whose rules send no shot for some switches (The
|
|
* Beatles' standups and lanes), the port can map those switches to shot bits of their own
|
|
* (`switch` lines, PM_CAN_SWITCH_SHOTS): each hit comes to .shot like any other shot,
|
|
* from the tick. The port names this game's shots, switch ones included: */
|
|
uint64_t pm_shot(const char *name); /* the named shot's mask, or 0 if this game has none */
|
|
const char *pm_shot_name(uint64_t shot); /* the first named shot in `shot`, or 0 */
|
|
int pm_shot_count(void); /* how many named shots the port has */
|
|
const char *pm_shot_at(int i, uint64_t *mask); /* the i-th named shot (0-based), or 0 */
|
|
|
|
/* ---- one mode at a time ------------------------------------------------------------ */
|
|
int pm_begin(void); /* 1 = your mode is now the one running; 0 = another one is */
|
|
void pm_end(void); /* your mode stopped running */
|
|
int pm_running(void); /* your mode is the one running */
|
|
void pm_running_name(const char *name); /* PAD-363: after pm_begin, the name the runtime's own lines give
|
|
your mode while it runs (one object running several modes) */
|
|
unsigned pm_begun(void); /* PAD-413: how many times one of our modes has begun. Remember it at your end: when it
|
|
moves, another mode has the screen, so drop your ending (total, own screen) at once.
|
|
A full-screen clip of yours still playing is stopped by the runtime then. */
|
|
|
|
/* ---- scoring ------------------------------------------------------------------------
|
|
* Through the game's own scoring, so its playfield multiplier and its rules about when a
|
|
* score may be added apply. Returns what was actually added (can be 0). A title whose
|
|
* scores are 32-bit (The Beatles: the port's score_add32) multiplies the points and adds
|
|
* them with no carry check, so the points are cut to the room left below 4,294,967,295
|
|
* divided by the multiplier (the port's data score_mult): the score stops at the top
|
|
* instead of wrapping, and at the top an award adds 0. */
|
|
uint64_t pm_score_add(unsigned player, uint64_t points);
|
|
/* PAD-314: takes points away. The loss is written straight into the game's score table (the
|
|
* same u64[4] / u32[4] pm_score reads), cut to the player's score so it never passes 0 - the
|
|
* scores are unsigned, so below 0 would read as an enormous number - and the game's scoring
|
|
* call is then given 0 points so whatever it does on a change (its score display) runs. No
|
|
* multiplier: a loss is the flat number asked for. Returns what was taken (can be 0). */
|
|
uint64_t pm_score_sub(unsigned player, uint64_t points);
|
|
|
|
/* ---- sound --------------------------------------------------------------------------
|
|
* A callout is one of the game's own speech/sound requests, by id. The port names the
|
|
* useful ones by role: pm_callout_id("ten_seconds"), "countdown", "time_up". */
|
|
unsigned pm_callout_id(const char *role); /* 0 if the port has no such role */
|
|
void pm_callout(unsigned id);
|
|
void pm_callout_nth(unsigned id, unsigned n); /* a numbered variant (a countdown's "3"); on the
|
|
countdown role n counts from the port's
|
|
`value countdown_first` (the clip "one" is in) */
|
|
/* A sound the card never shipped: carried by the game's callout `carrier`, with the
|
|
* sound container key the build gave your appended sound (Modes tab / Write).
|
|
* Countdown variants n = 0..4 are proven; others are not measured. */
|
|
int pm_callout_own_sound(unsigned carrier, const unsigned char key[8]);
|
|
/* A sound REQUEST by id, straight to the game's sound code: no city variant is swapped in.
|
|
* A sound of your own is a request whose record the build re-pointed (Modes tab / Write,
|
|
* MODE_SDK.md "Sounds of your own"). Each returns 0 when the port lacks the call. */
|
|
int pm_sound(unsigned request); /* start it; 1 if the port has the call */
|
|
int pm_sound_active(unsigned request); /* 1 while a channel plays it */
|
|
int pm_sound_stop(unsigned request); /* stop every channel playing it; 1 if one was */
|
|
/* How a request competes for its sound bus (music, voice and each effect bus hold ONE sound
|
|
* at a time): a new request takes a bus from a playing one of LOWER priority, or of EQUAL
|
|
* priority when the playing one's steal flag (bit 0) is set; otherwise it is not played.
|
|
* Sets the request's priority and flags (-1 leaves one as it is) and returns the old pair as
|
|
* priority << 8 | flags, or -1 when the port has no request table (data sound_requests) or
|
|
* the record does not look like one. Only for a request the game never plays itself, such
|
|
* as the carrier of a sound of your own. */
|
|
int pm_sound_priority(unsigned request, int priority, int flags);
|
|
/* Item 150 follow-up (MODE_SDK.md "A music bed of your own"):
|
|
* pm_sound_sid points `request` at ONE sound id until it is called again with sid 0, which puts
|
|
* the request's own list back. The one music carrier plays a different bed for every mode this
|
|
* way: each bed is a sound id the build bound to an appended record and no request names.
|
|
* 1 = done; 0 when the port has no request table or the record does not look like one.
|
|
* pm_sound_fade fades every channel playing `request` down to silence over `ms`, then stops it
|
|
* (a stop at full level is a click); 1 if one was playing. Without the port's channel and voice
|
|
* offsets it stops at once. pm_sound() on a request still fading ends that fade first.
|
|
* pm_sound_playing fills up to `max` requests (and their bus bits: 0x01 music, 0x02 voice) of
|
|
* the channels playing now; returns how many play, or -1 when the port has no channel table. */
|
|
int pm_sound_sid(unsigned request, unsigned sid);
|
|
/* Item 163: a sound of your own with NO stock sound id touched. For ONE play, every lookup of the
|
|
* carrier's own record key `stock` takes `ours` instead - a record the build appended that no
|
|
* descriptor names - and `request` plays at `priority` (no steal flag); the key and priority come
|
|
* back by themselves once `ms` (+1/8 +1 s; 10 s when 0) have passed. Call it
|
|
* right before pm_sound(request). The carrier's descriptor still says the bus, the loop and how
|
|
* long it plays, so `ours` must be no longer than the carrier's own record. 1 = armed; 0 when
|
|
* the port has no sound_lookup site (then do NOT play: the carrier would play its own line). */
|
|
int pm_sound_swap(unsigned request, const unsigned char stock[8], const unsigned char ours[8], int priority, unsigned ms);
|
|
int pm_sound_fade(unsigned request, unsigned ms);
|
|
int pm_sound_playing(unsigned *requests, unsigned *buses, int max);
|
|
/* PAD-415: the game's own hit sounds, a rising run the port names (`value hit_sound_<n>`, n from 1, low to high: on
|
|
* Godzilla its eight pitched orchestra hits), so every hit of a mode is heard - while one of ours runs the game's
|
|
* rules see no shots, and play no sound of their own for them. pm_hit_sound(n) plays the n-th (past the last, the
|
|
* last), so a mode's hits can climb as it goes; never two within 100 ms (a spinner's run is one sound).
|
|
* 1 = played; 0 = the port names none, or too soon. pm_hit_sounds: how many the port names (0 = none here). */
|
|
int pm_hit_sound(int n);
|
|
int pm_hit_sounds(void);
|
|
|
|
/* ---- lights -------------------------------------------------------------------------
|
|
* One command in the game's own light language, e.g.
|
|
* "blele --sweep 0 --lts 224 --red 0 --green 255 --blue 0 --freq 10 --use_alpha 1 --alpha 255"
|
|
* The port supplies the owner id. Returns 1 only if it ran under a live show - without
|
|
* one the game takes the command and lights nothing. MODE_SDK.md, "Lights". */
|
|
int pm_lights(const char *command);
|
|
int pm_lights_as(unsigned owner, const char *command); /* a specific owner id */
|
|
|
|
/* ---- named inserts: the playfield's lamps, one by one (MODE_SDK.md "Lights: named inserts")
|
|
* The port's `lamp` lines name every insert on the playfield (the game's own names: "LEFT RAMP",
|
|
* "MASER", "POWERLINE LEFT"...) and the shots the game itself ties to each. A mode HOLDS an insert
|
|
* with a colour and a pattern; while it holds it, that is what the insert shows, on top of the
|
|
* game's own light shows, and every insert it does not hold keeps doing what the game wants (its
|
|
* modes, battles, multiballs, attract). Releasing hands the insert back to the game at once.
|
|
*
|
|
* It rides the game's own layering: the game lights through LAYERS (lamp groups), each with a
|
|
* priority, composited bottom to top every frame; a layer covers only the lights it holds. The
|
|
* runtime gives the mode a layer of its own at priority 255 (the top) unless the mode asks for a
|
|
* lower one, so a game show of a higher priority covers it there.
|
|
*
|
|
* pm_lamp_shot(pm_shot("Left ramp"), PM_RGB(255, 0, 0), PM_LAMP_BLINK, 0);
|
|
* pm_lamp_set("TANK 2,ADV TRAIN,TANK 3", PM_RGB(255, 200, 0), PM_LAMP_CHASE, 150);
|
|
* ...
|
|
* pm_lamp_release_all(); at the mode's end: the game has them back
|
|
*
|
|
* `names` is one insert or several separated by commas (case does not matter); a CHASE runs
|
|
* across them in the order given, one lit at a time. `period_ms` 0 takes the pattern's own
|
|
* (blink 500, pulse 1600, chase 150 a step). Each returns how many inserts it held or released;
|
|
* 0 when none of the names is in the port, or the port has no lamp lines (pm_can(PM_CAN_LAMPS)). */
|
|
#define PM_CAN_LAMPS 0x2000u /* pm_lamp_*: the port names inserts and the game's lamp layers */
|
|
#define PM_LAMP_SOLID 0
|
|
#define PM_LAMP_BLINK 1 /* on half the period, off half */
|
|
#define PM_LAMP_PULSE 2 /* breathes between dim and full */
|
|
#define PM_LAMP_CHASE 3 /* one insert of the set at a time, stepping every period */
|
|
#define PM_RGB(r, g, b) ((((unsigned)(r) & 255u) << 16) | (((unsigned)(g) & 255u) << 8) | ((unsigned)(b) & 255u))
|
|
int pm_lamp_count(void); /* how many inserts the port names */
|
|
const char *pm_lamp_at(int i, uint64_t *shots); /* the i-th insert's name, and its shots */
|
|
int pm_lamp_find(const char *name); /* its index, or -1 */
|
|
int pm_lamp_xy(int i, int *x, int *y); /* its place on the playfield picture (the port's
|
|
"at X,Y"; x 0-300 across, y 0-600 down); 0 = not placed */
|
|
int pm_lamp_paint(int i, unsigned rgb); /* hold ONE insert solid in rgb, quietly: a light
|
|
show paints them all every tick, only changes reach
|
|
the game; release them as usual */
|
|
int pm_lamp_set(const char *names, unsigned rgb, int pattern, unsigned period_ms);
|
|
int pm_lamp_shot(uint64_t shots, unsigned rgb, int pattern, unsigned period_ms); /* a shot's inserts */
|
|
int pm_lamp_all(unsigned rgb, int pattern, unsigned period_ms); /* every insert the port names */
|
|
int pm_lamp_release(const char *names);
|
|
int pm_lamp_release_shot(uint64_t shots);
|
|
int pm_lamp_release_all(void); /* every insert THIS mode holds */
|
|
/* PAD-415: a hit answers at once. The inserts of `shots` strobe in `rgb` (60 ms on, 60 off) for `ms` (0 = 480; at
|
|
* most 2000) over whatever the mode holds them in, then show it again; one the mode does not hold is held for the
|
|
* strobe only and handed back to the game after it, and one the mode releases while it strobes finishes the strobe
|
|
* first. Returns how many inserts strobe (0: none tied to those shots, or no PM_CAN_LAMPS). */
|
|
int pm_lamp_flash(uint64_t shots, unsigned rgb, unsigned ms);
|
|
/* This mode's layer priority, 1-255 (255 when never called): a game show above it covers its
|
|
* inserts. The inserts it holds move to the new layer. Returns the priority now in force. */
|
|
int pm_lamp_priority(unsigned priority);
|
|
/* The game's lamp layers right now, bottom to top: each one's priority, with 0x100 added for a
|
|
* layer of ours. Returns how many there are (up to max), or -1 without the port's layer list. */
|
|
int pm_lamp_layers(unsigned *priorities, int max);
|
|
|
|
/* ---- the display --------------------------------------------------------------------
|
|
* A SCREEN of your own is a node the build added to the game's "hud" scene (the Modes
|
|
* tab does this). For a mode in the card project's folder modes/<folder>/ its names are
|
|
* "PadMode_<folder>_Screen" and "PadMode_<folder>_Screen.PadMode_<folder>_Screen_Words"
|
|
* (MODE_SDK.md, "Screens"). `scene` is a role the port names
|
|
* ("hud") or a 40-hex scene id. A node is drawn only while its timeline AND this flag
|
|
* allow it, and a built screen is VISIBLE by default - so find it and hide it. A scene
|
|
* can load after your init runs: pm_node returns 0 until it has, so keep trying from
|
|
* your tick (twice a second is plenty) until it is found. */
|
|
const char *pm_scene_id(const char *role); /* the port's scene for a role */
|
|
void *pm_node(const char *scene, const char *path); /* a picture/group node, or 0 */
|
|
void *pm_text(const char *scene, const char *path); /* a text node, or 0 */
|
|
void pm_show(void *node, int on);
|
|
void pm_set_text(void *text, const char *words);
|
|
|
|
/* ---- clips --------------------------------------------------------------------------
|
|
* A full-screen video from the game's in-game video bank, by name - a stock clip, or one
|
|
* the build added. The runtime keeps drawing it every frame until it ends. */
|
|
int pm_clip(const char *name); /* 1 if the game started it */
|
|
int pm_clip_playing(void);
|
|
void pm_clip_stop(void);
|
|
|
|
/* ---- the backdrop: a clip BEHIND the HUD (hud-layers) ---------------------------------
|
|
* The game's own battles play their clip in the main-play background's place, with the score
|
|
* panel, the top bar and the timers drawn over it. pm_backdrop does the same for a mode: the clip
|
|
* (a stock one, or one the build added) loops in the city's place until the mode ends it (0 or
|
|
* ""), pm_end, or the ball ends. The game's own framed awards (the Maser) still play in that
|
|
* place and the loop comes back after them. A full-screen clip of yours (pm_clip, over
|
|
* everything, the HUD too) is played first when both are asked for: the loop waits for it. */
|
|
#define PM_CAN_BACKDROP 0x20000u /* pm_backdrop / pm_backdrop_once */
|
|
int pm_backdrop(const char *name); /* 1 = asked for; 0 = this port cannot */
|
|
int pm_backdrop_once(const char *name); /* play this once in the loop's place, then the loop again */
|
|
int pm_backdrop_showing(void); /* 1 while the backdrop is on the glass */
|
|
|
|
/* ---- the game's own message screens (optional, title specific) --------------------- */
|
|
int pm_message_set(unsigned id, const char *words); /* show `words` wherever id is shown */
|
|
void pm_message_restore(unsigned id);
|
|
int pm_award_screen(unsigned type, unsigned message_id, uint64_t value);
|
|
|
|
/* ---- anything else a port carries ---------------------------------------------------- */
|
|
const char *pm_port_text(const char *name); /* a `text` line, or 0 */
|
|
long pm_port_value(const char *name, long fallback); /* a `value` line, or fallback */
|
|
|
|
/* ---- helpers --------------------------------------------------------------------------- */
|
|
void pm_log(const char *fmt, ...) __attribute__((format(printf, 1, 2))); /* /dump/mode.log */
|
|
unsigned long pm_ms(void); /* milliseconds since boot */
|
|
int pm_trigger(const char *name); /* 1, once, when the file /dump/<name> appears */
|
|
long pm_read_file(const char *path, char *buf, unsigned long cap); /* bytes read, or -1 */
|
|
int pm_trigger_text(const char *name, char *out, unsigned cap); /* ...and its first line */
|
|
void pm_commas(char *out, unsigned cap, uint64_t value); /* 1234567 -> "1,234,567" */
|
|
int pm_snprintf(char *out, unsigned long cap, const char *fmt, ...)
|
|
__attribute__((format(printf, 3, 4)));
|
|
|
|
/* ---- the game's own modes ------------------------------------------------------------
|
|
* Is one of the GAME'S modes going on (a battle, a multiball)? Asked through the game's
|
|
* own mode manager, with the queries the game itself uses (MODE_SDK.md, "The game's own
|
|
* modes"). "Running" means ACTIVE to the game: the mode is started for the player up, or
|
|
* its timer or start sequence is still live. A mode that should not stack on the game's
|
|
* own asks before it starts. */
|
|
#define PM_STOCK_ANY 0x1u /* any of the game's modes */
|
|
#define PM_STOCK_MULTIBALL 0x2u /* a multiball */
|
|
#define PM_STOCK_BATTLE 0x4u /* a battle (Godzilla's kaiju battles) */
|
|
/* Of the kinds asked for, the first one active (checked BATTLE, then MULTIBALL, then ANY);
|
|
* 0 = none of them; -1 = this port cannot tell (no stock queries for a kind asked) */
|
|
int pm_stock_mode_running(unsigned kinds);
|
|
const char *pm_stock_mode_what(unsigned kind); /* "a battle", "a multiball", "a stock mode" */
|
|
/* PAD-347: STACKING. Stern never shows two modes' words at once: one mode has the middle of the
|
|
* screen, the others keep to their badges at the edge. While one of the game's own modes runs for
|
|
* the player up, the middle is the game's: a mode that keeps running beside it moves its words out
|
|
* of the way (the title and instruction line, the counters along the top, a screen of its own) and
|
|
* keeps only what sits at the edges. pm_aside() says when: the kind running (PM_STOCK_BATTLE, then
|
|
* PM_STOCK_MULTIBALL, then PM_STOCK_ANY) or 0. Cheap to call every tick (the game is asked at most
|
|
* five times a second); 0 on a port that cannot tell, and outside a game. */
|
|
int pm_aside(void);
|
|
/* PAD-347 / PAD-363: ISOLATION. A mode may keep the game's own modes from STARTING while it runs: the
|
|
* start of each mode the port names (generated from the game program; never a multiball) is refused at its
|
|
* entry, so that mode never begins and the rule that asked carries on. Which ones: the mode's own list
|
|
* (pm_block_list: n of the game's mode ids, 0-127 - the port's `text block_name_<id>` names them), or the
|
|
* port's checked defaults (`text block_default <ids>`). On Godzilla a blocking mode also keeps a battle from
|
|
* being lit or its select screen from opening at the scoop. 1 = blocking (0 to stop); ends by itself with the
|
|
* mode; 0 when the port cannot. pm_block_list is called before pm_block_game_modes; n = 0: the defaults again.
|
|
* An id the port does not name is left out. */
|
|
#define PM_CAN_BLOCK_GAME 0x40000u /* pm_block_game_modes, pm_block_list */
|
|
int pm_block_game_modes(int on);
|
|
int pm_block_list(const unsigned char *ids, int n);
|
|
/* PAD-398: while a mode of ours blocks, every rule of the game's the port names (`site block_rule_<n>`, `text
|
|
* block_rule_name_<n>`: Godzilla's Destruction Jackpot, its building locks, the bridge, the cities...) is shown
|
|
* no shots - nothing of the game's lights, locks, counts or awards - except the ones the mode KEEPS: n indices
|
|
* <n> of those lines. Called from the mode's own files as they are read (a mode file's `keep_rules`, a code
|
|
* mode's assets file); it holds for the calling mode from its next pm_block_game_modes(1). 0 when the port
|
|
* cannot block. */
|
|
int pm_block_rules_keep(const unsigned char *ns, int n);
|
|
int pm_block_rules_keep_names(const char *names); /* the same by name: "Destruction Jackpot, Bridge" */
|
|
|
|
/* ---- events -------------------------------------------------------------------------------
|
|
* The game's rules talk through numbered EVENTS (a ball started, a multiball started, the
|
|
* skill shot was made). The port names the ones measured for its build (`event` lines,
|
|
* MODE_SDK.md "Events"); a mode's .event callback gets each named one as it fires:
|
|
*
|
|
* static void on_event(unsigned id) { if ((int)id == pm_event("ball_start")) ... }
|
|
*
|
|
* Only events the port names are delivered, once per firing, on the tick thread. */
|
|
int pm_event(const char *name); /* the named event's id, or -1 if this port has none */
|
|
const char *pm_event_name(unsigned id); /* the port's name for an id, or 0 */
|
|
|
|
/* ---- the battle roster (item 146; title specific: Godzilla) ---------------------------
|
|
* Godzilla's battles are picked on its BATTLE SELECTION screen from a roster of seven
|
|
* slots (0 Ebirah, 1 Titanosaurus, 2 Gigan, 3 Megalon, 4 King Ghidorah, 5 Megalon & Gigan,
|
|
* 6 King Ghidorah & Gigan). A mode can CLAIM one slot: when the player picks it, the
|
|
* game's battle for that slot does not start and `on_pick(slot)` runs instead, on the
|
|
* game's thread, at the moment the battle would have started. on_pick returns 1 when it
|
|
* took the pick (the battle is skipped) and 0 to let the game's own battle run.
|
|
* The roster is a fixed-size table in the game's code: a mode takes a slot's place, it
|
|
* cannot add an eighth. The slot's name and art on the selection screen are the card
|
|
* build's job (MODE_SDK.md, "A monster in the roster"). */
|
|
#define PM_CAN_ROSTER 0x0100u /* pm_roster_claim (0x0080 is item 147's PM_CAN_EVENTS) */
|
|
int pm_roster_slots(void); /* 0 when the port has no roster */
|
|
int pm_roster_claim(unsigned slot, int (*on_pick)(unsigned slot)); /* 1 = claimed */
|
|
void pm_roster_release(unsigned slot);
|
|
/* When a mode started by a pick ends, call this: the battle rule then does what it does when one
|
|
* of the game's battles stops (the ramps light again, so another battle can be qualified). Until
|
|
* then the ramps stay dark and the scoop opens nothing, as during a battle of the game's own.
|
|
* It acts only on a pick a mode took and has not given back yet, and only for the player who picked:
|
|
* called while another player is up it waits until that player is up again, and after the game has
|
|
* ended it does nothing (a new game lights every player's ramps). A mode that never calls it gets it
|
|
* called at the end of the ball its pick was taken in. */
|
|
void pm_roster_done(void);
|
|
/* The slot's spoken name. When the cursor lands on a slot the selection screen plays that slot's
|
|
* callout (Godzilla: slot 0 says "Ebirah!"). A claimed slot is SILENT, so the old monster's name is
|
|
* never heard over the new art; this sets a sound request id of the mode's own for the slot it holds
|
|
* (0 = silent), and releasing the slot puts the game's back. 1 = written; 0 when the caller does not
|
|
* hold the slot or the port has no `data roster_screen_table`. */
|
|
int pm_roster_callout(unsigned slot, unsigned id);
|
|
|
|
/* ---- display priority (item 154 display; MODE_SDK.md "Display priority") ------------------
|
|
* The game decides what is on its screen with PRIORITIES, on two levels: its DISPLAY EFFECTS
|
|
* (152 on Godzilla, each with a priority 1-255: BATTLE IS LIT 177, a multiball's jackpot 184,
|
|
* the tilt warning 241), one at a time, a higher one taking over and a lower one waiting; and,
|
|
* inside its layered-display effect (priority 1), its LAYERED displays (the shot awards: LOOPS,
|
|
* the Maser and powerline clips; the mode starts and totals), ranked the same way among
|
|
* themselves. pm_display_priority makes your RUNNING mode, to that arbitration, a display of
|
|
* `priority` (1-255, the display-effect scale): until you call it with 0, or your mode ends,
|
|
* - a display effect of the game's comes through only when its own priority beats `priority`
|
|
* (the battle select screen 196, the tilt warning 241); a lower one waits (BATTLE IS LIT
|
|
* 177), or is refused, as it would be behind one of the game's own effects;
|
|
* - a layered display the game flags as a MODE START or TOTAL counts as one of the game's mode
|
|
* displays (priority 184): it comes through when that beats yours;
|
|
* - any other FULL-SCREEN layered display (LOOPS, combos, bonus) waits;
|
|
* - a FRAMED layered display (the Maser, powerline, building clips, drawn inside the score
|
|
* frame, under your screen) plays as the game layers it, except while your own clip plays.
|
|
* A display that comes through covers your screen for its length; your screen is in view again
|
|
* when it ends (nothing to call). Your clip stops being drawn the moment the game plays another
|
|
* clip. 1 = held; 0 = the port has no display arbitration (PM_CAN_DISPLAY_PRIORITY), or your
|
|
* mode is not the one running. */
|
|
#define PM_CAN_DISPLAY_PRIORITY 0x4000u /* pm_display_priority (0x0100 is the roster's) */
|
|
int pm_display_priority(unsigned priority);
|
|
/* 1 while a display of the game's that beat your priority has the screen (your screen is
|
|
* under it); 0 otherwise, and with no priority held. */
|
|
int pm_display_covered(void);
|
|
/* Your mode is over (as pm_end: another may begin at once) but its ENDING - a full-screen clip, its
|
|
* total - should keep the screen: the display priority it holds stays for `ms` more, so an award of
|
|
* the game's that waited through the mode does not take the one video surface from the ending clip.
|
|
* The hold goes when the time is up, another mode begins, the ball or the game ends, or you call
|
|
* pm_display_priority(0). 1 = ended (the hold kept if one was held); 0 = your mode was not running. */
|
|
int pm_end_holding(unsigned ms);
|
|
|
|
/* ---- the game's own rules: their shot handlers (item 160; MODE_SDK.md "Counts as") ----------
|
|
* A rule the game shipped with (Godzilla's battle vs Ebirah, its tank attack multiball) is a
|
|
* compiled object; the port names it (`rule <id> <vtable> <label>`) with the manager's get and
|
|
* the slot numbers of its build. Its SHOT HANDLER tests the raw shot mask for fixed bits, so a
|
|
* shot it does not know can only count for it by ARRIVING as the bit it tests. The runtime
|
|
* wraps that vtable slot (the object's vtable is checked against the port first) and, from
|
|
* stock.cfg beside the mode files (`counts_as <rule> <shot> -> <shot>`, re-read twice a
|
|
* second), REPLACES a mapped dispatch with the target bit while that bit is lit in the rule's
|
|
* own per-player mask - never ORed in, never once the rule has cleared it. The stand-in's
|
|
* inserts are held (pm_lamp_*) while the target is lit. An empty table plays every rule stock.
|
|
*
|
|
* From C a mode may add rows, or take a rule's handler over entirely (item 161): a hook sees
|
|
* every shot before the game's handler, may change *shot, and returns 1 to let the game's
|
|
* handler run with it or 0 to keep it from running at all. Every call answers 0 (or -1) on a
|
|
* port without `rule` lines (pm_can(PM_CAN_STOCK_RULES)). */
|
|
#define PM_CAN_STOCK_RULES 0x8000u /* the port names rules, the manager's get and the shot slot */
|
|
typedef int (*pm_stock_shot_fn)(unsigned rule, uint64_t *shot, void *object);
|
|
int pm_stock_rule_count(void); /* rules the port names */
|
|
int pm_stock_rule_at(int i, unsigned *id, const char **label); /* the i-th; 1 = filled */
|
|
void *pm_stock_rule_object(unsigned rule); /* the rule's object; 0 until the manager is built */
|
|
int pm_stock_rule_active(unsigned rule); /* 1 / 0 for the player up; -1 unknown */
|
|
uint64_t pm_stock_rule_field(unsigned rule); /* the rule's lit mask for the player up */
|
|
int pm_stock_rule_hook(unsigned rule, pm_stock_shot_fn fn); /* 1 = the handler is wrapped and yours */
|
|
void pm_stock_rule_unhook(unsigned rule); /* the wrap stays; it passes through again */
|
|
/* a counts-as row from C (kept across stock.cfg reloads); to = 0 removes the row for `from`.
|
|
* `to` must be ONE bit outside `from`. 1 = the table now holds it. */
|
|
int pm_stock_counts_as(unsigned rule, uint64_t from, uint64_t to);
|
|
|
|
/* ---- a multiball of your own (item 167; MODE_SDK.md "A multiball of your own") ---------------
|
|
* Every Spike 2 build shares the framework's ball code, and one call of it is how the game's
|
|
* own multiballs put balls in play: "serve balls until `balls` are in play, with a ball save".
|
|
* The port names it (`site multiball_serve`, found on every build by its code) beside the
|
|
* framework's count of the balls in play (`site balls_in_play`). A mode calls it when it
|
|
* starts; the game's trough and auto-launcher do the serving, its ball saver hands a drained
|
|
* ball back for `ballsave_s` seconds, and the game's own end of ball comes only when the LAST
|
|
* ball drains (the events, `.ball_end`, the bonus: all as the game's multiballs have them).
|
|
* The game never knows the multiball is yours: its mode manager runs nothing for it, so its
|
|
* own multiball display, jackpots and music stay off - the mode's screen, clip, sounds and
|
|
* lights are what the player sees and hears.
|
|
* pm_multiball_start: 1 = the game is serving; 0 = it refused (no game in play, a tilt, the
|
|
* port lacks the call). `balls` is the TOTAL wanted in play, 2 to 6, cut to the balls the
|
|
* machine has. pm_multiball_add asks for `n` more than are in play now (an add-a-ball).
|
|
* pm_balls_in_play: the framework's count (2 or more during a multiball; while balls are being
|
|
* served it answers the balls asked for); -1 when the port has no balls_in_play site. A mode
|
|
* ends its multiball itself: when the count has been 1 for a couple of seconds after the ball
|
|
* save is over (mode_file.c does exactly that). */
|
|
#define PM_CAN_MULTIBALL 0x10000u /* pm_multiball_start / pm_multiball_add (and pm_balls_in_play) */
|
|
int pm_multiball_start(unsigned balls, unsigned ballsave_s);
|
|
int pm_multiball_add(unsigned n, unsigned ballsave_s);
|
|
int pm_balls_in_play(void);
|
|
/* A ball save on its own, with no multiball (MODE_SDK.md "A ball save of your own"). The game's
|
|
* own one-ball saves (Godzilla's Planet X hurry-up: 10 s; its adjustment-timed mode-start save)
|
|
* go through the same framework call as a multiball, asking for no more balls than are in play,
|
|
* with a ball save: "serve balls until the count in play are in play, with a ball save". So
|
|
* pm_ball_save asks exactly that: for `seconds` (1-120) a drained ball is served back and the
|
|
* ball does not end. It needs what a multiball needs (PM_CAN_MULTIBALL). 1 = the save is on;
|
|
* 0 = the game refused (no game in play, a tilt) or the port lacks the call. During the game's
|
|
* own multiball it only lengthens that multiball's save (the framework keeps the longer). */
|
|
int pm_ball_save(unsigned seconds);
|
|
|
|
/* ---- the magnet (PAD-381; MODE_SDK.md "The magnet") ------------------------------------------
|
|
* A grab with the playfield magnet, for `ms` milliseconds. You ask for a time and nothing else:
|
|
* the powers are the operator's own magnet adjustments (draw power for the draw time, then hold
|
|
* power), and the runtime decides whether you may and lets go by itself. It is a coil on 48 V, so
|
|
* the limits are the runtime's and cannot be raised:
|
|
* - `ms` is clamped to 100..5000, the draw time included. The whole grab goes to the board as ONE
|
|
* command that ends by itself, and is never re-sent: if your mode, the runtime or the game
|
|
* stops, the magnet still lets go when that time is up.
|
|
* - only while your mode runs, in a game (not attract, not tilted), with the magnet not disabled
|
|
* in the settings, while none of the game's own magnet sequences runs, one grab at a time, 3 s
|
|
* from the end of one grab to the start of the next, at most 6 grabs a minute.
|
|
* - the runtime lets go at the grab's end, when you call pm_magnet_release, when your mode ends,
|
|
* when the ball ends, and when the game ends or tilts. If the game starts a magnet sequence of
|
|
* its own while you hold, yours steps aside and the game drives the magnet.
|
|
* - the grab runs as a game process of the runtime's that takes control of the magnet, as the
|
|
* game's own grabs do (without it the game switches the magnet off at once); if the game ends
|
|
* that process (a drain, a tilt), the magnet goes off the game's own way.
|
|
* pm_magnet_grab: 1 = grabbing; 0 = refused (the reason is in mode.log). Godzilla only (the port's
|
|
* `site coil_fire` and `value magnet_dev`); PM_CAN_COILS says whether this game has it. */
|
|
#define PM_CAN_COILS 0x200000u /* pm_magnet_grab / pm_magnet_release / pm_magnet_holding */
|
|
int pm_magnet_grab(unsigned ms);
|
|
void pm_magnet_release(void);
|
|
int pm_magnet_holding(void);
|
|
/* PAD-381: the same hold for any coil of that kind the port names (`text held_coils`; Godzilla Premium/LE:
|
|
* "magnet" the Godzilla magnet, "mg_magnet" the Mechagodzilla magnet, "bridge" the bridge diverter), with
|
|
* the same limits each: one command from a process of the runtime's that controls the coil, the coil's own
|
|
* powers, at most 5000 ms, 3 s between holds, 6 a minute, never while the game uses it.
|
|
* pm_magnet_grab(ms) is pm_coil_hold("magnet", ms). pm_coil_known: 1 when this game has that coil. */
|
|
int pm_coil_hold(const char *name, unsigned ms);
|
|
void pm_coil_release(const char *name);
|
|
int pm_coil_holding(const char *name);
|
|
int pm_coil_known(const char *name);
|
|
|
|
/* ---- the scoop (PAD-381; MODE_SDK.md "The scoop") ---------------------------------------------
|
|
* Hold a ball that lands in the scoop for `ms` milliseconds while your mode runs, then let the game
|
|
* kick it out. You ask for a time and nothing else: the kick-out is the game's own, at the operator's
|
|
* SCOOP KICK POWER, with the game's own retries, and nothing here fires a coil. The game's own use of
|
|
* a landing ball (its rules, a select screen) always comes first; your hold starts when it is done.
|
|
* - `ms` is 100..10000; 0 = no hold (and a ball held now goes). It applies to every ball that
|
|
* settles in the scoop while your mode runs.
|
|
* - the hold ends at its time, on pm_scoop_release, when your mode ends, and when the game ends or
|
|
* tilts.
|
|
* pm_scoop_hold: 1 = set; 0 = not the running mode, or no PM_CAN_SCOOP on this game (the port's
|
|
* `site scoop_handler`, `data scoop_slot`, `value scoop_event`). pm_scoop_holding: 1 while a ball is held. */
|
|
#define PM_CAN_SCOOP 0x100000u /* pm_scoop_hold / pm_scoop_release / pm_scoop_holding */
|
|
int pm_scoop_hold(unsigned ms);
|
|
void pm_scoop_release(void);
|
|
int pm_scoop_holding(void);
|
|
|
|
/* ---- the shield platform (PAD-379) ---------------------------------------------------------------
|
|
* Godzilla Premium/LE carries its three shield targets on a platform a motor turns. AWAY: the spinner
|
|
* side faces the player and the shields cannot be reached from the flippers - the game's home, and
|
|
* where most of its battles play. TOWARD: the shield targets face the flippers (parts of the game's
|
|
* Mechagodzilla multiball). pm_shield asks the game's own motor to turn the platform: 1 = turning (it
|
|
* takes about a second to get there) or already there; 0 = refused (an operator switched the motor off
|
|
* in the adjustments) or not on this machine (a Pro: its shield targets are fixed and always face the
|
|
* player; or the port has no shield lines). pm_shield_position says where it
|
|
* is now: PM_SHIELD_AWAY or PM_SHIELD_TOWARD once it has stopped there, 0 while it turns (or before
|
|
* the game has found it), -1 when there is no platform. A mode that turns it toward the player turns
|
|
* it back AWAY when it ends - unless the game's own mode began, which then has the platform
|
|
* (intricate_kit.h: kit_shields_in / kit_shields_out). An operator who switched the motor off in the
|
|
* adjustments keeps it AWAY: pm_shield says 0 and the position stays AWAY.
|
|
* PAD-392: the limits are the runtime's and cannot be raised - only while your mode runs, in a game, and
|
|
* never while one of the game's own modes or multiballs runs (the game has the platform then); 1.5 s from
|
|
* one move's start to the next, at most 12 moves a minute (0 = refused, the reason in mode.log). When
|
|
* your mode ends, the ball ends, or the game ends or tilts, the runtime turns the platform back where it
|
|
* was before your first move - unless the game has since turned it itself. PAD-409: from your first move
|
|
* until then, the game's own background return of the platform to its resting place (every ~3 s) waits, so
|
|
* it stays where you put it; the game's ball search and its own modes still move it.
|
|
* pm_shield_keep turns it and KEEPS it there while your mode runs: the game's ball search swings the
|
|
* platform and leaves it AWAY, so a platform found resting elsewhere for 1.5 s is turned back, within the
|
|
* same limits. It is kept only while the game's own shield feature (on Godzilla: Mechagodzilla Shield)
|
|
* sees no shots - your mode blocks the game's rules (PAD-398, the default) and does not keep that one
|
|
* counting. While that feature counts, the game turns the platform back about 2 s after every move, and
|
|
* pm_shield_keep turns it once and leaves it to the game. 1 = keeping it (0 = stop keeping; it stays where
|
|
* it is); 0 = no platform or your mode is not running. */
|
|
#define PM_CAN_SHIELD 0x80000u /* pm_shield / pm_shield_position / pm_shield_keep */
|
|
#define PM_SHIELD_AWAY 1
|
|
#define PM_SHIELD_TOWARD 2
|
|
int pm_shield(int where);
|
|
int pm_shield_position(void);
|
|
int pm_shield_keep(int where);
|
|
|
|
/* ---- the building (PAD-393) -----------------------------------------------------------------------
|
|
* Godzilla Premium/LE's building rides a stepper. pm_building asks the game to move it to a FLOOR, 0 (where
|
|
* the game keeps it at rest, beside its home switch) .. 3 (the furthest the game sends it), at the operator's own building speed settings: the
|
|
* floors are the game's own, so nothing a mode asks can drive it past the travel the game itself uses.
|
|
* The limits are the runtime's and cannot be raised:
|
|
* - only while your mode runs, in a game (not attract, not tilted); never while the building is busy
|
|
* (moving, homing, or one of the game's own building sequences runs), switched off in the settings
|
|
* (BUILDING STEPPER DISABLED) or faulted; 3 s from one move's start to the next; at most 6 a minute.
|
|
* - when your mode ends, the ball ends, or the game ends or tilts, the runtime puts the building back
|
|
* on the floor it was at before your first move - unless the game has since moved it itself.
|
|
* pm_building: 1 = sent (or already there); 0 = refused (the reason is in mode.log) or no building on this
|
|
* machine (a Pro, or a port without the building lines). pm_building_floor: the floor it is stopped at,
|
|
* -2 while it moves (or before the game has homed it), -1 when there is no building. */
|
|
#define PM_CAN_BUILDING 0x400000u /* pm_building / pm_building_floor */
|
|
int pm_building(int floor);
|
|
int pm_building_floor(void);
|
|
|
|
/* ---- the game's own light shows (PAD-411) -----------------------------------------------------------
|
|
* The game's playfield shows (a jackpot's, a multiball's start, the attract run's) are processes of its own:
|
|
* each makes a lamp group, plays its layered light-language commands (several effects over the game's light
|
|
* sets, with their delays and loops), sleeps through the show and stops every effect it started. The port
|
|
* names them (`site show_<n>`, `text show_name_<n>`); pm_game_show(n) plays show n exactly that way - the
|
|
* game's own process body, run as a process of the runtime's (`value show_proc`, an id the game never uses),
|
|
* so its timing, its colours and its clean-up are the game's. One at a time: a new one replaces one still
|
|
* playing. 1 = playing; 0 = refused (only while your mode runs, or in the first 2 s after it ended - its
|
|
* ending's show; in a game; no such show; the reason in mode.log). A show keeps playing after your mode ends (an ending's show is meant to), but never longer than
|
|
* 20 s - some of the game's run until stopped - and the ball ending stops it as it stops every process of the
|
|
* game's. pm_game_show_stop ends it now: the game's own exit hook takes its lights away. pm_game_show_playing:
|
|
* 1 while one plays. pm_game_shows: how many the port names (0 = none on this game). */
|
|
#define PM_CAN_GAME_SHOWS 0x800000u /* pm_game_show / pm_game_show_stop / pm_game_show_playing / pm_game_shows */
|
|
int pm_game_show(int n);
|
|
int pm_game_show_named(const char *name); /* the same by the port's name for it (`text show_name_<n>`, any case) */
|
|
void pm_game_show_stop(void);
|
|
int pm_game_show_playing(void);
|
|
int pm_game_shows(void);
|
|
|
|
/* ---- the shaker (PAD-414, PAD-474; MODE_SDK.md "The shaker") ---------------------------------------
|
|
* Godzilla Premium/LE can have a shaker motor in the cabinet (optional on a Premium, fitted on an LE), and so
|
|
* can every other Spike 2 title with one (PAD-474: the port's shaker lines, read by shaker_lines.py).
|
|
* pm_shake shakes it for `ms` at a `strength` through the game's own shake call - the one every shake of the
|
|
* game's goes through - so the operator's SHAKER MOTOR setting always applies (off = nothing; a lower
|
|
* setting cuts every shake shorter) and the board stops each shake by itself when its time is up. (Where the
|
|
* game's call takes a kind of shake, not a time - Aerosmith, Guardians, Elvira, Sword of Rage - the runtime
|
|
* sends the shake the way that call sends its own: the setting first, then one timed command at its power.)
|
|
* `strength` is one the game uses: PM_SHAKE_HARD (0, its battle hits and jackpots) .. PM_SHAKE_SOFT (3,
|
|
* its long rumbles); most titles shake at one strength only, PM_SHAKE_HARD. The limits are the runtime's and
|
|
* cannot be raised:
|
|
* - `ms` is at least 100 and never longer than the game's own longest shake at that strength (Godzilla:
|
|
* 1000 ms at 0 and 1, 5000 ms at 2 and 3; the port's `text shake_max_ms`).
|
|
* - only while your mode runs, in a game (not attract, not tilted); never with the shaker set to off (or
|
|
* none fitted), while one of the game's own shakes runs, or while one of yours does; at most 20 shakes
|
|
* and 15 s of shaking in any minute.
|
|
* - the runtime stops your shake when your mode ends, the ball ends, or the game ends or tilts (and on
|
|
* pm_shake_stop) - unless the game has since asked for a shake of its own, which it leaves to run.
|
|
* pm_shake_game plays one of the GAME's own shakes by name, as the program has it (the port's `text
|
|
* shake_<name>` lines): on Godzilla "hit" (200 ms, hard: a battle's shot), "big_hit" (334 ms), "jackpot"
|
|
* (500 ms, hard: every super jackpot), "rumble" (3000 ms, soft: O2 Destroyer), "multiball_start" (five
|
|
* shakes over 4 s: the Godzilla Multiball start); on the other titles the one the game calls most in each
|
|
* length: "tap", "short", "medium", "long", "rumble". Same limits; its steps count as one shake.
|
|
* pm_shake / pm_shake_game: 1 = shaking; 0 = refused (the reason is in mode.log) or no shaker on this
|
|
* game's port (PM_CAN_SHAKER). pm_shaking: 1 while a shake of yours runs. */
|
|
#define PM_CAN_SHAKER 0x1000000u /* pm_shake / pm_shake_game / pm_shake_stop / pm_shaking / pm_shake_outlast */
|
|
#define PM_SHAKE_HARD 0
|
|
#define PM_SHAKE_STRONG 1
|
|
#define PM_SHAKE_MEDIUM 2
|
|
#define PM_SHAKE_SOFT 3
|
|
int pm_shake(unsigned ms, unsigned strength);
|
|
int pm_shake_game(const char *name);
|
|
void pm_shake_stop(void);
|
|
int pm_shaking(void);
|
|
/* A shake as your mode ENDS: call pm_shake or pm_shake_game, then pm_shake_outlast, then end the mode - the
|
|
* end then leaves that one shake to run out (its own length); a ball end, game end or tilt still stops it. */
|
|
void pm_shake_outlast(void);
|
|
|
|
/* ---- the game's own mini-wizards (PAD-436; MODE_SDK.md "The game's own mini-wizards") ---------------------
|
|
* James Bond LE 1.06 has four, each with its insert on the Right ramp: 1 Chaos at Crab Key (Dr. No), 2 Ahoy
|
|
* Mr. Bond (From Russia With Love), 3 Goldfinger's Jackpot, 4 Duel on the Disco Volante (Thunderball). The game
|
|
* lights them when a part (henchman, villain, Q Branch, gadget) is collected in all six films, and the Right
|
|
* ramp starts the one selected. pm_game_wizard hands the player up one of them, the game's own way:
|
|
* PM_WIZARD_LIGHT select it and light it: the game's start shot (the Right ramp) starts it
|
|
* PM_WIZARD_START the same, then the game's own start of it at once - if the game would start one now (none
|
|
* running, nothing of its own in the way: its own check); otherwise it stays lit and the
|
|
* runtime starts it the moment the game would, that ball (then the start shot does)
|
|
* Until the game starts it, it is the only one lit and the one selected (PAD-457): the game's own lighting and
|
|
* its selection shots cannot hand the start shot another; what the game lit itself is lit again once it started.
|
|
* Several handed over start in the order given. It runs the game's mode: its shots, lights, screens, sounds and
|
|
* award, as when the game starts it. No mode of
|
|
* yours has to be running, and one that is keeps running beside it. A wizard the player has played this game
|
|
* plays again. PM_WIZARD_LIT (1): lit for the start shot; PM_WIZARD_STARTED (2); 0: not on this game, no game,
|
|
* no such wizard (mode.log says which). pm_game_wizard_named: the same by the port's name for it (`text
|
|
* wizard_name_<n>`, any case). pm_game_wizards: how many the port names (0 = none on this game).
|
|
* PAD-473, the C++ titles (MODE_SDK.md "The game's own mini-wizards on the other titles"): the port names each one's
|
|
* mode object, and PM_WIZARD_START calls its own START as the game's rules do - at once, or the moment none of the
|
|
* game's own modes is in its way (nor a mode of yours holding them off), that ball (PM_WIZARD_WAITING, 3, until
|
|
* then). PM_WIZARD_LIGHT starts the game's own mode that lights it for a start shot where it has one, and is a start
|
|
* where it has none. The game's own rules still light and start them too. */
|
|
#define PM_CAN_GAME_WIZARDS 0x2000000u /* pm_game_wizard / pm_game_wizard_named / pm_game_wizards */
|
|
#define PM_WIZARD_LIGHT 0
|
|
#define PM_WIZARD_START 1
|
|
#define PM_WIZARD_LIT 1
|
|
#define PM_WIZARD_STARTED 2
|
|
#define PM_WIZARD_WAITING 3 /* PAD-473, the C++ titles: to start the moment nothing of the game's is in its way */
|
|
int pm_game_wizard(int n, int how);
|
|
int pm_game_wizard_named(const char *name, int how);
|
|
int pm_game_wizards(void);
|
|
/* PAD-457: claim one - this card's modes hand it out, so the game's own lighting (a part in all six films) leaves it
|
|
* unlit from now on and it lights only when a mode hands it over; the game still lights the unclaimed ones. A mode
|
|
* that hands one over claims it as it loads (a mode file's `game_wizard`, a blocks mode's init). 1 = claimed, 0 = not
|
|
* on this game, no such wizard, or the port names no `site wizard_light` (then the game lights it too). */
|
|
int pm_game_wizard_claim(int n);
|
|
int pm_game_wizard_claim_named(const char *name);
|
|
/* PAD-503: the game's own check before it starts one of its mini-wizards (Bond: none of them running, no henchman,
|
|
* villain or Q Branch mode, no Victory Laps - the port's `site wizard_way`). 1 = nothing of the game's is in the way,
|
|
* 0 = something is, -1 = no game, or this game's port names no such check. From a tick, shot or event callback (the
|
|
* game's own thread). A blocks mode lit at a shot (Light the mode at) waits on it, as the game's own mini-wizards do. */
|
|
int pm_game_wizard_way(void);
|
|
/* PAD-503: a mode lit at a shot claims it, renewed every tick while it would start there now (the player up's, nothing
|
|
* of its own or the game's in its way; 0 = no claim). A hit on a claimed shot's switch holds the game's own mode starts
|
|
* off (the ones the port's block_start lines hook) while the game handles that switch, so the shot starts the lit mode
|
|
* and not, on James Bond's Right ramp, one of the game's henchman or villain modes. A claim not renewed for 250 ms is
|
|
* gone; one of your modes beginning ends a hold. Shots from switches only (the port's switch lines). 1 = claimed. */
|
|
int pm_lit_claim(uint64_t shots);
|
|
|
|
/* ---- the game's own status bar (PAD-521; MODE_SDK.md "The game's status bar") ------------------------------------
|
|
* James Bond LE shows each of its own modes in a coloured bar under the TV: the mode's name, the bar in the mode's
|
|
* colour, and its clock as a progress wipe draining along it (Goldfinger's Jackpot: gold). pm_status_bar puts a mode
|
|
* of yours there the same way: `words` (shown in the game's own lettering, so capitals), `rgb` (PM_RGB) and
|
|
* `percent` 0-100 of the wipe (-1 = no wipe). Call it again to change any of them (cheap when nothing changed).
|
|
* NULL or "" gives the bar back as it was. The runtime keeps it up while you hold it (a game display that hides it
|
|
* is answered on the next tick) and gives it back by itself when the game is over. 1 = shown; 0 = this game's port
|
|
* names no status bar (pm_can(PM_CAN_STATUS_BAR)), or the game has none yet. */
|
|
#define PM_CAN_STATUS_BAR 0x4000000u /* pm_status_bar */
|
|
int pm_status_bar(const char *words, unsigned rgb, int percent);
|
|
|
|
#endif
|