1. 程式人生 > >[遊戲程式碼]求生之路外掛:人物角色選擇

[遊戲程式碼]求生之路外掛:人物角色選擇


#define PLUGIN_VERSION "1.0 Special"
#define PLUGIN_NAME "[紫冰]人物選擇外掛"
#define DEBUG 0

// includes
#include <sourcemod>
#include <sdktools>

// Client numbers are dynamic, keep changing according to the spawn order
new bool:g_bSpawned[MAXPLAYERS+1];

// CVars Handles
new Handle:g_hActivate;
new Handle:h_Fun;

// Variables
new g_InfectedTicket; // Number used to determine what the spawn infected function should spawn

// Arrays
new bool:OneToSpawn[MAXPLAYERS+1]; // Used to tell the plugin that this client will be the one to spawn and not place any spawn restrictions on that client
new bool:Fun;
new bool:Activate;

// Offsets

new offsetIsGhost; // Offset to see if the player is a ghost or not
new offsetIsAlive; // Offset to see if the player is alive or not
new offsetlifeState // Offset to prevent the player from spawning when the player is not supposed to

public Plugin:myinfo = 
{
	name = PLUGIN_NAME,
	author = "ط֟:L4D2&Ҡӫ:؏ҹ",
	description = "һպɋϯؔԉѡձ.",
	version = PLUGIN_VERSION,
	url = "http://blog.csdn.net/zbzibing"
}

public OnPluginStart()
{
	
	offsetIsGhost = FindSendPropInfo("CTerrorPlayer", "m_isGhost");
	offsetIsAlive = FindSendPropInfo("CTransitioningPlayer", "m_isAlive");
	offsetlifeState = FindSendPropInfo("CTerrorPlayer", "m_lifeState");
	
	// Clean up the spawn variable (deprecated)
	for(new i=1; i<=GetMaxClients(); ++i)
	{
		g_bSpawned[i]=false;
	}
	
	// convars
	g_hActivate = CreateConVar("l4d_chars_enable", "1", "人物更換角色開關.", FCVAR_PLUGIN|FCVAR_NOTIFY, true, 0.0, true, 1.0);
	h_Fun = CreateConVar("l4d_chars_fun_mode", "0", "值為1,只改變角色,不改變能力(1值感染者使用遊戲會崩潰)", FCVAR_PLUGIN|FCVAR_NOTIFY, true, 0.0, true, 1.0);
	HookConVarChange(h_Fun, ConVarFun);
	HookConVarChange(g_hActivate, ConVarActivate);
	Fun = GetConVarBool(h_Fun);
	Activate = GetConVarBool(g_hActivate);
	
	// config file
	AutoExecConfig(true, "L4D_BX");
	
	// sourcemod command
	RegConsoleCmd("sm_bx", PlayerPanelActivator);
}

// Adding ConVar Hooks allows us to change the cvar in-game AND have it take effect on change immediately

public ConVarFun(Handle:convar, const String:oldValue[], const String:newValue[])
{
	Fun = GetConVarBool(h_Fun);
}
public ConVarActivate(Handle:convar, const String:oldValue[], const String:newValue[])
{
	Activate = GetConVarBool(g_hActivate);
}
public OnClientPutInServer(client)
{
	if (client)
	{
		if (Activate)
			CreateTimer(30.0, AnnounceCharSelect, client);
	}
}

