/* Ski2 Shop by Seta00
 * 
 * This code is released under the GPLv3 License:
 * http://www.gnu.org/licenses/gpl-3.0.html
 * 
 * This plugin enables gameplay funcionalities to
 * the surf_ski_2 map.
 * 
 * When the player enters the Mario room, a menu 
 * is displayed with the Ski 2 Shop Items.
 * 
 * All item costs are changeable by cvar, changes
 * are applied on round start.
 * 
 * Items description:
 * 
 * - Get Out Of Jail ($1500) - One shot
 * 
 * Inspired by the "Get Out Of Jail Free Card"
 * from GTA2, this item removes you from the 
 * jail as soon as you enter it.
 * 
 * Note: Users with the Eye of the Spy can see
 * you entering the jail, but not leaving it.
 * You can use this to attract enemies to the
 * jail area.
 * 
 * - Health Reload ($7500) - One shot
 * 
 * Reloads your HP to 100. Useful but expensive.
 * 
 * Note: Be careful, two Health Reloads and you
 * are left with ~1000 in your pocket.
 * 
 * - Extra Amor ($2500) - 3 rounds
 * 
 * Sets your maximum armor to 150.
 * 
 * Note: If you are already wearing armor, it gets
 * filled to 100 AP.
 * 
 * - M3 and M4 ($5000) - 3 rounds
 * 
 * The second most expensive item in the shop, it's
 * a really god choice in surf_ski_2. Remember, if
 * you die with this item, you have to reach the
 * Mario room to get a new M3 and M4.
 * 
 * - Eye of the Spy ($1000) - 5 rounds
 * 
 * An item for the strategists. It warns you every
 * time someone enters the jail.
 * 
 * - Save ($2500) - One shot
 * 
 * Saves your current items. When you reconnect, items
 * are automatically restored.
 * 
 * Plugin Thread: http://forums.alliedmods.net/showthread.php?p=1092198
 * 
 * Thanks to xPaw for his surf_ski_2 rules watcher plugin.
 * Thanks to Arkshine for pointing me the message issue and by doing so allowed me to remove fakemeta dependency.
 * Thanks for ConnorMcLeod for reviving this trash from the deeper layer of hell. NOT!
 */
 
#include <amxmodx>
#include <engine>
#include <nvault>
#include <cstrike>
#include <fun>
#include <hamsandwich>
 
#define PLUGIN	"Ski2 Shop"
#define AUTHOR	"Seta00"
#define VERSION	"1.7.1"
 
#pragma semicolon 1
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// ENUMERATIONS / TYPEs
 
enum __cvar_t {
	cvName[50],
	cvValue[10],
	cvPtr
};
 
enum __duration_t {
	duration,
	lostMlStr[50]
};
 
enum {
	GET_OUT_OF_JAIL, // FREE_CARD
	HEALTH_RELOAD,
	EXTRA_ARMOR,
	M3_AND_M4,
	EYE_OF_THE_SPY,
	SAVE,
 
	LAST_ITEM
};
 
enum {
	JAIL,
	MARIOROOM,
 
	LAST_ZONE
};
 
enum Color {
	NORMAL = 1, // clients scr_concolor cvar color
	GREEN, // Green Color
	TEAM_COLOR, // Red, grey, blue
	GREY, // grey
	RED, // Red
	BLUE, // Blue
};
 
enum {
	M3,
	M4
};
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// GLOBAL VARS
 
new const Classname[] = "trigger_ski2_shop";
new UserItems[33], UserIPs[33][15], UserRoundStart[33][LAST_ITEM], M3AndM4[33][2], RoundCount, MaxPlayers;
new Float:LastTouch[33][LAST_ZONE];
new bool:IsOnMario[33], bool:IsOnJail[33];
 
new const CVARS[][__cvar_t] = {
	{"ss_getoutofjail_price", "1500", 0},
	{"ss_getoutofjail_duration", "-1", 0},
	{"ss_healthreload_price", "7500", 0},
	{"ss_healthreload_duration", "-1", 0},
	{"ss_extraarmor_price", "2500", 0},
	{"ss_extraarmor_duration", "3", 0},
	{"ss_m3andm4_price", "5000", 0},
	{"ss_m3andm4_duration", "3", 0},
	{"ss_eyeofthespy_price", "750", 0},
	{"ss_eyeofthespy_duration", "5", 0},
	{"ss_save_price", "2500", 0},
	{"ss_save_duration", "-1", 0},
	{"ss_version", VERSION, 0} // always the last one
};
 
