@lewismoten/gamemaker-sandbox-engine 0.0.0-stage → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +24 -0
  2. package/NOTICE.md +753 -0
  3. package/README.md +65 -2
  4. package/dist/assets/music.worklet-NqOeNo6T.js +464 -0
  5. package/dist/engine.js +5436 -0
  6. package/dist/types/audio/cmfLoad.d.ts +7 -0
  7. package/dist/types/audio/cmfPlayer.d.ts +69 -0
  8. package/dist/types/audio/context.d.ts +12 -0
  9. package/dist/types/audio/engine.d.ts +39 -0
  10. package/dist/types/audio/music.worklet.d.ts +14 -0
  11. package/dist/types/audio/opl.d.ts +37 -0
  12. package/dist/types/audio/output.d.ts +13 -0
  13. package/dist/types/dosPlayer/fileExtensions.d.ts +44 -0
  14. package/dist/types/dosPlayer/patchRules.d.ts +24 -0
  15. package/dist/types/dosPlayer/patchTable.d.ts +80 -0
  16. package/dist/types/engine/character.d.ts +30 -0
  17. package/dist/types/engine/funcKeys.d.ts +33 -0
  18. package/dist/types/engine/game.d.ts +52 -0
  19. package/dist/types/engine/keys.d.ts +17 -0
  20. package/dist/types/engine/machine.d.ts +99 -0
  21. package/dist/types/engine/menus.d.ts +46 -0
  22. package/dist/types/engine/monsters.d.ts +19 -0
  23. package/dist/types/engine/package.d.ts +4 -0
  24. package/dist/types/engine/playGain.d.ts +11 -0
  25. package/dist/types/engine/playerLook.d.ts +26 -0
  26. package/dist/types/engine/recorder.d.ts +60 -0
  27. package/dist/types/engine/render.d.ts +8 -0
  28. package/dist/types/engine/scene.d.ts +156 -0
  29. package/dist/types/engine/scroll.d.ts +16 -0
  30. package/dist/types/engine/showGraphic.d.ts +39 -0
  31. package/dist/types/engine/slowPc.d.ts +62 -0
  32. package/dist/types/engine/touch.d.ts +33 -0
  33. package/dist/types/engine/world.d.ts +215 -0
  34. package/dist/types/formats/binary.d.ts +44 -0
  35. package/dist/types/formats/blocks.d.ts +52 -0
  36. package/dist/types/formats/character.d.ts +65 -0
  37. package/dist/types/formats/cmf.d.ts +33 -0
  38. package/dist/types/formats/fli.d.ts +45 -0
  39. package/dist/types/formats/game.d.ts +72 -0
  40. package/dist/types/formats/gif.d.ts +15 -0
  41. package/dist/types/formats/graphicScripts.d.ts +18 -0
  42. package/dist/types/formats/highscore.d.ts +10 -0
  43. package/dist/types/formats/map.d.ts +24 -0
  44. package/dist/types/formats/monster.d.ts +48 -0
  45. package/dist/types/formats/palette.d.ts +15 -0
  46. package/dist/types/formats/recording.d.ts +7 -0
  47. package/dist/types/formats/sound.d.ts +13 -0
  48. package/dist/types/formats/voc.d.ts +13 -0
  49. package/dist/types/gfx/cp437Glyphs.d.ts +2 -0
  50. package/dist/types/gfx/font8x8.d.ts +2 -0
  51. package/dist/types/gfx/screen.d.ts +46 -0
  52. package/dist/types/input/chords.d.ts +22 -0
  53. package/dist/types/input/deskMouse.d.ts +14 -0
  54. package/dist/types/input/keyboard.d.ts +39 -0
  55. package/dist/types/input/scancodes.d.ts +3 -0
  56. package/dist/types/project/gameFiles.d.ts +49 -0
  57. package/package.json +26 -4