public CharPanel(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_Select)
	{
		switch (param2)
		{
			case 1:
			{
				if (!Fun)
				{
					// get prop
					new offset = FindSendPropInfo("CTerrorPlayer", "m_survivorCharacter");
					new char = GetEntData(param1, offset, 1);
					
					// set char
					char = 1;
					SetEntData(param1, offset, char, 1, true);
					
					// update client model info
					decl String:model[] = "models/survivors/survivor_teenangst.mdl";
					SetEntityModel(param1, model);
					
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03佐伊");
				}
				else
				{
					decl String:model[] = "models/survivors/survivor_teenangst.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03佐伊");
				}
			}
			case 2:
			{
				if (!Fun)
				{
					// get prop
					new offset = FindSendPropInfo("CTerrorPlayer", "m_survivorCharacter");
					new char = GetEntData(param1, offset, 1);
					
					// set char
					char = 2;
					SetEntData(param1, offset, char, 1, true);
					
					// update client model info
					decl String:model[] = "models/survivors/survivor_biker.mdl";
					SetEntityModel(param1, model);
					
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03弗朗西斯");
				}
				else
				{
					decl String:model[] = "models/survivors/survivor_biker.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03弗朗西斯");
				}
			}
			case 3:
			{
				if (!Fun)
				{
					// get prop
					new offset = FindSendPropInfo("CTerrorPlayer", "m_survivorCharacter");
					new char = GetEntData(param1, offset, 1);
					
					// set char
					char = 3;
					SetEntData(param1, offset, char, 1, true);
					
					// update client model info
					decl String:model[] = "models/survivors/survivor_manager.mdl";
					SetEntityModel(param1, model);
					
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03路易斯");
				}
				else
				{
					decl String:model[] = "models/survivors/survivor_manager.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03路易斯");
				}
			}
			case 4:
			{
				if (!Fun)
				{
					// get prop
					new offset = FindSendPropInfo("CTerrorPlayer", "m_survivorCharacter");
					new char = GetEntData(param1, offset, 1);
					
					// set char
					char = 0;
					SetEntData(param1, offset, char, 1, true);
					
					// update client model info
					decl String:model[] = "models/survivors/survivor_namvet.mdl";
					SetEntityModel(param1, model);
					
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03比爾");
				}
				else
				{
					decl String:model[] = "models/survivors/survivor_namvet.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03比爾");
				}
			}
			case 5:
			{
				CreateTimer(0.1, PlayerPanel_Infected, param1)
			}
		}
		
	} else if (action == MenuAction_Cancel)
	{
		
	}
}

