#include <amxmodx>
#include <amxmisc>
#include <fakemeta_util>
#include <engine>
#include <hamsandwich>

#define PLUGIN	"[AMXX] Spawns Manager"
#define VERSION	"1.2"
#define AUTHOR	"Re.Act!ve"

// CS Teams
enum
{
	FM_CS_TEAM_UNASSIGNED = 0,
	FM_CS_TEAM_T,
	FM_CS_TEAM_CT,
	FM_CS_TEAM_SPECTATOR
}
const OFFSET_LINUX = 5 // offsets 5 higher in Linux builds
const OFFSET_CSTEAMS = 114

// Number count team spawns
#define CURRENT_TEAM_SPAWNS 		2
// Max Spawns Number of Team's
#define MAX_TEAM_SPAWNS 			16
#define ACC_LEVEL ADMIN_IMMUNITY
#define STATIC_ANIMATION 			0
#define SPRLINE "sprites/laserbeam.spr"
#define SPAWN_CLASSNAME 	"sm_spawn_editor"

#define TIME_REFRESH 0.1

new is_map_spawned, gCurrentSpawn[CURRENT_TEAM_SPAWNS], gCurrentTeam, g_Spawns[CURRENT_TEAM_SPAWNS], gCurrentEntity[CURRENT_TEAM_SPAWNS][MAX_TEAM_SPAWNS];
new cvar_enable, g_MaxPlayers, gCache_Enable, g_TotalSpawns[CURRENT_TEAM_SPAWNS];
new Float:g_SpawnVecs[CURRENT_TEAM_SPAWNS][MAX_TEAM_SPAWNS][3];
new Float:g_SpawnAngles[CURRENT_TEAM_SPAWNS][MAX_TEAM_SPAWNS][3];
new Float:g_SpawnVAngles[CURRENT_TEAM_SPAWNS][MAX_TEAM_SPAWNS][3];
new bool:gMenuEnable[33], spr_line, Float:_gTimeRefresh;

// ====== MODELS (Количество моделей должно быть точно столько же, сколько константа CURRENT_TEAM_SPAWNS =====//
// Модели на замену спавнов (для видимости)
new const spawn_models[][] = {
	"models/player/leet/leet.mdl",		// team 1
	"models/player/gign/gign.mdl"	// team 2
}
// Имена команд, если игра не CS
/*new const team_names[][] = {
	"leet",	// team 1
	"clear"	// team 2
}*/

// ====== MODELS END =====//

public plugin_init()
{
	register_plugin(PLUGIN , VERSION , AUTHOR);
	RegisterHam(Ham_Spawn, "player", "fw_PlayerSpawn_Post", 1)
	register_forward(FM_PlayerPreThink, "player_think")
	register_clcmd("spawns_editor", "clcmd_spawns_editor")
	cvar_enable = register_cvar("amx_spawns_manager_enable", "1")
	register_dictionary("amx_spawns_manager.txt");

	// Load Map File
	new mapname[32]
	get_mapname(mapname, 31)
	load_file_map_spawns(mapname)
	g_MaxPlayers = get_maxplayers()
	//gLoadSpawns = false;

	gCurrentTeam = 1;
	set_task(0.4, "cache_cvars")
}

public cache_cvars() {
	gCache_Enable = get_pcvar_num(cvar_enable)
	LoadSpawn_Origins()
}

public plugin_precache() {
	for(new i=0;i<sizeof spawn_models;i++)
		precache_model(spawn_models[i])

	spr_line = precache_model(SPRLINE)
}