new COST[LAST_ITEM] = {
	1500,
	7500,
	2500,
	5000,
	750,
	2500
};
 
new DURATION[LAST_ITEM][__duration_t] = {
	{-1, "LOST_GET_OUT_OF_JAIL"},
	{-1, ""},
	{3, "LOST_EXTRA_ARMOR"},
	{3, "LOST_M3_AND_M4"},
	{5, "LOST_EYE_OF_THE_SPY"},
	{-1, ""}
};
 
new TeamName[][] = {
	"",
	"TERRORIST",
	"CT",
	"SPECTATOR"
};
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// MACROS
 
#define HAS_GET_OUT_OF_JAIL(%1) (UserItems[%1] & (1 << GET_OUT_OF_JAIL))
#define HAS_HEALTH_RELOAD(%1) (UserItems[%1] & (1 << HEALTH_RELOAD))
#define HAS_EXTRA_ARMOR(%1) (UserItems[%1] & (1 << EXTRA_ARMOR))
#define HAS_M3_AND_M4(%1) (UserItems[%1] & (1 << M3_AND_M4))
#define HAS_EYE_OF_THE_SPY(%1) (UserItems[%1] & (1 << EYE_OF_THE_SPY))
#define HAS_SAVE(%1) (UserItems[%1] & (1 << SAVE))
 
#define SET_GET_OUT_OF_JAIL(%1,%2) (UserItems[%1] |= (_:%2 << GET_OUT_OF_JAIL))
#define SET_HEALTH_RELOAD(%1,%2) (UserItems[%1] |= (_:%2 << HEALTH_RELOAD))
#define SET_EXTRA_ARMOR(%1,%2) (UserItems[%1] |= (_:%2 << EXTRA_ARMOR))
#define SET_M3_AND_M4(%1,%2) (UserItems[%1] |= (_:%2 << M3_AND_M4))
#define SET_EYE_OF_THE_SPY(%1,%2) (UserItems[%1] |= (_:%2 << EYE_OF_THE_SPY))
#define SET_SAVE(%1,%2) (UserItems[%1] |= (_:%2 << SAVE))
 
#define MAX_MENU_ITEMS 7
#define ML(%1) formatex(menuStr, charsmax(menuStr), "%L", id, %1)
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
 
public plugin_init() {
	register_plugin(PLUGIN, VERSION, AUTHOR);
	register_dictionary("ski2_shop.txt");
 
	new mapname[11];
	get_mapname(mapname, charsmax(mapname));
	if (equali(mapname, "surf_ski_2")) {
		CreateTrigger(JAIL, Float:{-579.0, 2983.0, 586.0}, Float:{-309.0, 3288.0, 796.0});
		CreateTrigger(MARIOROOM, Float:{2735.0, 193.0, -2361.0}, Float:{3709.0, 1180.0, -2087.0});
 
		register_touch(Classname, "player", "EnteredSpecialZone");
 
		RegisterHam(Ham_Spawn, "player", "FwdPlayerSpawn", 1);
 
		register_event("HLTV", "NewRound", "a", "1=0", "2=0");
		register_event("ItemPickup", "OnItemPickup", "be");
 
		register_clcmd("say /menu", "ShowShopMenu");
		register_clcmd("say_team /menu", "ShowShopMenu");
		register_clcmd("shop_menu", "ShowShopMenu");
 
		for (new i = 0; i < sizeof(CVARS)-1; ++i) {
			CVARS[i][cvPtr] = register_cvar(CVARS[i][cvName], CVARS[i][cvValue]);
		}
 
		// version cvar
		register_cvar(CVARS[sizeof(CVARS)-1][cvName], CVARS[sizeof(CVARS)-1][cvValue], FCVAR_SERVER|FCVAR_SPONLY);
 
		CacheCvars();
 
		MaxPlayers = get_maxplayers();
	}
}
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// FORWARDS
 
public client_connect(id) {
	get_user_ip(id, UserIPs[id], 15, 1);
	if (LoadUserItems(id) == -1) {
		UserItems[id] = 0;
	}
}
 
public client_disconnect(id) {
	if (HAS_SAVE(id))
		SaveUserItems(id);
}
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// EVENTS / TOUCHS / HAMS
 
