class Conveyor extends Phaser.GameObjects.TileSprite{
  
  constructor(scene, x, y, w, h) {
    super(scene, x, y, w, h, 'conveyor');
    this.scene = scene;
    scene.add.existing(this);
    this.setOrigin(0);
    scene.physics.add.existing(this, true);   
    
    this.surfaceSpeed = new Phaser.Math.Vector2(0.5,0);
    this.leftWheel = this.scene.add.sprite(x,y-1,'wheel').setOrigin(0,0)
    this.rightWheel = this.scene.add.sprite(x+w, y-1, 'wheel').setOrigin(1,0).setFlipX(true);
    this.reverseTrigger;
 
  }

  setSpeed(x = 0.5) {
    this.surfaceSpeed.set(x,0);
    this.moveWheels();
    return this;
  }
  
  moveWheels() {
     if (this.surfaceSpeed.x>0) {
      this.leftWheel.play('rotate');
      this.rightWheel.playReverse('rotate');
    } else {
      this.leftWheel.playReverse('rotate');
      this.rightWheel.play('rotate');
    }
  }

  setAutoReverse(cycle) {
    this.reverseTrigger = this.scene.time.addEvent({
      delay: cycle,
      repeat: -1,
      callback: this.reverse,
      callbackScope: this
    })
  }
  
  reverse() {
    this.surfaceSpeed.negate();
    this.moveWheels();
  }
  
}

class Lady extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y) {
    super(scene, x, y, 'lady');
    this.scene = scene;
    scene.add.existing(this);
    scene.physics.add.existing(this);
 
  } 
}

class Kong extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y) {
    super(scene, x, y, 'kong-idle');
    this.scene = scene;
    scene.add.existing(this);
    scene.physics.add.existing(this);
    this.setOrigin(0.5,0);
    this.staus = Kong.Status.Idle;
 
  }
  
  static Status = {
    Idle: 1,
    Stomping: 2,
  }

 
}


class Mario extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y) {
    super(scene, x, y, 'mario');
    this.scene = scene;
    scene.add.existing(this);
    scene.physics.add.existing(this);
    this.body.setSize(3, 16).setOffset(5,0); 
    this.setCollideWorldBounds(true);
    this.pushUpAnimFrame = 0; // this is the frame counter for the special animation for Mario pushing himself up at top of ladder
    
