/** * 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 UI is updated with the latest 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 `
${player.name} ${player.paygrade} ${parseInt(player.funds).toLocaleString()} Payday: ${paydayAmount.toLocaleString()} ${player.side}
`}).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