Jump to content
Sign in to follow this  
wvorster

Teargas / Toxic Gas Script Help

Recommended Posts

Hi Guys,

This is working great , however we are having a small issue - the toxic gas grenade doesn't affect anyone that's elevated. e.g. get into a bunker or a house that's not flat on the ground , throw grenade and it does nothing ..

Step out onto the ground and throw the toxic grenade and it works perfect.

What can I change or try to make sure this works at the correct elevation ?

//	@file Name: toxic_gas.sqf
//	@file Author: Mokey
//	@file Description: Toxic Gas addon for A3W
//	@web: http://www.fractured-gaming.com
//	@Special Thanks to Pitoucc


_gasMask = ["H_CrewHelmetHeli_B","H_CrewHelmetHeli_O","H_CrewHelmetHeli_I"]; // define the gasmasks here 

setNoGasStatus={
   "dynamicBlur" ppEffectEnable true;                  // enables ppeffect
   "dynamicBlur" ppEffectAdjust [0];                   // enables normal vision
   "dynamicBlur" ppEffectCommit 10;                    // time it takes to go back to normal vision
   resetCamShake;                                      // resets the shake
   20 fadeSound 1;                                     // fades the sound back to normal	
};

setGasStatus = { 
   "dynamicBlur" ppEffectEnable true;              	// enables ppeffect
   "dynamicBlur" ppEffectAdjust [15];             	  	// intensity of blur
"dynamicBlur" ppEffectCommit 5;                 	// time till vision is fully blurred
enableCamShake true;                             	// enables camera shake
addCamShake [10, 45, 10];                        	// sets shakevalues
//	player setFatigue 0;                            	// sets the fatigue to 100%
5 fadeSound 0.1;                                 	// fades the sound to 10% in 5 seconds
};

gasDamage = {
   player setDamage (damage player + 0.12);     		//damage per tick
   sleep 2.5;                                  		    // Timer damage is assigned "seconds"
};

While{true} do{
	call setNoGasStatus;
waituntil{
	((nearestObject [getPosATL player, "SmokeShell"]) distance player < 10)       // detects if player is within grenade radius
	&&
	(getPosATL (nearestObject [getPosATL player, "SmokeShell"]) select 2 < 0.5)
};

if !(headgear player in _gasMask) then 
	 {
		call setGasStatus;
		call gasDamage;
	 }
	 else
	 {};
};

Share this post


Link to post
Share on other sites

I believe this line of code is the culprit. It is getting the height of the player and sees if they are less than 0.5 m above the ground. So depending on how high the smoke effect is, you can edit that number to say 1 or 1.5?

(getPosATL (nearestObject [getPosATL player, "SmokeShell"]) select 2 < 0.5)

Share this post


Link to post
Share on other sites

This line

(getPosATL (nearestObject [getPosATL player, "SmokeShell"]) select 2 < 0.5)

is detecting whether the smoke shells height above the terrain is smaller than 0.5 meters. If you are throwing the shell in a building then it may well come to rest above 0.5 meters from the terrain.

A better solution maybe to check that the grenade is at rest rather than its height.

waituntil{
	_smokeShell = nearestObject [getPosATL player, "SmokeShell"];
	_smokeShell distance player < 10
	&&
	velocity _smokeShell isEqualTo [ 0, 0, 0 ]
};

if the smoke shells velocity is at [0,0,0] then you know the shell is at rest and spewing toxic gas from its position.

Also due to the way you have it set up once a smoke is near and the player has no mask you call your distortion and damage effects but then loop straight back around to then cancel the effects, which if the smoke is still near starts all over again. Its not a very efficient loop.

Share this post


Link to post
Share on other sites
This line

(getPosATL (nearestObject [getPosATL player, "SmokeShell"]) select 2 < 0.5)

is detecting whether the smoke shells height above the terrain is smaller than 0.5 meters. If you are throwing the shell in a building then it may well come to rest above 0.5 meters from the terrain.

A better solution maybe to check that the grenade is at rest rather than its height.

waituntil{
	_smokeShell = nearestObject [getPosATL player, "SmokeShell"];
	_smokeShell distance player < 10
	&&
	velocity _smokeShell isEqualTo [ 0, 0, 0 ]
};

