Remove duplicated code

This commit is contained in:
Tom
2025-05-11 10:23:47 +02:00
parent 890972cb0c
commit 6482fba2e5
4 changed files with 248 additions and 57 deletions
+99 -45
View File
@@ -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 -12
View File
@@ -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);
}
}
+45
View File
@@ -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,
+93
View File
@@ -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 };
}
}