public NewRound() {
	RoundCount++;
 
	CacheCvars();
 
	new players[32], num, i;
	get_players(players, num, "ch");
 
	for (new k = 0; k < num; ++k) {
		i = players[k];
		for (new j = 0; j < sizeof(UserItems[]); ++j) {
			if (DURATION[j][duration] == -1) break;
			if (~UserItems[i] & (1 << j)) break;
 
			if (RoundCount == UserRoundStart[i][j] + DURATION[j][duration]) {
				UserItems[i] &= ~(1 << j);
				client_print(i, print_chat, "%L", i, DURATION[j][lostMlStr]);
			}
		}
	}
}
 
public OnItemPickup(player) {
	if (!HAS_EXTRA_ARMOR(player))
		return;
 
	new itemName[17];
	read_data(1, itemName, charsmax(itemName));
 
	if (!(equal(itemName, "item_kevlar") || equal(itemName, "item_assaultsuit"))) {
		return;
	}
 
	entity_set_float(player, EV_FL_armorvalue, 150.0);
 
	return;
}
 
// used xPaw idea for avoiding multiple touchs
public EnteredSpecialZone(entity, id) {
	static Float:gametime;
	gametime = get_gametime();
 
	static location;
	location = entity_get_int(entity, EV_INT_iuser1);
 
	if( gametime > (LastTouch[id][location] + 1.2)) {
		LastTouch[id][location] = gametime;
		if(location)
			IsOnJail[id] = false;
		else
			IsOnMario[id] = false;
 
	} else if (LastTouch[id][location] < gametime) {
		switch (location) {
			case MARIOROOM: {
				if (!IsOnMario[id]) {
					IsOnMario[id] = true;
					client_print(id, print_chat, "%L", id, "MENU_ADVERTISING");
					ShowShopMenu(id);
					if (HAS_M3_AND_M4(id)) {
						if (M3AndM4[id][M3] <= 0) {
							M3AndM4[id][M3] = give_item(id, "weapon_m3");
							cs_set_user_bpammo(id, CSW_M3, 32);
						}
						if (M3AndM4[id][M4] <= 0) {
							M3AndM4[id][M3] = give_item(id, "weapon_m4a1");
							cs_set_user_bpammo(id, CSW_M4A1, 90);
						}
					}
				}
			}
 
			case JAIL:{
				if (!IsOnJail[id]) {
					IsOnJail[id] = true;
					if (HAS_GET_OUT_OF_JAIL(id)) {
						client_print(id, print_chat, "%L", id, "LOST_GET_OUT_OF_JAIL");
						entity_set_vector(id, EV_VEC_angles, Float:{0.0, 270.0, 0.0});
						entity_set_int(id, EV_INT_fixangle, 1);
						entity_set_origin(id, Float:{-405.0, 2900.0,720.0});
						SET_GET_OUT_OF_JAIL(id, false);
					}
					AnnounceJailEnter(id);
				}
			}
		}
		LastTouch[id][location] = gametime + 1.0;
	}
}
 
public FwdPlayerSpawn(id) {
	if(!is_user_alive(id))
		return HAM_IGNORED;
 
	for (new i = 0; i < sizeof(LastTouch[]); ++i) {
		LastTouch[id][i] = 0.0;
	}
	IsOnMario[id] = false;
	IsOnJail[id] = false;
 
	if (!HAS_M3_AND_M4(id)) {
		if (M3AndM4[id][M3] > 0)
			remove_entity(M3AndM4[id][M3]);
		if (M3AndM4[id][M4] > 0)
			remove_entity(M3AndM4[id][M4]);
	}
 
	if (!HAS_EXTRA_ARMOR(id) && get_user_armor(id) > 100) {
		new CsArmorType:armorType;
		cs_get_user_armor(id, armorType);
		cs_set_user_armor(id, 100, armorType);
	}
 
	return HAM_HANDLED;
}
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// COMMANDS
 
public ShowShopMenu(id) {
	if (!IsOnMario[id]) {
		client_print(id, print_chat, "%L", id, "NOT_ON_MARIO");
		return PLUGIN_HANDLED;
	}
 
	new menuStr[50]; // used by macro, do NOT rename or delete
	ML("MENU_TITLE");
 
	new menu = menu_create(menuStr, "ShopMenuHandler");
 
	new const mlPrefix[] = "MENU_ITEM_"; 
	new mlKey[12], nbToStr[2];
 
	for(new i = 1; i <= MAX_MENU_ITEMS; ++i) {
		copy(mlKey, charsmax(mlKey), mlPrefix);
		num_to_str(i, nbToStr, charsmax(nbToStr));
		add(mlKey, charsmax(mlKey), nbToStr);
 
		ML(mlKey);
		menu_additem(menu, menuStr, nbToStr);
	}
 
	menu_setprop(menu, MPROP_EXIT, 0);
 
	menu_display(id, menu);
 
	return PLUGIN_HANDLED;
}
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
 
