785f55a017
The meta object is extended by some commonly used functionality that Garry's Mod does not expose directly. Some of these are Server only and will produce wrong results on the Client, also due to Garry's Mod not exposing things properly.
405 lines
13 KiB
Lua
405 lines
13 KiB
Lua
--[[
|
|
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("meta/player.lua")
|
|
AddCSLuaFile("sh_config.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("cl_init.lua")
|
|
AddCSLuaFile("client/cl_ui_help.lua")
|
|
AddCSLuaFile("client/cl_ui_teamselection.lua")
|
|
|
|
-- ------------------------------------------------------------------------- --
|
|
--! 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.StartTime = CurTime()
|
|
|
|
print("Prop Hunt: Setting initial RoundManager State...")
|
|
self.RoundManager:SetState(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:SetRoundState(State)
|
|
SetGlobalInt("RoundState", State)
|
|
end
|
|
|
|
function GM:SetRoundTime(Time)
|
|
SetGlobalInt("RoundTime", 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
|
|
print("Prop Hunt: Enabling Prop Rotation")
|
|
ply:SetNWBool("PropRotation", true)
|
|
ply.Data.Prop:ApplyRotation(net.ReadAngle())
|
|
end)
|
|
net.Receive("PlayerDisablePropRotation", function(len, ply)
|
|
if (ply:Team() != GAMEMODE.Teams.Hiders) then
|
|
return
|
|
end
|
|
print("Prop Hunt: Disabling Prop Rotation")
|
|
ply:SetNWBool("PropRotation", false)
|
|
ply.Data.Prop:ApplyRotation(net.ReadAngle())
|
|
end) |