@@ -0,0 +1,4 @@
1
+ export { Machine, GameAborted } from './machine';
2
+ export { runGame, loadGameFile, gameNameOf, type RunOptions } from './game';
3
+ export { GameFiles, normalize, cdName, type FileStream } from '../project/gameFiles';
4
+ export { AudioEngine } from '../audio/engine';
@@ -0,0 +1,11 @@
1
+ export interface PlayGain {
2
+ /** Points from the stretches F6 closed. */
3
+ banked: number;
4
+ /** The score the current stretch started from (0, or the score a load put in place). */
5
+ base: number;
6
+ }
7
+ export declare const newPlayGain: () => PlayGain;
8
+ /** F6 loaded a save: the score went from `before` to the save's `after`. */
9
+ export declare function gainAtLoad(g: PlayGain, before: number, after: number): void;
10
+ /** The points this play scored, ending on `score`. */
11
+ export declare const gainAtEnd: (g: PlayGain, score: number) => number;
@@ -0,0 +1,26 @@
1
+ import { type PlayerPatches } from '../dosPlayer/patchRules';
2
+ type Rgb = [number, number, number];
3
+ type Frame = [number, number, number, number];
4
+ export interface PlayerLook {
5
+ /** A word or line (str.*). */
6
+ text(id: string): string;
7
+ /** A printf format (str.*) filled in as sprintf would. */
8
+ format(id: string, ...args: (number | string)[]): string;
9
+ /** The palette index a screen's color selector (color.select.*) matches in `pal`. */
10
+ color(pal: Uint8Array, select: string): number;
11
+ /** A color's levels (color.rgb.*, color.missing.*). */
12
+ rgb(id: string): Rgb;
13
+ frame(id: string): Frame;
14
+ /** A position or count (layout.*, border.marker.width). */
15
+ num(id: string): number;
16
+ /** A wait in seconds (timing.*); -1 is PauseTimeKey's unsigned 65535 (until a key). */
17
+ seconds(id: string): number;
18
+ /** False when the player's patches turned this F-key off. */
19
+ fkeyOn(key: number): boolean;
20
+ }
21
+ /** sprintf for the player's formats: %d, %ld, %s with a width and the 0 and - flags. */
22
+ export declare function sprintf(format: string, args: (number | string)[]): string;
23
+ export declare function playerLook(patches?: PlayerPatches): PlayerLook;
24
+ /** The CD player's own look. */
25
+ export declare const STOCK_LOOK: PlayerLook;
26
+ export {};
@@ -0,0 +1,60 @@
1
+ import { type RecordedKey } from '../formats/recording';
2
+ export declare const RECORD = 1;
3
+ export declare class Recorder {
4
+ mode: number;
5
+ private keys;
6
+ private index;
7
+ private timer;
8
+ private lastKeyPress;
9
+ private paused;
10
+ initRecord(): void;
11
+ /**
12
+ * Whether waits for a key count toward playback time. PLAYGAME's GetKey called
13
+ * DoPlayBack on every poll, so while a "hit any key" screen waited the timer raced
14
+ * ahead (thousands of polls a second), and a demo recorded in DOS stores those polls
15
+ * as one huge DeltaTime before the key that dismissed the screen (Nebula's 26058).
16
+ * Demos recorded here count game ticks only, so for them waits must not count.
17
+ */
18
+ countsWaits: boolean;
19
+ initPlayback(bytes: Uint8Array | undefined): boolean;
20
+ stop(): void;
21
+ restart(): void;
22
+ pause(): void;
23
+ unpause(): void;
24
+ /** Game ticks counted since the recording or playback (re)started; the playback position. */
25
+ get tick(): number;
26
+ get playing(): boolean;
27
+ /** Paused: play has left the levels (the won/lost pictures and score table follow). */
28
+ get halted(): boolean;
29
+ /** DoPlayBack: advance one tick; returns the keys that become pending now. */
30
+ doPlayback(): number[];
31
+ /**
32
+ * GetKey polls while a screen waits for a key: `polls` calls to DoPlayBack at once.
33
+ * Returns the recorded keys that become pending (only for recordings made in DOS).
34
+ */
35
+ advanceWait(polls: number): number[];
36
+ /**
37
+ * A screen that read keys has finished. In DOS the next key may have come during it
38
+ * (the recording shows a gap of many thousands of polls), however fast that PC polled;
39
+ * catch up to it, and to keys a poll or two behind it (taken by the keyboard flush that
40
+ * follows every screen), so gameplay resumes in step. Only for DOS recordings.
41
+ */
42
+ finishWait(): number[];
43
+ /**
44
+ * DoRecord: called when a key is actually applied to the character (keys held back
45
+ * while a sequence can't be interrupted are recorded on the tick they take effect).
46
+ * DeltaTime is a 16-bit uint in the C, and at most RECLEN keys are kept.
47
+ */
48
+ doRecord(key: number): void;
49
+ /** The recording so far, in .rec format. */
50
+ save(): Uint8Array;
51
+ /** For save games (the C wrote the Recording object and its keys into the .sav). */
52
+ snapshot(): unknown;
53
+ restore(s: {
54
+ mode: number;
55
+ keys: RecordedKey[];
56
+ index: number;
57
+ timer: number;
58
+ lastKeyPress: number;
59
+ }): void;
60
+ }
@@ -0,0 +1,8 @@
1
+ import type { Screen } from '../gfx/screen';
2
+ import { type World } from './world';
3
+ /** CropScreen: the CRTC was reprogrammed to show only 312x196 pixels while playing. */
4
+ export declare const VISIBLE_W = 312;
5
+ export declare const VISIBLE_H = 196;
6
+ /** drawchars' bookkeeping without the drawing: whether top/bottom blocks are shown (used by GetTouchbl). */
7
+ export declare function updateCharDrawState(w: World): void;
8
+ export declare function renderWorld(screen: Screen, w: World): void;
@@ -0,0 +1,156 @@
1
+ import type { Machine } from './machine';
2
+ import type { Recorder } from './recorder';
3
+ import { type PlayerLook } from './playerLook';
4
+ import { type CharInfo, type Coord, type World } from './world';
5
+ import { type SoundSet } from '../formats/sound';
6
+ import { type Game } from '../formats/game';
7
+ import { type PcCosts } from './slowPc';
8
+ import { type PlayGain } from './playGain';
9
+ /** How a play ended: the Game Won scene, no lives left, or Esc (or a scene that could not load). */
10
+ type PlayEnd = 'won' | 'lost' | 'quit';
11
+ /** Everything that lives across scenes (playgame's globals outside a level). */
12
+ export interface Session {
13
+ m: Machine;
14
+ game: Game;
15
+ gameName: string;
16
+ ci: CharInfo;
17
+ /** The `colors` global: the palette of the current scene or menu screen. */
18
+ colors: Uint8Array;
19
+ rec: Recorder;
20
+ sndClock: number;
21
+ timerCounter: number;
22
+ soundOn: boolean;
23
+ musicOn: boolean;
24
+ /** Use the PC speaker even when a .voc name is given (no Sound Blaster). */
25
+ speakerOnly: boolean;
26
+ joystick?: () => {
27
+ pos: number;
28
+ buttons: number;
29
+ } | null;
30
+ /** Called every rendered frame with the live world (for HUD overlays, touch controls…). */
31
+ onFrame?: (w: World) => void;
32
+ /** Emulated PC speed; read every tick so it can change while playing. */
33
+ speed: SpeedSetting;
34
+ /** RunOptions.gmHelp: Game-Maker's own GMHELP.TXT, which F1 shows first (PLAYGAME.EXE only). */
35
+ gmHelp?: Uint8Array;
36
+ /** RunOptions.player as the screens use it (engine/playerLook.ts); left out, the CD player's look. */
37
+ look?: PlayerLook;
38
+ /** The random numbers a level's monsters get as it loads (default Math.random; a walkthrough seeds them). */
39
+ rand?: () => () => number;
40
+ /** Told the scene number each time play() enters a scene (before its picture). */
41
+ onScene?: (scene: number) => void;
42
+ /** Not PLAYGAME's: a touch in `scene` changed map cell (x, y), shown as block `bnum` (World.onTouched). */
43
+ onTouched?: (scene: number, x: number, y: number, bnum: number) => void;
44
+ /**
45
+ * Not PLAYGAME's: play ended (won, lost, or left with Esc) with this score, just before the
46
+ * high score table. Never told during a demo's playback. It only listens: nothing changes.
47
+ * `gained` is the points the play scored itself, leaving out those F6 loaded (playGain.ts).
48
+ */
49
+ onPlayEnd?: (score: number, how: PlayEnd, gained: number) => void;
50
+ /** Not PLAYGAME's: the stretches of this play between F6 loads (playGain.ts). */
51
+ gain?: PlayGain;
52
+ /**
53
+ * The last level played. loadscene loads into globals, so a level that leaves its
54
+ * palette, block or character slot blank keeps the previous level's (PLAYGAME.C 4239).
55
+ */
56
+ lastWorld?: World;
57
+ }
58
+ /**
59
+ * How fast the emulated PC is. The game clock always runs at 18.2 Hz, but playgame
60
+ * skips (rather than catches up) ticks its main loop was too slow to handle, and loses
61
+ * the scroll steps of interrupts that went by while it was busy. `slowPc` (see slowPc.ts)
62
+ * charges each pass of the loop what it cost on a slower PC; left out, the PC is fast
63
+ * enough for every pass (a 486 in turbo).
64
+ */
65
+ export interface SpeedSetting {
66
+ slowPc?: PcCosts;
67
+ }
68
+ export declare function sceneFile(game: Game, scene: number, slot: number): string | undefined;
69
+ interface LoadedScene {
70
+ world: World;
71
+ sounds?: SoundSet;
72
+ song?: Uint8Array;
73
+ }
74
+ /**
75
+ * loadscene: returns the names of missing files on failure. Scene file slots are
76
+ * 0 palette, 1 map, 2 background blocks, 3 monsters, 4 monster blocks, 5 character,
77
+ * 6 character blocks, 7 sound set, 8 song. In the C a slot left blank keeps whatever
78
+ * the previous scene loaded into the globals; `prev` stands in for those globals.
79
+ */
80
+ export declare function loadScene(s: Pick<Session, 'game' | 'rand' | 'onTouched'> & {
81
+ m: {
82
+ files: {
83
+ get(name: string): Uint8Array | undefined;
84
+ };
85
+ };
86
+ }, scnum: number, prev?: World): LoadedScene | string[];
87
+ /** StartGameInits */
88
+ export declare function startGameInits(s: Session): void;
89
+ /** StartSceneInits */
90
+ export declare function startSceneInits(w: World, start: Coord): void;
91
+ /**
92
+ * keydn[CHRSEQ.key] = 1 at the end of the C's scene inits (PLAYGAME.C). keydn is a 128 byte
93
+ * array indexed by scan code, but a sequence's key is the whole 16 bit word (scan code in the
94
+ * high byte, ASCII in the low byte, CHAR.H), so the C only set a keydn entry when the key had
95
+ * no scan code: key 0 (no key, which every idle sequence on the CD has) sets keydn[0], and
96
+ * the idle sequence's "quit if key lifted" test (keydn[key>>8]==0) then reads it as held.
97
+ * For a real key the write landed key bytes past keydn (DGROUP:4C4E in PLAYGAME.MAP), never
98
+ * on another keydn entry: scan 1..4 in dirtrect.c's DirtyBoxes, scan 5 in the next modules'
99
+ * buffers, scan 5..0x48 in gifc.c's decoder tables, 0x49..0x4A in the atexit table and higher
100
+ * in the far end of the 15000 byte stack. Those are all written before they are read, so the
101
+ * stray byte had no effect: the key does not read as held (keydn[scan] stays 0).
102
+ */
103
+ export declare function setStartKey(keydn: Uint8Array, key: number): void;
104
+ /** changeblks: animate background blocks. */
105
+ export declare function changeBlocks(w: World): void;
106
+ interface SceneResult {
107
+ ok: boolean;
108
+ link: number;
109
+ /**
110
+ * The level played at the end and the one before it: another level's after F6 loaded a game
111
+ * saved there (LoadGame sets play's curscene and oldscene, globals in the C).
112
+ */
113
+ doscene?: number;
114
+ prevscene?: number;
115
+ }
116
+ /** What F5 writes to game<n>.sav (this port's JSON in place of SaveGame's binary records). */
117
+ export interface Saved {
118
+ version: 1;
119
+ prevscene: number;
120
+ doscene: number;
121
+ linkin: number;
122
+ sndClock: number;
123
+ world: Pick<World, 'nxtchg' | 'ci' | 'mx' | 'my' | 'mi' | 'mm' | 'noMons' | 'blkMon'> & {
124
+ blkmap: number[];
125
+ map: number[];
126
+ mapMon: number[];
127
+ chr: {
128
+ x: number[];
129
+ y: number[];
130
+ cframe: number;
131
+ cseq: number;
132
+ };
133
+ };
134
+ rec: ReturnType<Recorder['snapshot']>;
135
+ }
136
+ /** playscene: plays one level. link in/out follows the original's conventions. */
137
+ export declare function playScene(s: Session, dosc: number, prev: number, linkIn: number): Promise<SceneResult>;
138
+ /** The save of a game at this moment (F5; project/saveData's newGameSave makes one at the start). */
139
+ export declare function savedState(w: World, where: {
140
+ prevscene: number;
141
+ doscene: number;
142
+ linkin: number;
143
+ }, sndClock: number, rec: Pick<Recorder, 'snapshot'>): Saved;
144
+ /**
145
+ * F6's LoadGame once the save is read: the saved level loaded fresh, then the saved state put
146
+ * over it (also how project/saveData's tests check a save it made). Undefined when the
147
+ * level's files are missing.
148
+ */
149
+ export declare function restoreSaved(s: Pick<Session, 'game' | 'rand' | 'ci' | 'rec' | 'sndClock' | 'onTouched'> & {
150
+ m: {
151
+ files: {
152
+ get(name: string): Uint8Array | undefined;
153
+ };
154
+ };
155
+ }, w: World | undefined, data: Saved): LoadedScene | undefined;
156
+ export {};
@@ -0,0 +1,16 @@
1
+ import { type World } from './world';
2
+ /**
3
+ * Scene setup: Scroll.up/down/left/right are stored mirrored in the .gam file (the
4
+ * flags here name the way the map moves, e.g. `left` lets the map slide left as the
5
+ * character walks right). 0xff means that direction is allowed.
6
+ */
7
+ export declare function initScroll(w: World, s: {
8
+ up: number;
9
+ down: number;
10
+ left: number;
11
+ right: number;
12
+ }): void;
13
+ /** ChangeScroll: decide scrolling speed from how far the character is from center. */
14
+ export declare function changeScroll(w: World): number;
15
+ /** DrawScroll: one 1/145.6 s scroll step. Returns how many new 4 pixel edge strips it took. */
16
+ export declare function drawScroll(w: World): number;
@@ -0,0 +1,39 @@
1
+ import type { Machine } from './machine';
2
+ import { type PlayerLook } from './playerLook';
3
+ export declare const RGB_RED: [number, number, number];
4
+ export declare const RGB_BLUE: [number, number, number];
5
+ export declare const RGB_YELLOW: [number, number, number];
6
+ export declare const RGB_PURPLE: [number, number, number];
7
+ export declare const match: (pal: Uint8Array, c: [number, number, number]) => number;
8
+ interface GraphicOptions {
9
+ /** Receives the palette of the shown picture (the NewCols argument). */
10
+ cols?: Uint8Array;
11
+ /** Seconds to wait for a key; -1 = the default for the file type. */
12
+ pause?: number;
13
+ /** The game's current palette, used to pick text colors (`colors` global). */
14
+ gamePalette: Uint8Array;
15
+ /** The player's look (RunOptions.player): text page colors, default times. */
16
+ look?: PlayerLook;
17
+ }
18
+ /** Returns false when the player pressed Esc (which aborts slide shows). */
19
+ export declare function showGraphic(m: Machine, file: string, opts: GraphicOptions): Promise<boolean>;
20
+ /**
21
+ * FLI::View: every frame once, `speed` vertical retraces (1/70 s) apart. The C drew
22
+ * straight onto the VGA screen, so the animation starts over whatever is shown.
23
+ *
24
+ * A speed of 0 waits no retrace at all: the C's delay loop is
25
+ * for (uint Delay=0;Delay<Head.FrameSpd;Delay++) { wait out retrace; wait for retrace; }
26
+ * so with FrameSpd 0 the next frame was decoded into video memory straight away, as fast
27
+ * as the PC could read and draw it, and the monitor showed whatever was in video memory at
28
+ * each refresh (frames in between were never seen). The port does the same: it draws the
29
+ * next frame at once, only yielding to the event loop (no frame wait) so the page stays
30
+ * responsive, and each browser repaint shows the frame that is current then. Speed 0 FLIs
31
+ * on the CD (black.fli, menubkg.fli, textbkg.fli, ...) are one or two frame backgrounds.
32
+ */
33
+ export declare function playFli(m: Machine, file: string): Promise<boolean>;
34
+ /**
35
+ * Display_File: text pages over the current picture, 16 lines (rows 5..20 of 8 pixels)
36
+ * per page. fgets(…, 32) read at most 31 characters at a time, so long lines wrap.
37
+ */
38
+ export declare function displayFile(m: Machine, file: string, col: number, pause: number, gamePalette: Uint8Array, bytes?: Uint8Array<ArrayBufferLike> | undefined, look?: PlayerLook): Promise<boolean>;
39
+ export {};
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The emulated slow PC behind the desk's TURBO button (turbo off).
3
+ *
4
+ * A 486DX2-66's turbo switch did not make it a 286. On most boards turbo off turned off
5
+ * the CPU cache (some also dropped the bus or core clock), which left the machine at
6
+ * roughly 286-16 to 386-25 speed, depending on the board. So turbo off here is "about a
7
+ * fast 286": the PC Lewis played these games on, where they ran fine.
8
+ *
9
+ * Why they ran fine: playgame's main loop (PLAYGAME.C playscene) does two kinds of work.
10
+ * - Once per NewTimer interrupt (8 per game tick, 145.6 Hz), DrawScroll while the view
11
+ * scrolls. On a VGA/SVGA card this is hardware scrolling: it moves the CRTC start
12
+ * address, draws only the new 4 pixel strips at the edge (drawhorizside/
13
+ * drawvertside: 320x4 or 4x200 pixels each) and erases/redraws the character. It never
14
+ * redraws the whole screen.
15
+ * - Once per 18.2 Hz game tick: move and redraw the character and the monsters on screen,
16
+ * animate blocks, copy the dirty rectangles to the screen, check links.
17
+ * On a 286-class PC a still tick costs about 15 ms plus a millisecond or so per monster on
18
+ * screen, well under the 55 ms a tick lasts, so the game keeps full pace. Scrolling only
19
+ * runs once per pass of the loop, and the pass that runs the game tick takes several
20
+ * interrupts' worth of time; DrawScroll is called with a step of 1 however many
21
+ * interrupts went by (its `speed` argument is commented out), so those scroll steps are
22
+ * simply lost and the view trails the character a little more. Only a crowded screen
23
+ * that is also scrolling can take longer than a tick, and then playgame skips the missed
24
+ * tick (OldClock jumps to TimerCounter) instead of catching up.
25
+ *
26
+ * The figures below are estimates for that machine, in milliseconds. They are kept
27
+ * deliberately simple and deterministic (no wall clock): the same keys give the same
28
+ * game, and recordings replay the same way.
29
+ */
30
+ export interface PcCosts {
31
+ /** A game tick with nothing on screen but the character. */
32
+ tickMs: number;
33
+ /** Each monster drawn on screen that tick (move, erase, masked draw, dirty rect). */
34
+ monsterMs: number;
35
+ /** One DrawScroll call (Bresenham step, CRTC update, character erase/redraw). */
36
+ scrollPassMs: number;
37
+ /** Each new 4 pixel edge strip DrawScroll draws and copies to the screen. */
38
+ stripMs: number;
39
+ }
40
+ /** A 486DX2-66 with turbo off: about a 286-16. */
41
+ export declare const TURBO_OFF: PcCosts;
42
+ /**
43
+ * Keeps track of the time the slow PC still owes on work it started, counted in NewTimer
44
+ * interrupts. While it owes a whole interrupt or more, that loop pass never happens.
45
+ */
46
+ export declare class SlowCpu {
47
+ private readonly interruptMs;
48
+ /** Milliseconds of work still running. */
49
+ owed: number;
50
+ constructor(interruptMs: number);
51
+ /**
52
+ * Called once per NewTimer interrupt: true when the PC is still busy with earlier work
53
+ * and this interrupt's pass of the main loop (scroll step, game tick) is lost.
54
+ */
55
+ busy(): boolean;
56
+ spend(ms: number): void;
57
+ reset(): void;
58
+ }
59
+ /** What one game tick costs with `monsters` drawn on screen. */
60
+ export declare function tickCost(pc: PcCosts, monsters: number): number;
61
+ /** What one DrawScroll call costs when it drew `strips` new 4 pixel strips. */
62
+ export declare function scrollCost(pc: PcCosts, strips: number): number;
@@ -0,0 +1,33 @@
1
+ import type { Block } from '../formats/blocks';
2
+ import { type Coord, type TouchBlocks, type World } from './world';
3
+ export declare const KILLSOUND = 1;
4
+ export declare function doSound(w: World, n: number): boolean;
5
+ /** ScrnLowBnd/ScrnUpBnd ({1,1} to {SIZEX-BLEN,SIZEY-BLEN}): where a 20x20 sprite is considered on screen. */
6
+ export declare function inScreenBounds(p: Coord): boolean;
7
+ /**
8
+ * onscrn: screen position of map block (x, y); true if inside the screen bounds.
9
+ * Cells left of / above the scroll origin (mx, my) are taken as wrapped around the map.
10
+ */
11
+ export declare function onscrn(w: World, x: number, y: number, ret: Coord): boolean;
12
+ /**
13
+ * GetTouchbl: the up to 6 map cells the (two block high) character overlaps:
14
+ * 0/1 the top row (only while the top half is drawn), 2/3 its own row, 4/5 the
15
+ * row below, left then right. The right/lower cells count only when the pixel
16
+ * offset is past 0. Unused cells keep x = MLEN and BACKBL; blknum is the
17
+ * displayed variant (blkmap) of the map block.
18
+ */
19
+ export declare function getTouchBlocks(w: World): TouchBlocks;
20
+ /** checkseq_fortie: turns on/off every sequence tied to `block` (picked up or dropped). */
21
+ export declare function checkSeqForTie(w: World, block: number, setting: boolean): void;
22
+ /**
23
+ * DoBlockTouchChar: applies a block's (or monster block's) effects to the character.
24
+ * Returns 0 when blocked by an empty counter, otherwise (sound played ? 2 : 1).
25
+ * Effect 1 is lives and 3 is money; effects 3 and up are counters (ci.meter), and
26
+ * 3 or 20 and up refuse the touch when they would leave the counter at 0 or less.
27
+ * i16/u16 keep the C's 16-bit int wrap and its unsigned overflow checks.
28
+ */
29
+ export declare function doBlockTouchChar(w: World, blk: Block): number;
30
+ /** really_checktouch: 2 = touch refused (counter empty), else true. x === MLEN is an unused touch cell. */
31
+ export declare function reallyCheckTouch(w: World, x: number, y: number, bnum: number): number;
32
+ /** CheckTouch: touches every overlapped cell except objects, which only PickupBloc takes. */
33
+ export declare function checkTouch(w: World): void;
@@ -0,0 +1,215 @@
1
+ import { BACKBL, BLEN, CHARBL, MONBL, type Block } from '../formats/blocks';
2
+ import type { Character } from '../formats/character';
3
+ import { INF_REPEAT, MAXINV, MAXSEQ } from '../formats/character';
4
+ import { LASTMON, type Monster } from '../formats/monster';
5
+ import { LASTMM, MLEN, MWID, type GameMap } from '../formats/map';
6
+ import { LASTSND } from '../formats/sound';
7
+ export { BLEN, BACKBL, MONBL, CHARBL, LASTMM, LASTMON, MLEN, MWID, MAXSEQ, MAXINV, INF_REPEAT, LASTSND };
8
+ export declare const SIZEX = 320;
9
+ export declare const SIZEY = 200;
10
+ export declare const MAXKEYS = 20;
11
+ export declare const MAXGRAV = 11;
12
+ export declare const SCRNBLEN = 16;
13
+ export declare const SCRNBWID = 10;
14
+ export declare const GRAVSPEED = 1;
15
+ export declare const MONMOVESPEED = 1;
16
+ export declare const JSTICKKEY = 1;
17
+ export declare const ATTACKX = 8;
18
+ export declare const ATTACKY = 5;
19
+ export declare const OFFSCRN = -100;
20
+ export declare const SCRSPDX = 40;
21
+ export declare const SCRSPDY = 40;
22
+ export declare const SCROLLPERMOVE = 6;
23
+ export declare const MAXMONSSHOWN = 50;
24
+ export declare const CHARPOWER = 100;
25
+ export declare const NEVER = 4294967295;
26
+ export interface Coord {
27
+ x: number;
28
+ y: number;
29
+ }
30
+ /** cinfo: the player's state that persists across scenes. */
31
+ export interface CharInfo {
32
+ score: number;
33
+ update: boolean;
34
+ topthere: boolean;
35
+ bottomthere: boolean;
36
+ curtop: number;
37
+ curbottom: number;
38
+ /** Where the character will be on screen next; scrx/scry where it is drawn; oldx/oldy where it is erased. */
39
+ nextx: number;
40
+ nexty: number;
41
+ scrx: number;
42
+ scry: number;
43
+ oldx: number;
44
+ oldy: number;
45
+ /** Delta of the character's last 2 moves. */
46
+ netx: number;
47
+ nety: number;
48
+ /** Current velocity due to gravity. */
49
+ gravx: number;
50
+ gravy: number;
51
+ hitpts: number;
52
+ /** Lives left; -1 before the first scene sets it from the character's meter. */
53
+ lives: number;
54
+ /** Game clock tick at which the character next moves. */
55
+ nextAni: number;
56
+ /** Inventory block numbers (BACKBL+1 in inv[0] = not filled in yet). */
57
+ inv: number[];
58
+ /** Repetition counters, MAXSEQ + 10 of them; meter[0] is hit points, meter[1] lives. */
59
+ meter: number[];
60
+ seqon: boolean[];
61
+ }
62
+ /** minfo: one live monster. */
63
+ export interface MonInfo {
64
+ /** Born from the character, so it can't hurt the character. */
65
+ fromchar: number;
66
+ /** Bresenham's algorithm: dx = (curpos - dest). */
67
+ delta: Coord;
68
+ /** [0] map block, [1] pixel position within the block. */
69
+ curx: [number, number];
70
+ cury: [number, number];
71
+ /** Where the monster is going when on a path. */
72
+ target: Coord;
73
+ /** xy ratios in Bresenham's line algorithm. */
74
+ offl: number;
75
+ offs: number;
76
+ /** 0 normal, 1 attack pattern. */
77
+ curMarch: number;
78
+ /** Index into mon[].pic. */
79
+ curPic: number;
80
+ /** How to flip and rotate the picture. */
81
+ orient: number;
82
+ picDrawn: boolean;
83
+ scrPos: Coord;
84
+ oldPos: Coord;
85
+ /** Index of the next position in the monster's path. */
86
+ dest: number;
87
+ /** Game clock ticks of the next animation frame, next move, and when it was born. */
88
+ nextAni: number;
89
+ nextMove: number;
90
+ birthTime: number;
91
+ }
92
+ /** mm[]: monster number per slot (LASTMON = empty). */
93
+ interface MonSlot {
94
+ monnum: number;
95
+ curx: number;
96
+ cury: number;
97
+ }
98
+ interface KeyStackEntry {
99
+ key: number;
100
+ seq: number;
101
+ frame: number;
102
+ }
103
+ export interface BresScroll {
104
+ dscroll: number;
105
+ ctr: number;
106
+ iters: number;
107
+ }
108
+ interface ScrollState {
109
+ /** Pixel offset of the screen edge within the edge block (mx, my). */
110
+ nx: number;
111
+ ny: number;
112
+ /** How far to move the screen this game tick; negative is up or left. */
113
+ totalx: number;
114
+ totaly: number;
115
+ /** Character's offset from the screen center (pixels; the C comment says blocks). */
116
+ distx: number;
117
+ disty: number;
118
+ /** SOLLEF/SOLRIG/SOLTOP/SOLBOT flags of the directions currently scrolling. */
119
+ inMotion: number;
120
+ maxX: number;
121
+ maxY: number;
122
+ minX: number;
123
+ minY: number;
124
+ leftStop: number;
125
+ rightStop: number;
126
+ upStop: number;
127
+ downStop: number;
128
+ /** Pixels waiting to be scrolled; whole 4 pixel steps are taken, 0..3 remain as the pan. */
129
+ movx: number;
130
+ movy: number;
131
+ /** 0..3 pixel hardware pan (an absolute position, not a delta). */
132
+ littleShiftx: number;
133
+ littleShifty: number;
134
+ /** 0xff where the scene allows scrolling that way (mirrored from the .gam file). */
135
+ up: number;
136
+ down: number;
137
+ left: number;
138
+ right: number;
139
+ bx: BresScroll;
140
+ by: BresScroll;
141
+ }
142
+ export interface TouchBlocks {
143
+ blks: [number, number][];
144
+ blknum: number[];
145
+ }
146
+ interface SoundHooks {
147
+ /** DoSound: returns true if a sound was started. */
148
+ play(n: number): boolean;
149
+ }
150
+ export interface World {
151
+ palette: Uint8Array;
152
+ map: Uint8Array;
153
+ mapMon: Uint8Array;
154
+ bg: Block[];
155
+ mblk: Block[];
156
+ cblk: Block[];
157
+ mon: Monster[];
158
+ chr: Character;
159
+ noMons: boolean;
160
+ ci: CharInfo;
161
+ mi: MonInfo[];
162
+ mm: MonSlot[];
163
+ firstEmptyMM: number;
164
+ monsolid: number;
165
+ mmSolid: number;
166
+ blkMon: {
167
+ blnum: number;
168
+ nexttime: number;
169
+ }[];
170
+ /** Per-block "birth this tick" flag (bit 0x4000 of Birthtime in the original). */
171
+ birthFlag: Uint8Array;
172
+ nxtchg: number[];
173
+ blkmap: Uint8Array;
174
+ mx: number;
175
+ my: number;
176
+ scroll: ScrollState;
177
+ clock: number;
178
+ keydn: Uint8Array;
179
+ keystack: KeyStackEntry[];
180
+ keystkptr: number;
181
+ curkey: number;
182
+ gclk: number;
183
+ joyOldTime: number;
184
+ joyOldBut: number;
185
+ sound: SoundHooks;
186
+ rand: () => number;
187
+ /** GetJoyPos: position 0 (center) or 1..8 clockwise from up, buttons bit 0/1. */
188
+ joystick?: () => {
189
+ pos: number;
190
+ buttons: number;
191
+ } | null;
192
+ jstickOn: boolean;
193
+ /**
194
+ * Not PLAYGAME's: told when the character's touch did something to map cell (x, y), shown as
195
+ * block `bnum` (it changed into its touch block, or was picked up). The web's tin heart
196
+ * tracking listens here; nothing in play reads it.
197
+ */
198
+ onTouched?: (x: number, y: number, bnum: number) => void;
199
+ }
200
+ export declare function newCharInfo(): CharInfo;
201
+ /** Pads a block list with empty blocks so out-of-range numbers are harmless. */
202
+ export declare function padBlocks(list: Block[] | undefined, count: number): Block[];
203
+ export declare function mapBlock(w: World, x: number, y: number): number;
204
+ export declare function mapMonAt(w: World, x: number, y: number): number;
205
+ export declare function setMapMon(w: World, x: number, y: number, v: number): void;
206
+ /** blks[0][blkmap[n]]: the currently displayed variant of background block n. */
207
+ export declare function shownBlock(w: World, n: number): Block;
208
+ export declare function bgBlock(w: World, n: number): Block;
209
+ export declare function createWorld(map: GameMap, sound: SoundHooks, rand?: () => number): World;
210
+ export declare const random: (w: World, n: number) => number;
211
+ export declare const sign: (v: number) => 0 | 1 | -1;
212
+ /** C int (16-bit) wrap. */
213
+ export declare const i16: (v: number) => number;
214
+ /** C unsigned int (16-bit). */
215
+ export declare const u16: (v: number) => number;