diff --git a/resources/images/buildings/cityAlt1.png b/resources/images/buildings/cityAlt1.png index 2f9c52d8b..1975fd265 100644 Binary files a/resources/images/buildings/cityAlt1.png and b/resources/images/buildings/cityAlt1.png differ diff --git a/resources/images/buildings/factoryAlt1.png b/resources/images/buildings/factoryAlt1.png index 1d18be080..40e82d8c9 100644 Binary files a/resources/images/buildings/factoryAlt1.png and b/resources/images/buildings/factoryAlt1.png differ diff --git a/resources/images/buildings/fortAlt3.png b/resources/images/buildings/fortAlt3.png index 4637bdd8f..8087a8998 100644 Binary files a/resources/images/buildings/fortAlt3.png and b/resources/images/buildings/fortAlt3.png differ diff --git a/resources/images/buildings/port1.png b/resources/images/buildings/port1.png index 207c656e0..4ac18d4fb 100644 Binary files a/resources/images/buildings/port1.png and b/resources/images/buildings/port1.png differ diff --git a/resources/images/buildings/silo1.png b/resources/images/buildings/silo1.png index a68c5e5c9..89686639c 100644 Binary files a/resources/images/buildings/silo1.png and b/resources/images/buildings/silo1.png differ diff --git a/resources/images/buildings/silo4.png b/resources/images/buildings/silo4.png index b01f33adf..8216f6b2f 100644 Binary files a/resources/images/buildings/silo4.png and b/resources/images/buildings/silo4.png differ diff --git a/resources/lang/en.json b/resources/lang/en.json index 4db05fd3e..12cc0f05f 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -144,7 +144,7 @@ "instant_build": "Instant build", "infinite_gold": "Infinite gold", "infinite_troops": "Infinite troops", - "compact_map": "Mini Map", + "compact_map": "Compact Map", "max_timer": "Game length (minutes)", "disable_nukes": "Disable Nukes", "automatic_difficulty": "Automatic Difficulty", diff --git a/resources/sprites/halloween/bats.png b/resources/sprites/halloween/bats.png deleted file mode 100644 index ff96f164b..000000000 Binary files a/resources/sprites/halloween/bats.png and /dev/null differ diff --git a/resources/sprites/halloween/bubble.png b/resources/sprites/halloween/bubble.png deleted file mode 100644 index b8d82112f..000000000 Binary files a/resources/sprites/halloween/bubble.png and /dev/null differ diff --git a/resources/sprites/halloween/ghost.png b/resources/sprites/halloween/ghost.png deleted file mode 100644 index 15603066b..000000000 Binary files a/resources/sprites/halloween/ghost.png and /dev/null differ diff --git a/resources/sprites/halloween/miniPumpkin.png b/resources/sprites/halloween/miniPumpkin.png deleted file mode 100644 index 33274a1ef..000000000 Binary files a/resources/sprites/halloween/miniPumpkin.png and /dev/null differ diff --git a/resources/sprites/halloween/minifireGreen.png b/resources/sprites/halloween/minifireGreen.png deleted file mode 100644 index 8de5aa2ec..000000000 Binary files a/resources/sprites/halloween/minifireGreen.png and /dev/null differ diff --git a/resources/sprites/halloween/pumpkin.png b/resources/sprites/halloween/pumpkin.png deleted file mode 100644 index a6b772d41..000000000 Binary files a/resources/sprites/halloween/pumpkin.png and /dev/null differ diff --git a/resources/sprites/halloween/sam_explosion.png b/resources/sprites/halloween/sam_explosion.png deleted file mode 100644 index f8ad56602..000000000 Binary files a/resources/sprites/halloween/sam_explosion.png and /dev/null differ diff --git a/resources/sprites/halloween/shark.png b/resources/sprites/halloween/shark.png deleted file mode 100644 index 2ce0a2275..000000000 Binary files a/resources/sprites/halloween/shark.png and /dev/null differ diff --git a/resources/sprites/halloween/skull.png b/resources/sprites/halloween/skull.png deleted file mode 100644 index 73d9ecacd..000000000 Binary files a/resources/sprites/halloween/skull.png and /dev/null differ diff --git a/resources/sprites/halloween/skullNuke.png b/resources/sprites/halloween/skullNuke.png deleted file mode 100644 index 1a262ab83..000000000 Binary files a/resources/sprites/halloween/skullNuke.png and /dev/null differ diff --git a/resources/sprites/halloween/smokeAndFireGreen.png b/resources/sprites/halloween/smokeAndFireGreen.png deleted file mode 100644 index 641803e7d..000000000 Binary files a/resources/sprites/halloween/smokeAndFireGreen.png and /dev/null differ diff --git a/resources/sprites/halloween/tentacle.png b/resources/sprites/halloween/tentacle.png deleted file mode 100644 index 9fb590a66..000000000 Binary files a/resources/sprites/halloween/tentacle.png and /dev/null differ diff --git a/resources/sprites/halloween/tornado.png b/resources/sprites/halloween/tornado.png deleted file mode 100644 index ba51fc069..000000000 Binary files a/resources/sprites/halloween/tornado.png and /dev/null differ diff --git a/src/client/graphics/AnimatedSpriteLoader.ts b/src/client/graphics/AnimatedSpriteLoader.ts index ab6fd5d8e..03e49e0cc 100644 --- a/src/client/graphics/AnimatedSpriteLoader.ts +++ b/src/client/graphics/AnimatedSpriteLoader.ts @@ -1,21 +1,15 @@ +import miniBigSmoke from "../../../resources/sprites/bigsmoke.png"; import buildingExplosion from "../../../resources/sprites/buildingExplosion.png"; +import conquestSword from "../../../resources/sprites/conquestSword.png"; import dust from "../../../resources/sprites/dust.png"; import miniExplosion from "../../../resources/sprites/miniExplosion.png"; +import miniFire from "../../../resources/sprites/minifire.png"; +import nuke from "../../../resources/sprites/nukeExplosion.png"; import SAMExplosion from "../../../resources/sprites/samExplosion.png"; import sinkingShip from "../../../resources/sprites/sinkingShip.png"; +import miniSmoke from "../../../resources/sprites/smoke.png"; +import miniSmokeAndFire from "../../../resources/sprites/smokeAndFire.png"; import unitExplosion from "../../../resources/sprites/unitExplosion.png"; - -import bats from "../../../resources/sprites/halloween/bats.png"; -import bubble from "../../../resources/sprites/halloween/bubble.png"; -import ghost from "../../../resources/sprites/halloween/ghost.png"; -import minifireGreen from "../../../resources/sprites/halloween/minifireGreen.png"; -import shark from "../../../resources/sprites/halloween/shark.png"; -import skull from "../../../resources/sprites/halloween/skull.png"; -import skullNuke from "../../../resources/sprites/halloween/skullNuke.png"; -import miniSmokeAndFireGreen from "../../../resources/sprites/halloween/smokeAndFireGreen.png"; -import tentacle from "../../../resources/sprites/halloween/tentacle.png"; -import tornado from "../../../resources/sprites/halloween/tornado.png"; - import { Theme } from "../../core/configuration/Config"; import { PlayerView } from "../../core/game/GameView"; import { AnimatedSprite } from "./AnimatedSprite"; @@ -34,7 +28,7 @@ type AnimatedSpriteConfig = { const ANIMATED_SPRITE_CONFIG: Partial> = { [FxType.MiniFire]: { - url: minifireGreen, + url: miniFire, frameWidth: 7, frameCount: 6, frameDuration: 100, @@ -43,28 +37,28 @@ const ANIMATED_SPRITE_CONFIG: Partial> = { originY: 11, }, [FxType.MiniSmoke]: { - url: ghost, - frameWidth: 10, - frameCount: 5, - frameDuration: 100, + url: miniSmoke, + frameWidth: 11, + frameCount: 4, + frameDuration: 120, looping: true, - originX: 4, + originX: 2, originY: 10, }, [FxType.MiniBigSmoke]: { - url: bats, - frameWidth: 21, - frameCount: 6, + url: miniBigSmoke, + frameWidth: 24, + frameCount: 5, frameDuration: 120, looping: true, originX: 9, originY: 14, }, [FxType.MiniSmokeAndFire]: { - url: miniSmokeAndFireGreen, + url: miniSmokeAndFire, frameWidth: 24, frameCount: 5, - frameDuration: 90, + frameDuration: 120, looping: true, originX: 9, originY: 14, @@ -96,15 +90,6 @@ const ANIMATED_SPRITE_CONFIG: Partial> = { originX: 9, originY: 9, }, - [FxType.SinkingShip]: { - url: sinkingShip, - frameWidth: 16, - frameCount: 14, - frameDuration: 90, - looping: false, - originX: 7, - originY: 7, - }, [FxType.BuildingExplosion]: { url: buildingExplosion, frameWidth: 17, @@ -114,14 +99,23 @@ const ANIMATED_SPRITE_CONFIG: Partial> = { originX: 8, originY: 8, }, - [FxType.Nuke]: { - url: skullNuke, - frameWidth: 42, - frameCount: 19, - frameDuration: 50, + [FxType.SinkingShip]: { + url: sinkingShip, + frameWidth: 16, + frameCount: 14, + frameDuration: 90, looping: false, - originX: 20, - originY: 21, + originX: 7, + originY: 7, + }, + [FxType.Nuke]: { + url: nuke, + frameWidth: 60, + frameCount: 9, + frameDuration: 70, + looping: false, + originX: 30, + originY: 30, }, [FxType.SAMExplosion]: { url: SAMExplosion, @@ -133,51 +127,16 @@ const ANIMATED_SPRITE_CONFIG: Partial> = { originY: 19, }, [FxType.Conquest]: { - url: skull, - frameWidth: 14, - frameCount: 14, - frameDuration: 90, - looping: false, - originX: 7, - originY: 23, - }, - [FxType.Tentacle]: { - url: tentacle, - frameWidth: 22, - frameCount: 26, - frameDuration: 90, - looping: false, - originX: 13, - originY: 28, - }, - [FxType.Shark]: { - url: shark, - frameWidth: 25, - frameCount: 14, - frameDuration: 90, - looping: false, - originX: 13, - originY: 8, - }, - [FxType.Bubble]: { - url: bubble, - frameWidth: 22, - frameCount: 13, - frameDuration: 80, - looping: false, - originX: 13, - originY: 8, - }, - [FxType.Tornado]: { - url: tornado, - frameWidth: 30, + url: conquestSword, + frameWidth: 21, frameCount: 10, - frameDuration: 80, - looping: true, - originX: 11, - originY: 22, + frameDuration: 90, + looping: false, + originX: 10, + originY: 16, }, }; + export class AnimatedSpriteLoader { private animatedSpriteImageMap: Map = new Map(); // Do not color the same sprite twice diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index fdc3a3c14..29d5b7791 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -1,6 +1,6 @@ import { Colord } from "colord"; -import miniPumpkin from "../../../resources/sprites/halloween/miniPumpkin.png"; -import pumpkin from "../../../resources/sprites/halloween/pumpkin.png"; +import atomBombSprite from "../../../resources/sprites/atombomb.png"; +import hydrogenBombSprite from "../../../resources/sprites/hydrogenbomb.png"; import mirvSprite from "../../../resources/sprites/mirv2.png"; import samMissileSprite from "../../../resources/sprites/samMissile.png"; import tradeShipSprite from "../../../resources/sprites/tradeship.png"; @@ -26,8 +26,8 @@ const SPRITE_CONFIG: Partial> = { [UnitType.TransportShip]: transportShipSprite, [UnitType.Warship]: warshipSprite, [UnitType.SAMMissile]: samMissileSprite, - [UnitType.AtomBomb]: miniPumpkin, - [UnitType.HydrogenBomb]: pumpkin, + [UnitType.AtomBomb]: atomBombSprite, + [UnitType.HydrogenBomb]: hydrogenBombSprite, [UnitType.TradeShip]: tradeShipSprite, [UnitType.MIRV]: mirvSprite, [TrainTypeSprite.Engine]: trainEngineSprite, diff --git a/src/client/graphics/fx/ConquestFx.ts b/src/client/graphics/fx/ConquestFx.ts index 2c3f50413..7fa8d0690 100644 --- a/src/client/graphics/fx/ConquestFx.ts +++ b/src/client/graphics/fx/ConquestFx.ts @@ -26,6 +26,7 @@ export function conquestFxFactory( x, y, FxType.Conquest, + 2500, ); const fadeAnimation = new FadeFx(swordAnimation, 0.1, 0.6); conquestFx.push(fadeAnimation); diff --git a/src/client/graphics/fx/Fx.ts b/src/client/graphics/fx/Fx.ts index fa843de81..d4b206614 100644 --- a/src/client/graphics/fx/Fx.ts +++ b/src/client/graphics/fx/Fx.ts @@ -16,8 +16,4 @@ export enum FxType { UnderConstruction = "UnderConstruction", Dust = "Dust", Conquest = "Conquest", - Tentacle = "Tentacle", - Shark = "Shark", - Bubble = "Bubble", - Tornado = "Tornado", } diff --git a/src/client/graphics/fx/SpriteFx.ts b/src/client/graphics/fx/SpriteFx.ts index ebe8d410c..2926121a8 100644 --- a/src/client/graphics/fx/SpriteFx.ts +++ b/src/client/graphics/fx/SpriteFx.ts @@ -19,35 +19,6 @@ function fadeInOut( return 1 - f * f; } } -/** - * Move a sprite around - */ -export class MoveSpriteFx implements Fx { - private originX: number; - private originY: number; - constructor( - private fxToMove: SpriteFx, - private toX: number, - private toY: number, - private fadeIn: number = 0.1, - private fadeOut: number = 0.9, - ) { - this.originX = fxToMove.x; - this.originY = fxToMove.y; - } - - renderTick(duration: number, ctx: CanvasRenderingContext2D): boolean { - const t = this.fxToMove.getElapsedTime() / this.fxToMove.getDuration(); - this.fxToMove.x = Math.floor(this.originX * (1 - t) + this.toX * t); - this.fxToMove.y = Math.floor(this.originY * (1 - t) + this.toY * t); - ctx.save(); - ctx.globalAlpha = fadeInOut(t, this.fadeIn, this.fadeOut); - const result = this.fxToMove.renderTick(duration, ctx); - ctx.restore(); - return result; - } -} - /** * Fade in/out another FX */ @@ -78,8 +49,8 @@ export class SpriteFx implements Fx { protected waitToTheEnd = false; constructor( animatedSpriteLoader: AnimatedSpriteLoader, - public x: number, - public y: number, + protected x: number, + protected y: number, fxType: FxType, duration?: number, owner?: PlayerView, diff --git a/src/client/graphics/layers/FxLayer.ts b/src/client/graphics/layers/FxLayer.ts index d0df2d340..e1c454e5c 100644 --- a/src/client/graphics/layers/FxLayer.ts +++ b/src/client/graphics/layers/FxLayer.ts @@ -14,7 +14,7 @@ import { conquestFxFactory } from "../fx/ConquestFx"; import { Fx, FxType } from "../fx/Fx"; import { NukeAreaFx } from "../fx/NukeAreaFx"; import { nukeFxFactory, ShockwaveFx } from "../fx/NukeFx"; -import { FadeFx, MoveSpriteFx, SpriteFx } from "../fx/SpriteFx"; +import { SpriteFx } from "../fx/SpriteFx"; import { TargetFx } from "../fx/TargetFx"; import { TextFx } from "../fx/TextFx"; import { UnitExplosionFx } from "../fx/UnitExplosionFx"; @@ -22,8 +22,6 @@ import { Layer } from "./Layer"; export class FxLayer implements Layer { private canvas: HTMLCanvasElement; private context: CanvasRenderingContext2D; - private lastRandomEvent: number = 0; - private randomEventRate: number = 8; private lastRefresh: number = 0; private refreshRate: number = 10; @@ -44,14 +42,6 @@ export class FxLayer implements Layer { } tick() { - if (!this.game.config().userSettings()?.fxLayer()) { - return; - } - this.lastRandomEvent += 1; - if (this.lastRandomEvent > this.randomEventRate) { - this.lastRandomEvent = 0; - this.randomEvent(); - } this.manageBoatTargetFx(); this.game .updatesSinceLastTick() @@ -154,72 +144,6 @@ export class FxLayer implements Layer { this.allFx.push(textFx); } - randomEvent() { - const randX = Math.floor(Math.random() * this.game.width()); - const randY = Math.floor(Math.random() * this.game.height()); - const ref = this.game.ref(randX, randY); - if (this.game.isOcean(ref) && !this.game.isShoreline(ref)) { - const animation = Math.floor(Math.random() * 4); - if (animation === 0) { - const fx = new SpriteFx( - this.animatedSpriteLoader, - randX, - randY, - FxType.Shark, - ); - this.allFx.push(fx); - } else if (animation === 1) { - const fx = new SpriteFx( - this.animatedSpriteLoader, - randX, - randY, - FxType.Bubble, - ); - this.allFx.push(fx); - } else if (animation === 2) { - const fx = new MoveSpriteFx( - new SpriteFx( - this.animatedSpriteLoader, - randX, - randY, - FxType.Tornado, - 6000, - ), - randX - 40, - randY, - 0.1, - 0.8, - ); - this.allFx.push(fx); - } else if (animation === 3) { - const fx = new FadeFx( - new SpriteFx( - this.animatedSpriteLoader, - randX, - randY, - FxType.Tentacle, - ), - 0.1, - 0.8, - ); - this.allFx.push(fx); - } - } else { - const ghost = new FadeFx( - new SpriteFx( - this.animatedSpriteLoader, - randX, - randY, - FxType.MiniSmoke, - 4000, - ), - 0.1, - 0.8, - ); - this.allFx.push(ghost); - } - } - onUnitEvent(unit: UnitView) { switch (unit.type()) { case UnitType.TransportShip: { diff --git a/src/core/Util.ts b/src/core/Util.ts index d90516af6..159370de5 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -293,17 +293,17 @@ export function createRandomName( } export const emojiTable = [ - ["😀", "😊", "😇", "😎", "😈"], + ["😀", "😊", "🥰", "😇", "😎"], ["😞", "🥺", "😭", "😱", "😡"], - ["⏳", "🥱", "🤦‍♂️", "🖕", "🤡"], - ["👋", "👏", "👻", "💪", "🎃"], + ["😈", "🤡", "🖕", "🥱", "🤦‍♂️"], + ["👋", "👏", "🤌", "💪", "🫡"], ["👍", "👎", "❓", "🐔", "🐀"], - ["🆘", "🤝", "🕊️", "🏳️", "🛡️"], + ["🤝", "🆘", "🕊️", "🏳️", "⏳"], ["🔥", "💥", "💀", "☢️", "⚠️"], ["↖️", "⬆️", "↗️", "👑", "🥇"], ["⬅️", "🎯", "➡️", "🥈", "🥉"], ["↙️", "⬇️", "↘️", "❤️", "💔"], - ["💰", "🏭", "🚂", "⚓", "⛵"], + ["💰", "⚓", "⛵", "🏡", "🛡️"], ] as const; // 2d to 1d array export const flattenedEmojiTable = emojiTable.flat(); diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 0fb16b490..04d5c85cf 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -136,6 +136,7 @@ export interface Config { deleteUnitCooldown(): Tick; defaultDonationAmount(sender: Player): number; unitInfo(type: UnitType): UnitInfo; + tradeShipShortRangeDebuff(): number; tradeShipGold(dist: number, numPorts: number): Gold; tradeShipSpawnRate( numTradeShips: number, diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 7102f6630..d6d45489e 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -173,8 +173,9 @@ export abstract class DefaultServerConfig implements ServerConfig { turnIntervalMs(): number { return 100; } + gameCreationRate(): number { - return 60 * 1000; + return 30 * 1000; } lobbyMaxPlayers( @@ -378,9 +379,10 @@ export class DefaultConfig implements Config { } tradeShipGold(dist: number, numPorts: number): Gold { - // Sigmoid: concave start, sharp S-curve middle, linear end - heavily punishes trades under 200 + // Sigmoid: concave start, sharp S-curve middle, linear end - heavily punishes trades under range debuff. + const debuff = this.tradeShipShortRangeDebuff(); const baseGold = - 100_000 / (1 + Math.exp(-0.03 * (dist - 200))) + 100 * dist; + 100_000 / (1 + Math.exp(-0.03 * (dist - debuff))) + 100 * dist; const numPortBonus = numPorts - 1; // Hyperbolic decay, midpoint at 5 ports, 3x bonus max. const bonus = 1 + 2 * (numPortBonus / (numPortBonus + 5)); @@ -584,10 +586,11 @@ export class DefaultConfig implements Config { return 10 * 10; } deletionMarkDuration(): Tick { - return 15 * 10; + return 30 * 10; } + deleteUnitCooldown(): Tick { - return 15 * 10; + return 30 * 10; } emojiMessageDuration(): Tick { return 5 * 10; @@ -784,6 +787,10 @@ export class DefaultConfig implements Config { return 20; } + tradeShipShortRangeDebuff(): number { + return 300; + } + proximityBonusPortsNb(totalPorts: number) { return within(totalPorts / 3, 4, totalPorts); } diff --git a/src/core/configuration/PastelTheme.ts b/src/core/configuration/PastelTheme.ts index e660e6dc2..028fec8bf 100644 --- a/src/core/configuration/PastelTheme.ts +++ b/src/core/configuration/PastelTheme.ts @@ -108,15 +108,15 @@ export class PastelTheme implements Theme { } case TerrainType.Plains: return colord({ - r: 216, - g: 205 - 2 * mag, - b: 127, + r: 190, + g: 220 - 2 * mag, + b: 138, }); case TerrainType.Highland: return colord({ - r: 223 + 2 * mag, - g: 187 + 2 * mag, - b: 132 + 2 * mag, + r: 200 + 2 * mag, + g: 183 + 2 * mag, + b: 138 + 2 * mag, }); case TerrainType.Mountain: return colord({ diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index cdc16a496..de7b70752 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -58,7 +58,7 @@ export class PortExecution implements Execution { return; } - const ports = this.player.tradingPorts(this.port); + const ports = this.tradingPorts(); if (ports.length === 0) { return; @@ -103,4 +103,40 @@ export class PortExecution implements Execution { } } } + + // It's a probability list, so if an element appears twice it's because it's + // twice more likely to be picked later. + tradingPorts(): Unit[] { + const ports = this.mg + .players() + .filter((p) => p !== this.port!.owner() && p.canTrade(this.port!.owner())) + .flatMap((p) => p.units(UnitType.Port)) + .sort((p1, p2) => { + return ( + this.mg.manhattanDist(this.port!.tile(), p1.tile()) - + this.mg.manhattanDist(this.port!.tile(), p2.tile()) + ); + }); + + const weightedPorts: Unit[] = []; + + for (const [i, otherPort] of ports.entries()) { + const expanded = new Array(otherPort.level()).fill(otherPort); + weightedPorts.push(...expanded); + const tooClose = + this.mg.manhattanDist(this.port!.tile(), otherPort.tile()) < + this.mg.config().tradeShipShortRangeDebuff(); + const closeBonus = + i < this.mg.config().proximityBonusPortsNb(ports.length); + if (!tooClose && closeBonus) { + // If the port is close, but not too close, add it again + // to increase the chances of trading with it. + weightedPorts.push(...expanded); + } + if (!tooClose && this.port!.owner().isFriendly(otherPort.owner())) { + weightedPorts.push(...expanded); + } + } + return weightedPorts; + } } diff --git a/src/core/execution/utils/BotBehavior.ts b/src/core/execution/utils/BotBehavior.ts index e748b9f4a..d05912501 100644 --- a/src/core/execution/utils/BotBehavior.ts +++ b/src/core/execution/utils/BotBehavior.ts @@ -24,7 +24,7 @@ const EMOJI_ASSIST_ACCEPT = (["👍", "⛵", "🤝", "🎯"] as const).map(emoji const EMOJI_RELATION_TOO_LOW = (["🥱", "🤦‍♂️"] as const).map(emojiId); const EMOJI_TARGET_ME = (["🥺", "💀"] as const).map(emojiId); const EMOJI_TARGET_ALLY = (["🕊️", "👎"] as const).map(emojiId); -export const EMOJI_HECKLE = (["👻", "🎃"] as const).map(emojiId); +export const EMOJI_HECKLE = (["🤡", "😡"] as const).map(emojiId); export class BotBehavior { private enemy: Player | null = null; diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index dd4e225d1..37914f63d 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -656,7 +656,6 @@ export interface Player { // Misc toUpdate(): PlayerUpdate; playerProfile(): PlayerProfile; - tradingPorts(port: Unit): Unit[]; // WARNING: this operation is expensive. bestTransportShipSpawn(tile: TileRef): TileRef | false; } diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index f757e8e8c..f85cc7aab 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -1222,34 +1222,4 @@ export class PlayerImpl implements Player { bestTransportShipSpawn(targetTile: TileRef): TileRef | false { return bestShoreDeploymentSource(this.mg, this, targetTile); } - - // It's a probability list, so if an element appears twice it's because it's - // twice more likely to be picked later. - tradingPorts(port: Unit): Unit[] { - const ports = this.mg - .players() - .filter((p) => p !== port.owner() && p.canTrade(port.owner())) - .flatMap((p) => p.units(UnitType.Port)) - .sort((p1, p2) => { - return ( - this.mg.manhattanDist(port.tile(), p1.tile()) - - this.mg.manhattanDist(port.tile(), p2.tile()) - ); - }); - - const weightedPorts: Unit[] = []; - - for (const [i, otherPort] of ports.entries()) { - const expanded = new Array(otherPort.level()).fill(otherPort); - weightedPorts.push(...expanded); - if (i < this.mg.config().proximityBonusPortsNb(ports.length)) { - weightedPorts.push(...expanded); - } - if (port.owner().isFriendly(otherPort.owner())) { - weightedPorts.push(...expanded); - } - } - - return weightedPorts; - } } diff --git a/tests/DeleteUnitExecution.test.ts b/tests/DeleteUnitExecution.test.ts index 04034a82f..71444dca5 100644 --- a/tests/DeleteUnitExecution.test.ts +++ b/tests/DeleteUnitExecution.test.ts @@ -69,6 +69,9 @@ describe("DeleteUnitExecution Security Tests", () => { if (!tileOwner.isPlayer() || tileOwner.id() !== player.id()) { throw new Error("Unit is not on player's territory"); } + + game.config().deleteUnitCooldown = () => 10; + game.config().deletionMarkDuration = () => 10; }); describe("Security Validations", () => { diff --git a/tests/PlayerImpl.test.ts b/tests/PlayerImpl.test.ts index 7f47a1cd2..71d022f22 100644 --- a/tests/PlayerImpl.test.ts +++ b/tests/PlayerImpl.test.ts @@ -83,22 +83,6 @@ describe("PlayerImpl", () => { expect(cityToUpgrade).toBe(false); }); - test("Destination ports chances scale with level", () => { - game.config().proximityBonusPortsNb = () => 0; - - player.conquer(game.ref(10, 10)); - const playerPort = player.buildUnit(UnitType.Port, game.ref(10, 10), {}); - - other.conquer(game.ref(0, 0)); - const otherPort = other.buildUnit(UnitType.Port, game.ref(0, 0), {}); - otherPort.increaseLevel(); - otherPort.increaseLevel(); - - const ports = player.tradingPorts(playerPort); - - expect(ports.length).toBe(3); - }); - test("Can't send alliance requests when dead", () => { // conquer other const otherTiles = other.tiles(); diff --git a/tests/PortExecution.test.ts b/tests/PortExecution.test.ts new file mode 100644 index 000000000..d38500315 --- /dev/null +++ b/tests/PortExecution.test.ts @@ -0,0 +1,92 @@ +import { PortExecution } from "../src/core/execution/PortExecution"; +import { + Game, + Player, + PlayerInfo, + PlayerType, + UnitType, +} from "../src/core/game/Game"; +import { setup } from "./util/Setup"; + +let game: Game; +let player: Player; +let other: Player; + +describe("PortExecution", () => { + beforeEach(async () => { + game = await setup( + "half_land_half_ocean", + { + instantBuild: true, + }, + [ + new PlayerInfo("player", PlayerType.Human, null, "player_id"), + new PlayerInfo("other", PlayerType.Human, null, "other_id"), + ], + ); + + while (game.inSpawnPhase()) { + game.executeNextTick(); + } + + player = game.player("player_id"); + player.addGold(BigInt(1000000)); + other = game.player("other_id"); + + game.config().structureMinDist = () => 10; + }); + + test("Destination ports chances scale with level", () => { + game.config().proximityBonusPortsNb = () => 0; + game.config().tradeShipShortRangeDebuff = () => 0; + + player.conquer(game.ref(7, 10)); + const execution = new PortExecution(player, game.ref(7, 10)); + execution.init(game, 0); + execution.tick(0); + + other.conquer(game.ref(0, 0)); + const otherPort = other.buildUnit(UnitType.Port, game.ref(0, 0), {}); + otherPort.increaseLevel(); + otherPort.increaseLevel(); + + const ports = execution.tradingPorts(); + + expect(ports.length).toBe(3); + }); + + test("Trade ship proximity bonus", () => { + game.config().proximityBonusPortsNb = () => 10; + game.config().tradeShipShortRangeDebuff = () => 0; + + player.conquer(game.ref(7, 10)); + const execution = new PortExecution(player, game.ref(7, 10)); + execution.init(game, 0); + execution.tick(0); + + other.conquer(game.ref(0, 0)); + other.buildUnit(UnitType.Port, game.ref(0, 0), {}); + + const ports = execution.tradingPorts(); + + expect(ports.length).toBe(2); + }); + + test("Trade ship short range debuff", () => { + game.config().proximityBonusPortsNb = () => 10; + // Short range debuff cancels out the proximity bonus. + game.config().tradeShipShortRangeDebuff = () => 100; + + player.conquer(game.ref(7, 10)); + const execution = new PortExecution(player, game.ref(7, 10)); + execution.init(game, 0); + execution.tick(0); + + other.conquer(game.ref(0, 0)); + other.buildUnit(UnitType.Port, game.ref(0, 0), {}); + + const ports = execution.tradingPorts(); + + expect(ports.length).toBe(1); + }); +});