AddCSLuaFile( "cl_init.lua" ) AddCSLuaFile( "cl_hud.lua" ) AddCSLuaFile( "shared.lua" ) include( "shared.lua" ) include( "levels.lua" ) include( "resources.lua" ) GM.TeamCurrentCheckpoint = {} for i=1, 4 do GM.TeamCurrentCheckpoint[i] = 0 end GM.TeamTotalProps = {} local propPickupSound = Sound( "npc/scanner/scanner_nearmiss1.wav" ) local propDropSound = Sound( "npc/scanner/scanner_nearmiss2.wav" ) local checkpointSound = Sound( "ambient/levels/labs/machine_stop1.wav" ) local AFKThinkTimer = 0 local propThink = 0.5 local propLastThink = 0 local pointerLastThink = 0 local pointerThink = 0.20 local pointerCheckpointGap = 128 local totalPointerHeight = 416 function GM:KeyPress( ply, key ) if ( key == IN_USE and ply:Alive() ) then local tr = ply:GetEyeTraceNoCursor() if ( ply.HoldingItem == nil and tr.Entity:IsValid() and tr.Entity:GetClass() == "ascension_prop" ) then if ( tr.Entity:GetOwner() == NULL and tr.Entity:GetPos():Distance( ply:GetPos() + Vector(0,0,64) ) < 100 ) then tr.Entity:EmitSound( propPickupSound ) tr.Entity:SetOwner( ply ) tr.Entity:SetCollisionGroup( COLLISION_GROUP_WEAPON ) local phys = tr.Entity:GetPhysicsObject() if ( phys:IsValid() ) then phys:Wake() phys:EnableMotion( true ) end tr.Entity:ActivateBlock() ply.HoldingItem = tr.Entity umsg.Start( "PlyIsHolding" ) umsg.Entity( ply ) umsg.Entity( ply.HoldingItem ) umsg.End() end elseif ( ply.HoldingItem != nil ) then if ( ply.HoldingItem:IsValid() and ply.HoldingItem:GetOwner() == ply ) then local object = ply.HoldingItem timer.Simple( 0.5, function() // We need this delay to stop players boosting themselves really high when they drop blocks. if (object and object:IsValid()) then object:SetCollisionGroup( COLLISION_GROUP_NONE ) end end ) ply.HoldingItem:EmitSound( propDropSound ) ply.HoldingItem:SetOwner( NULL ) ply.HoldingItem:GetPhysicsObject():Wake() ply.HoldingItem = nil umsg.Start( "PlyIsHolding" ) umsg.Entity( ply ) umsg.Entity( nil ) umsg.End() end end end end function GM:NumPlyInCheckpoint( teamid, checkpointid ) //TODO: find a better way to handle checkpoints? local str if ( teamid == TEAM_ORANGE ) then str = "orange_checkpoint"..checkpointid elseif ( teamid == TEAM_RED ) then str = "red_checkpoint"..checkpointid elseif ( teamid == TEAM_GREEN ) then str = "green_checkpoint"..checkpointid else str = "blue_checkpoint"..checkpointid end local num = 0 for k, v in pairs( ents.FindByClass( str ) ) do num = v:GetNWInt("PlyInsideCheckpoint") end return num end function GM:OnRoundEnd( num ) umsg.Start( "StartRestartTimer" ) umsg.Long( GAMEMODE.RoundPostLength+CurTime() ) umsg.End() end function GM:CheckRoundEnd() //Get the total number of players alive local alivePlayers = 0 for k,v in pairs( player.GetAll() ) do if ( v:Alive() ) then alivePlayers = alivePlayers + 1 end end if ( GAMEMODE:InRound() ) then for teamid=1, 4 do //Get the number of players alive on specified team local numOfAlivePlayers = 0 for k,v in pairs( team.GetPlayers( teamid ) ) do if ( v:Alive() ) then numOfAlivePlayers = numOfAlivePlayers + 1 end end if ( alivePlayers > 1 and numOfAlivePlayers > 0 and numOfAlivePlayers == self:NumPlyInCheckpoint( teamid, 4 ) && self:NumPlyInCheckpoint( teamid, 3 ) == 0 ) then // We need to check if we're still in round just in case two teams finished at the exact same time. (So we can call "TeamReachedTopNotify") if (GAMEMODE:InRound()) then GAMEMODE:RoundEndWithResult( teamid, team.GetName(teamid) .. " wins!" ) // Probably need to find a better way to do this, but we'll send the team colour as a vector local colour = team.GetColor( teamid ) local effectdata = EffectData() effectdata:SetStart( Vector( colour.r, colour.g, colour.b ) ) for delay=0, 7 do timer.Simple( delay, function() effectdata:SetOrigin( Vector( -1336 + (560*teamid),0,(1500) ) + (Vector(math.random(-4,4),0,math.random(-2,2)) * 20) ) util.Effect( "effect_team_won", effectdata ) util.Effect( "effect_team_won_bits", effectdata ) end ) end else self:TeamReachedTop( teamid ) end self:TeamReachedCheckpoint( 4, teamid, true ) self.TeamCurrentCheckpoint[teamid] = 4 end for checkpoint=2, 3 do if ( numOfAlivePlayers > 0 and self:NumPlyInCheckpoint( teamid, checkpoint ) == numOfAlivePlayers ) then if ( self.TeamCurrentCheckpoint[teamid] < checkpoint && self:NumPlyInCheckpoint( teamid, self.TeamCurrentCheckpoint[teamid] ) == 0 ) then self.TeamCurrentCheckpoint[teamid] = checkpoint self:TeamReachedCheckpoint( checkpoint, teamid, false ) umsg.Start( "CheckpointNotify" ) umsg.Short( teamid ) umsg.Short( checkpoint ) umsg.End() end end end end else for teamid=1, 4 do local numOfAlivePlayers = 0 for k,v in pairs( team.GetPlayers( teamid ) ) do if ( v:Alive() ) then numOfAlivePlayers = numOfAlivePlayers + 1 end end if ( alivePlayers > 1 and numOfAlivePlayers > 0 and numOfAlivePlayers == self:NumPlyInCheckpoint( teamid, 4 ) && self:NumPlyInCheckpoint( teamid, 3 ) == 0 ) then self:TeamReachedCheckpoint( 4, teamid, true ) self:TeamReachedTop( teamid ) end end end end function GM:OnRoundResult( result, resulttext ) local num = 0 for k, v in pairs( player.GetAll() ) do if ( v:Alive() ) then num = num + 1 end end // We only want to add score when there's one or more players playing, so that a team doesn't get a score when there are no other players playing. if ( num > 1 ) then team.AddScore( result, 1 ) end end function GM:TeamReachedTop( teamid ) local position = 1 for id=1, 4 do if ( self.TeamCurrentCheckpoint[id] == 4 and id != teamid ) then position = position + 1 end end umsg.Start( "TeamReachedTopNotify" ) umsg.Short( teamid ) umsg.Short( position ) umsg.End() end function IntializeAscensionEntities() for k, v in pairs( ents.FindByClass("func_wall") ) do local kvalue = v:GetKeyValues().target if ( kvalue != "" ) then GAMEMODE.MapProps[kvalue] = v end end for k, v in pairs( ents.FindByClass("func_illusionary") ) do local kvalue = v:GetKeyValues().target if ( kvalue != "" ) then GAMEMODE.MapIllusionaries[kvalue] = v end end // For appearance sake, we'll create some platforms and spawners to make it look pretty until the first player starts a game. GAMEMODE:ObstacleSetup() GAMEMODE:SpawnerSetup() end hook.Add( "InitPostEntity", "IntializeAscensionEntities", IntializeAscensionEntities ) function PlayerInitSpawn( ply ) timer.Simple( 1, function() umsg.Start( "CPlayerInitialSpawn_umsg" ) umsg.End() end ) end hook.Add( "PlayerInitialSpawn", "PlayerInitSpawn_umsg", PlayerInitSpawn ) function GM:TeamReachedCheckpoint( checkpoint, teamid, finished ) // Find a better way to emit the gamemode related sounds? for k, v in pairs( player.GetAll() ) do v:EmitSound(checkpointSound) break end local teamcol = self:GetTeamColour( teamid ) for hole=1, 3 do self:ChangeHoleVisibility( false, teamcol, checkpoint-1, hole, teamid ) end umsg.Start( "UpdatePushEmitter" ) umsg.Short( teamid ) umsg.Short( checkpoint-1 ) umsg.Bool( false ) umsg.Vector( Vector(0,0,0) ) umsg.End() if (!finished) then for k, ent in pairs( ents.FindByClass( "prop_spawner" ) ) do if ( ent:GetTeam() == teamid ) then ent:SetPos( ent:GetPos() + Vector(0,0,416) ) ent:ResetLastProp() end end for k, ent in pairs( ents.FindByClass( "ascension_prop" ) ) do if ( ent:GetTeam() == teamid ) then local effectdata = EffectData() effectdata:SetOrigin( ent:GetPos() ) util.Effect( "effect_prop_remove", effectdata ) ent:Remove() end end for k, ply in pairs( team.GetPlayers( teamid ) ) do ply.HoldingItem = nil umsg.Start( "PlyIsHolding" ) umsg.Entity( ply ) umsg.Entity( nil ) umsg.End() end self.TeamTotalProps[teamid] = 0 end end function GM:ObstacleSetup() for k, v in pairs( ents.FindByClass("trigger_push") ) do local kvalue = v:GetKeyValues().target if ( kvalue != "" ) then GAMEMODE.MapPushes[kvalue] = v end end local teamcol = 0 for layer=0, 2 do local set = nil for i=1, 100 do set = math.random( 1, #self.LevelSets.holes ) local used = false for i=0, 2 do if (set == self.LastSelected[i]) then used = true end end if (!used) then self.LastSelected[layer] = set break end end self.SelectedLevelHoles = self.LevelSets.holes[set] self.SelectedLevelProps = self.LevelSets.props[set] for i=1, 4 do teamcol = self:GetTeamColour( i ) for hv, h in SortedPairs( self.SelectedLevelHoles ) do if (h == 1) then self:ChangeHoleVisibility( false, teamcol, layer+1, hv, i ) else self:ChangeHoleVisibility( true, teamcol, layer+1, hv, i ) end end for k, prop in pairs( self.SelectedLevelProps ) do if (prop != nil && type(prop) == "Vector") then local rotation = nil if (self.SelectedLevelProps[k.."_rotation"]) then rotation = self.SelectedLevelProps[k.."_rotation"] end self:CreateAscensionPlatform( prop + Vector( (-1656) + (560*i),0,(416*layer) ), i, rotation ) end end end end end function GM:ChangeHoleVisibility( invisible, teamcol, checkpoint, hole, teamid ) local prop = self.MapProps[tostring(teamcol.."_checkpoint"..checkpoint.."_hole"..hole)] prop:SetNotSolid( invisible ) prop:SetNoDraw( invisible ) local propIllusionary = self.MapIllusionaries[tostring(teamcol.."_checkpoint"..checkpoint.."_hole"..hole)] propIllusionary:SetNoDraw( invisible ) local effect = self.MapIllusionaries[tostring(teamcol.."_checkpoint"..checkpoint.."_effect"..hole)] local push = self.MapPushes[tostring(teamcol.."_checkpoint"..checkpoint.."_push"..hole)] if ( effect:IsValid() ) then if (invisible) then local mins, maxs = prop:WorldSpaceAABB(); local pos = ( mins + maxs ) * 0.5; self:CreateAscensionPlatform( pos - Vector(0,0,124), teamid ) effect:SetNoDraw( false ) push:Fire( "enable", "", 0 ) umsg.Start( "UpdatePushEmitter" ) umsg.Short( teamid ) umsg.Short( checkpoint ) umsg.Bool( true ) umsg.Vector( pos - Vector(0,0,110) ) umsg.End() else effect:SetNoDraw( true ) push:Fire( "disable", "", 0 ) end end end function GM:CreateAscensionPlatform( pos, teamid, rotation ) local ent = ents.Create( "prop_physics" ) ent:SetModel( "models/ascension/objects/platform_01.mdl" ) ent:SetPos( pos ) if (rotation) then ent:SetAngles( Angle(0,90,rotation) ) else ent:SetAngles( Angle(0,90,0) ) end ent:SetSkin( self:GetPlatformColour(teamid) ) ent:Spawn() ent:GetPhysicsObject():EnableMotion( false ) end function GM:GetPlatformColour( teamid ) if (teamid == TEAM_ORANGE) then return 3 elseif (teamid == TEAM_BLUE) then return 4 elseif (teamid == TEAM_RED) then return 1 elseif (teamid == TEAM_GREEN) then return 2 end end function GM:GetTeamColour( teamid ) if (teamid == TEAM_ORANGE) then return "orange" elseif (teamid == TEAM_GREEN) then return "green" elseif (teamid == TEAM_RED) then return "red" elseif (teamid == TEAM_BLUE) then return "blue" end end function GM:SpawnerSetup() for i=1, 4 do // Reset teams prop count self.TeamTotalProps[i] = 0 end for p=0, 1 do for i=1, 4 do local spawner = ents.Create( "prop_spawner" ) spawner:SetPos( Vector( (-1592) + (384*p) + (560*i),0,0 ) ) spawner:SetTeam( i ) spawner:Spawn() end end end /* // Not sure if I need this function still, so i'm keeping it here just in case I do later on. function GM:OnRoundStart( num ) UTIL_UnFreezeAllPlayers() end */ function GM:OnPreRoundStart( num ) game.CleanUpMap() self:ObstacleSetup() self:SpawnerSetup() for teamid=1, 4 do self.TeamCurrentCheckpoint[teamid] = 1 end UTIL_StripAllPlayers() UTIL_SpawnAllPlayers() UTIL_FreezeAllPlayers() for k, ply in pairs( player.GetAll() ) do if ( ply and ply:IsValid() ) then ply.HoldingItem = nil umsg.Start( "PlyIsHolding" ) umsg.Entity( ply ) umsg.Entity( nil ) umsg.End() umsg.Start( "startGoMessage" ) umsg.End() end end end function PointerThink() if ( pointerLastThink < CurTime() ) then local orangePos = 0 local greenPos = 0 local redPos = 0 local bluePos = 0 for teamID=1, 4 do local total = 0 local totalValue = 0 for k, ply in pairs( team.GetPlayers( teamID ) ) do if ( ply:Alive() ) then total = total + 1 local value = ply:GetPos().z + totalPointerHeight - (GAMEMODE.TeamCurrentCheckpoint[teamID]*totalPointerHeight) if (value > totalPointerHeight) then totalValue = totalValue + totalPointerHeight else totalValue = totalValue + value end end end if (totalValue > 0) then local checkpointNum = GAMEMODE.TeamCurrentCheckpoint[teamID]-1 if ( checkpointNum < 3 ) then totalValue = (((totalValue/total) / totalPointerHeight) * pointerCheckpointGap) + (pointerCheckpointGap * checkpointNum) else totalValue = 384 end if ( teamID == TEAM_ORANGE ) then orangePos = totalValue elseif ( teamID == TEAM_GREEN ) then greenPos = totalValue elseif ( teamID == TEAM_RED ) then redPos = totalValue elseif ( teamID == TEAM_BLUE ) then bluePos = totalValue end end end umsg.Start( "UpdatePointers" ) umsg.Long( orangePos ) umsg.Long( greenPos ) umsg.Long( redPos ) umsg.Long( bluePos ) umsg.End() pointerLastThink = pointerThink + CurTime() end end hook.Add( "Think", "PointerThink", PointerThink ) function PropThink() for k, ply in pairs( player.GetAll() ) do if ( ply:Alive() ) then if ( ply:IsOnGround() /*and ply.SittingOnProp == nil */) then local ent = ply:GetGroundEntity() if ( ent:GetClass() == "ascension_prop" ) then if ( ply.SittingOnProp == nil ) then ply.SittingOnProp = ent local phys = ent:GetPhysicsObject() if ( phys:IsValid() ) then phys:EnableMotion( false ) end end end elseif ( ply.SittingOnProp and ply.SittingOnProp:IsValid() ) then local phys = ply.SittingOnProp:GetPhysicsObject() if ( phys:IsValid() ) then phys:EnableMotion( true ) ply.SittingOnProp = nil end end end end end hook.Add( "Think", "PropThink", PropThink ) function AFKThink() if ( AFKThinkTimer < CurTime() ) then for k, ply in pairs(player.GetAll()) do if ( ply:Team() != TEAM_SPECTATOR and ply:Alive() ) then if ( not ply.LastPos or not ply.LastMoved ) then ply.LastPos = ply:GetPos() ply.LastMoved = CurTime() end if (CurTime()-ply.LastMoved > 45 and CurTime()-ply.LastMoved < 46 ) then ply:PrintMessage( HUD_PRINTTALK, "You will be moved to spectators unless you move within the next 15 seconds!" ) end if ( ply.LastPos != ply:GetPos() ) then ply.LastMoved = CurTime() ply.LastPos = ply:GetPos() elseif (CurTime()-ply.LastMoved > 60) then ply:KillSilent() ply:SetTeam( TEAM_SPECTATOR ) ply:PrintMessage( HUD_PRINTTALK, "You've been moved to spectators due to being idle." ) ply.LastMoved = CurTime() end end end AFKThinkTimer = CurTime() + 1 end end hook.Add( "Think", "AFKThink", AFKThink ) function GM:OnPlayerChangedTeam( ply, oldteam, newteam ) 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 or oldteam == TEAM_UNASSIGNED ) then local plyAlive = 0 for k, v in pairs( player.GetAll() ) do if ( v:Alive() ) then plyAlive = plyAlive + 1 end end if ( plyAlive == 1 ) then if (GAMEMODE:InRound()) then GAMEMODE:RoundEndWithResult( 1001, "Draw - Nobody Wins" ) end end //Check to see if there's more than one player alive before we spawn, otherwise we'll just wait for the new round to automatically start. //if ( plyAlive > 1 ) then ply:Spawn() local checkpoint = self.TeamCurrentCheckpoint[newteam]-1 if ( checkpoint > 0 ) then ply:SetPos( ply:GetPos() + Vector(0,0,(totalPointerHeight*checkpoint)) ) end //end else end PrintMessage( HUD_PRINTTALK, Format( "%s joined '%s'", ply:Nick(), team.GetName( newteam ) ) ) end function GM:PlayerUse() //Return false so player's can't 'use'. (This is to remove the annoying use sound when you don't actually 'use' an entity.) return false end function GM:ShowSpare1( ply ) if (ply.Overview) then ply.Overview = false else ply.Overview = true end umsg.Start( "ChangeOverview", ply ) umsg.Bool( ply.Overview ) umsg.End() end