public player_think(id)
{
	if(!is_user_alive(id) || !gMenuEnable[id]) 
		return

	if(get_gametime()<_gTimeRefresh)
		return

	new entity = gCurrentEntity[gCurrentTeam-1][gCurrentSpawn[gCurrentTeam-1]];
	if(g_Spawns[gCurrentTeam-1]>0 && is_valid_ent(entity)) {
		new Float:EntOrigin[3], Float:idOrigin[3]
		pev(id, pev_origin, idOrigin)
		pev(entity, pev_origin, EntOrigin)
		message_begin(MSG_ONE_UNRELIABLE ,SVC_TEMPENTITY,{0,0,0},id)
		write_byte(TE_BEAMPOINTS) // TE_BEAMPOINTS = 0
		write_coord(floatround(idOrigin[0])) // start position
		write_coord(floatround(idOrigin[1]))
		write_coord(floatround(idOrigin[2]))
		write_coord(floatround(EntOrigin[0])) // end position
		write_coord(floatround(EntOrigin[1]))
		write_coord(floatround(EntOrigin[2]))
		write_short(spr_line) // sprite index
		write_byte(1) // starting frame
		write_byte(0) // frame rate in 0.1's
		write_byte(1) // life in 0.1's
		write_byte(5) // line width in 0.1's
		write_byte(0) // noise amplitude in 0.01's
		write_byte(200)
		write_byte(200)
		write_byte(200)
		write_byte(155) // brightness)
		write_byte(0) // scroll speed in 0.1's
		message_end()

		_gTimeRefresh=get_gametime()+TIME_REFRESH;
	}
}

load_file_map_spawns(const mapname[])
{
	// Build customization file path
	new path[64]
	get_configsdir(path, charsmax(path))
	format(path, charsmax(path), "%s/spawns_editor/%s_spawns.cfg", path, mapname)
	
	// File not present
	if (!file_exists(path))
	{
		is_map_spawned = 0;
		return 0;
	}

	// True is cfg file spawns
	is_map_spawned = 1;
	
	new team
	// Open customization file for reading
	new file = fopen(path, "rt")

	team = 0;
	new Data[124], len
	new line = 0
	new pos[12][8]
   		
	while( (line = read_file(path , line , Data , 123 , len) ) != 0 ) 
	{
		if (strlen(Data)<2 || Data[0] == '[' || Data[0] == '/') {
			//g_TotalSpawns = 0;
			continue;
		}

		parse(Data, pos[0], 7, pos[1], 7, pos[2], 7, pos[3], 7, pos[4], 7, pos[5], 7, pos[6], 7, pos[8], 7, pos[9], 7, pos[10], 7);
		team = str_to_num(pos[0]);
		// Origin
		g_SpawnVecs[team][g_TotalSpawns[team]][0] = str_to_float(pos[1])
		g_SpawnVecs[team][g_TotalSpawns[team]][1] = str_to_float(pos[2])
		g_SpawnVecs[team][g_TotalSpawns[team]][2] = str_to_float(pos[3])
		
		//Angles
		g_SpawnAngles[team][g_TotalSpawns[team]][0] = str_to_float(pos[4])
		g_SpawnAngles[team][g_TotalSpawns[team]][1] = str_to_float(pos[5])
		g_SpawnAngles[team][g_TotalSpawns[team]][2] = str_to_float(pos[6])
		
		//v-Angles
		g_SpawnVAngles[team][g_TotalSpawns[team]][0] = str_to_float(pos[8])
		g_SpawnVAngles[team][g_TotalSpawns[team]][1] = str_to_float(pos[9])
		g_SpawnVAngles[team][g_TotalSpawns[team]][2] = str_to_float(pos[10])
		
		//Team - ignore - 7
		g_TotalSpawns[team]++
	}
	if(file) fclose(file)

	return 1;
}

public clcmd_spawns_editor(id)
{
	if( get_user_flags(id) & ACC_LEVEL )
	{
		//if(is_map_spawned && !gLoadSpawns)
		//	show_loading_menu(id)
		//else 
		//{
		gMenuEnable[id] = true;
		VisibleEntity_Spawns(0)
		show_spawns_menu(id)
		//}
	}
}

// Ham Player Spawn Post Forward
public fw_PlayerSpawn_Post(id)
{
	if(!is_map_spawned || !gCache_Enable) 
		return HAM_IGNORED

	VisibleEntity_Spawns(1)

	new team, gNum
	// If CS Teams
	if(fm_cs_get_user_team(id) == FM_CS_TEAM_T) team = 0;
	else if(fm_cs_get_user_team(id) == FM_CS_TEAM_CT) team = 1;

	// If HL Teams
	//new modelname[16]
	//entity_get_string(id, EV_SZ_model, modelname, charsmax(modelname)
	//for(new i=0;i<sizeof team_names;i++)
	//	if(equal(modelname, team_names[i]) team = i;

	gNum = ClearSpawn_index(team);
	if(gNum != -1) {
		entity_set_origin(id, g_SpawnVecs[team][gNum])
		entity_set_vector(id, EV_VEC_angles, g_SpawnAngles[team][gNum])
		entity_set_vector(id, EV_VEC_v_angle, g_SpawnVAngles[team][gNum])
		//client_print(id, print_chat, "SPAWN PLACE")
	}

	return HAM_IGNORED
}

