1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
// TODO: could probably add some checks for whether player setting stuff is player 0 to check for host, might fail in dedicated tho
global function PrivateLobby_Init
struct {
int startState
string map = "mp_forwardbase_kodai"
string mode = "aitdm"
} file
void function PrivateLobby_Init()
{
print( "PrivateLobby_Init()" )
file.map = GetConVarString( "ns_private_match_last_map" )
file.mode = GetConVarString( "ns_private_match_last_mode" )
thread SetupPrivateMatchUIVarsWhenReady()
AddClientCommandCallback( "PrivateMatchLaunch", ClientCommandCallback_PrivateMatchLaunch )
AddClientCommandCallback( "PrivateMatchSetMode", ClientCommandCallback_PrivateMatchSetMode )
AddClientCommandCallback( "SetCustomMap", ClientCommandCallback_SetCustomMap )
AddClientCommandCallback( "PrivateMatchSwitchTeams", ClientCommandCallback_PrivateMatchSwitchTeams )
AddClientCommandCallback( "PrivateMatchToggleSpectate", ClientCommandCallback_PrivateMatchToggleSpectate )
AddClientCommandCallback( "PrivateMatchSetPlaylistVarOverride", ClientCommandCallback_PrivateMatchSetPlaylistVarOverride )
AddClientCommandCallback( "ResetMatchSettingsToDefault", ClientCommandCallback_ResetMatchSettingsToDefault )
}
void function SetupPrivateMatchUIVarsWhenReady()
{
// have to wait until end of first frame for SetUIVar to work
WaitEndFrame()
SetUIVar( level, "privatematch_map", GetPrivateMatchMapIndex( file.map ) )
SetUIVar( level, "privatematch_mode", GetPrivateMatchModeIndex( file.mode ) )
}
bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<string> args )
{
if ( GetConVarBool( "ns_private_match_only_host_can_start" ) )
if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
return true
if ( file.startState == ePrivateMatchStartState.STARTING )
{
// cancel start if we're already mid-countdown
file.startState = ePrivateMatchStartState.READY
SetUIVar( level, "privatematch_starting", ePrivateMatchStartState.READY )
SetUIVar( level, "gameStartTime", null )
}
else
{
// start match
file.startState = ePrivateMatchStartState.STARTING
thread StartMatch()
}
return true
}
bool function ClientCommandCallback_PrivateMatchSetMode( entity player, array<string> args )
{
if ( file.startState == ePrivateMatchStartState.STARTING )
return true
if ( args.len() != 1 )
return true
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) == 2 )
if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
return true
// todo: need to verify this value
file.mode = args[0]
//GameRules_SetGameMode( args[0] ) // can't do this here due to out of sync errors with new clients
RefreshPlayerTeams()
SetUIVar( level, "privatematch_mode", GetPrivateMatchModeIndex( args[0] ) )
return true
}
bool function ClientCommandCallback_SetCustomMap( entity player, array<string> args )
{
if ( file.startState == ePrivateMatchStartState.STARTING )
return true
if ( args.len() != 1 )
return true
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) == 2 )
if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
return true
// todo: need to verify this value
file.map = args[0]
// todo: this should NOT be necessary, private matches should use an api to register maps in the future rather than hardcoded ids
// should be removed whenever possible really
SetUIVar( level, "privatematch_map", GetPrivateMatchMapIndex( args[0] ) )
return true
}
bool function ClientCommandCallback_PrivateMatchSwitchTeams( entity player, array<string> args )
{
if ( file.startState == ePrivateMatchStartState.STARTING || GetGamemodeVarOrUseValue( file.mode, "max_teams", "2" ).tointeger() != 2 )
return true
// currently only support 2 teams in private matches
SetTeam( player, player.GetTeam() == 2 ? 3 : 2 )
return true
}
bool function ClientCommandCallback_PrivateMatchToggleSpectate( entity player, array<string> args )
{
// not currently working, gotta figure it out at some point
return true
}
void function StartMatch()
{
// set starting uivar
SetUIVar( level, "privatematch_starting", ePrivateMatchStartState.STARTING )
// start countdown
SetUIVar( level, "gameStartTime", Time() + GetConVarFloat( "ns_private_match_countdown_length" ) )
float countdownEndTime = Time() + GetConVarFloat( "ns_private_match_countdown_length" )
// can't use start here because we need to check stuff
while ( Time() < countdownEndTime )
{
// stop if the countdown's been cancelled
if ( file.startState != ePrivateMatchStartState.STARTING )
return
WaitFrame()
}
// do this before setting playlist
if ( GetConVarBool( "ns_private_match_override_maxplayers" ) )
SetPlaylistVarOverride( "max_players", GetCurrentPlaylistVarString( "max_players", "16" ) )
try
{
// todo: not every gamemode uses the same playlist as their name! need some code to resolve these manually
// would be nice if the gamemode api got some tweaks to allow for registering private match gamemodes maybe
SetCurrentPlaylist( file.mode )
}
catch ( exception )
{
// temp
if ( file.mode == "speedball" )
SetCurrentPlaylist( "lf" )
print( "couldn't find playlist for gamemode " + file.mode )
}
RefreshPlayerTeams()
SetConVarString( "ns_private_match_last_map", file.map )
SetConVarString( "ns_private_match_last_mode", file.mode )
//SetConVarBool( "ns_should_return_to_lobby", true ) // potentially temp?
string mode = file.mode
if ( !( mode in GAMETYPE_TEXT ) )
mode = GetPlaylistGamemodeByIndex( file.mode, 0 )
GameRules_ChangeMap( file.map, mode )
}
void function RefreshPlayerTeams()
{
int maxTeams = GetGamemodeVarOrUseValue( file.mode, "max_teams", "2" ).tointeger()
int maxPlayers = GetGamemodeVarOrUseValue( file.mode, "max_players", "12" ).tointeger()
// special case for situations where we wrongly assume ffa teams because there's 2 teams/2 players
if ( maxPlayers == maxTeams && maxTeams > 2 )
{
array<entity> players = GetPlayerArray()
for ( int i = 0; i < players.len(); i++ )
SetTeam( players[ i ], i + 7 ) // 7 is the lowest ffa team
}
else
{
bool lastSetMilitia = false
foreach ( entity player in GetPlayerArray() )
{
if ( player.GetTeam() == TEAM_MILITIA || player.GetTeam() == TEAM_IMC )
continue
if ( lastSetMilitia ) // ensure roughly evenish distribution
SetTeam( player, TEAM_IMC )
else
SetTeam( player, TEAM_MILITIA )
lastSetMilitia = !lastSetMilitia
}
}
}
bool function ClientCommandCallback_PrivateMatchSetPlaylistVarOverride( entity player, array<string> args )
{
if ( args.len() < 2 )
return true
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) >= 1 )
if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
return true
bool found = false
foreach ( string category in GetPrivateMatchSettingCategories() )
{
foreach ( CustomMatchSettingContainer setting in GetPrivateMatchCustomSettingsForCategory( category ) )
{
if ( args[ 0 ] == setting.playlistVar )
{
found = true
break
}
}
}
if ( found )
SetPlaylistVarOverride( args[0], args[1] )
return true
}
bool function ClientCommandCallback_ResetMatchSettingsToDefault( entity player, array<string> args )
{
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) >= 1 )
if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
return true
ClearPlaylistVarOverrides()
return true
}
|