Initial commit

This commit is contained in:
2025-04-15 22:27:20 -04:00
parent 5b7b68f81f
commit 771d8fe8e8
597 changed files with 149544 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
public void AntiAir_ProjectileTick(int iProjectile, int iTower) {
if (IsValidEntity(iProjectile)) {
float fLocation[3];
GetEntPropVector(iProjectile, Prop_Data, "m_vecOrigin", fLocation);
char sEntityName[50];
GetEntPropString(iProjectile, Prop_Data, "m_iName", sEntityName, sizeof(sEntityName));
if (fLocation[2] >= g_fAirWaveSpawn[2] && fLocation[2] <= g_fAirWaveSpawn[2] + 95.0) {
float fAngles[3];
GetEntPropVector(iProjectile, Prop_Data, "m_angRotation", fAngles);
float fVelocity[3];
GetEntPropVector(iProjectile, Prop_Data, "m_vecAbsVelocity", fVelocity);
float fNewVelocity[3];
fNewVelocity[0] = fVelocity[0];
fNewVelocity[1] = fVelocity[1];
fNewVelocity[2] = -fVelocity[2];
float fBounceVec[3];
SubtractVectors(fNewVelocity, NULL_VECTOR, fBounceVec);
fBounceVec[2] = 0.0;
float fNewAngles[3];
GetVectorAngles(fBounceVec, fNewAngles);
TeleportEntity(iProjectile, NULL_VECTOR, fNewAngles, fBounceVec);
} else {
DataPack hPack = new DataPack();
hPack.WriteCell(iProjectile);
hPack.WriteCell(iTower);
RequestFrame(AntiAir_ProjectileTick2, hPack);
}
}
}
public void AntiAir_ProjectileTick2(DataPack iData) {
iData.Reset();
AntiAir_ProjectileTick(iData.ReadCell(), iData.ReadCell());
delete iData;
}

View File

@@ -0,0 +1,429 @@
/**
* Checks for clients near AoE Towers
*
* @param hTimer Timer Handle
* @param iTower The tower
* @noreturn
*/
public Action Timer_ClientNearAoETower(Handle hTimer) {
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsClientInGame(iClient) && IsPlayerAlive(iClient)) {
if (IsTower(iClient)) {
int iTower = iClient;
TDTowerId iTowerId = GetTowerId(iTower);
if (iTowerId == TDTower_AoE_Engineer) {
float fAreaScale = 80.0 * Tower_GetAreaScale(GetTowerId(iTower));
CreateBeamBoxAroundClient(iTower, fAreaScale, true, 0.2, { 231, 76, 60, 25 });
Tower_ObjectNearAoEEngineer(iTower, iClient);
}
if (iTowerId == TDTower_Medic) {
float fAreaScale = 160.0 * Tower_GetAreaScale(GetTowerId(iTower));
CreateBeamBoxAroundClient(iTower, fAreaScale, true, 0.2, { 46, 204, 113, 25 });
Tower_ObjectNearAoEMedic(iTower);
}
if (iTowerId == TDTower_Kritz_Medic) {
float fAreaScale = 60.0 * Tower_GetAreaScale(GetTowerId(iTower));
CreateBeamBoxAroundClient(iTower, fAreaScale, true, 0.2, { 41, 128, 185, 25 });
Tower_ObjectNearKritzMedic(iTower);
}
if (iTowerId == TDTower_Slow_Spy) {
float fAreaScale = 100.0 * Tower_GetAreaScale(GetTowerId(iTower));
CreateBeamBoxAroundClient(iTower, fAreaScale, false, 0.2, { 44, 62, 80, 25 });
Tower_ObjectNearAoESpy(iTower);
}
}
}
}
return Plugin_Continue;
}
/**
* Player is close to AoEMedic
*
* @param iTower The tower
* @param iClient The player
* @noreturn
*/
public void Tower_ObjectNearAoEMedic(int iTower) {
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient) && IsClientInZone(iClient, g_fBeamPoints[iTower])) {
Player_AddHealth(iClient, 5);
// Check if there is no Beam
if (g_iHealBeamIndex[iClient][0] == 0 && g_iHealBeamIndex[iClient][1] == 0) {
AttachHealBeam(iTower, iClient);
}
} else {
Tower_ObjectNotNearAoEMedic(iClient);
}
}
}
/**
* Player isn't close to AoEMedic
*
* @param iTower The tower
* @param iClient The player
* @noreturn
*/
public void Tower_ObjectNotNearAoEMedic(int iClient) {
// Remove Beam if there is one
if (g_iHealBeamIndex[iClient][0] != 0) {
if (IsValidEdict(g_iHealBeamIndex[iClient][0])) {
RemoveEdict(g_iHealBeamIndex[iClient][0]);
g_iHealBeamIndex[iClient][0] = 0;
}
}
if (g_iHealBeamIndex[iClient][1] != 0) {
if (IsValidEdict(g_iHealBeamIndex[iClient][1])) {
RemoveEdict(g_iHealBeamIndex[iClient][1]);
g_iHealBeamIndex[iClient][1] = 0;
}
}
}
/**
* Player is close to AoEEngineer
*
* @param iTower The tower
* @param iClient The player
* @noreturn
*/
public void Tower_ObjectNearAoEEngineer(int iTower, int iClient) {
if (iAoEEngineerTimer < 3 && !IsTowerAttached(iTower)) {
iAoEEngineerTimer++;
return;
} else if (!IsTowerAttached(iTower)) {
iAoEEngineerTimer = 0;
}
int iSentry = -1, iDispenser = -1;
float fLocation[3];
// Sentries
while ((iSentry = FindEntityByClassname(iSentry, "obj_sentrygun")) != -1) {
if (!IsValidEntity(iSentry)) {
continue;
}
GetEntPropVector(iSentry, Prop_Send, "m_vecOrigin", fLocation);
fLocation[2] += 20.0;
if (IsPointInZone(fLocation, g_fBeamPoints[iTower])) {
int iShellsMax, iHealthMax, iRocketsMax, iMetalMax;
if (IsMiniSentry(iSentry)) {
iShellsMax = 150;
iHealthMax = 150;
iRocketsMax = 0;
iMetalMax = 0;
} else {
switch (GetBuildingLevel(iSentry)) {
case 1: {
iShellsMax = 150;
iHealthMax = 150;
iRocketsMax = 0;
iMetalMax = 1000;
}
case 2: {
iShellsMax = 200;
iHealthMax = 180;
iRocketsMax = 0;
iMetalMax = 1000;
}
case 3: {
iShellsMax = 200;
iHealthMax = 216;
iRocketsMax = 20;
iMetalMax = 0;
}
}
}
int iState = GetEntProp(iSentry, Prop_Send, "m_iState");
// If is not building up
if (iState != 0) {
int iShells = GetEntProp(iSentry, Prop_Send, "m_iAmmoShells");
int iHealth = GetEntProp(iSentry, Prop_Send, "m_iHealth");
int iRockets = GetEntProp(iSentry, Prop_Send, "m_iAmmoRockets");
int iMetal = GetEntProp(iSentry, Prop_Send, "m_iUpgradeMetal");
if (IsMiniSentry(iSentry)) {
int iShellsPerHit = 20; // Default: 40
if (iShells + iShellsPerHit < iShellsMax) {
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShells + iShellsPerHit);
} else if (iShells < iShellsMax) {
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShellsMax);
}
} else {
int iMetalPerHit = 10; // Default: 25
int iHealthPerHit = 50; // Default: 105
int iShellsPerHit = 20; // Default: 40
int iRocketsPerHit = 4; // Default: 8
if (iMetal < iMetalMax) {
SetEntProp(iSentry, Prop_Send, "m_iUpgradeMetal", iMetal + iMetalPerHit);
// Upgrade to the next level
} else if (iMetalMax != 0 && !g_bAoEEngineerAttack) {
float fLocationTower[3];
GetClientAbsOrigin(iTower, fLocationTower);
TeleportEntity(iTower, fLocation, NULL_VECTOR, NULL_VECTOR);
g_bAoEEngineerAttack = true;
DataPack hPack = new DataPack();
hPack.WriteFloat(float(iTower));
hPack.WriteFloat(fLocationTower[0]);
hPack.WriteFloat(fLocationTower[1]);
hPack.WriteFloat(fLocationTower[2]);
CreateTimer(0.2, Timer_TeleportAoEEngineerBack, hPack, _);
}
if (iHealth + iHealthPerHit < iHealthMax) {
SetEntProp(iSentry, Prop_Send, "m_iHealth", iHealth + iHealthPerHit);
} else if (iHealth < iHealthMax) {
SetEntProp(iSentry, Prop_Send, "m_iHealth", iHealthMax);
}
if (iShells + iShellsPerHit < iShellsMax) {
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShells + iShellsPerHit);
} else if (iShells < iShellsMax) {
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShellsMax);
}
if (iRockets + iRocketsPerHit < iRocketsMax) {
SetEntProp(iSentry, Prop_Send, "m_iAmmoRockets", iRockets + iRocketsPerHit);
} else if (iRockets < iRocketsMax) {
SetEntProp(iSentry, Prop_Send, "m_iAmmoRockets", iRocketsMax);
}
}
}
}
}
// Dispensers
while ((iDispenser = FindEntityByClassname(iDispenser, "obj_dispenser")) != -1) {
if (!IsValidEntity(iDispenser)) {
continue;
}
GetEntPropVector(iDispenser, Prop_Send, "m_vecOrigin", fLocation);
fLocation[2] += 20.0;
if (IsPointInZone(fLocation, g_fBeamPoints[iTower])) {
int iHealthMax, iMetalMax;
switch (GetBuildingLevel(iDispenser)) {
case 1: {
iHealthMax = 150;
iMetalMax = 500;
}
case 2: {
iHealthMax = 180;
iMetalMax = 500;
}
case 3: {
iHealthMax = 216;
iMetalMax = 0;
}
}
int iBuildingUp = GetEntProp(iDispenser, Prop_Send, "m_bBuilding");
if (iBuildingUp != 1) { // Is not building up
int iHealth = GetEntProp(iDispenser, Prop_Send, "m_iHealth");
int iMetal = GetEntProp(iDispenser, Prop_Send, "m_iUpgradeMetal");
int iMetalPerHit = 10; // Default: 25
int iHealthPerHit = 50; // Default: 105
if (iMetal < iMetalMax) {
SetEntProp(iDispenser, Prop_Send, "m_iUpgradeMetal", iMetal + iMetalPerHit);
} else if (iMetalMax != 0 && !g_bAoEEngineerAttack) { // Upgrade to the next level
float fLocationTower[3];
GetClientAbsOrigin(iTower, fLocationTower);
TeleportEntity(iTower, fLocation, NULL_VECTOR, NULL_VECTOR);
g_bAoEEngineerAttack = true;
DataPack hPack = new DataPack();
hPack.WriteFloat(float(iTower));
hPack.WriteFloat(fLocationTower[0]);
hPack.WriteFloat(fLocationTower[1]);
hPack.WriteFloat(fLocationTower[2]);
CreateTimer(0.5, Timer_TeleportAoEEngineerBack, hPack, _);
}
if (iHealth + iHealthPerHit < iHealthMax) {
SetEntProp(iDispenser, Prop_Send, "m_iHealth", iHealth + iHealthPerHit);
} else if (iHealth < iHealthMax) {
SetEntProp(iDispenser, Prop_Send, "m_iHealth", iHealthMax);
}
}
}
}
}
public Action Timer_TeleportAoEEngineerBack(Handle hTimer, DataPack hPack) {
hPack.Reset();
g_bAoEEngineerAttack = false;
int iTower = RoundToZero(ReadPackFloat(hPack));
float fLocation[3];
fLocation[0] = hPack.ReadFloat();
fLocation[1] = hPack.ReadFloat();
fLocation[2] = hPack.ReadFloat();
TeleportEntity(iTower, fLocation, NULL_VECTOR, NULL_VECTOR);
return Plugin_Continue;
}
/**
* Player is close to KritzMedic
*
* @param iTower The tower
* @noreturn
*/
public void Tower_ObjectNearKritzMedic(int iTower) {
// If Kritz Time is up
if (TF2_GetUberLevel(iTower) <= 0.0)
g_bKritzMedicCharged = false;
// If Bot is fully charged, charge player
if (g_bKritzMedicCharged && TF2_GetUberLevel(iTower) > 0.0) {
TF2_SetUberLevel(iTower, TF2_GetUberLevel(iTower) - 0.02);
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsValidClient(iClient))
TF2_AddCondition(iClient, TFCond_Kritzkrieged, 0.3);
}
// If Bot is fully charged
} else if (TF2_GetUberLevel(iTower) >= 1.0) {
g_bKritzMedicCharged = true;
} else if (iAoEKritzMedicTimer < 3 && !IsTowerAttached(iTower)) {
iAoEKritzMedicTimer++;
return;
} else if (!IsTowerAttached(iTower)) {
iAoEKritzMedicTimer = 0;
}
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient) && IsClientInZone(iClient, g_fBeamPoints[iTower])) {
float fUberLevel = TF2_GetUberLevel(iTower);
TF2_SetUberLevel(iTower, fUberLevel + 0.01);
}
}
}
/**
* Player is close to AoESpy
*
* @param iTower The tower
* @param iClient The player
* @noreturn
*/
public void Tower_ObjectNearAoESpy(int iTower) {
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsAttacker(iClient) && IsClientInZone(iClient, g_fBeamPoints[iTower]) && !g_iSlowAttacker[iClient])
g_iSlowAttacker[iClient] = true;
else if (g_iSlowAttacker[iClient] && IsClientInZone(iClient, g_fBeamPoints[iTower]))
TF2_AddCondition(iClient, TFCond_TeleportedGlow, 1.0);
else if (g_iSlowAttacker[iClient])
g_iSlowAttacker[iClient] = false;
}
}
/**
* Gets the level of an building.
*
* @param iEntity The buildings entity index.
* @return The buildings level.
*/
public int GetBuildingLevel(int iEntity) {
return GetEntProp(iEntity, Prop_Send, "m_iUpgradeLevel");
}
/**
* Checks wheter a sentry is a Mini-Sentry or not.
*
* @param iSentry The sentries entity index.
* @return True if mini, false otherwise.
*/
public bool IsMiniSentry(int iSentry) {
return ((GetEntProp(iSentry, Prop_Send, "m_bMiniBuilding") == 0) ? false : true);
}
/**
* Attach a beam between two entities
*
* @param iEntity The start point
* @param iTarget The target
* @noreturn
*/
stock void AttachHealBeam(int iEntity, int iTarget) {
int iParticleIndex1 = CreateEntityByName("info_particle_system");
int iParticleIndex2 = CreateEntityByName("info_particle_system");
if (IsValidEdict(iParticleIndex1)) {
char sEntityName[128];
Format(sEntityName, sizeof(sEntityName), "target%i", iEntity);
DispatchKeyValue(iEntity, "targetname", sEntityName);
char sTargetName1[128];
Format(sTargetName1, sizeof(sTargetName1), "target%i", iTarget);
DispatchKeyValue(iTarget, "targetname", sTargetName1);
char sTargetName2[128];
Format(sTargetName2, sizeof(sTargetName2), "tf2particle%i", iTarget);
DispatchKeyValue(iParticleIndex2, "targetname", sTargetName2);
DispatchKeyValue(iParticleIndex2, "parentname", sTargetName1);
SetVariantString(sTargetName1);
AcceptEntityInput(iParticleIndex2, "SetParent");
SetVariantString("flag");
AcceptEntityInput(iParticleIndex2, "SetParentAttachment");
DispatchKeyValue(iParticleIndex1, "targetname", "tf2particle");
DispatchKeyValue(iParticleIndex1, "parentname", sEntityName);
DispatchKeyValue(iParticleIndex1, "effect_name", "dispenser_heal_blue");
DispatchKeyValue(iParticleIndex1, "cpoint1", sTargetName2);
DispatchSpawn(iParticleIndex1);
SetVariantString(sEntityName);
AcceptEntityInput(iParticleIndex1, "SetParent");
SetVariantString("flag");
AcceptEntityInput(iParticleIndex1, "SetParentAttachment");
ActivateEntity(iParticleIndex1);
AcceptEntityInput(iParticleIndex1, "start");
g_iHealBeamIndex[iTarget][0] = iParticleIndex1;
g_iHealBeamIndex[iTarget][1] = iParticleIndex2;
}
}
stock float TF2_GetUberLevel(int iClient) {
int iIndex = GetPlayerWeaponSlot(iClient, 1);
if (iIndex > 0)
return GetEntPropFloat(iIndex, Prop_Send, "m_flChargeLevel");
else
return 0.0;
}
stock void TF2_SetUberLevel(int iClient, float fUberLevel) {
int iIndex = GetPlayerWeaponSlot(iClient, 1);
if (iIndex > 0)
SetEntPropFloat(iIndex, Prop_Send, "m_flChargeLevel", fUberLevel);
}

