Jump to content
fn_Quiksilver

[Release] Visible Weapon Lasers (proof of concept)

Recommended Posts

Hi lads, had an interesting query over on the arma discord, thought I'd post the result for other modders:

 

5nHESLB.jpg

 

 

// QS Visible Lasers


// Custom laser point offsets
QS_laserOffset_array = [
								// element format:   ['arifle_mx_f',[0,0.25,0]],
								// element format:   ['srifle_LRR_f',[0,0.25,0]],
								// element format:   ['arifle_mx_f',[0,0.25,0]],
								// ETC


];


QS_laser_beamColor = [1000, 0, 0];
QS_laser_dotColor = [1000, 0, 0];
QS_laser_dotSize = 0.25;
QS_laser_beamThickness = 0.25;
QS_laser_beamMaxLength = 100;
QS_laser_forceIR = FALSE;

QS_fnc_updatePlayerWeaponObject = {
	if ((player getVariable ['QS_player_currentWeapon','']) isNotEqualTo '') then {
		player setVariable ['QS_player_currentWeapon','',FALSE];
	};
	if (!isNull (player getVariable ['QS_player_weaponObject',objNull])) then {
		player setVariable ['QS_player_weaponObject',objNull,FALSE];
	};
	if ((player getVariable ['QS_player_weaponMuzzlePos','']) isNotEqualTo '') then {
		player setVariable ['QS_player_weaponMuzzlePos','',FALSE];
	};
	if ((player getVariable ['QS_player_currentProxy','']) isNotEqualTo '') then {
		player setVariable ['QS_player_currentProxy','',FALSE];
	};
	_handPos = player modelToWorldWorld (player selectionPosition 'righthand');
	_intersections = (lineIntersectsSurfaces [
		_handPos vectorAdd [0,0,0.3],
		_handPos vectorAdd [0,0,-0.3]
	]) + (lineIntersectsSurfaces [
		_handPos vectorAdd [-0.3,0,0],
		_handPos vectorAdd [0.3,0,0]
	]) + (lineIntersectsSurfaces [
		_handPos vectorAdd [0,0.3,0],
		_handPos vectorAdd [0,-0.3,0]
	]);
	if (_intersections isNotEqualTo []) then {
		private _weaponModel = toLowerANSI (getText (configFile >> 'CfgWeapons' >> (currentWeapon player) >> 'model'));
		_weaponModelArray = toArray _weaponModel;
		if ((_weaponModelArray select 0) isEqualTo 92) then {
			_weaponModelArray deleteAt 0;
			_weaponModel = toString _weaponModelArray;
		};
		_intersections = _intersections apply { [_x # 2,toLowerANSI ((getModelInfo (_x # 2)) # 1),_weaponModel] };
		private _weapon = objNull;
		{
			if (_weaponModel isEqualTo (_x # 1)) exitWith {
				_weapon = _x # 0;
			};
		} forEach _intersections;
		if (!isNull _weapon) then {
			player setVariable ['QS_player_currentWeapon',toLowerANSI (currentWeapon player),FALSE];
			player setVariable ['QS_player_weaponObject',_weapon,FALSE];
			player setVariable ['QS_player_weaponMuzzlePos',toLowerANSI (getText (configFile >> "CfgWeapons" >> (currentWeapon player) >>'muzzleEnd')),FALSE];
			
			private _customOffset = QS_hashmap_laserOffset getOrDefault [toLowerANSI (currentWeapon player),[0,0,0]];
			private _currentProxy = '';
			
			if ((currentWeapon player) isEqualTo (primaryWeapon player)) then {
				_currentProxy = ((player selectionNames 'memory') select { ['weapon.001',_x,FALSE] call BIS_fnc_inString }) select 0;
				if (_customOffset isEqualTo [0,0,0]) then {
					_customOffset = [0,0.3,0];
				};
			};
			if ((currentWeapon player) isEqualTo (handgunWeapon player)) then {
				_currentProxy = ((player selectionNames 'memory') select { ['pistol.001',_x,FALSE] call BIS_fnc_inString }) select 0;
				if (_customOffset isEqualTo [0,0,0]) then {
					_customOffset = [0,0.25,0];
				};
			};
			if ((currentWeapon player) isEqualTo (secondaryWeapon player)) then {
				_currentProxy = ((player selectionNames 'memory') select { ['launcher.001',_x,FALSE] call BIS_fnc_inString }) select 0;
				if (_customOffset isEqualTo [0,0,0]) then {
					_customOffset = [0,0.25,0];
				};
			};
			player setVariable ['QS_player_laserCustomOffset',_customOffset,FALSE];
			player setVariable ['QS_player_currentProxy',_currentProxy,FALSE];
		};
	};
};
QS_fnc_updateLaserPos = {
	_player = _this # 0;
	_weapon = _this # 1;
	[
		(player modelToWorldVisualWorld ((player selectionPosition (player getVariable ['QS_player_currentProxy',''])) vectorAdd ((_weapon selectionPosition (player getVariable ['QS_player_weaponMuzzlePos',''])) vectorAdd (player getVariable ['QS_player_laserCustomOffset',[0,0,0]])))),
		(_player weaponDirection (currentWeapon _player))
	]
};
if (!isNil 'QS_EH_94') then {
	removeMissionEventHandler ['Draw3D',QS_EH_94];
};
if (isNil 'QS_hashmap_laserOffset') then {
	QS_hashmap_laserOffset = createHashMapFromArray QS_laserOffset_array;
};
player setVariable ['QS_toggle_visibleLaser',TRUE,FALSE];
player setVariable ['QS_player_currentWeapon','',FALSE];
player setVariable ['QS_player_weaponObject',objNull,FALSE];
QS_EH_94 = addMissionEventHandler [
	'Draw3D',
	{
		if (!isGameFocused) exitWith {};
		if (isNull (objectParent player)) then {
			if ((player getVariable ['QS_player_currentWeapon','']) isNotEqualTo (toLowerANSI (currentWeapon player))) then {
				call QS_fnc_updatePlayerWeaponObject;
			} else {
				if (
					(!isNull (player getVariable ['QS_player_weaponObject',objNull])) &&
					((player getVariable ['QS_player_currentProxy','']) isNotEqualTo '') &&
					((player getVariable ['QS_player_weaponMuzzlePos','']) isNotEqualTo '')
				) then {
					([player,(player getVariable ['QS_player_weaponObject',objNull])] call QS_fnc_updateLaserPos) params ['_laserStart','_weaponDirection'];
					if (player getVariable ['QS_toggle_visibleLaser',TRUE]) then {
						drawLaser [
							(player getVariable ['QS_player_weaponObject',objNull]) modelToWorldVisualWorld _laserStart,
							_weaponDirection, 
							QS_laser_beamColor, 
							QS_laser_dotColor, 
							QS_laser_dotSize, 
							QS_laser_beamThickness, 
							QS_laser_beamMaxLength, 
							(((currentVisionMode player) isNotEqualTo 0) || QS_laser_forceIR)
						];
					};
				};
			};
		};
	}
];
removeAllUserActionEventHandlers ['headlights','activate'];
addUserActionEventHandler ['headlights','activate',{
	player setVariable ['QS_toggle_visibleLaser',!(player getVariable ['QS_toggle_visibleLaser',FALSE]),FALSE];
}];

 

  • Like 8

Share this post


Link to post
Share on other sites

Very nice application of what can be done the intersections and selections commands. (I have to dig for the ((_weaponModelArray select 0) isEqualTo 92) condition).

Share this post


Link to post
Share on other sites
13 hours ago, pierremgi said:

Very nice application of what can be done the intersections and selections commands. (I have to dig for the ((_weaponModelArray select 0) isEqualTo 92) condition).

 

Here is the improved version, with heavy credits towards @Leopard20 , @fn_Quiksilver and @POLPOX

 

VAL_fn_drawLaser = {
    if !(isNil "V_PLAYERLASEREHID") exitWith {removeMissionEventHandler ["Draw3D", V_PLAYERLASEREHID]; V_PLAYERLASEREHID = nil; systemChat "Weapon Laser Off"; playSound3D ["A3\missions_f\data\sounds\click.wss", player];};
	if (currentWeapon player isEqualTo secondaryWeapon player) exitWith {systemChat "Weapon Laser NOT AVAILABLE";};
    playSound3D ["A3\missions_f\data\sounds\click.wss", player];
	
    V_PLAYERLASEREHID = addMissionEventHandler ["Draw3D", {
		private _laserDir =  (player weaponDirection (currentWeapon player));
		
		private _w = currentWeapon player;
		if (_w != player getVariable ["last_weapon", "?"]) then {
			private _cfg = configFile >> "CfgWeapons" >> _w;
			private _m = getText(_cfg >> "model");
			private _o = createSimpleObject [_m, [0,0,0], true];
			private _off = _o selectionPosition [getText(_cfg >> "muzzlePos"), "memory"]; 
			_off = _off apply {[_x]};
			deleteVehicle _o;
			player setVariable ["offset", _off];
			player setVariable ["last_weapon", _w];
			player setVariable ["proxy",
				[
					"proxy:\a3\characters_f\proxies\pistol.001",
					"proxy:\a3\characters_f\proxies\weapon.001",
					"proxy:\a3\characters_f\proxies\launcher.001",
					"proxy:\a3\characters_f\proxies\binoculars.001"
				] select (([1, 4, 4096] find getNumber(_cfg >> "type")) + 1)
			];
		};
		private _offset = player getVariable ["offset", []];
		private _proxy = player getVariable ["proxy", ""];
		player selectionVectorDirAndUp [_proxy, 1] params ["_vy", "_vz"];
		private _pos = selectionPosition [player, _proxy, 0];
		private _vx = _vy vectorCrossProduct _vz;
		private _mat = matrixTranspose [_vx, _vy, _vz];
		_pos = _pos vectorAdd flatten (_mat matrixMultiply _offset);
		_pos = _pos vectorAdd [0.01,0.15,-0.05];
		private _laserStart = player modelToWorldVisualWorld _pos;
	
        drawLaser [
            _laserStart,
            _laserDir,
            [1000, 0, 0],
            [1000, 0, 0],
            0.20,
            0.25,
            100,
            false
        ];
    }];
    systemChat "Weapon Laser ON";
};

Thank you guys, and enjoy!

  • Like 5

Share this post


Link to post
Share on other sites
17 hours ago, LSValmont said:

 

Here is the improved version, with heavy credits towards @Leopard20 , @fn_Quiksilver and @POLPOX

 


VAL_fn_drawLaser = {
    if !(isNil "V_PLAYERLASEREHID") exitWith {removeMissionEventHandler ["Draw3D", V_PLAYERLASEREHID]; V_PLAYERLASEREHID = nil; systemChat "Weapon Laser Off"; playSound3D ["A3\missions_f\data\sounds\click.wss", player];};
	if (currentWeapon player isEqualTo secondaryWeapon player) exitWith {systemChat "Weapon Laser NOT AVAILABLE";};
    playSound3D ["A3\missions_f\data\sounds\click.wss", player];
	
    V_PLAYERLASEREHID = addMissionEventHandler ["Draw3D", {
		private _laserDir =  (player weaponDirection (currentWeapon player));
		
		private _w = currentWeapon player;
		if (_w != player getVariable ["last_weapon", "?"]) then {
			private _cfg = configFile >> "CfgWeapons" >> _w;
			private _m = getText(_cfg >> "model");
			private _o = createSimpleObject [_m, [0,0,0], true];
			private _off = _o selectionPosition [getText(_cfg >> "muzzlePos"), "memory"]; 
			_off = _off apply {[_x]};
			deleteVehicle _o;
			player setVariable ["offset", _off];
			player setVariable ["last_weapon", _w];
			player setVariable ["proxy",
				[
					"proxy:\a3\characters_f\proxies\pistol.001",
					"proxy:\a3\characters_f\proxies\weapon.001",
					"proxy:\a3\characters_f\proxies\launcher.001",
					"proxy:\a3\characters_f\proxies\binoculars.001"
				] select (([1, 4, 4096] find getNumber(_cfg >> "type")) + 1)
			];
		};
		private _offset = player getVariable ["offset", []];
		private _proxy = player getVariable ["proxy", ""];
		player selectionVectorDirAndUp [_proxy, 1] params ["_vy", "_vz"];
		private _pos = selectionPosition [player, _proxy, 0];
		private _vx = _vy vectorCrossProduct _vz;
		private _mat = matrixTranspose [_vx, _vy, _vz];
		_pos = _pos vectorAdd flatten (_mat matrixMultiply _offset);
		_pos = _pos vectorAdd [0.01,0.15,-0.05];
		private _laserStart = player modelToWorldVisualWorld _pos;
	
        drawLaser [
            _laserStart,
            _laserDir,
            [1000, 0, 0],
            [1000, 0, 0],
            0.20,
            0.25,
            100,
            false
        ];
    }];
    systemChat "Weapon Laser ON";
};

Thank you guys, and enjoy!

 

awesome work lads

 

that "selectionVectorDirAndUp" is a game changer for stuff like this

  • Like 3

Share this post


Link to post
Share on other sites
On 4/10/2023 at 9:56 AM, LSValmont said:

 

Here is the improved version, with heavy credits towards @Leopard20 , @fn_Quiksilver and @POLPOX

 


VAL_fn_drawLaser = {
    if !(isNil "V_PLAYERLASEREHID") exitWith {removeMissionEventHandler ["Draw3D", V_PLAYERLASEREHID]; V_PLAYERLASEREHID = nil; systemChat "Weapon Laser Off"; playSound3D ["A3\missions_f\data\sounds\click.wss", player];};
	if (currentWeapon player isEqualTo secondaryWeapon player) exitWith {systemChat "Weapon Laser NOT AVAILABLE";};
    playSound3D ["A3\missions_f\data\sounds\click.wss", player];
	
    V_PLAYERLASEREHID = addMissionEventHandler ["Draw3D", {
		private _laserDir =  (player weaponDirection (currentWeapon player));
		
		private _w = currentWeapon player;
		if (_w != player getVariable ["last_weapon", "?"]) then {
			private _cfg = configFile >> "CfgWeapons" >> _w;
			private _m = getText(_cfg >> "model");
			private _o = createSimpleObject [_m, [0,0,0], true];
			private _off = _o selectionPosition [getText(_cfg >> "muzzlePos"), "memory"]; 
			_off = _off apply {[_x]};
			deleteVehicle _o;
			player setVariable ["offset", _off];
			player setVariable ["last_weapon", _w];
			player setVariable ["proxy",
				[
					"proxy:\a3\characters_f\proxies\pistol.001",
					"proxy:\a3\characters_f\proxies\weapon.001",
					"proxy:\a3\characters_f\proxies\launcher.001",
					"proxy:\a3\characters_f\proxies\binoculars.001"
				] select (([1, 4, 4096] find getNumber(_cfg >> "type")) + 1)
			];
		};
		private _offset = player getVariable ["offset", []];
		private _proxy = player getVariable ["proxy", ""];
		player selectionVectorDirAndUp [_proxy, 1] params ["_vy", "_vz"];
		private _pos = selectionPosition [player, _proxy, 0];
		private _vx = _vy vectorCrossProduct _vz;
		private _mat = matrixTranspose [_vx, _vy, _vz];
		_pos = _pos vectorAdd flatten (_mat matrixMultiply _offset);
		_pos = _pos vectorAdd [0.01,0.15,-0.05];
		private _laserStart = player modelToWorldVisualWorld _pos;
	
        drawLaser [
            _laserStart,
            _laserDir,
            [1000, 0, 0],
            [1000, 0, 0],
            0.20,
            0.25,
            100,
            false
        ];
    }];
    systemChat "Weapon Laser ON";
};

Thank you guys, and enjoy!

 

I must be missing something, I put this in an init file, and it doesn't do anything. How does this work?

Share this post


Link to post
Share on other sites
19 hours ago, stburr91 said:

 

I must be missing something, I put this in an init file, and it doesn't do anything. How does this work?

 

Put the code in initPlayerLocal.sqf and a few lines after put:

 

player call VAL_fn_drawLaser;

 

Then inside the game you might need to call the function again to turn  the laser ON/OFF.

 

I do that by adding a keyPressed EH for when shift+L is pressed by the player but you can add an addaction or call it from the debug console too, whatever is easier for you.

  • Like 1

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

×