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.
Files changed (42) hide show
  1. package/CHANGELOG.md +24 -5
  2. package/apps/viewer/src/session.js +12 -2
  3. package/apps/viewer/src/session.ts +12 -2
  4. package/lib/992.sc +173 -0
  5. package/lib/993.sc +135 -0
  6. package/lib/994.sc +13 -0
  7. package/lib/995.sc +64 -5
  8. package/lib/996.sc +88 -26
  9. package/lib/997.sc +26 -0
  10. package/lib/998.sc +43 -5
  11. package/lib/999.sc +4 -1
  12. package/lib/resources.js +33 -3
  13. package/lib/resources.ts +34 -3
  14. package/lib/system.sh +38 -1
  15. package/package.json +1 -1
  16. package/packages/content/src/room.js +13 -1
  17. package/packages/content/src/room.ts +11 -1
  18. package/packages/content/src/target.d.ts +5 -0
  19. package/packages/content/src/target.ts +5 -0
  20. package/packages/sci/src/vm/kernels/index.d.ts +1 -1
  21. package/packages/sci/src/vm/kernels/index.js +1 -1
  22. package/packages/sci/src/vm/kernels/index.ts +1 -1
  23. package/packages/sci/src/vm/kernels/saves.d.ts +8 -0
  24. package/packages/sci/src/vm/kernels/saves.js +24 -3
  25. package/packages/sci/src/vm/kernels/saves.ts +30 -3
  26. package/packages/sci/src/vm/kernels/text.js +27 -2
  27. package/packages/sci/src/vm/kernels/text.ts +23 -2
  28. package/tools/art/build.d.ts +10 -0
  29. package/tools/art/build.js +32 -2
  30. package/tools/art/build.ts +28 -2
  31. package/tools/art/font.d.ts +24 -0
  32. package/tools/art/font.js +40 -0
  33. package/tools/art/font.ts +56 -0
  34. package/tools/game/build.d.ts +4 -0
  35. package/tools/game/build.js +27 -1
  36. package/tools/game/build.ts +24 -1
  37. package/tools/game/kit.d.ts +1 -0
  38. package/tools/game/kit.js +1 -0
  39. package/tools/game/kit.ts +1 -0
  40. package/tools/game/target.d.ts +1 -1
  41. package/tools/game/target.js +5 -3
  42. package/tools/game/target.ts +5 -3
