Initial commit

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

View File

@@ -0,0 +1,465 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "info/constants.sp"
#include "info/enums.sp"
#include "info/variables.sp"
#endif
stock void RegisterCommands() {
// Commands for testing purposes
//RegAdminCmd("sm_gm", Command_GiveMetal, ADMFLAG_ROOT);
//RegAdminCmd("sm_r", Command_ReloadMap, ADMFLAG_ROOT);
//RegAdminCmd("sm_sw", Command_SetWave, ADMFLAG_ROOT);
//RegAdminCmd("sm_bt", Command_BuyTower, ADMFLAG_ROOT);
// Temporary commands
RegAdminCmd("sm_pregame", Command_PreGame, ADMFLAG_ROOT);
RegAdminCmd("sm_password", Command_Password, ADMFLAG_ROOT);
// Client Commands
RegConsoleCmd("sm_p", Command_GetPassword);
RegConsoleCmd("sm_s", Command_BuildSentry);
RegConsoleCmd("sm_sentry", Command_BuildSentry);
RegConsoleCmd("sm_d", Command_DropMetal);
RegConsoleCmd("sm_drop", Command_DropMetal);
RegConsoleCmd("sm_m", Command_ShowMetal);
RegConsoleCmd("sm_metal", Command_ShowMetal);
RegConsoleCmd("sm_w", Command_ShowWave);
RegConsoleCmd("sm_wave", Command_ShowWave);
RegConsoleCmd("sm_t", Command_TransferMetal);
RegConsoleCmd("sm_transfer", Command_TransferMetal);
RegConsoleCmd("sm_givemetal", Command_TransferMetal);
// Command Listeners
AddCommandListener(CommandListener_Build, "build");
AddCommandListener(CommandListener_ClosedMotd, "closed_htmlpage");
AddCommandListener(CommandListener_Exec, "exec");
}
/*=====================================
= Test Commands =
=====================================*/
public Action Command_GiveMetal(int iClient, any iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
char sMetal[16], arg[65], target_name[MAX_TARGET_LENGTH];
int target_list[MAXPLAYERS], target_count;
bool tn_is_ml;
if (iArgs < 1) {
ReplyToCommand(iClient, "[SM] Usage: sm_gm <#userid|name> <metal>");
return Plugin_Handled;
} else if (iArgs == 1) {
GetCmdArg(1, sMetal, sizeof(sMetal));
AddClientMetal(iClient, StringToInt(sMetal));
} else if (iArgs >= 2) {
GetCmdArg(1, arg, sizeof(arg));
GetCmdArg(2, sMetal, sizeof(sMetal));
if ((target_count = ProcessTargetString(
arg,
iClient,
target_list,
MAXPLAYERS,
COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_BOTS | COMMAND_FILTER_CONNECTED,
target_name,
sizeof(target_name),
tn_is_ml))
<= 0) {
ReplyToCommand(iClient, "[SM] Player not found");
return Plugin_Handled;
}
for (int i = 0; i < target_count; i++) {
AddClientMetal(target_list[i], StringToInt(sMetal));
}
}
return Plugin_Handled;
}
public Action Command_ReloadMap(int iClient, int iArgs) {
ReloadMap();
return Plugin_Handled;
}
public Action Command_SetWave(int iClient, int iArgs) {
if (iArgs != 1) {
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdSetWaveUsage");
//PrintToChat(iClient, "Usage: !sw <wave>");
return Plugin_Handled;
}
char sWave[6];
GetCmdArg(1, sWave, sizeof(sWave));
if(StringToInt(sWave) - 1 >= iMaxWaves)
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdSetWaveOOB", iMaxWaves);
//PrintToChat(iClient, "[SM] The highest wave is %i. Please choose a lower value than that!", iMaxWaves);
else {
g_iCurrentWave = StringToInt(sWave) - 1;
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdSetWave", g_iCurrentWave + 1);
//PrintToChat(iClient, "[SM] Wave set to %i.", g_iCurrentWave + 1);
}
return Plugin_Handled;
}
public Action Command_BuyTower(int iClient, int iArgs) {
if (iArgs != 1) {
return Plugin_Handled;
}
char sTower[6];
GetCmdArg(1, sTower, sizeof(sTower));
TDTowerId iTowerId = view_as<TDTowerId>(StringToInt(sTower));
if (!g_bTowerBought[view_as<int>(iTowerId)]) {
char sName[MAX_NAME_LENGTH];
Tower_GetName(iTowerId, sName, sizeof(sName));
Tower_Spawn(iTowerId);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdBuyTower", GetClientNameShort(iClient), sName);
//PrintToChatAll("\x01%N bought \x04%s", iClient, sName);
g_bTowerBought[view_as<int>(iTowerId)] = true;
}
return Plugin_Handled;
}
/*===================================
= Start Round =
===================================*/
public Action Command_PreGame(int iClient, int iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
SetPregameConVars();
SpawnMetalPacks(TDMetalPack_Start);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdPreGameInfo1");
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdPreGameInfo2");
//PrintToChatAll("\x04Have fun playing!");
//PrintToChatAll("\x04Don't forget to pick up dropped metal packs!");
// Hook func_nobuild events
int iEntity = -1;
while ((iEntity = FindEntityByClassname(iEntity, "func_nobuild")) != -1) {
SDKHook(iEntity, SDKHook_StartTouch, OnNobuildEnter);
SDKHook(iEntity, SDKHook_EndTouch, OnNobuildExit);
}
return Plugin_Handled;
}
public Action Command_Password(int iClient, int iArgs) {
if (g_bLockable) {
for (int i = 0; i < 4; i++) {
switch (GetRandomInt(0, 2)) {
case 0: {
g_sPassword[i] = GetRandomInt('1', '9');
}
case 1, 2: {
g_sPassword[i] = GetRandomInt('a', 'z');
}
}
}
g_sPassword[4] = '\0';
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdSetPasswordInfo1", g_sPassword);
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdSetPasswordInfo2");
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdSetPasswordInfo3");
//PrintToChatAll("\x01Set the server password to \x04%s", g_sPassword);
//PrintToChatAll("\x01If you want your friends to join, tell them the password.");
//PrintToChatAll("\x01Write \x04!p\x01 to see the password again.");
SetPassword(g_sPassword);
} else {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdServerNotLockable");
//PrintToChatAll("This server can't be locked!");
}
return Plugin_Handled;
}
/*=======================================
= Client Commands =
=======================================*/
public Action Command_GetPassword(int iClient, int iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
if(!StrEqual(g_sPassword, "")) {
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdPassword", g_sPassword);
//PrintToChatAll("\x01The server password is \x04%s", g_sPassword);
} else {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidNoPasswordSet");
}
return Plugin_Continue;
}
public Action Command_BuildSentry(int iClient, int iArgs) {
if (!CanClientBuild(iClient, TDBuilding_Sentry)) {
return Plugin_Handled;
}
if (IsInsideClient(iClient)) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidCantBuildInsideAPlayer");
return Plugin_Handled;
}
int iSentry = CreateEntityByName("obj_sentrygun");
if (DispatchSpawn(iSentry) && IsValidEntity(iSentry)) {
Player_CAddValue(iClient, PLAYER_OBJECTS_BUILT, 1);
AcceptEntityInput(iSentry, "SetBuilder", iClient);
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", 150);
SetEntProp(iSentry, Prop_Send, "m_iHealth", 150);
DispatchKeyValue(iSentry, "angles", "0 0 0");
DispatchKeyValue(iSentry, "defaultupgrade", "0");
DispatchKeyValue(iSentry, "TeamNum", "3");
DispatchKeyValue(iSentry, "spawnflags", "0");
float fLocation[3], fAngles[3];
GetClientAbsOrigin(iClient, fLocation);
GetClientEyeAngles(iClient, fAngles);
fLocation[2] += 30;
fAngles[0] = 0.0;
fAngles[2] = 0.0;
TeleportEntity(iSentry, fLocation, fAngles, NULL_VECTOR);
AddClientMetal(iClient, -130);
g_bPickupSentry[iClient] = true;
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdBuildSentryInfo");
//PrintToChat(iClient, "\x01Sentries need \x041000 metal\x01 to upgrade!");
}
return Plugin_Handled;
}
public Action Command_DropMetal(int iClient, int iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
if (iArgs != 1) {
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdDropMetalUsage");
//PrintToChat(iClient, "\x01Usage: !d <amount>");
return Plugin_Handled;
}
char sMetal[32];
GetCmdArg(1, sMetal, sizeof(sMetal));
int iMetal;
if (!IsStringNumeric(sMetal)) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidInvalidInput");
return Plugin_Handled;
} else {
iMetal = StringToInt(sMetal);
}
if (iMetal <= 0) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidDropMinMetal");
return Plugin_Handled;
}
if (!IsPlayerAlive(iClient)) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidDeadCantDropMetal");
return Plugin_Handled;
}
if (!(GetEntityFlags(iClient) & FL_ONGROUND)) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidInAirCantDropMetal");
return Plugin_Handled;
}
if (iMetal > GetClientMetal(iClient)) {
iMetal = GetClientMetal(iClient);
}
float fLocation[3], fAngles[3];
GetClientEyePosition(iClient, fLocation);
GetClientEyeAngles(iClient, fAngles);
fLocation[0] = fLocation[0] + 100 * Cosine(DegToRad(fAngles[1]));
fLocation[1] = fLocation[1] + 100 * Sine(DegToRad(fAngles[1]));
fLocation[2] = fLocation[2] - GetDistanceToGround(fLocation) + 10.0;
switch (SpawnMetalPack(TDMetalPack_Medium, fLocation, iMetal)) {
case TDMetalPack_InvalidMetal: {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidDropMinMetal");
}
case TDMetalPack_LimitReached: {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidDropMetalLimit");
}
case TDMetalPack_InvalidType: {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidUnableDropMetal");
}
case TDMetalPack_SpawnedPack: {
AddClientMetal(iClient, -iMetal);
Player_CAddValue(iClient, PLAYER_METAL_DROP, iMetal);
}
}
return Plugin_Handled;
}
public Action Command_ShowMetal(int iClient, int iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdMetalStats");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Metal stats:");
for (int iPlayer = 1; iPlayer <= MaxClients; iPlayer++) {
if (IsDefender(iPlayer)) {
CPrintToChatAll("%t", "cmdMetalStatsPlayer", GetClientNameShort(iPlayer), GetClientMetal(iPlayer));
//PrintToChatAll("\x04%N - %d metal", i, GetClientMetal(i));
}
}
return Plugin_Handled;
}
public Action Command_ShowWave(int iClient, int iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "cmdCurrentWave", g_iCurrentWave + 1, iMaxWaves);
//PrintToChatAll("\x04[\x03TD\x04]\x03 Currently on Wave %i out of %i", g_iCurrentWave + 1, iMaxWaves);
return Plugin_Handled;
}
public Action Command_TransferMetal(int iClient, int iArgs) {
if (!g_bEnabled) {
return Plugin_Handled;
}
if (iArgs != 2) {
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdTransferMetalUsage");
//PrintToChat(iClient, "\x01Usage: !t <target> <amount>");
return Plugin_Handled;
}
char sTarget[64];
GetCmdArg(1, sTarget, sizeof(sTarget));
char sMetal[32];
GetCmdArg(2, sMetal, sizeof(sMetal));
int iMetal;
int iTarget = GetClientByName(iClient, sTarget);
if (!IsStringNumeric(sMetal)) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidInvalidInput");
return Plugin_Handled;
} else {
iMetal = StringToInt(sMetal);
}
if (iMetal > GetClientMetal(iClient) || GetClientMetal(iClient) <= 0) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidTransferNotEnough");
//Forbid(iClient, true, "You can't transfer more metal then you have!");
return Plugin_Handled;
}
if (iMetal < 0) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidTransferNegative");
//Forbid(iClient, true, "Can't transfer negative amounts!");
return Plugin_Handled;
}
if (!IsPlayerAlive(iClient)) {
Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidTransferDead");
//Forbid(iClient, true, "Can't transfer while dead!");
return Plugin_Handled;
}
if (IsDefender(iTarget) && IsPlayerAlive(iTarget)) {
AddClientMetal(iTarget, iMetal);
AddClientMetal(iClient, -iMetal);
CPrintToChat(iTarget, "%s %t", PLUGIN_PREFIX, "cmdTransferMetalReceived", iMetal, GetClientNameShort(iClient));
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "cmdTransferMetalSent", GetClientNameShort(iTarget), iMetal);
//PrintToChat(iTarget, "\x04You received \x01%d metal \x04from \x01%N\x04.", iMetal, iClient);
//PrintToChat(iClient, "\x01%N \x04received \x01%d metal \x04from you.", iTarget, iMetal);
}
return Plugin_Continue;
}
/*=========================================
= Command Listeners =
=========================================*/
public Action CommandListener_Build(int iClient, const char[] sCommand, int iArgs) {
if (!g_bEnabled) {
return Plugin_Continue;
}
char sBuildingType[4];
GetCmdArg(1, sBuildingType, sizeof(sBuildingType));
TDBuildingType iBuildingType = view_as<TDBuildingType>(StringToInt(sBuildingType));
switch (iBuildingType) {
case TDBuilding_Sentry: {
// PrintToChat(iClient, "\x01Use \x04!s \x01or \x04!sentry \x01to build a Sentry!");
Command_BuildSentry(iClient, iArgs);
return Plugin_Handled;
}
case TDBuilding_Dispenser: {
if (!CanClientBuild(iClient, TDBuilding_Dispenser)) {
return Plugin_Handled;
}
}
}
return Plugin_Continue;
}
public Action CommandListener_ClosedMotd(int iClient, const char[] sCommand, int iArgs) {
if (!g_bEnabled) {
return Plugin_Continue;
}
if (GetClientMetal(iClient) <= 0) {
AddClientMetal(iClient, 1); // for resetting HUD
ResetClientMetal(iClient);
}
return Plugin_Continue;
}
public Action CommandListener_Exec(int iClient, const char[] sCommand, int iArgs) {
if (g_bConfigsExecuted && iClient == 0) {
Database_UpdateServer();
}
return Plugin_Continue;
}

