Skip to content

Commit

Permalink
Add missiles and launchers
Browse files Browse the repository at this point in the history
  • Loading branch information
twinprime committed Dec 30, 2020
1 parent 1e9855c commit 437315a
Show file tree
Hide file tree
Showing 27 changed files with 374 additions and 129 deletions.
6 changes: 5 additions & 1 deletion src/BlueForceControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,12 @@ export default class BlueForceControl extends ForceControl {
nextPos += 64 + 15

this.liftableBodies = this.scene.physics.add.group()

nextPos += 100
this.buildMissileBase(nextPos + 16, this.liftableBodies)
nextPos += 32 + 15

nextPos += 50
this.buildAAGun(nextPos + 16, this.liftableBodies)
nextPos += 32 + 15

Expand Down
29 changes: 29 additions & 0 deletions src/ForceControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import SoldierGameObject from "./mobile-objects/SoldierGameObject"
import TankGameObject from "./mobile-objects/TankGameObject"
import AAGunGameObject from "./static-objects/AAGunGameObject"
import BunkerGameObject from "./static-objects/BunkerGameObject"
import MissileLauncherGameObject from "./mobile-objects/MissileLauncherGameObject"
import MissileBaseGameObject from "./static-objects/MissileBaseGameObject"

export default abstract class ForceControl {
protected abstract factory: FactoryGameObject
Expand All @@ -14,13 +16,17 @@ export default abstract class ForceControl {
protected tankObjects = new Set<TankGameObject>()
protected soliderObjects = new Set<SoldierGameObject>()
protected aaGunObjects = new Set<AAGunGameObject>()
protected missileLauncherObjects = new Set<MissileLauncherGameObject>()
protected missileBaseObjects = new Set<MissileBaseGameObject>()
protected bunkerObjects = new Set<BunkerGameObject>()

constructor(protected readonly scene: GameScene, protected readonly owner: number) {}

update(time: number, delta: number): void {
this.soliderObjects.forEach(soldier => soldier.update(time, delta))
this.tankObjects.forEach(tank => tank.update(time, delta))
this.missileLauncherObjects.forEach(m => m.update(time, delta))
this.missileBaseObjects.forEach(m => m.update(time, delta))
this.aaGunObjects.forEach(gun => gun.update(time, delta))
this.bunkerObjects.forEach(b => b.update(time, delta))
}
Expand All @@ -46,6 +52,29 @@ export default abstract class ForceControl {
}
}

protected buildMissileLauncher(): void {
const obj = new MissileLauncherGameObject(this.scene, this.owner, this.factory.spawnX, 50)
obj.showHealthBar(50)
obj.move(50 * this.owner, this.owner < 0)
this.missileLauncherObjects.add(obj)
this.scene.gameMap.add(obj)
obj.destroyCallback = () => {
this.missileLauncherObjects.delete(obj)
this.scene.gameMap.remove(obj)
}
}

protected buildMissileBase(x: number, group: Phaser.Physics.Arcade.Group): void {
const obj = new MissileBaseGameObject(this.scene, this.owner, x, group)
obj.showHealthBar(50)
this.missileBaseObjects.add(obj)
this.scene.gameMap.add(obj)
obj.destroyCallback = () => {
this.missileBaseObjects.delete(obj)
this.scene.gameMap.remove(obj)
}
}

protected buildTank(): void {
const obj = new TankGameObject(this.scene, this.owner, this.factory.spawnX, 50)
obj.showHealthBar(50)
Expand Down
24 changes: 24 additions & 0 deletions src/GameMap.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import GameObject from "./GameObject"
import ChopperGameObject from "./mobile-objects/ChopperGameObject"

export default class GameMap {
private static readonly updateInterval = 32

private readonly cellCount: number
private lastUpdate = 0
private allGameObjects = new Map<number, Set<GameObject>>()
private gameObjects = new Map<number, Set<GameObject>[]>()
private gameObjectCells = new Map<GameObject, number[]>()

Expand All @@ -18,6 +20,8 @@ export default class GameMap {
}
this.gameObjects.set(-1, redCells)
this.gameObjects.set(1, blueCells)
this.allGameObjects.set(-1, new Set())
this.allGameObjects.set(1, new Set())
}

add(obj: GameObject): void {
Expand All @@ -26,6 +30,7 @@ export default class GameMap {
const cells = [this.getCell(obj.x1), this.getCell(obj.x2)]
for (let i = cells[0]; i <= cells[1]; i++) objs[i].add(obj)
this.gameObjectCells.set(obj, cells)
this.allGameObjects.get(obj.owner).add(obj)
}
}

Expand All @@ -34,9 +39,28 @@ export default class GameMap {
if (objs) {
const cells = this.gameObjectCells.get(obj)
for (let i = cells[0]; i <= cells[1]; i++) objs[i].delete(obj)
this.allGameObjects.get(obj.owner).delete(obj)
}
}

findObject(owner: number, predicate: (obj: GameObject) => boolean): GameObject {
const objs = this.allGameObjects.get(owner).values()
let obj = objs.next()
while (!obj.done) {
if (predicate(obj.value)) return obj.value as GameObject
obj = objs.next()
}
return undefined
}

findChopper(subject: GameObject, distanceSq: number): ChopperGameObject {
return this.findObject(-subject.owner, (obj) => {
if (obj instanceof ChopperGameObject) {
return Phaser.Math.Distance.BetweenPointsSquared(subject, obj) < distanceSq
} else return false
}) as ChopperGameObject
}

getObjectsWithin(owner: number, x1: number, x2: number,
filter?: (obj: GameObject) => boolean): Set<GameObject> {
const c1 = this.getCell(x1)
Expand Down
25 changes: 21 additions & 4 deletions src/GameObject.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Behaviour from "./Behaviour"
import GameScene from "./GameScene"
import { BulletGameObject, BulletType } from "./mobile-objects/BulletGameObject"
import { MissileGameObject } from "./mobile-objects/MissileGameObject"
import HealthBarGraphics from "./ui-objects/HealthBarGraphics"

interface WrappedPhaserGameObject {
Expand All @@ -11,6 +12,8 @@ export default abstract class GameObject {
protected _destroyCallback: () => void
set destroyCallback(cb: () => void) { this._destroyCallback = cb }

abstract readonly x: number
abstract readonly y: number
abstract readonly x1: number
abstract readonly x2: number
abstract readonly y1: number
Expand Down Expand Up @@ -39,13 +42,15 @@ export default abstract class GameObject {

// eslint-disable-next-line @typescript-eslint/no-unused-vars
update(time: number, delta: number): void {
this.healthBar?.draw(this.x1 + (this.width - this.healthBar.width) / 2, this.y1 - 5, this._health)
this.healthBar?.draw(this.x - this.healthBar.width / 2, this.y1 - 5, this._health)
if (this.behaviourActive) this.behaviour?.update(time)
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
moveTo(x: number, y: number): void { throw "object cannot be moved" }

abstract setVisible(visible: boolean): void

abstract remove(): void

removed(): void {
Expand Down Expand Up @@ -73,14 +78,26 @@ export default abstract class GameObject {
addBulletResponse(subject: Phaser.GameObjects.GameObject,
damageMap: Map<BulletType, number>,
defaultDamage = 0): void {
this.scene.physics.add.overlap(subject, this.scene.getBulletBodies(this.owner * -1), (me, bullet) => {
this.scene.physics.add.overlap(subject, this.scene.getBulletBodies(this.owner * -1), (me, b) => {
if (!this.dying) {
const bulletType = (this.getWrapper(bullet) as BulletGameObject).bulletType
this._health -= damageMap.get(bulletType) ?? defaultDamage
const bullet = this.getWrapper(b) as BulletGameObject
this._health -= damageMap.get(bullet.bulletType) ?? defaultDamage
this.healthCallback?.(this._health)
this.scene.removeBullet(bullet)
if (this._health <= 0) this.die()
}
})
}

addMissileResponse(subject: Phaser.GameObjects.GameObject, damage: number): void {
this.scene.physics.add.overlap(subject, this.scene.getMissileBodies(this.owner * -1), (me, m) => {
if (!this.dying) {
const missile = this.getWrapper(m) as MissileGameObject
this._health -= damage
this.healthCallback?.(this._health)
this.scene.removeMissile(missile)
if (this._health <= 0) this.die()
}
})
}
}
35 changes: 32 additions & 3 deletions src/GameScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ import TreeGameObject from './static-objects/TreeGameObject'
import VillageGameObject from './static-objects/VillageGameObject'
import GameMap from './GameMap'
import BunkerGameObject from './static-objects/BunkerGameObject'
import { MissileGameObject } from './mobile-objects/MissileGameObject'
import GameObject from './GameObject'
import MissileLauncherGameObject from './mobile-objects/MissileLauncherGameObject'
import MissileBaseGameObject from './static-objects/MissileBaseGameObject'

export default class GameScene extends Phaser.Scene {
private frameTime = 0
private treeObjects: TreeGameObject[] = []
private bulletObjects = new Map<Phaser.GameObjects.GameObject, BulletGameObject>()
private bulletBodies = new Map<number, Phaser.Physics.Arcade.Group>()
private missileObjects = new Map<Phaser.GameObjects.GameObject, MissileGameObject>()
private missileBodies = new Map<number, Phaser.Physics.Arcade.Group>()
private blueControl: BlueForceControl
private redControl: RedForceControl

Expand Down Expand Up @@ -59,6 +65,7 @@ export default class GameScene extends Phaser.Scene {
HomeBuildingGameObject.preload(this)
VillageGameObject.preload(this)
BulletGameObject.preload(this)
MissileGameObject.preload(this)
ChopperGameObject.preload(this)
AAGunGameObject.preload(this)
SoldierGameObject.preload(this)
Expand All @@ -68,6 +75,8 @@ export default class GameScene extends Phaser.Scene {
BarrackGameObject.preload(this)
BunkerGameObject.preload(this)
TankGameObject.preload(this)
MissileLauncherGameObject.preload(this)
MissileBaseGameObject.preload(this)
}

private createAnims(): void {
Expand Down Expand Up @@ -103,6 +112,8 @@ export default class GameScene extends Phaser.Scene {

this.bulletBodies.set(-1, this.physics.add.group())
this.bulletBodies.set(1, this.physics.add.group())
this.missileBodies.set(-1, this.physics.add.group())
this.missileBodies.set(1, this.physics.add.group())

HelipadGameObject.createCommon(this)
SoldierGameObject.createCommon(this)
Expand All @@ -114,6 +125,8 @@ export default class GameScene extends Phaser.Scene {
this.cameras.main.startFollow(this.blueControl.chopper.sprite)
}

getBulletBodies(owner: number): Phaser.Physics.Arcade.Group { return this.bulletBodies.get(owner) }

createBullet(owner: number, duration: number,
x: number, y: number, velocityX: number, velocityY: number,
type: BulletType): BulletGameObject {
Expand All @@ -124,11 +137,26 @@ export default class GameScene extends Phaser.Scene {
return bullet
}

removeBullet(b: Phaser.GameObjects.GameObject): void {
this.bulletObjects.get(b)?.remove()
removeBullet(b: BulletGameObject): void {
this.bulletObjects.get(b.sprite)?.remove()
}

getBulletBodies(owner: number): Phaser.Physics.Arcade.Group { return this.bulletBodies.get(owner) }
removeMissile(m: MissileGameObject): void {
m.die()
}

getMissileBodies(owner: number): Phaser.Physics.Arcade.Group { return this.missileBodies.get(owner) }

createMissile(owner: number,
duration: number, x: number, y: number,
velocityX: number, velocityY: number,
maxAcceleration: number, target: GameObject): MissileGameObject {
const missile = new MissileGameObject(this, owner, this.missileBodies.get(owner), duration,
x, y, velocityX, velocityY, maxAcceleration, target)
this.missileObjects.set(missile.sprite, missile)
missile.destroyCallback = () => this.missileObjects.delete(missile.sprite)
return missile
}

update(time: number, delta: number): void {
this.frameTime += delta
Expand All @@ -139,6 +167,7 @@ export default class GameScene extends Phaser.Scene {
this.redControl.update(time, delta)
this.treeObjects.forEach(tree => tree.update())
this.bulletObjects.forEach(bullet => bullet.update(time, delta))
this.missileObjects.forEach(m => m.update(time, delta))
}
}

Expand Down
8 changes: 3 additions & 5 deletions src/PhysicsBodyGameObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,22 @@ export default abstract class PhysicsBodyGameObject extends GameObject {
this._mainBody = b
}

get x(): number { return this._mainBody.x + this._mainBody.width / 2 }
get y(): number { return this._mainBody.y + this._mainBody.height / 2 }
get x1(): number { return this._mainBody.x }
get x2(): number { return this._mainBody.x + this._mainBody.width }
get y1(): number { return this._mainBody.y }
get y2(): number { return this._mainBody.y + this._mainBody.height }
get width(): number { return this._mainBody.width }
get height(): number { return this._mainBody.height }

protected abstract setVisible(visible: boolean): void

die(): void {
super.beforeDie()

this.setVisible(false)
this._mainBody.setVelocityX(0)
this._mainBody.setEnable(false)
const explosion = this.scene.add.sprite(this._mainBody.x + this._mainBody.width / 2,
this._mainBody.y + this._mainBody.height / 2,
"explode", 0)
const explosion = this.scene.add.sprite(this.x, this.y, "explode", 0)
explosion.setDepth(1)
explosion.anims.play("explode")
explosion.once("animationcomplete", () => {
Expand Down
8 changes: 6 additions & 2 deletions src/RedForceControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,18 @@ export default class RedForceControl extends ForceControl {

this.barrack = new BarrackGameObject(scene, -1, nextPos - 32)
nextPos -= 64 + 15

nextPos -= 100
const gunBodies = scene.physics.add.group()
this.buildMissileBase(nextPos - 16, gunBodies)
nextPos -= 32 + 15

nextPos -= 50
this.buildAAGun(nextPos - 16, gunBodies)
nextPos -= 32 + 15

setTimeout(() => this.buildSoldier(), 2000)
setTimeout(() => this.buildTank(), 5000)
setTimeout(() => this.buildMissileLauncher(), 5000)
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
8 changes: 8 additions & 0 deletions src/SpriteGameObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export default class SpriteGameObject extends GameObject {
this.halfHeight = s.height / 2
}

get sprite(): Phaser.GameObjects.Sprite { return this._mainSprite }

get x(): number { return this._mainSprite.x }
get y(): number { return this._mainSprite.y }
get x1(): number { return this._mainSprite.x - this.halfWidth }
get x2(): number { return this._mainSprite.x + this.halfWidth }
get y1(): number { return this._mainSprite.y - this.halfHeight }
Expand All @@ -24,6 +28,10 @@ export default class SpriteGameObject extends GameObject {
this.mainSprite.setY(Math.min(this.scene.groundPos - this.halfHeight, y))
}

setVisible(visible: boolean): void {
this.mainSprite.setVisible(visible)
}

remove(): void {
this._mainSprite.destroy()
this.removed()
Expand Down
14 changes: 7 additions & 7 deletions src/behaviours/ScanStopShootBehaviour.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import GameObject from "../GameObject"

export interface ScanStopShootBehaviourCallback {
findTarget(): boolean
findTarget(): Set<GameObject>
isMoving(): boolean
stop(): void
move(): void
createBullet(): void
createBullet(targets: Set<GameObject>): void
}

export class ScanStopShootBehaviour {
private lastFired = 0
private lastScan = 0
private haveTarget = false
private targets = new Set<GameObject>()

constructor(private readonly subject: GameObject, private readonly scanInterval: number, private readonly fireInterval: number,
private readonly callback: ScanStopShootBehaviourCallback) {}
Expand All @@ -20,16 +20,16 @@ export class ScanStopShootBehaviour {
if (this.subject.dying != true) {
if ((time - this.lastScan) >= this.scanInterval) {
this.lastScan = time
this.haveTarget = this.callback.findTarget()
if (this.haveTarget) {
this.targets = this.callback.findTarget()
if (this.targets.size > 0) {
this.callback.stop()
} else if (!this.callback.isMoving()) {
this.callback.move()
}
}
if (this.haveTarget && (time - this.lastFired) > this.fireInterval) {
if (this.targets.size > 0 && (this.lastFired == 0 || (time - this.lastFired) > this.fireInterval)) {
this.lastFired = time
this.callback.createBullet()
this.callback.createBullet(this.targets)
}
}
}
Expand Down
Loading

0 comments on commit 437315a

Please sign in to comment.