diff --git a/editor/imstate.fnl b/editor/imstate.fnl index 8192b0c..baf9a1b 100644 --- a/editor/imstate.fnl +++ b/editor/imstate.fnl @@ -95,11 +95,12 @@ (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))) -(fn textbutton [view label x y] - (local (w h) (values (+ (style.font:get_width label) style.padding.x) (+ (style.font:get_height) style.padding.y))) - (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) - (values (button view label x y w h) (+ y h))) +(fn textbutton [view label x y ?font] + (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_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)))) (fn checkbox [view name isset x y ?tag] (love.graphics.rectangle (if isset :fill :line) x y (* 12 SCALE) (* 12 SCALE)) diff --git a/neuttower/defs.fnl b/neuttower/defs.fnl index 636e73d..9531c1e 100644 --- a/neuttower/defs.fnl +++ b/neuttower/defs.fnl @@ -143,7 +143,7 @@ [:jmp (if (= (or map.moveword "") "") :move-noop map.moveword)] [: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 :entity-count :map 240 :+ :bget) (vm:word :map-jaye-yx :map 241 :+ :get) diff --git a/presentation/engine.fnl b/presentation/engine.fnl index e4cd404..9f23168 100644 --- a/presentation/engine.fnl +++ b/presentation/engine.fnl @@ -2,6 +2,7 @@ (local style (require :core.style)) (local common (require :core.common)) (local View (require :core.view)) +(local {: attach-imstate : textbutton} (require :editor.imstate)) (local SlideshowView (View:extend)) (fn SlideshowView.parse [slides] @@ -10,11 +11,12 @@ (icollect [_ elem (ipairs slide)] (match (type elem) (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}))))) (fn SlideshowView.new [self slides] (SlideshowView.super.new self) + (attach-imstate self) (set self.slides slides) (set self.imagecache {}) (set self.islide 1) @@ -87,7 +89,16 @@ lines)) (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) line-height (element.font:get_height) full-height (+ (* line-height (length lines)) (* style.padding.y (- (length lines) 1)))] diff --git a/presentation/pics/retro-game-dev-quote.png b/presentation/pics/retro-game-dev-quote.png new file mode 100644 index 0000000..e0c2043 Binary files /dev/null and b/presentation/pics/retro-game-dev-quote.png differ diff --git a/presentation/slides.fnl b/presentation/slides.fnl index ba67585..9d9081e 100644 --- a/presentation/slides.fnl +++ b/presentation/slides.fnl @@ -9,6 +9,7 @@ (local FontEditView (require :editor.fontedit)) (local ScreenEditView (require :editor.screenedit)) (local files (require :game.files)) +(local link (require :link)) (local h {:style true @@ -24,8 +25,8 @@ :justify :left :lowerPadding 7 :pause-after true}) -(fn p [style] (lume.merge style {:pause-after true})) -(fn np [style] (lume.merge style {:pause-after false})) +(fn p [style ?text] (lume.merge style {:pause-after true} (if ?text {:text ?text :style 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}) @@ -52,6 +53,21 @@ view) ?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 [ [h "" "" "" "Honeylisp" @@ -63,26 +79,58 @@ "https://gamemaking.social/@SpindleyQ" "https://twitter.com/SpindleyQ" {: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") h "Some Background" - ** "In 2019 I built a 16-bit MS-DOS game engine." - "* Built on hardware" - "* Using only period-appropriate software (Turbo C, NeoPaint)" - "* Powered by Forth" - "* Integrated custom tools" - "* Interactive development via serial terminal"] + ** "2019: Built a 16-bit MS-DOS game engine, using only retro hardware and software."] [(bgimg "presentation/pics/ggj2020.jpeg") - h "Neut Tower" - ** "In 2020, I did the Global Game Jam on my 286." - "Finished 'Shareware Episode 1' a couple of months later."] - [h "The Idea" - ** "What if I took a similar DIY approach with modern tooling?" - "* I'd done Forth; what about Lisp?" - " https://fennel-lang.org/" - "* How far can I push fast iterative development?" - "* Could I integrate an editor?" - "* How can I leverage emulation?"] - [h "Step 1: Assembler" + h "Neut Tower" + ** "2020: Created Neut Tower as part of two game jams. +* Global Game Jam - One weekend - Feb 2020 - First two rooms +* MS-DOS Game Jam - 1.5 months - April 2020 - 'Shareware Episode 1'"] + [(bgimg "presentation/pics/boot-tower.jpeg") + {:action #(files.reload :neuttower/game.json)} + h "Neu] [ower" + ** "A small puzzle adventure game!" + "Magic Trick #1" + {:action boot-game} + "--== 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" (openfile :neuttower/defs.fnl {:split :right :line 57}) " [:lda 0xff]" @@ -90,7 +138,6 @@ " :loop [:bne :loop]" "Lexical scope with nested blocks" " [:block :loop (generate-loop-code) [:bne :loop]]"] -; ;; DEMO before tech dive?? [h "Wait WTF Is An Assembler" ** "It's just converting mnemonics to bytes, right?" {:image "presentation/pics/assembly-markup.png" :justify :center :pause-after true} @@ -101,31 +148,8 @@ " [:db 123] [:dw 12345] [:bytes \"HELLO WORLD\"] [:ref :hello]" "Must be able to line up bytes on page boundaries" " [:align 0x100]"] - [h "Step 2: Virtual Machine" - {:image "presentation/pics/thinkhard.png" :justify :center} - ** "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." + [h "The Tools" + ** {:image "presentation/pics/retro-game-dev-quote.png" :justify :center :pause-after true} {:action #(files.reload :neuttower/game.json)} "14x16 tile editor" (openview #(TileEditView)) @@ -138,46 +162,20 @@ "Full-screen bitmap editor" (openview #(ScreenEditView :neuttower/title.screen) {:pause-after true}) (openfile :presentation/slides.fnl {:split :right :line 133}) - "Presentation viewer!?"] + "Presentation viewer"] [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)) (openfile :editor/mapedit.fnl {:split :right :line 235}) "Downside:" {:image "presentation/pics/bsod.png" :justify :center :pause-after true}] - [h "Step 4: Emulator Integration" - ** "Directly inspired by Dagen Brock's 2016 KFest talk on GSPlus" - "Ended up using MAME - has a full Lua plugin system" - "What if I could poke my program directly into an emulator's memory?" - "What if I could preserve the current runtime state but rewrite the code?" - "... even if the data has moved?" - "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") + {:action #(files.reload :bitsy/game.json)} + h "8-Bitsy" + ** "Bitsy is a popular free, accessible, web-based game-making tool" + {:action boot-game :pause-after true} + (openview #(MapEditView) {:pause-after true})] [(bgimg "presentation/pics/bitsy.png") {:action #(files.reload :bitsy/game.json)} h "8-Bitsy" @@ -187,5 +185,36 @@ (openview #(MapEditView) {:pause-after true})] [h "Thanks!" (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"] +