View File

@@ -0,0 +1,280 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Hooks buttons.
*
* @noreturn
*/
stock void HookButtons() {
HookEntityOutput("func_breakable", "OnHealthChanged", OnButtonShot);
}
public void OnButtonShot(const char[] sOutput, int iCaller, int iActivator, float fDelay) {
char sName[64];
GetEntPropString(iCaller, Prop_Data, "m_iName", sName, sizeof(sName));
if (StrContains(sName, "break_tower_tp_") != -1) {
// Tower teleport
char sNameParts[4][32];
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
TDTowerId iTowerId = view_as<TDTowerId>(StringToInt(sNameParts[3]));
Tower_OnButtonTeleport(iTowerId, iCaller, iActivator);
} else if (StrContains(sName, "break_tower_") != -1) {
// Tower buy
if (!g_bCanGetUnlocks) {
return;
}
g_bCanGetUnlocks = false;
CreateTimer(0.5, Timer_EnableUnlockButton);
char sNameParts[3][32];
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
TDTowerId iTowerId = view_as<TDTowerId>(StringToInt(sNameParts[2]));
Tower_OnButtonBuy(iTowerId, iCaller, iActivator);
} else if (StrEqual(sName, "break_pregame")) {
// Pregame button
if (hHintTimer == null) {
hHintTimer = CreateTimer(60.0, Timer_Hints, _, TIMER_REPEAT);
}
} else if (StrContains(sName, "wave_start") != -1) {
// Wave start
if (StrEqual(sName, "wave_start")) {
Wave_OnButtonStart(g_iCurrentWave, iCaller, iActivator);
} else {
char sNameParts[3][32];
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
Wave_OnButtonStart(StringToInt(sNameParts[2]), iCaller, iActivator);
}
} else if (StrContains(sName, "enable_sentry") != -1) {
// Allow another sentry
if (!g_bCanGetUnlocks) {
return;
}
g_bCanGetUnlocks = false;
CreateTimer(0.5, Timer_EnableUnlockButton);
g_iBuildingLimit[TDBuilding_Sentry] += 1;
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "sentryLimitChanged", g_iBuildingLimit[TDBuilding_Sentry]);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "sentryLimitBuildInfo");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Your sentry limit has been changed to:\x04 %i",g_iBuildingLimit[TDBuilding_Sentry]);
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can build additional sentries with the command \x04/s");
AcceptEntityInput(iCaller, "Break");
} else if (StrContains(sName, "enable_dispenser") != -1) {
// Enable dispenser
if (!g_bCanGetUnlocks) {
return;
}
g_bCanGetUnlocks = false;
CreateTimer(0.5, Timer_EnableUnlockButton);
g_iBuildingLimit[TDBuilding_Dispenser] += 1;
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "dispenserLimitChanged", g_iBuildingLimit[TDBuilding_Dispenser]);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "dispenserLimitBuildInfo");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Your dispenser limit has been changed to:\x04 %i",g_iBuildingLimit[TDBuilding_Dispenser]);
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can build dispensers via your PDA");
AcceptEntityInput(iCaller, "Break");
} else if (StrContains(sName, "bonus_metal") != -1) {
// Metal bonus reward
if (!g_bCanGetUnlocks) {
return;
}
g_bCanGetUnlocks = false;
CreateTimer(0.5, Timer_EnableUnlockButton);
SpawnMetalPacksNumber(TDMetalPack_Start, 4);
AcceptEntityInput(iCaller, "Break");
} else if (StrContains(sName, "multiplier") != -1) {
// Damage multipliers
if (!g_bCanGetUnlocks) {
return;
}
g_bCanGetUnlocks = false;
CreateTimer(0.5, Timer_EnableUnlockButton);
for (int i = 1; i <= iMaxMultiplierTypes; i++) {
char sKey[32], sMultiplier[32];
Format(sKey, sizeof(sKey), "%d_type", i);
if (GetTrieString(g_hMultiplierType, sKey, sMultiplier, sizeof(sMultiplier))) {
if (StrContains(sMultiplier, "crit") != -1 && StrContains(sName, "crit") != -1) {
// Crit chance
//Check if already has 100% crits
if(fMultiplier[i] >= 20.0) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "critChanceLimitReached");
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can't increase crit chance anymore.");
return;
}
int iPriceToPay = Multiplier_GetPrice(i) + Multiplier_GetIncrease(i) * RoundToZero(fMultiplier[i]);
int iClients = GetRealClientCount(true);
if (iClients <= 0) {
iClients = 1;
}
if(CanAfford(iPriceToPay, false)) {
for (int iLoopClient = 1; iLoopClient <= MaxClients; iLoopClient++) {
if (IsDefender(iLoopClient)) {
AddClientMetal(iLoopClient, -iPriceToPay);
}
}
fMultiplier[i] += 1.0;
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "critChanceSet", RoundToZero(fMultiplier[i] * 5.0));
//PrintToChatAll("\x04[\x03TD\x04]\x03 Crit Chance set to:\x04 %i%", RoundToZero(fMultiplier[i] * 5.0));
if(fMultiplier[i] >= 20.0) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "critChanceLimitReached");
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can't increase crit chance anymore.");
} else {
int iNextPrice = iPriceToPay + Multiplier_GetIncrease(i);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "nextUpgradeCost", iNextPrice);
//PrintToChatAll("\x04[\x03TD\x04]\x03 Next Upgrade will cost:\x04 %i\x03 metal per Player",iNextPrice);
}
}
} else if (StrContains(sName, sMultiplier) != -1) {
// Damage modifiers
int iPriceToPay = Multiplier_GetPrice(i) + Multiplier_GetIncrease(i) * RoundToZero(fMultiplier[i]);
int iClients = GetRealClientCount(true);
if (iClients <= 0) {
iClients = 1;
}
iPriceToPay /= iClients;
if(CanAfford(iPriceToPay, false)) {
for (int iLoopClient = 1; iLoopClient <= MaxClients; iLoopClient++) {
if (IsDefender(iLoopClient)) {
AddClientMetal(iLoopClient, -iPriceToPay);
}
}
fMultiplier[i] += 1.0;
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "dmgMultiplierSet", RoundToZero(fMultiplier[i] * 5.0));
//PrintToChatAll("\x04[\x03TD\x04]\x03 Multiplier set to:\x04 %i.0",RoundToZero(fMultiplier[i] + 1.0));
int iNextPrice = iPriceToPay + Multiplier_GetIncrease(i);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "nextUpgradeCost", iNextPrice);
//PrintToChatAll("\x04[\x03TD\x04]\x03 Next Upgrade will cost:\x04 %i\x03 metal per Player",iNextPrice);
}
}
}
}
}
}
/**
* Gets multiplier base price
*
* @param iMultiplierId The multipliers id.
* @return return 1000 on failure.
*/
stock int Multiplier_GetPrice(int iMultiplierId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_price", iMultiplierId);
int iPrice = 0;
if (!GetTrieValue(g_hMultiplier, sKey, iPrice)) {
return 1000;
}
return iPrice;
}
/**
* Gets multiplier increase
*
* @param iMultiplierId The multipliers id.
* @return return 1000 on failure.
*/
stock int Multiplier_GetIncrease(int iMultiplierId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_increase", iMultiplierId);
int iIncrease = 0;
if (!GetTrieValue(g_hMultiplier, sKey, iIncrease)) {
return 1000;
}
return iIncrease;
}
/**
* Gets multiplier amount
*
* @param sDamageType The damage type.
* @return return 1 on failure.
*/
stock int Multiplier_GetInt(const char[] sDamageType) {
char sKey[32], sMultiplier[32];
for (int i = 0; i <= iMaxMultiplierTypes; i++) {
Format(sKey, sizeof(sKey), "%d_type", i);
if (GetTrieString(g_hMultiplierType, sKey, sMultiplier, sizeof(sMultiplier))) {
if (StrContains(sMultiplier, sDamageType) != -1) {
return i;
}
}
}
return 1;
}
/**
* Checks if all clients have enough metal to pay a price.
*
* @param iPrice The price to pay.
* @return True if affordable, false otherwise.
*/
stock bool CanAfford(int iPrice, bool silent) {
bool bResult = true;
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
if (GetClientMetal(iClient) < iPrice) {
if (!silent) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "towerInsufficientMetal", GetClientNameShort(iClient), iPrice - GetClientMetal(iClient));
}
bResult = false;
}
}
}
return bResult;
}

