From a56705ac019cf34dd24a8f197eb7e315396cd9f8 Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Thu, 8 Jul 2021 23:36:15 -0400 Subject: [PATCH] Restructure presentation, add button support --- editor/imstate.fnl | 11 +- neuttower/defs.fnl | 2 +- presentation/engine.fnl | 15 +- presentation/pics/retro-game-dev-quote.png | Bin 0 -> 28003 bytes presentation/slides.fnl | 191 ++++++++++++--------- 5 files changed, 130 insertions(+), 89 deletions(-) create mode 100644 presentation/pics/retro-game-dev-quote.png 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 0000000000000000000000000000000000000000..e0c2043f31ebddba025001adc44e9d8e5db10b9e GIT binary patch literal 28003 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

3ibYr&2Kv@K|-<4mtl#c~w1wQuV0*|QJ~jf_^B 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:/" + (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"] +