mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:30:45 +00:00
Private lobby toggle donation (#1752)
Resolve #1652 1. Add the ability to toggle **gold donations** and **troop donations** for private lobbies ~2. Add relevant translations.~ 3. Refactor `canDonate` to be specific to gold and troop donations 4. Add placeholders for singleplayer mode if this is to be extended to support that too. 5. Add Tests for Donate logic <img width="1643" height="1788" alt="image" src="https://github.com/user-attachments/assets/82b93400-a1f0-45f0-8b2b-a7f78dc0c3e9" /> _Private Lobby_  _Testing Troop Send In Private Lobby_  _Troop Send Complete In Private Lobby_  Confirming that public teams still works - [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 have read and accepted the CLA agreement (only required once). regression is found: DISCORD_USERNAME: cool_clarky --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com>
This commit is contained in:
@@ -130,7 +130,9 @@
|
||||
"disable_nations": "Disable Nations",
|
||||
"instant_build": "Instant build",
|
||||
"infinite_gold": "Infinite gold",
|
||||
"donate_gold": "Donate gold",
|
||||
"infinite_troops": "Infinite troops",
|
||||
"donate_troops": "Donate troops",
|
||||
"disable_nukes": "Disable Nukes",
|
||||
"enables_title": "Enable Settings",
|
||||
"start": "Start Game"
|
||||
@@ -222,7 +224,9 @@
|
||||
"disable_nations": "Disable Nations",
|
||||
"instant_build": "Instant build",
|
||||
"infinite_gold": "Infinite gold",
|
||||
"donate_gold": "Donate gold",
|
||||
"infinite_troops": "Infinite troops",
|
||||
"donate_troops": "Donate troops",
|
||||
"enables_title": "Enable Settings",
|
||||
"player": "Player",
|
||||
"players": "Players",
|
||||
|
||||
@@ -41,7 +41,9 @@ export class HostLobbyModal extends LitElement {
|
||||
@state() private teamCount: TeamCountConfig = 2;
|
||||
@state() private bots: number = 400;
|
||||
@state() private infiniteGold: boolean = false;
|
||||
@state() private donateGold: boolean = false;
|
||||
@state() private infiniteTroops: boolean = false;
|
||||
@state() private donateTroops: boolean = false;
|
||||
@state() private instantBuild: boolean = false;
|
||||
@state() private lobbyId = "";
|
||||
@state() private copySuccess = false;
|
||||
@@ -362,6 +364,38 @@ export class HostLobbyModal extends LitElement {
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="donate-gold"
|
||||
class="option-card ${this.donateGold ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="donate-gold"
|
||||
@change=${this.handleDonateGoldChange}
|
||||
.checked=${this.donateGold}
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
${translateText("host_modal.donate_gold")}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="donate-troops"
|
||||
class="option-card ${this.donateTroops ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="donate-troops"
|
||||
@change=${this.handleDonateTroopsChange}
|
||||
.checked=${this.donateTroops}
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
${translateText("host_modal.donate_troops")}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="infinite-gold"
|
||||
class="option-card ${this.infiniteGold ? "selected" : ""}"
|
||||
@@ -562,11 +596,21 @@ export class HostLobbyModal extends LitElement {
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private handleDonateGoldChange(e: Event) {
|
||||
this.donateGold = Boolean((e.target as HTMLInputElement).checked);
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private handleInfiniteTroopsChange(e: Event) {
|
||||
this.infiniteTroops = Boolean((e.target as HTMLInputElement).checked);
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private handleDonateTroopsChange(e: Event) {
|
||||
this.donateTroops = Boolean((e.target as HTMLInputElement).checked);
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private async handleDisableNPCsChange(e: Event) {
|
||||
this.disableNPCs = Boolean((e.target as HTMLInputElement).checked);
|
||||
console.log(`updating disable npcs to ${this.disableNPCs}`);
|
||||
@@ -598,7 +642,9 @@ export class HostLobbyModal extends LitElement {
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
donateGold: this.donateGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
donateTroops: this.donateTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
|
||||
@@ -38,7 +38,9 @@ export class SinglePlayerModal extends LitElement {
|
||||
@state() private disableNPCs: boolean = false;
|
||||
@state() private bots: number = 400;
|
||||
@state() private infiniteGold: boolean = false;
|
||||
@state() private donateGold: boolean = false;
|
||||
@state() private infiniteTroops: boolean = false;
|
||||
@state() private donateTroops: boolean = false;
|
||||
@state() private instantBuild: boolean = false;
|
||||
@state() private useRandomMap: boolean = false;
|
||||
@state() private gameMode: GameMode = GameMode.FFA;
|
||||
@@ -459,7 +461,9 @@ export class SinglePlayerModal extends LitElement {
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
donateGold: this.donateGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
donateTroops: this.donateTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
disabledUnits: this.disabledUnits
|
||||
.map((u) => Object.values(UnitType).find((ut) => ut === u))
|
||||
|
||||
@@ -232,7 +232,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
}
|
||||
other = other as PlayerView;
|
||||
|
||||
const canDonate = this.actions?.interaction?.canDonate;
|
||||
const canDonateGold = this.actions?.interaction?.canDonateGold;
|
||||
const canDonateTroops = this.actions?.interaction?.canDonateTroops;
|
||||
const canSendAllianceRequest =
|
||||
this.actions?.interaction?.canSendAllianceRequest;
|
||||
const canSendEmoji =
|
||||
@@ -421,7 +422,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
<img src=${allianceIcon} alt="Alliance" class="w-6 h-6" />
|
||||
</button>`
|
||||
: ""}
|
||||
${canDonate
|
||||
${canDonateTroops
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleDonateTroopClick(e, myPlayer, other)}
|
||||
@@ -436,7 +437,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
/>
|
||||
</button>`
|
||||
: ""}
|
||||
${canDonate
|
||||
${canDonateGold
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleDonateGoldClick(e, myPlayer, other)}
|
||||
|
||||
@@ -208,7 +208,7 @@ const allyDonateGoldElement: MenuElement = {
|
||||
id: "ally_donate_gold",
|
||||
name: "donate gold",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canDonate,
|
||||
!params.playerActions?.interaction?.canDonateGold,
|
||||
color: COLORS.ally,
|
||||
icon: donateGoldIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
@@ -221,7 +221,7 @@ const allyDonateTroopsElement: MenuElement = {
|
||||
id: "ally_donate_troops",
|
||||
name: "donate troops",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canDonate,
|
||||
!params.playerActions?.interaction?.canDonateTroops,
|
||||
color: COLORS.ally,
|
||||
icon: donateTroopIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
|
||||
@@ -194,7 +194,8 @@ export class GameRunner {
|
||||
canTarget: player.canTarget(other),
|
||||
canSendAllianceRequest: player.canSendAllianceRequest(other),
|
||||
canBreakAlliance: player.isAlliedWith(other),
|
||||
canDonate: player.canDonate(other),
|
||||
canDonateGold: player.canDonateGold(other),
|
||||
canDonateTroops: player.canDonateTroops(other),
|
||||
canEmbargo: !player.hasEmbargoAgainst(other),
|
||||
};
|
||||
const alliance = player.allianceWith(other as Player);
|
||||
|
||||
@@ -145,6 +145,8 @@ export type TeamCountConfig = z.infer<typeof TeamCountConfigSchema>;
|
||||
export const GameConfigSchema = z.object({
|
||||
gameMap: z.enum(GameMapType),
|
||||
difficulty: z.enum(Difficulty),
|
||||
donateGold: z.boolean(),
|
||||
donateTroops: z.boolean(),
|
||||
gameType: z.enum(GameType),
|
||||
gameMode: z.enum(GameMode),
|
||||
disableNPCs: z.boolean(),
|
||||
|
||||
@@ -82,7 +82,9 @@ export interface Config {
|
||||
isUnitDisabled(unitType: UnitType): boolean;
|
||||
bots(): number;
|
||||
infiniteGold(): boolean;
|
||||
donateGold(): boolean;
|
||||
infiniteTroops(): boolean;
|
||||
donateTroops(): boolean;
|
||||
instantBuild(): boolean;
|
||||
numSpawnPhaseTurns(): number;
|
||||
userSettings(): UserSettings;
|
||||
|
||||
@@ -325,9 +325,16 @@ export class DefaultConfig implements Config {
|
||||
infiniteGold(): boolean {
|
||||
return this._gameConfig.infiniteGold;
|
||||
}
|
||||
donateGold(): boolean {
|
||||
return this._gameConfig.donateGold;
|
||||
}
|
||||
infiniteTroops(): boolean {
|
||||
return this._gameConfig.infiniteTroops;
|
||||
}
|
||||
donateTroops(): boolean {
|
||||
return this._gameConfig.donateTroops;
|
||||
}
|
||||
|
||||
trainSpawnRate(numPlayerFactories: number): number {
|
||||
// hyperbolic decay, midpoint at 10 factories
|
||||
// expected number of trains = numPlayerFactories / trainSpawnRate(numPlayerFactories)
|
||||
|
||||
@@ -25,7 +25,7 @@ export class DonateGoldExecution implements Execution {
|
||||
tick(ticks: number): void {
|
||||
if (this.gold === null) throw new Error("not initialized");
|
||||
if (
|
||||
this.sender.canDonate(this.recipient) &&
|
||||
this.sender.canDonateGold(this.recipient) &&
|
||||
this.sender.donateGold(this.recipient, this.gold)
|
||||
) {
|
||||
this.recipient.updateRelation(this.sender, 50);
|
||||
|
||||
@@ -28,7 +28,7 @@ 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.canDonateTroops(this.recipient) &&
|
||||
this.sender.donateTroops(this.recipient, this.troops)
|
||||
) {
|
||||
this.recipient.updateRelation(this.sender, 50);
|
||||
|
||||
@@ -589,7 +589,8 @@ export interface Player {
|
||||
sendEmoji(recipient: Player | typeof AllPlayers, emoji: string): void;
|
||||
|
||||
// Donation
|
||||
canDonate(recipient: Player): boolean;
|
||||
canDonateGold(recipient: Player): boolean;
|
||||
canDonateTroops(recipient: Player): boolean;
|
||||
donateTroops(recipient: Player, troops: number): boolean;
|
||||
donateGold(recipient: Player, gold: Gold): boolean;
|
||||
canDeleteUnit(): boolean;
|
||||
@@ -742,7 +743,8 @@ export interface PlayerInteraction {
|
||||
canSendAllianceRequest: boolean;
|
||||
canBreakAlliance: boolean;
|
||||
canTarget: boolean;
|
||||
canDonate: boolean;
|
||||
canDonateGold: boolean;
|
||||
canDonateTroops: boolean;
|
||||
canEmbargo: boolean;
|
||||
allianceExpiresAt?: Tick;
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ export class PlayerImpl implements Player {
|
||||
return true;
|
||||
}
|
||||
|
||||
canDonate(recipient: Player): boolean {
|
||||
canDonateGold(recipient: Player): boolean {
|
||||
if (!this.isFriendly(recipient)) {
|
||||
return false;
|
||||
}
|
||||
@@ -583,6 +583,36 @@ export class PlayerImpl implements Player {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (this.mg.config().donateGold() === false) {
|
||||
return false;
|
||||
}
|
||||
for (const donation of this.sentDonations) {
|
||||
if (donation.recipient === recipient) {
|
||||
if (
|
||||
this.mg.ticks() - donation.tick <
|
||||
this.mg.config().donateCooldown()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
canDonateTroops(recipient: Player): boolean {
|
||||
if (!this.isFriendly(recipient)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
recipient.type() === PlayerType.Human &&
|
||||
this.mg.config().gameConfig().gameMode === GameMode.FFA &&
|
||||
this.mg.config().gameConfig().gameType === GameType.Public
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (this.mg.config().donateTroops() === false) {
|
||||
return false;
|
||||
}
|
||||
for (const donation of this.sentDonations) {
|
||||
if (donation.recipient === recipient) {
|
||||
if (
|
||||
|
||||
@@ -39,6 +39,8 @@ export class GameManager {
|
||||
Date.now(),
|
||||
this.config,
|
||||
{
|
||||
donateGold: false,
|
||||
donateTroops: false,
|
||||
gameMap: GameMapType.World,
|
||||
gameType: GameType.Private,
|
||||
difficulty: Difficulty.Medium,
|
||||
|
||||
@@ -93,9 +93,15 @@ export class GameServer {
|
||||
if (gameConfig.infiniteGold !== undefined) {
|
||||
this.gameConfig.infiniteGold = gameConfig.infiniteGold;
|
||||
}
|
||||
if (gameConfig.donateGold !== undefined) {
|
||||
this.gameConfig.donateGold = gameConfig.donateGold;
|
||||
}
|
||||
if (gameConfig.infiniteTroops !== undefined) {
|
||||
this.gameConfig.infiniteTroops = gameConfig.infiniteTroops;
|
||||
}
|
||||
if (gameConfig.donateTroops !== undefined) {
|
||||
this.gameConfig.donateTroops = gameConfig.donateTroops;
|
||||
}
|
||||
if (gameConfig.instantBuild !== undefined) {
|
||||
this.gameConfig.instantBuild = gameConfig.instantBuild;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,8 @@ export class MapPlaylist {
|
||||
|
||||
// Create the default public game config (from your GameManager)
|
||||
return {
|
||||
donateGold: true,
|
||||
donateTroops: true,
|
||||
gameMap: map,
|
||||
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams),
|
||||
gameType: GameType.Public,
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
import { DonateGoldExecution } from "../src/core/execution/DonateGoldExecution";
|
||||
import { DonateTroopsExecution } from "../src/core/execution/DonateTroopExecution";
|
||||
import { SpawnExecution } from "../src/core/execution/SpawnExecution";
|
||||
import { PlayerInfo, PlayerType } from "../src/core/game/Game";
|
||||
import { setup } from "./util/Setup";
|
||||
|
||||
describe("Donate troops to an ally", () => {
|
||||
it("Troops should be successfully donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteTroops: false,
|
||||
donateTroops: true,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
"donor",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"donor_id",
|
||||
);
|
||||
const recipientInfo = new PlayerInfo(
|
||||
"recipient",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"recipient_id",
|
||||
);
|
||||
|
||||
game.addPlayer(donorInfo);
|
||||
game.addPlayer(recipientInfo);
|
||||
|
||||
const donor = game.player(donorInfo.id);
|
||||
const recipient = game.player(recipientInfo.id);
|
||||
|
||||
// Spawn both players
|
||||
const spawnA = game.ref(0, 10);
|
||||
const spawnB = game.ref(0, 15);
|
||||
|
||||
game.addExecution(
|
||||
new SpawnExecution(donorInfo, spawnA),
|
||||
new SpawnExecution(recipientInfo, spawnB),
|
||||
);
|
||||
|
||||
while (game.inSpawnPhase()) {
|
||||
game.executeNextTick();
|
||||
}
|
||||
|
||||
// donor sends alliance request to recipient
|
||||
const allianceRequest = donor.createAllianceRequest(recipient);
|
||||
expect(allianceRequest).not.toBeNull();
|
||||
|
||||
// recipient accepts the alliance request
|
||||
if (allianceRequest) {
|
||||
allianceRequest.accept();
|
||||
}
|
||||
|
||||
// Ensure donor can actually donate the requested amount
|
||||
donor.addTroops(6000);
|
||||
const donorTroopsBefore = donor.troops();
|
||||
const recipientTroopsBefore = recipient.troops();
|
||||
game.addExecution(new DonateTroopsExecution(donor, recipientInfo.id, 5000));
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
game.executeNextTick();
|
||||
}
|
||||
|
||||
expect(donor.troops() < donorTroopsBefore).toBe(true);
|
||||
expect(recipient.troops() > recipientTroopsBefore).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Donate gold to an ally", () => {
|
||||
it("Gold should be successfully donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteGold: false,
|
||||
donateGold: true,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
"donor",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"donor_id",
|
||||
);
|
||||
const recipientInfo = new PlayerInfo(
|
||||
"recipient",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"recipient_id",
|
||||
);
|
||||
|
||||
game.addPlayer(donorInfo);
|
||||
game.addPlayer(recipientInfo);
|
||||
|
||||
const donor = game.player(donorInfo.id);
|
||||
const recipient = game.player(recipientInfo.id);
|
||||
|
||||
// Spawn both players
|
||||
const spawnA = game.ref(0, 10);
|
||||
const spawnB = game.ref(0, 15);
|
||||
|
||||
game.addExecution(
|
||||
new SpawnExecution(donorInfo, spawnA),
|
||||
new SpawnExecution(recipientInfo, spawnB),
|
||||
);
|
||||
|
||||
while (game.inSpawnPhase()) {
|
||||
game.executeNextTick();
|
||||
}
|
||||
|
||||
// donor sends alliance request to recipient
|
||||
const allianceRequest = donor.createAllianceRequest(recipient);
|
||||
expect(allianceRequest).not.toBeNull();
|
||||
|
||||
// recipient accepts the alliance request
|
||||
if (allianceRequest) {
|
||||
allianceRequest.accept();
|
||||
}
|
||||
game.executeNextTick();
|
||||
|
||||
// Ensure donor can actually donate the requested amount
|
||||
donor.addGold(6000n);
|
||||
const donorGoldBefore = donor.gold();
|
||||
const recipientGoldBefore = recipient.gold();
|
||||
game.addExecution(new DonateGoldExecution(donor, recipientInfo.id, 5000n));
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
game.executeNextTick();
|
||||
}
|
||||
|
||||
expect(donor.gold() < donorGoldBefore).toBe(true);
|
||||
expect(recipient.gold() > recipientGoldBefore).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Donate troops to a non ally", () => {
|
||||
it("Troops should not be donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteTroops: false,
|
||||
donateTroops: true,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
"donor",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"donor_id",
|
||||
);
|
||||
const recipientInfo = new PlayerInfo(
|
||||
"recipient",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"recipient_id",
|
||||
);
|
||||
|
||||
game.addPlayer(donorInfo);
|
||||
game.addPlayer(recipientInfo);
|
||||
|
||||
const donor = game.player(donorInfo.id);
|
||||
const recipient = game.player(recipientInfo.id);
|
||||
|
||||
// Spawn both players
|
||||
const spawnA = game.ref(0, 10);
|
||||
const spawnB = game.ref(0, 15);
|
||||
|
||||
game.addExecution(
|
||||
new SpawnExecution(donorInfo, spawnA),
|
||||
new SpawnExecution(recipientInfo, spawnB),
|
||||
);
|
||||
|
||||
while (game.inSpawnPhase()) {
|
||||
game.executeNextTick();
|
||||
}
|
||||
|
||||
// Donor sends alliance request to Recipient
|
||||
const allianceRequest = donor.createAllianceRequest(recipient);
|
||||
expect(allianceRequest).not.toBeNull();
|
||||
|
||||
// Donor rejects the Recipient
|
||||
if (allianceRequest) {
|
||||
allianceRequest.reject();
|
||||
}
|
||||
|
||||
const donorTroopsBefore = donor.troops();
|
||||
const recipientTroopsBefore = recipient.troops();
|
||||
|
||||
game.addExecution(new DonateTroopsExecution(donor, recipientInfo.id, 5000));
|
||||
game.executeNextTick();
|
||||
|
||||
// Troops should not be donated since they are not allies
|
||||
expect(donor.troops() >= donorTroopsBefore).toBe(true);
|
||||
expect(recipient.troops() >= recipientTroopsBefore).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Donate Gold to a non ally", () => {
|
||||
it("Gold should not be donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteGold: false,
|
||||
donateGold: true,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
"donor",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"donor_id",
|
||||
);
|
||||
const recipientInfo = new PlayerInfo(
|
||||
"recipient",
|
||||
PlayerType.Human,
|
||||
null,
|
||||
"recipient_id",
|
||||
);
|
||||
|
||||
game.addPlayer(donorInfo);
|
||||
game.addPlayer(recipientInfo);
|
||||
|
||||
const donor = game.player(donorInfo.id);
|
||||
const recipient = game.player(recipientInfo.id);
|
||||
|
||||
// Spawn both players
|
||||
const spawnA = game.ref(0, 10);
|
||||
const spawnB = game.ref(0, 15);
|
||||
|
||||
game.addExecution(
|
||||
new SpawnExecution(donorInfo, spawnA),
|
||||
new SpawnExecution(recipientInfo, spawnB),
|
||||
);
|
||||
|
||||
while (game.inSpawnPhase()) {
|
||||
game.executeNextTick();
|
||||
}
|
||||
|
||||
// Donor sends alliance request to Recipient
|
||||
const allianceRequest = donor.createAllianceRequest(recipient);
|
||||
expect(allianceRequest).not.toBeNull();
|
||||
|
||||
// Donor rejects the Recipient
|
||||
if (allianceRequest) {
|
||||
allianceRequest.reject();
|
||||
}
|
||||
|
||||
const donorGoldBefore = donor.gold();
|
||||
const recipientGoldBefore = donor.gold();
|
||||
|
||||
game.addExecution(new DonateGoldExecution(donor, recipientInfo.id, 5000n));
|
||||
game.executeNextTick();
|
||||
|
||||
// Gold should not be donated since they are not allies
|
||||
expect(donor.gold() >= donorGoldBefore).toBe(true);
|
||||
expect(recipient.gold() >= recipientGoldBefore).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -129,7 +129,8 @@ describe("RadialMenuElements", () => {
|
||||
interaction: {
|
||||
canSendAllianceRequest: true,
|
||||
canBreakAlliance: false,
|
||||
canDonate: true,
|
||||
canDonateTroops: true,
|
||||
canDonateGold: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ export async function setup(
|
||||
gameType: GameType.Singleplayer,
|
||||
difficulty: Difficulty.Medium,
|
||||
disableNPCs: false,
|
||||
donateGold: false,
|
||||
donateTroops: false,
|
||||
bots: 0,
|
||||
infiniteGold: false,
|
||||
infiniteTroops: false,
|
||||
|
||||
Reference in New Issue
Block a user