2026-01-04 18:21:33 -06:00
-- ####################################################################################
-- ## ##
-- ## ##
-- ## CASUAL BANANAS CONFIDENTIAL ##
-- ## ##
-- ## __________________________ ##
-- ## ##
-- ## ##
-- ## Copyright 2014 (c) Casual Bananas ##
-- ## All Rights Reserved. ##
-- ## ##
-- ## NOTICE: All information contained herein is, and remains ##
-- ## the property of Casual Bananas. The intellectual and technical ##
-- ## concepts contained herein are proprietary to Casual Bananas and may be ##
-- ## covered by U.S. and Foreign Patents, patents in process, and are ##
-- ## protected by trade secret or copyright law. ##
-- ## Dissemination of this information or reproduction of this material ##
-- ## is strictly forbidden unless prior written permission is obtained ##
-- ## from Casual Bananas ##
-- ## ##
-- ## _________________________ ##
-- ## ##
-- ## ##
-- ## Casual Bananas is registered with the "Kamer van Koophandel" (Dutch ##
-- ## chamber of commerce) in The Netherlands. ##
-- ## ##
-- ## Company (KVK) number : 59449837 ##
-- ## Email : info@casualbananas.com ##
-- ## ##
-- ## ##
-- ####################################################################################
--------------------------------------------------------------------------------------
--
--
-- REGARDING CUSTOM MAPVOTE SYSTEMS:
--
-- _________________________
--
--
-- If you want to code your own mapvote, hook the JailBreakStartMapvote hook,
-- start your own mapvote here. Remember to return true in order to stop the
-- round system for a while, while you run your mapvote.
--
-- _________________________
--
--
-- You might want to use the following functions as well if you're writing a
-- custom mapvote:
--
-- JB:Mapvote_ExtendCurrentMap()
-- JB:Mapvote_StartMapVote()
--
--
--------------------------------------------------------------------------------------
/*
Compatability hooks - implement these in your admin mods
*/
function JB . Gamemode . JailBreakStartMapvote ( rounds_passed , extentions_passed ) // hook.Add ( " JailBreakStartMapvote " , ... ) to implement your own mapvote . NOTE : Remember to return true !
return false // return true in your own mapvote function , else there won ' t be a pause between rounds!
end
/*
State chaining
*/
local chainState ;
if SERVER then
local stateTime = 0 ;
local stateCallback ;
hook.Add ( " Think " , " JB.Think.StateLogic " , function ( )
if stateTime > 0 and stateTime < CurTime ( ) then
JB : DebugPrint ( " State chain ended " )
stateTime = 0
stateCallback ( )
end
end )
chainState = function ( state , time , callback )
JB : DebugPrint ( " State chained: " .. tostring ( state ) .. " [ " .. tostring ( time ) .. " s][ " .. tostring ( callback ) .. " ] " )
JB.State = state ;
stateTime = CurTime ( ) + time ;
stateCallback = callback ;
end
concommand.Add ( " testtime " , function ( )
print ( stateTime , CurTime ( ) , stateTime < CurTime ( ) )
end )
end
/*
Utility functions
*/
local ententionsDone = 0 ;
function JB : Mapvote_ExtendCurrentMap ( ) // You can call this from your own admin mod / mapvote if you want to extend the current map .
JB.RoundsPassed = 0 ;
ententionsDone = ententionsDone + 1 ;
chainState ( STATE_ENDED , 5 , function ( )
JB : NewRound ( ) ;
end ) ;
end
function JB : Mapvote_StartMapVote ( ) // You can call this from your admin mod / mapvote to initiate a mapvote .
if hook.Call ( " JailBreakStartMapvote " , JB.Gamemode , JB.RoundsPassed , ententionsDone ) then
JB.State = STATE_MAPVOTE ;
return true ;
end
return false ;
end
/*
Enums
*/
STATE_IDLE = 1 ; -- when the map loads, we wait for everyone to join
STATE_SETUP = 2 ; -- first few seconds of the round, when everyone can still spawn and damage is disabled
STATE_PLAYING = 3 ; -- normal playing
STATE_LASTREQUEST = 4 ; -- last request taking place, special rules apply
STATE_ENDED = 5 ; -- round ended, waiting for next round to start
STATE_MAPVOTE = 6 ; -- voting for a map, will result in either a new map loading or restarting the current without reloading
/*
Network strings
*/
if SERVER then
util.AddNetworkString ( " JB.LR.GetReady " ) ;
util.AddNetworkString ( " JB.SendRoundUpdate " ) ;
end
/*
Special days
*/
local function resetSpecial ( )
if SERVER then
game.ConsoleCommand ( " sv_gravity 600; \n " )
game.ConsoleCommand ( " sv_friction 8; \n " )
elseif CLIENT then
end
end
if SERVER then
JB.SpecialDays = {
[ " Low-Gravity Knife Party " ] = function ( )
game.ConsoleCommand ( " sv_gravity 200; \n " )
game.ConsoleCommand ( " sv_friction 3; \n " )
for k , v in ipairs ( team.GetPlayers ( TEAM_PRISONER ) ) do
v : SetJumpPower ( 400 )
v : StripWeapons ( )
v : Give ( " weapon_jb_knife " )
end
for k , v in ipairs ( team.GetPlayers ( TEAM_GUARD ) ) do
v : SetJumpPower ( 400 )
v : StripWeapons ( )
v : Give ( " weapon_jb_knife " )
end
for k , v in ipairs ( ents.GetAll ( ) ) do
if IsValid ( v ) and v.GetClass and string.Left ( v : GetClass ( ) , string.len ( " weapon_jb_ " ) ) == " weapon_jb_ " and v : GetClass ( ) ~= " weapon_jb_knife " then
v : Remove ( )
end
end
for k , v in ipairs ( ents.FindByClass ( " func_door " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_door_rotating " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_movelinear " ) ) do
v : Fire ( " Open " , 1 )
end
end ,
[ " Guns for everyone " ] = function ( )
for k , v in ipairs ( team.GetPlayers ( TEAM_PRISONER ) ) do
v : Give ( " weapon_jb_deagle " )
v : SelectWeapon ( " weapon_jb_deagle " )
end
for k , v in ipairs ( ents.FindByClass ( " func_door " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_door_rotating " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_movelinear " ) ) do
v : Fire ( " Open " , 1 )
end
end ,
[ " Super heros " ] = function ( )
for k , v in ipairs ( team.GetPlayers ( TEAM_PRISONER ) ) do
v : SetRunSpeed ( 600 )
v : SetWalkSpeed ( 270 )
v : SetJumpPower ( 400 )
end
for k , v in ipairs ( ents.FindByClass ( " func_door " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_door_rotating " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_movelinear " ) ) do
v : Fire ( " Open " , 1 )
end
end ,
[ " Slow guards " ] = function ( )
for k , v in ipairs ( team.GetPlayers ( TEAM_GUARD ) ) do
v : SetRunSpeed ( 100 )
v : SetWalkSpeed ( 100 )
end
for k , v in ipairs ( ents.FindByClass ( " func_door " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_door_rotating " ) ) do
v : Fire ( " Open " , 1 )
end
for k , v in ipairs ( ents.FindByClass ( " func_movelinear " ) ) do
v : Fire ( " Open " , 1 )
end
end
}
end
/*
Round System
*/
JB.ThisRound = { } ;
local wantStartup = false ;
function JB : NewRound ( rounds_passed )
rounds_passed = rounds_passed or JB.RoundsPassed ;
JB.ThisRound = { } ;
if SERVER then
game.CleanUpMap ( ) ;
rounds_passed = rounds_passed + 1 ;
JB.RoundsPassed = rounds_passed ;
JB.RoundStartTime = CurTime ( ) ;
chainState ( STATE_SETUP , tonumber ( JB.Config . setupTime ) , function ( )
JB : DebugPrint ( " Setup finished, round started. " )
chainState ( STATE_PLAYING , ( 600 ) - tonumber ( JB.Config . setupTime ) , function ( )
JB : EndRound ( ) ;
end ) ;
if not IsValid ( JB : GetWarden ( ) ) then
JB : DebugPrint ( " No warden after setup time; Freeday! " )
JB : BroadcastNotification ( " Today is a freeday " ) ;
end
end ) ;
if JB.RoundsPassed == 1 then
local count = table.Count ( JB.SpecialDays )
local which = math.random ( 1 , count )
count = 0 ;
for k , v in pairs ( JB.SpecialDays ) do
count = count + 1
if count == which then
which = k ;
break ;
end
end
if JB.SpecialDays [ which ] then
JB : BroadcastNotification ( " First round: " .. which )
JB.SpecialDays [ which ] ( ) ;
JB.ThisRound . IsSpecialRound = true ;
end
end
if IsValid ( JB.TRANSMITTER ) then
JB.TRANSMITTER : SetJBWarden_PVPDamage ( false ) ;
JB.TRANSMITTER : SetJBWarden_ItemPickup ( false ) ;
JB.TRANSMITTER : SetJBWarden_PointerType ( " 0 " ) ;
JB.TRANSMITTER : SetJBWarden ( NULL ) ;
end
JB : BalanceTeams ( )
JB.Util . iterate ( player.GetAll ( ) ) : SetRebel ( false ) : Spawn ( ) ;
timer.Simple ( 1 , function ( )
JB.Util . iterate ( player.GetAll ( ) ) : Freeze ( false ) ;
end )
net.Start ( " JB.SendRoundUpdate " ) ; net.WriteInt ( STATE_SETUP , 8 ) ; net.WriteInt ( rounds_passed , 32 ) ; net.Broadcast ( ) ;
elseif CLIENT and IsValid ( LocalPlayer ( ) ) then
notification.AddLegacy ( " Round " .. rounds_passed , NOTIFY_GENERIC ) ;
LocalPlayer ( ) : ConCommand ( " -voicerecord " ) ;
end
hook.Call ( " JailBreakRoundStart " , JB.Gamemode , JB.RoundsPassed ) ;
end
function JB : EndRound ( winner )
if JB.ThisRound . IsSpecialRound then
resetSpecial ( )
end
if SERVER then
if JB.RoundsPassed >= tonumber ( JB.Config . roundsPerMap ) and JB : Mapvote_StartMapVote ( ) then
return ; // Halt the round system ; we ' re running a custom mapvote!
end
chainState ( STATE_ENDED , 5 , function ( )
JB.Util . iterate ( player.GetAll ( ) ) : Freeze ( true ) ;
JB : NewRound ( ) ;
end ) ;
net.Start ( " JB.GetLogs " ) ;
net.WriteTable ( JB.ThisRound and JB.ThisRound . Logs or { } ) ;
2026-01-04 18:26:23 -06:00
net.WriteBool ( true ) ;
2026-01-04 18:21:33 -06:00
net.Broadcast ( p ) ;
net.Start ( " JB.SendRoundUpdate " ) ; net.WriteInt ( STATE_ENDED , 8 ) ; net.WriteInt ( winner or 0 , 8 ) ; net.Broadcast ( ) ;
elseif CLIENT then
notification.AddLegacy ( winner == TEAM_PRISONER and " Prisoners win " or winner == TEAM_GUARD and " Guards win " or " Draw " , NOTIFY_GENERIC ) ;
end
hook.Call ( " JailBreakRoundEnd " , JB.Gamemode , JB.RoundsPassed ) ;
end
if CLIENT then
net.Receive ( " JB.SendRoundUpdate " , function ( )
local state = net.ReadInt ( 8 ) ;
if state == STATE_ENDED then
JB : EndRound ( net.ReadInt ( 8 ) ) ;
elseif state == STATE_SETUP then
JB : NewRound ( net.ReadInt ( 32 ) ) ;
end
end ) ;
elseif SERVER then
timer.Create ( " JBRoundEndLogic " , 1 , 0 , function ( )
if JB.State == STATE_IDLE and wantStartup then
if # team.GetPlayers ( TEAM_GUARD ) >= 1 and # team.GetPlayers ( TEAM_PRISONER ) >= 1 then
JB : DebugPrint ( " State is currently idle, but people have joined; Starting round 1. " )
JB : NewRound ( ) ;
end
end
if ( JB.State ~= STATE_PLAYING and JB.State ~= STATE_SETUP and JB.State ~= STATE_LASTREQUEST ) or # team.GetPlayers ( TEAM_GUARD ) < 1 or # team.GetPlayers ( TEAM_PRISONER ) < 1 then return end
local count_guard = JB : AliveGuards ( ) ;
local count_prisoner = JB : AlivePrisoners ( ) ;
if count_prisoner < 1 and count_guard < 1 then
JB : EndRound ( 0 ) ; -- both win!
elseif count_prisoner < 1 then
JB : EndRound ( TEAM_GUARD ) ;
elseif count_guard < 1 then
JB : EndRound ( TEAM_PRISONER ) ;
end
end ) ;
end
/*
Transmission Entity
*/
JB.TRANSMITTER = JB.TRANSMITTER or NULL ;
hook.Add ( " InitPostEntity " , " JB.InitPostEntity.SpawnStateTransmit " , function ( )
if SERVER and not IsValid ( JB.TRANSMITTER ) then
JB.TRANSMITTER = ents.Create ( " jb_transmitter_state " ) ;
JB.TRANSMITTER : Spawn ( ) ;
JB.TRANSMITTER : Activate ( ) ;
chainState ( STATE_IDLE , tonumber ( JB.Config . joinTime ) , function ( )
wantStartup = true ; -- request a startup.
end ) ;
elseif CLIENT then
timer.Simple ( 0 , function ( )
notification.AddLegacy ( " Welcome to Jail Break 7 " , NOTIFY_GENERIC ) ;
if JB.State == STATE_IDLE then
notification.AddLegacy ( " The round will start once everyone had a chance to join " , NOTIFY_GENERIC ) ;
elseif JB.State == STATE_PLAYING or JB.State == STATE_LASTREQUEST then
notification.AddLegacy ( " A round is currently in progress " , NOTIFY_GENERIC ) ;
notification.AddLegacy ( " You will spawn when the current ends " , NOTIFY_GENERIC ) ;
elseif JB.State == STATE_MAPVOTE then
notification.AddLegacy ( " A mapvote is currently in progress " , NOTIFY_GENERIC ) ;
end
end ) ;
end
end ) ;
if CLIENT then
hook.Add ( " OnEntityCreated " , " JB.OnEntityCreated.SelectTransmitter " , function ( ent )
if ent : GetClass ( ) == " jb_transmitter_state " and not IsValid ( JB.TRANSMITTER ) then
JB.TRANSMITTER = ent ;
JB : DebugPrint ( " Transmitter found (OnEntityCreated) " ) ;
end
end )
timer.Create ( " JB.CheckOnStateTransmitter " , 10 , 0 , function ( )
if not IsValid ( JB.TRANSMITTER ) then
JB : DebugPrint ( " Panic! State Transmitter not found! " ) ;
local trans = ents.FindByClass ( " jb_transmitter_state " ) ;
if trans and trans [ 1 ] and IsValid ( trans [ 1 ] ) then
JB.TRANSMITTER = trans [ 1 ] ;
JB : DebugPrint ( " Automatically resolved; Transmitter relocated. " ) ;
else
JB : DebugPrint ( " Failed to locate transmitter - contact a developer! " ) ;
end
end
end ) ;
end
/*
Index Callback methods
*/
// State
JB._IndexCallback . State = {
get = function ( )
return IsValid ( JB.TRANSMITTER ) and JB.TRANSMITTER . GetJBState and JB.TRANSMITTER : GetJBState ( ) or STATE_IDLE ;
end ,
set = function ( state )
if SERVER and IsValid ( JB.TRANSMITTER ) then
JB.TRANSMITTER : SetJBState ( state or STATE_IDLE ) ;
JB : DebugPrint ( " State changed to: " .. state )
else
Error ( " Can not set state! " )
end
end
}
// Round - related methods .
JB._IndexCallback . RoundsPassed = {
get = function ( )
return IsValid ( JB.TRANSMITTER ) and JB.TRANSMITTER . GetJBRoundsPassed and JB.TRANSMITTER : GetJBRoundsPassed ( ) or 0 ;
end ,
set = function ( amount )
if SERVER and IsValid ( JB.TRANSMITTER ) then
JB.TRANSMITTER : SetJBRoundsPassed ( amount > 0 and amount or 0 ) ;
else
Error ( " Can not set rounds passed! " ) ;
end
end
}
JB._IndexCallback . RoundStartTime = {
get = function ( )
return IsValid ( JB.TRANSMITTER ) and JB.TRANSMITTER . GetJBRoundStartTime and JB.TRANSMITTER : GetJBRoundStartTime ( ) or 0 ;
end ,
set = function ( amount )
if SERVER and IsValid ( JB.TRANSMITTER ) then
JB.TRANSMITTER : SetJBRoundStartTime ( amount > 0 and amount or 0 ) ;
else
Error ( " Can not set round start time! " ) ;
end
end
}
// Last Request - related methods .
JB._IndexCallback . LastRequest = {
get = function ( )
return ( JB.State == STATE_LASTREQUEST ) and JB.TRANSMITTER : GetJBLastRequestPicked ( ) or " 0 " ;
end ,
set = function ( tab )
if not IsValid ( JB.TRANSMITTER ) or not SERVER then return end
if not tab or type ( tab ) ~= " table " or not tab.type or not JB.ValidLR ( JB.LastRequestTypes [ tab.type ] ) or not IsValid ( tab.prisoner ) or not IsValid ( tab.guard ) then
JB.TRANSMITTER : SetJBLastRequestPicked ( " 0 " ) ;
if not pcall ( function ( ) JB : DebugPrint ( " Attempted to select invalid LR: " , tab.type , " " , tab.prisoner , " " , tab.guard , " " , type ( tab ) ) ; end ) then JB : DebugPrint ( " Unexptected LR sequence abortion! " ) ; end
return
end
JB.TRANSMITTER : SetJBLastRequestPrisoner ( tab.prisoner ) ;
JB.TRANSMITTER : SetJBLastRequestGuard ( tab.guard ) ;
JB.TRANSMITTER : SetJBLastRequestPicked ( tab.type ) ;
chainState ( STATE_LASTREQUEST , 180 , function ( ) JB : EndRound ( ) end )
JB.RoundStartTime = CurTime ( ) ;
JB : BroadcastNotification ( tab.prisoner : Nick ( ) .. " requested a " .. JB.LastRequestTypes [ tab.type ] : GetName ( ) , { tab.prisoner , tab.guard } )
JB : DebugPrint ( " LR Initiated! " , tab.prisoner , " vs " , tab.guard ) ;
local players = { tab.guard , tab.prisoner } ;
JB.Util . iterate ( players ) : Freeze ( true ) : StripWeapons ( ) : GodEnable ( ) : SetHealth ( 100 ) : SetArmor ( 0 ) ;
if not JB.LastRequestTypes [ tab.type ] . setupCallback ( tab.prisoner , tab.guard ) then
net.Start ( " JB.LR.GetReady " ) ;
net.WriteString ( tab.type ) ;
net.Send ( players ) ;
timer.Simple ( 7 , function ( )
if not JB.Util . isValid ( tab.prisoner , tab.guard ) then return end
JB.Util . iterate ( players ) : Freeze ( false ) : GodDisable ( ) ;
timer.Simple ( .5 , function ( )
if not JB.Util . isValid ( tab.prisoner , tab.guard ) then return end
JB.LastRequestTypes [ tab.type ] . startCallback ( tab.prisoner , tab.guard ) ;
end ) ;
end )
end
end
}
JB._IndexCallback . LastRequestPlayers = {
get = function ( )
return JB.State == STATE_LASTREQUEST and { JB.TRANSMITTER : GetJBLastRequestGuard ( ) or NULL , JB.TRANSMITTER : GetJBLastRequestPrisoner ( ) or NULL } or { NULL , NULL } ;
end ,
set = function ( )
Error ( " Tried to set LR players through invalid methods! " ) ;
end
}
/*
Prevent Cleanup
*/
local old_cleanup = game.CleanUpMap ;
function game . CleanUpMap ( send , tab )
if not tab then tab = { } end
table.insert ( tab , " jb_transmitter_state " ) ;
old_cleanup ( send , tab ) ;
end