Release 0.9.0
This commit is contained in:
20
bot/modules/players/templates/jinja2_macros.html
Normal file
20
bot/modules/players/templates/jinja2_macros.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{%- macro construct_toggle_link(bool, active_text, deactivate_event, inactive_text, activate_event) -%}
|
||||
{%- set bool = bool|default(false) -%}
|
||||
{%- set active_text = active_text|default(none) -%}
|
||||
{%- set deactivate_event = deactivate_event|default(none) -%}
|
||||
{%- set inactive_text = inactive_text|default(none) -%}
|
||||
{%- set activate_event = activate_event|default(none) -%}
|
||||
{%- if bool == true -%}
|
||||
{%- if deactivate_event != none and activate_event != none -%}
|
||||
<span class="active"><a href="#" onclick="window.socket.emit('{{ deactivate_event[0] }}', {{ deactivate_event[1] }}); return false;">{{ active_text }}</a></span>
|
||||
{%- elif deactivate_event != none and activate_event == none -%}
|
||||
<span class="active"><a href="#" onclick="window.socket.emit('{{ deactivate_event[0] }}', {{ deactivate_event[1] }}); return false;">{{ active_text }}</a></span>
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
{%- if deactivate_event != none and activate_event != none -%}
|
||||
<span class="inactive"><a href="#" onclick="window.socket.emit('{{ activate_event[0] }}', {{ activate_event[1] }}); return false;">{{ inactive_text }}</a></span>
|
||||
{%- elif deactivate_event != none and activate_event == none -%}
|
||||
<span class="inactive"><a href="#" onclick="window.socket.emit('{{ deactivate_event[0] }}', {{ deactivate_event[1] }}); return false;">{{ active_text }}</a></span>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
@@ -0,0 +1,10 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_control_info_link" class="info">
|
||||
{{- construct_toggle_link(
|
||||
True,
|
||||
"i", ['widget_event', ['players', ['toggle_players_widget_view', {
|
||||
'steamid': player.steamid,
|
||||
'action': 'show_info_view'
|
||||
}]]]
|
||||
)-}}
|
||||
</span>
|
||||
@@ -0,0 +1,13 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
{%- if player.is_initialized == true -%}
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_control_kick_link" class="info">
|
||||
{{- construct_toggle_link(
|
||||
player.is_initialized,
|
||||
"kick", ['widget_event', ['players', ['kick_player', {
|
||||
'action': 'kick_player',
|
||||
'steamid': player.steamid,
|
||||
'confirmed': 'False'
|
||||
}]]]
|
||||
)-}}
|
||||
</span>
|
||||
{%- endif -%}
|
||||
@@ -0,0 +1,9 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
<div>
|
||||
{{ construct_toggle_link(
|
||||
options_view_toggle,
|
||||
"options", ['widget_event', ['players', ['toggle_players_widget_view', {'steamid': steamid, "action": "show_options"}]]],
|
||||
"back", ['widget_event', ['players', ['toggle_players_widget_view', {'steamid': steamid, "action": "show_frontend"}]]]
|
||||
)}}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<div id="player_table_widget_options_toggle" class="pull_out right">
|
||||
{{ control_switch_options_view }}
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
<table class="delete_modal">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p>Are you sure you want to kick player {{ steamid }}?</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p>By clicking [confirm] you will continue to proceed kicking the dude.</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Clicking [cancel] will abort the kicking.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div id="kick_player_reason">
|
||||
<input name="kick_player_reason" value="{{ reason }}" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="kick_player_confirm" class="modal_delete_button">
|
||||
<span class="active">
|
||||
{% include "manage_players_widget/modal_confirm_kick_send_data.html" %}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="kick_player_cancel" class="modal_cancel_button">
|
||||
{{ construct_toggle_link(
|
||||
True,
|
||||
"cancel", ['widget_event', ['players', ['kick_player', {
|
||||
'action': "cancel_kick_player",
|
||||
'steamid': 'steamid'
|
||||
}]]]
|
||||
) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script>
|
||||
function send_kick_data_to_bot() {
|
||||
let kick_reason = $("input[name='kick_player_reason']").val();
|
||||
window.socket.emit(
|
||||
'widget_event',
|
||||
['players', ['kick_player', {
|
||||
'action': 'kick_player',
|
||||
'steamid': '{{ steamid }}',
|
||||
'reason': kick_reason,
|
||||
'confirmed': 'True'
|
||||
}]]
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<a href="#" onclick="send_kick_data_to_bot(); return false;">confirm</a>
|
||||
@@ -0,0 +1,7 @@
|
||||
<tr>
|
||||
<td colspan="11">
|
||||
<div>
|
||||
{{ action_delete_button }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,13 @@
|
||||
<tr>
|
||||
<th>*</th>
|
||||
<th onclick="window.sorting(this, player_table, 1)">actions</th>
|
||||
<th onclick="window.sorting(this, player_table, 2)">level</th>
|
||||
<th onclick="window.sorting(this, player_table, 3)">name</th>
|
||||
<th onclick="window.sorting(this, player_table, 4)">health</th>
|
||||
<th onclick="window.sorting(this, player_table, 5)">id</th>
|
||||
<th onclick="window.sorting(this, player_table, 6)">steamid</th>
|
||||
<th onclick="window.sorting(this, player_table, 7)">pos</th>
|
||||
<th onclick="window.sorting(this, player_table, 8)">zombies</th>
|
||||
<th onclick="window.sorting(this, player_table, 9)" class="right">last seen</th>
|
||||
<th onclick="window.sorting(this, player_table, 10)" class="right">gametime</th>
|
||||
</tr>
|
||||
@@ -0,0 +1,30 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
<tr id="player_table_row_{{ player.dataset }}_{{ player.steamid }}"{%- if css_class %} class="{{ css_class }}"{%- endif -%}>
|
||||
<td>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_control_select_link" class="select_button">{{ control_select_link }}</span>
|
||||
</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_actions">{{ control_info_link }}{{ control_kick_link }}</td>
|
||||
<td class="center" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_level">{{ player.level }}</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_name" onclick="$(this).selectText();">{{ player.name }}</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_health">{{ player.health }}</td>
|
||||
<td class="right" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_id" onclick="$(this).selectText();">{{ player.id }}</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_steamid" onclick="$(this).selectText();">{{ player.steamid }}</td>
|
||||
<td class="position right" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos" onclick="$(this).selectText();">
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos_x">
|
||||
{{ ((player | default({})).pos | default({}) ).x | default('0') }}
|
||||
</span>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos_y">
|
||||
{{ ((player | default({})).pos | default({}) ).y | default('0') }}
|
||||
</span>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos_z">
|
||||
{{ ((player | default({})).pos | default({}) ).z | default('0') }}
|
||||
</span>
|
||||
</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_zombies">{{ player.zombies }}</td>
|
||||
<td class="nobr right" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_last_updated_servertime">
|
||||
{{ player.last_updated_servertime }}
|
||||
</td>
|
||||
<td class="nobr right" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_last_seen_gametime">
|
||||
{{ player.last_seen_gametime }}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,33 @@
|
||||
<header>
|
||||
<div>
|
||||
<span>Players</span>
|
||||
</div>
|
||||
</header>
|
||||
<aside>
|
||||
{{ options_toggle }}
|
||||
</aside>
|
||||
<main>
|
||||
<table class="data_table">
|
||||
<caption>
|
||||
<span>offline</span>
|
||||
<span class="in_limbo">offline and dead</span>
|
||||
<span class="is_online in_limbo">logging in</span>
|
||||
<span class="is_online is_initialized">online</span>
|
||||
<span class="in_limbo is_online is_initialized">online and dead</span>
|
||||
</caption>
|
||||
<thead>
|
||||
{{ table_header }}
|
||||
</thead>
|
||||
<tbody id="player_table">
|
||||
{{ table_rows }}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
{{ table_footer }}
|
||||
</tfoot>
|
||||
</table>
|
||||
<div class="dialog">
|
||||
<div id="manage_players_widget_modal" class="modal-content">
|
||||
<p>this is the text inside the modal</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -0,0 +1,155 @@
|
||||
<header>
|
||||
<div>
|
||||
<span>Players</span>
|
||||
</div>
|
||||
</header>
|
||||
<aside>
|
||||
{{ options_toggle }}
|
||||
</aside>
|
||||
<main>
|
||||
<table class="info_sheet">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3">player-info ({{ player.steamid }})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="player_table_row_{{ player.dataset }}_{{ player.steamid }}">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_name" onclick="$(this).selectText();">{{ player.name }}</td>
|
||||
<td>the players steam-name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_id" onclick="$(this).selectText();">{{ player.id }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SteamID</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_steamid" onclick="$(this).selectText();">{{ player.steamid }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Health</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_health">{{ player.health }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Position</td>
|
||||
<td class="position" onclick="$(this).selectText();">
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos_x">{{ ((player | default({})).pos | default({}) ).x | default('0') }}</span>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos_y">{{ ((player | default({})).pos | default({}) ).y | default('0') }}</span>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_pos_z">{{ ((player | default({})).pos | default({}) ).z | default('0') }}</span>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rotation</td>
|
||||
<td class="position">
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_rot_x">{{ ((player | default({})).rot | default({}) ).x | default('0') }}</span>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_rot_y">{{ ((player | default({})).rot | default({}) ).y | default('0') }}</span>
|
||||
<span id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_rot_z">{{ ((player | default({})).rot | default({}) ).z | default('0') }}</span>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Level</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_level">{{ player.level }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP-Address</td>
|
||||
<td onclick="$(this).selectText();" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_ip">{{ player.ip }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ping</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_ping">{{ player.ping }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Deaths</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_deaths">{{ player.deaths }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Zombie-Kills</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_zombies">{{ player.zombies }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Player-Kills</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_players">{{ player.players }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Score</td>
|
||||
<td id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_score">{{ player.score }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last seen</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_last_updated_servertime">
|
||||
{{ player.last_updated_servertime }}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>First seen (gametime)</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_first_seen_gametime">
|
||||
{{ player.first_seen_gametime }}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last seen (gametime)</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_last_seen_gametime">
|
||||
{{ player.last_seen_gametime }}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>dataset</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_origin">
|
||||
{{ player.dataset }}
|
||||
</td>
|
||||
<td>the server-instance this entry is from</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>is_authenticated</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_is_authenticated">
|
||||
{{ player.is_authenticated | default("False") }}
|
||||
</td>
|
||||
<td>has authenticated with the bot</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>in_limbo</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_in_limbo">
|
||||
{{ player.in_limbo | default("False") }}
|
||||
</td>
|
||||
<td>hasn't got any health, is dead(ish)!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>is_initialized</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_is_initialized">
|
||||
{{ player.is_initialized | default("False") }}
|
||||
</td>
|
||||
<td>player is online, has health, is ready to go!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>is_online</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_is_online">
|
||||
{{ player.is_online | default("False") }}
|
||||
</td>
|
||||
<td>we can see you!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>is_muted</td>
|
||||
<td class="nobr" id="player_table_row_{{ player.dataset }}_{{ player.steamid }}_is_muted">
|
||||
{{ player.is_muted | default("False") }}
|
||||
</td>
|
||||
<td>come again?</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
@@ -0,0 +1,27 @@
|
||||
<header>
|
||||
<div>
|
||||
<span>Players</span>
|
||||
</div>
|
||||
</header>
|
||||
<aside>
|
||||
{{ options_toggle }}
|
||||
</aside>
|
||||
<main>
|
||||
<table class="options_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">player table widget options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th colspan="2"><span>widget-options</span></th>
|
||||
</tr>
|
||||
{% for key, value in widget_options.items() %}
|
||||
<tr>
|
||||
<td><span>{{key}}</span></td><td>{{value}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
168
bot/modules/players/templates/webmap/player_actions.html
Normal file
168
bot/modules/players/templates/webmap/player_actions.html
Normal file
@@ -0,0 +1,168 @@
|
||||
// ========================================
|
||||
// Player Popup Actions
|
||||
// ========================================
|
||||
|
||||
// Kick player from map popup
|
||||
window.kickPlayerFromMap = function(steamid, playerName) {
|
||||
const reason = prompt('Kick reason for ' + playerName + ':', 'Admin action');
|
||||
if (reason === null) {
|
||||
return; // User cancelled
|
||||
}
|
||||
|
||||
window.socket.emit(
|
||||
'widget_event',
|
||||
['players',
|
||||
['kick_player', {
|
||||
'action': 'kick_player',
|
||||
'steamid': steamid,
|
||||
'reason': reason || 'No reason provided',
|
||||
'confirmed': 'True'
|
||||
}]]
|
||||
);
|
||||
console.log('[MAP] Kicking player:', steamid, 'Reason:', reason);
|
||||
};
|
||||
|
||||
// Send message to player from map popup
|
||||
window.messagePlayerFromMap = function(steamid, playerName) {
|
||||
const message = prompt('Message to ' + playerName + ':', '');
|
||||
if (!message) {
|
||||
return; // User cancelled or empty message
|
||||
}
|
||||
|
||||
window.socket.emit(
|
||||
'widget_event',
|
||||
['players',
|
||||
['say_to_player', {
|
||||
'steamid': steamid,
|
||||
'message': message
|
||||
}]]
|
||||
);
|
||||
console.log('[MAP] Sending message to player:', steamid, 'Message:', message);
|
||||
};
|
||||
|
||||
// Toggle player mute status from map popup
|
||||
window.togglePlayerMuteFromMap = function(steamid, dataset, isMuted) {
|
||||
window.socket.emit(
|
||||
'widget_event',
|
||||
['players',
|
||||
['toggle_player_mute', {
|
||||
'steamid': steamid,
|
||||
'dataset': dataset,
|
||||
'mute_status': isMuted
|
||||
}]]
|
||||
);
|
||||
console.log('[MAP] Toggling player mute status:', steamid, 'to', isMuted);
|
||||
};
|
||||
|
||||
// Toggle player authentication status from map popup
|
||||
window.togglePlayerAuthFromMap = function(steamid, dataset, isAuth) {
|
||||
window.socket.emit(
|
||||
'widget_event',
|
||||
['players',
|
||||
['toggle_player_authentication', {
|
||||
'steamid': steamid,
|
||||
'dataset': dataset,
|
||||
'auth_status': isAuth
|
||||
}]]
|
||||
);
|
||||
console.log('[MAP] Toggling player auth status:', steamid, 'to', isAuth);
|
||||
};
|
||||
|
||||
// Teleport player from map popup - click to select destination
|
||||
window.teleportPlayerFromMap = function(steamid, playerName) {
|
||||
// Close any open popups
|
||||
map.closePopup();
|
||||
|
||||
// Create info message
|
||||
const infoDiv = L.DomUtil.create('div', 'coordinates-display');
|
||||
infoDiv.style.bottom = '50px';
|
||||
infoDiv.style.background = 'rgba(255, 204, 0, 0.95)';
|
||||
infoDiv.style.borderColor = 'var(--lcars-golden-tanoi)';
|
||||
infoDiv.style.color = '#000';
|
||||
infoDiv.style.fontWeight = 'bold';
|
||||
infoDiv.innerHTML = '🎯 Click destination for ' + playerName + ' (Locations will use their TP entry)';
|
||||
document.getElementById('map').appendChild(infoDiv);
|
||||
|
||||
// Change cursor
|
||||
map.getContainer().style.cursor = 'crosshair';
|
||||
|
||||
// Wait for click
|
||||
map.once('click', function(e) {
|
||||
const clickCoords = e.latlng;
|
||||
let targetX = Math.round(clickCoords.lat);
|
||||
let targetY = 0; // Default Y, will be adjusted
|
||||
let targetZ = Math.round(clickCoords.lng);
|
||||
|
||||
// Check if click is inside any location with teleport_entry
|
||||
let foundLocationTeleport = false;
|
||||
for (const locationId in locations) {
|
||||
const loc = locations[locationId];
|
||||
const teleportEntry = loc.teleport_entry || {};
|
||||
const hasTeleport = teleportEntry.x !== undefined &&
|
||||
teleportEntry.y !== undefined &&
|
||||
teleportEntry.z !== undefined;
|
||||
|
||||
if (hasTeleport) {
|
||||
// Check if click is inside this location's bounds
|
||||
if (isInsideLocation(clickCoords.lat, clickCoords.lng, loc)) {
|
||||
// Use location's teleport entry
|
||||
targetX = Math.round(parseFloat(teleportEntry.x));
|
||||
targetY = Math.round(parseFloat(teleportEntry.y));
|
||||
targetZ = Math.round(parseFloat(teleportEntry.z));
|
||||
foundLocationTeleport = true;
|
||||
console.log('[MAP] Using location teleport entry:', loc.name, 'at', targetX, targetY, targetZ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no location teleport found, use default Y (ground level)
|
||||
if (!foundLocationTeleport) {
|
||||
targetY = -1; // Standard ground level
|
||||
}
|
||||
|
||||
// Call teleport_player action
|
||||
window.socket.emit(
|
||||
'widget_event',
|
||||
['players',
|
||||
['teleport_player', {
|
||||
'steamid': steamid,
|
||||
'coordinates': {
|
||||
'x': targetX.toString(),
|
||||
'y': targetY.toString(),
|
||||
'z': targetZ.toString()
|
||||
}
|
||||
}]]
|
||||
);
|
||||
|
||||
console.log('[MAP] Teleporting player:', steamid, 'to', targetX, targetY, targetZ);
|
||||
|
||||
// Cleanup
|
||||
map.getContainer().style.cursor = '';
|
||||
document.getElementById('map').removeChild(infoDiv);
|
||||
});
|
||||
};
|
||||
|
||||
// Helper function to check if coordinates are inside a location
|
||||
function isInsideLocation(x, z, loc) {
|
||||
const coords = loc.coordinates;
|
||||
const dims = loc.dimensions;
|
||||
const shape = loc.shape;
|
||||
|
||||
if (shape === 'circle' || shape === 'spherical') {
|
||||
const radius = parseFloat(dims.radius || 0);
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(x - coords.x, 2) +
|
||||
Math.pow(z - coords.z, 2)
|
||||
);
|
||||
return distance <= radius;
|
||||
} else if (shape === 'rectangle' || shape === 'box') {
|
||||
const width = parseFloat(dims.width || 0);
|
||||
const length = parseFloat(dims.length || 0);
|
||||
return (
|
||||
x >= coords.x - width && x <= coords.x + width &&
|
||||
z >= coords.z - length && z <= coords.z + length
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
3
bot/modules/players/templates/webmap/player_markers.html
Normal file
3
bot/modules/players/templates/webmap/player_markers.html
Normal file
@@ -0,0 +1,3 @@
|
||||
// Player markers are now loaded dynamically via Socket.IO
|
||||
// Initial loading is handled by player_position_update events
|
||||
// See player_update_handler.html for marker creation logic
|
||||
64
bot/modules/players/templates/webmap/player_popup.html
Normal file
64
bot/modules/players/templates/webmap/player_popup.html
Normal file
@@ -0,0 +1,64 @@
|
||||
// Helper function to create player popup content
|
||||
function createPlayerPopup(steamid, player) {
|
||||
const healthMax = 150;
|
||||
const healthPercent = Math.round((player.health / healthMax) * 100);
|
||||
|
||||
// Status badge
|
||||
let statusBadge = '🟢 Online';
|
||||
let statusColor = '#66ff66';
|
||||
if (player.in_limbo) {
|
||||
statusBadge = '💀 Dead';
|
||||
statusColor = '#ff6666';
|
||||
} else if (!player.is_initialized) {
|
||||
statusBadge = '🔄 Spawning';
|
||||
statusColor = '#ffaa66';
|
||||
}
|
||||
|
||||
// Permission badge
|
||||
let permissionBadge = '';
|
||||
if (player.permission_level === 0) {
|
||||
permissionBadge = '<span style="background: #ff0000; color: white; padding: 2px 6px; border-radius: 3px; font-size: 0.85em; font-weight: bold;">🛡️ ADMIN</span>';
|
||||
}
|
||||
|
||||
// Use template literal for clean HTML
|
||||
return `
|
||||
<div style="min-width: 280px; font-family: monospace;">
|
||||
<b style="font-size: 1.1em;">${player.name}</b>
|
||||
${permissionBadge ? '<br>' + permissionBadge : ''}
|
||||
<br><span style="font-size: 0.9em; color: ${statusColor};">${statusBadge}</span>
|
||||
<br><hr style="margin: 5px 0; border-color: #333;">
|
||||
<b>❤️ Health:</b> ${player.health}/${healthMax} (${healthPercent}%)
|
||||
<br><b>⭐ Level:</b> ${player.level} | <b>🎯 Score:</b> ${player.score}
|
||||
<br><b>🧟 Zombies:</b> ${player.zombies} | <b>💀 Deaths:</b> ${player.deaths}
|
||||
<br><b>👥 Players:</b> ${player.players} | <b>📡 Ping:</b> ${player.ping}ms
|
||||
<br><hr style="margin: 8px 0; border-color: #333;">
|
||||
<div style="display: flex; gap: 5px; margin-bottom: 5px;">
|
||||
<label style="display: flex; align-items: center; gap: 5px; cursor: pointer; flex: 1;">
|
||||
<input type="checkbox"
|
||||
${player.is_muted ? 'checked' : ''}
|
||||
onchange="togglePlayerMuteFromMap('${steamid}', this.checked)"
|
||||
style="cursor: pointer; width: 16px; height: 16px;" />
|
||||
<span style="font-weight: bold; font-size: 0.9em;">🔇 Muted</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 5px; cursor: pointer; flex: 1;">
|
||||
<input type="checkbox"
|
||||
${player.is_authenticated ? 'checked' : ''}
|
||||
onchange="togglePlayerAuthFromMap('${steamid}', this.checked)"
|
||||
style="cursor: pointer; width: 16px; height: 16px;" />
|
||||
<span style="font-weight: bold; font-size: 0.9em;">✅ Auth</span>
|
||||
</label>
|
||||
</div>
|
||||
<div style="display: flex; gap: 5px; margin-bottom: 5px;">
|
||||
<button onclick="messagePlayerFromMap('${steamid}', '${player.name.replace(/'/g, "\\'")}', event)"
|
||||
style="flex: 1; padding: 6px 10px; background: var(--lcars-anakiwa); color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.9em;">
|
||||
💬 Message</button>
|
||||
<button onclick="kickPlayerFromMap('${steamid}', '${player.name.replace(/'/g, "\\'")}', event)"
|
||||
style="flex: 1; padding: 6px 10px; background: var(--lcars-hopbush); color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.9em;">
|
||||
👢 Kick</button>
|
||||
</div>
|
||||
<button onclick="teleportPlayerFromMap('${steamid}', '${player.name.replace(/'/g, "\\'")}', event)"
|
||||
style="width: 100%; padding: 8px 10px; background: var(--lcars-golden-tanoi); color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.95em;">
|
||||
🎯 Teleport Player - Click Map</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Handle player position updates
|
||||
if (data.data_type === 'player_position_update' && data.payload) {
|
||||
const playerData = data.payload;
|
||||
const steamid = playerData.steamid;
|
||||
const pos = playerData.position;
|
||||
|
||||
console.log('[MAP] Processing player update for:', steamid, pos);
|
||||
|
||||
if (!steamid || !pos) {
|
||||
console.warn('[MAP] Invalid player update data:', playerData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update or create player data in players object
|
||||
if (!players[steamid]) {
|
||||
players[steamid] = {};
|
||||
}
|
||||
|
||||
// Update all player data
|
||||
players[steamid].pos = pos;
|
||||
players[steamid].name = playerData.name || players[steamid].name || 'Player';
|
||||
players[steamid].level = playerData.level !== undefined ? playerData.level : (players[steamid].level || 0);
|
||||
players[steamid].health = playerData.health !== undefined ? playerData.health : (players[steamid].health || 0);
|
||||
players[steamid].zombies = playerData.zombies !== undefined ? playerData.zombies : (players[steamid].zombies || 0);
|
||||
players[steamid].deaths = playerData.deaths !== undefined ? playerData.deaths : (players[steamid].deaths || 0);
|
||||
players[steamid].players = playerData.players !== undefined ? playerData.players : (players[steamid].players || 0);
|
||||
players[steamid].score = playerData.score !== undefined ? playerData.score : (players[steamid].score || 0);
|
||||
players[steamid].ping = playerData.ping !== undefined ? playerData.ping : (players[steamid].ping || 0);
|
||||
players[steamid].is_muted = playerData.is_muted !== undefined ? playerData.is_muted : (players[steamid].is_muted || false);
|
||||
players[steamid].is_authenticated = playerData.is_authenticated !== undefined ? playerData.is_authenticated : (players[steamid].is_authenticated || false);
|
||||
players[steamid].in_limbo = playerData.in_limbo !== undefined ? playerData.in_limbo : (players[steamid].in_limbo || false);
|
||||
players[steamid].is_initialized = playerData.is_initialized !== undefined ? playerData.is_initialized : (players[steamid].is_initialized || false);
|
||||
players[steamid].permission_level = playerData.permission_level || players[steamid].permission_level || null;
|
||||
players[steamid].dataset = playerData.dataset || players[steamid].dataset || '';
|
||||
|
||||
// Update or create marker
|
||||
if (playerMarkers[steamid]) {
|
||||
// Update existing marker position and popup
|
||||
playerMarkers[steamid].setLatLng([pos.x, pos.z]);
|
||||
playerMarkers[steamid].setPopupContent(createPlayerPopup(steamid, players[steamid]));
|
||||
console.log('[MAP] Updated player marker:', steamid);
|
||||
} else {
|
||||
// Create new marker
|
||||
const marker = L.circleMarker([pos.x, pos.z], {
|
||||
radius: 5,
|
||||
fillColor: '#66ccff',
|
||||
color: '#0099ff',
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.9,
|
||||
className: 'player-marker'
|
||||
}).addTo(map);
|
||||
|
||||
marker.bindPopup(createPlayerPopup(steamid, players[steamid]));
|
||||
playerMarkers[steamid] = marker;
|
||||
console.log('[MAP] Created new player marker:', steamid);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user