mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-18 01:31:56 +00:00
Added Gold & Troop donate button (#373)
## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors Changed the icon for the troop donation button, as it looked like it was donating gold. I replaced it with a soldier icon to better reflect its function. Additionally, I updated the icon below it to clearly represent gold donation instead. ## Please put your Discord username so you can be contacted if a bug or regression is found: aotumuri The cat is because of an extension I have put in. Please ignore it. <img width="345" alt="スクリーンショット 2025-03-30 10 13 06" src="https://github.com/user-attachments/assets/9088be1a-57b4-4d10-9507-ca8dc0fcc68c" /> <img width="396" alt="スクリーンショット 2025-03-30 10 18 10" src="https://github.com/user-attachments/assets/2555fd24-0bbc-4b40-8f80-ccf43a1e9a75" /> --------- Co-authored-by: Evan <evanpelle@gmail.com>
This commit is contained in:
+14
-5
@@ -23,7 +23,8 @@ export type Intent =
|
||||
| BreakAllianceIntent
|
||||
| TargetPlayerIntent
|
||||
| EmojiIntent
|
||||
| DonateIntent
|
||||
| DonateGoldIntent
|
||||
| DonateTroopsIntent
|
||||
| TargetTroopRatioIntent
|
||||
| BuildUnitIntent
|
||||
| EmbargoIntent
|
||||
@@ -40,7 +41,8 @@ export type AllianceRequestReplyIntent = z.infer<
|
||||
export type BreakAllianceIntent = z.infer<typeof BreakAllianceIntentSchema>;
|
||||
export type TargetPlayerIntent = z.infer<typeof TargetPlayerIntentSchema>;
|
||||
export type EmojiIntent = z.infer<typeof EmojiIntentSchema>;
|
||||
export type DonateIntent = z.infer<typeof DonateIntentSchema>;
|
||||
export type DonateGoldIntent = z.infer<typeof DonateGoldIntentSchema>;
|
||||
export type DonateTroopsIntent = z.infer<typeof DonateTroopIntentSchema>;
|
||||
export type EmbargoIntent = z.infer<typeof EmbargoIntentSchema>;
|
||||
export type TargetTroopRatioIntent = z.infer<
|
||||
typeof TargetTroopRatioIntentSchema
|
||||
@@ -232,8 +234,14 @@ export const EmbargoIntentSchema = BaseIntentSchema.extend({
|
||||
action: z.union([z.literal("start"), z.literal("stop")]),
|
||||
});
|
||||
|
||||
export const DonateIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("donate"),
|
||||
export const DonateGoldIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("donate_gold"),
|
||||
recipient: ID,
|
||||
gold: z.number().nullable(),
|
||||
});
|
||||
|
||||
export const DonateTroopIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("donate_troops"),
|
||||
recipient: ID,
|
||||
troops: z.number().nullable(),
|
||||
});
|
||||
@@ -271,7 +279,8 @@ const IntentSchema = z.union([
|
||||
BreakAllianceIntentSchema,
|
||||
TargetPlayerIntentSchema,
|
||||
EmojiIntentSchema,
|
||||
DonateIntentSchema,
|
||||
DonateGoldIntentSchema,
|
||||
DonateTroopIntentSchema,
|
||||
TargetTroopRatioIntentSchema,
|
||||
BuildUnitIntentSchema,
|
||||
EmbargoIntentSchema,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { consolex } from "../Consolex";
|
||||
import { Execution, Game, Player, PlayerID, Gold } from "../game/Game";
|
||||
|
||||
export class DonateGoldExecution implements Execution {
|
||||
private sender: Player;
|
||||
private recipient: Player;
|
||||
|
||||
private active = true;
|
||||
|
||||
constructor(
|
||||
private senderID: PlayerID,
|
||||
private recipientID: PlayerID,
|
||||
private gold: number | null,
|
||||
) {}
|
||||
|
||||
init(mg: Game, ticks: number): void {
|
||||
if (!mg.hasPlayer(this.senderID)) {
|
||||
console.warn(`DonateExecution: sender ${this.senderID} not found`);
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
if (!mg.hasPlayer(this.recipientID)) {
|
||||
console.warn(`DonateExecution recipient ${this.recipientID} not found`);
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.sender = mg.player(this.senderID);
|
||||
this.recipient = mg.player(this.recipientID);
|
||||
if (this.gold == null) {
|
||||
this.gold = Math.round(this.sender.gold() / 3);
|
||||
}
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.sender.canDonate(this.recipient)) {
|
||||
this.sender.donateGold(this.recipient, this.gold);
|
||||
this.recipient.updateRelation(this.sender, 50);
|
||||
} else {
|
||||
consolex.warn(
|
||||
`cannot send gold from ${this.sender.name()} to ${this.recipient.name()}`,
|
||||
);
|
||||
}
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
owner(): Player {
|
||||
return null;
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
activeDuringSpawnPhase(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { consolex } from "../Consolex";
|
||||
import { Execution, Game, Player, PlayerID } from "../game/Game";
|
||||
import { Execution, Game, Player, PlayerID, Gold } from "../game/Game";
|
||||
|
||||
export class DonateExecution implements Execution {
|
||||
export class DonateTroopsExecution implements Execution {
|
||||
private sender: Player;
|
||||
private recipient: Player;
|
||||
|
||||
@@ -34,7 +34,7 @@ export class DonateExecution implements Execution {
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.sender.canDonate(this.recipient)) {
|
||||
this.sender.donate(this.recipient, this.troops);
|
||||
this.sender.donateTroops(this.recipient, this.troops);
|
||||
this.recipient.updateRelation(this.sender, 50);
|
||||
} else {
|
||||
consolex.warn(
|
||||
@@ -29,7 +29,8 @@ import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyEx
|
||||
import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution";
|
||||
import { TargetPlayerExecution } from "./TargetPlayerExecution";
|
||||
import { EmojiExecution } from "./EmojiExecution";
|
||||
import { DonateExecution } from "./DonateExecution";
|
||||
import { DonateTroopsExecution } from "./DonateTroopExecution";
|
||||
import { DonateGoldExecution } from "./DonateGoldExecution";
|
||||
import { SetTargetTroopRatioExecution } from "./SetTargetTroopRatioExecution";
|
||||
import { ConstructionExecution } from "./ConstructionExecution";
|
||||
import { fixProfaneUsername, isProfaneUsername } from "../validations/username";
|
||||
@@ -111,8 +112,14 @@ export class Executor {
|
||||
return new TargetPlayerExecution(playerID, intent.target);
|
||||
case "emoji":
|
||||
return new EmojiExecution(playerID, intent.recipient, intent.emoji);
|
||||
case "donate":
|
||||
return new DonateExecution(playerID, intent.recipient, intent.troops);
|
||||
case "donate_troops":
|
||||
return new DonateTroopsExecution(
|
||||
playerID,
|
||||
intent.recipient,
|
||||
intent.troops,
|
||||
);
|
||||
case "donate_gold":
|
||||
return new DonateGoldExecution(playerID, intent.recipient, intent.gold);
|
||||
case "troop_ratio":
|
||||
return new SetTargetTroopRatioExecution(playerID, intent.ratio);
|
||||
case "embargo":
|
||||
|
||||
@@ -384,7 +384,8 @@ export interface Player {
|
||||
|
||||
// Donation
|
||||
canDonate(recipient: Player): boolean;
|
||||
donate(recipient: Player, troops: number): void;
|
||||
donateTroops(recipient: Player, troops: number): void;
|
||||
donateGold(recipient: Player, gold: number): void;
|
||||
|
||||
// Embargo
|
||||
hasEmbargoAgainst(other: Player): boolean;
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
import { CellString, GameImpl } from "./GameImpl";
|
||||
import { UnitImpl } from "./UnitImpl";
|
||||
import { MessageType } from "./Game";
|
||||
import { renderTroops } from "../../client/Utils";
|
||||
import { renderTroops, renderNumber } from "../../client/Utils";
|
||||
import { TerraNulliusImpl } from "./TerraNulliusImpl";
|
||||
import { andFN, manhattanDistFN, TileRef } from "./GameMap";
|
||||
import { AttackImpl } from "./AttackImpl";
|
||||
@@ -523,7 +523,7 @@ export class PlayerImpl implements Player {
|
||||
return true;
|
||||
}
|
||||
|
||||
donate(recipient: Player, troops: number): void {
|
||||
donateTroops(recipient: Player, troops: number): void {
|
||||
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
|
||||
recipient.addTroops(this.removeTroops(troops));
|
||||
this.mg.displayMessage(
|
||||
@@ -537,6 +537,20 @@ export class PlayerImpl implements Player {
|
||||
recipient.id(),
|
||||
);
|
||||
}
|
||||
donateGold(recipient: Player, gold: number): void {
|
||||
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,
|
||||
this.id(),
|
||||
);
|
||||
this.mg.displayMessage(
|
||||
`Recieved ${renderNumber(gold)} gold from ${this.name()}`,
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
}
|
||||
|
||||
hasEmbargoAgainst(other: Player): boolean {
|
||||
return this.embargoes.has(other.id());
|
||||
@@ -588,13 +602,13 @@ export class PlayerImpl implements Player {
|
||||
this._gold += toInt(toAdd);
|
||||
}
|
||||
|
||||
removeGold(toRemove: Gold): void {
|
||||
if (toRemove > this._gold) {
|
||||
throw Error(
|
||||
`Player ${this} does not enough gold (${toRemove} vs ${this._gold}))`,
|
||||
);
|
||||
removeGold(toRemove: Gold): number {
|
||||
if (toRemove <= 1) {
|
||||
return 0;
|
||||
}
|
||||
this._gold -= toInt(toRemove);
|
||||
const actualRemoved = minInt(this._gold, toInt(toRemove));
|
||||
this._gold -= actualRemoved;
|
||||
return Number(actualRemoved);
|
||||
}
|
||||
|
||||
population(): number {
|
||||
|
||||
Reference in New Issue
Block a user