mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 00:28:14 +00:00
Revert "Add new bouncing trajectory for april fools (#3534)"
This reverts commit c92895620f.
This commit is contained in:
@@ -5,7 +5,6 @@ import {
|
|||||||
BonusEventUpdate,
|
BonusEventUpdate,
|
||||||
ConquestUpdate,
|
ConquestUpdate,
|
||||||
GameUpdateType,
|
GameUpdateType,
|
||||||
TextEventUpdate,
|
|
||||||
} from "src/core/game/GameUpdates";
|
} from "src/core/game/GameUpdates";
|
||||||
import type { GameView, UnitView } from "../../../core/game/GameView";
|
import type { GameView, UnitView } from "../../../core/game/GameView";
|
||||||
import { MoveWarshipIntentEvent } from "../../Transport";
|
import { MoveWarshipIntentEvent } from "../../Transport";
|
||||||
@@ -63,32 +62,12 @@ export class DynamicUILayer implements Layer {
|
|||||||
this.onBonusEvent(bonusEvent);
|
this.onBonusEvent(bonusEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
updates[GameUpdateType.TextUIEvent]?.forEach((textEvent) => {
|
|
||||||
if (textEvent === undefined) return;
|
|
||||||
this.onTextEvent(textEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
updates[GameUpdateType.ConquestEvent]?.forEach((update) => {
|
updates[GameUpdateType.ConquestEvent]?.forEach((update) => {
|
||||||
if (update === undefined) return;
|
if (update === undefined) return;
|
||||||
this.onConquestEvent(update);
|
this.onConquestEvent(update);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextEvent(textEvent: TextEventUpdate) {
|
|
||||||
// Only display text fx for the current player
|
|
||||||
if (this.game.player(textEvent.player) !== this.game.myPlayer()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tile = textEvent.tile;
|
|
||||||
const x = this.game.x(tile);
|
|
||||||
const y = this.game.y(tile) + TEXT_OFFSET_Y;
|
|
||||||
const text = textEvent.text;
|
|
||||||
|
|
||||||
this.uiElements.push(
|
|
||||||
new TextIndicator(this.transformHandler, text, x, y, 500, 12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onBonusEvent(bonus: BonusEventUpdate) {
|
onBonusEvent(bonus: BonusEventUpdate) {
|
||||||
// Only display text fx for the current player
|
// Only display text fx for the current player
|
||||||
if (this.game.player(bonus.player) !== this.game.myPlayer()) {
|
if (this.game.player(bonus.player) !== this.game.myPlayer()) {
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import { Theme } from "../../../core/configuration/Config";
|
|||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import { UnitType } from "../../../core/game/Game";
|
import { UnitType } from "../../../core/game/Game";
|
||||||
import { TileRef } from "../../../core/game/GameMap";
|
import { TileRef } from "../../../core/game/GameMap";
|
||||||
import {
|
import { ConquestUpdate, GameUpdateType } from "../../../core/game/GameUpdates";
|
||||||
ConquestUpdate,
|
|
||||||
GameUpdateType,
|
|
||||||
TextEventUpdate,
|
|
||||||
} from "../../../core/game/GameUpdates";
|
|
||||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||||
import SoundManager, { SoundEffect } from "../../sound/SoundManager";
|
import SoundManager, { SoundEffect } from "../../sound/SoundManager";
|
||||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||||
@@ -60,12 +56,6 @@ export class FxLayer implements Layer {
|
|||||||
if (update === undefined) return;
|
if (update === undefined) return;
|
||||||
this.onConquestEvent(update);
|
this.onConquestEvent(update);
|
||||||
});
|
});
|
||||||
this.game
|
|
||||||
.updatesSinceLastTick()
|
|
||||||
?.[GameUpdateType.TextUIEvent]?.forEach((update) => {
|
|
||||||
if (update === undefined) return;
|
|
||||||
this.onBoingEvent(update);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnitEvent(unit: UnitView) {
|
onUnitEvent(unit: UnitView) {
|
||||||
@@ -149,12 +139,6 @@ export class FxLayer implements Layer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onBoingEvent(boing: TextEventUpdate) {
|
|
||||||
const x = this.game.x(boing.tile);
|
|
||||||
const y = this.game.y(boing.tile);
|
|
||||||
this.allFx.push(new ShockwaveFx(x, y, 1000, 20));
|
|
||||||
}
|
|
||||||
|
|
||||||
onConquestEvent(conquest: ConquestUpdate) {
|
onConquestEvent(conquest: ConquestUpdate) {
|
||||||
// Only display fx for the current player
|
// Only display fx for the current player
|
||||||
const conqueror = this.game.player(conquest.conquerorId);
|
const conqueror = this.game.player(conquest.conquerorId);
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ import {
|
|||||||
} from "../game/Game";
|
} from "../game/Game";
|
||||||
import { TileRef } from "../game/GameMap";
|
import { TileRef } from "../game/GameMap";
|
||||||
import { UniversalPathFinding } from "../pathfinding/PathFinder";
|
import { UniversalPathFinding } from "../pathfinding/PathFinder";
|
||||||
import {
|
import { ParabolaUniversalPathFinder } from "../pathfinding/PathFinder.Parabola";
|
||||||
BouncingParabolaUniversalPathFinder,
|
|
||||||
ParabolaUniversalPathFinder,
|
|
||||||
} from "../pathfinding/PathFinder.Parabola";
|
|
||||||
import { PathStatus } from "../pathfinding/types";
|
import { PathStatus } from "../pathfinding/types";
|
||||||
import { PseudoRandom } from "../PseudoRandom";
|
import { PseudoRandom } from "../PseudoRandom";
|
||||||
import { NukeType } from "../StatsSchemas";
|
import { NukeType } from "../StatsSchemas";
|
||||||
@@ -27,9 +24,7 @@ export class NukeExecution implements Execution {
|
|||||||
private mg: Game;
|
private mg: Game;
|
||||||
private nuke: Unit | null = null;
|
private nuke: Unit | null = null;
|
||||||
private tilesToDestroyCache: Set<TileRef> | undefined;
|
private tilesToDestroyCache: Set<TileRef> | undefined;
|
||||||
private pathFinder:
|
private pathFinder: ParabolaUniversalPathFinder;
|
||||||
| BouncingParabolaUniversalPathFinder
|
|
||||||
| ParabolaUniversalPathFinder;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private nukeType: NukeType,
|
private nukeType: NukeType,
|
||||||
@@ -46,32 +41,18 @@ export class NukeExecution implements Execution {
|
|||||||
if (this.speed === -1) {
|
if (this.speed === -1) {
|
||||||
this.speed = this.mg.config().defaultNukeSpeed();
|
this.speed = this.mg.config().defaultNukeSpeed();
|
||||||
}
|
}
|
||||||
|
this.pathFinder = UniversalPathFinding.Parabola(mg, {
|
||||||
const rand = new PseudoRandom(ticks);
|
increment: this.speed,
|
||||||
if (rand.chance(6)) {
|
distanceBasedHeight: this.nukeType !== UnitType.MIRVWarhead,
|
||||||
this.pathFinder = UniversalPathFinding.BouncingParabola(
|
directionUp: this.rocketDirectionUp,
|
||||||
mg,
|
});
|
||||||
this.player.id(),
|
|
||||||
{
|
|
||||||
increment: this.speed,
|
|
||||||
distanceBasedHeight: this.nukeType !== UnitType.MIRVWarhead,
|
|
||||||
directionUp: this.rocketDirectionUp,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.pathFinder = UniversalPathFinding.Parabola(mg, {
|
|
||||||
increment: this.speed,
|
|
||||||
distanceBasedHeight: this.nukeType !== UnitType.MIRVWarhead,
|
|
||||||
directionUp: this.rocketDirectionUp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public target(): Player | TerraNullius {
|
public target(): Player | TerraNullius {
|
||||||
return this.mg.owner(this.dst);
|
return this.mg.owner(this.dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
private tilesToDestroy(explosionTile: TileRef): Set<TileRef> {
|
private tilesToDestroy(): Set<TileRef> {
|
||||||
if (this.tilesToDestroyCache !== undefined) {
|
if (this.tilesToDestroyCache !== undefined) {
|
||||||
return this.tilesToDestroyCache;
|
return this.tilesToDestroyCache;
|
||||||
}
|
}
|
||||||
@@ -82,8 +63,8 @@ export class NukeExecution implements Execution {
|
|||||||
const rand = new PseudoRandom(this.mg.ticks());
|
const rand = new PseudoRandom(this.mg.ticks());
|
||||||
const inner2 = magnitude.inner * magnitude.inner;
|
const inner2 = magnitude.inner * magnitude.inner;
|
||||||
const outer2 = magnitude.outer * magnitude.outer;
|
const outer2 = magnitude.outer * magnitude.outer;
|
||||||
this.tilesToDestroyCache = this.mg.bfs(explosionTile, (_, n: TileRef) => {
|
this.tilesToDestroyCache = this.mg.bfs(this.dst, (_, n: TileRef) => {
|
||||||
const d2 = this.mg?.euclideanDistSquared(explosionTile, n) ?? 0;
|
const d2 = this.mg?.euclideanDistSquared(this.dst, n) ?? 0;
|
||||||
return d2 <= outer2 && (d2 <= inner2 || rand.chance(2));
|
return d2 <= outer2 && (d2 <= inner2 || rand.chance(2));
|
||||||
});
|
});
|
||||||
return this.tilesToDestroyCache;
|
return this.tilesToDestroyCache;
|
||||||
@@ -212,7 +193,7 @@ export class NukeExecution implements Execution {
|
|||||||
// Move to next tile
|
// Move to next tile
|
||||||
const result = this.pathFinder.next(this.src!, this.dst, this.speed);
|
const result = this.pathFinder.next(this.src!, this.dst, this.speed);
|
||||||
if (result.status === PathStatus.COMPLETE) {
|
if (result.status === PathStatus.COMPLETE) {
|
||||||
this.detonate(result.node);
|
this.detonate();
|
||||||
return;
|
return;
|
||||||
} else if (result.status === PathStatus.NEXT) {
|
} else if (result.status === PathStatus.NEXT) {
|
||||||
this.updateNukeTargetable();
|
this.updateNukeTargetable();
|
||||||
@@ -266,7 +247,7 @@ export class NukeExecution implements Execution {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private detonate(explosionTile: TileRef) {
|
private detonate() {
|
||||||
if (this.nuke === null) {
|
if (this.nuke === null) {
|
||||||
throw new Error("Not initialized");
|
throw new Error("Not initialized");
|
||||||
}
|
}
|
||||||
@@ -275,7 +256,7 @@ export class NukeExecution implements Execution {
|
|||||||
const config = mg.config();
|
const config = mg.config();
|
||||||
|
|
||||||
const magnitude = config.nukeMagnitudes(this.nuke.type());
|
const magnitude = config.nukeMagnitudes(this.nuke.type());
|
||||||
const toDestroy = this.tilesToDestroy(explosionTile);
|
const toDestroy = this.tilesToDestroy();
|
||||||
|
|
||||||
// Retrieve all impacted players and the number of tiles
|
// Retrieve all impacted players and the number of tiles
|
||||||
const tilesPerPlayers = new Map<Player, number>();
|
const tilesPerPlayers = new Map<Player, number>();
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ export enum GameUpdateType {
|
|||||||
Hash,
|
Hash,
|
||||||
UnitIncoming,
|
UnitIncoming,
|
||||||
BonusEvent,
|
BonusEvent,
|
||||||
TextUIEvent,
|
|
||||||
RailroadDestructionEvent,
|
RailroadDestructionEvent,
|
||||||
RailroadConstructionEvent,
|
RailroadConstructionEvent,
|
||||||
RailroadSnapEvent,
|
RailroadSnapEvent,
|
||||||
@@ -84,7 +83,6 @@ export type GameUpdate =
|
|||||||
| UnitIncomingUpdate
|
| UnitIncomingUpdate
|
||||||
| AllianceExtensionUpdate
|
| AllianceExtensionUpdate
|
||||||
| BonusEventUpdate
|
| BonusEventUpdate
|
||||||
| TextEventUpdate
|
|
||||||
| RailroadConstructionUpdate
|
| RailroadConstructionUpdate
|
||||||
| RailroadDestructionUpdate
|
| RailroadDestructionUpdate
|
||||||
| RailroadSnapUpdate
|
| RailroadSnapUpdate
|
||||||
@@ -100,13 +98,6 @@ export interface BonusEventUpdate {
|
|||||||
troops: number;
|
troops: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextEventUpdate {
|
|
||||||
type: GameUpdateType.TextUIEvent;
|
|
||||||
player: PlayerID;
|
|
||||||
tile: TileRef;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RailroadConstructionUpdate {
|
export interface RailroadConstructionUpdate {
|
||||||
type: GameUpdateType.RailroadConstructionEvent;
|
type: GameUpdateType.RailroadConstructionEvent;
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { Game, PlayerID } from "../game/Game";
|
|
||||||
import { GameMap, TileRef } from "../game/GameMap";
|
import { GameMap, TileRef } from "../game/GameMap";
|
||||||
import { GameUpdateType } from "../game/GameUpdates";
|
|
||||||
import { within } from "../Util";
|
import { within } from "../Util";
|
||||||
import { DistanceBasedBezierCurve } from "../utilities/Line";
|
import { DistanceBasedBezierCurve } from "../utilities/Line";
|
||||||
import { PathResult, PathStatus, SteppingPathFinder } from "./types";
|
import { PathResult, PathStatus, SteppingPathFinder } from "./types";
|
||||||
@@ -9,7 +7,6 @@ export interface ParabolaOptions {
|
|||||||
increment?: number;
|
increment?: number;
|
||||||
distanceBasedHeight?: boolean;
|
distanceBasedHeight?: boolean;
|
||||||
directionUp?: boolean;
|
directionUp?: boolean;
|
||||||
minHeight?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PARABOLA_MIN_HEIGHT = 50;
|
const PARABOLA_MIN_HEIGHT = 50;
|
||||||
@@ -28,7 +25,6 @@ export class ParabolaUniversalPathFinder
|
|||||||
private createCurve(from: TileRef, to: TileRef): DistanceBasedBezierCurve {
|
private createCurve(from: TileRef, to: TileRef): DistanceBasedBezierCurve {
|
||||||
const increment = this.options?.increment ?? 3;
|
const increment = this.options?.increment ?? 3;
|
||||||
const distanceBasedHeight = this.options?.distanceBasedHeight ?? true;
|
const distanceBasedHeight = this.options?.distanceBasedHeight ?? true;
|
||||||
const minHeight = this.options?.minHeight ?? PARABOLA_MIN_HEIGHT;
|
|
||||||
const directionUp = this.options?.directionUp ?? true;
|
const directionUp = this.options?.directionUp ?? true;
|
||||||
|
|
||||||
const p0 = { x: this.gameMap.x(from), y: this.gameMap.y(from) };
|
const p0 = { x: this.gameMap.x(from), y: this.gameMap.y(from) };
|
||||||
@@ -37,7 +33,7 @@ export class ParabolaUniversalPathFinder
|
|||||||
const dy = p3.y - p0.y;
|
const dy = p3.y - p0.y;
|
||||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
const maxHeight = distanceBasedHeight
|
const maxHeight = distanceBasedHeight
|
||||||
? Math.max(distance / 3, minHeight)
|
? Math.max(distance / 3, PARABOLA_MIN_HEIGHT)
|
||||||
: 0;
|
: 0;
|
||||||
const heightMult = directionUp ? -1 : 1;
|
const heightMult = directionUp ? -1 : 1;
|
||||||
const mapHeight = this.gameMap.height();
|
const mapHeight = this.gameMap.height();
|
||||||
@@ -92,106 +88,3 @@ export class ParabolaUniversalPathFinder
|
|||||||
return this.curve?.getCurrentIndex() ?? 0;
|
return this.curve?.getCurrentIndex() ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BouncingParabolaUniversalPathFinder
|
|
||||||
implements SteppingPathFinder<TileRef>
|
|
||||||
{
|
|
||||||
private parabola: ParabolaUniversalPathFinder;
|
|
||||||
private bouncing = false;
|
|
||||||
|
|
||||||
private fromBounce: TileRef;
|
|
||||||
private toBounce: TileRef;
|
|
||||||
private previousIndex: number = 0;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private mg: Game,
|
|
||||||
private playerId: PlayerID,
|
|
||||||
private options?: ParabolaOptions,
|
|
||||||
) {
|
|
||||||
this.parabola = new ParabolaUniversalPathFinder(mg.map(), options);
|
|
||||||
}
|
|
||||||
|
|
||||||
next(from: number, to: number, dist?: number): PathResult<TileRef> {
|
|
||||||
if (this.bouncing) {
|
|
||||||
return this.nextBounce(dist);
|
|
||||||
}
|
|
||||||
const result = this.parabola.next(from, to, dist);
|
|
||||||
if (result.status === PathStatus.COMPLETE) {
|
|
||||||
if (this.bounce(from, to)) {
|
|
||||||
return this.nextBounce();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bounce(from: number, to: number): boolean {
|
|
||||||
const bounceDest = this.computeBounceDestination(from, to);
|
|
||||||
if (!bounceDest) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.previousIndex = this.parabola.currentIndex();
|
|
||||||
this.bouncing = true;
|
|
||||||
this.fromBounce = to;
|
|
||||||
this.toBounce = bounceDest;
|
|
||||||
|
|
||||||
this.mg.addUpdate({
|
|
||||||
type: GameUpdateType.TextUIEvent,
|
|
||||||
player: this.playerId,
|
|
||||||
tile: to,
|
|
||||||
text: "Boing",
|
|
||||||
});
|
|
||||||
|
|
||||||
this.parabola = new ParabolaUniversalPathFinder(this.mg.map(), {
|
|
||||||
increment: this.options?.increment ?? 3,
|
|
||||||
distanceBasedHeight: true,
|
|
||||||
directionUp: this.options?.directionUp ?? true,
|
|
||||||
minHeight: 25,
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private nextBounce(dist?: number): PathResult<TileRef> {
|
|
||||||
return this.parabola.next(this.fromBounce, this.toBounce, dist);
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidate(): void {
|
|
||||||
this.parabola.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
findPath(from: number | number[], to: number): number[] | null {
|
|
||||||
if (Array.isArray(from)) {
|
|
||||||
throw new Error(
|
|
||||||
"ParabolaUniversalPathFinder does not support multiple start points",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const tiles = this.parabola.findPath(from, to);
|
|
||||||
const newDest = this.computeBounceDestination(from, to);
|
|
||||||
if (tiles && newDest) {
|
|
||||||
const bounceTiles = this.parabola.findPath(to, newDest);
|
|
||||||
if (bounceTiles) {
|
|
||||||
return tiles?.concat(bounceTiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentIndex(): number {
|
|
||||||
return this.parabola.currentIndex() + this.previousIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private computeBounceDestination(src: TileRef, dst: TileRef): TileRef | null {
|
|
||||||
const destX = this.mg.x(dst);
|
|
||||||
const destY = this.mg.y(dst);
|
|
||||||
const srcX = this.mg.x(src);
|
|
||||||
const srcY = this.mg.y(src);
|
|
||||||
const newX = Math.min(
|
|
||||||
Math.floor(destX + (destX - srcX) / 2),
|
|
||||||
this.mg.width() - 1,
|
|
||||||
);
|
|
||||||
const newY = Math.min(
|
|
||||||
Math.floor(destY + (destY - srcY) / 2),
|
|
||||||
this.mg.height() - 1,
|
|
||||||
);
|
|
||||||
return this.mg.isValidCoord(newX, newY) ? this.mg.ref(newX, newY) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Game, PlayerID } from "../game/Game";
|
import { Game } from "../game/Game";
|
||||||
import { GameMap, TileRef } from "../game/GameMap";
|
import { GameMap, TileRef } from "../game/GameMap";
|
||||||
import { TrainStation } from "../game/TrainStation";
|
import { TrainStation } from "../game/TrainStation";
|
||||||
import { AStarRail } from "./algorithms/AStar.Rail";
|
import { AStarRail } from "./algorithms/AStar.Rail";
|
||||||
import { AStarWater } from "./algorithms/AStar.Water";
|
import { AStarWater } from "./algorithms/AStar.Water";
|
||||||
import { AirPathFinder } from "./PathFinder.Air";
|
import { AirPathFinder } from "./PathFinder.Air";
|
||||||
import {
|
import {
|
||||||
BouncingParabolaUniversalPathFinder,
|
|
||||||
ParabolaOptions,
|
ParabolaOptions,
|
||||||
ParabolaUniversalPathFinder,
|
ParabolaUniversalPathFinder,
|
||||||
} from "./PathFinder.Parabola";
|
} from "./PathFinder.Parabola";
|
||||||
@@ -28,14 +27,6 @@ export class UniversalPathFinding {
|
|||||||
): ParabolaUniversalPathFinder {
|
): ParabolaUniversalPathFinder {
|
||||||
return new ParabolaUniversalPathFinder(gameMap, options);
|
return new ParabolaUniversalPathFinder(gameMap, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BouncingParabola(
|
|
||||||
mg: Game,
|
|
||||||
playerId: PlayerID,
|
|
||||||
options?: ParabolaOptions,
|
|
||||||
): BouncingParabolaUniversalPathFinder {
|
|
||||||
return new BouncingParabolaUniversalPathFinder(mg, playerId, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user