sci2-ts 0.4.0 → 0.5.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 +6 -0
- package/lib/991.sc +102 -0
- package/lib/993.sc +1 -1
- package/lib/995.sc +10 -1
- package/lib/998.sc +1 -0
- package/lib/999.sc +2 -1
- package/lib/resources.js +41 -0
- package/lib/resources.ts +38 -0
- package/lib/system.sh +11 -0
- package/package.json +1 -1
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.5.0](https://github.com/rlueder/sci-ts/compare/v0.4.0...v0.5.0) (2026-10-01)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* **lib:** an icon bar for players without a right button ([#3](https://github.com/rlueder/sci-ts/issues/3)) ([a28d5b2](https://github.com/rlueder/sci-ts/commit/a28d5b244417af6985c0c82c825a3af29ad68067))
|
|
12
|
+
|
|
7
13
|
## [0.4.0](https://github.com/rlueder/sci-ts/compare/v0.3.0...v0.4.0) (2026-10-01)
|
|
8
14
|
|
|
9
15
|
### Added
|
package/lib/991.sc
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
;;; The icon bar: the verbs, the item, the inventory and the game menu, for players without
|
|
2
|
+
;;; a right mouse button (touch screens, trackpads).
|
|
3
|
+
(script 991)
|
|
4
|
+
(include "system.sh")
|
|
5
|
+
|
|
6
|
+
;; An icon: a View hit anywhere in its square, not only where it's drawn. Its `actions`
|
|
7
|
+
;; says what it stands for.
|
|
8
|
+
(class Icon of View
|
|
9
|
+
(properties
|
|
10
|
+
size ICON_SIZE)
|
|
11
|
+
|
|
12
|
+
(method (onMe theX theY)
|
|
13
|
+
(return (and (>= theX x) (< theX (+ x size)) (>= theY y) (< theY (+ y size))))))
|
|
14
|
+
|
|
15
|
+
;; Slides down when the pointer reaches the top edge of the screen, or on a tap there. Its
|
|
16
|
+
;; icons are the cels of `view`, loop 0 as they are and loop 1 picked, each ICON_SIZE
|
|
17
|
+
;; square and anchored at the top-left corner: walk, look, do, talk, the inventory, the
|
|
18
|
+
;; game menu (the item in use shows its own icon). A game with its own sets view in its
|
|
19
|
+
;; init: (iconBar view: 266). Picking one, or a click below the bar, closes it.
|
|
20
|
+
(class IconBar of Obj
|
|
21
|
+
(properties
|
|
22
|
+
view ICON_BAR_VIEW
|
|
23
|
+
box 0
|
|
24
|
+
icons 0
|
|
25
|
+
height 0)
|
|
26
|
+
|
|
27
|
+
(method (show &tmp x)
|
|
28
|
+
(if box (return))
|
|
29
|
+
(= box
|
|
30
|
+
((TextItem new:)
|
|
31
|
+
text: ""
|
|
32
|
+
width: SCREEN_WIDTH
|
|
33
|
+
height: (+ ICON_SIZE (* 2 ICON_GAP) (* 2 (textStyle margin?)))
|
|
34
|
+
x: 0 y: 0
|
|
35
|
+
yourself:))
|
|
36
|
+
(if (!= (textStyle frame?) -1)
|
|
37
|
+
(box height: (+ (box height?) (CelHigh (textStyle frame?) 0 4) (CelHigh (textStyle frame?) 0 5))))
|
|
38
|
+
(box init:)
|
|
39
|
+
(= height (box height?))
|
|
40
|
+
(= icons (List new:))
|
|
41
|
+
(= x (+ (box textLeft?) ICON_GAP))
|
|
42
|
+
;; The verbs, then the item, the inventory and the menu.
|
|
43
|
+
(self addIcon: x 0 V_WALK)
|
|
44
|
+
(+= x (+ ICON_SIZE ICON_GAP))
|
|
45
|
+
(self addIcon: x 1 V_LOOK)
|
|
46
|
+
(+= x (+ ICON_SIZE ICON_GAP))
|
|
47
|
+
(self addIcon: x 2 V_DO)
|
|
48
|
+
(+= x (+ ICON_SIZE ICON_GAP))
|
|
49
|
+
(self addIcon: x 3 V_TALK)
|
|
50
|
+
(+= x (+ ICON_SIZE ICON_GAP))
|
|
51
|
+
(self addIcon: x -1 V_ITEM)
|
|
52
|
+
(+= x (+ ICON_SIZE ICON_GAP))
|
|
53
|
+
(self addIcon: x 4 ICON_INVENTORY)
|
|
54
|
+
(+= x (+ ICON_SIZE ICON_GAP))
|
|
55
|
+
(self addIcon: x 5 ICON_MENU)
|
|
56
|
+
(= dialog self))
|
|
57
|
+
|
|
58
|
+
;; An icon at x: cel c of the bar's view (-1: the item in use), standing for `what`.
|
|
59
|
+
(method (addIcon theX c what &tmp icon)
|
|
60
|
+
(if (and (== c -1) (not theItem)) (return))
|
|
61
|
+
(= icon
|
|
62
|
+
((Icon new:)
|
|
63
|
+
view: (if (== c -1) (theItem view?) else view)
|
|
64
|
+
loop: (if (and (!= c -1) (== what (user verb?))) 1 else 0)
|
|
65
|
+
cel: (if (== c -1) 0 else c)
|
|
66
|
+
actions: what
|
|
67
|
+
x: theX
|
|
68
|
+
y: (+ (box textTop?) ICON_GAP)
|
|
69
|
+
yourself:))
|
|
70
|
+
(icon init:)
|
|
71
|
+
(icon plane: uiPlane setPri: (+ (box priority?) 1))
|
|
72
|
+
(icons add: icon))
|
|
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 box (box dispose:) (= box 0)))
|
|
81
|
+
|
|
82
|
+
(method (dismiss)
|
|
83
|
+
(self hide:))
|
|
84
|
+
|
|
85
|
+
;; While it's open, the pointer going well below it closes it too.
|
|
86
|
+
(method (pointerAt theX theY)
|
|
87
|
+
(if (and box (> theY (+ height ICON_BAR_SLACK))) (self hide:)))
|
|
88
|
+
|
|
89
|
+
(method (handleEvent event &tmp icon what)
|
|
90
|
+
(event claimed: TRUE)
|
|
91
|
+
(if (!= (event type?) EV_MOUSE_DOWN) (return TRUE))
|
|
92
|
+
(= icon (icons firstTrue: #onMe (event x?) (event y?)))
|
|
93
|
+
(if (not icon)
|
|
94
|
+
(self hide:)
|
|
95
|
+
(return TRUE))
|
|
96
|
+
(= what (icon actions?))
|
|
97
|
+
(self hide:)
|
|
98
|
+
(switch what
|
|
99
|
+
(ICON_INVENTORY (inventory showSelf:))
|
|
100
|
+
(ICON_MENU (game showMenu:))
|
|
101
|
+
(else (user setVerb: what)))
|
|
102
|
+
(return TRUE)))
|
package/lib/993.sc
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
(for ((= node (FirstNode elements))) node ((= node (NextNode node)))
|
|
48
48
|
(= item (NodeValue node))
|
|
49
49
|
;; The icon keeps its item in `actions`.
|
|
50
|
-
(= icon ((
|
|
50
|
+
(= icon ((Icon new:) view: (item view?) loop: 0 cel: 0 actions: item x: x y: (+ (window y?) (window textTop?) INV_GAP) yourself:))
|
|
51
51
|
(icon init:)
|
|
52
52
|
(icon plane: uiPlane setPri: (+ (window priority?) 1))
|
|
53
53
|
(icons add: icon)
|
package/lib/995.sc
CHANGED
|
@@ -70,7 +70,11 @@
|
|
|
70
70
|
(method (doit)
|
|
71
71
|
(self showCursor:)
|
|
72
72
|
(while (GetEvent EV_ALL theEvent)
|
|
73
|
-
(self handleEvent: theEvent))
|
|
73
|
+
(self handleEvent: theEvent))
|
|
74
|
+
;; theEvent now says where the pointer is: at the top edge, the icon bar comes down.
|
|
75
|
+
(cond
|
|
76
|
+
((== dialog iconBar) (iconBar pointerAt: (theEvent x?) (theEvent y?)))
|
|
77
|
+
((and canInput (not dialog) (not talking) (<= (theEvent y?) ICON_BAR_EDGE)) (iconBar show:))))
|
|
74
78
|
|
|
75
79
|
;; The verb right-click goes to: walk, do, look, talk, the item picked (if any), walk.
|
|
76
80
|
(method (nextVerb)
|
|
@@ -101,6 +105,11 @@
|
|
|
101
105
|
(if (& (event modifiers?) MOD_RIGHT)
|
|
102
106
|
(self setVerb: (self nextVerb:))
|
|
103
107
|
(return))
|
|
108
|
+
;; A tap at the top edge (touch has no pointer to hover there) opens the icon bar.
|
|
109
|
+
(if (<= (event y?) (* 3 ICON_BAR_EDGE))
|
|
110
|
+
(event claimed: TRUE)
|
|
111
|
+
(iconBar show:)
|
|
112
|
+
(return))
|
|
104
113
|
(= ex (event x?))
|
|
105
114
|
(= ey (event y?))
|
|
106
115
|
(if (== verb V_WALK)
|
package/lib/998.sc
CHANGED
package/lib/999.sc
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
[gameFlags 64] ; story flags, a bit each
|
|
31
31
|
textStyle ; how text boxes and menus look (a TextStyle)
|
|
32
32
|
inventory ; what the hero carries (an Inventory)
|
|
33
|
-
theItem
|
|
33
|
+
theItem ; the item picked to use, or 0
|
|
34
|
+
iconBar) ; the icon bar (an IconBar)
|
|
34
35
|
|
|
35
36
|
(procedure (SetFlag n)
|
|
36
37
|
(|= [gameFlags (/ n 16)] (<< 1 (mod n 16))))
|
package/lib/resources.js
CHANGED
|
@@ -106,9 +106,50 @@ function shade() {
|
|
|
106
106
|
const cel = { width, height, displaceX: width >> 1, displaceY: height - 1, skipColor: Colour.Transparent, pixels: new Uint8Array(width * height).fill(253) };
|
|
107
107
|
return writeView({ flags: 1, loops: [{ link: -1, mirror: false, cels: [cel] }], palette: undefined });
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The icon bar's icons (ICON_BAR_VIEW): walk, look, do, talk, the inventory and the game
|
|
111
|
+
* menu, each 24 pixels square and anchored at the top-left corner; loop 0 as they are,
|
|
112
|
+
* loop 1 picked (inverted). The verbs reuse their cursors' drawings.
|
|
113
|
+
*/
|
|
114
|
+
const ICON_BAR_VIEW = 990;
|
|
115
|
+
const ICON = 24;
|
|
116
|
+
const BAG = [
|
|
117
|
+
"...XXXXX...",
|
|
118
|
+
"..X.....X..",
|
|
119
|
+
"..X.....X..",
|
|
120
|
+
"XXXXXXXXXXX",
|
|
121
|
+
"XWWWWWWWWWX",
|
|
122
|
+
"XWWWWWWWWWX",
|
|
123
|
+
"XWWWXXXWWWX",
|
|
124
|
+
"XWWWWWWWWWX",
|
|
125
|
+
"XWWWWWWWWWX",
|
|
126
|
+
"XXXXXXXXXXX",
|
|
127
|
+
];
|
|
128
|
+
const MENU = ["XXXXXXXXXXX", "", "", "XXXXXXXXXXX", "", "", "XXXXXXXXXXX"].map((r) => r || "...........");
|
|
129
|
+
function iconBar() {
|
|
130
|
+
const drawings = [CURSORS[3].art, CURSORS[1].art, CURSORS[4].art, CURSORS[2].art, BAG, MENU];
|
|
131
|
+
const icon = (art, picked) => {
|
|
132
|
+
const [ink, paper] = picked ? [Colour.White, Colour.Black] : [Colour.Black, Colour.White];
|
|
133
|
+
const pixels = new Uint8Array(ICON * ICON);
|
|
134
|
+
for (let y = 0; y < ICON; y++)
|
|
135
|
+
for (let x = 0; x < ICON; x++)
|
|
136
|
+
pixels[y * ICON + x] = x === 0 || y === 0 || x === ICON - 1 || y === ICON - 1 ? Colour.Black : paper;
|
|
137
|
+
const top = (ICON - art.length) >> 1, left = (ICON - art[0].length) >> 1;
|
|
138
|
+
art.forEach((row, y) => [...row].forEach((c, x) => {
|
|
139
|
+
if (c === "X")
|
|
140
|
+
pixels[(top + y) * ICON + left + x] = ink;
|
|
141
|
+
else if (c === "W")
|
|
142
|
+
pixels[(top + y) * ICON + left + x] = paper;
|
|
143
|
+
}));
|
|
144
|
+
return { width: ICON, height: ICON, displaceX: ICON >> 1, displaceY: ICON - 1, skipColor: Colour.Transparent, pixels };
|
|
145
|
+
};
|
|
146
|
+
const loop = (picked) => ({ link: -1, mirror: false, cels: drawings.map((art) => icon(art, picked)) });
|
|
147
|
+
return writeView({ flags: 1, loops: [loop(false), loop(true)], palette: undefined });
|
|
148
|
+
}
|
|
109
149
|
export default function resources() {
|
|
110
150
|
return [
|
|
111
151
|
...Object.entries(CURSORS).map(([verb, c]) => ({ type: ResourceType.View, number: CURSOR_BASE + Number(verb), data: cursor(c.art, c.hotspot) })),
|
|
112
152
|
{ type: ResourceType.View, number: CLOSE_UP_SHADE, data: shade() },
|
|
153
|
+
{ type: ResourceType.View, number: ICON_BAR_VIEW, data: iconBar() },
|
|
113
154
|
];
|
|
114
155
|
}
|
package/lib/resources.ts
CHANGED
|
@@ -111,9 +111,47 @@ function shade() {
|
|
|
111
111
|
return writeView({ flags: 1, loops: [{ link: -1, mirror: false, cels: [cel] }], palette: undefined });
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* The icon bar's icons (ICON_BAR_VIEW): walk, look, do, talk, the inventory and the game
|
|
116
|
+
* menu, each 24 pixels square and anchored at the top-left corner; loop 0 as they are,
|
|
117
|
+
* loop 1 picked (inverted). The verbs reuse their cursors' drawings.
|
|
118
|
+
*/
|
|
119
|
+
const ICON_BAR_VIEW = 990;
|
|
120
|
+
const ICON = 24;
|
|
121
|
+
const BAG = [
|
|
122
|
+
"...XXXXX...",
|
|
123
|
+
"..X.....X..",
|
|
124
|
+
"..X.....X..",
|
|
125
|
+
"XXXXXXXXXXX",
|
|
126
|
+
"XWWWWWWWWWX",
|
|
127
|
+
"XWWWWWWWWWX",
|
|
128
|
+
"XWWWXXXWWWX",
|
|
129
|
+
"XWWWWWWWWWX",
|
|
130
|
+
"XWWWWWWWWWX",
|
|
131
|
+
"XXXXXXXXXXX",
|
|
132
|
+
];
|
|
133
|
+
const MENU = ["XXXXXXXXXXX", "", "", "XXXXXXXXXXX", "", "", "XXXXXXXXXXX"].map((r) => r || "...........");
|
|
134
|
+
function iconBar() {
|
|
135
|
+
const drawings = [CURSORS[3]!.art, CURSORS[1]!.art, CURSORS[4]!.art, CURSORS[2]!.art, BAG, MENU];
|
|
136
|
+
const icon = (art: string[], picked: boolean) => {
|
|
137
|
+
const [ink, paper] = picked ? [Colour.White, Colour.Black] : [Colour.Black, Colour.White];
|
|
138
|
+
const pixels = new Uint8Array(ICON * ICON);
|
|
139
|
+
for (let y = 0; y < ICON; y++) for (let x = 0; x < ICON; x++) pixels[y * ICON + x] = x === 0 || y === 0 || x === ICON - 1 || y === ICON - 1 ? Colour.Black : paper;
|
|
140
|
+
const top = (ICON - art.length) >> 1, left = (ICON - art[0]!.length) >> 1;
|
|
141
|
+
art.forEach((row, y) => [...row].forEach((c, x) => {
|
|
142
|
+
if (c === "X") pixels[(top + y) * ICON + left + x] = ink;
|
|
143
|
+
else if (c === "W") pixels[(top + y) * ICON + left + x] = paper;
|
|
144
|
+
}));
|
|
145
|
+
return { width: ICON, height: ICON, displaceX: ICON >> 1, displaceY: ICON - 1, skipColor: Colour.Transparent, pixels };
|
|
146
|
+
};
|
|
147
|
+
const loop = (picked: boolean) => ({ link: -1, mirror: false, cels: drawings.map((art) => icon(art, picked)) });
|
|
148
|
+
return writeView({ flags: 1, loops: [loop(false), loop(true)], palette: undefined });
|
|
149
|
+
}
|
|
150
|
+
|
|
114
151
|
export default function resources(): ResourceData[] {
|
|
115
152
|
return [
|
|
116
153
|
...Object.entries(CURSORS).map(([verb, c]) => ({ type: ResourceType.View, number: CURSOR_BASE + Number(verb), data: cursor(c.art, c.hotspot) })),
|
|
117
154
|
{ type: ResourceType.View, number: CLOSE_UP_SHADE, data: shade() },
|
|
155
|
+
{ type: ResourceType.View, number: ICON_BAR_VIEW, data: iconBar() },
|
|
118
156
|
];
|
|
119
157
|
}
|
package/lib/system.sh
CHANGED
|
@@ -26,6 +26,17 @@
|
|
|
26
26
|
|
|
27
27
|
;; Saved games: a description is up to SAVE_DESCRIPTION - 1 characters; the menus show the
|
|
28
28
|
;; newest SAVES_SHOWN. What SaveRestore's open menu is for:
|
|
29
|
+
;; The icon bar: icons are ICON_SIZE pixels square, ICON_GAP apart; it opens when the
|
|
30
|
+
;; pointer is within ICON_BAR_EDGE pixels of the top and closes ICON_BAR_SLACK below it.
|
|
31
|
+
;; Besides verbs, an icon can stand for the inventory or the game menu.
|
|
32
|
+
(define ICON_SIZE 24)
|
|
33
|
+
(define ICON_GAP 4)
|
|
34
|
+
(define ICON_BAR_VIEW 990)
|
|
35
|
+
(define ICON_BAR_EDGE 3)
|
|
36
|
+
(define ICON_BAR_SLACK 12)
|
|
37
|
+
(define ICON_INVENTORY 100)
|
|
38
|
+
(define ICON_MENU 101)
|
|
39
|
+
|
|
29
40
|
(define SAVE_DESCRIPTION 36)
|
|
30
41
|
(define SAVES_SHOWN 8)
|
|
31
42
|
(define SR_MENU 1)
|