@@ -94,6 +94,9 @@ export function spawnCharacter(
9494 throw new Error ( `Unknown character type: ${ type } ` )
9595}
9696
97+ type MoveType = "linear" | "bounce" | "jump"
98+ type NextState = Dir | "jump" | undefined
99+
97100/** The abstract character class
98101 * The parent class of MainCharacter and NPC.
99102 */
@@ -117,7 +120,7 @@ export abstract class Character implements IChar {
117120 /** The counter of the idle state */
118121 #idleCounter: number = 0
119122 /** Type of the move */
120- #moveType: "linear" | "bounce" = "linear"
123+ #moveType: MoveType = "linear"
121124 /** The key of the physical grid, which is used for collision detection */
122125 #physicalGridKey: string
123126 /** The prefix of assets */
@@ -190,14 +193,14 @@ export abstract class Character implements IChar {
190193 _input : typeof Input ,
191194 _fieldTester : IFieldTester ,
192195 _collisionChecker : CollisionChecker ,
193- ) : Dir | undefined {
196+ ) : NextState {
194197 return undefined
195198 }
196199
197200 onMoveEnd (
198201 _fieldTester : IFieldTester ,
199202 _itemContainer : ItemContainer ,
200- _moveType : "linear" | "bounce" ,
203+ _moveType : MoveType ,
201204 ) : void { }
202205
203206 step (
@@ -208,7 +211,10 @@ export abstract class Character implements IChar {
208211 ) {
209212 if ( this . #movePhase === 0 ) {
210213 const nextState = this . getNextState ( input , fieldTester , collisionChecker )
211- if ( nextState ) {
214+ if (
215+ nextState === "up" || nextState === "down" ||
216+ nextState === "left" || nextState === "right"
217+ ) {
212218 this . setDir ( nextState )
213219 this . #isMoving = true
214220 this . #idleCounter = 0
@@ -218,6 +224,11 @@ export abstract class Character implements IChar {
218224 } else {
219225 this . #moveType = "bounce"
220226 }
227+ } else if ( nextState === "jump" ) {
228+ this . #isMoving = true
229+ this . #idleCounter = 0
230+ this . #moveType = "jump"
231+ // Jumping is always allowed.
221232 }
222233 }
223234
@@ -253,6 +264,19 @@ export abstract class Character implements IChar {
253264 this . #d = 0
254265 this . onMoveEnd ( fieldTester , itemContainer , this . #moveType)
255266 }
267+ } else if ( this . #moveType === "jump" ) {
268+ this . #movePhase += this . #speed
269+ if ( this . #movePhase < 4 ) {
270+ this . #d += this . #speed * 2
271+ } else {
272+ this . #d -= this . #speed * 2
273+ }
274+ if ( this . #movePhase == 8 ) {
275+ this . #movePhase = 0
276+ this . #isMoving = false
277+ this . #d = 0
278+ this . onMoveEnd ( fieldTester , itemContainer , this . #moveType)
279+ }
256280 }
257281 } else {
258282 this . #idleCounter += 1
@@ -291,6 +315,10 @@ export abstract class Character implements IChar {
291315
292316 /** Gets the x of the world coordinates */
293317 get x ( ) : number {
318+ if ( this . #isMoving && this . #moveType === "jump" ) {
319+ return this . #i * CELL_SIZE
320+ }
321+
294322 if ( this . #dir === LEFT ) {
295323 return this . #i * CELL_SIZE - this . #d
296324 } else if ( this . #dir === RIGHT ) {
@@ -306,6 +334,10 @@ export abstract class Character implements IChar {
306334
307335 /** Gets the y of the world coordinates */
308336 get y ( ) : number {
337+ if ( this . #isMoving && this . #moveType === "jump" ) {
338+ return this . #j * CELL_SIZE - this . #d
339+ }
340+
309341 if ( this . #dir === UP ) {
310342 return this . #j * CELL_SIZE - this . #d
311343 } else if ( this . #dir === DOWN ) {
@@ -417,7 +449,7 @@ export class MainCharacter extends Character {
417449 input : typeof Input ,
418450 _fieldTester : IFieldTester ,
419451 _collisionChecker : CollisionChecker ,
420- ) : Dir | undefined {
452+ ) : NextState {
421453 if ( input . up ) {
422454 return UP
423455 } else if ( input . down ) {
@@ -426,6 +458,8 @@ export class MainCharacter extends Character {
426458 return LEFT
427459 } else if ( input . right ) {
428460 return RIGHT
461+ } else if ( input . space ) {
462+ return "jump"
429463 }
430464 return undefined
431465 }
@@ -462,18 +496,21 @@ export class RandomWalkNPC extends Character {
462496 _input : typeof Input ,
463497 fieldTester : IFieldTester ,
464498 collisionChecker : CollisionChecker ,
465- ) : Dir | undefined {
499+ ) : NextState {
466500 this . #counter -= 1
467501 if ( this . #counter <= 0 ) {
468502 this . #counter = randomInt ( 8 ) + 4
469503 // If the character can keep going in the current direction,
470- // it will keep going with 80 % probability.
504+ // it will keep going with 96 % probability.
471505 if (
472506 this . canEnter ( this . dir , fieldTester , collisionChecker ) &&
473507 Math . random ( ) < 0.96
474508 ) {
475509 return this . dir
476510 }
511+ if ( randomInt ( 2 ) === 0 ) {
512+ return "jump"
513+ }
477514 const nextCandidate = [ UP , DOWN , LEFT , RIGHT ] as Dir [ ]
478515 return choice ( nextCandidate . filter ( ( d ) => {
479516 return this . canEnter ( d , fieldTester , collisionChecker )
0 commit comments