From 491d8efec7d451e1d010b3da08e04511a0c82d73 Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 08:30:43 -0500 Subject: [PATCH 1/7] Start of boat battle --- boatBattle.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++++--- island.ts | 45 +++++++++------------------------------- main.ts | 14 ++++++++++--- militia.ts | 2 +- pirate.ts | 3 +++ utils.ts | 30 ++++++++++++++++++++++++++- 6 files changed, 108 insertions(+), 43 deletions(-) diff --git a/boatBattle.ts b/boatBattle.ts index e364a30..966f463 100644 --- a/boatBattle.ts +++ b/boatBattle.ts @@ -1,15 +1,66 @@ namespace BoatBattle { - let enemies: Sprite[] + let enemies: Militia[] = [] let player1: Pirate let player2: Pirate + const player1StatLocation: number[] = [12, 10] + const player2StatLocation: number[] = [130, 10] + let _onWinCallback: () => void + let _onAllDeadCallback: () => void + + const _boundingBox: number[] = [0, 10, 160, 120] + export function init() { scene.setBackgroundColor(6) scene.setBackgroundImage(assets.image`empty`) // Spawn the players - // player1 = new Pirate({ control: controller.player1 }) + player1 = new Pirate({ control: controller.player1, playerNumber: 0, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player1StatLocation }) + player2 = new Pirate({ control: controller.player2, playerNumber: 1, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player2StatLocation }) + + // Spawn the enemies! + // Based on the amount of treasure you have, more enemies will appear! + const totalTreasure = TreasureStats.getTotal() + let numberOfEnemies = 1 + if (totalTreasure > 1000) { + numberOfEnemies = 8 + } else { + // A log curve to determine the number of enemies + numberOfEnemies = 1 + Math.log(TreasureStats.getTotal() + 1) / Math.log(1000) * 6; + } + + Utils.getArrayOfLength(numberOfEnemies).forEach(() => { + const militia = new Militia({ x: 50, y: 50, target: Math.pickRandom([player1, player2]) }) + enemies.push(militia) + }) + } + + export function destory() { + player1.destroy() + player2.destroy() + enemies.forEach((e) => e.destroy()) + + scene.setBackgroundImage(assets.image`empty`) + } + + export function onWin(callback: () => void) { + _onWinCallback = callback + } + + export function onAllDead(callback: () => void) { + _onAllDeadCallback = callback + } + + function onPirateAttack({ pirate }: { pirate: Pirate }) { + const hitEnemies = Utils.getHitEnemies({ pirate, enemies }) + hitEnemies.forEach((enemy) => { + if (enemy.health <= 0 && enemy.riches > 0) { + enemy.lootTheBody() + } else { + enemy.hit(1) + } + }) } - export function destory() {} + function onPirateDeath() {} } diff --git a/island.ts b/island.ts index 3602747..de0b68f 100644 --- a/island.ts +++ b/island.ts @@ -16,7 +16,7 @@ namespace Island { const player2StatLocation: number[] = [130, 10] // This is the bounding box for enemy and player movement (aka the street) // [topleftX, topLeftY, bottomLeftX, bottomLeftY] - let _boundingBox: number[] = [0, 55, 160, 120] + const _boundingBox: number[] = [0, 55, 160, 120] let _island: Map.Island let _onUpdateTreasure: (T: TreasureStats.OnUpdateTreasureProps) => void = () => undefined let _onLeaveIsland: () => void @@ -28,12 +28,6 @@ namespace Island { let _treasureOpened: boolean = false function onPirateAttack({ pirate }: { pirate: Pirate }) { - const dirPix = pirate.direction === 'left' ? -1 : 1 - // The hit zone is the pirate "sword" box: [center, right|left] and [top, bottom] - const hitXZone = [pirate.sprite.x, pirate.sprite.x + (13 * dirPix)] - // The sword is only near the top of the sprite, we don't kill with feet - const hitYZone = [pirate.sprite.y - 4, pirate.sprite.y + 2] - // Check to see if we slashed the treasure! if (_treasureSprite && isSegmentComplete && Math.abs(pirate.sprite.x - _treasureSprite.x) < 10 @@ -41,29 +35,13 @@ namespace Island { openTreasure() return } - - // manually check each enemy to see if they overlap, also check for parry - currentEnemies.forEach((enemy) => { - // Don't hurt the dead, that's just mean - if (enemy.health <= 0 && enemy.riches <= 0) return - if (pirate.direction === 'right' - && enemy.sprite.x >= hitXZone[0] && enemy.sprite.x <= hitXZone[1] - // Bottom of pirate is overlapping the top of the enemy (and opposite) - && hitYZone[1] >= enemy.sprite.y - (enemy.sprite.height / 2) && hitYZone[0] <= enemy.sprite.y + (enemy.sprite.height / 2)) { - if (enemy.health <= 0 && enemy.riches > 0) { - enemy.lootTheBody() - } else { - enemy.hit(1) - } - } else if (pirate.direction === 'left' - && enemy.sprite.x <= hitXZone[0] && enemy.sprite.x >= hitXZone[1] - // Same vertical check as the right side - && hitYZone[1] >= enemy.sprite.y - (enemy.sprite.height / 2) && hitYZone[0] <= enemy.sprite.y + (enemy.sprite.height / 2)) { - if (enemy.health <= 0 && enemy.riches > 0) { - enemy.lootTheBody() - } else { - enemy.hit(1) - } + + const hitEnemies = Utils.getHitEnemies({ pirate, enemies: currentEnemies }) + hitEnemies.forEach((enemy) => { + if (enemy.health <= 0 && enemy.riches > 0) { + enemy.lootTheBody() + } else { + enemy.hit(1) } }) @@ -71,9 +49,6 @@ namespace Island { } function onPirateDeath({ pirate }: { pirate: Pirate}) { - // Reduce lives - PirateLives.updatePirateCount(-1) - // If there's still a living pirate, re-target all enemies to the other pirate if (player1.health > 0) { retargetEnemies(player1) @@ -140,7 +115,7 @@ namespace Island { currentEnemies.forEach(enemy => { if (enemy.health <= 0) { - enemy.destory() + enemy.destroy() } }) @@ -260,7 +235,7 @@ namespace Island { player1.destroy() player2.destroy() - currentEnemies.map(enemy => enemy.destory()) + currentEnemies.map(enemy => enemy.destroy()) currentEnemies = [] currentCivilians.map(civilian => civilian.destroy()) currentCivilians = [] diff --git a/main.ts b/main.ts index 6b0c462..5d8ffc6 100644 --- a/main.ts +++ b/main.ts @@ -20,6 +20,7 @@ enum States { Overview, Island, AllDead, + BoatBattle, GameOver, Win } @@ -72,6 +73,9 @@ function switchState(state: States) { case States.Island: Island.init({ island: currentIsland }) break; + case States.BoatBattle: + BoatBattle.init() + break; case States.AllDead: AllDead.init() break; @@ -81,12 +85,13 @@ function switchState(state: States) { case States.Win: Win.init() break; + case States.Menu: default: Menu.init() } } -function startGame() { +function startGame(initialState?: States) { Map.onSelectIsland((island: Map.Island) => { currentIsland = island switchState(States.Island) @@ -107,6 +112,9 @@ function startGame() { } }) + BoatBattle.onWin(() => {}) + BoatBattle.onAllDead(() => {}) + Menu.onStartGame(() => { switchState(States.Overview) }) @@ -115,7 +123,7 @@ function startGame() { switchState(States.Overview) }) - switchState(States.Menu) + switchState(initialState ? initialState : States.Menu) } -startGame() \ No newline at end of file +startGame(States.BoatBattle) \ No newline at end of file diff --git a/militia.ts b/militia.ts index d7f4c71..8c525ee 100644 --- a/militia.ts +++ b/militia.ts @@ -60,7 +60,7 @@ class Militia { } } - public destory() { + public destroy() { this.sprite.destroy() } diff --git a/pirate.ts b/pirate.ts index 6301336..eaf619f 100644 --- a/pirate.ts +++ b/pirate.ts @@ -162,6 +162,9 @@ class Pirate { characterAnimations.clearCharacterState(this.sprite) }, this.hurtLeftAnimation.length * 200) } else { + // Reduce pirate lives count + PirateLives.updatePirateCount(-1) + // You dead! animation.runImageAnimation( this.sprite, diff --git a/utils.ts b/utils.ts index 5b7894d..01d5d1c 100644 --- a/utils.ts +++ b/utils.ts @@ -27,5 +27,33 @@ namespace Utils { return anim } - export function checkAttack({ pirate, enemies }: { pirate: Pirate, enemies: Sprite[] }) {} + export function getHitEnemies({ pirate, enemies }: { pirate: Pirate, enemies: Militia[] }) { + const dirPix = pirate.direction === 'left' ? -1 : 1 + // The hit zone is the pirate "sword" box: [center, right|left] and [top, bottom] + const hitXZone = [pirate.sprite.x, pirate.sprite.x + (13 * dirPix)] + // The sword is only near the top of the sprite, we don't kill with feet + const hitYZone = [pirate.sprite.y - 4, pirate.sprite.y + 2] + + // manually check each enemy to see if they overlap, also check for parry + return enemies.reduce((hitEnemies, enemy) => { + // Don't hurt the dead, that's just mean + if (enemy.health <= 0 && enemy.riches <= 0) { + return hitEnemies + } + + if (pirate.direction === 'right' + && enemy.sprite.x >= hitXZone[0] && enemy.sprite.x <= hitXZone[1] + // Bottom of pirate is overlapping the top of the enemy (and opposite) + && hitYZone[1] >= enemy.sprite.y - (enemy.sprite.height / 2) && hitYZone[0] <= enemy.sprite.y + (enemy.sprite.height / 2)) { + hitEnemies.push(enemy) + } else if (pirate.direction === 'left' + && enemy.sprite.x <= hitXZone[0] && enemy.sprite.x >= hitXZone[1] + // Same vertical check as the right side + && hitYZone[1] >= enemy.sprite.y - (enemy.sprite.height / 2) && hitYZone[0] <= enemy.sprite.y + (enemy.sprite.height / 2)) { + hitEnemies.push(enemy) + } + + return hitEnemies + }, []) + } } From fba988599d4fe149729c36489af5a5944e84ae1c Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 11:48:22 -0500 Subject: [PATCH 2/7] Fixed a critical memory leak between segments --- boatBattle.ts | 2 +- island.ts | 15 ++++++++------- main.ts | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/boatBattle.ts b/boatBattle.ts index 966f463..49721a2 100644 --- a/boatBattle.ts +++ b/boatBattle.ts @@ -16,7 +16,7 @@ namespace BoatBattle { // Spawn the players player1 = new Pirate({ control: controller.player1, playerNumber: 0, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player1StatLocation }) - player2 = new Pirate({ control: controller.player2, playerNumber: 1, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player2StatLocation }) + // player2 = new Pirate({ control: controller.player2, playerNumber: 1, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player2StatLocation }) // Spawn the enemies! // Based on the amount of treasure you have, more enemies will appear! diff --git a/island.ts b/island.ts index de0b68f..ea8ae7e 100644 --- a/island.ts +++ b/island.ts @@ -113,11 +113,13 @@ namespace Island { player2.sprite.x = 10 } - currentEnemies.forEach(enemy => { - if (enemy.health <= 0) { - enemy.destroy() - } - }) + + // Destroy all enemies! + currentEnemies.forEach((enemy) => enemy.destroy()) + currentEnemies = [] + // And civilians, just in case + currentCivilians.map(civilian => civilian.destroy()) + currentCivilians = [] isSegmentComplete = false @@ -198,7 +200,7 @@ namespace Island { // Start most enemies a bit from the left (avoiding starting ON the players) const averageAmount = Math.floor(_island.risk + (1 * currentSegment)) const numberOfEnemies = Math.max(Math.randomRange(averageAmount - 2, averageAmount), 1) - console.log('Enemies ' + currentSegment + ':' + _island.risk) + Utils.getArrayOfLength(numberOfEnemies).forEach(() => { const locX = Math.randomRange(_boundingBox[0] + 20, _boundingBox[2]) const locY = Math.randomRange(_boundingBox[1], _boundingBox[3]) @@ -254,7 +256,6 @@ namespace Island { } function whenAllDead() { - console.log('All dead! ' + _allDead) if (!_allDead) { _allDead = true // You lose all your inPocket AND boat coin! diff --git a/main.ts b/main.ts index 5d8ffc6..2d2b487 100644 --- a/main.ts +++ b/main.ts @@ -126,4 +126,4 @@ function startGame(initialState?: States) { switchState(initialState ? initialState : States.Menu) } -startGame(States.BoatBattle) \ No newline at end of file +startGame() // States.BoatBattle \ No newline at end of file From 7f0604e558ecac8ae544647c86d95a22dd591070 Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 12:27:11 -0500 Subject: [PATCH 3/7] Timer on boat battle is rough --- boatBattle.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++---- enemyPirate.ts | 5 +++++ main.ts | 13 +++++++++--- pxt.json | 3 ++- 4 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 enemyPirate.ts diff --git a/boatBattle.ts b/boatBattle.ts index 49721a2..82a5c2c 100644 --- a/boatBattle.ts +++ b/boatBattle.ts @@ -8,31 +8,66 @@ namespace BoatBattle { let _onWinCallback: () => void let _onAllDeadCallback: () => void + const _timeAllowed = 5 + let _currentTime = _timeAllowed + let _lastTick = 0 + let _isDone = false const _boundingBox: number[] = [0, 10, 160, 120] + const _enemyBoatBox: number[] = [0, 10, 160, 60] export function init() { scene.setBackgroundColor(6) scene.setBackgroundImage(assets.image`empty`) + _currentTime = _timeAllowed + _lastTick = control.millis() + // Spawn the players player1 = new Pirate({ control: controller.player1, playerNumber: 0, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player1StatLocation }) - // player2 = new Pirate({ control: controller.player2, playerNumber: 1, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player2StatLocation }) + player2 = new Pirate({ control: controller.player2, playerNumber: 1, onAttack: onPirateAttack, onDie: onPirateDeath, topBoundary: _boundingBox[1], statLocation: player2StatLocation }) // Spawn the enemies! // Based on the amount of treasure you have, more enemies will appear! const totalTreasure = TreasureStats.getTotal() let numberOfEnemies = 1 if (totalTreasure > 1000) { - numberOfEnemies = 8 + numberOfEnemies = 10 } else { // A log curve to determine the number of enemies - numberOfEnemies = 1 + Math.log(TreasureStats.getTotal() + 1) / Math.log(1000) * 6; + numberOfEnemies = 4 + Math.log(TreasureStats.getTotal() + 1) / Math.log(1000) * 8; } Utils.getArrayOfLength(numberOfEnemies).forEach(() => { - const militia = new Militia({ x: 50, y: 50, target: Math.pickRandom([player1, player2]) }) + const locX = Math.randomRange(_enemyBoatBox[0], _enemyBoatBox[2]) + const locY = Math.randomRange(_enemyBoatBox[1], _enemyBoatBox[3]) + const militia = new Militia({ x: locX, y: locY, target: Math.pickRandom([player1, player2]) }) enemies.push(militia) }) + + + } + + export function render() { + player1.render() + player2.render() + + enemies.forEach(enemy => enemy.render()) + + // Show timer (I know there's an extension but I want to try and roll my own) + if (control.millis() - _lastTick > 1000 && !_isDone) { + _lastTick = control.millis() + _currentTime -= 1 + console.log('Time! ' + _currentTime) + } + + if (_currentTime <= 0 && !_isDone && enemies.some((enemy) => enemy.health > 0)) { + _isDone = true + console.log('Game over!') + pause(1000) + + destory() + _onAllDeadCallback() + } } export function destory() { @@ -60,7 +95,21 @@ namespace BoatBattle { enemy.hit(1) } }) + + checkIfWin() } function onPirateDeath() {} + + function checkIfWin() { + const anyAlive = enemies.some((enemy) => enemy.health > 0) + if (!anyAlive) { + _isDone = true + console.log("you dit it!") + pause(1000) + + destory() + _onWinCallback() + } + } } diff --git a/enemyPirate.ts b/enemyPirate.ts new file mode 100644 index 0000000..8ff1276 --- /dev/null +++ b/enemyPirate.ts @@ -0,0 +1,5 @@ +class EnemyPirate extends Militia { + constructor({ x, y, target }: { x: number, y: number, target: Pirate }) { + super({ x, y, target }) + } +} diff --git a/main.ts b/main.ts index 2d2b487..103ec8d 100644 --- a/main.ts +++ b/main.ts @@ -50,6 +50,9 @@ game.onUpdate(() => { case States.Island: Island.render(); break; + case States.BoatBattle: + BoatBattle.render() + break; default: break; } @@ -112,8 +115,12 @@ function startGame(initialState?: States) { } }) - BoatBattle.onWin(() => {}) - BoatBattle.onAllDead(() => {}) + BoatBattle.onWin(() => { + switchState(States.Overview) + }) + BoatBattle.onAllDead(() => { + switchState(States.Overview) + }) Menu.onStartGame(() => { switchState(States.Overview) @@ -126,4 +133,4 @@ function startGame(initialState?: States) { switchState(initialState ? initialState : States.Menu) } -startGame() // States.BoatBattle \ No newline at end of file +startGame(States.BoatBattle) \ No newline at end of file diff --git a/pxt.json b/pxt.json index aea7478..36b4aa9 100644 --- a/pxt.json +++ b/pxt.json @@ -26,7 +26,8 @@ "pirateLives.ts", "gameOver.ts", "win.ts", - "boatBattle.ts" + "boatBattle.ts", + "enemyPirate.ts" ], "testFiles": [ "test.ts" From 05038a565246e68cf07064286fab11ba219ac674 Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 15:03:00 -0500 Subject: [PATCH 4/7] Enemy class is extracted --- boatBattle.ts | 11 ++-- enemy.ts | 83 +++++++++++++++++++++++++++++ enemyPirate.ts | 2 + island.ts | 2 +- main.ts | 6 +-- militia.ts | 136 ++++++++++++++--------------------------------- pxt.json | 3 +- treasureStats.ts | 37 ------------- utils.ts | 2 +- 9 files changed, 137 insertions(+), 145 deletions(-) create mode 100644 enemy.ts diff --git a/boatBattle.ts b/boatBattle.ts index 82a5c2c..ba603b3 100644 --- a/boatBattle.ts +++ b/boatBattle.ts @@ -1,5 +1,5 @@ namespace BoatBattle { - let enemies: Militia[] = [] + let enemies: EnemyPirate[] = [] let player1: Pirate let player2: Pirate @@ -8,7 +8,7 @@ namespace BoatBattle { let _onWinCallback: () => void let _onAllDeadCallback: () => void - const _timeAllowed = 5 + const _timeAllowed = 10 let _currentTime = _timeAllowed let _lastTick = 0 let _isDone = false @@ -43,8 +43,6 @@ namespace BoatBattle { const militia = new Militia({ x: locX, y: locY, target: Math.pickRandom([player1, player2]) }) enemies.push(militia) }) - - } export function render() { @@ -89,9 +87,8 @@ namespace BoatBattle { function onPirateAttack({ pirate }: { pirate: Pirate }) { const hitEnemies = Utils.getHitEnemies({ pirate, enemies }) hitEnemies.forEach((enemy) => { - if (enemy.health <= 0 && enemy.riches > 0) { - enemy.lootTheBody() - } else { + // No looting of EnemyPirates + if (enemy.health > 0) { enemy.hit(1) } }) diff --git a/enemy.ts b/enemy.ts new file mode 100644 index 0000000..766170e --- /dev/null +++ b/enemy.ts @@ -0,0 +1,83 @@ +class Enemy { + static speed = 10 + + public sprite: Sprite + public health: number = 1 + public riches = 1 + + protected _currentTarget: Pirate + protected _nextAttackTime: number = 0 + protected _lastAttackTick: number = 0 + protected _facing: 'left' | 'right' = 'left' + protected _isAttacking: boolean = false + protected _lastDirectionTick: number = 0 + + constructor({ x, y, target, sprite }: { x: number, y: number, target: Pirate, sprite: Sprite }) { + this.sprite = sprite + this.sprite.x = x + this.sprite.y = y + + // On initial spawn they are quick to attack! + this._nextAttackTime = Math.randomRange(Militia.attackDelayMin / 2, Militia.attackDelayMax / 2) + this._lastAttackTick = control.millis() + + this._currentTarget = target + } + + public destroy() { + if (this.sprite) { + this.sprite.destroy() + } + } + + public setCurrentTarget(pirate: Pirate) { + if (pirate.health > 0 && this.health > 0) { + this._currentTarget = pirate + this.sprite.follow(this._currentTarget.sprite, Enemy.speed) + } + } + + protected walk(direction?: 'left' | 'right') { + this._facing = direction ? direction : this._facing + this.sprite.follow(this._currentTarget.sprite, Enemy.speed) + } + + protected hit(damage: number) { + this.health -= damage + + if (this.health <= 0) { + this.sprite.follow(this._currentTarget.sprite, 0) + } + } + + protected attack() { + // Stop moving + this.sprite.follow(this._currentTarget.sprite, 0) + this._isAttacking = true + } + + protected die() {} + + public render() { + // Check your distance from the target randomly + if ((control.millis() - this._lastDirectionTick) > Militia.directionChangeInterval) { + this._lastDirectionTick = control.millis() + if (Math.abs(Utils.getDistance( + { x: this.sprite.x, y: this.sprite.y }, + { x: this._currentTarget.sprite.x, y: this._currentTarget.sprite.y } + )) < 30) { + this.sprite.follow(this._currentTarget.sprite, 0) + } else { + this.sprite.follow(this._currentTarget.sprite, Enemy.speed) + } + } + + // Face your target + if (this._currentTarget.sprite.x < this.sprite.x && this._facing === 'right' && !this._isAttacking) { + this.walk('left') + } else if (this._currentTarget.sprite.x > this.sprite.x && this._facing === 'left' && !this._isAttacking) { + this.walk('right') + } + this.sprite.z = this.sprite.y + } +} diff --git a/enemyPirate.ts b/enemyPirate.ts index 8ff1276..4d25919 100644 --- a/enemyPirate.ts +++ b/enemyPirate.ts @@ -1,4 +1,6 @@ class EnemyPirate extends Militia { + static walkRightAnimation: Image[] = assets.animation`Pirate Walk` + constructor({ x, y, target }: { x: number, y: number, target: Pirate }) { super({ x, y, target }) } diff --git a/island.ts b/island.ts index ea8ae7e..6d0d298 100644 --- a/island.ts +++ b/island.ts @@ -6,7 +6,7 @@ namespace Island { let player1: Pirate let player2: Pirate - let currentEnemies: Array = [] + let currentEnemies: Array = [] let currentCivilians: Array = [] let currentSegment: number = 0 let isSegmentComplete: boolean = false diff --git a/main.ts b/main.ts index 103ec8d..bd15547 100644 --- a/main.ts +++ b/main.ts @@ -51,7 +51,7 @@ game.onUpdate(() => { Island.render(); break; case States.BoatBattle: - BoatBattle.render() + // BoatBattle.render() break; default: break; @@ -77,7 +77,7 @@ function switchState(state: States) { Island.init({ island: currentIsland }) break; case States.BoatBattle: - BoatBattle.init() + // BoatBattle.init() break; case States.AllDead: AllDead.init() @@ -133,4 +133,4 @@ function startGame(initialState?: States) { switchState(initialState ? initialState : States.Menu) } -startGame(States.BoatBattle) \ No newline at end of file +startGame() \ No newline at end of file diff --git a/militia.ts b/militia.ts index 8c525ee..d826bee 100644 --- a/militia.ts +++ b/militia.ts @@ -1,4 +1,4 @@ -class Militia { +class Militia extends Enemy { static walkRightAnimation: Image[] = assets.animation`Militia Walk` static walkLeftAnimation: Image[] = Utils.flipAnimation(assets.animation`Militia Walk`) static attackRightAnimation: Image[] = assets.animation`Militia Shoot` @@ -7,47 +7,27 @@ class Militia { static deathLeftAnimation: Image[] = Utils.flipAnimation(assets.animation`Militia Die`) static parrySound: music.SoundEffect = music.createSoundEffect(WaveShape.Noise, 5000, 5000, 255, 0, 100, SoundExpressionEffect.Vibrato, InterpolationCurve.Curve) - static speed: number = 10 static directionChangeInterval: number = 1000 static attackDelayMin: number = 4000 static attackDelayMax: number = 6000 - private currentTarget?: Pirate private facing: 'left' | 'right' = 'right' - private _nextAttackTime: number - private _lastAttackTick: number - private _lastDirectionTick: number = 0 - private _isAttacking: boolean = false - private _isParrying: boolean = false - public riches = 1 - public sprite: Sprite - public health: number = 1 + // private _isParrying: boolean = false constructor({ x, y, target }: { x: number, y: number, target?: Pirate }) { - this.sprite = sprites.create(assets.animation`Militia Walk`[0]) - this.place(x, y) - // On initial spawn they are quick to attack! - this._nextAttackTime = Math.randomRange(Militia.attackDelayMin / 2, Militia.attackDelayMax / 2) - this._lastAttackTick = control.millis() - - this.currentTarget = target + super({ x, y, target, sprite: sprites.create(assets.animation`Militia Walk`[0]) }) // Most often we spawn to the right, so walk left this.walk('left') } - public place(x: number, y: number) { - this.sprite.x = x - this.sprite.y = y - } - public hit(damage: number) { - if (this._isParrying) { - music.play(Militia.parrySound, music.PlaybackMode.InBackground) - return - } - - this.health -= damage + // if (this._isParrying) { + // music.play(Militia.parrySound, music.PlaybackMode.InBackground) + // return + // } + + super.hit(damage) if (this.health <= 0) { animation.runImageAnimation( @@ -56,72 +36,20 @@ class Militia { 100, false ) - this.sprite.follow(this.currentTarget.sprite, 0) } } - public destroy() { - this.sprite.destroy() - } - public render() { - // Short circut if I die - if (this.health <= 0 ) return - + if (this.health <= 0) return + + super.render() + // Attack randomly if ((control.millis() - this._lastAttackTick) > this._nextAttackTime) { this._lastAttackTick = control.millis() this._nextAttackTime = Math.randomRange(Militia.attackDelayMin, Militia.attackDelayMax) this.attack() } - // Check your distance from the target randomly (TODO) - if ((control.millis() - this._lastDirectionTick) > Militia.directionChangeInterval) { - this._lastDirectionTick = control.millis() - if (Math.abs(Utils.getDistance( - { x: this.sprite.x, y: this.sprite.y }, - { x: this.currentTarget.sprite.x, y: this.currentTarget.sprite.y } - )) < 30) { - this.sprite.follow(this.currentTarget.sprite, 0) - } else { - this.sprite.follow(this.currentTarget.sprite, Militia.speed) - } - } - - // Face your target - if (this.currentTarget.sprite.x < this.sprite.x && this.facing === 'right' && !this._isAttacking) { - this.walk('left') - } else if (this.currentTarget.sprite.x > this.sprite.x && this.facing === 'left' && !this._isAttacking) { - this.walk('right') - } - this.sprite.z = this.sprite.y - } - - public setCurrentTarget(pirate: Pirate) { - if (pirate.health > 0 && this.health > 0) { - this.currentTarget = pirate - this.sprite.follow(this.currentTarget.sprite, Militia.speed) - } - } - - private walk(direction?: 'left' | 'right') { - this.facing = direction ? direction : this.facing - this.sprite.follow(this.currentTarget.sprite, Militia.speed) - - if (this.facing === 'left') { - animation.runImageAnimation( - this.sprite, - Militia.walkLeftAnimation, - 500, - true - ) - } else { - animation.runImageAnimation( - this.sprite, - Militia.walkRightAnimation, - 500, - true - ) - } } public lootTheBody() { @@ -148,10 +76,29 @@ class Militia { } } - private attack() { - // Stop moving - this.sprite.follow(this.currentTarget.sprite, 0) - this._isAttacking = true + protected walk(direction?: 'left' | 'right') { + super.walk(direction) + + if (this._facing === 'left') { + animation.runImageAnimation( + this.sprite, + Militia.walkLeftAnimation, + 500, + true + ) + } else { + animation.runImageAnimation( + this.sprite, + Militia.walkRightAnimation, + 500, + true + ) + } + } + + protected attack() { + super.attack() + // Play the fire animation if (this.facing === 'right') { animation.runImageAnimation( @@ -172,13 +119,13 @@ class Militia { // Slightly after the animation we check to see if we hit setTimeout(() => { // Make sure we didn't die in this tiny delay: - if (this.health > 0 && this.currentTarget && this.sprite) { + if (this.health > 0 && this._currentTarget && this.sprite) { // bigCrash or sonar.... music.play(music.melodyPlayable(music.bigCrash), music.PlaybackMode.InBackground) // Check to see that our target is in range and fire the hit - if (Math.abs(this.sprite.y - this.currentTarget.sprite.y) < 20) { - this.currentTarget.hit(this, 1) + if (Math.abs(this.sprite.y - this._currentTarget.sprite.y) < 20) { + this._currentTarget.hit(this, 1) } } }, Militia.attackRightAnimation.length / 2 * 100) @@ -186,10 +133,9 @@ class Militia { // Resume walking setTimeout(() => { this._isAttacking = false - if (this.health > 0 && this.currentTarget && this.sprite) { + if (this.health > 0 && this._currentTarget && this.sprite) { this.walk() } - }, Militia.attackRightAnimation.length * 100) - + }, Militia.attackRightAnimation.length * 100) } } diff --git a/pxt.json b/pxt.json index 36b4aa9..738a233 100644 --- a/pxt.json +++ b/pxt.json @@ -27,7 +27,8 @@ "gameOver.ts", "win.ts", "boatBattle.ts", - "enemyPirate.ts" + "enemyPirate.ts", + "enemy.ts" ], "testFiles": [ "test.ts" diff --git a/treasureStats.ts b/treasureStats.ts index 03577b7..25cd534 100644 --- a/treasureStats.ts +++ b/treasureStats.ts @@ -27,43 +27,6 @@ namespace TreasureStats { return currentTreasure.onBoat + currentTreasure.onIsland + currentTreasure.inPocket } - // TODO Find the time.... - // export function updateTreasure({ onBoat, onIsland, inPocket, pulledFromIsland }: OnUpdateTreasureProps) { - // if (pulledFromIsland != null) { - // // Find the island - // const island = Map.islands.find(i => { - // return i.id === pulledFromIsland - // }) - - // if (island) { - // currentTreasure.onBoat += island.riches - // // Also add any inPocket coin (a little too much logic here but oh well) - // currentTreasure.onBoat += currentTreasure.inPocket - // currentTreasure.inPocket = 0 - // island.riches = 0 - // } - // } else { - // // If any of the values are actually 0, then we set it to 0 - // // otherwise we increment (kind of strange? but I'm lazy and tired) - // if (onBoat === 0) { - // currentTreasure.onBoat = 0 - // } - // if (onIsland === 0) { - // currentTreasure.onIsland = 0 - // } - // if (inPocket === 0) { - // currentTreasure.inPocket = 0 - // } - - // // Now increment - // currentTreasure.onBoat = currentTreasure.onBoat + (onBoat ? onBoat : 0) - // currentTreasure.onIsland = currentTreasure.onIsland + (onIsland ? onIsland : 0) - // currentTreasure.inPocket = currentTreasure.inPocket + (inPocket ? inPocket : 0) - // } - - // show() - // } - export function show({ combination, location }: { combination?: Array<'island' | 'boat' | 'pocket'>, location?: 'left' | 'center' }) { currentDisplayCombo = combination ? combination : currentDisplayCombo currentPosition = location ? location : currentPosition diff --git a/utils.ts b/utils.ts index 01d5d1c..56afb5e 100644 --- a/utils.ts +++ b/utils.ts @@ -27,7 +27,7 @@ namespace Utils { return anim } - export function getHitEnemies({ pirate, enemies }: { pirate: Pirate, enemies: Militia[] }) { + export function getHitEnemies({ pirate, enemies }: { pirate: Pirate, enemies: Enemy[] }) { const dirPix = pirate.direction === 'left' ? -1 : 1 // The hit zone is the pirate "sword" box: [center, right|left] and [top, bottom] const hitXZone = [pirate.sprite.x, pirate.sprite.x + (13 * dirPix)] From 12c751b2e7fd3f015ecc0202150236402fb7f475 Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 15:25:29 -0500 Subject: [PATCH 5/7] Fixed some crazy bugs with shooting and moving --- enemy.ts | 4 ++++ militia.ts | 21 +++++++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/enemy.ts b/enemy.ts index 766170e..7471de2 100644 --- a/enemy.ts +++ b/enemy.ts @@ -52,6 +52,7 @@ class Enemy { protected attack() { // Stop moving + console.log("Should stop!") this.sprite.follow(this._currentTarget.sprite, 0) this._isAttacking = true } @@ -59,6 +60,9 @@ class Enemy { protected die() {} public render() { + // No Undead walking! + if (this.health <= 0 && !this._isAttacking) return + // Check your distance from the target randomly if ((control.millis() - this._lastDirectionTick) > Militia.directionChangeInterval) { this._lastDirectionTick = control.millis() diff --git a/militia.ts b/militia.ts index d826bee..4bf7821 100644 --- a/militia.ts +++ b/militia.ts @@ -11,9 +11,6 @@ class Militia extends Enemy { static attackDelayMin: number = 4000 static attackDelayMax: number = 6000 - private facing: 'left' | 'right' = 'right' - // private _isParrying: boolean = false - constructor({ x, y, target }: { x: number, y: number, target?: Pirate }) { super({ x, y, target, sprite: sprites.create(assets.animation`Militia Walk`[0]) }) @@ -22,26 +19,22 @@ class Militia extends Enemy { } public hit(damage: number) { - // if (this._isParrying) { - // music.play(Militia.parrySound, music.PlaybackMode.InBackground) - // return - // } - super.hit(damage) if (this.health <= 0) { animation.runImageAnimation( this.sprite, - this.facing === 'right' ? Militia.deathRightAnimation : Militia.deathLeftAnimation, + this._facing === 'right' ? Militia.deathRightAnimation : Militia.deathLeftAnimation, 100, false ) } } - public render() { - if (this.health <= 0) return - + public render() { + // No Undead walking! + if (this.health <= 0 && !this._isAttacking) return + super.render() // Attack randomly @@ -65,7 +58,7 @@ class Militia extends Enemy { const oldY = this.sprite.y this.sprite.destroy() - if (this.facing === 'left') { + if (this._facing === 'left') { this.sprite = sprites.create(assets.image`Militia Broken and Broke Left`) } else { this.sprite = sprites.create(assets.image`Militia Broken and Broke`) @@ -100,7 +93,7 @@ class Militia extends Enemy { super.attack() // Play the fire animation - if (this.facing === 'right') { + if (this._facing === 'right') { animation.runImageAnimation( this.sprite, Militia.attackRightAnimation, From 836bea6d79a1167d14d84d9723c6386eaf8fdcab Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 15:32:33 -0500 Subject: [PATCH 6/7] Riches! --- enemy.ts | 4 +++- images.g.jres | 10 +++++----- images.g.ts | 22 +++++++++++----------- island.ts | 2 +- militia.ts | 4 ++-- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/enemy.ts b/enemy.ts index 7471de2..7d64074 100644 --- a/enemy.ts +++ b/enemy.ts @@ -12,11 +12,13 @@ class Enemy { protected _isAttacking: boolean = false protected _lastDirectionTick: number = 0 - constructor({ x, y, target, sprite }: { x: number, y: number, target: Pirate, sprite: Sprite }) { + constructor({ x, y, target, sprite, riches }: { x: number, y: number, target: Pirate, sprite: Sprite, riches?: number }) { this.sprite = sprite this.sprite.x = x this.sprite.y = y + this.riches = riches != null ? riches : 1 + // On initial spawn they are quick to attack! this._nextAttackTime = Math.randomRange(Militia.attackDelayMin / 2, Militia.attackDelayMax / 2) this._lastAttackTick = control.millis() diff --git a/images.g.jres b/images.g.jres index 50668c4..2b98387 100644 --- a/images.g.jres +++ b/images.g.jres @@ -44,11 +44,6 @@ "mimeType": "image/x-mkcd-f4", "displayName": "Boat Icon" }, - "image11": { - "data": "hwQIAAgAAAAAAAAA4ADu7u7d7e7u3e3u7t3u7uDd7u7gAO4OAAAAAA==", - "mimeType": "image/x-mkcd-f4", - "displayName": "Pocket Icon" - }, "image14": { "data": "hwQIAAgAAAAAVUUAUFVVBFBVVQRQVVUEUFVVBFBVVQRQVVUEAFVFAA==", "mimeType": "image/x-mkcd-f4", @@ -84,6 +79,11 @@ "mimeType": "image/x-mkcd-f4", "displayName": "Militia Broken and Broke Left" }, + "image11": { + "data": "hwQIAAgAAAAAAAAAAFAFAABRVQAQVVUEUFVVBABVRQAAQAQAAAAAAA==", + "mimeType": "image/x-mkcd-f4", + "displayName": "Pocket Icon" + }, "anim14": { "namespace": "myAnimations", "id": "anim14", diff --git a/images.g.ts b/images.g.ts index 4c15562..c54edda 100644 --- a/images.g.ts +++ b/images.g.ts @@ -296,17 +296,6 @@ e e e e e e e e . e e e e e e e . . e e e e e e . . . . . . . . -`; - case "image11": - case "Pocket Icon":return img` -. . e e e . . . -. e e e e e e . -. . d d d d . . -. . d d d d . . -. e d d e e e . -. e e e e e e . -. e e e e e e . -. e e e e e . . `; case "image14": case "Coin":return img` @@ -514,6 +503,17 @@ d d a a a c c d d . . . . . . . . . . f e e a a d d d d a . . f . . . f e e c c a a a a c d d f . . . f e e c a a a a a a d d f +`; + case "image11": + case "Pocket Icon":return img` +. . . . . . . . +. . . 1 5 . . . +. . 1 5 5 5 . . +. 5 5 5 5 5 4 . +. 5 5 5 5 5 4 . +. . 5 5 5 4 . . +. . . 4 4 . . . +. . . . . . . . `; } return null; diff --git a/island.ts b/island.ts index 6d0d298..3ce8019 100644 --- a/island.ts +++ b/island.ts @@ -209,7 +209,7 @@ namespace Island { if (player2.health > 0) livingPirates.push(player2) const randomTarget = Math.pickRandom(livingPirates) - currentEnemies.push(new Militia({ x: locX, y: locY, target: randomTarget })) + currentEnemies.push(new Militia({ x: locX, y: locY, target: randomTarget, riches: 1 + currentSegment })) }) Utils.getArrayOfLength(Math.randomRange(1, 3)).forEach(() => { diff --git a/militia.ts b/militia.ts index 4bf7821..6c92440 100644 --- a/militia.ts +++ b/militia.ts @@ -11,8 +11,8 @@ class Militia extends Enemy { static attackDelayMin: number = 4000 static attackDelayMax: number = 6000 - constructor({ x, y, target }: { x: number, y: number, target?: Pirate }) { - super({ x, y, target, sprite: sprites.create(assets.animation`Militia Walk`[0]) }) + constructor({ x, y, target, riches }: { x: number, y: number, target?: Pirate, riches?: number }) { + super({ x, y, target, sprite: sprites.create(assets.animation`Militia Walk`[0]), riches }) // Most often we spawn to the right, so walk left this.walk('left') From 8fa9f5b3db709a6601dd31bbcb3d945ef574ea41 Mon Sep 17 00:00:00 2001 From: Chris Weed Date: Tue, 16 Jul 2024 15:48:42 -0500 Subject: [PATCH 7/7] Remove version --- menu.ts | 2 ++ pirateLives.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/menu.ts b/menu.ts index fba8022..6b17a78 100644 --- a/menu.ts +++ b/menu.ts @@ -10,6 +10,8 @@ namespace Menu { music.stopAllSounds() + _versionSprite.destroy() + _callback() } diff --git a/pirateLives.ts b/pirateLives.ts index b0b1173..9e8dd79 100644 --- a/pirateLives.ts +++ b/pirateLives.ts @@ -17,10 +17,12 @@ namespace PirateLives { _liveCountIcon = sprites.create(assets.image`Pirate Lives`) _liveCountIcon.x = 5 _liveCountIcon.y = 115 + _liveCountIcon.z = 120 _liveCountSprite = textsprite.create(currentPirateCount + '', 0, 1) _liveCountSprite.x = 8 + 5 + (_liveCountSprite.width / 2) _liveCountSprite.y = 115 + _liveCountSprite.z = 120 } export function hide() {