diff --git a/extras/body.html b/extras/body.html
--- a/extras/body.html
+++ b/extras/body.html
@@ -9,6 +9,10 @@
+
+
+
+
diff --git a/extras/default.css b/extras/default.css
--- a/extras/default.css
+++ b/extras/default.css
@@ -29,6 +29,10 @@
flex: 3 3 30px;
}
+.qsp-col3 {
+ flex: 0 0 40px;
+}
+
#qsp-main {
flex: 6 6 60px;
}
@@ -58,7 +62,7 @@
outline: #9E9E9E outset 3px
}
-// Dropdown
+/* Dropdown */
#qsp-dropdown {
display: none;
@@ -82,3 +86,18 @@
#qsp-dropdown a:hover {
background-color: #ddd;
}
+
+/* Buttons */
+
+.qsp-col3 a, .qsp-col3 img {
+ width: 50px;
+ height: 50px;
+}
+
+#qsp-btn-save {
+ 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==');
+}
+
+#qsp-btn-load {
+ 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');
+}
diff --git a/src/api.ps b/src/api.ps
--- a/src/api.ps
+++ b/src/api.ps
@@ -36,6 +36,8 @@
(defm (root api base64-to-state) (data)
(setf (root state-stash) (decode-u-r-i-component (atob data)))
(let ((data (*j-s-o-n.parse (root state-stash))))
+ (api-call clear-id :qsp-main)
+ (api-call clear-id :qsp-stat)
(api-call clear-act)
(setf (root vars) (ps:@ data vars))
(setf (root objs) (ps:@ data objs))
@@ -198,3 +200,11 @@
:do (incf i)
:do (incf elt.inner-h-t-m-l (api-call make-menu-item-html i item.text item.icon item.loc)))
(setf elt.style.display "block")))
+
+;;; Content
+
+(defm (root api clean-audio) ()
+ (loop :for k :in (*object.keys (root playing))
+ :for v := (ps:getprop (root playing) k)
+ :do (when (ps:@ v ended)
+ (ps:delete (ps:@ (root playing) k)))))
diff --git a/src/intrinsic-macros.lisp b/src/intrinsic-macros.lisp
--- a/src/intrinsic-macros.lisp
+++ b/src/intrinsic-macros.lisp
@@ -128,6 +128,9 @@
;;; 17sound
+(ps:defpsmacro isplay (filename)
+ `(funcall (root playing includes) ,filename))
+
;;; 18img
;;; 19input
diff --git a/src/intrinsics.ps b/src/intrinsics.ps
--- a/src/intrinsics.ps
+++ b/src/intrinsics.ps
@@ -197,13 +197,13 @@
(defm (root lib delobj) (name)
(let ((index (ps:chain (root objs) (index-of name))))
(when (> index -1)
- (funcall (root lib killobj) index)))
+ (funcall (root lib killobj) (1+ index))))
(values))
-(defm (root lib killobj) (&optional num)
- (if num
- (ps:chain (root objs) (splice (1+ num) 1))
- (setf (root objs) (list)))
+(defm (root lib killobj) (&optional (num nil))
+ (if (eq nil num)
+ (setf (root objs) (list))
+ (ps:chain (root objs) (splice (1- num) 1)))
(api-call update-objs)
(values))
@@ -232,13 +232,21 @@
;;; 17sound
-(defm (root lib play) ())
-
-(defm (root lib isplay) ())
+(defm (root lib play) (filename &optional (volume 100))
+ (let ((audio (ps:new (*audio filename))))
+ (setf (ps:getprop (root playing) filename) audio)
+ (setf (ps:@ audio volume) (* volume 0.01))
+ (ps:chain audio (play))))
-(defm (root lib close) ())
+(defm (root lib close) (filename)
+ (funcall (root playing filename) stop)
+ (ps:delete (root playing filename)))
-(defm (root lib closeall) ())
+(defm (root lib closeall) ()
+ (loop :for k :in (*object.keys (root playing))
+ :for v := (ps:getprop (root playing) k)
+ :do (funcall v stop))
+ (setf (root playing) (ps:create)))
;;; 18img
diff --git a/src/main.ps b/src/main.ps
--- a/src/main.ps
+++ b/src/main.ps
@@ -2,11 +2,22 @@
(in-package sugar-qsp)
(setf (root)
- (ps:create vars (ps:create)
- objs (list)
- state-stash (ps:create)
- acts (ps:create)
- locs (ps:create)))
+ (ps:create
+ ;;; Game session state
+ ;; Variables
+ vars (ps:create)
+ ;; Inventory (objects)
+ objs (list)
+ ;;; Transient state
+ ;; Savegame data
+ state-stash (ps:create)
+ ;; List of audio files being played
+ playing (ps:create)
+ ;;; Game data
+ ;; ACTions
+ acts (ps:create)
+ ;; Locations
+ locs (ps:create)))
;; Launch the game from the first location
(setf window.onload