View File

@@ -0,0 +1,393 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Checks if an entity is a corner.
*
* @param iCorner The corner.
* @return True if valid, false otherwise.
*/
stock bool Corner_IsValid(int iCorner) {
if (!IsValidEntity(iCorner)) {
return false;
}
char sBuffer[64];
GetEntityClassname(iCorner, sBuffer, sizeof(sBuffer));
if (StrEqual(sBuffer, "trigger_multiple")) {
GetEntPropString(iCorner, Prop_Data, "m_iName", sBuffer, sizeof(sBuffer));
return (StrContains(sBuffer, "corner_") != -1);
}
return false;
}
/**
* Finds a corner by name.
*
* @param sName The corners name.
* @return The corners entity index, or -1 on failure.
*/
stock int Corner_FindByName(const char[] sName) {
int iCorner = -1;
char sCornerName[64];
while ((iCorner = FindEntityByClassname(iCorner, "trigger_multiple")) != -1) {
Corner_GetName(iCorner, sCornerName, sizeof(sCornerName));
if (StrEqual(sCornerName, sName)) {
return iCorner;
}
}
return -1;
}
/**
* Finds a corner by it's next corners name.
*
* @param sName The next corners name.
* @return The corners entity index, or -1 on failure.
*/
stock int Corner_FindByNextName(const char[] sName) {
int iCorner = -1;
char sCornerName[64];
while ((iCorner = FindEntityByClassname(iCorner, "trigger_multiple")) != -1) {
Corner_GetNextName(iCorner, sCornerName, sizeof(sCornerName));
if (StrEqual(sCornerName, sName)) {
return iCorner;
}
}
return -1;
}
/**
* Gets a corners name.
*
* @param iCorner The corner.
* @param bValidate Validate the corner.
* @return The corner number, or -1 on failure.
*/
stock int Corner_GetNumber(int iCorner, bool bValidate = true) {
if (!bValidate) {
char sName[64];
Corner_GetName(iCorner, sName, sizeof(sName), false);
if (!StrEqual(sName, "corner_final")) {
char sNameParts[2][32];
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
return StringToInt(sNameParts[1]);
}
} else if (Corner_IsValid(iCorner)) {
char sName[64];
Corner_GetName(iCorner, sName, sizeof(sName), false);
if (!StrEqual(sName, "corner_final")) {
char sNameParts[2][32];
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
return StringToInt(sNameParts[1]);
}
}
return -1;
}
/**
* Gets a corners name.
*
* @param iCorner The corner.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @param bValidate Validate the corner.
* @return True on success, false if corner was not found.
*/
stock bool Corner_GetName(int iCorner, char[] sBuffer, int iMaxLength, bool bValidate = true) {
if (!bValidate) {
GetEntPropString(iCorner, Prop_Data, "m_iName", sBuffer, iMaxLength);
return true;
} else if (Corner_IsValid(iCorner)) {
GetEntPropString(iCorner, Prop_Data, "m_iName", sBuffer, iMaxLength);
return true;
}
return false;
}
/**
* Gets a corners next corner name.
*
* @param iCorner The corner.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @param bValidate Validate the corner.
* @return True on success, false if corner was not found.
*/
stock bool Corner_GetNextName(int iCorner, char[] sBuffer, int iMaxLength, bool bValidate = true) {
if (!bValidate) {
GetEntPropString(iCorner, Prop_Data, "m_iParent", sBuffer, iMaxLength);
return true;
} else if (Corner_IsValid(iCorner)) {
GetEntPropString(iCorner, Prop_Data, "m_iParent", sBuffer, iMaxLength);
return true;
}
return false;
}
/**
* Gets a corners location.
*
* @param iCorner The corner.
* @param fLocation The location vector.
* @param bValidate Validate the corner.
* @return True on success, false if corner was not found.
*/
stock bool Corner_GetLocation(int iCorner, float fLocation[3], bool bValidate = true) {
if (!bValidate) {
GetEntPropVector(iCorner, Prop_Data, "m_vecAbsOrigin", fLocation);
return true;
} else if (Corner_IsValid(iCorner)) {
GetEntPropVector(iCorner, Prop_Data, "m_vecAbsOrigin", fLocation);
return true;
}
return false;
}
/**
* Gets the angle between two corners.
*
* @param iCornerA The first corner.
* @param iCornerB The second corner.
* @param fAngles The angles vector.
* @param bValidate Validate the corner.
* @return True on success, false if corner was not found.
*/
stock bool Corner_GetAngles(int iCornerA, int iCornerB, float fAngles[3], bool bValidate = true) {
float fLocationCornerA[3], fLocationCornerB[3], fVector[3];
if (!bValidate) {
Corner_GetLocation(iCornerA, fLocationCornerA, false);
Corner_GetLocation(iCornerB, fLocationCornerB, false);
MakeVectorFromPoints(fLocationCornerA, fLocationCornerB, fVector);
GetVectorAngles(fVector, fAngles);
} else if (Corner_IsValid(iCornerA) && Corner_IsValid(iCornerB)) {
Corner_GetLocation(iCornerA, fLocationCornerA, false);
Corner_GetLocation(iCornerB, fLocationCornerB, false);
MakeVectorFromPoints(fLocationCornerA, fLocationCornerB, fVector);
GetVectorAngles(fVector, fAngles);
}
return false;
}
/**
* Gets the next corner.
*
* @param iCorner The current corner.
* @param sName The current corners name (optional).
* @return The next corners entity index, or -1 on failure.
*/
stock int Corner_GetNext(int iCorner = -1, const char[] sName = "") {
char sCornerName[64];
if (StrEqual(sName, "") && Corner_IsValid(iCorner)) {
Corner_GetName(iCorner, sCornerName, sizeof(sCornerName));
} else if (!StrEqual(sName, "") && !Corner_IsValid(iCorner)) {
strcopy(sCornerName, sizeof(sCornerName), sName);
iCorner = Corner_FindByName(sCornerName);
} else {
return -1;
}
if (StrContains(sCornerName, "corner_") != -1 && !StrEqual(sCornerName, "corner_final")) {
char sNextName[64];
Corner_GetNextName(iCorner, sNextName, sizeof(sNextName));
return Corner_FindByName(sNextName);
}
return -1;
}
/**
* Gets the previous corner.
*
* @param iCorner The current corner.
* @param sName The current corners name (optional).
* @return The previous corners entity index, or -1 on failure.
*/
stock int Corner_GetPrevious(int iCorner = -1, const char[] sName = "") {
char sCornerName[64];
if (StrEqual(sName, "") && Corner_IsValid(iCorner)) {
Corner_GetName(iCorner, sCornerName, sizeof(sCornerName));
} else if (!StrEqual(sName, "") && !Corner_IsValid(iCorner)) {
strcopy(sCornerName, sizeof(sCornerName), sName);
iCorner = Corner_FindByName(sCornerName);
} else {
return -1;
}
if (StrContains(sCornerName, "corner_") != -1) {
return Corner_FindByNextName(sCornerName);
}
return -1;
}
/**
* Gets the nearest corner to a client.
*
* @param iClient The client.
* @return The corners entity index, or -1 on failure.
*/
stock int Corner_GetNearest(int iClient) {
if (!IsValidClient(iClient) || !IsClientInGame(iClient) || !IsPlayerAlive(iClient)) {
return -1;
}
float fCornerLocation[3], fClientLocation[3];
GetClientAbsOrigin(iClient, fClientLocation);
int iCorner = -1, iNearestCorner = -1;
float fNearestDistance = 2147483647.0, fDistance;
while ((iCorner = FindEntityByClassname(iCorner, "trigger_multiple")) != -1) {
if (Corner_IsValid(iCorner)) {
Corner_GetLocation(iCorner, fCornerLocation, false);
fDistance = GetVectorDistance(fCornerLocation, fClientLocation);
if (fDistance < fNearestDistance) {
iNearestCorner = iCorner;
fNearestDistance = fDistance;
}
}
}
return iNearestCorner;
}
/**
* Gets the two corners a client is in between.
*
* @param iClient The client.
* @param iCornerA The first corner.
* @param iCornerB The second corner.
* @param fEpsilon The possible deviation.
* @return False if not between any corners, true otherwise.
*/
stock bool Corner_GetBetween(int iClient, int &iCornerA, int &iCornerB, float fEpsilon = 15.0) {
if (!IsValidClient(iClient) || !IsClientInGame(iClient) || !IsPlayerAlive(iClient)) {
return false;
}
static int iNearBefore[MAXPLAYERS + 1];
int iNear = Corner_GetNearest(iClient);
float fLocationNear[3], fLocationClient[3];
Corner_GetLocation(iNear, fLocationNear, false);
GetClientAbsOrigin(iClient, fLocationClient);
float fVector[3], fAngles[3], fAnglesClient[3];
MakeVectorFromPoints(fLocationNear, fLocationClient, fVector);
GetVectorAngles(fVector, fAngles);
GetClientEyeAngles(iClient, fAnglesClient);
float fAnglesDiff = FloatAbs(fAngles[1] - fAnglesClient[1]);
if (fAnglesDiff != 0.0 && fAnglesDiff != 45.0 && fAnglesDiff != 90.0 && fAnglesDiff != 135.0 && fAnglesDiff != 180.0 &&
fAnglesDiff != 225.0 && fAnglesDiff != 270.0 && fAnglesDiff != 315.0 && fAnglesDiff != 360.0 && fAnglesDiff > 5.0) {
return false;
}
int iNumberNearBefore = Corner_GetNumber(iNearBefore[iClient]);
if (iNumberNearBefore != -1 && IsValidEntity(iNear)) {
int iNumberNear = Corner_GetNumber(iNear);
if (iNumberNear != -1) {
if (Abs(iNumberNearBefore - iNumberNear) > 1) {
iNearBefore[iClient] = iNear;
return false;
}
}
}
int iNext = Corner_GetNext(iNear);
float fLocationNext[3];
Corner_GetLocation(iNext, fLocationNext);
float fResultA[3], fResultB[3];
SubtractVectors(fLocationNear, fLocationNext, fResultA);
SubtractVectors(fLocationClient, fLocationNext, fResultB);
float fScale = GetVectorDotProduct(fResultA, fResultB) / GetVectorDotProduct(fResultA, fResultA);
ScaleVector(fResultA, fScale);
float fResult[3];
SubtractVectors(fResultB, fResultA, fResult);
if (FloatAbs(fResult[0]) <= fEpsilon && FloatAbs(fResult[1]) <= fEpsilon) {
iCornerA = iNear;
iCornerB = iNext;
iNearBefore[iClient] = iNear;
return true;
}
int iPrev = Corner_GetPrevious(iNear);
float fLocationPrev[3];
Corner_GetLocation(iPrev, fLocationPrev);
SubtractVectors(fLocationNear, fLocationPrev, fResultA);
SubtractVectors(fLocationClient, fLocationPrev, fResultB);
fScale = GetVectorDotProduct(fResultA, fResultB) / GetVectorDotProduct(fResultA, fResultA);
ScaleVector(fResultA, fScale);
SubtractVectors(fResultB, fResultA, fResult);
if (FloatAbs(fResult[0]) <= fEpsilon && FloatAbs(fResult[1]) <= fEpsilon) {
iCornerA = iPrev;
iCornerB = iNear;
iNearBefore[iClient] = iNear;
return true;
}
return false;
}

