mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 11:30:43 +00:00
Use bigint for gold (#1000)
## Description: - Switch gold to bigint. - Remove unused or untrusted values from event payloads. ## 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 understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
AllPlayers,
|
||||
Cell,
|
||||
GameType,
|
||||
Gold,
|
||||
PlayerID,
|
||||
PlayerType,
|
||||
Tick,
|
||||
@@ -94,15 +95,13 @@ export class SendEmojiIntentEvent implements GameEvent {
|
||||
|
||||
export class SendDonateGoldIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly sender: PlayerView,
|
||||
public readonly recipient: PlayerView,
|
||||
public readonly gold: number | null,
|
||||
public readonly gold: Gold | null,
|
||||
) {}
|
||||
}
|
||||
|
||||
export class SendDonateTroopsIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly sender: PlayerView,
|
||||
public readonly recipient: PlayerView,
|
||||
public readonly troops: number | null,
|
||||
) {}
|
||||
@@ -110,7 +109,6 @@ export class SendDonateTroopsIntentEvent implements GameEvent {
|
||||
|
||||
export class SendQuickChatEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly sender: PlayerView,
|
||||
public readonly recipient: PlayerView,
|
||||
public readonly quickChatKey: string,
|
||||
public readonly variables: { [key: string]: string },
|
||||
@@ -119,17 +117,13 @@ export class SendQuickChatEvent implements GameEvent {
|
||||
|
||||
export class SendEmbargoIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly sender: PlayerView,
|
||||
public readonly target: PlayerView,
|
||||
public readonly action: "start" | "stop",
|
||||
) {}
|
||||
}
|
||||
|
||||
export class CancelAttackIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly playerID: PlayerID,
|
||||
public readonly attackID: string,
|
||||
) {}
|
||||
constructor(public readonly attackID: string) {}
|
||||
}
|
||||
|
||||
export class CancelBoatIntentEvent implements GameEvent {
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@ export function renderTroops(troops: number): string {
|
||||
return renderNumber(troops / 10);
|
||||
}
|
||||
|
||||
export function renderNumber(num: number): string {
|
||||
export function renderNumber(num: number | bigint): string {
|
||||
num = Number(num);
|
||||
num = Math.max(num, 0);
|
||||
|
||||
if (num >= 10_000_000) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import samlauncherIcon from "../../../../resources/images/SamLauncherIconWhite.s
|
||||
import shieldIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, PlayerActions, UnitType } from "../../../core/game/Game";
|
||||
import { Cell, Gold, PlayerActions, UnitType } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { BuildUnitIntentEvent } from "../../Transport";
|
||||
@@ -314,13 +314,13 @@ export class BuildMenu extends LitElement implements Layer {
|
||||
return unit[0].canBuild !== false;
|
||||
}
|
||||
|
||||
private cost(item: BuildItemDisplay): number {
|
||||
private cost(item: BuildItemDisplay): Gold {
|
||||
for (const bu of this.playerActions?.buildableUnits ?? []) {
|
||||
if (bu.type === item.unitType) {
|
||||
return bu.cost;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 0n;
|
||||
}
|
||||
|
||||
private count(item: BuildItemDisplay): string {
|
||||
|
||||
@@ -236,7 +236,6 @@ export class ChatModal extends LitElement {
|
||||
|
||||
this.eventBus.emit(
|
||||
new SendQuickChatEvent(
|
||||
this.sender,
|
||||
this.recipient,
|
||||
this.selectedQuickChatKey,
|
||||
variables,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { LitElement, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Gold } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { AttackRatioEvent } from "../../InputHandler";
|
||||
import { SendSetTargetTroopRatioEvent } from "../../Transport";
|
||||
@@ -46,10 +47,10 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
private _manpower: number = 0;
|
||||
|
||||
@state()
|
||||
private _gold: number;
|
||||
private _gold: Gold;
|
||||
|
||||
@state()
|
||||
private _goldPerSecond: number;
|
||||
private _goldPerSecond: Gold;
|
||||
|
||||
private _lastPopulationIncreaseRate: number;
|
||||
|
||||
@@ -124,7 +125,7 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
this._troops = player.troops();
|
||||
this._workers = player.workers();
|
||||
this.popRate = this.game.config().populationIncreaseRate(player) * 10;
|
||||
this._goldPerSecond = this.game.config().goldAdditionRate(player) * 10;
|
||||
this._goldPerSecond = this.game.config().goldAdditionRate(player) * 10n;
|
||||
|
||||
this.currentTroopRatio = player.troops() / player.population();
|
||||
this.requestUpdate();
|
||||
|
||||
@@ -380,7 +380,7 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
emitCancelAttackIntent(id: string) {
|
||||
const myPlayer = this.game.myPlayer();
|
||||
if (!myPlayer) return;
|
||||
this.eventBus.emit(new CancelAttackIntentEvent(myPlayer.id(), id));
|
||||
this.eventBus.emit(new CancelAttackIntentEvent(id));
|
||||
}
|
||||
|
||||
emitBoatCancelIntent(id: number) {
|
||||
|
||||
@@ -90,7 +90,6 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
e.stopPropagation();
|
||||
this.eventBus.emit(
|
||||
new SendDonateTroopsIntentEvent(
|
||||
myPlayer,
|
||||
other,
|
||||
myPlayer.troops() * this.uiState.attackRatio,
|
||||
),
|
||||
@@ -104,7 +103,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
other: PlayerView,
|
||||
) {
|
||||
e.stopPropagation();
|
||||
this.eventBus.emit(new SendDonateGoldIntentEvent(myPlayer, other, null));
|
||||
this.eventBus.emit(new SendDonateGoldIntentEvent(other, null));
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -114,7 +113,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
other: PlayerView,
|
||||
) {
|
||||
e.stopPropagation();
|
||||
this.eventBus.emit(new SendEmbargoIntentEvent(myPlayer, other, "start"));
|
||||
this.eventBus.emit(new SendEmbargoIntentEvent(other, "start"));
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -124,7 +123,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
other: PlayerView,
|
||||
) {
|
||||
e.stopPropagation();
|
||||
this.eventBus.emit(new SendEmbargoIntentEvent(myPlayer, other, "stop"));
|
||||
this.eventBus.emit(new SendEmbargoIntentEvent(other, "stop"));
|
||||
this.hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export class TeamStats extends LitElement implements Layer {
|
||||
|
||||
this.teams = Object.entries(grouped)
|
||||
.map(([teamStr, teamPlayers]) => {
|
||||
let totalGold = 0;
|
||||
let totalGold = 0n;
|
||||
let totalTroops = 0;
|
||||
let totalScoreSort = 0;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export class TopBar extends LitElement implements Layer {
|
||||
|
||||
const popRate = this.game.config().populationIncreaseRate(myPlayer) * 10;
|
||||
const maxPop = this.game.config().maxPopulation(myPlayer);
|
||||
const goldPerSecond = this.game.config().goldAdditionRate(myPlayer) * 10;
|
||||
const goldPerSecond = this.game.config().goldAdditionRate(myPlayer) * 10n;
|
||||
|
||||
return html`
|
||||
<div
|
||||
|
||||
+1
-1
@@ -240,7 +240,7 @@ export const EmbargoIntentSchema = BaseIntentSchema.extend({
|
||||
export const DonateGoldIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("donate_gold"),
|
||||
recipient: ID,
|
||||
gold: z.number().nullable(),
|
||||
gold: z.bigint().nullable(),
|
||||
});
|
||||
|
||||
export const DonateTroopIntentSchema = BaseIntentSchema.extend({
|
||||
|
||||
@@ -84,7 +84,7 @@ export interface Config {
|
||||
|
||||
startManpower(playerInfo: PlayerInfo): number;
|
||||
populationIncreaseRate(player: Player | PlayerView): number;
|
||||
goldAdditionRate(player: Player | PlayerView): number;
|
||||
goldAdditionRate(player: Player | PlayerView): Gold;
|
||||
troopAdjustmentRate(player: Player): number;
|
||||
attackTilesPerTick(
|
||||
attckTroops: number,
|
||||
|
||||
@@ -272,7 +272,7 @@ export class DefaultConfig implements Config {
|
||||
return this._gameConfig.infiniteTroops;
|
||||
}
|
||||
tradeShipGold(dist: number): Gold {
|
||||
return 10000 + 150 * Math.pow(dist, 1.1);
|
||||
return BigInt(Math.floor(10000 + 150 * Math.pow(dist, 1.1)));
|
||||
}
|
||||
tradeShipSpawnRate(numberOfPorts: number): number {
|
||||
return Math.min(50, Math.round(10 * Math.pow(numberOfPorts, 0.6)));
|
||||
@@ -282,44 +282,49 @@ export class DefaultConfig implements Config {
|
||||
switch (type) {
|
||||
case UnitType.TransportShip:
|
||||
return {
|
||||
cost: () => 0,
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.Warship:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: Math.min(
|
||||
1_000_000,
|
||||
(p.unitsIncludingConstruction(UnitType.Warship).length + 1) *
|
||||
250_000,
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
(p.unitsIncludingConstruction(UnitType.Warship).length +
|
||||
1) *
|
||||
250_000,
|
||||
),
|
||||
),
|
||||
territoryBound: false,
|
||||
maxHealth: 1000,
|
||||
};
|
||||
case UnitType.Shell:
|
||||
return {
|
||||
cost: () => 0,
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
damage: 250,
|
||||
};
|
||||
case UnitType.SAMMissile:
|
||||
return {
|
||||
cost: () => 0,
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.Port:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: Math.min(
|
||||
1_000_000,
|
||||
Math.pow(
|
||||
2,
|
||||
p.unitsIncludingConstruction(UnitType.Port).length,
|
||||
) * 125_000,
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
Math.pow(
|
||||
2,
|
||||
p.unitsIncludingConstruction(UnitType.Port).length,
|
||||
) * 125_000,
|
||||
),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
@@ -327,41 +332,43 @@ export class DefaultConfig implements Config {
|
||||
case UnitType.AtomBomb:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold() ? 0 : 750_000,
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: 750_000n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.HydrogenBomb:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: 5_000_000,
|
||||
? 0n
|
||||
: 5_000_000n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.MIRV:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: 25_000_000,
|
||||
? 0n
|
||||
: 25_000_000n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.MIRVWarhead:
|
||||
return {
|
||||
cost: () => 0,
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.TradeShip:
|
||||
return {
|
||||
cost: () => 0,
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.MissileSilo:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: 1_000_000,
|
||||
? 0n
|
||||
: 1_000_000n,
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 10 * 10,
|
||||
};
|
||||
@@ -369,12 +376,14 @@ export class DefaultConfig implements Config {
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: Math.min(
|
||||
250_000,
|
||||
(p.unitsIncludingConstruction(UnitType.DefensePost).length +
|
||||
1) *
|
||||
50_000,
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
250_000,
|
||||
(p.unitsIncludingConstruction(UnitType.DefensePost).length +
|
||||
1) *
|
||||
50_000,
|
||||
),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 5 * 10,
|
||||
@@ -383,12 +392,14 @@ export class DefaultConfig implements Config {
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: Math.min(
|
||||
3_000_000,
|
||||
(p.unitsIncludingConstruction(UnitType.SAMLauncher).length +
|
||||
1) *
|
||||
1_500_000,
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
3_000_000,
|
||||
(p.unitsIncludingConstruction(UnitType.SAMLauncher).length +
|
||||
1) *
|
||||
1_500_000,
|
||||
),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 30 * 10,
|
||||
@@ -397,20 +408,22 @@ export class DefaultConfig implements Config {
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: Math.min(
|
||||
1_000_000,
|
||||
Math.pow(
|
||||
2,
|
||||
p.unitsIncludingConstruction(UnitType.City).length,
|
||||
) * 125_000,
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
Math.pow(
|
||||
2,
|
||||
p.unitsIncludingConstruction(UnitType.City).length,
|
||||
) * 125_000,
|
||||
),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
};
|
||||
case UnitType.Construction:
|
||||
return {
|
||||
cost: () => 0,
|
||||
cost: () => 0n,
|
||||
territoryBound: true,
|
||||
};
|
||||
default:
|
||||
@@ -688,8 +701,8 @@ export class DefaultConfig implements Config {
|
||||
return Math.min(player.population() + toAdd, max) - player.population();
|
||||
}
|
||||
|
||||
goldAdditionRate(player: Player): number {
|
||||
return 0.045 * player.workers() ** 0.7;
|
||||
goldAdditionRate(player: Player): Gold {
|
||||
return BigInt(Math.floor(0.045 * player.workers() ** 0.7));
|
||||
}
|
||||
|
||||
troopAdjustmentRate(player: Player): number {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { consolex } from "../Consolex";
|
||||
import {
|
||||
Execution,
|
||||
Game,
|
||||
Gold,
|
||||
Player,
|
||||
PlayerID,
|
||||
Tick,
|
||||
@@ -26,7 +27,7 @@ export class ConstructionExecution implements Execution {
|
||||
|
||||
private ticksUntilComplete: Tick;
|
||||
|
||||
private cost: number;
|
||||
private cost: Gold;
|
||||
|
||||
constructor(
|
||||
private ownerId: PlayerID,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { consolex } from "../Consolex";
|
||||
import { Execution, Game, Player, PlayerID } from "../game/Game";
|
||||
import { Execution, Game, Gold, Player, PlayerID } from "../game/Game";
|
||||
|
||||
export class DonateGoldExecution implements Execution {
|
||||
private sender: Player;
|
||||
@@ -10,7 +10,7 @@ export class DonateGoldExecution implements Execution {
|
||||
constructor(
|
||||
private senderID: PlayerID,
|
||||
private recipientID: PlayerID,
|
||||
private gold: number | null,
|
||||
private gold: Gold | null,
|
||||
) {}
|
||||
|
||||
init(mg: Game, ticks: number): void {
|
||||
@@ -28,14 +28,16 @@ export class DonateGoldExecution implements Execution {
|
||||
this.sender = mg.player(this.senderID);
|
||||
this.recipient = mg.player(this.recipientID);
|
||||
if (this.gold === null) {
|
||||
this.gold = Math.round(this.sender.gold() / 3);
|
||||
this.gold = this.sender.gold() / 3n;
|
||||
}
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.gold === null) throw new Error("not initialized");
|
||||
if (this.sender.canDonate(this.recipient)) {
|
||||
this.sender.donateGold(this.recipient, this.gold);
|
||||
if (
|
||||
this.sender.canDonate(this.recipient) &&
|
||||
this.sender.donateGold(this.recipient, this.gold)
|
||||
) {
|
||||
this.recipient.updateRelation(this.sender, 50);
|
||||
} else {
|
||||
consolex.warn(
|
||||
|
||||
@@ -34,8 +34,10 @@ export class DonateTroopsExecution implements Execution {
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.troops === null) throw new Error("not initialized");
|
||||
if (this.sender.canDonate(this.recipient)) {
|
||||
this.sender.donateTroops(this.recipient, this.troops);
|
||||
if (
|
||||
this.sender.canDonate(this.recipient) &&
|
||||
this.sender.donateTroops(this.recipient, this.troops)
|
||||
) {
|
||||
this.recipient.updateRelation(this.sender, 50);
|
||||
} else {
|
||||
consolex.warn(
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Difficulty,
|
||||
Execution,
|
||||
Game,
|
||||
Gold,
|
||||
Nation,
|
||||
Player,
|
||||
PlayerID,
|
||||
@@ -543,7 +544,7 @@ export class FakeHumanExecution implements Execution {
|
||||
return null;
|
||||
}
|
||||
|
||||
private cost(type: UnitType): number {
|
||||
private cost(type: UnitType): Gold {
|
||||
if (this.player === null) throw new Error("not initialized");
|
||||
return this.mg.unitInfo(type).cost(this.player);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Stats } from "./Stats";
|
||||
|
||||
export type PlayerID = string;
|
||||
export type Tick = number;
|
||||
export type Gold = number;
|
||||
export type Gold = bigint;
|
||||
|
||||
export const AllPlayers = "AllPlayers" as const;
|
||||
|
||||
@@ -450,7 +450,7 @@ export interface Player {
|
||||
troops(): number;
|
||||
targetTroopRatio(): number;
|
||||
addGold(toAdd: Gold): void;
|
||||
removeGold(toRemove: Gold): void;
|
||||
removeGold(toRemove: Gold): Gold;
|
||||
addWorkers(toAdd: number): void;
|
||||
removeWorkers(toRemove: number): void;
|
||||
setTargetTroopRatio(target: number): void;
|
||||
@@ -506,8 +506,8 @@ export interface Player {
|
||||
|
||||
// Donation
|
||||
canDonate(recipient: Player): boolean;
|
||||
donateTroops(recipient: Player, troops: number): void;
|
||||
donateGold(recipient: Player, gold: number): void;
|
||||
donateTroops(recipient: Player, troops: number): boolean;
|
||||
donateGold(recipient: Player, gold: Gold): boolean;
|
||||
|
||||
// Embargo
|
||||
hasEmbargoAgainst(other: Player): boolean;
|
||||
@@ -619,7 +619,7 @@ export interface PlayerActions {
|
||||
export interface BuildableUnit {
|
||||
canBuild: TileRef | false;
|
||||
type: UnitType;
|
||||
cost: number;
|
||||
cost: Gold;
|
||||
}
|
||||
|
||||
export interface PlayerProfile {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AllPlayersStats, ClientID } from "../Schemas";
|
||||
import {
|
||||
EmojiMessage,
|
||||
GameUpdates,
|
||||
Gold,
|
||||
MessageType,
|
||||
NameViewData,
|
||||
PlayerID,
|
||||
@@ -103,7 +104,7 @@ export interface PlayerUpdate {
|
||||
playerType: PlayerType;
|
||||
isAlive: boolean;
|
||||
tilesOwned: number;
|
||||
gold: number;
|
||||
gold: Gold;
|
||||
population: number;
|
||||
workers: number;
|
||||
troops: number;
|
||||
|
||||
+23
-12
@@ -137,7 +137,7 @@ export class PlayerImpl implements Player {
|
||||
playerType: this.type(),
|
||||
isAlive: this.isAlive(),
|
||||
tilesOwned: this.numTilesOwned(),
|
||||
gold: Number(this._gold),
|
||||
gold: this._gold,
|
||||
population: this.population(),
|
||||
workers: this.workers(),
|
||||
troops: this.troops(),
|
||||
@@ -539,9 +539,13 @@ export class PlayerImpl implements Player {
|
||||
return true;
|
||||
}
|
||||
|
||||
donateTroops(recipient: Player, troops: number): void {
|
||||
donateTroops(recipient: Player, troops: number): boolean {
|
||||
if (troops <= 0) return false;
|
||||
const removed = this.removeTroops(troops);
|
||||
if (removed === 0) return false;
|
||||
recipient.addTroops(removed);
|
||||
|
||||
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
|
||||
recipient.addTroops(this.removeTroops(troops));
|
||||
this.mg.displayMessage(
|
||||
`Sent ${renderTroops(troops)} troops to ${recipient.name()}`,
|
||||
MessageType.INFO,
|
||||
@@ -552,10 +556,16 @@ export class PlayerImpl implements Player {
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
donateGold(recipient: Player, gold: number): void {
|
||||
|
||||
donateGold(recipient: Player, gold: Gold): boolean {
|
||||
if (gold <= 0n) return false;
|
||||
const removed = this.removeGold(gold);
|
||||
if (removed === 0n) return false;
|
||||
recipient.addGold(removed);
|
||||
|
||||
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
|
||||
recipient.addGold(this.removeGold(gold));
|
||||
this.mg.displayMessage(
|
||||
`Sent ${renderNumber(gold)} gold to ${recipient.name()}`,
|
||||
MessageType.INFO,
|
||||
@@ -566,6 +576,7 @@ export class PlayerImpl implements Player {
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
hasEmbargoAgainst(other: Player): boolean {
|
||||
@@ -632,20 +643,20 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
gold(): Gold {
|
||||
return Number(this._gold);
|
||||
return this._gold;
|
||||
}
|
||||
|
||||
addGold(toAdd: Gold): void {
|
||||
this._gold += toInt(toAdd);
|
||||
this._gold += toAdd;
|
||||
}
|
||||
|
||||
removeGold(toRemove: Gold): number {
|
||||
if (toRemove <= 1) {
|
||||
return 0;
|
||||
removeGold(toRemove: Gold): Gold {
|
||||
if (toRemove <= 0n) {
|
||||
return 0n;
|
||||
}
|
||||
const actualRemoved = minInt(this._gold, toInt(toRemove));
|
||||
const actualRemoved = minInt(this._gold, toRemove);
|
||||
this._gold -= actualRemoved;
|
||||
return Number(actualRemoved);
|
||||
return actualRemoved;
|
||||
}
|
||||
|
||||
population(): number {
|
||||
|
||||
Reference in New Issue
Block a user