diff --git a/proprietary/images/doomsdayicon.png b/proprietary/images/doomsdayicon.png new file mode 100644 index 000000000..4750b2cc7 Binary files /dev/null and b/proprietary/images/doomsdayicon.png differ diff --git a/resources/lang/en.json b/resources/lang/en.json index 37934c1a0..ec7dffb01 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -270,7 +270,8 @@ "factory": "Factory", "airfield": "Airfield", "air_field": "Airfield", - "fighter_jet": "Fighter Jet" + "fighter_jet": "Fighter Jet", + "doomsday_device": "Doomsday Device" }, "user_setting": { "title": "User Settings", @@ -487,7 +488,8 @@ "hospital": "Lowers troop casualties from combat", "academy": "Increases troop speed and enemy losses in combat", "airfield": "Send bombers, fighterjets and paratroopers", - "fighter_jet": "Destroys bombers and fighters jets" + "fighter_jet": "Destroys bombers and fighters jets", + "doomsday_device": "A mysterious device with unknown effects" }, "not_enough_money": "Not enough money" }, @@ -554,9 +556,8 @@ "reject_alliance": "Reject", "alliance_renewed": "Your alliance with {name} has been renewed", "ignore": "Ignore", - "paratrooper_sent": "Paratrooper" - }, - "game_messages": { + "paratrooper_sent": "Paratrooper", + "doomsday_triggered": "{player}'s Doomsday Device has been Triggered!", "max_paratrooper_units_reached": "Maximum number of paratrooper planes reached.", "incoming_paratrooper_attack": "Incoming Paratrooper Attack from {attackerName}" }, diff --git a/resources/lang/tr.json b/resources/lang/tr.json index 4185669f8..a992b5765 100644 --- a/resources/lang/tr.json +++ b/resources/lang/tr.json @@ -554,9 +554,7 @@ "reject_alliance": "Reddet", "alliance_renewed": "{name} ile ittifakınız yenilendi", "ignore": "Yoksay", - "paratrooper_sent": "Paraşütçü" - }, - "game_messages": { + "paratrooper_sent": "Paraşütçü", "max_paratrooper_units_reached": "Maksimum paraşütçü uçağı sayısına ulaşıldı.", "incoming_paratrooper_attack": "{attackerName} tarafından Gelen Paraşütçü Saldırısı" }, diff --git a/src/client/Utils.ts b/src/client/Utils.ts index 14697d7e2..890d1927f 100644 --- a/src/client/Utils.ts +++ b/src/client/Utils.ts @@ -196,6 +196,7 @@ export function getMessageTypeClasses(type: MessageType): string { case MessageType.WARN: case MessageType.PEACE_TIMER_BLOCKED: case MessageType.TRADE_SHIP_TURNED_AROUND: + case MessageType.DOOMSDAY_DEVICE_ACTIVATED: return severityColors["warn"]; case MessageType.WAR_DECLARED: return severityColors["warn"]; // war start: highlight prominently diff --git a/src/client/graphics/fx/NukeFx.ts b/src/client/graphics/fx/NukeFx.ts index 080f2d4f8..ba5382e05 100644 --- a/src/client/graphics/fx/NukeFx.ts +++ b/src/client/graphics/fx/NukeFx.ts @@ -131,3 +131,90 @@ export function nukeFxFactory( } return nukeFx; } + +/** + * Slower, larger, more lingering FX for Doomsday device trigger. + * - Larger shockwave (longer duration) + * - Higher density lingering smoke/fire with longer fade + */ +export function doomsdayFxFactory( + animatedSpriteLoader: AnimatedSpriteLoader, + x: number, + y: number, + radius: number, + game: GameView, + scale: number = 1.2, +): Fx[] { + const fx: Fx[] = []; + // Central sustained explosion sprite (scaled up) + fx.push( + new SpriteFx( + animatedSpriteLoader, + x, + y, + FxType.Nuke, + undefined, + undefined, + undefined, + scale, + ), + ); + // Slower shockwave (duration 6000ms, larger reach) + fx.push(new ShockwaveFx(x, y, 6000, radius * 2)); + // Lingering debris plan (higher density, longer fade via FadeFx wrapper) + const debrisPlan: Array<{ + type: FxType; + radiusFactor: number; + density: number; + fadeIn: number; + fadeOut: number; + }> = [ + { + type: FxType.MiniFire, + radiusFactor: 1.3, + density: 1 / 18, + fadeIn: 0.2, + fadeOut: 1.5, + }, + { + type: FxType.MiniSmoke, + radiusFactor: 1.4, + density: 1 / 20, + fadeIn: 0.2, + fadeOut: 1.8, + }, + { + type: FxType.MiniBigSmoke, + radiusFactor: 1.2, + density: 1 / 50, + fadeIn: 0.2, + fadeOut: 2.0, + }, + { + type: FxType.MiniSmokeAndFire, + radiusFactor: 1.1, + density: 1 / 55, + fadeIn: 0.2, + fadeOut: 2.2, + }, + ]; + for (const { type, radiusFactor, density, fadeIn, fadeOut } of debrisPlan) { + const count = Math.max(0, Math.floor(radius * density)); + for (let i = 0; i < count; i++) { + const angle = Math.random() * 2 * Math.PI; + const distance = Math.random() * (radius * radiusFactor); + const sx = Math.floor(x + Math.cos(angle) * distance * 0.5); + const sy = Math.floor(y + Math.sin(angle) * distance * 0.5); + if (game.isValidCoord(sx, sy) && game.isLand(game.ref(sx, sy))) { + fx.push( + new FadeFx( + new SpriteFx(animatedSpriteLoader, sx, sy, type), + fadeIn, + fadeOut, + ) as Fx, + ); + } + } + } + return fx; +} diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts index 0d81bf4a8..79562ff4b 100644 --- a/src/client/graphics/layers/BuildMenu.ts +++ b/src/client/graphics/layers/BuildMenu.ts @@ -1,5 +1,6 @@ import { LitElement, css, html } from "lit"; import { customElement, property, state } from "lit/decorators.js"; +import doomsdayDeviceIcon from "../../../../proprietary/images/doomsdayicon.png"; import researchLabIcon from "../../../../proprietary/images/researchlab.png"; import airfieldIcon from "../../../../resources/images/AirfieldIcon.svg"; import warshipIcon from "../../../../resources/images/BattleshipIconWhite.svg"; @@ -154,6 +155,13 @@ const buildTable: BuildItemDisplay[][] = [ key: "unit_type.defense_post", countable: true, }, + { + unitType: UnitType.DoomsdayDevice, + icon: doomsdayDeviceIcon, + description: "build_menu.desc.doomsday_device", + key: "unit_type.doomsday_device", + countable: true, + }, ], ]; diff --git a/src/client/graphics/layers/ControlPanel2.ts b/src/client/graphics/layers/ControlPanel2.ts index 8b6bad681..e65971d44 100644 --- a/src/client/graphics/layers/ControlPanel2.ts +++ b/src/client/graphics/layers/ControlPanel2.ts @@ -145,6 +145,7 @@ export class ControlPanel2 extends LitElement implements Layer { "SAM Launcher": "/images/SamLauncherIconWhite.svg", "Air Field": "/images/AirfieldIcon.svg", "Defense Post": "/images/ShieldIconWhite.svg", + "Doomsday Device": "/images/Doomsdayicon.png", }; // Per-unit icon scale used for small inline icons in this panel @@ -159,6 +160,7 @@ export class ControlPanel2 extends LitElement implements Layer { [UnitType.SAMLauncher]: 1, [UnitType.Airfield]: 1, [UnitType.DefensePost]: 1, + [UnitType.DoomsdayDevice]: 1, }; private iconPixelSize(t: UnitType | null, base = 16): number { @@ -198,6 +200,7 @@ export class ControlPanel2 extends LitElement implements Layer { UnitType.Academy, UnitType.Factory, UnitType.City, + UnitType.DoomsdayDevice, ]; private readonly investmentRequestHandler = (event: Event) => { @@ -1364,6 +1367,7 @@ export class ControlPanel2 extends LitElement implements Layer { UnitType.Academy, UnitType.ResearchLab, UnitType.Factory, + UnitType.DoomsdayDevice, ].map((s) => { return html`