Compare commits
No commits in common. "master" and "1.0.0.2" have entirely different histories.
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -16,7 +16,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Source Code
|
||||
uses: actions/checkout@v4
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -404,8 +404,6 @@ hemtt
|
||||
hemtt.exe
|
||||
*.biprivatekey
|
||||
|
||||
# Forge API
|
||||
/api
|
||||
|
||||
# Added by cargo
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
name: ctrlWebBrowserAction
|
||||
description: Executes an action on a web browser control
|
||||
groups:
|
||||
- GUI Control
|
||||
syntax:
|
||||
- call: !Binary [control, action]
|
||||
ret:
|
||||
- Nothing
|
||||
- Nothing
|
||||
params:
|
||||
- name: control
|
||||
type: Control
|
||||
- name: action
|
||||
type: String
|
||||
argument_loc: Local
|
||||
effect_loc: Local
|
||||
since:
|
||||
arma_3:
|
||||
major: 2
|
||||
minor: 2
|
||||
examples:
|
||||
- <sqf>_control ctrlWebBrowserAction ["ExecJS", "document.getElementById('test').innerHTML = 'Hello World!'"];</sqf>
|
||||
- <sqf>_control ctrlWebBrowserAction ["LoadURL", "https://community.bistudio.com"];</sqf>
|
@ -1,103 +0,0 @@
|
||||
# Forge Admin Module
|
||||
|
||||
## Overview
|
||||
The Admin module provides comprehensive administrative functionality for the Forge client system. It includes features for user management, financial operations, messaging, and administrative controls through both traditional and web-based interfaces.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Administrative Functions
|
||||
1. **Admin Initialization** (`fnc_initAdmin.sqf`)
|
||||
- Initializes the admin system
|
||||
- Sets up necessary permissions and configurations
|
||||
|
||||
2. **Admin Interface**
|
||||
- **Traditional UI** (`fnc_openAdmin.sqf`)
|
||||
- Opens the administrative user interface
|
||||
- Provides access to administrative controls
|
||||
- **Web-based UI** (`RscWebAdmin.hpp`)
|
||||
- Modern web interface for administrative tasks
|
||||
- Real-time player statistics and management
|
||||
|
||||
3. **User Management**
|
||||
- **Promotion** (`fnc_adminPromote.sqf`)
|
||||
- Handles user promotion to administrative roles
|
||||
- **Transfer** (`fnc_adminTransfer.sqf`)
|
||||
- Manages administrative role transfers
|
||||
- **Refresh** (`fnc_adminRefresh.sqf`)
|
||||
- Updates administrative permissions and states
|
||||
|
||||
4. **Financial Operations**
|
||||
- **Payday System**
|
||||
- Distributes funds based on player ranks
|
||||
- Configurable paygrade amounts
|
||||
- **Money Management**
|
||||
- Advance funds to players
|
||||
- Deduct funds from players
|
||||
- Global money distribution
|
||||
- Company account balance tracking
|
||||
|
||||
5. **Communication**
|
||||
- **Admin Messages** (`fnc_adminMessage.sqf`)
|
||||
- Handles administrative messaging system
|
||||
- **Broadcast System**
|
||||
- Send messages to all players
|
||||
- Targeted player messaging
|
||||
|
||||
### User Interface Components
|
||||
1. **Traditional UI** (`RscAdmin.hpp`)
|
||||
- Player list management
|
||||
- Rank selection and promotion
|
||||
- Financial operations
|
||||
- Messaging system
|
||||
|
||||
2. **Web Interface** (`ui/_site/`)
|
||||
- Modern, responsive design
|
||||
- Real-time statistics
|
||||
- Player management
|
||||
- Financial operations
|
||||
- Messaging system
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
|
||||
## Usage
|
||||
To use the admin module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Access administrative functions through either:
|
||||
- Traditional UI: Use the provided dialog interface
|
||||
- Web Interface: Access through the modern web-based panel
|
||||
3. Use appropriate administrative commands based on your role
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Security
|
||||
The module includes built-in security features:
|
||||
- Admin authentication
|
||||
- Permission-based access control
|
||||
- Secure financial transactions
|
||||
- Protected administrative functions
|
||||
|
||||
## Technical Details
|
||||
- Supports both traditional Arma 3 UI and modern web interface
|
||||
- Real-time data updates
|
||||
- Configurable paygrade system
|
||||
- Comprehensive player management
|
||||
- Secure financial operations
|
@ -1,5 +1,7 @@
|
||||
PREP(adminMessage);
|
||||
PREP(adminPromote);
|
||||
PREP(adminRefresh);
|
||||
PREP(handleTransfer);
|
||||
PREP(adminTransfer);
|
||||
PREP(initAdmin);
|
||||
PREP(openAdmin);
|
||||
PREP(sendMessage);
|
||||
PREP(updatePaygrade);
|
||||
PREP(printAddonName);
|
@ -1,93 +1 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
[QGVAR(handleEvents), {
|
||||
params ["_control", "_isConfirmDialog", "_message"];
|
||||
|
||||
diag_log format ["[FORGE::Client::Admin::XEH_postInit] Received event: %1", _message];
|
||||
|
||||
_message = fromJSON _message;
|
||||
private _event = _message get "event";
|
||||
private _data = _message get "data";
|
||||
|
||||
switch (_event) do {
|
||||
case "REQUEST::PLAYER::DATA": {
|
||||
private _playerData = createHashMap;
|
||||
private _playerList = [];
|
||||
|
||||
{
|
||||
private _player = _x;
|
||||
private _uid = getPlayerUID _player;
|
||||
private _name = name _player;
|
||||
private _paygrade = GETVAR(_player,FORGE_PayGrade,QUOTE(E1)); //TODO: Implement paygrade from server
|
||||
private _funds = GETVAR(_player,FORGE_Bank,0); //TODO: Implement funds from server
|
||||
|
||||
private _playerInfo = createHashMapFromArray [
|
||||
["uid", _uid],
|
||||
["name", _name],
|
||||
["paygrade", _paygrade],
|
||||
["funds", _funds],
|
||||
["side", str (side _player)]
|
||||
];
|
||||
|
||||
_playerList pushBack _playerInfo;
|
||||
} forEach allPlayers;
|
||||
|
||||
_playerData set ["players", _playerList];
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["handlePlayerDataRequest(%1)", (toJSON _playerList)]];
|
||||
};
|
||||
case "REQUEST::PAYGRADE::DATA": {
|
||||
private _payGrades = (missionConfigFile >> "CfgPaygrades" >> "payGrades") call BIS_fnc_getCfgData;
|
||||
private _paygradeData = createHashMap;
|
||||
private _paygradeList = [];
|
||||
|
||||
{
|
||||
private _paygradeInfo = createHashMapFromArray [
|
||||
["paygrade", _x select 0],
|
||||
["bonus", _x select 1]
|
||||
];
|
||||
|
||||
_paygradeList pushBack _paygradeInfo;
|
||||
} forEach _payGrades;
|
||||
|
||||
_paygradeData set ["paygrades", _paygradeList];
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["handlePaygradeDataRequest(%1)", (toJSON _paygradeList)]];
|
||||
};
|
||||
case "BROADCAST::MESSAGE": {
|
||||
_data params ["_uid", "_message"];
|
||||
|
||||
if ((isNil "_message") || {_message isEqualTo ""}) exitWith { systemChat "Message cannot be empty!"; };
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::SEND::MESSAGE", [_uid, _message]]] call CFUNC(serverEvent);
|
||||
};
|
||||
case "SEND::MESSAGE": {
|
||||
_data params ["_uid", "_message"];
|
||||
|
||||
if ((isNil "_uid") || {_uid isEqualTo ""}) exitWith { systemChat "You did not select a player!"; };
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::SEND::MESSAGE", [_uid, _message]]] call CFUNC(serverEvent);
|
||||
};
|
||||
case "UPDATE::PAYGRADE": {
|
||||
_data params ["_uid", "_paygrade"];
|
||||
|
||||
if ((isNil "_uid") || {_uid isEqualTo ""}) exitWith { systemChat "You did not select a player!"; };
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::UPDATE::PAYGRADE", [_uid, _paygrade]]] call CFUNC(serverEvent);
|
||||
};
|
||||
case "HANDLE::TRANSFER": {
|
||||
_data params ["_condition", "_amount", "_uid"];
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::TRANSFER", [_condition, _amount, _uid]]] call CFUNC(serverEvent);
|
||||
};
|
||||
case "ADVANCE::ALL": {
|
||||
_data params ["_amount"];
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::ADVANCE::ALL", [_amount]]] call CFUNC(serverEvent);
|
||||
};
|
||||
case "HANDLE::PAYDAY": {
|
||||
["forge_server_admin_handleEvents", ["ADMIN::PAYDAY"]] call CFUNC(serverEvent);
|
||||
};
|
||||
default {
|
||||
diag_log format ["[FORGE::Client::Admin::XEH_postInit] Unhandled event: %1", _event];
|
||||
};
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
#include "script_component.hpp"
|
@ -15,5 +15,4 @@ class CfgPatches {
|
||||
|
||||
#include "CfgEventHandlers.hpp"
|
||||
#include "ui\RscCommon.hpp"
|
||||
#include "ui\RscAdmin.hpp"
|
||||
#include "ui\RscWebAdmin.hpp"
|
||||
#include "ui\RscAdmin.hpp"
|
30
addons/admin/functions/fnc_adminMessage.sqf
Normal file
30
addons/admin/functions/fnc_adminMessage.sqf
Normal file
@ -0,0 +1,30 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
private ["_data", "_dialog", "_list", "_target", "_targetValue", "_textBox", "_textMessage"];
|
||||
|
||||
_dialog = findDisplay 202303;
|
||||
_list = _dialog displayCtrl 2023001;
|
||||
_textBox = _dialog displayCtrl 2023006;
|
||||
_targetValue = lbCurSel _list;
|
||||
_data = _list lbData _targetValue;
|
||||
|
||||
if ((isNil {_data})) exitWith { hintSilent "You did not select a player!" };
|
||||
|
||||
{
|
||||
if (str (name (_x)) == str _data) then {
|
||||
_target = _x;
|
||||
};
|
||||
} forEach playableUnits;
|
||||
|
||||
hintSilent format ["Player Selected. You have selected %1", _target];
|
||||
|
||||
if (isNil "_target") then {
|
||||
hintSilent "Please Select an Active Player First!"
|
||||
} else {
|
||||
_textMessage = ctrlText _textBox;
|
||||
[_target, _textMessage] remoteExec ["forge_server_misc_fnc_textMessage", 2];
|
||||
|
||||
// [format ["Message sent to %1: <br/>%2", _target, _textMessage], "blue-grey", 3] call EFUNC(misc,notify);
|
||||
};
|
||||
|
||||
["dummy"] call FUNC(adminRefresh);
|
31
addons/admin/functions/fnc_adminPromote.sqf
Normal file
31
addons/admin/functions/fnc_adminPromote.sqf
Normal file
@ -0,0 +1,31 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
params [["_condition", "", [""]]];
|
||||
private ["_data", "_data2", "_dialog", "_list", "_list2", "_paygrade", "_rankValue", "_target", "_targetValue"];
|
||||
|
||||
_dialog = findDisplay 202303;
|
||||
_list = _dialog displayCtrl 2023001;
|
||||
_list2 = _dialog displayCtrl 2023003;
|
||||
_targetValue = lbCurSel _list;
|
||||
_rankValue = lbCurSel _list2;
|
||||
_data = _list lbData _targetValue;
|
||||
_data2 = call compile format ["%1", (_list2 lbData _rankValue)];
|
||||
_paygrade = _data2 select 0;
|
||||
|
||||
if ((isNil {_data})) exitWith { hintSilent "You did not select a player!" };
|
||||
{
|
||||
if (str (name (_x)) == str _data) then {
|
||||
_target = _x;
|
||||
};
|
||||
} forEach playableUnits;
|
||||
|
||||
switch (_condition) do {
|
||||
case ("promote"): {
|
||||
SETPVAR(_target,PayGrade,_paygrade)
|
||||
};
|
||||
case ("demote"): {
|
||||
SETPVAR(_target,PayGrade,_paygrade)
|
||||
};
|
||||
};
|
||||
|
||||
["dummy"] call FUNC(adminRefresh);
|
@ -1,36 +1,52 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Refreshes the admin interface
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Dummy <ANY> - Optional parameter, not used (for compatibility with event handlers)
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* [] call forge_client_admin_fnc_adminRefresh;
|
||||
* ["dummy"] call forge_client_admin_fnc_adminRefresh;
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
params [["_condition", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
private _newFunds = 0;
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
|
||||
switch (_condition) do {
|
||||
case ("deduct"): {
|
||||
_newFunds = companyFunds - _amount;
|
||||
ctrlSetText [2023002, format ["$%1", (_newFunds call EFUNC(misc,formatNumber))]];
|
||||
};
|
||||
case ("advance"): {
|
||||
_newFunds = companyFunds + _amount;
|
||||
ctrlSetText [2023002, format ["$%1", (_newFunds call EFUNC(misc,formatNumber))]];
|
||||
};
|
||||
default {
|
||||
lbClear _list;
|
||||
|
||||
{
|
||||
if (str (side _x) == str (playerSide)) then {
|
||||
private _name = name (_x);
|
||||
private _defaultPaygrade = "E1";
|
||||
private _paygrade = GETVAR(_x,PayGrade,_defaultPaygrade);
|
||||
private _index = _list lbAdd format["%1 - %2", _name, _paygrade];
|
||||
|
||||
_list lbSetData [_index, name (_x)];
|
||||
};
|
||||
} forEach playableUnits;
|
||||
|
||||
lbSetCurSel [2023001, 0];
|
||||
ctrlSetText [2023005, ""];
|
||||
ctrlSetText [2023006, ""];
|
||||
};
|
||||
};
|
||||
|
||||
lbClear _list;
|
||||
|
||||
{
|
||||
if (str (side _x) == str (playerSide)) then {
|
||||
private _name = name (_x);
|
||||
private _paygrade = GETVAR(_x,FORGE_PayGrade,QUOTE(E1));
|
||||
private _defaultPaygrade = "E1";
|
||||
private _paygrade = GETVAR(_x,PayGrade,_defaultPaygrade);
|
||||
private _index = _list lbAdd format["%1 - %2", _name, _paygrade];
|
||||
|
||||
_list lbSetData [_index, getPlayerUID _x];
|
||||
_list lbSetData [_index, name (_x)];
|
||||
};
|
||||
} forEach allPlayers;
|
||||
} forEach playableUnits;
|
||||
|
||||
lbSetCurSel [2023001, 0];
|
||||
ctrlSetText [2023005, ""];
|
||||
|
92
addons/admin/functions/fnc_adminTransfer.sqf
Normal file
92
addons/admin/functions/fnc_adminTransfer.sqf
Normal file
@ -0,0 +1,92 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
params [["_condition", "", [""]]];
|
||||
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
private _targetValue = lbCurSel _list;
|
||||
private _data = _list lbData _targetValue;
|
||||
private _amount = round (parseNumber (ctrlText 2023005));
|
||||
|
||||
if ((isNil {_data})) exitWith { hint "You did not select a player!" };
|
||||
|
||||
{
|
||||
if (str (name (_x)) == str _data) then {
|
||||
private _target = _x;
|
||||
};
|
||||
} count playableUnits;
|
||||
|
||||
switch (_condition) do {
|
||||
case ("advance"): {
|
||||
private _bank = GETVAR(_target,FORGE_Bank,0);
|
||||
private _newBalance = _bank + _amount;
|
||||
|
||||
if (_amount > companyFunds) exitWith { ["Not enough money in the company's account!", "warning", 3] call EFUNC(misc,notify) };
|
||||
|
||||
SETPVAR(_target,FORGE_Bank,_newBalance);
|
||||
|
||||
["deduct", _amount] call FUNC(adminRefresh);
|
||||
["deduct", _amount] remoteExecCall ["forge_server_money_fnc_handleFunds", 2];
|
||||
};
|
||||
case ("advanceAll"): {
|
||||
private _count = count playableUnits;
|
||||
|
||||
if ((10000 * _count) > companyFunds) exitWith { ["Not enough money in the company's account!", "warning", 3] call EFUNC(misc,notify) };
|
||||
|
||||
{
|
||||
private _bank = GETVAR(_x,Cash_Bank,0);
|
||||
private _newBalance = _bank + 10000;
|
||||
|
||||
SETPVAR(_x,Cash_Bank,_newBalance);
|
||||
} count playableUnits;
|
||||
|
||||
["deduct", (10000 * _count)] call FUNC(adminRefresh);
|
||||
["deduct", (10000 * _count)] remoteExecCall ["forge_server_money_fnc_handleFunds", 2];
|
||||
};
|
||||
case ("deduct"): {
|
||||
private _bank = GETVAR(_target,FORGE_Bank,0);
|
||||
private _newBalance = _bank - _amount;
|
||||
|
||||
if (_amount > _bank) exitWith { ["Not enough money in the player's account!", "warning", 3] call EFUNC(misc,notify) };
|
||||
|
||||
SETPVAR(_target,FORGE_Bank,_newBalance);
|
||||
|
||||
["advance", _amount] call FUNC(adminRefresh);
|
||||
["advance", _amount] remoteExecCall ["forge_server_money_fnc_handleFunds", 2];
|
||||
};
|
||||
case ("payday"): {
|
||||
private _totalPayment = 0;
|
||||
private _paymentToDo = [];
|
||||
private _payGrades = (missionConfigFile >> "CfgPaygrades" >> "payGrades") call BIS_fnc_getCfgData;
|
||||
|
||||
{
|
||||
private _player = _x;
|
||||
private _payGrade = GETVAR(_player,PayGrade,nil);
|
||||
|
||||
{
|
||||
_x params ["_payGradeIndex", "_payGradeBonus"];
|
||||
|
||||
if ((_x select 0) == _payGrade) then {
|
||||
_paymentToDo pushBack [_player, _payGradeBonus];
|
||||
_totalPayment = _totalPayment + _payGradeBonus;
|
||||
};
|
||||
} forEach _payGrades;
|
||||
} count playableUnits;
|
||||
|
||||
if (_totalPayment > companyFunds) exitWith { ["Not enough money in the company's account!", "warning", 3] call EFUNC(misc,notify) };
|
||||
|
||||
{
|
||||
_x params ["_player", "_bonus"];
|
||||
|
||||
private _bank = GETVAR(_player,FORGE_Bank,0);
|
||||
private _newBalance = _bank + _bonus;
|
||||
|
||||
SETPVAR(_player,FORGE_Bank,_newBalance);
|
||||
} count _paymentToDo;
|
||||
|
||||
["deduct", _totalPayment] call FUNC(adminRefresh);
|
||||
["deduct", _totalPayment] remoteExecCall ["forge_server_money_fnc_handleFunds", 2];
|
||||
};
|
||||
};
|
||||
|
||||
ctrlSetText [2023005, ""];
|
@ -1,32 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Handles fund transfers
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Condition <STRING> - The type of transfer to perform ("advance", "deduct", "advanceAll", "payday")
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* ["advance"] call forge_client_admin_fnc_handleTransfer;
|
||||
* ["payday"] call forge_client_admin_fnc_handleTransfer;
|
||||
*
|
||||
* Public: No - Called from admin dialog controls
|
||||
*/
|
||||
|
||||
params [["_condition", "", [""]]];
|
||||
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
private _index = lbCurSel _list;
|
||||
private _uid = _list lbData _index;
|
||||
private _amount = round (parseNumber (ctrlText 2023005));
|
||||
|
||||
if ({_condition in ["advance", "deduct"]} && ((isNil "_uid") || { _uid isEqualTo "" })) exitWith { hint "You did not select a player!"; };
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::TRANSFER", [_condition, _amount, _uid]]] call CFUNC(serverEvent);
|
||||
|
||||
ctrlSetText [2023005, ""];
|
18
addons/admin/functions/fnc_initAdmin.sqf
Normal file
18
addons/admin/functions/fnc_initAdmin.sqf
Normal file
@ -0,0 +1,18 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
{
|
||||
private _configName = configName(_x);
|
||||
private _className = (missionConfigFile >> "CfgCpofs" >> "cpofs" >> _configName >> "className") call BFUNC(getCfgData);
|
||||
private _pos = (missionConfigFile >> "CfgCpofs" >> "cpofs" >> _configName >> "pos") call BFUNC(getCfgData);
|
||||
private _dir = (missionConfigFile >> "CfgCpofs" >> "cpofs" >> _configName >> "dir") call BFUNC(getCfgData);
|
||||
|
||||
private _cpof = createSimpleObject [_className, [0, 0, 0]];
|
||||
|
||||
_cpof setPosATL _pos;
|
||||
_cpof setDir _dir;
|
||||
_cpof allowDamage false;
|
||||
_cpof setVariable ["isCPOF", true, true];
|
||||
|
||||
diag_log text format ["[FORGE Admin] ClassName: '%1' Pos: '%2' Dir: '%3'", _className, _pos, _dir];
|
||||
|
||||
} forEach ("true" configClasses (missionConfigFile >> "CfgCpofs" >> "cpofs"));
|
@ -1,62 +1,31 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Opens the admin dialog.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_client_admin_fnc_openAdmin;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
disableSerialization;
|
||||
createDialog "RscAdmin";
|
||||
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
private _list2 = _dialog displayCtrl 2023003;
|
||||
|
||||
{
|
||||
if (str (side _x) == str (playerSide)) then {
|
||||
private _name = name (_x);
|
||||
private _payGrade = GETVAR(_x,paygrade,nil);
|
||||
private _index = _list lbAdd format["%1 - %2", _name, _payGrade];
|
||||
|
||||
_list lbSetData [_index, _name];
|
||||
};
|
||||
} count (allPlayers);
|
||||
|
||||
lbSetCurSel [2023001, 0];
|
||||
|
||||
private _productVersion = productVersion;
|
||||
private _steamBranchName = _productVersion select 8;
|
||||
private _payGrades = (missionConfigFile >> "CfgPaygrades" >> "payGrades") call BFUNC(getCfgData);
|
||||
|
||||
if (_steamBranchName == "profiling") then {
|
||||
private _display = (findDisplay 46) createDisplay "RscWebAdmin";
|
||||
private _ctrl = _display displayCtrl 2025;
|
||||
{
|
||||
private _index = _list2 lbAdd format ["%1 - $%2", (_x select 0), ((_x select 1) call EFUNC(misc,formatNumber))];
|
||||
|
||||
_ctrl ctrlAddEventHandler ["JSDialog", {
|
||||
params ["_control", "_isConfirmDialog", "_message"];
|
||||
[QGVAR(handleEvents), [_control, _isConfirmDialog, _message]] call CFUNC(localEvent);
|
||||
}];
|
||||
_list2 lbSetData [_index, str _x];
|
||||
} forEach _payGrades;
|
||||
|
||||
_ctrl ctrlWebBrowserAction ["LoadFile", QUOTE(PATHTOF(ui\_site\index.html))];
|
||||
} else {
|
||||
disableSerialization;
|
||||
createDialog "RscAdmin";
|
||||
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
private _list2 = _dialog displayCtrl 2023003;
|
||||
|
||||
{
|
||||
if (str (side _x) == str (playerSide) && isPlayer _x) then {
|
||||
private _name = name (_x);
|
||||
private _uid = getPlayerUID _x;
|
||||
private _payGrade = GETVAR(_x,FORGE_PayGrade,QUOTE(E1));
|
||||
private _index = _list lbAdd format["%1 - %2", _name, _payGrade];
|
||||
|
||||
_list lbSetData [_index, _uid];
|
||||
};
|
||||
} count (allPlayers);
|
||||
|
||||
lbSetCurSel [2023001, 0];
|
||||
|
||||
{
|
||||
private _index = _list2 lbAdd format ["%1 - $%2", (_x select 0), ((_x select 1) call EFUNC(misc,formatNumber))];
|
||||
|
||||
_list2 lbSetData [_index, str _x];
|
||||
} forEach _payGrades;
|
||||
|
||||
lbSetCurSel [2023003, 0];
|
||||
ctrlSetText [2023002, format ["$%1", (companyFunds call EFUNC(misc,formatNumber))]];
|
||||
};
|
||||
lbSetCurSel [2023003, 0];
|
||||
ctrlSetText [2023002, format ["$%1", (companyFunds call EFUNC(misc,formatNumber))]];
|
3
addons/admin/functions/fnc_printAddonName.sqf
Normal file
3
addons/admin/functions/fnc_printAddonName.sqf
Normal file
@ -0,0 +1,3 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
systemChat format ["Thank you for using the %1", 'ADDON'];
|
@ -1,30 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Sends a message to a selected player
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* [] call forge_client_admin_fnc_sendMessage;
|
||||
*
|
||||
* Public: No - Called from admin dialog controls
|
||||
*/
|
||||
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
private _control = _dialog displayCtrl 2023006;
|
||||
private _index = lbCurSel _list;
|
||||
private _uid = _list lbData _index;
|
||||
private _message = ctrlText _control;
|
||||
|
||||
if ((isNil "_uid") || {_uid isEqualTo ""}) exitWith { hintSilent "You did not select a player!"; };
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::SEND::MESSAGE", [_uid, _message]]] call CFUNC(serverEvent);
|
||||
|
||||
["dummy"] call FUNC(adminRefresh);
|
@ -1,32 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Updates a player's paygrade
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* [] call forge_client_admin_fnc_updatePaygrade;
|
||||
*
|
||||
* Public: No - Called from admin dialog controls
|
||||
*/
|
||||
|
||||
private _dialog = findDisplay 202303;
|
||||
private _list = _dialog displayCtrl 2023001;
|
||||
private _list2 = _dialog displayCtrl 2023003;
|
||||
private _targetIndex = lbCurSel _list;
|
||||
private _rankIndex = lbCurSel _list2;
|
||||
private _uid = _list lbData _targetIndex;
|
||||
private _rankData = call compile format ["%1", (_list2 lbData _rankIndex)];
|
||||
private _paygrade = _rankData select 0;
|
||||
|
||||
if ((isNil "_uid") || {_uid isEqualTo ""}) exitWith { hintSilent "You did not select a player!" };
|
||||
|
||||
["forge_server_admin_handleEvents", ["ADMIN::UPDATE::PAYGRADE", [_uid, _paygrade]]] call CFUNC(serverEvent);
|
||||
|
||||
["dummy"] call FUNC(adminRefresh);
|
@ -83,7 +83,7 @@ class RscAdmin {
|
||||
class RscAdminPromote: RscButton {
|
||||
idc = -1;
|
||||
colorText[] = {1,1,1,1};
|
||||
onButtonClick = "[] call forge_client_admin_fnc_updatePaygrade;";
|
||||
onButtonClick = "['promote'] call forge_client_admin_fnc_adminPromote;";
|
||||
soundClick[] = {"\A3\ui_f\data\sound\RscButton\soundClick",0.09,1};
|
||||
text = "Promote";
|
||||
x = "0.675 * safezoneW + safezoneX";
|
||||
@ -163,7 +163,7 @@ class RscAdmin {
|
||||
class RscAdminSend: RscButton {
|
||||
idc = -1;
|
||||
colorText[] = {1,1,1,1};
|
||||
onButtonClick = "[] call forge_client_admin_fnc_sendMessage;";
|
||||
onButtonClick = "[] call forge_client_admin_fnc_adminMessage;";
|
||||
soundClick[] = {"\A3\ui_f\data\sound\RscButton\soundClick",0.09,1};
|
||||
text = "Send Message";
|
||||
x = "0.6125 * safezoneW + safezoneX";
|
||||
|
@ -1,17 +0,0 @@
|
||||
class RscWebAdmin {
|
||||
idd = 20250502;
|
||||
fadein = 0;
|
||||
fadeout = 0;
|
||||
duration = 1e+011;
|
||||
|
||||
class controls {
|
||||
class Background: RscText {
|
||||
type = 106;
|
||||
idc = 2025;
|
||||
x = "safeZoneY * (pixelW/pixelH) * 2.975";
|
||||
y = "safeZoneY + (safeZoneH * 0.05)";
|
||||
w = "safeZoneW * (pixelW/pixelH) * 1.17";
|
||||
h = "safeZoneH * 0.875";
|
||||
};
|
||||
};
|
||||
};
|
@ -1,163 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!--
|
||||
Dynamic Resource Loading
|
||||
The following script loads CSS and JavaScript files dynamically using the A3API
|
||||
This approach is used instead of static HTML imports to work with Arma 3's file system
|
||||
-->
|
||||
<script>
|
||||
Promise.all([
|
||||
// Load CSS file
|
||||
A3API.RequestFile("z\\forge_client\\addons\\admin\\ui\\_site\\styles.css"),
|
||||
// Load JavaScript file
|
||||
A3API.RequestFile("z\\forge_client\\addons\\admin\\ui\\_site\\script.js")
|
||||
]).then(([css, js]) => {
|
||||
// Apply CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = css;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Load and execute JavaScript
|
||||
const script = document.createElement('script');
|
||||
script.text = js;
|
||||
document.head.appendChild(script);
|
||||
|
||||
// Initialize the admin interface
|
||||
initializeAdmin();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
Header Section
|
||||
Contains the main title and server statistics
|
||||
-->
|
||||
<header>
|
||||
<div class="header-content">
|
||||
<h1>Admin Panel</h1>
|
||||
<!-- Server Statistics Display -->
|
||||
<div class="admin-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-info">
|
||||
<span class="stat-label">Online Players</span>
|
||||
<span class="stat-value" id="playerCount">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-divider"></div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-info">
|
||||
<span class="stat-label">Staff Online</span>
|
||||
<span class="stat-value" id="staffCount">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!--
|
||||
Main Content Area
|
||||
Contains all admin functionality sections
|
||||
-->
|
||||
<main class="container">
|
||||
<div class="sections-grid">
|
||||
<!--
|
||||
Admin Action Sections
|
||||
Left column with various global admin actions
|
||||
-->
|
||||
<div class="action-sections">
|
||||
<!-- Global Payday Action -->
|
||||
<div class="admin-section">
|
||||
<h2>Global Actions</h2>
|
||||
<div class="form-group">
|
||||
<label for="paydayAmount">Payday</label>
|
||||
<p class="payday-description">Players will receive money based on their rank</p>
|
||||
</div>
|
||||
<button class="submit-btn" onclick="Payday()">Payday</button>
|
||||
</div>
|
||||
|
||||
<!-- Give All Money Action -->
|
||||
<div class="admin-section">
|
||||
<h2>Give All Money</h2>
|
||||
<div class="form-group">
|
||||
<label for="giveAllAmount">Amount to Give</label>
|
||||
<input type="number" id="giveAllAmount" min="0" placeholder="Enter amount to give to all players...">
|
||||
</div>
|
||||
<button class="submit-btn" onclick="giveAllMoney()">Give to All</button>
|
||||
</div>
|
||||
|
||||
<!-- Broadcast Message System -->
|
||||
<div class="admin-section">
|
||||
<h2>Broadcast Message</h2>
|
||||
<div class="form-group">
|
||||
<label for="broadcastMessage">Message</label>
|
||||
<input type="text" id="broadcastMessage" placeholder="Enter message to broadcast...">
|
||||
</div>
|
||||
<button class="submit-btn" onclick="broadcastMessage()">Send to All</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Player List Section
|
||||
Right column showing all players with filtering options
|
||||
-->
|
||||
<div class="admin-section player-list-section">
|
||||
<!-- Search and Filter Controls -->
|
||||
<div class="search-bar">
|
||||
<input type="text" id="playerSearch" class="search-input" placeholder="Search players...">
|
||||
<div class="filter-bar">
|
||||
<button class="filter-btn active" data-filter="all">All</button>
|
||||
<button class="filter-btn" data-filter="staff">Staff</button>
|
||||
<button class="filter-btn" data-filter="blufor">BLUFOR</button>
|
||||
<button class="filter-btn" data-filter="opfor">OPFOR</button>
|
||||
<button class="filter-btn" data-filter="independent">Independent</button>
|
||||
<button class="filter-btn" data-filter="civilian">Civilian</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dynamic Player List Container -->
|
||||
<div class="player-list" id="playerList">
|
||||
<!-- Players will be populated dynamically via JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!--
|
||||
Modal Dialogs
|
||||
Hidden by default, shown when specific actions are triggered
|
||||
-->
|
||||
|
||||
<!-- Message Modal - For sending messages to individual players -->
|
||||
<div id="messageModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>Send Message</h2>
|
||||
<button class="close-modal" onclick="closeMessageModal()">×</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="messageInput">Message to <span id="messagePlayerName"></span></label>
|
||||
<input type="text" id="messageInput" placeholder="Enter your message...">
|
||||
</div>
|
||||
<button class="submit-btn" onclick="sendPlayerMessage()">Send Message</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Money Modal - For modifying player funds -->
|
||||
<div id="moneyModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>Modify Money</h2>
|
||||
<button class="close-modal" onclick="closeMoneyModal()">×</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="moneyAmount">Amount</label>
|
||||
<input type="number" id="moneyAmount" placeholder="Enter amount...">
|
||||
</div>
|
||||
<div class="player-actions">
|
||||
<button class="action-btn promote-btn" onclick="giveMoney()">Give Money</button>
|
||||
<button class="action-btn demote-btn" onclick="takeMoney()">Take Money</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,488 +0,0 @@
|
||||
/**
|
||||
* 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 `
|
||||
<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
|
@ -1,646 +0,0 @@
|
||||
/* =============================================================================
|
||||
BASE STYLES AND VARIABLES
|
||||
============================================================================= */
|
||||
|
||||
/* Reset styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Color variables and theme configuration */
|
||||
:root {
|
||||
/* Primary colors */
|
||||
--primary-color: #3b82f6;
|
||||
--primary-hover: #2563eb;
|
||||
--secondary-color: #1e293b;
|
||||
|
||||
/* Background colors */
|
||||
--background-color: #f1f5f9;
|
||||
--card-background: #ffffff;
|
||||
--header-bg: #1e293b;
|
||||
--tile-hover: #f8fafc;
|
||||
|
||||
/* Text colors */
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--header-text: #f8fafc;
|
||||
|
||||
/* Status colors */
|
||||
--success-color: #16a34a;
|
||||
--success-hover: #15803d;
|
||||
--error-color: #dc2626;
|
||||
--error-hover: #b91c1c;
|
||||
--warning-color: #f59e0b;
|
||||
--warning-hover: #d97706;
|
||||
|
||||
/* Utility colors */
|
||||
--border-color: #e2e8f0;
|
||||
--shadow-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Base body styles */
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
LAYOUT COMPONENTS
|
||||
============================================================================= */
|
||||
|
||||
/* Main container */
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Header styles */
|
||||
header {
|
||||
background-color: var(--header-bg);
|
||||
color: var(--header-text);
|
||||
padding: 1rem 0;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
& .header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
& h1 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
}
|
||||
|
||||
/* Admin stats in header */
|
||||
.admin-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
& .stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
min-width: 140px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
& .stat-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
& .stat-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
}
|
||||
|
||||
& .stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
& .stat-value {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--header-text);
|
||||
}
|
||||
|
||||
& .stat-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Grid layout for sections */
|
||||
.sections-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.action-sections {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
ADMIN SECTION COMPONENTS
|
||||
============================================================================= */
|
||||
|
||||
/* Admin section cards */
|
||||
.admin-section {
|
||||
background-color: var(--card-background);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
border: none;
|
||||
transition: all 0.3s ease-in-out;
|
||||
height: auto;
|
||||
max-height: calc(100vw / 3);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&.square-ratio {
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Player list section */
|
||||
.player-list-section {
|
||||
grid-column: span 1;
|
||||
height: auto;
|
||||
max-height: calc(100vw / 3);
|
||||
}
|
||||
|
||||
.player-list {
|
||||
list-style: none;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding-right: 0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
|
||||
/* Customize scrollbar for webkit browsers */
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Player item in the list */
|
||||
.player-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background-color: var(--card-background);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border: none;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tile-hover);
|
||||
box-shadow: 0 4px 6px var(--shadow-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
& .player-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
& .player-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
& .player-money {
|
||||
font-size: 0.875rem;
|
||||
color: var(--success-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
& .player-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Player role badges */
|
||||
.player-role {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.role-admin {
|
||||
background-color: var(--error-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.role-mod {
|
||||
background-color: var(--warning-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.role-player {
|
||||
background-color: var(--success-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
BUTTONS AND INTERACTIVE ELEMENTS
|
||||
============================================================================= */
|
||||
|
||||
/* Action buttons */
|
||||
.action-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.promote-btn {
|
||||
background-color: #22c55e;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background-color: #16a34a;
|
||||
}
|
||||
}
|
||||
|
||||
.demote-btn {
|
||||
background-color: #ef4444;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
}
|
||||
|
||||
.message-btn {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
}
|
||||
|
||||
/* Search and filter components */
|
||||
.search-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--card-background);
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background-color: var(--card-background);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
FORMS AND INPUTS
|
||||
============================================================================= */
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
& label {
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
& input {
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--card-background);
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s ease-in-out;
|
||||
opacity: 0.9;
|
||||
margin-top: auto;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-hover);
|
||||
opacity: 1;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
MODALS
|
||||
============================================================================= */
|
||||
|
||||
.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: var(--card-background);
|
||||
padding: 1.5rem;
|
||||
border-radius: 12px;
|
||||
min-width: 400px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
& h2 {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
transition: color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
BADGES AND STATUS INDICATORS
|
||||
============================================================================= */
|
||||
|
||||
.badge {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
/* Rank & Side badges */
|
||||
.player-rank,
|
||||
.player-side {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
/* Rank styling based on type */
|
||||
.rank-enlisted {
|
||||
background-color: #e2e8f0;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.rank-warrant {
|
||||
background-color: #bfdbfe;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.rank-officer {
|
||||
background-color: #3b82f6;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Side indicators */
|
||||
.side-west {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.side-east {
|
||||
background-color: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.side-guer {
|
||||
background-color: #22c55e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.side-civ {
|
||||
background-color: #a855f7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Player payday indicator */
|
||||
.player-payday {
|
||||
font-size: 0.75rem;
|
||||
color: var(--success-color);
|
||||
font-weight: 500;
|
||||
background-color: rgba(22, 163, 74, 0.1);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* Description text */
|
||||
.payday-description {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
RESPONSIVE ADJUSTMENTS
|
||||
============================================================================= */
|
||||
|
||||
/* Adjustments for smaller screens */
|
||||
@media (max-width: 768px) {
|
||||
.action-sections {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.player-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
& .player-info {
|
||||
margin-bottom: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
& .player-actions {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
min-width: 90%;
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjustments for very small screens */
|
||||
@media (max-width: 480px) {
|
||||
.admin-stats {
|
||||
flex-direction: column;
|
||||
|
||||
& .stat-divider {
|
||||
width: 80%;
|
||||
height: 1px;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
# Forge Ambient Module
|
||||
|
||||
## Overview
|
||||
The Ambient module provides environmental sound management for the Forge client system. It enables the creation and management of ambient sound effects in the game environment, enhancing the immersive experience for players.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Sound Management
|
||||
1. **Ambient Sound System** (`fnc_ambientSound.sqf`)
|
||||
- Creates and manages sound sources in the game world
|
||||
- Handles sound effect playback and cleanup
|
||||
- Supports timed and continuous sound effects
|
||||
|
||||
2. **Sound Source Features**
|
||||
- Dynamic sound source creation
|
||||
- Position-based sound placement
|
||||
- Automatic cleanup of sound sources
|
||||
- Support for various sound types and durations
|
||||
|
||||
3. **Sound Control**
|
||||
- Lifecycle management of sound sources
|
||||
- Automatic cleanup when source is destroyed
|
||||
- Configurable sound duration
|
||||
- Position tracking for moving sound sources
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the ambient module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Create sound sources using the ambient sound function:
|
||||
```sqf
|
||||
[source, "sfx_sound_name"] spawn forge_client_ambient_fnc_ambientSound
|
||||
```
|
||||
3. Optionally specify a duration for temporary sounds:
|
||||
```sqf
|
||||
[source, "sfx_sound_name", duration] spawn forge_client_ambient_fnc_ambientSound
|
||||
```
|
||||
|
||||
## Parameters
|
||||
The ambient sound function accepts the following parameters:
|
||||
1. `source`: The object or position where the sound will be played
|
||||
2. `sfx`: The name of the sound effect to play
|
||||
3. `time` (optional): Duration in seconds before the sound is removed
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Supports both object-attached and position-based sound sources
|
||||
- Automatic cleanup of sound sources when source is destroyed
|
||||
- Configurable sound duration for temporary effects
|
||||
- Efficient sound source management
|
||||
- Client-side sound processing
|
@ -1,8 +1,19 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Create a sound source and play an ambient sfx sound
|
||||
* Function: forge_client_ambient_fnc_ambientSound
|
||||
* Author: J.Schmidt
|
||||
* Edit: 07.15.2024
|
||||
* Copyright © 2024 J.Schmidt, All rights reserved
|
||||
*
|
||||
* Do not edit without permission!
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivative 4.0 International License.
|
||||
* To view a copy of this license, vist https://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to Creative Commons,
|
||||
* PO Box 1866, Mountain View, CA 94042
|
||||
*
|
||||
* [Description]
|
||||
* Create a sound source and play an ambient sfx sound.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: The sound source <OBJECT>
|
||||
|
@ -1,119 +0,0 @@
|
||||
# Forge Arsenal Module
|
||||
|
||||
## Overview
|
||||
The Arsenal module provides a comprehensive weapon and vehicle management system for the Forge client. It includes features for managing armory items, vehicle garages, and unlock systems, with support for both traditional and virtual arsenals.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Armory Management
|
||||
1. **Armory Initialization** (`fnc_initArsenal.sqf`)
|
||||
- Initializes the arsenal system
|
||||
- Sets up necessary configurations and permissions
|
||||
- Configures default armory and garage data
|
||||
- Supports both traditional and virtual arsenal types
|
||||
|
||||
2. **Armory Interface** (`fnc_openArmory.sqf`)
|
||||
- Opens the armory user interface
|
||||
- Provides access to weapon and equipment management
|
||||
- Supports both traditional and ACE arsenal interfaces
|
||||
|
||||
3. **Item Management**
|
||||
- **Add Armory Items** (`fnc_addArmoryItem.sqf`)
|
||||
- Handles adding new items to the armory
|
||||
- Supports multiple item types:
|
||||
- Facewear (goggles, masks)
|
||||
- Headgear (helmets, caps)
|
||||
- HMD (night vision devices)
|
||||
- Items (tools, medical supplies)
|
||||
- Uniforms
|
||||
- Vests
|
||||
- Weapons
|
||||
- Magazines
|
||||
- Backpacks
|
||||
- **Save Unlocks** (`fnc_saveUnlocks.sqf`)
|
||||
- Manages the persistence of unlocked items
|
||||
- Integrates with player database system
|
||||
- **Update Unlocks** (`fnc_updateUnlocks.sqf`)
|
||||
- Updates the unlock status of items
|
||||
- Maintains synchronization between client and server
|
||||
|
||||
### Vehicle Management
|
||||
1. **Garage System**
|
||||
- **Open Garage** (`fnc_openGarage.sqf`)
|
||||
- Provides access to the vehicle garage interface
|
||||
- Categorizes vehicles by type
|
||||
- **Add Garage Vehicle** (`fnc_addGarageVehicle.sqf`)
|
||||
- Handles adding new vehicles to the garage
|
||||
- Supports vehicle categories:
|
||||
- Cars
|
||||
- Armored vehicles
|
||||
- Helicopters
|
||||
- Planes
|
||||
- Naval vessels
|
||||
- Static weapons
|
||||
- **Add Virtual Vehicles** (`fnc_addVirtualVehicles.sqf`)
|
||||
- Manages virtual vehicle entries in the garage
|
||||
- Automatically categorizes vehicles by type
|
||||
- Maintains vehicle model information
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the arsenal module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Access the armory through the provided UI for weapon management
|
||||
3. Use the garage interface for vehicle management
|
||||
4. Manage unlocks and permissions through the appropriate functions
|
||||
|
||||
## Item Types
|
||||
The module supports various item categories:
|
||||
- Weapons (Primary, Secondary, Handgun)
|
||||
- Magazines
|
||||
- Items (Tools, Medical, etc.)
|
||||
- Uniforms
|
||||
- Vests
|
||||
- Backpacks
|
||||
- Facewear
|
||||
- Headgear
|
||||
- HMD (Night Vision)
|
||||
|
||||
## Vehicle Categories
|
||||
Vehicles are organized into the following categories:
|
||||
1. Cars (Light vehicles, transport)
|
||||
2. Armor (Tanks, APCs)
|
||||
3. Helicopters (All rotary-wing aircraft)
|
||||
4. Planes (Fixed-wing aircraft)
|
||||
5. Naval (Boats, ships)
|
||||
6. Static (Weapons, emplacements)
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Supports both traditional Arma 3 arsenal and ACE arsenal systems
|
||||
- Automatic vehicle categorization based on class inheritance
|
||||
- Persistent unlock system with database integration
|
||||
- Efficient item and vehicle management
|
||||
- Client-server synchronization for unlocks
|
||||
- Integration with the locker system for item storage
|
||||
- Support for virtual and physical vehicle spawning
|
@ -1,10 +1,10 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
[{
|
||||
GETVAR(player,EGVAR(player,done),false)
|
||||
GETVAR(player,value_loadDone,false)
|
||||
}, {
|
||||
private _armory_unlocks = GETVAR(player,Armory_Unlocks,GVAR(default_armory));
|
||||
private _garage_unlocks = GETVAR(player,Garage_Unlocks,GVAR(default_garage));
|
||||
private _armory_unlocks = player getVariable ["Armory_Unlocks", [[],[],[],[]]];
|
||||
private _garage_unlocks = player getVariable ["Garage_Unlocks", [[],[],[],[],[],[]]];
|
||||
|
||||
[_armory_unlocks, _garage_unlocks] call FUNC(initArsenal);
|
||||
}] call CFUNC(waitUntilAndExecute);
|
@ -7,16 +7,6 @@ PREP_RECOMPILE_END;
|
||||
|
||||
GVAR(armory_unlocks) = [];
|
||||
GVAR(garage_unlocks) = [];
|
||||
|
||||
GVAR(car_unlocks) = [];
|
||||
GVAR(armor_unlocks) = [];
|
||||
GVAR(heli_unlocks) = [];
|
||||
GVAR(plane_unlocks) = [];
|
||||
GVAR(naval_unlocks) = [];
|
||||
GVAR(static_unlocks) = [];
|
||||
|
||||
GVAR(default_armory) = [[],[],[],[]];
|
||||
GVAR(default_garage) = [[],[],[],[],[],[]];
|
||||
GVAR(pdb_mode) = "PDB_MODE" call BFUNC(getParamValue);
|
||||
GVAR(armory_type) = "ARS_TYPE" call BFUNC(getParamValue);
|
||||
GVAR(gear_box) = "ReammoBox_F" createVehicleLocal [0, 0, -999];
|
||||
|
@ -3,39 +3,31 @@
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Adds item to player's armory unlocks and updates virtual arsenal
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Classname of item to add <STRING>
|
||||
* 1: Type of item to add <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* ["B_AssaultPack_rgr", "backpack"] call forge_client_arsenal_fnc_addArmoryItem
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params [["_class", "", [""]], ["_type", "", [""]]];
|
||||
|
||||
private _armory_unlocks = GETVAR(player,Armory_Unlocks,GVAR(default_armory));
|
||||
private _default = [[],[],[],[]];
|
||||
// private _armory_unlocks = player getVariable ["Armory_Unlocks", _default];
|
||||
private _armory_unlocks = GETVAR(player,Armory_Unlocks,_default);
|
||||
|
||||
private _typeToNumber = switch (_type) do {
|
||||
case "facewear";
|
||||
case "headgear";
|
||||
case "hmd";
|
||||
case "item";
|
||||
case "uniform";
|
||||
case "backpack": {3};
|
||||
case "facewear": {0};
|
||||
case "headgear": {0};
|
||||
case "hmd": {0};
|
||||
case "item": {0};
|
||||
case "magazine": {2};
|
||||
case "uniform": {0};
|
||||
case "vest": {0};
|
||||
case "weapon": {1};
|
||||
case "magazine": {2};
|
||||
case "backpack": {3};
|
||||
default {0};
|
||||
};
|
||||
|
||||
private _index = (_armory_unlocks select _typeToNumber) pushBackUnique _class;
|
||||
|
||||
if (_index > -1) then {
|
||||
// player setVariable ["Armory_Unlocks", _armory_unlocks, true];
|
||||
SETPVAR(player,Armory_Unlocks,_armory_unlocks);
|
||||
|
||||
if (GVAR(armory_type) == 0) then {
|
||||
@ -43,7 +35,8 @@ if (_index > -1) then {
|
||||
} else {
|
||||
[GVAR(gear_box), [_class]] call AFUNC(arsenal,addVirtualItems);
|
||||
};
|
||||
|
||||
|
||||
TRACE_2("Item added to armory",_class,_type);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -3,23 +3,15 @@
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Adds vehicle to player's garage unlocks and updates virtual garage
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Classname of vehicle to add <STRING>
|
||||
* 1: Type of vehicle to add <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* ["B_T_LSV_01_unarmed_F", "car"] call forge_client_arsenal_fnc_addGarageVehicle
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
|
||||
params [["_class", "", [""]], ["_type", "", [""]]];
|
||||
|
||||
private _garage_unlocks = GETVAR(player,Garage_Unlocks,GVAR(default_garage));
|
||||
private _default = [[],[],[],[],[],[]];
|
||||
// private _garage_unlocks = player getVariable ["Garage_Unlocks", _default];
|
||||
private _garage_unlocks = GETVAR(player,Garage_Unlocks,_default);
|
||||
|
||||
private _typeToNumber = switch (_type) do {
|
||||
case "car": {0};
|
||||
case "armor": {1};
|
||||
@ -33,9 +25,11 @@ private _typeToNumber = switch (_type) do {
|
||||
private _index = (_garage_unlocks select _typeToNumber) pushBackUnique _class;
|
||||
|
||||
if (_index > -1) then {
|
||||
// player setVariable ["Garage_Unlocks", _garage_unlocks, true];
|
||||
SETPVAR(player,Garage_Unlocks,_garage_unlocks);
|
||||
[[_class]] call FUNC(addVirtualVehicles);
|
||||
|
||||
|
||||
TRACE_2("Vehicle added to garage",_class,_type);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -6,20 +6,20 @@
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Vehicles <ARRAY> - Array of vehicle classnames
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* ["B_T_LSV_01_unarmed_F", "car"] call forge_client_arsenal_fnc_addVirtualVehicles
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params [["_vehicles", [], [[]]]];
|
||||
|
||||
private _garage_unlocks = GETVAR(player,Garage_Unlocks,GVAR(default_garage));
|
||||
_garage_unlocks params ["_cars", "_armor", "_helis", "_planes", "_naval", "_static"];
|
||||
private _default = [[],[],[],[],[],[]];
|
||||
// private _garage_unlocks = player getVariable ["Garage_Unlocks", _default];
|
||||
private _garage_unlocks = GETVAR(player,Garage_Unlocks,_default);
|
||||
|
||||
private _cars = _garage_unlocks select 0;
|
||||
private _armor = _garage_unlocks select 1;
|
||||
private _helis = _garage_unlocks select 2;
|
||||
private _planes = _garage_unlocks select 3;
|
||||
private _naval = _garage_unlocks select 4;
|
||||
private _static = _garage_unlocks select 5;
|
||||
|
||||
{
|
||||
switch true do {
|
||||
@ -27,14 +27,31 @@ _garage_unlocks params ["_cars", "_armor", "_helis", "_planes", "_naval", "_stat
|
||||
if ((_x isKindOf "Tank") || (_x isKindOf "Wheeled_APC_F")) exitWith {};
|
||||
_cars pushBackUnique _x;
|
||||
};
|
||||
case (_x isKindOf "Tank"): { _armor pushBackUnique _x; };
|
||||
case (_x isKindOf "Helicopter"): { _helis pushBackUnique _x; };
|
||||
case (_x isKindOf "Plane"): { _planes pushBackUnique _x; };
|
||||
case (_x isKindOf "Ship"): { _naval pushBackUnique _x; };
|
||||
case (_x isKindOf "Static"): { _static pushBackUnique _x; };
|
||||
case (_x isKindOf "Tank"): {
|
||||
_armor pushBackUnique _x;
|
||||
};
|
||||
case (_x isKindOf "Helicopter"): {
|
||||
_helis pushBackUnique _x;
|
||||
};
|
||||
case (_x isKindOf "Plane"): {
|
||||
_planes pushBackUnique _x;
|
||||
};
|
||||
case (_x isKindOf "Ship"): {
|
||||
_naval pushBackUnique _x;
|
||||
};
|
||||
case (_x isKindOf "Static"): {
|
||||
_static pushBackUnique _x;
|
||||
};
|
||||
};
|
||||
} forEach _vehicles;
|
||||
|
||||
GVAR(car_unlocks) = [];
|
||||
GVAR(armor_unlocks) = [];
|
||||
GVAR(heli_unlocks) = [];
|
||||
GVAR(plane_unlocks) = [];
|
||||
GVAR(naval_unlocks) = [];
|
||||
GVAR(static_unlocks) = [];
|
||||
|
||||
{
|
||||
GVAR(car_unlocks) append [getText(configFile >> "CfgVehicles" >> _x >> "model"),[configFile >> "CfgVehicles" >> _x]];
|
||||
} forEach _cars;
|
||||
@ -57,4 +74,6 @@ _garage_unlocks params ["_cars", "_armor", "_helis", "_planes", "_naval", "_stat
|
||||
|
||||
{
|
||||
GVAR(static_unlocks) append [getText(configFile >> "CfgVehicles" >> _x >> "model"),[configFile >> "CfgVehicles" >> _x]];
|
||||
} forEach _static;
|
||||
} forEach _static;
|
||||
|
||||
TRACE_1("Virtual vehicles updated",count _vehicles);
|
@ -1,24 +1,9 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Initializes the arsenal system with armory and garage data
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Armory data <ARRAY> - [items, weapons, magazines, backpacks]
|
||||
* 1: Garage data <ARRAY> - [cars, armor, helicopters, planes, naval, static]
|
||||
*
|
||||
* Return Value:
|
||||
* BOOLEAN - true if initialization successful, false if invalid data
|
||||
*
|
||||
* Example:
|
||||
* [[_items, _weapons, _magazines, _backpacks], [_cars, _armor, _helis, _planes, _naval, _statics]] call forge_client_arsenal_fnc_initArsenal
|
||||
*/
|
||||
|
||||
params [["_armory_data", [], [[]]], ["_garage_data", [], [[]]]];
|
||||
|
||||
if (!(_armory_data isEqualTypeArray GVAR(default_armory)) || (count _armory_data != 4)) then { _armory_data = GVAR(default_armory); };
|
||||
if (!(_garage_data isEqualTypeArray GVAR(default_garage)) || (count _garage_data != 6)) then { _garage_data = GVAR(default_garage); };
|
||||
if (count _armory_data isEqualTo [""]) then { _armory_data = [[],[],[],[]] };
|
||||
if (count _garage_data isEqualTo [""]) then { _garage_data = [[],[],[],[],[],[]] };
|
||||
if (GVAR(armory_type) == 0) then {
|
||||
{
|
||||
[GVAR(gear_box), _x, false, true, 1, _forEachIndex] call BFUNC(addVirtualItemCargo);
|
||||
@ -29,24 +14,23 @@ if (GVAR(armory_type) == 0) then {
|
||||
} forEach _armory_data;
|
||||
};
|
||||
|
||||
_armory_data params [["_items", [], [[]]], ["_weapons", [], [[]]], ["_magazines", [], [[]]], ["_backpacks", [], [[]]]];
|
||||
_garage_data params [["_cars", [], [[]]], ["_armor", [], [[]]], ["_helis", [], [[]]], ["_planes", [], [[]]], ["_naval", [], [[]]], ["_statics", [], [[]]]];
|
||||
|
||||
GVAR(armory_unlocks) = _armory_data;
|
||||
GVAR(garage_unlocks) = _garage_data;
|
||||
|
||||
GVAR(item_unlocks) = _items;
|
||||
GVAR(weapon_unlocks) = _weapons;
|
||||
GVAR(magazine_unlocks) = _magazines;
|
||||
GVAR(backpack_unlocks) = _backpacks;
|
||||
GVAR(item_unlocks) = _armory_data select 0;
|
||||
GVAR(weapon_unlocks) = _armory_data select 1;
|
||||
GVAR(magazine_unlocks) = _armory_data select 2;
|
||||
GVAR(backpack_unlocks) = _armory_data select 3;
|
||||
|
||||
GVAR(car_unlocks) = _cars;
|
||||
GVAR(armor_unlocks) = _armor;
|
||||
GVAR(heli_unlocks) = _helis;
|
||||
GVAR(plane_unlocks) = _planes;
|
||||
GVAR(naval_unlocks) = _naval;
|
||||
GVAR(static_unlocks) = _statics;
|
||||
GVAR(car_unlocks) = _garage_data select 0;
|
||||
GVAR(armor_unlocks) = _garage_data select 1;
|
||||
GVAR(heli_unlocks) = _garage_data select 2;
|
||||
GVAR(plane_unlocks) = _garage_data select 3;
|
||||
GVAR(naval_unlocks) = _garage_data select 4;
|
||||
GVAR(static_unlocks) = _garage_data select 5;
|
||||
|
||||
{
|
||||
[_x] call FUNC(addVirtualVehicles);
|
||||
} forEach GVAR(garage_unlocks);
|
||||
} forEach GVAR(garage_unlocks);
|
||||
|
||||
TRACE_2("Arsenal System Initialized with defaults",count GVAR(armory_unlocks),count GVAR(garage_unlocks));
|
@ -1,21 +1,5 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Opens the virtual arsenal
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* None
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
if (GVAR(armory_type) == 0) then {
|
||||
["Open", [false, GVAR(gear_box), player]] call BFUNC(arsenal);
|
||||
} else {
|
||||
|
@ -1,21 +1,5 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Opens the virtual garage
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* None
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
_locations = (missionConfigFile >> "CfgGarages" >> "locations") call BFUNC(getCfgData);
|
||||
{
|
||||
GVAR(vehSpawnPos) = (_x select 1) getPos [5, (_x select 2)];
|
||||
|
@ -10,26 +10,28 @@
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* ["armory"] call forge_client_arsenal_fnc_saveUnlocks
|
||||
*
|
||||
* Public: Yes
|
||||
* Example:
|
||||
* ["armory"] call forge_client_arsenal_fnc_saveArsenalUnlocks
|
||||
*/
|
||||
|
||||
params [["_type", "", [""]]];
|
||||
|
||||
switch (_type) do {
|
||||
case "armory": {
|
||||
private _armory_data = GETVAR(player,Armory_Unlocks,GVAR(default_armory));
|
||||
private _default_armory_data = [[],[],[],[]];
|
||||
// private _armory_data = player getVariable ["Armory_Unlocks", [[],[],[],[]]];
|
||||
private _armory_data = GETVAR(player,Armory_Unlocks,_default_armory_data);
|
||||
|
||||
switch (GVAR(pdb_mode)) do {
|
||||
case 0: {
|
||||
// profileNamespace setVariable ["Armory_Unlocks", _armory_data];
|
||||
SETVAR(profileNamespace,Armory_Unlocks,_armory_data);
|
||||
};
|
||||
case 1: {
|
||||
["hsetid", getPlayerUID player, "armory_unlocks", -1, _armory_data, "", false] remoteExec ["dragonfly_db_fnc_addTask", 2, false];
|
||||
};
|
||||
default {
|
||||
// profileNamespace setVariable ["Armory_Unlocks", _armory_data];
|
||||
SETVAR(profileNamespace,Armory_Unlocks,_armory_data);
|
||||
};
|
||||
};
|
||||
@ -38,20 +40,26 @@ switch (_type) do {
|
||||
};
|
||||
|
||||
case "garage": {
|
||||
private _garage_data = GETVAR(player,Garage_Unlocks,GVAR(default_garage));
|
||||
private _default_garage_data = [[],[],[],[],[],[]];
|
||||
// private _garage_data = player getVariable ["Garage_Unlocks", [[],[],[],[],[],[]]];
|
||||
private _garage_data = GETVAR(player,Garage_Unlocks,_default_garage_data);
|
||||
|
||||
switch (GVAR(pdb_mode)) do {
|
||||
case 0: {
|
||||
// profileNamespace setVariable ["Garage_Unlocks", _garage_data];
|
||||
SETVAR(profileNamespace,Garage_Unlocks,_garage_data);
|
||||
};
|
||||
case 1: {
|
||||
["hsetid", getPlayerUID player, "garage_unlocks", -1, _garage_data, "", false] remoteExec ["dragonfly_db_fnc_addTask", 2, false];
|
||||
};
|
||||
default {
|
||||
// profileNamespace setVariable ["Garage_Unlocks", _garage_data];
|
||||
SETVAR(profileNamespace,Garage_Unlocks,_garage_data);
|
||||
};
|
||||
};
|
||||
|
||||
[_type, _garage_data] call FUNC(updateUnlocks);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
TRACE_2("Arsenal Unlocks saved",_type,GVAR(pdb_mode));
|
||||
|
@ -8,37 +8,34 @@
|
||||
* 0: Type <STRING> ("armory" or "garage")
|
||||
* 1: Data <ARRAY> (Array of unlocks)
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* Example:
|
||||
* ["armory", _armory_unlocks] call forge_client_arsenal_fnc_updateUnlocks
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params [["_type", "", [""]], ["_data", [], [[]]]];
|
||||
|
||||
switch (_type) do {
|
||||
case "armory": {
|
||||
_data params ["_items", "_weapons", "_magazines", "_backpacks"];
|
||||
|
||||
GVAR(armory_unlocks) = _data;
|
||||
GVAR(item_unlocks) = _items;
|
||||
GVAR(weapon_unlocks) = _weapons;
|
||||
GVAR(magazine_unlocks) = _magazines;
|
||||
GVAR(backpack_unlocks) = _backpacks;
|
||||
|
||||
GVAR(item_unlocks) = _data select 0;
|
||||
GVAR(weapon_unlocks) = _data select 1;
|
||||
GVAR(magazine_unlocks) = _data select 2;
|
||||
GVAR(backpack_unlocks) = _data select 3;
|
||||
|
||||
TRACE_1("Armory unlocks updated",count GVAR(armory_unlocks));
|
||||
};
|
||||
|
||||
case "garage": {
|
||||
_data params ["_cars", "_armor", "_helis", "_planes", "_naval", "_static"];
|
||||
|
||||
GVAR(garage_unlocks) = _data;
|
||||
GVAR(car_unlocks) = _cars;
|
||||
GVAR(armor_unlocks) = _armor;
|
||||
GVAR(heli_unlocks) = _helis;
|
||||
GVAR(plane_unlocks) = _planes;
|
||||
GVAR(naval_unlocks) = _naval;
|
||||
GVAR(static_unlocks) = _static;
|
||||
|
||||
GVAR(car_unlocks) = _data select 0;
|
||||
GVAR(armor_unlocks) = _data select 1;
|
||||
GVAR(heli_unlocks) = _data select 2;
|
||||
GVAR(plane_unlocks) = _data select 3;
|
||||
GVAR(naval_unlocks) = _data select 4;
|
||||
GVAR(static_unlocks) = _data select 5;
|
||||
|
||||
TRACE_1("Garage unlocks updated",count GVAR(garage_unlocks));
|
||||
};
|
||||
};
|
@ -1,123 +0,0 @@
|
||||
# Forge Bank Module
|
||||
|
||||
## Overview
|
||||
The Bank module provides a comprehensive banking system for the Forge client. It includes features for managing player finances, transfers, and timesheet functionality, with a modern web-based interface for seamless user experience.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Banking System
|
||||
1. **Bank Initialization** (`fnc_initBank.sqf`)
|
||||
- Initializes the banking system
|
||||
- Sets up necessary configurations and account structures
|
||||
- Configures player account data and balances
|
||||
- Handles server-side synchronization
|
||||
|
||||
2. **Bank Interface** (`fnc_openBank.sqf`)
|
||||
- Opens the banking user interface
|
||||
- Provides access to all banking functions
|
||||
- Modern web-based UI with real-time updates
|
||||
- Responsive design for optimal user experience
|
||||
|
||||
3. **Transaction Management**
|
||||
- **Deposit** (`fnc_deposit.sqf`)
|
||||
- Handles money deposits into bank accounts
|
||||
- Validates transaction amounts
|
||||
- Updates account balances in real-time
|
||||
- **Withdraw** (`fnc_withdraw.sqf`)
|
||||
- Manages money withdrawals from bank accounts
|
||||
- Ensures sufficient funds
|
||||
- Updates wallet balance immediately
|
||||
- **Transfer** (`fnc_transfer.sqf`)
|
||||
- Handles money transfers between players
|
||||
- Validates recipient and amount
|
||||
- Supports both account-to-account and wallet transfers
|
||||
- **Submit** (`fnc_submit.sqf`)
|
||||
- Processes transaction submissions
|
||||
- Handles timesheet submissions
|
||||
- Manages pending payments
|
||||
- **Refresh** (`fnc_refresh.sqf`)
|
||||
- Updates account information and balances
|
||||
- Synchronizes with server data
|
||||
- Updates UI elements
|
||||
|
||||
### User Interface
|
||||
The module includes a modern web-based interface with:
|
||||
1. **Account Overview**
|
||||
- Current wallet balance
|
||||
- Bank account balance
|
||||
- Pending payments
|
||||
- Transaction history
|
||||
|
||||
2. **Transaction Features**
|
||||
- Transfer between wallet and account
|
||||
- Player-to-player transfers
|
||||
- Timesheet submission
|
||||
- Transaction history view
|
||||
|
||||
3. **UI Components**
|
||||
- Real-time balance updates
|
||||
- Transaction notifications
|
||||
- Input validation
|
||||
- Error handling
|
||||
- Responsive design
|
||||
|
||||
### Timesheet System
|
||||
1. **Rating Integration**
|
||||
- Tracks player rating
|
||||
- Calculates pending payments
|
||||
- Automatic payment processing
|
||||
- Rating reset after submission
|
||||
|
||||
2. **Payment Processing**
|
||||
- Automatic calculation based on rating
|
||||
- Configurable payment multiplier
|
||||
- Pending payment tracking
|
||||
- Instant payment processing
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the bank module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Access the bank through the provided UI
|
||||
3. Perform transactions using the appropriate functions:
|
||||
- Transfer money between wallet and account
|
||||
- Send money to other players
|
||||
- Submit timesheets for payment
|
||||
- View transaction history
|
||||
4. Monitor account status and transactions in real-time
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Web-based interface using HTML5, CSS3, and JavaScript
|
||||
- Real-time data synchronization with server
|
||||
- Secure transaction processing
|
||||
- Client-server event system
|
||||
- Persistent data storage
|
||||
- Rating-based payment system
|
||||
- Transaction history tracking
|
||||
- Input validation and error handling
|
||||
- Responsive UI design
|
||||
- Cross-browser compatibility
|
@ -1,4 +1,5 @@
|
||||
PREP(deposit);
|
||||
PREP(initBank);
|
||||
PREP(openBank);
|
||||
PREP(refresh);
|
||||
PREP(submit);
|
||||
|
@ -1,12 +1 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
[{
|
||||
GETVAR(player,value_loadDone,false)
|
||||
}, {
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _cash = GETVAR(player,FORGE_Cash,0);
|
||||
private _rating = GETVAR(player,FORGE_Rating,0);
|
||||
private _uid = getPlayerUID player;
|
||||
|
||||
["forge_server_bank_handleEvents", ["BANK::HANDLE::PLAYER::LOAD", [_uid, _bank, _cash, _rating]]] call CFUNC(serverEvent);
|
||||
}] call CFUNC(waitUntilAndExecute);
|
||||
#include "script_component.hpp"
|
@ -1,97 +1 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
[QGVAR(handleEvents), {
|
||||
params ["_control", "_isConfirmDialog", "_message"];
|
||||
|
||||
diag_log text format ["[FORGE::Client::Bank::XEH_postInit::handleEvents] Received event: '%1'", _message];
|
||||
|
||||
_message = fromJSON _message;
|
||||
private _event = _message get "event";
|
||||
private _data = _message get "data";
|
||||
|
||||
switch (_event) do {
|
||||
case "REQUEST::PLAYER::DATA": {
|
||||
private _playerData = createHashMap;
|
||||
private _playerList = [];
|
||||
|
||||
{
|
||||
private _uid = getPlayerUID _x;
|
||||
private _name = name _x;
|
||||
private _funds = GETVAR(_x,FORGE_Bank,0); //TODO: Implement funds from server
|
||||
private _cash = GETVAR(_x,FORGE_Cash,0); //TODO: Implement cash from server
|
||||
|
||||
private _playerInfo = createHashMapFromArray [
|
||||
["uid", _uid],
|
||||
["name", _name],
|
||||
["funds", _funds],
|
||||
["cash", _cash]
|
||||
];
|
||||
|
||||
_playerList pushBack _playerInfo;
|
||||
} forEach allPlayers;
|
||||
|
||||
_playerData set ["players", _playerList];
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["handlePlayerDataRequest(%1)", (toJSON _playerList)]];
|
||||
};
|
||||
case "REQUEST::PLAYER::FUNDS": {
|
||||
private _playerData = createHashMap;
|
||||
private _balance = GETVAR(player,FORGE_Bank,0); //TODO: Implement balance from server
|
||||
private _cash = GETVAR(player,FORGE_Cash,0); //TODO: Implement cash from server
|
||||
|
||||
private _payMultiplier = "MULTIPLYR" call BFUNC(getParamValue);
|
||||
private _rating = rating player; //TODO: Implement rating from server
|
||||
private _pending = _rating * _payMultiplier; //TODO: Implement pending from server
|
||||
|
||||
private _playerData = createHashMapFromArray [
|
||||
["balance", _balance],
|
||||
["cash", _cash],
|
||||
["pending", _pending]
|
||||
];
|
||||
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["handlePlayerFundsRequest(%1)", (toJSON _playerData)]];
|
||||
};
|
||||
case "REQUEST::TRANSACTION::HISTORY": {
|
||||
private _history = []; //TODO: Implement history from server
|
||||
private _historyData = createHashMapFromArray [["history", _history]];
|
||||
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["handleTransactionHistoryRequest(%1)", (toJSON _historyData)]];
|
||||
};
|
||||
case "DEPOSIT::FUNDS": {
|
||||
_data params [["_amount", 0, [0]]];
|
||||
|
||||
if (_amount <= 0) exitWith { systemChat "Invalid amount, must be greater than 0!"; false };
|
||||
["forge_server_bank_handleEvents", ["BANK::DEPOSIT", [getPlayerUID player, _amount]]] call CFUNC(serverEvent);
|
||||
|
||||
true
|
||||
};
|
||||
case "SUBMIT::TIMESHEET": {
|
||||
private _rating = rating player;
|
||||
private _uid = getPlayerUID player;
|
||||
|
||||
["forge_server_bank_handleEvents", ["BANK::SUBMIT::TIMESHEET", [_uid, _rating]]] call CFUNC(serverEvent);
|
||||
|
||||
player addRating -_rating;
|
||||
|
||||
true
|
||||
};
|
||||
case "TRANSFER::FUNDS": {
|
||||
_data params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if ({_uid isEqualTo ""} || _amount <= 0) exitWith { systemChat "Invalid UID or amount, UID cannot be empty and amount must be greater than 0!"; false };
|
||||
["forge_server_bank_handleEvents", ["BANK::TRANSFER", [getPlayerUID player, _uid, _amount]]] call CFUNC(serverEvent);
|
||||
|
||||
true
|
||||
};
|
||||
case "WITHDRAW::FUNDS": {
|
||||
_data params [["_amount", 0, [0]]];
|
||||
|
||||
if (_amount <= 0) exitWith { systemChat "Invalid amount, amount must be greater than 0!"; false };
|
||||
["forge_server_bank_handleEvents", ["BANK::WITHDRAW", [getPlayerUID player, _amount]]] call CFUNC(serverEvent);
|
||||
|
||||
true
|
||||
};
|
||||
default {
|
||||
diag_log format ["[FORGE::Client::Bank::XEH_postInit::handleEvents] Unhandled event: %1", _event];
|
||||
};
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
#include "script_component.hpp"
|
@ -15,5 +15,4 @@ class CfgPatches {
|
||||
|
||||
#include "CfgEventHandlers.hpp"
|
||||
#include "ui\BaseControls.hpp"
|
||||
#include "ui\RscBankDialog.hpp"
|
||||
#include "ui\RscWebBank.hpp"
|
||||
#include "ui\RscBankDialog.hpp"
|
@ -1,34 +1,23 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Deposits money into the bank.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_client_bank_fnc_deposit;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
private _amount = parseNumber (ctrlText IDC_AMOUNTINPUT);
|
||||
// private _bank = player getVariable ["FORGE_Bank", 0];
|
||||
// private _cash = player getVariable ["FORGE_Cash", 0];
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _cash = GETVAR(player,FORGE_Cash,0);
|
||||
|
||||
private _messageData = createHashMapFromArray [
|
||||
["event", "DEPOSIT::FUNDS"],
|
||||
["data", createHashMapFromArray [
|
||||
["amount", _amount]
|
||||
]]
|
||||
];
|
||||
if (_amount > 0 && _amount <= _cash) then {
|
||||
_cash = _cash - _amount;
|
||||
_bank = _bank + _amount;
|
||||
|
||||
private _response = ["forge_client_bank_handleEvents", (toJSON _messageData)] call CFUNC(localEvent);
|
||||
// player setVariable ["FORGE_Bank", _bank, true];
|
||||
// player setVariable ["FORGE_Cash", _cash, true];
|
||||
SETPVAR(player,FORGE_Bank,_bank);
|
||||
SETPVAR(player,FORGE_Cash,_cash);
|
||||
|
||||
if (_response) then {
|
||||
[format ["Deposited $%1", _amount], "info", 3, "right"] call EFUNC(misc,notify);
|
||||
// [] call forge_client_bank_fnc_refresh;
|
||||
[] call FUNC(refresh);
|
||||
hint "Money deposited successfully";
|
||||
} else {
|
||||
[format ["Deposit failed"], "warning", 3, "right"] call EFUNC(misc,notify);
|
||||
hint "Invalid amount";
|
||||
};
|
30
addons/bank/functions/fnc_initBank.sqf
Normal file
30
addons/bank/functions/fnc_initBank.sqf
Normal file
@ -0,0 +1,30 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
{
|
||||
private _configName = configName(_x);
|
||||
private _className = (missionConfigFile >> "CfgBanks" >> "banks" >> _configName >> "className") call BFUNC(getCfgData);
|
||||
private _pos = (missionConfigFile >> "CfgBanks" >> "banks" >> _configName >> "pos") call BFUNC(getCfgData);
|
||||
private _dir = (missionConfigFile >> "CfgBanks" >> "banks" >> _configName >> "dir") call BFUNC(getCfgData);
|
||||
private _type = (missionConfigFile >> "CfgBanks" >> "banks" >> _configName >> "type") call BFUNC(getCfgData);
|
||||
|
||||
if (_type == "object") then {
|
||||
private _bank = createSimpleObject [_className, [0, 0, 0]];
|
||||
|
||||
_bank setPosATL _pos;
|
||||
_bank setDir _dir;
|
||||
_bank allowDamage false;
|
||||
_bank setVariable ["isBank", true, true];
|
||||
} else {
|
||||
private _group = createGroup civilian;
|
||||
private _bank = _group createUnit [_className, [0, 0, 0], [], 0, "NONE"];
|
||||
|
||||
_bank disableAI "MOVE";
|
||||
_bank setPosATL _pos;
|
||||
_bank setDir _dir;
|
||||
_bank allowDamage false;
|
||||
_bank setVariable ["isBank", true, true];
|
||||
_bank setVariable ["BIS_enableRandomization", false];
|
||||
};
|
||||
|
||||
diag_log text format ["[FORGE Bank] ClassName: '%1' Pos: '%2' Dir: '%3'", _className, _pos, _dir];
|
||||
} forEach ("true" configClasses (missionConfigFile >> "CfgBanks" >> "banks"));
|
@ -1,60 +1,29 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Opens the bank dialog.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_client_bank_fnc_openBank;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
private _productVersion = productVersion;
|
||||
private _steamBranchName = _productVersion select 8;
|
||||
// private _bank = player getVariable ["FORGE_Bank", 0];
|
||||
// private _cash = player getVariable ["FORGE_Cash", 0];
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _cash = GETVAR(player,FORGE_Cash,0);
|
||||
private _payMultiplier = "MULTIPLYR" call BFUNC(getParamValue);
|
||||
private _plyscore = rating player;
|
||||
private _pending = _plyscore * _payMultiplier;
|
||||
|
||||
if (_steamBranchName == "profiling") then {
|
||||
private _display = (findDisplay 46) createDisplay "RscWebBank";
|
||||
private _ctrl = _display displayCtrl 2025;
|
||||
disableSerialization;
|
||||
createDialog "RscBankDialog";
|
||||
|
||||
_ctrl ctrlAddEventHandler ["JSDialog", {
|
||||
params ["_control", "_isConfirmDialog", "_message"];
|
||||
[QGVAR(handleEvents), [_control, _isConfirmDialog, _message]] call CFUNC(localEvent);
|
||||
}];
|
||||
private _formattedBank = _bank call EFUNC(misc,formatNumber);
|
||||
private _formattedCash = _cash call EFUNC(misc,formatNumber);
|
||||
private _formattedPending = _pending call EFUNC(misc,formatNumber);
|
||||
|
||||
_ctrl ctrlWebBrowserAction ["LoadFile", QUOTE(PATHTOF(ui\_site\index.html))];
|
||||
// _ctrl ctrlWebBrowserAction ["OpenDevConsole"];
|
||||
} else {
|
||||
disableSerialization;
|
||||
createDialog "RscBankDialog";
|
||||
ctrlSetText [IDC_CASHTEXT, format ["Cash: $%1", _formattedCash]];
|
||||
ctrlSetText [IDC_BANKTEXT, format ["Bank: $%1", _formattedBank]];
|
||||
ctrlSetText [IDC_RATINGTEXT, format ["Pending: %1", _formattedPending]];
|
||||
ctrlSetText [IDC_TIMESHEETTEXT, "Ready for Timesheet"];
|
||||
|
||||
private _uid = getPlayerUID player;
|
||||
private _bank = GETVAR(player,FORGE_Bank,0); //TODO: Implement balance from server
|
||||
private _cash = GETVAR(player,FORGE_Cash,0); //TODO: Implement cash from server
|
||||
private _plyscore = rating player;
|
||||
private _pending = _plyscore * _payMultiplier;
|
||||
{
|
||||
lbAdd [IDC_PLAYERINPUT, name _x];
|
||||
lbSetData [IDC_PLAYERINPUT, _forEachIndex, netId _x];
|
||||
} forEach allPlayers;
|
||||
|
||||
private _formattedBank = _bank call EFUNC(misc,formatNumber);
|
||||
private _formattedCash = _cash call EFUNC(misc,formatNumber);
|
||||
private _formattedPending = _pending call EFUNC(misc,formatNumber);
|
||||
|
||||
ctrlSetText [IDC_CASHTEXT, format ["Cash: $%1", _formattedCash]];
|
||||
ctrlSetText [IDC_BANKTEXT, format ["Bank: $%1", _formattedBank]];
|
||||
ctrlSetText [IDC_RATINGTEXT, format ["Pending: %1", _formattedPending]];
|
||||
ctrlSetText [IDC_TIMESHEETTEXT, "Ready for Timesheet"];
|
||||
|
||||
{
|
||||
lbAdd [IDC_PLAYERINPUT, name _x];
|
||||
lbSetData [IDC_PLAYERINPUT, _forEachIndex, netId _x];
|
||||
} forEach allPlayers;
|
||||
|
||||
lbSetCurSel [IDC_PLAYERINPUT, 0];
|
||||
ctrlSetText [IDC_AMOUNTINPUT, ""];
|
||||
};
|
||||
lbSetCurSel [IDC_PLAYERINPUT, 0];
|
||||
ctrlSetText [IDC_AMOUNTINPUT, ""];
|
@ -1,24 +1,9 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Refreshes the bank dialog.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_client_bank_fnc_refresh;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
private _bank = GETVAR(player,FORGE_Bank,0); //TODO: Implement balance from server
|
||||
private _cash = GETVAR(player,FORGE_Cash,0); //TODO: Implement cash from server
|
||||
|
||||
// private _bank = player getVariable ["FORGE_Bank", 0];
|
||||
// private _cash = player getVariable ["FORGE_Cash", 0];
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _cash = GETVAR(player,FORGE_Cash,0);
|
||||
private _payMultiplier = "MULTIPLYR" call BFUNC(getParamValue);
|
||||
private _plyscore = rating player;
|
||||
private _pending = _plyscore * _payMultiplier;
|
||||
|
@ -1,39 +1,23 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Submits the timesheet
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Examples:
|
||||
* None
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
// private _bank = player getVariable ["FORGE_Bank", 0];
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _payMultiplyer = "MULTIPLYR" call BFUNC(getParamValue);
|
||||
private _plyscore = rating player; //TODO: Implement rating from server
|
||||
private _plyscore = rating player;
|
||||
private _multiplyer = _plyscore * _payMultiplyer;
|
||||
|
||||
_bank = _bank + _multiplyer;
|
||||
|
||||
player addRating - _plyscore;
|
||||
// player setVariable ["FORGE_Bank", _bank];
|
||||
// player setVariable ["FORGE_Rating", 0];
|
||||
SETPVAR(player,FORGE_Bank,_bank);
|
||||
SETPVAR(player,FORGE_Rating,0);
|
||||
|
||||
// [] call forge_client_bank_fnc_refresh;
|
||||
[] call FUNC(refresh);
|
||||
|
||||
private _formattedRating = _bank call EFUNC(misc,formatNumber);
|
||||
private _messageData = createHashMapFromArray [
|
||||
["event", "SUBMIT::TIMESHEET"],
|
||||
["data", []]
|
||||
];
|
||||
|
||||
private _response = ["forge_client_bank_handleEvents", (toJSON _messageData)] call CFUNC(localEvent);
|
||||
|
||||
if (_response) then {
|
||||
[format ["Submitted timesheet! Received $%1 based on rating of %2", _formattedRating, _plyscore], "info", 3, "right"] call EFUNC(misc,notify);
|
||||
} else {
|
||||
[format ["Timesheet submission failed"], "warning", 3, "right"] call EFUNC(misc,notify);
|
||||
};
|
||||
|
||||
[] call FUNC(refresh);
|
||||
// hint format ["Timesheet submitted! Received $%1 bonus based on rating of %2", _formattedRating, _plyscore];
|
||||
[format ["Timesheet submitted! Received $%1 based on rating of %2", _formattedRating, _plyscore], "info", 3, "right"] call EFUNC(misc,notify);
|
@ -1,43 +1,32 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Transfers money to a player
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_client_bank_fnc_transfer;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
private _display = findDisplay IDD_BANKDIALOG;
|
||||
private _input = _display displayCtrl IDC_AMOUNTINPUT;
|
||||
private _dropdown = _display displayCtrl IDC_PLAYERINPUT;
|
||||
|
||||
private _amount = parseNumber (ctrlText _input);
|
||||
private _selectedTarget = lbCurSel _dropdown;
|
||||
private _selectedTargetData = _dropdown lbData _selectedTarget;
|
||||
private _target = objectFromNetId _selectedTargetData;
|
||||
private _uid = getPlayerUID _target;
|
||||
|
||||
private _messageData = createHashMapFromArray [
|
||||
["event", "TRANSFER::FUNDS"],
|
||||
["data", createHashMapFromArray [
|
||||
["uid", _uid],
|
||||
["amount", _amount]
|
||||
]]
|
||||
];
|
||||
// private _bank = player getVariable ["FORGE_Bank", 0];
|
||||
// private _targetBank = _target getVariable ["FORGE_Bank", 0];
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _targetBank = GETVAR(_target,FORGE_Bank,0);
|
||||
|
||||
private _response = [QGVAR(handleEvents), (toJSON _messageData)] call CFUNC(localEvent);
|
||||
if (!isNull _target && _amount > 0 && _amount <= _bank) then {
|
||||
_targetBank = _targetBank + _amount;
|
||||
// _target setVariable ["FORGE_Bank", _targetBank, true];
|
||||
SETPVAR(_target,FORGE_Bank,_targetBank);
|
||||
|
||||
if (_response) then {
|
||||
_bank = _bank - _amount;
|
||||
// player setVariable ["FORGE_Bank", _bank, true];
|
||||
SETPVAR(player,FORGE_Bank,_bank);
|
||||
|
||||
// [] call forge_client_bank_fnc_refresh;
|
||||
[] call FUNC(refresh);
|
||||
// hint format ["Transferred $%1", _amount];
|
||||
[format ["Transferred $%1", _amount], "info", 3, "right"] call EFUNC(misc,notify);
|
||||
} else {
|
||||
[format ["Transfer failed"], "warning", 3, "right"] call EFUNC(misc,notify);
|
||||
// hint "Invalid transfer details";
|
||||
[format ["Invalid transfer details"], "warning", 3, "right"] call EFUNC(misc,notify);
|
||||
};
|
@ -1,36 +1,28 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Withdraws money from the bank
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_client_bank_fnc_withdraw;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
private _display = findDisplay IDD_BANKDIALOG;
|
||||
private _input = _display displayCtrl IDC_AMOUNTINPUT;
|
||||
private _amount = parseNumber (ctrlText _input);
|
||||
|
||||
private _messageData = createHashMapFromArray [
|
||||
["event", "WITHDRAW::FUNDS"],
|
||||
["data", createHashMapFromArray [
|
||||
["amount", _amount]
|
||||
]]
|
||||
];
|
||||
// private _bank = player getVariable ["FORGE_Bank", 0];
|
||||
// private _cash = player getVariable ["FORGE_Cash", 0];
|
||||
private _bank = GETVAR(player,FORGE_Bank,0);
|
||||
private _cash = GETVAR(player,FORGE_Cash,0);
|
||||
|
||||
private _response = ["forge_client_bank_handleEvents", (toJSON _messageData)] call CFUNC(localEvent);
|
||||
if (_amount > 0 && _amount <= _bank) then {
|
||||
_bank = _bank - _amount;
|
||||
_cash = _cash + _amount;
|
||||
|
||||
if (_response) then {
|
||||
[format ["Withdrawn $%1", _amount], "info", 3, "right"] call EFUNC(misc,notify);
|
||||
// player setVariable ["FORGE_Bank", _bank, true];
|
||||
// player setVariable ["FORGE_Cash", _cash, true];
|
||||
SETPVAR(player,FORGE_Bank,_bank);
|
||||
SETPVAR(player,FORGE_Cash,_cash);
|
||||
|
||||
// [] call forge_client_bank_fnc_refresh;
|
||||
[] call FUNC(refresh);
|
||||
// hint "Money withdrawn successfully";
|
||||
[format ["Money withdrawn successfully"], "info", 3, "right"] call EFUNC(misc,notify);
|
||||
} else {
|
||||
[format ["Withdrawal failed"], "warning", 3, "right"] call EFUNC(misc,notify);
|
||||
// hint "Invalid amount";
|
||||
[format ["Invalid amount"], "warning", 3, "right"] call EFUNC(misc,notify);
|
||||
};
|
@ -4,6 +4,9 @@
|
||||
<Key ID="STR_forge_client_bank_Amount">
|
||||
<English>Amount</English>
|
||||
</Key>
|
||||
<Key ID="STR_forge_client_bank_Close">
|
||||
<English>Close</English>
|
||||
</Key>
|
||||
<Key ID="STR_forge_client_bank_Deposit">
|
||||
<English>Deposit</English>
|
||||
</Key>
|
||||
|
@ -1,17 +0,0 @@
|
||||
class RscWebBank {
|
||||
idd = 20250502;
|
||||
fadein = 0;
|
||||
fadeout = 0;
|
||||
duration = 1e+011;
|
||||
|
||||
class controls {
|
||||
class Background: RscText {
|
||||
type = 106;
|
||||
idc = 2025;
|
||||
x = "safeZoneY * (pixelW/pixelH) * 2.975";
|
||||
y = "safeZoneY + (safeZoneH * 0.05)";
|
||||
w = "safeZoneW * (pixelW/pixelH) * 1.17";
|
||||
h = "safeZoneH * 0.875";
|
||||
};
|
||||
};
|
||||
};
|
@ -1,111 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!--
|
||||
Dynamic Resource Loading
|
||||
The following script loads CSS and JavaScript files dynamically using the A3API
|
||||
This approach is used instead of static HTML imports to work with Arma 3's file system
|
||||
-->
|
||||
<script>
|
||||
Promise.all([
|
||||
// Load CSS file
|
||||
A3API.RequestFile("z\\forge_client\\addons\\bank\\ui\\_site\\styles.css"),
|
||||
// Load JavaScript file
|
||||
A3API.RequestFile("z\\forge_client\\addons\\bank\\ui\\_site\\script.js")
|
||||
]).then(([css, js]) => {
|
||||
// Apply CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = css;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Load and execute JavaScript
|
||||
const script = document.createElement('script');
|
||||
script.text = js;
|
||||
document.head.appendChild(script);
|
||||
|
||||
// Initialize the bank interface
|
||||
initializeBank();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="header-content">
|
||||
<h1>Federal Deposit Insurance Corporation</h1>
|
||||
<div class="balance-display">
|
||||
<div class="balance-item">
|
||||
<div class="balance-info">
|
||||
<span class="balance-label">Wallet Balance</span>
|
||||
<span class="balance-amount" id="walletBalance">$0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="balance-divider"></div>
|
||||
<div class="balance-item">
|
||||
<div class="balance-info">
|
||||
<span class="balance-label">Account Balance</span>
|
||||
<span class="balance-amount" id="accountBalance">$0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
<div class="actions-grid">
|
||||
<!-- Transfer Between Accounts -->
|
||||
<div class="action-tile">
|
||||
<h2>Transfer Money</h2>
|
||||
<div class="form-group">
|
||||
<label for="transferType">Transfer From</label>
|
||||
<select id="transferType" required>
|
||||
<option value="to_wallet">Account to Wallet</option>
|
||||
<option value="to_account">Wallet to Account</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="transferAmount">Amount</label>
|
||||
<input type="number" id="transferAmount" min="1" step="1" required>
|
||||
</div>
|
||||
<button type="button" class="submit-btn" onclick="handleTransfer()">Transfer</button>
|
||||
</div>
|
||||
|
||||
<!-- Transfer to Player -->
|
||||
<div class="action-tile">
|
||||
<h2>Transfer to Player</h2>
|
||||
<div class="form-group">
|
||||
<label for="playerSelect">Player</label>
|
||||
<select id="playerSelect" required>
|
||||
<option value="" disabled selected>Select Player</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="playerTransferAmount">Amount</label>
|
||||
<input type="number" id="playerTransferAmount" min="1" step="1" required>
|
||||
</div>
|
||||
<button type="button" class="submit-btn" onclick="handlePlayerTransfer()">Send Money</button>
|
||||
</div>
|
||||
|
||||
<!-- Submit Timesheet -->
|
||||
<div class="action-tile">
|
||||
<h2>Submit Timesheet</h2>
|
||||
<div class="pending-amount">
|
||||
<div class="amount-label">Pending Payment</div>
|
||||
<div class="amount-value" id="pending">$0</div>
|
||||
</div>
|
||||
<button type="button" class="submit-btn" onclick="handleTimesheet()">Submit Timesheet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transaction History -->
|
||||
<section class="history-section">
|
||||
<h2>Transaction History</h2>
|
||||
<ul class="history-list" id="transactionHistory">
|
||||
<!-- Transactions will be added here dynamically -->
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 92 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 109 KiB |
Binary file not shown.
@ -1,305 +0,0 @@
|
||||
/**
|
||||
* Bank Management Script
|
||||
* This script handles the bank interface functionality for the Arma 3 game interface.
|
||||
* It provides wallet/account management, transfers, and transaction history.
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
// #region DATA STRUCTURES AND VARIABLES
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Bank state - will be populated from game events
|
||||
*/
|
||||
let bankState = {
|
||||
wallet: 0,
|
||||
account: 0,
|
||||
pending: 0,
|
||||
players: [],
|
||||
transactions: []
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
//=============================================================================
|
||||
// #region INITIALIZATION AND DATA REQUESTS
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Initialize the bank interface
|
||||
* Sets up the UI and requests initial data from the game engine
|
||||
*/
|
||||
function initializeBank() {
|
||||
// Request initial data from the game
|
||||
requestPlayerFunds();
|
||||
requestPlayerData();
|
||||
requestTransactionHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request player funds data from the game engine
|
||||
*/
|
||||
function requestPlayerFunds() {
|
||||
const message = {
|
||||
event: "REQUEST::PLAYER::FUNDS",
|
||||
data: {}
|
||||
};
|
||||
|
||||
A3API.SendAlert(JSON.stringify(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request player data from the game engine
|
||||
*/
|
||||
function requestPlayerData() {
|
||||
const message = {
|
||||
event: "REQUEST::PLAYER::DATA",
|
||||
data: {}
|
||||
};
|
||||
|
||||
A3API.SendAlert(JSON.stringify(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request transaction history from the game engine
|
||||
*/
|
||||
function requestTransactionHistory() {
|
||||
const message = {
|
||||
event: "REQUEST::TRANSACTION::HISTORY",
|
||||
data: {}
|
||||
};
|
||||
|
||||
A3API.SendAlert(JSON.stringify(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a timer to refresh player data every 30 seconds
|
||||
* Ensures the UI is updated with the latest information
|
||||
*/
|
||||
function setupRefreshTimer() {
|
||||
setInterval(requestPlayerData, 30000); // Refresh every 30 seconds
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
//=============================================================================
|
||||
// #region DATA HANDLERS
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Handle player data received from the game engine
|
||||
* @param {Array} data - List of player objects
|
||||
*/
|
||||
function handlePlayerDataRequest(data) {
|
||||
bankState.players = data;
|
||||
populatePlayerList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle player funds data received from the game engine
|
||||
* @param {Object} data - Object containing cash and balance
|
||||
*/
|
||||
function handlePlayerFundsRequest(data) {
|
||||
console.log('Received funds data:', data);
|
||||
|
||||
// Parse the data if it's a string
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse data:', e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have valid numbers, default to 0 if null
|
||||
bankState.wallet = data.cash !== null ? Number(data.cash) : 0;
|
||||
bankState.account = data.balance !== null ? Number(data.balance) : 0;
|
||||
bankState.pending = data.pending !== null ? Number(data.pending) : 0;
|
||||
updateBalanceDisplays();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle transaction history data received from the game engine
|
||||
* @param {Object} data - Object containing transaction history
|
||||
*/
|
||||
function handleTransactionHistoryRequest(data) {
|
||||
console.log('Received transaction history:', data);
|
||||
|
||||
// Parse the data if it's a string
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse transaction history:', e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize empty array if history is null
|
||||
bankState.transactions = [];
|
||||
updateTransactionHistory();
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
//=============================================================================
|
||||
// #region UI UPDATES AND DISPLAY
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Update balance displays in the header
|
||||
*/
|
||||
function updateBalanceDisplays() {
|
||||
const walletElement = document.getElementById('walletBalance');
|
||||
const accountElement = document.getElementById('accountBalance');
|
||||
const pendingElement = document.getElementById('pending');
|
||||
|
||||
if (walletElement && accountElement && pendingElement) {
|
||||
walletElement.textContent = `$${(bankState.wallet || 0).toLocaleString()}`;
|
||||
accountElement.textContent = `$${(bankState.account || 0).toLocaleString()}`;
|
||||
pendingElement.textContent = `$${(bankState.pending || 0).toLocaleString()}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the player selection dropdown
|
||||
*/
|
||||
function populatePlayerList() {
|
||||
const playerSelect = document.getElementById('playerSelect');
|
||||
playerSelect.innerHTML = '<option value="" disabled selected>Select Player</option>';
|
||||
|
||||
bankState.players.forEach(player => {
|
||||
const option = document.createElement('option');
|
||||
option.value = player.uid;
|
||||
option.textContent = player.name;
|
||||
playerSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the transaction history display
|
||||
*/
|
||||
function updateTransactionHistory() {
|
||||
const historyList = document.getElementById('transactionHistory');
|
||||
if (!historyList) return;
|
||||
|
||||
historyList.innerHTML = '';
|
||||
|
||||
if (!Array.isArray(bankState.transactions)) {
|
||||
console.error('Transaction history is not an array:', bankState.transactions);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bankState.transactions.length === 0) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'history-item empty';
|
||||
li.textContent = 'No transactions yet';
|
||||
historyList.appendChild(li);
|
||||
return;
|
||||
}
|
||||
|
||||
bankState.transactions.forEach(transaction => {
|
||||
if (!transaction) return;
|
||||
|
||||
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 ? '-' : '+';
|
||||
const amount = Math.abs(Number(transaction.amount) || 0);
|
||||
|
||||
li.innerHTML = `
|
||||
<span class="transaction-type">${formatTransactionType(transaction.type)}</span>
|
||||
<span class="transaction-details">${transaction.details || ''}</span>
|
||||
<span class="amount ${amountClass}">${amountPrefix}$${amount.toLocaleString()}</span>
|
||||
`;
|
||||
|
||||
historyList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format transaction type for display
|
||||
* @param {string} type - Transaction type code
|
||||
* @returns {string} Formatted transaction type
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
//=============================================================================
|
||||
// #region ACTION HANDLERS
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Handle transfer between wallet and account
|
||||
*/
|
||||
function handleTransfer() {
|
||||
const amount = parseInt(document.getElementById('transferAmount').value);
|
||||
const transferType = document.getElementById('transferType').value;
|
||||
const event = transferType === 'to_wallet' ? 'WITHDRAW::FUNDS' : 'DEPOSIT::FUNDS';
|
||||
const message = {
|
||||
event: event,
|
||||
data: [amount]
|
||||
};
|
||||
|
||||
A3API.SendAlert(JSON.stringify(message));
|
||||
document.getElementById('transferAmount').value = '';
|
||||
setTimeout(requestPlayerFunds, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle transfer to another player
|
||||
*/
|
||||
function handlePlayerTransfer() {
|
||||
const amount = parseInt(document.getElementById('playerTransferAmount').value);
|
||||
const playerUid = document.getElementById('playerSelect').value;
|
||||
const message = {
|
||||
event: 'TRANSFER::FUNDS',
|
||||
data: [amount, playerUid]
|
||||
};
|
||||
|
||||
A3API.SendAlert(JSON.stringify(message));
|
||||
document.getElementById('playerTransferAmount').value = '';
|
||||
setTimeout(requestPlayerFunds, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle timesheet submission
|
||||
*/
|
||||
function handleTimesheet() {
|
||||
const message = {
|
||||
event: 'SUBMIT::TIMESHEET',
|
||||
data: {}
|
||||
};
|
||||
|
||||
A3API.SendAlert(JSON.stringify(message));
|
||||
setTimeout(requestPlayerFunds, 500);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
//=============================================================================
|
||||
// #region INITIALIZATION
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Initialize when DOM is loaded
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initializeBank()
|
||||
setupRefreshTimer();
|
||||
});
|
||||
|
||||
// #endregion
|
@ -1,336 +0,0 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-color: #3b82f6;
|
||||
--primary-hover: #2563eb;
|
||||
--secondary-color: #1e293b;
|
||||
--background-color: #f1f5f9;
|
||||
--card-background: #ffffff;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--border-color: #e2e8f0;
|
||||
--success-color: #16a34a;
|
||||
--success-hover: #15803d;
|
||||
--header-bg: #1e293b;
|
||||
--header-text: #f8fafc;
|
||||
--error-color: #dc2626;
|
||||
--error-hover: #b91c1c;
|
||||
--tile-hover: #f8fafc;
|
||||
--shadow-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--header-bg);
|
||||
color: var(--header-text);
|
||||
padding: 1rem 0;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
h1 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
|
||||
.balance-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
.balance-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
.balance-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
min-width: 140px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.balance-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
|
||||
.balance-label {
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.balance-amount {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--header-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.actions-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
|
||||
.action-tile {
|
||||
background-color: var(--card-background);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease-in-out;
|
||||
border: none;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
aspect-ratio: 1 / 1;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
label {
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--card-background);
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pending-amount {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
|
||||
.amount-label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.amount-value {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
opacity: 0.9;
|
||||
margin-top: auto;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-hover);
|
||||
opacity: 1;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.history-section {
|
||||
background-color: var(--card-background);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease-in-out;
|
||||
border: none;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
height: auto;
|
||||
max-height: calc(100vw / 3);
|
||||
margin-top: 1.5rem;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
list-style: none;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding-right: 0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.history-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background-color: var(--card-background);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border: none;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.transaction-type {
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.transaction-details {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.amount {
|
||||
&.amount-positive {
|
||||
color: var(--success-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.amount-negative {
|
||||
color: var(--error-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tile-hover);
|
||||
box-shadow: 0 4px 6px var(--shadow-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px var(--shadow-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
# Forge Briefing Module
|
||||
|
||||
## Overview
|
||||
The Briefing module provides a comprehensive presentation and lecture system for the Forge client. It includes features for creating interactive slideshows, managing presentations, and delivering lectures with synchronized audio and visual elements.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Presentation System
|
||||
1. **Slide Show Management** (`fnc_initSlideShow.sqf`)
|
||||
- Initializes interactive slide shows
|
||||
- Configures display screens and controllers
|
||||
- Sets up image sequences and timing
|
||||
- Manages presentation flow and transitions
|
||||
|
||||
2. **Presentation Controls**
|
||||
- **Start Slide Show** (`fnc_startSlideShow.sqf`)
|
||||
- Initiates the presentation sequence
|
||||
- Synchronizes audio and visual elements
|
||||
- Manages presentation state
|
||||
- **Next Image** (`fnc_nextImage.sqf`)
|
||||
- Advances to the next slide
|
||||
- Updates display screens
|
||||
- Maintains presentation state
|
||||
- **Previous Image** (`fnc_prevImage.sqf`)
|
||||
- Returns to the previous slide
|
||||
- Updates display screens
|
||||
- Maintains presentation state
|
||||
- **End Slide Show** (`fnc_endSlideShow.sqf`)
|
||||
- Terminates the presentation
|
||||
- Cleans up resources
|
||||
- Resets presentation state
|
||||
|
||||
3. **Lecture System**
|
||||
- **Spawn Lecture** (`fnc_spawnLecture.sqf`)
|
||||
- Initiates AI-driven lectures
|
||||
- Manages speaker animations
|
||||
- Controls presentation timing
|
||||
- **Spawn Presentation** (`fnc_spawnPresentation.sqf`)
|
||||
- Creates automated presentations
|
||||
- Manages slide timing
|
||||
- Controls visual transitions
|
||||
|
||||
### Presentation Features
|
||||
1. **Interactive Controls**
|
||||
- Manual slide navigation
|
||||
- Auto-scroll capability
|
||||
- Presentation state management
|
||||
- Speaker control integration
|
||||
|
||||
2. **Visual Elements**
|
||||
- Multiple display screen support
|
||||
- Synchronized image transitions
|
||||
- Customizable timing
|
||||
- Power-of-2 image optimization
|
||||
|
||||
3. **Audio Integration**
|
||||
- Speaker synchronization
|
||||
- Topic-based conversations
|
||||
- Sentence-specific playback
|
||||
- Multi-speaker support
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the briefing module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Set up presentation screens and controllers
|
||||
3. Configure slide shows or lectures:
|
||||
```sqf
|
||||
// Initialize a slide show
|
||||
[Screen01, [Controller01], ["images/folder/image.paa"]] call forge_client_briefing_fnc_initSlideShow;
|
||||
|
||||
// Start a lecture
|
||||
[player, "topic", "sentence"] call forge_client_briefing_fnc_spawnLecture;
|
||||
|
||||
// Create a presentation
|
||||
[screen, [["texture.paa", 5]]] call forge_client_briefing_fnc_spawnPresentation;
|
||||
```
|
||||
4. Control the presentation using the provided functions
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Support for multiple display screens
|
||||
- Synchronized audio-visual presentations
|
||||
- Interactive control system
|
||||
- AI-driven lecture capabilities
|
||||
- Power-of-2 image optimization
|
||||
- State management for presentations
|
||||
- Multi-speaker support
|
||||
- Customizable timing controls
|
||||
- Resource cleanup on completion
|
||||
- Error handling and validation
|
@ -1,20 +1,15 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Ends the slide show
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Controller <OBJECT> - object that controls the slide show
|
||||
*
|
||||
* Return Value:
|
||||
* Boolean - true if the slide show was ended; false if not
|
||||
*
|
||||
* Example:
|
||||
* [controller] call ace_briefing_fnc_endSlideShow
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Author: PDT for J. Schmidt
|
||||
ends the slide show
|
||||
|
||||
Arguments:
|
||||
0: _controller <OBJECT> - object that controls the slide show
|
||||
|
||||
Return Value:
|
||||
<BOOL> - true if the slide show was ended; false if not
|
||||
*/
|
||||
|
||||
params [["_controller", objNull]];
|
||||
|
||||
|
@ -1,26 +1,24 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Sets up a slide show
|
||||
*
|
||||
* Arguments:
|
||||
* 0: _screen <ARRAY> - array of objects to display images on
|
||||
* 1: _controllers <ARRAY> - array of objects to use are controlers
|
||||
* 2: _images <ARRAY> - array of image paths; images should be powers of 2
|
||||
* 3: _autoScroll <BOOL> - should the image auto-scroll
|
||||
* 4: _units <ARRAY> - unit(s) that should speak
|
||||
* 5: _topic <STRING> - conversation topic
|
||||
* 6: _sentence <STRING> - conversation sentence, "" = play all
|
||||
*
|
||||
* Return Value:
|
||||
* <BOOL> - true if slide show was setup; false if not
|
||||
*
|
||||
* Example:
|
||||
* [Screen01, [Controler01], ["images/folder/image.paa"]] call forge_client_briefing_fnc_initSlideShow;
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Author: PDT for J. Schmidt
|
||||
sets up a slide show
|
||||
|
||||
Arguments:
|
||||
0: _screen <ARRAY> - array of objects to display images on
|
||||
1: _controllers <ARRAY> - array of objects to use are controlers
|
||||
2: _images <ARRAY> - array of image paths; images should be powers of 2
|
||||
3: _autoScroll <BOOL> - should the image auto-scroll
|
||||
4: _units <ARRAY> - unit(s) that should speak
|
||||
5: _topic <STRING> - conversation topic
|
||||
6: _sentence <STRING> - conversation sentence, "" = play all
|
||||
|
||||
Return Value:
|
||||
<BOOL> - true if slide show was setup; false if not
|
||||
|
||||
Example:
|
||||
[Screen01, [Controler01], ["images/folder/image.paa"]] call forge_client_briefing_fnc_initSlideShow;
|
||||
*/
|
||||
|
||||
params [["_screens", []], ["_controllers", []], ["_images", []], ["_autoScroll", false], ["_units", []], ["_topic", ""], ["_sentence", ""]];
|
||||
|
||||
|
@ -1,20 +1,15 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Displays the next image
|
||||
*
|
||||
* Arguments:
|
||||
* 0: _controller <OBJECT> - object that controls the slide show
|
||||
*
|
||||
* Return Value:
|
||||
* 0: _return <BOOL> - true if the next image was displayed; false if not
|
||||
*
|
||||
* Example:
|
||||
* [controller] call ace_briefing_fnc_nextImage
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Author: PDT for J. Schmidt
|
||||
displays the next image
|
||||
|
||||
Arguments:
|
||||
0: _controller <OBJECT> - object that controls the slide show
|
||||
|
||||
Return Value:
|
||||
<BOOL> - true if the next image was displayed; false if not
|
||||
*/
|
||||
|
||||
params [["_controller", objNull]];
|
||||
|
||||
|
@ -1,20 +1,15 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Displays the previous image
|
||||
*
|
||||
* Arguments:
|
||||
* 0: _controller <OBJECT> - object that controls the slide show
|
||||
*
|
||||
* Return Value:
|
||||
* 0: _return <BOOL> - true if the previous image was displayed; false if not
|
||||
*
|
||||
* Example:
|
||||
* [controller] call forge_briefing_fnc_prevImage
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Author: PDT for J. Schmidt
|
||||
displays the previous image
|
||||
|
||||
Arguments:
|
||||
0: _controller <OBJECT> - object that controls the slide show
|
||||
|
||||
Return Value:
|
||||
<BOOL> - true if the previous image was displayed; false if not
|
||||
*/
|
||||
|
||||
params [["_controller", objNull]];
|
||||
|
||||
|
@ -1,25 +1,21 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* AI plays a given sentence/conversation
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Unit that is playing the given sentence/conversation <OBJECT>
|
||||
* 1: Topic that is being talked about <STRING>
|
||||
* 2: Sentence partaining to the topic at hand <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [player, "topic", "sentence"] call forge_client_briefing_fnc_spawnLecture
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Name: forge_client_briefing_fnc_spawnLecture
|
||||
Date: 8/6/2022
|
||||
Version: 1.0
|
||||
Author: J. Schmidt
|
||||
|
||||
Description:
|
||||
AI plays a given sentence/conversation.
|
||||
|
||||
Parameter(s):
|
||||
0: Unit that is playing the given sentence/conversation. <OBJECT>
|
||||
1: Topic that is being talked about. <STRING>
|
||||
2: Sentence partaining to the topic at hand. <STRING>
|
||||
*/
|
||||
|
||||
params ["_unit", "_topic", "_sentence"];
|
||||
|
||||
FORGE_Briefing_inProgress = true;
|
||||
publicVariable "FORGE_Briefing_inProgress";
|
||||
|
||||
|
@ -1,23 +1,20 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Object displays a slideshow presentation
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Object that will display the presentation <OBJECT>
|
||||
* 1: Slides that will be used for the presentation <ARRAY>
|
||||
* - 0: Texture used for the Slide <STRING>
|
||||
* - 1: Amount of Time the Slide will display <NUMBER>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [screen, [["texture.paa", 5]]] call forge_client_briefing_fnc_spawnLecture
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Name: forge_client_briefing_fnc_spawnLecture
|
||||
Date: 8/6/2022
|
||||
Version: 1.0
|
||||
Author: J. Schmidt
|
||||
|
||||
Description:
|
||||
Object displays a slideshow presentation.
|
||||
|
||||
Parameter(s):
|
||||
0: Object that will display the presentation. <OBJECT>
|
||||
1: Slides that will be used for the presentation. <ARRAY>
|
||||
1-0: Texture used for the Slide. <STRING>
|
||||
1-1: Amount of Time the Slide will display. <NUMBER>
|
||||
*/
|
||||
|
||||
params ["_object", "_slides"];
|
||||
|
||||
|
@ -1,20 +1,15 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Starts the slideshow
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Controller <OBJECT> - object that controls the slide show
|
||||
*
|
||||
* Return Value:
|
||||
* Boolean - true if slide show was started
|
||||
*
|
||||
* Example:
|
||||
* [controller] call ace_fnc_startSlideShow
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Author: PDT for J. Schmidt
|
||||
starts the slideshow
|
||||
|
||||
Arguments:
|
||||
0: _controller <OBJECT> - object that controls the slide show
|
||||
|
||||
Return Value:
|
||||
<BOOL> - true if slide show was started
|
||||
*/
|
||||
|
||||
params [["_controller", objNull], ["_units", []], ["_topic", ""], ["_sentence", ""]];
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
z\forge_client\addons\common
|
@ -1,19 +0,0 @@
|
||||
class Extended_PreStart_EventHandlers {
|
||||
class ADDON {
|
||||
init = QUOTE(call COMPILE_FILE(XEH_preStart));
|
||||
};
|
||||
};
|
||||
|
||||
class Extended_PreInit_EventHandlers {
|
||||
class ADDON {
|
||||
init = QUOTE(call COMPILE_FILE(XEH_preInit));
|
||||
serverInit = QUOTE(call COMPILE_FILE(XEH_preInit_server));
|
||||
};
|
||||
};
|
||||
|
||||
class Extended_PostInit_EventHandlers {
|
||||
class ADDON {
|
||||
init = QUOTE(call COMPILE_FILE(XEH_postInit));
|
||||
clientInit = QUOTE(call COMPILE_FILE(XEH_postInit_client));
|
||||
};
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
PREP(actorStateGet);
|
||||
PREP(actorStateSet);
|
||||
PREP(getPlayer);
|
@ -1 +0,0 @@
|
||||
#include "script_component.hpp"
|
@ -1 +0,0 @@
|
||||
#include "script_component.hpp"
|
@ -1,8 +0,0 @@
|
||||
#include "script_component.hpp"
|
||||
ADDON = false;
|
||||
|
||||
PREP_RECOMPILE_START;
|
||||
#include "XEH_PREP.hpp"
|
||||
PREP_RECOMPILE_END;
|
||||
|
||||
ADDON = true;
|
@ -1 +0,0 @@
|
||||
#include "script_component.hpp"
|
@ -1,2 +0,0 @@
|
||||
#include "script_component.hpp"
|
||||
#include "XEH_PREP.hpp"
|
@ -1,16 +0,0 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
class CfgPatches {
|
||||
class ADDON {
|
||||
name = COMPONENT_NAME;
|
||||
units[] = {};
|
||||
weapons[] = {};
|
||||
requiredVersion = REQUIRED_VERSION;
|
||||
requiredAddons[] = {"forge_client_main"};
|
||||
authors[] = {"J. Schmidt", "Creedcoder"};
|
||||
author = "IDSolutions";
|
||||
VERSION_CONFIG;
|
||||
};
|
||||
};
|
||||
|
||||
#include "CfgEventHandlers.hpp"
|
@ -1,43 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Gets the current state of a player
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Player <OBJECT>
|
||||
*
|
||||
* Return Value:
|
||||
* Player State <HASHMAP>
|
||||
*
|
||||
* Example:
|
||||
* _state = [player] call forge_client_common_fnc_actorStateGet
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params [["_player", objNull, [objNull]]];
|
||||
|
||||
private _loadout = getUnitLoadout _player;
|
||||
private _pos = getPosASL _player;
|
||||
private _dir = getDir _player;
|
||||
private _stance = stance _player;
|
||||
private _phone = GETVAR(_player,FORGE_Phone,QUOTE(000-000-0000));
|
||||
private _email = GETVAR(_player,FORGE_Email,QUOTE(player@example.com));
|
||||
private _bank = GETVAR(_player,FORGE_Bank,0);
|
||||
private _cash = GETVAR(_player,FORGE_Cash,0);
|
||||
private _state = lifeState _player;
|
||||
|
||||
private _hash = createHashMap;
|
||||
|
||||
_hash set ["loadout", _loadout];
|
||||
_hash set ["position", _pos];
|
||||
_hash set ["direction", _dir];
|
||||
_hash set ["stance", _stance];
|
||||
_hash set ["phone", _phone];
|
||||
_hash set ["email", _email];
|
||||
_hash set ["bank", _bank];
|
||||
_hash set ["cash", _cash];
|
||||
_hash set ["state", _state];
|
||||
|
||||
_hash
|
@ -1,65 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Sets the state of a player
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Player <OBJECT>
|
||||
* 1: Data <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* Unit State <HASHMAP>
|
||||
*
|
||||
* Example:
|
||||
* [player, _data] call forge_client_common_fnc_actorStateSet
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params [["_player", objNull, [objNull]], ["_data", "", [""]]];
|
||||
private _hash = createHashMap;
|
||||
|
||||
if (isNull _player || _data isEqualTo "") exitWith {
|
||||
diag_log text format ["[FORGE:Player:Actor] Invalid player or data: %1", _player];
|
||||
};
|
||||
|
||||
(parseSimpleArray _data) params ["_uid", "_loadout", "_pos", "_dir", "_stance", "_email", "_phone", "_bank", "_cash", "_state"];
|
||||
|
||||
_player setUnitLoadout _loadout;
|
||||
|
||||
if !(isNil "_pos") then {
|
||||
_player setPosASL _pos;
|
||||
|
||||
private _pAlt = ((getPosATLVisual player) select 2);
|
||||
private _pVelZ = ((velocity player) select 2);
|
||||
if (_pAlt > 5 && _pVelZ < 0) then {
|
||||
player setVelocity [0, 0, 0];
|
||||
player setPosATL [((getPosATLVisual player) select 0), ((getPosATLVisual player) select 1), 1];
|
||||
hint "You logged off mid air. You were moved to a safe position on the ground.";
|
||||
};
|
||||
};
|
||||
|
||||
if !(isNil "_dir") then {
|
||||
_player setDir _dir;
|
||||
};
|
||||
|
||||
_player playAction _stance;
|
||||
SETPVAR(_player,FORGE_Phone,_phone);
|
||||
SETPVAR(_player,FORGE_Email,_email);
|
||||
SETPVAR(_player,FORGE_Bank,_bank);
|
||||
SETPVAR(_player,FORGE_Cash,_cash);
|
||||
SETPVAR(_player,FORGE_State,_state);
|
||||
|
||||
_hash set ["uid", _uid];
|
||||
_hash set ["loadout", _loadout];
|
||||
_hash set ["position", _pos];
|
||||
_hash set ["direction", _dir];
|
||||
_hash set ["stance", _stance];
|
||||
_hash set ["phone", _phone];
|
||||
_hash set ["email", _email];
|
||||
_hash set ["bank", _bank];
|
||||
_hash set ["cash", _cash];
|
||||
_hash set ["state", _state];
|
||||
|
||||
_hash
|
@ -1,27 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Gets a player object by UID.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Player UID <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* Player object or objNull if not found <OBJECT>
|
||||
*
|
||||
* Example:
|
||||
* ["76561198012345678"] call forge_client_common_fnc_getPlayer
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params ["_uid"];
|
||||
|
||||
private _player = objNull;
|
||||
|
||||
{
|
||||
if ((getPlayerUID _x) isEqualTo _uid) exitWith { _player = _x; };
|
||||
} forEach allPlayers;
|
||||
|
||||
_player
|
@ -1,16 +0,0 @@
|
||||
#define COMPONENT common
|
||||
#define COMPONENT_BEAUTIFIED Common
|
||||
#include "\z\forge_client\addons\main\script_mod.hpp"
|
||||
|
||||
// #define DEBUG_MODE_FULL
|
||||
// #define DISABLE_COMPILE_CACHE
|
||||
|
||||
#ifdef DEBUG_ENABLED_COMMON
|
||||
#define DEBUG_MODE_FULL
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SETTINGS_COMMON
|
||||
#define DEBUG_SETTINGS DEBUG_SETTINGS_COMMON
|
||||
#endif
|
||||
|
||||
#include "\z\forge_client\addons\main\script_macros.hpp"
|
BIN
addons/db.7z
BIN
addons/db.7z
Binary file not shown.
@ -1,125 +0,0 @@
|
||||
# Forge Database Module
|
||||
|
||||
## Overview
|
||||
The Database module provides a comprehensive data persistence system for the Forge client. It includes features for managing player data, organization information, and game state persistence using the ArmaDragonflyClient database system.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
- ArmaDragonflyClient
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Database System
|
||||
1. **Server Communication** (`fnc_requestServerDB.sqf`)
|
||||
- Handles client-server database communication
|
||||
- Manages asynchronous database operations
|
||||
- Provides callback support for data retrieval
|
||||
- Ensures data consistency across clients
|
||||
|
||||
2. **Data Management**
|
||||
- **Player Data**
|
||||
- Armory unlocks
|
||||
- Garage unlocks
|
||||
- Locker contents
|
||||
- Vehicle inventory
|
||||
- Financial information
|
||||
- Contact details
|
||||
- Organization membership
|
||||
- Player statistics
|
||||
- Equipment loadouts
|
||||
- Position and state
|
||||
|
||||
- **Organization Data**
|
||||
- Member information
|
||||
- Asset tracking
|
||||
- Financial records
|
||||
- Reputation system
|
||||
- Activity logs
|
||||
- Creation timestamps
|
||||
- Modification history
|
||||
|
||||
3. **Persistence Features**
|
||||
- Automatic data saving
|
||||
- Periodic state synchronization
|
||||
- Data validation and sanitization
|
||||
- Error recovery mechanisms
|
||||
- Transaction logging
|
||||
- Data versioning
|
||||
|
||||
### Data Operations
|
||||
1. **Storage Operations**
|
||||
- Hash-based storage (HSET)
|
||||
- Bulk data operations
|
||||
- Atomic transactions
|
||||
- Data serialization
|
||||
- String normalization
|
||||
|
||||
2. **Retrieval Operations**
|
||||
- Hash-based retrieval (HGET)
|
||||
- Bulk data loading
|
||||
- Data deserialization
|
||||
- Error handling
|
||||
- Default value management
|
||||
|
||||
3. **Synchronization**
|
||||
- Client-server sync
|
||||
- Real-time updates
|
||||
- Conflict resolution
|
||||
- State verification
|
||||
- Data integrity checks
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the database module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Initialize database connections
|
||||
3. Perform database operations:
|
||||
```sqf
|
||||
// Request data from server
|
||||
["hgetall", "Hello World!", {
|
||||
systemChat format ["Message: %1", _this];
|
||||
}] call forge_db_fnc_requestServerDB;
|
||||
|
||||
// Save player data
|
||||
[] call forge_client_init_fnc_playerDBSave;
|
||||
|
||||
// Load player data
|
||||
[] call forge_client_init_fnc_playerDBLoad;
|
||||
```
|
||||
4. Handle database callbacks and responses
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Asynchronous database operations
|
||||
- Callback-based response handling
|
||||
- Data serialization and normalization
|
||||
- Error handling and recovery
|
||||
- Transaction logging
|
||||
- State management
|
||||
- Data validation
|
||||
- Conflict resolution
|
||||
- Real-time synchronization
|
||||
- Bulk operation support
|
||||
- Atomic transactions
|
||||
- Data versioning
|
||||
- Integrity verification
|
@ -1 +1,3 @@
|
||||
PREP(requestServerDB);
|
||||
PREP(saveToMission);
|
||||
PREP(saveToProfile);
|
||||
PREP(saveToTempDB);
|
@ -1 +1,3 @@
|
||||
#include "script_component.hpp"
|
||||
#include "script_component.hpp"
|
||||
|
||||
GVAR(tempDB) = [];
|
@ -1,3 +1 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
["forge_db_registerClient", [getPlayerUID player, clientOwner]] call CBA_fnc_serverEvent;
|
||||
#include "script_component.hpp"
|
@ -5,18 +5,4 @@ PREP_RECOMPILE_START;
|
||||
#include "XEH_PREP.hpp"
|
||||
PREP_RECOMPILE_END;
|
||||
|
||||
// Handle responses from server requests
|
||||
["forge_db_response", {
|
||||
params ["_targetUID", "_requestType", "_result", "_requestID"];
|
||||
|
||||
if (_targetUID isEqualTo getPlayerUID player) then {
|
||||
if (!isNil QGVAR(pendingCallbacks)) then {
|
||||
private _callback = GVAR(pendingCallbacks) getOrDefault [_requestID, {}];
|
||||
GVAR(pendingCallbacks) deleteAt _requestID;
|
||||
|
||||
[_result, _requestType] call _callback;
|
||||
};
|
||||
};
|
||||
}] call CBA_fnc_addEventHandler;
|
||||
|
||||
ADDON = true;
|
Binary file not shown.
@ -1,37 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Sends database requests to the server using CBA events
|
||||
*
|
||||
* Arguments:
|
||||
* 0: _type - Type of database operation <STRING>
|
||||
* 1: _data - Data for the operation <ANY>
|
||||
* 2: _callback - Function to call when data is returned <CODE>
|
||||
*
|
||||
* Return Value:
|
||||
* Request ID <STRING>
|
||||
*
|
||||
* Example:
|
||||
* ["hgetall", "Hello World!", { systemChat format ["Message: %1", _this]; }] call forge_db_fnc_requestServerDB;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params [
|
||||
["_type", "", [""]],
|
||||
["_data", nil, [createHashMap, [], "", 0, true, objNull]],
|
||||
["_callback", {}, [{}]]
|
||||
];
|
||||
|
||||
private _requestID = format ["%1_%2", diag_tickTime, random 1000];
|
||||
|
||||
if (isNil QGVAR(pendingCallbacks)) then {
|
||||
GVAR(pendingCallbacks) = createHashMap;
|
||||
};
|
||||
|
||||
GVAR(pendingCallbacks) set [_requestID, _callback];
|
||||
|
||||
["forge_db_request", [getPlayerUID player, _type, _data, _requestID]] call CBA_fnc_serverEvent;
|
||||
|
||||
_requestID
|
111
addons/db/functions/fnc_saveToMission.sqf
Normal file
111
addons/db/functions/fnc_saveToMission.sqf
Normal file
@ -0,0 +1,111 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Function: forge_client_db_fnc_saveToMission
|
||||
* Author: J.Schmidt
|
||||
* Edit: 07.23.2024
|
||||
* Copyright © 2024 J.Schmidt, All rights reserved
|
||||
*
|
||||
* Do not edit without permission!
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivative 4.0 International License.
|
||||
* To view a copy of this license, vist https://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to Creative Commons,
|
||||
* PO Box 1866, Mountain View, CA 94042
|
||||
*
|
||||
* [Description]
|
||||
* Store mission and player data in Mission Namespace.
|
||||
*
|
||||
* Arguments:
|
||||
* N/A
|
||||
*
|
||||
* Return Value:
|
||||
* N/A
|
||||
*
|
||||
* Examples:
|
||||
* [] call forge_client_db_fnc_saveToMission;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
if (isNil "companyFunds") then { companyFunds = 0 };
|
||||
if (isNil "companyRating") then { companyRating = 0 };
|
||||
if (isNil "companyGenerals") then { companyGenerals = [] };
|
||||
if (isNil "companyGarageUnlocks") then { companyGarageUnlocks = [] };
|
||||
if (isNil EGVAR(arsenal,armory_unlocks)) then { EGVAR(arsenal,armory_unlocks) = [[],[],[],[]] };
|
||||
if (isNil EGVAR(arsenal,garage_unlocks)) then { EGVAR(arsenal,garage_unlocks) = [[],[],[],[],[],[]] };
|
||||
|
||||
private _vdata = [];
|
||||
private _pdata = [];
|
||||
private _cdata = [
|
||||
["key", "CompanyState"],
|
||||
["funds", [companyFunds]],
|
||||
["rating", [companyRating]],
|
||||
["operations", [companyGenerals]],
|
||||
["garage_unlocks", [companyGarageUnlocks]]
|
||||
];
|
||||
|
||||
private _vehicles = nearestObjects [player, ["LandVehicle"], 50] apply {
|
||||
createHashMapFromArray [
|
||||
["vehicle", _x],
|
||||
["class", [typeOf _x]],
|
||||
["position", [getPosATL _x]],
|
||||
["direction", [getDir _x]],
|
||||
["health", [damage _x]]
|
||||
];
|
||||
};
|
||||
|
||||
{
|
||||
if (alive _x) then {
|
||||
_vdata pushBackUnique _x;
|
||||
};
|
||||
} forEach _vehicles;
|
||||
|
||||
{
|
||||
private _data = [
|
||||
["key", getPlayerUID player],
|
||||
// ["armory_unlocks", [forge_client_armory_arsenalUnlocks]],
|
||||
// ["garage_unlocks", [forge_client_armory_garageUnlocks]],
|
||||
["armory_unlocks", [EGVAR(arsenal,armory_unlocks)]],
|
||||
["garage_unlocks", [EGVAR(arsenal,garage_unlocks)]],
|
||||
// ["locker", [player getVariable ["Locker", []]]],
|
||||
// ["garage", [player getVariable ["Garage", []]]],
|
||||
["locker", [GETVAR(player,FORGE_Locker,[])]],
|
||||
["garage", [GETVAR(player,FORGE_Garage,[])]],
|
||||
// ["cash", [player getVariable ["FORGE_Cash", 0]]],
|
||||
// ["bank", [player getVariable ["FORGE_Bank", 0]]],
|
||||
["cash", [GETVAR(player,FORGE_Cash,0)]],
|
||||
["bank", [GETVAR(player,FORGE_Bank,0)]],
|
||||
// ["number", [player getVariable ["FORGE_Phone_Number", "unknown"]]],
|
||||
// ["email", [player getVariable ["FORGE_Email", "unknown@spearnet.mil"]]],
|
||||
["number", [GETVAR(player,FORGE_Phone_Number,"unknown")]],
|
||||
["email", [GETVAR(player,FORGE_Email,"unknown@spearnet.mil")]],
|
||||
// ["paygrade", [player getVariable ["Paygrade", "E1"]]],
|
||||
["paygrade", [GETVAR(player,FORGE_Paygrade,"E1")]],
|
||||
["reputation", [rating player]],
|
||||
["loadout", [getUnitLoadout player]],
|
||||
// ["holster", [player getVariable ["FORGE_Holster_Weapon", true]]],
|
||||
["holster", [GETVAR(player,FORGE_Holster_Weapon,true)]],
|
||||
["position", [getPosASLVisual player]],
|
||||
["direction", [getDirVisual player]]
|
||||
];
|
||||
|
||||
if (isNull objectParent player) then {
|
||||
_data pushBack "currentWeapon";
|
||||
_data pushBack [currentMuzzle player];
|
||||
_data pushBack "stance";
|
||||
_data pushBack [stance player];
|
||||
};
|
||||
|
||||
_pdata pushBackUnique _data;
|
||||
} forEach playableUnits;
|
||||
|
||||
private _cHashMap = createHashMapFromArray _cdata;
|
||||
private _pHashMap = createHashMapFromArray _pdata;
|
||||
private _vHashMap = createHashMapFromArray _vdata;
|
||||
|
||||
// missionProfileNamespace setVariable ["FORGE_MissionData", _cHashMap];
|
||||
// missionProfileNamespace setVariable ["FORGE_PlayerData", _pHashMap];
|
||||
// missionProfileNamespace setVariable ["FORGE_VehicleData", _vHashMap];
|
||||
SETPVAR(missionNamespace,FORGE_MissionData,_cHashMap);
|
||||
SETPVAR(missionNamespace,FORGE_PlayerData,_pHashMap);
|
||||
SETPVAR(missionNamespace,FORGE_VehicleData,_vHashMap);
|
69
addons/db/functions/fnc_saveToProfile.sqf
Normal file
69
addons/db/functions/fnc_saveToProfile.sqf
Normal file
@ -0,0 +1,69 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Function: forge_client_db_fnc_saveToProfile
|
||||
* Author: J.Schmidt
|
||||
* Edit: 07.23.2024
|
||||
* Copyright © 2024 J.Schmidt, All rights reserved
|
||||
*
|
||||
* Do not edit without permission!
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivative 4.0 International License.
|
||||
* To view a copy of this license, vist https://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to Creative Commons,
|
||||
* PO Box 1866, Mountain View, CA 94042
|
||||
*
|
||||
* [Description]
|
||||
* Store player data in Profile Namespace.
|
||||
*
|
||||
* Arguments:
|
||||
* N/A
|
||||
*
|
||||
* Return Value:
|
||||
* N/A
|
||||
*
|
||||
* Examples:
|
||||
* [] call forge_client_db_fnc_saveToProfile;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
// if (isNil "forge_client_armory_arsenalUnlocks") then { forge_client_armory_arsenalUnlocks = [] };
|
||||
// if (isNil "forge_client_armory_garageUnlocks") then { forge_client_armory_garageUnlocks = [] };
|
||||
if (isNil EGVAR(arsenal,armory_unlocks)) then { EGVAR(arsenal,armory_unlocks) = [[],[],[],[]] };
|
||||
if (isNil EGVAR(arsenal,garage_unlocks)) then { EGVAR(arsenal,garage_unlocks) = [[],[],[],[],[],[]] };
|
||||
|
||||
private _data = [
|
||||
["key", getPlayerUID player],
|
||||
["armory_unlocks", [EGVAR(arsenal,armory_unlocks)]],
|
||||
["garage_unlocks", [EGVAR(arsenal,garage_unlocks)]],
|
||||
// ["locker", [player getVariable ["Locker", []]]],
|
||||
// ["garage", [player getVariable ["Garage", []]]],
|
||||
["locker", [GETVAR(player,FORGE_Locker,[])]],
|
||||
["garage", [GETVAR(player,FORGE_Garage,[])]],
|
||||
// ["cash", [player getVariable ["FORGE_Cash", 0]]],
|
||||
// ["bank", [player getVariable ["FORGE_Bank", 0]]],
|
||||
["cash", [GETVAR(player,FORGE_Cash,0)]],
|
||||
["bank", [GETVAR(player,FORGE_Bank,0)]],
|
||||
// ["number", [player getVariable ["FORGE_Phone_Number", "unknown"]]],
|
||||
// ["email", [player getVariable ["FORGE_Email", "unknown@spearnet.mil"]]],
|
||||
["number", [GETVAR(player,FORGE_PhoneNumber,"unknown")]],
|
||||
["email", [GETVAR(player,FORGE_Email,"unknown@spearnet.mil")]],
|
||||
// ["paygrade", [player getVariable ["Paygrade", "E1"]]],
|
||||
["paygrade", [GETVAR(player,FORGE_Paygrade,"E1")]],
|
||||
["reputation", [rating player]],
|
||||
["loadout", [getUnitLoadout player]],
|
||||
// ["holster", [player getVariable ["FORGE_Holster_Weapon", true]]],
|
||||
["holster", [GETVAR(player,FORGE_Holster_Weapon,true)]],
|
||||
["position", [getPosASLVisual player]],
|
||||
["direction", [getDirVisual player]]
|
||||
];
|
||||
|
||||
if (isNull objectParent player) then {
|
||||
_data pushBack ["currentWeapon", [currentMuzzle player]];
|
||||
_data pushBack ["stance", [stance player]];
|
||||
};
|
||||
|
||||
private _hashMap = createHashMapFromArray _data;
|
||||
|
||||
// profileNamespace setVariable ["FORGE_PlayerData", _hashMap];
|
||||
SETVAR(profileNamespace,FORGE_PlayerData,_hashMap);
|
51
addons/db/functions/fnc_saveToTempDB.sqf
Normal file
51
addons/db/functions/fnc_saveToTempDB.sqf
Normal file
@ -0,0 +1,51 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Function: forge_client_db_fnc_saveToTempDB
|
||||
* Author: J.Schmidt
|
||||
* Edit: 07.23.2024
|
||||
* Copyright © 2024 J.Schmidt, All rights reserved
|
||||
*
|
||||
* Do not edit without permission!
|
||||
*
|
||||
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivative 4.0 International License.
|
||||
* To view a copy of this license, vist https://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to Creative Commons,
|
||||
* PO Box 1866, Mountain View, CA 94042
|
||||
*
|
||||
* [Description]
|
||||
* Store data in Temp DB.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Name of Event <STRING>
|
||||
* 1: Name of DB <STRING>
|
||||
* 2: UID of Player <STRING>
|
||||
* 3: Name of Key <STRING>
|
||||
* 4: Value to store in key <ARRAY|STRING|NUMBER|BOOL>
|
||||
*
|
||||
* Return Value:
|
||||
* N/A
|
||||
*
|
||||
* Examples:
|
||||
* ["playerStatus", "players", "76561198027566824", "status", "available"] call forge_client_db_fnc_saveToTempDB;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params [["_event", "", [""]], ["_db", "", [""]], ["_uid", "", [""]], ["_key", "", [""]], ["_value", "", [[], 0, "", false]]];
|
||||
|
||||
if (isNil "_event") exitWith { ["No Event provided"] call BFUNC(log); };
|
||||
if (isNil "_db") exitWith { ["No DB provided"] call BFUNC(log); };
|
||||
if (isNil "_uid") exitWith { ["No UID provided"] call BFUNC(log); };
|
||||
if (isNil "_key") exitWith { ["No key provided"] call BFUNC(log); };
|
||||
if (isNil "_value") exitWith { ["No Value provided"] call BFUNC(log); };
|
||||
|
||||
["Received event: %1", _this] call BFUNC(logFormat);
|
||||
|
||||
private _response = [GVAR(tempDB), [_db, _uid, _key], _value] call BFUNC(dbValueSet);
|
||||
|
||||
if !(_response) then {
|
||||
["Failed to set Key '%1' for UID '%2' with '%3' to DB", _key, _uid, _value] call BFUNC(logFormat);
|
||||
} else {
|
||||
SETPVAR(missionNamespace,FORGE_TempDb,GVAR(tempDB));
|
||||
["Successfully set key '%1' for UID '%2' with '%3'", _key, _uid, _value] call BFUNC(logFormat);
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
# Forge Dialogue Module
|
||||
|
||||
## Overview
|
||||
The Dialogue module provides a comprehensive AI conversation system for the Forge client. It enables dynamic interactions between players and AI units, supporting topic-based conversations, sentence management, and AI behavior control.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### AI Dialogue System
|
||||
1. **AI Selection** (`fnc_selectAI.sqf`)
|
||||
- Manages AI unit selection for dialogue
|
||||
- Controls AI behavior during conversations
|
||||
- Handles unit state management
|
||||
- Disables AI actions during dialogue
|
||||
|
||||
2. **Dialogue Management** (`fnc_selectDialogue.sqf`)
|
||||
- Topic-based conversation system
|
||||
- Sentence management and playback
|
||||
- AI unit synchronization
|
||||
- Conversation flow control
|
||||
|
||||
3. **Conversation Features**
|
||||
- **Topic Management**
|
||||
- Dynamic topic selection
|
||||
- Context-aware conversations
|
||||
- Topic-based responses
|
||||
- Conversation branching
|
||||
|
||||
- **Sentence Control**
|
||||
- Sentence playback
|
||||
- Timing management
|
||||
- Response coordination
|
||||
- Conversation state tracking
|
||||
|
||||
- **AI Behavior**
|
||||
- Animation control
|
||||
- Movement management
|
||||
- State synchronization
|
||||
- Interaction handling
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the dialogue module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Select AI units for dialogue:
|
||||
```sqf
|
||||
// Select AI units for dialogue
|
||||
[units group player] call forge_client_dialogue_fnc_selectAI;
|
||||
|
||||
// Select specific dialogue for AI
|
||||
[units, [["topic", "sentence"]]] call forge_client_dialogue_fnc_selectDialogue;
|
||||
```
|
||||
3. Manage conversation flow and AI behavior
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Topic-based conversation system
|
||||
- AI behavior control
|
||||
- Sentence management
|
||||
- State synchronization
|
||||
- Animation handling
|
||||
- Movement control
|
||||
- Interaction management
|
||||
- Conversation flow control
|
||||
- Response coordination
|
||||
- Error handling
|
||||
- Debug support
|
||||
- Event system integration
|
@ -1,20 +1,17 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Selects AI for dialogue
|
||||
*
|
||||
* Arguments:
|
||||
* 0: _units - Array of AI units <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [units group player] call FUNC(selectAI);
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
Name: forge_client_dialogue_fnc_selectAI
|
||||
Date: 8/6/2022
|
||||
Version: 1.0
|
||||
Author: J. Schmidt
|
||||
|
||||
Description:
|
||||
Select AI for dialogue.
|
||||
|
||||
Arguments:
|
||||
0: OBJECT - The AI unit
|
||||
*/
|
||||
|
||||
params [["_units", [], [[]]]];
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Function: forge_client_dialogue_fnc_selectDialogue
|
||||
* Author: IDSolutions
|
||||
*
|
||||
* Description:
|
||||
* Selects dialogue for AI
|
||||
*
|
||||
* Arguments:
|
||||
* 0: _units - Array of AI units <ARRAY>
|
||||
* 1: _dialogue - Array of dialogue <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*/
|
||||
Name: forge_client_dialogue_fnc_selectDialogue
|
||||
Date: 8/6/2022
|
||||
Version: 1.0
|
||||
Author: J. Schmidt
|
||||
|
||||
Description:
|
||||
Create dialogue for AI unit.
|
||||
|
||||
Arguments:
|
||||
0: OBJECT - The AI unit
|
||||
1: STRING - The dialogue
|
||||
*/
|
||||
|
||||
params [["_units", [], [[]]], ["_dialogue", [], [[]]]];
|
||||
|
||||
|
@ -1,132 +1,4 @@
|
||||
# Forge Garage Module
|
||||
forge_garage
|
||||
==========
|
||||
|
||||
## Overview
|
||||
The Garage module provides a comprehensive vehicle management system for the Forge client. It includes features for storing, spawning, and managing vehicles in a garage environment, with support for various vehicle types and states.
|
||||
|
||||
## Dependencies
|
||||
- forge_client_main
|
||||
|
||||
## Authors
|
||||
- J. Schmidt
|
||||
- Creedcoder
|
||||
- IDSolutions
|
||||
|
||||
## Features
|
||||
|
||||
### Garage System
|
||||
1. **Garage Initialization** (`fnc_initGarage.sqf`)
|
||||
- Initializes the garage system
|
||||
- Sets up vehicle storage and management
|
||||
- Configures garage locations and spawn points
|
||||
- Manages vehicle state persistence
|
||||
|
||||
2. **Vehicle Management**
|
||||
- **Spawn Vehicle** (`fnc_spawnVehicle.sqf`)
|
||||
- Handles vehicle spawning from garage
|
||||
- Manages spawn locations and orientations
|
||||
- Preserves vehicle type information
|
||||
- Updates garage inventory
|
||||
|
||||
- **Store Vehicle** (`fnc_storeVehicle.sqf`)
|
||||
- Manages vehicle storage in garage
|
||||
- Handles vehicle cleanup
|
||||
- Updates garage inventory
|
||||
- Preserves vehicle data
|
||||
|
||||
- **Fetch Garage** (`fnc_fetchGarage.sqf`)
|
||||
- Retrieves garage vehicle information
|
||||
- Updates garage interface
|
||||
- Manages vehicle listings
|
||||
- Handles vehicle categorization
|
||||
|
||||
- **Fetch Nearby** (`fnc_fetchNearby.sqf`)
|
||||
- Locates and manages nearby vehicles
|
||||
- Validates vehicle ownership
|
||||
- Handles vehicle state checks
|
||||
- Updates interface with nearby vehicles
|
||||
|
||||
3. **Vehicle Categories**
|
||||
- Cars (Light vehicles, transport)
|
||||
- Armor (Tanks, APCs)
|
||||
- Helicopters (All rotary-wing aircraft)
|
||||
- Planes (Fixed-wing aircraft)
|
||||
- Naval (Boats, ships)
|
||||
- Static (Weapons, emplacements)
|
||||
|
||||
4. **Vehicle States**
|
||||
- Available
|
||||
- In Use
|
||||
- Maintenance
|
||||
- Damaged
|
||||
- Fuel Status
|
||||
- Condition Tracking
|
||||
|
||||
### User Interface
|
||||
1. **Garage Dialog**
|
||||
- Vehicle listing and management
|
||||
- Category filtering
|
||||
- Status indicators
|
||||
- Action buttons (Spawn/Store)
|
||||
- Vehicle details display
|
||||
|
||||
2. **Vehicle Information**
|
||||
- Vehicle name and type
|
||||
- Status indicators
|
||||
- Fuel level
|
||||
- Damage state
|
||||
- Maintenance status
|
||||
- Last used timestamp
|
||||
|
||||
3. **Controls**
|
||||
- Category filters
|
||||
- Vehicle actions
|
||||
- Status management
|
||||
- Quick access buttons
|
||||
|
||||
## Event Handlers
|
||||
The module uses several event handlers for initialization and execution:
|
||||
- `XEH_preInit.sqf`: Pre-initialization setup
|
||||
- `XEH_postInit.sqf`: Post-initialization tasks
|
||||
- `XEH_preStart.sqf`: Pre-start configuration
|
||||
- `XEH_postInit_client.sqf`: Client-specific post-initialization
|
||||
- `XEH_preInit_server.sqf`: Server-specific pre-initialization
|
||||
|
||||
## Usage
|
||||
To use the garage module:
|
||||
1. Ensure the module is properly loaded in your mission
|
||||
2. Access the garage through the provided UI
|
||||
3. Manage vehicles using the appropriate functions:
|
||||
```sqf
|
||||
// Open garage interface
|
||||
[] call forge_client_garage_fnc_openGarage;
|
||||
|
||||
// Store a vehicle
|
||||
[] call forge_client_garage_fnc_storeVehicle;
|
||||
|
||||
// Spawn a vehicle
|
||||
[] call forge_client_garage_fnc_spawnVehicle;
|
||||
```
|
||||
4. Monitor vehicle status and location
|
||||
|
||||
## Debugging
|
||||
Debug mode can be enabled by uncommenting the following in `script_component.hpp`:
|
||||
```cpp
|
||||
#define DEBUG_MODE_FULL
|
||||
```
|
||||
|
||||
## Version Information
|
||||
Version information is managed through the main Forge client system configuration.
|
||||
|
||||
## Technical Details
|
||||
- Vehicle state persistence
|
||||
- Category-based organization
|
||||
- Status tracking system
|
||||
- Location management
|
||||
- Spawn point configuration
|
||||
- Vehicle cleanup handling
|
||||
- Interface synchronization
|
||||
- Event system integration
|
||||
- Error handling
|
||||
- Debug support
|
||||
- Data validation
|
||||
- State management
|
||||
A player garage addon that provides personal storage functionality. Players can store and retrieve their vehicles securely using individual garages. This addon handles the storage and management of player vehicles in a safe and organized way.
|
@ -1,48 +1 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
[QGVAR(handleEvents), {
|
||||
params ["_control", "_isConfirmDialog", "_message"];
|
||||
|
||||
diag_log text format ["[FORGE::Client::Garage::XEH_postInit] Received event: '%1'", _message];
|
||||
|
||||
_message = fromJSON _message;
|
||||
private _event = _message get "event";
|
||||
private _data = _message get "data";
|
||||
|
||||
private _vehicles = GETVAR(player,FORGE_Garage,[]); //TODO: Implement garage from server
|
||||
|
||||
switch (_event) do {
|
||||
case "REQUEST::GARAGE::DATA": {
|
||||
private _garageData = createHashMap;
|
||||
private _vehicleList = [];
|
||||
|
||||
{
|
||||
private _vehicle = _x;
|
||||
if (isNull _vehicle || { !alive _vehicle }) exitWith {};
|
||||
|
||||
private _vehicleInfo = createHashMapFromArray [
|
||||
["classname", typeOf _vehicle],
|
||||
["damage", damage _vehicle],
|
||||
["fuel", fuel _vehicle],
|
||||
["hitpoints", getAllHitPointsDamage _vehicle]
|
||||
];
|
||||
|
||||
_vehicleList pushBack _vehicleInfo;
|
||||
} forEach _vehicles;
|
||||
|
||||
_garageData set ["vehicles", _vehicleList];
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["handleGarageDataRequest(%1)", (toJSON _vehicleList)]];
|
||||
};
|
||||
case "STORE::VEHICLE": {
|
||||
// Logic to store vehicle in garage
|
||||
// This would typically involve saving the vehicle's state to a database or similar
|
||||
};
|
||||
case "RETRIEVE::VEHICLE": {
|
||||
// Logic to retrieve vehicle from garage
|
||||
// This would typically involve loading the vehicle's state from a database or similar
|
||||
};
|
||||
default {
|
||||
diag_log text format ["[FORGE::Client::Garage::XEH_postInit] Unknown event: '%1'", _event];
|
||||
};
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
#include "script_component.hpp"
|
@ -1,23 +1,8 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Fetches the vehicles in the garage
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* None
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
private _display = findDisplay IDD_GARAGEDIALOG;
|
||||
private _garageList = _display displayCtrl IDC_GARAGEVEHICLELIST;
|
||||
// private _storedVehicles = player getVariable ["FORGE_Garage", []];
|
||||
private _storedVehicles = GETVAR(player,FORGE_Garage,[]);
|
||||
|
||||
lbClear _garageList;
|
||||
|
@ -1,21 +1,5 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Fetches the nearby vehicles
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* None
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
private _display = findDisplay IDD_GARAGEDIALOG;
|
||||
private _vehicleList = _display displayCtrl IDC_VEHICLELIST;
|
||||
|
||||
|
@ -1,21 +1,5 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Initializes the garages
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* None
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
{
|
||||
private _configName = configName(_x);
|
||||
private _className = (missionConfigFile >> "CfgGarages" >> "garages" >> _configName >> "className") call BFUNC(getCfgData);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user