Jacob Schmidt ebfe77a340 feat: implement complete Forge framework with Rust/Redis backend and Arma 3 integration
Implemented features:
- High-performance Rust extension with Redis persistence
- Actor/player management with loadout, position, and state tracking
- Banking system with deposit, withdraw, and transfer operations
- Physical and virtual garage/locker systems for vehicle and equipment storage
- Organization management with member tracking and permissions
- Client-side UI with React-like state management
- Server-side event-driven architecture with CBA Events
- Security: Self-transfer prevention at multiple layers
- Logging system with per-module log files
- ICOM module for inter-server communication

Co-Authored-By: Warp <agent@warp.dev>
2026-01-04 12:52:15 -06:00

381 lines
11 KiB
JavaScript

/**
* ATM Interface
* Handles banking transactions with PIN authentication
*/
// ============================================================================
// STATE
// ============================================================================
let enteredPin = '';
let currentView = 'welcomeView';
let previousView = 'welcomeView';
// ============================================================================
// VIEW MANAGEMENT
// ============================================================================
function showView(viewId) {
// Hide all views
document.querySelectorAll('.atm-view').forEach(view => {
view.style.display = 'none';
});
// Show selected view
const view = document.getElementById(viewId);
if (view) {
view.style.display = 'flex';
previousView = currentView;
currentView = viewId;
// Update balance displays when showing certain views
if (viewId === 'menuView' || viewId === 'balanceView' || viewId === 'depositView') {
updateBalances();
}
}
}
// ============================================================================
// PIN AUTHENTICATION
// ============================================================================
function generateKeypad() {
const keypad = document.getElementById('keypad');
if (!keypad) return;
// Define keypad layout
const keys = [
{ value: '1', label: '1', type: 'number' },
{ value: '2', label: '2', type: 'number' },
{ value: '3', label: '3', type: 'number' },
{ value: '4', label: '4', type: 'number' },
{ value: '5', label: '5', type: 'number' },
{ value: '6', label: '6', type: 'number' },
{ value: '7', label: '7', type: 'number' },
{ value: '8', label: '8', type: 'number' },
{ value: '9', label: '9', type: 'number' },
{ value: 'clear', label: 'Clear', type: 'action', class: 'key-clear' },
{ value: '0', label: '0', type: 'number' },
{ value: 'enter', label: 'Enter', type: 'action', class: 'key-enter' }
];
// Clear existing keypad
keypad.innerHTML = '';
// Generate buttons
keys.forEach(key => {
const button = document.createElement('button');
button.className = `key-btn${key.class ? ' ' + key.class : ''}`;
button.textContent = key.label;
// Add click handler
if (key.type === 'number') {
button.onclick = () => enterPin(key.value);
} else if (key.value === 'clear') {
button.onclick = () => clearPin();
} else if (key.value === 'enter') {
button.onclick = () => submitPin();
}
keypad.appendChild(button);
});
}
function enterPin(digit) {
if (enteredPin.length < 4) {
enteredPin += digit;
updatePinDisplay();
}
}
function clearPin() {
enteredPin = '';
updatePinDisplay();
}
function updatePinDisplay() {
const dots = document.querySelectorAll('.pin-dot');
dots.forEach((dot, index) => {
if (index < enteredPin.length) {
dot.classList.add('filled');
} else {
dot.classList.remove('filled');
}
});
}
function submitPin() {
if (enteredPin.length !== 4) {
showError('Please enter a 4-digit PIN');
return;
}
// In a real implementation, this would validate with the server
const currentState = store.getState();
if (enteredPin === currentState.pin) {
enteredPin = '';
updatePinDisplay();
showView('menuView');
} else {
showError('Incorrect PIN');
clearPin();
}
}
// ============================================================================
// BALANCE MANAGEMENT
// ============================================================================
function updateBalances() {
const currentState = store.getState();
// Update all balance displays
const cashElements = ['cashBalance', 'cashBalanceDetail', 'availableCash'];
const bankElements = ['bankBalance', 'bankBalanceDetail'];
cashElements.forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = `$${currentState.accounts.cash.toLocaleString()}`;
});
bankElements.forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = `$${currentState.accounts.bank.toLocaleString()}`;
});
const totalEl = document.getElementById('totalBalance');
if (totalEl) {
const total = currentState.accounts.cash + currentState.accounts.bank;
totalEl.textContent = `$${total.toLocaleString()}`;
}
}
// ============================================================================
// WITHDRAW OPERATIONS
// ============================================================================
function withdrawAmount(amount) {
const currentState = store.getState();
if (amount > currentState.accounts.bank) {
showError('Insufficient funds');
return;
}
store.dispatch(withdraw(amount));
sendEvent('atm::withdraw', { amount: amount });
showSuccess(`Withdrew $${amount.toLocaleString()}`);
}
function withdrawCustom() {
const input = document.getElementById('withdrawInput');
const amount = parseFloat(input.value);
if (!amount || amount <= 0) {
showError('Please enter a valid amount');
return;
}
const currentState = store.getState();
if (amount > currentState.accounts.bank) {
showError('Insufficient funds');
return;
}
store.dispatch(withdraw(amount));
sendEvent('atm::withdraw', { amount: amount });
input.value = '';
showSuccess(`Withdrew $${amount.toLocaleString()}`);
}
// ============================================================================
// DEPOSIT OPERATIONS
// ============================================================================
/**
* Deposits specified amount into bank account
* @deprecated Use store actions instead
*/
function depositAmount() {
const input = document.getElementById('depositInput');
const amount = parseFloat(input.value);
if (!amount || amount <= 0) {
showError('Please enter a valid amount');
return;
}
const currentState = store.getState();
if (amount > currentState.accounts.cash) {
showError('Insufficient cash');
return;
}
store.dispatch(deposit(amount));
sendEvent('atm::deposit', { amount: amount });
input.value = '';
showSuccess(`Deposited $${amount.toLocaleString()}`);
}
/**
* Deposits all available cash into bank account
* @deprecated Use store actions instead
*/
function depositAll() {
const currentState = store.getState();
if (currentState.accounts.cash <= 0) {
showError('No cash to deposit');
return;
}
const amount = currentState.accounts.cash;
store.dispatch(deposit(amount));
sendEvent('atm::deposit', { amount: amount });
showSuccess(`Deposited $${amount.toLocaleString()}`);
}
// ============================================================================
// TRANSFER OPERATIONS
// ============================================================================
/**
* Transfers specified amount from bank account to player account
* @deprecated Use store actions instead
*/
function transferFunds() {
const playerIdInput = document.getElementById('transferPlayerId');
const amountInput = document.getElementById('transferAmount');
const playerId = playerIdInput.value.trim();
const amount = parseFloat(amountInput.value);
if (!playerId) {
showError('Please enter a player ID');
return;
}
if (!amount || amount <= 0) {
showError('Please enter a valid amount');
return;
}
const currentState = store.getState();
if (amount > currentState.accounts.bank) {
showError('Insufficient funds');
return;
}
store.dispatch(transfer('bank', amount, 'player'));
sendEvent('atm::transfer', {
playerId: playerId,
amount: amount
});
playerIdInput.value = '';
amountInput.value = '';
showSuccess(`Transferred $${amount.toLocaleString()} to Player ${playerId}`);
}
// ============================================================================
// RESULT SCREENS
// ============================================================================
function showSuccess(message) {
document.getElementById('successMessage').textContent = message;
showView('successView');
updateBalances();
}
function showError(message) {
document.getElementById('errorMessage').textContent = message;
showView('errorView');
}
function goBackFromError() {
// If error happened during PIN entry, go back to PIN view
// Otherwise go back to menu view
if (previousView === 'pinView') {
showView('pinView');
} else {
showView('menuView');
}
}
// ============================================================================
// ATM CONTROL
// ============================================================================
function exitATM() {
enteredPin = '';
updatePinDisplay();
sendEvent('atm::close', {});
showView('welcomeView');
}
// ============================================================================
// ARMA 3 INTEGRATION
// ============================================================================
/**
* Sends an event to Arma 3
* @param {string} event - Event name
* @param {Object} data - Event data
*/
function sendEvent(event, data) {
if (typeof A3API !== 'undefined') {
A3API.SendAlert(JSON.stringify({
event: event,
data: data
}));
} else {
console.log('Event:', event, 'Data:', data);
}
}
// ============================================================================
// INITIALIZATION
// ============================================================================
function initATM() {
// Subscribe to store updates
if (typeof store !== 'undefined') {
store.subscribe(() => {
updateBalances();
});
}
// Generate keypad
generateKeypad();
// Show welcome screen
showView('welcomeView');
// Update initial balances
updateBalances();
console.log('[ATM] Interface initialized');
}
// Auto-initialize
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initATM);
} else {
initATM();
}
// ============================================================================
// GLOBAL EXPORTS
// ============================================================================
window.showView = showView;
window.generateKeypad = generateKeypad;
window.enterPin = enterPin;
window.clearPin = clearPin;
window.submitPin = submitPin;
window.withdrawAmount = withdrawAmount;
window.withdrawCustom = withdrawCustom;
window.depositAmount = depositAmount;
window.depositAll = depositAll;
window.transferFunds = transferFunds;
window.goBackFromError = goBackFromError;
window.exitATM = exitATM;