public ShopMenuHandler(id, menu, item) {
	if (item == MENU_EXIT) {
		menu_destroy(menu);
		return;
	}
 
	new data[3], name[64], access, callback;
	menu_item_getinfo(menu, item, access, data, charsmax(data), name, charsmax(name), callback);
	new key = str_to_num(data);
 
	new cost;
	cost = COST[key-1];
 
	if (UserItems[id] & (1 << key-1)) {
		client_print(id, print_chat, "%L", id, "CANT_BUY_ITEM");
		return;
	}
 
	switch (key) {
		case 1: { // Get Out of Jail
			if(CheckAndSubtractUserMoney(id, cost)) {
				SET_GET_OUT_OF_JAIL(id, true);
				client_print(id, print_chat, "%L", id, "GET_OUT_OF_JAIL");
			}
		}
 
		case 2: { // Health Reload
			if (CheckAndSubtractUserMoney(id, cost)) {
				set_user_health(id, 100);
				client_print(id, print_chat, "%L", id, "HEALTH_RELOAD");
			}
		}
 
		case 3: { // Extra Armor
			if (CheckAndSubtractUserMoney(id, cost)) {
				SET_EXTRA_ARMOR(id, true);
 
				new CsArmorType:armorType;
				cs_get_user_armor(id, armorType);
				if (armorType != CS_ARMOR_NONE)
					cs_set_user_armor(id, 150, armorType);
 
				UserRoundStart[id][EXTRA_ARMOR] = RoundCount;
				client_print(id, print_chat, "%L", id, "EXTRA_ARMOR");
			}
		}
 
		case 4: { // M3 and M4
			if (CheckAndSubtractUserMoney(id, cost)) {
				SET_M3_AND_M4(id, true);
 
				if ((M3AndM4[id][M3] = give_item(id, "weapon_m3"))) {
					cs_set_user_bpammo(id, CSW_M3, 32);
				}
				if ((M3AndM4[id][M4] = give_item(id, "weapon_m4a1"))) {
					cs_set_user_bpammo(id, CSW_M4A1, 90);
				}
 
				UserRoundStart[id][M3_AND_M4] = RoundCount;
 
				client_print(id, print_chat, "%L", id, "M3_AND_M4");
			}
		}
 
		case 5: { // Eye of the Spy
			if (CheckAndSubtractUserMoney(id, cost)) {
				SET_EYE_OF_THE_SPY(id, true);
				UserRoundStart[id][EYE_OF_THE_SPY] = RoundCount;
				client_print(id, print_chat, "%L", id, "EYE_OF_THE_SPY");
			}
		}
 
		case 6: { // Save
			if (CheckAndSubtractUserMoney(id, cost)) {
				SaveUserItems(id);
				client_print(id, print_chat, "%L", id, "SAVE");
			}
		}
	}
 
	menu_destroy(menu);
	return;
}
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS
 
AnnounceJailEnter(id) {
	for (new i = 1; i < MaxPlayers; i++) {
		if (i == id) continue;
 
		if (HAS_EYE_OF_THE_SPY(i)) {
			new name[32];
			get_user_name(id, name, charsmax(name));
			ColorChat(i, (cs_get_user_team(id) == CS_TEAM_CT ? BLUE : RED), "%L", id, "PLAYER_ON_JAIL", name);
		}
	}
}
 
CheckAndSubtractUserMoney(id, money) {
	new m = cs_get_user_money(id);
	if (m < money) {
		client_print(id, print_center, "%L", id, "NOT_ENOUGH_MONEY");
		return 0;
	}
	cs_set_user_money(id, m-money);
	return 1;
}
 
SaveUserItems(id) {
	new vault = nvault_open("ski_2_shop");
 
	new value[100], i, tmpStr[10];
 
	format(value, charsmax(value), "%d", UserItems[id]);
 
	add(value, charsmax(value), "#");
 
	for (i = 0; i < sizeof(UserRoundStart[]); ++i) {
		num_to_str(UserRoundStart[id][i], tmpStr, charsmax(tmpStr));
		add(value, charsmax(value), tmpStr);
		if (i < sizeof(UserRoundStart[])-1) add(value, charsmax(value), "-");
	}
 
	nvault_set(vault, UserIPs[id], value);
 
	nvault_close(vault);
	return 0;
}
 
