diff --git a/resources/images/buildings/oil-rig_2623991.png b/resources/images/buildings/oil-rig_2623991.png new file mode 100644 index 000000000..a1dc158e9 Binary files /dev/null and b/resources/images/buildings/oil-rig_2623991.png differ diff --git a/resources/images/oil-rig_2623991.svg b/resources/images/oil-rig_2623991.svg new file mode 100644 index 000000000..3f6e52ec6 --- /dev/null +++ b/resources/images/oil-rig_2623991.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/lang/en.json b/resources/lang/en.json index 83160eb11..7483a3159 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -147,8 +147,8 @@ "build_desc": "Description", "build_city": "City", "build_city_desc": "Increases your max population. Useful when you can't expand your territory or you're about to hit your population limit.", - "build_factory": "Factory", - "build_factory_desc": "Automatically builds railroads to nearby cities, ports and other factories, and can also link up with friendly neighbors. Trains spawn regularly and give you a fixed amount of gold for each building they visit along the route, with extra gold for visiting your neighbors' buildings.", + "build_oil_rig": "Oil Rig", + "build_oil_rig_desc": "Oil rigs can only be placed on oil fields. Automatically builds railroads to nearby cities, ports and other oil rigs, and can also link up with friendly neighbors. Trains spawn regularly and give you a fixed amount of gold for each building they visit along the route, with extra gold for visiting your neighbors' buildings.", "build_defense": "Defense Post", "build_defense_desc": "Increases defenses around nearby borders, which show a checkered pattern. Attacks from enemies are slower and have more casualties.", "build_port": "Port", @@ -482,7 +482,7 @@ "atom_bomb": "Atom Bomb", "hydrogen_bomb": "Hydrogen Bomb", "mirv": "MIRV", - "factory": "Factory" + "oil_rig": "Oil Rig" }, "user_setting": { "title": "Settings", @@ -525,8 +525,8 @@ "build_controls": "Build Controls", "build_city": "Build City", "build_city_desc": "Build a City under your cursor.", - "build_factory": "Build Factory", - "build_factory_desc": "Build a Factory under your cursor.", + "build_oil_rig": "Build Oil Rig", + "build_oil_rig_desc": "Build a Oil Rig under your cursor.", "build_defense_post": "Build Defense Post", "build_defense_post_desc": "Build a Defense Post under your cursor.", "build_port": "Build Port", @@ -686,7 +686,7 @@ "port": "Sends trade ships to generate gold", "defense_post": "Increases defenses of nearby borders", "city": "Increases max population", - "factory": "Creates railroads and spawns trains" + "oil_rig": "Oil rigs can only be placed on oil fields. Creates railroads and spawns trains" }, "not_enough_money": "Not enough money" }, @@ -927,7 +927,7 @@ "saml": "SAM Launcher", "silo": "Missile Silo", "wshp": "Warship", - "fact": "Factory", + "orig": "Oil Rig", "trade": "Trade Ship", "trans": "Transport Ship", "abomb": "Atom Bomb", diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index 95ba88880..57f04639a 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -966,18 +966,18 @@ export class HelpModal extends BaseModal { - ${translateText("help_modal.build_factory")} + ${translateText("help_modal.build_oil_rig")} - ${translateText("help_modal.build_factory_desc")} + ${translateText("help_modal.build_oil_rig_desc")} diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 45d1188a3..87a32965a 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -216,7 +216,7 @@ export class InputHandler { modifierKey: isMac ? "MetaLeft" : "ControlLeft", altKey: "AltLeft", buildCity: "Digit1", - buildFactory: "Digit2", + buildOilRig: "Digit2", buildPort: "Digit3", buildDefensePost: "Digit4", buildMissileSilo: "Digit5", @@ -396,9 +396,9 @@ export class InputHandler { this.setGhostStructure(UnitType.City); } - if (e.code === this.keybinds.buildFactory) { + if (e.code === this.keybinds.buildOilRig) { e.preventDefault(); - this.setGhostStructure(UnitType.Factory); + this.setGhostStructure(UnitType.OilRig); } if (e.code === this.keybinds.buildPort) { diff --git a/src/client/JoinLobbyModal.ts b/src/client/JoinLobbyModal.ts index 22e674fda..ac0ad0486 100644 --- a/src/client/JoinLobbyModal.ts +++ b/src/client/JoinLobbyModal.ts @@ -478,7 +478,7 @@ export class JoinLobbyModal extends BaseModal { "SAM Launcher": "unit_type.sam_launcher", "Missile Silo": "unit_type.missile_silo", Warship: "unit_type.warship", - Factory: "unit_type.factory", + OilRig: "unit_type.oil_rig", "Atom Bomb": "unit_type.atom_bomb", "Hydrogen Bomb": "unit_type.hydrogen_bomb", MIRV: "unit_type.mirv", diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index a9eb3bc66..d701ae95f 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -22,7 +22,7 @@ const isMac = const DefaultKeybinds: Record = { toggleView: "Space", buildCity: "Digit1", - buildFactory: "Digit2", + buildOilRig: "Digit2", buildPort: "Digit3", buildDefensePost: "Digit4", buildMissileSilo: "Digit5", @@ -490,12 +490,12 @@ export class UserSettingModal extends BaseModal { > diff --git a/src/client/components/GameConfigSettings.ts b/src/client/components/GameConfigSettings.ts index b981a89fe..bef51827b 100644 --- a/src/client/components/GameConfigSettings.ts +++ b/src/client/components/GameConfigSettings.ts @@ -98,7 +98,7 @@ const unitOptions: { type: UnitType; translationKey: string }[] = [ { type: UnitType.AtomBomb, translationKey: "unit_type.atom_bomb" }, { type: UnitType.HydrogenBomb, translationKey: "unit_type.hydrogen_bomb" }, { type: UnitType.MIRV, translationKey: "unit_type.mirv" }, - { type: UnitType.Factory, translationKey: "unit_type.factory" }, + { type: UnitType.OilRig, translationKey: "unit_type.oil_rig" }, ]; const MAP_ICON = svg`
${this.displayUnitCount(player, UnitType.City, cityIcon)} - ${this.displayUnitCount(player, UnitType.Factory, factoryIcon)} + ${this.displayUnitCount(player, UnitType.OilRig, factoryIcon)} ${this.displayUnitCount(player, UnitType.Port, portIcon)} ${this.displayUnitCount( player, diff --git a/src/client/graphics/layers/RailroadLayer.ts b/src/client/graphics/layers/RailroadLayer.ts index 1b3815ac6..216303b46 100644 --- a/src/client/graphics/layers/RailroadLayer.ts +++ b/src/client/graphics/layers/RailroadLayer.ts @@ -29,7 +29,7 @@ type RailRef = { const SNAPPABLE_STRUCTURES: UnitType[] = [ UnitType.Port, UnitType.City, - UnitType.Factory, + UnitType.OilRig, ]; export class RailTileChangedEvent implements GameEvent { constructor(public tile: TileRef) {} diff --git a/src/client/graphics/layers/StructureDrawingUtils.ts b/src/client/graphics/layers/StructureDrawingUtils.ts index 9fe6a940b..91e17d0a6 100644 --- a/src/client/graphics/layers/StructureDrawingUtils.ts +++ b/src/client/graphics/layers/StructureDrawingUtils.ts @@ -4,8 +4,8 @@ import { Cell, UnitType } from "../../../core/game/Game"; import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; import { TransformHandler } from "../TransformHandler"; import anchorIcon from "/images/AnchorIcon.png?url"; +import factoryIcon from "/images/buildings/oil-rig_2623991.png?url"; import cityIcon from "/images/CityIcon.png?url"; -import factoryIcon from "/images/FactoryUnit.png?url"; import missileSiloIcon from "/images/MissileSiloUnit.png?url"; import SAMMissileIcon from "/images/SamLauncherUnit.png?url"; import shieldIcon from "/images/ShieldIcon.png?url"; @@ -13,7 +13,7 @@ import shieldIcon from "/images/ShieldIcon.png?url"; export const STRUCTURE_SHAPES: Partial> = { [UnitType.City]: "circle", [UnitType.Port]: "pentagon", - [UnitType.Factory]: "circle", + [UnitType.OilRig]: "circle", [UnitType.DefensePost]: "octagon", [UnitType.SAMLauncher]: "square", [UnitType.MissileSilo]: "triangle", @@ -57,7 +57,7 @@ export class SpriteFactory { { iconPath: string; image: HTMLImageElement | null } > = new Map([ [UnitType.City, { iconPath: cityIcon, image: null }], - [UnitType.Factory, { iconPath: factoryIcon, image: null }], + [UnitType.OilRig, { iconPath: factoryIcon, image: null }], [UnitType.DefensePost, { iconPath: shieldIcon, image: null }], [UnitType.Port, { iconPath: anchorIcon, image: null }], [UnitType.MissileSilo, { iconPath: missileSiloIcon, image: null }], @@ -464,7 +464,7 @@ export class SpriteFactory { case UnitType.SAMLauncher: radius = this.game.config().samRange(level ?? 1); break; - case UnitType.Factory: + case UnitType.OilRig: radius = this.game.config().trainStationMaxRange(); break; case UnitType.DefensePost: diff --git a/src/client/graphics/layers/StructureIconsLayer.ts b/src/client/graphics/layers/StructureIconsLayer.ts index 9055e47e3..afbe05540 100644 --- a/src/client/graphics/layers/StructureIconsLayer.ts +++ b/src/client/graphics/layers/StructureIconsLayer.ts @@ -84,7 +84,7 @@ export class StructureIconsLayer implements Layer { private factory: SpriteFactory; private readonly structures: Map = new Map([ [UnitType.City, { visible: true }], - [UnitType.Factory, { visible: true }], + [UnitType.OilRig, { visible: true }], [UnitType.DefensePost, { visible: true }], [UnitType.Port, { visible: true }], [UnitType.MissileSilo, { visible: true }], diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index 819c4e3f7..1580d411b 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -9,8 +9,8 @@ import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView, UnitView } from "../../../core/game/GameView"; import cityIcon from "/images/buildings/cityAlt1.png?url"; -import factoryIcon from "/images/buildings/factoryAlt1.png?url"; import shieldIcon from "/images/buildings/fortAlt3.png?url"; +import oilRigIcon from "/images/buildings/oil-rig_2623991.png?url"; import anchorIcon from "/images/buildings/port1.png?url"; import missileSiloIcon from "/images/buildings/silo1.png?url"; import SAMMissileIcon from "/images/buildings/silo4.png?url"; @@ -49,8 +49,8 @@ export class StructureLayer implements Layer { borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, - [UnitType.Factory]: { - icon: factoryIcon, + [UnitType.OilRig]: { + icon: oilRigIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index d8edb3f02..798dd41b9 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -114,7 +114,7 @@ export class UILayer implements Layer { break; } case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: case UnitType.DefensePost: case UnitType.Port: case UnitType.MissileSilo: @@ -334,7 +334,7 @@ export class UILayer implements Layer { ? unit.missileReadinesss() : this.deletionProgress(this.game, unit); case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: case UnitType.Port: case UnitType.DefensePost: return this.deletionProgress(this.game, unit); diff --git a/src/client/graphics/layers/UnitDisplay.ts b/src/client/graphics/layers/UnitDisplay.ts index 82d530dcb..0bcc65105 100644 --- a/src/client/graphics/layers/UnitDisplay.ts +++ b/src/client/graphics/layers/UnitDisplay.ts @@ -12,19 +12,19 @@ import { UIState } from "../UIState"; import { Layer } from "./Layer"; import warshipIcon from "/images/BattleshipIconWhite.svg?url"; import cityIcon from "/images/CityIconWhite.svg?url"; -import factoryIcon from "/images/FactoryIconWhite.svg?url"; import goldCoinIcon from "/images/GoldCoinIcon.svg?url"; import mirvIcon from "/images/MIRVIcon.svg?url"; import missileSiloIcon from "/images/MissileSiloIconWhite.svg?url"; import hydrogenBombIcon from "/images/MushroomCloudIconWhite.svg?url"; import atomBombIcon from "/images/NukeIconWhite.svg?url"; +import factoryIcon from "/images/oil-rig_2623991.svg?url"; import portIcon from "/images/PortIcon.svg?url"; import samLauncherIcon from "/images/SamLauncherIconWhite.svg?url"; import defensePostIcon from "/images/ShieldIconWhite.svg?url"; const BUILDABLE_UNITS: UnitType[] = [ UnitType.City, - UnitType.Factory, + UnitType.OilRig, UnitType.Port, UnitType.DefensePost, UnitType.MissileSilo, @@ -44,7 +44,7 @@ export class UnitDisplay extends LitElement implements Layer { private keybinds: Record = {}; private _cities = 0; private _warships = 0; - private _factories = 0; + private _oilRigs = 0; private _missileSilo = 0; private _port = 0; private _defensePost = 0; @@ -113,7 +113,7 @@ export class UnitDisplay extends LitElement implements Layer { this._port = player.totalUnitLevels(UnitType.Port); this._defensePost = player.totalUnitLevels(UnitType.DefensePost); this._samLauncher = player.totalUnitLevels(UnitType.SAMLauncher); - this._factories = player.totalUnitLevels(UnitType.Factory); + this._oilRigs = player.totalUnitLevels(UnitType.OilRig); this._warships = player.totalUnitLevels(UnitType.Warship); this.requestUpdate(); } @@ -147,10 +147,10 @@ export class UnitDisplay extends LitElement implements Layer { )} ${this.renderUnitItem( factoryIcon, - this._factories, - UnitType.Factory, - "factory", - this.keybinds["buildFactory"]?.key ?? "2", + this._oilRigs, + UnitType.OilRig, + "oil_rig", + this.keybinds["buildOilRig"]?.key ?? "2", )} ${this.renderUnitItem( portIcon, diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index eb21c193f..f7b3a94ad 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -107,7 +107,7 @@ export class GameRunner { this.game.addExecution(...this.execManager.nationExecutions()); } this.game.addExecution(new WinCheckExecution()); - if (!this.game.config().isUnitDisabled(UnitType.Factory)) { + if (!this.game.config().isUnitDisabled(UnitType.OilRig)) { this.game.addExecution( new RecomputeRailClusterExecution(this.game.railNetwork()), ); diff --git a/src/core/StatsSchemas.ts b/src/core/StatsSchemas.ts index c37607bd6..16239e57c 100644 --- a/src/core/StatsSchemas.ts +++ b/src/core/StatsSchemas.ts @@ -34,7 +34,7 @@ export const otherUnits = [ "wshp", "silo", "saml", - "fact", + "orig", ] as const; export const OtherUnitSchema = z.enum(otherUnits); export type OtherUnit = z.infer; @@ -45,7 +45,7 @@ export type OtherUnitType = | UnitType.Port | UnitType.SAMLauncher | UnitType.Warship - | UnitType.Factory; + | UnitType.OilRig; export const unitTypeToOtherUnit = { [UnitType.City]: "city", @@ -54,7 +54,7 @@ export const unitTypeToOtherUnit = { [UnitType.Port]: "port", [UnitType.SAMLauncher]: "saml", [UnitType.Warship]: "wshp", - [UnitType.Factory]: "fact", + [UnitType.OilRig]: "orig", } as const satisfies Record; // Attacks diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 55fbab613..e8f3da497 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -132,7 +132,7 @@ export interface Config { numTradeShips: number, ): number; trainGold(rel: "self" | "team" | "ally" | "other"): Gold; - trainSpawnRate(numPlayerFactories: number): number; + trainSpawnRate(numPlayerOilRigs: number): number; trainStationMinRange(): number; trainStationMaxRange(): number; railroadMaxSize(): number; @@ -170,6 +170,7 @@ export interface Config { structureMinDist(): number; isReplay(): boolean; allianceExtensionPromptOffset(): number; + randomSeed(): number; } export interface Theme { diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index b4ce99715..65f255b05 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -265,10 +265,10 @@ export class DefaultConfig implements Config { return BigInt(this._gameConfig.startingGold ?? 0); } - trainSpawnRate(numPlayerFactories: number): number { - // hyperbolic decay, midpoint at 10 factories - // expected number of trains = numPlayerFactories / trainSpawnRate(numPlayerFactories) - return (numPlayerFactories + 10) * 18; + trainSpawnRate(numPlayerOilRigs: number): number { + // hyperbolic decay, midpoint at 10 oil rigs + // expected number of trains = numPlayerOilRigs / trainSpawnRate(numPlayerOilRigs) + return (numPlayerOilRigs + 10) * 18; } trainGold(rel: "self" | "team" | "ally" | "other"): Gold { const multiplier = this.goldMultiplier(); @@ -362,7 +362,7 @@ export class DefaultConfig implements Config { (numUnits: number) => Math.min(1_000_000, Math.pow(2, numUnits) * 125_000), UnitType.Port, - UnitType.Factory, + UnitType.OilRig, ), constructionDuration: this.instantBuild() ? 0 : 2 * 10, upgradable: true, @@ -436,12 +436,12 @@ export class DefaultConfig implements Config { upgradable: true, }; break; - case UnitType.Factory: + case UnitType.OilRig: info = { cost: this.costWrapper( (numUnits: number) => Math.min(1_000_000, Math.pow(2, numUnits) * 125_000), - UnitType.Factory, + UnitType.OilRig, UnitType.Port, ), constructionDuration: this.instantBuild() ? 0 : 2 * 10, @@ -911,4 +911,8 @@ export class DefaultConfig implements Config { allianceExtensionPromptOffset(): number { return 300; // 30 seconds before expiration } + + randomSeed(): number { + return simpleHash(JSON.stringify(this.gameConfig())); + } } diff --git a/src/core/configuration/PastelTheme.ts b/src/core/configuration/PastelTheme.ts index 23ae4e653..a06603aa9 100644 --- a/src/core/configuration/PastelTheme.ts +++ b/src/core/configuration/PastelTheme.ts @@ -15,6 +15,7 @@ export class PastelTheme implements Theme { private nationColorAllocator = new ColorAllocator(nationColors, nationColors); private background = colord("rgb(60,60,60)"); + private oilField = colord("rgb(55,55,85)"); private shore = colord("rgb(204,203,158)"); private falloutColors = [ colord("rgb(120,255,71)"), // Original color @@ -147,6 +148,9 @@ export class PastelTheme implements Theme { // | **Water (Shore)** | 0 | Fixed: `rgb(100, 143, 255)` | Light blue near land. | // | **Water (Deep)** | 1 - 10+ | `rgb(70, 132, 180)` - `rgb(61, 123, 171)` | Darker blue, adjusted slightly by distance to land. | terrainColor(gm: GameMap, tile: TileRef): Colord { + if (gm.hasOilField(tile)) { + return this.oilField; + } const mag = gm.magnitude(tile); if (gm.isShore(tile)) { return this.shore; diff --git a/src/core/configuration/PastelThemeDark.ts b/src/core/configuration/PastelThemeDark.ts index 2cff80685..780d05501 100644 --- a/src/core/configuration/PastelThemeDark.ts +++ b/src/core/configuration/PastelThemeDark.ts @@ -19,6 +19,9 @@ export class PastelThemeDark extends PastelTheme { // | **Water (Deep)** | 1 - 10+ | `rgb(22, 19, 38)` - `rgb(14, 11, 30)` | Very dark blue/black. | terrainColor(gm: GameMap, tile: TileRef): Colord { + if (gm.hasOilField(tile)) { + return colord("rgb(50,40,80)"); + } const mag = gm.magnitude(tile); if (gm.isShore(tile)) { return this.darkShore; diff --git a/src/core/execution/CityExecution.ts b/src/core/execution/CityExecution.ts index fd31c940e..9f3919e38 100644 --- a/src/core/execution/CityExecution.ts +++ b/src/core/execution/CityExecution.ts @@ -35,7 +35,7 @@ export class CityExecution implements Execution { const nearbyFactory = this.mg.hasUnitNearby( this.city.tile()!, this.mg.config().trainStationMaxRange(), - UnitType.Factory, + UnitType.OilRig, ); if (nearbyFactory) { this.mg.addExecution(new TrainStationExecution(this.city)); diff --git a/src/core/execution/ConstructionExecution.ts b/src/core/execution/ConstructionExecution.ts index 2fdd92da1..41b398156 100644 --- a/src/core/execution/ConstructionExecution.ts +++ b/src/core/execution/ConstructionExecution.ts @@ -2,10 +2,10 @@ import { Execution, Game, Player, Tick, Unit, UnitType } from "../game/Game"; import { TileRef } from "../game/GameMap"; import { CityExecution } from "./CityExecution"; import { DefensePostExecution } from "./DefensePostExecution"; -import { FactoryExecution } from "./FactoryExecution"; import { MirvExecution } from "./MIRVExecution"; import { MissileSiloExecution } from "./MissileSiloExecution"; import { NukeExecution } from "./NukeExecution"; +import { OilRigExecution } from "./OilRigExecution"; import { PortExecution } from "./PortExecution"; import { SAMLauncherExecution } from "./SAMLauncherExecution"; import { WarshipExecution } from "./WarshipExecution"; @@ -141,8 +141,8 @@ export class ConstructionExecution implements Execution { case UnitType.City: this.mg.addExecution(new CityExecution(this.structure!)); break; - case UnitType.Factory: - this.mg.addExecution(new FactoryExecution(this.structure!)); + case UnitType.OilRig: + this.mg.addExecution(new OilRigExecution(this.structure!)); break; default: console.warn( @@ -159,7 +159,7 @@ export class ConstructionExecution implements Execution { case UnitType.DefensePost: case UnitType.SAMLauncher: case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: return true; default: return false; diff --git a/src/core/execution/FactoryExecution.ts b/src/core/execution/OilRigExecution.ts similarity index 75% rename from src/core/execution/FactoryExecution.ts rename to src/core/execution/OilRigExecution.ts index 92908ed39..afd2e4130 100644 --- a/src/core/execution/FactoryExecution.ts +++ b/src/core/execution/OilRigExecution.ts @@ -1,12 +1,12 @@ import { Execution, Game, Unit, UnitType } from "../game/Game"; import { TrainStationExecution } from "./TrainStationExecution"; -export class FactoryExecution implements Execution { +export class OilRigExecution implements Execution { private active: boolean = true; private game: Game; private stationCreated = false; - constructor(private factory: Unit) {} + constructor(private oilRig: Unit) {} init(mg: Game, ticks: number): void { this.game = mg; @@ -17,7 +17,7 @@ export class FactoryExecution implements Execution { this.createStation(); this.stationCreated = true; } - if (!this.factory.isActive()) { + if (!this.oilRig.isActive()) { this.active = false; return; } @@ -33,12 +33,12 @@ export class FactoryExecution implements Execution { private createStation(): void { const structures = this.game.nearbyUnits( - this.factory.tile()!, + this.oilRig.tile()!, this.game.config().trainStationMaxRange(), - [UnitType.City, UnitType.Port, UnitType.Factory], + [UnitType.City, UnitType.Port, UnitType.OilRig], ); - this.game.addExecution(new TrainStationExecution(this.factory, true)); + this.game.addExecution(new TrainStationExecution(this.oilRig, true)); for (const { unit } of structures) { if (!unit.hasTrainStation()) { this.game.addExecution(new TrainStationExecution(unit)); diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index 9483f1b70..cd10457e2 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -87,7 +87,7 @@ export class PortExecution implements Execution { const nearbyFactory = this.mg.hasUnitNearby( this.port.tile()!, this.mg.config().trainStationMaxRange(), - UnitType.Factory, + UnitType.OilRig, ); if (nearbyFactory) { this.mg.addExecution(new TrainStationExecution(this.port)); diff --git a/src/core/execution/TrainStationExecution.ts b/src/core/execution/TrainStationExecution.ts index 5f029a0cc..9936eca08 100644 --- a/src/core/execution/TrainStationExecution.ts +++ b/src/core/execution/TrainStationExecution.ts @@ -53,7 +53,7 @@ export class TrainStationExecution implements Execution { private shouldSpawnTrain(): boolean { const spawnRate = this.mg .config() - .trainSpawnRate(this.unit.owner().unitCount(UnitType.Factory)); + .trainSpawnRate(this.unit.owner().unitCount(UnitType.OilRig)); for (let i = 0; i < this.unit!.level(); i++) { if (this.random.chance(spawnRate)) { return true; diff --git a/src/core/execution/nation/NationNukeBehavior.ts b/src/core/execution/nation/NationNukeBehavior.ts index 7ab12df5e..885f0fe23 100644 --- a/src/core/execution/nation/NationNukeBehavior.ts +++ b/src/core/execution/nation/NationNukeBehavior.ts @@ -82,7 +82,7 @@ export class NationNukeBehavior { UnitType.MissileSilo, UnitType.Port, UnitType.SAMLauncher, - UnitType.Factory, + UnitType.OilRig, ); const structureTiles = structures.map((u) => u.tile()); const difficulty = this.game.config().gameConfig().difficulty; @@ -571,7 +571,7 @@ export class NationNukeBehavior { return 50_000 * level; case UnitType.Port: return 15_000 * level; - case UnitType.Factory: + case UnitType.OilRig: return 15_000 * level; default: return 0; diff --git a/src/core/execution/nation/NationStructureBehavior.ts b/src/core/execution/nation/NationStructureBehavior.ts index fcd30353d..5034a5bec 100644 --- a/src/core/execution/nation/NationStructureBehavior.ts +++ b/src/core/execution/nation/NationStructureBehavior.ts @@ -46,7 +46,7 @@ function getStructureRatios( ): Partial> { return { [UnitType.Port]: { ratioPerCity: 0.75, perceivedCostIncreasePerOwned: 1 }, - [UnitType.Factory]: { + [UnitType.OilRig]: { ratioPerCity: 0.75, perceivedCostIncreasePerOwned: 1, }, @@ -105,7 +105,7 @@ export class NationStructureBehavior { const buildOrder: UnitType[] = [ UnitType.DefensePost, UnitType.Port, - UnitType.Factory, + UnitType.OilRig, UnitType.SAMLauncher, UnitType.MissileSilo, ]; @@ -185,7 +185,7 @@ export class NationStructureBehavior { // Heavily reduce factory spawning if we have coastal tiles if ( - type === UnitType.Factory && + type === UnitType.OilRig && hasCoastalTiles && !gameConfig.isUnitDisabled(UnitType.Port) ) { @@ -446,7 +446,9 @@ export class NationStructureBehavior { const tiles = type === UnitType.Port ? this.randCoastalTileArray(25) - : randTerritoryTileArray(this.random, this.game, this.player, 25); + : type === UnitType.OilRig + ? this.randOilFieldTileArray(25) + : randTerritoryTileArray(this.random, this.game, this.player, 25); if (tiles.length === 0) return null; const valueFunction = this.structureSpawnTileValue(type); if (valueFunction === null) return null; @@ -470,6 +472,13 @@ export class NationStructureBehavior { return Array.from(this.arraySampler(tiles, numTiles)); } + private randOilFieldTileArray(numTiles: number): TileRef[] { + const tiles = Array.from(this.player.tiles()).filter((t) => + this.game.hasOilField(t), + ); + return Array.from(this.arraySampler(tiles, numTiles)); + } + private *arraySampler(a: T[], sampleSize: number): Generator { if (a.length <= sampleSize) { // Return all elements @@ -490,7 +499,7 @@ export class NationStructureBehavior { ): ((tile: TileRef) => number) | null { switch (type) { case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: case UnitType.MissileSilo: return this.interiorStructureValue(type); case UnitType.Port: @@ -652,7 +661,7 @@ export class NationStructureBehavior { for (const unit of player.units()) { switch (unit.type()) { case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: case UnitType.MissileSilo: case UnitType.Port: protectEntries.push({ diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 7381c35c7..69ea876de 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -249,7 +249,7 @@ export enum UnitType { MIRV = "MIRV", MIRVWarhead = "MIRV Warhead", Train = "Train", - Factory = "Factory", + OilRig = "OilRig", } export enum TrainType { @@ -264,7 +264,7 @@ const _structureTypes: ReadonlySet = new Set([ UnitType.SAMLauncher, UnitType.MissileSilo, UnitType.Port, - UnitType.Factory, + UnitType.OilRig, ]); export const StructureTypes: readonly UnitType[] = [..._structureTypes]; @@ -318,7 +318,7 @@ export interface UnitParamsMap { loaded?: boolean; }; - [UnitType.Factory]: Record; + [UnitType.OilRig]: Record; [UnitType.MissileSilo]: Record; diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 0339fdb33..9054fd5b1 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -41,6 +41,7 @@ import { } from "./Game"; import { GameMap, TileRef, TileUpdate } from "./GameMap"; import { GameUpdate, GameUpdateType } from "./GameUpdates"; +import { generateOilFields } from "./OilFieldGenerator"; import { PlayerImpl } from "./PlayerImpl"; import { RailNetwork } from "./RailNetwork"; import { createRailNetwork } from "./RailNetworkImpl"; @@ -117,6 +118,8 @@ export class GameImpl implements Game { } this.addPlayers(); + generateOilFields(this._map, this._config); + if (!_config.disableNavMesh()) { const graphBuilder = new AbstractGraphBuilder(this.miniGameMap); this._miniWaterGraph = graphBuilder.build(); @@ -962,6 +965,12 @@ export class GameImpl implements Game { setOwnerID(ref: TileRef, playerId: number): void { return this._map.setOwnerID(ref, playerId); } + hasOilField(ref: TileRef): boolean { + return this._map.hasOilField(ref); + } + setOilField(ref: TileRef, value: boolean): void { + return this._map.setOilField(ref, value); + } hasFallout(ref: TileRef): boolean { return this._map.hasFallout(ref); } diff --git a/src/core/game/GameMap.ts b/src/core/game/GameMap.ts index 136fdf1d9..351b42372 100644 --- a/src/core/game/GameMap.ts +++ b/src/core/game/GameMap.ts @@ -25,6 +25,8 @@ export interface GameMap { hasOwner(ref: TileRef): boolean; setOwnerID(ref: TileRef, playerId: number): void; + hasOilField(ref: TileRef): boolean; + setOilField(ref: TileRef, value: boolean): void; hasFallout(ref: TileRef): boolean; setFallout(ref: TileRef, value: boolean): void; isOnEdgeOfMap(ref: TileRef): boolean; @@ -76,6 +78,7 @@ export class GameMapImpl implements GameMap { // State bits (Uint16Array) private static readonly PLAYER_ID_MASK = 0xfff; + private static readonly OIL_FIELD_BIT = 12; private static readonly FALLOUT_BIT = 13; private static readonly DEFENSE_BONUS_BIT = 14; // Bit 15 still reserved @@ -192,6 +195,18 @@ export class GameMapImpl implements GameMap { (this.state[ref] & ~GameMapImpl.PLAYER_ID_MASK) | playerId; } + hasOilField(ref: TileRef): boolean { + return Boolean(this.state[ref] & (1 << GameMapImpl.OIL_FIELD_BIT)); + } + + setOilField(ref: TileRef, value: boolean): void { + if (value) { + this.state[ref] |= 1 << GameMapImpl.OIL_FIELD_BIT; + } else { + this.state[ref] &= ~(1 << GameMapImpl.OIL_FIELD_BIT); + } + } + hasFallout(ref: TileRef): boolean { return Boolean(this.state[ref] & (1 << GameMapImpl.FALLOUT_BIT)); } diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index f02e0e10b..773ee4685 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -34,6 +34,7 @@ import { PlayerUpdate, UnitUpdate, } from "./GameUpdates"; +import { generateOilFields } from "./OilFieldGenerator"; import { TerrainMapData } from "./TerrainMapLoader"; import { TerraNulliusImpl } from "./TerraNulliusImpl"; import { UnitGrid, UnitPredicate } from "./UnitGrid"; @@ -627,6 +628,7 @@ export class GameView implements GameMap { flag: nation.flag, } satisfies PlayerCosmetics); } + generateOilFields(this._map, this._config); } isOnEdgeOfMap(ref: TileRef): boolean { @@ -900,6 +902,12 @@ export class GameView implements GameMap { setOwnerID(ref: TileRef, playerId: number): void { return this._map.setOwnerID(ref, playerId); } + hasOilField(ref: TileRef): boolean { + return this._map.hasOilField(ref); + } + setOilField(ref: TileRef, value: boolean): void { + return this._map.setOilField(ref, value); + } hasFallout(ref: TileRef): boolean { return this._map.hasFallout(ref); } diff --git a/src/core/game/OilFieldGenerator.ts b/src/core/game/OilFieldGenerator.ts new file mode 100644 index 000000000..dab1be3f3 --- /dev/null +++ b/src/core/game/OilFieldGenerator.ts @@ -0,0 +1,258 @@ +import { Config } from "../configuration/Config"; +import { PseudoRandom } from "../PseudoRandom"; +import { simpleHash } from "../Util"; +import { GameMap, TileRef } from "./GameMap"; + +export function generateOilFields(map: GameMap, config: Config) { + const random = new PseudoRandom( + simpleHash("oil-fields-" + config.randomSeed()), + ); + + const numFields = random.nextInt(7, 10); + const width = map.width(); + const height = map.height(); + + // 1. Grid-based Seeding (Ensures spread across the map) + const gridDivs = Math.ceil(Math.sqrt(numFields + 2)); + const cellW = width / gridDivs; + const cellH = height / gridDivs; + + const cells: { r: number; c: number }[] = []; + for (let r = 0; r < gridDivs; r++) { + for (let c = 0; c < gridDivs; c++) { + cells.push({ r, c }); + } + } + + for (let i = cells.length - 1; i > 0; i--) { + const j = random.nextInt(0, i + 1); + [cells[i], cells[j]] = [cells[j], cells[i]]; + } + + const seeds: TileRef[] = []; + for (let i = 0; i < numFields && i < cells.length; i++) { + const cell = cells[i]; + let foundSeed = false; + for (let attempt = 0; attempt < 100; attempt++) { + const rx = random.nextInt( + Math.floor(cell.c * cellW + cellW * 0.1), + Math.floor((cell.c + 1) * cellW - cellW * 0.1), + ); + const ry = random.nextInt( + Math.floor(cell.r * cellH + cellH * 0.1), + Math.floor((cell.r + 1) * cellH - cellH * 0.1), + ); + if (map.isValidCoord(rx, ry)) { + const t = map.ref(rx, ry); + if (map.isLand(t)) { + seeds.push(t); + foundSeed = true; + break; + } + } + } + if (!foundSeed) { + for (let fallback = 0; fallback < 100; fallback++) { + const rx = random.nextInt(0, width); + const ry = random.nextInt(0, height); + const t = map.ref(rx, ry); + if (map.isLand(t)) { + seeds.push(t); + foundSeed = true; + break; + } + } + } + } + + // 2. Elliptical Noise Base + const totalLand = map.numLandTiles(); + const avgTilesPerField = (totalLand * 0.045) / numFields; + + for (const seed of seeds) { + const targetSize = avgTilesPerField * (0.6 + random.next() * 1.8); + const centerX = map.x(seed); + const centerY = map.y(seed); + + // Random rotation and aspect ratio (not too extreme to avoid "thin" shapes) + const angle = random.next() * Math.PI; + const cosA = Math.cos(angle); + const sinA = Math.sin(angle); + const ratio = 0.6 + random.next() * 0.4; // 1:1 to 1:1.6 + + // Rough radius based on area A = PI * r1 * r2 => A = PI * r * (r * ratio) + const r1 = Math.sqrt(targetSize / (Math.PI * ratio)); + const r2 = r1 * ratio; + + const fieldTiles = new Set(); + + // Fill an ellipse with noise - this is the "raw material" for the CA + const searchRadius = Math.ceil(Math.max(r1, r2) * 1.5); + for (let dx = -searchRadius; dx <= searchRadius; dx++) { + for (let dy = -searchRadius; dy <= searchRadius; dy++) { + const x = centerX + dx; + const y = centerY + dy; + if (!map.isValidCoord(x, y)) continue; + + // Rotated coordinate system + const rx = dx * cosA + dy * sinA; + const ry = -dx * sinA + dy * cosA; + + const dist = (rx * rx) / (r1 * r1) + (ry * ry) / (r2 * r2); + + // Add jittered probability to create organic edges + const prob = 0.85 - dist * 0.5; + if (random.next() < prob && map.isLand(map.ref(x, y))) { + const t = map.ref(x, y); + map.setOilField(t, true); + fieldTiles.add(t); + } + } + } + + // 3. Heavy Smoothing Pass (Cellular Automata) + // Running 5 passes creates very clean, lumpy organic blobs + for (let pass = 0; pass < 5; pass++) { + const toAdd: TileRef[] = []; + const toRemove: TileRef[] = []; + + // Slightly larger bounds to allow smoothing to expand/contract + const bounds = getBounds(map, fieldTiles, 2); + for (let x = bounds.minX; x <= bounds.maxX; x++) { + for (let y = bounds.minY; y <= bounds.maxY; y++) { + if (!map.isValidCoord(x, y)) continue; + const t = map.ref(x, y); + const isLand = map.isLand(t); + + let oilNeighbors = 0; + for (let dx = -1; dx <= 1; dx++) { + for (let dy = -1; dy <= 1; dy++) { + if (dx === 0 && dy === 0) continue; + const nx = x + dx, + ny = y + dy; + if ( + map.isValidCoord(nx, ny) && + map.hasOilField(map.ref(nx, ny)) + ) { + oilNeighbors++; + } + } + } + + if (map.hasOilField(t)) { + // Standard CA "Life" rules for smoothing: + // Keep if 4+ neighbors, remove if 3 or less (prunes thin parts) + if (oilNeighbors <= 3 || !isLand) { + toRemove.push(t); + } + } else if (isLand) { + // Fill if 5+ neighbors (fills gaps/holes) + if (oilNeighbors >= 5) { + toAdd.push(t); + } + } + } + } + + for (const t of toAdd) { + map.setOilField(t, true); + fieldTiles.add(t); + } + for (const t of toRemove) { + map.setOilField(t, false); + fieldTiles.delete(t); + } + } + + // 4. Guaranteed Hole Filling (Flood fill) + if (fieldTiles.size > 0) { + fillHoles(map, fieldTiles); + } + } +} + +function fillHoles(map: GameMap, fieldTiles: Set) { + const bounds = getBounds(map, fieldTiles, 1); + const outside = new Set(); + const queue: TileRef[] = []; + + for (let x = bounds.minX; x <= bounds.maxX; x++) { + for (let y = bounds.minY; y <= bounds.maxY; y++) { + if ( + x === bounds.minX || + x === bounds.maxX || + y === bounds.minY || + y === bounds.maxY + ) { + if (map.isValidCoord(x, y)) { + const t = map.ref(x, y); + if (!map.hasOilField(t)) { + outside.add(t); + queue.push(t); + } + } + } + } + } + + while (queue.length > 0) { + const curr = queue.shift()!; + const cx = map.x(curr); + const cy = map.y(curr); + const dirs = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + ]; + for (const [dx, dy] of dirs) { + const nx = cx + dx, + ny = cy + dy; + if ( + nx >= bounds.minX && + nx <= bounds.maxX && + ny >= bounds.minY && + ny <= bounds.maxY && + map.isValidCoord(nx, ny) + ) { + const next = map.ref(nx, ny); + if (!map.hasOilField(next) && !outside.has(next)) { + outside.add(next); + queue.push(next); + } + } + } + } + + for (let x = bounds.minX; x <= bounds.maxX; x++) { + for (let y = bounds.minY; y <= bounds.maxY; y++) { + if (map.isValidCoord(x, y)) { + const t = map.ref(x, y); + if (!map.hasOilField(t) && !outside.has(t) && map.isLand(t)) { + map.setOilField(t, true); + } + } + } + } +} + +function getBounds(map: GameMap, tiles: Set, margin: number) { + let minX = Infinity, + maxX = -Infinity, + minY = Infinity, + maxY = -Infinity; + for (const t of tiles) { + const x = map.x(t); + const y = map.y(t); + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + return { + minX: minX - margin, + maxX: maxX + margin, + minY: minY - margin, + maxY: maxY + margin, + }; +} diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 2f4d78dc0..1baeb91dc 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -1042,6 +1042,10 @@ export class PlayerImpl implements Player { (units === undefined || units.some((u) => isStructureType(u))) ? this.validStructureSpawnTiles(tile) : []; + const validOilRigTiles = + tile !== null && (units === undefined || units.includes(UnitType.OilRig)) + ? this.validStructureSpawnTiles(tile, true) + : []; return Object.values(UnitType) .filter((u) => units === undefined || units.includes(u)) .map((u) => { @@ -1053,7 +1057,11 @@ export class PlayerImpl implements Player { canUpgrade = existingUnit.id(); } if (tile !== null) { - canBuild = this.canBuild(u, tile, validTiles); + canBuild = this.canBuild( + u, + tile, + u === UnitType.OilRig ? validOilRigTiles : validTiles, + ); } } return { @@ -1117,8 +1125,12 @@ export class PlayerImpl implements Player { case UnitType.DefensePost: case UnitType.SAMLauncher: case UnitType.City: - case UnitType.Factory: - return this.landBasedStructureSpawn(targetTile, validTiles); + case UnitType.OilRig: + return this.landBasedStructureSpawn( + targetTile, + validTiles, + unitType === UnitType.OilRig, + ); default: assertNever(unitType); } @@ -1209,18 +1221,25 @@ export class PlayerImpl implements Player { landBasedStructureSpawn( tile: TileRef, validTiles: TileRef[] | null = null, + isOilRig: boolean = false, ): TileRef | false { - const tiles = validTiles ?? this.validStructureSpawnTiles(tile); + const tiles = validTiles ?? this.validStructureSpawnTiles(tile, isOilRig); if (tiles.length === 0) { return false; } return tiles[0]; } - private validStructureSpawnTiles(tile: TileRef): TileRef[] { + private validStructureSpawnTiles( + tile: TileRef, + isOilRig: boolean = false, + ): TileRef[] { if (this.mg.owner(tile) !== this) { return []; } + if (isOilRig && !this.mg.hasOilField(tile)) { + return []; + } const searchRadius = 15; const searchRadiusSquared = searchRadius ** 2; @@ -1234,7 +1253,8 @@ export class PlayerImpl implements Player { const nearbyTiles = this.mg.bfs(tile, (gm, t) => { return ( this.mg.euclideanDistSquared(tile, t) < searchRadiusSquared && - gm.ownerID(t) === this.smallID() + gm.ownerID(t) === this.smallID() && + (!isOilRig || gm.hasOilField(t)) ); }); const validSet: Set = new Set(nearbyTiles); diff --git a/src/core/game/RailNetworkImpl.ts b/src/core/game/RailNetworkImpl.ts index 813098401..b44df5785 100644 --- a/src/core/game/RailNetworkImpl.ts +++ b/src/core/game/RailNetworkImpl.ts @@ -248,13 +248,13 @@ export class RailNetworkImpl implements RailNetwork { const maxPathSize = this.game.config().railroadMaxSize(); // Cannot connect if outside the max range of a factory - if (!this.game.hasUnitNearby(tile, maxRange, UnitType.Factory)) { + if (!this.game.hasUnitNearby(tile, maxRange, UnitType.OilRig)) { return []; } const neighbors = this.game.nearbyUnits(tile, maxRange, [ UnitType.City, - UnitType.Factory, + UnitType.OilRig, UnitType.Port, ]); neighbors.sort((a, b) => a.distSquared - b.distSquared); @@ -293,7 +293,7 @@ export class RailNetworkImpl implements RailNetwork { const neighbors = this.game.nearbyUnits( station.tile(), this.game.config().trainStationMaxRange(), - [UnitType.City, UnitType.Factory, UnitType.Port], + [UnitType.City, UnitType.OilRig, UnitType.Port], ); const editedClusters = new Set(); diff --git a/src/core/game/TrainStation.ts b/src/core/game/TrainStation.ts index e2b687a6f..cb35a0916 100644 --- a/src/core/game/TrainStation.ts +++ b/src/core/game/TrainStation.ts @@ -45,7 +45,7 @@ export function createTrainStopHandlers( return { [UnitType.City]: new TradeStationStopHandler(), [UnitType.Port]: new TradeStationStopHandler(), - [UnitType.Factory]: new FactoryStopHandler(), + [UnitType.OilRig]: new FactoryStopHandler(), }; } diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 79b7c1ed8..e20e2f661 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -76,7 +76,7 @@ export class UnitImpl implements Unit { case UnitType.DefensePost: case UnitType.SAMLauncher: case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: this.mg.stats().unitBuild(_owner, this._type); } } @@ -195,7 +195,7 @@ export class UnitImpl implements Unit { case UnitType.DefensePost: case UnitType.SAMLauncher: case UnitType.City: - case UnitType.Factory: + case UnitType.OilRig: this.mg.stats().unitCapture(newOwner, this._type); this.mg.stats().unitLose(this._owner, this._type); break; @@ -290,7 +290,7 @@ export class UnitImpl implements Unit { case UnitType.Port: case UnitType.SAMLauncher: case UnitType.Warship: - case UnitType.Factory: + case UnitType.OilRig: this.mg.stats().unitDestroy(destroyer, this._type); this.mg.stats().unitLose(this.owner(), this._type); break; diff --git a/tests/client/graphics/RadialMenuElements.test.ts b/tests/client/graphics/RadialMenuElements.test.ts index 3404accbf..09d1667f1 100644 --- a/tests/client/graphics/RadialMenuElements.test.ts +++ b/tests/client/graphics/RadialMenuElements.test.ts @@ -30,9 +30,9 @@ vi.mock("../../../src/client/graphics/layers/BuildMenu", async () => { countable: true, }, { - unitType: UnitType.Factory, - key: "unit_type.factory", - description: "unit_type.factory_desc", + unitType: UnitType.OilRig, + key: "unit_type.oil_rig", + description: "unit_type.oil_rig_desc", icon: "factory-icon", countable: true, }, @@ -120,7 +120,7 @@ describe("RadialMenuElements", () => { mockPlayerActions = { buildableUnits: [ { type: UnitType.City, canBuild: true }, - { type: UnitType.Factory, canBuild: true }, + { type: UnitType.OilRig, canBuild: true }, { type: UnitType.AtomBomb, canBuild: true }, { type: UnitType.Warship, canBuild: true }, { type: UnitType.HydrogenBomb, canBuild: true }, @@ -211,7 +211,7 @@ describe("RadialMenuElements", () => { const subMenu = attackMenuElement.subMenu!(mockParams); - const constructionUnitTypes = [UnitType.City, UnitType.Factory]; + const constructionUnitTypes = [UnitType.City, UnitType.OilRig]; const returnedUnitTypes = subMenu.map((item) => { const unitTypeStr = item.id.replace("attack_", ""); return Object.values(UnitType).find( @@ -254,7 +254,7 @@ describe("RadialMenuElements", () => { expect(subMenu).toBeDefined(); expect(subMenu.length).toBeGreaterThan(0); - const constructionUnitTypes = [UnitType.City, UnitType.Factory]; + const constructionUnitTypes = [UnitType.City, UnitType.OilRig]; const returnedUnitTypes = subMenu.map((item) => { const unitTypeStr = item.id.replace("build_", ""); return Object.values(UnitType).find( @@ -597,8 +597,8 @@ describe("RadialMenuElements", () => { expect(translateText).toHaveBeenCalledWith("unit_type.city"); expect(translateText).toHaveBeenCalledWith("unit_type.city_desc"); - expect(translateText).toHaveBeenCalledWith("unit_type.factory"); - expect(translateText).toHaveBeenCalledWith("unit_type.factory_desc"); + expect(translateText).toHaveBeenCalledWith("unit_type.oil_rig"); + expect(translateText).toHaveBeenCalledWith("unit_type.oil_rig_desc"); }); it("should use translateText for tooltip items in attack menu", async () => {