View File

@@ -0,0 +1,544 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Database Connection Callback
*/
public void ConnectToDB(Database db, const char[] error, any data) {
if (!db || error[0]) {
LogError("Database failure: %s", error);
SetFailState("OnDatabaseConnectionResult: %s", error);
} else {
g_hDatabase = db;
}
}
/*======================================
= Data Functions =
======================================*/
/**
* Starts to load all data from the database, calls Database_OnDataLoaded() when finished.
*
* @noreturn
*/
stock void Database_LoadData() {
Database_LoadTowers();
}
/**
* Loads towers to its map.
*
* @noreturn
*/
stock void Database_LoadTowers() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `tower`.`tower_id`, `level`, `tower`.`name`, `class`, `price`, `teleport_tower`, `damagetype`, `description`, `metal`, `weapon_id`, `attack`, `rotate`, `pitch`, `damage`, `attackspeed`, `area` " ...
"FROM `tower` " ...
"INNER JOIN `map` " ...
"ON (`map`.`map_id` = %d) " ...
"INNER JOIN `towerlevel` " ...
"ON (`tower`.`tower_id` = `towerlevel`.`tower_id`) " ...
"ORDER BY `tower`.`name` ASC, `level` ASC",
g_iServerMap);
g_hDatabase.Query(Database_OnLoadTowers, sQuery);
}
public void Database_OnLoadTowers(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadTowers > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
int iTowerId = 0, iTowerLevel = 0;
char sKey[64], sBuffer[128];
// Level Name Class Price Location Damagetype Description Metal WeaponId AttackPrimary AttackSecondary Rotate Pitch Damage Attackspeed Area
// 1 EngineerTower Engineer 500 666 -626 -2 0 0 0 Melee ... 1000 1 1 0 0 45 1.0 1.0 1.0
while (SQL_FetchRow(hResult)) {
iTowerId = SQL_FetchInt(hResult, 0) - 1;
iTowerLevel = SQL_FetchInt(hResult, 1);
// Save data only once
if (iTowerLevel == 1) {
// Save tower name
Format(sKey, sizeof(sKey), "%d_name", iTowerId);
SQL_FetchString(hResult, 2, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapTowers, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save tower class
Format(sKey, sizeof(sKey), "%d_class", iTowerId);
SQL_FetchString(hResult, 3, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapTowers, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save tower price
Format(sKey, sizeof(sKey), "%d_price", iTowerId);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchInt(hResult, 4));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 4));
// Save tower location
Format(sKey, sizeof(sKey), "%d_location", iTowerId);
SQL_FetchString(hResult, 5, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapTowers, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save tower damagetype
Format(sKey, sizeof(sKey), "%d_damagetype", iTowerId);
SQL_FetchString(hResult, 6, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapTowers, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save tower description
Format(sKey, sizeof(sKey), "%d_description", iTowerId);
SQL_FetchString(hResult, 7, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapTowers, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
}
// PrintToServer("Level %d:", iTowerLevel);
// Save tower level metal
Format(sKey, sizeof(sKey), "%d_%d_metal", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchInt(hResult, 8));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 8));
// Save tower level weapon index
Format(sKey, sizeof(sKey), "%d_%d_weapon", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchInt(hResult, 9));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 9));
// Save tower level attack mode
Format(sKey, sizeof(sKey), "%d_%d_attack", iTowerId, iTowerLevel);
SQL_FetchString(hResult, 10, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapTowers, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save tower level rotate
Format(sKey, sizeof(sKey), "%d_%d_rotate", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchInt(hResult, 11));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 11));
// Save tower level pitch
Format(sKey, sizeof(sKey), "%d_%d_pitch", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchInt(hResult, 12));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 12));
// Save tower level damage
Format(sKey, sizeof(sKey), "%d_%d_damage", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchFloat(hResult, 13));
// PrintToServer("%s => %f", sKey, SQL_FetchFloat(hResult, 13));
// Save tower level attackspeed
Format(sKey, sizeof(sKey), "%d_%d_attackspeed", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchFloat(hResult, 14));
// PrintToServer("%s => %f", sKey, SQL_FetchFloat(hResult, 14));
// Save tower level area
Format(sKey, sizeof(sKey), "%d_%d_area", iTowerId, iTowerLevel);
SetTrieValue(g_hMapTowers, sKey, SQL_FetchFloat(hResult, 15));
// PrintToServer("%s => %f", sKey, SQL_FetchFloat(hResult, 15));
}
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
Database_LoadWeapons();
}
/**
* Loads weapons to its map.
*
* @noreturn
*/
stock void Database_LoadWeapons() {
char sQuery[256];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `name`, `index`, (`slot` - 1), `level`, (`quality` - 1), `classname`, `attributes`, (`preserve_attributes` - 1) " ...
"FROM `weapon` " ...
"ORDER BY `weapon_id` ASC");
g_hDatabase.Query(Database_OnLoadWeapons, sQuery);
}
public void Database_OnLoadWeapons(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadWeapons > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
int iWeaponId = 1;
char sKey[64], sBuffer[128];
// Name Index Slot Level Quality Classname Attributes Preserve
// Wrench 7 2 1 0 tf_weapon_wrench 1
while (SQL_FetchRow(hResult)) {
// Save weapon name
Format(sKey, sizeof(sKey), "%d_name", iWeaponId);
SQL_FetchString(hResult, 0, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapWeapons, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save weapon index
Format(sKey, sizeof(sKey), "%d_index", iWeaponId);
SetTrieValue(g_hMapWeapons, sKey, SQL_FetchInt(hResult, 1));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 1));
// Save weapon slot
Format(sKey, sizeof(sKey), "%d_slot", iWeaponId);
SetTrieValue(g_hMapWeapons, sKey, SQL_FetchInt(hResult, 2));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 2));
// Save weapon level
Format(sKey, sizeof(sKey), "%d_level", iWeaponId);
SetTrieValue(g_hMapWeapons, sKey, SQL_FetchInt(hResult, 3));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 3));
// Save weapon quality
Format(sKey, sizeof(sKey), "%d_quality", iWeaponId);
SetTrieValue(g_hMapWeapons, sKey, SQL_FetchInt(hResult, 4));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 4));
// Save weapon classname
Format(sKey, sizeof(sKey), "%d_classname", iWeaponId);
SQL_FetchString(hResult, 5, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapWeapons, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save weapon attributes
Format(sKey, sizeof(sKey), "%d_attributes", iWeaponId);
SQL_FetchString(hResult, 6, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapWeapons, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save weapon preserve attributes
Format(sKey, sizeof(sKey), "%d_preserve_attributes", iWeaponId);
SetTrieValue(g_hMapWeapons, sKey, SQL_FetchInt(hResult, 7));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 7));
iWeaponId++;
}
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
Database_LoadWaves();
}
/**
* Loads waves to its map.
*
* @noreturn
*/
stock void Database_LoadWaves() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `wavetype`, `wave`.`name`, `class`, `quantity`, `health`, IF(`wavetype` & (SELECT `bit_value` FROM `wavetype` WHERE `wavetype`.`type` = 'air'), `teleport_air`, `teleport_ground`) " ...
"FROM `wave` " ...
"INNER JOIN `map` " ...
"ON (`map`.`map_id` = %d) " ...
"WHERE `wave_id` >= `wave_start` AND `wave_id` <= `wave_end` " ...
"ORDER BY `wave_id` ASC",
g_iServerMap);
g_hDatabase.Query(Database_OnLoadWaves, sQuery);
}
public void Database_OnLoadWaves(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadWaves > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
int iWaveId = 0;
char sKey[64], sBuffer[128];
iMaxWaves = 0;
// Type Name Class Quantiy Health Location
// 0 WeakScout Scout 4 125 560 -1795 -78 0 90 0
while (SQL_FetchRow(hResult)) {
// Save wave type
Format(sKey, sizeof(sKey), "%d_type", iWaveId);
SetTrieValue(g_hMapWaves, sKey, SQL_FetchInt(hResult, 0));
if (iWaveId == 0) {
g_iNextWaveType = SQL_FetchInt(hResult, 0);
}
// Save wave name
Format(sKey, sizeof(sKey), "%d_name", iWaveId);
SQL_FetchString(hResult, 1, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapWaves, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save wave class
Format(sKey, sizeof(sKey), "%d_class", iWaveId);
SQL_FetchString(hResult, 2, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapWaves, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save wave quantity
Format(sKey, sizeof(sKey), "%d_quantity", iWaveId);
SetTrieValue(g_hMapWaves, sKey, SQL_FetchInt(hResult, 3));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 3));
// Save wave health
Format(sKey, sizeof(sKey), "%d_health", iWaveId);
SetTrieValue(g_hMapWaves, sKey, SQL_FetchInt(hResult, 4));
// PrintToServer("%s => %d", sKey, SQL_FetchInt(hResult, 4));
// Save wave location
Format(sKey, sizeof(sKey), "%d_location", iWaveId);
SQL_FetchString(hResult, 5, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapWaves, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
iWaveId++;
iMaxWaves++;
}
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
Database_LoadAirWaveSpawn();
}
/**
* Loads the spawn location of air waves (for anti-air towers).
*
* @noreturn
*/
stock void Database_LoadAirWaveSpawn() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `teleport_air` " ...
"FROM `map` " ...
"WHERE (`map_id` = %d)",
g_iServerMap);
g_hDatabase.Query(Database_OnLoadAirWaveSpawn, sQuery);
}
public void Database_OnLoadAirWaveSpawn(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadAirWaveSpawn > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
SQL_FetchRow(hResult);
SQL_FetchString(hResult, 0, g_sAirWaveSpawn, sizeof(g_sAirWaveSpawn));
char sLocationParts[6][16];
ExplodeString(g_sAirWaveSpawn, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
g_fAirWaveSpawn[0] = StringToFloat(sLocationParts[0]);
g_fAirWaveSpawn[1] = StringToFloat(sLocationParts[1]);
g_fAirWaveSpawn[2] = StringToFloat(sLocationParts[2]);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
Database_LoadMetalpacks();
}
/**
* Loads metalpacks to its map.
*
* @noreturn
*/
stock void Database_LoadMetalpacks() {
char sQuery[256];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `type`, `metal`, `location` " ...
"FROM `metalpack` " ...
"INNER JOIN `metalpacktype` " ...
"ON (`metalpack`.`metalpacktype_id` = `metalpacktype`.`metalpacktype_id`) " ...
"WHERE `map_id` = %d " ...
"ORDER BY `metalpack_id` ASC",
g_iServerMap);
g_hDatabase.Query(Database_OnLoadMetalpacks, sQuery);
}
public void Database_OnLoadMetalpacks(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadMetalpacks > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
int iMetalpackId = 0;
char sKey[64], sBuffer[128];
// Type Metal Location
// start 400 1100 -1200 -90
while (SQL_FetchRow(hResult)) {
// Save metalpack type
Format(sKey, sizeof(sKey), "%d_type", iMetalpackId);
SQL_FetchString(hResult, 0, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapMetalpacks, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
// Save metalpack metal
Format(sKey, sizeof(sKey), "%d_metal", iMetalpackId);
SetTrieValue(g_hMapMetalpacks, sKey, SQL_FetchInt(hResult, 1));
// PrintToServer("%s => %s", sKey, sBuffer);
// Save metalpack location
Format(sKey, sizeof(sKey), "%d_location", iMetalpackId);
SQL_FetchString(hResult, 2, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMapMetalpacks, sKey, sBuffer);
// PrintToServer("%s => %s", sKey, sBuffer);
iMetalpackId++;
}
// Save metalpack quantity
SetTrieValue(g_hMapMetalpacks, "quantity", iMetalpackId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
Database_LoadMultipliersTypes();
}
stock void Database_LoadMultipliersTypes() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery), "SELECT type FROM multipliertype ORDER BY `multipliertype_id` ASC", g_iServerMap);
g_hDatabase.Query(Database_OnLoadMultipliersTypes, sQuery);
}
/**
* Load Multiplier Types
*
* @noreturn
*/
public void Database_OnLoadMultipliersTypes(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadMaxWaves > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
// type
// bullet
int iMultiplierTypeId = 1;
while (SQL_FetchRow(hResult)) {
char sKey[64], sBuffer[128];
// Save type
Format(sKey, sizeof(sKey), "%d_type", iMultiplierTypeId);
SQL_FetchString(hResult, 0, sBuffer, sizeof(sBuffer));
SetTrieString(g_hMultiplierType, sKey, sBuffer);
iMultiplierTypeId++;
}
}
Database_LoadMultipliers();
}
stock void Database_LoadMultipliers() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery), "SELECT price,increase FROM `multiplier` WHERE map_id=%d ORDER BY `multipliertype_id` ASC", g_iServerMap);
g_hDatabase.Query(Database_OnLoadMultipliers, sQuery);
}
/**
* Load Multipliers
*
* @noreturn
*/
public void Database_OnLoadMultipliers(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_LoadMaxWaves > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
// multipliertype_id price increase
// 1 1000 1000
iMaxMultiplierTypes = 0;
int iMultiplierTypeId = 1;
while (SQL_FetchRow(hResult)) {
char sKey[64];
// Save price
Format(sKey, sizeof(sKey), "%d_price", iMultiplierTypeId);
SetTrieValue(g_hMultiplier, sKey, SQL_FetchInt(hResult, 0));
// Save increase
Format(sKey, sizeof(sKey), "%d_increase", iMultiplierTypeId);
SetTrieValue(g_hMultiplier, sKey, SQL_FetchInt(hResult, 1));
iMultiplierTypeId++;
iMaxMultiplierTypes++;
}
}
Database_OnDataLoaded();
}

