##// END OF EJS Templates
Some DOM stuff, VIEW
naryl -
r18:6b72d87e default
parent child Browse files
Show More
@@ -1,19 +1,23 b''
1 1
2 2 <div id="qsp">
3 3 <div class="qsp-col qsp-col1">
4 4 <div id="qsp-main" class="qsp-frame">&nbsp;</div>
5 5 <div id="qsp-acts" class="qsp-frame">&nbsp;</div>
6 6 <input id="qsp-input" class="qsp-frame">
7 7 </div>
8 8 <div class="qsp-col qsp-col2">
9 9 <div id="qsp-stat" class="qsp-frame">&nbsp;</div>
10 10 <div id="qsp-objs" class="qsp-frame">&nbsp;</div>
11 11 </div>
12 12 <div class="qsp-col qsp-col3">
13 <a href="javascript:SugarQSP.lib.savegame()"><img id="qsp-btn-save"></a>
14 <a href="javascript:SugarQSP.lib.opengame()"><img id="qsp-btn-load"></a>
13 <a id="qsp-btn-save"><img></a>
14 <a id="qsp-btn-open"><img></a>
15 15 </div>
16 16 </div>
17 17
18 18 <div id="qsp-dropdown">
19 19 </div>
20
21 <div id="qsp-image-container">
22 <img id="qsp-image">
23 </div>
@@ -1,103 +1,114 b''
1 1
2 2 .qsp-frame {
3 3 border: 1px solid black;
4 4 overflow: auto;
5 5 padding: 5px;
6 6 box-sizing: border-box;
7 7 }
8 8
9 9 #qsp {
10 10 position: absolute;
11 11 display: flex;
12 12 flex-flow: row;
13 13 top: 0;
14 14 left: 0;
15 15 width: 100%;
16 16 height: 100%;
17 17 }
18 18
19 19 .qsp-col {
20 20 display: flex;
21 21 flex-flow: column;
22 22 }
23 23
24 24 .qsp-col1 {
25 25 flex: 7 7 70px;
26 26 }
27 27
28 28 .qsp-col2 {
29 29 flex: 3 3 30px;
30 30 }
31 31
32 32 .qsp-col3 {
33 33 flex: 0 0 40px;
34 34 }
35 35
36 36 #qsp-main {
37 37 flex: 6 6 60px;
38 38 }
39 39
40 40 #qsp-acts {
41 41 flex: 4 4 40px;
42 42 }
43 43
44 44 #qsp-input {
45 45 }
46 46
47 47 #qsp-stat {
48 48 flex: 5 5 50px;
49 49 }
50 50
51 51 #qsp-objs {
52 52 flex: 5 5 50px;
53 53 }
54 54
55 55 .qsp-act {
56 56 display: block;
57 57 padding: 2px;
58 58 font-size: large;
59 59 }
60 60
61 61 .qsp-act:hover {
62 62 outline: #9E9E9E outset 3px
63 63 }
64 64
65 65 /* Dropdown */
66 66
67 67 #qsp-dropdown {
68 68 display: none;
69 69 position: absolute;
70 70 background-color: #f1f1f1;
71 71 min-width: 160px;
72 72 overflow: auto;
73 73 box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
74 74 z-index: 1;
75 75 margin: auto;
76 76 top: 200;
77 77 }
78 78
79 79 #qsp-dropdown a {
80 80 color: black;
81 81 padding: 12px 16px;
82 82 text-decoration: none;
83 83 display: block;
84 84 }
85 85
86 86 #qsp-dropdown a:hover {
87 87 background-color: #ddd;
88 88 }
89 89
90 90 /* Buttons */
91 91
92 92 .qsp-col3 a, .qsp-col3 img {
93 93 width: 50px;
94 94 height: 50px;
95 95 }
96 96
97 #qsp-btn-save {
97 #qsp-btn-save img {
98 98 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAACSklEQVRoge3ZPWgUQRjG8V/iRRFU/GoEQdSohSgWWqQSooVgK9iJFiKCnYV2BkFQsBRBQQwWVsHKIoWxEOzEwmhhogbRMhhFicTvYm+JCUlmZzabRLN/OPaWeZ+ZeW7feXf2lpqampqamnRaImKP4hA2VjCP27hbpoNGwbib2IPreB+IPYY7M7SvxWYM4QNO4QqWyQxVRgdeYnnB+K5A+wUcbB7z+O14hxPx08toLRDTgfv4mjrIJFrwwMS0HsABXMTxlE6LpNYqfE7pPJIBdOKhLM1uxIiLrpG5YtC4GSLMVGGkLdD+GpfxAuvxaVL7oCzN+jCG7iKDVmHkKS5hSSBuJ3bg6hRt+Zrpa553z8bEuoQrUVXk1exwKLBI1ZpPBnAL+0KBqanVim1Ykaifji+yNfIrVphipB1n8BgjCfqZ2IrTuIZXMcIUIydxFj8TtEW4JysW52JEKWtkVHUm4EdzjChSrkgLlmJXghb6m8ep9MN4K25XjvTF3sCaElrT6McS+0w2Mirb+JWhrH4CC/0+UpjUK7Ia5/86f4IeHMHeEvPJ+4km1chHE43k9KROpCyLPrViym8/vjW/b5Jt3acjL7/RzEX5bRg3sjKg+2fK7/PmZ9aZzdTqxwYzp06IBZFaDeHUCbFgUquy1Anx35TfRWXkd+WzCBOcQxEjQ7JH0PmiHW9CQUWM9Mr+Y+osO6ME8nF7Q4FFn8T2y14VDMt2vsFfqCRbZBvTdbLXFI9CgphHyjbsVu4+EcMInuH7HI1XU1NTU7OI+QMFe2N82rtssgAAAABJRU5ErkJggg==');
99 99 }
100 100
101 #qsp-btn-load {
101 #qsp-btn-open img {
102 102 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAACCklEQVRoge3Yz6sNYRzH8deVexNxSaEoroWFUuomOceSPVnIwsbSxs6Psrgphf9AsrZR7kJWsroUlrZKKZRSl4USGouZk9Npzp1n5pm5x4/nXdNzpvP9Pp/v95nv82OGRCKRSCQS/w8zuIn3yGpe37CEQ6sedQk38BA7G/jO4BQ+YEebQZUxVfH/OxzGVSxX2D4o2mO4jmt4jpNFe6d5mPFkRbvQ0H9h6OqUtR33/xXr0celGn4/sYjXoQ5dJ3ILB7AOW2r4bcIj7GsrkEFpXWyrwwbaf15nXWqv6SqK1abuHDmPjV0EMobhBWIZt8cZVu0j2ZDNHF7gblRozdiO49g1zqDOEzmKJ7gcGVQTzmLDSgZ1Eunh2dD9aewpsXtctH3cwzn5ALysoVWlXZvhleMV5ovfU8J363mciAliRLsRg0Q244vfT3A/zgT2cQHbImIY1S4ldPk9Ii+NH8V9H08DfbfiY6BtiHYpoYmMBr4bbwN9YzfVoEELnew9+blpwBv5u0oI9wPtQrUbkWFaXqOzsZ01IFg7pLQOyp/A58igmhCsHZJInYndNsHa/0wiVWTy9/a5NjprQGvag0Qmwd462iGltdQ8lihqlVVIIn/9/CAvrajDWgTRB8VhMt1/aSljVr4RToc6hJTWioe1jujJD4rfQx2qRvuTyX1JuTIh3UQikUgkEhPnF+1xZ9hHnLjAAAAAAElFTkSuQmCC');
103 103 }
104
105 #qsp-image-container {
106 position: absolute;
107 top: 0;
108 left: 0;
109 height: 100%;
110 width: 100%;
111 display: none;
112 justify-content: center;
113 align-items: center;
114 }
@@ -1,287 +1,348 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 ;;; API deals with DOM manipulation and some bookkeeping for the
5 5 ;;; intrinsics, namely variables
6 6 ;;; API is an implementation detail and has no QSP documentation. It
7 7 ;;; doesn't call intrinsics
8 8
9 9 (setf (root api) (ps:create))
10 10
11 11 ;;; Utils
12 12
13 13 (defm (root api make-act-html) (title img)
14 14 (+ "<a class='qsp-act' href='#' onclick='SugarQSP.acts[\"" title "\"].act();'>"
15 15 title
16 16 "</a>"))
17 17
18 18 (defm (root api make-menu-item-html) (num title img loc)
19 19 (+ "<a href='#' onclick='SugarQSP.api.runMenu(" num ", \"" loc "\")();'>"
20 20 "<img src='" img "'>"
21 21 title
22 22 "</a>"))
23 23
24 (defm (root api init-dom) ()
25 ;; Save/load buttons
26 (let ((btn (document.get-element-by-id "qsp-btn-save")))
27 (setf (ps:@ btn onclick) this.savegame)
28 (setf (ps:@ btn href) "#"))
29 (let ((btn (document.get-element-by-id "qsp-btn-open")))
30 (setf (ps:@ btn onclick) this.opengame)
31 (setf (ps:@ btn href) "#"))
32 ;; Close image on click
33 (setf (ps:@ (document.get-element-by-id "qsp-image-container") onclick)
34 (this.show-image nil))
35 ;; Close the dropdown on any click
36 (setf window.onclick
37 (lambda (event)
38 (setf (ps:@ (api-call get-frame :dropdown) style display) "none"))))
39
24 40 ;; To be used in saving game
25 41 (defm (root api stash-state) (args)
26 42 (setf (root state-stash)
27 43 (*j-s-o-n.stringify
28 44 (ps:create vars (root vars)
29 45 objs (root objs)
30 46 loc-args args
31 47 main-html (ps:@
32 48 (document.get-element-by-id :qsp-main)
33 49 inner-h-t-m-l)
34 50 stat-html (ps:@
35 51 (document.get-element-by-id :qsp-stat)
36 52 inner-h-t-m-l)
37 53 next-location (root current-location))))
38 54 (values))
39 55
40 56 (defm (root api unstash-state) ()
41 57 (let ((data (*j-s-o-n.parse (root state-stash))))
42 58 (this.clear-act)
43 59 (setf (root vars) (ps:@ data vars))
44 60 (loop :for k :in (*object.keys (root vars))
45 61 :do (*object.set-prototype-of (ps:getprop (root vars) k)
46 62 (root api *var prototype)))
47 63 (setf (root objs) (ps:@ data objs))
48 64 (setf (root current-location) (ps:@ data next-location))
49 65 (setf (ps:@ (document.get-element-by-id :qsp-main) inner-h-t-m-l)
50 66 (ps:@ data main-html))
51 67 (setf (ps:@ (document.get-element-by-id :qsp-stat) inner-h-t-m-l)
52 68 (ps:@ data stat-html))
53 69 (funcall (root locs (root current-location)) (ps:@ data loc-args))
54 70 (this.update-objs)
55 71 (values)))
56 72
57 73 (defm (root api state-to-base64) ()
58 74 (btoa (encode-u-r-i-component (root state-stash))))
59 75
60 76 (defm (root api base64-to-state) (data)
61 77 (setf (root state-stash) (decode-u-r-i-component (atob data))))
62 78
63 79 ;;; Misc
64 80
65 81 (defm (root api clear-id) (id)
66 82 (setf (ps:chain document (get-element-by-id id) inner-text) ""))
67 83
68 84 (defm (root api get-id) (id)
69 85 (if (var "USEHTML" 0 :num)
70 86 (ps:chain (document.get-element-by-id id) inner-h-t-m-l)
71 87 (ps:chain (document.get-element-by-id id) inner-text)))
72 88
73 89 (defm (root api set-id) (id contents)
74 90 (if (var "USEHTML" 0 :num)
75 91 (setf (ps:chain (document.get-element-by-id id) inner-h-t-m-l) contents)
76 92 (setf (ps:chain (document.get-element-by-id id) inner-text) contents)))
77 93
78 94 (defm (root api append-id) (id contents)
79 95 (if (var "USEHTML" 0 :num)
80 96 (incf (ps:chain (document.get-element-by-id id) inner-h-t-m-l) contents)
81 97 (incf (ps:chain (document.get-element-by-id id) inner-text) contents)))
82 98
83 99 ;;; Function calls
84 100
85 101 (defm (root api init-args) (args)
86 102 (dotimes (i (length args))
87 103 (let ((arg (elt args i)))
88 104 (if (numberp arg)
89 105 (this.set-var args i :num arg)
90 106 (this.set-var args i :str arg)))))
91 107
92 108 (defm (root api get-result) ()
93 109 (if (not (equal "" (var result 0 :str)))
94 110 (var result 0 :str)
95 111 (var result 0 :num)))
96 112
97 113 ;;; Text windows
98 114
99 115 (defm (root api key-to-id) (key)
100 116 (case key
101 117 (:main "qsp-main")
102 118 (:stat "qsp-stat")
103 119 (:objs "qsp-objs")
104 120 (:acts "qsp-acts")
105 121 (:input "qsp-input")
106 122 (:dropdown "qsp-dropdown")
107 123 (t (report-error "Internal error!"))))
108 124
109 125 (defm (root api get-frame) (key)
110 126 (document.get-element-by-id (this.key-to-id key)))
111 127
112 128 (defm (root api add-text) (key text)
113 129 (this.append-id (this.key-to-id key) text))
114 130
115 131 (defm (root api get-text) (key)
116 132 (this.get-id (this.key-to-id key)))
117 133
118 134 (defm (root api clear-text) (key)
119 135 (this.clear-id (this.key-to-id key)))
120 136
121 137 (defm (root api newline) (key)
122 138 (let ((div (this.get-frame key)))
123 139 (ps:chain div (append-child (document.create-element "br")))))
124 140
125 141 (defm (root api enable-frame) (key enable)
126 142 (let ((clss (ps:getprop (this.get-frame key) 'class-list)))
127 143 (setf clss.style.display (if enable "block" "none"))
128 144 (values)))
129 145
130 146 ;;; Actions
131 147
132 148 (defm (root api add-act) (title img act)
133 149 (setf (ps:getprop (root acts) title)
134 150 (ps:create :img img :act act))
135 151 (this.update-acts))
136 152
137 153 (defm (root api del-act) (title)
138 154 (delete (ps:getprop (root acts) title))
139 155 (this.update-acts))
140 156
141 157 (defm (root api clear-act) ()
142 158 (setf (root acts) (ps:create))
143 159 (this.clear-id "qsp-acts"))
144 160
145 161 (defm (root api update-acts) ()
146 162 (this.clear-id "qsp-acts")
147 163 (ps:for-in (title (root acts))
148 164 (let ((obj (ps:getprop (root acts) title)))
149 165 (this.append-id "qsp-acts"
150 166 (this.make-act-html title (ps:getprop obj :img))))))
151 167
152 168 ;;; Variable class
153 169
154 170 (defm (root api *var) (name)
155 171 ;; From strings to numbers
156 172 (setf this.indexes (ps:create))
157 173 ;; From numbers to {num: 0, str: ""} objects
158 174 (setf this.values (list))
159 175 (values))
160 176
161 177 (defm (root api *var prototype new-value) ()
162 178 (ps:create :num 0 :str ""))
163 179
164 180 (defm (root api *var prototype index-num) (index)
165 181 (let ((num-index
166 182 (if (stringp index)
167 183 (if (in index this.indexes)
168 184 (ps:getprop this.indexes index)
169 185 (let ((n (length this.values)))
170 186 (setf (ps:getprop this.indexes index) n)
171 187 n))
172 188 index)))
173 189 (unless (in num-index this.values)
174 190 (setf (elt this.values num-index) (this.new-value)))
175 191 num-index))
176 192
177 193 (defm (root api *var prototype get) (index slot)
178 194 (unless (or index (= 0 index))
179 195 (setf index (1- (length this.values))))
180 196 (ps:getprop this.values (this.index-num index) slot))
181 197
182 198 (defm (root api *var prototype set) (index slot value)
183 199 (unless (or index (= 0 index))
184 200 (setf index (length store)))
185 201 (case slot
186 202 (:num (setf value (ps:chain *number (parse-int value))))
187 203 (:str (setf value (ps:chain value (to-string)))))
188 204 (setf (ps:getprop this.values (this.index-num index) slot) value)
189 205 (values))
190 206
191 207 (defm (root api *var prototype kill) (index)
192 208 (setf (elt this.values (this.index-num index)) (this.new-value)))
193 209
194 210 ;;; Variables
195 211
196 212 (defm (root api var-real-name) (name)
197 213 (if (= (ps:@ name 0) #\$)
198 214 (values (ps:chain name (substr 1)) :str)
199 215 (values name :num)))
200 216
201 217 (defm (root api ensure-var) (name)
202 218 (let ((store (this.var-ref name)))
203 219 (unless store
204 220 (setf store (ps:new (this.-var name)))
205 221 (setf (ps:getprop (root vars) name) store))
206 222 store))
207 223
208 224 (defm (root api var-ref) (name)
209 225 (let ((local-store (this.current-local-frame)))
210 226 (cond ((in name local-store)
211 227 (ps:getprop local-store name))
212 228 ((in name (root vars))
213 229 (ps:getprop (root vars) name))
214 230 (t nil))))
215 231
216 232 (defm (root api get-var) (name index slot)
217 233 (ps:chain (this.ensure-var name) (get index slot)))
218 234
219 235 (defm (root api set-var) (name index slot value)
220 236 (ps:chain (this.ensure-var name) (set index slot value))
221 237 (values))
222 238
223 239 (defm (root api get-array) (name)
224 240 (this.var-ref name))
225 241
226 242 (defm (root api set-array) (name value)
227 243 (let ((store (this.var-ref name)))
228 244 (setf (ps:@ store values) (ps:@ value values))
229 245 (setf (ps:@ store indexes) (ps:@ value indexes)))
230 246 (values))
231 247
232 248 (defm (root api kill-var) (name &optional index)
233 249 (if (and index (not (= 0 index)))
234 250 (ps:chain (ps:getprop (root vars) name) (kill index))
235 251 (ps:delete (ps:getprop (root vars) name)))
236 252 (values))
237 253
238 254 (defm (root api array-size) (name)
239 255 (ps:getprop (this.var-ref name) 'length))
240 256
241 257 ;;; Locals
242 258
243 259 (defm (root api push-local-frame) ()
244 260 (ps:chain (root locals) (push (ps:create)))
245 261 (values))
246 262
247 263 (defm (root api pop-local-frame) ()
248 264 (ps:chain (root locals) (pop))
249 265 (values))
250 266
251 267 (defm (root api current-local-frame) ()
252 268 (elt (root locals) (1- (length (root locals)))))
253 269
254 270 (defm (root api new-local) (name)
255 271 (let ((frame (this.current-local-frame)))
256 272 (unless (in name frame)
257 273 (setf (ps:getprop frame name) (ps:create)))
258 274 (values)))
259 275
260 276 ;;; Objects
261 277
262 278 (defm (root api update-objs) ()
263 279 (let ((elt (document.get-element-by-id "qsp-objs")))
264 280 (setf elt.inner-h-t-m-l "<ul>")
265 281 (loop :for obj :in (root objs)
266 282 :do (incf elt.inner-h-t-m-l (+ "<li>" obj)))
267 283 (incf elt.inner-h-t-m-l "</ul>")))
268 284
269 285 ;;; Menu
270 286
271 287 (defm (root api menu) (menu-data)
272 288 (let ((elt (document.get-element-by-id "qsp-dropdown"))
273 289 (i 0))
274 290 (setf elt.inner-h-t-m-l "")
275 291 (loop :for item :in menu-data
276 292 :do (incf i)
277 293 :do (incf elt.inner-h-t-m-l (this.make-menu-item-html i item.text item.icon item.loc)))
278 294 (setf elt.style.display "block")))
279 295
280 296 ;;; Content
281 297
282 298 (defm (root api clean-audio) ()
283 299 (loop :for k :in (*object.keys (root playing))
284 300 :for v := (ps:getprop (root playing) k)
285 301 :do (when (ps:@ v ended)
286 302 (ps:delete (ps:@ (root playing) k)))))
287 303
304 (defm (root api show-image) (path)
305 (let ((img (document.get-element-by-id "qsp-image")))
306 (cond (path
307 (setf img.src path)
308 (setf img.style.display "flex"))
309 (t
310 (setf img.src "")
311 (setf img.style.display "hidden")))))
312
313 ;;; Saves
314
315 (defm (root api opengame) ()
316 (let ((element (document.create-element :input)))
317 (element.set-attribute :type :file)
318 (element.set-attribute :id :qsp-opengame)
319 (element.set-attribute :tabindex -1)
320 (element.set-attribute "aria-hidden" t)
321 (setf element.style.display :block)
322 (setf element.style.visibility :hidden)
323 (setf element.style.position :fixed)
324 (setf element.onchange
325 (lambda (event)
326 (let* ((file (elt event.target.files 0))
327 (reader (ps:new (*file-reader))))
328 (setf reader.onload
329 (lambda (ev)
330 (block nil
331 (let ((target ev.current-target))
332 (unless target.result
333 (return))
334 (api-call base64-to-state target.result)
335 (api-call unstash-state)))))
336 (reader.read-as-text file))))
337 (document.body.append-child element)
338 (element.click)
339 (document.body.remove-child element)))
340
341 (defm (root api savegame) ()
342 (let ((element (document.create-element :a)))
343 (element.set-attribute :href (+ "data:text/plain;charset=utf-8," (api-call state-to-base64)))
344 (element.set-attribute :download "savegame.sav")
345 (setf element.style.display :none)
346 (document.body.append-child element)
347 (element.click)
348 (document.body.remove-child element)))
@@ -1,150 +1,165 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 ;;;; Macros implementing some intrinsics where it makes sense
5 5 ;;;; E.g. an equivalent JS function exists, or it's a direct API call
6 6
7 7 ;;; 1loc
8 8
9 9 ;;; 2var
10 10
11 11 (ps:defpsmacro killvar (varname &optional index)
12 12 `(api-call kill-var ,varname ,index))
13 13
14 14 (ps:defpsmacro killall ()
15 15 `(api-call kill-all))
16 16
17 17 ;;; 3expr
18 18
19 19 (ps:defpsmacro obj (name)
20 20 `(funcall (root objs includes) ,name))
21 21
22 22 (ps:defpsmacro loc (name)
23 23 `(funcall (root locs includes) ,name))
24 24
25 25 (ps:defpsmacro no (arg)
26 26 `(- -1 ,arg))
27 27
28 28 ;;; 4code
29 29
30 30 (ps:defpsmacro qspver ()
31 31 "0.0.1")
32 32
33 33 (ps:defpsmacro curloc ()
34 34 `(root current-location))
35 35
36 36 (ps:defpsmacro rnd ()
37 37 `(funcall (root lib rand) 1 1000))
38 38
39 39 (ps:defpsmacro qspmax (&rest args)
40 40 (if (= 1 (length args))
41 41 `(*math.max.apply nil ,@args)
42 42 `(*math.max ,@args)))
43 43
44 44 (ps:defpsmacro qspmin (&rest args)
45 45 (if (= 1 (length args))
46 46 `(*math.min.apply nil ,@args)
47 47 `(*math.min ,@args)))
48 48
49 49 ;;; 5arrays
50 50
51 51 (ps:defpsmacro arrsize (name)
52 52 `(api-call array-size ,name))
53 53
54 54 ;;; 6str
55 55
56 56 (ps:defpsmacro len (s)
57 57 `(length ,s))
58 58
59 59 (ps:defpsmacro mid (s from &optional count)
60 60 `(ps:chain ,s (substring ,from ,count)))
61 61
62 62 (ps:defpsmacro ucase (s)
63 63 `(ps:chain ,s (to-upper-case)))
64 64
65 65 (ps:defpsmacro lcase (s)
66 66 `(ps:chain ,s (to-lower-case)))
67 67
68 68 (ps:defpsmacro trim (s)
69 69 `(ps:chain ,s (trim)))
70 70
71 71 (ps:defpsmacro replace (s from to)
72 72 `(ps:chain ,s (replace ,from ,to)))
73 73
74 74 (ps:defpsmacro val (s)
75 75 `(parse-int ,s 10))
76 76
77 77 (ps:defpsmacro qspstr (n)
78 78 `(ps:chain ,n (to-string)))
79 79
80 80 ;;; 7if
81 81
82 82 ;;; 8sub
83 83
84 84 ;;; 9loops
85 85
86 86 ;; JUMP is in ps-macros.lisp (because it's a part of a huge kludge)
87 87
88 88 (ps:defpsmacro exit ()
89 89 `(return-from nil (values)))
90 90
91 91 ;;; 10dynamic
92 92
93 93 ;;; 11main
94 94
95 95 (ps:defpsmacro desc (s)
96 96 (declare (ignore s))
97 97 "")
98 98
99 99 ;;; 12stat
100 100
101 101 (ps:defpsmacro showstat (enable)
102 102 `(api-call enable-frame :stat ,enable))
103 103
104 104 ;;; 13diag
105 105
106 106 (ps:defpsmacro msg (text)
107 107 `(alert ,text))
108 108
109 109 ;;; 14act
110 110
111 111 (ps:defpsmacro showacts (enable)
112 112 `(api-call enable-frame :acts ,enable))
113 113
114 114 (ps:defpsmacro delact (name)
115 115 `(api-call del-act ,name))
116 116
117 117 (ps:defpsmacro cla ()
118 118 `(api-call clear-act))
119 119
120 120 ;;; 15objs
121 121
122 122 (ps:defpsmacro showobjs (enable)
123 123 `(api-call enable-frame :objs ,enable))
124 124
125 125 (ps:defpsmacro countobj ()
126 126 `(length (root objs)))
127 127
128 128 (ps:defpsmacro getobj (index)
129 129 `(or (elt (root objs) ,index) ""))
130 130
131 131 ;;; 16menu
132 132
133 133 ;;; 17sound
134 134
135 135 (ps:defpsmacro isplay (filename)
136 136 `(funcall (root playing includes) ,filename))
137 137
138 138 ;;; 18img
139 139
140 (ps:defpsmacro view (&optional path)
141 `(api-call show-image ,path))
142
140 143 ;;; 19input
141 144
142 145 ;;; 20time
143 146
144 147 ;;; 21local
145 148
146 149 (ps:defpsmacro local (var &optional expr)
147 150 `(progn
148 151 (api-call new-local ,(string (second var)))
149 152 ,@(when expr
150 153 `((set ,var ,expr)))))
154
155 ;;; 22for
156
157 ;;; misc
158
159 (ps:defpsmacro opengame (&optional filename)
160 (declare (ignore filename))
161 `(api-call opengame))
162
163 (ps:defpsmacro savegame (&optional filename)
164 (declare (ignore filename))
165 `(api-call savegame))
@@ -1,324 +1,292 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 ;;;; Functions and procedures defined by the QSP language.
5 5 ;;;; They can call api and deal with locations and other data directly.
6 6 ;;;; Except vars. Use VAR and SET or GET-VAR and SET-VAR api calls.
7 7
8 8 (setf (root lib) (ps:create))
9 9
10 10 ;;; 1loc
11 11
12 12 (defm (root lib goto) (target args)
13 13 (api-call clear-text :main)
14 14 (funcall (root lib xgoto) target (or args (list)))
15 15 (values))
16 16
17 17 (defm (root lib xgoto) (target args)
18 18 (api-call clear-act)
19 19 (setf (root current-location) (ps:chain target (to-upper-case)))
20 20 (api-call stash-state args)
21 21 (funcall (ps:getprop (root locs) (root current-location))
22 22 (or args (list)))
23 23 (values))
24 24
25 25 ;;; 2var
26 26
27 27 ;;; 3expr
28 28
29 29 ;;; 4code
30 30
31 31 (defm (root lib rand) (a &optional (b 1))
32 32 (let ((min (min a b))
33 33 (max (max a b)))
34 34 (+ min (ps:chain *math (random (- max min))))))
35 35
36 36 ;;; 5arrays
37 37
38 38 (defm (root lib copyarr) (to from start count)
39 39 (multiple-value-bind (to-name to-slot)
40 40 (api-call var-real-name to)
41 41 (multiple-value-bind (from-name from-slot)
42 42 (api-call var-real-name from)
43 43 (ps:for ((i start))
44 44 ((< i (min (api-call array-size from-name)
45 45 (+ start count))))
46 46 ((incf i))
47 47 (api-call set-var to-name (+ start i) to-slot
48 48 (api-call get-var from-name (+ start i) from-slot))))))
49 49
50 50 (defm (root lib arrpos) (name value &optional (start 0))
51 51 (multiple-value-bind (real-name slot)
52 52 (api-call var-real-name name)
53 53 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
54 54 (when (eq (api-call get-var real-name i slot) value)
55 55 (return i))))
56 56 -1)
57 57
58 58 (defm (root lib arrcomp) (name pattern &optional (start 0))
59 59 (multiple-value-bind (real-name slot)
60 60 (api-call var-real-name name)
61 61 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
62 62 (when (funcall (ps:getprop (api-call get-var real-name i slot) 'match) pattern)
63 63 (return i))))
64 64 -1)
65 65
66 66 ;;; 6str
67 67
68 68 (defm (root lib instr) (s subs &optional (start 1))
69 69 (+ start (ps:chain s (substring (- start 1)) (search subs))))
70 70
71 71 (defm (root lib isnum) (s)
72 72 (if (is-na-n s)
73 73 0
74 74 -1))
75 75
76 76 (defm (root lib strcomp) (s pattern)
77 77 (if (s.match pattern)
78 78 -1
79 79 0))
80 80
81 81 (defm (root lib strfind) (s pattern group)
82 82 (let* ((re (ps:new (*reg-exp pattern)))
83 83 (match (re.exec s)))
84 84 (match.group group)))
85 85
86 86 (defm (root lib strpos) (s pattern &optional (group 0))
87 87 (let* ((re (ps:new (*reg-exp pattern)))
88 88 (match (re.exec s))
89 89 (found (match.group group)))
90 90 (if found
91 91 (s.search found)
92 92 0)))
93 93
94 94 ;;; 7if
95 95
96 96 ;; Has to be a function because it always evaluates all three of its
97 97 ;; arguments
98 98 (defm (root lib iif) (cond-expr then-expr else-expr)
99 99 (if cond-expr then-expr else-expr))
100 100
101 101 ;;; 8sub
102 102
103 103 (defm (root lib gosub) (target &rest args)
104 104 (funcall (ps:getprop (root locs) target) args)
105 105 (values))
106 106
107 107 (defm (root lib func) (target &rest args)
108 108 (funcall (ps:getprop (root locs) target) args))
109 109
110 110 ;;; 9loops
111 111
112 112 ;;; 10dynamic
113 113
114 114 (defm (root lib dynamic) (block &rest args)
115 115 (funcall block args)
116 116 (values))
117 117
118 118 (defm (root lib dyneval) (block &rest args)
119 119 (funcall block args))
120 120
121 121 ;;; 11main
122 122
123 123 (defm (root lib main-p) (s)
124 124 (api-call add-text :main s)
125 125 (values))
126 126
127 127 (defm (root lib main-pl) (s)
128 128 (api-call add-text :main s)
129 129 (api-call newline :main)
130 130 (values))
131 131
132 132 (defm (root lib main-nl) (s)
133 133 (api-call newline :main)
134 134 (api-call add-text :main s)
135 135 (values))
136 136
137 137 (defm (root lib maintxt) (s)
138 138 (api-call get-text :main)
139 139 (values))
140 140
141 141 ;; For clarity (it leaves a lib.desc() call in JS)
142 142 (defm (root lib desc) (s)
143 143 "")
144 144
145 145 (defm (root lib main-clear) ()
146 146 (api-call clear-text :main)
147 147 (values))
148 148
149 149 ;;; 12stat
150 150
151 151 (defm (root lib stat-p) (s)
152 152 (api-call add-text :stat s)
153 153 (values))
154 154
155 155 (defm (root lib stat-pl) (s)
156 156 (api-call add-text :stat s)
157 157 (api-call newline :stat)
158 158 (values))
159 159
160 160 (defm (root lib stat-nl) (s)
161 161 (api-call newline :stat)
162 162 (api-call add-text :stat s)
163 163 (values))
164 164
165 165 (defm (root lib stattxt) (s)
166 166 (api-call get-text :stat)
167 167 (values))
168 168
169 169 (defm (root lib stat-clear) ()
170 170 (api-call clear-text :stat)
171 171 (values))
172 172
173 173 (defm (root lib cls) ()
174 174 (funcall (root lib stat-clear))
175 175 (funcall (root lib main-clear))
176 176 (funcall (root lib cla))
177 177 (funcall (root lib cmdclear))
178 178 (values))
179 179
180 180 ;;; 13diag
181 181
182 182 ;;; 14act
183 183
184 184 (defm (root lib curacts) ()
185 185 (let ((acts (root acts)))
186 186 (lambda ()
187 187 (setf (root acts) acts)
188 188 (values))))
189 189
190 190 ;;; 15objs
191 191
192 192 (defm (root lib addobj) (name)
193 193 (ps:chain (root objs) (push name))
194 194 (api-call update-objs)
195 195 (values))
196 196
197 197 (defm (root lib delobj) (name)
198 198 (let ((index (ps:chain (root objs) (index-of name))))
199 199 (when (> index -1)
200 200 (funcall (root lib killobj) (1+ index))))
201 201 (values))
202 202
203 203 (defm (root lib killobj) (&optional (num nil))
204 204 (if (eq nil num)
205 205 (setf (root objs) (list))
206 206 (ps:chain (root objs) (splice (1- num) 1)))
207 207 (api-call update-objs)
208 208 (values))
209 209
210 210 ;;; 16menu
211 211
212 212 (defm (root lib menu) (menu-name)
213 213 (let ((menu-data (list)))
214 214 (loop :for item :in (api-call get-array (api-call var-real-name menu-name))
215 215 :do (cond ((string= item "")
216 216 (break))
217 217 ((string= item "-:-")
218 218 (ps:chain menu-data (push :delimiter)))
219 219 (t
220 220 (let* ((tokens (ps:chain item (split ":"))))
221 221 (when (= (length tokens) 2)
222 222 (tokens.push ""))
223 223 (let* ((text (ps:chain tokens (splice 0 (- tokens.length 2)) (join ":")))
224 224 (loc (ps:getprop tokens (- tokens.length 2)))
225 225 (icon (ps:getprop tokens (- tokens.length 1))))
226 226 (ps:chain menu-data
227 227 (push (ps:create text text
228 228 loc loc
229 229 icon icon))))))))
230 230 (api-call menu menu-data)
231 231 (values)))
232 232
233 233 ;;; 17sound
234 234
235 235 (defm (root lib play) (filename &optional (volume 100))
236 236 (let ((audio (ps:new (*audio filename))))
237 237 (setf (ps:getprop (root playing) filename) audio)
238 238 (setf (ps:@ audio volume) (* volume 0.01))
239 239 (ps:chain audio (play))))
240 240
241 241 (defm (root lib close) (filename)
242 242 (funcall (root playing filename) stop)
243 243 (ps:delete (root playing filename)))
244 244
245 245 (defm (root lib closeall) ()
246 246 (loop :for k :in (*object.keys (root playing))
247 247 :for v := (ps:getprop (root playing) k)
248 248 :do (funcall v stop))
249 249 (setf (root playing) (ps:create)))
250 250
251 251 ;;; 18img
252 252
253 (defm (root lib refint) ())
254
255 (defm (root lib view) ())
253 (defm (root lib refint) ()
254 ;; "Force interface update" Uh... what exactly do we do here?
255 )
256 256
257 257 ;;; 19input
258 258
259 259 (defm (root lib showinput) ())
260 260
261 261 (defm (root lib usertxt) ())
262 262
263 263 (defm (root lib cmdclear) ())
264 264
265 265 (defm (root lib input) ())
266 266
267 267 ;;; 20time
268 268
269 269 ;; I wonder if there's a better solution than busy-wait
270 270 (defm (root lib wait) (msec)
271 271 (let* ((now (ps:new (*date)))
272 272 (exit-time (+ (funcall now.get-time) msec)))
273 273 (loop :while (< (funcall now.get-time) exit-time))))
274 274
275 275 (defm (root lib msecscount) ())
276 276
277 277 (defm (root lib settimer) ())
278 278
279 279 ;;; 21local
280 280
281 ;;; 22for
282
281 283 ;;; misc
282 284
283 285 (defm (root lib rgb) ())
284 286
285 287 (defm (root lib openqst) ())
286 288
287 289 (defm (root lib addqst) ())
288 290
289 291 (defm (root lib killqst) ())
290 292
291 (defm (root lib opengame) (&optional filename)
292 (let ((element (document.create-element :input)))
293 (element.set-attribute :type :file)
294 (element.set-attribute :id :qsp-opengame)
295 (element.set-attribute :tabindex -1)
296 (element.set-attribute "aria-hidden" t)
297 (setf element.style.display :block)
298 (setf element.style.visibility :hidden)
299 (setf element.style.position :fixed)
300 (setf element.onchange
301 (lambda (event)
302 (let* ((file (elt event.target.files 0))
303 (reader (ps:new (*file-reader))))
304 (setf reader.onload
305 (lambda (ev)
306 (block nil
307 (let ((target ev.current-target))
308 (unless target.result
309 (return))
310 (api-call base64-to-state target.result)
311 (api-call unstash-state)))))
312 (reader.read-as-text file))))
313 (document.body.append-child element)
314 (element.click)
315 (document.body.remove-child element)))
316
317 (defm (root lib savegame) (&optional filename)
318 (let ((element (document.create-element :a)))
319 (element.set-attribute :href (+ "data:text/plain;charset=utf-8," (api-call state-to-base64)))
320 (element.set-attribute :download "savegame.sav")
321 (setf element.style.display :none)
322 (document.body.append-child element)
323 (element.click)
324 (document.body.remove-child element)))
@@ -1,35 +1,32 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 (setf (root)
5 5 (ps:create
6 6 ;;; Game session state
7 7 ;; Variables
8 8 vars (ps:create)
9 9 ;; Inventory (objects)
10 10 objs (list)
11 11 ;;; Transient state
12 12 ;; Savegame data
13 13 state-stash (ps:create)
14 14 ;; List of audio files being played
15 15 playing (ps:create)
16 16 ;; Local variables stack (starts with an empty frame)
17 17 locals (list)
18 18 ;;; Game data
19 19 ;; ACTions
20 20 acts (ps:create)
21 21 ;; Locations
22 22 locs (ps:create)))
23 23
24 24 ;; Launch the game from the first location
25 25 (setf window.onload
26 26 (lambda ()
27 (api-call init-dom)
27 28 (funcall (ps:getprop (root locs)
28 29 (ps:chain *object (keys (root locs)) 0))
29 30 (list))
30 31 (values)))
31 32
32 ;; Close the dropdown on any click
33 (setf window.onclick
34 (lambda (event)
35 (setf (ps:@ (api-call get-frame :dropdown) style display) "none")))
General Comments 0
You need to be logged in to leave comments. Login now