diff --git a/addons/task/CfgMissions.hpp b/addons/task/CfgMissions.hpp new file mode 100644 index 0000000..0e3e266 --- /dev/null +++ b/addons/task/CfgMissions.hpp @@ -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 + }; + }; +}; diff --git a/addons/task/XEH_PREP.hpp b/addons/task/XEH_PREP.hpp index b0eeb21..3598a3a 100644 --- a/addons/task/XEH_PREP.hpp +++ b/addons/task/XEH_PREP.hpp @@ -24,6 +24,7 @@ PREP(makeIED); PREP(makeObject); PREP(makeShooter); PREP(makeTarget); +PREP(missionManager); PREP(protectedModule); PREP(shootersModule); PREP(spawnEnemyWave); \ No newline at end of file diff --git a/addons/task/XEH_postInit.sqf b/addons/task/XEH_postInit.sqf index 8a25727..b936bad 100644 --- a/addons/task/XEH_postInit.sqf +++ b/addons/task/XEH_postInit.sqf @@ -2,6 +2,9 @@ GVAR(defusedCount) = 0; +// TODO: Implement mission manager +// if (isServer) then { [] call FUNC(missionManager); }; + ["ace_explosives_defuse", { GVAR(defusedCount) = GVAR(defusedCount) + 1; }] call CFUNC(addEventHandler); \ No newline at end of file diff --git a/addons/task/config.cpp b/addons/task/config.cpp index 8545832..1847fd5 100644 --- a/addons/task/config.cpp +++ b/addons/task/config.cpp @@ -15,4 +15,5 @@ class CfgPatches { #include "CfgEventHandlers.hpp" #include "CfgFactionClasses.hpp" -#include "CfgVehicles.hpp" \ No newline at end of file +#include "CfgVehicles.hpp" +#include "CfgMissions.hpp" \ No newline at end of file diff --git a/addons/task/functions/fnc_missionManager.sqf b/addons/task/functions/fnc_missionManager.sqf new file mode 100644 index 0000000..aaf7d74 --- /dev/null +++ b/addons/task/functions/fnc_missionManager.sqf @@ -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); \ No newline at end of file