Restructure presentation, add button support
This commit is contained in:
parent
17375a5929
commit
a56705ac01
|
@ -95,11 +95,12 @@
|
||||||
(activate view tag x y w h)
|
(activate view tag x y w h)
|
||||||
(values (and (active? view tag) (= view.imstate.left :released) (mouse-inside x y w h)) (+ y h style.padding.y)))
|
(values (and (active? view tag) (= view.imstate.left :released) (mouse-inside x y w h)) (+ y h style.padding.y)))
|
||||||
|
|
||||||
(fn textbutton [view label x y]
|
(fn textbutton [view label x y ?font]
|
||||||
(local (w h) (values (+ (style.font:get_width label) style.padding.x) (+ (style.font:get_height) style.padding.y)))
|
(let [font (or ?font style.font)]
|
||||||
|
(local (w h) (values (+ (font:get_width label) style.padding.x) (+ (font:get_height) style.padding.y)))
|
||||||
(renderer.draw_rect x y w h style.selection)
|
(renderer.draw_rect x y w h style.selection)
|
||||||
(renderer.draw_text style.font label (+ x (/ style.padding.x 2)) (+ y (/ style.padding.y 2)) style.text)
|
(renderer.draw_text font label (+ x (/ style.padding.x 2)) (+ y (/ style.padding.y 2)) style.text)
|
||||||
(values (button view label x y w h) (+ y h)))
|
(values (button view label x y w h) (+ y h))))
|
||||||
|
|
||||||
(fn checkbox [view name isset x y ?tag]
|
(fn checkbox [view name isset x y ?tag]
|
||||||
(love.graphics.rectangle (if isset :fill :line) x y (* 12 SCALE) (* 12 SCALE))
|
(love.graphics.rectangle (if isset :fill :line) x y (* 12 SCALE) (* 12 SCALE))
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
[:jmp (if (= (or map.moveword "") "") :move-noop map.moveword)]
|
[:jmp (if (= (or map.moveword "") "") :move-noop map.moveword)]
|
||||||
[:jmp (if (= (or map.loadword "") "") :next map.loadword)]))
|
[:jmp (if (= (or map.loadword "") "") :next map.loadword)]))
|
||||||
|
|
||||||
(vm.code:append :map-ptr [:db 0] :map-page [:db 0])
|
(vm.code:append :map-ptr [:db 0] [:hot-preserve :map-page [:db 0]])
|
||||||
(vm:word :map :lit :map-ptr :get)
|
(vm:word :map :lit :map-ptr :get)
|
||||||
(vm:word :entity-count :map 240 :+ :bget)
|
(vm:word :entity-count :map 240 :+ :bget)
|
||||||
(vm:word :map-jaye-yx :map 241 :+ :get)
|
(vm:word :map-jaye-yx :map 241 :+ :get)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
(local style (require :core.style))
|
(local style (require :core.style))
|
||||||
(local common (require :core.common))
|
(local common (require :core.common))
|
||||||
(local View (require :core.view))
|
(local View (require :core.view))
|
||||||
|
(local {: attach-imstate : textbutton} (require :editor.imstate))
|
||||||
|
|
||||||
(local SlideshowView (View:extend))
|
(local SlideshowView (View:extend))
|
||||||
(fn SlideshowView.parse [slides]
|
(fn SlideshowView.parse [slides]
|
||||||
|
@ -10,11 +11,12 @@
|
||||||
(icollect [_ elem (ipairs slide)]
|
(icollect [_ elem (ipairs slide)]
|
||||||
(match (type elem)
|
(match (type elem)
|
||||||
(where :table elem.style) (do (set style elem) nil)
|
(where :table elem.style) (do (set style elem) nil)
|
||||||
:table elem
|
:table (if elem.button (lume.merge style elem) elem)
|
||||||
:string (lume.merge style {:text elem})))))
|
:string (lume.merge style {:text elem})))))
|
||||||
|
|
||||||
(fn SlideshowView.new [self slides]
|
(fn SlideshowView.new [self slides]
|
||||||
(SlideshowView.super.new self)
|
(SlideshowView.super.new self)
|
||||||
|
(attach-imstate self)
|
||||||
(set self.slides slides)
|
(set self.slides slides)
|
||||||
(set self.imagecache {})
|
(set self.imagecache {})
|
||||||
(set self.islide 1)
|
(set self.islide 1)
|
||||||
|
@ -87,7 +89,16 @@
|
||||||
lines))
|
lines))
|
||||||
|
|
||||||
(fn SlideshowView.render-element [self element y]
|
(fn SlideshowView.render-element [self element y]
|
||||||
(if element.text
|
(if element.button
|
||||||
|
(let [(pressed yNext) (textbutton self
|
||||||
|
element.text
|
||||||
|
(+ self.position.x (self:justify element (element.font:get_width element.text)))
|
||||||
|
y
|
||||||
|
element.font)]
|
||||||
|
(when pressed (element:button))
|
||||||
|
(self:next-y element (- yNext y) y))
|
||||||
|
|
||||||
|
element.text
|
||||||
(let [lines (self:word-wrap element)
|
(let [lines (self:word-wrap element)
|
||||||
line-height (element.font:get_height)
|
line-height (element.font:get_height)
|
||||||
full-height (+ (* line-height (length lines)) (* style.padding.y (- (length lines) 1)))]
|
full-height (+ (* line-height (length lines)) (* style.padding.y (- (length lines) 1)))]
|
||||||
|
|
BIN
presentation/pics/retro-game-dev-quote.png
Normal file
BIN
presentation/pics/retro-game-dev-quote.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -9,6 +9,7 @@
|
||||||
(local FontEditView (require :editor.fontedit))
|
(local FontEditView (require :editor.fontedit))
|
||||||
(local ScreenEditView (require :editor.screenedit))
|
(local ScreenEditView (require :editor.screenedit))
|
||||||
(local files (require :game.files))
|
(local files (require :game.files))
|
||||||
|
(local link (require :link))
|
||||||
|
|
||||||
(local h
|
(local h
|
||||||
{:style true
|
{:style true
|
||||||
|
@ -24,8 +25,8 @@
|
||||||
:justify :left
|
:justify :left
|
||||||
:lowerPadding 7
|
:lowerPadding 7
|
||||||
:pause-after true})
|
:pause-after true})
|
||||||
(fn p [style] (lume.merge style {:pause-after true}))
|
(fn p [style ?text] (lume.merge style {:pause-after true} (if ?text {:text ?text :style false})))
|
||||||
(fn np [style] (lume.merge style {:pause-after false}))
|
(fn np [style ?text] (lume.merge style {:pause-after false} (if ?text {:text ?text :style false})))
|
||||||
|
|
||||||
(fn bgimg [filename] {:image filename :justify :center :overlay true :alpha 0.3 :topPadding 0})
|
(fn bgimg [filename] {:image filename :justify :center :overlay true :alpha 0.3 :topPadding 0})
|
||||||
|
|
||||||
|
@ -52,6 +53,21 @@
|
||||||
view)
|
view)
|
||||||
?extra))
|
?extra))
|
||||||
|
|
||||||
|
(fn boot-game []
|
||||||
|
(let [p (util.reload :game)]
|
||||||
|
(util.in-coro (fn [] (link:switch :mame)
|
||||||
|
(link.machine:run)
|
||||||
|
(util.waitfor #(link.machine:connected?))
|
||||||
|
(p:upload link.machine)
|
||||||
|
(link.machine:launch p)))
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(fn vm-eval [...]
|
||||||
|
(let [prg (require :game)
|
||||||
|
overlay (prg.vm:gen-eval-prg [:vm ...])]
|
||||||
|
(link.machine:overlay overlay)
|
||||||
|
nil))
|
||||||
|
|
||||||
(parse [
|
(parse [
|
||||||
[h "" "" ""
|
[h "" "" ""
|
||||||
"Honeylisp"
|
"Honeylisp"
|
||||||
|
@ -63,26 +79,58 @@
|
||||||
"https://gamemaking.social/@SpindleyQ"
|
"https://gamemaking.social/@SpindleyQ"
|
||||||
"https://twitter.com/SpindleyQ"
|
"https://twitter.com/SpindleyQ"
|
||||||
{:pause-after true}]
|
{:pause-after true}]
|
||||||
|
[h "Honeylisp is hard to explain"
|
||||||
|
** "It is an experimental programming environment designed to enable a productive Apple // game development workflow"
|
||||||
|
"https://fennel-lang.org/"]
|
||||||
[(bgimg "presentation/pics/pete286.jpeg")
|
[(bgimg "presentation/pics/pete286.jpeg")
|
||||||
h "Some Background"
|
h "Some Background"
|
||||||
** "In 2019 I built a 16-bit MS-DOS game engine."
|
** "2019: Built a 16-bit MS-DOS game engine, using only retro hardware and software."]
|
||||||
"* Built on hardware"
|
|
||||||
"* Using only period-appropriate software (Turbo C, NeoPaint)"
|
|
||||||
"* Powered by Forth"
|
|
||||||
"* Integrated custom tools"
|
|
||||||
"* Interactive development via serial terminal"]
|
|
||||||
[(bgimg "presentation/pics/ggj2020.jpeg")
|
[(bgimg "presentation/pics/ggj2020.jpeg")
|
||||||
h "Neut Tower"
|
h "Neut Tower"
|
||||||
** "In 2020, I did the Global Game Jam on my 286."
|
** "2020: Created Neut Tower as part of two game jams.
|
||||||
"Finished 'Shareware Episode 1' a couple of months later."]
|
* Global Game Jam - One weekend - Feb 2020 - First two rooms
|
||||||
[h "The Idea"
|
* MS-DOS Game Jam - 1.5 months - April 2020 - 'Shareware Episode 1'"]
|
||||||
** "What if I took a similar DIY approach with modern tooling?"
|
[(bgimg "presentation/pics/boot-tower.jpeg")
|
||||||
"* I'd done Forth; what about Lisp?"
|
{:action #(files.reload :neuttower/game.json)}
|
||||||
" https://fennel-lang.org/"
|
h "Neu] [ower"
|
||||||
"* How far can I push fast iterative development?"
|
** "A small puzzle adventure game!"
|
||||||
"* Could I integrate an editor?"
|
"Magic Trick #1"
|
||||||
"* How can I leverage emulation?"]
|
{:action boot-game}
|
||||||
[h "Step 1: Assembler"
|
"--== D E M O ==--"]
|
||||||
|
[h "How It's Made"
|
||||||
|
** "Is the Apple ][ running Lisp?"
|
||||||
|
" * Not really?"
|
||||||
|
"Is the code written in Lisp?"
|
||||||
|
" * Sort of!"
|
||||||
|
"Show me some Lisp already! >:/"
|
||||||
|
(openfile :neuttower/level1.fnl {:split :right :line 42})
|
||||||
|
" * OK!"]
|
||||||
|
[h "What is this unholy abomination?"
|
||||||
|
** "Lisp and Forth?!"
|
||||||
|
{:image "presentation/pics/thinkhard.png" :justify :center}
|
||||||
|
"Not super keen on writing a complicated compiler"
|
||||||
|
" * \"Direct threaded\" inner interpreter"
|
||||||
|
"Forth allows efficient, composable, interactive code"]
|
||||||
|
[h "Why use Lisp to compile Forth?"
|
||||||
|
** "\"Immediate words\" can be Fennel functions that generate code!"
|
||||||
|
"Program can be compiled into a rich data structure"
|
||||||
|
(openfile :neuttower/level1.fnl {:split :right :line 59})
|
||||||
|
"Magic Trick #2"
|
||||||
|
(np **) "Magic Trick #3"
|
||||||
|
{:button #(vm-eval :mixed) :text ":mixed"}
|
||||||
|
{:button #(vm-eval :hires) :text ":hires"}
|
||||||
|
{:button #(vm-eval 1 2 :+ :.) :text "1 2 :+ :."}
|
||||||
|
{:button #(vm-eval :earthquake) :text ":earthquake"}
|
||||||
|
{:pause-after true}]
|
||||||
|
[h "Explain this voodoo!"
|
||||||
|
** "Directly inspired by Dagen Brock's 2016 KFest talk on GSPlus"
|
||||||
|
"Ended up using MAME - Lua plugin system exposes EVERYTHING"
|
||||||
|
"Use Jeejah nREPL server library with custom nREPL client"
|
||||||
|
"1. What if I could poke my program directly into an emulator's memory?"
|
||||||
|
"2. What if I could preserve the current runtime state but rewrite the code?"
|
||||||
|
" ... even if the data has moved?"
|
||||||
|
"3. What if I could interactively try out new code while my game was running?"]
|
||||||
|
[h "Digging Deeper: Assembler"
|
||||||
** "Represent instructions using Fennel data literals"
|
** "Represent instructions using Fennel data literals"
|
||||||
(openfile :neuttower/defs.fnl {:split :right :line 57})
|
(openfile :neuttower/defs.fnl {:split :right :line 57})
|
||||||
" [:lda 0xff]"
|
" [:lda 0xff]"
|
||||||
|
@ -90,7 +138,6 @@
|
||||||
" :loop [:bne :loop]"
|
" :loop [:bne :loop]"
|
||||||
"Lexical scope with nested blocks"
|
"Lexical scope with nested blocks"
|
||||||
" [:block :loop (generate-loop-code) [:bne :loop]]"]
|
" [:block :loop (generate-loop-code) [:bne :loop]]"]
|
||||||
; ;; DEMO before tech dive??
|
|
||||||
[h "Wait WTF Is An Assembler"
|
[h "Wait WTF Is An Assembler"
|
||||||
** "It's just converting mnemonics to bytes, right?"
|
** "It's just converting mnemonics to bytes, right?"
|
||||||
{:image "presentation/pics/assembly-markup.png" :justify :center :pause-after true}
|
{:image "presentation/pics/assembly-markup.png" :justify :center :pause-after true}
|
||||||
|
@ -101,31 +148,8 @@
|
||||||
" [:db 123] [:dw 12345] [:bytes \"HELLO WORLD\"] [:ref :hello]"
|
" [:db 123] [:dw 12345] [:bytes \"HELLO WORLD\"] [:ref :hello]"
|
||||||
"Must be able to line up bytes on page boundaries"
|
"Must be able to line up bytes on page boundaries"
|
||||||
" [:align 0x100]"]
|
" [:align 0x100]"]
|
||||||
[h "Step 2: Virtual Machine"
|
[h "The Tools"
|
||||||
{:image "presentation/pics/thinkhard.png" :justify :center}
|
** {:image "presentation/pics/retro-game-dev-quote.png" :justify :center :pause-after true}
|
||||||
** "Not super keen on writing a complicated compiler"
|
|
||||||
"I'm already very comfortable with Forth"
|
|
||||||
"Let's build a stack machine!"
|
|
||||||
"\"Direct threaded\" inner interpreter"
|
|
||||||
"\"Immediate words\" can be Fennel functions that generate code!"]
|
|
||||||
[h "Extensible Assembler??"
|
|
||||||
** "How do you turn code into bytes?"
|
|
||||||
" [:vm 1 2 :+ :.]"
|
|
||||||
"Branching?"
|
|
||||||
" (vm:if [:do-true-thing] [:do-false-thing])"
|
|
||||||
"I can even do short-circuiting OR!"
|
|
||||||
" (vm:if-or [[:dup 1 :=] [:dup 3 :=]] [:do-true-thing] [:do-false-thing])"]
|
|
||||||
[h "Step 3: Tooling And Workflow"
|
|
||||||
** "I want an environment that makes it easy to make graphical tools"
|
|
||||||
"I'm SO tired of web tech"
|
|
||||||
"Could LÖVE2D with an imgui work?"]
|
|
||||||
[h "lite"
|
|
||||||
** "A small, highly-extensible text editor written in Lua"
|
|
||||||
"Backend is SDL"
|
|
||||||
"Could I rewrite it to run under LÖVE2D?"
|
|
||||||
" Yes! In a weekend!"]
|
|
||||||
[h "Custom Editors"
|
|
||||||
** "Retro game programming is just the process of writing a series of barely-usable custom paint programs."
|
|
||||||
{:action #(files.reload :neuttower/game.json)}
|
{:action #(files.reload :neuttower/game.json)}
|
||||||
"14x16 tile editor"
|
"14x16 tile editor"
|
||||||
(openview #(TileEditView))
|
(openview #(TileEditView))
|
||||||
|
@ -138,46 +162,20 @@
|
||||||
"Full-screen bitmap editor"
|
"Full-screen bitmap editor"
|
||||||
(openview #(ScreenEditView :neuttower/title.screen) {:pause-after true})
|
(openview #(ScreenEditView :neuttower/title.screen) {:pause-after true})
|
||||||
(openfile :presentation/slides.fnl {:split :right :line 133})
|
(openfile :presentation/slides.fnl {:split :right :line 133})
|
||||||
"Presentation viewer!?"]
|
"Presentation viewer"]
|
||||||
[h "Editing Editors With My Editor"
|
[h "Editing Editors With My Editor"
|
||||||
** "Lua provides a very dynamic environment that allows me tremendous flexibility"
|
** "lite is a small, highly-extensible text editor written in Lua"
|
||||||
|
"Lua provides a very dynamic environment"
|
||||||
(openview #(MapEditView))
|
(openview #(MapEditView))
|
||||||
(openfile :editor/mapedit.fnl {:split :right :line 235})
|
(openfile :editor/mapedit.fnl {:split :right :line 235})
|
||||||
"Downside:"
|
"Downside:"
|
||||||
{:image "presentation/pics/bsod.png" :justify :center :pause-after true}]
|
{:image "presentation/pics/bsod.png" :justify :center :pause-after true}]
|
||||||
[h "Step 4: Emulator Integration"
|
[(bgimg "presentation/pics/bitsy.png")
|
||||||
** "Directly inspired by Dagen Brock's 2016 KFest talk on GSPlus"
|
{:action #(files.reload :bitsy/game.json)}
|
||||||
"Ended up using MAME - has a full Lua plugin system"
|
h "8-Bitsy"
|
||||||
"What if I could poke my program directly into an emulator's memory?"
|
** "Bitsy is a popular free, accessible, web-based game-making tool"
|
||||||
"What if I could preserve the current runtime state but rewrite the code?"
|
{:action boot-game :pause-after true}
|
||||||
"... even if the data has moved?"
|
(openview #(MapEditView) {:pause-after true})]
|
||||||
"What if I could interactively try out new code while my game was running?"]
|
|
||||||
[h "Step 5: Running on Hardware"
|
|
||||||
** "I have a IIgs with a serial cable - I can poke bytes in directly from the monitor"
|
|
||||||
"]IN#2\n]PR#2\n]CALL-151"
|
|
||||||
"Easy to send bytes faster than the monitor can process them"]
|
|
||||||
[h "Audio"
|
|
||||||
** "I have a II+ with a cassette port"
|
|
||||||
"LÖVE2D is a game engine - my editor can generate audio and play it back immediately"
|
|
||||||
"Need to generate a BASIC program to bootstrap my machine code"
|
|
||||||
(openfile :asm/tape.fnl {:split :right})
|
|
||||||
" [:basic [10 :call :2061]]"
|
|
||||||
"Future work: Apple Game Server fastloader"]
|
|
||||||
[(bgimg "presentation/pics/beneath-apple-prodos.png")
|
|
||||||
h "ProDOS"
|
|
||||||
** "Disk image is a must-have for distribution"
|
|
||||||
(openfile :asm/prodos.fnl {:split :right :line 132})
|
|
||||||
"Of course I wrote my own disk image generation code!"
|
|
||||||
"Start with a blank ProDOS disk and add to it"
|
|
||||||
"Fun bugs!"
|
|
||||||
"* Accidentally implemented undelete instead of inserting new files at first"
|
|
||||||
"* Read the free space bitmap backwards and overwrote the OS"
|
|
||||||
"* Tried to name a volume starting with a number"]
|
|
||||||
[(bgimg "presentation/pics/boot-tower.jpeg")
|
|
||||||
{:action #(files.reload :neuttower/game.json)}
|
|
||||||
h "Neu] [ower"
|
|
||||||
** "A small puzzle adventure game!"
|
|
||||||
"--== D E M O ==--"]
|
|
||||||
[(bgimg "presentation/pics/bitsy.png")
|
[(bgimg "presentation/pics/bitsy.png")
|
||||||
{:action #(files.reload :bitsy/game.json)}
|
{:action #(files.reload :bitsy/game.json)}
|
||||||
h "8-Bitsy"
|
h "8-Bitsy"
|
||||||
|
@ -187,5 +185,36 @@
|
||||||
(openview #(MapEditView) {:pause-after true})]
|
(openview #(MapEditView) {:pause-after true})]
|
||||||
[h "Thanks!"
|
[h "Thanks!"
|
||||||
(openfile :neuttower/level6.fnl {:split :right :line 153})
|
(openfile :neuttower/level6.fnl {:split :right :line 153})
|
||||||
** "Questions?"]
|
** "Questions?"
|
||||||
|
(np **) {:topPadding 128}
|
||||||
|
"Jeremy Penner"
|
||||||
|
"https://spindleyq.itch.io/"
|
||||||
|
"https://blog.information-superhighway.net/"
|
||||||
|
"https://bitbucket.org/SpindleyQ/honeylisp"
|
||||||
|
"https://gamemaking.social/@SpindleyQ"
|
||||||
|
"https://twitter.com/SpindleyQ"
|
||||||
|
{:pause-after true}]
|
||||||
])
|
])
|
||||||
|
|
||||||
|
; [h "Step 5: Running on Hardware"
|
||||||
|
; ** "I have a IIgs with a serial cable - I can poke bytes in directly from the monitor"
|
||||||
|
; "]IN#2\n]PR#2\n]CALL-151"
|
||||||
|
; "Easy to send bytes faster than the monitor can process them"]
|
||||||
|
; [h "Audio"
|
||||||
|
; ** "I have a II+ with a cassette port"
|
||||||
|
; "LÖVE2D is a game engine - my editor can generate audio and play it back immediately"
|
||||||
|
; "Need to generate a BASIC program to bootstrap my machine code"
|
||||||
|
; (openfile :asm/tape.fnl {:split :right})
|
||||||
|
; " [:basic [10 :call :2061]]"
|
||||||
|
; "Future work: Apple Game Server fastloader"]
|
||||||
|
; [(bgimg "presentation/pics/beneath-apple-prodos.png")
|
||||||
|
; h "ProDOS"
|
||||||
|
; ** "Disk image is a must-have for distribution"
|
||||||
|
; (openfile :asm/prodos.fnl {:split :right :line 132})
|
||||||
|
; "Of course I wrote my own disk image generation code!"
|
||||||
|
; "Start with a blank ProDOS disk and add to it"
|
||||||
|
; "Fun bugs!"
|
||||||
|
; "* Accidentally implemented undelete instead of inserting new files at first"
|
||||||
|
; "* Read the free space bitmap backwards and overwrote the OS"
|
||||||
|
; "* Tried to name a volume starting with a number"]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue