mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 06:30:42 +00:00
Generated
+2470
-2324
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@
|
||||
593,
|
||||
473
|
||||
],
|
||||
"name": "Venezuala 🇻🇪",
|
||||
"name": "Venezuela 🇻🇪",
|
||||
"strength": 1
|
||||
},
|
||||
{
|
||||
|
||||
+21
-5
@@ -1,8 +1,8 @@
|
||||
import {Config} from "../core/configuration/Config"
|
||||
import {EventBus, GameEvent} from "../core/EventBus"
|
||||
import {AllianceRequest, AllPlayers, Cell, Player, PlayerID, PlayerType} from "../core/game/Game"
|
||||
import {ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema} from "../core/Schemas"
|
||||
import {LocalServer} from "./LocalServer"
|
||||
import { Config } from "../core/configuration/Config"
|
||||
import { EventBus, GameEvent } from "../core/EventBus"
|
||||
import { AllianceRequest, AllPlayers, Cell, Player, PlayerID, PlayerType } from "../core/game/Game"
|
||||
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema } from "../core/Schemas"
|
||||
import { LocalServer } from "./LocalServer"
|
||||
|
||||
|
||||
export class SendAllianceRequestIntentEvent implements GameEvent {
|
||||
@@ -76,6 +76,12 @@ export class SendNukeIntentEvent implements GameEvent {
|
||||
) { }
|
||||
}
|
||||
|
||||
export class SendSetTargetTroopRatioEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly ratio: number,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class Transport {
|
||||
|
||||
private socket: WebSocket
|
||||
@@ -108,6 +114,7 @@ export class Transport {
|
||||
this.eventBus.on(SendEmojiIntentEvent, (e) => this.onSendEmojiIntent(e))
|
||||
this.eventBus.on(SendDonateIntentEvent, (e) => this.onSendDonateIntent(e))
|
||||
this.eventBus.on(SendNukeIntentEvent, (e) => this.onSendNukeIntent(e))
|
||||
this.eventBus.on(SendSetTargetTroopRatioEvent, (e) => this.onSendSetTargetTroopRatioEvent(e))
|
||||
}
|
||||
|
||||
connect(onconnect: () => void, onmessage: (message: ServerMessage) => void) {
|
||||
@@ -298,6 +305,15 @@ export class Transport {
|
||||
})
|
||||
}
|
||||
|
||||
private onSendSetTargetTroopRatioEvent(event: SendSetTargetTroopRatioEvent) {
|
||||
this.sendIntent({
|
||||
type: "troop_ratio",
|
||||
clientID: this.clientID,
|
||||
player: this.playerID,
|
||||
ratio: event.ratio,
|
||||
})
|
||||
}
|
||||
|
||||
private sendIntent(intent: Intent) {
|
||||
if (this.isLocal || this.socket.readyState === WebSocket.OPEN) {
|
||||
const msg = ClientIntentMessageSchema.parse({
|
||||
|
||||
@@ -3,9 +3,10 @@ import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { Layer } from './Layer';
|
||||
import { Game } from '../../../core/game/Game';
|
||||
import { ClientID } from '../../../core/Schemas';
|
||||
import { renderTroops } from '../Utils';
|
||||
import { renderNumber, renderTroops } from '../Utils';
|
||||
import { EventBus } from '../../../core/EventBus';
|
||||
import { UIState } from '../UIState';
|
||||
import { SendSetTargetTroopRatioEvent } from '../../Transport';
|
||||
|
||||
@customElement('control-panel')
|
||||
export class ControlPanel extends LitElement implements Layer {
|
||||
@@ -17,18 +18,37 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
@state()
|
||||
private attackRatio: number = .2;
|
||||
|
||||
@state()
|
||||
private targetTroopRatio = 1;
|
||||
|
||||
@state()
|
||||
private _population: number;
|
||||
|
||||
@state()
|
||||
private _maxPopulation: number;
|
||||
|
||||
@state()
|
||||
private popRate: number;
|
||||
|
||||
@state()
|
||||
private _troops: number;
|
||||
|
||||
@state()
|
||||
private _maxTroops: number;
|
||||
|
||||
@state()
|
||||
private troopRate: number;
|
||||
private _workers: number;
|
||||
|
||||
@state()
|
||||
private _isVisible = false;
|
||||
|
||||
@state()
|
||||
private _manpower: number = 0;
|
||||
|
||||
@state()
|
||||
private _gold: number
|
||||
|
||||
|
||||
@state()
|
||||
private _goldPerSecond: number
|
||||
|
||||
init(game: Game) {
|
||||
this.game = game;
|
||||
this.attackRatio = .20
|
||||
@@ -43,10 +63,16 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
|
||||
const player = this.game.playerByClientID(this.clientID)
|
||||
if (player == null) {
|
||||
this._isVisible = false
|
||||
return
|
||||
}
|
||||
this._population = player.population()
|
||||
this._maxPopulation = this.game.config().maxPopulation(player)
|
||||
this._gold = player.gold()
|
||||
this._troops = player.troops()
|
||||
this._maxTroops = this.game.config().maxTroops(player)
|
||||
this._workers = player.workers()
|
||||
this.popRate = this.game.config().populationIncreaseRate(player) * 10
|
||||
this._goldPerSecond = this.game.config().goldAdditionRate(player) * 10
|
||||
}
|
||||
|
||||
onAttackRatioChange(newRatio: number) {
|
||||
@@ -67,7 +93,19 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
targetTroops(): number {
|
||||
return this._maxTroops * this.attackRatio
|
||||
return this._manpower * this.targetTroopRatio
|
||||
}
|
||||
|
||||
onTroopChange(newRatio: number) {
|
||||
this.eventBus.emit(new SendSetTargetTroopRatioEvent(newRatio))
|
||||
}
|
||||
|
||||
delta(): number {
|
||||
const d = this._population - this.targetTroops()
|
||||
// if (Math.abs(d) < this._manpower / 200) {
|
||||
// return 0
|
||||
// }
|
||||
return d
|
||||
}
|
||||
|
||||
|
||||
@@ -129,11 +167,31 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
<div class="control-panel ${this._isVisible ? '' : 'hidden'}">
|
||||
<div class="control-panel-info">
|
||||
<div class="info-row">
|
||||
<span class="info-label">Troops:</span>
|
||||
<span>${renderTroops(this._troops)} / ${renderTroops(this._maxTroops)}</span>
|
||||
<span class="info-label">Population:</span>
|
||||
<span>${renderTroops(this._population)} / ${renderTroops(this._maxPopulation)}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Population/s:</span>
|
||||
<span>${renderTroops(this.popRate)}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Gold:</span>
|
||||
<span>${renderNumber(this._gold)}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Gold/s:</span>
|
||||
<span>${renderNumber(this._goldPerSecond)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slider-container">
|
||||
<label for="numTroops">Troops: ${renderTroops(this._troops)} | Workers: ${renderTroops(this._workers)}</label>
|
||||
<input type="range" id="numTroops" min="1" max="10" .value=${this.targetTroopRatio * 10}
|
||||
@input=${(e: Event) => {
|
||||
this.targetTroopRatio = parseInt((e.target as HTMLInputElement).value) / 10;
|
||||
this.onTroopChange(this.targetTroopRatio);
|
||||
}}>
|
||||
</div>
|
||||
<div class="slider-container">
|
||||
<label for="numTroops">Attack Ratio: ${this.attackRatio * 100}%</label>
|
||||
<input type="range" id="numTroops" min="1" max="10" value=${this.attackRatio * 10}
|
||||
@input=${(e: Event) => {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {AllPlayers, Cell, Game, Player, PlayerType} from "../../../core/game/Game"
|
||||
import {PseudoRandom} from "../../../core/PseudoRandom"
|
||||
import {calculateBoundingBox} from "../../../core/Util"
|
||||
import {Theme} from "../../../core/configuration/Config"
|
||||
import {Layer} from "./Layer"
|
||||
import {placeName} from "../NameBoxCalculator"
|
||||
import {TransformHandler} from "../TransformHandler"
|
||||
import {renderTroops} from "../Utils"
|
||||
import { AllPlayers, Cell, Game, Player, PlayerType } from "../../../core/game/Game"
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom"
|
||||
import { calculateBoundingBox } from "../../../core/Util"
|
||||
import { Theme } from "../../../core/configuration/Config"
|
||||
import { Layer } from "./Layer"
|
||||
import { placeName } from "../NameBoxCalculator"
|
||||
import { TransformHandler } from "../TransformHandler"
|
||||
import { renderTroops } from "../Utils"
|
||||
import traitorIcon from '../../../../resources/images/TraitorIcon.png';
|
||||
import allianceIcon from '../../../../resources/images/AllianceIcon.png';
|
||||
import crownIcon from '../../../../resources/images/CrownIcon.png';
|
||||
import targetIcon from '../../../../resources/images/TargetIcon.png';
|
||||
import {ClientID} from "../../../core/Schemas"
|
||||
import { ClientID } from "../../../core/Schemas"
|
||||
|
||||
|
||||
class RenderInfo {
|
||||
@@ -19,7 +19,7 @@ class RenderInfo {
|
||||
public player: Player,
|
||||
public lastRenderCalc: number,
|
||||
public lastBoundingCalculated: number,
|
||||
public boundingBox: {min: Cell, max: Cell},
|
||||
public boundingBox: { min: Cell, max: Cell },
|
||||
public location: Cell,
|
||||
public fontSize: number
|
||||
) { }
|
||||
|
||||
+15
-1
@@ -14,6 +14,7 @@ export type Intent = SpawnIntent
|
||||
| EmojiIntent
|
||||
| DonateIntent
|
||||
| NukeIntent
|
||||
| TargetTroopRatioIntent
|
||||
|
||||
export type AttackIntent = z.infer<typeof AttackIntentSchema>
|
||||
export type SpawnIntent = z.infer<typeof SpawnIntentSchema>
|
||||
@@ -25,6 +26,7 @@ export type TargetPlayerIntent = z.infer<typeof TargetPlayerIntentSchema>
|
||||
export type EmojiIntent = z.infer<typeof EmojiIntentSchema>
|
||||
export type DonateIntent = z.infer<typeof DonateIntentSchema>
|
||||
export type NukeIntent = z.infer<typeof NukeIntentSchema>
|
||||
export type TargetTroopRatioIntent = z.infer<typeof TargetTroopRatioSchema>
|
||||
|
||||
export type Turn = z.infer<typeof TurnSchema>
|
||||
export type GameConfig = z.infer<typeof GameConfigSchema>
|
||||
@@ -65,7 +67,7 @@ const EmojiSchema = z.string().refine(
|
||||
);
|
||||
// Zod schemas
|
||||
const BaseIntentSchema = z.object({
|
||||
type: z.enum(['attack', 'spawn', 'boat', 'name', 'targetPlayer', 'emoji']),
|
||||
type: z.enum(['attack', 'spawn', 'boat', 'name', 'targetPlayer', 'emoji', 'nuke', 'troop_ratio']),
|
||||
clientID: z.string(),
|
||||
});
|
||||
|
||||
@@ -99,6 +101,11 @@ export const BoatAttackIntentSchema = BaseIntentSchema.extend({
|
||||
y: z.number(),
|
||||
})
|
||||
|
||||
export const UpdateNameIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('updateName'),
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export const AllianceRequestIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('allianceRequest'),
|
||||
requestor: z.string(),
|
||||
@@ -146,6 +153,12 @@ export const NukeIntentSchema = BaseIntentSchema.extend({
|
||||
magnitude: z.number().nullable(),
|
||||
})
|
||||
|
||||
export const TargetTroopRatioSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('troop_ratio'),
|
||||
player: z.string(),
|
||||
ratio: z.number().min(0).max(1),
|
||||
})
|
||||
|
||||
const IntentSchema = z.union([
|
||||
AttackIntentSchema,
|
||||
SpawnIntentSchema,
|
||||
@@ -157,6 +170,7 @@ const IntentSchema = z.union([
|
||||
EmojiIntentSchema,
|
||||
DonateIntentSchema,
|
||||
NukeIntentSchema,
|
||||
TargetTroopRatioSchema,
|
||||
]);
|
||||
|
||||
const TurnSchema = z.object({
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {Player, PlayerID, PlayerInfo, TerraNullius, Tick, Tile} from "../game/Game";
|
||||
import {Colord, colord} from "colord";
|
||||
import {devConfig} from "./DevConfig";
|
||||
import {defaultConfig} from "./DefaultConfig";
|
||||
import {GameID} from "../Schemas";
|
||||
import { Player, PlayerID, PlayerInfo, TerraNullius, Tick, Tile } from "../game/Game";
|
||||
import { Colord, colord } from "colord";
|
||||
import { devConfig } from "./DevConfig";
|
||||
import { defaultConfig } from "./DefaultConfig";
|
||||
import { GameID } from "../Schemas";
|
||||
|
||||
export enum GameEnv {
|
||||
Dev,
|
||||
@@ -33,8 +33,10 @@ export interface Config {
|
||||
numBots(): number
|
||||
numSpawnPhaseTurns(): number
|
||||
|
||||
startTroops(playerInfo: PlayerInfo): number
|
||||
troopAdditionRate(player: Player): number
|
||||
startManpower(playerInfo: PlayerInfo): number
|
||||
populationIncreaseRate(player: Player): number
|
||||
goldAdditionRate(player: Player): number
|
||||
troopAdjustmentRate(player: Player): number
|
||||
attackTilesPerTick(attacker: Player, defender: Player | TerraNullius, numAdjacentTilesWithEnemy: number): number
|
||||
attackLogic(attackTroops: number, attacker: Player, defender: Player | TerraNullius, tileToConquer: Tile): {
|
||||
attackerTroopLoss: number,
|
||||
@@ -42,7 +44,7 @@ export interface Config {
|
||||
tilesPerTickUsed: number
|
||||
}
|
||||
attackAmount(attacker: Player, defender: Player | TerraNullius): number
|
||||
maxTroops(player: Player): number
|
||||
maxPopulation(player: Player): number
|
||||
boatAttackAmount(attacker: Player, defender: Player | TerraNullius): number
|
||||
boatMaxDistance(): number
|
||||
boatMaxNumber(): number
|
||||
|
||||
@@ -95,7 +95,7 @@ export class DefaultConfig implements Config {
|
||||
return {
|
||||
attackerTroopLoss: attacker.type() == PlayerType.Bot ? mag / 10 : mag / 5,
|
||||
defenderTroopLoss: 0,
|
||||
tilesPerTickUsed: within(this.startTroops(attacker.info()) / (attackTroops * 5), .2, 3) * Math.max(10, speed / 1.5)
|
||||
tilesPerTickUsed: within(this.startManpower(attacker.info()) / (attackTroops * 5), .2, 3) * Math.max(10, speed / 1.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
}
|
||||
|
||||
startTroops(playerInfo: PlayerInfo): number {
|
||||
startManpower(playerInfo: PlayerInfo): number {
|
||||
if (playerInfo.playerType == PlayerType.Bot) {
|
||||
return 10000
|
||||
}
|
||||
@@ -130,20 +130,20 @@ export class DefaultConfig implements Config {
|
||||
return 25000
|
||||
}
|
||||
|
||||
maxTroops(player: Player): number {
|
||||
const troops = Math.sqrt(player.numTilesOwned()) * 3000 + 50000
|
||||
maxPopulation(player: Player): number {
|
||||
let maxPop = Math.sqrt(player.numTilesOwned()) * 3000 + 50000
|
||||
if (player.type() == PlayerType.Bot) {
|
||||
return troops
|
||||
return maxPop
|
||||
}
|
||||
return troops * 2
|
||||
return maxPop * 2
|
||||
}
|
||||
|
||||
troopAdditionRate(player: Player): number {
|
||||
let max = this.maxTroops(player)
|
||||
populationIncreaseRate(player: Player): number {
|
||||
let max = this.maxPopulation(player)
|
||||
|
||||
let toAdd = 10 + (player.troops() + Math.sqrt(player.troops() * player.numTilesOwned())) / 100
|
||||
let toAdd = 10 + (player.population() + Math.sqrt(player.population() * player.numTilesOwned())) / 100
|
||||
|
||||
const ratio = 1 - (player.troops() / max)
|
||||
const ratio = 1 - (player.population() / max)
|
||||
toAdd *= ratio
|
||||
toAdd *= .5
|
||||
// console.log(`to add ${toAdd}`)
|
||||
@@ -155,9 +155,29 @@ export class DefaultConfig implements Config {
|
||||
toAdd *= .7
|
||||
}
|
||||
|
||||
return Math.min(player.troops() + toAdd, max)
|
||||
return Math.min(player.troops() + toAdd, max) - player.troops()
|
||||
}
|
||||
|
||||
goldAdditionRate(player: Player): number {
|
||||
return Math.sqrt(player.workers() * player.numTilesOwned()) / 1000 + player.gold() * .001
|
||||
}
|
||||
|
||||
troopAdjustmentRate(player: Player): number {
|
||||
const maxDiff = this.maxPopulation(player) / 300
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const defaultConfig = new DefaultConfig()
|
||||
@@ -1,8 +1,8 @@
|
||||
import {PriorityQueue} from "@datastructures-js/priority-queue";
|
||||
import {Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, TileEvent} from "../game/Game";
|
||||
import {and, bfs, manhattanDistWrapped, sourceDstOceanShore} from "../Util";
|
||||
import {AttackExecution} from "./AttackExecution";
|
||||
import {DisplayMessageEvent, MessageType} from "../../client/graphics/layers/EventsDisplay";
|
||||
import { PriorityQueue } from "@datastructures-js/priority-queue";
|
||||
import { Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, TileEvent } from "../game/Game";
|
||||
import { and, bfs, manhattanDistWrapped, sourceDstOceanShore } from "../Util";
|
||||
import { AttackExecution } from "./AttackExecution";
|
||||
import { DisplayMessageEvent, MessageType } from "../../client/graphics/layers/EventsDisplay";
|
||||
|
||||
export class BoatAttackExecution implements Execution {
|
||||
|
||||
@@ -145,14 +145,14 @@ export class BoatAttackExecution implements Execution {
|
||||
}
|
||||
|
||||
export class AStar {
|
||||
private openSet: PriorityQueue<{tile: Tile, fScore: number}>;
|
||||
private openSet: PriorityQueue<{ tile: Tile, fScore: number }>;
|
||||
private cameFrom: Map<Tile, Tile>;
|
||||
private gScore: Map<Tile, number>;
|
||||
private current: Tile | null;
|
||||
public completed: boolean;
|
||||
|
||||
constructor(private src: Tile, private dst: Tile) {
|
||||
this.openSet = new PriorityQueue<{tile: Tile, fScore: number}>(
|
||||
this.openSet = new PriorityQueue<{ tile: Tile, fScore: number }>(
|
||||
(a, b) => a.fScore - b.fScore
|
||||
);
|
||||
this.cameFrom = new Map<Tile, Tile>();
|
||||
@@ -161,7 +161,7 @@ export class AStar {
|
||||
this.completed = false;
|
||||
|
||||
this.gScore.set(src, 0);
|
||||
this.openSet.enqueue({tile: src, fScore: this.heuristic(src, dst)});
|
||||
this.openSet.enqueue({ tile: src, fScore: this.heuristic(src, dst) });
|
||||
}
|
||||
|
||||
compute(iterations: number): boolean {
|
||||
@@ -189,7 +189,7 @@ export class AStar {
|
||||
this.gScore.set(neighbor, tentativeGScore);
|
||||
const fScore = tentativeGScore + this.heuristic(neighbor, this.dst);
|
||||
|
||||
this.openSet.enqueue({tile: neighbor, fScore: fScore});
|
||||
this.openSet.enqueue({ tile: neighbor, fScore: fScore });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { TargetPlayerExecution } from "./TargetPlayerExecution";
|
||||
import { EmojiExecution } from "./EmojiExecution";
|
||||
import { DonateExecution } from "./DonateExecution";
|
||||
import { NukeExecution } from "./NukeExecution";
|
||||
import { SetTargetTroopRatioExecution } from "./SetTargetTroopRatioExecution";
|
||||
|
||||
|
||||
|
||||
@@ -71,6 +72,8 @@ export class Executor {
|
||||
return new DonateExecution(intent.sender, intent.recipient, intent.troops)
|
||||
} else if (intent.type == "nuke") {
|
||||
return new NukeExecution(intent.sender, new Cell(intent.x, intent.y), intent.magnitude)
|
||||
} else if (intent.type == "troop_ratio") {
|
||||
return new SetTargetTroopRatioExecution(intent.player, intent.ratio)
|
||||
} else {
|
||||
throw new Error(`intent type ${intent} not found`)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class FakeHumanExecution implements Execution {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.player.troops() < this.mg.config().maxTroops(this.player) / 4) {
|
||||
if (this.player.troops() < this.mg.config().maxPopulation(this.player) / 4) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Cell, Execution, MutableGame, MutablePlayer, PlayerID, Tile} from "../game/Game";
|
||||
import {PseudoRandom} from "../PseudoRandom";
|
||||
import {bfs, dist, euclideanDist, manhattanDist} from "../Util";
|
||||
import { Cell, Execution, MutableGame, MutablePlayer, PlayerID, Tile } from "../game/Game";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { bfs, dist, euclideanDist, manhattanDist } from "../Util";
|
||||
|
||||
export class NukeExecution implements Execution {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Config} from "../configuration/Config"
|
||||
import {Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile} from "../game/Game"
|
||||
import {bfs, calculateBoundingBox, getMode, inscribed, simpleHash} from "../Util"
|
||||
import {GameImpl} from "../game/GameImpl"
|
||||
import { Config } from "../configuration/Config"
|
||||
import { Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile } from "../game/Game"
|
||||
import { bfs, calculateBoundingBox, getMode, inscribed, simpleHash } from "../Util"
|
||||
import { GameImpl } from "../game/GameImpl"
|
||||
|
||||
export class PlayerExecution implements Execution {
|
||||
|
||||
@@ -30,7 +30,14 @@ export class PlayerExecution implements Execution {
|
||||
if (ticks < this.config.numSpawnPhaseTurns()) {
|
||||
return
|
||||
}
|
||||
this.player.setTroops(this.config.troopAdditionRate(this.player))
|
||||
const popInc = this.config.populationIncreaseRate(this.player)
|
||||
|
||||
this.player.addWorkers(popInc * (1 - this.player.targetTroopRatio()))// (1 - this.player.targetTroopRatio()))
|
||||
this.player.addTroops(popInc * this.player.targetTroopRatio())
|
||||
this.player.addGold(this.config.goldAdditionRate(this.player))
|
||||
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) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Execution, MutableGame, MutablePlayer, PlayerID } from "../game/Game";
|
||||
|
||||
export class SetTargetTroopRatioExecution implements Execution {
|
||||
|
||||
private player: MutablePlayer
|
||||
|
||||
private active = true
|
||||
|
||||
constructor(private playerID: PlayerID, private targetTroopsRatio: number) { }
|
||||
|
||||
|
||||
init(mg: MutableGame, ticks: number): void {
|
||||
this.player = mg.player(this.playerID)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
owner(): MutablePlayer {
|
||||
return null
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active
|
||||
}
|
||||
|
||||
activeDuringSpawnPhase(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Cell, Execution, MutableGame, MutablePlayer, PlayerInfo, PlayerType} from "../game/Game"
|
||||
import {BotExecution} from "./BotExecution"
|
||||
import {PlayerExecution} from "./PlayerExecution"
|
||||
import {getSpawnCells} from "./Util"
|
||||
import { Cell, Execution, MutableGame, MutablePlayer, PlayerInfo, PlayerType } from "../game/Game"
|
||||
import { BotExecution } from "./BotExecution"
|
||||
import { PlayerExecution } from "./PlayerExecution"
|
||||
import { getSpawnCells } from "./Util"
|
||||
|
||||
export class SpawnExecution implements Execution {
|
||||
|
||||
@@ -33,7 +33,7 @@ export class SpawnExecution implements Execution {
|
||||
return
|
||||
}
|
||||
|
||||
const player = this.mg.addPlayer(this.playerInfo, this.mg.config().startTroops(this.playerInfo))
|
||||
const player = this.mg.addPlayer(this.playerInfo, this.mg.config().startManpower(this.playerInfo))
|
||||
getSpawnCells(this.mg, this.cell).forEach(c => {
|
||||
player.conquer(this.mg.tile(c))
|
||||
})
|
||||
|
||||
+19
-10
@@ -8,7 +8,7 @@ import { DonateExecution } from "../execution/DonateExecution"
|
||||
|
||||
export type PlayerID = string
|
||||
export type Tick = number
|
||||
export type Currency = number
|
||||
export type Gold = number
|
||||
|
||||
export const AllPlayers = "AllPlayers" as const;
|
||||
|
||||
@@ -167,7 +167,6 @@ export interface Player {
|
||||
clientID(): ClientID
|
||||
id(): PlayerID
|
||||
type(): PlayerType
|
||||
troops(): number
|
||||
boats(): Boat[]
|
||||
ownsTile(cell: Cell): boolean
|
||||
isAlive(): boolean
|
||||
@@ -195,14 +194,16 @@ export interface Player {
|
||||
canSendEmoji(recipient: Player | typeof AllPlayers): boolean
|
||||
outgoingEmojis(): EmojiMessage[]
|
||||
canDonate(recipient: Player): boolean
|
||||
currency(): Currency
|
||||
gold(): Gold
|
||||
// Population = troops + workers
|
||||
population(): number
|
||||
workers(): number
|
||||
// Number between 0, 1
|
||||
targetTroopRatio(): number
|
||||
troops(): number
|
||||
}
|
||||
|
||||
export interface MutablePlayer extends Player {
|
||||
setName(name: string): void
|
||||
setTroops(troops: number): void
|
||||
addTroops(troops: number): void
|
||||
removeTroops(troops: number): number
|
||||
conquer(tile: Tile): void
|
||||
relinquish(tile: Tile): void
|
||||
executions(): Execution[]
|
||||
@@ -220,8 +221,16 @@ export interface MutablePlayer extends Player {
|
||||
transitiveTargets(): MutablePlayer[]
|
||||
sendEmoji(recipient: Player | typeof AllPlayers, emoji: string): void
|
||||
donate(recipient: MutablePlayer, troops: number): void
|
||||
addCurrency(toAdd: Currency): void
|
||||
removeCurrency(toRemove: Currency): void
|
||||
|
||||
addGold(toAdd: Gold): void
|
||||
removeGold(toRemove: Gold): void
|
||||
|
||||
addWorkers(toAdd: number): void
|
||||
removeWorkers(toRemove: number): void
|
||||
setTargetTroopRatio(target: number): void
|
||||
setTroops(troops: number): void
|
||||
addTroops(troops: number): void
|
||||
removeTroops(troops: number): number
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
@@ -253,7 +262,7 @@ export interface MutableGame extends Game {
|
||||
player(id: PlayerID): MutablePlayer
|
||||
playerByClientID(id: ClientID): MutablePlayer | null
|
||||
players(): MutablePlayer[]
|
||||
addPlayer(playerInfo: PlayerInfo, troops: number): MutablePlayer
|
||||
addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer
|
||||
executions(): Execution[]
|
||||
boats(): MutableBoat[]
|
||||
}
|
||||
|
||||
@@ -210,8 +210,8 @@ export class GameImpl implements MutableGame {
|
||||
return this.player(id)
|
||||
}
|
||||
|
||||
addPlayer(playerInfo: PlayerInfo, troops: number): MutablePlayer {
|
||||
let player = new PlayerImpl(this, playerInfo, troops)
|
||||
addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer {
|
||||
let player = new PlayerImpl(this, playerInfo, manpower)
|
||||
this._players.set(playerInfo.id, player)
|
||||
this.eventBus.emit(new PlayerEvent(player))
|
||||
return player
|
||||
|
||||
+64
-27
@@ -1,4 +1,4 @@
|
||||
import { MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, EmojiMessage, EmojiMessageEvent, AllPlayers, Currency } from "./Game";
|
||||
import { MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, EmojiMessage, EmojiMessageEvent, AllPlayers, Gold } from "./Game";
|
||||
import { ClientID } from "../Schemas";
|
||||
import { processName, simpleHash } from "../Util";
|
||||
import { CellString, GameImpl } from "./GameImpl";
|
||||
@@ -19,7 +19,11 @@ class Donation {
|
||||
|
||||
export class PlayerImpl implements MutablePlayer {
|
||||
|
||||
private _currency: number
|
||||
|
||||
private _gold: Gold
|
||||
private _troops: number
|
||||
private _workers: number
|
||||
private _targetTroopRatio: number = 1
|
||||
|
||||
isTraitor_ = false
|
||||
|
||||
@@ -29,7 +33,7 @@ export class PlayerImpl implements MutablePlayer {
|
||||
public _tiles: Map<CellString, Tile> = new Map<CellString, Tile>();
|
||||
|
||||
private _name: string;
|
||||
private _displayerName: string;
|
||||
private _displayName: string;
|
||||
|
||||
public pastOutgoingAllianceRequests: AllianceRequest[] = []
|
||||
|
||||
@@ -39,16 +43,21 @@ export class PlayerImpl implements MutablePlayer {
|
||||
|
||||
private sentDonations: Donation[] = []
|
||||
|
||||
constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) {
|
||||
constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, startPopulation: number) {
|
||||
this._name = playerInfo.name;
|
||||
this._displayerName = processName(this._name)
|
||||
this._targetTroopRatio = 1
|
||||
this._troops = startPopulation * this._targetTroopRatio;
|
||||
this._workers = startPopulation * (1 - this._targetTroopRatio)
|
||||
this._gold = 0
|
||||
this._displayName = processName(this._name)
|
||||
|
||||
}
|
||||
|
||||
name(): string {
|
||||
return this._name;
|
||||
}
|
||||
displayName(): string {
|
||||
return this._displayerName
|
||||
return this._displayName
|
||||
}
|
||||
|
||||
clientID(): ClientID {
|
||||
@@ -63,8 +72,6 @@ export class PlayerImpl implements MutablePlayer {
|
||||
return this.playerInfo.playerType;
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
}
|
||||
|
||||
addBoat(troops: number, tile: Tile, target: Player | TerraNullius): BoatImpl {
|
||||
const b = new BoatImpl(this.gs, tile, troops, this, target as PlayerImpl | TerraNulliusImpl);
|
||||
@@ -111,15 +118,6 @@ export class PlayerImpl implements MutablePlayer {
|
||||
return Array.from(ns);
|
||||
}
|
||||
|
||||
addTroops(troops: number): void {
|
||||
this._troops += Math.floor(troops);
|
||||
}
|
||||
removeTroops(troops: number): number {
|
||||
const toRemove = Math.floor(Math.min(this._troops, troops))
|
||||
this._troops -= toRemove;
|
||||
return toRemove
|
||||
}
|
||||
|
||||
isPlayer(): this is MutablePlayer { return true as const; }
|
||||
ownsTile(cell: Cell): boolean { return this._tiles.has(cell.toString()); }
|
||||
setTroops(troops: number) { this._troops = Math.floor(troops); }
|
||||
@@ -131,7 +129,6 @@ export class PlayerImpl implements MutablePlayer {
|
||||
this.gs.relinquish(tile);
|
||||
}
|
||||
info(): PlayerInfo { return this.playerInfo; }
|
||||
troops(): number { return this._troops; }
|
||||
isAlive(): boolean { return this._tiles.size > 0; }
|
||||
executions(): Execution[] {
|
||||
return this.gs.executions().filter(exec => exec.owner().id() == this.id());
|
||||
@@ -271,23 +268,63 @@ export class PlayerImpl implements MutablePlayer {
|
||||
this.gs.displayMessage(`Recieved ${renderTroops(troops)} troops from ${this.name()}`, MessageType.SUCCESS, recipient.id())
|
||||
}
|
||||
|
||||
currency(): Currency {
|
||||
return this._currency
|
||||
gold(): Gold {
|
||||
return this._gold
|
||||
}
|
||||
|
||||
addCurrency(toAdd: Currency): void {
|
||||
this._currency += toAdd
|
||||
addGold(toAdd: Gold): void {
|
||||
this._gold += toAdd
|
||||
}
|
||||
|
||||
removeCurrency(toRemove: Currency): void {
|
||||
if (toRemove > this._currency) {
|
||||
throw Error(`cannot remove ${toRemove} from ${this} because only has ${this._currency}`)
|
||||
removeGold(toRemove: Gold): void {
|
||||
this._gold -= toRemove
|
||||
}
|
||||
|
||||
population(): number {
|
||||
return this._troops + this._workers
|
||||
}
|
||||
workers(): number {
|
||||
return Math.max(1, this._workers)
|
||||
}
|
||||
addWorkers(toAdd: number): void {
|
||||
this._workers += toAdd
|
||||
}
|
||||
removeWorkers(toRemove: number): void {
|
||||
this._workers = Math.max(1, this._workers - toRemove)
|
||||
}
|
||||
|
||||
targetTroopRatio(): number {
|
||||
return this._targetTroopRatio
|
||||
}
|
||||
|
||||
setTargetTroopRatio(target: number): void {
|
||||
if (target < 0 || target > 1) {
|
||||
throw new Error(`invalid targetTroopRatio ${target} set on player ${PlayerImpl}`)
|
||||
}
|
||||
this._currency -= toRemove
|
||||
this._targetTroopRatio = target
|
||||
}
|
||||
|
||||
troops(): number { return this._troops; }
|
||||
|
||||
addTroops(troops: number): void {
|
||||
if (troops < 0) {
|
||||
this.removeTroops(-1 * troops)
|
||||
return
|
||||
}
|
||||
this._troops += Math.floor(troops);
|
||||
}
|
||||
removeTroops(troops: number): number {
|
||||
if (troops <= 1) {
|
||||
return 0
|
||||
}
|
||||
const toRemove = Math.floor(Math.min(this._troops - 1, troops))
|
||||
this._troops -= toRemove;
|
||||
return toRemove
|
||||
}
|
||||
|
||||
|
||||
hash(): number {
|
||||
return simpleHash(this.id()) * (this.troops() + this.numTilesOwned());
|
||||
return simpleHash(this.id()) * (this.population() + this.numTilesOwned());
|
||||
}
|
||||
toString(): string {
|
||||
return `Player:{name:${this.info().name},clientID:${this.info().clientID},isAlive:${this.isAlive()},troops:${this._troops},numTileOwned:${this.numTilesOwned()}}]`;
|
||||
|
||||
Reference in New Issue
Block a user