diff --git a/dist/shattered_sword_assets/data/weaponData.json b/dist/shattered_sword_assets/data/weaponData.json new file mode 100644 index 0000000..637513b --- /dev/null +++ b/dist/shattered_sword_assets/data/weaponData.json @@ -0,0 +1,15 @@ +{ + "numWeapons": 1, + "weapons": [ + { + "weaponType": "slice", + "name": "knife", + "displayName": "Knife", + "animationSprite": "slice", + "spriteKey": "knife", + "damage": 1, + "cooldown": 3, + "useVolume": 0 + } + ] +} \ No newline at end of file diff --git a/dist/shattered_sword_assets/sprites/inventory.png b/dist/shattered_sword_assets/sprites/inventory.png new file mode 100644 index 0000000..628e772 Binary files /dev/null and b/dist/shattered_sword_assets/sprites/inventory.png differ diff --git a/dist/shattered_sword_assets/sprites/knife.png b/dist/shattered_sword_assets/sprites/knife.png new file mode 100644 index 0000000..a6db357 Binary files /dev/null and b/dist/shattered_sword_assets/sprites/knife.png differ diff --git a/dist/shattered_sword_assets/spritesheets/slice.json b/dist/shattered_sword_assets/spritesheets/slice.json new file mode 100644 index 0000000..4f68387 --- /dev/null +++ b/dist/shattered_sword_assets/spritesheets/slice.json @@ -0,0 +1,19 @@ +{ + "name": "slice", + "spriteSheetImage": "slice.png", + "spriteWidth": 16, + "spriteHeight": 16, + "columns": 4, + "rows": 1, + "durationType": "time", + "animations": [ + { + "name": "NORMAL", + "frames": [ {"index": 0, "duration": 1} ] + }, + { + "name": "SLICE", + "frames": [ {"index": 1, "duration": 2}, {"index": 2, "duration": 4}, {"index": 3, "duration": 2} ] + } + ] +} \ No newline at end of file diff --git a/dist/shattered_sword_assets/spritesheets/slice.png b/dist/shattered_sword_assets/spritesheets/slice.png new file mode 100644 index 0000000..5308b53 Binary files /dev/null and b/dist/shattered_sword_assets/spritesheets/slice.png differ diff --git a/dist/shattered_sword_assets/spritesheets/test_dummy.json b/dist/shattered_sword_assets/spritesheets/test_dummy.json new file mode 100644 index 0000000..8b11a63 --- /dev/null +++ b/dist/shattered_sword_assets/spritesheets/test_dummy.json @@ -0,0 +1,33 @@ +{ + "name": "test_dummy", + "spriteSheetImage": "test_dummy.png", + "spriteWidth": 32, + "spriteHeight": 32, + "columns": 1, + "rows": 2, + "durationType": "time", + "animations": [ + { + "name": "IDLE", + "frames": [ {"index": 0, "duration": 540} ] + }, + { + "name": "JUMP", + "frames":[ {"index": 0, "duration": 32}] + }, + { + "name": "WALK", + "frames": [ {"index": 0, "duration": 540} ] + }, + { + "name": "FALL", + "frames": [ {"index": 0, "duration": 540} ] + }, + { + "name": "HURT", + "frames": [ {"index": 0, "duration": 250}, + {"index": 1, "duration": 500}, + {"index": 0, "duration": 250} ] + } + ] +} \ No newline at end of file diff --git a/dist/shattered_sword_assets/spritesheets/test_dummy.png b/dist/shattered_sword_assets/spritesheets/test_dummy.png new file mode 100644 index 0000000..44a253a Binary files /dev/null and b/dist/shattered_sword_assets/spritesheets/test_dummy.png differ diff --git a/src/main.ts b/src/main.ts index c02ec58..c1d91fb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,7 +25,8 @@ import WeaponTypeRegistry from "./shattered_sword/Registry/WeaponTypeRegistry"; {name: "skill", keys: ["l","v"]}, {name: "inventory", keys: ["i","b"]}, {name: "pause", keys: ["escape"]}, - {name: "tab", keys: ["tab"]} + {name: "tab", keys: ["tab"]} , + {name: "spawn", keys: ["q"]} //debug feature to test enemy spawning, press q to spawn enemy at current location ], useWebGL: false, // Tell the game we want to use webgl showDebug: true // Whether to show debug messages. You can change this to true if you want diff --git a/src/shattered_sword/AI/BattlerAI.ts b/src/shattered_sword/AI/BattlerAI.ts index 05e91b0..d862308 100644 --- a/src/shattered_sword/AI/BattlerAI.ts +++ b/src/shattered_sword/AI/BattlerAI.ts @@ -7,7 +7,7 @@ import GameNode from "../../Wolfie2D/Nodes/GameNode"; export default interface BattlerAI extends AI { owner: GameNode; - health: number; + CURRENT_HP: number; //changed health to CURRENT_HP damage: (damage: number) => void; } \ No newline at end of file diff --git a/src/shattered_sword/AI/EnemyAI.ts b/src/shattered_sword/AI/EnemyAI.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/shattered_sword/GameSystems/InventoryManager.ts b/src/shattered_sword/GameSystems/InventoryManager.ts index e69de29..6e1fbd2 100644 --- a/src/shattered_sword/GameSystems/InventoryManager.ts +++ b/src/shattered_sword/GameSystems/InventoryManager.ts @@ -0,0 +1,112 @@ +import Vec2 from "../../Wolfie2D/DataTypes/Vec2"; +import { GraphicType } from "../../Wolfie2D/Nodes/Graphics/GraphicTypes"; +import Rect from "../../Wolfie2D/Nodes/Graphics/Rect"; +import Sprite from "../../Wolfie2D/Nodes/Sprites/Sprite"; +import Scene from "../../Wolfie2D/Scene/Scene"; +import Color from "../../Wolfie2D/Utils/Color"; +import Item from "./items/Item"; + +export default class InventoryManager { + + private position: Vec2; + private items: Array; + private inventorySlots: Array; + private slotSize: Vec2; + private padding: number; + private currentSlot: number; + private slotLayer: string; + private itemLayer: string; + private selectedSlot: Rect; + + constructor(scene: Scene, size: number, inventorySlot: string, position: Vec2, padding: number, slotLayer: string, itemLayer: string){ + this.items = new Array(size); + this.inventorySlots = new Array(size); + this.padding = padding; + this.position = position; + this.currentSlot = 0; + + // Add layers + this.slotLayer = slotLayer; + scene.addUILayer(this.slotLayer).setDepth(100); + this.itemLayer = itemLayer; + scene.addUILayer(this.itemLayer).setDepth(101); + + // Create the inventory slots + for(let i = 0; i < size; i++){ + this.inventorySlots[i] = scene.add.sprite(inventorySlot, this.slotLayer); + } + + this.slotSize = this.inventorySlots[0].size.clone(); + + // Position the inventory slots + for(let i = 0; i < size; i++){ + this.inventorySlots[i].position.set(position.x + i*(this.slotSize.x + this.padding), position.y); + } + + // Add a rect for the selected slot + this.selectedSlot = scene.add.graphic(GraphicType.RECT, slotLayer, {position: this.position.clone(), size: this.slotSize.clone().inc(-2)}); + this.selectedSlot.color = Color.WHITE; + this.selectedSlot.color.a = 0.2; + } + + getItem(): Item { + return this.items[this.currentSlot]; + } + + /** + * Changes the currently selected slot + */ + changeSlot(slot: number): void { + this.currentSlot = slot; + this.selectedSlot.position.copy(this.inventorySlots[slot].position); + } + + /** + * Gets the currently selected slot + */ + getSlot(): number { + return this.currentSlot; + } + + /** + * Adds an item to the currently selected slot + */ + addItem(item: Item): boolean { + if(!this.items[this.currentSlot]){ + // Add the item to the inventory + this.items[this.currentSlot] = item; + + // Update the gui + item.moveSprite(new Vec2(this.position.x + this.currentSlot*(this.slotSize.x + this.padding), this.position.y), this.itemLayer); + + return true; + } + + // Failed to add item, something was already in the slot + return false; + } + + /** + * Removes and returns an item from the the currently selected slot, if possible + */ + removeItem(): Item { + let item = this.items[this.currentSlot]; + + this.items[this.currentSlot] = null; + + if(item){ + return item; + } else { + return null; + } + } + + setActive(active: boolean) { + if (active){ + this.inventorySlots.forEach(slot => slot.alpha = 1.0); + } + else{ + this.inventorySlots.forEach(slot => slot.alpha = 0.5); + } + } +} \ No newline at end of file diff --git a/src/shattered_sword/GameSystems/items/Weapon.ts b/src/shattered_sword/GameSystems/items/Weapon.ts index 9db2233..038ff88 100644 --- a/src/shattered_sword/GameSystems/items/Weapon.ts +++ b/src/shattered_sword/GameSystems/items/Weapon.ts @@ -41,7 +41,7 @@ export default class Weapon extends Item { this.battleManager = battleManager; // Create the cooldown timer - this.cooldownTimer = new Timer(type.cooldown); + this.cooldownTimer = new Timer(type.cooldown); } // @override diff --git a/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts new file mode 100644 index 0000000..49413da --- /dev/null +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts @@ -0,0 +1,50 @@ +import Vec2 from "../../../../Wolfie2D/DataTypes/Vec2"; +import GameNode from "../../../../Wolfie2D/Nodes/GameNode"; +import AnimatedSprite from "../../../../Wolfie2D/Nodes/Sprites/AnimatedSprite"; +import Scene from "../../../../Wolfie2D/Scene/Scene"; +import Sprite from "../../../../Wolfie2D/Nodes/Sprites/Sprite"; +import WeaponType from "./WeaponType"; + +export default class Slice extends WeaponType { + + initialize(options: Record): void { + this.damage = options.damage; + this.cooldown = options.cooldown; + this.displayName = options.displayName; + this.spriteKey = options.spriteKey; + this.useVolume = options.useVolume; + } + + doAnimation(attacker: GameNode, direction: Vec2, sliceSprite: AnimatedSprite): void { + // Rotate this with the game node + // TODO - need to rotate the anim properly + sliceSprite.rotation = attacker.rotation; + sliceSprite.rotation = (attacker).invertX? .5* Math.PI : 1.5 * Math.PI; + // Move the slice out from the player + //scale = num of pixels between center of sprite and atk anim + sliceSprite.position = attacker.position.clone().add(direction.scaled(65)); + + sliceSprite.scaleX = 4; + sliceSprite.scaleY = 4; + // Play the slice animation w/o loop, but queue the normal animation + sliceSprite.animation.play("SLICE"); + sliceSprite.animation.queue("NORMAL", true); + } + + createRequiredAssets(scene: Scene): [AnimatedSprite] { + let slice = scene.add.animatedSprite("slice", "primary"); + slice.animation.play("NORMAL", true); + + return [slice]; + } + + hits(node: GameNode, sliceSprite: AnimatedSprite): boolean { + return sliceSprite.boundary.overlaps(node.collisionShape); + } + + clone(): WeaponType { + let newType = new Slice(); + newType.initialize({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/Player/PlayerController.ts b/src/shattered_sword/Player/PlayerController.ts index 1660505..8bd43ec 100644 --- a/src/shattered_sword/Player/PlayerController.ts +++ b/src/shattered_sword/Player/PlayerController.ts @@ -10,6 +10,12 @@ import InAir from "./PlayerStates/InAir"; import Jump from "./PlayerStates/Jump"; import Walk from "./PlayerStates/Walk"; import Debug from "../../Wolfie2D/Debug/Debug"; +import Item from "../GameSystems/items/Item"; +import InventoryManager from "../GameSystems/InventoryManager"; +import Input from "../../Wolfie2D/Input/Input"; +import BattlerAI from "../AI/BattlerAI"; +import MathUtils from "../../Wolfie2D/Utils/MathUtils"; + export enum PlayerType { PLATFORMER = "platformer", @@ -40,8 +46,8 @@ type Buffs = [ ] -export default class PlayerController extends StateMachineAI { - protected owner: GameNode; +export default class PlayerController extends StateMachineAI implements BattlerAI{ + owner: GameNode; velocity: Vec2 = Vec2.ZERO; speed: number = 200; MIN_SPEED: number = 200; @@ -57,6 +63,20 @@ export default class PlayerController extends StateMachineAI { CURRENT_DEF: number = 100; tilemap: OrthogonalTilemap; + // TODO - + damage(damage: number): void { + this.CURRENT_HP -= damage; + } + + private lookDirection: Vec2; + + + /** A list of items in the game world */ + private items: Array; + + // The inventory of the player + inventory: InventoryManager; + CURRENT_BUFFS: { atk: 0; hp: 0; @@ -91,7 +111,9 @@ export default class PlayerController extends StateMachineAI { this.tilemap = this.owner.getScene().getTilemap(options.tilemap) as OrthogonalTilemap; + this.inventory = options.inventory; + this.lookDirection = new Vec2(); } initializePlatformer(): void { @@ -132,5 +154,21 @@ export default class PlayerController extends StateMachineAI { Debug.log("playerstate", "Player State: Fall"); } Debug.log("playerspeed", "x: " + this.velocity.x + ", y:" + this.velocity.y); + + + //testing the attacks here, may be moved to another place latera + if(Input.isJustPressed("attack")){ + let item = this.inventory.getItem(); + //TODO - get proper look direction + this.lookDirection.x = (this.owner).invertX ? -1 : 1; + // If there is an item in the current slot, use it + if (item) { + item.use(this.owner, "player", this.lookDirection); + } + } + + } + + } \ No newline at end of file diff --git a/src/shattered_sword/Player/PlayerStates/InAir.ts b/src/shattered_sword/Player/PlayerStates/InAir.ts index ff3ceb2..fad5d7f 100644 --- a/src/shattered_sword/Player/PlayerStates/InAir.ts +++ b/src/shattered_sword/Player/PlayerStates/InAir.ts @@ -1,4 +1,6 @@ import GameEvent from "../../../Wolfie2D/Events/GameEvent"; +import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; +import MathUtils from "../../../Wolfie2D/Utils/MathUtils"; import { PlayerStates } from "../PlayerController"; import PlayerState from "./PlayerState"; @@ -9,6 +11,10 @@ export default abstract class InAir extends PlayerState { let dir = this.getInputDirection(); + if(dir.x !== 0){ + (this.owner).invertX = MathUtils.sign(dir.x) < 0; + } + this.parent.velocity.x += dir.x * this.parent.speed/3.5 - 0.3*this.parent.velocity.x; this.owner.move(this.parent.velocity.scaled(deltaT)); diff --git a/src/shattered_sword/Player/PlayerStates/OnGround.ts b/src/shattered_sword/Player/PlayerStates/OnGround.ts index c789576..d0f3b35 100644 --- a/src/shattered_sword/Player/PlayerStates/OnGround.ts +++ b/src/shattered_sword/Player/PlayerStates/OnGround.ts @@ -27,6 +27,8 @@ export default class OnGround extends PlayerState { } else if(!this.owner.onGround){ this.finished("fall"); } + + } onExit(): Record { diff --git a/src/shattered_sword/Registry/WeaponRegistry.ts b/src/shattered_sword/Registry/WeaponRegistry.ts index 0743c92..7e77e7a 100644 --- a/src/shattered_sword/Registry/WeaponRegistry.ts +++ b/src/shattered_sword/Registry/WeaponRegistry.ts @@ -2,6 +2,7 @@ import Registry from "../../Wolfie2D/Registry/Registries/Registry"; import ResourceManager from "../../Wolfie2D/ResourceManager/ResourceManager"; import WeaponType from "../GameSystems/items/WeaponTypes/WeaponType"; +import Slice from "../GameSystems/items/WeaponTypes/Slice"; export default class WeaponTemplateRegistry extends Registry { @@ -11,13 +12,15 @@ export default class WeaponTemplateRegistry extends Registry //TODO - // Load sprites for each weapon //rm.image("something", "shattered_sword_assets/sprites/something.png"); - + rm.image("knife", "shattered_sword_assets/sprites/knife.png"); // Load spritesheets //rm.spritesheet("weapon anim", "shattered_sword_assets/spritesheets/weapon anim.json"); + rm.spritesheet("slice", "shattered_sword_assets/spritesheets/slice.json"); // Register default types //this.registerItem("itemtype", itemTypefile); + this.registerItem("slice", Slice); } diff --git a/src/shattered_sword/Scenes/GameLevel.ts b/src/shattered_sword/Scenes/GameLevel.ts index 15360a6..defc666 100644 --- a/src/shattered_sword/Scenes/GameLevel.ts +++ b/src/shattered_sword/Scenes/GameLevel.ts @@ -23,6 +23,10 @@ import Weapon from "../GameSystems/items/Weapon"; import BattleManager from "../GameSystems/BattleManager"; //import EnemyAI from "../AI/EnemyAI"; import BattlerAI from "../AI/BattlerAI"; +import InventoryManager from "../GameSystems/InventoryManager"; +import Item from "../GameSystems/items/Item"; + + @@ -56,6 +60,9 @@ export default class GameLevel extends Scene { // Health UI protected healthLabel: Label; + + // A list of items in the scene + private items: Array; loadScene(): void { //can load player sprite here @@ -63,24 +70,44 @@ export default class GameLevel extends Scene { //can load enemy sprite here // Load the scene info - //this.load.object("weaponData", "shattered_sword_assets/data/weaponData.json"); + this.load.object("weaponData", "shattered_sword_assets/data/weaponData.json"); // Load in the enemy info //this.load.object("enemyData", "shattered_sword_assets/data/enemy.json"); // Load in item info //this.load.object("itemData", "shattered_sword_assets/data/items.json"); + + + this.load.image("knife", "shattered_sword_assets/sprites/knife.png"); + this.load.spritesheet("slice", "shattered_sword_assets/spritesheets/slice.json"); + this.load.image("inventorySlot", "shattered_sword_assets/sprites/inventory.png"); } startScene(): void { + + // Do the game level standard initializations this.initLayers(); this.initViewport(); + + // Create the battle manager + this.battleManager = new BattleManager(); + + // TODO + this.initializeWeapons(); + // Initialize the items array - this represents items that are in the game world + this.items = new Array(); + this.initPlayer(); this.subscribeToEvents(); this.addUI(); + + // Send the player and enemies to the battle manager + this.battleManager.setPlayers([this.player._ai]); + // Initialize the timers this.respawnTimer = new Timer(1000, () => { if(GameLevel.livesCount === 0){ @@ -98,6 +125,7 @@ export default class GameLevel extends Scene { }); + // Start the black screen fade out this.levelTransitionScreen.tweens.play("fadeOut"); @@ -118,9 +146,11 @@ export default class GameLevel extends Scene { } } + //update health UI + let playerAI = (this.player.ai); + this.healthLabel.text = "Player Health: "+ playerAI.CURRENT_HP +'/' + (playerAI.MAX_HP ); - - //handle collisions + //handle collisions - may be in battle manager instead //move background @@ -134,6 +164,7 @@ export default class GameLevel extends Scene { + } /** @@ -248,10 +279,57 @@ export default class GameLevel extends Scene { } + //TODO - determine whether we will have weapon datatype + /** + * + * Creates and returns a new weapon + * @param type The weaponType of the weapon, as a string + */ + createWeapon(type: string): Weapon { + let weaponType = RegistryManager.getRegistry("weaponTypes").get(type); + + let sprite = this.add.sprite(weaponType.spriteKey, "primary"); + + return new Weapon(sprite, weaponType, this.battleManager); + } + + /** + * Initalizes all weapon types based of data from weaponData.json + */ + initializeWeapons(): void{ + let weaponData = this.load.getObject("weaponData"); + + for(let i = 0; i < weaponData.numWeapons; i++){ + let weapon = weaponData.weapons[i]; + + // Get the constructor of the prototype + let constr = RegistryManager.getRegistry("weaponTemplates").get(weapon.weaponType); + + // Create a weapon type + let weaponType = new constr(); + + // Initialize the weapon type + weaponType.initialize(weapon); + + // Register the weapon type + RegistryManager.getRegistry("weaponTypes").registerItem(weapon.name, weaponType) + } + } /** * Initializes the player */ protected initPlayer(): void { + + + //create the inventory + let inventory = new InventoryManager(this, 1, "inventorySlot", new Vec2(16, 16), 4, "slots1", "items1"); + + + //add starting weapon to inventory + let startingWeapon = this.createWeapon("knife"); + inventory.addItem(startingWeapon); //using slice to test right now + + // Add the player this.player = this.add.animatedSprite("player", "primary"); this.player.scale.set(2, 2); @@ -262,7 +340,16 @@ export default class GameLevel extends Scene { this.player.position.copy(this.playerSpawn); this.player.addPhysics(new AABB(Vec2.ZERO, new Vec2(32, 32))); //sets the collision shape this.player.colliderOffset.set(0, 2); - this.player.addAI(PlayerController, {playerType: "platformer", tilemap: "Main"}); + this.player.addAI(PlayerController, { + playerType: "platformer", + tilemap: "Main", + speed: 100, + health: 10, + inventory: inventory, + items: this.items, + inputEnabled: false, + range: 100 + }); this.player.setGroup("player"); @@ -277,19 +364,19 @@ export default class GameLevel extends Scene { * @param tilePos The tilemap position to add the Enemy to * @param aiOptions The options for the Enemy AI */ - /* + protected addEnemy(spriteKey: string, tilePos: Vec2, aiOptions: Record): void { let enemy = this.add.animatedSprite(spriteKey, "primary"); enemy.position.set(tilePos.x*32, tilePos.y*32); enemy.scale.set(2, 2); enemy.addPhysics(); - enemy.addAI(EnemyController, aiOptions); //TODO - add individual enemy AI + //enemy.addAI(EnemyAI, aiOptions); //TODO - add individual enemy AI enemy.setGroup("Enemy"); enemy.setTrigger("player",Player_Events.PLAYER_HIT_ENEMY, null); } - */ + protected handlePlayerEnemyCollision(player: AnimatedSprite, enemy: AnimatedSprite) { @@ -337,17 +424,5 @@ export default class GameLevel extends Scene { } } - //TODO - determine whether we will have weapon datatype - /** - * - * Creates and returns a new weapon - * @param type The weaponType of the weapon, as a string - */ - createWeapon(type: string): Weapon { - let weaponType = RegistryManager.getRegistry("weaponTypes").get(type); - - let sprite = this.add.sprite(weaponType.spriteKey, "primary"); - - return new Weapon(sprite, weaponType, this.battleManager); - } + } \ No newline at end of file diff --git a/src/shattered_sword/Scenes/Tutorial.ts b/src/shattered_sword/Scenes/Tutorial.ts index 110ac31..6904cc0 100644 --- a/src/shattered_sword/Scenes/Tutorial.ts +++ b/src/shattered_sword/Scenes/Tutorial.ts @@ -20,6 +20,11 @@ export default class Tutorial extends GameLevel{ this.load.tilemapFromObject("forest1", this.map); this.load.spritesheet("player", "shattered_sword_assets/spritesheets/Hiro.json") + + // TODO - change when done testing + this.load.spritesheet("slice", "shattered_sword_assets/spritesheets/slice.json"); + + //load music here }