Gamemode Classes Problem

Hello Facepunch, I have recently encountered some problems with classes in a Fretta-based gamemode. What happens is that I create three separate classes, Ass-Salt, Scoot and Grenadier; and each class seems to appear with no weapons and with gasmask.mdl instead of the ones I’ve told it to include. Here’s the lua for each class:

Code for Ass-Salt:


local CLASS = {}

CLASS.DisplayName            = "Ass-Salt"
CLASS.WalkSpeed             = 250
CLASS.CrouchedWalkSpeed     = 0.2
CLASS.RunSpeed                = 450
CLASS.DuckSpeed                = 0.2
CLASS.JumpPower                = 200
CLASS.PlayerModel            = "models/player/urban.mdl"
CLASS.DrawTeamRing            = false
CLASS.DrawViewModel            = true
CLASS.CanUseFlashlight      = true
CLASS.MaxHealth                = 100
CLASS.StartHealth            = 100
CLASS.StartArmor            = 0
CLASS.RespawnTime           = 0 // 0 means use the default spawn time chosen by gamemode
CLASS.DropWeaponOnDie        = true
CLASS.TeammateNoCollide     = true
CLASS.AvoidPlayers            = true // Automatically avoid players that we're no colliding
CLASS.Selectable            = true // When false, this disables all the team checking
CLASS.FullRotation            = false // Allow the player's model to rotate upwards, etc etc

function CLASS:Loadout( pl )

    pl:GiveAmmo( 255,    "Pistol",         true )
    
    pl:Give( "weapon_pistol" )

    pl:GiveAmmo( 300,    "Ar2",         true )
    
    pl:Give( "weapon_ar2" )
end

function CLASS:OnSpawn( pl )
end

function CLASS:OnDeath( pl, attacker, dmginfo )
end

function CLASS:Think( pl )
end

function CLASS:Move( pl, mv )
end

function CLASS:OnKeyPress( pl, key )
end

function CLASS:OnKeyRelease( pl, key )
end

function CLASS:ShouldDrawLocalPlayer( pl )
    return false
end

function CLASS:CalcView( ply, origin, angles, fov )
end

player_class.Register( "Ass-Salt", CLASS )

local CLASS = {}
CLASS.DisplayName            = "Spectator Class"
CLASS.DrawTeamRing            = false
CLASS.PlayerModel            = "models/player/urban.mdl"

player_class.Register( "Spectator", CLASS )

Code for Scoot:


local CLASS = {}

CLASS.DisplayName            = "Scoot"
CLASS.WalkSpeed             = 400
CLASS.CrouchedWalkSpeed     = 0.2
CLASS.RunSpeed                = 600
CLASS.DuckSpeed                = 0.2
CLASS.JumpPower                = 200
CLASS.PlayerModel            = "models/player/gsg9.mdl"
CLASS.DrawTeamRing            = false
CLASS.DrawViewModel            = true
CLASS.CanUseFlashlight      = true
CLASS.MaxHealth                = 100
CLASS.StartHealth            = 100
CLASS.StartArmor            = 0
CLASS.RespawnTime           = 0 // 0 means use the default spawn time chosen by gamemode
CLASS.DropWeaponOnDie        = true
CLASS.TeammateNoCollide     = true
CLASS.AvoidPlayers            = true // Automatically avoid players that we're no colliding
CLASS.Selectable            = true // When false, this disables all the team checking
CLASS.FullRotation            = false // Allow the player's model to rotate upwards, etc etc

function CLASS:Loadout( pl )

    pl:GiveAmmo( 255,    "Pistol",         true )
    
    pl:Give( "weapon_pistol" )

    pl:GiveAmmo( 300,    "Smg1",         true )
    
    pl:Give( "weapon_smg1" )
end

function CLASS:OnSpawn( pl )
end

function CLASS:OnDeath( pl, attacker, dmginfo )
end

function CLASS:Think( pl )
end

function CLASS:Move( pl, mv )
end

function CLASS:OnKeyPress( pl, key )
end

function CLASS:OnKeyRelease( pl, key )
end

function CLASS:ShouldDrawLocalPlayer( pl )
    return false
end

function CLASS:CalcView( ply, origin, angles, fov )
end

player_class.Register( "Scoot", CLASS )

local CLASS = {}
CLASS.DisplayName            = "Spectator Class"
CLASS.DrawTeamRing            = false
CLASS.PlayerModel            = "models/player/gsg9.mdl"

player_class.Register( "Spectator", CLASS )

Code for Grenadier:


local CLASS = {}

CLASS.DisplayName            = "Grenadier"
CLASS.WalkSpeed             = 150
CLASS.CrouchedWalkSpeed     = 0.2
CLASS.RunSpeed                = 250
CLASS.DuckSpeed                = 0.2
CLASS.JumpPower                = 200
CLASS.PlayerModel            = "models/player/gasmask.mdl"
CLASS.DrawTeamRing            = false
CLASS.DrawViewModel            = true
CLASS.CanUseFlashlight      = true
CLASS.MaxHealth                = 100
CLASS.StartHealth            = 100
CLASS.StartArmor            = 0
CLASS.RespawnTime           = 0 // 0 means use the default spawn time chosen by gamemode
CLASS.DropWeaponOnDie        = true
CLASS.TeammateNoCollide     = true
CLASS.AvoidPlayers            = true // Automatically avoid players that we're no colliding
CLASS.Selectable            = true // When false, this disables all the team checking
CLASS.FullRotation            = false // Allow the player's model to rotate upwards, etc etc

