Initial commit
This commit is contained in:
45
scripting/towerdefense/handler/antiair.sp
Normal file
45
scripting/towerdefense/handler/antiair.sp
Normal 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;
|
||||
}
|
429
scripting/towerdefense/handler/aoe.sp
Normal file
429
scripting/towerdefense/handler/aoe.sp
Normal 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);
|
||||
}
|
280
scripting/towerdefense/handler/buttons.sp
Normal file
280
scripting/towerdefense/handler/buttons.sp
Normal 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;
|
||||
}
|
393
scripting/towerdefense/handler/corners.sp
Normal file
393
scripting/towerdefense/handler/corners.sp
Normal 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;
|
||||
}
|
179
scripting/towerdefense/handler/metalpacks.sp
Normal file
179
scripting/towerdefense/handler/metalpacks.sp
Normal 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;
|
||||
}
|
36
scripting/towerdefense/handler/panels.sp
Normal file
36
scripting/towerdefense/handler/panels.sp
Normal 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;
|
||||
}
|
428
scripting/towerdefense/handler/player.sp
Normal file
428
scripting/towerdefense/handler/player.sp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
361
scripting/towerdefense/handler/server.sp
Normal file
361
scripting/towerdefense/handler/server.sp
Normal 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;
|
||||
}
|
68
scripting/towerdefense/handler/sounds.sp
Normal file
68
scripting/towerdefense/handler/sounds.sp
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1032
scripting/towerdefense/handler/towers.sp
Normal file
1032
scripting/towerdefense/handler/towers.sp
Normal file
File diff suppressed because it is too large
Load Diff
782
scripting/towerdefense/handler/waves.sp
Normal file
782
scripting/towerdefense/handler/waves.sp
Normal 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);
|
||||
}
|
178
scripting/towerdefense/handler/weapons.sp
Normal file
178
scripting/towerdefense/handler/weapons.sp
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user