mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-24 10:25:06 +00:00
Remove duplicated code
This commit is contained in:
@@ -6,6 +6,7 @@ import { UnitType } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { BezenhamLine } from "../../../core/utilities/Line";
|
||||
import {
|
||||
AlternateViewEvent,
|
||||
MouseUpEvent,
|
||||
@@ -31,9 +32,9 @@ export class UnitLayer implements Layer {
|
||||
private canvas: HTMLCanvasElement;
|
||||
private context: CanvasRenderingContext2D;
|
||||
private transportShipTrailCanvas: HTMLCanvasElement;
|
||||
private transportShipTrailContext: CanvasRenderingContext2D;
|
||||
private unitTrailContext: CanvasRenderingContext2D;
|
||||
|
||||
private boatToTrail = new Map<UnitView, TileRef[]>();
|
||||
private unitToTrail = new Map<UnitView, TileRef[]>();
|
||||
|
||||
private theme: Theme = null;
|
||||
|
||||
@@ -190,8 +191,7 @@ export class UnitLayer implements Layer {
|
||||
this.canvas = document.createElement("canvas");
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.transportShipTrailCanvas = document.createElement("canvas");
|
||||
this.transportShipTrailContext =
|
||||
this.transportShipTrailCanvas.getContext("2d");
|
||||
this.unitTrailContext = this.transportShipTrailCanvas.getContext("2d");
|
||||
|
||||
this.canvas.width = this.game.width();
|
||||
this.canvas.height = this.game.height();
|
||||
@@ -200,7 +200,7 @@ export class UnitLayer implements Layer {
|
||||
|
||||
this.updateUnitsSprites();
|
||||
|
||||
this.boatToTrail.forEach((trail, unit) => {
|
||||
this.unitToTrail.forEach((trail, unit) => {
|
||||
for (const t of trail) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
@@ -208,7 +208,7 @@ export class UnitLayer implements Layer {
|
||||
this.relationship(unit),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
150,
|
||||
this.transportShipTrailContext,
|
||||
this.unitTrailContext,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -333,8 +333,92 @@ export class UnitLayer implements Layer {
|
||||
this.drawSprite(unit);
|
||||
}
|
||||
|
||||
private drawTrail(trail: number[], color: Colord, rel: Relationship) {
|
||||
// Paint new trail
|
||||
for (const t of trail) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
color,
|
||||
150,
|
||||
this.unitTrailContext,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private clearTrail(unit: UnitView) {
|
||||
const trail = this.unitToTrail.get(unit);
|
||||
const rel = this.relationship(unit);
|
||||
for (const t of trail) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t), this.unitTrailContext);
|
||||
}
|
||||
this.unitToTrail.delete(unit);
|
||||
|
||||
// Repaint overlapping trails
|
||||
const trailSet = new Set(trail);
|
||||
for (const [other, trail] of this.unitToTrail) {
|
||||
for (const t of trail) {
|
||||
if (trailSet.has(t)) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(other.owner()),
|
||||
150,
|
||||
this.unitTrailContext,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleNuke(unit: UnitView) {
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
if (!this.unitToTrail.has(unit)) {
|
||||
this.unitToTrail.set(unit, []);
|
||||
}
|
||||
|
||||
let newTrailSize = 1;
|
||||
const trail = this.unitToTrail.get(unit);
|
||||
// The nuke can move faster than 1 pixel, draw a line for the trail or else it will be dotted
|
||||
if (trail.length >= 1) {
|
||||
const currentX = this.game.x(unit.lastTile());
|
||||
const currentY = this.game.y(unit.lastTile());
|
||||
const lastX = this.game.x(trail[trail.length - 1]);
|
||||
const lastY = this.game.y(trail[trail.length - 1]);
|
||||
const line = new BezenhamLine(lastX, lastY, currentX, currentY);
|
||||
let point = line.increment();
|
||||
while (point !== true) {
|
||||
trail.push(this.game.ref(point.x, point.y));
|
||||
point = line.increment();
|
||||
}
|
||||
newTrailSize = line.size();
|
||||
} else {
|
||||
trail.push(unit.lastTile());
|
||||
}
|
||||
|
||||
// Paint new trail
|
||||
for (const t of trail.slice(-newTrailSize)) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
150,
|
||||
this.unitTrailContext,
|
||||
);
|
||||
}
|
||||
this.drawTrail(
|
||||
trail.slice(-newTrailSize),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
rel,
|
||||
);
|
||||
this.drawSprite(unit);
|
||||
if (!unit.isActive()) {
|
||||
this.clearTrail(unit);
|
||||
}
|
||||
}
|
||||
|
||||
private handleMIRVWarhead(unit: UnitView) {
|
||||
@@ -361,52 +445,22 @@ export class UnitLayer implements Layer {
|
||||
private handleBoatEvent(unit: UnitView) {
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
if (!this.boatToTrail.has(unit)) {
|
||||
this.boatToTrail.set(unit, []);
|
||||
if (!this.unitToTrail.has(unit)) {
|
||||
this.unitToTrail.set(unit, []);
|
||||
}
|
||||
const trail = this.boatToTrail.get(unit);
|
||||
const trail = this.unitToTrail.get(unit);
|
||||
trail.push(unit.lastTile());
|
||||
|
||||
// Paint trail
|
||||
for (const t of trail.slice(-1)) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
150,
|
||||
this.transportShipTrailContext,
|
||||
);
|
||||
}
|
||||
|
||||
this.drawTrail(
|
||||
trail.slice(-1),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
rel,
|
||||
);
|
||||
this.drawSprite(unit);
|
||||
|
||||
if (!unit.isActive()) {
|
||||
for (const t of trail) {
|
||||
this.clearCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
this.transportShipTrailContext,
|
||||
);
|
||||
}
|
||||
this.boatToTrail.delete(unit);
|
||||
|
||||
// Repaint overlapping trails
|
||||
const trailSet = new Set(trail);
|
||||
for (const [other, trail] of this.boatToTrail) {
|
||||
for (const t of trail) {
|
||||
if (trailSet.has(t)) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(other.owner()),
|
||||
150,
|
||||
this.transportShipTrailContext,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.clearTrail(unit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
UnitType,
|
||||
} from "../game/Game";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { AirPathFinder } from "../pathfinding/PathFinding";
|
||||
import { ParabolaPathFinder } from "../pathfinding/PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
|
||||
export class NukeExecution implements Execution {
|
||||
@@ -21,7 +21,7 @@ export class NukeExecution implements Execution {
|
||||
private nuke: Unit;
|
||||
|
||||
private random: PseudoRandom;
|
||||
private pathFinder: AirPathFinder;
|
||||
private pathFinder: ParabolaPathFinder;
|
||||
|
||||
constructor(
|
||||
private type: NukeType,
|
||||
@@ -45,7 +45,7 @@ export class NukeExecution implements Execution {
|
||||
if (this.speed == -1) {
|
||||
this.speed = this.mg.config().defaultNukeSpeed();
|
||||
}
|
||||
this.pathFinder = new AirPathFinder(mg, this.random);
|
||||
this.pathFinder = new ParabolaPathFinder(mg);
|
||||
}
|
||||
|
||||
public target(): Player | TerraNullius {
|
||||
@@ -95,6 +95,7 @@ export class NukeExecution implements Execution {
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
this.pathFinder.computeControlPoints(spawn, this.dst);
|
||||
this.nuke = this.player.buildUnit(this.type, 0, spawn, {
|
||||
detonationDst: this.dst,
|
||||
});
|
||||
@@ -146,15 +147,13 @@ export class NukeExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.speed; i++) {
|
||||
// Move to next tile
|
||||
const nextTile = this.pathFinder.nextTile(this.nuke.tile(), this.dst);
|
||||
if (nextTile === true) {
|
||||
this.detonate();
|
||||
return;
|
||||
} else {
|
||||
this.nuke.move(nextTile);
|
||||
}
|
||||
// Move to next tile
|
||||
const nextTile = this.pathFinder.nextTile(this.speed);
|
||||
if (nextTile === true) {
|
||||
this.detonate();
|
||||
return;
|
||||
} else {
|
||||
this.nuke.move(nextTile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,54 @@ import { consolex } from "../Consolex";
|
||||
import { Game } from "../game/Game";
|
||||
import { GameMap, TileRef } from "../game/GameMap";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { BezierCurve } from "../utilities/Line";
|
||||
import { AStar, PathFindResultType, TileResult } from "./AStar";
|
||||
import { MiniAStar } from "./MiniAStar";
|
||||
|
||||
export class ParabolaPathFinder {
|
||||
constructor(private mg: GameMap) {}
|
||||
private curve: BezierCurve;
|
||||
private distance: number;
|
||||
|
||||
computeControlPoints(orig: TileRef, dst: TileRef) {
|
||||
const origX = this.mg.x(orig);
|
||||
const origY = this.mg.y(orig);
|
||||
const dstX = this.mg.x(dst);
|
||||
const dstY = this.mg.y(dst);
|
||||
|
||||
this.curve = new BezierCurve(origX, origY, dstX, dstY);
|
||||
const dx = dstX - origX;
|
||||
const dy = dstY - origY;
|
||||
this.distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
const x0 = origX + (dstX - origX) / 4;
|
||||
const y0 = Math.max(
|
||||
origY + (dstY - origY) / 4 - Math.max(this.distance / 3, 50),
|
||||
0,
|
||||
);
|
||||
const x1 = origX + ((dstX - origX) * 3) / 4;
|
||||
const y1 = Math.max(
|
||||
origY + ((dstY - origY) * 3) / 4 - Math.max(this.distance / 3, 50),
|
||||
0,
|
||||
);
|
||||
|
||||
this.curve.setControlPoint0(x0, y0);
|
||||
this.curve.setControlPoint1(x1, y1);
|
||||
}
|
||||
|
||||
nextTile(speed: number): TileRef | true {
|
||||
if (!this.curve) {
|
||||
return;
|
||||
}
|
||||
const incr = speed / (this.distance * 2);
|
||||
const nextPoint = this.curve.increment(incr);
|
||||
if (!nextPoint) {
|
||||
return true;
|
||||
}
|
||||
return this.mg.ref(Math.floor(nextPoint.x), Math.floor(nextPoint.y));
|
||||
}
|
||||
}
|
||||
|
||||
export class AirPathFinder {
|
||||
constructor(
|
||||
private mg: GameMap,
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
export class BezenhamLine {
|
||||
constructor(
|
||||
private x0: number,
|
||||
private y0: number,
|
||||
private x1: number,
|
||||
private y1: number,
|
||||
) {
|
||||
this.dx = Math.abs(this.x1 - this.x0);
|
||||
this.dy = Math.abs(this.y1 - this.y0);
|
||||
this.sx = this.x0 < this.x1 ? 1 : -1;
|
||||
this.sy = this.y0 < this.y1 ? 1 : -1;
|
||||
this.error = this.dx - this.dy;
|
||||
}
|
||||
|
||||
private dx: number;
|
||||
private dy: number;
|
||||
private sx: number;
|
||||
private sy: number;
|
||||
private error: number;
|
||||
|
||||
size() {
|
||||
return Math.max(this.dx, this.dy) + 1;
|
||||
}
|
||||
|
||||
// Increment either by 1 in x or y
|
||||
increment(): { x: number; y: number } | true {
|
||||
if (this.x0 === this.x1 && this.y0 === this.y1) {
|
||||
return true;
|
||||
}
|
||||
const x = this.x0;
|
||||
const y = this.y0;
|
||||
const err2 = 2 * this.error;
|
||||
|
||||
if (err2 > -this.dy) {
|
||||
this.error -= this.dy;
|
||||
this.x0 += this.sx;
|
||||
}
|
||||
if (err2 < this.dx) {
|
||||
this.error += this.dx;
|
||||
this.y0 += this.sy;
|
||||
}
|
||||
return { x, y };
|
||||
}
|
||||
}
|
||||
|
||||
export class BezierCurve {
|
||||
constructor(
|
||||
private x0: number,
|
||||
private y0: number,
|
||||
private x1: number,
|
||||
private y2: number,
|
||||
) {
|
||||
const dx = this.x1 - this.x0;
|
||||
const dy = this.y2 - this.y0;
|
||||
const dist = Math.abs(this.x1 - this.x0);
|
||||
}
|
||||
|
||||
private t: number = 0;
|
||||
private controlPoint0X: number;
|
||||
private controlPoint0Y: number;
|
||||
private controlPoint1X: number;
|
||||
private controlPoint1Y: number;
|
||||
|
||||
setControlPoint0(x, y) {
|
||||
this.controlPoint0X = x;
|
||||
this.controlPoint0Y = y;
|
||||
}
|
||||
|
||||
setControlPoint1(x, y) {
|
||||
this.controlPoint1X = x;
|
||||
this.controlPoint1Y = y;
|
||||
}
|
||||
|
||||
increment(incr: number): { x: number; y: number } {
|
||||
// Calculate the next point on the Bézier curve
|
||||
// const incr = speed / (this.distance * 2);
|
||||
this.t = this.t + incr;
|
||||
if (this.t >= 1) {
|
||||
return null; // end reached
|
||||
}
|
||||
const nextX =
|
||||
Math.pow(1 - this.t, 3) * this.x0 +
|
||||
3 * Math.pow(1 - this.t, 2) * this.t * this.controlPoint0X +
|
||||
3 * (1 - this.t) * Math.pow(this.t, 2) * this.controlPoint1X +
|
||||
Math.pow(this.t, 3) * this.x1;
|
||||
const nextY =
|
||||
Math.pow(1 - this.t, 3) * this.y0 +
|
||||
3 * Math.pow(1 - this.t, 2) * this.t * this.controlPoint0Y +
|
||||
3 * (1 - this.t) * Math.pow(this.t, 2) * this.controlPoint1Y +
|
||||
Math.pow(this.t, 3) * this.y2;
|
||||
return { x: nextX, y: nextY };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user