Advertisement
Guest User

Untitled

a guest
Sep 19th, 2013
105
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ---- Trouble in Terrorist Town
  2.  
  3. AddCSLuaFile("cl_init.lua")
  4. AddCSLuaFile("shared.lua")
  5. AddCSLuaFile("cl_hud.lua")
  6. AddCSLuaFile("cl_msgstack.lua")
  7. AddCSLuaFile("cl_hudpickup.lua")
  8. AddCSLuaFile("cl_keys.lua")
  9. AddCSLuaFile("cl_wepswitch.lua")
  10. AddCSLuaFile("cl_awards.lua")
  11. AddCSLuaFile("cl_scoring_events.lua")
  12. AddCSLuaFile("cl_scoring.lua")
  13. AddCSLuaFile("cl_popups.lua")
  14. AddCSLuaFile("cl_equip.lua")
  15. AddCSLuaFile("equip_items_shd.lua")
  16. AddCSLuaFile("cl_help.lua")
  17. AddCSLuaFile("cl_scoreboard.lua")
  18. AddCSLuaFile("cl_tips.lua")
  19. AddCSLuaFile("cl_voice.lua")
  20. AddCSLuaFile("scoring_shd.lua")
  21. AddCSLuaFile("util.lua")
  22. AddCSLuaFile("lang_shd.lua")
  23. AddCSLuaFile("corpse_shd.lua")
  24. AddCSLuaFile("player_ext_shd.lua")
  25. AddCSLuaFile("weaponry_shd.lua")
  26. AddCSLuaFile("cl_radio.lua")
  27. AddCSLuaFile("cl_radar.lua")
  28. AddCSLuaFile("cl_tbuttons.lua")
  29. AddCSLuaFile("cl_disguise.lua")
  30. AddCSLuaFile("cl_transfer.lua")
  31. AddCSLuaFile("cl_search.lua")
  32. AddCSLuaFile("cl_targetid.lua")
  33. AddCSLuaFile("vgui/ColoredBox.lua")
  34. AddCSLuaFile("vgui/SimpleIcon.lua")
  35. AddCSLuaFile("vgui/ProgressBar.lua")
  36. AddCSLuaFile("vgui/ScrollLabel.lua")
  37. AddCSLuaFile("vgui/sb_main.lua")
  38. AddCSLuaFile("vgui/sb_row.lua")
  39. AddCSLuaFile("vgui/sb_team.lua")
  40. AddCSLuaFile("vgui/sb_info.lua")
  41.  
  42. include("resources.lua")
  43. include("shared.lua")
  44.  
  45. include("karma.lua")
  46. include("entity.lua")
  47. include("scoring_shd.lua")
  48. include("radar.lua")
  49. include("admin.lua")
  50. include("traitor_state.lua")
  51. include("propspec.lua")
  52. include("weaponry.lua")
  53. include("gamemsg.lua")
  54. include("ent_replace.lua")
  55. include("scoring.lua")
  56. include("corpse.lua")
  57. include("player_ext_shd.lua")
  58. include("player_ext.lua")
  59. include("player.lua")
  60. include("tags.lua")
  61.  
  62. CreateConVar("ttt_roundtime_minutes", "10", FCVAR_NOTIFY)
  63. CreateConVar("ttt_preptime_seconds", "30", FCVAR_NOTIFY)
  64. CreateConVar("ttt_posttime_seconds", "30", FCVAR_NOTIFY)
  65. CreateConVar("ttt_firstpreptime", "60")
  66.  
  67. local ttt_haste = CreateConVar("ttt_haste", "1", FCVAR_NOTIFY)
  68. CreateConVar("ttt_haste_starting_minutes", "5", FCVAR_NOTIFY)
  69. CreateConVar("ttt_haste_minutes_per_death", "0.5", FCVAR_NOTIFY)
  70.  
  71. CreateConVar("ttt_spawn_wave_interval", "0")
  72.  
  73. CreateConVar("ttt_traitor_pct", "0.25")
  74. CreateConVar("ttt_traitor_max", "32")
  75.  
  76. CreateConVar("ttt_detective_pct", "0.13", FCVAR_NOTIFY)
  77. CreateConVar("ttt_detective_max", "32")
  78. CreateConVar("ttt_detective_min_players", "8")
  79. CreateConVar("ttt_detective_karma_min", "600")
  80.  
  81.  
  82. -- Traitor credits
  83. CreateConVar("ttt_credits_starting", "2")
  84. CreateConVar("ttt_credits_award_pct", "0.35")
  85. CreateConVar("ttt_credits_award_size", "1")
  86. CreateConVar("ttt_credits_award_repeat", "1")
  87. CreateConVar("ttt_credits_detectivekill", "1")
  88.  
  89. CreateConVar("ttt_credits_alonebonus", "1")
  90.  
  91. -- Detective credits
  92. CreateConVar("ttt_det_credits_starting", "1")
  93. CreateConVar("ttt_det_credits_traitorkill", "0")
  94. CreateConVar("ttt_det_credits_traitordead", "1")
  95.  
  96.  
  97. CreateConVar("ttt_announce_deaths", "1", FCVAR_ARCHIVE + FCVAR_NOTIFY)
  98.  
  99. CreateConVar("ttt_use_weapon_spawn_scripts", "1")
  100.  
  101. CreateConVar("ttt_always_use_mapcycle", "0")
  102.  
  103. CreateConVar("ttt_round_limit", "6", FCVAR_ARCHIVE + FCVAR_NOTIFY + FCVAR_REPLICATED)
  104. CreateConVar("ttt_time_limit_minutes", "75", FCVAR_NOTIFY + FCVAR_REPLICATED)
  105.  
  106. CreateConVar("ttt_idle_limit", "180", FCVAR_NOTIFY)
  107.  
  108. CreateConVar("ttt_voice_drain", "0", FCVAR_NOTIFY)
  109. CreateConVar("ttt_voice_drain_normal", "0.2", FCVAR_NOTIFY)
  110. CreateConVar("ttt_voice_drain_admin", "0.05", FCVAR_NOTIFY)
  111. CreateConVar("ttt_voice_drain_recharge", "0.05", FCVAR_NOTIFY)
  112.  
  113. CreateConVar("ttt_namechange_kick", "1", FCVAR_NOTIFY)
  114. CreateConVar("ttt_namechange_bantime", "10")
  115.  
  116. local ttt_detective = CreateConVar("ttt_sherlock_mode", "1", FCVAR_ARCHIVE + FCVAR_NOTIFY)
  117. local ttt_minply = CreateConVar("ttt_minimum_players", "2", FCVAR_ARCHIVE + FCVAR_NOTIFY)
  118.  
  119. -- debuggery
  120. local ttt_dbgwin = CreateConVar("ttt_debug_preventwin", "0")
  121.  
  122. -- Localise stuff we use often. It's like Lua go-faster stripes.
  123. local math = math
  124. local table = table
  125. local umsg = umsg
  126. local player = player
  127. local timer = timer
  128.  
  129. ---- Round mechanics
  130. function GM:Initialize()
  131. MsgN("Trouble In Terrorist Town gamemode initializing...")
  132. ShowVersion()
  133.  
  134. -- Force friendly fire to be enabled. If it is off, we do not get lag compensation.
  135. RunConsoleCommand("mp_friendlyfire", "1")
  136.  
  137. -- Default crowbar unlocking settings, may be overridden by config entity
  138. GAMEMODE.crowbar_unlocks = {
  139. [OPEN_DOOR] = true,
  140. [OPEN_ROT] = true,
  141. [OPEN_BUT] = true,
  142. [OPEN_NOTOGGLE]= true
  143. };
  144.  
  145. -- More map config ent defaults
  146. GAMEMODE.force_plymodel = ""
  147. GAMEMODE.propspec_allow_named = true
  148.  
  149. GAMEMODE.MapWin = WIN_NONE
  150. GAMEMODE.AwardedCredits = false
  151. GAMEMODE.AwardedCreditsDead = 0
  152.  
  153. GAMEMODE.round_state = ROUND_WAIT
  154. GAMEMODE.FirstRound = true
  155. GAMEMODE.RoundStartTime = 0
  156.  
  157. GAMEMODE.DamageLog = {}
  158. GAMEMODE.LastRole = {}
  159. GAMEMODE.playermodel = GetRandomPlayerModel()
  160. GAMEMODE.playercolor = COLOR_WHITE
  161.  
  162. -- Delay reading of cvars until config has definitely loaded
  163. GAMEMODE.cvar_init = false
  164.  
  165. SetGlobalFloat("ttt_round_end", -1)
  166. SetGlobalFloat("ttt_haste_end", -1)
  167.  
  168. -- For the paranoid
  169. math.randomseed(os.time())
  170.  
  171. WaitForPlayers()
  172.  
  173. if cvars.Number("sv_alltalk", 0) > 0 then
  174. ErrorNoHalt("TTT WARNING: sv_alltalk is enabled. Dead players will be able to talk to living players. TTT will now attempt to set sv_alltalk 0.\n")
  175. RunConsoleCommand("sv_alltalk", "0")
  176. end
  177.  
  178. local cstrike = false
  179. for _, g in pairs(engine.GetGames()) do
  180. if g.folder == 'cstrike' then cstrike = true end
  181. end
  182. if not cstrike then
  183. ErrorNoHalt("TTT WARNING: CS:S does not appear to be mounted by GMod. Things may break in strange ways. Server admin? Check the TTT readme for help.\n")
  184. end
  185.  
  186. GAMEMODE:CheckFileConsistency()
  187. end
  188.  
  189. function GM:InitPostEntity()
  190. self.Customized = WEPS.HasCustomEquipment()
  191.  
  192. self:UpdateServerTags()
  193. end
  194.  
  195. -- Used to do this in Initialize, but server cfg has not always run yet by that
  196. -- point.
  197. function GM:InitCvars()
  198. MsgN("TTT initializing convar settings...")
  199.  
  200. -- Initialize game state that is synced with client
  201. SetGlobalInt("ttt_rounds_left", GetConVar("ttt_round_limit"):GetInt())
  202. GAMEMODE:SyncGlobals()
  203. KARMA.InitState()
  204.  
  205. self.cvar_init = true
  206. end
  207.  
  208. function GM:GetGameDescription() return self.Name end
  209.  
  210. -- Convar replication is broken in gmod, so we do this.
  211. -- I don't like it any more than you do, dear reader.
  212. function GM:SyncGlobals()
  213. SetGlobalBool("ttt_detective", ttt_detective:GetBool())
  214. SetGlobalBool("ttt_haste", ttt_haste:GetBool())
  215. SetGlobalInt("ttt_time_limit_minutes", GetConVar("ttt_time_limit_minutes"):GetInt())
  216. SetGlobalBool("ttt_highlight_admins", GetConVar("ttt_highlight_admins"):GetBool())
  217. SetGlobalBool("ttt_locational_voice", GetConVar("ttt_locational_voice"):GetBool())
  218. SetGlobalInt("ttt_idle_limit", GetConVar("ttt_idle_limit"):GetInt())
  219.  
  220. SetGlobalBool("ttt_voice_drain", GetConVar("ttt_voice_drain"):GetBool())
  221. SetGlobalFloat("ttt_voice_drain_normal", GetConVar("ttt_voice_drain_normal"):GetFloat())
  222. SetGlobalFloat("ttt_voice_drain_admin", GetConVar("ttt_voice_drain_admin"):GetFloat())
  223. SetGlobalFloat("ttt_voice_drain_recharge", GetConVar("ttt_voice_drain_recharge"):GetFloat())
  224. end
  225.  
  226. function SendRoundState(state, ply)
  227. if ply then
  228. umsg.Start("round_state", ply)
  229. else
  230. umsg.Start("round_state")
  231. end
  232. umsg.Char(state)
  233. umsg.End()
  234. end
  235.  
  236. -- Round state is encapsulated by set/get so that it can easily be changed to
  237. -- eg. a networked var if this proves more convenient
  238. function SetRoundState(state)
  239. GAMEMODE.round_state = state
  240.  
  241. SCORE:RoundStateChange(state)
  242.  
  243. SendRoundState(state)
  244. end
  245.  
  246. function GetRoundState()
  247. return GAMEMODE.round_state
  248. end
  249.  
  250. local function EnoughPlayers()
  251. local ready = 0
  252. -- only count truly available players, ie. no forced specs
  253. for _, ply in pairs(player.GetAll()) do
  254. if IsValid(ply) and ply:ShouldSpawn() then
  255. ready = ready + 1
  256. end
  257. end
  258. return ready >= ttt_minply:GetInt()
  259. end
  260.  
  261. -- Used to be in Think/Tick, now in a timer
  262. function WaitingForPlayersChecker()
  263. if GetRoundState() == ROUND_WAIT then
  264. if EnoughPlayers() then
  265. timer.Create("wait2prep", 1, 1, PrepareRound)
  266.  
  267. timer.Stop("waitingforply")
  268. end
  269. end
  270. end
  271.  
  272. -- Start waiting for players
  273. function WaitForPlayers()
  274. SetRoundState(ROUND_WAIT)
  275.  
  276. if not timer.Start("waitingforply") then
  277. timer.Create("waitingforply", 2, 0, WaitingForPlayersChecker)
  278. end
  279. end
  280.  
  281. -- When a player initially spawns after mapload, everything is a bit strange;
  282. -- just making him spectator for some reason does not work right. Therefore,
  283. -- we regularly check for these broken spectators while we wait for players
  284. -- and immediately fix them.
  285. function FixSpectators()
  286. for k, ply in pairs(player.GetAll()) do
  287. if ply:IsSpec() and not ply:GetRagdollSpec() and ply:GetMoveType() < MOVETYPE_NOCLIP then
  288. ply:Spectate(OBS_MODE_ROAMING)
  289. end
  290. end
  291. end
  292.  
  293. -- Used to be in think, now a timer
  294. local function WinChecker()
  295. if GetRoundState() == ROUND_ACTIVE then
  296. if CurTime() > GetGlobalFloat("ttt_round_end", 0) then
  297. EndRound(WIN_TIMELIMIT)
  298. else
  299. local win = CheckForWin()
  300. if win != WIN_NONE then
  301. EndRound(win)
  302. return
  303. end
  304. end
  305. end
  306. end
  307.  
  308. local function NameChangeKick()
  309. if not GetConVar("ttt_namechange_kick"):GetBool() then
  310. timer.Destroy("namecheck")
  311. return
  312. end
  313.  
  314. if GetRoundState() == ROUND_ACTIVE then
  315. for _, ply in pairs(player.GetHumans()) do
  316. if ply.spawn_nick then
  317. if ply.has_spawned and ply.spawn_nick != ply:Nick() then
  318. local t = GetConVar("ttt_namechange_bantime"):GetInt()
  319. local msg = "Changed name during a round"
  320. if t > 0 then
  321. ply:KickBan(t, msg)
  322. else
  323. ply:Kick(msg)
  324. end
  325. end
  326. else
  327. ply.spawn_nick = ply:Nick()
  328. end
  329. end
  330. end
  331. end
  332.  
  333. function StartNameChangeChecks()
  334. if not GetConVar("ttt_namechange_kick"):GetBool() then return end
  335.  
  336. -- bring nicks up to date, may have been changed during prep/post
  337. for _, ply in pairs(player.GetAll()) do
  338. ply.spawn_nick = ply:Nick()
  339. end
  340.  
  341. if not timer.Exists("namecheck") then
  342. timer.Create("namecheck", 3, 0, NameChangeKick)
  343. end
  344. end
  345.  
  346. function StartWinChecks()
  347. if not timer.Start("winchecker") then
  348. timer.Create("winchecker", 1, 0, WinChecker)
  349. end
  350. end
  351.  
  352. function StopWinChecks()
  353. timer.Stop("winchecker")
  354. end
  355.  
  356. local function CleanUp()
  357. local et = ents.TTT
  358. -- if we are going to import entities, it's no use replacing HL2DM ones as
  359. -- soon as they spawn, because they'll be removed anyway
  360. et.SetReplaceChecking(not et.CanImportEntities(game.GetMap()))
  361.  
  362. et.FixParentedPreCleanup()
  363.  
  364. game.CleanUpMap()
  365.  
  366. et.FixParentedPostCleanup()
  367.  
  368. -- Strip players now, so that their weapons are not seen by ReplaceEntities
  369. for k,v in pairs(player.GetAll()) do
  370. if IsValid(v) then
  371. v:StripWeapons()
  372. end
  373. end
  374.  
  375. -- a different kind of cleanup
  376. util.UnbridledHateForULX()
  377. end
  378.  
  379. local function SpawnEntities()
  380. local et = ents.TTT
  381. -- Spawn weapons from script if there is one
  382. local import = et.CanImportEntities(game.GetMap())
  383.  
  384. if import then
  385. et.ProcessImportScript(game.GetMap())
  386. else
  387. -- Replace HL2DM/ZM ammo/weps with our own
  388. et.ReplaceEntities()
  389.  
  390. -- Populate CS:S/TF2 maps with extra guns
  391. et.PlaceExtraWeapons()
  392. end
  393.  
  394. -- Finally, get players in there
  395. SpawnWillingPlayers()
  396. end
  397.  
  398.  
  399. local function StopRoundTimers()
  400. -- remove all timers
  401. timer.Stop("wait2prep")
  402. timer.Stop("prep2begin")
  403. timer.Stop("end2begin")
  404. timer.Stop("winchecker")
  405. end
  406.  
  407. -- Make sure we have the players to do a round, people can leave during our
  408. -- preparations so we'll call this numerous times
  409. local function CheckForAbort()
  410. if not EnoughPlayers() then
  411. LANG.Msg("round_minplayers")
  412. StopRoundTimers()
  413.  
  414. WaitForPlayers()
  415. return true
  416. end
  417.  
  418. return false
  419. end
  420.  
  421. function GM:TTTDelayRoundStartForVote()
  422. -- No voting system available in GM13 (yet)
  423. --return self:InGamemodeVote()
  424. return false
  425. end
  426.  
  427. function PrepareRound()
  428. GAMEMODE:UpdateServerTags()
  429.  
  430. -- Check playercount
  431. if CheckForAbort() then return end
  432.  
  433. if GetGlobalBool("InContinueVote", false) then
  434. GAMEMODE:FinishContinueVote() -- may start a gamemode vote
  435. end
  436.  
  437. if hook.Call("TTTDelayRoundStartForVote", GAMEMODE) then
  438. LANG.Msg("round_voting", {num = 30})
  439.  
  440. timer.Create("delayedprep", 30, 1, PrepareRound)
  441. return
  442. end
  443.  
  444. -- Cleanup
  445. CleanUp()
  446.  
  447. GAMEMODE.MapWin = WIN_NONE
  448. GAMEMODE.AwardedCredits = false
  449. GAMEMODE.AwardedCreditsDead = 0
  450.  
  451. SCORE:Reset()
  452.  
  453. -- Update damage scaling
  454. KARMA.RoundBegin()
  455.  
  456. -- New look. Random if no forced model set.
  457. GAMEMODE.playermodel = GAMEMODE.force_plymodel == "" and GetRandomPlayerModel() or GAMEMODE.force_plymodel
  458. GAMEMODE.playercolor = hook.Call("TTTPlayerColor", GAMEMODE, GAMEMODE.playermodel)
  459.  
  460. if CheckForAbort() then return end
  461.  
  462. -- Schedule round start
  463. local ptime = GetConVar("ttt_preptime_seconds"):GetInt()
  464. if GAMEMODE.FirstRound then
  465. ptime = GetConVar("ttt_firstpreptime"):GetInt()
  466. GAMEMODE.FirstRound = false
  467. end
  468.  
  469. -- Piggyback on "round end" time global var to show end of phase timer
  470. SetRoundEnd(CurTime() + ptime)
  471.  
  472. timer.Create("prep2begin", ptime, 1, BeginRound)
  473.  
  474. -- Mute for a second around traitor selection, to counter a dumb exploit
  475. -- related to traitor's mics cutting off for a second when they're selected.
  476. timer.Create("selectmute", ptime - 1, 1, function() MuteForRestart(true) end)
  477.  
  478. LANG.Msg("round_begintime", {num = ptime})
  479. SetRoundState(ROUND_PREP)
  480.  
  481. -- Delay spawning until next frame to avoid ent overload
  482. timer.Simple(0.01, SpawnEntities)
  483.  
  484. -- Undo the roundrestart mute, though they will once again be muted for the
  485. -- selectmute timer.
  486. timer.Create("restartmute", 1, 1, function() MuteForRestart(false) end)
  487.  
  488. SendUserMessage("clearclientstate")
  489.  
  490. -- In case client's cleanup fails, make client set all players to innocent role
  491. timer.Simple(1, SendRoleReset)
  492.  
  493. -- Tell hooks and map we started prep
  494. hook.Call("TTTPrepareRound")
  495.  
  496. ents.TTT.TriggerRoundStateOutputs(ROUND_PREP)
  497. end
  498.  
  499. function SetRoundEnd(endtime)
  500. SetGlobalFloat("ttt_round_end", endtime)
  501. end
  502.  
  503. function IncRoundEnd(incr)
  504. SetRoundEnd(GetGlobalFloat("ttt_round_end", 0) + incr)
  505. end
  506.  
  507. function TellTraitorsAboutTraitors()
  508. local traitornicks = {}
  509. for k,v in pairs(player.GetAll()) do
  510. if v:IsTraitor() then
  511. table.insert(traitornicks, v:Nick())
  512. end
  513. end
  514.  
  515. -- This is ugly as hell, but it's kinda nice to filter out the names of the
  516. -- traitors themselves in the messages to them
  517. for k,v in pairs(player.GetAll()) do
  518. if v:IsTraitor() then
  519. if #traitornicks < 2 then
  520. LANG.Msg(v, "round_traitors_one")
  521. return
  522. else
  523. local names = ""
  524. for i,name in pairs(traitornicks) do
  525. if name != v:Nick() then
  526. names = names .. name .. ", "
  527. end
  528. end
  529. names = string.sub(names, 1, -3)
  530. LANG.Msg(v, "round_traitors_more", {names = names})
  531. end
  532. end
  533. end
  534. end
  535.  
  536.  
  537. function SpawnWillingPlayers(dead_only)
  538. local plys = player.GetAll()
  539. local wave_delay = GetConVar("ttt_spawn_wave_interval"):GetFloat()
  540.  
  541. -- simple method, should make this a case of the other method once that has
  542. -- been tested.
  543. if wave_delay <= 0 or dead_only then
  544. for k, ply in pairs(player.GetAll()) do
  545. if IsValid(ply) then
  546. ply:SpawnForRound(dead_only)
  547. end
  548. end
  549. else
  550. -- wave method
  551. local num_spawns = #GetSpawnEnts()
  552.  
  553. local to_spawn = {}
  554. for _, ply in RandomPairs(plys) do
  555. if IsValid(ply) and ply:ShouldSpawn() then
  556. table.insert(to_spawn, ply)
  557. GAMEMODE:PlayerSpawnAsSpectator(ply)
  558. end
  559. end
  560.  
  561. local sfn = function()
  562. local c = 0
  563. -- fill the available spawnpoints with players that need
  564. -- spawning
  565. while c < num_spawns and #to_spawn > 0 do
  566. for k, ply in pairs(to_spawn) do
  567. if IsValid(ply) then
  568. if ply:SpawnForRound() then
  569. -- a spawn ent is now occupied
  570. c = c + 1
  571. end
  572. end
  573. -- Few possible cases:
  574. -- 1) player has now been spawned
  575. -- 2) player should remain spectator after all
  576. -- 3) player has disconnected
  577. -- In all cases we don't need to spawn them again.
  578. table.remove(to_spawn, k)
  579.  
  580. -- all spawn ents are occupied, so the rest will have
  581. -- to wait for next wave
  582. if c >= num_spawns then
  583. break
  584. end
  585. end
  586. end
  587.  
  588. MsgN("Spawned " .. c .. " players in spawn wave.")
  589.  
  590. if #to_spawn == 0 then
  591. timer.Destroy("spawnwave")
  592. MsgN("Spawn waves ending, all players spawned.")
  593. end
  594. end
  595.  
  596. MsgN("Spawn waves starting.")
  597. timer.Create("spawnwave", wave_delay, 0, sfn)
  598.  
  599. -- already run one wave, which may stop the timer if everyone is spawned
  600. -- in one go
  601. sfn()
  602. end
  603. end
  604.  
  605. local function InitRoundEndTime()
  606. -- Init round values
  607. local endtime = CurTime() + (GetConVar("ttt_roundtime_minutes"):GetInt() * 60)
  608. if HasteMode() then
  609. endtime = CurTime() + (GetConVar("ttt_haste_starting_minutes"):GetInt() * 60)
  610. -- this is a "fake" time shown to innocents, showing the end time if no
  611. -- one would have been killed, it has no gameplay effect
  612. SetGlobalFloat("ttt_haste_end", endtime)
  613. end
  614.  
  615. SetRoundEnd(endtime)
  616. end
  617.  
  618. function BeginRound()
  619. GAMEMODE:SyncGlobals()
  620.  
  621. if CheckForAbort() then return end
  622.  
  623. AnnounceVersion()
  624.  
  625. InitRoundEndTime()
  626.  
  627. if CheckForAbort() then return end
  628.  
  629. -- Respawn dumb people who died during prep
  630. SpawnWillingPlayers(true)
  631.  
  632. -- Remove their ragdolls
  633. ents.TTT.RemoveRagdolls(true)
  634.  
  635. WEPS.ForcePrecache()
  636.  
  637. if CheckForAbort() then return end
  638.  
  639. -- Select traitors & co. This is where things really start so we can't abort
  640. -- anymore.
  641. SelectRoles()
  642. LANG.Msg("round_selected")
  643. SendFullStateUpdate()
  644.  
  645. -- Edge case where a player joins just as the round starts and is picked as
  646. -- traitor, but for whatever reason does not get the traitor state msg. So
  647. -- re-send after a second just to make sure everyone is getting it.
  648. timer.Simple(1, SendFullStateUpdate)
  649.  
  650. SCORE:HandleSelection() -- log traitors and detectives
  651.  
  652. -- Give the StateUpdate messages ample time to arrive
  653. timer.Simple(1.5, TellTraitorsAboutTraitors)
  654. timer.Simple(2.5, ShowRoundStartPopup)
  655.  
  656. -- Start the win condition check timer
  657. StartWinChecks()
  658. StartNameChangeChecks()
  659. timer.Create("selectmute", 1, 1, function() MuteForRestart(false) end)
  660.  
  661. GAMEMODE.DamageLog = {}
  662. GAMEMODE.RoundStartTime = CurTime()
  663.  
  664. -- Sound start alarm
  665. SetRoundState(ROUND_ACTIVE)
  666. LANG.Msg("round_started")
  667. ServerLog("Round proper has begun...\n")
  668.  
  669. GAMEMODE:UpdatePlayerLoadouts() -- needs to happen when round_active
  670.  
  671. hook.Call("TTTBeginRound")
  672.  
  673. ents.TTT.TriggerRoundStateOutputs(ROUND_BEGIN)
  674. end
  675.  
  676. function PrintResultMessage(type)
  677. ServerLog("Round ended.\n")
  678. if type == WIN_TIMELIMIT then
  679. LANG.Msg("win_time")
  680. ServerLog("Result: timelimit reached, traitors lose.\n")
  681. elseif type == WIN_TRAITOR then
  682. LANG.Msg("win_traitor")
  683. ServerLog("Result: traitors win.\n")
  684. elseif type == WIN_INNOCENT then
  685. LANG.Msg("win_innocent")
  686. ServerLog("Result: innocent win.\n")
  687. else
  688. ServerLog("Result: unknown victory condition!\n")
  689. end
  690. end
  691.  
  692. local function ShouldMapSwitch()
  693. return true -- no voting until fretta replacement arrives
  694. -- return GetConVar("ttt_always_use_mapcycle"):GetBool()
  695. end
  696.  
  697. function CheckForMapSwitch()
  698. -- Check for mapswitch
  699. local rounds_left = math.max(0, GetGlobalInt("ttt_rounds_left", 6) - 1)
  700. SetGlobalInt("ttt_rounds_left", rounds_left)
  701.  
  702. local time_left = math.max(0, (GetConVar("ttt_time_limit_minutes"):GetInt() * 60) - CurTime())
  703. local switchmap = false
  704. local nextmap = string.upper(game.GetMapNext())
  705.  
  706. if ShouldMapSwitch() then
  707. if rounds_left <= 0 then
  708. LANG.Msg("limit_round", {mapname = nextmap})
  709. switchmap = true
  710. elseif time_left <= 0 then
  711. LANG.Msg("limit_time", {mapname = nextmap})
  712. switchmap = true
  713. end
  714. else
  715. -- we only get here if fretta_voting is on and always_use_mapcycle off
  716. if rounds_left <= 0 or time_left <= 0 then
  717. LANG.Msg("limit_vote")
  718.  
  719. -- pending fretta replacement...
  720. switchmap = true
  721. --GAMEMODE:StartFrettaVote()
  722. end
  723. end
  724.  
  725. if switchmap then
  726. timer.Stop("end2prep")
  727. MapVote.Start(15, false, 24, "ttt_")
  728. elseif ShouldMapSwitch() then
  729. LANG.Msg("limit_left", {num = rounds_left,
  730. time = math.ceil(time_left / 60),
  731. mapname = "chosen by a vote"})
  732. end
  733. end
  734.  
  735. function EndRound(type)
  736. PrintResultMessage(type)
  737.  
  738. -- first handle round end
  739. SetRoundState(ROUND_POST)
  740.  
  741. local ptime = math.max(5, GetConVar("ttt_posttime_seconds"):GetInt())
  742. LANG.Msg("win_showreport", {num = ptime})
  743. timer.Create("end2prep", ptime, 1, PrepareRound)
  744.  
  745. -- Piggyback on "round end" time global var to show end of phase timer
  746. SetRoundEnd(CurTime() + ptime)
  747.  
  748. timer.Create("restartmute", ptime - 1, 1, function() MuteForRestart(true) end)
  749.  
  750. -- Stop checking for wins
  751. StopWinChecks()
  752.  
  753. -- We may need to start a timer for a mapswitch, or start a vote
  754. CheckForMapSwitch()
  755.  
  756. -- Show unobtrusive vote window (only if fretta voting enabled and only if
  757. -- not already in a round/time limit induced vote)
  758. --if not GAMEMODE:InGamemodeVote() then
  759. -- GAMEMODE:StartContinueVote()
  760. --end
  761.  
  762. KARMA.RoundEnd()
  763.  
  764. -- now handle potentially error prone scoring stuff
  765.  
  766. -- register an end of round event
  767. SCORE:RoundComplete(type)
  768.  
  769. -- update player scores
  770. SCORE:ApplyEventLogScores(type)
  771.  
  772. -- send the clients the round log, players will be shown the report
  773. SCORE:StreamToClients()
  774.  
  775. -- server plugins might want to start a map vote here or something
  776. -- these hooks are not used by TTT internally
  777. hook.Call("TTTEndRound", GAMEMODE, type)
  778.  
  779. ents.TTT.TriggerRoundStateOutputs(ROUND_POST, type)
  780. end
  781.  
  782. function GM:MapTriggeredEnd(wintype)
  783. self.MapWin = wintype
  784. end
  785.  
  786. -- The most basic win check is whether both sides have one dude alive
  787. function CheckForWin()
  788. if ttt_dbgwin:GetBool() then return WIN_NONE end
  789.  
  790. if GAMEMODE.MapWin == WIN_TRAITOR or GAMEMODE.MapWin == WIN_INNOCENT then
  791. local mw = GAMEMODE.MapWin
  792. GAMEMODE.MapWin = WIN_NONE
  793. return mw
  794. end
  795.  
  796. local traitor_alive = false
  797. local innocent_alive = false
  798. for k,v in pairs(player.GetAll()) do
  799. if v:Alive() and v:IsTerror() then
  800. if v:GetTraitor() then
  801. traitor_alive = true
  802. else
  803. innocent_alive = true
  804. end
  805. end
  806.  
  807. if traitor_alive and innocent_alive then
  808. return WIN_NONE --early out
  809. end
  810. end
  811.  
  812. if traitor_alive and not innocent_alive then
  813. return WIN_TRAITOR
  814. elseif not traitor_alive and innocent_alive then
  815. return WIN_INNOCENT
  816. elseif not innocent_alive then
  817. -- ultimately if no one is alive, traitors win
  818. return WIN_TRAITOR
  819. end
  820.  
  821. return WIN_NONE
  822. end
  823.  
  824. local function GetTraitorCount(ply_count)
  825. -- get number of traitors: pct of players rounded down
  826. local traitor_count = math.floor(ply_count * GetConVar("ttt_traitor_pct"):GetFloat())
  827. -- make sure there is at least 1 traitor
  828. traitor_count = math.Clamp(traitor_count, 1, GetConVar("ttt_traitor_max"):GetInt())
  829.  
  830. return traitor_count
  831. end
  832.  
  833.  
  834. local function GetDetectiveCount(ply_count)
  835. if ply_count < GetConVar("ttt_detective_min_players"):GetInt() then return 0 end
  836.  
  837. local det_count = math.floor(ply_count * GetConVar("ttt_detective_pct"):GetFloat())
  838. -- limit to a max
  839. det_count = math.Clamp(det_count, 1, GetConVar("ttt_detective_max"):GetInt())
  840.  
  841. return det_count
  842. end
  843.  
  844.  
  845. function SelectRoles()
  846. local choices = {}
  847. local prev_roles = {
  848. [ROLE_INNOCENT] = {},
  849. [ROLE_TRAITOR] = {},
  850. [ROLE_DETECTIVE] = {}
  851. };
  852.  
  853. if not GAMEMODE.LastRole then GAMEMODE.LastRole = {} end
  854.  
  855. for k,v in pairs(player.GetAll()) do
  856. -- everyone on the spec team is in specmode
  857. if IsValid(v) and (not v:IsSpec()) then
  858. -- save previous role and sign up as possible traitor/detective
  859.  
  860. local r = GAMEMODE.LastRole[v:UniqueID()] or v:GetRole() or ROLE_INNOCENT
  861.  
  862. table.insert(prev_roles[r], v)
  863.  
  864. table.insert(choices, v)
  865. end
  866.  
  867. v:SetRole(ROLE_INNOCENT)
  868. end
  869.  
  870. -- determine how many of each role we want
  871. local choice_count = #choices
  872. local traitor_count = GetTraitorCount(choice_count)
  873. local det_count = GetDetectiveCount(choice_count)
  874.  
  875. if choice_count == 0 then return end
  876.  
  877. -- first select traitors
  878. local ts = 0
  879. while ts < traitor_count do
  880. -- select random index in choices table
  881. local pick = math.random(1, #choices)
  882.  
  883. -- the player we consider
  884. local pply = choices[pick]
  885.  
  886. -- make this guy traitor if he was not a traitor last time, or if he makes
  887. -- a roll
  888. if IsValid(pply) and
  889. ((not table.HasValue(prev_roles[ROLE_TRAITOR], pply)) or (math.random(1, 3) == 2)) then
  890. pply:SetRole(ROLE_TRAITOR)
  891.  
  892. table.remove(choices, pick)
  893. ts = ts + 1
  894. end
  895. end
  896.  
  897. -- now select detectives, explicitly choosing from players who did not get
  898. -- traitor, so becoming detective does not mean you lost a chance to be
  899. -- traitor
  900. local ds = 0
  901. local min_karma = GetConVarNumber("ttt_detective_min_karma") or 0
  902. while (ds < det_count) and (#choices >= 1) do
  903.  
  904. -- sometimes we need all remaining choices to be detective to fill the
  905. -- roles up, this happens more often with a lot of detective-deniers
  906. if #choices <= (det_count - ds) then
  907. for k, pply in pairs(choices) do
  908. if IsValid(pply) then
  909. pply:SetRole(ROLE_DETECTIVE)
  910. end
  911. end
  912.  
  913. break -- out of while
  914. end
  915.  
  916.  
  917. local pick = math.random(1, #choices)
  918. local pply = choices[pick]
  919.  
  920. -- we are less likely to be a detective unless we were innocent last round
  921. if (IsValid(pply) and
  922. ((pply:GetBaseKarma() > min_karma and
  923. table.HasValue(prev_roles[ROLE_INNOCENT], pply)) or
  924. math.random(1,3) == 2)) then
  925.  
  926. -- if a player has specified he does not want to be detective, we skip
  927. -- him here (he might still get it if we don't have enough
  928. -- alternatives)
  929. if not pply:GetAvoidDetective() then
  930. pply:SetRole(ROLE_DETECTIVE)
  931. ds = ds + 1
  932. end
  933.  
  934. table.remove(choices, pick)
  935. end
  936. end
  937.  
  938. GAMEMODE.LastRole = {}
  939.  
  940. for _, ply in pairs(player.GetAll()) do
  941. -- initialize credit count for everyone based on their role
  942. ply:SetDefaultCredits()
  943.  
  944. -- store a uid -> role map
  945. GAMEMODE.LastRole[ply:UniqueID()] = ply:GetRole()
  946. end
  947. end
  948.  
  949.  
  950. local function ForceRoundRestart(ply, command, args)
  951. -- ply is nil on dedicated server console
  952. if (not IsValid(ply)) or ply:IsAdmin() or ply:IsSuperAdmin() or cvars.Bool("sv_cheats", 0) then
  953. LANG.Msg("round_restart")
  954.  
  955. StopRoundTimers()
  956.  
  957. -- do prep
  958. PrepareRound()
  959. else
  960. ply:PrintMessage(HUD_PRINTCONSOLE, "You must be a GMod Admin or SuperAdmin on the server to use this command, or sv_cheats must be enabled.")
  961. end
  962. end
  963. concommand.Add("ttt_roundrestart", ForceRoundRestart)
  964.  
  965. -- Version announce also used in Initialize
  966. function ShowVersion(ply)
  967. local text = Format("This is TTT version %s\n", GAMEMODE.Version)
  968. if IsValid(ply) then
  969. ply:PrintMessage(HUD_PRINTNOTIFY, text)
  970. else
  971. Msg(text)
  972. end
  973. end
  974. concommand.Add("ttt_version", ShowVersion)
  975.  
  976. function AnnounceVersion()
  977. local text = Format("You are playing %s, version %s.\n", GAMEMODE.Name, GAMEMODE.Version)
  978.  
  979. -- announce to players
  980. for k, ply in pairs(player.GetAll()) do
  981. if IsValid(ply) then
  982. ply:PrintMessage(HUD_PRINTTALK, text)
  983. end
  984. end
  985. end
Advertisement
Advertisement
Advertisement
RAW Paste Data Copied
Advertisement