mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-02 15:38:22 +00:00
nukes spawn from missile silos
This commit is contained in:
@@ -184,6 +184,7 @@
|
||||
* trade ship gives gold when completes route DONE 11/15/2024
|
||||
* add missile silo DONE 11/15/2024
|
||||
* nuke spawns from missile silo
|
||||
* BUG: can build destroyer on land
|
||||
* destroyer can capture trade ships
|
||||
* add battleship
|
||||
* add defense post
|
||||
|
||||
@@ -80,6 +80,8 @@ export class UnitLayer implements Layer {
|
||||
break;
|
||||
case UnitType.TradeShip:
|
||||
this.handleTradeShipEvent(event)
|
||||
case UnitType.Nuke:
|
||||
this.handleNuke(event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +95,17 @@ export class UnitLayer implements Layer {
|
||||
.forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(event.unit.owner().info()), 180));
|
||||
}
|
||||
|
||||
private handleNuke(event: UnitEvent) {
|
||||
bfs(event.oldTile, euclDist(event.oldTile, 2)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
});
|
||||
if (event.unit.isActive()) {
|
||||
bfs(event.unit.tile(), euclDist(event.unit.tile(), 2))
|
||||
.forEach(t => this.paintCell(t.cell(), this.theme.borderColor(event.unit.owner().info()), 255));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private handleTradeShipEvent(event: UnitEvent) {
|
||||
bfs(event.oldTile, euclDist(event.oldTile, 1)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
@@ -130,6 +143,7 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
paintCell(cell: Cell, color: Colord, alpha: number) {
|
||||
const index = (cell.y * this.game.width()) + cell.x;
|
||||
const offset = index * 4;
|
||||
|
||||
@@ -12,7 +12,11 @@ export class AStar {
|
||||
private meetingPoint: Tile | null;
|
||||
public completed: boolean;
|
||||
|
||||
constructor(private src: Tile, private dst: Tile) {
|
||||
constructor(
|
||||
private src: Tile,
|
||||
private dst: Tile,
|
||||
private canMove: (t: Tile) => boolean
|
||||
) {
|
||||
this.fwdOpenSet = new PriorityQueue<{ tile: Tile; fScore: number; }>(
|
||||
(a, b) => a.fScore - b.fScore
|
||||
);
|
||||
@@ -70,7 +74,7 @@ export class AStar {
|
||||
|
||||
private expandNode(current: Tile, isForward: boolean) {
|
||||
for (const neighbor of current.neighborsWrapped()) {
|
||||
if (neighbor !== (isForward ? this.dst : this.src) && neighbor.isLand()) continue;
|
||||
if (neighbor !== (isForward ? this.dst : this.src) && !this.canMove(neighbor)) continue;
|
||||
|
||||
const gScore = isForward ? this.fwdGScore : this.bwdGScore;
|
||||
const openSet = isForward ? this.fwdOpenSet : this.bwdOpenSet;
|
||||
@@ -128,7 +132,7 @@ export class PathFinder {
|
||||
private aStar: AStar
|
||||
private inProgress = false
|
||||
|
||||
constructor(private iterations: number) {
|
||||
constructor(private iterations: number, private canMove: (t: Tile) => boolean) {
|
||||
|
||||
}
|
||||
|
||||
@@ -145,7 +149,7 @@ export class PathFinder {
|
||||
this.curr = curr
|
||||
this.dst = dst
|
||||
this.path = null
|
||||
this.aStar = new AStar(curr, dst)
|
||||
this.aStar = new AStar(curr, dst, this.canMove)
|
||||
if (this.aStar.compute(this.iterations)) {
|
||||
this.inProgress = false
|
||||
this.path = this.aStar.reconstructPath()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, Tile, UnitType } from "../game/Game";
|
||||
import { BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, Tile, UnitType } from "../game/Game";
|
||||
import { AStar, PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { distSort, distSortUnit, manhattanDist } from "../Util";
|
||||
@@ -12,7 +12,7 @@ export class DestroyerExecution implements Execution {
|
||||
private mg: MutableGame = null
|
||||
|
||||
private target: MutableUnit = null
|
||||
private pathfinder = new PathFinder(5000)
|
||||
private pathfinder = new PathFinder(5000, t => t.isWater())
|
||||
|
||||
private patrolTile: Tile;
|
||||
private patrolCenterTile: Tile
|
||||
@@ -37,6 +37,7 @@ export class DestroyerExecution implements Execution {
|
||||
tick(ticks: number): void {
|
||||
// TODO: remove gold from player
|
||||
if (this.destroyer == null) {
|
||||
// TODO validate can build
|
||||
const spawns = this._owner.units(UnitType.Port).map(u => u.tile()).sort(distSort(this.patrolTile))
|
||||
if (spawns.length == 0) {
|
||||
console.warn(`no ports found for destoryer for player ${this._owner}`)
|
||||
@@ -44,6 +45,7 @@ export class DestroyerExecution implements Execution {
|
||||
return
|
||||
}
|
||||
this.destroyer = this._owner.addUnit(UnitType.Destroyer, 0, spawns[0])
|
||||
this._owner.removeGold(BuildItems.Destroyer.cost)
|
||||
return
|
||||
}
|
||||
if (!this.destroyer.isActive()) {
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { Cell, Execution, BuildItems, MutableGame, MutablePlayer, PlayerID, Tile } from "../game/Game";
|
||||
import { BuildValidator } from "../game/BuildValidator";
|
||||
import { Cell, Execution, BuildItems, MutableGame, MutablePlayer, PlayerID, Tile, MutableUnit, UnitType } from "../game/Game";
|
||||
import { PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { bfs, dist, euclideanDist, manhattanDist } from "../Util";
|
||||
import { bfs, dist, distSortUnit, euclideanDist, manhattanDist } from "../Util";
|
||||
|
||||
export class NukeExecution implements Execution {
|
||||
|
||||
private sender: MutablePlayer
|
||||
private player: MutablePlayer
|
||||
|
||||
private active = true
|
||||
|
||||
private mg: MutableGame
|
||||
|
||||
private nuke: MutableUnit
|
||||
private dst: Tile
|
||||
|
||||
private pathFinder: PathFinder = null
|
||||
|
||||
constructor(
|
||||
private senderID: PlayerID,
|
||||
private cell: Cell,
|
||||
@@ -19,20 +26,41 @@ export class NukeExecution implements Execution {
|
||||
|
||||
init(mg: MutableGame, ticks: number): void {
|
||||
this.mg = mg
|
||||
this.sender = mg.player(this.senderID)
|
||||
this.player = mg.player(this.senderID)
|
||||
if (this.magnitude == null) {
|
||||
this.magnitude = 50
|
||||
}
|
||||
this.dst = this.mg.tile(this.cell)
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.sender.gold() < BuildItems.Nuke.cost) {
|
||||
console.warn(`player ${this.sender} insufficient gold for nuke`)
|
||||
this.active = false
|
||||
if (this.nuke == null) {
|
||||
if (new BuildValidator(this.mg).canBuild(this.player, this.dst, BuildItems.Nuke)) {
|
||||
const spawn = this.player.units(UnitType.MissileSilo)
|
||||
.sort((a, b) => manhattanDist(a.tile().cell(), this.cell) - manhattanDist(b.tile().cell(), this.cell))[0]
|
||||
this.nuke = this.player.addUnit(UnitType.Nuke, 0, spawn.tile())
|
||||
this.player.removeGold(BuildItems.Nuke.cost)
|
||||
this.pathFinder = new PathFinder(10_000, t => true)
|
||||
} else {
|
||||
console.warn(`cannot build Nuke`)
|
||||
this.active = false
|
||||
return
|
||||
}
|
||||
}
|
||||
if (this.nuke.tile() == this.dst) {
|
||||
this.detonate()
|
||||
return
|
||||
}
|
||||
this.sender.removeGold(BuildItems.Nuke.cost)
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const nextTile = this.pathFinder.nextTile(this.nuke.tile(), this.dst)
|
||||
if (nextTile == null) {
|
||||
return
|
||||
}
|
||||
this.nuke.move(nextTile)
|
||||
}
|
||||
}
|
||||
|
||||
private detonate() {
|
||||
const rand = new PseudoRandom(this.mg.ticks())
|
||||
const tile = this.mg.tile(this.cell)
|
||||
const toDestroy = bfs(tile, (n: Tile) => {
|
||||
@@ -52,6 +80,7 @@ export class NukeExecution implements Execution {
|
||||
.filter(b => euclideanDist(this.cell, b.tile().cell()) < this.magnitude + 50)
|
||||
.forEach(b => b.delete())
|
||||
this.active = false
|
||||
this.nuke.delete()
|
||||
}
|
||||
|
||||
owner(): MutablePlayer {
|
||||
|
||||
@@ -45,6 +45,7 @@ export class PortExecution implements Execution {
|
||||
return
|
||||
}
|
||||
this.port = this.player.addUnit(UnitType.Port, 0, spawns[0])
|
||||
this.player.removeGold(BuildItems.Port.cost)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +74,7 @@ export class PortExecution implements Execution {
|
||||
continue
|
||||
}
|
||||
if (!this.portPaths.has(port)) {
|
||||
this.computingPaths.set(port, new AStar(this.port.tile(), port.tile()))
|
||||
this.computingPaths.set(port, new AStar(this.port.tile(), port.tile(), t => t.isWater()))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class TransportShipExecution implements Execution {
|
||||
|
||||
private boat: MutableUnit
|
||||
|
||||
private pathFinder: PathFinder = new PathFinder(10_000)
|
||||
private pathFinder: PathFinder = new PathFinder(10_000, t => t.isWater())
|
||||
|
||||
constructor(
|
||||
private attackerID: PlayerID,
|
||||
|
||||
@@ -10,7 +10,7 @@ export class BuildValidator {
|
||||
}
|
||||
switch (item) {
|
||||
case BuildItems.Nuke:
|
||||
return true
|
||||
return player.units(UnitType.MissileSilo).length > 0
|
||||
case BuildItems.Port:
|
||||
return this.canBuildPort(player, tile)
|
||||
case BuildItems.Destroyer:
|
||||
|
||||
@@ -39,11 +39,10 @@ export class BuildItem {
|
||||
}
|
||||
|
||||
export const BuildItems = {
|
||||
// Nuke: new BuildItem(UnitType.Nuke, 1_000_000),
|
||||
Nuke: new BuildItem(UnitType.Nuke, 10),
|
||||
Destroyer: new BuildItem(UnitType.Destroyer, 10),
|
||||
Port: new BuildItem(UnitType.Port, 0),
|
||||
MissileSilo: new BuildItem(UnitType.MissileSilo, 10),
|
||||
Nuke: new BuildItem(UnitType.Nuke, 1_000_000),
|
||||
Destroyer: new BuildItem(UnitType.Destroyer, 100_000),
|
||||
Port: new BuildItem(UnitType.Port, 300_000),
|
||||
MissileSilo: new BuildItem(UnitType.MissileSilo, 1_000_000),
|
||||
} as const;
|
||||
|
||||
export class Nation {
|
||||
@@ -156,6 +155,8 @@ export interface Tile {
|
||||
neighbors(): Tile[]
|
||||
neighborsWrapped(): Tile[]
|
||||
onShore(): boolean
|
||||
x(): number
|
||||
y(): number
|
||||
}
|
||||
|
||||
export interface Unit {
|
||||
@@ -252,6 +253,7 @@ export interface MutablePlayer extends Player {
|
||||
addTroops(troops: number): void
|
||||
removeTroops(troops: number): number
|
||||
|
||||
// TODO: make addUnit require gold
|
||||
addUnit(type: UnitType, troops: number, tile: Tile): MutableUnit
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,12 @@ export class TileImpl implements Tile {
|
||||
isBorder(): boolean { return this._isBorder; }
|
||||
isInterior(): boolean { return this.hasOwner() && !this.isBorder(); }
|
||||
cell(): Cell { return this._cell; }
|
||||
x(): number {
|
||||
return this._cell.x
|
||||
}
|
||||
y(): number {
|
||||
return this._cell.y
|
||||
}
|
||||
|
||||
neighbors(): Tile[] {
|
||||
if (this._neighbors == null) {
|
||||
|
||||
Reference in New Issue
Block a user