diff --git a/src/shattered_sword/AI/BossAI.ts b/src/shattered_sword/AI/BossAI.ts new file mode 100644 index 0000000..9f0f9ba --- /dev/null +++ b/src/shattered_sword/AI/BossAI.ts @@ -0,0 +1,22 @@ +import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import EnemyAI, { EnemyStates } from "./EnemyAI"; +import BossAttack from "./EnemyStates/BossAttack"; +import Weapon from "../GameSystems/items/Weapon"; +import Vec2 from "../../Wolfie2D/DataTypes/Vec2"; + + +export default class BossAI extends EnemyAI { + + /** The weapon this AI has */ + weapon: Weapon; + + initializeAI(owner: AnimatedSprite, options: Record): void { + super.initializeAI(owner, options); + this.addState(EnemyStates.ATTACK, new BossAttack(this, owner)); + this.weapon = options.weapon; + } + + canAttack(position: Vec2): boolean { + return this.attackTimer.isStopped() && this.owner.position.distanceTo(position)<=96; + } +} \ No newline at end of file diff --git a/src/shattered_sword/AI/EnemyStates/BossAttack.ts b/src/shattered_sword/AI/EnemyStates/BossAttack.ts new file mode 100644 index 0000000..96c276c --- /dev/null +++ b/src/shattered_sword/AI/EnemyStates/BossAttack.ts @@ -0,0 +1,191 @@ +import EnemyAI, { EnemyStates } from "../EnemyAI"; +import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import Attack from "./Attack"; +import BossAI from "../BossAI"; +import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; +import Timer from "../../../Wolfie2D/Timing/Timer"; +import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; +import AABB from "../../../Wolfie2D/DataTypes/Shapes/AABB"; +//TODO - unfinished +export default class BossAttack extends Attack { + runTimer: Timer; + startPosition : Vec2; + pauseTimer : Timer; + atknum : number; + protected velocity: number; + protected distance: number; + + onEnter(options: Record): void { + super.onEnter(options); + this.pauseTimer = new Timer(1000); + this.pauseTimer.start(); + this.atknum = Math.floor(Math.random() * 5); + + this.owner.alpha = 1; //unstealth to attack + switch( this.atknum){ + case 0: //archer atk + this.pauseTimer = new Timer(1000); + this.pauseTimer.start(); + break; + case 1: //assassin atk + this.runTimer = new Timer(500); + this.pauseTimer = new Timer(1000); + this.startPosition = this.owner.position; + if(this.parent.getPlayerPosition() !==null) + this.owner.position = this.parent.getPlayerPosition().clone().add(new Vec2( (this.parent.player).invertX ? 64 : -64 ,0)); + + this.pauseTimer.start(); + break; + case 2: //bull atk + this.runTimer = new Timer(2000); + break; + case 3: //tiger atk + this.velocity = 0; + break; + case 4: //snake atk + break; + default: + break; + } + + } + + update(deltaT: number): void { + + //get random atk + switch( this.atknum){ + case 0: //archer atk + if(this.pauseTimer.isStopped()){ + this.parent.direction = this.parent.getPlayerPosition().x - this.owner.position.x >= 0 ? 1 : 0; + let dir = this.parent.getPlayerPosition().clone().sub(this.owner.position).normalize(); + + (this.parent).weapon.use(this.owner, "enemy", dir.scale(1,0)); + (this.owner).invertX = this.parent.direction === 1 ? true : false ; + this.finished(EnemyStates.ALERT); + } + super.update(deltaT); + break; + case 1: //assassin atk + if( this.pauseTimer.isStopped()){ + if (this.runTimer.isStopped() && this.parent.isAttacking || !this.canWalk()) { + this.emitter.fireEvent(this.attacked); + } + while (this.receiver.hasNextEvent()) { + let event = this.receiver.getNextEvent().type; + switch (event) { + case this.charged: + this.parent.isCharging = false; + this.parent.isAttacking = true; + this.runTimer.start(); + (this.owner).animation.play("ATTACK", true); + if(this.parent.getPlayerPosition() !==null) + this.parent.direction = this.parent.getPlayerPosition().x - this.owner.position.x >= 0 ? 1 : 0; + break; + case this.attacked: + this.parent.isAttacking = false; + this.finished(EnemyStates.ALERT); + break; + } + } + this.parent.velocity.x = this.parent.direction * 500; + (this.owner).invertX = this.parent.direction === 1 ? true : false ; + this.owner.alpha = .2; + super.update(deltaT); + } + break; + case 2: //bull atk + if (this.runTimer.isStopped() && this.parent.isAttacking || !this.canWalk()) { + this.emitter.fireEvent(this.attacked); + } + while (this.receiver.hasNextEvent()) { + let event = this.receiver.getNextEvent().type; + switch (event) { + case this.charged: + this.parent.isCharging = false; + this.parent.isAttacking = true; + this.runTimer.start(); + (this.owner).animation.play("ATTACK", true); + this.parent.direction = this.parent.getPlayerPosition().x - this.owner.position.x >= 0 ? 1 : 0; + break; + case this.attacked: + this.parent.isAttacking = false; + this.finished(EnemyStates.ALERT); + break; + } + } + this.parent.velocity.x = this.parent.direction * 1000; + (this.owner).invertX = this.parent.direction === 1 ? true : false ; + super.update(deltaT); + break; + case 3: //tiger atk + if (this.parent.isAttacking && this.owner.onGround) { + this.emitter.fireEvent(this.attacked); + } + while (this.receiver.hasNextEvent()) { + let event = this.receiver.getNextEvent().type; + switch (event) { + case this.charged: + this.parent.isCharging = false; + this.parent.isAttacking = true; + (this.owner).animation.play("ATTACK", true); + this.velocity = (this.parent.getPlayerPosition().x - this.owner.position.x)/1.5; + this.parent.direction = this.velocity >= 0 ? 1 : 0; + this.parent.velocity.y = -800; + break; + case this.attacked: + this.parent.isAttacking = false; + this.finished(EnemyStates.ALERT); + break; + } + } + this.parent.velocity.x = this.velocity; + (this.owner).invertX = this.parent.direction === 1 ? true : false ; + super.update(deltaT); + break; + case 4: //snake atk + while (this.receiver.hasNextEvent()) { + let event = this.receiver.getNextEvent().type; + switch (event) { + case this.charged: + this.parent.isCharging = false; + this.parent.isAttacking = true; + (this.owner).animation.play("ATTACK", false, this.attacked); + (this.owner.collisionShape).halfSize.x += 3.5; + break; + case this.attacked: + this.parent.isAttacking = false; + (this.owner.collisionShape).halfSize.x -= 3.5; + this.finished(EnemyStates.ALERT); + break; + } + } + (this.owner).invertX = this.parent.direction === 1 ? true : false ; + break; + default: + while (this.receiver.hasNextEvent()) { + let event = this.receiver.getNextEvent().type; + switch (event) { + case this.charged: + this.parent.isCharging = false; + this.parent.isAttacking = true; + (this.owner).animation.play("ATTACK", false, this.attacked); + (this.owner.collisionShape).halfSize.x += 3.5; + break; + case this.attacked: + this.parent.isAttacking = false; + (this.owner.collisionShape).halfSize.x -= 3.5; + this.finished(EnemyStates.ALERT); + break; + } + } + (this.owner).invertX = this.parent.direction === 1 ? true : false ; + break; + } + + + } + onExit(): Record { + + return null; + } +} \ No newline at end of file diff --git a/src/shattered_sword/AI/SlimeAI.ts b/src/shattered_sword/AI/SlimeAI.ts index 008aec0..6829240 100644 --- a/src/shattered_sword/AI/SlimeAI.ts +++ b/src/shattered_sword/AI/SlimeAI.ts @@ -3,6 +3,7 @@ import EnemyAI, { EnemyStates } from "./EnemyAI"; import SlimeAttack from "./EnemyStates/SlimeAttack"; export default class SlimeAI extends EnemyAI { + speed: number = 15; initializeAI(owner: AnimatedSprite, options: Record): void { super.initializeAI(owner, options); this.addState(EnemyStates.ATTACK, new SlimeAttack(this, owner)); diff --git a/src/shattered_sword/Scenes/GameLevel.ts b/src/shattered_sword/Scenes/GameLevel.ts index d185b09..c31f861 100644 --- a/src/shattered_sword/Scenes/GameLevel.ts +++ b/src/shattered_sword/Scenes/GameLevel.ts @@ -296,6 +296,9 @@ export default class GameLevel extends Scene { let node = this.sceneGraph.getNode(event.data.get("owner"));//get enemy id //remove enemy from enemies + //console.log(node); + //console.log("enemy killed at",node.position); + this.enemies = this.enemies.filter(item => item !== event.data.get("ai")); this.battleManager.removeEnemy(event.data.get("ai")); //give the player the exp value of the enemy killed @@ -861,14 +864,12 @@ export default class GameLevel extends Scene { //TODO - give each enemy unique weapon protected initializeEnemies( enemies: Enemy[]){ - - let pistol = this.createWeapon("pistol"); for (let enemy of enemies) { switch (enemy.type) { /* - case "Snake": + case "Archer": this.addEnemy("Archer", enemy.position.scale(32), ArcherAI, { player: this.player, health: 50, @@ -876,14 +877,14 @@ export default class GameLevel extends Scene { size: new Vec2(14,10), offset : new Vec2(0, 22), exp: 50, - weapon: pistol + weapon: this.createWeapon("pistol"); }) break; */ case "Snake": //Snake enemies drop from sky("trees")? or could just be very abundant this.addEnemy("Snake", enemy.position.scale(32), SnakeAI, { player: this.player, - health: 50, + health: 65, tilemap: "Main", size: new Vec2(14,10), offset : new Vec2(0, 22), @@ -914,7 +915,7 @@ export default class GameLevel extends Scene { case "black_pudding": this.addEnemy("black_pudding", enemy.position.scale(32), SlimeAI, { player: this.player, - health: 200, + health: 140 + (this.player._ai).level*2, tilemap: "Main", //actions:actions, scale: .25, diff --git a/src/shattered_sword/Scenes/MainMenu.ts b/src/shattered_sword/Scenes/MainMenu.ts index 5884e9b..9c419d2 100644 --- a/src/shattered_sword/Scenes/MainMenu.ts +++ b/src/shattered_sword/Scenes/MainMenu.ts @@ -120,16 +120,18 @@ export default class MainMenu extends Scene { lc.textColor = Color.WHITE; const rc =