mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 10:54:18 +00:00
bb24f18285
## 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:  ### New settings: - main menu:  - In game:  ## 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
77 lines
1.9 KiB
TypeScript
77 lines
1.9 KiB
TypeScript
import nuke from "../../../resources/sprites/nukeExplosion.png";
|
|
import { AnimatedSprite } from "./AnimatedSprite";
|
|
import { FxType } from "./fx/Fx";
|
|
|
|
type AnimatedSpriteConfig = {
|
|
url: string;
|
|
frameWidth: number;
|
|
frameCount: number;
|
|
frameDuration: number; // ms per frame
|
|
looping?: boolean;
|
|
originX: number;
|
|
originY: number;
|
|
};
|
|
|
|
const ANIMATED_SPRITE_CONFIG: Partial<Record<FxType, AnimatedSpriteConfig>> = {
|
|
[FxType.Nuke]: {
|
|
url: nuke,
|
|
frameWidth: 60,
|
|
frameCount: 9,
|
|
frameDuration: 70,
|
|
looping: false,
|
|
originX: 30,
|
|
originY: 30,
|
|
},
|
|
};
|
|
|
|
const animatedSpriteImageMap: Map<FxType, CanvasImageSource> = new Map();
|
|
|
|
export const loadAllAnimatedSpriteImages = async (): Promise<void> => {
|
|
const entries = Object.entries(ANIMATED_SPRITE_CONFIG);
|
|
|
|
await Promise.all(
|
|
entries.map(async ([fxType, config]) => {
|
|
const typedFxType = fxType as FxType;
|
|
if (!config?.url) return;
|
|
|
|
try {
|
|
const img = new Image();
|
|
img.crossOrigin = "anonymous";
|
|
img.src = config.url;
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
img.onload = () => resolve();
|
|
img.onerror = (e) => reject(e);
|
|
});
|
|
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = img.width;
|
|
canvas.height = img.height;
|
|
canvas.getContext("2d")!.drawImage(img, 0, 0);
|
|
|
|
animatedSpriteImageMap.set(typedFxType, canvas);
|
|
} catch (err) {
|
|
console.error(`Failed to load sprite for ${typedFxType}:`, err);
|
|
}
|
|
}),
|
|
);
|
|
};
|
|
|
|
export const createAnimatedSpriteForUnit = (
|
|
fxType: FxType,
|
|
): AnimatedSprite | null => {
|
|
const config = ANIMATED_SPRITE_CONFIG[fxType];
|
|
const image = animatedSpriteImageMap.get(fxType);
|
|
if (!config || !image) return null;
|
|
|
|
return new AnimatedSprite(
|
|
image,
|
|
config.frameWidth,
|
|
config.frameCount,
|
|
config.frameDuration,
|
|
config.looping ?? true,
|
|
config.originX,
|
|
config.originY,
|
|
);
|
|
};
|