
This commit introduces significant updates to the admin and bank systems, focusing on improved event handling and user interface enhancements. Key changes include: - Refactored event handling for player data requests, paygrade updates, and message broadcasting in the admin panel. - Implemented new event types for handling player funds and transaction history in the bank system. - Updated JavaScript functions for better interaction with the web-based UI, including dynamic data requests and improved user feedback. - Removed deprecated functions and streamlined code for better maintainability. These enhancements aim to provide a more efficient and user-friendly experience for administrators and players alike.
488 lines
15 KiB
JavaScript
488 lines
15 KiB
JavaScript
/**
|
|
* Admin Panel Management Script
|
|
* This script handles the admin panel functionality for the Arma 3 game interface.
|
|
* It provides player management, money operations, messaging, and other admin functions.
|
|
*/
|
|
|
|
//=============================================================================
|
|
// #region DATA STRUCTURES AND VARIABLES
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Admin data structure - will be populated from the game
|
|
* Contains player information and payday amount configuration
|
|
*/
|
|
let adminData = {
|
|
players: [], // List of all players with their details
|
|
paydayAmounts: {} // Map of paygrade to bonus amount
|
|
};
|
|
|
|
/**
|
|
* Currently selected player ID for operations that require a player selection
|
|
* @type {string|null}
|
|
*/
|
|
let selectedPlayerId = null;
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region INITIALIZATION AND DATA REQUESTS
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Initialize the admin panel
|
|
* Sets up the UI, requests initial data from the game engine
|
|
*/
|
|
function initializeAdmin() {
|
|
updateStats();
|
|
setupFilterListeners();
|
|
requestPlayerData();
|
|
requestPaygradeData();
|
|
}
|
|
|
|
/**
|
|
* Request player data from the game engine
|
|
* Sends an event to fetch current player information
|
|
*/
|
|
function requestPlayerData() {
|
|
const message = {
|
|
event: "REQUEST::PLAYER::DATA",
|
|
data: {}
|
|
};
|
|
|
|
// Send request to the game engine
|
|
A3API.SendAlert(JSON.stringify(message));
|
|
}
|
|
|
|
/**
|
|
* Request paygrade data from the game engine
|
|
* Sends an event to fetch current paygrade configuration
|
|
*/
|
|
function requestPaygradeData() {
|
|
const message = {
|
|
event: "REQUEST::PAYGRADE::DATA",
|
|
data: {}
|
|
};
|
|
|
|
// Send request to the game engine
|
|
A3API.SendAlert(JSON.stringify(message));
|
|
}
|
|
|
|
/**
|
|
* Set up a timer to periodically refresh player data
|
|
* Ensures the admin panel shows up-to-date information
|
|
*/
|
|
function setupRefreshTimer() {
|
|
setInterval(requestPlayerData, 30000); // Refresh every 30 seconds
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region DATA HANDLERS
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Handle paygrade data received from the game engine
|
|
* Processes the paygrade list and updates the UI accordingly
|
|
*
|
|
* @param {Array} paygradeList - List of paygrade objects with paygrade and bonus properties
|
|
*/
|
|
function handlePaygradeDataRequest(paygradeList) {
|
|
try {
|
|
// Convert the paygrade list to a map for easier lookup
|
|
const paygradeMap = {};
|
|
paygradeList.forEach(item => {
|
|
paygradeMap[item.paygrade] = item.bonus;
|
|
});
|
|
|
|
adminData.paydayAmounts = paygradeMap;
|
|
|
|
// Update the player list if we already have player data
|
|
if (adminData.players.length > 0) {
|
|
updatePlayerList();
|
|
}
|
|
|
|
console.log("Paygrade data updated successfully");
|
|
} catch (error) {
|
|
console.error("Error updating paygrade data:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle player data received from the game engine
|
|
* Updates the admin panel with current player information
|
|
*
|
|
* @param {Array} playerList - List of player objects with their details
|
|
*/
|
|
function handlePlayerDataRequest(playerList) {
|
|
adminData.players = playerList;
|
|
updateStats();
|
|
updatePlayerList();
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region UI UPDATES AND DISPLAY
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Update header statistics
|
|
* Shows counts of online players and staff
|
|
*/
|
|
function updateStats() {
|
|
const onlinePlayers = adminData.players.length;
|
|
const onlineStaff = adminData.players.filter(p => p.paygrade !== "E1").length;
|
|
|
|
document.getElementById('playerCount').textContent = onlinePlayers;
|
|
document.getElementById('staffCount').textContent = onlineStaff;
|
|
}
|
|
|
|
/**
|
|
* Set up filter button listeners
|
|
* Configures the filter buttons and search functionality
|
|
*/
|
|
function setupFilterListeners() {
|
|
const filterButtons = document.querySelectorAll('.filter-btn');
|
|
|
|
// Set up filter button click handlers
|
|
filterButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
filterButtons.forEach(btn => btn.classList.remove('active'));
|
|
button.classList.add('active');
|
|
filterPlayers(button.dataset.filter);
|
|
});
|
|
});
|
|
|
|
// Set up search functionality
|
|
const searchInput = document.getElementById('playerSearch');
|
|
searchInput.addEventListener('input', () => {
|
|
const activeFilter = document.querySelector('.filter-btn.active').dataset.filter;
|
|
filterPlayers(activeFilter, searchInput.value);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Filter players based on category and search term
|
|
*
|
|
* @param {string} filter - The filter category (all, staff, blufor, etc.)
|
|
* @param {string} searchTerm - Optional search term to filter by name
|
|
*/
|
|
function filterPlayers(filter, searchTerm = '') {
|
|
let filteredPlayers = adminData.players;
|
|
|
|
// Apply category filter
|
|
if (filter === 'staff') {
|
|
filteredPlayers = filteredPlayers.filter(p => p.paygrade !== "E1");
|
|
} else if (filter === 'blufor') {
|
|
filteredPlayers = filteredPlayers.filter(p => p.side === "WEST");
|
|
} else if (filter === 'opfor') {
|
|
filteredPlayers = filteredPlayers.filter(p => p.side === "EAST");
|
|
} else if (filter === 'independent') {
|
|
filteredPlayers = filteredPlayers.filter(p => p.side === "GUER");
|
|
} else if (filter === 'civilian') {
|
|
filteredPlayers = filteredPlayers.filter(p => p.side === "CIV");
|
|
}
|
|
|
|
// Apply search filter
|
|
if (searchTerm) {
|
|
const term = searchTerm.toLowerCase();
|
|
filteredPlayers = filteredPlayers.filter(p =>
|
|
p.name.toLowerCase().includes(term)
|
|
);
|
|
}
|
|
|
|
updatePlayerList(filteredPlayers);
|
|
}
|
|
|
|
/**
|
|
* Update the player list display
|
|
* Renders the filtered player list with all relevant information
|
|
*
|
|
* @param {Array} players - List of player objects to display, defaults to all players
|
|
*/
|
|
function updatePlayerList(players = adminData.players) {
|
|
const playerList = document.getElementById('playerList');
|
|
playerList.innerHTML = players.map(player => {
|
|
const paydayAmount = adminData.paydayAmounts[player.paygrade] || 1000;
|
|
const rankClass = getRankClass(player.paygrade);
|
|
|
|
return `
|
|
<div class="player-item" data-id="${player.uid}">
|
|
<div class="player-info">
|
|
<span class="player-name">${player.name}</span>
|
|
<span class="player-rank rank-${rankClass}">${player.paygrade}</span>
|
|
<span class="player-money">${parseInt(player.funds).toLocaleString()}</span>
|
|
<span class="player-payday">Payday: ${paydayAmount.toLocaleString()}</span>
|
|
<span class="player-side side-${player.side.toLowerCase()}">${player.side}</span>
|
|
</div>
|
|
<div class="player-actions">
|
|
<button class="action-btn promote-btn" onclick="updatePaygrade('${player.uid}', true)">
|
|
Promote
|
|
</button>
|
|
<button class="action-btn demote-btn" onclick="updatePaygrade('${player.uid}', false)">
|
|
Demote
|
|
</button>
|
|
<button class="action-btn message-btn" onclick="openMessageModal('${player.uid}', '${player.name}')">Message</button>
|
|
<button class="action-btn" onclick="openMoneyModal('${player.uid}')">Modify Money</button>
|
|
</div>
|
|
</div>
|
|
`}).join('');
|
|
}
|
|
|
|
/**
|
|
* Helper function to determine rank class based on paygrade
|
|
* Used for styling different ranks with appropriate CSS classes
|
|
*
|
|
* @param {string} paygrade - The player's paygrade code
|
|
* @returns {string} CSS class name for the rank
|
|
*/
|
|
function getRankClass(paygrade) {
|
|
if (paygrade.startsWith('E')) {
|
|
return 'enlisted';
|
|
} else if (paygrade.startsWith('WO')) {
|
|
return 'warrant';
|
|
} else if (paygrade.startsWith('O') ||
|
|
paygrade.startsWith('1') ||
|
|
paygrade.startsWith('2') ||
|
|
paygrade.startsWith('C') ||
|
|
paygrade.startsWith('M')) {
|
|
return 'officer';
|
|
} else {
|
|
return 'enlisted'; // Default
|
|
}
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region RANK MANAGEMENT
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Update a player's paygrade (promote or demote)
|
|
*
|
|
* @param {string} uid - Player's unique identifier
|
|
* @param {boolean} isPromotion - True for promotion, false for demotion
|
|
*/
|
|
function updatePaygrade(uid, isPromotion) {
|
|
const player = adminData.players.find(p => p.uid === uid);
|
|
if (!player) return;
|
|
|
|
// Use the paygrades from the configuration
|
|
const paygrades = Object.keys(adminData.paydayAmounts);
|
|
paygrades.sort((a, b) => adminData.paydayAmounts[a] - adminData.paydayAmounts[b]); // Sort by payment amount
|
|
|
|
const currentIndex = paygrades.indexOf(player.paygrade);
|
|
|
|
let newPaygrade;
|
|
if (isPromotion && currentIndex < paygrades.length - 1) {
|
|
newPaygrade = paygrades[currentIndex + 1];
|
|
} else if (!isPromotion && currentIndex > 0) {
|
|
newPaygrade = paygrades[currentIndex - 1];
|
|
} else {
|
|
return; // Can't promote/demote further
|
|
}
|
|
|
|
const message = {
|
|
event: "UPDATE::PAYGRADE",
|
|
data: [uid, newPaygrade]
|
|
};
|
|
|
|
A3API.SendAlert(JSON.stringify(message));
|
|
|
|
// Optimistic update
|
|
player.paygrade = newPaygrade;
|
|
updatePlayerList();
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region MONEY MANAGEMENT
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Open the money modification modal for a player
|
|
*
|
|
* @param {string} uid - Player's unique identifier
|
|
*/
|
|
function openMoneyModal(uid) {
|
|
selectedPlayerId = uid;
|
|
const modal = document.getElementById('moneyModal');
|
|
modal.style.display = 'block';
|
|
}
|
|
|
|
/**
|
|
* Close the money modification modal
|
|
*/
|
|
function closeMoneyModal() {
|
|
const modal = document.getElementById('moneyModal');
|
|
modal.style.display = 'none';
|
|
document.getElementById('moneyAmount').value = '';
|
|
selectedPlayerId = null;
|
|
}
|
|
|
|
/**
|
|
* Give money to the selected player
|
|
*/
|
|
function giveMoney() {
|
|
const amount = parseInt(document.getElementById('moneyAmount').value);
|
|
if (amount && selectedPlayerId) {
|
|
handleTransferFunds("advance", amount, selectedPlayerId);
|
|
closeMoneyModal();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Give money to all players
|
|
*/
|
|
function giveAllMoney() {
|
|
const amount = parseInt(document.getElementById('giveAllAmount').value);
|
|
const message = {
|
|
event: "ADVANCE::ALL",
|
|
data: [amount]
|
|
}
|
|
|
|
A3API.SendAlert(JSON.stringify(message));
|
|
|
|
// Request updated player data after giving money to all players
|
|
setTimeout(requestPlayerData, 500); // Short delay to allow server processing
|
|
}
|
|
|
|
/**
|
|
* Take money from the selected player
|
|
*/
|
|
function takeMoney() {
|
|
const amount = parseInt(document.getElementById('moneyAmount').value);
|
|
if (amount && selectedPlayerId) {
|
|
handleTransferFunds("deduct", amount, selectedPlayerId);
|
|
closeMoneyModal();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle funds transfer for a player
|
|
*
|
|
* @param {string} condition - "advance" to give money, "deduct" to take money
|
|
* @param {number} amount - Amount of money to transfer
|
|
* @param {string} uid - Player's unique identifier
|
|
*/
|
|
function handleTransferFunds(condition, amount, uid) {
|
|
const message = {
|
|
event: "HANDLE::TRANSFER",
|
|
data: [condition, amount, uid]
|
|
};
|
|
|
|
A3API.SendAlert(JSON.stringify(message));
|
|
|
|
// Optimistic update
|
|
const player = adminData.players.find(p => p.uid === uid);
|
|
if (player) {
|
|
if (condition === "advance") {
|
|
player.funds = parseInt(player.funds) + amount;
|
|
} else if (condition === "deduct") {
|
|
player.funds = Math.max(0, parseInt(player.funds) - amount);
|
|
}
|
|
updatePlayerList();
|
|
}
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region MESSAGE SYSTEM
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Open the message modal for a player
|
|
*
|
|
* @param {string} uid - Player's unique identifier
|
|
* @param {string} playerName - Player's name for display
|
|
*/
|
|
function openMessageModal(uid, playerName) {
|
|
selectedPlayerId = uid;
|
|
const modal = document.getElementById('messageModal');
|
|
document.getElementById('messagePlayerName').textContent = playerName;
|
|
modal.style.display = 'block';
|
|
}
|
|
|
|
/**
|
|
* Close the message modal
|
|
*/
|
|
function closeMessageModal() {
|
|
const modal = document.getElementById('messageModal');
|
|
modal.style.display = 'none';
|
|
document.getElementById('messageInput').value = '';
|
|
selectedPlayerId = null;
|
|
}
|
|
|
|
/**
|
|
* Send a message to the selected player
|
|
*/
|
|
function sendPlayerMessage() {
|
|
const message = document.getElementById('messageInput').value;
|
|
if (message && selectedPlayerId) {
|
|
const messageData = {
|
|
event: "SEND::MESSAGE",
|
|
data: [selectedPlayerId, message]
|
|
};
|
|
|
|
A3API.SendAlert(JSON.stringify(messageData));
|
|
closeMessageModal();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Broadcast a message to all players
|
|
*/
|
|
function broadcastMessage() {
|
|
const message = document.getElementById('broadcastMessage').value;
|
|
if (message) {
|
|
const messageData = {
|
|
event: "BROADCAST::MESSAGE",
|
|
data: ["", message]
|
|
};
|
|
|
|
A3API.SendAlert(JSON.stringify(messageData));
|
|
document.getElementById('broadcastMessage').value = '';
|
|
}
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region GLOBAL ACTIONS
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Trigger a payday for all players
|
|
*/
|
|
function Payday() {
|
|
const message = {
|
|
event: "HANDLE::PAYDAY",
|
|
data: []
|
|
};
|
|
|
|
A3API.SendAlert(JSON.stringify(message));
|
|
|
|
// Request updated player data after payday
|
|
setTimeout(requestPlayerData, 500); // Short delay to allow server processing
|
|
}
|
|
|
|
// #endregion
|
|
|
|
//=============================================================================
|
|
// #region EVENT LISTENERS
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Initialize when DOM is loaded
|
|
*/
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initializeAdmin();
|
|
setupRefreshTimer();
|
|
});
|
|
|
|
// #endregion
|