/*
	
	Player Model
	=================
	by commonbullet
	based on scriptyboy's script & idea
	
	written in AMXX-Studio
	
	description:
	------------
	  - custom models
	
	forum topic: 	  
	------------
	  http://www.amxmodx.org/forums/viewtopic.php?
	  
	requirements:
	-------------
	 - requires "cstrike" module
	 
	usage:
	------
	 look at models.ini
*/

#include <amxmodx> 
#include <amxmisc> 
#include <cstrike>

// ---- [debug settings]

#define DEBUG_MODE 1

#define DEBUG_LOGFILE "playermodel.log"
#define DEBUG_MSG_BEGIN "========= PLAYERMODEL DEBUG ========="
#define DEBUG_MSG_END   "====================================="
#define DEBUG_MSG_ERROR "[PLAYERMODEL ERROR] "
#define DEBUG_MSG_LOG "[PLAYERMODEL LOG] "

// ---- [end]


// ---- [console messages]

#define CONSOLE_MSG_HLIN "----------------------------------------"
#define CONSOLE_MSG_HEAD "[PLAYERMODEL - Models.ini]"
//#define CONSOLE_MSG_FOOT "[PLAYERMODEL - End of Models.ini]"
#define CONSOLE_MSG_MODELS_LIST "[MODELS]"

#define CONSOLE_MSG_DEL_INVALID "Invalid number. Type amx_playermodel_ini to get models index."
#define CONSOLE_MSG_DEL_DELETED "Line %s has been deleted."
#define CONSOLE_MSG_DEL_UPDATED "Current game model settings have been updated"
#define CONSOLE_MSG_SET_RESTART "You need to restart map to apply changes."
#define CONSOLE_MSG_SET_UPDATE "Models.ini has been updated."
#define CONSOLE_MSG_SET_ADD "Line added to Models.ini."
#define CONSOLE_MSG_SET_NORESTART "Model(s) have been set with precached files - no need to restart."
#define CONSOLE_MSG_SET_MODEL_INVALID "Invalid Model: %s. ^nType 'amx_playermodel_models to get a list of valid models"
#define CONSOLE_MSG_SET_ID_INVALID "Invalid ID"
#define CONSOLE_MSG_MODELSLIST_FOOT "(*) - Models that have been precached in this session."
#define CONSOLE_MSG_ACCESS_INVALID "You don't have admin access change/view models.."
#define CONSOLE_MSG_INI_CREATE "** Models.ini has just been created."
// ---- [end]


// ---- [constants indexes]

#define LN_TYPE_BLANK 0
#define LN_TYPE_VALID 1

#define AUTH_TYPE_BOTNAME 0
#define AUTH_TYPE_STEAMID 1
#define AUTH_TYPE_IP 2

// ---- [end]


// ---- [general settings]

#define MODELSINI_SCRATCH_LIMIT 9 //blank lines may be overwritten in models.ini after this line number

 //max models in models.ini..
#define MAX_MODELS 64
#define MAX_PLAYERS 33

// max cs and cz models, 
// shouldn't be changed unless there's a valve update...
#define MAX_CZ_MODELS 5
#define MAX_CS_MODELS 4

#define TAG_BOTDEFAULT "*BOTDEFAULT"
#define TAG_DEFAULT "*DEFAULT"
#define TAG_FORCEPRECACHE_CHAR "*"
#define TAG_COMMANDSET_BY_STEAMID "&"
#define TAG_COMMANDSET_BY_IP "%"
#define TAG_COMMANDSET_NULL "_"
#define TAG_REPLACE "*REPLACE"

#define STATUS_MODEL_NOTSET 0
#define STATUS_MODEL_SETBYID 1
#define STATUS_MODEL_BOTDEFAULT 2
#define STATUS_MODEL_DEFAULT 4
#define STATUS_MODEL_REPLACEMENT 8
#define STATUS_MODEL_NOMODEL 16
#define STATUS_MODEL_RESET 32

#define SETMODEL_RESET -4
#define SETMODEL_REPLACE -3
#define SETMODEL_DEFAULT -2
#define SETMODEL_BOTDEFAULT -1

#define NULLINDEX 8080 //-1 is being used...
#define REPLACEMENT_SIGN "$"


// ---- [end]


// Globals
new g_PlayerId[MAX_MODELS][25] // list of players ids
new g_PrecachedList[MAX_MODELS][32]
new g_PrecachedCount
new g_ModelInfo[2][MAX_MODELS] // info about the models for each team
new g_ModelsCount //models count    

new g_ModelStatus[MAX_PLAYERS] // array to check if a player model has been set, so it won't be set
		      //  unless it's necessary
new g_BotsModel[2] = {-1,-1}// bots' unique model
new g_DefaultModel[2] = {-1, -1} // human's default model

#if DEBUG_MODE == 1
new logmsg[192]
#endif

public plugin_init() { 
	
	register_plugin("Player Model", "2.13c", "commonbullet")
	
	register_event("ResetHUD", "set_players_models", "be")
	
	register_logevent ("auto_balance_triggered", 4, "3=auto")
	
	register_concmd ("amx_playermodel_ini","list_ini",ADMIN_KICK,"list models.ini")
	register_concmd ("amx_playermodel_del","del_line",ADMIN_KICK,"<line>")
	register_concmd ("amx_playermodel_models","list_models",ADMIN_KICK,"list valid models")
	register_concmd ("amx_playermodel_set","set_line",ADMIN_KICK,"<*modelname> | <steam|id|ip|botname> <model1> <model2>")
	register_cvar ("amx_playermodel_round_checkid", "0")

}