View File

@@ -0,0 +1,179 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Spawns all metalpacks (except the boss and reward metalpack).
*
* @param iMetalPackType The metal pack type.
* @return True on success, false otherwiseherwise.
*/
stock bool SpawnMetalPacks(TDMetalPackType iMetalPackType) {
int iNumPacks = 0;
if (!GetTrieValue(g_hMapMetalpacks, "quantity", iNumPacks)) {
return false;
}
if (iNumPacks <= 0) {
return true;
}
int iMetal = 0, iEntity;
float fLocation[3];
char sKey[32], sLocation[64], sLocationParts[6][16];
for (int iMetalPackId = 0; iMetalPackId < iNumPacks; iMetalPackId++) {
if (Metalpack_GetType(iMetalPackId) != iMetalPackType) {
continue;
}
Format(sKey, sizeof(sKey), "%d_metal", iMetalPackId);
if (!GetTrieValue(g_hMapMetalpacks, sKey, iMetal)) {
continue;
}
Format(sKey, sizeof(sKey), "%d_location", iMetalPackId);
if (!GetTrieString(g_hMapMetalpacks, sKey, sLocation, sizeof(sLocation))) {
continue;
}
ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
fLocation[0] = StringToFloat(sLocationParts[0]);
fLocation[1] = StringToFloat(sLocationParts[1]);
fLocation[2] = StringToFloat(sLocationParts[2]);
SpawnMetalPack2(TDMetalPack_Large, fLocation, iMetal, iEntity);
if (Metalpack_GetType(iMetalPackId) == TDMetalPack_Boss) {
ShowAnnotation(EntRefToEntIndex(iEntity), fLocation, 64.0, 60.0, "A reward spawned!");
}
}
return false;
}
/**
* Spawns certain amount of metalpacks.
*
* @param iMetalPackType The metal pack type.
* @param iNumPacks Amount of metalpacks
* @return True on success, false otherwiseherwise.
*/
stock void SpawnMetalPacksNumber(TDMetalPackType iMetalPackType, int iNumPacks) {
int iMetal = 0, iEntity;
float fLocation[3];
char sKey[32], sLocation[64], sLocationParts[6][16];
for (int iMetalPackId = 0; iMetalPackId < iNumPacks; iMetalPackId++) {
if (Metalpack_GetType(iMetalPackId) != iMetalPackType) {
continue;
}
Format(sKey, sizeof(sKey), "%d_metal", iMetalPackId);
if (!GetTrieValue(g_hMapMetalpacks, sKey, iMetal)) {
continue;
}
Format(sKey, sizeof(sKey), "%d_location", iMetalPackId);
if (!GetTrieString(g_hMapMetalpacks, sKey, sLocation, sizeof(sLocation))) {
continue;
}
ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
fLocation[0] = StringToFloat(sLocationParts[0]);
fLocation[1] = StringToFloat(sLocationParts[1]);
fLocation[2] = StringToFloat(sLocationParts[2]);
SpawnMetalPack2(TDMetalPack_Large, fLocation, iMetal, iEntity);
ShowAnnotation(EntRefToEntIndex(iEntity), fLocation, 64.0, 5.0, "A Metalpack spawned!");
}
}
/**
* Spawns reward metal pack.
*
* @param iMetalPackType The type of metal pack.
* @param fLocation The location to place the metal pack.
* @param iMetal The ammount of metal to spawn.
* @noreturn
*/
stock void SpawnRewardPack(TDMetalPackSpawnType iMetalPackType, float fLocation[3], int iMetal) {
int iEntity;
SpawnMetalPack2(iMetalPackType, fLocation, iMetal, iEntity);
// Dirty hack to add outlines to the ammo packs
int PackRef = EntRefToEntIndex(iEntity);
int dispenser = CreateEntityByName("obj_dispenser");
DispatchKeyValue(dispenser, "spawnflags", "2");
DispatchKeyValue(dispenser, "solid", "0");
DispatchKeyValue(dispenser, "teamnum", "3");
SetEntProp(dispenser, Prop_Send, "m_usSolidFlags", 12); // FSOLID_TRIGGER|FSOLID_NOT_SOLID
SetEntProp(dispenser, Prop_Data, "m_nSolidType", 6); // SOLID_VPHYSICS
SetEntProp(dispenser, Prop_Data, "m_CollisionGroup", 1); //COLLISION_GROUP_DEBRIS
char model[PLATFORM_MAX_PATH];
float pos[3], ang[3];
GetEntPropString(PackRef, Prop_Data, "m_ModelName", model, sizeof(model));
GetEntPropVector(PackRef, Prop_Send, "m_vecOrigin", pos);
GetEntPropVector(PackRef, Prop_Send, "m_angRotation", ang);
SetEntProp(dispenser, Prop_Send, "m_bGlowEnabled", 1);
TeleportEntity(dispenser, pos, ang, NULL_VECTOR);
DispatchSpawn(dispenser);
SetEntityModel(dispenser, model);
SetVariantString("!activator");
AcceptEntityInput(dispenser, "SetParent", PackRef);
SDKHook(dispenser, SDKHook_OnTakeDamage, OnTakeDamage_Dispenser);
}
public Action OnTakeDamage_Dispenser(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3])
{
return Plugin_Stop;
}
/*======================================
= Data Functions =
======================================*/
/**
* Gets a metalpacks type.
*
* @param iMetalpackId The metalpacks id.
* @return A TDMetalPackType value.
*/
stock TDMetalPackType Metalpack_GetType(int iMetalpackId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_type", iMetalpackId);
char sType[64];
GetTrieString(g_hMapMetalpacks, sKey, sType, sizeof(sType));
if (StrEqual(sType, "start")) {
return TDMetalPack_Start;
} else if (StrEqual(sType, "boss")) {
return TDMetalPack_Boss;
}
return TDMetalPack_Invalid;
}

View File

@@ -0,0 +1,36 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Removes a panel.
*
* @param iWave The panels wave.
* @return True on success, false otherwise.
*/
stock bool Panel_Remove(int iWave) {
int iEntity = -1;
bool bResult = false;
char sName[64], sPanelName[64], sPanelTextName[64];
Format(sPanelName, sizeof(sPanelName), "wavePanel%d", iWave); // TODO: Rename to panel_%d
Format(sPanelTextName, sizeof(sPanelTextName), "wavePanelText%d", iWave); // TODO: Rename to panel_text_%d
while ((iEntity = FindEntityByClassname(iEntity, "func_movelinear")) != -1) {
GetEntPropString(iEntity, Prop_Data, "m_iName", sName, sizeof(sName));
if (StrEqual(sName, sPanelName) || StrEqual(sName, sPanelTextName)) {
AcceptEntityInput(iEntity, "Kill");
bResult = true;
}
}
return bResult;
}

View File

