mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-10 04:04:45 +00:00
send fire emoji to other players
This commit is contained in:
+16
-3
@@ -1,5 +1,5 @@
|
||||
import {z} from 'zod';
|
||||
import {PlayerType} from './game/Game';
|
||||
import {Emoji, PlayerType} from './game/Game';
|
||||
|
||||
export type GameID = string
|
||||
export type ClientID = string
|
||||
@@ -12,6 +12,7 @@ export type Intent = SpawnIntent
|
||||
| AllianceRequestReplyIntent
|
||||
| BreakAllianceIntent
|
||||
| TargetPlayerIntent
|
||||
| EmojiIntent
|
||||
|
||||
export type AttackIntent = z.infer<typeof AttackIntentSchema>
|
||||
export type SpawnIntent = z.infer<typeof SpawnIntentSchema>
|
||||
@@ -21,7 +22,7 @@ export type AllianceRequestIntent = z.infer<typeof AllianceRequestIntentSchema>
|
||||
export type AllianceRequestReplyIntent = z.infer<typeof AllianceRequestReplyIntentSchema>
|
||||
export type BreakAllianceIntent = z.infer<typeof BreakAllianceIntentSchema>
|
||||
export type TargetPlayerIntent = z.infer<typeof TargetPlayerIntentSchema>
|
||||
|
||||
export type EmojiIntent = z.infer<typeof EmojiIntentSchema>
|
||||
|
||||
export type Turn = z.infer<typeof TurnSchema>
|
||||
|
||||
@@ -37,6 +38,8 @@ export type ClientJoinMessage = z.infer<typeof ClientJoinMessageSchema>
|
||||
export type ClientLeaveMessage = z.infer<typeof ClientLeaveMessageSchema>
|
||||
|
||||
const PlayerTypeSchema = z.nativeEnum(PlayerType);
|
||||
const EmojiSchema = z.nativeEnum(Emoji);
|
||||
|
||||
|
||||
// TODO: create Cell schema
|
||||
|
||||
@@ -46,9 +49,11 @@ export interface Lobby {
|
||||
numClients: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Zod schemas
|
||||
const BaseIntentSchema = z.object({
|
||||
type: z.enum(['attack', 'spawn', 'boat', 'name']),
|
||||
type: z.enum(['attack', 'spawn', 'boat', 'name', 'targetPlayer', 'emoji']),
|
||||
clientID: z.string(),
|
||||
});
|
||||
|
||||
@@ -112,6 +117,13 @@ export const TargetPlayerIntentSchema = BaseIntentSchema.extend({
|
||||
target: z.string(),
|
||||
})
|
||||
|
||||
export const EmojiIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('emoji'),
|
||||
sender: z.string(),
|
||||
recipient: z.string(),
|
||||
emoji: EmojiSchema,
|
||||
})
|
||||
|
||||
const IntentSchema = z.union([
|
||||
AttackIntentSchema,
|
||||
SpawnIntentSchema,
|
||||
@@ -121,6 +133,7 @@ const IntentSchema = z.union([
|
||||
AllianceRequestReplyIntentSchema,
|
||||
BreakAllianceIntentSchema,
|
||||
TargetPlayerIntentSchema,
|
||||
EmojiIntentSchema,
|
||||
]);
|
||||
|
||||
const TurnSchema = z.object({
|
||||
|
||||
@@ -51,6 +51,8 @@ export interface Config {
|
||||
allianceRequestCooldown(): Tick
|
||||
targetDuration(): Tick
|
||||
targetCooldown(): Tick
|
||||
emojiMessageCooldown(): Tick
|
||||
emojiMessageDuration(): Tick
|
||||
}
|
||||
|
||||
export interface Theme {
|
||||
|
||||
@@ -7,6 +7,12 @@ import {pastelTheme} from "./PastelTheme";
|
||||
|
||||
|
||||
export class DefaultConfig implements Config {
|
||||
emojiMessageDuration(): Tick {
|
||||
return 5 * 10
|
||||
}
|
||||
emojiMessageCooldown(): Tick {
|
||||
return 15 * 10
|
||||
}
|
||||
targetDuration(): Tick {
|
||||
return 10 * 10
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ export const devConfig = new class extends DefaultConfig {
|
||||
return 95
|
||||
}
|
||||
numSpawnPhaseTurns(): number {
|
||||
return 40
|
||||
}
|
||||
gameCreationRate(): number {
|
||||
return 2 * 1000
|
||||
}
|
||||
lobbyLifetime(): number {
|
||||
return 2 * 1000
|
||||
return 80
|
||||
}
|
||||
// gameCreationRate(): number {
|
||||
// return 2 * 1000
|
||||
// }
|
||||
// lobbyLifetime(): number {
|
||||
// return 2 * 1000
|
||||
// }
|
||||
turnIntervalMs(): number {
|
||||
return 100
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import {AllPlayers, Emoji, Execution, MutableGame, MutablePlayer, PlayerID} from "../game/Game";
|
||||
|
||||
export class EmojiExecution implements Execution {
|
||||
|
||||
private requestor: MutablePlayer
|
||||
private recipient: MutablePlayer | typeof AllPlayers
|
||||
|
||||
private active = true
|
||||
|
||||
constructor(
|
||||
private senderID: PlayerID,
|
||||
private recipientID: PlayerID | typeof AllPlayers,
|
||||
private emoji: Emoji
|
||||
) { }
|
||||
|
||||
|
||||
init(mg: MutableGame, ticks: number): void {
|
||||
this.requestor = mg.player(this.senderID)
|
||||
this.recipient = this.recipientID == AllPlayers ? AllPlayers : mg.player(this.recipientID)
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.requestor.canSendEmoji(this.recipient)) {
|
||||
this.requestor.sendEmoji(this.recipient, this.emoji)
|
||||
} else {
|
||||
console.warn(`cannot send emoji from ${this.requestor} to ${this.recipient}`)
|
||||
}
|
||||
this.active = false
|
||||
}
|
||||
|
||||
owner(): MutablePlayer {
|
||||
return null
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active
|
||||
}
|
||||
|
||||
activeDuringSpawnPhase(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import {AllianceRequestExecution} from "./alliance/AllianceRequestExecution";
|
||||
import {AllianceRequestReplyExecution} from "./alliance/AllianceRequestReplyExecution";
|
||||
import {BreakAllianceExecution} from "./alliance/BreakAllianceExecution";
|
||||
import {TargetPlayerExecution} from "./TargetPlayerExecution";
|
||||
import {EmojiExecution} from "./EmojiExecution";
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +68,8 @@ export class Executor {
|
||||
return new BreakAllianceExecution(intent.requestor, intent.recipient)
|
||||
} else if (intent.type == "targetPlayer") {
|
||||
return new TargetPlayerExecution(intent.requestor, intent.target)
|
||||
} else if (intent.type == "emoji") {
|
||||
return new EmojiExecution(intent.sender, intent.recipient, intent.emoji)
|
||||
}
|
||||
else {
|
||||
throw new Error(`intent type ${intent} not found`)
|
||||
|
||||
@@ -8,6 +8,26 @@ import {BreakAllianceExecution} from "../execution/alliance/BreakAllianceExecuti
|
||||
export type PlayerID = string
|
||||
export type Tick = number
|
||||
|
||||
export enum Emoji {
|
||||
ThumbsUp = "👍",
|
||||
ThumbsDown = "👎",
|
||||
Smile = "😊",
|
||||
Sad = "😢",
|
||||
Heart = "❤️",
|
||||
Fire = "🔥",
|
||||
}
|
||||
|
||||
export const AllPlayers = "AllPlayers" as const;
|
||||
|
||||
export class EmojiMessage {
|
||||
constructor(
|
||||
public readonly sender: Player,
|
||||
public readonly recipient: Player | typeof AllPlayers,
|
||||
public readonly emoji: Emoji,
|
||||
public readonly createdAt: Tick
|
||||
) { }
|
||||
}
|
||||
|
||||
export class Cell {
|
||||
|
||||
private strRepr: string
|
||||
@@ -156,6 +176,8 @@ export interface Player {
|
||||
// Targets of player and all allies.
|
||||
transitiveTargets(): Player[]
|
||||
toString(): string
|
||||
canSendEmoji(recipient: Player | typeof AllPlayers): boolean
|
||||
outgoingEmojis(): EmojiMessage[]
|
||||
}
|
||||
|
||||
export interface MutablePlayer extends Player {
|
||||
@@ -178,6 +200,8 @@ export interface MutablePlayer extends Player {
|
||||
target(other: Player): void
|
||||
targets(): MutablePlayer[]
|
||||
transitiveTargets(): MutablePlayer[]
|
||||
// Null means send to all Players
|
||||
sendEmoji(recipient: Player | typeof AllPlayers, emoji: Emoji): void
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
@@ -241,4 +265,8 @@ export class AllianceExpiredEvent implements GameEvent {
|
||||
|
||||
export class TargetPlayerEvent implements GameEvent {
|
||||
constructor(public readonly player: Player, public readonly target: Player) { }
|
||||
}
|
||||
|
||||
export class EmojiMessageEvent implements GameEvent {
|
||||
constructor(public readonly message: EmojiMessage) { }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent} from "./Game";
|
||||
import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, Emoji, EmojiMessage, EmojiMessageEvent, AllPlayers} from "./Game";
|
||||
import {ClientID} from "../Schemas";
|
||||
import {simpleHash} from "../Util";
|
||||
import {CellString, GameImpl} from "./GameImpl";
|
||||
@@ -26,6 +26,8 @@ export class PlayerImpl implements MutablePlayer {
|
||||
|
||||
private targets_: Target[] = []
|
||||
|
||||
private outgoingEmojis_: EmojiMessage[] = []
|
||||
|
||||
constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) {
|
||||
this._name = playerInfo.name;
|
||||
}
|
||||
@@ -207,6 +209,29 @@ export class PlayerImpl implements MutablePlayer {
|
||||
return [...new Set(ts)]
|
||||
}
|
||||
|
||||
sendEmoji(recipient: Player | typeof AllPlayers, emoji: Emoji): void {
|
||||
if (recipient == this) {
|
||||
throw Error(`Cannot send emoji to oneself: ${this}`)
|
||||
}
|
||||
const msg = new EmojiMessage(this, recipient, emoji, this.gs.ticks())
|
||||
this.outgoingEmojis_.push(msg)
|
||||
this.gs.eventBus.emit(new EmojiMessageEvent(msg))
|
||||
}
|
||||
|
||||
outgoingEmojis(): EmojiMessage[] {
|
||||
return null
|
||||
}
|
||||
|
||||
canSendEmoji(recipient: Player | null): boolean {
|
||||
const prevMsgs = this.outgoingEmojis_.filter(msg => msg.recipient == recipient)
|
||||
for (const msg of prevMsgs) {
|
||||
if (this.gs.ticks() - msg.createdAt < this.gs.config().emojiMessageCooldown()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
hash(): number {
|
||||
return simpleHash(this.id()) * (this.troops() + this.numTilesOwned());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user