Compare commits

...

7 Commits

Author SHA1 Message Date
github-actions
71438fe393 v1.0.0 Build 32 2025-06-15 16:58:29 +00:00
Jacob Schmidt
39d080fca5 Merge branch 'master' of https://gitea.innovativedevsolutions.org/IDSolutions/client
All checks were successful
Build / Build (push) Successful in 41s
2025-06-15 11:56:18 -05:00
Jacob Schmidt
b5bd3c6cb8 feat: Add core functionality for player and vehicle state management 2025-06-15 11:56:13 -05:00
github-actions
209b243620 v1.0.0 Build 31 2025-05-30 10:19:05 +00:00
Jacob Schmidt
73086cfe97 fix: Correct return value in createOrg function to true
All checks were successful
Build / Build (push) Successful in 1m12s
2025-05-30 05:18:10 -05:00
github-actions
a106fef2fd v1.0.0 Build 30 2025-05-26 05:47:43 +00:00
Jacob Schmidt
0858d9a173 feat: Implement basic mission management framework
All checks were successful
Build / Build (push) Successful in 28s
This commit introduces a basic framework for mission management.

- Includes `CfgMissions.hpp` in `config.cpp`.
- Adds `missionManager` to `XEH_PREP.hpp` for event handling preparation.
- Adds a placeholder call to `FUNC(missionManager)` in `XEH_postInit.sqf` with a TODO comment for future implementation.
2025-05-26 00:47:24 -05:00
22 changed files with 614 additions and 3 deletions

View File

@ -0,0 +1 @@
z\forge_client\addons\common

View File

@ -0,0 +1,19 @@
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));
};
};

View File

@ -0,0 +1,3 @@
PREP(actorStateGet);
PREP(actorStateSet);
PREP(getPlayer);

View File

@ -0,0 +1 @@
#include "script_component.hpp"

View File

@ -0,0 +1 @@
#include "script_component.hpp"

View File

@ -0,0 +1,8 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;

View File

@ -0,0 +1 @@
#include "script_component.hpp"

View File

@ -0,0 +1,2 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

16
addons/common/config.cpp Normal file
View File

@ -0,0 +1,16 @@
#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"

View File

@ -0,0 +1,43 @@
#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

View File

@ -0,0 +1,65 @@
#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

View File

@ -0,0 +1,27 @@
#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

View File

@ -0,0 +1,16 @@
#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"

View File

@ -1,4 +1,4 @@
#define MAJOR 1 #define MAJOR 1
#define MINOR 0 #define MINOR 0
#define PATCH 0 #define PATCH 0
#define BUILD 29 #define BUILD 32

View File