@@ -0,0 +1,428 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Called multiple times during server initialization.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @noreturn
*/
stock void Player_ServerInitializing(int iUserId, int iClient) {
if (!TF2_IsPlayerInCondition(iClient, TFCond_RestrictToMelee)) {
TF2_AddCondition(iClient, TFCond_RestrictToMelee);
SetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon", GetPlayerWeaponSlot(iClient, TFWeaponSlot_Melee));
}
SetEntityMoveType(iClient, MOVETYPE_NONE);
PrintToHud(iClient, "INITIALIZING GAME, PLEASE WAIT A MOMENT ...");
}
/**
* Called once the server is initialized.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @noreturn
*/
stock void Player_ServerInitialized(int iUserId, int iClient) {
char sCommunityId[32];
if (IsValidClient(iClient)) {
Player_UGetString(iUserId, PLAYER_COMMUNITY_ID, sCommunityId, sizeof(sCommunityId));
TF2_RemoveCondition(iClient, TFCond_RestrictToMelee);
SetEntityMoveType(iClient, MOVETYPE_WALK);
Player_SyncDatabase(iUserId, iClient, sCommunityId);
Log(TDLogLevel_Debug, "Successfully initialized player %N (%s)", iClient, sCommunityId);
}
}
/**
* Syncs all initial things of a client with the database.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @param sCommunityId The clients 64-bit steam id (community id).
* @noreturn
*/
stock void Player_SyncDatabase(int iUserId, int iClient, const char[] sCommunityId) {
Database_CheckPlayer(iUserId, iClient, sCommunityId);
}
/**
* Called once the client is connected.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @param sName The clients name.
* @param sCommunityId The clients 32-bit steam id.
* @param sCommunityId The clients 64-bit steam id (community id).
* @param sIp The clients network address (ip).
* @noreturn
*/
stock void Player_Connected(int iUserId, int iClient, const char[] sName, const char[] sSteamId, const char[] sCommunityId, const char[] sIp) {
Log(TDLogLevel_Debug, "Player connected (UserId=%d, Client=%d, Name=%s, SteamId=%s, CommunityId=%s, Address=%s)", iUserId, iClient, sName, sSteamId, sCommunityId, sIp);
if (!StrEqual(sSteamId, "BOT")) {
if (GetRealClientCount() > g_iMaxClients) {
KickClient(iClient, "Maximum number of players has been reached (%d/%d)", GetRealClientCount() - 1, g_iMaxClients);
Log(TDLogLevel_Info, "Kicked player (%N, %s) (Maximum players reached: %d/%d)", iClient, sSteamId, GetRealClientCount() - 1, g_iMaxClients);
return;
}
Log(TDLogLevel_Info, "Connected clients: %d/%d", GetRealClientCount(), g_iMaxClients);
Player_USetString(iUserId, PLAYER_STEAM_ID, sSteamId);
Player_USetString(iUserId, PLAYER_COMMUNITY_ID, sCommunityId);
Player_USetString(iUserId, PLAYER_IP_ADDRESS, sIp);
Server_UAddValue(g_iServerId, SERVER_CONNECTIONS, 1);
g_bCarryingObject[iClient] = false;
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = false;
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = false;
g_bReplaceWeapon[iClient][TFWeaponSlot_Melee] = false;
g_iAttachedTower[iClient] = 0;
ChangeClientTeam(iClient, TEAM_DEFENDER);
TF2_SetPlayerClass(iClient, TFClass_Engineer, false, true);
UpdateGameDescription();
Log(TDLogLevel_Debug, "Moved player %N to the Defenders team as Engineer", iClient);
}
}
/**
* Called before the client disconnects.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @noreturn
*/
stock void Player_OnDisconnectPre(int iUserId, int iClient) {
Database_UpdatePlayerDisconnect(iUserId);
int iTime = GetTime() - g_iTime;
Player_CSetValue(iClient, PLAYER_PLAYTIME, iTime);
Server_UAddValue(g_iServerId, SERVER_PLAYTIME, iTime);
if (GetRealClientCount(true) <= 1) { // the disconnected player is counted (thus 1 not 0)
Database_ServerStatsUpdate();
CreateTimer(10.0, Timer_Reset); // Give Queries time to send
}
}
/**
* Called once the client has entered the game (connected and loaded).
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @noreturn
*/
stock void Player_Loaded(int iUserId, int iClient) {
char sCommunityId[32];
Player_CGetString(iClient, PLAYER_COMMUNITY_ID, sCommunityId, sizeof(sCommunityId));
Log(TDLogLevel_Debug, "Player loaded (UserId=%d, Client=%d, CommunityId=%s)", iUserId, iClient, sCommunityId);
if (IsValidClient(iClient) && !IsFakeClient(iClient) && g_bTowerDefenseMap) {
CreateTimer(1.0, InitInfoTimer, iUserId, TIMER_FLAG_NO_MAPCHANGE);
}
}
public Action InitInfoTimer(Handle hTimer, any iUserId) {
if (g_bServerInitialized) {
Player_ServerInitialized(iUserId, GetClientOfUserId(iUserId));
return Plugin_Stop;
}
Player_ServerInitializing(iUserId, GetClientOfUserId(iUserId));
CreateTimer(1.0, InitInfoTimer, iUserId, TIMER_FLAG_NO_MAPCHANGE);
return Plugin_Stop;
}
/**
* Called when a client spawned.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @noreturn
*/
stock void Player_OnSpawn(int iUserId, int iClient) {
ResetClientMetal(iClient);
SetEntProp(iClient, Prop_Data, "m_bloodColor", view_as<int>(TDBlood_None));
if (g_bReplaceWeapon[iClient][TFWeaponSlot_Primary]) {
TF2Items_GiveWeapon(iClient, 9, TFWeaponSlot_Primary, 5, 1, true, "tf_weapon_shotgun_primary", "");
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = false;
}
if (g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary]) {
TF2Items_GiveWeapon(iClient, 22, TFWeaponSlot_Secondary, 5, 1, true, "tf_weapon_pistol", "");
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = false;
}
}
/**
* Called when a client dies.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @noreturn
*/
stock void Player_OnDeath(int iUserId, int iClient) {
g_bCarryingObject[iClient] = false;
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = false;
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = false;
g_bReplaceWeapon[iClient][TFWeaponSlot_Melee] = false;
Player_CAddValue(iClient, PLAYER_DEATHS, 1);
if (IsDefender(iClient) && g_iCurrentWave > 0) {
int iMetal = GetClientMetal(iClient) / 2;
if (iMetal > 0) {
float fLocation[3];
GetClientEyePosition(iClient, fLocation);
fLocation[2] = fLocation[2] - GetDistanceToGround(fLocation) + 10.0;
SpawnMetalPack(TDMetalPack_Medium, fLocation, iMetal);
}
}
if (IsTower(g_iAttachedTower[iClient])) {
Tower_OnCarrierDeath(g_iAttachedTower[iClient], iClient);
}
}
/**
* Called when a client data was set.
*
* @param iUserId The user id on server (unique on server).
* @param iClient The client.
* @param sKey The set key.
* @param iDataType The datatype of the set data.
* @param iValue The value if the set data is an integer, -1 otherwise.
* @param bValue The value if the set data is a boolean, false otherwise.
* @param fValue The value if the set data is a float, -1.0 otherwise.
* @param sValue The value if the set data is a string, empty string ("") otherwise.
* @noreturn
*/
stock void Player_OnDataSet(int iUserId, int iClient, const char[] sKey, TDDataType iDataType, int iValue, int bValue, float fValue, const char[] sValue) {
switch (iDataType) {
case TDDataType_Integer: {
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_Integer, iValue=%d", iUserId, iClient, sKey, iValue);
}
case TDDataType_Boolean: {
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_Boolean, bValue=%s", iUserId, iClient, sKey, (bValue ? "true" : "false"));
}
case TDDataType_Float: {
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_Float, fValue=%f", iUserId, iClient, sKey, fValue);
}
case TDDataType_String: {
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_String, sValue=%s", iUserId, iClient, sKey, sValue);
}
}
}
stock bool CheckClientForUserId(int iClient) {
return (iClient > 0 && iClient <= MaxClients && IsClientConnected(iClient));
}
stock void Player_UAddValue(int iUserId, const char[] sKey, int iValue) {
char sUserIdKey[128];
int iOldValue;
Player_UGetValue(iUserId, sKey, iOldValue);
if (iOldValue != -1)
iValue = iValue + iOldValue;
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, iValue, false, -1.0, "");
SetTrieValue(g_hPlayerData, sUserIdKey, iValue);
}
stock void Player_CAddValue(int iClient, const char[] sKey, int iValue) {
if (CheckClientForUserId(iClient)) {
Player_UAddValue(GetClientUserId(iClient), sKey, iValue);
}
}
stock void Player_USetValue(int iUserId, const char[] sKey, int iValue) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, iValue, false, -1.0, "");
SetTrieValue(g_hPlayerData, sUserIdKey, iValue);
}
stock void Player_CSetValue(int iClient, const char[] sKey, int iValue) {
if (CheckClientForUserId(iClient)) {
Player_USetValue(GetClientUserId(iClient), sKey, iValue);
}
}
stock bool Player_UGetValue(int iUserId, const char[] sKey, int &iValue) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Log(TDLogLevel_Trace, "Player_UGetValue: iUserId=%d, sKey=%s", iUserId, sKey);
if (!GetTrieValue(g_hPlayerData, sUserIdKey, iValue)) {
iValue = -1;
return false;
}
return true;
}
stock bool Player_CGetValue(int iClient, const char[] sKey, int &iValue) {
return CheckClientForUserId(iClient) && Player_UGetValue(GetClientUserId(iClient), sKey, iValue);
}
stock void Player_USetBool(int iUserId, const char[] sKey, bool bValue) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, -1, bValue, -1.0, "");
SetTrieValue(g_hPlayerData, sUserIdKey, (bValue ? 1 : 0));
}
stock void Player_CSetBool(int iClient, const char[] sKey, bool bValue) {
if (CheckClientForUserId(iClient)) {
Player_USetBool(GetClientUserId(iClient), sKey, bValue);
}
}
stock bool Player_UGetBool(int iUserId, const char[] sKey) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Log(TDLogLevel_Trace, "Player_UGetBool: iUserId=%d, sKey=%s", iUserId, sKey);
int iValue = 0;
GetTrieValue(g_hPlayerData, sUserIdKey, iValue);
return (iValue != 0);
}
stock bool Player_CGetBool(int iClient, const char[] sKey) {
return CheckClientForUserId(iClient) && Player_UGetBool(GetClientUserId(iClient), sKey);
}
stock void Player_USetFloat(int iUserId, const char[] sKey, float fValue) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
char sValue[64];
FloatToString(fValue, sValue, sizeof(sValue));
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, -1, false, fValue, "");
SetTrieString(g_hPlayerData, sUserIdKey, sValue);
}
stock void Player_CSetFloat(int iClient, const char[] sKey, float fValue) {
if (CheckClientForUserId(iClient)) {
Player_USetFloat(GetClientUserId(iClient), sKey, fValue);
}
}
stock bool Player_UGetFloat(int iUserId, const char[] sKey, float &fValue) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Log(TDLogLevel_Trace, "Player_UGetFloat: iUserId=%d, sKey=%s", iUserId, sKey);
char sValue[64];
if (!GetTrieString(g_hPlayerData, sUserIdKey, sValue, sizeof(sValue))) {
fValue = -1.0;
return false;
}
fValue = StringToFloat(sValue);
return true;
}
stock bool Player_CGetFloat(int iClient, const char[] sKey, float &fValue) {
return CheckClientForUserId(iClient) && Player_UGetFloat(GetClientUserId(iClient), sKey, fValue);
}
stock void Player_USetString(int iUserId, const char[] sKey, const char[] sValue, any...) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
char sFormattedValue[256];
VFormat(sFormattedValue, sizeof(sFormattedValue), sValue, 4);
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_String, -1, false, -1.0, sValue);
SetTrieString(g_hPlayerData, sUserIdKey, sFormattedValue);
}
stock void Player_CSetString(int iClient, const char[] sKey, const char[] sValue, any...) {
if (CheckClientForUserId(iClient)) {
char sFormattedValue[256];
VFormat(sFormattedValue, sizeof(sFormattedValue), sValue, 4);
Player_USetString(GetClientUserId(iClient), sKey, sFormattedValue);
}
}
stock bool Player_UGetString(int iUserId, const char[] sKey, char[] sValue, int iMaxLength) {
char sUserIdKey[128];
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
Log(TDLogLevel_Trace, "Player_UGetString: iUserId=%d, sKey=%s, iMaxLength=%d", iUserId, sKey, iMaxLength);
if (!GetTrieString(g_hPlayerData, sUserIdKey, sValue, iMaxLength)) {
Format(sValue, iMaxLength, "");
return false;
}
return true;
}
stock bool Player_CGetString(int iClient, const char[] sKey, char[] sValue, int iMaxLength) {
return CheckClientForUserId(iClient) && Player_UGetString(GetClientUserId(iClient), sKey, sValue, iMaxLength);
}
stock void Player_AddHealth(int iClient, int iHealth, bool ignoreMax = false) {
if (ignoreMax) {
SetEntityHealth(iClient, GetClientHealth(iClient) + iHealth);
} else {
int iCurrentHealth = GetEntProp(iClient, Prop_Send, "m_iHealth");
int iMaxHealth = GetEntData(iClient, FindDataMapInfo(iClient, "m_iMaxHealth"));
if (iCurrentHealth < iMaxHealth) {
SetEntityHealth(iClient, GetClientHealth(iClient) + iHealth);
}
}
}

View File

