--[[ The MIT License (MIT) Copyright (c) 2015 Xaymar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --]] -- ------------------------------------------------------------------------- -- --! Downloadable Lua -- ------------------------------------------------------------------------- -- -- Shared AddCSLuaFile("sh_init.lua") AddCSLuaFile("sh_config.lua") AddCSLuaFile("meta/player.lua") AddCSLuaFile("player_class/class_default.lua") AddCSLuaFile("player_class/class_spectator.lua") AddCSLuaFile("player_class/class_seeker.lua") AddCSLuaFile("player_class/class_hider.lua") -- Client-Only AddCSLuaFile("client/fontmanager.lua") AddCSLuaFile("client/uimanager.lua") AddCSLuaFile("client/hud/gamestatedisplay.lua") AddCSLuaFile("client/cl_ui_help.lua") AddCSLuaFile("client/cl_ui_teamselection.lua") AddCSLuaFile("derma/dscoreboard.lua") -- Client Init AddCSLuaFile("cl_init.lua") -- Immediately executed when downloaded, weird bug. -- ------------------------------------------------------------------------- -- --! Code -- ------------------------------------------------------------------------- -- -- Shared include "sh_init.lua" -- Server Only include "compat/compat_tauntpackloader.lua" include "server/roundmanager.lua" include "server/states/state_prematch.lua" include "server/states/state_preround.lua" include "server/states/state_hide.lua" include "server/states/state_seek.lua" include "server/states/state_postround.lua" include "server/states/state_postmatch.lua" function GM:Initialize() print("-------------------------------------------------------------------------") print("Prop Hunt: Initializing...") print("Prop Hunt: Registering Networked Messages...") util.AddNetworkString("PlayerManagerInitialClientSpawn") util.AddNetworkString("PlayerManagerClientSpawn") util.AddNetworkString("PlayerSetHull") util.AddNetworkString("PlayerResetHull") util.AddNetworkString("PlayerViewOffset") util.AddNetworkString("PlayerRegisterPropEntity") util.AddNetworkString("PlayerEnablePropRotation") util.AddNetworkString("PlayerDisablePropRotation") print("Prop Hunt: Initializing Gamemode Data...") self.Data = {} self.Data.RoundTime = 0 self.Data.RoundStartTime = 0 self.Data.StateTime = 0 print("Prop Hunt: Initializing Round Manager") self.RoundManager = roundManager(StatePreMatch) print("Prop Hunt: Precaching...") GAMEMODE.Config.Taunt:Seekers() GAMEMODE.Config.Taunt:Hiders() print("Prop Hunt: Complete.") print("-------------------------------------------------------------------------") end function GM:Think() self.RoundManager:Tick() end -- Player Connected function GM:PlayerConnect(name, ip) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..name.."' connecting from IP '"..ip.."'.") end end -- Player Authenticated function GM:PlayerAuthed(ply, steamid, uniqueid) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") authenticated.") end end -- Player Disconnected function GM:PlayerDisconnected(ply) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") disconnected.") end end -- Player Spawn (Initial) function GM:PlayerInitialSpawn(ply) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") spawned for the first time, applying defaults...") end if (!ply.Data) then -- Initialize Data Structure ply.Data = {} ply.Data.Alive = false ply.Data.AliveTime = 0 ply.Data.RandomWeight = 0 -- Higher means higher chance of becoming Seeker instead of Hider. end -- Kill Silently ply:KillSilent() -- Show Team Selection Menu ply:SetTeam(GAMEMODE.Teams.Spectators) self:ShowTeam(ply) -- Bot: Auto Assign to Team if (ply:IsBot()) then if team.NumPlayers(self.Teams.Hiders) > team.NumPlayers(self.Teams.Seekers) then if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Bot '"..ply:GetName().."' assigned to Seekers.") end ply:SetTeam(self.Teams.Seekers) else if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Bot '"..ply:GetName().."' assigned to Hiders.") end ply:SetTeam(self.Teams.Hiders) end end -- Notify Player Manager player_manager.RunClass(ply, "InitialSpawn") -- Signal Client net.Start("PlayerManagerInitialClientSpawn");net.Send(ply) end -- Player Spawn function GM:PlayerSpawn(ply) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") spawned in.") end -- Player Manager: Assign Player Class local class = team.GetClass(ply:Team()) if (class) then if (ply.Data.Alive) then -- Alive Class player_manager.SetPlayerClass(ply, class[1]) else -- Dead Class (Spectator) player_manager.SetPlayerClass(ply, class[2]) end else player_manager.SetPlayerClass(ply, "Spectator") end -- Notify Player Manager player_manager.OnPlayerSpawn(ply) player_manager.RunClass(ply, "Spawn") -- Some hooks are not called hook.Call("PlayerSetModel", self, ply) hook.Call("PlayerLoadout", self, ply) -- Signal Client net.Start("PlayerManagerClientSpawn");net.Send(ply) end -- Player requests Team Change function GM:PlayerRequestTeam(ply, teamId) if self:PlayerCanJoinTeam(ply, teamId) then if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") requested to join Team "..team.GetName(teamId)..".") end if (ply:Team() != teamId) then ply:KillSilent() ply:SetTeam(teamId) if (GetGlobalInt("RoundState", GAMEMODE.States.PreMatch) <= GAMEMODE.States.PreRound) then ply.Alive = true ply.AliveTime = CurTime() end else if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") attempted to rejoin the Team it is already in.") end end end end -- ------------------------------------------------------------------------- -- --! Player Manager Binding -- ------------------------------------------------------------------------- -- function GM:PlayerLoadout(ply) player_manager.RunClass(ply, "Loadout") end function GM:PlayerDeath(ply, inflictor, attacker) player_manager.RunClass(ply, "Death", inflictor, attacker) -- Signal Client Stuff if IsValid(attacker) then if attacker:IsPlayer() then if (attacker == ply) then net.Start("PlayerKilledSelf") net.WriteEntity(ply) net.Broadcast() else net.Start("PlayerKilledByPlayer") net.WriteEntity(ply) net.WriteString(inflictor:GetName()) net.WriteEntity(attacker) net.Broadcast() end else net.Start("PlayerKilled") net.WriteEntity(ply) net.WriteString(inflictor:GetName()) net.WriteString(attacker:GetClass()) net.Broadcast() end else net.Start("PlayerKilled") net.WriteEntity(ply) net.WriteString(inflictor:GetName()) net.WriteString("World") net.Broadcast() end end function GM:PlayerSilentDeath(ply) player_manager.RunClass(ply, "SilentDeath") end function GM:PostPlayerDeath(ply) player_manager.RunClass(ply, "PostDeath") -- Debug Mode: Respawn after Death if (GAMEMODE.Config:Debug()) then ply.Data.Alive = true if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Debug Mode active, Player "..ply:GetName().." was respawned.") end end end function GM:DoPlayerDeath(ply, attacker, dmg) player_manager.RunClass(ply, "DoDeath", attacker, dmg) end function GM:PlayerDeathThink(ply) return player_manager.RunClass(ply, "DeathThink") end function GM:CanPlayerSuicide(ply) return player_manager.RunClass(ply, "CanSuicide") end function GM:PlayerCanPickupWeapon(ply, weapon) return player_manager.RunClass(ply, "CanPickupWeapon", weapon) end function GM:PlayerCanPickupItem(ply, item) return player_manager.RunClass(ply, "CanPickupItem", item) end function GM:PlayerUse(ply, ent) return player_manager.RunClass(ply, "Use", ent) end function GM:AllowPlayerPickup(ply, ent) return player_manager.RunClass(ply, "AllowPickup", ent) end function GM:PlayerSetModel(ply) return player_manager.RunClass(ply, "SetModel") end -- Damage function GM:PlayerShouldTakeDamage(victim, attacker) return player_manager.RunClass(victim, "ShouldTakeDamage", attacker) end function GM:PlayerHurt(victim, attacker, healthRemaining, damageTaken) player_manager.RunClass(victim, "Hurt", victim, attacker, healthRemaining, damageTaken) if (IsValid(attacker) && attacker:IsPlayer()) then player_manager.RunClass(attacker, "Damage", victim, attacker, healthRemaining, damageTaken) end end function GM:EntityTakeDamage(ent, dmg) local att = dmg:GetAttacker() if (att) && (att:IsValid()) && (att:IsPlayer()) then player_manager.RunClass(att, "DamageEntity", ent, att, dmg) end end -- ------------------------------------------------------------------------- -- --! Gamemode Functionality -- ------------------------------------------------------------------------- -- function GM:SetRound(Round) SetGlobalInt("Round", Round) end function GM:SetRoundTime(Time) SetGlobalInt("RoundTime", Time) end function GM:SetRoundState(State) SetGlobalInt("RoundState", State) end function GM:SetRoundStateTime(Time) SetGlobalInt("RoundStateTime", Time) end function GM:SetRoundWinner(Winner) SetGlobalInt("RoundWinner", Winner) end function GM:PlayerHullFromEntity(ply, ent) if (ent) && (ent:IsValid()) then local hmin, hmax = ent:OBBMins(), ent:OBBMaxs() local hull = Vector(hmax.x - hmin.x, hmax.y - hmin.y, hmax.z - hmin.z) if hull.x <= hull.y then hull.y = hull.x else hull.x = hull.y end hull:Mul(0.5) hull:Mul(0.95) -- Reduce size slightly local hullmin = Vector(-hull.x, -hull.y, 0) local hullmax = Vector(hull.x, hull.y, hull.z * 2) ply:SetHull(hullmin, hullmax) ply:SetHullDuck(hullmin, hullmax) net.Start("PlayerSetHull") net.WriteVector(hullmin) net.WriteVector(hullmax) net.Send(ply) return {hullmin, hullmax} else ply:ResetHull() net.Start("PlayerResetHull") net.Send(ply) return nil end end function GM:PlayerSetViewOffset(ply, vo, voduck) ply:SetViewOffset(vo) ply:SetViewOffsetDucked(voduck) if ply:Crouching() then ply:SetCurrentViewOffset(voduck) else ply:SetCurrentViewOffset(vo) end -- Signal Client net.Start("PlayerViewOffset") net.WriteVector(vo) net.WriteVector(voduck) net.Send(ply) end -- ------------------------------------------------------------------------- -- --! Commands -- ------------------------------------------------------------------------- -- -- F1/ShowHelp function GM:ShowHelp(ply) ply:ConCommand("ph_show_help") end -- F2/ShowTeam - Select Team function GM:ShowTeam(ply) ply:ConCommand("ph_select_team") end function GM:ShowSpare1(ply) player_manager.RunClass(ply, "ShowSpare1") end -- F3/ShowSpare1 function GM:ShowSpare2(ply) player_manager.RunClass(ply, "ShowSpare2") end -- F4/ShowSpare2 -- Debug Command: Print All Players concommand.Add("ph_debug_printplayers", function(ply, cmd, args, argStr) if GAMEMODE.Config:DebugLog() then print ("All Players:") for i,ply in ipairs(player.GetAll()) do print(" "..ply:GetName().." (SteamID: "..ply:SteamID()..") - Team "..team.GetName(ply:Team()).. " - Alive "..tostring(ply.Data.Alive)) end print ("Spectators:") for i,ply in ipairs(team.GetPlayers(GAMEMODE.Teams.Spectators)) do print(" "..ply:GetName().." (SteamID: "..ply:SteamID()..")") end print ("Seekers:") for i,ply in ipairs(team.GetPlayers(GAMEMODE.Teams.Seekers)) do print(" "..ply:GetName().." (SteamID: "..ply:SteamID()..")") end print ("Hiders:") for i,ply in ipairs(team.GetPlayers(GAMEMODE.Teams.Hiders)) do print(" "..ply:GetName().." (SteamID: "..ply:SteamID()..")") end end end, nil, nil, FCVAR_CLIENTCMD_CAN_EXECUTE + FCVAR_CHEAT) -- Debug Command: Print All Players concommand.Add("ph_debug_stats", function(ply, cmd, args, argStr) if GAMEMODE.Config:DebugLog() then for i,ply in ipairs(player.GetAll()) do print(ply:GetName().." (SteamID: "..ply:SteamID()..")") print(" Team: "..team.GetName(ply:Team())) print(" Alive: "..tostring(ply.Data.Alive)) print(" AliveTime: "..tostring(ply.Data.AliveTime)) print(" Score: "..tostring(ply.Data.RandomWeight)) end end end, nil, nil, FCVAR_CLIENTCMD_CAN_EXECUTE + FCVAR_CHEAT) -- ------------------------------------------------------------------------- -- --! Network Messages -- ------------------------------------------------------------------------- -- net.Receive("PlayerEnablePropRotation", function(len, ply) if (ply:Team() != GAMEMODE.Teams.Hiders) then return end angle = net.ReadAngle() ply:SetNWBool("PropRotation", true) ply.Data.Prop:ApplyRotation(angle) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") enabled prop rotation with angle ["..tostring(angle).."].") end end) net.Receive("PlayerDisablePropRotation", function(len, ply) if (ply:Team() != GAMEMODE.Teams.Hiders) then return end angle = net.ReadAngle() ply:SetNWBool("PropRotation", false) ply.Data.Prop:ApplyRotation(angle) if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") enabled prop rotation with angle ["..tostring(angle).."].") end end)