diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index f42e096..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "Start Arma 3 Server", - "type": "process", - "command": "wscript.exe", - "args": [ - "D:\\SteamLibrary\\steamapps\\common\\Arma 3\\start_serverhub_hidden.vbs" - ], - "presentation": { - "reveal": "silent", - "panel": "shared", - "showReuseMessage": false, - "clear": true - }, - "problemMatcher": [], - "group": { - "kind": "build", - "isDefault": true - } - } - ] -} \ No newline at end of file diff --git a/addons/admin/ui/_site/index.html b/addons/admin/ui/_site/index.html new file mode 100644 index 0000000..3235c91 --- /dev/null +++ b/addons/admin/ui/_site/index.html @@ -0,0 +1,107 @@ + + + + + + Forge Admin Panel + + + + +
+
+
+

Admin Panel

+
+
+ 👥 +
+
Online Players
+
0
+
+
+
+ 👑 +
+
Staff Online
+
0
+
+
+
+
+
+
+
+
+ +
+

Global Actions

+
+ + +
+ +
+ + +
+

Broadcast Message

+
+ + +
+ +
+
+ + +
+ +
+ +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/addons/admin/ui/_site/script.js b/addons/admin/ui/_site/script.js new file mode 100644 index 0000000..9dc0b9c --- /dev/null +++ b/addons/admin/ui/_site/script.js @@ -0,0 +1,228 @@ +// Simulated admin data - this would be replaced with actual game data +let adminData = { + players: [ + { + id: 1, + name: "John_Doe", + role: "admin", + money: 50000, + status: "online" + }, + { + id: 2, + name: "Jane_Smith", + role: "mod", + money: 25000, + status: "online" + }, + { + id: 3, + name: "Mike_Johnson", + role: "player", + money: 10000, + status: "offline" + } + ], + paydayAmount: 1000 +}; + +let selectedPlayerId = null; + +// Initialize the admin panel +function initializeAdmin() { + updateStats(); + setupFilterListeners(); + updatePlayerList(); +} + +// Update header statistics +function updateStats() { + const onlinePlayers = adminData.players.filter(p => p.status === "online").length; + const onlineStaff = adminData.players.filter(p => p.status === "online" && (p.role === "admin" || p.role === "mod")).length; + + document.getElementById('playerCount').textContent = onlinePlayers; + document.getElementById('staffCount').textContent = onlineStaff; +} + +// Set up filter button listeners +function setupFilterListeners() { + const filterButtons = document.querySelectorAll('.filter-btn'); + + 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 +function filterPlayers(filter, searchTerm = '') { + let filteredPlayers = adminData.players; + + // Apply category filter + if (filter === 'online') { + filteredPlayers = filteredPlayers.filter(p => p.status === 'online'); + } else if (filter === 'staff') { + filteredPlayers = filteredPlayers.filter(p => p.role === 'admin' || p.role === 'moderator'); + } + + // 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 +function updatePlayerList(players = adminData.players) { + const playerList = document.getElementById('playerList'); + playerList.innerHTML = players.map(player => ` +
+
+ ${player.name} + ${player.role} + ${player.status} + $${player.money.toLocaleString()} +
+
+ ${player.role !== 'admin' ? ` + + ` : ''} + ${player.role !== 'player' ? ` + + ` : ''} + + +
+
+ `).join(''); +} + +// Role management functions +function promotePlayer(playerId) { + const player = adminData.players.find(p => p.id === playerId); + if (player) { + if (player.role === 'player') { + player.role = 'moderator'; + } else if (player.role === 'moderator') { + player.role = 'admin'; + } + updatePlayerList(); + } +} + +function demotePlayer(playerId) { + const player = adminData.players.find(p => p.id === playerId); + if (player) { + if (player.role === 'admin') { + player.role = 'moderator'; + } else if (player.role === 'moderator') { + player.role = 'player'; + } + updatePlayerList(); + } +} + +// Money management functions +function openMoneyModal(playerId) { + selectedPlayerId = playerId; + const modal = document.getElementById('moneyModal'); + modal.style.display = 'block'; +} + +function closeMoneyModal() { + const modal = document.getElementById('moneyModal'); + modal.style.display = 'none'; + document.getElementById('moneyAmount').value = ''; + selectedPlayerId = null; +} + +function giveMoney() { + const amount = parseInt(document.getElementById('moneyAmount').value); + if (amount && selectedPlayerId) { + const player = adminData.players.find(p => p.id === selectedPlayerId); + if (player) { + player.money += amount; + updatePlayerList(); + closeMoneyModal(); + } + } +} + +function takeMoney() { + const amount = parseInt(document.getElementById('moneyAmount').value); + if (amount && selectedPlayerId) { + const player = adminData.players.find(p => p.id === selectedPlayerId); + if (player) { + player.money = Math.max(0, player.money - amount); + updatePlayerList(); + closeMoneyModal(); + } + } +} + +// Message system functions +function openMessageModal(playerId) { + selectedPlayerId = playerId; + const player = adminData.players.find(p => p.id === playerId); + const modal = document.getElementById('messageModal'); + document.getElementById('messagePlayerName').textContent = player.name; + modal.style.display = 'block'; +} + +function closeMessageModal() { + const modal = document.getElementById('messageModal'); + modal.style.display = 'none'; + document.getElementById('messageInput').value = ''; + selectedPlayerId = null; +} + +function sendPlayerMessage() { + const message = document.getElementById('messageInput').value; + if (message && selectedPlayerId) { + const player = adminData.players.find(p => p.id === selectedPlayerId); + if (player) { + console.log(`Message sent to ${player.name}: ${message}`); + closeMessageModal(); + } + } +} + +function broadcastMessage() { + const message = document.getElementById('broadcastMessage').value; + if (message) { + console.log(`Broadcasting message to all players: ${message}`); + document.getElementById('broadcastMessage').value = ''; + } +} + +// Global actions +function triggerPayday() { + const amount = parseInt(document.getElementById('paydayAmount').value); + if (amount) { + adminData.players.forEach(player => { + player.money += amount; + }); + updatePlayerList(); + } +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', initializeAdmin); diff --git a/addons/admin/ui/_site/styles.css b/addons/admin/ui/_site/styles.css new file mode 100644 index 0000000..9539122 --- /dev/null +++ b/addons/admin/ui/_site/styles.css @@ -0,0 +1,313 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + background-color: #f4f4f4; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + background-color: #333; + color: white; + padding: 1rem 0; + margin-bottom: 2rem; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.admin-stats { + display: flex; + gap: 2rem; +} + +.stat-item { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.1); + padding: 0.5rem 1rem; + border-radius: 4px; +} + +.sections-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + padding: 1rem; +} + +.action-sections { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; +} + +.admin-section { + background-color: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.player-list-section { + grid-column: span 2; + grid-row: span 2; +} + +.player-list { + list-style: none; + overflow-y: auto; + flex-grow: 1; + height: 0; + min-height: 400px; +} + +.player-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border-bottom: 1px solid #eee; + transition: background-color 0.3s; +} + +.player-item:hover { + background-color: #f8f9fa; +} + +.player-info { + display: flex; + align-items: center; + gap: 1rem; + flex: 1; +} + +.player-name { + font-weight: 500; +} + +.player-role { + font-size: 0.875rem; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-weight: bold; +} + +.role-admin { + background-color: #dc3545; + color: white; +} + +.role-mod { + background-color: #ffc107; + color: black; +} + +.role-player { + background-color: #28a745; + color: white; +} + +.player-money { + font-size: 0.875rem; + color: #28a745; +} + +.player-actions { + display: flex; + gap: 0.5rem; +} + +.action-btn { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: 500; + transition: background-color 0.3s; +} + +.action-btn:disabled { + background-color: #6c757d; + cursor: not-allowed; + opacity: 0.65; +} + +.promote-btn { + background-color: #28a745; + color: white; +} + +.promote-btn:hover:not(:disabled) { + background-color: #218838; +} + +.demote-btn { + background-color: #dc3545; + color: white; +} + +.demote-btn:hover:not(:disabled) { + background-color: #c82333; +} + +.message-btn { + background-color: #007bff; + color: white; +} + +.message-btn:hover:not(:disabled) { + background-color: #0056b3; +} + +.search-bar { + display: flex; + gap: 1rem; + margin-bottom: 1rem; +} + +.search-input { + flex: 1; + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.form-group label { + font-weight: 500; + color: #333; +} + +.form-group input { + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; +} + +.submit-btn { + background-color: #007bff; + color: white; + padding: 0.75rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: bold; + transition: background-color 0.3s; +} + +.submit-btn:hover { + background-color: #0056b3; +} + +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1000; +} + +.modal-content { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: white; + padding: 2rem; + border-radius: 8px; + min-width: 400px; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.modal-header h2 { + margin: 0; +} + +.close-modal { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #666; +} + +.close-modal:hover { + color: #333; +} + +.badge { + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.875rem; + font-weight: bold; +} + +.badge-online { + background-color: #28a745; + color: white; +} + +.badge-offline { + background-color: #dc3545; + color: white; +} + +.filter-bar { + display: flex; + gap: 1rem; + margin-bottom: 1rem; +} + +.filter-btn { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + background-color: #444; + color: white; + transition: background-color 0.3s; +} + +.filter-btn:hover { + background-color: #666; +} + +.filter-btn.active { + background-color: #007bff; +} diff --git a/addons/bank/ui/_site/index.html b/addons/bank/ui/_site/index.html new file mode 100644 index 0000000..6a6f867 --- /dev/null +++ b/addons/bank/ui/_site/index.html @@ -0,0 +1,99 @@ + + + + + + FORGE - FDIC + + + + +
+
+
+

Federal Deposit Insurance Corporation

+
+
+ 💰 +
+
Wallet
+
$0
+
+
+
+ 🏦 +
+
Account
+
$0
+
+
+
+
+
+
+ +
+
+
+

Transfer Money

+
+
+ + +
+
+ + +
+ +
+
+ + +
+

Transfer to Player

+
+
+ + +
+
+ + +
+ +
+
+ + +
+

Submit Timesheet

+
+
+ + +
+
+ + +
+ +
+
+
+ + +
+

Transaction History

+ +
+
+ + diff --git a/addons/bank/ui/_site/public/fdic.png b/addons/bank/ui/_site/public/fdic.png new file mode 100644 index 0000000..7845bec Binary files /dev/null and b/addons/bank/ui/_site/public/fdic.png differ diff --git a/addons/bank/ui/_site/public/fdic_co.paa b/addons/bank/ui/_site/public/fdic_co.paa new file mode 100644 index 0000000..45fe964 Binary files /dev/null and b/addons/bank/ui/_site/public/fdic_co.paa differ diff --git a/addons/bank/ui/_site/public/fms.png b/addons/bank/ui/_site/public/fms.png new file mode 100644 index 0000000..553b09a Binary files /dev/null and b/addons/bank/ui/_site/public/fms.png differ diff --git a/addons/bank/ui/_site/public/fms_co.paa b/addons/bank/ui/_site/public/fms_co.paa new file mode 100644 index 0000000..9d32a24 Binary files /dev/null and b/addons/bank/ui/_site/public/fms_co.paa differ diff --git a/addons/bank/ui/_site/public/gms.png b/addons/bank/ui/_site/public/gms.png new file mode 100644 index 0000000..a4717b2 Binary files /dev/null and b/addons/bank/ui/_site/public/gms.png differ diff --git a/addons/bank/ui/_site/public/gms_co.paa b/addons/bank/ui/_site/public/gms_co.paa new file mode 100644 index 0000000..ddc6d35 Binary files /dev/null and b/addons/bank/ui/_site/public/gms_co.paa differ diff --git a/addons/bank/ui/_site/script.js b/addons/bank/ui/_site/script.js new file mode 100644 index 0000000..21d9dcd --- /dev/null +++ b/addons/bank/ui/_site/script.js @@ -0,0 +1,147 @@ +// Simulated data - this would be replaced with actual game data +let bankState = { + wallet: 1000, + account: 5000, + players: [ + { id: 1, name: "Player 1" }, + { id: 2, name: "Player 2" }, + { id: 3, name: "Player 3" } + ], + transactions: [] +}; + +// Initialize the interface +function initializeBank() { + updateBalanceDisplays(); + populatePlayerList(); + setupEventListeners(); + loadTransactionHistory(); +} + +// Update balance displays in the header +function updateBalanceDisplays() { + document.getElementById('walletBalance').textContent = `$${bankState.wallet.toLocaleString()}`; + document.getElementById('accountBalance').textContent = `$${bankState.account.toLocaleString()}`; +} + +// Populate the player selection dropdown +function populatePlayerList() { + const playerSelect = document.getElementById('playerSelect'); + playerSelect.innerHTML = ''; + + bankState.players.forEach(player => { + const option = document.createElement('option'); + option.value = player.id; + option.textContent = player.name; + playerSelect.appendChild(option); + }); +} + +// Add a new transaction to history +function addTransaction(type, amount, details = '') { + const transaction = { + type, + amount, + details, + timestamp: new Date().toISOString() + }; + + bankState.transactions.unshift(transaction); + updateTransactionHistory(); +} + +// Update the transaction history display +function updateTransactionHistory() { + const historyList = document.getElementById('transactionHistory'); + historyList.innerHTML = ''; + + bankState.transactions.forEach(transaction => { + const li = document.createElement('li'); + li.className = 'history-item'; + + const isNegative = ['transfer_out', 'to_wallet'].includes(transaction.type); + const amountClass = isNegative ? 'amount-negative' : 'amount-positive'; + const amountPrefix = isNegative ? '-' : '+'; + + li.innerHTML = ` + ${formatTransactionType(transaction.type)} + ${transaction.details} + ${amountPrefix}$${Math.abs(transaction.amount).toLocaleString()} + `; + + historyList.appendChild(li); + }); +} + +// Format transaction type for display +function formatTransactionType(type) { + const types = { + 'to_wallet': 'To Wallet', + 'to_account': 'To Account', + 'transfer_out': 'Transfer Out', + 'transfer_in': 'Transfer In', + 'timesheet': 'Timesheet' + }; + return types[type] || type; +} + +// Set up all form event listeners +function setupEventListeners() { // Handle transfers between wallet and account + document.getElementById('transferForm').addEventListener('submit', (e) => { + e.preventDefault(); + const amount = parseInt(document.getElementById('transferAmount').value); + const transferType = document.getElementById('transferType').value; + + if (transferType === 'to_wallet') { + if (amount > bankState.account) { + alert('Insufficient funds in account'); + return; + } + bankState.account -= amount; + bankState.wallet += amount; + } else { + if (amount > bankState.wallet) { + alert('Insufficient funds in wallet'); + return; + } + bankState.wallet -= amount; + bankState.account += amount; + } + + addTransaction(transferType, amount); + updateBalanceDisplays(); + e.target.reset(); + }); // Transfer to Player + document.getElementById('transferPlayerForm').addEventListener('submit', (e) => { + e.preventDefault(); + const amount = parseInt(document.getElementById('playerTransferAmount').value); + const playerId = document.getElementById('playerSelect').value; + const playerName = bankState.players.find(p => p.id.toString() === playerId)?.name; + + if (amount > bankState.account) { + alert('Insufficient funds in account'); + return; + } + + bankState.account -= amount; + addTransaction('transfer_out', amount, `To ${playerName}`); + updateBalanceDisplays(); + e.target.reset(); + }); + + // Submit Timesheet + document.getElementById('timesheetForm').addEventListener('submit', (e) => { + e.preventDefault(); + const hours = parseFloat(document.getElementById('hoursWorked').value); + const rate = parseInt(document.getElementById('hourlyRate').value); + const amount = Math.floor(hours * rate); + + bankState.account += amount; + addTransaction('timesheet', amount, `${hours} hours @ $${rate}/hr`); + updateBalanceDisplays(); + e.target.reset(); + }); +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', initializeBank); diff --git a/addons/bank/ui/_site/styles.css b/addons/bank/ui/_site/styles.css new file mode 100644 index 0000000..1486173 --- /dev/null +++ b/addons/bank/ui/_site/styles.css @@ -0,0 +1,163 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + background-color: #f4f4f4; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + background-color: #333; + color: white; + padding: 1rem 0; + margin-bottom: 2rem; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.balance-display { + display: flex; + gap: 2rem; + margin-left: auto; +} + +.balance-item { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.1); + padding: 0.5rem 1rem; + border-radius: 4px; +} + +.balance-item i { + font-size: 1.2rem; +} + +.actions-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + padding: 1rem; +} + +.action-tile { + background-color: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + transition: transform 0.3s; + aspect-ratio: 1; + padding: 2rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.action-tile:hover { + transform: translateY(-5px); +} + +.action-tile h2 { + font-size: 1.5rem; + color: #333; + margin-bottom: 1rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.form-group label { + font-weight: bold; + color: #555; +} + +.form-group input, .form-group select { + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; +} + +.submit-btn { + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + padding: 0.75rem 1rem; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.3s; + margin-top: auto; +} + +.submit-btn:hover { + background-color: #0056b3; +} + +.history-section { + background: white; + border-radius: 8px; + padding: 2rem; + margin-top: 2rem; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); +} + +.history-list { + list-style: none; + margin-top: 1rem; +} + +.history-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border-bottom: 1px solid #eee; +} + +.history-item:last-child { + border-bottom: none; +} + +.transaction-type { + font-weight: bold; +} + +.amount-positive { + color: #28a745; +} + +.amount-negative { + color: #dc3545; +} + +.error-message { + color: #dc3545; + font-size: 0.875rem; + margin-top: 0.25rem; +} + +.success-message { + color: #28a745; + font-size: 0.875rem; + margin-top: 0.25rem; +} diff --git a/addons/garage/ui/_site/index.html b/addons/garage/ui/_site/index.html new file mode 100644 index 0000000..1d73626 --- /dev/null +++ b/addons/garage/ui/_site/index.html @@ -0,0 +1,45 @@ + + + + + + FORGE - Garage + + + + +
+
+
+

Garage

+
+
+ 🚗 +
+
Vehicles
+
0
+
+
+
+ 🔧 +
+
In Maintenance
+
0
+
+
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+ + diff --git a/addons/garage/ui/_site/script.js b/addons/garage/ui/_site/script.js new file mode 100644 index 0000000..a0d882f --- /dev/null +++ b/addons/garage/ui/_site/script.js @@ -0,0 +1,184 @@ +// Simulated garage data - this would be replaced with actual game data +let garageData = { + vehicles: [ + { + id: 1, + name: "Offroad Vehicle", + category: "Land", + type: "Transport", + status: "available", + fuel: 100, + damage: 0, + maintenance: 95, + lastUsed: "2025-04-10T15:30:00", + image: "placeholder.jpg" + }, + { + id: 2, + name: "Transport Helicopter", + category: "Air", + type: "Transport", + status: "in-use", + fuel: 75, + damage: 15, + maintenance: 80, + lastUsed: "2025-04-12T09:15:00", + image: "placeholder.jpg" + }, + { + id: 3, + name: "Patrol Boat", + category: "Sea", + type: "Patrol", + status: "maintenance", + fuel: 50, + damage: 35, + maintenance: 45, + lastUsed: "2025-04-11T18:45:00", + image: "placeholder.jpg" + }, + { + id: 4, + name: "Armed SUV", + category: "Land", + type: "Combat", + status: "available", + fuel: 90, + damage: 5, + maintenance: 88, + lastUsed: "2025-04-12T11:20:00", + image: "placeholder.jpg" + } + ] +}; + +// Initialize the garage interface +function initializeGarage() { + updateStats(); + setupCategoryFilters(); + displayVehicles(); +} + +// Update garage statistics +function updateStats() { + const totalVehicles = garageData.vehicles.length; + const inMaintenance = garageData.vehicles.filter(v => v.status === 'maintenance').length; + + document.getElementById('vehicleCount').textContent = totalVehicles; + document.getElementById('maintenanceCount').textContent = inMaintenance; +} + +// Set up category filters +function setupCategoryFilters() { + const categories = ['All', ...new Set(garageData.vehicles.map(v => v.category))]; + const filtersContainer = document.getElementById('categoryFilters'); + + categories.forEach(category => { + const button = document.createElement('button'); + button.className = 'filter-btn' + (category === 'All' ? ' active' : ''); + button.textContent = category; + button.addEventListener('click', () => filterVehicles(category)); + filtersContainer.appendChild(button); + }); +} + +// Filter vehicles by category +function filterVehicles(category) { + // Update active filter button + document.querySelectorAll('.filter-btn').forEach(btn => { + btn.classList.toggle('active', btn.textContent === category); + }); + + // Filter and display vehicles + const filteredVehicles = category === 'All' + ? garageData.vehicles + : garageData.vehicles.filter(v => v.category === category); + + displayVehicles(filteredVehicles); +} + +// Display vehicles in the grid +function displayVehicles(vehicles = garageData.vehicles) { + const grid = document.getElementById('vehiclesGrid'); + grid.innerHTML = ''; + + vehicles.forEach(vehicle => { + const card = document.createElement('div'); + card.className = 'vehicle-card'; + + const statusText = { + 'available': 'Available', + 'in-use': 'In Use', + 'maintenance': 'Maintenance' + }[vehicle.status]; + + card.innerHTML = ` +
+
+
+
${vehicle.name}
+ ${statusText} +
+
+
+ Type + ${vehicle.type} +
+
+ Category + ${vehicle.category} +
+
+ Fuel +
+ + ${vehicle.fuel}% +
+
+
+ Condition +
+ + ${vehicle.maintenance}% +
+
+
+
+ +
+
+ `; + + grid.appendChild(card); + }); +} + +// Get color class for fuel status +function getFuelStatusColor(fuelLevel) { + if (fuelLevel > 66) return 'stat-green'; + if (fuelLevel > 33) return 'stat-yellow'; + return 'stat-red'; +} + +// Get color class for vehicle condition +function getConditionStatusColor(condition) { + if (condition > 66) return 'stat-green'; + if (condition > 33) return 'stat-yellow'; + return 'stat-red'; +} + +// Handle vehicle spawn +function spawnVehicle(vehicleId) { + const vehicle = garageData.vehicles.find(v => v.id === vehicleId); + if (vehicle && vehicle.status === 'available') { + alert(`Spawning vehicle: ${vehicle.name}`); + // Here you would typically integrate with your game's vehicle spawning system + } +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', initializeGarage); diff --git a/addons/garage/ui/_site/styles.css b/addons/garage/ui/_site/styles.css new file mode 100644 index 0000000..8411181 --- /dev/null +++ b/addons/garage/ui/_site/styles.css @@ -0,0 +1,233 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + background-color: #f4f4f4; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + background-color: #333; + color: white; + padding: 1rem 0; + margin-bottom: 2rem; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.garage-stats { + display: flex; + gap: 2rem; +} + +.stat-item { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.1); + padding: 0.5rem 1rem; + border-radius: 4px; +} + +.vehicles-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + padding: 1rem; +} + +.vehicle-card { + background-color: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + transition: transform 0.3s; + display: flex; + flex-direction: column; +} + +.vehicle-card:hover { + transform: translateY(-5px); +} + +.vehicle-image { + width: 100%; + height: 200px; + background-color: #eee; + background-size: cover; + background-position: center; +} + +.vehicle-info { + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.vehicle-header { + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.vehicle-name { + font-size: 1.2rem; + font-weight: bold; + color: #333; +} + +.vehicle-status { + font-size: 0.875rem; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-weight: bold; +} + +.status-available { + background-color: #28a745; + color: white; +} + +.status-in-use { + background-color: #dc3545; + color: white; +} + +.status-maintenance { + background-color: #ffc107; + color: black; +} + +.vehicle-details { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +.detail-item { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.detail-label { + font-size: 0.875rem; + color: #666; +} + +.detail-value { + font-weight: bold; + color: #333; +} + +.vehicle-actions { + display: flex; + gap: 1rem; + margin-top: auto; + padding-top: 1rem; + border-top: 1px solid #eee; +} + +.action-btn { + flex: 1; + padding: 0.75rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: bold; + transition: background-color 0.3s; +} + +.spawn-btn { + background-color: #007bff; + color: white; +} + +.spawn-btn:hover { + background-color: #0056b3; +} + +.spawn-btn:disabled { + background-color: #6c757d; + cursor: not-allowed; + opacity: 0.65; +} + +.spawn-btn:disabled:hover { + background-color: #6c757d; +} + +.maintain-btn { + background-color: #ffc107; + color: black; +} + +.maintain-btn:hover { + background-color: #d39e00; +} + +.category-filters { + display: flex; + gap: 1rem; + margin-bottom: 2rem; + flex-wrap: wrap; +} + +.filter-btn { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + background-color: #444; + color: white; + transition: background-color 0.3s; +} + +.filter-btn:hover { + background-color: #666; +} + +.filter-btn.active { + background-color: #007bff; +} + +.vehicle-stats { + display: flex; + gap: 0.5rem; + align-items: center; + color: #666; + font-size: 0.875rem; +} + +.stat-dot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.stat-green { + background-color: #28a745; +} + +.stat-yellow { + background-color: #ffc107; +} + +.stat-red { + background-color: #dc3545; +} diff --git a/addons/locker/ui/_site/index.html b/addons/locker/ui/_site/index.html new file mode 100644 index 0000000..beeca72 --- /dev/null +++ b/addons/locker/ui/_site/index.html @@ -0,0 +1,74 @@ + + + + + + FORGE - Locker + + + + +
+
+
+

Locker

+
+
+ 📦 +
+
Storage Space
+
0/100
+
+
+
+ 🎒 +
+
Items Stored
+
0
+
+
+
+
+
+
+ +
+
+ + + + + +
+ +
+ +
+

Current Equipment

+
+ +
+
+ + +
+

Stored Equipment

+
+ +
+
+ + +
+

Equipment Details

+
+ +
+ Select an item to view its details +
+
+
+
+
+ + diff --git a/addons/locker/ui/_site/script.js b/addons/locker/ui/_site/script.js new file mode 100644 index 0000000..b2c8ac1 --- /dev/null +++ b/addons/locker/ui/_site/script.js @@ -0,0 +1,236 @@ +// Simulated locker data - this would be replaced with actual game data +let lockerData = { + storageSpace: { + used: 45, + total: 100 + }, + playerEquipment: [ + { + id: 1, + name: "Combat Uniform", + type: "clothing", + category: "Uniform", + condition: 95 + }, + { + id: 2, + name: "Tactical Vest", + type: "clothing", + category: "Vest", + condition: 88 + }, + { + id: 3, + name: "Combat Backpack", + type: "clothing", + category: "Backpack", + condition: 90 + }, + { + id: 4, + name: "Assault Rifle", + type: "weapons", + category: "Primary", + condition: 92, + attachments: ["Scope", "Grip"] + } + ], + storedItems: [ + { + id: 5, + name: "Pistol", + type: "weapons", + category: "Secondary", + condition: 100 + }, + { + id: 6, + name: "Medical Kit", + type: "equipment", + category: "Medical", + quantity: 3 + }, + { + id: 7, + name: "Rifle Magazines", + type: "magazines", + category: "Magazine", + quantity: 5 + }, + { + id: 8, + name: "Combat Helmet", + type: "clothing", + category: "Headgear", + condition: 85 + } + ] +}; + +// Initialize the locker interface +function initializeLocker() { + updateStats(); + setupFilterListeners(); + updateEquipmentLists(); +} + +// Update storage statistics +function updateStats() { + document.getElementById('storageSpace').textContent = + `${lockerData.storageSpace.used}/${lockerData.storageSpace.total}`; + document.getElementById('itemCount').textContent = + lockerData.storedItems.length; +} + +// Set up category filter listeners +function setupFilterListeners() { + const filterButtons = document.querySelectorAll('.filter-btn'); + + filterButtons.forEach(button => { + button.addEventListener('click', (e) => { + // Update active state + filterButtons.forEach(btn => btn.classList.remove('active')); + button.classList.add('active'); + + // Filter items + const category = button.dataset.category; + filterEquipment(category); + }); + }); +} + +// Filter equipment by category +function filterEquipment(category) { + const playerItems = category === 'all' + ? lockerData.playerEquipment + : lockerData.playerEquipment.filter(item => item.type === category); + + const storedItems = category === 'all' + ? lockerData.storedItems + : lockerData.storedItems.filter(item => item.type === category); + + updateEquipmentLists(playerItems, storedItems); +} + +// Update equipment lists display +function updateEquipmentLists(playerItems = lockerData.playerEquipment, storedItems = lockerData.storedItems) { + const playerList = document.getElementById('playerEquipment'); + const storedList = document.getElementById('storedEquipment'); + + // Update player equipment + playerList.innerHTML = playerItems.map(item => ` +
+
+ ${item.name} + ${item.category} + ${item.condition ? ` + + ${item.condition}% + + ` : ''} +
+ +
+ `).join(''); + + // Update stored equipment + storedList.innerHTML = storedItems.map(item => ` +
+
+ ${item.name} + ${item.category} + ${item.quantity ? ` + + x${item.quantity} + + ` : item.condition ? ` + + ${item.condition}% + + ` : ''} +
+ +
+ `).join(''); + + // Add click listeners for showing details + document.querySelectorAll('.equipment-item').forEach(item => { + item.addEventListener('click', () => showItemDetails(item.dataset.id)); + }); +} + +// Show item details +function showItemDetails(itemId) { + const item = lockerData.playerEquipment.find(i => i.id === parseInt(itemId)) || + lockerData.storedItems.find(i => i.id === parseInt(itemId)); + + if (!item) return; + + const detailsDiv = document.getElementById('equipmentDetails'); + detailsDiv.innerHTML = ` +