@@ -0,0 +1,361 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Initializes the server.
*
* @noreturn
*/
stock void Server_Initialize() {
Log(TDLogLevel_Debug, "Initializing server");
StripConVarFlag("sv_cheats", FCVAR_NOTIFY);
StripConVarFlag("sv_tags", FCVAR_NOTIFY);
StripConVarFlag("tf_bot_count", FCVAR_NOTIFY);
StripConVarFlag("sv_password", FCVAR_NOTIFY);
HookButtons();
Server_Reset();
int iServerIp[4];
SteamWorks_GetPublicIP(iServerIp);
Format(g_sServerIp, sizeof(g_sServerIp), "%d.%d.%d.%d", iServerIp[0], iServerIp[1], iServerIp[2], iServerIp[3]);
char sServerPort[6];
GetConVarString(FindConVar("hostport"), sServerPort, sizeof(sServerPort));
g_iServerPort = StringToInt(sServerPort);
if (StrEqual(g_sServerIp, "0.0.0.0")) {
Log(TDLogLevel_Error, "ServerIP: %s", g_sServerIp);
Log(TDLogLevel_Error, "Vac enabled?: %b", SteamWorks_IsVACEnabled());
Log(TDLogLevel_Error, "Is connected to Steam?: %b", SteamWorks_IsConnected());
Log(TDLogLevel_Error, "This can be caused by using GSLT with an expired Token. https://steamcommunity.com/dev/managegameservers");
Log(TDLogLevel_Error, "The server will loop indefinetly if it can't connect to Steam");
Log(TDLogLevel_Info, "Server has been restarted completely, reloading map for initializing");
ReloadMap();
} else {
Database_CheckServer(); // Calls Database_OnServerChecked() when finished
}
}
stock void Database_OnServerChecked() {
Log(TDLogLevel_Trace, "Database_OnServerChecked");
Database_LoadData(); // Calls Database_OnDataLoaded() when finished
}
stock void Database_OnDataLoaded() {
Log(TDLogLevel_Debug, "Successfully initialized server");
PrintToHudAll("WELCOME TO TF2 TOWER DEFENSE");
g_bServerInitialized = true;
}
/**
* Resets the server.
*
* @noreturn
*/
stock void Server_Reset() {
g_bEnabled = g_hEnabled.BoolValue && g_bTowerDefenseMap && g_bSteamWorks && g_bTF2Attributes;
g_bMapRunning = true;
UpdateGameDescription();
if (!g_bEnabled) {
if (!g_bTowerDefenseMap) {
char sCurrentMap[PLATFORM_MAX_PATH];
GetCurrentMap(sCurrentMap, sizeof(sCurrentMap));
Log(TDLogLevel_Info, "Map \"%s\" is not supported, thus Tower Defense has been disabled.", sCurrentMap);
} else {
Log(TDLogLevel_Info, "Tower Defense is disabled.");
}
return;
}
g_iBuildingLimit[TDBuilding_Sentry] = 1;
g_iBuildingLimit[TDBuilding_Dispenser] = 0;
g_iBuildingLimit[TDBuilding_TeleporterEntry] = 1;
g_iBuildingLimit[TDBuilding_TeleporterExit] = 1;
// Reset Hint Timer
if (hHintTimer != null) {
CloseHandle(hHintTimer);
hHintTimer = null;
}
for (int iClient = 1; iClient <= MaxClients; iClient++) {
// Reset carry Towers/Sentry
if (IsTower(g_iAttachedTower[iClient])) {
TF2Attrib_RemoveByName(iClient, "cannot pick up buildings");
g_iLastMover[g_iAttachedTower[iClient]] = 0;
g_bCarryingObject[iClient] = false;
g_iAttachedTower[iClient] = 0;
}
// Reset bought Towers
if (IsTower(iClient)) {
TDTowerId iTowerId = GetTowerId(iClient);
// tf_bot_quota > 0 breaks this here
if (iTowerId != TDTower_Invalid) {
g_bTowerBought[view_as<int>(iTowerId)] = false;
}
}
if (IsAttacker(iClient) && g_iSlowAttacker[iClient]) {
g_iSlowAttacker[iClient] = false;
}
if (IsDefender(iClient)) {
g_bCarryingObject[iClient] = false;
// Remove Beam if there is one
if (g_iHealBeamIndex[iClient][0] != 0) {
if (IsValidEdict(g_iHealBeamIndex[iClient][0])) {
RemoveEdict(g_iHealBeamIndex[iClient][0]);
g_iHealBeamIndex[iClient][0] = 0;
}
}
if (g_iHealBeamIndex[iClient][1] != 0) {
if (IsValidEdict(g_iHealBeamIndex[iClient][1])) {
RemoveEdict(g_iHealBeamIndex[iClient][1]);
g_iHealBeamIndex[iClient][1] = 0;
}
}
}
}
// Reset Multipliers
for (int i = 1; i <= iMaxMultiplierTypes; i++) {
fMultiplier[i] = 0.0;
}
g_iTime = GetTime();
g_iMetalPackCount = 0;
g_bTowersLocked = false;
g_bAoEEngineerAttack = false;
g_bStartWaveEarly = false;
g_iBotsToSpawn = 0;
g_iTotalBotsLeft = 0;
g_iCurrentWave = 0;
g_iNextWaveType = 0;
iAoEEngineerTimer = 0;
iAoEKritzMedicTimer = 0;
g_iHealthBar = GetHealthBar();
g_bLockable = true;
g_bCanGetUnlocks = true;
// Reset AoE Timer
if (hAoETimer != null) {
CloseHandle(hAoETimer);
hAoETimer = null;
}
// Get map max clients
g_iMaxClients = PLAYER_LIMIT;
char sQuery[256];
char sCurrentMap[PLATFORM_MAX_PATH];
GetCurrentMap(sCurrentMap, sizeof(sCurrentMap));
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `player_limit` " ...
"FROM `map` " ...
"WHERE `name` = '%s' " ...
"LIMIT 1",
sCurrentMap);
DBResultSet queryResult = SQL_Query(g_hDatabase, sQuery);
if (queryResult == null) {
char error[255];
SQL_GetError(g_hDatabase, error, sizeof(error));
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to query (error: %s)", error);
} else {
if (queryResult.HasResults && queryResult.FetchRow()) {
int iResult = queryResult.FetchInt(0);
if (iResult > 0) {
g_iMaxClients = iResult;
LogType(TDLogLevel_Debug, TDLogType_FileAndConsole, "Max clients for map %s is %d", sCurrentMap, g_iMaxClients);
} else {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Max clients for map %s is %d. This isnt supported; Please check the database entries. Setting max clients to default %d", sCurrentMap, iResult, g_iMaxClients);
}
} else {
LogType(TDLogLevel_Debug, TDLogType_FileAndConsole, "Couldn't find entry for map '%s'. Setting max clients to default %d", sCurrentMap, g_iMaxClients);
}
delete queryResult;
}
int clients = (g_iMaxClients + g_hMaxBotsOnField.IntValue) - MaxClients;
if (clients > 0) {
char cvarName[32];
g_hMaxBotsOnField.GetName(cvarName, sizeof(cvarName));
LogType(TDLogLevel_Warning, TDLogType_FileAndConsole, "ConVar '%s' value + the allowed max clients '%d' (database) is higher than the server maxplayers '%d'. Shrinking convar value by '%d'", cvarName, g_iMaxClients, MaxClients, clients);
g_hMaxBotsOnField.IntValue = g_hMaxBotsOnField.IntValue - clients;
}
Format(g_sPassword, sizeof(g_sPassword), "");
SetPassword(g_sPassword, false); // Change upon release
}
/**
* Called when a servers data was set.
*
* @param iServerId The server id (unique for every server).
* @param sKey The set key.
* @param iDataType The datatype of the set data.
* @param iValue The value if the set data is an integer, -1 otherwise.
* @param bValue The value if the set data is a boolean, false otherwise.
* @param fValue The value if the set data is a float, -1.0 otherwise.
* @param sValue The value if the set data is a string, empty string ("") otherwise.
* @noreturn
*/
stock void Server_OnDataSet(int iServerId, const char[] sKey, TDDataType iDataType, int iValue, int bValue, float fValue, const char[] sValue) {
switch (iDataType) {
case TDDataType_Integer: {
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_Integer, iValue=%d", iServerId, sKey, iValue);
}
case TDDataType_Boolean: {
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_Boolean, bValue=%s", iServerId, sKey, (bValue ? "true" : "false"));
}
case TDDataType_Float: {
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_Float, fValue=%f", iServerId, sKey, fValue);
}
case TDDataType_String: {
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_String, sValue=%s", iServerId, sKey, sValue);
}
}
}
stock void Server_UAddValue(int iServerId, const char[] sKey, int iValue) {
char sServerIdKey[128];
int iOldValue;
Server_UGetValue(iServerId, sKey, iOldValue);
if (iOldValue != -1)
iValue = iValue + iOldValue;
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, iValue, false, -1.0, "");
SetTrieValue(g_hServerData, sServerIdKey, iValue);
}
stock void Server_USetValue(int iServerId, const char[] sKey, int iValue) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, iValue, false, -1.0, "");
SetTrieValue(g_hServerData, sServerIdKey, iValue);
}
stock bool Server_UGetValue(int iServerId, const char[] sKey, int &iValue) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Log(TDLogLevel_Trace, "Server_UGetValue: iServerId=%d, sKey=%s", iServerId, sKey);
if (!GetTrieValue(g_hServerData, sServerIdKey, iValue)) {
iValue = -1;
return false;
}
return true;
}
stock void Server_USetBool(int iServerId, const char[] sKey, bool bValue) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, -1, bValue, -1.0, "");
SetTrieValue(g_hServerData, sServerIdKey, (bValue ? 1 : 0));
}
stock bool Server_UGetBool(int iServerId, const char[] sKey) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Log(TDLogLevel_Trace, "Server_UGetBool: iServerId=%d, sKey=%s", iServerId, sKey);
int iValue = 0;
GetTrieValue(g_hServerData, sServerIdKey, iValue);
return (iValue != 0);
}
stock void Server_USetFloat(int iServerId, const char[] sKey, float fValue) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
char sValue[64];
FloatToString(fValue, sValue, sizeof(sValue));
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, -1, false, fValue, "");
SetTrieString(g_hServerData, sServerIdKey, sValue);
}
stock bool Server_UGetFloat(int iServerId, const char[] sKey, float &fValue) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Log(TDLogLevel_Trace, "Server_UGetFloat: iServerId=%d, sKey=%s", iServerId, sKey);
char sValue[64];
if (!GetTrieString(g_hServerData, sServerIdKey, sValue, sizeof(sValue))) {
fValue = -1.0;
return false;
}
fValue = StringToFloat(sValue);
return true;
}
stock void Server_USetString(int iServerId, const char[] sKey, const char[] sValue, any...) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
char sFormattedValue[256];
VFormat(sFormattedValue, sizeof(sFormattedValue), sValue, 4);
Server_OnDataSet(iServerId, sKey, TDDataType_String, -1, false, -1.0, sValue);
SetTrieString(g_hServerData, sServerIdKey, sFormattedValue);
}
stock bool Server_UGetString(int iServerId, const char[] sKey, char[] sValue, int iMaxLength) {
char sServerIdKey[128];
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
Log(TDLogLevel_Trace, "Server_UGetString: iServerId=%d, sKey=%s, iMaxLength=%d", iServerId, sKey, iMaxLength);
if (!GetTrieString(g_hServerData, sServerIdKey, sValue, iMaxLength)) {
Format(sValue, iMaxLength, "");
return false;
}
return true;
}

View File