// ---- [Models.ini handlers]

public list_ini (id,level,cid) {
	
	if (!cmd_access(id, level, cid, 0)) {
		console_print(id, CONSOLE_MSG_ACCESS_INVALID)
		return PLUGIN_HANDLED
	}
	
	new path[66]
	new validlines[MAX_MODELS]
	new vlinesnum
	new linestr[100]
	new len = 0
	
	get_inipath(path,65)
	get_ini_lines(validlines,vlinesnum,LN_TYPE_VALID)
	
	console_print(id, "^n%s^n%s^n%s",CONSOLE_MSG_HLIN,CONSOLE_MSG_HEAD,CONSOLE_MSG_HLIN)
	
	for (new i = 0;i<vlinesnum;i++){
		read_file (path,validlines[i],linestr,99,len)
		console_print(id, "[%d]^t^t%s",i+1,linestr)
	}
	console_print(id, CONSOLE_MSG_HLIN)	
	
	return PLUGIN_HANDLED
	
}



public del_line (id,level,cid){
	
	if (!cmd_access(id, level, cid, 0)){		
		console_print(id, CONSOLE_MSG_ACCESS_INVALID)
		return PLUGIN_HANDLED
	}
	
	new numst[2]
	new num
	new bool:isvalidnum
	new validlines[MAX_MODELS]
	new vlinesnum
	new msg[128]
	new bool:updated
	
	read_argv (1,numst,2)
	num = str_to_num(numst)
	
	if (strlen(numst)==0 || !num){
		list_ini(id,level,cid)
		console_print(id, CONSOLE_MSG_DEL_INVALID)
		return PLUGIN_HANDLED
	}
	
	else {
	
		get_ini_lines(validlines,vlinesnum,LN_TYPE_VALID)		
		isvalidnum = (num <= vlinesnum) ? true : false
		if (isvalidnum) {
			
			new authid[20]
			new linestr[30]
			new path[66]
			new len			
			
			get_inipath(path,65)
			read_file (path,validlines[num-1],linestr,29,len)
			
			parse (linestr,authid,19)			
			write_file (path, "", validlines[num-1])
			
			format (msg,127,CONSOLE_MSG_DEL_DELETED,num)
			console_print(id, "!! %s", authid)
			if(equali(authid,TAG_BOTDEFAULT)) {
				new bots[32]
				new botnum
				
				get_players (bots,botnum,"d")			
				g_BotsModel[0] = -1
				g_BotsModel[1] = -1
				updated = true
				
				for (new i=0;i<botnum;i++) {									
					g_ModelStatus[bots[i]] |= STATUS_MODEL_RESET				
					set_players_models(bots[i])					
				}				
			}
			
			else if(equali(authid, TAG_DEFAULT)) {
				new players[32]
				new playersnum
				
				get_players(players, playersnum, "c")
				g_DefaultModel[0] = -1
				g_DefaultModel[1] = -1
				updated = true
				for(new i = 0; i < playersnum; i++) {
					g_ModelStatus[players[i]] |= STATUS_MODEL_RESET
					set_players_models(players[i])
				}					
			}
			
			else if(equal(authid, TAG_REPLACE)) {
				new players[32]
				new playersnum
				new modelname[32]
				new modelfind[32]
				new modelreplace[32]		
		
				parse(linestr, authid, 1, modelfind, 31, modelreplace, 31)
				
				for(new ind = 0; ind < MAX_MODELS; ind++) {
					if(!equal(g_PlayerId[ind], REPLACEMENT_SIGN))
						continue
					if(equal(g_ModelInfo[0][ind], modelfind) && equal(g_ModelInfo[1][ind], modelreplace)) {
						for (new i=ind;i<(g_ModelsCount-1);i++){
							g_PlayerId[i] = g_PlayerId[i+1]
							g_ModelInfo[i] = g_ModelInfo[i+1]
							g_ModelsCount--
							break
						}
					}
				}
				
				get_players(players, playersnum)
				for(new i = 0; i < playersnum; i++) {
					cs_get_user_model(players[i], modelname, 31)
					if(equal(modelname, modelreplace)) {
						g_ModelStatus[players[i]] |= STATUS_MODEL_RESET
						set_players_models(players[i])
					}
				}
				updated = true
			}
			
			else if (!(strfind(authid,TAG_FORCEPRECACHE_CHAR) > -1) && strlen(authid)>1) {							
				new ind = find_registered_id(authid)			
				if (ind > -1){
					
					new players[32]
					new playersnum
					new authtype
					new checkid					
										
					for (new i=ind;i<(g_ModelsCount-1);i++){
						g_PlayerId[i] = g_PlayerId[i+1]
						g_ModelInfo[i] = g_ModelInfo[i+1]
					}
					
					g_ModelsCount--				
					get_players (players,playersnum)
			
					for (new i=0;i<playersnum;i++) {						
						check_authtype(players[i],authid,authtype,checkid)						
						if (checkid) {
							g_ModelStatus[players[i]] |= STATUS_MODEL_RESET
							set_players_models(players[i])
							break
						}								
					}
					if (checkid)
						updated = true
				}				
				
			}
			list_ini(id,level,cid)
			console_print(id, CONSOLE_MSG_DEL_DELETED,num)
			if (updated)
				console_print(id, CONSOLE_MSG_DEL_UPDATED)			
			
		}
		else {
			list_ini(id,level,cid)
			console_print(id, CONSOLE_MSG_DEL_INVALID)
			return PLUGIN_HANDLED
		}
	}
	
	
		
	return PLUGIN_HANDLED
	
}