function CLASS:Loadout( pl )

    pl:GiveAmmo( 255,    "Pistol",         true )
    
    pl:Give( "weapon_pistol" )

    pl:GiveAmmo( 300,    "Ar2",         true )
    
    pl:Give( "weapon_ar2" )
end

function CLASS:OnSpawn( pl )
end

function CLASS:OnDeath( pl, attacker, dmginfo )
end

function CLASS:Think( pl )
end

function CLASS:Move( pl, mv )
end

function CLASS:OnKeyPress( pl, key )
end

function CLASS:OnKeyRelease( pl, key )
end

function CLASS:ShouldDrawLocalPlayer( pl )
    return false
end

function CLASS:CalcView( ply, origin, angles, fov )
end

player_class.Register( "Grenadier", CLASS )

local CLASS = {}
CLASS.DisplayName            = "Grenadier"
CLASS.DrawTeamRing            = false
CLASS.PlayerModel            = "models/player/gasmask.mdl"

player_class.Register( "Grenadier", CLASS )

I don’t know if it’s necessary, but here’s the init.lua:


/*
    init.lua - Server Component
    -----------------------------------------------------
    The entire server side bit of Fretta starts here.
*/

AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( 'skin.lua' )
AddCSLuaFile( 'player_class.lua' )
AddCSLuaFile( 'class_default.lua' )
AddCSLuaFile( 'cl_splashscreen.lua' )
AddCSLuaFile( 'cl_selectscreen.lua' )
AddCSLuaFile( 'cl_gmchanger.lua' )
AddCSLuaFile( 'cl_help.lua' )
AddCSLuaFile( 'player_extension.lua' )
AddCSLuaFile( 'vgui/vgui_hudlayout.lua' )
AddCSLuaFile( 'vgui/vgui_hudelement.lua' )
AddCSLuaFile( 'vgui/vgui_hudbase.lua' )
AddCSLuaFile( 'vgui/vgui_hudcommon.lua' )
AddCSLuaFile( 'vgui/vgui_gamenotice.lua' )
AddCSLuaFile( 'vgui/vgui_scoreboard.lua' )
AddCSLuaFile( 'vgui/vgui_scoreboard_team.lua' )
AddCSLuaFile( 'vgui/vgui_scoreboard_small.lua' )
AddCSLuaFile( 'vgui/vgui_vote.lua' )
AddCSLuaFile( 'cl_hud.lua' )
AddCSLuaFile( 'cl_deathnotice.lua' )
AddCSLuaFile( 'cl_scores.lua' )
AddCSLuaFile( 'cl_notify.lua' )
AddCSLuaFile( 'player_colours.lua' )


include( "shared.lua" )
include( "sv_gmchanger.lua" )
include( "sv_spectator.lua" )
include( "round_controller.lua" )
include( "utility.lua" )

GM.ReconnectedPlayers = {}

function GM:Initialize()

    /*
    // Disabled - causes games to end in the middle of a round - we 

don't want that to happen!
    // ::Think takes care of this anyway.
    
    if ( GAMEMODE.GameLength > 0 ) then
        timer.Simple( GAMEMODE.GameLength * 60, function() 

GAMEMODE:EndOfGame( true ) end )
        SetGlobalFloat( "GameEndTime", CurTime() + 

GAMEMODE.GameLength * 60 )
    end
    */
    
    // If we're round based, wait 5 seconds before the first round 

starts
    if ( GAMEMODE.RoundBased ) then
        timer.Simple( 10, function() GAMEMODE:StartRoundBasedGame() 

end )
    end
    
    if ( GAMEMODE.AutomaticTeamBalance ) then
        timer.Create( "CheckTeamBalance", 30, 0, function() 

GAMEMODE:CheckTeamBalance() end )
    end
    
end

function GM:Think()

    self.BaseClass:Think()
    
    for k,v in pairs( player.GetAll() ) do
    
        local Class = v:GetPlayerClass()
        if ( !Class ) then return end
        
        v:CallClassFunction( "Think" )
        
    end

    // Game time related
    if( !GAMEMODE.IsEndOfGame && ( !GAMEMODE.RoundBased || ( 

GAMEMODE.RoundBased && GAMEMODE:CanEndRoundBasedGame() ) ) && CurTime() >= 

GAMEMODE.GetTimeLimit() ) then
        GAMEMODE:EndOfGame( true )
    end
    
end

/*---------------------------------------------------------
   Name: gamemode:CanPlayerSuicide( Player ply )
   Desc: Is the player allowed to commit suicide?
---------------------------------------------------------*/
function GM:CanPlayerSuicide( ply )

    if( ply:Team() == TEAM_UNASSIGNED || ply:Team() == TEAM_SPECTATOR ) 

