diff --git a/@ExileServer/addons/a3_dms/config.cpp b/@ExileServer/addons/a3_dms/config.cpp index 006e937..ce5050f 100644 --- a/@ExileServer/addons/a3_dms/config.cpp +++ b/@ExileServer/addons/a3_dms/config.cpp @@ -52,6 +52,7 @@ class CfgFunctions class SetAILocality {}; class SetGroupBehavior {}; class SpawnAIGroup {}; + class SpawnAIVehicle {}; class SpawnAISoldier {}; class SpawnAIStatic {}; class SpawnCrate {}; diff --git a/@ExileServer/addons/a3_dms/config.sqf b/@ExileServer/addons/a3_dms/config.sqf index b7a7bd4..d2af159 100644 --- a/@ExileServer/addons/a3_dms/config.sqf +++ b/@ExileServer/addons/a3_dms/config.sqf @@ -35,6 +35,7 @@ DMS_DEBUG = false; DMS_CompletedMissionCleanup = true; // Cleanup mission-spawned buildings and AI bodies after some time DMS_CompletedMissionCleanupTime = 3600; // Minimum time until mission-spawned buildings and AI are cleaned up DMS_CleanUp_PlayerNearLimit = 20; // Cleanup of an object is aborted if a player is this many meters close to the object + DMS_AIVehCleanUpTime = 900; // Time until a destroyed AI vehicle is cleaned up. DMS_MissionTimeoutReset = true; // Enable mission timeout timer reset if a player is close DMS_MissionTimeoutResetRange = 1000; // If a player is this close to a mission then it won't time-out @@ -44,6 +45,8 @@ DMS_DEBUG = false; DMS_MissionNearBlacklist = 4000; // Missions won't spawn in a position this many meters close to another mission DMS_WaterNearBlacklist = 750; // Missions won't spawn in a position this many meters close to water + DMS_MinWaterDepth = 20; // Minimum depth of water that an underwater mission can spawn at. + DMS_SpawnBoxSmoke = true; // Spawn a smoke grenade on mission box upon misson completion during daytime DMS_SpawnBoxIRGrenade = true; // Spawn an IR grenade on mission box upon misson completion during nighttime @@ -56,7 +59,7 @@ DMS_DEBUG = false; "standardHintRequest" //"systemChatRequest" ]; - DMS_dynamicText_Size = 0.65; // Dynamic Text size for "dynamicTextRequest" notification type. + DMS_dynamicText_Size = 0.65; // Dynamic Text size for "dynamicTextRequest" notification type. DMS_dynamicText_Color = "#FFCC00"; // Dynamic Text color for "dynamicTextRequest" notification type. DMS_MissionTypes = [ // List of missions with spawn chances. If they add up to 100%, they represent the percentage chance each one will spawn @@ -88,8 +91,10 @@ DMS_DEBUG = false; DMS_Bandit_Soldier_MoneyGain = 50; // The amount of Poptabs gained for killing a bandit soldier DMS_Bandit_Soldier_RepGain = 10; // The amount of Respect gained for killing a bandit soldier - DMS_Bandit_Static_MoneyGain = 100; // The amount of Poptabs gained for killing a bandit static gunner - DMS_Bandit_Static_RepGain = 25; // The amount of Respect gained for killing a bandit static gunner + DMS_Bandit_Static_MoneyGain = 75; // The amount of Poptabs gained for killing a bandit static gunner + DMS_Bandit_Static_RepGain = 15; // The amount of Respect gained for killing a bandit static gunner + DMS_Bandit_Vehicle_MoneyGain = 100; // The amount of Poptabs gained for killing a bandit vehicle crew member + DMS_Bandit_Vehicle_RepGain = 25; // The amount of Respect gained for killing a bandit vehicle crew member DMS_banditSide = EAST; // The side (team) that AI Bandits will spawn on DMS_clear_AI_body = false; // Clear AI body as soon as they die @@ -576,7 +581,7 @@ if(DMS_DEBUG) then { DMS_TimeBetweenMissions = [30,60]; DMS_MissionTimeOut = [60,90]; - //DMS_MissionTypes = [["testmission",1]]; + DMS_MissionTypes = [["testmission",1]]; //DMS_MissionTypes = [["mercbase",1]]; diag_log format ["DMS_DEBUG CONFIG :: Overriding DMS_TimeBetweenMissions (%1) and DMS_MissionTimeOut (%2)",DMS_TimeBetweenMissions,DMS_MissionTimeOut]; }; diff --git a/@ExileServer/addons/a3_dms/missions/testmission.sqf b/@ExileServer/addons/a3_dms/missions/testmission.sqf index ea3aa88..50f0f9c 100644 --- a/@ExileServer/addons/a3_dms/missions/testmission.sqf +++ b/@ExileServer/addons/a3_dms/missions/testmission.sqf @@ -65,6 +65,17 @@ _crate_loot_values = 3 // Backpacks ]; +[ + [ + [_pos,100,random 360] call DMS_fnc_SelectOffsetPos, + _pos + ], + _group, + "assault", + _difficulty, + _side +] call DMS_fnc_SpawnAIVehicle; + // Define mission-spawned AI Units _missionAIUnits = diff --git a/@ExileServer/addons/a3_dms/scripts/fn_FindSafePos.sqf b/@ExileServer/addons/a3_dms/scripts/fn_FindSafePos.sqf index 2a56437..f6cf442 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_FindSafePos.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_FindSafePos.sqf @@ -40,32 +40,32 @@ while{!_validspot} do try { // Check for nearby water - if ([_pos,DMS_WaterNearBlacklist] call DMS_fnc_isNearWater) then + if ((DMS_WaterNearBlacklist>0) && {[_pos,DMS_WaterNearBlacklist] call DMS_fnc_isNearWater}) then { throw ("water"); }; // Check for nearby players - if ([_pos,DMS_PlayerNearBlacklist] call DMS_fnc_IsPlayerNearby) then + if ((DMS_PlayerNearBlacklist>0) && {[_pos,DMS_PlayerNearBlacklist] call DMS_fnc_IsPlayerNearby}) then { throw ("players"); }; { // Check for nearby spawn points - if (((markertype _x) == "ExileSpawnZone") && {((getMarkerPos _x) distance2D _pos)<=DMS_SpawnZoneNearBlacklist}) then + if ((DMS_SpawnZoneNearBlacklist>0) && {((markertype _x) == "ExileSpawnZone") && {((getMarkerPos _x) distance2D _pos)<=DMS_SpawnZoneNearBlacklist}}) then { throw ("a spawn zone"); }; // Check for nearby trader zones - if (((markertype _x) == "ExileTraderZone") && {((getMarkerPos _x) distance2D _pos)<=DMS_TraderZoneNearBlacklist}) then + if ((DMS_TraderZoneNearBlacklist>0) && {((markertype _x) == "ExileTraderZone") && {((getMarkerPos _x) distance2D _pos)<=DMS_TraderZoneNearBlacklist}}) then { throw ("a trader zone"); }; // Check for nearby missions - if (((_x find "DMS_MissionMarkerDot")>-1) && {((getMarkerPos _x) distance2D _pos)<=DMS_MissionNearBlacklist}) then + if ((DMS_MissionNearBlacklist>0) && {((_x find "DMS_MissionMarkerDot")>-1) && {((getMarkerPos _x) distance2D _pos)<=DMS_MissionNearBlacklist}}) then { throw ("another mission"); }; diff --git a/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf b/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf index 3951f93..a5e3473 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_OnKilled.sqf @@ -15,7 +15,7 @@ */ -private ["_unit", "_player", "_side", "_type", "_launcher", "_playerObj", "_rockets", "_grpUnits", "_veh", "_moneyGain", "_repGain", "_money", "_respect"]; +private ["_unit", "_player", "_side", "_type", "_launcher", "_playerObj", "_rockets", "_grpUnits", "_av", "_memCount", "_gunner", "_driver", "_veh", "_moneyGain", "_repGain", "_money", "_respect"]; if (DMS_DEBUG) then @@ -78,8 +78,48 @@ if (((count (units group _unit)) > 1) && {(leader group _unit) == _unit}) then (group _unit) selectLeader (_grpUnits call BIS_fnc_selectRandom); }; +_av = _unit getVariable ["DMS_AssignedVeh",objNull]; +if (!isNull _av) then +{ + // Determine whether or not the vehicle has any active crew remaining. + _memCount = {alive _x} count (crew _av); + // Destroy the vehicle and add it to cleanup if there are no active crew members of the vehicle. + if (_memCount<1) then + { + _av setDamage 1; + DMS_CleanUpList pushBack [_av,diag_tickTime,DMS_AIVehCleanUpTime]; + + + if (DMS_DEBUG) then + { + diag_log format["DMS_DEBUG OnKilled :: Destroying used AI vehicle %1 and adding to cleanup.",typeOf _av]; + }; + } + else + { + // Only check for this stuff for ground vehicles that have guns... + if (_av isKindOf "LandVehicle" && {(count (weapons _av))>1}) then + { + _gunner = gunner _av; + _driver = driver _av; + + // If the gunner is dead but the driver is alive, then the driver becomes the gunner. + // Helps with troll AI vehicles driving around aimlessly after the gunner is shot off. More realistic imo + // Yes, I know, so many nested conditions. eraser is a scrublord. blah blah blah. Deal with it + if (((isNull _gunner) || {!(alive _gunner)}) && {!(isNull _driver) && {alive _driver}}) then + { + _driver moveInGunner _av; + if (DMS_DEBUG) then + { + diag_log format["DMS_DEBUG OnKilled :: Switching driver of AI Vehicle (%1) to gunner.",typeOf _av]; + }; + }; + }; + }; +}; + if (isPlayer _player) then { @@ -122,7 +162,8 @@ if (isPlayer _player) then { _unit call _removeAll; }; - };}; + }; +}; if ((!isNull _playerObj) && {((getPlayerUID _playerObj) != "") && {_playerObj isKindOf "Exile_Unit_Player"}}) then diff --git a/@ExileServer/addons/a3_dms/scripts/fn_RemoveMarkers.sqf b/@ExileServer/addons/a3_dms/scripts/fn_RemoveMarkers.sqf index b787c74..5b9c902 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_RemoveMarkers.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_RemoveMarkers.sqf @@ -35,7 +35,7 @@ if (_status == "win") then }; _markerDot setMarkerText ("COMPLETED: "+markerText _markerDot); _markerDot setMarkerColor DMS_MissionMarkerWinDotColor; - [DMS_MissionMarkerWinDotTime, {deleteMarker _this;}, _markerDot, false] call ExileServer_system_thread_addTask; + _markerDot spawn {sleep DMS_MissionMarkerWinDotTime;deleteMarker _this;}; if (DMS_DEBUG) then { diag_log format ["DMS_DEBUG RemoveMarkers :: %1 Marker will be removed in %2 seconds!",_markerDot,DMS_MissionMarkerWinDotTime]; @@ -49,7 +49,7 @@ else }; _markerDot setMarkerText ("FAILED: "+markerText _markerDot); _markerDot setMarkerColor DMS_MissionMarkerLoseDotColor; - [DMS_MissionMarkerLoseDotTime, {deleteMarker _this;}, _markerDot, false] call ExileServer_system_thread_addTask; + _markerDot spawn {sleep DMS_MissionMarkerLoseDotTime;deleteMarker _this;}; if (DMS_DEBUG) then { diag_log format ["DMS_DEBUG RemoveMarkers :: %1 Marker will be removed in %2 seconds!",_markerDot,DMS_MissionMarkerLoseDotTime]; diff --git a/@ExileServer/addons/a3_dms/scripts/fn_SetGroupBehavior.sqf b/@ExileServer/addons/a3_dms/scripts/fn_SetGroupBehavior.sqf index 3a7b7a2..cc08b03 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_SetGroupBehavior.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_SetGroupBehavior.sqf @@ -11,7 +11,7 @@ */ -private ["_OK", "_group", "_pos", "_difficulty", "_radius", "_npos", "_i", "_wp"]; +private ["_OK", "_group", "_behavior", "_pos", "_difficulty", "_radius", "_npos", "_i", "_wp"]; _OK = params [ @@ -25,8 +25,17 @@ if (!_OK) then diag_log format ["DMS ERROR :: Calling DMS_SetGroupBehavior with invalid params: %1",_this]; }; + +_behavior = "COMBAT"; + +// Mostly for DMS_fnc_SpawnAIVehicle, since setting behavior to COMBAT makes the driving suck... +if (((count _this)>3)) then +{ + _behavior = _this select 3; +}; + _group setCombatMode "RED"; -_group setBehaviour "COMBAT"; +_group setBehaviour _behavior; if(_difficulty == "random") then @@ -36,7 +45,8 @@ if(_difficulty == "random") then _radius = missionNamespace getVariable [format["DMS_AI_WP_Radius_%1",_difficulty],40]; -for "_i" from 0 to 359 step 45 do { +for "_i" from 0 to 359 step 45 do +{ _npos = [(_pos select 0) + (sin(_i)*_radius), (_pos select 1) + (cos(_i)*_radius)]; _wp = _group addWaypoint [_npos,(_radius/5)]; _wp setWaypointType "MOVE"; diff --git a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIStatic.sqf b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIStatic.sqf index 276203e..896e7ad 100644 --- a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIStatic.sqf +++ b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIStatic.sqf @@ -14,7 +14,7 @@ _group, // Group to which the AI unit(s) belongs to _class, // Class: "random","assault","MG","sniper" or "unarmed" _difficulty, // Difficulty: "random","static","hardcore","difficult","moderate", or "easy" - _side // "bandit","hero", etc. + _side, // "bandit","hero", etc. _MGClass // !OPTIONAL) String: classname of the MG. Use "random" to select a random one from DMS_static_weapons ] call DMS_fnc_SpawnAIStatic; @@ -62,8 +62,9 @@ _guns = []; _unit = [_group,_pos,_class,_difficulty,_side,"Static"] call DMS_fnc_SpawnAISoldier; - _unit moveingunner _gun; + _unit moveInGunner _gun; reload _unit; + _unit setVariable ["DMS_AssignedVeh",_gun]; if (DMS_DEBUG) then { diff --git a/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIVehicle.sqf b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIVehicle.sqf new file mode 100644 index 0000000..cc61934 --- /dev/null +++ b/@ExileServer/addons/a3_dms/scripts/fn_SpawnAIVehicle.sqf @@ -0,0 +1,83 @@ +/* + DMS_fnc_SpawnAIVehicle + Created by eraser1 + + Usage: + [ + [ + _spawnPos, // The position at which the AI vehicle will spawn + _gotoPos // (OPTIONAL) The position to which the AI vehicle will drive to. If it isn't defined, _spawnPos is used + ], + _group, // Group to which the AI units belongs to + _class, // Class: "random","assault","MG","sniper" or "unarmed" + _difficulty, // Difficulty: "random","static","hardcore","difficult","moderate", or "easy" + _side, // "bandit","hero", etc. + _vehClass // (OPTIONAL) String: classname of the Vehicle. Use "random" to select a random one from DMS_ArmedVehicles + ] call DMS_fnc_SpawnAIVehicle; + + Returns the spawned vehicle. +*/ + +private ["_OK", "_positions", "_veh", "_spawnPos", "_gotoPos", "_vehClass", "_driver", "_gunner", "_group", "_class", "_difficulty", "_side"]; + + +_OK = params +[ + ["_positions",[],[[]],[2]], + ["_group",grpNull,[grpNull]], + ["_class","random",[""]], + ["_difficulty","static",[""]], + ["_side","bandit",[""]] +]; +if (!_OK) exitWith +{ + diag_log format ["DMS ERROR :: Calling DMS_fnc_SpawnAIVehicle with invalid parameters: %1",_this]; +}; + + +// Using another params-exitwith structure just for _spawnPos because it's pretty important... +_OK = _positions params +[ + ["_spawnPos",[],[[]],[2,3]] +]; +if (!_OK) exitWith +{ + diag_log format ["DMS ERROR :: Calling DMS_fnc_SpawnAIVehicle with invalid _positions parameters: %1",_positions]; +}; + +// Simply use _spawnPos if _gotoPos isn't defined. Yes, you might get "param"/"params" RPT spam. Deal with it ;) +_gotoPos = _positions param [1,_spawnPos,[[]],[2,3]]; + + +_vehClass = param [5,"random",[""]]; + +if (_vehClass == "random") then +{ + _vehClass = DMS_ArmedVehicles call BIS_fnc_selectRandom; +}; + + +_veh = createVehicle [_vehClass, _spawnPos, [], 0, "NONE"]; +_veh setDir (random 360); +_veh setPosATL _spawnPos; +_veh addEventHandler ["GetOut",{(_this select 0) setDamage 1;}]; +_veh lock 2; + + +_driver = [_group,_spawnPos,_class,_difficulty,_side,"Vehicle"] call DMS_fnc_SpawnAISoldier; +_gunner = [_group,_spawnPos,_class,_difficulty,_side,"Vehicle"] call DMS_fnc_SpawnAISoldier; + +_driver moveInDriver _veh; +_gunner moveInGunner _veh; + +_driver setVariable ["DMS_AssignedVeh",_veh]; +_gunner setVariable ["DMS_AssignedVeh",_veh]; + +[_group,_gotoPos,_difficulty,"AWARE"] call DMS_fnc_SetGroupBehavior; + +if (DMS_DEBUG) then +{ + diag_log format ["DMS_DEBUG SpawnAIVehicle :: Created a %1 armed vehicle (%2) at %3 with %4 difficulty to group %5",_side,_vehClass,_spawnPos,_difficulty,_group]; +}; + +_veh \ No newline at end of file diff --git a/README.md b/README.md index 25cfc34..047e180 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,16 @@ if (!hasInterface && !isServer) then ## Changelog: +#### September 14, 2015 (11:00 PM CST-America): +* NEW CONFIG VALUES: DMS_AIVehCleanUpTime, DMS_MinWaterDepth, DMS_Bandit_Vehicle_MoneyGain, DMS_Bandit_Vehicle_RepGain. +* Changed default value of DMS_Bandit_Static_MoneyGain to 75, DMS_Bandit_Static_RepGain to 15. +* NEW FUNCTION: DMS_fnc_SpawnAIVehicle. +* You can now spawn AI in vehicles. +* Improved cleanup method for AI in static guns. +* Working on improving OnKilled EH for AI in vehicles. +* FindSafePos should no longer check for parameters if the corresponding blacklist radius is set to 0. + + #### September 13, 2015 (11:45 PM CST-America): * Updated parameter type for "DMS_dynamicText_Size", as well as commented out description for "dynamicTextRequest". Will try to work on functionality for it soon. * Improved cleaning of AI units. There may still be issues, specifically with launchers. If there are, please let me know, and test it. The more info I have, the quicker it can be fixed :)