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