Files
wnsrc/gamemodes/sandbox/gamemode/commands.lua
lifestorm 6a58f406b1 Upload
2024-08-04 23:54:45 +03:00

1123 lines
32 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "prop_tools.lua" )
-- A little hacky function to help prevent spawning props partially inside walls
-- Maybe it should use physics object bounds, not OBB, and use physics object bounds to initial position too
local function fixupProp( ply, ent, hitpos, mins, maxs )
local entPos = ent:GetPos()
local endposD = ent:LocalToWorld( mins )
local tr_down = util.TraceLine( {
start = entPos,
endpos = endposD,
filter = { ent, ply }
} )
local endposU = ent:LocalToWorld( maxs )
local tr_up = util.TraceLine( {
start = entPos,
endpos = endposU,
filter = { ent, ply }
} )
-- Both traces hit meaning we are probably inside a wall on both sides, do nothing
if ( tr_up.Hit && tr_down.Hit ) then return end
if ( tr_down.Hit ) then ent:SetPos( entPos + ( tr_down.HitPos - endposD ) ) end
if ( tr_up.Hit ) then ent:SetPos( entPos + ( tr_up.HitPos - endposU ) ) end
end
local function TryFixPropPosition( ply, ent, hitpos )
fixupProp( ply, ent, hitpos, Vector( ent:OBBMins().x, 0, 0 ), Vector( ent:OBBMaxs().x, 0, 0 ) )
fixupProp( ply, ent, hitpos, Vector( 0, ent:OBBMins().y, 0 ), Vector( 0, ent:OBBMaxs().y, 0 ) )
fixupProp( ply, ent, hitpos, Vector( 0, 0, ent:OBBMins().z ), Vector( 0, 0, ent:OBBMaxs().z ) )
end
--[[---------------------------------------------------------
Name: CCSpawn
Desc: Console Command for a player to spawn different items
-----------------------------------------------------------]]
function CCSpawn( ply, command, arguments )
-- We don't support this command from dedicated server console
if ( !IsValid( ply ) ) then return end
if ( arguments[ 1 ] == nil ) then return end
if ( arguments[ 1 ]:find( "%.[/\\]" ) ) then return end
-- Clean up the path from attempted blacklist bypasses
arguments[ 1 ] = arguments[ 1 ]:gsub( "\\\\+", "/" )
arguments[ 1 ] = arguments[ 1 ]:gsub( "//+", "/" )
arguments[ 1 ] = arguments[ 1 ]:gsub( "\\/+", "/" )
arguments[ 1 ] = arguments[ 1 ]:gsub( "/\\+", "/" )
if ( !gamemode.Call( "PlayerSpawnObject", ply, arguments[ 1 ], arguments[ 2 ] ) ) then return end
if ( !util.IsValidModel( arguments[ 1 ] ) ) then return end
local iSkin = tonumber( arguments[ 2 ] ) or 0
local strBody = arguments[ 3 ] or nil
if ( util.IsValidProp( arguments[ 1 ] ) ) then
GMODSpawnProp( ply, arguments[ 1 ], iSkin, strBody )
return
end
if ( util.IsValidRagdoll( arguments[ 1 ] ) ) then
GMODSpawnRagdoll( ply, arguments[ 1 ], iSkin, strBody )
return
end
-- Not a ragdoll or prop.. must be an 'effect' - spawn it as one
GMODSpawnEffect( ply, arguments[ 1 ], iSkin, strBody )
end
concommand.Add( "gm_spawn", CCSpawn, nil, "Spawns props/ragdolls" )
local function MakeRagdoll( ply, _, _, model, _, Data )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnRagdoll", ply, model ) ) then return end
local Ent = ents.Create( "prop_ragdoll" )
duplicator.DoGeneric( Ent, Data )
Ent:Spawn()
duplicator.DoGenericPhysics( Ent, ply, Data )
Ent:Activate()
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedRagdoll", ply, model, Ent )
end
DoPropSpawnedEffect( Ent )
return Ent
end
-- Register the "prop_ragdoll" class with the duplicator, (Args in brackets will be retreived for every bone)
duplicator.RegisterEntityClass( "prop_ragdoll", MakeRagdoll, "Pos", "Ang", "Model", "PhysicsObjects", "Data" )
--[[---------------------------------------------------------
Name: GMODSpawnRagdoll - player spawns a ragdoll
-----------------------------------------------------------]]
function GMODSpawnRagdoll( ply, model, iSkin, strBody )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnRagdoll", ply, model ) ) then return end
local e = DoPlayerEntitySpawn( ply, "prop_ragdoll", model, iSkin, strBody )
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedRagdoll", ply, model, e )
end
DoPropSpawnedEffect( e )
undo.Create( "Ragdoll" )
undo.SetPlayer( ply )
undo.AddEntity( e )
undo.Finish( "Ragdoll (" .. tostring( model ) .. ")" )
ply:AddCleanup( "ragdolls", e )
end
function MakeProp( ply, Pos, Ang, model, _, Data )
-- Uck.
Data.Pos = Pos
Data.Angle = Ang
Data.Model = model
-- Make sure this is allowed
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnProp", ply, model ) ) then return end
local Prop = ents.Create( "prop_physics" )
duplicator.DoGeneric( Prop, Data )
Prop:Spawn()
duplicator.DoGenericPhysics( Prop, ply, Data )
-- Tell the gamemode we just spawned something
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedProp", ply, model, Prop )
end
FixInvalidPhysicsObject( Prop )
DoPropSpawnedEffect( Prop )
return Prop
end
duplicator.RegisterEntityClass( "prop_physics", MakeProp, "Pos", "Ang", "Model", "PhysicsObjects", "Data" )
duplicator.RegisterEntityClass( "prop_physics_multiplayer", MakeProp, "Pos", "Ang", "Model", "PhysicsObjects", "Data" )
function MakeEffect( ply, model, Data )
Data.Model = model
-- Make sure this is allowed
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnEffect", ply, model ) ) then return end
local Prop = ents.Create( "prop_effect" )
duplicator.DoGeneric( Prop, Data )
if ( Data.AttachedEntityInfo ) then
Prop.AttachedEntityInfo = table.Copy( Data.AttachedEntityInfo ) -- This shouldn't be neccesary
end
Prop:Spawn()
-- duplicator.DoGenericPhysics( Prop, ply, Data )
-- Tell the gamemode we just spawned something
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedEffect", ply, model, Prop )
end
if ( IsValid( Prop.AttachedEntity ) ) then
DoPropSpawnedEffect( Prop.AttachedEntity )
end
return Prop
end
duplicator.RegisterEntityClass( "prop_effect", MakeEffect, "Model", "Data" )
--[[---------------------------------------------------------
Name: FixInvalidPhysicsObject
Desc: Attempts to detect and correct the physics object
on models such as the TF2 Turrets
-----------------------------------------------------------]]
function FixInvalidPhysicsObject( prop )
local PhysObj = prop:GetPhysicsObject()
if ( !IsValid( PhysObj ) ) then return end
local min, max = PhysObj:GetAABB()
if ( !min or !max ) then return end
local PhysSize = ( min - max ):Length()
if ( PhysSize > 5 ) then return end
local mins = prop:OBBMins()
local maxs = prop:OBBMaxs()
if ( !mins or !maxs ) then return end
local ModelSize = ( mins - maxs ):Length()
local Difference = math.abs( ModelSize - PhysSize )
if ( Difference < 10 ) then return end
-- This physics object is definitiely weird.
-- Make a new one.
prop:PhysicsInitBox( mins, maxs )
prop:SetCollisionGroup( COLLISION_GROUP_DEBRIS )
-- Check for success
PhysObj = prop:GetPhysicsObject()
if ( !IsValid( PhysObj ) ) then return end
PhysObj:SetMass( 100 )
PhysObj:Wake()
end
--[[---------------------------------------------------------
Name: GMODSpawnProp - player spawns a prop
-----------------------------------------------------------]]
function GMODSpawnProp( ply, model, iSkin, strBody )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnProp", ply, model ) ) then return end
local e = DoPlayerEntitySpawn( ply, "prop_physics", model, iSkin, strBody )
if ( !IsValid( e ) ) then return end
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedProp", ply, model, e )
end
-- This didn't work out - todo: Find a better way.
--timer.Simple( 0.01, CheckPropSolid, e, COLLISION_GROUP_NONE, COLLISION_GROUP_WORLD )
FixInvalidPhysicsObject( e )
DoPropSpawnedEffect( e )
undo.Create( "Prop" )
undo.SetPlayer( ply )
undo.AddEntity( e )
undo.Finish( "Prop (" .. tostring( model ) .. ")" )
ply:AddCleanup( "props", e )
end
--[[---------------------------------------------------------
Name: GMODSpawnEffect
-----------------------------------------------------------]]
function GMODSpawnEffect( ply, model, iSkin, strBody )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnEffect", ply, model ) ) then return end
local e = DoPlayerEntitySpawn( ply, "prop_effect", model, iSkin, strBody )
if ( !IsValid( e ) ) then return end
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedEffect", ply, model, e )
end
if ( IsValid( e.AttachedEntity ) ) then
DoPropSpawnedEffect( e.AttachedEntity )
end
undo.Create( "Effect" )
undo.SetPlayer( ply )
undo.AddEntity( e )
undo.Finish( "Effect (" .. tostring( model ) .. ")" )
ply:AddCleanup( "effects", e )
end
--[[---------------------------------------------------------
Name: DoPlayerEntitySpawn
Desc: Utility function for player entity spawning functions
-----------------------------------------------------------]]
function DoPlayerEntitySpawn( ply, entity_name, model, iSkin, strBody )
local vStart = ply:GetShootPos()
local vForward = ply:GetAimVector()
local trace = {}
trace.start = vStart
trace.endpos = vStart + ( vForward * 2048 )
trace.filter = ply
local tr = util.TraceLine( trace )
-- Prevent spawning too close
--[[if ( !tr.Hit or tr.Fraction < 0.05 ) then
return
end]]
local ent = ents.Create( entity_name )
if ( !IsValid( ent ) ) then return end
local ang = ply:EyeAngles()
ang.yaw = ang.yaw + 180 -- Rotate it 180 degrees in my favour
ang.roll = 0
ang.pitch = 0
if ( entity_name == "prop_ragdoll" ) then
ang.pitch = -90
tr.HitPos = tr.HitPos
end
ent:SetModel( model )
ent:SetSkin( iSkin )
ent:SetAngles( ang )
if ( strBody ) then ent:SetBodyGroups( strBody ) end
ent:SetPos( tr.HitPos )
ent:Spawn()
ent:Activate()
-- Special case for effects
if ( strBody && entity_name == "prop_effect" && IsValid( ent.AttachedEntity ) ) then
ent.AttachedEntity:SetBodyGroups( strBody )
end
-- Attempt to move the object so it sits flush
-- We could do a TraceEntity instead of doing all
-- of this - but it feels off after the old way
local vFlushPoint = tr.HitPos - ( tr.HitNormal * 512 ) -- Find a point that is definitely out of the object in the direction of the floor
vFlushPoint = ent:NearestPoint( vFlushPoint ) -- Find the nearest point inside the object to that point
vFlushPoint = ent:GetPos() - vFlushPoint -- Get the difference
vFlushPoint = tr.HitPos + vFlushPoint -- Add it to our target pos
if ( entity_name != "prop_ragdoll" ) then
-- Set new position
ent:SetPos( vFlushPoint )
ply:SendLua( "achievements.SpawnedProp()" )
else
-- With ragdolls we need to move each physobject
local VecOffset = vFlushPoint - ent:GetPos()
for i = 0, ent:GetPhysicsObjectCount() - 1 do
local phys = ent:GetPhysicsObjectNum( i )
phys:SetPos( phys:GetPos() + VecOffset )
end
ply:SendLua( "achievements.SpawnedRagdoll()" )
end
TryFixPropPosition( ply, ent, tr.HitPos )
return ent
end
local function InternalSpawnNPC( NPCData, ply, Position, Normal, Class, Equipment, SpawnFlagsSaved, NoDropToFloor )
-- Don't let them spawn this entity if it isn't in our NPC Spawn list.
-- We don't want them spawning any entity they like!
if ( !NPCData ) then return end
local isAdmin = ( IsValid( ply ) && ply:IsAdmin() ) or game.SinglePlayer()
if ( NPCData.AdminOnly && !isAdmin ) then return end
local bDropToFloor = false
local wasSpawnedOnCeiling = false
local wasSpawnedOnFloor = false
--
-- This NPC has to be spawned on a ceiling (Barnacle) or a floor (Turrets)
--
if ( NPCData.OnCeiling or NPCData.OnFloor ) then
local isOnCeiling = Vector( 0, 0, -1 ):Dot( Normal ) >= 0.95
local isOnFloor = Vector( 0, 0, 1 ):Dot( Normal ) >= 0.95
-- Not on ceiling, and we can't be on floor
if ( !isOnCeiling && !NPCData.OnFloor ) then return end
-- Not on floor, and we can't be on ceiling
if ( !isOnFloor && !NPCData.OnCeiling ) then return end
-- We can be on either, and we are on neither
if ( !isOnFloor && !isOnCeiling ) then return end
wasSpawnedOnCeiling = isOnCeiling
wasSpawnedOnFloor = isOnFloor
else
bDropToFloor = true
end
if ( NPCData.NoDrop or NoDropToFloor ) then bDropToFloor = false end
-- Create NPC
local NPC = ents.Create( NPCData.Class )
if ( !IsValid( NPC ) ) then return end
--
-- Offset the position
--
local Offset = NPCData.Offset or 32
NPC:SetPos( Position + Normal * Offset )
-- Rotate to face player (expected behaviour)
local Angles = Angle( 0, 0, 0 )
if ( IsValid( ply ) ) then
Angles = ply:GetAngles()
end
Angles.pitch = 0
Angles.roll = 0
Angles.yaw = Angles.yaw + 180
if ( NPCData.Rotate ) then Angles = Angles + NPCData.Rotate end
NPC:SetAngles( Angles )
if ( NPCData.SnapToNormal ) then
NPC:SetAngles( Normal:Angle() )
end
--
-- Does this NPC have a specified model? If so, use it.
--
if ( NPCData.Model ) then
NPC:SetModel( NPCData.Model )
end
--
-- Does this NPC have a specified material? If so, use it.
--
if ( NPCData.Material ) then
NPC:SetMaterial( NPCData.Material )
end
--
-- Spawn Flags
--
local SpawnFlags = bit.bor( SF_NPC_FADE_CORPSE, SF_NPC_ALWAYSTHINK )
if ( NPCData.SpawnFlags ) then SpawnFlags = bit.bor( SpawnFlags, NPCData.SpawnFlags ) end
if ( NPCData.TotalSpawnFlags ) then SpawnFlags = NPCData.TotalSpawnFlags end
if ( SpawnFlagsSaved ) then SpawnFlags = SpawnFlagsSaved end
NPC:SetKeyValue( "spawnflags", SpawnFlags )
NPC.SpawnFlags = SpawnFlags
--
-- Optional Key Values
--
if ( NPCData.KeyValues ) then
for k, v in pairs( NPCData.KeyValues ) do
NPC:SetKeyValue( k, v )
end
end
--
-- Does this NPC have a specified skin? If so, use it.
--
if ( NPCData.Skin ) then
NPC:SetSkin( NPCData.Skin )
end
--
-- What weapon this NPC should be carrying
--
-- Check if this is a valid weapon from the list, or the user is trying to fool us.
local valid = false
for _, v in pairs( list.Get( "NPCUsableWeapons" ) ) do
if ( v.class == Equipment ) then valid = true break end
end
for _, v in pairs( NPCData.Weapons or {} ) do
if ( v == Equipment ) then valid = true break end
end
if ( Equipment && Equipment != "none" && valid ) then
NPC:SetKeyValue( "additionalequipment", Equipment )
NPC.Equipment = Equipment
end
if ( wasSpawnedOnCeiling && isfunction( NPCData.OnCeiling ) ) then
NPCData.OnCeiling( NPC )
elseif ( wasSpawnedOnFloor && isfunction( NPCData.OnFloor ) ) then
NPCData.OnFloor( NPC )
end
-- Allow special case for duplicator stuff
if ( isfunction( NPCData.OnDuplicated ) ) then
NPC.OnDuplicated = NPCData.OnDuplicated
end
DoPropSpawnedEffect( NPC )
NPC:Spawn()
NPC:Activate()
-- Store spawnmenu data for addons and stuff
NPC.NPCName = Class
NPC.NPCTable = NPCData
NPC._wasSpawnedOnCeiling = wasSpawnedOnCeiling
-- For those NPCs that set their model/skin in Spawn function
-- We have to keep the call above for NPCs that want a model set by Spawn() time
-- BAD: They may adversly affect entity collision bounds
if ( NPCData.Model && NPC:GetModel():lower() != NPCData.Model:lower() ) then
NPC:SetModel( NPCData.Model )
end
if ( NPCData.Skin ) then
NPC:SetSkin( NPCData.Skin )
end
if ( bDropToFloor ) then
NPC:DropToFloor()
end
if ( NPCData.Health ) then
NPC:SetHealth( NPCData.Health )
NPC:SetMaxHealth( NPCData.Health )
end
-- Body groups
if ( NPCData.BodyGroups ) then
for k, v in pairs( NPCData.BodyGroups ) do
NPC:SetBodygroup( k, v )
end
end
return NPC
end
function Spawn_NPC( ply, NPCClassName, WeaponName, tr )
-- We don't support this command from dedicated server console
if ( !IsValid( ply ) ) then return end
if ( !NPCClassName ) then return end
-- Give the gamemode an opportunity to deny spawning
if ( !gamemode.Call( "PlayerSpawnNPC", ply, NPCClassName, WeaponName ) ) then return end
if ( !tr ) then
local vStart = ply:GetShootPos()
local vForward = ply:GetAimVector()
tr = util.TraceLine( {
start = vStart,
endpos = vStart + ( vForward * 2048 ),
filter = ply
} )
end
local NPCData = list.Get( "NPC" )[ NPCClassName ]
-- Create the NPC if you can.
local SpawnedNPC = InternalSpawnNPC( NPCData, ply, tr.HitPos, tr.HitNormal, NPCClassName, WeaponName )
if ( !IsValid( SpawnedNPC ) ) then return end
TryFixPropPosition( ply, SpawnedNPC, tr.HitPos )
-- Give the gamemode an opportunity to do whatever
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedNPC", ply, SpawnedNPC )
end
-- See if we can find a nice name for this NPC..
local NiceName = nil
if ( NPCData ) then
NiceName = NPCData.Name
end
-- Add to undo list
undo.Create( "NPC" )
undo.SetPlayer( ply )
undo.AddEntity( SpawnedNPC )
if ( NiceName ) then
undo.SetCustomUndoText( "Undone " .. NiceName )
end
undo.Finish( "NPC (" .. tostring( NPCClassName ) .. ")" )
-- And cleanup
ply:AddCleanup( "npcs", SpawnedNPC )
ply:SendLua( "achievements.SpawnedNPC()" )
end
concommand.Add( "gmod_spawnnpc", function( ply, cmd, args ) Spawn_NPC( ply, args[ 1 ], args[ 2 ] ) end )
-- This should be in base_npcs.lua really
local function GenericNPCDuplicator( ply, mdl, class, equipment, spawnflags, data )
-- Match the behavior of Spawn_NPC above - class should be the one in the list, NOT the entity class!
if ( data.NPCName ) then class = data.NPCName end
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnNPC", ply, class, equipment ) ) then return end
local NPCData = list.Get( "NPC" )[ class ]
-- I don't think we are ready for this
-- if ( !NPCData ) then NPCData = data.NPCTable end
local normal = Vector( 0, 0, 1 )
if ( NPCData && NPCData.OnCeiling && ( NPCData.OnFloor && data._wasSpawnedOnCeiling or !NPCData.OnFloor ) ) then
normal = Vector( 0, 0, -1 )
end
local ent = InternalSpawnNPC( NPCData, ply, data.Pos, normal, class, equipment, spawnflags, true )
if ( IsValid( ent ) ) then
local pos = ent:GetPos() -- Hack! Prevents the NPCs from falling through the floor
duplicator.DoGeneric( ent, data )
if ( NPCData && !NPCData.OnCeiling && !NPCData.NoDrop ) then
ent:SetPos( pos )
end
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedNPC", ply, ent )
ply:AddCleanup( "npcs", ent )
end
if ( data.CurHealth ) then ent:SetHealth( data.CurHealth ) end
if ( data.MaxHealth ) then ent:SetMaxHealth( data.MaxHealth ) end
table.Merge( ent:GetTable(), data )
end
return ent
end
-- Huuuuuuuuhhhh
local function AddNPCToDuplicator( class ) duplicator.RegisterEntityClass( class, GenericNPCDuplicator, "Model", "Class", "Equipment", "SpawnFlags", "Data" ) end
-- HL2
AddNPCToDuplicator( "npc_alyx" )
AddNPCToDuplicator( "npc_breen" )
AddNPCToDuplicator( "npc_kleiner" )
AddNPCToDuplicator( "npc_antlion" )
AddNPCToDuplicator( "npc_antlionguard" )
AddNPCToDuplicator( "npc_barnacle" )
AddNPCToDuplicator( "npc_barney" )
AddNPCToDuplicator( "npc_combine_s" )
AddNPCToDuplicator( "npc_crow" )
AddNPCToDuplicator( "npc_cscanner" )
AddNPCToDuplicator( "npc_clawscanner" )
AddNPCToDuplicator( "npc_dog" )
AddNPCToDuplicator( "npc_eli" )
AddNPCToDuplicator( "npc_gman" )
AddNPCToDuplicator( "npc_headcrab" )
AddNPCToDuplicator( "npc_headcrab_black" )
AddNPCToDuplicator( "npc_headcrab_poison" )
AddNPCToDuplicator( "npc_headcrab_fast" )
AddNPCToDuplicator( "npc_manhack" )
AddNPCToDuplicator( "npc_metropolice" )
AddNPCToDuplicator( "npc_monk" )
AddNPCToDuplicator( "npc_mossman" )
AddNPCToDuplicator( "npc_pigeon" )
AddNPCToDuplicator( "npc_rollermine" )
AddNPCToDuplicator( "npc_strider" )
AddNPCToDuplicator( "npc_helicopter" )
AddNPCToDuplicator( "npc_combinegunship" )
AddNPCToDuplicator( "npc_combinedropship" )
AddNPCToDuplicator( "npc_turret_ceiling" )
AddNPCToDuplicator( "npc_combine_camera" )
AddNPCToDuplicator( "npc_turret_floor" )
AddNPCToDuplicator( "npc_vortigaunt" )
AddNPCToDuplicator( "npc_sniper" )
AddNPCToDuplicator( "npc_seagull" )
AddNPCToDuplicator( "npc_citizen" )
AddNPCToDuplicator( "npc_stalker" )
AddNPCToDuplicator( "npc_zombie" )
AddNPCToDuplicator( "npc_zombie_torso" )
AddNPCToDuplicator( "npc_zombine" )
AddNPCToDuplicator( "npc_poisonzombie" )
AddNPCToDuplicator( "npc_fastzombie" )
AddNPCToDuplicator( "npc_fastzombie_torso" )
-- EP2
AddNPCToDuplicator( "npc_hunter" )
AddNPCToDuplicator( "npc_antlion_worker" )
AddNPCToDuplicator( "npc_antlion_grub" )
AddNPCToDuplicator( "npc_magnusson" )
AddNPCToDuplicator( "npc_fisherman" )
-- HL1
AddNPCToDuplicator( "monster_alien_grunt" )
AddNPCToDuplicator( "monster_alien_slave" )
AddNPCToDuplicator( "monster_alien_controller" )
AddNPCToDuplicator( "monster_barney" )
AddNPCToDuplicator( "monster_bigmomma" )
AddNPCToDuplicator( "monster_bullchicken" )
AddNPCToDuplicator( "monster_babycrab" )
AddNPCToDuplicator( "monster_cockroach" )
AddNPCToDuplicator( "monster_houndeye" )
AddNPCToDuplicator( "monster_headcrab" )
AddNPCToDuplicator( "monster_gargantua" )
AddNPCToDuplicator( "monster_human_assassin" )
AddNPCToDuplicator( "monster_human_grunt" )
AddNPCToDuplicator( "monster_scientist" )
AddNPCToDuplicator( "monster_snark" )
AddNPCToDuplicator( "monster_nihilanth" )
AddNPCToDuplicator( "monster_tentacle" )
AddNPCToDuplicator( "monster_zombie" )
AddNPCToDuplicator( "monster_turret" )
AddNPCToDuplicator( "monster_miniturret" )
AddNPCToDuplicator( "monster_sentry" )
-- Portal
AddNPCToDuplicator( "npc_portal_turret_floor" )
AddNPCToDuplicator( "npc_rocket_turret" )
AddNPCToDuplicator( "npc_security_camera" )
--[[---------------------------------------------------------
Name: CanPlayerSpawnSENT
-----------------------------------------------------------]]
local function CanPlayerSpawnSENT( ply, EntityName )
local isAdmin = ( IsValid( ply ) && ply:IsAdmin() ) or game.SinglePlayer()
-- Make sure that given EntityName is actually a SENT
local sent = scripted_ents.GetStored( EntityName )
if ( sent == nil ) then
-- Is the entity spawnable?
local SpawnableEntities = list.Get( "SpawnableEntities" )
if ( !SpawnableEntities ) then return false end
local EntTable = SpawnableEntities[ EntityName ]
if ( !EntTable ) then return false end
if ( EntTable.AdminOnly && !isAdmin ) then return false end
return true
end
-- We need a spawn function. The SENT can then spawn itself properly
local SpawnFunction = scripted_ents.GetMember( EntityName, "SpawnFunction" )
if ( !isfunction( SpawnFunction ) ) then return false end
-- You're not allowed to spawn this unless you're an admin!
if ( !scripted_ents.GetMember( EntityName, "Spawnable" ) && !isAdmin ) then return false end
if ( scripted_ents.GetMember( EntityName, "AdminOnly" ) && !isAdmin ) then return false end
return true
end
--[[---------------------------------------------------------
Name: Spawn_SENT
Desc: Console Command for a player to spawn different items
-----------------------------------------------------------]]
function Spawn_SENT( ply, EntityName, tr )
-- We don't support this command from dedicated server console
if ( !IsValid( ply ) ) then return end
if ( EntityName == nil ) then return end
if ( !CanPlayerSpawnSENT( ply, EntityName ) ) then return end
-- Ask the gamemode if it's OK to spawn this
if ( !gamemode.Call( "PlayerSpawnSENT", ply, EntityName ) ) then return end
if ( !tr ) then
local vStart = ply:EyePos()
local vForward = ply:GetAimVector()
tr = util.TraceLine( {
start = vStart,
endpos = vStart + ( vForward * 4096 ),
filter = ply
} )
end
local entity = nil
local PrintName = nil
local sent = scripted_ents.GetStored( EntityName )
if ( sent ) then
local sentTable = sent.t
ClassName = EntityName
local SpawnFunction = scripted_ents.GetMember( EntityName, "SpawnFunction" )
if ( !SpawnFunction ) then return end -- Fallback to default behavior below?
entity = SpawnFunction( sentTable, ply, tr, EntityName )
if ( IsValid( entity ) ) then
entity:SetCreator( ply )
end
ClassName = nil
PrintName = sentTable.PrintName
else
-- Spawn from list table
local SpawnableEntities = list.Get( "SpawnableEntities" )
if ( !SpawnableEntities ) then return end
local EntTable = SpawnableEntities[ EntityName ]
if ( !EntTable ) then return end
PrintName = EntTable.PrintName
local SpawnPos = tr.HitPos + tr.HitNormal * 16
if ( EntTable.NormalOffset ) then SpawnPos = SpawnPos + tr.HitNormal * EntTable.NormalOffset end
-- Make sure the spawn position is not out of bounds
local oobTr = util.TraceLine( {
start = tr.HitPos,
endpos = SpawnPos,
mask = MASK_SOLID_BRUSHONLY
} )
if ( oobTr.Hit ) then
SpawnPos = oobTr.HitPos + oobTr.HitNormal * ( tr.HitPos:Distance( oobTr.HitPos ) / 2 )
end
entity = ents.Create( EntTable.ClassName )
entity:SetPos( SpawnPos )
if ( EntTable.KeyValues ) then
for k, v in pairs( EntTable.KeyValues ) do
entity:SetKeyValue( k, v )
end
end
if ( EntTable.Material ) then
entity:SetMaterial( EntTable.Material )
end
entity:Spawn()
entity:Activate()
DoPropSpawnedEffect( entity )
if ( EntTable.DropToFloor ) then
entity:DropToFloor()
end
end
if ( !IsValid( entity ) ) then return end
TryFixPropPosition( ply, entity, tr.HitPos )
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedSENT", ply, entity )
end
undo.Create( "SENT" )
undo.SetPlayer( ply )
undo.AddEntity( entity )
if ( PrintName ) then
undo.SetCustomUndoText( "Undone " .. PrintName )
end
undo.Finish( "Scripted Entity (" .. tostring( EntityName ) .. ")" )
ply:AddCleanup( "sents", entity )
entity:SetVar( "Player", ply )
end
concommand.Add( "gm_spawnsent", function( ply, cmd, args ) Spawn_SENT( ply, args[ 1 ] ) end )
--[[---------------------------------------------------------
-- Give a swep.
-----------------------------------------------------------]]
function CCGiveSWEP( ply, command, arguments )
-- We don't support this command from dedicated server console
if ( !IsValid( ply ) ) then return end
if ( arguments[1] == nil ) then return end
if ( !ply:Alive() ) then return end
-- Make sure this is a SWEP
local swep = list.Get( "Weapon" )[ arguments[1] ]
if ( swep == nil ) then return end
-- You're not allowed to spawn this!
local isAdmin = ply:IsAdmin() or game.SinglePlayer()
if ( ( !swep.Spawnable && !isAdmin ) or ( swep.AdminOnly && !isAdmin ) ) then
return
end
if ( !gamemode.Call( "PlayerGiveSWEP", ply, arguments[1], swep ) ) then return end
if ( !ply:HasWeapon( swep.ClassName ) ) then
MsgAll( "Giving " .. ply:Nick() .. " a " .. swep.ClassName .. "\n" )
ply:Give( swep.ClassName )
end
-- And switch to it
ply:SelectWeapon( swep.ClassName )
end
concommand.Add( "gm_giveswep", CCGiveSWEP )
--[[---------------------------------------------------------
-- Spawn a SWEP on the ground
-----------------------------------------------------------]]
function Spawn_Weapon( ply, wepname, tr )
-- We don't support this command from dedicated server console
if ( !IsValid( ply ) ) then return end
if ( wepname == nil ) then return end
local swep = list.Get( "Weapon" )[ wepname ]
-- Make sure this is a SWEP
if ( swep == nil ) then return end
-- You're not allowed to spawn this!
local isAdmin = ply:IsAdmin() or game.SinglePlayer()
if ( ( !swep.Spawnable && !isAdmin ) or ( swep.AdminOnly && !isAdmin ) ) then
return
end
if ( !gamemode.Call( "PlayerSpawnSWEP", ply, wepname, swep ) ) then return end
if ( !tr ) then
tr = ply:GetEyeTraceNoCursor()
end
if ( !tr.Hit ) then return end
local entity = ents.Create( swep.ClassName )
if ( !IsValid( entity ) ) then return end
DoPropSpawnedEffect( entity )
local SpawnPos = tr.HitPos + tr.HitNormal * 32
-- Make sure the spawn position is not out of bounds
local oobTr = util.TraceLine( {
start = tr.HitPos,
endpos = SpawnPos,
mask = MASK_SOLID_BRUSHONLY
} )
if ( oobTr.Hit ) then
SpawnPos = oobTr.HitPos + oobTr.HitNormal * ( tr.HitPos:Distance( oobTr.HitPos ) / 2 )
end
entity:SetPos( SpawnPos )
entity:Spawn()
undo.Create( "SWEP" )
undo.SetPlayer( ply )
undo.AddEntity( entity )
undo.SetCustomUndoText( "Undone " .. tostring( swep.PrintName ) )
undo.Finish( "Scripted Weapon (" .. tostring( swep.ClassName ) .. ")" )
-- Throw it into SENTs category
ply:AddCleanup( "sents", entity )
TryFixPropPosition( ply, entity, tr.HitPos )
gamemode.Call( "PlayerSpawnedSWEP", ply, entity )
end
concommand.Add( "gm_spawnswep", function( ply, cmd, args ) Spawn_Weapon( ply, args[1] ) end )
-- Do not allow people to undo weapons from player's hands
hook.Add( "WeaponEquip", "SpawnWeaponUndoRemoval", function( wep, ply )
undo.ReplaceEntity( wep, nil )
cleanup.ReplaceEntity( wep, nil )
end )
local function MakeVehicle( ply, Pos, Ang, model, Class, VName, VTable, data )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnVehicle", ply, model, VName, VTable ) ) then return end
local Ent = ents.Create( Class )
if ( !IsValid( Ent ) ) then return NULL end
duplicator.DoGeneric( Ent, data )
Ent:SetModel( model )
-- Fallback vehiclescripts for HL2 maps ( dupe support )
if ( model == "models/buggy.mdl" ) then Ent:SetKeyValue( "vehiclescript", "scripts/vehicles/jeep_test.txt" ) end
if ( model == "models/vehicle.mdl" ) then Ent:SetKeyValue( "vehiclescript", "scripts/vehicles/jalopy.txt" ) end
-- Fill in the keyvalues if we have them
if ( VTable && VTable.KeyValues ) then
for k, v in pairs( VTable.KeyValues ) do
local kLower = string.lower( k )
if ( kLower == "vehiclescript" or
kLower == "limitview" or
kLower == "vehiclelocked" or
kLower == "cargovisible" or
kLower == "enablegun" )
then
Ent:SetKeyValue( k, v )
end
end
end
Ent:SetAngles( Ang )
Ent:SetPos( Pos )
DoPropSpawnedEffect( Ent )
Ent:Spawn()
Ent:Activate()
-- Some vehicles reset this in Spawn()
if ( data && data.ColGroup ) then Ent:SetCollisionGroup( data.ColGroup ) end
-- Store spawnmenu data for addons and stuff
if ( Ent.SetVehicleClass && VName ) then Ent:SetVehicleClass( VName ) end
Ent.VehicleName = VName
Ent.VehicleTable = VTable
-- We need to override the class in the case of the Jeep, because it
-- actually uses a different class than is reported by GetClass
Ent.ClassOverride = Class
if ( IsValid( ply ) ) then
gamemode.Call( "PlayerSpawnedVehicle", ply, Ent )
end
return Ent
end
duplicator.RegisterEntityClass( "prop_vehicle_jeep_old", MakeVehicle, "Pos", "Ang", "Model", "Class", "VehicleName", "VehicleTable", "Data" )
duplicator.RegisterEntityClass( "prop_vehicle_jeep", MakeVehicle, "Pos", "Ang", "Model", "Class", "VehicleName", "VehicleTable", "Data" )
duplicator.RegisterEntityClass( "prop_vehicle_airboat", MakeVehicle, "Pos", "Ang", "Model", "Class", "VehicleName", "VehicleTable", "Data" )
duplicator.RegisterEntityClass( "prop_vehicle_prisoner_pod", MakeVehicle, "Pos", "Ang", "Model", "Class", "VehicleName", "VehicleTable", "Data" )
--[[---------------------------------------------------------
Name: Spawn_Vehicle
Desc: Player attempts to spawn vehicle
-----------------------------------------------------------]]
function Spawn_Vehicle( ply, vname, tr )
-- We don't support this command from dedicated server console
if ( !IsValid( ply ) ) then return end
if ( !vname ) then return end
local VehicleList = list.Get( "Vehicles" )
local vehicle = VehicleList[ vname ]
-- Not a valid vehicle to be spawning..
if ( !vehicle ) then return end
if ( !tr ) then
tr = ply:GetEyeTraceNoCursor()
end
local Angles = ply:GetAngles()
Angles.pitch = 0
Angles.roll = 0
Angles.yaw = Angles.yaw + 180
local pos = tr.HitPos
if ( vehicle.Offset ) then
pos = pos + tr.HitNormal * vehicle.Offset
end
local Ent = MakeVehicle( ply, pos, Angles, vehicle.Model, vehicle.Class, vname, vehicle )
if ( !IsValid( Ent ) ) then return end
-- Unstable for Jeeps
-- TryFixPropPosition( ply, Ent, tr.HitPos )
if ( vehicle.Members ) then
table.Merge( Ent, vehicle.Members )
duplicator.StoreEntityModifier( Ent, "VehicleMemDupe", vehicle.Members )
end
undo.Create( "Vehicle" )
undo.SetPlayer( ply )
undo.AddEntity( Ent )
undo.SetCustomUndoText( "Undone " .. vehicle.Name )
undo.Finish( "Vehicle (" .. tostring( vehicle.Name ) .. ")" )
ply:AddCleanup( "vehicles", Ent )
end
concommand.Add( "gm_spawnvehicle", function( ply, cmd, args ) Spawn_Vehicle( ply, args[1] ) end )
local function VehicleMemDupe( ply, ent, Data )
table.Merge( ent, Data )
end
duplicator.RegisterEntityModifier( "VehicleMemDupe", VehicleMemDupe )