-- Ship resource.AddWorkshop("417565863") -- Send required file to clients AddCSLuaFile("sh_init.lua") AddCSLuaFile("sh_player.lua") AddCSLuaFile("cl_tauntwindow.lua") AddCSLuaFile("cl_init.lua") AddCSLuaFile("cl_hud_mask.lua") AddCSLuaFile("cl_hud.lua") AddCSLuaFile("cl_menu.lua") AddCSLuaFile("cl_targetid.lua") AddCSLuaFile("cl_autotaunt.lua") AddCSLuaFile("cl_credits.lua") -- Include the required lua files include("sv_networkfunctions.lua") include("sh_init.lua") include("sh_config.lua") include("sv_admin.lua") include("sv_autotaunt.lua") include("sv_tauntwindow.lua") include("sv_bbox_addition.lua") -- Server only constants PHE.EXPLOITABLE_DOORS = { "func_door", "prop_door_rotating", "func_door_rotating" } -- Voice Control Constant init PHE.VOICE_IS_END_ROUND = 0 -- Update cvar to variables changes every so seconds PHE.UPDATE_CVAR_TO_VARIABLE = 0 -- Spectator check PHE.SPECTATOR_CHECK = 0 -- Player Join/Leave message gameevent.Listen( "player_connect" ) hook.Add( "player_connect", "AnnouncePLJoin", function( data ) for k, v in pairs( player.GetAll() ) do v:PrintMessage( HUD_PRINTTALK, data.name .. " has connected to the server." ) end end ) gameevent.Listen( "player_disconnect" ) hook.Add( "player_disconnect", "AnnouncePLLeave", function( data ) for k,v in pairs( player.GetAll() ) do v:PrintMessage( HUD_PRINTTALK, data.name .. " has left the server (Reason: " .. data.reason ..")" ) end end ) -- Force Close taunt window function, determined whenever the round ends, or team winning. local function ForceCloseTauntWindow(num) if num == 1 then net.Start("PH_ForceCloseTauntWindow") net.Broadcast() elseif num == 0 then net.Start("PH_AllowTauntWindow") net.Broadcast() end end -- Called alot function GM:CheckPlayerDeathRoundEnd() if !GAMEMODE.RoundBased || !GAMEMODE:InRound() then return end local Teams = GAMEMODE:GetTeamAliveCounts() if table.Count(Teams) == 0 then GAMEMODE:RoundEndWithResult(1001, "Draw, everyone loses!") PHE.VOICE_IS_END_ROUND = 1 ForceCloseTauntWindow(1) net.Start("PH_RoundDraw_Snd") net.Broadcast() hook.Call("PH_OnRoundDraw", nil) return end if table.Count(Teams) == 1 then local TeamID = table.GetFirstKey(Teams) -- debug MsgAll("Round Result: "..team.GetName(TeamID).." ("..TeamID..") Wins!\n") -- End Round GAMEMODE:RoundEndWithResult(TeamID, team.GetName(TeamID).." win!") PHE.VOICE_IS_END_ROUND = 1 ForceCloseTauntWindow(1) -- send the win notification if TeamID == TEAM_HUNTERS then net.Start("PH_TeamWinning_Snd") net.WriteString(PHE.WINNINGSOUNDS[TEAM_HUNTERS]) net.Broadcast() elseif TeamID == TEAM_PROPS then net.Start("PH_TeamWinning_Snd") net.WriteString(PHE.WINNINGSOUNDS[TEAM_PROPS]) net.Broadcast() end hook.Call("PH_OnRoundWinTeam", nil, TeamID) return end end -- Player Voice & Chat Control to prevent Metagaming. (As requested by some server owners/suggestors.) -- You can disable this feature by typing 'sv_alltalk 1' in console to make everyone can hear. -- Control Player Voice function GM:PlayerCanHearPlayersVoice(listen, speaker) local alltalk_cvar = GetConVar("sv_alltalk"):GetInt() if (alltalk_cvar > 0) then return true, false end -- prevent Loopback check. if (listen == speaker) then return false, false end -- Only alive players can listen other living players. if listen:Alive() && speaker:Alive() then return true, false end -- Event: On Round Start. Living Players don't listen to dead players. if PHE.VOICE_IS_END_ROUND == 0 && listen:Alive() && !speaker:Alive() then return false, false end -- Listen to all dead players while you dead. if !listen:Alive() && !speaker:Alive() then return true, false end -- However, Living players can be heard from dead players. if !listen:Alive() && speaker:Alive() then return true, false end -- Event: On Round End/Time End. Listen to everyone. if PHE.VOICE_IS_END_ROUND == 1 && listen:Alive() && !speaker:Alive() then return true, false end -- Spectator can only read from themselves. if listen:Team() == TEAM_SPECTATOR && listen:Alive() && speaker:Alive() then return false, false end -- This is for ULX "Permanent Gag". Uncomment this if you have some issues. -- if speaker:GetPData( "permgagged" ) == "true" then return false, false end -- does return true, true required here? end -- Control Players Chat function GM:PlayerCanSeePlayersChat(txt, onteam, listen, speaker) if ( onteam ) then -- Generic Specific OnTeam chats if ( !IsValid( speaker ) || !IsValid( listen ) ) then return false end if ( listen:Team() != speaker:Team() ) then return false end -- ditto, this is same as below. if listen:Alive() && speaker:Alive() then return true end if PHE.VOICE_IS_END_ROUND == 0 && listen:Alive() && !speaker:Alive() then return false end if !listen:Alive() && !speaker:Alive() then return true end if !listen:Alive() && speaker:Alive() then return true end if PHE.VOICE_IS_END_ROUND == 1 && listen:Alive() && !speaker:Alive() then return true end if listen:Team() == TEAM_SPECTATOR && listen:Alive() && speaker:Alive() then return false end end local alltalk_cvar = GetConVar("sv_alltalk"):GetInt() if (alltalk_cvar > 0) then return true end -- Generic Checks if ( !IsValid( speaker ) || !IsValid( listen ) ) then return false end -- Only alive players can see other living players. if listen:Alive() && speaker:Alive() then return true end -- Event: On Round Start. Living Players don't see dead players' chat. if PHE.VOICE_IS_END_ROUND == 0 && listen:Alive() && !speaker:Alive() then return false end -- See Chat to all dead players while you dead. if !listen:Alive() && !speaker:Alive() then return true end -- However, Living players' chat can be seen from dead players. if !listen:Alive() && speaker:Alive() then return true end -- Event: On Round End/Time End. See Chat to everyone. if PHE.VOICE_IS_END_ROUND == 1 && listen:Alive() && !speaker:Alive() then return true end -- Spectator can only read from themselves. if listen:Team() == TEAM_SPECTATOR && listen:Alive() && speaker:Alive() then return false end return true end -- Called when an entity takes damage function EntityTakeDamage(ent, dmginfo) local att = dmginfo:GetAttacker() -- Code from: https://facepunch.com/showthread.php?t=1500179 , Special thanks from AlcoholicoDrogadicto(http://steamcommunity.com/profiles/76561198082241865/) for suggesting this. if GAMEMODE:InRound() && ent && ent:IsPlayer() && ent:Alive() && ent:Team() == TEAM_PROPS && ent.ph_prop then -- Prevent Prop 'Friendly Fire' if ( dmginfo:GetAttacker():IsPlayer() && dmginfo:GetAttacker():Team() == ent:Team() ) then printVerbose("DMGINFO::ATTACKED!!-> "..tostring(dmginfo:GetAttacker())..", DMGTYPE: "..dmginfo:GetDamageType()) return end --Debug purpose. printVerbose("!! " .. ent:Name() .. "'s PLAYER entity appears to have taken damage, we can redirect it to the prop! (Model is: " .. ent.ph_prop:GetModel() .. ")") ent.ph_prop:TakeDamageInfo(dmginfo) return end if GAMEMODE:InRound() && ent && (ent:GetClass() != "ph_prop" && ent:GetClass() != "func_breakable" && ent:GetClass() != "prop_door_rotating" && ent:GetClass() != "prop_dynamic*") && !ent:IsPlayer() && att && att:IsPlayer() && att:Team() == TEAM_HUNTERS && att:Alive() then if att:Armor() >= 5 && GetConVar("ph_hunter_fire_penalty"):GetInt() >= 5 then att:SetHealth(att:Health() - (math.Round(GetConVar("ph_hunter_fire_penalty"):GetInt()/2))) att:SetArmor(att:Armor() - 15) if att:Armor() < 0 then att:SetArmor(0) end else att:SetHealth(att:Health() - GetConVar("ph_hunter_fire_penalty"):GetInt()) end if att:Health() <= 0 then MsgAll(att:Name() .. " felt guilty for hurting so many innocent props and committed suicide\n") att:Kill() hook.Call("PH_HunterDeathPenalty", nil, att) end end end hook.Add("EntityTakeDamage", "PH_EntityTakeDamage", EntityTakeDamage) -- Called when player tries to pickup a weapon function GM:PlayerCanPickupWeapon(pl, ent) if pl:Team() != TEAM_HUNTERS then return false end return true end function PH_ResetCustomTauntWindowState() -- Force close any taunt menu windows ForceCloseTauntWindow(0) -- Extra additional PHE.VOICE_IS_END_ROUND = 0 -- Reset Player's Height end hook.Add("PostCleanupMap", "PH_ResetCustomTauntWindow", PH_ResetCustomTauntWindowState) -- Make a variable for 4 unique combines. -- Clean up, sorry btw. local playerModels = { "combine", "combineprison", "combineelite", "police" -- you may add more here. } function GM:PlayerSetModel(pl) -- player actual model to prevent multi-damage hitbox. local player_model = "models/props_idbs/phenhanced/box.mdl" if GetConVar("ph_use_custom_plmodel"):GetBool() then -- Use a delivered player model info from cl_playermodel ConVar. -- This however will use a custom player selection. It'll immediately apply once it is selected. local mdlinfo = pl:GetInfo("cl_playermodel") local mdlname = player_manager.TranslatePlayerModel(mdlinfo) if pl:Team() == TEAM_HUNTERS then player_model = mdlname end else -- Otherwise, Use Random one based from a table above. local customModel = table.Random(playerModels) local customMdlName = player_manager.TranslatePlayerModel(customModel) if pl:Team() == TEAM_HUNTERS then player_model = customMdlName end end -- precache and Set the model. util.PrecacheModel(player_model) pl:SetModel(player_model) end -- The [E] & Mouse Click 1 behaviour is now moved in here! function GM:PlayerExchangeProp(pl, ent) if !IsValid(pl) then return; end if !IsValid(ent) then return; end if pl:Team() == TEAM_PROPS && pl:IsOnGround() && !pl:Crouching() && table.HasValue(PHE.USABLE_PROP_ENTITIES, ent:GetClass()) && ent:GetModel() then if table.HasValue(PHE.BANNED_PROP_MODELS, ent:GetModel()) then pl:ChatPrint("[PH: Enhanced] Notice: That prop has been banned from the server.") elseif IsValid(ent:GetPhysicsObject()) && (pl.ph_prop:GetModel() != ent:GetModel() || pl.ph_prop:GetSkin() != ent:GetSkin()) then local ent_health = math.Clamp(ent:GetPhysicsObject():GetVolume() / 250, 1, 200) local new_health = math.Clamp((pl.ph_prop.health / pl.ph_prop.max_health) * ent_health, 1, 200) pl.ph_prop.health = new_health pl.ph_prop.max_health = ent_health pl.ph_prop:SetModel(ent:GetModel()) pl.ph_prop:SetSkin(ent:GetSkin()) pl.ph_prop:SetSolid(SOLID_VPHYSICS) pl.ph_prop:SetPos(pl:GetPos() - Vector(0, 0, ent:OBBMins().z)) pl.ph_prop:SetAngles(pl:GetAngles()) pl:SetHealth(new_health) if GetConVar("ph_sv_enable_obb_modifier"):GetBool() && ent:GetNWBool("hasCustomHull",false) then local hmin = ent.m_Hull[1] local hmax = ent.m_Hull[2] local dmin = ent.m_dHull[1] local dmax = ent.m_dHull[2] if hmax.z < 24 || dmax.z < 24 then pl:SetViewOffset(Vector(0,0,24)) pl:SetViewOffsetDucked(Vector(0,0,24)) elseif hmax.z > 84 || dmax.z > 84 then --what the heck Duck Size is 84? BigMomma.mdl? pl:SetViewOffset(Vector(0,0,84)) pl:SetViewOffsetDucked(Vector(0,0,84)) else pl:SetViewOffset(Vector(0,0,hmax.z)) pl:SetViewOffsetDucked(Vector(0,0,dmax.z)) end pl:SetHull(hmin,hmax) pl:SetHullDuck(dmin,dmax) net.Start("SetHull") net.WriteInt(math.Round(math.Max(hmax.x,hmax.y)),32) net.WriteInt(hmax.z,32) net.WriteInt(dmax.z,32) net.WriteInt(new_health,9) net.Send(pl) else local hullxymax = math.Round(math.Max(ent:OBBMaxs().x, ent:OBBMaxs().y)) local hullxymin = hullxymax * -1 local hullz = math.Round(ent:OBBMaxs().z - ent:OBBMins().z) local dhullz = hullz if hullz > 10 && hullz <= 30 then dhullz = hullz-(hullz*0.5) elseif hullz > 30 && hullz <= 40 then dhullz = hullz-(hullz*0.2) elseif hullz > 40 && hullz <= 50 then dhullz = hullz-(hullz*0.1) else dhullz = hullz end if hullz < 24 then pl:SetViewOffset(Vector(0,0,24)) pl:SetViewOffsetDucked(Vector(0,0,24)) elseif hullz > 84 then pl:SetViewOffset(Vector(0,0,84)) pl:SetViewOffsetDucked(Vector(0,0,84)) else pl:SetViewOffset(Vector(0,0,hullz)) pl:SetViewOffsetDucked(Vector(0,0,dhullz)) end pl:SetHull(Vector(hullxymin, hullxymin, 0), Vector(hullxymax, hullxymax, hullz)) pl:SetHullDuck(Vector(hullxymin, hullxymin, 0), Vector(hullxymax, hullxymax, dhullz)) net.Start("SetHull") net.WriteInt(hullxymax, 32) net.WriteInt(hullz, 32) net.WriteInt(dhullz, 32) net.WriteInt(new_health, 9) net.Send(pl) end end hook.Call("PH_OnChangeProp", nil, pl, ent) end end -- Called when a player tries to use an object. By default this pressed ['E'] button. MouseClick 1 will be mentioned below at line @351 function GM:PlayerUse(pl, ent) if !pl:Alive() || pl:Team() == TEAM_SPECTATOR || pl:Team() == TEAM_UNASSIGNED then return false; end -- Prevent Execution Spam by holding ['E'] button too long. if pl.UseTime <= CurTime() then local hmx, hz = ent:GetPropSize() if GetConVar("phe_check_props_boundaries"):GetBool() && !pl:CheckHull(hmx, hmx, hz) then pl:SendLua("chat.AddText(Color(235, 10, 15), \"[PH: Enhanced]\", Color(220, 220, 220), \" There is no room to change that prop!\")") else self:PlayerExchangeProp(pl, ent) end pl.UseTime = CurTime() + 1 end -- Prevent the door exploit if table.HasValue(PHE.EXPLOITABLE_DOORS, ent:GetClass()) && pl.last_door_time && pl.last_door_time + 1 > CurTime() then return false end pl.last_door_time = CurTime() return true end net.Receive("CL2SV_ExchangeProp", function(len, ply) local Prop = net.ReadEntity() ply:PrintMessage(HUD_PRINTCONSOLE, "-=* NOTICE *=-") ply:PrintMessage(HUD_PRINTCONSOLE, "Hello! We've noticed you tried using the \"CL2SV_ExchangeProp\" net message.") ply:PrintMessage(HUD_PRINTCONSOLE, "Sad news is that this net message is no longer used (due to exploits). Shame, isn't it?") ply:PrintMessage(HUD_PRINTCONSOLE, "") ply:PrintMessage(HUD_PRINTCONSOLE, "This net message will still respond, but you will receive this message instead.") ply:PrintMessage(HUD_PRINTCONSOLE, "-=* NOTICE *=-") --[[ if ply.UseTime <= CurTime() then if !ply:IsHoldingEntity() then local hmx,hz = Prop:GetPropSize() if (GetConVar("phe_check_props_boundaries"):GetBool() && !ply:CheckHull(hmx,hmx,hz)) then ply:SendLua("chat.AddText(Color(235,10,15), \"[PH: Enhanced]\", Color(220,220,220), \" There is no room to change that prop!\")") else GAMEMODE:PlayerExchangeProp(ply, Prop) end end ply.UseTime = CurTime() + 1 end ]] -- OBSOLETE : THIS IS COMMENTED OUT BECAUSE THIS METHOD IS SILLY AND SHOULD NOT BE USED. --yeah kind of my fault! >.< end) -- Called when player presses [F3]. Plays a taunt for their team function GM:ShowSpare1(pl) if (GetConVar("ph_enable_custom_taunts"):GetInt() == 1) && GAMEMODE:InRound() then pl:ConCommand("ph_showtaunts") end if ((GetConVar("ph_enable_custom_taunts"):GetInt() == 0) or (GetConVar("ph_enable_custom_taunts"):GetInt() == 2)) && GAMEMODE:InRound() && pl:Alive() && (pl:Team() == TEAM_HUNTERS || pl:Team() == TEAM_PROPS) && pl.last_taunt_time + GetConVar("ph_normal_taunt_delay"):GetInt() <= CurTime() && (table.Count(PHE.PROP_TAUNTS) > 1 && table.Count(PHE.HUNTER_TAUNTS) > 1) then local curTeamTaunt = { hunter = PHE:GetAllTeamTaunt(TEAM_HUNTERS), prop = PHE:GetAllTeamTaunt(TEAM_PROPS) } -- play the taunts based on listed curCustTaunt available. repeat if pl:Team() == TEAM_HUNTERS then rand_taunt = table.Random(curTeamTaunt.hunter) else rand_taunt = table.Random(curTeamTaunt.prop) end until rand_taunt != pl.last_taunt pl.last_taunt_time = CurTime() + GetConVar("ph_normal_taunt_delay"):GetInt() pl.last_taunt = rand_taunt pl:EmitSound(rand_taunt, 100) pl:SetNWFloat("LastTauntTime", CurTime()) end end -- Called when a player leaves function PlayerDisconnected(pl) pl:RemoveProp() end hook.Add("PlayerDisconnected", "PH_PlayerDisconnected", PlayerDisconnected) -- Set specific variable for checking in player initial spawn, then use Player:IsHoldingEntity() hook.Add("PlayerInitialSpawn", "PHE.SetupInitData", function(ply) ply.LastPickupEnt = NULL ply.UseTime = 0 end) hook.Add("AllowPlayerPickup", "PHE.IsHoldingEntity", function(ply,ent) ply.LastPickupEnt = ent ent.LastPickupPly = ply end) -- Spray Controls hook.Add( "PlayerSpray", "PH.GeneralSprayFunc", function( ply ) if ( ( !ply:Alive() ) || ( ply:Team() == TEAM_SPECTATOR ) ) then return true end end ) -- Called when the players spawns function PlayerSpawn(pl) pl:SetNWBool("PlayerLockedRotation", false) pl:SetNWBool("InFreezeCam", false) pl:SetNWEntity("PlayerKilledByPlayerEntity", nil) pl:Blind(false) pl:RemoveProp() pl:SetColor(Color(255,255,255,255)) pl:SetRenderMode(RENDERMODE_TRANSALPHA) pl:UnLock() pl:ResetHull() pl:SetNWFloat("LastTauntTime", CurTime()) pl.last_taunt_time = 0 net.Start("ResetHull") net.Send(pl) net.Start("DisableDynamicLight") net.Send(pl) pl:SetCollisionGroup(COLLISION_GROUP_PASSABLE_DOOR) pl:CollisionRulesChanged() if pl:Team() == TEAM_HUNTERS then pl:SetJumpPower(160) elseif pl:Team() == TEAM_PROPS then pl:SetJumpPower(160 * GetConVar("ph_prop_jumppower"):GetFloat()) end -- Listen server host if !game.IsDedicated() then pl:SetNWBool("ListenServerHost", pl:IsListenServerHost()) end end hook.Add("PlayerSpawn", "PH_PlayerSpawn", PlayerSpawn) -- Called when round ends function RoundEnd() -- Unblind the hunters for _, pl in pairs(team.GetPlayers(TEAM_HUNTERS)) do pl:Blind(false) pl:UnLock() end -- Stop autotaunting net.Start("AutoTauntRoundEnd") net.Broadcast() end hook.Add("PH_RoundEnd", "PH.ForceHuntersUnblind", RoundEnd) -- This is called when the round time ends (props win) function GM:RoundTimerEnd() if !GAMEMODE:InRound() then return end GAMEMODE:RoundEndWithResult(TEAM_PROPS, "Props win!") PHE.VOICE_IS_END_ROUND = 1 ForceCloseTauntWindow(1) net.Start("PH_TeamWinning_Snd") net.WriteString(PHE.WINNINGSOUNDS[TEAM_PROPS]) net.Broadcast() hook.Call("PH_OnTimerEnd", nil) end -- Called before start of round function GM:OnPreRoundStart(num) game.CleanUpMap() if GetGlobalInt("RoundNumber") != 1 && (GetConVar("ph_swap_teams_every_round"):GetInt() == 1 || ((team.GetScore(TEAM_PROPS) + team.GetScore(TEAM_HUNTERS)) > 0)) then for _, pl in pairs(player.GetAll()) do if pl:Team() == TEAM_PROPS || pl:Team() == TEAM_HUNTERS then if pl:Team() == TEAM_PROPS then pl:SetTeam(TEAM_HUNTERS) else pl:SetTeam(TEAM_PROPS) if GetConVar("ph_notice_prop_rotation"):GetBool() then timer.Simple(0.5, function() pl:SendLua( [[notification.AddLegacy("You are in Prop Team with Rotate support! You can rotate the prop around by moving your mouse.", NOTIFY_UNDO, 20 )]] ) end) pl:SendLua( [[notification.AddLegacy("Additionally you can toggle lock rotation by pressing R key!", NOTIFY_GENERIC, 18 )]] ) pl:SendLua( [[surface.PlaySound("garrysmod/content_downloaded.wav")]] ) end end pl:ChatPrint("Teams have been swapped!") end end -- Props will gain a Bonus Armor points Hunter teams has more than 4 players in it. The more player, the more armor they get. timer.Simple(1, function() local NumHunter = table.Count(team.GetPlayers(TEAM_HUNTERS)) if NumHunter >= 4 && NumHunter <= 8 then for _,prop in pairs(team.GetPlayers(TEAM_PROPS)) do if IsValid(prop) then prop:SetArmor(math.random(1,3) * 15) end end elseif NumHunter > 8 then for _,prop in pairs(team.GetPlayers(TEAM_PROPS)) do if IsValid(prop) then prop:SetArmor(math.random(3,7) * 15) end end end end) hook.Call("PH_OnPreRoundStart", nil, GetConVar("ph_swap_teams_every_round"):GetInt()) end UTIL_StripAllPlayers() UTIL_SpawnAllPlayers() UTIL_FreezeAllPlayers() end -- Called every server tick. function GM:Think() -- Prop spectating is a bit messy so let us clean it up a bit if PHE.SPECTATOR_CHECK < CurTime() then for _, pl in pairs(team.GetPlayers(TEAM_PROPS)) do if IsValid(pl) && !pl:Alive() && pl:GetObserverMode() == OBS_MODE_IN_EYE then hook.Call("ChangeObserverMode", GAMEMODE, pl, OBS_MODE_ROAMING) end end PHE.SPECTATOR_CHECK = CurTime() + PHE.SPECTATOR_CHECK_ADD end end -- Bonus Drop :D function PH_Props_OnBreak(ply, ent) if GetConVar("ph_enable_lucky_balls"):GetBool() then local pos = ent:GetPos() if math.random() < 0.08 then -- 8% Chance of drops. local dropent = ents.Create("ph_luckyball") dropent:SetPos(Vector(pos.x, pos.y, pos.z + 32)) -- to make sure the Lucky Ball didn't fall underground. dropent:SetAngles(Angle(0,0,0)) dropent:SetColor(Color(math.Round(math.random(0,255)),math.Round(math.random(0,255)),math.Round(math.random(0,255)),255)) dropent:Spawn() end end end hook.Add("PropBreak", "Props_OnBreak_WithDrops", PH_Props_OnBreak) -- Force Close the Taunt Menu whenever the prop is being killed. function close_PlayerKilledSilently(ply) if ply:Team() == TEAM_PROPS then net.Start( "PH_ForceCloseTauntWindow" ) net.Send(ply) end end hook.Add("PlayerSilentDeath", "SilentDed_ForceClose", close_PlayerKilledSilently) -- Flashlight toggling function GM:PlayerSwitchFlashlight(pl, on) if pl:Alive() && pl:Team() == TEAM_HUNTERS then return true end if pl:Alive() && pl:Team() == TEAM_PROPS then net.Start("PlayerSwitchDynamicLight") net.Send(pl) end return false end -- Round Control cvars.AddChangeCallback("ph_min_waitforplayers", function(cvar, old, new) if tonumber(new) < 1 then RunConsoleCommand("ph_min_waitforplayers", "1") print("[PH:E] Warning: Value must not be 0! Use ph_waitforplayers 0 to disable.") end end) local bAlreadyStarted = false function GM:OnRoundEnd( num ) -- Check if GetConVar("ph_waitforplayers"):GetBool() is true -- This is a fast implementation for a waiting system -- Make optimisations if needed if ( GetConVar("ph_waitforplayers"):GetBool() ) then -- Take away a round number quickly before it adds another when there are not enough players -- Set to false if ( ( team.NumPlayers( TEAM_HUNTERS ) < GetConVar("ph_min_waitforplayers"):GetInt() ) || ( team.NumPlayers( TEAM_PROPS ) < GetConVar("ph_min_waitforplayers"):GetInt() ) ) then bAlreadyStarted = false end -- Set to true if ( ( team.NumPlayers( TEAM_HUNTERS ) >= GetConVar("ph_min_waitforplayers"):GetInt() ) && ( team.NumPlayers( TEAM_PROPS ) >= GetConVar("ph_min_waitforplayers"):GetInt() ) ) then bAlreadyStarted = true end -- Check if the round was already started before so we count it as a fully played round if ( !bAlreadyStarted ) then SetGlobalInt( "RoundNumber", GetGlobalInt("RoundNumber") - 1 ) end end hook.Call("PH_OnRoundEnd", nil, num) end function GM:RoundStart() local roundNum = GetGlobalInt( "RoundNumber" ); local roundDuration = GAMEMODE:GetRoundTime( roundNum ) GAMEMODE:OnRoundStart( roundNum ) timer.Create( "RoundEndTimer", roundDuration, 0, function() GAMEMODE:RoundTimerEnd() end ) timer.Create( "CheckRoundEnd", 1, 0, function() GAMEMODE:CheckRoundEnd() end ) SetGlobalFloat( "RoundEndTime", CurTime() + roundDuration ); -- Check if GetConVar("ph_waitforplayers"):GetBool() is true -- This is a fast implementation for a waiting system -- Make optimisations if needed if ( GetConVar("ph_waitforplayers"):GetBool() ) then -- Pause these timers if there are not enough players on the teams in the server if ( ( team.NumPlayers( TEAM_HUNTERS ) < GetConVar("ph_min_waitforplayers"):GetInt() ) || ( team.NumPlayers( TEAM_PROPS ) < GetConVar("ph_min_waitforplayers"):GetInt() ) ) then if ( timer.Exists( "RoundEndTimer" ) && timer.Exists( "CheckRoundEnd" ) ) then timer.Pause( "RoundEndTimer" ) timer.Pause( "CheckRoundEnd" ) SetGlobalFloat( "RoundEndTime", -1 ); PrintMessage( HUD_PRINTTALK, "There's not enough players to start the game!" ) -- Reset the team score team.SetScore(TEAM_PROPS, 0) team.SetScore(TEAM_HUNTERS, 0) end end end -- Send this as a global boolean SetGlobalBool( "RoundWaitForPlayers", GetConVar("ph_waitforplayers"):GetBool() ) hook.Call("PH_RoundStart", nil) end -- End of Round Control Override -- Player pressed a key function PlayerPressedKey(pl, key) -- Use traces to select a prop local min,max = pl:GetHull() if pl && pl:IsValid() && pl:Alive() && pl:Team() == TEAM_PROPS then plhullz = max.z if key == IN_ATTACK then local trace = {} if plhullz < 24 then trace.start = pl:EyePos() + Vector(0, 0, plhullz + (24- plhullz)) trace.endpos = pl:EyePos() + Vector(0, 0, plhullz + (24 - plhullz)) + pl:EyeAngles():Forward() * 100 elseif plhullz > 84 then trace.start = pl:EyePos() + Vector(0, 0, plhullz - 84) trace.endpos = pl:EyePos() + Vector(0, 0, plhullz - 84) + pl:EyeAngles():Forward() * 300 else trace.start = pl:EyePos() + Vector(0, 0, 8) trace.endpos = pl:EyePos() + Vector(0, 0, 8) + pl:EyeAngles():Forward() * 100 end trace.filter = ents.FindByClass("ph_prop") local trace2 = util.TraceLine(trace) if trace2.Entity && trace2.Entity:IsValid() && table.HasValue(PHE.USABLE_PROP_ENTITIES, trace2.Entity:GetClass()) then if pl.UseTime <= CurTime() then if !pl:IsHoldingEntity() then local hmx, hz = trace2.Entity:GetPropSize() if GetConVar("phe_check_props_boundaries"):GetBool() && !pl:CheckHull(hmx, hmx, hz) then pl:SendLua("chat.AddText(Color(235, 10, 15), \"[PH: Enhanced]\", Color(220, 220, 220), \" There is no room to change that prop!\")") else GAMEMODE:PlayerExchangeProp(pl, trace2.Entity) end end pl.UseTime = CurTime() + 1 end end end end -- Prop rotation lock key if pl && pl:IsValid() && pl:Alive() && pl:Team() == TEAM_PROPS then if key == IN_RELOAD then if pl:GetPlayerLockedRot() then pl:SetNWBool("PlayerLockedRotation", false) pl:PrintMessage(HUD_PRINTCENTER, "Prop Rotation Lock: Disabled") net.Start("PHE.rotateState") net.WriteInt(0, 2) net.Send(pl) else pl:SetNWBool("PlayerLockedRotation", true) pl:PrintMessage(HUD_PRINTCENTER, "Prop Rotation Lock: Enabled") net.Start("PHE.rotateState") net.WriteInt(1, 2) net.Send(pl) end end end end hook.Add("KeyPress", "PlayerPressedKey", PlayerPressedKey)