Skip to content

Commit

Permalink
Adding player list and follow feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
kylepaulsen committed Apr 29, 2021
1 parent 01006d5 commit b0cd249
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 51 deletions.
8 changes: 4 additions & 4 deletions WebMap/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ public static string getWorldName() {
public static string str(int n) {
return n.ToString(culture);
}
public static string str(float n) {
return n.ToString(culture);
public static string str(float n, int precision = 2) {
return n.ToString("F" + precision, culture);
}
public static string str(double n) {
return n.ToString(culture);
public static string str(double n, int precision = 2) {
return n.ToString("F" + precision, culture);
}
public static string str(long n) {
return n.ToString(culture);
Expand Down
10 changes: 7 additions & 3 deletions WebMap/MapDataServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,19 @@ public MapDataServer() {

if (zdoData != null) {
var pos = zdoData.GetPosition();
var maxHealth = zdoData.GetFloat("max_health", 25f);
var health = zdoData.GetFloat("health", maxHealth);
maxHealth = Mathf.Max(maxHealth, health);

if (player.m_publicRefPos) {
dataString += $"{player.m_uid}\n{player.m_playerName}\n{str(pos.x)},{str(pos.y)},{str(pos.z)}\n";
dataString += $"{player.m_uid}\n{player.m_playerName}\n{str(pos.x)},{str(pos.y)},{str(pos.z)}\n{str(health)}\n{str(maxHealth)}\n\n";
} else {
dataString += $"{player.m_uid}\n{player.m_playerName}\nhidden\n";
dataString += $"{player.m_uid}\n{player.m_playerName}\nhidden\n\n";
}
}
});
if (dataString.Length > 0) {
webSocketHandler.Sessions.Broadcast("players\n" + dataString);
webSocketHandler.Sessions.Broadcast("players\n" + dataString.Trim());
}
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(WebMapConfig.PLAYER_UPDATE_INTERVAL));

Expand Down
21 changes: 17 additions & 4 deletions WebMap/web-src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ const fetchConfig = fetch('config').then(res => res.json()).then(config => {
constants.WORLD_START_POSITION = parseVector3(config.world_start_pos);
constants.DEFAULT_ZOOM = config.default_zoom || 200;
document.title = `Valheim WebMap - ${constants.WORLD_NAME}`;
createStyleSheet(`.mapIcon.player {
transition: top ${constants.UPDATE_INTERVAL}s linear, left ${constants.UPDATE_INTERVAL}s linear;
}`);
createStyleSheet(`
.mapIcon.player {
transition: top ${constants.UPDATE_INTERVAL}s linear, left ${constants.UPDATE_INTERVAL}s linear;
}
.map.smooth {
transition: top ${constants.UPDATE_INTERVAL}s linear, left ${constants.UPDATE_INTERVAL}s linear;
}
`);
});

const setup = async () => {
Expand Down Expand Up @@ -140,7 +145,7 @@ const setup = async () => {
window.addEventListener('mousedown', closeMenu);
window.addEventListener('touchstart', closeMenu);

const hideCheckboxes = ui.menu.querySelectorAll('.hideCheckbox');
const hideCheckboxes = ui.menu.querySelectorAll('.hideIconCheckbox');
hideCheckboxes.forEach(el => {
el.addEventListener('change', () => {
map.setIconHidden(el.dataset.hide, el.checked || ui.hideAll.checked);
Expand All @@ -152,6 +157,14 @@ const setup = async () => {
map.updateIcons();
});
});

ui.hidePlayerList.addEventListener('change', () => {
if (ui.hidePlayerList.checked) {
ui.playerListContainer.style.right = -ui.playerListContainer.offsetWidth + 'px';
} else {
ui.playerListContainer.style.right = 0;
}
});
};

setup();
41 changes: 40 additions & 1 deletion WebMap/web-src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ let currentZoom = 100;

const mapIcons = [];
const hiddenIcons = {};
let followIcon;

const createIconEl = (iconObj) => {
const iconEl = document.createElement('div');
Expand All @@ -44,11 +45,32 @@ const createIconEl = (iconObj) => {
iconEl.style.zIndex = iconObj.zIndex;
}
const iconTextEl = document.createElement('div');
iconTextEl.className = 'center text';
iconTextEl.textContent = iconObj.text;
iconEl.appendChild(iconTextEl);
if (iconObj.node) {
iconEl.appendChild(iconObj.node);
}
return iconEl;
};

const centerOnIcon = (iconObj) => {
const rect = iconObj.el.getBoundingClientRect();
const deltaX = window.innerWidth / 2 - rect.left;
const deltaY = window.innerHeight / 2 - rect.top;
if (Math.abs(deltaX) > 0.5 || Math.abs(deltaY) > 0.5) {
map.style.left = deltaX + map.offsetLeft + 'px';
map.style.top = deltaY + map.offsetTop + 'px';
}
};

const setFollowIcon = (iconObj) => {
followIcon = iconObj;
if (followIcon) {
centerOnIcon(followIcon);
}
};

const updateIcons = () => {
mapIcons.forEach(iconObj => {
let firstRender = false;
Expand All @@ -69,6 +91,10 @@ const updateIcons = () => {
iconObj.el.style.left = 100 * imgX / width + '%';
iconObj.el.style.top = 100 * imgY / height + '%';
});

if (followIcon) {
centerOnIcon(followIcon);
}
};

window.addEventListener('mousemove', e => {
Expand All @@ -94,6 +120,7 @@ const removeIcon = (iconObj) => {
mapIcons.splice(idx, 1);
if (iconObj.el) {
iconObj.el.remove();
iconObj.el = undefined;
}
}
};
Expand Down Expand Up @@ -149,6 +176,14 @@ const setZoom = function(zoomP, zoomTowardsX, zoomTowardsY) {
updateIcons();
};

let zoomingClassTimeout;
const removeZoomingClass = () => {
clearTimeout(zoomingClassTimeout);
zoomingClassTimeout = setTimeout(() => {
map.classList.remove('zooming');
}, 100);
};

const init = (options) => {
mapImage = options.mapImage;
fogImage = options.fogImage;
Expand All @@ -158,6 +193,7 @@ const init = (options) => {
redrawMap();

const zoomChange = (e, mult = 1) => {
map.classList.add('zooming');
const oldZoom = currentZoom;
const zoomAmount = Math.max(Math.floor(oldZoom / 5), 1) * mult;
const scrollAmt = e.deltaY === 0 ? e.deltaX : e.deltaY;
Expand All @@ -168,6 +204,7 @@ const init = (options) => {
// zoom in.
setZoom(oldZoom + zoomAmount, e.clientX, e.clientY);
}
removeZoomingClass();
};

if (options.zoom) {
Expand All @@ -193,7 +230,7 @@ const init = (options) => {
}
},
move: (pointers) => {
if (pointers.length === 1 && !isZooming) {
if (pointers.length === 1 && !isZooming && !followIcon) {
const e = pointers[0].event;
map.style.left = canvasPreDragPos.x + (e.clientX - pointers[0].downEvent.clientX) + 'px';
map.style.top = canvasPreDragPos.y + (e.clientY - pointers[0].downEvent.clientY) + 'px';
Expand Down Expand Up @@ -233,6 +270,8 @@ export default {
removeIconById,
setIconHidden,
explore,
centerOnIcon,
setFollowIcon,
update: redrawMap,
updateIcons,
canvas
Expand Down
95 changes: 89 additions & 6 deletions WebMap/web-src/players.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,103 @@
import ui, { createUi } from "./ui";
import websocket from "./websocket";
import map from "./map";

const playerMapIcons = {};
let followingPlayer;

const followPlayer = (playerMapIcon) => {
if (followingPlayer) {
followingPlayer.playerListEntry.el.classList.remove('selected');
}
if (playerMapIcon && playerMapIcon !== followingPlayer) {
followingPlayer = playerMapIcon;
followingPlayer.playerListEntry.el.classList.add('selected');
ui.map.classList.remove('smooth');
map.setFollowIcon(playerMapIcon);
ui.topMessage.textContent = `Following ${followingPlayer.name}`;
setTimeout(() => {
ui.map.classList.add('smooth');
}, 0);
} else {
followingPlayer = null;
map.setFollowIcon(null);
ui.map.classList.remove('smooth');
ui.topMessage.textContent = '';
}
};

const init = () => {
websocket.addActionListener('players', (players) => {
players.forEach((player) => {
let playerMapIcon = playerMapIcons[player.id];
if (player.hidden) {
return;
}
if (!playerMapIcon) {
playerMapIcon = { ...player, type: 'player', text: player.name, zIndex: 5 };
map.addIcon(playerMapIcon);
// new player
const playerListEntry = createUi(`
<div class="playerListEntry">
<div class="name" data-id="name"></div>
<div class="hpBar" data-id="hpBar">
<div class="hp" data-id="hp"></div>
<div class="hpText" data-id="hpText"></div>
</div>
</div>
`);
playerListEntry.ui.name.textContent = player.name;
ui.playerList.appendChild(playerListEntry.el);
playerMapIcon = {
...player,
type: 'player',
text: player.name,
zIndex: 5,
playerListEntry
};
if (!player.hidden) {
map.addIcon(playerMapIcon, false);
} else {
playerListEntry.ui.hpBar.style.display = 'none';
}
playerMapIcons[player.id] = playerMapIcon;
playerListEntry.el.addEventListener('click', () => {
if (!playerMapIcon.hidden) {
if (ui.playerListTut) {
ui.playerListTut.remove();
ui.playerListTut = undefined;
}
followPlayer(playerMapIcon);
}
});
}

if (!player.hidden && playerMapIcon.hidden) {
// no longer hidden
playerMapIcon.hidden = player.hidden;
map.addIcon(playerMapIcon, false);
playerMapIcon.playerListEntry.ui.hpBar.style.display = 'block';
} else if (player.hidden && !playerMapIcon.hidden) {
// becomming hidden
playerMapIcon.hidden = player.hidden;
map.removeIcon(playerMapIcon);
playerMapIcon.playerListEntry.ui.hpBar.style.display = 'none';
if (followingPlayer === playerMapIcon) {
followPlayer(null);
}
}

playerMapIcon.lastUpdate = Date.now();
playerMapIcon.x = player.x;
playerMapIcon.z = player.z;
map.explore(player.x, player.z);

if (!player.hidden) {
playerMapIcon.playerListEntry.ui.hp.style.width = `${
100 * Math.max(player.health / player.maxHealth, 0)
}%`;
playerMapIcon.playerListEntry.ui.hpText.textContent = `${
Math.round(Math.max(player.health, 0))
} / ${
Math.round(player.maxHealth)
}`;

map.explore(player.x, player.z);
}
});
map.updateIcons();
});
Expand All @@ -30,6 +109,10 @@ const init = () => {
const playerMapIcon = playerMapIcons[key];
if (now - playerMapIcon.lastUpdate > 5000) {
map.removeIcon(playerMapIcon);
if (playerMapIcon === followingPlayer) {
followPlayer(null);
}
playerMapIcon.playerListEntry.el.remove();
delete playerMapIcons[key];
}
});
Expand Down
16 changes: 16 additions & 0 deletions WebMap/web-src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,20 @@ allUi.forEach(el => {
ui[el.dataset.id] = el;
});

const tempDiv = document.createElement('div');
export const createUi = (html) => {
tempDiv.innerHTML = html;

const uiEls = {};
const dataEls = tempDiv.querySelectorAll('[data-id]');
dataEls.forEach(el => {
uiEls[el.dataset.id] = el;
});

return {
el: tempDiv.children[0],
ui: uiEls
};
};

export default ui;
Loading

0 comments on commit b0cd249

Please sign in to comment.