package/CHANGELOG.md CHANGED
@@ -1,8 +1,29 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
- versions follow [Semantic Versioning](https://semver.org/), and the entries are generated
5
- from commit messages ([Conventional Commits](https://www.conventionalcommits.org/)).
3
+ All notable changes to this project. Versions follow [Semantic Versioning](https://semver.org/),
4
+ and the entries are generated from commit messages
5
+ ([Conventional Commits](https://www.conventionalcommits.org/)) when a release is made.
6
+
7
+ ## [0.4.0](https://github.com/rlueder/sci-ts/compare/v0.3.0...v0.4.0) (2026-10-01)
8
+
9
+ ### Added
10
+
11
+ * **lib:** saving and restoring ([#2](https://github.com/rlueder/sci-ts/issues/2)) ([117ad9d](https://github.com/rlueder/sci-ts/commit/117ad9ddf4ca87d443d8b7143d66eef380dced9a))
12
+
13
+ ## [0.3.0](https://github.com/rlueder/sci-ts/compare/v0.2.2...v0.3.0) (2026-10-01)
14
+
15
+ ### Added
16
+
17
+ * **content:** close-ups from Yarn ([0546752](https://github.com/rlueder/sci-ts/commit/054675216342747bc5565301aff3d39d27a900c2))
18
+ * **lib:** a text style for every box and menu ([0350a4e](https://github.com/rlueder/sci-ts/commit/0350a4e28a57d951277372ff6144bd8e9e534bee))
19
+ * **lib:** an inventory and close-ups ([6d401d8](https://github.com/rlueder/sci-ts/commit/6d401d8f92d33a4b694dcc617adbb1d5878da246))
20
+ * **lib:** cursors a game can replace, and a wait cursor ([aa55d11](https://github.com/rlueder/sci-ts/commit/aa55d1142568aeb4b14681e44881a4223192d5d2))
21
+ * **lib:** portraits at the top left, with blinking eyes ([8fd6aa1](https://github.com/rlueder/sci-ts/commit/8fd6aa16e83f9608666f67fd998ac852be1a5a99))
22
+ * **tools:** fonts from sheets of glyphs drawn in a paint program ([cb13669](https://github.com/rlueder/sci-ts/commit/cb13669363fb419425b0a54f0a5afb9ca1576dcf))
23
+
24
+ ### Maintenance
25
+
26
+ * release from CI with semantic-release ([1757412](https://github.com/rlueder/sci-ts/commit/17574126c257961dd1058f596c6174445f3d9f16))
6
27
 
7
28
  ## [0.2.2] - 2026-10-01
8
29
 
@@ -74,5 +95,3 @@ from commit messages ([Conventional Commits](https://www.conventionalcommits.org
74
95
 
75
96
  - Workspace, typecheck, tests and CI
76
97
  - Build every game, and publish the site to GitHub Pages by hand
77
-
78
-
@@ -1,4 +1,4 @@
1
- import { EventType, ResourceManager, ResourceType, SciKey, SoundDevice, Vm, allKernels, audio, graphics, hostFiles, input, parseAdLibBank, readingTime, restoreGame, saves, snapshot, } from "../../../packages/sci/src/index.js";
1
+ import { EventType, GLOBAL_NAMES_VOCAB, ResourceManager, ResourceType, SciKey, SoundDevice, Vm, allKernels, audio, graphics, hostFiles, input, parseAdLibBank, readingTime, restoreGame, saves, snapshot, } from "../../../packages/sci/src/index.js";
2
2
  import { AdLibOutput } from "./adlib.js";
3
3
  import { WebAudioOutput } from "./audio.js";
4
4
  import { BASE, httpFiles } from "./files.js";
@@ -48,7 +48,7 @@ export class GameSession {
48
48
  const vm = new Vm(rm);
49
49
  vm.registerKernels(allKernels);
50
50
  // Saved games persist in this browser (IndexedDB) and appear in the game's Restore screen.
51
- saves(vm).store = await browserSaveStore(options.saveNamespace ?? [...(options.mods ?? [])].sort().join("+"));
51
+ saves(vm).store = await browserSaveStore(options.saveNamespace ?? defaultSaveNamespace(vm, rm, options.mods ?? []));
52
52
  if (options.debug)
53
53
  hostFiles(vm).add("18.scr");
54
54
  const sound = new WebAudioOutput(httpFiles, (name) => rm.file(name));
@@ -279,6 +279,16 @@ export class GameSession {
279
279
  });
280
280
  }
281
281
  }
282
+ /**
283
+ * Whose saves these are. Games built by sci-ts (they carry their globals' names, vocab 990)
284
+ * each get their own, by the game object's name, so games played from one site don't share
285
+ * save slots; Sierra's games keep the namespace they always had (their mods, if any).
286
+ */
287
+ function defaultSaveNamespace(vm, rm, mods) {
288
+ if (rm.has({ type: ResourceType.Vocab, number: GLOBAL_NAMES_VOCAB }))
289
+ return `game:${vm.object(vm.exportAddress(0, 0)).name}`;
290
+ return [...mods].sort().join("+");
291
+ }
282
292
  const KEY_CODES = {
283
293
  Enter: 13, Escape: 27, Backspace: 8, Tab: 9, " ": 32,
284
294
  ArrowUp: SciKey.Up, ArrowDown: SciKey.Down, ArrowLeft: SciKey.Left, ArrowRight: SciKey.Right,
@@ -1,5 +1,5 @@
1
1
  import {
2
- EventType, ResourceManager, ResourceType, SciKey, SoundDevice, Vm, allKernels, audio, graphics, hostFiles, input, parseAdLibBank, readingTime, restoreGame, saves, snapshot,
2
+ EventType, GLOBAL_NAMES_VOCAB, ResourceManager, ResourceType, SciKey, SoundDevice, Vm, allKernels, audio, graphics, hostFiles, input, parseAdLibBank, readingTime, restoreGame, saves, snapshot,
3
3
  type Frame, type VmSnapshot,
4
4
  } from "../../../packages/sci/src/index.ts";
5
5
  import { AdLibOutput } from "./adlib.ts";
@@ -76,7 +76,7 @@ export class GameSession {
76
76
  const vm = new Vm(rm);
77
77
  vm.registerKernels(allKernels);
78
78
  // Saved games persist in this browser (IndexedDB) and appear in the game's Restore screen.
79
- saves(vm).store = await browserSaveStore(options.saveNamespace ?? [...(options.mods ?? [])].sort().join("+"));
79
+ saves(vm).store = await browserSaveStore(options.saveNamespace ?? defaultSaveNamespace(vm, rm, options.mods ?? []));
80
80
  if (options.debug) hostFiles(vm).add("18.scr");
81
81
 
82
82
  const sound = new WebAudioOutput(httpFiles, (name) => rm.file(name));
@@ -294,6 +294,16 @@ export class GameSession {
294
294
  }
295
295
  }
296
296
 
297
+ /**
298
+ * Whose saves these are. Games built by sci-ts (they carry their globals' names, vocab 990)
299
+ * each get their own, by the game object's name, so games played from one site don't share
300
+ * save slots; Sierra's games keep the namespace they always had (their mods, if any).
301
+ */
302
+ function defaultSaveNamespace(vm: Vm, rm: ResourceManager, mods: string[]): string {
303
+ if (rm.has({ type: ResourceType.Vocab, number: GLOBAL_NAMES_VOCAB })) return `game:${vm.object(vm.exportAddress(0, 0)).name}`;
304
+ return [...mods].sort().join("+");
305
+ }
306
+
297
307
  const KEY_CODES: Record<string, number> = {
298
308
  Enter: 13, Escape: 27, Backspace: 8, Tab: 9, " ": 32,
299
309
  ArrowUp: SciKey.Up, ArrowDown: SciKey.Down, ArrowLeft: SciKey.Left, ArrowRight: SciKey.Right,
package/lib/992.sc ADDED
@@ -0,0 +1,173 @@
1
+ ;;; Saving, restoring and restarting: the game menu (Escape), F5 to save and F7 to restore.
2
+ (script 992)
3
+ (include "system.sh")
4
+
5
+ ;; A line to type into, for the interpreter's line editor (EditText): shown over the room
6
+ ;; until Enter, Escape or a click outside it, each of which is left for the next event.
7
+ (class EditField of Obj
8
+ (properties
9
+ x 0 y 0 z 0
10
+ view -1 loop 0 cel 0
11
+ priority 300 fixPriority 1
12
+ plane 0
13
+ bitmap 0
14
+ scaleSignal 0 scaleX 128 scaleY 128
15
+ text 0
16
+ font 0
17
+ fore 0
18
+ back 255
19
+ skip 254
20
+ mode 0
21
+ borderColor 0
22
+ nsLeft 0 nsTop 0 nsRight 0 nsBottom 0
23
+ textLeft 3 textTop 3 textRight 0 textBottom 0
24
+ width SAVE_DESCRIPTION) ; the most characters
25
+
26
+ ;; At (x, y), w pixels wide and one line of text tall.
27
+ (method (place theX theY w)
28
+ (= x theX)
29
+ (= y theY)
30
+ (= plane uiPlane)
31
+ (= font (textStyle font?))
32
+ (= fore (textStyle fore?))
33
+ (= back (textStyle back?))
34
+ (= borderColor (if (== (textStyle border?) -1) (textStyle fore?) else (textStyle border?)))
35
+ (= nsLeft x)
36
+ (= nsTop y)
37
+ (= nsRight (+ x w -1))
38
+ (= nsBottom (+ y (PointSize font) 5))
39
+ (= textRight (- w 4))
40
+ (= textBottom (- (- nsBottom nsTop) 3))))
41
+
42
+ ;; What the game menu, F5 and F7 open; (game save:), (game restore:) and (game showMenu:)
43
+ ;; open them from a script. A save is a snapshot of the whole game, named with a line the
44
+ ;; player types; only saves made by this build of the game are offered.
45
+ (class SaveRestore of Obj
46
+ (properties
47
+ mode 0 ; what the open menu is for: SR_MENU, SR_SAVE, SR_RESTORE, or SR_NAME
48
+ descriptions 0 ; GetSaveFiles: SAVE_DESCRIPTION characters a save, newest first
49
+ ids 0
50
+ count 0
51
+ field 0 ; the description being typed
52
+ slot 0) ; the save it goes into
53
+
54
+ (method (showMenu &tmp menu)
55
+ (= mode SR_MENU)
56
+ (= menu (Menu new:))
57
+ (menu
58
+ add: SR_SAVE "Save the game"
59
+ add: SR_RESTORE "Restore a game"
60
+ add: SR_RESTART "Start again"
61
+ add: 0 "Carry on"
62
+ show: self))
63
+
64
+ ;; The saves there are now.
65
+ (method (load)
66
+ (self release:)
67
+ (= descriptions (String ARRAY_NEW 0))
68
+ (= ids (Array ARRAY_NEW 0 0))
69
+ (= count (GetSaveFiles "" descriptions ids)))
70
+
71
+ (method (release)
72
+ (if descriptions (String ARRAY_FREE descriptions) (= descriptions 0))
73
+ (if ids (Array ARRAY_FREE ids) (= ids 0)))
74
+
75
+ ;; The description of the i-th save (a new string).
76
+ (method (description i &tmp s)
77
+ (= s (String ARRAY_NEW SAVE_DESCRIPTION))
78
+ (Array ARRAY_COPY s 0 descriptions (* i SAVE_DESCRIPTION) (- SAVE_DESCRIPTION 1))
79
+ (return s))
80
+
81
+ ;; One more than the highest save number.
82
+ (method (nextSlot &tmp i n id)
83
+ (= n 0)
84
+ (for ((= i 0)) (< i count) ((++ i))
85
+ (= id (Array ARRAY_AT ids i))
86
+ (if (>= id n) (= n (+ id 1))))
87
+ (return n))
88
+
89
+ ;; Menu values are save numbers plus one (0 is cancel).
90
+ (method (listSaves menu &tmp i)
91
+ (for ((= i 0)) (and (< i count) (< i SAVES_SHOWN)) ((++ i))
92
+ (menu add: (+ (Array ARRAY_AT ids i) 1) (self description: i))))
93
+
94
+ (method (save &tmp menu)
95
+ (game clearText:)
96
+ (self load:)
97
+ (= mode SR_SAVE)
98
+ (= menu ((Menu new:) y: 12 yourself:))
99
+ (menu add: (+ (self nextSlot:) 1) "A new saved game")
100
+ (self listSaves: menu)
101
+ (menu add: 0 "Cancel" show: self))
102
+
103
+ (method (restore &tmp menu)
104
+ (game clearText:)
105
+ (self load:)
106
+ (if (not count)
107
+ (narrator say: "There are no saved games yet.")
108
+ (return))
109
+ (= mode SR_RESTORE)
110
+ (= menu ((Menu new:) y: 12 yourself:))
111
+ (self listSaves: menu)
112
+ (menu add: 0 "Cancel" show: self))
113
+
114
+ (method (choose v)
115
+ (switch mode
116
+ (SR_MENU
117
+ (switch v
118
+ (SR_SAVE (self save:))
119
+ (SR_RESTORE (self restore:))
120
+ (SR_RESTART (RestartGame))))
121
+ (SR_SAVE (if v (self askDescription: (- v 1))))
122
+ (SR_RESTORE
123
+ (if v
124
+ ;; Returns only if it couldn't: the game starts again where the save left it.
125
+ (RestoreGame "" (- v 1) "")
126
+ (narrator say: "That saved game couldn't be restored.")))))
127
+
128
+ ;; Types the description of save n: the old one, or the room's number to start from.
129
+ (method (askDescription n &tmp i)
130
+ (= slot n)
131
+ (= field (EditField new:))
132
+ (field text: (String ARRAY_NEW SAVE_DESCRIPTION))
133
+ (for ((= i 0)) (< i count) ((++ i))
134
+ (if (== (Array ARRAY_AT ids i) n)
135
+ (Array ARRAY_COPY (field text?) 0 descriptions (* i SAVE_DESCRIPTION) (- SAVE_DESCRIPTION 1))))
136
+ (if (not (String STRING_LENGTH (field text?)))
137
+ (String STRING_FORMAT_INTO (field text?) "Room %d" curRoomNum))
138
+ (field place: 40 80 240)
139
+ (EditText field)
140
+ ;; How it ended (Enter, Escape, a click) is the next event: wait for it.
141
+ (= mode SR_NAME)
142
+ (= dialog self))
143
+
144
+ (method (handleEvent event)
145
+ (event claimed: TRUE)
146
+ (if (== mode SR_NAME)
147
+ (cond
148
+ ((and (== (event type?) EV_KEY_DOWN) (== (event message?) KEY_ENTER)) (self finishSave:))
149
+ ((or (== (event type?) EV_KEY_DOWN) (== (event type?) EV_MOUSE_DOWN)) (self endNaming:))))
150
+ (return TRUE))
151
+
152
+ (method (dismiss)
153
+ (self endNaming:))
154
+
155
+ (method (endNaming)
156
+ (if (== dialog self) (= dialog 0))
157
+ (= mode 0)
158
+ (if field
159
+ (String ARRAY_FREE (field text?))
160
+ (field dispose:)
161
+ (= field 0)))
162
+
163
+ (method (finishSave &tmp ok)
164
+ ;; Nothing of this menu goes into the save.
165
+ (if (== dialog self) (= dialog 0))
166
+ (= mode 0)
167
+ (= ok (SaveGame "" slot (field text?) ""))
168
+ (self endNaming: release:)
169
+ (narrator say: (if ok "Saved." else "The game couldn't be saved.")))
170
+
171
+ (method (dispose)
172
+ (self release:)
173
+ (super dispose:)))
package/lib/993.sc ADDED
@@ -0,0 +1,135 @@
1
+ ;;; Things: what the hero carries and the window to choose one from, and close looks at
2
+ ;;; things.
3
+ (script 993)
4
+ (include "system.sh")
5
+
6
+ ;; Something the hero can carry. Its view's loop 0 is its icon in the inventory window and
7
+ ;; loop 1 the cursor while it's being used (the cel's anchor is the hotspot); both anchored
8
+ ;; at the top-left corner. Using it on something sends that thing doVerb: with the item's
9
+ ;; own verb, 10 and up, so the room answers with noun and that verb (in Yarn, a node like
10
+ ;; filings.lens: the game names its items' verbs in items.yaml).
11
+ (class InvItem of Obj
12
+ (properties
13
+ view -1
14
+ verb 0
15
+ description 0) ; what looking at it in the window says
16
+
17
+ ;; Looked at in the inventory window.
18
+ (method (doVerb v)
19
+ (if (and (== v V_LOOK) description) (narrator say: description))))
20
+
21
+ ;; What the hero carries (the `inventory` global): (inventory add: lens), (inventory
22
+ ;; contains: lens). showSelf opens the window: the items in a row, one click picks one to
23
+ ;; use (it becomes the cursor), a right-click on one looks at it, a click elsewhere closes.
24
+ (class Inventory of Set
25
+ (properties
26
+ window 0
27
+ icons 0
28
+ y 24)
29
+
30
+ (method (showSelf &tmp node item icon x)
31
+ (if (not size)
32
+ (narrator say: "You aren't carrying anything.")
33
+ (return))
34
+ (= window
35
+ ((TextItem new:)
36
+ text: ""
37
+ width: (+ (* size (+ INV_ICON INV_GAP)) INV_GAP (* 2 (textStyle margin?)))
38
+ height: (+ INV_ICON (* 2 INV_GAP) (* 2 (textStyle margin?)))
39
+ yourself:))
40
+ (if (!= (textStyle frame?) -1)
41
+ (window
42
+ width: (+ (window width?) (CelWide (textStyle frame?) 0 6) (CelWide (textStyle frame?) 0 7))
43
+ height: (+ (window height?) (CelHigh (textStyle frame?) 0 4) (CelHigh (textStyle frame?) 0 5))))
44
+ (window x: (/ (- SCREEN_WIDTH (window width?)) 2) y: y init:)
45
+ (= icons (List new:))
46
+ (= x (+ (window x?) (window textLeft?) INV_GAP))
47
+ (for ((= node (FirstNode elements))) node ((= node (NextNode node)))
48
+ (= item (NodeValue node))
49
+ ;; The icon keeps its item in `actions`.
50
+ (= icon ((View new:) view: (item view?) loop: 0 cel: 0 actions: item x: x y: (+ (window y?) (window textTop?) INV_GAP) yourself:))
51
+ (icon init:)
52
+ (icon plane: uiPlane setPri: (+ (window priority?) 1))
53
+ (icons add: icon)
54
+ (+= x (+ INV_ICON INV_GAP)))
55
+ (= dialog self))
56
+
57
+ ;; The item whose icon is at (x, y), or 0.
58
+ (method (itemAt ex ey &tmp icon)
59
+ (= icon (icons firstTrue: #onMe ex ey))
60
+ (return (if icon (icon actions?) else 0)))
61
+
62
+ (method (handleEvent event &tmp item)
63
+ (event claimed: TRUE)
64
+ (if (!= (event type?) EV_MOUSE_DOWN) (return TRUE))
65
+ (= item (self itemAt: (event x?) (event y?)))
66
+ (cond
67
+ ((not item) (self hide:))
68
+ ((& (event modifiers?) MOD_RIGHT) (item doVerb: V_LOOK))
69
+ (else
70
+ (self hide:)
71
+ (user useItem: item)))
72
+ (return TRUE))
73
+
74
+ (method (hide)
75
+ (if (== dialog self) (= dialog 0))
76
+ (if icons
77
+ (icons eachElementDo: #dispose)
78
+ (icons dispose:)
79
+ (= icons 0))
80
+ (if window (window dispose:) (= window 0)))
81
+
82
+ (method (dismiss)
83
+ (self hide:))
84
+
85
+ (method (delete item)
86
+ (if (== theItem item) (user useItem: 0))
87
+ (super delete: item &rest)))
88
+
89
+ ;; A close look at something: a view shown in the middle of the screen with the room dimmed
90
+ ;; behind it, until a click; then it goes, and whoever asked is cued.
91
+ ;; ((CloseUp new:) show: view [loop [cel [whoCares]]])
92
+ ;; Its cels are anchored at their top-left corner. The dimming is the library's view 996,
93
+ ;; drawn in remap colour 253 (which the art palette keeps free) at `dim` percent.
94
+ (class CloseUp of Obj
95
+ (properties
96
+ shade 0
97
+ picture 0
98
+ caller 0
99
+ dim 50)
100
+
101
+ (method (show v l c whoCares &tmp lp cl)
102
+ (= lp (if (> argc 1) l else 0))
103
+ (= cl (if (> argc 2) c else 0))
104
+ (= caller (if (> argc 3) whoCares else 0))
105
+ (RemapColors REMAP_BY_PERCENT 253 dim)
106
+ (= shade ((View new:) view: CLOSE_UP_SHADE x: 0 y: 0 yourself:))
107
+ (shade init:)
108
+ (shade plane: uiPlane setPri: 10)
109
+ (= picture
110
+ ((View new:)
111
+ view: v loop: lp cel: cl
112
+ x: (/ (- SCREEN_WIDTH (CelWide v lp cl)) 2)
113
+ y: (/ (- SCREEN_HEIGHT (CelHigh v lp cl)) 2)
114
+ yourself:))
115
+ (picture init:)
116
+ (picture plane: uiPlane setPri: 20)
117
+ (= dialog self))
118
+
119
+ (method (handleEvent event)
120
+ (event claimed: TRUE)
121
+ (if (== (event type?) EV_MOUSE_DOWN)
122
+ (self close:))
123
+ (return TRUE))
124
+
125
+ (method (dismiss)
126
+ (self close:))
127
+
128
+ (method (close &tmp c)
129
+ (if (== dialog self) (= dialog 0))
130
+ (if picture (picture dispose:) (= picture 0))
131
+ (if shade (shade dispose:) (= shade 0))
132
+ (RemapColors REMAP_OFF 253)
133
+ (= c caller)
134
+ (self dispose:)
135
+ (if c (c cue:))))
package/lib/994.sc CHANGED
@@ -42,6 +42,19 @@
42
42
  (= client 0)
43
43
  (if c (c cue:)))))
44
44
 
45
+ ;; After a restore nothing is playing: a sound that loops forever starts again; one that
46
+ ;; was playing once is over (and whoever was waiting for it hears so).
47
+ (method (replay &tmp c)
48
+ (if handle
49
+ (= handle 0)
50
+ (= nodePtr 0)
51
+ (if (== loop -1)
52
+ (self play: client)
53
+ else
54
+ (= c client)
55
+ (= client 0)
56
+ (if c (c cue:)))))
57
+
45
58
  (method (dispose)
46
59
  (DoSound SND_STOP self)
47
60
  (DoSound SND_DISPOSE self)
package/lib/995.sc CHANGED
@@ -15,22 +15,70 @@
15
15
  ;; a click walks the hero there, or does the verb to what's under the cursor: an actor or
16
16
  ;; prop, a feature, or else the room. A click while a line is shown dismisses it; while a
17
17
  ;; menu is open, it goes to the menu.
18
+ ;;
19
+ ;; The cursor is a view for each verb, and another while the player can't act (handsOff).
20
+ ;; A game with its own sets them in its init: (user walkCursor: 261 waitCursor: 265 ...).
21
+ ;; I or Tab opens the inventory; an item picked there is used like a verb (V_ITEM), with
22
+ ;; its own cursor, until right-click goes back to walking.
23
+ ;; Escape opens the game menu (save, restore, start again); F5 saves and F7 restores.
18
24
  (class User of Obj
19
25
  (properties
20
26
  verb V_WALK
21
- canInput TRUE)
27
+ canInput TRUE
28
+ lookCursor 991 ; the library's: CURSOR_BASE + verb
29
+ talkCursor 992
30
+ walkCursor 993
31
+ doCursor 994
32
+ waitCursor 995 ; CURSOR_WAIT
33
+ cursor -1 ; the view and loop showing now
34
+ cursorLoop 0)
22
35
 
23
36
  (method (init)
24
37
  (self setVerb: verb))
25
38
 
26
39
  (method (setVerb v)
27
40
  (= verb v)
28
- (SetCursor (+ CURSOR_BASE v) 0 0))
41
+ (self showCursor:))
42
+
43
+ ;; Picks an item to use (0: none).
44
+ (method (useItem item)
45
+ (= theItem item)
46
+ (cond
47
+ (item (self setVerb: V_ITEM))
48
+ ((== verb V_ITEM) (self setVerb: V_WALK))))
49
+
50
+ ;; The cursor for a verb (0: waiting).
51
+ (method (cursorFor v)
52
+ (switch v
53
+ (V_LOOK (return lookCursor))
54
+ (V_TALK (return talkCursor))
55
+ (V_WALK (return walkCursor))
56
+ (V_DO (return doCursor))
57
+ (else (return waitCursor))))
58
+
59
+ (method (showCursor &tmp v l)
60
+ (= l 0)
61
+ (cond
62
+ ((not canInput) (= v waitCursor))
63
+ ((and (== verb V_ITEM) theItem) (= v (theItem view?)) (= l 1))
64
+ (else (= v (self cursorFor: verb))))
65
+ (if (or (!= v cursor) (!= l cursorLoop))
66
+ (= cursor v)
67
+ (= cursorLoop l)
68
+ (SetCursor v l 0)))
29
69
 
30
70
  (method (doit)
71
+ (self showCursor:)
31
72
  (while (GetEvent EV_ALL theEvent)
32
73
  (self handleEvent: theEvent)))
33
74
 
75
+ ;; The verb right-click goes to: walk, do, look, talk, the item picked (if any), walk.
76
+ (method (nextVerb)
77
+ (cond
78
+ ((== verb V_ITEM) (return V_WALK))
79
+ ((and (== verb V_TALK) theItem) (return V_ITEM))
80
+ (else (return (+ (mod verb VERB_COUNT) 1)))))
81
+
34
82
  (method (handleEvent event &tmp obj ex ey)
35
83
  (event claimed: FALSE)
36
84
  ;; A line being shown, then an open menu, get clicks first.
@@ -38,9 +86,20 @@
38
86
  (if dialog
39
87
  (dialog handleEvent: event)
40
88
  (return))
41
- (if (or (not canInput) (!= (event type?) EV_MOUSE_DOWN)) (return))
89
+ (if (not canInput) (return))
90
+ (if (== (event type?) EV_KEY_DOWN)
91
+ (switch (event message?)
92
+ (KEY_ESCAPE (event claimed: TRUE) (game showMenu:) (return))
93
+ (KEY_F5 (event claimed: TRUE) (game save:) (return))
94
+ (KEY_F7 (event claimed: TRUE) (game restore:) (return))))
95
+ (if (and (== (event type?) EV_KEY_DOWN)
96
+ (or (== (event message?) KEY_TAB) (== (event message?) KEY_i) (== (event message?) KEY_I)))
97
+ (event claimed: TRUE)
98
+ (inventory showSelf:)
99
+ (return))
100
+ (if (!= (event type?) EV_MOUSE_DOWN) (return))
42
101
  (if (& (event modifiers?) MOD_RIGHT)
43
- (self setVerb: (+ (mod verb VERB_COUNT) 1))
102
+ (self setVerb: (self nextVerb:))
44
103
  (return))
45
104
  (= ex (event x?))
46
105
  (= ey (event y?))
@@ -50,4 +109,4 @@
50
109
  (= obj (cast firstTrue: #onMe ex ey))
51
110
  (if (not obj) (= obj (features firstTrue: #onMe ex ey)))
52
111
  (if (not obj) (= obj curRoom))
53
- (if obj (obj doVerb: verb))))
112
+ (if obj (obj doVerb: (if (== verb V_ITEM) (theItem verb?) else verb)))))