Files
2025-04-15 22:27:20 -04:00

386 lines
11 KiB
SourcePawn

#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "info/constants.sp"
#include "info/enums.sp"
#include "info/variables.sp"
#endif
/**
* Hooks all necessary events.
* Some events may be hooked with EventHookMode_Pre or EventHookMode_Post.
*
* @noreturn
*/
stock void HookEvents() {
HookEvent("player_activate", Event_PlayerActivate);
HookEvent("player_carryobject", Event_PlayerCarryObject);
HookEvent("player_changeclass", Event_PlayerChangeClass);
HookEvent("player_team", Event_PlayerChangeTeamPre, EventHookMode_Pre);
HookEvent("player_connect_client", Event_PlayerConnectPre, EventHookMode_Pre);
HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_disconnect", Event_PlayerDisconnectPre, EventHookMode_Pre);
HookEvent("player_dropobject", Event_PlayerDropObject);
HookEvent("post_inventory_application", Event_PostInventoryApplication, EventHookMode_Post);
HookEvent("teamplay_round_win", Event_RoundWin);
// User Messages
HookUserMessage(GetUserMessageId("VGUIMenu"), Event_PlayerVGUIMenu, true);
AddNormalSoundHook(Event_Sound);
}
public void Event_PlayerActivate(Handle hEvent, const char[] sName, bool bDontBroadcast) {
int iUserId = GetEventInt(hEvent, "userid");
int iClient = GetClientOfUserId(iUserId);
char sSteamId[32];
GetClientAuthId(iClient, AuthId_Steam2, sSteamId, sizeof(sSteamId));
if (!StrEqual(sSteamId, "BOT")) {
Player_Loaded(iUserId, iClient);
}
}
public void Event_PlayerCarryObject(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return;
}
int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
if (IsDefender(iClient)) {
g_bCarryingObject[iClient] = true;
}
}
public void Event_PlayerChangeClass(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return;
}
int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
if (IsValidClient(iClient) && !IsFakeClient(iClient)) {
TF2_SetPlayerClass(iClient, TFClass_Engineer, false, true);
}
}
public Action Event_PlayerChangeTeamPre(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return Plugin_Continue;
}
if (GetEventBool(hEvent, "disconnect")) {
return Plugin_Continue;
}
int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
SetEventBroadcast(hEvent, true); // Block the chat output (Player ... joined team BLU)
if (IsValidClient(iClient) && !IsFakeClient(iClient)) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "eventPlayerJoined", GetClientNameShort(iClient));
}
return Plugin_Continue;
}
public Action Event_PlayerConnectPre(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return Plugin_Continue;
}
SetEventBroadcast(hEvent, true); // Block the original chat output (Player ... has joined the game)
Database_UpdateServerPlayerCount();
return Plugin_Continue;
}
public void Event_PlayerDeath(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return;
}
int iUserId = GetEventInt(hEvent, "userid");
int iClient = GetClientOfUserId(iUserId);
int iAttackerId = GetEventInt(hEvent, "attacker");
// Check if attacker is a Defender
if (IsValidClient(GetClientOfUserId(iAttackerId)) && IsDefender(GetClientOfUserId(iAttackerId))) {
int iAttacker = GetClientOfUserId(iAttackerId);
Player_CAddValue(iAttacker, PLAYER_KILLS, 1);
}
// Check if assister is a Defender
int iAssisterId = GetEventInt(hEvent, "assister");
if (IsValidClient(GetClientOfUserId(iAssisterId)) && IsDefender(GetClientOfUserId(iAssisterId))) {
int iAssister = GetClientOfUserId(iAssisterId);
Player_CAddValue(iAssister, PLAYER_ASSISTS, 1);
}
if (IsDefender(iClient)) {
Player_OnDeath(iUserId, iClient);
} else if (IsAttacker(iClient)) {
int entity = GetEventInt(hEvent, "victim_entindex");
float position[3];
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", position);
Wave_OnDeath(iClient, position);
}
}
public Action Event_PlayerDisconnectPre(Handle hEvent, const char[] sName, bool bDontBroadcast) {
int iUserId = GetEventInt(hEvent, "userid");
int iClient = GetClientOfUserId(iUserId);
if (IsDefender(iClient)) {
Player_OnDisconnectPre(iUserId, iClient);
Database_UpdateServerPlayerCount();
} else {
return Plugin_Handled;
}
return Plugin_Continue;
}
public void Event_PlayerDropObject(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return;
}
int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
if (IsDefender(iClient)) {
g_bCarryingObject[iClient] = false;
}
}
public void Event_PostInventoryApplication(Handle hEvent, const char[] sName, bool bDontBroadcast) {
if (!g_bEnabled) {
return;
}
int iUserId = GetEventInt(hEvent, "userid");
int iClient = GetClientOfUserId(iUserId);
if (IsValidClient(iClient) && IsClientConnected(iClient) && IsClientInGame(iClient) && !IsFakeClient(iClient) && GetClientTeam(iClient) == TEAM_ATTACKER) {
ChangeClientTeam(iClient, TEAM_DEFENDER);
CreateTimer(0.5, RespawnPlayer, iClient);
} else if (IsDefender(iClient)) {
Player_OnSpawn(iUserId, iClient);
} else if (IsTower(iClient)) {
Tower_OnSpawn(iClient, GetTowerId(iClient));
} else if (IsAttacker(iClient)) {
Wave_OnSpawn(iClient);
RequestFrame(Wave_OnSpawnPost, iClient);
}
SetEntProp(iClient, Prop_Data, "m_CollisionGroup", 13); // COLLISION_GROUP_PROJECTILE
}
public Action Event_RoundWin(Handle hEvent, const char[] sName, bool bDontBroadcast) {
int iTeam = GetEventInt(hEvent, "team");
Server_UAddValue(g_iServerId, SERVER_ROUNDS_PLAYED, 1);
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
int iUserId = GetClientUserId(iClient);
int iTime = GetTime() - g_iTime;
Player_CSetValue(iClient, PLAYER_PLAYTIME, iTime);
Server_UAddValue(g_iServerId, SERVER_PLAYTIME, iTime);
Player_CSetValue(iClient, PLAYER_ROUNDS_PLAYED, 1);
Database_UpdatePlayerDisconnect(iUserId);
}
}
Database_ServerStatsUpdate();
ServerCommand("bot_kick all");
ServerCommand("mp_autoteambalance 0");
if (iTeam == TEAM_ATTACKER) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "eventGameOver");
//PrintToChatAll("\x07FF0000Game over! Resetting the map...");
}
Server_Reset();
return Plugin_Handled;
}
public Action Event_Sound(int iClients[64], int &iNumClients, char sSample[PLATFORM_MAX_PATH], int &iEntity, int &iChannel, float &fVolume, int &iLevel, int &iPitch, int &iFlags) {
if (!g_bEnabled) {
return Plugin_Continue;
}
if (IsValidEntity(iEntity) && IsTower(iEntity)) {
// Allow engineer hitting building sounds
if (StrContains(sSample, "wrench_hit_build_") != -1 || StrContains(sSample, "wrench_swing") != -1) {
return Plugin_Continue;
}
return Plugin_Stop;
}
return Plugin_Continue;
}
/*====================================
= Usermessages =
====================================*/
public Action Event_PlayerVGUIMenu(UserMsg iMessageId, Handle hBitBuffer, const int[] iPlayers, int iPlayersNum, bool bReliable, bool bInit) {
char sBuffer1[64];
char sBuffer2[256];
// check menu name
BfReadString(hBitBuffer, sBuffer1, sizeof(sBuffer1));
if (strcmp(sBuffer1, "info") != 0) {
return Plugin_Continue;
}
// Skip hidden motd
if (BfReadByte(hBitBuffer) != 1) {
return Plugin_Continue;
}
int iCount = BfReadByte(hBitBuffer);
if (iCount == 0) {
return Plugin_Continue;
}
Handle hKeyValues = CreateKeyValues("data");
for (int i = 0; i < iCount; i++) {
BfReadString(hBitBuffer, sBuffer1, sizeof(sBuffer1));
BfReadString(hBitBuffer, sBuffer2, sizeof(sBuffer2));
if (StrEqual(sBuffer1, "customsvr") || (StrEqual(sBuffer1, "msg") && !StrEqual(sBuffer2, "motd"))) {
delete hKeyValues;
return Plugin_Continue;
}
KvSetString(hKeyValues, sBuffer1, sBuffer2);
}
Handle hPack;
CreateDataTimer(0.0, ShowMotd, hPack, TIMER_FLAG_NO_MAPCHANGE);
WritePackCell(hPack, GetClientUserId(iPlayers[0]));
WritePackCell(hPack, view_as<int>(hKeyValues));
return Plugin_Handled;
}
public Action ShowMotd(Handle hTimer, Handle hPack) {
ResetPack(hPack);
int iClient = GetClientOfUserId(ReadPackCell(hPack));
Handle hKeyValues = view_as<Handle>(ReadPackCell(hPack));
if (!IsValidClient(iClient)) {
CloseHandle(hKeyValues);
return Plugin_Stop;
}
KvSetNum(hKeyValues, "customsvr", 1);
KvSetNum(hKeyValues, "cmd", 5); // closed_htmlpage
KvSetNum(hKeyValues, "type", MOTDPANEL_TYPE_URL);
KvSetString(hKeyValues, "title", "Welcome to TF2 Tower Defense!");
KvSetString(hKeyValues, "msg", "https://tf2td.github.io/towerdefense/");
ShowVGUIPanel(iClient, "info", hKeyValues, true);
CloseHandle(hKeyValues);
return Plugin_Stop;
}
/*==================================
= TF2 Events =
==================================*/
/**
* Called on weapon fire to decide if the current shot should be critical.
*
* @noreturn
*/
public Action TF2_CalcIsAttackCritical(int iClient, int iWeapon, char[] sClassname, bool &bResult) {
if (!g_bEnabled) {
return Plugin_Handled;
}
// If hitting with Wrench
if (StrEqual(sClassname, "tf_weapon_wrench") || StrEqual(sClassname, "tf_weapon_robot_arm")) {
float fLocation[3], fAngles[3];
GetClientEyePosition(iClient, fLocation);
GetClientEyeAngles(iClient, fAngles);
TR_TraceRayFilter(fLocation, fAngles, MASK_PLAYERSOLID, RayType_Infinite, TraceRayPlayers, iClient);
if (TR_DidHit()) {
int iTower = TR_GetEntityIndex();
if (IsTower(iTower)) {
float fHitLocation[3];
TR_GetEndPosition(fHitLocation);
if (GetVectorDistance(fLocation, fHitLocation) <= 70.0) {
Tower_OnUpgrade(iTower, iClient);
}
}
}
}
// Unlimited ammo/metal for towers
if (IsTower(iClient)) {
SetEntData(iWeapon, FindSendPropInfo("CTFWeaponBase", "m_iClip1"), 100, _, true);
SetEntData(iClient, FindSendPropInfo("CTFPlayer", "m_iAmmo") + 4, 100);
SetEntData(iClient, FindSendPropInfo("CTFPlayer", "m_iAmmo") + 8, 100);
SetEntData(iClient, FindDataMapInfo(iClient, "m_iAmmo") + (3 * 4), 100);
}
// Calculate crit chances
if (fMultiplier[Multiplier_GetInt("crit")] == 0.0) {
bResult = false;
}
if (fMultiplier[Multiplier_GetInt("crit")] * 0.05 * 100 >= GetRandomInt(0, 100)) {
bResult = true;
}
return Plugin_Handled;
}
/*=======================================
= TF2Items Events =
=======================================*/
/**
* Called when an item was given to a client.
*
* @noreturn
*/
public int TF2Items_OnGiveNamedItem_Post(int iClient, char[] sClassname, int iItemDefinitionIndex, int iItemLevel, int iItemQuality, int iEntityIndex) {
if (!g_bEnabled) {
return;
}
if (!IsDefender(iClient)) {
return;
}
if (iItemDefinitionIndex != 9 || iItemDefinitionIndex != 199) {
// Engineers primaries => Engineer's Shotgun
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = true;
}
if (iItemDefinitionIndex != 22 || iItemDefinitionIndex != 209 || iItemDefinitionIndex != 160 || iItemDefinitionIndex != 294) {
// Engineers secondaries => Engineer's Pistol
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = true;
}
}