Add filters tabs to EvensDisplay to let users filter events (#1080)

## Description:
Big update to the EventsDisplay

- Style update for EventsDisplay, look & feel similar to other windows
- Component now hidden during spawn phase
- Adds new functionality for filtering events by category. Allows the
player to remove specific event types
- Displays latest gold amount, decays after 5 seconds

<img width="1147" alt="Screenshot 2025-06-07 at 20 18 55"
src="https://github.com/user-attachments/assets/11c39818-55ad-4ba1-a998-360057e2856c"
/>

<img width="422" alt="Screenshot 2025-06-07 at 19 01 55"
src="https://github.com/user-attachments/assets/09c0b998-6046-49fb-9fba-33b4f57f337b"
/>

<img width="444" alt="Screenshot 2025-06-07 at 20 20 25"
src="https://github.com/user-attachments/assets/022deadc-3a49-442a-85f5-f1cd128a5805"
/>

![Screen Shot 2025-06-07 at 20 32
07](https://github.com/user-attachments/assets/d8575ea0-109d-4841-b661-b233201a304a)



## 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
- [X] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

maxion_


Fixes #1025 
Fixes #1034
This commit is contained in:
Max Lundgren
2025-06-08 12:16:23 +03:00
committed by GitHub
parent 67c8e2799a
commit 4170aca548
17 changed files with 712 additions and 249 deletions
+3 -2
View File
@@ -180,7 +180,7 @@ export class AttackExecution implements Execution {
if (deaths) {
this.mg.displayMessage(
`Attack cancelled, ${renderTroops(deaths)} soldiers killed during retreat.`,
MessageType.SUCCESS,
MessageType.ATTACK_CANCELLED,
this._owner.id(),
);
}
@@ -340,8 +340,9 @@ export class AttackExecution implements Execution {
`Conquered ${this.target.displayName()} received ${renderNumber(
gold,
)} gold`,
MessageType.SUCCESS,
MessageType.CONQUERED_PLAYER,
this._owner.id(),
gold,
);
this.target.removeGold(gold);
this._owner.addGold(gold);
+2 -1
View File
@@ -67,8 +67,9 @@ export class MirvExecution implements Execution {
this.mg.displayIncomingUnit(
this.nuke.id(),
// TODO TranslateText
`⚠️⚠️⚠️ ${this.player.name()} - MIRV INBOUND ⚠️⚠️⚠️`,
MessageType.ERROR,
MessageType.MIRV_INBOUND,
this.targetPlayer.id(),
);
}
+4 -2
View File
@@ -111,16 +111,18 @@ export class NukeExecution implements Execution {
} else if (this.nukeType === UnitType.AtomBomb) {
this.mg.displayIncomingUnit(
this.nuke.id(),
// TODO TranslateText
`${this.player.name()} - atom bomb inbound`,
MessageType.ERROR,
MessageType.NUKE_INBOUND,
target.id(),
);
this.breakAlliances(this.tilesToDestroy());
} else if (this.nukeType === UnitType.HydrogenBomb) {
this.mg.displayIncomingUnit(
this.nuke.id(),
// TODO TranslateText
`${this.player.name()} - hydrogen bomb inbound`,
MessageType.ERROR,
MessageType.HYDROGEN_BOMB_INBOUND,
target.id(),
);
this.breakAlliances(this.tilesToDestroy());
+2 -1
View File
@@ -207,8 +207,9 @@ export class PlayerExecution implements Execution {
`Conquered ${this.player.displayName()} received ${renderNumber(
gold,
)} gold`,
MessageType.SUCCESS,
MessageType.CONQUERED_PLAYER,
capturing.id(),
gold,
);
capturing.addGold(gold);
this.player.removeGold(gold);
+2 -2
View File
@@ -174,7 +174,7 @@ export class SAMLauncherExecution implements Execution {
if (!hit) {
this.mg.displayMessage(
`Missile failed to intercept ${type}`,
MessageType.ERROR,
MessageType.SAM_MISS,
this.sam.owner().id(),
);
} else {
@@ -182,7 +182,7 @@ export class SAMLauncherExecution implements Execution {
// Message
this.mg.displayMessage(
`${mirvWarheadTargets.length} MIRV warheads intercepted`,
MessageType.SUCCESS,
MessageType.SAM_HIT,
this.sam.owner().id(),
);
// Delete warheads
+1 -1
View File
@@ -62,7 +62,7 @@ export class SAMMissileExecution implements Execution {
if (result === true) {
this.mg.displayMessage(
`Missile intercepted ${this.target.type()}`,
MessageType.SUCCESS,
MessageType.SAM_HIT,
this._owner.id(),
);
this.active = false;
+6 -3
View File
@@ -131,21 +131,24 @@ export class TradeShipExecution implements Execution {
this.tradeShip!.owner().addGold(gold);
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from ship captured from ${this.origOwner.displayName()}`,
MessageType.SUCCESS,
MessageType.CAPTURED_ENEMY_UNIT,
this.tradeShip!.owner().id(),
gold,
);
} else {
this.srcPort.owner().addGold(gold);
this._dstPort.owner().addGold(gold);
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from trade with ${this.srcPort.owner().displayName()}`,
MessageType.SUCCESS,
MessageType.RECEIVED_GOLD_FROM_TRADE,
this._dstPort.owner().id(),
gold,
);
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from trade with ${this._dstPort.owner().displayName()}`,
MessageType.SUCCESS,
MessageType.RECEIVED_GOLD_FROM_TRADE,
this.srcPort.owner().id(),
gold,
);
}
return;
+3 -2
View File
@@ -62,7 +62,7 @@ export class TransportShipExecution implements Execution {
) {
mg.displayMessage(
`No boats available, max ${mg.config().boatMaxNumber()}`,
MessageType.WARN,
MessageType.ATTACK_FAILED,
this.attacker.id(),
);
this.active = false;
@@ -130,8 +130,9 @@ export class TransportShipExecution implements Execution {
if (this.targetID && this.targetID !== mg.terraNullius().id()) {
mg.displayIncomingUnit(
this.boat.id(),
// TODO TranslateText
`Naval invasion incoming from ${this.attacker.displayName()}`,
MessageType.WARN,
MessageType.NAVAL_INVASION_INBOUND,
this.targetID,
);
}
+67 -4
View File
@@ -587,6 +587,7 @@ export interface Game extends GameMap {
message: string,
type: MessageType,
playerID: PlayerID | null,
goldAmount?: bigint,
): void;
displayIncomingUnit(
unitID: number,
@@ -653,13 +654,75 @@ export interface EmojiMessage {
}
export enum MessageType {
SUCCESS,
INFO,
WARN,
ERROR,
ATTACK_FAILED,
ATTACK_CANCELLED,
ATTACK_REQUEST,
CONQUERED_PLAYER,
MIRV_INBOUND,
NUKE_INBOUND,
HYDROGEN_BOMB_INBOUND,
NAVAL_INVASION_INBOUND,
SAM_MISS,
SAM_HIT,
CAPTURED_ENEMY_UNIT,
UNIT_CAPTURED_BY_ENEMY,
UNIT_DESTROYED,
ALLIANCE_ACCEPTED,
ALLIANCE_REJECTED,
ALLIANCE_REQUEST,
ALLIANCE_BROKEN,
ALLIANCE_EXPIRED,
SENT_GOLD_TO_PLAYER,
RECEIVED_GOLD_FROM_PLAYER,
RECEIVED_GOLD_FROM_TRADE,
SENT_TROOPS_TO_PLAYER,
RECEIVED_TROOPS_FROM_PLAYER,
CHAT,
}
// Message categories used for filtering events in the EventsDisplay
export enum MessageCategory {
ATTACK = "ATTACK",
ALLIANCE = "ALLIANCE",
TRADE = "TRADE",
CHAT = "CHAT",
}
// Ensures that all message types are included in a category
export const MESSAGE_TYPE_CATEGORIES: Record<MessageType, MessageCategory> = {
[MessageType.ATTACK_FAILED]: MessageCategory.ATTACK,
[MessageType.ATTACK_CANCELLED]: MessageCategory.ATTACK,
[MessageType.ATTACK_REQUEST]: MessageCategory.ATTACK,
[MessageType.CONQUERED_PLAYER]: MessageCategory.ATTACK,
[MessageType.MIRV_INBOUND]: MessageCategory.ATTACK,
[MessageType.NUKE_INBOUND]: MessageCategory.ATTACK,
[MessageType.HYDROGEN_BOMB_INBOUND]: MessageCategory.ATTACK,
[MessageType.NAVAL_INVASION_INBOUND]: MessageCategory.ATTACK,
[MessageType.SAM_MISS]: MessageCategory.ATTACK,
[MessageType.SAM_HIT]: MessageCategory.ATTACK,
[MessageType.CAPTURED_ENEMY_UNIT]: MessageCategory.ATTACK,
[MessageType.UNIT_CAPTURED_BY_ENEMY]: MessageCategory.ATTACK,
[MessageType.UNIT_DESTROYED]: MessageCategory.ATTACK,
[MessageType.ALLIANCE_ACCEPTED]: MessageCategory.ALLIANCE,
[MessageType.ALLIANCE_REJECTED]: MessageCategory.ALLIANCE,
[MessageType.ALLIANCE_REQUEST]: MessageCategory.ALLIANCE,
[MessageType.ALLIANCE_BROKEN]: MessageCategory.ALLIANCE,
[MessageType.ALLIANCE_EXPIRED]: MessageCategory.ALLIANCE,
[MessageType.SENT_GOLD_TO_PLAYER]: MessageCategory.TRADE,
[MessageType.RECEIVED_GOLD_FROM_PLAYER]: MessageCategory.TRADE,
[MessageType.RECEIVED_GOLD_FROM_TRADE]: MessageCategory.TRADE,
[MessageType.SENT_TROOPS_TO_PLAYER]: MessageCategory.TRADE,
[MessageType.RECEIVED_TROOPS_FROM_PLAYER]: MessageCategory.TRADE,
[MessageType.CHAT]: MessageCategory.CHAT,
} as const;
/**
* Get the category of a message type
*/
export function getMessageCategory(messageType: MessageType): MessageCategory {
return MESSAGE_TYPE_CATEGORIES[messageType];
}
export interface NameViewData {
x: number;
y: number;
+2
View File
@@ -612,6 +612,7 @@ export class GameImpl implements Game {
message: string,
type: MessageType,
playerID: PlayerID | null,
goldAmount?: bigint,
): void {
let id: number | null = null;
if (playerID !== null) {
@@ -622,6 +623,7 @@ export class GameImpl implements Game {
messageType: type,
message: message,
playerID: id,
goldAmount: goldAmount,
});
}
+1
View File
@@ -162,6 +162,7 @@ export interface DisplayMessageUpdate {
type: GameUpdateType.DisplayEvent;
message: string;
messageType: MessageType;
goldAmount?: bigint;
playerID: number | null;
}
+5 -4
View File
@@ -558,12 +558,12 @@ export class PlayerImpl implements Player {
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
this.mg.displayMessage(
`Sent ${renderTroops(troops)} troops to ${recipient.name()}`,
MessageType.INFO,
MessageType.SENT_TROOPS_TO_PLAYER,
this.id(),
);
this.mg.displayMessage(
`Received ${renderTroops(troops)} troops from ${this.name()}`,
MessageType.SUCCESS,
MessageType.RECEIVED_TROOPS_FROM_PLAYER,
recipient.id(),
);
return true;
@@ -578,13 +578,14 @@ export class PlayerImpl implements Player {
this.sentDonations.push(new Donation(recipient, this.mg.ticks()));
this.mg.displayMessage(
`Sent ${renderNumber(gold)} gold to ${recipient.name()}`,
MessageType.INFO,
MessageType.SENT_GOLD_TO_PLAYER,
this.id(),
);
this.mg.displayMessage(
`Received ${renderNumber(gold)} gold from ${this.name()}`,
MessageType.SUCCESS,
MessageType.RECEIVED_GOLD_FROM_PLAYER,
recipient.id(),
gold,
);
return true;
}
+4 -3
View File
@@ -160,6 +160,7 @@ export class UnitImpl implements Unit {
case UnitType.City:
this.mg.stats().unitCapture(newOwner, this._type);
this.mg.stats().unitLose(this._owner, this._type);
break;
}
this._lastOwner = this._owner;
this._lastOwner._units = this._lastOwner._units.filter((u) => u !== this);
@@ -168,12 +169,12 @@ export class UnitImpl implements Unit {
this.mg.addUpdate(this.toUpdate());
this.mg.displayMessage(
`Your ${this.type()} was captured by ${newOwner.displayName()}`,
MessageType.ERROR,
MessageType.UNIT_CAPTURED_BY_ENEMY,
this._lastOwner.id(),
);
this.mg.displayMessage(
`Captured ${this.type()} from ${this._lastOwner.displayName()}`,
MessageType.SUCCESS,
MessageType.CAPTURED_ENEMY_UNIT,
newOwner.id(),
);
}
@@ -200,7 +201,7 @@ export class UnitImpl implements Unit {
if (displayMessage !== false && this._type !== UnitType.MIRVWarhead) {
this.mg.displayMessage(
`Your ${this._type} was destroyed`,
MessageType.ERROR,
MessageType.UNIT_DESTROYED,
this.owner().id(),
);
}