sci2-ts 0.11.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 CHANGED
@@ -4,6 +4,12 @@ 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
+
7
13
  ## [0.11.0](https://github.com/rlueder/sci-ts/compare/v0.10.0...v0.11.0) (2026-10-02)
8
14
 
9
15
  ### Added
package/lib/996.sc CHANGED
@@ -17,7 +17,10 @@
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)) ; between the text and the border or frame
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
@@ -191,34 +194,80 @@
191
194
  (super say: line &rest whoCares)))
192
195
 
193
196
  ;; Someone with a portrait: a view whose loop 0 is the bust, loop 1 the mouth and loop 2 the
194
- ;; eyes, every cel the same size and anchored at its top-left corner. The room's talker hands
195
- ;; its parts to init: mouth bust eyes frame, where frame is drawn first (the bust), placed
196
- ;; where the portrait goes (the top left of the screen). The text goes to its right. While
197
- ;; 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).
198
207
  (class PortraitTalker of Talker
199
208
  (properties
200
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
201
215
  priority 150
202
216
  mouthFor 0) ; cycles the mouth moves for
203
217
 
204
218
  (method (init theMouth theBust theEyes theFrame)
205
- (= mouth theMouth)
206
- (= bust theBust)
207
- (= eyes theEyes)
208
- (= frame theFrame))
209
-
210
- (method (say txt whoCares &tmp portrait)
211
- (self init:)
212
- (= portrait (if frame frame else bust))
213
- (if portrait
214
- (= x (+ (portrait x?) (CelWide (portrait view?) (portrait loop?) (portrait cel?)) 6))
215
- (= y (portrait y?))
216
- (= width (- SCREEN_WIDTH (+ x 6))))
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)))))
217
265
  (super say: txt &rest whoCares)
218
- (self showPart: frame priority)
219
- (self showPart: bust priority)
220
- (self showPart: eyes (+ priority 1))
266
+ (self showPart: back (- priority 1))
267
+ (self showPart: face priority)
221
268
  (self showPart: mouth (+ priority 1))
269
+ (self showPart: eyes (+ priority 1))
270
+ (self showPart: front (+ priority 2))
222
271
  (if eyes (eyes setCycle: Blink))
223
272
  (if mouth
224
273
  (mouth setCycle: Forward)
@@ -236,11 +285,19 @@
236
285
  (part init:)
237
286
  (part plane: uiPlane setPri: pri)))
238
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
+
239
294
  (method (clear)
240
- (if frame (frame dispose:))
241
- (if bust (bust dispose:))
242
- (if eyes (eyes dispose:))
243
- (if mouth (mouth dispose:))
295
+ (self hidePart: front)
296
+ (self hidePart: back)
297
+ (self hidePart: frame)
298
+ (self hidePart: bust)
299
+ (self hidePart: eyes)
300
+ (self hidePart: mouth)
244
301
  (super clear:)))
245
302
 
246
303
  ;; Says the lines of a message file for a noun, verb and condition, in sequence:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sci2-ts",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "An SCI2 adventure game engine in TypeScript, and the tools to make games with it",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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
@@ -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, the text beside them.
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",
@@ -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, the text beside them.
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",