View File

@@ -0,0 +1,550 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Checks if a player already exists.
*
* @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 Database_CheckPlayer(int iUserId, int iClient, const char[] sCommunityId) {
if (!IsDefender(iClient)) {
return;
}
char sQuery[192];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `player_id` " ...
"FROM `player` " ...
"WHERE `player`.`steamid64` = '%s' " ...
"LIMIT 1",
sCommunityId);
g_hDatabase.Query(Database_OnCheckPlayer, sQuery, iUserId);
}
public void Database_OnCheckPlayer(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckPlayer > Error: %s", sError);
} else if (SQL_GetRowCount(hResult) == 0) {
// No player found, add it
Database_AddPlayer(iUserId);
} else {
SQL_FetchRow(hResult);
Player_USetValue(iUserId, PLAYER_DATABASE_ID, SQL_FetchInt(hResult, 0));
Database_UpdatePlayer(iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Adds a player.
*
* @param iUserId The user id on server (unique on server).
* @noreturn
*/
stock void Database_AddPlayer(int iUserId) {
char sQuery[256];
char sSteamId[32];
char sPlayerNameSave[MAX_NAME_LENGTH * 2 + 1];
char sPlayerIp[16];
char sPlayerIpSave[33];
int iClient = GetClientOfUserId(iUserId);
SQL_EscapeString(g_hDatabase, GetClientNameShort(iClient), sPlayerNameSave, sizeof(sPlayerNameSave));
Player_UGetString(iUserId, PLAYER_COMMUNITY_ID, sSteamId, sizeof(sSteamId));
Player_UGetString(iUserId, PLAYER_IP_ADDRESS, sPlayerIp, sizeof(sPlayerIp));
SQL_EscapeString(g_hDatabase, sPlayerIp, sPlayerIpSave, sizeof(sPlayerIpSave));
g_hDatabase.Format(sQuery, sizeof(sQuery),
"INSERT INTO `player` (`name`,`steamid64`,`ip`,`first_server`) " ...
"VALUES ('%s', '%s', '%s', %d)",
sPlayerNameSave, sSteamId, sPlayerIpSave, g_iServerId);
g_hDatabase.Query(Database_OnAddPlayer_1, sQuery, iUserId);
}
public void Database_OnAddPlayer_1(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_AddPlayer > Error: %s", sError);
} else {
char sQuery[32];
g_hDatabase.Format(sQuery, sizeof(sQuery), "SELECT LAST_INSERT_ID()");
g_hDatabase.Query(Database_OnAddPlayer_2, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnAddPlayer_2(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_AddPlayer > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
char sSteamId[32];
Player_UGetString(iUserId, PLAYER_COMMUNITY_ID, sSteamId, sizeof(sSteamId));
Log(TDLogLevel_Info, "Added player %N to database (%s)", GetClientOfUserId(iUserId), sSteamId);
SQL_FetchRow(hResult);
Player_USetValue(iUserId, PLAYER_DATABASE_ID, SQL_FetchInt(hResult, 0));
Database_UpdatePlayer(iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Updates a servers info.
*
* @param iUserId The user id on server (unique on server).
* @noreturn
*/
stock void Database_UpdatePlayer(int iUserId) {
char sQuery[512];
char sPlayerNameSave[MAX_NAME_LENGTH * 2 + 1];
int iClient = GetClientOfUserId(iUserId);
SQL_EscapeString(g_hDatabase, GetClientNameShort(iClient), sPlayerNameSave, sizeof(sPlayerNameSave));
char sPlayerIp[16];
char sPlayerIpSave[33];
Player_UGetString(iUserId, PLAYER_IP_ADDRESS, sPlayerIp, sizeof(sPlayerIp));
SQL_EscapeString(g_hDatabase, sPlayerIp, sPlayerIpSave, sizeof(sPlayerIpSave));
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `player` " ...
"SET `name` = '%s', " ...
"`ip` = '%s', " ...
"`last_server` = %d, " ...
"`current_server` = %d " ...
"WHERE `player_id` = %d " ...
"LIMIT 1",
sPlayerNameSave, sPlayerIpSave, g_iServerId, g_iServerId, iPlayerId);
g_hDatabase.Query(Database_OnUpdatePlayer_1, sQuery, iUserId);
}
public void Database_OnUpdatePlayer_1(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayer > Error: %s", sError);
} else {
char sQuery[512];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"INSERT IGNORE INTO `player_stats` (`player_id`, `map_id`, `first_connect`, `last_connect`, `last_disconnect`) " ...
"VALUES (%d, " ...
"(SELECT `map_id` " ...
"FROM `server` " ...
"WHERE `server_id` = %d " ...
"LIMIT 1), " ...
"UTC_TIMESTAMP(), UTC_TIMESTAMP(), UTC_TIMESTAMP())",
iPlayerId, g_iServerId);
g_hDatabase.Query(Database_OnUpdatePlayer_2, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnUpdatePlayer_2(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayer > Error: %s", sError);
} else {
char sQuery[128];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `steamid64` " ...
"FROM `player` " ...
"WHERE `player_id` = %d " ...
"LIMIT 1",
iPlayerId);
g_hDatabase.Query(Database_OnUpdatePlayer_3, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnUpdatePlayer_3(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayer > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
char sQuery[128];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server` " ...
"SET `players` = `players` + 1 " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
g_iServerId);
g_hDatabase.Query(Database_OnUpdatePlayer_4, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnUpdatePlayer_4(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayer > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
SQL_FetchRow(hResult);
char sSteamId[32];
SQL_FetchString(hResult, 0, sSteamId, sizeof(sSteamId));
Log(TDLogLevel_Info, "Updated player %N in database (%s)", GetClientOfUserId(iUserId), sSteamId);
Database_CheckPlayerBanned(iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Checks if a player's banned.
*
* @param iUserId The user id on server (unique on server).
* @noreturn
*/
stock void Database_CheckPlayerBanned(int iUserId) {
char sQuery[128];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `player_ban` " ...
"SET `active` = 'not active' " ...
"WHERE `player_id` = %d AND `expire` <= UTC_TIMESTAMP()",
iPlayerId);
g_hDatabase.Query(Database_OnCheckPlayerBanned_1, sQuery, iUserId);
}
public void Database_OnCheckPlayerBanned_1(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckPlayerBanned > Error: %s", sError);
} else {
char sQuery[512];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `reason`, CONCAT(`expire`, ' ', 'UTC') " ...
"FROM `player_ban` " ...
"WHERE `player_id` = %d AND `active` = 'active' AND `expire` " ...
"IN (SELECT MAX(`expire`) " ...
"FROM `player_ban` " ...
"WHERE `player_id` = %d) " ...
"LIMIT 1",
iPlayerId, iPlayerId);
g_hDatabase.Query(Database_OnCheckPlayerBanned_2, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnCheckPlayerBanned_2(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckPlayerBanned > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
bool bDontProceed = false;
int iClient = GetClientOfUserId(iUserId);
SQL_FetchRow(hResult);
char sReason[256], sExpire[32];
SQL_FetchString(hResult, 0, sReason, sizeof(sReason));
SQL_FetchString(hResult, 1, sExpire, sizeof(sExpire));
if (strlen(sReason) > 0) {
KickClient(iClient, "You have been banned from TF2 Tower Defense until %s! Reason: %s", sExpire, sReason);
bDontProceed = true;
} else {
KickClient(iClient, "You have been banned from TF2 Tower Defense until %s!", sExpire);
bDontProceed = true;
}
if (!bDontProceed) {
Database_CheckPlayerImmunity(iUserId);
}
} else {
Database_CheckPlayerImmunity(iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Checks a players immunity level.
*
* @param iUserId The user id on server (unique on server).
* @noreturn
*/
stock void Database_CheckPlayerImmunity(int iUserId) {
char sQuery[512];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `immunity` " ...
"FROM `player_immunity` " ...
"WHERE `player_id` = %d " ...
"LIMIT 1",
iPlayerId);
g_hDatabase.Query(Database_OnCheckPlayerImmunity, sQuery, iUserId);
}
public void Database_OnCheckPlayerImmunity(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckPlayerImmunity > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
int iClient = GetClientOfUserId(iUserId);
SQL_FetchRow(hResult);
int iImmunity = SQL_FetchInt(hResult, 0);
if (iImmunity >= 99 && GetUserAdmin(iClient) == INVALID_ADMIN_ID) {
AdminId iAdmin = CreateAdmin("Admin");
SetAdminFlag(iAdmin, Admin_Root, true);
SetUserAdmin(iClient, iAdmin);
}
Player_USetValue(iUserId, PLAYER_IMMUNITY, iImmunity);
} else {
Player_USetValue(iUserId, PLAYER_IMMUNITY, 0);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Updates a disconnect client things.
*
* @param iUserId The user id on server (unique on server).
* @noreturn
*/
stock void Database_UpdatePlayerDisconnect(int iUserId) {
char sQuery[128];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `player` " ...
"SET `current_server` = NULL " ...
"WHERE `player_id` = %d " ...
"LIMIT 1",
iPlayerId);
g_hDatabase.Query(Database_OnUpdatePlayerDisconnect_1, sQuery, iUserId);
}
public void Database_OnUpdatePlayerDisconnect_1(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayerDisconnect > Error: %s", sError);
} else {
char sQuery[128];
int iPlayerId;
Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `player_stats` " ...
"SET `last_disconnect` = UTC_TIMESTAMP() " ...
"WHERE `player_id` = %d " ...
"LIMIT 1",
iPlayerId);
g_hDatabase.Query(Database_OnUpdatePlayerDisconnect_2, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnUpdatePlayerDisconnect_2(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayerDisconnect > Error: %s", sError);
} else {
char sQuery[128];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server` " ...
"SET `players` = `players` - 1 " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
g_iServerId);
g_hDatabase.Query(Database_OnUpdatePlayerDisconnect_3, sQuery, iUserId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnUpdatePlayerDisconnect_3(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayerDisconnect > Error: %s", sError);
} else {
// Get Saved Player Info
int iKills, iAssists, iDeaths, iDamage, iObjects_Built, iTowers_Bought, iMetal_Pick, iMetal_Drop, iWaves_Played, iWaves_Reached, iRounds_Played, iRounds_Won, iPlayTime, iPlayerId;
if (!Player_UGetValue(iUserId, PLAYER_KILLS, iKills))
iKills = 0;
if (!Player_UGetValue(iUserId, PLAYER_ASSISTS, iAssists))
iAssists = 0;
if (!Player_UGetValue(iUserId, PLAYER_DEATHS, iDeaths))
iDeaths = 0;
if (!Player_UGetValue(iUserId, PLAYER_DAMAGE, iDamage))
iDamage = 0;
if (!Player_UGetValue(iUserId, PLAYER_OBJECTS_BUILT, iObjects_Built))
iObjects_Built = 0;
if (!Player_UGetValue(iUserId, PLAYER_TOWERS_BOUGHT, iTowers_Bought))
iTowers_Bought = 0;
if (!Player_UGetValue(iUserId, PLAYER_METAL_PICK, iMetal_Pick))
iMetal_Pick = 0;
if (!Player_UGetValue(iUserId, PLAYER_METAL_DROP, iMetal_Drop))
iMetal_Drop = 0;
if (!Player_UGetValue(iUserId, PLAYER_WAVES_PLAYED, iWaves_Played))
iWaves_Played = 0;
if (!Player_UGetValue(iUserId, PLAYER_WAVE_REACHED, iWaves_Reached))
iWaves_Reached = 0;
if (!Player_UGetValue(iUserId, PLAYER_ROUNDS_PLAYED, iRounds_Played))
iRounds_Played = 0;
if (!Player_UGetValue(iUserId, PLAYER_ROUNDS_WON, iRounds_Won))
iRounds_Won = 0;
if (!Player_UGetValue(iUserId, PLAYER_PLAYTIME, iPlayTime))
iPlayTime = 0;
if (!Player_UGetValue(iUserId, PLAYER_DATABASE_ID, iPlayerId))
iPlayerId = 0;
// Update Player info based on saved info
char sQuery[1024];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `player_stats` " ...
"SET `kills` = kills + %d, `assists` = assists + %d, `deaths` = deaths + %d, `damage` = damage + %d, " ...
"`objects_built` = objects_built + %d, `towers_bought` = towers_bought + %d, `metal_pick` = metal_pick + %d, " ...
"`metal_drop` = metal_drop + %d, `waves_played` = waves_played + %d, `wave_reached` = IF(wave_reached < %d, wave_reached = %d, wave_reached), " ...
"`rounds_played` = rounds_played + %d, `rounds_won` = rounds_won + %d, `playtime` = playtime + %d " ...
"WHERE `player_id` = %d AND map_id = %d",
iKills, iAssists, iDeaths, iDamage, iObjects_Built, iTowers_Bought, iMetal_Pick, iMetal_Drop, iWaves_Played, iWaves_Reached, iWaves_Reached, iRounds_Played, iRounds_Won, iPlayTime, iPlayerId, g_iServerMap);
g_hDatabase.Query(Database_OnUpdatePlayerDisconnect_4, sQuery, iUserId);
// Reset Values
Player_USetValue(iUserId, PLAYER_KILLS, 0);
Player_USetValue(iUserId, PLAYER_ASSISTS, 0);
Player_USetValue(iUserId, PLAYER_DEATHS, 0);
Player_USetValue(iUserId, PLAYER_DAMAGE, 0);
Player_USetValue(iUserId, PLAYER_OBJECTS_BUILT, 0);
Player_USetValue(iUserId, PLAYER_TOWERS_BOUGHT, 0);
Player_USetValue(iUserId, PLAYER_METAL_PICK, 0);
Player_USetValue(iUserId, PLAYER_METAL_DROP, 0);
Player_USetValue(iUserId, PLAYER_WAVES_PLAYED, 0);
Player_USetValue(iUserId, PLAYER_WAVES_PLAYED, 0);
Player_USetValue(iUserId, PLAYER_ROUNDS_WON, 0);
Player_USetValue(iUserId, PLAYER_PLAYTIME, 0);
Player_USetValue(iUserId, PLAYER_DATABASE_ID, 0);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
public void Database_OnUpdatePlayerDisconnect_4(Handle hDriver, Handle hResult, const char[] sError, any iUserId) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdatePlayerDisconnect > Error: %s", sError);
} else {
char sSteamId[32];
Player_UGetString(iUserId, PLAYER_COMMUNITY_ID, sSteamId, sizeof(sSteamId));
Log(TDLogLevel_Info, "Updated disconnected player in database (%s)", sSteamId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}

View File

@@ -0,0 +1,494 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Checks if a server does already exist.
*
* @noreturn
*/
stock void Database_CheckServer() {
char sQuery[128];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `server_id` " ...
"FROM `server` " ...
"WHERE `ip` = '%s' AND `port` = %d " ...
"LIMIT 1",
g_sServerIp, g_iServerPort);
g_hDatabase.Query(Database_OnCheckServer, sQuery);
}
public void Database_OnCheckServer(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckServer > Error: %s", sError);
} else if (SQL_GetRowCount(hResult) == 0) {
// No server found, add it
Database_AddServer();
} else {
SQL_FetchRow(hResult);
g_iServerId = SQL_FetchInt(hResult, 0);
Database_UpdateServer();
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Adds a server.
*
* @noreturn
*/
stock void Database_AddServer() {
char sQuery[256];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"INSERT INTO `server` (`ip`, `port`, `created`, `updated`) " ...
"VALUES ('%s', %d, UTC_TIMESTAMP(), UTC_TIMESTAMP())",
g_sServerIp, g_iServerPort);
g_hDatabase.Query(Database_OnAddServer, sQuery, 0);
}
public void Database_OnAddServer(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_AddServer > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
char sQuery[256];
if (iData == 0) {
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `server_id` " ...
"FROM `server` " ...
"WHERE `ip` = '%s' AND `port` = %d " ...
"LIMIT 1",
g_sServerIp, g_iServerPort);
g_hDatabase.Query(Database_OnAddServer, sQuery, 1);
} else if (iData == 1) {
SQL_FetchRow(hResult);
g_iServerId = SQL_FetchInt(hResult, 0);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"INSERT INTO `server_stats` (`server_id`) " ...
"VALUES (%d)",
g_iServerId);
g_hDatabase.Query(Database_OnAddServer, sQuery, 2);
} else if (iData == 2) {
Log(TDLogLevel_Info, "Added server to database (%s:%d)", g_sServerIp, g_iServerPort);
Database_UpdateServer();
}
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
stock void Database_UpdateServerPlayerCount() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server` " ...
"SET `players` = %d " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
GetRealClientCount(), g_iServerId);
g_hDatabase.Query(Database_OnUpdateServerPlayerCount, sQuery, 0);
}
public void Database_OnUpdateServerPlayerCount(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdateServer > Error: %s", sError);
}
}
/**
* Updates a servers info.
*
* @noreturn
*/
stock void Database_UpdateServer() {
char sQuery[512];
char sServerName[128], sServerNameSave[256];
GetConVarString(FindConVar("hostname"), sServerName, sizeof(sServerName));
SQL_EscapeString(g_hDatabase, sServerName, sServerNameSave, sizeof(sServerNameSave));
char sPassword[32], sPasswordSave[64];
GetConVarString(FindConVar("sv_password"), sPassword, sizeof(sPassword));
SQL_EscapeString(g_hDatabase, sPassword, sPasswordSave, sizeof(sPasswordSave));
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server` " ...
"SET `name` = '%s', " ...
"`version` = '%s', " ...
"`password` = '%s', " ...
"`players` = %d, " ...
"`updated` = UTC_TIMESTAMP() " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
sServerNameSave, PLUGIN_VERSION, sPasswordSave, GetRealClientCount(), g_iServerId);
g_hDatabase.Query(Database_OnUpdateServer, sQuery, 0);
}
public void Database_OnUpdateServer(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_UpdateServer > Error: %s", sError);
} else {
char sQuery[256];
char sCurrentMap[PLATFORM_MAX_PATH];
GetCurrentMap(sCurrentMap, sizeof(sCurrentMap));
if (iData == 0) {
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `map_id`, `respawn_wave_time` " ...
"FROM `map` " ...
"WHERE `name` = '%s' " ...
"LIMIT 1",
sCurrentMap);
g_hDatabase.Query(Database_OnUpdateServer, sQuery, 1);
} else if (iData == 1) {
if (SQL_GetRowCount(hResult) == 0) {
Log(TDLogLevel_Error, "Map \"%s\" is not supported, thus Tower Defense has been disabled.", sCurrentMap);
g_bEnabled = false;
UpdateGameDescription();
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
return;
}
SQL_FetchRow(hResult);
g_iServerMap = SQL_FetchInt(hResult, 0);
g_iRespawnWaveTime = SQL_FetchInt(hResult, 1);
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server` " ...
"SET `map_id` = %d " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
g_iServerMap, g_iServerId);
g_hDatabase.Query(Database_OnUpdateServer, sQuery, 2);
} else if (iData == 2) {
Log(TDLogLevel_Info, "Updated server in database (%s:%d)", g_sServerIp, g_iServerPort);
g_bConfigsExecuted = true;
Database_CheckServerSettings();
}
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Checks for the servers settings.
*
* @noreturn
*/
stock void Database_CheckServerSettings() {
char sQuery[256];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `lockable`, `loglevel`, `logtype` " ...
"FROM `server` " ...
"INNER JOIN `server_settings` " ...
"ON (`server`.`server_settings_id` = `server_settings`.`server_settings_id`) " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
g_iServerId);
g_hDatabase.Query(Database_OnCheckServerSettings, sQuery);
}
public void Database_OnCheckServerSettings(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckServerSettings > Error: %s", sError);
} else if (SQL_GetRowCount(hResult)) {
SQL_FetchRow(hResult);
char sLockable[32];
SQL_FetchString(hResult, 0, sLockable, sizeof(sLockable));
if (StrEqual(sLockable, "not lockable")) {
g_bLockable = false;
} else if (StrEqual(sLockable, "lockable")) {
g_bLockable = true;
}
char sLogLevel[32];
SQL_FetchString(hResult, 1, sLogLevel, sizeof(sLogLevel));
TDLogLevel iLogLevel;
if (StrEqual(sLogLevel, "None")) {
iLogLevel = TDLogLevel_None;
} else if (StrEqual(sLogLevel, "Error")) {
iLogLevel = TDLogLevel_Error;
} else if (StrEqual(sLogLevel, "Warning")) {
iLogLevel = TDLogLevel_Warning;
} else if (StrEqual(sLogLevel, "Info")) {
iLogLevel = TDLogLevel_Info;
} else if (StrEqual(sLogLevel, "Debug")) {
iLogLevel = TDLogLevel_Debug;
} else if (StrEqual(sLogLevel, "Trace")) {
iLogLevel = TDLogLevel_Trace;
}
char sLogType[32];
SQL_FetchString(hResult, 2, sLogType, sizeof(sLogType));
TDLogType iLogType;
if (StrEqual(sLogType, "File")) {
iLogType = TDLogType_File;
} else if (StrEqual(sLogType, "Console")) {
iLogType = TDLogType_Console;
} else if (StrEqual(sLogType, "File and console")) {
iLogType = TDLogType_FileAndConsole;
}
Log_Initialize(iLogLevel, iLogType);
Database_CheckServerConfig();
Database_CheckServerStats();
} else {
Database_CheckServerConfig();
Database_CheckServerStats();
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Checks for the servers config.
*
* @noreturn
*/
stock void Database_CheckServerConfig() {
char sQuery[512];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT CONCAT(`variable`, ' \"', `value`, '\"') " ...
"FROM `config` " ...
"INNER JOIN `server` " ...
"ON (`server_id` = %d) " ...
"INNER JOIN `server_settings` " ...
"ON (`server`.`server_settings_id` = `server_settings`.`server_settings_id`) " ...
"WHERE `config_id` >= `config_start` AND `config_id` <= `config_end` " ...
"ORDER BY `config_id` ASC",
g_iServerId);
g_hDatabase.Query(Database_OnCheckServerConfig, sQuery);
}
public void Database_OnCheckServerConfig(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckServerConfig > Error: %s", sError);
} else {
char sCommand[260];
while (SQL_FetchRow(hResult)) {
SQL_FetchString(hResult, 0, sCommand, sizeof(sCommand));
ServerCommand("%s", sCommand);
}
Database_OnServerChecked();
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Sets the servers password in the database.
*
* @param sPassword The password to set.
* @param bReloadMap Reload map afterwards.
* @noreturn
*/
stock void Database_SetServerPassword(const char[] sPassword, bool bReloadMap) {
char sQuery[128];
char sPasswordSave[64];
SQL_EscapeString(g_hDatabase, sPassword, sPasswordSave, sizeof(sPasswordSave));
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server` " ...
"SET `password` = '%s' " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
sPasswordSave, g_iServerId);
DataPack hPack = new DataPack();
hPack.WriteCell(bReloadMap ? 1 : 0);
hPack.WriteString(sPassword);
g_hDatabase.Query(Database_OnSetServerPassword, sQuery, hPack);
}
public void Database_OnSetServerPassword(Handle hDriver, Handle hResult, const char[] sError, DataPack hPack) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_SetServerPassword > Error: %s", sError);
} else {
ResetPack(hPack);
bool bReloadMap = (hPack.ReadCell() == 0 ? false : true);
char sPassword[32];
hPack.ReadString(sPassword, sizeof(sPassword));
Log(TDLogLevel_Debug, "Set server password to \"%s\"", sPassword);
if (bReloadMap) {
ReloadMap();
}
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
/**
* Checks if server stats does already exist.
*
* @noreturn
*/
stock void Database_CheckServerStats() {
char sQuery[128];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"SELECT `playtime` " ...
"FROM `server_stats` " ...
"WHERE `server_id` = %d " ...
"LIMIT 1",
g_iServerId);
g_hDatabase.Query(Database_OnCheckServerStats, sQuery);
}
public void Database_OnCheckServerStats(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_CheckServerStats > Error: %s", sError);
} else if (SQL_GetRowCount(hResult) == 0) {
// No server found, add it
Database_AddServerStats();
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
stock void Database_AddServerStats() {
char sQuery[256];
g_hDatabase.Format(sQuery, sizeof(sQuery),
"INSERT INTO `server_stats` (`server_id`) " ...
"VALUES (%d)",
g_iServerId);
g_hDatabase.Query(Database_OnAddServerStats, sQuery);
}
public void Database_OnAddServerStats(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_AddServerStats > Error: %s", sError);
} else {
Log(TDLogLevel_Info, "Added server stats (%i)", g_iServerId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}
stock void Database_ServerStatsUpdate() {
char sQuery[1024];
int iConnections, iRounds_Played, iRounds_Won, iPlaytime;
if (!Server_UGetValue(g_iServerId, SERVER_CONNECTIONS, iConnections))
iConnections = 0;
if (!Server_UGetValue(g_iServerId, SERVER_ROUNDS_PLAYED, iRounds_Played))
iRounds_Played = 0;
if (!Server_UGetValue(g_iServerId, SERVER_ROUNDS_WON, iRounds_Won))
iRounds_Won = 0;
if (!Server_UGetValue(g_iServerId, SERVER_PLAYTIME, iPlaytime))
iRounds_Won = 0;
g_hDatabase.Format(sQuery, sizeof(sQuery),
"UPDATE `server_stats` " ...
"SET `connections` = connections + %d, `rounds_played` = rounds_played + %d, `rounds_won` = rounds_won + %d, `playtime` = playtime + %d " ...
"WHERE `server_id` = %d",
iConnections, iRounds_Played, iRounds_Won, iPlaytime, g_iServerId);
Server_USetValue(g_iServerId, SERVER_CONNECTIONS, 0);
Server_USetValue(g_iServerId, SERVER_ROUNDS_PLAYED, 0);
Server_USetValue(g_iServerId, SERVER_ROUNDS_WON, 0);
Server_USetValue(g_iServerId, SERVER_PLAYTIME, 0);
g_hDatabase.Query(Database_OnServerStatsUpdate, sQuery);
}
public void Database_OnServerStatsUpdate(Handle hDriver, Handle hResult, const char[] sError, any iData) {
if (hResult == null) {
Log(TDLogLevel_Error, "Query failed at Database_ServerStatsUpdate > Error: %s", sError);
} else {
Log(TDLogLevel_Info, "Updated server stats in database (%i)", g_iServerId);
}
if (hResult != null) {
CloseHandle(hResult);
hResult = null;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,44 @@
#define GAME_DESCRIPTION "Tower Defense"
#define PLAYER_LIMIT 6
#define METALPACK_LIMIT 50
#define TEAM_DEFENDER 3
#define TEAM_ATTACKER 2
/*========================================
= Server Data Keys =
========================================*/
#define SERVER_CONNECTIONS "SERVER_CONNECTIONS"
#define SERVER_ROUNDS_PLAYED "SERVER_ROUNDS_PLAYED"
#define SERVER_ROUNDS_WON "SERVER_ROUNDS_WON"
#define SERVER_PLAYTIME "SERVER_PLAYTIME"
/*----- End of Server Data Keys ------*/
/*========================================
= Player Data Keys =
========================================*/
#define PLAYER_COMMUNITY_ID "PLAYER_COMMUNITY_ID"
#define PLAYER_DATABASE_ID "PLAYER_DATABASE_ID"
#define PLAYER_IMMUNITY "PLAYER_IMMUNITY"
#define PLAYER_IP_ADDRESS "PLAYER_IP_ADDRESS"
#define PLAYER_STEAM_ID "PLAYER_STEAM_ID"
#define PLAYER_KILLS "PLAYER_KILLS"
#define PLAYER_ASSISTS "PLAYER_ASSISTS"
#define PLAYER_DEATHS "PLAYER_DEATHS"
#define PLAYER_DAMAGE "PLAYER_DAMAGE"
#define PLAYER_OBJECTS_BUILT "PLAYER_OBJECTSBUILT"
#define PLAYER_TOWERS_BOUGHT "PLAYER_TOWERS_BOUGHT"
#define PLAYER_METAL_PICK "PLAYER_METAL_PICK"
#define PLAYER_METAL_DROP "PLAYER_METAL_DROP"
#define PLAYER_WAVES_PLAYED "PLAYER_WAVES_PLAYED"
#define PLAYER_WAVE_REACHED "PLAYER_WAVE_REACHED"
#define PLAYER_ROUNDS_PLAYED "PLAYER_ROUNDS_PLAYED"
#define PLAYER_ROUNDS_WON "PLAYER_ROUNDS_WON"
#define PLAYER_PLAYTIME "PLAYER_PLAYTIME"
/*----- End of Player Data Keys ------*/

View File

@@ -0,0 +1,72 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
stock void CreateConVars() {
CreateConVar("td_version", PLUGIN_VERSION, "Tower Defense Version", FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_DONTRECORD);
g_hEnabled = CreateConVar("td_enabled", "1", "Enables/disables Tower Defense", FCVAR_DONTRECORD, true, 0.0, true, 1.0);
g_hMaxBotsOnField = CreateConVar("td_max_bots_on_field", "8", "Max bots simultaneously on field. Might be actually lower than set due to maxplayer limit");
}
stock void LoadConVars() {
g_hEnabled.AddChangeHook(OnConVarChanged);
g_hTfBotQuota = FindConVar("tf_bot_quota");
g_hTfBotQuota.AddChangeHook(OnConVarChanged);
}
stock void SetConVars() {
g_hTfBotQuota.IntValue = 0;
}
stock void SetPregameConVars() {
FindConVar("sv_cheats").SetInt(1, true, false);
}
/**
* Called when a console variable's value is changed.
*
* @param hConVar Handle to the convar that was changed.
* @param sOldValue String containing the value of the convar before it was changed.
* @param sNewValue String containing the new value of the convar.
* @noreturn
*/
public void OnConVarChanged(ConVar hConVar, const char[] sOldValue, const char[] sNewValue) {
if (hConVar == g_hEnabled) {
if (!g_bMapRunning) {
return;
}
if (g_hEnabled.BoolValue) {
if (!g_bEnabled) {
bool bEnabled = IsTowerDefenseMap();
if (bEnabled) {
// Basically do the same as in OnConfigsExecuted().
g_bEnabled = true;
UpdateGameDescription();
}
}
} else {
if (g_bEnabled) {
// Basically do the same as in OnMapEnd().
g_bEnabled = false;
UpdateGameDescription();
}
}
} else if (hConVar == g_hTfBotQuota) {
if (StringToInt(sNewValue) > 0) {
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "ConVar 'tf_bot_quota' can't be above 0 - Current Value: %d - New Value: %d", StringToInt(sOldValue), StringToInt(sNewValue));
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Setting ConVar 'tf_bot_quota' to default");
ResetConVar(g_hTfBotQuota, true, false);
}
}
}

View File

@@ -0,0 +1,90 @@
enum TDDataType
{
TDDataType_Integer = 0,
TDDataType_Boolean,
TDDataType_Float,
TDDataType_String
};
enum TDBloodColor
{
TDBlood_None = -1,
TDBlood_Color_Red = 0,
TDBlood_Color_Yellow,
TDBlood_Color_Green,
TDBlood_Color_Mech
};
enum TDMetalPackSpawnType
{
TDMetalPack_Small = 0,
TDMetalPack_Medium,
TDMetalPack_Large
};
enum TDMetalPackReturn
{
TDMetalPack_InvalidMetal = 0,
TDMetalPack_LimitReached,
TDMetalPack_InvalidType,
TDMetalPack_SpawnedPack
};
enum TDBuildingType
{
TDBuilding_Dispenser = 0,
TDBuilding_TeleporterEntry,
TDBuilding_Sentry,
TDBuilding_TeleporterExit
};
enum TDTowerId
{
TDTower_Invalid = -1,
TDTower_Engineer = 0,
TDTower_Sniper = 1,
TDTower_Medic = 2,
TDTower_Grenade = 3,
TDTower_Pyro = 4,
TDTower_Jarate = 5,
TDTower_AnitAir_Rocket = 6,
TDTower_AntiAir_Flare = 7,
TDTower_Crossbow = 8,
TDTower_Flare = 9,
TDTower_Heavy = 10,
TDTower_Shotgun = 11,
TDTower_Knockback = 12,
TDTower_Rocket = 13,
TDTower_Rapidflare = 14,
TDTower_Backburner_Pyro = 15,
TDTower_Lochnload_Demoman = 16,
TDTower_Machina_Sniper = 17,
TDTower_Liberty_Soldier = 18,
TDTower_Juggle_Soldier = 19,
TDTower_Bushwacka_Sniper = 20,
TDTower_Natascha_Heavy = 21,
TDTower_Guillotine_Scout = 22,
TDTower_Homewrecker_Pyro = 23,
TDTower_Airblast_Pyro = 24,
TDTower_AoE_Engineer = 25,
TDTower_Kritz_Medic = 26,
TDTower_Slow_Spy = 27,
TDTower_Quantity
};
enum
{
TDWaveType_Boss = (1 << 0), // 1
TDWaveType_Rapid = (1 << 1), // 2
TDWaveType_Regen = (1 << 2), // 4
TDWaveType_KnockbackImmune = (1 << 3), // 8
TDWaveType_Air = (1 << 4), // 16
TDWaveType_JarateImmune = (1 << 5) // 32
};
enum TDMetalPackType
{
TDMetalPack_Invalid = -1,
TDMetalPack_Start = 0,
TDMetalPack_Boss
};

View File

@@ -0,0 +1,168 @@
/*======================================
= Data Variables =
======================================*/
Handle g_hMapTowers;
Handle g_hMapWeapons;
Handle g_hMapWaves;
Handle g_hMapMetalpacks;
Handle g_hMultiplierType;
Handle g_hMultiplier;
Handle g_hServerData;
int g_iTime;
/*=========================================
= Generic Variables =
=========================================*/
/*========== Boolean ==========*/
bool g_bConfigsExecuted;
bool g_bEnabled;
bool g_bLockable;
bool g_bMapRunning;
bool g_bServerInitialized;
bool g_bSteamWorks;
bool g_bTF2Attributes;
bool g_bTowerDefenseMap;
bool g_bCanGetUnlocks;
/*========== ConVar ==========*/
ConVar g_hEnabled;
ConVar g_hTfBotQuota;
ConVar g_hMaxBotsOnField;
/*========== Handle ==========*/
Handle hHintTimer;
/*========== Float ==========*/
float fMultiplier[50];
/*========== Integer ==========*/
int g_iBuildingLimit[TDBuildingType];
int g_iHaloMaterial;
int g_iLaserMaterial;
int g_iMetalPackCount;
int iMaxWaves;
int iMaxMultiplierTypes;
int g_iBotsToSpawn;
int g_iTotalBotsLeft;
int g_iMaxClients;
/*========== String ==========*/
char g_sPassword[8];
/*==========================================
= Database Variables =
==========================================*/
/*========== Boolean ==========*/
/*========== Handle ==========*/
Database g_hDatabase;
/*========== Float ==========*/
/*========== Integer ==========*/
int g_iServerId;
int g_iServerMap;
int g_iServerPort;
/*========== String ==========*/
char g_sServerIp[16];
/*========================================
= Client Variables =
========================================*/
/*========== Boolean ==========*/
bool g_bCarryingObject[MAXPLAYERS + 1];
bool g_bInsideNobuild[MAXPLAYERS + 1];
bool g_bPickupSentry[MAXPLAYERS + 1];
bool g_bReplaceWeapon[MAXPLAYERS + 1][3];
/*========== Handle ==========*/
Handle g_hPlayerData;
/*========== Float ==========*/
/*========== Integer ==========*/
int g_iAttachedTower[MAXPLAYERS + 1];
int g_iLastButtons[MAXPLAYERS + 1];
int g_iHealBeamIndex[MAXPLAYERS + 1][2];
/*========== String ==========*/
/*=======================================
= Tower Variables =
=======================================*/
/*========== Boolean ==========*/
int g_bTowerBought[TDTowerId];
bool g_bTowersLocked;
bool g_bAoEEngineerAttack;
bool g_bKritzMedicCharged;
/*========== Handle ==========*/
Handle hAoETimer;
/*========== Float ==========*/
/*========== Integer ==========*/
int g_iLastMover[MAXPLAYERS + 1];
int g_iUpgradeLevel[MAXPLAYERS + 1];
int g_iUpgradeMetal[MAXPLAYERS + 1];
int iAoEEngineerTimer;
int iAoEKritzMedicTimer;
/*========== String ==========*/
/*======================================
= Wave Variables =
======================================*/
/*========== Boolean ==========*/
bool g_bBoostWave;
bool g_bStartWaveEarly;
bool g_iSlowAttacker[MAXPLAYERS + 1];
/*========== Handle ==========*/
/*========== Float ==========*/
float g_fAirWaveSpawn[3];
float g_fWaveStartButtonLocation[3];
float g_fBeamPoints[MAXPLAYERS + 1][8][3];
/*========== Integer ==========*/
int g_iCurrentWave;
int g_iHealthBar;
int g_iNextWaveType;
int g_iRespawnWaveTime;
int g_iWaveStartButton;
/*========== String ==========*/
char g_sAirWaveSpawn[64];

View File

@@ -0,0 +1,49 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "info/constants.sp"
#include "info/enums.sp"
#include "info/variables.sp"
#endif
public Action Timer_Hints(Handle hTimer) {
int iRandom = GetRandomInt(1, 6);
if(iRandom == 1)
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "hintBuildInfo");
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can build sentries via your PDA or with the command \x04/s");
else if(iRandom == 2)
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "hintDropMetal");
//PrintToChatAll("\x04[\x03TD\x04]\x04 /d <amount> \x03to drop metal for other players.");
else if(iRandom == 3)
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "hintMetalStatus");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Check everyone's metal status with \x04/m ");
else if(iRandom == 4)
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "hintMetalTransfer");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Transfer metal between your comrades \x04/t <target> <amount> ");
else if(iRandom == 5)
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "hintCheckWave");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Check on what wave you're on with \x04/w ");
else if(iRandom == 6)
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "hintDeath");
//PrintToChatAll("\x04[\x03TD\x04]\x03 Dying will make you lose half your metal! ");
return Plugin_Continue;
}
public Action Timer_Reset(Handle hTimer) {
SetPassword("", true, true);
return Plugin_Continue;
}
public Action RespawnPlayer(Handle hTimer, any iClient) {
if (IsValidClient(iClient)) {
TF2_RespawnPlayer(iClient);
}
return Plugin_Continue;
}
public Action Timer_EnableUnlockButton(Handle hTimer) {
g_bCanGetUnlocks = true;
return Plugin_Continue;
}