LoadSpawn_Origins()
{
	for(new team=0;team<CURRENT_TEAM_SPAWNS;team++) {
		for(new i=0;i<g_TotalSpawns[team];i++) {
			CreateSpawnOrigin(0, g_SpawnVecs[team][i], g_SpawnAngles[team][i], team)
		}
	}

	//client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_LOAD_SPAWNS")
	//show_spawns_menu(id)
	//gLoadSpawns = true;
	VisibleEntity_Spawns(1)
	return PLUGIN_HANDLED;
}

show_spawns_menu(id)
{
	static menuid, menu[128], item, buffer[2]
	
	formatex(menu, charsmax(menu), "\r%L", id, "MSE_TITLE_MENU")

	menuid = menu_create(menu, "handle_spawns_menu")
	
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION1", gCurrentTeam)
	menu_additem(menuid, menu, buffer)
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION2", (g_Spawns[gCurrentTeam-1]>0)?(gCurrentSpawn[gCurrentTeam-1]+1):gCurrentSpawn[gCurrentTeam-1], g_Spawns[gCurrentTeam-1])
	menu_additem(menuid, menu, buffer)
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION3")
	menu_additem(menuid, menu, buffer)
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION4")
	menu_additem(menuid, menu, buffer)
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION5")
	menu_additem(menuid, menu, buffer)
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION6")
	menu_additem(menuid, menu, buffer)
	formatex(menu, charsmax(menu), "%L", id, "MSE_TITLE_OPTION7")
	menu_additem(menuid, menu, buffer)

	buffer[0] = item
	buffer[1] = 0
	
	// Back - Next - Exit
	formatex(menu, charsmax(menu), "%L", id, "MSE_MENU_BACK")
	menu_setprop(menuid, MPROP_BACKNAME, menu)
	formatex(menu, charsmax(menu), "%L", id, "MSE_MENU_NEXT")
	menu_setprop(menuid, MPROP_NEXTNAME, menu)
	formatex(menu, charsmax(menu), "%L", id, "MSE_MENU_EXIT")
	menu_setprop(menuid, MPROP_EXITNAME, menu)
	
	menu_display(id, menuid)
}

public handle_spawns_menu(id, menuid, item)
{
	// Menu was closed
	if (item == MENU_EXIT)
	{
		gMenuEnable[id] = false;
		VisibleEntity_Spawns(1)
		menu_destroy(menuid)
		return PLUGIN_HANDLED;
	}


	// Retrieve player id
	/*static buffer[2], dummy, item
	menu_item_getinfo(menuid, item, dummy, buffer, charsmax(buffer), _, _, dummy)
	new data[6], iName[64];
	new access, callback;
    
	menu_item_getinfo(menuid, item, access, data,5, iName, 63, callback);
	item = buffer[0]*/

	switch(item)
	{
		case 0: {
			// If team < 0
			if(gCurrentTeam < 1) gCurrentTeam = 1;

			if(gCurrentTeam < CURRENT_TEAM_SPAWNS) gCurrentTeam+=1
			else gCurrentTeam = 1;

		}
		case 1: {
			if(gCurrentSpawn[gCurrentTeam-1] < (g_Spawns[gCurrentTeam-1]-1)) gCurrentSpawn[gCurrentTeam-1]+=1
			else gCurrentSpawn[gCurrentTeam-1] = 0;
		}
		case 2: {
			// Create Respawn Origin
			new Float:Origin[3]
			pev(id, pev_origin, Origin)
			if(g_Spawns[gCurrentTeam-1]<MAX_TEAM_SPAWNS) CreateSpawnOrigin(id, Origin, Origin, (gCurrentTeam-1))
			else client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_ERROR_CREATE")
		}
		case 3: {
			// Delete Respawn
			DeleteSpawnOrigin(id, (gCurrentTeam-1))
		}
		case 4: {
			// Replace Respawn
			ReplaceSpawnOrigin(id, (gCurrentTeam-1))
		}
		case 5: {
			DeleteAllRespawns(id)
		}
		case 6: {
			Save_SpawnsFile(id)
		}
	}

	//UpdateSpawnModels()

	menu_destroy(menuid)
	show_spawns_menu(id)
	return PLUGIN_HANDLED;
}