LoadUserItems(id) {
	new vault = nvault_open("ski_2_shop");
 
	new value[100], timestamp, i, j, tmpStr[30], tmpStr2[30];
 
	if (!nvault_lookup(vault, UserIPs[id], value, charsmax(value), timestamp))
		return -1;
 
	strtok(value, tmpStr, charsmax(tmpStr), tmpStr2, charsmax(tmpStr2), '#');
 
	UserItems[id] = str_to_num(tmpStr);
 
	for (i += 2; i < strlen(value); ++i) {
		strtok(value[i], tmpStr, charsmax(tmpStr), tmpStr2, charsmax(tmpStr2), '-');
		UserRoundStart[id][j] = str_to_num(tmpStr);
		i += strlen(tmpStr)+2;
	}
 
	nvault_remove(vault, UserIPs[id]);
	nvault_close(vault);
	return 0;
}
 
// took from surf_ski_2 ruleswatcher by xPaw
CreateTrigger( iType, Float:flMins[ 3 ], Float:flMaxs[ 3 ] ) {
	new iEntity = create_entity( "info_target" );
 
	if( !is_valid_ent( iEntity ) ) {
		return 0;
	}
 
	entity_set_string( iEntity, EV_SZ_classname, Classname );
	entity_set_int( iEntity, EV_INT_iuser1, iType );
	entity_set_int( iEntity, EV_INT_movetype, MOVETYPE_FLY );
	entity_set_int( iEntity, EV_INT_solid, SOLID_TRIGGER );
	entity_set_size( iEntity, flMins, flMaxs );
 
	return iEntity;
}
 
CacheCvars() {
	for (new i = 0; i < (sizeof(CVARS)-1)/2; i++) {
		COST[i] = get_pcvar_num(CVARS[i][cvPtr]);
		DURATION[i][duration] = get_pcvar_num(CVARS[i+1][cvPtr]);
	}
}
 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// COLORCHAT FUNCTIONS
 
ColorChat(id, Color:type, const msg[], {Float,Sql,Result,_}:...) {
	if( !get_playersnum() ) return;
 
	new message[256];
 
	switch(type) {
		case NORMAL: { // clients scr_concolor cvar color
			message[0] = 0x01;
		}
		case GREEN: { // Green
			message[0] = 0x04;
		}
		default: {// White, Red, Blue
			message[0] = 0x03;
		}
	}
 
	vformat(message[1], 251, msg, 4);
 
	// Make sure message is not longer than 192 character. Will crash the server.
	message[192] = '^0';
 
	new team, ColorChange, index, MSG_Type;
 
	if(id) {
		MSG_Type = MSG_ONE;
		index = id;
	} else {
		index = FindPlayer();
		MSG_Type = MSG_ALL;
	}
 
	team = get_user_team(index);
	ColorChange = ColorSelection(index, MSG_Type, type);
 
	ShowColorMessage(index, MSG_Type, message);
 
	if(ColorChange) {
		Team_Info(index, MSG_Type, TeamName[team]);
	}
}
 
ShowColorMessage(id, type, message[]) {
	static bool:saytext_used;
	static get_user_msgid_saytext;
	if(!saytext_used)
	{
		get_user_msgid_saytext = get_user_msgid("SayText");
		saytext_used = true;
	}
	message_begin(type, get_user_msgid_saytext, _, id);
	write_byte(id);
	write_string(message);
	message_end();	
}
 
Team_Info(id, type, team[]) {
	static bool:teaminfo_used;
	static get_user_msgid_teaminfo;
	if(!teaminfo_used) {
		get_user_msgid_teaminfo = get_user_msgid("TeamInfo");
		teaminfo_used = true;
	}
	message_begin(type, get_user_msgid_teaminfo, _, id);
	write_byte(id);
	write_string(team);
	message_end();
 
	return 1;
}
 
ColorSelection(index, type, Color:Type) {
	switch(Type) {
		case RED: {
			return Team_Info(index, type, TeamName[1]);
		}
		case BLUE: {
			return Team_Info(index, type, TeamName[2]);
		}
		case GREY: {
			return Team_Info(index, type, TeamName[0]);
		}
	}
 
	return 0;
}
 
FindPlayer() {
	new i = -1;
 
	while(i <= get_maxplayers()) {
		if(is_user_connected(++i))
			return i;
	}
 
	return -1;
}