mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:40:44 +00:00
Target player creates target icon
This commit is contained in:
@@ -43,6 +43,12 @@ export class SendBoatAttackIntentEvent implements GameEvent {
|
||||
) { }
|
||||
}
|
||||
|
||||
export class SendTargetPlayerIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly targetID: PlayerID,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class Transport {
|
||||
|
||||
public onconnect: () => {}
|
||||
@@ -61,6 +67,7 @@ export class Transport {
|
||||
this.eventBus.on(SendSpawnIntentEvent, (e) => this.onSendSpawnIntentEvent(e))
|
||||
this.eventBus.on(SendAttackIntentEvent, (e) => this.onSendAttackIntent(e))
|
||||
this.eventBus.on(SendBoatAttackIntentEvent, (e) => this.onSendBoatAttackIntent(e))
|
||||
this.eventBus.on(SendTargetPlayerIntentEvent, (e) => this.onSendTargetPlayerIntent(e))
|
||||
}
|
||||
|
||||
connect(onconnect: () => void, onmessage: (message: ServerMessage) => void, isActive: () => boolean) {
|
||||
@@ -184,6 +191,15 @@ export class Transport {
|
||||
})
|
||||
}
|
||||
|
||||
private onSendTargetPlayerIntent(event: SendTargetPlayerIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "targetPlayer",
|
||||
clientID: this.clientID,
|
||||
requestor: this.playerID,
|
||||
target: event.targetID,
|
||||
})
|
||||
}
|
||||
|
||||
private sendIntent(intent: Intent) {
|
||||
if (this.socket.readyState === WebSocket.OPEN) {
|
||||
const msg = ClientIntentMessageSchema.parse({
|
||||
|
||||
@@ -9,6 +9,7 @@ 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"
|
||||
|
||||
|
||||
@@ -34,6 +35,7 @@ export class NameLayer implements Layer {
|
||||
private seenPlayers: Set<Player> = new Set()
|
||||
private traitorIconImage: HTMLImageElement;
|
||||
private allianceIconImage: HTMLImageElement;
|
||||
private targetIconImage: HTMLImageElement;
|
||||
private crownIconImage: HTMLImageElement;
|
||||
|
||||
private myPlayer: Player | null = null
|
||||
@@ -49,6 +51,9 @@ export class NameLayer implements Layer {
|
||||
|
||||
this.crownIconImage = new Image()
|
||||
this.crownIconImage.src = crownIcon
|
||||
|
||||
this.targetIconImage = new Image()
|
||||
this.targetIconImage.src = targetIcon
|
||||
}
|
||||
|
||||
shouldTransform(): boolean {
|
||||
@@ -173,6 +178,16 @@ export class NameLayer implements Layer {
|
||||
);
|
||||
}
|
||||
|
||||
if (new Set(myPlayer.transitiveTargets()).has(render.player)) {
|
||||
context.drawImage(
|
||||
this.targetIconImage,
|
||||
nameCenterX - iconSize / 2,
|
||||
nameCenterY - iconSize / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
|
||||
context.textRendering = "optimizeSpeed";
|
||||
|
||||
context.font = `${render.fontSize}px ${this.theme.font()}`;
|
||||
|
||||
@@ -3,7 +3,7 @@ import {Cell, Game, Player, PlayerID} from "../../../core/game/Game";
|
||||
import {ClientID} from "../../../core/Schemas";
|
||||
import {manhattanDist, manhattanDistWrapped, sourceDstOceanShore} from "../../../core/Util";
|
||||
import {ContextMenuEvent, MouseUpEvent} from "../../InputHandler";
|
||||
import {SendAllianceRequestIntentEvent, SendAttackIntentEvent, SendBoatAttackIntentEvent, SendBreakAllianceIntentEvent, SendSpawnIntentEvent} from "../../Transport";
|
||||
import {SendAllianceRequestIntentEvent, SendAttackIntentEvent, SendBoatAttackIntentEvent, SendBreakAllianceIntentEvent, SendSpawnIntentEvent, SendTargetPlayerIntentEvent} from "../../Transport";
|
||||
import {TransformHandler} from "../TransformHandler";
|
||||
import {MessageType} from "./EventsDisplay";
|
||||
import {Layer} from "./Layer";
|
||||
@@ -254,6 +254,11 @@ export class RadialMenu implements Layer {
|
||||
new SendAllianceRequestIntentEvent(myPlayer, other)
|
||||
)
|
||||
})
|
||||
this.activateMenuElement(Slot.Target, "#c74848", targetIcon, () => {
|
||||
this.eventBus.emit(
|
||||
new SendTargetPlayerIntentEvent(other.id())
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {ContextMenuEvent} from "../../InputHandler";
|
||||
import {Layer} from "./Layer";
|
||||
import {TransformHandler} from "../TransformHandler";
|
||||
import {MessageType} from "./EventsDisplay";
|
||||
import {SendAllianceRequestIntentEvent, SendBreakAllianceIntentEvent} from "../../Transport";
|
||||
import {SendBreakAllianceIntentEvent} from "../../Transport";
|
||||
|
||||
interface MenuOption {
|
||||
label: string;
|
||||
|
||||
+10
-1
@@ -11,6 +11,7 @@ export type Intent = SpawnIntent
|
||||
| AllianceRequestIntent
|
||||
| AllianceRequestReplyIntent
|
||||
| BreakAllianceIntent
|
||||
| TargetPlayerIntent
|
||||
|
||||
export type AttackIntent = z.infer<typeof AttackIntentSchema>
|
||||
export type SpawnIntent = z.infer<typeof SpawnIntentSchema>
|
||||
@@ -19,6 +20,7 @@ export type UpdateNameIntent = z.infer<typeof UpdateNameIntentSchema>
|
||||
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 Turn = z.infer<typeof TurnSchema>
|
||||
@@ -104,6 +106,12 @@ export const BreakAllianceIntentSchema = BaseIntentSchema.extend({
|
||||
recipient: z.string(),
|
||||
})
|
||||
|
||||
export const TargetPlayerIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal('targetPlayer'),
|
||||
requestor: z.string(),
|
||||
target: z.string(),
|
||||
})
|
||||
|
||||
const IntentSchema = z.union([
|
||||
AttackIntentSchema,
|
||||
SpawnIntentSchema,
|
||||
@@ -111,7 +119,8 @@ const IntentSchema = z.union([
|
||||
UpdateNameIntentSchema,
|
||||
AllianceRequestIntentSchema,
|
||||
AllianceRequestReplyIntentSchema,
|
||||
BreakAllianceIntentSchema
|
||||
BreakAllianceIntentSchema,
|
||||
TargetPlayerIntentSchema,
|
||||
]);
|
||||
|
||||
const TurnSchema = z.object({
|
||||
|
||||
@@ -49,6 +49,8 @@ export interface Config {
|
||||
boatMaxNumber(): number
|
||||
allianceDuration(): Tick
|
||||
allianceRequestCooldown(): Tick
|
||||
targetDuration(): Tick
|
||||
targetCooldown(): Tick
|
||||
}
|
||||
|
||||
export interface Theme {
|
||||
|
||||
@@ -7,6 +7,12 @@ import {pastelTheme} from "./PastelTheme";
|
||||
|
||||
|
||||
export class DefaultConfig implements Config {
|
||||
targetDuration(): Tick {
|
||||
return 10 * 10
|
||||
}
|
||||
targetCooldown(): Tick {
|
||||
return 30 * 10
|
||||
}
|
||||
allianceRequestCooldown(): Tick {
|
||||
return 30 * 10
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import {Tick} from "../game/Game";
|
||||
import {GameID} from "../Schemas";
|
||||
import {DefaultConfig} from "./DefaultConfig";
|
||||
|
||||
export const devConfig = new class extends DefaultConfig {
|
||||
percentageTilesOwnedToWin(): number {
|
||||
return 95
|
||||
}
|
||||
numSpawnPhaseTurns(): number {
|
||||
return 40
|
||||
}
|
||||
gameCreationRate(): number {
|
||||
return 2 * 1000
|
||||
}
|
||||
lobbyLifetime(): number {
|
||||
return 2 * 1000
|
||||
}
|
||||
// numSpawnPhaseTurns(): number {
|
||||
// return 40
|
||||
// }
|
||||
// gameCreationRate(): number {
|
||||
// return 2 * 1000
|
||||
// }
|
||||
// lobbyLifetime(): number {
|
||||
// return 2 * 1000
|
||||
// }
|
||||
turnIntervalMs(): number {
|
||||
return 100
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {simpleHash} from "../Util";
|
||||
import {AllianceRequestExecution} from "./alliance/AllianceRequestExecution";
|
||||
import {AllianceRequestReplyExecution} from "./alliance/AllianceRequestReplyExecution";
|
||||
import {BreakAllianceExecution} from "./alliance/BreakAllianceExecution";
|
||||
import {TargetPlayerExecution} from "./TargetPlayerExecution";
|
||||
|
||||
|
||||
|
||||
@@ -64,6 +65,8 @@ export class Executor {
|
||||
return new AllianceRequestReplyExecution(intent.requestor, intent.recipient, intent.accept)
|
||||
} else if (intent.type == "breakAlliance") {
|
||||
return new BreakAllianceExecution(intent.requestor, intent.recipient)
|
||||
} else if (intent.type == "targetPlayer") {
|
||||
return new TargetPlayerExecution(intent.requestor, intent.target)
|
||||
}
|
||||
else {
|
||||
throw new Error(`intent type ${intent} not found`)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import {Execution, MutableGame, MutablePlayer, PlayerID} from "../game/Game";
|
||||
|
||||
export class TargetPlayerExecution implements Execution {
|
||||
|
||||
private requestor: MutablePlayer
|
||||
private target: MutablePlayer
|
||||
|
||||
private active = true
|
||||
|
||||
constructor(private requestorID: PlayerID, private targetID: PlayerID) { }
|
||||
|
||||
|
||||
init(mg: MutableGame, ticks: number): void {
|
||||
this.requestor = mg.player(this.requestorID)
|
||||
this.target = mg.player(this.targetID)
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.requestor.canTarget(this.target)) {
|
||||
this.requestor.target(this.target)
|
||||
}
|
||||
this.active = false
|
||||
}
|
||||
|
||||
owner(): MutablePlayer {
|
||||
return null
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.active
|
||||
}
|
||||
|
||||
activeDuringSpawnPhase(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,13 @@ export class AllianceImpl implements MutableAlliance {
|
||||
readonly createdAtTick_: Tick,
|
||||
) { }
|
||||
|
||||
other(player: Player): PlayerImpl {
|
||||
if (this.requestor_ == player) {
|
||||
return this.recipient_
|
||||
}
|
||||
return this.requestor_
|
||||
}
|
||||
|
||||
requestor(): MutablePlayer {
|
||||
return this.requestor_
|
||||
}
|
||||
|
||||
@@ -64,10 +64,12 @@ export interface Alliance {
|
||||
requestor(): Player
|
||||
recipient(): Player
|
||||
createdAt(): Tick
|
||||
other(player: Player): Player
|
||||
}
|
||||
|
||||
export interface MutableAlliance extends Alliance {
|
||||
expire(): void
|
||||
other(player: Player): MutablePlayer
|
||||
}
|
||||
|
||||
export class PlayerInfo {
|
||||
@@ -148,6 +150,11 @@ export interface Player {
|
||||
// Includes recent requests that are in cooldown
|
||||
recentOrPendingAllianceRequestWith(other: Player): boolean
|
||||
isTraitor(): boolean
|
||||
canTarget(other: Player): boolean
|
||||
// Targets for this player
|
||||
targets(): Player[]
|
||||
// Targets of player and all allies.
|
||||
transitiveTargets(): Player[]
|
||||
toString(): string
|
||||
}
|
||||
|
||||
@@ -168,6 +175,9 @@ export interface MutablePlayer extends Player {
|
||||
breakAlliance(alliance: Alliance): void
|
||||
createAllianceRequest(recipient: Player): MutableAllianceRequest
|
||||
addBoat(troops: number, tile: Tile, target: Player | TerraNullius): MutableBoat
|
||||
target(other: Player): void
|
||||
targets(): MutablePlayer[]
|
||||
transitiveTargets(): MutablePlayer[]
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance} from "./Game";
|
||||
import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick} from "./Game";
|
||||
import {ClientID} from "../Schemas";
|
||||
import {simpleHash} from "../Util";
|
||||
import {CellString, GameImpl} from "./GameImpl";
|
||||
@@ -7,6 +7,10 @@ import {TileImpl} from "./TileImpl";
|
||||
import {TerraNulliusImpl} from "./TerraNulliusImpl";
|
||||
import {threadId} from "worker_threads";
|
||||
|
||||
interface Target {
|
||||
tick: Tick
|
||||
target: Player
|
||||
}
|
||||
|
||||
export class PlayerImpl implements MutablePlayer {
|
||||
isTraitor_ = false
|
||||
@@ -20,6 +24,8 @@ export class PlayerImpl implements MutablePlayer {
|
||||
|
||||
public pastOutgoingAllianceRequests: AllianceRequest[] = []
|
||||
|
||||
private targets_: Target[] = []
|
||||
|
||||
constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) {
|
||||
this._name = playerInfo.name;
|
||||
}
|
||||
@@ -172,6 +178,33 @@ export class PlayerImpl implements MutablePlayer {
|
||||
return this.gs.createAllianceRequest(this, recipient)
|
||||
}
|
||||
|
||||
canTarget(other: Player): boolean {
|
||||
for (const t of this.targets_) {
|
||||
if (t.target == other) {
|
||||
if (this.gs.ticks() - t.tick < this.gs.config().targetCooldown()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
target(other: Player): void {
|
||||
this.targets_.push({tick: this.gs.ticks(), target: other})
|
||||
}
|
||||
|
||||
targets(): PlayerImpl[] {
|
||||
return this.targets_
|
||||
.filter(t => this.gs.ticks() - t.tick < this.gs.config().targetDuration())
|
||||
.map(t => t.target as PlayerImpl)
|
||||
}
|
||||
|
||||
transitiveTargets(): MutablePlayer[] {
|
||||
const ts = this.alliances().map(a => a.other(this)).flatMap(ally => ally.targets())
|
||||
ts.push(...this.targets())
|
||||
return [...new Set(ts)]
|
||||
}
|
||||
|
||||
hash(): number {
|
||||
return simpleHash(this.id()) * (this.troops() + this.numTilesOwned());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user