View File

@@ -0,0 +1,178 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
enum TDLogLevel
{
TDLogLevel_None = 0, // disables all logging
TDLogLevel_Error = 1, // is for critical errors, the plugin may no longer work correctly
TDLogLevel_Warning = 2, // is for important warnings, the plugin will continue to work correctly
TDLogLevel_Info = 3, // is for informative messages, typically used for deployment
TDLogLevel_Debug = 4, // is for debug messages, this level is useful during development
TDLogLevel_Trace = 5 // is for trace messages, this level is usually only needed when debugging a problem
};
enum TDLogType
{
TDLogType_File = 0, // logs to SourceMod logs
TDLogType_Console = 1, // logs to server console
TDLogType_FileAndConsole = 2 // logs to SourceMod logs and server console
};
static TDLogLevel m_iLogLevel = TDLogLevel_Info;
static TDLogType m_iLogType = TDLogType_FileAndConsole;
stock void Log_Initialize(TDLogLevel iLogLevel = TDLogLevel_Info, TDLogType iLogType = TDLogType_FileAndConsole) {
m_iLogLevel = iLogLevel;
m_iLogType = iLogType;
}
stock void Log(TDLogLevel iLogLevel, const char[] sMessage, any...) {
if (m_iLogLevel >= iLogLevel) {
char sFormattedMessage[256];
VFormat(sFormattedMessage, sizeof(sFormattedMessage), sMessage, 3);
switch (m_iLogType) {
case TDLogType_File: {
switch (iLogLevel) {
case TDLogLevel_Error: {
LogError("[TF2TD > Error] %s", sFormattedMessage);
}
case TDLogLevel_Warning: {
LogError("[TF2TD > Warning] %s", sFormattedMessage);
}
case TDLogLevel_Info: {
LogMessage("[TF2TD > Info] %s", sFormattedMessage);
}
case TDLogLevel_Debug: {
LogMessage("[TF2TD > Debug] %s", sFormattedMessage);
}
case TDLogLevel_Trace: {
LogMessage("[TF2TD > Trace] %s", sFormattedMessage);
}
}
}
case TDLogType_Console: {
switch (iLogLevel) {
case TDLogLevel_Error: {
PrintToServer("[TF2TD > Error] %s", sFormattedMessage);
}
case TDLogLevel_Warning: {
PrintToServer("[TF2TD > Warning] %s", sFormattedMessage);
}
case TDLogLevel_Info: {
PrintToServer("[TF2TD > Info] %s", sFormattedMessage);
}
case TDLogLevel_Debug: {
PrintToServer("[TF2TD > Debug] %s", sFormattedMessage);
}
case TDLogLevel_Trace: {
PrintToServer("[TF2TD > Trace] %s", sFormattedMessage);
}
}
}
case TDLogType_FileAndConsole: {
switch (iLogLevel) {
case TDLogLevel_Error: {
LogError("[TF2TD > Error] %s", sFormattedMessage);
PrintToServer("[TF2TD > Error] %s", sFormattedMessage);
}
case TDLogLevel_Warning: {
LogError("[TF2TD > Warning] %s", sFormattedMessage);
PrintToServer("[TF2TD > Warning] %s", sFormattedMessage);
}
case TDLogLevel_Info: {
LogMessage("[TF2TD > Info] %s", sFormattedMessage);
PrintToServer("[TF2TD > Info] %s", sFormattedMessage);
}
case TDLogLevel_Debug: {
LogMessage("[TF2TD > Debug] %s", sFormattedMessage);
PrintToServer("[TF2TD > Debug] %s", sFormattedMessage);
}
case TDLogLevel_Trace: {
LogMessage("[TF2TD > Trace] %s", sFormattedMessage);
PrintToServer("[TF2TD > Trace] %s", sFormattedMessage);
}
}
}
}
}
}
stock void LogType(TDLogLevel iLogLevel, TDLogType iLogType, const char[] sMessage, any...) {
if (m_iLogLevel >= iLogLevel) {
char sFormattedMessage[256];
VFormat(sFormattedMessage, sizeof(sFormattedMessage), sMessage, 4);
switch (iLogType) {
case TDLogType_File: {
switch (iLogLevel) {
case TDLogLevel_Error: {
LogError("[TF2TD > Error] %s", sFormattedMessage);
}
case TDLogLevel_Warning: {
LogError("[TF2TD > Warning] %s", sFormattedMessage);
}
case TDLogLevel_Info: {
LogMessage("[TF2TD > Info] %s", sFormattedMessage);
}
case TDLogLevel_Debug: {
LogMessage("[TF2TD > Debug] %s", sFormattedMessage);
}
case TDLogLevel_Trace: {
LogMessage("[TF2TD > Trace] %s", sFormattedMessage);
}
}
}
case TDLogType_Console: {
switch (iLogLevel) {
case TDLogLevel_Error: {
PrintToServer("[TF2TD > Error] %s", sFormattedMessage);
}
case TDLogLevel_Warning: {
PrintToServer("[TF2TD > Warning] %s", sFormattedMessage);
}
case TDLogLevel_Info: {
PrintToServer("[TF2TD > Info] %s", sFormattedMessage);
}
case TDLogLevel_Debug: {
PrintToServer("[TF2TD > Debug] %s", sFormattedMessage);
}
case TDLogLevel_Trace: {
PrintToServer("[TF2TD > Trace] %s", sFormattedMessage);
}
}
}
case TDLogType_FileAndConsole: {
switch (iLogLevel) {
case TDLogLevel_Error: {
LogError("[TF2TD > Error] %s", sFormattedMessage);
PrintToServer("[TF2TD > Error] %s", sFormattedMessage);
}
case TDLogLevel_Warning: {
LogError("[TF2TD > Warning] %s", sFormattedMessage);
PrintToServer("[TF2TD > Warning] %s", sFormattedMessage);
}
case TDLogLevel_Info: {
LogMessage("[TF2TD > Info] %s", sFormattedMessage);
PrintToServer("[TF2TD > Info] %s", sFormattedMessage);
}
case TDLogLevel_Debug: {
LogMessage("[TF2TD > Debug] %s", sFormattedMessage);
PrintToServer("[TF2TD > Debug] %s", sFormattedMessage);
}
case TDLogLevel_Trace: {
LogMessage("[TF2TD > Trace] %s", sFormattedMessage);
PrintToServer("[TF2TD > Trace] %s", sFormattedMessage);
}
}
}
}
}
}

