From f23bdaf12144046690684273ae17f4b8e08b5e58 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Mon, 8 Apr 2024 18:51:25 +0200 Subject: [PATCH] switched to go coded animation system, LDTK is not used for this anymore --- assets/loader-levels.go | 168 ++++++++---------- assets/loader-sprites.go | 3 + ...tible.json => collectible-detonating.json} | 48 +++-- assets/sprites/collectible-detonating.png | Bin 2244 -> 2175 bytes assets/sprites/collectible-idle.ase | Bin 0 -> 1655 bytes assets/sprites/collectible-idle.json | 51 ++---- assets/sprites/collectible-idle.png | Bin 685 -> 931 bytes assets/sprites/collectible.ase | Bin 8156 -> 7443 bytes components/components.go | 4 +- components/renderable.go | 74 +++++--- config/static.go | 4 +- game/levels.go | 22 --- grid/grid.go | 25 ++- systems/animation_system.go | 51 +++--- systems/collectible_system.go | 23 +-- 15 files changed, 233 insertions(+), 240 deletions(-) rename assets/sprites/{collectible.json => collectible-detonating.json} (82%) create mode 100644 assets/sprites/collectible-idle.ase diff --git a/assets/loader-levels.go b/assets/loader-levels.go index ba97dbd..0fb9f76 100644 --- a/assets/loader-levels.go +++ b/assets/loader-levels.go @@ -14,62 +14,66 @@ import ( var Project = LoadLDTK("levels") var Tiles = InitTiles() +type TileAnimation struct { + OnCollision bool // wether to animate a collision + OnDestruction bool // wether to animate destruction + OnIdle bool // wether to animate during idling + + CollisionSheet AnimationSet // an entry in assets.Animations[name] + DestructionSheet AnimationSet + IdleSheet AnimationSet +} + // Tile: contains image, identifier (as used in level data) and // additional properties type Tile struct { - Id, Ref string - Sprite *ebiten.Image - ToggleSprite *ebiten.Image - Solid bool // wall brick - Player bool // player sphere - IsPrimary bool // primary player sphere - Renderable bool // visible, has sprite - Velocity bool // movable - Collectible bool // collectible, vanishes once collected - Transient bool // turns into brick wall when traversed - Destroyable bool // turns into empty floor when bumped into twice - Animation int // -1=unused, 0-3 = show image of slice - Tiles []*ebiten.Image // has N sprites - TileNames []string // same thing, only the names - Obstacle bool // is an obstacle/enemy - Direction int // obstacle business end shows into this direction - Shader *ebiten.Shader - Alpha *ebiten.Image - Bond bool // denotes an entity which can have a relation to another - Door bool // a door, can be manipulated by a switch - Switch bool // opens|closes a door - AnimateOnDestruct bool // wether to animate destruction - AnimationTrigger string // dynamically configured via LDTP - AnimationSpriteSheet AnimationSet // which sprites to use (refers to an entry in assets.Animations[name]) + Id, Ref string + Sprite *ebiten.Image + ToggleSprite *ebiten.Image + Solid bool // wall brick + Player bool // player sphere + IsPrimary bool // primary player sphere + Renderable bool // visible, has sprite + Velocity bool // movable + Collectible bool // collectible, vanishes once collected + Transient bool // turns into brick wall when traversed + Destroyable bool // turns into empty floor when bumped into twice + Tiles []*ebiten.Image // has N sprites + TileNames []string // same thing, only the names + Obstacle bool // is an obstacle/enemy + Direction int // obstacle business end shows into this direction + Shader *ebiten.Shader + Alpha *ebiten.Image + Bond bool // denotes an entity which can have a relation to another + Door bool // a door, can be manipulated by a switch + Switch bool // opens|closes a door + Animation TileAnimation } func (tile *Tile) Clone() *Tile { newtile := &Tile{ - Id: tile.Id, - Ref: tile.Ref, - Sprite: tile.Sprite, - ToggleSprite: tile.ToggleSprite, - Solid: tile.Solid, - Player: tile.Player, - IsPrimary: tile.IsPrimary, - Renderable: tile.Renderable, - Velocity: tile.Velocity, - Collectible: tile.Collectible, - Transient: tile.Transient, - Destroyable: tile.Destroyable, - Animation: tile.Animation, - Tiles: tile.Tiles, - TileNames: tile.TileNames, - Obstacle: tile.Obstacle, - Direction: tile.Direction, - Alpha: tile.Alpha, - Shader: tile.Shader, - Bond: tile.Bond, - Door: tile.Door, - Switch: tile.Switch, - AnimateOnDestruct: tile.AnimateOnDestruct, - AnimationSpriteSheet: tile.AnimationSpriteSheet, - AnimationTrigger: tile.AnimationTrigger, + Id: tile.Id, + Ref: tile.Ref, + Sprite: tile.Sprite, + ToggleSprite: tile.ToggleSprite, + Solid: tile.Solid, + Player: tile.Player, + IsPrimary: tile.IsPrimary, + Renderable: tile.Renderable, + Velocity: tile.Velocity, + Collectible: tile.Collectible, + Transient: tile.Transient, + Destroyable: tile.Destroyable, + Tiles: tile.Tiles, + TileNames: tile.TileNames, + Obstacle: tile.Obstacle, + Direction: tile.Direction, + Alpha: tile.Alpha, + Shader: tile.Shader, + Bond: tile.Bond, + Door: tile.Door, + Switch: tile.Switch, + Animation: tile.Animation, } return newtile @@ -115,6 +119,12 @@ func NewTileCollectible() *Tile { Solid: false, Renderable: true, Collectible: true, + Animation: TileAnimation{ + OnDestruction: true, + DestructionSheet: Animations["collectible-detonating"], + OnIdle: true, + IdleSheet: Animations["collectible-idle"], + }, } } @@ -128,17 +138,6 @@ func NewTileObstacle(direction int) *Tile { } } -func NewTileAnimation(class []string) *Tile { - sprites := GetSprites(class) - - return &Tile{ - Solid: false, - Renderable: false, - Animation: 0, - Tiles: sprites, - } -} - func NewTileTranswall() *Tile { return &Tile{ Solid: false, @@ -191,39 +190,22 @@ func InitTiles() TileRegistry { "ObstacleSouth": NewTileObstacle(config.South), "ObstacleWest": NewTileObstacle(config.West), "ObstacleEast": NewTileObstacle(config.East), - "Animation": NewTileAnimation([]string{ - "collectible-detonating1", - "collectible-detonating2", - "collectible-detonating3", - "collectible-detonating4", - "collectible-detonating5", - "collectible-detonating6", - "collectible-detonating7", - "collectible-detonating8", - "collectible-detonating9", - "collectible-detonating10", - "collectible-detonating11", - "collectible-detonating12", - "collectible-detonating13", - "collectible-detonating14", - "collectible-detonating15", - }), - "Transient": NewTileTranswall(), - "HiddenDoor": NewTileHiddenDoor("damage"), - "HiddenDoor2": NewTileHiddenDoor("damage"), - "HiddenDoor3": NewTileHiddenDoor("damage"), - "HiddenDoor4": NewTileHiddenDoor("damage"), - "HiddenDoor5": NewTileHiddenDoor("damage"), - "HiddenDoor6": NewTileHiddenDoor("damage"), - "HiddenDoor7": NewTileHiddenDoor("damage"), - "HiddenDoor8": NewTileHiddenDoor("damage"), - "HiddenDoor9": NewTileHiddenDoor("damage"), - "HiddenDoor10": NewTileHiddenDoor("damage"), - "HiddenDoor11": NewTileHiddenDoor("damage"), - "HiddenDoor12": NewTileHiddenDoor("damage"), - "HiddenDoor13": NewTileHiddenDoor("damage"), - "Switch": NewTileSwitch(), - "Door": NewTileDoor(), + "Transient": NewTileTranswall(), + "HiddenDoor": NewTileHiddenDoor("damage"), + "HiddenDoor2": NewTileHiddenDoor("damage"), + "HiddenDoor3": NewTileHiddenDoor("damage"), + "HiddenDoor4": NewTileHiddenDoor("damage"), + "HiddenDoor5": NewTileHiddenDoor("damage"), + "HiddenDoor6": NewTileHiddenDoor("damage"), + "HiddenDoor7": NewTileHiddenDoor("damage"), + "HiddenDoor8": NewTileHiddenDoor("damage"), + "HiddenDoor9": NewTileHiddenDoor("damage"), + "HiddenDoor10": NewTileHiddenDoor("damage"), + "HiddenDoor11": NewTileHiddenDoor("damage"), + "HiddenDoor12": NewTileHiddenDoor("damage"), + "HiddenDoor13": NewTileHiddenDoor("damage"), + "Switch": NewTileSwitch(), + "Door": NewTileDoor(), } } diff --git a/assets/loader-sprites.go b/assets/loader-sprites.go index 9c07f68..6d6fcda 100644 --- a/assets/loader-sprites.go +++ b/assets/loader-sprites.go @@ -60,6 +60,9 @@ type AnimationSet struct { File string } +// names in the registry match the sprite set png file name, the JSON +// file names are irrelevant as they point to the matching file using +// the "image" field. type AnimationRegistry map[string]AnimationSet //go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg sprites/*.json diff --git a/assets/sprites/collectible.json b/assets/sprites/collectible-detonating.json similarity index 82% rename from assets/sprites/collectible.json rename to assets/sprites/collectible-detonating.json index 3383e79..eaaf633 100644 --- a/assets/sprites/collectible.json +++ b/assets/sprites/collectible-detonating.json @@ -1,42 +1,42 @@ { "frames": [ { - "filename": "collectible 0.ase", + "filename": "collectible #Detonation 0.ase", "frame": { "x": 0, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 }, - "duration": 100 + "duration": 20 }, { - "filename": "collectible 1.ase", + "filename": "collectible #Detonation 1.ase", "frame": { "x": 64, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 }, - "duration": 100 + "duration": 20 }, { - "filename": "collectible 2.ase", + "filename": "collectible #Detonation 2.ase", "frame": { "x": 128, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 }, - "duration": 100 + "duration": 20 }, { - "filename": "collectible 3.ase", + "filename": "collectible #Detonation 3.ase", "frame": { "x": 192, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 }, - "duration": 100 + "duration": 20 }, { - "filename": "collectible 4.ase", + "filename": "collectible #Detonation 4.ase", "frame": { "x": 256, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -45,7 +45,7 @@ "duration": 100 }, { - "filename": "collectible 5.ase", + "filename": "collectible #Detonation 5.ase", "frame": { "x": 320, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -54,7 +54,7 @@ "duration": 100 }, { - "filename": "collectible 6.ase", + "filename": "collectible #Detonation 6.ase", "frame": { "x": 384, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -63,7 +63,7 @@ "duration": 100 }, { - "filename": "collectible 7.ase", + "filename": "collectible #Detonation 7.ase", "frame": { "x": 448, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -72,7 +72,7 @@ "duration": 100 }, { - "filename": "collectible 8.ase", + "filename": "collectible #Detonation 8.ase", "frame": { "x": 512, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -81,7 +81,7 @@ "duration": 100 }, { - "filename": "collectible 9.ase", + "filename": "collectible #Detonation 9.ase", "frame": { "x": 576, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -90,7 +90,7 @@ "duration": 100 }, { - "filename": "collectible 10.ase", + "filename": "collectible #Detonation 10.ase", "frame": { "x": 640, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -99,7 +99,7 @@ "duration": 100 }, { - "filename": "collectible 11.ase", + "filename": "collectible #Detonation 11.ase", "frame": { "x": 704, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -108,7 +108,7 @@ "duration": 100 }, { - "filename": "collectible 12.ase", + "filename": "collectible #Detonation 12.ase", "frame": { "x": 768, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, @@ -117,22 +117,13 @@ "duration": 100 }, { - "filename": "collectible 13.ase", + "filename": "collectible #Detonation 13.ase", "frame": { "x": 832, "y": 0, "w": 64, "h": 64 }, "rotated": false, "trimmed": false, "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, "sourceSize": { "w": 64, "h": 64 }, "duration": 100 - }, - { - "filename": "collectible 14.ase", - "frame": { "x": 896, "y": 0, "w": 64, "h": 64 }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, - "sourceSize": { "w": 64, "h": 64 }, - "duration": 100 } ], "meta": { @@ -140,9 +131,10 @@ "version": "1.x-dev", "image": "collectible-detonating.png", "format": "I8", - "size": { "w": 960, "h": 64 }, + "size": { "w": 896, "h": 64 }, "scale": "1", "frameTags": [ + { "name": "Detonation", "from": 1, "to": 14, "direction": "forward" } ], "layers": [ { "name": "Yellow Sphere", "opacity": 255, "blendMode": "normal" }, diff --git a/assets/sprites/collectible-detonating.png b/assets/sprites/collectible-detonating.png index d0aff47da74fcccc45c6d347c603d8dcc89dc49f..8ec9a0163de958b262d250d8697bf0a09aeac464 100644 GIT binary patch delta 1975 zcmV;o2T1tD5&sY&iBL{Q4GJ0x0000DNk~Le000Ak0000$2m=5B0DS7{Opzhre`!fX zK~#90?VW9!qc{wNeE|M}%_(yK>$N`!gkW2;3KBO_?LM=+ok%e=dbbLP5Mxv-l}e>j zsZ=VJN~Kb%KChwrsQu6?2z9ayl`5+`L_{qy+z#liQvHC25`Y#%^EB-d?_x4&KU-ff1Sr1$waz@KgmNzRirNy%8vplQn0DxshG7o>c1u- zKaZlWSY)5ddKkC15F)_@8SQJ#zM#D637Wq8>Rk%NM*;Mp*tflgrXio_<6(F==f+W=%dua(K9;4?r0DAhi zvp8$3?zPnvA9@7;s^-VQkjtF9;<=a8!-4PLK>&=J=qJ z4=u$3Qu}cLlZUk=Nb_o9&k)HKSI9_;4)pw z!x|D~tUZ_*D?z4Oe{W$#j09&l`4{x2-J}3rEa})1)np3@A9gWgA*+c+4{Jz}rfy>7 z8wo!0rWjeCaPYEAZ^Y_fy-PH-bT!!mf`?tqSjgVQo(YmSvsDu#Mt6yk1puA(Xpa-P z`!RiHr&9$;G_+(f-2{S%T}&U#n~ALnl67)bH8IF|rq~rTe=no{^$#Q&TDq8S0>Q&B zrjMm$Vyw%-3MO`jeTAlq&+R20TTXec1&B{mdG$y#CWhm4ihU`4zaZh*vdU{M5PX_y zsg-0*>;!z&zEnJk=wTJtdm!vI)lv(|=>&X~J{3pU&;oOhu+vmaEhMXc@NrH*2zO^; zLkrA3!cJ2yf3=XZ@YzQ@jH6Qk?=5U-fw@Q6X{x0b;!gr+=O2E%Tm+x}Gw8AB0O;Ub zy!X)ImJ$vvue#O(VW+8C$t#D?1S5};2n0&8Q=17Pk@C( z%d4)nK-g)jr554=eB*C90b~N0$CuTi0`JxXzX#y^e?8L#9qu~*9Y{2@%<@_bgq@~Z zawWsy(t{DaaoYf8&He%3R}md*@NQ3{*FXt%K=>Z>XLY!vL^I1QueX56VHY!wWJrIs z$%)1n0$CsFd*b`5brZGTp$aeYvf-msgn90szyHtRi4VOwU=I?HEu*~N0)mHK%s7%X zoDa7Ff7o)fqT&6yBl}InEWAw30U>tr`~L`Hb<^wVUhHKO4J}$v z7p@DPX2;*NTPO1g-Pk~&I937X30E)vsX9sGp{-YwEg*Q<#k^zjiEd8JS?g@zuX)#e zje`vyTDBGPmN0;Mrt8c>f)v2hBpq6!n$ZGaf2V;x&an*6Ew1N(og;vayN^qPH3APk zh-0B&j)b6ps$L`=TB4fK0%E6uKKe+8@R_89j7$F4_V(MFV2#v6UxDLwzy(1Q6J)@z$s@J|8zk`1Ex;OcN{9`AT}B`rf7L(V1>koV03(9oA7d+iy98?l z9{L(!gW5yvJ8QUyW}4nX-TEiM-?5t8qcZ>(Np6q5C0Nt$p}PPZ)Er_DZ^(zcW?#P% z^ex@dK)(WDsgl*>G5}?ICfJ}x58VgYf1t(?d%UJ4c_??oF$dbUAy$=@d|1UZkCD>} zd`TO0t!I$C02|a4V&_NZqdqMjn!TVc8Xy>A`{ehI{<%mr@d!Iz0Vvllzy`I1*h7Gg zitgwC=J1!jCUlB-^#wxgXw*SJEd@}j*8m&T5Mo1sJz5~!L$m&AQiwII5L@^ie~xzq zRs#QnAad@2U(g=7mJk~P>;aQ0d;u<_iGRkNN6eWyoJBH1Z2m(P_#TeZ`ynZSkUSyN z@ynQ)9y*xtP4PJdpCPthKU4Rb0L~&6zz<8v%umx76%joAjO(Ep{|t&D))3#35gsfe zwq#hb)IvWj1yC&Dn|-tXX-qXvf4hpJM^MLDRRVX9QOmU^g4b&=FRRjMD;9|?s8H2ZIt4N(99002ov JPDHLkV1gstq`m+E delta 2044 zcmV-#$nciQYTG++Pj9Sr_CS*wR8-d7U^OaoaBFN0e-tFJc01J9ROi>y{FtjJd!;oZ`NC~x=c z0?)J4XJ|nM?Ajhef0R`L`?fYhGi9)F?)&@{y=gl@z<ueVy{n(8e z16f1VWoSVKtkNNxeIv-0?7j?+8zMaI*cJlF&+mZrrSP!-7Ajmd>GQ3#U5MmkH)afE zAEHYI%$xJc5Y0^exeSg7Y_xr8=m+8M(eOVz%|OaD^#hfFe?C8o46d5=*~a-!MDnp4 z=a1zhMAr(KwYW@%s3`ab*Q+c%74q|2m?%X?nyM~8ATa+}Vj;>poiP%kXT&FU3jKZr zTR;00@%hp7Be-_jbDF2R5b9UdJb5I65S8n9N_@iJ-(gx)WS)GF7PzQwZX|&aJwZRj zCn$jSpXU^re-aj&RtZXh8}&=2+kKGT*Wz5B%&roggEFYrdZy_1&{N)BP-bSJ~f{nk^iQ8`*&@6Wj`oELuAtnFzp!^teNE2B69Vut^@LX&J zkKWv|e<*z{}*eD-il^-bTnD`P%yPu|4}uaaMpBU7`16ItwJv%-?1f*; zMCW2r5l#R7?Odv6j6gmLR3>3ggHx&3_bCF?B`F7gA-Z`Rp(ud23S3oY z_-;SBcq8oqe;IxYk}Gv1zK=)*jy31>(2&5h!pc>^q;FA1Z523Ck>OhbuGA?-IQ?^( z!Q%2A`F%D5eV-z*Q<#41OZDAV;Ho0Sf8PsmqPz(IbXA_=fli;mWAj_!@3A$vkH!R^ zsrh<_tpZo&8NMsPiQ*#sa6$)tYs2*$X?KADKXQw}v6iepE)%$pr2;1kGkjlw6NN?i z@ti83;oJ@1kf_%d;ouVLCwsLsGV>ad*Rx4mx`N&n;6zaoKE47U)ho&H;0c*7e}IKW zc$@wszoS$cBXO@Of$P~7;6zCgeki~lE`9C49Djr7L=Wz&@PZ<|S4i-00|M9bUVsw? zMK~4U#|UO;crd?}FTxe82#@>;yDQ4W;9o#w-V^jC_QIAF;Z%ShG_yBp&Xuk0BgU|z zMvb(JW)$J!4>ig6z+nHE0D(nVf5V33mmxAUyi+#6gDJZd;pz5c?pis5RWt zX?q<`;lpj*3=iZt;fipT`Hm>MGpguZgI9wH|1uzOHTBos3Fo&`{v{#TufIDc>FDlE z^pVZ)RPW$v{GPX`5Xx_&w|eqk7h;NW)Jxaj`euER;r|H)t|Xk11x0x9QdVDs0{~j4 zQ{ez#?*#y~Oe(1v0>|w~h68XPoEn@Sfs11T00J27VFPdxn diff --git a/assets/sprites/collectible-idle.ase b/assets/sprites/collectible-idle.ase new file mode 100644 index 0000000000000000000000000000000000000000..890bbf0cb1ffe244d9e50e84c488b813ce98bff0 GIT binary patch literal 1655 zcmd^En$=F|9#OeZR<5Ut3FvTL%HjdnK90MCv8e?Y z_ifetf_J^aDjplWFXVzl5+Nubr~~&{*nnYnc3{SPwqO%a%_m~@uYPNe1FcQe_jp@6 zf(h2@Y{d=^pp5MX4ySv7Z&oAOSBSzAitvLR%%BA)2*Cy_@PGsi5FgnQ9H|i*c@Y*#5fd2^5a|#Nxe&@qor#8r z{m(#Hf=nDf&X0(vIc`T<5{)3VG1p)F`yOM$m^drS5q9dgciyn8A zR9;|gz4NKw{(j0-flX4=tnQA^htbKC3F$Z1f@Xde9`uRG=DNqE6kVDTsUlq5W2Jv8 zCh3gCOQ%HLs$2FqtQG~Hk;e_3y8{nLEU&kg_R-r+-OhC#6%58)XiL&zo!9sK zcvN5ZQiohJ6EP#^&9$Y^UtTE_x3k;0)sli5*=gI{#d-tb&;%v#D|uyZ(p2fh)4JOF zrjYtrk#Ck{aLIxnDXJOgySG-JE^n-qkMqaw3_6X}ig)?X-H~w=sXwv<8KORw!51mY z_~6kLmX&uyb?X}JR~Ixkz6kYqFo&hChK%{D8Y9AJqnc*@76(1w#EZ_H9y+s5YggQ- zq>?bwTd36C3c#}NJ)X4L6_*+1U2*CBMa*sK4I@M3(s zX&)nt?qxJzrJJX|ZtFn0c_chr?43bPvsfy+62<`Q z=)BbVjK61IVm99&sXWjSnKQIzo?pXpn+-~SQM%Y4W~cOfF87k%m+8t=Z><-y_RACj iGWs>EfsQ}Aqw7aC2737;iDb^4!t8FBF!2BVnEwL9cYbXE literal 0 HcmV?d00001 diff --git a/assets/sprites/collectible-idle.json b/assets/sprites/collectible-idle.json index f084798..625893b 100644 --- a/assets/sprites/collectible-idle.json +++ b/assets/sprites/collectible-idle.json @@ -1,38 +1,38 @@ { "frames": [ { - "filename": "collectible #Idle 0.ase", - "frame": { "x": 0, "y": 0, "w": 64, "h": 64 }, + "filename": "collectible-idle 0.ase", + "frame": { "x": 0, "y": 0, "w": 32, "h": 32 }, "rotated": false, "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, - "sourceSize": { "w": 64, "h": 64 }, + "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, + "sourceSize": { "w": 32, "h": 32 }, "duration": 200 }, { - "filename": "collectible #Idle 1.ase", - "frame": { "x": 64, "y": 0, "w": 64, "h": 64 }, + "filename": "collectible-idle 1.ase", + "frame": { "x": 32, "y": 0, "w": 32, "h": 32 }, "rotated": false, "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, - "sourceSize": { "w": 64, "h": 64 }, + "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, + "sourceSize": { "w": 32, "h": 32 }, "duration": 200 }, { - "filename": "collectible #Idle 2.ase", - "frame": { "x": 128, "y": 0, "w": 64, "h": 64 }, + "filename": "collectible-idle 2.ase", + "frame": { "x": 64, "y": 0, "w": 32, "h": 32 }, "rotated": false, "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, - "sourceSize": { "w": 64, "h": 64 }, + "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, + "sourceSize": { "w": 32, "h": 32 }, "duration": 200 }, { - "filename": "collectible #Idle 3.ase", - "frame": { "x": 192, "y": 0, "w": 64, "h": 64 }, + "filename": "collectible-idle 3.ase", + "frame": { "x": 96, "y": 0, "w": 32, "h": 32 }, "rotated": false, "trimmed": false, - "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, - "sourceSize": { "w": 64, "h": 64 }, + "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 }, + "sourceSize": { "w": 32, "h": 32 }, "duration": 200 } ], @@ -40,26 +40,13 @@ "app": "http://www.aseprite.org/", "version": "1.x-dev", "image": "collectible-idle.png", - "format": "I8", - "size": { "w": 256, "h": 64 }, + "format": "RGBA8888", + "size": { "w": 128, "h": 32 }, "scale": "1", "frameTags": [ - { "name": "Detonation", "from": 1, "to": 14, "direction": "forward" }, - { "name": "Idle", "from": 15, "to": 18, "direction": "forward" } ], "layers": [ - { "name": "Yellow Sphere", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 8", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 7", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 6", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 5", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 4", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 3", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 2", "opacity": 255, "blendMode": "normal" }, - { "name": "Layer 1", "opacity": 255, "blendMode": "normal" }, - { "name": "Blitz Outer", "opacity": 70, "blendMode": "normal" }, - { "name": "Blitz Middle", "opacity": 84, "blendMode": "normal" }, - { "name": "Blitz Inner", "opacity": 97, "blendMode": "normal" } + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } ], "slices": [ ] diff --git a/assets/sprites/collectible-idle.png b/assets/sprites/collectible-idle.png index 8b5c56a3c378426387e169276d2299ba76c36af4..5e9160e8597d11219a70adf8b790961373cb00f2 100644 GIT binary patch literal 931 zcmV;U16=%xP)Px&T}ebiRCt{2nlVq?Kp2MKlrBVKqfWP^?T{!W>c-BHp}-gnB15DwVzE^U6LJUZbI!-vced}+O8H(XigUK_c|YHGclJ2| zi^XEGSS%Kc#ab+t@|?6>UJX>`57YZx^|Ah)GBudk@aKD#*uKp#chR=%L|F6$v>CtA z339{N>K@8(-eNkw0)W`|+U|8BL#AF9H34nMuRV}}?_a+}qZ(kn1vI-I0Kih~008hf zI0k?y2~N*^99`UHHe=$$Gyz>ThOPnT!L=FxGn0d;@o#QRxEzFK)cEo?>2Ra+q%5z* zBm}~M0bph81S?x7v9e*m9XrRSmdmTb`CbLXejCkh2erC~THQml+rhBk#<1UZ#s%EO zpPB$v$I08@xPDVa%ZcEYeMK6fl<1PoYkw%orrwe7XF~qvk8o(9T zRs5;L1Z6z+deV9ek(u7M7l8rb>-nt8Mm50E#a*FcQRTseZ-brWLC1H=HsJ&2@h9$7 zW!`!Vkr8o3XjEs3CYJAP28HHEl?N3^#V^X%c}U~CX6yKXdHktU0L>@0x`*DjhxY!j zDC0yCR5V5BQ5|_DdobnOU?+Ld@m;cQ$FIE!q&t3^>*zbl1nKjq7h1TfL=Pr>n{1Vb zG`?%L?f9Rk?G$fpl3=pvQPGxE9&~)$l0BsHU9)xktnq6{fa&<^N!BGn8r48KL=~0n zVcvY%LmJ;TTgT5De`*4}diU)~*`MzpBAER7xE~HxwffA2xH6bWvIi5sEw;*oDc=P< z3*Xd3;uP>Oy)V&S=;PoRORa+l-rM$~$BtS(+-`O|%I8z6OY6kYLsov#M={w$7QSt^ zi}w2n{@i~ES=0vjA2jzrgt#=oVzF2(7K_DVu~@8m>Mt6K=ukXr5CZ@J002ovPDHLk FV1hr0$#Vbz literal 685 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K595|SPRGDb89jxUGBW%RK$f_6MeOfsyrOmmWLmhQha^u zl;>ABo5-jq=;(BKYws)9zBkYD#lZpp2!U{eH_*PA(Av5qVKr4Fx@OORs?NqVmS+(^hQVbo~68*Kc1+bB1aIU8PqN+}rLn`LHoqoFahyjoD^7a!&%)6HT{}wjs^G7*f)(@@x%?Wxc&PUHZ z|E}C3;KZQ_BiN?BvFpyQ`2I}9Z9{d#(myNK$3AYK^+qm1^ZuI&*M3))&2LbU`^+%W zxPtq@E3bXu?T`Ha%~Hd#Des|h4I4*X^KynI>z@Ap(dWD+z5f6196`4AaxsqKi~+Hy zwyXJNKPq}(ucO~+Qt{;kSJryDDdL}!zjsEve6?Bk?ubdn*VodO5;0XFea~l`T@60` z;KboqZa&T7`}g%}T5C36Sgm5q8PSqA#vFe+t^?zoGw`Y$QV^n=@Z;{v4+@ciCF&C*)5rlTes`;8rX{ z&D`x9o1{NTeQ~UNAxEh7$g)JCx2sBn=Ht-kyDnDf#KIjCI*qs nZ&_p*Sw(=-%#%N|Y?*wB?Kqgn0^)6U;xJ}p6xm$HxkVTNE7BM% delta 627 zcmbPib;q9ljywaygQda~*((`SHa6xlv%4@bFi0pcP5#ELCeF_w1mqzB76#9hoYcv8 zSvF2S%fi8!viTZ|3?pj_PzCekTP#~9uVye(ogS zq+YjA!bZm}!`7c$o#&xHIsM{$x4Vt2X8uwRb-C2yB4KJI>{02GlD{J0#E)_t$4e@I zEWK>grs)W1PL_V+%2NMMirFfoZRaXhcgM%)Tyi_zpJe@I@_)j>-`ovCIaUG)5snpL5CA#W{~|A|A;&IRk(h?~@A2;!)?Sy% z^a)tAeb(*e zk5AwHU#TZPd*=+6e+iQqTt2Eya_bPC-{9W2hkeK6ocql0_u9YPCcK&4U@W0hIFb~M E0C(ry&j0`b diff --git a/components/components.go b/components/components.go index 61102cb..8be3044 100644 --- a/components/components.go +++ b/components/components.go @@ -4,7 +4,9 @@ package components type Tilish struct{} type Solid struct{} type Floor struct{} -type Collectible struct{} +type Collectible struct { + Hit bool +} type Obstacle struct { Direction int diff --git a/components/renderable.go b/components/renderable.go index d9bd7e9..b1d9622 100644 --- a/components/renderable.go +++ b/components/renderable.go @@ -8,40 +8,66 @@ import ( "github.com/hajimehoshi/ebiten/v2" ) +const ( + Destruction = iota + Idle + Collision +) + // virtual location, aka tile address type Animation struct { - Active bool // animation is running - Loop bool // remove the entity if false, loop endless otherwise - Index int // where we are currently - Sprites []assets.AnimationSprite - Width, Height int // single sprite measurements + Active bool // animation is running + Loop bool // remove the entity if false, loop endless otherwise + Index int // where we are currently + Sprites []assets.AnimationSprite // each element contains the sprite and duration + Width, Height int // single sprite measurements Timer Timer Trigger string + Data assets.TileAnimation } type Renderable struct { - Pos *Position // just for debugging, will not used as positiion! - Image *ebiten.Image - DamageImage *ebiten.Image // FIXME: put into its own struct - Damaged int - Shader *ebiten.Shader - Animate Animation - IdleAnimate Animation - Hidden bool + Pos *Position // just for debugging, will not used as positiion! + Image *ebiten.Image + DamageImage *ebiten.Image // FIXME: put into its own struct + Damaged int + Shader *ebiten.Shader + DestructionAnimate Animation + IdleAnimate Animation + CollisionAnimate Animation + Hidden bool } -func (render *Renderable) StartAnimation() { - render.Hidden = true - render.Animate.Active = true - render.Animate.Timer.Start(config.ANIMATION_STARTWAIT) - - switch render.Animate.Trigger { - case "OnCollision": +func (render *Renderable) StartAnimation(which int) { + switch which { + case Collision: fallthrough - case "OnDestruct": - render.Animate.Loop = false - case "OnIdle": - render.Animate.Loop = true + case Destruction: + render.Hidden = true + render.DestructionAnimate.Active = true + render.DestructionAnimate.Timer.Start(config.ANIMATION_STARTWAIT) + render.IdleAnimate.Active = false + case Idle: + render.IdleAnimate.Active = true + render.IdleAnimate.Loop = true + render.IdleAnimate.Timer.Start(0) + } +} + +func (render *Renderable) StopAnimation(which int) { + switch which { + case Collision: + render.CollisionAnimate.Active = false + case Idle: + render.IdleAnimate.Active = false + } +} + +func (render *Renderable) Animations() map[int]*Animation { + return map[int]*Animation{ + Collision: &render.CollisionAnimate, + Destruction: &render.DestructionAnimate, + Idle: &render.IdleAnimate, } } diff --git a/config/static.go b/config/static.go index a24cbbf..a7e0e61 100644 --- a/config/static.go +++ b/config/static.go @@ -18,8 +18,8 @@ const ( PLAYERSPEED int = 5 ANIMATION_STARTWAIT time.Duration = 30 * time.Millisecond // how long to wait to start collectible animation ANIMATION_LOOPWAIT time.Duration = 40 * time.Millisecond // how much time to wait between the sprites - LEVEL_END_WAIT time.Duration = 500 * time.Millisecond - version string = "1.3.0" + LEVEL_END_WAIT time.Duration = 100 * time.Millisecond + version string = "1.4.0" MenuRectX int = 600 MenuRectY int = 0 diff --git a/game/levels.go b/game/levels.go index c1b49cf..b5a01fd 100644 --- a/game/levels.go +++ b/game/levels.go @@ -2,7 +2,6 @@ package game import ( "image" - "log" "log/slog" "openquell/assets" "openquell/components" @@ -198,27 +197,6 @@ func LevelToSlice(game *Game, level *ldtkgo.Level, tilesize int) (Map, Map) { } tileRect := entity.TileRect - animationtrigger := util.GetPropertyString(entity, "AnimationTrigger") - slog.Debug("got trigger", "trigger", animationtrigger) - - //animateondestruct := util.GetPropertyBool(entity, "AnimateOnDestruct") - // FIXME: also check for AnimationLoop and other animation reasons - // if animateondestruct { - if animationtrigger != "" { - tile.AnimateOnDestruct = true - - animation := util.GetPropertyString(entity, "AnimateSpriteSheet") - if animation != "" { - if !util.Exists(assets.Animations, animation) { - log.Fatalf("entity %s refers to non existent animation set %s", - entity.Identifier, animation) - } - } - - tile.AnimationSpriteSheet = assets.Animations[animation] - tile.AnimationTrigger = animationtrigger - } - tile.Sprite = tileset.SubImage( image.Rect(tileRect.X, tileRect.Y, tileRect.X+tileRect.W, diff --git a/grid/grid.go b/grid/grid.go index f34dbeb..9799f2c 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -159,13 +159,24 @@ func NewGrid(world *ecs.World, render.Image = tile.Sprite render.Pos = pos - //if tile.AnimateOnDestruct { - if tile.AnimationTrigger != "" { - // FIXME: be more generic, use LDTK enum - render.Animate.Sprites = tile.AnimationSpriteSheet.Sprites - render.Animate.Width = tile.AnimationSpriteSheet.Width - render.Animate.Height = tile.AnimationSpriteSheet.Height - render.Animate.Trigger = tile.AnimationTrigger + if tile.Animation.OnCollision { + render.CollisionAnimate.Sprites = tile.Animation.CollisionSheet.Sprites + render.CollisionAnimate.Width = tile.Animation.CollisionSheet.Width + render.CollisionAnimate.Height = tile.Animation.CollisionSheet.Height + } + + if tile.Animation.OnDestruction { + render.DestructionAnimate.Sprites = tile.Animation.DestructionSheet.Sprites + render.DestructionAnimate.Width = tile.Animation.DestructionSheet.Width + render.DestructionAnimate.Height = tile.Animation.DestructionSheet.Height + } + + if tile.Animation.OnIdle { + render.IdleAnimate.Sprites = tile.Animation.IdleSheet.Sprites + render.IdleAnimate.Width = tile.Animation.IdleSheet.Width + render.IdleAnimate.Height = tile.Animation.IdleSheet.Height + render.StartAnimation(components.Idle) + render.Hidden = true // we do NOT render the sprite, but the idle animation instead } default: diff --git a/systems/animation_system.go b/systems/animation_system.go index e0808e1..254eb22 100644 --- a/systems/animation_system.go +++ b/systems/animation_system.go @@ -1,8 +1,9 @@ package systems import ( + "log/slog" + "openquell/components" . "openquell/components" - "openquell/config" "github.com/hajimehoshi/ebiten/v2" "github.com/mlange-42/arche/ecs" @@ -32,28 +33,35 @@ func (system *AnimationSystem) Update() error { for query.Next() { _, render := query.Get() - if render.Animate.Active { - if render.Animate.Timer.IsReady() { - switch { - // animation shows from earlier tick, animate - case render.Animate.Index > -1 && render.Animate.Index < len(render.Animate.Sprites)-1: - render.Animate.Index += 1 - render.Animate.Timer.Start(config.ANIMATION_LOOPWAIT) - default: - // last sprite reached - if render.Animate.Loop { - render.Animate.Index = 0 - } else { - EntitiesToRemove = append(EntitiesToRemove, query.Entity()) + + for animationtype, animate := range render.Animations() { + if animate.Active { + if animate.Timer.IsReady() { + switch { + // animation shows from earlier tick, animate + case animate.Index > -1 && animate.Index < len(animate.Sprites)-1: + animate.Index += 1 + animate.Timer.Start(animate.GetDuration()) + default: + // last sprite reached + if animate.Loop { + animate.Index = 0 + animate.Timer.Start(animate.GetDuration()) + } + + if animationtype == components.Destruction { + EntitiesToRemove = append(EntitiesToRemove, query.Entity()) + } } + } else { + animate.Timer.Update() } - } else { - render.Animate.Timer.Update() } } } for _, entity := range EntitiesToRemove { + slog.Debug("remove collectible") system.World.RemoveEntity(entity) } @@ -68,10 +76,13 @@ func (system *AnimationSystem) Draw(screen *ebiten.Image) { for query.Next() { pos, render := query.Get() - if render.Animate.Active { - op.GeoM.Reset() - op.GeoM.Translate(float64(pos.X), float64(pos.Y)) - screen.DrawImage(render.Animate.GetSprite(), op) + for _, animate := range render.Animations() { + if animate.Active { + op.GeoM.Reset() + op.GeoM.Translate(float64(pos.X), float64(pos.Y)) + sprite := animate.GetSprite() + screen.DrawImage(sprite, op) + } } } } diff --git a/systems/collectible_system.go b/systems/collectible_system.go index 79765b2..777d823 100644 --- a/systems/collectible_system.go +++ b/systems/collectible_system.go @@ -4,6 +4,7 @@ import ( "log/slog" "openquell/components" . "openquell/components" + . "openquell/config" "openquell/observers" @@ -38,12 +39,18 @@ func (system *CollectibleSystem) Update() error { numcollectibles := query.Count() if numcollectibles == 0 || observer.Lost { + slog.Debug("WON") + timer := observers.GetGameObserver(system.World).StopTimer + if !timer.Running { + timer.Start(LEVEL_END_WAIT) + + } query.Close() return nil } for query.Next() { - colposition, _, render := query.Get() + colposition, collectible, render := query.Get() for _, player := range observer.GetPlayers() { if !system.World.Alive(player) { @@ -54,10 +61,10 @@ func (system *CollectibleSystem) Update() error { playervelocity := (*Velocity)(system.World.Get(player, veloID)) ok, _ := colposition.Intersects(playerposition, playervelocity) - if ok && !render.Hidden { + if ok && !collectible.Hit { slog.Debug("bumped into collectible", "colpos", colposition) - render.StartAnimation() + render.StartAnimation(components.Destruction) // position the animation relative to the middle of the current entity colposition.Update( @@ -65,19 +72,13 @@ func (system *CollectibleSystem) Update() error { colposition.Y-(system.Cellsize/2), 64, ) + + collectible.Hit = true numcollectibles-- } } } - if numcollectibles == 0 { - // winner, winner, chicken dinner! - timer := observers.GetGameObserver(system.World).StopTimer - if !timer.Running { - timer.Start(LEVEL_END_WAIT) - } - } - return nil }