[遊戲程式碼]求生之路外掛:人物角色選擇
阿新 • • 發佈:2018-12-14
#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"); } }