public list_models(id,level,cid){
	
	if (!cmd_access(id, level, cid, 0)){
		console_print(id, CONSOLE_MSG_ACCESS_INVALID)
		return PLUGIN_HANDLED
	}
	
	new models [32][16]
	new teamid
	new len
	new tabcount
	new indexstr[8]
	new msghead[3][] = {
		"CT Default Models^n",
		"T Default Models^n",
		"Available Custom Models^n"
	}
	new msg[256]
	
	for (new i=0;i<3;i++){
		
		switch (i){
			case 0:{				
				teamid = 1
				get_default_models (models,len,teamid)
			}
			case 1:{
				teamid = 0
				get_default_models (models,len,teamid)
			}
			case 2:{
				get_custom_models (models,len,31)
			}
		}
		
		format(msg,96,"%s%s^n",msghead[i],CONSOLE_MSG_HLIN)
		
		for (new q=0;q<len;q++){
			
			strcat(msg,models[q],255)
			
			if (i<2){
				format (indexstr,7," (#%d)",q+1)
				strcat (msg,indexstr,255)
			}
			else if (find_precached_ind(models[q])>-1){
				format(indexstr,7," (*)")
				strcat (msg,indexstr,255)
			}
				
			
			if (tabcount < 3)
				strcat(msg,"^t^t^t^t",255)
			else if ((q+1)<len) {
			
				strcat(msg,"^n",255)
				
				tabcount = 0;
			}
			
			tabcount ++
				
		}
		console_print(id, "%s^n",msg)		
		tabcount = 0
	}
	console_print(id, CONSOLE_MSG_MODELSLIST_FOOT)
		
	return PLUGIN_HANDLED

}