public CharPanel_Infected(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_Select)
	{
		switch (param2)
		{
			case 1:
			{
				if (!Fun)
				{
					// Set the player as a ghost so that it can take the spawned infected
					SetGhostStatus(param1, true)
					
					// Set the player as the one to spawn so no spawn restrictions apply to that client
					OneToSpawn[param1] = true;
					
					// Tell the spawn infected function that it will spawn a smoker
					g_InfectedTicket = 2
					
					// Start the Spawn infected function
					Spawn_Infected()
					
					// The client is no longer the one to spawn as the client has already spawned
					OneToSpawn[param1] = false;
					
					// Set the client back to life
					SetGhostStatus(param1, false)
					
					// And Done!
					PrintToChat(param1, "\x04[紫冰]\x01轉變角色:\x03煙鬼Smoker");
				}
				else
				{
					decl String:model[] = "models/props/cs_office/shelves_metal.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03smoker");
				}
			}
			case 2:
			{
				if (!Fun)
				{
					// Set the player as a ghost so that it can take the spawned infected
					SetGhostStatus(param1, true)
					
					// Set the player as the one to spawn so no spawn restrictions apply to that client
					OneToSpawn[param1] = true;
					
					// Tell the spawn infected function that it will spawn a boomer
					g_InfectedTicket = 3
					
					// Start the Spawn infected function
					Spawn_Infected()
					
					// The client is no longer the one to spawn as the client has already spawned
					OneToSpawn[param1] = false;
					
					// Set the client back to life
					SetGhostStatus(param1, false)
					
					// And Done!
					PrintToChat(param1, "\x04[紫冰]\x01轉變角色:\x03胖子Boomer");
				}
				else
				{
					decl String:model[] = "models/props/cs_office/shelves_metal.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03Boomer");
				}
			}
			case 3:
			{
				if (!Fun)
				{
					// Set the player as a ghost so that it can take the spawned infected
					SetGhostStatus(param1, true)
					
					// Set the player as the one to spawn so no spawn restrictions apply to that client
					OneToSpawn[param1] = true;
					
					// Tell the spawn infected function that it will spawn a hunter
					g_InfectedTicket = 1
					
					// Start the Spawn infected function
					Spawn_Infected()
					
					// The client is no longer the one to spawn as the client has already spawned
					OneToSpawn[param1] = false;
					
					// Set the client back to life
					SetGhostStatus(param1, false)
					
					// And Done!
					PrintToChat(param1, "\x04[紫冰]\x01轉變角色:\x03獵人Hunter");
				}
				else
				{
					decl String:model[] = "models/props/cs_office/shelves_metal.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03Hunter");
				}
			}
			case 4:
			{
				if (!Fun)
				{
					// Set the player as a ghost so that it can take the spawned infected
					SetGhostStatus(param1, true)
					
					// Set the player as the one to spawn so no spawn restrictions apply to that client
					OneToSpawn[param1] = true;
					
					// Tell the spawn infected function that it will spawn a tank
					g_InfectedTicket = 4
					
					// Start the Spawn infected function
					Spawn_Infected()
					
					// The client is no longer the one to spawn as the client has already spawned
					OneToSpawn[param1] = false;
					
					// Set the client back to life
					SetGhostStatus(param1, false)
					
					// And Done!
					PrintToChat(param1, "\x04[紫冰]\x01轉變角色:\x03坦克Tank");
				}
				else
				{
					decl String:model[] = "models/props/cs_office/shelves_metal.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03Tank");
				}
			}
			case 5:
			{
				if (!Fun)
				{
					// Set the player as a ghost so that it can take the spawned infected
					SetGhostStatus(param1, true)
					
					// Set the player as the one to spawn so no spawn restrictions apply to that client
					OneToSpawn[param1] = true;
					
					// Tell the spawn infected function that it will spawn a hunter
					g_InfectedTicket = 1
					
					// Start the Spawn infected function
					Spawn_Infected()
					
					// The client is no longer the one to spawn as the client has already spawned
					OneToSpawn[param1] = false;
					
					// Set the client back to life
					SetGhostStatus(param1, false)
					// update client model info
					
					// Change the hunter model into the witch's model (if I changed the actual zombie class, it would cause crashes)
					decl String:model[] = "models/infected/witch.mdl";
					SetEntityModel(param1, model);
					
					// And Done!
					PrintToChat(param1, "\x04[紫冰]\x01轉變角色:\x03女巫Witch");
				}
				else
				{
					decl String:model[] = "models/props/cs_office/shelves_metal.mdl";
					SetEntityModel(param1, model);
					PrintToChat(param1, "\x04[紫冰]\x01當前角色:\x03Witch");
				}
			}
		}
		
	} else if (action == MenuAction_Cancel)
	{
		
	}
}

public Action:PlayerPanelActivator(client, args)
{
	if ((client) && (Activate) && (!Fun) && (GetClientTeam(client) == 2))
	{
		CreateTimer(0.1, PlayerPanel, client);
	}
	if ((client) && (Activate) && (!Fun) && (GetClientTeam(client) == 3) && (GetUserFlagBits(client) != 0))
	{
		CreateTimer(0.1, PlayerPanel_Infected, client);
	}
	if ((client) && (Activate) && (!Fun) && (GetClientTeam(client) == 3) && (GetUserFlagBits(client) == 0))
	{
		PrintToChat(client, "The infected character select panel can only be shown to \x03infected admins")
	}
	else if (client && Activate && Fun)
	{
		CreateTimer(0.1, PlayerPanel_Fun, client);
	}
}

public Action:AnnounceCharSelect(Handle:timer, any:client)
{
	if (IsClientInGame(client))
	{
		PrintHintText(client, "[紫冰] 輸入!bx選擇角色");
	}
}