${item.name}

+
+
+ Category: + ${item.category} +
+
+ Type: + ${item.type} +
+ ${item.condition ? ` +
+ Condition: + ${item.condition}% +
+ ` : ''} + ${item.quantity ? ` +
+ Quantity: + x${item.quantity} +
+ ` : ''} + ${item.attachments ? ` +
+ Attachments: + ${item.attachments.join(', ')} +
+ ` : ''} +
+ `; +} + +// Store an item in the locker +function storeItem(itemId) { + const itemIndex = lockerData.playerEquipment.findIndex(item => item.id === itemId); + if (itemIndex === -1) return; + + const item = lockerData.playerEquipment[itemIndex]; + lockerData.playerEquipment.splice(itemIndex, 1); + lockerData.storedItems.push(item); + lockerData.storageSpace.used += 1; + + updateStats(); + updateEquipmentLists(); +} + +// Equip an item from the locker +function equipItem(itemId) { + const itemIndex = lockerData.storedItems.findIndex(item => item.id === itemId); + if (itemIndex === -1) return; + + const item = lockerData.storedItems[itemIndex]; + lockerData.storedItems.splice(itemIndex, 1); + lockerData.playerEquipment.push(item); + lockerData.storageSpace.used -= 1; + + updateStats(); + updateEquipmentLists(); +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', initializeLocker); diff --git a/addons/locker/ui/_site/styles.css b/addons/locker/ui/_site/styles.css new file mode 100644 index 0000000..eb5a7ab --- /dev/null +++ b/addons/locker/ui/_site/styles.css @@ -0,0 +1,194 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + background-color: #f4f4f4; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + background-color: #333; + color: white; + padding: 1rem 0; + margin-bottom: 2rem; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.storage-stats { + display: flex; + gap: 2rem; +} + +.stat-item { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.1); + padding: 0.5rem 1rem; + border-radius: 4px; +} + +.sections-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: auto auto; + gap: 2rem; + padding: 1rem; + min-height: calc(100vh - 200px); /* Account for header and padding */ +} + +.equipment-section { + background-color: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + transition: transform 0.3s; + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + max-height: 100%; +} + +.equipment-section h2 { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 1.2rem; + color: #333; +} + +.equipment-list { + list-style: none; + overflow-y: auto; + flex-grow: 1; + height: 0; /* This forces the flex-grow to work properly */ + min-height: 400px; +} + +.equipment-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem; + border-bottom: 1px solid #eee; + transition: background-color 0.3s; +} + +.equipment-item:hover { + background-color: #f8f9fa; +} + +.equipment-item:last-child { + border-bottom: none; +} + +.item-info { + display: flex; + align-items: center; + gap: 1rem; +} + +.item-name { + font-weight: 500; +} + +.item-type { + font-size: 0.875rem; + color: #666; +} + +.item-quantity { + font-size: 0.875rem; + color: #28a745; + background: rgba(40, 167, 69, 0.1); + padding: 0.2rem 0.5rem; + border-radius: 4px; +} + +.item-actions { + display: flex; + gap: 0.5rem; +} + +.action-btn { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: bold; + transition: background-color 0.3s; +} + +.store-btn { + background-color: #007bff; + color: white; +} + +.store-btn:hover { + background-color: #0056b3; +} + +.equip-btn { + background-color: #28a745; + color: white; +} + +.equip-btn:hover { + background-color: #218838; +} + +.equip-btn:disabled { + background-color: #6c757d; + cursor: not-allowed; + opacity: 0.65; +} + +.category-filters { + display: flex; + gap: 1rem; + margin-bottom: 2rem; + flex-wrap: wrap; +} + +.filter-btn { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + background-color: #444; + color: white; + transition: background-color 0.3s; +} + +.filter-btn:hover { + background-color: #666; +} + +.filter-btn.active { + background-color: #007bff; +} + +.equipment-section.player-equipment { + grid-column: span 2; +} + +.equipment-section.stored-equipment { + grid-column: span 1; + grid-row: span 2; +} diff --git a/addons/org/ui/BaseControl.hpp b/addons/org/ui/BaseControl.hpp new file mode 100644 index 0000000..e69de29 diff --git a/addons/org/ui/_site/index.html b/addons/org/ui/_site/index.html new file mode 100644 index 0000000..1be3c64 --- /dev/null +++ b/addons/org/ui/_site/index.html @@ -0,0 +1,90 @@ + + + + + + FORGE - ORG + + + + +
+
+
+

Organization Name

+
+
+ +
+
Reputation
+
0
+
+
+
+ 💰 +
+
Funds
+
$0
+
+
+
+
+
+
+ +
+
+ +
+

Members

+
    + +
+
+ + +
+

Vehicles

+
    + +
+
+ + +
+

Equipment

+
    + +
+
+
+

Properties

+
    + +
+
+
+

Supplies

+
    + +
+
+
+

Transactions

+
    + +
+
+
+

Memos & Logs

+
+ +
+
    + +
+
+
+
+ + diff --git a/addons/org/ui/_site/public/fdic.png b/addons/org/ui/_site/public/fdic.png new file mode 100644 index 0000000..7845bec Binary files /dev/null and b/addons/org/ui/_site/public/fdic.png differ diff --git a/addons/org/ui/_site/public/fdic_co.paa b/addons/org/ui/_site/public/fdic_co.paa new file mode 100644 index 0000000..45fe964 Binary files /dev/null and b/addons/org/ui/_site/public/fdic_co.paa differ diff --git a/addons/org/ui/_site/public/fms.png b/addons/org/ui/_site/public/fms.png new file mode 100644 index 0000000..553b09a Binary files /dev/null and b/addons/org/ui/_site/public/fms.png differ diff --git a/addons/org/ui/_site/public/fms_co.paa b/addons/org/ui/_site/public/fms_co.paa new file mode 100644 index 0000000..9d32a24 Binary files /dev/null and b/addons/org/ui/_site/public/fms_co.paa differ diff --git a/addons/org/ui/_site/public/gms.png b/addons/org/ui/_site/public/gms.png new file mode 100644 index 0000000..a4717b2 Binary files /dev/null and b/addons/org/ui/_site/public/gms.png differ diff --git a/addons/org/ui/_site/public/gms_co.paa b/addons/org/ui/_site/public/gms_co.paa new file mode 100644 index 0000000..ddc6d35 Binary files /dev/null and b/addons/org/ui/_site/public/gms_co.paa differ diff --git a/addons/org/ui/_site/script.js b/addons/org/ui/_site/script.js new file mode 100644 index 0000000..dbe3198 --- /dev/null +++ b/addons/org/ui/_site/script.js @@ -0,0 +1,302 @@ +// Simulated organization data - this would be replaced with actual game data +let orgData = { + name: "Black Rifle Company", + reputation: 1250, + funds: 75000, + transactions: [ + { id: 1, type: "deposit", amount: 5000, description: "Weekly income", date: "2025-04-12T10:30:00" }, + { id: 2, type: "withdrawal", amount: -2000, description: "Vehicle maintenance", date: "2025-04-11T15:45:00" }, + { id: 3, type: "deposit", amount: 3000, description: "Property rent", date: "2025-04-10T09:15:00" }, + { id: 4, type: "withdrawal", amount: -1500, description: "Equipment purchase", date: "2025-04-09T14:20:00" } + ], + members: [ + { id: 1, name: "John Doe", role: "owner", status: "online" }, + { id: 2, name: "Jane Smith", role: "admin", status: "online" }, + { id: 3, name: "Mike Johnson", role: "member", status: "offline" }, + { id: 4, name: "Sarah Wilson", role: "member", status: "online" } + ], + vehicles: [ + { id: 1, name: "Transport Truck", type: "Vehicle", value: 25000 }, + { id: 2, name: "Patrol Car", type: "Vehicle", value: 15000 } + ], + equipment: [ + { id: 1, name: "Combat Gear Set", type: "Equipment", value: 5000 }, + { id: 3, name: "Radio Equipment", type: "Equipment", value: 3000 } + ], + properties: [ + { id: 1, name: "Main Base", type: "Property", value: 100000 }, + { id: 2, name: "Storage Facility", type: "Property", value: 50000 } + ], + supplies: [ + { id: 1, name: "Medical Supplies", type: "Supply", value: 2000, quantity: 50 }, + { id: 2, name: "Ammunition", type: "Supply", value: 5000, quantity: 1000 }, + { id: 3, name: "Food Rations", type: "Supply", value: 1000, quantity: 100 }, + { id: 4, name: "Repair Kits", type: "Supply", value: 3000, quantity: 25 } + ], + memos: [ + { + id: 1, + title: "Weekly Mission Update", + content: "New assignments available in the northern sector. All teams please check your mission boards.", + author: "John Doe", + date: "2025-04-12T08:30:00", + priority: "high" + }, + { + id: 2, + title: "Equipment Maintenance", + content: "Regular maintenance check required for all vehicles by end of week.", + author: "Jane Smith", + date: "2025-04-11T14:15:00", + priority: "medium" + } + ] +}; + +// Initialize the organization interface +function initializeOrg() { + updateOrgInfo(); + updateMembers(); + updateAssets(); + updateTransactions(); + updateMemos(); + setupMemoControls(); +} + +// Update organization info in the header +function updateOrgInfo() { + document.getElementById('orgName').textContent = orgData.name; + document.getElementById('orgReputation').textContent = orgData.reputation.toLocaleString(); + document.getElementById('orgFunds').textContent = `$${orgData.funds.toLocaleString()}`; +} + +// Update members list +function updateMembers() { + const membersList = document.getElementById('membersList'); + membersList.innerHTML = ''; + + orgData.members.forEach(member => { + const li = document.createElement('li'); + li.className = 'member-item'; + + li.innerHTML = ` +
+ + ${member.name} + ${member.role} +
+ `; + + membersList.appendChild(li); + }); +} + +// Update assets lists (vehicles, equipment, properties) +function updateAssets() { + // Update vehicles + const vehiclesList = document.getElementById('vehiclesList'); + vehiclesList.innerHTML = ''; + orgData.vehicles.forEach(vehicle => { + const li = document.createElement('li'); + li.className = 'asset-item'; + li.innerHTML = ` +
+ ${vehicle.name} + ${vehicle.type} +
+ $${vehicle.value.toLocaleString()} + `; + vehiclesList.appendChild(li); + }); + + // Update equipment + const equipmentList = document.getElementById('equipmentList'); + equipmentList.innerHTML = ''; + orgData.equipment.forEach(item => { + const li = document.createElement('li'); + li.className = 'asset-item'; + li.innerHTML = ` +
+ ${item.name} + ${item.type} +
+ $${item.value.toLocaleString()} + `; + equipmentList.appendChild(li); + }); // Update properties + const propertiesList = document.getElementById('propertiesList'); + propertiesList.innerHTML = ''; + orgData.properties.forEach(property => { + const li = document.createElement('li'); + li.className = 'asset-item'; + li.innerHTML = ` +
+ ${property.name} + ${property.type} +
+ $${property.value.toLocaleString()} + `; + propertiesList.appendChild(li); + }); + + // Update supplies + const suppliesList = document.getElementById('suppliesList'); + suppliesList.innerHTML = ''; + orgData.supplies.forEach(supply => { + const li = document.createElement('li'); + li.className = 'asset-item'; + li.innerHTML = ` +
+ ${supply.name} +
+ ${supply.type} + Qty: ${supply.quantity} +
+
+ $${supply.value.toLocaleString()} + `; + suppliesList.appendChild(li); + }); +} + +// Update transactions list +function updateTransactions() { + const transactionsList = document.getElementById('transactionsList'); + transactionsList.innerHTML = ''; + + orgData.transactions.forEach(transaction => { + const li = document.createElement('li'); + li.className = 'transaction-item'; + + const date = new Date(transaction.date); + const formattedDate = date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + + const amountClass = transaction.amount >= 0 ? 'amount-positive' : 'amount-negative'; + const prefix = transaction.amount >= 0 ? '+' : ''; + + li.innerHTML = ` +
+
${transaction.description}
+
${formattedDate}
+
+ ${prefix}$${Math.abs(transaction.amount).toLocaleString()} + `; + + transactionsList.appendChild(li); + }); +} + +// Update memos list +function updateMemos() { + const memosList = document.getElementById('memosList'); + memosList.innerHTML = ''; + + orgData.memos.forEach(memo => { + const li = document.createElement('li'); + li.className = 'memo-item'; + + const date = new Date(memo.date); + const formattedDate = date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + + li.innerHTML = ` +
+
${memo.title}
+
+ by ${memo.author} + ${formattedDate} +
+
+
${memo.content}
+ `; + + memosList.appendChild(li); + }); +} + +// Set up memo controls and dialog +function setupMemoControls() { + const addMemoBtn = document.getElementById('addMemoBtn'); + + addMemoBtn.addEventListener('click', () => { + showMemoDialog(); + }); +} + +// Show memo creation dialog +function showMemoDialog() { + const dialog = document.createElement('div'); + dialog.className = 'memo-dialog'; + + dialog.innerHTML = ` +
+
+
+ +
+
+
+ +
+
+ + +
+
+ `; + + const overlay = document.createElement('div'); + overlay.className = 'memo-dialog-overlay'; + + document.body.appendChild(overlay); + document.body.appendChild(dialog); + + const form = dialog.querySelector('#memoForm'); + const cancelBtn = dialog.querySelector('#cancelMemo'); + + form.addEventListener('submit', (e) => { + e.preventDefault(); + + const newMemo = { + id: orgData.memos.length + 1, + title: document.getElementById('memoTitle').value, + content: document.getElementById('memoContent').value, + author: orgData.members.find(m => m.role === 'owner').name, + date: new Date().toISOString(), + priority: "normal" + }; + + orgData.memos.unshift(newMemo); + updateMemos(); + + closeMemoDialog(dialog, overlay); + }); + + cancelBtn.addEventListener('click', () => { + closeMemoDialog(dialog, overlay); + }); + + overlay.addEventListener('click', () => { + closeMemoDialog(dialog, overlay); + }); +} + +// Close memo dialog +function closeMemoDialog(dialog, overlay) { + document.body.removeChild(dialog); + document.body.removeChild(overlay); +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', initializeOrg); diff --git a/addons/org/ui/_site/styles.css b/addons/org/ui/_site/styles.css new file mode 100644 index 0000000..547a080 --- /dev/null +++ b/addons/org/ui/_site/styles.css @@ -0,0 +1,359 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + background-color: #f4f4f4; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + background-color: #333; + color: white; + padding: 1rem 0; + margin-bottom: 2rem; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.org-stats { + display: flex; + gap: 2rem; + margin-left: auto; +} + +.stat-item { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.1); + padding: 0.5rem 1rem; + border-radius: 4px; +} + +.stat-item i { + font-size: 1.2rem; +} + +.sections-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + padding: 1rem; +} + +/* Make memo section span full width */ +.section-tile.memo-section { + grid-column: 1 / -1; + aspect-ratio: unset; + min-height: 400px; +} + +.section-tile { + background-color: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + transition: transform 0.3s; + aspect-ratio: 1; + padding: 2rem; + display: flex; + flex-direction: column; +} + +.section-tile:hover { + transform: translateY(-5px); +} + +.section-tile h2 { + font-size: 1.5rem; + color: #333; + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.member-list, .asset-list { + list-style: none; + overflow-y: auto; + flex-grow: 1; +} + +.member-item, .asset-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem; + border-bottom: 1px solid #eee; +} + +.member-item:last-child, .asset-item:last-child { + border-bottom: none; +} + +.member-info, .asset-info { + display: flex; + align-items: center; + gap: 1rem; +} + +.member-role { + font-size: 0.875rem; + padding: 0.25rem 0.5rem; + border-radius: 4px; + background-color: #e9ecef; +} + +.role-owner { + background-color: #ffd700; + color: #000; +} + +.role-admin { + background-color: #dc3545; + color: white; +} + +.role-member { + background-color: #28a745; + color: white; +} + +.asset-type { + font-size: 0.875rem; + color: #666; +} + +.supply-details { + display: flex; + gap: 1rem; + align-items: center; +} + +.supply-quantity { + font-size: 0.875rem; + color: #28a745; + background: rgba(40, 167, 69, 0.1); + padding: 0.2rem 0.5rem; + border-radius: 4px; +} + +.asset-value { + color: #007bff; + font-weight: bold; +} + +.transaction-list { + list-style: none; + overflow-y: auto; + flex-grow: 1; +} + +.transaction-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem; + border-bottom: 1px solid #eee; +} + +.transaction-item:last-child { + border-bottom: none; +} + +.transaction-info { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.transaction-description { + font-size: 0.9rem; + color: #666; +} + +.transaction-date { + font-size: 0.8rem; + color: #888; +} + +.transaction-amount { + font-weight: bold; +} + +.amount-positive { + color: #28a745; +} + +.amount-negative { + color: #dc3545; +} + +.memo-controls { + margin-bottom: 1rem; + display: flex; + justify-content: flex-end; +} + +.memo-list { + list-style: none; + overflow-y: auto; + flex-grow: 1; +} + +.memo-item { + padding: 1rem; + border-bottom: 1px solid #eee; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.memo-item:last-child { + border-bottom: none; +} + +.memo-header { + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.memo-title { + font-weight: bold; + color: #333; +} + +.memo-metadata { + display: flex; + gap: 1rem; + font-size: 0.8rem; + color: #666; +} + +.memo-author { + color: #007bff; +} + +.memo-content { + color: #444; + line-height: 1.4; +} + +.memo-dialog { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + z-index: 1000; + width: 90%; + max-width: 500px; +} + +.memo-dialog-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0,0,0,0.5); + z-index: 999; +} + +.memo-form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.memo-form input { + width: 100%; + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; +} + +.memo-form textarea { + width: 100%; + min-height: 100px; + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + resize: vertical; +} + +.memo-form-buttons { + display: flex; + justify-content: flex-end; + gap: 1rem; +} + +.cancel-btn { + width: 100%; + background-color: #6c757d; + color: white; + border: none; + border-radius: 4px; + padding: 0.75rem 1rem; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.3s; +} + +.add-btn { + border: none; + border-radius: 4px; + padding: 0.75rem 1rem; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.3s; +} + +.submit-btn { + width: 100%; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + padding: 0.75rem 1rem; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.3s; +} + +.cancel-btn:hover { + background-color: #5a6268; +} + +.member-status { + width: 8px; + height: 8px; + border-radius: 50%; + margin-right: 0.5rem; +} + +.status-online { + background-color: #28a745; +} + +.status-offline { + background-color: #dc3545; +} diff --git a/addons/store/functions/fnc_buyItem.sqf b/addons/store/functions/fnc_buyItem.sqf index be265d3..0e258ce 100644 --- a/addons/store/functions/fnc_buyItem.sqf +++ b/addons/store/functions/fnc_buyItem.sqf @@ -11,13 +11,13 @@ * 0: Class Name - The classname of the item to purchase * 1: Price - The price of the item * 2: Config Type - The type of config ("item", "weapon", "magazine", "backpack") - * 3: Item Type - The type of item for locker storage + * 3: Item Type - The type of item for locker storage ("backpack", "facewear", "headgear", "hmd", "item", "magazine", "uniform", "vest", "weapon") * * Return Value: * None * * Example: - * ["arifle_MX_F", 1000, "weapon", "primary"] call forge_store_fnc_buyItem + * ["arifle_MX_F", 1000, "weapon", "weapon"] call forge_store_fnc_buyItem * * Public: No */ diff --git a/addons/store/functions/fnc_buyVehicle.sqf b/addons/store/functions/fnc_buyVehicle.sqf index 9237293..f9cbd78 100644 --- a/addons/store/functions/fnc_buyVehicle.sqf +++ b/addons/store/functions/fnc_buyVehicle.sqf @@ -10,7 +10,7 @@ * Arguments: * 0: Class Name - The classname of the vehicle to purchase * 1: Price - The price of the vehicle - * 2: Vehicle Type - The type of vehicle ("car", "air", "ship", "tank") + * 2: Vehicle Type - The type of vehicle ("car", "armor", "heli", "plane", "naval", "static") * * Return Value: * None diff --git a/addons/store/functions/fnc_handlePurchase.sqf b/addons/store/functions/fnc_handlePurchase.sqf index 846936f..6cc1cef 100644 --- a/addons/store/functions/fnc_handlePurchase.sqf +++ b/addons/store/functions/fnc_handlePurchase.sqf @@ -23,6 +23,7 @@ params ["_price"]; private _paymentData = GVAR(activePayment); private _payment = call compile _paymentData; +private _store = nil; scopeName "main"; @@ -36,7 +37,7 @@ if (count _payment > 3) then { }; if (_payment select 0 == "Organization") then { - private _store = call EFUNC(org,verifyOrgStore); + _store = call EFUNC(org,verifyOrgStore); private _org = _store call ["getOrg", []]; private _ownerUID = _org get "owner"; @@ -49,7 +50,6 @@ if (_payment select 0 == "Organization") then { private _varType = _payment select 2; private _balance = switch (_varType) do { case "organization": { - private _store = call EFUNC(org,verifyOrgStore); _store call ["getFunds", []]; }; case "player": { player getVariable [_payment select 1, 0] }; @@ -64,7 +64,6 @@ if (_balance < _price) exitWith { switch (_varType) do { case "organization": { - private _store = call EFUNC(org,verifyOrgStore); _store call ["updateFunds", -_price]; }; case "player": { diff --git a/addons/store/ui/_site/data/categories.json b/addons/store/ui/_site/data/categories.json new file mode 100644 index 0000000..900d10a --- /dev/null +++ b/addons/store/ui/_site/data/categories.json @@ -0,0 +1,150 @@ +{ + "paymentMethods": [ + { + "id": "cash", + "name": "Cash", + "icon": "💵" + }, + { + "id": "bank", + "name": "Bank", + "icon": "🏦" + } + ], + "Weapons": { + "icon": "", + "subcategories": { + "Rifles": { + "icon": "", + "products": [ + { + "id": 1, + "name": "Combat Rifle", + "price": 1500, + "image": "placeholder.jpg" + }, + { + "id": 2, + "name": "Sniper Rifle", + "price": 2500, + "image": "placeholder.jpg" + } + ] + }, + "Pistols": { + "icon": "", + "products": [ + { + "id": 3, + "name": "Tactical Pistol", + "price": 800, + "image": "placeholder.jpg" + } + ] + } + } + }, + "Equipment": { + "icon": "", + "subcategories": { + "Vests": { + "icon": "", + "products": [ + { + "id": 4, + "name": "Tactical Vest", + "price": 800, + "image": "placeholder.jpg" + } + ] + }, + "Headwear": { + "icon": "", + "products": [ + { + "id": 5, + "name": "Combat Helmet", + "price": 1200, + "image": "placeholder.jpg" + } + ] + }, + "Facewear": { + "icon": "", + "products": [ + { + "id": 6, + "name": "Balaclava", + "price": 500, + "image": "placeholder.jpg" + } + ] + }, + "Hmd": { + "icon": "", + "products": [ + { + "id": 7, + "name": "Night Vision Goggles", + "price": 2000, + "image": "placeholder.jpg" + } + ] + }, + "Backpacks": { + "icon": "", + "products": [ + { + "id": 8, + "name": "Combat Backpack", + "price": 450, + "image": "placeholder.jpg" + } + ] + }, + "Uniforms": { + "icon": "", + "products": [ + { + "id": 9, + "name": "Combat Fatigues", + "price": 1000, + "image": "placeholder.jpg" + } + ] + } + } + }, + "Vehicles": { + "icon": "", + "subcategories": { + "Wheeled": { + "icon": "", + "products": [ + { + "id": 10, + "name": "Transport Vehicle", + "price": 25000, + "image": "placeholder.jpg" + } + ] + } + } + }, + "Supplies": { + "icon": "", + "subcategories": { + "Medical": { + "icon": "", + "products": [ + { + "id": 11, + "name": "First Aid Kit", + "price": 300, + "image": "placeholder.jpg" + } + ] + } + } + } +} diff --git a/addons/store/ui/_site/index.html b/addons/store/ui/_site/index.html new file mode 100644 index 0000000..6279940 --- /dev/null +++ b/addons/store/ui/_site/index.html @@ -0,0 +1,27 @@ + + + + + + FORGE - GMS + + + + +
+
+
+

General Military Surplus

+
+ +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/addons/store/ui/_site/public/fdic.png b/addons/store/ui/_site/public/fdic.png new file mode 100644 index 0000000..7845bec Binary files /dev/null and b/addons/store/ui/_site/public/fdic.png differ diff --git a/addons/store/ui/_site/public/fdic_co.paa b/addons/store/ui/_site/public/fdic_co.paa new file mode 100644 index 0000000..45fe964 Binary files /dev/null and b/addons/store/ui/_site/public/fdic_co.paa differ diff --git a/addons/store/ui/_site/public/fms.png b/addons/store/ui/_site/public/fms.png new file mode 100644 index 0000000..553b09a Binary files /dev/null and b/addons/store/ui/_site/public/fms.png differ diff --git a/addons/store/ui/_site/public/fms_co.paa b/addons/store/ui/_site/public/fms_co.paa new file mode 100644 index 0000000..9d32a24 Binary files /dev/null and b/addons/store/ui/_site/public/fms_co.paa differ diff --git a/addons/store/ui/_site/public/gms.png b/addons/store/ui/_site/public/gms.png new file mode 100644 index 0000000..a4717b2 Binary files /dev/null and b/addons/store/ui/_site/public/gms.png differ diff --git a/addons/store/ui/_site/public/gms_co.paa b/addons/store/ui/_site/public/gms_co.paa new file mode 100644 index 0000000..ddc6d35 Binary files /dev/null and b/addons/store/ui/_site/public/gms_co.paa differ diff --git a/addons/store/ui/_site/script.js b/addons/store/ui/_site/script.js new file mode 100644 index 0000000..dccc61b --- /dev/null +++ b/addons/store/ui/_site/script.js @@ -0,0 +1,174 @@ +// Store data will be loaded from JSON +let categories = null; +let currentCategory = null; +let currentSubcategory = null; +let selectedPaymentMethod = null; + +function initializePaymentMethods() { + const paymentSelect = document.getElementById('paymentMethod'); + paymentSelect.innerHTML = ` + + ${categories.paymentMethods.map(method => ` + + `).join('')} + `; + paymentSelect.addEventListener('change', (e) => { + selectedPaymentMethod = e.target.value; + updateBuyButtons(); + }); +} + +function updateBuyButtons() { + const buyButtons = document.querySelectorAll('.buy-btn'); + buyButtons.forEach(button => { + button.disabled = !selectedPaymentMethod; + }); +} + +function handleQuantityChange(productId, change, quantityDisplay) { + const currentQty = parseInt(quantityDisplay.textContent); + const newQty = Math.max(1, currentQty + change); + quantityDisplay.textContent = newQty; +} + +function handlePurchase(product, quantity) { + if (!selectedPaymentMethod) { + alert('Please select a payment method first'); + return; + } + + const total = product.price * quantity; + alert(`Purchase Summary: +Product: ${product.name} +Quantity: ${quantity} +Total: $${total.toLocaleString()} +Payment Method: ${categories.paymentMethods.find(m => m.id === selectedPaymentMethod).name}`); + // Here you would typically integrate with your game's purchasing system +} + +function showMainCategories() { + const mainContent = document.querySelector('main'); + currentCategory = null; + currentSubcategory = null; + + mainContent.innerHTML = ` +
+ `; + + const categoriesGrid = document.querySelector('.categories-grid'); + Object.entries(categories) + .filter(([key]) => key !== 'paymentMethods') + .forEach(([categoryName, categoryData]) => { + const categoryTile = document.createElement('button'); + categoryTile.className = 'category-tile'; + categoryTile.innerHTML = ` + ${categoryData.icon} + ${categoryName} + `; + categoryTile.addEventListener('click', () => showSubcategories(categoryName)); + categoriesGrid.appendChild(categoryTile); + }); +} + +function showSubcategories(categoryName) { + const mainContent = document.querySelector('main'); + currentCategory = categoryName; + currentSubcategory = null; + + mainContent.innerHTML = ` + +

