From d8982ead593e2af9334c9853c9aee203610e91e1 Mon Sep 17 00:00:00 2001 From: Wizzard <rich@bandaholics.cash> Date: Wed, 9 Apr 2025 17:17:37 -0400 Subject: [PATCH] Track dead players and remember selected --- webradar/index.html | 2 +- webradar/script.js | 243 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 214 insertions(+), 31 deletions(-) diff --git a/webradar/index.html b/webradar/index.html index 432372e..9a32c56 100644 --- a/webradar/index.html +++ b/webradar/index.html @@ -4,7 +4,7 @@ <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>radarflow</title> + <title>radar</title> <link href="styles.css" rel="stylesheet" type="text/css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js"></script> </head> diff --git a/webradar/script.js b/webradar/script.js index b7affbb..fec70a4 100644 --- a/webradar/script.js +++ b/webradar/script.js @@ -51,15 +51,21 @@ let currentFps = 0; let temporarilyDisableRotation = false; let rotationDisabledUntilRespawn = false; let lastKnownPositions = {}; +let deadPlayers = {}; let entityInterpolationData = {}; let lastUpdateTime = 0; let networkLatencyHistory = []; let lastPingSent = 0; +// Special values for spectator mode +const FULL_MAP_VIEW = "FULL_MAP"; +const LOCAL_PLAYER_VIEW = "YOU"; + let focusedPlayerYaw = 0; -let focusedPlayerName = "YOU"; +let focusedPlayerName = localStorage.getItem('selectedPlayer') || LOCAL_PLAYER_VIEW; let focusedPlayerPos = null; let playerList = {}; +let lastFocusedPlayer = localStorage.getItem('lastFocusedPlayer') || null; // Common let canvas = null; @@ -323,7 +329,8 @@ function processPlayerPositions() { focusedPlayerPos = null; focusedPlayerYaw = 0; let oldPlayerList = { ...playerList }; - playerList = {}; + let newPlayerList = {}; + let wasPlayerAlive = oldPlayerList[focusedPlayerName] && !oldPlayerList[focusedPlayerName].isDead; entityData.forEach((data, index) => { const entityId = `entity_${index}`; @@ -337,23 +344,25 @@ function processPlayerPositions() { if (player.playerType === "Local") { localYaw = player.yaw; localPlayerPos = player.pos; - playerList["YOU"] = { + newPlayerList[LOCAL_PLAYER_VIEW] = { pos: player.pos, yaw: player.yaw }; - lastKnownPositions["YOU"] = player.pos; + lastKnownPositions[LOCAL_PLAYER_VIEW] = player.pos; + if (deadPlayers[LOCAL_PLAYER_VIEW]) delete deadPlayers[LOCAL_PLAYER_VIEW]; } else { - playerList[player.playerName] = { + newPlayerList[player.playerName] = { pos: player.pos, yaw: player.yaw }; lastKnownPositions[player.playerName] = player.pos; + if (deadPlayers[player.playerName]) delete deadPlayers[player.playerName]; } if (player.playerName === focusedPlayerName || - (focusedPlayerName === "YOU" && player.playerType === "Local")) { + (focusedPlayerName === LOCAL_PLAYER_VIEW && player.playerType === "Local")) { focusedPlayerPos = player.pos; focusedPlayerYaw = player.yaw; @@ -365,10 +374,91 @@ function processPlayerPositions() { } }); - if (focusedPlayerPos === null) { - if (oldPlayerList[focusedPlayerName] && oldPlayerList[focusedPlayerName].pos) { - console.log("[radarflow] Focused player disappeared, disabling rotation until respawn"); - rotationDisabledUntilRespawn = true; + for (const playerName in oldPlayerList) { + if (!newPlayerList[playerName] && playerName !== FULL_MAP_VIEW) { + deadPlayers[playerName] = { + pos: lastKnownPositions[playerName] || null, + yaw: oldPlayerList[playerName].yaw || 0, + lastSeen: Date.now() + }; + + if (playerName === focusedPlayerName && wasPlayerAlive) { + console.log(`[radarflow] Focused player ${playerName} died, switching to full map view`); + localStorage.setItem('lastFocusedPlayer', playerName); + lastFocusedPlayer = playerName; + focusedPlayerName = FULL_MAP_VIEW; + localStorage.setItem('selectedPlayer', FULL_MAP_VIEW); + playerCentered = false; + update = true; + } + } + } + + for (const playerName in deadPlayers) { + if (!newPlayerList[playerName] && playerName !== FULL_MAP_VIEW) { + if (Date.now() - deadPlayers[playerName].lastSeen < 60000) { + newPlayerList[playerName] = { + pos: deadPlayers[playerName].pos, + yaw: deadPlayers[playerName].yaw, + isDead: true + }; + } + } + } + + newPlayerList[FULL_MAP_VIEW] = { + isFullMap: true + }; + + playerList = newPlayerList; + + if (focusedPlayerName === FULL_MAP_VIEW && lastFocusedPlayer) { + const playerIsBackAlive = newPlayerList[lastFocusedPlayer] && !newPlayerList[lastFocusedPlayer].isDead; + + if (playerIsBackAlive) { + console.log(`[radarflow] Last focused player ${lastFocusedPlayer} is back alive, reselecting them`); + focusedPlayerName = lastFocusedPlayer; + localStorage.setItem('selectedPlayer', lastFocusedPlayer); + + if (document.getElementById('centerCheck').checked) { + playerCentered = true; + } + + focusedPlayerPos = newPlayerList[lastFocusedPlayer].pos; + focusedPlayerYaw = newPlayerList[lastFocusedPlayer].yaw; + + rotationDisabledUntilRespawn = false; + + const dropdown = document.getElementById('playerSelect'); + if (dropdown) { + dropdown.value = lastFocusedPlayer; + } + + update = true; + } + } + + // Handle focused player state + if (focusedPlayerName === FULL_MAP_VIEW) { + playerCentered = false; + } else if (focusedPlayerPos === null) { + if (playerList[focusedPlayerName]?.isDead) { + console.log(`[radarflow] Focused player ${focusedPlayerName} is dead, switching to full map view`); + localStorage.setItem('lastFocusedPlayer', focusedPlayerName); + lastFocusedPlayer = focusedPlayerName; + focusedPlayerName = FULL_MAP_VIEW; + localStorage.setItem('selectedPlayer', FULL_MAP_VIEW); + playerCentered = false; + update = true; + } + else if (lastKnownPositions[focusedPlayerName]) { + console.log("[radarflow] Focused player disappeared, switching to full map view"); + localStorage.setItem('lastFocusedPlayer', focusedPlayerName); + lastFocusedPlayer = focusedPlayerName; + focusedPlayerName = FULL_MAP_VIEW; + localStorage.setItem('selectedPlayer', FULL_MAP_VIEW); + playerCentered = false; + update = true; } } } @@ -378,7 +468,12 @@ function drawImage() { ctx.save(); - if (playerCentered && focusedPlayerPos) { + if (focusedPlayerName === FULL_MAP_VIEW || !playerCentered) { + if (rotateMap && !temporarilyDisableRotation) { + ctx.translate(canvas.width / 2, canvas.height / 2); + ctx.translate(-canvas.width / 2, -canvas.height / 2); + } + } else if (playerCentered && focusedPlayerPos) { if (playerCenteredZoom !== 1.0) { ctx.translate(canvas.width / 2, canvas.height / 2); ctx.scale(playerCenteredZoom, playerCenteredZoom); @@ -527,6 +622,10 @@ function updateZoomLevel(value) { function toggleCentered() { playerCentered = !playerCentered; updateZoomSliderVisibility(); + if (focusedPlayerName === FULL_MAP_VIEW) { + playerCentered = false; + document.getElementById('centerCheck').checked = false; + } } function updateZoomSliderVisibility() { @@ -864,36 +963,99 @@ function updatePlayerDropdown() { if (!dropdown) return; const currentValue = dropdown.value; + const savedPlayer = localStorage.getItem('selectedPlayer'); - while (dropdown.options.length > 1) { - dropdown.remove(1); + while (dropdown.options.length > 0) { + dropdown.remove(0); } + const fullMapOption = document.createElement('option'); + fullMapOption.value = FULL_MAP_VIEW; + fullMapOption.textContent = "FULL MAP"; + dropdown.appendChild(fullMapOption); + + const localOption = document.createElement('option'); + localOption.value = "local"; + localOption.textContent = "YOU"; + dropdown.appendChild(localOption); + for (const playerName in playerList) { - if (playerName !== "YOU") { + if (playerName !== LOCAL_PLAYER_VIEW && playerName !== FULL_MAP_VIEW) { const option = document.createElement('option'); option.value = playerName; - option.textContent = playerName; + + if (playerList[playerName].isDead) { + option.textContent = `${playerName} (DEAD)`; + option.style.color = "#777777"; + } else { + option.textContent = playerName; + } + dropdown.appendChild(option); } } - if (Object.keys(playerList).includes(currentValue)) { + if (Object.keys(playerList).includes(currentValue) || currentValue === "local" || currentValue === FULL_MAP_VIEW) { dropdown.value = currentValue; - } else { - dropdown.value = "local"; - focusedPlayerName = "YOU"; - if (playerList["YOU"]) { - focusedPlayerPos = playerList["YOU"].pos; - focusedPlayerYaw = playerList["YOU"].yaw; + } + else if (savedPlayer === FULL_MAP_VIEW) { + dropdown.value = FULL_MAP_VIEW; + focusedPlayerName = FULL_MAP_VIEW; + playerCentered = false; + } + else if (savedPlayer && Object.keys(playerList).includes(savedPlayer)) { + dropdown.value = savedPlayer; + focusedPlayerName = savedPlayer; + + if (playerList[savedPlayer]?.isDead) { + dropdown.value = FULL_MAP_VIEW; + focusedPlayerName = FULL_MAP_VIEW; + localStorage.setItem('selectedPlayer', FULL_MAP_VIEW); + playerCentered = false; } + } + else { + dropdown.value = FULL_MAP_VIEW; + focusedPlayerName = FULL_MAP_VIEW; + localStorage.setItem('selectedPlayer', FULL_MAP_VIEW); + playerCentered = false; } } function changePlayerFocus() { const dropdown = document.getElementById('playerSelect'); - focusedPlayerName = dropdown.value === "local" ? "YOU" : dropdown.value; - rotationDisabledUntilRespawn = false; + + if (dropdown.value !== "local" && + dropdown.value !== FULL_MAP_VIEW && + playerList[dropdown.value]?.isDead) { + + console.log(`[radarflow] Attempted to focus on dead player ${dropdown.value}, switching to full map view`); + localStorage.setItem('lastFocusedPlayer', dropdown.value); + lastFocusedPlayer = dropdown.value; + dropdown.value = FULL_MAP_VIEW; + focusedPlayerName = FULL_MAP_VIEW; + playerCentered = false; + } + else if (dropdown.value === FULL_MAP_VIEW) { + focusedPlayerName = FULL_MAP_VIEW; + playerCentered = false; + } + else { + localStorage.removeItem('lastFocusedPlayer'); + lastFocusedPlayer = null; + focusedPlayerName = dropdown.value === "local" ? LOCAL_PLAYER_VIEW : dropdown.value; + + playerCentered = document.getElementById('centerCheck').checked; + + if (playerList[focusedPlayerName]) { + focusedPlayerPos = playerList[focusedPlayerName].pos; + focusedPlayerYaw = playerList[focusedPlayerName].yaw; + } + + rotationDisabledUntilRespawn = false; + } + + localStorage.setItem('selectedPlayer', focusedPlayerName); update = true; } @@ -1067,7 +1229,9 @@ function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, i const arrowWidth = 35; const isFocusedPlayer = playerName === focusedPlayerName || - (focusedPlayerName === "YOU" && playerType === "Local"); + (focusedPlayerName === LOCAL_PLAYER_VIEW && playerType === "Local"); + + const isDeadPlayer = playerList[playerName]?.isDead || false; let adjustedYaw = yaw; @@ -1083,11 +1247,30 @@ function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, i } } - if (dormant) { - ctx.font = `bold ${transformed.textSize}px Arial`; - ctx.textAlign = "center"; - ctx.fillStyle = fillStyle; - ctx.fillText("?", mapPos.x, mapPos.y); + if (dormant || isDeadPlayer) { + if (isDeadPlayer) { + ctx.beginPath(); + ctx.arc(mapPos.x, mapPos.y, circleRadius, 0, 2 * Math.PI); + ctx.fillStyle = "#777777"; + ctx.fill(); + ctx.closePath(); + + const xSize = circleRadius * 0.7; + ctx.beginPath(); + ctx.moveTo(mapPos.x - xSize, mapPos.y - xSize); + ctx.lineTo(mapPos.x + xSize, mapPos.y + xSize); + ctx.moveTo(mapPos.x + xSize, mapPos.y - xSize); + ctx.lineTo(mapPos.x - xSize, mapPos.y + xSize); + ctx.strokeStyle = "#000000"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.closePath(); + } else { + ctx.font = `bold ${transformed.textSize}px Arial`; + ctx.textAlign = "center"; + ctx.fillStyle = fillStyle; + ctx.fillText("?", mapPos.x, mapPos.y); + } } else { if (isFocusedPlayer) { ctx.beginPath();