Configuration¶
Override game mechanics by implementing a partial Config class.
Overview¶
Mods can customize game behavior by providing a Config class:
import { Config, Player } from '@lands.io/mod-sdk';
export class MyConfig implements Partial<Config> {
// Only override what you need
attackAmount(attacker: Player, defender: Player): number {
return attacker.troops() / 10; // Reduced attack power
}
}
Attach it to your mod:
DefaultConfig Reference¶
The DefaultConfig class provides the base implementation for all game mechanics.
Your config only needs to override what you want to change:
/**
* Default game configuration used as the base.
* Mods don't need to extend this - instead implement Partial<Config>
* and attach it to your Mod class via the static Config property.
*
* @example
* ```typescript
* import { Mod, Config } from '@lands.io/mod-sdk';
*
* class MyConfig implements Partial<Config> {
* // Override only what you need - rest falls back to DefaultConfig
* attackAmount(attacker, defender) {
* return attacker.troops() / 10;
* }
* }
*
* export default class MyMod extends Mod {
* static Config = MyConfig;
* // ... lifecycle hooks
* }
* ```
*/
export class DefaultConfig implements Config {
constructor(protected _gameConfig: GameConfig) {}
// ============================================================
// Game configuration (override to customize)
// ============================================================
gameConfig(): GameConfig {
return this._gameConfig;
}
spawnImmunityDuration(): Tick {
return 5 * 10;
}
numSpawnPhaseTurns(): number {
return this._gameConfig.gameType === GameType.Singleplayer ? 70 : 150;
}
percentageTilesOwnedToWin(): number {
return 80;
}
bots(): number {
return this._gameConfig.bots;
}
numBots(): number {
return this.bots();
}
fakeHumans(): number {
return this._gameConfig.fakeHumans || 0;
}
infiniteGold(): boolean {
return this._gameConfig.infiniteGold;
}
infiniteTroops(): boolean {
return this._gameConfig.infiniteTroops;
}
// ============================================================
// Player settings (override to customize)
// ============================================================
startManpower(player: Player): number {
const maxPop = this.maxPopulation(player);
const ratio = player.type() === PlayerType.Bot ? 0.5 : 0.75;
return maxPop * ratio;
}
maxPopulation(player: Player): number {
const maxPop = this.infiniteTroops()
? 1_000_000_000
: Math.pow(player.numTilesOwned(), 0.6) * 100;
return maxPop;
}
populationIncreaseRate(player: Player): number {
const max = this.maxPopulation(player);
const toAdd = max * 0.0007;
return Math.min(player.population() + toAdd, max) - player.population();
}
goldAdditionRate(player: Player): number {
return Math.sqrt(player.workers() * player.numTilesOwned()) / 200;
}
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);
return adjustment < 0 ? adjustment * 5 : adjustment;
}
// ============================================================
// Combat settings (override to customize)
// ============================================================
attackAmount(attacker: Player, defender: Player | TerraNullius): number {
if (attacker.type() === PlayerType.Bot) {
return attacker.troops() / 20;
}
return attacker.troops() / 5;
}
attackTilesPerTick(
attackTroops: number,
attacker: Player,
defender: Player | TerraNullius,
numAdjacentTilesWithEnemy: number,
): number {
if ('isPlayer' in defender && defender.isPlayer()) {
return (
within(((5 * attackTroops) / defender.troops()) * 2, 0.01, 0.5) *
numAdjacentTilesWithEnemy *
3
);
}
const troopFactor = Math.min(3, Math.max(1, Math.sqrt(attackTroops / 500) * 3));
return numAdjacentTilesWithEnemy * 2 * troopFactor;
}
// ============================================================
// Unit costs (override to customize)
// ============================================================
unitInfo(type: UnitType): UnitInfo {
// Default costs - override for custom pricing
const defaultInfo: UnitInfo = {
cost: () => 100_000,
territoryBound: true,
};
return defaultInfo;
}
// ============================================================
// Difficulty settings
// ============================================================
difficultyModifier(difficulty: Difficulty): number {
switch (difficulty) {
case Difficulty.Easy:
return 1;
case Difficulty.Medium:
return 3;
case Difficulty.Hard:
return 9;
case Difficulty.Impossible:
return 18;
}
}
}
Available Overrides¶
Combat Settings¶
attackAmount(attacker: Player, defender: Player | TerraNullius): number {
if (attacker.type() === PlayerType.Bot) {
return attacker.troops() / 20;
}
return attacker.troops() / 5;
}
attackTilesPerTick(
attackTroops: number,
attacker: Player,
defender: Player | TerraNullius,
numAdjacentTilesWithEnemy: number,
): number {
if ('isPlayer' in defender && defender.isPlayer()) {
return (
within(((5 * attackTroops) / defender.troops()) * 2, 0.01, 0.5) *
numAdjacentTilesWithEnemy *
3
);
}
const troopFactor = Math.min(3, Math.max(1, Math.sqrt(attackTroops / 500) * 3));
return numAdjacentTilesWithEnemy * 2 * troopFactor;
}
Player Settings¶
startManpower(player: Player): number {
const maxPop = this.maxPopulation(player);
const ratio = player.type() === PlayerType.Bot ? 0.5 : 0.75;
return maxPop * ratio;
}
populationIncreaseRate(player: Player): number {
const max = this.maxPopulation(player);
const toAdd = max * 0.0007;
return Math.min(player.population() + toAdd, max) - player.population();
}
goldAdditionRate(player: Player): number {
return Math.sqrt(player.workers() * player.numTilesOwned()) / 200;
}
Game Settings¶
numSpawnPhaseTurns(): number {
return this._gameConfig.gameType === GameType.Singleplayer ? 70 : 150;
}
Difficulty Modifiers¶
difficultyModifier(difficulty: Difficulty): number {
switch (difficulty) {
case Difficulty.Easy:
return 1;
case Difficulty.Medium:
return 3;
case Difficulty.Hard:
return 9;
case Difficulty.Impossible:
return 18;
}
}
Example: Team Mode Config¶
import { Config, Player, TerraNullius, PlayerType } from '@lands.io/mod-sdk';
export class TeamConfig implements Partial<Config> {
// Boost attack power for humans
attackAmount(attacker: Player, defender: Player | TerraNullius): number {
if (attacker.type() === PlayerType.Bot) {
return attacker.troops() / 20;
}
return attacker.troops() / 5;
}
// Longer games with higher win threshold
percentageTilesOwnedToWin(): number {
return 90;
}
// More bots for larger battles
numBots(): number {
return 8;
}
}
How It Works¶
- Your Config class implements only the methods you want to override
- The engine creates a proxy that falls back to
DefaultConfigfor unimplemented methods - All game mechanics use your overrides seamlessly