DeleteAllRespawns(id)
{
	new nextitem  = find_ent_by_class(-1, SPAWN_CLASSNAME)
	while(nextitem)
	{
		remove_entity(nextitem)
		nextitem = find_ent_by_class(-1, SPAWN_CLASSNAME)
	}

	for(new i=0;i<CURRENT_TEAM_SPAWNS;i++) {
		g_Spawns[i] = 0;
		g_TotalSpawns[i] = 0;
		gCurrentSpawn[i] = 0;
	}

	client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_DELETE_SPAWNS")
}

VisibleEntity_Spawns(trans)
{
	new ent;
	for(new team=0;team<CURRENT_TEAM_SPAWNS;team++)
		for(new i=0;i<g_Spawns[team];i++) {
			ent = gCurrentEntity[team][i];
			if(is_valid_ent(ent)) {
				if(trans) fm_set_rendering(ent,kRenderFxGlowShell, 0, 0, 0, kRenderTransAlpha, 0)
				else fm_set_rendering(ent, kRenderFxGlowShell, 0, 0, 0, kRenderNormal, 16);
			}
		}
}

CreateSpawnOrigin(id, Float:Origin[3], Float:Angles[3], team)
{
	new Float:fOrigin[3],Float:fAngles[3]
	if(id != 0) {
		entity_get_vector(id, EV_VEC_origin, fOrigin)
		entity_get_vector(id, EV_VEC_angles, fAngles)
		fOrigin[2] += 5.0 //offset Z
	}
	else {
		fOrigin[0] = Origin[0];			fOrigin[1] = Origin[1];			fOrigin[2] = Origin[2]
		fAngles[0] = Angles[0];			fAngles[1] = Angles[1];			fAngles[2] = Angles[2];
	}
	
	new entity = create_entity("info_target")
	if (entity){
		entity_set_string(entity, EV_SZ_classname, SPAWN_CLASSNAME)
		entity_set_model(entity, spawn_models[team])
		entity_set_origin(entity, fOrigin)
		entity_set_vector(entity, EV_VEC_angles, fAngles)
		entity_set_int(entity, EV_INT_sequence, STATIC_ANIMATION)
		entity_set_int(entity,EV_INT_iuser2,team)
		entity_set_int(entity, EV_INT_solid, SOLID_NOT)
		gCurrentEntity[team][g_Spawns[team]] = entity;
		g_Spawns[team]+=1;
		client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_CREATE_SPAWN", g_Spawns[team], team)
		ReCounterSpawns(team);
	}
	else client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_ERROR_CREATE")
}

ReplaceSpawnOrigin(id, team)
{
	new entity = gCurrentEntity[team][gCurrentSpawn[team]];
	if(is_valid_ent(entity))
	{
		new Float:fOrigin[3], Float:fAngles[3]
		entity_get_vector(id, EV_VEC_origin, fOrigin)
		entity_get_vector(id, EV_VEC_angles, fAngles)

		entity_set_origin(entity, fOrigin)
		entity_set_vector(entity, EV_VEC_angles, fAngles)
		client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_SPAWN_REPLACE", (gCurrentSpawn[team]+1), team)
	}
	else client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_ERROR_REPLACE")
}

DeleteSpawnOrigin(id, team)
{
	new entity = gCurrentEntity[team][gCurrentSpawn[team]];
	if(is_valid_ent(entity)) {
		new team = entity_get_int(entity,EV_INT_iuser2)
		client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_DELETE_SPAWN", (gCurrentSpawn[team]+1), team)
		remove_entity(entity);
		ReCounterSpawns(team);
	}
	else client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_ERROR_SPAWN")
}