if the smoke shells velocity is at [0,0,0] then you know the shell is at rest and spewing toxic gas from its position.

Also due to the way you have it set up once a smoke is near and the player has no mask you call your distortion and damage effects but then loop straight back around to then cancel the effects, which if the smoke is still near starts all over again. Its not a very efficient loop.

On occasion the velocity calcuations go nuts with PhysX bugging out (object typically starts 'warping' into the floor then pops back up) thus its velocity might not ever equal zero...

Share this post


Link to post
Share on other sites
On occasion the velocity calcuations go nuts with PhysX bugging out (object typically starts 'warping' into the floor then pops back up) thus its velocity might not ever equal zero...
true.

How about checking the magnitude of the velocity instead? Just need to find a decent threshold for it to be below!

Something like....

//Gas masks
gasMasks = ["H_CrewHelmetHeli_B","H_CrewHelmetHeli_O","H_CrewHelmetHeli_I"];

//Are we near a smoke shell
fnc_smokeNear = {

//Are we not wearing a gas mask
if ( !( headgear player in gasMasks ) ) then {

	//Get nearest smoke shell
	_smokeShell = nearestObject [ getPosATL player, "SmokeShell" ];

	//If there is a smoke shell
	if !( isNull _smokeShell ) then {

		//Is it at rest AND within 10 meters of the player ( will need to experiment with magnitude threshold )
		vectorMagnitudeSqr velocity _smokeShell <= 0.5 && { _smokeShell distance player < 10 }

	//There is no smoke shell
	}else{

		//Return false
		false
	};

//If we are wearing a gas mask
}else{

	//Return false as it does not matter if a smoke shell is near
	false
};

};

//We are in smoke
fnc_inSmoke = {

//Set a variable on the player so we know we are in smoke
player setVariable [ "inSmoke", true ];

//Do effect
"dynamicBlur" ppEffectEnable true;
   "dynamicBlur" ppEffectAdjust [15];
"dynamicBlur" ppEffectCommit 5;
enableCamShake true;
addCamShake [10, 45, 10];
5 fadeSound 0.1;

//While were in smoke
while { [] call fnc_smokeNear } do {

	//Damage the player
	player setDamage (damage player + 0.12);
   	sleep 2.5;
};

//We are no longer in smoke
[] call fnc_smokeClear;

};

//We are not in smoke
fnc_smokeClear = {

//Reset the player variable
player setVariable [ "inSmoke", false ];

//Clear effects
"dynamicBlur" ppEffectEnable true;
   "dynamicBlur" ppEffectAdjust [0];
   "dynamicBlur" ppEffectCommit 10;
   resetCamShake;
   20 fadeSound 1;

};

//Check each frame if we are near smoke
//If we are not already flagged as in smoke AND near a smoke shell
	//Start gas effects
smokeNearSEHID = [ "smokeNear", "onEachFrame", {

if ( !( player getVariable [ "inSmoke", false ] ) && { [] call fnc_smokeNear } ) then {
	_inSmokeThread = [] spawn fnc_inSmoke;
};

}] call BIS_fnc_addStackedEventHandler;

Edited by Larrow
added extra clause for null smokeShell

Share this post


Link to post
Share on other sites

Superb help guys !! the check for velocity is a good start ! I will look at the magnitude as well !! Thank you so much !!

Just a question - this will not affect loot that has spawned into buildings and dropped on the floor I assume - I'd need to test this.

Share this post


Link to post
Share on other sites

Sorry to dig up old thread, but this is relevant to this post.
As usual Larrow's code is solid.
I've Taken Larrow's modified code and enhanced upon it adding 3d choking sounds and functionality for server to apply damage to AI from player's gas grenade.
Also added list of qualified gas grenades in addition to lists of gas masks.
Tested working with Vanilla. Untested, but should also work with Hidden Identity Pack mod gas masks.
In my implementation and test on dedi. these script block parts are divided up into multiple scripts ex; for sake of performance I've call compiled global variables and functions in other scripts and divided per locality.
For simplicity all together as one script would look like this.

// Global Variables //