@ -460,7 +460,7 @@ private _orgStoreInterface = createHashMapObject [[
_self set ["currentOrganization", _orgData]; _self set ["currentOrganization", _orgData];
_self call ["saveToDatabase", []]; _self call ["saveToDatabase", []];
_assetId // Return the generated ID true
}], }],
["getAssets", { ["getAssets", {
/** /**

191
addons/task/CfgMissions.hpp Normal file
View File

@ -0,0 +1,191 @@
// TODO: Move to mission template and provide documentation
class CfgMissions {
// Global settings
maxConcurrentMissions = 3;
missionInterval = 300; // 5 minutes between mission generation
// Mission type weights
class MissionWeights {
attack = 0.2;
defend = 0.2;
hostage = 0.2;
hvt = 0.15;
defuse = 0.15;
delivery = 0.1;
};
// Mission locations
class Locations {
class CityOne {
position[] = {1000, 1000, 0};
type = "city";
radius = 300;
suitable[] = {"attack", "defend", "hostage"};
};
class MilitaryBase {
position[] = {2000, 2000, 0};
type = "military";
radius = 500;
suitable[] = {"hvt", "defend", "attack"};
};
class Industrial {
position[] = {3000, 3000, 0};
type = "industrial";
radius = 200;
suitable[] = {"delivery", "defuse"};
};
};
// AI Groups configuration
class AIGroups {
class Infantry {
side = "EAST";
class Units {
class Unit0 {
vehicle = "O_Soldier_TL_F";
rank = "SERGEANT";
position[] = {0, 0, 0};
};
class Unit1 {
vehicle = "O_Soldier_AR_F";
rank = "CORPORAL";
position[] = {5, -5, 0};
};
class Unit2 {
vehicle = "O_Soldier_LAT_F";
rank = "PRIVATE";
position[] = {-5, -5, 0};
};
};
suitable[] = {"attack", "defend", "hostage"};
};
class SpecOps {
side = "EAST";
class Units {
class Unit0 {
vehicle = "O_recon_TL_F";
rank = "SERGEANT";
position[] = {0, 0, 0};
};
class Unit1 {
vehicle = "O_recon_M_F";
rank = "CORPORAL";
position[] = {5, -5, 0};
};
};
suitable[] = {"hvt", "hostage"};
};
};
// TODO: Continue to refine mission types and their specific settings
// Mission type specific settings
class MissionTypes {
class Attack {
minUnits = 4;
maxUnits = 8;
class Rewards {
money[] = {500000, 1500000};
reputation[] = {100, 500};
equipment[] = {{"ItemGPS", 0.5}, {"ItemCompass", 0.3}};
supplies[] = {{"FirstAidKit", 0.2}, {"Medikit", 0.1}};
weapons[] = {{"arifle_MX_F", 0.3}, {"arifle_Katiba_F", 0.2}};
vehicles[] = {{"B_MRAP_01_F", 0.1}, {"B_APC_Wheeled_01_cannon_F", 0.05}};
special[] = {{"B_UAV_01_F", 0.05}, {"B_Heli_Light_01_F", 0.02}};
};
penalty[] = {-100, -25};
timeLimit[] = {900, 1800}; // 15-30 minutes
};
class Defend {
minWaves = 3;
maxWaves = 8;
unitsPerWave[] = {4, 8};
waveCooldown = 300;
class Rewards {
money[] = {750000, 2000000};
reputation[] = {150, 600};
equipment[] = {{"ItemGPS", 0.5}, {"ItemCompass", 0.3}};
supplies[] = {{"FirstAidKit", 0.2}, {"Medikit", 0.1}};
weapons[] = {{"arifle_MX_F", 0.3}, {"arifle_Katiba_F", 0.2}};
vehicles[] = {{"B_MRAP_01_F", 0.1}, {"B_APC_Wheeled_01_cannon_F", 0.05}};
special[] = {{"B_UAV_01_F", 0.05}, {"B_Heli_Light_01_F", 0.02}};
};
penalty[] = {-150, -50};
timeLimit[] = {1800, 3600}; // 30-60 minutes
};
class Hostage {
class Hostages {
civilian[] = {"C_man_1", "C_man_polo_1_F"};
military[] = {"B_Pilot_F", "B_officer_F"};
};
class Rewards {
money[] = {1000000, 2500000};
reputation[] = {200, 700};
equipment[] = {{"ItemGPS", 0.5}, {"ItemCompass", 0.3}};
supplies[] = {{"FirstAidKit", 0.2}, {"Medikit", 0.1}};
weapons[] = {{"arifle_MX_F", 0.3}, {"arifle_Katiba_F", 0.2}};
vehicles[] = {{"B_MRAP_01_F", 0.1}, {"B_APC_Wheeled_01_cannon_F", 0.05}};
special[] = {{"B_UAV_01_F", 0.05}, {"B_Heli_Light_01_F", 0.02}};
};
penalty[] = {-200, -75};
timeLimit[] = {600, 900}; // 10-15 minutes
};
class HVT {
class Targets {
officer[] = {"O_officer_F"};
sniper[] = {"O_sniper_F"};
};
escorts = 4;
class Rewards {
money[] = {800000, 2000000};
reputation[] = {175, 650};
equipment[] = {{"ItemGPS", 0.5}, {"ItemCompass", 0.3}};
supplies[] = {{"FirstAidKit", 0.2}, {"Medikit", 0.1}};
weapons[] = {{"arifle_MX_F", 0.3}, {"arifle_Katiba_F", 0.2}};
vehicles[] = {{"B_MRAP_01_F", 0.1}, {"B_APC_Wheeled_01_cannon_F", 0.05}};
special[] = {{"B_UAV_01_F", 0.05}, {"B_Heli_Light_01_F", 0.02}};
};
penalty[] = {-175, -50};
timeLimit[] = {900, 1800}; // 15-30 minutes
};
class Defuse {
class Devices {
small[] = {"DemoCharge_Remote_Mag"};
large[] = {"SatchelCharge_Remote_Mag"};
};
maxDevices = 3;
class Rewards {
money[] = {600000, 1500000};
reputation[] = {125, 450};
equipment[] = {{"ItemGPS", 0.5}, {"ItemCompass", 0.3}};
supplies[] = {{"FirstAidKit", 0.2}, {"Medikit", 0.1}};
weapons[] = {{"arifle_MX_F", 0.3}, {"arifle_Katiba_F", 0.2}};
vehicles[] = {{"B_MRAP_01_F", 0.1}, {"B_APC_Wheeled_01_cannon_F", 0.05}};
special[] = {{"B_UAV_01_F", 0.05}, {"B_Heli_Light_01_F", 0.02}};
};
penalty[] = {-125, -40};
timeLimit[] = {600, 900}; // 10-15 minutes
};
class Delivery {
class Cargo {
supplies[] = {"Land_CargoBox_V1_F"};
vehicles[] = {"B_MRAP_01_F", "B_Truck_01_transport_F"};
};
class Rewards {
money[] = {400000, 1200000};
reputation[] = {75, 350};
equipment[] = {{"ItemGPS", 0.5}, {"ItemCompass", 0.3}};
supplies[] = {{"FirstAidKit", 0.2}, {"Medikit", 0.1}};
weapons[] = {{"arifle_MX_F", 0.3}, {"arifle_Katiba_F", 0.2}};
vehicles[] = {{"B_MRAP_01_F", 0.1}, {"B_APC_Wheeled_01_cannon_F", 0.05}};
special[] = {{"B_UAV_01_F", 0.05}, {"B_Heli_Light_01_F", 0.02}};
};
penalty[] = {-75, -25};
timeLimit[] = {900, 1800}; // 15-30 minutes
};
};
};

View File

@ -24,6 +24,7 @@ PREP(makeIED);
PREP(makeObject); PREP(makeObject);
PREP(makeShooter); PREP(makeShooter);
PREP(makeTarget); PREP(makeTarget);
PREP(missionManager);
PREP(protectedModule); PREP(protectedModule);
PREP(shootersModule); PREP(shootersModule);
PREP(spawnEnemyWave); PREP(spawnEnemyWave);

View File

@ -2,6 +2,9 @@
GVAR(defusedCount) = 0; GVAR(defusedCount) = 0;
// TODO: Implement mission manager
// if (isServer) then { [] call FUNC(missionManager); };
["ace_explosives_defuse", { ["ace_explosives_defuse", {
GVAR(defusedCount) = GVAR(defusedCount) + 1; GVAR(defusedCount) = GVAR(defusedCount) + 1;
}] call CFUNC(addEventHandler); }] call CFUNC(addEventHandler);

View File

@ -16,3 +16,4 @@ class CfgPatches {
#include "CfgEventHandlers.hpp" #include "CfgEventHandlers.hpp"
#include "CfgFactionClasses.hpp" #include "CfgFactionClasses.hpp"
#include "CfgVehicles.hpp" #include "CfgVehicles.hpp"
#include "CfgMissions.hpp"

View File

@ -0,0 +1,212 @@
#include "..\script_component.hpp"
/*
* Author: IDSolutions
* Manages the dynamic mission system
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call forge_client_task_fnc_missionManager
*/
// Load mission configuration
private _missionConfig = missionConfigFile >> "CfgMissions";
private _weightsConfig = _missionConfig >> "MissionWeights";
private _locationsConfig = _missionConfig >> "Locations";
private _aiGroupsConfig = _missionConfig >> "AIGroups";
private _missionTypesConfig = _missionConfig >> "MissionTypes";
// Get mission weights from config
private _missionTypes = [];
{
private _weight = getNumber(_weightsConfig >> configName _x);
_missionTypes pushBack [configName _x, _weight];
} forEach "true" configClasses _weightsConfig;
// Function to get a suitable location for a mission type
private _fnc_getMissionLocation = {
params ["_type"];
private _suitableLocations = [];
{
private _suitable = getArray(_x >> "suitable");
if (_type in _suitable) then {
_suitableLocations pushBack _x;
};
} forEach "true" configClasses _locationsConfig;
selectRandom _suitableLocations
};
// TODO: Implement a more sophisticated AI spawning system
// Function to spawn AI group for mission
private _fnc_spawnAIGroup = {
params ["_type", "_pos"];
private _suitableGroups = [];
{
private _suitable = getArray(_x >> "suitable");
if (_type in _suitable) then {
_suitableGroups pushBack _x;
};
} forEach "true" configClasses _aiGroupsConfig;
private _groupConfig = selectRandom _suitableGroups;
if (isNil "_groupConfig") exitWith { grpNull };
private _side = getText(_groupConfig >> "side");
private _group = createGroup (call compile _side);
{
private _unitClass = getText(_x >> "vehicle");
private _unitPos = getArray(_x >> "position");
private _unit = _group createUnit [_unitClass, _pos vectorAdd _unitPos, [], 0, "NONE"];
_unit setRank getText(_x >> "rank");
} forEach "true" configClasses (_groupConfig >> "Units");
_group
};
// Generate a random mission based on weights
private _fnc_getRandomMission = {
private _rand = random 1;
private _cumulative = 0;
{
_x params ["_type", "_weight"];
_cumulative = _cumulative + _weight;
if (_rand <= _cumulative) exitWith { _type };
} forEach _missionTypes;
};
// TODO: Implement a more sophisticated mission selection algorithm
// Generate mission parameters based on type
private _fnc_generateMissionParams = {
params ["_type"];
private _taskId = format ["task_%1_%2", _type, round(random 999999)];
private _typeConfig = _missionTypesConfig >> (_type call CFUNC(capitalize));
// Get location for mission
private _location = [_type] call _fnc_getMissionLocation;
private _pos = getArray(_location >> "position");
private _radius = getNumber(_location >> "radius");
// Generate rewards
private _moneyRange = getArray(_typeConfig >> "Rewards" >> "money");
private _repRange = getArray(_typeConfig >> "Rewards" >> "reputation");
private _penaltyRange = getArray(_typeConfig >> "penalty");
private _timeRange = getArray(_typeConfig >> "timeLimit");
private _reward = _moneyRange call BIS_fnc_randomNum;
private _reputation = _repRange call BIS_fnc_randomNum;
private _penalty = _penaltyRange call BIS_fnc_randomNum;
private _timeLimit = _timeRange call BIS_fnc_randomNum;
// Generate random reward items
private _equipmentRewards = [];
private _supplyRewards = [];
private _weaponRewards = [];
private _vehicleRewards = [];
private _specialRewards = [];
{
private _category = _x;
{
_x params ["_item", "_chance"];
if (random 1 < _chance) then {
switch (_category) do {
case "equipment": { _equipmentRewards pushBack _item };
case "supplies": { _supplyRewards pushBack _item };
case "weapons": { _weaponRewards pushBack _item };
case "vehicles": { _vehicleRewards pushBack _item };
case "special": { _specialRewards pushBack _item };
};
};
} forEach (getArray(_typeConfig >> "Rewards" >> _category));
} forEach ["equipment", "supplies", "weapons", "vehicles", "special"];
// TODO: Continue to refine mission types and their specific settings
// Return parameters based on mission type
switch (_type) do {
case "attack": {
private _group = [_type, _pos] call _fnc_spawnAIGroup;
private _units = units _group;
private _unitCount = count _units;
[_taskId, _unitCount, _radius, _reward, _penalty, _reputation, false, false, _equipmentRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]
};
case "defend": {
private _minWaves = getNumber(_typeConfig >> "minWaves");
private _maxWaves = getNumber(_typeConfig >> "maxWaves");
private _waves = floor(random (_maxWaves - _minWaves + 1)) + _minWaves;
private _waveCooldown = getNumber(_typeConfig >> "waveCooldown");
[_taskId, _pos, _timeLimit, _reward, _penalty, _reputation, false, false, _waves, _waveCooldown, _radius, _equipmentRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]
};
case "hostage": {
private _hostages = getArray(_typeConfig >> "Hostages" >> "civilian");
private _hostage = selectRandom _hostages;
[_taskId, _hostage, _radius, _pos, _reward, _penalty, _reputation, [false, true], false, false, _equipmentRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]
};
case "hvt": {
private _targets = getArray(_typeConfig >> "Targets" >> "officer");
private _target = selectRandom _targets;
private _escorts = getNumber(_typeConfig >> "escorts");
[_taskId, _target, _escorts, _pos, _reward, _penalty, _reputation, false, false, _equipmentRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]
};
case "defuse": {
private _devices = getArray(_typeConfig >> "Devices" >> "small");
private _maxDevices = getNumber(_typeConfig >> "maxDevices");
private _deviceCount = floor(random _maxDevices) + 1;
[_taskId, _deviceCount, _timeLimit, _reward, _penalty, _reputation, false, false, _devices, _equipmentRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]
};
case "delivery": {
private _cargo = selectRandom (getArray(_typeConfig >> "Cargo" >> "supplies"));
[_taskId, _pos, [_pos, _radius] call BIS_fnc_randomPos, _timeLimit, _reward, _penalty, _reputation, false, false, _cargo, _equipmentRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]
};
};
};
// Generate and start a random mission
private _fnc_startRandomMission = {
private _type = call _fnc_getRandomMission;
private _params = [_type] call _fnc_generateMissionParams;
// Start the mission based on type
switch (_type) do {
case "attack": { _params spawn forge_client_task_fnc_attack };
case "defend": { _params spawn forge_client_task_fnc_defend };
case "hostage": { _params spawn forge_client_task_fnc_hostage };
case "hvt": { _params spawn forge_client_task_fnc_hvt };
case "defuse": { _params spawn forge_client_task_fnc_defuse };
case "delivery": { _params spawn forge_client_task_fnc_delivery };
};
// Return the task ID for tracking
_params select 0
};
// Mission queue management
GVAR(missionQueue) = [];
GVAR(activeMissions) = [];
GVAR(maxConcurrentMissions) = getNumber(_missionConfig >> "maxConcurrentMissions");
GVAR(missionInterval) = getNumber(_missionConfig >> "missionInterval");
// Main mission manager loop
[{
// Check if we can generate new missions
if (count GVAR(activeMissions) < GVAR(maxConcurrentMissions)) then {
private _taskId = call _fnc_startRandomMission;
GVAR(activeMissions) pushBack _taskId;
// Clean up completed missions
GVAR(activeMissions) = GVAR(activeMissions) select {
!((missionNamespace getVariable [format ["FORGE_task_%1_completed", _x], false]) ||
(missionNamespace getVariable [format ["FORGE_task_%1_failed", _x], false]))
};
};
}, GVAR(missionInterval), []] call CFUNC(addPerFrameHandler);