diff --git a/AJ_CBRN_V2/CfgFunctions.hpp b/AJ_CBRN_V2/CfgFunctions.hpp new file mode 100644 index 0000000..b2740c7 --- /dev/null +++ b/AJ_CBRN_V2/CfgFunctions.hpp @@ -0,0 +1,18 @@ +class AJDJ +{ + class CBRN + { + file = "AJ_CBRN_V2\functions"; + class zenModules{ postInit = 1; }; + class createArea{}; + class chemicalParticleLoop{}; + class chemicalDamage{}; + class getProtectionLevel{}; + class equipMask{}; + class chemicalInitClient{}; + class chemicalDetector{}; + class unconAlarm{}; + class chemicalAlarm{}; + class deleteArea{}; + }; +}; diff --git a/AJ_CBRN_V2/CfgSounds.hpp b/AJ_CBRN_V2/CfgSounds.hpp new file mode 100644 index 0000000..61f3010 --- /dev/null +++ b/AJ_CBRN_V2/CfgSounds.hpp @@ -0,0 +1,13 @@ + + class chemical_alarm + { + name = "chemical_alarm"; + sound[] = {"AJ_CBRN_V2\sounds\AJ_warning_2.ogg", 0.9, 1, 3}; //directory, volume, pitch, range + titles[]={}; + }; + class uncon_alarm + { + name = "uncon_alarm"; + sound[] = {"AJ_CBRN_V2\sounds\AJ_warning_1.ogg", 0.9, 1, 5}; //directory, volume, pitch, range + titles[]={}; + }; \ No newline at end of file diff --git a/AJ_CBRN_V2/config.hpp b/AJ_CBRN_V2/config.hpp new file mode 100644 index 0000000..b2f8843 --- /dev/null +++ b/AJ_CBRN_V2/config.hpp @@ -0,0 +1,2 @@ +#include "CfgFunctions.hpp" +#include "CfgSounds.hpp" \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_chemicalAlarm.sqf b/AJ_CBRN_V2/functions/fn_chemicalAlarm.sqf new file mode 100644 index 0000000..733970f --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_chemicalAlarm.sqf @@ -0,0 +1,12 @@ +//chemical detector audible warning of lethal levels of contamianation +//gets triggered when activating chemical trigger +//turns off when exiting the chemical trigger + +while { true } do { + + if ("ChemicalDetector_01_watch_F" in (assignedItems player)) then { + // play the sound + playSound "chemical_alarm"; + }; + sleep 5; +}; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_chemicalDamage.sqf b/AJ_CBRN_V2/functions/fn_chemicalDamage.sqf new file mode 100644 index 0000000..83b2955 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_chemicalDamage.sqf @@ -0,0 +1,39 @@ +if (!hasInterface) exitWith {}; +waitUntil { + !isNull player +}; + +params ["_trg"]; + +// start the alarm audio script (includes checking for device) +private _alarmHandle = ([] spawn AJDJ_fnc_chemicalAlarm); + +// main loop for doing hurty things when in chemical area +while { player inArea _trg } do { + // check for player protection level + private _protectionLevel = call AJDJ_fnc_getProtectionLevel; + + //if the player does not have full protection + if !(_protectionLevel == 2) then { + //if the player does not have mask protection + if (_protectionLevel == 1) then { + + sleep 5.0; + + _limbSelection = selectRandom ["body", "head", "hand_r", "hand_l", "leg_r", "leg_l"]; + [player, 0.2, _limbSelection, "vehiclecrash"] call ace_medical_fnc_addDamageToUnit; + } else { + + // deal the damage + _limbSelection = selectRandom ["body", "head", "hand_r", "hand_l", "leg_r", "leg_l"]; + [player, 0.2, _limbSelection, "vehiclecrash"] call ace_medical_fnc_addDamageToUnit; + + // deal it again for extra speedy killing + _limbSelection = selectRandom ["body", "head", "hand_r", "hand_l", "leg_r", "leg_l"]; + [player, 0.2, _limbSelection, "vehiclecrash"] call ace_medical_fnc_addDamageToUnit; + }; + }; + sleep 5; +}; + +terminate _alarmHandle; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_chemicalDetector.sqf b/AJ_CBRN_V2/functions/fn_chemicalDetector.sqf new file mode 100644 index 0000000..c2d6ef2 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_chemicalDetector.sqf @@ -0,0 +1,80 @@ +/* + Chemical Detector script by Ajdj100 + Version 1.0.0 + + Adds functionality to the chemical detector + + call this script from initPlayerLocal.sqf (example on line 16). + + + Parameters: + 0: Array of objects - the sources of contamination. (Required) + 1: Number - the radius of contamination around the source object(s) in meters. (optional, defaults to 50m) + + + initPlayerLocal.sqf Example: + chemDetector = [[Chemical1], 250] execVM "chemicalDetector.sqf"; (Creates a contamination zone 250m around the location of the Chemical1 object) +*/ + +// if not a real gamer, exit +if (!hasInterface) exitWith {}; +waitUntil { + !isNull player; +}; + +while { true } do { + // get all chemical areas + private _sources =+ (missionNamespace getVariable ["chemicalSources", []]); + + // if there are chemical sources present, and the player has a chemical detector + if (!(_sources isEqualTo []) && ("ChemicalDetector_01_watch_F" in (assignedItems player))) then { + // sort based on distance to chemical source, including radius of chemical + private _sortedSources =+ [_sources, [], { + (_x distance player) - (_x getVariable "radius") + }] call BIS_fnc_sortBy; + + // selects nearest source of chemical + private _source = (_sortedSources select 0); + + // global threat variable (for use in audio functions) + private _threat = (10 - ((player distance _source) - (_source getVariable "radius")))/3 min 9.99 max 0 toFixed 2; + + // do some stuff for the display + "ChemicalDetector" cutRsc ["RscWeaponChemicalDetector", "PLAIN", 1, false]; + + private _ui = uiNamespace getVariable "RscWeaponChemicalDetector"; + if !(isNull _ui) then { + private _obj = _ui displayCtrl 101; + _obj ctrlAnimateModel ["Threat_Level_Source", parseNumber _threat, true]; + }; + + // if the player is in a chemical zone + if ((parseNumber _threat) > 3) then { + // if the player is knocked out + if ((player getVariable "ACE_isUnconscious")) then { + //wait 20 seconds to detect uncon + sleep 20; + // loop until the player wakes up + // if !(player getVariable "ACE_isUnconscious") exitWith { + // systemChat "DEBUG unit woke up before alarm"; + // }; + waitUntil { + sleep 1; + [player] remoteExecCall ["AJDJ_fnc_unconAlarm", 0]; //THIS HAS A BUG, IT WONT TURN OFF AFTER ESCAPING THE GAS. + !(player getVariable "ACE_isUnconscious"); //THIS NEEDS TO BE SPAWNED AS A SEPERATE + }; + }; + }; + } else { //default for if there are no chemical sources present + + "ChemicalDetector" cutRsc ["RscWeaponChemicalDetector", "PLAIN", 1, false]; + + private _ui = uiNamespace getVariable "RscWeaponChemicalDetector"; + if !(isNull _ui) then { + private _obj = _ui displayCtrl 101; + _obj ctrlAnimateModel ["Threat_Level_Source", 0.00, true]; + }; + }; + + sleep 0.5; +}; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_chemicalInitClient.sqf b/AJ_CBRN_V2/functions/fn_chemicalInitClient.sqf new file mode 100644 index 0000000..e4f6a39 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_chemicalInitClient.sqf @@ -0,0 +1,17 @@ +/* +CBRN Script client side initialization Ajdj100 +Version 0.1.0 + +Adds functionality to a few things for the client +Call this script from initPlayerLocal.sqf (example on line 10). + +initPlayerLocal.sqf Example: + + call AJDJ_fnc_chemicalInitClient; +*/ + + +aceMask = ["EquipMask","Put on Gas Mask","",{call AJDJ_fnc_equipMask},{true}] call ace_interact_menu_fnc_createAction; +[player, 1, ["ACE_SelfActions", "ACE_Equipment"], aceMask] call ace_interact_menu_fnc_addActionToObject; + +call AJDJ_fnc_chemicalDetector; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_chemicalParticleLoop.sqf b/AJ_CBRN_V2/functions/fn_chemicalParticleLoop.sqf new file mode 100644 index 0000000..e562856 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_chemicalParticleLoop.sqf @@ -0,0 +1,58 @@ +/* +spawns the visible component of the chemical area + +returns the particle source for use in deletion +*/ + +if (!hasInterface) exitWith {}; + +params ["_location", "_radius"]; + +_location set [2, 0]; + +private _pSource = ("#particlesource" createVehicleLocal _location); +_pSource setParticleParams +[ + ["\A3\data_f\ParticleEffects\Universal\Universal", 16, 12, 0, 8], + "", "Billboard", 1, 10, // animationName, type, timerPeriod, lifeTime + [0,0,-1000], // position relative to referenceObject + [0, 0, 0], // velocity + 0, 0.005, 0.003925, 0.1, [5, 5], // rotation, weight, volume, rubbing, size + [[0.6, 0.6, 0.2, 0], [0.6, 0.6, 0.2, 0.7], [0.6, 0.6, 0.2, 0]], // colors + [1], // animationPhase + 0, 0, // randomDirectionPeriod, randomDirectionIntensity + "", "", // onTimer, beforeDestroy + _pSource, // referenceObject + 0, false, // angle, bounces + -1, [], // bounceOnSurface, emissiveColor + [0, 1, 0]// vectorDir - CANNOT be [0, 0, 0] +]; + +_pSource setDropInterval (0.5/_radius); +_pSource setParticleCircle [(_radius/2), [0,1,0]]; +/* + lifeTime, + position, + moveVelocity, + rotationVelocity, + size, + color, + directionPeriod, + directionIntensity, + angle, + bounceOnSurface +*/ +_pSource setParticleRandom +[ + 0, + [(_radius/2), (_radius/2), 1], + [0, 0, 0.5], + 4, + 1, + [0, 0, 0, 0], + 0, + 0 +]; + +//return the particle source +_pSource; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_createArea.sqf b/AJ_CBRN_V2/functions/fn_createArea.sqf new file mode 100644 index 0000000..e4d1321 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_createArea.sqf @@ -0,0 +1,44 @@ +/* +creates a new contamination area + +Params: +0: Position array OR Object - The source of the contamination +1: Radius - the radius of contamination around the object + +Location can be either a position array [x,y,z] or an object +[location, radius] call AJDJ_fnc_createArea; +*/ + +params ["_location", "_radius"]; + +//init temp for emitter object +private _emitterObject = _location; + +if ("OBJECT" isEqualTo typeName _location) then { + _location = getPos _location; +} else { + _emitterObject = ("Land_GarbageBarrel_02_F" createVehicle _location); +}; + +//create trigger to control area +private _trg = createTrigger ["EmptyDetector", _location, false]; +_trg setTriggerArea [_radius, _radius, 0, false, (_radius/2)]; +_trg setTriggerActivation ["ANYPLAYER", "PRESENT", true]; +_trg setTriggerStatements ["this", "[thisTrigger] spawn AJDJ_fnc_chemicalDamage", ""]; + +//start loop for particle effects +private _pSource = [_location, _radius, _emitterObject] call AJDJ_fnc_chemicalParticleLoop; + + +//bind variables +_trg setVariable ["pSource", _pSource]; //particle source +_trg setVariable ["radius", _radius]; //area of effect (for chemical detector use); + +systemChat str _emitterObject; +//store trigger in barrel for deletion +_emitterObject setVariable ["trigger", _trg]; + + +private _sources =+ (missionNamespace getVariable ["chemicalSources", []]); +_sources pushBack _trg; +missionNamespace setVariable ["chemicalSources", _sources]; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_deleteArea.sqf b/AJ_CBRN_V2/functions/fn_deleteArea.sqf new file mode 100644 index 0000000..420a32e --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_deleteArea.sqf @@ -0,0 +1,18 @@ +params["_target"]; + +if (_target isEqualTo objNull) exitWith {}; + +private _trg = (_target getVariable "trigger"); + +private _sources =+ (missionNamespace getVariable ["chemicalSources", []]); +_sources deleteAt (_sources find _trg); +missionNamespace setVariable ["chemicalSources", _sources]; + +// delete the particle emitter +deleteVehicle (_trg getVariable "pSource"); + +// set the trigger area to 0 (removes the area of effect) +// _trg setTriggerArea [0, 0, 0, false, 0]; + +deleteVehicle _trg; +deleteVehicle _target; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_equipMask.sqf b/AJ_CBRN_V2/functions/fn_equipMask.sqf new file mode 100644 index 0000000..97a64e1 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_equipMask.sqf @@ -0,0 +1,43 @@ +/* +Gasmask equipping script by Ajdj100 +Version 0.1.0 + +Script to allow players to equip gasmasks via ACE self interact if they are holding one in their inventory. + +PASTE THE FOLLOWING INTO initPlayerLocal.sqf TO INITIALIZE: + +aceMask = ["EquipMask","Put on Gas Mask","",{execVM "scripts\aceGasmask.sqf"},{true}] call ace_interact_menu_fnc_createAction; +[player, 1, ["ACE_SelfActions", "ACE_Equipment"], aceMask] call ace_interact_menu_fnc_addActionToObject; +*/ + + +//if not a player, exit +if (!hasInterface) exitwith {}; + +// init lists of masks +private _allowedGoggles = [ + "G_AirPurifyingRespirator_01_F", + "G_AirPurifyingRespirator_02_black_F", + "G_AirPurifyingRespirator_02_olive_F", + "G_AirPurifyingRespirator_02_sand_F" +]; + +private _items = items player; + +private _availableGoggles = (_items arrayIntersect _allowedGoggles); + +//if the player has a gasmask in their inventory +if !(_availableGoggles isEqualTo []) then { + + //temporarily stores faceware + private _tempGoggles = goggles player; + + //Swaps mask with current facewear + player addGoggles (_availableGoggles select 0); + player removeItem (_availableGoggles select 0); + player addItem _tempGoggles; + + hint "Added mask"; //debug message +} else { + hint "No mask in inventory" +}; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_getProtectionLevel.sqf b/AJ_CBRN_V2/functions/fn_getProtectionLevel.sqf new file mode 100644 index 0000000..dcf1fe6 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_getProtectionLevel.sqf @@ -0,0 +1,51 @@ +/* +returns protection level of local player +*/ + +// init lists of protective equiptment +private _allowedGoggles = [ + "G_AirPurifyingRespirator_01_F", + "G_AirPurifyingRespirator_02_black_F", + "G_AirPurifyingRespirator_02_olive_F", + "G_AirPurifyingRespirator_02_sand_F", + "G_RegulatorMask_F" +]; +private _allowedUniforms = [ + "U_B_CBRN_Suit_01_MTP_F", + "U_B_CBRN_Suit_01_Tropic_F", + "U_B_CBRN_Suit_01_Blue_F", + "U_B_CBRN_Suit_01_White_F", + "U_B_CBRN_Suit_01_Wdl_F", + "U_I_CBRN_Suit_01_AAF_F", + "U_I_E_CBRN_Suit_01_EAF_F" +]; +private _allowedVehicles = [ + "rhsusf_m1a2sep1wd_usarmy", + "rhsusf_m1a2sep1tuskiwd_usarmy", + "rhsusf_m1a2sep1tuskiiwd_usarmy", + "rhsusf_m1a2sep2wd_usarmy", + "RHS_M2A3_BUSKIII_wd", + "B_APC_Wheeled_01_cannon_F" +]; + +//init protection level variable +private _protectionLevel = 0; + +// if they are wearing a mask +if (goggles player in _allowedGoggles) then { + // if they are wearing the suit + if (uniform player in _allowedUniforms) then { + _protectionLevel = 2; + } else { + _protectionLevel = 1; + }; +}; + + +// if the player is in a protected vehicle +if (typeOf objectParent player in _allowedVehicles) then { + _protectionLevel = 2 +}; + +//return +_protectionLevel; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_unconAlarm.sqf b/AJ_CBRN_V2/functions/fn_unconAlarm.sqf new file mode 100644 index 0000000..14c82fb --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_unconAlarm.sqf @@ -0,0 +1,8 @@ +/* + Script to handle the audio alert for going unconsious in gas + By Ajdj100 + + version 0.1.0 +*/ +params ["_unit"]; +_unit say3D "uncon_alarm"; \ No newline at end of file diff --git a/AJ_CBRN_V2/functions/fn_zenModules.sqf b/AJ_CBRN_V2/functions/fn_zenModules.sqf new file mode 100644 index 0000000..807e325 --- /dev/null +++ b/AJ_CBRN_V2/functions/fn_zenModules.sqf @@ -0,0 +1,27 @@ +["CBRN", "Create Chemical Area", + { + params ["_position", "_attachedObj"]; + + ["Chemical Area Option", + [[ "SLIDER:RADIUS", "Radius", [0, 500, 50, 0, _position, [1,0,0,1]]]], + { + params ["_dialog", "_args"]; + _args params ["_position"]; + _dialog params ["_radius"]; + + [_position, _radius] remoteExec ["AJDJ_fnc_createArea", 0, true]; + }, + {}, + [_position, _attachedObj] + ] call zen_dialog_fnc_create; + } +] call zen_custom_modules_fnc_register; + + +["CBRN", "Delete Chemical Area", + { + params ["_position", "_attachedObj"]; + + [_attachedObj] remoteExec ["AJDJ_fnc_deleteArea", 0, true]; + } +] call zen_custom_modules_fnc_register; \ No newline at end of file diff --git a/AJ_CBRN_V2/readme.md b/AJ_CBRN_V2/readme.md new file mode 100644 index 0000000..17d80a4 --- /dev/null +++ b/AJ_CBRN_V2/readme.md @@ -0,0 +1,43 @@ +### How to add to mission: + +Drag the AJ_CBRN_V2 folder into your mission folder and add the following line into class CfgFunctions in description.ext: + + #include "AJ_CBRN_V2\CfgFunctions.hpp" + +Add the following line into CfgSounds in description.ext: + + #include "AJ_CBRN_V2\CfgSounds.hpp" + +lastly, add the following line to initPlayerLocal.sqf: + + call AJDJ_fnc_chemicalInitClient; + + +### How to use: + +to create and delete a chemical area in zeus, use the modules under the CBRN tab. + +to create a chemical area in editor or through script, use the following line: + + [location, radius] call AJDJ_fnc_createArea; + +location can be either an object, or an array of coordinates. Examples below: + + [myObject, 40] call AJDJ_fnc_createArea; + + [[6113.79,8642.26,0], 10] call AJDJ_fnc_createArea; + +to delete an area through script, use the following line: + + [object] call ["AJDJ_fnc_deleteArea", 0, true]; + + +[IMPORTANT] note that these must be executed globally to function correctly. When using the init field of an object, it will automatically run globally +If you are working in an sqf, you will have to use remoteExec + + +currently all the CBRN equiptment from the contact DLC is supported. It is possible to add your own gear to this, but it involves editing a number of +arrays across a number of sqf's. In future I will make it easier to customize what gear provides protection. + + +Please reach out to me if you have any issues or suggestions at @ajdj100 on discord. diff --git a/AJ_CBRN_V2/sounds/AJ_Warning_1.ogg b/AJ_CBRN_V2/sounds/AJ_Warning_1.ogg new file mode 100644 index 0000000..ef1c203 Binary files /dev/null and b/AJ_CBRN_V2/sounds/AJ_Warning_1.ogg differ diff --git a/AJ_CBRN_V2/sounds/AJ_Warning_2.ogg b/AJ_CBRN_V2/sounds/AJ_Warning_2.ogg new file mode 100644 index 0000000..316b7cb Binary files /dev/null and b/AJ_CBRN_V2/sounds/AJ_Warning_2.ogg differ diff --git a/description.ext b/description.ext index 1efe1da..5c7b8ab 100644 --- a/description.ext +++ b/description.ext @@ -94,6 +94,7 @@ class Params {}; class CfgFunctions { #include "framework\CfgFunctions.hpp" #include "custom_scripts.hpp" + #include "AJ_CBRN_V2\CfgFunctions.hpp" }; class CfgLeaflets {