then
        return false // no suicide in spectator mode
    end

    return !GAMEMODE.NoPlayerSuicide
    
end 

/*---------------------------------------------------------
   Name: gamemode:PlayerSwitchFlashlight( Player ply, Bool on )
   Desc: Can we turn our flashlight on or off?
---------------------------------------------------------*/
function GM:PlayerSwitchFlashlight( ply, on ) 

    if ( ply:Team() == TEAM_SPECTATOR || ply:Team() == TEAM_UNASSIGNED 

|| ply:Team() == TEAM_CONNECTING ) then
        return not on
    end

    return ply:CanUseFlashlight()
    
end

/*---------------------------------------------------------
   Name: gamemode:PlayerInitialSpawn( Player ply )
   Desc: Our very first spawn in the game.
---------------------------------------------------------*/
function GM:PlayerInitialSpawn( pl )

    pl:SetTeam( TEAM_UNASSIGNED )
    pl:SetPlayerClass( "Spectator" )
    pl.m_bFirstSpawn = true
    pl:UpdateNameColor()
    
    GAMEMODE:CheckPlayerReconnected( pl )

end

function GM:CheckPlayerReconnected( pl )

    if table.HasValue( GAMEMODE.ReconnectedPlayers, pl:UniqueID() ) 

then
        GAMEMODE:PlayerReconnected( pl )
    end

end

/*---------------------------------------------------------
   Name: gamemode:PlayerReconnected( Player ply )
   Desc: Called if the player has appeared to have reconnected.
---------------------------------------------------------*/
function GM:PlayerReconnected( pl )

    // Use this hook to do stuff when a player rejoins and has been in 

the server previously

end

function GM:PlayerDisconnected( pl )

    table.insert( GAMEMODE.ReconnectedPlayers, pl:UniqueID() )

    self.BaseClass:PlayerDisconnected( pl )

end  

function GM:ShowHelp( pl )

    pl:SendLua( "GAMEMODE:ShowHelp()" )
    
end


function GM:PlayerSpawn( pl ) 

    pl:UpdateNameColor()

    // The player never spawns straight into the game in Fretta
    // They spawn as a spectator first (during the splash screen and 

team picking screens)
    if ( pl.m_bFirstSpawn ) then
    
        pl.m_bFirstSpawn = nil
        
        if ( pl:IsBot() ) then
        
            GAMEMODE:AutoTeam( pl )
            
            // The bot doesn't send back the 'seen splash' 

