SteamVR/Environments/Scripting/flashlight script code full
< SteamVR | Environments | Scripting
Jump to navigation
Jump to search

This article has no
links to other VDC articles. Please help improve this article by adding links
that are relevant to the context within the existing text.
January 2024


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