diff --git a/dist/shattered_sword_assets/data/weaponData.json b/dist/shattered_sword_assets/data/weaponData.json index 4861da6..3b4baa0 100644 --- a/dist/shattered_sword_assets/data/weaponData.json +++ b/dist/shattered_sword_assets/data/weaponData.json @@ -1,5 +1,5 @@ { - "numWeapons": 1, + "numWeapons": 2, "weapons": [ { "weaponType": "slice", @@ -10,6 +10,16 @@ "damage": 50, "cooldown": 500, "useVolume": 0 + }, + { + "weaponType": "semiAutoGun", + "name": "pistol", + "displayName": "Pistol", + "spriteKey": "pistol", + "damage": 2, + "cooldown": 500, + "useVolume": 320, + "color": "#FFEEDD" } ] } \ No newline at end of file diff --git a/dist/shattered_sword_assets/sprites/pistol.png b/dist/shattered_sword_assets/sprites/pistol.png new file mode 100644 index 0000000..5d02460 Binary files /dev/null and b/dist/shattered_sword_assets/sprites/pistol.png differ diff --git a/src/shattered_sword/AI/ArcherAI.ts b/src/shattered_sword/AI/ArcherAI.ts new file mode 100644 index 0000000..96bfa00 --- /dev/null +++ b/src/shattered_sword/AI/ArcherAI.ts @@ -0,0 +1,19 @@ +import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import EnemyAI, { EnemyStates } from "./EnemyAI"; +import ArcherAttack from "./EnemyStates/ArcherAttack"; +import Weapon from "../GameSystems/items/Weapon"; + + + + +export default class ArcherAI extends EnemyAI { + + /** The weapon this AI has */ + weapon: Weapon; + + initializeAI(owner: AnimatedSprite, options: Record): void { + super.initializeAI(owner, options); + this.addState(EnemyStates.ATTACK, new ArcherAttack(this, owner)); + this.weapon = options.weapon; + } +} \ No newline at end of file diff --git a/src/shattered_sword/AI/AssassinAI.ts b/src/shattered_sword/AI/AssassinAI.ts new file mode 100644 index 0000000..6a62b4d --- /dev/null +++ b/src/shattered_sword/AI/AssassinAI.ts @@ -0,0 +1,17 @@ +import Vec2 from "../../Wolfie2D/DataTypes/Vec2"; +import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import EnemyAI, { EnemyStates } from "./EnemyAI"; +import AssassinAttack from "./EnemyStates/AssassinAttack"; + +export default class AssassinAI extends EnemyAI { + initializeAI(owner: AnimatedSprite, options: Record): void { + super.initializeAI(owner, options); + this.addState(EnemyStates.ATTACK, new AssassinAttack(this, owner)); + } + + + canAttack(position: Vec2): boolean { + return this.attackTimer.isStopped() && this.owner.position.distanceTo(position)<=150; + } + +} \ No newline at end of file diff --git a/src/shattered_sword/AI/BullAI.ts b/src/shattered_sword/AI/BullAI.ts index 9e276e8..e8dacb6 100644 --- a/src/shattered_sword/AI/BullAI.ts +++ b/src/shattered_sword/AI/BullAI.ts @@ -18,7 +18,7 @@ export default class BullAI extends EnemyAI { collideWithPlayer(player: PlayerController): void { player.damage(10); - if (this.isAttaking && !player.invincible && !player.godMode) { + if (this.isAttacking && !player.invincible && !player.godMode) { player.bleedCounter += 3; } } diff --git a/src/shattered_sword/AI/EnemyAI.ts b/src/shattered_sword/AI/EnemyAI.ts index 59ebf85..7eb337b 100644 --- a/src/shattered_sword/AI/EnemyAI.ts +++ b/src/shattered_sword/AI/EnemyAI.ts @@ -64,7 +64,7 @@ export default class EnemyAI extends StateMachineAI implements BattlerAI { attackTimer : Timer; isCharging: boolean = false; - isAttaking: boolean = false; + isAttacking: boolean = false; damageTimer: Timer; physicWidth: number; @@ -112,7 +112,7 @@ export default class EnemyAI extends StateMachineAI implements BattlerAI { console.log(damage +" damage taken, "+this.CURRENT_HP+" hp left"); this.CURRENT_HP -= damage; //TODO - - if (!this.isAttaking && !this.isCharging) { + if (!this.isAttacking && !this.isCharging) { this.owner.animation.play("HURT",false); } diff --git a/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts b/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts new file mode 100644 index 0000000..6adbc7f --- /dev/null +++ b/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts @@ -0,0 +1,25 @@ +import EnemyAI, { EnemyStates } from "../EnemyAI"; +import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import Attack from "./Attack"; +import ArcherAI from "../ArcherAI"; +import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; + +//TODO - unfinished +export default class ArcherAttack extends Attack { + onEnter(options: Record): void { + } + update(deltaT: number): void { + + + 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); + } + onExit(): Record { + return null; + } +} \ No newline at end of file diff --git a/src/shattered_sword/AI/EnemyStates/AssassinAttack.ts b/src/shattered_sword/AI/EnemyStates/AssassinAttack.ts new file mode 100644 index 0000000..e0cd46d --- /dev/null +++ b/src/shattered_sword/AI/EnemyStates/AssassinAttack.ts @@ -0,0 +1,73 @@ +import EnemyAI, { EnemyStates } from "../EnemyAI"; +import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import Attack from "./Attack"; +import AssassinAI from "../ArcherAI"; +import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; +import Timer from "../../../Wolfie2D/Timing/Timer"; +import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; + +//TODO - unfinished +export default class AssassinAttack extends Attack { + runTimer: Timer; + startPosition : Vec2; + pauseTimer : Timer; + + onEnter(options: Record): void { + super.onEnter(options); + this.runTimer = new Timer(500); + this.pauseTimer = new Timer(1000); + + + this.owner.alpha = 1; //unstealth to attack + 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(); + + } + + + update(deltaT: number): void { + + + 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 ; + super.update(deltaT); + } + + + + } + + onExit(): Record { + this.owner.alpha = .2; //stealth again + //this.owner.position = this.startPosition; + return null; + } +} \ No newline at end of file diff --git a/src/shattered_sword/AI/EnemyStates/BullAttack.ts b/src/shattered_sword/AI/EnemyStates/BullAttack.ts index 3f3c68d..1914850 100644 --- a/src/shattered_sword/AI/EnemyStates/BullAttack.ts +++ b/src/shattered_sword/AI/EnemyStates/BullAttack.ts @@ -14,7 +14,7 @@ export default class TigerAttack extends Attack { } update(deltaT: number): void { - if (this.runTimer.isStopped() && this.parent.isAttaking || !this.canWalk()) { + if (this.runTimer.isStopped() && this.parent.isAttacking || !this.canWalk()) { this.emitter.fireEvent(this.attacked); } while (this.receiver.hasNextEvent()) { @@ -22,13 +22,13 @@ export default class TigerAttack extends Attack { switch (event) { case this.charged: this.parent.isCharging = false; - this.parent.isAttaking = true; + 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.isAttaking = false; + this.parent.isAttacking = false; this.finished(EnemyStates.ALERT); break; } diff --git a/src/shattered_sword/AI/EnemyStates/EnemyState.ts b/src/shattered_sword/AI/EnemyStates/EnemyState.ts index b784489..21f5f8d 100644 --- a/src/shattered_sword/AI/EnemyStates/EnemyState.ts +++ b/src/shattered_sword/AI/EnemyStates/EnemyState.ts @@ -27,7 +27,7 @@ export default abstract class EnemyState extends State { } update(deltaT: number): void { - if (!this.parent.damageTimer.isStopped() && !this.parent.isAttaking && !this.parent.isCharging) { + if (!this.parent.damageTimer.isStopped() && !this.parent.isAttacking && !this.parent.isCharging) { this.parent.velocity.x = 0; } // Do gravity diff --git a/src/shattered_sword/AI/EnemyStates/SnakeAttack.ts b/src/shattered_sword/AI/EnemyStates/SnakeAttack.ts index 61bc090..04f657d 100644 --- a/src/shattered_sword/AI/EnemyStates/SnakeAttack.ts +++ b/src/shattered_sword/AI/EnemyStates/SnakeAttack.ts @@ -11,12 +11,12 @@ export default class SnakeAttack extends Attack { switch (event) { case this.charged: this.parent.isCharging = false; - this.parent.isAttaking = true; + 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.isAttaking = false; + this.parent.isAttacking = false; (this.owner.collisionShape).halfSize.x -= 3.5; this.finished(EnemyStates.ALERT); break; diff --git a/src/shattered_sword/AI/EnemyStates/TigerAttack.ts b/src/shattered_sword/AI/EnemyStates/TigerAttack.ts index f30a63d..566e40d 100644 --- a/src/shattered_sword/AI/EnemyStates/TigerAttack.ts +++ b/src/shattered_sword/AI/EnemyStates/TigerAttack.ts @@ -14,7 +14,7 @@ export default class TigerAttack extends Attack { } update(deltaT: number): void { - if (this.parent.isAttaking && this.owner.onGround) { + if (this.parent.isAttacking && this.owner.onGround) { this.emitter.fireEvent(this.attacked); } while (this.receiver.hasNextEvent()) { @@ -22,14 +22,14 @@ export default class TigerAttack extends Attack { switch (event) { case this.charged: this.parent.isCharging = false; - this.parent.isAttaking = true; + 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.isAttaking = false; + this.parent.isAttacking = false; this.finished(EnemyStates.ALERT); break; } diff --git a/src/shattered_sword/AI/SnakeAI.ts b/src/shattered_sword/AI/SnakeAI.ts index d12e4b2..a84e1ab 100644 --- a/src/shattered_sword/AI/SnakeAI.ts +++ b/src/shattered_sword/AI/SnakeAI.ts @@ -11,7 +11,7 @@ export default class SnakeAI extends EnemyAI { collideWithPlayer(player: PlayerController): void { player.damage(10); - if (this.isAttaking && !player.invincible && !player.godMode) { + if (this.isAttacking && !player.invincible && !player.godMode) { player.poisonCounter = 5; } } diff --git a/src/shattered_sword/AI/TigerAI.ts b/src/shattered_sword/AI/TigerAI.ts index c9f0ce2..f9925c9 100644 --- a/src/shattered_sword/AI/TigerAI.ts +++ b/src/shattered_sword/AI/TigerAI.ts @@ -23,7 +23,7 @@ export default class TigerAI extends EnemyAI { collideWithPlayer(player: PlayerController): void { player.damage(10); - if (this.isAttaking && !player.invincible && !player.godMode) { + if (this.isAttacking && !player.invincible && !player.godMode) { player.bleedCounter += 3; } } diff --git a/src/shattered_sword/GameSystems/items/WeaponTypes/SemiAutoGun.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/SemiAutoGun.ts new file mode 100644 index 0000000..3f89709 --- /dev/null +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/SemiAutoGun.ts @@ -0,0 +1,102 @@ +import AABB from "../../../../Wolfie2D/DataTypes/Shapes/AABB"; +import Vec2 from "../../../../Wolfie2D/DataTypes/Vec2"; +import GameNode, { TweenableProperties } from "../../../../Wolfie2D/Nodes/GameNode"; +import { GraphicType } from "../../../../Wolfie2D/Nodes/Graphics/GraphicTypes"; +import Line from "../../../../Wolfie2D/Nodes/Graphics/Line"; +import OrthogonalTilemap from "../../../../Wolfie2D/Nodes/Tilemaps/OrthogonalTilemap"; +import Scene from "../../../../Wolfie2D/Scene/Scene"; +import Color from "../../../../Wolfie2D/Utils/Color"; +import { EaseFunctionType } from "../../../../Wolfie2D/Utils/EaseFunctions"; +import { Player_Events } from "../../../sword_enums"; +import WeaponType from "./WeaponType"; + +export default class SemiAutoGun extends WeaponType { + + color: Color; + private hexColor: string; + + initialize(options: Record): void { + this.damage = options.damage; + this.cooldown = options.cooldown; + this.hexColor = options.color; + this.color = Color.fromStringHex(options.color); + this.displayName = options.displayName; + this.spriteKey = options.spriteKey; + this.useVolume = options.useVolume; + } + + doAnimation(shooter: GameNode, direction: Vec2, line: Line): void { + let start = shooter.position.clone(); + let end = shooter.position.clone().add(direction.scaled(900)); + let delta = end.clone().sub(start); + + // Iterate through the tilemap region until we find a collision + let minX = Math.min(start.x, end.x); + let maxX = Math.max(start.x, end.x); + let minY = Math.min(start.y, end.y); + let maxY = Math.max(start.y, end.y); + + // Get the wall tilemap + let walls = shooter.getScene().getLayer("Wall").getItems()[0]; + + let minIndex = walls.getColRowAt(new Vec2(minX, minY)); + let maxIndex = walls.getColRowAt(new Vec2(maxX, maxY)); + + let tileSize = walls.getTileSize(); + + for(let col = minIndex.x; col <= maxIndex.x; col++){ + for(let row = minIndex.y; row <= maxIndex.y; row++){ + if(walls.isTileCollidable(col, row)){ + // Get the position of this tile + let tilePos = new Vec2(col * tileSize.x + tileSize.x/2, row * tileSize.y + tileSize.y/2); + + // Create a collider for this tile + let collider = new AABB(tilePos, tileSize.scaled(1/2)); + + let hit = collider.intersectSegment(start, delta, Vec2.ZERO); + + if(hit !== null && start.distanceSqTo(hit.pos) < start.distanceSqTo(end)){ + console.log("Found hit"); + end = hit.pos; + } + } + } + } + + line.start = start; + line.end = end; + + line.tweens.play("fade"); + } + + createRequiredAssets(scene: Scene): [Line] { + let line = scene.add.graphic(GraphicType.LINE, "primary", {start: new Vec2(-1, 1), end: new Vec2(-1, -1)}); + line.color = this.color; + + line.tweens.add("fade", { + startDelay: 0, + duration: 300, + effects: [ + { + property: TweenableProperties.alpha, + start: 1, + end: 0, + ease: EaseFunctionType.OUT_SINE + } + ], + onEnd: Player_Events.UNLOAD_ASSET + }); + + return [line]; + } + + hits(node: GameNode, line: Line): boolean { + return node.collisionShape.getBoundingRect().intersectSegment(line.start, line.end.clone().sub(line.start)) !== null; + } + + clone(): WeaponType { + let newType = new SemiAutoGun(); + newType.initialize({color: this.hexColor,damage: this.damage, cooldown: this.cooldown, displayName: this.displayName, spriteKey: this.spriteKey, useVolume: this.useVolume}); + return newType; + } +} \ No newline at end of file diff --git a/src/shattered_sword/Registry/WeaponRegistry.ts b/src/shattered_sword/Registry/WeaponRegistry.ts index 7e77e7a..c0f012d 100644 --- a/src/shattered_sword/Registry/WeaponRegistry.ts +++ b/src/shattered_sword/Registry/WeaponRegistry.ts @@ -3,7 +3,7 @@ import ResourceManager from "../../Wolfie2D/ResourceManager/ResourceManager"; import WeaponType from "../GameSystems/items/WeaponTypes/WeaponType"; import Slice from "../GameSystems/items/WeaponTypes/Slice"; - +import SemiAutoGun from "../GameSystems/items/WeaponTypes/SemiAutoGun"; export default class WeaponTemplateRegistry extends Registry { public preload(): void { @@ -13,6 +13,7 @@ export default class WeaponTemplateRegistry extends Registry // Load sprites for each weapon //rm.image("something", "shattered_sword_assets/sprites/something.png"); rm.image("knife", "shattered_sword_assets/sprites/knife.png"); + rm.image("pistol", "shattered_sword_assets/sprites/pistol.png"); // Load spritesheets //rm.spritesheet("weapon anim", "shattered_sword_assets/spritesheets/weapon anim.json"); @@ -21,6 +22,7 @@ export default class WeaponTemplateRegistry extends Registry // Register default types //this.registerItem("itemtype", itemTypefile); this.registerItem("slice", Slice); + this.registerItem("semiAutoGun", SemiAutoGun); } diff --git a/src/shattered_sword/Scenes/GameLevel.ts b/src/shattered_sword/Scenes/GameLevel.ts index 4c7d247..4dbd4cc 100644 --- a/src/shattered_sword/Scenes/GameLevel.ts +++ b/src/shattered_sword/Scenes/GameLevel.ts @@ -23,6 +23,8 @@ import EnemyAI from "../AI/EnemyAI"; import SnakeAI from "../AI/SnakeAI"; import SlimeAI from "../AI/SlimeAI"; import TigerAI from "../AI/TigerAI"; +import ArcherAI from "../AI/ArcherAI"; +import AssassinAI from "../AI/AssassinAi"; import BattlerAI from "../AI/BattlerAI"; import InventoryManager from "../GameSystems/InventoryManager"; import Item from "../GameSystems/items/Item"; @@ -170,6 +172,7 @@ export default class GameLevel extends Scene { this.load.image("knife", "shattered_sword_assets/sprites/knife.png"); + this.load.image("pistol","shattered_sword_assets/sprites/pistol.png"); this.load.image("inventorySlot", "shattered_sword_assets/sprites/inventory.png"); this.load.image("black", "shattered_sword_assets/images/black.png"); this.load.image("poisoning", "shattered_sword_assets/images/poisoning.png"); @@ -271,6 +274,12 @@ export default class GameLevel extends Scene { while(this.receiver.hasNextEvent()){ let event = this.receiver.getNextEvent(); + if(event.isType(Player_Events.UNLOAD_ASSET)){ + console.log(event.data); + let asset = this.sceneGraph.getNode(event.data.get("node")); + + asset.destroy(); + } if (this.gameStateStack.peek() === GameState.GAMING) { switch(event.type){ case Player_Events.PLAYER_COLLIDE: @@ -854,8 +863,11 @@ export default class GameLevel extends Scene { //TODO - give each enemy unique weapon protected initializeEnemies( enemies: Enemy[]){ + + for (let enemy of enemies) { switch (enemy.type) { + 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, @@ -866,6 +878,7 @@ export default class GameLevel extends Scene { exp: 50, }) break; + case "Tiger": //Tiger can be miniboss for now? this.addEnemy("Tiger", enemy.position.scale(32), TigerAI, { player: this.player, @@ -936,23 +949,6 @@ export default class GameLevel extends Scene { } - /** - * Increments the amount of life the player has - * @param amt The amount to add to the player life - */ - /* - protected incPlayerLife(amt: number): void { - GameLevel.livesCount += amt; - this.livesCountLabel.text = "Lives: " + GameLevel.livesCount; - if (GameLevel.livesCount === 0){ - InputWrapper.disableInput(); - this.player.disablePhysics(); - this.emitter.fireEvent(GameEventType.PLAY_SOUND, {key: "player_death", loop: false, holdReference: false}); - this.player.tweens.play("death"); - } - } - */ - /** * Returns the player to spawn @@ -978,9 +974,6 @@ export default class GameLevel extends Scene { this.player.position.set(this.playerSpawn.x,this.playerSpawn.y); - //TODO - decrease player health or can kill player here - //(this.player._ai).CURRENT_HP *= .75; - //this.emitter.fireEvent(Player_Events.PLAYER_KILLED); } } diff --git a/src/shattered_sword/sword_enums.ts b/src/shattered_sword/sword_enums.ts index 2752438..5482aa3 100644 --- a/src/shattered_sword/sword_enums.ts +++ b/src/shattered_sword/sword_enums.ts @@ -12,8 +12,10 @@ export enum Player_Events { BOSS_KILLED = "BossKilled", GIVE_REGULAR_BUFF = "GiveRegularBuff", GIVE_SPECIAL_BUFF = "GiveSpecialBuff", - PLAYER_COLLIDE = "PlayerCollide" + PLAYER_COLLIDE = "PlayerCollide", + UNLOAD_ASSET = "UnloadAsset" } + export enum Damage_Type { NORMAL_DAMAGE = "NormalDamage", ENVIRONMENT_DAMAGE = "EnvironmentDamage",