public Action:PlayerPanel(Handle:timer, any:client) //client, args)
{
	//Commenting these out allows infected to access this menu as well
	if(!IsClientInGame(client)) return;
	//if(GetClientTeam(client)!=2) return;
	
	// The normal player panel
	
	new Handle:panel = CreatePanel();
	SetPanelTitle(panel, "角色選擇");
	
	DrawPanelText(panel, "     ");
	DrawPanelItem(panel, "美女:佐伊Zoey");		//1
	DrawPanelItem(panel, "流氓:弗朗西斯Francis"); 	//2
	DrawPanelItem(panel, "黑鬼:路易斯Louis"); 	//3
	DrawPanelItem(panel, "老兵:比爾Bill"); 		//4
	
	
	if (g_bSpawned[client]==false)
	{
		SendPanelToClient(panel, client, CharPanel, 20);
		//g_bSpawned[client]=true;
	}
	
	CloseHandle(panel);
}

public Action:PlayerPanel_Infected(Handle:timer, any:client) //client, args)
{
	//Commenting these out allows infected to access this menu as well
	if(!IsClientInGame(client)) return;
	//if(GetClientTeam(client)!=2) return;
	
	// The Infected Player Panel, it's only shown to admins or when fun mode is on
	
	new Handle:panel = CreatePanel();
	SetPanelTitle(panel, "角色轉換:");
	
	DrawPanelText(panel, "     ");
	DrawPanelItem(panel, "煙鬼Smoker");		//1
	DrawPanelItem(panel, "胖子Boomer"); 	//2
	DrawPanelItem(panel, "獵人Hunter"); 	//3
	DrawPanelItem(panel, "坦克Tank"); 		//4
	DrawPanelItem(panel, "女巫Witch"); 		//5
	
	if (g_bSpawned[client]==false)
	{
		SendPanelToClient(panel, client, CharPanel_Infected, 20);
		//g_bSpawned[client]=true;
	}
	
	CloseHandle(panel);
}

public Action:PlayerPanel_Fun(Handle:timer, any:client) //client, args)
{
	//Commenting these out allows infected to access this menu as well
	if(!IsClientInGame(client)) return;
	//if(GetClientTeam(client)!=2) return;
	
	// This panel is different because it has an extra option: Infected
	// This panel allows Infected players to choose an infected model, but still does not allow survivors to pick infected models because it will crash
	
	new Handle:panel = CreatePanel();
	SetPanelTitle(panel, "模仿人類");
	
	DrawPanelText(panel, "     ");
	DrawPanelItem(panel, "人類:佐伊");		//1
	DrawPanelItem(panel, "人類:弗朗西斯"); 	//2
	DrawPanelItem(panel, "人類:路易斯"); 	//3
	DrawPanelItem(panel, "人類:比爾"); 		//4
	
	if (GetClientTeam(client) == 3)
	{
		DrawPanelItem(panel, "模仿其他");		//5
	}
	
	if (g_bSpawned[client]==false)
	{
		SendPanelToClient(panel, client, CharPanel, 20);
		//g_bSpawned[client]=true;
	}
	
	CloseHandle(panel);
}

