sci2-ts 0.8.0 → 0.8.1

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,16 @@ 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.8.1](https://github.com/rlueder/sci-ts/compare/v0.8.0...v0.8.1) (2026-10-02)
8
+
9
+ ### Fixed
10
+
11
+ * timers past 32767 cycles, waits on missing sounds, methods hidden by properties ([#9](https://github.com/rlueder/sci-ts/issues/9)) ([7ec09d7](https://github.com/rlueder/sci-ts/commit/7ec09d765ab2818cf0171d5e96d9fa1b6744b576))
12
+
13
+ ### Documentation
14
+
15
+ * AGENTS.md for people and agents working here ([#8](https://github.com/rlueder/sci-ts/issues/8)) ([42bec1f](https://github.com/rlueder/sci-ts/commit/42bec1fafbb2798b26539923608882fef0c5118d))
16
+
7
17
  ## [0.8.0](https://github.com/rlueder/sci-ts/compare/v0.7.0...v0.8.0) (2026-10-02)
8
18
 
9
19
  ### Added
package/lib/996.sc CHANGED
@@ -100,7 +100,8 @@
100
100
  (properties
101
101
  caller 0
102
102
  box 0
103
- until 0 ; the game time it goes
103
+ shownAt 0 ; the game time it went up
104
+ readFor 0 ; how long it stays (-1: until dismissed)
104
105
  font -1 ; -1: the textStyle's
105
106
  x -1 ; -1: centred
106
107
  y 16
@@ -120,19 +121,21 @@
120
121
  yourself:))
121
122
  (box init:)
122
123
  (= talking self)
123
- (= until (self readingTime: (String STRING_LENGTH txt))))
124
+ (= shownAt gameTime)
125
+ (= readFor (self readingTime: (String STRING_LENGTH txt))))
124
126
 
125
- ;; Game time to read a line of n characters: normally two seconds, and more for longer
126
- ;; lines; -1 is until the player dismisses it.
127
+ ;; Cycles to read a line of n characters: normally two seconds, and more for longer lines;
128
+ ;; -1 is until the player dismisses it.
127
129
  (method (readingTime n)
128
130
  (switch textSpeed
129
- (TEXT_SLOW (return (+ gameTime 180 (* 5 n))))
130
- (TEXT_FAST (return (+ gameTime 70 (* 2 n))))
131
+ (TEXT_SLOW (return (+ 180 (* 5 n))))
132
+ (TEXT_FAST (return (+ 70 (* 2 n))))
131
133
  (TEXT_CLICK (return -1))
132
- (else (return (+ gameTime 120 (* 3 n))))))
134
+ (else (return (+ 120 (* 3 n))))))
133
135
 
136
+ ;; Time is measured as cycles since, which stays right when gameTime wraps past 32767.
134
137
  (method (doit)
135
- (if (and box (!= until -1) (>= gameTime until)) (self done:)))
138
+ (if (and box (!= readFor -1) (>= (- gameTime shownAt) readFor)) (self done:)))
136
139
 
137
140
  ;; A click, Enter, Space or "." while a line is up dismisses it (and nothing else).
138
141
  (method (handleEvent event)
@@ -174,7 +177,7 @@
174
177
  (properties
175
178
  mouth 0 bust 0 eyes 0 frame 0
176
179
  priority 150
177
- mouthUntil 0) ; the game time the mouth stops
180
+ mouthFor 0) ; cycles the mouth moves for
178
181
 
179
182
  (method (init theMouth theBust theEyes theFrame)
180
183
  (= mouth theMouth)
@@ -198,10 +201,10 @@
198
201
  (if mouth
199
202
  (mouth setCycle: Forward)
200
203
  ;; About as long as it takes to say: half a second, and two cycles a letter.
201
- (= mouthUntil (+ gameTime 30 (* 2 (String STRING_LENGTH txt))))))
204
+ (= mouthFor (+ 30 (* 2 (String STRING_LENGTH txt))))))
202
205
 
203
206
  (method (doit)
204
- (if (and mouth (mouth cycler?) (>= gameTime mouthUntil))
207
+ (if (and mouth (mouth cycler?) (>= (- gameTime shownAt) mouthFor))
205
208
  (mouth setCycle: 0 setCel: 0))
206
209
  (super doit:))
207
210
 
package/lib/997.sc CHANGED
@@ -173,18 +173,20 @@
173
173
  ;; (shut) and back to 0.
174
174
  (class Blink of Cycle
175
175
  (properties
176
- nextBlink 0)
176
+ restSince 0
177
+ restFor 0)
177
178
 
178
179
  (method (init who)
179
180
  (super init: who)
180
181
  (self rest:))
181
182
 
182
183
  (method (rest)
183
- (= nextBlink (+ gameTime 90 (Random 0 90))))
184
+ (= restSince gameTime)
185
+ (= restFor (+ 90 (Random 0 90))))
184
186
 
