mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 12:32:21 +00:00
Update tradeship spawn & gold meta (#3232)
## Description: Now that pathfinding is much more efficient with hpa*, we can add more trade ships. This PR does the following: 1. No gold bonus for additional ports, keeps the meta simple 2. cut the gold per trade ship roughly in half. 3. Use a "pity bonus", the more times a port has failed to spawn a tradeship, the higher the likelyhood it will spawn one 4. Increase the sigmoid so the mid-point is 200, with a half life of 50. In tests about ~400 trade ships max. It's pretty difficult to balance on singleplayer so I'm sure the values will be adjusted after playtests. ## 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 ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -126,11 +126,10 @@ export interface Config {
|
||||
defaultDonationAmount(sender: Player): number;
|
||||
unitInfo(type: UnitType): UnitInfo;
|
||||
tradeShipShortRangeDebuff(): number;
|
||||
tradeShipGold(dist: number, numPorts: number): Gold;
|
||||
tradeShipGold(dist: number): Gold;
|
||||
tradeShipSpawnRate(
|
||||
tradeShipSpawnRejections: number,
|
||||
numTradeShips: number,
|
||||
numPlayerPorts: number,
|
||||
numPlayerTradeShips: number,
|
||||
): number;
|
||||
trainGold(rel: "self" | "team" | "ally" | "other"): Gold;
|
||||
trainSpawnRate(numPlayerFactories: number): number;
|
||||
|
||||
@@ -297,52 +297,29 @@ export class DefaultConfig implements Config {
|
||||
return 120;
|
||||
}
|
||||
|
||||
tradeShipGold(dist: number, numPorts: number): Gold {
|
||||
tradeShipGold(dist: number): Gold {
|
||||
// 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 - debuff))) + 100 * dist;
|
||||
const numPortBonus = numPorts - 1;
|
||||
// Hyperbolic decay, midpoint at 5 ports, 3x bonus max.
|
||||
const bonus = 1 + 2 * (numPortBonus / (numPortBonus + 5));
|
||||
50_000 / (1 + Math.exp(-0.03 * (dist - debuff))) + 50 * dist;
|
||||
const multiplier = this.goldMultiplier();
|
||||
return BigInt(Math.floor(baseGold * bonus * multiplier));
|
||||
return BigInt(Math.floor(baseGold * multiplier));
|
||||
}
|
||||
|
||||
// Probability of trade ship spawn = 1 / tradeShipSpawnRate
|
||||
tradeShipSpawnRate(
|
||||
tradeShipSpawnRejections: number,
|
||||
numTradeShips: number,
|
||||
numPlayerPorts: number,
|
||||
numPlayerTradeShips: number,
|
||||
): number {
|
||||
// Geometric mean of base spawn rate and port multiplier
|
||||
const combined = Math.sqrt(
|
||||
this.tradeShipBaseSpawn(numTradeShips, numPlayerTradeShips) *
|
||||
this.tradeShipPortMultiplier(numPlayerPorts),
|
||||
);
|
||||
const decayRate = Math.LN2 / 50;
|
||||
|
||||
return Math.floor(25 / combined);
|
||||
}
|
||||
// Approaches 0 as numTradeShips increase
|
||||
const baseSpawnRate = 1 - sigmoid(numTradeShips, decayRate, 200);
|
||||
|
||||
private tradeShipBaseSpawn(
|
||||
numTradeShips: number,
|
||||
numPlayerTradeShips: number,
|
||||
): number {
|
||||
if (numPlayerTradeShips < 3) {
|
||||
// If other players have many ports, then they can starve out smaller players.
|
||||
// So this prevents smaller players from being completely starved out.
|
||||
return 1;
|
||||
}
|
||||
const decayRate = Math.LN2 / 10;
|
||||
return 1 - sigmoid(numTradeShips, decayRate, 55);
|
||||
}
|
||||
// Pity timer: increases spawn chance after consecutive rejections
|
||||
const rejectionModifier = 1 / (tradeShipSpawnRejections + 1);
|
||||
|
||||
private tradeShipPortMultiplier(numPlayerPorts: number): number {
|
||||
// Hyperbolic decay function with midpoint at 10 ports
|
||||
// Expected trade ship spawn rate is proportional to numPlayerPorts * multiplier
|
||||
// Gradual decay prevents scenario where more ports => fewer ships
|
||||
const decayRate = 1 / 10;
|
||||
return 1 / (1 + decayRate * numPlayerPorts);
|
||||
return Math.floor((100 * rejectionModifier) / baseSpawnRate);
|
||||
}
|
||||
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
|
||||
@@ -9,6 +9,7 @@ export class PortExecution implements Execution {
|
||||
private port: Unit;
|
||||
private random: PseudoRandom;
|
||||
private checkOffset: number;
|
||||
private tradeShipSpawnRejections = 0;
|
||||
|
||||
constructor(port: Unit) {
|
||||
this.port = port;
|
||||
@@ -69,17 +70,15 @@ export class PortExecution implements Execution {
|
||||
|
||||
shouldSpawnTradeShip(): boolean {
|
||||
const numTradeShips = this.mg.unitCount(UnitType.TradeShip);
|
||||
const numPlayerPorts = this.port!.owner().unitCount(UnitType.Port);
|
||||
const numPlayerTradeShips = this.port!.owner().unitCount(
|
||||
UnitType.TradeShip,
|
||||
);
|
||||
const spawnRate = this.mg
|
||||
.config()
|
||||
.tradeShipSpawnRate(numTradeShips, numPlayerPorts, numPlayerTradeShips);
|
||||
.tradeShipSpawnRate(this.tradeShipSpawnRejections, numTradeShips);
|
||||
for (let i = 0; i < this.port!.level(); i++) {
|
||||
if (this.random.chance(spawnRate)) {
|
||||
this.tradeShipSpawnRejections = 0;
|
||||
return true;
|
||||
}
|
||||
this.tradeShipSpawnRejections++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -133,12 +133,7 @@ export class TradeShipExecution implements Execution {
|
||||
private complete() {
|
||||
this.active = false;
|
||||
this.tradeShip!.delete(false);
|
||||
const gold = this.mg
|
||||
.config()
|
||||
.tradeShipGold(
|
||||
this.tilesTraveled,
|
||||
this.tradeShip!.owner().unitCount(UnitType.Port),
|
||||
);
|
||||
const gold = this.mg.config().tradeShipGold(this.tilesTraveled);
|
||||
|
||||
if (this.wasCaptured) {
|
||||
this.tradeShip!.owner().addGold(gold, this._dstPort.tile());
|
||||
|
||||
Reference in New Issue
Block a user