public set_line (id,level,cid){
	
	if (!cmd_access(id, level, cid, 0)){
		console_print(id, CONSOLE_MSG_ACCESS_INVALID)
		return PLUGIN_HANDLED
	}
	
	new arg1[32]
	new arg2[32]
	new arg3[32]
	new forceprecache
	
	read_argv (1,arg1,31)
	read_argv (2,arg2,31)
	read_argv (3,arg3,31)
	
	if (strlen(arg1)==0){
		console_print(id, CONSOLE_MSG_SET_ID_INVALID)
		return PLUGIN_HANDLED
	}
	
	if ((strfind (arg1,TAG_FORCEPRECACHE_CHAR)==0) && (strlen(arg2)==0) && (strlen(arg3)==0) && (!equali(arg1,TAG_BOTDEFAULT))){
				
		substr (arg1,arg3,31,1)
		
		if (is_valid_model(arg3))
			forceprecache = 1
		
		else {
			console_print(id, CONSOLE_MSG_SET_MODEL_INVALID,arg2)
			return PLUGIN_HANDLED
		}		
		
	}
	
	else {
		if (strfind (arg1,TAG_COMMANDSET_BY_STEAMID) ==0){
			
			new temp[32]
			new pid
			
			substr (arg1,temp,31,1)
			
			pid = get_user_index (temp)
			
			if (pid && !is_user_bot(pid))
				get_user_authid(pid,arg1,31)
			
			else {
				console_print(id, CONSOLE_MSG_SET_ID_INVALID)
				return PLUGIN_HANDLED
			}
			
		}
		
		if (strfind (arg1,TAG_COMMANDSET_BY_IP) ==0){
			
			new temp[32]
			new pid
			
			substr (arg1,temp,31,1)
			pid = get_user_index (temp)
			
			if (pid && !is_user_bot(pid))
				get_user_ip (pid,arg1,31)
			
			else{
				console_print(id, CONSOLE_MSG_SET_ID_INVALID)
				return PLUGIN_HANDLED
			}
			
		}
		
		
		
	
		if (!is_valid_model(arg2)){
			console_print(id, CONSOLE_MSG_SET_MODEL_INVALID,arg2)
			return PLUGIN_HANDLED
		}
		
		if ((strlen(arg3)>0) && !is_valid_model(arg3)){
			console_print(id, CONSOLE_MSG_SET_MODEL_INVALID,arg3)
			return PLUGIN_HANDLED
		}
	}
	
	new line	
	new blanklines[MAX_MODELS]
	new blanknum
	new linestr[100]
	new path[66]	
	new msg[128]
	new iniline
	new searchstr[21]	
	
	get_inipath(path,65)
	line = -1
	format (searchstr,20,"^"%s^"",arg1)
	
	iniline = find_line_by_id (searchstr)
	
	if (iniline>-1)
		line = iniline
	else {	
		get_ini_lines (blanklines,blanknum,LN_TYPE_BLANK)		
		if (blanknum && (blanklines[blanknum-1]>=MODELSINI_SCRATCH_LIMIT))
			line = blanklines[blanknum-1]
	}
	
	if (strlen(arg3)>0 && !forceprecache)
		format(linestr,99,"^"%s^" ^"%s^" ^"%s^"",arg1,arg2,arg3)
	
	else if (forceprecache)
		format(linestr,99,"^"%s^"",arg1)
	
	else
		format(linestr,99,"^"%s^" ^"%s^"",arg1,arg2)
	
	
	new Tind 
	new CTind
	new mayupdate // may update without restart
	
	if (strfind(arg2,"#")==0){
		new error
		get_models_by_number(arg2,arg2,arg3,error)
	}
	
	Tind =  (equal(arg2, "_")) ? NULLINDEX : find_precached_ind(arg2)
	CTind = (equal(arg2, "_")) ? NULLINDEX : (strlen(arg3)>0) ? find_precached_ind(arg3) : Tind	
	
	mayupdate = (!forceprecache)? ((Tind>-1) && (CTind>-1)) : ((CTind>-1))
	
	if (mayupdate) {
		
		if (equali(arg1,TAG_DEFAULT)) {			
			new players[32]
			new playernum			
			get_players (players,playernum,"c")			
			g_DefaultModel[0] = Tind
			g_DefaultModel[1] = CTind
			
			for (new i=0; i < playernum; i++) {							
				g_ModelStatus[players[i]] |= STATUS_MODEL_RESET				
				set_players_models(players[i])				
			}
		}
		
		if (equali(arg1,TAG_BOTDEFAULT)){			
			
			new bots[32]
			new botnum
			
			get_players (bots,botnum,"d")			
			g_BotsModel[0] = Tind
			g_BotsModel[1] = CTind
			
			for (new i=0;i<botnum;i++){				
								
				g_ModelStatus[bots[i]] |= STATUS_MODEL_RESET				
				set_players_models(bots[i])				
			}
		}
		
		else if(equal(arg1, TAG_REPLACE)) {
			new players[32]
			new playersnum
			new modelname[32]		
			get_players(players, playersnum)
			
			new slot = find_empty_slot()
			if(slot > -1) {
				format(g_PlayerId[slot], 24, REPLACEMENT_SIGN)
				g_ModelInfo[0][slot] = Tind
				g_ModelInfo[1][slot] = CTind
				for(new i = 0; i < playersnum; i++) {
					cs_get_user_model(players[i], modelname, 31)
					if(equali(modelname, arg2)) {						
						g_ModelStatus[players[i]] |= STATUS_MODEL_RESET
						set_players_models(players[i])
					}
				}
			}
		}
		
		else if (!forceprecache){
		
			new ind = find_registered_id(arg1)
			new players[32]
			new playersnum
			new authtype
			new checkid			
			
			if (ind > -1){
								
				g_ModelInfo[0][ind] = Tind
				g_ModelInfo[1][ind] = CTind							
				format(msg,47,CONSOLE_MSG_SET_UPDATE)
			}
			
			else {				
				copy (g_PlayerId[g_ModelsCount],24,arg1)			
				g_ModelInfo[0][g_ModelsCount] = Tind
				g_ModelInfo[1][g_ModelsCount] = CTind
				ind = g_ModelsCount
				g_ModelsCount ++			
				format(msg,47,CONSOLE_MSG_SET_ADD)				
			}
									
			get_players (players,playersnum)
		
			for (new i=0;i<playersnum;i++){
				
				check_authtype(players[i],g_PlayerId[ind],authtype,checkid)
				
				if (checkid){
					g_ModelStatus[players[i]] |= STATUS_MODEL_RESET
					set_players_models(players[i])
					break
				}			
			}						
		}
		
		strcat (msg,"^n",127)
		strcat (msg,CONSOLE_MSG_SET_NORESTART,127)
		
		
	}
	
	else {
		format(msg,47,CONSOLE_MSG_SET_ADD,127)
		strcat (msg,"^n",127)
		strcat (msg,CONSOLE_MSG_SET_RESTART,127)
	}
	
	write_file (path,linestr,line)
	
	list_ini(id,level,cid)
	console_print(id, msg)
	
	return PLUGIN_HANDLED
	
}

find_empty_slot() {
	for(new i = 0; i < MAX_MODELS; i++) {
		if(!strlen(g_PlayerId[i]))
			return i
	}
	return -1
}

// ---- [End of Models.ini handlers]


// ---- [Shared functions]

get_inipath(path[],len){
	
	get_configsdir( path , len ) 
	format(path , len , "%s/models.ini",path)
	
	if (!file_exists(path)){
		write_file (path,"",-1)
		server_print (CONSOLE_MSG_INI_CREATE)
	}
}

get_ini_lines(validlines[],&vlinesnum,linetype){
	vlinesnum = 0;
	new szFile[66] 
	
	get_inipath (szFile,65)	
	
	new szLine[63] , line = 0, len = 0
	
	while(read_file(szFile, line ++ , szLine , 62 , len) && vlinesnum<MAX_MODELS){
		switch (linetype){
			case LN_TYPE_VALID: {		
				
				if(szLine[0] == ';' || !len) 
					continue
				else {
					validlines[vlinesnum] = line -1
					vlinesnum ++
				}
			}
			case LN_TYPE_BLANK: {
				if (!len){
					validlines[vlinesnum] = line - 1
					vlinesnum ++
				}
			}
		}
			
	}
}