${categoryName}

+
+ `; + + const subcategoriesGrid = document.querySelector('.subcategories-grid'); + const backButton = document.querySelector('.back-button'); + backButton.addEventListener('click', showMainCategories); + + Object.entries(categories[categoryName].subcategories).forEach(([subCategoryName, subCategoryData]) => { + const subCategoryTile = document.createElement('button'); + subCategoryTile.className = 'subcategory-tile'; + subCategoryTile.innerHTML = ` + ${subCategoryData.icon} + ${subCategoryName} + `; + subCategoryTile.addEventListener('click', () => showProducts(categoryName, subCategoryName)); + subcategoriesGrid.appendChild(subCategoryTile); + }); +} + +function showProducts(categoryName, subCategoryName) { + const mainContent = document.querySelector('main'); + currentSubcategory = subCategoryName; + + mainContent.innerHTML = ` + +

${categoryName} > ${subCategoryName}

+
+ `; + + const productsGrid = document.querySelector('.products-grid'); + const backButton = document.querySelector('.back-button'); + backButton.addEventListener('click', () => showSubcategories(categoryName)); + + const products = categories[categoryName].subcategories[subCategoryName].products; + products.forEach(product => { + const productCard = document.createElement('div'); + productCard.className = 'product-card'; + productCard.innerHTML = ` +
+
+

