mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-25 06:22:47 +00:00
nuke icon (#207)
- **feat: white nuke icon next to name if player nukes you**  - **feat: red nuke icon if player sends nuke towards you** - 
This commit is contained in:
@@ -2,8 +2,11 @@ import {
|
||||
AllPlayers,
|
||||
Cell,
|
||||
Game,
|
||||
NukeType,
|
||||
nukeTypes,
|
||||
Player,
|
||||
PlayerType,
|
||||
UnitType,
|
||||
} from "../../../core/game/Game";
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
@@ -15,6 +18,8 @@ import allianceRequestIcon from "../../../../resources/images/AllianceRequestIco
|
||||
import crownIcon from "../../../../resources/images/CrownIcon.svg";
|
||||
import targetIcon from "../../../../resources/images/TargetIcon.svg";
|
||||
import embargoIcon from "../../../../resources/images/EmbargoIcon.svg";
|
||||
import nukeWhiteIcon from "../../../../resources/images/NukeIconWhite.svg";
|
||||
import nukeRedIcon from "../../../../resources/images/NukeIconRed.svg";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { createCanvas, renderTroops } from "../../Utils";
|
||||
@@ -47,6 +52,8 @@ export class NameLayer implements Layer {
|
||||
private targetIconImage: HTMLImageElement;
|
||||
private crownIconImage: HTMLImageElement;
|
||||
private embargoIconImage: HTMLImageElement;
|
||||
private nukeWhiteIconImage: HTMLImageElement;
|
||||
private nukeRedIconImage: HTMLImageElement;
|
||||
private container: HTMLDivElement;
|
||||
private myPlayer: PlayerView | null = null;
|
||||
private firstPlace: PlayerView | null = null;
|
||||
@@ -69,6 +76,10 @@ export class NameLayer implements Layer {
|
||||
this.targetIconImage.src = targetIcon;
|
||||
this.embargoIconImage = new Image();
|
||||
this.embargoIconImage.src = embargoIcon;
|
||||
this.nukeWhiteIconImage = new Image();
|
||||
this.nukeWhiteIconImage.src = nukeWhiteIcon;
|
||||
this.nukeRedIconImage = new Image();
|
||||
this.nukeRedIconImage.src = nukeRedIcon;
|
||||
}
|
||||
|
||||
resizeCanvas() {
|
||||
@@ -405,6 +416,47 @@ export class NameLayer implements Layer {
|
||||
existingEmbargo.remove();
|
||||
}
|
||||
|
||||
const nukesSentByOtherPlayer = this.game.units().filter((unit) => {
|
||||
const isSendingNuke = render.player.id() == unit.owner().id();
|
||||
const notMyPlayer = unit.owner().id() != myPlayer.id();
|
||||
return (
|
||||
nukeTypes.includes(unit.type()) &&
|
||||
isSendingNuke &&
|
||||
notMyPlayer &&
|
||||
unit.isActive()
|
||||
);
|
||||
});
|
||||
const isMyPlayerTarget = nukesSentByOtherPlayer.find((unit) => {
|
||||
const detonationDst = unit.detonationDst();
|
||||
const targetId = this.game.owner(detonationDst).id();
|
||||
return targetId == this.myPlayer.id();
|
||||
});
|
||||
const existingNuke = iconsDiv.querySelector(
|
||||
'[data-icon="nuke"]',
|
||||
) as HTMLImageElement;
|
||||
|
||||
if (existingNuke) {
|
||||
if (nukesSentByOtherPlayer.length == 0) {
|
||||
existingNuke.remove();
|
||||
} else if (
|
||||
isMyPlayerTarget &&
|
||||
existingNuke.src != this.nukeRedIconImage.src
|
||||
) {
|
||||
existingNuke.src = this.nukeRedIconImage.src;
|
||||
} else if (
|
||||
!isMyPlayerTarget &&
|
||||
existingNuke.src != this.nukeWhiteIconImage.src
|
||||
) {
|
||||
existingNuke.src = this.nukeWhiteIconImage.src;
|
||||
}
|
||||
} else if (myPlayer && nukesSentByOtherPlayer.length > 0) {
|
||||
if (!existingNuke) {
|
||||
const icon = isMyPlayerTarget
|
||||
? this.nukeRedIconImage.src
|
||||
: this.nukeWhiteIconImage.src;
|
||||
iconsDiv.appendChild(this.createIconElement(icon, iconSize, "nuke"));
|
||||
}
|
||||
}
|
||||
// Update all icon sizes
|
||||
const icons = iconsDiv.getElementsByTagName("img");
|
||||
for (const icon of icons) {
|
||||
|
||||
@@ -245,10 +245,10 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
|
||||
let outerColor = this.theme.territoryColor(unit.owner().info());
|
||||
if (unit.targetId()) {
|
||||
if (unit.warshipTargetId()) {
|
||||
const targetOwner = this.game
|
||||
.units()
|
||||
.find((u) => u.id() == unit.targetId())
|
||||
.find((u) => u.id() == unit.warshipTargetId())
|
||||
?.owner();
|
||||
if (targetOwner == this.myPlayer) {
|
||||
outerColor = colord({ r: 200, b: 0, g: 0 });
|
||||
|
||||
@@ -55,7 +55,9 @@ export class NukeExecution implements Execution {
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
this.nuke = this.player.buildUnit(this.type, 0, spawn);
|
||||
this.nuke = this.player.buildUnit(this.type, 0, spawn, {
|
||||
detonationDst: this.dst,
|
||||
});
|
||||
if (this.mg.hasOwner(this.dst)) {
|
||||
const target = this.mg.owner(this.dst) as Player;
|
||||
if (this.type == UnitType.AtomBomb) {
|
||||
|
||||
@@ -47,12 +47,9 @@ export class TradeShipExecution implements Execution {
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
this.tradeShip = this.origOwner.buildUnit(
|
||||
UnitType.TradeShip,
|
||||
0,
|
||||
spawn,
|
||||
this._dstPort,
|
||||
);
|
||||
this.tradeShip = this.origOwner.buildUnit(UnitType.TradeShip, 0, spawn, {
|
||||
dstPort: this._dstPort,
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.tradeShip.isActive()) {
|
||||
|
||||
@@ -79,10 +79,10 @@ export class WarshipExecution implements Execution {
|
||||
.filter((u) => u != this.warship)
|
||||
.filter((u) => !u.owner().isAlliedWith(this.warship.owner()))
|
||||
.filter((u) => !this.alreadySentShell.has(u))
|
||||
.filter(
|
||||
(u) =>
|
||||
u.type() != UnitType.TradeShip || u.dstPort().owner() != this.owner(),
|
||||
);
|
||||
.filter((u) => {
|
||||
const portOwner = u.dstPort() ? u.dstPort().owner() : null;
|
||||
return u.type() != UnitType.TradeShip || portOwner != this.owner();
|
||||
});
|
||||
|
||||
this.target =
|
||||
ships.sort((a, b) => {
|
||||
@@ -110,7 +110,7 @@ export class WarshipExecution implements Execution {
|
||||
return distSortUnit(this.mg, this.warship)(a, b);
|
||||
})[0] ?? null;
|
||||
|
||||
this.warship.setTarget(this.target);
|
||||
this.warship.setWarshipTarget(this.target);
|
||||
if (this.target == null || this.target.type() != UnitType.TradeShip) {
|
||||
// Patrol unless we are hunting down a tradeship
|
||||
const result = this.pathfinder.nextTile(
|
||||
|
||||
+22
-12
@@ -80,11 +80,14 @@ export enum UnitType {
|
||||
MIRVWarhead = "MIRV Warhead",
|
||||
Construction = "Construction",
|
||||
}
|
||||
export type NukeType =
|
||||
| UnitType.AtomBomb
|
||||
| UnitType.HydrogenBomb
|
||||
| UnitType.MIRVWarhead
|
||||
| UnitType.MIRV;
|
||||
|
||||
export const nukeTypes = [
|
||||
UnitType.AtomBomb,
|
||||
UnitType.HydrogenBomb,
|
||||
UnitType.MIRVWarhead,
|
||||
UnitType.MIRV,
|
||||
] as UnitType[];
|
||||
export type NukeType = (typeof nukeTypes)[number];
|
||||
|
||||
export enum Relation {
|
||||
Hostile = 0,
|
||||
@@ -197,6 +200,13 @@ export class PlayerInfo {
|
||||
) {}
|
||||
}
|
||||
|
||||
// Some units have info specific to them
|
||||
export interface UnitSpecificInfos {
|
||||
dstPort?: Unit; // Only for trade ships
|
||||
detonationDst?: TileRef; // Only for nukes
|
||||
warshipTarget?: Unit;
|
||||
}
|
||||
|
||||
export interface Unit {
|
||||
id(): number;
|
||||
|
||||
@@ -216,9 +226,12 @@ export interface Unit {
|
||||
hasHealth(): boolean;
|
||||
health(): number;
|
||||
modifyHealth(delta: number): void;
|
||||
// State for warships (currently)
|
||||
setTarget(target: Unit): void;
|
||||
target(): Unit;
|
||||
|
||||
setWarshipTarget(target: Unit): void; // warship only
|
||||
warshipTarget(): Unit;
|
||||
|
||||
dstPort(): Unit; // Only for trade ships
|
||||
detonationDst(): TileRef; // Only for nukes
|
||||
|
||||
// Mutations
|
||||
setTroops(troops: number): void;
|
||||
@@ -230,9 +243,6 @@ export interface Unit {
|
||||
|
||||
// Updates
|
||||
toUpdate(): UnitUpdate;
|
||||
|
||||
// Only for some types, otherwise return null
|
||||
dstPort(): Unit;
|
||||
}
|
||||
|
||||
export interface TerraNullius {
|
||||
@@ -290,7 +300,7 @@ export interface Player {
|
||||
type: UnitType,
|
||||
troops: number,
|
||||
tile: TileRef,
|
||||
dstPort?: Unit,
|
||||
unitSpecificInfos?: UnitSpecificInfos,
|
||||
): Unit;
|
||||
captureUnit(unit: Unit): void;
|
||||
|
||||
|
||||
@@ -70,9 +70,11 @@ export interface UnitUpdate {
|
||||
pos: TileRef;
|
||||
lastPos: TileRef;
|
||||
isActive: boolean;
|
||||
dstPortId?: number; // Only for trade ships
|
||||
detonationDst?: TileRef; // Only for nukes
|
||||
warshipTargetId?: number;
|
||||
health?: number;
|
||||
constructionType?: UnitType;
|
||||
targetId?: number;
|
||||
}
|
||||
|
||||
export interface AttackUpdate {
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
GameUpdates,
|
||||
MapPos,
|
||||
MessageType,
|
||||
nukeTypes,
|
||||
Player,
|
||||
PlayerActions,
|
||||
PlayerProfile,
|
||||
@@ -92,8 +93,23 @@ export class UnitView {
|
||||
constructionType(): UnitType | undefined {
|
||||
return this.data.constructionType;
|
||||
}
|
||||
targetId() {
|
||||
return this.data.targetId;
|
||||
dstPortId(): number {
|
||||
if (this.type() != UnitType.TradeShip) {
|
||||
throw Error("Must be a trade ship");
|
||||
}
|
||||
return this.data.dstPortId;
|
||||
}
|
||||
detonationDst(): TileRef {
|
||||
if (!nukeTypes.includes(this.type())) {
|
||||
throw Error("Must be a nuke");
|
||||
}
|
||||
return this.data.detonationDst;
|
||||
}
|
||||
warshipTargetId(): number {
|
||||
if (this.type() != UnitType.Warship) {
|
||||
throw Error("Must be a warship");
|
||||
}
|
||||
return this.data.warshipTargetId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
EmojiMessage,
|
||||
PlayerProfile,
|
||||
Attack,
|
||||
UnitSpecificInfos,
|
||||
} from "./Game";
|
||||
import { AttackUpdate, PlayerUpdate } from "./GameUpdates";
|
||||
import { GameUpdateType } from "./GameUpdates";
|
||||
@@ -648,7 +649,7 @@ export class PlayerImpl implements Player {
|
||||
type: UnitType,
|
||||
troops: number,
|
||||
spawnTile: TileRef,
|
||||
dstPort?: Unit,
|
||||
unitSpecificInfos: UnitSpecificInfos = {},
|
||||
): UnitImpl {
|
||||
const cost = this.mg.unitInfo(type).cost(this);
|
||||
const b = new UnitImpl(
|
||||
@@ -658,7 +659,7 @@ export class PlayerImpl implements Player {
|
||||
troops,
|
||||
this.mg.nextUnitID(),
|
||||
this,
|
||||
dstPort,
|
||||
unitSpecificInfos,
|
||||
);
|
||||
this._units.push(b);
|
||||
this.removeGold(cost);
|
||||
|
||||
+27
-11
@@ -1,4 +1,4 @@
|
||||
import { MessageType } from "./Game";
|
||||
import { MessageType, nukeTypes, UnitSpecificInfos } from "./Game";
|
||||
import { UnitUpdate } from "./GameUpdates";
|
||||
import { GameUpdateType } from "./GameUpdates";
|
||||
import { simpleHash, toInt, within, withinInt } from "../Util";
|
||||
@@ -6,6 +6,7 @@ import { Unit, TerraNullius, UnitType, Player, UnitInfo } from "./Game";
|
||||
import { GameImpl } from "./GameImpl";
|
||||
import { PlayerImpl } from "./PlayerImpl";
|
||||
import { TileRef } from "./GameMap";
|
||||
import { consolex } from "../Consolex";
|
||||
|
||||
export class UnitImpl implements Unit {
|
||||
private _active = true;
|
||||
@@ -16,6 +17,10 @@ export class UnitImpl implements Unit {
|
||||
|
||||
private _constructionType: UnitType = undefined;
|
||||
|
||||
private _dstPort: Unit | null = null; // Only for trade ships
|
||||
private _detonationDst: TileRef | null = null; // Only for nukes
|
||||
private _warshipTarget: Unit | null = null;
|
||||
|
||||
constructor(
|
||||
private _type: UnitType,
|
||||
private mg: GameImpl,
|
||||
@@ -23,10 +28,13 @@ export class UnitImpl implements Unit {
|
||||
private _troops: number,
|
||||
private _id: number,
|
||||
public _owner: PlayerImpl,
|
||||
private _dstPort?: Unit,
|
||||
unitsSpecificInfos: UnitSpecificInfos = {},
|
||||
) {
|
||||
this._health = toInt(this.mg.unitInfo(_type).maxHealth ?? 1);
|
||||
this._lastTile = _tile;
|
||||
this._dstPort = unitsSpecificInfos.dstPort;
|
||||
this._detonationDst = unitsSpecificInfos.detonationDst;
|
||||
this._warshipTarget = unitsSpecificInfos.warshipTarget;
|
||||
}
|
||||
|
||||
id() {
|
||||
@@ -34,6 +42,8 @@ export class UnitImpl implements Unit {
|
||||
}
|
||||
|
||||
toUpdate(): UnitUpdate {
|
||||
const warshipTarget = this.warshipTarget();
|
||||
const dstPort = this.dstPort();
|
||||
return {
|
||||
type: GameUpdateType.Unit,
|
||||
unitType: this._type,
|
||||
@@ -45,7 +55,9 @@ export class UnitImpl implements Unit {
|
||||
lastPos: this._lastTile,
|
||||
health: this.hasHealth() ? Number(this._health) : undefined,
|
||||
constructionType: this._constructionType,
|
||||
targetId: this.target() ? this.target().id() : null,
|
||||
dstPortId: dstPort ? dstPort.id() : null,
|
||||
warshipTargetId: warshipTarget ? warshipTarget.id() : null,
|
||||
detonationDst: this.detonationDst(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,15 +165,19 @@ export class UnitImpl implements Unit {
|
||||
return `Unit:${this._type},owner:${this.owner().name()}`;
|
||||
}
|
||||
|
||||
setWarshipTarget(target: Unit) {
|
||||
this._warshipTarget = target;
|
||||
}
|
||||
|
||||
warshipTarget(): Unit {
|
||||
return this._warshipTarget;
|
||||
}
|
||||
|
||||
detonationDst(): TileRef {
|
||||
return this._detonationDst;
|
||||
}
|
||||
|
||||
dstPort(): Unit {
|
||||
return this._dstPort;
|
||||
}
|
||||
|
||||
setTarget(target: Unit) {
|
||||
this._target = target;
|
||||
}
|
||||
|
||||
target() {
|
||||
return this._target;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user