get_default_models(models[][],&len,teamid){
	
	new defaultTmodels [5][] = {		
		"terror",
		"leet",
		"arctic",
		"guerilla",
		"militia"
	}
	new defaultCTmodels[5][] = {
		"urban",
		"gsg9",
		"sas",
		"gign",
		"spetsnaz"
	}
	
	len = (is_running("czero")) ? 5 : 4
	
	for (new i=0;i<len;i++){
		if (teamid){
			copy (models[i],15,defaultCTmodels[i])
			
		}
		else {
			copy (models[i],15,defaultTmodels[i])			
		}
	}
	
}

get_custom_models(custommodels[][],&len,maxlen){
	
	new models[16]
	new modelpath [65]
	new fh = open_dir("models/player",models,31)
	
	next_file (fh,models,15)
	len = 0
	
	while (next_file(fh,models,15)){		
		format (modelpath,64,"models/player/%s/%s%s",models,models,".mdl")
		if (file_exists(modelpath) && (len < maxlen)){
			
			copy(custommodels[len],15,models)
			len ++			
		}
			
	}	
	close_dir(fh)	

}

is_valid_model(model[]){
	
	new bool:isvalid
	new lim = (is_running("czero")) ? 5 : 4
	
	if ( (strfind(model,"#")==0) && (isdigit(model[1])) ){
		new num = str_to_num (model[1])		
		if ((num>=1) && (num<=lim)) 
			isvalid = true
	}
	
	else if(equal(model, "_"))
		isvalid = true
	
	else {		
		for (new j=0;j<3;j++){
			
			new cdmodels[32][16]
			
			if (j<2)
				get_default_models (cdmodels,lim,j)
			
			else
				get_custom_models (cdmodels,lim,31)
			
			for (new i=0;i<lim;i++){
				if (equali(cdmodels[i],model)){
					isvalid = true
					break
				}
			}
			if (isvalid)
				break
		}
	}
	
	return isvalid			
	
}

get_models_by_number(numst[],Tmodel[],CTmodel[],&error=0){
	
	new Default_T_Models [5][16]
	new Default_CT_Models[5][16]
	new dlen
	
	get_default_models (Default_T_Models,dlen,0)
	get_default_models (Default_CT_Models,dlen,1)
	
	if (equal("#1",numst)){
		copy (Tmodel,31,Default_T_Models[0])
		copy (CTmodel,31,Default_CT_Models[0])
		
	}
			
	else if (equal("#2",numst)){
		copy (Tmodel,31,Default_T_Models[1])
		copy (CTmodel,31,Default_CT_Models[1])
	}
	
	else if (equal("#3",numst)){
		copy (Tmodel,31,Default_T_Models[2])
		copy (CTmodel,31,Default_CT_Models[2])
	}
	
	else if (equal("#4",numst)){
		copy (Tmodel,31,Default_T_Models[3])
		copy (CTmodel,31,Default_CT_Models[3])
	}
	
	else if (equal("#5",numst) && is_running("czero")){
		copy (Tmodel,31,Default_T_Models[4])
		copy (CTmodel,31,Default_CT_Models[4])
	}
	
	else error = 1
}

find_registered_id(idstr[]){
	new index = -1
	for (new i=0;i<g_ModelsCount;i++){
		
		if (equali(idstr,g_PlayerId[i])){
			index  = i
			break
		}		
	}
	return index	
}

find_line_by_id(idstr[]){
	
	new szFile[66]
	new idline = -1	
	get_inipath (szFile,65)	
	new szLine[21] , line = 0, len = 0
	new bool:linefound = false
	
	while(read_file(szFile, line ++ , szLine , 20 , len)){
		
		if ((strfind(szLine,idstr)==0)){
			
			if (!linefound){
				idline = line - 1
				linefound = true
			}
			// clean models.ini duplicate ids
			else 
				write_file(szFile,"",line)
		}
		
	}
	
	return idline
}

find_precached_ind(model[]){
	new ind = -1
	for (new i=0;i<g_PrecachedCount;i++){
		if (equali(model,g_PrecachedList[i])){
			ind = i
			break
		}
	}
	return ind
}

check_authtype(id, checkauthid[], &authtype, &checkid=false, authid[50]=""){
	
	authtype = -1
		
	// bots don't have steamid, so they are indexed by name ...
	if (is_user_bot(id)){
		get_user_name(id,authid,49)
		checkid = (equali(authid,checkauthid))
		#if DEBUG_MODE == 1
		authtype = AUTH_TYPE_BOTNAME
		#endif			
	}
	
	//  ... while humans are supposed to have a steamid or a IP.
	else if ( strfind(checkauthid,"STEAM")>-1 || strfind (checkauthid,"VALVE")>-1) {
		get_user_authid(id,authid,49)
		checkid = equali(authid,checkauthid)
		#if DEBUG_MODE == 1
		authtype = AUTH_TYPE_STEAMID
		#endif
	}
						
	else {
		get_user_ip (id,authid,49)
		checkid = (strfind(authid,checkauthid)>-1)
		#if DEBUG_MODE == 1
		authtype = AUTH_TYPE_IP
		#endif
	}
	return authtype
}

stock substr(const str[],dest[],len,lpos){
	
	new pos,ind
	
	for (pos=lpos;pos<strlen(str);pos++){
		if (ind<len){
			dest[ind] = str[pos]
			ind ++
		}
	}
	
}

// ---- [End of shared funtions]



#if DEBUG_MODE == 1
public debugMsg(const msg[]){
	server_print (msg)
	write_file (DEBUG_LOGFILE,msg)
}
#endif


// ---- [Precache]

