From d28745f89e9d82c0b73eb0af7adc526b0a531ff9 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sun, 5 May 2019 19:04:02 -0700 Subject: [PATCH] Mobile Enemy Doodad Test * Add a Red Azulian as a test for mobile enemies. * Its A.I. has it walk back and forth, changing directions when it comes up against an obstacle for a few moments. * It plays walking animations and can trigger collision events with other Doodads, such as the Electric Door and Trapdoor. * Move Gravity responsibility to the doodad scripts themselves. * Call `Self.SetGravity(true)` to opt the Doodad in to gravity. * The canvas.Loop() adds gravity to any doodad that has it enabled. --- dev-assets/doodads/azulian/azulian-red.js | 45 ++++++ dev-assets/doodads/azulian/azulian.js | 35 ++-- dev-assets/doodads/azulian/red-back.png | Bin 0 -> 826 bytes dev-assets/doodads/azulian/red-front.png | Bin 0 -> 839 bytes dev-assets/doodads/azulian/red-wl1.png | Bin 0 -> 803 bytes dev-assets/doodads/azulian/red-wl2.png | Bin 0 -> 816 bytes dev-assets/doodads/azulian/red-wl3.png | Bin 0 -> 800 bytes dev-assets/doodads/azulian/red-wl4.png | Bin 0 -> 819 bytes dev-assets/doodads/azulian/red-wr1.png | Bin 0 -> 829 bytes dev-assets/doodads/azulian/red-wr2.png | Bin 0 -> 834 bytes dev-assets/doodads/azulian/red-wr3.png | Bin 0 -> 815 bytes dev-assets/doodads/azulian/red-wr4.png | Bin 0 -> 838 bytes docs/public/Doodad Scripts.md | 189 ++++++++++++++++++++++ pkg/doodle.go | 1 + pkg/level/chunk.go | 2 + pkg/play_scene.go | 14 +- pkg/scripting/vm.go | 10 ++ pkg/uix/actor.go | 8 + pkg/uix/actor_animation.go | 2 - pkg/uix/canvas.go | 3 + 20 files changed, 275 insertions(+), 34 deletions(-) create mode 100644 dev-assets/doodads/azulian/azulian-red.js create mode 100644 dev-assets/doodads/azulian/red-back.png create mode 100644 dev-assets/doodads/azulian/red-front.png create mode 100644 dev-assets/doodads/azulian/red-wl1.png create mode 100644 dev-assets/doodads/azulian/red-wl2.png create mode 100644 dev-assets/doodads/azulian/red-wl3.png create mode 100644 dev-assets/doodads/azulian/red-wl4.png create mode 100644 dev-assets/doodads/azulian/red-wr1.png create mode 100644 dev-assets/doodads/azulian/red-wr2.png create mode 100644 dev-assets/doodads/azulian/red-wr3.png create mode 100644 dev-assets/doodads/azulian/red-wr4.png create mode 100644 docs/public/Doodad Scripts.md diff --git a/dev-assets/doodads/azulian/azulian-red.js b/dev-assets/doodads/azulian/azulian-red.js new file mode 100644 index 0000000..4ff17bc --- /dev/null +++ b/dev-assets/doodads/azulian/azulian-red.js @@ -0,0 +1,45 @@ +function main() { + log.Info("Azulian '%s' initialized!", Self.Doodad.Title); + + var playerSpeed = 4; + var gravity = 4; + var Vx = Vy = 0; + + var direction = "right"; + + Self.SetGravity(true); + Self.AddAnimation("walk-left", 100, ["red-wl1", "red-wl2", "red-wl3", "red-wl4"]); + Self.AddAnimation("walk-right", 100, ["red-wr1", "red-wr2", "red-wr3", "red-wr4"]); + + // var nextTurn = time.Add(time.Now(), 2500); + + // Sample our X position every few frames and detect if we've hit a solid wall. + var sampleTick = 0; + var sampleRate = 5; + var lastSampledX = 0; + + setInterval(function() { + // if (time.Now().After(nextTurn)) { + // direction = direction === "right" ? "left" : "right"; + // nextTurn = time.Add(time.Now(), 2500); + // } + + if (sampleTick % sampleRate === 0) { + var curX = Self.Position().X; + var delta = Math.abs(curX - lastSampledX); + if (delta < 5) { + log.Error("flip red azulian"); + direction = direction === "right" ? "left" : "right"; + } + lastSampledX = curX; + } + sampleTick++; + + var Vx = playerSpeed * (direction === "left" ? -1 : 1); + Self.SetVelocity(Point(Vx, 0)); + + if (!Self.IsAnimating()) { + Self.PlayAnimation("walk-"+direction, null); + } + }, 100); +} diff --git a/dev-assets/doodads/azulian/azulian.js b/dev-assets/doodads/azulian/azulian.js index 25ff361..69bf9c8 100644 --- a/dev-assets/doodads/azulian/azulian.js +++ b/dev-assets/doodads/azulian/azulian.js @@ -9,44 +9,29 @@ function main() { var animStart = animEnd = 0; var animFrame = animStart; - setInterval(function() { - if (animating) { - if (animFrame < animStart || animFrame > animEnd) { - animFrame = animStart; - } - - animFrame++; - if (animFrame === animEnd) { - animFrame = animStart; - } - Self.ShowLayer(animFrame); - } else { - Self.ShowLayer(animStart); - } - }, 100); + Self.SetGravity(true); + Self.AddAnimation("walk-left", 100, ["blu-wl1", "blu-wl2", "blu-wl3", "blu-wl4"]); + Self.AddAnimation("walk-right", 100, ["blu-wr1", "blu-wr2", "blu-wr3", "blu-wr4"]); Events.OnKeypress(function(ev) { Vx = 0; Vy = 0; if (ev.Right.Now) { - animStart = 2; - animEnd = animStart+4; - animating = true; + if (!Self.IsAnimating()) { + Self.PlayAnimation("walk-right", null); + } Vx = playerSpeed; } else if (ev.Left.Now) { - animStart = 6; - animEnd = animStart+4; - animating = true; + if (!Self.IsAnimating()) { + Self.PlayAnimation("walk-left", null); + } Vx = -playerSpeed; } else { + Self.StopAnimation(); animating = false; } - if (!Self.Grounded()) { - Vy += gravity; - } - // Self.SetVelocity(Point(Vx, Vy)); }) } diff --git a/dev-assets/doodads/azulian/red-back.png b/dev-assets/doodads/azulian/red-back.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5d2efc260829e8792427f6b2e4c3098f4fafa7 GIT binary patch literal 826 zcmV-A1I7G_P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_^6j6DDV02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{009z7L_t(o!|hhf4umiWoEpDXzKQ;9_kN;)+Q;@JiO9E80*C6*kpj(MqxXmEl;j?XS;>z*){N~KaN~DgALvK|CGQrq>t+W#DW;P2xdh44}HVPxDD2X22z8jUKu)3a+ zV8CetGZHL#stE-sRV3W;GbN-|;7Rm^n0`qS7)8XUo}PsLa`6$`lw^kxMPQazA2nm! z${E0d(Q{q)F>V`n;*waDVB}m{?9l{qGU3V*_K!K0sE@KK_en`mH+?%07*qoM6N<$ Ef*mYt6951J literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-front.png b/dev-assets/doodads/azulian/red-front.png new file mode 100644 index 0000000000000000000000000000000000000000..8e7b797c2f2fe22f06f5b260355bee085a44df1b GIT binary patch literal 839 zcmV-N1GxN&P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00AFKL_t(o!|hhf62l+}Tr>Gr`6m2Xd+6BG1Qm?7J%pJY znpjxb2dZ+#Kw{rWUonuB!86Y4e zc{`1gPVgFyW(rOqjJ)?W6h@j$&0#BeO^{)W_tht literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-wl1.png b/dev-assets/doodads/azulian/red-wl1.png new file mode 100644 index 0000000000000000000000000000000000000000..a05f987bd187774b63e57b5133d23e84d814f962 GIT binary patch literal 803 zcmV+;1Kj+HP)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{008?*L_t(o!|j$^3dA4`MQ>!*YSt7t>wL@zj`&ETr3_Rk z^rxPin^%G7Jm=66AbY+GcfyYd5Fkep+#+BX4gj=cc?(a>VB?*yJEC?wpn+Rm2yQs@ z=TUw;>XhJy1ArnGE7#050OldMwO^AmyMWGx>FdY{KC27zT9~T*x0h?-E(Z#@26*8n z0kzGgqGteYI|ePB0V?nkVHDX~L}~%ENLmpLP-0e#3l?~r-xH(Sy$z$tPeGPxXDPtF zQ9Kak-HK`vStMtXJbS-%9rrqEk`G2 hz>Wx)1D0K}I&T`&v7Vh?b*caW002ovPDHLkV1mL|SGfQH literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-wl2.png b/dev-assets/doodads/azulian/red-wl2.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f746851cc19f932ef7966ecee7d2459d614df3 GIT binary patch literal 816 zcmV-01JC@4P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{009U|L_t(o!|he=3WG2VJd4k2o+*9V?#Jv1(Mt0%g|UJ# zhUTux-39ocUpWjk&v)fX@Du_Jw2c{$5Qu^Uz!GV>2CqaQW6kx6Te~|j18(g?af1`R zpP);M8yo;8M$x|=kuYljL_=WR{+W^437izHdq+mIztY9(6te1r(h$NxGQ**%5!t@{-*-Fkn63?z}SO*FC)n(Lg zS&0tz%3Mw+NA%xT;iYUtJ%*6iJza?0IwV2@mO8nH(+KDwlA=pVJFDEX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{008(&L_t(o!|hf}4#gk{oSL4soCz<>TQnxbr&W-~O-Y(G z{WvhrfWQ}D1O)@)`7iAR4@rOlxtOsffmLt-I94dH!7~xaxaPT|YBvKj;8qrr8=UCA z7TuBD-~jNz#;i5%E+D#r8{mb0i%w3v1=_#>%nq0mVXvC3_P++!HXVu5%s>GyN#%|Z z!>r1H2(vICr851j_kHiZD*?Tb2B<+i;8QMm+QWF0000EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{009e0L_t(o!|hh>3WG2Zi{i7c&vd?Q{o!sLM_YRzqcBzw zRMf;YNelePuM7gJ=e>O;cnSdm>Wc_V2t>gFV2enu!Ov1qjNd#~{Iy$w8E~r#yBoZe z`v{KR4GsVkQ}Gm0dkRXqfg9jjekML5ao8i!1_tl}@1?|L!k+O?G5@WLNAUC>~a0H0xgLde8wwzO{ff*mUn9}WaZs#La*Dy%W>=6nt8wE?>6LfzWz$} xjz~!GEI-(Kfa52oAi66yZ%SJmk4O(J9~VJ;+q)|w8y)}v002ovPDHLkV1oY*V=e#y literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-wr1.png b/dev-assets/doodads/azulian/red-wr1.png new file mode 100644 index 0000000000000000000000000000000000000000..dfcfffebc1529a1cdf2ba548d5464ac386bdeda3 GIT binary patch literal 829 zcmV-D1H$}?P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{009+AL_t(o!|j*R4uc>JMK5IEO24W6ta~ubN&riX^Tj4h zjGJ(Gds-^+#v6uUU|Y3Td|HHAKw@4ZB6=QPAtG3;Pt2!#nFs(thV?!x^7s=(w9SeL z-hW?5CYo|0_BK-ImT3PNxrlDS4*N|a;tb^kge*2XiC{j7a5EtDUFWrk8u6fIA!wm% zEhyb-iP4A{ra)TW%WGmrVn!xHHl#pZej9VMGV1S21@(Tvv=PY^r5(bf5fS;TKyK0k zaRi~Z8O51)+q1OCiAKbwfEI{#bzHS)w*$3^f6NKPYAA&Fu5UYz2?&Mm0;b1)(?Bxn zg0n~Q?M>?Tnefr6cvS?i{SrcX7-?Aut2V!79`L*!X&v|iEF8w!8h;wu00000NkvXX Hu0mjfO!R1G literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-wr2.png b/dev-assets/doodads/azulian/red-wr2.png new file mode 100644 index 0000000000000000000000000000000000000000..a79f3a34527defa22a270235fad0633981d18dc3 GIT binary patch literal 834 zcmV-I1HJr-P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00A0FL_t(o!|hhl4#OY_Jk7pUz6pPpJ#222OxFV4vScQv zjka>a9RU2tFAfGI>#X{15N-gCIR!+G{G0)zcwFC@H*1{;06>oOE*Wur03wp3AWHQ1 zc66dco~U7#6Wykh7j<>hT?akg?*dVeu$+L>m7xs?<_(CD2d0M9-V{id3^n0qGSl99 zMb15hoXU`Z0wQV5OPN`a&EbW&(qlk`S%g_ExyIgD*WYIrExZDu*Q~(-B&`;#h&c`6 zS;ff8Q^()RHWk3i3*C_e!u^1LZ_MaKX??;jLh7fN`6U8kmJcX!Zi+lV){;f~fD{0> z7T82|zU%MPhT)V{yfXeSEvGi63`o=Lzdgjuhms+|VQj2_REE)U1-7a2ZttjA4FCWD M07*qoM6N<$f~cWziU0rr literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-wr3.png b/dev-assets/doodads/azulian/red-wr3.png new file mode 100644 index 0000000000000000000000000000000000000000..5e79c3616c13d1b8e1e7893c9c3da93febdb0fd7 GIT binary patch literal 815 zcmV+~1JL}5P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{009R{L_t(o!|j$$4#Xe`g`cEnEoZ{ZGK&c_YWkx{+f7Nj z(Nv$$4+MDQ4Mzx|QESoDBHRL2kq{9y^K*oV}4TEZSL(2ryZDm3qJaQzLxsK1yle4002ovPDHLkV1li5XOsW{ literal 0 HcmV?d00001 diff --git a/dev-assets/doodads/azulian/red-wr4.png b/dev-assets/doodads/azulian/red-wr4.png new file mode 100644 index 0000000000000000000000000000000000000000..219ec34fef6dc46e070d313287e620c2814c63bb GIT binary patch literal 838 zcmV-M1G)T(P)EX>4Tx04R}tkv&MmKpe$iQ)@*k4rY+zkfAzRkSgM+RVYG*P%E_RU_SZ@4|zbb@Z5kfC`M3Imb%a{|zGt~4AUmwAfDc| z4aWP#yi%4_;&bA0lP*a7$aLA`H^wEGIhM(r*~~mKPb`$WSngt_Y-+?)#4%OVDWA)E ztTNtWtX1nu`=0EDk%GRm%ygO~NMR965FtQD9TikzBTlPMiiHgACw=@;*DsPwCRYUt zITlcZ2Fdk<{lV{Ut>R?DONu0c(2L`Ii~`|Zpw)1k?_eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00ACJL_t(o!|j*R4#OY_MbYeA<(ufwvWM<=mbg}twOKNg zKGdvoyu!s1{l_m3A=0R|^4$<_fFcqCk!L=RfJh$ci^y(nVJ0FXcC7b^IL8f$Sw|t# z?C$MYn1__e)7xiWA7@_V=~8>k?}Er@a@PwnQCg}%I|z{-MA#7k0w93+2ohO_HtB4F z)#|j8vnL@B%aDl@F)IR;Rpwo(Autpv4i&mfP(@TlwB9-9F^A)_zYB$^g8`}q)&r2e zCUj;>y=F9QL`R9$D%&sslpliF 0) { + clearTimeout(timer); + } + + Self.ShowLayer(1); + timer = setTimeout(function() { + Self.ShowLayer(0); + timer = 0; + }, 200); + }) +} +``` + +# JavaScript API + +# Functions + +Global functions available to your script: + +## RGBA(uint8 red, uint8 green, uint8 blue, uint8 alpha) + +Get a render.Color object from the given color code. Each number +must be between 0 and 255. For example, RGBA(255, 0, 255, 255) +creates an opaque magenta color equivalent to #FF00FF. + +The render.Color type may be needed in certain API calls that +require the game's native color type. + +## Point(x int, y int) + +Get a render.Point object that refers to a position in the game +world. + +## Common JavaScript Functions + +The following common JavaScript APIs seen in web browsers work +in the doodad scripts: + +* int setTimeout(function, int milliseconds) + + Set a timeout to run your function after a delay in + milliseconds. Returns a timer ID to be used with + clearTimeout() if you want to cancel the timeout. + +* int setInterval(function, int milliseconds) + + Like setTimeout, but repeatedly re-runs the function after + the delay in milliseconds. Returns a timer ID to be used + with clearInterval() if you want to cancel the interval. + +* clearTimeout(int timerID), clearInterval(int timerID) + + Cancel a timeout or interval by passing its timer ID, which + was returned when the timer or interval was first created. + +* console.log(str message, v...) + + Write to the game's log console. There are also `console.warn`, + `console.error` and `console.log` variants. + +## Self + +The global variable `Self` holds an API for the current doodad. The full +surface area of this API is subject to change, but some useful examples you +can do with this are as follows. + +### Self.Doodad + +Self.Doodad is a pointer into the doodad's metadata file. Not +all properties in there can be written to or read from the +JavaScript engine, but some useful attributes are: + +* `str Self.Doodad.Title`: the title of the doodad. +* `str Self.Doodad.Author`: the author name of the doodad. +* `str Self.Doodad.Script`: your own source code. Note that + editing this won't have any effect in-game, as your doodad's + source has already been loaded into the interpreter. +* `str Self.Doodad.GameVersion`: the game version that created + the doodad. + +### Self.ShowLayer(int index) + +Set the doodad's visible layer to the index. + +A layer is a drawing created in the in-game format. Only one layer +is visible on-screen during Edit Mode or Play Mode. Layers can be +used to store alternate versions of your doodad to show different +states or as animation frames. + +The first and default layer is always zero. Use `CountLayers()` +to query how many layers are in the doodad. + +### int Self.CountLayers() + +Returns the number of layers, or frames, available in your doodad's +drawing data. Usually these layers are for alternate drawings or animation frames. + +The number is the `len()` of the array, and layers are +zero-indexed, so the first and default layer is always layer 0 +and the final layer is like so: + +```javascript +// set the final frame as the active one +Self.ShowLayer( Self.CountLayers() - 1 ); +``` + +## Animations + +### Self.AddAnimation(string name, int interval, [layers...]) error + +Add a new animation using some of the layers in your doodad's drawing. + +The interval is counted in milliseconds, with 1000 meaning one second between +frames of the animation. + +The layers are an array of strings or integers. If strings, use the layer names +from the drawing. With integers, these are the layer index numbers where 0 is +the first (default) layer. + +### Self.PlayAnimation(string name, function callback) + +Play the named animation. When the animation is finished, the callback function +will be called. Set the callback to `null` if you don't want a callback function. + +### Self.StopAnimation() + +Stop and cancel any current animations. Their callback functions will not be +called. + +### Self.IsAnimating() bool + +Returns `true` if an animation is currently playing. + +## Events + +Use the Events object to register event handlers from your +doodad. Usually you'll configure these in your main() function. + +Example configuring event handlers: + +```javascript +function main() { + Events.OnCollide(function(e) { + console.log("I've been collided with!"); + }) +} +``` + +### OnCollide + +Triggers when another doodad has collided with your doodad's box +space on the level. Arguments TBD. + +### OnEnter + +Triggers when another doodad has fully intersected your doodad's +box. + +### OnLeave + +Triggers when a doodad who was intersecting your box has left +your box. + +### KeypressEvent + +Triggers when the player character has pressed a key. + +This only triggers when your doodad is the focus of the camera +in-game, i.e. for the player character doodad. diff --git a/pkg/doodle.go b/pkg/doodle.go index 4cc24de..3812a99 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -60,6 +60,7 @@ func New(debug bool, engine render.Engine) *Doodle { if debug { log.Logger.Config.Level = golog.DebugLevel + DebugOverlay = true // on by default in debug mode, F3 to disable } return d diff --git a/pkg/level/chunk.go b/pkg/level/chunk.go index fda8515..33c12aa 100644 --- a/pkg/level/chunk.go +++ b/pkg/level/chunk.go @@ -123,6 +123,8 @@ func (c *Chunk) toBitmap(mask render.Color) string { ) } + log.Info("Chunk<%d>.toBitmap() called", c.Size) + // Get the temp bitmap image. bitmap := userdir.CacheFilename("chunk", filename+".bmp") err := c.ToBitmap(bitmap, mask) diff --git a/pkg/play_scene.go b/pkg/play_scene.go index b502e83..c2ae7ab 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -173,7 +173,7 @@ func (s *PlayScene) Draw(d *Doodle) error { // movePlayer updates the player's X,Y coordinate based on key pressed. func (s *PlayScene) movePlayer(ev *events.State) { var playerSpeed = int32(balance.PlayerMaxVelocity) - var gravity = int32(balance.Gravity) + // var gravity = int32(balance.Gravity) var velocity render.Point @@ -190,12 +190,12 @@ func (s *PlayScene) movePlayer(ev *events.State) { velocity.Y = -playerSpeed } - // Apply gravity if not grounded. - if !s.Player.Grounded() { - // Gravity has to pipe through the collision checker, too, so it - // can't give us a cheated downward boost. - velocity.Y += gravity - } + // // Apply gravity if not grounded. + // if !s.Player.Grounded() { + // // Gravity has to pipe through the collision checker, too, so it + // // can't give us a cheated downward boost. + // velocity.Y += gravity + // } s.Player.SetVelocity(velocity) diff --git a/pkg/scripting/vm.go b/pkg/scripting/vm.go index b1b6543..63d0082 100644 --- a/pkg/scripting/vm.go +++ b/pkg/scripting/vm.go @@ -2,6 +2,8 @@ package scripting import ( "fmt" + "reflect" + "time" "git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/pkg/log" @@ -55,6 +57,14 @@ func (vm *VM) RegisterLevelHooks() error { "Self": vm.Self, // i.e., the uix.Actor object "Events": vm.Events, + "TypeOf": reflect.TypeOf, + "time": map[string]interface{}{ + "Now": time.Now, + "Add": func(t time.Time, ms int64) time.Time { + return t.Add(time.Duration(ms) * time.Millisecond) + }, + }, + // Timer functions with APIs similar to the web browsers. "setTimeout": vm.SetTimeout, "setInterval": vm.SetInterval, diff --git a/pkg/uix/actor.go b/pkg/uix/actor.go index 4405a5f..48d11d2 100644 --- a/pkg/uix/actor.go +++ b/pkg/uix/actor.go @@ -27,6 +27,9 @@ type Actor struct { activeLayer int // active drawing frame for display flagDestroy bool // flag the actor for destruction + // Actor runtime variables. + hasGravity bool + // Animation variables. animations map[string]*Animation activeAnimation *Animation @@ -65,6 +68,11 @@ func NewActor(id string, levelActor *level.Actor, doodad *doodads.Doodad) *Actor return actor } +// SetGravity configures whether the actor is affected by gravity. +func (a *Actor) SetGravity(v bool) { + a.hasGravity = v +} + // LayerCount returns the number of layers in this actor's drawing. func (a *Actor) LayerCount() int { return len(a.Doodad.Layers) diff --git a/pkg/uix/actor_animation.go b/pkg/uix/actor_animation.go index 585163c..ea25dd1 100644 --- a/pkg/uix/actor_animation.go +++ b/pkg/uix/actor_animation.go @@ -34,12 +34,10 @@ frames left to animate. func (a *Actor) TickAnimation(an *Animation) bool { an.activeLayer++ if an.activeLayer < len(an.Layers) { - log.Warn("TickAnimation(%s): new layer=%d", a.activeAnimation.Name, an.Layers[an.activeLayer]) a.ShowLayer(an.Layers[an.activeLayer]) } else if an.activeLayer >= len(an.Layers) { // final layer has been shown for 2 ticks, return that the animation has // been concluded. - log.Warn("TickAnimation(%s): finished", a.activeAnimation.Name) return true } diff --git a/pkg/uix/canvas.go b/pkg/uix/canvas.go index f0ed9ff..78ac0e1 100644 --- a/pkg/uix/canvas.go +++ b/pkg/uix/canvas.go @@ -207,6 +207,9 @@ func (w *Canvas) Loop(ev *events.State) error { // Get the actor's velocity to see if it's moving this tick. v := a.Velocity() + if a.hasGravity { + v.Y += int32(balance.Gravity) + } // If not moving, grab the bounding box right now. if v == render.Origin {