Choke_Sounds = [
    "A3\Sounds_f\characters\human-sfx\Person0\P0_choke_02.wss",
    "A3\Sounds_f\characters\human-sfx\Person0\P0_choke_03.wss",
    "A3\Sounds_f\characters\human-sfx\Person0\P0_choke_04.wss",
    "A3\Sounds_f\characters\human-sfx\Person1\P1_choke_04.wss",
    "A3\Sounds_f\characters\human-sfx\Person2\P2_choke_04.wss",
    "A3\Sounds_f\characters\human-sfx\Person2\P2_choke_05.wss",
    "A3\Sounds_f\characters\human-sfx\Person3\P3_choke_02.wss",
    "A3\Sounds_f\characters\human-sfx\P06\Soundbreathinjured_Max_2.wss",
    "A3\Sounds_f\characters\human-sfx\P05\Soundbreathinjured_Max_5.wss"
];

// All Headgear to use as Gasmask
INS_gasMaskH = ["H_CrewHelmetHeli_B","H_CrewHelmetHeli_O","H_CrewHelmetHeli_I","H_PilotHelmetFighter_B","H_PilotHelmetFighter_O","H_PilotHelmetFighter_I"];
// All Goggles to use as Gasmask
INS_gasMaskG = ["Mask_M50","Mask_M40","Mask_M40_OD"];//Hidden Identity Pack V.3
// All Grenades to use as Poisonous Gas Grenades
INS_Gas_Grenades = ["SmokeShellYellow","G_40mm_SmokeYellow"];//fired ammo