185
187
  (method (doit &tmp c)
186
188
  (if (== (client cel?) 0)
187
- (if (and (>= gameTime nextBlink) (> (self lastCel:) 0))
189
+ (if (and (>= (- gameTime restSince) restFor) (> (self lastCel:) 0))
188
190
  (= cycleCnt gameTime)
189
191
  (client cel: 1))
190
192
  else
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sci2-ts",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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",
@@ -95,6 +95,27 @@ export function compileScripts(sources, ctx) {
95
95
  defined.add(m.name);
96
96
  }
97
97
  }
98
+ // A method named like a property of its object: `x:` sets the property and the method
99
+ // never runs.
100
+ const propertiesOf = (name, seen = new Set()) => {
101
+ if (!name || seen.has(name))
102
+ return new Set(OBJECT_HEADER);
103
+ seen.add(name);
104
+ const c = classes.get(name);
105
+ if (c)
106
+ return new Set([...c.obj.props.map((p) => p.name), ...propertiesOf(c.obj.superName, seen)]);
107
+ return new Set([...OBJECT_HEADER, ...(ctx.externalClass?.(name)?.properties.map((p) => p.name) ?? [])]);
108
+ };
109
+ for (const u of units) {
110
+ for (const o of u.objects) {
111
+ const props = new Set([...o.props.map((p) => p.name), ...propertiesOf(o.superName)]);
112
+ for (const m of o.methods) {
113
+ if (!props.has(m.name))
114
+ continue;
115
+ ctx.onWarning({ message: `method ${m.name}: ${o.name} has a property called ${m.name}, so ${m.name}: sets it and this never runs`, file: u.file, line: m.line });
116
+ }
117
+ }
118
+ }
98
119
  const reported = new Set();
99
120
  for (const s of project.sent) {
100
121
  if (defined.has(s.name) || reported.has(s.name))
@@ -195,6 +195,24 @@ export function compileScripts(sources: readonly CompileSource[], ctx: CompileCo
195
195
  for (const m of o.methods) defined.add(m.name);
196
196
  }
197
197
  }
198
+ // A method named like a property of its object: `x:` sets the property and the method
199
+ // never runs.
200
+ const propertiesOf = (name: string | undefined, seen = new Set<string>()): Set<string> => {
201
+ if (!name || seen.has(name)) return new Set(OBJECT_HEADER);
202
+ seen.add(name);
203
+ const c = classes.get(name);
204
+ if (c) return new Set([...c.obj.props.map((p) => p.name), ...propertiesOf(c.obj.superName, seen)]);
205
+ return new Set([...OBJECT_HEADER, ...(ctx.externalClass?.(name)?.properties.map((p) => p.name) ?? [])]);
206
+ };
207
+ for (const u of units) {
208
+ for (const o of u.objects) {
209
+ const props = new Set([...o.props.map((p) => p.name), ...propertiesOf(o.superName)]);
210
+ for (const m of o.methods) {
211
+ if (!props.has(m.name)) continue;
212
+ ctx.onWarning({ message: `method ${m.name}: ${o.name} has a property called ${m.name}, so ${m.name}: sets it and this never runs`, file: u.file, line: m.line });
213
+ }
214
+ }
215
+ }
198
216
  const reported = new Set<string>();
199
217
  for (const s of project.sent) {
200
218
  if (defined.has(s.name) || reported.has(s.name)) continue;
@@ -266,6 +266,10 @@ export const audioKernels = {
266
266
  a.play(soundKey(obj), clip, loop, volume);
267
267
  else
268
268
  a.startSong(obj, number, loop, volume);
269
+ // Neither an effect nor a song by that number: it has ended already, so the next
270
+ // update reports it and whoever waits for it carries on.
271
+ if (!clip && !a.songs.has(obj))
272
+ vm.setProp(obj, "signal", NOT_PLAYING);
269
273
  return 0;
270
274
  }
271
275
  case 9: // stop
@@ -274,6 +274,9 @@ export const audioKernels: Record<string, KernelFn> = {
274
274
  const clip = a.index.effects.get(number);
275
275
  if (clip) a.play(soundKey(obj), clip, loop, volume);
276
276
  else a.startSong(obj, number, loop, volume);
277
+ // Neither an effect nor a song by that number: it has ended already, so the next
278
+ // update reports it and whoever waits for it carries on.
279
+ if (!clip && !a.songs.has(obj)) vm.setProp(obj, "signal", NOT_PLAYING);
277
280
  return 0;
278
281
  }
279
282
  case 9: // stop