From e6c285e9c1a61e817898e113e8dcdb2fb75170e1 Mon Sep 17 00:00:00 2001 From: OfficialCHenry Date: Fri, 8 Apr 2022 18:01:21 -0400 Subject: [PATCH] Added shell for game weapon system --- src/main.ts | 11 +++ src/shattered_sword/AI/BattlerAI.ts | 13 ++++ src/shattered_sword/AI/EnemyAI.ts | 0 .../GameSystems/BattleManager.ts | 47 +++++++++++ .../GameSystems/InventoryManager.ts | 0 src/shattered_sword/GameSystems/items/Item.ts | 28 +++++++ .../GameSystems/items/Weapon.ts | 78 +++++++++++++++++++ .../items/WeaponTypes/WeaponType.ts | 35 +++++++++ .../Registry/WeaponRegistry.ts | 32 ++++++++ .../Registry/WeaponTypeRegistry.ts | 14 ++++ src/shattered_sword/Scenes/GameLevel.ts | 53 ++++++++++++- src/shattered_sword/Scenes/Tutorial.ts | 1 + 12 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 src/shattered_sword/AI/BattlerAI.ts create mode 100644 src/shattered_sword/AI/EnemyAI.ts create mode 100644 src/shattered_sword/GameSystems/InventoryManager.ts create mode 100644 src/shattered_sword/GameSystems/items/Item.ts create mode 100644 src/shattered_sword/Registry/WeaponRegistry.ts create mode 100644 src/shattered_sword/Registry/WeaponTypeRegistry.ts diff --git a/src/main.ts b/src/main.ts index 80f1904..c02ec58 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,9 @@ import Game from "./Wolfie2D/Loop/Game"; import MainMenu from "./shattered_sword/Scenes/MainMenu"; +import RegistryManager from "./Wolfie2D/Registry/RegistryManager"; +import WeaponTemplateRegistry from "./shattered_sword/Registry/WeaponRegistry"; +import WeaponTypeRegistry from "./shattered_sword/Registry/WeaponTypeRegistry"; // The main function is your entrypoint into Wolfie2D. Specify your first scene and any options here. (function main(){ @@ -28,6 +31,14 @@ import MainMenu from "./shattered_sword/Scenes/MainMenu"; showDebug: true // Whether to show debug messages. You can change this to true if you want } + + // Set up custom registries + let weaponTemplateRegistry = new WeaponTemplateRegistry(); + RegistryManager.addCustomRegistry("weaponTemplates", weaponTemplateRegistry); + + let weaponTypeRegistry = new WeaponTypeRegistry(); + RegistryManager.addCustomRegistry("weaponTypes", weaponTypeRegistry); + // Create a game with the options specified const game = new Game(options); diff --git a/src/shattered_sword/AI/BattlerAI.ts b/src/shattered_sword/AI/BattlerAI.ts new file mode 100644 index 0000000..05e91b0 --- /dev/null +++ b/src/shattered_sword/AI/BattlerAI.ts @@ -0,0 +1,13 @@ +import AI from "../../Wolfie2D/DataTypes/Interfaces/AI"; +import GameEvent from "../../Wolfie2D/Events/GameEvent"; +import GameNode from "../../Wolfie2D/Nodes/GameNode"; + + +//TODO - +export default interface BattlerAI extends AI { + owner: GameNode; + + health: number; + + 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 new file mode 100644 index 0000000..e69de29 diff --git a/src/shattered_sword/GameSystems/BattleManager.ts b/src/shattered_sword/GameSystems/BattleManager.ts index e69de29..906b5b7 100644 --- a/src/shattered_sword/GameSystems/BattleManager.ts +++ b/src/shattered_sword/GameSystems/BattleManager.ts @@ -0,0 +1,47 @@ + +import GameNode from "../../Wolfie2D/Nodes/GameNode"; +import BattlerAI from "../AI/BattlerAI"; +import Weapon from "./items/Weapon"; + +export default class BattleManager { + players: Array; + + enemies: Array; + + handleInteraction(attackerType: string, weapon: Weapon) { + //may be unneeded since we are controlling the player - + //we determine enemy collision there + /* + if (attackerType === "player") { + // Check for collisions with enemies + for (let enemy of this.enemies) { + if (weapon.hits(enemy.owner)) { + enemy.damage(weapon.type.damage); + } + } + } else { + // Check for collision with player + for (let player of this.players) { + if (weapon.hits(player.owner)) { + player.damage(weapon.type.damage); + } + } + } + */ + + // Check for collision with player + for (let player of this.players) { + if (weapon.hits(player.owner)) { + player.damage(weapon.type.damage); + } + } + } + + setPlayers(player: Array): void { + this.players = player; + } + + setEnemies(enemies: Array): void { + this.enemies = enemies; + } +} \ No newline at end of file diff --git a/src/shattered_sword/GameSystems/InventoryManager.ts b/src/shattered_sword/GameSystems/InventoryManager.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/shattered_sword/GameSystems/items/Item.ts b/src/shattered_sword/GameSystems/items/Item.ts new file mode 100644 index 0000000..782b101 --- /dev/null +++ b/src/shattered_sword/GameSystems/items/Item.ts @@ -0,0 +1,28 @@ +import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; +import GameNode from "../../../Wolfie2D/Nodes/GameNode"; +import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; + +export default abstract class Item { + /** The sprite that represents this weapon in the world or in an inventory */ + sprite: Sprite; + + constructor(sprite: Sprite){ + this.sprite = sprite; + } + + moveSprite(position: Vec2, layer?: string){ + // Change the layer if needed + if(layer){ + let currentLayer = this.sprite.getLayer(); + currentLayer.removeNode(this.sprite); + let newLayer = this.sprite.getScene().getLayer(layer); + newLayer.addNode(this.sprite); + this.sprite.setLayer(newLayer); + } + + // Move the sprite + this.sprite.position.copy(position); + } + + abstract use(user: GameNode, ...args: any): void; +} \ 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 e69de29..9db2233 100644 --- a/src/shattered_sword/GameSystems/items/Weapon.ts +++ b/src/shattered_sword/GameSystems/items/Weapon.ts @@ -0,0 +1,78 @@ +//TODO import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; +import Emitter from "../../../Wolfie2D/Events/Emitter"; +import GameNode from "../../../Wolfie2D/Nodes/GameNode"; +import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite"; +import Timer from "../../../Wolfie2D/Timing/Timer"; +import BattleManager from "../BattleManager"; +import Item from "./Item"; +import WeaponType from "./WeaponTypes/WeaponType"; +import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; + + +export default class Weapon extends Item { + /** The type of this weapon */ + type: WeaponType; + + /** A list of assets this weapon needs to be animated */ + assets: Array; + + /** An event emitter to hook into the EventQueue */ + emitter: Emitter + + /** The battle manager */ + battleManager: BattleManager; + + /** The cooldown timer for this weapon's use */ + cooldownTimer: Timer; + + constructor(sprite: Sprite, type: WeaponType, battleManager: BattleManager){ + super(sprite); + + // Set the weapon type + this.type = type.clone(); + + // Keep a reference to the sprite of this weapon + this.sprite = sprite; + + // Create an event emitter + this.emitter = new Emitter(); + + // Save a reference to the battler manager + this.battleManager = battleManager; + + // Create the cooldown timer + this.cooldownTimer = new Timer(type.cooldown); + } + + // @override + /** + * Uses this weapon in the specified direction. + * This only works if the cooldown timer has ended + */ + use(user: GameNode, userType: string, direction: Vec2): boolean { + // If the cooldown timer is still running, we can't use the weapon + if(!this.cooldownTimer.isStopped()){ + return false; + } + // Rely on the weapon type to create any necessary assets + this.assets = this.type.createRequiredAssets(this.sprite.getScene()); + + // Do a type specific weapon animation + this.type.doAnimation(user, direction, ...this.assets); + + // Apply damage + this.battleManager.handleInteraction(userType, this); + + // Reset the cooldown timer + this.cooldownTimer.start(); + + return true; + } + + /** + * A check for whether or not this weapon hit a node + */ + hits(node: GameNode): boolean { + return this.type.hits(node, ...this.assets); + } +} \ No newline at end of file diff --git a/src/shattered_sword/GameSystems/items/WeaponTypes/WeaponType.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/WeaponType.ts index e69de29..a6833e3 100644 --- a/src/shattered_sword/GameSystems/items/WeaponTypes/WeaponType.ts +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/WeaponType.ts @@ -0,0 +1,35 @@ +import GameNode from "../../../../Wolfie2D/Nodes/GameNode"; +import Scene from "../../../../Wolfie2D/Scene/Scene"; + +export default abstract class WeaponType { + /** The key for this sprite image */ + spriteKey: string; + + /** How much damage this weapon does */ + damage: number; + + /** Display name */ + displayName: string; + + /** The use cooldown of the weapon */ + cooldown: number; + + /** How loud it is to use this weapon */ + useVolume: number; + + /** + * Initializes this weapon type with data + */ + abstract initialize(options: Record): void; + + /** + * The animation to do when this weapon is used + */ + abstract doAnimation(...args: any): void; + + abstract createRequiredAssets(scene: Scene): Array; + + abstract hits(node: GameNode, ...args: any): boolean; + + abstract clone(): WeaponType; +} \ No newline at end of file diff --git a/src/shattered_sword/Registry/WeaponRegistry.ts b/src/shattered_sword/Registry/WeaponRegistry.ts new file mode 100644 index 0000000..0743c92 --- /dev/null +++ b/src/shattered_sword/Registry/WeaponRegistry.ts @@ -0,0 +1,32 @@ +import Registry from "../../Wolfie2D/Registry/Registries/Registry"; +import ResourceManager from "../../Wolfie2D/ResourceManager/ResourceManager"; + +import WeaponType from "../GameSystems/items/WeaponTypes/WeaponType"; + +export default class WeaponTemplateRegistry extends Registry { + + public preload(): void { + const rm = ResourceManager.getInstance(); + + //TODO - + // Load sprites for each weapon + //rm.image("something", "shattered_sword_assets/sprites/something.png"); + + + // Load spritesheets + //rm.spritesheet("weapon anim", "shattered_sword_assets/spritesheets/weapon anim.json"); + + // Register default types + //this.registerItem("itemtype", itemTypefile); + + } + + + public registerAndPreloadItem(key: string): void {} + + public registerItem(key: string, constr: WeaponConstructor): void { + this.add(key, constr); + } +} + +type WeaponConstructor = new (...args: any) => WeaponType; \ No newline at end of file diff --git a/src/shattered_sword/Registry/WeaponTypeRegistry.ts b/src/shattered_sword/Registry/WeaponTypeRegistry.ts new file mode 100644 index 0000000..31228db --- /dev/null +++ b/src/shattered_sword/Registry/WeaponTypeRegistry.ts @@ -0,0 +1,14 @@ +import Registry from "../../Wolfie2D/Registry/Registries/Registry"; +import WeaponType from "../GameSystems/items/WeaponTypes/WeaponType"; + +export default class WeaponTypeRegistry extends Registry { + + public preload(): void {} + + // We don't need this for this assignment + public registerAndPreloadItem(key: string): void {} + + public registerItem(key: string, type: WeaponType): void { + this.add(key, type); + } +} \ No newline at end of file diff --git a/src/shattered_sword/Scenes/GameLevel.ts b/src/shattered_sword/Scenes/GameLevel.ts index 6bd85aa..15360a6 100644 --- a/src/shattered_sword/Scenes/GameLevel.ts +++ b/src/shattered_sword/Scenes/GameLevel.ts @@ -17,6 +17,14 @@ import { EaseFunctionType } from "../../Wolfie2D/Utils/EaseFunctions"; import PlayerController from "../Player/PlayerController"; import MainMenu from "./MainMenu"; import { Player_Events } from "../sword_enums"; +import RegistryManager from "../../Wolfie2D/Registry/RegistryManager"; +import WeaponType from "../GameSystems/items/WeaponTypes/WeaponType"; +import Weapon from "../GameSystems/items/Weapon"; +import BattleManager from "../GameSystems/BattleManager"; +//import EnemyAI from "../AI/EnemyAI"; +import BattlerAI from "../AI/BattlerAI"; + + // TODO /** @@ -42,12 +50,27 @@ export default class GameLevel extends Scene { // Screen fade in/out for level start and end protected levelTransitionTimer: Timer; protected levelTransitionScreen: Rect; - + + // The battle manager for the scene + private battleManager: BattleManager; // Health UI protected healthLabel: Label; + loadScene(): void { + //can load player sprite here + //can load enemy sprite here + + // Load the scene info + //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"); + } startScene(): void { @@ -211,6 +234,18 @@ export default class GameLevel extends Scene { }); + // Initialize all enemies + //this.initializeEnemies(); + + // Send the player and enemies to the battle manager + //this.battleManager.setPlayers([this.player._ai]); + //this.battleManager.setEnemies(this.enemies.map(enemy => enemy._ai)); + + // Subscribe to relevant events + //this.receiver.subscribe(""); + + + } /** @@ -258,7 +293,7 @@ export default class GameLevel extends Scene { protected handlePlayerEnemyCollision(player: AnimatedSprite, enemy: AnimatedSprite) { - + //collisions are handled by the battleManager - no need for this in gamelevel for now } /** @@ -301,4 +336,18 @@ export default class GameLevel extends Scene { this.player.position.set(this.playerSpawn.x,this.playerSpawn.y); } } + + //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 ca4c40e..5432ee2 100644 --- a/src/shattered_sword/Scenes/Tutorial.ts +++ b/src/shattered_sword/Scenes/Tutorial.ts @@ -9,6 +9,7 @@ import GameLevel from "./GameLevel"; export default class Tutorial extends GameLevel{ loadScene(): void { + super.loadScene(); // Load resources // this.load.tilemap("forest1", "shattered_sword_assets/tilemaps/Tutorial.json"); // let map = localStorage.getItem("map");