    this.status = Mario.Status.Walking;
  } 

  static Status = {
    Walking: 1,
    Jumping: 2,
    Climbing: 3,
    PushUp: 4,
  }

  update() {

    switch(this.status) {
 
      case Mario.Status.Walking:
        
        if (this.scene.cursors.right.isDown) {
          this.setVelocityX(60);
          this.setFlipX(true)
          this.anims.play('walking', true)
        } else if (this.scene.cursors.left.isDown) {   
          this.setVelocityX(-60)
          this.setFlipX(false)
          this.anims.play('walking', true)
        } else {
          this.setVelocityX(0)
          this.anims.play('idle', true);
        }
     
        if (this.scene.cursors.up.isDown && this.canClimb(this)) {
            this.setStatus(Mario.Status.Climbing);
 
        }       
        if (this.scene.cursors.down.isDown && this.canClimb(this.getBottomCenter())) {
          this.setStatus(Mario.Status.Climbing)
        }

        if (Phaser.Input.Keyboard.JustDown(this.scene.spacebar) && this.body.onFloor()) {
          this.setVelocityY(-120);
          this.setStatus(Mario.Status.Jumping);
        }
    
        break;
     
      case Mario.Status.Jumping:
    
        if (this.body.onFloor()) this.setStatus(Mario.Status.Walking);
        
         break;
    
      case Mario.Status.PushUp:
   
        if (this.pushUpAnimFrame <7)  {
          this.pushUpAnimFrame +=0.25;        
          this.setFrame(Math.floor(this.pushUpAnimFrame));
          this.y-=0.25;    
        }
        else 
        {
          this.body.stop()
          this.anims.play('walking', true);
          this.setStatus(Mario.Status.Walking);
        }
      
        break;

      case Mario.Status.Climbing:

        if (this.scene.cursors.right.isDown && this.isScaffolding(this.getBottomCenter()))
        {
          this.setVelocityX(40);
          this.setFlipX(false)
          this.anims.play('walking', true);
          this.setStatus(Mario.Status.Walking);
        }
        else if (this.scene.cursors.left.isDown && this.isScaffolding(this.getBottomCenter()))
        {   
          this.setVelocityX(-40)
          this.setFlipX(true)
          this.anims.play('walking', true);
          this.setStatus(Mario.Status.Walking);
        } 
        else if (this.scene.cursors.up.isDown)
        {
          if (this.isLadder(this.getCenter()))
          {
            this.anims.play('climb', true);
            this.body.setVelocityY(-40);            
          }
          else {
            if (this.isScaffolding(this.getBottomCenter()))           
            {
              this.setStatus(Mario.Status.PushUp); 
            } else 
            {
              this.body.stop()
              this.anims.stop()
            }  

          }
        }
        else if (this.scene.cursors.down.isDown) 
        {
          if (this.isLadder(this.getBottomCenter()))
          {
            this.anims.play('climb', true);
            this.body.setVelocityY(40)                       
          } else  {
            this.anims.stop();
            this.body.setVelocityY(0);
            this.setStatus(Mario.Status.Walking);
          }
        }
        else
        {
          this.anims.stop()
          this.body.setVelocityY(0);
        }
        break;
     
    }
  }

  setStatus(newStatus) {
    this.status = newStatus;
    switch (this.status) {
      case Mario.Status.Walking:
        this.body.setAllowGravity(true);
        break;
      case Mario.Status.PushUp:
        this.pushUpAnimFrame = 0;
        this.anims.stop();
        this.setTexture('liftUp', this.pushUpAnimFrame);
        this.body.stop();
        break;
      case Mario.Status.Jumping:
        this.scene.jumpSFX.play();
        this.anims.play('jump', true)
        break;
      case Mario.Status.Climbing:
        this.body.stop();
        this.body.setAllowGravity(false);
        break;
    }
      
  }
  
  canClimb(position) {
    const tile = this.scene.map.getTileAtWorldXY(position.x,position.y, true, this.scene.cameras.main, this.scene.ladders);
    const isLadder = (tile.index === 9);
    return (
      isLadder && (this.body.left>=tile.pixelX && this.body.right<=tile.right)
    )
  }
  
  isLadder(position) {
    const tile = this.scene.map.getTileAtWorldXY(position.x,position.y, true, this.scene.cameras.main, this.scene.ladders);
    return tile.index === 9 ;  
  }
  
  isScaffolding(position) {
    const tile = this.scene.map.getTileAtWorldXY(position.x,position.y, true, this.scene.cameras.main, this.scene.scaffolding);
    return (tile.index === 17 || tile.index === 33 || tile.index === 2);  
  }
  
}

class Game extends Phaser.Scene {
  constructor() {
    super({key: "Game"});
    this.score = 0;
    
  }

  preload() {
   
    this.load.setBaseURL('https://raw.githubusercontent.com/cedarcantab/DONKEY_KONG/main/assets/');
    this.load.atlas('mario', 'mario_sprite.png', 'mario_sprite.json');
    this.load.tilemapTiledJSON('map', 'ConverBeltStage.json');
    this.load.image('tileset', 'tileset2.png');
  
    this.load.image('conveyor', 'conveyorBelt.png'); // 62 x 46
    this.load.image('pie', 'pie.png');
    this.load.spritesheet('wheel', 'wheel.png', {frameWidth: 13, frameHeight: 10})
    this.load.spritesheet('liftUp', 'liftUp.png', {frameWidth: 16, frameHeight: 16});
    this.load.spritesheet('kong-idle', 'kong-idle.png', {frameWidth: 46, frameHeight: 32});
    this.load.spritesheet('lady', 'lady.png', {frameWidth: 16, frameHeight: 22});
    this.load.spritesheet('oildrum', 'oildrum.png', {frameWidth: 16, frameHeight: 32})
    this.load.audio('jumpSFX', 'jump.wav');
    this.load.audio ('stompSFX','dkstomp.wav')
    this.load.image('namcoFont', 'fontset.png');
    this.load.image('HUD', 'HUD.png');
    this.load.image('100', '100.png')
   
  }

