diff --git a/package.json b/package.json index 5c96232..1944e38 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "pixi.js": "^8.5.1", "preact": "^10.24.3", "prettier": "^3.4.2", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "use-local-storage-state": "^19.5.0" }, "devDependencies": { "@jest/globals": "^29.7.0", @@ -32,6 +33,7 @@ "@preact/preset-vite": "^2.9.1", "@typescript-eslint/eslint-plugin": "^8.9.0", "@typescript-eslint/parser": "^8.9.0", + "core-js": "^3.40.0", "eslint": "^9.12.0", "jest": "^29.7.0", "ts-jest": "^29.2.5", diff --git a/src/components/menus/team-editor.tsx b/src/components/menus/team-editor.tsx deleted file mode 100644 index 1394fd1..0000000 --- a/src/components/menus/team-editor.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export default function AccountMenu() { - return ( -
-

Team Editor

-

You can edit teams on this page. Teams are saved to your account.

-
- -
-
- ); -} diff --git a/src/logic/teams.ts b/src/logic/teams.ts index 60e43fd..4a0aac1 100644 --- a/src/logic/teams.ts +++ b/src/logic/teams.ts @@ -23,6 +23,7 @@ export interface Team { name: string; group: TeamGroup; worms: WormIdentity[]; + flag?: string; // For net games only playerUserId: string | null; ammo: Record; @@ -87,6 +88,10 @@ export class InternalTeam implements Team { return this.team.group; } + get flag() { + return this.team.flag; + } + get health() { return this.worms.map((w) => w.health).reduce((a, b) => a + b); } diff --git a/src/main.tsx b/src/main.tsx index 0c17816..29da5ce 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,7 +1,8 @@ import { render } from "preact"; import "./index.css"; -import { App } from "./components/app"; +import { App } from "./frontend/components/app"; import { loadAssets } from "./assets"; +import "core-js/actual/typed-array/from-base64" void loadAssets(); diff --git a/src/overlays/gameStateOverlay.ts b/src/overlays/gameStateOverlay.ts index 2a2810d..e65933f 100644 --- a/src/overlays/gameStateOverlay.ts +++ b/src/overlays/gameStateOverlay.ts @@ -1,4 +1,4 @@ -import { Container, Graphics, Text, Ticker, UPDATE_PRIORITY } from "pixi.js"; +import { Assets, Container, Graphics, Text, Texture, Ticker, UPDATE_PRIORITY } from "pixi.js"; import { GameState } from "../logic/gamestate"; import { applyGenericBoxStyle, @@ -30,6 +30,7 @@ export class GameStateOverlay { private readonly winddial: WindDial; private readonly bottomOfScreenY; private readonly roundTimerWidth; + private readonly teamFlagTextures: Record = {}; constructor( private readonly ticker: Ticker, @@ -72,6 +73,10 @@ export class GameStateOverlay { .reduceRight((value, team) => Math.max(value, team.maxHealth), 0); this.gameState.getActiveTeams().forEach((t) => { this.visibleTeamHealth[t.name] = t.health; + if (t.flag) { + this.teamFlagTextures[t.name] = Texture.from(`team-flag-${t.name}`, true); + console.log(this.teamFlagTextures); + } }); } @@ -166,6 +171,7 @@ export class GameStateOverlay { }); const border = team === this.gameState.activeTeam ? 0xffffff : undefined; const nameTagStartX = centerX - nameTag.width - 120; + const flagBoxWidth = 24; const nameWidth = nameTag.width + 6; const nameHeight = nameTag.height - 2; @@ -175,10 +181,30 @@ export class GameStateOverlay { .fill(); nameTag.position.set(nameTagStartX, teamBottomY - 8); + // Render flag + if (this.teamFlagTextures[team.name]) { + const flagX = (centerX - TEAM_HEALTH_WIDTH_PX / 2) - 8; + const flagY = teamBottomY - 2; + applyGenericBoxStyle(this.gfx, border) + .roundRect( + flagX, + flagY, + nameHeight, + nameHeight, + 4, + ) + .stroke() + .fill(); + this.gfx.texture(this.teamFlagTextures[team.name], undefined, flagX + 2, flagY + 2, nameHeight - 4, nameHeight - 4); + } + + + + // Render health box. applyGenericBoxStyle(this.gfx, border) .roundRect( - centerX - TEAM_HEALTH_WIDTH_PX / 2, + (centerX - TEAM_HEALTH_WIDTH_PX / 2) + flagBoxWidth, teamBottomY - 2, TEAM_HEALTH_WIDTH_PX, nameHeight, @@ -197,7 +223,7 @@ export class GameStateOverlay { }) .setFillStyle({ color: bg }) .roundRect( - centerX - 99, + centerX + flagBoxWidth - 99, teamBottomY + 1, (TEAM_HEALTH_WIDTH_PX - 4) * teamHealthPercentage - 2, nameHeight - 5, diff --git a/src/scenarios/tiledMap.ts b/src/scenarios/tiledMap.ts index 57f472c..7ccec89 100644 --- a/src/scenarios/tiledMap.ts +++ b/src/scenarios/tiledMap.ts @@ -1,4 +1,4 @@ -import { Ticker } from "pixi.js"; +import { Assets, Ticker } from "pixi.js"; import { Background } from "../entities/background"; import { BitmapTerrain } from "../entities/bitmapTerrain"; import type { Game } from "../game"; @@ -29,6 +29,7 @@ import { scenarioParser } from "../levels/scenarioParser"; import { WeaponTarget } from "../entities/phys/target"; import { WormSpawnRecordedState } from "../entities/state/wormSpawn"; import { InnerWormState } from "../entities/playable/wormState"; +import { getLocalTeams } from "../settings"; const weapons = [ WeaponBazooka, @@ -60,6 +61,24 @@ export default async function runScenario(game: Game) { level.terrain.destructible, ); + // Hack! + const [topLocalTeam] = getLocalTeams(); + if (topLocalTeam) { + const team = level.teams[0] = { + ...level.teams[0], + name: topLocalTeam.name, + worms: level.teams[0].worms.map((w,i) => ({ + ...w, + name: topLocalTeam.worms[i] + })), + flag: topLocalTeam.flagb64, + } + if (team.flag) { + Assets.add({alias: `team-flag-${team.name}`, src: team.flag}); + await Assets.load(`team-flag-${team.name}`); + } + } + const gameState = new GameState(level.teams, world, level.rules); const recordedGameplayKey = `wormgine_recorded_${new Date().toISOString()}`; diff --git a/src/settings.ts b/src/settings.ts index 94c74ba..0a05d05 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1 +1,20 @@ export const SOUND_EFFECT_VOLUME = 0.5; + +export const WORMGINE_STORAGE_KEY_TEAMS = "wormgine.teams"; + +export interface StoredTeam { + name: string; + worms: string[]; + flagb64?: string; + synced: boolean|null, +} + + +export function getLocalTeams(): StoredTeam[] { + const item = localStorage.getItem(WORMGINE_STORAGE_KEY_TEAMS); + if (!item) { + return []; + } + // TODO: Sanitize. + return JSON.parse(item); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 61cbb72..a9401e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,6 +1786,11 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +core-js@^3.40.0: + version "3.40.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.40.0.tgz#2773f6b06877d8eda102fc42f828176437062476" + integrity sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ== + create-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" @@ -3754,6 +3759,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-local-storage-state@^19.5.0: + version "19.5.0" + resolved "https://registry.yarnpkg.com/use-local-storage-state/-/use-local-storage-state-19.5.0.tgz#25bf46dd45b491020c03db516b46a4ff93b3a923" + integrity sha512-sUJAyFvsmqMpBhdwaRr7GTKkkoxb6PWeNVvpBDrLuwQF1PpbJRKIbOYeLLeqJI7B3wdfFlLLCBbmOdopiSTBOw== + uuid@10: version "10.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"