diff --git a/@ExileServer/addons/a3_dms/config.cpp b/@ExileServer/addons/a3_dms/config.cpp index 084447a..55529c3 100644 --- a/@ExileServer/addons/a3_dms/config.cpp +++ b/@ExileServer/addons/a3_dms/config.cpp @@ -4,8 +4,8 @@ class CfgPatches { units[] = {}; weapons[] = {}; - a3_DMS_version = "September 5, 2016"; - requiredVersion = 1.36; + a3_DMS_version = "February 23, 2017"; + requiredVersion = 1.66; requiredAddons[] = {"exile_client","exile_server_config"}; }; }; diff --git a/@ExileServer/addons/a3_dms/config.sqf b/@ExileServer/addons/a3_dms/config.sqf index 7f8f8ca..18131d9 100644 --- a/@ExileServer/addons/a3_dms/config.sqf +++ b/@ExileServer/addons/a3_dms/config.sqf @@ -55,9 +55,10 @@ DMS_SpawnMissions_Scheduled = false; // Whether or not to spawn missions in a sc DMS_MaxBanditMissions = 3; // Maximum number of Bandit Missions running at the same time DMS_TimeToFirstMission = [180,420]; // [Minimum,Maximum] time between first mission spawn. | DEFAULT: 3-7 minutes. DMS_TimeBetweenMissions = [600,900]; // [Minimum,Maximum] time between missions (if mission limit is not reached) | DEFAULT: 10-15 mins - DMS_MissionTimeOut = [900,1800]; // [Minimum,Maximum] time it will take for a mission to timeout | DEFAULT: 15-30 mins + DMS_MissionTimeout = [900,1800]; // [Minimum,Maximum] time it will take for a mission to timeout | DEFAULT: 15-30 mins DMS_MissionTimeoutResetRange = 1500; // If a player is this close to a mission then it won't time-out. Set to 0 to disable this check. DMS_MissionTimeoutResetFrequency = 180; // How often (in seconds) to check for nearby players and reset the mission timeout. + DMS_ResetMissionTimeoutOnKill = true; // Whether or not to reset the mission timeout when an AI is killed. /*General settings for dynamic missions*/ /*General settings for static missions*/ @@ -68,6 +69,7 @@ DMS_SpawnMissions_Scheduled = false; // Whether or not to spawn missions in a sc DMS_StaticMissionTimeOut = [1800,3600]; // [Minimum,Maximum] time it will take for a static mission to timeout | DEFAULT: 30-60 mins DMS_StaticMissionTimeoutResetRange = 1500; // If a player is this close to a mission then it won't time-out. Set to 0 to disable this check. DMS_SMissionTimeoutResetFrequency = 180; // How often (in seconds) to check for nearby players and reset the mission timeout for static missions. + DMS_ResetStaticMissionTimeoutOnKill = true; // Whether or not to reset the mission timeout when an AI is killed (for Static Missions). DMS_StaticMinPlayerDistance = 1500; // If a player is this close to a mission location, then it won't spawn the mission and will wait 60 seconds before attempting to spawn it. DMS_AllowStaticReinforcements = true; // Whether or not static missions will receive reinforcements. This will simply disable the calling of GroupReinforcementsMonitor; DMS_SpawnFlareOnReinforcements = true; // Whether or not to spawn a flare and noise when AI reinforcements have spawned. @@ -267,12 +269,16 @@ DMS_SpawnMissions_Scheduled = false; // Whether or not to spawn missions in a sc DMS_StaticMissionTypes = [ // List of STATIC missions with spawn chances. - //["saltflats",1] //<--Example (already imported by default on Altis in map configs) - //["slums",1] //<--Example (already imported by default on Altis in map configs) + //["saltflats",1] //<--Example (already imported by default on Altis in map configs) + //["slums",1] //<--Example (already imported by default on Altis in map configs) //["occupation",1] //<--Example //["sectorB",1] //<--Example for Taviana ]; + DMS_SpecialMissions = [ // List of special missions with restrictions. Each element must be defined as [mission, minPlayers, maxPlayers, timesPerRestart, _timeBetween]. + //["specops",15,60,2,900] //<-- Example for a mission named "specops.sqf" that must be placed in the "special" folder. It will only spawn when there are at least 15 players, less than 60 players, it will only spawn up to twice per restart, and at least 900 seconds must pass before another instance of the mission can spawn. + ]; + DMS_BasesToImportOnServerStart = [ // List of static bases to import on server startup (spawned post-init). This will reduce the amount of work the server has to do when it actually spawns static missions, and players won't be surprised when a base suddenly pops up. You can also include any other M3E-exported bases to spawn here. //"saltflatsbase", //<--Example (already imported by default on Altis) //"slums_objects" //<--Example (already imported by default on Altis) @@ -301,7 +307,24 @@ DMS_SpawnMissions_Scheduled = false; // Whether or not to spawn missions in a sc /* AI Settings */ DMS_AI_Classname = "O_recon_F"; // Since some of you wanted this... - DMS_AI_UseRealNames = true; // true if you want Arma assigned real names, false if you want random DMS assigned unit numbers + + DMS_AI_NamingType = 0; // This specifies the "naming scheme" for the AI. 0 corresponds with the default ArmA names; 1 means you want a DMS name (eg: [DMS BANDIT SOLDIER 123]); 2 means you want to generate a name from a list of first and last names (DMS_AI_FirstNames, DMS_AI_LastNames). + DMS_AI_FirstNames = [ // List of "first names" that an AI can have. Only used when DMS_AI_NamingType = 2. + "Adam", + "Benjamin", + "Charles", + "David", + "Eric" + // etc. + ]; + DMS_AI_LastNames = [ // List of "last names" that an AI can have. Only used when DMS_AI_NamingType = 2. + "Smith", + "Johnson", + "Williams", + "Jones", + "Brown" + // etc. + ]; DMS_Show_Kill_Poptabs_Notification = true; // Whether or not to show the poptabs gained/lost message on the player's screen when killing an AI. (It will still change the player's money, it just won't show the "Money Received" notification) DMS_Show_Kill_Respect_Notification = true; // Whether or not to show the "Frag Message" on the player's screen when killing an AI. (It will still change the player's respect, it just won't show the "AI Killed" frag message) @@ -411,7 +434,7 @@ DMS_SpawnMissions_Scheduled = false; // Whether or not to spawn missions in a sc DMS_AIDistanceCheckFrequency = 60; // How often to check within DMS_fnc_TargetsKilled whether or not the AI is out of the maximum radius. Lower values increase frequency and increase server load, greater values decrease frequency and may cause longer delays for "runaway" AI. DMS_ai_offload_to_client = true; // Offload spawned AI groups to random clients. Helps with server performance. - DMS_ai_offload_Only_DMS_AI = false; // Do you use other mission systems on your server but still want to offload AI? You should probably enable this then, unless you have tested it for compatibility. + DMS_ai_offload_Only_DMS_AI = true; // Don't set this to false unless you know what you're doing. DMS_ai_offload_notifyClient = false; // Notify the client when AI has been offloaded to the client. DMS_ai_allowFreezing = true; // Whether or not to "freeze" AI that are a certain distance away from players (and therefore inactive). @@ -1268,3 +1291,6 @@ DMS_SpawnMissions_Scheduled = false; // Whether or not to spawn missions in a sc //"Exile_Car_Offroad_Armed_Guerilla01" ] + DMS_MilitaryVehicles + DMS_TransportTrucks; /* Loot Settings */ + + +DMS_ConfigLoaded = true; diff --git a/@ExileServer/addons/a3_dms/fn_DMS_postInit.sqf b/@ExileServer/addons/a3_dms/fn_DMS_postInit.sqf index 9dfa70a..7d38e86 100644 --- a/@ExileServer/addons/a3_dms/fn_DMS_postInit.sqf +++ b/@ExileServer/addons/a3_dms/fn_DMS_postInit.sqf @@ -14,7 +14,7 @@ if !(isServer) exitWith -if (isNil "DMS_DynamicMission") exitWith +if (isNil "DMS_ConfigLoaded") exitWith { for "_i" from 0 to 99 do { diff --git a/@ExileServer/addons/a3_dms/fn_DMS_preInit.sqf b/@ExileServer/addons/a3_dms/fn_DMS_preInit.sqf index e5d4a1a..ba1bb87 100644 --- a/@ExileServer/addons/a3_dms/fn_DMS_preInit.sqf +++ b/@ExileServer/addons/a3_dms/fn_DMS_preInit.sqf @@ -1,6 +1,6 @@ /* DMS Pre-init - Written by eraser1 (trainwreckdayz.com) + Created by eraser1 */ #define CALLFILE(FILE) call compile preprocessFileLineNumbers FILE; @@ -20,6 +20,16 @@ DMS_Version = getText (configFile >> "CfgPatches" >> "a3_dms" >> "a3_DMS_version //Load main config CALLFILE("\x\addons\dms\config.sqf"); +// Let's be honest - you know it's gonna happen. +if (isNil "DMS_AI_NamingType") then +{ + for "_i" from 0 to 99 do + { + diag_log format["!!!!!!!!MAKE SURE YOUR DMS CONFIG IS UPDATED!!!!!"]; + }; + DMS_ConfigLoaded = nil; +}; + //Load map-specific configs. Should make it easier for people with multiple servers/maps. One PBO to rule them all... if (DMS_Use_Map_Config) then @@ -120,3 +130,17 @@ DMS_CLIENT_fnc_hintSilent = compileFinal "hintSilent parsetext format['%1',_this // Initialize mission variables... CALLFILE("\x\addons\dms\missions\static_init.sqf"); CALLFILE("\x\addons\dms\missions\mission_init.sqf"); + + +{ + private _mission = _x select 0; + + missionNamespace setVariable + [ + format["DMS_SpecialMission_%1",_mission], + compileFinal preprocessFileLineNumbers (format ["\x\addons\DMS\missions\special\%1.sqf",_mission]) + ]; + + missionNamespace setVariable [format["DMS_SpecialMissionSpawnCount_%1",_mission], 0]; + missionNamespace setVariable [format["DMS_SpecialMissionLastSpawn_%1",_mission], 0]; +} forEach DMS_SpecialMissions; diff --git a/@ExileServer/addons/a3_dms/missions/bandit/nedcar_mission.sqf b/@ExileServer/addons/a3_dms/missions/bandit/nedcar_mission.sqf index 98160d4..ecea72e 100644 --- a/@ExileServer/addons/a3_dms/missions/bandit/nedcar_mission.sqf +++ b/@ExileServer/addons/a3_dms/missions/bandit/nedcar_mission.sqf @@ -173,8 +173,9 @@ _PossibleVehicleClass = _VehicleClass = selectRandom _PossibleVehicleClass; -//DMS_fnc_SpawnPersistentVehicle will automatically turn the pincode into a string and format it. -_pinCode = round (random 9999); +// DMS_fnc_SpawnPersistentVehicle will automatically turn the pincode into a string and format it. +// Generate a pincode greater than 1000 because we shouldn't waste time having to format it in the mission messages. +_pinCode = 1000 + round (random 8999); _vehicle = [_VehicleClass, _pos getPos [30, random 360], _pinCode] call DMS_fnc_SpawnPersistentVehicle; diff --git a/@ExileServer/addons/a3_dms/missions/bandit/nedhatchback_mission.sqf b/@ExileServer/addons/a3_dms/missions/bandit/nedhatchback_mission.sqf index a5c59c0..f64e51b 100644 --- a/@ExileServer/addons/a3_dms/missions/bandit/nedhatchback_mission.sqf +++ b/@ExileServer/addons/a3_dms/missions/bandit/nedhatchback_mission.sqf @@ -173,7 +173,7 @@ _VehicleClass = selectRandom _PossibleVehicleClass; //DMS_fnc_SpawnPersistentVehicle will automatically turn the pincode into a string and format it. -_pinCode = round (random 9999); +_pinCode = 1000 + round (random 8999); _vehicle = [_VehicleClass,[(_pos select 0) -30, (_pos select 1) -30],_pinCode] call DMS_fnc_SpawnPersistentVehicle; diff --git a/@ExileServer/addons/a3_dms/missions/bandit/thieves.sqf b/@ExileServer/addons/a3_dms/missions/bandit/thieves.sqf index c75c1b7..6e2f095 100644 --- a/@ExileServer/addons/a3_dms/missions/bandit/thieves.sqf +++ b/@ExileServer/addons/a3_dms/missions/bandit/thieves.sqf @@ -82,7 +82,7 @@ _class = }; //DMS_fnc_SpawnPersistentVehicle will automatically turn the pincode into a string and format it. -_pinCode = round (random 9999); +_pinCode = 1000 + round (random 8999); _vehicle = [_class,_pos,_pinCode] call DMS_fnc_SpawnPersistentVehicle; diff --git a/@ExileServer/addons/a3_dms/missions/mission_init.sqf b/@ExileServer/addons/a3_dms/missions/mission_init.sqf index 0930401..71b5cfb 100644 --- a/@ExileServer/addons/a3_dms/missions/mission_init.sqf +++ b/@ExileServer/addons/a3_dms/missions/mission_init.sqf @@ -23,9 +23,7 @@ if (DMS_DEBUG) then // Set mission frequencies from config DMS_BanditMissionTypesArray = []; { - private "_missionName"; - - _missionName = _x select 0; + private _missionName = _x select 0; for "_i" from 1 to (_x select 1) do { diff --git a/@ExileServer/addons/a3_dms/missions/static_init.sqf b/@ExileServer/addons/a3_dms/missions/static_init.sqf index cab144d..de35bbf 100644 --- a/@ExileServer/addons/a3_dms/missions/static_init.sqf +++ b/@ExileServer/addons/a3_dms/missions/static_init.sqf @@ -22,9 +22,7 @@ if (DMS_DEBUG) then // Set mission frequencies from config DMS_StaticMissionTypesArray = []; { - private "_missionName"; - - _missionName = _x select 0; + private _missionName = _x select 0; for "_i" from 1 to (_x select 1) do { diff --git a/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor.sqf b/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor.sqf index e18b66c..c31c5f0 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor.sqf @@ -156,6 +156,8 @@ try throw format["_onEndingScripts |%1|",_onEndingScripts]; }; + private _unitCount = count (_units call DMS_fnc_GetAllUnits); + private _arr = [ _pos, @@ -185,7 +187,8 @@ try _onFailScripts, _onMonitorStart, _onMonitorEnd - ] + ], + _unitCount ]; DMS_Mission_Arr pushBack _arr; _added = true; @@ -193,7 +196,7 @@ try if (DMS_MarkerText_ShowAICount) then { private _markerDot = _markers select 0; - _markerDot setMarkerText (format ["%1 (%2 %3 remaining)",markerText _markerDot,count _units,DMS_MarkerText_AIName]); + _markerDot setMarkerText (format ["%1 (%2 %3 remaining)",markerText _markerDot,_unitCount,DMS_MarkerText_AIName]); }; if (DMS_DEBUG) then diff --git a/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor_Static.sqf b/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor_Static.sqf index bc9d3a8..254fe0b 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor_Static.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_AddMissionToMonitor_Static.sqf @@ -158,6 +158,8 @@ try throw format["_onEndingScripts |%1|",_onEndingScripts]; }; + private _unitCount = count (_units call DMS_fnc_GetAllUnits); + private _arr = [ _pos, @@ -188,7 +190,8 @@ try _onFailScripts, _onMonitorStart, _onMonitorEnd - ] + ], + _unitCount ]; DMS_StaticMission_Arr pushBack _arr; _added = true; @@ -196,7 +199,7 @@ try if (DMS_MarkerText_ShowAICount_Static) then { private _markerDot = _markers select 0; - _markerDot setMarkerText (format ["%1 (%2 %3 remaining)",markerText _markerDot,count (_units call DMS_fnc_GetAllUnits),DMS_MarkerText_AIName]); + _markerDot setMarkerText (format ["%1 (%2 %3 remaining)",markerText _markerDot,_unitCount,DMS_MarkerText_AIName]); }; if (DMS_DEBUG) then diff --git a/@ExileServer/addons/a3_dms/scripts/fn_ImportFromM3E_3DEN.sqf b/@ExileServer/addons/a3_dms/scripts/fn_ImportFromM3E_3DEN.sqf index 3ffd9cc..88d0a06 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_ImportFromM3E_3DEN.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_ImportFromM3E_3DEN.sqf @@ -54,6 +54,7 @@ private _objs = _export apply _object setPosATL (_center vectorAdd (_x select 1)); _object enableSimulationGlobal ((_x select 3) select 0); _object allowDamage ((_x select 3) select 1); + _object; }; diff --git a/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Dynamic.sqf b/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Dynamic.sqf index df5371a..ab54d0f 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Dynamic.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Dynamic.sqf @@ -28,7 +28,8 @@ _onFailScripts, _onMonitorStart, _onMonitorEnd - ] + ], + _prevAICount ] A semi-full breakdown can be found in fn_AddMissionToMonitor.sqf @@ -47,7 +48,9 @@ "_missionSide", "_missionDifficulty", "_missionEvents", - "_missionScripts" + "_missionScripts", + "_prevAICount", + "_isSpecial" ]) then { @@ -134,10 +137,6 @@ if (_missionSide == "bandit") then { DMS_RunningBMissionCount = DMS_RunningBMissionCount - 1; - } - else - { - // Not yet implemented }; { @@ -195,71 +194,91 @@ throw format ["Mission (%1) Success at %2 with message %3.",_missionName,_pos,_msgWIN]; }; - if ((diag_tickTime-_timeStarted)>_failTime) then + + private _timeElapsed = diag_tickTime - _timeStarted; + + if (DMS_ResetMissionTimeoutOnKill) then { - // Check to see if the timeout should be extended before ending the mission. - if ((DMS_MissionTimeoutResetRange>0) && {[_pos,DMS_MissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby}) then - { - _x set [2,[diag_tickTime,_failTime]]; - - throw format ["Mission Timeout Extended at %1 with timeout after %2 seconds. Position: %3",diag_tickTime,_failTime,_pos]; - }; - - //Nobody is nearby so just cleanup objects from here - private _cleanupList = ((_units call DMS_fnc_GetAllUnits)+_buildings+_vehs+_mines); - - { - _cleanupList pushBack (_x select 0); - } forEach _crate_info_array; - - private _prev = DMS_CleanUp_PlayerNearLimit; - DMS_CleanUp_PlayerNearLimit = 0; // Temporarily set the limit to 0 since we want to delete all the stuff regardless of player presence. - - _cleanupList call DMS_fnc_CleanUp; - - DMS_CleanUp_PlayerNearLimit = _prev; - - - if (_missionSide == "bandit") then - { - DMS_RunningBMissionCount = DMS_RunningBMissionCount - 1; - } - else - { - // Not yet implemented - }; - - { - _params = _x select 0; - _code = _x select 1; - if (_code isEqualType "") then - { - _code = compile _code; - }; - _params call _code; - } forEach _onFailScripts; - - [_missionName,_msgLose] call DMS_fnc_BroadcastMissionStatus; - [_markers,"lose"] call DMS_fnc_RemoveMarkers; - - DMS_Mission_Arr deleteAt _forEachIndex; - - throw format ["Mission (%1) Fail at %2 with message %3.",_missionName,_pos,_msgLose]; - }; - - if ((diag_tickTime-_timeStarted)>DMS_MissionTimeoutResetFrequency) then - { - if ((DMS_MissionTimeoutResetRange>0) && {[_pos,DMS_MissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby}) then + private _AICount = count (_units call DMS_fnc_GetAllUnits); + + if (_AICount != _prevAICount) then { _x set [2,[diag_tickTime,_failTime]]; + _x set [11, _AICount]; + _timeElapsed = 0; if (DMS_DEBUG) then { - format["Mission Timeout Extended at %1 with timeout after %2 seconds. Position: %3",diag_tickTime,_failTime,_pos] call DMS_fnc_DebugLog; + format["MissionsMonitor_Dynamic :: Mission Timeout Extended to %1 more seconds; AI count changed from %2 to %3. Position: %4, MissionIndex: %5",_failTime, _prevAICount, _AICount,_pos,_forEachIndex] call DMS_fnc_DebugLog; }; }; }; + switch (true) do + { + case (_timeElapsed > DMS_MissionTimeoutResetFrequency): + { + if ([_pos,DMS_MissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby) then + { + _x set [2,[diag_tickTime,_failTime]]; + + if (DMS_DEBUG) then + { + format["MissionsMonitor_Dynamic :: Mission Timeout Extended to %1 more seconds; player found within %2 meters. Position: %3, MissionIndex: %4",_failTime,DMS_MissionTimeoutResetRange,_pos,_forEachIndex] call DMS_fnc_DebugLog; + }; + }; + }; + + case (_timeElapsed > _failTime): + { + // Check to see if the timeout should be extended before ending the mission. + if ([_pos,DMS_MissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby) then + { + _x set [2,[diag_tickTime,_failTime]]; + + throw format["Mission Timeout Extended to %1 more seconds; player found within %2 meters. Position: %3, MissionIndex: %4",_failTime,DMS_MissionTimeoutResetRange,_pos,_forEachIndex] call DMS_fnc_DebugLog; + }; + + //Nobody is nearby so just cleanup objects from here + private _cleanupList = ((_units call DMS_fnc_GetAllUnits)+_buildings+_vehs+_mines); + + { + _cleanupList pushBack (_x select 0); + } forEach _crate_info_array; + + private _prev = DMS_CleanUp_PlayerNearLimit; + DMS_CleanUp_PlayerNearLimit = 0; // Temporarily set the limit to 0 since we want to delete all the stuff regardless of player presence. + + _cleanupList call DMS_fnc_CleanUp; + + DMS_CleanUp_PlayerNearLimit = _prev; + + + if (_missionSide == "bandit") then + { + DMS_RunningBMissionCount = DMS_RunningBMissionCount - 1; + }; + + { + _params = _x select 0; + _code = _x select 1; + if (_code isEqualType "") then + { + _code = compile _code; + }; + _params call _code; + } forEach _onFailScripts; + + [_missionName,_msgLose] call DMS_fnc_BroadcastMissionStatus; + [_markers,"lose"] call DMS_fnc_RemoveMarkers; + + DMS_Mission_Arr deleteAt _forEachIndex; + + throw format ["Mission (%1) Fail at %2 with message %3.",_missionName,_pos,_msgLose]; + }; + }; + + if (DMS_MarkerText_ShowAICount) then { private _dot = _markers select 0; @@ -273,14 +292,6 @@ _dot setMarkerText (format ["%1 (%2 %3 remaining)",_text,count (_units call DMS_fnc_GetAllUnits),DMS_MarkerText_AIName]); }; - /* - Coming soon... - - { - _x call DMS_fnc_HandleMissionEvents; - } forEach _missionEvents; - */ - if !(_onMonitorEnd isEqualTo {}) then { diff --git a/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Static.sqf b/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Static.sqf index b19e576..69979eb 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Static.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_MissionsMonitor_Static.sqf @@ -34,7 +34,8 @@ _onFailScripts, _onMonitorStart, _onMonitorEnd - ] + ], + _prevAICount ] A semi-full breakdown can be found in fn_AddStaticMissionToMonitor.sqf @@ -54,7 +55,8 @@ "_missionSide", "_missionDifficulty", "_missionEvents", - "_missionScripts" + "_missionScripts", + "_prevAICount" ]) then { @@ -194,63 +196,87 @@ throw format ["Mission (%1) Success at %2 with message %3.",_missionName,_missionPos,_msgWIN]; }; - if ((diag_tickTime-_timeStarted)>_failTime) then + + private _timeElapsed = diag_tickTime - _timeStarted; + + if (DMS_ResetStaticMissionTimeoutOnKill) then { - // Check to see if the timeout should be extended before ending the mission. - if ((DMS_StaticMissionTimeoutResetRange>0) && {[_missionPos,DMS_StaticMissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby}) then - { - _x set [3,[diag_tickTime,_failTime]]; - - throw format ["Mission Timeout Extended at %1 with timeout after %2 seconds. Position: %3",diag_tickTime,_failTime,_missionPos]; - }; - - //Nobody is nearby so just cleanup objects from here - _cleanupList = ((_inputAIUnits call DMS_fnc_GetAllUnits)+_buildings+_vehs+_mines); - - { - _cleanupList pushBack (_x select 0); - } forEach _crate_info_array; - - private _prev = DMS_CleanUp_PlayerNearLimit; - DMS_CleanUp_PlayerNearLimit = 0; // Temporarily set the limit to 0 since we want to delete all the stuff regardless of player presence. - - _cleanupList call DMS_fnc_CleanUp; - - DMS_CleanUp_PlayerNearLimit = _prev; - - - { - _params = _x select 0; - _code = _x select 1; - if (_code isEqualType "") then - { - _code = compile _code; - }; - _params call _code; - } forEach _onFailScripts; - - [_missionName,_msgLose] call DMS_fnc_BroadcastMissionStatus; - [_markers,"lose"] call DMS_fnc_RemoveMarkers; - - DMS_StaticMission_Arr deleteAt _forEachIndex; - DMS_RunningStaticMissions deleteAt _forEachIndex; - - throw format ["Mission (%1) Fail at %2 with message %3.",_missionName,_missionPos,_msgLose]; - }; - - if ((diag_tickTime-_timeStarted)>DMS_SMissionTimeoutResetFrequency) then - { - if ((DMS_StaticMissionTimeoutResetRange>0) && {[_missionPos,DMS_StaticMissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby}) then + private _AICount = count (_inputAIUnits call DMS_fnc_GetAllUnits); + + if (_AICount != _prevAICount) then { _x set [3,[diag_tickTime,_failTime]]; + _x set [12, _AICount]; + _timeElapsed = 0; if (DMS_DEBUG) then { - format["Static Mission Timeout Extended at %1 with timeout after %2 seconds. Position: %3",diag_tickTime,_failTime,_missionPos] call DMS_fnc_DebugLog; + format["MissionsMonitor_Static :: Static Mission Timeout Extended to %1 more seconds; AI count changed from %2 to %3. Position: %4, MissionIndex: %5",_failTime, _prevAICount, _AICount,_pos,_forEachIndex] call DMS_fnc_DebugLog; }; }; }; + switch (true) do + { + case (_timeElapsed > DMS_SMissionTimeoutResetFrequency): + { + if ([_missionPos,DMS_StaticMissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby) then + { + _x set [3,[diag_tickTime,_failTime]]; + + if (DMS_DEBUG) then + { + format["MissionsMonitor_Static :: Static Mission Timeout Extended to %1 more seconds; player found within %2 meters. Position: %3, MissionIndex: %4",_failTime,DMS_StaticMissionTimeoutResetRange,_pos,_forEachIndex] call DMS_fnc_DebugLog; + }; + }; + }; + + case (_timeElapsed > _failTime): + { + // Check to see if the timeout should be extended before ending the mission. + if ((DMS_StaticMissionTimeoutResetRange>0) && {[_missionPos,DMS_StaticMissionTimeoutResetRange] call DMS_fnc_IsPlayerNearby}) then + { + _x set [3,[diag_tickTime,_failTime]]; + + throw format ["Mission Timeout Extended at %1 with timeout after %2 seconds. Position: %3",diag_tickTime,_failTime,_missionPos]; + }; + + //Nobody is nearby so just cleanup objects from here + _cleanupList = ((_inputAIUnits call DMS_fnc_GetAllUnits)+_buildings+_vehs+_mines); + + { + _cleanupList pushBack (_x select 0); + } forEach _crate_info_array; + + private _prev = DMS_CleanUp_PlayerNearLimit; + DMS_CleanUp_PlayerNearLimit = 0; // Temporarily set the limit to 0 since we want to delete all the stuff regardless of player presence. + + _cleanupList call DMS_fnc_CleanUp; + + DMS_CleanUp_PlayerNearLimit = _prev; + + + { + _params = _x select 0; + _code = _x select 1; + if (_code isEqualType "") then + { + _code = compile _code; + }; + _params call _code; + } forEach _onFailScripts; + + [_missionName,_msgLose] call DMS_fnc_BroadcastMissionStatus; + [_markers,"lose"] call DMS_fnc_RemoveMarkers; + + DMS_StaticMission_Arr deleteAt _forEachIndex; + DMS_RunningStaticMissions deleteAt _forEachIndex; + + throw format ["Mission (%1) Fail at %2 with message %3.",_missionName,_missionPos,_msgLose]; + }; + }; + + if (DMS_MarkerText_ShowAICount_Static) then { private _dot = _markers select 0; @@ -264,14 +290,6 @@ _dot setMarkerText (format ["%1 (%2 %3 remaining)",_text,count (_inputAIUnits call DMS_fnc_GetAllUnits),DMS_MarkerText_AIName]); }; - /* - Coming soon... - - { - _x call DMS_fnc_HandleMissionEvents; - } forEach _missionEvents; - */ - if (DMS_AllowStaticReinforcements) then { diff --git a/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf b/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf index f80d954..fcc73d7 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf @@ -5,13 +5,10 @@ Usage: [ - [ - _killedUnit, - _killer - ], - _side, // "bandit" only for now - _type // Type of AI: "soldier","static","vehicle","heli", etc. + _killedUnit, + _killer ] call DMS_fnc_OnKilled; + ***Designed for use with the ArmA "MPKilled" EH. This function should not be explicitly called otherwise.*** */ if (DMS_DEBUG) then { diff --git a/@ExileServer/addons/a3_dms/scripts/fn_SelectMission.sqf b/@ExileServer/addons/a3_dms/scripts/fn_SelectMission.sqf index 34694c5..4d0c337 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_SelectMission.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_SelectMission.sqf @@ -68,4 +68,51 @@ if (diag_fps >= DMS_MinServerFPS && {_playerCount >= DMS_MinPlayerCount}) then (format ["SelectMission :: Spawning of static mission ""%1"" complete!", _mission]) call DMS_fnc_DebugLog; }; }; + + + { + _x params + [ + "_mission", + "_minPlayers", + "_maxPlayers", + "_maxTimesPerRestart", + "_timeBetween" + ]; + + private _timesPerRestart = missionNamespace getVariable format["DMS_SpecialMissionSpawnCount_%1",_mission]; + + if + ( + (_playerCount > _minPlayers) && + {_playerCount < _maxPlayers} && + {_maxTimesPerRestart < _timesPerRestart} && + {(_time - (missionNamespace getVariable format["DMS_SpecialMissionLastSpawn_%1",_mission])) > _timeBetween} + ) then + { + private _missionCode = missionNamespace getVariable format + [ + "DMS_SpecialMission_%1", + "no" + ]; + + if (_missionCode isEqualTo "no") then + { + diag_log format ["DMS ERROR :: Attempting to spawn a special mission that isn't precompiled! Parameters: %1", DMS_SpecialMissions deleteAt _forEachIndex]; + } + else + { + missionNamespace setVariable [format["DMS_SpecialMissionSpawnCount_%1",_mission], _timesPerRestart+1]; + + call _missionCode; + + missionNamespace setVariable [format["DMS_SpecialMissionLastSpawn_%1",_mission], _time]; + + if (DMS_DEBUG) then + { + (format ["SelectMission :: Spawned special mission %1. DMS_SpawnedSpecialMissions: %2", _mission, DMS_SpawnedSpecialMissions]) call DMS_fnc_DebugLog; + }; + }; + }; + } forEach DMS_SpecialMissions; }; diff --git a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIGroup.sqf b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIGroup.sqf index 5d1f728..24e4418 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIGroup.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIGroup.sqf @@ -8,7 +8,7 @@ _pos, // ARRAY (positionATL): Position of AI _count, // SCALAR: Number of AI _difficulty, // STRING: AI Difficulty: "random","hardcore","difficult","moderate", or "easy" - _class, // STRING: AI Class: "random","assault","MG","sniper" or "unarmed" OR [_class,_launcherType] + _class, // STRING: AI Class: "random","assault","MG", or "sniper" OR [_class,_launcherType] _side, // STRING: Only "bandit" is supported by default _customGearSet // (OPTIONAL) ARRAY: Manually defined AI gear. Refer to functional documentation of fn_SpawnAISoldier.sqf for more info: https://github.com/Defent/DMS_Exile/blob/master/%40ExileServer/addons/a3_dms/scripts/fn_SpawnAISoldier.sqf ] call DMS_fnc_SpawnAIGroup; @@ -30,13 +30,6 @@ exitWith grpNull }; -if (_count < 1) exitWith -{ - diag_log format ["DMS ERROR :: Calling DMS_SpawnAIGroup with less than 1 _count! _this: %1",_this]; - grpNull -}; - - if (DMS_DEBUG) then { (format["SpawnAIGroup :: Spawning %1 %2 %3 AI at %4 with %5 difficulty.",_count,_class,_side,_pos,_difficulty]) call DMS_fnc_DebugLog; diff --git a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAISoldier.sqf b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAISoldier.sqf index 45fa87b..117286f 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAISoldier.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAISoldier.sqf @@ -7,7 +7,7 @@ [ _group, // GROUP: Group the AI will belong to _pos, // ARRAY (positionATL): Position of AI - _class, // STRING: Classname: "random","assault","MG","sniper" or "unarmed". Use "custom" to use "_customGearSet" + _class, // STRING: Classname: "random","assault","MG", or "sniper". Use "custom" to use "_customGearSet" _difficulty, // STRING: Difficulty: "random","static","hardcore","difficult","moderate", or "easy" _side, // STRING: "bandit" only by default _type, // STRING: Type of AI: "soldier","static","vehicle","heli", etc. @@ -29,11 +29,12 @@ _backpack // String | EG: "B_Carryall_oli" ] - Returns AI Unit + Returns AI Object */ +// Enabling this ensures that any optic/bipod/accessory that isn't compatible with a weapon cannot be selected. (Doesn't apply to custom gear sets) +#define USE_EXTRA_CHECKING 1 private _customGearSet = []; -private _unarmed = false; if !(params [ @@ -114,34 +115,36 @@ removeBackpackGlobal _unit; } forEach DMS_ai_default_items; -if (_class == "unarmed") then +if (_class in DMS_ai_SupportedRandomClasses) then { - _class = "assault"; - _unarmed = true; -} -else -{ - if (_class in DMS_ai_SupportedRandomClasses) then - { - _class = selectRandom (missionNamespace getVariable [format["DMS_%1_AI",_class], DMS_random_AI]); - }; + _class = selectRandom (missionNamespace getVariable [format["DMS_%1_AI",_class], DMS_random_AI]); }; // Set random DMS unit names if you don't want Arma assigned (real names) -if !(DMS_AI_UseRealNames) then +switch (DMS_AI_NamingType) do { - _unit setName format["[DMS %1 %2 %3]",toUpper _side,_class,floor(random 1000)]; + case 1: + { + _unit setName format["[DMS %1 %2 %3]",toUpper _side,_class,floor(random 1000)]; + }; + + case 2: + { + _unit setName format["%1 %2", selectRandom DMS_AI_FirstNames, selectRandom DMS_AI_LastNames]; + }; + + default {}; // Default ArmA names otherwise... +}; + + +if !(_class in DMS_ai_SupportedClasses) exitWith +{ + diag_log format ["DMS ERROR :: DMS_SpawnAISoldier called with unsupported _class: %1 | _this: %2",_class,_this]; + deleteVehicle _unit; }; if (_customGearSet isEqualTo []) then { - if !(_class in DMS_ai_SupportedClasses) exitWith - { - diag_log format ["DMS ERROR :: DMS_SpawnAISoldier called with unsupported _class: %1 | _this: %2",_class,_this]; - deleteVehicle _unit; - }; - - // Equipment (Stuff that goes in the toolbelt slots) { if (_x in ["Binocular","Rangefinder","Laserdesignator","Laserdesignator_02","Laserdesignator_03"]) then @@ -184,61 +187,104 @@ if (_customGearSet isEqualTo []) then _unit linkItem "NVGoggles"; }; - if (!_unarmed) then + + private _weapon = selectRandom (missionNamespace getVariable [format ["DMS_%1_weps",_class],DMS_assault_weps]); + [_unit, _weapon, 6 + floor(random 3)] call DMS_fnc_AddWeapon; + _unit selectWeapon _weapon; + + +#ifdef USE_EXTRA_CHECKING + // "Guaranteed" method of finding/adding weapon attachments. + if ((random 100) <= (missionNamespace getVariable [format["DMS_%1_optic_chance",_class],0])) then { - private _weapon = selectRandom (missionNamespace getVariable [format ["DMS_%1_weps",_class],DMS_assault_weps]); - [_unit, _weapon, 6 + floor(random 3)] call DMS_fnc_AddWeapon; - _unit selectWeapon _weapon; + private _optic = selectRandom + ( + (missionNamespace getVariable [format ["DMS_%1_optics",_class],DMS_assault_optics]) arrayIntersect + (getArray (configfile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "CowsSlot")) + ); - - if((random 100) <= (missionNamespace getVariable [format["DMS_%1_optic_chance",_class],0])) then + if !(isNil "_optic") then { - _unit addPrimaryWeaponItem (selectRandom (missionNamespace getVariable [format ["DMS_%1_optics",_class],DMS_assault_optics])); + _unit addPrimaryWeaponItem _optic; }; - - if (_nighttime && {(random 100) <= DMS_ai_nighttime_accessory_chance}) then - { - _unit addPrimaryWeaponItem (selectRandom ["acc_pointer_IR","acc_flashlight"]); - }; - - if((random 100) <= (missionNamespace getVariable [format["DMS_%1_bipod_chance",_class],0])) then - { - _unit addPrimaryWeaponItem (selectRandom DMS_ai_BipodList); - }; - - if((random 100) <= (missionNamespace getVariable [format["DMS_%1_suppressor_chance",_class],0])) then - { - private _suppressor = _weapon call DMS_fnc_FindSuppressor; - if (_suppressor != "") then - { - _unit addPrimaryWeaponItem _suppressor; - }; - }; - - /* - // In case spawn position is water - if (DMS_ai_enable_water_equipment && {surfaceIsWater _pos}) then - { - removeHeadgear _unit; - removeAllWeapons _unit; - _unit forceAddUniform "U_O_Wetsuit"; - _unit addVest "V_RebreatherIA"; - _unit addGoggles "G_Diving"; - [_unit, "arifle_SDAR_F", 4 + floor(random 3), "20Rnd_556x45_UW_mag"] call DMS_fnc_AddWeapon; - }; - */ - - private _pistols = missionNamespace getVariable [format ["DMS_%1_pistols",_class],[]]; - if !(_pistols isEqualTo []) then - { - private _pistol = selectRandom _pistols; - [_unit, _pistol, 2 + floor(random 2)] call DMS_fnc_AddWeapon; - }; - - // Infinite Ammo - // This will NOT work if AI unit is offloaded to client - _unit addeventhandler ["Fired", {(vehicle (_this select 0)) setvehicleammo 1;}]; }; + + if (_nighttime && {(random 100) <= DMS_ai_nighttime_accessory_chance}) then + { + private _accessory = selectRandom (getArray (configfile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "PointerSlot")); + + if !(isNil "_accessory") then + { + _unit addPrimaryWeaponItem _accessory; + }; + }; + + if ((random 100) <= (missionNamespace getVariable [format["DMS_%1_bipod_chance",_class],0])) then + { + private _bipod = selectRandom + ( + DMS_AI_BipodList arrayIntersect + (getArray (configfile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "UnderBarrelSlot")) + ); + + if !(isNil "_bipod") then + { + _unit addPrimaryWeaponItem _bipod; + }; + }; + +#else + // "Regular" method of finding/adding weapon attachments. + if ((random 100) <= (missionNamespace getVariable [format["DMS_%1_optic_chance",_class],0])) then + { + _unit addPrimaryWeaponItem (selectRandom (missionNamespace getVariable [format ["DMS_%1_optics",_class],DMS_assault_optics])); + }; + + if (_nighttime && {(random 100) <= DMS_ai_nighttime_accessory_chance}) then + { + _unit addPrimaryWeaponItem (selectRandom ["acc_pointer_IR","acc_flashlight"]); + }; + + if ((random 100) <= (missionNamespace getVariable [format["DMS_%1_bipod_chance",_class],0])) then + { + _unit addPrimaryWeaponItem (selectRandom DMS_AI_BipodList); + }; + +#endif + + + if ((random 100) <= (missionNamespace getVariable [format["DMS_%1_suppressor_chance",_class],0])) then + { + private _suppressor = _weapon call DMS_fnc_FindSuppressor; + if (_suppressor != "") then + { + _unit addPrimaryWeaponItem _suppressor; + }; + }; + + /* + // In case spawn position is water + if (DMS_ai_enable_water_equipment && {surfaceIsWater _pos}) then + { + removeHeadgear _unit; + removeAllWeapons _unit; + _unit forceAddUniform "U_O_Wetsuit"; + _unit addVest "V_RebreatherIA"; + _unit addGoggles "G_Diving"; + [_unit, "arifle_SDAR_F", 4 + floor(random 3), "20Rnd_556x45_UW_mag"] call DMS_fnc_AddWeapon; + }; + */ + + private _pistols = missionNamespace getVariable [format ["DMS_%1_pistols",_class],[]]; + if !(_pistols isEqualTo []) then + { + private _pistol = selectRandom _pistols; + [_unit, _pistol, 2 + floor(random 2)] call DMS_fnc_AddWeapon; + }; + + // Infinite Ammo. This will NOT work if AI unit is offloaded to client. + // Removed because there isn't much need for this. + // _unit addeventhandler ["Fired", {(vehicle (_this select 0)) setvehicleammo 1;}]; } else {