1- import { audio , collision , Entity , game , ParticleEmitter , Rect } from "melonjs" ;
1+ import {
2+ audio ,
3+ Body ,
4+ collision ,
5+ game ,
6+ ParticleEmitter ,
7+ Rect ,
8+ Sprite ,
9+ } from "melonjs" ;
210import { gameState } from "../gameState" ;
311
412/**
5- * An enemy entity
13+ * A base enemy entity using Sprite + Body
614 * follow a horizontal path defined by the box size in Tiled
715 */
8- class PathEnemyEntity extends Entity {
16+ class PathEnemyEntity extends Sprite {
17+ alive : boolean ;
18+ startX : number ;
19+ endX : number ;
20+ walkLeft : boolean ;
21+ isMovingEnemy : boolean ;
22+ particleTint : string ;
23+
924 /**
1025 * constructor
1126 */
12- constructor ( x , y , settings ) {
27+ constructor ( x , y , settings , frameNames : string [ ] ) {
1328 // save the area size defined in Tiled
1429 const width = settings . width || settings . framewidth ;
1530
16- // adjust the setting size to the sprite one
17- settings . width = settings . framewidth ;
18- settings . height = settings . frameheight ;
31+ // create the sprite from texture atlas animation frames
32+ super ( x , y , {
33+ ...gameState . texture . getAnimationSettings ( frameNames ) ,
34+ anchorPoint : { x : 0 , y : 0 } ,
35+ } ) ;
1936
20- // redefine the default shape (used to define path) with a shape matching the renderable
21- settings . shapes [ 0 ] = new Rect (
22- 0 ,
23- 0 ,
24- settings . framewidth ,
25- settings . frameheight ,
26- ) ;
27-
28- // call the super constructor
29- super ( x , y , settings ) ;
37+ // add a physic body matching the sprite max frame dimensions
38+ this . body = new Body ( this , new Rect ( 0 , 0 , this . width , this . height ) ) ;
3039
3140 // set start/end position based on the initial area size
32- this . startX = this . pos . x ;
33- this . endX = this . pos . x + width - settings . framewidth ;
34- this . pos . x = this . pos . x + width - settings . framewidth ;
35-
36- // enemies are not impacted by gravity
37- this . body . gravityScale = 0 ;
41+ this . startX = x ;
42+ this . endX = x + width - settings . framewidth ;
43+ this . pos . x = x + width - settings . framewidth ;
3844
3945 this . walkLeft = false ;
4046
4147 // body walking & flying speed
42- this . body . setMaxVelocity ( settings . velX || 1 , settings . velY || 0 ) ;
48+ this . body . setMaxVelocity ( settings . velX || 1 , settings . velY || 15 ) ;
4349
4450 // set a "enemyObject" type
4551 this . body . collisionType = collision . types . ENEMY_OBJECT ;
@@ -55,6 +61,9 @@ class PathEnemyEntity extends Entity {
5561 // a specific flag to recognize these enemies
5662 this . isMovingEnemy = true ;
5763
64+ // living state
65+ this . alive = true ;
66+
5867 // default tint for particles
5968 this . particleTint = "#FFF" ;
6069 }
@@ -68,7 +77,7 @@ class PathEnemyEntity extends Entity {
6877 if ( this . pos . x <= this . startX ) {
6978 // if reach start position
7079 this . walkLeft = false ;
71- this . renderable . flipX ( true ) ;
80+ this . flipX ( true ) ;
7281 } else {
7382 this . body . force . x = - this . body . maxVel . x ;
7483 }
@@ -78,7 +87,7 @@ class PathEnemyEntity extends Entity {
7887 if ( this . pos . x >= this . endX ) {
7988 // if reach the end position
8089 this . walkLeft = true ;
81- this . renderable . flipX ( false ) ;
90+ this . flipX ( false ) ;
8291 } else {
8392 this . body . force . x = this . body . maxVel . x ;
8493 }
@@ -91,7 +100,12 @@ class PathEnemyEntity extends Entity {
91100 /**
92101 * collision handle
93102 */
94- onCollision ( response ) {
103+ onCollision ( response , other ) {
104+ if ( other . body . collisionType === collision . types . WORLD_SHAPE ) {
105+ // solid against world shapes (platforms, ground)
106+ return true ;
107+ }
108+
95109 // res.y >0 means touched by something on the bottom
96110 // which mean at top position for this one
97111 if ( this . alive && response . overlapV . y > 0 && response . a . body . falling ) {
@@ -102,7 +116,7 @@ class PathEnemyEntity extends Entity {
102116 // make the body static
103117 this . body . setStatic ( true ) ;
104118 // set dead animation
105- this . renderable . setCurrentAnimation ( "dead" ) ;
119+ this . setCurrentAnimation ( "dead" ) ;
106120
107121 const emitter = new ParticleEmitter ( this . centerX , this . centerY , {
108122 width : this . width / 4 ,
@@ -129,84 +143,102 @@ class PathEnemyEntity extends Entity {
129143}
130144
131145/**
132- * An Slime enemy entity
146+ * A Slime enemy entity
133147 * follow a horizontal path defined by the box size in Tiled
134148 */
135149export class SlimeEnemyEntity extends PathEnemyEntity {
136150 /**
137151 * constructor
138152 */
139153 constructor ( x , y , settings ) {
140- // super constructor
141- super ( x , y , settings ) ;
142-
143- // set a renderable
144- this . renderable = gameState . texture . createAnimationFromName ( [
154+ // super constructor with slime frame names
155+ super ( x , y , settings , [
145156 "slime_normal.png" ,
146157 "slime_walk.png" ,
147158 "slime_dead.png" ,
148159 ] ) ;
149160
150161 // custom animation speed ?
151162 if ( settings . animationspeed ) {
152- this . renderable . animationspeed = settings . animationspeed ;
163+ this . animationspeed = settings . animationspeed ;
153164 }
154165
155- // walking animatin
156- this . renderable . addAnimation ( "walk" , [
157- "slime_normal.png" ,
158- "slime_walk.png" ,
159- ] ) ;
160- // dead animatin
161- this . renderable . addAnimation ( "dead" , [ "slime_dead.png" ] ) ;
166+ // walking animation
167+ this . addAnimation ( "walk" , [ "slime_normal.png" , "slime_walk.png" ] ) ;
168+ // dead animation
169+ this . addAnimation ( "dead" , [ "slime_dead.png" ] ) ;
162170
163171 // set default one
164- this . renderable . setCurrentAnimation ( "walk" ) ;
165-
166- // set the renderable position to bottom center
167- this . anchorPoint . set ( 0.5 , 1.0 ) ;
172+ this . setCurrentAnimation ( "walk" ) ;
168173
169174 // particle tint matching the sprite color
170175 this . particleTint = "#FF35B8" ;
171176 }
172177}
173178
174179/**
175- * An Fly enemy entity
180+ * A Fly enemy entity
176181 * follow a horizontal path defined by the box size in Tiled
177182 */
178183export class FlyEnemyEntity extends PathEnemyEntity {
184+ startY : number ;
185+ endY : number ;
186+ flyUp : boolean ;
187+
179188 /**
180189 * constructor
181190 */
182191 constructor ( x , y , settings ) {
183- // super constructor
184- super ( x , y , settings ) ;
185-
186- // set a renderable
187- this . renderable = gameState . texture . createAnimationFromName ( [
188- "fly_normal.png" ,
189- "fly_fly.png" ,
190- "fly_dead.png" ,
191- ] ) ;
192+ // super constructor with fly frame names
193+ super ( x , y , settings , [ "fly_normal.png" , "fly_fly.png" , "fly_dead.png" ] ) ;
194+
195+ // set vertical patrol range (bob up and down by half height)
196+ const bobRange = settings . height || this . height ;
197+ this . startY = y ;
198+ this . endY = y + bobRange ;
199+ this . flyUp = true ;
200+
201+ // allow vertical movement
202+ this . body . setMaxVelocity ( settings . velX || 1 , settings . velY || 1 ) ;
192203
193204 // custom animation speed ?
194205 if ( settings . animationspeed ) {
195- this . renderable . animationspeed = settings . animationspeed ;
206+ this . animationspeed = settings . animationspeed ;
196207 }
197208
198- // walking animatin
199- this . renderable . addAnimation ( "walk" , [ "fly_normal.png" , "fly_fly.png" ] ) ;
200- // dead animatin
201- this . renderable . addAnimation ( "dead" , [ "fly_dead.png" ] ) ;
209+ // walking animation
210+ this . addAnimation ( "walk" , [ "fly_normal.png" , "fly_fly.png" ] ) ;
211+ // dead animation
212+ this . addAnimation ( "dead" , [ "fly_dead.png" ] ) ;
202213
203214 // set default one
204- this . renderable . setCurrentAnimation ( "walk" ) ;
205-
206- // set the renderable position to bottom center
207- this . anchorPoint . set ( 0.5 , 1.0 ) ;
215+ this . setCurrentAnimation ( "walk" ) ;
208216
209217 // particle tint matching the sprite color
210218 this . particleTint = "#000000" ;
211219 }
220+
221+ /**
222+ * manage the fly movement (horizontal + vertical bobbing)
223+ */
224+ update ( dt ) {
225+ if ( this . alive ) {
226+ // vertical bobbing — apply force against gravity to fly
227+ if ( this . flyUp ) {
228+ if ( this . pos . y <= this . startY ) {
229+ this . flyUp = false ;
230+ } else {
231+ this . body . force . y = - this . body . maxVel . y ;
232+ }
233+ } else {
234+ if ( this . pos . y >= this . endY ) {
235+ this . flyUp = true ;
236+ } else {
237+ this . body . force . y = this . body . maxVel . y ;
238+ }
239+ }
240+ }
241+
242+ return super . update ( dt ) ;
243+ }
212244}
0 commit comments