stock Save_SpawnsFile(id)
{
	new mapname[32]
	get_mapname(mapname, 31)
	// Build customization file path
	new path[64]
	get_configsdir(path, charsmax(path))
	format(path, charsmax(path), "%s/spawns_editor/%s_spawns.cfg", path, mapname)
	if (file_exists(path))
		delete_file(path)
	
	new line[128]
	get_mapname(mapname,31)
	format(line,127,"/* %s player  Map spawns */ Spawns Manager File [by Re.Act!ve]",mapname)
	write_file(path, line, -1)
	
	new entity,team
	while ((entity = find_ent_by_class(entity, SPAWN_CLASSNAME))){
		team = entity_get_int(entity,EV_INT_iuser2)
		Point_WriteToFlie(path,team,entity,1)
	}

	client_print(id, print_chat, "[SM] %L", LANG_PLAYER, "MSE_SAVE_SPAWN")

	return 1
}

// store one entity data to file
stock Point_WriteToFlie(Flie[],team,entity,saveformat)
{
	new line[128],sTeam[32]
	new nOrigin[3],nAngles[3]
	new Float:fOrigin[3],Float:fAngles[3]
	
	entity_get_vector(entity, EV_VEC_origin, fOrigin)
	entity_get_vector(entity, EV_VEC_angles, fAngles)
	FVecIVec(fOrigin,nOrigin)
	FVecIVec(fAngles,nAngles)
	if (nAngles[1]>=360) nAngles[1] -= 360
	if (nAngles[1]<0) nAngles[1] += 360
	
	if (saveformat==1){ // write for plugin using format
		format(line, 127, "%d %d %d %d %d %d %d", team, nOrigin[0], nOrigin[1], nOrigin[2], 0, nAngles[1], 0)
		write_file(Flie, line, -1)
	}
	else if (saveformat==2){ // write for ripent.exe format
		if (team==1) sTeam = "info_player_deathmatch"
		else sTeam = "info_player_start"
		format(line, 127,"{^n  ^"classname^" ^"%s^"",sTeam)
		write_file(Flie, line , -1)
		format(line, 127, "  ^"origin^" ^"%d %d %d^"", nOrigin[0], nOrigin[1], nOrigin[2])
		write_file(Flie, line, -1)
		format(line, 127, "  ^"angle^" ^"0 %d 0^"^n}^n", nAngles[1])
		write_file(Flie, line, -1)
	}
}

stock ReCounterSpawns(team)
{
	new gSp[MAX_TEAM_SPAWNS], num=0
	for(new i=0;i<g_Spawns[team];i++) {
		if(is_valid_ent(gCurrentEntity[team][i])) {
			gSp[num] = gCurrentEntity[team][i];
			num=num+1;
		}
	}

	for(new i=0;i<num;i++)
		gCurrentEntity[team][i] = gSp[i];

	g_Spawns[team] = num;
}

stock ClearSpawn_index(team)
{
	new spawn_id, i, temp, g_spawn[MAX_TEAM_SPAWNS];
	temp=0;

	for(i=0;i<g_Spawns[team];i++) {
		if(func_SpawnPlace(i, team)) {
			g_spawn[temp]=i;
			temp+=1;
		}
	}
	// random spawn origin
	spawn_id = g_spawn[random_num(0, (temp-1))]

	if(is_valid_ent(gCurrentEntity[team][spawn_id]))
		return spawn_id;

	return -1;	
}

func_SpawnPlace(spawn, team)
{
	if(!is_valid_ent(gCurrentEntity[team][spawn])) 
		return false

	new Float:fOrigin[3], Float:entOrigin[3]
	for(new i=0;i<=g_MaxPlayers;i++)
	{
		pev(i, pev_origin, fOrigin)
		pev(gCurrentEntity[team][spawn], pev_origin, entOrigin)
		if( get_distance_f(fOrigin, entOrigin) < 25.0)
			return false
	}

	return true;
}

// Get User Team
stock fm_cs_get_user_team(id)
{
	return get_pdata_int(id, OFFSET_CSTEAMS, OFFSET_LINUX);
}