mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-27 15:54:24 +00:00
Add red warning circle when nuke would break alliance (#2728)
## Description: When placing a nuke (Atom Bomb or Hydrogen Bomb), the range circle now turns red to warn players when the attack would break an alliance. <img width="456" height="333" alt="Screenshot 2025-12-28 211927" src="https://github.com/user-attachments/assets/dfe6f874-3f8b-4662-8877-0af30aa20139" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: abodcraft1 --------- Co-authored-by: iamlewis <lewismmmm@gmail.com>
This commit is contained in:
@@ -454,6 +454,7 @@ export class SpriteFactory {
|
||||
stage: PIXI.Container,
|
||||
pos: { x: number; y: number },
|
||||
level?: number,
|
||||
targetingAlly: boolean = false,
|
||||
): PIXI.Container | null {
|
||||
if (stage === undefined) throw new Error("Not initialized");
|
||||
const parentContainer = new PIXI.Container();
|
||||
@@ -478,10 +479,18 @@ export class SpriteFactory {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
// Add warning colors (red/orange) when targeting an ally to indicate alliance will break
|
||||
const isNuke = type === UnitType.AtomBomb || type === UnitType.HydrogenBomb;
|
||||
const fillColor = targetingAlly && isNuke ? 0xff6b35 : 0xffffff;
|
||||
const fillAlpha = targetingAlly && isNuke ? 0.35 : 0.2;
|
||||
const strokeColor = targetingAlly && isNuke ? 0xff4444 : 0xffffff;
|
||||
const strokeAlpha = targetingAlly && isNuke ? 0.8 : 0.5;
|
||||
const strokeWidth = targetingAlly && isNuke ? 2 : 1;
|
||||
|
||||
circle
|
||||
.circle(0, 0, radius)
|
||||
.fill({ color: 0xffffff, alpha: 0.2 })
|
||||
.stroke({ width: 1, color: 0xffffff, alpha: 0.5 });
|
||||
.fill({ color: fillColor, alpha: fillAlpha })
|
||||
.stroke({ width: strokeWidth, color: strokeColor, alpha: strokeAlpha });
|
||||
parentContainer.addChild(circle);
|
||||
parentContainer.position.set(pos.x, pos.y);
|
||||
parentContainer.scale.set(this.transformHandler.scale);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { OutlineFilter } from "pixi-filters";
|
||||
import * as PIXI from "pixi.js";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { wouldNukeBreakAlliance } from "../../../core/execution/Util";
|
||||
import {
|
||||
BuildableUnit,
|
||||
Cell,
|
||||
@@ -65,6 +66,7 @@ export class StructureIconsLayer implements Layer {
|
||||
priceBox: { height: number; y: number; paddingX: number; minWidth: number };
|
||||
range: PIXI.Container | null;
|
||||
rangeLevel?: number;
|
||||
targetingAlly?: boolean;
|
||||
buildableUnit: BuildableUnit;
|
||||
} | null = null;
|
||||
private pixicanvas: HTMLCanvasElement;
|
||||
@@ -258,6 +260,29 @@ export class StructureIconsLayer implements Layer {
|
||||
tileRef = this.game.ref(tile.x, tile.y);
|
||||
}
|
||||
|
||||
// Check if targeting an ally (for nuke warning visual)
|
||||
// Uses shared logic with NukeExecution.maybeBreakAlliances()
|
||||
let targetingAlly = false;
|
||||
const myPlayer = this.game.myPlayer();
|
||||
const nukeType = this.ghostUnit.buildableUnit.type;
|
||||
if (
|
||||
tileRef &&
|
||||
myPlayer &&
|
||||
(nukeType === UnitType.AtomBomb || nukeType === UnitType.HydrogenBomb)
|
||||
) {
|
||||
// Only check if player has allies
|
||||
const allies = myPlayer.allies();
|
||||
if (allies.length > 0) {
|
||||
targetingAlly = wouldNukeBreakAlliance({
|
||||
gm: this.game,
|
||||
targetTile: tileRef,
|
||||
magnitude: this.game.config().nukeMagnitudes(nukeType),
|
||||
allySmallIds: new Set(allies.map((a) => a.smallID())),
|
||||
threshold: this.game.config().nukeAllianceBreakThreshold(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.game
|
||||
?.myPlayer()
|
||||
?.actions(tileRef)
|
||||
@@ -292,7 +317,7 @@ export class StructureIconsLayer implements Layer {
|
||||
this.updateGhostPrice(unit.cost ?? 0, showPrice);
|
||||
|
||||
const targetLevel = this.resolveGhostRangeLevel(unit);
|
||||
this.updateGhostRange(targetLevel);
|
||||
this.updateGhostRange(targetLevel, targetingAlly);
|
||||
|
||||
if (unit.canUpgrade) {
|
||||
this.potentialUpgrade = this.renders.find(
|
||||
@@ -470,18 +495,23 @@ export class StructureIconsLayer implements Layer {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private updateGhostRange(level?: number) {
|
||||
private updateGhostRange(level?: number, targetingAlly: boolean = false) {
|
||||
if (!this.ghostUnit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ghostUnit.range && this.ghostUnit.rangeLevel === level) {
|
||||
if (
|
||||
this.ghostUnit.range &&
|
||||
this.ghostUnit.rangeLevel === level &&
|
||||
this.ghostUnit.targetingAlly === targetingAlly
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ghostUnit.range?.destroy();
|
||||
this.ghostUnit.range = null;
|
||||
this.ghostUnit.rangeLevel = level;
|
||||
this.ghostUnit.targetingAlly = targetingAlly;
|
||||
|
||||
const position = this.ghostUnit.container.position;
|
||||
const range = this.factory.createRange(
|
||||
@@ -489,6 +519,7 @@ export class StructureIconsLayer implements Layer {
|
||||
this.ghostStage,
|
||||
{ x: position.x, y: position.y },
|
||||
level,
|
||||
targetingAlly,
|
||||
);
|
||||
if (range) {
|
||||
this.ghostUnit.range = range;
|
||||
|
||||
Reference in New Issue
Block a user