objects/enemies/ogre.js

import Phaser from "phaser";

const Movement = require('../../actions/movement');
const GameCoin = require('../items/coin');
const Sense = require('../../actions/sense');
const dataManager = require('../data');

/**
 * Default characteristics for the ogre - health, speed, damage, coin result, etc
 * 
 * @static
 * @final
 * @memberof ClubCrawler.Objects.Ogre
 */
const DEFAULT_OGRE_STATS = {
    name: "Ogre",
    health: 300,
    speed: 100, // might be redundant with velocity increment being the more relevant one
    maxSpeed: 500,
    updateSpeed: 500,
    velocityIncrement: 400,
    mass: 5,
    drag: 50,
    maxCoins: 10,
    minCoins: 5,
    senseRange: 800,
    damage: 5,
    sfxVolume: 0.1, // not sure why i can't get this to work to turn down the volume on the ogre fx
    spriteKey: "ogre",
    spriteAttackFrame: "attack.png", // not implemented yet
    spriteStillFrame: "still.png", // not implemented yet
    audioSpriteKey: "ogre-sound",
    weapon: null, // implement
}

/** 
 * @classdesc 
 * A class for an Ogre enemy. Uses an atlas w sprite animations
 * 
 * @memberof ClubCrawler.Objects
 * @extends Phaser.GameObjects.Image
 * @implements {ClubCrawler.Actions.Sense.Senser}
 * @implements {ClubCrawler.Actions.Interact.Hurtable}
 * @implements {ClubCrawler.Actions.Interact.DamageDealer}
*/
class Ogre extends Phaser.GameObjects.Image {
    

    /**
     * Constructs the ogre. Ogre uses {@link ClubCrawler.Actions.Sense.sensePlayerRepeat sensePlayerRepeat action} to determine behavior based on player distance.
     * 
     * @param {ClubCrawler.Types.EnemyConfig} config - The configuration object
     * @param {Phaser.Scene} config.scene - The creating scene
     * @param {Item} config.item - The Tiled item having the x,y etc
     */
    constructor(config) {

        //call super
        super(config.scene, config.x, config.y, config.spriteKey ? config.spriteKey : DEFAULT_OGRE_STATS.spriteKey, config.spriteStillFrame ? config.spriteStillFrame : DEFAULT_OGRE_STATS.spriteStillFrame);
        config.scene.add.existing(this);
        config.scene.physics.add.existing(this);
        Object.assign(this, DEFAULT_OGRE_STATS);
        Object.assign(this, config);
        this.setScale(0.75, 0.75);
        this.dealDamageSfx = this.scene.sound.addAudioSprite(this.audioSpriteKey);
        this.dealDamageSfx.volume = this.sfxVolume;
        this.takeDamageSfx = this.scene.sound.addAudioSprite(this.audioSpriteKey);
        this.takeDamageSfx.volume = this.sfxVolume;
        this.shoutSfx = this.scene.sound.addAudioSprite(this.audioSpriteKey);
        this.shoutSfx.volume = this.sfxVolume;
        this.dieSfx = this.scene.sound.addAudioSprite(this.audioSpriteKey);
        this.dieSfx.volume = this.sfxVolume;
        this.hasSensedPlayer = false;
        
        this.scene.time.delayedCall(100, ()=> {
            this.body.setMaxSpeed(this.maxSpeed);
            this.body.setMass(this.mass);
            this.body.setDrag(this.drag);
            Sense.SensePlayerRepeat(this, {});

        },[], this);
    }

    /**
     * @deprecated
     * old bullet interaction
     * @param {any} bullet
     * @returns {any}
     */
    hit(bullet) {
        this.health -= bullet.damage;
        if(this.health <= 0) {
            this.die();
        } else {
            //this.sfx.play('takedamage');
        }
    }
    /**
     * Plays damage sound
     */
    dealDamage() {
        this.dealDamageSfx.play('dealdamage');
    }
    /**
     * Called by collision function. Can react to damage here.
     * 
     * @param {number} damage - amount of damage taken
     */
    takeDamage(damage) { 
        let ogre = this;
        ogre.health -= damage;
        this.takeDamageSfx.play('takedamage');
    }
    /**
     * Called internally if the result of, for example, takeDamage causes it to die
     * 
     * Can clean up any other objects it needs to, like ongoing events, when it dies
     * @fires ClubCrawler.Events.enemyDied
     */
    die() {
        this.dieSfx.play('die');
        this.scene.time.delayedCall(6000, (sounds)=> {
            sounds.forEach( (sound) => {
                sound.destroy();
            })
        }, [[this.dieSfx, this.takeDamageSfx, this.dealDamageSfx, this.shoutSfx]]);
        for(let i = this.minCoins; i < Math.random() * this.maxCoins + this.minCoins; i++) {
            let coin = new GameCoin({scene:this.scene, x:this.x, y:this.y});
            coin.body.setVelocityX(Math.random() * 100 - 50);
            coin.body.setVelocityY(Math.random() * 100 - 50);
        }
        if(this.nextMoveEvent) {
            this.nextMoveEvent.destroy();
        }
        if(this.senseEvents) {
            this.senseEvents.forEach( (event)=> {
                event.destroy();
            });
        }
        dataManager.emitter.emit("enemyDied");
        this.destroy();
    }
    /**
     * Implementation of the sense interface.
     * 
     * This is where Ogre determines action depending on player distance.
     * 
     * Uses either {@link ClubCrawler.Actions.MoveRandomly moveRandomly} or {@link ClubCrawler.Actions.MoveTowardsPlayer moveTowardsPlayer} actions.
     * 
     * @param {ClubCrawler.Types.SensationConfig} sensation
     * @override
     */
    sense(sensation) {
        if(this.body.velocity.x > 0) {
            this.setFlipX(true);
        } else {
            this.setFlipX(false);
        }
        if(sensation.distance < this.senseRange) {
            if(!this.hasSensedPlayer || !this.shoutSfx.isPlaying) { 
                this.hasSensedPlayer = true;
                this.shoutSfx.play("shout");
            }
            Movement.MoveTowardsPlayer(this, {});
        } else {
            Movement.MoveRandomly(this, {speedRatio:0.5});
        }
        if(sensation.distance < this.senseRange/3*2) {
            this.setFrame("attack.png")
        } else {
            this.hasSensedPlayer = false;
            this.setFrame("still.png");
        }
    }

}


module.exports = Ogre