· 6 years ago · Apr 14, 2019, 04:54 PM
1/* Uncomment the below define to use SQL stats intead of flat-file stats. */
2
3//#define SQL
4
5/* Uncomment the below define to have a player's timestamp get refreshed right away as soon as they join, instead of only when they play to the end of a game.
6** There is a slight overhead for flat-file stats, but it's fairly speedy. When using SQL stats, you might as well uncomment this -- it'll be speedy. */
7
8//#define REFRESH_TIMESTAMP_ON_JOIN
9
10// Thanks a lot to 3volution for helping me iron out some
11// bugs and for giving me some helpful suggestions.
12//
13// Thanks a lot to raa for helping me pinpoint the crash,
14// and discovering the respawn bug.
15//
16// Thanks a lot to BAILOPAN for binary logging, and for
17// CSDM spawn files that I could leech off of. Oh, and
18// also AMXX, etcetera.
19//
20// Thanks to VEN for Fakemeta Utilities to ease development.
21//
22// Thanks a lot to all of my supporters, but especially:
23// 3volution, aligind4h0us3, arkshine, bmp, Curryking,
24// Gunny, IdiotSavant, Mordekay, polakpolak, raa, Silver
25// Dragon, Smileypt, Tomek Kalkowski, ToT | V!PER, and Vm|Mayhem.
26//
27// Thanks especially to all of the translators:
28// arkshine, b!orn, commonbullet, Curryking, Deviance,
29// D o o m, eD., e-N-z, Fr3ak0ut, godlike, harbu, iggy_bus,
30// jopmako, jozzz, KylixMynxAltoLAG, MeRcyLeZZ, Min2liz,
31// Mordekay, Morpheus759, rpm, SAMURAI16, Simbad, TEG,
32// ToT | V!PER, trawiator, Twilight Suzuka, webpsiho, and
33// others that I surely forgot (sorry!).
34//
35// Thanks to SeNz0r for many of the new sounds!
36//
37// If I missed you, please yell at me.
38
39#pragma dynamic 8192 // just a little bit extra, not too much
40
41#include <amxmodx>
42#include <amxmisc>
43#include <fakemeta>
44#include <fakemeta_util>
45#include <cstrike>
46#include <hamsandwich>
47
48// defines to be left alone
49new const GG_VERSION[] = "2.13c";
50#define LANG_PLAYER_C -76 // for gungame_print (arbitrary number)
51#define TNAME_SAVE pev_noise3 // for blocking game_player_equip and player_weaponstrip
52#define WINSOUNDS_SIZE (MAX_WINSOUNDS*MAX_WINSOUND_LEN)+1 // for gg_sound_winner
53
54// more customizable-friendly defines
55#define TOP_PLAYERS 10 // for !top10
56#define MAX_WEAPONS 36 // for gg_weapon_order
57#define MAX_WINSOUNDS 12 // for gg_sound_winnner
58#define MAX_WINSOUND_LEN 48 // for gg_sound_winner
59#define TEMP_SAVES 32 // for gg_save_temp
60#define MAX_WEAPON_ORDERS 10 // for random gg_weapon_order
61#define LEADER_DISPLAY_RATE 10.0 // for gg_leader_display
62#define MAX_SPAWNS 128 // for gg_dm_spawn_random
63#define MAX_STATS_RANK 1000 // cap of 1000 = 0.0063495ish sec for longest stats_get_position, cap of 5000 = 0.0327655ish sec
64
65// returns which of the two stats files we should be using currently (si stands for Stats Index)
66#define get_gg_si() (get_pcvar_num(gg_stats_split) && get_pcvar_num(gg_teamplay))
67
68// cs_set_user_money
69#if cellbits == 32
70#define OFFSET_CSMONEY 115
71#else
72#define OFFSET_CSMONEY 140
73#endif
74#define OFFSET_LINUX 5
75
76// animations
77#define USP_DRAWANIM 6
78#define M4A1_DRAWANIM 5
79
80// saves memory???
81new const WEAPON_HEGRENADE[] = "weapon_hegrenade";
82new const WEAPON_KNIFE[] = "weapon_knife";
83new const WEAPON_GLOCK18[] = "weapon_glock18";
84new const HEGRENADE[] = "hegrenade";
85new const KNIFE[] = "knife";
86new const BRASS_BELL_SOUND[] = "gungame/gg_brass_bell.wav";
87new const KILL_DING_SOUND[] = "buttons/bell1.wav";
88
89// toggle_gungame
90enum
91{
92 TOGGLE_FORCE = -1,
93 TOGGLE_DISABLE,
94 TOGGLE_ENABLE
95};
96
97// gg_status_display
98enum
99{
100 STATUS_LEADERWPN = 1,
101 STATUS_YOURWPN,
102 STATUS_KILLSLEFT,
103 STATUS_KILLSDONE
104};
105
106// value of bombStatus[3]
107enum
108{
109 BOMB_PICKEDUP = -1,
110 BOMB_DROPPED,
111 BOMB_PLANTED
112};
113
114// for gg_messages
115#define MSGS_CLASSIC 2
116#define MSGS_NOCOLOR 4
117#define MSGS_HIDETEXT 8
118#define MSGS_HIDEHUD 16
119
120// task ids
121#define TASK_END_STAR 200
122#define TASK_RESPAWN 300
123#define TASK_CLEAR_SAVE 500
124#define TASK_CHECK_DEATHMATCH 600
125#define TASK_REMOVE_PROTECTION 700
126#define TASK_TOGGLE_GUNGAME 800
127#define TASK_WARMUP_CHECK 900
128#define TASK_VERIFY_WEAPON 1000
129#define TASK_DELAYED_SUICIDE 1100
130#define TASK_REFRESH_NADE 1200
131#define TASK_LEADER_DISPLAY 1300
132#define TASK_PLAY_LEAD_SOUNDS 1400
133#define TASK_CHECK_JOINCLASS 1500
134#define TASK_AUTOVOTE_RESULT 1600
135#define TASK_GET_TOP_PLAYERS 1700
136
137//**********************************************************************
138// VARIABLE DEFINITIONS
139//**********************************************************************
140
141// pcvar holders
142new gg_enabled, gg_ff_auto, gg_vote_setting, gg_map_setup, gg_join_msg,
143gg_weapon_order, gg_max_lvl, gg_triple_on, gg_turbo, gg_knife_pro,
144gg_worldspawn_suicide, gg_handicap_on, gg_top10_handicap, gg_warmup_timer_setting,
145gg_warmup_weapon, gg_sound_levelup, gg_sound_leveldown, gg_sound_levelsteal,
146gg_sound_nade, gg_sound_knife, gg_sound_welcome, gg_sound_triple, gg_sound_winner,
147gg_kills_per_lvl, gg_vote_custom, gg_changelevel_custom, gg_ammo_amount,
148gg_stats_prune, gg_refill_on_kill, gg_messages, gg_tk_penalty,
149gg_save_temp, gg_stats_mode, gg_pickup_others, gg_stats_winbonus, gg_map_iterations,
150gg_warmup_multi, gg_stats_ip, gg_extra_nades, gg_endmap_setup, gg_autovote_rounds,
151gg_autovote_ratio, gg_autovote_delay, gg_autovote_time, gg_autovote_mode, gg_ignore_bots, gg_nade_refresh,
152gg_block_equips, gg_leader_display, gg_leader_display_x, gg_leader_display_y,
153gg_sound_takenlead, gg_sound_tiedlead, gg_sound_lostlead, gg_lead_sounds, gg_knife_elite,
154gg_teamplay, gg_teamplay_knife_mod, gg_teamplay_nade_mod, gg_suicide_penalty, gg_winner_motd,
155gg_bomb_defuse_lvl, gg_nade_glock, gg_nade_smoke, gg_nade_flash, gg_give_armor, gg_give_helmet,
156gg_dm, gg_dm_sp_time, gg_dm_sp_mode, gg_dm_spawn_random, gg_dm_spawn_delay, gg_dm_corpses, gg_awp_oneshot,
157gg_host_touch_reward, gg_host_rescue_reward, gg_host_kill_reward, gg_dm_countdown, gg_status_display,
158gg_dm_spawn_afterplant, gg_block_objectives, gg_host_kill_penalty, gg_dm_start_random, gg_allow_changeteam,
159gg_teamplay_timeratio, gg_disable_money, gg_kills_botmod, gg_bots_skipnade, gg_bots_knifeable,
160gg_afk_protection, gg_stats_split, gg_top10_ppp;
161
162// weapon information
163new maxClip[31] = { -1, 13, -1, 10, 1, 7, -1, 30, 30, 1, 30, 20, 25, 30, 35, 25, 12, 20,
164 10, 30, 100, 8, 30, 30, 20, 2, 7, 30, 30, -1, 50 };
165
166new maxAmmo[31] = { -1, 52, -1, 90, -1, 32, 1, 100, 90, -1, 120, 100, 100, 90, 90, 90, 100, 100,
167 30, 120, 200, 32, 90, 120, 60, -1, 35, 90, 90, -1, 100 };
168
169new weaponSlots[31] = { -1, 2, -1, 1, 4, 1, 5, 1, 1, 4, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1,
170 4, 2, 1, 1, 3, 1 };
171
172enum statsData
173{
174 sdAuthid[32],
175 sdWins[2],
176 sdName[32],
177 sdTimestamp, // [1]
178 sdPoints[2],
179 sdStreak[2]
180}; // size = 71
181
182enum saveData
183{
184 svAuthid[32],
185 svLevel, // [1]
186 svScore, // [1]
187 svStatsPosition[2],
188 svTeamTimes[2],
189 svTime // [1]
190}; // size = 39
191
192// misc
193new weapons_menu, scores_menu, level_menu, warmup = -1, warmupWeapon[24], voted, won, trailSpr, roundEnded,
194menuText[512], dummy[2], tempSave[TEMP_SAVES][saveData], czero, maxPlayers, mapIteration = 1, cfgDir[32],
195autovoted, autovotes[3], autovote_mode, roundsElapsed, gameCommenced, cycleNum = -1, czbot_hams, mp_friendlyfire,
196winSounds[MAX_WINSOUNDS][MAX_WINSOUND_LEN+1], numWinSounds, currentWinSound, hudSyncWarmup, hudSyncReqKills,
197hudSyncLDisplay, shouldWarmup, ggActive, teamLevel[3], teamLvlWeapon[3][24], teamScore[3], bombMap, hostageMap,
198bombStatus[4], c4planter, Float:spawns[MAX_SPAWNS][9], spawnCount, csdmSpawnCount, hudSyncCountdown,
199weaponName[MAX_WEAPONS+1][24], Float:weaponGoal[MAX_WEAPONS+1], weaponNum, initTeamplayStr[32], initTeamplayInt = -1,
200bot_quota, spareName[32], sqlInit, galileoID = -1;
201
202// stats file stuff
203new sfStatsStruct[statsData], lastStatsMode = -909;
204
205// event ids
206new gmsgSayText, gmsgCurWeapon, gmsgStatusIcon, gmsgBombDrop, gmsgBombPickup, gmsgHideWeapon,
207gmsgCrosshair, gmsgScenario;
208
209// player values
210new level[33], levelsThisRound[33], score[33], lvlWeapon[33][24], star[33], welcomed[33],
211page[33], lastKilled[33], hosties[33][2], silenced[33], respawn_timeleft[33], Float:lastSwitch[33], lastTeam[33],
212spawnSounds[33], spawnProtected[33], statsPosition[33][2], Float:teamTimes[33][2], pointsExtraction[33][5],
213Float:spawnOrigin[33][3], Float:spawnAngles[33][3], afkCheck[33], playerStats[33][statsData];
214
215#if defined SQL
216 #include <sqlx>
217
218 // flags for the "flags" field in the gg_sql_winmotd table
219 #define WON 1
220 #define LOST 2
221 #define LASTKILL 4
222 #define NEWRECORD 8
223
224 new gg_sql_host, gg_sql_user, gg_sql_pass, gg_sql_db, gg_sql_table, gg_sql_streak_table, gg_sql_winmotd_table,
225 sqlTable[128], sqlStreakTable[128], sqlPlayersTable[128], serverip[64], Handle:tuple, Handle:db, Handle:query, safeName[64], mkQuery[1536];
226#else
227 new gg_stats_file, gg_stats_streak_file;
228
229 new sfFile[64], sfLineData[112], sfAuthid[32], sfTimestamp[12], Array:statsArray, Array:statsPointers[2], statsSize[2];
230#endif
231
232//**********************************************************************
233// INITIATION FUNCTIONS
234//**********************************************************************
235
236// plugin load
237public plugin_init()
238{
239 register_plugin("GunGame AMXX",GG_VERSION,"Avalanche");
240 register_cvar("gg_version",GG_VERSION,FCVAR_SERVER);
241 set_cvar_string("gg_version",GG_VERSION);
242
243 // mehrsprachige unterstützung (nein, spreche ich nicht Deutsches)
244 register_dictionary("gungame.txt");
245 register_dictionary("common.txt");
246 register_dictionary("adminvote.txt");
247
248 // event ids
249 gmsgSayText = get_user_msgid("SayText");
250 gmsgCurWeapon = get_user_msgid("CurWeapon");
251 gmsgStatusIcon = get_user_msgid("StatusIcon");
252 gmsgScenario = get_user_msgid("Scenario");
253 gmsgBombDrop = get_user_msgid("BombDrop");
254 gmsgBombPickup = get_user_msgid("BombPickup");
255 gmsgHideWeapon = get_user_msgid("HideWeapon");
256 gmsgCrosshair = get_user_msgid("Crosshair");
257
258 // events
259 register_event("ResetHUD","event_resethud","be");
260 register_event("HLTV","event_new_round","a","1=0","2=0");
261 register_event("CurWeapon","event_curweapon","be","1=1");
262 register_event("AmmoX","event_ammox","be");
263 register_event("30","event_intermission","a");
264 register_event("TextMsg","event_round_restart","a","2=#Game_Commencing","2=#Game_will_restart_in");
265 register_event("23","event_bomb_detonation","a","1=17","6=-105","7=17"); // planted bomb exploded
266
267 // forwards
268 register_forward(FM_SetModel,"fw_setmodel");
269 register_forward(FM_EmitSound,"fw_emitsound");
270
271 // logevents
272 register_logevent("event_bomb_detonation",6,"3=Target_Bombed"); // another bomb exploded event, for security
273 register_logevent("logevent_bomb_planted",3,"2=Planted_The_Bomb"); // bomb planted
274 register_logevent("logevent_bomb_defused",3,"2=Defused_The_Bomb"); // bomb defused
275 register_logevent("logevent_round_end",2,"1=Round_End"); // round ended
276 register_logevent("logevent_hostage_touched",3,"2=Touched_A_Hostage");
277 register_logevent("logevent_hostage_rescued",3,"2=Rescued_A_Hostage");
278 register_logevent("logevent_hostage_killed",3,"2=Killed_A_Hostage");
279 register_logevent("logevent_team_join",3,"1=joined team");
280
281 // messages
282 register_message(gmsgScenario,"message_scenario");
283 register_message(get_user_msgid("ClCorpse"),"message_clcorpse");
284 register_message(get_user_msgid("Money"),"message_money");
285 register_message(gmsgBombDrop,"message_bombdrop");
286 register_message(gmsgBombPickup,"message_bombpickup");
287 register_message(get_user_msgid("WeapPickup"),"message_weappickup"); // for gg_block_objectives
288 register_message(get_user_msgid("AmmoPickup"),"message_ammopickup"); // for gg_block_objectives
289 register_message(get_user_msgid("TextMsg"),"message_textmsg"); // for gg_block_objectives
290 register_message(get_user_msgid("HostagePos"),"message_hostagepos"); // for gg_block_objectives
291
292 // hams
293 RegisterHam(Ham_Touch,"weaponbox","ham_weapon_touch",0);
294 RegisterHam(Ham_Touch,"armoury_entity","ham_weapon_touch",0);
295 RegisterHam(Ham_Spawn,"player","ham_player_spawn",1);
296 RegisterHam(Ham_Killed,"player","ham_player_killed_pre",0);
297 RegisterHam(Ham_Killed,"player","ham_player_killed_post",1);
298
299 // commands
300 register_clcmd("joinclass","cmd_joinclass"); // new menus
301 register_menucmd(register_menuid("Terrorist_Select",1),511,"cmd_joinclass"); // old menus
302 register_menucmd(register_menuid("CT_Select",1),511,"cmd_joinclass"); // old menus
303 register_concmd("amx_gungame","cmd_gungame",ADMIN_CVAR,"<0|1> - toggles the functionality of GunGame.");
304 register_concmd("amx_gungame_level","cmd_gungame_level",ADMIN_BAN,"<target> <level> - sets target's level. use + or - for relative, otherwise it's absolute.");
305 register_concmd("amx_gungame_score","cmd_gungame_score",ADMIN_BAN,"<target> <score> [dont_refill] - sets target's score. use + or - for relative, otherwise it's absolute.");
306 register_concmd("amx_gungame_vote","cmd_gungame_vote",ADMIN_VOTE,"[mode] - starts a vote to toggle GunGame.");
307 register_concmd("amx_gungame_win","cmd_gungame_win",ADMIN_BAN,"[target] - if target, forces target to win. if no target, forces highest level player to win.");
308 register_concmd("amx_gungame_teamplay","cmd_gungame_teamplay",ADMIN_BAN,"<0|1> [killsperlvl] [suicidepenalty] - toggles teamplay mode. optionally specify new cvar values.");
309 register_concmd("amx_gungame_restart","cmd_gungame_restart",ADMIN_BAN,"[delay] [full] - restarts GunGame. optionally specify a delay, in seconds. if full, reloads config and everything.");
310 register_srvcmd("gg_reloadweapons","cmd_reloadweapons",ADMIN_CVAR,"- reloads the weapon order and kills per level from cvars");
311 register_clcmd("say","cmd_say");
312 register_clcmd("say_team","cmd_say");
313
314 // menus
315 register_menucmd(register_menuid("autovote_menu"),MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_0,"autovote_menu_handler");
316 register_menucmd(register_menuid("welcome_menu"),1023,"welcome_menu_handler");
317 register_menucmd(register_menuid("restart_menu"),MENU_KEY_1|MENU_KEY_0,"restart_menu_handler");
318 weapons_menu = register_menuid("weapons_menu");
319 register_menucmd(weapons_menu,MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_0,"weapons_menu_handler");
320 register_menucmd(register_menuid("top10_menu"),MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_4|MENU_KEY_0,"top10_menu_handler");
321 scores_menu = register_menuid("scores_menu");
322 register_menucmd(scores_menu,MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_0,"scores_menu_handler");
323 level_menu = register_menuid("level_menu");
324 register_menucmd(level_menu,1023,"level_menu_handler");
325
326 // basic cvars
327 gg_enabled = register_cvar("gg_enabled","1");
328 gg_vote_setting = register_cvar("gg_vote_setting","2");
329 gg_vote_custom = register_cvar("gg_vote_custom","");
330 gg_changelevel_custom = register_cvar("gg_changelevel_custom","");
331 gg_map_setup = register_cvar("gg_map_setup","mp_timelimit 45; mp_winlimit 0; sv_alltalk 0; mp_chattime 10; mp_c4timer 25");
332 gg_endmap_setup = register_cvar("gg_endmap_setup","");
333 gg_join_msg = register_cvar("gg_join_msg","1");
334 gg_messages = register_cvar("gg_messages","1");
335 gg_save_temp = register_cvar("gg_save_temp","300"); // = 5 * 60 = 5 minutes
336 gg_status_display = register_cvar("gg_status_display","1");
337 gg_map_iterations = register_cvar("gg_map_iterations","1");
338 gg_ignore_bots = register_cvar("gg_ignore_bots","0");
339 gg_block_equips = register_cvar("gg_block_equips","2");
340 gg_leader_display = register_cvar("gg_leader_display","1");
341 gg_leader_display_x = register_cvar("gg_leader_display_x","-1.0");
342 gg_leader_display_y = register_cvar("gg_leader_display_y","0.0");
343 gg_allow_changeteam = register_cvar("gg_allow_changeteam","2");
344 gg_disable_money = register_cvar("gg_disable_money","1");
345 gg_winner_motd = register_cvar("gg_winner_motd","1");
346 gg_afk_protection = register_cvar("gg_afk_protection","0");
347 gg_top10_ppp = register_cvar("gg_top10_ppp","8");
348
349 // autovote cvars
350 gg_autovote_mode = register_cvar("gg_autovote_mode","0");
351 gg_autovote_rounds = register_cvar("gg_autovote_rounds","1");
352 gg_autovote_delay = register_cvar("gg_autovote_delay","8.0");
353 gg_autovote_ratio = register_cvar("gg_autovote_ratio","0.51");
354 gg_autovote_time = register_cvar("gg_autovote_time","10.0");
355
356 // stats cvars
357#if !defined SQL
358 gg_stats_file = register_cvar("gg_stats_file","gungame.stats");
359 gg_stats_streak_file = register_cvar("gg_stats_streak_file","gungame.streaks");
360#endif
361 gg_stats_ip = register_cvar("gg_stats_ip","0");
362 gg_stats_prune = register_cvar("gg_stats_prune","2592000"); // = 60 * 60 * 24 * 30 = 30 days
363 gg_stats_mode = register_cvar("gg_stats_mode","2");
364 gg_stats_split = register_cvar("gg_stats_split","0");
365 gg_stats_winbonus = register_cvar("gg_stats_winbonus","1.5");
366
367 // deathmatch cvars
368 gg_dm = register_cvar("gg_dm","1");
369 gg_dm_sp_time = register_cvar("gg_dm_sp_time","1.0");
370 gg_dm_sp_mode = register_cvar("gg_dm_sp_mode","1");
371 gg_dm_spawn_random = register_cvar("gg_dm_spawn_random","2");
372 gg_dm_start_random = register_cvar("gg_dm_start_random","1");
373 gg_dm_spawn_delay = register_cvar("gg_dm_spawn_delay","3.0");
374 gg_dm_spawn_afterplant = register_cvar("gg_dm_spawn_afterplant","1");
375 gg_dm_corpses = register_cvar("gg_dm_corpses","1");
376 gg_dm_countdown = register_cvar("gg_dm_countdown","2");
377
378 // objective cvars
379 gg_block_objectives = register_cvar("gg_block_objectives","0");
380 gg_bomb_defuse_lvl = register_cvar("gg_bomb_defuse_lvl","1");
381 gg_host_touch_reward = register_cvar("gg_host_touch_reward","2");
382 gg_host_rescue_reward = register_cvar("gg_host_rescue_reward","2");
383 gg_host_kill_reward = register_cvar("gg_host_kill_reward","1");
384 gg_host_kill_penalty = register_cvar("gg_host_kill_penalty","1");
385
386 // teamplay cvars
387 gg_teamplay = register_cvar("gg_teamplay","0");
388 gg_teamplay_knife_mod = register_cvar("gg_teamplay_knife_mod","0.33");
389 gg_teamplay_nade_mod = register_cvar("gg_teamplay_nade_mod","0.50");
390 gg_teamplay_timeratio = register_cvar("gg_teamplay_timeratio","1");
391
392 // gameplay cvars
393 gg_ff_auto = register_cvar("gg_ff_auto","1");
394 gg_weapon_order = register_cvar("gg_weapon_order","glock18,usp,p228,deagle,fiveseven,elite,m3,xm1014,tmp,mac10,mp5navy,ump45,p90,galil,famas,ak47,scout,m4a1,sg552,aug,m249,hegrenade,knife");
395 gg_max_lvl = register_cvar("gg_max_lvl","3");
396 gg_triple_on = register_cvar("gg_triple_on","0");
397 gg_turbo = register_cvar("gg_turbo","1");
398 gg_knife_pro = register_cvar("gg_knife_pro","1");
399 gg_knife_elite = register_cvar("gg_knife_elite","0");
400 gg_suicide_penalty = register_cvar("gg_suicide_penalty","1");
401 gg_worldspawn_suicide = register_cvar("gg_worldspawn_suicide","1");
402 gg_pickup_others = register_cvar("gg_pickup_others","0");
403 gg_handicap_on = register_cvar("gg_handicap_on","1");
404 gg_top10_handicap = register_cvar("gg_top10_handicap","1");
405 gg_warmup_timer_setting = register_cvar("gg_warmup_timer_setting","60");
406 gg_warmup_weapon = register_cvar("gg_warmup_weapon",KNIFE);
407 gg_warmup_multi = register_cvar("gg_warmup_multi","0");
408 gg_nade_glock = register_cvar("gg_nade_glock","1");
409 gg_nade_smoke = register_cvar("gg_nade_smoke","0");
410 gg_nade_flash = register_cvar("gg_nade_flash","0");
411 gg_extra_nades = register_cvar("gg_extra_nades","1");
412 gg_nade_refresh = register_cvar("gg_nade_refresh","5.0");
413 gg_kills_per_lvl = register_cvar("gg_kills_per_lvl","2");
414 gg_kills_botmod = register_cvar("gg_kills_botmod","1.0");
415 gg_give_armor = register_cvar("gg_give_armor","100");
416 gg_give_helmet = register_cvar("gg_give_helmet","1");
417 gg_ammo_amount = register_cvar("gg_ammo_amount","200");
418 gg_refill_on_kill = register_cvar("gg_refill_on_kill","1");
419 gg_tk_penalty = register_cvar("gg_tk_penalty","1");
420 gg_awp_oneshot = register_cvar("gg_awp_oneshot","1");
421 gg_bots_skipnade = register_cvar("gg_bots_skipnade","0");
422 gg_bots_knifeable = register_cvar("gg_bots_knifeable","1");
423
424#if defined SQL
425 // SQL cvars
426 gg_sql_host = register_cvar("gg_sql_host","127.0.0.1",FCVAR_PROTECTED);
427 gg_sql_user = register_cvar("gg_sql_user","root",FCVAR_PROTECTED);
428 gg_sql_pass = register_cvar("gg_sql_pass","",FCVAR_PROTECTED);
429 gg_sql_db = register_cvar("gg_sql_db","amx",FCVAR_PROTECTED);
430 gg_sql_table = register_cvar("gg_sql_table","gg_stats",FCVAR_PROTECTED);
431 gg_sql_streak_table = register_cvar("gg_sql_streak_table","gg_streaks",FCVAR_PROTECTED);
432 gg_sql_winmotd_table = register_cvar("gg_sql_winmotd_table","gg_winmotd",FCVAR_PROTECTED);
433
434 get_user_ip(0,serverip,63,0); // with port
435#else
436 sqlInit = 1;
437#endif
438
439 // sound cvars done in plugin_precache now
440
441 // random weapon order cvars
442 new i, cvar[20];
443 for(i=1;i<=MAX_WEAPON_ORDERS;i++)
444 {
445 formatex(cvar,19,"gg_weapon_order%i",i);
446 register_cvar(cvar,"");
447 }
448
449 // update status immediately
450 ggActive = get_pcvar_num(gg_enabled);
451
452 // make sure to setup amx_nextmap incase nextmap.amxx isn't running
453 if(!cvar_exists("amx_nextmap")) register_cvar("amx_nextmap","",FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY);
454
455 // make sure we have this to trick mapchooser.amxx into working
456 if(!cvar_exists("mp_maxrounds")) register_cvar("mp_maxrounds","0",FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY);
457
458 // collect some other information that would be handy
459 maxPlayers = get_maxplayers();
460
461 // create hud sync objects
462 hudSyncWarmup = CreateHudSyncObj();
463 hudSyncReqKills = CreateHudSyncObj();
464 hudSyncLDisplay = CreateHudSyncObj();
465 hudSyncCountdown = CreateHudSyncObj();
466
467 // remember the mod
468 new modName[7];
469 get_modname(modName,6);
470 if(equal(modName,"czero"))
471 {
472 czero = 1;
473 bot_quota = get_cvar_pointer("bot_quota");
474 }
475
476 // identify this as a bomb map
477 if(fm_find_ent_by_class(maxPlayers,"info_bomb_target") || fm_find_ent_by_class(1,"func_bomb_target"))
478 bombMap = 1;
479
480 // identify this as a hostage map
481 if(fm_find_ent_by_class(maxPlayers,"hostage_entity"))
482 hostageMap = 1;
483
484 // get spawns for deathmatch
485 init_spawns();
486
487 // delay for server.cfg
488 set_task(1.0,"toggle_gungame",TASK_TOGGLE_GUNGAME + TOGGLE_FORCE);
489
490 // manage pruning (longer delay for toggle_gungame)
491 set_task(2.0,"manage_pruning");
492
493 // map configs take 6.1 seconds to load
494 set_task(6.2,"setup_weapon_order");
495 set_task(6.2,"stats_get_top_players",TASK_GET_TOP_PLAYERS);
496}
497
498// plugin precache
499public plugin_precache()
500{
501 // used in precache_sounds_from_config()
502 get_configsdir(cfgDir,31);
503
504 // sound cvars
505 gg_sound_levelup = register_cvar("gg_sound_levelup","sound/gungame/gg_levelup.wav");
506 gg_sound_leveldown = register_cvar("gg_sound_leveldown","sound/ambience/xtal_down1(e70)");
507 gg_sound_levelsteal = register_cvar("gg_sound_levelsteal","sound/turret/tu_die.wav");
508 gg_sound_nade = register_cvar("gg_sound_nade","sound/gungame/gg_nade_level.wav");
509 gg_sound_knife = register_cvar("gg_sound_knife","sound/gungame/gg_knife_level.wav");
510 gg_sound_welcome = register_cvar("gg_sound_welcome","sound/gungame/gg_welcome.wav");
511 gg_sound_triple = register_cvar("gg_sound_triple","sound/gungame/gg_triple.wav");
512 gg_sound_winner = register_cvar("gg_sound_winner","media/Half-Life03.mp3;media/Half-Life08.mp3;media/Half-Life11.mp3;media/Half-Life17.mp3");
513 gg_sound_takenlead = register_cvar("gg_sound_takenlead","sound/gungame/gg_takenlead.wav");
514 gg_sound_tiedlead = register_cvar("gg_sound_tiedlead","sound/gungame/gg_tiedlead.wav");
515 gg_sound_lostlead = register_cvar("gg_sound_lostlead","sound/gungame/gg_lostlead.wav");
516 gg_lead_sounds = register_cvar("gg_lead_sounds","0.9");
517
518 mp_friendlyfire = get_cvar_pointer("mp_friendlyfire");
519
520 // precache everything in the config (regular and teamplay) -- we might need them
521 precache_sounds_from_config();
522
523 // also precache what we have now, in case the server doesn't have a GunGame config
524 precache_sound_by_cvar(gg_sound_levelup);
525 precache_sound_by_cvar(gg_sound_leveldown);
526 precache_sound_by_cvar(gg_sound_levelsteal);
527 precache_sound_by_cvar(gg_sound_nade);
528 precache_sound_by_cvar(gg_sound_knife);
529 precache_sound_by_cvar(gg_sound_welcome);
530 precache_sound_by_cvar(gg_sound_triple);
531 precache_sound_by_cvar(gg_sound_takenlead);
532 precache_sound_by_cvar(gg_sound_tiedlead);
533 precache_sound_by_cvar(gg_sound_lostlead);
534
535 get_pcvar_string(gg_sound_winner,dummy,1);
536 if(dummy[0]) // win sounds enabled
537 {
538 // gg_sound_winner might contain multiple sounds
539 new buffer[WINSOUNDS_SIZE], temp[MAX_WINSOUND_LEN+1], pos;
540 get_pcvar_string(gg_sound_winner,buffer,WINSOUNDS_SIZE-1);
541
542 while(numWinSounds < MAX_WINSOUNDS)
543 {
544 pos = contain_char(buffer,';');
545
546 // no more after this, precache what we have left
547 if(pos == -1)
548 {
549 if(buffer[0])
550 {
551 precache_sound_special(buffer);
552 copy(winSounds[numWinSounds++],MAX_WINSOUND_LEN,buffer);
553 }
554 break;
555 }
556
557 // copy up to the semicolon and precache that
558 copy(temp,pos,buffer);
559
560 if(temp[0])
561 {
562 precache_sound_special(temp);
563 copy(winSounds[numWinSounds++],MAX_WINSOUND_LEN,temp);
564 }
565
566 // copy everything after the semicolon
567 copy(buffer,WINSOUNDS_SIZE-1,buffer[pos+1]);
568 }
569 }
570
571 // some generic, non-changing things
572 precache_sound(BRASS_BELL_SOUND);
573 precache_sound(KILL_DING_SOUND);
574 precache_sound("common/null.wav");
575
576 // for the star
577 trailSpr = precache_model("sprites/laserbeam.spr");
578}
579
580public plugin_cfg()
581{
582 galileoID = is_plugin_loaded("Galileo");
583}
584
585public plugin_end()
586{
587#if defined SQL
588 sql_uninit();
589#endif
590
591 // run endmap setup on plugin close
592 if(ggActive)
593 {
594 // reset random teamplay
595 if(initTeamplayInt != -1) set_pcvar_string(gg_teamplay,initTeamplayStr);
596
597 new setup[512];
598 get_pcvar_string(gg_endmap_setup,setup,511);
599 if(setup[0]) server_cmd(setup);
600 }
601}
602
603//**********************************************************************
604// FORWARDS
605//**********************************************************************
606
607// client gets a steamid
608public client_authorized(id)
609{
610 clear_values(id);
611
612 static authid[32];
613 get_gg_authid(id,authid,31);
614
615 // load temporary save
616 if(ggActive && get_pcvar_num(gg_save_temp))
617 {
618 new i, save = -1;
619
620 // find our possible temp save
621 for(i=0;i<TEMP_SAVES;i++)
622 {
623 if(equal(authid,tempSave[i][svAuthid],31))
624 {
625 save = i;
626 break;
627 }
628 }
629
630 // we found a save
631 if(save > -1)
632 {
633 if(!get_pcvar_num(gg_teamplay))
634 {
635 // these are solo-only
636 level[id] = tempSave[save][svLevel];
637 score[id] = tempSave[save][svScore];
638 get_level_weapon(level[id],lvlWeapon[id],23);
639 }
640
641 statsPosition[id][0] = tempSave[save][svStatsPosition][0];
642 statsPosition[id][1] = tempSave[save][svStatsPosition][1];
643 teamTimes[id][0] = Float:tempSave[save][svTeamTimes][0];
644 teamTimes[id][1] = Float:tempSave[save][svTeamTimes][1];
645
646 // clear it
647 clear_save(TASK_CLEAR_SAVE+save);
648 }
649 }
650
651#if defined SQL
652 if(!statsPosition[id][0]) stats_get_position(id,authid,0);
653 if(!statsPosition[id][1]) stats_get_position(id,authid,1);
654#else
655 // cache our position if we didn't get it from a save
656 if(!statsPosition[id][0] || !statsPosition[id][1])
657 {
658 if(statsArray) // we've set up the stats array
659 {
660 recheck_stats_sorting(); // see if anything changed
661
662 // if nothing happened, get my position
663 if(!statsPosition[id][0]) stats_get_position(id,authid,0);
664 if(!statsPosition[id][1]) stats_get_position(id,authid,1);
665 }
666 }
667#endif
668
669#if defined REFRESH_TIMESTAMP_ON_JOIN
670 stats_refresh_timestamp(authid);
671#endif
672}
673
674// client leaves, reset values
675public client_disconnected(id)
676{
677 // remove certain tasks
678 remove_task(TASK_VERIFY_WEAPON+id);
679 remove_task(TASK_REFRESH_NADE+id);
680 remove_task(TASK_RESPAWN+id);
681 remove_task(TASK_CHECK_DEATHMATCH+id);
682 remove_task(TASK_REMOVE_PROTECTION+id);
683 remove_task(TASK_DELAYED_SUICIDE+id);
684
685 // don't bother saving if in winning period or warmup
686 if(!won && warmup <= 0)
687 {
688 new save_temp = get_pcvar_num(gg_save_temp);
689
690 // temporarily save values
691 if(ggActive && save_temp && (level[id] > 1 || score[id] > 0))
692 {
693 // keep track of times
694 new team = get_user_team(id);
695 if(team == 1 || team == 2) teamTimes[id][team-1] += get_gametime() - lastSwitch[id];
696
697 new freeSave = -1, oldestSave = -1, i;
698
699 for(i=0;i<TEMP_SAVES;i++)
700 {
701 // we found a free one
702 if(!tempSave[i][svAuthid][0])
703 {
704 freeSave = i;
705 break;
706 }
707
708 // keep track of one soonest to expire
709 if(oldestSave == -1 || tempSave[i][svTime] < tempSave[oldestSave][svTime])
710 oldestSave = i;
711 }
712
713 // no free, use oldest
714 if(freeSave == -1) freeSave = oldestSave;
715
716 get_gg_authid(id,tempSave[freeSave][svAuthid],31);
717
718 tempSave[freeSave][svLevel] = level[id];
719 tempSave[freeSave][svScore] = score[id];
720 tempSave[freeSave][svStatsPosition][0] = statsPosition[id][0];
721 tempSave[freeSave][svStatsPosition][1] = statsPosition[id][1];
722 tempSave[freeSave][svTeamTimes][0] = _:teamTimes[id][0];
723 tempSave[freeSave][svTeamTimes][1] = _:teamTimes[id][1];
724 tempSave[freeSave][svTime] = _:get_gametime();
725
726 set_task(float(save_temp),"clear_save",TASK_CLEAR_SAVE+freeSave);
727 }
728 }
729
730 clear_values(id);
731 statsPosition[id][0] = 0;
732 statsPosition[id][1] = 0;
733 stats_clear_struct(playerStats[id]);
734}
735
736// someone joins, monitor ham hooks
737public client_putinserver(id)
738{
739 if(czero && !czbot_hams && is_user_bot(id) && get_pcvar_num(bot_quota) > 0)
740 set_task(0.1,"czbot_hook_ham",id);
741
742 // bots don't call joinclass
743 if(is_user_bot(id)) cmd_joinclass(id);
744}
745
746// delay for private data to initialize --
747// here is the problem: registering a ham hook for "player" won't
748// register it for CZ bots, for some reason. so we have to register
749// it by entity. so we do this ridiculous thing in order to do so.
750public czbot_hook_ham(id)
751{
752 if(czbot_hams || !is_user_connected(id)) return;
753
754 // probably a czero bot
755 if(is_user_bot(id) && get_pcvar_num(bot_quota) > 0)
756 {
757 RegisterHamFromEntity(Ham_Spawn,id,"ham_player_spawn",1);
758 RegisterHamFromEntity(Ham_Killed,id,"ham_player_killed_pre",0);
759 RegisterHamFromEntity(Ham_Killed,id,"ham_player_killed_post",1);
760
761 czbot_hams = 1;
762
763 // bug fix for mid-round spawning, thanks to MeRcyLeZZ
764 if(is_user_alive(id)) ham_player_spawn(id);
765 }
766}
767
768// remove a save
769public clear_save(taskid)
770{
771 remove_task(taskid);
772 tempSave[taskid-TASK_CLEAR_SAVE][svAuthid][0] = 0;
773}
774
775// my info... it's changed!
776public client_infochanged(id)
777{
778 if(!ggActive || !is_user_connected(id))
779 return PLUGIN_CONTINUE;
780
781 new oldTeam = lastTeam[id], newTeam = _:cs_get_user_team(id);
782
783 // this means it was caught by logevent_team_join, or wasn't a team change
784 if(oldTeam == newTeam) return PLUGIN_CONTINUE;
785
786 player_teamchange(id,oldTeam,newTeam);
787
788 // invalid team
789 if((newTeam != 1 && newTeam != 2) || !get_pcvar_num(gg_teamplay))
790 return PLUGIN_CONTINUE;
791
792 // something is out of synch
793 if(teamLevel[newTeam] && (level[id] != teamLevel[newTeam] || score[id] != teamScore[newTeam] || !equal(lvlWeapon[id],teamLvlWeapon[newTeam])))
794 {
795 // set them directly
796 level[id] = teamLevel[newTeam];
797 lvlWeapon[id] = teamLvlWeapon[newTeam];
798 score[id] = teamScore[newTeam];
799
800 // gimme mah weapon!
801 if(is_user_alive(id)) give_level_weapon(id);
802 }
803
804 return PLUGIN_CONTINUE;
805}
806
807//**********************************************************************
808// FORWARD HOOKS
809//**********************************************************************
810
811// an entity is given a model, check for silenced/burst status
812public fw_setmodel(ent,model[])
813{
814 if(!ggActive) return FMRES_IGNORED;
815
816 new owner = pev(ent,pev_owner);
817
818 // no owner
819 if(!is_user_connected(owner)) return FMRES_IGNORED;
820
821 static classname[24]; // the extra space is used later
822 pev(ent,pev_classname,classname,10);
823
824 // not a weapon
825 // checks for weaponbox, weapon_shield
826 if(classname[8] != 'x' && !(classname[6] == '_' && classname[7] == 's' && classname[8] == 'h'))
827 return FMRES_IGNORED;
828
829 // makes sure we don't get memory access error,
830 // but also helpful to narrow down matches
831 new len = strlen(model);
832
833 // ignore weaponboxes whose models haven't been set to correspond with their weapon types yet
834 // checks for models/w_weaponbox.mdl
835 if(len == 22 && model[17] == 'x') return FMRES_IGNORED;
836
837 // ignore C4
838 // checks for models/w_backpack.mdl
839 if(len == 21 && model[9] == 'b') return FMRES_IGNORED;
840
841 // checks for models/w_usp.mdl, usp, models/w_m4a1.mdl, m4a1
842 if((len == 16 && model[10] == 's' && lvlWeapon[owner][1] == 's')
843 || (len == 17 && model[10] == '4' && lvlWeapon[owner][1] == '4') )
844 {
845 copyc(model,len-1,model[contain_char(model,'_')+1],'.'); // strips off models/w_ and .mdl
846 formatex(classname,23,"weapon_%s",model);
847
848 // remember silenced status
849 new wEnt = fm_find_ent_by_owner(maxPlayers,classname,ent);
850 if(pev_valid(wEnt)) silenced[owner] = cs_get_weapon_silen(wEnt);
851 }
852
853 // checks for models/w_glock18.mdl, glock18, models/w_famas.mdl, famas
854 else if((len == 20 && model[15] == '8' && lvlWeapon[owner][6] == '8')
855 || (len == 18 && model[9] == 'f' && model[10] == 'a' && lvlWeapon[owner][0] == 'f' && lvlWeapon[owner][1] == 'a') )
856 {
857 copyc(model,len-1,model[contain_char(model,'_')+1],'.'); // strips off models/w_ and .mdl
858 formatex(classname,23,"weapon_%s",model);
859
860 // remember burst status
861 new wEnt = fm_find_ent_by_owner(maxPlayers,classname,ent);
862 if(pev_valid(wEnt)) silenced[owner] = cs_get_weapon_burst(wEnt);
863 }
864
865 // if owner is dead, remove it if we need to
866 if(get_user_health(owner) <= 0 && get_pcvar_num(gg_dm) && !get_pcvar_num(gg_pickup_others))
867 {
868 dllfunc(DLLFunc_Think,ent);
869 return FMRES_SUPERCEDE;
870 }
871
872 return FMRES_IGNORED;
873}
874
875// HELLO HELLo HELlo HEllo Hello hello
876public fw_emitsound(ent,channel,sample[],Float:volume,Float:atten,flags,pitch)
877{
878 if(!ggActive || !is_user_connected(ent) || !get_pcvar_num(gg_dm) || spawnSounds[ent])
879 return FMRES_IGNORED;
880
881 // used to stop spawn sounds in deathmatch
882 return FMRES_SUPERCEDE;
883}
884
885//**********************************************************************
886// EVENT HOOKS
887//**********************************************************************
888
889// our HUD gets reset (obviously)
890public event_resethud(id)
891{
892 if(ggActive && is_user_connected(id))
893 set_task(0.1,"reset_hud",id);
894}
895
896// fix the leader display and hide money
897public reset_hud(id)
898{
899 if(is_user_connected(id))
900 {
901 status_display(id);
902 if(get_pcvar_num(gg_disable_money)) hide_money(id);
903 }
904}
905
906// someone changes weapons
907public event_curweapon(id)
908{
909 if(!ggActive || !is_user_connected(id)) return;
910
911 // keep star speed
912 if(star[id]) fm_set_user_maxspeed(id,fm_get_user_maxspeed(id)*1.5);
913
914 // monitor weapon activity
915 if(afkCheck[id] && is_user_alive(id)) afkCheck[id]++;
916
917 // have at least one bullet in AWP clip
918 if(get_pcvar_num(gg_awp_oneshot) && read_data(2) == CSW_AWP && read_data(3) > 1)
919 {
920 new wEnt = get_weapon_ent(id,CSW_AWP);
921 if(pev_valid(wEnt)) cs_set_weapon_ammo(wEnt,1);
922
923 message_begin(MSG_ONE,gmsgCurWeapon,_,id);
924 write_byte(1); // current?
925 write_byte(CSW_AWP); // weapon
926 write_byte(1); // clip
927 message_end();
928 }
929}
930
931// a new round has begun
932public event_new_round()
933{
934 static armourysHidden = 0;
935
936 roundEnded = 0;
937 roundsElapsed++;
938
939 c4planter = 0;
940 bombStatus[3] = BOMB_PICKEDUP;
941
942 if(gameCommenced && !autovoted)
943 {
944 // don't check mode until vote starts, so map configs have chance to execute
945 if(/*autovote_mode &&*/ roundsElapsed >= get_pcvar_num(gg_autovote_rounds))
946 {
947 autovoted = 1;
948 autovote_mode = -1; // signal to check in autovote_start
949 set_task(get_pcvar_float(gg_autovote_delay),"autovote_start");
950 }
951 }
952
953 // game_player_equip
954 manage_equips();
955
956 if(!ggActive) return;
957
958 // we should probably warmup...
959 // don't ask me where I'm getting this from.
960 if(shouldWarmup)
961 {
962 shouldWarmup = 0;
963 start_warmup();
964 }
965
966 if(warmup <= 0)
967 {
968 new leader = get_leader();
969
970 if(equal(lvlWeapon[leader],HEGRENADE)) play_sound_by_cvar(0,gg_sound_nade);
971 else if(equal(lvlWeapon[leader],KNIFE)) play_sound_by_cvar(0,gg_sound_knife);
972 }
973
974 // reset leader display
975 remove_task(TASK_LEADER_DISPLAY);
976 set_task(0.5,"show_leader_display"); // wait to initialize levels
977
978 new pickup_others = get_pcvar_num(gg_pickup_others);
979 if(!pickup_others /*&& !armourysHidden*/) // they show up again on new round
980 {
981 set_task(0.1,"hide_armory_entitys");
982 armourysHidden = 1;
983 }
984 else if(pickup_others && armourysHidden)
985 {
986 set_task(0.1,"show_armory_entitys");
987 armourysHidden = 0;
988 }
989
990 // block hostages
991 if(hostageMap)
992 {
993 // block hostages
994 if(get_pcvar_num(gg_block_objectives))
995 set_task(0.1,"move_hostages");
996 else
997 {
998 // reset hostage info
999 new i;
1000 for(i=0;i<33;i++)
1001 {
1002 hosties[i][0] = 0;
1003 hosties[i][1] = 0;
1004 }
1005 }
1006 }
1007
1008 // start in random positions at round start
1009 if(get_pcvar_num(gg_dm) && get_pcvar_num(gg_dm_start_random))
1010 set_task(0.1,"randomly_place_everyone");
1011}
1012
1013// hide the armoury_entity's so players cannot pick them up
1014public hide_armory_entitys()
1015{
1016 new ent = maxPlayers;
1017 while((ent = fm_find_ent_by_class(ent,"armoury_entity")))
1018 {
1019 set_pev(ent,pev_solid,SOLID_NOT);
1020 fm_set_entity_visibility(ent,0);
1021 }
1022}
1023
1024// reveal the armoury_entity's so players CAN pick them up
1025public show_armory_entitys()
1026{
1027 new ent = maxPlayers;
1028 while((ent = fm_find_ent_by_class(ent,"armoury_entity")))
1029 {
1030 set_pev(ent,pev_solid,SOLID_TRIGGER);
1031 fm_set_entity_visibility(ent,1);
1032 }
1033}
1034
1035// move the hostages so that CTs can't get to them
1036public move_hostages()
1037{
1038 new ent = maxPlayers;
1039 while((ent = fm_find_ent_by_class(ent,"hostage_entity")))
1040 set_pev(ent,pev_origin,Float:{8192.0,8192.0,8192.0});
1041}
1042
1043// round is restarting (TAG: sv_restartround)
1044public event_round_restart()
1045{
1046 // re-entrancy fix
1047 static Float:lastThis;
1048 new Float:now = get_gametime();
1049 if(now == lastThis) return;
1050 lastThis = now;
1051
1052 static message[17];
1053 read_data(2,message,16);
1054
1055 if(equal(message,"#Game_Commencing"))
1056 {
1057 // don't reset values on game commencing,
1058 // if it has already commenced once
1059 if(gameCommenced) return;
1060 gameCommenced = 1;
1061
1062 // start warmup
1063 if(ggActive)
1064 {
1065 clear_all_values();
1066
1067 shouldWarmup = 0;
1068 start_warmup();
1069
1070 return;
1071 }
1072 }
1073 /*else if(ggActive) // #Game_will_restart_in
1074 {
1075 read_data(3,message,4); // time to restart in
1076 new Float:time = floatstr(message) - 0.1;
1077 set_task((time < 0.1) ? 0.1 : time,"clear_all_values");
1078 }*/
1079}
1080
1081// a delayed clearing
1082public clear_all_values()
1083{
1084 new player;
1085 for(player=1;player<=maxPlayers;player++)
1086 {
1087 if(is_user_connected(player)) clear_values(player,1);
1088 }
1089
1090 clear_team_values(1);
1091 clear_team_values(2);
1092}
1093
1094// the bomb explodes
1095public event_bomb_detonation()
1096{
1097 if(!ggActive || get_pcvar_num(gg_bomb_defuse_lvl) != 2 || !c4planter)
1098 return;
1099
1100 // re-entrancy fix
1101 static Float:lastThis;
1102 new Float:now = get_gametime();
1103 if(now == lastThis) return;
1104 lastThis = now;
1105
1106 new id = c4planter;
1107 c4planter = 0;
1108
1109 if(!is_user_connected(id)) return;
1110
1111 if(!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE) && level[id] < weaponNum)
1112 {
1113 change_level(id,1);
1114 //score[id] = 0;
1115 }
1116 else if(is_user_alive(id)) refill_ammo(id);
1117}
1118
1119// ammo amount changes
1120public event_ammox(id)
1121{
1122 new type = read_data(1);
1123
1124 // not HE grenade ammo, or not on the grenade level
1125 if(type != 12 || !equal(lvlWeapon[id],HEGRENADE)) return;
1126
1127 new amount = read_data(2);
1128
1129 // still have some left, ignore
1130 if(amount > 0)
1131 {
1132 remove_task(TASK_REFRESH_NADE+id);
1133 return;
1134 }
1135
1136 new Float:refresh = get_pcvar_float(gg_nade_refresh);
1137
1138 // refreshing is disabled, or we are already giving one out
1139 if(refresh <= 0.0 || task_exists(TASK_REFRESH_NADE+id)) return;
1140
1141 // start the timer for the new grenade
1142 set_task(refresh,"refresh_nade",TASK_REFRESH_NADE+id);
1143}
1144
1145// map is changing
1146public event_intermission()
1147{
1148 if(!ggActive) return;
1149
1150 if(won)
1151 {
1152 if(set_nextmap()) set_task(1.0,"goto_nextmap");
1153 return;
1154 }
1155
1156 new player, found;
1157 for(player=1;player<=maxPlayers;player++)
1158 {
1159 if(is_user_connected(player) && on_valid_team(player))
1160 {
1161 found = 1;
1162 break;
1163 }
1164 }
1165
1166 // did not find any players on a valid team, game over man
1167 if(!found)
1168 {
1169 if(set_nextmap()) set_task(1.0,"goto_nextmap");
1170 return;
1171 }
1172
1173 // teamplay, easier to decide
1174 if(get_pcvar_num(gg_teamplay))
1175 {
1176 new winner;
1177
1178 // clear winner
1179 if(teamLevel[1] > teamLevel[2]) winner = 1;
1180 else if(teamLevel[2] > teamLevel[1]) winner = 2;
1181 else
1182 {
1183 // tied for level, check score
1184 if(teamScore[1] > teamScore[2]) winner = 1;
1185 else if(teamScore[2] > teamScore[1]) winner = 2;
1186 else
1187 {
1188 // tied for level and score, pick random
1189 winner = random_num(1,2);
1190 }
1191 }
1192
1193 // grab a player from the winning and losing teams
1194 new plWinner, plLoser, team;
1195 for(player=1;player<=maxPlayers;player++)
1196 {
1197 if(is_user_connected(player) && on_valid_team(player))
1198 {
1199 team = _:cs_get_user_team(player);
1200
1201 if(!plWinner && team == winner) plWinner = player;
1202 else if(!plLoser && team != winner) plLoser = player;
1203
1204 if(plWinner && plLoser) break;
1205 }
1206 }
1207
1208 win(plWinner,plLoser);
1209 if(set_nextmap()) set_task(1.0,"goto_nextmap");
1210
1211 return;
1212 }
1213
1214 // grab highest level
1215 new leaderLevel;
1216 get_leader(leaderLevel);
1217
1218 // grab player list
1219 new players[32], pNum, winner, i;
1220 get_players(players,pNum);
1221
1222 // no one here
1223 if(pNum <= 0)
1224 {
1225 if(set_nextmap()) set_task(1.0,"goto_nextmap");
1226 return;
1227 }
1228
1229 new topLevel[32], tlNum;
1230
1231 // get all of the highest level players
1232 for(i=0;i<pNum;i++)
1233 {
1234 player = players[i];
1235
1236 if(level[player] == leaderLevel)
1237 topLevel[tlNum++] = player;
1238 }
1239
1240 // only one on top level
1241 if(tlNum == 1) winner = topLevel[0];
1242 else
1243 {
1244 new highestKills, frags;
1245
1246 // get the most kills
1247 for(i=0;i<tlNum;i++)
1248 {
1249 frags = get_user_frags(topLevel[i]);
1250
1251 if(frags >= highestKills)
1252 highestKills = frags;
1253 }
1254
1255 new topKillers[32], tkNum;
1256
1257 // get all of the players with highest kills
1258 for(i=0;i<tlNum;i++)
1259 {
1260 if(get_user_frags(topLevel[i]) == highestKills)
1261 topKillers[tkNum++] = topLevel[i];
1262 }
1263
1264 // only one on top kills
1265 if(tkNum == 1) winner = topKillers[0];
1266 else
1267 {
1268 new leastDeaths, deaths;
1269
1270 // get the least deaths
1271 for(i=0;i<tkNum;i++)
1272 {
1273 deaths = cs_get_user_deaths(topKillers[i]);
1274 if(deaths <= leastDeaths) leastDeaths = deaths;
1275 }
1276
1277 new leastDead[32], ldNum;
1278
1279 // get all of the players with lowest deaths
1280 for(i=0;i<tkNum;i++)
1281 {
1282 if(cs_get_user_deaths(topKillers[i]) == leastDeaths)
1283 leastDead[ldNum++] = topKillers[i];
1284 }
1285
1286 leastDead[random_num(0,ldNum-1)];
1287 }
1288 }
1289
1290 // crown them
1291 win(winner,0);
1292
1293 // go to next map in cycle
1294 if(set_nextmap()) set_task(1.0,"goto_nextmap");
1295}
1296
1297//**********************************************************************
1298// MESSAGE HOOKS
1299//**********************************************************************
1300
1301// bomb is dropped, remember for DM
1302public message_bombdrop(msg_id,msg_dest,msg_entity)
1303{
1304 if(ggActive && get_pcvar_num(gg_block_objectives))
1305 return PLUGIN_HANDLED;
1306
1307 // you can't simply get_msg_arg_int the coords
1308 bombStatus[0] = floatround(get_msg_arg_float(1));
1309 bombStatus[1] = floatround(get_msg_arg_float(2));
1310 bombStatus[2] = floatround(get_msg_arg_float(3));
1311 bombStatus[3] = get_msg_arg_int(4);
1312
1313 return PLUGIN_CONTINUE;
1314}
1315
1316// bomb is picked up, remember for DM
1317public message_bombpickup(msg_id,msg_dest,msg_entity)
1318{
1319 bombStatus[3] = BOMB_PICKEDUP;
1320 return PLUGIN_CONTINUE;
1321}
1322
1323// scenario changes
1324public message_scenario(msg_id,msg_dest,msg_entity)
1325{
1326 // disabled
1327 if(!ggActive) return PLUGIN_CONTINUE;
1328
1329 // don't override our custom display, if we have one
1330 if(get_pcvar_num(gg_status_display))
1331 return PLUGIN_HANDLED;
1332
1333 // block hostage display if we disabled objectives
1334 else if(get_msg_args() > 1 && get_pcvar_num(gg_block_objectives))
1335 {
1336 new sprite[8];
1337 get_msg_arg_string(2,sprite,7);
1338
1339 if(equal(sprite,"hostage"))
1340 return PLUGIN_HANDLED;
1341 }
1342
1343 return PLUGIN_CONTINUE;
1344}
1345
1346// remove c4 if we disabled objectives
1347public message_weappickup(msg_id,msg_dest,msg_entity)
1348{
1349 if(!bombMap || !ggActive || !get_pcvar_num(gg_block_objectives))
1350 return PLUGIN_CONTINUE;
1351
1352 if(get_msg_arg_int(1) == CSW_C4)
1353 {
1354 set_task(0.1,"strip_c4",msg_entity);
1355 return PLUGIN_HANDLED;
1356 }
1357
1358 return PLUGIN_CONTINUE;
1359}
1360
1361// delay, since weappickup is slightly before we actually get the weapon
1362public strip_c4(id)
1363{
1364 if(!is_user_connected(id)) return;
1365
1366 ham_strip_weapon(id,"weapon_c4");
1367
1368 // remove it from HUD
1369 message_begin(MSG_ONE,gmsgStatusIcon,_,id);
1370 write_byte(0);
1371 write_string("c4");
1372 message_end();
1373}
1374
1375// block c4 ammo message if we disabled objectives
1376public message_ammopickup(msg_id,msg_dest,msg_entity)
1377{
1378 if(!bombMap || !ggActive || !get_pcvar_num(gg_block_objectives))
1379 return PLUGIN_CONTINUE;
1380
1381 if(get_msg_arg_int(1) == 14) // C4
1382 return PLUGIN_HANDLED;
1383
1384 return PLUGIN_CONTINUE;
1385}
1386
1387// block dropped the bomb message if we disabled objectives
1388public message_textmsg(msg_id,msg_dest,msg_entity)
1389{
1390 if(!bombMap || !ggActive || !get_pcvar_num(gg_block_objectives))
1391 return PLUGIN_CONTINUE;
1392
1393 static message[16];
1394 get_msg_arg_string(2,message,15);
1395
1396 if(equal(message,"#Game_bomb_drop"))
1397 return PLUGIN_HANDLED;
1398
1399 return PLUGIN_CONTINUE;
1400}
1401
1402// block hostages from appearing on radar if we disabled objectives
1403public message_hostagepos(msg_id,msg_dest,msg_entity)
1404{
1405 if(!ggActive || !get_pcvar_num(gg_block_objectives))
1406 return PLUGIN_CONTINUE;
1407
1408 return PLUGIN_HANDLED;
1409}
1410
1411// a corpse is to be set, stop player shells bug (thanks sawce)
1412public message_clcorpse(msg_id,msg_dest,msg_entity)
1413{
1414 if(!ggActive || get_msg_args() < 12)
1415 return PLUGIN_CONTINUE;
1416
1417 if(get_pcvar_num(gg_dm) && !get_pcvar_num(gg_dm_corpses))
1418 return PLUGIN_HANDLED;
1419
1420 return PLUGIN_CONTINUE;
1421}
1422
1423// money money money!
1424public message_money(msg_id,msg_dest,msg_entity)
1425{
1426 if(!ggActive || !is_user_connected(msg_entity) || !is_user_alive(msg_entity) || !get_pcvar_num(gg_disable_money))
1427 return PLUGIN_CONTINUE;
1428
1429 // this now just changes the value of the message, passes it along,
1430 // and then modifies the pdata, instead of calling another cs_set_user_money
1431 // and sending out more messages than needed.
1432
1433 set_msg_arg_int(1,ARG_LONG,0); // money
1434 set_msg_arg_int(2,ARG_BYTE,0); // flash
1435
1436 set_pdata_int(msg_entity,OFFSET_CSMONEY,0,OFFSET_LINUX);
1437 return PLUGIN_CONTINUE;
1438}
1439
1440//**********************************************************************
1441// LOG EVENT HOOKS
1442//**********************************************************************
1443
1444// someone planted the bomb
1445public logevent_bomb_planted()
1446{
1447 if(!ggActive || !get_pcvar_num(gg_bomb_defuse_lvl) || roundEnded)
1448 return;
1449
1450 new id = get_loguser_index();
1451 if(!is_user_connected(id)) return;
1452
1453 if(get_pcvar_num(gg_bomb_defuse_lvl) == 2) c4planter = id;
1454 else
1455 {
1456 if(!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE) && level[id] < weaponNum)
1457 {
1458 change_level(id,1);
1459 }
1460 else refill_ammo(id);
1461 }
1462
1463}
1464
1465// someone defused the bomb
1466public logevent_bomb_defused()
1467{
1468 if(!ggActive || !get_pcvar_num(gg_bomb_defuse_lvl))
1469 return;
1470
1471 new id = get_loguser_index();
1472 if(!is_user_connected(id)) return;
1473
1474 if(!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE) && level[id] < weaponNum)
1475 {
1476 change_level(id,1);
1477 }
1478 else refill_ammo(id);
1479}
1480
1481// the round ends
1482public logevent_round_end()
1483{
1484 roundEnded = 1;
1485}
1486
1487// hostage is touched
1488public logevent_hostage_touched()
1489{
1490 new reward = get_pcvar_num(gg_host_touch_reward);
1491
1492 if(!ggActive || !reward || roundEnded)
1493 return;
1494
1495 new id = get_loguser_index();
1496 if(!is_user_connected(id) || hosties[id][0] == -1) return;
1497
1498 hosties[id][0]++;
1499
1500 if(hosties[id][0] >= reward)
1501 {
1502 if((!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE) && level[id] < weaponNum)
1503 || score[id] + 1 < get_level_goal(level[id],id))
1504 {
1505 // didn't level off of it
1506 if(!change_score(id,1)) show_required_kills(id);
1507 }
1508 else refill_ammo(id);
1509
1510 hosties[id][0] = -1;
1511
1512 if(get_pcvar_num(gg_teamplay))
1513 {
1514 new CsTeams:team = cs_get_user_team(id), i;
1515 for(i=1;i<=maxPlayers;i++)
1516 {
1517 // one per team
1518 if(is_user_connected(i) && cs_get_user_team(i) == team)
1519 hosties[i][0] = -1;
1520 }
1521 }
1522 }
1523}
1524
1525// hostage is rescued
1526public logevent_hostage_rescued()
1527{
1528 new reward = get_pcvar_num(gg_host_rescue_reward);
1529
1530 if(!ggActive || !reward || roundEnded)
1531 return;
1532
1533 new id = get_loguser_index();
1534 if(!is_user_connected(id) || hosties[id][1] == -1) return;
1535
1536 hosties[id][1]++;
1537
1538 if(hosties[id][1] >= reward)
1539 {
1540 if(!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE) && level[id] < weaponNum)
1541 change_level(id,1);
1542 else
1543 refill_ammo(id);
1544
1545 hosties[id][1] = -1;
1546
1547 if(get_pcvar_num(gg_teamplay))
1548 {
1549 new CsTeams:team = cs_get_user_team(id), i;
1550 for(i=1;i<=maxPlayers;i++)
1551 {
1552 // one per team
1553 if(is_user_connected(i) && cs_get_user_team(i) == team)
1554 hosties[i][1] = -1;
1555 }
1556 }
1557 }
1558}
1559
1560// hostage is killed
1561public logevent_hostage_killed()
1562{
1563 new penalty = get_pcvar_num(gg_host_kill_penalty);
1564
1565 if(!ggActive || !penalty)
1566 return;
1567
1568 new id = get_loguser_index();
1569 if(!is_user_connected(id)) return;
1570
1571 new teamplay = get_pcvar_num(gg_teamplay), name[32];
1572
1573 if(teamplay) get_user_team(id,name,9);
1574 else get_user_name(id,name,31);
1575
1576 if(score[id] - penalty < 0)
1577 gungame_print(0,id,1,"%L",LANG_PLAYER_C,(teamplay) ? "HK_LEVEL_DOWN_TEAM" : "HK_LEVEL_DOWN",name,(level[id] > 1) ? level[id]-1 : level[id]);
1578 else
1579 gungame_print(0,id,1,"%L",LANG_PLAYER_C,(teamplay) ? "HK_SCORE_DOWN_TEAM" : "HK_SCORE_DOWN",name,penalty);
1580
1581 change_score(id,-penalty);
1582}
1583
1584// someone joins a team
1585public logevent_team_join()
1586{
1587 if(!ggActive) return;
1588
1589 new id = get_loguser_index();
1590 if(!is_user_connected(id)) return;
1591
1592 new oldTeam = get_user_team(id), newTeam = _:cs_get_user_team(id);
1593 player_teamchange(id,oldTeam,newTeam);
1594
1595 // teamplay, team switch allowed
1596 if(get_pcvar_num(gg_teamplay))
1597 {
1598 remove_task(TASK_DELAYED_SUICIDE+id);
1599
1600 // I was the one who planted the bomb
1601 if(c4planter == id)
1602 {
1603 // clear in case we don't find anyone
1604 c4planter = 0;
1605
1606 new player;
1607 for(player=1;player<=maxPlayers;player++)
1608 {
1609 if(player != id && is_user_connected(player) && cs_get_user_team(player) == CS_TEAM_T)
1610 {
1611 // assign it to someone else so terrorists get points
1612 c4planter = player;
1613 break;
1614 }
1615 }
1616 }
1617
1618 return;
1619 }
1620
1621 // no (valid) previous team or didn't switch teams, ignore (suicide)
1622 if(oldTeam < 1 || oldTeam > 2 || newTeam < 1 || newTeam > 2 || oldTeam == newTeam)
1623 return;
1624
1625 // check to see if the team change was beneficial
1626 if(get_pcvar_num(gg_allow_changeteam) == 2)
1627 {
1628 new teamCount[2], i;
1629 for(i=1;i<=maxPlayers;i++)
1630 {
1631 if(!is_user_connected(i))
1632 continue;
1633
1634 switch(cs_get_user_team(i))
1635 {
1636 case CS_TEAM_T: teamCount[0]++;
1637 case CS_TEAM_CT: teamCount[1]++;
1638 }
1639 }
1640
1641 if(teamCount[newTeam-1] <= teamCount[oldTeam-1])
1642 remove_task(TASK_DELAYED_SUICIDE+id);
1643 }
1644 else remove_task(TASK_DELAYED_SUICIDE+id);
1645}
1646
1647//**********************************************************************
1648// HAM HOOKS
1649//**********************************************************************
1650
1651// a player respawned
1652public ham_player_spawn(id)
1653{
1654 if(ggActive && is_user_alive(id) && cs_get_user_team(id)) // do team check here for bots
1655 spawned(id);
1656
1657 return HAM_IGNORED;
1658}
1659
1660// what do you think happened here?
1661public ham_player_killed_pre(victim,killer,gib)
1662{
1663 if(!ggActive || won || !is_user_connected(victim)) return HAM_IGNORED;
1664
1665 // stops defusal kits from dropping in deathmatch mode
1666 if(bombMap && get_pcvar_num(gg_dm)) cs_set_user_defuse(victim,0);
1667
1668 // remember victim's silenced status
1669 if(equal(lvlWeapon[victim],"usp") || equal(lvlWeapon[victim],"m4a1"))
1670 {
1671 new wEnt = get_weapon_ent(victim,_,lvlWeapon[victim]);
1672 if(pev_valid(wEnt)) silenced[victim] = cs_get_weapon_silen(wEnt);
1673 }
1674
1675 // or, remember burst status
1676 else if(equal(lvlWeapon[victim],"glock18") || equal(lvlWeapon[victim],"famas"))
1677 {
1678 new wEnt = get_weapon_ent(victim,_,lvlWeapon[victim]);
1679 if(pev_valid(wEnt)) silenced[victim] = cs_get_weapon_burst(wEnt);
1680 }
1681
1682 // some sort of death that we don't want to count
1683 if(killer == victim || !is_user_connected(killer) || cs_get_user_team(killer) == cs_get_user_team(victim))
1684 return HAM_IGNORED;
1685
1686 // award for killing hostage carrier
1687 new host_kill_reward = get_pcvar_num(gg_host_kill_reward);
1688
1689 // note that this doesn't work with CZ hostages
1690 if(hostageMap && !czero && host_kill_reward && !equal(lvlWeapon[killer],HEGRENADE) && !equal(lvlWeapon[killer],KNIFE))
1691 {
1692 // check for hostages following this player
1693 new hostage = maxPlayers;
1694 while((hostage = fm_find_ent_by_class(hostage,"hostage_entity")))
1695 {
1696 if(cs_get_hostage_foll(hostage) == victim && pev(hostage,pev_deadflag) == DEAD_NO)
1697 break;
1698 }
1699
1700 // award bonus score if victim had hostages
1701 if(hostage)
1702 {
1703 if(!equal(lvlWeapon[killer],HEGRENADE) && !equal(lvlWeapon[killer],KNIFE) && level[killer] < weaponNum)
1704 {
1705 // didn't level off of it
1706 if(!change_score(killer,host_kill_reward) || score[killer])
1707 show_required_kills(killer);
1708 }
1709 }
1710 }
1711
1712 return HAM_IGNORED;
1713}
1714
1715// it's just that easy (multiplay_gamerules.cpp, ln 709)
1716public ham_player_killed_post(victim,killer,gib)
1717{
1718 if(!ggActive || won) return HAM_IGNORED;
1719
1720 // log in bounds
1721 if(killer > 0 && killer < 33 && victim > 0 && victim < 33)
1722 lastKilled[killer] = victim;
1723
1724 if(!is_user_connected(victim)) return HAM_IGNORED;
1725
1726 // moved the below from killed_pre to killed_post because sometimes user is still alive in pre.
1727 // don't know why this wasn't here before, but just in case I need to change it back... (thanks addam)
1728
1729 // allow us to join in on deathmatch
1730 if(!get_pcvar_num(gg_dm))
1731 {
1732 remove_task(TASK_CHECK_DEATHMATCH+victim);
1733 set_task(10.0,"check_deathmatch",TASK_CHECK_DEATHMATCH+victim);
1734 }
1735
1736 // respawn us
1737 else
1738 {
1739 remove_task(TASK_RESPAWN+victim);
1740 remove_task(TASK_REMOVE_PROTECTION+victim);
1741 begin_respawn(victim);
1742 fm_set_user_rendering(victim); // clear spawn protection
1743 }
1744
1745 remove_task(TASK_VERIFY_WEAPON+victim);
1746
1747 star[victim] = 0;
1748 remove_task(TASK_END_STAR+victim);
1749
1750 static wpnName[24];
1751 get_killer_weapon(killer,pev(victim,pev_dmg_inflictor),wpnName,23);
1752
1753 // grenade death
1754 if(equal(wpnName,"grenade"))
1755 {
1756 new inflictor = pev(victim,pev_dmg_inflictor);
1757
1758 if(pev_valid(inflictor))
1759 {
1760 new Float:dmgtime;
1761 pev(inflictor,pev_dmgtime,dmgtime);
1762
1763 // a C4 kill will be reported as hegrenade. however, C4 has no
1764 // pev_dmgtime, while a real hegrenade does. so distinguish between hegrenade
1765 // and C4, and ignore C4 kills. also note that we can't compare models,
1766 // because at this stage both an hegrenade and C4 have no model.
1767 if(!dmgtime)
1768 {
1769 afkCheck[victim] = 0;
1770 return HAM_IGNORED;
1771 }
1772 }
1773
1774 // fix name
1775 formatex(wpnName,23,HEGRENADE);
1776 }
1777
1778 // killed self with world
1779 if(killer == victim && equal(wpnName,"world") && is_user_connected(killer))
1780 {
1781 // this might be a valid team switch, wait it out
1782 if(!roundEnded && get_pcvar_num(gg_allow_changeteam))
1783 {
1784 set_task(0.1,"delayed_suicide",TASK_DELAYED_SUICIDE+victim);
1785 afkCheck[victim] = 0;
1786
1787 return HAM_IGNORED; // in the meantime, don't penalize the suicide
1788 }
1789
1790 player_suicided(killer);
1791 afkCheck[victim] = 0;
1792
1793 return HAM_IGNORED;
1794 }
1795
1796 // afk checker
1797 if(afkCheck[victim] && afkCheck[victim] < 3) // 0 = no afk check, 3+ = they did something with a weapon (it is set to 1, and it goes to 2 when they get their new weapon)
1798 {
1799 new Float:origin[3], Float:angles[3], afk;
1800 pev(victim,pev_origin,origin);
1801 pev(victim,pev_v_angle,angles);
1802
1803 if(get_pcvar_num(gg_afk_protection) == 2)
1804 {
1805 // this mode requires that your origin and angles be exactly as they were when you spawned,
1806 // but it ignores the z-component because often players spawn a few units above ground
1807 afk = (origin[0] == spawnOrigin[victim][0]) && (origin[1] == spawnOrigin[victim][1]) && (angles[0] == spawnAngles[victim][0]) && (angles[1] == spawnAngles[victim][1]) && (angles[2] == spawnAngles[victim][2]);
1808 }
1809 else
1810 {
1811 // this mode allows a slight XY shift due to pushback from getting shot by certain weapons,
1812 // and also ignores the Y-component of the angle, because it sometimes inexplicably doesn't match up
1813 origin[2] = spawnOrigin[victim][2]; // ignore Z-component, they fall
1814 afk = (vector_distance(origin,spawnOrigin[victim]) < 28.0) && (angles[0] == spawnAngles[victim][0]) && (angles[2] == spawnAngles[victim][2]);
1815 }
1816
1817 if(afk)
1818 {
1819 new name[32];
1820 get_user_name(victim,name,31);
1821
1822 gungame_print(killer,victim,1,"%L",killer,"AFK_KILL",name);
1823 afkCheck[victim] = 0;
1824
1825 return HAM_IGNORED;
1826 }
1827 }
1828
1829 afkCheck[victim] = 0;
1830
1831 // other player had spawn protection
1832 if(spawnProtected[victim])
1833 {
1834 new name[32];
1835 get_user_name(victim,name,31);
1836 gungame_print(killer,victim,1,"%L",killer,"SPAWNPROTECTED_KILL",name,floatround(get_pcvar_float(gg_dm_sp_time)));
1837
1838 spawnProtected[victim] = 0;
1839 return HAM_IGNORED;
1840 }
1841
1842 // killed self with worldspawn (fall damage usually)
1843 if(equal(wpnName,"worldspawn"))
1844 {
1845 if(get_pcvar_num(gg_worldspawn_suicide)) player_suicided(victim);
1846 return HAM_IGNORED;
1847 }
1848
1849 // killed self not with worldspawn
1850 if(!killer || killer == victim)
1851 {
1852 player_suicided(victim);
1853 return HAM_IGNORED;
1854 }
1855
1856 // a non-player entity killed this man!
1857 if(!is_user_connected(killer))
1858 {
1859 // not linked so return is hit either way
1860 if(pev_valid(killer))
1861 {
1862 static classname[14];
1863 pev(killer,pev_classname,classname,13);
1864
1865 // killed by a trigger_hurt, count as suicide
1866 if(equal(classname,"trigger_hurt"))
1867 player_suicided(victim);
1868 }
1869
1870 return HAM_IGNORED;
1871 }
1872
1873 new teamplay = get_pcvar_num(gg_teamplay), penalty = get_pcvar_num(gg_tk_penalty);
1874
1875 // team kill
1876 if(is_user_connected(victim) && cs_get_user_team(killer) == cs_get_user_team(victim) && penalty >= 0)
1877 {
1878 if(penalty > 0)
1879 {
1880 new name[32];
1881 if(teamplay) get_user_team(killer,name,9);
1882 else get_user_name(killer,name,31);
1883
1884 if(warmup <= 0 || !warmupWeapon[0])
1885 {
1886 if(score[killer] - penalty < 0)
1887 gungame_print(0,killer,1,"%L",LANG_PLAYER_C,(teamplay) ? "TK_LEVEL_DOWN_TEAM" : "TK_LEVEL_DOWN",name,(level[killer] > 1) ? level[killer]-1 : level[killer]);
1888 else
1889 gungame_print(0,killer,1,"%L",LANG_PLAYER_C,(teamplay) ? "TK_SCORE_DOWN_TEAM" : "TK_SCORE_DOWN",name,penalty);
1890 }
1891
1892 change_score(killer,-penalty);
1893 }
1894
1895 return HAM_IGNORED;
1896 }
1897
1898 new canLevel = 1, scored;
1899
1900 // already reached max levels this round
1901 new max_lvl = get_pcvar_num(gg_max_lvl);
1902 if(!teamplay && !get_pcvar_num(gg_turbo) && max_lvl > 0 && levelsThisRound[killer] >= max_lvl) canLevel = 0;
1903
1904 new nade = equal(lvlWeapon[killer],HEGRENADE), knife_pro = get_pcvar_num(gg_knife_pro),
1905 victimIsBot = is_user_bot(victim), knifeLevel = equal(lvlWeapon[killer],KNIFE), bots_knifeable = get_pcvar_num(gg_bots_knifeable);
1906
1907 // knife_pro:
1908 // 0 - nothing
1909 // 1 - killer +1 level, victim -1 level
1910 // 2 - killer +1 level, victim stays
1911 // 3 - killer +1 point, victim -1 level
1912
1913 // was it a melee kill, and does it matter?
1914 if( !(victimIsBot && !bots_knifeable) && !knifeLevel && knife_pro && equal(wpnName,KNIFE))
1915 {
1916 static killerName[32], victimName[32], authid[24], teamName[10];
1917 get_user_name(killer,killerName,31);
1918 get_user_name(victim,victimName,31);
1919 get_user_authid(killer,authid,23);
1920 get_user_team(killer,teamName,9);
1921
1922 log_message("^"%s<%i><%s><%s>^" triggered ^"Stole_Level^"",killerName,get_user_userid(killer),authid,teamName);
1923
1924 new tpGainPoints, tpLosePoints, tpOverride;
1925 if(teamplay)
1926 {
1927 tpGainPoints = (knife_pro == 3) ? 1 : get_level_goal(level[killer],0);
1928 tpLosePoints = (knife_pro == 2) ? 0 : get_level_goal(level[victim],0);
1929
1930 if(warmup <= 0 || !warmupWeapon[0]) gungame_print(0,killer,1,"%L",LANG_PLAYER_C,"STOLE_LEVEL_TEAM",killerName,tpLosePoints,victimName,tpGainPoints);
1931
1932 // allow points awarded on nade or final level if it won't level us
1933 tpOverride = (score[killer] + tpGainPoints < get_level_goal(level[killer],killer));
1934 }
1935 else // solo play
1936 {
1937 if(warmup <= 0 || !warmupWeapon[0]) gungame_print(0,killer,1,"%L",LANG_PLAYER_C,"STOLE_LEVEL",killerName,victimName); // not a knife warmup
1938 if(nade && knife_pro == 3 && score[killer] + 1 < get_level_goal(level[killer],killer)) tpOverride = 1; // if I'm just getting 1 point and it won't level me, it's okay for the nade level
1939 }
1940
1941 if(tpOverride || (canLevel && !nade))
1942 {
1943 if(tpOverride || level[killer] < weaponNum)
1944 {
1945 if(teamplay)
1946 {
1947 if(!change_score(killer,tpGainPoints,_,0)) show_required_kills(killer); // don't play sounds
1948 }
1949 else
1950 {
1951 if(knife_pro == 3)
1952 {
1953 if(!change_score(killer,1,_,0)) show_required_kills(killer); // don't play sounds
1954 }
1955 else
1956 {
1957 change_level(killer,1,_,_,_,0); // don't play sounds
1958 }
1959 }
1960 }
1961 }
1962
1963 play_sound_by_cvar(killer,gg_sound_levelsteal); // use this one instead!
1964
1965 // knife pro 2 = victim doesn't lose a level
1966 if(knife_pro != 2 && (level[victim] > 1 || teamplay))
1967 {
1968 if(teamplay) change_score(victim,-tpLosePoints);
1969 else change_level(victim,-1);
1970 }
1971 }
1972
1973 // otherwise, if he killed with his appropiate weapon, give him a point
1974 else if( !(victimIsBot && !bots_knifeable && knifeLevel) && canLevel && equal(lvlWeapon[killer],wpnName))
1975 {
1976 scored = 1;
1977
1978 // didn't level off of it
1979 if(!change_score(killer,1)) show_required_kills(killer);
1980 }
1981
1982 // refresh grenades
1983 if(nade && get_pcvar_num(gg_extra_nades))
1984 {
1985 remove_task(TASK_REFRESH_NADE+killer);
1986
1987 // instant refresh, and refresh_nade makes sure we don't already have a nade
1988 refresh_nade(TASK_REFRESH_NADE+killer);
1989 }
1990
1991 if((!scored || !get_pcvar_num(gg_turbo)) && get_pcvar_num(gg_refill_on_kill))
1992 refill_ammo(killer,1);
1993
1994 return HAM_IGNORED;
1995}
1996
1997// a player is touching a weaponbox or armoury_entity, possibly disallow
1998public ham_weapon_touch(weapon,other)
1999{
2000 // gungame off and non-player or dead-player
2001 if(!ggActive || !is_user_alive(other)) return HAM_IGNORED;
2002
2003 new knife_elite = get_pcvar_num(gg_knife_elite);
2004
2005 // pickup others enabled, and no conflict with knife elite, stop here
2006 if(get_pcvar_num(gg_pickup_others) && (!knife_elite || !levelsThisRound[other])) return HAM_IGNORED;
2007
2008 static model[24];
2009 pev(weapon,pev_model,model,23);
2010
2011 // strips off models/w_ and .mdl
2012 copyc(model,23,model[contain_char(model,'_')+1],'.');
2013
2014 // weaponbox model is no good, but C4 is okay
2015 // checks for weaponbox, backpack
2016 if(model[8] == 'x' || model[0] == 'b') return HAM_IGNORED;
2017
2018 // now that we allowed C4, check for knife elite again
2019 if(knife_elite && levelsThisRound[other]) return HAM_SUPERCEDE;
2020
2021 // weapon is weapon_mp5navy, but model is w_mp5.mdl
2022 // checks for mp5
2023 if(model[1] == 'p' && model[2] == '5') model = "mp5navy";
2024
2025 // check hegrenade exceptions
2026 // checks for hegrenade
2027 if(lvlWeapon[other][0] == 'h')
2028 {
2029 // checks for glock18, smokegrenade, flashbang
2030 if((model[6] == '8' && get_pcvar_num(gg_nade_glock))
2031 || (model[0] == 's' && model[1] == 'm' && get_pcvar_num(gg_nade_smoke))
2032 || (model[0] == 'f' && model[1] == 'l' && get_pcvar_num(gg_nade_flash)))
2033 return HAM_IGNORED;
2034 }
2035
2036 // this is our weapon, don't mess with it
2037 if(equal(lvlWeapon[other],model)) return HAM_IGNORED;
2038
2039 return HAM_SUPERCEDE;
2040}
2041
2042//**********************************************************************
2043// COMMAND HOOKS
2044//**********************************************************************
2045
2046// turning GunGame on or off
2047public cmd_gungame(id,level,cid)
2048{
2049 // no access, or GunGame ending anyway
2050 if(!cmd_access(id,level,cid,2) || won)
2051 return PLUGIN_HANDLED;
2052
2053 // already working on toggling GunGame
2054 if(task_exists(TASK_TOGGLE_GUNGAME + TOGGLE_FORCE)
2055 || task_exists(TASK_TOGGLE_GUNGAME + TOGGLE_DISABLE)
2056 || task_exists(TASK_TOGGLE_GUNGAME + TOGGLE_ENABLE))
2057 {
2058 console_print(id,"[GunGame] GunGame is already being turned on or off");
2059 return PLUGIN_HANDLED;
2060 }
2061
2062 new arg[32], oldStatus = ggActive, newStatus;
2063 read_argv(1,arg,31);
2064
2065 if(equali(arg,"on") || str_to_num(arg))
2066 newStatus = 1;
2067
2068 // no change
2069 if((!oldStatus && !newStatus) || (oldStatus && newStatus))
2070 {
2071 console_print(id,"[GunGame] GunGame is already %s!",(newStatus) ? "on" : "off");
2072 return PLUGIN_HANDLED;
2073 }
2074
2075 restart_round(5);
2076 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+newStatus);
2077
2078 if(!newStatus)
2079 {
2080 set_pcvar_num(gg_enabled,0);
2081 ggActive = 0;
2082 }
2083
2084 console_print(id,"[GunGame] Turned GunGame %s",(newStatus) ? "on" : "off");
2085
2086 return PLUGIN_HANDLED;
2087}
2088
2089// voting for GunGame
2090public cmd_gungame_vote(id,lvl,cid)
2091{
2092 if(!cmd_access(id,lvl,cid,1))
2093 return PLUGIN_HANDLED;
2094
2095 if(autovotes[0] || autovotes[1] || autovotes[2] || task_exists(TASK_AUTOVOTE_RESULT))
2096 {
2097 console_print(id,"[GunGame] Could not start vote: another vote is already in progress!");
2098 return PLUGIN_HANDLED;
2099 }
2100
2101 new arg[8];
2102 read_argv(1,arg,7);
2103
2104 // override our autovote mode, but default to our autovote setting, then to 1
2105 autovote_mode = str_to_num(arg);
2106 //if(autovote_mode < 1 || autovote_mode > 3) autovote_mode = get_pcvar_num(gg_autovote_mode); // won't work with autovote rotation, so forget it for now
2107 if(autovote_mode < 1 || autovote_mode > 3) autovote_mode = 1;
2108
2109 console_print(id,"[GunGame] Started a vote to play GunGame (mode %i)",autovote_mode);
2110 autovote_start();
2111
2112 return PLUGIN_HANDLED;
2113}
2114
2115// setting players levels
2116public cmd_gungame_level(id,lvl,cid)
2117{
2118 if(!cmd_access(id,lvl,cid,3))
2119 return PLUGIN_HANDLED;
2120
2121 new arg1[32], arg2[8], targets[32], name[32], tnum, i;
2122 read_argv(1,arg1,31);
2123 read_argv(2,arg2,7);
2124
2125 if(equali(arg1,"@T")) arg1 = "@TERRORIST";
2126
2127 // get player list
2128 if(equali(arg1,"*") || equali(arg1,"@ALL"))
2129 {
2130 get_players(targets,tnum);
2131 name = "ALL PLAYERS";
2132 }
2133 else if(arg1[0] == '@')
2134 {
2135 new players[32], team[10], pnum;
2136 get_players(players,pnum);
2137
2138 for(i=0;i<pnum;i++)
2139 {
2140 get_user_team(players[i],team,9);
2141 if(equali(team,arg1[1])) targets[tnum++] = players[i];
2142 }
2143
2144 formatex(name,31,"ALL %s",arg1[1]);
2145 }
2146 else
2147 {
2148 targets[tnum++] = cmd_target(id,arg1,2);
2149 if(!targets[0]) return PLUGIN_HANDLED;
2150
2151 get_user_name(targets[0],name,31);
2152 }
2153
2154 new intval = str_to_num(arg2);
2155
2156 // relative
2157 if(arg2[0] == '+' || arg2[0] == '-')
2158 for(i=0;i<tnum;i++) change_level(targets[i],intval,_,_,1); // always score
2159
2160 // absolute
2161 else
2162 for(i=0;i<tnum;i++) change_level(targets[i],intval-level[targets[i]],_,_,1); // always score
2163
2164 console_print(id,"[GunGame] Changed %s's level to %s",name,arg2);
2165
2166 return PLUGIN_HANDLED;
2167}
2168
2169// setting players score
2170public cmd_gungame_score(id,lvl,cid)
2171{
2172 if(!cmd_access(id,lvl,cid,3))
2173 return PLUGIN_HANDLED;
2174
2175 new arg1[32], arg2[8], arg3[8], targets[32], name[32], tnum, i;
2176 read_argv(1,arg1,31);
2177 read_argv(2,arg2,7);
2178 read_argv(3,arg3,7);
2179
2180 if(equali(arg1,"@T")) arg1 = "@TERRORIST";
2181
2182 // get player list
2183 if(equali(arg1,"*") || equali(arg1,"@ALL"))
2184 {
2185 get_players(targets,tnum);
2186 name = "ALL PLAYERS";
2187 }
2188 else if(arg1[0] == '@')
2189 {
2190 new players[32], team[10], pnum;
2191 get_players(players,pnum);
2192
2193 for(i=0;i<pnum;i++)
2194 {
2195 get_user_team(players[i],team,9);
2196 if(equali(team,arg1[1])) targets[tnum++] = players[i];
2197 }
2198
2199 formatex(name,31,"ALL %s",arg1[1]);
2200 }
2201 else
2202 {
2203 targets[tnum++] = cmd_target(id,arg1,2);
2204 if(!targets[0]) return PLUGIN_HANDLED;
2205
2206 get_user_name(targets[0],name,31);
2207 }
2208
2209 new intval = str_to_num(arg2), dont_refill = str_to_num(arg3);
2210
2211 // relative
2212 if(arg2[0] == '+' || arg2[0] == '-')
2213 for(i=0;i<tnum;i++) change_score(targets[i],intval,!dont_refill,_,_,1); // always score
2214
2215 // absolute
2216 else
2217 for(i=0;i<tnum;i++) change_score(targets[i],intval-score[targets[i]],!dont_refill,_,_,1); // always score
2218
2219 console_print(id,"[GunGame] Changed %s's score to %s",name,arg2);
2220
2221 return PLUGIN_HANDLED;
2222}
2223
2224// forcing a win
2225public cmd_gungame_win(id,lvl,cid)
2226{
2227 if(!cmd_access(id,lvl,cid,1))
2228 return PLUGIN_HANDLED;
2229
2230 new arg[32];
2231 read_argv(1,arg,31);
2232
2233 // no target given, select best player
2234 if(!arg[0])
2235 {
2236 console_print(id,"[GunGame] Forcing the best player to win...");
2237 event_intermission();
2238 return PLUGIN_HANDLED;
2239 }
2240
2241 new target = cmd_target(id,arg,2);
2242 if(!target) return PLUGIN_HANDLED;
2243
2244 new name[32];
2245 get_user_name(target,name,31);
2246 console_print(id,"[GunGame] Forcing %s to win (cheater)...",name);
2247
2248 // make our target win (oh, we're dirty!)
2249 win(target,0);
2250
2251 return PLUGIN_HANDLED;
2252}
2253
2254// turn teamplay on or off
2255public cmd_gungame_teamplay(id,lvl,cid)
2256{
2257 if(!cmd_access(id,lvl,cid,2))
2258 return PLUGIN_HANDLED;
2259
2260 new oldValue = get_pcvar_num(gg_teamplay);
2261
2262 new arg1[8], arg2[8], arg3[8];
2263 read_argv(1,arg1,7);
2264 read_argv(2,arg2,7);
2265 read_argv(3,arg3,7);
2266
2267 new teamplay = str_to_num(arg1);
2268 new Float:killsperlvl = floatstr(arg2);
2269 new suicideloselvl = str_to_num(arg3);
2270
2271 if(teamplay != oldValue)
2272 {
2273 restart_round(1);
2274
2275 // rerun configs if teamplay changes, and don't allow toggling of GunGame on/off
2276 exec_gg_config_file(0,0);
2277 if(teamplay) exec_gg_config_file(1,0);
2278
2279 set_pcvar_num(gg_teamplay,teamplay);
2280 map_start_cvars(1); // keepTeamplay
2281 }
2282
2283 //server_cmd("gg_teamplay %i",teamplay);
2284
2285 new result[128];
2286 new len = formatex(result,127,"[GunGame] Turned Teamplay Mode %s",(teamplay) ? "on" : "off");
2287
2288 // important to run these after the config above
2289
2290 if(killsperlvl > 0.0)
2291 {
2292 server_cmd("gg_kills_per_lvl %f",killsperlvl);
2293 len += formatex(result[len],127-len,", set kills per level to %f",killsperlvl);
2294 }
2295 if(arg3[0])
2296 {
2297 server_cmd("gg_suicide_penalty %i",suicideloselvl);
2298 len += formatex(result[len],127-len,", set suicide penalty to %i",suicideloselvl);
2299 }
2300
2301 console_print(id,"%s",result);
2302
2303 return PLUGIN_HANDLED;
2304}
2305
2306// restarts GunGame
2307public cmd_gungame_restart(id,lvl,cid)
2308{
2309 if(!cmd_access(id,lvl,cid,1))
2310 return PLUGIN_HANDLED;
2311
2312 new arg[8];
2313 read_argv(1,arg,7);
2314
2315 new Float:time = floatstr(arg);
2316 if(time < 0.2) time = 0.2;
2317
2318 restart_round(floatround(time,floatround_ceil));
2319 console_print(id,"[GunGame] Restarting GunGame in %i seconds",floatround(time,floatround_ceil));
2320
2321 read_argv(2,arg,1);
2322 if(str_to_num(arg)) set_task(time-0.1,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
2323
2324 return PLUGIN_HANDLED;
2325}
2326
2327// reload weapon order
2328public cmd_reloadweapons(id,lvl,cid)
2329{
2330 if(!cmd_access(id,lvl,cid,1))
2331 return PLUGIN_HANDLED;
2332
2333 setup_weapon_order();
2334 console_print(id,"* Reloaded the weapon order");
2335
2336 return PLUGIN_HANDLED;
2337}
2338
2339// hook say
2340public cmd_say(id)
2341{
2342 if(!ggActive) return PLUGIN_CONTINUE;
2343
2344 static message[10];
2345 read_argv(1,message,9);
2346
2347 // doesn't begin with !, ignore
2348 if(message[0] != '!') return PLUGIN_CONTINUE;
2349
2350 if(equali(message,"!rules") || equali(message,"!help"))
2351 {
2352 new num = 1, max_lvl = get_pcvar_num(gg_max_lvl), turbo = get_pcvar_num(gg_turbo);
2353
2354 console_print(id,"-----------------------------");
2355 console_print(id,"-----------------------------");
2356 console_print(id,"*** Avalanche's %L %s %L ***",id,"GUNGAME",GG_VERSION,id,"RULES");
2357 console_print(id,"%L",id,"RULES_CONSOLE_LINE1",num++);
2358 console_print(id,"%L",id,"RULES_CONSOLE_LINE2",num++);
2359 if(get_pcvar_num(gg_bomb_defuse_lvl)) console_print(id,"%L",id,"RULES_CONSOLE_LINE3",num++);
2360 console_print(id,"%L",id,"RULES_CONSOLE_LINE4",num++);
2361 if(get_pcvar_num(gg_ff_auto)) console_print(id,"%L",id,"RULES_CONSOLE_LINE5",num++);
2362 if(turbo || !max_lvl) console_print(id,"%L",id,"RULES_CONSOLE_LINE6A",num++);
2363 else if(max_lvl == 1) console_print(id,"%L",id,"RULES_CONSOLE_LINE6B",num++);
2364 else if(max_lvl > 1) console_print(id,"%L",id,"RULES_CONSOLE_LINE6C",num++,max_lvl);
2365 console_print(id,"%L",id,"RULES_CONSOLE_LINE7",num++);
2366 if(get_pcvar_num(gg_knife_pro)) console_print(id,"%L",id,"RULES_CONSOLE_LINE8",num++);
2367 if(turbo) console_print(id,"%L",id,"RULES_CONSOLE_LINE9",num++);
2368 if(get_pcvar_num(gg_knife_elite)) console_print(id,"%L",id,"RULES_CONSOLE_LINE10",num++);
2369 if(get_pcvar_num(gg_dm) || get_cvar_num("csdm_active")) console_print(id,"%L",id,"RULES_CONSOLE_LINE11",num++);
2370 if(get_pcvar_num(gg_teamplay)) console_print(id,"%L",id,"RULES_CONSOLE_LINE12",num++);
2371 console_print(id,"****************************************************************");
2372 console_print(id,"%L",id,"RULES_CONSOLE_LINE13");
2373 console_print(id,"%L",id,"RULES_CONSOLE_LINE14");
2374 console_print(id,"%L",id,"RULES_CONSOLE_LINE15");
2375 console_print(id,"%L",id,"RULES_CONSOLE_LINE16");
2376 console_print(id,"%L",id,"RULES_CONSOLE_LINE17");
2377 console_print(id,"-----------------------------");
2378 console_print(id,"-----------------------------");
2379
2380 new len = formatex(menuText,511,"%L^n",id,"RULES_MESSAGE_LINE1");
2381 len += formatex(menuText[len],511-len,"\d----------\w^n");
2382 len += formatex(menuText[len],511-len,"%L^n",id,"RULES_MESSAGE_LINE2");
2383 len += formatex(menuText[len],511-len,"\d----------\w^n");
2384 len += formatex(menuText[len],511-len,"%L^n",id,"RULES_MESSAGE_LINE3");
2385 len += formatex(menuText[len],511-len,"\d----------\w^n%L",id,"PRESS_KEY_TO_CONTINUE");
2386
2387 show_menu(id,1023,menuText);
2388
2389 return PLUGIN_HANDLED;
2390 }
2391 else if(equali(message,"!weapons") || equali(message,"!guns"))
2392 {
2393 page[id] = 1;
2394 //show_weapons_menu(id);
2395 weapons_menu_handler(id,2); // jump to me
2396
2397 return PLUGIN_HANDLED;
2398 }
2399 else if(equali(message,"!top",4) && !str_count(message,' ')) // !topANYTHING
2400 {
2401 if(!sqlInit || !get_pcvar_num(gg_stats_mode))
2402 {
2403 client_print(id,print_chat,"%L",id,"NO_WIN_LOGGING");
2404 return PLUGIN_HANDLED;
2405 }
2406
2407 page[id] = 1;
2408
2409 if(get_pcvar_num(gg_stats_split) && get_pcvar_num(gg_teamplay))
2410 page[id] *= -1; // use negative page numbers to denote teamplay stats
2411
2412 show_top10_menu(id);
2413 //top10_menu_handler(id,2); // jump to me
2414
2415 return PLUGIN_HANDLED;
2416 }
2417 else if(equali(message,"!score") || equali(message,"!scores"))
2418 {
2419 page[id] = 1;
2420 //show_scores_menu(id);
2421 scores_menu_handler(id,2); // jump to me
2422
2423 return PLUGIN_HANDLED;
2424 }
2425 else if(equali(message,"!level"))
2426 {
2427 show_level_menu(id);
2428
2429 return PLUGIN_HANDLED;
2430 }
2431 else if(equali(message,"!restart") || equali(message,"!reset"))
2432 {
2433 if(level[id] <= 1)
2434 {
2435 client_print(id,print_chat,"%L",id,"STILL_LEVEL_ONE");
2436 return PLUGIN_HANDLED;
2437 }
2438
2439 new len = formatex(menuText,511,"%L^n^n",id,"RESET_QUERY");
2440 len += formatex(menuText[len],511-len,"1. %L^n",id,"YES");
2441 len += formatex(menuText[len],511-len,"0. %L",id,"CANCEL");
2442 show_menu(id,MENU_KEY_1|MENU_KEY_0,menuText,-1,"restart_menu");
2443
2444 return PLUGIN_HANDLED;
2445 }
2446
2447 return PLUGIN_CONTINUE;
2448}
2449
2450// joining a team
2451public cmd_joinclass(id)
2452{
2453 if(!ggActive) return PLUGIN_CONTINUE;
2454
2455 // allow us to join in on deathmatch
2456 if(!get_pcvar_num(gg_dm))
2457 {
2458 remove_task(TASK_CHECK_DEATHMATCH+id);
2459 set_task(10.0,"check_deathmatch",TASK_CHECK_DEATHMATCH+id);
2460 return PLUGIN_CONTINUE;
2461 }
2462
2463 if(roundEnded || (bombStatus[3] == BOMB_PLANTED && !get_pcvar_num(gg_dm_spawn_afterplant)))
2464 return PLUGIN_CONTINUE;
2465
2466 set_task(5.0,"check_joinclass",TASK_CHECK_JOINCLASS+id);
2467 return PLUGIN_CONTINUE;
2468}
2469
2470// wait a bit after joinclass to see if we should jump in
2471public check_joinclass(taskid)
2472{
2473 new id = taskid-TASK_CHECK_JOINCLASS;
2474
2475 if(!is_user_connected(id)) return;
2476
2477 // already respawning
2478 if(task_exists(TASK_RESPAWN+id) || is_user_alive(id) || !on_valid_team(id))
2479 return;
2480
2481 respawn(TASK_RESPAWN+id);
2482}
2483
2484//**********************************************************************
2485// RESPAWN FUNCTIONS
2486//**********************************************************************
2487
2488// get all of our spawns into their arrays
2489init_spawns()
2490{
2491 // grab CSDM file
2492 new mapName[32], csdmFile[64], lineData[64];
2493 get_configsdir(cfgDir,31);
2494 get_mapname(mapName,31);
2495 formatex(csdmFile,63,"%s/csdm/%s.spawns.cfg",cfgDir,mapName);
2496
2497 // collect CSDM spawns
2498 if(file_exists(csdmFile))
2499 {
2500 new csdmData[10][6];
2501
2502 new file = fopen(csdmFile,"rt");
2503 while(file && !feof(file))
2504 {
2505 fgets(file,lineData,63);
2506
2507 // invalid spawn
2508 if(!lineData[0] || str_count(lineData,' ') < 2)
2509 continue;
2510
2511 // BREAK IT UP!
2512 parse(lineData,csdmData[0],5,csdmData[1],5,csdmData[2],5,csdmData[3],5,csdmData[4],5,csdmData[5],5,csdmData[6],5,csdmData[7],5,csdmData[8],5,csdmData[9],5);
2513
2514 // origin
2515 spawns[spawnCount][0] = floatstr(csdmData[0]);
2516 spawns[spawnCount][1] = floatstr(csdmData[1]);
2517 spawns[spawnCount][2] = floatstr(csdmData[2]);
2518
2519 // angles
2520 spawns[spawnCount][3] = floatstr(csdmData[3]);
2521 spawns[spawnCount][4] = floatstr(csdmData[4]);
2522 spawns[spawnCount][5] = floatstr(csdmData[5]);
2523
2524 // team, csdmData[6], unused
2525
2526 // vangles
2527 spawns[spawnCount][6] = floatstr(csdmData[7]);
2528 spawns[spawnCount][7] = floatstr(csdmData[8]);
2529 spawns[spawnCount][8] = floatstr(csdmData[9]);
2530
2531 spawnCount++;
2532 csdmSpawnCount++;
2533 if(spawnCount >= MAX_SPAWNS) break;
2534 }
2535 if(file) fclose(file);
2536 }
2537
2538 // collect regular, boring spawns
2539 else
2540 {
2541 collect_spawns("info_player_deathmatch");
2542 collect_spawns("info_player_start");
2543 }
2544}
2545
2546// collect boring spawns into our spawn data
2547collect_spawns(classname[])
2548{
2549 new ent = maxPlayers, Float:spawnData[3];
2550 while((ent = fm_find_ent_by_class(ent,classname)))
2551 {
2552 // origin
2553 pev(ent,pev_origin,spawnData);
2554 spawns[spawnCount][0] = spawnData[0];
2555 spawns[spawnCount][1] = spawnData[1];
2556 spawns[spawnCount][2] = spawnData[2];
2557
2558 // angles
2559 pev(ent,pev_angles,spawnData);
2560 spawns[spawnCount][3] = spawnData[0];
2561 spawns[spawnCount][4] = spawnData[1];
2562 spawns[spawnCount][5] = spawnData[2];
2563
2564 // vangles
2565 spawns[spawnCount][6] = spawnData[0];
2566 spawns[spawnCount][7] = spawnData[1];
2567 spawns[spawnCount][8] = spawnData[2];
2568
2569 spawnCount++;
2570 if(spawnCount >= MAX_SPAWNS) break;
2571 }
2572}
2573
2574// bring someone back to life
2575public begin_respawn(id)
2576{
2577 if(!ggActive || !get_pcvar_num(gg_dm) || !is_user_connected(id))
2578 return;
2579
2580 // now on spectator
2581 if(!on_valid_team(id)) return;
2582
2583 // alive, and not in the broken sort of way
2584 if(is_user_alive(id) && !pev(id,pev_iuser1))
2585 return;
2586
2587 // round is over, or bomb is planted
2588 if(roundEnded || (bombStatus[3] == BOMB_PLANTED && !get_pcvar_num(gg_dm_spawn_afterplant)))
2589 return;
2590
2591 new Float:delay = get_pcvar_float(gg_dm_spawn_delay);
2592 if(delay < 0.1) delay = 0.1;
2593
2594 new dm_countdown = get_pcvar_num(gg_dm_countdown);
2595
2596 if((dm_countdown & 1) || (dm_countdown & 2))
2597 {
2598 respawn_timeleft[id] = floatround(delay);
2599 respawn_countdown(id);
2600 }
2601
2602 remove_task(TASK_RESPAWN+id);
2603 set_task(delay,"respawn",TASK_RESPAWN+id);
2604}
2605
2606// show the respawn countdown to a player
2607public respawn_countdown(id)
2608{
2609 if(!is_user_connected(id) || is_user_alive(id))
2610 {
2611 respawn_timeleft[id] = 0;
2612 return;
2613 }
2614
2615 new dm_countdown = get_pcvar_num(gg_dm_countdown);
2616
2617 if(dm_countdown & 1)
2618 client_print(id,print_center,"%L",id,"RESPAWN_COUNTDOWN",respawn_timeleft[id]);
2619
2620 if(dm_countdown & 2)
2621 {
2622 set_hudmessage(255,255,255,-1.0,0.75,0,6.0,1.0,0.1,0.5);
2623 ShowSyncHudMsg(id,hudSyncCountdown,"%L",id,"RESPAWN_COUNTDOWN",respawn_timeleft[id]);
2624 }
2625
2626 if(--respawn_timeleft[id] >= 1) set_task(1.0,"respawn_countdown",id);
2627}
2628
2629// REALLY bring someone back to life
2630public respawn(taskid)
2631{
2632 new id = taskid-TASK_RESPAWN;
2633 if(!is_user_connected(id) || !ggActive) return;
2634
2635 // round is over, or bomb is planted
2636 if(roundEnded || (bombStatus[3] == BOMB_PLANTED && !get_pcvar_num(gg_dm_spawn_afterplant)))
2637 return;
2638
2639 // now on spectator
2640 if(!on_valid_team(id)) return;
2641
2642 // clear countdown
2643 new dm_countdown = get_pcvar_num(gg_dm_countdown);
2644 if(dm_countdown & 1) client_print(id,print_center," ");
2645 if(dm_countdown & 2) ClearSyncHud(id,hudSyncCountdown);
2646
2647 // alive, and not in the broken sort of way
2648 if(is_user_alive(id)) return;
2649
2650 static model[22];
2651
2652 // remove his dropped weapons from before
2653 new ent = maxPlayers;
2654 while((ent = fm_find_ent_by_class(ent,"weaponbox")))
2655 {
2656 pev(ent,pev_model,model,21);
2657
2658 // don't remove the bomb!! (thanks ToT | V!PER)
2659 if(equal(model,"models/w_c4.mdl",15) || equal(model,"models/w_backpack.mdl"))
2660 continue;
2661
2662 // this is mine
2663 if(pev(ent,pev_owner) == id) dllfunc(DLLFunc_Think,ent);
2664 }
2665
2666 new spawn_random = get_pcvar_num(gg_dm_spawn_random);
2667 if(spawn_random) spawnSounds[id] = 0;
2668
2669 ExecuteHamB(Ham_CS_RoundRespawn,id); // note the B
2670
2671 if(spawn_random)
2672 {
2673 do_random_spawn(id,spawn_random);
2674 spawnSounds[id] = 1;
2675
2676 // to be fair, play a spawn noise at new location
2677 engfunc(EngFunc_EmitSound,id,CHAN_ITEM,"items/gunpickup2.wav",VOL_NORM,ATTN_NORM,0,PITCH_NORM);
2678 }
2679
2680 new Float:time = get_pcvar_float(gg_dm_sp_time);
2681 new mode = get_pcvar_num(gg_dm_sp_mode);
2682
2683 // spawn protection
2684 if(time > 0.0 && mode)
2685 {
2686 spawnProtected[id] = 1;
2687 if(mode == 2)
2688 {
2689 fm_set_user_godmode(id,1);
2690 fm_set_rendering(id,kRenderFxGlowShell,200,200,100,kRenderNormal,8); // goldenish
2691 }
2692 else fm_set_rendering(id,kRenderFxGlowShell,100,100,100,kRenderNormal,8); // gray/white
2693
2694 set_task(time,"remove_spawn_protection",TASK_REMOVE_PROTECTION+id);
2695 }
2696}
2697
2698// place a user at a random spawn
2699do_random_spawn(id,spawn_random)
2700{
2701 // not even alive, don't bother
2702 if(!is_user_alive(id)) return;
2703
2704 // no spawns???
2705 if(spawnCount <= 0) return;
2706
2707 // no CSDM spawns, mode 2
2708 if(spawn_random == 2 && !csdmSpawnCount)
2709 return;
2710
2711 static Float:vecHolder[3];
2712 new sp_index = random_num(0,spawnCount-1);
2713
2714 // get origin for comparisons
2715 vecHolder[0] = spawns[sp_index][0];
2716 vecHolder[1] = spawns[sp_index][1];
2717 vecHolder[2] = spawns[sp_index][2];
2718
2719 // this one is taken
2720 if(!is_hull_vacant(vecHolder,HULL_HUMAN) && spawnCount > 1)
2721 {
2722 // attempt to pick another random one up to three times
2723 new i;
2724 for(i=0;i<3;i++)
2725 {
2726 sp_index = random_num(0,spawnCount-1);
2727
2728 vecHolder[0] = spawns[sp_index][0];
2729 vecHolder[1] = spawns[sp_index][1];
2730 vecHolder[2] = spawns[sp_index][2];
2731
2732 if(is_hull_vacant(vecHolder,HULL_HUMAN)) break;
2733 }
2734
2735 // we made it through the entire loop, no free spaces
2736 if(i == 3)
2737 {
2738 // just find the first available
2739 for(i=sp_index+1;i!=sp_index;i++)
2740 {
2741 // start over when we reach the end
2742 if(i >= spawnCount) i = 0;
2743
2744 vecHolder[0] = spawns[i][0];
2745 vecHolder[1] = spawns[i][1];
2746 vecHolder[2] = spawns[i][2];
2747
2748 // free space! office space!
2749 if(is_hull_vacant(vecHolder,HULL_HUMAN))
2750 {
2751 sp_index = i;
2752 break;
2753 }
2754 }
2755 }
2756 }
2757
2758 // origin
2759 vecHolder[0] = spawns[sp_index][0];
2760 vecHolder[1] = spawns[sp_index][1];
2761 vecHolder[2] = spawns[sp_index][2];
2762 engfunc(EngFunc_SetOrigin,id,vecHolder);
2763
2764 // angles
2765 vecHolder[0] = spawns[sp_index][3];
2766 vecHolder[1] = spawns[sp_index][4];
2767 vecHolder[2] = spawns[sp_index][5];
2768 set_pev(id,pev_angles,vecHolder);
2769
2770 // vangles
2771 vecHolder[0] = spawns[sp_index][6];
2772 vecHolder[1] = spawns[sp_index][7];
2773 vecHolder[2] = spawns[sp_index][8];
2774 set_pev(id,pev_v_angle,vecHolder);
2775
2776 set_pev(id,pev_fixangle,1);
2777}
2778
2779// get rid of the spawn protection effects
2780public remove_spawn_protection(taskid)
2781{
2782 new id = taskid-TASK_REMOVE_PROTECTION;
2783 spawnProtected[id] = 0;
2784
2785 if(!is_user_connected(id)) return;
2786
2787 if(get_pcvar_num(gg_dm_sp_mode) == 2) fm_set_user_godmode(id,0);
2788 fm_set_rendering(id); // reset back to normal
2789}
2790
2791// keep checking if a player needs to rejoin
2792public check_deathmatch(taskid)
2793{
2794 new id = taskid-TASK_CHECK_DEATHMATCH;
2795
2796 // left the game, or gungame is now disabled
2797 if(!is_user_connected(id) || !ggActive) return;
2798
2799 // now on spectator, or spawned already
2800 if(!on_valid_team(id) || is_user_alive(id)) return;
2801
2802 // DM still not enabled, keep waiting (or: we are still on choose-a-class screen)
2803 if(!get_pcvar_num(gg_dm) || !pev(id,pev_iuser1))
2804 {
2805 set_task(10.0,"check_deathmatch",taskid);
2806 return;
2807 }
2808
2809 // DM is enabled, respawn
2810 respawn(TASK_RESPAWN+id);
2811}
2812
2813// what do you think??
2814public randomly_place_everyone()
2815{
2816 // count number of legitimate players
2817 new player, validNum;
2818 for(player=1;player<=maxPlayers;player++)
2819 {
2820 if(is_user_connected(player) && on_valid_team(player))
2821 validNum++;
2822 }
2823
2824 // not enough CSDM spawns for everyone
2825 if(validNum > csdmSpawnCount)
2826 return;
2827
2828 // now randomly place them
2829 for(player=1;player<=maxPlayers;player++)
2830 {
2831 // not spectator or unassigned
2832 if(is_user_connected(player) && on_valid_team(player))
2833 do_random_spawn(player,2);
2834 }
2835}
2836
2837//**********************************************************************
2838// MENU FUNCTIONS
2839//**********************************************************************
2840
2841// handle the welcome menu
2842public welcome_menu_handler(id,key)
2843{
2844 // just save welcomed status and let menu close
2845 welcomed[id] = 1;
2846 return PLUGIN_HANDLED;
2847}
2848
2849// this menu does nothing but display stuff
2850public level_menu_handler(id,key)
2851{
2852 return PLUGIN_HANDLED;
2853}
2854
2855// handle the reset level menu
2856public restart_menu_handler(id,key)
2857{
2858 if(get_pcvar_num(gg_teamplay))
2859 {
2860 client_print(id,print_chat,"%L",id,"RESET_NOT_ALLOWED");
2861 return PLUGIN_HANDLED;
2862 }
2863
2864 if(level[id] <= 1)
2865 {
2866 client_print(id,print_chat,"%L",id,"STILL_LEVEL_ONE");
2867 return PLUGIN_HANDLED;
2868 }
2869
2870 // 1. Yes
2871 if(key == 0)
2872 {
2873 new name[32];
2874 get_user_name(id,name,31);
2875
2876 change_level(id,-(level[id]-1),_,_,1); // back to level 1 -- always score
2877 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"PLAYER_RESET",name);
2878 }
2879
2880 return PLUGIN_HANDLED;
2881}
2882
2883// show the level display
2884show_level_menu(id)
2885{
2886#if !defined SQL
2887 recheck_stats_sorting();
2888#endif
2889
2890 new goal, tied, leaderNum, leaderList[128], name[32];
2891
2892 new leaderLevel, numLeaders, leader, runnerUp, len;
2893 new teamplay = get_pcvar_num(gg_teamplay), team;
2894
2895 if(teamplay) leader = teamplay_get_lead_team(leaderLevel,numLeaders,runnerUp);
2896 else leader = get_leader(leaderLevel,numLeaders,runnerUp);
2897
2898 if(numLeaders > 1) tied = 1;
2899
2900 if(teamplay)
2901 {
2902 team = _:cs_get_user_team(id);
2903
2904 if(numLeaders == 1)
2905 {
2906 new team1[10];
2907 get_team_name(CsTeams:leader,team1,9);
2908 len += formatex(leaderList[len],127-len,"%s %L",team1,id,"TEAM");
2909 }
2910 else
2911 {
2912 new team1[10], team2[10];
2913 get_team_name(CS_TEAM_T,team1,9);
2914 get_team_name(CS_TEAM_CT,team2,9);
2915 len += formatex(leaderList[len],127-len,"%s %L, %s %L",team1,id,"TEAM",team2,id,"TEAM");
2916 }
2917 }
2918 else
2919 {
2920 new players[32], num, i, player;
2921 get_players(players,num);
2922
2923 // check for multiple leaders
2924 for(i=0;i<num;i++)
2925 {
2926 player = players[i];
2927
2928 if(level[player] == leaderLevel)
2929 {
2930 if(++leaderNum == 5)
2931 {
2932 len += formatex(leaderList[len],127-len,", ...");
2933 break;
2934 }
2935
2936 if(leaderList[0]) len += formatex(leaderList[len],127-len,", ");
2937 get_user_name(player,name,31);
2938 len += formatex(leaderList[len],127-len,"%s",name);
2939 }
2940 }
2941 }
2942
2943 goal = get_level_goal(level[id],id);
2944
2945 new displayWeapon[16];
2946 if(level[id]) copy(displayWeapon,15,lvlWeapon[id]);
2947 else formatex(displayWeapon,15,"%L",id,"NONE");
2948
2949 len = formatex(menuText,511,"%L %i (%s)^n",id,(teamplay) ? "ON_LEVEL_TEAM" : "ON_LEVEL",level[id],displayWeapon);
2950 len += formatex(menuText[len],511-len,"%L^n",id,(teamplay) ? "LEVEL_MESSAGE_LINE1B" : "LEVEL_MESSAGE_LINE1A",score[id],goal);
2951
2952 // winning
2953 if(!tied && ((teamplay && leader == team) || (!teamplay && leader == id)))
2954 {
2955 if(teamplay) len += formatex(menuText[len],511-len,"%L^n",id,"PROGRESS_DISPLAY_TEAM1",teamLevel[leader]-teamLevel[runnerUp]);
2956 else len += formatex(menuText[len],511-len,"%L^n",id,"PROGRESS_DISPLAY1",level[id]-level[runnerUp]);
2957 }
2958
2959 // tied
2960 else if(tied)
2961 {
2962 if(teamplay) len += formatex(menuText[len],511-len,"%L^n",id,"PROGRESS_DISPLAY_TEAM2");
2963 else len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE2B");
2964 }
2965
2966 // losing
2967 else
2968 {
2969 if(teamplay) len += formatex(menuText[len],511-len,"%L^n",id,"PROGRESS_DISPLAY_TEAM3",teamLevel[leader]-teamLevel[runnerUp]);
2970 else len += formatex(menuText[len],511-len,"%L^n",id,"PROGRESS_DISPLAY4",leaderLevel-level[id]);
2971 }
2972
2973 len += formatex(menuText[len],511-len,"\d----------\w^n");
2974
2975 new authid[32];
2976 get_gg_authid(id,authid,31);
2977
2978 new stats_mode = get_pcvar_num(gg_stats_mode);
2979
2980 if(sqlInit && stats_mode)
2981 {
2982 new stats_split = get_pcvar_num(gg_stats_split);
2983
2984 // decide which sentences we should be using based on current split/teamplay settings
2985 new keyLINE3A[25], keyLINE3B[25], keyLINE4[24]; // should I rename these? nah, too long
2986 if(stats_split)
2987 {
2988 keyLINE3A = "LEVEL_MESSAGE_LINE3A_REG";
2989 keyLINE3B = "LEVEL_MESSAGE_LINE3B_REG";
2990 keyLINE4 = "LEVEL_MESSAGE_LINE4_REG";
2991 }
2992 else
2993 {
2994 keyLINE3A = "LEVEL_MESSAGE_LINE3A";
2995 keyLINE3B = "LEVEL_MESSAGE_LINE3B";
2996 keyLINE4 = "LEVEL_MESSAGE_LINE4";
2997 }
2998
2999 stats_get_data(authid,playerStats[id],id);
3000
3001 if(statsPosition[id][0] > 0) // show rank
3002 {
3003 new statsSuffix[3];
3004 get_number_suffix(statsPosition[id][0],statsSuffix,2);
3005
3006 if(stats_mode == 1) len += formatex(menuText[len],511-len,"%L (%i%s)^n",id,keyLINE3A,playerStats[id][sdWins][0],statsPosition[id][0],statsSuffix);
3007 else len += formatex(menuText[len],511-len,"%L (%i%s)^n",id,keyLINE3B,playerStats[id][sdPoints][0],playerStats[id][sdWins][0],statsPosition[id][0],statsSuffix);
3008 }
3009 else // don't show rank
3010 {
3011 if(stats_mode == 1) len += formatex(menuText[len],511-len,"%L^n",id,keyLINE3A,playerStats[id][sdWins][0]);
3012 else len += formatex(menuText[len],511-len,"%L^n",id,keyLINE3B,playerStats[id][sdPoints][0],playerStats[id][sdWins][0]);
3013 }
3014
3015 len += formatex(menuText[len],511-len,"%L^n",id,keyLINE4,playerStats[id][sdStreak][0]);
3016
3017 // now show teamplay if we can/should
3018 if(stats_split)
3019 {
3020 if(statsPosition[id][1] > 0) // show rank
3021 {
3022 new statsSuffix[3];
3023 get_number_suffix(statsPosition[id][1],statsSuffix,2);
3024
3025 if(stats_mode == 1) len += formatex(menuText[len],511-len,"%L (%i%s)^n",id,"LEVEL_MESSAGE_LINE3A_TP",playerStats[id][sdWins][1],statsPosition[id][1],statsSuffix);
3026 else len += formatex(menuText[len],511-len,"%L (%i%s)^n",id,"LEVEL_MESSAGE_LINE3B_TP",playerStats[id][sdPoints][1],playerStats[id][sdWins][1],statsPosition[id][1],statsSuffix);
3027 }
3028 else // don't show rank
3029 {
3030 if(stats_mode == 1) len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE3A_TP",playerStats[id][sdWins][1]);
3031 else len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE3B_TP",playerStats[id][sdPoints][1],playerStats[id][sdWins][1]);
3032 }
3033
3034 len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE4_TP",playerStats[id][sdStreak][1]);
3035 }
3036
3037 len += formatex(menuText[len],511-len,"\d----------\w^n");
3038 }
3039
3040 if(leaderNum > 1) len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE5A",leaderList);
3041 else len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE5B",leaderList);
3042
3043 if(teamplay)
3044 {
3045 if(teamLevel[leader]) copy(displayWeapon,15,teamLvlWeapon[leader]);
3046 else formatex(displayWeapon,15,"%L",id,"NONE");
3047 }
3048 else
3049 {
3050 if(level[leader]) copy(displayWeapon,15,lvlWeapon[leader]);
3051 else formatex(displayWeapon,15,"%L",id,"NONE");
3052 }
3053
3054 len += formatex(menuText[len],511-len,"%L^n",id,"LEVEL_MESSAGE_LINE6",leaderLevel,displayWeapon);
3055 len += formatex(menuText[len],511-len,"\d----------\w^n");
3056
3057 len += formatex(menuText[len],511-len,"%L",id,"PRESS_KEY_TO_CONTINUE");
3058
3059 show_menu(id,1023,menuText,-1,"level_menu");
3060}
3061
3062// show the top10 list menu
3063show_top10_menu(id)
3064{
3065 new pppString[74], len = get_pcvar_string(gg_top10_ppp,pppString,63);
3066
3067 // URL specified
3068 if(pppString[0] && !isdigit(pppString[0]))
3069 {
3070 new header[32], lang[3];
3071 formatex(header,31,"%L %L",id,"GUNGAME",id,"STATS");
3072 get_user_info(id,"lang",lang,2);
3073
3074 formatex(pppString[len],73-len,"?i=%i&l=%s",id,lang);
3075 show_motd(id,pppString,header);
3076
3077 return;
3078 }
3079
3080#if !defined SQL
3081 recheck_stats_sorting();
3082#endif
3083
3084 new absPage = abs(page[id]), stats_split = get_pcvar_num(gg_stats_split), si = (page[id] < 0);
3085
3086 new playersPerPage = str_to_num(pppString), stats_mode = get_pcvar_num(gg_stats_mode);
3087 //if(stats_split == 2) playersPerPage = 7;
3088
3089#if defined SQL
3090 new winsColumn[8], pointsColumn[10], streakColumn[10], totalPlayers, numRows;
3091
3092 if(si == 0)
3093 {
3094 winsColumn = "wins";
3095 pointsColumn = "points";
3096 streakColumn = "streak";
3097 }
3098 else
3099 {
3100 winsColumn = "wins_tp";
3101 pointsColumn = "points_tp";
3102 streakColumn = "streak_tp";
3103 }
3104
3105 if(stats_mode == 2) query = SQL_PrepareQuery(db,"SELECT NULL FROM `%s` WHERE serverip='%s' AND (%s > 0 OR %s > 0);",sqlTable,serverip,winsColumn,pointsColumn);
3106 else query = SQL_PrepareQuery(db,"SELECT NULL FROM `%s` WHERE serverip='%s' AND %s > 0;",sqlTable,serverip,winsColumn);
3107
3108 if(SQL_ExecuteAndLog(query))
3109 {
3110 numRows = SQL_NumRows(query);
3111 totalPlayers = playersPerPage * floatround(float(numRows+1) / float(playersPerPage),floatround_ceil);
3112 }
3113
3114 SQL_FreeHandle(query);
3115#else
3116 new totalPlayers = playersPerPage * floatround(float(statsSize[si]+1) / float(playersPerPage),floatround_ceil); // +1 for streak display
3117#endif
3118
3119 new pageTotal = floatround(float(totalPlayers) / float(playersPerPage),floatround_ceil);
3120
3121 if(pageTotal < 1) pageTotal = 1;
3122 if(totalPlayers < playersPerPage) totalPlayers = playersPerPage;
3123
3124 if(absPage > pageTotal)
3125 {
3126 new negative = (page[id] < 0);
3127 page[id] = absPage = pageTotal;
3128 if(negative) page[id] *= -1;
3129 }
3130
3131 if(stats_split) len = formatex(menuText,511,"\y%L %L (%i/%i)^n",id,"GUNGAME",id,(page[id] < 0) ? "STATS_TEAMPLAY" : "STATS_REGULAR",absPage,pageTotal);
3132 else len = formatex(menuText,511,"\y%L %L (%i/%i)^n",id,"GUNGAME",id,"STATS",absPage,pageTotal);
3133
3134 new start = (playersPerPage * (absPage-1)), i;
3135
3136 // show the top streak for the first page
3137 new topStreak, champName[32], champAuthid[32];
3138 if(absPage == 1)
3139 {
3140#if defined SQL
3141 query = SQL_PrepareQuery(db,"SELECT authid,streak,name FROM `%s` WHERE type='%iR' AND serverip='%s' LIMIT 1;",sqlStreakTable,si,serverip);
3142 if(SQL_ExecuteAndLog(query) && SQL_NumResults(query))
3143 {
3144 SQL_ReadResult(query,0,champAuthid,31);
3145 topStreak = SQL_ReadResult(query,1);
3146 SQL_ReadResult(query,2,champName,31);
3147 }
3148 SQL_FreeHandle(query);
3149#else
3150 new sfStreak[4], stats_streak_file[64];
3151 get_pcvar_string(gg_stats_streak_file,stats_streak_file,63);
3152
3153 if(file_exists(stats_streak_file))
3154 {
3155 new file = fopen(stats_streak_file,"rt");
3156 while(file && !feof(file))
3157 {
3158 fgets(file,sfLineData,82);
3159
3160 // blank, not for our stats mode, or not the record
3161 if(!sfLineData[0] || str_to_num(sfLineData[0]) != si || sfLineData[1] != 'R') continue;
3162
3163 strtok(sfLineData[3],champAuthid,31,sfLineData,82,'^t'); // cut off prefix and authid from the left
3164 strtok(sfLineData,sfStreak,3,champName,31,'^t'); // get our streak, and the name as well
3165
3166 new pos = contain_char(champName,'^t');
3167 if(pos != -1) champName[pos] = 0; // cut off the name at the tab
3168
3169 topStreak = str_to_num(sfStreak);
3170 }
3171 if(file) fclose(file);
3172 }
3173#endif
3174
3175 if(!champName[0]) formatex(champName,31,"%L",id,"NO_ONE");
3176 }
3177
3178 //len += formatex(menuText[len],511-len,"\d-----------\w^n");
3179
3180 new authid[32];
3181 get_gg_authid(id,authid,31);
3182
3183#if defined SQL
3184 if(numRows)
3185 {
3186 // do this to account for the streak display in our LIMIT clause
3187 if(absPage == 1) playersPerPage--;
3188 else start--;
3189
3190 if(stats_mode == 2)
3191 {
3192 query = SQL_PrepareQuery(db,"SELECT authid,name,%s,%s,%s,(SELECT COUNT(*)+1 FROM `%s` y WHERE y.%s > x.%s AND serverip='%s' AND (y.%s > 0 OR y.%s > 0) LIMIT 1) AS ranking FROM `%s` x WHERE x.serverip='%s' AND (x.%s > 0 OR x.%s > 0) ORDER BY %s DESC, %s DESC LIMIT %i, %i;",
3193 winsColumn,pointsColumn,streakColumn,sqlTable,pointsColumn,pointsColumn,serverip,winsColumn,pointsColumn,sqlTable,serverip,winsColumn,pointsColumn,pointsColumn,winsColumn,start,playersPerPage);
3194 }
3195 else
3196 {
3197 query = SQL_PrepareQuery(db,"SELECT authid,name,%s,%s,%s,(SELECT COUNT(*)+1 FROM `%s` y WHERE y.%s > x.%s AND serverip='%s' AND y.%s > 0 LIMIT 1) AS ranking FROM `%s` x WHERE x.serverip='%s' AND x.%s > 0 ORDER BY %s DESC LIMIT %i, %i;",
3198 winsColumn,pointsColumn,streakColumn,sqlTable,winsColumn,winsColumn,serverip,winsColumn,sqlTable,serverip,winsColumn,winsColumn,start,playersPerPage);
3199 }
3200
3201 // reverse changes made above for LIMIT
3202 if(absPage == 1) playersPerPage++;
3203 else start++;
3204 }
3205
3206 if(!numRows || SQL_ExecuteAndLog(query))
3207 {
3208 new ranking, moreResults, lastRanking = start;
3209 if(numRows) moreResults = SQL_MoreResults(query);
3210
3211 for(i=start;i<start+playersPerPage;i++)
3212 {
3213 if(i >= totalPlayers) break;
3214
3215 // use the first slot to display the record streak
3216 if(i == 0)
3217 {
3218 len += formatex(menuText[len],511-len,"%s%L^n",(equal(authid,champAuthid)) ? "\r" : "\w",id,"RECORD_STREAK",champName,topStreak);
3219 continue;
3220 }
3221
3222 // all out of rows
3223 if(!moreResults)
3224 {
3225 lastRanking++;
3226 len += formatex(menuText[len],511-len,"\w#%i \d%L^n",lastRanking,id,"NONE");
3227 continue;
3228 }
3229
3230 SQL_ReadResult(query,0,sfStatsStruct[sdAuthid],31);
3231 SQL_ReadResult(query,1,sfStatsStruct[sdName],31);
3232 sfStatsStruct[sdWins][si] = SQL_ReadResult(query,2);
3233 sfStatsStruct[sdPoints][si] = SQL_ReadResult(query,3);
3234 sfStatsStruct[sdStreak][si] = SQL_ReadResult(query,4);
3235 ranking = SQL_ReadResult(query,5);
3236
3237 if(stats_mode == 1)
3238 {
3239 if(sfStatsStruct[sdStreak][si] > 1) len += formatex(menuText[len],511-len,"%s#%i %s (%i %L, %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",ranking,sfStatsStruct[sdName],sfStatsStruct[sdWins][si],id,"WINS",id,"IN_A_ROW",sfStatsStruct[sdStreak][si]);
3240 else len += formatex(menuText[len],511-len,"%s#%i %s (%i %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",ranking,sfStatsStruct[sdName],sfStatsStruct[sdWins][si],id,"WINS");
3241 }
3242 else
3243 {
3244 if(sfStatsStruct[sdStreak][si] > 1) len += formatex(menuText[len],511-len,"%s#%i %s (%i %L, %i %L, %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",ranking,sfStatsStruct[sdName],sfStatsStruct[sdPoints][si],id,"POINTS",sfStatsStruct[sdWins][si],id,"WINS",id,"IN_A_ROW",sfStatsStruct[sdStreak][si]);
3245 else len += formatex(menuText[len],511-len,"%s#%i %s (%i %L, %i %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",ranking,sfStatsStruct[sdName],sfStatsStruct[sdPoints][si],id,"POINTS",sfStatsStruct[sdWins][si],id,"WINS");
3246 }
3247
3248 SQL_NextRow(query);
3249 moreResults = SQL_MoreResults(query);
3250
3251 lastRanking = ranking;
3252 }
3253 }
3254
3255 if(numRows) SQL_FreeHandle(query);
3256#else
3257 for(i=start;i<start+playersPerPage;i++)
3258 {
3259 if(i >= totalPlayers) break;
3260
3261 // use the first slot to display the record streak
3262 if(i == 0)
3263 {
3264 len += formatex(menuText[len],511-len,"%s%L^n",(equal(authid,champAuthid)) ? "\r" : "\w",id,"RECORD_STREAK",champName,topStreak);
3265 continue;
3266 }
3267
3268 // blank
3269 if(i-1 >= statsSize[si])
3270 {
3271 len += formatex(menuText[len],511-len,"\w#%i \d%L^n",i/*+1-1*/,id,"NONE");
3272 continue;
3273 }
3274
3275 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[si],i-1),sfStatsStruct);
3276
3277 if(stats_mode == 1)
3278 {
3279 if(sfStatsStruct[sdStreak][si] > 1) len += formatex(menuText[len],511-len,"%s#%i %s (%i %L, %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",i/*+1-1*/,sfStatsStruct[sdName],sfStatsStruct[sdWins][si],id,"WINS",id,"IN_A_ROW",sfStatsStruct[sdStreak][si]);
3280 else len += formatex(menuText[len],511-len,"%s#%i %s (%i %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",i/*+1-1*/,sfStatsStruct[sdName],sfStatsStruct[sdWins][si],id,"WINS");
3281 }
3282 else
3283 {
3284 if(sfStatsStruct[sdStreak][si] > 1) len += formatex(menuText[len],511-len,"%s#%i %s (%i %L, %i %L, %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",i/*+1-1*/,sfStatsStruct[sdName],sfStatsStruct[sdPoints][si],id,"POINTS",sfStatsStruct[sdWins][si],id,"WINS",id,"IN_A_ROW",sfStatsStruct[sdStreak][si]);
3285 else len += formatex(menuText[len],511-len,"%s#%i %s (%i %L, %i %L)^n",(equal(authid,sfStatsStruct[sdAuthid])) ? "\r" : "\w",i/*+1-1*/,sfStatsStruct[sdName],sfStatsStruct[sdPoints][si],id,"POINTS",sfStatsStruct[sdWins][si],id,"WINS");
3286 }
3287 }
3288#endif
3289
3290 len += formatex(menuText[len],511-len,"\d-----------\w^n");
3291
3292 new keys = MENU_KEY_0;
3293
3294 if(absPage > 1)
3295 {
3296 len += formatex(menuText[len],511-len,"1. %L^n",id,"PREVIOUS");
3297 keys |= MENU_KEY_1;
3298 }
3299 if(absPage < pageTotal)
3300 {
3301 len += formatex(menuText[len],511-len,"2. %L^n",id,"NEXT");
3302 keys |= MENU_KEY_2;
3303 }
3304 if(statsPosition[id][si] > 0)
3305 {
3306 len += formatex(menuText[len],511-len,"3. %L^n",id,"JUMP_TO_ME");
3307 keys |= MENU_KEY_3;
3308 }
3309 if(stats_split)
3310 {
3311 len += formatex(menuText[len],511-len,"4. %L^n",id,(page[id] < 0) ? "STATS_REGULAR" : "STATS_TEAMPLAY");
3312 keys |= MENU_KEY_4;
3313 }
3314 len += formatex(menuText[len],511-len,"0. %L",id,"CLOSE");
3315
3316 show_menu(id,keys,menuText,-1,"top10_menu");
3317}
3318
3319// someone pressed a key on the top10 list menu page
3320public top10_menu_handler(id,key)
3321{
3322#if !defined SQL
3323 recheck_stats_sorting();
3324#endif
3325
3326 new si = (page[id] < 0);
3327
3328 new playersPerPage = get_pcvar_num(gg_top10_ppp);
3329 //if(get_pcvar_num(gg_stats_split) == 2) playersPerPage = 7;
3330
3331#if defined SQL
3332 new winsColumn[8], pointsColumn[10], totalPlayers, stats_mode = get_pcvar_num(gg_stats_mode);
3333
3334 if(si == 0)
3335 {
3336 winsColumn = "wins";
3337 pointsColumn = "points";
3338 }
3339 else
3340 {
3341 winsColumn = "wins_tp";
3342 pointsColumn = "points_tp";
3343 }
3344
3345 if(stats_mode == 2) query = SQL_PrepareQuery(db,"SELECT NULL FROM `%s` WHERE serverip='%s' AND (%s > 0 OR %s > 0);",sqlTable,serverip,winsColumn,pointsColumn);
3346 else query = SQL_PrepareQuery(db,"SELECT NULL FROM `%s` WHERE serverip='%s' AND %s > 0;",sqlTable,serverip,winsColumn);
3347
3348 if(SQL_ExecuteAndLog(query)) totalPlayers = playersPerPage * floatround(float(SQL_NumRows(query)+1) / float(playersPerPage),floatround_ceil);
3349 SQL_FreeHandle(query);
3350#else
3351 new totalPlayers = playersPerPage * floatround(float(statsSize[si]+1) / float(playersPerPage),floatround_ceil); // +1 for streak display
3352#endif
3353
3354 new pageTotal = floatround(float(totalPlayers) / float(playersPerPage),floatround_ceil);
3355 if(pageTotal < 1) pageTotal = 1;
3356
3357 if(!page[id] || page[id] > pageTotal) return;
3358
3359 // 1. Previous
3360 if(key == 0)
3361 {
3362 if(page[id] < 0) page[id]++;
3363 else page[id]--;
3364
3365 show_top10_menu(id);
3366 }
3367
3368 // 2. Next
3369 else if(key == 1)
3370 {
3371 if(page[id] < 0) page[id]--;
3372 else page[id]++;
3373
3374 show_top10_menu(id);
3375 }
3376
3377 // 3. Jump to me
3378 else if(key == 2)
3379 {
3380#if defined SQL
3381 if(statsPosition[id][si] > 0)
3382 {
3383 // using SQL stats, players can have tied rankings (ie: 1st, 1st, 3rd). so, if we know a player's ranking, we don't necessarily know
3384 // what page they're on (they could be the last of 20 players all tied for 1st, for example). however, we do know what position (and
3385 // therefore also the page) that their ranking starts on. then we can select everyone with an equal score, and process the results
3386 // until we find their authid, to see how many positions off they are from the start of their ranking. then find the page for that.
3387
3388 new authid[32], myPoints;
3389 get_gg_authid(id,authid,31);
3390
3391 stats_get_data(authid,playerStats[id],id);
3392
3393 if(stats_mode == 2)
3394 {
3395 myPoints = playerStats[id][sdPoints][si];
3396 query = SQL_PrepareQuery(db,"SELECT authid FROM `%s` WHERE %s='%i' AND serverip='%s' ORDER BY %s, %s DESC;",sqlTable,pointsColumn,myPoints,serverip,pointsColumn,winsColumn);
3397 }
3398 else
3399 {
3400 myPoints = playerStats[id][sdWins][si];
3401 query = SQL_PrepareQuery(db,"SELECT authid FROM `%s` WHERE %s='%i' AND serverip='%s' ORDER BY %s DESC;",sqlTable,winsColumn,myPoints,serverip,winsColumn);
3402 }
3403
3404 if(SQL_ExecuteAndLog(query))
3405 {
3406 new position = statsPosition[id][si]; // start at my position
3407
3408 if(SQL_NumRows(query) > 1) // if I'm the only one with my score, no searching is necessary
3409 {
3410 new rowAuthid[32];
3411 while(SQL_MoreResults(query))
3412 {
3413 SQL_ReadResult(query,0,rowAuthid,31);
3414 if(equal(authid,rowAuthid)) break;
3415
3416 position++;
3417 SQL_NextRow(query);
3418 }
3419 }
3420
3421 new negative = (page[id] < 0);
3422 page[id] = floatround(float(position) / float(playersPerPage),floatround_floor) + 1;
3423 if(negative) page[id] *= -1;
3424 }
3425
3426 SQL_FreeHandle(query);
3427 }
3428#else
3429 if(statsPosition[id][si] > 0)
3430 {
3431 // this method of finding the page is slightly different from the weapons and scores menu because
3432 // this listing is 0-based, because we use the 0th index to display the record win streak. also,
3433 // because we use negative numbers for the teamplay stats index.
3434
3435 new negative = (page[id] < 0);
3436 page[id] = floatround(float(statsPosition[id][si]) / float(playersPerPage),floatround_floor) + 1;
3437 if(negative) page[id] *= -1;
3438 }
3439#endif
3440 show_top10_menu(id);
3441 }
3442
3443 // 4. Regular Stats / Teamplay Stats
3444 else if(key == 3)
3445 {
3446 page[id] *= -1;
3447 show_top10_menu(id);
3448 }
3449
3450 // 0. Close
3451 // do nothing, menu closes automatically
3452}
3453
3454// show the weapon list menu
3455show_weapons_menu(id)
3456{
3457 new totalWeapons = weaponNum, wpnsPerPage = 10;
3458 new pageTotal = floatround(float(totalWeapons) / float(wpnsPerPage),floatround_ceil);
3459
3460 if(page[id] < 1) page[id] = 1;
3461 if(page[id] > pageTotal) page[id] = pageTotal;
3462
3463 new len = formatex(menuText,511,"\y%L %L (%i/%i)\w^n",id,"GUNGAME",id,"WEAPONS",page[id],pageTotal);
3464 //len += formatex(menuText[len],511-len,"\d-----------\w^n");
3465
3466 new start = (wpnsPerPage * (page[id]-1)) + 1, i;
3467
3468 // are there any custom kill requirements?
3469 new customKills, Float:expected, Float:killsperlvl = get_pcvar_float(gg_kills_per_lvl);
3470 for(i=0;i<weaponNum;i++)
3471 {
3472 if(equal(weaponName[i],KNIFE) || equal(weaponName[i],HEGRENADE)) expected = 1.0;
3473 else expected = killsperlvl;
3474
3475 if(weaponGoal[i] != expected)
3476 {
3477 customKills = 1;
3478 break;
3479 }
3480 }
3481
3482 for(i=start;i<start+wpnsPerPage;i++)
3483 {
3484 if(i > totalWeapons) break;
3485
3486 if(customKills)
3487 len += formatex(menuText[len],511-len,"%s%L %i: %s (%i)^n",(i == level[id]) ? "\r" : "\w",id,"LEVEL",i,weaponName[i-1],get_level_goal(i));
3488 else
3489 len += formatex(menuText[len],511-len,"%s%L %i: %s^n",(i == level[id]) ? "\r" : "\w",id,"LEVEL",i,weaponName[i-1]);
3490 }
3491
3492 len += formatex(menuText[len],511-len,"\d-----------\w^n");
3493
3494 new keys = MENU_KEY_0;
3495
3496 if(page[id] > 1)
3497 {
3498 len += formatex(menuText[len],511-len,"1. %L^n",id,"PREVIOUS");
3499 keys |= MENU_KEY_1;
3500 }
3501 if(page[id] < pageTotal)
3502 {
3503 len += formatex(menuText[len],511-len,"2. %L^n",id,"NEXT");
3504 keys |= MENU_KEY_2;
3505 }
3506
3507 len += formatex(menuText[len],511-len,"3. %L^n",id,"JUMP_TO_ME");
3508 keys |= MENU_KEY_3;
3509
3510 len += formatex(menuText[len],511-len,"0. %L",id,"CLOSE");
3511
3512 show_menu(id,keys,menuText,-1,"weapons_menu");
3513}
3514
3515// someone pressed a key on the weapon list menu page
3516public weapons_menu_handler(id,key)
3517{
3518 new wpnsPerPage = 10, pageTotal = floatround(float(weaponNum) / float(wpnsPerPage),floatround_ceil);
3519
3520 if(page[id] < 1 || page[id] > pageTotal) return;
3521
3522 // 1. Previous
3523 if(key == 0)
3524 {
3525 page[id]--;
3526 show_weapons_menu(id);
3527 return;
3528 }
3529
3530 // 2. Next
3531 else if(key == 1)
3532 {
3533 page[id]++;
3534 show_weapons_menu(id);
3535 return;
3536 }
3537
3538 // 3. Jump to me
3539 else if(key == 2)
3540 {
3541 page[id] = clamp(floatround(float(level[id]) / float(wpnsPerPage),floatround_ceil),1,pageTotal);
3542 show_weapons_menu(id);
3543 }
3544
3545 // 0. Close
3546 // do nothing, menu closes automatically
3547}
3548
3549// show the score list menu
3550show_scores_menu(id)
3551{
3552#if !defined SQL
3553 recheck_stats_sorting();
3554#endif
3555
3556 new keys, len, teamplay = get_pcvar_num(gg_teamplay);
3557
3558 if(teamplay)
3559 {
3560 if(page[id] != 1) page[id] = 1;
3561
3562 new leader = teamplay_get_lead_team(), otherTeam = (leader == 1) ? 2 : 1;
3563 new displayWeapon[24], teamName[10];
3564
3565 len = formatex(menuText,511,"\y%L %L (%i/%i)\w^n",id,"GUNGAME",id,"SCORES",page[id],1);
3566
3567 new team, myTeam = _:cs_get_user_team(id);
3568 for(team=leader;team>0;team=otherTeam)
3569 {
3570 if(teamLevel[team] && teamLvlWeapon[team][0]) copy(displayWeapon,23,teamLvlWeapon[team]);
3571 else formatex(displayWeapon,23,"%L",id,"NONE");
3572
3573 get_team_name(CsTeams:team,teamName,9);
3574 len += formatex(menuText[len],511-len,"%s#%i %s %L, %L %i (%s) %i/%i^n",(team == myTeam) ? "\r" : "\w",(team == leader) ? 1 : 2,teamName,id,"TEAM",id,"LEVEL",teamLevel[team],displayWeapon,teamScore[team],teamplay_get_team_goal(team));
3575
3576 // finished
3577 if(team == otherTeam) break;
3578 }
3579
3580 // nice separator!
3581 len += formatex(menuText[len],511-len,"\d-----------\w^n");
3582
3583 keys = MENU_KEY_0;
3584 len += formatex(menuText[len],511-len,"0. %L",id,"CLOSE");
3585 }
3586 else
3587 {
3588 new totalPlayers = get_playersnum(), playersPerPage = 7, stats_mode = get_pcvar_num(gg_stats_mode);
3589 new pageTotal = floatround(float(totalPlayers) / float(playersPerPage),floatround_ceil);
3590
3591 if(page[id] < 1) page[id] = 1;
3592 if(page[id] > pageTotal) page[id] = pageTotal;
3593
3594 new players[32], num;
3595 get_players(players,num);
3596
3597 // order by highest level first
3598 SortCustom1D(players,num,"score_custom_compare");
3599
3600 if(get_pcvar_num(gg_stats_split)) len = formatex(menuText,511,"\y%L %L (%i/%i) %L\w^n",id,"GUNGAME",id,"SCORES",page[id],pageTotal,id,"STATS_REGULAR");
3601 else len = formatex(menuText,511,"\y%L %L (%i/%i)\w^n",id,"GUNGAME",id,"SCORES",page[id],pageTotal);
3602 //len += formatex(menuText[len],511-len,"\d-----------\w^n");
3603
3604 new start = (playersPerPage * (page[id]-1)), i, name[32], player, authid[32];
3605 new stats_ip = get_pcvar_num(gg_stats_ip), displayWeapon[24], statsSuffix[3];
3606 new si = get_gg_si();
3607
3608 for(i=start;i<start+playersPerPage;i++)
3609 {
3610 if(i >= totalPlayers) break;
3611
3612 player = players[i];
3613 get_user_name(player,name,31);
3614
3615 if(level[player] && lvlWeapon[player][0]) copy(displayWeapon,23,lvlWeapon[player]);
3616 else formatex(displayWeapon,23,"%L",id,"NONE");
3617
3618 if(sqlInit && stats_mode)
3619 {
3620 get_gg_authid(player,authid,31,stats_ip);
3621 stats_get_data(authid,playerStats[player],player);
3622
3623 if(statsPosition[player][si] > 0)
3624 {
3625 get_number_suffix(statsPosition[player][si],statsSuffix,2);
3626 len += formatex(menuText[len],511-len,"%s#%i %s, %L %i (%s) %i/%i, %i %L (%i%s)^n",(player == id) ? "\r" : "\w",i+1,name,id,"LEVEL",level[player],displayWeapon,score[player],get_level_goal(level[player],player),(stats_mode == 1) ? playerStats[player][sdWins][si] : playerStats[player][sdPoints][si],id,(stats_mode == 1) ? "WINS" : "POINTS_ABBR",statsPosition[player][si],statsSuffix);
3627 }
3628 else len += formatex(menuText[len],511-len,"%s#%i %s, %L %i (%s) %i/%i, %i %L^n",(player == id) ? "\r" : "\w",i+1,name,id,"LEVEL",level[player],displayWeapon,score[player],get_level_goal(level[player],player),(stats_mode == 1) ? playerStats[player][sdWins][si] : playerStats[player][sdPoints][si],id,(stats_mode == 1) ? "WINS" : "POINTS_ABBR");
3629 }
3630 else len += formatex(menuText[len],511-len,"#%i %s, %L %i (%s) %i/%i^n",i+1,name,id,"LEVEL",level[player],displayWeapon,score[player],get_level_goal(level[player],player));
3631 }
3632
3633 len += formatex(menuText[len],511-len,"\d-----------\w^n");
3634
3635 keys = MENU_KEY_0;
3636
3637 if(page[id] > 1)
3638 {
3639 len += formatex(menuText[len],511-len,"1. %L^n",id,"PREVIOUS");
3640 keys |= MENU_KEY_1;
3641 }
3642 if(page[id] < pageTotal)
3643 {
3644 len += formatex(menuText[len],511-len,"2. %L^n",id,"NEXT");
3645 keys |= MENU_KEY_2;
3646 }
3647
3648 len += formatex(menuText[len],511-len,"3. %L^n",id,"JUMP_TO_ME");
3649 keys |= MENU_KEY_3;
3650
3651 len += formatex(menuText[len],511-len,"0. %L",id,"CLOSE");
3652 }
3653
3654 show_menu(id,keys,menuText,-1,"scores_menu");
3655}
3656
3657// sort list of players with their level first
3658public score_custom_compare(elem1,elem2)
3659{
3660 // invalid players
3661 if(elem1 < 1 || elem1 > 32 || elem2 < 1 || elem2 > 32)
3662 return 0;
3663
3664 // tied levels, compare scores
3665 if(level[elem1] == level[elem2])
3666 {
3667 if(score[elem1] > score[elem2]) return -1;
3668 else if(score[elem1] < score[elem2]) return 1;
3669 else return 0;
3670 }
3671
3672 // compare levels
3673 else if(level[elem1] > level[elem2]) return -1;
3674 else if(level[elem1] < level[elem2]) return 1;
3675
3676 return 0; // equal
3677}
3678
3679// someone pressed a key on the score list menu page
3680public scores_menu_handler(id,key)
3681{
3682 new totalPlayers = get_playersnum(), playersPerPage = 7;
3683 new pageTotal = floatround(float(totalPlayers) / float(playersPerPage),floatround_ceil);
3684
3685 if(page[id] < 1 || page[id] > pageTotal) return;
3686
3687 // 1. Previous
3688 if(key == 0)
3689 {
3690 page[id]--;
3691 show_scores_menu(id);
3692 return;
3693 }
3694
3695 // 2. Next
3696 else if(key == 1)
3697 {
3698 page[id]++;
3699 show_scores_menu(id);
3700 return;
3701 }
3702
3703 // 3. Jump to me
3704 else if(key == 2)
3705 {
3706 new players[32], num, i;
3707 get_players(players,num);
3708 SortCustom1D(players,num,"score_custom_compare");
3709
3710 for(i=0;i<num;i++)
3711 {
3712 if(players[i] == id) break;
3713 }
3714
3715 page[id] = floatround(float(i+1) / float(playersPerPage),floatround_ceil);
3716 show_scores_menu(id);
3717 }
3718
3719 // 0. Close
3720 // do nothing, menu closes automatically
3721}
3722
3723//**********************************************************************
3724// MAIN FUNCTIONS
3725//**********************************************************************
3726
3727// toggle the status of gungame
3728public toggle_gungame(taskid)
3729{
3730 new status = taskid-TASK_TOGGLE_GUNGAME, i;
3731
3732 // clear player tasks and values
3733 for(i=1;i<=32;i++) clear_values(i);
3734
3735 clear_team_values(1);
3736 clear_team_values(2);
3737
3738 // clear temp saves
3739 for(i=0;i<TEMP_SAVES;i++) clear_save(TASK_CLEAR_SAVE+i);
3740
3741 if(status == TOGGLE_FORCE || status == TOGGLE_ENABLE)
3742 {
3743 // these allow toggling of GunGame on/off
3744 exec_gg_config_file(0,1); // run the regular config file
3745 if(get_pcvar_num(gg_teamplay)) exec_gg_config_file(1,1); // if teamplay, run the teamplay config file also
3746 }
3747
3748 // set to what we chose from amx_gungame
3749 if(status != TOGGLE_FORCE)
3750 {
3751 set_pcvar_num(gg_enabled,status);
3752 ggActive = status;
3753 }
3754 else ggActive = get_pcvar_num(gg_enabled); // otherwise see what this is after the configs have been run
3755
3756 // run appropiate cvars
3757 map_start_cvars(0); // this sets up weapon order
3758
3759 // reset some things
3760 if(!ggActive)
3761 {
3762 // set armouries to be solid again
3763 show_armory_entitys();
3764
3765 // clear HUD message
3766 if(warmup > 0) ClearSyncHud(0,hudSyncWarmup);
3767
3768 warmup = -1;
3769 warmupWeapon[0] = 0;
3770 voted = 0;
3771 won = 0;
3772
3773 remove_task(TASK_WARMUP_CHECK);
3774 }
3775
3776 // we need to get these stats (GunGame is on, we don't have them, and we aren't in the process of getting them)
3777#if defined SQL
3778 if(ggActive && !task_exists(TASK_GET_TOP_PLAYERS)) stats_get_top_players(); // there is no statsArray for SQL
3779#else
3780 if(ggActive && !statsArray && !task_exists(TASK_GET_TOP_PLAYERS)) stats_get_top_players();
3781#endif
3782
3783 // game_player_equip
3784 manage_equips();
3785
3786 // start (or stop) the leader display
3787 remove_task(TASK_LEADER_DISPLAY);
3788 show_leader_display();
3789
3790 // warmup weapon may've change
3791 if(warmup > 0) get_pcvar_string(gg_warmup_weapon,warmupWeapon,23);
3792
3793#if defined SQL
3794 // fire up the engines!!
3795 if(!sqlInit)
3796 {
3797 sql_init();
3798
3799 // because we can't refresh timestamps before SQL is initiated, refresh timestamps for people who joined before this
3800#if defined REFRESH_TIMESTAMP_ON_JOIN
3801 new authid[32];
3802 for(new i=1;i<=maxPlayers;i++)
3803 {
3804 if(is_user_connected(i))
3805 {
3806 get_gg_authid(i,authid,31);
3807 stats_refresh_timestamp(authid);
3808 }
3809 }
3810#endif // REFRESH_TIMESTAMP_ON_JOIN
3811 }
3812#endif // SQL
3813}
3814
3815// run cvars that should be run on map start
3816//
3817// see declaration of d_rOrder for explanation of keepTeamplay
3818public map_start_cvars(keepTeamplay)
3819{
3820 new setup[512];
3821
3822 // gungame is disabled, run endmap_setup
3823 if(!ggActive)
3824 {
3825 get_pcvar_string(gg_endmap_setup,setup,511);
3826 if(setup[0]) server_cmd(setup);
3827 }
3828 else
3829 {
3830 // run map setup
3831 get_pcvar_string(gg_map_setup,setup,511);
3832 if(setup[0]) server_cmd(setup);
3833
3834 do_rOrder(keepTeamplay); // also does random teamplay
3835 setup_weapon_order();
3836
3837 // random win sounds
3838 currentWinSound = do_rWinSound();
3839 }
3840}
3841
3842precache_sounds_from_config()
3843{
3844 new cfgFile[64], command[WINSOUNDS_SIZE+32], cvar[32], value[WINSOUNDS_SIZE], file, i, pos, len;
3845 for(i=0;i<2;i++)
3846 {
3847 get_gg_config_file(i,cfgFile,63);
3848
3849 if(cfgFile[0] && file_exists(cfgFile))
3850 {
3851 file = fopen(cfgFile,"rt");
3852 while(file && !feof(file))
3853 {
3854 fgets(file,command,WINSOUNDS_SIZE+31);
3855 len = strlen(command) - 2;
3856
3857 // stop at coding-style (//) comments
3858 for(pos=0;pos<len;pos++)
3859 {
3860 if(command[pos] == '/' && command[pos+1] == '/')
3861 {
3862 copy(command,pos,command);
3863 break;
3864 }
3865 }
3866
3867 // this is a sound
3868 if(equal(command,"gg_sound_",9))
3869 {
3870 parse(command,cvar,31,value,WINSOUNDS_SIZE-1);
3871
3872 // gg_sound_winner might contain multiple sounds
3873 if(command[9] == 'w' && command[10] == 'i' && value[0]) // check w AND i, thanks Tomek Kalkowski
3874 {
3875 new temp[MAX_WINSOUND_LEN+1];
3876 while(numWinSounds < MAX_WINSOUNDS)
3877 {
3878 pos = contain_char(value,';');
3879
3880 // no more after this, precache what we have left
3881 if(pos == -1)
3882 {
3883 if(value[0])
3884 {
3885 precache_sound_special(value);
3886 copy(winSounds[numWinSounds++],MAX_WINSOUND_LEN,value);
3887 }
3888 break;
3889 }
3890
3891 // copy up to the semicolon and precache that
3892 copy(temp,pos,value);
3893
3894 if(temp[0])
3895 {
3896 precache_sound_special(temp);
3897 copy(winSounds[numWinSounds++],MAX_WINSOUND_LEN,temp);
3898 }
3899
3900 // copy everything after the semicolon
3901 copy(value,WINSOUNDS_SIZE-1,value[pos+1]);
3902 }
3903 }
3904 else precache_sound_special(value);
3905 }
3906 }
3907 if(file) fclose(file);
3908 }
3909 }
3910}
3911
3912// manage stats pruning
3913public manage_pruning()
3914{
3915 // stats disabled/file doesn't exist/pruning disabled
3916 if(!sqlInit || !get_pcvar_num(gg_stats_mode) || !get_pcvar_num(gg_stats_prune)) return;
3917
3918#if !defined SQL
3919 get_pcvar_string(gg_stats_file,sfFile,63);
3920 if(!file_exists(sfFile)) return; // no existy
3921#endif
3922
3923 // get how many plugin loads more until we prune
3924 new prune_in_str[3], prune_in;
3925 get_localinfo("gg_prune_in",prune_in_str,2);
3926 prune_in = str_to_num(prune_in_str);
3927
3928 // localinfo not set yet
3929 if(prune_in <= 0)
3930 {
3931 set_localinfo("gg_prune_in","9");
3932 return;
3933 }
3934
3935 // time to prune
3936 if(prune_in == 1)
3937 {
3938 // result is -1 for a threaded query, so wait for results until we display
3939 new result = stats_prune();
3940 if(result != -1)
3941 {
3942#if defined SQL
3943 log_amx("%L",LANG_SERVER,"PRUNING",sqlTable,result);
3944#else
3945 log_amx("%L",LANG_SERVER,"PRUNING",sfFile,result);
3946#endif
3947 }
3948
3949 set_localinfo("gg_prune_in","10"); // reset our prune count
3950 return;
3951 }
3952
3953 // decrement our count
3954 num_to_str(prune_in-1,prune_in_str,2);
3955 set_localinfo("gg_prune_in",prune_in_str);
3956}
3957
3958// manage warmup mode
3959public warmup_check(taskid)
3960{
3961 warmup--;
3962 set_hudmessage(255,255,255,-1.0,0.4,0,6.0,1.0,0.1,0.2);
3963
3964 if(warmup <= 0)
3965 {
3966 warmup = -13;
3967 warmupWeapon[0] = 0;
3968
3969 ShowSyncHudMsg(0,hudSyncWarmup,"%L",LANG_PLAYER,"WARMUP_ROUND_OVER");
3970 restart_round(1);
3971
3972 return;
3973 }
3974
3975 ShowSyncHudMsg(0,hudSyncWarmup,"%L",LANG_PLAYER,"WARMUP_ROUND_DISPLAY",warmup);
3976 set_task(1.0,"warmup_check",taskid);
3977}
3978
3979// show the leader display
3980public show_leader_display()
3981{
3982 static Float:lastDisplay, lastLeader, lastLevel, leaderName[32];
3983
3984 if(!ggActive || !get_pcvar_num(gg_leader_display))
3985 {
3986 remove_task(TASK_LEADER_DISPLAY);
3987 return 0;
3988 }
3989
3990 // keep it going
3991 if(!task_exists(TASK_LEADER_DISPLAY))
3992 set_task(LEADER_DISPLAY_RATE,"show_leader_display",TASK_LEADER_DISPLAY,_,_,"b");
3993
3994 // don't show during warmup or game over
3995 if(warmup > 0 || won) return 0;
3996
3997 new leaderLevel, numLeaders, leader, teamplay = get_pcvar_num(gg_teamplay);
3998
3999 if(teamplay) leader = teamplay_get_lead_team(leaderLevel,numLeaders);
4000 else leader = get_leader(leaderLevel,numLeaders);
4001
4002 if(!leader || leaderLevel <= 0) return 0;
4003
4004 // we just displayed the same message, don't flood
4005 new Float:now = get_gametime();
4006 if(lastLevel == leaderLevel && lastLeader == leader && lastDisplay == now) return 0;
4007
4008 // remember for later
4009 lastDisplay = now;
4010 lastLeader = leader;
4011 lastLevel = leaderLevel;
4012
4013 if(teamplay) get_team_name(CsTeams:leader,leaderName,9);
4014 else get_user_name(leader,leaderName,31);
4015
4016 set_hudmessage(200,200,200,get_pcvar_float(gg_leader_display_x),get_pcvar_float(gg_leader_display_y),_,_,LEADER_DISPLAY_RATE+0.5,0.0,0.0);
4017
4018 if(numLeaders > 1)
4019 {
4020 if(teamplay)
4021 {
4022 static otherName[10];
4023 get_team_name((leader == 1) ? CS_TEAM_CT : CS_TEAM_T,otherName,9);
4024
4025 ShowSyncHudMsg(0,hudSyncLDisplay,"%L: %s + %s (%i - %s)",LANG_PLAYER,"LEADER",leaderName,otherName,leaderLevel,teamLvlWeapon[leader])
4026 }
4027 else ShowSyncHudMsg(0,hudSyncLDisplay,"%L: %s +%i (%i - %s)",LANG_PLAYER,"LEADER",leaderName,numLeaders-1,leaderLevel,lvlWeapon[leader]);
4028 }
4029 else ShowSyncHudMsg(0,hudSyncLDisplay,"%L: %s (%i - %s)",LANG_PLAYER,"LEADER",leaderName,leaderLevel,(teamplay) ? teamLvlWeapon[leader] : lvlWeapon[leader]);
4030
4031 return 1;
4032}
4033
4034// show the nice HUD progress display
4035show_progress_display(id)
4036{
4037 static statusString[48];
4038
4039 // weapon-specific warmup
4040 if(warmup > 0 && warmupWeapon[0]) return;
4041
4042 new teamplay = get_pcvar_num(gg_teamplay);
4043
4044 // old-school: sweet and simple
4045 if((get_pcvar_num(gg_messages) & MSGS_CLASSIC))
4046 {
4047 new goal;
4048 if(teamplay) goal = get_level_goal(teamLevel[_:cs_get_user_team(id)],id);
4049 else goal = get_level_goal(level[id],id);
4050
4051 gungame_print(id,0,1,"%L %%n%i%%e :: %%n%s%%e",id,(teamplay) ? "ON_LEVEL_TEAM" : "ON_LEVEL",level[id],lvlWeapon[id]);
4052 gungame_print(id,0,1,"%L",id,"PROGRESS_DISPLAY",goal-score[id],score[id],goal);
4053
4054 return;
4055 }
4056
4057 if(teamplay)
4058 {
4059 new team = _:cs_get_user_team(id), otherTeam = (team == 1) ? 2 : 1;
4060 if(team != 1 && team != 2) return;
4061
4062 new leaderLevel, numLeaders, leader = teamplay_get_lead_team(leaderLevel,numLeaders);
4063
4064 // tied
4065 if(numLeaders > 1) formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY_TEAM2");
4066
4067 // leading
4068 else if(leader == team) formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY_TEAM1",teamLevel[team]-teamLevel[otherTeam]);
4069
4070 // losing
4071 else formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY_TEAM3",teamLevel[otherTeam]-teamLevel[team]);
4072 }
4073 else
4074 {
4075 new leaderLevel, numLeaders, runnerUp;
4076 new leader = get_leader(leaderLevel,numLeaders,runnerUp);
4077
4078 if(level[id] == leaderLevel)
4079 {
4080 if(numLeaders == 1) formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY1",leaderLevel-level[runnerUp]);
4081 else if(numLeaders == 2)
4082 {
4083 new otherLeader;
4084 if(leader != id) otherLeader = leader;
4085 else
4086 {
4087 new player;
4088 for(player=1;player<=maxPlayers;player++)
4089 {
4090 if(is_user_connected(player) && level[player] == leaderLevel && player != id)
4091 {
4092 otherLeader = player;
4093 break;
4094 }
4095 }
4096 }
4097
4098 static otherName[32];
4099 get_user_name(otherLeader,otherName,31);
4100
4101 formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY2",otherName);
4102 }
4103 else
4104 {
4105 static numWord[16], lang[3];
4106
4107 // if english, use words, otherwise use digits
4108 get_user_info(id,"lang",lang,2);
4109 if(equali(lang,"en"))
4110 {
4111 num_to_word(numLeaders-1,numWord,15);
4112 trim(numWord);
4113 }
4114 else formatex(numWord,15,"%i",numLeaders-1);
4115
4116 formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY3",numWord);
4117 }
4118 }
4119 else formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY4",leaderLevel-level[id]);
4120 }
4121
4122 gungame_hudmessage(id,5.0,"%L %i (%s)^n%s",id,(teamplay) ? "ON_LEVEL_TEAM" : "ON_LEVEL",level[id],lvlWeapon[id],statusString);
4123}
4124
4125// play the taken/tied/lost lead sounds
4126public play_lead_sounds(id,oldLevel,Float:playDelay)
4127{
4128 // id: the player whose level changed
4129 // oldLevel: his level before it changed
4130 // playDelay: how long to wait until we play id's sounds
4131
4132 if(get_pcvar_num(gg_teamplay))
4133 {
4134 // redirect to other function
4135 teamplay_play_lead_sounds(id,oldLevel,Float:playDelay);
4136 return;
4137 }
4138
4139 // warmup or game over, no one cares
4140 if(warmup > 0 || won) return;
4141
4142 // no level change
4143 if(level[id] == oldLevel) return;
4144
4145 //
4146 // monitor MY stuff first
4147 //
4148
4149 new leaderLevel, numLeaders;
4150 get_leader(leaderLevel,numLeaders);
4151
4152 // I'm now on the leader level
4153 if(level[id] == leaderLevel)
4154 {
4155 // someone else here?
4156 if(numLeaders > 1)
4157 {
4158 new params[2];
4159 params[0] = id;
4160 params[1] = gg_sound_tiedlead;
4161
4162 remove_task(TASK_PLAY_LEAD_SOUNDS+id);
4163 set_task(playDelay,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+id,params,2);
4164 }
4165
4166 // just me, I'm the winner!
4167 else
4168 {
4169 // did I just pass someone?
4170 if(level[id] > oldLevel && num_players_on_level(oldLevel))
4171 {
4172 new params[2];
4173 params[0] = id;
4174 params[1] = gg_sound_takenlead;
4175
4176 remove_task(TASK_PLAY_LEAD_SOUNDS+id);
4177 set_task(playDelay,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+id,params,2);
4178 }
4179 }
4180 }
4181
4182 // WAS I on the leader level?
4183 else if(oldLevel == leaderLevel)
4184 {
4185 new params[2];
4186 params[0] = id;
4187 params[1] = gg_sound_lostlead;
4188
4189 remove_task(TASK_PLAY_LEAD_SOUNDS+id);
4190 set_task(playDelay,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+id,params,2);
4191
4192 //return; // will not effect other players
4193 }
4194
4195 // nothing of importance
4196 else return; // will not effect other players
4197
4198 //
4199 // now monitor other players.
4200 // if we get this far, id is now in the lead level
4201 //
4202
4203 new player;
4204 for(player=1;player<=maxPlayers;player++)
4205 {
4206 if(!is_user_connected(player) || player == id) continue;
4207
4208 // PLAYER tied with ID
4209 if(level[player] == level[id])
4210 {
4211 // don't tell him if he already got it from another player
4212 if(num_players_on_level(level[id]) <= 2
4213 || (oldLevel > level[id] && leaderLevel == level[id])) // dropped into tied position
4214 {
4215 new params[2];
4216 params[0] = player;
4217 params[1] = gg_sound_tiedlead;
4218
4219 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
4220 set_task(0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
4221 }
4222
4223 continue;
4224 }
4225
4226 // PLAYER passed by ID
4227 else if(level[id] > level[player] && level[player] == oldLevel)
4228 {
4229 // don't tell him if he already got it from another player
4230 if(num_players_on_level(level[id]) <= 1)
4231 {
4232 new params[2];
4233 params[0] = player;
4234 params[1] = gg_sound_lostlead;
4235
4236 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
4237 set_task(0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
4238 }
4239
4240 continue;
4241 }
4242
4243 // ID passed by PLAYER
4244 else if(level[player] > level[id] && leaderLevel == level[player])
4245 {
4246 // I stand alone!
4247 if(num_players_on_level(level[player]) <= 1)
4248 {
4249 new params[2];
4250 params[0] = player;
4251 params[1] = gg_sound_takenlead;
4252
4253 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
4254 set_task(0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
4255 }
4256
4257 continue;
4258 }
4259 }
4260}
4261
4262// manage game_player_equip and player_weaponstrip entities
4263public manage_equips()
4264{
4265 static classname[20], targetname[24];
4266 new ent, i, block_equips = get_pcvar_num(gg_block_equips), enabled = ggActive;
4267
4268 // go through both entities to monitor
4269 for(i=0;i<4;i++)
4270 {
4271 // get classname for current iteration
4272 switch(i)
4273 {
4274 case 0: classname = "game_player_equip";
4275 case 1: classname = "game_player_equip2";
4276 case 2: classname = "player_weaponstrip";
4277 default: classname = "player_weaponstrip2";
4278 }
4279
4280 // go through whatever entity
4281 ent = 0;
4282 while((ent = fm_find_ent_by_class(ent,classname)))
4283 {
4284 // allowed to have this, reverse possible changes
4285 if(!enabled || !block_equips || (i >= 2 && block_equips < 2)) // player_weaponstrip switch
4286 {
4287 pev(ent,pev_targetname,targetname,23);
4288
4289 // this one was blocked
4290 if(equal(targetname,"gg_block_equips"))
4291 {
4292 pev(ent,TNAME_SAVE,targetname,23);
4293
4294 set_pev(ent,pev_targetname,targetname);
4295 set_pev(ent,TNAME_SAVE,"");
4296
4297 switch(i)
4298 {
4299 case 0, 1: set_pev(ent,pev_classname,"game_player_equip");
4300 default: set_pev(ent,pev_classname,"player_weaponstrip");
4301 }
4302 }
4303 }
4304
4305 // not allowed to pickup others, make possible changes
4306 else
4307 {
4308 pev(ent,pev_targetname,targetname,23);
4309
4310 // needs to be blocked, but hasn't been yet
4311 if(targetname[0] && !equal(targetname,"gg_block_equips"))
4312 {
4313 set_pev(ent,TNAME_SAVE,targetname);
4314 set_pev(ent,pev_targetname,"gg_block_equips");
4315
4316 // classname change is required sometimes for some reason
4317 switch(i)
4318 {
4319 case 0, 1: set_pev(ent,pev_classname,"game_player_equip2");
4320 default: set_pev(ent,pev_classname,"player_weaponstrip2");
4321 }
4322 }
4323 }
4324 }
4325 }
4326}
4327
4328// someone respawned
4329spawned(id)
4330{
4331 // should be filtered in ham hook
4332 if(/*!ggActive || !is_user_connected(id) ||*/ !on_valid_team(id))
4333 return;
4334
4335 remove_task(TASK_CHECK_DEATHMATCH+id);
4336
4337 // should be frozen?
4338 if(won)
4339 {
4340 new iterations = get_pcvar_num(gg_map_iterations);
4341 if(mapIteration < iterations || !iterations)
4342 {
4343 // not done yet, just freeze players
4344 set_pev(id,pev_flags,pev(id,pev_flags) | FL_FROZEN);
4345 fm_set_user_godmode(id,1);
4346 }
4347
4348 // done, make sure HUD is hidden
4349 emessage_begin(MSG_ALL,gmsgHideWeapon);
4350 ewrite_byte((1<<0)|(1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<6)); // can't use (1<<2) or text disappears
4351 emessage_end();
4352
4353 emessage_begin(MSG_ALL,gmsgCrosshair);
4354 ewrite_byte(0); // hide
4355 emessage_end();
4356
4357 return;
4358 }
4359
4360 if(get_pcvar_num(gg_pickup_others)) strip_starting_pistols(id);
4361
4362 afkCheck[id] = 0;
4363 levelsThisRound[id] = 0;
4364
4365 // just joined
4366 if(!level[id])
4367 {
4368 new teamplay = get_pcvar_num(gg_teamplay);
4369
4370 // warming up
4371 if(warmup > 0 && !teamplay)
4372 {
4373 change_level(id,1,1,_,1); // just joined, always score
4374 }
4375 else
4376 {
4377 // handicap
4378 new handicapMode = get_pcvar_num(gg_handicap_on);
4379 if(handicapMode && !teamplay)
4380 {
4381 new rcvHandicap = 1;
4382
4383 // top10 doesn't receive handicap -- also make sure we are using top10
4384 if(sqlInit && !get_pcvar_num(gg_top10_handicap) && get_pcvar_num(gg_stats_mode))
4385 {
4386 static authid[32];
4387 get_gg_authid(id,authid,31);
4388
4389 new si = get_gg_si();
4390#if defined SQL
4391 if(!statsPosition[id][si]) statsPosition[id][si] = stats_get_position(id,authid,si);
4392 if(0 < statsPosition[id][si] && statsPosition[id][si] <= TOP_PLAYERS) rcvHandicap = 0; // I'm in the top10
4393#else
4394 for(new i=0;i<TOP_PLAYERS;i++)
4395 {
4396 if(i >= statsSize[si]) continue;
4397 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[si],i),sfStatsStruct);
4398
4399 // I'm in top10, don't give me handicap
4400 if(equal(authid,sfStatsStruct[sdAuthid]))
4401 {
4402 rcvHandicap = 0;
4403 break;
4404 }
4405 }
4406#endif
4407 }
4408
4409 if(rcvHandicap)
4410 {
4411 new player;
4412
4413 // find lowest level (don't use bots unless we have to)
4414 if(handicapMode == 2)
4415 {
4416 new isBot, myLevel, lowestLevel, lowestBotLevel;
4417 for(player=1;player<=maxPlayers;player++)
4418 {
4419 if(!is_user_connected(player) || player == id)
4420 continue;
4421
4422 isBot = is_user_bot(player);
4423 myLevel = level[player];
4424
4425 if(!myLevel) continue;
4426
4427 if(!isBot && (!lowestLevel || myLevel < lowestLevel))
4428 lowestLevel = myLevel;
4429 else if(isBot && (!lowestBotLevel || myLevel < lowestBotLevel))
4430 lowestBotLevel = myLevel;
4431 }
4432
4433 // CLAMP!
4434 if(!lowestLevel) lowestLevel = 1;
4435 if(!lowestBotLevel) lowestBotLevel = 1;
4436
4437 change_level(id,(lowestLevel > 1) ? lowestLevel : lowestBotLevel,1,_,1); // just joined, always score
4438 }
4439
4440 // find average level
4441 else
4442 {
4443 new Float:average, num;
4444 for(player=1;player<=maxPlayers;player++)
4445 {
4446 if(is_user_connected(player) && level[player])
4447 {
4448 average += float(level[player]);
4449 num++;
4450 }
4451 }
4452
4453 average /= float(num);
4454 change_level(id,(average >= 0.5) ? floatround(average) : 1,1,_,1); // just joined, always score
4455 }
4456 }
4457
4458 // not eligible for handicap (in top10 with gg_top10_handicap disabled)
4459 else change_level(id,1,1,_,1); // just joined, always score
4460 }
4461
4462 // no handicap enabled or playing teamplay
4463 else
4464 {
4465 if(teamplay)
4466 {
4467 new team = _:cs_get_user_team(id);
4468
4469 if(team == 1 || team == 2)
4470 {
4471 // my team has a level already
4472 if(teamLevel[team])
4473 {
4474 change_level(id,teamLevel[team],1,_,1,_,0); // just joined, always score, don't effect team
4475 if(score[id] != teamScore[team]) change_score(id,teamScore[team]-score[id],_,_,0); // don't effect team
4476 }
4477
4478 // my team just started
4479 else
4480 {
4481 // initialize its values
4482 teamplay_update_level(team,1,id);
4483 teamplay_update_score(team,0,id);
4484
4485 change_level(id,teamLevel[team],1,_,1,_,0); // just joined, always score, don't effect team
4486 }
4487 }
4488 }
4489
4490 // solo-play
4491 else change_level(id,1,1,_,1); // just joined, always score
4492 }
4493 }
4494 }
4495
4496 // didn't just join
4497 else
4498 {
4499 if(star[id])
4500 {
4501 end_star(TASK_END_STAR+id);
4502 remove_task(TASK_END_STAR+id);
4503 }
4504
4505 if(get_pcvar_num(gg_teamplay))
4506 {
4507 new team = _:cs_get_user_team(id);
4508
4509 // my team just started
4510 if((team == 1 || team == 2) && !teamLevel[team])
4511 {
4512 // initialize its values
4513 teamplay_update_level(team,1,id);
4514 teamplay_update_score(team,0,id);
4515
4516 change_level(id,teamLevel[team]-level[id],_,_,1,_,0); // always score, don't effect team
4517 change_score(id,teamScore[team]-score[id],_,_,0); // don't effect team
4518 }
4519 }
4520
4521 give_level_weapon(id);
4522 refill_ammo(id);
4523 }
4524
4525 // show welcome message
4526 if(!welcomed[id] && get_pcvar_num(gg_join_msg))
4527 show_welcome(id);
4528
4529 // update bomb for DM
4530 if(cs_get_user_team(id) == CS_TEAM_T && !get_pcvar_num(gg_block_objectives) && get_pcvar_num(gg_dm))
4531 {
4532 if(bombStatus[3] == BOMB_PICKEDUP)
4533 {
4534 message_begin(MSG_ONE,gmsgBombPickup,_,id);
4535 message_end();
4536 }
4537 else if(bombStatus[0] || bombStatus[1] || bombStatus[2])
4538 {
4539 message_begin(MSG_ONE,gmsgBombDrop,_,id);
4540 write_coord(bombStatus[0]);
4541 write_coord(bombStatus[1]);
4542 write_coord(bombStatus[2]);
4543 write_byte(bombStatus[3]);
4544 message_end();
4545 }
4546 }
4547
4548 if(get_pcvar_num(gg_disable_money)) hide_money(id);
4549
4550 // switch to our appropiate weapon, for those without the switch to new weapon option
4551 if((warmup > 0 && warmupWeapon[0] && equal(warmupWeapon,KNIFE)) || equal(lvlWeapon[id],KNIFE) /* || (get_pcvar_num(gg_knife_elite) && levelsThisRound[id] > 0)*/)
4552 {
4553 engclient_cmd(id,WEAPON_KNIFE);
4554 client_cmd(id,WEAPON_KNIFE);
4555 }
4556 else if(get_pcvar_num(gg_nade_glock) && equal(lvlWeapon[id],HEGRENADE))
4557 {
4558 engclient_cmd(id,WEAPON_GLOCK18);
4559 client_cmd(id,WEAPON_GLOCK18);
4560 }
4561 else if(lvlWeapon[id][0])
4562 {
4563 static wpnName[24];
4564 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
4565
4566 engclient_cmd(id,wpnName);
4567 client_cmd(id,wpnName);
4568 }
4569
4570 // remember spawn info for AFK protection
4571 if(get_pcvar_num(gg_afk_protection))
4572 {
4573 pev(id,pev_origin,spawnOrigin[id]);
4574 pev(id,pev_v_angle,spawnAngles[id]);
4575 afkCheck[id] = 1;
4576 }
4577}
4578
4579// player changed his team
4580player_teamchange(id,oldTeam,newTeam)
4581{
4582 if(!ggActive) return 0;
4583
4584 // remember for crazy team switches
4585 lastTeam[id] = newTeam;
4586
4587 // allow us to join in on deathmatch
4588 if(oldTeam == 0 && (newTeam == 1 || newTeam == 2) && !roundEnded && get_pcvar_num(gg_dm) && !task_exists(TASK_CHECK_JOINCLASS+id))
4589 {
4590 remove_task(TASK_CHECK_DEATHMATCH+id);
4591 set_task(5.0,"check_deathmatch",TASK_CHECK_DEATHMATCH+id);
4592 }
4593
4594 // keep track of time
4595 new Float:now = get_gametime();
4596 if(oldTeam == 1 || oldTeam == 2) teamTimes[id][oldTeam-1] += now - lastSwitch[id];
4597 lastSwitch[id] = now;
4598
4599 // we already have a level, set our values to our new team's
4600 if(level[id] && get_pcvar_num(gg_teamplay) && (newTeam == 1 || newTeam == 2))
4601 {
4602 // set them directly
4603 level[id] = teamLevel[newTeam];
4604 lvlWeapon[id] = teamLvlWeapon[newTeam];
4605 score[id] = teamScore[newTeam];
4606 }
4607
4608 return 1;
4609}
4610
4611// restart the round
4612public restart_round(delay)
4613{
4614 // clear values
4615 /*new player;
4616 for(player=1;player<=maxPlayers;player++)
4617 {
4618 if(is_user_connected(player)) clear_values(player,1); // ignore welcome
4619 }
4620
4621 // reset teams as well
4622 clear_team_values(1);
4623 clear_team_values(2);*/
4624
4625 if(delay < 1) delay = 1;
4626
4627 set_cvar_num("amx_restartg",delay);
4628 set_task(float(delay)-0.1,"clear_all_values");
4629}
4630
4631// select a random weapon order
4632//
4633// in cmd_gungame_teamplay we call map_start_cvars which leads to d_rOrder.
4634// when called this way we don't want to let it change teamplay or run teamplay
4635// configs, so we added the keepTeamplay parameter.
4636do_rOrder(keepTeamplay)
4637{
4638 // manage random teamplay
4639 if(initTeamplayInt == -1)
4640 {
4641 get_pcvar_string(gg_teamplay,initTeamplayStr,31);
4642 initTeamplayInt = str_to_num(initTeamplayStr[0]);
4643 }
4644
4645 new amount;
4646
4647 // if we are allowed to change teamplay, and our initial teamplay value was either a
4648 // sequence, or it was just 2 (so select one randomly), then sort through it and pick a value
4649 if(!keepTeamplay && ((amount = str_count(initTeamplayStr,',')+1) > 1 || initTeamplayInt == 2))
4650 {
4651 new info[6], rotation[32];
4652 get_localinfo("gg_tp_iter",info,5);
4653 copy(rotation,31,initTeamplayStr); // store initTeamplayStr in a variable that we can break apart, so on map end we can set gg_teamplay back to initTeamplayStr
4654
4655 new iter = str_to_num(info), teamplay;
4656
4657 if(iter <= 0 || iter > amount)
4658 {
4659 iter = 1;
4660 set_localinfo("gg_tp_iter","1");
4661 }
4662
4663 // no rotation, just use the given value
4664 if(amount <= 1)
4665 {
4666 if(iter != 1) set_localinfo("gg_tp_iter","1");
4667 // initTeamplayInt should still be set to the one we want to use
4668 }
4669 else
4670 {
4671 for(new i=1;i<=amount;i++)
4672 {
4673 if(contain(rotation,",") != -1)
4674 {
4675 strtok(rotation,info,5,rotation,31,',');
4676 if(i == iter) // this is the one we're looking for
4677 {
4678 initTeamplayInt = str_to_num(info);
4679 break;
4680 }
4681 }
4682 else // we've stripped away everything else and are left with the last one, so use it
4683 {
4684 initTeamplayInt = str_to_num(rotation);
4685 break;
4686 }
4687 }
4688
4689 iter++;
4690 if(iter > amount) iter = 1;
4691 num_to_str(iter,info,5);
4692 set_localinfo("gg_tp_iter",info);
4693 }
4694
4695 if(initTeamplayInt == 2) teamplay = random_num(0,1);
4696 else teamplay = initTeamplayInt;
4697
4698 set_pcvar_num(gg_teamplay,teamplay);
4699
4700 // re-run config files based on teamplay, don't allow toggling
4701 exec_gg_config_file(0,0);
4702 if(teamplay) exec_gg_config_file(1,0);
4703 }
4704
4705 new i, maxRandom, cvar[20], weaponOrder[(MAX_WEAPONS*16)+1];
4706 for(i=1;i<=MAX_WEAPON_ORDERS+1;i++) // +1 so we can detect final
4707 {
4708 formatex(cvar,19,"gg_weapon_order%i",i);
4709 get_cvar_string(cvar,weaponOrder,MAX_WEAPONS*16);
4710 trim(weaponOrder);
4711
4712 // found a blank one, stop here
4713 if(!weaponOrder[0])
4714 {
4715 maxRandom = i - 1;
4716 break;
4717 }
4718 }
4719
4720 // there is just one
4721 if(maxRandom == 1)
4722 {
4723 // get its weapon order and set as current
4724 formatex(cvar,19,"gg_weapon_order1");
4725 get_cvar_string(cvar,weaponOrder,MAX_WEAPONS*16);
4726 set_pcvar_string(gg_weapon_order,weaponOrder);
4727 return;
4728 }
4729
4730 // we found some random ones
4731 if(maxRandom)
4732 {
4733 new randomOrder[30], lastOIstr[6], lastOI, orderAmt;
4734 get_localinfo("gg_rand_order",randomOrder,29);
4735 get_localinfo("gg_last_oi",lastOIstr,5);
4736 lastOI = str_to_num(lastOIstr);
4737 orderAmt = get_rOrder_amount(randomOrder);
4738
4739 // no random order yet, or amount of random orders changed
4740 if(!randomOrder[0] || orderAmt != maxRandom)
4741 {
4742 shuffle_rOrder(randomOrder,29,maxRandom);
4743 lastOI = 0;
4744 }
4745
4746 // reached the end, reshuffle while avoiding this one
4747 else if(get_rOrder_index_val(orderAmt,randomOrder) == get_rOrder_index_val(lastOI,randomOrder))
4748 {
4749 shuffle_rOrder(randomOrder,29,maxRandom,lastOI);
4750 lastOI = 0;
4751 }
4752
4753 new choice = get_rOrder_index_val(lastOI+1,randomOrder);
4754
4755 // get its weapon order
4756 formatex(cvar,19,"gg_weapon_order%i",choice);
4757 get_cvar_string(cvar,weaponOrder,MAX_WEAPONS*16);
4758
4759 // set as current
4760 set_pcvar_string(gg_weapon_order,weaponOrder);
4761
4762 // remember for next time
4763 num_to_str(lastOI+1,lastOIstr,5);
4764 set_localinfo("gg_last_oi",lastOIstr);
4765 }
4766}
4767
4768// get the value of an order index in an order string
4769get_rOrder_index_val(index,randomOrder[])
4770{
4771 // only one listed
4772 if(str_count(randomOrder,',') < 1)
4773 return str_to_num(randomOrder);
4774
4775 // find preceding comma
4776 new search = str_find_num(randomOrder,',',index-1);
4777
4778 // go until succeeding comma
4779 new extract[6];
4780 copyc(extract,5,randomOrder[search+1],',');
4781
4782 return str_to_num(extract);
4783}
4784
4785// gets the amount of orders in an order string
4786get_rOrder_amount(randomOrder[])
4787{
4788 return str_count(randomOrder,',')+1;
4789}
4790
4791// shuffle up our random order
4792stock shuffle_rOrder(randomOrder[],len,maxRandom,avoid=-1)
4793{
4794 randomOrder[0] = 0;
4795
4796 // fill up array with order indexes
4797 new order[MAX_WEAPON_ORDERS], i;
4798 for(i=0;i<maxRandom;i++) order[i] = i+1;
4799
4800 // shuffle it
4801 SortCustom1D(order,maxRandom,"sort_shuffle");
4802
4803 // avoid a specific number as the starting number
4804 while(avoid > 0 && order[0] == avoid)
4805 SortCustom1D(order,maxRandom,"sort_shuffle");
4806
4807 // get them into a string
4808 for(i=0;i<maxRandom;i++)
4809 {
4810 format(randomOrder,len,"%s%s%i",randomOrder,(i>0) ? "," : "",order[i]);
4811 set_localinfo("gg_rand_order",randomOrder);
4812 }
4813}
4814
4815// play a random win sound
4816do_rWinSound()
4817{
4818 // just one, no one cares
4819 if(numWinSounds <= 1)
4820 {
4821 return 0; // 1 minus 1
4822 }
4823
4824 new randomOrder[30], lastWSIstr[6], lastWSI, orderAmt;
4825 get_localinfo("gg_winsound_order",randomOrder,29);
4826 get_localinfo("gg_last_wsi",lastWSIstr,5);
4827 lastWSI = str_to_num(lastWSIstr);
4828 orderAmt = get_rWinSound_amount(randomOrder);
4829
4830 // no random order yet, or amount of random orders changed
4831 if(!randomOrder[0] || orderAmt != numWinSounds)
4832 {
4833 shuffle_rWinSound(randomOrder,29);
4834 lastWSI = 0;
4835 }
4836
4837 // reached the end, reshuffle while avoiding this one
4838 else if(get_rWinSound_index_val(orderAmt,randomOrder) == get_rWinSound_index_val(lastWSI,randomOrder))
4839 {
4840 shuffle_rWinSound(randomOrder,29,lastWSI);
4841 lastWSI = 0;
4842 }
4843
4844 new choice = get_rWinSound_index_val(lastWSI+1,randomOrder);
4845
4846 // remember for next time
4847 num_to_str(lastWSI+1,lastWSIstr,5);
4848 set_localinfo("gg_last_wsi",lastWSIstr);
4849
4850 return choice-1;
4851}
4852
4853// get the value of an order index in an order string
4854get_rWinSound_index_val(index,randomOrder[])
4855{
4856 // only one listed
4857 if(str_count(randomOrder,',') < 1)
4858 return str_to_num(randomOrder);
4859
4860 // find preceding comma
4861 new search = str_find_num(randomOrder,',',index-1);
4862
4863 // go until succeeding comma
4864 new extract[6];
4865 copyc(extract,5,randomOrder[search+1],',');
4866
4867 return str_to_num(extract);
4868}
4869
4870// gets the amount of orders in an order string
4871get_rWinSound_amount(randomOrder[])
4872{
4873 return str_count(randomOrder,',')+1;
4874}
4875
4876// shuffle up our random order
4877stock shuffle_rWinSound(randomOrder[],len,avoid=-1)
4878{
4879 randomOrder[0] = 0;
4880
4881 // fill up array with order indexes
4882 new order[MAX_WINSOUNDS], i;
4883 for(i=0;i<numWinSounds;i++) order[i] = i+1;
4884
4885 // shuffle it
4886 SortCustom1D(order,numWinSounds,"sort_shuffle");
4887
4888 // avoid a specific number as the starting number
4889 while(avoid > 0 && order[0] == avoid)
4890 SortCustom1D(order,numWinSounds,"sort_shuffle");
4891
4892 // get them into a string
4893 for(i=0;i<numWinSounds;i++)
4894 {
4895 format(randomOrder,len,"%s%s%i",randomOrder,(i>0) ? "," : "",order[i]);
4896 set_localinfo("gg_winsound_order",randomOrder);
4897 }
4898}
4899
4900// shuffle an array
4901public sort_shuffle(elem1,elem2)
4902{
4903 return random_num(-1,1);
4904}
4905
4906// clear all saved values
4907clear_values(id,ignoreWelcome=0)
4908{
4909 level[id] = 0;
4910 levelsThisRound[id] = 0;
4911 score[id] = 0;
4912 lvlWeapon[id][0] = 0;
4913 star[id] = 0;
4914 if(!ignoreWelcome) welcomed[id] = 0;
4915 page[id] = 0;
4916 lastKilled[id] = 0;
4917 respawn_timeleft[id] = 0;
4918 silenced[id] = 0;
4919 spawnSounds[id] = 1;
4920 spawnProtected[id] = 0;
4921 teamTimes[id][0] = 0.0;
4922 teamTimes[id][1] = 0.0;
4923 lastSwitch[id] = get_gametime();
4924 lastTeam[id] = 0;
4925
4926 if(c4planter == id) c4planter = 0;
4927
4928 remove_task(TASK_RESPAWN+id);
4929 remove_task(TASK_CHECK_DEATHMATCH+id);
4930 remove_task(TASK_REMOVE_PROTECTION+id);
4931
4932 if(is_user_connected(id)) fm_set_rendering(id);
4933
4934 return 1;
4935}
4936
4937// clears a TEAM's values
4938clear_team_values(team)
4939{
4940 if(team != 1 && team != 2) return;
4941
4942 teamLevel[team] = 0;
4943 teamLvlWeapon[team][0] = 0;
4944 teamScore[team] = 0;
4945}
4946
4947// possibly start a warmup round
4948start_warmup()
4949{
4950 new warmup_value = get_pcvar_num(gg_warmup_timer_setting);
4951
4952 // warmup is set to -13 after its finished if gg_warmup_multi is 0,
4953 // so this stops multiple warmups for multiple map iterations
4954 if(warmup_value > 0 && warmup != -13)
4955 {
4956 warmup = warmup_value;
4957 get_pcvar_string(gg_warmup_weapon,warmupWeapon,23);
4958 set_task(0.1,"warmup_check",TASK_WARMUP_CHECK);
4959
4960 // now that warmup is in effect, reset player weapons
4961 new player;
4962 for(player=1;player<=maxPlayers;player++)
4963 {
4964 if(is_user_connected(player))
4965 {
4966 // just joined for all intents and purposes
4967 change_level(player,-MAX_WEAPONS,1,_,1,0,0); // just joined, always score, don't play sounds, don't effect team
4968 }
4969 }
4970
4971 // a single team update instead of for everyone
4972 if(get_pcvar_num(gg_teamplay))
4973 {
4974 teamplay_update_score(1,0);
4975 teamplay_update_score(2,0);
4976 teamplay_update_level(1,1);
4977 teamplay_update_level(2,1);
4978 }
4979
4980 // clear leader display for warmup
4981 if(warmup > 0) ClearSyncHud(0,hudSyncLDisplay);
4982 }
4983}
4984
4985// refresh a player's hegrenade stock
4986public refresh_nade(taskid)
4987{
4988 new id = taskid-TASK_REFRESH_NADE;
4989
4990 // player left, player died, or GunGame turned off
4991 if(!is_user_connected(id) || !is_user_alive(id) || !ggActive) return;
4992
4993 // on the grenade level, and lacking that aforementioned thing
4994 if(equal(lvlWeapon[id],HEGRENADE) && !user_has_weapon(id,CSW_HEGRENADE))
4995 ham_give_weapon(id,WEAPON_HEGRENADE);
4996
4997 // get bots to use the grenade (doesn't work very well)
4998 if(is_user_bot(id))
4999 {
5000 engclient_cmd(id,WEAPON_HEGRENADE);
5001 client_cmd(id,WEAPON_HEGRENADE);
5002 }
5003}
5004
5005// refill a player's ammo
5006stock refill_ammo(id,current=0)
5007{
5008 if(!is_user_alive(id)) return 0;
5009
5010 // weapon-specific warmup, no ammo for knives only
5011 if(warmup > 0 && warmupWeapon[0] && equal(warmupWeapon,KNIFE))
5012 return 0;
5013
5014 // get weapon name and index
5015 static fullName[24], curWpnName[24];
5016 new wpnid, curWpnMelee, curweapon = get_user_weapon(id);
5017
5018 // re-init start of strings
5019 fullName[0] = 0;
5020 curWpnName[0] = 0;
5021
5022 // we have a valid current weapon (stupid runtime errors)
5023 if(curweapon)
5024 {
5025 get_weaponname(curweapon,curWpnName,23);
5026 curWpnMelee = equal(curWpnName,WEAPON_KNIFE);
5027 }
5028
5029 // if we are refilling our current weapon instead of our level weapon,
5030 // we actually have a current weapon, and this isn't a melee weapon or the
5031 // other alternative, our level weapon, is a melee weapon
5032 if(current && curweapon && (!curWpnMelee || equal(lvlWeapon[id],KNIFE)))
5033 {
5034 // refill our current weapon
5035 get_weaponname(curweapon,fullName,23);
5036 wpnid = curweapon;
5037 }
5038 else
5039 {
5040 // refill our level weapon
5041 formatex(fullName,23,"weapon_%s",lvlWeapon[id]);
5042 wpnid = get_weaponid(fullName);
5043
5044 // so that we know for sure
5045 current = 0;
5046 }
5047
5048 new armor = get_pcvar_num(gg_give_armor), helmet = get_pcvar_num(gg_give_helmet);
5049
5050 // giving armor and helmets away like candy
5051 if(helmet) cs_set_user_armor(id,armor,CS_ARMOR_VESTHELM);
5052 else cs_set_user_armor(id,armor,CS_ARMOR_KEVLAR);
5053
5054 // didn't find anything valid to refill somehow
5055 if(wpnid < 1 || wpnid > 30 || !fullName[0])
5056 return 0;
5057
5058 // no reason to refill a melee weapon, or a bomb.
5059 // make use of our curWpnMelee cache here
5060 if((current && curWpnMelee) || wpnid == CSW_KNIFE || wpnid == CSW_C4)
5061 return 1;
5062
5063 new ammo, wEnt;
5064 ammo = get_pcvar_num(gg_ammo_amount);
5065
5066 // don't give away hundreds of grenades
5067 if(wpnid != CSW_HEGRENADE)
5068 {
5069 // set clip ammo
5070 wEnt = get_weapon_ent(id,wpnid);
5071 if(pev_valid(wEnt)) cs_set_weapon_ammo(wEnt,maxClip[wpnid]);
5072
5073 // glock on the nade level
5074 if(wpnid == CSW_GLOCK18 && equal(lvlWeapon[id],HEGRENADE))
5075 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5076 else
5077 {
5078 // set backpack ammo
5079 if(ammo > 0) cs_set_user_bpammo(id,wpnid,ammo);
5080 else cs_set_user_bpammo(id,wpnid,maxAmmo[wpnid]);
5081 }
5082
5083 // update display if we need to
5084 if(curweapon == wpnid)
5085 {
5086 message_begin(MSG_ONE,gmsgCurWeapon,_,id);
5087 write_byte(1);
5088 write_byte(wpnid);
5089 write_byte(maxClip[wpnid]);
5090 message_end();
5091 }
5092 }
5093
5094 // now do stupid grenade stuff
5095 else
5096 {
5097 // we don't have this nade yet
5098 if(!user_has_weapon(id,wpnid))
5099 {
5100 ham_give_weapon(id,fullName);
5101 remove_task(TASK_REFRESH_NADE+id);
5102 }
5103
5104 if(get_pcvar_num(gg_nade_glock))
5105 {
5106 // set clip ammo
5107 new wEnt = get_weapon_ent(id,CSW_GLOCK18);
5108 if(pev_valid(wEnt)) cs_set_weapon_ammo(wEnt,20);
5109
5110 // set backpack ammo
5111 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5112
5113 new curweapon = get_user_weapon(id);
5114
5115 // update display if we need to
5116 if(curweapon == CSW_GLOCK18)
5117 {
5118 message_begin(MSG_ONE,gmsgCurWeapon,_,id);
5119 write_byte(1);
5120 write_byte(CSW_GLOCK18);
5121 write_byte(20);
5122 message_end();
5123 }
5124 }
5125
5126 if(get_pcvar_num(gg_nade_smoke) && !cs_get_user_bpammo(id,CSW_SMOKEGRENADE))
5127 ham_give_weapon(id,"weapon_smokegrenade");
5128
5129 if(get_pcvar_num(gg_nade_flash) && !cs_get_user_bpammo(id,CSW_FLASHBANG))
5130 ham_give_weapon(id,"weapon_flashbang");
5131 }
5132
5133 // keep melee weapon out if we had it out
5134 if(curweapon && curWpnMelee)
5135 {
5136 engclient_cmd(id,curWpnName);
5137 client_cmd(id,curWpnName);
5138 }
5139
5140 return 1;
5141}
5142
5143// show someone a welcome message
5144public show_welcome(id)
5145{
5146 if(welcomed[id]) return;
5147
5148 new menuid, keys;
5149 get_user_menu(id,menuid,keys);
5150
5151 // another old-school menu opened
5152 if(menuid > 0)
5153 {
5154 // wait and try again
5155 set_task(3.0,"show_welcome",id);
5156 return;
5157 }
5158
5159 play_sound_by_cvar(id,gg_sound_welcome);
5160
5161 new len = formatex(menuText,511,"\y%L\w^n",id,"WELCOME_MESSAGE_LINE1",GG_VERSION);
5162 len += formatex(menuText[len],511-len,"\d---------------\w^n");
5163
5164 new special;
5165 if(get_pcvar_num(gg_knife_pro))
5166 {
5167 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE2");
5168 special = 1;
5169 }
5170 if(get_pcvar_num(gg_turbo))
5171 {
5172 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE3");
5173 special = 1;
5174 }
5175 if(get_pcvar_num(gg_knife_elite))
5176 {
5177 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE4");
5178 special = 1;
5179 }
5180 if(get_pcvar_num(gg_dm) || get_cvar_num("csdm_active"))
5181 {
5182 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE5");
5183 special = 1;
5184 }
5185 if(get_pcvar_num(gg_teamplay))
5186 {
5187 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE6");
5188 special = 1;
5189 }
5190
5191 if(special) len += formatex(menuText[len],511-len,"\d---------------\w^n");
5192 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE7",weaponNum);
5193 len += formatex(menuText[len],511-len,"\d---------------\w^n");
5194 len += formatex(menuText[len],511-len,"%L",id,"WELCOME_MESSAGE_LINE8");
5195 len += formatex(menuText[len],511-len,"\d---------------\w^n");
5196 len += formatex(menuText[len],511-len,"%L",id,"PRESS_KEY_TO_CONTINUE");
5197
5198 show_menu(id,1023,menuText,-1,"welcome_menu");
5199}
5200
5201// show the required kills message
5202stock show_required_kills(id,always_individual=0)
5203{
5204 // weapon-specific warmup, who cares
5205 if(warmup > 0 && warmupWeapon[0]) return 0;
5206
5207 if(always_individual || !get_pcvar_num(gg_teamplay))
5208 return gungame_hudmessage(id,3.0,"%L: %i / %i",id,"REQUIRED_KILLS",score[id],get_level_goal(level[id],id));
5209
5210 new player, myTeam = _:cs_get_user_team(id), goal = get_level_goal(teamLevel[myTeam],id);
5211 for(player=1;player<=maxPlayers;player++)
5212 {
5213 if(player == id || (is_user_connected(player) && _:cs_get_user_team(player) == myTeam))
5214 gungame_hudmessage(player,3.0,"%L: %i / %i",player,"REQUIRED_KILLS",teamScore[myTeam],goal);
5215 }
5216
5217 return 1;
5218}
5219
5220// player killed himself
5221player_suicided(id)
5222{
5223 static name[32];
5224
5225 // we still have protection (round ended, new one hasn't started yet)
5226 // or, suicide level downs are disabled
5227 if(roundEnded || !get_pcvar_num(gg_suicide_penalty)) return 0;
5228
5229 // weapon-specific warmup, no one cares
5230 if(warmup > 0 && warmupWeapon[0]) return 0;
5231
5232 if(!get_pcvar_num(gg_teamplay))
5233 {
5234 get_user_name(id,name,31);
5235
5236 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"SUICIDE_LEVEL_DOWN",name);
5237
5238 // this is going to start a respawn counter HUD message
5239 if(get_pcvar_num(gg_dm) && (get_pcvar_num(gg_dm_countdown) & 2))
5240 return change_level(id,-1,_,0,1); // don't show message, always score
5241
5242 // show with message
5243 return change_level(id,-1,_,_,1); // always score
5244 }
5245 else
5246 {
5247 new team = _:cs_get_user_team(id);
5248 if(team != 1 && team != 2) return 0;
5249
5250 new penalty = get_level_goal(teamLevel[team],0);
5251 if(penalty > 0)
5252 {
5253 get_user_team(id,name,9);
5254
5255 if(teamScore[team] - penalty < 0)
5256 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"SUICIDE_LEVEL_DOWN_TEAM",name,(teamLevel[team] > 1) ? teamLevel[team]-1 : teamLevel[team]);
5257 else
5258 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"SUICIDE_SCORE_DOWN_TEAM",name,penalty);
5259
5260 return change_score(id,-penalty);
5261 }
5262 }
5263
5264 return 0;
5265}
5266
5267// player scored or lost a point
5268stock change_score(id,value,refill=1,play_sounds=1,effect_team=1,always_score=0)
5269{
5270 // don't bother scoring up on weapon-specific warmup
5271 if(warmup > 0 && warmupWeapon[0] && value > 0)
5272 return 0;
5273
5274 // can't score!
5275 if(!always_score && !can_score(id))
5276 return 0;
5277
5278 // already won, isn't important
5279 if(level[id] > weaponNum) return 0;
5280
5281 new oldScore = score[id], goal = get_level_goal(level[id],id);
5282
5283 new teamplay = get_pcvar_num(gg_teamplay), team;
5284 if(teamplay) team = _:cs_get_user_team(id);
5285
5286 // if this is going to level us
5287 if(score[id] + value >= goal)
5288 {
5289 new max_lvl = get_pcvar_num(gg_max_lvl);
5290
5291 // already reached max levels this round
5292 if(!teamplay && !get_pcvar_num(gg_turbo) && max_lvl > 0 && levelsThisRound[id] >= max_lvl)
5293 {
5294 // put it as high as we can without leveling
5295 score[id] = goal - 1;
5296 }
5297 else score[id] += value;
5298 }
5299 else score[id] += value;
5300
5301 // check for level up
5302 if(score[id] >= goal)
5303 {
5304 score[id] = 0;
5305
5306 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5307 teamplay_update_score(team,score[id],id,1); // direct
5308
5309 change_level(id,1,_,_,always_score,play_sounds);
5310 return 1;
5311 }
5312
5313 // check for level down
5314 if(score[id] < 0)
5315 {
5316 // can't go down below level 1
5317 if(level[id] <= 1)
5318 {
5319 score[id] = 0;
5320
5321 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5322 teamplay_update_score(team,score[id],id,1); // direct
5323
5324 new sdisplay = get_pcvar_num(gg_status_display);
5325 if(sdisplay == STATUS_KILLSLEFT || sdisplay == STATUS_KILLSDONE)
5326 status_display(id);
5327
5328 if(value < 0) show_required_kills(id);
5329 return 0;
5330 }
5331 else
5332 {
5333 goal = get_level_goal(level[id] > 1 ? level[id]-1 : 1,id);
5334
5335 score[id] = (oldScore + value) + goal; // carry over points
5336 if(score[id] < 0) score[id] = 0;
5337
5338 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5339 teamplay_update_score(team,score[id],id,1); // direct
5340
5341 change_level(id,-1,_,_,always_score);
5342 return -1;
5343 }
5344 }
5345
5346 // refresh menus
5347 new menu;
5348 get_user_menu(id,menu,dummy[0]);
5349 if(menu == level_menu) show_level_menu(id);
5350
5351 if(refill && get_pcvar_num(gg_refill_on_kill)) refill_ammo(id);
5352
5353 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5354 teamplay_update_score(team,score[id],id,1); // direct
5355
5356 if(value < 0) show_required_kills(id);
5357 else if(play_sounds) client_cmd(id,"spk ^"%s^"",KILL_DING_SOUND);
5358
5359 new sdisplay = get_pcvar_num(gg_status_display);
5360 if(sdisplay == STATUS_KILLSLEFT || sdisplay == STATUS_KILLSDONE)
5361 status_display(id);
5362
5363 return 0;
5364}
5365
5366// player gained or lost a level
5367stock change_level(id,value,just_joined=0,show_message=1,always_score=0,play_sounds=1,effect_team=1)
5368{
5369 // can't score
5370 if(level[id] > 0 && !always_score && !can_score(id))
5371 return 0;
5372
5373 // don't bother leveling up on weapon-specific warmup
5374 if(level[id] > 0 && warmup > 0 && warmupWeapon[0] && value > 0)
5375 return 0;
5376
5377 new oldLevel = level[id], oldValue = value;
5378
5379 new teamplay = get_pcvar_num(gg_teamplay), team;
5380 if(teamplay) team = _:cs_get_user_team(id);
5381
5382 // teamplay, on a valid team
5383 if(teamplay && (team == 1 || team == 2) && value != -MAX_WEAPONS) // ignore warmup reset
5384 {
5385 // not effecting team, but setting me to something that doesn't match team
5386 // OR
5387 // effecting team, and not even starting on same thing as team
5388 if((!effect_team && level[id] + value != teamLevel[team]) || (effect_team && level[id] != teamLevel[team]))
5389 {
5390 log_amx("MISSYNCH -- id: %i, value: %i, just_joined: %i, show_message: %i, always_score: %i, play_sounds: %i, effect_team: %i, team: %i, level: %i, teamlevel: %i, usertime: %i, score: %i, teamscore: %i, lvlweapon: %s, teamlvlweapon: %s",
5391 id,value,just_joined,show_message,always_score,play_sounds,effect_team,team,level[id],teamLevel[team],get_user_time(id,1),score[id],teamScore[team],lvlWeapon[id],teamLvlWeapon[team]);
5392
5393 log_message("MISSYNCH -- id: %i, value: %i, just_joined: %i, show_message: %i, always_score: %i, play_sounds: %i, effect_team: %i, team: %i, level: %i, teamlevel: %i, usertime: %i, score: %i, teamscore: %i, lvlweapon: %s, teamlvlweapon: %s",
5394 id,value,just_joined,show_message,always_score,play_sounds,effect_team,team,level[id],teamLevel[team],get_user_time(id,1),score[id],teamScore[team],lvlWeapon[id],teamLvlWeapon[team]);
5395 }
5396 }
5397
5398 // this will put us below level 1
5399 if(level[id] + value < 1)
5400 {
5401 value = 1 - level[id]; // go down only to level 1
5402
5403 // bottom out the score
5404 score[id] = 0;
5405
5406 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5407 teamplay_update_score(team,score[id],id,1); // direct
5408 }
5409
5410 // going up
5411 if(value > 0)
5412 {
5413 new max_lvl = get_pcvar_num(gg_max_lvl);
5414
5415 // already reached max levels for this round
5416 if(!teamplay && !get_pcvar_num(gg_turbo) && max_lvl > 0 && levelsThisRound[id] >= max_lvl)
5417 return 0;
5418 }
5419
5420 // can't win on the warmup round
5421 if(level[id] + value > weaponNum && warmup > 0)
5422 {
5423 score[id] = get_level_goal(level[id],id) - 1;
5424
5425 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5426 teamplay_update_score(team,score[id],id,1); // direct
5427
5428 return 0;
5429 }
5430
5431 level[id] += value;
5432 if(!just_joined) levelsThisRound[id] += value;
5433
5434 silenced[id] = 0; // for going to Glock->USP, for example
5435
5436 // win???
5437 if(level[id] > weaponNum)
5438 {
5439 // already won, ignore this
5440 if(won) return 1;
5441
5442 // bot, and not allowed to win
5443 if(is_user_bot(id) && get_pcvar_num(gg_ignore_bots) == 2 && !only_bots())
5444 {
5445 change_level(id,-value,just_joined,_,1); // always score
5446 return 1;
5447 }
5448
5449 // cap out score
5450 score[id] = get_level_goal(level[id],id);
5451
5452 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5453 teamplay_update_score(team,score[id],id,1); // direct
5454
5455 if(teamplay && effect_team && (team == 1 || team == 2) && teamLevel[team] != level[id])
5456 teamplay_update_level(team,level[id],id,1); // direct
5457
5458 // crown the winner
5459 win(id,lastKilled[id]);
5460
5461 return 1;
5462 }
5463
5464 // set weapon based on it
5465 get_level_weapon(level[id],lvlWeapon[id],23);
5466
5467 // update the status display
5468 new sdisplay = get_pcvar_num(gg_status_display);
5469 if(sdisplay == STATUS_LEADERWPN) status_display(0); // to all
5470 else if(sdisplay) status_display(id); // only to me
5471
5472 new nade = equal(lvlWeapon[id],HEGRENADE);
5473
5474 // I'm a leader!
5475 if(warmup <= 0 && level[get_leader()] == level[id])
5476 {
5477 new sound_cvar;
5478 if(nade) sound_cvar = gg_sound_nade;
5479 else if(equal(lvlWeapon[id],KNIFE)) sound_cvar = gg_sound_knife;
5480
5481 if(sound_cvar)
5482 {
5483 // only play sound if we reached this level first
5484 if(num_players_on_level(level[id]) == 1) play_sound_by_cvar(0,sound_cvar);
5485 }
5486 }
5487
5488 // NOW play level up sounds, so that they potentially
5489 // override the global "Player is on X level" sounds
5490
5491 if(play_sounds)
5492 {
5493 // level up!
5494 if(oldValue >= 0) play_sound_by_cvar(id,gg_sound_levelup);
5495
5496 // level down :(
5497 else play_sound_by_cvar(id,gg_sound_leveldown);
5498 }
5499
5500 // remember to modify changes
5501 new oldTeamLevel;
5502 if(team == 1 || team == 2) oldTeamLevel = teamLevel[team];
5503
5504 if(teamplay && effect_team && (team == 1 || team == 2) && teamLevel[team] != level[id])
5505 teamplay_update_level(team,level[id],id);
5506
5507 // refresh menus
5508 new player, menu;
5509 for(player=1;player<=maxPlayers;player++)
5510 {
5511 if(!is_user_connected(player)) continue;
5512 get_user_menu(player,menu,dummy[0]);
5513
5514 if(menu == scores_menu) show_scores_menu(player);
5515 else if(menu == level_menu) show_level_menu(player);
5516 else if(player == id && menu == weapons_menu) show_weapons_menu(player);
5517 }
5518
5519 // make sure we don't have more than required now
5520 new goal = get_level_goal(level[id],id);
5521 if(score[id] >= goal)
5522 {
5523 score[id] = goal-1; // 1 under
5524
5525 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5526 teamplay_update_score(team,score[id],id,1); // direct
5527 }
5528
5529 new turbo = get_pcvar_num(gg_turbo);
5530
5531 // give weapon right away?
5532 if((turbo || just_joined) && is_user_alive(id)) give_level_weapon(id);
5533 else show_progress_display(id); // still show display anyway
5534
5535 // update the leader display (cvar check done in that function)
5536 if(!just_joined)
5537 {
5538 remove_task(TASK_LEADER_DISPLAY);
5539 show_leader_display();
5540
5541 new Float:lead_sounds = get_pcvar_float(gg_lead_sounds);
5542 if(lead_sounds > 0.0 && (!teamplay || effect_team)) play_lead_sounds(id,oldLevel,lead_sounds);
5543 }
5544
5545 new vote_setting = get_pcvar_num(gg_vote_setting), map_iterations = get_pcvar_num(gg_map_iterations);
5546
5547 // the level to start a map vote on
5548 if(!voted && warmup <= 0 && vote_setting > 0
5549 && level[id] >= weaponNum - (vote_setting - 1)
5550 && mapIteration >= map_iterations && map_iterations > 0)
5551 {
5552 new mapCycleFile[64];
5553 get_gg_mapcycle_file(mapCycleFile,63);
5554
5555 // start map vote?
5556 if(!mapCycleFile[0] || !file_exists(mapCycleFile))
5557 {
5558 voted = 1;
5559
5560 // check for a custom vote
5561 new custom[256];
5562 get_pcvar_string(gg_vote_custom,custom,255);
5563
5564 if(custom[0]) server_cmd(custom);
5565 else start_mapvote();
5566 }
5567 }
5568
5569 // grab my name
5570 static name[32];
5571 if(!teamplay) get_user_name(id,name,31);
5572
5573 // only calculate position if we didn't just join
5574 if(!just_joined && show_message)
5575 {
5576 if(teamplay)
5577 {
5578 // is the first call for this level change
5579 if((team == 1 || team == 2) && teamLevel[team] != oldTeamLevel)
5580 {
5581 new leaderLevel, numLeaders, leader = teamplay_get_lead_team(leaderLevel,numLeaders);
5582
5583 // tied
5584 if(numLeaders > 1) gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TIED_LEADER_TEAM",leaderLevel,teamLvlWeapon[team]);
5585
5586 // leading
5587 else if(leader == team)
5588 {
5589 get_user_team(id,name,9);
5590 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"LEADING_ON_LEVEL_TEAM",name,leaderLevel,teamLvlWeapon[team]);
5591 }
5592
5593 // trailing
5594 else
5595 {
5596 get_user_team(id,name,9);
5597 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TRAILING_ON_LEVEL_TEAM",name,teamLevel[team],teamLvlWeapon[team]);
5598 }
5599 }
5600 }
5601 else
5602 {
5603 new leaderLevel, numLeaders, leader = get_leader(leaderLevel,numLeaders);
5604
5605 // tied
5606 if(level[id] == leaderLevel && numLeaders > 1 && level[id] > 1)
5607 {
5608 if(numLeaders == 2)
5609 {
5610 new otherLeader;
5611 if(leader != id) otherLeader = leader;
5612 else
5613 {
5614 new player;
5615 for(player=1;player<=maxPlayers;player++)
5616 {
5617 if(is_user_connected(player) && level[player] == leaderLevel && player != id)
5618 {
5619 otherLeader = player;
5620 break;
5621 }
5622 }
5623 }
5624
5625 static otherName[32];
5626 get_user_name(otherLeader,otherName,31);
5627
5628 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TIED_LEADER_ONE",name,leaderLevel,lvlWeapon[id],otherName);
5629 }
5630 else
5631 {
5632 static numWord[16], digiWord[3], lang[3];
5633 num_to_word(numLeaders-1,numWord,15);
5634 trim(numWord);
5635 formatex(digiWord,2,"%i",numLeaders-1);
5636
5637 new player;
5638 for(player=1;player<=maxPlayers;player++)
5639 {
5640 if(is_user_connected(player))
5641 {
5642 // use word for english, digit otherwise
5643 get_user_info(player,"lang",lang,2);
5644 gungame_print(player,id,1,"%L",player,"TIED_LEADER_MULTI",name,leaderLevel,lvlWeapon[id],equali(lang,"en") ? numWord : digiWord);
5645 }
5646 }
5647 }
5648 }
5649
5650 // I'M THE BEST!!!!!!!
5651 else if(leader == id && level[id] > 1)
5652 {
5653 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"LEADING_ON_LEVEL",name,level[id],lvlWeapon[id]);
5654 }
5655 }
5656 }
5657
5658 // teamplay, didn't grab name yet
5659 if(teamplay) get_user_name(id,name,31);
5660
5661 // triple bonus!
5662 if(levelsThisRound[id] == 3)
5663 {
5664 new triple_on = get_pcvar_num(gg_triple_on);
5665
5666 if(triple_on == 2 || (triple_on && !turbo))
5667 {
5668 star[id] = 1;
5669
5670 new sound[64];
5671 get_pcvar_string(gg_sound_triple,sound,63);
5672
5673 fm_set_user_maxspeed(id,fm_get_user_maxspeed(id)*1.5);
5674
5675 if(sound[0]) engfunc(EngFunc_EmitSound,id,CHAN_VOICE,sound[6],VOL_NORM,ATTN_NORM,0,PITCH_NORM); // ignore sound/ prefix
5676 else engfunc(EngFunc_EmitSound,id,CHAN_VOICE,sound,VOL_NORM,ATTN_NORM,0,PITCH_NORM);
5677
5678 set_pev(id,pev_effects,pev(id,pev_effects) | EF_BRIGHTLIGHT);
5679 fm_set_rendering(id,kRenderFxGlowShell,255,255,100,kRenderNormal,1);
5680 fm_set_user_godmode(id,1);
5681
5682 message_begin(MSG_BROADCAST,SVC_TEMPENTITY);
5683 write_byte(22); // TE_BEAMFOLLOW
5684 write_short(id); // entity
5685 write_short(trailSpr); // sprite
5686 write_byte(20); // life
5687 write_byte(10); // width
5688 write_byte(255); // r
5689 write_byte(255); // g
5690 write_byte(100); // b
5691 write_byte(100); // brightness
5692 message_end();
5693
5694 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TRIPLE_LEVELED",name);
5695 set_task(10.0,"end_star",TASK_END_STAR+id);
5696 }
5697 }
5698
5699 new ff_auto = get_pcvar_num(gg_ff_auto), ff = get_pcvar_num(mp_friendlyfire);
5700
5701 // turn on FF?
5702 if(ff_auto && !ff && nade)
5703 {
5704 server_cmd("mp_friendlyfire 1"); // so console is notified
5705 set_pcvar_num(mp_friendlyfire,1); // so it changes instantly
5706
5707 gungame_print(0,0,1,"%L",LANG_PLAYER_C,"FRIENDLYFIRE_ON");
5708
5709 client_cmd(0,"spk ^"%s^"",BRASS_BELL_SOUND);
5710 }
5711
5712 // turn off FF?
5713 else if(ff_auto && ff)
5714 {
5715 new keepFF, player;
5716
5717 for(player=1;player<=maxPlayers;player++)
5718 {
5719 if(equal(lvlWeapon[player],HEGRENADE) || equal(lvlWeapon[player],KNIFE))
5720 {
5721 keepFF = 1;
5722 break;
5723 }
5724 }
5725
5726 // no one is on nade or knife level anymore
5727 if(!keepFF)
5728 {
5729 server_cmd("mp_friendlyfire 0"); // so console is notified
5730 set_pcvar_num(mp_friendlyfire,0); // so it changes instantly
5731 }
5732 }
5733
5734 // some bots are actually allergic to the chemicals used in HE grenades
5735 if(is_user_bot(id) && get_pcvar_num(gg_bots_skipnade) && !get_pcvar_num(gg_teamplay) && equal(lvlWeapon[id],HEGRENADE))
5736 change_level(id,1);
5737
5738 return 1;
5739}
5740
5741// forces a player to a level, skipping a lot of important stuff.
5742// it's assumed that this is used as a result of "id" being leveled
5743// up because his teammate leveled up in teamplay.
5744stock set_level_noifandsorbuts(id,newLevel,play_sounds=1)
5745{
5746 // okay, this is our only but
5747 if(!is_user_connected(id)) return 0;
5748
5749 new oldLevel = level[id];
5750
5751 level[id] = newLevel;
5752 get_level_weapon(level[id],lvlWeapon[id],23);
5753
5754 if(play_sounds)
5755 {
5756 // level up!
5757 if(newLevel >= oldLevel) play_sound_by_cvar(id,gg_sound_levelup);
5758
5759 // level down :(
5760 else play_sound_by_cvar(id,gg_sound_leveldown);
5761 }
5762
5763 // refresh menus
5764 new player, menu;
5765 for(player=1;player<=maxPlayers;player++)
5766 {
5767 if(!is_user_connected(player)) continue;
5768 get_user_menu(player,menu,dummy[0]);
5769
5770 if(menu == scores_menu) show_scores_menu(player);
5771 else if(menu == level_menu) show_level_menu(player);
5772 }
5773
5774 // give weapon right away?
5775 if(get_pcvar_num(gg_turbo) && is_user_alive(id)) give_level_weapon(id);
5776 else show_progress_display(id); // still show display anyway
5777
5778 return 1;
5779}
5780
5781// get rid of a player's star
5782public end_star(taskid)
5783{
5784 new id = taskid - TASK_END_STAR;
5785 if(!star[id]) return;
5786
5787 star[id] = 0;
5788 //gungame_print(id,0,1,"Your star has run out!");
5789
5790 if(is_user_alive(id))
5791 {
5792 fm_set_user_maxspeed(id,fm_get_user_maxspeed(id)/1.5);
5793 engfunc(EngFunc_EmitSound,id,CHAN_VOICE,"common/null.wav",VOL_NORM,ATTN_NORM,0,PITCH_NORM); // stop sound
5794 set_pev(id,pev_effects,pev(id,pev_effects) & ~EF_BRIGHTLIGHT);
5795 fm_set_rendering(id);
5796 fm_set_user_godmode(id,0);
5797
5798 message_begin(MSG_BROADCAST,SVC_TEMPENTITY);
5799 write_byte(99); // TE_KILLBEAM
5800 write_short(id); // entity
5801 message_end();
5802 }
5803}
5804
5805// give a player a weapon based on his level
5806stock give_level_weapon(id,notify=1,verify=1)
5807{
5808 if(!is_user_alive(id) || level[id] <= 0) return 0;
5809
5810 // not warming up, didn't just win
5811 if(notify && warmup <= 0 && level[id] > 0 && level[id] <= weaponNum)
5812 show_progress_display(id);
5813
5814 // stop attacks from bleeding over into the new weapon
5815 //client_cmd(id,"-attack;-attack2");
5816
5817 // give CTs defuse kits on bomb maps
5818 if(bombMap && !get_pcvar_num(gg_block_objectives) && cs_get_user_team(id) == CS_TEAM_CT)
5819 cs_set_user_defuse(id,1);
5820
5821 new armor = get_pcvar_num(gg_give_armor), helmet = get_pcvar_num(gg_give_helmet);
5822
5823 // giving armor and helmets away like candy
5824 if(helmet) cs_set_user_armor(id,armor,CS_ARMOR_VESTHELM);
5825 else cs_set_user_armor(id,armor,CS_ARMOR_KEVLAR);
5826
5827 new oldWeapon = get_user_weapon(id);
5828
5829 static wpnName[24];
5830 new weapons = pev(id,pev_weapons), wpnid, alright, myCategory, hasMain;
5831
5832 new ammo = get_pcvar_num(gg_ammo_amount),
5833 knife_elite = get_pcvar_num(gg_knife_elite),
5834 pickup_others = (get_pcvar_num(gg_pickup_others) && (!knife_elite || !levelsThisRound[id])),
5835 mainCategory = get_weapon_category(_,lvlWeapon[id]);
5836
5837 new hasGlock, hasSmoke, hasFlash,
5838 nade_level = (equal(lvlWeapon[id],HEGRENADE)),
5839 nade_glock = get_pcvar_num(gg_nade_glock),
5840 nade_smoke = get_pcvar_num(gg_nade_smoke),
5841 nade_flash = get_pcvar_num(gg_nade_flash);
5842
5843 new melee_only = ((warmup > 0 && warmupWeapon[0] && equal(warmupWeapon,KNIFE)) || (knife_elite && levelsThisRound[id] > 0));
5844
5845 // remove stuff first
5846 for(wpnid=1;wpnid<31;wpnid++)
5847 {
5848 // don't have this, or it's the C4
5849 if(!(weapons & (1<<wpnid)) || wpnid == CSW_C4) continue;
5850
5851 alright = 0;
5852 get_weaponname(wpnid,wpnName,23);
5853
5854 if(melee_only)
5855 {
5856 if(wpnid == CSW_KNIFE)
5857 {
5858 alright = 1;
5859 hasMain = 1;
5860 }
5861 }
5862 else
5863 {
5864 replace(wpnName,23,"weapon_","");
5865
5866 // this is our designated weapon
5867 if(equal(lvlWeapon[id],wpnName))
5868 {
5869 alright = 1;
5870 hasMain = 1;
5871 }
5872
5873 // nade extras
5874 else if(nade_level)
5875 {
5876 if(nade_glock && wpnid == CSW_GLOCK18)
5877 {
5878 alright = 1;
5879 hasGlock = 1;
5880 }
5881 else if(nade_smoke && wpnid == CSW_SMOKEGRENADE)
5882 {
5883 alright = 1;
5884 hasSmoke = 1;
5885 }
5886 else if(nade_flash && wpnid == CSW_FLASHBANG)
5887 {
5888 alright = 1;
5889 hasFlash = 1;
5890 }
5891 }
5892
5893 // get the tag back on there
5894 format(wpnName,23,"weapon_%s",wpnName);
5895 }
5896
5897 // don't do anything about the knife
5898 if(wpnid != CSW_KNIFE)
5899 {
5900 // was it alright?
5901 if(alright)
5902 {
5903 // reset ammo
5904 if(wpnid != CSW_HEGRENADE && wpnid != CSW_FLASHBANG && wpnid != CSW_SMOKEGRENADE)
5905 {
5906 if(nade_level && nade_glock && wpnid == CSW_GLOCK18)
5907 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5908 else
5909 {
5910 if(ammo > 0) cs_set_user_bpammo(id,wpnid,ammo);
5911 else cs_set_user_bpammo(id,wpnid,maxAmmo[wpnid]);
5912 }
5913 }
5914 else cs_set_user_bpammo(id,wpnid,1); // grenades
5915 }
5916
5917 // we should probably remove this weapon
5918 else
5919 {
5920 myCategory = get_weapon_category(wpnid);
5921
5922 // pistol in the way of glock, remove it
5923 if(nade_level && nade_glock && myCategory == 2) ham_strip_weapon(id,wpnName);
5924 else
5925 {
5926 // we aren't allowed to have any other weapons,
5927 // or this is in the way of the weapon that I want.
5928 if(!pickup_others || myCategory == mainCategory)
5929 ham_strip_weapon(id,wpnName);
5930 }
5931 }/*not alright*/
5932 }/*not a knife*/
5933 }/*wpnid for-loop*/
5934
5935 // I should have a weapon but don't
5936 if(lvlWeapon[id][0] && !hasMain)
5937 {
5938 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
5939
5940 // give a player his weapon
5941 ham_give_weapon(id,wpnName);
5942
5943 remove_task(TASK_REFRESH_NADE+id);
5944
5945 if(!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE))
5946 {
5947 wpnid = get_weaponid(wpnName);
5948
5949 if(!wpnid) log_amx("INVALID WEAPON ID FOR ^"%s^"",lvlWeapon[id]);
5950 else
5951 {
5952 if(ammo > 0) cs_set_user_bpammo(id,wpnid,ammo);
5953 else cs_set_user_bpammo(id,wpnid,maxAmmo[wpnid]);
5954 }
5955 }
5956 }
5957
5958 if(nade_level)
5959 {
5960 if(nade_glock && !hasGlock)
5961 {
5962 ham_give_weapon(id,WEAPON_GLOCK18);
5963 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5964 }
5965 if(nade_smoke && !hasSmoke) ham_give_weapon(id,"weapon_smokegrenade");
5966 if(nade_flash && !hasFlash) ham_give_weapon(id,"weapon_flashbang");
5967 }
5968
5969 new weapon = get_user_weapon(id);
5970
5971 // using a knife probably
5972 if(melee_only || equal(lvlWeapon[id],KNIFE))
5973 {
5974 // draw knife on knife warmup and knife level... this is so that
5975 // the terrorist that spawns with the C4 won't be spawned with his
5976 // C4 selected, but instead his knife
5977 engclient_cmd(id,WEAPON_KNIFE);
5978 client_cmd(id,WEAPON_KNIFE);
5979 }
5980
5981 // switch back to knife if we had it out. also don't do this when called
5982 // by the verification check, because their old weapon will obviously be
5983 // a knife and they will want to use their new one.
5984 else if(verify /*&& !notify*/ && oldWeapon)
5985 {
5986 get_weaponname(oldWeapon,wpnName,23);
5987 if(wpnName[0] && equal(wpnName,WEAPON_KNIFE))
5988 {
5989 engclient_cmd(id,wpnName);
5990 client_cmd(id,wpnName);
5991 }
5992 else if(lvlWeapon[id][0])
5993 {
5994 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
5995 engclient_cmd(id,wpnName);
5996 client_cmd(id,wpnName);
5997 }
5998 }
5999
6000 // switch to glock for nade level
6001 else if(weapon != CSW_KNIFE && equal(lvlWeapon[id],HEGRENADE) && nade_glock)
6002 {
6003 engclient_cmd(id,WEAPON_GLOCK18);
6004 client_cmd(id,WEAPON_GLOCK18);
6005 }
6006
6007 // otherwise, switch to our new weapon
6008 else if(lvlWeapon[id][0])
6009 {
6010 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
6011 engclient_cmd(id,wpnName);
6012 client_cmd(id,wpnName);
6013 }
6014
6015 // make sure that we get this...
6016 if(verify)
6017 {
6018 remove_task(TASK_VERIFY_WEAPON+id);
6019 set_task(1.0,"verify_weapon",TASK_VERIFY_WEAPON+id);
6020 }
6021
6022 // remember burst or silenced status
6023 if(silenced[id])
6024 {
6025 if(equal(lvlWeapon[id],"usp") || equal(lvlWeapon[id],"m4a1"))
6026 {
6027 new wEnt = get_weapon_ent(id,_,lvlWeapon[id]);
6028 if(pev_valid(wEnt))
6029 {
6030 cs_set_weapon_silen(wEnt,1,0);
6031
6032 // play draw with silencer animation
6033 if(lvlWeapon[id][0] == 'u') set_pev(id,pev_weaponanim,USP_DRAWANIM);
6034 else set_pev(id,pev_weaponanim,M4A1_DRAWANIM);
6035 }
6036 }
6037 else if(equal(lvlWeapon[id],"glock18") || equal(lvlWeapon[id],"famas"))
6038 {
6039 new wEnt = get_weapon_ent(id,_,lvlWeapon[id]);
6040 if(pev_valid(wEnt)) cs_set_weapon_burst(wEnt,1);
6041 }
6042
6043 silenced[id] = 0;
6044 }
6045
6046 return 1;
6047}
6048
6049// verify that we have our stupid weapon
6050public verify_weapon(taskid)
6051{
6052 new id = taskid-TASK_VERIFY_WEAPON;
6053
6054 if(!is_user_alive(id)) return;
6055
6056 static wpnName[24];
6057 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
6058 new wpnid = get_weaponid(wpnName);
6059
6060 if(!wpnid) return;
6061
6062 // we don't have it, but we want it
6063 if(!user_has_weapon(id,wpnid)) give_level_weapon(id,0,0);
6064}
6065
6066// crown a winner
6067win(winner,loser)
6068{
6069 // we have an invalid winner here
6070 if(won || !is_user_connected(winner) || !can_score(winner))
6071 return;
6072
6073 won = 1;
6074 roundEnded = 1;
6075
6076 server_cmd("sv_alltalk 1");
6077 client_cmd(0,"stopsound;speak null;mp3 stop");
6078 play_sound(0,winSounds[currentWinSound]);
6079
6080 new map_iterations = get_pcvar_num(gg_map_iterations), restart,
6081 player, Float:chattime = get_cvar_float("mp_chattime");
6082
6083 // final playthrough, get ready for next map
6084 if(mapIteration >= map_iterations && map_iterations > 0)
6085 {
6086 set_nextmap();
6087 set_task(floatmax(chattime,2.5),"goto_nextmap");
6088
6089 // as of GG1.16, we always send a non-emessage intermission, because
6090 // other map changing plugins (as well as StatsMe) intercepting it
6091 // was causing problems.
6092
6093 // as of GG1.20, we no longer do this because it closes the MOTD.
6094
6095 // as of GG2.10, we use finale, which freezes players like the
6096 // intermission but doesn't otherwise do any intermission stuff.
6097 message_begin(MSG_ALL,SVC_FINALE);
6098 write_string(""); // although you could put a nice typewrite-style centersay here
6099 message_end();
6100
6101 // godmode everyone
6102 new fullName[32];
6103 for(player=1;player<=maxPlayers;player++)
6104 {
6105 if(!is_user_alive(player)) continue;
6106
6107 // finale won't stop players from shooting technically
6108 formatex(fullName,31,"weapon_%s",lvlWeapon[player]);
6109 ham_strip_weapon(player,fullName);
6110
6111 fm_set_user_godmode(player,1);
6112 }
6113 }
6114
6115 // get ready to go again!!
6116 else
6117 {
6118 restart = 1;
6119
6120 // freeze and godmode everyone
6121 for(player=1;player<=maxPlayers;player++)
6122 {
6123 if(!is_user_connected(player)) continue;
6124
6125 client_cmd(player,"-attack;-attack2");
6126 set_pev(player,pev_flags,pev(player,pev_flags) | FL_FROZEN);
6127 fm_set_user_godmode(player,1);
6128 set_pev(player,pev_viewmodel2,"");
6129 }
6130 }
6131
6132 emessage_begin(MSG_ALL,gmsgHideWeapon);
6133 ewrite_byte((1<<0)|(1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<6)); // can't use (1<<2) or text disappears
6134 emessage_end();
6135
6136 emessage_begin(MSG_ALL,gmsgCrosshair);
6137 ewrite_byte(0); // hide
6138 emessage_end();
6139
6140 new winnerName[32], i, teamplay = get_pcvar_num(gg_teamplay);
6141 if(teamplay) get_user_team(winner,winnerName,9);
6142 else get_user_name(winner,winnerName,31);
6143
6144 // old-fashioned
6145 for(i=0;i<5;i++)
6146 {
6147 if(teamplay) gungame_print(0,winner,1,"%L!!",LANG_PLAYER_C,"WON_TEAM",winnerName);
6148 else gungame_print(0,winner,1,"%L!",LANG_PLAYER_C,"WON",winnerName);
6149 }
6150
6151 // our new super function
6152 stats_award_points(winner);
6153
6154 // finally show it off
6155 new winner_motd[64];
6156 get_pcvar_string(gg_winner_motd,winner_motd,63);
6157 if(winner_motd[0] && winner_motd[0] != '0')
6158 {
6159 new params[66];
6160 params[0] = winner;
6161 params[1] = loser;
6162 copy(params[2],63,winner_motd);
6163 set_task(1.0,"show_win_screen",_,params,65);
6164 }
6165
6166 // we can restart now (do it after calculations because points might get reset)
6167 if(restart)
6168 {
6169 // delay it, because it will reset stuff
6170 set_task(1.1,"restart_round",floatround(chattime-1.1));
6171
6172 set_task(floatmax(chattime-0.1,1.2),"restart_gungame",czero ? get_cvar_num("bot_stop") : 0);
6173 set_task(floatmax(chattime+5.0,0.1),"stop_win_sound",currentWinSound);
6174
6175 if(czero) server_cmd("bot_stop 1"); // freeze CZ bots
6176 }
6177}
6178
6179// restart gungame, for the next map iteration
6180public restart_gungame(old_bot_stop_value)
6181{
6182 won = 0;
6183 mapIteration++;
6184
6185 /*new i;
6186 for(i=0;i<sizeof teamLevel;i++)
6187 clear_team_values(i);*/
6188
6189 // game already commenced, but we are restarting, allow us to warmup again
6190 if(gameCommenced) shouldWarmup = 1;
6191
6192 stats_clear_all(); // clear out everything about our stats
6193
6194 toggle_gungame(TASK_TOGGLE_GUNGAME + TOGGLE_ENABLE); // reset stuff
6195
6196 // toggle_gungame calls map_start_cvars which calls do_rOrder, so we theoretically don't need to do it again here
6197 //do_rOrder(0); // also does random teamplay
6198
6199 setup_weapon_order();
6200 currentWinSound = do_rWinSound(); // pick the next win sound
6201
6202 // unfreeze and ungodmode everyone
6203 for(new player=1;player<=maxPlayers;player++)
6204 {
6205 if(!is_user_connected(player)) continue;
6206
6207 set_pev(player,pev_flags,pev(player,pev_flags) & ~FL_FROZEN);
6208 fm_set_user_godmode(player,0);
6209 welcomed[player] = 1; // also don't show welcome again
6210 }
6211 if(czero) server_cmd("bot_stop %i",old_bot_stop_value); // unfreeze CZ bots
6212
6213 // only have warmup once?
6214 if(!get_pcvar_num(gg_warmup_multi)) warmup = -13; // -13 is the magical stop number
6215 else warmup = -1; // -1 isn't magical at all... :(
6216
6217 warmupWeapon[0] = 0;
6218}
6219
6220// stop the winner sound (for multiple map iterations)
6221public stop_win_sound(winSound)
6222{
6223 // stop winning sound
6224 if(containi(winSounds[winSound],".mp3") != -1) client_cmd(0,"mp3 stop");
6225 else client_cmd(0,"speak null");
6226}
6227
6228// calculate the winner screen... severely cut down from before
6229public show_win_screen(params[66]) // [winner,loser,gg_win_motd[64]]
6230{
6231 new winner = params[0], loser = params[1];
6232 if(!is_user_connected(winner)) return 0;
6233
6234 new winner_motd[64];
6235 copy(winner_motd,63,params[2]);
6236
6237 new motd[1536], len, header[32];
6238
6239 new teamplay = get_pcvar_num(gg_teamplay), stats_mode = get_pcvar_num(gg_stats_mode), stats_split = get_pcvar_num(gg_stats_split),
6240 timeratio = get_pcvar_num(gg_teamplay_timeratio), iterations = get_pcvar_num(gg_map_iterations), roundsleft = iterations - mapIteration, nextmap[32],
6241 loserDC = !is_user_connected(loser);
6242
6243 get_cvar_string("amx_nextmap",nextmap,31);
6244
6245 new winnerTeam[10], winnerName[32], winnerColor[8], winnerWinSuffix[3], winnerStreakSuffix[3], streakChampColor[8], myStreakSuffix[3],
6246 winningTeam = _:cs_get_user_team(winner), losingTeam = _:(!(winningTeam-1)) + 1;
6247
6248#if defined SQL
6249 // abort if showing web page but could not initialize SQL
6250 if(!isdigit(winner_motd[0]) && !sqlInit)
6251 {
6252 return 0;
6253 }
6254
6255 // if web page or set to 2, update database
6256 if((!isdigit(winner_motd[0]) || winner_motd[0] == '2') && sqlInit)
6257 {
6258 new systime = get_systime(), player, playerAuthid[32], playerName[32], playerSafeAuthid[64], playerSafeName[64], si = get_gg_si(), flags, team,
6259 origLen = len = formatex(motd,1535,"DELETE FROM `%s`; INSERT INTO `%s` VALUES ('0','','%s','%i','%i','','%i','%i','%i','%i','%i','%i','%i','%i','%i','%i','%s'),",sqlPlayersTable,sqlPlayersTable,nextmap,stats_mode,stats_split,teamplay,timeratio,winner,loser,loserDC,winningTeam,losingTeam,iterations,roundsleft,systime,serverip);
6260
6261 for(player=1;player<=maxPlayers;player++)
6262 {
6263 if(!is_user_connected(player)) continue;
6264
6265 if(len >= 1200) // getting too close for comfort, cash in what we have and start a new query
6266 {
6267 motd[len] = ';';
6268 motd[len+1] = 0;
6269
6270 // thread this to prevent weird connection errors??? only solution I could find
6271 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",motd);
6272
6273 len = origLen = formatex(motd,1535,"INSERT INTO `%s` VALUES ",sqlPlayersTable);
6274 }
6275
6276 flags = 0;
6277 team = _:cs_get_user_team(player);
6278
6279 if(team == winningTeam)
6280 {
6281 flags |= WON;
6282 if(player == winner) flags |= LASTKILL;
6283 }
6284 else if(team > 0 && team < 3)
6285 {
6286 flags |= LOST;
6287 if(player == loser) flags |= LASTKILL;
6288 }
6289
6290 if(player == pointsExtraction[0][1]) flags |= NEWRECORD;
6291
6292 get_gg_authid(player,playerAuthid,31);
6293 get_user_name(player,playerName,31);
6294
6295 // get new stats position
6296 stats_clear_struct(playerStats[player]);
6297 stats_get_position(player,playerAuthid,si);
6298
6299 SQL_QuoteString(db,playerSafeName,63,playerName);
6300 SQL_QuoteString(db,playerSafeAuthid,63,playerAuthid);
6301
6302 len += formatex(motd[len],1535-len,"%s('%i','%s','%s','%i','%i','%s','%i','%i','%i','%i','%i','%i','%i','%i','%i','%i','%s')",(len != origLen) ? "," : "",
6303 player,playerSafeAuthid,playerSafeName,team,level[player],lvlWeapon[player],flags,pointsExtraction[player][0],pointsExtraction[player][2],pointsExtraction[player][1],pointsExtraction[player][4],pointsExtraction[player][3],statsPosition[player][si],floatround(teamTimes[player][winningTeam-1]),floatround(teamTimes[player][losingTeam-1]),systime,serverip);
6304 }
6305
6306 // if we actually added someone
6307 if(len > origLen)
6308 {
6309 motd[len] = ';';
6310 motd[len+1] = 0;
6311
6312 query = SQL_PrepareQuery(db,motd);
6313 SQL_ExecuteAndLog(query);
6314 SQL_FreeHandle(query);
6315 }
6316
6317 // an actual web page
6318 if(winner_motd[0] != '2')
6319 {
6320 new url[74], lang[3];
6321 for(player=1;player<=maxPlayers;player++)
6322 {
6323 if(!is_user_connected(player)) continue;
6324
6325 get_user_info(player,"lang",lang,2);
6326
6327 formatex(header,31,"%L",player,"WIN_MOTD_LINE1",winnerName);
6328 formatex(url,73,"%s?i=%i&l=%s",winner_motd,player,lang);
6329
6330 show_motd(player,url,header);
6331 }
6332
6333 return 1;
6334 }
6335 }
6336#endif
6337
6338 if(sqlInit && stats_mode && is_user_connected(pointsExtraction[0][1]))
6339 get_team_color(CsTeams:get_user_team(pointsExtraction[0][1]),streakChampColor,7);
6340
6341 get_user_team(winner,winnerTeam,9);
6342 get_user_name(winner,winnerName,31);
6343 get_team_color(CsTeams:winningTeam,winnerColor,7);
6344 get_number_suffix(pointsExtraction[winner][0],winnerWinSuffix,2);
6345 get_number_suffix(pointsExtraction[winner][3],winnerStreakSuffix,2);
6346
6347 // pointsExtraction[player][0] = total wins
6348 // pointsExtraction[player][1] = points from this round
6349 // pointsExtraction[player][2] = total points
6350 // pointsExtraction[player][3] = current streak
6351 // pointsExtraction[player][4] = previous record streak
6352
6353 // pointsExtraction[0][0] = old record (0 = no old record)
6354 // pointsExtraction[0][1] = record holder (-1 = old guy)
6355 // pointsExtraction[0][2] = new record (0 = no new record)
6356
6357 new loserName[32], loserColor[8];
6358 if(!loserDC)
6359 {
6360 get_user_name(loser,loserName,31);
6361 get_team_color(cs_get_user_team(loser),loserColor,7);
6362 }
6363 else loserColor = "gray";
6364
6365 // figure out which lines to use based on stats split stuff
6366 new key_LINE5A[20], key_LINE5B[20], key_LINE5C[20], key_LINE7A[20], key_LINE7B[20], key_LINE7C[20],
6367 key_STREAK1[21], key_STREAK2[21], key_STREAK3[21], key_STREAK4[21];
6368
6369 if(stats_split)
6370 {
6371 if(teamplay)
6372 {
6373 key_LINE5A = "WIN_MOTD_LINE5A_TP";
6374 key_LINE5B = "WIN_MOTD_LINE5B_TP";
6375 key_LINE5C = "WIN_MOTD_LINE5C_TP";
6376 key_LINE7A = "WIN_MOTD_LINE7A_TP";
6377 key_LINE7B = "WIN_MOTD_LINE7B_TP";
6378 key_LINE7C = "WIN_MOTD_LINE7C_TP";
6379 key_STREAK1 = "WIN_MOTD_STREAK1_TP";
6380 key_STREAK2 = "WIN_MOTD_STREAK2_TP";
6381 key_STREAK3 = "WIN_MOTD_STREAK3_TP";
6382 key_STREAK4 = "WIN_MOTD_STREAK4_TP";
6383 }
6384 else
6385 {
6386 key_LINE5A = "WIN_MOTD_LINE5A_REG";
6387 key_LINE5B = "WIN_MOTD_LINE5B_REG";
6388 key_LINE5C = "WIN_MOTD_LINE5C_REG";
6389 key_LINE7A = "WIN_MOTD_LINE7A_REG";
6390 key_LINE7B = "WIN_MOTD_LINE7B_REG";
6391 key_LINE7C = "WIN_MOTD_LINE7C_REG";
6392 key_STREAK1 = "WIN_MOTD_STREAK1_REG";
6393 key_STREAK2 = "WIN_MOTD_STREAK2_REG";
6394 key_STREAK3 = "WIN_MOTD_STREAK3_REG";
6395 key_STREAK4 = "WIN_MOTD_STREAK4_REG";
6396 }
6397 }
6398 else
6399 {
6400 key_LINE5A = "WIN_MOTD_LINE5A";
6401 key_LINE5B = "WIN_MOTD_LINE5B";
6402 key_LINE5C = "WIN_MOTD_LINE5C";
6403 key_LINE7A = "WIN_MOTD_LINE7A";
6404 key_LINE7B = "WIN_MOTD_LINE7B";
6405 key_LINE7C = "WIN_MOTD_LINE7C";
6406 key_STREAK1 = "WIN_MOTD_STREAK1";
6407 key_STREAK2 = "WIN_MOTD_STREAK2";
6408 key_STREAK3 = "WIN_MOTD_STREAK3";
6409 key_STREAK4 = "WIN_MOTD_STREAK4";
6410 }
6411
6412 // format for each language
6413 new player;
6414 for(player=1;player<=maxPlayers;player++)
6415 {
6416 if(!is_user_connected(player)) continue;
6417
6418 if(loserDC) formatex(loserName,31,"%L",player,"NO_ONE");
6419 formatex(header,31,"%L",player,"WIN_MOTD_LINE1",winnerName);
6420
6421 len = formatex(motd,1535,"<meta http-equiv=^"Content-Type^" content=^"text/html;charset=UTF-8^">");
6422 len += formatex(motd[len],1535-len,"<body bgcolor=black style=line-height:1;color:white><center><font color=00CC00 size=7 face=Georgia>[GUNGAME] AMXX<p>");
6423
6424 len += formatex(motd[len],1535-len,"<font color=%s size=6 style=letter-spacing:2px>",winnerColor);
6425 len += formatex(motd[len],1535-len,"<div style=height:1px;width:80%%;background-color:%s;overflow:hidden></div>",winnerColor);
6426 if(teamplay) len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE2",winnerTeam); else len += formatex(motd[len],1535-len,"%s",winnerName);
6427 len += formatex(motd[len],1535-len,"<div style=height:1px;width:80%%;background-color:%s;overflow:hidden></div>",winnerColor);
6428 len += formatex(motd[len],1535-len,"<font size=4 color=white style=letter-spacing:1px>%L<p>",player,"WIN_MOTD_LINE3");
6429
6430 if(!teamplay) len += formatex(motd[len],1535-len,"<font size=3>%L</font>.<p>",player,"WIN_MOTD_LINE4A",lvlWeapon[winner],loserColor,loserName);
6431 else len += formatex(motd[len],1535-len,"<font size=3>%L</font>.<p>",player,"WIN_MOTD_LINE4B",lvlWeapon[winner],loserColor,loserName,winnerColor,winnerName);
6432
6433 if(sqlInit && stats_mode == 1)
6434 {
6435 if(teamplay && timeratio)
6436 {
6437 // not enough for a win
6438 if(teamTimes[winner][winningTeam-1]/(teamTimes[winner][winningTeam-1]+teamTimes[winner][losingTeam-1]) < 0.5)
6439 len += formatex(motd[len],1535-len,"<p>");
6440 else
6441 len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5A,winnerColor,winnerName,pointsExtraction[winner][0],winnerWinSuffix,pointsExtraction[winner][3],winnerStreakSuffix,pointsExtraction[winner][4]);
6442
6443 // no play time
6444 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
6445 len += formatex(motd[len],1535-len,"%L<br>",player,"WIN_MOTD_LINE6",0);
6446 else
6447 len += formatex(motd[len],1535-len,"%L<br>",player,"WIN_MOTD_LINE6",floatround(teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1])*100.0));
6448 }
6449 else len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5A,winnerColor,winnerName,pointsExtraction[winner][0],winnerWinSuffix,pointsExtraction[winner][3],winnerStreakSuffix,pointsExtraction[winner][4]);
6450
6451 // we won somehow
6452 if( (!teamplay && winner == player) || (teamplay && !timeratio && winningTeam == _:cs_get_user_team(player)) ||
6453 (teamplay && timeratio && teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]) >= 0.5) )
6454 {
6455 get_number_suffix(pointsExtraction[player][3],myStreakSuffix,2);
6456 len += formatex(motd[len],1535-len,"%L<br>",player,key_LINE7A,pointsExtraction[player][0],pointsExtraction[player][3],myStreakSuffix,pointsExtraction[player][4]);
6457 }
6458
6459 // we didn't get a win
6460 else len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE7B,pointsExtraction[player][0]);
6461
6462 }
6463 else if(sqlInit && stats_mode == 2)
6464 {
6465 if(teamplay && timeratio)
6466 {
6467 // winner didn't play enough to get a win
6468 if(teamTimes[winner][winningTeam-1]/(teamTimes[winner][winningTeam-1]+teamTimes[winner][losingTeam-1]) < 0.5)
6469 len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5B,winnerColor,winnerName,pointsExtraction[winner][2]);
6470 else
6471 len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5C,winnerColor,winnerName,pointsExtraction[winner][0],winnerWinSuffix,pointsExtraction[winner][2],pointsExtraction[winner][3],winnerStreakSuffix,pointsExtraction[winner][4]);
6472
6473 // no play time
6474 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
6475 len += formatex(motd[len],1535-len,"%L<br>",player,"WIN_MOTD_LINE6",0);
6476 else
6477 len += formatex(motd[len],1535-len,"%L<br>",player,"WIN_MOTD_LINE6",floatround(teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1])*100.0));
6478 }
6479 else len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5C,winnerColor,winnerName,pointsExtraction[winner][0],winnerWinSuffix,pointsExtraction[winner][2],pointsExtraction[winner][3],winnerStreakSuffix,pointsExtraction[winner][4]);
6480
6481 len += formatex(motd[len],1535-len,"%L<%s>",player,key_LINE7C,pointsExtraction[player][1],pointsExtraction[player][2],pointsExtraction[player][0],(pointsExtraction[player][3]) ? "p" : "p");
6482
6483 if(pointsExtraction[player][3])
6484 {
6485 get_number_suffix(pointsExtraction[player][3],myStreakSuffix,2);
6486 len += formatex(motd[len],1535-len,"%L<br>",player,key_STREAK1,pointsExtraction[player][3],myStreakSuffix,pointsExtraction[player][4]);
6487 }
6488 }
6489 else len += formatex(motd[len],1535-len,"<p>");
6490
6491 if(sqlInit && stats_mode)
6492 {
6493 if(!pointsExtraction[player][3] && pointsExtraction[player][4]) // I'm not on a streak, but I do have a record
6494 {
6495 len += formatex(motd[len],1535-len,"%L<br>",player,key_STREAK2,pointsExtraction[player][4]);
6496 }
6497
6498 if(pointsExtraction[0][1] == -1) // champion is the previous record holder
6499 {
6500 // there actually was a previous record
6501 if(pointsExtraction[0][0]) len += formatex(motd[len],1535-len,"%L<p>",player,key_STREAK3,pointsExtraction[0][0],spareName);
6502 }
6503 else len += formatex(motd[len],1535-len,"%L<p>",player,key_STREAK4,streakChampColor,spareName,pointsExtraction[0][2]);
6504 }
6505
6506 if(iterations > 0)
6507 {
6508 if(roundsleft <= 0) len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE8A",nextmap);
6509 else if(roundsleft == 1) len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE8B");
6510 else len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE8C",roundsleft);
6511 }
6512
6513 show_motd(player,motd,header);
6514 }
6515
6516 return 1;
6517}
6518
6519//**********************************************************************
6520// TEAMPLAY FUNCTIONS
6521//**********************************************************************
6522
6523// change the score of a team
6524stock teamplay_update_score(team,newScore,exclude=0,direct=0)
6525{
6526 if(team != 1 && team != 2) return;
6527
6528 teamScore[team] = newScore;
6529
6530 new player, sdisplay = get_pcvar_num(gg_status_display);
6531 for(player=1;player<=maxPlayers;player++)
6532 {
6533 if(is_user_connected(player) && player != exclude && _:cs_get_user_team(player) == team)
6534 {
6535 if(direct)
6536 {
6537 score[player] = newScore;
6538 if(sdisplay == STATUS_KILLSLEFT || sdisplay == STATUS_KILLSDONE)
6539 status_display(player);
6540 }
6541 else change_score(player,newScore-score[player],0); // don't refill
6542 }
6543 }
6544}
6545
6546// change the level of a team
6547stock teamplay_update_level(team,newLevel,exclude=0,direct=1)
6548{
6549 if(team != 1 && team != 2) return;
6550
6551 teamLevel[team] = newLevel;
6552 get_level_weapon(teamLevel[team],teamLvlWeapon[team],23);
6553
6554 new player;
6555 for(player=1;player<=maxPlayers;player++)
6556 {
6557 if(is_user_connected(player) && player != exclude && _:cs_get_user_team(player) == team)
6558 {
6559 //if(direct) level[player] = newLevel;
6560 if(direct) set_level_noifandsorbuts(player,newLevel);
6561 else change_level(player,newLevel-level[player],_,_,1); // always score
6562 }
6563 }
6564}
6565
6566// play the taken/tied/lost lead sounds
6567public teamplay_play_lead_sounds(id,oldLevel,Float:playDelay)
6568{
6569 // both teams not initialized yet
6570 if(!teamLevel[1] || !teamLevel[2]) return;
6571
6572 // id: the player whose level changed
6573 // oldLevel: his level before it changed
6574 // playDelay: how long to wait until we play id's sounds
6575
6576 // warmup or game over, no one cares
6577 if(warmup > 0 || won) return;
6578
6579 // no level change
6580 if(level[id] == oldLevel) return;
6581
6582 new team = _:cs_get_user_team(id), otherTeam = (team == 1) ? 2 : 1, thisTeam, player, params[2];
6583 if(team != 1 && team != 2) return;
6584
6585 new leaderLevel, numLeaders, leader = teamplay_get_lead_team(leaderLevel,numLeaders);
6586
6587 // this team is leading
6588 if(leader == team)
6589 {
6590 // the other team here?
6591 if(numLeaders > 1)
6592 {
6593 params[1] = gg_sound_tiedlead;
6594
6595 // play to both teams
6596 for(player=1;player<=maxPlayers;player++)
6597 {
6598 if(!is_user_connected(player)) continue;
6599
6600 thisTeam = _:cs_get_user_team(player);
6601 if(thisTeam == team || thisTeam == otherTeam)
6602 {
6603 params[0] = player;
6604 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
6605 set_task((thisTeam == team) ? playDelay : 0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
6606 }
6607 }
6608 }
6609
6610 // just us, we are the winners!
6611 else
6612 {
6613 // did we just pass the other team?
6614 if(level[id] > oldLevel && teamLevel[otherTeam] == oldLevel)
6615 {
6616 // play to both teams (conditional)
6617 for(player=1;player<=maxPlayers;player++)
6618 {
6619 if(!is_user_connected(player)) continue;
6620
6621 thisTeam = _:cs_get_user_team(player);
6622
6623 if(thisTeam == team) params[1] = gg_sound_takenlead;
6624 else if(thisTeam == otherTeam) params[1] = gg_sound_lostlead;
6625 else continue;
6626
6627 params[0] = player;
6628 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
6629 set_task((thisTeam == team) ? playDelay : 0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
6630 }
6631 }
6632 }
6633 }
6634
6635 // WAS this team on the leader level?
6636 else if(oldLevel == leaderLevel)
6637 {
6638 // play to entire team
6639 for(player=1;player<=maxPlayers;player++)
6640 {
6641 if(!is_user_connected(player)) continue;
6642
6643 thisTeam = _:cs_get_user_team(player);
6644
6645 if(thisTeam == team) params[1] = gg_sound_lostlead;
6646 else if(thisTeam == otherTeam) params[1] = gg_sound_takenlead;
6647 else continue;
6648
6649 params[0] = player;
6650 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
6651 set_task((thisTeam == team) ? playDelay : 0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
6652 }
6653 }
6654}
6655
6656// find the highest level team and such
6657stock teamplay_get_lead_team(&retLevel=0,&retNumLeaders=0,&retRunnerUp=0)
6658{
6659 new leader, numLeaders, runnerUp;
6660
6661 if(teamLevel[1] >= teamLevel[2]) leader = 1;
6662 else leader = 2;
6663
6664 if(teamLevel[1] == teamLevel[2]) numLeaders = 2;
6665 else
6666 {
6667 numLeaders = 1;
6668 runnerUp = (leader == 1) ? 2 : 1;
6669 }
6670
6671 retLevel = teamLevel[leader];
6672 retNumLeaders = numLeaders;
6673 retRunnerUp = runnerUp;
6674
6675 return leader;
6676}
6677
6678// gets the team's level goal without a player passed
6679teamplay_get_team_goal(team)
6680{
6681 if(team != 1 && team != 2) return 0;
6682
6683 new player;
6684 for(player=1;player<=maxPlayers;player++)
6685 {
6686 if(is_user_connected(player) && _:cs_get_user_team(player) == team)
6687 return get_level_goal(teamLevel[team],player);
6688 }
6689
6690 return 0;
6691}
6692
6693//**********************************************************************
6694// AUTOVOTE FUNCTIONS
6695//**********************************************************************
6696
6697// start the autovote
6698public autovote_start()
6699{
6700 // vote in progress
6701 if(autovotes[0] || autovotes[1] || autovotes[2] || task_exists(TASK_AUTOVOTE_RESULT)) return 0;
6702
6703 // if autovote_mode < 0, we haven't actually checked it yet
6704 if(autovote_mode < 0)
6705 {
6706 new info[6];
6707 get_localinfo("gg_av_iter",info,5);
6708 new iter = str_to_num(info);
6709
6710 new rotation[32];
6711 get_pcvar_string(gg_autovote_mode,rotation,31);
6712 new amount = str_count(rotation,',')+1;
6713
6714 if(iter <= 0 || iter > amount)
6715 {
6716 iter = 1;
6717 set_localinfo("gg_av_iter","1");
6718 }
6719
6720 // no rotation, just use the given value
6721 if(amount <= 1)
6722 {
6723 if(iter != 1) set_localinfo("gg_av_iter","1");
6724 autovote_mode = str_to_num(rotation);
6725 }
6726 else
6727 {
6728 for(new i=1;i<=amount;i++)
6729 {
6730 if(contain(rotation,",") != -1)
6731 {
6732 strtok(rotation,info,5,rotation,31,',');
6733 if(i == iter) // this is the one we're looking for
6734 {
6735 autovote_mode = str_to_num(info);
6736 break;
6737 }
6738 }
6739 else // we've stripped away everything else and are left with the last one, so use it
6740 {
6741 autovote_mode = str_to_num(rotation);
6742 break;
6743 }
6744 }
6745
6746 iter++;
6747 if(iter > amount) iter = 1;
6748 num_to_str(iter,info,5);
6749 set_localinfo("gg_av_iter",info);
6750 }
6751 }
6752
6753 // turns out it's disabled
6754 if(autovote_mode <= 0) return 0;
6755
6756 new Float:autovote_time = get_pcvar_float(gg_autovote_time);
6757
6758 new i;
6759 for(i=1;i<=maxPlayers;i++)
6760 {
6761 if(!is_user_connected(i)) continue;
6762
6763 switch(autovote_mode)
6764 {
6765 case 1:
6766 {
6767 formatex(menuText,511,"\y%L^n^n\w1. %L^n2. %L^n^n0. %L",i,"PLAY_GUNGAME",i,"YES",i,"NO",i,"CANCEL");
6768 show_menu(i,MENU_KEY_1|MENU_KEY_2|MENU_KEY_0,menuText,floatround(autovote_time),"autovote_menu");
6769 }
6770 case 2:
6771 {
6772 formatex(menuText,511,"\y%L^n^n\w1. %L^n2. %L^n^n0. %L",i,"PLAY_GUNGAME",i,"YES_TEAMPLAY",i,"YES_REGULAR",i,"CANCEL");
6773 show_menu(i,MENU_KEY_1|MENU_KEY_2|MENU_KEY_0,menuText,floatround(autovote_time),"autovote_menu");
6774 }
6775 default:
6776 {
6777 formatex(menuText,511,"\y%L^n^n\w1. %L^n2. %L^n3. %L^n^n0. %L",i,"PLAY_GUNGAME",i,"YES_TEAMPLAY",i,"YES_REGULAR",i,"NO",i,"CANCEL");
6778 show_menu(i,MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_0,menuText,floatround(autovote_time),"autovote_menu");
6779 }
6780 }
6781 }
6782
6783 gungame_print(0,0,1,"%L",LANG_PLAYER_C,"VOTING_STARTED");
6784 set_task(autovote_time,"autovote_result",TASK_AUTOVOTE_RESULT);
6785
6786 return 1;
6787}
6788
6789// take in votes
6790public autovote_menu_handler(id,key)
6791{
6792 switch(key)
6793 {
6794 case 0: // 1.
6795 {
6796 /* MODE 1- YES
6797 MODE 2- YES_TEAMPLAY
6798 MODE 3- YES_TEAMPLAY */
6799
6800 autovotes[0]++;
6801 }
6802 case 1: // 2.
6803 {
6804 /* MODE 1- NO
6805 MODE 2- YES_REGULAR
6806 MODE 3- YES_REGULAR */
6807
6808 if(autovote_mode == 1) autovotes[2]++;
6809 else autovotes[1]++;
6810 }
6811 case 2: // 3.
6812 {
6813 /* MODE 1-
6814 MODE 2-
6815 MODE 3- NO */
6816
6817 autovotes[2]++;
6818 }
6819 //case 9: 0. /* ALL MODES- CANCEL */ let menu close
6820 }
6821
6822 return PLUGIN_HANDLED;
6823}
6824
6825// calculate end of vote, some of this was thanks to VEN
6826public autovote_result()
6827{
6828 new vYes = autovotes[0] + autovotes[1], vNo = autovotes[2], vTotal = vYes + vNo, vSuccess, teamplay = get_pcvar_num(gg_teamplay), key[16];
6829
6830 switch(autovote_mode)
6831 {
6832 case 1: // this mode asks if they want to play GunGame, yes/no
6833 {
6834 if(vTotal)
6835 {
6836 if(float(vYes) / float(vTotal) >= get_pcvar_float(gg_autovote_ratio))
6837 vSuccess = 1;
6838
6839 // the choice that changes the current game mode is the one that needs to meet the ratio. so if you are
6840 // playing GunGame, at least however many people as defined by the ratio need to vote for it off to switch it,
6841 // and vice-versa.
6842 if( ( ggActive && (float(vNo) / float(vTotal)) < get_pcvar_float(gg_autovote_ratio))
6843 || (!ggActive && (float(vYes) / float(vTotal)) >= get_pcvar_float(gg_autovote_ratio)) )
6844 vSuccess = 1; // means that we will be playing GunGame
6845 }
6846 else if(ggActive) vSuccess = 1;
6847
6848 if(vSuccess && !ggActive)
6849 {
6850 restart_round(5);
6851 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
6852 }
6853 else if(!vSuccess && ggActive)
6854 {
6855 restart_round(5);
6856 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_DISABLE);
6857
6858 set_pcvar_num(gg_enabled,0);
6859 ggActive = 0;
6860 }
6861
6862 if(vSuccess && teamplay) key = "AUTOVOTE_RES1";
6863 else if(vSuccess && !teamplay) key = "AUTOVOTE_RES2";
6864 else key = "AUTOVOTE_RES3";
6865
6866 gungame_print(0,0,1,"%L %i %L - %i %L - %L :: %L",LANG_PLAYER_C,"PLAY_GUNGAME",vYes,LANG_PLAYER_C,"YES",vNo,LANG_PLAYER_C,"NO",LANG_PLAYER_C,"THE_RESULT",LANG_PLAYER_C,key);
6867 }
6868 case 2: // this mode asks if they want to play teamplay, yes/no
6869 {
6870 if(!ggActive)
6871 {
6872 restart_round(5);
6873 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
6874 }
6875
6876 if(vTotal)
6877 {
6878 // see above comment
6879 if( ( teamplay && (float(autovotes[1]) / float(vTotal)) < get_pcvar_float(gg_autovote_ratio))
6880 || (!teamplay && (float(autovotes[0]) / float(vTotal)) >= get_pcvar_float(gg_autovote_ratio)) )
6881 vSuccess = 1; // means that we will be playing teamplay mode
6882 }
6883 else if(teamplay) vSuccess = 1;
6884
6885 if(vSuccess)
6886 {
6887 if(!teamplay)
6888 {
6889 set_pcvar_num(gg_teamplay,1);
6890 if(ggActive && warmup <= 0) restart_round(3);
6891
6892 exec_gg_config_file(0,0);
6893 exec_gg_config_file(1,0);
6894 }
6895
6896 set_task(4.9,"force_teamplay",1);
6897 }
6898 else if(!vSuccess)
6899 {
6900 if(teamplay)
6901 {
6902 set_pcvar_num(gg_teamplay,0);
6903 if(ggActive && warmup <= 0) restart_round(3);
6904
6905 exec_gg_config_file(0,0);
6906 }
6907
6908 set_task(4.9,"force_teamplay",0);
6909 }
6910
6911 if(vSuccess) key = "AUTOVOTE_RES1";
6912 else key = "AUTOVOTE_RES2";
6913
6914 gungame_print(0,0,1,"%L %i %L - %i %L - %L :: %L",LANG_PLAYER_C,"PLAY_GUNGAME",autovotes[0],LANG_PLAYER_C,"YES_TEAMPLAY",autovotes[1],LANG_PLAYER_C,"YES_REGULAR",LANG_PLAYER_C,"THE_RESULT",LANG_PLAYER_C,key);
6915 }
6916 default: // this mode asks if they want to play, teamplay/regular/no
6917 {
6918 if(vTotal)
6919 {
6920 // see above comment
6921 if( ( ggActive && (float(vNo) / float(vTotal)) < get_pcvar_float(gg_autovote_ratio))
6922 || (!ggActive && (float(vYes) / float(vTotal)) >= get_pcvar_float(gg_autovote_ratio)) )
6923 vSuccess = 1; // means that we will be playing GunGame
6924 }
6925 else if(ggActive) vSuccess = 1;
6926
6927 if(vSuccess)
6928 {
6929 if(!ggActive)
6930 {
6931 restart_round(5);
6932 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
6933 }
6934
6935 // pick a random value for teamplay if we need it, then see if we should be using it.
6936 // use it in the case that we have a tie, and we are using random teamplay mode.
6937 new rand_val = random_num(0,1);
6938 new use_rand = (autovotes[0] == autovotes[1] && (teamplay == 2 || initTeamplayInt == 2));
6939
6940 if(autovotes[0] > autovotes[1] || (use_rand && rand_val == 1)) // more votes for teamplay
6941 {
6942 if(!teamplay)
6943 {
6944 set_pcvar_num(gg_teamplay,1);
6945 if(ggActive && warmup <= 0) restart_round(3);
6946
6947 exec_gg_config_file(0,0);
6948 exec_gg_config_file(1,0);
6949 }
6950
6951 key = "AUTOVOTE_RES1";
6952 set_task(4.9,"force_teamplay",1);
6953 }
6954 else if(autovotes[0] < autovotes[1] || (use_rand && rand_val == 0)) // more votes for regular
6955 {
6956 if(teamplay)
6957 {
6958 set_pcvar_num(gg_teamplay,0);
6959 if(ggActive && warmup <= 0) restart_round(3);
6960
6961 exec_gg_config_file(0,0);
6962 }
6963
6964 key = "AUTOVOTE_RES2";
6965 set_task(4.9,"force_teamplay",0);
6966 }
6967 else // if equal, leave it be
6968 {
6969 if(teamplay)
6970 {
6971 key = "AUTOVOTE_RES1";
6972 set_task(4.9,"force_teamplay",1);
6973 }
6974 else
6975 {
6976 key = "AUTOVOTE_RES2";
6977 set_task(4.9,"force_teamplay",0);
6978 }
6979 }
6980 }
6981 else if(!vSuccess && ggActive)
6982 {
6983 restart_round(5);
6984 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_DISABLE);
6985
6986 set_pcvar_num(gg_enabled,0);
6987 ggActive = 0;
6988 }
6989
6990 if(!vSuccess) key = "AUTOVOTE_RES3";
6991 gungame_print(0,0,1,"%L %i %L - %i %L - %i %L - %L :: %L",LANG_PLAYER_C,"PLAY_GUNGAME",autovotes[0],LANG_PLAYER_C,"YES_TEAMPLAY",autovotes[1],LANG_PLAYER_C,"YES_REGULAR",vNo,LANG_PLAYER_C,"NO",LANG_PLAYER_C,"THE_RESULT",LANG_PLAYER_C,key);
6992 }
6993 }
6994
6995 autovotes[0] = 0;
6996 autovotes[1] = 0;
6997 autovotes[2] = 0;
6998}
6999
7000// force teamplay mode to what we want after a vote has been completed.
7001// otherwise, when switching from GunGame off to on, it will be overwritten by gungame.cfg.
7002public force_teamplay(teamplay)
7003{
7004 set_pcvar_num(gg_teamplay,teamplay);
7005
7006 exec_gg_config_file(0,0);
7007 if(teamplay) exec_gg_config_file(1,0);
7008}
7009
7010//**********************************************************************
7011// STAT FUNCTIONS
7012//**********************************************************************
7013
7014// clear out everything about our stats
7015stats_clear_all()
7016{
7017#if !defined SQL
7018 // destroy large array
7019 if(statsArray) ArrayDestroy(statsArray);
7020
7021 // destroy the two "pointer" arrays
7022 if(statsPointers[0])
7023 {
7024 ArrayDestroy(statsPointers[0]);
7025 statsSize[0] = 0;
7026 }
7027 if(statsPointers[1])
7028 {
7029 ArrayDestroy(statsPointers[1]);
7030 statsSize[1] = 0;
7031 }
7032#endif
7033
7034 // clear out saved stats data for the players
7035 for(new i=1;i<=maxPlayers;i++)
7036 {
7037 statsPosition[i][0] = 0;
7038 statsPosition[i][1] = 0;
7039 stats_clear_struct(playerStats[i]);
7040 }
7041
7042 // clear out saved stats data from the temp saves
7043 for(new i=0;i<TEMP_SAVES;i++)
7044 {
7045 tempSave[i][svStatsPosition][0] = 0;
7046 tempSave[i][svStatsPosition][1] = 0;
7047 }
7048}
7049
7050#if !defined SQL
7051// converts a stats string (as in gungame.stats) into a stats struct (by reference)
7052stats_str_to_struct(str[],struct[statsData])
7053{
7054 // The format for str (as saved in gungame.stats):
7055 //
7056 // AUTHID WINS LAST_USED_NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
7057
7058 static piece[32], whole[112];
7059 piece[0] = 0;
7060 whole[0] = 0;
7061
7062 stats_clear_struct(struct);
7063
7064 strtok(str,piece,31,whole,111,'^t');
7065 copy(struct[sdAuthid],31,piece);
7066
7067 strtok(whole,piece,5,whole,111,'^t');
7068 struct[sdWins][0] = str_to_num(piece);
7069
7070 strtok(whole,piece,31,whole,111,'^t');
7071 copy(struct[sdName],31,piece);
7072
7073 strtok(whole,piece,11,whole,111,'^t');
7074 struct[sdTimestamp] = str_to_num(piece);
7075
7076 strtok(whole,piece,7,whole,111,'^t');
7077 struct[sdPoints][0] = str_to_num(piece);
7078
7079 strtok(whole,piece,3,whole,111,'^t');
7080 struct[sdStreak][0] = str_to_num(piece);
7081
7082 strtok(whole,piece,5,whole,111,'^t');
7083 struct[sdWins][1] = str_to_num(piece);
7084
7085 strtok(whole,piece,7,whole,3,'^t');
7086 struct[sdPoints][1] = str_to_num(piece);
7087
7088 //strtok(whole,piece,3,whole,1,'^t');
7089 struct[sdStreak][1] = str_to_num(whole);
7090}
7091#endif
7092
7093#if defined SQL
7094// sql version] much simpler.
7095stock stats_refresh_timestamp(authid[])
7096{
7097 if(!sqlInit) return 0;
7098
7099 SQL_QuoteString(db,safeName,63,authid);
7100 query = SQL_PrepareQuery(db,"UPDATE `%s` SET timestamp='%i' WHERE authid='%s' AND serverip='%s' LIMIT 1;",sqlTable,get_systime(),safeName,serverip);
7101
7102 SQL_ExecuteAndLog(query);
7103 SQL_FreeHandle(query);
7104
7105 return 1;
7106}
7107#else
7108// IT'S BEEN REVIVED!
7109//
7110// This took me way too long to develop, but it actually is able to overwrite the
7111// old timestamp without having to copy over to a new file or anything. It should
7112// work until sometime in the year 2287 when Unix timestamps will be 11 digits.
7113//
7114// On my old 2.80 gHz Pentium IV, it takes less than a tenth of a second to read to
7115// the bottom of a 10,000 line stats file and then refresh the final player's timestamp.
7116stock stats_refresh_timestamp(authid[])
7117{
7118 get_pcvar_string(gg_stats_file,sfFile,63);
7119 if(!file_exists(sfFile)) return 0;
7120
7121 // Open in binary mode, because text mode can present issues regarding
7122 // carriage returns and line feeds.
7123 // (See http://support.microsoft.com/default.aspx?scid=kb;en-us;68337)
7124 new file = fopen(sfFile,"rb+");
7125 if(!file) return 0;
7126
7127 new readLen, authidLen = strlen(authid), q;
7128 formatex(sfTimestamp,10,"%10i",get_systime());
7129
7130 while(!feof(file))
7131 {
7132 new start_of_line = ftell(file); // remember start of line position
7133
7134 readLen = fgets(file,sfLineData,111);
7135
7136 // check to see if this line starts with our authid
7137 for(q=0;q<authidLen;q++)
7138 {
7139 if(sfLineData[q] != authid[q]) { q = 255; break; }
7140 }
7141
7142 // if q == 255, we definitely did not have a match.
7143 // else, if sfLineData[q] != '^t', our authid is probably
7144 // a proper prefix for the authid on this line.
7145 if(q == 255 || sfLineData[q] != '^t')
7146 {
7147 q = 255;
7148 continue;
7149 }
7150
7151 q++; // q was pointing to first tab, so skip over it
7152 new tabsFound = 1;
7153 while(sfLineData[q] != 0)
7154 {
7155 // keep going until we find the 3rd tab overall
7156 if(sfLineData[q] == '^t' && ++tabsFound == 3) break;
7157 q++;
7158 }
7159
7160 // somehow we did not encounter the 3rd tab, abort
7161 if(tabsFound != 3 || sfLineData[q] != '^t')
7162 {
7163 log_amx("Error in stats_refresh_timestamp -- stats file formatted incorrectly");
7164 q = 255;
7165 break;
7166 }
7167
7168 q++; // q was the position of the tab before the timestamp, so skip over it
7169
7170 // Make sure we have room for the timestamp (though we might assume it).
7171 // Minimum 12 characters: 10 for the timestamp, 1 for carriage return, 1 for newline.
7172 if(readLen-q >= 12)
7173 {
7174 fseek(file,start_of_line+q,SEEK_SET);
7175 fwrite_blocks(file,sfTimestamp,10,BLOCK_CHAR); // overwrite timestamp with current one
7176 }
7177
7178 break;
7179 }
7180
7181 fclose(file);
7182
7183 return (q != 255);
7184}
7185#endif
7186
7187// we now have a super-duper function so that we only have to go through
7188// the stats file once, instead of rereading and rewriting it for every
7189// single player in a points match.
7190//
7191// also, timestamps are refreshed here, instead of every time you join.
7192public stats_award_points(winner)
7193{
7194 new stats_mode = get_pcvar_num(gg_stats_mode);
7195 if(!sqlInit || !stats_mode) return;
7196
7197 new teamplay = get_pcvar_num(gg_teamplay), winningTeam =_:cs_get_user_team(winner),
7198 losingTeam = _:(!(winningTeam-1)) + 1, stats_ip = get_pcvar_num(gg_stats_ip),
7199 timeratio = get_pcvar_num(gg_teamplay_timeratio), ignore_bots = get_pcvar_num(gg_ignore_bots);
7200
7201 new si = get_gg_si();
7202
7203 new player, playerWins[32], playerPoints[32], playerStreak[32], playerAuthid[32][32], playerName[32][32],
7204 playerTotalPoints[32], players[32], intStreak, playerNum, i, team, Float:time = get_gametime(), systime = get_systime();
7205
7206#if defined SQL
7207 new playerSafeName[32][64], playerSafeAuthid[32][64];
7208#endif
7209
7210 //new botid;
7211
7212 get_players(players,playerNum);
7213 for(i=0;i<playerNum;i++)
7214 {
7215 player = players[i];
7216
7217 // keep track of time
7218 team = _:cs_get_user_team(player);
7219 if(team == 1 || team == 2) teamTimes[player][team-1] += time - lastSwitch[player];
7220 lastSwitch[player] = time;
7221
7222 get_gg_authid(player,playerAuthid[i],31,stats_ip);
7223 get_user_name(player,playerName[i],31);
7224
7225 /*if(equal(playerAuthid[i],"BOT"))
7226 {
7227 botid++;
7228 formatex(playerAuthid[i],31,"BOT_%i",botid);
7229 }*/
7230
7231#if defined SQL
7232 SQL_QuoteString(db,playerSafeName[i],63,playerName[i]);
7233 SQL_QuoteString(db,playerSafeAuthid[i],63,playerAuthid[i]);
7234#endif
7235
7236 // solo wins only
7237 if(player == winner && stats_mode == 1 && !teamplay)
7238 playerWins[i] = 1;
7239 }
7240
7241 //
7242 // COLLECT LIST OF PLAYERS AND THEIR INFORMATION
7243 //
7244
7245 // points system
7246 if(stats_mode == 2)
7247 {
7248 new wins, Float:flPoints, Float:winbonus = get_pcvar_float(gg_stats_winbonus), Float:percent;
7249
7250 for(i=0;i<playerNum;i++)
7251 {
7252 player = players[i];
7253 if(!is_user_connected(player) || (ignore_bots && is_user_bot(player)))
7254 continue;
7255
7256 wins = 0; // WHY DID I FORGET THIS BEFORE
7257
7258 // point ratio based on time played in teamplay
7259 if(teamplay && timeratio)
7260 {
7261 // no play time
7262 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
7263 continue;
7264
7265 // give us points from losing team
7266 flPoints = (float(teamLevel[losingTeam]) - 1.0) * teamTimes[player][losingTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]);
7267
7268 // give us points from winning team
7269 percent = teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]);
7270 flPoints += (float(teamLevel[winningTeam]) - 1.0) * percent;
7271
7272 // we played over half on winning team, give us a bonus and win
7273 if(percent >= 0.5)
7274 {
7275 flPoints *= winbonus;
7276 wins = 1;
7277 }
7278 }
7279 else
7280 {
7281 flPoints = float(level[player]) - 1.0; // calculate points and add
7282
7283 // winner gets bonus points plus a win
7284 if(player == winner || (teamplay && _:cs_get_user_team(player) == winningTeam))
7285 {
7286 flPoints *= winbonus;
7287 wins = 1;
7288 }
7289 }
7290
7291 // unnecessary
7292 if(flPoints < 0.5 && !wins) continue;
7293
7294 playerWins[i] = wins;
7295 playerPoints[i] = floatround(flPoints);
7296 if(playerPoints[i] < 0) playerPoints[i] = 0;
7297 }
7298 }
7299
7300 // regular wins teamplay (solo regular wins is above)
7301 else if(teamplay)
7302 {
7303 for(i=0;i<playerNum;i++)
7304 {
7305 player = players[i];
7306 if(_:cs_get_user_team(player) == winningTeam && (!ignore_bots || !is_user_bot(player)))
7307 {
7308 // no play time
7309 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
7310 continue;
7311
7312 // have to play at least half to get a win
7313 if(!timeratio || teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]) >= 0.5)
7314 playerWins[i] = 1;
7315 }
7316 }
7317 }
7318
7319 //
7320 // START MANAGING STREAKS
7321 //
7322
7323 new streakToBeat = 0, streakChamp = -1;
7324
7325#if defined SQL
7326 new champRowExists;
7327
7328 // find current champion's info
7329 query = SQL_PrepareQuery(db,"SELECT streak,name FROM `%s` WHERE type='%iR' AND serverip='%s' LIMIT 1;",sqlStreakTable,si,serverip);
7330 if(SQL_ExecuteAndLog(query))
7331 {
7332 if(SQL_NumResults(query))
7333 {
7334 champRowExists = 1;
7335 streakToBeat = SQL_ReadResult(query,0);
7336 SQL_ReadResult(query,1,spareName,31);
7337 }
7338 }
7339 SQL_FreeHandle(query);
7340
7341 pointsExtraction[0][0] = streakToBeat; // record old record
7342 pointsExtraction[0][1] = -1; // record champion
7343
7344 new len, origLen;
7345
7346 // form query to delete players who did not win this round from the cache
7347 origLen = len = formatex(mkQuery,1535,"DELETE FROM `%s` WHERE type='%iC' AND serverip='%s' AND authid NOT IN (",sqlStreakTable,si,serverip);
7348 for(i=0;i<playerNum;i++)
7349 {
7350 if(playerWins[i]) len += formatex(mkQuery[len],1535-len,"%s'%s'",(len!=origLen) ? "," : "",playerSafeAuthid[i]);
7351 }
7352
7353 if(len == origLen)
7354 {
7355 // no one was added, then add empty apostrophes to avoid SQL syntax error
7356 len += formatex(mkQuery[len],1535-len,"''");
7357 }
7358
7359 len += formatex(mkQuery[len],1535-len,");");
7360
7361 query = SQL_PrepareQuery(db,mkQuery);
7362 SQL_ExecuteAndLog(query);
7363 SQL_FreeHandle(query);
7364
7365 // use one query to get all current streaks, then process them in the loop and save to playerStreak array
7366 new authid[64];
7367 query = SQL_PrepareQuery(db,"SELECT authid,streak FROM `%s` WHERE type='%iC' AND serverip='%s';",sqlStreakTable,si,serverip);
7368 if(SQL_ExecuteAndLog(query) && SQL_NumResults(query))
7369 {
7370 while(SQL_MoreResults(query))
7371 {
7372 SQL_ReadResult(query,0,authid,63);
7373
7374 for(i=0;i<playerNum;i++)
7375 {
7376 if(equal(authid,playerSafeAuthid[i]))
7377 {
7378 playerStreak[i] = SQL_ReadResult(query,1);
7379 break;
7380 }
7381 }
7382
7383 SQL_NextRow(query);
7384 }
7385 }
7386 SQL_FreeHandle(query);
7387
7388 len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` VALUES ",sqlStreakTable);
7389
7390 // now go through players, and make sure to start a streak for those who don't have one
7391 for(i=0;i<playerNum;i++)
7392 {
7393 if(playerWins[i])
7394 {
7395 playerStreak[i]++; // if they won, they get one added to their streak
7396
7397 // am I the champion?? also, in the case of a tie, allow the winner of this round to be considered the champ
7398 if(playerStreak[i] > streakToBeat || (streakChamp != -1 && playerStreak[i] == streakToBeat && players[i] == winner))
7399 {
7400 streakToBeat = playerStreak[i];
7401 streakChamp = i;
7402 }
7403
7404 // didn't find one with previous loop
7405 if(playerStreak[i] == 1) // because of increment above, will be 1 at minimum
7406 {
7407 // insert a new row. note that the streak is inserted as 0 because it gets incremented below.
7408 len += formatex(mkQuery[len],1535-len,"('%iC','%s','0','%s','%i','%s'),",si,playerSafeAuthid[i],playerSafeName[i],systime,serverip);
7409
7410 // cash in if we get too close
7411 if(len >= 1200)
7412 {
7413 mkQuery[len-1] = ';';
7414 mkQuery[len] = 0;
7415
7416 query = SQL_PrepareQuery(db,mkQuery);
7417 SQL_ExecuteAndLog(query);
7418 SQL_FreeHandle(query);
7419
7420 len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` VALUES ",sqlStreakTable);
7421 }
7422 }
7423 }
7424 }
7425
7426 // if we have some leftover query
7427 if(len > origLen)
7428 {
7429 mkQuery[len-1] = ';';
7430 mkQuery[len] = 0;
7431
7432 query = SQL_PrepareQuery(db,mkQuery);
7433 SQL_ExecuteAndLog(query);
7434 SQL_FreeHandle(query);
7435 }
7436
7437 // update everyone's current streaks by +1
7438 query = SQL_PrepareQuery(db,"UPDATE `%s` SET streak=streak+1 WHERE type='%iC' AND serverip='%s';",sqlStreakTable,si,serverip);
7439 SQL_ExecuteAndLog(query);
7440 SQL_FreeHandle(query);
7441
7442 // somebody beat the champion!
7443 if(streakToBeat && streakChamp != -1)
7444 {
7445 if(champRowExists) query = SQL_PrepareQuery(db,"UPDATE `%s` SET authid='%s', streak='%i', name='%s', timestamp='%i' WHERE type='%iR' AND serverip='%s' LIMIT 1;",sqlStreakTable,playerSafeAuthid[streakChamp],streakToBeat,playerSafeName[streakChamp],systime,si,serverip);
7446 else query = SQL_PrepareQuery(db,"INSERT INTO `%s` VALUES ('%iR','%s','%i','%s',%i,'%s');",sqlStreakTable,si,playerSafeAuthid[streakChamp],streakToBeat,playerSafeName[streakChamp],systime,serverip);
7447
7448 SQL_ExecuteAndLog(query);
7449 SQL_FreeHandle(query);
7450 }
7451#else
7452 // go through and copy down the list of players who got a win last round
7453 new sfStreak[2][4], tempFileName[65], streakRecord[53], streakFile, stats_streak_file[64];
7454 get_pcvar_string(gg_stats_streak_file,stats_streak_file,63);
7455
7456 if(file_exists(stats_streak_file))
7457 {
7458 formatex(tempFileName,64,"%s2",stats_streak_file); // our temp file, append 2
7459 rename_file(stats_streak_file,tempFileName,1); // copy it over to the temp name (relative flag)
7460
7461 // there are two basic types of lines:
7462 // a record line, ie: 0R AUTHID STREAK NAME TIMESTAMP
7463 // a "cache" line (a player with a running streak), ie: 0C AUTHID STREAK
7464 // the first digit is the stats index (0 for regular, 1 for teamplay)
7465
7466 new streakTempFile = fopen(tempFileName,"rt");
7467 streakFile = fopen(stats_streak_file,"wt");
7468
7469 // go through the old file, copy over unimportant lines, figure out the record, and distribute streaks
7470 while(!feof(streakTempFile))
7471 {
7472 fgets(streakTempFile,sfLineData,82);
7473 if(!sfLineData[0]) continue;
7474
7475 // not for our stats mode
7476 if(str_to_num(sfLineData[0]) != si)
7477 {
7478 fprintf(streakFile,sfLineData); // just copy it over
7479 continue;
7480 }
7481
7482 // this is the record
7483 if(sfLineData[1] == 'R')
7484 {
7485 copy(streakRecord,82,sfLineData); // just save it for now
7486 continue;
7487 }
7488
7489 // this is a cache (a player who got a win last game)
7490 if(sfLineData[1] == 'C')
7491 {
7492 // use sfLineData[3] to skip characters "0C " (should be a tab)
7493 strtok(sfLineData[3],sfAuthid,31,sfStreak[0],3,'^t'); // split them up
7494
7495 for(i=0;i<playerNum;i++)
7496 {
7497 // if we won, and we haven't read our streak yet, and this is our authid
7498 if(playerWins[i] && !playerStreak[i] && equal(playerAuthid[i],sfAuthid))
7499 {
7500 playerStreak[i] = str_to_num(sfStreak[0]); // set our streak to our previous one
7501 break; // in the case of duplicate authids, don't continually add to the streak
7502 }
7503 }
7504
7505 continue;
7506 }
7507
7508 // don't copy over the rest, so it sort of auto-prunes
7509 //fprintf(streakFile,sfLineData); // OLD COMMENT IGNORE ME: anything else, we might as well copy it
7510 }
7511
7512 // get rid of the old copy
7513 fclose(streakTempFile);
7514 delete_file(tempFileName);
7515 }
7516 else if(stats_streak_file[0]) streakFile = fopen(stats_streak_file,"wt"); // did not exist, create it
7517
7518 //new streakToBeat = 0, streakChamp = -1;
7519 if(streakRecord[0]) // if there was a record, figure it out
7520 {
7521 strtok(streakRecord[3],dummy,1,sfLineData,82,'^t'); // cut off prefix and authid from the left
7522 strtok(sfLineData,sfStreak[0],3,spareName,31,'^t'); // get our streak, and the name as well
7523
7524 new pos = contain_char(spareName,'^t');
7525 if(pos != -1) spareName[pos] = 0; // cut off the timestamp from the name
7526
7527 streakToBeat = str_to_num(sfStreak[0]);
7528 }
7529
7530 pointsExtraction[0][0] = streakToBeat; // record old record
7531 pointsExtraction[0][1] = -1; // record champion
7532
7533 // now go through current players, and rewrite them with their new streaks into the file
7534 for(i=0;i<playerNum;i++)
7535 {
7536 if(playerWins[i])
7537 {
7538 playerStreak[i]++; // if they won, they get one added to their streak
7539
7540 // am I the champion?? also, in the case of a tie, allow the winner of this round to be considered the champ
7541 if(playerStreak[i] > streakToBeat || (streakChamp != -1 && playerStreak[i] == streakToBeat && players[i] == winner))
7542 {
7543 streakToBeat = playerStreak[i];
7544 streakChamp = i;
7545 }
7546
7547 if(streakFile)
7548 {
7549 fprintf(streakFile,"%iC^t%s^t%i",si,playerAuthid[i],playerStreak[i]);
7550 fputc(streakFile,'^n');
7551 }
7552 }
7553 }
7554
7555 if(streakFile)
7556 {
7557 if(streakToBeat)
7558 {
7559 if(streakChamp == -1) fprintf(streakFile,"%s",streakRecord); // no one here beat the champ, reprint old champ
7560 else // otherwise, give the new guy credit
7561 {
7562 fprintf(streakFile,"%iR^t%s^t%i^t%s^t%i",si,playerAuthid[streakChamp],playerStreak[streakChamp],playerName[streakChamp],systime);
7563 fputc(streakFile,'^n');
7564 }
7565 }
7566
7567 fclose(streakFile);
7568 }
7569#endif
7570
7571 // we have a new champion, record their stuff
7572 if(streakChamp != -1)
7573 {
7574 pointsExtraction[0][1] = players[streakChamp]; // record champion
7575 pointsExtraction[0][2] = playerStreak[streakChamp]; // record new record
7576 copy(spareName,31,playerName[streakChamp]);
7577 }
7578
7579 //
7580 // NOW GO THROUGH THE FILE
7581 //
7582
7583#if defined SQL
7584 new sfWins[2], sfPoints[2], sfStreak[2];
7585
7586 // do one query to grab all stats we need
7587 len = formatex(mkQuery,1535,"SELECT authid,wins,points,streak,wins_tp,points_tp,streak_tp FROM `%s` WHERE serverip='%s' AND authid IN(",sqlTable,serverip);
7588 for(i=0;i<playerNum;i++)
7589 {
7590 len += formatex(mkQuery[len],1535-len,"'%s'%s",playerSafeAuthid[i],(i==playerNum-1) ? "" : ",");
7591 }
7592 len += formatex(mkQuery[len],1535-len,");");
7593
7594 // go through results
7595 new foundMe[33];
7596
7597 query = SQL_PrepareQuery(db,mkQuery);
7598 if(SQL_ExecuteAndLog(query) && SQL_NumResults(query))
7599 {
7600 new Handle:query2;
7601
7602 while(SQL_MoreResults(query))
7603 {
7604 SQL_ReadResult(query,0,authid,63);
7605
7606 for(i=0;i<playerNum;i++)
7607 {
7608 if(foundMe[i] || !equal(authid,playerSafeAuthid[i])) continue;
7609
7610 // remember for later iterations
7611 foundMe[i] = 1;
7612
7613 sfWins[0] = SQL_ReadResult(query,1);
7614 sfPoints[0] = SQL_ReadResult(query,2);
7615 sfStreak[0] = SQL_ReadResult(query,3);
7616 sfWins[1] = SQL_ReadResult(query,4);
7617 sfPoints[1] = SQL_ReadResult(query,5);
7618 sfStreak[1] = SQL_ReadResult(query,6);
7619
7620 // if we set a new personal streak record, save it
7621 intStreak = sfStreak[si];
7622 pointsExtraction[players[i]][4] = intStreak; // show old record
7623 if(playerStreak[i] > intStreak) intStreak = playerStreak[i];
7624
7625 // so we can reference this for the MOTD
7626 playerTotalPoints[i] = playerPoints[i] + sfPoints[si];
7627 pointsExtraction[players[i]][0] = sfWins[si] + playerWins[i];
7628 pointsExtraction[players[i]][1] = playerPoints[i];
7629 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7630 pointsExtraction[players[i]][3] = playerStreak[i];
7631 //pointsExtraction[players[i]][4] = intStreak; // uncomment to show new record
7632
7633 // something worth updating
7634 if(playerWins[i] || playerPoints[i])
7635 {
7636 if(si == 0) query2 = SQL_PrepareQuery(db,"UPDATE `%s` SET wins=wins+'%i',name='%s',timestamp='%i',points=points+'%i',streak='%i' WHERE authid='%s' AND serverip='%s' LIMIT 1;",sqlTable,playerWins[i],playerSafeName[i],systime,playerPoints[i],intStreak,playerSafeAuthid[i],serverip);
7637 else query2 = SQL_PrepareQuery(db,"UPDATE `%s` SET wins_tp=wins_tp+'%i',name='%s',timestamp='%i',points_tp=points_tp+'%i',streak_tp='%i' WHERE authid='%s' AND serverip='%s' LIMIT 1;",sqlTable,playerWins[i],playerSafeName[i],systime,playerPoints[i],intStreak,playerSafeAuthid[i],serverip);
7638
7639 SQL_ExecuteAndLog(query2);
7640 SQL_FreeHandle(query2);
7641 }
7642
7643 break;
7644 }
7645
7646 SQL_NextRow(query);
7647 }
7648 }
7649 SQL_FreeHandle(query);
7650
7651 len = 0;
7652
7653 // go through again for people we didn't find
7654 for(i=0;i<playerNum;i++)
7655 {
7656 // nothing worth reporting, still refresh timestamp
7657 if(!playerWins[i] && !playerPoints[i])
7658 {
7659 stats_refresh_timestamp(playerAuthid[i]);
7660 continue;
7661 }
7662
7663 // already found me
7664 if(foundMe[i]) continue;
7665
7666 // so we can reference this for the MOTD
7667 playerTotalPoints[i] = playerPoints[i];
7668 pointsExtraction[players[i]][0] = playerWins[i];
7669 pointsExtraction[players[i]][1] = playerPoints[i];
7670 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7671 pointsExtraction[players[i]][3] = playerStreak[i];
7672 pointsExtraction[players[i]][4] = 0; // show old record
7673
7674 // haven't started yet
7675 if(len == 0)
7676 {
7677 if(si == 0) len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` (authid,name,timestamp,wins,points,streak,serverip) VALUES ",sqlTable);
7678 else len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` (authid,name,timestamp,wins_tp,points_tp,streak_tp,serverip) VALUES ",sqlTable);
7679 }
7680
7681 len += formatex(mkQuery[len],1535-len,"('%s','%s','%i','%i','%i','%i','%s'),",playerSafeAuthid[i],playerSafeName[i],systime,playerWins[i],playerTotalPoints[i],playerStreak[i],serverip);
7682
7683 // cash in if we get too close
7684 if(len >= 1200)
7685 {
7686 mkQuery[len-1] = ';';
7687 mkQuery[len] = 0;
7688
7689 query = SQL_PrepareQuery(db,mkQuery);
7690 SQL_ExecuteAndLog(query);
7691 SQL_FreeHandle(query);
7692
7693 len = 0;
7694 }
7695 }
7696
7697 // if we have some leftover query
7698 if(len && len > origLen)
7699 {
7700 mkQuery[len-1] = ';';
7701 mkQuery[len] = 0;
7702
7703 query = SQL_PrepareQuery(db,mkQuery);
7704 SQL_ExecuteAndLog(query);
7705 SQL_FreeHandle(query);
7706 }
7707#else
7708 new file;
7709 get_pcvar_string(gg_stats_file,sfFile,63);
7710 formatex(tempFileName,64,"%s2",sfFile); // our temp file, append 2
7711
7712 // create file if it does not exist (thanks Min2liz)
7713 if(!file_exists(sfFile))
7714 {
7715 file = fopen(sfFile,"wt");
7716 fclose(file);
7717 }
7718
7719 rename_file(sfFile,tempFileName,1); // copy over current stat file (relative flag)
7720 if(!file_exists(tempFileName)) return; // rename failed?
7721
7722 new sfWins[2][6], sfPoints[2][8], set[32], setNum;
7723
7724 new tempFile = fopen(tempFileName,"rt"), lastSetNum;
7725 file = fopen(sfFile,"wt");
7726
7727 // go through our old copy and rewrite entries
7728 while(tempFile && file && !feof(tempFile))
7729 {
7730 fgets(tempFile,sfLineData,111);
7731 if(!sfLineData[0]) continue;
7732
7733 // still have scores to add to
7734 lastSetNum = setNum;
7735 if(setNum < playerNum)
7736 {
7737 strtok(sfLineData,sfAuthid,31,sfLineData,111,'^t'); // isolate authid
7738
7739 // see if we need to change this one
7740 for(i=0;i<playerNum;i++)
7741 {
7742 if(!set[i] && equal(playerAuthid[i],sfAuthid))
7743 {
7744 set[i] = 1;
7745 setNum++;
7746
7747 strtok(sfLineData,sfWins[0],5,sfLineData,111,'^t'); // get wins
7748 strtok(sfLineData,dummy,1,sfLineData,111,'^t'); // get name
7749 strtok(sfLineData,dummy,1,sfLineData,111,'^t'); // get timestamp
7750 strtok(sfLineData,sfPoints[0],7,sfLineData,111,'^t'); // get points
7751 strtok(sfLineData,sfStreak[0],3,sfLineData,111,'^t'); // get streak
7752
7753 strtok(sfLineData,sfWins[1],5,sfLineData,111,'^t'); // get teamplay wins
7754 strtok(sfLineData,sfPoints[1],7,sfStreak[1],3,'^t'); // get teamplay points and teamplay streak
7755
7756 // if we set a new personal streak record, save it
7757 intStreak = str_to_num(sfStreak[si]);
7758 pointsExtraction[players[i]][4] = intStreak; // show old record
7759 if(playerStreak[i] > intStreak) intStreak = playerStreak[i];
7760
7761 playerTotalPoints[i] = playerPoints[i] + str_to_num(sfPoints[si]); // AUTHID WINS NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
7762
7763 if(si == 0) fprintf(file,"%s^t%i^t%s^t%i^t%i^t%i^t%i^t%i^t%i",sfAuthid,str_to_num(sfWins[0])+playerWins[i],playerName[i],systime,playerTotalPoints[i],intStreak,str_to_num(sfWins[1]),str_to_num(sfPoints[1]),str_to_num(sfStreak[1]));
7764 else fprintf(file,"%s^t%i^t%s^t%i^t%i^t%i^t%i^t%i^t%i",sfAuthid,str_to_num(sfWins[0]),playerName[i],systime,str_to_num(sfPoints[0]),str_to_num(sfStreak[0]),str_to_num(sfWins[1])+playerWins[i],playerTotalPoints[i],intStreak);
7765
7766 fputc(file,'^n');
7767
7768 // so we can reference this for the MOTD
7769 pointsExtraction[players[i]][0] = str_to_num(sfWins[si])+playerWins[i];
7770 pointsExtraction[players[i]][1] = playerPoints[i];
7771 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7772 pointsExtraction[players[i]][3] = playerStreak[i];
7773 //pointsExtraction[players[i]][4] = intStreak; // uncomment to show new record
7774
7775 break;
7776 }
7777 }
7778
7779 // nothing to replace, just copy it over (newline is already included)
7780 if(lastSetNum == setNum) fprintf(file,"%s^t%s",sfAuthid,sfLineData); // we cut authid earlier
7781 }
7782 else fprintf(file,"%s",sfLineData); // nothing to replace, just copy it over (newline is already included)
7783 }
7784
7785 for(i=0;i<playerNum;i++)
7786 {
7787 // never found an existing entry, make a new one
7788 if(!set[i] && (playerWins[i] || playerPoints[i]))
7789 {
7790 playerTotalPoints[i] = playerPoints[i]; // AUTHID WINS NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
7791
7792 if(si == 0) fprintf(file,"%s^t%i^t%s^t%i^t%i^t%i^t0^t0^t0",playerAuthid[i],playerWins[i],playerName[i],systime,playerTotalPoints[i],playerStreak[i]);
7793 else fprintf(file,"%s^t0^t%s^t%i^t0^t0^t%i^t%i^t%i",playerAuthid[i],playerName[i],systime,playerWins[i],playerTotalPoints[i],playerStreak[i]);
7794
7795 fputc(file,'^n');
7796
7797 // so we can reference this for the MOTD
7798 pointsExtraction[players[i]][0] = playerWins[i];
7799 pointsExtraction[players[i]][1] = playerPoints[i];
7800 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7801 pointsExtraction[players[i]][3] = playerStreak[i];
7802 pointsExtraction[players[i]][4] = 0; // show old record
7803 }
7804 }
7805
7806 if(tempFile) fclose(tempFile);
7807 if(file) fclose(file);
7808
7809 // remove our copy
7810 delete_file(tempFileName);
7811#endif
7812
7813 new teamName[10], key_GAINED_POINTS[18];
7814
7815 // decide what to use based on split and such
7816 if(get_pcvar_num(gg_stats_split))
7817 {
7818 if(teamplay) key_GAINED_POINTS = "GAINED_POINTS_TP";
7819 else key_GAINED_POINTS = "GAINED_POINTS_REG";
7820 }
7821 else key_GAINED_POINTS = "GAINED_POINTS";
7822
7823 for(i=0;i<playerNum;i++)
7824 {
7825 get_user_team(players[i],teamName,9);
7826
7827 //if(players[i] == winner || (teamplay && _:cs_get_user_team(players[i]) == winningTeam))
7828 if(playerWins[i]) log_message("^"%s<%i><%s><%s>^" triggered ^"Won_GunGame^"",playerName[i],get_user_userid(players[i]),playerAuthid[i],teamName);
7829
7830 if(stats_mode == 2 && playerPoints[i])
7831 {
7832 log_message("^"%s<%i><%s><%s>^" triggered ^"GunGame_Points^" amount ^"%i^"",playerName[i],get_user_userid(players[i]),playerAuthid[i],teamName,playerPoints[i]);
7833 gungame_print(players[i],0,1,"%L",players[i],key_GAINED_POINTS,playerPoints[i],playerTotalPoints[i],pointsExtraction[players[i]][0]);
7834 }
7835 }
7836}
7837
7838stock stats_clear_struct(struct[statsData],clearAuthid=1)
7839{
7840 if(clearAuthid) struct[sdAuthid][0] = 0;
7841 struct[sdWins][0] = 0;
7842 struct[sdName][0] = 0;
7843 struct[sdTimestamp] = 0;
7844 struct[sdPoints][0] = 0;
7845 struct[sdStreak][0] = 0;
7846 struct[sdWins][1] = 0;
7847 struct[sdPoints][1] = 0;
7848 struct[sdStreak][1] = 0;
7849}
7850
7851#if defined SQL
7852// sql version] get a player's last used name and wins from save file
7853stock stats_get_data(authid[],struct[statsData],id=0)
7854{
7855 if(!sqlInit || !get_pcvar_num(gg_stats_mode))
7856 {
7857 stats_clear_struct(struct);
7858 return 0;
7859 }
7860
7861 if(id > 0 && playerStats[id][sdAuthid][0]) // already saved my stats
7862 {
7863 struct = playerStats[id];
7864 return 1;
7865 }
7866
7867 stats_clear_struct(struct);
7868
7869 SQL_QuoteString(db,safeName,63,authid);
7870 query = SQL_PrepareQuery(db,"SELECT wins,name,points,streak,wins_tp,points_tp,streak_tp FROM `%s` WHERE authid='%s' AND serverip='%s' LIMIT 1;",sqlTable,safeName,serverip);
7871
7872 if(!SQL_ExecuteAndLog(query))
7873 {
7874 SQL_FreeHandle(query);
7875 return 0;
7876 }
7877
7878 copy(struct[sdAuthid],31,authid); // setting authid will cache it if this is playerStats
7879
7880 new found = SQL_NumResults(query);
7881 if(found)
7882 {
7883 struct[sdWins][0] = SQL_ReadResult(query,0);
7884 SQL_ReadResult(query,1,struct[sdName],31);
7885 struct[sdPoints][0] = SQL_ReadResult(query,2);
7886 struct[sdStreak][0] = SQL_ReadResult(query,3);
7887 struct[sdWins][1] = SQL_ReadResult(query,4);
7888 struct[sdPoints][1] = SQL_ReadResult(query,5);
7889 struct[sdStreak][1] = SQL_ReadResult(query,6);
7890 }
7891
7892 if(id > 0) playerStats[id] = struct; // if this is a connected player, save it
7893
7894 SQL_FreeHandle(query);
7895 return found;
7896}
7897#else
7898// get a player's last used name and wins from save file
7899stock stats_get_data(authid[],struct[statsData],id=0)
7900{
7901 if(!get_pcvar_num(gg_stats_mode))
7902 {
7903 stats_clear_struct(struct);
7904 return 0;
7905 }
7906
7907 get_pcvar_string(gg_stats_file,sfFile,63);
7908 if(!file_exists(sfFile))
7909 {
7910 stats_clear_struct(struct);
7911 return 0;
7912 }
7913
7914 if(id > 0)
7915 {
7916 if(playerStats[id][sdAuthid][0]) // already saved my stats
7917 {
7918 struct = playerStats[id];
7919 return 1;
7920 }
7921 else if(statsPosition[id][0] > 0) // check top regular players
7922 {
7923 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[0],statsPosition[id][0]-1),playerStats[id]);
7924 struct = playerStats[id];
7925 return 1;
7926 }
7927 else if(statsPosition[id][1] > 0) // check top teamplay players
7928 {
7929 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[1],statsPosition[id][1]-1),playerStats[id]);
7930 struct = playerStats[id];
7931 return 1;
7932 }
7933 }
7934
7935 stats_clear_struct(struct);
7936
7937 // now we have to go through the file
7938
7939 // open 'er up, boys!
7940 new found, file = fopen(sfFile,"rt");
7941 if(!file) return 0;
7942
7943 // go through it
7944 while(!feof(file))
7945 {
7946 fgets(file,sfLineData,111);
7947 strtok(sfLineData,sfAuthid,31,dummy,1,'^t'); // isolate authid
7948
7949 // this is it, stop now because our data is already stored in sfLineData
7950 if(equal(authid,sfAuthid))
7951 {
7952 found = 1;
7953 break;
7954 }
7955 }
7956
7957 // close 'er up, boys! (hmm....)
7958 fclose(file);
7959
7960 copy(struct[sdAuthid],31,authid); // setting authid will cache it if this is playerStats
7961
7962 // couldn't find
7963 if(found)
7964 {
7965 stats_str_to_struct(sfLineData,struct); // convert line from file to a struct
7966 }
7967
7968 if(id > 0) playerStats[id] = struct; // if this is a connected player, save it
7969
7970 return found;
7971}
7972#endif
7973
7974#if defined SQL
7975public stats_get_top_players()
7976{
7977 if(!ggActive || !sqlInit) return 0;
7978
7979 lastStatsMode = get_pcvar_num(gg_stats_mode);
7980 if(!lastStatsMode) return 0;
7981
7982 // assign stat position to players already in the game
7983 new i, authid[32], stats_ip = get_pcvar_num(gg_stats_ip);
7984 for(i=1;i<=maxPlayers;i++)
7985 {
7986 if(is_user_connected(i))
7987 {
7988 get_gg_authid(i,authid,31,stats_ip);
7989 if(!statsPosition[i][0]) stats_get_position(i,authid,0);
7990 if(!statsPosition[i][1]) stats_get_position(i,authid,1);
7991 }
7992 }
7993
7994 return 1;
7995}
7996#else
7997public stats_get_top_players()
7998{
7999 if(!ggActive || !sqlInit) return 0;
8000
8001 lastStatsMode = get_pcvar_num(gg_stats_mode);
8002 if(!lastStatsMode) return 0;
8003
8004 // we never made the array
8005 if(!statsArray)
8006 {
8007 get_pcvar_string(gg_stats_file,sfFile,63);
8008 if(!file_exists(sfFile)) return 0;
8009
8010 statsArray = ArrayCreate(statsData,100);
8011 statsPointers[0] = ArrayCreate(1,100);
8012 statsPointers[1] = ArrayCreate(1,100);
8013
8014 new file = fopen(sfFile,"rt");
8015 if(file)
8016 {
8017 new entryNum;
8018
8019 while(!feof(file))
8020 {
8021 fgets(file,sfLineData,111);
8022 if(!sfLineData[0]) continue;
8023 stats_str_to_struct(sfLineData,sfStatsStruct);
8024
8025 // put all of our stats entries, with all of the info and stuff, into our big stats array
8026 ArrayPushArray(statsArray,sfStatsStruct);
8027
8028 // and then put just their indexes into our "pointer" cell arrays
8029 if(sfStatsStruct[sdWins][0] || sfStatsStruct[sdPoints][0])
8030 {
8031 ArrayPushCell(statsPointers[0],entryNum);
8032 statsSize[0]++;
8033 }
8034 if(sfStatsStruct[sdWins][1] || sfStatsStruct[sdPoints][1])
8035 {
8036 ArrayPushCell(statsPointers[1],entryNum);
8037 statsSize[1]++;
8038 }
8039
8040 entryNum++;
8041 }
8042 fclose(file);
8043
8044 ArraySort(statsPointers[0],"stats_pointer_sort_reg");
8045 ArraySort(statsPointers[1],"stats_pointer_sort_tp");
8046
8047 if(statsSize[0] > MAX_STATS_RANK) statsSize[0] = MAX_STATS_RANK;
8048 if(statsSize[1] > MAX_STATS_RANK) statsSize[1] = MAX_STATS_RANK;
8049 }
8050 }
8051
8052 // if we were able to get anything put together
8053 if(statsSize[0] || statsSize[1])
8054 {
8055 // assign stat position to players already in the game
8056 new i, authid[32], stats_ip = get_pcvar_num(gg_stats_ip);
8057 for(i=1;i<=maxPlayers;i++)
8058 {
8059 if(is_user_connected(i))
8060 {
8061 get_gg_authid(i,authid,31,stats_ip);
8062 if(!statsPosition[i][0]) stats_get_position(i,authid,0); // these only work if statsSize[si] > 0
8063 if(!statsPosition[i][1]) stats_get_position(i,authid,1);
8064 }
8065 }
8066 }
8067
8068 return 1;
8069}
8070
8071// seems to be slightly faster without passing in extra data, and this is how we used to do it, so we'll continue to use two functions to sort
8072public stats_pointer_sort_reg(Array:array,item1,item2)
8073{
8074 static score[2];
8075
8076 if(lastStatsMode == 1) // sort by wins
8077 {
8078 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8079 score[0] = sfStatsStruct[sdWins][0];
8080
8081 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8082 score[1] = sfStatsStruct[sdWins][0];
8083 }
8084 else // sort by points
8085 {
8086 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8087 score[0] = sfStatsStruct[sdPoints][0];
8088
8089 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8090 score[1] = sfStatsStruct[sdPoints][0];
8091 }
8092
8093 return score[1] - score[0];
8094}
8095
8096// seems to be slightly faster without passing in extra data, and this is how we used to do it, so we'll continue to use two functions to sort
8097public stats_pointer_sort_tp(Array:array,item1,item2)
8098{
8099 static score[2];
8100
8101 if(lastStatsMode == 1) // sort by wins
8102 {
8103 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8104 score[0] = sfStatsStruct[sdWins][1];
8105
8106 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8107 score[1] = sfStatsStruct[sdWins][1];
8108 }
8109 else // sort by points
8110 {
8111 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8112 score[0] = sfStatsStruct[sdPoints][1];
8113
8114 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8115 score[1] = sfStatsStruct[sdPoints][1];
8116 }
8117
8118 return score[1] - score[0];
8119}
8120#endif
8121
8122// gather up our players
8123/*public stats_get_top_players()
8124{
8125 if(!ggActive) return 0;
8126
8127 glStatsMode = get_pcvar_num(gg_stats_mode);
8128 if(!glStatsMode) return 0;
8129
8130 get_pcvar_string(gg_stats_file,sfFile,63);
8131 if(!file_exists(sfFile)) return 0;
8132
8133 new buildMe[2], stats_split = get_pcvar_num(gg_stats_split);
8134
8135 // figure out if we need to build any of the arrays still
8136 if(!statsArray[0])
8137 {
8138 statsArray[0] = ArrayCreate(statsData,100);
8139 statsSize[0] = 0;
8140 buildMe[0] = 1;
8141 }
8142 if(!statsArray[1] && stats_split)
8143 {
8144 statsArray[1] = ArrayCreate(statsData,100);
8145 statsSize[1] = 0;
8146 buildMe[1] = 1;
8147 }
8148
8149 // if we do have an array or two to build
8150 if(buildMe[0] || buildMe[1])
8151 {
8152 // storage format:
8153 // AUTHID WINS LAST_USED_NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
8154
8155 new file = fopen(sfFile,"rt");
8156 if(file)
8157 {
8158 while(!feof(file))
8159 {
8160 fgets(file,sfLineData,111);
8161 if(!sfLineData[0]) continue;
8162 stats_str_to_struct(sfLineData,sfStatsStruct);
8163
8164 // if we are working on this array, and we have some wins or points for it
8165 if(buildMe[0] && (sfStatsStruct[sdWins][0] || sfStatsStruct[sdPoints][0]))
8166 {
8167 ArrayPushArray(statsArray[0],sfStatsStruct);
8168 statsSize[0]++;
8169 }
8170 if(buildMe[1] && (sfStatsStruct[sdWins][1] || sfStatsStruct[sdPoints][1]))
8171 {
8172 ArrayPushArray(statsArray[1],sfStatsStruct);
8173 statsSize[1]++;
8174 }
8175 }
8176 fclose(file);
8177
8178 if(buildMe[0] && statsSize[0] > 1) // if we were working on this array and we have something to sort
8179 {
8180 ArraySort(statsArray[0],"stats_custom_compare_regular");
8181 if(statsSize[0] > 1000) statsSize[0] = 1000; // arbitrarily limit because we have to look through this every time someone connects
8182 }
8183 if(buildMe[1] && statsSize[1] > 1)
8184 {
8185 ArraySort(statsArray[1],"stats_custom_compare_teamplay");
8186 if(statsSize[1] > 1000) statsSize[1] = 1000;
8187 }
8188 }
8189
8190 // clear any saved stats positions out of the tempsaves, so no one rejoins with the wrong position
8191 for(new i=0;i<TEMP_SAVES;i++)
8192 {
8193 tempSave[i][34] = 0;
8194 tempSave[i][35] = 0;
8195 }
8196 }
8197
8198 // if we were able to get anything put together
8199 if(statsSize[0] || statsSize[1])
8200 {
8201 // assign stat position to players already in the game
8202 new i, authid[32], stats_ip = get_pcvar_num(gg_stats_ip);
8203 for(i=1;i<=maxPlayers;i++)
8204 {
8205 if(is_user_connected(i))
8206 {
8207 get_gg_authid(i,authid,31,stats_ip);
8208 if(!statsPosition[i][0]) stats_get_position(i,authid,0); // these will only do anything if we created the respective array above
8209 if(!statsPosition[i][1]) stats_get_position(i,authid,1);
8210 }
8211 }
8212 }
8213
8214 lastStatsMode = glStatsMode;
8215 statsLastSplit = stats_split;
8216
8217 return 1;
8218}*/
8219
8220// our custom sorting functions
8221/*public stats_custom_compare_regular(Array:array,item1,item2)
8222{
8223 static score[2];
8224
8225 ArrayGetArray(array,item1,sfStatsStruct);
8226 if(glStatsMode == 1) score[0] = sfStatsStruct[sdWins][0]; // sort by wins
8227 else score[0] = sfStatsStruct[sdPoints][0]; // sort by wins
8228
8229 ArrayGetArray(array,item2,sfStatsStruct);
8230 if(glStatsMode == 1) score[1] = sfStatsStruct[sdWins][0]; // sort by wins
8231 else score[1] = sfStatsStruct[sdPoints][0]; // sort by wins
8232
8233 return score[1] - score[0];
8234}*/
8235
8236/*public stats_custom_compare_teamplay(Array:array,item1,item2)
8237{
8238 static score[2];
8239
8240 ArrayGetArray(array,item1,sfStatsStruct);
8241 if(glStatsMode == 1) score[0] = sfStatsStruct[sdWins][1]; // sort by wins
8242 else score[0] = sfStatsStruct[sdPoints][1]; // sort by wins
8243
8244 ArrayGetArray(array,item2,sfStatsStruct);
8245 if(glStatsMode == 1) score[1] = sfStatsStruct[sdWins][1]; // sort by wins
8246 else score[1] = sfStatsStruct[sdPoints][1]; // sort by wins
8247
8248 return score[1] - score[0];
8249}*/
8250
8251#if defined SQL
8252// get a player's overall position
8253stats_get_position(id,authid[],si)
8254{
8255 if(!sqlInit) return 0; // can't get position yet
8256
8257 statsPosition[id][si] = -1; // mark that we've at least attempted to find it
8258
8259 new stats_mode = get_pcvar_num(gg_stats_mode), myPoints;
8260
8261 // first, we need to get their current amount of wins/points. this allows us to make sure that they are really in the database
8262 // (could cause problems with a sub-query returning 0 instead of NULL) and that they actually have wins/points for the current
8263 // stats mode (could cause problems when they are in the database but you are looking at their stats index for which they have
8264 // no points). then, we might as well use the result for our ranking query instead of using a sub-query.
8265
8266 // if we don't have the cached stats, we will have to do a query to get the wins/points. since we would be doing a query for it
8267 // anyway, we might as well just get all of the stats at once.
8268 stats_get_data(authid,playerStats[id],id); // stats_get_data should automatically manage cache
8269
8270 if(stats_mode == 2) myPoints = playerStats[id][sdPoints][si];
8271 else myPoints = playerStats[id][sdWins][si];
8272
8273 if(myPoints <= 0 && (stats_mode != 2 || playerStats[id][sdWins][si] <= 0)) return -1;
8274
8275 new winsColumn[8], pointsColumn[10];
8276
8277 if(si == 0)
8278 {
8279 winsColumn = "wins";
8280 pointsColumn = "points";
8281 }
8282 else
8283 {
8284 winsColumn = "wins_tp";
8285 pointsColumn = "points_tp";
8286 }
8287
8288 // select the number of people who have a score higher than I do, plus one. query from Giuseppe Maxia
8289 if(stats_mode == 2)
8290 {
8291 query = SQL_PrepareQuery(db,"SELECT COUNT(*)+1 AS ranking FROM `%s` WHERE %s > %i AND serverip='%s' AND (%s > 0 OR %s > 0);",
8292 sqlTable,pointsColumn,myPoints,serverip,winsColumn,pointsColumn);
8293 }
8294 else
8295 {
8296 query = SQL_PrepareQuery(db,"SELECT COUNT(*)+1 AS ranking FROM `%s` WHERE %s > %i AND serverip='%s';",
8297 sqlTable,winsColumn,myPoints,serverip);
8298 }
8299
8300 if(SQL_ExecuteAndLog(query))
8301 {
8302 new result = SQL_ReadResult(query,0);
8303 SQL_FreeHandle(query);
8304
8305 statsPosition[id][si] = result;
8306 return result;
8307 }
8308
8309 SQL_FreeHandle(query);
8310 return -1;
8311}
8312#else
8313// get a player's overall position
8314stats_get_position(id,authid[],si)
8315{
8316 if(statsArray && statsPointers[si] && statsSize[si])
8317 {
8318 statsPosition[id][si] = -1; // mark that we've at least attempted to find it
8319
8320 for(new i=0;i<statsSize[si];i++)
8321 {
8322 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[si],i),sfStatsStruct);
8323 if(equal(authid,sfStatsStruct[sdAuthid]))
8324 {
8325 statsPosition[id][si] = i+1;
8326 return i+1;
8327 }
8328 }
8329
8330 return -1;
8331 }
8332
8333 return 0;
8334}
8335#endif
8336
8337#if defined SQL
8338// prune old entries
8339stock stats_prune(max_time=-1)
8340{
8341 if(!sqlInit) return 0;
8342
8343 if(max_time == -1) max_time = get_pcvar_num(gg_stats_prune); // -1 = use value from cvar
8344 if(max_time == 0) return 0; // 0 = no pruning
8345
8346 // prune old entries
8347 formatex(mkQuery,255,"DELETE FROM `%s` WHERE timestamp < %i AND serverip='%s';",sqlTable,get_systime() - max_time,serverip);
8348 SQL_ThreadQuery(tuple,"sql_qhandler_prune",mkQuery);
8349
8350 // fix up all of our tables
8351 formatex(mkQuery,255,"REPAIR TABLE `%s`;",sqlTable);
8352 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8353
8354 formatex(mkQuery,255,"OPTIMIZE TABLE `%s`;",sqlTable);
8355 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8356
8357 formatex(mkQuery,255,"REPAIR TABLE `%s`;",sqlStreakTable);
8358 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8359
8360 formatex(mkQuery,255,"OPTIMIZE TABLE `%s`;",sqlStreakTable);
8361 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8362
8363 return -1;
8364}
8365#else
8366// prune old entries
8367stock stats_prune(max_time=-1)
8368{
8369 get_pcvar_string(gg_stats_file,sfFile,63);
8370 if(!file_exists(sfFile)) return 0;
8371
8372 if(max_time == -1) max_time = get_pcvar_num(gg_stats_prune); // -1 = use value from cvar
8373 if(max_time == 0) return 0; // 0 = no pruning
8374
8375 new tempFileName[33];
8376 formatex(tempFileName,32,"%s2",sfFile); // our temp file, append 2
8377
8378 // copy over current stat file
8379 rename_file(sfFile,tempFileName,1); // relative
8380
8381 // rename failed?
8382 if(!file_exists(tempFileName)) return 0;
8383
8384 new tempFile = fopen(tempFileName,"rt");
8385 new file = fopen(sfFile,"wt");
8386
8387 // go through our old copy and rewrite valid entries into the new copy
8388 new systime = get_systime(), original[112], removed;
8389 while(tempFile && file && !feof(tempFile))
8390 {
8391 fgets(tempFile,sfLineData,111);
8392 if(!sfLineData[0]) continue;
8393
8394 // save original
8395 original = sfLineData;
8396
8397 // break off authid
8398 strtok(sfLineData,dummy,1,sfLineData,111,'^t');
8399
8400 // break off wins
8401 strtok(sfLineData,dummy,1,sfLineData,111,'^t');
8402
8403 // break off name, and thus get timestamp
8404 strtok(sfLineData,dummy,1,sfTimestamp,11,'^t');
8405 copyc(sfTimestamp,11,sfTimestamp,'^t'); // cut off points
8406
8407 // not too old, write it to our new file
8408 if(systime - str_to_num(sfTimestamp) <= max_time)
8409 fprintf(file,"%s",original); // newline is already included
8410 else
8411 removed++;
8412 }
8413
8414 if(tempFile) fclose(tempFile);
8415 if(file) fclose(file);
8416
8417 // remove our copy
8418 delete_file(tempFileName);
8419 return removed;
8420}
8421#endif
8422
8423//**********************************************************************
8424// SQL FUNCTIONS
8425//**********************************************************************
8426
8427#if defined SQL
8428
8429// opens the database
8430stock sql_init(creation_queries=1)
8431{
8432 new host[128], user[128], pass[128], dbname[128];
8433 get_pcvar_string(gg_sql_host,host,127);
8434 get_pcvar_string(gg_sql_user,user,127);
8435 get_pcvar_string(gg_sql_pass,pass,127);
8436 get_pcvar_string(gg_sql_db,dbname,127);
8437
8438 new sqlErrorCode, sqlError[1024];
8439
8440 // free old stuff if reconnecting
8441 if(tuple != Empty_Handle) SQL_FreeHandleAndClear(tuple);
8442 if(db != Empty_Handle) SQL_FreeHandleAndClear(db);
8443
8444 tuple = SQL_MakeDbTuple(host,user,pass,dbname);
8445
8446 if(tuple == Empty_Handle)
8447 {
8448 log_amx("Could not create database tuple. Error #%i: %s",sqlErrorCode,sqlError);
8449 return 0;
8450 }
8451
8452 db = SQL_Connect(tuple,sqlErrorCode,sqlError,1023);
8453
8454 if(db == Empty_Handle)
8455 {
8456 log_amx("Could not connect to database. Error #%i: %s",sqlErrorCode,sqlError);
8457 return 0;
8458 }
8459
8460 if(creation_queries)
8461 {
8462 // set up the main table, unfortunately we have to split it up to avoid "input line too long" compile errors
8463 get_pcvar_string(gg_sql_table,sqlTable,127);
8464
8465 new super_long_query[716],
8466 len = formatex(super_long_query,715,"CREATE TABLE IF NOT EXISTS `%s`(`authid` VARCHAR(31) NOT NULL,`wins` SMALLINT(6) default '0',`name` VARCHAR(31) NOT NULL,`timestamp` INT(10) UNSIGNED default '0',`points` MEDIUMINT(9) default '0',`streak` SMALLINT(6) default '0',`wins_tp` SMALLINT(6) default '0',`points_tp` MEDIUMINT(9) default '0',",sqlTable);
8467 formatex(super_long_query[len],715-len,"`streak_tp` SMALLINT(6) default '0',`serverip` VARCHAR(63) NOT NULL,PRIMARY KEY(`authid`,`serverip`),INDEX(`wins`),INDEX(`points`),INDEX(`wins_tp`), INDEX(`points_tp`));");
8468
8469 query = SQL_PrepareQuery(db,super_long_query);
8470 if(!SQL_ExecuteAndLog(query))
8471 {
8472 SQL_FreeHandle(query);
8473 return 0;
8474 }
8475
8476 // set up the streaks table
8477 get_pcvar_string(gg_sql_streak_table,sqlStreakTable,127);
8478
8479 query = SQL_PrepareQuery(db,"CREATE TABLE IF NOT EXISTS `%s`(`type` ENUM('0C','0R','1C','1R') default NULL,`authid` VARCHAR(31) NOT NULL,`streak` SMALLINT(6) default '0',`name` VARCHAR(31) NOT NULL,`timestamp` INT(10) UNSIGNED default '0',`serverip` VARCHAR(63) NOT NULL,INDEX(`authid`,`serverip`),INDEX(`type`));",sqlStreakTable);
8480 if(!SQL_ExecuteAndLog(query))
8481 {
8482 SQL_FreeHandle(query);
8483 return 0;
8484 }
8485
8486 // set up the players table
8487 get_pcvar_string(gg_sql_winmotd_table,sqlPlayersTable,127);
8488
8489 len = formatex(super_long_query,715,"CREATE TABLE IF NOT EXISTS `%s`(`id` tinyint(4) NOT NULL default '0',`authid` varchar(31) NOT NULL,`name` varchar(31) NOT NULL,`team` tinyint(4) default '0',`level` tinyint(4) default '0',`weapon` varchar(23) NOT NULL,`flags` tinyint(4) default '0',`wins` smallint(6) default '0',`points` mediumint(9) default '0',`new_points` mediumint(9) default '0',`record_streak` smallint(6) default '0',",sqlPlayersTable);
8490 formatex(super_long_query[len],715-len,"`current_streak` smallint(6) default '0',`stats_position` smallint(6) unsigned default '0',`teamtime_winning` smallint(6) unsigned default '0',`teamtime_losing` smallint(6) unsigned default '0',`timestamp` int(10) unsigned default '0',`serverip` varchar(63) NOT NULL, PRIMARY KEY(`id`));");
8491
8492 query = SQL_PrepareQuery(db,super_long_query);
8493 if(!SQL_ExecuteAndLog(query))
8494 {
8495 SQL_FreeHandle(query);
8496 return 0;
8497 }
8498
8499 SQL_FreeHandle(query);
8500 }
8501
8502 sqlInit = 1;
8503 return 1;
8504}
8505
8506// closes the database
8507public sql_uninit()
8508{
8509 if(sqlInit)
8510 {
8511 if(db != Empty_Handle) SQL_FreeHandleAndClear(db);
8512 if(tuple != Empty_Handle) SQL_FreeHandleAndClear(tuple);
8513 }
8514}
8515
8516// frees a handle and resets its variable to empty
8517SQL_FreeHandleAndClear(&Handle:arg)
8518{
8519 SQL_FreeHandle(arg);
8520 arg = Empty_Handle;
8521}
8522
8523// execute a query and log any errors that come up
8524stock SQL_ExecuteAndLog(&Handle:queryHandle,const queryStr[]="")
8525{
8526 static preventLoopback = 0;
8527
8528 if(!SQL_Execute(queryHandle))
8529 {
8530 static error[256];
8531 new errnum = SQL_QueryError(queryHandle,error,255);
8532
8533 if(queryStr[0]) log_amx("Could not execute query [%s] -- err #%i [%s]",queryStr,errnum,error);
8534 else
8535 {
8536 SQL_GetQueryString(queryHandle,mkQuery,255);
8537 log_amx("Could not execute query [%s] -- err #%i [%s]",mkQuery,errnum,error);
8538 }
8539
8540 // fix thanks to 2inspyr
8541 if(errnum == 2006 && !preventLoopback) // MySQL server has gone away
8542 {
8543 log_amx("Attempting to reconnect to server");
8544
8545 if(sql_init(0)) // no creation queries
8546 {
8547 log_amx("Successfully reconnected to server, executing query again");
8548
8549 SQL_GetQueryString(queryHandle,mkQuery,1535);
8550 new Handle:newQuery = SQL_PrepareQuery(db,mkQuery);
8551
8552 preventLoopback = 1;
8553 SQL_ExecuteAndLog(newQuery);
8554
8555 // transfer handles
8556 SQL_FreeHandle(queryHandle);
8557 queryHandle = newQuery;
8558
8559 preventLoopback = 0;
8560 return 1;
8561
8562 //return SQL_Execute(queryHandle);
8563 }
8564 else
8565 {
8566 log_amx("Could not reconnect to server");
8567
8568 preventLoopback = 0;
8569 return 0;
8570 }
8571 }
8572
8573 preventLoopback = 0;
8574 return 0;
8575 }
8576
8577 preventLoopback = 0;
8578 return 1;
8579}
8580
8581public sql_qhandler_prune(failstate,Handle:query,error[],errnum,data[],size,Float:queuetime)
8582{
8583 if(error[0])
8584 {
8585 SQL_GetQueryString(query,mkQuery,255);
8586 log_amx("Could not execute query [%s] -- err #%i [%s]",mkQuery,errnum,error);
8587 }
8588 else log_amx("%L",LANG_SERVER,"PRUNING",sqlTable,SQL_NumRows(query));
8589}
8590
8591public sql_qhandler_dummy(failstate,Handle:query,error[],errnum,data[],size,Float:queuetime)
8592{
8593 if(error[0])
8594 {
8595 SQL_GetQueryString(query,mkQuery,255);
8596 log_amx("Could not execute query [%s] -- err #%i [%s]",mkQuery,errnum,error);
8597 }
8598}
8599
8600#endif
8601
8602//**********************************************************************
8603// WHATEV
8604//**********************************************************************
8605
8606#if !defined SQL
8607// monitor stats mode and resort if it changed
8608public recheck_stats_sorting()
8609{
8610 new stats_mode = get_pcvar_num(gg_stats_mode);
8611
8612 if(stats_mode && stats_mode != lastStatsMode && lastStatsMode != -909) // set to -909 on init
8613 {
8614 lastStatsMode = stats_mode;
8615
8616 // we are judging by a different criteria, so we need to resort
8617 if(statsPointers[0] && statsSize[0]) ArraySort(statsPointers[0],"stats_pointer_sort_reg");
8618 if(statsPointers[1] && statsSize[1]) ArraySort(statsPointers[1],"stats_pointer_sort_tp");
8619
8620 // clear any saved stats positions out of the tempsaves, so no one rejoins with the wrong position
8621 for(new i=0;i<TEMP_SAVES;i++)
8622 {
8623 tempSave[i][svStatsPosition][0] = 0;
8624 tempSave[i][svStatsPosition][1] = 0;
8625 }
8626
8627 // also make sure that everyone will get assigned new positions
8628 for(new i=1;i<=maxPlayers;i++)
8629 {
8630 statsPosition[i][0] = 0;
8631 statsPosition[i][1] = 0;
8632 }
8633
8634 stats_get_top_players();
8635 }
8636
8637 lastStatsMode = stats_mode;
8638}
8639#endif
8640
8641// task is set on a potential team change, and removed on an
8642// approved team change, so if we reach it, deduct level
8643public delayed_suicide(taskid)
8644{
8645 new id = taskid-TASK_DELAYED_SUICIDE;
8646 if(is_user_connected(id)) player_suicided(id);
8647}
8648
8649// remove those annoying pesky pistols, if they haven't been already
8650strip_starting_pistols(id)
8651{
8652 if(!is_user_alive(id)) return;
8653
8654 switch(cs_get_user_team(id))
8655 {
8656 case CS_TEAM_T:
8657 {
8658 if(!equal(lvlWeapon[id],"glock18") && user_has_weapon(id,CSW_GLOCK18))
8659 ham_strip_weapon(id,WEAPON_GLOCK18);
8660 }
8661 case CS_TEAM_CT:
8662 {
8663 if(!equal(lvlWeapon[id],"usp") && user_has_weapon(id,CSW_USP))
8664 ham_strip_weapon(id,"weapon_usp");
8665 }
8666 }
8667}
8668
8669// weapon display
8670stock status_display(id,status=1)
8671{
8672 new sdisplay = get_pcvar_num(gg_status_display);
8673
8674 // display disabled
8675 if(!sdisplay) return;
8676
8677 // dead
8678 if(id && !is_user_alive(id)) return;
8679
8680 new dest;
8681 static sprite[32];
8682
8683 if(!id) dest = MSG_BROADCAST;
8684 else dest = MSG_ONE_UNRELIABLE;
8685
8686 // disable display if status is 0, or we are doing a warmup
8687 if(!status || warmup > 0)
8688 {
8689 // don't send to bots
8690 if(dest == MSG_BROADCAST || !is_user_bot(id))
8691 {
8692 message_begin(dest,gmsgScenario,_,id);
8693 write_byte(0);
8694 message_end();
8695 }
8696
8697 return;
8698 }
8699
8700 // weapon display
8701 if(sdisplay == STATUS_LEADERWPN || sdisplay == STATUS_YOURWPN)
8702 {
8703 if(sdisplay == STATUS_LEADERWPN)
8704 {
8705 new ldrLevel;
8706 get_leader(ldrLevel);
8707
8708 // get leader's weapon
8709 if(ldrLevel <= 0) return;
8710 copy(sprite,31,weaponName[ldrLevel-1]);
8711 }
8712 else
8713 {
8714 // get your weapon
8715 if(level[id] <= 0) return;
8716 copy(sprite,31,lvlWeapon[id]);
8717 }
8718
8719 strtolower(sprite);
8720
8721 // sprite is d_grenade, not d_hegrenade
8722 if(sprite[0] == 'h') sprite = "grenade";
8723
8724 // get true sprite name
8725 format(sprite,31,"d_%s",sprite);
8726 }
8727
8728 // kill display
8729 else if(sdisplay == STATUS_KILLSLEFT)
8730 {
8731 new goal = get_level_goal(level[id],id);
8732 formatex(sprite,31,"number_%i",goal-score[id]);
8733 }
8734 else if(sdisplay == STATUS_KILLSDONE)
8735 {
8736 formatex(sprite,31,"number_%i",score[id]);
8737 }
8738
8739 // don't send to bots
8740 if(!id || !is_user_bot(id))
8741 {
8742 message_begin(dest,gmsgScenario,_,id);
8743 write_byte(1);
8744 write_string(sprite);
8745 write_byte(150);
8746 message_end();
8747 }
8748}
8749
8750// hide someone's money display
8751public hide_money(id)
8752{
8753 // hide money
8754 emessage_begin(MSG_ONE,gmsgHideWeapon,_,id);
8755 ewrite_byte(1<<5);
8756 emessage_end();
8757
8758 // hide crosshair that appears from hiding money
8759 emessage_begin(MSG_ONE,gmsgCrosshair,_,id);
8760 ewrite_byte(0);
8761 emessage_end();
8762}
8763
8764//**********************************************************************
8765// SUPPORT FUNCTIONS
8766//**********************************************************************
8767
8768// gets a players info, intended for other plugins to callfunc
8769public get_player_info(id,&rf_level,&rf_score,rf_lvlWeapon[],len,&rf_spawnProtected,rf_statsPosition[2])
8770{
8771 rf_level = level[id];
8772 rf_score = score[id];
8773 copy(rf_lvlWeapon,len,lvlWeapon[id]);
8774 rf_spawnProtected = spawnProtected[id];
8775 rf_statsPosition = statsPosition[id];
8776 return 1;
8777}
8778
8779// analyzes the weapon order and saves it into our variables
8780public setup_weapon_order()
8781{
8782 new weaponOrder[(MAX_WEAPONS*16)+1], temp[27];
8783 get_pcvar_string(gg_weapon_order,weaponOrder,MAX_WEAPONS*16);
8784
8785 new Float:killsperlvl = get_pcvar_float(gg_kills_per_lvl), i, done, colon, goal[6];
8786
8787 // cut them apart
8788 for(i=0;i<MAX_WEAPONS;i++)
8789 {
8790 // out of stuff
8791 if(strlen(weaponOrder) <= 1)
8792 {
8793 i--; // for our count
8794 break;
8795 }
8796
8797 // we still have a comma, go up to it
8798 if(contain_char(weaponOrder,',') != -1)
8799 {
8800 strtok(weaponOrder,temp,26,weaponOrder,MAX_WEAPONS*16,',');
8801 trim(temp);
8802 strtolower(temp);
8803 }
8804
8805 // otherwise, finish up
8806 else
8807 {
8808 copy(temp,26,weaponOrder);
8809 trim(temp);
8810 strtolower(temp);
8811 done = 1; // flag for end of loop
8812 }
8813
8814 colon = contain_char(temp,':');
8815
8816 // no custom requirement, easy
8817 if(colon == -1)
8818 {
8819 copy(weaponName[i],23,temp);
8820 if(equal(temp,KNIFE) || equal(temp,HEGRENADE)) weaponGoal[i] = (killsperlvl > 1.0) ? 1.0 : killsperlvl;
8821 else weaponGoal[i] = killsperlvl;
8822 }
8823 else
8824 {
8825 copyc(weaponName[i],23,temp,':');
8826 copy(goal,5,temp[colon+1]);
8827 weaponGoal[i] = floatstr(goal);
8828 }
8829
8830 if(done) break;
8831 }
8832
8833 // we break to end our loop, so "i" will be where we left it. but it's 0-based.
8834 weaponNum = i+1;
8835}
8836
8837// gets the goal for a level, taking into account default and custom values
8838stock get_level_goal(level,id=0)
8839{
8840 if(level < 1) return 1;
8841
8842 // no teamplay, return preset goal
8843 if(!is_user_connected(id) || !get_pcvar_num(gg_teamplay))
8844 {
8845 if(is_user_bot(id)) return floatround(weaponGoal[level-1]*get_pcvar_float(gg_kills_botmod),floatround_ceil);
8846 return floatround(weaponGoal[level-1],floatround_ceil);
8847 }
8848
8849 // one of this for every player on team
8850 new Float:result = weaponGoal[level-1] * float(team_player_count(cs_get_user_team(id)));
8851
8852 // modifiers for nade and knife levels
8853 if(equal(weaponName[level-1],HEGRENADE)) result *= get_pcvar_float(gg_teamplay_nade_mod);
8854 else if(equal(weaponName[level-1],KNIFE)) result *= get_pcvar_float(gg_teamplay_knife_mod);
8855
8856 if(result <= 0.0) result = 1.0;
8857 return floatround(result,floatround_ceil);
8858}
8859
8860// gets the level a player should use for his level
8861stock get_level_weapon(theLevel,var[],varLen)
8862{
8863 if(warmup > 0 && warmupWeapon[0]) copy(var,varLen,warmupWeapon);
8864 else if(theLevel > 0) copy(var,varLen,weaponName[theLevel-1]);
8865 else var[0] = 0;
8866}
8867
8868// easy function to precache sound via cvar
8869stock precache_sound_by_cvar(pcvar)
8870{
8871 new value[64];
8872 get_pcvar_string(pcvar,value,63);
8873 precache_sound_special(value);
8874}
8875
8876// precache sounds with a little bit of magic
8877stock precache_sound_special(sound[])
8878{
8879 if(!sound[0]) return;
8880
8881 if(containi(sound,".mp3") != -1) precache_generic(sound);
8882 else
8883 {
8884 // stop at ( character to allow precaching sounds that use speak's special functions, eg sound/ambience/xtal_down1(e70)
8885 new value[64], len = copyc(value,63,sound,'(');
8886
8887 // make sure we have a suffix for precaching
8888 if(containi(value,".wav") == -1) formatex(value[len],63-len,".wav");
8889
8890 // precache_sound doesn't take the "sound/" prefix
8891 if(equali(value,"sound/",6)) precache_sound(value[6]);
8892 else precache_sound(value);
8893 }
8894}
8895
8896// gets a player's "authid", or whatever token we want to identify them with.
8897// if you already know the value of gg_stats_ip, you can pass it in and save a cvar check.
8898stock get_gg_authid(id,ret[],len,stats_ip=-777)
8899{
8900 new mode = stats_ip;
8901 if(mode == -777) mode = get_pcvar_num(gg_stats_ip);
8902
8903 switch(mode)
8904 {
8905 case 0: return get_user_authid(id,ret,len); // 0 = by authid
8906 case -1, 2: return get_user_name(id,ret,len); // 2 = by name
8907 }
8908
8909 return get_user_ip(id,ret,len); // 1 = by ip
8910}
8911
8912// figure out which gungame.cfg (or gungame_teamplay.cfg) file to use
8913stock get_gg_config_file(teamplay=0,filename[],len)
8914{
8915 formatex(filename,len,"%s/gungame%s.cfg",cfgDir,(teamplay) ? "_teamplay" : "");
8916
8917 if(!file_exists(filename))
8918 {
8919 formatex(filename,len,"gungame%s.cfg",(teamplay) ? "_teamplay" : "");
8920 if(!file_exists(filename)) filename[0] = 0;
8921 }
8922}
8923
8924// executes what's inside of the config file
8925stock exec_gg_config_file(teamplay=0,allowToggling=0)
8926{
8927 new oldActive = ggActive, oldTeamplay = get_pcvar_num(gg_teamplay);
8928
8929 new cfgFile[64];
8930 get_gg_config_file(teamplay,cfgFile,63);
8931 if(cfgFile[0] && file_exists(cfgFile))
8932 {
8933 server_cmd("exec ^"%s^"",cfgFile);
8934 server_exec();
8935 }
8936
8937 // remember old values of gg_enabled and gg_teamplay if toggling is not allowed.
8938 // this is like we just turned teamplay on via command and we want to make sure
8939 // the configs get run appropriately, but obviously teamplay should still be on afterwards.
8940 if(!allowToggling)
8941 {
8942 set_pcvar_num(gg_enabled,oldActive);
8943 set_pcvar_num(gg_teamplay,oldTeamplay);
8944 }
8945
8946 // reselect random weapon order
8947 new lastOIstr[6], lastOI;
8948
8949 get_localinfo("gg_last_oi",lastOIstr,5);
8950 lastOI = str_to_num(lastOIstr);
8951
8952 // decrement it 1 b/c we probably already did do_rOrder
8953 // and don't want to end up skipping orders
8954 if(lastOI > 0)
8955 {
8956 num_to_str(lastOI-1,lastOIstr,5);
8957 set_localinfo("gg_last_oi",lastOIstr);
8958 }
8959
8960 do_rOrder(1); // thanks for pointing this out Tomek Kalkowski
8961
8962 // in case cvars changed. thanks BbIX!
8963 setup_weapon_order();
8964}
8965
8966// figure out which gungame_mapcycle file to use
8967stock get_gg_mapcycle_file(filename[],len)
8968{
8969 static testFile[64];
8970
8971 // cstrike/addons/amxmodx/configs/gungame_mapcycle.cfg
8972 formatex(testFile,63,"%s/gungame_mapcycle.cfg",cfgDir);
8973 if(file_exists(testFile))
8974 {
8975 copy(filename,len,testFile);
8976 return 1;
8977 }
8978
8979 // cstrike/addons/amxmodx/configs/gungame_mapcycle.txt
8980 formatex(testFile,63,"%s/gungame_mapcycle.txt",cfgDir);
8981 if(file_exists(testFile))
8982 {
8983 copy(filename,len,testFile);
8984 return 1;
8985 }
8986
8987 // cstrike/gungame_mapcycle.cfg
8988 testFile = "gungame_mapcycle.cfg";
8989 if(file_exists(testFile))
8990 {
8991 copy(filename,len,testFile);
8992 return 1;
8993 }
8994
8995 // cstrike/gungame_mapcycle.txt
8996 testFile = "gungame_mapcycle.txt";
8997 if(file_exists(testFile))
8998 {
8999 copy(filename,len,testFile);
9000 return 1;
9001 }
9002
9003 return 0;
9004}
9005
9006// another easy function to play sound via cvar
9007stock play_sound_by_cvar(id,pcvar)
9008{
9009 static value[64];
9010 get_pcvar_string(pcvar,value,63);
9011
9012 if(!value[0]) return;
9013
9014 if(containi(value,".mp3") != -1) client_cmd(id,"mp3 play ^"%s^"",value);
9015 else client_cmd(id,"spk ^"%s^"",value);
9016}
9017
9018// a taskable play_sound_by_cvar
9019public play_sound_by_cvar_task(params[2])
9020{
9021 play_sound_by_cvar(params[0],params[1]);
9022}
9023
9024// this functions take a filepath, but manages speak/mp3 play
9025stock play_sound(id,value[])
9026{
9027 if(!value[0]) return;
9028
9029 if(containi(value,".mp3") != -1) client_cmd(id,"mp3 play ^"%s^"",value);
9030 else
9031 {
9032 if(equali(value,"sound/",6)) client_cmd(id,"spk ^"%s^"",value[6]);
9033 else client_cmd(id,"spk ^"%s^"",value);
9034 }
9035}
9036
9037// find the highest level player and his level
9038stock get_leader(&retLevel=0,&retNumLeaders=0,&retRunnerUp=0)
9039{
9040 new player, leader, numLeaders, runnerUp;
9041
9042 // locate highest player
9043 for(player=1;player<=maxPlayers;player++)
9044 {
9045 if(!is_user_connected(player)) continue;
9046
9047 if(leader == 0 || level[player] > level[leader])
9048 {
9049 // about to dethrown leader, monitor runnerup
9050 if(leader && (runnerUp == 0 || level[leader] > level[runnerUp]))
9051 runnerUp = leader;
9052
9053 leader = player;
9054 numLeaders = 1; // reset tied count
9055 }
9056 else if(level[player] == level[leader])
9057 numLeaders++;
9058 else
9059 {
9060 // monitor runnerup
9061 if(runnerUp == 0 || level[player] > level[runnerUp])
9062 runnerUp = player;
9063 }
9064 }
9065
9066 retLevel = level[leader];
9067 retNumLeaders = numLeaders;
9068 retRunnerUp = runnerUp;
9069
9070 return leader;
9071}
9072
9073// gets the number of players on a particular level
9074stock num_players_on_level(checkLvl)
9075{
9076 new player, result;
9077 for(player=1;player<=maxPlayers;player++)
9078 {
9079 if(is_user_connected(player) && level[player] == checkLvl)
9080 result++;
9081 }
9082 return result;
9083}
9084
9085// a butchered version of teame06's CS Color Chat Function.
9086// actually it's now almost completely different, but that's
9087// where I started from.
9088gungame_print(id,custom,tag,msg[],any:...)
9089{
9090 new messages = get_pcvar_num(gg_messages);
9091 if(!messages || (messages & MSGS_HIDETEXT)) return 0;
9092
9093 new changeCount, num, i, j, argnum = numargs(), player, colored_messages = !(messages & MSGS_NOCOLOR);
9094 static newMsg[191], message[191], changed[8], players[32];
9095
9096 if(id)
9097 {
9098 players[0] = id;
9099 num = 1;
9100 }
9101 else get_players(players,num);
9102
9103 for(i=0;i<num;i++)
9104 {
9105 player = players[i];
9106 changeCount = 0;
9107
9108 if(!is_user_connected(player)) continue;
9109
9110 // we have to change LANG_PLAYER into
9111 // a player-specific argument, because
9112 // ML doesn't work well with SayText
9113 for(j=4;j<argnum;j++)
9114 {
9115 if(getarg(j) == LANG_PLAYER_C)
9116 {
9117 setarg(j,0,player);
9118 changed[changeCount++] = j;
9119 }
9120 }
9121
9122 // do user formatting
9123 vformat(newMsg,190,msg,5);
9124
9125 // and now we have to change what we changed
9126 // back into LANG_PLAYER, so that the next
9127 // player will be able to have it in his language
9128 for(j=0;j<changeCount;j++)
9129 {
9130 setarg(changed[j],0,LANG_PLAYER_C);
9131 }
9132
9133 // optimized color swapping
9134 if(colored_messages)
9135 {
9136 replace_all(newMsg,190,"%n","^x03"); // %n = team color
9137 replace_all(newMsg,190,"%g","^x04"); // %g = green
9138 replace_all(newMsg,190,"%e","^x01"); // %e = regular
9139 }
9140 else
9141 {
9142 replace_all(newMsg,190,"%n","");
9143 replace_all(newMsg,190,"%g","");
9144 replace_all(newMsg,190,"%e","");
9145 }
9146
9147 // now do our formatting (I used two variables because sharing one caused glitches)
9148
9149 if(tag)
9150 {
9151 if(colored_messages) formatex(message,190,"^x04[%L]^x01 %s",player,"GUNGAME",newMsg);
9152 else formatex(message,190,"^x01[%L] %s",player,"GUNGAME",newMsg);
9153 }
9154 else formatex(message,190,"^x01%s",newMsg);
9155
9156 message_begin(MSG_ONE,gmsgSayText,_,player);
9157 write_byte((custom > 0) ? custom : player);
9158 write_string(message);
9159 message_end();
9160 }
9161
9162 return 1;
9163}
9164
9165// show a HUD message to a user
9166gungame_hudmessage(id,Float:holdTime,msg[],any:...)
9167{
9168 new messages = get_pcvar_num(gg_messages);
9169 if(!messages || (messages & MSGS_HIDEHUD)) return 0;
9170
9171 // user formatting
9172 static newMsg[191];
9173 vformat(newMsg,190,msg,4);
9174
9175 // show it
9176 set_hudmessage(255,255,255,-1.0,0.8,0,6.0,holdTime,0.1,0.5);
9177 return ShowSyncHudMsg(id,hudSyncReqKills,newMsg);
9178}
9179
9180// start a map vote
9181stock start_mapvote()
9182{
9183 new dmmName[24], plugin;
9184
9185 // Galileo - galileo.amxx
9186 if(galileoID != -1)
9187 {
9188 log_amx("Starting a map vote from Galileo");
9189
9190 server_cmd("gal_startvote -nochange");
9191 }
9192
9193 // AMXX Nextmap Chooser - mapchooser.amxx
9194 else if((plugin = is_plugin_loaded("Nextmap Chooser")) != -1)
9195 {
9196 log_amx("Starting a map vote from Nextmap Chooser");
9197
9198 new oldWinLimit = get_cvar_num("mp_winlimit"), oldMaxRounds = get_cvar_num("mp_maxrounds");
9199 set_cvar_num("mp_winlimit",0); // skip winlimit check
9200 set_cvar_num("mp_maxrounds",-1); // trick plugin to think game is almost over
9201
9202 // call the vote
9203 if(callfunc_begin_i(get_func_id("voteNextmap",plugin),plugin) == 1)
9204 callfunc_end();
9205
9206 // set maxrounds back
9207 set_cvar_num("mp_winlimit",oldWinLimit);
9208 set_cvar_num("mp_maxrounds",oldMaxRounds);
9209 }
9210
9211 // Deagles' Map Management 2.30b - deagsmapmanage230b.amxx
9212 else if((plugin = is_plugin_loaded("DeagsMapManage")) != -1)
9213 {
9214 dmmName = "DeagsMapManage";
9215 }
9216
9217 // Deagles' Map Management 2.40 - deagsmapmanager.amxx
9218 else if((plugin = is_plugin_loaded("DeagsMapManager")) != -1)
9219 {
9220 dmmName = "DeagsMapManager";
9221 }
9222
9223 // Mapchooser4 - mapchooser4.amxx
9224 else if((plugin = is_plugin_loaded("Nextmap Chooser 4")) != -1)
9225 {
9226 log_amx("Starting a map vote from Nextmap Chooser 4");
9227
9228 new oldWinLimit = get_cvar_num("mp_winlimit"), oldMaxRounds = get_cvar_num("mp_maxrounds");
9229 set_cvar_num("mp_winlimit",0); // skip winlimit check
9230 set_cvar_num("mp_maxrounds",1); // trick plugin to think game is almost over
9231
9232 // deactivate g_buyingtime variable
9233 if(callfunc_begin_i(get_func_id("buyFinished",plugin),plugin) == 1)
9234 callfunc_end();
9235
9236 // call the vote
9237 if(callfunc_begin_i(get_func_id("voteNextmap",plugin),plugin) == 1)
9238 {
9239 callfunc_push_str("",false);
9240 callfunc_end();
9241 }
9242
9243 // set maxrounds back
9244 set_cvar_num("mp_winlimit",oldWinLimit);
9245 set_cvar_num("mp_maxrounds",oldMaxRounds);
9246 }
9247
9248 // NOTHING?
9249 else log_amx("Using gg_vote_setting without any compatible plugins: could not start a vote!");
9250
9251 // do DMM stuff
9252 if(dmmName[0])
9253 {
9254 log_amx("Starting a map vote from %s",dmmName);
9255
9256 // allow voting
9257 /*if(callfunc_begin("dmapvotemode",dmmName) == 1)
9258 {
9259 callfunc_push_int(0); // server
9260 callfunc_end();
9261 }*/
9262
9263 new oldWinLimit = get_cvar_num("mp_winlimit"), Float:oldTimeLimit = get_cvar_float("mp_timelimit");
9264 set_cvar_num("mp_winlimit",99999); // don't allow extending
9265 set_cvar_float("mp_timelimit",0.0); // don't wait for buying
9266 set_cvar_num("enforce_timelimit",1); // don't change map after vote
9267
9268 // call the vote
9269 if(callfunc_begin_i(get_func_id("startthevote",plugin),plugin) == 1)
9270 callfunc_end();
9271
9272 set_cvar_num("mp_winlimit",oldWinLimit);
9273 set_cvar_float("mp_timelimit",oldTimeLimit);
9274
9275 // disallow further voting
9276 /*if(callfunc_begin("dmapcyclemode",dmmName) == 1)
9277 {
9278 callfunc_push_int(0); // server
9279 callfunc_end();
9280 }*/
9281
9282 set_task(20.1,"dmm_stop_mapchange");
9283 }
9284}
9285
9286// stop DMM from changing maps after the vote has been tallied
9287public dmm_stop_mapchange()
9288{
9289 remove_task(333333,1); // outside
9290}
9291
9292// set amx_nextmap to the next map
9293stock set_nextmap()
9294{
9295 new mapCycleFile[64];
9296 get_gg_mapcycle_file(mapCycleFile,63);
9297
9298 // no mapcycle, leave amx_nextmap alone
9299 if(!mapCycleFile[0] || !file_exists(mapCycleFile))
9300 {
9301 set_localinfo("gg_cycle_num","0");
9302 return 0;
9303 }
9304
9305 new strVal[10];
9306
9307 // have not gotten cycleNum yet (only get it once, because
9308 // set_nextmap is generally called at least twice per map, and we
9309 // don't want to change it twice)
9310 if(cycleNum == -1)
9311 {
9312 get_localinfo("gg_cycle_num",strVal,9);
9313 cycleNum = str_to_num(strVal);
9314 }
9315
9316 new firstMap[32], currentMap[32], lineData[32], i, line, foundMap;
9317 get_mapname(currentMap,31);
9318
9319 new file = fopen(mapCycleFile,"rt");
9320 while(file && !feof(file))
9321 {
9322 fgets(file,lineData,31);
9323
9324 trim(lineData);
9325 replace(lineData,31,".bsp",""); // remove extension
9326 new len = strlen(lineData) - 2;
9327
9328 // stop at a comment
9329 for(i=0;i<len;i++)
9330 {
9331 // supports config-style (;) and coding-style (//)
9332 if(lineData[i] == ';' || (lineData[i] == '/' && lineData[i+1] == '/'))
9333 {
9334 copy(lineData,i,lineData);
9335 break;
9336 }
9337 }
9338
9339 trim(lineData);
9340 if(!lineData[0]) continue;
9341
9342 // save first map
9343 if(!firstMap[0]) copy(firstMap,31,lineData);
9344
9345 // we reached the line after our current map's line
9346 if(line == cycleNum+1)
9347 {
9348 // remember so
9349 foundMap = 1;
9350
9351 // get ready to change to it
9352 set_cvar_string("amx_nextmap",lineData);
9353
9354 // remember this map's line for next time
9355 num_to_str(line,strVal,9);
9356 set_localinfo("gg_cycle_num",strVal);
9357
9358 break;
9359 }
9360
9361 line++;
9362 }
9363 if(file) fclose(file);
9364
9365 // we didn't find next map
9366 if(!foundMap)
9367 {
9368 // reset line number to first (it's zero-based)
9369 set_localinfo("gg_cycle_num","0");
9370
9371 // no maps listed, go to current
9372 if(!firstMap[0]) set_cvar_string("amx_nextmap",currentMap);
9373
9374 // go to first map listed
9375 else set_cvar_string("amx_nextmap",firstMap);
9376 }
9377
9378 return 1;
9379}
9380
9381// go to amx_nextmap
9382public goto_nextmap()
9383{
9384 new mapCycleFile[64];
9385 get_gg_mapcycle_file(mapCycleFile,63);
9386
9387 // no gungame mapcycle
9388 if(!mapCycleFile[0] || !file_exists(mapCycleFile))
9389 {
9390 new custom[256];
9391 get_pcvar_string(gg_changelevel_custom,custom,255);
9392
9393 // try custom changelevel command
9394 if(custom[0])
9395 {
9396 server_cmd(custom);
9397 return;
9398 }
9399 }
9400
9401 if(galileoID != -1)
9402 {
9403 if(callfunc_begin_i(get_func_id("map_change",galileoID),galileoID) == 1)
9404 {
9405 callfunc_end();
9406 return;
9407 }
9408 }
9409
9410 // otherwise, go to amx_nextmap
9411 new nextMap[32];
9412 get_cvar_string("amx_nextmap",nextMap,31);
9413
9414 server_cmd("changelevel %s",nextMap);
9415}
9416
9417// find a player's weapon entity
9418stock get_weapon_ent(id,wpnid=0,wpnName[]="")
9419{
9420 // who knows what wpnName will be
9421 static newName[24];
9422
9423 // need to find the name
9424 if(wpnid) get_weaponname(wpnid,newName,23);
9425
9426 // go with what we were told
9427 else copy(newName,23,wpnName);
9428
9429 // prefix it if we need to
9430 if(!equal(newName,"weapon_",7))
9431 format(newName,23,"weapon_%s",newName);
9432
9433 return fm_find_ent_by_owner(maxPlayers,newName,id);
9434}
9435
9436// counts number of chars in a string, by (probably) Twilight Suzuka
9437stock str_count(str[],searchchar)
9438{
9439 new i = 0;
9440 new maxlen = strlen(str);
9441 new count = 0;
9442
9443 for(i=0;i<=maxlen;i++)
9444 {
9445 if(str[i] == searchchar)
9446 count++;
9447 }
9448 return count;
9449}
9450
9451// find the nth occurance of a character in a string, based on str_count
9452stock str_find_num(str[],searchchar,number)
9453{
9454 new i;
9455 new maxlen = strlen(str);
9456 new found = 0;
9457
9458 for(i=0;i<=maxlen;i++)
9459 {
9460 if(str[i] == searchchar)
9461 {
9462 if(++found == number)
9463 return i;
9464 }
9465 }
9466 return -1;
9467}
9468
9469// works like contain, but looks only for a specific character
9470stock contain_char(str[],chara)
9471{
9472 new i;
9473 while(str[i] != 0)
9474 {
9475 if(str[i] == chara) return i;
9476 i++;
9477 }
9478 return -1;
9479}
9480
9481// cuts a snippet out of a string
9482stock remove_snippet(string[],strLen,start,end)
9483{
9484 new i, newpos;
9485 for(i=start;i<strLen;i++)
9486 {
9487 if(!string[i]) break;
9488 newpos = i + end - start + 1;
9489
9490 if(newpos >= strLen) string[i] = 0;
9491 else string[i] = string[newpos];
9492 }
9493
9494 return 1;
9495}
9496
9497// gets a player id that triggered certain logevents, by VEN
9498stock get_loguser_index()
9499{
9500 static loguser[80], name[32];
9501 read_logargv(0,loguser,79);
9502 parse_loguser(loguser,name,31);
9503
9504 return get_user_index(name);
9505}
9506
9507// checks if a space is vacant, by VEN
9508stock bool:is_hull_vacant(const Float:origin[3],hull)
9509{
9510 new tr = 0;
9511 engfunc(EngFunc_TraceHull,origin,origin,0,hull,0,tr);
9512
9513 if(!get_tr2(tr,TR_StartSolid) && !get_tr2(tr,TR_AllSolid) && get_tr2(tr,TR_InOpen))
9514 return true;
9515
9516 return false;
9517}
9518
9519// gets a weapon's category, just a shortcut to the weaponSlots table basically
9520stock get_weapon_category(id=0,name[]="")
9521{
9522 if(name[0])
9523 {
9524 if(equal(name,"weapon_",7)) id = get_weaponid(name);
9525 else
9526 {
9527 static newName[24];
9528 formatex(newName,23,"weapon_%s",name);
9529 id = get_weaponid(newName);
9530 }
9531 }
9532
9533 if(id < 1 || id > 30) return -1;
9534 return weaponSlots[id];
9535}
9536
9537// if a player is allowed to score (at least 1 rival player)
9538stock can_score(id)
9539{
9540 if(!is_user_connected(id)) return 0;
9541
9542 new penalty = get_pcvar_num(gg_tk_penalty);
9543 for(new player=1;player<=maxPlayers;player++)
9544 {
9545 // this player is on a real team, and he's on the opposite team, or we are playing FFA
9546 if( player != id && is_user_connected(player) && on_valid_team(player) && (penalty < 0 || cs_get_user_team(id) != cs_get_user_team(player)) )
9547 return 1;
9548 }
9549
9550 return 0;
9551}
9552
9553// returns 1 if there are only bots in the server, 0 if not
9554stock only_bots()
9555{
9556 new player;
9557 for(player=1;player<=maxPlayers;player++)
9558 {
9559 if(is_user_connected(player) && !is_user_bot(player))
9560 return 0;
9561 }
9562
9563 // didn't find any humans
9564 return 1;
9565}
9566
9567// gives a player a weapon efficiently
9568stock ham_give_weapon(id,const weapon[])
9569{
9570 if(!equal(weapon,"weapon_",7)) return 0;
9571
9572 new wEnt = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,weapon));
9573 if(!pev_valid(wEnt)) return 0;
9574
9575 set_pev(wEnt,pev_spawnflags,SF_NORESPAWN);
9576 dllfunc(DLLFunc_Spawn,wEnt);
9577
9578 if(!ExecuteHamB(Ham_AddPlayerItem,id,wEnt))
9579 {
9580 if(pev_valid(wEnt)) set_pev(wEnt,pev_flags,pev(wEnt,pev_flags) | FL_KILLME);
9581 return 0;
9582 }
9583
9584 ExecuteHamB(Ham_Item_AttachToPlayer,wEnt,id)
9585 return 1;
9586}
9587
9588// takes a weapon from a player efficiently
9589stock ham_strip_weapon(id,const weapon[])
9590{
9591 if(!equal(weapon,"weapon_",7)) return 0;
9592
9593 new wId = get_weaponid(weapon);
9594 if(!wId) return 0;
9595
9596 new wEnt;
9597 while((wEnt = engfunc(EngFunc_FindEntityByString,wEnt,"classname",weapon)) && pev(wEnt,pev_owner) != id) {}
9598 if(!wEnt) return 0;
9599
9600 if(get_user_weapon(id) == wId) ExecuteHamB(Ham_Weapon_RetireWeapon,wEnt);
9601
9602 if(!ExecuteHamB(Ham_RemovePlayerItem,id,wEnt)) return 0;
9603 ExecuteHamB(Ham_Item_Kill,wEnt);
9604
9605 set_pev(id,pev_weapons,pev(id,pev_weapons) & ~(1<<wId));
9606
9607 if(wId == CSW_C4)
9608 {
9609 cs_set_user_plant(id,0,0);
9610 cs_set_user_bpammo(id,CSW_C4,0);
9611 }
9612 else if(wId == CSW_SMOKEGRENADE || wId == CSW_FLASHBANG || wId == CSW_HEGRENADE)
9613 cs_set_user_bpammo(id,wId,0);
9614
9615 return 1;
9616}
9617
9618// gets the weapon that a killer used, just like CHalfLifeMultiplay::DeathNotice
9619stock get_killer_weapon(killer,inflictor,retVar[],retLen)
9620{
9621 static killer_weapon_name[32];
9622 killer_weapon_name = "world"; // by default, the player is killed by the world
9623
9624 if(pev_valid(killer) && (pev(killer,pev_flags) & FL_CLIENT))
9625 {
9626 if(pev_valid(inflictor))
9627 {
9628 if(inflictor == killer)
9629 {
9630 // if the inflictor is the killer, then it must be their current weapon doing the damage
9631 new weapon = get_user_weapon(killer);
9632 get_weaponname(weapon,killer_weapon_name,31);
9633 }
9634 else pev(inflictor,pev_classname,killer_weapon_name,31); // it's just that easy
9635 }
9636 }
9637 else
9638 {
9639 if(pev_valid(killer)) pev(inflictor,pev_classname,killer_weapon_name,31);
9640 else if(killer == 0) killer_weapon_name = "worldspawn";
9641 }
9642
9643 // strip the monster_* or weapon_* from the inflictor's classname
9644 if(equal(killer_weapon_name,"weapon_",7))
9645 copy(killer_weapon_name,31,killer_weapon_name[7]);
9646 else if(equal(killer_weapon_name,"monster_",8))
9647 copy(killer_weapon_name,31,killer_weapon_name[8]);
9648 else if(equal(killer_weapon_name,"func_",5))
9649 copy(killer_weapon_name,31,killer_weapon_name[5]);
9650
9651 // output
9652 copy(retVar,retLen,killer_weapon_name);
9653}
9654
9655// gets a team's color
9656stock get_team_color(CsTeams:team,ret[],retLen)
9657{
9658 switch(team)
9659 {
9660 case CS_TEAM_T: return formatex(ret,retLen,"FF3F3F");
9661 case CS_TEAM_CT: return formatex(ret,retLen,"99CCFF");
9662 }
9663
9664 return formatex(ret,retLen,"FFFFFF");
9665}
9666
9667// gets the name of a team
9668stock get_team_name(CsTeams:team,ret[],retLen)
9669{
9670 switch(team)
9671 {
9672 case CS_TEAM_T: return formatex(ret,retLen,"TERRORIST");
9673 case CS_TEAM_CT: return formatex(ret,retLen,"CT");
9674 case CS_TEAM_SPECTATOR: return formatex(ret,retLen,"SPECTATOR");
9675 }
9676
9677 return formatex(ret,retLen,"UNASSIGNED");
9678}
9679
9680// gets the amount of players on a team
9681stock team_player_count(CsTeams:team)
9682{
9683 new player, count;
9684 for(player=1;player<=maxPlayers;player++)
9685 {
9686 if(is_user_connected(player) && cs_get_user_team(player) == team)
9687 count++;
9688 }
9689
9690 return count;
9691}
9692
9693// is this player on a valid team?
9694on_valid_team(id)
9695{
9696 new CsTeams:team = cs_get_user_team(id);
9697 return (team == CS_TEAM_T || team == CS_TEAM_CT);
9698}
9699
9700// gets a number's suffix. sort of bad, has to convert to string.
9701stock get_number_suffix(number,ret[],retLen)
9702{
9703 static str[8];
9704 num_to_str(number,str,7);
9705 new len = strlen(str);
9706
9707 if(number >= 10 && str[len-2] == '1') // second to last digit
9708 return formatex(ret,retLen,"th"); // 10-19 end in 'th
9709
9710 switch(str[len-1]) // last digit
9711 {
9712 case '1': return formatex(ret,retLen,"st");
9713 case '2': return formatex(ret,retLen,"nd");
9714 case '3': return formatex(ret,retLen,"rd");
9715 }
9716
9717 return formatex(ret,retLen,"th");
9718}