Add new FX layer and a nuke animation (#807)

## Description:

Changes:
- Added an AnimatedSprite class to handle spritesheets
- New FX layer, displaying cosmectics effects
- New Nuke FX: an animated sprite explosion, and a shockwave effect
- New "Special effects" setting, toggle to deactivate the FX layer for
lower-end hardware / personal taste


#### Note that the animation is a placeholder. It should be replaced
when a better looking one is available.

- Nuke:


https://github.com/user-attachments/assets/6eff1d0d-5081-47ad-932f-2bfcda72cb3c


- Mirv:


https://github.com/user-attachments/assets/3bc891b4-449c-4acb-8e24-e237b423c2a9


- SAM are also using the same Nuke animation. To be improved with a
custom FX:


https://github.com/user-attachments/assets/d65addce-5890-42c2-81e0-3eaa79ed87f3

## Performances:

Excellent since it's not manipulating the underlying imagedata directly.
Profiling during a MIRV with 100's of animations:

![image](https://github.com/user-attachments/assets/3477c963-d10f-493b-bcb1-93b7990d3edb)



### New settings:

- main menu:


![image](https://github.com/user-attachments/assets/5b1127bb-3b89-4c06-b519-fb173301d9fd)

- In game:


![image](https://github.com/user-attachments/assets/ba899253-a7cf-4d1c-8801-da41d2b1536b)


## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom
This commit is contained in:
DevelopingTom
2025-05-18 12:43:12 -07:00
committed by GitHub
parent 1ebf6d4fab
commit bb24f18285
12 changed files with 367 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
export interface Fx {
renderTick(duration: number, ctx: CanvasRenderingContext2D): boolean;
}
export enum FxType {
Nuke = "Nuke",
}
+62
View File
@@ -0,0 +1,62 @@
import { AnimatedSprite } from "../AnimatedSprite";
import { createAnimatedSpriteForUnit } from "../AnimatedSpriteLoader";
import { Fx, FxType } from "./Fx";
/**
* Shockwave effect: draw a growing 1px white circle
*/
export class ShockwaveFx implements Fx {
private lifeTime: number = 0;
constructor(
private x: number,
private y: number,
private duration: number,
private maxRadius: number,
) {}
renderTick(frameTime: number, ctx: CanvasRenderingContext2D): boolean {
this.lifeTime += frameTime;
if (this.lifeTime >= this.duration) {
return false;
}
const t = this.lifeTime / this.duration;
const radius = t * this.maxRadius;
ctx.beginPath();
ctx.arc(this.x, this.y, radius, 0, Math.PI * 2);
ctx.strokeStyle = "rgba(255, 255, 255, " + (1 - t) + ")";
ctx.lineWidth = 0.5;
ctx.stroke();
return true;
}
}
/**
* Explosion effect: sprite animation of an explosion
*/
export class NukeExplosionFx implements Fx {
private lifeTime: number = 0;
private nukeExplosionSprite: AnimatedSprite | null;
constructor(
private x: number,
private y: number,
private duration: number,
) {
this.nukeExplosionSprite = createAnimatedSpriteForUnit(FxType.Nuke);
}
renderTick(frameTime: number, ctx: CanvasRenderingContext2D): boolean {
if (this.nukeExplosionSprite) {
this.lifeTime += frameTime;
if (this.lifeTime >= this.duration) {
return false;
}
if (this.nukeExplosionSprite.isActive()) {
this.nukeExplosionSprite.update(frameTime);
this.nukeExplosionSprite.draw(ctx, this.x, this.y);
return true;
}
return false;
}
return false;
}
}