--[[ This is an expanded lua library made by wacev I do not care if you use this, credit me if you want :) I made this because I found myself copying common functions from other files or they just didn't exist in the base raflua/lua lib Some of the function here were made by other people, I just included them here to have them all in one space Last Updated: 9/21/25 For Documentation: Returns tells you what type of data it returns, if there isn't a Returns field then it doesn't return anything. Param has 2 parts, the first part is the name is the example function, the second part is the type of data it uses Vararg mean that the function supports unlimited amount of args, indicated by " ... " How To Use: At the start of your popfile, in WaveSchedule put: LuaScriptFile "scripts/wacev_expanded_lib.lua" [$SIGSEGV] Documentation: ----CVector---- --@Retuns angle (vector but on a range of 0-360) --@Param start vector --@Param goal vector --Returns the angle that a forward movement from the start vector would need to get to the goal vector --Can be used with CEntity:SnapEyeAngles() to make them look at a point in space --Can also be used with util.Trace for the angle param start:FaceVector( goal ) --@Returns x, y, and z in 3 different vars x,y,z = CVector:Unpack() ----Table---- --@Return the index of the chance that passed (say if 0.05 passed then it would return 2) --@Vararg table of numbers or a table within the table contain the index "chance" table.RandomChance( { 0.95, 0.05, ... } ) table.RandomChance( { { name = "Hello", chance = 0.95 }, { name = "World", chance = 0.05 }, ... } ) ----Ents---- --@Returns entity --@Param index number --@Param filter string, entity classname ( optional ) --Returns the ent with the matching handle index ents.FindByHandleIndex( index, filter ) --@Returns entity --@Param index number --@Param filter string, entity classname ( optional ) --Returns the ent with the matching net index ents.FindByNetIndex( index, filter ) --@Returns table (players) ents.GetAllRealPlayers() --@Returns table (players) ents.GetAllAlivePlayers() --@Returns table (players) ents.GetAllAliveRealPlayers() ----CEntity---- --@Returns entity CEntity:GetOwner() --@Returns entity CEntity:GetBuilder() --@Param scale number --@Param over_time number (optional) --@Param increment number (optional) --If over_time is left out, then instantly scale CEntity:SetModelScale( scale, over_time, increment ) --@Returns number --Returns the number index for a team. 0 = unassigned, 1 = spec, 2 = red, 3 = blu, 5 = halloween bosses CEntity:GetTeam() --@Returns boolean --@Param team number --The number index for a team. 0 = unassigned, 1 = spec, 2 = red, 3 = blu, 5 = halloween bosses CEntity:OnTeam( team ) --@Param particle string --@Param offset Vector --@Param lifetime number --@Param removeFunc function ( (optional) fire this function on remove, no args are passed to the function ) CEntity:PlayParentedParticle( particle, offset, lifetime, removeFunc ) --@Returns boolean --@Param target entity CEntity:HasLineOfSight( target ) --@Returns string CEntity:GetModel() ----Player---- --@Returns boolean player:IsMidair() --@Returns Vector --Gets a player's eye pos player:GetEyePos() --@Returns Vector --Gets a player's eye angles, third vector is always 0 player:GetEyeAngles() --@Returns Vector --Gets a player's eye angles, third vector isn't always 0, most of the time is the same as player:GetEyeAngles() player:GetEyeAnglesExact() --@Returns Vector --Gets a player's abs velocity in all three directions player:GetPlayerVelocity() --@Param velo Vector player:SetPlayerVelocity( velo ) --@Returns entity player:GetActivePlayerWeapon() player:GetActiveWeapon() --@Param weapon entity --if the weapon entity does not exist then nothing will happen --bug: certain weapons like the thermal thruster become unusable when forced to --note: calling this every tick will force the player to use an item but the draw anim of other weapons will still play player:SetActivePlayerWeapon( weapon ) player:SetActiveWeapon( weapon ) --@Returns boolean player:IsInvuln() --@Returns boolean --Doesn't check for minicrit boost conds player:IsCritBoosted() --@Returns boolean --Doesn't check for critboost conds player:IsMiniCritBoosted() ----Math---- --@Returns number --@Param value number --@Param min number --@Param max number math.clamp( value, min, max ) --@Returns number --@Param value number --@Param decimals number --Set decimals to 0 to have decimals removed math.round( value, decimals ) --@Returns number --@Param min number --@Param max number --Returns a pseudorandom float between the given values ( normal math.random() only returns ints ) math.randomfloat( min, max ) --@Returns number --@Param value number --@Param startMin number --@Param startMax number --@Param endMin number --@Param endMax number --remaps the value from one range to another math.remap( value, startMin, startMax, endMin, endMax ) --@Returns number --@Vararg numbers math.average( ... ) --@Returns number --@Param start number --@Param goal number --@Param percent float (0-1) --percent values greater than 1 work, the return will just be greater than the goal --Returns a value between the start and the goal based on the percent lerp( start, goal, percent ) --@Returns vector --@Param start vector --@Param end vector --@Param percent float (0-1) --percent values greater than 1 work, the return will just be greater than the goal --Returns a vector with each point being between the start and the goal based on the percent lerpvector( start, goal, percent ) ----Misc/Global Functions---- --@Returns boolean --@Param player player IsValidAlivePlayer(player) --@Returns boolean --@Param player player IsValidAliveRealPlayer(player) --@Returns boolean --@Param player player IsValidRealPlayer(player) --@Returns boolean --@Param player player IsValidPlayer(player) --@Returns boolean --@Param damagetype number/global --@Param filter number/global --Filters one damagetype from the mess that is in dmginfo.DamageType from the ON_DAMAGE_RECIEVED* callbacks IsDamageType( damagetype, filter ) --@Returns boolean --@Param player entity --@Param weapon entity or string IsValidWeapon( player, weapon ) --@Returns boolean --@Param val any tobool( val ) ----Hooks/Functions---- --@Param player entity --@Param text string --Add this function block as if it were OnGameTick() function OnPlayerChat( player, text ) end --@Param player entity --@Param cash number --Add this function block as if it were OnGameTick() function OnPickupCurrency( player, cash ) end --Add this function block as if it were OnGameTick() --No Params function OnTankDestroyed() end --@Param player entity --@Param attacker entity --@Param weapon string ( internal name of the weapon that killed the player EX: back_scatter ) --@Param dmgtype number ( combined number of all damagetypes ) --@Param assister entity --@Param crit_type number ( 0 = no crit, 1 = mini-crit, 2 = full crit ) --@Param silent_kill numberbool ( 0-1 ) --Add this function block as if it were OnGameTick() function OnPlayerDeath(player, attacker, weapon, dmgtype, assister, crit_type, silent_kill) end --@Param sound string --@Param pos Vector function PlaySound( sound, pos ) end --@Return entity ( the entity that was created ) --@Param class string ( classname of the entity to crate ) --@Param { ent_info } table ( entity info ( such as model and pos ) ) --@Param client entity ( player to show it to ) function CreateClientSidedProp( class, { ent_info }, client ) --@Return Vector --@Param radius number --Make sure to add an offset otherwise it will get a point around the origin function GetRandomPointInCircle( radius ) end --@Return boolean ( did it hit anything ) --@Param pos Vector --@Param mins Vector --@Param maxs Vector --@Param filter entity or table ( what to ignore ) --This function can be intesive, do not overuse function CustomHitbox( pos, mins, maxs, filter ) TODO/Roadmap: Disable buildings function Enable buildings function IsStunned() func IsDisabled() func (buildings only) GetPlayerClass() func util.BlastDamage() func GetParent() func Vector:Abs() --]] --Misc/Global Functions timer.Simple(1, function() print( "Running Expanded Lib Version 1.5" ) end) function IsValidAlivePlayer(ent) return IsValid(ent) and ent:IsPlayer() and ent:IsAlive(); end function IsValidAliveRealPlayer(ent) return IsValid(ent) and ent:IsRealPlayer() and ent:IsAlive(); end function IsValidRealPlayer(ent) return IsValid(ent) and ent:IsRealPlayer(); end function IsValidPlayer(ent) return IsValid(ent) and ent:IsPlayer(); end function IsDamageType( damagetype, filter ) return ( damagetype & filter ) == filter end function IsValidWeapon( ply, wep ) return wep and ( ply:GetPlayerItemByName( wep:GetItemName() ):IsValid() or ply:GetPlayerItemByName( wep ):IsValid() or ply:GetPlayerItemBySlot( wep ):IsValid() ) end function tobool( val ) if ( ( val == nil ) or ( val == false ) or ( val == 0 ) or ( val == "0" ) or ( val == "false" ) ) then return false end return true end function PlaySound(sound, position) local ent = ents.CreateWithKeys("info_target", {}, true, true); ent:SetAbsOrigin(position); ent:PlaySound(sound); ent:Remove(); end function CreateClientSidedProp( classname, table, client ) local prop = ents.CreateWithKeys( classname, table, true, true ) prop:Enable() prop:HideToAll() prop:ShowTo( client ) return prop end function GetRandomPointInCircle( Radius ) --welcome to fucked up math local pi = math.pi local theta = math.random() * Radius * pi local r = math.random() + math.random() if r >= 1 then r = 2 - r end --local dist = ( r * math.cos( theta ) )^2 + ( r * math.sin( theta ) )^2 --dist = math.sqrt( dist ) --print( dist * Radius ) --print( ( r * math.cos( theta ) ) * Radius, ( r * math.sin( theta ) ) * Radius ) return Vector( ( r * math.cos( theta ) ) * Radius, ( r * math.sin( theta ) ) * Radius, 0 ) end function CustomHitbox( origin, maxs, mins, filt ) local st1 = origin + Vector( maxs[1], maxs[2], maxs[3] ) local ed1 = origin + Vector( mins[1], mins[2], mins[3] ) local st2 = origin + Vector( maxs[1], maxs[2], mins[3] ) local ed2 = origin + Vector( mins[1], mins[2], maxs[3] ) local st3 = origin + Vector( mins[1], maxs[2], maxs[3] ) local ed3 = origin + Vector( maxs[1], mins[2], mins[3] ) local st4 = origin + Vector( mins[1], maxs[2], mins[3] ) local ed4 = origin + Vector( maxs[1], mins[2], maxs[3] ) local tr1 = util.Trace( { start = st1, endpos = ed1, filter = filt } ) local tr2 = util.Trace( { start = st2, endpos = ed2, filter = filt } ) local tr3 = util.Trace( { start = st3, endpos = ed3, filter = filt } ) local tr4 = util.Trace( { start = st4, endpos = ed4, filter = filt } ) if tr1.Hit or tr2.Hit or tr3.Hit or tr4.Hit then return false else return true end end --Ents ents.FindByHandleIndex = function( filter, class ) if not filter then return nil end if not class then for _, ent in pairs( ents.GetAll( ) ) do if ent:GetHandleIndex() == filter then return ent end end return nil else for _, ent in pairs( ents.FindAllByClass( class ) ) do if ent:GetHandleIndex() == filter then return ent end end return nil end end ents.FindByNetIndex = function( filter, class ) if not filter then return nil end if not class then for _, ent in pairs( ents.GetAll( ) ) do if ent:GetNetIndex() == filter then return ent end end return nil else for _, ent in pairs( ents.FindAllByClass( class ) ) do if ent:GetNetIndex() == filter then return ent end end return nil end end ents.GetAllBots = function() local players = {} for index, ply in pairs(ents.GetAllPlayers()) do if ply:IsBot() then table.insert( players, ply ) end end return players end ents.GetAllRealPlayers = function() local players = {} for index, ply in pairs(ents.GetAllPlayers()) do if ply:IsRealPlayer() then table.insert( players, ply ) end end return players end ents.GetAllAlivePlayers = function() local ply = {} for _, player in pairs( ents.GetAllPlayers() ) do if player:IsPlayer() and player:IsAlive() then table.insert( ply, player ) end end return ply end ents.GetAllAliveRealPlayers = function() local ply = {} for _, player in pairs( ents.GetAllPlayers() ) do if player:IsRealPlayer() and player:IsAlive() then table.insert( ply, player ) end end return ply end --Table table.RandomChance = function(t) local rand = math.random() local cumulativeProbability = 0 for key, item in pairs(t) do if (type(item) == "table") then cumulativeProbability = cumulativeProbability + item.chance; else cumulativeProbability = cumulativeProbability + item; end if (rand <= cumulativeProbability) then return key; end end end --CVector CVector.FaceVector = function(self, target) if target then local targetAngles = ( target - self ):ToAngles() targetAngles = Vector(targetAngles[1], targetAngles[2], 0) return targetAngles end end CVector.Unpack = function(self) return self[1], self[2], self[3] end --CEntity CEntity.GetModel = function(self) if self:IsValid() then return self.m_ModelName end end CEntity.HasLineOfSight = function( self, tar ) if self and tar then local ignore_tbl = { self, tar } for _, ply in pairs(ents.GetAllPlayers()) do table.insert(ignore_tbl, ply) end local Trace = util.Trace({ start = self:GetAbsOrigin() + Vector(0,0,16), endpos = tar:GetEyePos(), mask = MASK_SOLID, filter = ents.GetAllPlayers() }) local Trace2 = util.Trace({ start = self:GetAbsOrigin(), endpos = tar:GetAbsOrigin(), mask = MASK_SOLID, filter = ents.GetAllPlayers() }) if ( not Trace.Hit ) or ( not Trace2.Hit ) then return true else return false end end end CEntity.PlayParentedParticle = function(self, particle, offset, remove_after, RemoveFunction) if (not IsValid(self)) then return; end particle = ents.CreateWithKeys("info_particle_system", { effect_name=particle, start_active=1, ["$modules"]="fakeparent", ["$positiononly"]=1, }, true, true) local entity_origin = self:GetAbsOrigin(); if (offset) then entity_origin = entity_origin + offset; end particle.m_vecOrigin = entity_origin; particle["$SetFakeParent"](particle, self); particle["Start"](particle); timer.Create(remove_after, function() pcall(particle["Remove"], particle); if (RemoveFunction) then pcall(RemoveFunction); end end, 1); return particle; end CEntity.GetOwner = function(self) if self:IsValid() then return self.m_hOwnerEntity end end CEntity.GetBuilder = function(self) if self:IsValid() then return self.m_hBuilder end end CEntity.GetTeam = function(self) if self:IsValid() then return self.m_iTeamNum end end CEntity.OnTeam = function(self, team) if self:IsValid() then return self.m_iTeamNum == team end end CEntity.SetModelScale = function(self, scale, over_time, increment) if (not IsValid(self)) then return; end if (over_time and over_time > 0) then if (not increment or increment <= 0) then increment = 0.01; end local current_scale = self.m_flModelScale; if (scale - current_scale == 0) then return; elseif (scale - current_scale < 0) then increment = -increment; end local exec_times = math.floor(math.abs((scale - current_scale) / increment)); local counter = 0; timer.Create(over_time / exec_times, function() if (not IsValid(self)) then return; end counter = counter + 1; current_scale = current_scale + increment; if (counter == exec_times and current_scale ~= scale) then self.m_flModelScale = scale; else self.m_flModelScale = current_scale; end end, exec_times); else self.m_flModelScale = scale; end end --Players CEntity.IsInvuln = function(self) if (not IsValidPlayer(self)) then return end if (self:InCond(TF_COND_INVULNERABLE) or self:InCond(TF_COND_INVULNERABLE_CARD_EFFECT) or self:InCond(TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED) or self:InCond(TF_COND_INVULNERABLE_USER_BUFF)) then return true end return false end CEntity.IsCritBoosted = function(self) if (not IsValidPlayer(self)) then return end if (self:InCond(TF_COND_CRITBOOSTED) or self:InCond(TF_COND_CRITBOOSTED_PUMPKIN) or self:InCond(TF_COND_CRITBOOSTED_USER_BUFF) or self:InCond(TF_COND_CRITBOOSTED_BONUS_TIME) or self:InCond(TF_COND_CRITBOOSTED_CTF_CAPTURE) or self:InCond(TF_COND_CRITBOOSTED_ON_KILL) or self:InCond(TF_COND_CRITBOOSTED_RAGE_BUFF) or self:InCond(TF_COND_CRITBOOSTED_CARD_EFFECT) or self:InCond(TF_COND_CRITBOOSTED_RUNE_TEMP) or self:InCond(TF_COND_CRITBOOSTED_FIRST_BLOOD)) then return true end return false end CEntity.IsMiniCritBoosted = function(self) if (not IsValidPlayer(self)) then return end if (self:InCond(TF_COND_OFFENSEBUFF) or self:InCond(TF_COND_NOHEALINGDAMAGEBUFF) or self:InCond(TF_COND_ENERGY_BUFF) or self:InCond(TF_COND_MINICRITBOOSTED_ON_KILL)) then return true end return false end CEntity.IsMidair = function(self) if (IsValidAlivePlayer(self)) then return not (self.movetype == MOVETYPE_WALK and (self.m_fFlags & FL_ONGROUND ~= 0)); end end CEntity.GetEyePos = function(self) if (not IsValidPlayer(self)) then return; end local eyepos = self:GetAbsOrigin(); eyepos.z = eyepos.z + self["m_vecViewOffset[2]"]; return eyepos end CEntity.GetEyeAngles = function(self) if (IsValidPlayer(self)) then return Vector( self.m_angEyeAngles[1], self.m_angEyeAngles[2], 0 ) end end CEntity.GetEyeAnglesExact = function(self) if (IsValidPlayer(self)) then return Vector( self.m_angEyeAngles[1], self.m_angEyeAngles[2], self.m_angEyeAngles[3] ) end end CEntity.GetActivePlayerWeapon = function(self) if (IsValidPlayer(self)) then return self.m_hActiveWeapon end end CEntity.GetActiveWeapon = function(self) if (IsValidPlayer(self)) then return self.m_hActiveWeapon end end CEntity.SetActivePlayerWeapon = function(self, wep) if ( IsValidPlayer(self) ) and ( self.m_hActiveWeapon ) and ( self.m_hActiveWeapon:IsValid() ) then if IsValidWeapon( self, wep ) and ( wep.m_iViewModelIndex ~= nil ) and ( wep.m_iViewModelIndex ~= 0 ) then self.m_hActiveWeapon = wep end end end CEntity.SetActiveWeapon = function(self, wep) if ( IsValidPlayer(self) ) and ( self.m_hActiveWeapon ) and ( self.m_hActiveWeapon:IsValid() ) then if IsValidWeapon( self, wep ) and ( wep.m_iViewModelIndex ~= nil ) and ( wep.m_iViewModelIndex ~= 0 ) then self.m_hActiveWeapon = wep end end end CEntity.GetPlayerVelocity = function(self) if (IsValidPlayer(self)) then return self.m_vecAbsVelocity end end CEntity.SetPlayerVelocity = function(self, vel) if (IsValidAlivePlayer(self)) then self.m_vecAbsVelocity = vel end end --Math math.clamp = function( value, min, max ) if min and max then if min > max then min, max = max, min end if value > max then value = max end if min > value then value = min end return value elseif min and ( not max ) then return math.min( value ) elseif max and ( not min ) then return math.max( value ) end end math.round = function(num, decimals) if (not decimals or decimals <= 0) then return math.floor(num + 0.5) else local mod = 10 ^ decimals return math.floor((num * mod) + 0.5) / mod end end math.randomfloat = function(m, n) if (m) then if (n) then if (n == m) then return m; elseif (m > n) then m, n = n, m; end -- n should be greater than m local dif = n - m; local mod = dif * math.random(); return m + mod; else return m * math.random(); end else return math.random(); end end math.remap = function( value, inMin, inMax, outMin, outMax ) return outMin + ( ( ( value - inMin ) / ( inMax - inMin ) ) * ( outMax - outMin ) ) end math.average = function( ... ) local total = 0 for _, val in pairs({ ... }) do total = total + val end total = total / #{ ... } return total end function lerp(start, goal, exp) return start + (goal - start) * exp end function lerpvector(start, goal, exp) local return_vec = Vector(0, 0, 0) return_vec[1] = start[1] + (goal[1] - start[1]) * exp return_vec[2] = start[2] + (goal[2] - start[2]) * exp return_vec[3] = start[3] + (goal[3] - start[3]) * exp return return_vec end --Hooks local function PlayerChat(event_table) local text = event_table.text local userid = event_table.userid local player = ents.GetPlayerByUserId(userid) if ( player ) and ( player:IsValid() ) and ( player ~= nil ) then pcall( OnPlayerChat, player, text ) end end local function PickupCurrency(event_table) --prints error -- local cash = event_table.currency -- local player = ents.FindByNetIndex( event_table.player, "player" ) -- if ( player ) and ( player:IsValid() ) and ( player ~= nil ) then -- pcall( OnPickupCurrency, player, cash ) -- end end local function TankDestroyed() pcall( OnTankDestroyed ) end local function PlayerDeath(event_table) local userid = event_table.userid local player = ents.GetPlayerByUserId( userid ) local attacker_id = event_table.attacker local attacker = ents.GetPlayerByUserId( attacker_id ) local weapon = event_table.weapon local damagetype = event_table.damagebits local assister_id = event_table.assister local assister = ents.GetPlayerByUserId( assister_id ) local silent_kill = event_table.silent_kill local crit_type = event_table.crit_type if ( player ) and ( player:IsValid() ) and ( player ~= nil ) then pcall( OnPlayerDeath, player, attacker, weapon, damagetype, assister, crit_type, silent_kill ) end end AddEventCallback("player_say", PlayerChat) AddEventCallback("mvm_pickup_currency", PickupCurrency) AddEventCallback("mvm_tank_destroyed_by_players", TankDestroyed) AddEventCallback("player_death", PlayerDeath)