  create() {

    this.createAnims();
    this.createFonts();
    
    this.map = this.make.tilemap({ key: 'map', tileWidth: 8, tileHeight: 8 });
    const tileset = this.map.addTilesetImage('tileset','tileset');
    const laddersLayerID = this.map.getLayerIndex('Ladders');
    const scaffoldingLayerID = this.map.getLayerIndex('Scaffolding');
    this.ladders = this.map.createLayer(laddersLayerID, tileset, 0, 0);
    this.scaffolding = this.map.createLayer(scaffoldingLayerID, tileset, 0, 0); // layer index, tileset, x, y
    this.map.setCollision([17,33], true, true, this.scaffolding);

    this.conveyors = this.add.group();
    
    var conveyor
    conveyor = new Conveyor(this, 0,11*8,28*8, 8);
    conveyor.setSpeed(0.5).setAutoReverse(5500);
    this.conveyors.add(conveyor);
    conveyor = new Conveyor(this, -16,16*8,15*8, 8);
    conveyor.setSpeed(-0.5).setAutoReverse(4000);
    this.conveyors.add(conveyor);
    conveyor = new Conveyor(this, 15*8,16*8,15*8, 8);
    conveyor.setSpeed(0.5).setAutoReverse(4000);
    this.conveyors.add(conveyor);
    conveyor = new Conveyor(this, 0,26*8,28*8, 8);
    conveyor.setSpeed(-0.5);    
    this.conveyors.add(conveyor);
    
        
    this.kong = new Kong(this, 40,30);
    this.mario = new Mario(this, 90,220);   
    this.lady = new Lady(this, 112,20);
    
    this.physics.add.collider(this.mario, this.scaffolding, null, this.checkXSection, this);
    this.physics.add.collider(this.lady, this.scaffolding)

    this.physics.add.collider(this.mario, this.conveyors, this.surfaceEffect,this.checkXSection, this);
    this.physics.add.collider(this.kong, this.conveyors, this.surfaceEffect);
    
    this.add.sprite(122-10,16*8,'oildrum').play('oil-burn').setScale(1.2,1)
    
    this.add.image(0,0,'HUD').setOrigin(0)
    
    this.jumpSFX = this.sound.add('jumpSFX');
    this.stompSFX = this.sound.add('stompSFX');
  
    this.scoreText = this.add.bitmapText(8,8,'namcoFont', "000000");
    this.score100 = this.add.image(0,0,'100').setVisible(false);
   
    this.cursors = this.input.keyboard.createCursorKeys();
    this.spacebar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

  }

  update() {
    this.mario.update();

  }

  renderScore(score) {
     this.scoreText.setText(Phaser.Utils.String.Pad(this.score, 6, '0', 1));
  }
  
  checkXSection(mario, tile) {
   
    return !(
     ((mario.status === Mario.Status.Climbing) || (mario.status === Mario.Status.Walking && this.cursors.down.isDown && mario.canClimb(mario.getBottomCenter())))
   )
  } 
  
  surfaceEffect(object, belt) {
    if (belt.body.touching.up && object.body.touching.down) {
      object.body.position.add(belt.surfaceSpeed);      
    }
  }
  
  createFonts() {
     this.cache.bitmapFont.add('namcoFont', Phaser.GameObjects.RetroFont.Parse(this, {
      image: 'namcoFont',
      width: 8,
      height: 8,
      chars:  '0123456789',
      charsPerRow: 10,
    }));
  }
  
  createAnims() {
   this.anims.create({
      key: 'rotate',
      frames: this.anims.generateFrameNumbers('wheel', { start: 0, end: 2 }),
      frameRate: 3,
      repeat: -1
    });
    this.anims.create({
      key: 'walking',
       frames: this.anims.generateFrameNames('mario', {
        prefix: 'mario',
        suffix: '.png',
        start: 1,
        end: 2
      }),
      frameRate: 13,
      repeat: -1
    });
 
    this.anims.create({
      key: 'climb',
      frames: this.anims.generateFrameNames('mario', {
        prefix: 'climb',
        suffix: '.png',
        start: 0,
        end: 1
      }),
      frameRate: 13,
      repeat: -1
    });

    this.anims.create({
      key: 'idle',
      frames: [{key:'mario', frame: 'mario0.png' }]
    });

    this.anims.create({
      key: 'jump',
      frames: [{key:'mario', frame: 'jump.png' }] 
    });
   
 
    this.anims.create({
      key: 'kong-stomp',
      frames: this.anims.generateFrameNumbers('kong-idle', { start: 1, end: 2 }),
      frameRate: 1,
      repeat: 2
    });
   this.anims.create({
      key: 'oil-burn',
      frames: this.anims.generateFrameNumbers('oildrum', { start: 1, end: 4 }),
      frameRate: 10,
      repeat: -1
    });
   
  }

}

const config = {
  width: 28*8,
  height: 32*8,
  backgroundColor: 0x000000,
  pixelArt: true,
  zoom: 2,
  physics: {
    default: "arcade",
    arcade: {
      gravity: { y: 500},
      debug: false
    }
  },
  scene: [Game]
};


const game = new Phaser.Game(config);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/phaser/3.55.2/phaser.min.js