Add naval combat animations (#858)

## Description:


https://github.com/user-attachments/assets/b46f949a-eb50-4656-8492-216cf820ac46

Add a couple animations for naval combat:

- shell hit
- ship explosion
- ship sinking

Added a simple `Timeline` class to spread FX animations over time.
Added a `ColoredAnimatedSprite` similar to the existing `ColoredSprite`.
Refactored the latter to avoid code duplication.

## 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-27 01:59:11 +02:00
committed by Scott Anderson
parent 182a42e0db
commit cea22c8220
20 changed files with 431 additions and 178 deletions
+1
View File
@@ -239,6 +239,7 @@ export class NukeExecution implements Execution {
}
}
this.active = false;
this.nuke.setReachedTarget();
this.nuke.delete(false);
// Record stats
@@ -181,7 +181,6 @@ export class SAMLauncherExecution implements Execution {
);
// Delete warheads
mirvWarheadTargets.forEach((u) => {
u.setInterceptedBySam();
u.delete();
});
} else if (target !== null) {
@@ -66,7 +66,6 @@ export class SAMMissileExecution implements Execution {
this._owner.id(),
);
this.active = false;
this.target.setInterceptedBySam();
this.target.delete(true, this._owner);
this.SAMMissile.delete(false);
+1
View File
@@ -52,6 +52,7 @@ export class ShellExecution implements Execution {
if (result === true) {
this.active = false;
this.target.modifyHealth(-this.effectOnTarget(), this._owner);
this.shell.setReachedTarget();
this.shell.delete(false);
return;
} else {
+2 -2
View File
@@ -369,8 +369,8 @@ export interface Unit {
targetUnit(): Unit | undefined;
setTargetedBySAM(targeted: boolean): void;
targetedBySAM(): boolean;
setInterceptedBySam(): void;
interceptedBySam(): boolean;
setReachedTarget(): void;
reachedTarget(): boolean;
// Health
hasHealth(): boolean;
+1 -1
View File
@@ -73,7 +73,7 @@ export interface UnitUpdate {
pos: TileRef;
lastPos: TileRef;
isActive: boolean;
wasIntercepted: boolean;
reachedTarget: boolean;
retreating: boolean;
targetUnitId?: number; // Only for trade ships
targetTile?: TileRef; // Only for nukes
+2 -2
View File
@@ -93,8 +93,8 @@ export class UnitView {
isActive(): boolean {
return this.data.isActive;
}
wasInterceptedBySAM(): boolean {
return this.data.wasIntercepted;
reachedTarget(): boolean {
return this.data.reachedTarget;
}
hasHealth(): boolean {
return this.data.health !== undefined;
+6 -6
View File
@@ -21,7 +21,7 @@ export class UnitImpl implements Unit {
private _lastTile: TileRef;
private _retreating: boolean = false;
private _targetedBySAM = false;
private _interceptedBySAM = false;
private _reachedTarget = false;
private _lastSetSafeFromPirates: number; // Only for trade ships
private _constructionType: UnitType | undefined;
private _lastOwner: PlayerImpl | null = null;
@@ -104,7 +104,7 @@ export class UnitImpl implements Unit {
ownerID: this._owner.smallID(),
lastOwnerID: this._lastOwner?.smallID(),
isActive: this._active,
wasIntercepted: this._interceptedBySAM,
reachedTarget: this._reachedTarget,
retreating: this._retreating,
pos: this._tile,
lastPos: this._lastTile,
@@ -324,12 +324,12 @@ export class UnitImpl implements Unit {
return this._targetedBySAM;
}
setInterceptedBySam(): void {
this._interceptedBySAM = true;
setReachedTarget(): void {
this._reachedTarget = true;
}
interceptedBySam(): boolean {
return this._interceptedBySAM;
reachedTarget(): boolean {
return this._reachedTarget;
}
setSafeFromPirates(): void {