Initial commit
This commit is contained in:
465
scripting/towerdefense/commands.sp
Normal file
465
scripting/towerdefense/commands.sp
Normal 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;
|
||||
}
|
544
scripting/towerdefense/database/general.sp
Normal file
544
scripting/towerdefense/database/general.sp
Normal 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();
|
||||
}
|
550
scripting/towerdefense/database/player.sp
Normal file
550
scripting/towerdefense/database/player.sp
Normal 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;
|
||||
}
|
||||
}
|
494
scripting/towerdefense/database/server.sp
Normal file
494
scripting/towerdefense/database/server.sp
Normal 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;
|
||||
}
|
||||
}
|
386
scripting/towerdefense/events.sp
Normal file
386
scripting/towerdefense/events.sp
Normal 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;
|
||||
}
|
||||
}
|
45
scripting/towerdefense/handler/antiair.sp
Normal file
45
scripting/towerdefense/handler/antiair.sp
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
|
||||
public void AntiAir_ProjectileTick(int iProjectile, int iTower) {
|
||||
if (IsValidEntity(iProjectile)) {
|
||||
float fLocation[3];
|
||||
GetEntPropVector(iProjectile, Prop_Data, "m_vecOrigin", fLocation);
|
||||
char sEntityName[50];
|
||||
GetEntPropString(iProjectile, Prop_Data, "m_iName", sEntityName, sizeof(sEntityName));
|
||||
|
||||
if (fLocation[2] >= g_fAirWaveSpawn[2] && fLocation[2] <= g_fAirWaveSpawn[2] + 95.0) {
|
||||
float fAngles[3];
|
||||
GetEntPropVector(iProjectile, Prop_Data, "m_angRotation", fAngles);
|
||||
|
||||
float fVelocity[3];
|
||||
GetEntPropVector(iProjectile, Prop_Data, "m_vecAbsVelocity", fVelocity);
|
||||
|
||||
float fNewVelocity[3];
|
||||
fNewVelocity[0] = fVelocity[0];
|
||||
fNewVelocity[1] = fVelocity[1];
|
||||
fNewVelocity[2] = -fVelocity[2];
|
||||
|
||||
float fBounceVec[3];
|
||||
SubtractVectors(fNewVelocity, NULL_VECTOR, fBounceVec);
|
||||
fBounceVec[2] = 0.0;
|
||||
|
||||
float fNewAngles[3];
|
||||
GetVectorAngles(fBounceVec, fNewAngles);
|
||||
|
||||
TeleportEntity(iProjectile, NULL_VECTOR, fNewAngles, fBounceVec);
|
||||
} else {
|
||||
DataPack hPack = new DataPack();
|
||||
|
||||
hPack.WriteCell(iProjectile);
|
||||
hPack.WriteCell(iTower);
|
||||
|
||||
RequestFrame(AntiAir_ProjectileTick2, hPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AntiAir_ProjectileTick2(DataPack iData) {
|
||||
iData.Reset();
|
||||
AntiAir_ProjectileTick(iData.ReadCell(), iData.ReadCell());
|
||||
delete iData;
|
||||
}
|
429
scripting/towerdefense/handler/aoe.sp
Normal file
429
scripting/towerdefense/handler/aoe.sp
Normal file
@@ -0,0 +1,429 @@
|
||||
/**
|
||||
* Checks for clients near AoE Towers
|
||||
*
|
||||
* @param hTimer Timer Handle
|
||||
* @param iTower The tower
|
||||
* @noreturn
|
||||
*/
|
||||
public Action Timer_ClientNearAoETower(Handle hTimer) {
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsClientInGame(iClient) && IsPlayerAlive(iClient)) {
|
||||
if (IsTower(iClient)) {
|
||||
int iTower = iClient;
|
||||
TDTowerId iTowerId = GetTowerId(iTower);
|
||||
|
||||
if (iTowerId == TDTower_AoE_Engineer) {
|
||||
float fAreaScale = 80.0 * Tower_GetAreaScale(GetTowerId(iTower));
|
||||
CreateBeamBoxAroundClient(iTower, fAreaScale, true, 0.2, { 231, 76, 60, 25 });
|
||||
Tower_ObjectNearAoEEngineer(iTower, iClient);
|
||||
}
|
||||
if (iTowerId == TDTower_Medic) {
|
||||
float fAreaScale = 160.0 * Tower_GetAreaScale(GetTowerId(iTower));
|
||||
CreateBeamBoxAroundClient(iTower, fAreaScale, true, 0.2, { 46, 204, 113, 25 });
|
||||
Tower_ObjectNearAoEMedic(iTower);
|
||||
}
|
||||
if (iTowerId == TDTower_Kritz_Medic) {
|
||||
float fAreaScale = 60.0 * Tower_GetAreaScale(GetTowerId(iTower));
|
||||
CreateBeamBoxAroundClient(iTower, fAreaScale, true, 0.2, { 41, 128, 185, 25 });
|
||||
Tower_ObjectNearKritzMedic(iTower);
|
||||
}
|
||||
if (iTowerId == TDTower_Slow_Spy) {
|
||||
float fAreaScale = 100.0 * Tower_GetAreaScale(GetTowerId(iTower));
|
||||
CreateBeamBoxAroundClient(iTower, fAreaScale, false, 0.2, { 44, 62, 80, 25 });
|
||||
Tower_ObjectNearAoESpy(iTower);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is close to AoEMedic
|
||||
*
|
||||
* @param iTower The tower
|
||||
* @param iClient The player
|
||||
* @noreturn
|
||||
*/
|
||||
public void Tower_ObjectNearAoEMedic(int iTower) {
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient) && IsClientInZone(iClient, g_fBeamPoints[iTower])) {
|
||||
Player_AddHealth(iClient, 5);
|
||||
|
||||
// Check if there is no Beam
|
||||
if (g_iHealBeamIndex[iClient][0] == 0 && g_iHealBeamIndex[iClient][1] == 0) {
|
||||
AttachHealBeam(iTower, iClient);
|
||||
}
|
||||
|
||||
} else {
|
||||
Tower_ObjectNotNearAoEMedic(iClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player isn't close to AoEMedic
|
||||
*
|
||||
* @param iTower The tower
|
||||
* @param iClient The player
|
||||
* @noreturn
|
||||
*/
|
||||
public void Tower_ObjectNotNearAoEMedic(int iClient) {
|
||||
// Remove Beam if there is one
|
||||
if (g_iHealBeamIndex[iClient][0] != 0) {
|
||||
if (IsValidEdict(g_iHealBeamIndex[iClient][0])) {
|
||||
RemoveEdict(g_iHealBeamIndex[iClient][0]);
|
||||
g_iHealBeamIndex[iClient][0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iHealBeamIndex[iClient][1] != 0) {
|
||||
if (IsValidEdict(g_iHealBeamIndex[iClient][1])) {
|
||||
RemoveEdict(g_iHealBeamIndex[iClient][1]);
|
||||
g_iHealBeamIndex[iClient][1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is close to AoEEngineer
|
||||
*
|
||||
* @param iTower The tower
|
||||
* @param iClient The player
|
||||
* @noreturn
|
||||
*/
|
||||
public void Tower_ObjectNearAoEEngineer(int iTower, int iClient) {
|
||||
if (iAoEEngineerTimer < 3 && !IsTowerAttached(iTower)) {
|
||||
iAoEEngineerTimer++;
|
||||
return;
|
||||
} else if (!IsTowerAttached(iTower)) {
|
||||
iAoEEngineerTimer = 0;
|
||||
}
|
||||
int iSentry = -1, iDispenser = -1;
|
||||
float fLocation[3];
|
||||
|
||||
// Sentries
|
||||
while ((iSentry = FindEntityByClassname(iSentry, "obj_sentrygun")) != -1) {
|
||||
if (!IsValidEntity(iSentry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GetEntPropVector(iSentry, Prop_Send, "m_vecOrigin", fLocation);
|
||||
fLocation[2] += 20.0;
|
||||
|
||||
if (IsPointInZone(fLocation, g_fBeamPoints[iTower])) {
|
||||
int iShellsMax, iHealthMax, iRocketsMax, iMetalMax;
|
||||
|
||||
if (IsMiniSentry(iSentry)) {
|
||||
iShellsMax = 150;
|
||||
iHealthMax = 150;
|
||||
iRocketsMax = 0;
|
||||
iMetalMax = 0;
|
||||
} else {
|
||||
switch (GetBuildingLevel(iSentry)) {
|
||||
case 1: {
|
||||
iShellsMax = 150;
|
||||
iHealthMax = 150;
|
||||
iRocketsMax = 0;
|
||||
iMetalMax = 1000;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
iShellsMax = 200;
|
||||
iHealthMax = 180;
|
||||
iRocketsMax = 0;
|
||||
iMetalMax = 1000;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
iShellsMax = 200;
|
||||
iHealthMax = 216;
|
||||
iRocketsMax = 20;
|
||||
iMetalMax = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iState = GetEntProp(iSentry, Prop_Send, "m_iState");
|
||||
|
||||
// If is not building up
|
||||
if (iState != 0) {
|
||||
int iShells = GetEntProp(iSentry, Prop_Send, "m_iAmmoShells");
|
||||
int iHealth = GetEntProp(iSentry, Prop_Send, "m_iHealth");
|
||||
int iRockets = GetEntProp(iSentry, Prop_Send, "m_iAmmoRockets");
|
||||
int iMetal = GetEntProp(iSentry, Prop_Send, "m_iUpgradeMetal");
|
||||
|
||||
if (IsMiniSentry(iSentry)) {
|
||||
int iShellsPerHit = 20; // Default: 40
|
||||
|
||||
if (iShells + iShellsPerHit < iShellsMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShells + iShellsPerHit);
|
||||
} else if (iShells < iShellsMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShellsMax);
|
||||
}
|
||||
} else {
|
||||
int iMetalPerHit = 10; // Default: 25
|
||||
int iHealthPerHit = 50; // Default: 105
|
||||
int iShellsPerHit = 20; // Default: 40
|
||||
int iRocketsPerHit = 4; // Default: 8
|
||||
|
||||
if (iMetal < iMetalMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iUpgradeMetal", iMetal + iMetalPerHit);
|
||||
|
||||
// Upgrade to the next level
|
||||
} else if (iMetalMax != 0 && !g_bAoEEngineerAttack) {
|
||||
float fLocationTower[3];
|
||||
GetClientAbsOrigin(iTower, fLocationTower);
|
||||
|
||||
TeleportEntity(iTower, fLocation, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
g_bAoEEngineerAttack = true;
|
||||
|
||||
DataPack hPack = new DataPack();
|
||||
hPack.WriteFloat(float(iTower));
|
||||
hPack.WriteFloat(fLocationTower[0]);
|
||||
hPack.WriteFloat(fLocationTower[1]);
|
||||
hPack.WriteFloat(fLocationTower[2]);
|
||||
|
||||
CreateTimer(0.2, Timer_TeleportAoEEngineerBack, hPack, _);
|
||||
}
|
||||
|
||||
if (iHealth + iHealthPerHit < iHealthMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iHealth", iHealth + iHealthPerHit);
|
||||
} else if (iHealth < iHealthMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iHealth", iHealthMax);
|
||||
}
|
||||
|
||||
if (iShells + iShellsPerHit < iShellsMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShells + iShellsPerHit);
|
||||
} else if (iShells < iShellsMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iAmmoShells", iShellsMax);
|
||||
}
|
||||
if (iRockets + iRocketsPerHit < iRocketsMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iAmmoRockets", iRockets + iRocketsPerHit);
|
||||
} else if (iRockets < iRocketsMax) {
|
||||
SetEntProp(iSentry, Prop_Send, "m_iAmmoRockets", iRocketsMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispensers
|
||||
while ((iDispenser = FindEntityByClassname(iDispenser, "obj_dispenser")) != -1) {
|
||||
if (!IsValidEntity(iDispenser)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GetEntPropVector(iDispenser, Prop_Send, "m_vecOrigin", fLocation);
|
||||
|
||||
fLocation[2] += 20.0;
|
||||
|
||||
if (IsPointInZone(fLocation, g_fBeamPoints[iTower])) {
|
||||
int iHealthMax, iMetalMax;
|
||||
|
||||
switch (GetBuildingLevel(iDispenser)) {
|
||||
case 1: {
|
||||
iHealthMax = 150;
|
||||
iMetalMax = 500;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
iHealthMax = 180;
|
||||
iMetalMax = 500;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
iHealthMax = 216;
|
||||
iMetalMax = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int iBuildingUp = GetEntProp(iDispenser, Prop_Send, "m_bBuilding");
|
||||
if (iBuildingUp != 1) { // Is not building up
|
||||
int iHealth = GetEntProp(iDispenser, Prop_Send, "m_iHealth");
|
||||
int iMetal = GetEntProp(iDispenser, Prop_Send, "m_iUpgradeMetal");
|
||||
int iMetalPerHit = 10; // Default: 25
|
||||
int iHealthPerHit = 50; // Default: 105
|
||||
|
||||
if (iMetal < iMetalMax) {
|
||||
SetEntProp(iDispenser, Prop_Send, "m_iUpgradeMetal", iMetal + iMetalPerHit);
|
||||
} else if (iMetalMax != 0 && !g_bAoEEngineerAttack) { // Upgrade to the next level
|
||||
float fLocationTower[3];
|
||||
GetClientAbsOrigin(iTower, fLocationTower);
|
||||
|
||||
TeleportEntity(iTower, fLocation, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
g_bAoEEngineerAttack = true;
|
||||
|
||||
DataPack hPack = new DataPack();
|
||||
hPack.WriteFloat(float(iTower));
|
||||
hPack.WriteFloat(fLocationTower[0]);
|
||||
hPack.WriteFloat(fLocationTower[1]);
|
||||
hPack.WriteFloat(fLocationTower[2]);
|
||||
|
||||
CreateTimer(0.5, Timer_TeleportAoEEngineerBack, hPack, _);
|
||||
}
|
||||
|
||||
if (iHealth + iHealthPerHit < iHealthMax) {
|
||||
SetEntProp(iDispenser, Prop_Send, "m_iHealth", iHealth + iHealthPerHit);
|
||||
} else if (iHealth < iHealthMax) {
|
||||
SetEntProp(iDispenser, Prop_Send, "m_iHealth", iHealthMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action Timer_TeleportAoEEngineerBack(Handle hTimer, DataPack hPack) {
|
||||
hPack.Reset();
|
||||
|
||||
g_bAoEEngineerAttack = false;
|
||||
int iTower = RoundToZero(ReadPackFloat(hPack));
|
||||
float fLocation[3];
|
||||
fLocation[0] = hPack.ReadFloat();
|
||||
fLocation[1] = hPack.ReadFloat();
|
||||
fLocation[2] = hPack.ReadFloat();
|
||||
TeleportEntity(iTower, fLocation, NULL_VECTOR, NULL_VECTOR);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is close to KritzMedic
|
||||
*
|
||||
* @param iTower The tower
|
||||
* @noreturn
|
||||
*/
|
||||
public void Tower_ObjectNearKritzMedic(int iTower) {
|
||||
// If Kritz Time is up
|
||||
if (TF2_GetUberLevel(iTower) <= 0.0)
|
||||
g_bKritzMedicCharged = false;
|
||||
|
||||
// If Bot is fully charged, charge player
|
||||
if (g_bKritzMedicCharged && TF2_GetUberLevel(iTower) > 0.0) {
|
||||
TF2_SetUberLevel(iTower, TF2_GetUberLevel(iTower) - 0.02);
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsValidClient(iClient))
|
||||
TF2_AddCondition(iClient, TFCond_Kritzkrieged, 0.3);
|
||||
}
|
||||
// If Bot is fully charged
|
||||
} else if (TF2_GetUberLevel(iTower) >= 1.0) {
|
||||
g_bKritzMedicCharged = true;
|
||||
} else if (iAoEKritzMedicTimer < 3 && !IsTowerAttached(iTower)) {
|
||||
iAoEKritzMedicTimer++;
|
||||
return;
|
||||
} else if (!IsTowerAttached(iTower)) {
|
||||
iAoEKritzMedicTimer = 0;
|
||||
}
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient) && IsClientInZone(iClient, g_fBeamPoints[iTower])) {
|
||||
float fUberLevel = TF2_GetUberLevel(iTower);
|
||||
TF2_SetUberLevel(iTower, fUberLevel + 0.01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is close to AoESpy
|
||||
*
|
||||
* @param iTower The tower
|
||||
* @param iClient The player
|
||||
* @noreturn
|
||||
*/
|
||||
public void Tower_ObjectNearAoESpy(int iTower) {
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsAttacker(iClient) && IsClientInZone(iClient, g_fBeamPoints[iTower]) && !g_iSlowAttacker[iClient])
|
||||
g_iSlowAttacker[iClient] = true;
|
||||
else if (g_iSlowAttacker[iClient] && IsClientInZone(iClient, g_fBeamPoints[iTower]))
|
||||
TF2_AddCondition(iClient, TFCond_TeleportedGlow, 1.0);
|
||||
else if (g_iSlowAttacker[iClient])
|
||||
g_iSlowAttacker[iClient] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the level of an building.
|
||||
*
|
||||
* @param iEntity The buildings entity index.
|
||||
* @return The buildings level.
|
||||
*/
|
||||
public int GetBuildingLevel(int iEntity) {
|
||||
return GetEntProp(iEntity, Prop_Send, "m_iUpgradeLevel");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wheter a sentry is a Mini-Sentry or not.
|
||||
*
|
||||
* @param iSentry The sentries entity index.
|
||||
* @return True if mini, false otherwise.
|
||||
*/
|
||||
public bool IsMiniSentry(int iSentry) {
|
||||
return ((GetEntProp(iSentry, Prop_Send, "m_bMiniBuilding") == 0) ? false : true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a beam between two entities
|
||||
*
|
||||
* @param iEntity The start point
|
||||
* @param iTarget The target
|
||||
* @noreturn
|
||||
*/
|
||||
stock void AttachHealBeam(int iEntity, int iTarget) {
|
||||
int iParticleIndex1 = CreateEntityByName("info_particle_system");
|
||||
int iParticleIndex2 = CreateEntityByName("info_particle_system");
|
||||
|
||||
if (IsValidEdict(iParticleIndex1)) {
|
||||
char sEntityName[128];
|
||||
Format(sEntityName, sizeof(sEntityName), "target%i", iEntity);
|
||||
DispatchKeyValue(iEntity, "targetname", sEntityName);
|
||||
|
||||
char sTargetName1[128];
|
||||
Format(sTargetName1, sizeof(sTargetName1), "target%i", iTarget);
|
||||
DispatchKeyValue(iTarget, "targetname", sTargetName1);
|
||||
|
||||
char sTargetName2[128];
|
||||
Format(sTargetName2, sizeof(sTargetName2), "tf2particle%i", iTarget);
|
||||
|
||||
DispatchKeyValue(iParticleIndex2, "targetname", sTargetName2);
|
||||
DispatchKeyValue(iParticleIndex2, "parentname", sTargetName1);
|
||||
|
||||
SetVariantString(sTargetName1);
|
||||
AcceptEntityInput(iParticleIndex2, "SetParent");
|
||||
|
||||
SetVariantString("flag");
|
||||
AcceptEntityInput(iParticleIndex2, "SetParentAttachment");
|
||||
|
||||
DispatchKeyValue(iParticleIndex1, "targetname", "tf2particle");
|
||||
DispatchKeyValue(iParticleIndex1, "parentname", sEntityName);
|
||||
DispatchKeyValue(iParticleIndex1, "effect_name", "dispenser_heal_blue");
|
||||
DispatchKeyValue(iParticleIndex1, "cpoint1", sTargetName2);
|
||||
|
||||
DispatchSpawn(iParticleIndex1);
|
||||
|
||||
SetVariantString(sEntityName);
|
||||
AcceptEntityInput(iParticleIndex1, "SetParent");
|
||||
|
||||
SetVariantString("flag");
|
||||
AcceptEntityInput(iParticleIndex1, "SetParentAttachment");
|
||||
|
||||
ActivateEntity(iParticleIndex1);
|
||||
AcceptEntityInput(iParticleIndex1, "start");
|
||||
|
||||
g_iHealBeamIndex[iTarget][0] = iParticleIndex1;
|
||||
g_iHealBeamIndex[iTarget][1] = iParticleIndex2;
|
||||
}
|
||||
}
|
||||
|
||||
stock float TF2_GetUberLevel(int iClient) {
|
||||
int iIndex = GetPlayerWeaponSlot(iClient, 1);
|
||||
if (iIndex > 0)
|
||||
return GetEntPropFloat(iIndex, Prop_Send, "m_flChargeLevel");
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
stock void TF2_SetUberLevel(int iClient, float fUberLevel) {
|
||||
int iIndex = GetPlayerWeaponSlot(iClient, 1);
|
||||
if (iIndex > 0)
|
||||
SetEntPropFloat(iIndex, Prop_Send, "m_flChargeLevel", fUberLevel);
|
||||
}
|
280
scripting/towerdefense/handler/buttons.sp
Normal file
280
scripting/towerdefense/handler/buttons.sp
Normal file
@@ -0,0 +1,280 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Hooks buttons.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void HookButtons() {
|
||||
HookEntityOutput("func_breakable", "OnHealthChanged", OnButtonShot);
|
||||
}
|
||||
|
||||
public void OnButtonShot(const char[] sOutput, int iCaller, int iActivator, float fDelay) {
|
||||
char sName[64];
|
||||
GetEntPropString(iCaller, Prop_Data, "m_iName", sName, sizeof(sName));
|
||||
|
||||
if (StrContains(sName, "break_tower_tp_") != -1) {
|
||||
// Tower teleport
|
||||
|
||||
char sNameParts[4][32];
|
||||
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
|
||||
|
||||
TDTowerId iTowerId = view_as<TDTowerId>(StringToInt(sNameParts[3]));
|
||||
Tower_OnButtonTeleport(iTowerId, iCaller, iActivator);
|
||||
} else if (StrContains(sName, "break_tower_") != -1) {
|
||||
// Tower buy
|
||||
|
||||
if (!g_bCanGetUnlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_bCanGetUnlocks = false;
|
||||
CreateTimer(0.5, Timer_EnableUnlockButton);
|
||||
|
||||
char sNameParts[3][32];
|
||||
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
|
||||
|
||||
TDTowerId iTowerId = view_as<TDTowerId>(StringToInt(sNameParts[2]));
|
||||
Tower_OnButtonBuy(iTowerId, iCaller, iActivator);
|
||||
} else if (StrEqual(sName, "break_pregame")) {
|
||||
// Pregame button
|
||||
|
||||
if (hHintTimer == null) {
|
||||
hHintTimer = CreateTimer(60.0, Timer_Hints, _, TIMER_REPEAT);
|
||||
}
|
||||
} else if (StrContains(sName, "wave_start") != -1) {
|
||||
// Wave start
|
||||
|
||||
if (StrEqual(sName, "wave_start")) {
|
||||
Wave_OnButtonStart(g_iCurrentWave, iCaller, iActivator);
|
||||
} else {
|
||||
char sNameParts[3][32];
|
||||
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
|
||||
|
||||
Wave_OnButtonStart(StringToInt(sNameParts[2]), iCaller, iActivator);
|
||||
}
|
||||
} else if (StrContains(sName, "enable_sentry") != -1) {
|
||||
// Allow another sentry
|
||||
|
||||
if (!g_bCanGetUnlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_bCanGetUnlocks = false;
|
||||
CreateTimer(0.5, Timer_EnableUnlockButton);
|
||||
|
||||
g_iBuildingLimit[TDBuilding_Sentry] += 1;
|
||||
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "sentryLimitChanged", g_iBuildingLimit[TDBuilding_Sentry]);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "sentryLimitBuildInfo");
|
||||
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 Your sentry limit has been changed to:\x04 %i",g_iBuildingLimit[TDBuilding_Sentry]);
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can build additional sentries with the command \x04/s");
|
||||
|
||||
AcceptEntityInput(iCaller, "Break");
|
||||
} else if (StrContains(sName, "enable_dispenser") != -1) {
|
||||
// Enable dispenser
|
||||
|
||||
if (!g_bCanGetUnlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_bCanGetUnlocks = false;
|
||||
CreateTimer(0.5, Timer_EnableUnlockButton);
|
||||
|
||||
g_iBuildingLimit[TDBuilding_Dispenser] += 1;
|
||||
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "dispenserLimitChanged", g_iBuildingLimit[TDBuilding_Dispenser]);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "dispenserLimitBuildInfo");
|
||||
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 Your dispenser limit has been changed to:\x04 %i",g_iBuildingLimit[TDBuilding_Dispenser]);
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can build dispensers via your PDA");
|
||||
|
||||
AcceptEntityInput(iCaller, "Break");
|
||||
} else if (StrContains(sName, "bonus_metal") != -1) {
|
||||
// Metal bonus reward
|
||||
|
||||
if (!g_bCanGetUnlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_bCanGetUnlocks = false;
|
||||
CreateTimer(0.5, Timer_EnableUnlockButton);
|
||||
|
||||
SpawnMetalPacksNumber(TDMetalPack_Start, 4);
|
||||
|
||||
AcceptEntityInput(iCaller, "Break");
|
||||
} else if (StrContains(sName, "multiplier") != -1) {
|
||||
// Damage multipliers
|
||||
|
||||
if (!g_bCanGetUnlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_bCanGetUnlocks = false;
|
||||
CreateTimer(0.5, Timer_EnableUnlockButton);
|
||||
|
||||
for (int i = 1; i <= iMaxMultiplierTypes; i++) {
|
||||
char sKey[32], sMultiplier[32];
|
||||
Format(sKey, sizeof(sKey), "%d_type", i);
|
||||
|
||||
if (GetTrieString(g_hMultiplierType, sKey, sMultiplier, sizeof(sMultiplier))) {
|
||||
if (StrContains(sMultiplier, "crit") != -1 && StrContains(sName, "crit") != -1) {
|
||||
// Crit chance
|
||||
|
||||
//Check if already has 100% crits
|
||||
if(fMultiplier[i] >= 20.0) {
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "critChanceLimitReached");
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can't increase crit chance anymore.");
|
||||
return;
|
||||
}
|
||||
|
||||
int iPriceToPay = Multiplier_GetPrice(i) + Multiplier_GetIncrease(i) * RoundToZero(fMultiplier[i]);
|
||||
int iClients = GetRealClientCount(true);
|
||||
|
||||
if (iClients <= 0) {
|
||||
iClients = 1;
|
||||
}
|
||||
|
||||
if(CanAfford(iPriceToPay, false)) {
|
||||
|
||||
for (int iLoopClient = 1; iLoopClient <= MaxClients; iLoopClient++) {
|
||||
if (IsDefender(iLoopClient)) {
|
||||
AddClientMetal(iLoopClient, -iPriceToPay);
|
||||
}
|
||||
}
|
||||
fMultiplier[i] += 1.0;
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "critChanceSet", RoundToZero(fMultiplier[i] * 5.0));
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 Crit Chance set to:\x04 %i%", RoundToZero(fMultiplier[i] * 5.0));
|
||||
|
||||
if(fMultiplier[i] >= 20.0) {
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "critChanceLimitReached");
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 You can't increase crit chance anymore.");
|
||||
} else {
|
||||
int iNextPrice = iPriceToPay + Multiplier_GetIncrease(i);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "nextUpgradeCost", iNextPrice);
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 Next Upgrade will cost:\x04 %i\x03 metal per Player",iNextPrice);
|
||||
}
|
||||
}
|
||||
} else if (StrContains(sName, sMultiplier) != -1) {
|
||||
// Damage modifiers
|
||||
|
||||
int iPriceToPay = Multiplier_GetPrice(i) + Multiplier_GetIncrease(i) * RoundToZero(fMultiplier[i]);
|
||||
int iClients = GetRealClientCount(true);
|
||||
|
||||
if (iClients <= 0) {
|
||||
iClients = 1;
|
||||
}
|
||||
|
||||
iPriceToPay /= iClients;
|
||||
|
||||
if(CanAfford(iPriceToPay, false)) {
|
||||
|
||||
for (int iLoopClient = 1; iLoopClient <= MaxClients; iLoopClient++) {
|
||||
if (IsDefender(iLoopClient)) {
|
||||
AddClientMetal(iLoopClient, -iPriceToPay);
|
||||
}
|
||||
}
|
||||
fMultiplier[i] += 1.0;
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "dmgMultiplierSet", RoundToZero(fMultiplier[i] * 5.0));
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 Multiplier set to:\x04 %i.0",RoundToZero(fMultiplier[i] + 1.0));
|
||||
|
||||
int iNextPrice = iPriceToPay + Multiplier_GetIncrease(i);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "nextUpgradeCost", iNextPrice);
|
||||
//PrintToChatAll("\x04[\x03TD\x04]\x03 Next Upgrade will cost:\x04 %i\x03 metal per Player",iNextPrice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets multiplier base price
|
||||
*
|
||||
* @param iMultiplierId The multipliers id.
|
||||
* @return return 1000 on failure.
|
||||
*/
|
||||
|
||||
stock int Multiplier_GetPrice(int iMultiplierId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_price", iMultiplierId);
|
||||
|
||||
int iPrice = 0;
|
||||
if (!GetTrieValue(g_hMultiplier, sKey, iPrice)) {
|
||||
return 1000;
|
||||
}
|
||||
return iPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets multiplier increase
|
||||
*
|
||||
* @param iMultiplierId The multipliers id.
|
||||
* @return return 1000 on failure.
|
||||
*/
|
||||
|
||||
stock int Multiplier_GetIncrease(int iMultiplierId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_increase", iMultiplierId);
|
||||
|
||||
int iIncrease = 0;
|
||||
if (!GetTrieValue(g_hMultiplier, sKey, iIncrease)) {
|
||||
return 1000;
|
||||
}
|
||||
return iIncrease;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets multiplier amount
|
||||
*
|
||||
* @param sDamageType The damage type.
|
||||
* @return return 1 on failure.
|
||||
*/
|
||||
|
||||
stock int Multiplier_GetInt(const char[] sDamageType) {
|
||||
char sKey[32], sMultiplier[32];
|
||||
|
||||
for (int i = 0; i <= iMaxMultiplierTypes; i++) {
|
||||
Format(sKey, sizeof(sKey), "%d_type", i);
|
||||
if (GetTrieString(g_hMultiplierType, sKey, sMultiplier, sizeof(sMultiplier))) {
|
||||
if (StrContains(sMultiplier, sDamageType) != -1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all clients have enough metal to pay a price.
|
||||
*
|
||||
* @param iPrice The price to pay.
|
||||
* @return True if affordable, false otherwise.
|
||||
*/
|
||||
|
||||
stock bool CanAfford(int iPrice, bool silent) {
|
||||
bool bResult = true;
|
||||
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
if (GetClientMetal(iClient) < iPrice) {
|
||||
if (!silent) {
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "towerInsufficientMetal", GetClientNameShort(iClient), iPrice - GetClientMetal(iClient));
|
||||
}
|
||||
bResult = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
393
scripting/towerdefense/handler/corners.sp
Normal file
393
scripting/towerdefense/handler/corners.sp
Normal file
@@ -0,0 +1,393 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks if an entity is a corner.
|
||||
*
|
||||
* @param iCorner The corner.
|
||||
* @return True if valid, false otherwise.
|
||||
*/
|
||||
|
||||
stock bool Corner_IsValid(int iCorner) {
|
||||
if (!IsValidEntity(iCorner)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char sBuffer[64];
|
||||
GetEntityClassname(iCorner, sBuffer, sizeof(sBuffer));
|
||||
|
||||
if (StrEqual(sBuffer, "trigger_multiple")) {
|
||||
GetEntPropString(iCorner, Prop_Data, "m_iName", sBuffer, sizeof(sBuffer));
|
||||
|
||||
return (StrContains(sBuffer, "corner_") != -1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a corner by name.
|
||||
*
|
||||
* @param sName The corners name.
|
||||
* @return The corners entity index, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Corner_FindByName(const char[] sName) {
|
||||
int iCorner = -1;
|
||||
char sCornerName[64];
|
||||
|
||||
while ((iCorner = FindEntityByClassname(iCorner, "trigger_multiple")) != -1) {
|
||||
Corner_GetName(iCorner, sCornerName, sizeof(sCornerName));
|
||||
|
||||
if (StrEqual(sCornerName, sName)) {
|
||||
return iCorner;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a corner by it's next corners name.
|
||||
*
|
||||
* @param sName The next corners name.
|
||||
* @return The corners entity index, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Corner_FindByNextName(const char[] sName) {
|
||||
int iCorner = -1;
|
||||
char sCornerName[64];
|
||||
|
||||
while ((iCorner = FindEntityByClassname(iCorner, "trigger_multiple")) != -1) {
|
||||
Corner_GetNextName(iCorner, sCornerName, sizeof(sCornerName));
|
||||
|
||||
if (StrEqual(sCornerName, sName)) {
|
||||
return iCorner;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a corners name.
|
||||
*
|
||||
* @param iCorner The corner.
|
||||
* @param bValidate Validate the corner.
|
||||
* @return The corner number, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Corner_GetNumber(int iCorner, bool bValidate = true) {
|
||||
if (!bValidate) {
|
||||
char sName[64];
|
||||
Corner_GetName(iCorner, sName, sizeof(sName), false);
|
||||
|
||||
if (!StrEqual(sName, "corner_final")) {
|
||||
char sNameParts[2][32];
|
||||
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
|
||||
|
||||
return StringToInt(sNameParts[1]);
|
||||
}
|
||||
} else if (Corner_IsValid(iCorner)) {
|
||||
char sName[64];
|
||||
Corner_GetName(iCorner, sName, sizeof(sName), false);
|
||||
|
||||
if (!StrEqual(sName, "corner_final")) {
|
||||
char sNameParts[2][32];
|
||||
ExplodeString(sName, "_", sNameParts, sizeof(sNameParts), sizeof(sNameParts[]));
|
||||
|
||||
return StringToInt(sNameParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a corners name.
|
||||
*
|
||||
* @param iCorner The corner.
|
||||
* @param sBuffer The destination string buffer.
|
||||
* @param iMaxLength The maximum length of the output string buffer.
|
||||
* @param bValidate Validate the corner.
|
||||
* @return True on success, false if corner was not found.
|
||||
*/
|
||||
|
||||
stock bool Corner_GetName(int iCorner, char[] sBuffer, int iMaxLength, bool bValidate = true) {
|
||||
if (!bValidate) {
|
||||
GetEntPropString(iCorner, Prop_Data, "m_iName", sBuffer, iMaxLength);
|
||||
return true;
|
||||
} else if (Corner_IsValid(iCorner)) {
|
||||
GetEntPropString(iCorner, Prop_Data, "m_iName", sBuffer, iMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a corners next corner name.
|
||||
*
|
||||
* @param iCorner The corner.
|
||||
* @param sBuffer The destination string buffer.
|
||||
* @param iMaxLength The maximum length of the output string buffer.
|
||||
* @param bValidate Validate the corner.
|
||||
* @return True on success, false if corner was not found.
|
||||
*/
|
||||
|
||||
stock bool Corner_GetNextName(int iCorner, char[] sBuffer, int iMaxLength, bool bValidate = true) {
|
||||
if (!bValidate) {
|
||||
GetEntPropString(iCorner, Prop_Data, "m_iParent", sBuffer, iMaxLength);
|
||||
return true;
|
||||
} else if (Corner_IsValid(iCorner)) {
|
||||
GetEntPropString(iCorner, Prop_Data, "m_iParent", sBuffer, iMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a corners location.
|
||||
*
|
||||
* @param iCorner The corner.
|
||||
* @param fLocation The location vector.
|
||||
* @param bValidate Validate the corner.
|
||||
* @return True on success, false if corner was not found.
|
||||
*/
|
||||
|
||||
stock bool Corner_GetLocation(int iCorner, float fLocation[3], bool bValidate = true) {
|
||||
if (!bValidate) {
|
||||
GetEntPropVector(iCorner, Prop_Data, "m_vecAbsOrigin", fLocation);
|
||||
return true;
|
||||
} else if (Corner_IsValid(iCorner)) {
|
||||
GetEntPropVector(iCorner, Prop_Data, "m_vecAbsOrigin", fLocation);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the angle between two corners.
|
||||
*
|
||||
* @param iCornerA The first corner.
|
||||
* @param iCornerB The second corner.
|
||||
* @param fAngles The angles vector.
|
||||
* @param bValidate Validate the corner.
|
||||
* @return True on success, false if corner was not found.
|
||||
*/
|
||||
|
||||
stock bool Corner_GetAngles(int iCornerA, int iCornerB, float fAngles[3], bool bValidate = true) {
|
||||
float fLocationCornerA[3], fLocationCornerB[3], fVector[3];
|
||||
|
||||
if (!bValidate) {
|
||||
Corner_GetLocation(iCornerA, fLocationCornerA, false);
|
||||
Corner_GetLocation(iCornerB, fLocationCornerB, false);
|
||||
|
||||
MakeVectorFromPoints(fLocationCornerA, fLocationCornerB, fVector);
|
||||
GetVectorAngles(fVector, fAngles);
|
||||
} else if (Corner_IsValid(iCornerA) && Corner_IsValid(iCornerB)) {
|
||||
Corner_GetLocation(iCornerA, fLocationCornerA, false);
|
||||
Corner_GetLocation(iCornerB, fLocationCornerB, false);
|
||||
|
||||
MakeVectorFromPoints(fLocationCornerA, fLocationCornerB, fVector);
|
||||
GetVectorAngles(fVector, fAngles);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next corner.
|
||||
*
|
||||
* @param iCorner The current corner.
|
||||
* @param sName The current corners name (optional).
|
||||
* @return The next corners entity index, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Corner_GetNext(int iCorner = -1, const char[] sName = "") {
|
||||
char sCornerName[64];
|
||||
|
||||
if (StrEqual(sName, "") && Corner_IsValid(iCorner)) {
|
||||
Corner_GetName(iCorner, sCornerName, sizeof(sCornerName));
|
||||
} else if (!StrEqual(sName, "") && !Corner_IsValid(iCorner)) {
|
||||
strcopy(sCornerName, sizeof(sCornerName), sName);
|
||||
iCorner = Corner_FindByName(sCornerName);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (StrContains(sCornerName, "corner_") != -1 && !StrEqual(sCornerName, "corner_final")) {
|
||||
char sNextName[64];
|
||||
Corner_GetNextName(iCorner, sNextName, sizeof(sNextName));
|
||||
|
||||
return Corner_FindByName(sNextName);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the previous corner.
|
||||
*
|
||||
* @param iCorner The current corner.
|
||||
* @param sName The current corners name (optional).
|
||||
* @return The previous corners entity index, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Corner_GetPrevious(int iCorner = -1, const char[] sName = "") {
|
||||
char sCornerName[64];
|
||||
|
||||
if (StrEqual(sName, "") && Corner_IsValid(iCorner)) {
|
||||
Corner_GetName(iCorner, sCornerName, sizeof(sCornerName));
|
||||
} else if (!StrEqual(sName, "") && !Corner_IsValid(iCorner)) {
|
||||
strcopy(sCornerName, sizeof(sCornerName), sName);
|
||||
iCorner = Corner_FindByName(sCornerName);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (StrContains(sCornerName, "corner_") != -1) {
|
||||
return Corner_FindByNextName(sCornerName);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest corner to a client.
|
||||
*
|
||||
* @param iClient The client.
|
||||
* @return The corners entity index, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Corner_GetNearest(int iClient) {
|
||||
if (!IsValidClient(iClient) || !IsClientInGame(iClient) || !IsPlayerAlive(iClient)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
float fCornerLocation[3], fClientLocation[3];
|
||||
GetClientAbsOrigin(iClient, fClientLocation);
|
||||
|
||||
int iCorner = -1, iNearestCorner = -1;
|
||||
float fNearestDistance = 2147483647.0, fDistance;
|
||||
|
||||
while ((iCorner = FindEntityByClassname(iCorner, "trigger_multiple")) != -1) {
|
||||
if (Corner_IsValid(iCorner)) {
|
||||
Corner_GetLocation(iCorner, fCornerLocation, false);
|
||||
|
||||
fDistance = GetVectorDistance(fCornerLocation, fClientLocation);
|
||||
|
||||
if (fDistance < fNearestDistance) {
|
||||
iNearestCorner = iCorner;
|
||||
fNearestDistance = fDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iNearestCorner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the two corners a client is in between.
|
||||
*
|
||||
* @param iClient The client.
|
||||
* @param iCornerA The first corner.
|
||||
* @param iCornerB The second corner.
|
||||
* @param fEpsilon The possible deviation.
|
||||
* @return False if not between any corners, true otherwise.
|
||||
*/
|
||||
|
||||
stock bool Corner_GetBetween(int iClient, int &iCornerA, int &iCornerB, float fEpsilon = 15.0) {
|
||||
if (!IsValidClient(iClient) || !IsClientInGame(iClient) || !IsPlayerAlive(iClient)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int iNearBefore[MAXPLAYERS + 1];
|
||||
|
||||
int iNear = Corner_GetNearest(iClient);
|
||||
|
||||
float fLocationNear[3], fLocationClient[3];
|
||||
Corner_GetLocation(iNear, fLocationNear, false);
|
||||
GetClientAbsOrigin(iClient, fLocationClient);
|
||||
|
||||
float fVector[3], fAngles[3], fAnglesClient[3];
|
||||
MakeVectorFromPoints(fLocationNear, fLocationClient, fVector);
|
||||
GetVectorAngles(fVector, fAngles);
|
||||
GetClientEyeAngles(iClient, fAnglesClient);
|
||||
|
||||
float fAnglesDiff = FloatAbs(fAngles[1] - fAnglesClient[1]);
|
||||
|
||||
if (fAnglesDiff != 0.0 && fAnglesDiff != 45.0 && fAnglesDiff != 90.0 && fAnglesDiff != 135.0 && fAnglesDiff != 180.0 &&
|
||||
fAnglesDiff != 225.0 && fAnglesDiff != 270.0 && fAnglesDiff != 315.0 && fAnglesDiff != 360.0 && fAnglesDiff > 5.0) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int iNumberNearBefore = Corner_GetNumber(iNearBefore[iClient]);
|
||||
|
||||
if (iNumberNearBefore != -1 && IsValidEntity(iNear)) {
|
||||
int iNumberNear = Corner_GetNumber(iNear);
|
||||
|
||||
if (iNumberNear != -1) {
|
||||
if (Abs(iNumberNearBefore - iNumberNear) > 1) {
|
||||
iNearBefore[iClient] = iNear;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iNext = Corner_GetNext(iNear);
|
||||
|
||||
float fLocationNext[3];
|
||||
Corner_GetLocation(iNext, fLocationNext);
|
||||
|
||||
float fResultA[3], fResultB[3];
|
||||
SubtractVectors(fLocationNear, fLocationNext, fResultA);
|
||||
SubtractVectors(fLocationClient, fLocationNext, fResultB);
|
||||
|
||||
float fScale = GetVectorDotProduct(fResultA, fResultB) / GetVectorDotProduct(fResultA, fResultA);
|
||||
ScaleVector(fResultA, fScale);
|
||||
|
||||
float fResult[3];
|
||||
SubtractVectors(fResultB, fResultA, fResult);
|
||||
|
||||
if (FloatAbs(fResult[0]) <= fEpsilon && FloatAbs(fResult[1]) <= fEpsilon) {
|
||||
iCornerA = iNear;
|
||||
iCornerB = iNext;
|
||||
|
||||
iNearBefore[iClient] = iNear;
|
||||
return true;
|
||||
}
|
||||
|
||||
int iPrev = Corner_GetPrevious(iNear);
|
||||
|
||||
float fLocationPrev[3];
|
||||
Corner_GetLocation(iPrev, fLocationPrev);
|
||||
|
||||
SubtractVectors(fLocationNear, fLocationPrev, fResultA);
|
||||
SubtractVectors(fLocationClient, fLocationPrev, fResultB);
|
||||
|
||||
fScale = GetVectorDotProduct(fResultA, fResultB) / GetVectorDotProduct(fResultA, fResultA);
|
||||
ScaleVector(fResultA, fScale);
|
||||
|
||||
SubtractVectors(fResultB, fResultA, fResult);
|
||||
|
||||
if (FloatAbs(fResult[0]) <= fEpsilon && FloatAbs(fResult[1]) <= fEpsilon) {
|
||||
iCornerA = iPrev;
|
||||
iCornerB = iNear;
|
||||
|
||||
iNearBefore[iClient] = iNear;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
179
scripting/towerdefense/handler/metalpacks.sp
Normal file
179
scripting/towerdefense/handler/metalpacks.sp
Normal file
@@ -0,0 +1,179 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Spawns all metalpacks (except the boss and reward metalpack).
|
||||
*
|
||||
* @param iMetalPackType The metal pack type.
|
||||
* @return True on success, false otherwiseherwise.
|
||||
*/
|
||||
|
||||
stock bool SpawnMetalPacks(TDMetalPackType iMetalPackType) {
|
||||
int iNumPacks = 0;
|
||||
if (!GetTrieValue(g_hMapMetalpacks, "quantity", iNumPacks)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iNumPacks <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int iMetal = 0, iEntity;
|
||||
float fLocation[3];
|
||||
char sKey[32], sLocation[64], sLocationParts[6][16];
|
||||
|
||||
for (int iMetalPackId = 0; iMetalPackId < iNumPacks; iMetalPackId++) {
|
||||
if (Metalpack_GetType(iMetalPackId) != iMetalPackType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Format(sKey, sizeof(sKey), "%d_metal", iMetalPackId);
|
||||
|
||||
if (!GetTrieValue(g_hMapMetalpacks, sKey, iMetal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Format(sKey, sizeof(sKey), "%d_location", iMetalPackId);
|
||||
|
||||
if (!GetTrieString(g_hMapMetalpacks, sKey, sLocation, sizeof(sLocation))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
|
||||
|
||||
fLocation[0] = StringToFloat(sLocationParts[0]);
|
||||
fLocation[1] = StringToFloat(sLocationParts[1]);
|
||||
fLocation[2] = StringToFloat(sLocationParts[2]);
|
||||
|
||||
SpawnMetalPack2(TDMetalPack_Large, fLocation, iMetal, iEntity);
|
||||
|
||||
if (Metalpack_GetType(iMetalPackId) == TDMetalPack_Boss) {
|
||||
ShowAnnotation(EntRefToEntIndex(iEntity), fLocation, 64.0, 60.0, "A reward spawned!");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns certain amount of metalpacks.
|
||||
*
|
||||
* @param iMetalPackType The metal pack type.
|
||||
* @param iNumPacks Amount of metalpacks
|
||||
* @return True on success, false otherwiseherwise.
|
||||
*/
|
||||
|
||||
stock void SpawnMetalPacksNumber(TDMetalPackType iMetalPackType, int iNumPacks) {
|
||||
|
||||
int iMetal = 0, iEntity;
|
||||
float fLocation[3];
|
||||
char sKey[32], sLocation[64], sLocationParts[6][16];
|
||||
|
||||
for (int iMetalPackId = 0; iMetalPackId < iNumPacks; iMetalPackId++) {
|
||||
if (Metalpack_GetType(iMetalPackId) != iMetalPackType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Format(sKey, sizeof(sKey), "%d_metal", iMetalPackId);
|
||||
|
||||
if (!GetTrieValue(g_hMapMetalpacks, sKey, iMetal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Format(sKey, sizeof(sKey), "%d_location", iMetalPackId);
|
||||
|
||||
if (!GetTrieString(g_hMapMetalpacks, sKey, sLocation, sizeof(sLocation))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
|
||||
|
||||
fLocation[0] = StringToFloat(sLocationParts[0]);
|
||||
fLocation[1] = StringToFloat(sLocationParts[1]);
|
||||
fLocation[2] = StringToFloat(sLocationParts[2]);
|
||||
|
||||
SpawnMetalPack2(TDMetalPack_Large, fLocation, iMetal, iEntity);
|
||||
ShowAnnotation(EntRefToEntIndex(iEntity), fLocation, 64.0, 5.0, "A Metalpack spawned!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns reward metal pack.
|
||||
*
|
||||
* @param iMetalPackType The type of metal pack.
|
||||
* @param fLocation The location to place the metal pack.
|
||||
* @param iMetal The ammount of metal to spawn.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void SpawnRewardPack(TDMetalPackSpawnType iMetalPackType, float fLocation[3], int iMetal) {
|
||||
int iEntity;
|
||||
SpawnMetalPack2(iMetalPackType, fLocation, iMetal, iEntity);
|
||||
|
||||
// Dirty hack to add outlines to the ammo packs
|
||||
int PackRef = EntRefToEntIndex(iEntity);
|
||||
int dispenser = CreateEntityByName("obj_dispenser");
|
||||
|
||||
DispatchKeyValue(dispenser, "spawnflags", "2");
|
||||
DispatchKeyValue(dispenser, "solid", "0");
|
||||
DispatchKeyValue(dispenser, "teamnum", "3");
|
||||
|
||||
SetEntProp(dispenser, Prop_Send, "m_usSolidFlags", 12); // FSOLID_TRIGGER|FSOLID_NOT_SOLID
|
||||
SetEntProp(dispenser, Prop_Data, "m_nSolidType", 6); // SOLID_VPHYSICS
|
||||
SetEntProp(dispenser, Prop_Data, "m_CollisionGroup", 1); //COLLISION_GROUP_DEBRIS
|
||||
|
||||
char model[PLATFORM_MAX_PATH];
|
||||
float pos[3], ang[3];
|
||||
|
||||
GetEntPropString(PackRef, Prop_Data, "m_ModelName", model, sizeof(model));
|
||||
GetEntPropVector(PackRef, Prop_Send, "m_vecOrigin", pos);
|
||||
GetEntPropVector(PackRef, Prop_Send, "m_angRotation", ang);
|
||||
SetEntProp(dispenser, Prop_Send, "m_bGlowEnabled", 1);
|
||||
|
||||
TeleportEntity(dispenser, pos, ang, NULL_VECTOR);
|
||||
DispatchSpawn(dispenser);
|
||||
SetEntityModel(dispenser, model);
|
||||
SetVariantString("!activator");
|
||||
AcceptEntityInput(dispenser, "SetParent", PackRef);
|
||||
|
||||
SDKHook(dispenser, SDKHook_OnTakeDamage, OnTakeDamage_Dispenser);
|
||||
}
|
||||
|
||||
public Action OnTakeDamage_Dispenser(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3])
|
||||
{
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
/*======================================
|
||||
= Data Functions =
|
||||
======================================*/
|
||||
|
||||
/**
|
||||
* Gets a metalpacks type.
|
||||
*
|
||||
* @param iMetalpackId The metalpacks id.
|
||||
* @return A TDMetalPackType value.
|
||||
*/
|
||||
|
||||
stock TDMetalPackType Metalpack_GetType(int iMetalpackId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_type", iMetalpackId);
|
||||
|
||||
char sType[64];
|
||||
GetTrieString(g_hMapMetalpacks, sKey, sType, sizeof(sType));
|
||||
|
||||
if (StrEqual(sType, "start")) {
|
||||
return TDMetalPack_Start;
|
||||
} else if (StrEqual(sType, "boss")) {
|
||||
return TDMetalPack_Boss;
|
||||
}
|
||||
|
||||
return TDMetalPack_Invalid;
|
||||
}
|
36
scripting/towerdefense/handler/panels.sp
Normal file
36
scripting/towerdefense/handler/panels.sp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Removes a panel.
|
||||
*
|
||||
* @param iWave The panels wave.
|
||||
* @return True on success, false otherwise.
|
||||
*/
|
||||
|
||||
stock bool Panel_Remove(int iWave) {
|
||||
int iEntity = -1;
|
||||
bool bResult = false;
|
||||
char sName[64], sPanelName[64], sPanelTextName[64];
|
||||
|
||||
Format(sPanelName, sizeof(sPanelName), "wavePanel%d", iWave); // TODO: Rename to panel_%d
|
||||
Format(sPanelTextName, sizeof(sPanelTextName), "wavePanelText%d", iWave); // TODO: Rename to panel_text_%d
|
||||
|
||||
while ((iEntity = FindEntityByClassname(iEntity, "func_movelinear")) != -1) {
|
||||
GetEntPropString(iEntity, Prop_Data, "m_iName", sName, sizeof(sName));
|
||||
|
||||
if (StrEqual(sName, sPanelName) || StrEqual(sName, sPanelTextName)) {
|
||||
AcceptEntityInput(iEntity, "Kill");
|
||||
bResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
428
scripting/towerdefense/handler/player.sp
Normal file
428
scripting/towerdefense/handler/player.sp
Normal file
@@ -0,0 +1,428 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Called multiple times during server initialization.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_ServerInitializing(int iUserId, int iClient) {
|
||||
if (!TF2_IsPlayerInCondition(iClient, TFCond_RestrictToMelee)) {
|
||||
TF2_AddCondition(iClient, TFCond_RestrictToMelee);
|
||||
SetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon", GetPlayerWeaponSlot(iClient, TFWeaponSlot_Melee));
|
||||
}
|
||||
|
||||
SetEntityMoveType(iClient, MOVETYPE_NONE);
|
||||
|
||||
PrintToHud(iClient, "INITIALIZING GAME, PLEASE WAIT A MOMENT ...");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the server is initialized.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_ServerInitialized(int iUserId, int iClient) {
|
||||
char sCommunityId[32];
|
||||
if (IsValidClient(iClient)) {
|
||||
Player_UGetString(iUserId, PLAYER_COMMUNITY_ID, sCommunityId, sizeof(sCommunityId));
|
||||
|
||||
TF2_RemoveCondition(iClient, TFCond_RestrictToMelee);
|
||||
SetEntityMoveType(iClient, MOVETYPE_WALK);
|
||||
|
||||
Player_SyncDatabase(iUserId, iClient, sCommunityId);
|
||||
|
||||
Log(TDLogLevel_Debug, "Successfully initialized player %N (%s)", iClient, sCommunityId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs all initial things of a client with the database.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @param sCommunityId The clients 64-bit steam id (community id).
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_SyncDatabase(int iUserId, int iClient, const char[] sCommunityId) {
|
||||
Database_CheckPlayer(iUserId, iClient, sCommunityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the client is connected.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @param sName The clients name.
|
||||
* @param sCommunityId The clients 32-bit steam id.
|
||||
* @param sCommunityId The clients 64-bit steam id (community id).
|
||||
* @param sIp The clients network address (ip).
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_Connected(int iUserId, int iClient, const char[] sName, const char[] sSteamId, const char[] sCommunityId, const char[] sIp) {
|
||||
Log(TDLogLevel_Debug, "Player connected (UserId=%d, Client=%d, Name=%s, SteamId=%s, CommunityId=%s, Address=%s)", iUserId, iClient, sName, sSteamId, sCommunityId, sIp);
|
||||
|
||||
if (!StrEqual(sSteamId, "BOT")) {
|
||||
if (GetRealClientCount() > g_iMaxClients) {
|
||||
KickClient(iClient, "Maximum number of players has been reached (%d/%d)", GetRealClientCount() - 1, g_iMaxClients);
|
||||
Log(TDLogLevel_Info, "Kicked player (%N, %s) (Maximum players reached: %d/%d)", iClient, sSteamId, GetRealClientCount() - 1, g_iMaxClients);
|
||||
return;
|
||||
}
|
||||
|
||||
Log(TDLogLevel_Info, "Connected clients: %d/%d", GetRealClientCount(), g_iMaxClients);
|
||||
|
||||
Player_USetString(iUserId, PLAYER_STEAM_ID, sSteamId);
|
||||
Player_USetString(iUserId, PLAYER_COMMUNITY_ID, sCommunityId);
|
||||
Player_USetString(iUserId, PLAYER_IP_ADDRESS, sIp);
|
||||
Server_UAddValue(g_iServerId, SERVER_CONNECTIONS, 1);
|
||||
|
||||
g_bCarryingObject[iClient] = false;
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = false;
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = false;
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Melee] = false;
|
||||
|
||||
g_iAttachedTower[iClient] = 0;
|
||||
|
||||
ChangeClientTeam(iClient, TEAM_DEFENDER);
|
||||
TF2_SetPlayerClass(iClient, TFClass_Engineer, false, true);
|
||||
|
||||
UpdateGameDescription();
|
||||
|
||||
Log(TDLogLevel_Debug, "Moved player %N to the Defenders team as Engineer", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before the client disconnects.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_OnDisconnectPre(int iUserId, int iClient) {
|
||||
Database_UpdatePlayerDisconnect(iUserId);
|
||||
int iTime = GetTime() - g_iTime;
|
||||
Player_CSetValue(iClient, PLAYER_PLAYTIME, iTime);
|
||||
Server_UAddValue(g_iServerId, SERVER_PLAYTIME, iTime);
|
||||
|
||||
if (GetRealClientCount(true) <= 1) { // the disconnected player is counted (thus 1 not 0)
|
||||
Database_ServerStatsUpdate();
|
||||
CreateTimer(10.0, Timer_Reset); // Give Queries time to send
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the client has entered the game (connected and loaded).
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_Loaded(int iUserId, int iClient) {
|
||||
char sCommunityId[32];
|
||||
Player_CGetString(iClient, PLAYER_COMMUNITY_ID, sCommunityId, sizeof(sCommunityId));
|
||||
|
||||
Log(TDLogLevel_Debug, "Player loaded (UserId=%d, Client=%d, CommunityId=%s)", iUserId, iClient, sCommunityId);
|
||||
|
||||
if (IsValidClient(iClient) && !IsFakeClient(iClient) && g_bTowerDefenseMap) {
|
||||
CreateTimer(1.0, InitInfoTimer, iUserId, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
public Action InitInfoTimer(Handle hTimer, any iUserId) {
|
||||
if (g_bServerInitialized) {
|
||||
Player_ServerInitialized(iUserId, GetClientOfUserId(iUserId));
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
Player_ServerInitializing(iUserId, GetClientOfUserId(iUserId));
|
||||
|
||||
CreateTimer(1.0, InitInfoTimer, iUserId, TIMER_FLAG_NO_MAPCHANGE);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a client spawned.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_OnSpawn(int iUserId, int iClient) {
|
||||
ResetClientMetal(iClient);
|
||||
SetEntProp(iClient, Prop_Data, "m_bloodColor", view_as<int>(TDBlood_None));
|
||||
|
||||
if (g_bReplaceWeapon[iClient][TFWeaponSlot_Primary]) {
|
||||
TF2Items_GiveWeapon(iClient, 9, TFWeaponSlot_Primary, 5, 1, true, "tf_weapon_shotgun_primary", "");
|
||||
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = false;
|
||||
}
|
||||
|
||||
if (g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary]) {
|
||||
TF2Items_GiveWeapon(iClient, 22, TFWeaponSlot_Secondary, 5, 1, true, "tf_weapon_pistol", "");
|
||||
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a client dies.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_OnDeath(int iUserId, int iClient) {
|
||||
g_bCarryingObject[iClient] = false;
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Primary] = false;
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Secondary] = false;
|
||||
g_bReplaceWeapon[iClient][TFWeaponSlot_Melee] = false;
|
||||
Player_CAddValue(iClient, PLAYER_DEATHS, 1);
|
||||
|
||||
if (IsDefender(iClient) && g_iCurrentWave > 0) {
|
||||
int iMetal = GetClientMetal(iClient) / 2;
|
||||
|
||||
if (iMetal > 0) {
|
||||
float fLocation[3];
|
||||
|
||||
GetClientEyePosition(iClient, fLocation);
|
||||
fLocation[2] = fLocation[2] - GetDistanceToGround(fLocation) + 10.0;
|
||||
|
||||
SpawnMetalPack(TDMetalPack_Medium, fLocation, iMetal);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsTower(g_iAttachedTower[iClient])) {
|
||||
Tower_OnCarrierDeath(g_iAttachedTower[iClient], iClient);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a client data was set.
|
||||
*
|
||||
* @param iUserId The user id on server (unique on server).
|
||||
* @param iClient The client.
|
||||
* @param sKey The set key.
|
||||
* @param iDataType The datatype of the set data.
|
||||
* @param iValue The value if the set data is an integer, -1 otherwise.
|
||||
* @param bValue The value if the set data is a boolean, false otherwise.
|
||||
* @param fValue The value if the set data is a float, -1.0 otherwise.
|
||||
* @param sValue The value if the set data is a string, empty string ("") otherwise.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Player_OnDataSet(int iUserId, int iClient, const char[] sKey, TDDataType iDataType, int iValue, int bValue, float fValue, const char[] sValue) {
|
||||
switch (iDataType) {
|
||||
case TDDataType_Integer: {
|
||||
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_Integer, iValue=%d", iUserId, iClient, sKey, iValue);
|
||||
}
|
||||
|
||||
case TDDataType_Boolean: {
|
||||
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_Boolean, bValue=%s", iUserId, iClient, sKey, (bValue ? "true" : "false"));
|
||||
}
|
||||
|
||||
case TDDataType_Float: {
|
||||
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_Float, fValue=%f", iUserId, iClient, sKey, fValue);
|
||||
}
|
||||
|
||||
case TDDataType_String: {
|
||||
Log(TDLogLevel_Trace, "Player_OnDataSet: iUserId=%d, iClient=%d, sKey=%s, iDataType=TDDataType_String, sValue=%s", iUserId, iClient, sKey, sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stock bool CheckClientForUserId(int iClient) {
|
||||
return (iClient > 0 && iClient <= MaxClients && IsClientConnected(iClient));
|
||||
}
|
||||
|
||||
stock void Player_UAddValue(int iUserId, const char[] sKey, int iValue) {
|
||||
char sUserIdKey[128];
|
||||
int iOldValue;
|
||||
Player_UGetValue(iUserId, sKey, iOldValue);
|
||||
if (iOldValue != -1)
|
||||
iValue = iValue + iOldValue;
|
||||
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, iValue, false, -1.0, "");
|
||||
|
||||
SetTrieValue(g_hPlayerData, sUserIdKey, iValue);
|
||||
}
|
||||
|
||||
stock void Player_CAddValue(int iClient, const char[] sKey, int iValue) {
|
||||
if (CheckClientForUserId(iClient)) {
|
||||
Player_UAddValue(GetClientUserId(iClient), sKey, iValue);
|
||||
}
|
||||
}
|
||||
|
||||
stock void Player_USetValue(int iUserId, const char[] sKey, int iValue) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, iValue, false, -1.0, "");
|
||||
|
||||
SetTrieValue(g_hPlayerData, sUserIdKey, iValue);
|
||||
}
|
||||
|
||||
stock void Player_CSetValue(int iClient, const char[] sKey, int iValue) {
|
||||
if (CheckClientForUserId(iClient)) {
|
||||
Player_USetValue(GetClientUserId(iClient), sKey, iValue);
|
||||
}
|
||||
}
|
||||
|
||||
stock bool Player_UGetValue(int iUserId, const char[] sKey, int &iValue) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Player_UGetValue: iUserId=%d, sKey=%s", iUserId, sKey);
|
||||
|
||||
if (!GetTrieValue(g_hPlayerData, sUserIdKey, iValue)) {
|
||||
iValue = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
stock bool Player_CGetValue(int iClient, const char[] sKey, int &iValue) {
|
||||
return CheckClientForUserId(iClient) && Player_UGetValue(GetClientUserId(iClient), sKey, iValue);
|
||||
}
|
||||
|
||||
stock void Player_USetBool(int iUserId, const char[] sKey, bool bValue) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, -1, bValue, -1.0, "");
|
||||
|
||||
SetTrieValue(g_hPlayerData, sUserIdKey, (bValue ? 1 : 0));
|
||||
}
|
||||
|
||||
stock void Player_CSetBool(int iClient, const char[] sKey, bool bValue) {
|
||||
if (CheckClientForUserId(iClient)) {
|
||||
Player_USetBool(GetClientUserId(iClient), sKey, bValue);
|
||||
}
|
||||
}
|
||||
|
||||
stock bool Player_UGetBool(int iUserId, const char[] sKey) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Player_UGetBool: iUserId=%d, sKey=%s", iUserId, sKey);
|
||||
|
||||
int iValue = 0;
|
||||
GetTrieValue(g_hPlayerData, sUserIdKey, iValue);
|
||||
|
||||
return (iValue != 0);
|
||||
}
|
||||
|
||||
stock bool Player_CGetBool(int iClient, const char[] sKey) {
|
||||
return CheckClientForUserId(iClient) && Player_UGetBool(GetClientUserId(iClient), sKey);
|
||||
}
|
||||
|
||||
stock void Player_USetFloat(int iUserId, const char[] sKey, float fValue) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
char sValue[64];
|
||||
FloatToString(fValue, sValue, sizeof(sValue));
|
||||
|
||||
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_Integer, -1, false, fValue, "");
|
||||
|
||||
SetTrieString(g_hPlayerData, sUserIdKey, sValue);
|
||||
}
|
||||
|
||||
stock void Player_CSetFloat(int iClient, const char[] sKey, float fValue) {
|
||||
if (CheckClientForUserId(iClient)) {
|
||||
Player_USetFloat(GetClientUserId(iClient), sKey, fValue);
|
||||
}
|
||||
}
|
||||
|
||||
stock bool Player_UGetFloat(int iUserId, const char[] sKey, float &fValue) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Player_UGetFloat: iUserId=%d, sKey=%s", iUserId, sKey);
|
||||
|
||||
char sValue[64];
|
||||
if (!GetTrieString(g_hPlayerData, sUserIdKey, sValue, sizeof(sValue))) {
|
||||
fValue = -1.0;
|
||||
return false;
|
||||
}
|
||||
|
||||
fValue = StringToFloat(sValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
stock bool Player_CGetFloat(int iClient, const char[] sKey, float &fValue) {
|
||||
return CheckClientForUserId(iClient) && Player_UGetFloat(GetClientUserId(iClient), sKey, fValue);
|
||||
}
|
||||
|
||||
stock void Player_USetString(int iUserId, const char[] sKey, const char[] sValue, any...) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
char sFormattedValue[256];
|
||||
VFormat(sFormattedValue, sizeof(sFormattedValue), sValue, 4);
|
||||
|
||||
Player_OnDataSet(iUserId, GetClientOfUserId(iUserId), sKey, TDDataType_String, -1, false, -1.0, sValue);
|
||||
|
||||
SetTrieString(g_hPlayerData, sUserIdKey, sFormattedValue);
|
||||
}
|
||||
|
||||
stock void Player_CSetString(int iClient, const char[] sKey, const char[] sValue, any...) {
|
||||
if (CheckClientForUserId(iClient)) {
|
||||
char sFormattedValue[256];
|
||||
VFormat(sFormattedValue, sizeof(sFormattedValue), sValue, 4);
|
||||
|
||||
Player_USetString(GetClientUserId(iClient), sKey, sFormattedValue);
|
||||
}
|
||||
}
|
||||
|
||||
stock bool Player_UGetString(int iUserId, const char[] sKey, char[] sValue, int iMaxLength) {
|
||||
char sUserIdKey[128];
|
||||
Format(sUserIdKey, sizeof(sUserIdKey), "%d_%s", iUserId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Player_UGetString: iUserId=%d, sKey=%s, iMaxLength=%d", iUserId, sKey, iMaxLength);
|
||||
|
||||
if (!GetTrieString(g_hPlayerData, sUserIdKey, sValue, iMaxLength)) {
|
||||
Format(sValue, iMaxLength, "");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
stock bool Player_CGetString(int iClient, const char[] sKey, char[] sValue, int iMaxLength) {
|
||||
return CheckClientForUserId(iClient) && Player_UGetString(GetClientUserId(iClient), sKey, sValue, iMaxLength);
|
||||
}
|
||||
|
||||
stock void Player_AddHealth(int iClient, int iHealth, bool ignoreMax = false) {
|
||||
if (ignoreMax) {
|
||||
SetEntityHealth(iClient, GetClientHealth(iClient) + iHealth);
|
||||
} else {
|
||||
int iCurrentHealth = GetEntProp(iClient, Prop_Send, "m_iHealth");
|
||||
int iMaxHealth = GetEntData(iClient, FindDataMapInfo(iClient, "m_iMaxHealth"));
|
||||
if (iCurrentHealth < iMaxHealth) {
|
||||
SetEntityHealth(iClient, GetClientHealth(iClient) + iHealth);
|
||||
}
|
||||
}
|
||||
}
|
361
scripting/towerdefense/handler/server.sp
Normal file
361
scripting/towerdefense/handler/server.sp
Normal file
@@ -0,0 +1,361 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes the server.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Server_Initialize() {
|
||||
Log(TDLogLevel_Debug, "Initializing server");
|
||||
|
||||
StripConVarFlag("sv_cheats", FCVAR_NOTIFY);
|
||||
StripConVarFlag("sv_tags", FCVAR_NOTIFY);
|
||||
StripConVarFlag("tf_bot_count", FCVAR_NOTIFY);
|
||||
StripConVarFlag("sv_password", FCVAR_NOTIFY);
|
||||
|
||||
HookButtons();
|
||||
Server_Reset();
|
||||
|
||||
int iServerIp[4];
|
||||
SteamWorks_GetPublicIP(iServerIp);
|
||||
Format(g_sServerIp, sizeof(g_sServerIp), "%d.%d.%d.%d", iServerIp[0], iServerIp[1], iServerIp[2], iServerIp[3]);
|
||||
|
||||
char sServerPort[6];
|
||||
GetConVarString(FindConVar("hostport"), sServerPort, sizeof(sServerPort));
|
||||
g_iServerPort = StringToInt(sServerPort);
|
||||
|
||||
if (StrEqual(g_sServerIp, "0.0.0.0")) {
|
||||
Log(TDLogLevel_Error, "ServerIP: %s", g_sServerIp);
|
||||
Log(TDLogLevel_Error, "Vac enabled?: %b", SteamWorks_IsVACEnabled());
|
||||
Log(TDLogLevel_Error, "Is connected to Steam?: %b", SteamWorks_IsConnected());
|
||||
Log(TDLogLevel_Error, "This can be caused by using GSLT with an expired Token. https://steamcommunity.com/dev/managegameservers");
|
||||
Log(TDLogLevel_Error, "The server will loop indefinetly if it can't connect to Steam");
|
||||
|
||||
Log(TDLogLevel_Info, "Server has been restarted completely, reloading map for initializing");
|
||||
ReloadMap();
|
||||
} else {
|
||||
Database_CheckServer(); // Calls Database_OnServerChecked() when finished
|
||||
}
|
||||
}
|
||||
|
||||
stock void Database_OnServerChecked() {
|
||||
Log(TDLogLevel_Trace, "Database_OnServerChecked");
|
||||
|
||||
Database_LoadData(); // Calls Database_OnDataLoaded() when finished
|
||||
}
|
||||
|
||||
stock void Database_OnDataLoaded() {
|
||||
Log(TDLogLevel_Debug, "Successfully initialized server");
|
||||
|
||||
PrintToHudAll("WELCOME TO TF2 TOWER DEFENSE");
|
||||
|
||||
g_bServerInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the server.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Server_Reset() {
|
||||
g_bEnabled = g_hEnabled.BoolValue && g_bTowerDefenseMap && g_bSteamWorks && g_bTF2Attributes;
|
||||
g_bMapRunning = true;
|
||||
|
||||
UpdateGameDescription();
|
||||
|
||||
if (!g_bEnabled) {
|
||||
if (!g_bTowerDefenseMap) {
|
||||
char sCurrentMap[PLATFORM_MAX_PATH];
|
||||
GetCurrentMap(sCurrentMap, sizeof(sCurrentMap));
|
||||
|
||||
Log(TDLogLevel_Info, "Map \"%s\" is not supported, thus Tower Defense has been disabled.", sCurrentMap);
|
||||
} else {
|
||||
Log(TDLogLevel_Info, "Tower Defense is disabled.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_iBuildingLimit[TDBuilding_Sentry] = 1;
|
||||
g_iBuildingLimit[TDBuilding_Dispenser] = 0;
|
||||
g_iBuildingLimit[TDBuilding_TeleporterEntry] = 1;
|
||||
g_iBuildingLimit[TDBuilding_TeleporterExit] = 1;
|
||||
|
||||
// Reset Hint Timer
|
||||
if (hHintTimer != null) {
|
||||
CloseHandle(hHintTimer);
|
||||
hHintTimer = null;
|
||||
}
|
||||
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
// Reset carry Towers/Sentry
|
||||
if (IsTower(g_iAttachedTower[iClient])) {
|
||||
TF2Attrib_RemoveByName(iClient, "cannot pick up buildings");
|
||||
g_iLastMover[g_iAttachedTower[iClient]] = 0;
|
||||
g_bCarryingObject[iClient] = false;
|
||||
g_iAttachedTower[iClient] = 0;
|
||||
}
|
||||
// Reset bought Towers
|
||||
if (IsTower(iClient)) {
|
||||
TDTowerId iTowerId = GetTowerId(iClient);
|
||||
// tf_bot_quota > 0 breaks this here
|
||||
if (iTowerId != TDTower_Invalid) {
|
||||
g_bTowerBought[view_as<int>(iTowerId)] = false;
|
||||
}
|
||||
}
|
||||
if (IsAttacker(iClient) && g_iSlowAttacker[iClient]) {
|
||||
g_iSlowAttacker[iClient] = false;
|
||||
}
|
||||
if (IsDefender(iClient)) {
|
||||
g_bCarryingObject[iClient] = false;
|
||||
// Remove Beam if there is one
|
||||
if (g_iHealBeamIndex[iClient][0] != 0) {
|
||||
if (IsValidEdict(g_iHealBeamIndex[iClient][0])) {
|
||||
RemoveEdict(g_iHealBeamIndex[iClient][0]);
|
||||
g_iHealBeamIndex[iClient][0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iHealBeamIndex[iClient][1] != 0) {
|
||||
if (IsValidEdict(g_iHealBeamIndex[iClient][1])) {
|
||||
RemoveEdict(g_iHealBeamIndex[iClient][1]);
|
||||
g_iHealBeamIndex[iClient][1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Multipliers
|
||||
for (int i = 1; i <= iMaxMultiplierTypes; i++) {
|
||||
fMultiplier[i] = 0.0;
|
||||
}
|
||||
g_iTime = GetTime();
|
||||
g_iMetalPackCount = 0;
|
||||
|
||||
g_bTowersLocked = false;
|
||||
g_bAoEEngineerAttack = false;
|
||||
|
||||
g_bStartWaveEarly = false;
|
||||
g_iBotsToSpawn = 0;
|
||||
g_iTotalBotsLeft = 0;
|
||||
|
||||
g_iCurrentWave = 0;
|
||||
g_iNextWaveType = 0;
|
||||
|
||||
iAoEEngineerTimer = 0;
|
||||
iAoEKritzMedicTimer = 0;
|
||||
|
||||
g_iHealthBar = GetHealthBar();
|
||||
|
||||
g_bLockable = true;
|
||||
g_bCanGetUnlocks = true;
|
||||
|
||||
// Reset AoE Timer
|
||||
if (hAoETimer != null) {
|
||||
CloseHandle(hAoETimer);
|
||||
hAoETimer = null;
|
||||
}
|
||||
|
||||
// Get map max clients
|
||||
g_iMaxClients = PLAYER_LIMIT;
|
||||
|
||||
char sQuery[256];
|
||||
char sCurrentMap[PLATFORM_MAX_PATH];
|
||||
GetCurrentMap(sCurrentMap, sizeof(sCurrentMap));
|
||||
|
||||
g_hDatabase.Format(sQuery, sizeof(sQuery),
|
||||
"SELECT `player_limit` " ...
|
||||
"FROM `map` " ...
|
||||
"WHERE `name` = '%s' " ...
|
||||
"LIMIT 1",
|
||||
sCurrentMap);
|
||||
|
||||
DBResultSet queryResult = SQL_Query(g_hDatabase, sQuery);
|
||||
|
||||
if (queryResult == null) {
|
||||
char error[255];
|
||||
SQL_GetError(g_hDatabase, error, sizeof(error));
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to query (error: %s)", error);
|
||||
} else {
|
||||
if (queryResult.HasResults && queryResult.FetchRow()) {
|
||||
int iResult = queryResult.FetchInt(0);
|
||||
if (iResult > 0) {
|
||||
g_iMaxClients = iResult;
|
||||
LogType(TDLogLevel_Debug, TDLogType_FileAndConsole, "Max clients for map %s is %d", sCurrentMap, g_iMaxClients);
|
||||
} else {
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Max clients for map %s is %d. This isnt supported; Please check the database entries. Setting max clients to default %d", sCurrentMap, iResult, g_iMaxClients);
|
||||
}
|
||||
} else {
|
||||
LogType(TDLogLevel_Debug, TDLogType_FileAndConsole, "Couldn't find entry for map '%s'. Setting max clients to default %d", sCurrentMap, g_iMaxClients);
|
||||
}
|
||||
delete queryResult;
|
||||
}
|
||||
|
||||
int clients = (g_iMaxClients + g_hMaxBotsOnField.IntValue) - MaxClients;
|
||||
|
||||
if (clients > 0) {
|
||||
char cvarName[32];
|
||||
g_hMaxBotsOnField.GetName(cvarName, sizeof(cvarName));
|
||||
LogType(TDLogLevel_Warning, TDLogType_FileAndConsole, "ConVar '%s' value + the allowed max clients '%d' (database) is higher than the server maxplayers '%d'. Shrinking convar value by '%d'", cvarName, g_iMaxClients, MaxClients, clients);
|
||||
g_hMaxBotsOnField.IntValue = g_hMaxBotsOnField.IntValue - clients;
|
||||
}
|
||||
|
||||
Format(g_sPassword, sizeof(g_sPassword), "");
|
||||
|
||||
SetPassword(g_sPassword, false); // Change upon release
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a servers data was set.
|
||||
*
|
||||
* @param iServerId The server id (unique for every server).
|
||||
* @param sKey The set key.
|
||||
* @param iDataType The datatype of the set data.
|
||||
* @param iValue The value if the set data is an integer, -1 otherwise.
|
||||
* @param bValue The value if the set data is a boolean, false otherwise.
|
||||
* @param fValue The value if the set data is a float, -1.0 otherwise.
|
||||
* @param sValue The value if the set data is a string, empty string ("") otherwise.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Server_OnDataSet(int iServerId, const char[] sKey, TDDataType iDataType, int iValue, int bValue, float fValue, const char[] sValue) {
|
||||
switch (iDataType) {
|
||||
case TDDataType_Integer: {
|
||||
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_Integer, iValue=%d", iServerId, sKey, iValue);
|
||||
}
|
||||
|
||||
case TDDataType_Boolean: {
|
||||
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_Boolean, bValue=%s", iServerId, sKey, (bValue ? "true" : "false"));
|
||||
}
|
||||
|
||||
case TDDataType_Float: {
|
||||
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_Float, fValue=%f", iServerId, sKey, fValue);
|
||||
}
|
||||
|
||||
case TDDataType_String: {
|
||||
Log(TDLogLevel_Trace, "Server_OnDataSet: iServerId=%d, sKey=%s, iDataType=TDDataType_String, sValue=%s", iServerId, sKey, sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stock void Server_UAddValue(int iServerId, const char[] sKey, int iValue) {
|
||||
char sServerIdKey[128];
|
||||
int iOldValue;
|
||||
Server_UGetValue(iServerId, sKey, iOldValue);
|
||||
if (iOldValue != -1)
|
||||
iValue = iValue + iOldValue;
|
||||
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, iValue, false, -1.0, "");
|
||||
|
||||
SetTrieValue(g_hServerData, sServerIdKey, iValue);
|
||||
}
|
||||
|
||||
stock void Server_USetValue(int iServerId, const char[] sKey, int iValue) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, iValue, false, -1.0, "");
|
||||
|
||||
SetTrieValue(g_hServerData, sServerIdKey, iValue);
|
||||
}
|
||||
|
||||
stock bool Server_UGetValue(int iServerId, const char[] sKey, int &iValue) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Server_UGetValue: iServerId=%d, sKey=%s", iServerId, sKey);
|
||||
|
||||
if (!GetTrieValue(g_hServerData, sServerIdKey, iValue)) {
|
||||
iValue = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
stock void Server_USetBool(int iServerId, const char[] sKey, bool bValue) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, -1, bValue, -1.0, "");
|
||||
|
||||
SetTrieValue(g_hServerData, sServerIdKey, (bValue ? 1 : 0));
|
||||
}
|
||||
|
||||
stock bool Server_UGetBool(int iServerId, const char[] sKey) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Server_UGetBool: iServerId=%d, sKey=%s", iServerId, sKey);
|
||||
|
||||
int iValue = 0;
|
||||
GetTrieValue(g_hServerData, sServerIdKey, iValue);
|
||||
|
||||
return (iValue != 0);
|
||||
}
|
||||
|
||||
stock void Server_USetFloat(int iServerId, const char[] sKey, float fValue) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
char sValue[64];
|
||||
FloatToString(fValue, sValue, sizeof(sValue));
|
||||
|
||||
Server_OnDataSet(iServerId, sKey, TDDataType_Integer, -1, false, fValue, "");
|
||||
|
||||
SetTrieString(g_hServerData, sServerIdKey, sValue);
|
||||
}
|
||||
|
||||
stock bool Server_UGetFloat(int iServerId, const char[] sKey, float &fValue) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Server_UGetFloat: iServerId=%d, sKey=%s", iServerId, sKey);
|
||||
|
||||
char sValue[64];
|
||||
if (!GetTrieString(g_hServerData, sServerIdKey, sValue, sizeof(sValue))) {
|
||||
fValue = -1.0;
|
||||
return false;
|
||||
}
|
||||
|
||||
fValue = StringToFloat(sValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
stock void Server_USetString(int iServerId, const char[] sKey, const char[] sValue, any...) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
char sFormattedValue[256];
|
||||
VFormat(sFormattedValue, sizeof(sFormattedValue), sValue, 4);
|
||||
|
||||
Server_OnDataSet(iServerId, sKey, TDDataType_String, -1, false, -1.0, sValue);
|
||||
|
||||
SetTrieString(g_hServerData, sServerIdKey, sFormattedValue);
|
||||
}
|
||||
|
||||
stock bool Server_UGetString(int iServerId, const char[] sKey, char[] sValue, int iMaxLength) {
|
||||
char sServerIdKey[128];
|
||||
Format(sServerIdKey, sizeof(sServerIdKey), "%d_%s", iServerId, sKey);
|
||||
|
||||
Log(TDLogLevel_Trace, "Server_UGetString: iServerId=%d, sKey=%s, iMaxLength=%d", iServerId, sKey, iMaxLength);
|
||||
|
||||
if (!GetTrieString(g_hServerData, sServerIdKey, sValue, iMaxLength)) {
|
||||
Format(sValue, iMaxLength, "");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
68
scripting/towerdefense/handler/sounds.sp
Normal file
68
scripting/towerdefense/handler/sounds.sp
Normal file
@@ -0,0 +1,68 @@
|
||||
stock void PlaySound(char[] sSoundName, int iClient) {
|
||||
if (iClient == 0) {
|
||||
if (StrEqual(sSoundName, "Win")) {
|
||||
int iRandom = GetRandomInt(1, 2);
|
||||
switch (iRandom) {
|
||||
case 1: {
|
||||
EmitSoundToAll("vo/mvm_mannup_wave_end01.mp3");
|
||||
}
|
||||
case 2: {
|
||||
EmitSoundToAll("vo/mvm_mannup_wave_end02.mp3");
|
||||
}
|
||||
}
|
||||
} else if (StrEqual(sSoundName, "WaveComplete")) {
|
||||
int iRandom = GetRandomInt(1, 21);
|
||||
switch (iRandom) {
|
||||
case 1: {
|
||||
EmitSoundToAll("vo/mvm_wave_end01.mp3");
|
||||
}
|
||||
case 2: {
|
||||
EmitSoundToAll("vo/mvm_wave_end02.mp3");
|
||||
}
|
||||
case 3: {
|
||||
EmitSoundToAll("vo/mvm_wave_end03.mp3");
|
||||
}
|
||||
case 4: {
|
||||
EmitSoundToAll("vo/mvm_wave_end04.mp3");
|
||||
}
|
||||
case 5: {
|
||||
EmitSoundToAll("vo/mvm_wave_end05.mp3");
|
||||
}
|
||||
case 6: {
|
||||
EmitSoundToAll("vo/mvm_wave_end06.mp3");
|
||||
}
|
||||
case 7: {
|
||||
EmitSoundToAll("vo/mvm_wave_end07.mp3");
|
||||
}
|
||||
}
|
||||
} else if (StrEqual(sSoundName, "Music")) {
|
||||
int iRandom = GetRandomInt(1, 15);
|
||||
switch (iRandom) {
|
||||
case 1: {
|
||||
EmitSoundToAll("music/mvm_start_mid_wave.wav");
|
||||
}
|
||||
case 2: {
|
||||
EmitSoundToAll("music/mvm_start_last_wave.wav");
|
||||
}
|
||||
case 3: {
|
||||
EmitSoundToAll("music/mvm_end_last_wave.wav");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (StrEqual(sSoundName, "Forbid")) {
|
||||
int iRandom = GetRandomInt(1, 3);
|
||||
switch (iRandom) {
|
||||
case 1: {
|
||||
EmitSoundToClient(iClient, "vo/engineer_no01.mp3");
|
||||
}
|
||||
case 2: {
|
||||
EmitSoundToClient(iClient, "vo/engineer_no02.mp3");
|
||||
}
|
||||
case 3: {
|
||||
EmitSoundToClient(iClient, "vo/engineer_no03.mp3");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1032
scripting/towerdefense/handler/towers.sp
Normal file
1032
scripting/towerdefense/handler/towers.sp
Normal file
File diff suppressed because it is too large
Load Diff
782
scripting/towerdefense/handler/waves.sp
Normal file
782
scripting/towerdefense/handler/waves.sp
Normal file
@@ -0,0 +1,782 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Called when the start button is being shot.
|
||||
*
|
||||
* @param iWave The incoming wave.
|
||||
* @param iButton The button entity.
|
||||
* @param iActivator The activator entity.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Wave_OnButtonStart(int iWave, int iButton, int iActivator) {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDefender(iActivator)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char sName[64];
|
||||
Format(sName, sizeof(sName), "wave_start_%d", iWave + 1);
|
||||
DispatchKeyValue(iButton, "targetname", sName);
|
||||
|
||||
TeleportEntity(iButton, view_as<float>({ 0.0, 0.0, -9192.0 }), NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
|
||||
|
||||
/*Translation Example
|
||||
* Format: %s %t
|
||||
* Values: PLUGIN_PREFIX "waveStart" GetClientNameShort(iActivator) (g_iCurrentWave + 1)
|
||||
* Output: [TF2TD] [PrWh] Dragonisser started Wave 1
|
||||
*/
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveStart", GetClientNameShort(iActivator), g_iCurrentWave + 1);
|
||||
|
||||
//Wave Health
|
||||
int iWaveHealth;
|
||||
int iPlayerCount = GetRealClientCount();
|
||||
if (iPlayerCount > 1)
|
||||
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
|
||||
else
|
||||
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
|
||||
|
||||
SetHudTextParams(-1.0, 0.6, 3.1, 255, 255, 255, 255, 1, 2.0);
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
if(Wave_GetType(g_iCurrentWave) == 0) {
|
||||
ShowHudText(iClient, -1, "%t", "waveIncommingWithHealth", g_iCurrentWave + 1, iWaveHealth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iWave == 0) {
|
||||
Timer_NextWaveCountdown(null, 5);
|
||||
} else {
|
||||
g_bStartWaveEarly = true;
|
||||
Wave_Spawn();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an attacker spawned.
|
||||
*
|
||||
* @param iAttacker The attacker.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Wave_OnSpawn(int iAttacker) {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_bBoostWave = false;
|
||||
|
||||
SetRobotModel(iAttacker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called the frame after an attacker spawned.
|
||||
*
|
||||
* @param iAttacker The attacker.
|
||||
* @noreturn
|
||||
*/
|
||||
public void Wave_OnSpawnPost(any iAttacker) {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
int iMaxHealth = GetEntProp(iAttacker, Prop_Data, "m_iMaxHealth");
|
||||
int iWaveHealth;
|
||||
int iPlayerCount = GetRealClientCount();
|
||||
if (iPlayerCount > 1)
|
||||
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
|
||||
else
|
||||
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
|
||||
|
||||
TF2Attrib_SetByName(iAttacker, "max health additive bonus", float(iWaveHealth - iMaxHealth));
|
||||
SetEntityHealth(iAttacker, iWaveHealth);
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Boss) {
|
||||
// TODO(?)
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Rapid) {
|
||||
g_bBoostWave = true;
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Regen) {
|
||||
TF2Attrib_SetByName(iAttacker, "health regen", float(RoundFloat(iWaveHealth * 0.05)));
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_KnockbackImmune) {
|
||||
TF2Attrib_SetByName(iAttacker, "damage force reduction", 0.0);
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Air) {
|
||||
TF2Attrib_SetByName(iAttacker, "damage force reduction", 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
public void TF2_OnConditionAdded(int iClient, TFCond Condition) {
|
||||
if (Condition == TFCond_Jarated && g_iNextWaveType & TDWaveType_JarateImmune)
|
||||
TF2_RemoveCondition(iClient, TFCond_Jarated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called the frame after an attacker gets damaged.
|
||||
*
|
||||
* @param iVictim The victim.
|
||||
* @param iAttacker The attacker.
|
||||
* @param iInflictor The inflictor.
|
||||
* @param fDamage The damage.
|
||||
* @param iDamageType The damage type.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Wave_OnTakeDamagePost(int iVictim, int iAttacker, int iInflictor, float fDamage, int iDamageType) {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsValidEntity(g_iHealthBar)) {
|
||||
int iWaveHealth;
|
||||
int iPlayerCount = GetRealClientCount();
|
||||
if (iPlayerCount > 1)
|
||||
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
|
||||
else
|
||||
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
|
||||
int iTotalHealth = iWaveHealth * g_iBotsToSpawn;
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsAttacker(iClient)) {
|
||||
iTotalHealth += GetClientHealth(iClient);
|
||||
}
|
||||
}
|
||||
|
||||
int iTotalHealthMax = iWaveHealth * Wave_GetQuantity(g_iCurrentWave);
|
||||
float fPercentage = float(iTotalHealth) / float(iTotalHealthMax);
|
||||
|
||||
SetEntProp(g_iHealthBar, Prop_Send, "m_iBossHealthPercentageByte", RoundToFloor(fPercentage * 255));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an attacker dies.
|
||||
*
|
||||
* @param iAttacker The attacker.
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Wave_OnDeath(int iAttacker, float fPosition[3]) {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(hurp): Find a better way to make the ammo from air waves spawn on the ground,
|
||||
// This method probably wont work for all maps
|
||||
if (Wave_GetType(g_iCurrentWave) == TDWaveType_Air) {
|
||||
fPosition[2] = fPosition[2] - 10.0;
|
||||
}
|
||||
|
||||
// TODO(hurp): Customize metal ammount based off the wave in config files
|
||||
fPosition[2] = fPosition[2] - GetDistanceToGround(fPosition) + 10.0;
|
||||
SpawnRewardPack(TDMetalPack_Small, fPosition, 100);
|
||||
|
||||
CreateTimer(1.0, Delay_KickAttacker, iAttacker, TIMER_FLAG_NO_MAPCHANGE);
|
||||
if (g_iBotsToSpawn >= 1) {
|
||||
Wave_SpawnBots();
|
||||
} else if (GetAliveAttackerCount() <= 1 && g_iBotsToSpawn <= 0) {
|
||||
Wave_OnDeathAll();
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
Player_CAddValue(iClient, PLAYER_WAVES_PLAYED, g_iCurrentWave);
|
||||
Player_CSetValue(iClient, PLAYER_WAVE_REACHED, g_iCurrentWave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all attackers died.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Wave_OnDeathAll() {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
if (g_iNextWaveType & TDWaveType_Boss) {
|
||||
SpawnMetalPacks(TDMetalPack_Boss);
|
||||
}
|
||||
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient))
|
||||
Player_CAddValue(iClient, PLAYER_WAVES_PLAYED, 1);
|
||||
}
|
||||
|
||||
if (g_iCurrentWave + 1 >= iMaxWaves) {
|
||||
PrintToServer("[TF2TD] Round won (Wave: %d)", g_iCurrentWave + 1);
|
||||
Server_UAddValue(g_iServerId, SERVER_ROUNDS_WON, 1);
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
Player_CSetValue(iClient, PLAYER_ROUNDS_WON, 1);
|
||||
}
|
||||
}
|
||||
PlaySound("Win", 0);
|
||||
Wave_Win(TEAM_DEFENDER);
|
||||
return;
|
||||
} else {
|
||||
PlaySound("WaveComplete", 0);
|
||||
}
|
||||
|
||||
g_iTotalBotsLeft = 0;
|
||||
|
||||
g_bStartWaveEarly = false;
|
||||
|
||||
g_iCurrentWave++;
|
||||
|
||||
g_iNextWaveType = Wave_GetType(g_iCurrentWave);
|
||||
|
||||
TeleportEntity(g_iWaveStartButton, g_fWaveStartButtonLocation, NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
|
||||
|
||||
Timer_NextWaveCountdown(null, g_iRespawnWaveTime);
|
||||
|
||||
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "wavePassed", g_iCurrentWave);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "wavePrepareTime", g_iRespawnWaveTime);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveTowersUnlocked");
|
||||
|
||||
g_bTowersLocked = false;
|
||||
|
||||
Log(TDLogLevel_Info, "Wave %d passed", g_iCurrentWave);
|
||||
|
||||
if (Panel_Remove(g_iCurrentWave)) {
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveBonusAvailable", g_iCurrentWave);
|
||||
Log(TDLogLevel_Debug, "New bonus available (Wave: %d)", g_iCurrentWave);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an attacker touches a corner.
|
||||
*
|
||||
* @param iCorner The corner trigger entity.
|
||||
* @param iAttacker The attacker.
|
||||
* @noreturn
|
||||
*/
|
||||
public void Wave_OnTouchCorner(int iCorner, int iAttacker) {
|
||||
if (!g_bEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAttacker(iAttacker)) {
|
||||
int iNextCorner = Corner_GetNext(iCorner);
|
||||
|
||||
if (iNextCorner != -1) {
|
||||
float fAngles[3];
|
||||
Corner_GetAngles(iCorner, iNextCorner, fAngles);
|
||||
TeleportEntity(iAttacker, NULL_VECTOR, fAngles, NULL_VECTOR);
|
||||
} else {
|
||||
g_bBoostWave = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a wave.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
|
||||
stock void Wave_Spawn() {
|
||||
// Delete ammo packs loot that have not been picked up
|
||||
char buffer[64];
|
||||
int entity = -1;
|
||||
while ((entity = FindEntityByClassname(entity, "prop_dynamic")) != INVALID_ENT_REFERENCE) {
|
||||
GetEntPropString(entity, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
|
||||
|
||||
if (StrEqual(buffer, "models/items/ammopack_small.mdl")) {
|
||||
AcceptEntityInput(entity, "Kill");
|
||||
g_iMetalPackCount--;
|
||||
}
|
||||
}
|
||||
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveIncomming", g_iCurrentWave + 1);
|
||||
CPrintToChatAll("%s %t", PLUGIN_PREFIX, "waveTowersLocked");
|
||||
g_bTowersLocked = true;
|
||||
|
||||
g_iBotsToSpawn = Wave_GetQuantity(g_iCurrentWave);
|
||||
|
||||
SetEntProp(g_iHealthBar, Prop_Send, "m_iBossHealthPercentageByte", 255);
|
||||
g_iTotalBotsLeft = Wave_GetQuantity(g_iCurrentWave);
|
||||
Wave_SpawnBots();
|
||||
}
|
||||
|
||||
stock void Wave_SpawnBots() {
|
||||
if (g_iBotsToSpawn <= 0) {
|
||||
return;
|
||||
}
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
if (!Wave_GetName(g_iCurrentWave, sName, sizeof(sName))) {
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to spawn wave %d, could not read name!", g_iCurrentWave);
|
||||
return;
|
||||
}
|
||||
|
||||
char sClass[32];
|
||||
if (!Wave_GetClassString(g_iCurrentWave, sClass, sizeof(sClass))) {
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to spawn wave %d, could not read class!", g_iCurrentWave);
|
||||
return;
|
||||
}
|
||||
|
||||
int iTotalBots = Wave_GetQuantity(g_iCurrentWave);
|
||||
int iAliveBots = GetAliveAttackerCount();
|
||||
|
||||
// If only less than g_iMaxBotsOnField bots in total
|
||||
if (iTotalBots <= g_hMaxBotsOnField.IntValue) {
|
||||
if (iTotalBots > 1) {
|
||||
for (int i = 1; i <= iTotalBots; i++) {
|
||||
ServerCommand("bot -team red -class %s -name %s%d", sClass, sName, i);
|
||||
g_iBotsToSpawn--;
|
||||
}
|
||||
// If only 1 bot
|
||||
} else {
|
||||
ServerCommand("bot -team red -class %s -name %s", sClass, sName);
|
||||
g_iBotsToSpawn = 0;
|
||||
}
|
||||
CreateTimer(1.0, TeleportWaveDelay, iTotalBots, TIMER_FLAG_NO_MAPCHANGE);
|
||||
// Else more than g_iMaxBotsOnField bots
|
||||
} else {
|
||||
// If no bot alive
|
||||
if (iAliveBots <= 0) {
|
||||
for (int i = 1; i <= g_hMaxBotsOnField.IntValue; i++) {
|
||||
g_iBotsToSpawn--;
|
||||
ServerCommand("bot -team red -class %s -name %s%d", sClass, sName, -(g_iBotsToSpawn - iTotalBots));
|
||||
}
|
||||
CreateTimer(1.0, TeleportWaveDelay, g_hMaxBotsOnField.IntValue, TIMER_FLAG_NO_MAPCHANGE);
|
||||
// If bots alive
|
||||
} else {
|
||||
int iBotsToSpawn = g_hMaxBotsOnField.IntValue - iAliveBots;
|
||||
for (int i = 1; i <= iBotsToSpawn; i++) {
|
||||
g_iBotsToSpawn--;
|
||||
ServerCommand("bot -team red -class %s -name %s%d", sClass, sName, -(g_iBotsToSpawn - iTotalBots));
|
||||
}
|
||||
CreateTimer(1.0, TeleportWaveDelay, iBotsToSpawn, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action TeleportWaveDelay(Handle hTimer, any iNumber) {
|
||||
if (iNumber <= 0) {
|
||||
return Plugin_Stop;
|
||||
}
|
||||
int iTotalBots = Wave_GetQuantity(g_iCurrentWave);
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
if (!Wave_GetName(g_iCurrentWave, sName, sizeof(sName))) {
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to teleport wave %d, could not read name!", g_iCurrentWave);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
if (iTotalBots <= 1) {
|
||||
Format(sName, sizeof(sName), "%s", sName);
|
||||
} else if (iTotalBots > g_hMaxBotsOnField.IntValue) {
|
||||
g_iTotalBotsLeft--;
|
||||
Format(sName, sizeof(sName), "%s%d", sName, -(g_iTotalBotsLeft - iTotalBots));
|
||||
} else if (iNumber > 0) {
|
||||
Format(sName, sizeof(sName), "%s%d", sName, iNumber);
|
||||
}
|
||||
|
||||
int iAttacker = GetClientByNameExact(sName, TEAM_ATTACKER);
|
||||
|
||||
Log(TDLogLevel_Trace, "Should teleport attacker %d (%d, %s) of wave %d (%d attackers)", iNumber, iAttacker, sName, g_iCurrentWave + 1, Wave_GetQuantity(g_iCurrentWave));
|
||||
|
||||
if (IsAttacker(iAttacker)) {
|
||||
float fLocation[3];
|
||||
if (!Wave_GetLocation(g_iCurrentWave, fLocation)) {
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to teleport wave %d, could not read location!", g_iCurrentWave);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
float fAngles[3];
|
||||
if (!Wave_GetAngles(g_iCurrentWave, fAngles)) {
|
||||
LogType(TDLogLevel_Error, TDLogType_FileAndConsole, "Failed to teleport wave %d, could not read angles!", g_iCurrentWave);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
TeleportEntity(iAttacker, fLocation, fAngles, view_as<float>({ 0.0, 0.0, 0.0 }));
|
||||
|
||||
Log(TDLogLevel_Trace, " -> Teleported attacker");
|
||||
CreateTimer(1.0, TeleportWaveDelay, iNumber - 1, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
public void Wave_Win(int iTeam) {
|
||||
int iEntity = -1;
|
||||
|
||||
iEntity = FindEntityByClassname2(iEntity, "team_control_point_master");
|
||||
|
||||
if (iEntity == -1 || !IsValidEntity(iEntity)) {
|
||||
// No team_control_point_master either... lets create one.
|
||||
iEntity = CreateEntityByName("team_control_point_master");
|
||||
DispatchKeyValue(iEntity, "targetname", "master_control_point");
|
||||
DispatchKeyValue(iEntity, "StartDisabled", "0");
|
||||
DispatchSpawn(iEntity);
|
||||
}
|
||||
SetVariantInt(iTeam);
|
||||
AcceptEntityInput(iEntity, "SetWinner");
|
||||
}
|
||||
|
||||
/*=========================================
|
||||
= Utility Functions =
|
||||
=========================================*/
|
||||
public Action Delay_KickAttacker(Handle hTimer, any iAttacker) {
|
||||
if (IsAttacker(iAttacker)) {
|
||||
KickClient(iAttacker);
|
||||
}
|
||||
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
public Action Timer_NextWaveCountdown(Handle hTimer, any iTime) {
|
||||
if (g_bStartWaveEarly) {
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
CPrintToChat(iClient, "%s %t", PLUGIN_PREFIX, "waveStartedEarly", (iTime + 1) * 10, iTime + 1);
|
||||
AddClientMetal(iClient, (iTime + 1) * 10);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
switch (iTime) {
|
||||
case 5: {
|
||||
SetHudTextParams(-1.0, 0.6, 5.1, 255, 255, 255, 255, 2, 2.0);
|
||||
|
||||
// Wave Health
|
||||
int iWaveHealth;
|
||||
int iPlayerCount = GetRealClientCount();
|
||||
if (iPlayerCount > 1)
|
||||
iWaveHealth = RoundToZero(float(Wave_GetHealth(g_iCurrentWave)) * (float(iPlayerCount) * 0.125 + 1.0));
|
||||
else
|
||||
iWaveHealth = Wave_GetHealth(g_iCurrentWave);
|
||||
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
char sType[256];
|
||||
strcopy(sType, sizeof(sType), "");
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Rapid) {
|
||||
if (StrEqual(sType, "")) {
|
||||
Format(sType, sizeof(sType), "%T", "waveTypeRapid", iClient);
|
||||
} else {
|
||||
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeRapid", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Regen) {
|
||||
if (StrEqual(sType, "")) {
|
||||
Format(sType, sizeof(sType), "%T", "waveTypeRegen", iClient);
|
||||
} else {
|
||||
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeRegen", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_KnockbackImmune) {
|
||||
if (StrEqual(sType, "")) {
|
||||
Format(sType, sizeof(sType), "%T", "waveTypeKnockbackImmune", iClient);
|
||||
} else {
|
||||
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeKnockbackImmune", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Air) {
|
||||
if (StrEqual(sType, "")) {
|
||||
Format(sType, sizeof(sType), "%T", "waveTypeAir", iClient);
|
||||
} else {
|
||||
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeAir", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_JarateImmune) {
|
||||
if (StrEqual(sType, "")) {
|
||||
Format(sType, sizeof(sType), "%T", "waveTypeJarateImmune", iClient);
|
||||
} else {
|
||||
Format(sType, sizeof(sType), "%s + %T", sType, "waveTypeJarateImmune", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_iNextWaveType & TDWaveType_Boss) {
|
||||
if (StrEqual(sType, "")) {
|
||||
Format(sType, sizeof(sType), "%T", "waveTypeBoss", iClient);
|
||||
} else {
|
||||
Format(sType, sizeof(sType), "%s %T", sType, "waveTypeBoss", iClient);
|
||||
}
|
||||
}
|
||||
|
||||
if (StrEqual(sType, "")) {
|
||||
ShowHudText(iClient, -1, "%t", "waveIncommingWithHealth", g_iCurrentWave + 1, iWaveHealth);
|
||||
} else {
|
||||
ShowHudText(iClient, -1, "%t", "waveIncommingWithHealthAndType", sType, g_iCurrentWave + 1, iWaveHealth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmitSoundToAll("vo/announcer_begins_5sec.mp3");
|
||||
}
|
||||
case 4: {
|
||||
EmitSoundToAll("vo/announcer_begins_4sec.mp3");
|
||||
}
|
||||
case 3: {
|
||||
EmitSoundToAll("vo/announcer_begins_3sec.mp3");
|
||||
}
|
||||
case 2: {
|
||||
EmitSoundToAll("vo/announcer_begins_2sec.mp3");
|
||||
}
|
||||
case 1: {
|
||||
EmitSoundToAll("vo/announcer_begins_1sec.mp3");
|
||||
|
||||
TeleportEntity(g_iWaveStartButton, view_as<float>({ 0.0, 0.0, -9192.0 }), NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
|
||||
}
|
||||
case 0: {
|
||||
PlaySound("Music", 0);
|
||||
Wave_Spawn();
|
||||
|
||||
return Plugin_Stop;
|
||||
}
|
||||
}
|
||||
|
||||
SetHudTextParams(-1.0, 0.85, 1.1, 255, 255, 255, 255);
|
||||
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsDefender(iClient)) {
|
||||
ShowHudText(iClient, -1, "%t", "waveArrivingIn", iTime);
|
||||
}
|
||||
}
|
||||
|
||||
CreateTimer(1.0, Timer_NextWaveCountdown, iTime - 1, TIMER_FLAG_NO_MAPCHANGE);
|
||||
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of alive attackers.
|
||||
*
|
||||
* @return Count of alive attackers.
|
||||
*/
|
||||
|
||||
stock int GetAliveAttackerCount() {
|
||||
int iAttackers = 0;
|
||||
|
||||
for (int iClient = 1; iClient <= MaxClients; iClient++) {
|
||||
if (IsAttacker(iClient) && IsPlayerAlive(iClient)) {
|
||||
iAttackers++;
|
||||
}
|
||||
}
|
||||
|
||||
return iAttackers;
|
||||
}
|
||||
|
||||
/*======================================
|
||||
= Data Functions =
|
||||
======================================*/
|
||||
|
||||
/**
|
||||
* Gets the name of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @param sBuffer The destination string buffer.
|
||||
* @param iMaxLength The maximum length of the output string buffer.
|
||||
* @return True on success, false if wave was not found.
|
||||
*/
|
||||
|
||||
stock bool Wave_GetName(int iWave, char[] sBuffer, int iMaxLength) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_name", iWave);
|
||||
|
||||
return GetTrieString(g_hMapWaves, sKey, sBuffer, iMaxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @return The waves type bit field.
|
||||
*/
|
||||
|
||||
stock int Wave_GetType(int iWave) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_type", iWave);
|
||||
|
||||
int iType = 0;
|
||||
if (!GetTrieValue(g_hMapWaves, sKey, iType)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @param sBuffer The destination string buffer.
|
||||
* @param iMaxLength The maximum length of the output string buffer.
|
||||
* @return True on success, false if wave was not found.
|
||||
*/
|
||||
|
||||
stock bool Wave_GetClassString(int iWave, char[] sBuffer, int iMaxLength) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_class", iWave);
|
||||
|
||||
return GetTrieString(g_hMapWaves, sKey, sBuffer, iMaxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @return The waves class type, or TFClass_Unknown on error.
|
||||
*/
|
||||
|
||||
stock TFClassType Wave_GetClass(int iWave) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_class", iWave);
|
||||
|
||||
char sClass[32];
|
||||
GetTrieString(g_hMapWaves, sKey, sClass, sizeof(sClass));
|
||||
|
||||
if (StrEqual(sClass, "Scout")) {
|
||||
return TFClass_Scout;
|
||||
} else if (StrEqual(sClass, "Sniper")) {
|
||||
return TFClass_Sniper;
|
||||
} else if (StrEqual(sClass, "Soldier")) {
|
||||
return TFClass_Soldier;
|
||||
} else if (StrEqual(sClass, "Demoman")) {
|
||||
return TFClass_DemoMan;
|
||||
} else if (StrEqual(sClass, "Medic")) {
|
||||
return TFClass_Medic;
|
||||
} else if (StrEqual(sClass, "Heavy")) {
|
||||
return TFClass_Heavy;
|
||||
} else if (StrEqual(sClass, "Pyro")) {
|
||||
return TFClass_Pyro;
|
||||
} else if (StrEqual(sClass, "Spy")) {
|
||||
return TFClass_Spy;
|
||||
} else if (StrEqual(sClass, "Spy")) {
|
||||
return TFClass_Engineer;
|
||||
}
|
||||
|
||||
return TFClass_Unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quantity of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @return The waves quantity, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Wave_GetQuantity(int iWave) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_quantity", iWave);
|
||||
|
||||
int iQuantity = 0;
|
||||
if (!GetTrieValue(g_hMapWaves, sKey, iQuantity)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iQuantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the health of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @return The waves health, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Wave_GetHealth(int iWave) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_health", iWave);
|
||||
|
||||
int iHealth = 0;
|
||||
if (!GetTrieValue(g_hMapWaves, sKey, iHealth)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iHealth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @param fLocation The location vector.
|
||||
* @return True on success, false if wave was not found.
|
||||
*/
|
||||
|
||||
stock bool Wave_GetLocation(int iWave, float fLocation[3]) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_location", iWave);
|
||||
|
||||
char sLocation[64];
|
||||
if (GetTrieString(g_hMapWaves, sKey, sLocation, sizeof(sLocation))) {
|
||||
char sLocationParts[6][16];
|
||||
ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));
|
||||
|
||||
fLocation[0] = StringToFloat(sLocationParts[0]);
|
||||
fLocation[1] = StringToFloat(sLocationParts[1]);
|
||||
fLocation[2] = StringToFloat(sLocationParts[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the angles of a wave.
|
||||
*
|
||||
* @param iWave The wave.
|
||||
* @param fAngles The angles vector.
|
||||
* @return True on success, false if wave was not found.
|
||||
*/
|
||||
|
||||
stock bool Wave_GetAngles(int iWave, float fAngles[3]) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_location", iWave);
|
||||
|
||||
char sAngles[64];
|
||||
if (GetTrieString(g_hMapWaves, sKey, sAngles, sizeof(sAngles))) {
|
||||
char sAnglesParts[6][16];
|
||||
ExplodeString(sAngles, " ", sAnglesParts, sizeof(sAnglesParts), sizeof(sAnglesParts[]));
|
||||
|
||||
fAngles[0] = StringToFloat(sAnglesParts[3]);
|
||||
fAngles[1] = StringToFloat(sAnglesParts[4]);
|
||||
fAngles[2] = StringToFloat(sAnglesParts[5]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity by using its classname.
|
||||
*
|
||||
* @param iStartEnt The start entity entity index.
|
||||
* @param sClassname The entity classname to look for.
|
||||
* @return The entitys entity index, or -1 on failure.
|
||||
*/
|
||||
public int FindEntityByClassname2(int iStartEnt, const char[] sClassname) {
|
||||
/* If iStartEnt isn't valid shifting it back to the nearest valid one */
|
||||
while (iStartEnt > -1 && !IsValidEntity(iStartEnt))
|
||||
iStartEnt--;
|
||||
return FindEntityByClassname(iStartEnt, sClassname);
|
||||
}
|
178
scripting/towerdefense/handler/weapons.sp
Normal file
178
scripting/towerdefense/handler/weapons.sp
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
/*======================================
|
||||
= Data Functions =
|
||||
======================================*/
|
||||
|
||||
/**
|
||||
* Gets the name of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @param sBuffer The destination string buffer.
|
||||
* @param iMaxLength The maximum length of the output string buffer.
|
||||
* @return True on success, false if weapon was not found.
|
||||
*/
|
||||
|
||||
stock bool Weapon_GetName(int iWeaponId, char[] sBuffer, int iMaxLength) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_name", iWeaponId);
|
||||
|
||||
return GetTrieString(g_hMapWeapons, sKey, sBuffer, iMaxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @return The weapons index, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Weapon_GetIndex(int iWeaponId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_index", iWeaponId);
|
||||
|
||||
int iIndex = 0;
|
||||
if (!GetTrieValue(g_hMapWeapons, sKey, iIndex)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slot of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @return The weapons slot, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Weapon_GetSlot(int iWeaponId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_slot", iWeaponId);
|
||||
|
||||
int iSlot = 0;
|
||||
if (!GetTrieValue(g_hMapWeapons, sKey, iSlot)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iSlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the level of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @return The weapons level, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Weapon_GetLevel(int iWeaponId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_level", iWeaponId);
|
||||
|
||||
int iLevel = 0;
|
||||
if (!GetTrieValue(g_hMapWeapons, sKey, iLevel)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quality of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @return The weapons quality, or -1 on failure.
|
||||
*/
|
||||
|
||||
stock int Weapon_GetQuality(int iWeaponId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_quality", iWeaponId);
|
||||
|
||||
int iQuality = 0;
|
||||
if (!GetTrieValue(g_hMapWeapons, sKey, iQuality)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iQuality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the classname of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @param sBuffer The destination string buffer.
|
||||
* @param iMaxLength The maximum length of the output string buffer.
|
||||
* @return True on success, false if weapon was not found.
|
||||
*/
|
||||
|
||||
stock bool Weapon_GetClassname(int iWeaponId, char[] sBuffer, int iMaxLength) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_classname", iWeaponId);
|
||||
|
||||
return GetTrieString(g_hMapWeapons, sKey, sBuffer, iMaxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attributes of a weapon.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @param iAttributes The attribute id array.
|
||||
* @param iWeaponId The attribute value array.
|
||||
* @return The attribute count.
|
||||
*/
|
||||
|
||||
stock int Weapon_GetAttributes(int iWeaponId, int iAttributes[16], float iValues[16]) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_attributes", iWeaponId);
|
||||
|
||||
char sAttributes[256];
|
||||
if (GetTrieString(g_hMapWeapons, sKey, sAttributes, sizeof(sAttributes))) {
|
||||
char sAttributeParts[32][6];
|
||||
int iCount = ExplodeString(sAttributes, ";", sAttributeParts, sizeof(sAttributeParts), sizeof(sAttributeParts[]));
|
||||
|
||||
if (iCount % 2 != 0) {
|
||||
Log(TDLogLevel_Error, "Failed to parse weapon attributes for weapon id %d (some attribute has no value)", iWeaponId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iAttributeCount = 0;
|
||||
|
||||
for (int i = 0; i < iCount && iAttributeCount < 16; i += 2) {
|
||||
iAttributes[iAttributeCount] = StringToInt(sAttributeParts[i]);
|
||||
iValues[iAttributeCount] = StringToFloat(sAttributeParts[i + 1]);
|
||||
|
||||
iAttributeCount++;
|
||||
}
|
||||
|
||||
return iAttributeCount;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a weapon should preserve its attributes.
|
||||
*
|
||||
* @param iWeaponId The weapons id.
|
||||
* @return True if should preserve, false otherwise.
|
||||
*/
|
||||
|
||||
stock bool Weapon_GetPreserveAttributes(int iWeaponId) {
|
||||
char sKey[32];
|
||||
Format(sKey, sizeof(sKey), "%d_preserve_attributes", iWeaponId);
|
||||
|
||||
int iPreserve = 0;
|
||||
if (!GetTrieValue(g_hMapWeapons, sKey, iPreserve)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (iPreserve != 0);
|
||||
}
|
44
scripting/towerdefense/info/constants.sp
Normal file
44
scripting/towerdefense/info/constants.sp
Normal 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 ------*/
|
72
scripting/towerdefense/info/convars.sp
Normal file
72
scripting/towerdefense/info/convars.sp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
90
scripting/towerdefense/info/enums.sp
Normal file
90
scripting/towerdefense/info/enums.sp
Normal 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
|
||||
};
|
168
scripting/towerdefense/info/variables.sp
Normal file
168
scripting/towerdefense/info/variables.sp
Normal 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];
|
49
scripting/towerdefense/timers.sp
Normal file
49
scripting/towerdefense/timers.sp
Normal 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;
|
||||
}
|
178
scripting/towerdefense/util/log.sp
Normal file
178
scripting/towerdefense/util/log.sp
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
#if defined INFO_INCLUDES
|
||||
#include "../info/constants.sp"
|
||||
#include "../info/enums.sp"
|
||||
#include "../info/variables.sp"
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
237
scripting/towerdefense/util/metal.sp
Normal file
237
scripting/towerdefense/util/metal.sp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
48
scripting/towerdefense/util/steamid.sp
Normal file
48
scripting/towerdefense/util/steamid.sp
Normal 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;
|
||||
}
|
111
scripting/towerdefense/util/tf2items.sp
Normal file
111
scripting/towerdefense/util/tf2items.sp
Normal 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);
|
||||
}
|
307
scripting/towerdefense/util/zones.sp
Normal file
307
scripting/towerdefense/util/zones.sp
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user