From cb02feff1d6d8d782416784a20d697d76013fb39 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sat, 6 Jul 2019 18:30:03 -0700 Subject: [PATCH] Add Switches, Fire/Water Collision and Play Menu * New doodads: Switches. * They come in four varieties: wall switch (background element, with "ON/OFF" text) and three side-profile switches for the floor, left or right walls. * On collision with the player, they flip their state from "OFF" to "ON" or vice versa. If the player walks away and then collides again, the switch flips again. * Can be used to open/close Electric Doors when turned on/off. Their default state is "off" * If a switch receives a power signal from another linked switch, it sets its own state to match. So, two "on/off" switches that are connected to a door AND to each other will both flip on/off when one of them flips. * Update the Level Collision logic to support Decoration, Fire and Water pixel collisions. * Previously, ALL pixels in the level were acting as though solid. * Non-solid pixels don't count for collision detection, but their attributes (fire and water) are collected and returned. * Updated the MenuScene to support loading a map file in Play Mode instead of Edit Mode. Updated the title screen menu to add a button for playing levels instead of editing them. * Wrote some documentation. --- Ideas.md | 10 ++-- TODO.md | 15 ++++- dev-assets/doodads/build.sh | 18 ++++++ dev-assets/doodads/doors/electric-door.js | 2 +- dev-assets/doodads/switches/down-off.png | Bin 0 -> 678 bytes dev-assets/doodads/switches/down-on.png | Bin 0 -> 674 bytes dev-assets/doodads/switches/left-off.png | Bin 0 -> 702 bytes dev-assets/doodads/switches/left-on.png | Bin 0 -> 696 bytes dev-assets/doodads/switches/right-off.png | Bin 0 -> 695 bytes dev-assets/doodads/switches/right-on.png | Bin 0 -> 702 bytes dev-assets/doodads/switches/switch-off.png | Bin 0 -> 687 bytes dev-assets/doodads/switches/switch-on.png | Bin 0 -> 699 bytes dev-assets/doodads/switches/switch.js | 38 +++++++++++++ docs/Doodad Ideas.md | 22 ++++++++ docs/Shell.md | 43 +++++++++++++- lib/render/color.go | 15 +++++ pkg/collision/collide_level.go | 29 +++++++++- pkg/level/chunk.go | 7 ++- pkg/main_scene.go | 62 +++++++++++---------- pkg/menu_scene.go | 26 ++++++++- pkg/play_scene.go | 12 ++++ pkg/uix/actor_collision.go | 3 + pkg/uix/canvas.go | 4 ++ pkg/uix/canvas_wallpaper.go | 3 + 24 files changed, 263 insertions(+), 46 deletions(-) create mode 100644 dev-assets/doodads/switches/down-off.png create mode 100644 dev-assets/doodads/switches/down-on.png create mode 100644 dev-assets/doodads/switches/left-off.png create mode 100644 dev-assets/doodads/switches/left-on.png create mode 100644 dev-assets/doodads/switches/right-off.png create mode 100644 dev-assets/doodads/switches/right-on.png create mode 100644 dev-assets/doodads/switches/switch-off.png create mode 100644 dev-assets/doodads/switches/switch-on.png create mode 100644 dev-assets/doodads/switches/switch.js create mode 100644 docs/Doodad Ideas.md diff --git a/Ideas.md b/Ideas.md index f100945..a087fce 100644 --- a/Ideas.md +++ b/Ideas.md @@ -76,10 +76,10 @@ The major milestones of the game are roughly: * Colors are not tied to behaviors. Each "Swatch" on the palette has its own color and a set of boolean flags for `solid`, `fire` and `water` behaviors. * [ ] User interface to edit (add/remove) swatches from the palette. -* [ ] A Toolbox window with radio buttons to select between various drawing tools. +* [x] A Toolbox window with radio buttons to select between various drawing tools. * [x] Pencil (the default) draws single pixels on the level. - * [ ] Rectangle would draw a rectangular outline. - * [ ] Line would draw a line from point to point. + * [x] Rectangle would draw a rectangular outline. + * [x] Line would draw a line from point to point. * [ ] A way to adjust brush properties: * [ ] Brush size, shape (round or square). * [ ] Tools to toggle "layers" of visibility into your level: @@ -98,11 +98,11 @@ The major milestones of the game are roughly: For creating Doodads in particular: -* [ ] Make a way to enter Edit Mode in either "Level Mode" or "Doodad Mode", +* [x] Make a way to enter Edit Mode in either "Level Mode" or "Doodad Mode", i.e. by a "New Level" or "New Doodad" button. * [ ] Create a "frame manager" window to see and page between the frames of the drawing. -* [ ] Ability to work on canvases with constrained size (including smaller than +* [x] Ability to work on canvases with constrained size (including smaller than your window). This will use a Canvas widget in the UI toolkit as an abstraction layer. Small canvases will be useful for drawing doodads of a fixed size. diff --git a/TODO.md b/TODO.md index b682471..29f5f66 100644 --- a/TODO.md +++ b/TODO.md @@ -47,8 +47,8 @@ - [x] Buttons - [x] Press Button - [x] Sticky Button -- [ ] Switches -- [ ] Doors +- [x] Switches (4 varieties) +- [x] Doors - [x] Locked Doors and Keys - [x] Electric Doors - [x] Trapdoors (all 4 directions) @@ -66,6 +66,7 @@ In addition to those listed above: as a level goal and ends the level. - Doodads "Warp Door A" through "Warp Door D" - The campaign.json would link levels together. +- [ ] Conveyor Belt ## New Ideas @@ -75,3 +76,13 @@ In addition to those listed above: keys only get picked up by player characters and not "any doodad that touches them" - [ ] `` + +## Path to Multiplayer + +* Add a Player abstraction between events and player characters. + * Keyboard keys would update PlayerOne's state with actions (move left, right, jump, etc) + * Possible to have multiple local players (i.e. bound to different keyboard keys, bound to joypads, etc.) +* A NetworkPlayer provides a Player's inputs from over a network. +* Client/server negotiation, protocol + * Client can request chunks from server for local rendering. + * Players send inputs over network sockets. diff --git a/dev-assets/doodads/build.sh b/dev-assets/doodads/build.sh index e06763e..97ce263 100755 --- a/dev-assets/doodads/build.sh +++ b/dev-assets/doodads/build.sh @@ -24,6 +24,23 @@ buttons() { cd .. } +switches() { + cd switches/ + + doodad convert -t "Switch" switch-off.png switch-on.png switch.doodad + doodad convert -t "Floor Switch" down-off.png down-on.png switch-down.doodad + doodad convert -t "Left Switch" left-off.png left-on.png switch-left.doodad + doodad convert -t "Right Switch" right-off.png right-on.png switch-right.doodad + + doodad install-script switch.js switch.doodad + doodad install-script switch.js switch-down.doodad + doodad install-script switch.js switch-left.doodad + doodad install-script switch.js switch-right.doodad + + cp *.doodad ../../../assets/doodads/ + cd .. +} + doors() { cd doors/ @@ -104,6 +121,7 @@ objects() { } buttons +switches doors trapdoors azulians diff --git a/dev-assets/doodads/doors/electric-door.js b/dev-assets/doodads/doors/electric-door.js index cd028b7..7ecdbdb 100644 --- a/dev-assets/doodads/doors/electric-door.js +++ b/dev-assets/doodads/doors/electric-door.js @@ -23,8 +23,8 @@ function main() { }); } else { animating = true; + opened = false; Self.PlayAnimation("close", function() { - opened = false; animating = false; }) } diff --git a/dev-assets/doodads/switches/down-off.png b/dev-assets/doodads/switches/down-off.png new file mode 100644 index 0000000000000000000000000000000000000000..2b08ca01119caaae2027d970bb24ae49a1c08094 GIT binary patch literal 678 zcmV;X0$KfuP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6C@&{2vM2xm02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{004eTL_t(o!|jzp4uBvG1P9}f_!E5-uf?kuDi~V{>740y zLI?{%5ClOGY)tc5a%cjzRu_?;L@T8L;31MU0f3w{03y;mZzAC?q6NB%JcYT4j0BmB zjE0zs%mkQ;%nqA~yczslBzg6N#(Ddie?dgl0^6^ywf#%n#1aql0x9n_*U3pNr2qf` M07*qoM6N<$f}Wol`v3p{ literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/switches/down-on.png b/dev-assets/doodads/switches/down-on.png new file mode 100644 index 0000000000000000000000000000000000000000..527b2a00edb260164d719c543b81282f1483b4d1 GIT binary patch literal 674 zcmV;T0$u%yP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6DH3K~&v*a;02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{004SPL_t(o!|l~U4ge7lMA0I1R?ftooXM=rPDXTfSkzw% z@rVSVlu}A5HLb5qik^T{$`T|x1CUbsg#=GPYwa7XwNCd7rvYrl7J!M^2Cxt&00UtL zSP>L}1wjMsBB+3|sN--C0F0BJDCg|@f$_gv&;75si7OuF3t1db*JnOlY5)KL07*qo IM6N<$g3E~+AOHXW literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/switches/left-off.png b/dev-assets/doodads/switches/left-off.png new file mode 100644 index 0000000000000000000000000000000000000000..33accebce394ec550b9d0ace9d95e9626485f569 GIT binary patch literal 702 zcmV;v0zv(WP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6DlCieSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{005RrL_t(o!|m8h3cw%?1kj0>^-S?jp2=>c2(^A%97{>A%25M+Yz~&rtRfmL2eN-MjE7<2^OTg zI~b5#LY&WYirQ81}4%Bo|*#%1FV*mgE07*qoM6N<$f)rCD>Hq)$ literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/switches/left-on.png b/dev-assets/doodads/switches/left-on.png new file mode 100644 index 0000000000000000000000000000000000000000..36d5c45d3f28fc5c43108895285f6c5f075f7599 GIT binary patch literal 696 zcmV;p0!RIcP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6DjC|MMKb^Z02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{0059lL_t(o!|mA73Bxc9MbT@maWdIGyJ>U)0000EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6Dh5}oNX7sF02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{0056kL_t(o!|m2l3cw%;24LM~JCoh1Gr5Atzn26fFlql01{yapb#zq0^tTA5L`-e0Z`w;Py1N_$iieR0NtW&4b08T dokHYxbpwNlP}l1=S?mA+002ovPDHLkV1l4T9+Ln7 literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/switches/right-on.png b/dev-assets/doodads/switches/right-on.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a196edf6077eace0e972599517ee7b5b0984cc GIT binary patch literal 702 zcmV;v0zv(WP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6DJ-Wv($fF{02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{005RrL_t(o!|m9+4Zt7_1kfWZu@lU6(>hH=g%GlhNns=! zS5JI=h=SN5v*Hts07;Mpfbz|F#4lDN5m5-ajgZ!w8uB;-poLfwLK3ysQP4oFh|+rp z029PxJIoNTgK2_zUQso~yIH9qCpzW|656(>kQ>>t4LKcPTabtp9gy2mzJx@+CrZAo k5|M~RB;r^rjr^Z@0SVAD*A>LBApigX07*qoM6N<$g0Yb$1poj5 literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/switches/switch-off.png b/dev-assets/doodads/switches/switch-off.png new file mode 100644 index 0000000000000000000000000000000000000000..4e7c006f6bf976cc9edc593b478758e238607d93 GIT binary patch literal 687 zcmV;g0#N;lP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6Co;pZ!ae{102y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{004(cL_t(Y$L*Iv3IHJpMRn+`b*8+mvzZ`TSc*|F{#AnY z>PKY=6Xl#43?d=~tE|vZd;)5NJ3&SG1BVpg0AC4a^Cb~h2ROi6@rTB@-)tpdh9zWqoY`Avv@aoemXW54I$ VMHARk@{9lg002ovPDHLkV1fZDAr=4t literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/switches/switch-on.png b/dev-assets/doodads/switches/switch-on.png new file mode 100644 index 0000000000000000000000000000000000000000..bb191ceb34088939814073122478456f4fe8a97c GIT binary patch literal 699 zcmV;s0!00ZP)EX>4Tx04R}tkv&MmKpe$iQ?()$2P=p=WN4i%h>AFB6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfadlF3krMxx6k5c1aNLh~_a1le0HI!Hs@X9PsG4P@ z;xRFkT@?eb2%rlCh+tS^raqTUCg3@~?&0I>U6f~epZjz4Dmjw@K9P8q>4rtTK|H-_ z>74h8L#!w%#OK6g23?T&k?XR{Z=4Gb3p_Jqq*L?6A!4!6#&R38qM;H`5r-93qkJLb zvch?bvs$jQ<~{ifgE?(wnd>x15XT~xkc0>sRg_SMg($5WDJD|1AM@}JIsPQMWO9|j z$gzM5R7j2={11M2Yvv~>+@xR(=zOv5j|dRh1sXNm{yw(t#tGnm2ClT0zfuQgK1r{& zwD1wow+&oew={VVxZD8-pLEHP9LY~p$mfCgGy0}1(0>c`thv24_i_3Fq^YaK4RCM> zj20++-Q(Te?Y;ebrrF;QTx@clua%s}00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru;|B&6Cm!)=uH66t02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{005IoL_t(Y$L*I<4uBvGLp$PG;Y@JZ`lE?4h;Axqvi<>K zZ);!y!HHswgh8bgfmK%MB94IC;73pb ` + +The value is truthy if its first character is the letter T or the number 1. +All other values are false. Examples: True, true, T, t, 1. + +* `Debug` or `D`: toggle debug mode within the app. +* `DebugOverlay` or `DO`: toggle the debug text overlay. +* `DebugCollision` or `DC`: toggle collision hitbox lines. + +## Interesting Tricks + +### Editable Map While Playing + +In Play Mode run the command: + +| Command | Effect | +|--------------------------------------------|----------------------------------------------------------------| +| `$ d.Scene.Drawing().Editable = true` | Can click and drag new pixels onto the level while playing it. | +| `$ d.Scene.Drawing().Scrollable = true` | Arrow keys scroll the map, like in editor mode. | +| `$ d.Scene.Drawing().NoLimitScroll = true` | Allow map to scroll beyond bounded limits. | + +The equivalent Canvas in the Edit Mode is at `d.Scene.UI.Canvas` + +### Edit Out-of-Bounds in Editor Mode + +In Edit Mode run the command: + +`$ d.Scene.UI.Canvas.NoLimitScroll = true` + +and you can scroll the map freely outside of the normal scroll boundaries. For +example, to see/edit pixels outside the top-left edges of bounded levels. diff --git a/lib/render/color.go b/lib/render/color.go index 3ec3ac5..e6b045d 100644 --- a/lib/render/color.go +++ b/lib/render/color.go @@ -211,6 +211,11 @@ func (c Color) DecodeMsgpack(dec *msgpack.Decoder) error { // return nil // } +// IsZero returns if the color is all zeroes (invisible). +func (c Color) IsZero() bool { + return c.Red+c.Green+c.Blue+c.Alpha == 0 +} + // Add a relative color value to the color. func (c Color) Add(r, g, b, a int) Color { var ( @@ -237,6 +242,16 @@ func (c Color) Add(r, g, b, a int) Color { } } +// AddColor adds another Color to your Color. +func (c Color) AddColor(other Color) Color { + return c.Add( + int(other.Red), + int(other.Green), + int(other.Blue), + int(other.Alpha), + ) +} + // Lighten a color value. func (c Color) Lighten(v int) Color { return c.Add(v, v, v, 0) diff --git a/pkg/collision/collide_level.go b/pkg/collision/collide_level.go index 11f54df..1f5acf5 100644 --- a/pkg/collision/collide_level.go +++ b/pkg/collision/collide_level.go @@ -23,6 +23,10 @@ type Collide struct { BottomPoint render.Point BottomPixel *level.Swatch MoveTo render.Point + + // Swatch attributes affecting the collision at this time. + InFire bool + InWater bool } // Reset a Collide struct flipping all the bools off, but keeping MoveTo. @@ -196,7 +200,8 @@ func CollidesWithGrid(d doodads.Actor, grid *level.Chunker, target render.Point) // IsColliding returns whether any sort of collision has occurred. func (c *Collide) IsColliding() bool { - return c.Top || c.Bottom || c.Left || c.Right + return c.Top || c.Bottom || c.Left || c.Right || + c.InFire || c.InWater } // ScanBoundingBox scans all of the pixels in a bounding box on the grid and @@ -235,21 +240,39 @@ func (c *Collide) ScanBoundingBox(box render.Rect, grid *level.Chunker) bool { // bounding boxes of the doodad. func (c *Collide) ScanGridLine(p1, p2 render.Point, grid *level.Chunker, side Side) { for point := range render.IterLine2(p1, p2) { - if _, err := grid.Get(point); err == nil { - // A hit! + if swatch, err := grid.Get(point); err == nil { + // We're intersecting a pixel! If it's a solid one we'll return it + // in our result. If non-solid, we'll collect attributes from it + // and return them in the final result for gameplay behavior. + if swatch.Fire { + c.InFire = true + } + if swatch.Water { + c.InWater = true + } + + // Non-solid swatches don't collide so don't pay them attention. + if !swatch.Solid { + continue + } + switch side { case Top: c.Top = true c.TopPoint = point + c.TopPixel = swatch case Bottom: c.Bottom = true c.BottomPoint = point + c.BottomPixel = swatch case Left: c.Left = true c.LeftPoint = point + c.LeftPixel = swatch case Right: c.Right = true c.RightPoint = point + c.RightPixel = swatch } } } diff --git a/pkg/level/chunk.go b/pkg/level/chunk.go index b8dda84..5dac9ae 100644 --- a/pkg/level/chunk.go +++ b/pkg/level/chunk.go @@ -161,7 +161,12 @@ func (c *Chunk) ToBitmap(filename string, mask render.Color) (render.Texturer, e for px := range c.Iter() { var color = px.Swatch.Color if mask != render.Invisible { - color = mask + // A semi-transparent mask will overlay on top of the actual color. + if mask.Alpha < 255 { + color = color.AddColor(mask) + } else { + color = mask + } } img.Set( int(px.X-pointOffset.X), diff --git a/pkg/main_scene.go b/pkg/main_scene.go index 84465f3..72819f8 100644 --- a/pkg/main_scene.go +++ b/pkg/main_scene.go @@ -40,34 +40,40 @@ func (s *MainScene) Setup(d *Doodle) error { frame := ui.NewFrame("frame") s.frame = frame - button1 := ui.NewButton("Button1", ui.NewLabel(ui.Label{ - Text: "New Map", - Font: balance.StatusFont, - })) - button1.Handle(ui.Click, func(p render.Point) { - d.GotoNewMenu() - }) - - button2 := ui.NewButton("Button2", ui.NewLabel(ui.Label{ - Text: "Load Map", - Font: balance.StatusFont, - })) - button2.Handle(ui.Click, func(p render.Point) { - d.GotoLoadMenu() - }) - - frame.Pack(button1, ui.Pack{ - Anchor: ui.N, - Fill: true, - }) - frame.Pack(button2, ui.Pack{ - Anchor: ui.N, - PadY: 12, - Fill: true, - }) - - s.Supervisor.Add(button1) - s.Supervisor.Add(button2) + var buttons = []struct { + Name string + Func func() + }{ + { + Name: "Play a Level", + Func: d.GotoPlayMenu, + }, + { + Name: "Create a New Level", + Func: d.GotoNewMenu, + }, + { + Name: "Edit a Level", + Func: d.GotoLoadMenu, + }, + } + for _, button := range buttons { + button := button + btn := ui.NewButton(button.Name, ui.NewLabel(ui.Label{ + Text: button.Name, + Font: balance.StatusFont, + })) + btn.Handle(ui.Click, func(p render.Point) { + button.Func() + }) + s.Supervisor.Add(btn) + frame.Pack(btn, ui.Pack{ + Anchor: ui.N, + PadY: 8, + // Fill: true, + FillX: true, + }) + } return nil } diff --git a/pkg/menu_scene.go b/pkg/menu_scene.go index 5662599..91587f5 100644 --- a/pkg/menu_scene.go +++ b/pkg/menu_scene.go @@ -36,6 +36,9 @@ type MenuScene struct { // Values for the New menu newPageType string newWallpaper string + + // Values for the Load/Play menu. + loadForPlay bool // false = load for edit } // Name of the scene. @@ -54,13 +57,24 @@ func (d *Doodle) GotoNewMenu() { // GotoLoadMenu loads the MenuScene and shows the "Load" window. func (d *Doodle) GotoLoadMenu() { - log.Info("Loading the MenuScene to the Load window") + log.Info("Loading the MenuScene to the Load window for Edit Mode") scene := &MenuScene{ StartupMenu: "load", } d.Goto(scene) } +// GotoPlayMenu loads the MenuScene and shows the "Load" window for playing a +// level, not editing it. +func (d *Doodle) GotoPlayMenu() { + log.Info("Loading the MenuScene to the Load window for Play Mode") + scene := &MenuScene{ + StartupMenu: "load", + loadForPlay: true, + } + d.Goto(scene) +} + // Setup the scene. func (s *MenuScene) Setup(d *Doodle) error { s.Supervisor = ui.NewSupervisor() @@ -358,7 +372,11 @@ func (s *MenuScene) setupLoadWindow(d *Doodle) error { Font: balance.MenuFont, })) btn.Handle(ui.Click, func(p render.Point) { - d.EditFile(lvl) + if s.loadForPlay { + d.PlayLevel(lvl) + } else { + d.EditFile(lvl) + } }) s.Supervisor.Add(btn) lvlRow.Pack(btn, ui.Pack{ @@ -383,7 +401,9 @@ func (s *MenuScene) setupLoadWindow(d *Doodle) error { * Frame for selecting User Doodads ******************/ - if !balance.FreeVersion { + // Doodads not shown if we're loading a map to play, nor are they + // available to the free version. + if !s.loadForPlay && !balance.FreeVersion { label2 := ui.NewLabel(ui.Label{ Text: "Doodads", Font: balance.LabelFont, diff --git a/pkg/play_scene.go b/pkg/play_scene.go index 0633f52..549c66e 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -7,6 +7,7 @@ import ( "git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/pkg/balance" + "git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/log" @@ -115,6 +116,17 @@ func (s *PlayScene) Setup(d *Doodle) error { s.drawing.Resize(render.NewRect(int32(d.width), int32(d.height))) s.drawing.Compute(d.Engine) + // Handler when an actor touches water or fire. + s.drawing.OnLevelCollision = func(a *uix.Actor, col *collision.Collide) { + if col.InFire { + a.Canvas.MaskColor = render.Black + } else if col.InWater { + a.Canvas.MaskColor = render.DarkBlue + } else { + a.Canvas.MaskColor = render.Invisible + } + } + // Given a filename or map data to play? if s.Level != nil { log.Debug("PlayScene.Setup: received level from scene caller") diff --git a/pkg/uix/actor_collision.go b/pkg/uix/actor_collision.go index 7befaa0..7664865 100644 --- a/pkg/uix/actor_collision.go +++ b/pkg/uix/actor_collision.go @@ -77,6 +77,9 @@ func (w *Canvas) loopActorCollision() error { info, ok := collision.CollidesWithGrid(a, w.chunks, delta) if ok { // Collision happened with world. + if w.OnLevelCollision != nil { + w.OnLevelCollision(a, info) + } } delta = info.MoveTo // Move us back where the collision check put us diff --git a/pkg/uix/canvas.go b/pkg/uix/canvas.go index e0e90c7..f2b6695 100644 --- a/pkg/uix/canvas.go +++ b/pkg/uix/canvas.go @@ -11,6 +11,7 @@ import ( "git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/bindata" + "git.kirsle.net/apps/doodle/pkg/collision" "git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/drawtool" "git.kirsle.net/apps/doodle/pkg/level" @@ -75,6 +76,9 @@ type Canvas struct { OnLinkActors func(a, b *Actor) linkFirst *Actor + // Collision handlers for level geometry. + OnLevelCollision func(*Actor, *collision.Collide) + /******** * Editable canvas private variables. ********/ diff --git a/pkg/uix/canvas_wallpaper.go b/pkg/uix/canvas_wallpaper.go index 86bc1ac..897043b 100644 --- a/pkg/uix/canvas_wallpaper.go +++ b/pkg/uix/canvas_wallpaper.go @@ -114,6 +114,9 @@ func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error { limit.Y = S.H } + limit.X += size.W + limit.Y += size.H + // Tile the repeat texture. for x := origin.X - size.W; x < limit.X; x += size.W { for y := origin.Y - size.H; y < limit.Y; y += size.H {