public plugin_precache() {	
	
	new Default_T_Models [5][16]
	new Default_CT_Models[5][16]
	new dlen
	
	get_default_models (Default_T_Models,dlen,0)
	get_default_models (Default_CT_Models,dlen,1)	
	
	for (new i=0;i<dlen;i++){
		copy(g_PrecachedList[g_PrecachedCount],15,Default_T_Models[i])
		g_PrecachedCount ++
	}
	
	for (new i=0;i<dlen;i++){
		copy(g_PrecachedList[g_PrecachedCount],15,Default_CT_Models[i])
		g_PrecachedCount ++
	}	
	
	#if DEBUG_MODE == 1
	format (logmsg,127,DEBUG_MSG_BEGIN)
	debugMsg (logmsg)
	#endif
	
	new szFile[66] 
	get_inipath (szFile,65)
	
	#if DEBUG_MODE == 1
	format (logmsg,191,"%s%s%s",DEBUG_MSG_LOG,"AMXMODX CONFIG DIR: ",szFile)
	debugMsg (logmsg)
	#endif
	
	new szLine[101]
	new line
	new len
	new mayprecache_CT
	new mayprecache_T
	new forceprecache
	new isnullmodel_CT
	new isnullmodel_T
	new isreplacement
	
	while(read_file(szFile, line ++ , szLine , 100 , len) && (g_ModelsCount < MAX_MODELS) ) { 
		
		if(szLine[0] == ';' || !len) continue
	
		new arg1[32]
		new arg2[32]
		new arg3[32]
		
		mayprecache_CT = 1 //flags if a CT model needs to be precached
		mayprecache_T = 1 //flags if a T model needs to be precached
		forceprecache = 0
		isnullmodel_CT = 0
		isnullmodel_T = 0
		isreplacement = 0
		
		parse (szLine, arg1 , 24 , arg2 , 31, arg3 , 31)		
		
		if (strfind(arg2, "#")>-1) {
			
			new error
			
			mayprecache_CT = 0
			mayprecache_T = 0
			
			get_models_by_number (arg2,arg2,arg3,error)			
			if (error){
				server_print ("[model error] Invalid Model Index: %s",arg2)
				continue
			}			
		}
		
		else if ( (strfind(arg1,TAG_FORCEPRECACHE_CHAR)==0) && (strlen(arg1)>1) && (strlen(arg2)==0) && (strlen(arg3)==0)){
			
			substr(arg1,arg3,31,1)
			mayprecache_T = 0
			if (find_precached_ind(arg3)>-1)
				continue			
		}
		
				
		
		else {	
			// model replacement
			if (equali(arg1, TAG_REPLACE)) {
				if(equali(arg2, arg3))
					continue
				isreplacement = 1
			}	
			
			// if there's only one model they'll be the same for CTs and Ts
			else if (!strlen(arg3)){
				if(!strlen(arg2))
					continue
				copy (arg3,31,arg2)
				mayprecache_T = 0
			}
			else {
				if(equal(arg3, TAG_COMMANDSET_NULL)) {
					isnullmodel_CT = 1
					mayprecache_CT = 0
				}
				if(equal(arg2, TAG_COMMANDSET_NULL)) {
					isnullmodel_T = 1
					mayprecache_T = 0
				}
			}			
			
			// This will prevent unecessary caching.
			// for default models			
			for (new ind = 0; ind < g_PrecachedCount; ind ++) {				
				if (mayprecache_CT && equali(arg3, g_PrecachedList[ind]))
					mayprecache_CT = 0				
				if (mayprecache_T && equali(arg2, g_PrecachedList[ind])) 
					mayprecache_T = 0				
				if (!mayprecache_CT && !mayprecache_T)
					break		
			}							
		}				
		
		// if it's a default model, it doesn't need to be cached	
		if (mayprecache_CT){
			
			new modelpath [65]
			format (modelpath,64,"models/player/%s/%s%s",arg3,arg3,".mdl")
			
			if (file_exists(modelpath)) {				
				#if DEBUG_MODE == 1
				format (logmsg,191,"%s%s%s",DEBUG_MSG_LOG," PRECACHING CT CUSTOM MODEL :",modelpath)
				debugMsg (logmsg)
				#endif
				
				precache_model(modelpath)				
				copy (g_PrecachedList[g_PrecachedCount],31,arg3)
				g_PrecachedCount ++				
			}
			
			else {				
				server_print ("[model error] File Not Found: %s",modelpath)
				#if DEBUG_MODE == 1
				format (logmsg,191,"[model error] File Not Found: %s",modelpath)
				debugMsg (logmsg)
				#endif
				continue
			}
		}
		
		if (mayprecache_T && (!equali(arg2,arg3))){
			
			new modelpath [65]
			format (modelpath,64,"models/player/%s/%s%s",arg2,arg2,".mdl")
			
			if (file_exists(modelpath)){
				
				#if DEBUG_MODE == 1
				format (logmsg,191,"%s%s%s",DEBUG_MSG_LOG," PRECACHING T CUSTOM MODEL :",modelpath)
				debugMsg (logmsg)				
				#endif
				
				precache_model(modelpath)
				copy (g_PrecachedList[g_PrecachedCount],31,arg2)					
				g_PrecachedCount ++
				
			}
			else {
				server_print ("[model error] File Not Found: %s",modelpath)
				#if DEBUG_MODE == 1
				format (logmsg,191,"[model error] File Not Found: %s",modelpath)
				debugMsg (logmsg)
				#endif
				continue
			}
		}
		
		// sets bots' unique models, if specified
		
		new CTmodelind
		new Tmodelind
		
		CTmodelind = (isnullmodel_CT) ? NULLINDEX : find_precached_ind(arg3)
		Tmodelind = (isnullmodel_T) ? NULLINDEX : find_precached_ind(arg2)
		
		if ((CTmodelind>-1) && (Tmodelind>-1) && !forceprecache){
		
			if (equali(TAG_BOTDEFAULT,arg1)){
				g_BotsModel[0] = Tmodelind
				g_BotsModel[1] = CTmodelind
			}
			
			if (equali(TAG_DEFAULT, arg1)){
				g_DefaultModel[0] = Tmodelind
				g_DefaultModel[1] = CTmodelind
			}
			
			else {
				if(isreplacement)
					format(g_PlayerId[g_ModelsCount], 24, REPLACEMENT_SIGN)
				else
					copy(g_PlayerId[g_ModelsCount],24, arg1)
				g_ModelInfo[0][g_ModelsCount] = Tmodelind
				g_ModelInfo[1][g_ModelsCount] = CTmodelind
				g_ModelsCount++
			}
		}		
		
	}
	
	#if DEBUG_MODE == 1
	server_print(DEBUG_MSG_END)
	if (line == 0){
		format (logmsg,65,"%s models.ini EMPTY OR NOT FOUND",DEBUG_MSG_ERROR)
		debugMsg (logmsg)
	}	
	#endif
	
 
}

