mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-29 03:44:40 +00:00
Use bigint for gold (#1000)
This commit is contained in:
+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,
|
||||
|
||||
@@ -281,7 +281,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.round(10 * Math.pow(numberOfPorts, 0.38));
|
||||
@@ -291,44 +291,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,
|
||||
@@ -336,41 +341,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,
|
||||
};
|
||||
@@ -378,12 +385,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,
|
||||
@@ -392,12 +401,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,
|
||||
@@ -406,20 +417,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:
|
||||
@@ -684,8 +697,8 @@ export class DefaultConfig implements Config {
|
||||
return Math.min(totalPop + toAdd, max) - totalPop;
|
||||
}
|
||||
|
||||
goldAdditionRate(player: Player): number {
|
||||
return 0.08 * player.workers() ** 0.65;
|
||||
goldAdditionRate(player: Player): bigint {
|
||||
return BigInt(Math.floor(0.08 * player.workers() ** 0.65));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -451,7 +451,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;
|
||||
@@ -507,8 +507,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;
|
||||
@@ -620,7 +620,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;
|
||||
totalPopulation: number;
|
||||
workers: 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(),
|
||||
totalPopulation: this.totalPopulation(),
|
||||
workers: this.workers(),
|
||||
@@ -540,9 +540,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,
|
||||
@@ -553,10 +557,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,
|
||||
@@ -567,6 +577,7 @@ export class PlayerImpl implements Player {
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
hasEmbargoAgainst(other: Player): boolean {
|
||||
@@ -633,20 +644,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