command, so fake it.
            if ( !GAMEMODE.TeamBased && 

!GAMEMODE.NoAutomaticSpawning ) then
                pl:Spawn()
            end
    
        else
        
            pl:StripWeapons()
            GAMEMODE:PlayerSpawnAsSpectator( pl )
            
            // Follow a random player until we join a team
            if ( #player.GetAll() > 1 ) then
                pl:Spectate( OBS_MODE_CHASE )
                pl:SpectateEntity( table.Random( 

player.GetAll() ) )
            end
            
        end
    
        return
        
    end
        
    pl:CheckPlayerClassOnSpawn()
        
    if ( GAMEMODE.TeamBased && ( pl:Team() == TEAM_SPECTATOR || 

pl:Team() == TEAM_UNASSIGNED ) ) then

        GAMEMODE:PlayerSpawnAsSpectator( pl )
        return
    
    end
    
    // Stop observer mode
    pl:UnSpectate()

    // Call item loadout function
    hook.Call( "PlayerLoadout", GAMEMODE, pl )
    
    // Set player model
    hook.Call( "PlayerSetModel", GAMEMODE, pl )
    
    // Call class function
    pl:OnSpawn()
    
end


function GM:PlayerLoadout( pl )

    pl:CheckPlayerClassOnSpawn()

    pl:OnLoadout()
    
    // Switch to prefered weapon if they have it
    local cl_defaultweapon = pl:GetInfo( "cl_defaultweapon" )
    
    if ( pl:HasWeapon( cl_defaultweapon )  ) then
        pl:SelectWeapon( cl_defaultweapon ) 
    end
    
end


function GM:PlayerSetModel( pl )

    pl:OnPlayerModel()
    
end


function GM:AutoTeam( pl )

    if ( !GAMEMODE.AllowAutoTeam ) then return end
    if ( !GAMEMODE.TeamBased ) then return end
    
    GAMEMODE:PlayerRequestTeam( pl, team.BestAutoJoinTeam() )

end

concommand.Add( "autoteam", function( pl, cmd, args ) hook.Call( 

"AutoTeam", GAMEMODE, pl ) end )


function GM:PlayerRequestClass( ply, class, disablemessage )
    
    local Classes = team.GetClass( ply:Team() )
    if (!Classes) then return end
    
    local RequestedClass = Classes[ class ]
    if (!RequestedClass) then return end
    
    if ( ply:Alive() && SERVER ) then
    
        if ( ply.m_SpawnAsClass && ply.m_SpawnAsClass == 

RequestedClass ) then return end
    
        ply.m_SpawnAsClass = RequestedClass
        
        if ( !disablemessage ) then
            ply:ChatPrint( "Your class will change to '".. 

player_class.GetClassName( RequestedClass ) .. "' when you respawn" )
        end
        
    else
        self:PlayerJoinClass( ply, RequestedClass )
        ply.m_SpawnAsClass = nil
    end
    
end

concommand.Add( "changeclass", function( pl, cmd, args ) hook.Call( 

"PlayerRequestClass", GAMEMODE, pl, tonumber(args[1]) ) end )


local function SeenSplash( ply )

    if ( ply.m_bSeenSplashScreen ) then return end
    ply.m_bSeenSplashScreen = true
    
    if ( !GAMEMODE.TeamBased && !GAMEMODE.NoAutomaticSpawning ) then
        ply:KillSilent()
    end
    
end

concommand.Add( "seensplash", SeenSplash )


function GM:PlayerJoinTeam( ply, teamid )
    
    local iOldTeam = ply:Team()
    
    if ( ply:Alive() ) then
        if ( iOldTeam == TEAM_SPECTATOR || (iOldTeam == 

TEAM_UNASSIGNED && GAMEMODE.TeamBased) ) then
            ply:KillSilent()
        else
            ply:Kill()
        end
    end
    
    ply:SetTeam( teamid )
    ply.LastTeamSwitch = RealTime()
    
    local Classes = team.GetClass( teamid )
    
    
    // Needs to choose class
    if ( Classes && #Classes > 1 ) then
    
        if ( ply:IsBot() || !GAMEMODE.SelectClass ) then
    
            GAMEMODE:PlayerRequestClass( ply, math.random( 1, 

#Classes ) )
    
        else

            ply.m_fnCallAfterClassChoose = function() 
                                        

        ply.DeathTime = CurTime()
                                        

        GAMEMODE:OnPlayerChangedTeam( ply, iOldTeam, teamid ) 
                                        

        ply:EnableRespawn() 
                                        

    end

            ply:SendLua( "GAMEMODE:ShowClassChooser( ".. teamid 

.." )" )
            ply:DisableRespawn()
            ply:SetRandomClass() // put the player in a VALID 

class in case they don't choose and get spawned
            return
                    
        end
        
    end
    
    // No class, use default
    if ( !Classes || #Classes == 0 ) then
        ply:SetPlayerClass( "Default" )
    end
    
    // Only one class, use that
    if ( Classes && #Classes == 1 ) then
        GAMEMODE:PlayerRequestClass( ply, 1 )
    end
    
    GAMEMODE:OnPlayerChangedTeam( ply, iOldTeam, teamid )
    
end

function GM:PlayerJoinClass( ply, classname )

    ply.m_SpawnAsClass = nil
    ply:SetPlayerClass( classname )
    
    if ( ply.m_fnCallAfterClassChoose ) then
    
        ply.m_fnCallAfterClassChoose()
        ply.m_fnCallAfterClassChoose = nil
        
    end

end

function GM:OnPlayerChangedTeam( ply, oldteam, newteam )

    // Here's an immediate respawn thing by default. If you want to 
    // re-create something more like CS or some shit you could probably
    // change to a spectator or something while dead.
    if ( newteam == TEAM_SPECTATOR ) then
    
        // If we changed to spectator mode, respawn where we are
        local Pos = ply:EyePos()
        ply:Spawn()
        ply:SetPos( Pos )
        
    elseif ( oldteam == TEAM_SPECTATOR ) then
    
        // If we're changing from spectator, join the game
        if ( !GAMEMODE.NoAutomaticSpawning ) then
            ply:Spawn()
        end
    
    else
    
        // If we're straight up changing teams just hang
        //  around until we're ready to respawn onto the 
        //  team that we chose
        
    end
    
    //PrintMessage( HUD_PRINTTALK, Format( "%s joined '%s'", 

ply:Nick(), team.GetName( newteam ) ) )
    
    // Send umsg for team change
    local rf = RecipientFilter();
    rf:AddAllPlayers();
 
    umsg.Start( "fretta_teamchange", rf );
        umsg.Entity( ply );
        umsg.Short( oldteam );
        umsg.Short( newteam );
    umsg.End();
    
end

function GM:CheckTeamBalance()

    local highest
    local notice

    for id, tm in pairs( team.GetAllTeams() ) do
        if ( id > 0 && id < 1000 && team.Joinable( id ) ) then
            if ( !highest || team.NumPlayers( id ) > 

team.NumPlayers( highest ) ) then
            
                highest = id
                
            elseif team.NumPlayers( id ) < team.NumPlayers( 

highest ) then
                while team.NumPlayers( id ) < 

team.NumPlayers( highest ) - 1 do
                
                    local ply = 

GAMEMODE:FindLeastCommittedPlayerOnTeam( highest )

                    ply:Kill()
                    ply:SetTeam( id )

                    // Todo: Notify player 'you have 

been swapped'
                    // This is a placeholder
                    ply:ChatPrint( "You have been 

swapped to "..team.GetName( id ).." for team balance" )
                    
                    notice = true
                    
                end
            end
        end
    end
    
    if !notice then return end
    
    // Send a notice to every player here - Use chatprint? Something 

else?
    
end

//
// Todo before release: Move this to team module
//
function GM:FindLeastCommittedPlayerOnTeam( teamid )

    local worst = nil

    for k,v in pairs( team.GetPlayers( teamid ) ) do

        // Todo.. least time on team too
        if ( !worst || v:Frags() < worst:Frags() ) then
            worst = v
        end

    end
    
    return worst
    
end

function GM:OnEndOfGame()

    for k,v in pairs( player.GetAll() ) do

        v:Freeze(true)
        v:ConCommand( "+showscores" )
        
    end
    
end

// Override OnEndOfGame to do any other stuff. like winning music.
function GM:EndOfGame( bGamemodeVote )

    if GAMEMODE.IsEndOfGame then return end

    GAMEMODE.IsEndOfGame = true
    SetGlobalBool( "IsEndOfGame", true );
    
    GAMEMODE:OnEndOfGame();
    
    if ( bGamemodeVote ) then
    
        MsgN( "Starting gamemode voting..." )
        PrintMessage( HUD_PRINTTALK, "Starting gamemode voting..." 

);
        timer.Simple( GAMEMODE.VotingDelay, function() 

GAMEMODE:StartGamemodeVote() end )
        
    end

end

function GM:GetWinningFraction()
    if ( !GAMEMODE.GMVoteResults ) then return end
    return GAMEMODE.GMVoteResults.Fraction
end

function GM:PlayerShouldTakeDamage( ply, attacker )

    if ( GAMEMODE.NoPlayerSelfDamage && IsValid( attacker ) && ply == 

attacker ) then return false end
    if ( GAMEMODE.NoPlayerDamage ) then return false end
    
    if ( GAMEMODE.NoPlayerTeamDamage && IsValid( attacker ) ) then
        if ( attacker.Team && ply:Team() == attacker:Team() && ply 

!= attacker ) then return false end
    end
    
    if ( IsValid( attacker ) && attacker:IsPlayer() && 

GAMEMODE.NoPlayerPlayerDamage ) then return false end
    if ( IsValid( attacker ) && !attacker:IsPlayer() && 

GAMEMODE.NoNonPlayerPlayerDamage ) then return false end
    
    return true

end


function GM:PlayerDeathThink( pl )

    pl.DeathTime = pl.DeathTime or CurTime()
    local timeDead = CurTime() - pl.DeathTime
    
    // If we're in deathcam mode, promote to a generic spectator mode
    if ( GAMEMODE.DeathLingerTime > 0 && timeDead > 

GAMEMODE.DeathLingerTime && ( pl:GetObserverMode() == OBS_MODE_FREEZECAM || 

pl:GetObserverMode() == OBS_MODE_DEATHCAM ) ) then
        GAMEMODE:BecomeObserver( pl )
    end
    
    // If we're in a round based game, player NEVER spawns in death 

think
    if ( GAMEMODE.NoAutomaticSpawning ) then return end
    
    // The gamemode is holding the player from respawning.
    // Probably because they have to choose a class..
    if ( !pl:CanRespawn() ) then return end

    // Don't respawn yet - wait for minimum time...
    if ( GAMEMODE.MinimumDeathLength ) then 
    
        pl:SetNWFloat( "RespawnTime", pl.DeathTime + 

GAMEMODE.MinimumDeathLength )
        
        if ( timeDead < pl:GetRespawnTime() ) then
            return
        end
        
    end

    // Force respawn
    if ( pl:GetRespawnTime() != 0 && GAMEMODE.MaximumDeathLength != 0 

&& timeDead > GAMEMODE.MaximumDeathLength ) then
        pl:Spawn()
        return
    end

    // We're between min and max death length, player can press a key 

to spawn.
    if ( pl:KeyPressed( IN_ATTACK ) || pl:KeyPressed( IN_ATTACK2 ) || 

pl:KeyPressed( IN_JUMP ) ) then
        pl:Spawn()
    end
    
end

function GM:GetFallDamage( ply, flFallSpeed )
    
    if ( GAMEMODE.RealisticFallDamage ) then
        return flFallSpeed / 8
    end
    
    return 10
    
end

function GM:PostPlayerDeath( ply )

    // Note, this gets called AFTER DoPlayerDeath.. AND it gets called
    // for KillSilent too. So if Freezecam isn't set by DoPlayerDeath, 

we
    // pick up the slack by setting DEATHCAM here.
    
    if ( ply:GetObserverMode() == OBS_MODE_NONE ) then
        ply:Spectate( OBS_MODE_DEATHCAM )
    end    
    
    ply:OnDeath()

end

function GM:DoPlayerDeath( ply, attacker, dmginfo )

    ply:CallClassFunction( "OnDeath", attacker, dmginfo )
    ply:CreateRagdoll()
    ply:AddDeaths( 1 )
    
    if ( attacker:IsValid() && attacker:IsPlayer() ) then
    
        if ( attacker == ply ) then
        
            if ( GAMEMODE.TakeFragOnSuicide ) then
            
                attacker:AddFrags( -1 )
                
                if ( GAMEMODE.TeamBased && 

GAMEMODE.AddFragsToTeamScore ) then
                    team.AddScore( attacker:Team(), -1 

)
                end
            
            end
            
        else
        
            attacker:AddFrags( 1 )
            
            if ( GAMEMODE.TeamBased && 

GAMEMODE.AddFragsToTeamScore ) then
                team.AddScore( attacker:Team(), 1 )
            end
            
        end
        
    end
    
    if ( GAMEMODE.EnableFreezeCam && IsValid( attacker ) && attacker != 

ply ) then
    
        ply:SpectateEntity( attacker )
        ply:Spectate( OBS_MODE_FREEZECAM )
        
    end
    
end

function GM:StartSpectating( ply )

    if ( !GAMEMODE:PlayerCanJoinTeam( ply ) ) then return end
    
    ply:StripWeapons();
    GAMEMODE:PlayerJoinTeam( ply, TEAM_SPECTATOR )
    GAMEMODE:BecomeObserver( ply )

end


function GM:EndSpectating( ply )

    if ( !GAMEMODE:PlayerCanJoinTeam( ply ) ) then return end

    GAMEMODE:PlayerJoinTeam( ply, TEAM_UNASSIGNED )
    
    ply:KillSilent()

end

/*---------------------------------------------------------
   Name: gamemode:PlayerRequestTeam()
        Player wants to change team
---------------------------------------------------------*/
function GM:PlayerRequestTeam( ply, teamid )

    if ( !GAMEMODE.TeamBased && GAMEMODE.AllowSpectating ) then
    
        if ( teamid == TEAM_SPECTATOR ) then
            GAMEMODE:StartSpectating( ply )
        else
            GAMEMODE:EndSpectating( ply )
        end
    
        return
    
    end
    
    return self.BaseClass:PlayerRequestTeam( ply, teamid )

end

local function TimeLeft( ply )

    local tl = GAMEMODE:GetGameTimeLeft()
    if ( tl == -1 ) then return end
    
    local Time = string.ToMinutesSeconds( tl )
    
    if ( IsValid( ply ) ) then
        ply:PrintMessage( HUD_PRINTCONSOLE, Time )
    else
        MsgN( Time )
    end
    
end

concommand.Add( "timeleft", TimeLeft )

Shared.lua:


/*
    shared.lua - Shared Component
    -----------------------------------------------------
    This is the shared component of your gamemode, a lot of the game variables
    can be changed from here.
*/

include( "player_class.lua" )
include( "player_extension.lua" )
include( "class_default.lua" )
include( "player_colours.lua" )

fretta_voting = CreateConVar( "fretta_voting", "1", { FCVAR_REPLICATED, FCVAR_NOTIFY, FCVAR_ARCHIVE } )

GM.Name     = "Team Cock Match"
GM.Author     = "Nosehair26"
GM.Email     = ""
GM.Website     = "www.meatspin.com"
GM.Help        = "GOD CAN'T HELP YOU NOW"

GM.SelectClass = true
GM.TeamBased = true                    // Team based game or a Free For All game?
GM.AllowAutoTeam = true                // Allow auto-assign?
GM.AllowSpectating = true            // Allow people to spectate during the game?
GM.SecondsBetweenTeamSwitches = 10    // The minimum time between each team change?
GM.GameLength = 15                    // The overall length of the game
GM.RoundLimit = -1                    // Maximum amount of rounds to be played in round based games
GM.VotingDelay = 5                    // Delay between end of game, and vote. if you want to display any extra screens before the vote pops up

GM.NoPlayerSuicide = false            // Set to true if players should not be allowed to commit suicide.
GM.NoPlayerDamage = false            // Set to true if players should not be able to damage each other.
GM.NoPlayerSelfDamage = false        // Allow players to hurt themselves?
GM.NoPlayerTeamDamage = true        // Allow team-members to hurt each other?
GM.NoPlayerPlayerDamage = false     // Allow players to hurt each other?
GM.NoNonPlayerPlayerDamage = false     // Allow damage from non players (physics, fire etc)
GM.NoPlayerFootsteps = false        // When true, all players have silent footsteps
GM.PlayerCanNoClip = false            // When true, players can use noclip without sv_cheats
GM.TakeFragOnSuicide = true            // -1 frag on suicide

GM.MaximumDeathLength = 0            // Player will repspawn if death length > this (can be 0 to disable)
GM.MinimumDeathLength = 2            // Player has to be dead for at least this long
GM.AutomaticTeamBalance = false     // Teams will be periodically balanced 
GM.ForceJoinBalancedTeams = true    // Players won't be allowed to join a team if it has more players than another team
GM.RealisticFallDamage = false        // Set to true if you want realistic fall damage instead of the fix 10 damage.
GM.AddFragsToTeamScore = false        // Adds player's individual kills to team score (must be team based)

GM.NoAutomaticSpawning = false        // Players don't spawn automatically when they die, some other system spawns them
GM.RoundBased = false                // Round based, like CS
GM.RoundLength = 30                    // Round length, in seconds
GM.RoundPreStartTime = 5            // Preperation time before a round starts
GM.RoundPostLength = 8                // Seconds to show the 'x team won!' screen at the end of a round
GM.RoundEndsWhenOneTeamAlive = true    // CS Style rules

GM.EnableFreezeCam = false            // TF2 Style Freezecam
GM.DeathLingerTime = 4                // The time between you dying and it going into spectator mode, 0 disables

GM.SelectModel = true               // Can players use the playermodel picker in the F1 menu?
GM.SelectColor = false                // Can players modify the colour of their name? (ie.. no teams)

GM.PlayerRingSize = 48              // How big are the colored rings under the player's feet (if they are enabled) ?
GM.HudSkin = "SimpleSkin"            // The Derma skin to use for the HUD components
GM.SuicideString = "died"            // The string to append to the player's name when they commit suicide.
GM.DeathNoticeDefaultColor = Color( 255, 128, 0 ); // Default colour for entity kills
GM.DeathNoticeTextColor = color_white; // colour for text ie. "died", "killed"

GM.ValidSpectatorModes = { OBS_MODE_CHASE, OBS_MODE_IN_EYE, OBS_MODE_ROAMING } // The spectator modes that are allowed
GM.ValidSpectatorEntities = { "player" }    // Entities we can spectate, players being the obvious default choice.
GM.CanOnlySpectateOwnTeam = true; // you can only spectate players on your own team

DeriveGamemode( "base" )

TEAM_BLUE         = 1
TEAM_RED         = 2

/*---------------------------------------------------------
   Name: gamemode:CreateTeams()
   Desc: Set up all your teams here. Note - HAS to be shared.
---------------------------------------------------------*/
function GM:CreateTeams()

    if ( !GAMEMODE.TeamBased ) then return end
    
    team.SetUp( TEAM_BLUE, "CUNTer-Terrorists", Color( 80, 150, 255 ) )
    team.SetSpawnPoint( TEAM_BLUE, "info_player_counterterrorist", true )
        team.SetClass( TEAM_BLUE, { "class_ass-salt", "class_scoot", "class_grenadier" } )
    
    team.SetUp( TEAM_RED, "Dem' Dirty Towel Heads", Color( 255, 80, 80 ) )
    team.SetSpawnPoint( TEAM_RED, "info_player_terrorist", true )
        team.SetClass( TEAM_RED, { "class_ass-salt", "class_scoot", "class_grenadier" } )
    
    team.SetUp( TEAM_SPECTATOR, "Spick-Taters", Color( 200, 200, 200 ), true )
    team.SetSpawnPoint( TEAM_SPECTATOR, "info_player_start" )
    team.SetClass( TEAM_SPECTATOR, { "Spectator" } )

end

function GM:InGamemodeVote()
    return GetGlobalBool( "InGamemodeVote", false )
end

/*---------------------------------------------------------
   Name: gamemode:TeamHasEnoughPlayers( Number teamid )
   Desc: Return true if the team has too many players.
         Useful for when forced auto-assign is on.
---------------------------------------------------------*/
function GM:TeamHasEnoughPlayers( teamid )

    local PlayerCount = team.NumPlayers( teamid )

    // Don't let them join a team if it has more players than another team
    if ( GAMEMODE.ForceJoinBalancedTeams ) then
    
        for id, tm in pairs( team.GetAllTeams() ) do
            if ( id > 0 && id < 1000 && team.NumPlayers( id ) < PlayerCount && team.Joinable(id) ) then return true end
        end
        
    end

    return false
    
end

/*---------------------------------------------------------
   Name: gamemode:PlayerCanJoinTeam( Player ply, Number teamid )
   Desc: Are we allowed to join a team? Return true if so.
---------------------------------------------------------*/
function GM:PlayerCanJoinTeam( ply, teamid )

    if ( SERVER && !self.BaseClass:PlayerCanJoinTeam( ply, teamid ) ) then 
        return false 
    end

    if ( GAMEMODE:TeamHasEnoughPlayers( teamid ) ) then
        ply:ChatPrint( "That team is full!" )
        return false
    end
    
    return true
    
end

/*---------------------------------------------------------
   Name: gamemode:Move( Player ply, CMoveData mv )
   Desc: Setup Move, this also calls the player's class move
         function.
---------------------------------------------------------*/
function GM:Move( ply, mv )

    if ( ply:CallClassFunction( "Move", mv ) ) then return true end

end

/*---------------------------------------------------------
   Name: gamemode:KeyPress( Player ply, Number key )
   Desc: Player presses a key, this also calls the player's class
         OnKeyPress function.
---------------------------------------------------------*/
function GM:KeyPress( ply, key )

    if ( ply:CallClassFunction( "OnKeyPress", key ) ) then return true end

end

/*---------------------------------------------------------
   Name: gamemode:KeyRelease( Player ply, Number key )
   Desc: Player releases a key, this also calls the player's class
         OnKeyRelease function.
---------------------------------------------------------*/
function GM:KeyRelease( ply, key )

    if ( ply:CallClassFunction( "OnKeyRelease", key ) ) then return true end

end

/*---------------------------------------------------------
   Name: gamemode:PlayerFootstep( Player ply, Vector pos, Number foot, String sound, Float volume, CReceipientFilter rf )
   Desc: Player's feet makes a sound, this also calls the player's class Footstep function.
         If you want to disable all footsteps set GM.NoPlayerFootsteps to true.
         If you want to disable footsteps on a class, set Class.DisableFootsteps to true.
---------------------------------------------------------*/
function GM:PlayerFootstep( ply, pos, foot, sound, volume, rf ) 

    if( GAMEMODE.NoPlayerFootsteps || !ply:Alive() || ply:Team() == TEAM_SPECTATOR || ply:IsObserver() ) then
        return true;
    end
    
    local Class = ply:GetPlayerClass();
    if( !Class ) then return end
    
    if( Class.DisableFootsteps ) then // rather than using a hook, we can just do this to override the function instead.
        return true;
    end
    
    if( Class.Footstep ) then
        return Class:Footstep( ply, pos, foot, sound, volume, rf ); // Call footstep function in class, you can use this to make custom footstep sounds
    end
    
end

/*---------------------------------------------------------
   Name: gamemode:CalcView( Player ply, Vector origin, Angles angles, Number fov )
   Desc: Calculates the players view. Also calls the players class
         CalcView function, as well as GetViewModelPosition and CalcView
         on the current weapon. Returns a table.
---------------------------------------------------------*/
function GM:CalcView( ply, origin, angles, fov )

    local view = ply:CallClassFunction( "CalcView", origin, angles, fov ) or { ["origin"] = origin, ["angles"] = angles, ["fov"] = fov };
    
    origin = view.origin or origin
    angles = view.angles or angles
    fov = view.fov or fov
        
    local wep = ply:GetActiveWeapon()
    if ( ValidEntity( wep ) ) then
    
        local func = wep.GetViewModelPosition
        if ( func ) then view.vm_origin,  view.vm_angles = func( wep, origin*1, angles*1 ) end
        
        local func = wep.CalcView
        if ( func ) then view.origin, view.angles, view.fov = func( wep, ply, origin*1, angles*1, fov ) end
    
    end

    return view
    
end

/*---------------------------------------------------------
   Name: gamemode:GetTimeLimit()
   Desc: Returns the time limit of a game in seconds, so you could
         make it use a cvar instead. Return -1 for unlimited.
         Unlimited length games can be changed using vote for
         change.
---------------------------------------------------------*/
function GM:GetTimeLimit()

    if( GAMEMODE.GameLength > 0 ) then
        return GAMEMODE.GameLength * 60;
    end
    
    return -1;
    
end

/*---------------------------------------------------------
   Name: gamemode:GetGameTimeLeft()
   Desc: Get the remaining time in seconds.
---------------------------------------------------------*/
function GM:GetGameTimeLeft()

    local EndTime = GAMEMODE:GetTimeLimit();
    if ( EndTime == -1 ) then return -1 end
    
    return EndTime - CurTime()

end

/*---------------------------------------------------------
   Name: gamemode:PlayerNoClip( player, bool )
   Desc: Player pressed the noclip key, return true if
          the player is allowed to noclip, false to block
---------------------------------------------------------*/
function GM:PlayerNoClip( pl, on )
    
    // Allow noclip if we're in single player or have cheats enabled
    if ( GAMEMODE.PlayerCanNoClip || SinglePlayer() || GetConVar( "sv_cheats" ):GetBool() ) then return true end
    
    // Don't if it's not.
    return false
    
end


// This function includes /yourgamemode/player_class/*.lua
// And AddCSLuaFile's each of those files.
// You need to call it in your derived shared.lua IF you have files in that folder
// and want to include them!

function IncludePlayerClasses()

    local Folder = string.Replace( GM.Folder, "gamemodes/", "" );

    for c,d in pairs(file.FindInLua(Folder.."/gamemode/player_class/*.lua")) do
        include( Folder.."/gamemode/player_class/"..d )
        AddCSLuaFile( Folder.."/gamemode/player_class/"..d )
    end

end

IncludePlayerClasses()        

Thanks for taking this post into consideration, guize.

Edit: I don’t even know if this belongs in the gamemode forums but I’m a total noob on Facepunch.

Wouldnt the ( ’ ) conflict with something?

[sp]I dont think you can have it there.[/sp]

No, it doesn’t conflict with anything. Because the " starting the string is a " not a '. It’s fine,

Yea, I know, it would conflict with that, but arent you opening a new string with the ( ’ ) ? Thus needing to create another one to close it?

I might just be terribly wrong.

[lua]
print( " Look, a '." )
[/lua]

The string is being started with a


"

not a


'

. Meaning you can’t end it with a


'

only a


"

.

Oh I read your comment wrong, and no, you aren’t. It’s already in a string.

Okay, I get it :slight_smile:

xD

No errors in your console?

There are errors in my console, but I don’t believe they’re related to my game mode.

EDIT: I found the solution, everyone. I had to replace the “player_class.Register( “insertclasshere”, CLASS )” with "player_class.Register( “**class_**insertmyclass”, CLASS )

Thanks for your attempts at helping me everyone, I appreciate it.

Good luck with your project brodooski. (You know who this is)