// ---- [End of Precache]


// ---- [reset models functions]

public auto_balance_triggered(){
	
	new id
	new name [32]
	new logdata0[128]
	
	read_logargv(0,logdata0,127)	
	parse_loguser(logdata0, name, 31) 
	id = get_user_index(name) 
	if(g_ModelStatus[id] != STATUS_MODEL_NOTSET)
		g_ModelStatus[id] |= STATUS_MODEL_RESET
	else
		g_ModelStatus[id] = STATUS_MODEL_NOTSET
	
}

public client_infochanged (id){
	if(g_ModelStatus[id] != STATUS_MODEL_NOTSET)
		g_ModelStatus[id] |= STATUS_MODEL_RESET
	else
		g_ModelStatus[id] = STATUS_MODEL_NOTSET
}

public client_command (id) {
	// check if a player selected another team
	new arg[13]
	
	if (read_argv(0, arg, 12) > 11)
		return PLUGIN_CONTINUE
		
	if (equal("chooseteam",arg) || equal ("jointeam",arg)){
		#if DEBUG_MODE == 1
		new playername[32]
		get_user_name (id,playername,31)
		format(logmsg,191,"%s%s%s(%d)",DEBUG_MSG_LOG,"RESET MODEL OF ",playername,id)
		debugMsg (logmsg)
		#endif
		g_ModelStatus[id] = STATUS_MODEL_NOTSET
		
	}
	
	return PLUGIN_CONTINUE
}


public client_disconnect(id) {

	g_ModelStatus[id] = STATUS_MODEL_NOTSET
	#if DEBUG_MODE == 1
	format (logmsg,191,"%s%s%d",DEBUG_MSG_LOG,"DISCONNECTED slot ",id)
	debugMsg (logmsg)
	#endif
	
}

// ---- [end of reset models functions]


// ---- [set models]

