sci2-ts 0.2.2 → 0.4.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.
- package/CHANGELOG.md +24 -5
- package/apps/viewer/src/session.js +12 -2
- package/apps/viewer/src/session.ts +12 -2
- package/lib/992.sc +173 -0
- package/lib/993.sc +135 -0
- package/lib/994.sc +13 -0
- package/lib/995.sc +64 -5
- package/lib/996.sc +88 -26
- package/lib/997.sc +26 -0
- package/lib/998.sc +43 -5
- package/lib/999.sc +4 -1
- package/lib/resources.js +33 -3
- package/lib/resources.ts +34 -3
- package/lib/system.sh +38 -1
- package/package.json +1 -1
- package/packages/content/src/room.js +13 -1
- package/packages/content/src/room.ts +11 -1
- package/packages/content/src/target.d.ts +5 -0
- package/packages/content/src/target.ts +5 -0
- package/packages/sci/src/vm/kernels/index.d.ts +1 -1
- package/packages/sci/src/vm/kernels/index.js +1 -1
- package/packages/sci/src/vm/kernels/index.ts +1 -1
- package/packages/sci/src/vm/kernels/saves.d.ts +8 -0
- package/packages/sci/src/vm/kernels/saves.js +24 -3
- package/packages/sci/src/vm/kernels/saves.ts +30 -3
- package/packages/sci/src/vm/kernels/text.js +27 -2
- package/packages/sci/src/vm/kernels/text.ts +23 -2
- package/tools/art/build.d.ts +10 -0
- package/tools/art/build.js +32 -2
- package/tools/art/build.ts +28 -2
- package/tools/art/font.d.ts +24 -0
- package/tools/art/font.js +40 -0
- package/tools/art/font.ts +56 -0
- package/tools/game/build.d.ts +4 -0
- package/tools/game/build.js +27 -1
- package/tools/game/build.ts +24 -1
- package/tools/game/kit.d.ts +1 -0
- package/tools/game/kit.js +1 -0
- package/tools/game/kit.ts +1 -0
- package/tools/game/target.d.ts +1 -1
- package/tools/game/target.js +5 -3
- package/tools/game/target.ts +5 -3
package/lib/996.sc
CHANGED
|
@@ -3,8 +3,25 @@
|
|
|
3
3
|
(script 996)
|
|
4
4
|
(include "system.sh")
|
|
5
5
|
|
|
6
|
+
;; How text boxes and menus look, for the whole game (the `textStyle` global): the font, the
|
|
7
|
+
;; ink, paper and border colours, and a frame drawn around each box instead of the border.
|
|
8
|
+
;; The game sets it in its init, after (super init:):
|
|
9
|
+
;; (textStyle font: 1 fore: 3 back: 12 frame: 260)
|
|
10
|
+
;; A frame is a view whose loop 0 has eight cels, each anchored at its top-left corner: the
|
|
11
|
+
;; corners (top-left, top-right, bottom-left, bottom-right), then the edges (top, bottom, left,
|
|
12
|
+
;; right). The edges are tiled between the corners; transparent pixels show the paper.
|
|
13
|
+
(class TextStyle of Obj
|
|
14
|
+
(properties
|
|
15
|
+
font 0
|
|
16
|
+
fore 0 ; ink
|
|
17
|
+
back 255 ; paper
|
|
18
|
+
border 0 ; a one-pixel border's colour; -1: none
|
|
19
|
+
frame -1 ; a frame view, or -1
|
|
20
|
+
margin 4)) ; between the text and the border or frame
|
|
21
|
+
|
|
6
22
|
;; A box of text over the room: drawn by the interpreter into a bitmap, shown as a screen
|
|
7
|
-
;; item in the UI plane. Set text (and
|
|
23
|
+
;; item in the UI plane. Set text (and width, x, y), then init; it's as tall as the text
|
|
24
|
+
;; unless height is set. Font, colours and frame come from the textStyle unless set here.
|
|
8
25
|
(class TextItem of Obj
|
|
9
26
|
(properties
|
|
10
27
|
x 0 y 0 z 0
|
|
@@ -14,36 +31,66 @@
|
|
|
14
31
|
bitmap 0
|
|
15
32
|
scaleSignal 0 scaleX 128 scaleY 128
|
|
16
33
|
text 0
|
|
17
|
-
font
|
|
18
|
-
fore
|
|
19
|
-
back
|
|
34
|
+
font -1 ; -1 here and below: the textStyle's
|
|
35
|
+
fore -1
|
|
36
|
+
back -1
|
|
20
37
|
skip 254
|
|
21
38
|
mode 0 ; 0 left, 1 centred
|
|
22
|
-
borderColor
|
|
39
|
+
borderColor -2 ; -1: none
|
|
40
|
+
frame -2 ; -1: none
|
|
23
41
|
textLeft 0 textTop 0 textRight -1 textBottom -1
|
|
24
42
|
width 200
|
|
25
43
|
height 0)
|
|
26
44
|
|
|
27
|
-
(method (init &tmp r)
|
|
28
|
-
|
|
29
|
-
(
|
|
30
|
-
(
|
|
31
|
-
(
|
|
32
|
-
(
|
|
33
|
-
|
|
34
|
-
(=
|
|
35
|
-
(=
|
|
36
|
-
(
|
|
45
|
+
(method (init &tmp r m padL padT padR padB)
|
|
46
|
+
(if (== font -1) (= font (textStyle font?)))
|
|
47
|
+
(if (== fore -1) (= fore (textStyle fore?)))
|
|
48
|
+
(if (== back -1) (= back (textStyle back?)))
|
|
49
|
+
(if (== frame -2) (= frame (textStyle frame?)))
|
|
50
|
+
(if (== borderColor -2) (= borderColor (if (!= frame -1) -1 else (textStyle border?))))
|
|
51
|
+
;; As tall as the text needs, inside the margin and the frame.
|
|
52
|
+
(= m (textStyle margin?))
|
|
53
|
+
(= padL m) (= padT m) (= padR m) (= padB m)
|
|
54
|
+
(if (!= frame -1)
|
|
55
|
+
(+= padL (CelWide frame 0 6))
|
|
56
|
+
(+= padR (CelWide frame 0 7))
|
|
57
|
+
(+= padT (CelHigh frame 0 4))
|
|
58
|
+
(+= padB (CelHigh frame 0 5)))
|
|
59
|
+
(if (not height)
|
|
60
|
+
(= r (Array ARRAY_NEW 4 0))
|
|
61
|
+
(TextSize r text font (- width (+ padL padR)))
|
|
62
|
+
(= height (+ (Array ARRAY_AT r 3) 1 padT padB))
|
|
63
|
+
(Array ARRAY_FREE r))
|
|
64
|
+
(= textLeft padL)
|
|
65
|
+
(= textTop padT)
|
|
66
|
+
(= textRight (- width (+ padR 1)))
|
|
67
|
+
(= textBottom (- height (+ padB 1)))
|
|
37
68
|
(= plane uiPlane)
|
|
38
69
|
(= bitmap (CreateTextBitmap 0 width height self))
|
|
70
|
+
(if (!= frame -1) (self drawFrame:))
|
|
39
71
|
(AddScreenItem self))
|
|
40
72
|
|
|
73
|
+
;; The edges, tiled, then the corners over their ends.
|
|
74
|
+
(method (drawFrame &tmp i step)
|
|
75
|
+
(if (< (= step (CelWide frame 0 4)) 1) (= step 1))
|
|
76
|
+
(for ((= i (CelWide frame 0 0))) (< i width) ((+= i step))
|
|
77
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 4 i 0)
|
|
78
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 5 i (- height (CelHigh frame 0 5))))
|
|
79
|
+
(if (< (= step (CelHigh frame 0 6)) 1) (= step 1))
|
|
80
|
+
(for ((= i (CelHigh frame 0 0))) (< i height) ((+= i step))
|
|
81
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 6 0 i)
|
|
82
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 7 (- width (CelWide frame 0 7)) i))
|
|
83
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 0 0 0)
|
|
84
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 1 (- width (CelWide frame 0 1)) 0)
|
|
85
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 2 0 (- height (CelHigh frame 0 2)))
|
|
86
|
+
(Bitmap BITMAP_DRAW_VIEW bitmap frame 0 3 (- width (CelWide frame 0 3)) (- height (CelHigh frame 0 3))))
|
|
87
|
+
|
|
41
88
|
(method (onMe theX theY)
|
|
42
89
|
(return (and (>= theX x) (< theX (+ x width)) (>= theY y) (< theY (+ y height)))))
|
|
43
90
|
|
|
44
91
|
(method (dispose)
|
|
45
92
|
(DeleteScreenItem self)
|
|
46
|
-
(if bitmap (Bitmap
|
|
93
|
+
(if bitmap (Bitmap BITMAP_DISPOSE bitmap) (= bitmap 0))
|
|
47
94
|
(super dispose:)))
|
|
48
95
|
|
|
49
96
|
;; Shows a line until it's been there long enough to read, or the player clicks; then
|
|
@@ -53,7 +100,7 @@
|
|
|
53
100
|
caller 0
|
|
54
101
|
box 0
|
|
55
102
|
until 0 ; the game time it goes
|
|
56
|
-
font
|
|
103
|
+
font -1 ; -1: the textStyle's
|
|
57
104
|
x -1 ; -1: centred
|
|
58
105
|
y 16
|
|
59
106
|
width 220)
|
|
@@ -106,14 +153,16 @@
|
|
|
106
153
|
(= line (String 11 "%s: %s" name txt))
|
|
107
154
|
(super say: line &rest whoCares)))
|
|
108
155
|
|
|
109
|
-
;; Someone with a portrait: a view whose loop 0 is the bust, loop 1 the mouth
|
|
110
|
-
;;
|
|
111
|
-
;;
|
|
112
|
-
;;
|
|
156
|
+
;; Someone with a portrait: a view whose loop 0 is the bust, loop 1 the mouth and loop 2 the
|
|
157
|
+
;; eyes, every cel the same size and anchored at its top-left corner. The room's talker hands
|
|
158
|
+
;; its parts to init: mouth bust eyes frame, where frame is drawn first (the bust), placed
|
|
159
|
+
;; where the portrait goes (the top left of the screen). The text goes to its right. While
|
|
160
|
+
;; the line is said the mouth moves, then closes; the eyes blink.
|
|
113
161
|
(class PortraitTalker of Talker
|
|
114
162
|
(properties
|
|
115
163
|
mouth 0 bust 0 eyes 0 frame 0
|
|
116
|
-
priority 150
|
|
164
|
+
priority 150
|
|
165
|
+
mouthUntil 0) ; the game time the mouth stops
|
|
117
166
|
|
|
118
167
|
(method (init theMouth theBust theEyes theFrame)
|
|
119
168
|
(= mouth theMouth)
|
|
@@ -125,15 +174,24 @@
|
|
|
125
174
|
(self init:)
|
|
126
175
|
(= portrait (if frame frame else bust))
|
|
127
176
|
(if portrait
|
|
128
|
-
(= x (+ (portrait x?) (
|
|
129
|
-
(= y (
|
|
130
|
-
(= width (- (
|
|
177
|
+
(= x (+ (portrait x?) (CelWide (portrait view?) (portrait loop?) (portrait cel?)) 6))
|
|
178
|
+
(= y (portrait y?))
|
|
179
|
+
(= width (- SCREEN_WIDTH (+ x 6))))
|
|
131
180
|
(super say: txt &rest whoCares)
|
|
132
181
|
(self showPart: frame priority)
|
|
133
182
|
(self showPart: bust priority)
|
|
134
183
|
(self showPart: eyes (+ priority 1))
|
|
135
184
|
(self showPart: mouth (+ priority 1))
|
|
136
|
-
(if
|
|
185
|
+
(if eyes (eyes setCycle: Blink))
|
|
186
|
+
(if mouth
|
|
187
|
+
(mouth setCycle: Forward)
|
|
188
|
+
;; About as long as it takes to say: half a second, and two cycles a letter.
|
|
189
|
+
(= mouthUntil (+ gameTime 30 (* 2 (String STRING_LENGTH txt))))))
|
|
190
|
+
|
|
191
|
+
(method (doit)
|
|
192
|
+
(if (and mouth (mouth cycler?) (>= gameTime mouthUntil))
|
|
193
|
+
(mouth setCycle: 0 setCel: 0))
|
|
194
|
+
(super doit:))
|
|
137
195
|
|
|
138
196
|
(method (showPart part pri)
|
|
139
197
|
(if part
|
|
@@ -232,6 +290,10 @@
|
|
|
232
290
|
(c choose: v))
|
|
233
291
|
(return TRUE))
|
|
234
292
|
|
|
293
|
+
;; Closed without a choice.
|
|
294
|
+
(method (dismiss)
|
|
295
|
+
(self dispose:))
|
|
296
|
+
|
|
235
297
|
(method (dispose)
|
|
236
298
|
(if (== dialog self) (= dialog 0))
|
|
237
299
|
(if items
|
package/lib/997.sc
CHANGED
|
@@ -169,6 +169,32 @@
|
|
|
169
169
|
else
|
|
170
170
|
(client cel: c))))
|
|
171
171
|
|
|
172
|
+
;; Eyes: cel 0 (open) most of the time; every two or three seconds, the other cels in turn
|
|
173
|
+
;; (shut) and back to 0.
|
|
174
|
+
(class Blink of Cycle
|
|
175
|
+
(properties
|
|
176
|
+
nextBlink 0)
|
|
177
|
+
|
|
178
|
+
(method (init who)
|
|
179
|
+
(super init: who)
|
|
180
|
+
(self rest:))
|
|
181
|
+
|
|
182
|
+
(method (rest)
|
|
183
|
+
(= nextBlink (+ gameTime 90 (Random 0 90))))
|
|
184
|
+
|
|
185
|
+
(method (doit &tmp c)
|
|
186
|
+
(if (== (client cel?) 0)
|
|
187
|
+
(if (and (>= gameTime nextBlink) (> (self lastCel:) 0))
|
|
188
|
+
(= cycleCnt gameTime)
|
|
189
|
+
(client cel: 1))
|
|
190
|
+
else
|
|
191
|
+
(= c (self nextCel:))
|
|
192
|
+
(if (> c (self lastCel:))
|
|
193
|
+
(client cel: 0)
|
|
194
|
+
(self rest:)
|
|
195
|
+
else
|
|
196
|
+
(client cel: c)))))
|
|
197
|
+
|
|
172
198
|
;; Sizes its client by how far up the screen it stands: frontSize percent at frontY,
|
|
173
199
|
;; backSize at backY, in between in proportion.
|
|
174
200
|
(class Scaler of Obj
|
package/lib/998.sc
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
;; The game: sets everything up, then runs one cycle after another until `quit`.
|
|
21
21
|
(class Game of Obj
|
|
22
22
|
(properties
|
|
23
|
-
script 0
|
|
23
|
+
script 0
|
|
24
|
+
saver 0) ; the SaveRestore behind the game menu
|
|
24
25
|
|
|
25
26
|
(method (init)
|
|
26
27
|
(= game self)
|
|
@@ -30,6 +31,8 @@
|
|
|
30
31
|
(= theEvent (Event new:))
|
|
31
32
|
(= messager (Messager new:))
|
|
32
33
|
(= narrator (Narrator new:))
|
|
34
|
+
(= textStyle (TextStyle new:))
|
|
35
|
+
(= inventory (Inventory new:))
|
|
33
36
|
(= music (Sound new:))
|
|
34
37
|
(= sfx (Sound new:))
|
|
35
38
|
(= uiPlane ((Plane new:) priority: 200 picture: -2 yourself:))
|
|
@@ -42,6 +45,38 @@
|
|
|
42
45
|
(while (not quit)
|
|
43
46
|
(self doit:)))
|
|
44
47
|
|
|
48
|
+
;; After a restore (RestoreGame), the interpreter starts the game here, not at play: the
|
|
49
|
+
;; game is as it was saved, but nothing is on screen and nothing is playing. Put the
|
|
50
|
+
;; planes and the cast back and start looping sounds again, then carry on.
|
|
51
|
+
(method (replay)
|
|
52
|
+
(uiPlane init:)
|
|
53
|
+
(if curRoom ((curRoom plane?) init:))
|
|
54
|
+
(cast eachElementDo: #replay)
|
|
55
|
+
(sounds eachElementDo: #replay)
|
|
56
|
+
(user cursor: -1)
|
|
57
|
+
(while (not quit)
|
|
58
|
+
(self doit:)))
|
|
59
|
+
|
|
60
|
+
;; The game menu (Escape): save, restore, start again.
|
|
61
|
+
(method (showMenu)
|
|
62
|
+
(self clearText:)
|
|
63
|
+
((self saveRestore:) showMenu:))
|
|
64
|
+
|
|
65
|
+
(method (save)
|
|
66
|
+
((self saveRestore:) save:))
|
|
67
|
+
|
|
68
|
+
(method (restore)
|
|
69
|
+
((self saveRestore:) restore:))
|
|
70
|
+
|
|
71
|
+
(method (saveRestore)
|
|
72
|
+
(if (not saver) (= saver (SaveRestore new:)))
|
|
73
|
+
(return saver))
|
|
74
|
+
|
|
75
|
+
;; Takes down a line being shown and whatever is open (a menu, the inventory, a close-up).
|
|
76
|
+
(method (clearText)
|
|
77
|
+
(if talking (talking clear:))
|
|
78
|
+
(if dialog (dialog dismiss:)))
|
|
79
|
+
|
|
45
80
|
;; One cycle: scripts, the room, everything on screen, text, drawing, then input.
|
|
46
81
|
(method (doit)
|
|
47
82
|
(++ gameTime)
|
|
@@ -57,8 +92,7 @@
|
|
|
57
92
|
;; Leaves the current room for room n (its script's export 0).
|
|
58
93
|
(method (newRoom n &tmp old)
|
|
59
94
|
(= newRoomNum 0)
|
|
60
|
-
(
|
|
61
|
-
(if dialog (dialog dispose:))
|
|
95
|
+
(self clearText:)
|
|
62
96
|
(if curRoom
|
|
63
97
|
(= old curRoomNum)
|
|
64
98
|
(curRoom dispose:)
|
|
@@ -77,10 +111,10 @@
|
|
|
77
111
|
|
|
78
112
|
;; Clicks do nothing (but dismiss text) until handsOn: for cutscenes.
|
|
79
113
|
(method (handsOff)
|
|
80
|
-
(user canInput: FALSE))
|
|
114
|
+
(user canInput: FALSE showCursor:))
|
|
81
115
|
|
|
82
116
|
(method (handsOn)
|
|
83
|
-
(user canInput: TRUE)))
|
|
117
|
+
(user canInput: TRUE showCursor:)))
|
|
84
118
|
|
|
85
119
|
;; A room: its picture, where you can walk, its exits, and what's said about it.
|
|
86
120
|
(class Room of Obj
|
|
@@ -237,6 +271,10 @@
|
|
|
237
271
|
(method (roomDisposed)
|
|
238
272
|
(self dispose:))
|
|
239
273
|
|
|
274
|
+
;; Back on screen after a restore.
|
|
275
|
+
(method (replay)
|
|
276
|
+
(if (not (& signal SIG_HIDDEN)) (AddScreenItem self)))
|
|
277
|
+
|
|
240
278
|
(method (dispose)
|
|
241
279
|
(if (not (& signal SIG_HIDDEN)) (DeleteScreenItem self))
|
|
242
280
|
(cast delete: self)
|
package/lib/999.sc
CHANGED
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
uiPlane ; the plane over the room, for text
|
|
28
28
|
gameTime ; cycles since the game started
|
|
29
29
|
quit ; set to end the game
|
|
30
|
-
[gameFlags 64]
|
|
30
|
+
[gameFlags 64] ; story flags, a bit each
|
|
31
|
+
textStyle ; how text boxes and menus look (a TextStyle)
|
|
32
|
+
inventory ; what the hero carries (an Inventory)
|
|
33
|
+
theItem) ; the item picked to use, or 0
|
|
31
34
|
|
|
32
35
|
(procedure (SetFlag n)
|
|
33
36
|
(|= [gameFlags (/ n 16)] (<< 1 (mod n 16))))
|
package/lib/resources.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Colour, ResourceType, writeView } from "../tools/game/kit.js";
|
|
2
2
|
/**
|
|
3
|
-
* The class library's own resources: a cursor for each verb, view CURSOR_BASE + verb
|
|
4
|
-
* (system.sh). X is black, W white, . transparent.
|
|
3
|
+
* The class library's own resources: a cursor for each verb, view CURSOR_BASE + verb, and
|
|
4
|
+
* one while the player waits, CURSOR_WAIT (system.sh). X is black, W white, . transparent.
|
|
5
5
|
*/
|
|
6
6
|
const CURSOR_BASE = 990;
|
|
7
|
+
const CURSOR_WAIT = 995;
|
|
7
8
|
const CURSORS = {
|
|
8
9
|
// V_LOOK: an eye.
|
|
9
10
|
1: {
|
|
@@ -70,6 +71,22 @@ const CURSORS = {
|
|
|
70
71
|
"...XXXXXX..",
|
|
71
72
|
],
|
|
72
73
|
},
|
|
74
|
+
// Waiting: an hourglass.
|
|
75
|
+
[CURSOR_WAIT - CURSOR_BASE]: {
|
|
76
|
+
hotspot: "centre",
|
|
77
|
+
art: [
|
|
78
|
+
"XXXXXXXXX",
|
|
79
|
+
"XWWWWWWWX",
|
|
80
|
+
".XWWWWWX.",
|
|
81
|
+
"..XWWWX..",
|
|
82
|
+
"...XWX...",
|
|
83
|
+
"...XWX...",
|
|
84
|
+
"..XWWWX..",
|
|
85
|
+
".XWWWWWX.",
|
|
86
|
+
"XWWWWWWWX",
|
|
87
|
+
"XXXXXXXXX",
|
|
88
|
+
],
|
|
89
|
+
},
|
|
73
90
|
};
|
|
74
91
|
function cursor(art, hotspot) {
|
|
75
92
|
const width = art[0].length, height = art.length;
|
|
@@ -79,6 +96,19 @@ function cursor(art, hotspot) {
|
|
|
79
96
|
const cel = { width, height, displaceX: (width >> 1) - hx, displaceY: height - 1 - hy, skipColor: Colour.Transparent, pixels };
|
|
80
97
|
return writeView({ flags: 1, loops: [{ link: -1, mirror: false, cels: [cel] }], palette: undefined });
|
|
81
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* What a close-up dims the room with (CLOSE_UP_SHADE): the whole screen in colour 253, which
|
|
101
|
+
* CloseUp makes a remap colour that darkens what's under it.
|
|
102
|
+
*/
|
|
103
|
+
const CLOSE_UP_SHADE = 996;
|
|
104
|
+
function shade() {
|
|
105
|
+
const [width, height] = [320, 200];
|
|
106
|
+
const cel = { width, height, displaceX: width >> 1, displaceY: height - 1, skipColor: Colour.Transparent, pixels: new Uint8Array(width * height).fill(253) };
|
|
107
|
+
return writeView({ flags: 1, loops: [{ link: -1, mirror: false, cels: [cel] }], palette: undefined });
|
|
108
|
+
}
|
|
82
109
|
export default function resources() {
|
|
83
|
-
return
|
|
110
|
+
return [
|
|
111
|
+
...Object.entries(CURSORS).map(([verb, c]) => ({ type: ResourceType.View, number: CURSOR_BASE + Number(verb), data: cursor(c.art, c.hotspot) })),
|
|
112
|
+
{ type: ResourceType.View, number: CLOSE_UP_SHADE, data: shade() },
|
|
113
|
+
];
|
|
84
114
|
}
|
package/lib/resources.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Colour, ResourceType, writeView, type ResourceData } from "../tools/game/kit.ts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The class library's own resources: a cursor for each verb, view CURSOR_BASE + verb
|
|
5
|
-
* (system.sh). X is black, W white, . transparent.
|
|
4
|
+
* The class library's own resources: a cursor for each verb, view CURSOR_BASE + verb, and
|
|
5
|
+
* one while the player waits, CURSOR_WAIT (system.sh). X is black, W white, . transparent.
|
|
6
6
|
*/
|
|
7
7
|
const CURSOR_BASE = 990;
|
|
8
|
+
const CURSOR_WAIT = 995;
|
|
8
9
|
|
|
9
10
|
const CURSORS: Record<number, { art: string[]; hotspot: "tip" | "centre" }> = {
|
|
10
11
|
// V_LOOK: an eye.
|
|
@@ -72,6 +73,22 @@ const CURSORS: Record<number, { art: string[]; hotspot: "tip" | "centre" }> = {
|
|
|
72
73
|
"...XXXXXX..",
|
|
73
74
|
],
|
|
74
75
|
},
|
|
76
|
+
// Waiting: an hourglass.
|
|
77
|
+
[CURSOR_WAIT - CURSOR_BASE]: {
|
|
78
|
+
hotspot: "centre",
|
|
79
|
+
art: [
|
|
80
|
+
"XXXXXXXXX",
|
|
81
|
+
"XWWWWWWWX",
|
|
82
|
+
".XWWWWWX.",
|
|
83
|
+
"..XWWWX..",
|
|
84
|
+
"...XWX...",
|
|
85
|
+
"...XWX...",
|
|
86
|
+
"..XWWWX..",
|
|
87
|
+
".XWWWWWX.",
|
|
88
|
+
"XWWWWWWWX",
|
|
89
|
+
"XXXXXXXXX",
|
|
90
|
+
],
|
|
91
|
+
},
|
|
75
92
|
};
|
|
76
93
|
|
|
77
94
|
function cursor(art: string[], hotspot: "tip" | "centre") {
|
|
@@ -83,6 +100,20 @@ function cursor(art: string[], hotspot: "tip" | "centre") {
|
|
|
83
100
|
return writeView({ flags: 1, loops: [{ link: -1, mirror: false, cels: [cel] }], palette: undefined });
|
|
84
101
|
}
|
|
85
102
|
|
|
103
|
+
/**
|
|
104
|
+
* What a close-up dims the room with (CLOSE_UP_SHADE): the whole screen in colour 253, which
|
|
105
|
+
* CloseUp makes a remap colour that darkens what's under it.
|
|
106
|
+
*/
|
|
107
|
+
const CLOSE_UP_SHADE = 996;
|
|
108
|
+
function shade() {
|
|
109
|
+
const [width, height] = [320, 200];
|
|
110
|
+
const cel = { width, height, displaceX: width >> 1, displaceY: height - 1, skipColor: Colour.Transparent, pixels: new Uint8Array(width * height).fill(253) };
|
|
111
|
+
return writeView({ flags: 1, loops: [{ link: -1, mirror: false, cels: [cel] }], palette: undefined });
|
|
112
|
+
}
|
|
113
|
+
|
|
86
114
|
export default function resources(): ResourceData[] {
|
|
87
|
-
return
|
|
115
|
+
return [
|
|
116
|
+
...Object.entries(CURSORS).map(([verb, c]) => ({ type: ResourceType.View, number: CURSOR_BASE + Number(verb), data: cursor(c.art, c.hotspot) })),
|
|
117
|
+
{ type: ResourceType.View, number: CLOSE_UP_SHADE, data: shade() },
|
|
118
|
+
];
|
|
88
119
|
}
|
package/lib/system.sh
CHANGED
|
@@ -8,15 +8,46 @@
|
|
|
8
8
|
V_WALK
|
|
9
9
|
V_DO)
|
|
10
10
|
(define VERB_COUNT 4)
|
|
11
|
+
(define V_ITEM 5) ; using the item picked in the inventory (theItem)
|
|
11
12
|
|
|
12
|
-
;; Cursors:
|
|
13
|
+
;; Cursors: the library's are views CURSOR_BASE + verb, and CURSOR_WAIT while the player
|
|
14
|
+
;; can't act (a game can bring its own: User).
|
|
13
15
|
(define CURSOR_BASE 990)
|
|
16
|
+
(define CURSOR_WAIT 995)
|
|
17
|
+
|
|
18
|
+
;; The inventory window: icons are INV_ICON pixels square, INV_GAP apart.
|
|
19
|
+
(define INV_ICON 24)
|
|
20
|
+
(define INV_GAP 4)
|
|
21
|
+
|
|
22
|
+
;; Close-ups: the room is dimmed by this view, drawn in a remap colour.
|
|
23
|
+
(define CLOSE_UP_SHADE 996)
|
|
24
|
+
(define REMAP_OFF 0)
|
|
25
|
+
(define REMAP_BY_PERCENT 2)
|
|
26
|
+
|
|
27
|
+
;; Saved games: a description is up to SAVE_DESCRIPTION - 1 characters; the menus show the
|
|
28
|
+
;; newest SAVES_SHOWN. What SaveRestore's open menu is for:
|
|
29
|
+
(define SAVE_DESCRIPTION 36)
|
|
30
|
+
(define SAVES_SHOWN 8)
|
|
31
|
+
(define SR_MENU 1)
|
|
32
|
+
(define SR_SAVE 2)
|
|
33
|
+
(define SR_RESTORE 3)
|
|
34
|
+
(define SR_RESTART 4)
|
|
35
|
+
(define SR_NAME 5)
|
|
14
36
|
|
|
15
37
|
;; Event types, as GetEvent reports them.
|
|
16
38
|
(define EV_NULL 0)
|
|
17
39
|
(define EV_MOUSE_DOWN 1)
|
|
18
40
|
(define EV_MOUSE_UP 2)
|
|
19
41
|
(define EV_KEY_DOWN 4)
|
|
42
|
+
|
|
43
|
+
;; Keys (an event's message).
|
|
44
|
+
(define KEY_TAB 9)
|
|
45
|
+
(define KEY_ENTER 13)
|
|
46
|
+
(define KEY_ESCAPE 27)
|
|
47
|
+
(define KEY_F5 $3f00)
|
|
48
|
+
(define KEY_F7 $4100)
|
|
49
|
+
(define KEY_I 73)
|
|
50
|
+
(define KEY_i 105)
|
|
20
51
|
(define EV_ALL $7fff)
|
|
21
52
|
;; The right button arrives as a click with shift held.
|
|
22
53
|
(define MOD_RIGHT 3)
|
|
@@ -36,6 +67,12 @@
|
|
|
36
67
|
(define ARRAY_AT_PUT 3)
|
|
37
68
|
(define ARRAY_FREE 4)
|
|
38
69
|
(define STRING_LENGTH 10)
|
|
70
|
+
(define STRING_FORMAT_INTO 12)
|
|
71
|
+
(define ARRAY_COPY 6)
|
|
72
|
+
|
|
73
|
+
;; Bitmap subfunctions.
|
|
74
|
+
(define BITMAP_DISPOSE 1)
|
|
75
|
+
(define BITMAP_DRAW_VIEW 3)
|
|
39
76
|
|
|
40
77
|
;; DoSound kernel calls.
|
|
41
78
|
(define SND_INIT 6)
|
package/package.json
CHANGED
|
@@ -537,11 +537,23 @@ class RoomCompiler {
|
|
|
537
537
|
? [{ op: "async", code: send({ global: g.sound }, [["number", [n]], ["loop", [1]], ["play", ["self"]]]) }]
|
|
538
538
|
: [{ op: "code", code: send({ global: g.sound }, [["number", [n]], ["loop", [1]], ["play", []]]) }];
|
|
539
539
|
}
|
|
540
|
+
case "closeup": {
|
|
541
|
+
if (args.length < 1 || args.length > 3)
|
|
542
|
+
fail("expected <<closeup view [loop [cel]]>>");
|
|
543
|
+
if (!this.target.closeUp)
|
|
544
|
+
fail("this game has no close-ups");
|
|
545
|
+
const [view, loop = "0", cel = "0"] = args;
|
|
546
|
+
// A new close-up, shown until the player clicks; then it cues the script.
|
|
547
|
+
return [{ op: "async", code: [
|
|
548
|
+
...send({ cls: this.target.closeUp }, [["new", []]]),
|
|
549
|
+
"pushi #show", "pushi 4", ...pushVal(int(view, "view", 0, 65535)), ...pushVal(int(loop, "loop", 0, 255)), ...pushVal(int(cel, "cel", 0, 255)), "pushSelf", "send 12",
|
|
550
|
+
] }];
|
|
551
|
+
}
|
|
540
552
|
case "room":
|
|
541
553
|
arity(1, "number");
|
|
542
554
|
return [{ op: "code", code: send({ global: g.curRoom }, [["newRoom", [int(args[0], "room", 0, 65535)]]]) }];
|
|
543
555
|
default:
|
|
544
|
-
return fail("unknown command (known: walk, face, wait, hide, show, loop, cel, animate, stop, music, sound, room; and set, if)");
|
|
556
|
+
return fail("unknown command (known: walk, face, wait, hide, show, loop, cel, animate, stop, music, sound, closeup, room; and set, if)");
|
|
545
557
|
}
|
|
546
558
|
}
|
|
547
559
|
/** Loop music n, unless it's what's playing already. */
|
|
@@ -636,11 +636,21 @@ class RoomCompiler {
|
|
|
636
636
|
? [{ op: "async", code: send({ global: g.sound }, [["number", [n]], ["loop", [1]], ["play", ["self"]]]) }]
|
|
637
637
|
: [{ op: "code", code: send({ global: g.sound }, [["number", [n]], ["loop", [1]], ["play", []]]) }];
|
|
638
638
|
}
|
|
639
|
+
case "closeup": {
|
|
640
|
+
if (args.length < 1 || args.length > 3) fail("expected <<closeup view [loop [cel]]>>");
|
|
641
|
+
if (!this.target.closeUp) fail("this game has no close-ups");
|
|
642
|
+
const [view, loop = "0", cel = "0"] = args;
|
|
643
|
+
// A new close-up, shown until the player clicks; then it cues the script.
|
|
644
|
+
return [{ op: "async", code: [
|
|
645
|
+
...send({ cls: this.target.closeUp! }, [["new", []]]),
|
|
646
|
+
"pushi #show", "pushi 4", ...pushVal(int(view, "view", 0, 65535)), ...pushVal(int(loop, "loop", 0, 255)), ...pushVal(int(cel, "cel", 0, 255)), "pushSelf", "send 12",
|
|
647
|
+
] }];
|
|
648
|
+
}
|
|
639
649
|
case "room":
|
|
640
650
|
arity(1, "number");
|
|
641
651
|
return [{ op: "code", code: send({ global: g.curRoom }, [["newRoom", [int(args[0], "room", 0, 65535)]]]) }];
|
|
642
652
|
default:
|
|
643
|
-
return fail("unknown command (known: walk, face, wait, hide, show, loop, cel, animate, stop, music, sound, room; and set, if)");
|
|
653
|
+
return fail("unknown command (known: walk, face, wait, hide, show, loop, cel, animate, stop, music, sound, closeup, room; and set, if)");
|
|
644
654
|
}
|
|
645
655
|
}
|
|
646
656
|
|
|
@@ -59,6 +59,11 @@ export interface Target {
|
|
|
59
59
|
};
|
|
60
60
|
/** The class that cycles a prop's cels forever (<<animate x forever>>, cycle: forward). */
|
|
61
61
|
forwardCycle: string;
|
|
62
|
+
/**
|
|
63
|
+
* Close-ups (<<closeup view [loop [cel]]>>), if the target has them: a class whose new
|
|
64
|
+
* instance takes `show: view loop cel caller` and cues the caller when it's dismissed.
|
|
65
|
+
*/
|
|
66
|
+
closeUp?: string;
|
|
62
67
|
/** Verb number for "said on arrival" messages. */
|
|
63
68
|
narrationVerb: number;
|
|
64
69
|
/** Verb names as used in Yarn node titles (lower case) -> verb numbers. */
|
|
@@ -46,6 +46,11 @@ export interface Target {
|
|
|
46
46
|
teller: { class: string; props: Record<string, number>; verb: number };
|
|
47
47
|
/** The class that cycles a prop's cels forever (<<animate x forever>>, cycle: forward). */
|
|
48
48
|
forwardCycle: string;
|
|
49
|
+
/**
|
|
50
|
+
* Close-ups (<<closeup view [loop [cel]]>>), if the target has them: a class whose new
|
|
51
|
+
* instance takes `show: view loop cel caller` and cues the caller when it's dismissed.
|
|
52
|
+
*/
|
|
53
|
+
closeUp?: string;
|
|
49
54
|
/** Verb number for "said on arrival" messages. */
|
|
50
55
|
narrationVerb: number;
|
|
51
56
|
/** Verb names as used in Yarn node titles (lower case) -> verb numbers. */
|
|
@@ -3,6 +3,6 @@ export { graphics, Graphics, type Frame, SCREEN_WIDTH, SCREEN_HEIGHT, ShowStyle,
|
|
|
3
3
|
export { input, Input, EventType, SciKey, hostFiles, type SciEvent } from "./system.ts";
|
|
4
4
|
export { audio, AudioState, readingTime } from "./audio.ts";
|
|
5
5
|
export { paletteEffects, PaletteEffects } from "./palette.ts";
|
|
6
|
-
export { saves, restoreGame, MemorySaveStore, type SaveStore, type SaveInfo } from "./saves.ts";
|
|
6
|
+
export { saves, restoreGame, scriptsFingerprint, MemorySaveStore, type SaveStore, type SaveInfo } from "./saves.ts";
|
|
7
7
|
export declare const allKernels: Record<string, KernelFn>;
|
|
8
8
|
export { stringHelpers } from "./arrays.ts";
|
|
@@ -12,7 +12,7 @@ export { graphics, Graphics, SCREEN_WIDTH, SCREEN_HEIGHT, ShowStyle, frameRgb }
|
|
|
12
12
|
export { input, Input, EventType, SciKey, hostFiles } from "./system.js";
|
|
13
13
|
export { audio, AudioState, readingTime } from "./audio.js";
|
|
14
14
|
export { paletteEffects, PaletteEffects } from "./palette.js";
|
|
15
|
-
export { saves, restoreGame, MemorySaveStore } from "./saves.js";
|
|
15
|
+
export { saves, restoreGame, scriptsFingerprint, MemorySaveStore } from "./saves.js";
|
|
16
16
|
export const allKernels = {
|
|
17
17
|
...coreKernels,
|
|
18
18
|
...mathKernels,
|
|
@@ -14,7 +14,7 @@ export { graphics, Graphics, type Frame, SCREEN_WIDTH, SCREEN_HEIGHT, ShowStyle,
|
|
|
14
14
|
export { input, Input, EventType, SciKey, hostFiles, type SciEvent } from "./system.ts";
|
|
15
15
|
export { audio, AudioState, readingTime } from "./audio.ts";
|
|
16
16
|
export { paletteEffects, PaletteEffects } from "./palette.ts";
|
|
17
|
-
export { saves, restoreGame, MemorySaveStore, type SaveStore, type SaveInfo } from "./saves.ts";
|
|
17
|
+
export { saves, restoreGame, scriptsFingerprint, MemorySaveStore, type SaveStore, type SaveInfo } from "./saves.ts";
|
|
18
18
|
|
|
19
19
|
export const allKernels: Record<string, KernelFn> = {
|
|
20
20
|
...coreKernels,
|
|
@@ -5,6 +5,11 @@ export interface SaveInfo {
|
|
|
5
5
|
description: string;
|
|
6
6
|
version: string;
|
|
7
7
|
date: number;
|
|
8
|
+
/**
|
|
9
|
+
* The scripts it was made with (scriptsFingerprint). A save is a snapshot of running
|
|
10
|
+
* scripts, so it only restores into the same ones; saves from before this was kept have none.
|
|
11
|
+
*/
|
|
12
|
+
scripts?: string;
|
|
8
13
|
}
|
|
9
14
|
/** Where saves live. Synchronous because kernels are; hosts preload and write through. */
|
|
10
15
|
export interface SaveStore {
|
|
@@ -32,7 +37,10 @@ export declare class MemorySaveStore implements SaveStore {
|
|
|
32
37
|
declare class SaveState {
|
|
33
38
|
store: SaveStore;
|
|
34
39
|
restarting: number;
|
|
40
|
+
fingerprint: string | undefined;
|
|
35
41
|
}
|
|
42
|
+
/** A hash (FNV-1a) of every script and heap resource: which scripts a save belongs to. */
|
|
43
|
+
export declare function scriptsFingerprint(vm: Vm): string;
|
|
36
44
|
export declare const saves: (vm: Vm) => SaveState;
|
|
37
45
|
/**
|
|
38
46
|
* Loads a saved game the way SSCI's RestoreGame does: host state (planes, screen items,
|