mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:50:43 +00:00
Removed contest speed tracking and related properties from TerritoryLayer and TerritoryWebGLRenderer to simplify contest handling.
- Updated contest strength calculations to utilize troop counts directly, enhancing accuracy in contest dynamics. - Streamlined rendering logic by eliminating unnecessary checks and textures related to contest speeds, improving performance and clarity. - Refactored related methods to focus on contest strength, ensuring a more cohesive approach to contest state management.
This commit is contained in:
@@ -21,10 +21,6 @@ const CONTEST_ID_MASK = 0x7fff;
|
||||
const CONTEST_ATTACKER_EVER_BIT = 0x8000;
|
||||
const CONTEST_TIME_WRAP = 32768;
|
||||
const DEFAULT_CONTEST_DURATION_TICKS = 2;
|
||||
const CONTEST_SPEED_TPS_MAX = 20;
|
||||
const CONTEST_SPEED_EMA_ALPHA = 0.8;
|
||||
const CONTEST_SPEED_DECAY_HALFLIFE_MS = 100;
|
||||
const CONTEST_SPEED_DT_MAX_MS = 200;
|
||||
const CONTEST_STRENGTH_EMA_ALPHA = 0.8;
|
||||
const CONTEST_STRENGTH_MIN = 0.01;
|
||||
const CONTEST_STRENGTH_MAX = 0.95;
|
||||
@@ -36,7 +32,6 @@ type ContestComponent = {
|
||||
defender: number;
|
||||
lastActivityPacked: number;
|
||||
tiles: TileRef[];
|
||||
speed: number;
|
||||
strength: number;
|
||||
};
|
||||
|
||||
@@ -83,8 +78,6 @@ export class TerritoryLayer implements Layer {
|
||||
private tickNumberOlder: number | null = null;
|
||||
private interpolationDelayMs = 100;
|
||||
private lastInterpolationPair: "prevCurrent" | "olderPrev" = "prevCurrent";
|
||||
private contestSpeedDeltas = new Map<number, number>();
|
||||
private contestSpeedLastUpdateMs = 0;
|
||||
|
||||
constructor(
|
||||
private game: GameView,
|
||||
@@ -136,11 +129,9 @@ export class TerritoryLayer implements Layer {
|
||||
|
||||
this.game.recentlyUpdatedTiles().forEach((t) => this.markTile(t));
|
||||
const ownerUpdates = this.game.recentlyUpdatedOwnerTiles();
|
||||
this.contestSpeedDeltas.clear();
|
||||
const nowTickPacked = this.packContestTick(this.game.ticks());
|
||||
this.applyContestChanges(ownerUpdates, nowTickPacked);
|
||||
this.updateContestState(nowTickPacked);
|
||||
this.updateContestSpeeds(now);
|
||||
this.updateContestStrengths();
|
||||
const updates = this.game.updatesSinceLastTick();
|
||||
|
||||
@@ -629,11 +620,6 @@ export class TerritoryLayer implements Layer {
|
||||
this.territoryRenderer.setSmoothEnabled(true);
|
||||
}
|
||||
|
||||
private recordContestSpeed(componentId: number) {
|
||||
const current = this.contestSpeedDeltas.get(componentId) ?? 0;
|
||||
this.contestSpeedDeltas.set(componentId, current + 1);
|
||||
}
|
||||
|
||||
private applyContestChanges(
|
||||
changes: Array<{ tile: TileRef; previousOwner: number; newOwner: number }>,
|
||||
nowTickPacked: number,
|
||||
@@ -650,30 +636,24 @@ export class TerritoryLayer implements Layer {
|
||||
const tile = change.tile;
|
||||
const currentId = this.contestId(tile);
|
||||
if (currentId === 0) {
|
||||
const component = this.startContestForTile(
|
||||
this.startContestForTile(
|
||||
tile,
|
||||
change.previousOwner,
|
||||
change.newOwner,
|
||||
nowTickPacked,
|
||||
);
|
||||
if (component) {
|
||||
this.recordContestSpeed(component.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const component = this.contestComponents.get(currentId);
|
||||
if (!component) {
|
||||
this.clearContestTile(tile);
|
||||
const newComponent = this.startContestForTile(
|
||||
this.startContestForTile(
|
||||
tile,
|
||||
change.previousOwner,
|
||||
change.newOwner,
|
||||
nowTickPacked,
|
||||
);
|
||||
if (newComponent) {
|
||||
this.recordContestSpeed(newComponent.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -692,57 +672,18 @@ export class TerritoryLayer implements Layer {
|
||||
);
|
||||
component.lastActivityPacked = nowTickPacked;
|
||||
this.territoryRenderer.setContestTime(component.id, nowTickPacked);
|
||||
this.recordContestSpeed(component.id);
|
||||
} else {
|
||||
this.removeTileFromComponent(tile, component);
|
||||
const newComponent = this.startContestForTile(
|
||||
this.startContestForTile(
|
||||
tile,
|
||||
change.previousOwner,
|
||||
change.newOwner,
|
||||
nowTickPacked,
|
||||
);
|
||||
if (newComponent) {
|
||||
this.recordContestSpeed(newComponent.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateContestSpeeds(now: number) {
|
||||
if (!this.territoryRenderer) {
|
||||
return;
|
||||
}
|
||||
if (this.contestComponents.size === 0) {
|
||||
this.contestSpeedLastUpdateMs = now;
|
||||
return;
|
||||
}
|
||||
if (this.contestSpeedLastUpdateMs <= 0) {
|
||||
this.contestSpeedLastUpdateMs = now;
|
||||
}
|
||||
let dt = now - this.contestSpeedLastUpdateMs;
|
||||
dt = Math.max(1, Math.min(CONTEST_SPEED_DT_MAX_MS, dt));
|
||||
this.contestSpeedLastUpdateMs = now;
|
||||
|
||||
const decay = Math.pow(0.5, dt / CONTEST_SPEED_DECAY_HALFLIFE_MS);
|
||||
for (const component of this.contestComponents.values()) {
|
||||
const delta = this.contestSpeedDeltas.get(component.id) ?? 0;
|
||||
if (delta > 0) {
|
||||
const tilesPerSecond = (delta / dt) * 1000;
|
||||
const instant = Math.min(
|
||||
1,
|
||||
tilesPerSecond / CONTEST_SPEED_TPS_MAX,
|
||||
);
|
||||
component.speed =
|
||||
component.speed * (1 - CONTEST_SPEED_EMA_ALPHA) +
|
||||
instant * CONTEST_SPEED_EMA_ALPHA;
|
||||
} else {
|
||||
component.speed *= decay;
|
||||
}
|
||||
component.speed = Math.max(0, Math.min(1, component.speed));
|
||||
this.territoryRenderer.setContestSpeed(component.id, component.speed);
|
||||
}
|
||||
}
|
||||
|
||||
private updateContestStrengths() {
|
||||
if (!this.territoryRenderer) {
|
||||
return;
|
||||
@@ -750,14 +691,25 @@ export class TerritoryLayer implements Layer {
|
||||
if (this.contestComponents.size === 0) {
|
||||
return;
|
||||
}
|
||||
const pairStrength = new Map<string, number>();
|
||||
|
||||
const involvedIds = new Set<number>();
|
||||
for (const component of this.contestComponents.values()) {
|
||||
const key = `${component.attacker}:${component.defender}`;
|
||||
involvedIds.add(component.attacker);
|
||||
involvedIds.add(component.defender);
|
||||
}
|
||||
const totalTroopsById = this.buildTotalTroopsLookup(involvedIds);
|
||||
const attackTroopsById = this.buildAttackTroopsLookup(involvedIds);
|
||||
|
||||
const pairStrength = new Map<number, number>();
|
||||
for (const component of this.contestComponents.values()) {
|
||||
const key = (component.attacker << 16) | component.defender;
|
||||
let strength = pairStrength.get(key);
|
||||
if (strength === undefined) {
|
||||
strength = this.computeContestStrength(
|
||||
component.attacker,
|
||||
component.defender,
|
||||
totalTroopsById,
|
||||
attackTroopsById,
|
||||
);
|
||||
pairStrength.set(key, strength);
|
||||
}
|
||||
@@ -775,21 +727,68 @@ export class TerritoryLayer implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
private computeContestStrength(attackerId: number, defenderId: number) {
|
||||
const attacker = this.game.playerBySmallID(attackerId);
|
||||
const defender = this.game.playerBySmallID(defenderId);
|
||||
if (
|
||||
!attacker ||
|
||||
!defender ||
|
||||
!(attacker instanceof PlayerView) ||
|
||||
!(defender instanceof PlayerView)
|
||||
) {
|
||||
private buildTotalTroopsLookup(
|
||||
involvedIds: Set<number>,
|
||||
): Map<number, number> {
|
||||
const totals = new Map<number, number>();
|
||||
for (const id of involvedIds) {
|
||||
const player = this.game.playerBySmallID(id);
|
||||
if (player instanceof PlayerView) {
|
||||
totals.set(id, player.troops());
|
||||
}
|
||||
}
|
||||
return totals;
|
||||
}
|
||||
|
||||
private buildAttackTroopsLookup(
|
||||
involvedIds: Set<number>,
|
||||
): Map<number, Map<number, number>> {
|
||||
const totals = new Map<number, Map<number, number>>();
|
||||
for (const id of involvedIds) {
|
||||
const player = this.game.playerBySmallID(id);
|
||||
if (!(player instanceof PlayerView)) {
|
||||
continue;
|
||||
}
|
||||
const outgoing = player.outgoingAttacks();
|
||||
if (outgoing.length === 0) {
|
||||
continue;
|
||||
}
|
||||
for (const attack of outgoing) {
|
||||
if (!involvedIds.has(attack.targetID)) {
|
||||
continue;
|
||||
}
|
||||
let byTarget = totals.get(id);
|
||||
if (!byTarget) {
|
||||
byTarget = new Map<number, number>();
|
||||
totals.set(id, byTarget);
|
||||
}
|
||||
byTarget.set(
|
||||
attack.targetID,
|
||||
(byTarget.get(attack.targetID) ?? 0) + attack.troops,
|
||||
);
|
||||
}
|
||||
}
|
||||
return totals;
|
||||
}
|
||||
|
||||
private computeContestStrength(
|
||||
attackerId: number,
|
||||
defenderId: number,
|
||||
totalTroopsById: Map<number, number>,
|
||||
attackTroopsById: Map<number, Map<number, number>>,
|
||||
) {
|
||||
const attackerTroops = totalTroopsById.get(attackerId);
|
||||
const defenderTroops = totalTroopsById.get(defenderId);
|
||||
if (attackerTroops === undefined || defenderTroops === undefined) {
|
||||
return 0.5;
|
||||
}
|
||||
const attackerAttackTroops = this.attackTroops(attacker, defenderId);
|
||||
const defenderAttackTroops = this.attackTroops(defender, attackerId);
|
||||
const attackerPower = attacker.troops() + attackerAttackTroops;
|
||||
const defenderPower = defender.troops() + defenderAttackTroops;
|
||||
|
||||
const attackerAttackTroops =
|
||||
attackTroopsById.get(attackerId)?.get(defenderId) ?? 0;
|
||||
const defenderAttackTroops =
|
||||
attackTroopsById.get(defenderId)?.get(attackerId) ?? 0;
|
||||
const attackerPower = attackerTroops + attackerAttackTroops;
|
||||
const defenderPower = defenderTroops + defenderAttackTroops;
|
||||
const totalPower = attackerPower + defenderPower;
|
||||
if (totalPower <= 0) {
|
||||
return 0.5;
|
||||
@@ -797,16 +796,6 @@ export class TerritoryLayer implements Layer {
|
||||
return Math.max(0, Math.min(1, attackerPower / totalPower));
|
||||
}
|
||||
|
||||
private attackTroops(attacker: PlayerView, targetId: number) {
|
||||
let total = 0;
|
||||
for (const attack of attacker.outgoingAttacks()) {
|
||||
if (attack.targetID === targetId) {
|
||||
total += attack.troops;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private updateContestState(nowTickPacked: number) {
|
||||
if (!this.territoryRenderer) {
|
||||
return;
|
||||
@@ -849,7 +838,11 @@ export class TerritoryLayer implements Layer {
|
||||
const neighbors = this.collectNeighborComponents(tile, attacker, defender);
|
||||
let component: ContestComponent;
|
||||
if (neighbors.length === 0) {
|
||||
component = this.createContestComponent(attacker, defender, nowTickPacked);
|
||||
component = this.createContestComponent(
|
||||
attacker,
|
||||
defender,
|
||||
nowTickPacked,
|
||||
);
|
||||
} else {
|
||||
component = neighbors[0];
|
||||
for (let i = 1; i < neighbors.length; i++) {
|
||||
@@ -899,13 +892,11 @@ export class TerritoryLayer implements Layer {
|
||||
defender,
|
||||
lastActivityPacked: nowTickPacked,
|
||||
tiles: [],
|
||||
speed: 0,
|
||||
strength: 0.5,
|
||||
};
|
||||
this.contestComponents.set(id, component);
|
||||
this.contestActive = true;
|
||||
this.territoryRenderer?.ensureContestTimeCapacity(id);
|
||||
this.territoryRenderer?.setContestSpeed(id, 0);
|
||||
this.territoryRenderer?.setContestStrength(id, 0.5);
|
||||
return component;
|
||||
}
|
||||
@@ -957,7 +948,6 @@ export class TerritoryLayer implements Layer {
|
||||
this.contestTileIndices![tile] = -1;
|
||||
this.clearContestTile(tile);
|
||||
if (component.tiles.length === 0) {
|
||||
this.territoryRenderer?.setContestSpeed(component.id, 0);
|
||||
this.territoryRenderer?.setContestStrength(component.id, 0);
|
||||
this.contestComponents.delete(component.id);
|
||||
this.releaseContestComponentId(component.id);
|
||||
@@ -973,10 +963,6 @@ export class TerritoryLayer implements Layer {
|
||||
const sourceSize = source.tiles.length;
|
||||
const totalSize = targetSize + sourceSize;
|
||||
if (totalSize > 0) {
|
||||
target.speed = Math.min(
|
||||
1,
|
||||
(target.speed * targetSize + source.speed * sourceSize) / totalSize,
|
||||
);
|
||||
target.strength = Math.min(
|
||||
1,
|
||||
(target.strength * targetSize + source.strength * sourceSize) /
|
||||
@@ -1004,7 +990,6 @@ export class TerritoryLayer implements Layer {
|
||||
target.lastActivityPacked,
|
||||
);
|
||||
this.contestComponents.delete(source.id);
|
||||
this.territoryRenderer?.setContestSpeed(source.id, 0);
|
||||
this.territoryRenderer?.setContestStrength(source.id, 0);
|
||||
this.releaseContestComponentId(source.id);
|
||||
}
|
||||
@@ -1015,7 +1000,6 @@ export class TerritoryLayer implements Layer {
|
||||
this.clearContestTile(tile);
|
||||
}
|
||||
component.tiles.length = 0;
|
||||
this.territoryRenderer?.setContestSpeed(component.id, 0);
|
||||
this.territoryRenderer?.setContestStrength(component.id, 0);
|
||||
this.contestComponents.delete(component.id);
|
||||
this.releaseContestComponentId(component.id);
|
||||
@@ -1083,7 +1067,6 @@ export class TerritoryLayer implements Layer {
|
||||
}
|
||||
if (maxId > 0) {
|
||||
this.territoryRenderer.ensureContestTimeCapacity(maxId);
|
||||
this.territoryRenderer.ensureContestSpeedCapacity(maxId);
|
||||
this.territoryRenderer.ensureContestStrengthCapacity(maxId);
|
||||
}
|
||||
for (const component of this.contestComponents.values()) {
|
||||
@@ -1091,7 +1074,6 @@ export class TerritoryLayer implements Layer {
|
||||
component.id,
|
||||
component.lastActivityPacked,
|
||||
);
|
||||
this.territoryRenderer.setContestSpeed(component.id, component.speed);
|
||||
this.territoryRenderer.setContestStrength(
|
||||
component.id,
|
||||
component.strength,
|
||||
|
||||
@@ -40,7 +40,6 @@ export class TerritoryWebGLRenderer {
|
||||
private readonly contestOwnersTexture: WebGLTexture | null;
|
||||
private readonly contestIdsTexture: WebGLTexture | null;
|
||||
private readonly contestTimesTexture: WebGLTexture | null;
|
||||
private readonly contestSpeedsTexture: WebGLTexture | null;
|
||||
private readonly contestStrengthsTexture: WebGLTexture | null;
|
||||
private readonly prevOwnerTexture: WebGLTexture | null;
|
||||
private readonly olderOwnerTexture: WebGLTexture | null;
|
||||
@@ -93,7 +92,6 @@ export class TerritoryWebGLRenderer {
|
||||
contestOwners: WebGLUniformLocation | null;
|
||||
contestIds: WebGLUniformLocation | null;
|
||||
contestTimes: WebGLUniformLocation | null;
|
||||
contestSpeeds: WebGLUniformLocation | null;
|
||||
contestStrengths: WebGLUniformLocation | null;
|
||||
jfaAvailable: WebGLUniformLocation | null;
|
||||
contestNow: WebGLUniformLocation | null;
|
||||
@@ -134,14 +132,12 @@ export class TerritoryWebGLRenderer {
|
||||
private contestOwnersState: Uint16Array;
|
||||
private contestIdsState: Uint16Array;
|
||||
private contestTimesState: Uint16Array;
|
||||
private contestSpeedsState: Uint16Array;
|
||||
private contestStrengthsState: Uint16Array;
|
||||
private readonly dirtyRows: Map<number, DirtySpan> = new Map();
|
||||
private readonly contestDirtyRows: Map<number, DirtySpan> = new Map();
|
||||
private needsFullUpload = true;
|
||||
private needsContestFullUpload = true;
|
||||
private needsContestTimesUpload = true;
|
||||
private needsContestSpeedsUpload = true;
|
||||
private needsContestStrengthsUpload = true;
|
||||
private alternativeView = false;
|
||||
private paletteWidth = 0;
|
||||
@@ -188,7 +184,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.contestOwnersState = new Uint16Array(state.length * 2);
|
||||
this.contestIdsState = new Uint16Array(state.length);
|
||||
this.contestTimesState = new Uint16Array(1);
|
||||
this.contestSpeedsState = new Uint16Array(1);
|
||||
this.contestStrengthsState = new Uint16Array(1);
|
||||
|
||||
this.gl = this.canvas.getContext("webgl2", {
|
||||
@@ -210,7 +205,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.contestOwnersTexture = null;
|
||||
this.contestIdsTexture = null;
|
||||
this.contestTimesTexture = null;
|
||||
this.contestSpeedsTexture = null;
|
||||
this.contestStrengthsTexture = null;
|
||||
this.prevOwnerTexture = null;
|
||||
this.olderOwnerTexture = null;
|
||||
@@ -256,7 +250,6 @@ export class TerritoryWebGLRenderer {
|
||||
contestOwners: null,
|
||||
contestIds: null,
|
||||
contestTimes: null,
|
||||
contestSpeeds: null,
|
||||
contestStrengths: null,
|
||||
jfaAvailable: null,
|
||||
contestNow: null,
|
||||
@@ -301,7 +294,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.contestOwnersTexture = null;
|
||||
this.contestIdsTexture = null;
|
||||
this.contestTimesTexture = null;
|
||||
this.contestSpeedsTexture = null;
|
||||
this.contestStrengthsTexture = null;
|
||||
this.prevOwnerTexture = null;
|
||||
this.olderOwnerTexture = null;
|
||||
@@ -347,7 +339,6 @@ export class TerritoryWebGLRenderer {
|
||||
contestOwners: null,
|
||||
contestIds: null,
|
||||
contestTimes: null,
|
||||
contestSpeeds: null,
|
||||
contestStrengths: null,
|
||||
jfaAvailable: null,
|
||||
contestNow: null,
|
||||
@@ -439,7 +430,6 @@ export class TerritoryWebGLRenderer {
|
||||
contestOwners: gl.getUniformLocation(this.program, "u_contestOwners"),
|
||||
contestIds: gl.getUniformLocation(this.program, "u_contestIds"),
|
||||
contestTimes: gl.getUniformLocation(this.program, "u_contestTimes"),
|
||||
contestSpeeds: gl.getUniformLocation(this.program, "u_contestSpeeds"),
|
||||
contestStrengths: gl.getUniformLocation(
|
||||
this.program,
|
||||
"u_contestStrengths",
|
||||
@@ -540,7 +530,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.contestOwnersTexture = gl.createTexture();
|
||||
this.contestIdsTexture = gl.createTexture();
|
||||
this.contestTimesTexture = gl.createTexture();
|
||||
this.contestSpeedsTexture = gl.createTexture();
|
||||
this.contestStrengthsTexture = gl.createTexture();
|
||||
this.prevOwnerTexture = gl.createTexture();
|
||||
this.olderOwnerTexture = gl.createTexture();
|
||||
@@ -654,25 +643,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.contestTimesState,
|
||||
);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE10);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.contestSpeedsTexture);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||
gl.texImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.R16UI,
|
||||
this.contestSpeedsState.length,
|
||||
1,
|
||||
0,
|
||||
gl.RED_INTEGER,
|
||||
gl.UNSIGNED_SHORT,
|
||||
this.contestSpeedsState,
|
||||
);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE11);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.contestStrengthsTexture);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
@@ -998,7 +968,6 @@ export class TerritoryWebGLRenderer {
|
||||
gl.uniform1i(this.uniforms.contestOwners, 4);
|
||||
gl.uniform1i(this.uniforms.contestIds, 5);
|
||||
gl.uniform1i(this.uniforms.contestTimes, 6);
|
||||
gl.uniform1i(this.uniforms.contestSpeeds, 10);
|
||||
gl.uniform1i(this.uniforms.contestStrengths, 11);
|
||||
gl.uniform1i(this.uniforms.prevOwner, 7);
|
||||
gl.uniform1i(this.uniforms.jfaSeedsOld, 8);
|
||||
@@ -1336,34 +1305,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.needsContestTimesUpload = true;
|
||||
}
|
||||
|
||||
setContestSpeed(componentId: number, speed: number) {
|
||||
if (componentId <= 0) {
|
||||
return;
|
||||
}
|
||||
this.ensureContestSpeedCapacity(componentId);
|
||||
const clamped = Math.max(0, Math.min(1, speed));
|
||||
const packed = Math.round(clamped * 65535) & 0xffff;
|
||||
if (this.contestSpeedsState[componentId] === packed) {
|
||||
return;
|
||||
}
|
||||
this.contestSpeedsState[componentId] = packed;
|
||||
this.needsContestSpeedsUpload = true;
|
||||
}
|
||||
|
||||
ensureContestSpeedCapacity(componentId: number) {
|
||||
if (componentId < this.contestSpeedsState.length) {
|
||||
return;
|
||||
}
|
||||
let nextLength = Math.max(1, this.contestSpeedsState.length);
|
||||
while (nextLength <= componentId) {
|
||||
nextLength *= 2;
|
||||
}
|
||||
const nextState = new Uint16Array(nextLength);
|
||||
nextState.set(this.contestSpeedsState);
|
||||
this.contestSpeedsState = nextState;
|
||||
this.needsContestSpeedsUpload = true;
|
||||
}
|
||||
|
||||
setContestStrength(componentId: number, strength: number) {
|
||||
if (componentId <= 0) {
|
||||
return;
|
||||
@@ -1544,7 +1485,6 @@ export class TerritoryWebGLRenderer {
|
||||
this.dirtyRows.clear();
|
||||
this.needsContestFullUpload = true;
|
||||
this.needsContestTimesUpload = true;
|
||||
this.needsContestSpeedsUpload = true;
|
||||
this.needsContestStrengthsUpload = true;
|
||||
this.contestDirtyRows.clear();
|
||||
this.jfaDirty = true;
|
||||
@@ -1582,13 +1522,6 @@ export class TerritoryWebGLRenderer {
|
||||
uploadContestTimesSpan,
|
||||
);
|
||||
|
||||
const uploadContestSpeedsSpan = FrameProfiler.start();
|
||||
this.uploadContestSpeedsTexture();
|
||||
FrameProfiler.end(
|
||||
"TerritoryWebGLRenderer:uploadContestSpeeds",
|
||||
uploadContestSpeedsSpan,
|
||||
);
|
||||
|
||||
const uploadContestStrengthsSpan = FrameProfiler.start();
|
||||
this.uploadContestStrengthsTexture();
|
||||
FrameProfiler.end(
|
||||
@@ -1684,10 +1617,6 @@ export class TerritoryWebGLRenderer {
|
||||
gl.activeTexture(gl.TEXTURE13);
|
||||
gl.bindTexture(gl.TEXTURE_2D, changeMaskTexture);
|
||||
}
|
||||
if (this.contestSpeedsTexture) {
|
||||
gl.activeTexture(gl.TEXTURE10);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.contestSpeedsTexture);
|
||||
}
|
||||
if (this.contestStrengthsTexture) {
|
||||
gl.activeTexture(gl.TEXTURE11);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.contestStrengthsTexture);
|
||||
@@ -1960,34 +1889,6 @@ export class TerritoryWebGLRenderer {
|
||||
return { rows: 1, bytes };
|
||||
}
|
||||
|
||||
private uploadContestSpeedsTexture(): { rows: number; bytes: number } {
|
||||
if (!this.gl || !this.contestSpeedsTexture) {
|
||||
return { rows: 0, bytes: 0 };
|
||||
}
|
||||
if (!this.needsContestSpeedsUpload) {
|
||||
return { rows: 0, bytes: 0 };
|
||||
}
|
||||
const gl = this.gl;
|
||||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||
gl.activeTexture(gl.TEXTURE10);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.contestSpeedsTexture);
|
||||
gl.texImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.R16UI,
|
||||
this.contestSpeedsState.length,
|
||||
1,
|
||||
0,
|
||||
gl.RED_INTEGER,
|
||||
gl.UNSIGNED_SHORT,
|
||||
this.contestSpeedsState,
|
||||
);
|
||||
this.needsContestSpeedsUpload = false;
|
||||
const bytes =
|
||||
this.contestSpeedsState.length * Uint16Array.BYTES_PER_ELEMENT;
|
||||
return { rows: 1, bytes };
|
||||
}
|
||||
|
||||
private uploadContestStrengthsTexture(): { rows: number; bytes: number } {
|
||||
if (!this.gl || !this.contestStrengthsTexture) {
|
||||
return { rows: 0, bytes: 0 };
|
||||
@@ -2712,7 +2613,6 @@ export class TerritoryWebGLRenderer {
|
||||
uniform usampler2D u_contestOwners;
|
||||
uniform usampler2D u_contestIds;
|
||||
uniform usampler2D u_contestTimes;
|
||||
uniform usampler2D u_contestSpeeds;
|
||||
uniform usampler2D u_contestStrengths;
|
||||
uniform bool u_jfaAvailable;
|
||||
uniform int u_contestNow;
|
||||
@@ -2800,18 +2700,6 @@ export class TerritoryWebGLRenderer {
|
||||
return texelFetch(u_contestIds, clamped, 0).r;
|
||||
}
|
||||
|
||||
float contestSpeed(uint contestId) {
|
||||
if (contestId == 0u) {
|
||||
return 0.0;
|
||||
}
|
||||
uint speedRaw = texelFetch(
|
||||
u_contestSpeeds,
|
||||
ivec2(int(contestId), 0),
|
||||
0
|
||||
).r;
|
||||
return clamp(float(speedRaw) / 65535.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float contestStrength(uint contestId) {
|
||||
if (contestId == 0u) {
|
||||
return 0.5;
|
||||
@@ -3212,18 +3100,18 @@ export class TerritoryWebGLRenderer {
|
||||
if (seedOld.x >= 0.0 && seedNew.x >= 0.0) {
|
||||
float oldDistance = length(seedOld - vec2(texCoord));
|
||||
float newDistance = length(seedNew - vec2(texCoord));
|
||||
float bandWidth = mix(1.6, 0.8, contestSpeed(contestId));
|
||||
float battle = clamp(abs(contestStrength(contestId) - 0.5) * 2.0, 0.0, 1.0);
|
||||
float bandWidth = mix(1.6, 0.9, battle);
|
||||
float frontDistance = min(oldDistance, newDistance);
|
||||
float band =
|
||||
1.0 - smoothstep(bandWidth, bandWidth + 0.6, frontDistance);
|
||||
float speed = contestSpeed(contestId);
|
||||
float scale = mix(0.1, 0.22, speed);
|
||||
float drift = mix(0.05, 0.18, speed);
|
||||
float scale = mix(0.1, 0.2, battle);
|
||||
float drift = mix(0.05, 0.14, battle);
|
||||
vec2 p = vec2(texCoord) * scale +
|
||||
vec2(u_time * drift, -u_time * drift * 0.6);
|
||||
float n = fbm(p);
|
||||
float cloud = smoothstep(0.55, 0.82, n);
|
||||
float intensity = mix(0.08, 0.28, speed);
|
||||
float intensity = mix(0.06, 0.22, battle);
|
||||
float alpha = cloud * band * intensity;
|
||||
vec3 smoke = vec3(0.85, 0.83, 0.8);
|
||||
color = mix(color, smoke, alpha);
|
||||
|
||||
Reference in New Issue
Block a user