${product.name}

+

$${product.price.toLocaleString()}

+
+
+ + 1 + +
+ +
+
+ `; + + const quantityDisplay = productCard.querySelector('.quantity-display'); + const minusBtn = productCard.querySelector('.quantity-btn.minus'); + const plusBtn = productCard.querySelector('.quantity-btn.plus'); + const buyBtn = productCard.querySelector('.buy-btn'); + + minusBtn.addEventListener('click', () => handleQuantityChange(product.id, -1, quantityDisplay)); + plusBtn.addEventListener('click', () => handleQuantityChange(product.id, 1, quantityDisplay)); + buyBtn.addEventListener('click', () => handlePurchase(product, parseInt(quantityDisplay.textContent))); + + productsGrid.appendChild(productCard); + }); +} + +// Initialize the store +async function initializeStore() { + try { + const response = await fetch('http://localhost:8000/data/categories.json'); + if (!response.ok) { + throw new Error('Failed to load store data'); + } + categories = await response.json(); + initializePaymentMethods(); + showMainCategories(); + } catch (error) { + console.error('Error loading store data:', error); + document.querySelector('main').innerHTML = ` +
+

Error Loading Store Data

+

Please try again later.

+
+ `; + } +} + +document.addEventListener('DOMContentLoaded', () => { + initializeStore(); +}); diff --git a/addons/store/ui/_site/server.py b/addons/store/ui/_site/server.py new file mode 100644 index 0000000..3bf590c --- /dev/null +++ b/addons/store/ui/_site/server.py @@ -0,0 +1,16 @@ +import http.server +import socketserver + +PORT = 8000 +DIRECTORY = '.' + +class Handler(http.server.SimpleHTTPRequestHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, directory=DIRECTORY, **kwargs) + +with socketserver.TCPServer(("", PORT), Handler) as httpd: + print(f"Serving at http://localhost:{PORT}") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nServer stopped.") diff --git a/addons/store/ui/_site/styles.css b/addons/store/ui/_site/styles.css new file mode 100644 index 0000000..caff0c7 --- /dev/null +++ b/addons/store/ui/_site/styles.css @@ -0,0 +1,200 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + line-height: 1.6; + background-color: #f4f4f4; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + background-color: #333; + color: white; + padding: 1rem 0; + margin-bottom: 2rem; +} + +header h1 { + text-align: center; +} + +.categories-grid, .subcategories-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 2rem; + padding: 1rem; + margin-bottom: 2rem; +} + +.category-tile, .subcategory-tile { + background-color: #444; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s; + aspect-ratio: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 1rem; + font-size: 1.3rem; +} + +.category-tile:hover, .subcategory-tile:hover { + background-color: #666; + transform: translateY(-5px); +} + +.category-tile.active, .subcategory-tile.active { + background-color: #007bff; +} + +.category-tile i, .subcategory-tile i { + font-size: 2rem; + margin-bottom: 1rem; +} + +.back-button { + background-color: #444; + color: white; + border: none; + border-radius: 4px; + padding: 0.5rem 1rem; + cursor: pointer; + margin-bottom: 1rem; + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.back-button:hover { + background-color: #666; +} + +.products-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + padding: 1rem; +} + +.product-card { + background-color: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + transition: transform 0.3s; +} + +.product-card:hover { + transform: translateY(-5px); +} + +.product-image { + width: 100%; + height: 256px; + object-fit: cover; + background-color: #eee; +} + +.product-info { + padding: 1rem; +} + +.product-name { + font-size: 1.1rem; + margin-bottom: 0.5rem; + color: #333; +} + +.product-price { + font-size: 1.2rem; + color: #007bff; + font-weight: bold; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.payment-select { + padding: 0.5rem; + border-radius: 4px; + background-color: white; + border: 1px solid #ddd; + font-size: 1rem; +} + +.product-controls { + display: flex; + align-items: center; + gap: 1rem; + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid #eee; +} + +.quantity-controls { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.quantity-btn { + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + width: 30px; + height: 30px; + font-size: 1.2rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.quantity-btn:hover { + background-color: #0056b3; +} + +.quantity-display { + font-size: 1.1rem; + min-width: 40px; + text-align: center; +} + +.buy-btn { + flex: 1; + background-color: #28a745; + color: white; + border: none; + border-radius: 4px; + padding: 0.5rem 1rem; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.3s; +} + +.buy-btn:hover { + background-color: #218838; +} + +.buy-btn:disabled { + background-color: #6c757d; + cursor: not-allowed; +}