sci2-ts 0.10.0 → 0.12.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 +12 -0
- package/lib/996.sc +127 -37
- package/package.json +1 -1
- package/packages/content/src/room.js +2 -0
- package/packages/content/src/room.ts +2 -0
- package/packages/content/src/target.d.ts +3 -1
- package/packages/content/src/target.ts +3 -2
- package/tools/game/target.js +2 -2
- package/tools/game/target.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project. Versions follow [Semantic Versioning](https
|
|
|
4
4
|
and the entries are generated from commit messages
|
|
5
5
|
([Conventional Commits](https://www.conventionalcommits.org/)) when a release is made.
|
|
6
6
|
|
|
7
|
+
## [0.12.0](https://github.com/rlueder/sci-ts/compare/v0.11.0...v0.12.0) (2026-10-02)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* **lib:** portraits that face the middle from their character's side, framed, and one for the hero ([#14](https://github.com/rlueder/sci-ts/issues/14)) ([e7be055](https://github.com/rlueder/sci-ts/commit/e7be0555af2c49743c539e7231cf979ac274617e))
|
|
12
|
+
|
|
13
|
+
## [0.11.0](https://github.com/rlueder/sci-ts/compare/v0.10.0...v0.11.0) (2026-10-02)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
* **lib:** menus as one framed box, and text boxes that sit on the bottom of the screen ([#13](https://github.com/rlueder/sci-ts/issues/13)) ([83cf03d](https://github.com/rlueder/sci-ts/commit/83cf03d0bb4e044d24cbff769a102294e3a66a03))
|
|
18
|
+
|
|
7
19
|
## [0.10.0](https://github.com/rlueder/sci-ts/compare/v0.9.0...v0.10.0) (2026-10-02)
|
|
8
20
|
|
|
9
21
|
### Added
|
package/lib/996.sc
CHANGED
|
@@ -17,11 +17,15 @@
|
|
|
17
17
|
back 255 ; paper
|
|
18
18
|
border 0 ; a one-pixel border's colour; -1: none
|
|
19
19
|
frame -1 ; a frame view, or -1
|
|
20
|
-
margin 4
|
|
20
|
+
margin 4 ; between the text and the border or frame
|
|
21
|
+
portraitFrame -1 ; a view around portraits: loop 0 behind the face, loop 1 over its edges
|
|
22
|
+
portraitX 8 ; where a portrait's face goes on the left (mirrored on the right)
|
|
23
|
+
portraitY 8))
|
|
21
24
|
|
|
22
25
|
;; A box of text over the room: drawn by the interpreter into a bitmap, shown as a screen
|
|
23
26
|
;; item in the UI plane. Set text (and width, x, y), then init; it's as tall as the text
|
|
24
27
|
;; unless height is set. Font, colours and frame come from the textStyle unless set here.
|
|
28
|
+
;; With bottom set, y is worked out so the box ends there: it grows upwards with the text.
|
|
25
29
|
(class TextItem of Obj
|
|
26
30
|
(properties
|
|
27
31
|
x 0 y 0 z 0
|
|
@@ -40,7 +44,8 @@
|
|
|
40
44
|
frame -2 ; -1: none
|
|
41
45
|
textLeft 0 textTop 0 textRight -1 textBottom -1
|
|
42
46
|
width 200
|
|
43
|
-
height 0
|
|
47
|
+
height 0
|
|
48
|
+
bottom -1) ; -1: placed at y
|
|
44
49
|
|
|
45
50
|
(method (init &tmp r m padL padT padR padB)
|
|
46
51
|
(if (== font -1) (= font (textStyle font?)))
|
|
@@ -65,6 +70,7 @@
|
|
|
65
70
|
(= textTop padT)
|
|
66
71
|
(= textRight (- width (+ padR 1)))
|
|
67
72
|
(= textBottom (- height (+ padB 1)))
|
|
73
|
+
(if (!= bottom -1) (= y (- bottom height)))
|
|
68
74
|
(= plane uiPlane)
|
|
69
75
|
(= bitmap (CreateTextBitmap 0 width height self))
|
|
70
76
|
(if (!= frame -1) (self drawFrame:))
|
|
@@ -111,7 +117,7 @@
|
|
|
111
117
|
noun 0 verb 0 cond 0 seq 0
|
|
112
118
|
font -1 ; -1: the textStyle's
|
|
113
119
|
x -1 ; -1: centred
|
|
114
|
-
y 16
|
|
120
|
+
y 16 ; below 0: that far above the bottom of the screen, growing upwards
|
|
115
121
|
width 220)
|
|
116
122
|
|
|
117
123
|
(method (say txt whoCares)
|
|
@@ -128,7 +134,8 @@
|
|
|
128
134
|
font: font
|
|
129
135
|
width: width
|
|
130
136
|
x: (if (== x -1) (/ (- SCREEN_WIDTH width) 2) else x)
|
|
131
|
-
y: y
|
|
137
|
+
y: (if (< y 0) 0 else y)
|
|
138
|
+
bottom: (if (< y 0) (+ SCREEN_HEIGHT y) else -1)
|
|
132
139
|
yourself:))
|
|
133
140
|
(box init:))
|
|
134
141
|
(= talking self)
|
|
@@ -187,34 +194,80 @@
|
|
|
187
194
|
(super say: line &rest whoCares)))
|
|
188
195
|
|
|
189
196
|
;; Someone with a portrait: a view whose loop 0 is the bust, loop 1 the mouth and loop 2 the
|
|
190
|
-
;; eyes, every cel the same size and anchored at its top-left corner
|
|
191
|
-
;;
|
|
192
|
-
;;
|
|
193
|
-
;; the line is said the mouth moves, then closes; the eyes blink.
|
|
197
|
+
;; eyes, every cel the same size and anchored at its top-left corner; loops 3 to 5, if there
|
|
198
|
+
;; are any, are the same facing left. A room's talker is handed its parts (init: mouth bust
|
|
199
|
+
;; eyes frame, where frame is drawn as the bust); one with a view of its own (the hero's)
|
|
200
|
+
;; makes them. While the line is said the mouth moves, then closes; the eyes blink.
|
|
201
|
+
;;
|
|
202
|
+
;; The portrait goes at the top left facing right, or the top right facing left, with the
|
|
203
|
+
;; text beside it on the side towards the middle. A character takes the side of the hero
|
|
204
|
+
;; it stands on (who: is its body in the room; without one, the right); the hero takes the
|
|
205
|
+
;; side opposite whoever spoke last. The textStyle places it (portraitX, portraitY) and can
|
|
206
|
+
;; frame it (portraitFrame).
|
|
194
207
|
(class PortraitTalker of Talker
|
|
195
208
|
(properties
|
|
196
209
|
mouth 0 bust 0 eyes 0 frame 0
|
|
210
|
+
view -1 ; the portrait's view, for a talker that makes its own parts
|
|
211
|
+
who 0 ; the character's body in the room
|
|
212
|
+
side 0 ; where the portrait is: 0 the left, 1 the right
|
|
213
|
+
otherSide 1 ; the hero's: the side of whoever spoke last
|
|
214
|
+
back 0 front 0 ; the portraitFrame, behind the face and over its edges
|
|
197
215
|
priority 150
|
|
198
216
|
mouthFor 0) ; cycles the mouth moves for
|
|
199
217
|
|
|
200
218
|
(method (init theMouth theBust theEyes theFrame)
|
|
201
|
-
(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
(
|
|
209
|
-
|
|
210
|
-
(=
|
|
211
|
-
(=
|
|
212
|
-
|
|
219
|
+
(if argc
|
|
220
|
+
(= mouth theMouth)
|
|
221
|
+
(= bust theBust)
|
|
222
|
+
(= eyes theEyes)
|
|
223
|
+
(= frame theFrame)))
|
|
224
|
+
|
|
225
|
+
(method (makeParts)
|
|
226
|
+
(if (and (!= view -1) (not bust) (not frame))
|
|
227
|
+
(= bust ((View new:) view: view yourself:))
|
|
228
|
+
(= mouth ((Prop new:) view: view yourself:))
|
|
229
|
+
(= eyes ((Prop new:) view: view yourself:))))
|
|
230
|
+
|
|
231
|
+
;; This line's side: see above.
|
|
232
|
+
(method (pickSide &tmp s)
|
|
233
|
+
(if (== self heroTalker) (return (- 1 otherSide)))
|
|
234
|
+
(= s (if (and who ego (< (who x?) (ego x?))) 0 else 1))
|
|
235
|
+
(if (and heroTalker (!= heroTalker self) (heroTalker respondsTo: #otherSide)) (heroTalker otherSide: s))
|
|
236
|
+
(return s))
|
|
237
|
+
|
|
238
|
+
(method (say txt whoCares &tmp face l pw f fx fy over)
|
|
239
|
+
(self init: makeParts:)
|
|
240
|
+
(= face (if frame frame else bust))
|
|
241
|
+
(if face
|
|
242
|
+
(= side (self pickSide:))
|
|
243
|
+
(= l (if (and side (>= (NumLoops face) 6)) 3 else 0))
|
|
244
|
+
(= pw (CelWide (face view?) l 0))
|
|
245
|
+
(= f (textStyle portraitFrame?))
|
|
246
|
+
(= over (if (!= f -1) (/ (- (CelWide f 0 0) pw) 2) else 0))
|
|
247
|
+
(= fx (if side (- SCREEN_WIDTH (+ (textStyle portraitX?) pw)) else (textStyle portraitX?)))
|
|
248
|
+
(= fy (textStyle portraitY?))
|
|
249
|
+
(face x: fx y: fy loop: l cel: 0)
|
|
250
|
+
(if mouth (mouth x: fx y: fy loop: (+ l 1) cel: 0))
|
|
251
|
+
(if eyes (eyes x: fx y: fy loop: (+ l 2) cel: 0))
|
|
252
|
+
(if (!= f -1)
|
|
253
|
+
(if (not back) (= back ((View new:) view: f loop: 0 cel: 0 yourself:)))
|
|
254
|
+
(back x: fx y: fy)
|
|
255
|
+
(if (and (not front) (>= (NumLoops back) 2)) (= front ((View new:) view: f loop: 1 cel: 0 yourself:)))
|
|
256
|
+
(if front (front x: fx y: fy)))
|
|
257
|
+
;; The text beside it, towards the middle.
|
|
258
|
+
(= y fy)
|
|
259
|
+
(if side
|
|
260
|
+
(= x 6)
|
|
261
|
+
(= width (- fx (+ over 12)))
|
|
262
|
+
else
|
|
263
|
+
(= x (+ fx pw over 6))
|
|
264
|
+
(= width (- SCREEN_WIDTH (+ x 6)))))
|
|
213
265
|
(super say: txt &rest whoCares)
|
|
214
|
-
(self showPart:
|
|
215
|
-
(self showPart:
|
|
216
|
-
(self showPart: eyes (+ priority 1))
|
|
266
|
+
(self showPart: back (- priority 1))
|
|
267
|
+
(self showPart: face priority)
|
|
217
268
|
(self showPart: mouth (+ priority 1))
|
|
269
|
+
(self showPart: eyes (+ priority 1))
|
|
270
|
+
(self showPart: front (+ priority 2))
|
|
218
271
|
(if eyes (eyes setCycle: Blink))
|
|
219
272
|
(if mouth
|
|
220
273
|
(mouth setCycle: Forward)
|
|
@@ -232,11 +285,19 @@
|
|
|
232
285
|
(part init:)
|
|
233
286
|
(part plane: uiPlane setPri: pri)))
|
|
234
287
|
|
|
288
|
+
;; Off the screen, kept for the next line.
|
|
289
|
+
(method (hidePart part)
|
|
290
|
+
(if part
|
|
291
|
+
(DeleteScreenItem part)
|
|
292
|
+
(cast delete: part)))
|
|
293
|
+
|
|
235
294
|
(method (clear)
|
|
236
|
-
(
|
|
237
|
-
(
|
|
238
|
-
(
|
|
239
|
-
(
|
|
295
|
+
(self hidePart: front)
|
|
296
|
+
(self hidePart: back)
|
|
297
|
+
(self hidePart: frame)
|
|
298
|
+
(self hidePart: bust)
|
|
299
|
+
(self hidePart: eyes)
|
|
300
|
+
(self hidePart: mouth)
|
|
240
301
|
(super clear:)))
|
|
241
302
|
|
|
242
303
|
;; Says the lines of a message file for a noun, verb and condition, in sequence:
|
|
@@ -294,25 +355,53 @@
|
|
|
294
355
|
(String ARRAY_FREE text)
|
|
295
356
|
(super dispose:)))
|
|
296
357
|
|
|
297
|
-
;; Choices
|
|
298
|
-
;; gets every click (it's the `dialog`). The caller
|
|
358
|
+
;; Choices one under another in a box (the textStyle's frame around them all); the first
|
|
359
|
+
;; click on one ends it. While it's open it gets every click (it's the `dialog`). The caller
|
|
360
|
+
;; hears the choice by `choose: value`.
|
|
299
361
|
(class Menu of Obj
|
|
300
362
|
(properties
|
|
301
363
|
items 0
|
|
302
364
|
caller 0
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
365
|
+
panel 0
|
|
366
|
+
y 20 ; the top of the box
|
|
367
|
+
width 240
|
|
368
|
+
next 0) ; where the next choice goes
|
|
369
|
+
|
|
370
|
+
(method (add v txt &tmp item f)
|
|
371
|
+
(= f (textStyle frame?))
|
|
372
|
+
(if (not items)
|
|
373
|
+
(= items (List new:))
|
|
374
|
+
(= next (+ y (if (!= f -1) (CelHigh f 0 4) else 1))))
|
|
375
|
+
(= item
|
|
376
|
+
((MenuItem new:)
|
|
377
|
+
value: v
|
|
378
|
+
text: txt
|
|
379
|
+
frame: -1
|
|
380
|
+
borderColor: -1
|
|
381
|
+
width: (- width (if (!= f -1) (+ (CelWide f 0 6) (CelWide f 0 7)) else 2))
|
|
382
|
+
x: (+ (/ (- SCREEN_WIDTH width) 2) (if (!= f -1) (CelWide f 0 6) else 1))
|
|
383
|
+
y: next
|
|
384
|
+
yourself:))
|
|
309
385
|
(item init:)
|
|
310
|
-
(+=
|
|
386
|
+
(+= next (item height?))
|
|
311
387
|
(items add: item)
|
|
312
388
|
(return self))
|
|
313
389
|
|
|
314
|
-
|
|
390
|
+
;; The box behind the choices, then it waits for one.
|
|
391
|
+
(method (show whoCares &tmp f)
|
|
392
|
+
(= f (textStyle frame?))
|
|
315
393
|
(= caller whoCares)
|
|
394
|
+
(if items
|
|
395
|
+
(= panel
|
|
396
|
+
((TextItem new:)
|
|
397
|
+
text: ""
|
|
398
|
+
x: (/ (- SCREEN_WIDTH width) 2)
|
|
399
|
+
y: y
|
|
400
|
+
width: width
|
|
401
|
+
height: (+ (- next y) (if (!= f -1) (CelHigh f 0 5) else 1))
|
|
402
|
+
priority: 99
|
|
403
|
+
yourself:))
|
|
404
|
+
(panel init:))
|
|
316
405
|
(= dialog self))
|
|
317
406
|
|
|
318
407
|
(method (handleEvent event &tmp item v c)
|
|
@@ -336,6 +425,7 @@
|
|
|
336
425
|
(items eachElementDo: #dispose)
|
|
337
426
|
(items dispose:)
|
|
338
427
|
(= items 0))
|
|
428
|
+
(if panel (panel dispose:) (= panel 0))
|
|
339
429
|
(super dispose:)))
|
|
340
430
|
|
|
341
431
|
;; A conversation with something: talking to it opens a menu of topics, choosing one says
|
package/package.json
CHANGED
|
@@ -837,6 +837,8 @@ class RoomCompiler {
|
|
|
837
837
|
"&rest 1", `super ${p.class} 12`,
|
|
838
838
|
"pushi #setPri", "push1", "pTos priority", "ldi 1", "add", "push", `lofsa @${key}Mouth`, "send 6",
|
|
839
839
|
"pushi #setPri", "push1", "pTos priority", "ldi 1", "add", "push", `lofsa @${key}Eyes`, "send 6",
|
|
840
|
+
// Its body in the room, if a prop has its name: where it stands picks the portrait's side.
|
|
841
|
+
...(p.who && this.spec.props?.[key] ? send("self", [["who", [{ obj: key }]]]) : []),
|
|
840
842
|
"ret",
|
|
841
843
|
], "");
|
|
842
844
|
}
|
|
@@ -975,6 +975,8 @@ class RoomCompiler {
|
|
|
975
975
|
"&rest 1", `super ${p.class} 12`,
|
|
976
976
|
"pushi #setPri", "push1", "pTos priority", "ldi 1", "add", "push", `lofsa @${key}Mouth`, "send 6",
|
|
977
977
|
"pushi #setPri", "push1", "pTos priority", "ldi 1", "add", "push", `lofsa @${key}Eyes`, "send 6",
|
|
978
|
+
// Its body in the room, if a prop has its name: where it stands picks the portrait's side.
|
|
979
|
+
...(p.who && this.spec.props?.[key] ? send("self", [["who", [{ obj: key }]]]) : []),
|
|
978
980
|
"ret",
|
|
979
981
|
],
|
|
980
982
|
"",
|
|
@@ -40,7 +40,8 @@ export interface Target {
|
|
|
40
40
|
/**
|
|
41
41
|
* A character with a portrait view: loop 0 the bust, loop 1 mouth shapes, loop 2 eyes
|
|
42
42
|
* (cel 0 open), all cels the same size so the parts line up. The talker class gets
|
|
43
|
-
* `init: mouth bust eyes frame`; parts are a View (bust) and Props at `at`.
|
|
43
|
+
* `init: mouth bust eyes frame`; parts are a View (bust) and Props at `at`. With `who`,
|
|
44
|
+
* the talker is also told the character's prop in the room, if it has one (`who:`).
|
|
44
45
|
*/
|
|
45
46
|
portrait: {
|
|
46
47
|
class: string;
|
|
@@ -48,6 +49,7 @@ export interface Target {
|
|
|
48
49
|
at: [number, number];
|
|
49
50
|
frameSignal: number;
|
|
50
51
|
partSignal: number;
|
|
52
|
+
who?: boolean;
|
|
51
53
|
};
|
|
52
54
|
/**
|
|
53
55
|
* Conversations: an instance of this class runs a menu of topics for a thing (e.g. a
|
|
@@ -37,9 +37,10 @@ export interface Target {
|
|
|
37
37
|
/**
|
|
38
38
|
* A character with a portrait view: loop 0 the bust, loop 1 mouth shapes, loop 2 eyes
|
|
39
39
|
* (cel 0 open), all cels the same size so the parts line up. The talker class gets
|
|
40
|
-
* `init: mouth bust eyes frame`; parts are a View (bust) and Props at `at`.
|
|
40
|
+
* `init: mouth bust eyes frame`; parts are a View (bust) and Props at `at`. With `who`,
|
|
41
|
+
* the talker is also told the character's prop in the room, if it has one (`who:`).
|
|
41
42
|
*/
|
|
42
|
-
portrait: { class: string; props: Record<string, number>; at: [number, number]; frameSignal: number; partSignal: number };
|
|
43
|
+
portrait: { class: string; props: Record<string, number>; at: [number, number]; frameSignal: number; partSignal: number; who?: boolean };
|
|
43
44
|
/**
|
|
44
45
|
* Conversations: an instance of this class runs a menu of topics for a thing (e.g. a
|
|
45
46
|
* Teller with `init: client modNum sayNoun verb rootNoun`; topic labels are messages at
|
package/tools/game/target.js
CHANGED
|
@@ -31,8 +31,8 @@ export function libraryTarget(globals, items = {}) {
|
|
|
31
31
|
heroTalker: 98,
|
|
32
32
|
firstRoomTalker: 200,
|
|
33
33
|
voice: { class: "Talker", props: {} },
|
|
34
|
-
// Portraits stand at the top left
|
|
35
|
-
portrait: { class: "PortraitTalker", props: {}, at: [8, 8], frameSignal: 0, partSignal: 0 },
|
|
34
|
+
// Portraits stand at the top, left or right by where the character is (who:).
|
|
35
|
+
portrait: { class: "PortraitTalker", props: {}, at: [8, 8], frameSignal: 0, partSignal: 0, who: true },
|
|
36
36
|
teller: { class: "Teller", props: {}, verb: 2 },
|
|
37
37
|
forwardCycle: "Forward",
|
|
38
38
|
closeUp: "CloseUp",
|
package/tools/game/target.ts
CHANGED
|
@@ -33,8 +33,8 @@ export function libraryTarget(globals: readonly string[], items: Record<string,
|
|
|
33
33
|
heroTalker: 98,
|
|
34
34
|
firstRoomTalker: 200,
|
|
35
35
|
voice: { class: "Talker", props: {} },
|
|
36
|
-
// Portraits stand at the top left
|
|
37
|
-
portrait: { class: "PortraitTalker", props: {}, at: [8, 8], frameSignal: 0, partSignal: 0 },
|
|
36
|
+
// Portraits stand at the top, left or right by where the character is (who:).
|
|
37
|
+
portrait: { class: "PortraitTalker", props: {}, at: [8, 8], frameSignal: 0, partSignal: 0, who: true },
|
|
38
38
|
teller: { class: "Teller", props: {}, verb: 2 },
|
|
39
39
|
forwardCycle: "Forward",
|
|
40
40
|
closeUp: "CloseUp",
|