--[[ | 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 )