SteamVR/Environments/Scripting/flashlight script code full

From Valve Developer Community
Jump to navigation Jump to search
Dead End - Icon.png
This article has no Wikipedia icon links to other VDC articles. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024
 --===========================================================================--
 --
 -- Flashlight Tool Script
 --
 --===========================================================================--
 
 local m_bIsEquipped = false; -- keep track of whether we are equipped or not
 local m_hHand = nil; -- keep a handle to the hand that is holding the tool
 local m_nHandID = -1; -- keep track of the hand index that is holding the tool (0==right, 1==left)
 local m_hHandAttachment = nil; -- this is the handle to the tool attachment which displays the actual model in the hand
 local m_hPlayer = nil; -- handle to the player holding the tool
 
 local m_bFlashlightOn = false; -- keep track of whether the flashlight is on or not
 local m_hFlashlightBeam = nil; -- a handle to the flashlight particle beam
 local m_particleTrackpad = nil; -- handle to the trackpad particle system
 local m_hLight = nil; -- handle to the light entity that actually illuminates the world in front of the flashlight
 
 -- we store off the trackpad position input from the player
 local m_flTrackpadX = 0; 
 local m_flTrackpadY = 0;
 
 local m_nTotalColors = 0; -- Total number of colors that can be switched between - this gets set on equip
 local m_nCurrentColor = 0; -- our current flashlight color
 
 -- this is the table of colors that we can switch between
 local m_tLightColors = {};
 m_tLightColors[0] = {255, 255, 255} -- white at the center
 
 m_tLightColors[1] = {255, 128, 0} --
 m_tLightColors[2] = {255, 0, 0} --
 m_tLightColors[3] = {255, 0, 64} -- 
 
 m_tLightColors[4] = {180, 0, 180} --
 m_tLightColors[5] = {64, 0, 180} --
 m_tLightColors[6] = {0, 0, 255} -- 
 
 m_tLightColors[7] = {0, 180, 180} -- 
 m_tLightColors[8] = {0, 255, 128} -- 
 m_tLightColors[9] = {0, 255, 0} --
 
 m_tLightColors[10] = {128, 255, 0} -- 
 m_tLightColors[11] = {255, 255, 0} --
 m_tLightColors[12] = {255, 180, 0} -- 
 
 -- this stores the min and max degrees for picking colors on the trackpad
 local m_tColorDegrees= {};
 m_tColorDegrees[0]=0;
 
 -- the in-game time the tool has been equipped
 local m_fEquipTime = 0;
 
 ---------------------------------------------------------------------------
 -- SetEquipped
 -- this and SetUnequipped are called by code when the tool is picked up and dropped
 ---------------------------------------------------------------------------
 function SetEquipped( self, pHand, nHandID, pHandAttachment, pPlayer )
 	print( "================  SetEquipped() ");
 
 	-- if somehow we don't have a player here, just return out
 	if ( pPlayer == nil ) then return; end
 
 	-- store these into our global scope so we can use them in other functions
 	m_hHand = pHand;
 	m_nHandID = nHandID;
 	m_hHandAttachment = pHandAttachment;
 	m_hPlayer = pPlayer;
 	m_bIsEquipped = true;
 	m_fEquipTime = Time();
 
 	-- create the trackpad particle system
 	local particleName = "particles/tool_fx/controller_trackpad_position_dot.vpcf";
 	-- the created particle system is now stored in this handle for use elsewhere
 	m_particleTrackpad = ParticleManager:CreateParticle(particleName, PATTACH_POINT_FOLLOW, m_hHandAttachment);
 	-- set the position to be on the trackpad_center attachment on the tool model
 	ParticleManager:SetParticleControlEnt( m_particleTrackpad, 0, m_hHandAttachment, PATTACH_POINT_FOLLOW, "trackpad_center", Vector( 0, 0, 0 ), true );
 
 	-- based on the number of entries in the m_tLightColors table, set the degree values for later use 
 	local m_nTotalColors = table.getn(m_tLightColors);
 	local flDegreeChunk = 360.0/m_nTotalColors;
 	local nMinDegree = 0;
 	for i=1,m_nTotalColors do 
 		local max = i*flDegreeChunk;
 		m_tColorDegrees[i] = max;
 		print( "m_tColorDegrees["..i.."] = "..m_tColorDegrees[i] );
 	end
 
 	-- call this function once so the position of the newly created particle system is updated right away
 	UpdateTrackpadPosition();
 	
 	-- this disables non-tool inputs in the trackpad
	-- to prevent teleporting and turning around
 	m_hPlayer:AllowTeleportFromHand(m_nHandID, false);
 
 	-- this sets up our think function which will run continually
 	m_hHand:SetThink( "FlashlightThink", self, 0.0 );
 
 	return true;
 end
 
 ---------------------------------------------------------------------------
 -- SetUnequipped
 -- called when the player drops the tool
 ---------------------------------------------------------------------------
 function SetUnequipped()
 	print( "================  SetUnequipped() ");
 	
 	m_hPlayer:AllowTeleportFromHand(m_nHandID, true);
 
 	m_hHand = nil;
 	m_nHandID = -1;
 	m_hHandAttachment = nil;
 	m_hPlayer = nil;
 	m_bIsEquipped = false;
 	
 	if ( m_hLight ~= nil ) then
 		m_hLight:Destroy();
 		m_hLight = nil;
 	end
 
 	ReleaseFireButton();
 
 	if ( m_particleTrackpad ~= nil ) then
 		ParticleManager:DestroyParticle( m_particleTrackpad, true );
 		m_particleTrackpad = nil;
 	end
 
 	return true;
 end
 
 ---------------------------------------------------------------------------
 -- GetDegrees
 -- this is a helper function for getting the degrees of an x and y position on a 2d surface
 -- we use this to figure out which color in the color wheel the user is selecting
 ---------------------------------------------------------------------------
 function GetDegrees( x, y )
     local deltaX = 0 - x;
     local deltaY = 0 - y;
 
    local radAngle = math.atan2(deltaY, deltaX);
    local degreeAngle = radAngle * 180.0 / math.pi;
 
    return (180.0 - degreeAngle);
 end
 
 ---------------------------------------------------------------------------
 -- Precache
 -- Gets called from code when this entity is created
 -- Content that this entity uses needs to be precached before it's used
 ---------------------------------------------------------------------------
 function Precache( context )
 	--Cache the models
 	PrecacheModel("models/props_gameplay/flashlight001.vmdl", context);
 	--Cache the particles
 	PrecacheParticle("particles/tool_fx/flashlight_thirdperson.vpcf", context);
 	PrecacheParticle("particles/tool_fx/flashlight_thirdperson_beamlet.vpcf", context);
 	PrecacheParticle("particles/tool_fx/controller_trackpad_position_dot.vpcf", context);
 end
 
 ---------------------------------------------------------------------------
 -- FlashlightThink
 -- The frequency at which this think will run is based on the value that it returns
 -- Initiated from SetEquipped
 ---------------------------------------------------------------------------
 function FlashlightThink()
 
 	-- if we don't have a hand attachment, that means it's not equipped
 	if ( m_hHandAttachment == nil ) then
 		-- the tool is probably already dropped, but we call this function to make sure
 		-- everything is cleared and reset as if it were dropped normally
 		DropTool();
 		return nil;
 	end
 
 	-- get the index of the model attachment on the tool model
 	local modelAttachmentIndex = m_hHandAttachment:ScriptLookupAttachment( "flashlight_beam" );
 	-- get the position of the attachment that we just got the index for
 	local vecStartPos = m_hHandAttachment:GetAttachmentOrigin( modelAttachmentIndex );
 	-- get the forward vector of the tool (it needs to be reversed)
 	local direction = -m_hHandAttachment:GetForwardVector();
 	-- now move 140 units out from our start position in the direction than tool is facing
 	local vecEndPos = (vecStartPos+(direction*140));
 
 	-- if we've already created our flashlight beam particle
 	if ( m_hFlashlightBeam ~= nil ) then
 		-- make sure the point one is attached to the attachment point
 		ParticleManager:SetParticleControlEnt( m_hFlashlightBeam, 1, m_hHandAttachment, PATTACH_POINT_FOLLOW, "flashlight_beam", Vector(0,0,0), true );
 		-- and the second point is where we want our end position to be
 		-- the particle system draws particles between these two points along that line
 		ParticleManager:SetParticleControl( m_hFlashlightBeam, 2, vecEndPos );
 	end
 
 	-- enable this to visualize the line created between vecStartPos and vecEndPos
 	--DebugDrawLine(vecStartPos, vecEndPos, 0, 255, 0, true, 0.02);
 
 	return 0.01;
 end
 
 ---------------------------------------------------------------------------
 -- called when the player presses the trigger button (from OnHandleInput)
 ---------------------------------------------------------------------------
 function PressFireButton()
 	-- add a delay to prevent the flashlight from turning on when picked up with the trigger
 	if( Time() > m_fEquipTime + 0.2 ) then
 		ToggleFlashlight();
 	end
 end
 
 ---------------------------------------------------------------------------
 -- called when the player presses the trigger button (from OnHandleInput)
 ---------------------------------------------------------------------------
 function ReleaseFireButton()
 	-- currently nothing happens when the player releases the trigger button
 end
 
 ---------------------------------------------------------------------------
 -- When the player presses the grip button, we drop the tool
 ---------------------------------------------------------------------------
 function DropTool()
 	-- make sure we let the code know the trigger has been released
 	ReleaseFireButton();
 	-- force the flashlight to go off when we release it
 	TurnFlashlightOff();
 	-- this calls C++ code on the prop tool entity and forces it to be dropped by the player
 	thisEntity:ForceDropTool();
 	print( "-------------------DropTool" );
 end
 
 ---------------------------------------------------------------------------
 -- Our color changing function
 ---------------------------------------------------------------------------
 function ChangeColor( nNewColor )
 	-- if this function was called, but the color we want to 
 	-- change to is the same as the one we are on don't do anything
 	-- it prevents code, sounds, etc from running unnecessarily
 	if ( m_nLightColor == nNewColor ) then
 		return;
 	end
 
 	-- store the new color we want to switch to
 	m_nLightColor = nNewColor;
 	print( "m_nLightColor = "..nNewColor );
 
 	-- if we have a valid flashlight particle beam
 	if ( m_hFlashlightBeam ~= nil ) then
 		-- tell the particle system that it should use the new color
 		ParticleManager:SetParticleControl( m_hFlashlightBeam, 5, Vector( m_tLightColors[m_nLightColor][1], m_tLightColors[m_nLightColor][2], m_tLightColors[m_nLightColor][3] ) );
 	end
 
 	-- if we have a valid handle to the light entity
 	if ( m_hLight ~= nil ) then
 		-- tell it to switch to the new light color
 		EntFireByHandle( self, m_hLight, "setlightcolor", ""..m_tLightColors[m_nLightColor][1].." "..m_tLightColors[m_nLightColor][2].." "..m_tLightColors[m_nLightColor][3].."", 0 );		
 	end
 
 	EmitSoundOn("ui_select", thisEntity);
 end
 
 ---------------------------------------------------------------------------
 -- OnHandleInput
 -- Called from C++ code
 -- This function receives input passed from the controller inputs from the player
 -- we can capture these inputs and choose to pass them back to be used by another attachment
 -- or we can clear them and "swallow" the input here
 -- other hand attachments, like the one that lets you teleport around or change your hand gesture
 -- use certain inputs from the player. if you want to prevent these actions while you have
 -- THIS tool equipped, you need to clear the input after you catch it
 ---------------------------------------------------------------------------
 function OnHandleInput( input )
 
 	-- here are all of the inputs that can be passed from code:
 --		IN_USE_HAND0,			-- when the trigger is pressed
 --		IN_USE_HAND1,
 --		IN_PAD_LEFT_HAND0,		-- when the pad is pressed on the left side
 --		IN_PAD_RIGHT_HAND0,		-- when the pad is pressed on the right side
 --		IN_PAD_UP_HAND0,		-- when the pad is pressed on the top
 --		IN_PAD_DOWN_HAND0,		-- when the pad is pressed on the bottom
 --		IN_PAD_LEFT_HAND1,
 --		IN_PAD_RIGHT_HAND1,
 --		IN_PAD_UP_HAND1,
 --		IN_PAD_DOWN_HAND1,
 --		IN_MENU_HAND0,
 --		IN_MENU_HAND1,
 --		IN_GRIP_HAND0,
 --		IN_GRIP_HAND1,
 --		IN_GRIPANALOG_HAND0,
 --		IN_GRIPANALOG_HAND1,
 --		IN_PAD_HAND0,			-- when the pad is pressed anywhere
 --		IN_PAD_HAND1,
 --		IN_PAD_TOUCH_HAND0,		-- when the pad is touched anywhere
 --		IN_PAD_TOUCH_HAND1, 
 
 	-- these are what's passed in
 	--input.buttonsDown;
 	--input.buttonsPressed;
 	--input.buttonsReleased;
 	--input.trackpadX;		-- ( -1.0 to 1.0 ) 
 	--input.trackpadY;		-- ( -1.0 to 1.0 ) 
 	--input.triggerValue;	-- ( 0 to 1.0 )
 
 	local bUpdateTrackpad = false;
 
 	-- get the current position of the player's finger on the track pad on the X axis
 	local flTrackpadX = input.trackpadX;
 	if ( flTrackpadX ~= 0 and flTrackpadX ~= m_flTrackpadX ) then
 		-- if it changed from the last time we got an input change, note it
 		m_flTrackpadX = flTrackpadX;
 		--print( "m_flTrackpadX = "..m_flTrackpadX );
 		-- and let the check down below know we should update the trackpad visuals
 		bUpdateTrackpad = true;
 	end
 
 	-- get the current position of the player's finger on the track pad on the Y axis
 	local flTrackpadY = input.trackpadY;
 	if ( flTrackpadY ~= 0 and flTrackpadY ~= m_flTrackpadY ) then
 		-- if it changed from the last time we got an input change, note it
 		m_flTrackpadY = flTrackpadY;
 		--print( "m_flTrackpadY = "..m_flTrackpadY );
 		-- and let the check down below know we should update the trackpad visuals
 		bUpdateTrackpad = true;
 	end
 
 	-- trackpad position has been changed, update the position and color picked
 	if ( bUpdateTrackpad ) then
 		UpdateTrackpadPosition();
 	end
 
 	-- this is lua's ugly version of a ternary operator
 	-- we do this because we only care about the input from the hand that is holding us
 	-- but you could check input from the other hand if you want
 	local nIN_TRIGGER = IN_USE_HAND1; if (m_nHandID == 0) then nIN_TRIGGER = IN_USE_HAND0 end;
 	local nIN_GRIP = IN_GRIP_HAND1; if (m_nHandID == 0) then nIN_GRIP = IN_GRIP_HAND0 end;
 
 	-- this checks to see if the TRIGGER has been pressed on this hand
 	-- this is only set when the trigger is first pressed
 	-- if you want to see if it's HELD, you need to keep track of that yourself
 	if ( input.buttonsPressed:IsBitSet( nIN_TRIGGER ) ) then
 		print( "TRIGGER is pressed" );
 		-- now clear it because we don't want any other tools to do anything with the trigger press
 		input.buttonsPressed:ClearBit( nIN_TRIGGER );
 
 		PressFireButton();
 	end
 
 	-- this checks if the TRIGGER has just been released
 	if ( input.buttonsReleased:IsBitSet( nIN_TRIGGER ) ) then
 		print( "TRIGGER is released" );
 		-- clear it!
 		input.buttonsReleased:ClearBit( nIN_TRIGGER );
 
 		ReleaseFireButton();
 	end
 
 	-- checks to see if the GRIP has been pressed this update
 	if ( input.buttonsReleased:IsBitSet( nIN_GRIP ) ) then
 		print( "GRIP is released" );
 		-- if it's pressed, clear the bit!
 		input.buttonsReleased:ClearBit( nIN_GRIP );
 
 		-- drop the tool
 		DropTool();
 	end
 
 	-- this code previously disabled teleporting
 	--if ( input.buttonsDown:IsBitSet( nIN_PAD ) ) then
 	--	input.buttonsDown:ClearBit( nIN_PAD );
 	--end
 	--if ( input.buttonsDown:IsBitSet( nIN_PAD_TOUCH ) ) then
 	--	input.buttonsDown:ClearBit( nIN_PAD_TOUCH );
 	--end
 
 
 	-- we return the input data back to C++ code so other tools can use it
 	return input;
 end
 
 ---------------------------------------------------------------------------
 -- Called from OnHandleInput to update the position and color of our trackpad color indicator 
 ---------------------------------------------------------------------------
 function UpdateTrackpadPosition() 
 
 	-- get the degrees of our current trackpad position
 	local deg = GetDegrees( m_flTrackpadX, m_flTrackpadY );
 	--print( "degrees = "..deg );
 
 	-- get the distance the player's finger is from the center of the trackpad'
 	local flDist = math.sqrt( (m_flTrackpadX ^ 2) + (m_flTrackpadY ^ 2) );
 	--print( "flDist = "..flDist );
 	if ( flDist < 0.24 ) then
 		-- if the player fingers is within this amount of the center - they are choosing WHITE
 		ChangeColor( 0 );
 	else
 		-- otherwise, use the degrees we stored earlier to determine which color they want to pick
 		local m_nTotalColors = table.getn(m_tLightColors);
 		for i=1,m_nTotalColors do 
 			local mindegree = m_tColorDegrees[i-1];
 			local maxdegree = m_tColorDegrees[i];
 			if ( deg >= mindegree and deg < maxdegree ) then
 				ChangeColor( i );
 				break;
 			end		
 		end
 	end
 
 	-- set the color on the trackpad particle
 	ParticleManager:SetParticleControl( m_particleTrackpad, 1, Vector( -m_flTrackpadY, m_flTrackpadX, 0 ) );
 	ParticleManager:SetParticleControl( m_particleTrackpad, 2, Vector( m_tLightColors[m_nLightColor][1], m_tLightColors[m_nLightColor][2], m_tLightColors[m_nLightColor][3] ) );
 
 end
 
 ---------------------------------------------------------------------------
 -- Called as a single toggle function to turn the flashlight on and off
 ---------------------------------------------------------------------------
 function ToggleFlashlight()
 
 	if ( m_bFlashlightOn == true ) then
 		TurnFlashlightOff()
 	else
 		TurnFlashlightOn();
 	end
 
 	-- FireHapticPulse is what makes the controller rumble
 	-- you can pass 0.1, 0.5 or 1 for light, medium and heavy
 	m_hHand:FireHapticPulse(1);
 end
 
 ---------------------------------------------------------------------------
 -- TURN ON!
 ---------------------------------------------------------------------------
 function TurnFlashlightOn()
 
 	-- if we don't have a hand holding us, just return
 	if ( m_hHand == nil ) then return; end
 
 	-- shouldn't happen, but if we dont have a player, return
 	if ( m_hPlayer == nil ) then print( "NO PLAYER!"); return; end
 
 	-- keep track of the flashlight now being ON
 	m_bFlashlightOn = true;
 
 	-- emit a SOUND on thisEntity - just borrow from the drone
 	EmitSoundOn("drone_equip", thisEntity);
 
 	-- this may not be working right now
 	EntFireByHandle( self, m_hHandAttachment, "Skin", "1", 0 );		
 
 	local modelAttachmentIndex = m_hHandAttachment:ScriptLookupAttachment( "flashlight_beam" );
 	local vecStartPost = m_hHandAttachment:GetAttachmentOrigin( modelAttachmentIndex );
 	local angAttachment = m_hHandAttachment:GetAttachmentAngles( modelAttachmentIndex );
 	local direction = -m_hHandAttachment:GetForwardVector();	
 	local vecEndPos = (vecStartPost+(direction*190));
 
 	local particleName = "particles/tool_fx/flashlight_thirdperson.vpcf";
 	m_hFlashlightBeam = ParticleManager:CreateParticle(particleName, PATTACH_POINT_FOLLOW, m_hHandAttachment);
 	ParticleManager:SetParticleControlEnt( m_hFlashlightBeam, 0, m_hHandAttachment, PATTACH_POINT_FOLLOW, "flashlight_beam", Vector(0,0,0), true );
 	ParticleManager:SetParticleControlEnt( m_hFlashlightBeam, 1, m_hHandAttachment, PATTACH_POINT_FOLLOW, "flashlight_beam", Vector(0,0,0), true );
 	ParticleManager:SetParticleControl( m_hFlashlightBeam, 2, vecEndPos );
 	ParticleManager:SetParticleControl( m_hFlashlightBeam, 5, Vector( m_tLightColors[m_nLightColor][1], m_tLightColors[m_nLightColor][2], m_tLightColors[m_nLightColor][3] ) );
 
 	local lightTable = 
 	{
 		origin = vecStartPost,
 		angles = angAttachment,
 		targetname = "light"..thisEntity:entindex(),
 		enabled = "1",
 		color = ""..m_tLightColors[m_nLightColor][1].." "..m_tLightColors[m_nLightColor][2].." "..m_tLightColors[m_nLightColor][3].." 255",
 		brightness = "1.5",
 		range = "400",
 		castshadows = "1",
 		shadowtexturewidth = "64",
 		shadowtextureheight = "64",
 		style = "0",
 		fademindist = "0",
 		fademaxdist = "4000",
 		bouncescale = "1.0",
 		renderdiffuse = "1",
 		renderspecular = "1",
 		directlight = "2",
 		indirectlight = "0",
 		attenuation1 = "0.0",
 		attenuation2 = "1.0",	
 		innerconeangle = "20",
 		outerconeangle = "32"
 	}
 	m_hLight = SpawnEntityFromTableSynchronous( "light_spot", lightTable )
 	m_hLight:SetAngles( angAttachment[1], angAttachment[2], angAttachment[3] );
 	m_hLight:SetParent(m_hHandAttachment, "flashlight_beam")
 end
 
 function TurnFlashlightOff()
 	m_bFlashlightOn = false;
 
 	EmitSoundOn("drone_equip", thisEntity);
 
 	EntFireByHandle( self, m_hHandAttachment, "Skin", "0", 0 );		
 
 	if ( m_hLight ~= nil ) then
 		m_hLight:Destroy();
 		m_hLight = nil;
 	end
 
 	if ( m_hFlashlightBeam ~= nil ) then
 		ParticleManager:DestroyParticle( m_hFlashlightBeam, true );
 		m_hFlashlightBeam = nil;
 	end
 end