Spawn_Infected()
{
	// Before spawning the bot, we determine if an real infected player is dead, since the new infected bot will be controlled by this player
	new bool:resetGhost[MAXPLAYERS+1];
	new bool:resetDead[MAXPLAYERS+1];
	new bool:resetLife[MAXPLAYERS+1];
	
	for (new i=1;i<=MaxClients;i++)
	{
		if (IsClientConnected(i) && (!IsFakeClient(i)) && IsClientInGame(i)) // player is connected and is not fake and it's in game ...
		{
			// If player is on infected's team and is dead ..
			if (GetClientTeam(i)==3)
			{
				if (OneToSpawn[i] == false)
				{
					// If player is a ghost ....
					if (IsPlayerGhost(i))
					{
						resetGhost[i] = true;
						SetGhostStatus(i, false);
						resetDead[i] = true;
						SetAliveStatus(i, true);
						#if DEBUG
						LogMessage("Player is a ghost, taking preventive measures for spawning an infected bot")
						#endif
					}
					else if (!IsPlayerAlive(i)) // if player is just dead ...
					{
						resetLife[i] = true;
						SetLifeState(i, false)
					}
					else if (!IsPlayerAlive(i))
					{
						resetLife[i] = true;
						SetLifeState(i, false)
						#if DEBUG
						LogMessage("Found a dead player, spawn time has not reached zero, delaying player to Spawn an infected bot")
						#endif
					}
				}
			}
		}
	}
	
	// We get any client ....
	new anyclient = GetAnyClient();
	new bool:temp = false;
	if (anyclient == 0)
	{
		#if DEBUG
		LogMessage("[Character Select] Creating temp client to fake command");
		#endif
		// we create a fake client
		anyclient = CreateFakeClient("Bot");
		if (anyclient == 0)
		{
			LogError("[L4D] Character Select: CreateFakeClient returned 0 -- Infected bot was not spawned");
			return;
		}
		temp = true;
	}
	
	new admindata = GetUserFlagBits(anyclient)
	SetUserFlagBits(anyclient, ADMFLAG_ROOT)
	
	new String:command[] = "z_spawn";
	new flags = GetCommandFlags(command);
	SetCommandFlags(command, flags & ~FCVAR_CHEAT);
	
	// We spawn the bot ...
	switch (g_InfectedTicket)
	{
		case 1: // Hunter
		{
			#if DEBUG
			LogMessage("Spawning Hunter")
			#endif
			FakeClientCommand(anyclient, "z_spawn hunter auto", command);
		}
		case 2: // Smoker
		{	
			#if DEBUG
			LogMessage("Spawning Smoker")
			#endif
			FakeClientCommand(anyclient, "z_spawn smoker auto", command);
		}
		case 3: // Boomer
		{
			#if DEBUG
			LogMessage("Spawning Boomer")
			#endif
			FakeClientCommand(anyclient, "z_spawn boomer auto", command);
		}
		case 4: // Tank
		{
			#if DEBUG
			LogMessage("Spawning Tank")
			#endif
			FakeClientCommand(anyclient, "z_spawn tank auto", command);
		}
	}
	
	// restore z_spawn and user flags
	
	SetUserFlagBits(anyclient, admindata)
	SetCommandFlags(command, flags);
	
	// We restore the player's status
	for (new i=1;i<=MaxClients;i++)
	{
		if (resetGhost[i] == true)
			SetGhostStatus(i, true);
		if (resetDead[i] == true)
			SetAliveStatus(i, false);
		if (resetLife[i] == true)
			SetLifeState(i, true);
		//ChangeClientTeam(i, 3)
	}
	
	// If client was temp, we setup a timer to kick the fake player
	if (temp) CreateTimer(0.1,kickbot,anyclient);
}

bool:IsPlayerGhost (client)
{
	new isghost;
	isghost = GetEntData(client, offsetIsGhost, 1);
	
	if (isghost == 1)
		return true;
	else
	return false;
}

SetAliveStatus (client, bool:alive)
{
	if (alive)
		SetEntData(client, offsetIsAlive, 1, 1, true);
	else
	SetEntData(client, offsetIsAlive, 0, 1, false);
}
SetGhostStatus (client, bool:ghost)
{
	if (ghost)
	{	
		SetEntData(client, offsetIsGhost, 1, 1, true);
		SetEntityMoveType(client, MOVETYPE_ISOMETRIC)
	}
	else
	{
		SetEntData(client, offsetIsGhost, 0, 1, false);
		SetEntityMoveType(client, MOVETYPE_WALK)
	}
}

SetLifeState (client, bool:ready)
{
	if (ready)
		SetEntData(client,offsetlifeState, 1, 1, true);
	else
	SetEntData(client, offsetlifeState, 0, 1, false);
}

public GetAnyClient ()
{
	#if DEBUG
	LogMessage("[Character Select] Looking for any real client to fake command");
	#endif
	for (new i=1;i<=MaxClients;i++)
	{
		if (IsClientConnected(i) && IsClientInGame(i) && (!IsFakeClient(i)))
		{
			return i;
		}
	}
	return 0;
}

public Action:kickbot(Handle:timer, any:value)
{
	KickThis(value);
}

KickThis (client)
{
	
	if (IsClientConnected(client) && (!IsClientInKickQueue(client)))
	{
		if (IsFakeClient(client)) KickClient(client,"Kick");
	}
}