diff --git a/dist/shattered_sword_assets/data/weaponData.json b/dist/shattered_sword_assets/data/weaponData.json index 3b4baa0..066e0f7 100644 --- a/dist/shattered_sword_assets/data/weaponData.json +++ b/dist/shattered_sword_assets/data/weaponData.json @@ -1,5 +1,5 @@ { - "numWeapons": 2, + "numWeapons": 3, "weapons": [ { "weaponType": "slice", @@ -16,10 +16,20 @@ "name": "pistol", "displayName": "Pistol", "spriteKey": "pistol", - "damage": 2, + "damage": 15, "cooldown": 500, "useVolume": 320, "color": "#FFEEDD" + }, + { + "weaponType": "laserGun", + "name": "laserGun", + "displayName": "laserGun", + "spriteKey": "laserGun", + "damage": 30, + "cooldown": 650, + "useVolume": 320, + "color": "#15E631" } ] } \ No newline at end of file diff --git a/dist/shattered_sword_assets/sprites/laserGun.png b/dist/shattered_sword_assets/sprites/laserGun.png new file mode 100644 index 0000000..794ceed Binary files /dev/null and b/dist/shattered_sword_assets/sprites/laserGun.png differ diff --git a/src/shattered_sword/AI/ArcherAI.ts b/src/shattered_sword/AI/ArcherAI.ts index 96bfa00..3c919cd 100644 --- a/src/shattered_sword/AI/ArcherAI.ts +++ b/src/shattered_sword/AI/ArcherAI.ts @@ -2,8 +2,7 @@ import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; import EnemyAI, { EnemyStates } from "./EnemyAI"; import ArcherAttack from "./EnemyStates/ArcherAttack"; import Weapon from "../GameSystems/items/Weapon"; - - +import Vec2 from "../../Wolfie2D/DataTypes/Vec2"; export default class ArcherAI extends EnemyAI { @@ -16,4 +15,8 @@ export default class ArcherAI extends EnemyAI { this.addState(EnemyStates.ATTACK, new ArcherAttack(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/EnemyAI.ts b/src/shattered_sword/AI/EnemyAI.ts index 7eb337b..68bc376 100644 --- a/src/shattered_sword/AI/EnemyAI.ts +++ b/src/shattered_sword/AI/EnemyAI.ts @@ -70,6 +70,7 @@ export default class EnemyAI extends StateMachineAI implements BattlerAI { physicWidth: number; physicHeight: number; + lastPlayerPosition: Vec2; initializeAI(owner: AnimatedSprite, options: Record): void { this.owner = owner; @@ -198,6 +199,7 @@ export default class EnemyAI extends StateMachineAI implements BattlerAI { } } } + this.lastPlayerPosition = pos; return pos; } @@ -206,7 +208,10 @@ export default class EnemyAI extends StateMachineAI implements BattlerAI { * @returns position of the player if visible, else null */ getPlayerPosition(): Vec2 { - return this.isPlayerVisible(this.player.position); + if(this.isPlayerVisible(this.player.position) == null){ + return this.lastPlayerPosition; + } + return this.isPlayerVisible(this.player.position) ; } update(deltaT: number){ diff --git a/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts b/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts index 6adbc7f..938e609 100644 --- a/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts +++ b/src/shattered_sword/AI/EnemyStates/ArcherAttack.ts @@ -3,23 +3,31 @@ import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; import Attack from "./Attack"; import ArcherAI from "../ArcherAI"; import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; +import Timer from "../../../Wolfie2D/Timing/Timer"; //TODO - unfinished export default class ArcherAttack extends Attack { + pauseTimer : Timer; onEnter(options: Record): void { + super.onEnter(options); + this.pauseTimer = new Timer(1000); + this.pauseTimer.start(); } + 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); + 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); } 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 index e0cd46d..0fecafd 100644 --- a/src/shattered_sword/AI/EnemyStates/AssassinAttack.ts +++ b/src/shattered_sword/AI/EnemyStates/AssassinAttack.ts @@ -21,12 +21,9 @@ export default class AssassinAttack extends Attack { 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(); } diff --git a/src/shattered_sword/GameSystems/items/Weapon.ts b/src/shattered_sword/GameSystems/items/Weapon.ts index fa2b9f9..0d6ec68 100644 --- a/src/shattered_sword/GameSystems/items/Weapon.ts +++ b/src/shattered_sword/GameSystems/items/Weapon.ts @@ -67,7 +67,7 @@ export default class Weapon extends Item { this.assets = this.type.createRequiredAssets(this.sprite.getScene()); // Do a type specific weapon animation - this.type.doAnimation(user, direction, this.EXTRA_RANGE, ...this.assets); + this.type.doAnimation(user, direction, ...this.assets, this.EXTRA_RANGE); // Apply damage this.battleManager.handleInteraction(userType, this, user); diff --git a/src/shattered_sword/GameSystems/items/WeaponTypes/LaserGun.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/LaserGun.ts new file mode 100644 index 0000000..ea1c91a --- /dev/null +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/LaserGun.ts @@ -0,0 +1,103 @@ +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 laserGun 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().add(shooter.colliderOffset); + let end = shooter.position.clone().add(direction.scaled(900)).add(shooter.colliderOffset); + 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.thickness = 25; + + line.tweens.add("fade", { + startDelay: 0, + duration: 800, + 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 laserGun(); + newType.initialize({damage: this.damage, color: this.hexColor, 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/GameSystems/items/WeaponTypes/SemiAutoGun.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/SemiAutoGun.ts index 3f89709..81ac73f 100644 --- a/src/shattered_sword/GameSystems/items/WeaponTypes/SemiAutoGun.ts +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/SemiAutoGun.ts @@ -26,8 +26,8 @@ export default class SemiAutoGun extends WeaponType { } doAnimation(shooter: GameNode, direction: Vec2, line: Line): void { - let start = shooter.position.clone(); - let end = shooter.position.clone().add(direction.scaled(900)); + let start = shooter.position.clone().add(shooter.colliderOffset); + let end = shooter.position.clone().add(direction.scaled(900)).add(shooter.colliderOffset); let delta = end.clone().sub(start); // Iterate through the tilemap region until we find a collision @@ -72,10 +72,11 @@ export default class SemiAutoGun extends WeaponType { 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.thickness = 5; line.tweens.add("fade", { startDelay: 0, - duration: 300, + duration: 600, effects: [ { property: TweenableProperties.alpha, diff --git a/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts index 3deb6aa..e208c66 100644 --- a/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts @@ -15,7 +15,7 @@ export default class Slice extends WeaponType { this.useVolume = options.useVolume; } - doAnimation(attacker: GameNode, direction: Vec2, extraRange:number,sliceSprite: AnimatedSprite): void { + doAnimation(attacker: GameNode, direction: Vec2, sliceSprite: AnimatedSprite, extraRange:number): void { // Rotate this with the game node // TODO - need to rotate the anim properly diff --git a/src/shattered_sword/Player/PlayerController.ts b/src/shattered_sword/Player/PlayerController.ts index 68093e5..598dd72 100644 --- a/src/shattered_sword/Player/PlayerController.ts +++ b/src/shattered_sword/Player/PlayerController.ts @@ -391,7 +391,14 @@ export default class PlayerController extends StateMachineAI implements BattlerA PlayerController.buffPool.sort(() => 0.5 - Math.random()); // Get sub-array of first 3 elements after shuffled - let shuffled = PlayerController.buffPool.slice(0, 3); //3 buff categories + //let shuffled = PlayerController.buffPool.slice(0, 3); //3 buff categories + + let shuffled = new Set(); + while(shuffled.size < 3){ + shuffled.add(PlayerController.buffPool.slice(0,1)[0]); + PlayerController.buffPool.sort(() => 0.5 - Math.random()); + } + //random number from 5 to 15 if no value given let num = Math.floor(Math.random() *10) +5; @@ -459,8 +466,7 @@ export default class PlayerController extends StateMachineAI implements BattlerA let selected = new Array(); - while( shuffled.length != 0){ - let cat = shuffled.pop(); + for ( let cat of shuffled){ switch(cat){ case BuffCategory.ATTACK: attackBuffs.sort(() => 0.5 - Math.random()); @@ -532,7 +538,8 @@ export default class PlayerController extends StateMachineAI implements BattlerA } else if (!init){ //increase weight of selected buff category - PlayerController.buffPool.push(buff.category); + if(buff.category != BuffCategory.EXTRA) + PlayerController.buffPool.push(buff.category); PlayerController.appliedBuffs.push(buff); } // TODO diff --git a/src/shattered_sword/Registry/WeaponRegistry.ts b/src/shattered_sword/Registry/WeaponRegistry.ts index c0f012d..3035e68 100644 --- a/src/shattered_sword/Registry/WeaponRegistry.ts +++ b/src/shattered_sword/Registry/WeaponRegistry.ts @@ -4,6 +4,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"; +import laserGun from "../GameSystems/items/WeaponTypes/laserGun"; export default class WeaponTemplateRegistry extends Registry { public preload(): void { @@ -14,6 +15,7 @@ export default class WeaponTemplateRegistry extends Registry //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"); + rm.image("laserGun", "shattered_sword_assets/sprites/laserGun.png"); // Load spritesheets //rm.spritesheet("weapon anim", "shattered_sword_assets/spritesheets/weapon anim.json"); @@ -23,6 +25,7 @@ export default class WeaponTemplateRegistry extends Registry //this.registerItem("itemtype", itemTypefile); this.registerItem("slice", Slice); this.registerItem("semiAutoGun", SemiAutoGun); + this.registerItem("laserGun",laserGun); } diff --git a/src/shattered_sword/Scenes/GameLevel.ts b/src/shattered_sword/Scenes/GameLevel.ts index 5745b18..d185b09 100644 --- a/src/shattered_sword/Scenes/GameLevel.ts +++ b/src/shattered_sword/Scenes/GameLevel.ts @@ -169,6 +169,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("laserGun","shattered_sword_assets/sprites/laserGun.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"); @@ -555,7 +556,8 @@ export default class GameLevel extends Scene { Player_Events.LEVEL_END, Player_Events.PLAYER_KILLED, Player_Events.GIVE_REGULAR_BUFF, - Player_Events.GIVE_SPECIAL_BUFF + Player_Events.GIVE_SPECIAL_BUFF, + Player_Events.UNLOAD_ASSET ]); this.receiver.subscribe("buff1"); this.receiver.subscribe("buff2"); @@ -860,10 +862,24 @@ 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": + this.addEnemy("Archer", enemy.position.scale(32), ArcherAI, { + player: this.player, + health: 50, + tilemap: "Main", + size: new Vec2(14,10), + offset : new Vec2(0, 22), + exp: 50, + weapon: 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, @@ -874,7 +890,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, @@ -1148,6 +1164,10 @@ export default class GameLevel extends Scene { case "SLD": (this.player._ai).CURRENT_SHIELD = parseInt(commands[2]); break; + case "SPD": + (this.player._ai).speed = parseInt(commands[2]); + (this.player._ai).MIN_SPEED = parseInt(commands[2]); + break; default: break; }