View File

@@ -0,0 +1,237 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Gets a clients current metal amount.
*
* @param iClient The client.
* @return The clients current metal.
*/
stock int GetClientMetal(int iClient) {
return GetEntData(iClient, FindDataMapInfo(iClient, "m_iAmmo") + (3 * 4), 4);
}
/**
* Sets a clients metal amount.
*
* @param iClient The client.
* @param iMetal The metal amount the client should get.
* @noreturn
*/
stock void SetClientMetal(int iClient, int iMetal) {
SetEntData(iClient, FindDataMapInfo(iClient, "m_iAmmo") + (3 * 4), iMetal, 4);
Log(TDLogLevel_Trace, "Set %N's metal to %d", iClient, iMetal);
}
/**
* Resets a clients metal amount back to zero.
*
* @param iClient The client.
* @noreturn
*/
stock void ResetClientMetal(int iClient) {
CreateTimer(0.0, ResetClientMetalDelayed, iClient, TIMER_FLAG_NO_MAPCHANGE); // Process next frame
}
public Action ResetClientMetalDelayed(Handle hTimer, any iClient) {
SetClientMetal(iClient, 0);
return Plugin_Stop;
}
/**
* Add a amount to the clients metal amount.
*
* @param iClient The client.
* @param iMetal The metal amount to add.
* @return True if amount could be added, false otherwise.
*/
stock bool AddClientMetal(int iClient, int iMetal) {
if (iMetal < 0) {
if (GetClientMetal(iClient) + iMetal >= 0) {
SetClientMetal(iClient, GetClientMetal(iClient) + iMetal);
return true;
} else {
return false;
}
}
Player_CAddValue(iClient, PLAYER_METAL_PICK, iMetal);
SetClientMetal(iClient, GetClientMetal(iClient) + iMetal);
return true;
}
/**
* Spawns a metal pack.
*
* @param iMetalPackSpawnType The metal pack type.
* @param fLocation The location it should spawn at.
* @param iMetal The amount of metal it should spawn with.
* @return A TDMetalPackReturn value.
*/
stock TDMetalPackReturn SpawnMetalPack(TDMetalPackSpawnType iMetalPackSpawnType, float fLocation[3], int iMetal) {
int iEntity;
return SpawnMetalPack2(iMetalPackSpawnType, fLocation, iMetal, iEntity);
}
/**
* Spawns a metal pack.
*
* @param iMetalPackSpawnType The metal pack type.
* @param fLocation The location it should spawn at.
* @param iMetal The amount of metal it should spawn with.
* @param iEntity The entity reference to the metal pack.
* @return A TDMetalPackReturn value.
*/
stock TDMetalPackReturn SpawnMetalPack2(TDMetalPackSpawnType iMetalPackSpawnType, float fLocation[3], int iMetal, int &iEntity) {
Log(TDLogLevel_Trace, "SpawnMetalPack2: iMetalPackSpawnType=%d, fLocation=[%f, %f, %f], iMetal=%d", iMetalPackSpawnType, fLocation[0], fLocation[1], fLocation[2], iMetal);
if (iMetal <= 0) {
return TDMetalPack_InvalidMetal;
}
if (g_iMetalPackCount >= METALPACK_LIMIT) {
return TDMetalPack_LimitReached;
}
char sModelPath[PLATFORM_MAX_PATH];
switch (iMetalPackSpawnType) {
case TDMetalPack_Small: {
strcopy(sModelPath, sizeof(sModelPath), "models/items/ammopack_small.mdl");
}
case TDMetalPack_Medium: {
strcopy(sModelPath, sizeof(sModelPath), "models/items/ammopack_medium.mdl");
}
case TDMetalPack_Large: {
strcopy(sModelPath, sizeof(sModelPath), "models/items/ammopack_large.mdl");
}
default: {
return TDMetalPack_InvalidType;
}
}
int iMetalPack = CreateEntityByName("prop_dynamic");
DispatchKeyValue(iMetalPack, "model", sModelPath);
char sMetal[32];
IntToString(iMetal, sMetal, sizeof(sMetal));
DispatchKeyValue(iMetalPack, "targetname", sMetal);
if (DispatchSpawn(iMetalPack)) {
// Make it not solid, but still "collidable"
SetEntProp(iMetalPack, Prop_Send, "m_usSolidFlags", 12); // FSOLID_TRIGGER|FSOLID_NOT_SOLID
SetEntProp(iMetalPack, Prop_Data, "m_nSolidType", 6); // SOLID_VPHYSICS
SetEntProp(iMetalPack, Prop_Data, "m_CollisionGroup", 1); // COLLISION_GROUP_DEBRIS
SetVariantString("idle");
AcceptEntityInput(iMetalPack, "SetAnimation");
TeleportEntity(iMetalPack, fLocation, NULL_VECTOR, NULL_VECTOR);
SDKHook(iMetalPack, SDKHook_Touch, OnMetalPackPickup);
g_iMetalPackCount++;
iEntity = EntIndexToEntRef(iMetalPack);
}
Log(TDLogLevel_Debug, "Spawned metal pack (%d, Metal: %d)", iMetalPack, iMetal);
return TDMetalPack_SpawnedPack;
}
public void OnMetalPackPickup(int iMetalPack, int iClient) {
if (!IsDefender(iClient) || !IsValidEntity(iMetalPack)) {
return;
}
// TODO(hurp): Disperse / give metal to each client instead of just one.
char sMetal[32];
GetEntPropString(iMetalPack, Prop_Data, "m_iName", sMetal, sizeof(sMetal));
int iMetal = StringToInt(sMetal);
AddClientMetal(iClient, iMetal);
ResupplyClient(iClient, true, 0.25);
EmitSoundToClient(iClient, "items/gunpickup2.wav");
HideAnnotation(iMetalPack);
AcceptEntityInput(iMetalPack, "Kill");
g_iMetalPackCount--;
}
/**
* Refills a clients ammo, clip and/or ammo.
*
* @param iClient The client.
* @param bAmmoOnly Only refill ammo not clip.
* @param fPercent The percent to refill (0.5 would be 50%).
* @noreturn
*/
stock void ResupplyClient(int iClient, bool bAmmoOnly = false, float fPercent = 1.0) {
if (!IsDefender(iClient) || !IsPlayerAlive(iClient)) {
return;
}
int iWeapon = GetPlayerWeaponSlot(iClient, TFWeaponSlot_Primary);
if (IsValidEntity(iWeapon)) {
// Engineer's Shotgun
GivePlayerAmmo(iClient, RoundToFloor(32 * fPercent), GetEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType"), true);
if (!bAmmoOnly) {
SetClientClip(iClient, TFWeaponSlot_Primary, 6);
}
}
iWeapon = GetPlayerWeaponSlot(iClient, TFWeaponSlot_Secondary);
if (IsValidEntity(iWeapon)) {
// Engineer's Pistol
GivePlayerAmmo(iClient, RoundToFloor(200 * fPercent), GetEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType"), true);
if (!bAmmoOnly) {
SetClientClip(iClient, TFWeaponSlot_Secondary, 12);
}
}
}
/**
* Sets the clip of a weapon.
*
* @param iClient The client.
* @param iSlot The weapons slot index.
* @param iClip The clip the weapon should get.
* @noreturn
*/
stock void SetClientClip(int iClient, int iSlot, int iClip) {
if (IsValidClient(iClient) && IsClientInGame(iClient) && IsPlayerAlive(iClient)) {
int iWeapon = GetPlayerWeaponSlot(iClient, iSlot);
if (IsValidEntity(iWeapon)) {
int iAmmoTable = FindSendPropInfo("CTFWeaponBase", "m_iClip1");
SetEntData(iWeapon, iAmmoTable, iClip, 4, true);
}
}
}

View File

@@ -0,0 +1,48 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Gets a clients steam community id.
*
* @param iClient The client.
* @param sBuffer The destination string buffer.
* @param iMaxLength The maximum length of the output string buffer.
* @return True on success, false otherwise.
*/
stock bool GetClientCommunityId(int iClient, char[] sBuffer, int iMaxLength) {
if (!IsClientConnected(iClient) || IsFakeClient(iClient)) {
return false;
}
int iSteamAccountId = GetSteamAccountID(iClient);
if (iSteamAccountId > 0) {
char sSteamAccountId[32];
char[] sBase = "76561197960265728";
// char[] sBase = { "7", "6", "5", "6", "1", "1", "9", "7", "9", "6", "0", "2", "6", "5", "7", "2", "8" };
char[] sSteamId = new char[iMaxLength];
IntToString((iSteamAccountId - iSteamAccountId % 2) / 2, sSteamAccountId, sizeof(sSteamAccountId));
int iCurrent, iCarryOver = iSteamAccountId % 2;
sSteamId[iMaxLength - 1] = '\0';
int iPos = FindCharInString(sSteamId, '7');
if (iPos > 0 && Substring(sBuffer, iMaxLength, sSteamId, iMaxLength, iPos, strlen(sSteamId))) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,111 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Gives a weapon to a client.
* See https://wiki.alliedmods.net/Team_fortress_2_item_definition_indexes for item indexes and classnames.
*
* @param iClient The client who should get the weapon.
* @param iItemDefinitionIndex The weapons items index.
* @param iSlot The weapons slot.
* @param iLevel The weapons level. (must be between 0 an 100)
* @param iQuality The weapons quality. (must be between 0 an 10)
* @param bPreserveAttributes Should attributes be preserved?
* @param sClassname The weapons classname.
* @param sAttributes The attributes to apply tp the weapon. (Format: "<AttributeId1>=<Value1>;<AttributeId2>=<Value2>;...")
* @noreturn
*/
stock void TF2Items_GiveWeapon(int iClient, int iItemDefinitionIndex, int iSlot, int iLevel, int iQuality, bool bPreserveAttributes, char[] sClassname, char[] sAttributes) {
Log(TDLogLevel_Trace, "%d, %d, %d, %d, %d, %s, \"%s\", \"%s\"", iClient, iItemDefinitionIndex, iSlot, iLevel, iQuality, (bPreserveAttributes ? "true" : "false"), sClassname, sAttributes);
Handle hItem = TF2Items_CreateItem(OVERRIDE_ALL);
int iFlags = 0;
TF2Items_SetItemIndex(hItem, iItemDefinitionIndex);
if (iLevel >= 0 && iLevel <= 100) {
TF2Items_SetLevel(hItem, iLevel);
iFlags |= OVERRIDE_ITEM_LEVEL;
}
if (iQuality >= 0 && iQuality <= 10) {
TF2Items_SetQuality(hItem, iQuality);
iFlags |= OVERRIDE_ITEM_QUALITY;
}
if (bPreserveAttributes) {
iFlags |= PRESERVE_ATTRIBUTES;
}
char sAttributeList[15][16], sAttribute[2][16];
int iAttributeIndex;
int iNumAttributes = 0;
float fAttributeValue;
// More than 1 attribute
if (FindCharInString(sAttributes, ';') != -1) {
ExplodeString(sAttributes, ";", sAttributeList, sizeof(sAttributeList), sizeof(sAttributeList[]));
for (int i = 0; i < sizeof(sAttributeList); i++) {
if (!StrEqual(sAttributeList[i], "")) {
ExplodeString(sAttributeList[i], "=", sAttribute, sizeof(sAttribute), sizeof(sAttribute[]));
iAttributeIndex = StringToInt(sAttribute[0]);
fAttributeValue = StringToFloat(sAttribute[1]);
PrintToServer("Attribute: %d, Value: %f", iAttributeIndex, fAttributeValue);
TF2Items_SetAttribute(hItem, i, iAttributeIndex, fAttributeValue);
iNumAttributes++;
}
}
} else {
// Exactly 1 attribute
if (!StrEqual(sAttributes, "")) {
ExplodeString(sAttributes, "=", sAttribute, sizeof(sAttribute), sizeof(sAttribute[]));
iAttributeIndex = StringToInt(sAttribute[0]);
fAttributeValue = StringToFloat(sAttribute[1]);
PrintToServer("Attribute: %d, Value: %f", iAttributeIndex, fAttributeValue);
TF2Items_SetAttribute(hItem, 0, iAttributeIndex, fAttributeValue);
iNumAttributes++;
}
}
if (iNumAttributes != 0 && iNumAttributes <= 15) {
TF2Items_SetNumAttributes(hItem, iNumAttributes);
iFlags |= OVERRIDE_ATTRIBUTES;
}
TF2Items_SetFlags(hItem, iFlags | FORCE_GENERATION);
TF2Items_SetClassname(hItem, sClassname);
TF2_RemoveWeaponSlot(iClient, iSlot);
int iWeapon = TF2Items_GiveNamedItem(iClient, hItem);
if (IsValidEntity(iWeapon)) {
EquipPlayerWeapon(iClient, iWeapon);
Log(TDLogLevel_Debug, "Gave weapon (%d) to %N", iItemDefinitionIndex, iClient);
}
if (IsTower(iClient)) {
TDTowerId iTowerId = GetTowerId(iClient);
Tower_OnWeaponChanged(iClient, iTowerId, iItemDefinitionIndex, iSlot, iWeapon);
}
CloseHandle(hItem);
}

View File

@@ -0,0 +1,307 @@
#pragma semicolon 1
#include <sourcemod>
#if defined INFO_INCLUDES
#include "../info/constants.sp"
#include "../info/enums.sp"
#include "../info/variables.sp"
#endif
/**
* Creates a beam line.
*
* @param iClient The client.
* @param fStart The start point.
* @param fEnd The end point.
* @param fDuration The duration it should appear (in seconds).
* @param iColors The colors.
* @noreturn
*/
stock void CreateBeamLine(int iClient, float fStart[3], float fEnd[3], float fDuration = 5.0, const int[] iColors = { 255, 0, 0, 255 } ) {
int iColors2[4];
iColors2[0] = iColors[0];
iColors2[1] = iColors[1];
iColors2[2] = iColors[2];
iColors2[3] = iColors[3];
TE_SetupBeamPoints(fStart, fEnd, g_iLaserMaterial, g_iHaloMaterial, 0, 0, fDuration + 0.1, 1.0, 1.0, 1, 0.0, iColors2, 0);
if (fDuration == 0.0) {
DataPack hPack = new DataPack();
CreateDataTimer(fDuration, Timer_CreateBeam, hPack);
hPack.WriteCell(iClient);
hPack.WriteFloat(fDuration);
hPack.WriteFloat(fStart[0]);
hPack.WriteFloat(fStart[1]);
hPack.WriteFloat(fStart[2]);
hPack.WriteFloat(fEnd[0]);
hPack.WriteFloat(fEnd[1]);
hPack.WriteFloat(fEnd[2]);
hPack.WriteCell(iColors[0]);
hPack.WriteCell(iColors[1]);
hPack.WriteCell(iColors[2]);
hPack.WriteCell(iColors[3]);
}
TE_SendToAll();
}
public Action Timer_CreateBeam(Handle hTimer, DataPack hPack) {
int iClient, iColors[4];
float fDuration, fStart[3], fEnd[3];
hPack.Reset();
iClient = hPack.ReadCell();
fDuration = hPack.ReadFloat();
fStart[0] = hPack.ReadFloat();
fStart[1] = hPack.ReadFloat();
fStart[2] = hPack.ReadFloat();
fEnd[0] = hPack.ReadFloat();
fEnd[1] = hPack.ReadFloat();
fEnd[2] = hPack.ReadFloat();
iColors[0] = hPack.ReadCell();
iColors[1] = hPack.ReadCell();
iColors[2] = hPack.ReadCell();
iColors[3] = hPack.ReadCell();
CreateBeamLine(iClient, fStart, fEnd, fDuration, iColors);
return Plugin_Stop;
}
/**
* Creates a beam box.
*
* @param iClient The client.
* @param fStart The start point.
* @param fEnd The end point.
* @param fDuration The duration it should appear (in seconds).
* @param iColors The colors.
* @noreturn
*/
stock void CreateBeamBox(int iClient, float fStart[3], float fEnd[3], float fDuration = 5.0, const int[] iColors = { 255, 0, 0, 255 } ) {
float fPoint[8][3];
CopyVector(fStart, fPoint[0]);
CopyVector(fEnd, fPoint[7]);
CreateZonePoints(fPoint);
for (int i = 0, i2 = 3; i2 >= 0; i += i2--) {
for (int j = 1; j <= 7; j += (j / 2) + 1) {
if (j != 7 - i) {
CreateBeamLine(iClient, fPoint[i], fPoint[j], fDuration, iColors);
}
}
}
}
/**
* Creates all 8 zone points by using start and end point.
*
* @param fPoint The array to store the points in.
* @noreturn
*/
stock void CreateZonePoints(float fPoint[8][3]) {
for (int i = 1; i < 7; i++) {
for (int j = 0; j < 3; j++) {
fPoint[i][j] = fPoint[((i >> (2 - j)) & 1) * 7][j];
}
}
}
/**
* Creates all an beam box around a client.
*
* @param iClient The client.
* @param fStart The start point.
* @param fEnd The end point.
* @param fDuration The duration it should appear (in seconds).
* @param iColors The colors.
* @noreturn
*/
stock void CreateBeamBoxAroundClient(int iClient, float fDistance, bool OnlyPlayerHeight = true, float fDuration = 5.0, const int[] iColors = { 255, 0, 0, 255 } ) {
if (!IsValidClient(iClient) || !IsClientConnected(iClient) || !IsClientInGame(iClient) || !IsPlayerAlive(iClient)) {
return;
}
float fLocation[3], fStart[3], fEnd[3];
GetClientAbsOrigin(iClient, fLocation);
if (OnlyPlayerHeight) {
fStart[0] = fLocation[0] - fDistance;
fStart[1] = fLocation[1] - fDistance;
fStart[2] = fLocation[2];
fEnd[0] = fLocation[0] + fDistance;
fEnd[1] = fLocation[1] + fDistance;
fEnd[2] = fLocation[2] + 83;
} else {
fStart[0] = fLocation[0] - fDistance;
fStart[1] = fLocation[1] - fDistance;
fStart[2] = fLocation[2] - fDistance;
fEnd[0] = fLocation[0] + fDistance;
fEnd[1] = fLocation[1] + fDistance;
fEnd[2] = fLocation[2] + 83 + fDistance;
}
CreateBeamBox(iClient, fStart, fEnd, fDuration, iColors);
float fPoint[8][3];
CopyVector(fStart, fPoint[0]);
CopyVector(fEnd, fPoint[7]);
CreateZonePoints(fPoint);
CopyZone(fPoint, g_fBeamPoints[iClient]);
}
/**
* Copies one float vector onto another one.
*
* @param fSource The vector to copy.
* @param fDestination The destination vector.
* @noreturn
*/
stock void CopyVector(float fSource[3], float fDestination[3]) {
for (int i = 0; i < sizeof(fSource); i++) {
fDestination[i] = fSource[i];
}
}
/**
* Copies one zone array onto another one.
*
* @param fSource The array to copy.
* @param fDestination The destination array.
* @noreturn
*/
stock void CopyZone(float fSource[8][3], float fDestination[8][3]) {
for (int i = 0; i < sizeof(fSource); i++) {
for (int j = 0; j < sizeof(fSource[]); j++) {
fDestination[i][j] = fSource[i][j];
}
}
}
/**
* Checkes if an entity is inside a zone.
*
* @param iClient The entity.
* @param fZone The zone array.
* @return True if inside, false otherwise.
*/
stock bool IsEntityInZone(int iEntity, float fZone[8][3], float fDifferenceZ) {
float fEntityPosition[3];
GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fEntityPosition);
fEntityPosition[2] += fDifferenceZ;
return IsPointInZone(fEntityPosition, fZone);
}
/**
* Checkes if a client is inside a zone.
*
* @param iClient The client.
* @param fPoint The zone array.
* @return True if inside, false otherwise.
*/
stock bool IsClientInZone(int iClient, float fPoint[8][3]) {
float fPlayerPosition[3], fPlayerPoint[8][3];
GetEntPropVector(iClient, Prop_Send, "m_vecOrigin", fPlayerPosition);
fPlayerPosition[2] += 41.5;
fPlayerPoint[0][0] = fPlayerPosition[0] - 12.0;
fPlayerPoint[0][1] = fPlayerPosition[1] - 12.0;
fPlayerPoint[0][2] = fPlayerPosition[2] - 20.0;
fPlayerPoint[7][0] = fPlayerPosition[0] + 12.0;
fPlayerPoint[7][1] = fPlayerPosition[1] + 12.0;
fPlayerPoint[7][2] = fPlayerPosition[2] + 20.0;
CreateZonePoints(fPlayerPoint);
return Box3DIntersects(fPlayerPoint, fPoint);
}
/**
* Draws a bounding box around a client.
*
* @param iClient The client.
* @noreturn
*/
stock void DrawClientBoundingBox(int iClient) {
float fPlayerPosition[3], fPlayerPoint[8][3];
GetEntPropVector(iClient, Prop_Send, "m_vecOrigin", fPlayerPosition);
fPlayerPosition[2] += 41.5;
fPlayerPoint[0][0] = fPlayerPosition[0] - 12.0;
fPlayerPoint[0][1] = fPlayerPosition[1] - 12.0;
fPlayerPoint[0][2] = fPlayerPosition[2] - 20.0;
fPlayerPoint[7][0] = fPlayerPosition[0] + 12.0;
fPlayerPoint[7][1] = fPlayerPosition[1] + 12.0;
fPlayerPoint[7][2] = fPlayerPosition[2] + 20.0;
CreateZonePoints(fPlayerPoint);
CreateBeamBox(iClient, fPlayerPoint[0], fPlayerPoint[7]);
}
/**
* Checkes if a zone intersects with another zone.
*
* @param fCheck The 1st zone array.
* @param fSource The 2nd zone array.
* @return True if intersects, false otherwise.
*/
stock bool Box3DIntersects(float fCheck[8][3], float fSource[8][3]) {
if (fCheck[0][0] > fSource[4][0] || // fCheck is right of fSource
fCheck[4][0] < fSource[0][0] || // fCheck is left of fSource
fCheck[1][2] < fSource[0][2] || // fCheck is below fSource
fCheck[0][2] > fSource[1][2] || // fCheck is above fSource
fCheck[3][1] < fSource[1][1] || // fCheck is behind fSource
fCheck[1][1] > fSource[3][1]) { // fCheck is in front fSource
return false;
}
return true;
}
/**
* Checkes if a point is inside a zone.
*
* @param fPoint The point vector.
* @param fZone The zone array.
* @return True if inside, false otherwise.
*/
stock bool IsPointInZone(float fPoint[3], float fZone[8][3]) {
if (fPoint[0] > fZone[4][0] || // fPoint is right of fZone
fPoint[0] < fZone[0][0] || // fPoint is left of fZone
fPoint[2] < fZone[0][2] || // fPoint is below fZone
fPoint[2] > fZone[1][2] || // fPoint is above fZone
fPoint[1] < fZone[1][1] || // fPoint is behind fZone
fPoint[1] > fZone[3][1]) { // fPoint is in front fZone
return false;
}
return true;
}