· 8 years ago · Feb 05, 2018, 01:46 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,"Marian");
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_disconnect(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"); // wite 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], runnerUpName[32], runnerUp3Name[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,runnerUp, teamplay = get_pcvar_num(gg_teamplay),runnerUp3;
3998
3999 if(teamplay) leader = teamplay_get_lead_team(leaderLevel,numLeaders);
4000 else leader = get_leader(leaderLevel,numLeaders,runnerUp,runnerUp3);
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 get_user_name(runnerUp,runnerUpName,31)
4016 get_user_name(runnerUp3,runnerUp3Name,31)
4017
4018 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);
4019 /*
4020 if(numLeaders > 1)
4021 {
4022 if(teamplay)
4023 {
4024 static otherName[10];
4025 get_team_name((leader == 1) ? CS_TEAM_CT : CS_TEAM_T,otherName,9);
4026
4027 ShowSyncHudMsg(0,hudSyncLDisplay,"%L: %s + %s (%i - %s)",LANG_PLAYER,"LEADER",leaderName,otherName,leaderLevel,teamLvlWeapon[leader])
4028 }
4029 else ShowSyncHudMsg(0,hudSyncLDisplay,"%L: %s +%i (%i - %s)",LANG_PLAYER,"LEADER",leaderName,numLeaders-1,leaderLevel,lvlWeapon[leader]);
4030 }
4031 else
4032 */
4033 ShowSyncHudMsg(0,hudSyncLDisplay,"1. %s - Level %i (%s)^n 2. %s - Level %i (%s)^n 3. %s - Level %i (%s)",leaderName,leaderLevel,(teamplay) ? teamLvlWeapon[leader] : lvlWeapon[leader],runnerUpName,level[runnerUp],(teamplay) ? teamLvlWeapon[runnerUp] : lvlWeapon[runnerUp],runnerUp3Name,level[runnerUp3],(teamplay) ? teamLvlWeapon[runnerUp3] : lvlWeapon[runnerUp3])
4034
4035 return 1;
4036}
4037
4038// show the nice HUD progress display
4039show_progress_display(id)
4040{
4041 static statusString[48];
4042
4043 // weapon-specific warmup
4044 if(warmup > 0 && warmupWeapon[0]) return;
4045
4046 new teamplay = get_pcvar_num(gg_teamplay);
4047
4048 // old-school: sweet and simple
4049 if((get_pcvar_num(gg_messages) & MSGS_CLASSIC))
4050 {
4051 new goal;
4052 if(teamplay) goal = get_level_goal(teamLevel[_:cs_get_user_team(id)],id);
4053 else goal = get_level_goal(level[id],id);
4054
4055 gungame_print(id,0,1,"%L %%n%i%%e :: %%n%s%%e",id,(teamplay) ? "ON_LEVEL_TEAM" : "ON_LEVEL",level[id],lvlWeapon[id]);
4056 gungame_print(id,0,1,"%L",id,"PROGRESS_DISPLAY",goal-score[id],score[id],goal);
4057
4058 return;
4059 }
4060
4061 if(teamplay)
4062 {
4063 new team = _:cs_get_user_team(id), otherTeam = (team == 1) ? 2 : 1;
4064 if(team != 1 && team != 2) return;
4065
4066 new leaderLevel, numLeaders, leader = teamplay_get_lead_team(leaderLevel,numLeaders);
4067
4068 // tied
4069 if(numLeaders > 1) formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY_TEAM2");
4070
4071 // leading
4072 else if(leader == team) formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY_TEAM1",teamLevel[team]-teamLevel[otherTeam]);
4073
4074 // losing
4075 else formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY_TEAM3",teamLevel[otherTeam]-teamLevel[team]);
4076 }
4077 else
4078 {
4079 new leaderLevel, numLeaders, runnerUp;
4080 new leader = get_leader(leaderLevel,numLeaders,runnerUp);
4081
4082 if(level[id] == leaderLevel)
4083 {
4084 if(numLeaders == 1) formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY1",leaderLevel-level[runnerUp]);
4085 else if(numLeaders == 2)
4086 {
4087 new otherLeader;
4088 if(leader != id) otherLeader = leader;
4089 else
4090 {
4091 new player;
4092 for(player=1;player<=maxPlayers;player++)
4093 {
4094 if(is_user_connected(player) && level[player] == leaderLevel && player != id)
4095 {
4096 otherLeader = player;
4097 break;
4098 }
4099 }
4100 }
4101
4102 static otherName[32];
4103 get_user_name(otherLeader,otherName,31);
4104
4105 formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY2",otherName);
4106 }
4107 else
4108 {
4109 static numWord[16], lang[3];
4110
4111 // if english, use words, otherwise use digits
4112 get_user_info(id,"lang",lang,2);
4113 if(equali(lang,"en"))
4114 {
4115 num_to_word(numLeaders-1,numWord,15);
4116 trim(numWord);
4117 }
4118 else formatex(numWord,15,"%i",numLeaders-1);
4119
4120 formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY3",numWord);
4121 }
4122 }
4123 else formatex(statusString,47,"%L",id,"PROGRESS_DISPLAY4",leaderLevel-level[id]);
4124 }
4125
4126 gungame_hudmessage(id,5.0,"%L %i (%s)^n%s",id,(teamplay) ? "ON_LEVEL_TEAM" : "ON_LEVEL",level[id],lvlWeapon[id],statusString);
4127}
4128
4129// play the taken/tied/lost lead sounds
4130public play_lead_sounds(id,oldLevel,Float:playDelay)
4131{
4132 // id: the player whose level changed
4133 // oldLevel: his level before it changed
4134 // playDelay: how long to wait until we play id's sounds
4135
4136 if(get_pcvar_num(gg_teamplay))
4137 {
4138 // redirect to other function
4139 teamplay_play_lead_sounds(id,oldLevel,Float:playDelay);
4140 return;
4141 }
4142
4143 // warmup or game over, no one cares
4144 if(warmup > 0 || won) return;
4145
4146 // no level change
4147 if(level[id] == oldLevel) return;
4148
4149 //
4150 // monitor MY stuff first
4151 //
4152
4153 new leaderLevel, numLeaders;
4154 get_leader(leaderLevel,numLeaders);
4155
4156 // I'm now on the leader level
4157 if(level[id] == leaderLevel)
4158 {
4159 // someone else here?
4160 if(numLeaders > 1)
4161 {
4162 new params[2];
4163 params[0] = id;
4164 params[1] = gg_sound_tiedlead;
4165
4166 remove_task(TASK_PLAY_LEAD_SOUNDS+id);
4167 set_task(playDelay,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+id,params,2);
4168 }
4169
4170 // just me, I'm the winner!
4171 else
4172 {
4173 // did I just pass someone?
4174 if(level[id] > oldLevel && num_players_on_level(oldLevel))
4175 {
4176 new params[2];
4177 params[0] = id;
4178 params[1] = gg_sound_takenlead;
4179
4180 remove_task(TASK_PLAY_LEAD_SOUNDS+id);
4181 set_task(playDelay,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+id,params,2);
4182 }
4183 }
4184 }
4185
4186 // WAS I on the leader level?
4187 else if(oldLevel == leaderLevel)
4188 {
4189 new params[2];
4190 params[0] = id;
4191 params[1] = gg_sound_lostlead;
4192
4193 remove_task(TASK_PLAY_LEAD_SOUNDS+id);
4194 set_task(playDelay,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+id,params,2);
4195
4196 //return; // will not effect other players
4197 }
4198
4199 // nothing of importance
4200 else return; // will not effect other players
4201
4202 //
4203 // now monitor other players.
4204 // if we get this far, id is now in the lead level
4205 //
4206
4207 new player;
4208 for(player=1;player<=maxPlayers;player++)
4209 {
4210 if(!is_user_connected(player) || player == id) continue;
4211
4212 // PLAYER tied with ID
4213 if(level[player] == level[id])
4214 {
4215 // don't tell him if he already got it from another player
4216 if(num_players_on_level(level[id]) <= 2
4217 || (oldLevel > level[id] && leaderLevel == level[id])) // dropped into tied position
4218 {
4219 new params[2];
4220 params[0] = player;
4221 params[1] = gg_sound_tiedlead;
4222
4223 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
4224 set_task(0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
4225 }
4226
4227 continue;
4228 }
4229
4230 // PLAYER passed by ID
4231 else if(level[id] > level[player] && level[player] == oldLevel)
4232 {
4233 // don't tell him if he already got it from another player
4234 if(num_players_on_level(level[id]) <= 1)
4235 {
4236 new params[2];
4237 params[0] = player;
4238 params[1] = gg_sound_lostlead;
4239
4240 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
4241 set_task(0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
4242 }
4243
4244 continue;
4245 }
4246
4247 // ID passed by PLAYER
4248 else if(level[player] > level[id] && leaderLevel == level[player])
4249 {
4250 // I stand alone!
4251 if(num_players_on_level(level[player]) <= 1)
4252 {
4253 new params[2];
4254 params[0] = player;
4255 params[1] = gg_sound_takenlead;
4256
4257 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
4258 set_task(0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
4259 }
4260
4261 continue;
4262 }
4263 }
4264}
4265
4266// manage game_player_equip and player_weaponstrip entities
4267public manage_equips()
4268{
4269 static classname[20], targetname[24];
4270 new ent, i, block_equips = get_pcvar_num(gg_block_equips), enabled = ggActive;
4271
4272 // go through both entities to monitor
4273 for(i=0;i<4;i++)
4274 {
4275 // get classname for current iteration
4276 switch(i)
4277 {
4278 case 0: classname = "game_player_equip";
4279 case 1: classname = "game_player_equip2";
4280 case 2: classname = "player_weaponstrip";
4281 default: classname = "player_weaponstrip2";
4282 }
4283
4284 // go through whatever entity
4285 ent = 0;
4286 while((ent = fm_find_ent_by_class(ent,classname)))
4287 {
4288 // allowed to have this, reverse possible changes
4289 if(!enabled || !block_equips || (i >= 2 && block_equips < 2)) // player_weaponstrip switch
4290 {
4291 pev(ent,pev_targetname,targetname,23);
4292
4293 // this one was blocked
4294 if(equal(targetname,"gg_block_equips"))
4295 {
4296 pev(ent,TNAME_SAVE,targetname,23);
4297
4298 set_pev(ent,pev_targetname,targetname);
4299 set_pev(ent,TNAME_SAVE,"");
4300
4301 switch(i)
4302 {
4303 case 0, 1: set_pev(ent,pev_classname,"game_player_equip");
4304 default: set_pev(ent,pev_classname,"player_weaponstrip");
4305 }
4306 }
4307 }
4308
4309 // not allowed to pickup others, make possible changes
4310 else
4311 {
4312 pev(ent,pev_targetname,targetname,23);
4313
4314 // needs to be blocked, but hasn't been yet
4315 if(targetname[0] && !equal(targetname,"gg_block_equips"))
4316 {
4317 set_pev(ent,TNAME_SAVE,targetname);
4318 set_pev(ent,pev_targetname,"gg_block_equips");
4319
4320 // classname change is required sometimes for some reason
4321 switch(i)
4322 {
4323 case 0, 1: set_pev(ent,pev_classname,"game_player_equip2");
4324 default: set_pev(ent,pev_classname,"player_weaponstrip2");
4325 }
4326 }
4327 }
4328 }
4329 }
4330}
4331
4332// someone respawned
4333spawned(id)
4334{
4335 // should be filtered in ham hook
4336 if(/*!ggActive || !is_user_connected(id) ||*/ !on_valid_team(id))
4337 return;
4338
4339 remove_task(TASK_CHECK_DEATHMATCH+id);
4340
4341 // should be frozen?
4342 if(won)
4343 {
4344 new iterations = get_pcvar_num(gg_map_iterations);
4345 if(mapIteration < iterations || !iterations)
4346 {
4347 // not done yet, just freeze players
4348 set_pev(id,pev_flags,pev(id,pev_flags) | FL_FROZEN);
4349 fm_set_user_godmode(id,1);
4350 }
4351
4352 // done, make sure HUD is hidden
4353 emessage_begin(MSG_ALL,gmsgHideWeapon);
4354 ewrite_byte((1<<0)|(1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<6)); // can't use (1<<2) or text disappears
4355 emessage_end();
4356
4357 emessage_begin(MSG_ALL,gmsgCrosshair);
4358 ewrite_byte(0); // hide
4359 emessage_end();
4360
4361 return;
4362 }
4363
4364 if(get_pcvar_num(gg_pickup_others)) strip_starting_pistols(id);
4365
4366 afkCheck[id] = 0;
4367 levelsThisRound[id] = 0;
4368
4369 // just joined
4370 if(!level[id])
4371 {
4372 new teamplay = get_pcvar_num(gg_teamplay);
4373
4374 // warming up
4375 if(warmup > 0 && !teamplay)
4376 {
4377 change_level(id,1,1,_,1); // just joined, always score
4378 }
4379 else
4380 {
4381 // handicap
4382 new handicapMode = get_pcvar_num(gg_handicap_on);
4383 if(handicapMode && !teamplay)
4384 {
4385 new rcvHandicap = 1;
4386
4387 // top10 doesn't receive handicap -- also make sure we are using top10
4388 if(sqlInit && !get_pcvar_num(gg_top10_handicap) && get_pcvar_num(gg_stats_mode))
4389 {
4390 static authid[32];
4391 get_gg_authid(id,authid,31);
4392
4393 new si = get_gg_si();
4394#if defined SQL
4395 if(!statsPosition[id][si]) statsPosition[id][si] = stats_get_position(id,authid,si);
4396 if(0 < statsPosition[id][si] && statsPosition[id][si] <= TOP_PLAYERS) rcvHandicap = 0; // I'm in the top10
4397#else
4398 for(new i=0;i<TOP_PLAYERS;i++)
4399 {
4400 if(i >= statsSize[si]) continue;
4401 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[si],i),sfStatsStruct);
4402
4403 // I'm in top10, don't give me handicap
4404 if(equal(authid,sfStatsStruct[sdAuthid]))
4405 {
4406 rcvHandicap = 0;
4407 break;
4408 }
4409 }
4410#endif
4411 }
4412
4413 if(rcvHandicap)
4414 {
4415 new player;
4416
4417 // find lowest level (don't use bots unless we have to)
4418 if(handicapMode == 2)
4419 {
4420 new isBot, myLevel, lowestLevel, lowestBotLevel;
4421 for(player=1;player<=maxPlayers;player++)
4422 {
4423 if(!is_user_connected(player) || player == id)
4424 continue;
4425
4426 isBot = is_user_bot(player);
4427 myLevel = level[player];
4428
4429 if(!myLevel) continue;
4430
4431 if(!isBot && (!lowestLevel || myLevel < lowestLevel))
4432 lowestLevel = myLevel;
4433 else if(isBot && (!lowestBotLevel || myLevel < lowestBotLevel))
4434 lowestBotLevel = myLevel;
4435 }
4436
4437 // CLAMP!
4438 if(!lowestLevel) lowestLevel = 1;
4439 if(!lowestBotLevel) lowestBotLevel = 1;
4440
4441 change_level(id,(lowestLevel > 1) ? lowestLevel : lowestBotLevel,1,_,1); // just joined, always score
4442 }
4443
4444 // find average level
4445 else
4446 {
4447 new Float:average, num;
4448 for(player=1;player<=maxPlayers;player++)
4449 {
4450 if(is_user_connected(player) && level[player])
4451 {
4452 average += float(level[player]);
4453 num++;
4454 }
4455 }
4456
4457 average /= float(num);
4458 change_level(id,(average >= 0.5) ? floatround(average) : 1,1,_,1); // just joined, always score
4459 }
4460 }
4461
4462 // not eligible for handicap (in top10 with gg_top10_handicap disabled)
4463 else change_level(id,1,1,_,1); // just joined, always score
4464 }
4465
4466 // no handicap enabled or playing teamplay
4467 else
4468 {
4469 if(teamplay)
4470 {
4471 new team = _:cs_get_user_team(id);
4472
4473 if(team == 1 || team == 2)
4474 {
4475 // my team has a level already
4476 if(teamLevel[team])
4477 {
4478 change_level(id,teamLevel[team],1,_,1,_,0); // just joined, always score, don't effect team
4479 if(score[id] != teamScore[team]) change_score(id,teamScore[team]-score[id],_,_,0); // don't effect team
4480 }
4481
4482 // my team just started
4483 else
4484 {
4485 // initialize its values
4486 teamplay_update_level(team,1,id);
4487 teamplay_update_score(team,0,id);
4488
4489 change_level(id,teamLevel[team],1,_,1,_,0); // just joined, always score, don't effect team
4490 }
4491 }
4492 }
4493
4494 // solo-play
4495 else change_level(id,1,1,_,1); // just joined, always score
4496 }
4497 }
4498 }
4499
4500 // didn't just join
4501 else
4502 {
4503 if(star[id])
4504 {
4505 end_star(TASK_END_STAR+id);
4506 remove_task(TASK_END_STAR+id);
4507 }
4508
4509 if(get_pcvar_num(gg_teamplay))
4510 {
4511 new team = _:cs_get_user_team(id);
4512
4513 // my team just started
4514 if((team == 1 || team == 2) && !teamLevel[team])
4515 {
4516 // initialize its values
4517 teamplay_update_level(team,1,id);
4518 teamplay_update_score(team,0,id);
4519
4520 change_level(id,teamLevel[team]-level[id],_,_,1,_,0); // always score, don't effect team
4521 change_score(id,teamScore[team]-score[id],_,_,0); // don't effect team
4522 }
4523 }
4524
4525 give_level_weapon(id);
4526 refill_ammo(id);
4527 }
4528
4529 // show welcome message
4530 if(!welcomed[id] && get_pcvar_num(gg_join_msg))
4531 show_welcome(id);
4532
4533 // update bomb for DM
4534 if(cs_get_user_team(id) == CS_TEAM_T && !get_pcvar_num(gg_block_objectives) && get_pcvar_num(gg_dm))
4535 {
4536 if(bombStatus[3] == BOMB_PICKEDUP)
4537 {
4538 message_begin(MSG_ONE,gmsgBombPickup,_,id);
4539 message_end();
4540 }
4541 else if(bombStatus[0] || bombStatus[1] || bombStatus[2])
4542 {
4543 message_begin(MSG_ONE,gmsgBombDrop,_,id);
4544 write_coord(bombStatus[0]);
4545 write_coord(bombStatus[1]);
4546 write_coord(bombStatus[2]);
4547 write_byte(bombStatus[3]);
4548 message_end();
4549 }
4550 }
4551
4552 if(get_pcvar_num(gg_disable_money)) hide_money(id);
4553
4554 // switch to our appropiate weapon, for those without the switch to new weapon option
4555 if((warmup > 0 && warmupWeapon[0] && equal(warmupWeapon,KNIFE)) || equal(lvlWeapon[id],KNIFE) /* || (get_pcvar_num(gg_knife_elite) && levelsThisRound[id] > 0)*/)
4556 {
4557 engclient_cmd(id,WEAPON_KNIFE);
4558 client_cmd(id,WEAPON_KNIFE);
4559 }
4560 else if(get_pcvar_num(gg_nade_glock) && equal(lvlWeapon[id],HEGRENADE))
4561 {
4562 engclient_cmd(id,WEAPON_GLOCK18);
4563 client_cmd(id,WEAPON_GLOCK18);
4564 }
4565 else if(lvlWeapon[id][0])
4566 {
4567 static wpnName[24];
4568 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
4569
4570 engclient_cmd(id,wpnName);
4571 client_cmd(id,wpnName);
4572 }
4573
4574 // remember spawn info for AFK protection
4575 if(get_pcvar_num(gg_afk_protection))
4576 {
4577 pev(id,pev_origin,spawnOrigin[id]);
4578 pev(id,pev_v_angle,spawnAngles[id]);
4579 afkCheck[id] = 1;
4580 }
4581}
4582
4583// player changed his team
4584player_teamchange(id,oldTeam,newTeam)
4585{
4586 if(!ggActive) return 0;
4587
4588 // remember for crazy team switches
4589 lastTeam[id] = newTeam;
4590
4591 // allow us to join in on deathmatch
4592 if(oldTeam == 0 && (newTeam == 1 || newTeam == 2) && !roundEnded && get_pcvar_num(gg_dm) && !task_exists(TASK_CHECK_JOINCLASS+id))
4593 {
4594 remove_task(TASK_CHECK_DEATHMATCH+id);
4595 set_task(5.0,"check_deathmatch",TASK_CHECK_DEATHMATCH+id);
4596 }
4597
4598 // keep track of time
4599 new Float:now = get_gametime();
4600 if(oldTeam == 1 || oldTeam == 2) teamTimes[id][oldTeam-1] += now - lastSwitch[id];
4601 lastSwitch[id] = now;
4602
4603 // we already have a level, set our values to our new team's
4604 if(level[id] && get_pcvar_num(gg_teamplay) && (newTeam == 1 || newTeam == 2))
4605 {
4606 // set them directly
4607 level[id] = teamLevel[newTeam];
4608 lvlWeapon[id] = teamLvlWeapon[newTeam];
4609 score[id] = teamScore[newTeam];
4610 }
4611
4612 return 1;
4613}
4614
4615// restart the round
4616public restart_round(delay)
4617{
4618 // clear values
4619 /*new player;
4620 for(player=1;player<=maxPlayers;player++)
4621 {
4622 if(is_user_connected(player)) clear_values(player,1); // ignore welcome
4623 }
4624
4625 // reset teams as well
4626 clear_team_values(1);
4627 clear_team_values(2);*/
4628
4629 if(delay < 1) delay = 1;
4630
4631 set_cvar_num("sv_restartround",delay);
4632 set_task(float(delay)-0.1,"clear_all_values");
4633}
4634
4635// select a random weapon order
4636//
4637// in cmd_gungame_teamplay we call map_start_cvars which leads to d_rOrder.
4638// when called this way we don't want to let it change teamplay or run teamplay
4639// configs, so we added the keepTeamplay parameter.
4640do_rOrder(keepTeamplay)
4641{
4642 // manage random teamplay
4643 if(initTeamplayInt == -1)
4644 {
4645 get_pcvar_string(gg_teamplay,initTeamplayStr,31);
4646 initTeamplayInt = str_to_num(initTeamplayStr[0]);
4647 }
4648
4649 new amount;
4650
4651 // if we are allowed to change teamplay, and our initial teamplay value was either a
4652 // sequence, or it was just 2 (so select one randomly), then sort through it and pick a value
4653 if(!keepTeamplay && ((amount = str_count(initTeamplayStr,',')+1) > 1 || initTeamplayInt == 2))
4654 {
4655 new info[6], rotation[32];
4656 get_localinfo("gg_tp_iter",info,5);
4657 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
4658
4659 new iter = str_to_num(info), teamplay;
4660
4661 if(iter <= 0 || iter > amount)
4662 {
4663 iter = 1;
4664 set_localinfo("gg_tp_iter","1");
4665 }
4666
4667 // no rotation, just use the given value
4668 if(amount <= 1)
4669 {
4670 if(iter != 1) set_localinfo("gg_tp_iter","1");
4671 // initTeamplayInt should still be set to the one we want to use
4672 }
4673 else
4674 {
4675 for(new i=1;i<=amount;i++)
4676 {
4677 if(contain(rotation,",") != -1)
4678 {
4679 strtok(rotation,info,5,rotation,31,',');
4680 if(i == iter) // this is the one we're looking for
4681 {
4682 initTeamplayInt = str_to_num(info);
4683 break;
4684 }
4685 }
4686 else // we've stripped away everything else and are left with the last one, so use it
4687 {
4688 initTeamplayInt = str_to_num(rotation);
4689 break;
4690 }
4691 }
4692
4693 iter++;
4694 if(iter > amount) iter = 1;
4695 num_to_str(iter,info,5);
4696 set_localinfo("gg_tp_iter",info);
4697 }
4698
4699 if(initTeamplayInt == 2) teamplay = random_num(0,1);
4700 else teamplay = initTeamplayInt;
4701
4702 set_pcvar_num(gg_teamplay,teamplay);
4703
4704 // re-run config files based on teamplay, don't allow toggling
4705 exec_gg_config_file(0,0);
4706 if(teamplay) exec_gg_config_file(1,0);
4707 }
4708
4709 new i, maxRandom, cvar[20], weaponOrder[(MAX_WEAPONS*16)+1];
4710 for(i=1;i<=MAX_WEAPON_ORDERS+1;i++) // +1 so we can detect final
4711 {
4712 formatex(cvar,19,"gg_weapon_order%i",i);
4713 get_cvar_string(cvar,weaponOrder,MAX_WEAPONS*16);
4714 trim(weaponOrder);
4715
4716 // found a blank one, stop here
4717 if(!weaponOrder[0])
4718 {
4719 maxRandom = i - 1;
4720 break;
4721 }
4722 }
4723
4724 // there is just one
4725 if(maxRandom == 1)
4726 {
4727 // get its weapon order and set as current
4728 formatex(cvar,19,"gg_weapon_order1");
4729 get_cvar_string(cvar,weaponOrder,MAX_WEAPONS*16);
4730 set_pcvar_string(gg_weapon_order,weaponOrder);
4731 return;
4732 }
4733
4734 // we found some random ones
4735 if(maxRandom)
4736 {
4737 new randomOrder[30], lastOIstr[6], lastOI, orderAmt;
4738 get_localinfo("gg_rand_order",randomOrder,29);
4739 get_localinfo("gg_last_oi",lastOIstr,5);
4740 lastOI = str_to_num(lastOIstr);
4741 orderAmt = get_rOrder_amount(randomOrder);
4742
4743 // no random order yet, or amount of random orders changed
4744 if(!randomOrder[0] || orderAmt != maxRandom)
4745 {
4746 shuffle_rOrder(randomOrder,29,maxRandom);
4747 lastOI = 0;
4748 }
4749
4750 // reached the end, reshuffle while avoiding this one
4751 else if(get_rOrder_index_val(orderAmt,randomOrder) == get_rOrder_index_val(lastOI,randomOrder))
4752 {
4753 shuffle_rOrder(randomOrder,29,maxRandom,lastOI);
4754 lastOI = 0;
4755 }
4756
4757 new choice = get_rOrder_index_val(lastOI+1,randomOrder);
4758
4759 // get its weapon order
4760 formatex(cvar,19,"gg_weapon_order%i",choice);
4761 get_cvar_string(cvar,weaponOrder,MAX_WEAPONS*16);
4762
4763 // set as current
4764 set_pcvar_string(gg_weapon_order,weaponOrder);
4765
4766 // remember for next time
4767 num_to_str(lastOI+1,lastOIstr,5);
4768 set_localinfo("gg_last_oi",lastOIstr);
4769 }
4770}
4771
4772// get the value of an order index in an order string
4773get_rOrder_index_val(index,randomOrder[])
4774{
4775 // only one listed
4776 if(str_count(randomOrder,',') < 1)
4777 return str_to_num(randomOrder);
4778
4779 // find preceding comma
4780 new search = str_find_num(randomOrder,',',index-1);
4781
4782 // go until succeeding comma
4783 new extract[6];
4784 copyc(extract,5,randomOrder[search+1],',');
4785
4786 return str_to_num(extract);
4787}
4788
4789// gets the amount of orders in an order string
4790get_rOrder_amount(randomOrder[])
4791{
4792 return str_count(randomOrder,',')+1;
4793}
4794
4795// shuffle up our random order
4796stock shuffle_rOrder(randomOrder[],len,maxRandom,avoid=-1)
4797{
4798 randomOrder[0] = 0;
4799
4800 // fill up array with order indexes
4801 new order[MAX_WEAPON_ORDERS], i;
4802 for(i=0;i<maxRandom;i++) order[i] = i+1;
4803
4804 // shuffle it
4805 SortCustom1D(order,maxRandom,"sort_shuffle");
4806
4807 // avoid a specific number as the starting number
4808 while(avoid > 0 && order[0] == avoid)
4809 SortCustom1D(order,maxRandom,"sort_shuffle");
4810
4811 // get them into a string
4812 for(i=0;i<maxRandom;i++)
4813 {
4814 format(randomOrder,len,"%s%s%i",randomOrder,(i>0) ? "," : "",order[i]);
4815 set_localinfo("gg_rand_order",randomOrder);
4816 }
4817}
4818
4819// play a random win sound
4820do_rWinSound()
4821{
4822 // just one, no one cares
4823 if(numWinSounds <= 1)
4824 {
4825 return 0; // 1 minus 1
4826 }
4827
4828 new randomOrder[30], lastWSIstr[6], lastWSI, orderAmt;
4829 get_localinfo("gg_winsound_order",randomOrder,29);
4830 get_localinfo("gg_last_wsi",lastWSIstr,5);
4831 lastWSI = str_to_num(lastWSIstr);
4832 orderAmt = get_rWinSound_amount(randomOrder);
4833
4834 // no random order yet, or amount of random orders changed
4835 if(!randomOrder[0] || orderAmt != numWinSounds)
4836 {
4837 shuffle_rWinSound(randomOrder,29);
4838 lastWSI = 0;
4839 }
4840
4841 // reached the end, reshuffle while avoiding this one
4842 else if(get_rWinSound_index_val(orderAmt,randomOrder) == get_rWinSound_index_val(lastWSI,randomOrder))
4843 {
4844 shuffle_rWinSound(randomOrder,29,lastWSI);
4845 lastWSI = 0;
4846 }
4847
4848 new choice = get_rWinSound_index_val(lastWSI+1,randomOrder);
4849
4850 // remember for next time
4851 num_to_str(lastWSI+1,lastWSIstr,5);
4852 set_localinfo("gg_last_wsi",lastWSIstr);
4853
4854 return choice-1;
4855}
4856
4857// get the value of an order index in an order string
4858get_rWinSound_index_val(index,randomOrder[])
4859{
4860 // only one listed
4861 if(str_count(randomOrder,',') < 1)
4862 return str_to_num(randomOrder);
4863
4864 // find preceding comma
4865 new search = str_find_num(randomOrder,',',index-1);
4866
4867 // go until succeeding comma
4868 new extract[6];
4869 copyc(extract,5,randomOrder[search+1],',');
4870
4871 return str_to_num(extract);
4872}
4873
4874// gets the amount of orders in an order string
4875get_rWinSound_amount(randomOrder[])
4876{
4877 return str_count(randomOrder,',')+1;
4878}
4879
4880// shuffle up our random order
4881stock shuffle_rWinSound(randomOrder[],len,avoid=-1)
4882{
4883 randomOrder[0] = 0;
4884
4885 // fill up array with order indexes
4886 new order[MAX_WINSOUNDS], i;
4887 for(i=0;i<numWinSounds;i++) order[i] = i+1;
4888
4889 // shuffle it
4890 SortCustom1D(order,numWinSounds,"sort_shuffle");
4891
4892 // avoid a specific number as the starting number
4893 while(avoid > 0 && order[0] == avoid)
4894 SortCustom1D(order,numWinSounds,"sort_shuffle");
4895
4896 // get them into a string
4897 for(i=0;i<numWinSounds;i++)
4898 {
4899 format(randomOrder,len,"%s%s%i",randomOrder,(i>0) ? "," : "",order[i]);
4900 set_localinfo("gg_winsound_order",randomOrder);
4901 }
4902}
4903
4904// shuffle an array
4905public sort_shuffle(elem1,elem2)
4906{
4907 return random_num(-1,1);
4908}
4909
4910// clear all saved values
4911clear_values(id,ignoreWelcome=0)
4912{
4913 level[id] = 0;
4914 levelsThisRound[id] = 0;
4915 score[id] = 0;
4916 lvlWeapon[id][0] = 0;
4917 star[id] = 0;
4918 if(!ignoreWelcome) welcomed[id] = 0;
4919 page[id] = 0;
4920 lastKilled[id] = 0;
4921 respawn_timeleft[id] = 0;
4922 silenced[id] = 0;
4923 spawnSounds[id] = 1;
4924 spawnProtected[id] = 0;
4925 teamTimes[id][0] = 0.0;
4926 teamTimes[id][1] = 0.0;
4927 lastSwitch[id] = get_gametime();
4928 lastTeam[id] = 0;
4929
4930 if(c4planter == id) c4planter = 0;
4931
4932 remove_task(TASK_RESPAWN+id);
4933 remove_task(TASK_CHECK_DEATHMATCH+id);
4934 remove_task(TASK_REMOVE_PROTECTION+id);
4935
4936 if(is_user_connected(id)) fm_set_rendering(id);
4937
4938 return 1;
4939}
4940
4941// clears a TEAM's values
4942clear_team_values(team)
4943{
4944 if(team != 1 && team != 2) return;
4945
4946 teamLevel[team] = 0;
4947 teamLvlWeapon[team][0] = 0;
4948 teamScore[team] = 0;
4949}
4950
4951// possibly start a warmup round
4952start_warmup()
4953{
4954 new warmup_value = get_pcvar_num(gg_warmup_timer_setting);
4955
4956 // warmup is set to -13 after its finished if gg_warmup_multi is 0,
4957 // so this stops multiple warmups for multiple map iterations
4958 if(warmup_value > 0 && warmup != -13)
4959 {
4960 warmup = warmup_value;
4961 get_pcvar_string(gg_warmup_weapon,warmupWeapon,23);
4962 set_task(0.1,"warmup_check",TASK_WARMUP_CHECK);
4963
4964 // now that warmup is in effect, reset player weapons
4965 new player;
4966 for(player=1;player<=maxPlayers;player++)
4967 {
4968 if(is_user_connected(player))
4969 {
4970 // just joined for all intents and purposes
4971 change_level(player,-MAX_WEAPONS,1,_,1,0,0); // just joined, always score, don't play sounds, don't effect team
4972 }
4973 }
4974
4975 // a single team update instead of for everyone
4976 if(get_pcvar_num(gg_teamplay))
4977 {
4978 teamplay_update_score(1,0);
4979 teamplay_update_score(2,0);
4980 teamplay_update_level(1,1);
4981 teamplay_update_level(2,1);
4982 }
4983
4984 // clear leader display for warmup
4985 if(warmup > 0) ClearSyncHud(0,hudSyncLDisplay);
4986 }
4987}
4988
4989// refresh a player's hegrenade stock
4990public refresh_nade(taskid)
4991{
4992 new id = taskid-TASK_REFRESH_NADE;
4993
4994 // player left, player died, or GunGame turned off
4995 if(!is_user_connected(id) || !is_user_alive(id) || !ggActive) return;
4996
4997 // on the grenade level, and lacking that aforementioned thing
4998 if(equal(lvlWeapon[id],HEGRENADE) && !user_has_weapon(id,CSW_HEGRENADE))
4999 ham_give_weapon(id,WEAPON_HEGRENADE);
5000
5001 // get bots to use the grenade (doesn't work very well)
5002 if(is_user_bot(id))
5003 {
5004 engclient_cmd(id,WEAPON_HEGRENADE);
5005 client_cmd(id,WEAPON_HEGRENADE);
5006 }
5007}
5008
5009// refill a player's ammo
5010stock refill_ammo(id,current=0)
5011{
5012 if(!is_user_alive(id)) return 0;
5013
5014 // weapon-specific warmup, no ammo for knives only
5015 if(warmup > 0 && warmupWeapon[0] && equal(warmupWeapon,KNIFE))
5016 return 0;
5017
5018 // get weapon name and index
5019 static fullName[24], curWpnName[24];
5020 new wpnid, curWpnMelee, curweapon = get_user_weapon(id);
5021
5022 // re-init start of strings
5023 fullName[0] = 0;
5024 curWpnName[0] = 0;
5025
5026 // we have a valid current weapon (stupid runtime errors)
5027 if(curweapon)
5028 {
5029 get_weaponname(curweapon,curWpnName,23);
5030 curWpnMelee = equal(curWpnName,WEAPON_KNIFE);
5031 }
5032
5033 // if we are refilling our current weapon instead of our level weapon,
5034 // we actually have a current weapon, and this isn't a melee weapon or the
5035 // other alternative, our level weapon, is a melee weapon
5036 if(current && curweapon && (!curWpnMelee || equal(lvlWeapon[id],KNIFE)))
5037 {
5038 // refill our current weapon
5039 get_weaponname(curweapon,fullName,23);
5040 wpnid = curweapon;
5041 }
5042 else
5043 {
5044 // refill our level weapon
5045 formatex(fullName,23,"weapon_%s",lvlWeapon[id]);
5046 wpnid = get_weaponid(fullName);
5047
5048 // so that we know for sure
5049 current = 0;
5050 }
5051
5052 new armor = get_pcvar_num(gg_give_armor), helmet = get_pcvar_num(gg_give_helmet);
5053
5054 // giving armor and helmets away like candy
5055 if(helmet) cs_set_user_armor(id,armor,CS_ARMOR_VESTHELM);
5056 else cs_set_user_armor(id,armor,CS_ARMOR_KEVLAR);
5057
5058 // didn't find anything valid to refill somehow
5059 if(wpnid < 1 || wpnid > 30 || !fullName[0])
5060 return 0;
5061
5062 // no reason to refill a melee weapon, or a bomb.
5063 // make use of our curWpnMelee cache here
5064 if((current && curWpnMelee) || wpnid == CSW_KNIFE || wpnid == CSW_C4)
5065 return 1;
5066
5067 new ammo, wEnt;
5068 ammo = get_pcvar_num(gg_ammo_amount);
5069
5070 // don't give away hundreds of grenades
5071 if(wpnid != CSW_HEGRENADE)
5072 {
5073 // set clip ammo
5074 wEnt = get_weapon_ent(id,wpnid);
5075 if(pev_valid(wEnt)) cs_set_weapon_ammo(wEnt,maxClip[wpnid]);
5076
5077 // glock on the nade level
5078 if(wpnid == CSW_GLOCK18 && equal(lvlWeapon[id],HEGRENADE))
5079 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5080 else
5081 {
5082 // set backpack ammo
5083 if(ammo > 0) cs_set_user_bpammo(id,wpnid,ammo);
5084 else cs_set_user_bpammo(id,wpnid,maxAmmo[wpnid]);
5085 }
5086
5087 // update display if we need to
5088 if(curweapon == wpnid)
5089 {
5090 message_begin(MSG_ONE,gmsgCurWeapon,_,id);
5091 write_byte(1);
5092 write_byte(wpnid);
5093 write_byte(maxClip[wpnid]);
5094 message_end();
5095 }
5096 }
5097
5098 // now do stupid grenade stuff
5099 else
5100 {
5101 // we don't have this nade yet
5102 if(!user_has_weapon(id,wpnid))
5103 {
5104 ham_give_weapon(id,fullName);
5105 remove_task(TASK_REFRESH_NADE+id);
5106 }
5107
5108 if(get_pcvar_num(gg_nade_glock))
5109 {
5110 // set clip ammo
5111 new wEnt = get_weapon_ent(id,CSW_GLOCK18);
5112 if(pev_valid(wEnt)) cs_set_weapon_ammo(wEnt,20);
5113
5114 // set backpack ammo
5115 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5116
5117 new curweapon = get_user_weapon(id);
5118
5119 // update display if we need to
5120 if(curweapon == CSW_GLOCK18)
5121 {
5122 message_begin(MSG_ONE,gmsgCurWeapon,_,id);
5123 write_byte(1);
5124 write_byte(CSW_GLOCK18);
5125 write_byte(20);
5126 message_end();
5127 }
5128 }
5129
5130 if(get_pcvar_num(gg_nade_smoke) && !cs_get_user_bpammo(id,CSW_SMOKEGRENADE))
5131 ham_give_weapon(id,"weapon_smokegrenade");
5132
5133 if(get_pcvar_num(gg_nade_flash) && !cs_get_user_bpammo(id,CSW_FLASHBANG))
5134 ham_give_weapon(id,"weapon_flashbang");
5135 }
5136
5137 // keep melee weapon out if we had it out
5138 if(curweapon && curWpnMelee)
5139 {
5140 engclient_cmd(id,curWpnName);
5141 client_cmd(id,curWpnName);
5142 }
5143
5144 return 1;
5145}
5146
5147// show someone a welcome message
5148public show_welcome(id)
5149{
5150 if(welcomed[id]) return;
5151
5152 new menuid, keys;
5153 get_user_menu(id,menuid,keys);
5154
5155 // another old-school menu opened
5156 if(menuid > 0)
5157 {
5158 // wait and try again
5159 set_task(3.0,"show_welcome",id);
5160 return;
5161 }
5162
5163 play_sound_by_cvar(id,gg_sound_welcome);
5164
5165 new len = formatex(menuText,511,"\y%L\w^n",id,"WELCOME_MESSAGE_LINE1",GG_VERSION);
5166 len += formatex(menuText[len],511-len,"\d---------------\w^n");
5167
5168 new special;
5169 if(get_pcvar_num(gg_knife_pro))
5170 {
5171 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE2");
5172 special = 1;
5173 }
5174 if(get_pcvar_num(gg_turbo))
5175 {
5176 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE3");
5177 special = 1;
5178 }
5179 if(get_pcvar_num(gg_knife_elite))
5180 {
5181 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE4");
5182 special = 1;
5183 }
5184 if(get_pcvar_num(gg_dm) || get_cvar_num("csdm_active"))
5185 {
5186 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE5");
5187 special = 1;
5188 }
5189 if(get_pcvar_num(gg_teamplay))
5190 {
5191 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE6");
5192 special = 1;
5193 }
5194
5195 if(special) len += formatex(menuText[len],511-len,"\d---------------\w^n");
5196 len += formatex(menuText[len],511-len,"%L^n",id,"WELCOME_MESSAGE_LINE7",weaponNum);
5197 len += formatex(menuText[len],511-len,"\d---------------\w^n");
5198 len += formatex(menuText[len],511-len,"%L",id,"WELCOME_MESSAGE_LINE8");
5199 len += formatex(menuText[len],511-len,"\d---------------\w^n");
5200 len += formatex(menuText[len],511-len,"%L",id,"PRESS_KEY_TO_CONTINUE");
5201
5202 show_menu(id,1023,menuText,-1,"welcome_menu");
5203}
5204
5205// show the required kills message
5206stock show_required_kills(id,always_individual=0)
5207{
5208 // weapon-specific warmup, who cares
5209 if(warmup > 0 && warmupWeapon[0]) return 0;
5210
5211 if(always_individual || !get_pcvar_num(gg_teamplay))
5212 return gungame_hudmessage(id,3.0,"%L: %i / %i",id,"REQUIRED_KILLS",score[id],get_level_goal(level[id],id));
5213
5214 new player, myTeam = _:cs_get_user_team(id), goal = get_level_goal(teamLevel[myTeam],id);
5215 for(player=1;player<=maxPlayers;player++)
5216 {
5217 if(player == id || (is_user_connected(player) && _:cs_get_user_team(player) == myTeam))
5218 gungame_hudmessage(player,3.0,"%L: %i / %i",player,"REQUIRED_KILLS",teamScore[myTeam],goal);
5219 }
5220
5221 return 1;
5222}
5223
5224// player killed himself
5225player_suicided(id)
5226{
5227 static name[32];
5228
5229 // we still have protection (round ended, new one hasn't started yet)
5230 // or, suicide level downs are disabled
5231 if(roundEnded || !get_pcvar_num(gg_suicide_penalty)) return 0;
5232
5233 // weapon-specific warmup, no one cares
5234 if(warmup > 0 && warmupWeapon[0]) return 0;
5235
5236 if(!get_pcvar_num(gg_teamplay))
5237 {
5238 get_user_name(id,name,31);
5239
5240 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"SUICIDE_LEVEL_DOWN",name);
5241
5242 // this is going to start a respawn counter HUD message
5243 if(get_pcvar_num(gg_dm) && (get_pcvar_num(gg_dm_countdown) & 2))
5244 return change_level(id,-1,_,0,1); // don't show message, always score
5245
5246 // show with message
5247 return change_level(id,-1,_,_,1); // always score
5248 }
5249 else
5250 {
5251 new team = _:cs_get_user_team(id);
5252 if(team != 1 && team != 2) return 0;
5253
5254 new penalty = get_level_goal(teamLevel[team],0);
5255 if(penalty > 0)
5256 {
5257 get_user_team(id,name,9);
5258
5259 if(teamScore[team] - penalty < 0)
5260 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"SUICIDE_LEVEL_DOWN_TEAM",name,(teamLevel[team] > 1) ? teamLevel[team]-1 : teamLevel[team]);
5261 else
5262 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"SUICIDE_SCORE_DOWN_TEAM",name,penalty);
5263
5264 return change_score(id,-penalty);
5265 }
5266 }
5267
5268 return 0;
5269}
5270
5271// player scored or lost a point
5272stock change_score(id,value,refill=1,play_sounds=1,effect_team=1,always_score=0)
5273{
5274 // don't bother scoring up on weapon-specific warmup
5275 if(warmup > 0 && warmupWeapon[0] && value > 0)
5276 return 0;
5277
5278 // can't score!
5279 if(!always_score && !can_score(id))
5280 return 0;
5281
5282 // already won, isn't important
5283 if(level[id] > weaponNum) return 0;
5284
5285 new oldScore = score[id], goal = get_level_goal(level[id],id);
5286
5287 new teamplay = get_pcvar_num(gg_teamplay), team;
5288 if(teamplay) team = _:cs_get_user_team(id);
5289
5290 // if this is going to level us
5291 if(score[id] + value >= goal)
5292 {
5293 new max_lvl = get_pcvar_num(gg_max_lvl);
5294
5295 // already reached max levels this round
5296 if(!teamplay && !get_pcvar_num(gg_turbo) && max_lvl > 0 && levelsThisRound[id] >= max_lvl)
5297 {
5298 // put it as high as we can without leveling
5299 score[id] = goal - 1;
5300 }
5301 else score[id] += value;
5302 }
5303 else score[id] += value;
5304
5305 // check for level up
5306 if(score[id] >= goal)
5307 {
5308 score[id] = 0;
5309
5310 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5311 teamplay_update_score(team,score[id],id,1); // direct
5312
5313 change_level(id,1,_,_,always_score,play_sounds);
5314 return 1;
5315 }
5316
5317 // check for level down
5318 if(score[id] < 0)
5319 {
5320 // can't go down below level 1
5321 if(level[id] <= 1)
5322 {
5323 score[id] = 0;
5324
5325 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5326 teamplay_update_score(team,score[id],id,1); // direct
5327
5328 new sdisplay = get_pcvar_num(gg_status_display);
5329 if(sdisplay == STATUS_KILLSLEFT || sdisplay == STATUS_KILLSDONE)
5330 status_display(id);
5331
5332 if(value < 0) show_required_kills(id);
5333 return 0;
5334 }
5335 else
5336 {
5337 goal = get_level_goal(level[id] > 1 ? level[id]-1 : 1,id);
5338
5339 score[id] = (oldScore + value) + goal; // carry over points
5340 if(score[id] < 0) score[id] = 0;
5341
5342 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5343 teamplay_update_score(team,score[id],id,1); // direct
5344
5345 change_level(id,-1,_,_,always_score);
5346 return -1;
5347 }
5348 }
5349
5350 // refresh menus
5351 new menu;
5352 get_user_menu(id,menu,dummy[0]);
5353 if(menu == level_menu) show_level_menu(id);
5354
5355 if(refill && get_pcvar_num(gg_refill_on_kill)) refill_ammo(id);
5356
5357 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5358 teamplay_update_score(team,score[id],id,1); // direct
5359
5360 if(value < 0) show_required_kills(id);
5361 else if(play_sounds) client_cmd(id,"spk ^"%s^"",KILL_DING_SOUND);
5362
5363 new sdisplay = get_pcvar_num(gg_status_display);
5364 if(sdisplay == STATUS_KILLSLEFT || sdisplay == STATUS_KILLSDONE)
5365 status_display(id);
5366
5367 return 0;
5368}
5369
5370// player gained or lost a level
5371stock change_level(id,value,just_joined=0,show_message=1,always_score=0,play_sounds=1,effect_team=1)
5372{
5373 // can't score
5374 if(level[id] > 0 && !always_score && !can_score(id))
5375 return 0;
5376
5377 // don't bother leveling up on weapon-specific warmup
5378 if(level[id] > 0 && warmup > 0 && warmupWeapon[0] && value > 0)
5379 return 0;
5380
5381 new oldLevel = level[id], oldValue = value;
5382
5383 new teamplay = get_pcvar_num(gg_teamplay), team;
5384 if(teamplay) team = _:cs_get_user_team(id);
5385
5386 // teamplay, on a valid team
5387 if(teamplay && (team == 1 || team == 2) && value != -MAX_WEAPONS) // ignore warmup reset
5388 {
5389 // not effecting team, but setting me to something that doesn't match team
5390 // OR
5391 // effecting team, and not even starting on same thing as team
5392 if((!effect_team && level[id] + value != teamLevel[team]) || (effect_team && level[id] != teamLevel[team]))
5393 {
5394 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",
5395 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]);
5396
5397 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",
5398 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]);
5399 }
5400 }
5401
5402 // this will put us below level 1
5403 if(level[id] + value < 1)
5404 {
5405 value = 1 - level[id]; // go down only to level 1
5406
5407 // bottom out the score
5408 score[id] = 0;
5409
5410 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5411 teamplay_update_score(team,score[id],id,1); // direct
5412 }
5413
5414 // going up
5415 if(value > 0)
5416 {
5417 new max_lvl = get_pcvar_num(gg_max_lvl);
5418
5419 // already reached max levels for this round
5420 if(!teamplay && !get_pcvar_num(gg_turbo) && max_lvl > 0 && levelsThisRound[id] >= max_lvl)
5421 return 0;
5422 }
5423
5424 // can't win on the warmup round
5425 if(level[id] + value > weaponNum && warmup > 0)
5426 {
5427 score[id] = get_level_goal(level[id],id) - 1;
5428
5429 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5430 teamplay_update_score(team,score[id],id,1); // direct
5431
5432 return 0;
5433 }
5434
5435 level[id] += value;
5436 if(!just_joined) levelsThisRound[id] += value;
5437
5438 silenced[id] = 0; // for going to Glock->USP, for example
5439
5440 // win???
5441 if(level[id] > weaponNum)
5442 {
5443 // already won, ignore this
5444 if(won) return 1;
5445
5446 // bot, and not allowed to win
5447 if(is_user_bot(id) && get_pcvar_num(gg_ignore_bots) == 2 && !only_bots())
5448 {
5449 change_level(id,-value,just_joined,_,1); // always score
5450 return 1;
5451 }
5452
5453 // cap out score
5454 score[id] = get_level_goal(level[id],id);
5455
5456 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5457 teamplay_update_score(team,score[id],id,1); // direct
5458
5459 if(teamplay && effect_team && (team == 1 || team == 2) && teamLevel[team] != level[id])
5460 teamplay_update_level(team,level[id],id,1); // direct
5461
5462 // crown the winner
5463 win(id,lastKilled[id]);
5464
5465 return 1;
5466 }
5467
5468 // set weapon based on it
5469 get_level_weapon(level[id],lvlWeapon[id],23);
5470
5471 // update the status display
5472 new sdisplay = get_pcvar_num(gg_status_display);
5473 if(sdisplay == STATUS_LEADERWPN) status_display(0); // to all
5474 else if(sdisplay) status_display(id); // only to me
5475
5476 new nade = equal(lvlWeapon[id],HEGRENADE);
5477
5478 // I'm a leader!
5479 if(warmup <= 0 && level[get_leader()] == level[id])
5480 {
5481 new sound_cvar;
5482 if(nade) sound_cvar = gg_sound_nade;
5483 else if(equal(lvlWeapon[id],KNIFE)) sound_cvar = gg_sound_knife;
5484
5485 if(sound_cvar)
5486 {
5487 // only play sound if we reached this level first
5488 if(num_players_on_level(level[id]) == 1) play_sound_by_cvar(0,sound_cvar);
5489 }
5490 }
5491
5492 // NOW play level up sounds, so that they potentially
5493 // override the global "Player is on X level" sounds
5494
5495 if(play_sounds)
5496 {
5497 // level up!
5498 if(oldValue >= 0) play_sound_by_cvar(id,gg_sound_levelup);
5499
5500 // level down :(
5501 else play_sound_by_cvar(id,gg_sound_leveldown);
5502 }
5503
5504 // remember to modify changes
5505 new oldTeamLevel;
5506 if(team == 1 || team == 2) oldTeamLevel = teamLevel[team];
5507
5508 if(teamplay && effect_team && (team == 1 || team == 2) && teamLevel[team] != level[id])
5509 teamplay_update_level(team,level[id],id);
5510
5511 // refresh menus
5512 new player, menu;
5513 for(player=1;player<=maxPlayers;player++)
5514 {
5515 if(!is_user_connected(player)) continue;
5516 get_user_menu(player,menu,dummy[0]);
5517
5518 if(menu == scores_menu) show_scores_menu(player);
5519 else if(menu == level_menu) show_level_menu(player);
5520 else if(player == id && menu == weapons_menu) show_weapons_menu(player);
5521 }
5522
5523 // make sure we don't have more than required now
5524 new goal = get_level_goal(level[id],id);
5525 if(score[id] >= goal)
5526 {
5527 score[id] = goal-1; // 1 under
5528
5529 if(teamplay && effect_team && (team == 1 || team == 2) && teamScore[team] != score[id])
5530 teamplay_update_score(team,score[id],id,1); // direct
5531 }
5532
5533 new turbo = get_pcvar_num(gg_turbo);
5534
5535 // give weapon right away?
5536 if((turbo || just_joined) && is_user_alive(id)) give_level_weapon(id);
5537 else show_progress_display(id); // still show display anyway
5538
5539 // update the leader display (cvar check done in that function)
5540 if(!just_joined)
5541 {
5542 remove_task(TASK_LEADER_DISPLAY);
5543 show_leader_display();
5544
5545 new Float:lead_sounds = get_pcvar_float(gg_lead_sounds);
5546 if(lead_sounds > 0.0 && (!teamplay || effect_team)) play_lead_sounds(id,oldLevel,lead_sounds);
5547 }
5548
5549 new vote_setting = get_pcvar_num(gg_vote_setting), map_iterations = get_pcvar_num(gg_map_iterations);
5550
5551 // the level to start a map vote on
5552 if(!voted && warmup <= 0 && vote_setting > 0
5553 && level[id] >= weaponNum - (vote_setting - 1)
5554 && mapIteration >= map_iterations && map_iterations > 0)
5555 {
5556 new mapCycleFile[64];
5557 get_gg_mapcycle_file(mapCycleFile,63);
5558
5559 // start map vote?
5560 if(!mapCycleFile[0] || !file_exists(mapCycleFile))
5561 {
5562 voted = 1;
5563
5564 // check for a custom vote
5565 new custom[256];
5566 get_pcvar_string(gg_vote_custom,custom,255);
5567
5568 if(custom[0]) server_cmd(custom);
5569 else start_mapvote();
5570 }
5571 }
5572
5573 // grab my name
5574 static name[32];
5575 if(!teamplay) get_user_name(id,name,31);
5576
5577 // only calculate position if we didn't just join
5578 if(!just_joined && show_message)
5579 {
5580 if(teamplay)
5581 {
5582 // is the first call for this level change
5583 if((team == 1 || team == 2) && teamLevel[team] != oldTeamLevel)
5584 {
5585 new leaderLevel, numLeaders, leader = teamplay_get_lead_team(leaderLevel,numLeaders);
5586
5587 // tied
5588 if(numLeaders > 1) gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TIED_LEADER_TEAM",leaderLevel,teamLvlWeapon[team]);
5589
5590 // leading
5591 else if(leader == team)
5592 {
5593 get_user_team(id,name,9);
5594 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"LEADING_ON_LEVEL_TEAM",name,leaderLevel,teamLvlWeapon[team]);
5595 }
5596
5597 // trailing
5598 else
5599 {
5600 get_user_team(id,name,9);
5601 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TRAILING_ON_LEVEL_TEAM",name,teamLevel[team],teamLvlWeapon[team]);
5602 }
5603 }
5604 }
5605 else
5606 {
5607 new leaderLevel, numLeaders, leader = get_leader(leaderLevel,numLeaders);
5608
5609 // tied
5610 if(level[id] == leaderLevel && numLeaders > 1 && level[id] > 1)
5611 {
5612 if(numLeaders == 2)
5613 {
5614 new otherLeader;
5615 if(leader != id) otherLeader = leader;
5616 else
5617 {
5618 new player;
5619 for(player=1;player<=maxPlayers;player++)
5620 {
5621 if(is_user_connected(player) && level[player] == leaderLevel && player != id)
5622 {
5623 otherLeader = player;
5624 break;
5625 }
5626 }
5627 }
5628
5629 static otherName[32];
5630 get_user_name(otherLeader,otherName,31);
5631
5632 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TIED_LEADER_ONE",name,leaderLevel,lvlWeapon[id],otherName);
5633 }
5634 else
5635 {
5636 static numWord[16], digiWord[3], lang[3];
5637 num_to_word(numLeaders-1,numWord,15);
5638 trim(numWord);
5639 formatex(digiWord,2,"%i",numLeaders-1);
5640
5641 new player;
5642 for(player=1;player<=maxPlayers;player++)
5643 {
5644 if(is_user_connected(player))
5645 {
5646 // use word for english, digit otherwise
5647 get_user_info(player,"lang",lang,2);
5648 gungame_print(player,id,1,"%L",player,"TIED_LEADER_MULTI",name,leaderLevel,lvlWeapon[id],equali(lang,"en") ? numWord : digiWord);
5649 }
5650 }
5651 }
5652 }
5653
5654 // I'M THE BEST!!!!!!!
5655 else if(leader == id && level[id] > 1)
5656 {
5657 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"LEADING_ON_LEVEL",name,level[id],lvlWeapon[id]);
5658 }
5659 }
5660 }
5661
5662 // teamplay, didn't grab name yet
5663 if(teamplay) get_user_name(id,name,31);
5664
5665 // triple bonus!
5666 if(levelsThisRound[id] == 3)
5667 {
5668 new triple_on = get_pcvar_num(gg_triple_on);
5669
5670 if(triple_on == 2 || (triple_on && !turbo))
5671 {
5672 star[id] = 1;
5673
5674 new sound[64];
5675 get_pcvar_string(gg_sound_triple,sound,63);
5676
5677 fm_set_user_maxspeed(id,fm_get_user_maxspeed(id)*1.5);
5678
5679 if(sound[0]) engfunc(EngFunc_EmitSound,id,CHAN_VOICE,sound[6],VOL_NORM,ATTN_NORM,0,PITCH_NORM); // ignore sound/ prefix
5680 else engfunc(EngFunc_EmitSound,id,CHAN_VOICE,sound,VOL_NORM,ATTN_NORM,0,PITCH_NORM);
5681
5682 set_pev(id,pev_effects,pev(id,pev_effects) | EF_BRIGHTLIGHT);
5683 fm_set_rendering(id,kRenderFxGlowShell,255,255,100,kRenderNormal,1);
5684 fm_set_user_godmode(id,1);
5685
5686 message_begin(MSG_BROADCAST,SVC_TEMPENTITY);
5687 write_byte(22); // TE_BEAMFOLLOW
5688 write_short(id); // entity
5689 write_short(trailSpr); // sprite
5690 write_byte(20); // life
5691 write_byte(10); // width
5692 write_byte(255); // r
5693 write_byte(255); // g
5694 write_byte(100); // b
5695 write_byte(100); // brightness
5696 message_end();
5697
5698 gungame_print(0,id,1,"%L",LANG_PLAYER_C,"TRIPLE_LEVELED",name);
5699 set_task(10.0,"end_star",TASK_END_STAR+id);
5700 }
5701 }
5702
5703 new ff_auto = get_pcvar_num(gg_ff_auto), ff = get_pcvar_num(mp_friendlyfire);
5704
5705 // turn on FF?
5706 if(ff_auto && !ff && nade)
5707 {
5708 server_cmd("mp_friendlyfire 1"); // so console is notified
5709 set_pcvar_num(mp_friendlyfire,1); // so it changes instantly
5710
5711 gungame_print(0,0,1,"%L",LANG_PLAYER_C,"FRIENDLYFIRE_ON");
5712
5713 client_cmd(0,"spk ^"%s^"",BRASS_BELL_SOUND);
5714 }
5715
5716 // turn off FF?
5717 else if(ff_auto && ff)
5718 {
5719 new keepFF, player;
5720
5721 for(player=1;player<=maxPlayers;player++)
5722 {
5723 if(equal(lvlWeapon[player],HEGRENADE) || equal(lvlWeapon[player],KNIFE))
5724 {
5725 keepFF = 1;
5726 break;
5727 }
5728 }
5729
5730 // no one is on nade or knife level anymore
5731 if(!keepFF)
5732 {
5733 server_cmd("mp_friendlyfire 0"); // so console is notified
5734 set_pcvar_num(mp_friendlyfire,0); // so it changes instantly
5735 }
5736 }
5737
5738 // some bots are actually allergic to the chemicals used in HE grenades
5739 if(is_user_bot(id) && get_pcvar_num(gg_bots_skipnade) && !get_pcvar_num(gg_teamplay) && equal(lvlWeapon[id],HEGRENADE))
5740 change_level(id,1);
5741
5742 return 1;
5743}
5744
5745// forces a player to a level, skipping a lot of important stuff.
5746// it's assumed that this is used as a result of "id" being leveled
5747// up because his teammate leveled up in teamplay.
5748stock set_level_noifandsorbuts(id,newLevel,play_sounds=1)
5749{
5750 // okay, this is our only but
5751 if(!is_user_connected(id)) return 0;
5752
5753 new oldLevel = level[id];
5754
5755 level[id] = newLevel;
5756 get_level_weapon(level[id],lvlWeapon[id],23);
5757
5758 if(play_sounds)
5759 {
5760 // level up!
5761 if(newLevel >= oldLevel) play_sound_by_cvar(id,gg_sound_levelup);
5762
5763 // level down :(
5764 else play_sound_by_cvar(id,gg_sound_leveldown);
5765 }
5766
5767 // refresh menus
5768 new player, menu;
5769 for(player=1;player<=maxPlayers;player++)
5770 {
5771 if(!is_user_connected(player)) continue;
5772 get_user_menu(player,menu,dummy[0]);
5773
5774 if(menu == scores_menu) show_scores_menu(player);
5775 else if(menu == level_menu) show_level_menu(player);
5776 }
5777
5778 // give weapon right away?
5779 if(get_pcvar_num(gg_turbo) && is_user_alive(id)) give_level_weapon(id);
5780 else show_progress_display(id); // still show display anyway
5781
5782 return 1;
5783}
5784
5785// get rid of a player's star
5786public end_star(taskid)
5787{
5788 new id = taskid - TASK_END_STAR;
5789 if(!star[id]) return;
5790
5791 star[id] = 0;
5792 //gungame_print(id,0,1,"Your star has run out!");
5793
5794 if(is_user_alive(id))
5795 {
5796 fm_set_user_maxspeed(id,fm_get_user_maxspeed(id)/1.5);
5797 engfunc(EngFunc_EmitSound,id,CHAN_VOICE,"common/null.wav",VOL_NORM,ATTN_NORM,0,PITCH_NORM); // stop sound
5798 set_pev(id,pev_effects,pev(id,pev_effects) & ~EF_BRIGHTLIGHT);
5799 fm_set_rendering(id);
5800 fm_set_user_godmode(id,0);
5801
5802 message_begin(MSG_BROADCAST,SVC_TEMPENTITY);
5803 write_byte(99); // TE_KILLBEAM
5804 write_short(id); // entity
5805 message_end();
5806 }
5807}
5808
5809// give a player a weapon based on his level
5810stock give_level_weapon(id,notify=1,verify=1)
5811{
5812 if(!is_user_alive(id) || level[id] <= 0) return 0;
5813
5814 // not warming up, didn't just win
5815 if(notify && warmup <= 0 && level[id] > 0 && level[id] <= weaponNum)
5816 show_progress_display(id);
5817
5818 // stop attacks from bleeding over into the new weapon
5819 //client_cmd(id,"-attack;-attack2");
5820
5821 // give CTs defuse kits on bomb maps
5822 if(bombMap && !get_pcvar_num(gg_block_objectives) && cs_get_user_team(id) == CS_TEAM_CT)
5823 cs_set_user_defuse(id,1);
5824
5825 new armor = get_pcvar_num(gg_give_armor), helmet = get_pcvar_num(gg_give_helmet);
5826
5827 // giving armor and helmets away like candy
5828 if(helmet) cs_set_user_armor(id,armor,CS_ARMOR_VESTHELM);
5829 else cs_set_user_armor(id,armor,CS_ARMOR_KEVLAR);
5830
5831 new oldWeapon = get_user_weapon(id);
5832
5833 static wpnName[24];
5834 new weapons = pev(id,pev_weapons), wpnid, alright, myCategory, hasMain;
5835
5836 new ammo = get_pcvar_num(gg_ammo_amount),
5837 knife_elite = get_pcvar_num(gg_knife_elite),
5838 pickup_others = (get_pcvar_num(gg_pickup_others) && (!knife_elite || !levelsThisRound[id])),
5839 mainCategory = get_weapon_category(_,lvlWeapon[id]);
5840
5841 new hasGlock, hasSmoke, hasFlash,
5842 nade_level = (equal(lvlWeapon[id],HEGRENADE)),
5843 nade_glock = get_pcvar_num(gg_nade_glock),
5844 nade_smoke = get_pcvar_num(gg_nade_smoke),
5845 nade_flash = get_pcvar_num(gg_nade_flash);
5846
5847 new melee_only = ((warmup > 0 && warmupWeapon[0] && equal(warmupWeapon,KNIFE)) || (knife_elite && levelsThisRound[id] > 0));
5848
5849 // remove stuff first
5850 for(wpnid=1;wpnid<31;wpnid++)
5851 {
5852 // don't have this, or it's the C4
5853 if(!(weapons & (1<<wpnid)) || wpnid == CSW_C4) continue;
5854
5855 alright = 0;
5856 get_weaponname(wpnid,wpnName,23);
5857
5858 if(melee_only)
5859 {
5860 if(wpnid == CSW_KNIFE)
5861 {
5862 alright = 1;
5863 hasMain = 1;
5864 }
5865 }
5866 else
5867 {
5868 replace(wpnName,23,"weapon_","");
5869
5870 // this is our designated weapon
5871 if(equal(lvlWeapon[id],wpnName))
5872 {
5873 alright = 1;
5874 hasMain = 1;
5875 }
5876
5877 // nade extras
5878 else if(nade_level)
5879 {
5880 if(nade_glock && wpnid == CSW_GLOCK18)
5881 {
5882 alright = 1;
5883 hasGlock = 1;
5884 }
5885 else if(nade_smoke && wpnid == CSW_SMOKEGRENADE)
5886 {
5887 alright = 1;
5888 hasSmoke = 1;
5889 }
5890 else if(nade_flash && wpnid == CSW_FLASHBANG)
5891 {
5892 alright = 1;
5893 hasFlash = 1;
5894 }
5895 }
5896
5897 // get the tag back on there
5898 format(wpnName,23,"weapon_%s",wpnName);
5899 }
5900
5901 // don't do anything about the knife
5902 if(wpnid != CSW_KNIFE)
5903 {
5904 // was it alright?
5905 if(alright)
5906 {
5907 // reset ammo
5908 if(wpnid != CSW_HEGRENADE && wpnid != CSW_FLASHBANG && wpnid != CSW_SMOKEGRENADE)
5909 {
5910 if(nade_level && nade_glock && wpnid == CSW_GLOCK18)
5911 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5912 else
5913 {
5914 if(ammo > 0) cs_set_user_bpammo(id,wpnid,ammo);
5915 else cs_set_user_bpammo(id,wpnid,maxAmmo[wpnid]);
5916 }
5917 }
5918 else cs_set_user_bpammo(id,wpnid,1); // grenades
5919 }
5920
5921 // we should probably remove this weapon
5922 else
5923 {
5924 myCategory = get_weapon_category(wpnid);
5925
5926 // pistol in the way of glock, remove it
5927 if(nade_level && nade_glock && myCategory == 2) ham_strip_weapon(id,wpnName);
5928 else
5929 {
5930 // we aren't allowed to have any other weapons,
5931 // or this is in the way of the weapon that I want.
5932 if(!pickup_others || myCategory == mainCategory)
5933 ham_strip_weapon(id,wpnName);
5934 }
5935 }/*not alright*/
5936 }/*not a knife*/
5937 }/*wpnid for-loop*/
5938
5939 // I should have a weapon but don't
5940 if(lvlWeapon[id][0] && !hasMain)
5941 {
5942 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
5943
5944 // give a player his weapon
5945 ham_give_weapon(id,wpnName);
5946
5947 remove_task(TASK_REFRESH_NADE+id);
5948
5949 if(!equal(lvlWeapon[id],HEGRENADE) && !equal(lvlWeapon[id],KNIFE))
5950 {
5951 wpnid = get_weaponid(wpnName);
5952
5953 if(!wpnid) log_amx("INVALID WEAPON ID FOR ^"%s^"",lvlWeapon[id]);
5954 else
5955 {
5956 if(ammo > 0) cs_set_user_bpammo(id,wpnid,ammo);
5957 else cs_set_user_bpammo(id,wpnid,maxAmmo[wpnid]);
5958 }
5959 }
5960 }
5961
5962 if(nade_level)
5963 {
5964 if(nade_glock && !hasGlock)
5965 {
5966 ham_give_weapon(id,WEAPON_GLOCK18);
5967 cs_set_user_bpammo(id,CSW_GLOCK18,50);
5968 }
5969 if(nade_smoke && !hasSmoke) ham_give_weapon(id,"weapon_smokegrenade");
5970 if(nade_flash && !hasFlash) ham_give_weapon(id,"weapon_flashbang");
5971 }
5972
5973 new weapon = get_user_weapon(id);
5974
5975 // using a knife probably
5976 if(melee_only || equal(lvlWeapon[id],KNIFE))
5977 {
5978 // draw knife on knife warmup and knife level... this is so that
5979 // the terrorist that spawns with the C4 won't be spawned with his
5980 // C4 selected, but instead his knife
5981 engclient_cmd(id,WEAPON_KNIFE);
5982 client_cmd(id,WEAPON_KNIFE);
5983 }
5984
5985 // switch back to knife if we had it out. also don't do this when called
5986 // by the verification check, because their old weapon will obviously be
5987 // a knife and they will want to use their new one.
5988 else if(verify /*&& !notify*/ && oldWeapon)
5989 {
5990 get_weaponname(oldWeapon,wpnName,23);
5991 if(wpnName[0] && equal(wpnName,WEAPON_KNIFE))
5992 {
5993 engclient_cmd(id,wpnName);
5994 client_cmd(id,wpnName);
5995 }
5996 else if(lvlWeapon[id][0])
5997 {
5998 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
5999 engclient_cmd(id,wpnName);
6000 client_cmd(id,wpnName);
6001 }
6002 }
6003
6004 // switch to glock for nade level
6005 else if(weapon != CSW_KNIFE && equal(lvlWeapon[id],HEGRENADE) && nade_glock)
6006 {
6007 engclient_cmd(id,WEAPON_GLOCK18);
6008 client_cmd(id,WEAPON_GLOCK18);
6009 }
6010
6011 // otherwise, switch to our new weapon
6012 else if(lvlWeapon[id][0])
6013 {
6014 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
6015 engclient_cmd(id,wpnName);
6016 client_cmd(id,wpnName);
6017 }
6018
6019 // make sure that we get this...
6020 if(verify)
6021 {
6022 remove_task(TASK_VERIFY_WEAPON+id);
6023 set_task(1.0,"verify_weapon",TASK_VERIFY_WEAPON+id);
6024 }
6025
6026 // remember burst or silenced status
6027 if(silenced[id])
6028 {
6029 if(equal(lvlWeapon[id],"usp") || equal(lvlWeapon[id],"m4a1"))
6030 {
6031 new wEnt = get_weapon_ent(id,_,lvlWeapon[id]);
6032 if(pev_valid(wEnt))
6033 {
6034 cs_set_weapon_silen(wEnt,1,0);
6035
6036 // play draw with silencer animation
6037 if(lvlWeapon[id][0] == 'u') set_pev(id,pev_weaponanim,USP_DRAWANIM);
6038 else set_pev(id,pev_weaponanim,M4A1_DRAWANIM);
6039 }
6040 }
6041 else if(equal(lvlWeapon[id],"glock18") || equal(lvlWeapon[id],"famas"))
6042 {
6043 new wEnt = get_weapon_ent(id,_,lvlWeapon[id]);
6044 if(pev_valid(wEnt)) cs_set_weapon_burst(wEnt,1);
6045 }
6046
6047 silenced[id] = 0;
6048 }
6049
6050 return 1;
6051}
6052
6053// verify that we have our stupid weapon
6054public verify_weapon(taskid)
6055{
6056 new id = taskid-TASK_VERIFY_WEAPON;
6057
6058 if(!is_user_alive(id)) return;
6059
6060 static wpnName[24];
6061 formatex(wpnName,23,"weapon_%s",lvlWeapon[id]);
6062 new wpnid = get_weaponid(wpnName);
6063
6064 if(!wpnid) return;
6065
6066 // we don't have it, but we want it
6067 if(!user_has_weapon(id,wpnid)) give_level_weapon(id,0,0);
6068}
6069
6070// crown a winner
6071win(winner,loser)
6072{
6073 // we have an invalid winner here
6074 if(won || !is_user_connected(winner) || !can_score(winner))
6075 return;
6076
6077 won = 1;
6078 roundEnded = 1;
6079
6080 server_cmd("sv_alltalk 1");
6081 client_cmd(0,"stopsound;speak null;mp3 stop");
6082 play_sound(0,winSounds[currentWinSound]);
6083
6084 new map_iterations = get_pcvar_num(gg_map_iterations), restart,
6085 player, Float:chattime = get_cvar_float("mp_chattime");
6086
6087 // final playthrough, get ready for next map
6088 if(mapIteration >= map_iterations && map_iterations > 0)
6089 {
6090 set_nextmap();
6091 set_task(floatmax(chattime,2.5),"goto_nextmap");
6092
6093 // as of GG1.16, we always send a non-emessage intermission, because
6094 // other map changing plugins (as well as StatsMe) intercepting it
6095 // was causing problems.
6096
6097 // as of GG1.20, we no longer do this because it closes the MOTD.
6098
6099 // as of GG2.10, we use finale, which freezes players like the
6100 // intermission but doesn't otherwise do any intermission stuff.
6101 message_begin(MSG_ALL,SVC_FINALE);
6102 write_string(""); // although you could put a nice typewrite-style centersay here
6103 message_end();
6104
6105 // godmode everyone
6106 new fullName[32];
6107 for(player=1;player<=maxPlayers;player++)
6108 {
6109 if(!is_user_alive(player)) continue;
6110
6111 // finale won't stop players from shooting technically
6112 formatex(fullName,31,"weapon_%s",lvlWeapon[player]);
6113 ham_strip_weapon(player,fullName);
6114
6115 fm_set_user_godmode(player,1);
6116 }
6117 }
6118
6119 // get ready to go again!!
6120 else
6121 {
6122 restart = 1;
6123
6124 // freeze and godmode everyone
6125 for(player=1;player<=maxPlayers;player++)
6126 {
6127 if(!is_user_connected(player)) continue;
6128
6129 client_cmd(player,"-attack;-attack2");
6130 set_pev(player,pev_flags,pev(player,pev_flags) | FL_FROZEN);
6131 fm_set_user_godmode(player,1);
6132 set_pev(player,pev_viewmodel2,"");
6133 }
6134 }
6135
6136 emessage_begin(MSG_ALL,gmsgHideWeapon);
6137 ewrite_byte((1<<0)|(1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<6)); // can't use (1<<2) or text disappears
6138 emessage_end();
6139
6140 emessage_begin(MSG_ALL,gmsgCrosshair);
6141 ewrite_byte(0); // hide
6142 emessage_end();
6143
6144 new winnerName[32], i, teamplay = get_pcvar_num(gg_teamplay);
6145 if(teamplay) get_user_team(winner,winnerName,9);
6146 else get_user_name(winner,winnerName,31);
6147
6148 // old-fashioned
6149 for(i=0;i<5;i++)
6150 {
6151 if(teamplay) gungame_print(0,winner,1,"%L!!",LANG_PLAYER_C,"WON_TEAM",winnerName);
6152 else gungame_print(0,winner,1,"%L!",LANG_PLAYER_C,"WON",winnerName);
6153 }
6154
6155 // our new super function
6156 stats_award_points(winner);
6157
6158 // finally show it off
6159 new winner_motd[64];
6160 get_pcvar_string(gg_winner_motd,winner_motd,63);
6161 if(winner_motd[0] && winner_motd[0] != '0')
6162 {
6163 new params[66];
6164 params[0] = winner;
6165 params[1] = loser;
6166 copy(params[2],63,winner_motd);
6167 set_task(1.0,"show_win_screen",_,params,65);
6168 }
6169
6170 // we can restart now (do it after calculations because points might get reset)
6171 if(restart)
6172 {
6173 // delay it, because it will reset stuff
6174 set_task(1.1,"restart_round",floatround(chattime-1.1));
6175
6176 set_task(floatmax(chattime-0.1,1.2),"restart_gungame",czero ? get_cvar_num("bot_stop") : 0);
6177 set_task(floatmax(chattime+5.0,0.1),"stop_win_sound",currentWinSound);
6178
6179 if(czero) server_cmd("bot_stop 1"); // freeze CZ bots
6180 }
6181}
6182
6183// restart gungame, for the next map iteration
6184public restart_gungame(old_bot_stop_value)
6185{
6186 won = 0;
6187 mapIteration++;
6188
6189 /*new i;
6190 for(i=0;i<sizeof teamLevel;i++)
6191 clear_team_values(i);*/
6192
6193 // game already commenced, but we are restarting, allow us to warmup again
6194 if(gameCommenced) shouldWarmup = 1;
6195
6196 stats_clear_all(); // clear out everything about our stats
6197
6198 toggle_gungame(TASK_TOGGLE_GUNGAME + TOGGLE_ENABLE); // reset stuff
6199
6200 // toggle_gungame calls map_start_cvars which calls do_rOrder, so we theoretically don't need to do it again here
6201 //do_rOrder(0); // also does random teamplay
6202
6203 setup_weapon_order();
6204 currentWinSound = do_rWinSound(); // pick the next win sound
6205
6206 // unfreeze and ungodmode everyone
6207 for(new player=1;player<=maxPlayers;player++)
6208 {
6209 if(!is_user_connected(player)) continue;
6210
6211 set_pev(player,pev_flags,pev(player,pev_flags) & ~FL_FROZEN);
6212 fm_set_user_godmode(player,0);
6213 welcomed[player] = 1; // also don't show welcome again
6214 }
6215 if(czero) server_cmd("bot_stop %i",old_bot_stop_value); // unfreeze CZ bots
6216
6217 // only have warmup once?
6218 if(!get_pcvar_num(gg_warmup_multi)) warmup = -13; // -13 is the magical stop number
6219 else warmup = -1; // -1 isn't magical at all... :(
6220
6221 warmupWeapon[0] = 0;
6222}
6223
6224// stop the winner sound (for multiple map iterations)
6225public stop_win_sound(winSound)
6226{
6227 // stop winning sound
6228 if(containi(winSounds[winSound],".mp3") != -1) client_cmd(0,"mp3 stop");
6229 else client_cmd(0,"speak null");
6230}
6231
6232// calculate the winner screen... severely cut down from before
6233public show_win_screen(params[66]) // [winner,loser,gg_win_motd[64]]
6234{
6235 new winner = params[0], loser = params[1];
6236 if(!is_user_connected(winner)) return 0;
6237
6238 new winner_motd[64];
6239 copy(winner_motd,63,params[2]);
6240
6241 new motd[1536], len, header[32];
6242
6243 new teamplay = get_pcvar_num(gg_teamplay), stats_mode = get_pcvar_num(gg_stats_mode), stats_split = get_pcvar_num(gg_stats_split),
6244 timeratio = get_pcvar_num(gg_teamplay_timeratio), iterations = get_pcvar_num(gg_map_iterations), roundsleft = iterations - mapIteration, nextmap[32],
6245 loserDC = !is_user_connected(loser);
6246
6247 get_cvar_string("amx_nextmap",nextmap,31);
6248
6249 new winnerTeam[10], winnerName[32], winnerColor[8], winnerWinSuffix[3], winnerStreakSuffix[3], streakChampColor[8], myStreakSuffix[3],
6250 winningTeam = _:cs_get_user_team(winner), losingTeam = _:(!(winningTeam-1)) + 1;
6251
6252#if defined SQL
6253 // abort if showing web page but could not initialize SQL
6254 if(!isdigit(winner_motd[0]) && !sqlInit)
6255 {
6256 return 0;
6257 }
6258
6259 // if web page or set to 2, update database
6260 if((!isdigit(winner_motd[0]) || winner_motd[0] == '2') && sqlInit)
6261 {
6262 new systime = get_systime(), player, playerAuthid[32], playerName[32], playerSafeAuthid[64], playerSafeName[64], si = get_gg_si(), flags, team,
6263 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);
6264
6265 for(player=1;player<=maxPlayers;player++)
6266 {
6267 if(!is_user_connected(player)) continue;
6268
6269 if(len >= 1200) // getting too close for comfort, cash in what we have and start a new query
6270 {
6271 motd[len] = ';';
6272 motd[len+1] = 0;
6273
6274 // thread this to prevent weird connection errors??? only solution I could find
6275 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",motd);
6276
6277 len = origLen = formatex(motd,1535,"INSERT INTO `%s` VALUES ",sqlPlayersTable);
6278 }
6279
6280 flags = 0;
6281 team = _:cs_get_user_team(player);
6282
6283 if(team == winningTeam)
6284 {
6285 flags |= WON;
6286 if(player == winner) flags |= LASTKILL;
6287 }
6288 else if(team > 0 && team < 3)
6289 {
6290 flags |= LOST;
6291 if(player == loser) flags |= LASTKILL;
6292 }
6293
6294 if(player == pointsExtraction[0][1]) flags |= NEWRECORD;
6295
6296 get_gg_authid(player,playerAuthid,31);
6297 get_user_name(player,playerName,31);
6298
6299 // get new stats position
6300 stats_clear_struct(playerStats[player]);
6301 stats_get_position(player,playerAuthid,si);
6302
6303 SQL_QuoteString(db,playerSafeName,63,playerName);
6304 SQL_QuoteString(db,playerSafeAuthid,63,playerAuthid);
6305
6306 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) ? "," : "",
6307 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);
6308 }
6309
6310 // if we actually added someone
6311 if(len > origLen)
6312 {
6313 motd[len] = ';';
6314 motd[len+1] = 0;
6315
6316 query = SQL_PrepareQuery(db,motd);
6317 SQL_ExecuteAndLog(query);
6318 SQL_FreeHandle(query);
6319 }
6320
6321 // an actual web page
6322 if(winner_motd[0] != '2')
6323 {
6324 new url[74], lang[3];
6325 for(player=1;player<=maxPlayers;player++)
6326 {
6327 if(!is_user_connected(player)) continue;
6328
6329 get_user_info(player,"lang",lang,2);
6330
6331 formatex(header,31,"%L",player,"WIN_MOTD_LINE1",winnerName);
6332 formatex(url,73,"%s?i=%i&l=%s",winner_motd,player,lang);
6333
6334 show_motd(player,url,header);
6335 }
6336
6337 return 1;
6338 }
6339 }
6340#endif
6341
6342 if(sqlInit && stats_mode && is_user_connected(pointsExtraction[0][1]))
6343 get_team_color(CsTeams:get_user_team(pointsExtraction[0][1]),streakChampColor,7);
6344
6345 get_user_team(winner,winnerTeam,9);
6346 get_user_name(winner,winnerName,31);
6347 get_team_color(CsTeams:winningTeam,winnerColor,7);
6348 get_number_suffix(pointsExtraction[winner][0],winnerWinSuffix,2);
6349 get_number_suffix(pointsExtraction[winner][3],winnerStreakSuffix,2);
6350
6351 // pointsExtraction[player][0] = total wins
6352 // pointsExtraction[player][1] = points from this round
6353 // pointsExtraction[player][2] = total points
6354 // pointsExtraction[player][3] = current streak
6355 // pointsExtraction[player][4] = previous record streak
6356
6357 // pointsExtraction[0][0] = old record (0 = no old record)
6358 // pointsExtraction[0][1] = record holder (-1 = old guy)
6359 // pointsExtraction[0][2] = new record (0 = no new record)
6360
6361 new loserName[32], loserColor[8];
6362 if(!loserDC)
6363 {
6364 get_user_name(loser,loserName,31);
6365 get_team_color(cs_get_user_team(loser),loserColor,7);
6366 }
6367 else loserColor = "gray";
6368
6369 // figure out which lines to use based on stats split stuff
6370 new key_LINE5A[20], key_LINE5B[20], key_LINE5C[20], key_LINE7A[20], key_LINE7B[20], key_LINE7C[20],
6371 key_STREAK1[21], key_STREAK2[21], key_STREAK3[21], key_STREAK4[21];
6372
6373 if(stats_split)
6374 {
6375 if(teamplay)
6376 {
6377 key_LINE5A = "WIN_MOTD_LINE5A_TP";
6378 key_LINE5B = "WIN_MOTD_LINE5B_TP";
6379 key_LINE5C = "WIN_MOTD_LINE5C_TP";
6380 key_LINE7A = "WIN_MOTD_LINE7A_TP";
6381 key_LINE7B = "WIN_MOTD_LINE7B_TP";
6382 key_LINE7C = "WIN_MOTD_LINE7C_TP";
6383 key_STREAK1 = "WIN_MOTD_STREAK1_TP";
6384 key_STREAK2 = "WIN_MOTD_STREAK2_TP";
6385 key_STREAK3 = "WIN_MOTD_STREAK3_TP";
6386 key_STREAK4 = "WIN_MOTD_STREAK4_TP";
6387 }
6388 else
6389 {
6390 key_LINE5A = "WIN_MOTD_LINE5A_REG";
6391 key_LINE5B = "WIN_MOTD_LINE5B_REG";
6392 key_LINE5C = "WIN_MOTD_LINE5C_REG";
6393 key_LINE7A = "WIN_MOTD_LINE7A_REG";
6394 key_LINE7B = "WIN_MOTD_LINE7B_REG";
6395 key_LINE7C = "WIN_MOTD_LINE7C_REG";
6396 key_STREAK1 = "WIN_MOTD_STREAK1_REG";
6397 key_STREAK2 = "WIN_MOTD_STREAK2_REG";
6398 key_STREAK3 = "WIN_MOTD_STREAK3_REG";
6399 key_STREAK4 = "WIN_MOTD_STREAK4_REG";
6400 }
6401 }
6402 else
6403 {
6404 key_LINE5A = "WIN_MOTD_LINE5A";
6405 key_LINE5B = "WIN_MOTD_LINE5B";
6406 key_LINE5C = "WIN_MOTD_LINE5C";
6407 key_LINE7A = "WIN_MOTD_LINE7A";
6408 key_LINE7B = "WIN_MOTD_LINE7B";
6409 key_LINE7C = "WIN_MOTD_LINE7C";
6410 key_STREAK1 = "WIN_MOTD_STREAK1";
6411 key_STREAK2 = "WIN_MOTD_STREAK2";
6412 key_STREAK3 = "WIN_MOTD_STREAK3";
6413 key_STREAK4 = "WIN_MOTD_STREAK4";
6414 }
6415
6416 // format for each language
6417 new player;
6418 for(player=1;player<=maxPlayers;player++)
6419 {
6420 if(!is_user_connected(player)) continue;
6421
6422 if(loserDC) formatex(loserName,31,"%L",player,"NO_ONE");
6423 formatex(header,31,"%L",player,"WIN_MOTD_LINE1",winnerName);
6424
6425 len = formatex(motd,1535,"<meta http-equiv=^"Content-Type^" content=^"text/html;charset=UTF-8^">");
6426 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>");
6427
6428 len += formatex(motd[len],1535-len,"<font color=%s size=6 style=letter-spacing:2px>",winnerColor);
6429 len += formatex(motd[len],1535-len,"<div style=height:1px;width:80%%;background-color:%s;overflow:hidden></div>",winnerColor);
6430 if(teamplay) len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE2",winnerTeam); else len += formatex(motd[len],1535-len,"%s",winnerName);
6431 len += formatex(motd[len],1535-len,"<div style=height:1px;width:80%%;background-color:%s;overflow:hidden></div>",winnerColor);
6432 len += formatex(motd[len],1535-len,"<font size=4 color=white style=letter-spacing:1px>%L<p>",player,"WIN_MOTD_LINE3");
6433
6434 if(!teamplay) len += formatex(motd[len],1535-len,"<font size=3>%L</font>.<p>",player,"WIN_MOTD_LINE4A",lvlWeapon[winner],loserColor,loserName);
6435 else len += formatex(motd[len],1535-len,"<font size=3>%L</font>.<p>",player,"WIN_MOTD_LINE4B",lvlWeapon[winner],loserColor,loserName,winnerColor,winnerName);
6436
6437 if(sqlInit && stats_mode == 1)
6438 {
6439 if(teamplay && timeratio)
6440 {
6441 // not enough for a win
6442 if(teamTimes[winner][winningTeam-1]/(teamTimes[winner][winningTeam-1]+teamTimes[winner][losingTeam-1]) < 0.5)
6443 len += formatex(motd[len],1535-len,"<p>");
6444 else
6445 len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5A,winnerColor,winnerName,pointsExtraction[winner][0],winnerWinSuffix,pointsExtraction[winner][3],winnerStreakSuffix,pointsExtraction[winner][4]);
6446
6447 // no play time
6448 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
6449 len += formatex(motd[len],1535-len,"%L<br>",player,"WIN_MOTD_LINE6",0);
6450 else
6451 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));
6452 }
6453 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]);
6454
6455 // we won somehow
6456 if( (!teamplay && winner == player) || (teamplay && !timeratio && winningTeam == _:cs_get_user_team(player)) ||
6457 (teamplay && timeratio && teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]) >= 0.5) )
6458 {
6459 get_number_suffix(pointsExtraction[player][3],myStreakSuffix,2);
6460 len += formatex(motd[len],1535-len,"%L<br>",player,key_LINE7A,pointsExtraction[player][0],pointsExtraction[player][3],myStreakSuffix,pointsExtraction[player][4]);
6461 }
6462
6463 // we didn't get a win
6464 else len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE7B,pointsExtraction[player][0]);
6465
6466 }
6467 else if(sqlInit && stats_mode == 2)
6468 {
6469 if(teamplay && timeratio)
6470 {
6471 // winner didn't play enough to get a win
6472 if(teamTimes[winner][winningTeam-1]/(teamTimes[winner][winningTeam-1]+teamTimes[winner][losingTeam-1]) < 0.5)
6473 len += formatex(motd[len],1535-len,"%L<p>",player,key_LINE5B,winnerColor,winnerName,pointsExtraction[winner][2]);
6474 else
6475 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]);
6476
6477 // no play time
6478 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
6479 len += formatex(motd[len],1535-len,"%L<br>",player,"WIN_MOTD_LINE6",0);
6480 else
6481 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));
6482 }
6483 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]);
6484
6485 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");
6486
6487 if(pointsExtraction[player][3])
6488 {
6489 get_number_suffix(pointsExtraction[player][3],myStreakSuffix,2);
6490 len += formatex(motd[len],1535-len,"%L<br>",player,key_STREAK1,pointsExtraction[player][3],myStreakSuffix,pointsExtraction[player][4]);
6491 }
6492 }
6493 else len += formatex(motd[len],1535-len,"<p>");
6494
6495 if(sqlInit && stats_mode)
6496 {
6497 if(!pointsExtraction[player][3] && pointsExtraction[player][4]) // I'm not on a streak, but I do have a record
6498 {
6499 len += formatex(motd[len],1535-len,"%L<br>",player,key_STREAK2,pointsExtraction[player][4]);
6500 }
6501
6502 if(pointsExtraction[0][1] == -1) // champion is the previous record holder
6503 {
6504 // there actually was a previous record
6505 if(pointsExtraction[0][0]) len += formatex(motd[len],1535-len,"%L<p>",player,key_STREAK3,pointsExtraction[0][0],spareName);
6506 }
6507 else len += formatex(motd[len],1535-len,"%L<p>",player,key_STREAK4,streakChampColor,spareName,pointsExtraction[0][2]);
6508 }
6509
6510 if(iterations > 0)
6511 {
6512 if(roundsleft <= 0) len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE8A",nextmap);
6513 else if(roundsleft == 1) len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE8B");
6514 else len += formatex(motd[len],1535-len,"%L",player,"WIN_MOTD_LINE8C",roundsleft);
6515 }
6516
6517 show_motd(player,motd,header);
6518 }
6519
6520 return 1;
6521}
6522
6523//**********************************************************************
6524// TEAMPLAY FUNCTIONS
6525//**********************************************************************
6526
6527// change the score of a team
6528stock teamplay_update_score(team,newScore,exclude=0,direct=0)
6529{
6530 if(team != 1 && team != 2) return;
6531
6532 teamScore[team] = newScore;
6533
6534 new player, sdisplay = get_pcvar_num(gg_status_display);
6535 for(player=1;player<=maxPlayers;player++)
6536 {
6537 if(is_user_connected(player) && player != exclude && _:cs_get_user_team(player) == team)
6538 {
6539 if(direct)
6540 {
6541 score[player] = newScore;
6542 if(sdisplay == STATUS_KILLSLEFT || sdisplay == STATUS_KILLSDONE)
6543 status_display(player);
6544 }
6545 else change_score(player,newScore-score[player],0); // don't refill
6546 }
6547 }
6548}
6549
6550// change the level of a team
6551stock teamplay_update_level(team,newLevel,exclude=0,direct=1)
6552{
6553 if(team != 1 && team != 2) return;
6554
6555 teamLevel[team] = newLevel;
6556 get_level_weapon(teamLevel[team],teamLvlWeapon[team],23);
6557
6558 new player;
6559 for(player=1;player<=maxPlayers;player++)
6560 {
6561 if(is_user_connected(player) && player != exclude && _:cs_get_user_team(player) == team)
6562 {
6563 //if(direct) level[player] = newLevel;
6564 if(direct) set_level_noifandsorbuts(player,newLevel);
6565 else change_level(player,newLevel-level[player],_,_,1); // always score
6566 }
6567 }
6568}
6569
6570// play the taken/tied/lost lead sounds
6571public teamplay_play_lead_sounds(id,oldLevel,Float:playDelay)
6572{
6573 // both teams not initialized yet
6574 if(!teamLevel[1] || !teamLevel[2]) return;
6575
6576 // id: the player whose level changed
6577 // oldLevel: his level before it changed
6578 // playDelay: how long to wait until we play id's sounds
6579
6580 // warmup or game over, no one cares
6581 if(warmup > 0 || won) return;
6582
6583 // no level change
6584 if(level[id] == oldLevel) return;
6585
6586 new team = _:cs_get_user_team(id), otherTeam = (team == 1) ? 2 : 1, thisTeam, player, params[2];
6587 if(team != 1 && team != 2) return;
6588
6589 new leaderLevel, numLeaders, leader = teamplay_get_lead_team(leaderLevel,numLeaders);
6590
6591 // this team is leading
6592 if(leader == team)
6593 {
6594 // the other team here?
6595 if(numLeaders > 1)
6596 {
6597 params[1] = gg_sound_tiedlead;
6598
6599 // play to both teams
6600 for(player=1;player<=maxPlayers;player++)
6601 {
6602 if(!is_user_connected(player)) continue;
6603
6604 thisTeam = _:cs_get_user_team(player);
6605 if(thisTeam == team || thisTeam == otherTeam)
6606 {
6607 params[0] = player;
6608 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
6609 set_task((thisTeam == team) ? playDelay : 0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
6610 }
6611 }
6612 }
6613
6614 // just us, we are the winners!
6615 else
6616 {
6617 // did we just pass the other team?
6618 if(level[id] > oldLevel && teamLevel[otherTeam] == oldLevel)
6619 {
6620 // play to both teams (conditional)
6621 for(player=1;player<=maxPlayers;player++)
6622 {
6623 if(!is_user_connected(player)) continue;
6624
6625 thisTeam = _:cs_get_user_team(player);
6626
6627 if(thisTeam == team) params[1] = gg_sound_takenlead;
6628 else if(thisTeam == otherTeam) params[1] = gg_sound_lostlead;
6629 else continue;
6630
6631 params[0] = player;
6632 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
6633 set_task((thisTeam == team) ? playDelay : 0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
6634 }
6635 }
6636 }
6637 }
6638
6639 // WAS this team on the leader level?
6640 else if(oldLevel == leaderLevel)
6641 {
6642 // play to entire team
6643 for(player=1;player<=maxPlayers;player++)
6644 {
6645 if(!is_user_connected(player)) continue;
6646
6647 thisTeam = _:cs_get_user_team(player);
6648
6649 if(thisTeam == team) params[1] = gg_sound_lostlead;
6650 else if(thisTeam == otherTeam) params[1] = gg_sound_takenlead;
6651 else continue;
6652
6653 params[0] = player;
6654 remove_task(TASK_PLAY_LEAD_SOUNDS+player);
6655 set_task((thisTeam == team) ? playDelay : 0.1,"play_sound_by_cvar_task",TASK_PLAY_LEAD_SOUNDS+player,params,2);
6656 }
6657 }
6658}
6659
6660// find the highest level team and such
6661stock teamplay_get_lead_team(&retLevel=0,&retNumLeaders=0,&retRunnerUp=0)
6662{
6663 new leader, numLeaders, runnerUp;
6664
6665 if(teamLevel[1] >= teamLevel[2]) leader = 1;
6666 else leader = 2;
6667
6668 if(teamLevel[1] == teamLevel[2]) numLeaders = 2;
6669 else
6670 {
6671 numLeaders = 1;
6672 runnerUp = (leader == 1) ? 2 : 1;
6673 }
6674
6675 retLevel = teamLevel[leader];
6676 retNumLeaders = numLeaders;
6677 retRunnerUp = runnerUp;
6678
6679 return leader;
6680}
6681
6682// gets the team's level goal without a player passed
6683teamplay_get_team_goal(team)
6684{
6685 if(team != 1 && team != 2) return 0;
6686
6687 new player;
6688 for(player=1;player<=maxPlayers;player++)
6689 {
6690 if(is_user_connected(player) && _:cs_get_user_team(player) == team)
6691 return get_level_goal(teamLevel[team],player);
6692 }
6693
6694 return 0;
6695}
6696
6697//**********************************************************************
6698// AUTOVOTE FUNCTIONS
6699//**********************************************************************
6700
6701// start the autovote
6702public autovote_start()
6703{
6704 // vote in progress
6705 if(autovotes[0] || autovotes[1] || autovotes[2] || task_exists(TASK_AUTOVOTE_RESULT)) return 0;
6706
6707 // if autovote_mode < 0, we haven't actually checked it yet
6708 if(autovote_mode < 0)
6709 {
6710 new info[6];
6711 get_localinfo("gg_av_iter",info,5);
6712 new iter = str_to_num(info);
6713
6714 new rotation[32];
6715 get_pcvar_string(gg_autovote_mode,rotation,31);
6716 new amount = str_count(rotation,',')+1;
6717
6718 if(iter <= 0 || iter > amount)
6719 {
6720 iter = 1;
6721 set_localinfo("gg_av_iter","1");
6722 }
6723
6724 // no rotation, just use the given value
6725 if(amount <= 1)
6726 {
6727 if(iter != 1) set_localinfo("gg_av_iter","1");
6728 autovote_mode = str_to_num(rotation);
6729 }
6730 else
6731 {
6732 for(new i=1;i<=amount;i++)
6733 {
6734 if(contain(rotation,",") != -1)
6735 {
6736 strtok(rotation,info,5,rotation,31,',');
6737 if(i == iter) // this is the one we're looking for
6738 {
6739 autovote_mode = str_to_num(info);
6740 break;
6741 }
6742 }
6743 else // we've stripped away everything else and are left with the last one, so use it
6744 {
6745 autovote_mode = str_to_num(rotation);
6746 break;
6747 }
6748 }
6749
6750 iter++;
6751 if(iter > amount) iter = 1;
6752 num_to_str(iter,info,5);
6753 set_localinfo("gg_av_iter",info);
6754 }
6755 }
6756
6757 // turns out it's disabled
6758 if(autovote_mode <= 0) return 0;
6759
6760 new Float:autovote_time = get_pcvar_float(gg_autovote_time);
6761
6762 new i;
6763 for(i=1;i<=maxPlayers;i++)
6764 {
6765 if(!is_user_connected(i)) continue;
6766
6767 switch(autovote_mode)
6768 {
6769 case 1:
6770 {
6771 formatex(menuText,511,"\y%L^n^n\w1. %L^n2. %L^n^n0. %L",i,"PLAY_GUNGAME",i,"YES",i,"NO",i,"CANCEL");
6772 show_menu(i,MENU_KEY_1|MENU_KEY_2|MENU_KEY_0,menuText,floatround(autovote_time),"autovote_menu");
6773 }
6774 case 2:
6775 {
6776 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");
6777 show_menu(i,MENU_KEY_1|MENU_KEY_2|MENU_KEY_0,menuText,floatround(autovote_time),"autovote_menu");
6778 }
6779 default:
6780 {
6781 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");
6782 show_menu(i,MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_0,menuText,floatround(autovote_time),"autovote_menu");
6783 }
6784 }
6785 }
6786
6787 gungame_print(0,0,1,"%L",LANG_PLAYER_C,"VOTING_STARTED");
6788 set_task(autovote_time,"autovote_result",TASK_AUTOVOTE_RESULT);
6789
6790 return 1;
6791}
6792
6793// take in votes
6794public autovote_menu_handler(id,key)
6795{
6796 switch(key)
6797 {
6798 case 0: // 1.
6799 {
6800 /* MODE 1- YES
6801 MODE 2- YES_TEAMPLAY
6802 MODE 3- YES_TEAMPLAY */
6803
6804 autovotes[0]++;
6805 }
6806 case 1: // 2.
6807 {
6808 /* MODE 1- NO
6809 MODE 2- YES_REGULAR
6810 MODE 3- YES_REGULAR */
6811
6812 if(autovote_mode == 1) autovotes[2]++;
6813 else autovotes[1]++;
6814 }
6815 case 2: // 3.
6816 {
6817 /* MODE 1-
6818 MODE 2-
6819 MODE 3- NO */
6820
6821 autovotes[2]++;
6822 }
6823 //case 9: 0. /* ALL MODES- CANCEL */ let menu close
6824 }
6825
6826 return PLUGIN_HANDLED;
6827}
6828
6829// calculate end of vote, some of this was thanks to VEN
6830public autovote_result()
6831{
6832 new vYes = autovotes[0] + autovotes[1], vNo = autovotes[2], vTotal = vYes + vNo, vSuccess, teamplay = get_pcvar_num(gg_teamplay), key[16];
6833
6834 switch(autovote_mode)
6835 {
6836 case 1: // this mode asks if they want to play GunGame, yes/no
6837 {
6838 if(vTotal)
6839 {
6840 if(float(vYes) / float(vTotal) >= get_pcvar_float(gg_autovote_ratio))
6841 vSuccess = 1;
6842
6843 // the choice that changes the current game mode is the one that needs to meet the ratio. so if you are
6844 // playing GunGame, at least however many people as defined by the ratio need to vote for it off to switch it,
6845 // and vice-versa.
6846 if( ( ggActive && (float(vNo) / float(vTotal)) < get_pcvar_float(gg_autovote_ratio))
6847 || (!ggActive && (float(vYes) / float(vTotal)) >= get_pcvar_float(gg_autovote_ratio)) )
6848 vSuccess = 1; // means that we will be playing GunGame
6849 }
6850 else if(ggActive) vSuccess = 1;
6851
6852 if(vSuccess && !ggActive)
6853 {
6854 restart_round(5);
6855 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
6856 }
6857 else if(!vSuccess && ggActive)
6858 {
6859 restart_round(5);
6860 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_DISABLE);
6861
6862 set_pcvar_num(gg_enabled,0);
6863 ggActive = 0;
6864 }
6865
6866 if(vSuccess && teamplay) key = "AUTOVOTE_RES1";
6867 else if(vSuccess && !teamplay) key = "AUTOVOTE_RES2";
6868 else key = "AUTOVOTE_RES3";
6869
6870 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);
6871 }
6872 case 2: // this mode asks if they want to play teamplay, yes/no
6873 {
6874 if(!ggActive)
6875 {
6876 restart_round(5);
6877 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
6878 }
6879
6880 if(vTotal)
6881 {
6882 // see above comment
6883 if( ( teamplay && (float(autovotes[1]) / float(vTotal)) < get_pcvar_float(gg_autovote_ratio))
6884 || (!teamplay && (float(autovotes[0]) / float(vTotal)) >= get_pcvar_float(gg_autovote_ratio)) )
6885 vSuccess = 1; // means that we will be playing teamplay mode
6886 }
6887 else if(teamplay) vSuccess = 1;
6888
6889 if(vSuccess)
6890 {
6891 if(!teamplay)
6892 {
6893 set_pcvar_num(gg_teamplay,1);
6894 if(ggActive && warmup <= 0) restart_round(3);
6895
6896 exec_gg_config_file(0,0);
6897 exec_gg_config_file(1,0);
6898 }
6899
6900 set_task(4.9,"force_teamplay",1);
6901 }
6902 else if(!vSuccess)
6903 {
6904 if(teamplay)
6905 {
6906 set_pcvar_num(gg_teamplay,0);
6907 if(ggActive && warmup <= 0) restart_round(3);
6908
6909 exec_gg_config_file(0,0);
6910 }
6911
6912 set_task(4.9,"force_teamplay",0);
6913 }
6914
6915 if(vSuccess) key = "AUTOVOTE_RES1";
6916 else key = "AUTOVOTE_RES2";
6917
6918 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);
6919 }
6920 default: // this mode asks if they want to play, teamplay/regular/no
6921 {
6922 if(vTotal)
6923 {
6924 // see above comment
6925 if( ( ggActive && (float(vNo) / float(vTotal)) < get_pcvar_float(gg_autovote_ratio))
6926 || (!ggActive && (float(vYes) / float(vTotal)) >= get_pcvar_float(gg_autovote_ratio)) )
6927 vSuccess = 1; // means that we will be playing GunGame
6928 }
6929 else if(ggActive) vSuccess = 1;
6930
6931 if(vSuccess)
6932 {
6933 if(!ggActive)
6934 {
6935 restart_round(5);
6936 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_ENABLE);
6937 }
6938
6939 // pick a random value for teamplay if we need it, then see if we should be using it.
6940 // use it in the case that we have a tie, and we are using random teamplay mode.
6941 new rand_val = random_num(0,1);
6942 new use_rand = (autovotes[0] == autovotes[1] && (teamplay == 2 || initTeamplayInt == 2));
6943
6944 if(autovotes[0] > autovotes[1] || (use_rand && rand_val == 1)) // more votes for teamplay
6945 {
6946 if(!teamplay)
6947 {
6948 set_pcvar_num(gg_teamplay,1);
6949 if(ggActive && warmup <= 0) restart_round(3);
6950
6951 exec_gg_config_file(0,0);
6952 exec_gg_config_file(1,0);
6953 }
6954
6955 key = "AUTOVOTE_RES1";
6956 set_task(4.9,"force_teamplay",1);
6957 }
6958 else if(autovotes[0] < autovotes[1] || (use_rand && rand_val == 0)) // more votes for regular
6959 {
6960 if(teamplay)
6961 {
6962 set_pcvar_num(gg_teamplay,0);
6963 if(ggActive && warmup <= 0) restart_round(3);
6964
6965 exec_gg_config_file(0,0);
6966 }
6967
6968 key = "AUTOVOTE_RES2";
6969 set_task(4.9,"force_teamplay",0);
6970 }
6971 else // if equal, leave it be
6972 {
6973 if(teamplay)
6974 {
6975 key = "AUTOVOTE_RES1";
6976 set_task(4.9,"force_teamplay",1);
6977 }
6978 else
6979 {
6980 key = "AUTOVOTE_RES2";
6981 set_task(4.9,"force_teamplay",0);
6982 }
6983 }
6984 }
6985 else if(!vSuccess && ggActive)
6986 {
6987 restart_round(5);
6988 set_task(4.8,"toggle_gungame",TASK_TOGGLE_GUNGAME+TOGGLE_DISABLE);
6989
6990 set_pcvar_num(gg_enabled,0);
6991 ggActive = 0;
6992 }
6993
6994 if(!vSuccess) key = "AUTOVOTE_RES3";
6995 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);
6996 }
6997 }
6998
6999 autovotes[0] = 0;
7000 autovotes[1] = 0;
7001 autovotes[2] = 0;
7002}
7003
7004// force teamplay mode to what we want after a vote has been completed.
7005// otherwise, when switching from GunGame off to on, it will be overwritten by gungame.cfg.
7006public force_teamplay(teamplay)
7007{
7008 set_pcvar_num(gg_teamplay,teamplay);
7009
7010 exec_gg_config_file(0,0);
7011 if(teamplay) exec_gg_config_file(1,0);
7012}
7013
7014//**********************************************************************
7015// STAT FUNCTIONS
7016//**********************************************************************
7017
7018// clear out everything about our stats
7019stats_clear_all()
7020{
7021#if !defined SQL
7022 // destroy large array
7023 if(statsArray) ArrayDestroy(statsArray);
7024
7025 // destroy the two "pointer" arrays
7026 if(statsPointers[0])
7027 {
7028 ArrayDestroy(statsPointers[0]);
7029 statsSize[0] = 0;
7030 }
7031 if(statsPointers[1])
7032 {
7033 ArrayDestroy(statsPointers[1]);
7034 statsSize[1] = 0;
7035 }
7036#endif
7037
7038 // clear out saved stats data for the players
7039 for(new i=1;i<=maxPlayers;i++)
7040 {
7041 statsPosition[i][0] = 0;
7042 statsPosition[i][1] = 0;
7043 stats_clear_struct(playerStats[i]);
7044 }
7045
7046 // clear out saved stats data from the temp saves
7047 for(new i=0;i<TEMP_SAVES;i++)
7048 {
7049 tempSave[i][svStatsPosition][0] = 0;
7050 tempSave[i][svStatsPosition][1] = 0;
7051 }
7052}
7053
7054#if !defined SQL
7055// converts a stats string (as in gungame.stats) into a stats struct (by reference)
7056stats_str_to_struct(str[],struct[statsData])
7057{
7058 // The format for str (as saved in gungame.stats):
7059 //
7060 // AUTHID WINS LAST_USED_NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
7061
7062 static piece[32], whole[112];
7063 piece[0] = 0;
7064 whole[0] = 0;
7065
7066 stats_clear_struct(struct);
7067
7068 strtok(str,piece,31,whole,111,'^t');
7069 copy(struct[sdAuthid],31,piece);
7070
7071 strtok(whole,piece,5,whole,111,'^t');
7072 struct[sdWins][0] = str_to_num(piece);
7073
7074 strtok(whole,piece,31,whole,111,'^t');
7075 copy(struct[sdName],31,piece);
7076
7077 strtok(whole,piece,11,whole,111,'^t');
7078 struct[sdTimestamp] = str_to_num(piece);
7079
7080 strtok(whole,piece,7,whole,111,'^t');
7081 struct[sdPoints][0] = str_to_num(piece);
7082
7083 strtok(whole,piece,3,whole,111,'^t');
7084 struct[sdStreak][0] = str_to_num(piece);
7085
7086 strtok(whole,piece,5,whole,111,'^t');
7087 struct[sdWins][1] = str_to_num(piece);
7088
7089 strtok(whole,piece,7,whole,3,'^t');
7090 struct[sdPoints][1] = str_to_num(piece);
7091
7092 //strtok(whole,piece,3,whole,1,'^t');
7093 struct[sdStreak][1] = str_to_num(whole);
7094}
7095#endif
7096
7097#if defined SQL
7098// sql version] much simpler.
7099stock stats_refresh_timestamp(authid[])
7100{
7101 if(!sqlInit) return 0;
7102
7103 SQL_QuoteString(db,safeName,63,authid);
7104 query = SQL_PrepareQuery(db,"UPDATE `%s` SET timestamp='%i' WHERE authid='%s' AND serverip='%s' LIMIT 1;",sqlTable,get_systime(),safeName,serverip);
7105
7106 SQL_ExecuteAndLog(query);
7107 SQL_FreeHandle(query);
7108
7109 return 1;
7110}
7111#else
7112// IT'S BEEN REVIVED!
7113//
7114// This took me way too long to develop, but it actually is able to overwrite the
7115// old timestamp without having to copy over to a new file or anything. It should
7116// work until sometime in the year 2287 when Unix timestamps will be 11 digits.
7117//
7118// On my old 2.80 gHz Pentium IV, it takes less than a tenth of a second to read to
7119// the bottom of a 10,000 line stats file and then refresh the final player's timestamp.
7120stock stats_refresh_timestamp(authid[])
7121{
7122 get_pcvar_string(gg_stats_file,sfFile,63);
7123 if(!file_exists(sfFile)) return 0;
7124
7125 // Open in binary mode, because text mode can present issues regarding
7126 // carriage returns and line feeds.
7127 // (See http://support.microsoft.com/default.aspx?scid=kb;en-us;68337)
7128 new file = fopen(sfFile,"rb+");
7129 if(!file) return 0;
7130
7131 new readLen, authidLen = strlen(authid), q;
7132 formatex(sfTimestamp,10,"%10i",get_systime());
7133
7134 while(!feof(file))
7135 {
7136 new start_of_line = ftell(file); // remember start of line position
7137
7138 readLen = fgets(file,sfLineData,111);
7139
7140 // check to see if this line starts with our authid
7141 for(q=0;q<authidLen;q++)
7142 {
7143 if(sfLineData[q] != authid[q]) { q = 255; break; }
7144 }
7145
7146 // if q == 255, we definitely did not have a match.
7147 // else, if sfLineData[q] != '^t', our authid is probably
7148 // a proper prefix for the authid on this line.
7149 if(q == 255 || sfLineData[q] != '^t')
7150 {
7151 q = 255;
7152 continue;
7153 }
7154
7155 q++; // q was pointing to first tab, so skip over it
7156 new tabsFound = 1;
7157 while(sfLineData[q] != 0)
7158 {
7159 // keep going until we find the 3rd tab overall
7160 if(sfLineData[q] == '^t' && ++tabsFound == 3) break;
7161 q++;
7162 }
7163
7164 // somehow we did not encounter the 3rd tab, abort
7165 if(tabsFound != 3 || sfLineData[q] != '^t')
7166 {
7167 log_amx("Error in stats_refresh_timestamp -- stats file formatted incorrectly");
7168 q = 255;
7169 break;
7170 }
7171
7172 q++; // q was the position of the tab before the timestamp, so skip over it
7173
7174 // Make sure we have room for the timestamp (though we might assume it).
7175 // Minimum 12 characters: 10 for the timestamp, 1 for carriage return, 1 for newline.
7176 if(readLen-q >= 12)
7177 {
7178 fseek(file,start_of_line+q,SEEK_SET);
7179 fwrite_blocks(file,sfTimestamp,10,BLOCK_CHAR); // overwrite timestamp with current one
7180 }
7181
7182 break;
7183 }
7184
7185 fclose(file);
7186
7187 return (q != 255);
7188}
7189#endif
7190
7191// we now have a super-duper function so that we only have to go through
7192// the stats file once, instead of rereading and rewriting it for every
7193// single player in a points match.
7194//
7195// also, timestamps are refreshed here, instead of every time you join.
7196public stats_award_points(winner)
7197{
7198 new stats_mode = get_pcvar_num(gg_stats_mode);
7199 if(!sqlInit || !stats_mode) return;
7200
7201 new teamplay = get_pcvar_num(gg_teamplay), winningTeam =_:cs_get_user_team(winner),
7202 losingTeam = _:(!(winningTeam-1)) + 1, stats_ip = get_pcvar_num(gg_stats_ip),
7203 timeratio = get_pcvar_num(gg_teamplay_timeratio), ignore_bots = get_pcvar_num(gg_ignore_bots);
7204
7205 new si = get_gg_si();
7206
7207 new player, playerWins[32], playerPoints[32], playerStreak[32], playerAuthid[32][32], playerName[32][32],
7208 playerTotalPoints[32], players[32], intStreak, playerNum, i, team, Float:time = get_gametime(), systime = get_systime();
7209
7210#if defined SQL
7211 new playerSafeName[32][64], playerSafeAuthid[32][64];
7212#endif
7213
7214 //new botid;
7215
7216 get_players(players,playerNum);
7217 for(i=0;i<playerNum;i++)
7218 {
7219 player = players[i];
7220
7221 // keep track of time
7222 team = _:cs_get_user_team(player);
7223 if(team == 1 || team == 2) teamTimes[player][team-1] += time - lastSwitch[player];
7224 lastSwitch[player] = time;
7225
7226 get_gg_authid(player,playerAuthid[i],31,stats_ip);
7227 get_user_name(player,playerName[i],31);
7228
7229 /*if(equal(playerAuthid[i],"BOT"))
7230 {
7231 botid++;
7232 formatex(playerAuthid[i],31,"BOT_%i",botid);
7233 }*/
7234
7235#if defined SQL
7236 SQL_QuoteString(db,playerSafeName[i],63,playerName[i]);
7237 SQL_QuoteString(db,playerSafeAuthid[i],63,playerAuthid[i]);
7238#endif
7239
7240 // solo wins only
7241 if(player == winner && stats_mode == 1 && !teamplay)
7242 playerWins[i] = 1;
7243 }
7244
7245 //
7246 // COLLECT LIST OF PLAYERS AND THEIR INFORMATION
7247 //
7248
7249 // points system
7250 if(stats_mode == 2)
7251 {
7252 new wins, Float:flPoints, Float:winbonus = get_pcvar_float(gg_stats_winbonus), Float:percent;
7253
7254 for(i=0;i<playerNum;i++)
7255 {
7256 player = players[i];
7257 if(!is_user_connected(player) || (ignore_bots && is_user_bot(player)))
7258 continue;
7259
7260 wins = 0; // WHY DID I FORGET THIS BEFORE
7261
7262 // point ratio based on time played in teamplay
7263 if(teamplay && timeratio)
7264 {
7265 // no play time
7266 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
7267 continue;
7268
7269 // give us points from losing team
7270 flPoints = (float(teamLevel[losingTeam]) - 1.0) * teamTimes[player][losingTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]);
7271
7272 // give us points from winning team
7273 percent = teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]);
7274 flPoints += (float(teamLevel[winningTeam]) - 1.0) * percent;
7275
7276 // we played over half on winning team, give us a bonus and win
7277 if(percent >= 0.5)
7278 {
7279 flPoints *= winbonus;
7280 wins = 1;
7281 }
7282 }
7283 else
7284 {
7285 flPoints = float(level[player]) - 1.0; // calculate points and add
7286
7287 // winner gets bonus points plus a win
7288 if(player == winner || (teamplay && _:cs_get_user_team(player) == winningTeam))
7289 {
7290 flPoints *= winbonus;
7291 wins = 1;
7292 }
7293 }
7294
7295 // unnecessary
7296 if(flPoints < 0.5 && !wins) continue;
7297
7298 playerWins[i] = wins;
7299 playerPoints[i] = floatround(flPoints);
7300 if(playerPoints[i] < 0) playerPoints[i] = 0;
7301 }
7302 }
7303
7304 // regular wins teamplay (solo regular wins is above)
7305 else if(teamplay)
7306 {
7307 for(i=0;i<playerNum;i++)
7308 {
7309 player = players[i];
7310 if(_:cs_get_user_team(player) == winningTeam && (!ignore_bots || !is_user_bot(player)))
7311 {
7312 // no play time
7313 if(teamTimes[player][winningTeam-1] < 1.0 && teamTimes[player][losingTeam-1] < 1.0)
7314 continue;
7315
7316 // have to play at least half to get a win
7317 if(!timeratio || teamTimes[player][winningTeam-1]/(teamTimes[player][winningTeam-1]+teamTimes[player][losingTeam-1]) >= 0.5)
7318 playerWins[i] = 1;
7319 }
7320 }
7321 }
7322
7323 //
7324 // START MANAGING STREAKS
7325 //
7326
7327 new streakToBeat = 0, streakChamp = -1;
7328
7329#if defined SQL
7330 new champRowExists;
7331
7332 // find current champion's info
7333 query = SQL_PrepareQuery(db,"SELECT streak,name FROM `%s` WHERE type='%iR' AND serverip='%s' LIMIT 1;",sqlStreakTable,si,serverip);
7334 if(SQL_ExecuteAndLog(query))
7335 {
7336 if(SQL_NumResults(query))
7337 {
7338 champRowExists = 1;
7339 streakToBeat = SQL_ReadResult(query,0);
7340 SQL_ReadResult(query,1,spareName,31);
7341 }
7342 }
7343 SQL_FreeHandle(query);
7344
7345 pointsExtraction[0][0] = streakToBeat; // record old record
7346 pointsExtraction[0][1] = -1; // record champion
7347
7348 new len, origLen;
7349
7350 // form query to delete players who did not win this round from the cache
7351 origLen = len = formatex(mkQuery,1535,"DELETE FROM `%s` WHERE type='%iC' AND serverip='%s' AND authid NOT IN (",sqlStreakTable,si,serverip);
7352 for(i=0;i<playerNum;i++)
7353 {
7354 if(playerWins[i]) len += formatex(mkQuery[len],1535-len,"%s'%s'",(len!=origLen) ? "," : "",playerSafeAuthid[i]);
7355 }
7356
7357 if(len == origLen)
7358 {
7359 // no one was added, then add empty apostrophes to avoid SQL syntax error
7360 len += formatex(mkQuery[len],1535-len,"''");
7361 }
7362
7363 len += formatex(mkQuery[len],1535-len,");");
7364
7365 query = SQL_PrepareQuery(db,mkQuery);
7366 SQL_ExecuteAndLog(query);
7367 SQL_FreeHandle(query);
7368
7369 // use one query to get all current streaks, then process them in the loop and save to playerStreak array
7370 new authid[64];
7371 query = SQL_PrepareQuery(db,"SELECT authid,streak FROM `%s` WHERE type='%iC' AND serverip='%s';",sqlStreakTable,si,serverip);
7372 if(SQL_ExecuteAndLog(query) && SQL_NumResults(query))
7373 {
7374 while(SQL_MoreResults(query))
7375 {
7376 SQL_ReadResult(query,0,authid,63);
7377
7378 for(i=0;i<playerNum;i++)
7379 {
7380 if(equal(authid,playerSafeAuthid[i]))
7381 {
7382 playerStreak[i] = SQL_ReadResult(query,1);
7383 break;
7384 }
7385 }
7386
7387 SQL_NextRow(query);
7388 }
7389 }
7390 SQL_FreeHandle(query);
7391
7392 len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` VALUES ",sqlStreakTable);
7393
7394 // now go through players, and make sure to start a streak for those who don't have one
7395 for(i=0;i<playerNum;i++)
7396 {
7397 if(playerWins[i])
7398 {
7399 playerStreak[i]++; // if they won, they get one added to their streak
7400
7401 // am I the champion?? also, in the case of a tie, allow the winner of this round to be considered the champ
7402 if(playerStreak[i] > streakToBeat || (streakChamp != -1 && playerStreak[i] == streakToBeat && players[i] == winner))
7403 {
7404 streakToBeat = playerStreak[i];
7405 streakChamp = i;
7406 }
7407
7408 // didn't find one with previous loop
7409 if(playerStreak[i] == 1) // because of increment above, will be 1 at minimum
7410 {
7411 // insert a new row. note that the streak is inserted as 0 because it gets incremented below.
7412 len += formatex(mkQuery[len],1535-len,"('%iC','%s','0','%s','%i','%s'),",si,playerSafeAuthid[i],playerSafeName[i],systime,serverip);
7413
7414 // cash in if we get too close
7415 if(len >= 1200)
7416 {
7417 mkQuery[len-1] = ';';
7418 mkQuery[len] = 0;
7419
7420 query = SQL_PrepareQuery(db,mkQuery);
7421 SQL_ExecuteAndLog(query);
7422 SQL_FreeHandle(query);
7423
7424 len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` VALUES ",sqlStreakTable);
7425 }
7426 }
7427 }
7428 }
7429
7430 // if we have some leftover query
7431 if(len > origLen)
7432 {
7433 mkQuery[len-1] = ';';
7434 mkQuery[len] = 0;
7435
7436 query = SQL_PrepareQuery(db,mkQuery);
7437 SQL_ExecuteAndLog(query);
7438 SQL_FreeHandle(query);
7439 }
7440
7441 // update everyone's current streaks by +1
7442 query = SQL_PrepareQuery(db,"UPDATE `%s` SET streak=streak+1 WHERE type='%iC' AND serverip='%s';",sqlStreakTable,si,serverip);
7443 SQL_ExecuteAndLog(query);
7444 SQL_FreeHandle(query);
7445
7446 // somebody beat the champion!
7447 if(streakToBeat && streakChamp != -1)
7448 {
7449 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);
7450 else query = SQL_PrepareQuery(db,"INSERT INTO `%s` VALUES ('%iR','%s','%i','%s',%i,'%s');",sqlStreakTable,si,playerSafeAuthid[streakChamp],streakToBeat,playerSafeName[streakChamp],systime,serverip);
7451
7452 SQL_ExecuteAndLog(query);
7453 SQL_FreeHandle(query);
7454 }
7455#else
7456 // go through and copy down the list of players who got a win last round
7457 new sfStreak[2][4], tempFileName[65], streakRecord[53], streakFile, stats_streak_file[64];
7458 get_pcvar_string(gg_stats_streak_file,stats_streak_file,63);
7459
7460 if(file_exists(stats_streak_file))
7461 {
7462 formatex(tempFileName,64,"%s2",stats_streak_file); // our temp file, append 2
7463 rename_file(stats_streak_file,tempFileName,1); // copy it over to the temp name (relative flag)
7464
7465 // there are two basic types of lines:
7466 // a record line, ie: 0R AUTHID STREAK NAME TIMESTAMP
7467 // a "cache" line (a player with a running streak), ie: 0C AUTHID STREAK
7468 // the first digit is the stats index (0 for regular, 1 for teamplay)
7469
7470 new streakTempFile = fopen(tempFileName,"rt");
7471 streakFile = fopen(stats_streak_file,"wt");
7472
7473 // go through the old file, copy over unimportant lines, figure out the record, and distribute streaks
7474 while(!feof(streakTempFile))
7475 {
7476 fgets(streakTempFile,sfLineData,82);
7477 if(!sfLineData[0]) continue;
7478
7479 // not for our stats mode
7480 if(str_to_num(sfLineData[0]) != si)
7481 {
7482 fprintf(streakFile,sfLineData); // just copy it over
7483 continue;
7484 }
7485
7486 // this is the record
7487 if(sfLineData[1] == 'R')
7488 {
7489 copy(streakRecord,82,sfLineData); // just save it for now
7490 continue;
7491 }
7492
7493 // this is a cache (a player who got a win last game)
7494 if(sfLineData[1] == 'C')
7495 {
7496 // use sfLineData[3] to skip characters "0C " (should be a tab)
7497 strtok(sfLineData[3],sfAuthid,31,sfStreak[0],3,'^t'); // split them up
7498
7499 for(i=0;i<playerNum;i++)
7500 {
7501 // if we won, and we haven't read our streak yet, and this is our authid
7502 if(playerWins[i] && !playerStreak[i] && equal(playerAuthid[i],sfAuthid))
7503 {
7504 playerStreak[i] = str_to_num(sfStreak[0]); // set our streak to our previous one
7505 break; // in the case of duplicate authids, don't continually add to the streak
7506 }
7507 }
7508
7509 continue;
7510 }
7511
7512 // don't copy over the rest, so it sort of auto-prunes
7513 //fprintf(streakFile,sfLineData); // OLD COMMENT IGNORE ME: anything else, we might as well copy it
7514 }
7515
7516 // get rid of the old copy
7517 fclose(streakTempFile);
7518 delete_file(tempFileName);
7519 }
7520 else if(stats_streak_file[0]) streakFile = fopen(stats_streak_file,"wt"); // did not exist, create it
7521
7522 //new streakToBeat = 0, streakChamp = -1;
7523 if(streakRecord[0]) // if there was a record, figure it out
7524 {
7525 strtok(streakRecord[3],dummy,1,sfLineData,82,'^t'); // cut off prefix and authid from the left
7526 strtok(sfLineData,sfStreak[0],3,spareName,31,'^t'); // get our streak, and the name as well
7527
7528 new pos = contain_char(spareName,'^t');
7529 if(pos != -1) spareName[pos] = 0; // cut off the timestamp from the name
7530
7531 streakToBeat = str_to_num(sfStreak[0]);
7532 }
7533
7534 pointsExtraction[0][0] = streakToBeat; // record old record
7535 pointsExtraction[0][1] = -1; // record champion
7536
7537 // now go through current players, and rewrite them with their new streaks into the file
7538 for(i=0;i<playerNum;i++)
7539 {
7540 if(playerWins[i])
7541 {
7542 playerStreak[i]++; // if they won, they get one added to their streak
7543
7544 // am I the champion?? also, in the case of a tie, allow the winner of this round to be considered the champ
7545 if(playerStreak[i] > streakToBeat || (streakChamp != -1 && playerStreak[i] == streakToBeat && players[i] == winner))
7546 {
7547 streakToBeat = playerStreak[i];
7548 streakChamp = i;
7549 }
7550
7551 if(streakFile)
7552 {
7553 fprintf(streakFile,"%iC^t%s^t%i",si,playerAuthid[i],playerStreak[i]);
7554 fputc(streakFile,'^n');
7555 }
7556 }
7557 }
7558
7559 if(streakFile)
7560 {
7561 if(streakToBeat)
7562 {
7563 if(streakChamp == -1) fprintf(streakFile,"%s",streakRecord); // no one here beat the champ, reprint old champ
7564 else // otherwise, give the new guy credit
7565 {
7566 fprintf(streakFile,"%iR^t%s^t%i^t%s^t%i",si,playerAuthid[streakChamp],playerStreak[streakChamp],playerName[streakChamp],systime);
7567 fputc(streakFile,'^n');
7568 }
7569 }
7570
7571 fclose(streakFile);
7572 }
7573#endif
7574
7575 // we have a new champion, record their stuff
7576 if(streakChamp != -1)
7577 {
7578 pointsExtraction[0][1] = players[streakChamp]; // record champion
7579 pointsExtraction[0][2] = playerStreak[streakChamp]; // record new record
7580 copy(spareName,31,playerName[streakChamp]);
7581 }
7582
7583 //
7584 // NOW GO THROUGH THE FILE
7585 //
7586
7587#if defined SQL
7588 new sfWins[2], sfPoints[2], sfStreak[2];
7589
7590 // do one query to grab all stats we need
7591 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);
7592 for(i=0;i<playerNum;i++)
7593 {
7594 len += formatex(mkQuery[len],1535-len,"'%s'%s",playerSafeAuthid[i],(i==playerNum-1) ? "" : ",");
7595 }
7596 len += formatex(mkQuery[len],1535-len,");");
7597
7598 // go through results
7599 new foundMe[33];
7600
7601 query = SQL_PrepareQuery(db,mkQuery);
7602 if(SQL_ExecuteAndLog(query) && SQL_NumResults(query))
7603 {
7604 new Handle:query2;
7605
7606 while(SQL_MoreResults(query))
7607 {
7608 SQL_ReadResult(query,0,authid,63);
7609
7610 for(i=0;i<playerNum;i++)
7611 {
7612 if(foundMe[i] || !equal(authid,playerSafeAuthid[i])) continue;
7613
7614 // remember for later iterations
7615 foundMe[i] = 1;
7616
7617 sfWins[0] = SQL_ReadResult(query,1);
7618 sfPoints[0] = SQL_ReadResult(query,2);
7619 sfStreak[0] = SQL_ReadResult(query,3);
7620 sfWins[1] = SQL_ReadResult(query,4);
7621 sfPoints[1] = SQL_ReadResult(query,5);
7622 sfStreak[1] = SQL_ReadResult(query,6);
7623
7624 // if we set a new personal streak record, save it
7625 intStreak = sfStreak[si];
7626 pointsExtraction[players[i]][4] = intStreak; // show old record
7627 if(playerStreak[i] > intStreak) intStreak = playerStreak[i];
7628
7629 // so we can reference this for the MOTD
7630 playerTotalPoints[i] = playerPoints[i] + sfPoints[si];
7631 pointsExtraction[players[i]][0] = sfWins[si] + playerWins[i];
7632 pointsExtraction[players[i]][1] = playerPoints[i];
7633 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7634 pointsExtraction[players[i]][3] = playerStreak[i];
7635 //pointsExtraction[players[i]][4] = intStreak; // uncomment to show new record
7636
7637 // something worth updating
7638 if(playerWins[i] || playerPoints[i])
7639 {
7640 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);
7641 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);
7642
7643 SQL_ExecuteAndLog(query2);
7644 SQL_FreeHandle(query2);
7645 }
7646
7647 break;
7648 }
7649
7650 SQL_NextRow(query);
7651 }
7652 }
7653 SQL_FreeHandle(query);
7654
7655 len = 0;
7656
7657 // go through again for people we didn't find
7658 for(i=0;i<playerNum;i++)
7659 {
7660 // nothing worth reporting, still refresh timestamp
7661 if(!playerWins[i] && !playerPoints[i])
7662 {
7663 stats_refresh_timestamp(playerAuthid[i]);
7664 continue;
7665 }
7666
7667 // already found me
7668 if(foundMe[i]) continue;
7669
7670 // so we can reference this for the MOTD
7671 playerTotalPoints[i] = playerPoints[i];
7672 pointsExtraction[players[i]][0] = playerWins[i];
7673 pointsExtraction[players[i]][1] = playerPoints[i];
7674 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7675 pointsExtraction[players[i]][3] = playerStreak[i];
7676 pointsExtraction[players[i]][4] = 0; // show old record
7677
7678 // haven't started yet
7679 if(len == 0)
7680 {
7681 if(si == 0) len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` (authid,name,timestamp,wins,points,streak,serverip) VALUES ",sqlTable);
7682 else len = origLen = formatex(mkQuery,1535,"INSERT INTO `%s` (authid,name,timestamp,wins_tp,points_tp,streak_tp,serverip) VALUES ",sqlTable);
7683 }
7684
7685 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);
7686
7687 // cash in if we get too close
7688 if(len >= 1200)
7689 {
7690 mkQuery[len-1] = ';';
7691 mkQuery[len] = 0;
7692
7693 query = SQL_PrepareQuery(db,mkQuery);
7694 SQL_ExecuteAndLog(query);
7695 SQL_FreeHandle(query);
7696
7697 len = 0;
7698 }
7699 }
7700
7701 // if we have some leftover query
7702 if(len && len > origLen)
7703 {
7704 mkQuery[len-1] = ';';
7705 mkQuery[len] = 0;
7706
7707 query = SQL_PrepareQuery(db,mkQuery);
7708 SQL_ExecuteAndLog(query);
7709 SQL_FreeHandle(query);
7710 }
7711#else
7712 new file;
7713 get_pcvar_string(gg_stats_file,sfFile,63);
7714 formatex(tempFileName,64,"%s2",sfFile); // our temp file, append 2
7715
7716 // create file if it does not exist (thanks Min2liz)
7717 if(!file_exists(sfFile))
7718 {
7719 file = fopen(sfFile,"wt");
7720 fclose(file);
7721 }
7722
7723 rename_file(sfFile,tempFileName,1); // copy over current stat file (relative flag)
7724 if(!file_exists(tempFileName)) return; // rename failed?
7725
7726 new sfWins[2][6], sfPoints[2][8], set[32], setNum;
7727
7728 new tempFile = fopen(tempFileName,"rt"), lastSetNum;
7729 file = fopen(sfFile,"wt");
7730
7731 // go through our old copy and rewrite entries
7732 while(tempFile && file && !feof(tempFile))
7733 {
7734 fgets(tempFile,sfLineData,111);
7735 if(!sfLineData[0]) continue;
7736
7737 // still have scores to add to
7738 lastSetNum = setNum;
7739 if(setNum < playerNum)
7740 {
7741 strtok(sfLineData,sfAuthid,31,sfLineData,111,'^t'); // isolate authid
7742
7743 // see if we need to change this one
7744 for(i=0;i<playerNum;i++)
7745 {
7746 if(!set[i] && equal(playerAuthid[i],sfAuthid))
7747 {
7748 set[i] = 1;
7749 setNum++;
7750
7751 strtok(sfLineData,sfWins[0],5,sfLineData,111,'^t'); // get wins
7752 strtok(sfLineData,dummy,1,sfLineData,111,'^t'); // get name
7753 strtok(sfLineData,dummy,1,sfLineData,111,'^t'); // get timestamp
7754 strtok(sfLineData,sfPoints[0],7,sfLineData,111,'^t'); // get points
7755 strtok(sfLineData,sfStreak[0],3,sfLineData,111,'^t'); // get streak
7756
7757 strtok(sfLineData,sfWins[1],5,sfLineData,111,'^t'); // get teamplay wins
7758 strtok(sfLineData,sfPoints[1],7,sfStreak[1],3,'^t'); // get teamplay points and teamplay streak
7759
7760 // if we set a new personal streak record, save it
7761 intStreak = str_to_num(sfStreak[si]);
7762 pointsExtraction[players[i]][4] = intStreak; // show old record
7763 if(playerStreak[i] > intStreak) intStreak = playerStreak[i];
7764
7765 playerTotalPoints[i] = playerPoints[i] + str_to_num(sfPoints[si]); // AUTHID WINS NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
7766
7767 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]));
7768 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);
7769
7770 fputc(file,'^n');
7771
7772 // so we can reference this for the MOTD
7773 pointsExtraction[players[i]][0] = str_to_num(sfWins[si])+playerWins[i];
7774 pointsExtraction[players[i]][1] = playerPoints[i];
7775 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7776 pointsExtraction[players[i]][3] = playerStreak[i];
7777 //pointsExtraction[players[i]][4] = intStreak; // uncomment to show new record
7778
7779 break;
7780 }
7781 }
7782
7783 // nothing to replace, just copy it over (newline is already included)
7784 if(lastSetNum == setNum) fprintf(file,"%s^t%s",sfAuthid,sfLineData); // we cut authid earlier
7785 }
7786 else fprintf(file,"%s",sfLineData); // nothing to replace, just copy it over (newline is already included)
7787 }
7788
7789 for(i=0;i<playerNum;i++)
7790 {
7791 // never found an existing entry, make a new one
7792 if(!set[i] && (playerWins[i] || playerPoints[i]))
7793 {
7794 playerTotalPoints[i] = playerPoints[i]; // AUTHID WINS NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
7795
7796 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]);
7797 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]);
7798
7799 fputc(file,'^n');
7800
7801 // so we can reference this for the MOTD
7802 pointsExtraction[players[i]][0] = playerWins[i];
7803 pointsExtraction[players[i]][1] = playerPoints[i];
7804 pointsExtraction[players[i]][2] = playerTotalPoints[i];
7805 pointsExtraction[players[i]][3] = playerStreak[i];
7806 pointsExtraction[players[i]][4] = 0; // show old record
7807 }
7808 }
7809
7810 if(tempFile) fclose(tempFile);
7811 if(file) fclose(file);
7812
7813 // remove our copy
7814 delete_file(tempFileName);
7815#endif
7816
7817 new teamName[10], key_GAINED_POINTS[18];
7818
7819 // decide what to use based on split and such
7820 if(get_pcvar_num(gg_stats_split))
7821 {
7822 if(teamplay) key_GAINED_POINTS = "GAINED_POINTS_TP";
7823 else key_GAINED_POINTS = "GAINED_POINTS_REG";
7824 }
7825 else key_GAINED_POINTS = "GAINED_POINTS";
7826
7827 for(i=0;i<playerNum;i++)
7828 {
7829 get_user_team(players[i],teamName,9);
7830
7831 //if(players[i] == winner || (teamplay && _:cs_get_user_team(players[i]) == winningTeam))
7832 if(playerWins[i]) log_message("^"%s<%i><%s><%s>^" triggered ^"Won_GunGame^"",playerName[i],get_user_userid(players[i]),playerAuthid[i],teamName);
7833
7834 if(stats_mode == 2 && playerPoints[i])
7835 {
7836 log_message("^"%s<%i><%s><%s>^" triggered ^"GunGame_Points^" amount ^"%i^"",playerName[i],get_user_userid(players[i]),playerAuthid[i],teamName,playerPoints[i]);
7837 gungame_print(players[i],0,1,"%L",players[i],key_GAINED_POINTS,playerPoints[i],playerTotalPoints[i],pointsExtraction[players[i]][0]);
7838 }
7839 }
7840}
7841
7842stock stats_clear_struct(struct[statsData],clearAuthid=1)
7843{
7844 if(clearAuthid) struct[sdAuthid][0] = 0;
7845 struct[sdWins][0] = 0;
7846 struct[sdName][0] = 0;
7847 struct[sdTimestamp] = 0;
7848 struct[sdPoints][0] = 0;
7849 struct[sdStreak][0] = 0;
7850 struct[sdWins][1] = 0;
7851 struct[sdPoints][1] = 0;
7852 struct[sdStreak][1] = 0;
7853}
7854
7855#if defined SQL
7856// sql version] get a player's last used name and wins from save file
7857stock stats_get_data(authid[],struct[statsData],id=0)
7858{
7859 if(!sqlInit || !get_pcvar_num(gg_stats_mode))
7860 {
7861 stats_clear_struct(struct);
7862 return 0;
7863 }
7864
7865 if(id > 0 && playerStats[id][sdAuthid][0]) // already saved my stats
7866 {
7867 struct = playerStats[id];
7868 return 1;
7869 }
7870
7871 stats_clear_struct(struct);
7872
7873 SQL_QuoteString(db,safeName,63,authid);
7874 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);
7875
7876 if(!SQL_ExecuteAndLog(query))
7877 {
7878 SQL_FreeHandle(query);
7879 return 0;
7880 }
7881
7882 copy(struct[sdAuthid],31,authid); // setting authid will cache it if this is playerStats
7883
7884 new found = SQL_NumResults(query);
7885 if(found)
7886 {
7887 struct[sdWins][0] = SQL_ReadResult(query,0);
7888 SQL_ReadResult(query,1,struct[sdName],31);
7889 struct[sdPoints][0] = SQL_ReadResult(query,2);
7890 struct[sdStreak][0] = SQL_ReadResult(query,3);
7891 struct[sdWins][1] = SQL_ReadResult(query,4);
7892 struct[sdPoints][1] = SQL_ReadResult(query,5);
7893 struct[sdStreak][1] = SQL_ReadResult(query,6);
7894 }
7895
7896 if(id > 0) playerStats[id] = struct; // if this is a connected player, save it
7897
7898 SQL_FreeHandle(query);
7899 return found;
7900}
7901#else
7902// get a player's last used name and wins from save file
7903stock stats_get_data(authid[],struct[statsData],id=0)
7904{
7905 if(!get_pcvar_num(gg_stats_mode))
7906 {
7907 stats_clear_struct(struct);
7908 return 0;
7909 }
7910
7911 get_pcvar_string(gg_stats_file,sfFile,63);
7912 if(!file_exists(sfFile))
7913 {
7914 stats_clear_struct(struct);
7915 return 0;
7916 }
7917
7918 if(id > 0)
7919 {
7920 if(playerStats[id][sdAuthid][0]) // already saved my stats
7921 {
7922 struct = playerStats[id];
7923 return 1;
7924 }
7925 else if(statsPosition[id][0] > 0) // check top regular players
7926 {
7927 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[0],statsPosition[id][0]-1),playerStats[id]);
7928 struct = playerStats[id];
7929 return 1;
7930 }
7931 else if(statsPosition[id][1] > 0) // check top teamplay players
7932 {
7933 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[1],statsPosition[id][1]-1),playerStats[id]);
7934 struct = playerStats[id];
7935 return 1;
7936 }
7937 }
7938
7939 stats_clear_struct(struct);
7940
7941 // now we have to go through the file
7942
7943 // open 'er up, boys!
7944 new found, file = fopen(sfFile,"rt");
7945 if(!file) return 0;
7946
7947 // go through it
7948 while(!feof(file))
7949 {
7950 fgets(file,sfLineData,111);
7951 strtok(sfLineData,sfAuthid,31,dummy,1,'^t'); // isolate authid
7952
7953 // this is it, stop now because our data is already stored in sfLineData
7954 if(equal(authid,sfAuthid))
7955 {
7956 found = 1;
7957 break;
7958 }
7959 }
7960
7961 // close 'er up, boys! (hmm....)
7962 fclose(file);
7963
7964 copy(struct[sdAuthid],31,authid); // setting authid will cache it if this is playerStats
7965
7966 // couldn't find
7967 if(found)
7968 {
7969 stats_str_to_struct(sfLineData,struct); // convert line from file to a struct
7970 }
7971
7972 if(id > 0) playerStats[id] = struct; // if this is a connected player, save it
7973
7974 return found;
7975}
7976#endif
7977
7978#if defined SQL
7979public stats_get_top_players()
7980{
7981 if(!ggActive || !sqlInit) return 0;
7982
7983 lastStatsMode = get_pcvar_num(gg_stats_mode);
7984 if(!lastStatsMode) return 0;
7985
7986 // assign stat position to players already in the game
7987 new i, authid[32], stats_ip = get_pcvar_num(gg_stats_ip);
7988 for(i=1;i<=maxPlayers;i++)
7989 {
7990 if(is_user_connected(i))
7991 {
7992 get_gg_authid(i,authid,31,stats_ip);
7993 if(!statsPosition[i][0]) stats_get_position(i,authid,0);
7994 if(!statsPosition[i][1]) stats_get_position(i,authid,1);
7995 }
7996 }
7997
7998 return 1;
7999}
8000#else
8001public stats_get_top_players()
8002{
8003 if(!ggActive || !sqlInit) return 0;
8004
8005 lastStatsMode = get_pcvar_num(gg_stats_mode);
8006 if(!lastStatsMode) return 0;
8007
8008 // we never made the array
8009 if(!statsArray)
8010 {
8011 get_pcvar_string(gg_stats_file,sfFile,63);
8012 if(!file_exists(sfFile)) return 0;
8013
8014 statsArray = ArrayCreate(statsData,100);
8015 statsPointers[0] = ArrayCreate(1,100);
8016 statsPointers[1] = ArrayCreate(1,100);
8017
8018 new file = fopen(sfFile,"rt");
8019 if(file)
8020 {
8021 new entryNum;
8022
8023 while(!feof(file))
8024 {
8025 fgets(file,sfLineData,111);
8026 if(!sfLineData[0]) continue;
8027 stats_str_to_struct(sfLineData,sfStatsStruct);
8028
8029 // put all of our stats entries, with all of the info and stuff, into our big stats array
8030 ArrayPushArray(statsArray,sfStatsStruct);
8031
8032 // and then put just their indexes into our "pointer" cell arrays
8033 if(sfStatsStruct[sdWins][0] || sfStatsStruct[sdPoints][0])
8034 {
8035 ArrayPushCell(statsPointers[0],entryNum);
8036 statsSize[0]++;
8037 }
8038 if(sfStatsStruct[sdWins][1] || sfStatsStruct[sdPoints][1])
8039 {
8040 ArrayPushCell(statsPointers[1],entryNum);
8041 statsSize[1]++;
8042 }
8043
8044 entryNum++;
8045 }
8046 fclose(file);
8047
8048 ArraySort(statsPointers[0],"stats_pointer_sort_reg");
8049 ArraySort(statsPointers[1],"stats_pointer_sort_tp");
8050
8051 if(statsSize[0] > MAX_STATS_RANK) statsSize[0] = MAX_STATS_RANK;
8052 if(statsSize[1] > MAX_STATS_RANK) statsSize[1] = MAX_STATS_RANK;
8053 }
8054 }
8055
8056 // if we were able to get anything put together
8057 if(statsSize[0] || statsSize[1])
8058 {
8059 // assign stat position to players already in the game
8060 new i, authid[32], stats_ip = get_pcvar_num(gg_stats_ip);
8061 for(i=1;i<=maxPlayers;i++)
8062 {
8063 if(is_user_connected(i))
8064 {
8065 get_gg_authid(i,authid,31,stats_ip);
8066 if(!statsPosition[i][0]) stats_get_position(i,authid,0); // these only work if statsSize[si] > 0
8067 if(!statsPosition[i][1]) stats_get_position(i,authid,1);
8068 }
8069 }
8070 }
8071
8072 return 1;
8073}
8074
8075// 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
8076public stats_pointer_sort_reg(Array:array,item1,item2)
8077{
8078 static score[2];
8079
8080 if(lastStatsMode == 1) // sort by wins
8081 {
8082 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8083 score[0] = sfStatsStruct[sdWins][0];
8084
8085 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8086 score[1] = sfStatsStruct[sdWins][0];
8087 }
8088 else // sort by points
8089 {
8090 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8091 score[0] = sfStatsStruct[sdPoints][0];
8092
8093 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8094 score[1] = sfStatsStruct[sdPoints][0];
8095 }
8096
8097 return score[1] - score[0];
8098}
8099
8100// 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
8101public stats_pointer_sort_tp(Array:array,item1,item2)
8102{
8103 static score[2];
8104
8105 if(lastStatsMode == 1) // sort by wins
8106 {
8107 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8108 score[0] = sfStatsStruct[sdWins][1];
8109
8110 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8111 score[1] = sfStatsStruct[sdWins][1];
8112 }
8113 else // sort by points
8114 {
8115 ArrayGetArray(statsArray,ArrayGetCell(array,item1),sfStatsStruct);
8116 score[0] = sfStatsStruct[sdPoints][1];
8117
8118 ArrayGetArray(statsArray,ArrayGetCell(array,item2),sfStatsStruct);
8119 score[1] = sfStatsStruct[sdPoints][1];
8120 }
8121
8122 return score[1] - score[0];
8123}
8124#endif
8125
8126// gather up our players
8127/*public stats_get_top_players()
8128{
8129 if(!ggActive) return 0;
8130
8131 glStatsMode = get_pcvar_num(gg_stats_mode);
8132 if(!glStatsMode) return 0;
8133
8134 get_pcvar_string(gg_stats_file,sfFile,63);
8135 if(!file_exists(sfFile)) return 0;
8136
8137 new buildMe[2], stats_split = get_pcvar_num(gg_stats_split);
8138
8139 // figure out if we need to build any of the arrays still
8140 if(!statsArray[0])
8141 {
8142 statsArray[0] = ArrayCreate(statsData,100);
8143 statsSize[0] = 0;
8144 buildMe[0] = 1;
8145 }
8146 if(!statsArray[1] && stats_split)
8147 {
8148 statsArray[1] = ArrayCreate(statsData,100);
8149 statsSize[1] = 0;
8150 buildMe[1] = 1;
8151 }
8152
8153 // if we do have an array or two to build
8154 if(buildMe[0] || buildMe[1])
8155 {
8156 // storage format:
8157 // AUTHID WINS LAST_USED_NAME TIMESTAMP POINTS STREAK TPWINS TPPOINTS TPSTREAK
8158
8159 new file = fopen(sfFile,"rt");
8160 if(file)
8161 {
8162 while(!feof(file))
8163 {
8164 fgets(file,sfLineData,111);
8165 if(!sfLineData[0]) continue;
8166 stats_str_to_struct(sfLineData,sfStatsStruct);
8167
8168 // if we are working on this array, and we have some wins or points for it
8169 if(buildMe[0] && (sfStatsStruct[sdWins][0] || sfStatsStruct[sdPoints][0]))
8170 {
8171 ArrayPushArray(statsArray[0],sfStatsStruct);
8172 statsSize[0]++;
8173 }
8174 if(buildMe[1] && (sfStatsStruct[sdWins][1] || sfStatsStruct[sdPoints][1]))
8175 {
8176 ArrayPushArray(statsArray[1],sfStatsStruct);
8177 statsSize[1]++;
8178 }
8179 }
8180 fclose(file);
8181
8182 if(buildMe[0] && statsSize[0] > 1) // if we were working on this array and we have something to sort
8183 {
8184 ArraySort(statsArray[0],"stats_custom_compare_regular");
8185 if(statsSize[0] > 1000) statsSize[0] = 1000; // arbitrarily limit because we have to look through this every time someone connects
8186 }
8187 if(buildMe[1] && statsSize[1] > 1)
8188 {
8189 ArraySort(statsArray[1],"stats_custom_compare_teamplay");
8190 if(statsSize[1] > 1000) statsSize[1] = 1000;
8191 }
8192 }
8193
8194 // clear any saved stats positions out of the tempsaves, so no one rejoins with the wrong position
8195 for(new i=0;i<TEMP_SAVES;i++)
8196 {
8197 tempSave[i][34] = 0;
8198 tempSave[i][35] = 0;
8199 }
8200 }
8201
8202 // if we were able to get anything put together
8203 if(statsSize[0] || statsSize[1])
8204 {
8205 // assign stat position to players already in the game
8206 new i, authid[32], stats_ip = get_pcvar_num(gg_stats_ip);
8207 for(i=1;i<=maxPlayers;i++)
8208 {
8209 if(is_user_connected(i))
8210 {
8211 get_gg_authid(i,authid,31,stats_ip);
8212 if(!statsPosition[i][0]) stats_get_position(i,authid,0); // these will only do anything if we created the respective array above
8213 if(!statsPosition[i][1]) stats_get_position(i,authid,1);
8214 }
8215 }
8216 }
8217
8218 lastStatsMode = glStatsMode;
8219 statsLastSplit = stats_split;
8220
8221 return 1;
8222}*/
8223
8224// our custom sorting functions
8225/*public stats_custom_compare_regular(Array:array,item1,item2)
8226{
8227 static score[2];
8228
8229 ArrayGetArray(array,item1,sfStatsStruct);
8230 if(glStatsMode == 1) score[0] = sfStatsStruct[sdWins][0]; // sort by wins
8231 else score[0] = sfStatsStruct[sdPoints][0]; // sort by wins
8232
8233 ArrayGetArray(array,item2,sfStatsStruct);
8234 if(glStatsMode == 1) score[1] = sfStatsStruct[sdWins][0]; // sort by wins
8235 else score[1] = sfStatsStruct[sdPoints][0]; // sort by wins
8236
8237 return score[1] - score[0];
8238}*/
8239
8240/*public stats_custom_compare_teamplay(Array:array,item1,item2)
8241{
8242 static score[2];
8243
8244 ArrayGetArray(array,item1,sfStatsStruct);
8245 if(glStatsMode == 1) score[0] = sfStatsStruct[sdWins][1]; // sort by wins
8246 else score[0] = sfStatsStruct[sdPoints][1]; // sort by wins
8247
8248 ArrayGetArray(array,item2,sfStatsStruct);
8249 if(glStatsMode == 1) score[1] = sfStatsStruct[sdWins][1]; // sort by wins
8250 else score[1] = sfStatsStruct[sdPoints][1]; // sort by wins
8251
8252 return score[1] - score[0];
8253}*/
8254
8255#if defined SQL
8256// get a player's overall position
8257stats_get_position(id,authid[],si)
8258{
8259 if(!sqlInit) return 0; // can't get position yet
8260
8261 statsPosition[id][si] = -1; // mark that we've at least attempted to find it
8262
8263 new stats_mode = get_pcvar_num(gg_stats_mode), myPoints;
8264
8265 // first, we need to get their current amount of wins/points. this allows us to make sure that they are really in the database
8266 // (could cause problems with a sub-query returning 0 instead of NULL) and that they actually have wins/points for the current
8267 // stats mode (could cause problems when they are in the database but you are looking at their stats index for which they have
8268 // no points). then, we might as well use the result for our ranking query instead of using a sub-query.
8269
8270 // 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
8271 // anyway, we might as well just get all of the stats at once.
8272 stats_get_data(authid,playerStats[id],id); // stats_get_data should automatically manage cache
8273
8274 if(stats_mode == 2) myPoints = playerStats[id][sdPoints][si];
8275 else myPoints = playerStats[id][sdWins][si];
8276
8277 if(myPoints <= 0 && (stats_mode != 2 || playerStats[id][sdWins][si] <= 0)) return -1;
8278
8279 new winsColumn[8], pointsColumn[10];
8280
8281 if(si == 0)
8282 {
8283 winsColumn = "wins";
8284 pointsColumn = "points";
8285 }
8286 else
8287 {
8288 winsColumn = "wins_tp";
8289 pointsColumn = "points_tp";
8290 }
8291
8292 // select the number of people who have a score higher than I do, plus one. query from Giuseppe Maxia
8293 if(stats_mode == 2)
8294 {
8295 query = SQL_PrepareQuery(db,"SELECT COUNT(*)+1 AS ranking FROM `%s` WHERE %s > %i AND serverip='%s' AND (%s > 0 OR %s > 0);",
8296 sqlTable,pointsColumn,myPoints,serverip,winsColumn,pointsColumn);
8297 }
8298 else
8299 {
8300 query = SQL_PrepareQuery(db,"SELECT COUNT(*)+1 AS ranking FROM `%s` WHERE %s > %i AND serverip='%s';",
8301 sqlTable,winsColumn,myPoints,serverip);
8302 }
8303
8304 if(SQL_ExecuteAndLog(query))
8305 {
8306 new result = SQL_ReadResult(query,0);
8307 SQL_FreeHandle(query);
8308
8309 statsPosition[id][si] = result;
8310 return result;
8311 }
8312
8313 SQL_FreeHandle(query);
8314 return -1;
8315}
8316#else
8317// get a player's overall position
8318stats_get_position(id,authid[],si)
8319{
8320 if(statsArray && statsPointers[si] && statsSize[si])
8321 {
8322 statsPosition[id][si] = -1; // mark that we've at least attempted to find it
8323
8324 for(new i=0;i<statsSize[si];i++)
8325 {
8326 ArrayGetArray(statsArray,ArrayGetCell(statsPointers[si],i),sfStatsStruct);
8327 if(equal(authid,sfStatsStruct[sdAuthid]))
8328 {
8329 statsPosition[id][si] = i+1;
8330 return i+1;
8331 }
8332 }
8333
8334 return -1;
8335 }
8336
8337 return 0;
8338}
8339#endif
8340
8341#if defined SQL
8342// prune old entries
8343stock stats_prune(max_time=-1)
8344{
8345 if(!sqlInit) return 0;
8346
8347 if(max_time == -1) max_time = get_pcvar_num(gg_stats_prune); // -1 = use value from cvar
8348 if(max_time == 0) return 0; // 0 = no pruning
8349
8350 // prune old entries
8351 formatex(mkQuery,255,"DELETE FROM `%s` WHERE timestamp < %i AND serverip='%s';",sqlTable,get_systime() - max_time,serverip);
8352 SQL_ThreadQuery(tuple,"sql_qhandler_prune",mkQuery);
8353
8354 // fix up all of our tables
8355 formatex(mkQuery,255,"REPAIR TABLE `%s`;",sqlTable);
8356 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8357
8358 formatex(mkQuery,255,"OPTIMIZE TABLE `%s`;",sqlTable);
8359 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8360
8361 formatex(mkQuery,255,"REPAIR TABLE `%s`;",sqlStreakTable);
8362 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8363
8364 formatex(mkQuery,255,"OPTIMIZE TABLE `%s`;",sqlStreakTable);
8365 SQL_ThreadQuery(tuple,"sql_qhandler_dummy",mkQuery);
8366
8367 return -1;
8368}
8369#else
8370// prune old entries
8371stock stats_prune(max_time=-1)
8372{
8373 get_pcvar_string(gg_stats_file,sfFile,63);
8374 if(!file_exists(sfFile)) return 0;
8375
8376 if(max_time == -1) max_time = get_pcvar_num(gg_stats_prune); // -1 = use value from cvar
8377 if(max_time == 0) return 0; // 0 = no pruning
8378
8379 new tempFileName[33];
8380 formatex(tempFileName,32,"%s2",sfFile); // our temp file, append 2
8381
8382 // copy over current stat file
8383 rename_file(sfFile,tempFileName,1); // relative
8384
8385 // rename failed?
8386 if(!file_exists(tempFileName)) return 0;
8387
8388 new tempFile = fopen(tempFileName,"rt");
8389 new file = fopen(sfFile,"wt");
8390
8391 // go through our old copy and rewrite valid entries into the new copy
8392 new systime = get_systime(), original[112], removed;
8393 while(tempFile && file && !feof(tempFile))
8394 {
8395 fgets(tempFile,sfLineData,111);
8396 if(!sfLineData[0]) continue;
8397
8398 // save original
8399 original = sfLineData;
8400
8401 // break off authid
8402 strtok(sfLineData,dummy,1,sfLineData,111,'^t');
8403
8404 // break off wins
8405 strtok(sfLineData,dummy,1,sfLineData,111,'^t');
8406
8407 // break off name, and thus get timestamp
8408 strtok(sfLineData,dummy,1,sfTimestamp,11,'^t');
8409 copyc(sfTimestamp,11,sfTimestamp,'^t'); // cut off points
8410
8411 // not too old, write it to our new file
8412 if(systime - str_to_num(sfTimestamp) <= max_time)
8413 fprintf(file,"%s",original); // newline is already included
8414 else
8415 removed++;
8416 }
8417
8418 if(tempFile) fclose(tempFile);
8419 if(file) fclose(file);
8420
8421 // remove our copy
8422 delete_file(tempFileName);
8423 return removed;
8424}
8425#endif
8426
8427//**********************************************************************
8428// SQL FUNCTIONS
8429//**********************************************************************
8430
8431#if defined SQL
8432
8433// opens the database
8434stock sql_init(creation_queries=1)
8435{
8436 new host[128], user[128], pass[128], dbname[128];
8437 get_pcvar_string(gg_sql_host,host,127);
8438 get_pcvar_string(gg_sql_user,user,127);
8439 get_pcvar_string(gg_sql_pass,pass,127);
8440 get_pcvar_string(gg_sql_db,dbname,127);
8441
8442 new sqlErrorCode, sqlError[1024];
8443
8444 // free old stuff if reconnecting
8445 if(tuple != Empty_Handle) SQL_FreeHandleAndClear(tuple);
8446 if(db != Empty_Handle) SQL_FreeHandleAndClear(db);
8447
8448 tuple = SQL_MakeDbTuple(host,user,pass,dbname);
8449
8450 if(tuple == Empty_Handle)
8451 {
8452 log_amx("Could not create database tuple. Error #%i: %s",sqlErrorCode,sqlError);
8453 return 0;
8454 }
8455
8456 db = SQL_Connect(tuple,sqlErrorCode,sqlError,1023);
8457
8458 if(db == Empty_Handle)
8459 {
8460 log_amx("Could not connect to database. Error #%i: %s",sqlErrorCode,sqlError);
8461 return 0;
8462 }
8463
8464 if(creation_queries)
8465 {
8466 // set up the main table, unfortunately we have to split it up to avoid "input line too long" compile errors
8467 get_pcvar_string(gg_sql_table,sqlTable,127);
8468
8469 new super_long_query[716],
8470 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);
8471 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`));");
8472
8473 query = SQL_PrepareQuery(db,super_long_query);
8474 if(!SQL_ExecuteAndLog(query))
8475 {
8476 SQL_FreeHandle(query);
8477 return 0;
8478 }
8479
8480 // set up the streaks table
8481 get_pcvar_string(gg_sql_streak_table,sqlStreakTable,127);
8482
8483 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);
8484 if(!SQL_ExecuteAndLog(query))
8485 {
8486 SQL_FreeHandle(query);
8487 return 0;
8488 }
8489
8490 // set up the players table
8491 get_pcvar_string(gg_sql_winmotd_table,sqlPlayersTable,127);
8492
8493 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);
8494 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`));");
8495
8496 query = SQL_PrepareQuery(db,super_long_query);
8497 if(!SQL_ExecuteAndLog(query))
8498 {
8499 SQL_FreeHandle(query);
8500 return 0;
8501 }
8502
8503 SQL_FreeHandle(query);
8504 }
8505
8506 sqlInit = 1;
8507 return 1;
8508}
8509
8510// closes the database
8511public sql_uninit()
8512{
8513 if(sqlInit)
8514 {
8515 if(db != Empty_Handle) SQL_FreeHandleAndClear(db);
8516 if(tuple != Empty_Handle) SQL_FreeHandleAndClear(tuple);
8517 }
8518}
8519
8520// frees a handle and resets its variable to empty
8521SQL_FreeHandleAndClear(&Handle:arg)
8522{
8523 SQL_FreeHandle(arg);
8524 arg = Empty_Handle;
8525}
8526
8527// execute a query and log any errors that come up
8528stock SQL_ExecuteAndLog(&Handle:queryHandle,const queryStr[]="")
8529{
8530 static preventLoopback = 0;
8531
8532 if(!SQL_Execute(queryHandle))
8533 {
8534 static error[256];
8535 new errnum = SQL_QueryError(queryHandle,error,255);
8536
8537 if(queryStr[0]) log_amx("Could not execute query [%s] -- err #%i [%s]",queryStr,errnum,error);
8538 else
8539 {
8540 SQL_GetQueryString(queryHandle,mkQuery,255);
8541 log_amx("Could not execute query [%s] -- err #%i [%s]",mkQuery,errnum,error);
8542 }
8543
8544 // fix thanks to 2inspyr
8545 if(errnum == 2006 && !preventLoopback) // MySQL server has gone away
8546 {
8547 log_amx("Attempting to reconnect to server");
8548
8549 if(sql_init(0)) // no creation queries
8550 {
8551 log_amx("Successfully reconnected to server, executing query again");
8552
8553 SQL_GetQueryString(queryHandle,mkQuery,1535);
8554 new Handle:newQuery = SQL_PrepareQuery(db,mkQuery);
8555
8556 preventLoopback = 1;
8557 SQL_ExecuteAndLog(newQuery);
8558
8559 // transfer handles
8560 SQL_FreeHandle(queryHandle);
8561 queryHandle = newQuery;
8562
8563 preventLoopback = 0;
8564 return 1;
8565
8566 //return SQL_Execute(queryHandle);
8567 }
8568 else
8569 {
8570 log_amx("Could not reconnect to server");
8571
8572 preventLoopback = 0;
8573 return 0;
8574 }
8575 }
8576
8577 preventLoopback = 0;
8578 return 0;
8579 }
8580
8581 preventLoopback = 0;
8582 return 1;
8583}
8584
8585public sql_qhandler_prune(failstate,Handle:query,error[],errnum,data[],size,Float:queuetime)
8586{
8587 if(error[0])
8588 {
8589 SQL_GetQueryString(query,mkQuery,255);
8590 log_amx("Could not execute query [%s] -- err #%i [%s]",mkQuery,errnum,error);
8591 }
8592 else log_amx("%L",LANG_SERVER,"PRUNING",sqlTable,SQL_NumRows(query));
8593}
8594
8595public sql_qhandler_dummy(failstate,Handle:query,error[],errnum,data[],size,Float:queuetime)
8596{
8597 if(error[0])
8598 {
8599 SQL_GetQueryString(query,mkQuery,255);
8600 log_amx("Could not execute query [%s] -- err #%i [%s]",mkQuery,errnum,error);
8601 }
8602}
8603
8604#endif
8605
8606//**********************************************************************
8607// WHATEV
8608//**********************************************************************
8609
8610#if !defined SQL
8611// monitor stats mode and resort if it changed
8612public recheck_stats_sorting()
8613{
8614 new stats_mode = get_pcvar_num(gg_stats_mode);
8615
8616 if(stats_mode && stats_mode != lastStatsMode && lastStatsMode != -909) // set to -909 on init
8617 {
8618 lastStatsMode = stats_mode;
8619
8620 // we are judging by a different criteria, so we need to resort
8621 if(statsPointers[0] && statsSize[0]) ArraySort(statsPointers[0],"stats_pointer_sort_reg");
8622 if(statsPointers[1] && statsSize[1]) ArraySort(statsPointers[1],"stats_pointer_sort_tp");
8623
8624 // clear any saved stats positions out of the tempsaves, so no one rejoins with the wrong position
8625 for(new i=0;i<TEMP_SAVES;i++)
8626 {
8627 tempSave[i][svStatsPosition][0] = 0;
8628 tempSave[i][svStatsPosition][1] = 0;
8629 }
8630
8631 // also make sure that everyone will get assigned new positions
8632 for(new i=1;i<=maxPlayers;i++)
8633 {
8634 statsPosition[i][0] = 0;
8635 statsPosition[i][1] = 0;
8636 }
8637
8638 stats_get_top_players();
8639 }
8640
8641 lastStatsMode = stats_mode;
8642}
8643#endif
8644
8645// task is set on a potential team change, and removed on an
8646// approved team change, so if we reach it, deduct level
8647public delayed_suicide(taskid)
8648{
8649 new id = taskid-TASK_DELAYED_SUICIDE;
8650 if(is_user_connected(id)) player_suicided(id);
8651}
8652
8653// remove those annoying pesky pistols, if they haven't been already
8654strip_starting_pistols(id)
8655{
8656 if(!is_user_alive(id)) return;
8657
8658 switch(cs_get_user_team(id))
8659 {
8660 case CS_TEAM_T:
8661 {
8662 if(!equal(lvlWeapon[id],"glock18") && user_has_weapon(id,CSW_GLOCK18))
8663 ham_strip_weapon(id,WEAPON_GLOCK18);
8664 }
8665 case CS_TEAM_CT:
8666 {
8667 if(!equal(lvlWeapon[id],"usp") && user_has_weapon(id,CSW_USP))
8668 ham_strip_weapon(id,"weapon_usp");
8669 }
8670 }
8671}
8672
8673// weapon display
8674stock status_display(id,status=1)
8675{
8676 new sdisplay = get_pcvar_num(gg_status_display);
8677
8678 // display disabled
8679 if(!sdisplay) return;
8680
8681 // dead
8682 if(id && !is_user_alive(id)) return;
8683
8684 new dest;
8685 static sprite[32];
8686
8687 if(!id) dest = MSG_BROADCAST;
8688 else dest = MSG_ONE_UNRELIABLE;
8689
8690 // disable display if status is 0, or we are doing a warmup
8691 if(!status || warmup > 0)
8692 {
8693 // don't send to bots
8694 if(dest == MSG_BROADCAST || !is_user_bot(id))
8695 {
8696 message_begin(dest,gmsgScenario,_,id);
8697 write_byte(0);
8698 message_end();
8699 }
8700
8701 return;
8702 }
8703
8704 // weapon display
8705 if(sdisplay == STATUS_LEADERWPN || sdisplay == STATUS_YOURWPN)
8706 {
8707 if(sdisplay == STATUS_LEADERWPN)
8708 {
8709 new ldrLevel;
8710 get_leader(ldrLevel);
8711
8712 // get leader's weapon
8713 if(ldrLevel <= 0) return;
8714 copy(sprite,31,weaponName[ldrLevel-1]);
8715 }
8716 else
8717 {
8718 // get your weapon
8719 if(level[id] <= 0) return;
8720 copy(sprite,31,lvlWeapon[id]);
8721 }
8722
8723 strtolower(sprite);
8724
8725 // sprite is d_grenade, not d_hegrenade
8726 if(sprite[0] == 'h') sprite = "grenade";
8727
8728 // get true sprite name
8729 format(sprite,31,"d_%s",sprite);
8730 }
8731
8732 // kill display
8733 else if(sdisplay == STATUS_KILLSLEFT)
8734 {
8735 new goal = get_level_goal(level[id],id);
8736 formatex(sprite,31,"number_%i",goal-score[id]);
8737 }
8738 else if(sdisplay == STATUS_KILLSDONE)
8739 {
8740 formatex(sprite,31,"number_%i",score[id]);
8741 }
8742
8743 // don't send to bots
8744 if(!id || !is_user_bot(id))
8745 {
8746 message_begin(dest,gmsgScenario,_,id);
8747 write_byte(1);
8748 write_string(sprite);
8749 write_byte(150);
8750 message_end();
8751 }
8752}
8753
8754// hide someone's money display
8755public hide_money(id)
8756{
8757 // hide money
8758 emessage_begin(MSG_ONE,gmsgHideWeapon,_,id);
8759 ewrite_byte(1<<5);
8760 emessage_end();
8761
8762 // hide crosshair that appears from hiding money
8763 emessage_begin(MSG_ONE,gmsgCrosshair,_,id);
8764 ewrite_byte(0);
8765 emessage_end();
8766}
8767
8768//**********************************************************************
8769// SUPPORT FUNCTIONS
8770//**********************************************************************
8771
8772// gets a players info, intended for other plugins to callfunc
8773public get_player_info(id,&rf_level,&rf_score,rf_lvlWeapon[],len,&rf_spawnProtected,rf_statsPosition[2])
8774{
8775 rf_level = level[id];
8776 rf_score = score[id];
8777 copy(rf_lvlWeapon,len,lvlWeapon[id]);
8778 rf_spawnProtected = spawnProtected[id];
8779 rf_statsPosition = statsPosition[id];
8780 return 1;
8781}
8782
8783// analyzes the weapon order and saves it into our variables
8784public setup_weapon_order()
8785{
8786 new weaponOrder[(MAX_WEAPONS*16)+1], temp[27];
8787 get_pcvar_string(gg_weapon_order,weaponOrder,MAX_WEAPONS*16);
8788
8789 new Float:killsperlvl = get_pcvar_float(gg_kills_per_lvl), i, done, colon, goal[6];
8790
8791 // cut them apart
8792 for(i=0;i<MAX_WEAPONS;i++)
8793 {
8794 // out of stuff
8795 if(strlen(weaponOrder) <= 1)
8796 {
8797 i--; // for our count
8798 break;
8799 }
8800
8801 // we still have a comma, go up to it
8802 if(contain_char(weaponOrder,',') != -1)
8803 {
8804 strtok(weaponOrder,temp,26,weaponOrder,MAX_WEAPONS*16,',');
8805 trim(temp);
8806 strtolower(temp);
8807 }
8808
8809 // otherwise, finish up
8810 else
8811 {
8812 copy(temp,26,weaponOrder);
8813 trim(temp);
8814 strtolower(temp);
8815 done = 1; // flag for end of loop
8816 }
8817
8818 colon = contain_char(temp,':');
8819
8820 // no custom requirement, easy
8821 if(colon == -1)
8822 {
8823 copy(weaponName[i],23,temp);
8824 if(equal(temp,KNIFE) || equal(temp,HEGRENADE)) weaponGoal[i] = (killsperlvl > 1.0) ? 1.0 : killsperlvl;
8825 else weaponGoal[i] = killsperlvl;
8826 }
8827 else
8828 {
8829 copyc(weaponName[i],23,temp,':');
8830 copy(goal,5,temp[colon+1]);
8831 weaponGoal[i] = floatstr(goal);
8832 }
8833
8834 if(done) break;
8835 }
8836
8837 // we break to end our loop, so "i" will be where we left it. but it's 0-based.
8838 weaponNum = i+1;
8839}
8840
8841// gets the goal for a level, taking into account default and custom values
8842stock get_level_goal(level,id=0)
8843{
8844 if(level < 1) return 1;
8845
8846 // no teamplay, return preset goal
8847 if(!is_user_connected(id) || !get_pcvar_num(gg_teamplay))
8848 {
8849 if(is_user_bot(id)) return floatround(weaponGoal[level-1]*get_pcvar_float(gg_kills_botmod),floatround_ceil);
8850 return floatround(weaponGoal[level-1],floatround_ceil);
8851 }
8852
8853 // one of this for every player on team
8854 new Float:result = weaponGoal[level-1] * float(team_player_count(cs_get_user_team(id)));
8855
8856 // modifiers for nade and knife levels
8857 if(equal(weaponName[level-1],HEGRENADE)) result *= get_pcvar_float(gg_teamplay_nade_mod);
8858 else if(equal(weaponName[level-1],KNIFE)) result *= get_pcvar_float(gg_teamplay_knife_mod);
8859
8860 if(result <= 0.0) result = 1.0;
8861 return floatround(result,floatround_ceil);
8862}
8863
8864// gets the level a player should use for his level
8865stock get_level_weapon(theLevel,var[],varLen)
8866{
8867 if(warmup > 0 && warmupWeapon[0]) copy(var,varLen,warmupWeapon);
8868 else if(theLevel > 0) copy(var,varLen,weaponName[theLevel-1]);
8869 else var[0] = 0;
8870}
8871
8872// easy function to precache sound via cvar
8873stock precache_sound_by_cvar(pcvar)
8874{
8875 new value[64];
8876 get_pcvar_string(pcvar,value,63);
8877 precache_sound_special(value);
8878}
8879
8880// precache sounds with a little bit of magic
8881stock precache_sound_special(sound[])
8882{
8883 if(!sound[0]) return;
8884
8885 if(containi(sound,".mp3") != -1) precache_generic(sound);
8886 else
8887 {
8888 // stop at ( character to allow precaching sounds that use speak's special functions, eg sound/ambience/xtal_down1(e70)
8889 new value[64], len = copyc(value,63,sound,'(');
8890
8891 // make sure we have a suffix for precaching
8892 if(containi(value,".wav") == -1) formatex(value[len],63-len,".wav");
8893
8894 // precache_sound doesn't take the "sound/" prefix
8895 if(equali(value,"sound/",6)) precache_sound(value[6]);
8896 else precache_sound(value);
8897 }
8898}
8899
8900// gets a player's "authid", or whatever token we want to identify them with.
8901// if you already know the value of gg_stats_ip, you can pass it in and save a cvar check.
8902stock get_gg_authid(id,ret[],len,stats_ip=-777)
8903{
8904 new mode = stats_ip;
8905 if(mode == -777) mode = get_pcvar_num(gg_stats_ip);
8906
8907 switch(mode)
8908 {
8909 case 0: return get_user_authid(id,ret,len); // 0 = by authid
8910 case -1, 2: return get_user_name(id,ret,len); // 2 = by name
8911 }
8912
8913 return get_user_ip(id,ret,len); // 1 = by ip
8914}
8915
8916// figure out which gungame.cfg (or gungame_teamplay.cfg) file to use
8917stock get_gg_config_file(teamplay=0,filename[],len)
8918{
8919 formatex(filename,len,"%s/gungame%s.cfg",cfgDir,(teamplay) ? "_teamplay" : "");
8920
8921 if(!file_exists(filename))
8922 {
8923 formatex(filename,len,"gungame%s.cfg",(teamplay) ? "_teamplay" : "");
8924 if(!file_exists(filename)) filename[0] = 0;
8925 }
8926}
8927
8928// executes what's inside of the config file
8929stock exec_gg_config_file(teamplay=0,allowToggling=0)
8930{
8931 new oldActive = ggActive, oldTeamplay = get_pcvar_num(gg_teamplay);
8932
8933 new cfgFile[64];
8934 get_gg_config_file(teamplay,cfgFile,63);
8935 if(cfgFile[0] && file_exists(cfgFile))
8936 {
8937 server_cmd("exec ^"%s^"",cfgFile);
8938 server_exec();
8939 }
8940
8941 // remember old values of gg_enabled and gg_teamplay if toggling is not allowed.
8942 // this is like we just turned teamplay on via command and we want to make sure
8943 // the configs get run appropriately, but obviously teamplay should still be on afterwards.
8944 if(!allowToggling)
8945 {
8946 set_pcvar_num(gg_enabled,oldActive);
8947 set_pcvar_num(gg_teamplay,oldTeamplay);
8948 }
8949
8950 // reselect random weapon order
8951 new lastOIstr[6], lastOI;
8952
8953 get_localinfo("gg_last_oi",lastOIstr,5);
8954 lastOI = str_to_num(lastOIstr);
8955
8956 // decrement it 1 b/c we probably already did do_rOrder
8957 // and don't want to end up skipping orders
8958 if(lastOI > 0)
8959 {
8960 num_to_str(lastOI-1,lastOIstr,5);
8961 set_localinfo("gg_last_oi",lastOIstr);
8962 }
8963
8964 do_rOrder(1); // thanks for pointing this out Tomek Kalkowski
8965
8966 // in case cvars changed. thanks BbIX!
8967 setup_weapon_order();
8968}
8969
8970// figure out which gungame_mapcycle file to use
8971stock get_gg_mapcycle_file(filename[],len)
8972{
8973 static testFile[64];
8974
8975 // cstrike/addons/amxmodx/configs/gungame_mapcycle.cfg
8976 formatex(testFile,63,"%s/gungame_mapcycle.cfg",cfgDir);
8977 if(file_exists(testFile))
8978 {
8979 copy(filename,len,testFile);
8980 return 1;
8981 }
8982
8983 // cstrike/addons/amxmodx/configs/gungame_mapcycle.txt
8984 formatex(testFile,63,"%s/gungame_mapcycle.txt",cfgDir);
8985 if(file_exists(testFile))
8986 {
8987 copy(filename,len,testFile);
8988 return 1;
8989 }
8990
8991 // cstrike/gungame_mapcycle.cfg
8992 testFile = "gungame_mapcycle.cfg";
8993 if(file_exists(testFile))
8994 {
8995 copy(filename,len,testFile);
8996 return 1;
8997 }
8998
8999 // cstrike/gungame_mapcycle.txt
9000 testFile = "gungame_mapcycle.txt";
9001 if(file_exists(testFile))
9002 {
9003 copy(filename,len,testFile);
9004 return 1;
9005 }
9006
9007 return 0;
9008}
9009
9010// another easy function to play sound via cvar
9011stock play_sound_by_cvar(id,pcvar)
9012{
9013 static value[64];
9014 get_pcvar_string(pcvar,value,63);
9015
9016 if(!value[0]) return;
9017
9018 if(containi(value,".mp3") != -1) client_cmd(id,"mp3 play ^"%s^"",value);
9019 else client_cmd(id,"spk ^"%s^"",value);
9020}
9021
9022// a taskable play_sound_by_cvar
9023public play_sound_by_cvar_task(params[2])
9024{
9025 play_sound_by_cvar(params[0],params[1]);
9026}
9027
9028// this functions take a filepath, but manages speak/mp3 play
9029stock play_sound(id,value[])
9030{
9031 if(!value[0]) return;
9032
9033 if(containi(value,".mp3") != -1) client_cmd(id,"mp3 play ^"%s^"",value);
9034 else
9035 {
9036 if(equali(value,"sound/",6)) client_cmd(id,"spk ^"%s^"",value[6]);
9037 else client_cmd(id,"spk ^"%s^"",value);
9038 }
9039}
9040
9041// find the highest level player and his level
9042stock get_leader(&retLevel=0,&retNumLeaders=0,&retRunnerUp=0,&retRunnerUp3=0)
9043{
9044 new player, leader, numLeaders, runnerUp, runnerUp3, runnerUp4;
9045
9046 // locate highest player
9047 for(player=1;player<=maxPlayers;player++)
9048 {
9049 if(!is_user_connected(player)) continue;
9050
9051 if(leader == 0 || level[player] > level[leader])
9052 {
9053 // about to dethrown leader, monitor runnerup
9054 if(leader && (runnerUp == 0 || level[leader] > level[runnerUp]))
9055 runnerUp = leader;
9056
9057 leader = player;
9058 numLeaders = 1; // reset tied count
9059 }
9060 else if(level[player] == level[leader])
9061 numLeaders++;
9062 else
9063 {
9064 // monitor runnerup
9065 if(runnerUp == 0 || level[player] > level[runnerUp])
9066 runnerUp = player; // locul 2
9067 }
9068 }
9069
9070 // locate highest player
9071 for(player=1;player<=maxPlayers;player++)
9072 {
9073 if(!is_user_connected(player)) continue;
9074
9075 if(player != leader)
9076 {
9077 if(runnerUp == 0 || level[player] > level[runnerUp])
9078 {
9079 // about to dethrown runnerUp, monitor runnerup
9080 if(runnerUp && (runnerUp3 == 0 || level[runnerUp] > level[runnerUp3]))
9081 runnerUp3 = runnerUp;
9082
9083 runnerUp = player;
9084 }
9085 else if(level[player] == level[runnerUp])
9086 numLeaders++;
9087 else
9088 {
9089 // monitor runnerup
9090 if(runnerUp3 == 0 || level[player] > level[runnerUp3])
9091 runnerUp3 = player; // locul 3
9092 }
9093 }
9094 }
9095
9096 // locate highest player
9097 for(player=1;player<=maxPlayers;player++)
9098 {
9099 if(!is_user_connected(player)) continue;
9100
9101 if(player != leader && player != runnerUp)
9102 {
9103 if(runnerUp3 == 0 || level[player] > level[runnerUp3])
9104 {
9105 // about to dethrown runnerUp, monitor runnerup
9106 if(runnerUp3 && (runnerUp4 == 0 || level[runnerUp3] > level[runnerUp4]))
9107 runnerUp4 = runnerUp3;
9108
9109 runnerUp3 = player;
9110 }
9111 else if(level[player] == level[runnerUp3])
9112 numLeaders++;
9113 else
9114 {
9115 // monitor runnerup
9116 if(runnerUp4 == 0 || level[player] > level[runnerUp4])
9117 runnerUp4 = player; // locul 4
9118 }
9119 }
9120 }
9121
9122
9123
9124
9125 retLevel = level[leader];
9126 retNumLeaders = numLeaders;
9127 retRunnerUp = runnerUp;
9128 retRunnerUp3 = runnerUp3;
9129
9130 return leader;
9131}
9132
9133// gets the number of players on a particular level
9134stock num_players_on_level(checkLvl)
9135{
9136 new player, result;
9137 for(player=1;player<=maxPlayers;player++)
9138 {
9139 if(is_user_connected(player) && level[player] == checkLvl)
9140 result++;
9141 }
9142 return result;
9143}
9144
9145// a butchered version of teame06's CS Color Chat Function.
9146// actually it's now almost completely different, but that's
9147// where I started from.
9148gungame_print(id,custom,tag,msg[],any:...)
9149{
9150 new messages = get_pcvar_num(gg_messages);
9151 if(!messages || (messages & MSGS_HIDETEXT)) return 0;
9152
9153 new changeCount, num, i, j, argnum = numargs(), player, colored_messages = !(messages & MSGS_NOCOLOR);
9154 static newMsg[191], message[191], changed[8], players[32];
9155
9156 if(id)
9157 {
9158 players[0] = id;
9159 num = 1;
9160 }
9161 else get_players(players,num);
9162
9163 for(i=0;i<num;i++)
9164 {
9165 player = players[i];
9166 changeCount = 0;
9167
9168 if(!is_user_connected(player)) continue;
9169
9170 // we have to change LANG_PLAYER into
9171 // a player-specific argument, because
9172 // ML doesn't work well with SayText
9173 for(j=4;j<argnum;j++)
9174 {
9175 if(getarg(j) == LANG_PLAYER_C)
9176 {
9177 setarg(j,0,player);
9178 changed[changeCount++] = j;
9179 }
9180 }
9181
9182 // do user formatting
9183 vformat(newMsg,190,msg,5);
9184
9185 // and now we have to change what we changed
9186 // back into LANG_PLAYER, so that the next
9187 // player will be able to have it in his language
9188 for(j=0;j<changeCount;j++)
9189 {
9190 setarg(changed[j],0,LANG_PLAYER_C);
9191 }
9192
9193 // optimized color swapping
9194 if(colored_messages)
9195 {
9196 replace_all(newMsg,190,"%n","^x03"); // %n = team color
9197 replace_all(newMsg,190,"%g","^x04"); // %g = green
9198 replace_all(newMsg,190,"%e","^x01"); // %e = regular
9199 }
9200 else
9201 {
9202 replace_all(newMsg,190,"%n","");
9203 replace_all(newMsg,190,"%g","");
9204 replace_all(newMsg,190,"%e","");
9205 }
9206
9207 // now do our formatting (I used two variables because sharing one caused glitches)
9208
9209 if(tag)
9210 {
9211 if(colored_messages) formatex(message,190,"^x04[%L]^x01 %s",player,"GUNGAME",newMsg);
9212 else formatex(message,190,"^x01[%L] %s",player,"GUNGAME",newMsg);
9213 }
9214 else formatex(message,190,"^x01%s",newMsg);
9215
9216 message_begin(MSG_ONE,gmsgSayText,_,player);
9217 write_byte((custom > 0) ? custom : player);
9218 write_string(message);
9219 message_end();
9220 }
9221
9222 return 1;
9223}
9224
9225// show a HUD message to a user
9226gungame_hudmessage(id,Float:holdTime,msg[],any:...)
9227{
9228 new messages = get_pcvar_num(gg_messages);
9229 if(!messages || (messages & MSGS_HIDEHUD)) return 0;
9230
9231 // user formatting
9232 static newMsg[191];
9233 vformat(newMsg,190,msg,4);
9234
9235 // show it
9236 set_hudmessage(255,255,255,-1.0,0.8,0,6.0,holdTime,0.1,0.5);
9237 return ShowSyncHudMsg(id,hudSyncReqKills,newMsg);
9238}
9239
9240// start a map vote
9241stock start_mapvote()
9242{
9243 new dmmName[24], plugin;
9244
9245 // Galileo - galileo.amxx
9246 if(galileoID != -1)
9247 {
9248 log_amx("Starting a map vote from Galileo");
9249
9250 server_cmd("gal_startvote -nochange");
9251 }
9252
9253 // AMXX Nextmap Chooser - mapchooser.amxx
9254 else if((plugin = is_plugin_loaded("Nextmap Chooser")) != -1)
9255 {
9256 log_amx("Starting a map vote from Nextmap Chooser");
9257
9258 new oldWinLimit = get_cvar_num("mp_winlimit"), oldMaxRounds = get_cvar_num("mp_maxrounds");
9259 set_cvar_num("mp_winlimit",0); // skip winlimit check
9260 set_cvar_num("mp_maxrounds",-1); // trick plugin to think game is almost over
9261
9262 // call the vote
9263 if(callfunc_begin_i(get_func_id("voteNextmap",plugin),plugin) == 1)
9264 callfunc_end();
9265
9266 // set maxrounds back
9267 set_cvar_num("mp_winlimit",oldWinLimit);
9268 set_cvar_num("mp_maxrounds",oldMaxRounds);
9269 }
9270
9271 // Deagles' Map Management 2.30b - deagsmapmanage230b.amxx
9272 else if((plugin = is_plugin_loaded("DeagsMapManage")) != -1)
9273 {
9274 dmmName = "DeagsMapManage";
9275 }
9276
9277 // Deagles' Map Management 2.40 - deagsmapmanager.amxx
9278 else if((plugin = is_plugin_loaded("DeagsMapManager")) != -1)
9279 {
9280 dmmName = "DeagsMapManager";
9281 }
9282
9283 // Mapchooser4 - mapchooser4.amxx
9284 else if((plugin = is_plugin_loaded("Nextmap Chooser 4")) != -1)
9285 {
9286 log_amx("Starting a map vote from Nextmap Chooser 4");
9287
9288 new oldWinLimit = get_cvar_num("mp_winlimit"), oldMaxRounds = get_cvar_num("mp_maxrounds");
9289 set_cvar_num("mp_winlimit",0); // skip winlimit check
9290 set_cvar_num("mp_maxrounds",1); // trick plugin to think game is almost over
9291
9292 // deactivate g_buyingtime variable
9293 if(callfunc_begin_i(get_func_id("buyFinished",plugin),plugin) == 1)
9294 callfunc_end();
9295
9296 // call the vote
9297 if(callfunc_begin_i(get_func_id("voteNextmap",plugin),plugin) == 1)
9298 {
9299 callfunc_push_str("",false);
9300 callfunc_end();
9301 }
9302
9303 // set maxrounds back
9304 set_cvar_num("mp_winlimit",oldWinLimit);
9305 set_cvar_num("mp_maxrounds",oldMaxRounds);
9306 }
9307
9308 // NOTHING?
9309 else log_amx("Using gg_vote_setting without any compatible plugins: could not start a vote!");
9310
9311 // do DMM stuff
9312 if(dmmName[0])
9313 {
9314 log_amx("Starting a map vote from %s",dmmName);
9315
9316 // allow voting
9317 /*if(callfunc_begin("dmapvotemode",dmmName) == 1)
9318 {
9319 callfunc_push_int(0); // server
9320 callfunc_end();
9321 }*/
9322
9323 new oldWinLimit = get_cvar_num("mp_winlimit"), Float:oldTimeLimit = get_cvar_float("mp_timelimit");
9324 set_cvar_num("mp_winlimit",99999); // don't allow extending
9325 set_cvar_float("mp_timelimit",0.0); // don't wait for buying
9326 set_cvar_num("enforce_timelimit",1); // don't change map after vote
9327
9328 // call the vote
9329 if(callfunc_begin_i(get_func_id("startthevote",plugin),plugin) == 1)
9330 callfunc_end();
9331
9332 set_cvar_num("mp_winlimit",oldWinLimit);
9333 set_cvar_float("mp_timelimit",oldTimeLimit);
9334
9335 // disallow further voting
9336 /*if(callfunc_begin("dmapcyclemode",dmmName) == 1)
9337 {
9338 callfunc_push_int(0); // server
9339 callfunc_end();
9340 }*/
9341
9342 set_task(20.1,"dmm_stop_mapchange");
9343 }
9344}
9345
9346// stop DMM from changing maps after the vote has been tallied
9347public dmm_stop_mapchange()
9348{
9349 remove_task(333333,1); // outside
9350}
9351
9352// set amx_nextmap to the next map
9353stock set_nextmap()
9354{
9355 new mapCycleFile[64];
9356 get_gg_mapcycle_file(mapCycleFile,63);
9357
9358 // no mapcycle, leave amx_nextmap alone
9359 if(!mapCycleFile[0] || !file_exists(mapCycleFile))
9360 {
9361 set_localinfo("gg_cycle_num","0");
9362 return 0;
9363 }
9364
9365 new strVal[10];
9366
9367 // have not gotten cycleNum yet (only get it once, because
9368 // set_nextmap is generally called at least twice per map, and we
9369 // don't want to change it twice)
9370 if(cycleNum == -1)
9371 {
9372 get_localinfo("gg_cycle_num",strVal,9);
9373 cycleNum = str_to_num(strVal);
9374 }
9375
9376 new firstMap[32], currentMap[32], lineData[32], i, line, foundMap;
9377 get_mapname(currentMap,31);
9378
9379 new file = fopen(mapCycleFile,"rt");
9380 while(file && !feof(file))
9381 {
9382 fgets(file,lineData,31);
9383
9384 trim(lineData);
9385 replace(lineData,31,".bsp",""); // remove extension
9386 new len = strlen(lineData) - 2;
9387
9388 // stop at a comment
9389 for(i=0;i<len;i++)
9390 {
9391 // supports config-style (;) and coding-style (//)
9392 if(lineData[i] == ';' || (lineData[i] == '/' && lineData[i+1] == '/'))
9393 {
9394 copy(lineData,i,lineData);
9395 break;
9396 }
9397 }
9398
9399 trim(lineData);
9400 if(!lineData[0]) continue;
9401
9402 // save first map
9403 if(!firstMap[0]) copy(firstMap,31,lineData);
9404
9405 // we reached the line after our current map's line
9406 if(line == cycleNum+1)
9407 {
9408 // remember so
9409 foundMap = 1;
9410
9411 // get ready to change to it
9412 set_cvar_string("amx_nextmap",lineData);
9413
9414 // remember this map's line for next time
9415 num_to_str(line,strVal,9);
9416 set_localinfo("gg_cycle_num",strVal);
9417
9418 break;
9419 }
9420
9421 line++;
9422 }
9423 if(file) fclose(file);
9424
9425 // we didn't find next map
9426 if(!foundMap)
9427 {
9428 // reset line number to first (it's zero-based)
9429 set_localinfo("gg_cycle_num","0");
9430
9431 // no maps listed, go to current
9432 if(!firstMap[0]) set_cvar_string("amx_nextmap",currentMap);
9433
9434 // go to first map listed
9435 else set_cvar_string("amx_nextmap",firstMap);
9436 }
9437
9438 return 1;
9439}
9440
9441// go to amx_nextmap
9442public goto_nextmap()
9443{
9444 new mapCycleFile[64];
9445 get_gg_mapcycle_file(mapCycleFile,63);
9446
9447 // no gungame mapcycle
9448 if(!mapCycleFile[0] || !file_exists(mapCycleFile))
9449 {
9450 new custom[256];
9451 get_pcvar_string(gg_changelevel_custom,custom,255);
9452
9453 // try custom changelevel command
9454 if(custom[0])
9455 {
9456 server_cmd(custom);
9457 return;
9458 }
9459 }
9460
9461 if(galileoID != -1)
9462 {
9463 if(callfunc_begin_i(get_func_id("map_change",galileoID),galileoID) == 1)
9464 {
9465 callfunc_end();
9466 return;
9467 }
9468 }
9469
9470 // otherwise, go to amx_nextmap
9471 new nextMap[32];
9472 get_cvar_string("amx_nextmap",nextMap,31);
9473
9474 server_cmd("changelevel %s",nextMap);
9475}
9476
9477// find a player's weapon entity
9478stock get_weapon_ent(id,wpnid=0,wpnName[]="")
9479{
9480 // who knows what wpnName will be
9481 static newName[24];
9482
9483 // need to find the name
9484 if(wpnid) get_weaponname(wpnid,newName,23);
9485
9486 // go with what we were told
9487 else copy(newName,23,wpnName);
9488
9489 // prefix it if we need to
9490 if(!equal(newName,"weapon_",7))
9491 format(newName,23,"weapon_%s",newName);
9492
9493 return fm_find_ent_by_owner(maxPlayers,newName,id);
9494}
9495
9496// counts number of chars in a string, by (probably) Twilight Suzuka
9497stock str_count(str[],searchchar)
9498{
9499 new i = 0;
9500 new maxlen = strlen(str);
9501 new count = 0;
9502
9503 for(i=0;i<=maxlen;i++)
9504 {
9505 if(str[i] == searchchar)
9506 count++;
9507 }
9508 return count;
9509}
9510
9511// find the nth occurance of a character in a string, based on str_count
9512stock str_find_num(str[],searchchar,number)
9513{
9514 new i;
9515 new maxlen = strlen(str);
9516 new found = 0;
9517
9518 for(i=0;i<=maxlen;i++)
9519 {
9520 if(str[i] == searchchar)
9521 {
9522 if(++found == number)
9523 return i;
9524 }
9525 }
9526 return -1;
9527}
9528
9529// works like contain, but looks only for a specific character
9530stock contain_char(str[],chara)
9531{
9532 new i;
9533 while(str[i] != 0)
9534 {
9535 if(str[i] == chara) return i;
9536 i++;
9537 }
9538 return -1;
9539}
9540
9541// cuts a snippet out of a string
9542stock remove_snippet(string[],strLen,start,end)
9543{
9544 new i, newpos;
9545 for(i=start;i<strLen;i++)
9546 {
9547 if(!string[i]) break;
9548 newpos = i + end - start + 1;
9549
9550 if(newpos >= strLen) string[i] = 0;
9551 else string[i] = string[newpos];
9552 }
9553
9554 return 1;
9555}
9556
9557// gets a player id that triggered certain logevents, by VEN
9558stock get_loguser_index()
9559{
9560 static loguser[80], name[32];
9561 read_logargv(0,loguser,79);
9562 parse_loguser(loguser,name,31);
9563
9564 return get_user_index(name);
9565}
9566
9567// checks if a space is vacant, by VEN
9568stock bool:is_hull_vacant(const Float:origin[3],hull)
9569{
9570 new tr = 0;
9571 engfunc(EngFunc_TraceHull,origin,origin,0,hull,0,tr);
9572
9573 if(!get_tr2(tr,TR_StartSolid) && !get_tr2(tr,TR_AllSolid) && get_tr2(tr,TR_InOpen))
9574 return true;
9575
9576 return false;
9577}
9578
9579// gets a weapon's category, just a shortcut to the weaponSlots table basically
9580stock get_weapon_category(id=0,name[]="")
9581{
9582 if(name[0])
9583 {
9584 if(equal(name,"weapon_",7)) id = get_weaponid(name);
9585 else
9586 {
9587 static newName[24];
9588 formatex(newName,23,"weapon_%s",name);
9589 id = get_weaponid(newName);
9590 }
9591 }
9592
9593 if(id < 1 || id > 30) return -1;
9594 return weaponSlots[id];
9595}
9596
9597// if a player is allowed to score (at least 1 rival player)
9598stock can_score(id)
9599{
9600 if(!is_user_connected(id)) return 0;
9601
9602 new penalty = get_pcvar_num(gg_tk_penalty);
9603 for(new player=1;player<=maxPlayers;player++)
9604 {
9605 // this player is on a real team, and he's on the opposite team, or we are playing FFA
9606 if( player != id && is_user_connected(player) && on_valid_team(player) && (penalty < 0 || cs_get_user_team(id) != cs_get_user_team(player)) )
9607 return 1;
9608 }
9609
9610 return 0;
9611}
9612
9613// returns 1 if there are only bots in the server, 0 if not
9614stock only_bots()
9615{
9616 new player;
9617 for(player=1;player<=maxPlayers;player++)
9618 {
9619 if(is_user_connected(player) && !is_user_bot(player))
9620 return 0;
9621 }
9622
9623 // didn't find any humans
9624 return 1;
9625}
9626
9627// gives a player a weapon efficiently
9628stock ham_give_weapon(id,const weapon[])
9629{
9630 if(!equal(weapon,"weapon_",7)) return 0;
9631
9632 new wEnt = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,weapon));
9633 if(!pev_valid(wEnt)) return 0;
9634
9635 set_pev(wEnt,pev_spawnflags,SF_NORESPAWN);
9636 dllfunc(DLLFunc_Spawn,wEnt);
9637
9638 if(!ExecuteHamB(Ham_AddPlayerItem,id,wEnt))
9639 {
9640 if(pev_valid(wEnt)) set_pev(wEnt,pev_flags,pev(wEnt,pev_flags) | FL_KILLME);
9641 return 0;
9642 }
9643
9644 ExecuteHamB(Ham_Item_AttachToPlayer,wEnt,id)
9645 return 1;
9646}
9647
9648// takes a weapon from a player efficiently
9649stock ham_strip_weapon(id,const weapon[])
9650{
9651 if(!equal(weapon,"weapon_",7)) return 0;
9652
9653 new wId = get_weaponid(weapon);
9654 if(!wId) return 0;
9655
9656 new wEnt;
9657 while((wEnt = engfunc(EngFunc_FindEntityByString,wEnt,"classname",weapon)) && pev(wEnt,pev_owner) != id) {}
9658 if(!wEnt) return 0;
9659
9660 if(get_user_weapon(id) == wId) ExecuteHamB(Ham_Weapon_RetireWeapon,wEnt);
9661
9662 if(!ExecuteHamB(Ham_RemovePlayerItem,id,wEnt)) return 0;
9663 ExecuteHamB(Ham_Item_Kill,wEnt);
9664
9665 set_pev(id,pev_weapons,pev(id,pev_weapons) & ~(1<<wId));
9666
9667 if(wId == CSW_C4)
9668 {
9669 cs_set_user_plant(id,0,0);
9670 cs_set_user_bpammo(id,CSW_C4,0);
9671 }
9672 else if(wId == CSW_SMOKEGRENADE || wId == CSW_FLASHBANG || wId == CSW_HEGRENADE)
9673 cs_set_user_bpammo(id,wId,0);
9674
9675 return 1;
9676}
9677
9678// gets the weapon that a killer used, just like CHalfLifeMultiplay::DeathNotice
9679stock get_killer_weapon(killer,inflictor,retVar[],retLen)
9680{
9681 static killer_weapon_name[32];
9682 killer_weapon_name = "world"; // by default, the player is killed by the world
9683
9684 if(pev_valid(killer) && (pev(killer,pev_flags) & FL_CLIENT))
9685 {
9686 if(pev_valid(inflictor))
9687 {
9688 if(inflictor == killer)
9689 {
9690 // if the inflictor is the killer, then it must be their current weapon doing the damage
9691 new weapon = get_user_weapon(killer);
9692 get_weaponname(weapon,killer_weapon_name,31);
9693 }
9694 else pev(inflictor,pev_classname,killer_weapon_name,31); // it's just that easy
9695 }
9696 }
9697 else
9698 {
9699 if(pev_valid(killer)) pev(inflictor,pev_classname,killer_weapon_name,31);
9700 else if(killer == 0) killer_weapon_name = "worldspawn";
9701 }
9702
9703 // strip the monster_* or weapon_* from the inflictor's classname
9704 if(equal(killer_weapon_name,"weapon_",7))
9705 copy(killer_weapon_name,31,killer_weapon_name[7]);
9706 else if(equal(killer_weapon_name,"monster_",8))
9707 copy(killer_weapon_name,31,killer_weapon_name[8]);
9708 else if(equal(killer_weapon_name,"func_",5))
9709 copy(killer_weapon_name,31,killer_weapon_name[5]);
9710
9711 // output
9712 copy(retVar,retLen,killer_weapon_name);
9713}
9714
9715// gets a team's color
9716stock get_team_color(CsTeams:team,ret[],retLen)
9717{
9718 switch(team)
9719 {
9720 case CS_TEAM_T: return formatex(ret,retLen,"FF3F3F");
9721 case CS_TEAM_CT: return formatex(ret,retLen,"99CCFF");
9722 }
9723
9724 return formatex(ret,retLen,"FFFFFF");
9725}
9726
9727// gets the name of a team
9728stock get_team_name(CsTeams:team,ret[],retLen)
9729{
9730 switch(team)
9731 {
9732 case CS_TEAM_T: return formatex(ret,retLen,"TERRORIST");
9733 case CS_TEAM_CT: return formatex(ret,retLen,"CT");
9734 case CS_TEAM_SPECTATOR: return formatex(ret,retLen,"SPECTATOR");
9735 }
9736
9737 return formatex(ret,retLen,"UNASSIGNED");
9738}
9739
9740// gets the amount of players on a team
9741stock team_player_count(CsTeams:team)
9742{
9743 new player, count;
9744 for(player=1;player<=maxPlayers;player++)
9745 {
9746 if(is_user_connected(player) && cs_get_user_team(player) == team)
9747 count++;
9748 }
9749
9750 return count;
9751}
9752
9753// is this player on a valid team?
9754on_valid_team(id)
9755{
9756 new CsTeams:team = cs_get_user_team(id);
9757 return (team == CS_TEAM_T || team == CS_TEAM_CT);
9758}
9759
9760// gets a number's suffix. sort of bad, has to convert to string.
9761stock get_number_suffix(number,ret[],retLen)
9762{
9763 static str[8];
9764 num_to_str(number,str,7);
9765 new len = strlen(str);
9766
9767 if(number >= 10 && str[len-2] == '1') // second to last digit
9768 return formatex(ret,retLen,"th"); // 10-19 end in 'th
9769
9770 switch(str[len-1]) // last digit
9771 {
9772 case '1': return formatex(ret,retLen,"st");
9773 case '2': return formatex(ret,retLen,"nd");
9774 case '3': return formatex(ret,retLen,"rd");
9775 }
9776
9777 return formatex(ret,retLen,"th");
9778}