Show player relations in player info overlay

This commit is contained in:
Evan
2024-12-30 09:40:16 -08:00
parent a159c50160
commit f4c98318b6
11 changed files with 90 additions and 30 deletions
@@ -1,7 +1,7 @@
import { LitElement, html, css } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { Layer } from './Layer';
import { Game, GameType, Player, PlayerType, Unit, UnitType } from '../../../core/game/Game';
import { Game, GameType, Player, PlayerType, Relation, Unit, UnitType } from '../../../core/game/Game';
import { ClientID } from '../../../core/Schemas';
import { EventBus } from '../../../core/EventBus';
import { TransformHandler } from '../TransformHandler';
@@ -114,8 +114,28 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
const isAlly = (myPlayer?.isAlliedWith(player) || player == this.myPlayer()) ?? false;
let relation = null
if (player.type() == PlayerType.FakeHuman && myPlayer != null) {
// Don't create an HTML string, let Lit handle the templating
relation = html`<div class="type-label">Attitude: ${player.relation(myPlayer)}</div>`;
let classType = ''
let relationName = ''
switch (player.relation(myPlayer)) {
case Relation.Hostile:
classType = 'hostile'
relationName = 'Hostile'
break
case Relation.Distrustful:
classType = 'distrustful'
relationName = 'Distrustful'
break
case Relation.Neutral:
classType = 'neutral'
relationName = 'Neutral'
break
case Relation.Friendly:
classType = 'friendly'
relationName = 'Friendly'
break
}
relation = html`<div class="type-label">Attitude: <span class="${classType}">${relationName}</span></div>`;
}
return html`
<div class="info-content">
@@ -273,6 +293,19 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
.type-label {
font-size: 12px;
}
}
.hostile {
color: #ff4444;
}
.distrustful {
color: #ff8888;
}
.neutral {
color: #ffffff;
}
.friendly {
color: #4CAF50;
}
`;
}
+1 -1
View File
@@ -101,7 +101,7 @@ export class AttackExecution implements Execution {
// No updates should happen in init.
this.breakAlliance = true
}
this.target.updateRelation(this._owner, -500)
this.target.updateRelation(this._owner, -80)
}
}
+2 -1
View File
@@ -1,5 +1,5 @@
import { consolex } from "../Consolex";
import {AllPlayers, Execution, MutableGame, MutablePlayer, PlayerID} from "../game/Game";
import { Execution, MutableGame, MutablePlayer, PlayerID } from "../game/Game";
export class DonateExecution implements Execution {
@@ -26,6 +26,7 @@ export class DonateExecution implements Execution {
tick(ticks: number): void {
if (this.sender.canDonate(this.recipient)) {
this.sender.donate(this.recipient, this.troops)
this.recipient.updateRelation(this.sender, 50)
} else {
consolex.warn(`cannot send tropps from ${this.sender} to ${this.recipient}`)
}
+1 -1
View File
@@ -24,7 +24,7 @@ export class EmojiExecution implements Execution {
if (this.requestor.canSendEmoji(this.recipient)) {
this.requestor.sendEmoji(this.recipient, this.emoji)
if (this.emoji == "🖕" && this.recipient != AllPlayers && this.recipient.type() == PlayerType.FakeHuman) {
this.recipient.updateRelation(this.requestor, -10000)
this.recipient.updateRelation(this.requestor, -100)
}
} else {
consolex.warn(`cannot send emoji from ${this.requestor} to ${this.recipient}`)
+5 -5
View File
@@ -1,4 +1,4 @@
import { AllianceRequest, Cell, Execution, MutableGame, MutablePlayer, Player, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tile, UnitType } from "../game/Game"
import { AllianceRequest, Cell, Execution, MutableGame, MutablePlayer, Player, PlayerInfo, PlayerType, Relation, TerrainType, TerraNullius, Tile, UnitType } from "../game/Game"
import { PseudoRandom } from "../PseudoRandom"
import { and, bfs, calculateBoundingBox, dist, euclDist, manhattanDist, simpleHash } from "../Util";
import { AttackExecution } from "./AttackExecution";
@@ -144,12 +144,12 @@ export class FakeHumanExecution implements Execution {
}
const target = this.player.allies()
.filter(ally => this.player.relation(ally) > 0)
.filter(ally => this.player.relation(ally) >= Relation.Neutral)
.filter(ally => ally.targets().length > 0)
.map(ally => ({ ally: ally, t: ally.targets() }))[0] ?? null
if (target != null) {
this.player.updateRelation(target.ally, -2000)
this.player.updateRelation(target.ally, -20)
this.enemy = target.t[0]
this.lastEnemyUpdateTick = this.mg.ticks()
this.mg.addExecution(new EmojiExecution(this.player.id(), target.ally.id(), "👍"))
@@ -157,7 +157,7 @@ export class FakeHumanExecution implements Execution {
if (this.enemy == null) {
const mostHated = this.player.allRelationsSorted()[0] ?? null
if (mostHated != null && mostHated.relation < - 2000) {
if (mostHated != null && mostHated.relation == Relation.Hostile) {
this.enemy = mostHated.player
this.lastEnemyUpdateTick = this.mg.ticks()
this.mg.addExecution(
@@ -323,7 +323,7 @@ export class FakeHumanExecution implements Execution {
this.replyToAllianceRequest(req, false)
continue
}
if (this.player.relation(req.requestor()) < 0) {
if (this.player.relation(req.requestor()) < Relation.Neutral) {
this.replyToAllianceRequest(req, false)
continue
}
+1 -1
View File
@@ -103,7 +103,7 @@ export class NukeExecution implements Execution {
this.player.breakAlliance(alliance)
}
if (other != this.player) {
other.updateRelation(this.player, -5000)
other.updateRelation(this.player, -100)
}
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ export class TargetPlayerExecution implements Execution {
tick(ticks: number): void {
if (this.requestor.canTarget(this.target)) {
this.requestor.target(this.target)
this.target.updateRelation(this.requestor, -5000)
this.target.updateRelation(this.requestor, -40)
}
this.active = false
}
@@ -25,8 +25,8 @@ export class AllianceRequestReplyExecution implements Execution {
} else {
if (this.accept) {
request.accept()
this.requestor.updateRelation(this.recipient, 5000)
this.recipient.updateRelation(this.requestor, 5000)
this.requestor.updateRelation(this.recipient, 100)
this.recipient.updateRelation(this.requestor, 100)
} else {
request.reject()
}
@@ -5,12 +5,14 @@ export class BreakAllianceExecution implements Execution {
private active = true
private requestor: MutablePlayer;
private recipient: MutablePlayer
private mg: MutableGame
constructor(private requestorID: PlayerID, private recipientID: PlayerID) { }
init(mg: MutableGame, ticks: number): void {
this.requestor = mg.player(this.requestorID)
this.recipient = mg.player(this.recipientID)
this.mg = mg
}
tick(ticks: number): void {
@@ -19,7 +21,12 @@ export class BreakAllianceExecution implements Execution {
consolex.warn('cant break alliance, not allied')
} else {
this.requestor.breakAlliance(alliance)
this.recipient.updateRelation(this.requestor, -5000)
this.recipient.updateRelation(this.requestor, -200)
for (const player of this.mg.players()) {
if (player != this.requestor) {
player.updateRelation(this.requestor, -40)
}
}
}
this.active = false
}
+9 -2
View File
@@ -53,6 +53,13 @@ export enum UnitType {
City = "City"
}
export enum Relation {
Hostile = 0,
Distrustful = 1,
Neutral = 2,
Friendly = 3
}
export class Nation {
constructor(
public readonly name: string,
@@ -242,9 +249,9 @@ export interface Player {
// TODO: why can't I have "canSendAllyRequest" function instead?
recentOrPendingAllianceRequestWith(other: Player): boolean
// How this player feels about other player.
relation(other: Player): number
relation(other: Player): Relation
// Sorted from most hated to most liked
allRelationsSorted(): { player: Player, relation: number }[]
allRelationsSorted(): { player: Player, relation: Relation }[]
isTraitor(): boolean
canTarget(other: Player): boolean
// Targets for this player
+24 -12
View File
@@ -1,4 +1,4 @@
import { MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, EmojiMessage, EmojiMessageEvent, AllPlayers, Gold, UnitType, Unit, MutableUnit } from "./Game";
import { MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, EmojiMessage, EmojiMessageEvent, AllPlayers, Gold, UnitType, Unit, MutableUnit, Relation } from "./Game";
import { ClientID } from "../Schemas";
import { assertNever, bfs, closestOceanShoreFromPlayer, dist, distSortUnit, manhattanDist, manhattanDistWrapped, processName, simpleHash, sourceDstOceanShore, within } from "../Util";
import { CellString, GameImpl } from "./GameImpl";
@@ -200,19 +200,33 @@ export class PlayerImpl implements MutablePlayer {
return this.gs.createAllianceRequest(this, recipient as MutablePlayer)
}
relation(other: Player): number {
relation(other: Player): Relation {
if (other == this) {
throw new Error(`cannot get relation with self: ${this}`)
}
if (this.relations.has(other)) {
return this.relations.get(other)
return this.relationFromValue(this.relations.get(other))
}
return 0
return Relation.Neutral
}
allRelationsSorted(): { player: Player, relation: number }[] {
private relationFromValue(relationValue: number): Relation {
if (relationValue < -50) {
return Relation.Hostile
}
if (relationValue < 0) {
return Relation.Distrustful
}
if (relationValue < 50) {
return Relation.Neutral
}
return Relation.Friendly
}
allRelationsSorted(): { player: Player, relation: Relation }[] {
return Array.from(this.relations, ([k, v]) => ({ player: k, relation: v }))
.sort((a, b) => a.relation - b.relation)
.map(r => ({ player: r.player, relation: this.relationFromValue(r.relation) }))
}
updateRelation(other: Player, delta: number): void {
@@ -223,18 +237,16 @@ export class PlayerImpl implements MutablePlayer {
if (this.relations.has(other)) {
relation = this.relations.get(other)
}
const newRelation = within(relation + delta, -10000, 10000)
const newRelation = within(relation + delta, -100, 100)
this.relations.set(other, newRelation)
}
decayRelations() {
this.relations.forEach((r: number, p: Player) => {
// Have relationships decay over time
if (r > 1) {
r -= 1
} else if (r < -1) {
r += 1
} else {
const sign = -1 * Math.sign(r)
const delta = .05
r += sign * delta
if (Math.abs(r) < delta * 2) {
r = 0
}
this.relations.set(p, r)