// Server //
if (isServer) then
{
    // Server functions //
    
    editorAI_GasMask = {
        private "_ai";
        _ai = allUnits;
        {if (isPlayer _x) then {_ai =_ai - [_x];};} count _ai;
        {
            removeHeadgear _x;
            if (side _x isEqualTo resistance) then {_x addHeadgear "H_CrewHelmetHeli_I";};
            if (side _x isEqualTo east) then {_x addHeadgear "H_CrewHelmetHeli_O";};
            If (side _x isEqualTo west) then {_x addHeadgear "H_CrewHelmetHeli_B";};
        } count _ai;
    };
    
    GAS_smoke_AIdamage = {
        //Apply damage to AIs not wearing gas mask and play choking sounds in 3d
        private ["_currPos","_aiArray","_maxtype","_sound","_odd"];
        _currPos = _this;
        _maxtype = (count Choke_Sounds);
        _odd = 1;
        // loop time based on approximate life time of smoke grenade (22 seconds)
        for '_i' from 1 to 10 do {
            _aiArray = _currPos nearEntities [["CAManBase"],15];
            {if ((isPlayer _x) || (headgear _x in INS_gasMaskH) || (goggles _x in INS_gasMaskG)) then {_aiArray = _aiArray - [_x];};} count _aiArray;
            {
                if (_aiArray isEqualTo []) exitWith {};
                if (_odd isEqualTo 1) then {
                    _sound = Choke_Sounds select (floor random _maxtype);
                    playsound3d [_sound, _x, false, getPosasl _x, 14,1,40];
                    _odd = 2;
                }else{
                    _odd = 1;
                };
                uiSleep (random 0.21);
                _x setDamage (damage _x + 0.13);
            } count _aiArray;
            uiSleep 2.1;
        };
        ToxicGasLoc = [];
    };

    // Give Gas masks to all existing AI
    [] spawn editorAI_GasMask;

    // Server eventHandler fires when player's fired gas grenade comes to rest. Variable "ToxicGasLoc" is position of rested grenade.
    ToxicGasLoc = [];publicVariable "ToxicGasLoc";
    "ToxicGasLoc" addPublicVariableEventHandler {(_this select 1) spawn GAS_smoke_AIdamage};

// Client //
if (!isDedicated && hasInterface) then
{
    // Client functions //

    GAS_smokeNear = {
        //Are we near a smoke shell
        //Are we not wearing a gas mask
        if ((headgear player in INS_gasMaskH) || {(goggles player in INS_gasMaskG)}) then {
            //We are wearing a gas mask. Return false as it does not matter if a smoke shell is near
            false
        } else {
            _smokeShell = player nearObjects ["GrenadeHand", 30];
            {
                if !(typeOf _x in INS_Gas_Grenades) then {_smokeShell = _smokeShell - [_x];};
            } count _smokeShell;

            //If there is a smoke shell
            if !(isNull (_smokeShell select 0)) then {
                //Is it at rest AND within 10 meters of the player ( will need to experiment with magnitude threshold )
                vectorMagnitudeSqr velocity (_smokeShell select 0) <= 0.5 && { (_smokeShell select 0) distance player < 10 }
            } else {
                //There is no smoke shell so Return false
                false
            };
        };
    };

    GAS_inSmoke = {
        // We are in smoke
        player setVariable ["inSmoke",true];

        private ["_maxtype","_sound"];
        _maxtype = (count Choke_Sounds);

        //Do effects
        "dynamicBlur" ppEffectEnable true;
        "dynamicBlur" ppEffectAdjust [12];
        "dynamicBlur" ppEffectCommit 5;
        enableCamShake true;
        addCamShake [10, 45, 10];
        5 fadeSound 0.1;

        //While were in smoke
        while { alive player && not captive player && [] call GAS_smokeNear } do {
            _sound = Choke_Sounds select (floor random _maxtype);
            playsound3d [_sound, player, false, getPosasl player, 10,1,30];
            player setDamage (damage player + 0.12);
            //if(round(random(1)) isEqualTo 0) then {hint "You Should Wear a Gas Mask";};
            uiSleep 2.8123;
        };

        //We are no longer in smoke
        [] call GAS_smokeClear;
    };

    GAS_smokeClear = {
        player setVariable ["inSmoke",false];

        //Clear effects
        "dynamicBlur" ppEffectEnable true;
        "dynamicBlur" ppEffectAdjust [0];
        "dynamicBlur" ppEffectCommit 10;
        resetCamShake;
        20 fadeSound 1;
    };

    waitUntil {!isNull player && player == player};

    // player eventhandlers //

    // debug
    //player addEventHandler ["Fired", {hint format ["Fired ammo object: %1", (_this select 4)]; diag_log text format ["Fired ammo object: %1", (_this select 4)];}];};

    // broadcast to server position of newly fired gas grenade once resting
    player addEventHandler ["Fired", {
        if ((_this select 4) in INS_Gas_Grenades) then {
            (_this select 6) spawn {
                private "_grenadePos";
                waitUntil { vectorMagnitudeSqr velocity _this <= 0.5};
                _grenadePos = getPosATL _this;
                sleep 0.2;
                ToxicGasLoc = _grenadePos;
                publicVariableServer "ToxicGasLoc";
            };
        };
    }];

//Check each frame if we are near smoke
    //If we are not already flagged as in smoke AND near a smoke shell
        //Start gas effects
    smokeNearSEHID = [ "smokeNear", "onEachFrame", {
        if (!(player getVariable ["inSmoke",false]) && { [] call GAS_smokeNear }) then {
            _inSmokeThread = [] spawn GAS_inSmoke;
        };
    }] call BIS_fnc_addStackedEventHandler;
};
Edited by jigsor

Share this post


Link to post
Share on other sites

Nevermind, solution found.

All working as intended now.

Corrections made to code above.

Share this post


Link to post
Share on other sites

Hmm, code is working fine and does not give errors while playing, but when playing on dedi, after playing some and then going back to lobby, while in lobby I get this error:

false
}else{
_smokeShell = player nearObjects ["GrenadeHand", 30];
{
if !(>
20:56:23   Error position: <nearObjects ["GrenadeHand", 30];
{
if !(>
20:56:23   Error 0 elements provided, 3 expected
20:56:23 Bad conversion: array

I suspect the oneachframe is still running and trying to reference player object/player position even though it does not exist at that point.
Really just nit picking here I guess because all is working, but I just don't like to see script errors ever. I don't have much scripting experience with oneachframe stacked eventhandler.
Is this behaviour typical or is there a way to fix without adding much more overhead?

 

Script above reuploaded. I forgot to add some changes. Just tested again Issue from this post still remains.

Edited by jigsor

Share this post


Link to post
Share on other sites

Looks like it is an acknowledged problem of stacked event handlers that need to run for duration of mission will not be removed by engine if player goes back to lobby and rejoins. This is a major design flaw. We need your help folks. Please up vote fix  here.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×