public set_players_models(id) {
	
	if(!is_user_connected(id) || !is_user_alive(id))
		return PLUGIN_CONTINUE
	
	if (cs_get_user_vip(id)) 
		g_ModelStatus[id] = STATUS_MODEL_NOTSET
		
	//#if DEBUG_MODE == 1
	new playername[32]
	get_user_name (id,playername,31)
	//#endif
	
	if(g_ModelStatus[id] & STATUS_MODEL_RESET) {
		if(!(g_ModelStatus[id] & STATUS_MODEL_NOMODEL)) {
			cs_reset_user_model(id)
		}
		g_ModelStatus[id] = STATUS_MODEL_NOTSET
	}
			
	
	if (g_ModelStatus[id] != STATUS_MODEL_NOTSET && !get_cvar_num("amx_playermodel_round_checkid")) {
			#if DEBUG_MODE == 1		
			format (logmsg,191,"%s%s%s(%d)",DEBUG_MSG_LOG,"WON'T SET MODEL THIS TIME BECAUSE IT'S ALREADY BEEN SET ",playername,id)
			debugMsg (logmsg)
			#endif
			return PLUGIN_CONTINUE
	} 
	
	//new id = read_data (1)
	new authid[50]
	
	#if DEBUG_MODE == 1
	new ip[200]
	new steamid[32]
	get_user_ip (id,ip,199)
	get_user_authid (id,steamid,31)
	format (logmsg,191,"%s%s%s(%d) - ip:%s, steamid:%s",DEBUG_MSG_LOG," PLAYER CHECK MODEL UPDATE: ",playername,id,ip,steamid)
	debugMsg (logmsg)
	#endif
	
	// avoid extra processing - models are only set when needed
	
	
	new authtype
	new checkid
	new index
	new currentmodel[32]
	new params[3]
	new name[32]
	get_user_name(id, name, 31)
	
	cs_get_user_model(id, currentmodel, 31)
	
	for (index = 0; index < g_ModelsCount; index++) {
		if(equal(g_PlayerId[index], REPLACEMENT_SIGN)) {
			if(equali(g_PrecachedList[g_ModelInfo[0][index]], currentmodel)) {
				params[0] = id
				params[1] = SETMODEL_REPLACE
				params[2] = index
				server_print("$$%d", index)
				//if(task_exists(id))
				set_task (0.8,"set_model",id,params,3)
				return PLUGIN_CONTINUE				
			}
		}
	}
	
	for (index = 0; index < g_ModelsCount; index ++) {
		if(equal(g_PlayerId[index], REPLACEMENT_SIGN))
			continue
		check_authtype(id,g_PlayerId[index],authtype,checkid,authid)
		if (checkid)
			break
	}
						
	params[0] = id //playerid
	 
	if (checkid)
		params[1] = index
	else if (is_user_bot(id)) {
		if((g_BotsModel[0]>-1) && (g_BotsModel[1]>-1)) {
			params[1] = SETMODEL_BOTDEFAULT
			format (logmsg,191,"%s%s%s(%d)",DEBUG_MSG_LOG,"SETING BOT'S DEFAULT MODEL ",playername,id)
		}
		else {
			g_ModelStatus[id] = STATUS_MODEL_NOMODEL
			format (logmsg,191,"%s%s%s(%d)",DEBUG_MSG_LOG,"NO MODEL SETING FOR BOT ",playername,id)
			return PLUGIN_CONTINUE
		}
	}
	else if(g_DefaultModel[0] > -1 && g_DefaultModel[1] > -1) {
		params[1] = SETMODEL_DEFAULT
	}
	else {
		g_ModelStatus[id] = STATUS_MODEL_NOMODEL
		return PLUGIN_CONTINUE
	}
	
	// this delay prevents some bugs.					
	set_task (0.8,"set_model",id,params,3)
	
			
	#if DEBUG_MODE == 1
	switch (authtype){
		case AUTH_TYPE_BOTNAME: {
			format (logmsg,191,"%s%s%s(%d)",DEBUG_MSG_LOG,"AUTH TYPE = NAME FOR BOT: ",playername,id)
			debugMsg (logmsg)
		}
		case AUTH_TYPE_STEAMID: {
			format (logmsg,191,"%s%s%s(%d) - steamid: %s",DEBUG_MSG_LOG,"AUTH TYPE = STEAMID FOR USER: ",playername,id,authid)
			debugMsg (logmsg)
		}
		case AUTH_TYPE_IP: {
			format (logmsg,191,"%s%s%s(%d) - ip: %s",DEBUG_MSG_LOG,"AUTH TYPE = IP FOR USER: ",playername,id,authid)
			debugMsg (logmsg)
		}
	}
	#endif	

	return PLUGIN_CONTINUE
}

public set_model (const params[]){
	
	new id = params[0] // playerid.
	new ind = params[1] // model index.
	new team 		
	
	team = get_user_team(id) - 1
	
	if (team>=0 && team<2){
		
		if(ind == SETMODEL_REPLACE) {
			new ind2 = params[2]		
			if(g_ModelInfo[1][ind2] != NULLINDEX) {				
				cs_set_user_model(id, g_PrecachedList[g_ModelInfo[1][ind2]])
				g_ModelStatus[id] = STATUS_MODEL_REPLACEMENT
				#if DEBUG_MODE == 1
				new playername[32]
				get_user_name(id, playername, 31)
				format(logmsg, 191, "%s <%s> MODEL REPLACED %s->%s",
					DEBUG_MSG_LOG,
					playername,
					g_PrecachedList[g_ModelInfo[0][ind2]],
					g_PrecachedList[g_ModelInfo[1][ind2]])
				#endif
			}
		}
		
		//else if(ind & SETMODEL_RESET)
		//	cs_reset_user_model(id)
		else if(ind == SETMODEL_BOTDEFAULT) {
			if(g_BotsModel[team] != NULLINDEX) {
				cs_set_user_model (id, g_PrecachedList[g_BotsModel[team]])
				g_ModelStatus[id] = STATUS_MODEL_BOTDEFAULT
				#if DEBUG_MODE == 1
				new playername[32]
				get_user_name(id, playername, 31)
				format(logmsg, 191, "%s <%s> MODEL SET FOR BOT %s",
					DEBUG_MSG_LOG,
					playername,
					g_PrecachedList[g_BotsModel[team]])
				#endif
			}
		}
		else if(ind == SETMODEL_DEFAULT) {
			if(g_DefaultModel[team] != NULLINDEX) {
				cs_set_user_model (id, g_PrecachedList[g_DefaultModel[team]])				
				g_ModelStatus[id] = STATUS_MODEL_DEFAULT
			}
		}
			
		else {
			if(g_ModelInfo[team][ind] != NULLINDEX) {
				cs_set_user_model (id, g_PrecachedList[g_ModelInfo[team][ind]])
				#if DEBUG_MODE == 1
				new playername[32]
				get_user_name (id,playername,31)
				format (logmsg,191,"%s%s<%s(%d)> HAS BEEN SET TO <%s>",DEBUG_MSG_LOG,"MODEL OF ",playername,id,g_PrecachedList[g_ModelInfo[team][ind]])
				debugMsg (logmsg)
				#endif
				g_ModelStatus[id] = STATUS_MODEL_SETBYID
			}
		}		
		
	}
	
}
// ---- [end of set models]
