Merge branch 'main' into patterned-territory

This commit is contained in:
Aotumuri
2025-05-23 07:59:39 +09:00
committed by GitHub
25 changed files with 193 additions and 116 deletions
+3 -2
View File
@@ -188,9 +188,10 @@ export class ClientGameRunner {
}
private saveGame(update: WinUpdate) {
if (this.myPlayer === null) throw new Error("Not initialized");
const players: PlayerRecord[] = [
{
ip: null,
playerID: this.myPlayer.id(),
persistentID: getPersistentIDFromCookie(),
username: this.lobby.playerName,
clientID: this.lobby.clientID,
@@ -211,7 +212,7 @@ export class ClientGameRunner {
}
const record = createGameRecord(
this.lobby.gameStartInfo.gameID,
this.lobby.gameStartInfo,
this.lobby.gameStartInfo.config,
players,
// Not saving turns locally
[],
+1 -1
View File
@@ -47,7 +47,7 @@ export function endGame(gameRecord: GameRecord) {
}
const stats = getStats();
const gameStat = stats[gameRecord.id];
const gameStat = stats[gameRecord.info.gameID];
if (!gameStat) {
consolex.log("LocalPersistantStats: game not found");
+2 -2
View File
@@ -176,7 +176,7 @@ export class LocalServer {
}
const players: PlayerRecord[] = [
{
ip: null,
playerID: this.lobbyConfig.clientID, // hack?
persistentID: getPersistentIDFromCookie(),
username: this.lobbyConfig.playerName,
clientID: this.lobbyConfig.clientID,
@@ -188,7 +188,7 @@ export class LocalServer {
}
const record = createGameRecord(
this.lobbyConfig.gameStartInfo.gameID,
this.lobbyConfig.gameStartInfo,
this.lobbyConfig.gameStartInfo.config,
players,
this.turns,
this.startedAt,
+1 -1
View File
@@ -305,7 +305,7 @@ class Client {
playerName: this.usernameInput?.getCurrentUsername() ?? "",
token: localStorage.getItem("token") ?? getPersistentIDFromCookie(),
clientID: lobby.clientID,
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.gameStartInfo,
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info,
gameRecord: lobby.gameRecord,
},
() => {
@@ -1,4 +1,5 @@
import nuke from "../../../resources/sprites/nukeExplosion.png";
import SAMExplosion from "../../../resources/sprites/samExplosion.png";
import { AnimatedSprite } from "./AnimatedSprite";
import { FxType } from "./fx/Fx";
@@ -22,6 +23,15 @@ const ANIMATED_SPRITE_CONFIG: Partial<Record<FxType, AnimatedSpriteConfig>> = {
originX: 30,
originY: 30,
},
[FxType.SAMExplosion]: {
url: SAMExplosion,
frameWidth: 48,
frameCount: 9,
frameDuration: 70,
looping: false,
originX: 23,
originY: 19,
},
};
const animatedSpriteImageMap: Map<FxType, CanvasImageSource> = new Map();
+1
View File
@@ -4,4 +4,5 @@ export interface Fx {
export enum FxType {
Nuke = "Nuke",
SAMExplosion = "SAMExplosion",
}
+34
View File
@@ -0,0 +1,34 @@
import { AnimatedSprite } from "../AnimatedSprite";
import { createAnimatedSpriteForUnit } from "../AnimatedSpriteLoader";
import { Fx, FxType } from "./Fx";
/**
* Explosion effect: sprite animation of an explosion
*/
export class SAMExplosionFx implements Fx {
private lifeTime: number = 0;
private explosionSprite: AnimatedSprite | null;
constructor(
private x: number,
private y: number,
private duration: number,
) {
this.explosionSprite = createAnimatedSpriteForUnit(FxType.SAMExplosion);
}
renderTick(frameTime: number, ctx: CanvasRenderingContext2D): boolean {
if (this.explosionSprite) {
this.lifeTime += frameTime;
if (this.lifeTime >= this.duration) {
return false;
}
if (this.explosionSprite.isActive()) {
this.explosionSprite.update(frameTime);
this.explosionSprite.draw(ctx, this.x, this.y);
return true;
}
return false;
}
return false;
}
}
+28 -9
View File
@@ -4,6 +4,7 @@ import { GameView, UnitView } from "../../../core/game/GameView";
import { loadAllAnimatedSpriteImages } from "../AnimatedSpriteLoader";
import { Fx } from "../fx/Fx";
import { NukeExplosionFx, ShockwaveFx } from "../fx/NukeFx";
import { SAMExplosionFx } from "../fx/SAMExplosionFx";
import { Layer } from "./Layer";
export class FxLayer implements Layer {
@@ -35,25 +36,43 @@ export class FxLayer implements Layer {
switch (unit.type()) {
case UnitType.AtomBomb:
case UnitType.MIRVWarhead:
this.handleNukeExplosion(unit, 70);
this.handleNukes(unit, 70);
break;
case UnitType.HydrogenBomb:
this.handleNukeExplosion(unit, 250);
this.handleNukes(unit, 250);
break;
}
}
handleNukeExplosion(unit: UnitView, shockwaveRadius: number) {
handleNukes(unit: UnitView, shockwaveRadius: number) {
if (!unit.isActive()) {
const x = this.game.x(unit.lastTile());
const y = this.game.y(unit.lastTile());
const nuke = new NukeExplosionFx(x, y, 1000);
this.allFx.push(nuke as Fx);
const shockwave = new ShockwaveFx(x, y, 1500, shockwaveRadius);
this.allFx.push(shockwave as Fx);
if (unit.wasInterceptedBySAM()) {
this.handleSAMInterception(unit);
} else {
// Kaboom
this.handleNukeExplosion(unit, shockwaveRadius);
}
}
}
handleNukeExplosion(unit: UnitView, shockwaveRadius: number) {
const x = this.game.x(unit.lastTile());
const y = this.game.y(unit.lastTile());
const nuke = new NukeExplosionFx(x, y, 1000);
this.allFx.push(nuke as Fx);
const shockwave = new ShockwaveFx(x, y, 1500, shockwaveRadius);
this.allFx.push(shockwave as Fx);
}
handleSAMInterception(unit: UnitView) {
const x = this.game.x(unit.lastTile());
const y = this.game.y(unit.lastTile());
const interception = new SAMExplosionFx(x, y, 1000);
this.allFx.push(interception as Fx);
const shockwave = new ShockwaveFx(x, y, 800, 40);
this.allFx.push(shockwave as Fx);
}
async init() {
this.redraw();
try {