Update plugins

This commit is contained in:
Jackz 2023-07-20 20:15:44 -05:00
parent cc9eb7d9d4
commit 52c3d04c89
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
25 changed files with 2163 additions and 870 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -120,13 +120,19 @@ public Action OnBanIdentity(const char[] identity, int time, int flags, const ch
}
return Plugin_Continue;
}
public Action OnBanClient(int client, int time, int flags, const char[] reason, const char[] kick_message, const char[] command, any source) {
if(GetUserAdmin(client) != INVALID_ADMIN_ID) return Plugin_Stop;
if(GetUserAdmin(client) != INVALID_ADMIN_ID) {
LogMessage("Ignoring OnBanClient with admin id as target");
return Plugin_Stop;
}
char executor[32], identity[32], ip[32];
GetClientAuthId(client, AuthId_Steam2, identity, sizeof(identity));
LogMessage("OnBanClient client=%d flags=%d source=%d, command=%s", client, flags, source, command);
DataPack pack;
if(source > 0 && source <= MaxClients && IsClientConnected(source) && GetClientAuthId(source, AuthId_Steam2, executor, sizeof(executor))) {
pack = new DataPack();
@ -168,6 +174,8 @@ public Action OnBanClient(int client, int time, int flags, const char[] reason,
PrintToServer("Adding %N to OnBanClient queue. Key: %d", client, key);
// TODO: REMOVE : FOR DEBUG
return Plugin_Handled;
return Plugin_Continue;
}

View file

@ -4,7 +4,7 @@
//Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 49
#define MAX_TROLLS 50
#endif
enum trollModifier {
@ -123,7 +123,9 @@ enum struct Troll {
}
int AddFlag(const char[] name, bool defaultOn) {
if(this.flagNames == null) this.flagNames = new ArrayList(MAX_TROLL_FLAG_LENGTH);
if(this.flagNames == null) {
this.flagNames = new ArrayList(MAX_TROLL_FLAG_LENGTH);
}
// Check if flag already added
int flagIndex = this.GetFlagIndex(name);
@ -131,8 +133,13 @@ enum struct Troll {
// Grab the prompt
static TrollFlagPrompt prompt;
// TODO: CHECK IF MISSING PROMPT
if(g_trollAddPromptIndex >= this.flagPrompts.Length) {
ThrowError("No prompt added for troll \"%s\", for flag \"%s\"", this.id, name);
}
this.flagPrompts.GetArray(g_trollAddPromptIndex, prompt);
prompt.flags |= (1 << flagIndex);
if(defaultOn) {

View file

@ -776,8 +776,10 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float&
if(IsTrollActive(victim, "Damage Boost")) {
damage * 2;
return Plugin_Changed;
} else if(Trolls[reverseFF].IsActive(attacker) && damagetype != DMG_BURN && attacker != victim && GetClientTeam(attacker) == GetClientTeam(victim)) {
} else if(Trolls[reverseFF].IsActive(attacker) && attacker != victim && GetClientTeam(attacker) == GetClientTeam(victim)
&& (damagetype == DMG_BURN && Trolls[reverseFF].activeFlagClients[attacker] & 32)
&& (damagetype == DMG_BLAST && Trolls[reverseFF].activeFlagClients[attacker] & 64)
) {
float returnDmg = damage; //default is 1:1
if(Trolls[reverseFF].activeFlagClients[attacker] & 2) {
returnDmg *= 2.0;

View file

@ -46,14 +46,14 @@ public int Insta_SpecialHandler(Menu menu, MenuAction action, int client, int pa
if(inFace) {
if(SpawnSpecialForTarget(special, target, view_as<int>(Special_OnTarget))) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ on \"%L\"", client, SPECIAL_NAMES[specialInt-1], target);
ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ on %N", SPECIAL_NAMES[specialInt-1], target);
CShowActivityEx(client, "[FTT] ", "spawned {olive}Insta-%s™{default} on {olive}%N", SPECIAL_NAMES[specialInt-1], target);
} else {
ReplyToCommand(client, "Could not spawn special.");
}
} else {
if(SpawnSpecialForTarget(special, target)) {
CShowActivityEx(client, "[FTT] ", "spawned {green}Insta-%s™{default} near {green}%N", SPECIAL_NAMES[specialInt-1], target);
LogAction(client, target, "\"%L\" spawned Insta-%s™ near \"%L\"", client, SPECIAL_NAMES[specialInt-1], target);
ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ near %N", SPECIAL_NAMES[specialInt-1], target);
} else {
ReplyToCommand(client, "Could not spawn special.");
}

View file

@ -27,6 +27,19 @@ public Action Timer_Main(Handle timer) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
if(Trolls[t_randomizeAnglesIndex].IsActive(i)) {
float chance = 0.10;
if(Trolls[t_randomizeAnglesIndex].activeFlagClients[i] & 2) chance = 0.35;
else if(Trolls[t_randomizeAnglesIndex].activeFlagClients[i] & 4) chance = 0.58;
else if(Trolls[t_randomizeAnglesIndex].activeFlagClients[i] & 8) chance = 0.90;
else if(Trolls[t_randomizeAnglesIndex].activeFlagClients[i] & 16) chance = 1.00;
if(GetURandomFloat() < chance) {
float ang[3];
ang[0] = GetRandomFloat(-180.0, 180.0);
ang[1] = GetRandomFloat(-180.0, 180.0);
TeleportEntity(i, NULL_VECTOR, ang, NULL_VECTOR);
}
}
if(Trolls[slowDrainIndex].IsActive(i)) {
if(loopTick % 4 == 0) {
int hp = GetClientHealth(i);
@ -34,7 +47,8 @@ public Action Timer_Main(Handle timer) {
SetEntProp(i, Prop_Send, "m_iHealth", hp - 1);
}
}
}else if(Trolls[tempHealthQuickDrainIndex].IsActive(i)) {
}
if(Trolls[tempHealthQuickDrainIndex].IsActive(i)) {
if(loopTick % 2 == 0) {
float bufferTime = GetEntPropFloat(i, Prop_Send, "m_healthBufferTime");
float tempHealth = L4D_GetTempHealth(i);
@ -42,13 +56,16 @@ public Action Timer_Main(Handle timer) {
SetEntPropFloat(i, Prop_Send, "m_healthBufferTime", bufferTime - 7.0);
}
}
}else if(Trolls[swarmIndex].IsActive(i)) {
}
if(Trolls[swarmIndex].IsActive(i)) {
L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", GetClientUserId(i), 15000);
} else if(Trolls[slipperyShoesIndex].IsActive(i) && Trolls[slipperyShoesIndex].activeFlagClients[i] & 1) {
}
if(Trolls[slipperyShoesIndex].IsActive(i) && Trolls[slipperyShoesIndex].activeFlagClients[i] & 1) {
if(GetRandomFloat() <= 0.4) {
L4D_StaggerPlayer(i, i, NULL_VECTOR);
}
} else if(Trolls[instantCommonIndex].IsActive(i)) {
}
if(Trolls[instantCommonIndex].IsActive(i)) {
int common = EntRefToEntIndex(instantCommonRef[i]);
if(common <= 0 || !IsValidEntity(common)) {
static float pos[3];
@ -58,7 +75,8 @@ public Action Timer_Main(Handle timer) {
}
SetEntPropEnt(common, Prop_Send, "m_clientLookatTarget", i);
SetEntProp(common, Prop_Send, "m_nSequence", 96);
} else if(loopTick % 60 && Trolls[randomizeAmmoIndex].IsActive(i)) {
}
if(loopTick % 60 && Trolls[randomizeAmmoIndex].IsActive(i)) {
int primaryWpn = GetPlayerWeaponSlot(i, 0);
if(primaryWpn > 0) {
int maxCap = GetEntProp(primaryWpn, Prop_Send, "m_iClip1");

View file

@ -3,6 +3,7 @@
int slipperyShoesIndex = 0;
int stickyGooIndex = 0;
int invertedTrollIndex;
int t_randomizeAnglesIndex;
// int fireSpitMagnetTrollIndex;
void SetupTrolls() {
@ -75,7 +76,10 @@ void SetupTrolls() {
Trolls[index].AddFlag("Biles", true);
Trolls[index].AddFlag("Molotovs", true);
Trolls[index].AddFlag("Pipebombs", true);
SetupTroll("Molotov Bath", "Throws a molotov on their feet", TrollMod_Instant);
index = SetupTroll("Molotov Bath", "Throws a molotov on their feet", TrollMod_Instant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Normal", true);
Trolls[index].AddFlag("Set the town ablaze", false);
// CATEGORY: Items
SetCategory("Items");
@ -149,6 +153,11 @@ void SetupTrolls() {
Trolls[index].AddFlag("0.5x Ratio", false); //4
Trolls[index].AddFlag("0.0x Ratio (None)", false); //8
Trolls[index].AddFlag("3x Ratio", false); //16
Trolls[index].AddCustomFlagPrompt("Modes", true);
Trolls[index].AddFlag("Reverse Fire Damage", false); //32
Trolls[index].AddFlag("Reverse Explosions", false); //64
index = SetupTroll("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Do not spawn extra", true); // 1
@ -187,6 +196,15 @@ void SetupTrolls() {
Trolls[index].AddFlag("On pills/adrenaline use", false);
Trolls[index].AddFlag("On zombie bite", false);
slipperyShoesIndex = index;
index = SetupTroll("Randomize Angles", "Randomly change their angles", TrollMod_Constant);
Trolls[index].AddCustomFlagPrompt("Frequency:", false);
Trolls[index].AddFlag("Once in a while", true); //1
Trolls[index].AddFlag("Periodically", false); //2
Trolls[index].AddFlag("A lot", false); //4
Trolls[index].AddFlag("Painful", false); //8
Trolls[index].AddFlag("Seizure", false); //16
t_randomizeAnglesIndex = index;
/// CATEGORY: MISC
SetCategory("Misc");
@ -364,6 +382,8 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
ang[0] = 90.0;
L4D_TankRockPrj(0, dropPos, ang);
} else if(StrEqual(troll.name, "Molotov Bath")) {
int count = 1;
if(flags & 2) count = 8;
float pos[3], dropPos[3];
GetClientEyePosition(victim, pos);
dropPos = pos;
@ -379,7 +399,9 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
}
float vel[3];
vel[2] = -90.0;
for(int i = 0; i < count; i++) {
L4D_MolotovPrj(victim, dropPos, vel);
}
} else if(StrEqual(troll.name, "Dep Bots")) {
if(!toActive) {
StopHealingBots();

View file

@ -59,6 +59,11 @@ char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
"prop_ragdoll"
};
#define MAX_FORBIDDEN_MODELS 1
char FORBIDDEN_MODELS[MAX_FORBIDDEN_MODELS][] = {
"models/props_vehicles/c130.mdl"
};
#define MAX_REVERSE_CLASSNAMES 2
static char REVERSE_CLASSNAMES[MAX_REVERSE_CLASSNAMES][] = {
"infected",
@ -66,6 +71,9 @@ static char REVERSE_CLASSNAMES[MAX_REVERSE_CLASSNAMES][] = {
};
public Action Command_DoAHat(int client, int args) {
static char cmdName[8];
GetCmdArg(0, cmdName, sizeof(cmdName));
int hatter = GetHatter(client);
if(hatter > 0) {
ClearHat(hatter, HasFlag(hatter, HAT_REVERSED));
@ -74,16 +82,16 @@ public Action Command_DoAHat(int client, int args) {
}
AdminId adminId = GetUserAdmin(client);
bool isForced = adminId != INVALID_ADMIN_ID && StrEqual(cmdName, "sm_hatf");
if(cvar_sm_hats_enabled.IntValue == 1) {
if(adminId == INVALID_ADMIN_ID) {
PrintToChat(client, "[Hats] Hats are for admins only");
return Plugin_Handled;
}
} else if(!adminId.HasFlag(Admin_Cheats)) {
PrintToChat(client, "[Hats] You do not have permission");
return Plugin_Handled;
}
if(cvar_sm_hats_enabled.IntValue == 0) {
} else if(cvar_sm_hats_enabled.IntValue == 0) {
ReplyToCommand(client, "[Hats] Hats are disabled");
return Plugin_Handled;
} else if(GetClientTeam(client) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) {
@ -239,6 +247,7 @@ public Action Command_DoAHat(int client, int args) {
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, vel);
// Sleep the physics after enoug time for it to most likely have landed
if(hatData[client].yeetGroundTimer != null) {
// TODO: FIX null issue
delete hatData[client].yeetGroundTimer;
}
DataPack pack1;
@ -335,8 +344,8 @@ public Action Command_DoAHat(int client, int args) {
}
// Make hat reversed if 'r' passed in
if(args > 0) {
char arg[4];
if(args > 0) {
GetCmdArg(1, arg, sizeof(arg));
if(arg[0] == 'r') {
flags |= view_as<int>(HAT_REVERSED);
@ -384,10 +393,14 @@ public Action Command_DoAHat(int client, int args) {
return Plugin_Handled;
}
}
if(!IsFakeClient(entity) && cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_PlayerHatConsent) && ~flags & view_as<int>(HAT_REVERSED)) {
if(!isForced &&
!IsFakeClient(entity) &&
cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_PlayerHatConsent) &&
~flags & view_as<int>(HAT_REVERSED)
) {
int lastRequestDiff = GetTime() - lastHatRequestTime[client];
if(lastRequestDiff < PLAYER_HAT_REQUEST_COOLDOWN) {
PrintToChat(client, "[Hats] Player hat under %d seconds cooldown", GetTime() - lastRequestDiff);
PrintToChat(client, "[Hats] Player hat under %d seconds cooldown", PLAYER_HAT_REQUEST_COOLDOWN - lastRequestDiff);
return Plugin_Handled;
}
@ -533,7 +546,7 @@ int GetHat(int client) {
int GetHatter(int client) {
for(int i = 1; i <= MaxClients; i++) {
if(EntRefToEntIndex(hatData[client].entity) == client) {
if(EntRefToEntIndex(hatData[i].entity) == client) {
return i;
}
}
@ -546,29 +559,43 @@ bool CanTarget(int victim) {
return StringToInt(buf) == 0;
}
bool IsHatAllowed(int client) {
bool IsHatAllowedInSaferoom(int client) {
if(L4D_IsMissionFinalMap()) return true;
char name[32];
GetEntityClassname(hatData[client].entity, name, sizeof(name));
// Don't allow non-weapons in saferoom
if(StrEqual(name, "prop_physics")) {
if(StrEqual(name, "prop_physics") || StrEqual(name, "prop_dynamic")) {
GetEntPropString(hatData[client].entity, Prop_Data, "m_ModelName", name, sizeof(name));
if(StrContains(name, "gnome") != -1) {
if(StrContains(name, "gnome") != -1 || StrContains(name, "propanecanist") != -1) {
return true;
}
PrintToConsole(client, "Dropping hat: prop_physics (%s)", name);
float mins[3], maxs[3];
GetEntPropVector(hatData[client].entity, Prop_Data, "m_vecMins", mins);
GetEntPropVector(hatData[client].entity, Prop_Data, "m_vecMaxs", maxs);
PrintToConsoleAll("Dropping hat for %N: prop_something (%s) (min %.0f,%.0f,%.0f) (max %.0f,%.0f,%.0f)", client, name, mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
return false;
}
else if(StrEqual(name, "player") || StrContains(name, "weapon_") > -1 || StrContains(name, "upgrade_") > -1) {
} else if(StrEqual(name, "player") || StrContains(name, "weapon_") > -1 || StrContains(name, "upgrade_") > -1) {
return true;
}
PrintToConsole(client, "Dropping hat: %s", name);
return false;
}
bool IsHatAllowed(int client) {
char name[32];
GetEntityClassname(hatData[client].entity, name, sizeof(name));
if(StrEqual(name, "prop_physics") || StrEqual(name, "prop_dynamic")) {
GetEntPropString(hatData[client].entity, Prop_Data, "m_ModelName", name, sizeof(name));
if(StrContains(name, "models/props_vehicles/c130.mdl") != -1) {
return false;
}
}
return true;
}
bool CanHatBePlaced(int client, const float pos[3]) {
if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats)) {
if(IsHatAllowed(client)) return true;
if(IsHatAllowedInSaferoom(client)) return true;
Address nav = L4D_GetNearestNavArea(pos, 200.0);
if(nav != Address_Null) {
int spawnFlags = L4D_GetNavArea_SpawnAttributes(nav) ;

View file

@ -396,7 +396,7 @@ stock bool IsPrimaryWeapon(const char[] wpnName) {
|| StrContains(wpnName, "shotgun") > -1;
}
stock int GetClientWeaponEntIndex(int client, int slot) {
if(slot >= 0 && slot <= 4) {
if(slot >= 0 && slot <= 5) {
int wpnRef = GetPlayerWeaponSlot(client, slot);
if(wpnRef != -1) {
int wpn = EntRefToEntIndex(wpnRef);
@ -409,7 +409,7 @@ stock int GetClientWeaponEntIndex(int client, int slot) {
return -1;
}
}else{
ThrowError("Slot must be a number between 0 and 4");
ThrowError("Slot must be a number between 0 and 5");
return -1;
}
}
@ -425,6 +425,19 @@ stock bool GetClientWeaponName(int client, int slot, char[] name, int nameSize)
return false;
}
}
stock bool GetClientWeaponNameSmart(int client, int slot, char[] name, int nameSize) {
int wpn = GetClientWeaponEntIndex(client, slot);
if(wpn > 0) {
GetEntityClassname(wpn, name, nameSize);
if(slot == 1 && StrEqual(name, "weapon_melee")) {
GetEntPropString(wpn, Prop_Data, "m_strMapSetScriptName", name, nameSize);
}
return true;
}else{
return false;
}
}
stock bool DoesClientHaveWeapon(int client, int slot, const char[] name) {
char wpn[32];
if(GetClientWeaponName(client, slot, wpn, sizeof(wpn))) {
@ -645,7 +658,7 @@ stock int GetRealClient(int client) {
stock int FindIdleBot(int client) {
for(int i = 1; i <= MaxClients; i++ ) {
if(IsClientConnected(i) && HasEntProp(i, Prop_Send, "m_humanSpectatorUserID")) {
if(IsClientConnected(i) && IsClientInGame(i) && HasEntProp(i, Prop_Send, "m_humanSpectatorUserID")) {
int realPlayer = GetClientOfUserId(GetEntProp(i, Prop_Send, "m_humanSpectatorUserID"));
if(realPlayer == client) {
return i;

File diff suppressed because it is too large Load diff

View file

@ -306,6 +306,34 @@ stock bool L4D_IsPlayerBurning(int client)
return (fBurning > 0.0) ? true : false;
}
/**
* @brief Returns true if a physics object or alarmed car can be moved by the tank
*
* @param entity Entity index to check
*
* @return true if it can be moved, false otherwise
*/
stock bool L4D_IsTankProp(int entity)
{
static char classname[16];
GetEdictClassname(entity, classname, sizeof(classname));
if( strcmp(classname, "prop_physics") == 0 )
{
if( GetEntProp(entity, Prop_Send, "m_hasTankGlow") )
{
return true;
}
}
else if( strcmp(classname, "prop_car_alarm") == 0 )
{
return true;
}
return false;
}
// ==================================================

View file

@ -433,7 +433,7 @@ stock int L4D_GetBotOfIdlePlayer(int client)
{
int idleClient;
int offset;
char netclass[128];
char netclass[64];
for (int bot = 1; bot <= MaxClients; bot++)
{
@ -474,7 +474,7 @@ stock int L4D_GetBotOfIdlePlayer(int client)
*/
stock int L4D_GetIdlePlayerOfBot(int bot)
{
char netclass[128];
char netclass[64];
GetEntityNetClass(bot, netclass, sizeof(netclass));
int offset = FindSendPropInfo(netclass, "m_humanSpectatorUserID");
@ -672,7 +672,7 @@ stock bool L4D2_SetEntityGlow(int entity, L4D2GlowType type, int range, int minR
return false;
}
char netclass[128];
char netclass[64];
GetEntityNetClass(entity, netclass, sizeof(netclass));
int offset = FindSendPropInfo(netclass, "m_iGlowType");
@ -1065,9 +1065,10 @@ stock void L4D_SetPlayerIsGoingToDie(int client, bool isGoingToDie)
*/
stock bool L4D2_IsWeaponUpgradeCompatible(int weapon)
{
char netclass[128];
GetEntityNetClass(weapon, netclass, sizeof(netclass));
return FindSendPropInfo(netclass, "m_upgradeBitVec") > 0;
if( HasEntProp(weapon, Prop_Send, "m_upgradeBitVec") )
return true;
return false;
}
/**
@ -1080,7 +1081,10 @@ stock bool L4D2_IsWeaponUpgradeCompatible(int weapon)
// L4D2 only.
stock int L4D2_GetWeaponUpgradeAmmoCount(int weapon)
{
return HasEntProp(weapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded") && GetEntProp(weapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded");
if( HasEntProp(weapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded") )
return GetEntProp(weapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded");
return 0;
}
/**
@ -1095,10 +1099,8 @@ stock int L4D2_GetWeaponUpgradeAmmoCount(int weapon)
stock void L4D2_SetWeaponUpgradeAmmoCount(int weapon, int count)
{
if( HasEntProp(weapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded") )
{
SetEntProp(weapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded", count);
}
}
/**
* Returns weapon upgrades of weapon.
@ -1110,7 +1112,10 @@ stock void L4D2_SetWeaponUpgradeAmmoCount(int weapon, int count)
// L4D2 only.
stock int L4D2_GetWeaponUpgrades(int weapon)
{
return HasEntProp(weapon, Prop_Send, "m_upgradeBitVec") && GetEntProp(weapon, Prop_Send, "m_upgradeBitVec");
if( HasEntProp(weapon, Prop_Send, "m_upgradeBitVec") )
return GetEntProp(weapon, Prop_Send, "m_upgradeBitVec");
return 0;
}
/**

View file

@ -7,7 +7,7 @@ void AddNote(int noteCreator, int noteTarget, const char[] message) {
char steamidCreator[32], steamidTarget[32];
GetClientAuthId(noteCreator, AuthId_Steam2, steamidCreator, sizeof(steamidCreator));
GetClientAuthId(noteTarget, AuthId_Steam2, steamidTarget, sizeof(steamidTarget));
AddNoteIdentity(steamidCreator, steamidTarget, message);
AddPlayerNoteIdentity(steamidCreator, steamidTarget, message);
}
native void AddNoteIdentity(const char noteCreator[32], const char noteTarget[32], const char[] message);
native void AddPlayerNoteIdentity(const char noteCreator[32], const char noteTarget[32], const char[] message);

View file

@ -0,0 +1,26 @@
#if defined _ripext_included_
#endinput
#endif
#define _ripext_included_
#include <ripext/json>
#include <ripext/http>
/**
* Do not edit below this line!
*/
public Extension __ext_rip =
{
name = "REST in Pawn",
file = "rip.ext",
#if defined AUTOLOAD_EXTENSIONS
autoload = 1,
#else
autoload = 0,
#endif
#if defined REQUIRE_EXTENSIONS
required = 1,
#else
required = 0,
#endif
};

View file

@ -0,0 +1,359 @@
enum HTTPStatus
{
HTTPStatus_Invalid = 0,
// 1xx Informational
HTTPStatus_Continue = 100,
HTTPStatus_SwitchingProtocols = 101,
// 2xx Success
HTTPStatus_OK = 200,
HTTPStatus_Created = 201,
HTTPStatus_Accepted = 202,
HTTPStatus_NonAuthoritativeInformation = 203,
HTTPStatus_NoContent = 204,
HTTPStatus_ResetContent = 205,
HTTPStatus_PartialContent = 206,
// 3xx Redirection
HTTPStatus_MultipleChoices = 300,
HTTPStatus_MovedPermanently = 301,
HTTPStatus_Found = 302,
HTTPStatus_SeeOther = 303,
HTTPStatus_NotModified = 304,
HTTPStatus_UseProxy = 305,
HTTPStatus_TemporaryRedirect = 307,
HTTPStatus_PermanentRedirect = 308,
// 4xx Client Error
HTTPStatus_BadRequest = 400,
HTTPStatus_Unauthorized = 401,
HTTPStatus_PaymentRequired = 402,
HTTPStatus_Forbidden = 403,
HTTPStatus_NotFound = 404,
HTTPStatus_MethodNotAllowed = 405,
HTTPStatus_NotAcceptable = 406,
HTTPStatus_ProxyAuthenticationRequired = 407,
HTTPStatus_RequestTimeout = 408,
HTTPStatus_Conflict = 409,
HTTPStatus_Gone = 410,
HTTPStatus_LengthRequired = 411,
HTTPStatus_PreconditionFailed = 412,
HTTPStatus_RequestEntityTooLarge = 413,
HTTPStatus_RequestURITooLong = 414,
HTTPStatus_UnsupportedMediaType = 415,
HTTPStatus_RequestedRangeNotSatisfiable = 416,
HTTPStatus_ExpectationFailed = 417,
HTTPStatus_MisdirectedRequest = 421,
HTTPStatus_TooEarly = 425,
HTTPStatus_UpgradeRequired = 426,
HTTPStatus_PreconditionRequired = 428,
HTTPStatus_TooManyRequests = 429,
HTTPStatus_RequestHeaderFieldsTooLarge = 431,
HTTPStatus_UnavailableForLegalReasons = 451,
// 5xx Server Error
HTTPStatus_InternalServerError = 500,
HTTPStatus_NotImplemented = 501,
HTTPStatus_BadGateway = 502,
HTTPStatus_ServiceUnavailable = 503,
HTTPStatus_GatewayTimeout = 504,
HTTPStatus_HTTPVersionNotSupported = 505,
HTTPStatus_VariantAlsoNegotiates = 506,
HTTPStatus_NotExtended = 510,
HTTPStatus_NetworkAuthenticationRequired = 511,
};
typeset HTTPRequestCallback
{
function void (HTTPResponse response, any value);
function void (HTTPResponse response, any value, const char[] error);
};
typeset HTTPFileCallback
{
function void (HTTPStatus status, any value);
function void (HTTPStatus status, any value, const char[] error);
};
methodmap HTTPRequest < Handle
{
// Creates an HTTP request.
//
// The Handle is automatically freed when the request is performed.
// Otherwise, the Handle must be freed via delete or CloseHandle().
//
// @param url URL to the REST API endpoint.
public native HTTPRequest(const char[] url);
// Appends a parameter to the form data.
//
// The parameter name and value are encoded according to RFC 3986.
//
// @param name Parameter name.
// @param format Formatting rules.
// @param ... Variable number of format parameters.
public native void AppendFormParam(const char[] name, const char[] format, any ...);
// Appends a query parameter to the URL.
//
// The parameter name and value are encoded according to RFC 3986.
//
// @param name Parameter name.
// @param format Formatting rules.
// @param ... Variable number of format parameters.
public native void AppendQueryParam(const char[] name, const char[] format, any ...);
// Sets the credentials for HTTP Basic authentication.
//
// @param username Username to use.
// @param password Password to use.
public native void SetBasicAuth(const char[] username, const char[] password);
// Sets an HTTP header.
//
// @param name Header name.
// @param format Formatting rules.
// @param ... Variable number of format parameters.
public native void SetHeader(const char[] name, const char[] format, any ...);
// Performs an HTTP GET request.
//
// This function closes the request Handle after completing.
//
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
public native void Get(HTTPRequestCallback callback, any value = 0);
// Performs an HTTP POST request.
//
// This function closes the request Handle after completing.
//
// @param data JSON data to send.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
public native void Post(JSON data, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP PUT request.
//
// This function closes the request Handle after completing.
//
// @param data JSON data to send.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
public native void Put(JSON data, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP PATCH request.
//
// This function closes the request Handle after completing.
//
// @param data JSON data to send.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
public native void Patch(JSON data, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP DELETE request.
//
// This function closes the request Handle after completing.
//
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
public native void Delete(HTTPRequestCallback callback, any value = 0);
// Downloads a file.
//
// This function closes the request Handle after completing.
//
// @param path File path to write to.
// @param callback A function to use as a callback when the download has finished.
// @param value Optional value to pass to the callback function.
public native void DownloadFile(const char[] path, HTTPFileCallback callback, any value = 0);
// Uploads a file.
//
// This function performs an HTTP PUT request. The file contents are sent in the request body.
// This function closes the request Handle after completing.
//
// @param path File path to read from.
// @param callback A function to use as a callback when the upload has finished.
// @param value Optional value to pass to the callback function.
public native void UploadFile(const char[] path, HTTPFileCallback callback, any value = 0);
// Performs an HTTP POST request with form data.
//
// This function closes the request Handle after completing.
//
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
public native void PostForm(HTTPRequestCallback callback, any value = 0);
// Connect timeout in seconds. Defaults to 10.
property int ConnectTimeout {
public native get();
public native set(int connectTimeout);
}
// Maximum number of redirects to follow. Defaults to 5.
property int MaxRedirects {
public native get();
public native set(int maxRedirects);
}
// Maximum download speed in bytes per second. Defaults to unlimited speed.
property int MaxRecvSpeed {
public native get();
public native set(int maxSpeed);
}
// Maximum upload speed in bytes per second. Defaults to unlimited speed.
property int MaxSendSpeed {
public native get();
public native set(int maxSpeed);
}
// Timeout in seconds. Defaults to 30.
property int Timeout {
public native get();
public native set(int timeout);
}
}
methodmap HTTPResponse
{
// Retrieves an HTTP header from the response.
//
// @param name Header name.
// @param buffer String buffer to store value.
// @param maxlength Maximum length of the string buffer.
// @return True on success, false if the header was not found.
public native bool GetHeader(const char[] name, char[] buffer, int maxlength);
// Retrieves the JSON data of the response.
//
// @error Invalid JSON response.
property JSON Data {
public native get();
}
// Retrieves the HTTP status of the response.
property HTTPStatus Status {
public native get();
}
};
// Deprecated. Use HTTPRequest instead.
methodmap HTTPClient < Handle
{
// Creates an HTTP client.
//
// The HTTPClient must be freed via delete or CloseHandle().
//
// @param baseURL Base URL to the REST API.
#pragma deprecated Use HTTPRequest instead.
public native HTTPClient(const char[] baseURL);
// Sets an HTTP header to be used for all requests.
//
// @param name Header name.
// @param value String value to set.
#pragma deprecated Use HTTPRequest.SetHeader() instead.
public native void SetHeader(const char[] name, const char[] value);
// Performs an HTTP GET request.
//
// @param endpoint API endpoint to request.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.Get() instead.
public native void Get(const char[] endpoint, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP POST request.
//
// @param endpoint API endpoint to request.
// @param data JSON data to send.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.Post() instead.
public native void Post(const char[] endpoint, JSON data, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP PUT request.
//
// @param endpoint API endpoint to request.
// @param data JSON data to send.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.Put() instead.
public native void Put(const char[] endpoint, JSON data, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP PATCH request.
//
// @param endpoint API endpoint to request.
// @param data JSON data to send.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.Patch() instead.
public native void Patch(const char[] endpoint, JSON data, HTTPRequestCallback callback, any value = 0);
// Performs an HTTP DELETE request.
//
// @param endpoint API endpoint to request.
// @param callback A function to use as a callback when the request has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.Delete() instead.
public native void Delete(const char[] endpoint, HTTPRequestCallback callback, any value = 0);
// Downloads a file.
//
// @param endpoint API endpoint to download from.
// @param path File path to write to.
// @param callback A function to use as a callback when the download has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.DownloadFile() instead.
public native void DownloadFile(const char[] endpoint, const char[] path, HTTPFileCallback callback, any value = 0);
// Uploads a file.
//
// This function performs an HTTP PUT request. The file contents are sent in the request body.
//
// @param endpoint API endpoint to upload to.
// @param path File path to read from.
// @param callback A function to use as a callback when the upload has finished.
// @param value Optional value to pass to the callback function.
#pragma deprecated Use HTTPRequest.UploadFile() instead.
public native void UploadFile(const char[] endpoint, const char[] path, HTTPFileCallback callback, any value = 0);
// Connect timeout in seconds. Defaults to 10.
#pragma deprecated Use HTTPRequest.ConnectTimeout instead.
property int ConnectTimeout {
public native get();
public native set(int connectTimeout);
}
// Follow HTTP 3xx redirects. Defaults to true.
#pragma deprecated Use HTTPRequest.MaxRedirects instead.
property bool FollowLocation {
public native get();
public native set(bool followLocation);
}
// Timeout in seconds. Defaults to 30.
#pragma deprecated Use HTTPRequest.Timeout instead.
property int Timeout {
public native get();
public native set(int timeout);
}
// Maximum upload speed in bytes per second. Defaults to unlimited speed.
#pragma deprecated Use HTTPRequest.MaxSendSpeed instead.
property int MaxSendSpeed {
public native get();
public native set(int speed);
}
// Maximum download speed in bytes per second. Defaults to unlimited speed.
#pragma deprecated Use HTTPRequest.MaxRecvSpeed instead.
property int MaxRecvSpeed {
public native get();
public native set(int speed);
}
};

View file

@ -0,0 +1,400 @@
// Decoding flags
enum
{
JSON_REJECT_DUPLICATES = 0x1, /**< Error if any JSON object contains duplicate keys */
JSON_DISABLE_EOF_CHECK = 0x2, /**< Allow extra data after a valid JSON array or object */
JSON_DECODE_ANY = 0x4, /**< Decode any value */
JSON_DECODE_INT_AS_REAL = 0x8, /**< Interpret all numbers as floats */
JSON_ALLOW_NUL = 0x10 /**< Allow \u0000 escape inside string values */
};
// Encoding flags
enum
{
JSON_COMPACT = 0x20, /**< Compact representation */
JSON_ENSURE_ASCII = 0x40, /**< Escape all Unicode characters outside the ASCII range */
JSON_SORT_KEYS = 0x80, /**< Sort object keys */
JSON_ENCODE_ANY = 0x200, /**< Encode any value */
JSON_ESCAPE_SLASH = 0x400, /**< Escape / with \/ */
JSON_EMBED = 0x10000 /**< Omit opening and closing braces of the top-level object */
};
// Maximum indentation
static const int JSON_MAX_INDENT = 0x1F;
// Pretty-print the result, indenting with n spaces
stock int JSON_INDENT(int n)
{
return n & JSON_MAX_INDENT;
}
// Output floats with at most n digits of precision
stock int JSON_REAL_PRECISION(int n)
{
return (n & 0x1F) << 11;
}
// Generic type for encoding JSON.
methodmap JSON < Handle
{
// Writes the JSON string representation to a file.
//
// @param file File to write to.
// @param flags Encoding flags.
// @return True on success, false on failure.
public native bool ToFile(const char[] file, int flags = 0);
// Retrieves the JSON string representation.
//
// @param buffer String buffer to write to.
// @param maxlength Maximum length of the string buffer.
// @param flags Encoding flags.
// @return True on success, false on failure.
public native bool ToString(char[] buffer, int maxlength, int flags = 0);
};
methodmap JSONObject < JSON
{
// Creates a JSON object. A JSON object maps strings (called "keys") to values. Keys in a
// JSON object are unique. That is, there is at most one entry in the map for a given key.
//
// The JSONObject must be freed via delete or CloseHandle().
public native JSONObject();
// Loads a JSON object from a file.
//
// @param file File to read from.
// @param flags Decoding flags.
// @return Object handle, or null on failure.
// @error Invalid JSON.
public static native JSONObject FromFile(const char[] file, int flags = 0);
// Loads a JSON object from a string.
//
// @param buffer String buffer to load into the JSON object.
// @param flags Decoding flags.
// @return Object handle, or null on failure.
// @error Invalid JSON.
public static native JSONObject FromString(const char[] buffer, int flags = 0);
// Retrieves an array or object value from the object.
//
// The JSON must be freed via delete or CloseHandle().
//
// @param key Key string.
// @return Value read.
// @error Invalid key.
public native JSON Get(const char[] key);
// Retrieves a boolean value from the object.
//
// @param key Key string.
// @return Value read.
// @error Invalid key.
public native bool GetBool(const char[] key);
// Retrieves a float value from the object.
//
// @param key Key string.
// @return Value read.
// @error Invalid key.
public native float GetFloat(const char[] key);
// Retrieves an integer value from the object.
//
// @param key Key string.
// @return Value read.
// @error Invalid key.
public native int GetInt(const char[] key);
// Retrieves a 64-bit integer value from the object.
//
// @param key Key string.
// @param buffer String buffer to store value.
// @param maxlength Maximum length of the string buffer.
// @return True on success, false if the key was not found.
public native bool GetInt64(const char[] key, char[] buffer, int maxlength);
// Retrieves a string value from the object.
//
// @param key Key string.
// @param buffer String buffer to store value.
// @param maxlength Maximum length of the string buffer.
// @return True on success. False if the key was not found, or the value is not a string.
public native bool GetString(const char[] key, char[] buffer, int maxlength);
// Returns whether or not a value in the object is null.
//
// @param key Key string.
// @return True if the value is null, false otherwise.
// @error Invalid key.
public native bool IsNull(const char[] key);
// Returns whether or not a key exists in the object.
//
// @param key Key string.
// @return True if the key exists, false otherwise.
public native bool HasKey(const char[] key);
// Sets an array or object value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @param value Value to store at this key.
// @return True on success, false on failure.
public native bool Set(const char[] key, JSON value);
// Sets a boolean value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @param value Value to store at this key.
// @return True on success, false on failure.
public native bool SetBool(const char[] key, bool value);
// Sets a float value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @param value Value to store at this key.
// @return True on success, false on failure.
public native bool SetFloat(const char[] key, float value);
// Sets an integer value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @param value Value to store at this key.
// @return True on success, false on failure.
public native bool SetInt(const char[] key, int value);
// Sets a 64-bit integer value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @param value Value to store at this key.
// @return True on success, false on failure.
public native bool SetInt64(const char[] key, const char[] value);
// Sets a null value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @return True on success, false on failure.
public native bool SetNull(const char[] key);
// Sets a string value in the object, either inserting a new entry or replacing an old one.
//
// @param key Key string.
// @param value Value to store at this key.
// @return True on success, false on failure.
public native bool SetString(const char[] key, const char[] value);
// Removes an entry from the object.
//
// @param key Key string.
// @return True on success, false if the key was not found.
public native bool Remove(const char[] key);
// Clears the object of all entries.
// @return True on success, false on failure.
public native bool Clear();
// Returns an iterator for the object's keys. See JSONObjectKeys.
public native JSONObjectKeys Keys();
// Retrieves the size of the object.
property int Size {
public native get();
}
};
/**
* A JSONObjectKeys is created via JSONObject.Keys(). It allows the keys of an
* object to be iterated. The JSONObjectKeys must be freed with delete or
* CloseHandle().
*/
methodmap JSONObjectKeys < Handle
{
// Reads an object key, then advances to the next key if any.
//
// @param buffer String buffer to store key.
// @param maxlength Maximum length of the string buffer.
// @return True on success, false if there are no more keys.
public native bool ReadKey(char[] buffer, int maxlength);
};
methodmap JSONArray < JSON
{
// Creates a JSON array.
//
// The JSONArray must be freed via delete or CloseHandle().
public native JSONArray();
// Loads a JSON array from a file.
//
// @param file File to read from.
// @param flags Decoding flags.
// @return Array handle, or null on failure.
// @error Invalid JSON.
public static native JSONArray FromFile(const char[] file, int flags = 0);
// Loads a JSON array from a string.
//
// @param buffer String buffer to load into the JSON array.
// @param flags Decoding flags.
// @return Array handle, or null on failure.
// @error Invalid JSON.
public static native JSONArray FromString(const char[] buffer, int flags = 0);
// Retrieves an array or object value from the array.
//
// The JSON must be freed via delete or CloseHandle().
//
// @param index Index in the array.
// @return Value read.
// @error Invalid index.
public native JSON Get(int index);
// Retrieves a boolean value from the array.
//
// @param index Index in the array.
// @return Value read.
// @error Invalid index.
public native bool GetBool(int index);
// Retrieves a float value from the array.
//
// @param index Index in the array.
// @return Value read.
// @error Invalid index.
public native float GetFloat(int index);
// Retrieves an integer value from the array.
//
// @param index Index in the array.
// @return Value read.
// @error Invalid index.
public native int GetInt(int index);
// Retrieves an 64-bit integer value from the array.
//
// @param index Index in the array.
// @param buffer Buffer to copy to.
// @param maxlength Maximum size of the buffer.
// @error Invalid index.
public native void GetInt64(int index, char[] buffer, int maxlength);
// Retrieves a string value from the array.
//
// @param index Index in the array.
// @param buffer Buffer to copy to.
// @param maxlength Maximum size of the buffer.
// @return True on success, false if the value is not a string.
// @error Invalid index.
public native bool GetString(int index, char[] buffer, int maxlength);
// Returns whether or not a value in the array is null.
//
// @param index Index in the array.
// @return True if the value is null, false otherwise.
// @error Invalid index.
public native bool IsNull(int index);
// Sets an array or object value in the array.
//
// @param index Index in the array.
// @param value Value to set.
// @return True on success, false on failure.
public native bool Set(int index, JSON value);
// Sets a boolean value in the array.
//
// @param index Index in the array.
// @param value Value to set.
// @return True on success, false on failure.
public native bool SetBool(int index, bool value);
// Sets a float value in the array.
//
// @param index Index in the array.
// @param value Value to set.
// @return True on success, false on failure.
public native bool SetFloat(int index, float value);
// Sets an integer value in the array.
//
// @param index Index in the array.
// @param value Value to set.
// @return True on success, false on failure.
public native bool SetInt(int index, int value);
// Sets a 64 bit integer value in the array.
//
// @param index Index in the array.
// @param value 64-bit integer value to set.
// @return True on success, false on failure.
public native bool SetInt64(int index, const char[] value);
// Sets a null value in the array.
//
// @param index Index in the array.
// @return True on success, false on failure.
public native bool SetNull(int index);
// Sets a string value in the array.
//
// @param index Index in the array.
// @param value String value to set.
// @return True on success, false on failure.
public native bool SetString(int index, const char[] value);
// Pushes an array or object value onto the end of the array, adding a new index.
//
// @param value Value to push.
// @return True on success, false on failure.
public native bool Push(JSON value);
// Pushes a boolean value onto the end of the array, adding a new index.
//
// @param value Value to push.
// @return True on success, false on failure.
public native bool PushBool(bool value);
// Pushes a float value onto the end of the array, adding a new index.
//
// @param value Value to push.
// @return True on success, false on failure.
public native bool PushFloat(float value);
// Pushes an integer value onto the end of the array, adding a new index.
//
// @param value Value to push.
// @return True on success, false on failure.
public native bool PushInt(int value);
// Pushes a 64-bit integer value onto the end of the array, adding a new index.
//
// @param value 64-bit integer value to push.
// @return True on success, false on failure.
public native bool PushInt64(const char[] value);
// Pushes a null value onto the end of the array, adding a new index.
// @return True on success, false on failure.
public native bool PushNull();
// Pushes a string value onto the end of the array, adding a new index.
//
// @param value String value to push.
// @return True on success, false on failure.
public native bool PushString(const char[] value);
// Removes an entry from the array.
//
// @param index Index in the array to remove.
// @return True on success, false on invalid index.
public native bool Remove(int index);
// Clears the array of all entries.
// @return True on success, false on failure.
public native bool Clear();
// Retrieves the size of the array.
property int Length {
public native get();
}
};

View file

@ -119,6 +119,7 @@ public Action Event_ButtonPress(const char[] output, int entity, int client, flo
Group groups[MAX_GROUPS];
GroupResult result;
ComputeGroups(groups, result, activatorFlow);
PrintToConsoleAll("[CC] Button Press by %N", client);
AdminId admin = GetUserAdmin(client);
if(admin != INVALID_ADMIN_ID && admin.HasFlag(Admin_Custom1)) {
@ -139,7 +140,6 @@ public Action Event_ButtonPress(const char[] output, int entity, int client, flo
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos);
PrintToConsoleAll("[CC] Button Press by %N", client);
if(hEnabled.IntValue == 2 || !IsActivationAllowed(activatorFlow, 1500.0)) {
ClientCommand(client, "play ui/menu_invalid.wav");
PrintToChat(client, "Please wait for players to catch up.");
@ -299,6 +299,7 @@ public Action L4D2_CGasCan_EventKilled(int gascan, int &inflictor, int &attacker
float activatorFlow = L4D2Direct_GetFlowDistance(attacker);
Group groups[MAX_GROUPS];
GroupResult result;
PrintToConsoleAll("[CC] Gascan Shot by %N", attacker);
ComputeGroups(groups, result, activatorFlow);
AdminId admin = GetUserAdmin(attacker);
@ -383,6 +384,6 @@ stock void PrintDebug(const char[] format, any ... ) {
VFormat(buffer, sizeof(buffer), format, 2);
// PrintToServer("[CrescendoControl:Debug] %s", buffer);
PrintToConsoleAll("[CrescendoControl:Debug] %s", buffer);
LogMessage("%s", buffer);
LogToFile("crescendo_control.log", "%s", buffer);
#endif
}

View file

@ -32,6 +32,7 @@
#define EXTRA_TANK_MIN_SEC 2.0
#define EXTRA_TANK_MAX_SEC 20.0
#define DATE_FORMAT "%F at %I:%M %p"
@ -77,14 +78,17 @@ public Plugin myinfo =
url = "https://github.com/Jackzmc/sourcemod-plugins"
};
static ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime, hSplitTankChance, cvFFDecreaseRate, cvZDifficulty;
static ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime, hSplitTankChance, cvFFDecreaseRate, cvZDifficulty, cvEPIHudFlags;
static int extraKitsAmount, extraKitsStarted, abmExtraCount, firstSaferoomDoorEntity, playersLoadedIn, playerstoWaitFor;
static int currentChapter;
static bool isCheckpointReached, isLateLoaded, firstGiven, isFailureRound, areItemsPopulated;
static ArrayList ammoPacks;
static Handle updateHudTimer;
static bool showHudPingMode;
static int hudModeTicks;
static char gamemode[32];
bool isCoop;
enum Difficulty {
@ -107,15 +111,70 @@ enum State {
char StateNames[3][] = {
"Empty",
"PendingEmpty",
"Actve"
"Active"
};
#endif
#define HUD_NAME_LENGTH 8
stock float SnapTo(const float value, const float degree) {
return float(RoundFloat(value / degree)) * degree;
}
stock int StrLenMB(const char[] str){
int len = strlen(str);
int count;
for(int i; i < len; i++) {
count += ((str[i] & 0xc0) != 0x80) ? 1 : 0;
}
return count;
}
enum struct PlayerData {
bool itemGiven; //Is player being given an item (such that the next pickup event is ignored)
bool isUnderAttack; //Is the player under attack (by any special)
State state;
bool hasJoined;
char nameCache[64];
int scrollIndex;
int scrollMax;
void Setup(int client) {
char name[32];
GetClientName(client, name, sizeof(name));
this.scrollMax = strlen(name);
for(int i = 0; i < this.scrollMax; i++) {
// if(IsCharMB(name[i])) {
// this.scrollMax--;
// name[i] = '\0';
// }
}
Format(this.nameCache, 64, "%s %s", name, name);
this.ResetScroll();
}
void ResetScroll() {
this.scrollIndex = 0;
// TOOD: figure out keeping unicode symbols and not scrolling when 7 characters view
// this.scrollMax = strlen(name);
// this.scrollMax = RoundFloat(SnapTo(float(this.scrollMax), float(HUD_NAME_LENGTH)));
if(this.scrollMax >= 32) {
this.scrollMax = 31;
}
}
void AdvanceScroll() {
if(cvEPIHudFlags.IntValue & 1) {
if(this.scrollMax > HUD_NAME_LENGTH) {
this.scrollIndex += 1;
// TODO: if name is < 8
if(this.scrollIndex >= this.scrollMax) {
this.scrollIndex = 0;
}
}
}
}
}
enum struct PlayerInventory {
@ -131,6 +190,8 @@ enum struct PlayerInventory {
char model[64];
int survivorType;
float location[3];
}
PlayerData playerData[MAXPLAYERS+1];
@ -150,6 +211,7 @@ Restore from saved inventory
static StringMap weaponMaxClipSizes;
static StringMap pInv;
static char HUD_SCRIPT_DATA[] = "eph <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"%s\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT | g_ModeScript.HUD_FLAG_TEAM_SURVIVORS | g_ModeScript.HUD_FLAG_NOBG } } }\nHUDSetLayout(eph)\nHUDPlace(g_ModeScript.HUD_RIGHT_BOT,0.78,0.77,0.3,0.3)\ng_ModeScript;";
static char HUD_SCRIPT_CLEAR[] = "g_ModeScript._eph <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT|g_ModeScript.HUD_FLAG_TEAM_SURVIVORS|g_ModeScript.HUD_FLAG_NOBG } } };HUDSetLayout( g_ModeScript._eph );g_ModeScript";
@ -195,6 +257,7 @@ public void OnPluginStart() {
HookEvent("tank_spawn", Event_TankSpawn);
//Special Event Tracking
HookEvent("player_info", Event_PlayerInfo);
HookEvent("player_disconnect", Event_PlayerDisconnect);
HookEvent("charger_carry_start", Event_ChargerCarry);
@ -223,8 +286,11 @@ public void OnPluginStart() {
hSplitTankChance = CreateConVar("l4d2_extraitems_splittank_chance", "0.75", "The % chance of a split tank occurring in non-finales", FCVAR_NONE, true, 0.0, true, 1.0);
cvDropDisconnectTime = CreateConVar("l4d2_extraitems_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
cvFFDecreaseRate = CreateConVar("l4d2_extraitems_ff_decrease_rate", "0.3", "The friendly fire factor is subtracted from the formula (playerCount-4) * this rate. Effectively reduces ff penalty when more players. 0.0 to subtract none", FCVAR_NONE, true, 0.0);
cvEPIHudFlags = CreateConVar("l4d2_extraitems_hud_flags", "3", "Add together.\n1 = Scrolling hud, 2 = Show ping", FCVAR_NONE, true, 0.0);
// TODO: hook flags, reset name index / ping mode
cvEPIHudFlags.AddChangeHook(Cvar_HudFlagChange);
hEPIHudState.AddChangeHook(Cvar_HudStateChange);
cvEPIHudFlags.AddChangeHook(Cvar_HudStateChange);
if(hUpdateMinPlayers.BoolValue) {
hMinPlayers = FindConVar("abm_minplayers");
@ -233,10 +299,13 @@ public void OnPluginStart() {
if(isLateLoaded) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
UpdatePlayerInventory(i);
if(IsClientConnected(i) && IsClientInGame(i)) {
if(GetClientTeam(i) == 2) {
SaveInventory(i);
SDKHook(i, SDKHook_WeaponEquip, Event_Pickup);
}
playerData[i].Setup(i);
}
}
int count = GetRealSurvivorsCount();
@ -272,24 +341,38 @@ public void OnPluginStart() {
RegAdminCmd("sm_epi_lock", Command_ToggleDoorLocks, ADMFLAG_CHEATS, "Toggle all toggle\'s lock state");
RegAdminCmd("sm_epi_kits", Command_GetKitAmount, ADMFLAG_CHEATS);
RegAdminCmd("sm_epi_items", Command_RunExtraItems, ADMFLAG_CHEATS);
RegConsoleCmd("sm_epi_status", Command_DebugStatus);
RegConsoleCmd("sm_epi_stats", Command_DebugStats);
#endif
RegAdminCmd("sm_epi_restore", Command_RestoreInventory, ADMFLAG_KICK);
RegAdminCmd("sm_epi_save", Command_SaveInventory, ADMFLAG_KICK);
CreateTimer(10.0, Timer_ForceUpdateInventories, _, TIMER_REPEAT);
CreateTimer(30.0, Timer_ForceUpdateInventories, _, TIMER_REPEAT);
}
public Action Timer_ForceUpdateInventories(Handle h) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
UpdatePlayerInventory(i);
// SaveInventory(i);
}
}
return Plugin_Continue;
}
public void OnClientPutInServer(int client) {
if(!IsFakeClient(client) && GetClientTeam(client) == 2 && !StrEqual(gamemode, "hideandseek")) {
CreateTimer(0.2, Timer_CheckInventory, client);
if(!IsFakeClient(client)) {
playerData[client].Setup(client);
if(GetClientTeam(client) == 2) {
if(!StrEqual(gamemode, "hideandseek")) {
// CreateTimer(0.2, Timer_CheckInventory, client);
}
}
}
}
public void OnClientDisconnect(int client) {
if(!IsFakeClient(client)) {
SaveInventory(client);
}
}
@ -337,21 +420,33 @@ public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcas
// CVAR HOOKS
///////////////////////////////////////////////////////////////////////////////
public void Cvar_HudStateChange(ConVar convar, const char[] oldValue, const char[] newValue) {
if(convar.IntValue == 0 && updateHudTimer != null) {
if(convar.IntValue == 0) {
if(updateHudTimer != null) {
PrintToServer("[EPI] Stopping timer externally: Cvar changed to 0");
delete updateHudTimer;
}
} else {
int count = GetRealSurvivorsCount();
int threshold = 0;
// Default to 0 for state == 2 (force)
if(hEPIHudState.IntValue == 1) {
// On L4D1 map start if 5 players, on L4D2 start with 6
// On L4D1 more chance of duplicate models, so can't see health
threshold = L4D2_GetSurvivorSetMap() == 2 ? 4 : 5;
}
if(convar.IntValue > 0 && count > threshold && updateHudTimer == null) {
if(count > threshold && updateHudTimer == null) {
PrintToServer("[EPI] Creating new hud timer");
updateHudTimer = CreateTimer(EXTRA_PLAYER_HUD_UPDATE_INTERVAL, Timer_UpdateHud, _, TIMER_REPEAT);
}
}
}
void Cvar_HudFlagChange(ConVar convar, const char[] oldValue, const char[] newValue) {
hudModeTicks = 0;
showHudPingMode = false;
for(int i = 0; i <= MaxClients; i++) {
playerData[i].ResetScroll();
}
}
public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) {
cvar.GetString(gamemode, sizeof(gamemode));
@ -389,6 +484,50 @@ public void Event_DifficultyChange(ConVar cvar, const char[] oldValue, const cha
/////////////////////////////////////
/// COMMANDS
////////////////////////////////////
Action Command_SaveInventory(int client, int args) {
if(args == 0) {
ReplyToCommand(client, "Syntax: /epi_save <player>");
return Plugin_Handled;
}
char arg[32];
GetCmdArg(1, arg, sizeof(arg));
int player = GetSinglePlayer(client, arg, COMMAND_FILTER_NO_BOTS);
if(player == -1) {
ReplyToCommand(client, "No player found");
return Plugin_Handled;
}
SaveInventory(player);
ReplyToCommand(client, "Saved inventory for %N", player);
}
Action Command_RestoreInventory(int client, int args) {
if(args == 0) {
ReplyToCommand(client, "Syntax: /epi_restore <player> <full/pos/model/items>");
return Plugin_Handled;
}
char arg[32];
GetCmdArg(1, arg, sizeof(arg));
int player = GetSinglePlayer(client, arg, COMMAND_FILTER_NO_BOTS);
if(player == -1) {
ReplyToCommand(client, "No player found");
return Plugin_Handled;
}
GetCmdArg(2, arg, sizeof(arg));
PlayerInventory inv;
if(!GetLatestInventory(client, inv)) {
ReplyToCommand(client, "No stored inventory for player");
return Plugin_Handled;
}
if(StrEqual(arg, "full")) {
RestoreInventory(client, inv);
} else if(StrEqual(arg, "pos")) {
TeleportEntity(player, inv.location, NULL_VECTOR, NULL_VECTOR);
} else {
ReplyToCommand(client, "Syntax: /epi_restore <player> <full/pos/model/items>");
return Plugin_Handled;
}
return Plugin_Handled;
}
public Action Command_SetSurvivorCount(int client, int args) {
int oldCount = abmExtraCount;
if(args > 0) {
@ -452,13 +591,48 @@ public Action Command_RunExtraItems(int client, int args) {
PopulateItems();
return Plugin_Handled;
}
public Action Command_DebugStatus(int client, int args) {
public Action Command_DebugStats(int client, int args) {
if(args == 0) {
ReplyToCommand(client, "Player Statuses:");
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && !IsFakeClient(i)) {
ReplyToCommand(i, "\t%d. %N: %s", i, i, StateNames[view_as<int>(playerData[i].state)]);
ReplyToCommand(client, "\t\x04%d. %N:\x05 %s", i, i, StateNames[view_as<int>(playerData[i].state)]);
}
}
} else {
char arg[32];
GetCmdArg(1, arg, sizeof(arg));
PlayerInventory inv;
int player = GetSinglePlayer(client, arg, COMMAND_FILTER_NO_BOTS);
if(player == 0) {
if(!GetInventory(arg, inv)) {
ReplyToCommand(client, "No player found");
return Plugin_Handled;
}
}
if(!GetLatestInventory(player, inv)) {
ReplyToCommand(client, "No saved inventory for %N", player);
return Plugin_Handled;
}
if(inv.isAlive)
ReplyToCommand(client, "\x04State: \x05%s (Alive)", StateNames[view_as<int>(playerData[player].state)]);
else
ReplyToCommand(client, "\x04State: \x05%s (Dead)", StateNames[view_as<int>(playerData[player].state)]);
FormatTime(arg, sizeof(arg), DATE_FORMAT, inv.timestamp);
ReplyToCommand(client, "\x04Timestamp: \x05%s (%d seconds)", arg, GetTime() - inv.timestamp);
ReplyToCommand(client, "\x04Location: \x05%.1f %.1f %.1f", inv.location[0], inv.location[1], inv.location[2]);
ReplyToCommand(client, "\x04Model: \x05%s (%d)", inv.model, inv.survivorType);
ReplyToCommand(client, "\x04Health: \x05%d perm. / %d temp.", inv.primaryHealth, inv.tempHealth);
ReplyToCommand(client, "\x04Items: \x05(Lasers:%b)", inv.lasers);
for(int i = 0; i < 6; i++) {
if(inv.itemID[i] != WEPID_NONE) {
GetLongWeaponName(inv.itemID[i], arg, sizeof(arg));
ReplyToCommand(client, "\x04%d. \x05%s", i, arg);
}
}
ReplyToCommand(client, "%s", inv.meleeID);
}
return Plugin_Handled;
}
#endif
@ -651,7 +825,8 @@ public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast
if(GetClientTeam(client) == 2) {
if(!IsFakeClient(client)) {
if(!L4D_IsFirstMapInScenario()) {
if(++playersLoadedIn == 1) {
playersLoadedIn++;
if(playersLoadedIn == 1) {
CreateTimer(hSaferoomDoorWaitSeconds.FloatValue, Timer_OpenSaferoomDoor, _, TIMER_FLAG_NO_MAPCHANGE);
}
if(playerstoWaitFor > 0) {
@ -679,22 +854,22 @@ public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast
}
public Action Timer_CheckInventory(Handle h, int client) {
if(IsClientConnected(client) && IsClientInGame(client) && GetClientTeam(client) == 2 && DoesInventoryDiffer(client)) {
PrintToConsoleAll("[EPI] Detected mismatch inventory for %N, restoring", client);
RestoreInventory(client);
}
return Plugin_Handled;
}
// public Action Timer_CheckInventory(Handle h, int client) {
// if(IsClientConnected(client) && IsClientInGame(client) && GetClientTeam(client) == 2 && DoesInventoryDiffer(client)) {
// PrintToConsoleAll("[EPI] Detected mismatch inventory for %N, restoring", client);
// RestoreInventory(client);
// }
// return Plugin_Handled;
// }
public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid");
int client = GetClientOfUserId(userid);
if(client > 0 && IsClientConnected(client) && IsClientInGame(client) && !IsFakeClient(client) && GetClientTeam(client) == 2) { //TODO: re-add && !event.GetBool("isbot")
playerData[client].hasJoined = false;
SaveInventory(client);
PrintToServer("debug: Player %N (index %d, uid %d) now pending empty", client, client, userid);
playerData[client].state = State_PendingEmpty;
playerData[client].nameCache[0] = '\0';
/*DataPack pack;
CreateDataTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, pack);
pack.WriteCell(userid);
@ -703,6 +878,13 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
}
}
public void Event_PlayerInfo(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client && !IsFakeClient(client)) {
playerData[client].Setup(client);
}
}
public Action Timer_DropSurvivor(Handle h, int client) {
if(playerData[client].state == State_PendingEmpty) {
playerData[client].state = State_Empty;
@ -982,6 +1164,8 @@ public void OnMapStart() {
AcceptEntityInput(entity, "Lock");
AcceptEntityInput(entity, "ForceClosed");
SDKHook(entity, SDKHook_Use, Hook_Use);
// Failsafe:
CreateTimer(20.0, Timer_OpenSaferoomDoor, _, TIMER_FLAG_NO_MAPCHANGE);
break;
}
}
@ -1231,6 +1415,7 @@ void UnlockDoor(int flag) {
int entity = EntRefToEntIndex(firstSaferoomDoorEntity);
if(entity > 0) {
PrintDebug(DEBUG_GENERIC, "Door unlocked, flag %d", flag);
AcceptEntityInput(entity, "Unlock");
SetEntProp(entity, Prop_Send, "m_bLocked", 0);
SDKUnhook(entity, SDKHook_Use, Hook_Use);
if(hSaferoomDoorAutoOpen.IntValue & flag) {
@ -1244,38 +1429,56 @@ void UnlockDoor(int flag) {
}
public Action Timer_UpdateHud(Handle h) {
if(hEPIHudState.IntValue == 1 && !isCoop) { // TODO: Optimize
if(hEPIHudState.IntValue == 1 && !isCoop) {
PrintToServer("[EPI] Gamemode no longer coop, stopping (hudState=%d, abmExtraCount=%d)", hEPIHudState.IntValue, abmExtraCount);
L4D2_RunScript(HUD_SCRIPT_CLEAR);
updateHudTimer = null;
return Plugin_Stop;
}
// int threshold = hEPIHudState.IntValue == 1 ? 4 : 0;
if(hEPIHudState.IntValue == 0) { //|| abmExtraCount <= threshold broke
PrintToServer("[EPI] Less than threshold, stopping hud timer (hudState=%d, abmExtraCount=%d)", hEPIHudState.IntValue, abmExtraCount);
// TODO: Turn it off when state == 1
int threshold = hEPIHudState.IntValue == 1 ? 4 : 0;
if(hEPIHudState.IntValue == 1 && abmExtraCount < threshold) { //|| broke && abmExtraCount < threshold
PrintToServer("[EPI] Less than threshold (%d), stopping hud timer (hudState=%d, abmExtraCount=%d)", threshold, hEPIHudState.IntValue, abmExtraCount);
L4D2_RunScript(HUD_SCRIPT_CLEAR);
updateHudTimer = null;
return Plugin_Stop;
}
if(cvEPIHudFlags.IntValue & 2) {
hudModeTicks++;
if(hudModeTicks > (showHudPingMode ? 8 : 20)) {
hudModeTicks = 0;
showHudPingMode = !showHudPingMode;
}
}
static char players[512], data[32], prefix[16];
players[0] = '\0';
// TODO: name scrolling
// TODO: name cache (hook name change event), strip out invalid
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
data[0] = '\0';
prefix[0] = '\0';
int health = GetClientRealHealth(i);
int client = i;
if(IsFakeClient(i) && HasEntProp(i, Prop_Send, "m_humanSpectatorUserID")) {
int client = GetClientOfUserId(GetEntProp(i, Prop_Send, "m_humanSpectatorUserID"));
client = GetClientOfUserId(GetEntProp(i, Prop_Send, "m_humanSpectatorUserID"));
if(client > 0)
Format(prefix, 13, "AFK %N", client);
Format(prefix, 5 + HUD_NAME_LENGTH, "AFK %s", playerData[client].nameCache[playerData[client].scrollIndex]);
else
Format(prefix, 8, "%N", i);
Format(prefix, HUD_NAME_LENGTH, "%N", i);
} else {
Format(prefix, 8, "%N", i);
Format(prefix, HUD_NAME_LENGTH, "%s", playerData[client].nameCache[playerData[client].scrollIndex]);
}
playerData[client].AdvanceScroll();
if(showHudPingMode) {
if(client == 0) continue;
int ping = L4D_GetPlayerResourceData(client, L4DResource_Ping);
Format(data, sizeof(data), "%d ms", ping);
} else {
if(!IsPlayerAlive(i))
Format(data, sizeof(data), "xx");
else if(GetEntProp(i, Prop_Send, "m_bIsOnThirdStrike") == 1)
@ -1284,6 +1487,7 @@ public Action Timer_UpdateHud(Handle h) {
Format(data, sizeof(data), "+%d --", health);
else
Format(data, sizeof(data), "+%d %s%s%s", health, items[i].throwable, items[i].usable, items[i].consumable);
}
Format(players, sizeof(players), "%s%s %s\\n", players, prefix, data);
}
@ -1401,15 +1605,18 @@ void DropDroppedInventories() {
snapshot.GetKey(i, buffer, sizeof(buffer));
pInv.GetArray(buffer, inv, sizeof(inv));
if(time - inv.timestamp > PLAYER_DROP_TIMEOUT_SECONDS) {
PrintDebug(DEBUG_GENERIC, "[EPI] Dropping inventory for %s", buffer);
pInv.Remove(buffer);
}
}
}
void SaveInventory(int client) {
PrintDebug(DEBUG_GENERIC, "Saving inventory for %N", client);
PlayerInventory inventory;
inventory.timestamp = GetTime();
inventory.isAlive = IsPlayerAlive(client);
playerData[client].state = State_Active;
GetClientAbsOrigin(client, inventory.location);
inventory.primaryHealth = GetClientHealth(client);
GetClientModel(client, inventory.model, 64);
@ -1428,18 +1635,13 @@ void SaveInventory(int client) {
pInv.SetArray(buffer, inventory, sizeof(inventory));
}
void RestoreInventory(int client) {
static char buffer[32];
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
PlayerInventory inventory;
pInv.GetArray(buffer, inventory, sizeof(inventory));
void RestoreInventory(int client, PlayerInventory inventory) {
PrintToConsoleAll("[debug:RINV] health=%d primaryID=%d secondID=%d throw=%d kit=%d pill=%d surv=%d", inventory.primaryHealth, inventory.itemID[0], inventory.itemID[1], inventory.itemID[2], inventory.itemID[3], inventory.itemID[4], inventory.itemID[5], inventory.survivorType);
return;
SetEntityModel(client, inventory.model);
SetEntProp(client, Prop_Send, "m_survivorCharacter", inventory.survivorType);
char buffer[32];
if(inventory.isAlive) {
SetEntProp(client, Prop_Send, "m_iHealth", inventory.primaryHealth);
@ -1458,9 +1660,16 @@ void RestoreInventory(int client) {
SetEntProp(weapon, Prop_Send, "m_upgradeBitVec", 4);
}
}
}
bool GetLatestInventory(int client, PlayerInventory inventory) {
static char buffer[32];
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
pInv.Remove(buffer);
return pInv.GetArray(buffer, inventory, sizeof(inventory));
}
bool GetInventory(const char[] steamid, PlayerInventory inventory) {
return pInv.GetArray(steamid, inventory, sizeof(inventory));
}
bool DoesInventoryDiffer(int client) {

View file

@ -40,7 +40,7 @@ static ArrayList lastPlayers;
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
CreateNative("AddNoteIdentity", Native_AddNoteIdentity);
CreateNative("AddPlayerNoteIdentity", Native_AddNoteIdentity);
return APLRes_Success;
}
@ -83,7 +83,7 @@ void ShowRepMenu(int client, int targetUserid) {
char id[16];
Format(id, sizeof(id), "%d|1", targetUserid);
menu.AddItem(id, "+Rep");
Format(id, sizeof(id), "%d|1", targetUserid);
Format(id, sizeof(id), "%d|-1", targetUserid);
menu.AddItem(id, "-Rep");
menu.Display(client, 0);
}
@ -428,12 +428,10 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a
TrimString(reason);
if(ParseActions(data, reason)) {
actions++;
} else if((reason[0] == '+' || reason[0] == '-') && reason[1] == 'r' && reason[2] == 'e' && reason[3] == 'p') {
if(reason[0] == '+') {
} else if(StrEqual(reason, "+rep")) {
repP++;
} else {
repN--;
}
} else if(StrEqual(reason, "-rep")) {
repN++;
} else {
CPrintChatToAdmins(" {olive}%s: {default}%s", noteCreator, reason);
}
@ -466,6 +464,8 @@ bool ParseActions(int userid, const char[] input) {
strcopy(key, sizeof(key), piece[keyIndex + 1]);
piece[keyIndex] = '\0';
} else {
// Ignore empty actions
if(piece[1] == '\0') return false;
key[0] = '\0';
value[0] = '\0';
}