mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-24 13:52:45 +00:00
Remove workers & troop ratio bar, only have troops (#1676)
## Description: The troop/worker ratio bar is almost never changed. so remove it and the entire concept of workers. Now there is just troops. Now players get a consistent 1k/s gold. ## 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 - [x] I have read and accepted the CLA agreement (only required once). ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -166,11 +166,7 @@ export class HelpModal extends LitElement {
|
||||
<div>
|
||||
<p class="mb-4">${translateText("help_modal.ui_control_desc")}</p>
|
||||
<ul>
|
||||
<li class="mb-4">${translateText("help_modal.ui_pop")}</li>
|
||||
<li class="mb-4">${translateText("help_modal.ui_gold")}</li>
|
||||
<li class="mb-4">
|
||||
${translateText("help_modal.ui_troops_workers")}
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
${translateText("help_modal.ui_attack_ratio")}
|
||||
</li>
|
||||
|
||||
@@ -97,11 +97,6 @@ export class LocalServer {
|
||||
return;
|
||||
}
|
||||
if (this.paused) {
|
||||
if (clientMsg.intent.type === "troop_ratio") {
|
||||
// Store troop change events because otherwise they are
|
||||
// not registered when game is paused.
|
||||
this.intents.push(clientMsg.intent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.intents.push(clientMsg.intent);
|
||||
|
||||
@@ -141,10 +141,6 @@ export class CancelBoatIntentEvent implements GameEvent {
|
||||
constructor(public readonly unitID: number) {}
|
||||
}
|
||||
|
||||
export class SendSetTargetTroopRatioEvent implements GameEvent {
|
||||
constructor(public readonly ratio: number) {}
|
||||
}
|
||||
|
||||
export class SendWinnerEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly winner: Winner,
|
||||
@@ -227,9 +223,6 @@ export class Transport {
|
||||
this.eventBus.on(SendEmbargoIntentEvent, (e) =>
|
||||
this.onSendEmbargoIntent(e),
|
||||
);
|
||||
this.eventBus.on(SendSetTargetTroopRatioEvent, (e) =>
|
||||
this.onSendSetTargetTroopRatioEvent(e),
|
||||
);
|
||||
this.eventBus.on(BuildUnitIntentEvent, (e) => this.onBuildUnitIntent(e));
|
||||
|
||||
this.eventBus.on(PauseGameEvent, (e) => this.onPauseGameEvent(e));
|
||||
@@ -532,14 +525,6 @@ export class Transport {
|
||||
});
|
||||
}
|
||||
|
||||
private onSendSetTargetTroopRatioEvent(event: SendSetTargetTroopRatioEvent) {
|
||||
this.sendIntent({
|
||||
type: "troop_ratio",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
ratio: event.ratio,
|
||||
});
|
||||
}
|
||||
|
||||
private onBuildUnitIntent(event: BuildUnitIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "build_unit",
|
||||
|
||||
@@ -342,17 +342,6 @@ export class UserSettingModal extends LitElement {
|
||||
@change=${this.sliderAttackRatio}
|
||||
></setting-slider>
|
||||
|
||||
<!-- 🪖🛠️ Troop Ratio -->
|
||||
<setting-slider
|
||||
label="${translateText("user_setting.troop_ratio_label")}"
|
||||
description="${translateText("user_setting.troop_ratio_desc")}"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${Number(localStorage.getItem("settings.troopRatio") ?? "0.95") *
|
||||
100}
|
||||
@change=${this.sliderTroopRatio}
|
||||
></setting-slider>
|
||||
|
||||
${this.showEasterEggSettings
|
||||
? html`
|
||||
<setting-slider
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Gold } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
import { AttackRatioEvent } from "../../InputHandler";
|
||||
import { SendSetTargetTroopRatioEvent } from "../../Transport";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { UIState } from "../UIState";
|
||||
import { Layer } from "./Layer";
|
||||
@@ -22,54 +21,29 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
private attackRatio: number = 0.2;
|
||||
|
||||
@state()
|
||||
private targetTroopRatio = 0.95;
|
||||
private _maxTroops: number;
|
||||
|
||||
@state()
|
||||
private currentTroopRatio = 0.95;
|
||||
|
||||
@state()
|
||||
private _population: number;
|
||||
|
||||
@state()
|
||||
private _maxPopulation: number;
|
||||
|
||||
@state()
|
||||
private popRate: number;
|
||||
private troopRate: number;
|
||||
|
||||
@state()
|
||||
private _troops: number;
|
||||
|
||||
@state()
|
||||
private _workers: number;
|
||||
|
||||
@state()
|
||||
private _isVisible = false;
|
||||
|
||||
@state()
|
||||
private _manpower: number = 0;
|
||||
|
||||
@state()
|
||||
private _gold: Gold;
|
||||
|
||||
@state()
|
||||
private _goldPerSecond: Gold;
|
||||
private _troopRateIsIncreasing: boolean = true;
|
||||
|
||||
private _popRateIsIncreasing: boolean = true;
|
||||
|
||||
private _lastPopulationIncreaseRate: number;
|
||||
|
||||
private init_: boolean = false;
|
||||
private _lastTroopIncreaseRate: number;
|
||||
|
||||
init() {
|
||||
this.attackRatio = Number(
|
||||
localStorage.getItem("settings.attackRatio") ?? "0.2",
|
||||
);
|
||||
this.targetTroopRatio = Number(
|
||||
localStorage.getItem("settings.troopRatio") ?? "0.95",
|
||||
);
|
||||
this.init_ = true;
|
||||
this.uiState.attackRatio = this.attackRatio;
|
||||
this.currentTroopRatio = this.targetTroopRatio;
|
||||
this.eventBus.on(AttackRatioEvent, (event) => {
|
||||
let newAttackRatio =
|
||||
(parseInt(
|
||||
@@ -97,13 +71,6 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.init_) {
|
||||
this.eventBus.emit(
|
||||
new SendSetTargetTroopRatioEvent(this.targetTroopRatio),
|
||||
);
|
||||
this.init_ = false;
|
||||
}
|
||||
|
||||
if (!this._isVisible && !this.game.inSpawnPhase()) {
|
||||
this.setVisibile(true);
|
||||
}
|
||||
@@ -115,28 +82,24 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
if (this.game.ticks() % 5 === 0) {
|
||||
this.updatePopulationIncrease();
|
||||
this.updateTroopIncrease();
|
||||
}
|
||||
|
||||
this._population = player.population();
|
||||
this._maxPopulation = this.game.config().maxPopulation(player);
|
||||
this._troops = player.troops();
|
||||
this._maxTroops = this.game.config().maxTroops(player);
|
||||
this._gold = player.gold();
|
||||
this._troops = player.troops();
|
||||
this._workers = player.workers();
|
||||
this.popRate = this.game.config().populationIncreaseRate(player) * 10;
|
||||
this._goldPerSecond = this.game.config().goldAdditionRate(player) * 10n;
|
||||
|
||||
this.currentTroopRatio = player.troops() / player.population();
|
||||
this.troopRate = this.game.config().troopIncreaseRate(player) * 10;
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private updatePopulationIncrease() {
|
||||
private updateTroopIncrease() {
|
||||
const player = this.game?.myPlayer();
|
||||
if (player === null) return;
|
||||
const popIncreaseRate = this.game.config().populationIncreaseRate(player);
|
||||
this._popRateIsIncreasing =
|
||||
popIncreaseRate >= this._lastPopulationIncreaseRate;
|
||||
this._lastPopulationIncreaseRate = popIncreaseRate;
|
||||
const troopIncreaseRate = this.game.config().troopIncreaseRate(player);
|
||||
this._troopRateIsIncreasing =
|
||||
troopIncreaseRate >= this._lastTroopIncreaseRate;
|
||||
this._lastTroopIncreaseRate = troopIncreaseRate;
|
||||
}
|
||||
|
||||
onAttackRatioChange(newRatio: number) {
|
||||
@@ -156,19 +119,6 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
targetTroops(): number {
|
||||
return this._manpower * this.targetTroopRatio;
|
||||
}
|
||||
|
||||
onTroopChange(newRatio: number) {
|
||||
this.eventBus.emit(new SendSetTargetTroopRatioEvent(newRatio));
|
||||
}
|
||||
|
||||
delta(): number {
|
||||
const d = this._population - this.targetTroops();
|
||||
return d;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<style>
|
||||
@@ -219,17 +169,16 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
<div class="block bg-black/30 text-white mb-4 p-2 rounded">
|
||||
<div class="flex justify-between mb-1">
|
||||
<span class="font-bold"
|
||||
>${translateText("control_panel.pop")}:</span
|
||||
>${translateText("control_panel.troops")}:</span
|
||||
>
|
||||
<span translate="no"
|
||||
>${renderTroops(this._population)} /
|
||||
${renderTroops(this._maxPopulation)}
|
||||
>${renderTroops(this._troops)} / ${renderTroops(this._maxTroops)}
|
||||
<span
|
||||
class="${this._popRateIsIncreasing
|
||||
class="${this._troopRateIsIncreasing
|
||||
? "text-green-500"
|
||||
: "text-yellow-500"}"
|
||||
translate="no"
|
||||
>(+${renderTroops(this.popRate)})</span
|
||||
>(+${renderTroops(this.troopRate)})</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
@@ -237,43 +186,7 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
<span class="font-bold"
|
||||
>${translateText("control_panel.gold")}:</span
|
||||
>
|
||||
<span translate="no"
|
||||
>${renderNumber(this._gold)}
|
||||
(+${renderNumber(this._goldPerSecond)})</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative mb-4 sm:mb-4">
|
||||
<label class="block text-white mb-1" translate="no"
|
||||
>${translateText("control_panel.troops")}:
|
||||
<span translate="no">${renderTroops(this._troops)}</span> |
|
||||
${translateText("control_panel.workers")}:
|
||||
<span translate="no">${renderTroops(this._workers)}</span></label
|
||||
>
|
||||
<div class="relative h-8">
|
||||
<!-- Background track -->
|
||||
<div
|
||||
class="absolute left-0 right-0 top-3 h-2 bg-white/20 rounded"
|
||||
></div>
|
||||
<!-- Fill track -->
|
||||
<div
|
||||
class="absolute left-0 top-3 h-2 bg-blue-500/60 rounded transition-all duration-300"
|
||||
style="width: ${this.currentTroopRatio * 100}%"
|
||||
></div>
|
||||
<!-- Range input - exactly overlaying the visual elements -->
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${(this.targetTroopRatio * 100).toString()}
|
||||
@input=${(e: Event) => {
|
||||
this.targetTroopRatio =
|
||||
parseInt((e.target as HTMLInputElement).value) / 100;
|
||||
this.onTroopChange(this.targetTroopRatio);
|
||||
}}
|
||||
class="absolute left-0 right-0 top-2 m-0 h-4 cursor-pointer targetTroopRatio"
|
||||
/>
|
||||
<span translate="no">${renderNumber(this._gold)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ export type Intent =
|
||||
| EmojiIntent
|
||||
| DonateGoldIntent
|
||||
| DonateTroopsIntent
|
||||
| TargetTroopRatioIntent
|
||||
| BuildUnitIntent
|
||||
| EmbargoIntent
|
||||
| QuickChatIntent
|
||||
@@ -59,9 +58,6 @@ export type EmojiIntent = z.infer<typeof EmojiIntentSchema>;
|
||||
export type DonateGoldIntent = z.infer<typeof DonateGoldIntentSchema>;
|
||||
export type DonateTroopsIntent = z.infer<typeof DonateTroopIntentSchema>;
|
||||
export type EmbargoIntent = z.infer<typeof EmbargoIntentSchema>;
|
||||
export type TargetTroopRatioIntent = z.infer<
|
||||
typeof TargetTroopRatioIntentSchema
|
||||
>;
|
||||
export type BuildUnitIntent = z.infer<typeof BuildUnitIntentSchema>;
|
||||
export type UpgradeStructureIntent = z.infer<
|
||||
typeof UpgradeStructureIntentSchema
|
||||
@@ -314,11 +310,6 @@ export const DonateTroopIntentSchema = BaseIntentSchema.extend({
|
||||
troops: z.number().nullable(),
|
||||
});
|
||||
|
||||
export const TargetTroopRatioIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("troop_ratio"),
|
||||
ratio: z.number().min(0).max(1),
|
||||
});
|
||||
|
||||
export const BuildUnitIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("build_unit"),
|
||||
unit: z.enum(UnitType),
|
||||
@@ -378,7 +369,6 @@ const IntentSchema = z.discriminatedUnion("type", [
|
||||
EmojiIntentSchema,
|
||||
DonateGoldIntentSchema,
|
||||
DonateTroopIntentSchema,
|
||||
TargetTroopRatioIntentSchema,
|
||||
BuildUnitIntentSchema,
|
||||
UpgradeStructureIntentSchema,
|
||||
EmbargoIntentSchema,
|
||||
|
||||
@@ -89,9 +89,8 @@ export interface Config {
|
||||
playerTeams(): TeamCountConfig;
|
||||
|
||||
startManpower(playerInfo: PlayerInfo): number;
|
||||
populationIncreaseRate(player: Player | PlayerView): number;
|
||||
troopIncreaseRate(player: Player | PlayerView): number;
|
||||
goldAdditionRate(player: Player | PlayerView): Gold;
|
||||
troopAdjustmentRate(player: Player): number;
|
||||
attackTilesPerTick(
|
||||
attckTroops: number,
|
||||
attacker: Player,
|
||||
@@ -114,8 +113,8 @@ export interface Config {
|
||||
// When computing likelihood of trading for any given port, the X closest port
|
||||
// are twice more likely to be selected. X is determined below.
|
||||
proximityBonusPortsNb(totalPorts: number): number;
|
||||
maxPopulation(player: Player | PlayerView): number;
|
||||
cityPopulationIncrease(): number;
|
||||
maxTroops(player: Player | PlayerView): number;
|
||||
cityTroopIncrease(): number;
|
||||
boatAttackAmount(attacker: Player, defender: Player | TerraNullius): number;
|
||||
shellLifetime(): number;
|
||||
boatMaxNumber(): number;
|
||||
@@ -162,7 +161,7 @@ export interface Config {
|
||||
nukeType: NukeType,
|
||||
humans: number,
|
||||
tilesOwned: number,
|
||||
maxPop: number,
|
||||
maxTroops: number,
|
||||
): number;
|
||||
structureMinDist(): number;
|
||||
isReplay(): boolean;
|
||||
|
||||
@@ -273,7 +273,7 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
}
|
||||
|
||||
cityPopulationIncrease(): number {
|
||||
cityTroopIncrease(): number {
|
||||
return 250_000;
|
||||
}
|
||||
|
||||
@@ -714,8 +714,8 @@ export class DefaultConfig implements Config {
|
||||
return this.infiniteTroops() ? 1_000_000 : 25_000;
|
||||
}
|
||||
|
||||
maxPopulation(player: Player | PlayerView): number {
|
||||
const maxPop =
|
||||
maxTroops(player: Player | PlayerView): number {
|
||||
const maxTroops =
|
||||
player.type() === PlayerType.Human && this.infiniteTroops()
|
||||
? 1_000_000_000
|
||||
: 2 * (Math.pow(player.numTilesOwned(), 0.6) * 1000 + 50000) +
|
||||
@@ -723,34 +723,34 @@ export class DefaultConfig implements Config {
|
||||
.units(UnitType.City)
|
||||
.map((city) => city.level())
|
||||
.reduce((a, b) => a + b, 0) *
|
||||
this.cityPopulationIncrease();
|
||||
this.cityTroopIncrease();
|
||||
|
||||
if (player.type() === PlayerType.Bot) {
|
||||
return maxPop / 2;
|
||||
return maxTroops / 2;
|
||||
}
|
||||
|
||||
if (player.type() === PlayerType.Human) {
|
||||
return maxPop;
|
||||
return maxTroops;
|
||||
}
|
||||
|
||||
switch (this._gameConfig.difficulty) {
|
||||
case Difficulty.Easy:
|
||||
return maxPop * 0.5;
|
||||
return maxTroops * 0.5;
|
||||
case Difficulty.Medium:
|
||||
return maxPop * 1;
|
||||
return maxTroops * 1;
|
||||
case Difficulty.Hard:
|
||||
return maxPop * 1.5;
|
||||
return maxTroops * 1.5;
|
||||
case Difficulty.Impossible:
|
||||
return maxPop * 2;
|
||||
return maxTroops * 2;
|
||||
}
|
||||
}
|
||||
|
||||
populationIncreaseRate(player: Player): number {
|
||||
const max = this.maxPopulation(player);
|
||||
troopIncreaseRate(player: Player): number {
|
||||
const max = this.maxTroops(player);
|
||||
|
||||
let toAdd = 10 + Math.pow(player.population(), 0.73) / 4;
|
||||
let toAdd = 10 + Math.pow(player.troops(), 0.73) / 4;
|
||||
|
||||
const ratio = 1 - player.population() / max;
|
||||
const ratio = 1 - player.troops() / max;
|
||||
toAdd *= ratio;
|
||||
|
||||
if (player.type() === PlayerType.Bot) {
|
||||
@@ -774,26 +774,11 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(player.population() + toAdd, max) - player.population();
|
||||
return Math.min(player.troops() + toAdd, max) - player.troops();
|
||||
}
|
||||
|
||||
goldAdditionRate(player: Player): Gold {
|
||||
return BigInt(Math.floor(0.045 * player.workers() ** 0.7));
|
||||
}
|
||||
|
||||
troopAdjustmentRate(player: Player): number {
|
||||
const maxDiff = this.maxPopulation(player) / 1000;
|
||||
const target = player.population() * player.targetTroopRatio();
|
||||
const diff = target - player.troops();
|
||||
if (Math.abs(diff) < maxDiff) {
|
||||
return diff;
|
||||
}
|
||||
const adjustment = maxDiff * Math.sign(diff);
|
||||
// Can ramp down troops much faster
|
||||
if (adjustment < 0) {
|
||||
return adjustment * 5;
|
||||
}
|
||||
return adjustment;
|
||||
return 100n;
|
||||
}
|
||||
|
||||
nukeMagnitudes(unitType: UnitType): NukeMagnitude {
|
||||
@@ -824,22 +809,22 @@ export class DefaultConfig implements Config {
|
||||
return 80;
|
||||
}
|
||||
|
||||
// Humans can be population, soldiers attacking, soldiers in boat etc.
|
||||
// Humans can be soldiers, soldiers attacking, soldiers in boat etc.
|
||||
nukeDeathFactor(
|
||||
nukeType: NukeType,
|
||||
humans: number,
|
||||
tilesOwned: number,
|
||||
maxPop: number,
|
||||
maxTroops: number,
|
||||
): number {
|
||||
if (nukeType !== UnitType.MIRVWarhead) {
|
||||
return (5 * humans) / Math.max(1, tilesOwned);
|
||||
}
|
||||
const targetPop = 0.03 * maxPop;
|
||||
const excessPop = Math.max(0, humans - targetPop);
|
||||
const targetTroops = 0.03 * maxTroops;
|
||||
const excessTroops = Math.max(0, humans - targetTroops);
|
||||
const scalingFactor = 500;
|
||||
|
||||
const steepness = 2;
|
||||
const normalizedExcess = excessPop / maxPop;
|
||||
const normalizedExcess = excessTroops / maxTroops;
|
||||
return scalingFactor * (1 - Math.exp(-steepness * normalizedExcess));
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,6 @@ export class DevConfig extends DefaultConfig {
|
||||
// return 1
|
||||
// }
|
||||
|
||||
// populationIncreaseRate(player: Player): number {
|
||||
// return this.maxPopulation(player)
|
||||
// }
|
||||
|
||||
// boatMaxDistance(): number {
|
||||
// return 5000
|
||||
// }
|
||||
|
||||
@@ -31,7 +31,6 @@ export class BotExecution implements Execution {
|
||||
|
||||
init(mg: Game) {
|
||||
this.mg = mg;
|
||||
this.bot.setTargetTroopRatio(0.7);
|
||||
}
|
||||
|
||||
tick(ticks: number) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export class DonateTroopsExecution implements Execution {
|
||||
this.recipient = mg.player(this.recipientID);
|
||||
this.troops ??= mg.config().defaultDonationAmount(this.sender);
|
||||
const maxDonation =
|
||||
mg.config().maxPopulation(this.recipient) - this.recipient.population();
|
||||
mg.config().maxTroops(this.recipient) - this.recipient.troops();
|
||||
this.troops = Math.min(this.troops, maxDonation);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import { MoveWarshipExecution } from "./MoveWarshipExecution";
|
||||
import { NoOpExecution } from "./NoOpExecution";
|
||||
import { QuickChatExecution } from "./QuickChatExecution";
|
||||
import { RetreatExecution } from "./RetreatExecution";
|
||||
import { SetTargetTroopRatioExecution } from "./SetTargetTroopRatioExecution";
|
||||
import { SpawnExecution } from "./SpawnExecution";
|
||||
import { TargetPlayerExecution } from "./TargetPlayerExecution";
|
||||
import { TransportShipExecution } from "./TransportShipExecution";
|
||||
@@ -98,8 +97,6 @@ export class Executor {
|
||||
);
|
||||
case "donate_gold":
|
||||
return new DonateGoldExecution(player, intent.recipient, intent.gold);
|
||||
case "troop_ratio":
|
||||
return new SetTargetTroopRatioExecution(player, intent.ratio);
|
||||
case "embargo":
|
||||
return new EmbargoExecution(player, intent.targetID, intent.action);
|
||||
case "build_unit":
|
||||
|
||||
@@ -153,13 +153,6 @@ export class FakeHumanExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this.player.troops() > 100_000 &&
|
||||
this.player.targetTroopRatio() > 0.7
|
||||
) {
|
||||
this.player.setTargetTroopRatio(0.7);
|
||||
}
|
||||
|
||||
this.updateRelationsFromEmbargos();
|
||||
this.behavior.handleAllianceRequests();
|
||||
this.handleUnits();
|
||||
|
||||
@@ -203,8 +203,8 @@ export class NukeExecution implements Execution {
|
||||
const toDestroy = this.tilesToDestroy();
|
||||
this.maybeBreakAlliances(toDestroy);
|
||||
|
||||
const maxPop = this.target().isPlayer()
|
||||
? this.mg.config().maxPopulation(this.target() as Player)
|
||||
const maxTroops = this.target().isPlayer()
|
||||
? this.mg.config().maxTroops(this.target() as Player)
|
||||
: 1;
|
||||
|
||||
for (const tile of toDestroy) {
|
||||
@@ -218,17 +218,7 @@ export class NukeExecution implements Execution {
|
||||
this.nukeType,
|
||||
owner.troops(),
|
||||
owner.numTilesOwned(),
|
||||
maxPop,
|
||||
),
|
||||
);
|
||||
owner.removeWorkers(
|
||||
this.mg
|
||||
.config()
|
||||
.nukeDeathFactor(
|
||||
this.nukeType,
|
||||
owner.workers(),
|
||||
owner.numTilesOwned(),
|
||||
maxPop,
|
||||
maxTroops,
|
||||
),
|
||||
);
|
||||
owner.outgoingAttacks().forEach((attack) => {
|
||||
@@ -239,7 +229,7 @@ export class NukeExecution implements Execution {
|
||||
this.nukeType,
|
||||
attack.troops(),
|
||||
owner.numTilesOwned(),
|
||||
maxPop,
|
||||
maxTroops,
|
||||
) ?? 0;
|
||||
attack.setTroops(attack.troops() - deaths);
|
||||
});
|
||||
@@ -251,7 +241,7 @@ export class NukeExecution implements Execution {
|
||||
this.nukeType,
|
||||
attack.troops(),
|
||||
owner.numTilesOwned(),
|
||||
maxPop,
|
||||
maxTroops,
|
||||
) ?? 0;
|
||||
attack.setTroops(attack.troops() - deaths);
|
||||
});
|
||||
|
||||
@@ -56,19 +56,14 @@ export class PlayerExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
|
||||
const popInc = this.config.populationIncreaseRate(this.player);
|
||||
this.player.addWorkers(popInc * (1 - this.player.targetTroopRatio()));
|
||||
this.player.addTroops(popInc * this.player.targetTroopRatio());
|
||||
const troopInc = this.config.troopIncreaseRate(this.player);
|
||||
this.player.addTroops(troopInc);
|
||||
const goldFromWorkers = this.config.goldAdditionRate(this.player);
|
||||
this.player.addGold(goldFromWorkers);
|
||||
|
||||
// Record stats
|
||||
this.mg.stats().goldWork(this.player, goldFromWorkers);
|
||||
|
||||
const adjustRate = this.config.troopAdjustmentRate(this.player);
|
||||
this.player.addTroops(adjustRate);
|
||||
this.player.removeWorkers(adjustRate);
|
||||
|
||||
const alliances = Array.from(this.player.alliances());
|
||||
for (const alliance of alliances) {
|
||||
if (alliance.expiresAt() <= this.mg.ticks()) {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Execution, Game, Player } from "../game/Game";
|
||||
|
||||
export class SetTargetTroopRatioExecution implements Execution {
|
||||
private active = true;
|
||||
|
||||
constructor(
|
||||
private player: Player,
|
||||
private targetTroopsRatio: number,
|
||||
) {}
|
||||
|
||||
init(mg: Game, ticks: number): void {}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.targetTroopsRatio < 0 || this.targetTroopsRatio > 1) {
|
||||
console.warn(
|
||||
`target troop ratio of ${this.targetTroopsRatio} for player ${this.player} invalid`,
|
||||
);
|
||||
} else {
|
||||
this.player.setTargetTroopRatio(this.targetTroopsRatio);
|
||||
}
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
activeDuringSpawnPhase(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -59,8 +59,8 @@ export class BotBehavior {
|
||||
}
|
||||
|
||||
private hasSufficientTroops(): boolean {
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
const maxTroops = this.game.config().maxTroops(this.player);
|
||||
const ratio = this.player.troops() / maxTroops;
|
||||
return ratio >= this.triggerRatio;
|
||||
}
|
||||
|
||||
@@ -208,8 +208,7 @@ export class BotBehavior {
|
||||
|
||||
sendAttack(target: Player | TerraNullius) {
|
||||
if (target.isPlayer() && this.player.isOnSameTeam(target)) return;
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const maxTroops = maxPop * this.player.targetTroopRatio();
|
||||
const maxTroops = this.game.config().maxTroops(this.player);
|
||||
const reserveRatio = target.isPlayer()
|
||||
? this.reserveRatio
|
||||
: this.expandRatio;
|
||||
|
||||
@@ -510,17 +510,11 @@ export interface Player {
|
||||
conquer(tile: TileRef): void;
|
||||
relinquish(tile: TileRef): void;
|
||||
|
||||
// Resources & Population
|
||||
// Resources & Troops
|
||||
gold(): Gold;
|
||||
population(): number;
|
||||
workers(): number;
|
||||
troops(): number;
|
||||
targetTroopRatio(): number;
|
||||
addGold(toAdd: Gold, tile?: TileRef): void;
|
||||
removeGold(toRemove: Gold): Gold;
|
||||
addWorkers(toAdd: number): void;
|
||||
removeWorkers(toRemove: number): void;
|
||||
setTargetTroopRatio(target: number): void;
|
||||
troops(): number;
|
||||
setTroops(troops: number): void;
|
||||
addTroops(troops: number): void;
|
||||
removeTroops(troops: number): number;
|
||||
|
||||
@@ -154,10 +154,7 @@ export interface PlayerUpdate {
|
||||
isDisconnected: boolean;
|
||||
tilesOwned: number;
|
||||
gold: Gold;
|
||||
population: number;
|
||||
workers: number;
|
||||
troops: number;
|
||||
targetTroopRatio: number;
|
||||
allies: number[];
|
||||
embargoes: Set<PlayerID>;
|
||||
isTraitor: boolean;
|
||||
|
||||
@@ -291,15 +291,6 @@ export class PlayerView {
|
||||
gold(): Gold {
|
||||
return this.data.gold;
|
||||
}
|
||||
population(): number {
|
||||
return this.data.population;
|
||||
}
|
||||
workers(): number {
|
||||
return this.data.workers;
|
||||
}
|
||||
targetTroopRatio(): number {
|
||||
return this.data.targetTroopRatio;
|
||||
}
|
||||
|
||||
troops(): number {
|
||||
return this.data.troops;
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ClientID } from "../Schemas";
|
||||
import {
|
||||
assertNever,
|
||||
distSortUnit,
|
||||
maxInt,
|
||||
minInt,
|
||||
simpleHash,
|
||||
toInt,
|
||||
@@ -72,10 +71,6 @@ export class PlayerImpl implements Player {
|
||||
|
||||
private _gold: bigint;
|
||||
private _troops: bigint;
|
||||
private _workers: bigint;
|
||||
|
||||
// 0 to 100
|
||||
private _targetTroopRatio: bigint;
|
||||
|
||||
markedTraitorTick = -1;
|
||||
|
||||
@@ -115,9 +110,7 @@ export class PlayerImpl implements Player {
|
||||
private readonly _team: Team | null,
|
||||
) {
|
||||
this._name = sanitizeUsername(playerInfo.name);
|
||||
this._targetTroopRatio = 95n;
|
||||
this._troops = toInt(startTroops);
|
||||
this._workers = 0n;
|
||||
this._gold = 0n;
|
||||
this._displayName = this._name; // processName(this._name)
|
||||
this._pseudo_random = new PseudoRandom(simpleHash(this.playerInfo.id));
|
||||
@@ -144,10 +137,7 @@ export class PlayerImpl implements Player {
|
||||
isDisconnected: this.isDisconnected(),
|
||||
tilesOwned: this.numTilesOwned(),
|
||||
gold: this._gold,
|
||||
population: this.population(),
|
||||
workers: this.workers(),
|
||||
troops: this.troops(),
|
||||
targetTroopRatio: this.targetTroopRatio(),
|
||||
allies: this.alliances().map((a) => a.other(this).smallID()),
|
||||
embargoes: new Set([...this.embargoes.keys()].map((p) => p.toString())),
|
||||
isTraitor: this.isTraitor(),
|
||||
@@ -731,32 +721,6 @@ export class PlayerImpl implements Player {
|
||||
return actualRemoved;
|
||||
}
|
||||
|
||||
population(): number {
|
||||
return Number(this._troops + this._workers);
|
||||
}
|
||||
workers(): number {
|
||||
return Math.max(1, Number(this._workers));
|
||||
}
|
||||
addWorkers(toAdd: number): void {
|
||||
this._workers += toInt(toAdd);
|
||||
}
|
||||
removeWorkers(toRemove: number): void {
|
||||
this._workers = maxInt(1n, this._workers - toInt(toRemove));
|
||||
}
|
||||
|
||||
targetTroopRatio(): number {
|
||||
return Number(this._targetTroopRatio) / 100;
|
||||
}
|
||||
|
||||
setTargetTroopRatio(target: number): void {
|
||||
if (target < 0 || target > 1) {
|
||||
throw new Error(
|
||||
`invalid targetTroopRatio ${target} set on player ${PlayerImpl}`,
|
||||
);
|
||||
}
|
||||
this._targetTroopRatio = toInt(target * 100);
|
||||
}
|
||||
|
||||
troops(): number {
|
||||
return Number(this._troops);
|
||||
}
|
||||
@@ -1051,7 +1015,7 @@ export class PlayerImpl implements Player {
|
||||
|
||||
hash(): number {
|
||||
return (
|
||||
simpleHash(this.id()) * (this.population() + this.numTilesOwned()) +
|
||||
simpleHash(this.id()) * (this.troops() + this.numTilesOwned()) +
|
||||
this._units.reduce((acc, unit) => acc + unit.hash(), 0)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user