From d4cdfa82f411cd9b39b123daea02e86d001ec42c Mon Sep 17 00:00:00 2001 From: OfficialCHenry Date: Thu, 21 Apr 2022 04:51:56 -0400 Subject: [PATCH] added snakes falling, buggy enemy attacks, GOAP not working correclty --- .../data/weaponData.json | 2 +- src/shattered_sword/AI/EnemyAI.ts | 54 ++++++++--- .../AI/EnemyActions/AttackAction.ts | 47 ++++++++++ src/shattered_sword/AI/EnemyActions/Move.ts | 47 ++++++++++ src/shattered_sword/AI/EnemyStates/Patrol.ts | 10 +- .../GameSystems/items/WeaponTypes/Slice.ts | 2 +- .../Player/PlayerController.ts | 40 +++++--- src/shattered_sword/Scenes/GameLevel.ts | 93 +++++++++++++++---- src/shattered_sword/Scenes/GameOver.ts | 18 ++++ src/shattered_sword/sword_enums.ts | 3 +- 10 files changed, 259 insertions(+), 57 deletions(-) create mode 100644 src/shattered_sword/AI/EnemyActions/AttackAction.ts create mode 100644 src/shattered_sword/AI/EnemyActions/Move.ts create mode 100644 src/shattered_sword/Scenes/GameOver.ts diff --git a/dist/shattered_sword_assets/data/weaponData.json b/dist/shattered_sword_assets/data/weaponData.json index f66b7fd..ef9bb15 100644 --- a/dist/shattered_sword_assets/data/weaponData.json +++ b/dist/shattered_sword_assets/data/weaponData.json @@ -8,7 +8,7 @@ "animationSprite": "slice", "spriteKey": "knife", "damage": 50, - "cooldown": 3, + "cooldown": 30, "useVolume": 0 } ] diff --git a/src/shattered_sword/AI/EnemyAI.ts b/src/shattered_sword/AI/EnemyAI.ts index edef365..5b35db5 100644 --- a/src/shattered_sword/AI/EnemyAI.ts +++ b/src/shattered_sword/AI/EnemyAI.ts @@ -105,15 +105,10 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { this.planner = new GoapActionPlanner(); //TODO - get correct tilemap - //this.tilemap = this.owner.getScene().getTilemap(options.tilemap) as OrthogonalTilemap; this.tilemap = this.owner.getScene().getLayer("Wall").getItems()[0]; - //this.tilemap = this.owner.getScene().getTilemap("Main"); - // Initialize to the default state this.initialize(EnemyStates.DEFAULT); - //this.getPlayerPosition(); - this.direction = 1; //default moving to the right //exp value @@ -158,9 +153,9 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { } //TODO - need to modify for side view - /* + isPlayerVisible(pos: Vec2): Vec2{ - //Check if one player is visible, taking into account walls + //Check ifplayer is visible, taking into account walls // Get the new player location let start = this.owner.position.clone(); @@ -173,7 +168,7 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { let maxY = Math.max(start.y, pos.y); // Get the wall tilemap - let walls = this.owner.getScene().getLayer("Wall").getItems()[0]; + let walls = this.tilemap let minIndex = walls.getColRowAt(new Vec2(minX, minY)); let maxIndex = walls.getColRowAt(new Vec2(maxX, maxY)); @@ -193,6 +188,7 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { if (hit !== null && start.distanceSqTo(hit.pos) < start.distanceSqTo(pos)) { // We hit a wall, we can't see the player + console.log("player not visible") return null; } } @@ -201,10 +197,21 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { return pos; } - */ + + /** + * gets the position of the player + * @returns position of the player if visible, else null + */ + getPlayerPosition(): Vec2 { + //TODO - check if player is visible + if(this.isPlayerVisible(this.player.position)) + return this.player.position; + else + return null; + } update(deltaT: number){ if (InputWrapper.getState() != GameState.GAMING) { this.owner.animation.pause(); @@ -214,12 +221,18 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { super.update(deltaT); // This is the plan that is executed in the Active state, so whenever we don't have a plan, acquire a new one given the current statuses the enemy has - /* if (this.plan.isEmpty()) { //get a new plan + if(this.possibleActions === undefined){ + console.log("undefined possiblse actions"); + } + + if(this.currentState === undefined){ + console.log("undefined current status"); + } this.plan = this.planner.plan(Statuses.REACHED_GOAL, this.possibleActions, this.currentStatus, null); } - */ + //TODO if(this.burnTimer.isStopped() && this.burnCounter >0){ @@ -237,6 +250,25 @@ export default class EnemyAI extends StateMachineGoapAI implements BattlerAI { this.bleedTimer.start(); this.damage(5 + (this.player._ai).extraDotDmg + (this.player._ai).CURRENT_ATK * .08); } + + + //TODO - GOAP NOT WORKING, ATTACK WORKAROUND + if(this.getPlayerPosition() == null){ + return; + } + let distance = this.owner.position.distanceTo(this.getPlayerPosition()); + if( distance <= 60){ + if( this.direction == Math.sign(this.getPlayerPosition().x -this.owner.position.x) ){ + let dir = this.getPlayerPosition().clone().sub(this.owner.position).normalize(); + if(this.weapon.use(this.owner, "enemy", dir.scale(1,0))){ + + } + } + + } + + + } } diff --git a/src/shattered_sword/AI/EnemyActions/AttackAction.ts b/src/shattered_sword/AI/EnemyActions/AttackAction.ts new file mode 100644 index 0000000..9f414ab --- /dev/null +++ b/src/shattered_sword/AI/EnemyActions/AttackAction.ts @@ -0,0 +1,47 @@ +import StateMachineGoapAI from "../../../Wolfie2D/AI/StateMachineGoapAI"; +import GoapAction from "../../../Wolfie2D/DataTypes/Interfaces/GoapAction"; +import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; +import Emitter from "../../../Wolfie2D/Events/Emitter"; +import GameNode from "../../../Wolfie2D/Nodes/GameNode"; +import EnemyAI from "../EnemyAI"; + +export default class AttackAction extends GoapAction { + protected emitter: Emitter; + + constructor(cost: number, preconditions: Array, effects: Array, options?: Record) { + super(); + this.cost = cost; + this.preconditions = preconditions; + this.effects = effects; + } + + performAction(statuses: Array, actor: StateMachineGoapAI, deltaT: number, target?: StateMachineGoapAI): Array { + //Check if preconditions are met for this action to be performed + if (this.checkPreconditions(statuses)){ + let enemy = actor; + + //If the player is out of sight, don't bother attacking + if (enemy.getPlayerPosition() == null){ + return null; + } + + //Randomize attack direction, gives the enemy gun users stormtrooper aim + let dir = enemy.getPlayerPosition().clone().sub(enemy.owner.position).normalize(); + dir.rotateCCW(Math.PI / 4 * Math.random() - Math.PI/8); + if(enemy.weapon.use(enemy.owner, "enemy", dir)){ + // If we fired, face that direction + enemy.owner.rotation = Vec2.UP.angleToCCW(dir); + } + + return this.effects; + } + return null; + } + + updateCost(options: Record): void {} + + toString(): string { + return "(AttackAction)"; + } + +} \ No newline at end of file diff --git a/src/shattered_sword/AI/EnemyActions/Move.ts b/src/shattered_sword/AI/EnemyActions/Move.ts new file mode 100644 index 0000000..664e9a0 --- /dev/null +++ b/src/shattered_sword/AI/EnemyActions/Move.ts @@ -0,0 +1,47 @@ +import StateMachineGoapAI from "../../../Wolfie2D/AI/StateMachineGoapAI"; +import GoapAction from "../../../Wolfie2D/DataTypes/Interfaces/GoapAction"; +import Vec2 from "../../../Wolfie2D/DataTypes/Vec2"; +import Emitter from "../../../Wolfie2D/Events/Emitter"; +import NavigationPath from "../../../Wolfie2D/Pathfinding/NavigationPath"; +import EnemyAI from "../EnemyAI"; + +export default class Move extends GoapAction { + private inRange: number; + + private path: NavigationPath; + protected emitter: Emitter; + + constructor(cost: number, preconditions: Array, effects: Array, options?: Record) { + super(); + this.cost = cost; + this.preconditions = preconditions; + this.effects = effects; + this.loopAction = true; + this.inRange = options.inRange; + } + + performAction(statuses: Array, actor: StateMachineGoapAI, deltaT: number, target?: StateMachineGoapAI): Array { + if (this.checkPreconditions(statuses)){ + //Check distance from player + let enemy = actor; + let playerPos = enemy.lastPlayerPos; + let distance = enemy.owner.position.distanceTo(playerPos); + + //If close enough, we've moved far enough and this loop action is done + if (distance <= this.inRange){ + return this.effects; + } + + //Otherwise move + return null; + } + return this.effects; + } + + updateCost(options: Record): void {} + + toString(): string { + return "(Move)"; + } + +} \ No newline at end of file diff --git a/src/shattered_sword/AI/EnemyStates/Patrol.ts b/src/shattered_sword/AI/EnemyStates/Patrol.ts index dd395d6..68005b9 100644 --- a/src/shattered_sword/AI/EnemyStates/Patrol.ts +++ b/src/shattered_sword/AI/EnemyStates/Patrol.ts @@ -42,23 +42,15 @@ export default class Patrol extends EnemyState { //check if next tile on walking path is collidable if(this.parent.tilemap.isTileCollidable(colrow.x+this.parent.direction,colrow.y) || this.parent.tilemap.isTileCollidable(colrow.x+this.parent.direction,colrow.y-1)){ //turn around - //console.log(this.parent.tilemap.getTileAtRowCol(colrow)); - this.parent.direction *= -1; - //console.log((this.owner).invertX); - //(this.owner).invertX != (this.owner).invertX ; - //console.log("turn around cus wall in front"); + this.parent.direction *= -1; } //check if next ground tile is collidable else if(!this.parent.tilemap.isTileCollidable(colrow.x+this.parent.direction,colrow.y+1)){ //turn around this.parent.direction *=-1; - //console.log((this.owner).invertX); - //console.log("turn around cus no floor in front"); } else{ //keep moving - //this.velocity.x = this.speed; - //console.log("there is floor ahead"); } //move this.parent.velocity.x = this.parent.direction * this.parent.speed; diff --git a/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts b/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts index bad952b..5b131ff 100644 --- a/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts +++ b/src/shattered_sword/GameSystems/items/WeaponTypes/Slice.ts @@ -21,7 +21,7 @@ export default class Slice extends WeaponType { // TODO - need to rotate the anim properly //sliceSprite.rotation = attacker.rotation; //sliceSprite.rotation = (attacker).invertX? .5* Math.PI : 1.5 * Math.PI; - sliceSprite.invertX = (attacker).invertX; + //sliceSprite.invertX = (attacker).invertX; //TODO- //4 to scale up the default sprite - may be different later depending on atk anim diff --git a/src/shattered_sword/Player/PlayerController.ts b/src/shattered_sword/Player/PlayerController.ts index 44fd8ef..5f8d21c 100644 --- a/src/shattered_sword/Player/PlayerController.ts +++ b/src/shattered_sword/Player/PlayerController.ts @@ -52,7 +52,8 @@ export enum BuffType { LIFESTEAL = "lifesteal", LIFESTEALBUFF = "lifestealbuff", EXTRALIFE= "extralife", - ONESHOT = "oneshot" + ONESHOT = "oneshot", + JUMP = "jump" } @@ -128,6 +129,8 @@ export default class PlayerController extends StateMachineAI implements BattlerA lifestealratio : number = 0; //percent of damage to steal hasOneShot: Boolean = false; extraDotDmg : number =0; + lives: number = 1; + cooldownMultiplier : number = 1; //TODO - add new buffs here CURRENT_BUFFS: { @@ -138,8 +141,7 @@ export default class PlayerController extends StateMachineAI implements BattlerA range:number; //range will be a multiplier value: 1.5 = 150% range } - - + //TODO - get the correct tilemap initializeAI(owner: GameNode, options: Record){ @@ -260,6 +262,7 @@ export default class PlayerController extends StateMachineAI implements BattlerA this.invincible = true; //console.log("hurt anim"); (this.owner).animation.play("HURT" ); + damage /= this.BASE_DEF/ this.CURRENT_DEF; this.CURRENT_HP -= damage; //if player has shield buff give them shield when damaged if(this.hasShield){ @@ -276,6 +279,7 @@ export default class PlayerController extends StateMachineAI implements BattlerA if(this.CURRENT_HP <= 0){ (this.owner).animation.play("DYING"); (this.owner).animation.queue("DEAD", true, Player_Events.PLAYER_KILLED); + this.emitter.fireEvent(Player_Events.PLAYER_KILLED); } } @@ -331,14 +335,14 @@ export default class PlayerController extends StateMachineAI implements BattlerA {type:BuffType.ATKSPEED, value:num, category: BuffCategory.ATTACK}, ]; if(!this.hasDoubleStrike){ - attackBuffs.push({type:BuffType.DOUBLESTRIKE, value:num, category: BuffCategory.ATTACK, string:"your attacks are followed by a weaker strike"}); + attackBuffs.push({type:BuffType.DOUBLESTRIKE, value:num, category: BuffCategory.ATTACK, string:"your attacks are \nfollowed by a \nweaker strike"}); } let dotBuffs : Buff[] = [ - {type:BuffType.BLEED, value:1, category: BuffCategory.DOT, string: "Your hits apply Bleed"}, - {type:BuffType.BURN, value:1, category: BuffCategory.DOT, string: "Your hits apply Burn"}, - {type:BuffType.POISON, value:1, category: BuffCategory.DOT, string: "Your hits apply poison"}, - {type:BuffType.EXTRA_DOT, value:num, category: BuffCategory.DOT, string: "increase your DOT damage"}, + {type:BuffType.BLEED, value:1, category: BuffCategory.DOT, string: "Your hits \napply Bleed"}, + {type:BuffType.BURN, value:1, category: BuffCategory.DOT, string: "Your hits \napply Burn"}, + {type:BuffType.POISON, value:1, category: BuffCategory.DOT, string: "Your hits \napply poison"}, + {type:BuffType.EXTRA_DOT, value:num, category: BuffCategory.DOT, string: "increase your \nDOT damage"}, ]; @@ -347,10 +351,10 @@ export default class PlayerController extends StateMachineAI implements BattlerA ]; //if player doesnt have shield buff, give them the option, otherwise give buff shield option if(!this.hasShield){ - shieldBuffs.push({type:BuffType.SHIELD, value:1, category: BuffCategory.SHIELD, string: "Gain Shield When Damaged"}); + shieldBuffs.push({type:BuffType.SHIELD, value:1, category: BuffCategory.SHIELD, string: "Gain Shield \nWhen Damaged \n Shields return \nthe damage taken \nto attacker"}); } else{ - shieldBuffs.push({type:BuffType.SHIELD_DMG, value:num, category: BuffCategory.SHIELD}); + shieldBuffs.push({type:BuffType.SHIELD_DMG, value:num, category: BuffCategory.SHIELD, string: "increase damage \nreturned by shield"}); } @@ -361,17 +365,17 @@ export default class PlayerController extends StateMachineAI implements BattlerA healthBuffs.push({type:BuffType.LIFESTEAL, value:1, category: BuffCategory.HEALTH, string:"Gain lifesteal"}); } else{ - healthBuffs.push({type:BuffType.LIFESTEALBUFF, value:num, category: BuffCategory.HEALTH}); + healthBuffs.push({type:BuffType.LIFESTEALBUFF, value:num/10, category: BuffCategory.HEALTH, string:"Increase Lifesteal \nstrength by "+ num+ "%"}); } let extraBuffs : Buff[] = [ - {type:BuffType.EXTRALIFE, value:1, category: BuffCategory.EXTRA, string: "Gain an Extra Life"}, + {type:BuffType.EXTRALIFE, value:1, category: BuffCategory.EXTRA, string: "Gain an \nExtra Life"}, {type:BuffType.SPEED, value:num, category: BuffCategory.EXTRA}, {type:BuffType.ATK, value:num, category: BuffCategory.EXTRA} ]; if(!this.hasOneShot){ - extraBuffs.push({type:BuffType.ONESHOT, value:1, category: BuffCategory.EXTRA, string: "Your hits hurt 100x more but you die in one shot"}); + extraBuffs.push({type:BuffType.ONESHOT, value:1, category: BuffCategory.EXTRA, string: "Your hits hurt \n100x more but \nyour max health \nis set to 1 "}); }; @@ -460,20 +464,26 @@ export default class PlayerController extends StateMachineAI implements BattlerA case BuffType.ATKSPEED: if (item) { + this.cooldownMultiplier -= buff.value; //reduce cooldowntimer - //(item).cooldownTimer + (item).cooldownTimer = new Timer((item).cooldown * this.cooldownMultiplier ) } break; case BuffType.DOUBLESTRIKE: + //TODO - break; case BuffType.SHIELD_DMG: this.shieldDamage += buff.value/10 ; break; case BuffType.EXTRALIFE: - + this.lives ++; break; case BuffType.LIFESTEAL: this.hasLifesteal = true; + this.lifestealratio = .2; //20% lifesteal + break; + case BuffType.LIFESTEALBUFF: + this.lifestealratio += buff.value; break; case BuffType.ONESHOT: this.MAX_HP = 1; diff --git a/src/shattered_sword/Scenes/GameLevel.ts b/src/shattered_sword/Scenes/GameLevel.ts index fb44a8a..6e6ec46 100644 --- a/src/shattered_sword/Scenes/GameLevel.ts +++ b/src/shattered_sword/Scenes/GameLevel.ts @@ -36,8 +36,9 @@ import Sprite from "../../Wolfie2D/Nodes/Sprites/Sprite"; import Platformer from "../../demos/Platformer"; import TextInput from "../../Wolfie2D/Nodes/UIElements/TextInput"; import { TiledTilemapData } from "../../Wolfie2D/DataTypes/Tilesets/TiledData"; - - +import AttackAction from "../AI/EnemyActions/AttackAction"; +import Move from "../AI/EnemyActions/Move"; +import GameOver from "./GameOver"; // TODO /** @@ -262,7 +263,7 @@ export default class GameLevel extends Scene { else{ this.buffButton1.text = "Increase "+this.buffs[0].type + " by "+this.buffs[0].value; } - + if(this.buffs[1].string !== undefined){ this.buffButton2.text = this.buffs[1].string; } @@ -286,7 +287,12 @@ export default class GameLevel extends Scene { console.log("player Died"); (this.player).animation.play("DEAD", false); InputWrapper.disableInput(); - this.respawnPlayer(); + if((this.player._ai).lives >0){ + this.respawnPlayer(); + } + else{ //no more lives + this.sceneManager.changeToScene(GameOver, {}); + } break; } } @@ -343,10 +349,6 @@ export default class GameLevel extends Scene { this.expLabel.text = "EXP: "+ playerAI.CURRENT_EXP +'/' + (playerAI.MAX_EXP); // this.expLabel.sizeToText(); - - - - //handle collisions - may be in battle manager instead //move background @@ -379,6 +381,24 @@ export default class GameLevel extends Scene { } + + //spawn snake() + if(Math.random() < .002){ + console.log("RANDOM SNAKE!"); + this.addEnemy("Snake", this.player.position.clone().add(new Vec2(0,-320)),{ + player: this.player, + health: 50, + tilemap: "Main", + goal: Statuses.REACHED_GOAL, + size: new Vec2(16,16), + offset : new Vec2(0, 16), + exp: 50, + actions: [new AttackAction(3, [Statuses.IN_RANGE], [Statuses.REACHED_GOAL]), + new Move(2, [], [Statuses.IN_RANGE], {inRange: 60})], + status : [Statuses.CAN_RETREAT, Statuses.CAN_BERSERK], + weapon : this.createWeapon("knife") + }) + } } // TODO put UI changes in here @@ -403,7 +423,7 @@ export default class GameLevel extends Scene { // Add a layer for players and enemies this.addLayer("primary", 1); - this.buffLayer = this.addUILayer("buffLayer"); //TODO - test depth later, may be just a regular Layer + this.buffLayer = this.addUILayer("buffLayer"); this.storyLayer = this.addUILayer("story"); @@ -708,7 +728,19 @@ export default class GameLevel extends Scene { enemy.addAI(EnemyAI, aiOptions); //TODO - add individual enemy AI enemy.setGroup("Enemy"); enemy.setTrigger("player", Player_Events.PLAYER_COLLIDE, null); - + let actionsDefault = [new AttackAction(3, [Statuses.IN_RANGE], [Statuses.REACHED_GOAL]), + new Move(2, [], [Statuses.IN_RANGE], {inRange: 60}), + ]; + + let statusArray : Array = [Statuses.CAN_RETREAT, Statuses.CAN_BERSERK]; + + //TODO - not working correctly + if ( "status" !in aiOptions ){ + aiOptions["status"] = statusArray; + } + if( "actions" !in aiOptions){ + aiOptions["actions"] = actionsDefault; + } //add enemy to the enemy array this.enemies.push(enemy); //this.battleManager.setEnemies(this.enemies.map(enemy => enemy._ai)); @@ -716,7 +748,16 @@ export default class GameLevel extends Scene { } + //TODO - give each enemy unique weapon protected initializeEnemies( enemies: Enemy[]){ + + + let actionsDefault = [new AttackAction(3, [Statuses.IN_RANGE], [Statuses.REACHED_GOAL]), + new Move(2, [], [Statuses.IN_RANGE], {inRange: 60}), + ]; + + let statusArray : Array = [Statuses.CAN_RETREAT, Statuses.CAN_BERSERK]; + for (let enemy of enemies) { switch (enemy.type) { case "test_dummy": @@ -724,11 +765,12 @@ export default class GameLevel extends Scene { player: this.player, health: 100, tilemap: "Main", - //actions:actions, + actions: actionsDefault, + status: statusArray, goal: Statuses.REACHED_GOAL, - //TODO - test Add collision shape for each enemy type //size: new AABB(Vec2.ZERO, new Vec2(16, 25)), - exp: 100 + exp: 100, + weapon : this.createWeapon("knife") }) break; case "Snake": //Snake enemies drop from sky("trees")? or could just be very abundant @@ -736,11 +778,13 @@ export default class GameLevel extends Scene { player: this.player, health: 50, tilemap: "Main", - //actions:actions, + actions: actionsDefault, + status: statusArray, goal: Statuses.REACHED_GOAL, size: new Vec2(16,16), offset : new Vec2(0, 16), - exp: 50 + exp: 50, + weapon : this.createWeapon("knife"), }) break; case "Tiger": //Tiger can be miniboss for now? @@ -748,9 +792,11 @@ export default class GameLevel extends Scene { player: this.player, health: 200, tilemap: "Main", - //actions:actions, goal: Statuses.REACHED_GOAL, - exp: 100 + exp: 100, + weapon : this.createWeapon("knife"), + actions: actionsDefault, + status: statusArray, }) break; @@ -761,7 +807,10 @@ export default class GameLevel extends Scene { tilemap: "Main", //actions:actions, goal: Statuses.REACHED_GOAL, - exp: 50 + exp: 50, + weapon : this.createWeapon("knife"), + actions: actionsDefault, + status: statusArray, }) break; case "black_pudding": @@ -774,7 +823,10 @@ export default class GameLevel extends Scene { scale: .25, size: new Vec2(16,16), offset : new Vec2(0,0), - exp: 50 + exp: 50, + weapon : this.createWeapon("knife"), + actions: actionsDefault, + status: statusArray, }) break; default: @@ -831,6 +883,8 @@ export default class GameLevel extends Scene { InputWrapper.enableInput(); this.player.position.copy(this.startpos); (this.player._ai).CURRENT_HP = (this.player._ai).MAX_HP + (this.player._ai).CURRENT_BUFFS.hp; + (this.player._ai).lives --; + } @@ -848,6 +902,7 @@ export default class GameLevel extends Scene { //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/Scenes/GameOver.ts b/src/shattered_sword/Scenes/GameOver.ts new file mode 100644 index 0000000..0394720 --- /dev/null +++ b/src/shattered_sword/Scenes/GameOver.ts @@ -0,0 +1,18 @@ +import Vec2 from "../../Wolfie2D/DataTypes/Vec2"; +import Input from "../../Wolfie2D/Input/Input"; +import Label from "../../Wolfie2D/Nodes/UIElements/Label"; +import { UIElementType } from "../../Wolfie2D/Nodes/UIElements/UIElementTypes"; +import Scene from "../../Wolfie2D/Scene/Scene"; +import Color from "../../Wolfie2D/Utils/Color"; + +export default class GameOver extends Scene { + + startScene() { + const center = this.viewport.getCenter(); + + this.addUILayer("primary"); + + const gameOver =