@@ -0,0 +1,68 @@
stock void PlaySound(char[] sSoundName, int iClient) {
if (iClient == 0) {
if (StrEqual(sSoundName, "Win")) {
int iRandom = GetRandomInt(1, 2);
switch (iRandom) {
case 1: {
EmitSoundToAll("vo/mvm_mannup_wave_end01.mp3");
}
case 2: {
EmitSoundToAll("vo/mvm_mannup_wave_end02.mp3");
}
}
} else if (StrEqual(sSoundName, "WaveComplete")) {
int iRandom = GetRandomInt(1, 21);
switch (iRandom) {
case 1: {
EmitSoundToAll("vo/mvm_wave_end01.mp3");
}
case 2: {
EmitSoundToAll("vo/mvm_wave_end02.mp3");
}
case 3: {
EmitSoundToAll("vo/mvm_wave_end03.mp3");
}
case 4: {
EmitSoundToAll("vo/mvm_wave_end04.mp3");
}
case 5: {
EmitSoundToAll("vo/mvm_wave_end05.mp3");
}
case 6: {
EmitSoundToAll("vo/mvm_wave_end06.mp3");
}
case 7: {
EmitSoundToAll("vo/mvm_wave_end07.mp3");
}
}
} else if (StrEqual(sSoundName, "Music")) {
int iRandom = GetRandomInt(1, 15);
switch (iRandom) {
case 1: {
EmitSoundToAll("music/mvm_start_mid_wave.wav");
}
case 2: {
EmitSoundToAll("music/mvm_start_last_wave.wav");
}
case 3: {
EmitSoundToAll("music/mvm_end_last_wave.wav");
}
}
}
} else {
if (StrEqual(sSoundName, "Forbid")) {
int iRandom = GetRandomInt(1, 3);
switch (iRandom) {
case 1: {
EmitSoundToClient(iClient, "vo/engineer_no01.mp3");
}
case 2: {
EmitSoundToClient(iClient, "vo/engineer_no02.mp3");
}
case 3: {
EmitSoundToClient(iClient, "vo/engineer_no03.mp3");
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,782 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Called when the start button is being shot.
*
* @param iWave The incoming wave.
* @param iButton The button entity.
* @param iActivator The activator entity.
* @noreturn
*/
stock void Wave_OnButtonStart(int iWave, int iButton, int iActivator) {
if (!g_bEnabled) {
return;
}
if (!IsDefender(iActivator)) {
return;
}
char sName[64];
Format(sName, sizeof(sName), "wave_start_%d", iWave + 1);
DispatchKeyValue(iButton, "targetname", sName);
TeleportEntity(iButton, view_as<float>({ 0.0, 0.0, -9192.0 }), NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
/*Translation Example
* Format: %s %t
* Values: PLUGIN_PREFIX "waveStart" GetClientNameShort(iActivator) (g_iCurrentWave + 1)
* Output: [TF2TD] [PrWh] Dragonisser started Wave 1
*/
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveStart", GetClientNameShort(iActivator), g_iCurrentWave + 1);
//Wave Health
int iWaveHealth;
int iPlayerCount = GetRealClientCount();
if (iPlayerCount > 1)
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
else
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
SetHudTextParams(-1.0, 0.6, 3.1, 255, 255, 255, 255, 1, 2.0);
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
if(Wave_GetType(g_iCurrentWave) == 0) {
ShowHudText(iClient, -1, "%t", "waveIncommingWithHealth", g_iCurrentWave + 1, iWaveHealth);
}
}
}
if (iWave == 0) {
Timer_NextWaveCountdown(null, 5);
} else {
g_bStartWaveEarly = true;
Wave_Spawn();
}
}
/**
* Called when an attacker spawned.
*
* @param iAttacker The attacker.
* @noreturn
*/
stock void Wave_OnSpawn(int iAttacker) {
if (!g_bEnabled) {
return;
}
g_bBoostWave = false;
SetRobotModel(iAttacker);
}
/**
* Called the frame after an attacker spawned.
*
* @param iAttacker The attacker.
* @noreturn
*/
public void Wave_OnSpawnPost(any iAttacker) {
if (!g_bEnabled) {
return;
}
int iMaxHealth = GetEntProp(iAttacker, Prop_Data, "m_iMaxHealth");
int iWaveHealth;
int iPlayerCount = GetRealClientCount();
if (iPlayerCount > 1)
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
else
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
TF2Attrib_SetByName(iAttacker, "max health additive bonus", float(iWaveHealth - iMaxHealth));
SetEntityHealth(iAttacker, iWaveHealth);
if (g_iNextWaveType & TDWaveType_Boss) {
// TODO(?)
}
if (g_iNextWaveType & TDWaveType_Rapid) {
g_bBoostWave = true;
}
if (g_iNextWaveType & TDWaveType_Regen) {
TF2Attrib_SetByName(iAttacker, "health regen", float(RoundFloat(iWaveHealth * 0.05)));
}
if (g_iNextWaveType & TDWaveType_KnockbackImmune) {
TF2Attrib_SetByName(iAttacker, "damage force reduction", 0.0);
}
if (g_iNextWaveType & TDWaveType_Air) {
TF2Attrib_SetByName(iAttacker, "damage force reduction", 0.0);
}
}
public void TF2_OnConditionAdded(int iClient, TFCond Condition) {
if (Condition == TFCond_Jarated && g_iNextWaveType & TDWaveType_JarateImmune)
TF2_RemoveCondition(iClient, TFCond_Jarated);
}
/**
* Called the frame after an attacker gets damaged.
*
* @param iVictim The victim.
* @param iAttacker The attacker.
* @param iInflictor The inflictor.
* @param fDamage The damage.
* @param iDamageType The damage type.
* @noreturn
*/
stock void Wave_OnTakeDamagePost(int iVictim, int iAttacker, int iInflictor, float fDamage, int iDamageType) {
if (!g_bEnabled) {
return;
}
if (IsValidEntity(g_iHealthBar)) {
int iWaveHealth;
int iPlayerCount = GetRealClientCount();
if (iPlayerCount > 1)
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
else
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
int iTotalHealth = iWaveHealth * g_iBotsToSpawn;
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsAttacker(iClient)) {
iTotalHealth += GetClientHealth(iClient);
}
}
int iTotalHealthMax = iWaveHealth * Wave_GetQuantity(g_iCurrentWave);
float fPercentage = float(iTotalHealth) / float(iTotalHealthMax);
SetEntProp(g_iHealthBar, Prop_Send, "m_iBossHealthPercentageByte", RoundToFloor(fPercentage * 255));
}
}
/**
* Called when an attacker dies.
*
* @param iAttacker The attacker.
* @noreturn
*/
stock void Wave_OnDeath(int iAttacker, float fPosition[3]) {
if (!g_bEnabled) {
return;
}
// TODO(hurp): Find a better way to make the ammo from air waves spawn on the ground,
// This method probably wont work for all maps
if (Wave_GetType(g_iCurrentWave) == TDWaveType_Air) {
fPosition[2] = fPosition[2] - 10.0;
}
// TODO(hurp): Customize metal ammount based off the wave in config files
fPosition[2] = fPosition[2] - GetDistanceToGround(fPosition) + 10.0;
SpawnRewardPack(TDMetalPack_Small, fPosition, 100);
CreateTimer(1.0, Delay_KickAttacker, iAttacker, TIMER_FLAG_NO_MAPCHANGE);
if (g_iBotsToSpawn >= 1) {
Wave_SpawnBots();
} else if (GetAliveAttackerCount() <= 1 && g_iBotsToSpawn <= 0) {
Wave_OnDeathAll();
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
Player_CAddValue(iClient, PLAYER_WAVES_PLAYED, g_iCurrentWave);
Player_CSetValue(iClient, PLAYER_WAVE_REACHED, g_iCurrentWave);
}
}
}
}
/**
* Called when all attackers died.
*
* @noreturn
*/
stock void Wave_OnDeathAll() {
if (!g_bEnabled) {
return;
}
if (g_iNextWaveType & TDWaveType_Boss) {
SpawnMetalPacks(TDMetalPack_Boss);
}
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient))
Player_CAddValue(iClient, PLAYER_WAVES_PLAYED, 1);
}
if (g_iCurrentWave + 1 >= iMaxWaves) {
PrintToServer("[TF2TD] Round won (Wave: %d)", g_iCurrentWave + 1);
Server_UAddValue(g_iServerId, SERVER_ROUNDS_WON, 1);
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
Player_CSetValue(iClient, PLAYER_ROUNDS_WON, 1);
}
}
PlaySound("Win", 0);
Wave_Win(TEAM_DEFENDER);
return;
} else {
PlaySound("WaveComplete", 0);
}
g_iTotalBotsLeft = 0;
g_bStartWaveEarly = false;
g_iCurrentWave++;
g_iNextWaveType = Wave_GetType(g_iCurrentWave);
TeleportEntity(g_iWaveStartButton, g_fWaveStartButtonLocation, NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
Timer_NextWaveCountdown(null, g_iRespawnWaveTime);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "wavePassed", g_iCurrentWave);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "wavePrepareTime", g_iRespawnWaveTime);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveTowersUnlocked");
g_bTowersLocked = false;
Log(TDLogLevel_Info, "Wave %d passed", g_iCurrentWave);
if (Panel_Remove(g_iCurrentWave)) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveBonusAvailable", g_iCurrentWave);
Log(TDLogLevel_Debug, "New bonus available (Wave: %d)", g_iCurrentWave);
}
}
/**
* Called when an attacker touches a corner.
*
* @param iCorner The corner trigger entity.
* @param iAttacker The attacker.
* @noreturn
*/
public void Wave_OnTouchCorner(int iCorner, int iAttacker) {
if (!g_bEnabled) {
return;
}
if (IsAttacker(iAttacker)) {
int iNextCorner = Corner_GetNext(iCorner);
if (iNextCorner != -1) {
float fAngles[3];
Corner_GetAngles(iCorner, iNextCorner, fAngles);
TeleportEntity(iAttacker, NULL_VECTOR, fAngles, NULL_VECTOR);
} else {
g_bBoostWave = false;
}
}
}
/**
* Spawns a wave.
*
* @noreturn
*/
stock void Wave_Spawn() {
// Delete ammo packs loot that have not been picked up
char buffer[64];
int entity = -1;
while ((entity = FindEntityByClassname(entity, "prop_dynamic")) != INVALID_ENT_REFERENCE) {
GetEntPropString(entity, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
if (StrEqual(buffer, "models/items/ammopack_small.mdl")) {
AcceptEntityInput(entity, "Kill");
g_iMetalPackCount--;
}
}
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveIncomming", g_iCurrentWave + 1);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveTowersLocked");
g_bTowersLocked = true;
g_iBotsToSpawn = Wave_GetQuantity(g_iCurrentWave);
SetEntProp(g_iHealthBar, Prop_Send, "m_iBossHealthPercentageByte", 255);
g_iTotalBotsLeft = Wave_GetQuantity(g_iCurrentWave);
Wave_SpawnBots();
}
stock void Wave_SpawnBots() {
if (g_iBotsToSpawn <= 0) {
return;
}
char sName[MAX_NAME_LENGTH];
if (!Wave_GetName(g_iCurrentWave, sName, sizeof(sName))) {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to spawn wave %d, could not read name!", g_iCurrentWave);
return;
}
char sClass[32];
if (!Wave_GetClassString(g_iCurrentWave, sClass, sizeof(sClass))) {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to spawn wave %d, could not read class!", g_iCurrentWave);
return;
}
int iTotalBots = Wave_GetQuantity(g_iCurrentWave);
int iAliveBots = GetAliveAttackerCount();
// If only less than g_iMaxBotsOnField bots in total
if (iTotalBots <= g_hMaxBotsOnField.IntValue) {
if (iTotalBots > 1) {
for (int i = 1; i <= iTotalBots; i++) {
ServerCommand("bot -team red -class %s -name %s%d", sClass, sName, i);
g_iBotsToSpawn--;
}
// If only 1 bot
} else {
ServerCommand("bot -team red -class %s -name %s", sClass, sName);
g_iBotsToSpawn = 0;
}
CreateTimer(1.0, TeleportWaveDelay, iTotalBots, TIMER_FLAG_NO_MAPCHANGE);
// Else more than g_iMaxBotsOnField bots
} else {
// If no bot alive
if (iAliveBots <= 0) {
for (int i = 1; i <= g_hMaxBotsOnField.IntValue; i++) {
g_iBotsToSpawn--;
ServerCommand("bot -team red -class %s -name %s%d", sClass, sName, -(g_iBotsToSpawn - iTotalBots));
}
CreateTimer(1.0, TeleportWaveDelay, g_hMaxBotsOnField.IntValue, TIMER_FLAG_NO_MAPCHANGE);
// If bots alive
} else {
int iBotsToSpawn = g_hMaxBotsOnField.IntValue - iAliveBots;
for (int i = 1; i <= iBotsToSpawn; i++) {
g_iBotsToSpawn--;
ServerCommand("bot -team red -class %s -name %s%d", sClass, sName, -(g_iBotsToSpawn - iTotalBots));
}
CreateTimer(1.0, TeleportWaveDelay, iBotsToSpawn, TIMER_FLAG_NO_MAPCHANGE);
}
}
}
public Action TeleportWaveDelay(Handle hTimer, any iNumber) {
if (iNumber <= 0) {
return Plugin_Stop;
}
int iTotalBots = Wave_GetQuantity(g_iCurrentWave);
char sName[MAX_NAME_LENGTH];
if (!Wave_GetName(g_iCurrentWave, sName, sizeof(sName))) {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to teleport wave %d, could not read name!", g_iCurrentWave);
return Plugin_Stop;
}
if (iTotalBots <= 1) {
Format(sName, sizeof(sName), "%s", sName);
} else if (iTotalBots > g_hMaxBotsOnField.IntValue) {
g_iTotalBotsLeft--;
Format(sName, sizeof(sName), "%s%d", sName, -(g_iTotalBotsLeft - iTotalBots));
} else if (iNumber > 0) {
Format(sName, sizeof(sName), "%s%d", sName, iNumber);
}
int iAttacker = GetClientByNameExact(sName, TEAM_ATTACKER);
Log(TDLogLevel_Trace, "Should teleport attacker %d (%d, %s) of wave %d (%d attackers)", iNumber, iAttacker, sName, g_iCurrentWave + 1, Wave_GetQuantity(g_iCurrentWave));
if (IsAttacker(iAttacker)) {
float fLocation[3];
if (!Wave_GetLocation(g_iCurrentWave, fLocation)) {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to teleport wave %d, could not read location!", g_iCurrentWave);
return Plugin_Stop;
}
float fAngles[3];
if (!Wave_GetAngles(g_iCurrentWave, fAngles)) {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to teleport wave %d, could not read angles!", g_iCurrentWave);
return Plugin_Stop;
}
TeleportEntity(iAttacker, fLocation, fAngles, view_as<float>({ 0.0, 0.0, 0.0 }));
Log(TDLogLevel_Trace, " -> Teleported attacker");
CreateTimer(1.0, TeleportWaveDelay, iNumber - 1, TIMER_FLAG_NO_MAPCHANGE);
}
return Plugin_Stop;
}
public void Wave_Win(int iTeam) {
int iEntity = -1;
iEntity = FindEntityByClassname2(iEntity, "team_control_point_master");
if (iEntity == -1 || !IsValidEntity(iEntity)) {
// No team_control_point_master either... lets create one.
iEntity = CreateEntityByName("team_control_point_master");
DispatchKeyValue(iEntity, "targetname", "master_control_point");
DispatchKeyValue(iEntity, "StartDisabled", "0");
DispatchSpawn(iEntity);
}
SetVariantInt(iTeam);
AcceptEntityInput(iEntity, "SetWinner");
}
/*=========================================
= Utility Functions =
=========================================*/
public Action Delay_KickAttacker(Handle hTimer, any iAttacker) {
if (IsAttacker(iAttacker)) {
KickClient(iAttacker);
}
return Plugin_Stop;
}
public Action Timer_NextWaveCountdown(Handle hTimer, any iTime) {
if (g_bStartWaveEarly) {
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "waveStartedEarly", (iTime + 1) * 10, iTime + 1);
AddClientMetal(iClient, (iTime + 1) * 10);
}
}
return Plugin_Stop;
}
switch (iTime) {
case 5: {
SetHudTextParams(-1.0, 0.6, 5.1, 255, 255, 255, 255, 2, 2.0);
// Wave Health
int iWaveHealth;
int iPlayerCount = GetRealClientCount();
if (iPlayerCount > 1)
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
else
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
char sType[256];
strcopy(sType, sizeof(sType), "");
if (g_iNextWaveType & TDWaveType_Rapid) {
if (StrEqual(sType, "")) {
Format(sType, sizeof(sType), "%T", "waveTypeRapid", iClient);
} else {
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeRapid", iClient);
}
}
if (g_iNextWaveType & TDWaveType_Regen) {
if (StrEqual(sType, "")) {
Format(sType, sizeof(sType), "%T", "waveTypeRegen", iClient);
} else {
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeRegen", iClient);
}
}
if (g_iNextWaveType & TDWaveType_KnockbackImmune) {
if (StrEqual(sType, "")) {
Format(sType, sizeof(sType), "%T", "waveTypeKnockbackImmune", iClient);
} else {
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeKnockbackImmune", iClient);
}
}
if (g_iNextWaveType & TDWaveType_Air) {
if (StrEqual(sType, "")) {
Format(sType, sizeof(sType), "%T", "waveTypeAir", iClient);
} else {
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeAir", iClient);
}
}
if (g_iNextWaveType & TDWaveType_JarateImmune) {
if (StrEqual(sType, "")) {
Format(sType, sizeof(sType), "%T", "waveTypeJarateImmune", iClient);
} else {
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeJarateImmune", iClient);
}
}
if (g_iNextWaveType & TDWaveType_Boss) {
if (StrEqual(sType, "")) {
Format(sType, sizeof(sType), "%T", "waveTypeBoss", iClient);
} else {
Format(sType, sizeof(sType), "%s %T", sType, "waveTypeBoss", iClient);
}
}
if (StrEqual(sType, "")) {
ShowHudText(iClient, -1, "%t", "waveIncommingWithHealth", g_iCurrentWave + 1, iWaveHealth);
} else {
ShowHudText(iClient, -1, "%t", "waveIncommingWithHealthAndType", sType, g_iCurrentWave + 1, iWaveHealth);
}
}
}
EmitSoundToAll("vo/announcer_begins_5sec.mp3");
}
case 4: {
EmitSoundToAll("vo/announcer_begins_4sec.mp3");
}
case 3: {
EmitSoundToAll("vo/announcer_begins_3sec.mp3");
}
case 2: {
EmitSoundToAll("vo/announcer_begins_2sec.mp3");
}
case 1: {
EmitSoundToAll("vo/announcer_begins_1sec.mp3");
TeleportEntity(g_iWaveStartButton, view_as<float>({ 0.0, 0.0, -9192.0 }), NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
}
case 0: {
PlaySound("Music", 0);
Wave_Spawn();
return Plugin_Stop;
}
}
SetHudTextParams(-1.0, 0.85, 1.1, 255, 255, 255, 255);
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsDefender(iClient)) {
ShowHudText(iClient, -1, "%t", "waveArrivingIn", iTime);
}
}
CreateTimer(1.0, Timer_NextWaveCountdown, iTime - 1, TIMER_FLAG_NO_MAPCHANGE);
return Plugin_Stop;
}
/**
* Gets the count of alive attackers.
*
* @return Count of alive attackers.
*/
stock int GetAliveAttackerCount() {
int iAttackers = 0;
for (int iClient = 1; iClient <= MaxClients; iClient++) {
if (IsAttacker(iClient) && IsPlayerAlive(iClient)) {
iAttackers++;
}
}
return iAttackers;
}
/*======================================
= Data Functions =
======================================*/
/**
* Gets the name of a wave.
*
* @param iWave The wave.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @return True on success, false if wave was not found.
*/
stock bool Wave_GetName(int iWave, char[] sBuffer, int iMaxLength) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_name", iWave);
return GetTrieString(g_hMapWaves, sKey, sBuffer, iMaxLength);
}
/**
* Gets the type of a wave.
*
* @param iWave The wave.
* @return The waves type bit field.
*/
stock int Wave_GetType(int iWave) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_type", iWave);
int iType = 0;
if (!GetTrieValue(g_hMapWaves, sKey, iType)) {
return -1;
}
return iType;
}
/**
* Gets the class of a wave.
*
* @param iWave The wave.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @return True on success, false if wave was not found.
*/
stock bool Wave_GetClassString(int iWave, char[] sBuffer, int iMaxLength) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_class", iWave);
return GetTrieString(g_hMapWaves, sKey, sBuffer, iMaxLength);
}
/**
* Gets the class of a wave.
*
* @param iWave The wave.
* @return The waves class type, or TFClass_Unknown on error.
*/
stock TFClassType Wave_GetClass(int iWave) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_class", iWave);
char sClass[32];
GetTrieString(g_hMapWaves, sKey, sClass, sizeof(sClass));
if (StrEqual(sClass, "Scout")) {
return TFClass_Scout;
} else if (StrEqual(sClass, "Sniper")) {
return TFClass_Sniper;
} else if (StrEqual(sClass, "Soldier")) {
return TFClass_Soldier;
} else if (StrEqual(sClass, "Demoman")) {
return TFClass_DemoMan;
} else if (StrEqual(sClass, "Medic")) {
return TFClass_Medic;
} else if (StrEqual(sClass, "Heavy")) {
return TFClass_Heavy;
} else if (StrEqual(sClass, "Pyro")) {
return TFClass_Pyro;
} else if (StrEqual(sClass, "Spy")) {
return TFClass_Spy;
} else if (StrEqual(sClass, "Spy")) {
return TFClass_Engineer;
}
return TFClass_Unknown;
}
/**
* Gets the quantity of a wave.
*
* @param iWave The wave.
* @return The waves quantity, or -1 on failure.
*/
stock int Wave_GetQuantity(int iWave) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_quantity", iWave);
int iQuantity = 0;
if (!GetTrieValue(g_hMapWaves, sKey, iQuantity)) {
return -1;
}
return iQuantity;
}
/**
* Gets the health of a wave.
*
* @param iWave The wave.
* @return The waves health, or -1 on failure.
*/
stock int Wave_GetHealth(int iWave) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_health", iWave);
int iHealth = 0;
if (!GetTrieValue(g_hMapWaves, sKey, iHealth)) {
return -1;
}
return iHealth;
}
/**
* Gets the location of a wave.
*
* @param iWave The wave.
* @param fLocation The location vector.
* @return True on success, false if wave was not found.
*/
stock bool Wave_GetLocation(int iWave, float fLocation[3]) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_location", iWave);
char sLocation[64];
if (GetTrieString(g_hMapWaves, sKey, sLocation, sizeof(sLocation))) {
char sLocationParts[6][16];
ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
fLocation[0] = StringToFloat(sLocationParts[0]);
fLocation[1] = StringToFloat(sLocationParts[1]);
fLocation[2] = StringToFloat(sLocationParts[2]);
return true;
}
return false;
}
/**
* Gets the angles of a wave.
*
* @param iWave The wave.
* @param fAngles The angles vector.
* @return True on success, false if wave was not found.
*/
stock bool Wave_GetAngles(int iWave, float fAngles[3]) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_location", iWave);
char sAngles[64];
if (GetTrieString(g_hMapWaves, sKey, sAngles, sizeof(sAngles))) {
char sAnglesParts[6][16];
ExplodeString(sAngles, " ", sAnglesParts, sizeof(sAnglesParts), sizeof(sAnglesParts[]));
fAngles[0] = StringToFloat(sAnglesParts[3]);
fAngles[1] = StringToFloat(sAnglesParts[4]);
fAngles[2] = StringToFloat(sAnglesParts[5]);
return true;
}
return false;
}
/**
* Finds an entity by using its classname.
*
* @param iStartEnt The start entity entity index.
* @param sClassname The entity classname to look for.
* @return The entitys entity index, or -1 on failure.
*/
public int FindEntityByClassname2(int iStartEnt, const char[] sClassname) {
/* If iStartEnt isn't valid shifting it back to the nearest valid one */
while (iStartEnt > -1 && !IsValidEntity(iStartEnt))
iStartEnt--;
return FindEntityByClassname(iStartEnt, sClassname);
}

