mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-27 16:55:06 +00:00
Add new nuke preview FX (#2322)
## Description: New FX played to preview the nuke explosion area (follow up of https://github.com/openfrontio/OpenFrontIO/pull/2309) Will prevent over-nuking the same area in team games  Visuals inspired from https://github.com/openfrontio/OpenFrontIO/pull/1814 (@ryanbarlow97) ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: IngloriousTom --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { NukeMagnitude } from "../../../core/configuration/Config";
|
||||
import { Fx } from "./Fx";
|
||||
|
||||
export class NukeAreaFx implements Fx {
|
||||
private lifeTime = 0;
|
||||
private ended = false;
|
||||
private readonly endAnimationDuration = 300; // in ms
|
||||
private readonly startAnimationDuration = 200; // in ms
|
||||
|
||||
private readonly innerDiameter: number;
|
||||
private readonly outerDiameter: number;
|
||||
|
||||
private offset = 0;
|
||||
private readonly dashSize: number;
|
||||
private readonly rotationSpeed = 20; // px per seconds
|
||||
private readonly baseAlpha = 0.9;
|
||||
|
||||
constructor(
|
||||
private x: number,
|
||||
private y: number,
|
||||
magnitude: NukeMagnitude,
|
||||
) {
|
||||
this.innerDiameter = magnitude.inner;
|
||||
this.outerDiameter = magnitude.outer;
|
||||
const numDash = Math.max(1, Math.floor(this.outerDiameter / 3));
|
||||
this.dashSize = (Math.PI / numDash) * this.outerDiameter;
|
||||
}
|
||||
|
||||
end() {
|
||||
this.ended = true;
|
||||
this.lifeTime = 0; // reset for fade-out timing
|
||||
}
|
||||
|
||||
renderTick(frameTime: number, ctx: CanvasRenderingContext2D): boolean {
|
||||
this.lifeTime += frameTime;
|
||||
|
||||
if (this.ended && this.lifeTime >= this.endAnimationDuration) return false;
|
||||
let t: number;
|
||||
if (this.ended) {
|
||||
t = Math.max(0, 1 - this.lifeTime / this.endAnimationDuration);
|
||||
} else {
|
||||
t = Math.min(1, this.lifeTime / this.startAnimationDuration);
|
||||
}
|
||||
const alpha = Math.max(0, Math.min(1, this.baseAlpha * t));
|
||||
|
||||
ctx.save();
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = `rgba(255,0,0,${alpha})`;
|
||||
ctx.fillStyle = `rgba(255,0,0,${Math.max(0, alpha - 0.6)})`;
|
||||
|
||||
// Inner circle
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 1;
|
||||
const innerDiameter =
|
||||
(this.innerDiameter / 2) * (1 - t) + this.innerDiameter * t;
|
||||
ctx.arc(this.x, this.y, innerDiameter, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
// Outer circle
|
||||
this.offset += this.rotationSpeed * (frameTime / 1000);
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = `rgba(255,0,0,${alpha})`;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.lineDashOffset = this.offset;
|
||||
ctx.setLineDash([this.dashSize]);
|
||||
const outerDiameter =
|
||||
(this.outerDiameter + 20) * (1 - t) + this.outerDiameter * t;
|
||||
ctx.arc(this.x, this.y, outerDiameter, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.restore();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { renderNumber } from "../../Utils";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { conquestFxFactory } from "../fx/ConquestFx";
|
||||
import { Fx, FxType } from "../fx/Fx";
|
||||
import { NukeAreaFx } from "../fx/NukeAreaFx";
|
||||
import { nukeFxFactory, ShockwaveFx } from "../fx/NukeFx";
|
||||
import { FadeFx, MoveSpriteFx, SpriteFx } from "../fx/SpriteFx";
|
||||
import { TargetFx } from "../fx/TargetFx";
|
||||
@@ -32,7 +33,7 @@ export class FxLayer implements Layer {
|
||||
|
||||
private allFx: Fx[] = [];
|
||||
private boatTargetFxByUnitId: Map<number, TargetFx> = new Map();
|
||||
private nukeTargetFxByUnitId: Map<number, TargetFx> = new Map();
|
||||
private nukeTargetFxByUnitId: Map<number, NukeAreaFx> = new Map();
|
||||
|
||||
constructor(private game: GameView) {
|
||||
this.theme = this.game.config().theme();
|
||||
@@ -112,7 +113,11 @@ export class FxLayer implements Layer {
|
||||
if (t !== undefined) {
|
||||
const x = this.game.x(t);
|
||||
const y = this.game.y(t);
|
||||
const fx = new TargetFx(x, y, 0, true);
|
||||
const fx = new NukeAreaFx(
|
||||
x,
|
||||
y,
|
||||
this.game.config().nukeMagnitudes(unit.type()),
|
||||
);
|
||||
this.allFx.push(fx);
|
||||
this.nukeTargetFxByUnitId.set(unit.id(), fx);
|
||||
}
|
||||
@@ -236,7 +241,7 @@ export class FxLayer implements Layer {
|
||||
}
|
||||
case UnitType.AtomBomb: {
|
||||
this.createNukeTargetFxIfOwned(unit);
|
||||
this.onNukeEvent(unit, 160);
|
||||
this.onNukeEvent(unit, 70);
|
||||
break;
|
||||
}
|
||||
case UnitType.MIRVWarhead:
|
||||
|
||||
Reference in New Issue
Block a user