View File

@@ -0,0 +1,178 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/*======================================
= Data Functions =
======================================*/
/**
* Gets the name of a weapon.
*
* @param iWeaponId The weapons id.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @return True on success, false if weapon was not found.
*/
stock bool Weapon_GetName(int iWeaponId, char[] sBuffer, int iMaxLength) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_name", iWeaponId);
return GetTrieString(g_hMapWeapons, sKey, sBuffer, iMaxLength);
}
/**
* Gets the index of a weapon.
*
* @param iWeaponId The weapons id.
* @return The weapons index, or -1 on failure.
*/
stock int Weapon_GetIndex(int iWeaponId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_index", iWeaponId);
int iIndex = 0;
if (!GetTrieValue(g_hMapWeapons, sKey, iIndex)) {
return -1;
}
return iIndex;
}
/**
* Gets the slot of a weapon.
*
* @param iWeaponId The weapons id.
* @return The weapons slot, or -1 on failure.
*/
stock int Weapon_GetSlot(int iWeaponId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_slot", iWeaponId);
int iSlot = 0;
if (!GetTrieValue(g_hMapWeapons, sKey, iSlot)) {
return -1;
}
return iSlot;
}
/**
* Gets the level of a weapon.
*
* @param iWeaponId The weapons id.
* @return The weapons level, or -1 on failure.
*/
stock int Weapon_GetLevel(int iWeaponId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_level", iWeaponId);
int iLevel = 0;
if (!GetTrieValue(g_hMapWeapons, sKey, iLevel)) {
return -1;
}
return iLevel;
}
/**
* Gets the quality of a weapon.
*
* @param iWeaponId The weapons id.
* @return The weapons quality, or -1 on failure.
*/
stock int Weapon_GetQuality(int iWeaponId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_quality", iWeaponId);
int iQuality = 0;
if (!GetTrieValue(g_hMapWeapons, sKey, iQuality)) {
return -1;
}
return iQuality;
}
/**
* Gets the classname of a weapon.
*
* @param iWeaponId The weapons id.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @return True on success, false if weapon was not found.
*/
stock bool Weapon_GetClassname(int iWeaponId, char[] sBuffer, int iMaxLength) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_classname", iWeaponId);
return GetTrieString(g_hMapWeapons, sKey, sBuffer, iMaxLength);
}
/**
* Gets the attributes of a weapon.
*
* @param iWeaponId The weapons id.
* @param iAttributes The attribute id array.
* @param iWeaponId The attribute value array.
* @return The attribute count.
*/
stock int Weapon_GetAttributes(int iWeaponId, int iAttributes[16], float iValues[16]) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_attributes", iWeaponId);
char sAttributes[256];
if (GetTrieString(g_hMapWeapons, sKey, sAttributes, sizeof(sAttributes))) {
char sAttributeParts[32][6];
int iCount = ExplodeString(sAttributes, ";", sAttributeParts, sizeof(sAttributeParts), sizeof(sAttributeParts[]));
if (iCount % 2 != 0) {
Log(TDLogLevel_Error, "Failed to parse weapon attributes for weapon id %d (some attribute has no value)", iWeaponId);
return 0;
}
int iAttributeCount = 0;
for (int i = 0; i < iCount && iAttributeCount < 16; i += 2) {
iAttributes[iAttributeCount] = StringToInt(sAttributeParts[i]);
iValues[iAttributeCount] = StringToFloat(sAttributeParts[i + 1]);
iAttributeCount++;
}
return iAttributeCount;
}
return 0;
}
/**
* Checks if a weapon should preserve its attributes.
*
* @param iWeaponId The weapons id.
* @return True if should preserve, false otherwise.
*/
stock bool Weapon_GetPreserveAttributes(int iWeaponId) {
char sKey[32];
Format(sKey, sizeof(sKey), "%d_preserve_attributes", iWeaponId);
int iPreserve = 0;
if (!GetTrieValue(g_hMapWeapons, sKey, iPreserve)) {
return false;
}
return (iPreserve != 0);
}