From 01f65c9ee5687ca3222a24d87e4d415c381e341f Mon Sep 17 00:00:00 2001 From: mbalex Date: Sat, 4 Oct 2008 22:59:11 +0200 Subject: [PATCH] Initial check in. --- Makefile | 32 + bin/spcomp | Bin 0 -> 351855 bytes src/include/admin.inc | 622 ++++++++++++++++ src/include/adminmenu.inc | 153 ++++ src/include/adt.inc | 39 + src/include/adt_array.inc | 283 ++++++++ src/include/adt_trie.inc | 158 ++++ src/include/banning.inc | 157 ++++ src/include/bitbuffer.inc | 325 +++++++++ src/include/clients.inc | 684 +++++++++++++++++ src/include/commandfilters.inc | 134 ++++ src/include/console.inc | 779 ++++++++++++++++++++ src/include/core.inc | 141 ++++ src/include/cstrike.inc | 88 +++ src/include/datapack.inc | 142 ++++ src/include/dbi.inc | 633 ++++++++++++++++ src/include/entity.inc | 677 +++++++++++++++++ src/include/entity_prop_stocks.inc | 324 +++++++++ src/include/events.inc | 243 +++++++ src/include/files.inc | 388 ++++++++++ src/include/float.inc | 415 +++++++++++ src/include/functions.inc | 489 +++++++++++++ src/include/geoip.inc | 102 +++ src/include/hacks.inc | 199 +++++ src/include/halflife.inc | 544 ++++++++++++++ src/include/handles.inc | 96 +++ src/include/helpers.inc | 294 ++++++++ src/include/keyvalues.inc | 429 +++++++++++ src/include/lang.inc | 96 +++ src/include/logging.inc | 153 ++++ src/include/market.inc | 62 ++ src/include/menus.inc | 787 ++++++++++++++++++++ src/include/profiler.inc | 77 ++ src/include/regex.inc | 173 +++++ src/include/sdktools.inc | 212 ++++++ src/include/sdktools_engine.inc | 69 ++ src/include/sdktools_entinput.inc | 116 +++ src/include/sdktools_entoutput.inc | 91 +++ src/include/sdktools_functions.inc | 310 ++++++++ src/include/sdktools_sound.inc | 442 +++++++++++ src/include/sdktools_stocks.inc | 76 ++ src/include/sdktools_stringtables.inc | 180 +++++ src/include/sdktools_tempents.inc | 229 ++++++ src/include/sdktools_tempents_stocks.inc | 458 ++++++++++++ src/include/sdktools_trace.inc | 367 ++++++++++ src/include/sdktools_voice.inc | 92 +++ src/include/sorting.inc | 176 +++++ src/include/sourcebans.inc | 31 + src/include/sourcemod.inc | 554 ++++++++++++++ src/include/string.inc | 557 ++++++++++++++ src/include/textparse.inc | 203 ++++++ src/include/tf2.inc | 121 ++++ src/include/tf2_stocks.inc | 219 ++++++ src/include/timers.inc | 208 ++++++ src/include/topmenus.inc | 290 ++++++++ src/include/usermessages.inc | 200 +++++ src/include/vector.inc | 188 +++++ src/include/version.inc | 42 ++ src/include/zr.inc | 42 ++ src/zombiereloaded.sp | 209 ++++++ src/zr/ambience.inc | 97 +++ src/zr/classes.inc | 295 ++++++++ src/zr/commands.inc | 118 +++ src/zr/cvars.inc | 191 +++++ src/zr/damagecontrol.inc | 178 +++++ src/zr/event.inc | 465 ++++++++++++ src/zr/global.inc | 36 + src/zr/menu.inc | 175 +++++ src/zr/models.inc | 139 ++++ src/zr/offsets.inc | 190 +++++ src/zr/overlays.inc | 47 ++ src/zr/sayhooks.inc | 346 +++++++++ src/zr/translation.inc | 118 +++ src/zr/weaponrestrict.inc | 261 +++++++ src/zr/zombie.inc | 887 +++++++++++++++++++++++ src/zr/zombiereloaded.inc | 177 +++++ 76 files changed, 19320 insertions(+) create mode 100755 Makefile create mode 100755 bin/spcomp create mode 100644 src/include/admin.inc create mode 100644 src/include/adminmenu.inc create mode 100644 src/include/adt.inc create mode 100644 src/include/adt_array.inc create mode 100644 src/include/adt_trie.inc create mode 100644 src/include/banning.inc create mode 100644 src/include/bitbuffer.inc create mode 100644 src/include/clients.inc create mode 100644 src/include/commandfilters.inc create mode 100644 src/include/console.inc create mode 100644 src/include/core.inc create mode 100644 src/include/cstrike.inc create mode 100644 src/include/datapack.inc create mode 100644 src/include/dbi.inc create mode 100644 src/include/entity.inc create mode 100644 src/include/entity_prop_stocks.inc create mode 100644 src/include/events.inc create mode 100644 src/include/files.inc create mode 100644 src/include/float.inc create mode 100644 src/include/functions.inc create mode 100644 src/include/geoip.inc create mode 100644 src/include/hacks.inc create mode 100644 src/include/halflife.inc create mode 100644 src/include/handles.inc create mode 100644 src/include/helpers.inc create mode 100644 src/include/keyvalues.inc create mode 100644 src/include/lang.inc create mode 100644 src/include/logging.inc create mode 100644 src/include/market.inc create mode 100644 src/include/menus.inc create mode 100644 src/include/profiler.inc create mode 100644 src/include/regex.inc create mode 100644 src/include/sdktools.inc create mode 100644 src/include/sdktools_engine.inc create mode 100644 src/include/sdktools_entinput.inc create mode 100644 src/include/sdktools_entoutput.inc create mode 100644 src/include/sdktools_functions.inc create mode 100644 src/include/sdktools_sound.inc create mode 100644 src/include/sdktools_stocks.inc create mode 100644 src/include/sdktools_stringtables.inc create mode 100644 src/include/sdktools_tempents.inc create mode 100644 src/include/sdktools_tempents_stocks.inc create mode 100644 src/include/sdktools_trace.inc create mode 100644 src/include/sdktools_voice.inc create mode 100644 src/include/sorting.inc create mode 100644 src/include/sourcebans.inc create mode 100644 src/include/sourcemod.inc create mode 100644 src/include/string.inc create mode 100644 src/include/textparse.inc create mode 100644 src/include/tf2.inc create mode 100644 src/include/tf2_stocks.inc create mode 100644 src/include/timers.inc create mode 100644 src/include/topmenus.inc create mode 100644 src/include/usermessages.inc create mode 100644 src/include/vector.inc create mode 100644 src/include/version.inc create mode 100644 src/include/zr.inc create mode 100644 src/zombiereloaded.sp create mode 100644 src/zr/ambience.inc create mode 100644 src/zr/classes.inc create mode 100644 src/zr/commands.inc create mode 100644 src/zr/cvars.inc create mode 100644 src/zr/damagecontrol.inc create mode 100644 src/zr/event.inc create mode 100644 src/zr/global.inc create mode 100644 src/zr/menu.inc create mode 100644 src/zr/models.inc create mode 100644 src/zr/offsets.inc create mode 100644 src/zr/overlays.inc create mode 100644 src/zr/sayhooks.inc create mode 100644 src/zr/translation.inc create mode 100644 src/zr/weaponrestrict.inc create mode 100644 src/zr/zombie.inc create mode 100644 src/zr/zombiereloaded.inc diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..c5eddd1 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ + +SOURCEDIR=src +BUILDDIR=build +SPCOMP=bin/spcomp + +vpath %.sp $(SOURCEDIR) +vpath %.inc $(SOURCEDIR)/include +vpath %.smx $(BUILDDIR) + +SOURCEFILES=$(SOURCEDIR)/*.sp +OBJECTS=$(patsubst %.sp, %.smx, $(notdir $(wildcard $(SOURCEFILES)))) + +all: prepare $(OBJECTS) + +prepare: prepare_newlines prepare_builddir + +prepare_newlines: + @echo "Removing windows newlines" + @find $(SOURCEDIR) -name \*.inc -exec dos2unix -p '{}' \; + @find $(SOURCEDIR) -name \*.sp -exec dos2unix -p '{}' \; + +prepare_builddir: + @echo "Creating build directory" + @mkdir -p $(BUILDDIR) + +%.smx: %.sp + $(SPCOMP) -i$(SOURCEDIR) -i$(SOURCEDIR)/include -o$(BUILDDIR)/$@ $< + +clean: + @echo "Removing build directory" + @rm -fr $(BUILDDIR) + diff --git a/bin/spcomp b/bin/spcomp new file mode 100755 index 0000000000000000000000000000000000000000..04c6cb8fe98b733ce4e35d58279731a9481e5e19 GIT binary patch literal 351855 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 Sl!}iB)huZF6V}xQ. + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: admin.inc 1409 2007-09-10 23:38:58Z dvander $ + */ + +#if defined _admin_included + #endinput +#endif +#define _admin_included + +/** + * Access levels (flags) for admins. + */ +enum AdminFlag +{ + Admin_Reservation = 0, /**< Reserved slot */ + Admin_Generic, /**< Generic admin abilities */ + Admin_Kick, /**< Kick another user */ + Admin_Ban, /**< Ban another user */ + Admin_Unban, /**< Unban another user */ + Admin_Slay, /**< Slay/kill/damage another user */ + Admin_Changemap, /**< Change the map */ + Admin_Convars, /**< Change basic convars */ + Admin_Config, /**< Change configuration */ + Admin_Chat, /**< Special chat privileges */ + Admin_Vote, /**< Special vote privileges */ + Admin_Password, /**< Set a server password */ + Admin_RCON, /**< Use RCON */ + Admin_Cheats, /**< Change sv_cheats and use its commands */ + Admin_Root, /**< All access by default */ + Admin_Custom1, /**< First custom flag type */ + Admin_Custom2, /**< Second custom flag type */ + Admin_Custom3, /**< Third custom flag type */ + Admin_Custom4, /**< Fourth custom flag type */ + Admin_Custom5, /**< Fifth custom flag type */ + Admin_Custom6, /**< Sixth custom flag type */ + /* --- */ +}; + +#define AdminFlags_TOTAL 21 /**< Total number of admin flags */ + +/** + * @section Bitwise values definitions for admin flags. + */ +#define ADMFLAG_RESERVATION (1<<0) /**< Convenience macro for Admin_Reservation as a FlagBit */ +#define ADMFLAG_GENERIC (1<<1) /**< Convenience macro for Admin_Generic as a FlagBit */ +#define ADMFLAG_KICK (1<<2) /**< Convenience macro for Admin_Kick as a FlagBit */ +#define ADMFLAG_BAN (1<<3) /**< Convenience macro for Admin_Ban as a FlagBit */ +#define ADMFLAG_UNBAN (1<<4) /**< Convenience macro for Admin_Unban as a FlagBit */ +#define ADMFLAG_SLAY (1<<5) /**< Convenience macro for Admin_Slay as a FlagBit */ +#define ADMFLAG_CHANGEMAP (1<<6) /**< Convenience macro for Admin_Changemap as a FlagBit */ +#define ADMFLAG_CONVARS (1<<7) /**< Convenience macro for Admin_Convars as a FlagBit */ +#define ADMFLAG_CONFIG (1<<8) /**< Convenience macro for Admin_Config as a FlagBit */ +#define ADMFLAG_CHAT (1<<9) /**< Convenience macro for Admin_Chat as a FlagBit */ +#define ADMFLAG_VOTE (1<<10) /**< Convenience macro for Admin_Vote as a FlagBit */ +#define ADMFLAG_PASSWORD (1<<11) /**< Convenience macro for Admin_Password as a FlagBit */ +#define ADMFLAG_RCON (1<<12) /**< Convenience macro for Admin_RCON as a FlagBit */ +#define ADMFLAG_CHEATS (1<<13) /**< Convenience macro for Admin_Cheats as a FlagBit */ +#define ADMFLAG_ROOT (1<<14) /**< Convenience macro for Admin_Root as a FlagBit */ +#define ADMFLAG_CUSTOM1 (1<<15) /**< Convenience macro for Admin_Custom1 as a FlagBit */ +#define ADMFLAG_CUSTOM2 (1<<16) /**< Convenience macro for Admin_Custom2 as a FlagBit */ +#define ADMFLAG_CUSTOM3 (1<<17) /**< Convenience macro for Admin_Custom3 as a FlagBit */ +#define ADMFLAG_CUSTOM4 (1<<18) /**< Convenience macro for Admin_Custom4 as a FlagBit */ +#define ADMFLAG_CUSTOM5 (1<<19) /**< Convenience macro for Admin_Custom5 as a FlagBit */ +#define ADMFLAG_CUSTOM6 (1<<20) /**< Convenience macro for Admin_Custom6 as a FlagBit */ + +/** + * @endsection + */ + +/** + * @section Hardcoded authentication methods + */ +#define AUTHMETHOD_STEAM "steam" /**< SteamID based authentication */ +#define AUTHMETHOD_IP "ip" /**< IP based authentication */ +#define AUTHMETHOD_NAME "name" /**< Name based authentication */ + +/** + * @endsection + */ + +/** + * Access override types. + */ +enum OverrideType +{ + Override_Command = 1, /**< Command */ + Override_CommandGroup, /**< Command group */ +}; + +/** + * Access override rules. + */ +enum OverrideRule +{ + Command_Deny = 0, + Command_Allow = 1, +}; + +/** + * DEPRECATED, do not use. + */ +enum ImmunityType +{ + Immunity_Default = 1, /**< Deprecated. */ + Immunity_Global, /**< Deprecated. */ +}; + +/** + * Identifies a unique entry in the group permissions cache. These are not Handles. + */ +enum GroupId +{ + INVALID_GROUP_ID = -1, /**< An invalid/nonexistant group */ +}; + +/** + * Identifies a unique entry in the admin permissions cache. These are not Handles. + */ +enum AdminId +{ + INVALID_ADMIN_ID = -1, /**< An invalid/nonexistant admin */ +}; + +/** + * Methods of computing access permissions. + */ +enum AdmAccessMode +{ + Access_Real, /**< Access the user has inherently */ + Access_Effective, /**< Access the user has from their groups */ +}; + +/** + * Represents the various cache regions. + */ +enum AdminCachePart +{ + AdminCache_Overrides = 0, /**< Global overrides */ + AdminCache_Groups = 1, /**< All groups (automatically invalidates admins too) */ + AdminCache_Admins = 2, /**< All admins */ +}; + +/** + * Called when part of the cache which needs to be rebuilt. + * + * @param part Part of the admin cache to rebuild. + */ +forward OnRebuildAdminCache(AdminCachePart:part); + +/** + * Tells the admin system to dump a portion of the cache. + * + * @param part Part of the cache to dump. Specifying groups also dumps admins. + * @param rebuild If true, the rebuild forwards will fire. + * @noreturn + */ +native DumpAdminCache(AdminCachePart:part, bool:rebuild); + +/** + * Adds a global command flag override. Any command registered with this name + * will assume the new flag. This is applied retroactively as well. + * + * @param cmd String containing command name (case sensitive). + * @param type Override type (specific command or group). + * @param flags New admin flag. + * @noreturn + */ +native AddCommandOverride(const String:cmd[], OverrideType:type, flags); + +/** + * Returns a command override. + * + * @param cmd String containing command name (case sensitive). + * @param type Override type (specific command or group). + * @param flags By-reference cell to store the flag (undefined if not found). + * @return True if there is an override, false otherwise. + */ +native bool:GetCommandOverride(const String:cmd[], OverrideType:type, &flags); + +/** + * Unsets a command override. + * + * @param cmd String containing command name (case sensitive). + * @param type Override type (specific command or group). + * @noreturn + */ +native UnsetCommandOverride(const String:cmd[], OverrideType:type); + +/** + * Adds a new group. Name must be unique. + * + * @param group_name String containing the group name. + * @return A new group id, INVALID_GROUP_ID if it already exists. + */ +native GroupId:CreateAdmGroup(const String:group_name[]); + +/** + * Finds a group by name. + * + * @param group_name String containing the group name. + * @return A group id, or INVALID_GROUP_ID if not found. + */ +native GroupId:FindAdmGroup(const String:group_name[]); + +/** + * Adds or removes a flag from a group's flag set. + * @note These are called "add flags" because they add to a user's flags. + * + * @param id Group id. + * @param flag Admin flag to toggle. + * @param enabled True to set the flag, false to unset/disable. + * @noreturn + */ +native SetAdmGroupAddFlag(GroupId:id, AdminFlag:flag, bool:enabled); + +/** + * Gets the set value of an add flag on a group's flag set. + * @note These are called "add flags" because they add to a user's flags. + * + * @param id Group id. + * @param flag Admin flag to retrieve. + * @return True if enabled, false otherwise, + */ +native bool:GetAdmGroupAddFlag(GroupId:id, AdminFlag:flag); + +/** + * Returns the flag set that is added to a user from their group. + * @note These are called "add flags" because they add to a user's flags. + * + * @param id GroupId of the group. + * @return Bitstring containing the flags enabled. + */ +native GetAdmGroupAddFlags(GroupId:id); + +/** + * @deprecated Functionality removed. + */ +#pragma deprecated Use SetAdmGroupImmunityLevel() instead. +native SetAdmGroupImmunity(GroupId:id, ImmunityType:type, bool:enabled); + +/** + * @deprecated Functionality removed. + */ +#pragma deprecated Use GetAdmGroupImmunityLevel() instead. +native bool:GetAdmGroupImmunity(GroupId:id, ImmunityType:type); + +/** + * Adds immunity to a specific group. + * + * @param id Group id. + * @param other_id Group id to receive immunity to. + * @noreturn + */ +native SetAdmGroupImmuneFrom(GroupId:id, GroupId:other_id); + +/** + * Returns the number of specific group immunities. + * + * @param id Group id. + * @return Number of group immunities. + */ +native GetAdmGroupImmuneCount(GroupId:id); + +/** + * Returns a group that this group is immune to given an index. + * + * @param id Group id. + * @param number Index from 0 to N-1, from GetAdmGroupImmuneCount(). + * @return GroupId that this group is immune to, or INVALID_GROUP_ID on failure. + */ +native GroupId:GetAdmGroupImmuneFrom(GroupId:id, number); + +/** + * Adds a group-specific override type. + * + * @param id Group id. + * @param name String containing command name (case sensitive). + * @param type Override type (specific command or group). + * @param rule Override allow/deny setting. + * @noreturn + */ +native AddAdmGroupCmdOverride(GroupId:id, const String:name[], OverrideType:type, OverrideRule:rule); + +/** + * Retrieves a group-specific command override. + * + * @param id Group id. + * @param name String containing command name (case sensitive). + * @param type Override type (specific command or group). + * @param rule Optional pointer to store allow/deny setting. + * @return True if an override exists, false otherwise. + */ +native bool:GetAdmGroupCmdOverride(GroupId:id, const String:name[], OverrideType:type, &OverrideRule:rule); + +/** + * Registers an authentication identity type. You normally never need to call this except for + * very specific systems. + * + * @param codename Codename to use for your authentication type. + * @noreturn + */ +native RegisterAuthIdentType(const String:name[]); + +/** + * Creates a new admin entry in the permissions cache. + * + * @param name Name for this entry (does not have to be unique). + * Specify an empty string for an anonymous admin. + */ +native AdminId:CreateAdmin(const String:name[]=""); + +/** + * Retrieves an admin's user name as made with CreateAdmin(). + * + * @note This function can return UTF-8 strings, and will safely chop UTF-8 strings. + * + * @param id AdminId of the admin. + * @param name String buffer to store name. + * @param maxlength Maximum size of string buffer. + * @return Number of bytes written. + */ +native GetAdminUsername(AdminId:id, const String:name[], maxlength); + +/** + * Binds an admin to an identity for fast lookup later on. The bind must be unique. + * + * @param id AdminId of the admin. + * @param auth Auth method to use, predefined or from RegisterAuthIdentType(). + * @param ident String containing the arbitrary, unique identity. + * @return True on success, false if the auth method was not found, + * or ident was already taken. + */ +native bool:BindAdminIdentity(AdminId:id, const String:auth[], const String:ident[]); + +/** + * Sets whether or not a flag is enabled on an admin. + * + * @param id AdminId index of the admin. + * @param flag Admin flag to use. + * @param enabled True to enable, false to disable. + * @noreturn + */ +native SetAdminFlag(AdminId:id, AdminFlag:flag, bool:enabled); + +/** + * Returns whether or not a flag is enabled on an admin. + * + * @param id AdminId index of the admin. + * @param flag Admin flag to use. + * @param mode Access mode to check. + * @return True if enabled, false otherwise. + */ +native bool:GetAdminFlag(AdminId:id, AdminFlag:flag, AdmAccessMode:mode=Access_Effective); + +/** + * Returns the bitstring of access flags on an admin. + * + * @param id AdminId index of the admin. + * @param mode Access mode to use. + * @return A bitstring containing which flags are enabled. + */ +native GetAdminFlags(AdminId:id, AdmAccessMode:mode); + +/** + * Adds a group to an admin's inherited group list. Any flags the group has + * will be added to the admin's effective flags. + * + * @param id AdminId index of the admin. + * @param gid GroupId index of the group. + * @return True on success, false on invalid input or duplicate membership. + */ +native bool:AdminInheritGroup(AdminId:id, GroupId:gid); + +/** + * Returns the number of groups this admin is a member of. + * + * @param id AdminId index of the admin. + * @return Number of groups this admin is a member of. + */ +native GetAdminGroupCount(AdminId:id); + +/** + * Returns group information from an admin. + * + * @param id AdminId index of the admin. + * @param index Group number to retrieve, from 0 to N-1, where N + * is the value of GetAdminGroupCount(id). + * @param name Buffer to store the group's name. + * Note: This will safely chop UTF-8 strings. + * @param maxlength Maximum size of the output name buffer. + * @return A GroupId index and a name pointer, or + * INVALID_GROUP_ID and NULL if an error occurred. +*/ +native GroupId:GetAdminGroup(AdminId:id, index, const String:name[], maxlength); + +/** + * Sets a password on an admin. + * + * @param id AdminId index of the admin. + * @param passwd String containing the password. + * @noreturn + */ +native SetAdminPassword(AdminId:id, const String:password[]); + +/** + * Gets an admin's password. + * + * @param id AdminId index of the admin. + * @param name Optional buffer to store the admin's password. + * @param maxlength Maximum size of the output name buffer. + * Note: This will safely chop UTF-8 strings. + * @return True if there was a password set, false otherwise. + */ +native bool:GetAdminPassword(AdminId:id, const String:buffer[]="", maxlength=0); + +/** + * Attempts to find an admin by an auth method and an identity. + * + * @param auth Auth method to try. + * @param identity Identity string to look up. + * @return An AdminId index if found, INVALID_ADMIN_ID otherwise. + */ +native AdminId:FindAdminByIdentity(const String:auth[], const String:identity[]); + +/** + * Removes an admin entry from the cache. + * + * @note This will remove any bindings to a specific user. + * + * @param id AdminId index to remove/invalidate. + * @return True on success, false otherwise. + */ +native bool:RemoveAdmin(AdminId:id); + +/** + * Converts a flag bit string to a bit array. + * + * @param bits Bit string containing the flags. + * @param array Array to write the flags to. Enabled flags will be 'true'. + * @param maxSize Maximum number of flags the array can store. + * @return Number of flags written. + */ +native FlagBitsToBitArray(bits, bool:array[], maxSize); + +/** + * Converts a flag array to a bit string. + * + * @param array Array containing true or false for each AdminFlag. + * @param maxSize Maximum size of the flag array. + * @return A bit string composed of the array bits. + */ +native FlagBitArrayToBits(const bool:array[], maxSize); + +/** + * Converts an array of flags to bits. + * + * @param array Array containing flags that are enabled. + * @param numFlags Number of flags in the array. + * @return A bit string composed of the array flags. + */ +native FlagArrayToBits(const AdminFlag:array[], numFlags); + +/** + * Converts a bit string to an array of flags. + * + * @param bits Bit string containing the flags. + * @param array Output array to write flags. + * @param maxSize Maximum size of the flag array. + * @return Number of flags written. + */ +native FlagBitsToArray(bits, AdminFlag:array[], maxSize); + +/** + * Finds a flag by its string name. + * + * @param name Flag name (like "kick"), case sensitive. + * @param flag Variable to store flag in. + * @return True on success, false if not found. + */ +native bool:FindFlagByName(const String:name[], &AdminFlag:flag); + +/** + * Finds a flag by a given character. + * + * @param c Flag ASCII character/token. + * @param flag Variable to store flag in. + * @return True on success, false if not found. + */ +native bool:FindFlagByChar(c, &AdminFlag:flag); + +/** + * Converts a string of flag characters to a bit string. + * + * @param flags Flag ASCII string. + * @param numchars Optional variable to store the number of bytes read. + * @return Bit string of ADMFLAG values. + */ +native ReadFlagString(const String:flags[], &numchars=0); + +/** + * Tests whether one admin can target another. + * + * The hueristics for this check are as follows: + * 0. If the targeting AdminId is INVALID_ADMIN_ID, targeting fails. + * 1. If the targeted AdminId is INVALID_ADMIN_ID, targeting succeeds. + * 2. If the targeted AdminId is the same as the targeting AdminId, + * (self) targeting succeeds. + * 3. If the targeting admin is root, targeting succeeds. + * 4. If the targeted admin has access higher (as interpreted by + * (sm_immunity_mode) than the targeting admin, then targeting fails. + * 5. If the targeted admin has specific immunity from the + * targeting admin via group immunities, targeting fails. + * 6. Targeting succeeds. + * + * @param admin Admin doing the targetting (may be INVALID_ADMIN_ID). + * @param target Target admin (may be INVALID_ADMIN_ID). + * @return True if targetable, false if immune. + */ +native CanAdminTarget(AdminId:admin, AdminId:target); + +/** + * Creates an admin auth method. This does not need to be called more than once + * per method, ever. + * + * @param method Name of the authentication method. + * @return True on success, false on failure. + */ +native bool:CreateAuthMethod(const String:method[]); + +/** + * Sets a group's immunity level. + * + * @param gid Group Id. + * @param level Immunity level value. + * @return Old immunity level value. + */ +native SetAdmGroupImmunityLevel(GroupId:gid, level); + +/** + * Gets a group's immunity level (defaults to 0). + * + * @param gid Group Id. + * @return Immunity level value. + */ +native GetAdmGroupImmunityLevel(GroupId:gid); + +/** + * Sets an admin's immunity level. + * + * @param id Admin Id. + * @param level Immunity level value. + * @return Old immunity level value. + */ +native SetAdminImmunityLevel(AdminId:id, level); + +/** + * Gets an admin's immunity level. + * + * @param id Admin Id. + * @return Immunity level value. + */ +native GetAdminImmunityLevel(AdminId:id); + +/** + * Converts a flag to its single bit. + * + * @param flag Flag to convert. + * @return Bit representation of the flag. + */ +stock FlagToBit(AdminFlag:flag) +{ + return (1<<_:flag); +} + +/** + * Converts a bit to an AdminFlag. + * + * @param bit Bit to convert. + * @param flag Stores the converted flag by reference. + * @return True on success, false otherwise. + */ +stock bool:BitToFlag(bit, &AdminFlag:flag) +{ + new AdminFlag:array[1]; + + if (FlagBitsToArray(bit, array, 1)) + { + flag = array[0]; + return true; + } + + return false; +} + diff --git a/src/include/adminmenu.inc b/src/include/adminmenu.inc new file mode 100644 index 0000000..9385055 --- /dev/null +++ b/src/include/adminmenu.inc @@ -0,0 +1,153 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: adminmenu.inc 1838 2008-01-04 16:05:26Z dvander $ + */ + +#if defined _adminmenu_included + #endinput +#endif +#define _adminmenu_included + +/* Decide whether topmenus should be required */ +#if !defined REQUIRE_PLUGIN + #if defined REQUIRE_EXTENSIONS + #define TEMP_REQUIRE_EXTENSIONS + #undef REQUIRE_EXTENSIONS +#endif + +#include + +/* Restore old REQUIRE_EXTENSIONS value if necessary */ +#if defined TEMP_REQUIRE_EXTENSIONS + #define REQUIRE_EXTENSIONS + #undef TEMP_REQUIRE_EXTENSIONS +#endif + +/** Category for player commands. */ +#define ADMINMENU_PLAYERCOMMANDS "PlayerCommands" +/** Category for server commands. */ +#define ADMINMENU_SERVERCOMMANDS "ServerCommands" +/** Category for voting commands. */ +#define ADMINMENU_VOTINGCOMMANDS "VotingCommands" + +/** + * Called when the admin menu is created and 3rd party plugins can grab + * the Handle or add categories. + * + * @param topmenu Handle to the admin menu's TopMenu. + * @noreturn + */ +forward OnAdminMenuCreated(Handle:topmenu); + +/** + * Called when the admin menu is ready to have items added. + * + * @param topmenu Handle to the admin menu's TopMenu. + * @noreturn + */ +forward OnAdminMenuReady(Handle:topmenu); + +/** + * Retrieves the Handle to the admin top menu. + * + * @return Handle to the admin menu's TopMenu, + * or INVALID_HANDLE if not created yet. + */ +native Handle:GetAdminTopMenu(); + +/** + * Adds targets to an admin menu. + * + * Each client is displayed as: name (userid) + * Each item contains the userid as a string for its info. + * + * @param menu Menu Handle. + * @param source_client Source client, or 0 to ignore immunity. + * @param in_game_only True to only select in-game players. + * @param alive_only True to only select alive players. + * @return Number of clients added. + */ +native AddTargetsToMenu(Handle:menu, + source_client, + bool:in_game_only=true, + bool:alive_only=false); + +/** + * Adds targets to an admin menu. + * + * Each client is displayed as: name (userid) + * Each item contains the userid as a string for its info. + * + * @param menu Menu Handle. + * @param source_client Source client, or 0 to ignore immunity. + * @param flags COMMAND_FILTER flags from commandfilters.inc. + * @return Number of clients added. + */ +native AddTargetsToMenu2(Handle:menu, source_client, flags); + +/** + * Re-displays the admin menu to a client after selecting an item. + * Auto-aborts if the Handle is invalid. + * + * @param topmenu TopMenu Handle. + * @param client Client index. + * @return True on success, false on failure. + */ +stock bool:RedisplayAdminMenu(Handle:topmenu, client) +{ + if (topmenu == INVALID_HANDLE) + { + return false; + } + + return DisplayTopMenu(topmenu, client, TopMenuPosition_LastCategory); +} + +/* DO NOT EDIT BELOW THIS LINE */ + +public SharedPlugin:__pl_adminmenu = +{ + name = "adminmenu", + file = "adminmenu.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_adminmenu_SetNTVOptional() +{ + MarkNativeAsOptional("GetAdminTopMenu"); + MarkNativeAsOptional("AddTargetsToMenu"); + MarkNativeAsOptional("AddTargetsToMenu2"); +} +#endif diff --git a/src/include/adt.inc b/src/include/adt.inc new file mode 100644 index 0000000..185395c --- /dev/null +++ b/src/include/adt.inc @@ -0,0 +1,39 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: adt.inc 2093 2008-04-24 10:25:27Z damagedsoul $ + */ + +#if defined _adt_included + #endinput +#endif +#define _adt_included + +#include +#include diff --git a/src/include/adt_array.inc b/src/include/adt_array.inc new file mode 100644 index 0000000..65c8231 --- /dev/null +++ b/src/include/adt_array.inc @@ -0,0 +1,283 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: adt_array.inc 2093 2008-04-24 10:25:27Z damagedsoul $ + */ + +#if defined _adt_array_included + #endinput +#endif +#define _adt_array_included + +/** + * Given a maximum string size (including the null terminator), + * returns the number of cells required to fit that string. + * + * @param size Number of bytes. + * @return Minimum number of cells required to fit the byte count. + */ +stock ByteCountToCells(size) +{ + if (!size) + { + return 1; + } + + return (size + 3) / 4; +} + +/** + * Creates a dynamic global cell array. While slower than a normal array, + * it can be used globally AND dynamically, which is otherwise impossible. + * + * The contents of the array are uniform; i.e. storing a string at index X + * and then retrieving it as an integer is NOT the same as StringToInt()! + * The "blocksize" determines how many cells each array slot has; it cannot + * be changed after creation. + * + * @param blocksize The number of cells each member of the array can + * hold. For example, 32 cells is equivalent to: + * new Array[X][32] + * @param startsize Initial size of the array. Note that data will + * NOT be auto-intialized. + * @return New Handle to the array object. + */ +native Handle:CreateArray(blocksize=1, startsize=0); + +/** + * Clears an array of all entries. This is the same as ResizeArray(0). + * + * @param array Array Handle. + * @noreturn + * @error Invalid Handle. + */ +native ClearArray(Handle:array); + +/** + * Clones an array, returning a new handle with the same size and data. This should NOT + * be confused with CloneHandle. This is a completely new handle with the same data but + * no relation to the original. You MUST close it. + * + * @param array Array handle to be cloned + * @return New handle to the cloned array object + * @error Invalid Handle + */ + native Handle:CloneArray(Handle:array); + +/** + * Resizes an array. If the size is smaller than the current size, + * the array is truncated. + * + * @param array Array Handle. + * @param newsize New size. + * @noreturn + * @error Invalid Handle or out of memory. + */ +native bool:ResizeArray(Handle:array, newsize); + +/** + * Returns the array size. + * + * @param array Array Handle. + * @return Number of elements in the array. + * @error Invalid Handle. + */ +native GetArraySize(Handle:array); + +/** + * Pushes a value onto the end of an array, adding a new index. + * + * This may safely be used even if the array has a blocksize + * greater than 1. + * + * @param array Array Handle. + * @param value Value to push. + * @return Index of the new entry. + * @error Invalid Handle or out of memory. + */ +native PushArrayCell(Handle:array, any:value); + +/** + * Pushes a string onto the end of an array, truncating it + * if it is too big. + * + * @param array Array Handle. + * @param value String to push. + * @return Index of the new entry. + * @error Invalid Handle or out of memory. + */ +native PushArrayString(Handle:array, const String:value[]); + +/** + * Pushes an array of cells onto the end of an array. The cells + * are pushed as a block (i.e. the entire array sits at the index), + * rather than pushing each cell individually. + * + * @param array Array Handle. + * @param values Block of values to copy. + * @param size If not set, the number of elements copied from the array + * will be equal to the blocksize. If set higher than the + * blocksize, the operation will be truncated. + * @return Index of the new entry. + * @error Invalid Handle or out of memory. + */ +native PushArrayArray(Handle:array, const any:values[], size=-1); + +/** + * Retrieves a cell value from an array. + * + * @param array Array Handle. + * @param index Index in the array. + * @param block Optionally specify which block to read from + * (useful if the blocksize > 0). + * @param asChar Optionally read as a byte instead of a cell. + * @return Value read. + * @error Invalid Handle, invalid index, or invalid block. + */ +native any:GetArrayCell(Handle:array, index, block=0, bool:asChar=false); + +/** + * Retrieves a string value from an array. + * + * @param array Array Handle. + * @param index Index in the array. + * @param buffer Buffer to copy to. + * @param maxlength Maximum size of the buffer. + * @return Number of characters copied. + * @error Invalid Handle or invalid index. + */ +native GetArrayString(Handle:array, index, String:buffer[], maxlength); + +/** + * Retrieves an array of cells from an array. + * + * @param array Array Handle. + * @param index Index in the array. + * @param buffer Buffer to store the array in. + * @param size If not set, assumes the buffer size is equal to the + * blocksize. Otherwise, the size passed is used. + * @return Number of cells copied. + * @error Invalid Handle or invalid index. + */ +native GetArrayArray(Handle:array, index, any:buffer[], size=-1); + +/** + * Sets a cell value in an array. + * + * @param array Array Handle. + * @param index Index in the array. + * @param value Cell value to set. + * @param block Optionally specify which block to write to + * (useful if the blocksize > 0). + * @param asChar Optionally set as a byte instead of a cell. + * @noreturn + * @error Invalid Handle, invalid index, or invalid block. + */ +native any:SetArrayCell(Handle:array, index, any:value, block=0, bool:asChar=false); + +/** + * Sets a string value in an array. + * + * @param array Array Handle. + * @param index Index in the array. + * @param value String value to set. + * @return Number of characters copied. + * @error Invalid Handle or invalid index. + */ +native SetArrayString(Handle:array, index, const String:buffer[]); + +/** + * Sets an array of cells in an array. + * + * @param array Array Handle. + * @param index Index in the array. + * @param buffer Array to copy. + * @param size If not set, assumes the buffer size is equal to the + * blocksize. Otherwise, the size passed is used. + * @return Number of cells copied. + * @error Invalid Handle or invalid index. + */ +native SetArrayArray(Handle:array, index, const any:values[], size=-1); + +/** + * Shifts an array up. All array contents after and including the given + * index are shifted up by one, and the given index is then "free." + * After shifting, the contents of the given index is undefined. + * + * @param array Array Handle. + * @param index Index in the array to shift up from. + * @noreturn + * @error Invalid Handle or invalid index. + */ +native ShiftArrayUp(Handle:array, index); + +/** + * Removes an array index, shifting the entire array down from that position + * on. For example, if item 8 of 10 is removed, the last 3 items will then be + * (6,7,8) instead of (7,8,9), and all indexes before 8 will remain unchanged. + * + * @param array Array Handle. + * @param index Index in the array to remove at. + * @noreturn + * @error Invalid Handle or invalid index. + */ +native RemoveFromArray(Handle:array, index); + +/** + * Swaps two items in the array. + * + * @param array Array Handle. + * @param index1 First index. + * @param index2 Second index. + * @noreturn + * @error Invalid Handle or invalid index. + */ +native SwapArrayItems(Handle:array, index1, index2); + +/** + * Returns the index for the first occurance of the provided string. If the string + * cannot be located, -1 will be returned. + * + * @param array Array Handle. + * @param item String to search for + * @return Array index, or -1 on failure + * @error Invalid Handle + */ +native FindStringInArray(Handle:array, const String:item[]); + +/** + * Returns the index for the first occurance of the provided value. If the value + * cannot be located, -1 will be returned. + * + * @param array Array Handle. + * @param item Value to search for + * @return Array index, or -1 on failure + * @error Invalid Handle + */ +native FindValueInArray(Handle:array, any:item); diff --git a/src/include/adt_trie.inc b/src/include/adt_trie.inc new file mode 100644 index 0000000..37fcee1 --- /dev/null +++ b/src/include/adt_trie.inc @@ -0,0 +1,158 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: adt_trie.inc 2093 2008-04-24 10:25:27Z damagedsoul $ + */ + +#if defined _adt_trie_included + #endinput +#endif +#define _adt_trie_included + +/** + * Creates a Trie structure. A trie is a data storage object that maps any value to a + * string of text. It features very fast lookup and deletion, but grows very slow for + * insertion once tens of thousands of items are added. + * + * Keys in Tries are unique. That is, each key may only have one value. Unlike arrays, + * Tries cannot be iterated right now. Since the contents are known to be unique, to + * work around this, you can use ADT Arrays to store a list of keys known to be in a + * Trie. + * + * @return New Trie Handle, which must be freed via CloseHandle(). + */ +native Handle:CreateTrie(); + +/** + * Sets a value in a Trie, either inserting a new entry or replacing an old one. + * + * @param trie Trie Handle. + * @param key Key string. + * @param value Value to store at this key. + * @param replace If false, operation will fail if the key is already set. + * @return True on success, false on failure. + * @error Invalid Handle. + */ +native bool:SetTrieValue(Handle:trie, const String:key[], any:value, bool:replace=true); + +/** + * Sets an array value in a Trie, either inserting a new entry or replacing an old one. + * + * @param trie Trie Handle. + * @param key Key string. + * @param array Array to store. + * @param num_items Number of items in the array. + * @param replace If false, operation will fail if the key is already set. + * @return True on success, false on failure. + * @error Invalid Handle. + */ +native bool:SetTrieArray(Handle:trie, const String:key[], const any:array[], num_items, bool:replace=true); + +/** + * Sets a string value in a Trie, either inserting a new entry or replacing an old one. + * + * @param trie Trie Handle. + * @param key Key string. + * @param array Array to store. + * @param num_items Number of items in the array. + * @param replace If false, operation will fail if the key is already set. + * @return True on success, false on failure. + * @error Invalid Handle. + */ +native bool:SetTrieString(Handle:trie, const String:key[], const String:value[], bool:replace=true); + +/** + * Retrieves a value in a Trie. + * + * @param trie Trie Handle. + * @param key Key string. + * @param val Variable to store value. + * @return True on success. False if the key is not set, or the key is set + * as an array or string (not a value). + * @error Invalid Handle. + */ +native bool:GetTrieValue(Handle:trie, const String:key[], &any:value); + +/** + * Retrieves an array in a Trie. + * + * @param trie Trie Handle. + * @param key Key string. + * @param array Buffer to store array. + * @param max_size Maximum size of array buffer. + * @param size Optional parameter to store the number of elements written to the buffer. + * @return True on success. False if the key is not set, or the key is set + * as a value or string (not an array). + * @error Invalid Handle. + */ +native bool:GetTrieArray(Handle:trie, const String:key[], any:array[], max_size, &size=0); + +/** + * Retrieves a string in a Trie. + * + * @param trie Trie Handle. + * @param key Key string. + * @param value Buffer to store value. + * @param max_size Maximum size of string buffer. + * @param size Optional parameter to store the number of bytes written to the buffer. + * @return True on success. False if the key is not set, or the key is set + * as a value or array (not a string). + * @error Invalid Handle. + */ +native bool:GetTrieString(Handle:trie, const String:key[], String:value[], max_size, &size=0); + +/** + * Removes a key entry from a Trie. + * + * @param trie Trie Handle. + * @param key Key string. + * @return True on success, false if the value was never set. + * @error Invalid Handle. + */ +native RemoveFromTrie(Handle:trie, const String:key[]); + +/** + * Clears all entries from a Trie. + * + * @param trie Trie Handle. + * @error Invalid Handle. + */ +native ClearTrie(Handle:trie); + +/** + * Retrieves the number of elements in a trie. + * + * Note that trie items are not enumerable/iteratable. If you need to + * retrieve the elements in a trie, store its keys in an ADT Array. + * + * @param trie Trie Handle. + * @return Number of elements in the trie. + * @error Invalid Handle. + */ +native GetTrieSize(Handle:trie); diff --git a/src/include/banning.inc b/src/include/banning.inc new file mode 100644 index 0000000..c3431eb --- /dev/null +++ b/src/include/banning.inc @@ -0,0 +1,157 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: banning.inc 1456 2007-09-21 14:56:18Z dvander $ + */ + +#if defined _banning_included + #endinput +#endif +#define _banning_included + +#define BANFLAG_AUTO (1<<0) /**< Auto-detects whether to ban by steamid or IP */ +#define BANFLAG_IP (1<<1) /**< Always ban by IP address */ +#define BANFLAG_AUTHID (1<<2) /**< Always ban by authstring (for BanIdentity) if possible */ +#define BANFLAG_NOKICK (1<<3) /**< Does not kick the client */ + +/** + * Called for calls to BanClient() with a non-empty command. + * + * @param client Client being banned. + * @param time Time the client is being banned for (0 = permanent). + * @param flags One if AUTHID or IP will be enabled. If AUTO is also + * enabled, it means Core autodetected which to use. + * @param reason Reason passed via BanClient(). + * @param kick_message Kick message passed via BanClient(). + * @param command Command string to identify the ban source. + * @param source Source value passed via BanClient(). + * @return Plugin_Handled to block the actual server banning. + * Kicking will still occur. + */ +forward Action:OnBanClient(client, + time, + flags, + const String:reason[], + const String:kick_message[], + const String:command[], + any:source); + +/** + * Called for calls to BanIdentity() with a non-empty command. + * + * @param identity Identity string being banned (authstring or ip). + * @param time Time the client is being banned for (0 = permanent). + * @param flags Ban flags (only IP or AUTHID are valid here). + * @param reason Reason passed via BanIdentity(). + * @param command Command string to identify the ban source. + * @param source Source value passed via BanIdentity(). + * @return Plugin_Handled to block the actual server banning. + */ +forward Action:OnBanIdentity(const String:identity[], + time, + flags, + const String:reason[], + const String:command[], + any:source); + +/** + * Called for calls to RemoveBan() with a non-empty command. + * + * @param identity Identity string being banned (authstring or ip). + * @param flags Ban flags (only IP or AUTHID are valid here). + * @param command Command string to identify the ban source. + * @param source Source value passed via BanIdentity(). + * @return Plugin_Handled to block the actual server banning. + */ +forward Action:OnRemoveBan(const String:identity[], + flags, + const String:command[], + any:source); + +/** + * Bans a client. + * + * @param client Client being banned. + * @param time Time (in minutes) to ban (0 = permanent). + * @param flags Flags for controlling the ban mechanism. If AUTHID + * is set and no AUTHID is available, the ban will fail + * unless AUTO is also flagged. + * @param reason Reason to ban the client for. + * @param kick_message Message to display to the user when kicking. + * @param command Command string to identify the source. If this is left + * empty, then the OnBanClient forward will not be called. + * @param source A source value that could be interpreted as a player + * index of any sort (not actually checked by Core). + * @return True on success, false on failure. + * @error Invalid client index or client not in game. + */ +native bool:BanClient(client, + time, + flags, + const String:reason[], + const String:kick_message[]="", + const String:command[]="", + any:source=0); + +/** + * Bans an identity (either an IP address or auth string). + * + * @param identity String to ban (ip or authstring). + * @param time Time to ban for (0 = permanent). + * @param flags Flags (only IP and AUTHID are valid flags here). + * @param reason Ban reason string. + * @param command Command string to identify the source. If this is left + * empty, then the OnBanIdentity forward will not be called. + * @param source A source value that could be interpreted as a player + * index of any sort (not actually checked by Core). + * @return True on success, false on failure. + */ +native bool:BanIdentity(const String:identity[], + time, + flags, + const String:reason[], + const String:command[]="", + any:source=0); + +/** + * Removes a ban that was written to the server (either in memory or on disk). + * + * @param identity String to unban (ip or authstring). + * @param flags Flags (only IP and AUTHID are valid flags here). + * @param command Command strnig to identify the source. If this is left + * empty, then OnRemoveBan will not be called. + * @param source A source value that could be interpreted as a player + * index of any sort (not actually checked by Core). + * @return True on success, false on failure. + */ +native bool:RemoveBan(const String:identity[], + flags, + const String:command[]="", + any:source=0); + diff --git a/src/include/bitbuffer.inc b/src/include/bitbuffer.inc new file mode 100644 index 0000000..b8741d3 --- /dev/null +++ b/src/include/bitbuffer.inc @@ -0,0 +1,325 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: bitbuffer.inc 2038 2008-04-11 17:22:19Z damagedsoul $ + */ + +#if defined _bitbuffer_included + #endinput +#endif +#define _bitbuffer_included + +/** + * Writes a single bit to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param bit Bit to write (true for 1, false for 0). + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteBool(Handle:bf, bool:bit); + +/** + * Writes a byte to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param byte Byte to write (value will be written as 8bit). + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteByte(Handle:bf, byte); + +/** + * Writes a byte to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param chr Character to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteChar(Handle:bf, chr); + +/** + * Writes a 16bit integer to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param num Integer to write (value will be written as 16bit). + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteShort(Handle:bf, num); + +/** + * Writes a 16bit unsigned integer to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param num Integer to write (value will be written as 16bit). + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteWord(Handle:bf, num); + +/** + * Writes a normal integer to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param num Integer to write (value will be written as 32bit). + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteNum(Handle:bf, num); + +/** + * Writes a floating point number to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param num Number to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteFloat(Handle:bf, Float:num); + +/** + * Writes a string to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param string Text string to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteString(Handle:bf, const String:string[]); + +/** + * Writes an entity to a writable bitbuffer (bf_write). + * @note This is a wrapper around BfWriteShort(). + * + * @param bf bf_write handle to write to. + * @param ent Entity index to write. + * @noreturn + * @error Invalid or incorrect Handle, or invalid entity. + */ +native BfWriteEntity(Handle:bf, ent); + +/** + * Writes a bit angle to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param angle Angle to write. + * @param numBits Optional number of bits to use. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteAngle(Handle:bf, Float:angle, numBits=8); + +/** + * Writes a coordinate to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param coord Coordinate to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteCoord(Handle:bf, Float:coord); + +/** + * Writes a 3D vector of coordinates to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param coord Coordinate array to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteVecCoord(Handle:bf, Float:coord[3]); + +/** + * Writes a 3D normal vector to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param vec Vector to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteVecNormal(Handle:bf, Float:vec[3]); + +/** + * Writes a 3D angle vector to a writable bitbuffer (bf_write). + * + * @param bf bf_write handle to write to. + * @param angles Angle vector to write. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfWriteAngles(Handle:bf, Float:angles[3]); + +/** + * Reads a single bit from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Bit value read. + * @error Invalid or incorrect Handle. + */ +native bool:BfReadBool(Handle:bf); + +/** + * Reads a byte from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Byte value read (read as 8bit). + * @error Invalid or incorrect Handle. + */ +native BfReadByte(Handle:bf); + +/** + * Reads a character from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Character value read. + * @error Invalid or incorrect Handle. + */ +native BfReadChar(Handle:bf); + +/** + * Reads a 16bit integer from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Integer value read (read as 16bit). + * @error Invalid or incorrect Handle. + */ +native BfReadShort(Handle:bf); + +/** + * Reads a 16bit unsigned integer from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Integer value read (read as 16bit). + * @error Invalid or incorrect Handle. + */ +native BfReadWord(Handle:bf); + +/** + * Reads a normal integer to a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Integer value read (read as 32bit). + * @error Invalid or incorrect Handle. + */ +native BfReadNum(Handle:bf); + +/** + * Reads a floating point number from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Floating point value read. + * @error Invalid or incorrect Handle. + */ +native Float:BfReadFloat(Handle:bf); + +/** + * Reads a string from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @param buffer Destination string buffer. + * @param maxlength Maximum length of output string buffer. + * @param line If true the buffer will be copied until it reaches a '\n' or a null terminator. + * @return Number of bytes written to the buffer. If the bitbuffer stream overflowed, + * that is, had no terminator before the end of the stream, then a negative + * number will be returned equal to the number of characters written to the + * buffer minus 1. The buffer will be null terminated regardless of the + * return value. + * @error Invalid or incorrect Handle. + */ +native BfReadString(Handle:bf, String:buffer[], maxlength, bool:line=false); + +/** + * Reads an entity from a readable bitbuffer (bf_read). + * @note This is a wrapper around BfReadShort(). + * + * @param bf bf_read handle to read from. + * @return Entity index read. + * @error Invalid or incorrect Handle. + */ +native BfReadEntity(Handle:bf); + +/** + * Reads a bit angle from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @param numBits Optional number of bits to use. + * @return Angle read. + * @error Invalid or incorrect Handle. + */ +native Float:BfReadAngle(Handle:bf, numBits=8); + +/** + * Reads a coordinate from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Coordinate read. + * @error Invalid or incorrect Handle. + */ +native Float:BfReadCoord(Handle:bf); + +/** + * Reads a 3D vector of coordinates from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @param coord Destination coordinate array. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfReadVecCoord(Handle:bf, Float:coord[3]); + +/** + * Reads a 3D normal vector from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @param vec Destination vector array. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfReadVecNormal(Handle:bf, Float:vec[3]); + +/** + * Reads a 3D angle vector from a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @param angles Destination angle vector. + * @noreturn + * @error Invalid or incorrect Handle. + */ +native BfReadAngles(Handle:bf, Float:angles[3]); + +/** + * Returns the number of bytes left in a readable bitbuffer (bf_read). + * + * @param bf bf_read handle to read from. + * @return Number of bytes left unread. + * @error Invalid or incorrect Handle. + */ +native BfGetNumBytesLeft(Handle:bf); diff --git a/src/include/clients.inc b/src/include/clients.inc new file mode 100644 index 0000000..eb59667 --- /dev/null +++ b/src/include/clients.inc @@ -0,0 +1,684 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: clients.inc 2362 2008-07-06 00:44:56Z dvander $ + */ + +#if defined _clients_included + #endinput +#endif +#define _clients_included + +/** + * Network flow directions. + */ +enum NetFlow +{ + NetFlow_Outgoing = 0, /**< Outgoing traffic */ + NetFlow_Incoming, /**< Incoming traffic */ + NetFlow_Both, /**< Both values added together */ +}; + +#define MAXPLAYERS 64 /**< Maximum number of players that can be in server */ +#define MAX_NAME_LENGTH 32 /**< Maximum buffer required to store a client name */ + +/** + * Called on client connection. + * + * @param client Client index. + * @param rejectmsg Buffer to store the rejection message when the connection is refused. + * @param maxlen Maximum number of characters for rejection buffer. + * @return True to validate client's connection, false to refuse it. + */ +forward bool:OnClientConnect(client, String:rejectmsg[], maxlen); + +/** + * Called when a client is entering the game. + * + * Whether a client has a steamid is undefined until OnClientAuthorized + * is called, which may occur either before or after OnClientPutInServer. + * Similarly, use OnClientPostAdminCheck() if you need to verify whether + * connecting players are admins. + * + * GetClientCount() will include clients as they are passed through this + * function, as clients are already in game at this point. + * + * @param client Client index. + * @noreturn + */ +forward OnClientPutInServer(client); + +/** + * Called when a client is disconnecting from the server. + * + * @param client Client index. + * @noreturn + */ +forward OnClientDisconnect(client); + +/** + * Called when a client is disconnected from the server. + * + * @param client Client index. + * @noreturn + */ +forward OnClientDisconnect_Post(client); + +/** + * Called when a client is sending a command. + * + * @param client Client index. + * @param args Number of arguments. + * @noreturn + */ +forward Action:OnClientCommand(client, args); + +/** + * Called whenever the client's settings are changed. + * + * @param client Client index. + * @noreturn + */ +forward OnClientSettingsChanged(client); + +/** + * Called when a client receives a Steam ID. The state of a client's + * authorization via Steam or as an admin is not guaranteed here. + * Use OnClientAuthorized() or OnClientPostAdminCheck() for those, + * respectively. + * + * This is called by bots, but the ID will be "BOT". + * + * @param client Client index. + * @param auth Client auth string. + * @noreturn + */ +forward OnClientAuthorized(client, const String:auth[]); + +/** + * Called once a client is authorized and fully in-game, but + * before admin checks are done. This can be used to override + * the default admin checks for a client. You should only use + * this for overriding; use OnClientPostAdminCheck() instead + * if you want notification. + * + * Note: If handled/blocked, PostAdminCheck must be signalled + * manually via NotifyPostAdminCheck(). + * + * This callback is gauranteed to occur on all clients, and always + * after each OnClientPutInServer() call. + * + * @param client Client index. + * @return Plugin_Handled to block admin checks. + */ +forward Action:OnClientPreAdminCheck(client); + +/** + * Called directly before OnClientPostAdminCheck() as a method to + * alter administrative permissions before plugins perform final + * post-connect operations. + * + * In general, do not use this function unless you are specifically + * attempting to change access permissions. Use OnClientPostAdminCheck() + * instead if you simply want to perform post-connect authorization + * routines. + * + * See OnClientPostAdminCheck() for more information. + * + * @param client Client index. + * @noreturn + */ +forward OnClientPostAdminFilter(client); + +/** + * Called once a client is authorized and fully in-game, and + * after all post-connection authorizations have been performed. + * + * This callback is gauranteed to occur on all clients, and always + * after each OnClientPutInServer() call. + * + * @param client Client index. + * @noreturn + */ +forward OnClientPostAdminCheck(client); + +/** + * Returns the maximum number of clients allowed on the server. This may + * return 0 if called before OnMapStart(), and thus should not be called + * in OnPluginStart(). + * + * @return Maximum number of clients allowed. + */ +native GetMaxClients(); + +/** + * Returns the client count put in the server. + * + * @param inGameOnly If false connecting players are also counted. + * @return Client count in the server. + */ +native GetClientCount(bool:inGameOnly=true); + +/** + * Returns the client's name. + * + * @param client Player index. + * @param name Buffer to store the client's name. + * @param maxlen Maximum length of string buffer (includes NULL terminator). + * @return True on success, false otherwise. + * @error If the client is not connected an error will be thrown. + */ +native bool:GetClientName(client, String:name[], maxlen); + +/** + * Retrieves a client's IP address. + * + * @param client Player index. + * @param name Buffer to store the client's ip address. + * @param maxlen Maximum length of string buffer (includes NULL terminator). + * @param remport Remove client's port from the ip string (true by default). + * @return True on success, false otherwise. + * @error If the client is not connected or the index is invalid. + */ +native bool:GetClientIP(client, String:ip[], maxlen, bool:remport=true); + +/** + * Retrieves a client's authentication string (SteamID). + * + * @param client Player index. + * @param auth Buffer to store the client's auth string. + * @param maxlen Maximum length of string buffer (includes NULL terminator). + * @return True on success, false otherwise. + * @error If the client is not connected or the index is invalid. + */ +native bool:GetClientAuthString(client, String:auth[], maxlen); + +/** + * Retrieves a client's user id, which is an index incremented for every client + * that joins the server. + * + * @param client Player index. + * @return User id of the client. + * @error If the client is not connected or the index is invalid. + */ +native GetClientUserId(client); + +/** + * Returns if a certain player is connected. + * + * @param client Player index. + * @return True if player is connected to the server, false otherwise. + */ +native bool:IsClientConnected(client); + +/** + * Returns if a certain player has entered the game. + * + * @param client Player index (index does not have to be connected). + * @return True if player has entered the game, false otherwise. + * @error Invalid client index. + */ +native bool:IsClientInGame(client); + +/** + * Returns if a client is in the "kick queue" (i.e. the client will be kicked + * shortly and thus they should not appear as valid). + * + * @param client Player index (must be connected). + * @return True if in the kick queue, false otherwise. + * @error Invalid client index. + */ +native bool:IsClientInKickQueue(client); + +/** + * Backwards compatibility stock - use IsClientInGame + * @deprecated Renamed to IsClientInGame + */ +#pragma deprecated Use IsClientInGame() instead +stock bool:IsPlayerInGame(client) +{ + return IsClientInGame(client); +} + +/** + * Returns if a certain player has been authenticated. + * + * @param client Player index. + * @return True if player has been authenticated, false otherwise. + */ +native bool:IsClientAuthorized(client); + +/** + * Returns if a certain player is a fake client. + * + * @param client Player index. + * @return True if player is a fake client, false otherwise. + */ +native bool:IsFakeClient(client); + +/** + * Returns if a certain player is an observer/spectator. + * + * @param client Player index. + * @return True if player is an obverser, false otherwise. + */ +native bool:IsClientObserver(client); + +/** + * Returns if the client is alive or dead. + * + * Note: This function was originally in SDKTools and was moved to core. + * + * @param client Player's index. + * @return True if the client is alive, false otherwise. + * @error Invalid client index, client not in game, or no mod support. + */ +native bool:IsPlayerAlive(client); + +/** + * Retrieves values from client replicated keys. + * + * @param client Player's index. + * @param key Key string. + * @param value Buffer to store value. + * @param maxlen Maximum length of valve (UTF-8 safe). + * @return True on success, false otherwise. + * @error Invalid client index, or client not connected. + */ +native bool:GetClientInfo(client, const String:key[], String:value[], maxlen); + +/** + * Retrieves a client's team index. + * + * @param client Player's index. + * @return Team index the client is on (mod specific). + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientTeam(client); + +/** + * Sets a client's AdminId. + * + * @param client Player's index. + * @param id AdminId to set. INVALID_ADMIN_ID removes admin permissions. + * @param temp True if the id should be freed on disconnect. + * @noreturn + * @error Invalid client index, client not connected, or bogus AdminId. + */ +native SetUserAdmin(client, AdminId:id, bool:temp=false); + +/** + * Retrieves a client's AdminId. + * + * @param client Player's index. + * @return AdminId of the client, or INVALID_ADMIN_ID if none. + * @error Invalid client index, or client not connected. + */ +native AdminId:GetUserAdmin(client); + +/** + * Sets access flags on a client. If the client is not an admin, + * a temporary, anonymous AdminId is given. + * + * @param client Player's index. + * @param ... Flags to set on the client. + * @noreturn + * @error Invalid client index, or client not connected. + */ +native AddUserFlags(client, AdminFlag:...); + +/** + * Removes flags from a client. If the client is not an admin, + * this has no effect. + * + * @param client Player's index. + * @param ... Flags to remove from the client. + * @noreturn + * @error Invalid client index, or client not connected. + */ +native RemoveUserFlags(client, AdminFlag:...); + +/** + * Sets access flags on a client using bits instead of flags. If the + * client is not an admin, and flags not 0, a temporary, anonymous AdminId is given. + * + * @param client Player's index. + * @param flags Bitstring of flags to set on client. + * @noreturn + */ +native SetUserFlagBits(client, flags); + +/** + * Returns client access flags. If the client is not an admin, + * the result is always 0. + * + * @param client Player's index. + * @return Flags + * @error Invalid client index, or client not connected. + */ +native GetUserFlagBits(client); + +/** + * Returns whether a user can target another user. + * This is a helper function for CanAdminTarget. + * + * @param client Player's index. + * @param target Target player's index. + * @return True if target is targettable by the player, false otherwise. + * @error Invalid or unconnected player indexers. + */ +native bool:CanUserTarget(client, target); + +/** + * Runs through the Core-defined admin authorization checks on a player. + * Has no effect if the player is already an admin. + * + * Note: This function is based on the internal cache only. + * + * @param client Client index. + * @return True if access was changed, false if it did not. + * @error Invalid client index or client not in-game AND authorized. + */ +native bool:RunAdminCacheChecks(client); + +/** + * Signals that a player has completed post-connection admin checks. + * Has no effect if the player has already had this event signalled. + * + * Note: This must be sent even if no admin id was assigned. + * + * @param client Client index. + * @noreturn + * @error Invalid client index or client not in-game AND authorized. + */ +native NotifyPostAdminCheck(client); + +/** + * Creates a fake client. + * + * @param name Name to use. + * @return Client index on success, 0 otherwise. + */ +native CreateFakeClient(const String:name[]); + +/** + * Sets a convar value on a fake client. + * + * @param client Client index. + * @param cvar ConVar name. + * @param value ConVar value. + * @noreturn + * @error Invalid client index, client not connected, + * or client not a fake client. + */ +native SetFakeClientConVar(client, const String:cvar[], const String:value[]); + +/** + * Returns the client's health. + * + * @param client Player's index. + * @return Health value. + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientHealth(client); + +/** + * Returns the client's model name. + * + * @param client Player's index. + * @param model Buffer to store the client's model name. + * @param maxlen Maximum length of string buffer (includes NULL terminator). + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientModel(client, String:model[], maxlen); + +/** + * Returns the client's weapon name. + * + * @param client Player's index. + * @param weapon Buffer to store the client's weapon name. + * @param maxlen Maximum length of string buffer (includes NULL terminator). + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientWeapon(client, String:weapon[], maxlen); + +/** + * Returns the client's max size vector. + * + * @param client Player's index. + * @param vec Destination vector to store the client's max size. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientMaxs(client, Float:vec[3]); + +/** + * Returns the client's min size vector. + * + * @param client Player's index. + * @param vec Destination vector to store the client's min size. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientMins(client, Float:vec[3]); + +/** + * Returns the client's position angle. + * + * @param client Player's index. + * @param ang Destination vector to store the client's position angle. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientAbsAngles(client, Float:ang[3]); + +/** + * Returns the client's origin vector. + * + * @param client Player's index. + * @param vec Destination vector to store the client's origin vector. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientAbsOrigin(client, Float:vec[3]); + +/** + * Returns the client's armor. + * + * @param client Player's index. + * @return Armor value. + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientArmor(client); + +/** + * Returns the client's death count. + * + * @param client Player's index. + * @return Death count. + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientDeaths(client); + +/** + * Returns the client's frag count. + * + * @param client Player's index. + * @return Frag count. + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientFrags(client); + +/** + * Returns the client's send data rate in bytes/sec. + * + * @param client Player's index. + * @return Data rate. + * @error Invalid client index, client not in game, or fake client. + */ +native GetClientDataRate(client); + +/** + * Returns if a client is timing out + * + * @param client Player's index. + * @return True if client is timing out, false otherwise. + * @error Invalid client index, client not in game, or fake client. + */ +native bool:IsClientTimingOut(client); + +/** + * Returns the client's connection time in seconds. + * + * @param client Player's index. + * @return Connection time. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientTime(client); + +/** + * Returns the client's current latency (RTT), more accurate than GetAvgLatency but jittering. + * + * @param client Player's index. + * @param flow Traffic flowing direction. + * @return Latency. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientLatency(client, NetFlow:flow); + +/** + * Returns the client's average packet latency in seconds. + * + * @param client Player's index. + * @param flow Traffic flowing direction. + * @return Average latency. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientAvgLatency(client, NetFlow:flow); + +/** + * Returns the client's average packet loss, values go from 0 to 1 (for percentages). + * + * @param client Player's index. + * @param flow Traffic flowing direction. + * @return Average packet loss. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientAvgLoss(client, NetFlow:flow); + +/** + * Returns the client's average packet choke, values go from 0 to 1 (for percentages). + * + * @param client Player's index. + * @param flow Traffic flowing direction. + * @return Average packet choke. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientAvgChoke(client, NetFlow:flow); + +/** + * Returns the client's data flow in bytes/sec. + * + * @param client Player's index. + * @param flow Traffic flowing direction. + * @return Data flow. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientAvgData(client, NetFlow:flow); + +/** + * Returns the client's average packet frequency in packets/sec. + * + * @param client Player's index. + * @param flow Traffic flowing direction. + * @return Packet frequency. + * @error Invalid client index, client not in game, or fake client. + */ +native Float:GetClientAvgPackets(client, NetFlow:flow); + +/** + * Translates an userid index to the real player index. + * + * @param userid Userid value. + * @return Client value. + * @error Returns 0 if invalid userid. + */ +native GetClientOfUserId(userid); + +/** + * Disconnects a client from the server as soon as the next frame starts. + * + * Note: Originally, KickClient() was immediate. The delay was introduced + * because despite warnings, plugins were using it in ways that would crash. + * The new safe version can break cases that rely on immediate disconnects, + * but ensures that plugins do not accidentally cause crashes. + * + * If you need immediate disconnects, use KickClientEx(). + * + * Note: IsClientInKickQueue() will return true before the kick occurs. + * + * @param client Client index. + * @param format Optional formatting rules for disconnect reason. + * Note that a period is automatically appended to the string by the engine. + * @param ... Variable number of format parameters. + * @noreturn + * @error Invalid client index, or client not connected. + */ +native KickClient(client, const String:format[]="", any:...); + +/** + * Immediately disconnects a client from the server. + * + * Kicking clients from certain events or callbacks may cause crashes. If in + * doubt, create a short (0.1 second) timer to kick the client in the next + * available frame. + * + * @param client Client index. + * @param format Optional formatting rules for disconnect reason. + * Note that a period is automatically appended to the string by the engine. + * @param ... Variable number of format parameters. + * @noreturn + * @error Invalid client index, or client not connected. + */ +native KickClientEx(client, const String:format[]="", any:...); + +/** + * Changes a client's team through the mod's generic team changing function. + * On CS:S, this will kill the player. + * + * @param client Client index. + * @param team Mod-specific team index. + * @noreturn + * @error Invalid client index, client not connected, or lack of + * mod support. + */ +native ChangeClientTeam(client, team); + diff --git a/src/include/commandfilters.inc b/src/include/commandfilters.inc new file mode 100644 index 0000000..f00a654 --- /dev/null +++ b/src/include/commandfilters.inc @@ -0,0 +1,134 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: commandfilters.inc 2080 2008-04-19 00:44:11Z dvander $ + */ + +#if defined _commandfilters_included + #endinput +#endif +#define _commandfilters_included + +#define MAX_TARGET_LENGTH 64 + +#define COMMAND_FILTER_ALIVE (1<<0) /**< Only allow alive players */ +#define COMMAND_FILTER_DEAD (1<<1) /**< Only filter dead players */ +#define COMMAND_FILTER_CONNECTED (1<<2) /**< Allow players not fully in-game */ +#define COMMAND_FILTER_NO_IMMUNITY (1<<3) /**< Ignore immunity rules */ +#define COMMAND_FILTER_NO_MULTI (1<<4) /**< Do not allow multiple target patterns */ +#define COMMAND_FILTER_NO_BOTS (1<<5) /**< Do not allow bots to be targetted */ + +#define COMMAND_TARGET_NONE 0 /**< No target was found */ +#define COMMAND_TARGET_NOT_ALIVE -1 /**< Single client is not alive */ +#define COMMAND_TARGET_NOT_DEAD -2 /**< Single client is not dead */ +#define COMMAND_TARGET_NOT_IN_GAME -3 /**< Single client is not in game */ +#define COMMAND_TARGET_IMMUNE -4 /**< Single client is immune */ +#define COMMAND_TARGET_EMPTY_FILTER -5 /**< A multi-filter (such as @all) had no targets */ +#define COMMAND_TARGET_NOT_HUMAN -6 /**< Target was not human */ +#define COMMAND_TARGET_AMBIGUOUS -7 /**< Partial name had too many targets */ + +/** + * Processes a generic command target string, and resolves it to a list + * of clients or one client, based on filtering rules and a pattern. + * + * Note that you should use LoadTranslations("common.phrases") in OnPluginStart(), + * as that file is guaranteed to contain all of the translatable phrases that + * ProcessTargetString() will return. + * + * @param pattern Pattern to find clients against. + * @param admin Admin performing the action, or 0 if the server. + * @param targets Array to hold targets. + * @param max_targets Maximum size of the targets array. + * @param filter_flags Filter flags. + * @param target_name Buffer to store the target name. + * @param tn_maxlength Maximum length of the target name buffer. + * @param tn_is_ml OUTPUT: Will be true if the target name buffer is an ML phrase, + * false if it is a normal string. + * @return If a multi-target pattern was used, the number of clients found + * is returned. If a single-target pattern was used, 1 is returned + * if one valid client is found. Otherwise, a COMMAND_TARGET reason + * for failure is returned. + */ +native ProcessTargetString(const String:pattern[], + admin, + targets[], + max_targets, + filter_flags, + String:target_name[], + tn_maxlength, + &bool:tn_is_ml); + +/** + * Replies to a client with a given message describing a targetting + * failure reason. + * + * Note: The translation phrases are found in common.phrases.txt. + * + * @param client Client index, or 0 for server. + * @param reason COMMAND_TARGET reason. + * @noreturn + */ +stock ReplyToTargetError(client, reason) +{ + switch (reason) + { + case COMMAND_TARGET_NONE: + { + ReplyToCommand(client, "[SM] %t", "No matching client"); + } + case COMMAND_TARGET_NOT_ALIVE: + { + ReplyToCommand(client, "[SM] %t", "Target must be alive"); + } + case COMMAND_TARGET_NOT_DEAD: + { + ReplyToCommand(client, "[SM] %t", "Target must be dead"); + } + case COMMAND_TARGET_NOT_IN_GAME: + { + ReplyToCommand(client, "[SM] %t", "Target is not in game"); + } + case COMMAND_TARGET_IMMUNE: + { + ReplyToCommand(client, "[SM] %t", "Unable to target"); + } + case COMMAND_TARGET_EMPTY_FILTER: + { + ReplyToCommand(client, "[SM] %t", "No matching clients"); + } + case COMMAND_TARGET_NOT_HUMAN: + { + ReplyToCommand(client, "[SM] %t", "Cannot target bot"); + } + case COMMAND_TARGET_AMBIGUOUS: + { + ReplyToCommand(client, "[SM] %t", "More than one client matched"); + } + } +} diff --git a/src/include/console.inc b/src/include/console.inc new file mode 100644 index 0000000..578054a --- /dev/null +++ b/src/include/console.inc @@ -0,0 +1,779 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: console.inc 2482 2008-09-02 03:58:23Z dvander $ + */ + +#if defined _console_included + #endinput +#endif +#define _console_included + +#define INVALID_FCVAR_FLAGS (-1) + +/** + * Console variable bound values used with Get/SetConVarBounds() + */ +enum ConVarBounds +{ + ConVarBound_Upper = 0, + ConVarBound_Lower +}; + +/** + * Console variable query helper values. + */ +enum QueryCookie +{ + QUERYCOOKIE_FAILED = 0, +}; + +/** + * Reply sources for commands. + */ +enum ReplySource +{ + SM_REPLY_TO_CONSOLE = 0, + SM_REPLY_TO_CHAT = 1, +}; + +/** + * Console variable query result values. + */ +enum ConVarQueryResult +{ + ConVarQuery_Okay = 0, /**< Retrieval of client convar value was successful. */ + ConVarQuery_NotFound, /**< Client convar was not found. */ + ConVarQuery_NotValid, /**< A console command with the same name was found, but there is no convar. */ + ConVarQuery_Protected /**< Client convar was found, but it is protected. The server cannot retrieve its value. */ +}; + +/** + * @section Flags for console commands and console variables. The descriptions + * for each constant come directly from the Source SDK. + */ +#define FCVAR_NONE 0 /**< The default, no flags at all */ +#define FCVAR_UNREGISTERED (1<<0) /**< If this is set, don't add to linked list, etc. */ +#define FCVAR_LAUNCHER (1<<1) /**< Defined by launcher. */ +#define FCVAR_GAMEDLL (1<<2) /**< Defined by the game DLL. */ +#define FCVAR_CLIENTDLL (1<<3) /**< Defined by the client DLL. */ +#define FCVAR_MATERIAL_SYSTEM (1<<4) /**< Defined by the material system. */ +#define FCVAR_PROTECTED (1<<5) /**< It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value. */ +#define FCVAR_SPONLY (1<<6) /**< This cvar cannot be changed by clients connected to a multiplayer server. */ +#define FCVAR_ARCHIVE (1<<7) /**< Set to cause it to be saved to vars.rc */ +#define FCVAR_NOTIFY (1<<8) /**< Notifies players when changed. */ +#define FCVAR_USERINFO (1<<9) /**< Changes the client's info string. */ +#define FCVAR_PRINTABLEONLY (1<<10) /**< This cvar's string cannot contain unprintable characters (e.g., used for player name, etc.) */ +#define FCVAR_UNLOGGED (1<<11) /**< If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log */ +#define FCVAR_NEVER_AS_STRING (1<<12) /**< Never try to print that cvar. */ +#define FCVAR_REPLICATED (1<<13) /**< Server setting enforced on clients. */ +#define FCVAR_CHEAT (1<<14) /**< Only useable in singleplayer / debug / multiplayer & sv_cheats */ +#define FCVAR_STUDIORENDER (1<<15) /**< Defined by the studiorender system. */ +#define FCVAR_DEMO (1<<16) /**< Record this cvar when starting a demo file. */ +#define FCVAR_DONTRECORD (1<<17) /**< Don't record these command in demo files. */ +#define FCVAR_PLUGIN (1<<18) /**< Defined by a 3rd party plugin. */ +#define FCVAR_DATACACHE (1<<19) /**< Defined by the datacache system. */ +#define FCVAR_TOOLSYSTEM (1<<20) /**< Defined by an IToolSystem library */ +#define FCVAR_FILESYSTEM (1<<21) /**< Defined by the file system. */ +#define FCVAR_NOT_CONNECTED (1<<22) /**< Cvar cannot be changed by a client that is connected to a server. */ +#define FCVAR_SOUNDSYSTEM (1<<23) /**< Defined by the soundsystem library. */ +#define FCVAR_ARCHIVE_XBOX (1<<24) /**< Cvar written to config.cfg on the Xbox. */ +#define FCVAR_INPUTSYSTEM (1<<25) /**< Defined by the inputsystem DLL. */ +#define FCVAR_NETWORKSYSTEM (1<<26) /**< Defined by the network system. */ +#define FCVAR_VPHYSICS (1<<27) /**< Defined by vphysics. */ + +/** + * @endsection + */ + +/** + * Executes a server command as if it were on the server console (or RCON) + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +native ServerCommand(const String:format[], any:...); + +/** + * Inserts a server command at the beginning of the server command buffer. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +native InsertServerCommand(const String:format[], any:...); + +/** + * Executes every command in the server's command buffer, rather than once per frame. + * + * @noreturn + */ +native ServerExecute(); + +/** + * Executes a client command. Note that this will not work on clients unless + * they have cl_restrict_server_commands set to 0. + * + * @param client Index of the client. + * @param fmt Format of the client command. + * @param ... Format parameters/ + * @noreturn + * @error Invalid client index, or client not connected. + */ +native ClientCommand(client, const String:fmt[], any:...); + +/** + * Executes a client command on the server without being networked. + * + * FakeClientCommand() overwrites the command tokenization buffer. This can + * cause undesired effects because future calls to GetCmdArg* will return + * data from the FakeClientCommand(), not the parent command. If you are in + * a hook where this matters (for example, a "say" hook), you should use + * FakeClientCommandEx() instead. + * + * @param client Index of the client. + * @param fmt Format of the client command. + * @param ... Format parameters + * @noreturn + * @error Invalid client index, or client not connected. + */ +native FakeClientCommand(client, const String:fmt[], any:...); + +/** + * Executes a client command on the server without being networked. The + * execution of the client command is delayed by one frame to prevent any + * re-entrancy issues that might surface with FakeClientCommand(). + * + * @param client Index of the client. + * @param fmt Format of the client command. + * @param ... Format parameters + * @noreturn + * @error Invalid client index, or client not connected. + */ +native FakeClientCommandEx(client, const String:fmt[], any:...); + +/** + * Sends a message to the server console. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +native PrintToServer(const String:format[], any:...); + +/** + * Sends a message to a client's console. + * + * @param client Client index. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native PrintToConsole(client, const String:format[], any:...); + +/** + * Reples to a message in a command. + * + * A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. + * If the command was from chat, PrintToChat() is used. + * + * @param client Client index, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error If the client is not connected or invalid. + */ +native ReplyToCommand(client, const String:format[], any:...); + +/** + * Returns the current reply source of a command. + * + * @return ReplySource value. + */ +native ReplySource:GetCmdReplySource(); + +/** + * Sets the current reply source of a command. + * + * Only use this if you know what you are doing. You should save the old value + * and restore it once you are done. + * + * @param source New ReplySource value. + * @return Old ReplySource value. + */ +native ReplySource:SetCmdReplySource(ReplySource:source); + +/** + * Returns whether the current say hook is a chat trigger. + * + * This function is only meaningful inside say or say_team hooks. + * + * @return True if a chat trigger, false otherwise. + */ +native bool:IsChatTrigger(); + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. All users receive a message + * in their chat text, except for the originating client, who receives + * the message based on the current ReplySource. + * + * @param client Client index doing the action, or 0 for server. + * @param tag Tag to prepend to the message. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +native ShowActivity2(client, const String:tag[], const String:format[], any:...); + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. + * + * This version does not display a message to the originating client + * if used from chat triggers or menus. If manual replies are used + * for these cases, then this function will suffice. Otherwise, + * ShowActivity2() is slightly more useful. + * + * @param client Client index doing the action, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +native ShowActivity(client, const String:format[], any:...); + +/** + * Same as ShowActivity(), except the tag parameter is used instead of + * "[SM] " (note that you must supply any spacing). + * + * @param client Client index doing the action, or 0 for server. + * @param tag Tag to display with. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +native ShowActivityEx(client, const String:tag[], const String:format[], any:...); + +/** + * Called when a server-only command is invoked. + * + * @params args Number of arguments that were in the argument string. + * @return An Action value. Not handling the command + * means that Source will report it as "not found." + */ +functag SrvCmd Action:public(args); + +/** + * Creates a server-only console command, or hooks an already existing one. + * + * Server commands are case sensitive. + * + * @param cmd Name of the command to hook or create. + * @param callback A function to use as a callback for when the command is invoked. + * @param description Optional description to use for command creation. + * @param flags Optional flags to use for command creation. + * @noreturn + * @error Command name is the same as an existing convar. + */ +native RegServerCmd(const String:cmd[], SrvCmd:callback, const String:description[]="", flags=0); + +/** + * Called when a generic console command is invoked. + * + * @param client Index of the client, or 0 from the server. + * @param args Number of arguments that were in the argument string. + * @return An Action value. Not handling the command + * means that Source will report it as "not found." + */ +functag ConCmd Action:public(client, args); + +/** + * Creates a console command, or hooks an already existing one. + * + * Console commands are case sensitive. However, if the command already exists in the game, + * the a client may enter the command in any case. SourceMod corrects for this automatically, + * and you should only hook the "real" version of the command. + * + * @param cmd Name of the command to hook or create. + * @param callback A function to use as a callback for when the command is invoked. + * @param description Optional description to use for command creation. + * @param flags Optional flags to use for command creation. + * @noreturn + * @error Command name is the same as an existing convar. + */ +native RegConsoleCmd(const String:cmd[], ConCmd:callback, const String:description[]="", flags=0); + +/** + * Creates a console command as an administrative command. If the command does not exist, + * it is created. When this command is invoked, the access rights of the player are + * automatically checked before allowing it to continue. + * + * Admin commands are case sensitive from both the client and server. + * + * @param cmd String containing command to register. + * @param callback A function to use as a callback for when the command is invoked. + * @param adminflags Administrative flags (bitstring) to use for permissions. + * @param description Optional description to use for help. + * @param group String containing the command group to use. If empty, + * the plugin's filename will be used instead. + * @param flags Optional console flags. + * @noreturn + * @error Command name is the same as an existing convar. + */ +native RegAdminCmd(const String:cmd[], + ConCmd:callback, + adminflags, + const String:description[]="", + const String:group[]="", + flags=0); + +/** + * Returns the number of arguments from the current console or server command. + * @note Unlike the HL2 engine call, this does not include the command itself. + * + * @return Number of arguments to the current command. + */ +native GetCmdArgs(); + +/** + * Retrieves a command argument given its index, from the current console or + * server command. + * @note Argument indexes start at 1; 0 retrieves the command name. + * + * @param argnum Argument number to retrieve. + * @param buffer Buffer to use for storing the string. + * @param maxlength Maximum length of the buffer. + * @return Length of string written to buffer. + */ +native GetCmdArg(argnum, String:buffer[], maxlength); + +/** + * Retrieves the entire command argument string in one lump from the current + * console or server command. + * + * @param buffer Buffer to use for storing the string. + * @param maxlength Maximum length of the buffer. + * @return Length of string written to buffer. + */ +native GetCmdArgString(String:buffer[], maxlength); + +/** + * Creates a new console variable. + * + * @param name Name of new convar. + * @param defaultValue String containing the default value of new convar. + * @param description Optional description of the convar. + * @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details. + * @param hasMin Optional boolean that determines if the convar has a minimum value. + * @param min Minimum floating point value that the convar can have if hasMin is true. + * @param hasMax Optional boolean that determines if the convar has a maximum value. + * @param max Maximum floating point value that the convar can have if hasMax is true. + * @return A handle to the newly created convar. If the convar already exists, a handle to it will still be returned. + * @error Convar name is blank or is the same as an existing console command. + */ +native Handle:CreateConVar(const String:name[], const String:defaultValue[], const String:description[]="", flags=0, bool:hasMin=false, Float:min=0.0, bool:hasMax=false, Float:max=0.0); + +/** + * Searches for a console variable. + * + * @param name Name of convar to find. + * @return A handle to the convar if it is found. INVALID_HANDLE otherwise. + */ +native Handle:FindConVar(const String:name[]); + +/** + * Called when a console variable's value is changed. + * + * @param convar Handle to the convar that was changed. + * @param oldValue String containing the value of the convar before it was changed. + * @param newValue String containing the new value of the convar. + * @noreturn + */ +functag ConVarChanged public(Handle:convar, const String:oldValue[], const String:newValue[]); + +/** + * Creates a hook for when a console variable's value is changed. + * + * @param convar Handle to the convar. + * @param callback An OnConVarChanged function pointer. + * @noreturn + * @error Invalid or corrupt Handle or invalid callback function. + */ +native HookConVarChange(Handle:convar, ConVarChanged:callback); + +/** + * Removes a hook for when a console variable's value is changed. + * + * @param convar Handle to the convar. + * @param callback An OnConVarChanged function pointer. + * @noreturn + * @error Invalid or corrupt Handle, invalid callback function, or no active hook on convar. + */ +native UnhookConVarChange(Handle:convar, ConVarChanged:callback); + +/** + * Returns the boolean value of a console variable. + * + * @param convar Handle to the convar. + * @return The boolean value of the convar. + * @error Invalid or corrupt Handle. + */ +native bool:GetConVarBool(Handle:convar); + +/** + * Sets the boolean value of a console variable. + * + * @param convar Handle to the convar. + * @param value New boolean value. + * @param replicate If set to true, the new convar value will be set on all clients. + * This will only work if the convar has the FCVAR_REPLICATED flag + * and actually exists on clients. + * @param notify If set to true, clients will be notified that the convar has changed. + * This will only work if the convar has the FCVAR_NOTIFY flag. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetConVarBool(Handle:convar, bool:value, bool:replicate=false, bool:notify=false); + +/** + * Returns the integer value of a console variable. + * + * @param convar Handle to the convar. + * @return The integer value of the convar. + * @error Invalid or corrupt Handle. + */ +native GetConVarInt(Handle:convar); + +/** + * Sets the integer value of a console variable. + * + * @param convar Handle to the convar. + * @param value New integer value. + * @param replicate If set to true, the new convar value will be set on all clients. + * This will only work if the convar has the FCVAR_REPLICATED flag + * and actually exists on clients. + * @param notify If set to true, clients will be notified that the convar has changed. + * This will only work if the convar has the FCVAR_NOTIFY flag. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetConVarInt(Handle:convar, value, bool:replicate=false, bool:notify=false); + +/** + * Returns the floating point value of a console variable. + * + * @param convar Handle to the convar. + * @return The floating point value of the convar. + * @error Invalid or corrupt Handle. + */ +native Float:GetConVarFloat(Handle:convar); + +/** + * Sets the floating point value of a console variable. + * + * @param convar Handle to the convar. + * @param value New floating point value. + * @param replicate If set to true, the new convar value will be set on all clients. + * This will only work if the convar has the FCVAR_REPLICATED flag + * and actually exists on clients. + * @param notify If set to true, clients will be notified that the convar has changed. + * This will only work if the convar has the FCVAR_NOTIFY flag. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetConVarFloat(Handle:convar, Float:value, bool:replicate=false, bool:notify=false); + +/** + * Retrieves the string value of a console variable. + * + * @param convar Handle to the convar. + * @param value Buffer to store the value of the convar. + * @param maxlength Maximum length of string buffer. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native GetConVarString(Handle:convar, String:value[], maxlength); + +/** + * Sets the string value of a console variable. + * + * @param convar Handle to the convar. + * @param value New string value. + * @param replicate If set to true, the new convar value will be set on all clients. + * This will only work if the convar has the FCVAR_REPLICATED flag + * and actually exists on clients. + * @param notify If set to true, clients will be notified that the convar has changed. + * This will only work if the convar has the FCVAR_NOTIFY flag. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetConVarString(Handle:convar, const String:value[], bool:replicate=false, bool:notify=false); + +/** + * Resets the console variable to its default value. + * + * @param convar Handle to the convar. + * @param replicate If set to true, the new convar value will be set on all clients. + * This will only work if the convar has the FCVAR_REPLICATED flag + * and actually exists on clients. + * @param notify If set to true, clients will be notified that the convar has changed. + * This will only work if the convar has the FCVAR_NOTIFY flag. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native ResetConVar(Handle:convar, bool:replicate=false, bool:notify=false); + +/** + * Returns the bitstring of flags on a console variable. + * + * @param convar Handle to the convar. + * @return A bitstring containing the FCVAR_* flags that are enabled. + * @error Invalid or corrupt Handle. + */ +native GetConVarFlags(Handle:convar); + +/** + * Sets the bitstring of flags on a console variable. + * + * @param convar Handle to the convar. + * @param flags A bitstring containing the FCVAR_* flags to enable. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetConVarFlags(Handle:convar, flags); + +/** + * Retrieves the specified bound of a console variable. + * + * @param convar Handle to the convar. + * @param type Type of bound to retrieve, ConVarBound_Lower or ConVarBound_Upper. + * @param value By-reference cell to store the specified floating point bound value. + * @return True if the convar has the specified bound set, false otherwise. + * @error Invalid or corrupt Handle. + */ +native bool:GetConVarBounds(Handle:convar, ConVarBounds:type, &Float:value); + +/** + * Sets the specified bound of a console variable. + * + * @param convar Handle to the convar. + * @param type Type of bound to set, ConVarBound_Lower or ConVarBound_Upper + * @param set If set to true, convar will use specified bound. If false, bound will be removed. + * @param value Floating point value to use as the specified bound. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetConVarBounds(Handle:convar, ConVarBounds:type, bool:set, Float:value=0.0); + +/** + * Retrieves the name of a console variable. + * + * @param convar Handle to the convar. + * @param value Buffer to store the name of the convar. + * @param maxlength Maximum length of string buffer. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native GetConVarName(Handle:convar, const String:name[], maxlength); + +funcenum ConVarQueryFinished +{ + /** + * Called when a query to retrieve a client's console variable has finished. + * + * @param cookie Unique identifier of query. + * @param client Player index. + * @param result Result of query that tells one whether or not query was successful. + * See ConVarQueryResult enum for more details. + * @param convarName Name of client convar that was queried. + * @param convarValue Value of client convar that was queried if successful. This will be "" if it was not. + * @param value Value that was passed when query was started. + * @noreturn + */ + public(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[], any:value), + + /** + * Called when a query to retrieve a client's console variable has finished. + * + * @param cookie Unique identifier of query. + * @param client Player index. + * @param result Result of query that tells one whether or not query was successful. + * See ConVarQueryResult enum for more details. + * @param convarName Name of client convar that was queried. + * @param convarValue Value of client convar that was queried if successful. This will be "" if it was not. + * @noreturn + */ + public(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[]) +}; + +/** + * Starts a query to retrieve the value of a client's console variable. + * + * @param client Player index. + * @param name Name of client convar to query. + * @param callback A function to use as a callback when the query has finished. + * @param value Optional value to pass to the callback function. + * @return A cookie that uniquely identifies the query. + * Returns QUERYCOOKIE_FAILED on failure, such as when used on a bot. + */ +native QueryCookie:QueryClientConVar(client, const String:cvarName[], ConVarQueryFinished:callback, any:value=0); + +/** + * Gets a command iterator. Must be freed with CloseHandle(). + * + * @return A new command iterator. + */ +native Handle:GetCommandIterator(); + +/** + * Reads a command iterator, then advances to the next command if any. + * Only SourceMod specific commands are returned. + * + * @param iter Command iterator Handle. + * @param name Name buffer. + * @param nameLen Name buffer size. + * @param eflags Effective default flags of a command. + * @param desc Command description buffer. + * @param descLen Command description buffer size. + * @return True on success, false if there are no more commands. + */ +native bool:ReadCommandIterator(Handle:iter, + String:name[], + nameLen, + &eflags=0, + String:desc[]="", + descLen=0); + +/** + * Returns whether a client has access to a given command string. The string + * can also be any override string, as overrides can be independent of + * commands. This important feature essentially allows you to create custom + * flags using the override system. + * + * @param client Client index. + * @param command Command name. If the command is not found, the default + * flags are used. + * @param flags Flag string to use as a default, if the command or override + * is not found. + * @param override_only If true, SourceMod will not attempt to find a matching + * command, and it will only use the default flags specified. + * Otherwise, SourceMod will ignore the default flags if + * there is a matching admin command. + * @return True if the client has access, false otherwise. + */ +native bool:CheckCommandAccess(client, + const String:command[], + flags, + bool:override_only=false); + +/** + * Returns true if the supplied character is valid in a ConVar name. + * + * @param c Character to validate. + * @return True is valid for ConVars, false otherwise + */ +stock bool:IsValidConVarChar(c) +{ + return (c == '_' || IsCharAlpha(c) || IsCharNumeric(c)); +} + +/** + * Returns the bitstring of flags of a command. + * + * @param name Name of the command. + * @return A bitstring containing the FCVAR_* flags that are enabled + * or INVALID_FCVAR_FLAGS if command not found. + */ +native GetCommandFlags(const String:name[]); + +/** + * Sets the bitstring of flags of a command. + * + * @param name Name of the command. + * @param flags A bitstring containing the FCVAR_* flags to enable. + * @return True on success, otherwise false. + */ +native bool:SetCommandFlags(const String:name[], flags); + +/** + * Starts a ConCommandBase search, traversing the list of ConVars and + * ConCommands. If a Handle is returned, the next entry must be read + * via FindNextConCommand(). The order of the list is undefined. + * + * @param buffer Buffer to store entry name. + * @param max_size Maximum size of the buffer. + * @param isCommand Variable to store whether the entry is a command. + * If it is not a command, it is a ConVar. + * @param flags Variable to store entry flags. + * @param description Buffer to store the description, empty if no description present. + * @param descrmax_size Maximum size of the description buffer. + * @return On success, a ConCmdIter Handle is returned, which + * can be read via FindNextConCommand(), and must be + * closed via CloseHandle(). Additionally, the output + * parameters will be filled with information of the + * first ConCommandBase entry. + * On failure, INVALID_HANDLE is returned, and the + * contents of outputs is undefined. + */ +native Handle:FindFirstConCommand(String:buffer[], max_size, &bool:isCommand, &flags=0, String:description[]="", descrmax_size=0); + +/** + * Reads the next entry in a ConCommandBase iterator. + * + * @param search ConCmdIter Handle to search. + * @param buffer Buffer to store entry name. + * @param max_size Maximum size of the buffer. + * @param isCommand Variable to store whether the entry is a command. + * If it is not a command, it is a ConVar. + * @param flags Variable to store entry flags. + * @param description Buffer to store the description, empty if no description present. + * @param descrmax_size Maximum size of the description buffer. + * @return On success, the outputs are filled, the iterator is + * advanced to the next entry, and true is returned. + * If no more entries exist, false is returned, and the + * contents of outputs is undefined. + */ +native bool:FindNextConCommand(Handle:search, String:buffer[], max_size, &bool:isCommand, &flags=0, String:description[]="", descrmax_size=0); + +/** + * Replicates a convar value to a specific client. This does not change the actual convar value. + * + * @param client Client index + * @param convar ConVar handle + * @param value String value to send + * @return True on success, false on failure + * @error Invalid client index, client not in game, or client is fake + */ +native bool:SendConVarValue(client, Handle:convar, const String:value[]); diff --git a/src/include/core.inc b/src/include/core.inc new file mode 100644 index 0000000..76b792f --- /dev/null +++ b/src/include/core.inc @@ -0,0 +1,141 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: core.inc 2243 2008-06-02 05:04:02Z dvander $ + */ + +#if defined _core_included + #endinput +#endif +#define _core_included + +#include + +#define SOURCEMOD_PLUGINAPI_VERSION 3 +struct PlVers +{ + version, + String:filevers[], + String:date[], + String:time[] +}; + +/** + * Function helper values. + */ +enum Function +{ + INVALID_FUNCTION = -1, +}; + +/** + * Specifies what to do after a hook completes. + */ +enum Action +{ + Plugin_Continue = 0, /**< Continue with the original action */ + Plugin_Changed = 1, /**< Inputs or outputs have been overridden with new values */ + Plugin_Handled = 3, /**< Handle the action at the end (don't call it) */ + Plugin_Stop = 4, /**< Immediately stop the hook chain and handle the original */ +}; + +/** + * Specifies identity types. + */ +enum Identity +{ + Identity_Core = 0, + Identity_Extension = 1, + Identity_Plugin = 2 +}; + +public PlVers:__version = +{ + version = SOURCEMOD_PLUGINAPI_VERSION, + filevers = SOURCEMOD_VERSION, + date = __DATE__, + time = __TIME__ +}; + +/** + * Plugin status values. + */ +enum PluginStatus +{ + Plugin_Running=0, /**< Plugin is running */ + /* All states below are "temporarily" unexecutable */ + Plugin_Paused, /**< Plugin is loaded but paused */ + Plugin_Error, /**< Plugin is loaded but errored/locked */ + /* All states below do not have all natives */ + Plugin_Loaded, /**< Plugin has passed loading and can be finalized */ + Plugin_Failed, /**< Plugin has a fatal failure */ + Plugin_Created, /**< Plugin is created but not initialized */ + Plugin_Uncompiled, /**< Plugin is not yet compiled by the JIT */ + Plugin_BadLoad, /**< Plugin failed to load */ +}; + +/** + * Plugin information properties. + */ +enum PluginInfo +{ + PlInfo_Name, /**< Plugin name */ + PlInfo_Author, /**< Plugin author */ + PlInfo_Description, /**< Plugin description */ + PlInfo_Version, /**< Plugin verison */ + PlInfo_URL, /**< Plugin URL */ +}; + +/** + * Defines how an extension must expose itself for autoloading. + */ +struct Extension +{ + const String:name[], /**< Short name */ + const String:file[], /**< Default file name */ + bool:autoload, /**< Whether or not to auto-load */ + bool:required, /**< Whether or not to require */ +}; + +/** + * Defines how a plugin must expose itself for native requiring. + */ +struct SharedPlugin +{ + const String:name[], /**< Short name */ + const String:file[], /**< File name */ + bool:required, /**< Whether or not to require */ +}; + +public Float:NULL_VECTOR[3]; /**< Pass this into certain functions to act as a C++ NULL */ +public const String:NULL_STRING[1]; /**< pass this into certain functions to act as a C++ NULL */ + +#define AUTOLOAD_EXTENSIONS +#define REQUIRE_EXTENSIONS +#define REQUIRE_PLUGIN diff --git a/src/include/cstrike.inc b/src/include/cstrike.inc new file mode 100644 index 0000000..eb5ad84 --- /dev/null +++ b/src/include/cstrike.inc @@ -0,0 +1,88 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: cstrike.inc 1828 2007-12-24 20:41:33Z pred $ + */ + +#if defined _cstrike_included + #endinput +#endif +#define _cstrike_included + +#define CS_TEAM_NONE 0 /**< No team yet. */ +#define CS_TEAM_SPECTATOR 1 /**< Spectators. */ +#define CS_TEAM_T 2 /**< Terrorists. */ +#define CS_TEAM_CT 3 /**< Counter-Terrorists. */ + +#define CS_SLOT_PRIMARY 0 /**< Primary weapon slot. */ +#define CS_SLOT_SECONDARY 1 /**< Secondary weapon slot. */ +#define CS_SLOT_GRENADE 3 /**< Grenade slot (will only return one grenade). */ +#define CS_SLOT_C4 4 /**< C4 slot. */ + +/** + * Respawns a player. + * + * @param client Player's index. + * @noreturn + * @error Invalid client index, client not in game. + */ +native CS_RespawnPlayer(client); + +/** + * Switches the player's team. + * + * @param client Player's index. + * @param team Team index. + * @noreturn + * @error Invalid client index, client not in game. + */ +native CS_SwitchTeam(client, team); + +/** + * Do not edit below this line! + */ +public Extension:__ext_cstrike = +{ + name = "cstrike", + file = "games/game.cstrike.ext", + autoload = 0, +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_cstrike_SetNTVOptional() +{ + MarkNativeAsOptional("CS_RespawnPlayer"); + MarkNativeAsOptional("CS_SwitchTeam"); +} +#endif diff --git a/src/include/datapack.inc b/src/include/datapack.inc new file mode 100644 index 0000000..a289081 --- /dev/null +++ b/src/include/datapack.inc @@ -0,0 +1,142 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: datapack.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _datapack_included + #endinput +#endif +#define _datapack_included + +/** + * Creates a new data pack. + * + * @return A Handle to the data pack. Must be closed with CloseHandle(). + */ +native Handle:CreateDataPack(); + +/** + * Packs a normal cell into a data pack. + * + * @param pack Handle to the data pack. + * @param cell Cell to add. + * @noreturn + * @error Invalid handle. + */ +native WritePackCell(Handle:pack, cell); + +/** + * Packs a float into a data pack. + * + * @param pack Handle to the data pack. + * @param val Float to add. + * @noreturn + * @error Invalid handle. + */ +native WritePackFloat(Handle:pack, Float:val); + +/** + * Packs a string into a data pack. + * + * @param pack Handle to the data pack. + * @param str String to add. + * @noreturn + * @error Invalid handle. + */ +native WritePackString(Handle:pack, const String:str[]); + +/** + * Reads a cell from a data pack. + * + * @param pack Handle to the data pack. + * @return Cell value. + * @error Invalid handle, or bounds error. + */ +native ReadPackCell(Handle:pack); + +/** + * Reads a float from a data pack. + * + * @param pack Handle to the data pack. + * @return Float value. + * @error Invalid handle, or bounds error. + */ +native Float:ReadPackFloat(Handle:pack); + +/** + * Reads a string from a data pack. + * + * @param pack Handle to the data pack. + * @param buffer Destination string buffer. + * @param maxlen Maximum length of output string buffer. + * @noreturn + * @error Invalid handle, or bounds error. + */ +native ReadPackString(Handle:pack, String:buffer[], maxlen); + +/** + * Resets the position in a data pack. + * + * @param pack Handle to the data pack. + * @param clear If true, clears the contained data. + * @noreturn + * @error Invalid handle. + */ +native ResetPack(Handle:pack, bool:clear=false); + +/** + * Returns the read or write position in a data pack. + * + * @param pack Handle to the data pack. + * @return Numerical position in the data pack. + * @error Invalid handle. + */ +native GetPackPosition(Handle:pack); + +/** + * Sets the read/write position in a data pack. + * + * @param pack Handle to the data pack. + * @param position New position to set. + * @noreturn + * @error Invalid handle, or position is beyond the pack bounds. + */ +native SetPackPosition(Handle:pack, position); + +/** + * Returns whether or not a specified number of bytes from the data pack + * position to the end can be read. + * + * @param pack Handle to the data pack. + * @param bytes Number of bytes to simulate reading. + * @return True if can be read, false otherwise. + * @error Invalid handle. + */ +native bool:IsPackReadable(Handle:pack, bytes); diff --git a/src/include/dbi.inc b/src/include/dbi.inc new file mode 100644 index 0000000..cc60e94 --- /dev/null +++ b/src/include/dbi.inc @@ -0,0 +1,633 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: dbi.inc 1918 2008-03-02 18:01:49Z dvander $ + */ + +#if defined _dbi_included + #endinput +#endif +#define _dbi_included + +/** + * @handle Driver + * + * Contains information about an SQL driver. + */ + +/** + * @handle Database + * + * Contains information about a database connection. + */ + +/** + * @handle Query + * + * Contains information about an active query and its + * result sets. + */ + +/** + * @handle Statement : Query + * + * Extends a Query Handle and can be used as a Query Handle. + * Statement Handles are for prepared queries and contain + * their own function for binding parameters. Statement + * Handles can be used instead of database Handles in a few + * select functions. + */ + +/** + * Describes a database field fetch status. + */ +enum DBResult +{ + DBVal_Error = 0, /**< Column number/field is invalid. */ + DBVal_TypeMismatch = 1, /**< You cannot retrieve this data with this type. */ + DBVal_Null = 2, /**< Field has no data (NULL) */ + DBVal_Data = 3, /**< Field has data */ +}; + +/** + * Describes binding types. + */ +enum DBBindType +{ + DBBind_Int = 0, /**< Bind an integer. */ + DBBind_Float = 1, /**< Bind a float. */ + DBBind_String = 2, /**< Bind a string. */ +}; + +/** + * Threading priority level. + */ +enum DBPriority +{ + DBPrio_High = 0, /**< High priority. */ + DBPrio_Normal = 1, /**< Normal priority. */ + DBPrio_Low = 2, /**< Low priority. */ +}; + +/** + * Creates an SQL connection from a named configuration. + * + * @param confname Named configuration. + * @param persistent True to re-use a previous persistent connection if + * possible, false otherwise. + * @param error Error buffer. + * @param maxlength Maximum length of the error buffer. + * @return A database connection Handle, or INVALID_HANDLE on failure. + */ +native Handle:SQL_Connect(const String:confname[], bool:persistent, String:error[], maxlength); + +/** + * Creates a default SQL connection. + * + * @param error Error buffer. + * @param maxlength Maximum length of the error buffer. + * @param persistent True to re-use a previous persistent connection + * if possible, false otherwise. + * @return A database connection Handle, or INVALID_HANDLE on failure. + */ +stock Handle:SQL_DefConnect(String:error[], maxlength, bool:persistent=true) +{ + return SQL_Connect("default", persistent, error, maxlength); +} + +/** + * Creates an SQL connection from specific parameters. + * + * @param driver Driver Handle, or INVALID_HANDLE for default. + * @param host Host name. + * @param user User name. + * @param pass User password. + * @param database Database name. + * @param error Error buffer. + * @param maxlength Maximum length of the error buffer. + * @param persistent True to re-use a previous persistent connection + * if possible, false otherwise. + * @param port Optional port to specify. + * @param maxTimeout Maximum timeout in seconds if applicable. + * @return A database connection Handle, or INVALID_HANDLE on failure. + * @error Invalid driver Handle other than INVALID_HANDLE. + */ +native Handle:SQL_ConnectEx(Handle:driver, + const String:host[], + const String:user[], + const String:pass[], + const String:database[], + String:error[], + maxlength, + bool:persistent=true, + port=0, + maxTimeout=0); + +/** + * Returns if a named configuration is present in databases.cfg. + * + * @param name Configuration name. + * @return True if it exists, false otherwise. + */ +native bool:SQL_CheckConfig(const String:name[]); + +/** + * Returns a driver Handle from a name string. + * + * If the driver is not found, SourceMod will attempt + * to load an extension named dbi..ext.[dll|so]. + * + * @param name Driver identification string, or an empty + * string to return the default driver. + * @return Driver Handle, or INVALID_HANDLE on failure. + */ +native Handle:SQL_GetDriver(const String:name[]=""); + +/** + * Reads the driver of an opened database. + * + * @param database Database Handle. + * @param ident Option buffer to store the identification string. + * @param ident_length Maximum length of the buffer. + * @return Driver Handle. + */ +native Handle:SQL_ReadDriver(Handle:database, String:ident[]="", ident_length=0); + +/** + * Retrieves a driver's identification string. + * + * Example: "mysql", "sqlite" + * + * @param driver Driver Handle, or INVALID_HANDLE for the default driver. + * @param ident Identification string buffer. + * @param maxlength Maximum length of the buffer. + * @noreturn + * @error Invalid Handle other than INVALID_HANDLE. + */ +native SQL_GetDriverIdent(Handle:driver, String:ident[], maxlength); + +/** + * Retrieves a driver's product string. + * + * Example: "MySQL", "SQLite" + * + * @param driver Driver Handle, or INVALID_HANDLE for the default driver. + * @param product Product string buffer. + * @param maxlength Maximum length of the buffer. + * @noreturn + * @error Invalid Handle other than INVALID_HANDLE. + */ +native SQL_GetDriverProduct(Handle:driver, String:product[], maxlength); + +/** + * Returns the number of affected rows from the last query. + * + * @param hndl A database OR statement Handle. + * @return Number of rows affected by the last query. + * @error Invalid database or statement Handle. + */ +native SQL_GetAffectedRows(Handle:hndl); + +/** + * Returns the last query's insertion id. + * + * @param hndl A database OR statement Handle. + * @return Last query's insertion id. + * @error Invalid database or statement Handle. + */ +native SQL_GetInsertId(Handle:hndl); + +/** + * Returns the error reported by the last query. + * + * @param hndl A database OR statement Handle. + * @param error Error buffer. + * @param maxlength Maximum length of the buffer. + * @return True if there was an error, false otherwise. + * @error Invalid database or statement Handle. + */ +native bool:SQL_GetError(Handle:hndl, String:error[], maxlength); + +/** + * Escapes a database string for literal insertion. This is not needed + * for binding strings in prepared statements. + * + * Generally, database strings are inserted into queries enclosed in + * single quotes ('). If user input has a single quote in it, the + * quote needs to be escaped. This function ensures that any unsafe + * characters are safely escaped according to the database engine and + * the database's character set. + * + * @param hndl A database Handle. + * @param string String to quote. + * @param buffer Buffer to store quoted string in. + * @param maxlength Maximum length of the buffer. + * @param written Optionally returns the number of bytes written. + * @return True on success, false if buffer is not big enough. + * The buffer must be at least 2*strlen(string)+1. + * @error Invalid database or statement Handle. + */ +native bool:SQL_EscapeString(Handle:database, + const String:string[], + String:buffer[], + maxlength, + &written=0); + +/** + * This is a backwards compatibility stock. You should use SQL_EscapeString() + * instead, as this function will probably be deprecated in SourceMod 1.1. + */ +stock bool:SQL_QuoteString(Handle:database, + const String:string[], + String:buffer[], + maxlength, + &written=0) +{ + return SQL_EscapeString(database, string, buffer, maxlength, written); +} + +/** + * Executes a query and ignores the result set. + * + * @param database A database Handle. + * @param query Query string. + * @param len Optional parameter to specify the query length, in + * bytes. This can be used to send binary queries that + * have a premature terminator. + * @return True if query succeeded, false otherwise. Use + * SQL_GetError to find the last error. + * @error Invalid database Handle. + */ +native bool:SQL_FastQuery(Handle:database, const String:query[], len=-1); + +/** + * Executes a simple query and returns a new query Handle for + * receiving the results. + * + * @param database A database Handle. + * @param query Query string. + * @param len Optional parameter to specify the query length, in + * bytes. This can be used to send binary queries that + * have a premature terminator. + * @return A new Query Handle on success, INVALID_HANDLE + * otherwise. The Handle must be freed with CloseHandle(). + * @error Invalid database Handle. + */ +native Handle:SQL_Query(Handle:database, const String:query[], len=-1); + +/** + * Creates a new prepared statement query. Prepared statements can + * be executed any number of times. They can also have placeholder + * parameters, similar to variables, which can be bound safely and + * securely (for example, you do not need to quote bound strings). + * + * Statement handles will work in any function that accepts a Query handle. + * + * @param database A database Handle. + * @param query Query string. + * @param error Error buffer. + * @param maxlength Maximum size of the error buffer. + * @return A new statement Handle on success, INVALID_HANDLE + * otherwise. The Handle must be freed with CloseHandle(). + * @error Invalid database Handle. + */ +native Handle:SQL_PrepareQuery(Handle:database, const String:query[], String:error[], maxlength); + +/** + * Advances to the next set of results. + * + * In some SQL implementations, multiple result sets can exist on one query. + * This is possible in MySQL with simple queries when executing a CALL + * query. If this is the case, all result sets must be processed before + * another query is made. + * + * @param query A query Handle. + * @return True if there was another result set, false otherwise. + * @error Invalid query Handle. + */ +native bool:SQL_FetchMoreResults(Handle:query); + +/** + * Returns whether or not a result set exists. This will + * return true even if 0 results were returned, but false + * on queries like UPDATE, INSERT, or DELETE. + * + * @param query A query (or statement) Handle. + * @return True if there is a result set, false otherwise. + * @error Invalid query Handle. + */ +native bool:SQL_HasResultSet(Handle:query); + +/** + * Retrieves the number of rows in the last result set. + * + * @param query A query (or statement) Handle. + * @return Number of rows in the current result set. + * @error Invalid query Handle. + */ +native SQL_GetRowCount(Handle:query); + +/** + * Retrieves the number of fields in the last result set. + * + * @param query A query (or statement) Handle. + * @return Number of fields in the current result set. + * @error Invalid query Handle. + */ +native SQL_GetFieldCount(Handle:query); + +/** + * Retrieves the name of a field by index. + * + * @param query A query (or statement) Handle. + * @param field Field number (starting from 0). + * @param name Name buffer. + * @param maxlength Maximum length of the name buffer. + * @noreturn + * @error Invalid query Handle, invalid field index, or + * no current result set. + */ +native SQL_FieldNumToName(Handle:query, field, String:name[], maxlength); + +/** + * Retrieves a field index by name. + * + * @param query A query (or statement) Handle. + * @param name Name of the field (case sensitive). + * @param field Variable to store field index in. + * @return True if found, false if not found. + * @error Invalid query Handle or no current result set. + */ +native bool:SQL_FieldNameToNum(Handle:query, const String:name[], &field); + +/** + * Fetches a row from the current result set. This must be + * successfully called before any results are fetched. + * + * If this function fails, SQL_MoreResults() can be used to + * tell if there was an error or the result set is finished. + * + * @param query A query (or statement) Handle. + * @return True if a row was fetched, false otherwise. + * @error Invalid query Handle. + */ +native bool:SQL_FetchRow(Handle:query); + +/** + * Returns if there are more rows. + * + * @param query A query (or statement) Handle. + * @return True if there are more rows, false otherwise. + * @error Invalid query Handle. + */ +native bool:SQL_MoreRows(Handle:query); + +/** + * Rewinds a result set back to the first result. + * + * @param query A query (or statement) Handle. + * @return True on success, false otherwise. + * @error Invalid query Handle or no current result set. + */ +native bool:SQL_Rewind(Handle:query); + +/** + * Fetches a string from a field in the current row of a result set. + * If the result is NULL, an empty string will be returned. A NULL + * check can be done with the result parameter, or SQL_IsFieldNull(). + * + * @param query A query (or statement) Handle. + * @param field The field index (starting from 0). + * @param buffer String buffer. + * @param maxlength Maximum size of the string buffer. + * @param result Optional variable to store the status of the return value. + * @return Number of bytes written. + * @error Invalid query Handle or field index, invalid + * type conversion requested from the database, + * or no current result set. + */ +native SQL_FetchString(Handle:query, field, String:buffer[], maxlength, &DBResult:result=DBVal_Error); + +/** + * Fetches a float from a field in the current row of a result set. + * If the result is NULL, a value of 0.0 will be returned. A NULL + * check can be done with the result parameter, or SQL_IsFieldNull(). + * + * @param query A query (or statement) Handle. + * @param field The field index (starting from 0). + * @param result Optional variable to store the status of the return value. + * @return A float value. + * @error Invalid query Handle or field index, invalid + * type conversion requested from the database, + * or no current result set. + */ +native Float:SQL_FetchFloat(Handle:query, field, &DBResult:result=DBVal_Error); + +/** + * Fetches an integer from a field in the current row of a result set. + * If the result is NULL, a value of 0 will be returned. A NULL + * check can be done with the result parameter, or SQL_IsFieldNull(). + * + * @param query A query (or statement) Handle. + * @param field The field index (starting from 0). + * @param result Optional variable to store the status of the return value. + * @return An integer value. + * @error Invalid query Handle or field index, invalid + * type conversion requested from the database, + * or no current result set. + */ +native SQL_FetchInt(Handle:query, field, &DBResult:result=DBVal_Error); + +/** + * Returns whether a field's data in the current row of a result set is + * NULL or not. NULL is an SQL type which means "no data." + * + * @param query A query (or statement) Handle. + * @param field The field index (starting from 0). + * @return True if data is NULL, false otherwise. + * @error Invalid query Handle or field index, or no + * current result set. + */ +native bool:SQL_IsFieldNull(Handle:query, field); + +/** + * Returns the length of a field's data in the current row of a result + * set. This only needs to be called for strings to determine how many + * bytes to use. Note that the return value does not include the null + * terminator. + * + * @param query A query (or statement) Handle. + * @param field The field index (starting from 0). + * @return Number of bytes for the field's data size. + * @error Invalid query Handle or field index or no + * current result set. + */ +native SQL_FetchSize(Handle:query, field); + +/** + * Binds a parameter in a prepared statement to a given integer value. + * + * @param statement A statement (prepared query) Handle. + * @param param The parameter index (starting from 0). + * @param number The number to bind. + * @param signed True to bind the number as signed, false to + * bind it as unsigned. + * @noreturn + * @error Invalid statement Handle or parameter index, or + * SQL error. + */ +native SQL_BindParamInt(Handle:statement, param, number, bool:signed=true); + +/** + * Binds a parameter in a prepared statement to a given float value. + * + * @param statement A statement (prepared query) Handle. + * @param param The parameter index (starting from 0). + * @param float The float number to bind. + * @noreturn + * @error Invalid statement Handle or parameter index, or + * SQL error. + */ +native SQL_BindParamFloat(Handle:statement, param, Float:value); + +/** + * Binds a parameter in a prepared statement to a given string value. + * + * @param statement A statement (prepared query) Handle. + * @param param The parameter index (starting from 0). + * @param value The string to bind. + * @param copy Whether or not SourceMod should copy the value + * locally if necessary. If the string contents + * won't change before calling SQL_Execute(), this + * can be set to false for optimization. + * @noreturn + * @error Invalid statement Handle or parameter index, or + * SQL error. + */ +native SQL_BindParamString(Handle:statement, param, const String:value[], bool:copy); + +/** + * Executes a prepared statement. All parameters must be bound beforehand. + * + * @param statement A statement (prepared query) Handle. + * @return True on success, false on failure. + * @error Invalid statement Handle. + */ +native bool:SQL_Execute(Handle:statement); + +/** + * Locks a database so threading operations will not interrupt. + * + * If you are using a database Handle for both threading and non-threading, + * this MUST be called before doing any set of non-threading DB operations. + * Otherwise you risk corrupting the database driver's memory or network + * connection. + * + * Leaving a lock on a database and then executing a threaded query results + * in a dead lock! Make sure to call SQL_UnlockDatabase()! + * + * If the lock cannot be acquired, the main thread will pause until the + * threaded operation has concluded. + * + * @param database A database Handle. + * @noreturn + * @error Invalid database Handle. + */ +native SQL_LockDatabase(Handle:database); + +/** + * Unlocks a database so threading operations may continue. + * + * @param database A database Handle. + * @noreturn + * @error Invalid database Handle. + */ +native SQL_UnlockDatabase(Handle:database); + +/** + * General callback for threaded SQL stuff. + * + * @param db Parent object of the Handle (or INVALID_HANDLE if none). + * @param hndl Handle to the child object (or INVALID_HANDLE if none). + * @param error Error string if there was an error. The error could be + * empty even if an error condition exists, so it is important + * to check the actual Handle value instead. + * @param data Data passed in via the original threaded invocation. + * @param + */ +functag SQLTCallback public(Handle:owner, Handle:hndl, const String:error[], any:data); + +/** + * Tells whether two database handles both point to the same database + * connection. + * + * @param hndl1 First database Handle. + * @param hndl2 Second database Handle. + * @return True if the Handles point to the same + * connection, false otherwise. + * @error Invalid Handle. + */ +native bool:SQL_IsSameConnection(Handle:hndl1, Handle:hndl2); + +/** + * Connects to a database via a thread. This can be used instead of + * SQL_Connect() if you wish for non-blocking functionality. + * + * It is not necessary to use this to use threaded queries. However, if you + * don't (or you mix threaded/non-threaded queries), you should see + * SQL_LockDatabase(). + * + * @param callback Callback; new Handle will be in hndl, owner is the driver. + * If no driver was found, the owner is INVALID_HANDLE. + * @param name Database name. + * @noreturn + */ +native SQL_TConnect(SQLTCallback:callback, const String:name[]="default", any:data=0); + +/** + * Executes a simple query via a thread. The query Handle is passed through + * the callback. + * + * The database Handle returned through the callback is always a new Handle, + * and if necessary, SQL_IsSameConnection() should be used to test against + * other conenctions. + * + * The query Handle returned through the callback is temporary and destroyed + * at the end of the callback. If you need to hold onto it, use CloneHandle(). + * + * @param database A database Handle. + * @param callback Callback; database is in "owner" and the query Handle + * is passed in "hndl". + * @param query Query string. + * @param data Extra data value to pass to the callback. + * @param prio Priority queue to use. + * @noreturn + * @error Invalid database Handle. + */ +native SQL_TQuery(Handle:database, SQLTCallback:callback, const String:query[], any:data=0, DBPriority:prio=DBPrio_Normal); diff --git a/src/include/entity.inc b/src/include/entity.inc new file mode 100644 index 0000000..26eaa96 --- /dev/null +++ b/src/include/entity.inc @@ -0,0 +1,677 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: entity.inc 1890 2008-02-22 16:51:15Z dvander $ + */ + +#if defined _entity_included + #endinput +#endif +#define _entity_included + +/** + * Property types for entities. + */ +enum PropType +{ + Prop_Send = 0, /**< This property is networked. */ + Prop_Data = 1, /**< This property is for save game data fields. */ +}; + +/** + * @section For more information on these, see the HL2SDK (public/edict.h) + */ +#define FL_EDICT_CHANGED (1<<0) /**< Game DLL sets this when the entity state changes + Mutually exclusive with FL_EDICT_PARTIAL_CHANGE. */ +#define FL_EDICT_FREE (1<<1) /**< this edict if free for reuse */ +#define FL_EDICT_FULL (1<<2) /**< this is a full server entity */ +#define FL_EDICT_FULLCHECK (0<<0) /**< call ShouldTransmit() each time, this is a fake flag */ +#define FL_EDICT_ALWAYS (1<<3) /**< always transmit this entity */ +#define FL_EDICT_DONTSEND (1<<4) /**< don't transmit this entity */ +#define FL_EDICT_PVSCHECK (1<<5) /**< always transmit entity, but cull against PVS */ +#define FL_EDICT_PENDING_DORMANT_CHECK (1<<6) +#define FL_EDICT_DIRTY_PVS_INFORMATION (1<<7) +#define FL_FULL_EDICT_CHANGED (1<<8) + +enum PropFieldType +{ + PropField_Unsupported, /**< The type is unsupported. */ + PropField_Integer, /**< Valid for SendProp and Data fields */ + PropField_Float, /**< Valid for SendProp and Data fields */ + PropField_Entity, /**< Valid for Data fields only (SendProp shows as int) */ + PropField_Vector, /**< Valid for SendProp and Data fields */ + PropField_String, /**< Valid for SendProp and Data fields */ + PropField_String_T, /**< Valid for Data fields. Read only. + Note that the size of a string_t is dynamic, and + thus FindDataMapOffs() will return the constant size + of the string_t container (which is 32 bits right now). + */ +}; + +/** + * @endsection + */ + +/** + * Returns the maximum number of entities. + * + * @return Maximum number of entities. + */ +native GetMaxEntities(); + +/** + * Returns the number of entities in the server. + * + * @return Number of entities in the server. + */ +native GetEntityCount(); + +/** + * Returns whether or not an entity is valid. Returns false + * if there is no matching CBaseEntity for this edict index. + * + * @param edict Index of the entity/edict. + * @return True if valid, false otherwise. + */ +native bool:IsValidEntity(edict); + +/** + * Returns whether or not an edict index is valid. + * + * @param edict Index of the edict. + * @return True if valid, false otherwise. + */ +native bool:IsValidEdict(edict); + +/** + * Returns whether or not an entity is a valid networkable edict. + * + * @param edict Index of the edict. + * @return True if networkable, false if invalid or not networkable. + */ +native bool:IsEntNetworkable(edict); + +/** + * Creates a new edict (the basis of a networkable entity) + * + * @return Index of the edict, 0 on failure. + */ +native CreateEdict(); + +/** + * Removes an edict from the world. + * + * @param edict Index of the edict. + * @noreturn + * @error Invalid edict index. + */ +native RemoveEdict(edict); + +/** + * Returns the flags on an edict. These are not the same as entity flags. + * + * @param edict Index of the entity. + * @return Edict flags. + * @error Invalid edict index. + */ +native GetEdictFlags(edict); + +/** + * Sets the flags on an edict. These are not the same as entity flags. + * + * @param edict Index of the entity. + * @param flags Flags to set. + * @noreturn + * @error Invalid edict index. + */ +native SetEdictFlags(edict, flags); + +/** + * Retrieves an edict classname. + * + * @param edict Index of the entity. + * @param clsname Buffer to store the classname. + * @param maxlength Maximum length of the buffer. + * @return True on success, false if there is no classname set. + */ +native bool:GetEdictClassname(edict, String:clsname[], maxlength); + +/** + * Retrieves an entity's networkable serverclass name. + * This is not the same as the classname and is used for networkable state changes. + * + * @param edict Index of the entity. + * @param clsname Buffer to store the serverclass name. + * @param maxlength Maximum lnegth of the buffer. + * @return True on success, false if the edict is not networkable. + * @error Invalid edict index. + */ +native bool:GetEntityNetClass(edict, String:clsname[], maxlength); + +/** + * @section Entity offset functions + * + * Offsets should be specified in byte distance from the CBaseEntity + * structure, not short (double byte) or integer (four byte) multiples. + * It is somewhat common practice to use offsets aligned to their final + * type, and thus make sure you are not falling to this error in SourceMod. + * For example, if your "integer-aligned" offset was 119, your byte-aligned + * offset is 119*4, or 476. + + * Specifying incorrect offsets or the incorrect data type for an offset + * can have fatal consequences. If you are hardcoding offsets, and the + * layout of CBaseEntity does not match, you can easily crash the server. + * + * The reasonable bounds for offsets is greater than or equal to 0 and + * below 32768. Offsets out of these bounds will throw an error. However, + * this does not represent any real range, it is simply a sanity check for + * illegal values. Any range outside of the CBaseEntity structure's private + * size will cause undefined behaviour or even crash. + */ + +/** + * Marks an entity as state changed. This can be useful if you set an offset + * and wish for it to be immediately changed over the network. By default this + * is not done for offset setting functions. + * + * @param edict Index to the edict. + * @param offset Offset to mark as changed. If 0, + * the entire edict is marked as changed. + * @noreturn + * @error Invalid entity or offset out of bounds. + */ +native ChangeEdictState(edict, offset = 0); + +/** + * Peeks into an entity's object data and retrieves the integer value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param size Number of bytes to read (valid values are 1, 2, or 4). + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntData(entity, offset, size=4); + +/** + * Peeks into an entity's object data and sets the integer value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param size Number of bytes to write (valid values are 1, 2, or 4). + * @param changeState If true, change will be sent over the network. + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + * @noreturn + */ +native SetEntData(entity, offset, any:value, size=4, bool:changeState=false); + +/** + * Peeks into an entity's object data and retrieves the float value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + */ +native Float:GetEntDataFloat(entity, offset); + +/** + * Peeks into an entity's object data and sets the integer value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param changeState If true, change will be sent over the network. + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + * @noreturn + */ +native SetEntDataFloat(entity, offset, Float:value, bool:changeState=false); + +/** + * This function is deprecated. Use GetEntDataEnt2 instead, for + * reasons explained in the notes. + * + * Note: This function returns 0 on failure, which may be misleading, + * as the number 0 is also used for the world entity index. + * + * Note: This function makes no attempt to validate the returned + * entity, and in fact, it could be garbage or completely unexpected. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Entity index at the given location, or 0 if none. + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntDataEnt(entity, offset); + +/** + * This function is deprecated. Use GetEntDataEnt2 instead, for + * reasons explained in the notes. + * + * Note: This function uses 0 as an indicator to unset data, but + * 0 is also the world entity index. Thus, the a property cannot + * be set to the world entity using this native. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param other Entity index to set, or 0 to clear. + * @param changeState If true, change will be sent over the network. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntDataEnt(entity, offset, other, bool:changeState=false); + +/** + * Peeks into an entity's object data and retrieves the entity index + * at the given offset. + * + * Note: This will only work on offsets that are stored as "entity + * handles" (which usually looks like m_h* in properties). These + * are not SourceMod Handles, but internal Source structures. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Entity index at the given location. If there is no entity, + * or the stored entity is invalid, then -1 is returned. + * @error Invalid input entity, or offset out of reasonable bounds. + */ +native GetEntDataEnt2(entity, offset); + +/** + * Peeks into an entity's object data and sets the entity index at the + * given offset. + * + * Note: This will only work on offsets that are stored as "entity + * handles" (which usually looks like m_h* in properties). These + * are not SourceMod Handles, but internal Source structures. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param other Entity index to set, or -1 to clear. + * @param changeState If true, change will be sent over the network. + * @noreturn + * @error Invalid input entity, or offset out of reasonable bounds. + */ +native SetEntDataEnt2(entity, offset, other, bool:changeState=false); + +/** + * Peeks into an entity's object data and retrieves the vector at the + * given offset. + * @note Both a Vector and a QAngle are three floats. This is a + * convenience function and will work with both types. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param vec Vector buffer to store data in. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntDataVector(entity, offset, Float:vec[3]); + +/** + * Peeks into an entity's object data and sets the vector at the given + * offset. + * @note Both a Vector and a QAngle are three floats. This is a + * convenience function and will work with both types. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param vec Vector to set. + * @param changeState If true, change will be sent over the network. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntDataVector(entity, offset, const Float:vec[3], bool:changeState=false); + +/** + * Peeks into an entity's object data and retrieves the string at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param buffer Destination string buffer. + * @param maxlen Maximum length of output string buffer. + * @return Number of non-null bytes written. + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntDataString(entity, offset, String:buffer[], maxlen); + +/** + * Peeks into an entity's object data and sets the string at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param buffer String to set. + * @param maxlen Maximum length of bytes to write. + * @param changeState If true, change will be sent over the network. + * @return Number of non-null bytes written. + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntDataString(entity, offset, const String:buffer[], maxlen, bool:changeState=false); + +/** + * @endsection + */ + +/** + * Given a ServerClass name, finds a networkable send property offset. + * This information is cached for future calls. + * + * Note, this function may return offsets that do not work! + * If a property is nested beneath a parent object, the resulting offset + * will be invalid for direct use with data functions. Therefore, you + * should use FindSendPropInfo() instead. An example of such a property is + * CTFPlayer::DT_LocalPlayer.m_nDisguiseClass on Team Fortress. + * + * @param cls Classname. + * @param prop Property name. + * @return An offset, or -1 on failure. + */ +native FindSendPropOffs(const String:cls[], const String:prop[]); + +/** + * Given a ServerClass name, finds a networkable send property offset. + * This information is cached for future calls. + * + * Note: This function will correctly compute nested offsets, unlike + * FindSendPropOffs(). YOU SHOULD NOT use this function to self-compute + * nested offsets. For example, it is okay to add indexes for arrays, + * but not to add DT_LocalPlayer to m_nDisguiseClass. + * + * @param cls Classname. + * @param prop Property name. + * @param type Optional parameter to store the type. + * @param num_bits Optional parameter to store the number of bits the field + * uses, if applicable (otherwise 0 is stored). The number + * of bits varies for integers and floats, and is always 0 + * for strings. + * @param local_offset Optional parameter to store the local offset, as + * FindSendPropOffs() would return. + * @return On success, returns an absolutely computed offset. + * If no offset is available, 0 is returned. + * If the property is not found, -1 is returned. + */ +native FindSendPropInfo(const String:cls[], + const String:prop[], + &PropFieldType:type=PropFieldType:0, + &num_bits=0, + &local_offset=0); + +/** + * Given an entity, finds a datamap property offset. + * This information is cached for future calls. + * + * @param entity Entity index. + * @param prop Property name. + * @param type Optional parameter to store the type. + * @param num_bits Optional parameter to store the number of bits the field + * uses. The bit count will either be 1 (for boolean) or + * divisible by 8 (including 0 if unknown). + * @return An offset, or -1 on failure. + */ +native FindDataMapOffs(entity, + const String:prop[], + &PropFieldType:type=PropFieldType:0, + &num_bits=0); + +/** + * Wrapper function for finding a send property for a particular entity. + * + * @param ent Entity index. + * @param prop Property name. + * @param actual Defaults to false for backwards compatibility. + * If true, the newer FindSendPropInfo() function + * is used instead. + * @return An offset, or -1 on failure. + */ +stock GetEntSendPropOffs(ent, const String:prop[], bool:actual=false) +{ + decl String:cls[64]; + + if (!GetEntityNetClass(ent, cls, sizeof(cls))) + { + return -1; + } + + if (actual) + { + return FindSendPropInfo(cls, prop); + } + else + { + return FindSendPropOffs(cls, prop); + } +} + +/** + * Retrieves an integer value from an entity's property. + * + * This function is considered safer and more robust over GetEntData, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @param size Number of bytes to write (valid values are 1, 2, or 4). + * This value is auto-detected, and the size parameter is + * only used as a fallback in case detection fails. + * @return Value at the given property offset. + * @error Invalid entity or property not found. + */ +native GetEntProp(entity, PropType:type, const String:prop[], size=4); + +/** + * Sets an integer value in an entity's property. + * + * This function is considered safer and more robust over SetEntData, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @param size Number of bytes to write (valid values are 1, 2, or 4). + * This value is auto-detected, and the size parameter is + * only used as a fallback in case detection fails. + * @error Invalid entity or offset out of reasonable bounds. + * @noreturn + */ +native SetEntProp(entity, PropType:type, const String:prop[], any:value, size=4); + +/** + * Retrieves a float value from an entity's property. + * + * This function is considered safer and more robust over GetEntDataFloat, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @return Value at the given property offset. + * @error Invalid entity or offset out of reasonable bounds. + */ +native Float:GetEntPropFloat(entity, PropType:type, const String:prop[]); + +/** + * Sets a float value in an entity's property. + * + * This function is considered safer and more robust over SetEntDataFloat, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @param value Value to set. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value); + +/** + * Retrieves an entity index from an entity's property. + * + * This function is considered safer and more robust over GetEntDataEnt*, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @return Entity index at the given property. + * If there is no entity, or the entity is not valid, + * then -1 is returned. + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntPropEnt(entity, PropType:type, const String:prop[]); + +/** + * Sets an entity index in an entity's property. + * + * This function is considered safer and more robust over SetEntDataEnt*, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @param other Entity index to set, or -1 to unset. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntPropEnt(entity, PropType:type, const String:prop[], other); + +/** + * Retrieves a vector of floats from an entity, given a named network property. + * + * This function is considered safer and more robust over GetEntDataVector, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @param vec Vector buffer to store data in. + * @noreturn + * @error Invalid entity, property not found, or property not + * actually a vector data type. + */ +native GetEntPropVector(entity, PropType:type, const String:prop[], Float:vec[3]); + +/** + * Sets a vector of floats in an entity, given a named network property. + * + * This function is considered safer and more robust over SetEntDataVector, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. + * @param type Property type. + * @param prop Property name. + * @param vec Vector to set. + * @noreturn + * @error Invalid entity, property not found, or property not + * actually a vector data type. + */ +native SetEntPropVector(entity, PropType:type, const String:prop[], const Float:vec[3]); + +/** + * Gets a network property as a string. + * + * @param entity Edict index. + * @param type Property type. + * @param prop Property to use. + * @param buffer Destination string buffer. + * @param maxlen Maximum length of output string buffer. + * @return Number of non-null bytes written. + * @error Invalid entity, offset out of reasonable bounds, or property is not a valid string. + */ +native GetEntPropString(entity, PropType:type, const String:prop[], String:buffer[], maxlen); + +/** + * Sets a network property as a string. + * + * This cannot set property fields of type PropField_String_T (such as "m_target"). + * To set such fields, you should use DispatchKeyValue() from SDKTools. + * + * @param entity Edict index. + * @param type Property type. + * @param prop Property to use. + * @param buffer String to set. + * @return Number of non-null bytes written. + * @error Invalid entity, offset out of reasonable bounds, or property is not a valid string. + */ +native SetEntPropString(entity, PropType:type, const String:prop[], const String:buffer[]); + +/** + * Copies an array of cells from an entity at a given offset. + * + * @param entity Entity index. + * @param offset Offset to use. + * @param array Array to read into. + * @param arraySize Number of values to read. + * @param dataSize Size of each value in bytes (1, 2, or 4). + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +stock GetEntDataArray(entity, offset, array[], arraySize, dataSize=4) +{ + for (new i=0; i. + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: entity_prop_stocks.inc 1943 2008-03-16 23:16:34Z dvander $ + */ + +#if defined _entity_prop_stocks_included + #endinput +#endif +#define _entity_prop_stocks_included + +enum MoveType +{ + MOVETYPE_NONE = 0, /**< never moves */ + MOVETYPE_ISOMETRIC, /**< For players */ + MOVETYPE_WALK, /**< Player only - moving on the ground */ + MOVETYPE_STEP, /**< gravity, special edge handling -- monsters use this */ + MOVETYPE_FLY, /**< No gravity, but still collides with stuff */ + MOVETYPE_FLYGRAVITY, /**< flies through the air + is affected by gravity */ + MOVETYPE_VPHYSICS, /**< uses VPHYSICS for simulation */ + MOVETYPE_PUSH, /**< no clip to world, push and crush */ + MOVETYPE_NOCLIP, /**< No gravity, no collisions, still do velocity/avelocity */ + MOVETYPE_LADDER, /**< Used by players only when going onto a ladder */ + MOVETYPE_OBSERVER, /**< Observer movement, depends on player's observer mode */ + MOVETYPE_CUSTOM, /**< Allows the entity to describe its own physics */ +}; + +enum RenderMode +{ + RENDER_NORMAL, /**< src */ + RENDER_TRANSCOLOR, /**< c*a+dest*(1-a) */ + RENDER_TRANSTEXTURE, /**< src*a+dest*(1-a) */ + RENDER_GLOW, /**< src*a+dest -- No Z buffer checks -- Fixed size in screen space */ + RENDER_TRANSALPHA, /**< src*srca+dest*(1-srca) */ + RENDER_TRANSADD, /**< src*a+dest */ + RENDER_ENVIRONMENTAL, /**< not drawn, used for environmental effects */ + RENDER_TRANSADDFRAMEBLEND, /**< use a fractional frame value to blend between animation frames */ + RENDER_TRANSALPHAADD, /**< src + dest*(1-a) */ + RENDER_WORLDGLOW, /**< Same as kRenderGlow but not fixed size in screen space */ + RENDER_NONE, /**< Don't render. */ +}; + +enum RenderFx +{ + RENDERFX_NONE = 0, + RENDERFX_PULSE_SLOW, + RENDERFX_PULSE_FAST, + RENDERFX_PULSE_SLOW_WIDE, + RENDERFX_PULSE_FAST_WIDE, + RENDERFX_FADE_SLOW, + RENDERFX_FADE_FAST, + RENDERFX_SOLID_SLOW, + RENDERFX_SOLID_FAST, + RENDERFX_STROBE_SLOW, + RENDERFX_STROBE_FAST, + RENDERFX_STROBE_FASTER, + RENDERFX_FLICKER_SLOW, + RENDERFX_FLICKER_FAST, + RENDERFX_NO_DISSIPATION, + RENDERFX_DISTORT, /**< Distort/scale/translate flicker */ + RENDERFX_HOLOGRAM, /**< kRenderFxDistort + distance fade */ + RENDERFX_EXPLODE, /**< Scale up really big! */ + RENDERFX_GLOWSHELL, /**< Glowing Shell */ + RENDERFX_CLAMP_MIN_SCALE, /**< Keep this sprite from getting very small (SPRITES only!) */ + RENDERFX_ENV_RAIN, /**< for environmental rendermode, make rain */ + RENDERFX_ENV_SNOW, /**< " " " , make snow */ + RENDERFX_SPOTLIGHT, /**< TEST CODE for experimental spotlight */ + RENDERFX_RAGDOLL, /**< HACKHACK: TEST CODE for signalling death of a ragdoll character */ + RENDERFX_PULSE_FAST_WIDER, + RENDERFX_MAX +}; + +// These defines are for client button presses. +#define IN_ATTACK (1 << 0) +#define IN_JUMP (1 << 1) +#define IN_DUCK (1 << 2) +#define IN_FORWARD (1 << 3) +#define IN_BACK (1 << 4) +#define IN_USE (1 << 5) +#define IN_CANCEL (1 << 6) +#define IN_LEFT (1 << 7) +#define IN_RIGHT (1 << 8) +#define IN_MOVELEFT (1 << 9) +#define IN_MOVERIGHT (1 << 10) +#define IN_ATTACK2 (1 << 11) +#define IN_RUN (1 << 12) +#define IN_RELOAD (1 << 13) +#define IN_ALT1 (1 << 14) +#define IN_ALT2 (1 << 15) +#define IN_SCORE (1 << 16) // Used by client.dll for when scoreboard is held down +#define IN_SPEED (1 << 17) // Player is holding the speed key +#define IN_WALK (1 << 18) // Player holding walk key +#define IN_ZOOM (1 << 19) // Zoom key for HUD zoom +#define IN_WEAPON1 (1 << 20) // weapon defines these bits +#define IN_WEAPON2 (1 << 21) // weapon defines these bits +#define IN_BULLRUSH (1 << 22) +#define IN_GRENADE1 (1 << 23) // grenade 1 +#define IN_GRENADE2 (1 << 24) // grenade 2 + +// CBaseEntity::m_fFlags +// PLAYER SPECIFIC FLAGS FIRST BECAUSE WE USE ONLY A FEW BITS OF NETWORK PRECISION +#define FL_ONGROUND (1 << 0) // At rest / on the ground +#define FL_DUCKING (1 << 1) // Player flag -- Player is fully crouched +#define FL_WATERJUMP (1 << 2) // player jumping out of water +#define FL_ONTRAIN (1 << 3) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. +#define FL_INRAIN (1 << 4) // Indicates the entity is standing in rain +#define FL_FROZEN (1 << 5) // Player is frozen for 3rd person camera +#define FL_ATCONTROLS (1 << 6) // Player can't move, but keeps key inputs for controlling another entity +#define FL_CLIENT (1 << 7) // Is a player +#define FL_FAKECLIENT (1 << 8) // Fake client, simulated server side; don't send network messages to them +// NOTE if you move things up, make sure to change this value +#define PLAYER_FLAG_BITS 9 +// NON-PLAYER SPECIFIC (i.e., not used by GameMovement or the client .dll ) -- Can still be applied to players, though +#define FL_INWATER (1 << 9) // In water +#define FL_FLY (1 << 10) // Changes the SV_Movestep() behavior to not need to be on ground +#define FL_SWIM (1 << 11) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) +#define FL_CONVEYOR (1 << 12) +#define FL_NPC (1 << 13) +#define FL_GODMODE (1 << 14) +#define FL_NOTARGET (1 << 15) +#define FL_AIMTARGET (1 << 16) // set if the crosshair needs to aim onto the entity +#define FL_PARTIALGROUND (1 << 17) // not all corners are valid +#define FL_STATICPROP (1 << 18) // Eetsa static prop! +#define FL_GRAPHED (1 << 19) // worldgraph has this ent listed as something that blocks a connection +#define FL_GRENADE (1 << 20) +#define FL_STEPMOVEMENT (1 << 21) // Changes the SV_Movestep() behavior to not do any processing +#define FL_DONTTOUCH (1 << 22) // Doesn't generate touch functions, generates Untouch() for anything it was touching when this flag was set +#define FL_BASEVELOCITY (1 << 23) // Base velocity has been applied this frame (used to convert base velocity into momentum) +#define FL_WORLDBRUSH (1 << 24) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) +#define FL_OBJECT (1 << 25) // Terrible name. This is an object that NPCs should see. Missiles, for example. +#define FL_KILLME (1 << 26) // This entity is marked for death -- will be freed by game DLL +#define FL_ONFIRE (1 << 27) // You know... +#define FL_DISSOLVING (1 << 28) // We're dissolving! +#define FL_TRANSRAGDOLL (1 << 29) // In the process of turning into a client side ragdoll. +#define FL_UNBLOCKABLE_BY_PLAYER (1 << 30) // pusher that can't be blocked by the player +// END m_fFlags #defines + +/** + * Get an entity's flags. + * + * @param entity Entity index. + * @return Entity's flags, see m_fFlag defines above + * @error Invalid entity index, or lack of mod compliance. + */ +stock GetEntityFlags(entity) +{ + return GetEntProp(entity, Prop_Data, "m_fFlags"); +} + +/** + * Gets an entity's movetype. + * + * @param entity Entity index. + * @return Movetype, see enum above. + * @error Invalid entity index, or lack of mod compliance. + */ +stock MoveType:GetEntityMoveType(entity) +{ + new offset = GetEntSendPropOffs(entity, "movetype"); + return MoveType:GetEntData(entity, offset, 1); +} + +/** + * Sets an entity's movetype. + * + * @param entity Entity index. + * @param mt Movetype, see enum above. + * @noreturn + * @error Invalid entity index, or lack of mod compliance. + */ +stock SetEntityMoveType(entity, MoveType:mt) +{ + new offset = GetEntSendPropOffs(entity, "movetype"); + SetEntData(entity, offset, mt, 1, true); +} + +/** + * Gets an entity's render mode. + * + * @param entity Entity index. + * @return RenderMode value. + * @error Invalid entity index, or lack of mod compliance. + */ +stock RenderMode:GetEntityRenderMode(entity) +{ + return RenderMode:GetEntProp(entity, Prop_Send, "m_nRenderMode", 1); +} + +/** + * Sets an entity's render mode. + * + * @param entity Entity index. + * @param mode RenderMode value. + * @noreturn + * @error Invalid entity index, or lack of mod compliance. + */ +stock SetEntityRenderMode(entity, RenderMode:mode) +{ + SetEntProp(entity, Prop_Send, "m_nRenderMode", mode, 1); +} + +/** + * Gets an entity's render Fx. + * + * @param entity Entity index. + * @return RenderFx value. + * @error Invalid entity index, or lack of mod compliance. + */ +stock RenderFx:GetEntityRenderFx(entity) +{ + return RenderFx:GetEntProp(entity, Prop_Send, "m_nRenderFX", 1); +} + +/** + * Sets an entity's render Fx. + * + * @param entity Entity index. + * @param fx RenderFx value. + * @noreturn + * @error Invalid entity index, or lack of mod compliance. + */ +stock SetEntityRenderFx(entity, RenderFx:fx) +{ + SetEntProp(entity, Prop_Send, "m_nRenderFX", fx, 1); +} + +/** + * Sets an entity's color. + * + * @param entity Entity index + * @param r Amount of red (0-255) + * @param g Amount of green (0-255) + * @param b Amount of blue (0-255) + * @param a Amount of alpha (0-255) + * @noreturn + * @error Invalid entity index, or lack of mod compliance. + */ +stock SetEntityRenderColor(entity, r=255, g=255, b=255, a=255) +{ + new offset = GetEntSendPropOffs(entity, "m_clrRender"); + SetEntData(entity, offset, r, 1, true); + SetEntData(entity, offset + 1, g, 1, true); + SetEntData(entity, offset + 2, b, 1, true); + SetEntData(entity, offset + 3, a, 1, true); +} + +/* GuessSDKVersion */ + +/** + * Gets an entity's gravity. + * + * @param entity Entity index. + * @return Entity's m_flGravity value. + * @error Invalid entity index, or lack of mod compliance. + */ +stock Float:GetEntityGravity(entity) +{ + return GetEntPropFloat(entity, Prop_Data, "m_flGravity"); +} + +/** + * Sets an entity's gravity. + * + * @param entity Entity index. + * @param amount Gravity to set (default = 1.0, half = 0.5, double = 2.0). + * @noreturn + * @error Invalid entity index, or lack of mod compliance. + */ +stock SetEntityGravity(entity, Float:amount) +{ + SetEntPropFloat(entity, Prop_Data, "m_flGravity", amount); +} + +/** + * Sets an entity's health + * + * @param entity Entity index. + * @param amount Health amount. + * @noreturn + * @error Invalid entity index, or lack of mod compliance. + */ +stock SetEntityHealth(entity, amount) +{ + SetEntProp(entity, Prop_Send, "m_iHealth", amount); +} + +/** + * Get's a users current pressed buttons + * + * @param client Client index + * @return Bitsum of buttons + * @error Invalid client index, client not in game, + * or lack of mod compliance. + */ +stock GetClientButtons(client) +{ + return GetEntProp(client, Prop_Data, "m_nButtons"); +} diff --git a/src/include/events.inc b/src/include/events.inc new file mode 100644 index 0000000..d643c65 --- /dev/null +++ b/src/include/events.inc @@ -0,0 +1,243 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: events.inc 1857 2008-01-19 20:58:10Z dvander $ + */ + +#if defined _events_included + #endinput +#endif +#define _events_included + +/** + * Event hook modes determining how hooking should be handled + */ +enum EventHookMode +{ + EventHookMode_Pre, /**< Hook callback fired before event is fired */ + EventHookMode_Post, /**< Hook callback fired after event is fired */ + EventHookMode_PostNoCopy /**< Hook callback fired after event is fired, but event data won't be copied */ +}; + +/** + * Hook function types for events. + */ +funcenum EventHook +{ + /** + * Called when a game event is fired. + * + * @param event Handle to event. This could be INVALID_HANDLE if every plugin hooking + * this event has set the hook mode EventHookMode_PostNoCopy. + * @param name String containing the name of the event. + * @param dontBroadcast True if event was not broadcast to clients, false otherwise. + * @return Ignored for post hooks. Plugin_Handled will block event if hooked as pre. + */ + Action:public(Handle:event, const String:name[], bool:dontBroadcast), + /** + * Called when a game event is fired. + * + * @param event Handle to event. This could be INVALID_HANDLE if every plugin hooking + * this event has set the hook mode EventHookMode_PostNoCopy. + * @param name String containing the name of the event. + * @param dontBroadcast True if event was not broadcast to clients, false otherwise. + * @noreturn + */ + public(Handle:event, const String:name[], bool:dontBroadcast), +}; + +/** + * Creates a hook for when a game event is fired. + * + * @param name Name of event. + * @param callback An EventHook function pointer. + * @param mode Optional EventHookMode determining the type of hook. + * @noreturn + * @error Invalid event name or invalid callback function. + */ +native HookEvent(const String:name[], EventHook:callback, EventHookMode:mode=EventHookMode_Post); + +/** + * Creates a hook for when a game event is fired. + * + * @param name Name of event. + * @param callback An EventHook function pointer. + * @param mode Optional EventHookMode determining the type of hook. + * @return True if event exists and was hooked successfully, false otherwise. + * @error Invalid callback function. + */ +native bool:HookEventEx(const String:name[], EventHook:callback, EventHookMode:mode=EventHookMode_Post); + +/** + * Removes a hook for when a game event is fired. + * + * @param name Name of event. + * @param callback An EventHook function pointer. + * @param mode Optional EventHookMode determining the type of hook. + * @noreturn + * @error Invalid callback function or no active hook for specified event. + */ +native UnhookEvent(const String:name[], EventHook:callback, EventHookMode:mode=EventHookMode_Post); + +/** + * Creates a game event to be fired later. + * + * The Handle should not be closed via CloseHandle(). It must be closed via + * FireEvent() or CancelCreatedEvent(). + * + * @param name Name of event. + * @param force If set to true, this forces the event to be created even if it's not being hooked. + * Note that this will not force it if the event doesn't exist at all. + * @return Handle to event. INVALID_HANDLE is returned if the event doesn't exist or isn't + being hooked (unless force is true). + */ +native Handle:CreateEvent(const String:name[], bool:force=false); + +/** + * Fires a game event. + * + * This function closes the event Handle after completing. + * + * @param event Handle to the event. + * @param dontBroadcast Optional boolean that determines if event should be broadcast to clients. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native FireEvent(Handle:event, bool:dontBroadcast=false); + +/** + * Cancels a previously created game event that has not been fired. + * + * @param event Handled to the event. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native CancelCreatedEvent(Handle:event); + +/** + * Returns the boolean value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @return The boolean value of the specfied event key. + * @error Invalid or corrupt Handle. + */ +native bool:GetEventBool(Handle:event, const String:key[]); + +/** + * Sets the boolean value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @param value New boolean value. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetEventBool(Handle:event, const String:key[], bool:value); + +/** + * Returns the integer value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @return The integer value of the specfied event key. + * @error Invalid or corrupt Handle. + */ +native GetEventInt(Handle:event, const String:key[]); + +/** + * Sets the integer value of a game event's key. + * + * Integer value refers to anything that can be reduced to an integer. + * The various size specifiers, such as "byte" and "short" are still + * integers, and only refer to how much data will actually be sent + * over the network (if applicable). + * + * @param event Handle to the event. + * @param key Name of event key. + * @param value New integer value. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetEventInt(Handle:event, const String:key[], value); + +/** + * Returns the floating point value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @return The floating point value of the specfied event key. + * @error Invalid or corrupt Handle. + */ +native Float:GetEventFloat(Handle:event, const String:key[]); + +/** + * Sets the floating point value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @param value New floating point value. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetEventFloat(Handle:event, const String:key[], Float:value); + +/** + * Retrieves the string value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @param value Buffer to store the value of the specified event key. + * @param maxlength Maximum length of string buffer. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native GetEventString(Handle:event, const String:key[], String:value[], maxlength); + +/** + * Sets the string value of a game event's key. + * + * @param event Handle to the event. + * @param key Name of event key. + * @param value New string value. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SetEventString(Handle:event, const String:key[], const String:value[]); + +/** + * Retrieves the name of a game event. + * + * @param event Handle to the event. + * @param value Buffer to store the name of the event. + * @param maxlength Maximum length of string buffer. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native GetEventName(Handle:event, String:name[], maxlength); diff --git a/src/include/files.inc b/src/include/files.inc new file mode 100644 index 0000000..d462d69 --- /dev/null +++ b/src/include/files.inc @@ -0,0 +1,388 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: files.inc 1860 2008-01-22 17:04:59Z dvander $ + */ + +#if defined _files_included + #endinput +#endif +#define _files_included + +/** + * @global All paths in SourceMod natives are relative to the mod folder + * unless otherwise noted. + * + * Most functions in SourceMod (at least, ones that deal with direct + * file manipulation) will support an alternate path specification. + * + * If the path starts with the string "file://" and the PathType is + * not relative, then the "file://" portion is stripped off, and the + * rest of the path is used without any modification (except for + * correcting slashes). This can be used to override the path + * builder to supply alternate absolute paths. Examples: + * + * file://C:/Temp/file.txt + * file:///tmp/file.txt + */ + +/** + * File inode types. + */ +enum FileType +{ + FileType_Unknown = 0, /* Unknown file type (device/socket) */ + FileType_Directory = 1, /* File is a directory */ + FileType_File = 2, /* File is a file */ +}; + +/** + * File time modes. + */ +enum FileTimeMode +{ + FileTime_LastAccess = 0, /* Last access (does not work on FAT) */ + FileTime_Created = 1, /* Creation (does not work on FAT) */ + FileTime_LastChange = 2, /* Last modification */ +}; + +#define PLATFORM_MAX_PATH 256 /**< Maximum path length. */ + +#define SEEK_SET 0 /**< Seek from start. */ +#define SEEK_CUR 1 /**< Seek from current position. */ +#define SEEK_END 2 /**< Seek from end position. */ + +/** + * Path types. + */ +enum PathType +{ + Path_SM, /**< SourceMod root folder */ +}; + +/** + * Builds a path relative to the SourceMod folder. This should be used instead of + * directly referencing addons/sourcemod, in case users change the name of their + * folder layout. + * + * @param type Type of path to build as the base. + * @param buffer Buffer to store the path. + * @param maxlength Maximum length of buffer. + * @param fmt Format string. + * @param ... Format arguments. + * @return Number of bytes written to buffer (not including null terminator). + */ +native BuildPath(PathType:type, String:buffer[], maxlength, const String:fmt[], any:...); + +/** + * Opens a directory/folder for contents enumeration. + * + * @note Directories are closed with CloseHandle(). + * @note Directories Handles can be cloned. + * @note OpenDirectory() supports the "file://" notation. + * + * @param path Path to open. + * @return A Handle to the directory, INVALID_HANDLE on open error. + */ +native Handle:OpenDirectory(const String:path[]); + +/** + * Reads the current directory entry as a local filename, then moves to the next file. + * + * @note Contents of buffers are undefined when returning false. + * @note Both the '.' and '..' automatic directory entries will be retrieved for Windows and Linux. + * + * @param dir Handle to a directory. + * @param buffer String buffer to hold directory name. + * @param maxlength Maximum size of string buffer. + * @param type Optional variable to store the file type. + * @return True on success, false if there are no more files to read. + * @error Invalid or corrupt Handle. + */ +native bool:ReadDirEntry(Handle:dir, String:buffer[], maxlength, &FileType:type=FileType_Unknown); + +/** + * Opens a file. + * + * @note Files are closed with CloseHandle(). + * @note File Handles can be cloned. + * @note OpenFile() supports the "file://" notation. + * + * @param file File to open. + * @param mode Open mode. + * @return A Handle to the file, INVALID_HANDLE on open error. + */ +native Handle:OpenFile(const String:file[], const String:mode[]); + +/** + * Deletes a file. + * + * @param path Path of the file to delete. + * @return True on success, false otherwise. + */ +native bool:DeleteFile(const String:path[]); + +/** + * Reads a line from a text file. + * + * @param hndl Handle to the file. + * @param buffer String buffer to hold the line. + * @param maxlength Maximum size of string buffer. + * @return True on success, false otherwise. + */ +native bool:ReadFileLine(Handle:hndl, String:buffer[], maxlength); + +/** + * Reads binary data from a file. + * + * @param hndl Handle to the file. + * @param items Array to store each item read. + * @param num_items Number of items to read into the array. + * @param size Size of each element, in bytes, to be read. + * Valid sizes are 1, 2, or 4. + * @return Number of elements read, or -1 on error. + */ +native ReadFile(Handle:hndl, items[], num_items, size); + +/** + * Reads a UTF8 or ANSI string from a file. + * + * @param hndl Handle to the file. + * @param buffer Buffer to store the string. + * @param max_size Maximum size of the string buffer. + * @param stop If true, reading will stop once max_size-1 bytes have + * been read. If false, reading will stop once a NUL + * terminator is reached. The buffer will simply be + * terminated in either case, the difference is in how + * the far the file position is changed. + * @return Number of characters written to the buffer, or -1 + * if an error was encountered. + * @error Invalid Handle, or read_count > max_size. + */ +native ReadFileString(Handle:hndl, String:buffer[], max_size, read_count=-1); + +/** + * Writes binary data to a file. + * + * @param hndl Handle to the file. + * @param items Array of items to write. The data is read directly. + * That is, in 1 or 2-byte mode, the lower byte(s) in + * each cell are used directly, rather than performing + * any casts from a 4-byte number to a smaller number. + * @param num_items Number of items in the array. + * @param size Size of each item in the array in bytes. + * Valid sizes are 1, 2, or 4. + * @return True on success, false on error. + * @error Invalid Handle. + */ +native bool:WriteFile(Handle:hndl, const items[], num_items, size); + +/** + * Writes a binary string to a file. + * + * @param hndl Handle to th efile. + * @param buffer String to write. + * @param term True to append NUL terminator, false otherwise. + * @return True on success, false on error. + * @error Invalid Handle. + */ +native bool:WriteFileString(Handle:hndl, const String:buffer[], bool:term); + +/** + * Writes a line of text to a text file. A newline is automatically appended. + * + * @param hndl Handle to the file. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @return True on success, false otherwise. + * @error Invalid Handle. + */ +native bool:WriteFileLine(Handle:hndl, const String:format[], any:...); + +/** + * Reads a single binary cell from a file. + * + * @param hndl Handle to the file. + * @param data Variable to store the data read. + * @param size Size of the data to read in bytes. Valid + * sizes are 1, 2, or 4 bytes. + * @return Number of elements read (max 1), or -1 on error. + * @error Invalid Handle. + */ +stock ReadFileCell(Handle:hndl, &data, size) +{ + new array[1], ret; + + if ((ret = ReadFile(hndl, array, 1, size)) == 1) + { + data = array[0]; + } + + return ret; +} + +/** + * Writes a single binary cell to a file. + * + * @param hndl Handle to the file. + * @param data Cell to write to the file. + * @param size Size of the data to read in bytes. Valid + * sizes are 1, 2, or 4 bytes. If the size + * is less than 4 bytes, the data is truncated + * rather than casted. That is, only the lower + * bits will be read. + * @return True on success, false on error. + * @error Invalid Handle. + */ +stock bool:WriteFileCell(Handle:hndl, data, size) +{ + new array[1]; + + array[0] = data; + + return WriteFile(hndl, array, 1, size); +} + +/** + * Tests if the end of file has been reached. + * + * @param file Handle to the file. + * @return True if end of file has been reached, false otherwise. + * @error Invalid Handle. + */ +native bool:IsEndOfFile(Handle:file); + +/** + * Sets the file position indicator. + * + * @param file Handle to the file. + * @param position Position relative to what is specified in whence. + * @param where SEEK_ constant value of where to see from. + * @return True on success, false otherwise. + * @error Invalid Handle. + */ +native bool:FileSeek(Handle:file, position, where); + +/** + * Get current position in the file. + * + * @param file Handle to the file. + * @return Value for the file position indicator. + * @error Invalid Handle. + */ +native FilePosition(Handle:file); + +/** + * Checks if a file exists. + * + * @param path Path to the file. + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to check for the existance of files + * inside GCFs or the game cache, rather than solely files + * that are on disk. + * @return True if the file exists, false otherwise. + */ +native bool:FileExists(const String:path[], bool:use_valve_fs=false); + +/** + * Renames a file. + * + * @param newpath New path to the file. + * @param oldpath Path to the existing file. + * @return True on success, false otherwise. + */ +native bool:RenameFile(const String:newpath[], const String:oldpath[]); + +/** + * Checks if a directory exists. + * + * @param path Path to the directory. + * @return True if the directory exists, false otherwise. + */ +native bool:DirExists(const String:path[]); + +/** + * Get the file size in bytes. + * + * @param path Path to the file. + * @return File size in bytes, -1 if file not found. + */ +native FileSize(const String:path[]); + +/** + * Flushes a file's buffered output; any buffered output + * is immediately written to the file. + * + * @param file Handle to the file. + * @return True on success, false on failure. + */ +native FlushFile(Handle:file); + +/** + * Removes a directory. + * @note On most Operating Systems you cannot remove a directory which has files inside it. + * + * @param path Path to the directory. + * @return True on success, false otherwise. + */ +native bool:RemoveDir(const String:path[]); + +/** + * Returns a file timestamp as a unix timestamp. + * + * @param file File name. + * @param tmode Time mode. + * @return Time value, or -1 on failure. + */ +native GetFileTime(const String:file[], FileTimeMode:tmode); + +/** + * Same as LogToFile(), except uses an open file Handle. The file must + * be opened in text appending mode. + * + * @param hndl Handle to the file. + * @param message Message format. + * @param ... Message format parameters. + * @noreturn + * @error Invalid Handle. + */ +native LogToOpenFile(Handle:hndl, const String:message[], any:...); + +/** + * Same as LogToFileEx(), except uses an open file Handle. The file must + * be opened in text appending mode. + * + * @param hndl Handle to the file. + * @param message Message format. + * @param ... Message format parameters. + * @noreturn + * @error Invalid Handle. + */ +native LogToOpenFileEx(Handle:hndl, const String:message[], any:...); + diff --git a/src/include/float.inc b/src/include/float.inc new file mode 100644 index 0000000..1e44a7d --- /dev/null +++ b/src/include/float.inc @@ -0,0 +1,415 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: float.inc 1942 2008-03-16 23:08:56Z dvander $ + */ + +#if defined _float_included + #endinput +#endif +#define _float_included + +/** + * Converts an integer into a floating point value. + * + * @param value Integer to convert. + * @return Floating point value. + */ +native Float:float(value); + +/** + * Multiplies two floats together. + * + * @param oper1 First value. + * @param oper2 Second value. + * @return oper1*oper2. + */ +native Float:FloatMul(Float:oper1, Float:oper2); + +/** + * Divides the dividend by the divisor. + * + * @param dividend First value. + * @param divisor Second value. + * @return dividend/divisor. + */ +native Float:FloatDiv(Float:dividend, Float:divisor); + +/** + * Adds two floats together. + * + * @param oper1 First value. + * @param oper2 Second value. + * @return oper1+oper2. + */ +native Float:FloatAdd(Float:oper1, Float:oper2); + +/** + * Subtracts oper2 from oper1. + * + * @param oper1 First value. + * @param oper2 Second value. + * @return oper1-oper2. + */ +native Float:FloatSub(Float:oper1, Float:oper2); + +/** + * Returns the decimal part of a float. + * + * @param value Input value. + * @return Decimal part. + */ +native Float:FloatFraction(Float:value); + +/** + * Rounds a float to the closest integer to zero. + * + * @param value Input value to be rounded. + * @return Rounded value. + */ +native RoundToZero(Float:value); + +/** + * Rounds a float to the next highest integer value. + * + * @param value Input value to be rounded. + * @return Rounded value. + */ +native RoundToCeil(Float:value); + +/** + * Rounds a float to the next lowest integer value. + * + * @param value Input value to be rounded. + * @return Rounded value. + */ +native RoundToFloor(Float:value); + +/** + * Standard IEEE rounding. + * + * @param value Input value to be rounded. + * @return Rounded value. + */ +native RoundToNearest(Float:value); + +/** + * Compares two floats. + * + * @param fOne First value. + * @param fTwo Second value. + * @return Returns 1 if the first argument is greater than the second argument. + * Returns -1 if the first argument is smaller than the second argument. + * Returns 0 if both arguments are equal. + */ +native FloatCompare(Float:fOne, Float:fTwo); + +/** + * Returns the square root of the input value, equivalent to floatpower(value, 0.5). + * + * @param value Input value. + * @return Square root of the value. + */ +native Float:SquareRoot(Float:value); + +/** + * Returns the value raised to the power of the exponent. + * + * @param value Value to be raised. + * @param exponent Value to raise the base. + * @return value^exponent. + */ +native Float:Pow(Float:value, Float:exponent); + +/** + * Returns the value of raising the input by e. + * + * @param value Input value. + * @return exp(value). + */ +native Float:Exponential(Float:value); + +/** + * Returns the logarithm of any base specified. + * + * @param value Input value. + * @param base Logarithm base to use, default is 10. + * @return log(value)/log(base). + */ +native Float:Logarithm(Float:value, Float:base=10.0); + +/** + * Returns the sine of the argument. + * + * @param value Input value in radians. + * @return sin(value). + */ +native Float:Sine(Float:value); + +/** + * Returns the cosine of the argument. + * + * @param value Input value in radians. + * @return cos(value). + */ +native Float:Cosine(Float:value); + +/** + * Returns the tangent of the argument. + * + * @param value Input value in radians. + * @return tan(value). + */ +native Float:Tangent(Float:value); + +/** + * Returns an absolute value. + * + * @param value Input value. + * @return Absolute value of the input. + */ +native Float:FloatAbs(Float:value); + +/** + * Returns the arctangent of the input value. + * + * @param angle Input value. + * @return atan(value) in radians. + */ +native Float:ArcTangent(Float:angle); + +/** + * Returns the arccosine of the input value. + * + * @param angle Input value. + * @return acos(value) in radians. + */ +native Float:ArcCosine(Float:angle); + +/** + * Returns the arcsine of the input value. + * + * @param angle Input value. + * @return asin(value) in radians. + */ +native Float:ArcSine(Float:angle); + +/** + * Returns the arctangent2 of the input values. + * + * @param x Horizontal value. + * @param y Vertical value. + * @return atan2(value) in radians. + */ +native Float:ArcTangent2(Float:x, Float:y); + +/** + * Rounds a floating point number using the "round to nearest" algorithm. + * + * @param value Floating point value to round. + * @return The value rounded to the nearest integer. + */ +stock RoundFloat(Float:value) +{ + return RoundToNearest(value); +} + +/** + * User defined operators. + * + */ +#pragma rational Float + +native Float:operator*(Float:oper1, Float:oper2) = FloatMul; +native Float:operator/(Float:oper1, Float:oper2) = FloatDiv; +native Float:operator+(Float:oper1, Float:oper2) = FloatAdd; +native Float:operator-(Float:oper1, Float:oper2) = FloatSub; + +stock Float:operator++(Float:oper) +{ + return oper+1.0; +} + +stock Float:operator--(Float:oper) +{ + return oper-1.0; +} + +stock Float:operator-(Float:oper) +{ + return oper^Float:((-1)^((-1)/2)); /* IEEE values are sign/magnitude */ +} + +stock Float:operator*(Float:oper1, oper2) +{ + return FloatMul(oper1, float(oper2)); /* "*" is commutative */ +} + +stock Float:operator/(Float:oper1, oper2) +{ + return FloatDiv(oper1, float(oper2)); +} + +stock Float:operator/(oper1, Float:oper2) +{ + return FloatDiv(float(oper1), oper2); +} + +stock Float:operator+(Float:oper1, oper2) +{ + return FloatAdd(oper1, float(oper2)); /* "+" is commutative */ +} + +stock Float:operator-(Float:oper1, oper2) +{ + return FloatSub(oper1, float(oper2)); +} + +stock Float:operator-(oper1, Float:oper2) +{ + return FloatSub(float(oper1), oper2); +} + +stock bool:operator==(Float:oper1, Float:oper2) +{ + return FloatCompare(oper1, oper2) == 0; +} + +stock bool:operator==(Float:oper1, oper2) +{ + return FloatCompare(oper1, float(oper2)) == 0; /* "==" is commutative */ +} + +stock bool:operator!=(Float:oper1, Float:oper2) +{ + return FloatCompare(oper1, oper2) != 0; +} + +stock bool:operator!=(Float:oper1, oper2) +{ + return FloatCompare(oper1, float(oper2)) != 0; /* "==" is commutative */ +} + +stock bool:operator>(Float:oper1, Float:oper2) +{ + return FloatCompare(oper1, oper2) > 0; +} + +stock bool:operator>(Float:oper1, oper2) +{ + return FloatCompare(oper1, float(oper2)) > 0; +} + +stock bool:operator>(oper1, Float:oper2) +{ + return FloatCompare(float(oper1), oper2) > 0; +} + +stock bool:operator>=(Float:oper1, Float:oper2) +{ + return FloatCompare(oper1, oper2) >= 0; +} + +stock bool:operator>=(Float:oper1, oper2) +{ + return FloatCompare(oper1, float(oper2)) >= 0; +} + +stock bool:operator>=(oper1, Float:oper2) +{ + return FloatCompare(float(oper1), oper2) >= 0; +} + +stock bool:operator<(Float:oper1, Float:oper2) +{ + return FloatCompare(oper1, oper2) < 0; +} + +stock bool:operator<(Float:oper1, oper2) +{ + return FloatCompare(oper1, float(oper2)) < 0; +} + +stock bool:operator<(oper1, Float:oper2) +{ + return FloatCompare(float(oper1), oper2) < 0; +} + +stock bool:operator<=(Float:oper1, Float:oper2) +{ + return FloatCompare(oper1, oper2) <= 0; +} + +stock bool:operator<=(Float:oper1, oper2) +{ + return FloatCompare(oper1, float(oper2)) <= 0; +} + +stock bool:operator<=(oper1, Float:oper2) +{ + return FloatCompare(float(oper1), oper2) <= 0; +} + +stock bool:operator!(Float:oper) +{ + return (_:oper & ((-1)/2)) == 0; /* -1 = all bits to 1; /2 = remove most significant bit (sign) + works on both 32bit and 64bit systems; no constant required */ +} + +/** + * Forbidden operators. + * + */ +forward operator%(Float:oper1, Float:oper2); +forward operator%(Float:oper1, oper2); +forward operator%(oper1, Float:oper2); + +#define FLOAT_PI 3.1415926535897932384626433832795 + +/** + * Converts degrees to radians. + * + * @param angle Degrees. + * @return Radians. + */ +stock Float:DegToRad(Float:angle) +{ + return (angle*FLOAT_PI)/180; +} + +/** + * Converts degrees to radians. + * + * @param angle Radians. + * @return Degrees. + */ +stock Float:RadToDeg(Float:angle) +{ + return (angle*180)/FLOAT_PI; +} diff --git a/src/include/functions.inc b/src/include/functions.inc new file mode 100644 index 0000000..4d8048a --- /dev/null +++ b/src/include/functions.inc @@ -0,0 +1,489 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: functions.inc 2360 2008-07-06 00:01:40Z dvander $ + */ + +#define SP_PARAMFLAG_BYREF (1<<0) /**< Internal use only. */ + +/** + * Describes the various ways to pass parameters to functions or forwards. + */ +enum ParamType +{ + Param_Any = 0, /**< Any data type can be pushed */ + Param_Cell = (1<<1), /**< Only basic cells can be pushed */ + Param_Float = (2<<1), /**< Only floats can be pushed */ + Param_String = (3<<1)|SP_PARAMFLAG_BYREF, /**< Only strings can be pushed */ + Param_Array = (4<<1)|SP_PARAMFLAG_BYREF, /**< Only arrays can be pushed */ + Param_VarArgs = (5<<1), /**< Same as "..." in plugins, anything can be pushed, but it will always be byref */ + Param_CellByRef = (1<<1)|SP_PARAMFLAG_BYREF, /**< Only a cell by reference can be pushed */ + Param_FloatByRef = (2<<1)|SP_PARAMFLAG_BYREF /**< Only a float by reference can be pushed */ +}; + +/** + * Defines how a forward iterates through plugin functions. + */ +enum ExecType +{ + ET_Ignore = 0, /**< Ignore all return values, return 0 */ + ET_Single = 1, /**< Only return the last exec, ignore all others */ + ET_Event = 2, /**< Acts as an event with the Actions defined in core.inc, no mid-Stops allowed, returns highest */ + ET_Hook = 3 /**< Acts as a hook with the Actions defined in core.inc, mid-Stops allowed, returns highest */ +}; + +/** + * @section Flags that are used with Call_PushArrayEx() and Call_PushStringEx() + */ + +#define SM_PARAM_COPYBACK (1<<0) /**< Copy an array/reference back after call */ + +#define SM_PARAM_STRING_UTF8 (1<<0) /**< String should be UTF-8 handled */ +#define SM_PARAM_STRING_COPY (1<<1) /**< String should be copied into the plugin */ +#define SM_PARAM_STRING_BINARY (1<<2) /**< Treat the string as a binary string */ + +/** + * @endsection + */ + +/** + * @section Error codes + */ +#define SP_ERROR_NONE 0 /**< No error occurred */ +#define SP_ERROR_FILE_FORMAT 1 /**< File format unrecognized */ +#define SP_ERROR_DECOMPRESSOR 2 /**< A decompressor was not found */ +#define SP_ERROR_HEAPLOW 3 /**< Not enough space left on the heap */ +#define SP_ERROR_PARAM 4 /**< Invalid parameter or parameter type */ +#define SP_ERROR_INVALID_ADDRESS 5 /**< A memory address was not valid */ +#define SP_ERROR_NOT_FOUND 6 /**< The object in question was not found */ +#define SP_ERROR_INDEX 7 /**< Invalid index parameter */ +#define SP_ERROR_STACKLOW 8 /**< Nnot enough space left on the stack */ +#define SP_ERROR_NOTDEBUGGING 9 /**< Debug mode was not on or debug section not found */ +#define SP_ERROR_INVALID_INSTRUCTION 10 /**< Invalid instruction was encountered */ +#define SP_ERROR_MEMACCESS 11 /**< Invalid memory access */ +#define SP_ERROR_STACKMIN 12 /**< Stack went beyond its minimum value */ +#define SP_ERROR_HEAPMIN 13 /**< Heap went beyond its minimum value */ +#define SP_ERROR_DIVIDE_BY_ZERO 14 /**< Division by zero */ +#define SP_ERROR_ARRAY_BOUNDS 15 /**< Array index is out of bounds */ +#define SP_ERROR_INSTRUCTION_PARAM 16 /**< Instruction had an invalid parameter */ +#define SP_ERROR_STACKLEAK 17 /**< A native leaked an item on the stack */ +#define SP_ERROR_HEAPLEAK 18 /**< A native leaked an item on the heap */ +#define SP_ERROR_ARRAY_TOO_BIG 19 /**< A dynamic array is too big */ +#define SP_ERROR_TRACKER_BOUNDS 20 /**< Tracker stack is out of bounds */ +#define SP_ERROR_INVALID_NATIVE 21 /**< Native was pending or invalid */ +#define SP_ERROR_PARAMS_MAX 22 /**< Maximum number of parameters reached */ +#define SP_ERROR_NATIVE 23 /**< Error originates from a native */ +#define SP_ERROR_NOT_RUNNABLE 24 /**< Function or plugin is not runnable */ +#define SP_ERROR_ABORTED 25 /**< Function call was aborted */ + +/** + * @endsection + */ + +/** + * Gets a function id from a function name. + * + * @param plugin Handle of the plugin that contains the function. + Pass INVALID_HANDLE to search in the calling plugin. + * @param name Name of the function. + * @return Function id or INVALID_FUNCTION if not found. + * @error Invalid or corrupt plugin handle. + */ +native Function:GetFunctionByName(Handle:plugin, const String:name[]); + +/** + * Creates a global forward. + * + * @note The name used to create the forward is used as its public function in all target plugins. + * @note This is ideal for global, static forwards that are never changed. + * @note Global forwards cannot be cloned. + * @note Use CloseHandle() to destroy these. + * + * @param name Name of public function to use in forward. + * @param type Execution type to be used. + * @param ... Variable number of parameter types (up to 32). + * @return Handle to new global forward. + * @error More than 32 paramater types passed. + */ +native Handle:CreateGlobalForward(const String:name[], ExecType:type, ParamType:...); + +/** + * Creates a private forward. + * + * @note No functions are automatically added. Use AddToForward() to do this. + * @note Private forwards can be cloned. + * @note Use CloseHandle() to destroy these. + * + * @param type Execution type to be used. + * @param ... Variable number of parameter types (up to 32). + * @return Handle to new private forward. + * @error More than 32 paramater types passed. + */ +native Handle:CreateForward(ExecType:type, ParamType:...); + +/** + * Returns the number of functions in a global or private forward's call list. + * + * @param fwd Handle to global or private forward. + * @return Number of functions in forward. + * @error Invalid or corrupt forward handle. + */ +native GetForwardFunctionCount(Handle:fwd); + +/** + * Adds a function to a private forward's call list. + * + * @note Cannot be used during an incompleted call. + * + * @param fwd Handle to private forward. + * @param plugin Handle of the plugin that contains the function. + * Pass INVALID_HANDLE to specify the calling plugin. + * @param func Function to add to forward. + * @return True on success, false otherwise. + * @error Invalid or corrupt private forward handle, invalid or corrupt plugin handle, or invalid function. + */ +native bool:AddToForward(Handle:fwd, Handle:plugin, Function:func); + +/** + * Removes a function from a private forward's call list. + * + * @note Only removes one instance. + * @note Functions will be removed automatically if their parent plugin is unloaded. + * + * @param fwd Handle to private forward. + * @param plugin Handle of the plugin that contains the function. + * Pass INVALID_HANDLE to specify the calling plugin. + * @param func Function to remove from forward. + * @return True on success, false otherwise. + * @error Invalid or corrupt private forward handle, invalid or corrupt plugin handle, or invalid function. + */ +native bool:RemoveFromForward(Handle:fwd, Handle:plugin, Function:func); + +/** + * Removes all instances of a plugin from a private forward's call list. + * + * @note Functions will be removed automatically if their parent plugin is unloaded. + * + * @param fwd Handle to private forward. + * @param plugin Handle of the plugin to remove instances of. + * Pass INVALID_HANDLE to specify the calling plugin. + * @return Number of functions removed from forward. + * @error Invalid or corrupt private forward handle or invalid or corrupt plugin handle. + */ +native RemoveAllFromForward(Handle:fwd, Handle:plugin); + +/** + * Starts a call to functions in a forward's call list. + * + * @note Cannot be used during an incompleted call. + * + * @param fwd Handle to global or private forward. + * @noreturn + * @error Invalid or corrupt forward handle or called before another call has completed. + */ +native Call_StartForward(Handle:fwd); + +/** + * Starts a call to a function. + * + * @note Cannot be used during an incompleted call. + * + * @param plugin Handle of the plugin that contains the function. + * Pass INVALID_HANDLE to specify the calling plugin. + * @param func Function to call. + * @noreturn + * @error Invalid or corrupt plugin handle, invalid function, or called before another call has completed. + */ +native Call_StartFunction(Handle:plugin, Function:func); + +/** + * Pushes a cell onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Cell value to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushCell(any:value); + +/** + * Pushes a cell by reference onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Cell reference to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushCellRef(&any:value); + +/** + * Pushes a float onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Floating point value to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushFloat(Float:value); + +/** + * Pushes a float by reference onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Floating point reference to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushFloatRef(&Float:value); + +/** + * Pushes an array onto the current call. + * + * @note Changes to array are not copied back to caller. Use PushArrayEx() to do this. + * @note Cannot be used before a call has been started. + * + * @param value Array to push. + * @param size Size of array. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushArray(const any:value[], size); + +/** + * Pushes an array onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Array to push. + * @param size Size of array. + * @param cpflags Whether or not changes should be copied back to the input array. + * See SP_PARAM_* constants for details. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushArrayEx(any:value[], size, cpflags); + +/** + * Pushes a string onto the current call. + * + * @note Changes to string are not copied back to caller. Use PushStringEx() to do this. + * @note Cannot be used before a call has been started. + * + * @param value String to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushString(const String:value[]); + +/** + * Pushes a string onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value String to push. + * @param length Length of string buffer. + * @param szflags Flags determining how string should be handled. + * See SP_PARAM_STRING_* constants for details. + * The default (0) is to push ASCII. + * @param cpflags Whether or not changes should be copied back to the input array. + * See SP_PARAM_* constants for details. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushStringEx(String:value[], length, szflags, cpflags); + +/** + * Completes a call to a function or forward's call list. + * + * @note Cannot be used before a call has been started. + * + * @param result Return value of function or forward's call list. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Called before a call has been started. + */ +native Call_Finish(&any:result=0); + +/** + * Cancels a call to a function or forward's call list. + * + * @note Cannot be used before a call has been started. + * + * @noreturn + * @error Called before a call has been started. + */ +native Call_Cancel(); + +/** + * Defines a native function. + * + * It is not necessary to validate the parameter count + * + * @param plugin Handle of the calling plugin. + * @param numParams Number of parameters passed to the native. + * @return Value for the native call to return. + */ +functag NativeCall public(Handle:plugin, numParams); + +/** + * Creates a dynamic native. This should only be called in AskPluginLoad(), or + * else you risk not having your native shared with other plugins. + * + * @param name Name of the dynamic native; must be unique amongst + * all other registered dynamic natives. + * @param func Function to use as the dynamic native. + * @noreturn + */ +native CreateNative(const String:name[], NativeCall:func); + +/** + * Throws an error in the calling plugin of a native, instead of your own plugin. + * + * @param error Error code to use. + * @param fmt Error message format. + * @param ... Format arguments. + */ +native ThrowNativeError(error, const String:fmt[], any:...); + +/** + * Retrieves the string length from a native parameter string. This is useful + * fetching the entire string using dynamic arrays. + * @note If this function succeeds, Get/SetNativeString will also succeed. + * + * @param param Parameter number, starting from 1. + * @param length Stores the length of the string. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeStringLength(param, &length); + +/** + * Retrieves a string from a native parameter. + * @note Output conditions are undefined on failure. + * + * @param param Parameter number, starting from 1. + * @param buffer Buffer to store the string in. + * @param maxlength Maximum length of the buffer. + * @param bytes Optionally store the number of bytes written. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeString(param, String:buffer[], maxlength, &bytes=0); + +/** + * Sets a string in a native parameter. + * @note Output conditions are undefined on failure. + * + * @param param Parameter number, starting from 1. + * @param source Source string to use. + * @param maxlength Maximum number of bytes to write. + * @param utf8 If false, string will not be written + * with UTF8 safety. + * @param bytes Optionally store the number of bytes written. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native SetNativeString(param, const String:source[], maxlength, bool:utf8=true, &bytes=0); + +/** + * Gets a cell from a native parameter. + * + * @param param Parameter number, starting from 1. + * @return Cell value at the parameter number. + * @error Invalid parameter number or calling from a non-native function. + */ +native any:GetNativeCell(param); + +/** + * Gets a cell from a native parameter, by reference. + * + * @param param Parameter number, starting from 1. + * @return Cell value at the parameter number. + * @error Invalid parameter number or calling from a non-native function. + */ +native any:GetNativeCellRef(param); + +/** + * Sets a cell from a native parameter, by reference. + * + * @param param Parameter number, starting from 1. + * @param value Cell value at the parameter number to set by reference. + * @noreturn + * @error Invalid parameter number or calling from a non-native function. + */ +native SetNativeCellRef(param, any:value); + +/** + * Gets an array from a native parameter (always by reference). + * + * @param param Parameter number, starting from 1. + * @param local Local array to copy into. + * @param size Maximum size of local array. + * @return SP_ERROR_NONE on success, anything else on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native GetNativeArray(param, any:local[], size); + +/** + * Copies a local array into a native parameter array (always by reference). + * + * @param param Parameter number, starting from 1. + * @param local Local array to copy from. + * @param size Size of the local array to copy. + * @return SP_ERROR_NONE on success, anything else on failure. + * @error Invalid parameter number or calling from a non-native function. + */ +native SetNativeArray(param, const any:local[], size); + +/** + * Formats a string using parameters from a native. + * + * @note All parameter indexes start at 1. + * @note If the input and output buffers overlap, the contents + * of the output buffer at the end is undefined. + * + * @param out_param Output parameter number to write to. If 0, out_string is used. + * @param fmt_param Format parameter number. If 0, fmt_string is used. + * @param vararg_param First variable parameter number. + * @param out_len Output string buffer maximum length (always required). + * @param written Optionally stores the number of bytes written. + * @param out_string Output string buffer to use if out_param is not used. + * @param fmt_string Format string to use if fmt_param is not used. + * @return SP_ERROR_NONE on success, anything else on failure. + */ +native FormatNativeString(out_param, + fmt_param, + vararg_param, + out_len, + &written=0, + String:out_string[]="", + const String:fmt_string[]=""); diff --git a/src/include/geoip.inc b/src/include/geoip.inc new file mode 100644 index 0000000..8048bc9 --- /dev/null +++ b/src/include/geoip.inc @@ -0,0 +1,102 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: geoip.inc 1579 2007-10-15 04:27:56Z dvander $ + */ + +#if defined _geoip_included + #endinput +#endif +#define _geoip_included + +#include + +/** + * @section IP addresses can contain ports, the ports will be stripped out. + */ + +/** + * Gets the two character country code from an IP address. (US, CA, etc) + * + * @param ip Ip to determine the country code. + * @param ccode Destination string buffer to store the code. + * @return True on success, false if no country found. + */ +native bool:GeoipCode2(const String:ip[], String:ccode[3]); + +/** + * Gets the three character country code from an IP address. (USA, CAN, etc) + * + * @param ip Ip to determine the country code. + * @param ccode Destination string buffer to store the code. + * @return True on success, false if no country found. + */ +native bool:GeoipCode3(const String:ip[], String:ccode[4]); + +/** + * Gets the full country name. (max length of output string is 45) + * + * @param ip Ip to determine the country code. + * @param ccode Destination string buffer to store the country name. + * @param len Maximum length of output string buffer. + * @return True on success, false if no country found. + */ +native bool:GeoipCountry(const String:ip[], String:name[], maxlength); + +/** + * @endsection + */ + +/** + * Do not edit below this line! + */ +public Extension:__ext_geoip = +{ + name = "GeoIP", + file = "geoip.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_geoip_SetNTVOptional() +{ + MarkNativeAsOptional("GeoipCode2"); + MarkNativeAsOptional("GeoipCode3"); + MarkNativeAsOptional("GeoipCountry"); +} +#endif diff --git a/src/include/hacks.inc b/src/include/hacks.inc new file mode 100644 index 0000000..6145213 --- /dev/null +++ b/src/include/hacks.inc @@ -0,0 +1,199 @@ +#if defined _hacks_included + #endinput +#endif +#define _hacks_included + +#include + +// Version: 1.3.0.0 + +#define Hacks_Continue -54321 + +enum Hacks_HookType +{ + HACKS_HTYPE_SPAWN=0, + HACKS_HTYPE_TRACEATTACK, + HACKS_HTYPE_ONTAKEDAMAGE, + HACKS_HTYPE_EVENT_KILLED, + HACKS_HTYPE_STARTTOUCH, + HACKS_HTYPE_TOUCH, + HACKS_HTYPE_ENDTOUCH, + HACKS_HTYPE_UPDATEONREMOVE, + HACKS_HTYPE_WEAPON_CANUSE, + HACKS_HTYPE_WEAPON_DROP, + HACKS_HTYPE_WEAPON_CANSWITCHTO, + HACKS_HTYPE_COMMITSUICIDE, + HACKS_HTYPE_IMPULSECOMMANDS, + HACKS_HTYPE_PLAYERRUNCOMMAND +}; + +enum Hacks_CallType +{ + HACKS_CTYPE_ENTITY=0, + HACKS_CTYPE_COLLISIONPROP, + HACKS_CTYPE_PHYSICSOBJECT, + HACKS_CTYPE_EVENTQUEUE, + HACKS_CTYPE_GAMERULES +}; + +enum Hacks_ReturnType +{ + HACKS_RTYPE_VOID=0, + HACKS_RTYPE_INT, + HACKS_RTYPE_FLOAT, + HACKS_RTYPE_EDICT, + HACKS_RTYPE_ENTITY +}; + +enum Hacks_Param +{ + HACKS_PARAM_NULL=0, + HACKS_PARAM_INT, + HACKS_PARAM_FLOAT, + HACKS_PARAM_STRING, + HACKS_PARAM_VECTOR, // Pointer + HACKS_PARAM_QANGLE, // Pointer + HACKS_PARAM_EDICT, + HACKS_PARAM_ENTITY, + HACKS_PARAM_PHYSICSOBJECT +}; + +/** + * Callback for a hooked function. + * + * @param entity Index of the entity that was hooked. + * @param arg1 + * @param arg2 + * @param arg3 + * @param arg4 + * @param arg5 + * @return Hacks_Continue, other = Supercede the + * function and return the value. + */ +functag Hacks_HookFunc public(entity, arg1, arg2, arg3, arg4, arg5); + +/** + * Hooks a virtual function. + * + * @param entity Index of the entity. + * @param type Type of the function, see Hacks_HookType. + * @param handler Your function that will be called on hooking. + * @param post True for post operation, false for pre operation. + * @return HookID, that's needed to unhook. + */ +native Hacks_Hook(entity, Hacks_HookType:type, Hacks_HookFunc:func, bool:post=false); + +/** + * Unhooks a virtual function. + * + * @param hookid The returned hookid from Hacks_Hook. + * @noreturn + */ +native Hacks_Unhook(hookid); + +/** + * Scans for a function in the memory. + * + * @param name Name of the function (e.g. "CBaseAnimating::Ignite"). + * @param signature Signature to scan for (Windows). 2A = wildcard + * (e.g. "56 8B 74 24 0C 83 FE FF 57 8B 7C 24 0C 74 25 8B") + * @param symbol Symbol to scan for (Linux). + * @return CallID, that's needed to call the function. + */ +native Hacks_Find(const String:name[], const String:signature[], const String:symbol[]); + +/** + * Calls a previously scanned function. + * + * @param entity Index of the entity. + * @param callid The returned callid from Hacks_Find. + * @param calltype Type to call, see Hacks_CallType. + * @param returntype Return type, see Hacks_ReturnType. + * @param[x] argtype Datatype of argument, see Hacks_Param. + * @param[y] arg Argument for function. + * @return If there was a failure in the function, it will return -1, + * otherwise see Hacks_ReturnType. + */ +native Hacks_Call(entity, callid, Hacks_CallType:calltype, Hacks_ReturnType:returntype, any:...); + +/** + * Calls a virtual function by its offset. + * + * @param entity Index of the entity. + * @param windows Windows offset. + * @param linux Linux offset. + * @param calltype Type to call, see Hacks_CallType. + * @param returntype Return type, see Hacks_ReturnType. + * @param[x] argtype Datatype of argument, see Hacks_Param. + * @param[y] arg Argument for function. + * @return If there was a failure in the function, it will return -1, + * otherwise see Hacks_ReturnType. + */ +native Hacks_Call_Offset(entity, windows, linux, Hacks_CallType:calltype, Hacks_ReturnType:returntype, any:...); + +/** + * Calls a previously scanned function, but without an entity. + * + * @param callid The returned callid from Hacks_Find. + * @param returntype Return type, see Hacks_ReturnType. + * @param[x] argtype Datatype of argument, see Hacks_Param. + * @param[y] arg Argument for function. + * @return If there was a failure in the function, it will return -1, + * otherwise see Hacks_ReturnType. + */ +native Hacks_Call_NoEntity(callid, Hacks_ReturnType:returntype, any:...); + +/** + * Adds a file to be force downloaded (cleared every map change). + * + * @param ... Each argument = One file that will be added. + * @noreturn + */ +native Hacks_AddDownload(const String:...); + +/** + * Creates a patch that you can use with Hacks_Patch/Hacks_Unpatch + * + * @param callid The returned callid from Hacks_Find. + * @param windows_bytes New Bytes (eg "C2 08 00") + * @param windows_offset Offset (addr to patch = func_addr + offset) + * @param linux_bytes + * @param linux_offset + * @return PatchID, that's needed to patch/unpatch. + */ +native Hacks_CreatePatch(callid, const String:windows_bytes[], windows_offset, const String:linux_bytes[], linux_offset); + +/** + * Writes new bytes to a given address + * + * @param patchid The returned patchid from Hacks_CreatePatch. + * @noreturn + */ +native Hacks_Patch(patchid); + +/** + * Writes the original bytes to a given address + * + * @param patchid The returned patchid from Hacks_CreatePatch. + * @noreturn + */ +native Hacks_Unpatch(patchid); + +/** + * Do not edit below this line! + */ +public Extension:__ext_hacks = +{ + name = "Hacks Extension", + file = "hacks.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; \ No newline at end of file diff --git a/src/include/halflife.inc b/src/include/halflife.inc new file mode 100644 index 0000000..6d832e3 --- /dev/null +++ b/src/include/halflife.inc @@ -0,0 +1,544 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: halflife.inc 1921 2008-03-02 23:54:38Z dvander $ + */ + +#if defined _halflife_included + #endinput +#endif +#define _halflife_included + +#define SOURCE_SDK_UNKNOWN 0 /**< Could not determine the engine version */ +#define SOURCE_SDK_ORIGINAL 10 /**< Original Source engine (still used by "The Ship") */ +#define SOURCE_SDK_EPISODE1 20 /**< SDK+Engine released after Episode 1 */ +#define SOURCE_SDK_EPISODE2 30 /**< Engine released after Episode 2 (no SDK yet) */ + +#define MOTDPANEL_TYPE_TEXT 0 /**< Treat msg as plain text */ +#define MOTDPANEL_TYPE_INDEX 1 /**< Msg is auto determined by the engine */ +#define MOTDPANEL_TYPE_URL 2 /**< Treat msg as an URL link */ +#define MOTDPANEL_TYPE_FILE 3 /**< Treat msg as a filename to be openned */ + +enum DialogType +{ + DialogType_Msg = 0, /**< just an on screen message */ + DialogType_Menu, /**< an options menu */ + DialogType_Text, /**< a richtext dialog */ + DialogType_Entry, /**< an entry box */ + DialogType_AskConnect /**< ask the client to connect to a specified IP */ +}; + +/** + * Logs a generic message to the HL2 logs. + * + * @param format String format. + * @param ... Format arguments. + * @noreturn + */ +native LogToGame(const String:format[], any:...); + +/** + * Sets the seed value for the global Half-Life 2 Random Stream. + * + * @param seed Seed value. + * @noreturn + */ +native SetRandomSeed(seed); + +/** + * Returns a random floating point number from the Half-Life 2 Random Stream. + * + * @param fMin Minimum random bound. + * @param fMax Maximum random bound. + * @return A random number between (inclusive) fMin and fMax. + */ +native Float:GetRandomFloat(Float:fMin=0.0, Float:fMax=1.0); + +/** + * Returns a random number from the Half-Life 2 Random Stream. + * + * @param nmin Minimum random bound. + * @param nmax Maximum random bound. + * @return A random number between (inclusive) nmin and nmax. + */ +native GetRandomInt(nmin, nmax); + +/** + * Returns whether a map is valid or not. + * + * @param Map name, excluding .bsp extension. + * @return True if valid, false otherwise. + */ +native bool:IsMapValid(const String:map[]); + +/** + * Returns whether the server is dedicated. + * + * @return True if dedicated, false otherwise. + */ +native bool:IsDedicatedServer(); + +/** + * Returns a high-precision time value for profiling the engine. + * + * @return A floating point time value. + */ +native Float:GetEngineTime(); + +/** + * Returns the game time based on the game tick. + * + * @return Game tick time. + */ +native Float:GetGameTime(); + +/** + * Returns the game description from the mod. + * + * @param buffer Buffer to store the description. + * @param maxlength Maximum size of the buffer. + * @param original If true, retrieves the original game description, + * ignoring any potential hooks from plugins. + * @return Number of bytes written to the buffer (UTF-8 safe). + */ +native GetGameDescription(String:buffer[], maxlength, bool:original=false); + +/** + * Returns the name of the game's directory. + * + * @param buffer Buffer to store the directory name. + * @param maxlength Maximum size of the buffer. + * + * return Number of bytes written to the buffer (UTF-8 safe). + */ +native GetGameFolderName(String:buffer[], maxlength); + +/** + * Returns the current map name. + * + * @param buffer Buffer to store map name. + * @param maxlength Maximum length of buffer. + * @return Number of bytes written (UTF-8 safe). + */ +native GetCurrentMap(String:buffer[], maxlength); + +/** + * Precaches a given model. + * + * @param model Name of the model to precache. + * @param preload If preload is true the file will be precached before level startup. + * @return Returns the model index, 0 for error. + */ +native PrecacheModel(const String:model[], bool:preload=false); + +/** + * Precaches a given sentence file. + * + * @param file Name of the sentence file to precache. + * @param preload If preload is true the file will be precached before level startup. + * @return Returns a sentence file index. + */ +native PrecacheSentenceFile(const String:file[], bool:preload=false); + +/** + * Precaches a given decal. + * + * @param decal Name of the decal to precache. + * @param preload If preload is true the file will be precached before level startup. + * @return Returns a decal index. + */ +native PrecacheDecal(const String:decal[], bool:preload=false); + +/** + * Precaches a given generic file. + * + * @param generic Name of the generic file to precache. + * @param preload If preload is true the file will be precached before level startup. + * @return Returns a generic file index. + */ +native PrecacheGeneric(const String:generic[], bool:preload=false); + +/** + * Returns if a given model is precached. + * + * @param model Name of the model to check. + * @return True if precached, false otherwise. + */ +native bool:IsModelPrecached(const String:model[]); + +/** + * Returns if a given decal is precached. + * + * @param decal Name of the decal to check. + * @return True if precached, false otherwise. + */ +native bool:IsDecalPrecached(const String:decal[]); + +/** + * Returns if a given generic file is precached. + * + * @param decal Name of the generic file to check. + * @return True if precached, false otherwise. + */ +native bool:IsGenericPrecached(const String:generic[]); + +/** + * Precaches a given sound. + * + * @param sound Name of the sound to precache. + * @param preload If preload is true the file will be precached before level startup. + * @return True if successfully precached, false otherwise. + */ +native bool:PrecacheSound(const String:sound[], bool:preload=false); + +/** + * Returns if a given sound is precached. + * + * @param sound Name of the sound to check. + * @return True if precached, false otherwise. + */ +native bool:IsSoundPrecached(const String:sound[]); + +/** + * Creates different types of ingame messages. + * + * @param client Index of the client. + * @param kv KeyValues handle to set the menu keys and options. (Check iserverplugin.h for more information). + * @param type Message type to display ingame. + * @noreturn + * @error Invalid client index, or client not connected. + */ +native CreateDialog(client, Handle:kv, DialogType:type); + +/** + * Guesses the SDK version a mod was compiled against. If nothing + * specific is known about the game, the engine version is used instead. + * + * The return values are guaranteed to increase chronologically (that is, + * a later release will have a higher value). + * + * @return SOURCE_SDK version code. + */ +native GuessSDKVersion(); + +/** + * Prints a message to a specific client in the chat area. + * + * @param client Client index. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native PrintToChat(client, const String:format[], any:...); + +/** + * Prints a message to all clients in the chat area. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +stock PrintToChatAll(const String:format[], any:...) +{ + new maxClients = GetMaxClients(); + decl String:buffer[192]; + + for (new i = 1; i <= maxClients; i++) + { + if (IsClientInGame(i)) + { + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), format, 2); + PrintToChat(i, "%s", buffer); + } + } +} + +/** + * Prints a message to a specific client in the center of the screen. + * + * @param client Client index. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native PrintCenterText(client, const String:format[], any:...); + +/** + * Prints a message to all clients in the center of the screen. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +stock PrintCenterTextAll(const String:format[], any:...) +{ + new maxClients = GetMaxClients(); + decl String:buffer[192]; + + for (new i = 1; i <= maxClients; i++) + { + if (IsClientInGame(i)) + { + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), format, 2); + PrintCenterText(i, "%s", buffer); + } + } +} + +/** + * Prints a message to a specific client with a hint box. + * + * @param client Client index. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native PrintHintText(client, const String:format[], any:...); + +/** + * Prints a message to all clients with a hint box. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +stock PrintHintTextToAll(const String:format[], any:...) +{ + new maxClients = GetMaxClients(); + decl String:buffer[192]; + + for (new i = 1; i <= maxClients; i++) + { + if (IsClientInGame(i)) + { + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), format, 2); + PrintHintText(i, "%s", buffer); + } + } +} + +/** + * Shows a VGUI panel to a specific client. + * + * @param client Client index. + * @param name Panel type name (Check viewport_panel_names.h to see a list of + * some panel names). + * @param Kv KeyValues handle with all the data for the panel setup (Depends + * on the panel type and may be unused). + * @param show True to show the panel, or false to remove it from the client screen. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native ShowVGUIPanel(client, const String:name[], Handle:Kv=INVALID_HANDLE, bool:show=true); + +/** + * Creates a HUD synchronization object. This object is used to automatically assign and + * re-use channels for a set of messages. + * + * The HUD has a hardcoded number of channels (usually 6) for displaying + * text. You can use any channel for any area of the screen. Text on + * different channels can overlap, but text on the same channel will + * erase the old text first. This overlapping and overwriting gets problematic. + * + * A HUD synchronization object automatically selects channels for you based on + * the following heuristics: + * - If channel X was last used by the object, and hasn't been modified again, + * channel X gets re-used. + * - Otherwise, a new channel is chosen based on the least-recently-used channel. + * + * This ensures that if you display text on a sync object, that the previous text + * displayed on it will always be cleared first. This is because your new text + * will either overwrite the old text on the same channel, or because another + * channel has already erased your text. + * + * Note that messages can still overlap if they are on different synchronization + * objects, or they are displayed to manual channels. + * + * These are particularly useful for displaying repeating or refreshing HUD text, in + * addition to displaying multiple message sets in one area of the screen (for example, + * center-say messages that may pop up randomly that you don't want to overlap each + * other). + * + * @return New HUD synchronization object. + * The Handle can be closed with CloseHandle(). + * If HUD text is not supported on this mod, then + * INVALID_HANDLE is returned. + */ +native Handle:CreateHudSynchronizer(); + +/** + * Sets the HUD parameters for drawing text. These parameters are stored + * globally, although nothing other than this function and SetHudTextParamsEx + * modify them. + * + * You must call this function before drawing text. If you are drawing + * text to multiple clients, you can set the parameters once, since + * they won't be modified. However, as soon as you pass control back + * to other plugins, you must reset the parameters next time you draw. + * + * @param x x coordinate, from 0 to 1. -1.0 is the center. + * @param y y coordinate, from 0 to 1. -1.0 is the center. + * @param holdTime Number of seconds to hold the text. + * @param r Red color value. + * @param g Green color value. + * @param b Blue color value. + * @param a Alpha transparency value. + * @param effect 0/1 causes the text to fade in and fade out. + * 2 causes the text to flash[?]. + * @param fxTime Duration of chosen effect (may not apply to all effects). + * @param fadeIn Number of seconds to spend fading in. + * @param fadeOut Number of seconds to spend fading out. + * @noreturn + */ +native SetHudTextParams(Float:x, Float:y, Float:holdTime, r, g, b, a, effect = 0, + Float:fxTime=6.0, Float:fadeIn=0.1, Float:fadeOut=0.2); + +/** + * Sets the HUD parameters for drawing text. These parameters are stored + * globally, although nothing other than this function and SetHudTextParams + * modify them. + * + * This is the same as SetHudTextParams(), except it lets you set the alternate + * color for when effects require it. + * + * @param x x coordinate, from 0 to 1. -1.0 is the center. + * @param y y coordinate, from 0 to 1. -1.0 is the center. + * @param holdTime Number of seconds to hold the text. + * @param color1 First color set, array values being [red, green, blue, alpha] + * @param color2 Second color set, array values being [red, green, blue, alpha] + * @param effect 0/1 causes the text to fade in and fade out. + * 2 causes the text to flash[?]. + * @param fxTime Duration of chosen effect (may not apply to all effects). + * @param fadeIn Number of seconds to spend fading in. + * @param fadeOut Number of seconds to spend fading out. + * @noreturn + */ +native SetHudTextParamsEx(Float:x, Float:y, Float:holdTime, color1[4], + color2[4]={255,255,255,0}, effect = 0, Float:fxTime=6.0, + Float:fadeIn=0.1, Float:fadeOut=0.2); + +/** + * Shows a synchronized HUD message to a client. + * + * As of this writing, only TF, HL2MP, and SourceForts support HUD Text. + * + * @param client Client index to send the message to. + * @param sync Synchronization object. + * @param message Message text or formatting rules. + * @param ... Message formatting parameters. + * @return -1 on failure, anything else on success. + * This function fails if the mod does not support it. + * @error Client not in-game, or sync object not valid. + */ +native ShowSyncHudText(client, Handle:sync, const String:message[], any:...); + +/** + * Clears the text on a synchronized HUD channel. + * + * This is not the same as sending "" because it guarantees that it won't + * overwrite text on another channel. For example, consider the scenario: + * + * 1. Your synchronized message goes to channel 3. + * 2. Someone else's non-synchronized message goes to channel 3. + * + * If you were to simply send "" on your synchronized message, + * then someone else's text could be overwritten. + * + * @param client Client index to send the message to. + * @param sync Synchronization object. + * @noreturn + * @error Client not in-game, or sync object not valid. + */ +native ClearSyncHud(client, Handle:sync); + +/** + * Shows a HUD message to a client on the given channel. + * + * As of this writing, only TF, HL2MP, and SourceForts support HUD Text. + * + * @param client Client index to send the message to. + * @param channel A channel number. + * If -1, then a channel will automatically be selected + * based on the least-recently-used channel. If the + * channel is any other number, it will be modulo'd with + * the channel count to get a final channel number. + * @param message Message text or formatting rules. + * @param ... Message formatting parameters. + * @return -1 on failure (lack of mod support). + * Any other return value is the channel number that was + * used to render the text. + */ +native ShowHudText(client, channel, const String:message[], any:...); + +/** + * Shows a MOTD panel to a specific client. + * + * @param client Client index. + * @param title Title of the panel (printed on the top border of the window). + * @param msg Contents of the panel, it can be treated as an url, filename or plain text + * depending on the type parameter (WARNING: msg has to be 192 bytes maximum!) + * @param type Determines the way to treat the message body of the panel. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +stock ShowMOTDPanel(client, const String:title[], const String:msg[], type=MOTDPANEL_TYPE_INDEX) +{ + decl String:num[3]; + new Handle:Kv = CreateKeyValues("data"); + IntToString(type, num, sizeof(num)); + + KvSetString(Kv, "title", title); + KvSetString(Kv, "type", num); + KvSetString(Kv, "msg", msg); + ShowVGUIPanel(client, "info", Kv); + CloseHandle(Kv); +} + +/** + * Displays a panel asking the client to connect to a specified IP. + * + * @param client Client index. + * @param time Duration to hold the panel on the client's screen. + * @param ip Destionation IP. + * @noreturn + */ +stock DisplayAskConnectBox(client, Float:time, const String:ip[]) +{ + new Handle:Kv = CreateKeyValues("data"); + KvSetFloat(Kv, "time", time); + KvSetString(Kv, "title", ip); + CreateDialog(client, Kv, DialogType_AskConnect); + CloseHandle(Kv); +} diff --git a/src/include/handles.inc b/src/include/handles.inc new file mode 100644 index 0000000..19a8b6c --- /dev/null +++ b/src/include/handles.inc @@ -0,0 +1,96 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: handles.inc 1771 2007-12-05 15:38:45Z dvander $ + */ + +#if defined _handles_included + #endinput +#endif +#define _handles_included + +/** + * Handle helper macros. + */ +enum Handle +{ + INVALID_HANDLE = 0, +}; + + +/** + * Closes a Handle. If the handle has multiple copies open, + * it is not destroyed unless all copies are closed. + * + * @note Closing a Handle has a different meaning for each Handle type. Make + * sure you read the documentation on whatever provided the Handle. + * + * @param hndl Handle to close. + * @return True if successful, false if not closeable. + * @error Invalid handles will cause a run time error. + */ +native bool:CloseHandle(Handle:hndl); + +/** + * Clones a Handle. When passing handles in between plugins, caching handles + * can result in accidental invalidation when one plugin releases the Handle, or is its owner + * is unloaded from memory. To prevent this, the Handle may be "cloned" with a new owner. + * + * @note Usually, you will be cloning Handles for other plugins. This means that if you clone + * the Handle without specifying the new owner, it will assume the identity of your original calling + * plugin, which is not very useful. You should either specify that the receiving plugin should + * clone the handle on its own, or you should explicitly clone the Handle using the receiving plugin's + * identity Handle. + * + * @param hndl Handle to clone/duplicate. + * @param plugin Optional Handle to another plugin to mark as the new owner. + * If no owner is passed, the owner becomes the calling plugin. + * @return Handle on success, INVALID_HANDLE if not cloneable. + * @error Invalid handles will cause a run time error. + */ +native Handle:CloneHandle(Handle:hndl, Handle:plugin=INVALID_HANDLE); + +/** + * Do not use this function. Returns if a Handle and its contents + * are readable, whereas INVALID_HANDLE only checks for the absence + * of a Handle. + * + * This function is intended only for tests where the validity of a + * Handle can absolutely not be known. + * + * Do not use this to check the return values of functions, or to + * check if timers should be closed (except in very rare cases). + * This function is for very specific usage and using it for general + * purpose routines can and will hide very subtle bugs. + * + * @param hndl Handle to test for validity. + * @return True if handle is valid, false otherwise. + */ +#pragma deprecated Do not use this function. +native bool:IsValidHandle(Handle:hndl); diff --git a/src/include/helpers.inc b/src/include/helpers.inc new file mode 100644 index 0000000..841601a --- /dev/null +++ b/src/include/helpers.inc @@ -0,0 +1,294 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: helpers.inc 1895 2008-02-22 21:01:08Z dvander $ + */ + +#if defined _helpers_included + #endinput +#endif +#define _helpers_included + +/** + * Formats a user's info as log text. This is usually not needed because + * %L can be used to auto-format client information into a string. + * + * @param client Client index. + * @param buffer Buffer for text. + * @param maxlength Maximum length of text. + */ +stock FormatUserLogText(client, String:buffer[], maxlength) +{ + decl String:auth[32]; + decl String:name[40]; + + new userid = GetClientUserId(client); + if (!GetClientAuthString(client, auth, sizeof(auth))) + { + strcopy(auth, sizeof(auth), "UNKNOWN"); + } + if (!GetClientName(client, name, sizeof(name))) + { + strcopy(name, sizeof(name), "UNKNOWN"); + } + + /** Currently, no team stuff ... */ + + Format(buffer, maxlength, "\"%s<%d><%s><>\"", name, userid, auth); +} + +/** + * Returns plugin handle from plugin filename. + * + * @param filename Filename of the plugin to search for. + * @return Handle to plugin if found, INVALID_HANDLE otherwise. + */ +stock Handle:FindPluginByFile(const String:filename[]) +{ + decl String:buffer[256]; + + new Handle:iter = GetPluginIterator(); + new Handle:pl; + + while (MorePlugins(iter)) + { + pl = ReadPlugin(iter); + + GetPluginFilename(pl, buffer, sizeof(buffer)); + if (strcmp(buffer, filename, false) == 0) + { + CloseHandle(iter); + return pl; + } + } + + CloseHandle(iter); + + return INVALID_HANDLE; +} + +/** + * @deprecated Use FindTarget() or ProcessTargetString(). + */ +#pragma deprecated Use FindTarget() or ProcessTargetString() +stock SearchForClients(const String:pattern[], clients[], maxClients) +{ + new maxclients = GetMaxClients(); + new total = 0; + + if (maxClients == 0) + { + return 0; + } + + if (pattern[0] == '#') + { + new input = StringToInt(pattern[1]); + if (!input) + { + decl String:name[65] + for (new i=1; i<=maxclients; i++) + { + if (!IsClientInGame(i)) + { + continue; + } + GetClientName(i, name, sizeof(name)); + if (strcmp(name, pattern, false) == 0) + { + clients[0] = i; + return 1; + } + } + } else { + new client = GetClientOfUserId(input); + if (client) + { + clients[0] = client; + return 1; + } + } + } + + decl String:name[65] + for (new i=1; i<=maxclients; i++) + { + if (!IsClientInGame(i)) + { + continue; + } + GetClientName(i, name, sizeof(name)); + if (StrContains(name, pattern, false) != -1) + { + clients[total++] = i; + if (total >= maxClients) + { + break; + } + } + } + + return total; +} + +/** + * Wraps ProcessTargetString() and handles producing error messages for + * bad targets. + * + * @param client Client who issued command + * @param target Client's target argument + * @param nobots Optional. Set to true if bots should NOT be targetted + * @param immunity Optional. Set to false to ignore target immunity. + * @return Index of target client, or -1 on error. + */ +stock FindTarget(client, const String:target[], bool:nobots = false, bool:immunity = true) +{ + decl String:target_name[MAX_TARGET_LENGTH]; + decl target_list[1], target_count, bool:tn_is_ml; + + new flags = COMMAND_FILTER_NO_MULTI; + if (nobots) + { + flags |= COMMAND_FILTER_NO_BOTS; + } + if (!immunity) + { + flags |= COMMAND_FILTER_NO_IMMUNITY; + } + + if ((target_count = ProcessTargetString( + target, + client, + target_list, + 1, + flags, + target_name, + sizeof(target_name), + tn_is_ml)) > 0) + { + return target_list[0]; + } + else + { + ReplyToTargetError(client, target_count); + return -1; + } +} + +/** + * This function is no longer supported. It has been replaced with ReadMapList(), + * which uses a more unified caching and configuration mechanism. This function also + * has a bug where if the cvar contents changes, the fileTime change won't be recognized. + * + * Loads a specified array with maps. The maps will be either loaded from mapcyclefile, or if supplied + * a cvar containing a file name. If the file in the cvar is bad, it will use mapcyclefile. The fileTime + * parameter is used to store a timestamp of the file. If specified, the file will only be reloaded if it + * has changed. + * + * @param array Valid array handle, should be created with CreateArray(33) or larger. + * @param fileTime Variable containing the "last changed" time of the file. Used to avoid needless reloading. + * @param fileCvar CVAR set to the file to be loaded. Optional. + * @return Number of maps loaded or 0 if in error. + */ +#pragma deprecated Use ReadMapList() instead. + stock LoadMaps(Handle:array, &fileTime = 0, Handle:fileCvar = INVALID_HANDLE) + { + decl String:mapPath[256], String:mapFile[64]; + new bool:fileFound = false; + + if (fileCvar != INVALID_HANDLE) + { + GetConVarString(fileCvar, mapFile, 64); + BuildPath(Path_SM, mapPath, sizeof(mapPath), mapFile); + fileFound = FileExists(mapPath); + } + + if (!fileFound) + { + new Handle:mapCycleFile = FindConVar("mapcyclefile"); + GetConVarString(mapCycleFile, mapPath, sizeof(mapPath)); + fileFound = FileExists(mapPath); + } + + if (!fileFound) + { + LogError("Failed to find a file to load maps from. No maps loaded."); + ClearArray(array); + + return 0; + } + + // If the file hasn't changed, there's no reason to reload + // all of the maps. + new newTime = GetFileTime(mapPath, FileTime_LastChange); + if (fileTime == newTime) + { + return GetArraySize(array); + } + + fileTime = newTime; + + ClearArray(array); + + new Handle:file = OpenFile(mapPath, "rt"); + if (file == INVALID_HANDLE) + { + LogError("Could not open file: %s", mapPath); + + return 0; + } + + LogMessage("Loading maps from file: %s", mapPath); + + decl String:buffer[64], len; + while (!IsEndOfFile(file) && ReadFileLine(file, buffer, sizeof(buffer))) + { + TrimString(buffer); + + if ((len = StrContains(buffer, ".bsp", false)) != -1) + { + buffer[len] = '\0'; + } + + if (buffer[0] == '\0' || !IsValidConVarChar(buffer[0]) || !IsMapValid(buffer)) + { + continue; + } + + if (FindStringInArray(array, buffer) != -1) + { + continue; + } + + PushArrayString(array, buffer); + } + + CloseHandle(file); + return GetArraySize(array); +} diff --git a/src/include/keyvalues.inc b/src/include/keyvalues.inc new file mode 100644 index 0000000..9e07883 --- /dev/null +++ b/src/include/keyvalues.inc @@ -0,0 +1,429 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: keyvalues.inc 1740 2007-12-01 00:37:47Z faluco $ + */ + +#if defined _keyvalues_included + #endinput +#endif +#define _keyvalues_included + +/** + * KeyValue data value types + */ +enum KvDataTypes +{ + KvData_None = 0, /**< Type could not be identified, or no type */ + KvData_String, /**< String value */ + KvData_Int, /**< Integer value */ + KvData_Float, /**< Floating point value */ + KvData_Ptr, /**< Pointer value (sometimes called "long") */ + KvData_WString, /**< Wide string value */ + KvData_Color, /**< Color value */ + KvData_UInt64, /**< Large integer value */ + /* --- */ + KvData_NUMTYPES, +}; + +/** + * Creates a new KeyValues structure. The Handle must always be closed. + * + * @param name Name of the root section. + * @param firstKey If non-empty, specifies the first key value. + * @param firstValue If firstKey is non-empty, specifies the first key's value. + * @return A Handle to a new KeyValues structure. + */ +native Handle:CreateKeyValues(const String:name[], const String:firstkey[]="", const String:firstValue[]=""); + +/** + * Sets a string value of a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param value String value. + * @noreturn + * @error Invalid Handle. + */ +native KvSetString(Handle:kv, const String:key[], const String:value[]); + +/** + * Sets an integer value of a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param value Value number. + * @noreturn + * @error Invalid Handle. + */ +native KvSetNum(Handle:kv, const String:key[], value); + +/** + * Sets a large integer value of a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param value Large integer value (0=High bits, 1=Low bits) + * @noreturn + * @error Invalid Handle. + */ +native KvSetUInt64(Handle:kv, const String:key[], const value[2]); + +/** + * Sets a floating point value of a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param value Floating point value. + * @noreturn + * @error Invalid Handle. + */ +native KvSetFloat(Handle:kv, const String:key[], Float:value); + +/** + * Sets a set of color values of a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param r Red value. + * @param g Green value. + * @param b Blue value. + * @param a Alpha value. + * @noreturn + * @error Invalid Handle. + */ +native KvSetColor(Handle:kv, const String:key[], r, g, b, a=0); + +/** + * Sets a vector value of a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param vec Vector value. + * @noreturn + * @error Invalid Handle. + */ +native KvSetVector(Handle:kv, const String:key[], const Float:vec[3]); + +/** + * Retrieves a string value from a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param value Buffer to store key value in. + * @param maxlength Maximum length of the value buffer. + * @param defvalue Optional default value to use if the key is not found. + * @noreturn + * @error Invalid Handle. + */ +native KvGetString(Handle:kv, const String:key[], String:value[], maxlength, const String:defvalue[]=""); + +/** + * Retrieves an integer value from a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param defvalue Optional default value to use if the key is not found. + * @return Integer value of the key. + * @error Invalid Handle. + */ +native KvGetNum(Handle:kv, const String:key[], defvalue=0); + +/** + * Retrieves a floating point value from a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param defvalue Optional default value to use if the key is not found. + * @return Floating point value of the key. + * @error Invalid Handle. + */ +native Float:KvGetFloat(Handle:kv, const String:key[], Float:defvalue=0.0); + +/** + * Retrieves a set of color values from a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param r Red value, set by reference. + * @param g Green value, set by reference. + * @param b Blue value, set by reference. + * @param a Alpha value, set by reference. + * @noreturn + * @error Invalid Handle. + */ +native KvGetColor(Handle:kv, const String:key[], &r, &g, &b, &a); + +/** + * Retrieves a large integer value from a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param value Array to represent the large integer. + * @param defvalue Optional default value to use if the key is not found. + * @noreturn + * @error Invalid Handle. + */ +native KvGetUInt64(Handle:kv, const String:key[], value[2], defvalue[2]={0,0}); + +/** + * Retrieves a vector value from a KeyValues key. + * + * @param kv KeyValues Handle. + * @param key Name of the key, or NULL_STRING. + * @param vec Destination vector to store the value in. + * @param defvalue Optional default value to use if the key is not found. + * @noreturn + * @error Invalid Handle. + */ +native KvGetVector(Handle:kv, const String:key[], Float:vec[3], const Float:defvalue[3]={0.0, 0.0, 0.0}); + +/** + * Sets the current position in the KeyValues tree to the given key. + * + * @param kv KeyValues Handle. + * @param key Name of the key. + * @param create If true, and the key does not exist, it will be created. + * @return True if the key exists, false if it does not and was not created. + */ +native bool:KvJumpToKey(Handle:kv, const String:key[], bool:create=false); + +/** + * Sets the current position in the KeyValues tree to the given key. + * + * @param kv KeyValues Handle. + * @param id KeyValues id. + * @return True if the key exists, false if it does not. + */ +native bool:KvJumpToKeySymbol(Handle:kv, id); + +/** + * Sets the current position in the KeyValues tree to the first sub key. + * This native adds to the internal traversal stack. + * + * @param kv KeyValues Handle. + * @param keyOnly If false, non-keys will be traversed (values). + * @return True on success, false if there was no first sub key. + * @error Invalid Handle. + */ +native bool:KvGotoFirstSubKey(Handle:kv, bool:keyOnly=true); + +/** + * Sets the current position in the KeyValues tree to the next sub key. + * This native does NOT add to the internal traversal stack, and thus + * KvGoBack() is not needed for each successive call to this function. + * + * @param kv KeyValues Handle. + * @param keyOnly If false, non-keys will be traversed (values). + * @return True on success, false if there was no next sub key. + * @error Invalid Handle. + */ +native bool:KvGotoNextKey(Handle:kv, bool:keyOnly=true); + +/** + * Saves the current position in the traversal stack onto the traversal + * stack. This can be useful if you wish to use KvGotoNextKey() and + * have the previous key saved for backwards traversal. + * + * @param kv KeyValues Handle. + * @noreturn + * @error Invalid Handle. + */ +native KvSavePosition(Handle:kv); + +/** + * Removes the given key from the current position. + * + * @param kv KeyValues Handle. + * @param key Name of the key. + * @return True on success, false if key did not exist. + * @error Invalid Handle. + */ +native bool:KvDeleteKey(Handle:kv, const String:key[]); + +/** + * Removes the current sub-key and attempts to set the position + * to the sub-key after the removed one. If no such sub-key exists, + * the position will be the parent key in the traversal stack. + * Given the sub-key having position "N" in the traversal stack, the + * removal will always take place from position "N-1." + * + * @param kv KeyValues Handle. + * @return 1 if removal succeeded and there was another key. + * 0 if the current node was not contained in the + * previous node, or no previous node exists. + * -1 if removal succeeded and there were no more keys, + * thus the state is as if KvGoBack() was called. + * @error Invalid Handle. + */ +native KvDeleteThis(Handle:kv); + +/** + * Jumps back to the previous position. Returns false if there are no + * previous positions (i.e., at the root node). This should be called + * once for each successful Jump call, in order to return to the top node. + * This function pops one node off the internal traversal stack. + * + * @param kv KeyValues Handle. + * @return True on success, false if there is no higher node. + * @error Invalid Handle. + */ +native bool:KvGoBack(Handle:kv); + +/** + * Sets the position back to the top node, emptying the entire node + * traversal history. This can be used instead of looping KvGoBack() + * if recursive iteration is not important. + * + * @param kv KeyValues Handle. + * @noreturn + * @error Invalid Handle. + */ +native KvRewind(Handle:kv); + +/** + * Retrieves the current section name. + * + * @param kv KeyValues Handle. + * @param section Buffer to store the section name. + * @param maxlength Maximum length of the name buffer. + * @return True on success, false on failure. + * @error Invalid Handle. + */ +native bool:KvGetSectionName(Handle:kv, String:section[], maxlength); + +/** + * Sets the current section name. + * + * @param kv KeyValues Handle. + * @param section Section name. + * @noreturn + * @error Invalid Handle. + */ +native KvSetSectionName(Handle:kv, const String:section[]); + +/** + * Returns the data type at a key. + * + * @param kv KeyValues Handle. + * @param key Key name. + * @return KvDataType value of the key. + * @error Invalid Handle. + */ +native KvDataTypes:KvGetDataType(Handle:kv, const String:key[]); + +/** + * Converts a KeyValues tree to a file. The tree is dumped + * from the current position. + * + * @param kv KeyValues Handle. + * @param file File to dump write to. + * @return True on success, false otherwise. + * @error Invalid Handle. + */ +native bool:KeyValuesToFile(Handle:kv, const String:file[]); + +/** + * Converts a file to a KeyValues tree. The file is read into + * the current position of the tree. + * + * @param kv KeyValues Handle. + * @param file File to read from. + * @return True on success, false otherwise. + * @error Invalid Handle. + */ +native bool:FileToKeyValues(Handle:kv, const String:file[]); + +/** + * Sets whether or not the KeyValues parser will read escape sequences. + * For example, \n would be read as a literal newline. This defaults + * to false for new KeyValues structures. + * + * @param kv KeyValues Handle. + * @param useEscapes Whether or not to read escape sequences. + * @noreturn + * @error Invalid Handle. + */ +native KvSetEscapeSequences(Handle:kv, bool:useEscapes); + +/** + * Returns the position in the jump stack; I.e. the number of calls + * required for KvGoBack to return to the root node. If at the root node, + * 0 is returned. + * + * @param kv KeyValues Handle. + * @return Number of non-root nodes in the jump stack. + * @error Invalid Handle. + */ +native KvNodesInStack(Handle:kv); + +/** + * Makes a new copy of all subkeys in the origin KeyValues to + * the destination KeyValues. + * NOTE: All KeyValues are processed from the current location not the root one. + * + * @param origin Origin KeyValues Handle. + * @param dest Destination KeyValues Handlee. + * @noreturn + * @error Invalid Handle. + */ +native KvCopySubkeys(Handle:origin, Handle:dest); + +/** + * Finds a KeyValues name by id. + * + * @param kv KeyValues Handle. + * @param id KeyValues id. + * @param name Buffer to store the name. + * @param maxlength Maximum length of the value buffer. + * @return True on success, false if id not found. + * @error Invalid Handle. + */ +native bool:KvFindKeyById(Handle:kv, id, String:name[], maxlength); + +/** + * Finds a KeyValues id inside a KeyValues tree. + * + * @param kv KeyValues Handle. + * @param key Key name. + * @param id Id of the found KeyValue. + * @return True on success, false if key not found. + * @error Invalid Handle. + */ +native bool:KvGetNameSymbol(Handle:kv, const String:key[], &id); + +/** + * Retrieves the current section id. + * + * @param kv KeyValues Handle. + * @param id Id of the current section. + * @return True on success, false on failure. + * @error Invalid Handle. + */ +native bool:KvGetSectionSymbol(Handle:kv, &id); diff --git a/src/include/lang.inc b/src/include/lang.inc new file mode 100644 index 0000000..3186d40 --- /dev/null +++ b/src/include/lang.inc @@ -0,0 +1,96 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: lang.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _lang_included + #endinput +#endif +#define _lang_included + +#define LANG_SERVER 0 /**< Translate using the server's language */ + +/** + * Loads a translation file for the plugin calling this native. + * If no extension is specified, .txt is assumed. + * + * @param path Translation file. + * @noreturn + */ +native LoadTranslations(const String:file[]); + +/** + * Sets the global language target. This is useful for creating functions + * that will be compatible with the %t format specifier. Note that invalid + * indexes can be specified but the error will occur during translation, + * not during this function call. + * + * @param client Client index or LANG_SERVER. + * @noreturn + */ +native SetGlobalTransTarget(client); + +/** + * Retrieves the language number of a client. + * Currently this simply returns the server language index. + * + * @param client Client index. + * @return Language number client is using. + * @error Invalid client index or client not in game. + */ +native GetClientLanguage(client); + +/** + * Retrieves the server's language. + * + * @return Language number server is using. + */ +native GetServerLanguage(); + +/** + * Returns the number of languages known in languages.cfg. + * + * @return Language count. + */ +native GetLanguageCount(); + +/** + * Retrieves info about a given language number. + * + * @param language Language number. + * @param code Language code buffer (2-3 characters usually). + * @param codeLen Maximum length of the language code buffer. + * @param name Language name buffer. + * @param nameLen Maximum length of the language name buffer. + * @noreturn + * @error Invalid language number. + */ +native GetLanguageInfo(language, String:code[]="", codeLen=0, String:name[]="", nameLen=0); + diff --git a/src/include/logging.inc b/src/include/logging.inc new file mode 100644 index 0000000..569c3c5 --- /dev/null +++ b/src/include/logging.inc @@ -0,0 +1,153 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: logging.inc 1920 2008-03-02 19:08:27Z dvander $ + */ + +#if defined _sm_logging_included + #endinput +#endif +#define _sm_logging_included + +/** + * Logs a plugin message to the SourceMod logs. The log message will be + * prefixed by the plugin's logtag (filename). + * + * @param format String format. + * @param ... Format arguments. + * @noreturn + */ +native LogMessage(const String:format[], any:...); + +/** + * Logs a message to the SourceMod logs without any plugin logtag. This is + * useful for re-routing messages from other plugins, for example, messages + * from LogAction(). + * + * @param format String format. + * @param ... Format arguments. + * @noreturn + */ +native LogMessageEx(const String:format[], any:...); + +/** + * Logs a message to any file. The log message will be in the normal + * SourceMod format, with the plugin logtag prepended. + * + * @param file File to write the log message in. + * @param format String format. + * @param ... Format arguments. + * @noreturn + * @error File could not be opened/written. + */ +native LogToFile(const String:file[], const String:format[], any:...); + +/** + * Same as LogToFile(), except no plugin logtag is prepended. + * + * @param file File to write the log message in. + * @param format String format. + * @param ... Format arguments. + * @noreturn + * @error File could not be opened/written. + */ +native LogToFileEx(const String:file[], const String:format[], any:...); + +/** + * Logs an action from a command or event whereby interception and routing may + * be important. This is intended to be a logging version of ShowActivity(). + * + * @param client Client performing the action, 0 for server, or -1 if not + * applicable. + * @param target Client being targetted, or -1 if not applicable. + * @param message Message format. + * @param ... Message formatting parameters. + * @noreturn + */ +native LogAction(client, target, const String:message[], any:...); + +/** + * Logs a plugin error message to the SourceMod logs. + * + * @param format String format. + * @param ... Format arguments. + * @noreturn + */ +native LogError(const String:format[], any:...); + +/** + * Called when an action is going to be logged. + * + * @param source Handle to the object logging the action, or INVALID_HANDLE + * if Core is logging the action. + * @param ident Type of object logging the action (plugin, ext, or core). + * @param client Client the action is from; 0 for server, -1 if not applicable. + * @param target Client the action is targetting, or -1 if not applicable. + * @param message Message that is being logged. + * @return Plugin_Continue will cause Core to defaulty log the message. + * Plugin_Handled will stop Core from logging the message. + * Plugin_Stop is the same as Handled, but prevents any other + * plugins from handling the message. + */ +forward Action:OnLogAction(Handle:source, + Identity:ident, + client, + target, + const String:message[]); + +/** + * Called when a game log message is received. + * + * Any Log*() functions called within this callback will not recursively + * pass through. That is, they will log directly, bypassing this callback. + * + * Note that this does not capture log messages from the engine. It only + * captures log messages being sent from the game/mod itself. + * + * @param message Message contents. + * @return Plugin_Handled or Plugin_Stop will prevent the message + * from being written to the log file. + */ +functag GameLogHook Action:public(const String:message[]); + +/** + * Adds a game log hook. + * + * @param hook Hook function. + * @noreturn + */ +native AddGameLogHook(GameLogHook:hook); + +/** + * Removes a game log hook. + * + * @param hook Hook function. + * @noreturn + */ +native RemoveGameLogHook(GameLogHook:hook); diff --git a/src/include/market.inc b/src/include/market.inc new file mode 100644 index 0000000..64eeff0 --- /dev/null +++ b/src/include/market.inc @@ -0,0 +1,62 @@ +/** + * ==================== + * Market + * File: market.inc + * Version: 1.2 + * Author: Greyscale + * ==================== + */ + +/** + * Used to send the market to a client + * @param client Client index + * @param title Title of the market menu + * @param rebuy Text to display for the "Rebuy" option + * @noreturn + */ +native Market_Send(client, const String:title[], const String:rebuy[]); + +/** + * Used to send the market to a client + * @param weapondid ID of the weapon whose info will be returned + * @param display The display name of the weapon + * @param weapon The entity name of the weapon + * @param price The price of the weapon + * @return True if weaponid exists, false if not + */ +native bool:Market_GetWeaponIDInfo(const String:weaponid[], String:display[], String:weapon[], &price); + +/** + * Called when a player makes a selection from the menu + * @param client Client index + * @param weaponid Returns the ID of the weapon selected ("rebuy" is returned when player selected rebuy) + * @return True to allow purchase, false to block + */ +forward bool:Market_OnWeaponSelected(client, String:weaponid[]); + +/** + * Called after a player makes a selection from the menu + * @param client Client index + * @param allowed True if selection was allowed and false if it was blocked + * noreturn + */ +forward Market_PostOnWeaponSelected(client, &bool:allowed); + + + +public SharedPlugin:__pl_market = +{ + name = "market", + file = "market.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +public __pl_market_SetNTVOptional() +{ + MarkNativeAsOptional("Market_Send"); + MarkNativeAsOptional("Market_GetWeaponIDInfo"); +} \ No newline at end of file diff --git a/src/include/menus.inc b/src/include/menus.inc new file mode 100644 index 0000000..700ac51 --- /dev/null +++ b/src/include/menus.inc @@ -0,0 +1,787 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: menus.inc 1777 2007-12-06 12:26:59Z dvander $ + */ + +#if defined _menus_included + #endinput +#endif +#define _menus_included + +/** + * Low-level drawing style of the menu. + */ +enum MenuStyle +{ + MenuStyle_Default = 0, /**< The "default" menu style for the mod */ + MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */ + MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */ +}; + +/** + * Different actions for the menu "pump" callback + */ +enum MenuAction +{ + MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */ + MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */ + MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */ + MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=reason) */ + MenuAction_End = (1<<4), /**< A menu display has fully ended. + param1 is the MenuEnd reason, and if it's MenuEnd_Cancelled, then + param2 is the MenuCancel reason from MenuAction_Cancel. + */ + MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has succeeded (param1=chosen item) + This is not called if SetVoteResultCallback has been used on the menu. */ + MenuAction_VoteStart = (1<<6), /**< (VOTE ONLY): A vote sequence has started (nothing passed) */ + MenuAction_VoteCancel = (1<<7), /**< (VOTE ONLY): A vote sequence has been cancelled (param1=reason) */ + MenuAction_DrawItem = (1<<8), /**< An item is being drawn; return the new style (param1=client, param2=item) */ + MenuAction_DisplayItem = (1<<9),/**< Item text is being drawn to the display (param1=client, param2=item) + To change the text, use RedrawMenuItem(). + If you do so, return its return value. Otherwise, return 0. + */ +}; + +/** Default menu actions */ +#define MENU_ACTIONS_DEFAULT MenuAction_Select|MenuAction_Cancel|MenuAction_End +/** All menu actions */ +#define MENU_ACTIONS_ALL MenuAction:0xFFFFFFFF + +#define MENU_NO_PAGINATION 0 /**< Menu should not be paginated (10 items max) */ +#define MENU_TIME_FOREVER 0 /**< Menu should be displayed as long as possible */ + +#define ITEMDRAW_DEFAULT (0) /**< Item should be drawn normally */ +#define ITEMDRAW_DISABLED (1<<0) /**< Item is drawn but not selectable */ +#define ITEMDRAW_RAWLINE (1<<1) /**< Item should be a raw line, without a slot */ +#define ITEMDRAW_NOTEXT (1<<2) /**< No text should be drawn */ +#define ITEMDRAW_SPACER (1<<3) /**< Item should be drawn as a spacer, if possible */ +#define ITEMDRAW_IGNORE ((1<<1)|(1<<2)) /**< Item should be completely ignored (rawline + notext) */ +#define ITEMDRAW_CONTROL (1<<4) /**< Item is control text (back/next/exit) */ + +#define MENUFLAG_BUTTON_EXIT (1<<0) /**< Menu has an "exit" button (default if paginated) */ +#define MENUFLAG_BUTTON_EXITBACK (1<<1) /**< Menu has an "exit back" button */ +#define MENUFLAG_NO_SOUND (1<<2) /**< Menu will not have any select sounds */ + +#define VOTEINFO_CLIENT_INDEX 0 /**< Client index */ +#define VOTEINFO_CLIENT_ITEM 1 /**< Item the client selected, or -1 for none */ +#define VOTEINFO_ITEM_INDEX 0 /**< Item index */ +#define VOTEINFO_ITEM_VOTES 1 /**< Number of votes for the item */ + +/** + * Reasons a menu can be cancelled (MenuAction_Cancel). + */ +enum +{ + MenuCancel_Disconnected = -1, /**< Client dropped from the server */ + MenuCancel_Interrupted = -2, /**< Client was interrupted with another menu */ + MenuCancel_Exit = -3, /**< Client exited via "exit" */ + MenuCancel_NoDisplay = -4, /**< Menu could not be displayed to the client */ + MenuCancel_Timeout = -5, /**< Menu timed out */ + MenuCancel_ExitBack = -6, /**< Client selected "exit back" on a paginated menu */ +}; + +/** + * Reasons a vote can be cancelled (MenuAction_VoteCancel). + */ +enum +{ + VoteCancel_Generic = -1, /**< Vote was generically cancelled. */ + VoteCancel_NoVotes = -2, /**< Vote did not receive any votes. */ +}; + +/** + * Reasons a menu ended (MenuAction_End). + */ +enum +{ + MenuEnd_Selected = 0, /**< Menu item was selected */ + MenuEnd_VotingDone = -1, /**< Voting finished */ + MenuEnd_VotingCancelled = -2, /**< Voting was cancelled */ + MenuEnd_Cancelled = -3, /**< Menu was cancelled (reason in param2) */ + MenuEnd_Exit = -4, /**< Menu was cleanly exited via "exit" */ + MenuEnd_ExitBack = -5, /**< Menu was cleanly exited via "back" */ +}; + +/** + * Describes a menu's source + */ +enum MenuSource +{ + MenuSource_None = 0, /**< No menu is being displayed */ + MenuSource_External = 1, /**< External menu */ + MenuSource_Normal = 2, /**< A basic menu is being displayed */ + MenuSource_RawPanel = 3, /**< A display is active, but it is not tied to a menu */ +}; + +/** + * Called when a menu action is completed. + * + * @param menu The menu being acted upon. + * @param action The action of the menu. + * @param param1 First action parameter (usually the client). + * @param param2 Second action parameter (usually the item). + * @noreturn + */ +functag MenuHandler public(Handle:menu, MenuAction:action, param1, param2); + +/** + * Creates a new, empty menu using the default style. + * + * @param handler Function which will receive menu actions. + * @param actions Optionally set which actions to receive. Select, + * Cancel, and End will always be received regardless + * of whether they are set or not. They are also + * the only default actions. + * @return A new menu Handle. + */ +native Handle:CreateMenu(MenuHandler:handler, MenuAction:actions=MENU_ACTIONS_DEFAULT); + +/** + * Displays a menu to a client. + * + * @param menu Menu Handle. + * @param client Client index. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false on failure. + * @error Invalid Handle or client not in game. + */ +native bool:DisplayMenu(Handle:menu, client, time); + +/** + * Displays a menu to a client, starting from the given item. + * + * @param menu Menu Handle. + * @param client Client index. + * @param first_item First item to begin drawing from. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false on failure. + * @error Invalid Handle or client not in game. + */ +native bool:DisplayMenuAtItem(Handle:menu, client, first_item, time); + +/** + * Appends a new item to the end of a menu. + * + * @param menu Menu Handle. + * @param info Item information string. + * @param display Default item display string. + * @param style Drawing style flags. Anything other than DEFAULT or + * DISABLED will be completely ignored when paginating. + * @return True on success, false on failure. + * @error Invalid Handle or item limit reached. + */ +native AddMenuItem(Handle:menu, + const String:info[], + const String:display[], + style=ITEMDRAW_DEFAULT); + +/** + * Inserts an item into the menu before a certain position; the new item will + * be at the given position and all next items pushed forward. + * + * @param menu Menu Handle. + * @param position Position, starting from 0. + * @param info Item information string. + * @param display Default item display string. + * @param style Drawing style flags. Anything other than DEFAULT or + * DISABLED will be completely ignored when paginating. + * @return True on success, false on failure. + * @error Invalid Handle or menu position. + */ +native bool:InsertMenuItem(Handle:menu, + position, + const String:info[], + const String:display[], + style=ITEMDRAW_DEFAULT); + +/** + * Removes an item from the menu. + * + * @param menu Menu Handle. + * @param position Position, starting from 0. + * @return True on success, false on failure. + * @error Invalid Handle or menu position. + */ +native bool:RemoveMenuItem(Handle:menu, position); + +/** + * Removes all items from a menu. + * + * @param menu Menu Handle. + * @noreturn + * @error Invalid Handle or menu position. + */ +native RemoveAllMenuItems(Handle:menu); + +/** + * Retrieves information about a menu item. + * + * @param menu Menu Handle. + * @param position Position, starting from 0. + * @param infoBuf Info buffer. + * @param infoBufLen Maximum length of the info buffer. + * @param style By-reference variable to store drawing flags. + * @param dispBuf Display buffer. + * @param dispBufLen Maximum length of the display buffer. + * @return True on success, false if position is invalid. + * @error Invalid Handle. + */ +native bool:GetMenuItem(Handle:menu, + position, + String:infoBuf[], + infoBufLen, + &style=0, + String:dispBuf[]="", + dispBufLen=0); + +/** + * Returns the first item on the page of a currently selected menu. + * + * This is only valid inside a MenuAction_Select callback. + * + * @return First item number on the page the client was viewing + * before selecting the item in the callback. This can + * be used to re-display the menu from the original + * position. + * @error Not called from inside a MenuAction_Select callback. + */ +native GetMenuSelectionPosition(); + +/** + * Returns the number of items in a menu. + * + * @param menu Menu Handle. + * @return Number of items in the menu. + * @error Invalid Handle. + */ +native GetMenuItemCount(Handle:menu); + +/** + * Sets whether the menu should be paginated or not. + * + * If itemsPerPage is MENU_NO_PAGINATION, and the exit button flag is set, + * then the exit button flag is removed. It can be re-applied if desired. + * + * @param menu Handle to the menu. + * @param itemsPerPage Number of items per page, or MENU_NO_PAGINATION. + * @return True on success, false if pagination is too high or + * low. + * @error Invalid Handle. + */ +native bool:SetMenuPagination(Handle:menu, itemsPerPage); + +/** + * Returns a menu's pagination setting. + * + * @param menu Handle to the menu. + * @return Pagination setting. + * @error Invalid Handle. + */ +native GetMenuPagination(Handle:menu); + +/** + * Returns a menu's MenuStyle Handle. The Handle + * is global and cannot be freed. + * + * @param menu Handle to the menu. + * @return Handle to the menu's draw style. + * @error Invalid Handle. + */ +native Handle:GetMenuStyle(Handle:menu); + +/** + * Sets the menu's default title/instruction message. + * + * @param menu Menu Handle. + * @param fmt Message string format + * @param ... Message string arguments. + * @noreturn + * @error Invalid Handle. + */ +native SetMenuTitle(Handle:menu, const String:fmt[], any:...); + +/** + * Returns the text of a menu's title. + * + * @param menu Menu Handle. + * @param buffer Buffer to store title. + * @param maxlength Maximum length of the buffer. + * @return Number of bytes written. + * @error Invalid Handle/ + */ +native GetMenuTitle(Handle:menu, String:buffer[], maxlength); + +/** + * Creates a raw MenuPanel based off the menu's style. + * The Handle must be freed with CloseHandle(). + * + * @return A new MenuPanel Handle. + * @error Invalid Handle. + */ +native Handle:CreatePanelFromMenu(Handle:menu); + +/** + * Returns whether or not the menu has an exit button. + * By default, menus have an exit button. + * + * @param menu Menu Handle. + * @return True if the menu has an exit button; false otherwise. + * @error Invalid Handle. + */ +native bool:GetMenuExitButton(Handle:menu); + +/** + * Sets whether or not the menu has an exit button. By default, paginated menus + * have an exit button. + * + * If a menu's pagination is changed to MENU_NO_PAGINATION, and the pagination + * was previously a different value, then the Exit button status is changed to + * false. It must be explicitly re-enabled afterwards. + * + * If a non-paginated menu has an exit button, then at most 9 items will be + * displayed. + * + * @param menu Menu Handle. + * @param button True to enable the button, false to remove it. + * @return True if allowed; false on failure. + * @error Invalid Handle. + */ +native bool:SetMenuExitButton(Handle:menu, bool:button); + +/** + * Returns whether or not the menu has an "exit back" button. By default, + * menus do not have an exit back button. + * + * Exit Back buttons appear as "Back" on page 1 of paginated menus and have + * functionality defined by the user in MenuEnd_ExitBack. + * + * @param menu Menu Handle. + * @return True if the menu has an exit back button; false otherwise. + * @error Invalid Handle. + */ +native bool:GetMenuExitBackButton(Handle:menu); + +/** + * Sets whether or not the menu has an "exit back" button. By default, menus + * do not have an exit back button. + * + * Exit Back buttons appear as "Back" on page 1 of paginated menus and have + * functionality defined by the user in MenuEnd_ExitBack. + * + * @param menu Menu Handle. + * @param button True to enable the button, false to remove it. + * @error Invalid Handle. + */ +native SetMenuExitBackButton(Handle:menu, bool:button); + + +/** + * Cancels a menu from displaying on all clients. While the + * cancellation is in progress, this menu cannot be re-displayed + * to any clients. + * + * The menu may still exist on the client's screen after this command. + * This simply verifies that the menu is not being used anywhere. + * + * If any vote is in progress on a menu, it will be cancelled. + * + * @param menu Menu Handle. + * @noreturn + * @error Invalid Handle. + */ +native CancelMenu(Handle:menu); + +/** + * Retrieves a menu's option flags. + * + * @param menu Menu Handle. + * @return A bitstring of MENUFLAG bits. + * @error Invalid Handle. + */ +native GetMenuOptionFlags(Handle:menu); + +/** + * Sets a menu's option flags. + * + * If a certain bit is not supported, it will be stripped before being set. + * See SetMenuExitButton() for information on Exit buttons. + * See SetMenuExitBackButton() for information on Exit Back buttons. + * + * @param menu Menu Handle. + * @param flags A new bitstring of MENUFLAG bits. + * @noreturn + * @error Invalid Handle. + */ +native SetMenuOptionFlags(Handle:menu, flags); + +/** + * Returns whether a vote is in progress. + * + * @param menu Deprecated; no longer used. + * @return True if a vote is in progress, false otherwise. + */ +native bool:IsVoteInProgress(Handle:menu=INVALID_HANDLE); + +/** + * Cancels the vote in progress. + * + * @noreturn + * @error If no vote is in progress. + */ +native CancelVote(); + +/** + * Broadcasts a menu to a list of clients. The most selected item will be + * returned through MenuAction_End. On a tie, a random item will be returned + * from a list of the tied items. + * + * Note that MenuAction_VoteEnd and MenuAction_VoteStart are both + * default callbacks and do not need to be enabled. + * + * @param menu Menu Handle. + * @param clients Array of clients to broadcast to. + * @param numClients Number of clients in the array. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false if this menu already has a vote session + * in progress. + * @error Invalid Handle, or a vote is already in progress. + */ +native bool:VoteMenu(Handle:menu, clients[], numClients, time); + +/** + * Sends a vote menu to all clients. See VoteMenu() for more information. + * + * @param menu Menu Handle. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false if this menu already has a vote session + * in progress. + * @error Invalid Handle. + */ +stock VoteMenuToAll(Handle:menu, time) +{ + new num = GetMaxClients(); + new total; + decl players[num]; + + for (new i=1; i<=num; i++) + { + if (!IsClientInGame(i)) + { + continue; + } + players[total++] = i; + } + + return VoteMenu(menu, players, total, time); +} +/** + * Callback for when a vote has ended and results are available. + * + * @param menu The menu being voted on. + * @param num_votes Number of votes tallied in total. + * @param num_clients Number of clients who could vote. + * @param client_info Array of clients. Use VOTEINFO_CLIENT_ defines. + * @param num_items Number of unique items that were selected. + * @param item_info Array of items, sorted by count. Use VOTEINFO_ITEM + * defines. + * @noreturn + */ +functag VoteHandler public(Handle:menu, + num_votes, + num_clients, + const client_info[][2], + num_items, + const item_info[][2]); + +/** + * Sets an advanced vote handling callback. If this callback is set, + * MenuAction_VoteEnd will not be called. + * + * @param menu Menu Handle. + * @param callback Callback function. + * @noreturn + * @error Invalid Handle or callback. + */ +native SetVoteResultCallback(Handle:menu, VoteHandler:callback); + +/** + * Returns the number of seconds you should "wait" before displaying + * a publicly invocable menu. This number is the time remaining until + * (last_vote + sm_vote_delay). + * + * @return Number of seconds to wait, or 0 for none. + */ +native CheckVoteDelay(); + +/** + * Returns a style's global Handle. + * + * @param style Menu Style. + * @return A Handle, or INVALID_HANDLE if not found or unusable. + */ +native Handle:GetMenuStyleHandle(MenuStyle:style); + +/** + * Creates a MenuPanel from a MenuStyle. Panels are used for drawing raw + * menus without any extra helper functions. The Handle must be closed + * with CloseHandle(). + * + * @param hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style. + * @return A new MenuPanel Handle. + * @error Invalid Handle other than INVALID_HANDLE. + */ +native Handle:CreatePanel(Handle:hStyle=INVALID_HANDLE); + +/** + * Creates a Menu from a MenuStyle. The Handle must be closed with + * CloseHandle(). + * + * @parma hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style. + * @param handler Function which will receive menu actions. + * @param actions Optionally set which actions to receive. Select, + * Cancel, and End will always be received regardless + * of whether they are set or not. They are also + * the only default actions. + * @return A new menu Handle. + * @error Invalid Handle other than INVALID_HANDLE. + */ +native Handle:CreateMenuEx(Handle:hStyle=INVALID_HANDLE, MenuHandler:handler, MenuAction:actions=MENU_ACTIONS_DEFAULT); + +/** + * Returns whether a client is viewing a menu. + * + * @param client Client index. + * @param hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style. + * @return A MenuSource value. + * @error Invalid Handle other than INVALID_HANDLE. + */ +native MenuSource:GetClientMenu(client, Handle:hStyle=INVALID_HANDLE); + +/** + * Cancels a menu on a client. This will only affect non-external menus. + * + * @param hstyle MenuStyle Handle, or INVALID_HANDLE to use the default style. + * @param client Client index. + * @param autoIgnore If true, no menus can be re-drawn on the client during + * the cancellation process. + * @return True if a menu was cancelled, false otherwise. + */ +native bool:CancelClientMenu(client, bool:autoIgnore=false, Handle:hStyle=INVALID_HANDLE); + +/** + * Returns a style's maximum items per page. + * + * @param hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style. + * @return Maximum items per page. + * @error Invalid Handle other than INVALID_HANDLE. + */ +native GetMaxPageItems(Handle:hStyle=INVALID_HANDLE); + +/** + * Returns a MenuPanel's parent style. + * + * @param panel A MenuPanel Handle. + * @return The MenuStyle Handle that created the panel. + * @error Invalid Handle. + */ +native Handle:GetPanelStyle(Handle:panel); + +/** + * Sets the panel's title. + * + * @param panel A MenuPanel Handle. + * @param title Text to set as the title. + * @param onlyIfEmpty If true, the title will only be set if no title is set. + * @noreturn + * @error Invalid Handle. + */ +native Handle:SetPanelTitle(Handle:panel, const String:text[], bool:onlyIfEmpty=false); + +/** + * Draws an item on a panel. If the item takes up a slot, the position + * is returned. + * + * @param panel A MenuPanel Handle. + * @param text Display text to use. If not a raw line, + * the style may automatically add color markup. + * No numbering or newlines are needed. + * @param style ITEMDRAW style flags. + * @return A slot position, or 0 if item was a rawline or could not be drawn. + * @error Invalid Handle. + */ +native DrawPanelItem(Handle:panel, const String:text[], style=ITEMDRAW_DEFAULT); + +/** + * Draws a raw line of text on a panel, without any markup other than a newline. + * + * @param panel A MenuPanel Handle, or INVALID_HANDLE if inside a + * MenuAction_DisplayItem callback. + * @param text Display text to use. + * @return True on success, false if raw lines are not supported. + * @error Invalid Handle. + */ +native DrawPanelText(Handle:panel, const String:text[]); + +/** + * Returns whether or not the given drawing flags are supported by + * the menu style. + * + * @param panel A MenuPanel Handle. + * @param style ITEMDRAW style flags. + * @return True if item is drawable, false otherwise. + * @error Invalid Handle. + */ +native CanPanelDrawFlags(Handle:panel, style); + +/** + * Sets the selectable key map of a panel. This is not supported by + * all styles (only by Radio, as of this writing). + * + * @param keys An integer where each bit N allows key + * N+1 to be selected. If no keys are selectable, + * then key 0 (bit 9) is automatically set. + * @return True if supported, false otherwise. + */ +native bool:SetPanelKeys(Handle:panel, keys); + +/** + * Sends a panel to a client. Unlike full menus, the handler + * function will only receive the following actions, both of + * which will have INVALID_HANDLE for a menu, and the client + * as param1. + * + * MenuAction_Select (param2 will be the key pressed) + * MenuAction_Cancel (param2 will be the reason) + * + * Also, if the menu fails to display, no callbacks will be called. + * + * @param panel A MenuPanel Handle. + * @param client A client to draw to. + * @param handler The MenuHandler function to catch actions with. + * @param time Time to hold the menu for. + * @return True on success, false on failure. + * @error Invalid Handle. + */ +native bool:SendPanelToClient(Handle:panel, client, MenuHandler:handler, time); + +/** + * @brief Returns the amount of text the menu can still hold. If this is + * limit is reached or overflowed, the text is silently truncated. + * + * Radio menus: Currently 511 characters (512 bytes). + * Valve menus: Currently -1 (no meaning). + * + * @param panel A MenuPanel Handle. + * @return Number of characters that the menu can still hold, + * or -1 if there is no known limit. + * @error Invalid Handle. + */ +native GetPanelTextRemaining(Handle:panel); + +/** + * @brief Returns the current key position. + * + * @param panel A MenuPanel Handle. + * @return Current key position starting at 1. + * @error Invalid Handle. + */ +native GetPanelCurrentKey(Handle:panel); + +/** + * @brief Sets the next key position. This cannot be used + * to traverse backwards. + * + * @param panel A MenuPanel Handle. + * @param key Key that is greater or equal to + * GetPanelCurrentKey(). + * @return True on success, false otherwise. + * @error Invalid Handle. + */ +native bool:SetPanelCurrentKey(Handle:panel, key); + +/** + * @brief Redraws menu text from inside a MenuAction_DisplayItem callback. + * + * @param text Menu text to draw. + * @return Item position; must be returned via the callback. + */ +native RedrawMenuItem(const String:text[]); + +/** + * This function is provided for legacy code only. Some older plugins may use + * network messages instead of the panel API. This function wraps the panel + * API for eased portability into the SourceMod menu system. + * + * This function is only usable with the Radio Menu style. You do not need to + * split up your menu into multiple packets; SourceMod will break the string + * up internally. + * + * @param client Client index. + * @param str Full menu string as would be passed over the network. + * @param time Time to hold the menu for. + * @param keys Selectable key bitstring. + * @param handler Optional handler function, with the same rules as + * SendPanelToClient(). + * @return True on success, false on failure. + * @error Invalid client index, or radio menus not supported. + */ +native bool:InternalShowMenu(client, const String:str[], time, keys=-1, MenuHandler:handler=MenuHandler:-1); + +/** + * Retrieves voting information from MenuAction_VoteEnd. + * + * @param param2 Second parameter of MenuAction_VoteEnd. + * @param winningVotes Number of votes received by the winning option. + * @param totalVotes Number of total votes received. + * @noreturn + */ +stock GetMenuVoteInfo(param2, &winningVotes, &totalVotes) +{ + winningVotes = param2 & 0xFFFF; + totalVotes = param2 >> 16; +} + +/** + * Quick stock to determine whether voting is allowed. This doesn't let you + * fine-tune a reason for not voting, so it's not recommended for lazily + * telling clients that voting isn't allowed. + * + * @return True if voting is allowed, false if voting is in progress + * or the cooldown is active. + */ +stock bool:IsNewVoteAllowed() +{ + if (IsVoteInProgress() || CheckVoteDelay() != 0) + { + return false; + } + + return true; +} diff --git a/src/include/profiler.inc b/src/include/profiler.inc new file mode 100644 index 0000000..a6f6c9e --- /dev/null +++ b/src/include/profiler.inc @@ -0,0 +1,77 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: profiler.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _profiler_included + #endinput +#endif +#define _profiler_included + +/** + * ONLY AVAILABLE ON WINDOWS RIGHT NOW K. + */ + +/** + * Creates a new profile object. The Handle must be freed + * using CloseHandle(). + * + * @return Handle to the profiler object. + */ +native Handle:CreateProfiler(); + +/** + * Starts profiling. + * + * @param prof Profiling object. + * @noreturn + * @error Invalid Handle. + */ +native StartProfiling(Handle:prof); + +/** + * Stops profiling. + * + * @param prof Profiling object. + * @noreturn + * @error Invalid Handle or profiling was never started. + */ +native StopProfiling(Handle:prof); + +/** + * Returns the amount of high-precision time in seconds + * that passed during the profiler's last start/stop + * cycle. + * + * @param prof Profiling object. + * @return Time elapsed in seconds. + * @error Invalid Handle. + */ +native Float:GetProfilerTime(Handle:prof); diff --git a/src/include/regex.inc b/src/include/regex.inc new file mode 100644 index 0000000..53ed2bd --- /dev/null +++ b/src/include/regex.inc @@ -0,0 +1,173 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: regex.inc 2086 2008-04-19 10:34:56Z pred $ + */ + +#if defined _regex_included + #endinput +#endif +#define _regex_included + +/** + * @section Flags for compiling regex expressions. These come directly from the + * pcre library and can be used in MatchRegex and CompileRegex. + */ +#define PCRE_CASELESS 0x00000001 /* Ignore Case */ +#define PCRE_MULTILINE 0x00000002 /* Multilines (affects ^ and $ so that they match the start/end of a line rather than matching the start/end of the string). */ +#define PCRE_DOTALL 0x00000004 /* Single line (affects . so that it matches any character, even new line characters). */ +#define PCRE_EXTENDED 0x00000008 /* Pattern extension (ignore whitespace and # comments). */ +#define PCRE_UNGREEDY 0x00000200 /* Invert greediness of quantifiers */ +#define PCRE_UTF8 0x00000800 /* Use UTF-8 Chars */ +#define PCRE_NO_UTF8_CHECK 0x00002000 /* Do not check the pattern for UTF-8 validity (only relevant if PCRE_UTF8 is set) */ + + +/** + * Regex expression error codes. + */ +enum RegexError +{ + REGEX_ERROR_NONE = 0, /* No error */ + REGEX_ERROR_NOMATCH = -1, /* No match was found */ + REGEX_ERROR_NULL = -2, + REGEX_ERROR_BADOPTION = -3, + REGEX_ERROR_BADMAGIC = -4, + REGEX_ERROR_UNKNOWN_OPCODE = -5, + REGEX_ERROR_NOMEMORY = -6, + REGEX_ERROR_NOSUBSTRING = -7, + REGEX_ERROR_MATCHLIMIT = -8, + REGEX_ERROR_CALLOUT = -9, /* Never used by PCRE itself */ + REGEX_ERROR_BADUTF8 = -10, + REGEX_ERROR_BADUTF8_OFFSET = -11, + REGEX_ERROR_PARTIAL = -12, + REGEX_ERROR_BADPARTIAL = -13, + REGEX_ERROR_INTERNAL = -14, + REGEX_ERROR_BADCOUNT = -15, + REGEX_ERROR_DFA_UITEM = -16, + REGEX_ERROR_DFA_UCOND = -17, + REGEX_ERROR_DFA_UMLIMIT = -18, + REGEX_ERROR_DFA_WSSIZE = -19, + REGEX_ERROR_DFA_RECURSE = -20, + REGEX_ERROR_RECURSIONLIMIT = -21, + REGEX_ERROR_NULLWSLIMIT = -22, /* No longer actually used */ + REGEX_ERROR_BADNEWLINE = -23 +}; + +/** + * Precompile a regular expression. Use this if you intend on using the + * same expression multiple times. Pass the regex handle returned here to + * MatchRegex to check for matches. + * + * @param pattern The regular expression pattern. + * @param flags General flags for the regular expression. + * @param error Error message encountered, if applicable. + * @param maxLen Maximum string length of the error buffer. + * @param errcode Regex type error code encountered, if applicable. + * @return Valid regex handle on success, INVALID_HANDLE on failure. + */ +native Handle:CompileRegex(const String:pattern[], flags = 0, String:error[]="", maxLen = 0, &RegexError:errcode = REGEX_ERROR_NONE); + +/** + * Matches a string against a pre-compiled regular expression pattern. + * + * @param str The string to check. + * @param regex Regex Handle from CompileRegex() + * @param ret Error code, if applicable. + * @return Number of substrings found or -1 on failure. + * + * @note Use the regex handle passed to this function to extract + * matches with GetRegexSubString(). + */ +native MatchRegex(Handle:regex, const String:str[], &RegexError:ret = REGEX_ERROR_NONE); + +/** + * Returns a matched substring from a regex handle. + * Substring ids start at 0 and end at substrings-1, where substrings is the number returned + * by MatchRegex + * + * @param regex The regex handle to extract data from. + * @param str_id The index of the expression to get - starts at 0, and ends at substrings - 1. + * @param buffer The buffer to set to the matching substring. + * @param maxLen The maximum string length of the buffer. + * @return True if a substring was found, False on fail/error + */ +native bool:GetRegexSubString(Handle:regex, str_id, String:buffer[], maxlen); + +/** + * Matches a string against a regular expression pattern. + * + * @note If you intend on using the same regular expression pattern + * multiple times, consider using CompileRegex and MatchRegex + * instead of making this function reparse the expression each time. + * + * @param str The string to check. + * @param pattern The regular expression pattern. + * @param flags General flags for the regular expression. + * @param error Error message, if applicable. + * @param maxLen Maximum length of the error buffer. + * @return Number of substrings found or -1 on failure. + */ +stock SimpleRegexMatch(const String:str[], const String:pattern[], flags = 0, String:error[]="", maxLen = 0) +{ + new Handle:regex = CompileRegex(pattern, flags, error, maxLen); + + if (regex == INVALID_HANDLE) + { + return -1; + } + + new substrings = MatchRegex(regex, str); + + CloseHandle(regex); + + return substrings; +} + +/** + * @endsection + */ + +/** + * Do not edit below this line! + */ +public Extension:__ext_regex = +{ + name = "Regex Extension", + file = "regex.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; diff --git a/src/include/sdktools.inc b/src/include/sdktools.inc new file mode 100644 index 0000000..176721d --- /dev/null +++ b/src/include/sdktools.inc @@ -0,0 +1,212 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools.inc 1990 2008-04-06 05:40:11Z dvander $ + */ + +#if defined _sdktools_included + #endinput +#endif +#define _sdktools_included + +#include +#include +#include +#if !defined SDKTOOLS_DISABLE_SOUNDAPI +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +enum SDKCallType +{ + SDKCall_Static, /**< Static call */ + SDKCall_Entity, /**< CBaseEntity call */ + SDKCall_Player, /**< CBasePlayer call */ + SDKCall_GameRules, /**< CGameRules call */ + SDKCall_EntityList, /**< CGlobalEntityList call */ +}; + +enum SDKLibrary +{ + SDKLibrary_Server, /**< server.dll/server_i486.so */ + SDKLibrary_Engine, /**< engine.dll/engine_*.so */ +}; + +enum SDKFuncConfSource +{ + SDKConf_Virtual = 0, /**< Read a virtual index from the Offsets section */ + SDKConf_Signature = 1, /**< Read a signature from the Signatures section */ +}; + +enum SDKType +{ + SDKType_CBaseEntity, /**< CBaseEntity (always as pointer) */ + SDKType_CBasePlayer, /**< CBasePlayer (always as pointer) */ + SDKType_Vector, /**< Vector (pointer, byval, or byref) */ + SDKType_QAngle, /**< QAngles (pointer, byval, or byref) */ + SDKType_PlainOldData, /**< Integer/generic data <=32bit (any) */ + SDKType_Float, /**< Float (any) */ + SDKType_Edict, /**< edict_t (always as pointer) */ + SDKType_String, /**< NULL-terminated string (always as pointer) */ + SDKType_Bool, /**< Boolean (any) */ +}; + +enum SDKPassMethod +{ + SDKPass_Pointer, /**< Pass as a pointer */ + SDKPass_Plain, /**< Pass as plain data */ + SDKPass_ByValue, /**< Pass an object by value */ + SDKPass_ByRef, /**< Pass an object by reference */ +}; + +#define VDECODE_FLAG_ALLOWNULL (1<<0) /**< Allow NULL for pointers */ +#define VDECODE_FLAG_ALLOWNOTINGAME (1<<1) /**< Allow players not in game */ +#define VDECODE_FLAG_ALLOWWORLD (1<<2) /**< Allow World entity */ +#define VDECODE_FLAG_BYREF (1<<3) /**< Floats/ints by reference */ + +#define VENCODE_FLAG_COPYBACK (1<<0) /**< Copy back data once done */ + +/** + * Starts the preparation of an SDK call. + * + * @param type Type of function call this will be. + * @noreturn + */ +native StartPrepSDKCall(SDKCallType:type); + +/** + * Sets the virtual index of the SDK call if it is virtual. + * + * @param vtblidx Virtual table index. + * @noreturn + */ +native PrepSDKCall_SetVirtual(vtblidx); + +/** + * Finds an address in a library and sets it as the address to use for the SDK call. + * + * @param lib Library to use. + * @param signature Binary data to search for in the library. If it starts with '@', + * the bytes parameter is ignored and the signature is interpreted + * as a symbol lookup in the library. + * @param bytes Number of bytes in the binary search string. + * @return True on success, false if nothing was found. + */ +native bool:PrepSDKCall_SetSignature(SDKLibrary:lib, const String:signature[], bytes); + +/** + * Finds an address or virtual function index in a GameConfig file and sets it as + * the calling information for the SDK call. + * + * @param gameconf GameConfig Handle, or INVALID_HANDLE to use sdktools.games.txt. + * @param source Whether to look in Offsets or Signatures. + * @param name Name of the property to find. + * @return True on success, false if nothing was found. + */ +native bool:PrepSDKCall_SetFromConf(Handle:gameconf, SDKFuncConfSource:source, const String:name[]); + +/** + * Sets the return information of an SDK call. Do not call this if there is no return data. + * This must be called if there is a return value (i.e. it is not necessarily safe to ignore + * the data). + * + * @param type Data type to convert to/from. + * @param pass How the data is passed in C++. + * @param decflags Flags on decoding from the plugin to C++. + * @param encflags Flags on encoding from C++ to the plugin. + * @noreturn + */ +native PrepSDKCall_SetReturnInfo(SDKType:type, SDKPassMethod:pass, decflags=0, encflags=0); + +/** + * Adds a parameter to the calling convention. This should be called in normal ascending order. + * + * @param type Data type to convert to/from. + * @param pass How the data is passed in C++. + * @param decflags Flags on decoding from the plugin to C++. + * @param encflags Flags on encoding from C++ to the plugin. + * @noreturn + */ +native PrepSDKCall_AddParameter(SDKType:type, SDKPassMethod:pass, decflags=0, encflags=0); + +/** + * Finalizes an SDK call preparation and returns the resultant Handle. + * + * @return A new SDKCall Handle on success, or INVALID_HANDLE on failure. + */ +native Handle:EndPrepSDKCall(); + +/** + * Calls an SDK function with the given parameters. + * + * If the call type is Entity or Player, the index MUST ALWAYS be the FIRST parameter passed. + * If the call type is GameRules, then nothing special needs to be passed. + * If the return value is a Vector or QAngles, the SECOND parameter must be a Float[3]. + * If the return value is a string, the THIRD parameter must be a String buffer, and the + * FOURTH parameter must be the maximum length. + * All parameters must be passed after the above is followed. Failure to follow these + * rules will result in crashes or wildly unexpected behavior! + * + * If the return value is a float or integer, the return value will be this value. + * If the return value is a CBaseEntity, CBasePlayer, or edict, the return value will + * always be the entity index, or -1 for NULL. + * + * @param call SDKCall Handle. + * @param ... Call Parameters. + * @return Simple return value, if any. + * @error Invalid Handle or internal decoding error. + */ +native any:SDKCall(Handle:call, any:...); + +#include + +/** + * Do not edit below this line! + */ +public Extension:__ext_sdktools = +{ + name = "SDKTools", + file = "sdktools.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; diff --git a/src/include/sdktools_engine.inc b/src/include/sdktools_engine.inc new file mode 100644 index 0000000..1c44dae --- /dev/null +++ b/src/include/sdktools_engine.inc @@ -0,0 +1,69 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_engine.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _sdktools_engine_included + #endinput +#endif +#define _sdktools_engine_included + +#define MAX_LIGHTSTYLES 64 + +/** + * Sets a client's "viewing entity." + * + * @param client Client index. + * @param entity Entity index. + * @noreturn + * @error Invalid client or entity, lack of mod support, or client not in + * game. + */ +native SetClientViewEntity(client, entity); + +/** + * Sets a light style. + * + * @param style Light style (from 0 to MAX_LIGHTSTYLES-1) + * @param value Light value string (see world.cpp/light.cpp in dlls) + * @noreturn + * @error Light style index is out of range. + */ +native SetLightStyle(style, const String:value[]); + +/** + * Returns the client's eye position. + * + * @param client Player's index. + * @param pos Destination vector to store the client's eye position. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native GetClientEyePosition(client, Float:pos[3]); diff --git a/src/include/sdktools_entinput.inc b/src/include/sdktools_entinput.inc new file mode 100644 index 0000000..ad66d10 --- /dev/null +++ b/src/include/sdktools_entinput.inc @@ -0,0 +1,116 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_entinput.inc 1504 2007-09-28 19:56:19Z faluco $ + */ + +#if defined _sdktools_entinput_included + #endinput +#endif +#define _sdktools_entinput_included + +/** + * Invokes a named input method on an entity. + * + * After completion (successful or not), the current global variant is re-initalized. + * + * @param dest Destination entity index. + * @param input Input action. + * @param activator Entity index which initiated the sequence of actions (-1 for a NULL entity). + * @param caller Entity index from which this event is sent (-1 for a NULL entity). + * @param outputid Unknown. + * @return True if successful otherwise false. + * @error Invalid entity index or no mod support. + */ +native bool:AcceptEntityInput(dest, const String:input[], activator=-1, caller=-1, outputid=0); + +/** + * Sets a bool value in the global variant object. + * + * @param val Input value. + * @noreturn + */ +native SetVariantBool(bool:val); + +/** + * Sets a string in the global variant object. + * + * @param str Input string. + * @noreturn + */ +native SetVariantString(const String:str[]); + +/** + * Sets an integer value in the global variant object. + * + * @param val Input value. + * @noreturn + */ +native SetVariantInt(val); + +/** + * Sets a floating point value in the global variant object. + * + * @param val Input value. + * @noreturn + */ +native SetVariantFloat(Float:val); + +/** + * Sets a 3D vector in the global variant object. + * + * @param vec Input vector. + * @noreturn + */ +native SetVariantVector3D(const Float:vec[3]); + +/** + * Sets a 3D position vector in the global variant object. + * + * @param vec Input position vector. + * @noreturn + */ +native SetVariantPosVector3D(const Float:vec[3]); + +/** + * Sets a color in the global variant object. + * + * @param color Input color. + * @noreturn + */ +native SetVariantColor(const color[4]); + +/** + * Sets an entity in the global variant object. + * + * @param Entity index. + * @noreturn + * @error Invalid entity index. + */ +native SetVariantEntity(entity); diff --git a/src/include/sdktools_entoutput.inc b/src/include/sdktools_entoutput.inc new file mode 100644 index 0000000..350ae7f --- /dev/null +++ b/src/include/sdktools_entoutput.inc @@ -0,0 +1,91 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_entoutput.inc 1990 2008-04-06 05:40:11Z dvander $ + */ + +#if defined _sdktools_entoutput_included + #endinput +#endif +#define _sdktools_entoutput_included + +/** + * Called when an entity output is fired. + * + * @param output Name of the output that fired. + * @param caller Entity index of the caller. + * @param activator Entity index of the activator. + * @param delay Delay in seconds? before the event gets fired. + */ +functag EntityOutput public(const String:output[], caller, activator, Float:delay); + +/** + * Add an entity output hook on a entity classname + * + * @param classname The classname to hook. + * @param output The output name to hook. + * @param callback An EntityOutput function pointer. + * @noreturn + * @error Entity Outputs disabled. + */ +native HookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback); + +/** + * Remove an entity output hook. + * @param classname The classname to hook. + * @param output The output name to hook. + * @param callback An EntityOutput function pointer. + * @return True on success, false if no valid hook was found. + * @error Entity Outputs disabled. + */ +native bool:UnhookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback); + +/** + * Add an entity output hook on a single entity instance + * + * @param entity The entity on which to add a hook. + * @param output The output name to hook. + * @param callback An EntityOutput function pointer. + * @param once Only fire this hook once and then remove itself. + * @noreturn + * @error Entity Outputs disabled or Invalid Entity index. + */ +native HookSingleEntityOutput(entity, const String:output[], EntityOutput:callback , bool:once=false); + +/** + * Remove a single entity output hook. + * + * @param entity The entity on which to remove the hook. + * @param output The output name to hook. + * @param callback An EntityOutput function pointer. + * @return True on success, false if no valid hook was found. + * @error Entity Outputs disabled or Invalid Entity index. + */ +native bool:UnhookSingleEntityOutput(entity, const String:output[], EntityOutput:callback); + diff --git a/src/include/sdktools_functions.inc b/src/include/sdktools_functions.inc new file mode 100644 index 0000000..ccb65bb --- /dev/null +++ b/src/include/sdktools_functions.inc @@ -0,0 +1,310 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_functions.inc 2069 2008-04-16 23:29:37Z dvander $ + */ + +#if defined _sdktools_functions_included + #endinput +#endif +#define _sdktools_functions_included + +/** + * Removes a player's item. + * + * @param client Client index. + * @param item CBaseCombatWeapon entity index. + * @return True on success, false otherwise. + * @error Invalid client or entity, lack of mod support, or client not in + * game. + */ +native bool:RemovePlayerItem(client, item); + +/** + * Gives a named item to a player. + * + * @param client Client index. + * @param item Item classname (such as weapon_ak47). + * @param iSubType Unknown. + * @return Entity index on success, or -1 on failure. + * @error Invalid client or client not in game, or lack of mod support. + */ +native GivePlayerItem(client, const String:item[], iSubType=0); + +/** + * Returns the weapon in a player's slot. + * + * @param client Client index. + * @param slot Slot index (mod specific). + * @return Entity index on success, -1 if no weapon existed. + * @error Invalid client or client not in game, or lack of mod support. + */ +native GetPlayerWeaponSlot(client, slot); + +/** + * Ignites an entity on fire. + * + * @param entity Entity index. + * @param time Number of seconds to set on fire. + * @param npc True to only affect NPCs. + * @param size Unknown. + * @param level Unknown. + * @noreturn + * @error Invalid entity or client not in game, or lack of mod support. + */ +native IgniteEntity(entity, Float:time, bool:npc=false, Float:size=0.0, bool:level=false); + +/** + * Extinguishes a player that is on fire. + * + * @param entity Entity index. + * @noreturn + * @error Invalid entity or client not in game, or lack of mod support. + */ +native ExtinguishEntity(client); + +/** + * Teleports an entity. + * + * @param entity Client index. + * @param origin New origin, or NULL_VECTOR for no change. + * @param angles New angles, or NULL_VECTOR for no change. + * @param velocity New velocity, or NULL_VECTOR for no change. + * @noreturn + * @error Invalid entity or client not in game, or lack of mod support. + */ +native TeleportEntity(entity, const Float:origin[3], const Float:angles[3], const Float:velocity[3]); + +/** + * Forces a player to commit suicide. + * + * @param client Client index. + * @noreturn + * @error Invalid client or client not in game, or lack of mod support. + */ +native ForcePlayerSuicide(client); + +/** + * Slaps a player in a random direction. + * + * @param client Client index. + * @param health Health to subtract. + * @param sound False to disable the sound effects. + * @noreturn + * @error Invalid client or client not in game, or lack of mod support. + */ +native SlapPlayer(client, health=5, bool:sound=true); + +/** + * Searches for an entity by classname. + * + * @param startEnt The entity index after which to begin searching from. + * Use -1 to start from the first entity. + * @param classname Classname of the entity to find. + * @return Entity index >= 0 if found, -1 otherwise. + * @error Lack of mod support. + */ +native FindEntityByClassname(startEnt, const String:classname[]); + +/** + * Returns the client's eye angles. + * + * @param client Player's index. + * @param ang Destination vector to store the client's eye angles. + * @return True on success, false on failure. + * @error Invalid client index, client not in game, or no mod support. + */ +native bool:GetClientEyeAngles(client, Float:ang[3]); + +/** + * Creates an entity by string name, but does not spawn it (see DispatchSpawn). + * If ForceEdictIndex is not -1, then it will use the edict by that index. If the index is + * invalid or there is already an edict using that index, it will error out. + * + * @param classname Entity classname. + * @param ForceEdictIndex Edict index used by the created entity. + * @return Entity index on success, or -1 on failure. + * @error Invalid edict index, or no mod support. + */ +native CreateEntityByName(const String:classname[], ForceEdictIndex=-1); + +/** + * Spawns an entity into the game. + * + * @param entity Entity index of the created entity. + * @return True on success, false otherwise. + * @error Invalid entity index, or no mod support. + */ +native bool:DispatchSpawn(entity); + +/** + * Dispatches a KeyValue into given entity using a string value. + * + * @param entity Destination entity index. + * @param keyName Name of the key. + * @param value String value. + * @return True on success, false otherwise. + * @error Invalid entity index, or no mod support. + */ +native bool:DispatchKeyValue(entity, const String:keyName[], const String:value[]); + +/** + * Dispatches a KeyValue into given entity using a floating point value. + * + * @param entity Destination entity index. + * @param keyName Name of the key. + * @param value Floating point value. + * @return True on success, false otherwise. + * @error Invalid entity index, or no mod support. + */ +native bool:DispatchKeyValueFloat(entity, const String:keyName[], Float:value); + +/** + * Dispatches a KeyValue into given entity using a vector value. + * + * @param entity Destination entity index. + * @param keyName Name of the key. + * @param vec Vector value. + * @return True on success, false otherwise. + * @error Invalid entity index, or no mod support. + */ +native bool:DispatchKeyValueVector(entity, const String:keyName[], const Float:vector[3]); + +/** + * Returns the entity a client is aiming at. + * + * @param client Client performing the aiming. + * @param only_clients True to exclude all entities but clients. + * @return Entity index being aimed at. + * -1 if no entity is being aimed at. + * -2 if the function is not supported. + * @error Invalid client index or client not in game. + */ +native GetClientAimTarget(client, bool:only_clients=true); + +/** + * Returns the total number of teams in a game. + * Note: This native should not be called before OnMapStart. + * + * @return Total number of teams. + */ +native GetTeamCount(); + +/** + * Retrieves the team name based on a team index. + * Note: This native should not be called before OnMapStart. + * + * @param index Team index. + * @param name Buffer to store string in. + * @param maxlength Maximum length of string buffer. + * @noreturn + * @error Invalid team index. + */ +native GetTeamName(index, String:name[], maxlength); + +/** + * Returns the score of a team based on a team index. + * Note: This native should not be called before OnMapStart. + * + * @param index Team index. + * @return Score. + * @error Invalid team index. + */ +native GetTeamScore(index); + +/** + * Sets the score of a team based on a team index. + * Note: This native should not be called before OnMapStart. + * + * @param index Team index. + * @param value New score value. + * @return Score. + * @error Invalid team index. + */ +native SetTeamScore(index, value); + +/** + * Retrieves the number of players in a certain team. + * Note: This native should not be called before OnMapStart. + * + * @param index Team index. + * @return Number of players in the team. + * @error Invalid team index. + */ +native GetTeamClientCount(index); + +/** + * Sets the model to a given entity. + * + * @param entity Entity index. + * @param model Model name. + * @noreturn + * @error Invalid entity index, or no mod support. + */ +native SetEntityModel(entity, const String:model[]); + +/** + * Retrieves the decal file name associated to a given client. + * + * @param client Player's index. + * @param hex Buffer to store the logo filename. + * @param maxlength Maximum length of string buffer. + * @return True on success, otherwise false. + * @error Invalid client or client not in game. + */ +native bool:GetPlayerDecalFile(client, String:hex[], maxlength); + +/** + * Returns the average server network traffic in bytes/sec. + * + * @param in Buffer to store the input traffic velocity. + * @param out Buffer to store the output traffic velocity. + * @noreturn + */ +native GetServerNetStats(&Float:in, &Float:out); + +/** + * Equip's a player's weapon. + * + * @param client Client index. + * @param item CBaseCombatWeapon entity index. + * @noreturn + * @error Invalid client or entity, lack of mod support, or client not in + * game. + */ +native EquipPlayerWeapon(client, weapon); + +/** + * Activates an entity (CBaseAnimating::Activate) + * + * @param entity Entity index. + * @noreturn + * @error Invalid entity or lack of mod support. + */ +native ActivateEntity(entity); diff --git a/src/include/sdktools_sound.inc b/src/include/sdktools_sound.inc new file mode 100644 index 0000000..5688325 --- /dev/null +++ b/src/include/sdktools_sound.inc @@ -0,0 +1,442 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_sound.inc 1934 2008-03-12 05:21:56Z dvander $ + */ + +#if defined _sdktools_sound_included + #endinput +#endif +#define _sdktools_sound_included + +/** + * Sound should be from the target client. + */ +#define SOUND_FROM_PLAYER -2 + +/** + * Sound should be from the listen server player. + */ +#define SOUND_FROM_LOCAL_PLAYER -1 + +/** + * Sound is from the world. + */ +#define SOUND_FROM_WORLD 0 + +/** + * Sound channels. + */ +enum +{ + SNDCHAN_REPLACE = -1, /**< Unknown */ + SNDCHAN_AUTO = 0, /**< Auto */ + SNDCHAN_WEAPON = 1, /**< Weapons */ + SNDCHAN_VOICE = 2, /**< Voices */ + SNDCHAN_ITEM = 3, /**< Items */ + SNDCHAN_BODY = 4, /**< Player? */ + SNDCHAN_STREAM = 5, /**< "Stream channel from the static or dynamic area" */ + SNDCHAN_STATIC = 6, /**< "Stream channel from the static area" */ + SNDCHAN_VOICE_BASE = 7, /**< "Channel for network voice data" */ + SNDCHAN_USER_BASE = 135 /**< Anything >= this is allocated to game code */ +}; + +/** + * Sound flags for the sound emitter system. + */ +enum +{ + SND_NOFLAGS= 0, /**< Nothing */ + SND_CHANGEVOL = 1, /**< Change sound volume */ + SND_CHANGEPITCH = 2, /**< Change sound pitch */ + SND_STOP = 3, /**< Stop the sound */ + SND_SPAWNING = 4, /**< Used in some cases for ambients */ + SND_DELAY = 5, /**< Sound has an initial delay */ + SND_STOPLOOPING = 6, /**< Stop looping all sounds on the entity */ + SND_SPEAKER = 7, /**< Being played by a mic through a speaker */ + SND_SHOULDPAUSE = 8, /**< Pause if game is paused */ +}; + +/** + * Various predefined sound levels in dB. + */ +enum +{ + SNDLEVEL_NONE = 0, /**< None */ + SNDLEVEL_RUSTLE = 20, /**< Rustling leaves */ + SNDLEVEL_WHISPER = 25, /**< Whispering */ + SNDLEVEL_LIBRARY = 30, /**< In a library */ + SNDLEVEL_FRIDGE = 45, /**< Refridgerator */ + SNDLEVEL_HOME = 50, /**< Average home (3.9 attn) */ + SNDLEVEL_CONVO = 60, /**< Normal conversation (2.0 attn) */ + SNDLEVEL_DRYER = 60, /**< Clothes dryer */ + SNDLEVEL_DISHWASHER = 65, /**< Dishwasher/washing machine (1.5 attn) */ + SNDLEVEL_CAR = 70, /**< Car or vacuum cleaner (1.0 attn) */ + SNDLEVEL_NORMAL = 75, /**< Normal sound level */ + SNDLEVEL_TRAFFIC = 75, /**< Busy traffic (0.8 attn) */ + SNDLEVEL_MINIBIKE = 80, /**< Mini-bike, alarm clock (0.7 attn) */ + SNDLEVEL_SCREAMING = 90, /**< Screaming child (0.5 attn) */ + SNDLEVEL_TRAIN = 100, /**< Subway train, pneumatic drill (0.4 attn) */ + SNDLEVEL_HELICOPTER = 105, /**< Helicopter */ + SNDLEVEL_SNOWMOBILE = 110, /**< Snow mobile */ + SNDLEVEL_AIRCRAFT = 120, /**< Auto horn, aircraft */ + SNDLEVEL_RAIDSIREN = 130, /**< Air raid siren */ + SNDLEVEL_GUNFIRE = 140, /**< Gunshot, jet engine (0.27 attn) */ + SNDLEVEL_ROCKET = 180, /**< Rocket launching (0.2 attn) */ +}; + +#define SNDVOL_NORMAL 1.0 /**< Normal volume */ +#define SNDPITCH_NORMAL 100 /**< Normal pitch */ +#define SNDPITCH_LOW 95 /**< A low pitch */ +#define SNDPITCH_HIGH 120 /**< A high pitch */ +#define SNDATTN_NONE 0.0 /**< No attenuation */ +#define SNDATTN_NORMAL 0.8 /**< Normal attenuation */ +#define SNDATTN_STATIC 1.25 /**< Static attenuation? */ +#define SNDATTN_RICOCHET 1.5 /**< Ricochet effect */ +#define SNDATTN_IDLE 2.0 /**< Idle attenuation? */ + +/** + * Prefetches a sound. + * + * @param name Sound file name relative to the "sounds" folder. + * @noreturn + */ +native PrefetchSound(const String:nane[]); + +/** + * This function is not known to work, and may crash. You should + * not use it. It is provided for backwards compatibility only. + * + * @param name Sound file name relative to the "sounds" folder. + * @return Duration in seconds. + */ +#pragma deprecated Does not work, may crash. +native Float:GetSoundDuration(const String:name[]); + +/** + * Emits an ambient sound. + * + * @param name Sound file name relative to the "sounds" folder. + * @param pos Origin of sound. + * @param entity Entity index to associate sound with. + * @param level Sound level (from 0 to 255). + * @param flags Sound flags. + * @param vol Volume (from 0.0 to 1.0). + * @param pitch Pitch (from 0 to 255). + * @param delay Play delay. + * @noreturn + */ +native EmitAmbientSound(const String:name[], + const Float:pos[3], + entity = SOUND_FROM_WORLD, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:vol = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + Float:delay = 0.0); + +/** + * Fades a client's volume level toward silence or a given percentage. + * + * @param client Client index. + * @param percent Fade percentage. + * @param outtime Fade out time, in seconds. + * @param holdtime Hold time, in seconds. + * @param intime Fade in time, in seconds. + * @noreturn + * @error Invalid client index or client not in game. + */ +native FadeClientVolume(client, Float:percent, Float:outtime, Float:holdtime, Float:intime); + +/** + * Stops a sound. + * + * @param entity Entity index. + * @param channel Channel number. + * @param name Sound file name relative to the "sounds" folder. + * @noreturn + */ +native StopSound(entity, channel, const String:name[]); + +/** + * Emits a sound to a list of clients. + * + * @param clients Array of client indexes. + * @param numClients Number of clients in the array. + * @param sample Sound file name relative to the "sounds" folder. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @param ... Optional list of Float[3] arrays to specify additional origins. + * @noreturn + * @error Invalid client index. + */ +native EmitSound(const clients[], + numClients, + const String:sample[], + entity = SOUND_FROM_PLAYER, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0, + any:...); + +/** + * Emits a sentence to a list of clients. + * + * @param clients Array of client indexes. + * @param numClients Number of clients in the array. + * @param sentence Sentence index (from PrecacheSenteFile). + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @param ... Optional list of Float[3] arrays to specify additional origins. + * @noreturn + * @error Invalid client index. + */ +native EmitSentence(const clients[], + numClients, + sentence, + entity, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0, + any:...); + +/** + * Called when an ambient sound is about to be emitted to one or more clients. + * + * NOTICE: all parameters can be overwritten to modify the default behavior. + * + * @param sample Sound file name relative to the "sounds" folder. + * @param entity Entity index associated to the sound. + * @param volume Volume (from 0.0 to 1.0). + * @param level Sound level (from 0 to 255). + * @param pitch Pitch (from 0 to 255). + * @param pos Origin of sound. + * @param flags Sound flags. + * @param delay Play delay. + * @return Plugin_Continue to allow the sound to be played, Plugin_Stop to block it, + * Plugin_Changed when any parameter has been modified. + */ +functag AmbientSHook Action:public(String:sample[PLATFORM_MAX_PATH], &entity, &Float:volume, &level, &pitch, Float:pos[3], &flags, &Float:delay); + +/** + * Called when a sound is going to be emitted to one or more clients. + * NOTICE: all params can be overwritten to modify the default behaviour. + * + * @param clients Array of client indexes. + * @param numClients Number of clients in the array (modify this value if you add/remove elements from the client array). + * @param sample Sound file name relative to the "sounds" folder. + * @param entity Entity emitting the sound. + * @param channel Channel emitting the sound. + * @param volume Sound volume. + * @param level Sound level. + * @param pitch Sound pitch. + * @param flags Sound flags. + * @return Plugin_Continue to allow the sound to be played, Plugin_Stop to block it, + * Plugin_Changed when any parameter has been modified. + */ +functag NormalSHook Action:public(clients[64], &numClients, String:sample[PLATFORM_MAX_PATH], &entity, &channel, &Float:volume, &level, &pitch, &flags); + +/** + * Hooks all played ambient sounds. + * + * @param hook Function to use as a hook. + * @noreturn + * @error Invalid function hook. + */ +native AddAmbientSoundHook(AmbientSHook:hook); + +/** + * Hooks all played normal sounds. + * + * @param hook Function to use as a hook. + * @noreturn + * @error Invalid function hook. + */ +native AddNormalSoundHook(NormalSHook:hook); + +/** + * Unhooks all played ambient sounds. + * + * @param hook Function used for the hook. + * @noreturn + * @error Invalid function hook. + */ +native RemoveAmbientSoundHook(AmbientSHook:hook); + +/** + * Unhooks all played normal sounds. + * + * @param hook Function used for the hook. + * @noreturn + * @error Invalid function hook. + */ +native RemoveNormalSoundHook(NormalSHook:hook); + +/** + * Wrapper to emit sound to one client. + * + * @param client Client index. + * @param sample Sound file name relative to the "sounds" folder. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @noreturn + * @error Invalid client index. + */ +stock EmitSoundToClient(client, + const String:sample[], + entity = SOUND_FROM_PLAYER, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0) +{ + new clients[1]; + clients[0] = client; + /* Save some work for SDKTools and remove SOUND_FROM_PLAYER references */ + entity = (entity == SOUND_FROM_PLAYER) ? client : entity; + EmitSound(clients, 1, sample, entity, channel, + level, flags, volume, pitch, speakerentity, + origin, dir, updatePos, soundtime); +} + +/** + * Wrapper to emit sound to all clients. + * + * @param sample Sound file name relative to the "sounds" folder. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param level Sound level. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @noreturn + * @error Invalid client index. + */ +stock EmitSoundToAll(const String:sample[], + entity = SOUND_FROM_PLAYER, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0) +{ + new maxClients = GetMaxClients(); + new clients[maxClients]; + new total = 0; + + for (new i=1; i<=maxClients; i++) + { + if (IsClientInGame(i)) + { + clients[total++] = i; + } + } + + if (!total) + { + return; + } + + EmitSound(clients, total, sample, entity, channel, + level, flags, volume, pitch, speakerentity, + origin, dir, updatePos, soundtime); +} + +/** + * Converts an attenuation value to a sound level. + * This function is from the HL2SDK. + * + * @param attn Attenuation value. + * @return Integer sound level. + */ +stock ATTN_TO_SNDLEVEL(Float:attn) +{ + if (attn > 0.0) + { + return RoundFloat(50.0 + (20.0 / attn)); + } + return 0; +} diff --git a/src/include/sdktools_stocks.inc b/src/include/sdktools_stocks.inc new file mode 100644 index 0000000..ec25166 --- /dev/null +++ b/src/include/sdktools_stocks.inc @@ -0,0 +1,76 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_stocks.inc 1622 2007-10-21 00:16:46Z dvander $ + */ + +#if defined _sdktools_stocks_included + #endinput +#endif +#define _sdktools_stocks_included + +/** + * Given a partial team name, attempts to find a matching team. + * + * The search is performed case insensitively and only against the + * first N characters of the team names, where N is the number of + * characters in the search pattern. + * + * @param name Partial or full team name. + * @return A valid team index on success. + * -1 if no team matched. + * -2 if more than one team matched. + */ +stock FindTeamByName(const String:name[]) +{ + new name_len = strlen(name); + new num_teams = GetTeamCount(); + decl String:team_name[32]; + new found_team = -1 + + for (new i = 0; i < num_teams; i++) + { + GetTeamName(i, team_name, sizeof(team_name)); + + if (strncmp(team_name, name, name_len) == 0) + { + if (found_team >= 0) + { + return -2; + } + else + { + found_team = i; + } + } + } + + return found_team; +} + diff --git a/src/include/sdktools_stringtables.inc b/src/include/sdktools_stringtables.inc new file mode 100644 index 0000000..4eec397 --- /dev/null +++ b/src/include/sdktools_stringtables.inc @@ -0,0 +1,180 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_stringtables.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _sdktools_stringtables_included + #endinput +#endif +#define _sdktools_stringtables_included + +#define INVALID_STRING_TABLE -1 /**< An invalid string table index */ +#define INVALID_STRING_INDEX -1 /**< An invalid string index in a table */ + +/** + * Searches for a string table. + * + * @param name Name of string table to find. + * @return A string table index number if found, INVALID_STRING_TABLE otherwise. + */ +native FindStringTable(const String:name[]); + +/** + * Returns the number of string tables that currently exist. + * + * @return Number of string tables that currently exist. + */ +native GetNumStringTables(); + +/** + * Returns the number of strings that currently exist in a given string table. + * + * @param tableidx A string table index. + * @return Number of strings that currently exist. + * @error Invalid string table index. + */ +native GetStringTableNumStrings(tableidx); + +/** + * Returns the maximum number of strings that are allowed in a given string table. + * + * @param tableidx A string table index. + * @return Maximum number of strings allowed. + * @error Invalid string table index. + */ +native GetStringTableMaxStrings(tableidx); + +/** + * Retrieves the name of a string table. + * + * @param tableidx A string table index. + * @param name Buffer to store the name of the string table. + * @param maxlength Maximum length of string buffer. + * @return Number of bytes written to the buffer (UTF-8 safe). + * @error Invalid string table index. + */ +native GetStringTableName(tableidx, String:name[], maxlength); + +/** + * Searches for the index of a given string in a string table. + * + * @param tableidx A string table index. + * @param string String to find. + * @return String index if found, INVALID_STRING_INDEX otherwise. + * @error Invalid string table index. + */ +native FindStringIndex(tableidx, const String:str[]); + +/** + * Retrieves the string at a given index of a string table. + * + * @param tableidx A string table index. + * @param stringidx A string index. + * @param name Buffer to store the string value. + * @param maxlength Maximum length of string buffer. + * @return Number of bytes written to the buffer (UTF-8 safe). + * @error Invalid string table index or string index. + */ +native ReadStringTable(tableidx, stringIdx, String:str[], maxlength); + +/** + * Returns the length of the user data associated with a given string index. + * + * @param tableidx A string table index. + * @param stringidx A string index. + * @return Length of user data. This will be 0 if there is no user data. + * @error Invalid string table index or string index. + */ +native GetStringTableDataLength(tableidx, stringidx); + +/** + * Retrieves the user data associated with a given string index. + * + * @param tableidx A string table index. + * @param stringidx A string index. + * @param userdata Buffer to store the user data. This will be set to "" if there is no user data. + * @param maxlength Maximum length of string buffer. + * @return Number of bytes written to the buffer (UTF-8 safe). + * @error Invalid string table index or string index. + */ +native GetStringTableData(tableidx, stringIdx, String:userdata[], maxlength); + +/** + * Sets the user data associated with a given string index. + * + * @param tableidx A string table index. + * @param stringidx A string index. + * @param userdata User data string that will be set. + * @param length Length of user data string. This should include the null terminator. + * @return Number of bytes written to the buffer (UTF-8 safe). + * @error Invalid string table index or string index. + */ +native SetStringTableData(tableidx, stringIdx, const String:userdata[], length); + +/** + * Adds a string to a given string table. + * + * @param tableidx A string table index. + * @param string String to add. + * @param userdata An optional user data string. + * @param length Length of user data string. This should include the null terminator. + * If set to -1, then user data will be not be altered if the specified string + * already exists in the string table. + */ +native AddToStringTable(tableidx, const String:str[], const String:userdata[]="", length=-1); + +/** + * Locks or unlocks the network string tables. + * + * @param lock Determines whether network string tables should be locked. + * True means the tables should be locked for writing; false means unlocked. + * @return Previous lock state. + */ +native bool:LockStringTables(bool:lock); + +/** + * Adds a file to the downloadables network string table. + * This forces a client to download the file if they do not already have it. + * + * @param filename File that will be added to downloadables table. + */ +stock AddFileToDownloadsTable(const String:filename[]) +{ + static table = INVALID_STRING_TABLE; + + if (table == INVALID_STRING_TABLE) + { + table = FindStringTable("downloadables"); + } + + new bool:save = LockStringTables(false); + AddToStringTable(table, filename); + LockStringTables(save); +} diff --git a/src/include/sdktools_tempents.inc b/src/include/sdktools_tempents.inc new file mode 100644 index 0000000..acea043 --- /dev/null +++ b/src/include/sdktools_tempents.inc @@ -0,0 +1,229 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_tempents.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _sdktools_tempents_included + #endinput +#endif +#define _sdktools_tempents_included + +/** + * Called when a temp entity is going to be sent. + * + * @param te_name TE name. + * @param Players Array containing target player indexes. + * @param numClients Number of players in the array. + * @param delay Delay in seconds to send the TE. + * @return Plugin_Continue to allow the transmission of the TE, Plugin_Stop to block it. + */ +functag TEHook Action:public(const String:te_name[], const Players[], numClients, Float:delay); + +/** + * Hooks a temp entity. + * + * @param te_name TE name to hook. + * @param hook Function to use as a hook. + * @noreturn + * @error Temp Entity name not available or invalid function hook. + */ +native AddTempEntHook(const String:te_name[], TEHook:hook); + +/** + * Removes a temp entity hook. + * + * @param te_name TE name to unhook. + * @param hook Function used for the hook. + * @noreturn + * @error Temp Entity name not available or invalid function hook. + */ +native RemoveTempEntHook(const String:te_name[], TEHook:hook); + +/** + * Starts a temp entity transmission. + * + * @param te_name TE name. + * @noreturn + * @error Temp Entity name not available. + */ +native TE_Start(const String:te_name[]); + +/** + * Checks if a certain TE property exists. + * + * @param prop Property to use. + * @return True if the property exists, otherwise false. + */ +native bool:TE_IsValidProp(const String:prop[]); + +/** + * Sets an integer value in the current temp entity. + * + * @param prop Property to use. + * @param value Integer value to set. + * @noreturn + * @error Property not found. + */ +native TE_WriteNum(const String:prop[], value); + +/** + * Reads an integer value in the current temp entity. + * + * @param prop Property to use. + * @return Property value. + * @error Property not found. + */ +native TE_ReadNum(const String:prop[]); + +/** + * Sets a floating point number in the current temp entity. + * + * @param prop Property to use. + * @param value Floating point number to set. + * @noreturn + * @error Property not found. + */ +native TE_WriteFloat(const String:prop[], Float:value); + +/** + * Reads a floating point number in the current temp entity. + * + * @param prop Property to use. + * @noreturn Property value. + * @error Property not found. + */ +native Float:TE_ReadFloat(const String:prop[]); + +/** + * Sets a vector in the current temp entity. + * + * @param prop Property to use. + * @param vector Vector to set. + * @noreturn + * @error Property not found. + */ +native TE_WriteVector(const String:prop[], const Float:vector[3]); + +/** + * Reads a vector in the current temp entity. + * + * @param prop Property to use. + * @param vector Vector to read. + * @noreturn + * @error Property not found. + */ +native TE_ReadVector(const String:prop[], Float:vector[3]); + +/** + * Sets a QAngle in the current temp entity. + * + * @param prop Property to use. + * @param angles Angles to set. + * @return True on success, otherwise false. + * @error Property not found. + */ +native TE_WriteAngles(const String:prop[], const Float:angles[3]); + +/** + * Sets an array of floats in the current temp entity. + * + * @param prop Property to use. + * @param array Array of values to copy. + * @param arraySize Number of values to copy. + * @return True on success, otherwise false. + * @error Property not found. + */ +native TE_WriteFloatArray(const String:prop[], const Float:array[], arraySize); + +/** + * Sends the current temp entity to one or more clients. + * + * @param clients Array containing player indexes to broadcast to. + * @param numClients Number of players in the array. + * @param delay Delay in seconds to send the TE. + * @noreturn + * @error Invalid client index or client not in game. + */ +native TE_Send(clients[], numClients, Float:delay=0.0); + +/** + * Sets an encoded entity index in the current temp entity. + * (This is usually used for m_nStartEntity and m_nEndEntity). + * + * @param prop Property to use. + * @param value Value to set. + * @noreturn + * @error Property not found. + */ +stock TE_WriteEncodedEnt(const String:prop[], value) +{ + new encvalue = (value & 0x0FFF) | ((1 & 0xF)<<12); + return TE_WriteNum(prop, encvalue); +} + +/** + * Broadcasts the current temp entity to all clients. + * @note See TE_Start(). + * + * @param delay Delay in seconds to send the TE. + * @noreturn + */ +stock TE_SendToAll(Float:delay=0.0) +{ + new maxClients = GetMaxClients(); + new total = 0; + new clients[maxClients]; + for (new i=1; i<=maxClients; i++) + { + if (IsClientInGame(i)) + { + clients[total++] = i; + } + } + return TE_Send(clients, total, delay); +} + +/** + * Sends the current TE to only a client. + * @note See TE_Start(). + * + * @param client Client to send to. + * @param delay Delay in seconds to send the TE. + * @noreturn + * @error Invalid client index or client not in game. + */ +stock TE_SendToClient(client, Float:delay=0.0) +{ + new players[1]; + + players[0] = client; + + return TE_Send(players, 1, delay); +} diff --git a/src/include/sdktools_tempents_stocks.inc b/src/include/sdktools_tempents_stocks.inc new file mode 100644 index 0000000..6730cac --- /dev/null +++ b/src/include/sdktools_tempents_stocks.inc @@ -0,0 +1,458 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_tempents_stocks.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _te_stocks_included + #endinput +#endif +#define _te_stocks_included + +/** + * @section TE Explosion flags. + */ +#define TE_EXPLFLAG_NONE 0x0 /**< all flags clear makes default Half-Life explosion */ +#define TE_EXPLFLAG_NOADDITIVE 0x1 /**< sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) */ +#define TE_EXPLFLAG_NODLIGHTS 0x2 /**< do not render dynamic lights */ +#define TE_EXPLFLAG_NOSOUND 0x4 /**< do not play client explosion sound */ +#define TE_EXPLFLAG_NOPARTICLES 0x8 /**< do not draw particles */ +#define TE_EXPLFLAG_DRAWALPHA 0x10 /**< sprite will be drawn alpha */ +#define TE_EXPLFLAG_ROTATE 0x20 /**< rotate the sprite randomly */ +#define TE_EXPLFLAG_NOFIREBALL 0x40 /**< do not draw a fireball */ +#define TE_EXPLFLAG_NOFIREBALLSMOKE 0x80 /**< do not draw smoke with the fireball */ + +/** + * @endsection + */ + +/** + * @section TE Beam flags. + */ +#define FBEAM_STARTENTITY 0x00000001 +#define FBEAM_ENDENTITY 0x00000002 +#define FBEAM_FADEIN 0x00000004 +#define FBEAM_FADEOUT 0x00000008 +#define FBEAM_SINENOISE 0x00000010 +#define FBEAM_SOLID 0x00000020 +#define FBEAM_SHADEIN 0x00000040 +#define FBEAM_SHADEOUT 0x00000080 +#define FBEAM_ONLYNOISEONCE 0x00000100 /**< Only calculate our noise once */ +#define FBEAM_NOTILE 0x00000200 +#define FBEAM_USE_HITBOXES 0x00000400 /**< Attachment indices represent hitbox indices instead when this is set. */ +#define FBEAM_STARTVISIBLE 0x00000800 /**< Has this client actually seen this beam's start entity yet? */ +#define FBEAM_ENDVISIBLE 0x00001000 /**< Has this client actually seen this beam's end entity yet? */ +#define FBEAM_ISACTIVE 0x00002000 +#define FBEAM_FOREVER 0x00004000 +#define FBEAM_HALOBEAM 0x00008000 /**< When drawing a beam with a halo, don't ignore the segments and endwidth */ + +/** + * @endsection + */ + +/** + * Sets up a sparks effect. + * + * @param pos Position of the sparks. + * @param dir Direction of the sparks. + * @param Magnitude Sparks size. + * @param TrailLength Trail lenght of the sparks. + * @noreturn + */ +stock TE_SetupSparks(const Float:pos[3], const Float:dir[3], Magnitude, TrailLength) +{ + TE_Start("Sparks"); + TE_WriteVector("m_vecOrigin[0]", pos); + TE_WriteVector("m_vecDir", dir); + TE_WriteNum("m_nMagnitude", Magnitude); + TE_WriteNum("m_nTrailLength", TrailLength); +} + +/** + * Sets up a smoke effect. + * + * @param pos Position of the smoke. + * @param Model Precached model index. + * @param Scale Scale of the smoke. + * @param Framerate Frame rate of the smoke. + * @noreturn + */ +stock TE_SetupSmoke(const Float:pos[3], Model, Float:Scale, FrameRate) +{ + TE_Start("Smoke"); + TE_WriteVector("m_vecOrigin", pos); + TE_WriteNum("m_nModelIndex", Model); + TE_WriteFloat("m_fScale", Scale); + TE_WriteNum("m_nFrameRate", FrameRate); +} + +/** + * Sets up a dust cloud effect. + * + * @param pos Position of the dust. + * @param dir Direction of the dust. + * @param Size Dust cloud size. + * @param Speed Dust cloud speed. + * @noreturn + */ +stock TE_SetupDust(const Float:pos[3], const Float:dir[3], Float:Size, Float:Speed) +{ + TE_Start("Dust"); + TE_WriteVector("m_vecOrigin[0]", pos); + TE_WriteVector("m_vecDirection", dir); + TE_WriteFloat("m_flSize", Size); + TE_WriteFloat("m_flSpeed", Speed); +} + +/** + * Sets up a muzzle flash effect. + * + * @param pos Position of the muzzle flash. + * @param angles Rotation angles of the muzzle flash. + * @param Scale Scale of the muzzle flash. + * @param Type Muzzle flash type to render (Mod specific). + * @noreturn + */ +stock TE_SetupMuzzleFlash(const Float:pos[3], const Float:angles[3], Float:Scale, Type) +{ + TE_Start("MuzzleFlash"); + TE_WriteVector("m_vecOrigin", pos); + TE_WriteVector("m_vecAngles", angles); + TE_WriteFloat("m_flScale", Scale); + TE_WriteNum("m_nType", Type); +} + +/** + * Sets up a metal sparks effect. + * + * @param pos Position of the metal sparks. + * @param dir Direction of the metal sparks. + * @noreturn + */ +stock TE_SetupMetalSparks(const Float:pos[3], const Float:dir[3]) +{ + TE_Start("Metal Sparks"); + TE_WriteVector("m_vecPos", pos); + TE_WriteVector("m_vecDir", dir); +} + +/** + * Sets up an energy splash effect. + * + * @param pos Position of the energy splash. + * @param dir Direction of the energy splash. + * @param Explosive Makes the effect explosive. + * @noreturn + */ +stock TE_SetupEnergySplash(const Float:pos[3], const Float:dir[3], bool:Explosive) +{ + TE_Start("Energy Splash"); + TE_WriteVector("m_vecPos", pos); + TE_WriteVector("m_vecDir", dir); + TE_WriteNum("m_bExplosive", Explosive); +} + +/** + * Sets up an armor ricochet effect. + * + * @param pos Position of the armor ricochet. + * @param dir Directon of the armor ricochet. + * @noreturn + */ +stock TE_SetupArmorRicochet(const Float:pos[3], const Float:dir[3]) +{ + TE_Start("Armor Ricochet"); + TE_WriteVector("m_vecPos", pos); + TE_WriteVector("m_vecDir", dir); +} + +/** + * Sets up a glowing sprite effect. + * + * @param pos Position of the sprite. + * @param Model Precached model index. + * @param Life Time duration of the sprite. + * @param Size Sprite size. + * @param Brightness Sprite brightness. + * @noreturn + */ +stock TE_SetupGlowSprite(const Float:pos[3], Model, Float:Life, Float:Size, Brightness) +{ + TE_Start("GlowSprite"); + TE_WriteVector("m_vecOrigin", pos); + TE_WriteNum("m_nModelIndex", Model); + TE_WriteFloat("m_fScale", Size); + TE_WriteFloat("m_fLife", Life); + TE_WriteNum("m_nBrightness", Brightness); +} + +/** + * Sets up a explosion effect. + * + * @param pos Explosion position. + * @param Model Precached model index. + * @param Scale Explosion scale. + * @param Framerate Explosion frame rate. + * @param Flags Explosion flags. + * @param Radius Explosion radius. + * @param Magnitude Explosion size. + * @param normal Normal vector to the explosion. + * @param MaterialType Exploded material type. + * @noreturn + */ +stock TE_SetupExplosion(const Float:pos[3], Model, Float:Scale, Framerate, Flags, Radius, Magnitude, const Float:normal[3]={0.0, 0.0, 1.0}, MaterialType='C') +{ + TE_Start("Explosion"); + TE_WriteVector("m_vecOrigin[0]", pos); + TE_WriteVector("m_vecNormal", normal); + TE_WriteNum("m_nModelIndex", Model); + TE_WriteFloat("m_fScale", Scale); + TE_WriteNum("m_nFrameRate", Framerate); + TE_WriteNum("m_nFlags", Flags); + TE_WriteNum("m_nRadius", Radius); + TE_WriteNum("m_nMagnitude", Magnitude); + TE_WriteNum("m_chMaterialType", MaterialType); +} + +/** + * Sets up a blood sprite effect. + * + * @param pos Position of the sprite. + * @param dir Sprite direction. + * @param color Color array (r, g, b, a). + * @param Size Sprite size. + * @param SprayModel Precached model index. + * @param BloodDropModel Precached model index. + * @noreturn + */ +stock TE_SetupBloodSprite(const Float:pos[3], const Float:dir[3], const color[4], Size, SprayModel, BloodDropModel) +{ + TE_Start("Blood Sprite"); + TE_WriteVector("m_vecOrigin", pos); + TE_WriteVector("m_vecDirection", dir); + TE_WriteNum("r", color[0]); + TE_WriteNum("g", color[1]); + TE_WriteNum("b", color[2]); + TE_WriteNum("a", color[3]); + TE_WriteNum("m_nSize", Size); + TE_WriteNum("m_nSprayModel", SprayModel); + TE_WriteNum("m_nDropModel", BloodDropModel); +} + +/** + * Sets up a beam ring point effect. + * + * @param center Center position of the ring. + * @param Start_Radius Initial ring radius. + * @param End_Radius Final ring radius. + * @param ModelIndex Precached model index. + * @param HaloIndex Precached model index. + * @param StartFrame Initital frame to render. + * @param FrameRate Ring frame rate. + * @param Life Time duration of the ring. + * @param Width Beam width. + * @param Amplitude Beam amplitude. + * @param color Color array (r, g, b, a). + * @param Speed Speed of the beam. + * @param Flags Beam flags. + * @noreturn + */ +stock TE_SetupBeamRingPoint(const Float:center[3], Float:Start_Radius, Float:End_Radius, ModelIndex, HaloIndex, StartFrame, + FrameRate, Float:Life, Float:Width, Float:Amplitude, const Color[4], Speed, Flags) +{ + TE_Start("BeamRingPoint"); + TE_WriteVector("m_vecCenter", center); + TE_WriteFloat("m_flStartRadius", Start_Radius); + TE_WriteFloat("m_flEndRadius", End_Radius); + TE_WriteNum("m_nModelIndex", ModelIndex); + TE_WriteNum("m_nHaloIndex", HaloIndex); + TE_WriteNum("m_nStartFrame", StartFrame); + TE_WriteNum("m_nFrameRate", FrameRate); + TE_WriteFloat("m_fLife", Life); + TE_WriteFloat("m_fWidth", Width); + TE_WriteFloat("m_fEndWidth", Width); + TE_WriteFloat("m_fAmplitude", Amplitude); + TE_WriteNum("r", Color[0]); + TE_WriteNum("g", Color[1]); + TE_WriteNum("b", Color[2]); + TE_WriteNum("a", Color[3]); + TE_WriteNum("m_nSpeed", Speed); + TE_WriteNum("m_nFlags", Flags); + TE_WriteNum("m_nFadeLength", 0); +} + +/** + * Sets up a point to point beam effect. + * + * @param start Start position of the beam. + * @param end End position of the beam. + * @param ModelIndex Precached model index. + * @param HaloIndex Precached model index. + * @param StartFrame Initital frame to render. + * @param FrameRate Beam frame rate. + * @param Life Time duration of the beam. + * @param Width Initial beam width. + * @param EndWidth Final beam width. + * @param FadeLength Beam fade time duration. + * @param Amplitude Beam amplitude. + * @param color Color array (r, g, b, a). + * @param Speed Speed of the beam. + * @noreturn + */ +stock TE_SetupBeamPoints(const Float:start[3], const Float:end[3], ModelIndex, HaloIndex, StartFrame, FrameRate, Float:Life, + Float:Width, Float:EndWidth, FadeLength, Float:Amplitude, const Color[4], Speed) +{ + TE_Start("BeamPoints"); + TE_WriteVector("m_vecStartPoint", start); + TE_WriteVector("m_vecEndPoint", end); + TE_WriteNum("m_nModelIndex", ModelIndex); + TE_WriteNum("m_nHaloIndex", HaloIndex); + TE_WriteNum("m_nStartFrame", StartFrame); + TE_WriteNum("m_nFrameRate", FrameRate); + TE_WriteFloat("m_fLife", Life); + TE_WriteFloat("m_fWidth", Width); + TE_WriteFloat("m_fEndWidth", EndWidth); + TE_WriteFloat("m_fAmplitude", Amplitude); + TE_WriteNum("r", Color[0]); + TE_WriteNum("g", Color[1]); + TE_WriteNum("b", Color[2]); + TE_WriteNum("a", Color[3]); + TE_WriteNum("m_nSpeed", Speed); + TE_WriteNum("m_nFadeLength", FadeLength); +} + +/** + * Sets up an entity to entity laser effect. + * + * @param StartEntity Entity index from where the beam starts. + * @param EndEntity Entity index from where the beam ends. + * @param ModelIndex Precached model index. + * @param HaloIndex Precached model index. + * @param StartFrame Initital frame to render. + * @param FrameRate Beam frame rate. + * @param Life Time duration of the beam. + * @param Width Initial beam width. + * @param EndWidth Final beam width. + * @param FadeLength Beam fade time duration. + * @param Amplitude Beam amplitude. + * @param color Color array (r, g, b, a). + * @param Speed Speed of the beam. + * @noreturn + */ +stock TE_SetupBeamLaser(StartEntity, EndEntity, ModelIndex, HaloIndex, StartFrame, FrameRate, Float:Life, + Float:Width, Float:EndWidth, FadeLength, Float:Amplitude, const Color[4], Speed) +{ + TE_Start("BeamLaser"); + TE_WriteEncodedEnt("m_nStartEntity", StartEntity); + TE_WriteEncodedEnt("m_nEndEntity", EndEntity); + TE_WriteNum("m_nModelIndex", ModelIndex); + TE_WriteNum("m_nHaloIndex", HaloIndex); + TE_WriteNum("m_nStartFrame", StartFrame); + TE_WriteNum("m_nFrameRate", FrameRate); + TE_WriteFloat("m_fLife", Life); + TE_WriteFloat("m_fWidth", Width); + TE_WriteFloat("m_fEndWidth", EndWidth); + TE_WriteFloat("m_fAmplitude", Amplitude); + TE_WriteNum("r", Color[0]); + TE_WriteNum("g", Color[1]); + TE_WriteNum("b", Color[2]); + TE_WriteNum("a", Color[3]); + TE_WriteNum("m_nSpeed", Speed); + TE_WriteNum("m_nFadeLength", FadeLength); +} + +/** + * Sets up a beam ring effect. + * + * @param StartEntity Entity index from where the ring starts. + * @param EndEntity Entity index from where the ring ends. + * @param ModelIndex Precached model index. + * @param HaloIndex Precached model index. + * @param StartFrame Initital frame to render. + * @param FrameRate Ring frame rate. + * @param Life Time duration of the ring. + * @param Width Beam width. + * @param Amplitude Beam amplitude. + * @param color Color array (r, g, b, a). + * @param Speed Speed of the beam. + * @param Flags Beam flags. + * @noreturn + */ +stock TE_SetupBeamRing(StartEntity, EndEntity, ModelIndex, HaloIndex, StartFrame, FrameRate, Float:Life, Float:Width, Float:Amplitude, const Color[4], Speed, Flags) +{ + TE_Start("BeamRing"); + TE_WriteEncodedEnt("m_nStartEntity", StartEntity); + TE_WriteEncodedEnt("m_nEndEntity", EndEntity); + TE_WriteNum("m_nModelIndex", ModelIndex); + TE_WriteNum("m_nHaloIndex", HaloIndex); + TE_WriteNum("m_nStartFrame", StartFrame); + TE_WriteNum("m_nFrameRate", FrameRate); + TE_WriteFloat("m_fLife", Life); + TE_WriteFloat("m_fWidth", Width); + TE_WriteFloat("m_fEndWidth", Width); + TE_WriteFloat("m_fAmplitude", Amplitude); + TE_WriteNum("r", Color[0]); + TE_WriteNum("g", Color[1]); + TE_WriteNum("b", Color[2]); + TE_WriteNum("a", Color[3]); + TE_WriteNum("m_nSpeed", Speed); + TE_WriteNum("m_nFadeLength", 0); + TE_WriteNum("m_nFlags", Flags); +} + +/** + * Sets up a follow beam effect. + * + * @param EntIndex Entity index from where the beam starts. + * @param ModelIndex Precached model index. + * @param HaloIndex Precached model index. + * @param Life Time duration of the beam. + * @param Width Initial beam width. + * @param EndWidth Final beam width. + * @param FadeLength Beam fade time duration. + * @param color Color array (r, g, b, a). + * @noreturn + */ +stock TE_SetupBeamFollow(EntIndex, ModelIndex, HaloIndex, Float:Life, Float:Width, Float:EndWidth, FadeLength, const Color[4]) +{ + TE_Start("BeamFollow"); + TE_WriteEncodedEnt("m_iEntIndex", EntIndex); + TE_WriteNum("m_nModelIndex", ModelIndex); + TE_WriteNum("m_nHaloIndex", HaloIndex); + TE_WriteNum("m_nStartFrame", 0); + TE_WriteNum("m_nFrameRate", 0); + TE_WriteFloat("m_fLife", Life); + TE_WriteFloat("m_fWidth", Width); + TE_WriteFloat("m_fEndWidth", EndWidth); + TE_WriteNum("m_nFadeLength", FadeLength); + TE_WriteNum("r", Color[0]); + TE_WriteNum("g", Color[1]); + TE_WriteNum("b", Color[2]); + TE_WriteNum("a", Color[3]); +} diff --git a/src/include/sdktools_trace.inc b/src/include/sdktools_trace.inc new file mode 100644 index 0000000..06eb955 --- /dev/null +++ b/src/include/sdktools_trace.inc @@ -0,0 +1,367 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_trace.inc 2208 2008-05-29 03:50:36Z dvander $ + */ + +#if defined _sdktools_trace_included + #endinput +#endif +#define _sdktools_trace_included + +#define CONTENTS_EMPTY 0 /**< No contents. */ +#define CONTENTS_SOLID 0x1 /**< an eye is never valid in a solid . */ +#define CONTENTS_WINDOW 0x2 /**< translucent, but not watery (glass). */ +#define CONTENTS_AUX 0x4 +#define CONTENTS_GRATE 0x8 /**< alpha-tested "grate" textures. Bullets/sight pass through, but solids don't. */ +#define CONTENTS_SLIME 0x10 +#define CONTENTS_WATER 0x20 +#define CONTENTS_MIST 0x40 +#define CONTENTS_OPAQUE 0x80 /**< things that cannot be seen through (may be non-solid though). */ +#define LAST_VISIBLE_CONTENTS 0x80 +#define ALL_VISIBLE_CONTENTS (LAST_VISIBLE_CONTENTS | (LAST_VISIBLE_CONTENTS-1)) +#define CONTENTS_TESTFOGVOLUME 0x100 +#define CONTENTS_UNUSED5 0x200 +#define CONTENTS_UNUSED6 0x4000 +#define CONTENTS_TEAM1 0x800 /**< per team contents used to differentiate collisions. */ +#define CONTENTS_TEAM2 0x1000 /**< between players and objects on different teams. */ +#define CONTENTS_IGNORE_NODRAW_OPAQUE 0x2000 /**< ignore CONTENTS_OPAQUE on surfaces that have SURF_NODRAW. */ +#define CONTENTS_MOVEABLE 0x4000 /**< hits entities which are MOVETYPE_PUSH (doors, plats, etc) */ +#define CONTENTS_AREAPORTAL 0x8000 /**< remaining contents are non-visible, and don't eat brushes. */ +#define CONTENTS_PLAYERCLIP 0x10000 +#define CONTENTS_MONSTERCLIP 0x20000 + +/** + * @section currents can be added to any other contents, and may be mixed + */ +#define CONTENTS_CURRENT_0 0x40000 +#define CONTENTS_CURRENT_90 0x80000 +#define CONTENTS_CURRENT_180 0x100000 +#define CONTENTS_CURRENT_270 0x200000 +#define CONTENTS_CURRENT_UP 0x400000 +#define CONTENTS_CURRENT_DOWN 0x800000 + +/** + * @endsection + */ + +#define CONTENTS_ORIGIN 0x1000000 /**< removed before bsping an entity. */ +#define CONTENTS_MONSTER 0x2000000 /**< should never be on a brush, only in game. */ +#define CONTENTS_DEBRIS 0x4000000 +#define CONTENTS_DETAIL 0x8000000 /**< brushes to be added after vis leafs. */ +#define CONTENTS_TRANSLUCENT 0x10000000 /**< auto set if any surface has trans. */ +#define CONTENTS_LADDER 0x20000000 +#define CONTENTS_HITBOX 0x40000000 /**< use accurate hitboxes on trace. */ + +/** + * @section Trace masks. + */ +#define MASK_ALL (0xFFFFFFFF) +#define MASK_SOLID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE) /**< everything that is normally solid */ +#define MASK_PLAYERSOLID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE) /**< everything that blocks player movement */ +#define MASK_NPCSOLID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE) /**< blocks npc movement */ +#define MASK_WATER (CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_SLIME) /**< water physics in these contents */ +#define MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_OPAQUE) /**< everything that blocks line of sight for AI, lighting, etc */ +#define MASK_OPAQUE_AND_NPCS (MASK_OPAQUE|CONTENTS_MONSTER) /**< everything that blocks line of sight for AI, lighting, etc, but with monsters added. */ +#define MASK_VISIBLE (MASK_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE) /**< everything that blocks line of sight for players */ +#define MASK_VISIBLE_AND_NPCS (MASK_OPAQUE_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE) /**< everything that blocks line of sight for players, but with monsters added. */ +#define MASK_SHOT (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_HITBOX) /**< bullets see these as solid */ +#define MASK_SHOT_HULL (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_GRATE) /**< non-raycasted weapons see this as solid (includes grates) */ +#define MASK_SHOT_PORTAL (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW) /**< hits solids (not grates) and passes through everything else */ +#define MASK_SOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE) /**< everything normally solid, except monsters (world+brush only) */ +#define MASK_PLAYERSOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_PLAYERCLIP|CONTENTS_GRATE) /**< everything normally solid for player movement, except monsters (world+brush only) */ +#define MASK_NPCSOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE) /**< everything normally solid for npc movement, except monsters (world+brush only) */ +#define MASK_NPCWORLDSTATIC (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE) /**< just the world, used for route rebuilding */ +#define MASK_SPLITAREAPORTAL (CONTENTS_WATER|CONTENTS_SLIME) /**< These are things that can split areaportals */ + +/** + * @endsection + */ + +enum RayType +{ + RayType_EndPoint, /**< The trace ray will go from the start position to the end position. */ + RayType_Infinite /**< The trace ray will go from the start position to infinity using a direction vector. */ +}; + +funcenum TraceEntityFilter +{ + /** + * Called on entity filtering. + * + * @param entity Entity index. + * @param contentsMask Contents Mask. + * @return True to allow the current entity to be hit, otherwise false. + */ + bool:public(entity, contentsMask), + + /** + * Called on entity filtering. + * + * @param entity Entity index. + * @param contentsMask Contents Mask. + * @param data Data value, if used. + * @return True to allow the current entity to be hit, otherwise false. + */ + bool:public(entity, contentsMask, any:data), +}; + +/** + * Get the contents mask and the entity index at the given position. + * + * @param pos World position to test. + * @param entindex Entity index found at the given position (by reference). + * @return Contents mask. + */ +native TR_GetPointContents(const Float:pos[3], &entindex=-1); + +/** + * Get the point contents testing only the given entity index. + * + * @param entindex Entity index to test. + * @param pos World position. + * @return Contents mask. + */ +native TR_GetPointContentsEnt(entindex, const Float:pos[3]); + +/** + * Starts up a new trace ray using a global trace result. + * + * @param pos Starting position of the ray. + * @param vec Depending on RayType, it will be used as the + * ending point, or the direction angle. + * @param flags Trace flags. + * @param rtype Method to calculate the ray direction. + * @noreturn + */ +native TR_TraceRay(const Float:pos[3], + const Float:vec[3], + flags, + RayType:rtype); + +/** + * Starts up a new trace hull using a global trace result. + * + * @param pos Starting position of the ray. + * @param vec Ending position of the ray. + * @param mins Hull minimum size. + * @param maxs Hull maximum size. + * @param flags Trace flags. + * @noreturn + */ +native TR_TraceHull(const Float:pos[3], + const Float:vec[3], + const Float:mins[3], + const Float:maxs[3], + flags); + +/** + * Starts up a new trace ray using a global trace result and a customized + * trace ray filter. + * + * Calling TR_Trace*Filter or TR_Trace*FilterEx from inside a filter + * function is currently not allowed and may not work. + * + * @param pos Starting position of the ray. + * @param vec Depending on RayType, it will be used as the ending + * point, or the direction angle. + * @param flags Trace flags. + * @param rtype Method to calculate the ray direction. + * @param filter Function to use as a filter. + * @param data Arbitrary data value to pass through to the filter + * function. + * @noreturn + */ +native TR_TraceRayFilter(const Float:pos[3], + const Float:vec[3], + flags, + RayType:rtype, + TraceEntityFilter:filter, + any:data=0); + +/** + * Starts up a new trace hull using a global trace result and a customized + * trace ray filter. + * + * Calling TR_Trace*Filter or TR_Trace*FilterEx from inside a filter + * function is currently not allowed and may not work. + * + * @param pos Starting position of the ray. + * @param vec Depending on RayType, it will be used as the ending + * point, or the direction angle. + * @param mins Hull minimum size. + * @param maxs Hull maximum size. + * @param flags Trace flags. + * @param filter Function to use as a filter. + * @param data Arbitrary data value to pass through to the filter + * function. + * @noreturn + */ +native TR_TraceHullFilter(const Float:pos[3], + const Float:vec[3], + const Float:mins[3], + const Float:maxs[3], + flags, + TraceEntityFilter:filter, + any:data=0); + +/** + * Starts up a new trace ray using a new trace result. + * + * @param pos Starting position of the ray. + * @param vec Depending on RayType, it will be used as the ending + * point, or the direction angle. + * @param flags Trace flags. + * @param rtype Method to calculate the ray direction. + * @return Ray trace handle, which must be closed via CloseHandle(). + */ +native Handle:TR_TraceRayEx(const Float:pos[3], + const Float:vec[3], + flags, + RayType:rtype); + +/** + * Starts up a new trace hull using a new trace result. + * + * @param pos Starting position of the ray. + * @param vec Ending position of the ray. + * @param mins Hull minimum size. + * @param maxs Hull maximum size. + * @param flags Trace flags. + * @return Ray trace handle, which must be closed via CloseHandle(). + */ +native Handle:TR_TraceHullEx(const Float:pos[3], + const Float:vec[3], + const Float:mins[3], + const Float:maxs[3], + flags); + +/** + * Starts up a new trace ray using a new trace result and a customized + * trace ray filter. + * + * Calling TR_Trace*Filter or TR_TraceRay*Ex from inside a filter + * function is currently not allowed and may not work. + * + * @param pos Starting position of the ray. + * @param vec Depending on RayType, it will be used as the ending + * point, or the direction angle. + * @param flags Trace flags. + * @param rtype Method to calculate the ray direction. + * @param filter Function to use as a filter. + * @param data Arbitrary data value to pass through to the filter function. + * @return Ray trace handle, which must be closed via CloseHandle(). + */ +native Handle:TR_TraceRayFilterEx(const Float:pos[3], + const Float:vec[3], + flags, + RayType:rtype, + TraceEntityFilter:filter, + any:data=0); + +/** + * Starts up a new trace hull using a new trace result and a customized + * trace ray filter. + * + * Calling TR_Trace*Filter or TR_Trace*FilterEx from inside a filter + * function is currently not allowed and may not work. + * + * @param pos Starting position of the ray. + * @param vec Ending position of the ray. + * @param mins Hull minimum size. + * @param maxs Hull maximum size. + * @param flags Trace flags. + * @param filter Function to use as a filter. + * @param data Arbitrary data value to pass through to the filter function. + * @return Ray trace handle, which must be closed via CloseHandle(). + */ +native Handle:TR_TraceHullFilterEx(const Float:pos[3], + const Float:vec[3], + const Float:mins[3], + const Float:maxs[3], + flags, + TraceEntityFilter:filter, + any:data=0); + +/** + * Returns the time fraction from a trace result (1.0 means no collision). + * + * @param hndl A trace Handle, or INVALID_HANDLE to use a global trace result. + * @return Time fraction value of the trace. + * @error Invalid Handle. + */ +native Float:TR_GetFraction(Handle:hndl=INVALID_HANDLE); + +/** + * Returns the collision position of a trace result. + * + * @param pos Vector buffer to store data in. + * @param hndl A trace Handle, or INVALID_HANDLE to use a global trace result. + * @noreturn + * @error Invalid Handle. + */ +native TR_GetEndPosition(Float:pos[3], Handle:hndl=INVALID_HANDLE); + +/** + * Returns the entity index that collided with the trace. + * + * @param hndl A trace Handle, or INVALID_HANDLE to use a global trace result. + * @return Entity index or -1 for no collision. + * @error Invalid Handle. + */ +native TR_GetEntityIndex(Handle:hndl=INVALID_HANDLE); + +/** + * Returns if there was any kind of collision along the trace ray. + * + * @param hndl A trace Handle, or INVALID_HANDLE to use a global trace result. + * @return True if any collision found, otherwise false. + * @error Invalid Handle. + */ +native bool:TR_DidHit(Handle:hndl=INVALID_HANDLE); + +/** + * Returns in which body hit group the trace collided if any. + * + * @param hndl A trace Handle, or INVALID_HANDLE to use a global trace result. + * @return Body hit group. + * @error Invalid Handle. + */ +native TR_GetHitGroup(Handle:hndl=INVALID_HANDLE); + +/** + * Find the normal vector to the collison plane of a trace. + * + * @param hndl A trace Handle, or INVALID_HANDLE to use a global trace result. + * @param normal Vector buffer to store the vector normal to the collision plane + * @noreturn + * @error Invalid Handle + */ +native TR_GetPlaneNormal(Handle:hndl, Float:normal[3]); + diff --git a/src/include/sdktools_voice.inc b/src/include/sdktools_voice.inc new file mode 100644 index 0000000..2cf33bb --- /dev/null +++ b/src/include/sdktools_voice.inc @@ -0,0 +1,92 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sdktools_voice.inc 2163 2008-05-21 03:31:27Z dvander $ + */ + +#if defined _sdktools_voice_included + #endinput +#endif +#define _sdktools_voice_included + +/** + * @section voice flags. + */ +#define VOICE_NORMAL 0 /**< Allow the client to listen and speak normally. */ +#define VOICE_MUTED 1 /**< Mutes the client from speaking to everyone. */ +#define VOICE_SPEAKALL 2 /**< Allow the client to speak to everyone. */ +#define VOICE_LISTENALL 4 /**< Allow the client to listen to everyone. */ +#define VOICE_TEAM 8 /**< Allow the client to always speak to team, even when dead. */ +#define VOICE_LISTENTEAM 16 /**< Allow the client to always hear teammates, including dead ones. */ + +#define LISTEN_DEFAULT 0 /**< Default action (flags or game) is taken */ +#define LISTEN_NO 1 /**< Receiver cannot hear sender */ +#define LISTEN_YES 2 /**< Receiver can hear sender */ + +/** + * @endsection + */ + +/** + * Set the client listening flags. + * + * @param client The client index + * @param flags The voice flags + * @noreturn + */ +native SetClientListeningFlags(client, flags); + +/** + * Retrieve the client current listening flags. + * + * @param client The client index + * @return The current voice flags + */ +native GetClientListeningFlags(client); + +/** + * Set the receiver's ability to listen to the sender. + * + * @param receiver The listener client index. + * @param sender The sender client index. + * @param control A LISTEN_ constant describing the voice relationship. + * @noreturn + * @error If either client index is invalid or not connected. + */ +native bool:SetClientListening(receiver, sender, control); + +/** + * Retrieves if a receiver can listen to the sender. + * + * @param receiver The listener client index. + * @param sender The sender client index. + * @return A LISTEN_ constant describing the voice relationship. + * @error If either client index is invalid or not connected. + */ +native bool:GetClientListening(receiver, sender); diff --git a/src/include/sorting.inc b/src/include/sorting.inc new file mode 100644 index 0000000..fe21511 --- /dev/null +++ b/src/include/sorting.inc @@ -0,0 +1,176 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sorting.inc 1923 2008-03-03 06:28:41Z pred $ + */ + + +#if defined _sorting_included + #endinput +#endif +#define _sorting_included + +/** + * Contains sorting orders. + */ +enum SortOrder +{ + Sort_Ascending = 0, /**< Ascending order */ + Sort_Descending = 1, /**< Descending order */ + Sort_Random = 2 /**< Random order */ +}; + +/** + * Data types for ADT Array Sorts + */ +enum SortType +{ + Sort_Integer = 0, + Sort_Float, + Sort_String, +}; + +/** + * Sorts an array of integers. + * + * @param array Array of integers to sort in-place. + * @param array_size Size of the array. + * @param order Sorting order to use. + * @noreturn + */ +native SortIntegers(array[], array_size, SortOrder:order = Sort_Ascending); + +/** + * Sorts an array of float point numbers. + * + * @param array Array of floating point numbers to sort in-place. + * @param array_size Size of the array. + * @param order Sorting order to use. + * @noreturn + */ +native SortFloats(Float:array[], array_size, SortOrder:order = Sort_Ascending); + +/** + * Sorts an array of strings. + * + * @param array Array of strings to sort in-place. + * @param array_size Size of the array. + * @param order Sorting order to use. + * @noreturn + */ +native SortStrings(String:array[][], num_strings, SortOrder:order = Sort_Ascending); + +/** + * Sort comparison function for 1D array elements. + * @note You may need to use explicit tags in order to use data properly. + * + * @param elem1 First element to compare. + * @param elem2 Second element to compare. + * @param array Array that is being sorted (order is undefined). + * @param hndl Handle optionally passed in while sorting. + * @return -1 if first should go before second + * 0 if first is equal to second + * 1 if first should go after second + */ +functag SortFunc1D public(elem1, elem2, const array[], Handle:hndl); + +/** + * Sorts a custom 1D array. You must pass in a comparison function. + * + * @param array Array to sort. + * @param array_size Size of the array to sort. + * @param sortfunc Sort function. + * @param hndl Optional Handle to pass through the comparison calls. + * @noreturn + */ +native SortCustom1D(array[], array_size, SortFunc1D:sortfunc, Handle:hndl=INVALID_HANDLE); + +/** + * Sort comparison function for 2D array elements (sub-arrays). + * @note You may need to use explicit tags in order to use data properly. + * + * @param elem1 First array to compare. + * @param elem2 Second array to compare. + * @param array Array that is being sorted (order is undefined). + * @param hndl Handle optionally passed in while sorting. + * @return -1 if first should go before second + * 0 if first is equal to second + * 1 if first should go after second + */ +funcenum SortFunc2D +{ + public(array[], array[], const array[][], Handle:hndl), + public(String:array[], String:array[], const String:array[][], Handle:hndl), +}; + +/** + * Sorts a custom 2D array. You must pass in a comparison function. + * + * @param array Array to sort. + * @param array_size Size of the major array to sort (first index, outermost). + * @param sortfunc Sort comparison function to use. + * @param hndl Optional Handle to pass through the comparison calls. + * @noreturn + */ +native SortCustom2D(array[][], array_size, SortFunc2D:sortfunc, Handle:hndl=INVALID_HANDLE); + +/** + * Sort an ADT Array. Specify the type as Integer, Float, or String. + * + * @param array Array Handle to sort + * @param order Sort order to use, same as other sorts. + * @param type Data type stored in the ADT Array + * @noreturn + */ +native SortADTArray(Handle:array, SortOrder:order, SortType:type); + +/** + * Sort comparison function for ADT Array elements. Function provides you with + * indexes currently being sorted, use ADT Array functions to retrieve the + * index values and compare. + * + * @param index1 First index to compare. + * @param index2 Second index to compare. + * @param array Array that is being sorted (order is undefined). + * @param hndl Handle optionally passed in while sorting. + * @return -1 if first should go before second + * 0 if first is equal to second + * 1 if first should go after second + */ +functag SortFuncADTArray public(index1, index2, Handle:array, Handle:hndl); + +/** + * Custom sorts an ADT Array. You must pass in a comparison function. + * + * @param array Array Handle to sort + * @param sortfunc Sort comparision function to use + * @param hndl Optional Handle to pass through the comparison calls. + * @noreturn + */ +native SortADTArrayCustom(Handle:array, SortFuncADTArray:sortfunc, Handle:hndl=INVALID_HANDLE); \ No newline at end of file diff --git a/src/include/sourcebans.inc b/src/include/sourcebans.inc new file mode 100644 index 0000000..1a55da0 --- /dev/null +++ b/src/include/sourcebans.inc @@ -0,0 +1,31 @@ +#if defined _sourcebans_included + #endinput +#endif +#define _sourcebans_included + +public SharedPlugin:__pl_sourcebans = +{ + name = "SourceBans", + file = "sourcebans.smx", + required = 0 +}; + + +public __pl_sourcebans_SetNTVOptional() +{ + MarkNativeAsOptional("SBBanPlayer"); +} + + +/********************************************************* + * Ban Player from server + * + * @param client The client index of the admin who is banning the client + * @param target The client index of the player to ban + * @param time The time to ban the player for (in minutes, 0 = permanent) + * @param reason The reason to ban the player from the server + * @noreturn + *********************************************************/ +native SBBanPlayer(client, target, time, String:reason[]); + +//Yarr! diff --git a/src/include/sourcemod.inc b/src/include/sourcemod.inc new file mode 100644 index 0000000..7a55072 --- /dev/null +++ b/src/include/sourcemod.inc @@ -0,0 +1,554 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: sourcemod.inc 2212 2008-05-29 03:55:31Z dvander $ + */ + +#if defined _sourcemod_included + #endinput +#endif +#define _sourcemod_included + +/** + * Plugin public information. + */ +struct Plugin +{ + const String:name[], /**< Plugin Name */ + const String:description[], /**< Plugin Description */ + const String:author[], /**< Plugin Author */ + const String:version[], /**< Plugin Version */ + const String:url[], /**< Plugin URL */ +}; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Declare this as a struct in your plugin to expose its information. + * Example: + * + * public Plugin:myinfo = + * { + * name = "My Plugin", + * //etc + * }; + */ +public Plugin:myinfo; + +/** + * Called when the plugin is fully initialized and all known external references + * are resolved. This is only called once in the lifetime of the plugin, and is + * paired with OnPluginEnd(). + * + * If any run-time error is thrown during this callback, the plugin will be marked + * as failed. + * + * It is not necessary to close any handles or remove hooks in this function. + * SourceMod guarantees that plugin shutdown automatically and correctly releases + * all resources. + * + * @noreturn + */ +forward OnPluginStart(); + +/** + * Called before OnPluginStart, in case the plugin wants to check for load failure. + * This is called even if the plugin type is "private." Any natives from modules are + * not available at this point. Thus, this forward should only be used for explicit + * pre-emptive things, such as adding dynamic natives, or setting certain types of load filters. + * + * @note It is not safe to call externally resolved natives until OnPluginStart(). + * @note Any sort of RTE in this function will cause the plugin to fail loading. + * @note You MUST remember to return SOMETHING here. The act of not returning is + * equivalent to returning 0 (false). If you forgot to return, it will + * cause your plugin to not load with a very strange error message. + * + * @param myself Handle to the plugin. + * @param late Whether or not the plugin was loaded "late" (after map load). + * @param error Error message buffer in case load failed. + * @param err_max Maximum number of characters for error message buffer. + * @return True if load success, false otherwise. + */ +forward bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max); + +/** + * Called when the plugin is about to be unloaded. + * + * @noreturn + */ +forward OnPluginEnd(); + +/** + * Called when the plugin's pause status is changing. + * + * @param pause True if the plugin is being paused, false otherwise. + * @noreturn + */ +forward OnPluginPauseChange(bool:pause); + +/** + * Called before every server frame. Note that you should avoid + * doing expensive computations here, and you should declare large + * local arrays using 'decl' instead of 'new'. + */ +forward OnGameFrame(); + +/** + * Called when the map is loaded. + * + * @note This used to be OnServerLoad(), which is now deprecated. + * Plugins still using the old forward will work. + */ +forward OnMapStart(); + +/** + * Called right before a map ends. + */ +forward OnMapEnd(); + +/** + * Called when the map has loaded, servercfgfile (server.cfg) has been + * executed, and all plugin configs are done executing. This is the best + * place to initialize plugin functions which are based on cvar data. + * + * @note This will always be called once and only once per map. It will be + * called after OnMapStart(). + * + * @noreturn + */ +forward OnConfigsExecuted(); + +/** + * This is called once, right after OnMapStart() but any time before + * OnConfigsExecuted(). It is called after the "exec sourcemod.cfg" + * command and all AutoExecConfig() exec commands have been added to + * the ServerCommand() buffer. + * + * If you need to load per-map settings that override default values, + * adding commands to the ServerCommand() buffer here will guarantee + * that they're set before OnConfigsExecuted(). + * + * Unlike OnMapStart() and OnConfigsExecuted(), this is not called on + * late loads that occur after OnMapStart(). + * + * @noreturn + */ +forward OnAutoConfigsBuffered(); + +/** + * @deprecated Use OnConfigsExecuted() instead. + */ +#pragma deprecated Use OnConfigsExecuted() instead +forward OnServerCfg(); + +/** + * Called after all plugins have been loaded. This is called once for + * every plugin. If a plugin late loads, it will be called immediately + * after OnPluginStart(). + */ +forward OnAllPluginsLoaded(); + +/** + * Returns the calling plugin's Handle. + * + * @return Handle of the calling plugin. + */ +native Handle:GetMyHandle(); + +/** + * Returns an iterator that can be used to search through plugins. + * + * @return Handle to iterate with. Must be closed via + * CloseHandle(). + * @error Invalid Handle. + */ +native Handle:GetPluginIterator(); + +/** + * Returns whether there are more plugins available in the iterator. + * + * @param iter Handle to the plugin iterator. + * @return True on more plugins, false otherwise. + * @error Invalid Handle. + */ +native bool:MorePlugins(Handle:iter); + +/** + * Returns the current plugin in the iterator and advances the iterator. + * + * @param iter Handle to the plugin iterator. + * @return Current plugin the iterator is at, before + * the iterator is advanced. + * @error Invalid Handle. + */ +native Handle:ReadPlugin(Handle:iter); + +/** + * Returns a plugin's status. + * + * @param plugin Plugin Handle (INVALID_HANDLE uses the calling plugin). + * @return Status code for the plugin. + * @error Invalid Handle. + */ +native PluginStatus:GetPluginStatus(Handle:plugin); + +/** + * Retrieves a plugin's file name relative to the plugins folder. + * + * @param plugin Plugin Handle (INVALID_HANDLE uses the calling plugin). + * @param buffer Buffer to the store the file name. + * @param maxlength Maximum length of the name buffer. + * @noreturn + * @error Invalid Handle. + */ +native GetPluginFilename(Handle:plugin, String:buffer[], maxlength); + +/** + * Retrieves whether or not a plugin is being debugged. + * + * @param plugin Plugin Handle (INVALID_HANDLE uses the calling plugin). + * @return True if being debugged, false otherwise. + * @error Invalid Handle. + */ +native bool:IsPluginDebugging(Handle:hndl); + +/** + * Retrieves a plugin's public info. + * + * @param plugin Plugin Handle (INVALID_HANDLE uses the calling plugin). + * @param info Plugin info property to retrieve. + * @param buffer Buffer to store info in. + * @param maxlength Maximum length of buffer. + * @return True on success, false if property is not available. + * @error Invalid Handle. + */ +native bool:GetPluginInfo(Handle:plugin, PluginInfo:info, String:buffer[], maxlength); + +/** + * Finds a plugin by its order in the list from the "plugins list" server + * "sm" command. You should not use this function to loop through all plugins, + * use the iterator instead. Looping through all plugins using this native + * is O(n^2), whereas using the iterator is O(n). + * + * @param order_num Number of the plugin as it appears in "sm plugins list". + * @return Plugin Handle on success, INVALID_HANDLE if no plugin + * matches the given number. + */ +native Handle:FindPluginByNumber(order_num); + +/** + * Causes the plugin to enter a failed state. An error will be thrown and + * the plugin will be paused until it is unloaded or reloaded. + * + * For backwards compatibility, if no extra arguments are passed, no + * formatting is applied. If one or more additional arguments is passed, + * the string is formatted using Format(). If any errors are encountered + * during formatting, both the format specifier string and an additional + * error message are written. + * + * This function does not return, and no further code in the plugin is + * executed. + * + * @param string Format specifier string. + * @param ... Formatting arguments. + * @noreturn + * @error Always throws SP_ERROR_ABORT. + */ +native SetFailState(const String:string[], any:...); + +/** + * Aborts the current callback and throws an error. This function + * does not return in that no code is executed following it. + * + * @param format String format. + * @param ... Format arguments. + * @noreturn + * @error Always! + */ +native ThrowError(const String:fmt[], any:...); + +/** + * Gets the system time as a unix timestamp. + * + * @param bigStamp Optional array to store the 64bit timestamp in. + * @return 32bit timestamp (number of seconds since unix epoch). + */ +native GetTime(bigStamp[2]={0,0}); + +/** + * Produces a date and/or time string value for a timestamp. + * + * See this URL for valid parameters: + * http://cplusplus.com/reference/clibrary/ctime/strftime.html + * + * @param buffer Destination string buffer. + * @param maxlength Maximum length of output string buffer. + * @param format Formatting rules (passing NULL_STRING will use the rules defined in sm_datetime_format). + * @param stamp Optional time stamp. + * @noreturn + * @error Buffer too small or invalid time format. + */ +native FormatTime(String:buffer[], maxlength, const String:format[], stamp=-1); + +/** + * Loads a game config file. + * + * @param file File to load. The path must be relative to the 'gamedata' folder under the config folder + * and the extension should be omitted. + * @return A handle to the game config file or INVALID_HANDLE in failure. + */ +native Handle:LoadGameConfigFile(const String:file[]); + +/** + * Returns an offset value. + * + * @param gc Game config handle. + * @param key Key to retrieve from the offset section. + * @return An offset, or -1 on failure. + */ +native GameConfGetOffset(Handle:gc, const String:key[]); + +/** + * Gets the value of a key from the "Keys" section. + * + * @param gc Game config handle. + * @param key Key to retrieve from the Keys section. + * @param buffer Destination string buffer. + * @param maxlen Maximum length of output string buffer. + * @return True if key existed, false otherwise. + */ +native bool:GameConfGetKeyValue(Handle:gc, const String:key[], String:buffer[], maxlen); + +/** + * Returns the operating system's "tick count," which is a number of + * milliseconds since the operating system loaded. This can be used + * for basic benchmarks. + * + * @return Tick count in milliseconds. + */ +native GetSysTickCount(); + +/** + * Specifies that the given config file should be executed after plugin load. + * OnConfigsExecuted() will not be called until the config file has executed, + * but it will be called if the execution fails. + * + * @param autoCreate If true, and the config file does not exist, such a config + * file will be automatically created and populated with + * information from the plugin's registered cvars. + * @param name Name of the config file, excluding the .cfg extension. + * If empty, is assumed. + * @param folder Folder under cfg/ to use. By default this is "sourcemod." + * @noreturn + */ +native AutoExecConfig(bool:autoCreate=true, const String:name[]="", const String:folder[]="sourcemod"); + +/** + * Sets a native as optional, such that if it is unloaded, removed, + * or otherwise non-existent, the plugin will still work. Calling + * removed natives results in a run-time error. + * + * @param name Native name. + * @noreturn + */ +native MarkNativeAsOptional(const String:name[]); + +/** + * Registers a library name for identifying as a dependency to + * other plugins. + * + * @param name Library name. + * @noreturn + */ +native RegPluginLibrary(const String:name[]); + +/** + * Returns whether a library exists. This function should be considered + * expensive; it should only be called on plugin to determine availability + * of resources. Use OnLibraryAdded()/OnLibraryRemoved() to detect changes + * in optional resources. + * + * @param name Library name of a plugin or extension. + * @return True if exists, false otherwise. + */ +native bool:LibraryExists(const String:name[]); + +/** + * Returns the status of an extension, by filename. + * + * @param name Extension name (like "sdktools.ext"). + * @param error Optional error message buffer. + * @param maxlength Length of optional error message buffer. + * @return -2 if the extension was not found. + * -1 if the extension was found but failed to load. + * 0 if the extension loaded but reported an error. + * 1 if the extension is running without error. + */ +native GetExtensionFileStatus(const String:name[], String:error[]="", maxlength=0); + +/** + * Called after a library is added that the current plugin references + * optionally. A library is either a plugin name or extension name, as + * exposed via its include file. + * + * @param name Library name. + */ +forward OnLibraryAdded(const String:name[]); + +/** + * Called right before a library is removed that the current plugin references + * optionally. A library is either a plugin name or extension name, as + * exposed via its include file. + * + * @param name Library name. + */ +forward OnLibraryRemoved(const String:name[]); + +#define MAPLIST_FLAG_MAPSFOLDER (1<<0) /**< On failure, use all maps in the maps folder. */ +#define MAPLIST_FLAG_CLEARARRAY (1<<1) /**< If an input array is specified, clear it before adding. */ +#define MAPLIST_FLAG_NO_DEFAULT (1<<2) /**< Do not read "default" or "mapcyclefile" on failure. */ + +/** + * Loads a map list to an ADT Array. + * + * A map list is a list of maps from a file. SourceMod allows easy configuration of + * maplists through addons/sourcemod/configs/maplists.cfg. Each entry is given a + * name and a file (for example, "rtv" => "rtv.cfg"), or a name and a redirection + * (for example, "rtv" => "default"). This native will read a map list entry, + * cache the file, and return the list of maps it holds. + * + * Serial change numbers are used to identify if a map list has changed. Thus, if + * you pass a serial change number and it's equal to what SourceMod currently knows + * about the map list, then SourceMod won't reparse the file. + * + * If the maps end up being read from the maps folder (MAPLIST_FLAG_MAPSFOLDER), they + * are automatically sorted in alphabetical, ascending order. + * + * Arrays created by this function are temporary and must be freed via CloseHandle(). + * Modifying arrays created by this function will not affect future return values or + * or the contents of arrays returned to other plugins. + * + * @param array Array to store the map list. If INVALID_HANDLE, a new blank + * array will be created. The blocksize should be at least 16; + * otherwise results may be truncated. Items are added to the array + * as strings. The array is never checked for duplicates, and it is + * not read beforehand. Only the serial number is used to detect + * changes. + * @param serial Serial number to identify last known map list change. If -1, the + * the value will not be checked. If the map list has since changed, + * the serial is updated (even if -1 was passed). If there is an error + * finding a valid maplist, then the serial is set to -1. + * @param str Config name, or "default" for the default map list. Config names + * should be somewhat descriptive. For example, the admin menu uses + * a config name of "admin menu". The list names can be configured + * by users in addons/sourcemod/configs/maplists.cfg. + * @param flags MAPLIST_FLAG flags. + * @return On failure: + * INVALID_HANDLE is returned, the serial is set to -1, and the input + * array (if any) is left unchanged. + * On no change: + INVALID_HANDLE is returned, the serial is unchanged, and the input + array (if any) is left unchanged. + * On success: + * A valid array Handle is returned, containing at least one map string. + * If an array was passed, the return value is equal to the passed Array + * Handle. If the passed array was not cleared, it will have grown by at + * least one item. The serial number is updated to a positive number. + * @error Invalid array Handle that is not INVALID_HANDLE. + */ +native Handle:ReadMapList(Handle:array=INVALID_HANDLE, + &serial=-1, + const String:str[]="default", + flags=MAPLIST_FLAG_CLEARARRAY); + +/** + * Makes a compatibility binding for map lists. For example, if a function previously used + * "clam.cfg" for map lists, this function will insert a "fake" binding to "clam.cfg" that + * will be overridden if it's in the maplists.cfg file. + * + * @param name Configuration name that would be used with ReadMapList(). + * @param file Default file to use. + * @noreturn + */ +native SetMapListCompatBind(const String:name[], const String:file[]); + +/** + * Called when a client has sent chat text. This must return either true or + * false to indicate that a client is or is not spamming the server. + * + * The return value is a hint only. Core or another plugin may decide + * otherwise. + * + * @param client Client index. The server (0) will never be passed. + * @return True if client is spamming the server, false otherwise. + */ +forward bool:OnClientFloodCheck(client); + +/** + * Called after a client's flood check has been computed. This can be used + * by antiflood algorithms to decay/increase flooding weights. + * + * Since the result from "OnClientFloodCheck" isn't guaranteed to be the + * final result, it is generally a good idea to use this to play with other + * algorithms nicely. + * + * @param client Client index. The server (0) will never be passed. + * @param blocked True if client flooded last "say", false otherwise. + * @noreturn + */ +forward OnClientFloodResult(client, bool:blocked); + +#include +#include +#include + diff --git a/src/include/string.inc b/src/include/string.inc new file mode 100644 index 0000000..8d57b3f --- /dev/null +++ b/src/include/string.inc @@ -0,0 +1,557 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: string.inc 2206 2008-05-29 03:46:42Z dvander $ + */ + +#if defined _string_included + #endinput +#endif +#define _string_included + +/** + * @global Unless otherwise noted, all string functions which take in a + * writable buffer and maximum length should have the null terminator INCLUDED + * in the length. This means that this is valid: + * strcopy(string, sizeof(string), ...) + */ + +/** + * Calculates the length of a string. + * + * @param str String to check. + * @return Number of valid character bytes in the string. + */ +native strlen(const String:str[]); + +/** + * Tests whether a string is found inside another string. + * + * @param str String to search in. + * @param substr Substring to find inside the original string. + * @param caseSensitive If true (default), search is case sensitive. + * If false, search is case insensitive. + * @return -1 on failure (no match found). Any other value + * indicates a position in the string where the match starts. + */ +native StrContains(const String:str[], const String:substr[], bool:caseSensitive=true); + +/** + * Compares two strings lexographically. + * + * @param str1 First string (left). + * @param str2 Second string (right). + * @param caseSensitive If true (default), comparison is case sensitive. + * If false, comparison is case insensitive. + * @return -1 if str1 < str2 + * 0 if str1 == str2 + * 1 if str1 > str2 + */ +native strcmp(const String:str1[], const String:str2[], bool:caseSensitive=true); + +/** + * Compares two strings parts lexographically. + * + * @param str1 First string (left). + * @param str2 Second string (right). + * @param num Number of characters to compare. + * @param caseSensitive If true (default), comparison is case sensitive. + * If false, comparison is case insensitive. + * @return -1 if str1 < str2 + * 0 if str1 == str2 + * 1 if str1 > str2 + */ +native strncmp(const String:str1[], const String:str2[], num, bool:caseSensitive=true); + +/** + * Backwards compatible stock - StrCompare is now strcmp + * @deprecated Renamed to strcmp + */ +#pragma deprecated Use strcmp() instead +stock StrCompare(const String:str1[], const String:str2[], bool:caseSensitive=true) +{ + return strcmp(str1, str2, caseSensitive); +} + +/** + * Returns whether two strings are equal. + * + * @param str1 First string (left). + * @param str2 Second string (right). + * @param caseSensitive If true (default), comparison is case sensitive. + * If false, comparison is case insensitive. + * @return True if equal, false otherwise. + */ +stock bool:StrEqual(const String:str1[], const String:str2[], bool:caseSensitive=true) +{ + return (strcmp(str1, str2, caseSensitive) == 0); +} + +/** + * Copies one string to another string. + * @note If the destination buffer is too small to hold the source string, the + * destination will be truncated. + * + * @param dest Destination string buffer to copy to. + * @param destlen Destination buffer length (includes null terminator). + * @param source Source string buffer to copy from. + * @return Number of cells written. + */ +native strcopy(String:dest[], destLen, const String:source[]); + +/** + * Backwards compatibility stock - use strcopy + * @deprecated Renamed to strcopy + */ +#pragma deprecated Use strcopy() instead +stock StrCopy(String:dest[], destLen, const String:source[]) +{ + return strcopy(dest, destLen, source); +} + +/** + * Formats a string according to the SourceMod format rules (see documentation). + * + * @param buffer Destination string buffer. + * @param maxlength Maximum length of output string buffer. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @return Number of cells written. + */ +native Format(String:buffer[], maxlength, const String:format[], any:...); + +/** + * Formats a string according to the SourceMod format rules (see documentation). + * @note This is the same as Format(), except none of the input buffers can + * overlap the same memory as the output buffer. Since this security + * check is removed, it is slightly faster. + * + * @param buffer Destination string buffer. + * @param maxlength Maximum length of output string buffer. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @return Number of cells written. + */ +native FormatEx(String:buffer[], maxlength, const String:format[], any:...); + +/** + * Formats a string according to the SourceMod format rules (see documentation). + * @note This is the same as Format(), except it grabs parameters from a + * parent parameter stack, rather than a local. This is useful for + * implementing your own variable argument functions. + * + * @param buffer Destination string buffer. + * @param maxlength Maximum length of output string buffer. + * @param format Formatting rules. + * @param varpos Argument number which contains the '...' symbol. + * Note: Arguments start at 1. + * @return Number of bytes written. + */ +native VFormat(String:buffer[], maxlength, const String:format[], varpos); + +/** + * Converts a string to an integer. + * + * @param str String to convert. + * @param nBase Numerical base to use. 10 is default. + * @return Integer conversion of string, or 0 on failure. + */ +native StringToInt(const String:str[], nBase=10); + +/** + * Converts a string to an integer with some more options. + * + * @param str String to convert. + * @param result Variable to store the result in. + * @param nBase Numerical base to use. 10 is default. + * @return Number of characters consumed. + */ +native StringToIntEx(const String:str[], &result, nBase=10); + +/** + * Converts an integer to a string. + * + * @param num Integer to convert. + * @param str Buffer to store string in. + * @param maxlength Maximum length of string buffer. + * @return Number of cells written to buffer. + */ +native IntToString(num, String:str[], maxlength); + +/** + * Converts a string to a floating point number. + * + * @param str String to convert to a foat. + * @return Floating point result, or 0.0 on error. + */ +native Float:StringToFloat(const String:str[]); + +/** + * Converts a string to a floating point number with some more options. + * + * @param str String to convert to a foat. + * @param result Variable to store result in. + * @return Number of characters consumed. + */ +native StringToFloatEx(const String:str[], &Float:result); + +/** + * Converts a floating point number to a string. + * + * @param num Floating point number to convert. + * @param str Buffer to store string in. + * @param maxlength Maximum length of string buffer. + * @return Number of cells written to buffer. + */ +native FloatToString(Float:num, String:str[], maxlength); + +/** + * Finds the first "argument" in a string; either a set of space + * terminated characters, or a fully quoted string. After the + * argument is found, whitespace is read until the next portion + * of the string is reached. If nothing remains, -1 is returned. + * Otherwise, the index to the first character is returned. + * + * @param source Source input string. + * @param arg Stores argument read from string. + * @param argLen Maximum length of argument buffer. + * @return Index to next piece of string, or -1 if none. + */ +native BreakString(const String:source[], String:arg[], argLen); + +/** + * Backwards compatibility stock - use BreakString + * @deprecated Renamed to BreakString. + */ +#pragma deprecated Use BreakString() instead +stock StrBreak(const String:source[], String:arg[], argLen) +{ + return BreakString(source, arg, argLen); +} + +/** + * Removes whitespace characters from the beginning and end of a string. + * + * @param str The string to trim. + * @return Number of bytes written (UTF-8 safe). + */ +native TrimString(String:str[]); + +/** + * Returns text in a string up until a certain character sequence is reached. + * + * @param source Source input string. + * @param split A string which specifies a search point to break at. + * @param part Buffer to store string part. + * @param partLen Maximum length of the string part buffer. + * @return -1 if no match was found; otherwise, an index into source + * marking the first index after the searched text. The + * index is always relative to the start of the input string. + */ +native SplitString(const String:source[], const String:split[], String:part[], partLen); + +/** + * Given a string, replaces all occurrences of a search string with a + * replacement string. + * + * @param text String to perform search and replacements on. + * @param maxlength Maximum length of the string buffer. + * @param search String to search for. + * @param replace String to replace the search string with. + * @return Number of replacements that were performed. + */ +native ReplaceString(String:text[], maxlength, const String:search[], const String:replace[]); + +/** + * Given a string, replaces the first occurrence of a search string with a + * replacement string. + * + * @param text String to perform search and replacements on. + * @param maxlength Maximum length of the string buffer. + * @param search String to search for. + * @param replace String to replace the search string with. + * @param searchLen If higher than -1, its value will be used instead of + * a strlen() call on the search parameter. + * @param replaceLen If higher than -1, its value will be used instead of + * a strlen() call on the replace parameter. + * @return Index into the buffer (relative to the start) from where + * the last replacement ended, or -1 if no replacements were + * made. + */ +native ReplaceStringEx(String:text[], maxlength, const String:search[], const String:replace[], searchLen=-1, replaceLen=-1); + +/** + * Returns the number of bytes a character is using. This is + * for multi-byte characters (UTF-8). For normal ASCII characters, + * this will return 1. + * + * @param source Source input string. + * @return Number of bytes the current character uses. + */ +native GetCharBytes(const String:source[]); + +/** + * Returns whether a character is an ASCII alphabet character. + * + * @note Multi-byte characters will always return false. + * + * @param char Character to test. + * @return True if character is alphabetical, otherwise false. + */ +native bool:IsCharAlpha(chr); + +/** + * Returns whether a character is numeric. + * + * @note Multi-byte characters will always return false. + * + * @param char Character to test. + * @return True if character is numeric, otherwise false. + */ +native bool:IsCharNumeric(chr); + +/** + * Returns whether a character is whitespace. + * + * @note Multi-byte characters will always return false. + * + * @param char Character to test. + * @return True if character is whitespace, otherwise false. + */ +native bool:IsCharSpace(chr); + +/** + * Returns if a character is multi-byte or not. + * + * @param char Character to test. + * @return 0 for a normal 7-bit ASCII character, + * otherwise number of bytes in multi-byte character. + */ +native IsCharMB(chr); + +/** + * Returns whether an alphabetic character is uppercase. + * + * @note Multi-byte characters will always return false. + * + * @param char Character to test. + * @return True if character is uppercase, otherwise false. + */ +native bool:IsCharUpper(chr); + +/** + * Returns whether an alphabetic character is lowercase. + * + * @note Multi-byte characters will always return false. + * + * @param char Character to test. + * @return True if character is lowercase, otherwise false. + */ +native bool:IsCharLower(chr); + +/** + * Strips a quote pair off a string if it exists. That is, the following + * replace rule is applied once: $"(.*)"^ -> $\1^ + * + * Note that the leading and trailing quotes will only be removed if both + * exist. Otherwise, the string is left unmodified. This function should + * be considered O(k) (all characters get shifted down). + * + * @param text String to modify (in place). + * @return True if string was modified, false if there was no + * set of quotes. + */ +native bool:StripQuotes(String:text[]); + +/** + * Returns an uppercase character to a lowercase character. + * + * @param chr Characer to convert. + * @return Lowercase character on success, + * no change on failure. + */ +stock CharToUpper(chr) +{ + if (IsCharLower(chr)) + { + return (chr & ~(1<<5)); + } + return chr; +} + +/** + * Returns a lowercase character to an uppercase character. + * + * @param chr Characer to convert. + * @return Uppercase character on success, + * no change on failure. + */ +stock CharToLower(chr) +{ + if (IsCharUpper(chr)) + { + return (chr | (1<<5)); + } + return chr; +} + +/** + * Finds the first occurrence of a character in a string. + * + * @param str String. + * @param c Character to search for. + * @param reverse False (default) to search forward, true to search + * backward. + * @return The index of the first occurrence of the character + * in the string, or -1 if the character was not found. + */ +stock FindCharInString(const String:str[], c, bool:reverse = false) +{ + new i, len + + len = strlen(str); + + if (!reverse) + { + for (i = 0; i < len; i++) + { + if (str[i] == c) + { + return i; + } + } + } + else + { + for (i = len - 1; i >= 0; i--) + { + if (str[i] == c) + { + return i; + } + } + } + + return -1; +} + +/** + * Concatenates one string onto another. + * + * @param buffer String to append to. + * @param maxlength Maximum length of entire buffer. + * @param source Source string to concatenate. + * @return Number of bytes written. + */ +stock StrCat(String:buffer[], maxlength, const String:source[]) +{ + new len = strlen(buffer); + if (len >= maxlength) + { + return 0; + } + + return Format(buffer[len], maxlength-len, "%s", source); +} + +/** + * Breaks a string into pieces and stores each piece into an array of buffers. + * + * @param text The string to split. + * @param split The string to use as a split delimiter. + * @param buffers An array of string buffers (2D array). + * @param maxStrings Number of string buffers (first dimension size). + * @param maxStringLength Maximum length of each string buffer. + * @return Number of strings retrieved. + */ +stock ExplodeString(const String:text[], const String:split[], String:buffers[][], maxStrings, maxStringLength) +{ + new reloc_idx, idx, total; + + if (maxStrings < 1 || split[0] == '\0') + { + return 0; + } + + while ((idx = SplitString(text[reloc_idx], split, buffers[total], maxStringLength)) != -1) + { + reloc_idx += idx; + if (text[reloc_idx] == '\0') + { + break; + } + if (++total >= maxStrings) + { + return total; + } + } + + if (text[reloc_idx] != '\0' && total <= maxStrings - 1) + { + strcopy(buffers[total++], maxStringLength, text[reloc_idx]); + } + + return total; +} + +/** + * Joins an array of strings into one string, with a "join" string inserted in + * between each given string. This function complements ExplodeString. + * + * @param strings An array of strings. + * @param numStrings Number of strings in the array. + * @param join The join string to insert between each string. + * @param buffer Output buffer to write the joined string to. + * @param maxLength Maximum length of the output buffer. + * @return Number of bytes written to the output buffer. + */ +stock ImplodeStrings(const String:strings[][], numStrings, const String:join[], String:buffer[], maxLength) +{ + new total, length, part_length; + new join_length = strlen(join); + for (new i=0; i. + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: textparse.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _textparse_included + #endinput +#endif +#define _textparse_included + + +/******************************** + * Everything below describes the SMC Parse, or "SourceMod Configuration" format. + * This parser is entirely event based. You must hook events to receive data. + * The file format itself is nearly identical to Valve's KeyValues format. + ********************************/ + +/** + * Parse result directive. + */ +enum SMCResult +{ + SMCParse_Continue, /**< Continue parsing */ + SMCParse_Halt, /**< Stop parsing here */ + SMCParse_HaltFail /**< Stop parsing and return failure */ +}; + +/** + * Parse error codes. + */ +enum SMCError +{ + SMCError_Okay = 0, /**< No error */ + SMCError_StreamOpen, /**< Stream failed to open */ + SMCError_StreamError, /**< The stream died... somehow */ + SMCError_Custom, /**< A custom handler threw an error */ + SMCError_InvalidSection1, /**< A section was declared without quotes, and had extra tokens */ + SMCError_InvalidSection2, /**< A section was declared without any header */ + SMCError_InvalidSection3, /**< A section ending was declared with too many unknown tokens */ + SMCError_InvalidSection4, /**< A section ending has no matching beginning */ + SMCError_InvalidSection5, /**< A section beginning has no matching ending */ + SMCError_InvalidTokens, /**< There were too many unidentifiable strings on one line */ + SMCError_TokenOverflow, /**< The token buffer overflowed */ + SMCError_InvalidProperty1, /**< A property was declared outside of any section */ +}; + +/** + * Creates a new SMC file format parser. This is used to set parse hooks. + * + * @return A new Handle to an SMC Parse structure. + */ +native Handle:SMC_CreateParser(); + +/** + * Parses an SMC file. + * + * @param smc A Handle to an SMC Parse structure. + * @param file A string containing the file path. + * @param line An optional by reference cell to store the last line number read. + * @param col An optional by reference cell to store the last column number read. + * @return An SMCParseError result. + * @error Invalid or corrupt Handle. + */ +native SMCError:SMC_ParseFile(Handle:smc, const String:file[], &line=0, &col=0); + +/** + * Gets an error string for an SMCError code. + * @note SMCError_Okay returns false. + * @note SMCError_Custom (which is thrown on SMCParse_HaltFail) returns false. + * + * @param error The SMCParseError code. + * @param buffer A string buffer for the error (contents undefined on failure). + * @param buf_max The maximum size of the buffer. + * @return True on success, false otherwise. + */ +native bool:SMC_GetErrorString(SMCError:error, String:buffer[], buf_max); + +/** + * Called when parsing is started. + * + * @param smc The SMC Parse Handle. + * @noreturn + */ +functag SMC_ParseStart public(Handle:smc); + +/** + * Sets the SMC_ParseStart function of a parse Handle. + * + * @param smc Handle to an SMC Parse. + * @param func SMC_ParseStart function. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SMC_SetParseStart(Handle:smc, SMC_ParseStart:func); + +/** + * Called when parsing is halted. + * + * @param smc The SMC Parse Handle. + * @param halted True if abnormally halted, false otherwise. + * @param failed True if parsing failed, false otherwise. + * @noreturn + */ +functag SMC_ParseEnd public(Handle:smc, bool:halted, bool:failed); + +/** + * Sets the SMC_ParseEnd of a parse handle. + * + * @param smc Handle to an SMC Parse. + * @param func SMC_ParseEnd function. + * @noreturn + * @error Invalid or corrupt Handle. + */ +native SMC_SetParseEnd(Handle:smc, SMC_ParseEnd:func); + +/** + * Called when the parser is entering a new section or sub-section. + * @note Enclosing quotes are always stripped. + * + * @param smc The SMC Parse Handle. + * @param name String containing section name. + * @param opt_quotes True if the section name was quote-enclosed in the file. + * @return An SMCResult action to take. + */ +functag SMC_NewSection SMCResult:public(Handle:smc, const String:name[], bool:opt_quotes); + +/** + * Called when the parser finds a new key/value pair. + * @note Enclosing quotes are always stripped. + * + * @param smc The SMC Parse Handle. + * @param key String containing key name. + * @param value String containing value name. + * @param key_quotes Whether or not the key was enclosed in quotes. + * @param value_quotes Whether or not the value was enclosed in quotes. + * @return An SMCResult action to take. + */ +functag SMC_KeyValue SMCResult:public(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes); + +/** + * Called when the parser finds the end of the current section. + * + * @param smc The SMC Parse Handle. + * @return An SMCResult action to take. + */ +functag SMC_EndSection SMCResult:public(Handle:smc); + +/** + * Sets the three main reader functions. + * + * @param smc An SMC parse Handle. + * @param ns An SMC_NewSection function pointer. + * @param kv An SMC_KeyValue function pointer. + * @param es An SMC_EndSection function pointer. + * @noreturn + */ +native SMC_SetReaders(Handle:smc, SMC_NewSection:ns, SMC_KeyValue:kv, SMC_EndSection:es); + +/** + * Called whenever a raw line is read. + * + * @param smc The SMC Parse Handle. + * @param line A string containing the raw line from the file. + * @param lineno The line number it occurs on. + * @return An SMCResult action to take. + */ +functag SMC_RawLine SMCResult:public(Handle:smc, const String:line[], lineno); + +/** + * Sets a raw line reader on an SMC parser Handle. + * + * @param smc Handle to an SMC Parse. + * @param func SMC_RawLine function. + * @noreturn + */ +native SMC_SetRawLine(Handle:smc, SMC_RawLine:func); diff --git a/src/include/tf2.inc b/src/include/tf2.inc new file mode 100644 index 0000000..6b868fa --- /dev/null +++ b/src/include/tf2.inc @@ -0,0 +1,121 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: tf2.inc 2215 2008-05-29 04:06:16Z dvander $ + */ + +#if defined _tf2_included + #endinput +#endif +#define _tf2_included + +enum TFClassType +{ + TFClass_Unknown = 0, + TFClass_Scout, + TFClass_Sniper, + TFClass_Soldier, + TFClass_DemoMan, + TFClass_Medic, + TFClass_Heavy, + TFClass_Pyro, + TFClass_Spy, + TFClass_Engineer +}; + +enum TFTeam +{ + TFTeam_Unassigned = 0, + TFTeam_Spectator = 1, + TFTeam_Red = 2, + TFTeam_Blue = 3 +}; + + +/** + * Respawns a client + * + * @param client Player's index. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_RespawnPlayer(client); + +/** + * Disguises a client to the given model and team. Only has an effect on spies. + * + * Note: This only starts the disguise process and a delay occurs before the spy is fully disguised + * + * @param client Player's index. + * @param team Team to disguise the player as (only TFTeam_Red and TFTeam_Blue have an effect) + * @param class TFClassType class to disguise the player as + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_DisguisePlayer(client, TFTeam:team, TFClassType:class); + +/** + * Removes the current disguise from a client. Only has an effect on spies. + * + * @param client Player's index. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_RemovePlayerDisguise(client); + +/** + * Retrieves the entity index of the CPlayerResource entity + * + * @return The current resource entity index. + */ +native TF2_GetResourceEntity(); + +/** + * Finds the TFClassType for a given class name. + * + * @param classname A classname string such as "sniper" or "demoman" + * @return A TFClassType constant. + */ +native TFClassType:TF2_GetClass(const String:classname[]); + +/** + * Do not edit below this line! + */ +public Extension:__ext_tf2 = +{ + name = "TF2 Tools", + file = "game.tf2.ext", + autoload = 1, +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + diff --git a/src/include/tf2_stocks.inc b/src/include/tf2_stocks.inc new file mode 100644 index 0000000..f659248 --- /dev/null +++ b/src/include/tf2_stocks.inc @@ -0,0 +1,219 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: tf2_stocks.inc 2236 2008-05-31 22:30:57Z damagedsoul $ + */ + +#if defined _tf2_stocks_included + #endinput +#endif +#define _tf2_stocks_included + +#include +#include + +enum TFResourceType +{ + TFResource_Ping, + TFResource_Score, + TFResource_Deaths, + TFResource_TotalScore, + TFResource_Captures, + TFResource_Defenses, + TFResource_Dominations, + TFResource_Revenge, + TFResource_BuildingsDestroyed, + TFResource_Headshots, + TFResource_Backstabs, + TFResource_HealPoints, + TFResource_Invulns, + TFResource_Teleports, + TFResource_ResupplyPoints, + TFResource_KillAssists, + TFResource_MaxHealth, + TFResource_PlayerClass +}; + +static const String:TFResourceNames[TFResourceType][] = +{ + "m_iPing", + "m_iScore", + "m_iDeaths", + "m_iTotalScore", + "m_iCaptures", + "m_iDefenses", + "m_iDominations", + "m_iRevenge", + "m_iBuildingsDestroyed", + "m_iHeadshots", + "m_iBackstabs", + "m_iHealPoints", + "m_iInvulns", + "m_iTeleports", + "m_iResupplyPoints", + "m_iKillAssists", + "m_iMaxHealth", + "m_iPlayerClass" +}; + +/** + * Get's a Clients current class. + * + * @param client Player's index. + * @param class TFClassType to change to. + * @noreturn + * @error Invalid client index. + */ +stock TFClassType:TF2_GetPlayerClass(client) +{ + return TFClassType:GetEntProp(client, Prop_Send, "m_iClass"); +} + +/** + * Set's a Clients class. + * + * Note: If setting player class in a player spawn hook weapons should be set to false. + * + * @param client Player's index. + * @param class TFClassType class symbol. + * @param weapons This paramater is ignored. + * @param persistant If true changes the players desired class so the change stays after death. + * @noreturn + * @error Invalid client index. + */ +stock TF2_SetPlayerClass(client, TFClassType:class, bool:weapons=true, bool:persistant=true) +{ + SetEntProp(client, Prop_Send, "m_iClass", _:class); + + if (persistant) + { + SetEntProp(client, Prop_Send, "m_iDesiredPlayerClass", _:class); + } +} + +/** + * Retrieves client data from the resource entity + * + * @param client Player's index. + * @param type ResourceType constant + * @return Value or -1 on failure. + * @error Invalid client index, client not in game or failed to find resource entity. + */ +stock TF2_GetPlayerResourceData(client, TFResourceType:type) +{ + if (!IsClientConnected(client)) + { + return -1; + } + + new offset = FindSendPropInfo("CTFPlayerResource", TFResourceNames[type]); + + if (offset < 1) + { + return -1; + } + + new entity = TF2_GetResourceEntity(); + + if (entity == -1) + { + return -1; + } + + return GetEntData(entity, offset + (client*4)); +} + +/** + * Sets client data in the resource entity + * + * Note: The game overwrites these values every frame, so changing them will have very little effect. + * + * @param client Player's index. + * @param type ResourceType constant + * @param value Value to set. + * @return Value or -1 on failure. + * @error Invalid client index, client not in game or failed to find resource entity. + */ +stock bool:TF2_SetPlayerResourceData(client, TFResourceType:type, any:value) +{ + if (!IsClientConnected(client)) + { + return false; + } + + new offset = FindSendPropInfo("CTFPlayerResource", TFResourceNames[type]); + + if (offset < 1) + { + return false; + } + + new entity = TF2_GetResourceEntity(); + + if (entity == -1) + { + return false; + } + + SetEntData(entity, offset + (client*4), value); + + return true; +} + +/** + * Removes all weapons from a client's weapon slot + * + * @param client Player's index. + * @param slot Slot index (0-5) + * @noreturn + * @error Invalid client, invalid slot or lack of mod support + */ +stock TF2_RemoveWeaponSlot(client, slot) +{ + new weaponIndex; + while ((weaponIndex = GetPlayerWeaponSlot(client, slot)) != -1) + { + RemovePlayerItem(client, weaponIndex); + RemoveEdict(weaponIndex); + } +} + +/** + * Removes all weapons from a client + * + * @param client Player's index. + * @noreturn + */ +stock TF2_RemoveAllWeapons(client) +{ + for (new i = 0; i <= 5; i++) + { + TF2_RemoveWeaponSlot(client, i); + } +} diff --git a/src/include/timers.inc b/src/include/timers.inc new file mode 100644 index 0000000..1b4ac74 --- /dev/null +++ b/src/include/timers.inc @@ -0,0 +1,208 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: timers.inc 2213 2008-05-29 03:59:00Z dvander $ + */ + +#if defined _timers_included + #endinput +#endif +#define _timers_included + +#include + +#define TIMER_REPEAT (1<<0) /**< Timer will repeat until it returns Plugin_Stop */ +#define TIMER_FLAG_NO_MAPCHANGE (1<<1) /**< Timer will not carry over mapchanges */ +#define TIMER_HNDL_CLOSE (1<<9) /**< Timer will automatically call CloseHandle() on its value when finished */ + +/** + * Any of the following prototypes will work for a timed function. + */ +funcenum Timer +{ + /** + * Called when the timer interval has elapsed. + * + * @param timer Handle to the timer object. + * @param hndl Handle passed when the timer was created. + * @return Plugin_Stop to stop a repeating timer, any other value for + * default behavior. + */ + Action:public(Handle:timer, Handle:hndl), + + /** + * Called when the timer interval has elapsed. + * + * @param timer Handle to the timer object. + * @param value Value passed when the timer was created. + * @return Plugin_Stop to stop a repeating timer, any other value for + * default behavior. + */ + Action:public(Handle:timer, any:value), + + /** + * Called when the timer interval has elapsed. + * + * @param timer Handle to the timer object. + * @return Plugin_Stop to stop a repeating timer, any other value for + * default behavior. + */ + Action:public(Handle:timer), +}; + +/** + * Creates a basic timer. Calling CloseHandle() on a timer will end the timer. + * + * @param interval Interval from the current game time to execute the given function. + * @param func Function to execute once the given interval has elapsed. + * @param value Handle or value to give to the timer function. + * @param flags Flags to set (such as repeatability or auto-Handle closing). + * @return Handle to the timer object. You do not need to call CloseHandle(). + * If the timer could not be created, INVALID_HANDLE will be returned. + */ +native Handle:CreateTimer(Float:interval, Timer:func, any:value=INVALID_HANDLE, flags=0); + +/** + * Kills a timer. Use this instead of CloseHandle() if you need more options. + * + * @param autoClose If autoClose is true, the timer's value will be + * closed as a handle if TIMER_HNDL_CLOSE was not specified. + * @noreturn + */ +native KillTimer(Handle:timer, bool:autoClose=false); + +/** + * Manually triggers a timer so its function will be called. + * + * @param timer Timer Handle to trigger. + * @param reset If reset is true, the elapsed time counter is reset + * so the full interval must pass again. + * @noreturn + */ +native TriggerTimer(Handle:timer, bool:reset=false); + +/** + * Returns the simulated game time. + * + * This time is internally maintained by SourceMod and is based on the game + * tick count and tick rate. Unlike GetGameTime(), it will increment past + * map changes and while no players are connected. Unlike GetEngineTime(), + * it will not increment based on the system clock (i.e. it is still bound + * to the ticking process). + * + * @return Time based on the game tick count. + */ +native Float:GetTickedTime(); + +/** + * Returns an estimate of the time left before the map ends. If the server + * has not processed any frames yet (i.e. no players have joined the map yet), + * then the time left returned will always be infinite. + * + * @param timeleft Variable to store the time, in seconds. If the + * value is less than 0, the time limit is infinite. + * @return True if the operation is supported, false otherwise. + */ +native bool:GetMapTimeLeft(&timeleft); + +/** + * Retrieves the current map time limit. If the server has not processed any + * frames yet (i.e. no players have joined the map yet), then the time limit + * returned will always be 0. + * + * @param time Set to the number of total seconds in the map time + * limit, or 0 if there is no time limit set. + * @return True on success, false if operation is not supported. + */ +native bool:GetMapTimeLimit(&time); + +/** + * Extends the map time limit in a way that will notify all plugins. + * + * @param time Number of seconds to extend map time limit by. + * The number can be negative to decrease the time limit. + * If 0, the map will be set to have no time limit. + * @return True on success, false if operation is not supported. + */ +native bool:ExtendMapTimeLimit(time); + +/** + * Returns the number of seconds in between game server ticks. + * + * Note: A tick, in this context, is a frame. + * + * @return Number of seconds in between ticks. + */ +native Float:GetTickInterval(); + +/** + * Notification that the map's time left has changed via a change in the time + * limit or a change in the game rules (such as mp_restartgame). This is useful + * for plugins trying to create timers based on the time left in the map. + * + * Calling ExtendMapTimeLimit() from here, without proper precaution, will + * cause infinite recursion. + * + * If the operation is not supported, this will never be called. + + * If the server has not yet processed any frames (i.e. no players have joined + * the map yet), then this will be called once the server begins ticking, even + * if there is no time limit set. + */ +forward OnMapTimeLeftChanged(); + +/** + * Returns whether or not the server is processing frames or not. + * + * The server does not process frames until at least one client joins the game. + * Once the first player has in, even if that player, leaves, the server's + * timers and entities will work. + * + * @return True if the server is ticking, false otherwise. + */ +native bool:IsServerProcessing(); + +/** + * Creates a timer associated with a new data pack, and returns the datapack. + * @note The datapack is automatically freed when the timer ends. + * @note The position of the datapack is not reset or changed for the timer function. + * + * @param interval Interval from the current game time to execute the given function. + * @param func Function to execute once the given interval has elapsed. + * @param data The newly created datapack is passed though this by-reference parameter. + * @param flags Timer flags. + * @return Handle to the timer object. You do not need to call CloseHandle(). + */ +stock Handle:CreateDataTimer(Float:interval, Timer:func, &Handle:data, flags=0) +{ + data = CreateDataPack(); + flags |= TIMER_HNDL_CLOSE; + return CreateTimer(interval, func, data, flags); +} + diff --git a/src/include/topmenus.inc b/src/include/topmenus.inc new file mode 100644 index 0000000..44776a5 --- /dev/null +++ b/src/include/topmenus.inc @@ -0,0 +1,290 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: topmenus.inc 2086 2008-04-19 10:34:56Z pred $ + */ + +#if defined _topmenus_included + #endinput +#endif +#define _topmenus_included + +#include + +/** + * Actions a top menu will take on an object. + */ +enum TopMenuAction +{ + /** + * An option is being drawn for a menu (or for sorting purposes). + * + * INPUT : TopMenu Handle, object ID, client index. + * OUTPUT: Buffer for rendering, maxlength of buffer. + */ + TopMenuAction_DisplayOption = 0, + + /** + * The title of a menu is being drawn for a given object. + * + * Note: The Object ID will be INVALID_TOPMENUOBJECT if drawing the + * root title. Otherwise, the Object ID is a category. + * + * INPUT : TopMenu Handle, object ID, client index. + * OUTPUT: Buffer for rendering, maxlength of buffer. + */ + TopMenuAction_DisplayTitle = 1, + + /** + * A menu option has been selected. + * + * The Object ID will always be an item (not a category). + * + * INPUT : TopMenu Handle, object ID, client index. + */ + TopMenuAction_SelectOption = 2, + + /** + * A menu option is being drawn and its flags can be overridden. + * + * INPUT : TopMenu Handle, object ID, client index. + * OUTPUT: The first byte of the 'buffer' string should be set + * to the desired flags. By default, it will contain + * ITEMDRAW_DEFAULT. + */ + TopMenuAction_DrawOption = 3, + + /** + * Called when an object is being removed from the menu. + * This can be used to clean up data stored in the info string. + * + * INPUT : TopMenu Handle, object ID. + */ + TopMenuAction_RemoveObject = 4, +}; + +/** + * Top menu object types. + */ +enum TopMenuObjectType +{ + TopMenuObject_Category = 0, /**< Category (sub-menu branching from root) */ + TopMenuObject_Item = 1 /**< Item on a sub-menu */ +}; + +/** + * Top menu starting positions for display. + */ +enum TopMenuPosition +{ + TopMenuPosition_Start = 0, /**< Start/root of the menu */ + TopMenuPosition_LastRoot = 1, /**< Last position in the root menu */ + TopMenuPosition_LastCategory = 3, /**< Last position in their last category */ +}; + +/** + * Top menu object tag for type checking. + */ +enum TopMenuObject +{ + INVALID_TOPMENUOBJECT = 0, +}; + +/** + * TopMenu callback prototype. + * + * @param topmenu Handle to the TopMenu. + * @param action TopMenuAction being performed. + * @param object_id The object ID (if used). + * @param param Extra parameter (if used). + * @param buffer Output buffer (if used). + * @param maxlength Output buffer (if used). + * @noreturn + */ +functag TopMenuHandler public(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength); + +/** + * Creates a TopMenu. + * + * @param handler Handler to use for drawing the root title. + * @return A new TopMenu Handle, or INVALID_HANDLE on failure. + */ +native Handle:CreateTopMenu(TopMenuHandler:handler); + +/** + * Re-sorts the items in a TopMenu via a configuration file. + * + * The format of the configuration file should be a Valve Key-Values + * formatted file that SourceMod can parse. There should be one root + * section, and one sub-section for each category. Each sub-section's + * name should match the category name. + * + * Each sub-section may only contain key/value pairs in the form of: + * key: "item" + * value: Name of the item as passed to AddToTopMenu(). + * + * The TopMenu will draw items in the order declared in the configuration + * file. If items do not appear in the configuration file, they are sorted + * per-player based on how the handler function renders for that player. + * These items appear after the configuration sorted items. + * + * @param topmenu TopMenu Handle. + * @param file File path. + * @param error Error buffer. + * @param maxlength Maximum size of the error buffer. + * Error buffer will be filled with a + * zero-terminated string if false is + * returned. + * @return True on success, false on failure. + * @error Invalid TopMenu Handle. + */ +native bool:LoadTopMenuConfig(Handle:topmenu, const String:file[], String:error[], maxlength); + +/** + * Adds an object to a TopMenu. + * + * @param topmenu TopMenu Handle. + * @param name Object name (MUST be unique). + * @param type Object type. + * @param handler Handler for object. + * @param cmdname Command name (for access overrides). + * @param flags Default access flags. + * @param parent Parent object ID, or INVALID_TOPMENUOBJECT for none. + * Items must have a category parent. + * Categories must not have a parent. + * @param info_string Arbitrary storage (max 255 bytes). + * @return A new TopMenuObject ID, or INVALID_TOPMENUOBJECT on + * failure. + * @error Invalid TopMenu Handle. + */ +native TopMenuObject:AddToTopMenu(Handle:topmenu, + const String:name[], + TopMenuObjectType:type, + TopMenuHandler:handler, + TopMenuObject:parent, + const String:cmdname[]="", + flags=0, + const String:info_string[]=""); + +/** + * Retrieves the info string of a top menu item. + * + * @param topmenu TopMenu Handle. + * @param object TopMenuObject ID. + * @param buffer Buffer to store info string. + * @param maxlength Maximum size of info string. + * @return Number of bytes written, not including the + * null terminator. + * @error Invalid TopMenu Handle or TopMenuObject ID. + */ +native GetTopMenuInfoString(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength); + +/** + * Retrieves the name string of a top menu item. + * + * @param topmenu TopMenu Handle. + * @param object TopMenuObject ID. + * @param buffer Buffer to store info string. + * @param maxlength Maximum size of info string. + * @return Number of bytes written, not including the + * null terminator. + * @error Invalid TopMenu Handle or TopMenuObject ID. + */ +native GetTopMenuObjName(Handle:topmenu, TopMenuObject:object, String:buffer[], maxlength); + +/** + * Removes an object from a TopMenu. + * + * Plugins' objects are automatically removed all TopMenus when the given + * plugin unloads or pauses. In the case of unpausing, all items are restored. + * + * @param topmenu TopMenu Handle. + * @param object TopMenuObject ID. + * @noreturn + * @error Invalid TopMenu Handle. + */ +native RemoveFromTopMenu(Handle:topmenu, TopMenuObject:object); + +/** + * Displays a TopMenu to a client. + * + * @param topmenu TopMenu Handle. + * @param client Client index. + * @param position Position to display from. + * @return True on success, false on failure. + * @error Invalid TopMenu Handle or client not in game. + */ +native bool:DisplayTopMenu(Handle:topmenu, client, TopMenuPosition:position); + +/** + * Finds a category's object ID in a TopMenu. + * + * @param topmenu TopMenu Handle. + * @param name Object's unique name. + * @return TopMenuObject ID on success, or + * INVALID_TOPMENUOBJECT on failure. + * @error Invalid TopMenu Handle. + */ +native TopMenuObject:FindTopMenuCategory(Handle:topmenu, const String:name[]); + +/** + * Do not edit below this line! + */ +public Extension:__ext_topmenus = +{ + name = "TopMenus", + file = "topmenus.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_topmenus_SetNTVOptional() +{ + MarkNativeAsOptional("CreateTopMenu"); + MarkNativeAsOptional("LoadTopMenuConfig"); + MarkNativeAsOptional("AddToTopMenu"); + MarkNativeAsOptional("RemoveFromTopMenu"); + MarkNativeAsOptional("DisplayTopMenu"); + MarkNativeAsOptional("FindTopMenuCategory"); +} +#endif diff --git a/src/include/usermessages.inc b/src/include/usermessages.inc new file mode 100644 index 0000000..7d0c1b8 --- /dev/null +++ b/src/include/usermessages.inc @@ -0,0 +1,200 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: usermessages.inc 1943 2008-03-16 23:16:34Z dvander $ + */ + +#if defined _eventsmsgs_included + #endinput +#endif +#define _eventsmsgs_included + +/** + * UserMsg helper values. + */ +enum UserMsg +{ + INVALID_MESSAGE_ID = -1, +}; + +/** + * @section Message Flags. + */ +#define USERMSG_RELIABLE (1<<2) /**< Message will be set on the reliable stream */ +#define USERMSG_INITMSG (1<<3) /**< Message will be considered to be an initmsg */ +#define USERMSG_BLOCKHOOKS (1<<7) /**< Prevents the message from triggering SourceMod and Metamod hooks */ + +/** + * @endsection + */ + +/** + * Returns the ID of a given message, or -1 on failure. + * + * @param msg String containing message name (case sensitive). + * @return A message index, or INVALID_MESSAGE_ID on failure. + */ +native UserMsg:GetUserMessageId(const String:msg[]); + +/** + * Retrieves the name of a message by ID. + * + * @param msg_id Message index. + * @param msg Buffer to store the name of the message. + * @param maxlength Maximum length of string buffer. + * @return True if message index is valid, false otherwise. + */ +native bool:GetUserMessageName(UserMsg:msg_id, String:msg[], maxlength); + +/** + * Starts a usermessage (network message). + * @note Only one message can be active at a time. + * @note It is illegal to send any message while a non-intercept hook is in progress. + * + * @param msgname Message name to start. + * @param clients Array containing player indexes to broadcast to. + * @param numClients Number of players in the array. + * @param flags Optional flags to set. + * @return A handle to a bf_write bit packing structure, or + * INVALID_HANDLE on failure. + * @error Invalid message name or unable to start a message. + */ +native Handle:StartMessage(String:msgname[], clients[], numClients, flags=0); + +/** + * Starts a usermessage (network message). + * @note Only one message can be active at a time. + * @note It is illegal to send any message while a non-intercept hook is in progress. + * + * @param msg Message index to start. + * @param clients Array containing player indexes to broadcast to. + * @param numClients Number of players in the array. + * @param flags Optional flags to set. + * @return A handle to a bf_write bit packing structure, or + * INVALID_HANDLE on failure. + * @error Invalid message name or unable to start a message. + */ +native Handle:StartMessageEx(UserMsg:msg, clients[], numClients, flags=0); + +/** + * Ends a previously started user message (network message). + * + * @noreturn + */ +native EndMessage(); + +/** + * Called when a message is hooked + * + * @param msg_id Message index. + * @param bf Handle to the input bit buffer of the message. + * @param players Array containing player indexes. + * @param playersNum Number of players in the array. + * @param reliable True if message is reliable, false otherwise. + * @param init True if message is an initmsg, false otherwise. + * @return Ignored for normal hooks. For intercept hooks, Plugin_Handled + * blocks the message from being sent, and Plugin_Continue + * resumes normal functionality. + */ +functag MsgHook Action:public(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init); + +/** + * Called when a message is finished sending. + * + * @param msg_id Message index. + */ +functag MsgSentNotify public(UserMsg:msg_id); + +/** + * Hooks a user message. + * + * @param msg_id Message index. + * @param hook Function to use as a hook. + * @param intercept If intercept is true, message will be fully intercepted, + * allowing the user to block the message. Otherwise, + * the hook is normal and ignores the return value. + * @param notify Notification function. + * @noreturn + * @error Invalid message index. + */ +native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false, MsgSentNotify:notify=MsgSentNotify:-1); + +/** + * Removes one usermessage hook. + * + * @param msg_id Message index. + * @param hook Function used for the hook. + * @param intercept Specifies whether the hook was an intercept hook or not. + * @noreturn + * @error Invalid message index. + */ +native UnhookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false); + +/** + * Starts a usermessage (network message) that broadcasts to all clients. + * @note See StartMessage or StartMessageEx(). + * + * @param msgname Message name to start. + * @param flags Optional flags to set. + * @return A handle to a bf_write bit packing structure, or + * INVALID_HANDLE on failure. + */ +stock Handle:StartMessageAll(String:msgname[], flags=0) +{ + new maxClients = GetMaxClients(); + new total = 0; + new clients[maxClients]; + for (new i=1; i<=maxClients; i++) + { + if (IsClientConnected(i)) + { + clients[total++] = i; + } + } + return StartMessage(msgname, clients, total, flags); +} + +/** + * Starts a simpler usermessage (network message) for one client. + * @note See StartMessage or StartMessageEx(). + * + * @param msgname Message name to start. + * @param client Client to send to. + * @param flags Optional flags to set. + * @return A handle to a bf_write bit packing structure, or + * INVALID_HANDLE on failure. + */ +stock Handle:StartMessageOne(String:msgname[], client, flags=0) +{ + new players[1]; + + players[0] = client; + + return StartMessage(msgname, players, 1, flags); +} diff --git a/src/include/vector.inc b/src/include/vector.inc new file mode 100644 index 0000000..da8f9a7 --- /dev/null +++ b/src/include/vector.inc @@ -0,0 +1,188 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: vector.inc 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#if defined _vector_included + #endinput +#endif +#define _vector_included + +/** + * Calculates a vector's length. + * + * @param vec Vector. + * @param squared If true, the result will be squared (for optimization). + * @return Vector length (magnitude). + */ +native Float:GetVectorLength(const Float:vec[3], bool:squared=false); + +/** + * Calculates the distance between two vectors. + * + * @param vec1 First vector. + * @param vec2 Second vector. + * @param squared If true, the result will be squared (for optimization). + * @return Vector distance. + */ +native Float:GetVectorDistance(const Float:vec1[3], const Float:vec2[3], bool:squared=false); + +/** + * Calculates the dot product of two vectors. + * + * @param vec1 First vector. + * @param vec2 Second vector. + * @return Dot product of the two vectors. + */ +native Float:GetVectorDotProduct(const Float:vec1[3], const Float:vec2[3]); + +/** + * Computes the cross product of two vectors. Any input array can be the same + * as the output array. + * + * @param vec1 First vector. + * @param vec2 Second vector. + * @param result Resultant vector. + * @noreturn + */ +native GetVectorCrossProduct(const Float:vec1[3], const Float:vec2[3], Float:result[3]); + +/** + * Normalizes a vector. The input array can be the same as the output array. + * + * @param vec Vector. + * @param result Resultant vector. + * @return Vector length. + */ +native Float:NormalizeVector(const Float:vec[3], Float:result[3]); + +/** + * Returns vectors in the direction of an angle. + * + * @param angle Angle. + * @param fwd Forward vector buffer or NULL_VECTOR. + * @param right Right vector buffer or NULL_VECTOR. + * @param up Up vector buffer or NULL_VECTOR. + * @noreturn + */ +native GetAngleVectors(const Float:angle[3], Float:fwd[3], Float:right[3], Float:up[3]); + +/** + * Returns angles from a vector. + * + * @param vec Vector. + * @param angle Angle buffer. + * @noreturn + */ +native GetVectorAngles(const Float:vec[3], Float:angle[3]); + +/** + * Returns direction vectors from a vector. + * + * @param vec Vector. + * @param right Right vector buffer or NULL_VECTOR. + * @param up Up vector buffer or NULL_VECTOR. + * @noreturn + */ +native GetVectorVectors(const Float:vec[3], Float:right[3], Float:up[3]); + +/** + * Adds two vectors. It is safe to use either input buffer as an output + * buffer. + * + * @param vec1 First vector. + * @param vec2 Second vector. + * @param result Result buffer. + * @noreturn + */ +stock AddVectors(const Float:vec1[3], const Float:vec2[3], Float:result[3]) +{ + result[0] = vec1[0] + vec2[0]; + result[1] = vec1[1] + vec2[1]; + result[2] = vec1[2] + vec2[2]; +} + +/** + * Subtracts a vector from another vector. It is safe to use either input + * buffer as an output buffer. + * + * @param vec1 First vector. + * @param vec2 Second vector to subtract from first. + * @param result Result buffer. + * @noreturn + */ +stock SubtractVectors(const Float:vec1[3], const Float:vec2[3], Float:result[3]) +{ + result[0] = vec1[0] - vec2[0]; + result[1] = vec1[1] - vec2[1]; + result[2] = vec1[2] - vec2[2]; +} + +/** + * Scales a vector. + * + * @param vec Vector. + * @param scale Scale value. + * @noreturn + */ +stock ScaleVector(Float:vec[3], Float:scale) +{ + vec[0] *= scale; + vec[1] *= scale; + vec[2] *= scale; +} + +/** + * Negatives a vector. + * + * @param vec Vector. + * @noreturn + */ +stock NegateVector(Float:vec[3]) +{ + vec[0] = -vec[0]; + vec[1] = -vec[1]; + vec[2] = -vec[2]; +} + +/** + * Builds a vector from two points by subtracting the points. + * + * @param pt1 First point (to be subtracted from the second). + * @param pt2 Second point. + * @param output Output vector buffer. + * @noreturn + */ +stock MakeVectorFromPoints(const Float:pt1[3], const Float:pt2[3], Float:output[3]) +{ + output[0] = pt2[0] - pt1[0]; + output[1] = pt2[1] - pt1[1]; + output[2] = pt2[2] - pt1[2]; +} diff --git a/src/include/version.inc b/src/include/version.inc new file mode 100644 index 0000000..ee3a51f --- /dev/null +++ b/src/include/version.inc @@ -0,0 +1,42 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: version.inc 2534 2008-09-14 05:13:21Z dvander $ + */ + +#if defined _version_included + #endinput +#endif +#define _version_included + +#define SOURCEMOD_V_MAJOR 1 /**< SourceMod Major version */ +#define SOURCEMOD_V_MINOR 0 /**< SourceMod Minor version */ +#define SOURCEMOD_V_RELEASE 4 /**< SourceMod Release version */ + +#define SOURCEMOD_VERSION "1.0.4" /**< SourceMod version string (major.minor.release.build) */ diff --git a/src/include/zr.inc b/src/include/zr.inc new file mode 100644 index 0000000..fa656b6 --- /dev/null +++ b/src/include/zr.inc @@ -0,0 +1,42 @@ +/** + * ==================== + * Zombie:Reloaded + * File: zr.inc + * Author: Greyscale + * ==================== + */ + +/** + * Used to check if a player is a zombie. + * @param client Client index. + * @return True if the player is a zombie, and false if human. + */ +native bool:ZR_IsClientZombie(client); + +/** + * Returns the client's zombie class. + * @param client Client index. + * @return The client's zombie class. + */ +native ZR_GetClientZClass(client); + +/** + * Returns if the zombie has spawned or not. + * @return Returns true, if the first zombie has spawned during the round, false if not. + */ +native bool:ZR_HasZombieSpawned(); + +/** + * Called when a player turns into a zombie. + * @param client Client index. + * @param mother True if zombie is a mother, false if zombified by another zombie. + */ +forward ZR_Zombify(client, bool:mother); + +/** + * Called when a player changes their zombie class. + * @param client Client index. + * @param oldclass The client's old class. + * @param newclass The client's new class. + */ +forward ZR_OnZClassChanged(client, oldclass, newclass); \ No newline at end of file diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp new file mode 100644 index 0000000..51e591d --- /dev/null +++ b/src/zombiereloaded.sp @@ -0,0 +1,209 @@ +/** + * ==================== + * Zombie:Reloaded + * File: zombiereloaded.sp + * Author: Greyscale + * ==================== + */ + +#pragma semicolon 1 +#include +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include + +#define VERSION "2.5.1" + +#include "zr/zombiereloaded" +#include "zr/global" +#include "zr/cvars" +#include "zr/translation" +#include "zr/offsets" +#include "zr/ambience" +#include "zr/classes" +#include "zr/models" +#include "zr/overlays" +#include "zr/zombie" +#include "zr/menu" +#include "zr/sayhooks" +#include "zr/weaponrestrict" +#include "zr/damagecontrol" +#include "zr/commands" +#include "zr/event" + +public Plugin:myinfo = +{ + name = "Zombie:Reloaded", + author = "Greyscale", + description = "Infection/survival style gameplay", + version = VERSION, + url = "" +}; + +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +{ + CreateGlobals(); + + return true; +} + +public OnPluginStart() +{ + LoadTranslations("common.phrases.txt"); + LoadTranslations("zombiereloaded.phrases.txt"); + + // ====================================================================== + + ZR_PrintToServer("Plugin loading"); + + // ====================================================================== + + HookEvents(); + HookChatCmds(); + CreateCvars(); + HookCvars(); + CreateCommands(); + HookCommands(); + FindOffsets(); + SetupGameData(); + InitWeaponRestrict(); + InitDmgControl(); + + // ====================================================================== + + market = LibraryExists("market"); + + // ====================================================================== + + CreateConVar("gs_zombiereloaded_version", VERSION, "[ZR] Current version of this plugin", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_UNLOGGED|FCVAR_DONTRECORD|FCVAR_REPLICATED|FCVAR_NOTIFY); + + // ====================================================================== + + ZR_PrintToServer("Plugin loaded"); +} + +public OnPluginEnd() +{ + ZREnd(); +} + +public OnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, "market")) + { + market = false; + } +} + +public OnLibraryAdded(const String:name[]) +{ + if (StrEqual(name, "market")) + { + market = true; + } +} + +public OnMapStart() +{ + MapChangeCleanup(); + + LoadModelData(); + LoadDownloadData(); +} + +public OnConfigsExecuted() +{ + FindMapSky(); + + LoadClassData(); + LoadAmbienceData(); + + decl String:mapconfig[PLATFORM_MAX_PATH]; + + GetCurrentMap(mapconfig, sizeof(mapconfig)); + Format(mapconfig, sizeof(mapconfig), "sourcemod/zombiereloaded/%s.cfg", mapconfig); + + decl String:path[PLATFORM_MAX_PATH]; + Format(path, sizeof(path), "cfg/%s", mapconfig); + + if (FileExists(path)) + { + ServerCommand("exec %s", mapconfig); + } +} + +public OnClientPutInServer(client) +{ + pClass[client] = 0; + gBlockMotherInfect[client] = false; + + bZVision[client] = !IsFakeClient(client); + + new bool:zhp = GetConVarBool(gCvars[CVAR_ZHP_DEFAULT]); + dispHP[client] = zhp; + + ClientHookUse(client); + ClientHookAttack(client); + + FindClientDXLevel(client); + + for (new x = 0; x < MAXTIMERS; x++) + { + tHandles[client][x] = INVALID_HANDLE; + } +} + +public OnClientDisconnect(client) +{ + ClientUnHookUse(client); + ClientUnHookAttack(client); + + PlayerLeft(client); + + for (new x = 0; x < MAXTIMERS; x++) + { + if (tHandles[client][x] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][x]); + tHandles[client][x] = INVALID_HANDLE; + } + } +} + +MapChangeCleanup() +{ + ClearArray(restrictedWeapons); + + tRound = INVALID_HANDLE; + tInfect = INVALID_HANDLE; + tAmbience = INVALID_HANDLE; +} + +ZREnd() +{ + TerminateRound(3.0, Game_Commencing); + + UnhookCvars(); + UnhookEvents(); + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x)) + { + continue; + } + + for (new y = 0; y < MAXTIMERS; y++) + { + if (tHandles[x][y] != INVALID_HANDLE) + { + CloseHandle(tHandles[x][y]); + tHandles[x][y] = INVALID_HANDLE; + } + } + } +} \ No newline at end of file diff --git a/src/zr/ambience.inc b/src/zr/ambience.inc new file mode 100644 index 0000000..91f3a61 --- /dev/null +++ b/src/zr/ambience.inc @@ -0,0 +1,97 @@ +/** + * ==================== + * Zombie:Reloaded + * File: ambience.inc + * Author: Greyscale + * ==================== + */ + +new bool:soundValid = false; + +new Handle:tAmbience = INVALID_HANDLE; + +LoadAmbienceData() +{ + new bool:ambience = GetConVarBool(gCvars[CVAR_AMBIENCE]); + if (!ambience) + { + return; + } + + decl String:sound[64]; + GetConVarString(gCvars[CVAR_AMBIENCE_FILE], sound, sizeof(sound)); + Format(sound, sizeof(sound), "sound/%s", sound); + + soundValid = FileExists(sound, true); + + if (soundValid) + { + AddFileToDownloadsTable(sound); + } + else + { + ZR_LogMessage("Ambient sound load failed", sound); + } +} + +RestartAmbience() +{ + if (tAmbience != INVALID_HANDLE) + { + CloseHandle(tAmbience); + } + + CreateTimer(0.0, AmbienceLoop, _, TIMER_FLAG_NO_MAPCHANGE); +} + +public Action:AmbienceLoop(Handle:timer) +{ + new bool:ambience = GetConVarBool(gCvars[CVAR_AMBIENCE]); + + if (!ambience || !soundValid) + { + return; + } + + decl String:sound[64]; + GetConVarString(gCvars[CVAR_AMBIENCE_FILE], sound, sizeof(sound)); + + EmitAmbience(sound); + + new Float:delay = GetConVarFloat(gCvars[CVAR_AMBIENCE_LENGTH]); + tAmbience = CreateTimer(delay, AmbienceLoop, _, TIMER_FLAG_NO_MAPCHANGE); +} + +StopAmbience() +{ + new bool:ambience = GetConVarBool(gCvars[CVAR_AMBIENCE]); + + if (!ambience) + { + return; + } + + decl String:sound[64]; + GetConVarString(gCvars[CVAR_AMBIENCE_FILE], sound, sizeof(sound)); + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x)) + { + continue; + } + + StopSound(x, SNDCHAN_AUTO, sound); + } +} + +EmitAmbience(const String:sound[]) +{ + PrecacheSound(sound); + + StopAmbience(); + + new Float:volume = GetConVarFloat(gCvars[CVAR_AMBIENCE_VOLUME]); + EmitSoundToAll(sound, SOUND_FROM_PLAYER, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_NOFLAGS, volume, SNDPITCH_NORMAL, -1, NULL_VECTOR, NULL_VECTOR, true, 0.0); +} \ No newline at end of file diff --git a/src/zr/classes.inc b/src/zr/classes.inc new file mode 100644 index 0000000..b9cc736 --- /dev/null +++ b/src/zr/classes.inc @@ -0,0 +1,295 @@ +/** + * ==================== + * Zombie:Reloaded + * File: classes.inc + * Author: Greyscale + * ==================== + */ + +enum ZR_ClassOptions +{ + String:data_name[64], + String:data_model[256], + String:data_menu_description[256], + String:data_zvision[256], + data_health, + Float:data_speed, + Float:data_jump_distance, + Float:data_jump_height, + Float:data_knockback, + bool:data_nvgs[8], + data_fov, + bool:data_regen, + data_regen_health, + Float:data_regen_interval, + bool:data_napalm, + Float:data_napalm_time, + bool:data_nofalldamage, + data_kill_bonus, + data_infect_health +} + +#define MAXCLASSES 20 + +new Handle:kvClasses = INVALID_HANDLE; + +new arrayClasses[MAXCLASSES][ZR_ClassOptions]; +new classCount; + +LoadClassData() +{ + if (kvClasses != INVALID_HANDLE) + { + CloseHandle(kvClasses); + } + + kvClasses = CreateKeyValues("classes"); + + decl String:path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "configs/zr/classes.txt"); + + if (!FileToKeyValues(kvClasses, path)) + { + SetFailState("\"%s\" missing from server", path); + } + + KvRewind(kvClasses); + if (!KvGotoFirstSubKey(kvClasses)) + { + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + SetConVarBool(gCvars[CVAR_CLASSES], false); + + ZR_LogMessage("Class auto-disable", path); + } + } + + decl String:name[64]; + decl String:model[256]; + decl String:menu_description[256]; + decl String:zvision[256]; + + classCount = 0; + + do + { + KvGetString(kvClasses, "name", name, sizeof(name)); + strcopy(arrayClasses[classCount][data_name], 64, name); + + KvGetString(kvClasses, "model", model, sizeof(model)); + strcopy(arrayClasses[classCount][data_model], 256, model); + + KvGetString(kvClasses, "menu_description", menu_description, sizeof(menu_description)); + strcopy(arrayClasses[classCount][data_menu_description], 256, menu_description); + + decl String:cvar_zvision[256]; + GetConVarString(gCvars[CVAR_ZOMBIE_ZVISION], cvar_zvision, sizeof(cvar_zvision)); + + KvGetString(kvClasses, "zvision", zvision, sizeof(zvision), cvar_zvision); + strcopy(arrayClasses[classCount][data_zvision], 256, zvision); + + arrayClasses[classCount][data_health] = KvGetNum(kvClasses, "health"), GetConVarInt(gCvars[CVAR_ZOMBIE_HEALTH]); + arrayClasses[classCount][data_speed] = KvGetFloat(kvClasses, "speed"), GetConVarFloat(gCvars[CVAR_ZOMBIE_SPEED]); + arrayClasses[classCount][data_jump_distance] = KvGetFloat(kvClasses, "jump_distance"), GetConVarFloat(gCvars[CVAR_ZOMBIE_JUMP_DISTANCE]); + arrayClasses[classCount][data_jump_height] = KvGetFloat(kvClasses, "jump_height"), GetConVarFloat(gCvars[CVAR_ZOMBIE_JUMP_HEIGHT]); + arrayClasses[classCount][data_knockback] = KvGetFloat(kvClasses, "knockback"), GetConVarFloat(gCvars[CVAR_ZOMBIE_KNOCKBACK]); + arrayClasses[classCount][data_nvgs] = bool:KvGetNum(kvClasses, "nvgs"), GetConVarBool(gCvars[CVAR_ZOMBIE_NVGS]); + arrayClasses[classCount][data_fov] = KvGetNum(kvClasses, "fov"), GetConVarInt(gCvars[CVAR_ZOMBIE_FOV]); + arrayClasses[classCount][data_regen] = bool:KvGetNum(kvClasses, "regen"), GetConVarBool(gCvars[CVAR_ZOMBIE_REGEN]); + arrayClasses[classCount][data_regen_health] = KvGetNum(kvClasses, "regen_health"), GetConVarInt(gCvars[CVAR_ZOMBIE_REGEN_HEALTH]); + arrayClasses[classCount][data_regen_interval] = KvGetFloat(kvClasses, "regen_interval"), GetConVarFloat(gCvars[CVAR_ZOMBIE_REGEN_INTERVAL]); + arrayClasses[classCount][data_napalm] = bool:KvGetNum(kvClasses, "napalm"), GetConVarBool(gCvars[CVAR_ZOMBIE_NAPALM]); + arrayClasses[classCount][data_napalm_time] = KvGetFloat(kvClasses, "napalm_time"), GetConVarFloat(gCvars[CVAR_ZOMBIE_NAPALM_TIME]); + arrayClasses[classCount][data_nofalldamage] = bool:KvGetNum(kvClasses, "nofalldamage"), GetConVarBool(gCvars[CVAR_ZOMBIE_NOFALLDAMAGE]); + arrayClasses[classCount][data_kill_bonus] = KvGetNum(kvClasses, "kill_bonus"), GetConVarInt(gCvars[CVAR_ZOMBIE_KILL_BONUS]); + arrayClasses[classCount][data_infect_health] = KvGetNum(kvClasses, "infect_health"), GetConVarInt(gCvars[CVAR_ZOMBIE_INFECT_HEALTH]); + + classCount++; + } while (KvGotoNextKey(kvClasses)); +} + +GetClassName(classindex, String:name[], maxlen) +{ + strcopy(name, maxlen, arrayClasses[classindex][data_name]); +} + +GetClassModel(classindex, String:model[], maxlen) +{ + strcopy(model, maxlen, arrayClasses[classindex][data_model]); +} + +GetClassMenuDescription(classindex, String:menudescription[], maxlen) +{ + strcopy(menudescription, maxlen, arrayClasses[classindex][data_menu_description]); +} + +GetClassZVision(classindex, String:zvision[], maxlen) +{ + strcopy(zvision, maxlen, arrayClasses[classindex][data_zvision]); +} + +GetClassHealth(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_health]; + } + + return GetConVarInt(gCvars[CVAR_ZOMBIE_HEALTH]); +} + +Float:GetClassSpeed(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_speed]; + } + + return GetConVarFloat(gCvars[CVAR_ZOMBIE_SPEED]); +} + +Float:GetClassJumpDistance(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_jump_distance]; + } + + return GetConVarFloat(gCvars[CVAR_ZOMBIE_JUMP_DISTANCE]); +} + +Float:GetClassJumpHeight(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_jump_height]; + } + + return GetConVarFloat(gCvars[CVAR_ZOMBIE_JUMP_HEIGHT]); +} + +Float:GetClassKnockback(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_knockback]; + } + + return GetConVarFloat(gCvars[CVAR_ZOMBIE_KNOCKBACK]); +} + +bool:GetClassNVGs(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_nvgs]; + } + + return GetConVarBool(gCvars[CVAR_ZOMBIE_NVGS]); +} + +GetClassFOV(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_fov]; + } + + return GetConVarInt(gCvars[CVAR_ZOMBIE_FOV]); +} + +bool:GetClassRegen(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_regen]; + } + + return GetConVarBool(gCvars[CVAR_ZOMBIE_REGEN]); +} + +GetClassRegenHealth(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_regen_health]; + } + + return GetConVarInt(gCvars[CVAR_ZOMBIE_REGEN_HEALTH]); +} + +Float:GetClassRegenInterval(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_regen_interval]; + } + + return GetConVarFloat(gCvars[CVAR_ZOMBIE_REGEN_INTERVAL]); +} + +bool:GetClassNapalm(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_napalm]; + } + + return GetConVarBool(gCvars[CVAR_ZOMBIE_NAPALM]); +} + +Float:GetClassNapalmTime(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_napalm_time]; + } + + return GetConVarFloat(gCvars[CVAR_ZOMBIE_NAPALM_TIME]); +} + +bool:GetClassNoFallDamage(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_nofalldamage]; + } + + return GetConVarBool(gCvars[CVAR_ZOMBIE_NOFALLDAMAGE]); +} + +GetClassKillBonus(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_kill_bonus]; + } + + return GetConVarInt(gCvars[CVAR_ZOMBIE_KILL_BONUS]); +} + +GetClassInfectHealth(classindex) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + return arrayClasses[classindex][data_infect_health]; + } + + return GetConVarInt(gCvars[CVAR_ZOMBIE_INFECT_HEALTH]); +} \ No newline at end of file diff --git a/src/zr/commands.inc b/src/zr/commands.inc new file mode 100644 index 0000000..93dfc54 --- /dev/null +++ b/src/zr/commands.inc @@ -0,0 +1,118 @@ +/** + * ==================== + * Zombie:Reloaded + * File: commands.inc + * Author: Greyscale + * ==================== + */ + +CreateCommands() +{ + RegAdminCmd("zr_infect", Command_Infect, ADMFLAG_GENERIC, "Infects the specified player"); + RegAdminCmd("zr_spawn", Command_Respawn, ADMFLAG_GENERIC, "Respawns the specified player following auto-respawning rules"); + + RegAdminCmd("zr_restrict", Command_Restrict, ADMFLAG_GENERIC, "Restrict a specified weapon"); + RegAdminCmd("zr_unrestrict", Command_UnRestrict, ADMFLAG_GENERIC, "Unrestrict a specified weapon"); +} + +public Action:Command_Infect(client, argc) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (argc < 1 || !enabled) + { + return Plugin_Handled; + } + + decl String:arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + + decl String:target_name[MAX_TARGET_LENGTH]; + new targets[MAXPLAYERS]; + new bool:tn_is_ml; + + new tcount = ProcessTargetString(arg1, client, targets, MAXPLAYERS, COMMAND_FILTER_ALIVE, target_name, sizeof(target_name), tn_is_ml); + if (tcount <= 0) + { + ReplyToTargetError(client, tcount); + return Plugin_Handled; + } + + for (new x = 0; x < tcount; x++) + { + Zombify(targets[x], 0); + } + + return Plugin_Handled; +} + +public Action:Command_Respawn(client, argc) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (argc < 1 || !enabled) + { + return Plugin_Handled; + } + + decl String:arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + + decl String:target_name[MAX_TARGET_LENGTH]; + new targets[MAXPLAYERS]; + new bool:tn_is_ml; + + new tcount = ProcessTargetString(arg1, client, targets, MAXPLAYERS, COMMAND_FILTER_DEAD, target_name, sizeof(target_name), tn_is_ml); + if (tcount <= 0) + { + ReplyToTargetError(client, tcount); + return Plugin_Handled; + } + + for (new x = 0; x < tcount; x++) + { + RespawnPlayer(targets[x]); + } + + return Plugin_Handled; +} + +public Action:Command_Restrict(client, argc) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (argc < 1 || !enabled) + { + return Plugin_Handled; + } + + decl String:arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + + new WepRestrictQuery:output = RestrictWeapon(arg1); + + if (output == Existing) + { + ZR_ReplyToCommand(client, "Weapon already restricted", arg1); + } + + return Plugin_Handled; +} + +public Action:Command_UnRestrict(client, argc) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (argc < 1 || !enabled) + { + return Plugin_Handled; + } + + decl String:arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + + new WepRestrictQuery:output = UnRestrictWeapon(arg1); + + if (output == Invalid) + { + ZR_ReplyToCommand(client, "Weapon invalid", arg1); + } + + return Plugin_Handled; +} \ No newline at end of file diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc new file mode 100644 index 0000000..5e5b3ec --- /dev/null +++ b/src/zr/cvars.inc @@ -0,0 +1,191 @@ +/** + * ==================== + * Zombie:Reloaded + * File: cvars.inc + * Author: Greyscale + * ==================== + */ + +enum ZRSettings +{ + Handle:CVAR_ENABLE, + Handle:CVAR_ALLOW_PLAYER_TEAM, + Handle:CVAR_AMBIENCE, + Handle:CVAR_AMBIENCE_FILE, + Handle:CVAR_AMBIENCE_LENGTH, + Handle:CVAR_AMBIENCE_VOLUME, + Handle:CVAR_EMITSOUNDS, + Handle:CVAR_CLASSES, + Handle:CVAR_CLASSES_SPAWN, + Handle:CVAR_CLASSES_RANDOM, + Handle:CVAR_ZOMBIE_HEALTH, + Handle:CVAR_ZOMBIE_SPEED, + Handle:CVAR_ZOMBIE_JUMP_DISTANCE, + Handle:CVAR_ZOMBIE_JUMP_HEIGHT, + Handle:CVAR_ZOMBIE_KNOCKBACK, + Handle:CVAR_ZOMBIE_NVGS, + Handle:CVAR_ZOMBIE_FOV, + Handle:CVAR_ZOMBIE_REGEN, + Handle:CVAR_ZOMBIE_REGEN_HEALTH, + Handle:CVAR_ZOMBIE_REGEN_INTERVAL, + Handle:CVAR_ZOMBIE_NAPALM, + Handle:CVAR_ZOMBIE_NAPALM_TIME, + Handle:CVAR_ZOMBIE_NOFALLDAMAGE, + Handle:CVAR_ZOMBIE_KILL_BONUS, + Handle:CVAR_ZOMBIE_INFECT_HEALTH, + Handle:CVAR_ZOMBIE_ZVISION, + Handle:CVAR_ZVISION_REDISPLAY, + Handle:CVAR_ZVISION_ALLOW_DISABLE, + Handle:CVAR_DARK, + Handle:CVAR_DARK_LEVEL, + Handle:CVAR_DARK_SKY, + Handle:CVAR_MOTHER_ZOMBIE_RATIO, + Handle:CVAR_MOTHER_ZOMBIE_RESPAWN, + Handle:CVAR_RESPAWN, + Handle:CVAR_RESPAWN_TEAM, + Handle:CVAR_RESPAWN_DELAY, + Handle:CVAR_SUICIDE, + Handle:CVAR_SPAWN_MIN, + Handle:CVAR_SPAWN_MAX, + Handle:CVAR_PROTECT, + Handle:CVAR_CONSECUTIVE_INFECT, + Handle:CVAR_OVERLAYS, + Handle:CVAR_OVERLAYS_HUMAN, + Handle:CVAR_OVERLAYS_ZOMBIE, + Handle:CVAR_ZMARKET_BUYZONE, + Handle:CVAR_ZSPAWN, + Handle:CVAR_ZTELE, + Handle:CVAR_ZTELE_LIMIT, + Handle:CVAR_ZSTUCK, + Handle:CVAR_ZHP, + Handle:CVAR_ZHP_DEFAULT, + Handle:CVAR_CASHFILL, + Handle:CVAR_CASHAMOUNT, + Handle:CVAR_INFECT_FIREBALL, + Handle:CVAR_INFECT_SMOKE, + Handle:CVAR_INFECT_SPARKS, + Handle:CVAR_INFECT_SOUND, + Handle:CVAR_INFECT_ESPLASH, + Handle:CVAR_INFECT_SHAKE, + Handle:CVAR_INFECT_SHAKE_AMP, + Handle:CVAR_INFECT_SHAKE_FREQUENCY, + Handle:CVAR_INFECT_SHAKE_DURATION +} + +new gCvars[ZRSettings]; + +CreateCvars() +{ + gCvars[CVAR_ENABLE] = CreateConVar("zr_enable", "1", "Enable zombie gameplay (0: Disable)"); + gCvars[CVAR_ALLOW_PLAYER_TEAM] = CreateConVar("zr_allow_player_team", "0", "This will allow the player_team event to be fired on first team join, enable when using mani model menu (0: Disable)"); + gCvars[CVAR_AMBIENCE] = CreateConVar("zr_ambience", "1", "Enable creepy ambience to be played throughout the game (0: Disable)"); + gCvars[CVAR_AMBIENCE_FILE] = CreateConVar("zr_ambience_file", "ambient/zr/zr_ambience.mp3", "Path to ambient sound file that will be played throughout the game, when zr_ambience is 1"); + gCvars[CVAR_AMBIENCE_LENGTH] = CreateConVar("zr_ambience_length", "60.0", "The length, in seconds, of the ambient sound file"); + gCvars[CVAR_AMBIENCE_VOLUME] = CreateConVar("zr_ambience_volume", "0.6", "Volume of ambient sounds when zr_ambience is 1 (0.0: Unhearable, 1.0: Max volume)"); + gCvars[CVAR_EMITSOUNDS] = CreateConVar("zr_emitsounds", "50", "How often a zombie emits a sound, in seconds (0: Disable)"); + gCvars[CVAR_CLASSES] = CreateConVar("zr_classes", "1", "Enable zombie classes"); + gCvars[CVAR_CLASSES_SPAWN] = CreateConVar("zr_classes_spawn", "0", "Classmenu is re-displayed every spawn (0: Disable)"); + gCvars[CVAR_CLASSES_RANDOM] = CreateConVar("zr_classes_random", "0", "A random class is assigned to each player every round (0: Disable)"); + gCvars[CVAR_ZOMBIE_HEALTH] = CreateConVar("zr_zombie_health", "5000", "The default health of a zombie"); + gCvars[CVAR_ZOMBIE_SPEED] = CreateConVar("zr_zombie_speed", "350", "How fast zombies travel (300: Default speed, 600: Double speed)"); + gCvars[CVAR_ZOMBIE_JUMP_DISTANCE] = CreateConVar("zr_zombie_jump_distance", "0.1", "How far the zombie jumps, (0: Regular jump distance)"); + gCvars[CVAR_ZOMBIE_JUMP_HEIGHT] = CreateConVar("zr_zombie_jump_height", "10.0", "How high a zombie jumps (0: Regular jump height)"); + gCvars[CVAR_ZOMBIE_KNOCKBACK] = CreateConVar("zr_zombie_knockback", "4", "How far zombies are pushed back when shot (1: Default)"); + gCvars[CVAR_ZOMBIE_NVGS] = CreateConVar("zr_zombie_nvgs", "0", "Zombies will receive nightvision and it will automatically be enabled (0: No night vision, recommended if using zvision)"); + gCvars[CVAR_ZOMBIE_FOV] = CreateConVar("zr_zombie_fov", "110", "The field of vision of zombies (90: Default vision)"); + gCvars[CVAR_ZOMBIE_REGEN] = CreateConVar("zr_zombie_regen", "0", "Zombies will regenerate health"); + gCvars[CVAR_ZOMBIE_REGEN_HEALTH] = CreateConVar("zr_zombie_regen_health", "1", "How much health is regenerated when zr_zombie_regen is 1"); + gCvars[CVAR_ZOMBIE_REGEN_INTERVAL] = CreateConVar("zr_zombie_regen_interval", "5", "How often, in seconds, a zombie regenerates health when zr_zombie_regen is 1"); + gCvars[CVAR_ZOMBIE_NAPALM] = CreateConVar("zr_zombie_napalm", "1", "Turns grenades into napalm grenades that light zombies on fire (0: Disable)"); + gCvars[CVAR_ZOMBIE_NAPALM_TIME] = CreateConVar("zr_zombie_napalm_time", "20", "How long the zombie burns when zr_napalm is 1"); + gCvars[CVAR_ZOMBIE_NOFALLDAMAGE] = CreateConVar("zr_zombie_nofalldamage", "0", "Zombies wont be hurt from falling (0: Disable)"); + gCvars[CVAR_ZOMBIE_KILL_BONUS] = CreateConVar("zr_zombie_kill_bonus", "2", "How many additional kills are rewarded to the killer of the zombie"); + gCvars[CVAR_ZOMBIE_INFECT_HEALTH] = CreateConVar("zr_zombie_infect_health", "100", "How much health a zombie gains when infecting a human (0: Disable)"); + gCvars[CVAR_ZOMBIE_ZVISION] = CreateConVar("zr_zombie_zvision", "overlays/zr/zvision", "Overlay to be shown on all zombies' screen on infection (Leave empty to disable)"); + gCvars[CVAR_ZVISION_REDISPLAY] = CreateConVar("zr_zvision_redisplay", "0.2", "Frequency, in seconds, to display zvision on the client's screen (Never go below 0.1, 0.2 seems safe)"); + gCvars[CVAR_ZVISION_ALLOW_DISABLE] = CreateConVar("zr_zvision_allow_disable", "1", "Allow users to disable ZVision with their nightvision key (0: Disable)"); + gCvars[CVAR_DARK] = CreateConVar("zr_dark", "0", "Default value for darkening maps, most dislike this feature (0: Disable)"); + gCvars[CVAR_DARK_LEVEL] = CreateConVar("zr_dark_level", "a", "The darkness of the map, a being the darkest, z being extremely bright when zr_dark is 1 (n: Default)"); + gCvars[CVAR_DARK_SKY] = CreateConVar("zr_dark_sky", "sky_borealis01", "The sky the map will have when zr_dark is 1"); + gCvars[CVAR_MOTHER_ZOMBIE_RATIO] = CreateConVar("zr_mother_zombie_ratio", "5", "For every 'x' number of humans, there will be 1 zombie (0: Always only 1 mother zombie)"); + gCvars[CVAR_MOTHER_ZOMBIE_RESPAWN] = CreateConVar("zr_mother_zombie_respawn", "0", "First zombie(s) will be teleported back to spawn when infected (0: Disable)"); + gCvars[CVAR_RESPAWN] = CreateConVar("zr_respawn", "0", "When player is killed, player will respawn"); + gCvars[CVAR_RESPAWN_TEAM] = CreateConVar("zr_respawn_team", "zombie", "Which team to respawn player as (Choices: zombie, human)"); + gCvars[CVAR_RESPAWN_DELAY] = CreateConVar("zr_respawn_delay", "1", "How long to wait after death to respawn, in seconds"); + gCvars[CVAR_SUICIDE] = CreateConVar("zr_suicide", "1", "Stops players from suiciding"); + gCvars[CVAR_SPAWN_MIN] = CreateConVar("zr_spawn_min", "30", "Minimum time a player is picked to be zombie after the round starts, in seconds"); + gCvars[CVAR_SPAWN_MAX] = CreateConVar("zr_spawn_max", "50", "Maximum time a player is picked to be zombie after the round starts, in seconds"); + gCvars[CVAR_PROTECT] = CreateConVar("zr_protect", "10", "Players that join late will be protected for this long, in seconds (0: Disable)"); + gCvars[CVAR_CONSECUTIVE_INFECT] = CreateConVar("zr_consecutive_infect", "0", "Allow player to be randomly chosen twice in a row to be a mother zombie (0: Disable)"); + gCvars[CVAR_OVERLAYS] = CreateConVar("zr_overlays", "1", "Will show overlays that tell who the winner of the round was (0: Disable)"); + gCvars[CVAR_OVERLAYS_HUMAN] = CreateConVar("zr_overlays_human", "overlays/zr/humans_win", "The overlay shown to tell everyone that humans won when zr_overlays is 1"); + gCvars[CVAR_OVERLAYS_ZOMBIE] = CreateConVar("zr_overlays_zombie", "overlays/zr/zombies_win", "The overlay shown to tell everyone that zombies won when zr_overlays is 1"); + gCvars[CVAR_ZMARKET_BUYZONE] = CreateConVar("zr_zmarket_buyzone", "1", "Must be in buyzone to access !zmarket, if Market is installed (0: Can be used anywhere)"); + gCvars[CVAR_ZSPAWN] = CreateConVar("zr_zspawn", "1", "Allow players to spawn if they just joined the game (0: Disable)"); + gCvars[CVAR_ZTELE] = CreateConVar("zr_ztele", "1", "Allows zombies who get stuck to teleport back to spawn (0: Disable)"); + gCvars[CVAR_ZTELE_LIMIT] = CreateConVar("zr_ztele_limit", "1", "Max amount of teleports per round when zr_ztele is 1 (0: Unlimited)"); + gCvars[CVAR_ZSTUCK] = CreateConVar("zr_zstuck", "1", "Allow players that are stuck together to get unstuck (0: Disable)"); + gCvars[CVAR_ZHP] = CreateConVar("zr_zhp", "1", "Allows clients to enable/disable zombie health display (1: On, 0: Off)"); + gCvars[CVAR_ZHP_DEFAULT] = CreateConVar("zr_zhp_default", "1", "The default value of zombie health display to new clients (1: On, 0: Off)"); + gCvars[CVAR_CASHFILL] = CreateConVar("zr_cashfill", "1", "Enable the mod to set the players cash to zr_cashamount (0: Disabled)"); + gCvars[CVAR_CASHAMOUNT] = CreateConVar("zr_cashamount", "12000", "How much money players will have when they spawn when zr_cashfill is 1"); + gCvars[CVAR_INFECT_FIREBALL] = CreateConVar("zr_infect_fireball", "1", "A fireball is created when a player is infected ( 0: Disable)"); + gCvars[CVAR_INFECT_SMOKE] = CreateConVar("zr_infect_smoke", "1", "A puff of smoke is created when a player is infected (0: Disable)"); + gCvars[CVAR_INFECT_SPARKS] = CreateConVar("zr_infect_sparks", "1", "Sparks are emitted when a player is infected (0: Disable)"); + gCvars[CVAR_INFECT_SOUND] = CreateConVar("zr_infect_sound", "npc/fast_zombie/fz_scream1.wav", "Sound played from from player on infection (Leave blank to disable)"); + gCvars[CVAR_INFECT_ESPLASH] = CreateConVar("zr_infect_esplash", "1", "An energy splash is emitted when player is infected ( 0: Disable)"); + gCvars[CVAR_INFECT_SHAKE] = CreateConVar("zr_infect_shake", "1", "Player's screen is shaken on infection (0: Disable)"); + gCvars[CVAR_INFECT_SHAKE_AMP] = CreateConVar("zr_infect_shake_amp", "15.0", "Amplitude of the shake, when zr_infect_shake is 1"); + gCvars[CVAR_INFECT_SHAKE_FREQUENCY] = CreateConVar("zr_infect_shake_frequency", "1.0", "Frequency of the shake, when zr_infect_shake is 1"); + gCvars[CVAR_INFECT_SHAKE_DURATION] = CreateConVar("zr_infect_shake_duration", "5.0", "Duration of the shake, when zr_infect_shake is 1"); + + HookConVarChange(gCvars[CVAR_ENABLE], EnableHook); + + AutoExecConfig(true, "zombiereloaded", "sourcemod/zombiereloaded"); +} + +HookCvars() +{ + SetConVarBool(FindConVar("mp_autoteambalance"), false); + SetConVarInt(FindConVar("mp_limitteams"), 0); + + HookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook); + HookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook); + + HookConVarChange(FindConVar("mp_restartgame"), RestartGameHook); +} + +UnhookCvars() +{ + UnhookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook); + UnhookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook); + + UnhookConVarChange(FindConVar("mp_restartgame"), RestartGameHook); +} + +public EnableHook(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + new bool:enable = bool:StringToInt(newValue); + + if (enable) + { + HookEvents(); + HookCvars(); + + TerminateRound(3.0, Game_Commencing); + } + else + { + ZREnd(); + } +} + +public AutoTeamBalanceHook(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + SetConVarBool(FindConVar("mp_autoteambalance"), false); +} + +public LimitTeamsHook(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + SetConVarInt(FindConVar("mp_limitteams"), 0); +} \ No newline at end of file diff --git a/src/zr/damagecontrol.inc b/src/zr/damagecontrol.inc new file mode 100644 index 0000000..a6f8bef --- /dev/null +++ b/src/zr/damagecontrol.inc @@ -0,0 +1,178 @@ +/** + * ==================== + * Zombie:Reloaded + * File: damagecontrol.inc + * Author: Greyscale + * ==================== + */ + +#define DMG_GENERIC 0 // generic damage was done +#define DMG_BULLET (1 << 1) // shot +#define DMG_SLASH (1 << 2) // cut, clawed, stabbed +#define DMG_BURN (1 << 3) // heat burned +#define DMG_FALL (1 << 5) // fell too far +#define DMG_BLAST (1 << 6) // explosive blast damage +#define DMG_DROWN (1 << 14) // Drowning + +enum ZRHooks +{ + Hook_TraceAttack, + Hook_OnTakeDamage +} + +new gHooks[MAXPLAYERS+1][ZRHooks]; + +InitDmgControl() +{ + RegConsoleCmd("kill", Attempt_Suicide); + RegConsoleCmd("jointeam", Attempt_Suicide); + RegConsoleCmd("spectate", Attempt_Suicide); +} + +ClientHookAttack(client) +{ + gHooks[client][Hook_TraceAttack] = Hacks_Hook(client, HACKS_HTYPE_TRACEATTACK, TraceAttack, false); + gHooks[client][Hook_OnTakeDamage] = Hacks_Hook(client, HACKS_HTYPE_ONTAKEDAMAGE, OnTakeDamage, false); +} + +ClientUnHookAttack(client) +{ + Hacks_Unhook(gHooks[client][Hook_TraceAttack]); + Hacks_Unhook(gHooks[client][Hook_OnTakeDamage]); +} + +public TraceAttack(client, inflictor, attacker, damage, hitbox, hitgroup) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + + if (!attacker || !IsClientInGame(attacker) || !enabled) + { + return Hacks_Continue; + } + + if (IsPlayerZombie(client) && IsPlayerZombie(attacker)) + { + return 0; + } + + if (IsPlayerHuman(client) && IsPlayerHuman(attacker)) + { + return 0; + } + + return Hacks_Continue; +} + +public OnTakeDamage(client, inflictor, attacker, damage, damagetype, ammotype) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (!enabled) + { + return Hacks_Continue; + } + + decl String:classname[64]; + GetEdictClassname(inflictor, classname, sizeof(classname)); + if (StrContains(classname, "trigger") > -1) + { + return Hacks_Continue; + } + + if (damagetype & DMG_FALL) + { + if (!IsPlayerZombie(client)) + { + return Hacks_Continue; + } + + new bool:blockfalldamage = GetClassNoFallDamage(pClass[client]); + if (!blockfalldamage) + { + return Hacks_Continue; + } + + return 0; + } + + if (damagetype & DMG_BLAST) + { + if (!IsPlayerHuman(client) || !IsClientInGame(attacker)) + { + return Hacks_Continue; + } + + return 0; + } + + if (damagetype & DMG_BULLET) + { + if (!client || !IsClientInGame(client)) + { + return Hacks_Continue; + } + + if (!attacker || !IsClientInGame(attacker)) + { + return Hacks_Continue; + } + + if (IsPlayerZombie(client) && IsPlayerHuman(attacker)) + { + return Hacks_Continue; + } + + if (IsPlayerHuman(client) && IsPlayerZombie(attacker)) + { + new health = GetClientHealth(client); + SetEntityHealth(client, health + damage); + + return Hacks_Continue; + } + + return 0; + } + + return Hacks_Continue; +} + +public Action:Attempt_Suicide(client, argc) +{ + if (!client) + { + return Plugin_Continue; + } + + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (!enabled) + { + return Plugin_Continue; + } + + new bool:suicide = GetConVarBool(gCvars[CVAR_SUICIDE]); + if (!suicide) + { + return Plugin_Continue; + } + + decl String:cmd[16]; + GetCmdArg(0, cmd, sizeof(cmd)); + + if (!IsPlayerZombie(client) && StrEqual(cmd, "spectate", false)) + { + return Plugin_Continue; + } + + if (!IsPlayerZombie(client) && !GetConVarBool(gCvars[CVAR_RESPAWN])) + { + return Plugin_Continue; + } + + if (!IsPlayerAlive(client)) + { + return Plugin_Continue; + } + + ZR_ReplyToCommand(client, "Suicide text"); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/src/zr/event.inc b/src/zr/event.inc new file mode 100644 index 0000000..bc96752 --- /dev/null +++ b/src/zr/event.inc @@ -0,0 +1,465 @@ +/** + * ==================== + * Zombie:Reloaded + * File: events.inc + * Author: Greyscale + * ==================== + */ + +HookEvents() +{ + HookEvent("round_start", RoundStart); + HookEvent("round_freeze_end", RoundFreezeEnd); + HookEvent("round_end", RoundEnd); + HookEvent("player_team", PlayerTeam, EventHookMode_Pre); + HookEvent("player_spawn", PlayerSpawn); + HookEvent("player_hurt", PlayerHurt); + HookEvent("player_death", PlayerDeath); + HookEvent("player_jump", PlayerJump); +} + +UnhookEvents() +{ + UnhookEvent("round_start", RoundStart); + UnhookEvent("round_freeze_end", RoundFreezeEnd); + UnhookEvent("round_end", RoundEnd); + UnhookEvent("player_team", PlayerTeam, EventHookMode_Pre); + UnhookEvent("player_spawn", PlayerSpawn); + UnhookEvent("player_hurt", PlayerHurt); + UnhookEvent("player_death", PlayerDeath); + UnhookEvent("player_jump", PlayerJump); +} + +public Action:RoundStart(Handle:event, const String:name[], bool:dontBroadcast) +{ + ChangeLightStyle(); + + RestartAmbience(); + + RefreshList(); + + if (tRound != INVALID_HANDLE) + { + CloseHandle(tRound); + tRound = INVALID_HANDLE; + } + + if (tInfect != INVALID_HANDLE) + { + CloseHandle(tInfect); + tInfect = INVALID_HANDLE; + } + + + ZR_PrintToChat(0, "Round objective"); +} + +public Action:RoundFreezeEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + RemoveObjectives(); + + if (tRound != INVALID_HANDLE) + { + CloseHandle(tRound); + } + + new Float:roundlen = GetConVarFloat(FindConVar("mp_roundtime")) * 60.0; + tRound = CreateTimer(roundlen, RoundOver, _, TIMER_FLAG_NO_MAPCHANGE); + + if (tInfect != INVALID_HANDLE) + { + CloseHandle(tInfect); + } + + new Float:min = GetConVarFloat(gCvars[CVAR_SPAWN_MIN]); + new Float:max = GetConVarFloat(gCvars[CVAR_SPAWN_MAX]); + new Float:randlen = GetRandomFloat(min, max); + tInfect = CreateTimer(randlen, MotherZombie, _, TIMER_FLAG_NO_MAPCHANGE); +} + +public Action:RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (tRound != INVALID_HANDLE) + { + CloseHandle(tRound); + tRound = INVALID_HANDLE; + } + + if (tInfect != INVALID_HANDLE) + { + CloseHandle(tInfect); + tInfect = INVALID_HANDLE; + } + + zombieSpawned = false; + + new maxplayers = GetMaxClients(); + for (new x = 1; x<= maxplayers; x++) + { + if (!IsClientInGame(x)) + { + continue; + } + + gZombie[x] = false; + + new bool:consecutive_infect = GetConVarBool(gCvars[CVAR_CONSECUTIVE_INFECT]); + gBlockMotherInfect[x] = consecutive_infect ? false : motherZombie[x]; + } + + BalanceTeams(); + + new reason = GetEventInt(event, "reason"); + + if (reason == CTs_PreventEscape) + { + ShowOverlays(5.0, Human); + } + else if (reason == Terrorists_Escaped) + { + ShowOverlays(5.0, Zombie); + } +} + +public Action:PlayerTeam(Handle:event, const String:name[], bool:dontBroadcast) +{ + new index = GetClientOfUserId(GetEventInt(event, "userid")); + new team = GetEventInt(event, "team"); + + if (team == 1) + { + gZombie[index] = false; + motherZombie[index] = false; + } + + new bool:allow_player_team = GetConVarBool(gCvars[CVAR_ALLOW_PLAYER_TEAM]); + if (allow_player_team && !IsPlayerInList(index)) + { + return Plugin_Continue; + } + + return Plugin_Handled; +} + +public Action:PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + new index = GetClientOfUserId(GetEventInt(event, "userid")); + + for (new x = 0; x < MAXTIMERS; x++) + { + if (x == TRESPAWN) + { + continue; + } + + if (tHandles[index][x] != INVALID_HANDLE) + { + CloseHandle(tHandles[index][x]); + tHandles[index][x] = INVALID_HANDLE; + } + } + + gZombie[index] = false; + motherZombie[index] = false; + + SetPlayerFOV(index, 90); + + ClientCommand(index, "r_screenoverlay \"\""); + + new team = GetClientTeam(index); + if (team != CS_TEAM_T && team != CS_TEAM_CT) + { + return; + } + + new bool:cashfill = GetConVarBool(gCvars[CVAR_CASHFILL]); + if (cashfill) + { + new cash = GetConVarInt(gCvars[CVAR_CASHAMOUNT]); + SetPlayerMoney(index, cash); + } + + teleCount[index] = 0; + GetClientAbsOrigin(index, spawnLoc[index]); + + NightVisionOn(index, false); + NightVision(index, false); + + if (pNextClass[index] != -1) + { + Call_StartForward(hOnZClassChanged); + Call_PushCell(index); + Call_PushCell(pClass[index]); + Call_PushCell(pNextClass[index]); + Call_Finish(); + + pClass[index] = pNextClass[index]; + pNextClass[index] = -1; + } + + pProtect[index] = false; + if (zombieSpawned) + { + if (team == CS_TEAM_T) + { + CS_SwitchTeam(index, CS_TEAM_CT); + CS_RespawnPlayer(index); + } + + new protect = GetConVarInt(gCvars[CVAR_PROTECT]); + if (protect > 0) + { + decl String:respawnteam[32]; + GetConVarString(gCvars[CVAR_RESPAWN_TEAM], respawnteam, sizeof(respawnteam)); + + if (!StrEqual(respawnteam, "zombie", false)) + { + pProtect[index] = true; + + ZR_PrintToChat(index, "Spawn protection begin", protect); + ZR_PrintCenterText(index, "Spawn protection begin", protect); + + if (tHandles[index][TPROTECT] != INVALID_HANDLE) + { + CloseHandle(tHandles[index][TPROTECT]); + } + + tHandles[index][TPROTECT] = CreateTimer(float(protect), EndProtect, index, TIMER_FLAG_NO_MAPCHANGE); + } + } + } + + new bool:randomclass = GetConVarBool(gCvars[CVAR_CLASSES_RANDOM]); + + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + new bool:showmenu = GetConVarBool(gCvars[CVAR_CLASSES_SPAWN]); + if (showmenu && !randomclass) + { + ClassMenu(index); + } + } + + ZR_PrintToChat(index, "!zmenu reminder"); + + decl String:steamid[16]; + GetClientAuthString(index, steamid, sizeof(steamid)); + if (StrEqual(steamid, "BOT") || randomclass) + { + new class = GetRandomInt(0, classCount - 1); + + Call_StartForward(hOnZClassChanged); + Call_PushCell(index); + Call_PushCell(pClass[index]); + Call_PushCell(class); + Call_Finish(); + + pClass[index] = class; + + decl String:classname[32]; + GetClassName(class, classname, sizeof(classname)); + + ZR_PrintToChat(index, "Auto-assign", classname); + } +} + +public Action:PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + new index = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + new dmg = GetEventInt(event, "dmg_health"); + + decl String:weapon[32]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + if (attacker != 0) + { + if (IsPlayerHuman(index) && IsPlayerZombie(attacker)) + { + if (StrEqual(weapon, "knife")) + { + Zombify(index, attacker); + } + } + else if (IsPlayerHuman(attacker)) + { + new Float:knockback = GetClassKnockback(pClass[index]); + + new Float:clientloc[3]; + new Float:attackerloc[3]; + + GetClientAbsOrigin(index, clientloc); + + if (!StrEqual(weapon, "hegrenade")) + { + GetClientAbsOrigin(attacker, attackerloc); + + new bool:shotgun = (StrEqual(weapon, "m3") || StrEqual(weapon, "xm1014")); + + KnockBack(index, clientloc, attackerloc, knockback, dmg, shotgun); + } + else + { + new Float:heLoc[3]; + FindExplodingGrenade(heLoc); + + KnockBack(index, clientloc, heLoc, knockback, dmg, true); + } + } + } + + if (!IsPlayerZombie(index)) + { + return; + } + + if (GetRandomInt(1, 5) == 1) + { + decl String:sound[64]; + new randsound = GetRandomInt(1, 6); + + Format(sound, sizeof(sound), "npc/zombie/zombie_pain%d.wav", randsound); + + PrecacheSound(sound); + EmitSoundToAll(sound, index); + } + + new bool:napalm = GetClassNapalm(pClass[index]); + if (napalm) + { + if (StrEqual(weapon, "hegrenade", false)) + { + new Float:napalm_time = GetClassNapalmTime(pClass[index]); + IgniteEntity(index, napalm_time); + } + } + + UpdateHPDisplay(index); + + if (GetClassRegen(pClass[index])) + { + if (tHandles[index][TREGEN] == INVALID_HANDLE) + { + new Float:interval = GetClassRegenInterval(pClass[index]); + tHandles[index][TREGEN] = CreateTimer(interval, Regenerate, index, TIMER_REPEAT); + } + } +} + +FindExplodingGrenade(Float:heLoc[3]) +{ + decl String:classname[64]; + + new maxentities = GetMaxEntities(); + for (new x = GetMaxClients(); x <= maxentities; x++) + { + if (IsValidEdict(x)) + { + GetEdictClassname(x, classname, sizeof(classname)); + if (StrEqual(classname, "hegrenade_projectile")) + { + new takedamage = GetEntProp(x, Prop_Data, "m_takedamage"); + if (takedamage == 0) + { + GetEntPropVector(x, Prop_Send, "m_vecOrigin", heLoc); + return x; + } + } + } + } + + return -1; +} + +public Action:PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + new index = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + SetPlayerFOV(index, DEFAULT_FOV); + + ExtinguishEntity(index); + + decl String:weapon[32]; + + GetEventString(event, "weapon", weapon, sizeof(weapon)); + if (StrEqual(weapon, "zombie_claws_of_death", false)) + { + if (index) + { + AddPlayerDeath(index, 1); + } + + if (attacker) + { + AddPlayerScore(attacker, 1); + + new healthgain = GetClassInfectHealth(pClass[attacker]); + new health = GetClientHealth(attacker); + + SetEntityHealth(attacker, health + healthgain); + + UpdateHPDisplay(attacker); + } + } + else + { + if (IsPlayerZombie(index)) + { + decl String:sound[64]; + + new randsound = GetRandomInt(1, 3); + Format(sound, sizeof(sound), "npc/zombie/zombie_die%d.wav", randsound); + + PrecacheSound(sound); + EmitSoundToAll(sound, index); + + if (attacker) + { + new bonus = GetClassKillBonus(pClass[index]); + AddPlayerScore(attacker, bonus); + } + } + + for (new x = 0; x < MAXTIMERS; x++) + { + if (tHandles[index][x] != INVALID_HANDLE) + { + CloseHandle(tHandles[index][x]); + tHandles[index][x] = INVALID_HANDLE; + } + } + + new bool:respawn = GetConVarBool(gCvars[CVAR_RESPAWN]); + if (respawn) + { + new Float:delay = GetConVarFloat(gCvars[CVAR_RESPAWN_DELAY]); + tHandles[index][TRESPAWN] = CreateTimer(delay, RespawnTimer, index, TIMER_FLAG_NO_MAPCHANGE); + } + } + + new ZTeam:team = IsRoundOver(); + if (team == Neither) + { + ClientCommand(index, "r_screenoverlay \"\""); + + return; + } + + RoundWin(team); +} + +public Action:PlayerJump(Handle:event, const String:name[], bool:dontBroadcast) +{ + new index = GetClientOfUserId(GetEventInt(event, "userid")); + + if (IsPlayerZombie(index)) + { + new Float:distance = GetClassJumpDistance(pClass[index]); + new Float:height = GetClassJumpHeight(pClass[index]); + + JumpBoost(index, distance, height); + } +} \ No newline at end of file diff --git a/src/zr/global.inc b/src/zr/global.inc new file mode 100644 index 0000000..e29d3c9 --- /dev/null +++ b/src/zr/global.inc @@ -0,0 +1,36 @@ +/** + * ==================== + * Zombie:Reloaded + * File: global.inc + * Author: Greyscale + * ==================== + */ + +new Handle:hZombify = INVALID_HANDLE; +new Handle:hOnZClassChanged = INVALID_HANDLE; + +CreateGlobals() +{ + CreateNative("ZR_IsClientZombie", Native_IsClientZombie); + CreateNative("ZR_GetClientZClass", Native_GetClientZClass); + CreateNative("ZR_HasZombieSpawned", Native_HasZombieSpawned); + + hZombify = CreateGlobalForward("ZR_Zombify", ET_Ignore, Param_Cell, Param_Cell); + hOnZClassChanged = CreateGlobalForward("ZR_OnZClassChanged", ET_Ignore, Param_Cell, Param_Cell, Param_Cell); +} + +public Native_IsClientZombie(Handle:plugin, argc) +{ + return gZombie[GetNativeCell(1)]; +} + +public Native_GetClientZClass(Handle:plugin, argc) +{ + new class = GetNativeCell(1); + return pClass[class]; +} + +public Native_HasZombieSpawned(Handle:plugin, argc) +{ + return zombieSpawned; +} \ No newline at end of file diff --git a/src/zr/menu.inc b/src/zr/menu.inc new file mode 100644 index 0000000..e69d059 --- /dev/null +++ b/src/zr/menu.inc @@ -0,0 +1,175 @@ +/** + * ==================== + * Zombie:Reloaded + * File: menu.sp + * Author: Greyscale + * ==================== + */ + +MainMenu(client) +{ + new Handle:menu_main = CreateMenu(MainMenuHandle); + + SetGlobalTransTarget(client); + + SetMenuTitle(menu_main, "%t\n ", "!zmenu title"); + + decl String:zmenu[128]; + decl String:zclass[128]; + decl String:zmarket[128]; + decl String:zspawn[128]; + decl String:ztele[128]; + decl String:zstuck[128]; + decl String:zhp[128]; + + Format(zmenu, sizeof(zmenu), "%t", "!zmenu menu"); + Format(zclass, sizeof(zclass), "%t", "!zmenu class"); + Format(zmarket, sizeof(zmarket), "%t", "!zmenu market"); + Format(zspawn, sizeof(zspawn), "%t", "!zmenu spawn"); + Format(ztele, sizeof(ztele), "%t", "!zmenu tele"); + Format(zstuck, sizeof(zstuck), "%t", "!zmenu stuck"); + Format(zhp, sizeof(zhp), "%t (%d HP)", "!zmenu hp", GetClientHealth(client)); + + AddMenuItem(menu_main, "zmenu", zmenu, ITEMDRAW_DISABLED); + AddMenuItem(menu_main, "zclass", zclass); + + if (market) + { + AddMenuItem(menu_main, "zmarket", zmarket); + } + else + { + AddMenuItem(menu_main, "zmarket", zmarket, ITEMDRAW_DISABLED); + } + + AddMenuItem(menu_main, "zspawn", zspawn); + AddMenuItem(menu_main, "ztele", ztele); + AddMenuItem(menu_main, "zstuck", zstuck); + AddMenuItem(menu_main, "zhp", zhp); + + DisplayMenu(menu_main, client, MENU_TIME_FOREVER); +} + +public MainMenuHandle(Handle:menu_main, MenuAction:action, client, slot) +{ + if (action == MenuAction_Select) + { + switch(slot) + { + case 1: + { + if (!ZClass(client)) + { + MainMenu(client); + } + } + case 2: + { + if (!ZMarket(client)) + { + MainMenu(client); + } + } + case 3: + { + ZSpawn(client); + MainMenu(client); + } + case 4: + { + ZTele(client); + MainMenu(client); + } + case 5: + { + ZStuck(client); + MainMenu(client); + } + case 6: + { + ZHP(client); + MainMenu(client); + } + } + } + if (action == MenuAction_End) + { + CloseHandle(menu_main); + } +} + +ClassMenu(client) +{ + new Handle:menu_classes = CreateMenu(ClassMenuHandle); + + decl String:menu_description[256]; + decl String:display[288]; // 32 + 256 + + SetGlobalTransTarget(client); + + SetMenuTitle(menu_classes, "%t\n ", "!zclass title"); + + for (new x = 0; x < classCount; x++) + { + GetClassName(x, display, sizeof(display)); + GetClassMenuDescription(x, menu_description, sizeof(menu_description)); + + if (pNextClass[client] == -1) + { + if (x == pClass[client]) + { + Format(display, sizeof(display), "%s (current)", display); + } + } + else if (x == pNextClass[client]) + { + Format(display, sizeof(display), "%s (current)", display); + } + + Format(display, sizeof(display), "%s\n %s", display, menu_description); + + AddMenuItem(menu_classes, "", display); + } + + SetMenuExitBackButton(menu_classes, true); + + DisplayMenu(menu_classes, client, MENU_TIME_FOREVER); +} + +public ClassMenuHandle(Handle:menu_classes, MenuAction:action, client, slot) +{ + if (action == MenuAction_Select) + { + if (IsPlayerHuman(client) || !IsPlayerAlive(client)) + { + Call_StartForward(hOnZClassChanged); + Call_PushCell(client); + Call_PushCell(pClass[client]); + Call_PushCell(slot); + Call_Finish(); + + pClass[client] = slot; + } + else + { + pNextClass[client] = slot; + } + + decl String:name[64]; + GetClassName(slot, name, sizeof(name)); + + ZR_PrintToChat(client, "Class select", name); + } + + if (action == MenuAction_Cancel) + { + if (slot == MenuCancel_ExitBack) + { + MainMenu(client); + } + } + else if (action == MenuAction_End) + { + CloseHandle(menu_classes); + } +} \ No newline at end of file diff --git a/src/zr/models.inc b/src/zr/models.inc new file mode 100644 index 0000000..631f865 --- /dev/null +++ b/src/zr/models.inc @@ -0,0 +1,139 @@ +/** + * ==================== + * Zombie:Reloaded + * File: models.inc + * Author: Greyscale + * ==================== + */ + +new String:modelSuffix[8][16] = {".dx80.vtx", ".dx90.vtx", ".mdl", ".phy", ".sw.vtx", ".vvd", ".xbox", ".xbox.vtx"}; + +new Handle:arrayModels = INVALID_HANDLE; + +FileLinesToArray(Handle:array, const Handle:file) +{ + ClearArray(array); + + decl String:line[128]; + + while(!IsEndOfFile(file) && ReadFileLine(file, line, sizeof(line))) + { + if (StrContains(line, ";") == -1) + { + if (StrContains(line, "//") > -1) + { + SplitString(line, "//", line, sizeof(line)); + } + TrimString(line); + + if (!StrEqual(line, "", false)) + { + PushArrayString(array, line); + } + } + } +} + +LoadModelData() +{ + decl String:path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "configs/zr/models.txt"); + + if (arrayModels != INVALID_HANDLE) + { + CloseHandle(arrayModels); + } + + arrayModels = CreateArray(256, 0); + + new Handle:fileModels = OpenFile(path, "r"); + if (fileModels == INVALID_HANDLE) + { + SetFailState("\"%s\" missing from server", path); + } + + FileLinesToArray(arrayModels, fileModels); + + if (!GetArraySize(arrayModels)) + { + SetFailState("No models listed in models.txt, please add some models then restart"); + } + + decl String:model[256]; + decl String:modelpath[256]; + + new modelsize = GetArraySize(arrayModels); + for (new x = 0; x < modelsize; x++) + { + for (new y = 0; y < 8; y++) + { + GetArrayString(arrayModels, x, model, sizeof(model)); + Format(modelpath, sizeof(modelpath), "%s%s", model, modelSuffix[y]); + if (FileExists(modelpath)) + { + AddFileToDownloadsTable(modelpath); + } + } + } + + CloseHandle(fileModels); +} + +LoadDownloadData() +{ + decl String:path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "configs/zr/downloads.txt"); + + new Handle:fileDownloads = OpenFile(path, "r"); + + if (fileDownloads == INVALID_HANDLE) + { + SetFailState("\"%s\" missing from server", path); + } + + new Handle:arrayDownloads = CreateArray(256, 0); + + FileLinesToArray(arrayDownloads, fileDownloads); + + decl String:file[256]; + + new downloadsize = GetArraySize(arrayDownloads); + for (new x = 0; x < downloadsize; x++) + { + GetArrayString(arrayDownloads, x, file, sizeof(file)); + if (FileExists(file)) + { + AddFileToDownloadsTable(file); + } + else + { + ZR_LogMessage("File load failed", file); + } + } + + CloseHandle(fileDownloads); + CloseHandle(arrayDownloads); +} + +ApplyZombieModel(client) +{ + decl String:modelpath[256]; + + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (classes) + { + GetClassModel(pClass[client], modelpath, sizeof(modelpath)); + if (!StrEqual(modelpath, "default", false)) + { + SetPlayerModel(client, modelpath); + return; + } + } + + new randmodel = GetRandomInt(0, GetArraySize(arrayModels) - 1); + GetArrayString(arrayModels, randmodel, modelpath, sizeof(modelpath)); + Format(modelpath, sizeof(modelpath), "%s.mdl", modelpath); + + SetPlayerModel(client, modelpath); +} + diff --git a/src/zr/offsets.inc b/src/zr/offsets.inc new file mode 100644 index 0000000..646ed77 --- /dev/null +++ b/src/zr/offsets.inc @@ -0,0 +1,190 @@ +/** + * ==================== + * Zombie:Reloaded + * File: offsets.inc + * Author: Greyscale + * ==================== + */ + +new offsBaseVelocity; +new offsGetVelocity0; +new offsGetVelocity1; +new offsGetVelocity2; +new offsSpeed; +new offsNVG; +new offsNVGOn; +new offsCollision; +new offsMoney; +new offsFOV; +new offsBuyZone; + +new Handle:hGameConf = INVALID_HANDLE; +new Handle:hRemoveAllItems = INVALID_HANDLE; +new Handle:hTerminateRound = INVALID_HANDLE; + +FindOffsets() +{ + offsBaseVelocity = FindSendPropInfo("CBasePlayer", "m_vecBaseVelocity"); + if (offsBaseVelocity == -1) + { + SetFailState("Couldn't find \"m_vecBaseVelocity\"!"); + } + + offsGetVelocity0 = FindSendPropInfo("CBasePlayer", "m_vecVelocity[0]"); + if (offsGetVelocity0 == -1) + { + SetFailState("Couldn't find \"m_vecVelocity[0]\"!"); + } + + offsGetVelocity1 = FindSendPropInfo("CBasePlayer", "m_vecVelocity[1]"); + if (offsGetVelocity1 == -1) + { + SetFailState("Couldn't find \"m_vecVelocity[1]\"!"); + } + + offsGetVelocity2 = FindSendPropInfo("CBasePlayer", "m_vecVelocity[2]"); + if (offsGetVelocity2 == -1) + { + SetFailState("Couldn't find \"m_vecVelocity[2]\"!"); + } + + offsSpeed = FindSendPropInfo("CCSPlayer", "m_flLaggedMovementValue"); + if (offsSpeed == -1) + { + SetFailState("Couldn't find \"m_flLaggedMovementValue\"!"); + } + + offsNVG = FindSendPropInfo("CCSPlayer", "m_bHasNightVision"); + if (offsNVG == -1) + { + SetFailState("Couldn't find \"m_bHasNightVision\"!"); + } + + offsNVGOn = FindSendPropInfo("CCSPlayer", "m_bNightVisionOn"); + if (offsNVGOn == -1) + { + SetFailState("Couldn't find \"m_bNightVisionOn\"!"); + } + + offsCollision = FindSendPropInfo("CBaseEntity", "m_CollisionGroup"); + if (offsCollision == -1) + { + SetFailState("Couldn't find \"m_CollisionGroup\"!"); + } + + offsMoney = FindSendPropInfo("CCSPlayer", "m_iAccount"); + if (offsMoney == -1) + { + SetFailState("Couldn't find \"m_iAccount\"!"); + } + + offsFOV = FindSendPropInfo("CBasePlayer", "m_iDefaultFOV"); + if (offsFOV == -1) + { + SetFailState("Couldn't find \"m_iDefaultFOV\"!"); + } + + offsBuyZone = FindSendPropInfo("CCSPlayer", "m_bInBuyZone"); + if (offsBuyZone == -1) + { + SetFailState("Couldn't find \"m_bInBuyZone\"!"); + } +} + +SetupGameData() +{ + hGameConf = LoadGameConfigFile("plugin.zombiereloaded"); + + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "RemoveAllItems"); + hRemoveAllItems = EndPrepSDKCall(); + + StartPrepSDKCall(SDKCall_GameRules); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "TerminateRound"); + PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + hTerminateRound = EndPrepSDKCall(); +} + +SetPlayerVelocity(client, const Float:vec[3]) +{ + SetEntDataVector(client, offsBaseVelocity, vec, true); +} + +GetPlayerVelocity(client, Float:vel[3]) +{ + vel[0] = GetEntDataFloat(client, offsGetVelocity0); + vel[1] = GetEntDataFloat(client, offsGetVelocity1); + vel[2] = GetEntDataFloat(client, offsGetVelocity2); +} + +SetPlayerSpeed(client, Float:speed) +{ + new Float:newspeed = speed / 300.0; + SetEntDataFloat(client, offsSpeed, newspeed, true); +} + +NightVision(client, bool:enable) +{ + SetEntData(client, offsNVG, enable, 1, true); +} + +NightVisionOn(client, bool:enable) +{ + SetEntData(client, offsNVGOn, enable, 1, true); +} + +NoCollide(client, bool:nocollide) +{ + if (nocollide) + { + SetEntData(client, offsCollision, 2, 1, true); + } + else + { + SetEntData(client, offsCollision, 5, 1, true); + } +} + +SetPlayerMoney(client, amount) +{ + SetEntData(client, offsMoney, amount, 4, true); +} + +SetPlayerFOV(client, fov) +{ + SetEntData(client, offsFOV, fov, 1, true); +} + +bool:IsClientInBuyZone(client) +{ + return bool:GetEntData(client, offsBuyZone); +} + +AddPlayerScore(client, amount) +{ + new frags = GetEntProp(client, Prop_Data, "m_iFrags"); + SetEntProp(client, Prop_Data, "m_iFrags", frags + amount); +} + +AddPlayerDeath(client, amount) +{ + new deaths = GetEntProp(client, Prop_Data, "m_iDeaths"); + SetEntProp(client, Prop_Data, "m_iDeaths", deaths + amount); +} + +RemoveAllPlayersWeapons(client) +{ + SDKCall(hRemoveAllItems, client); +} + +TerminateRound(Float:delay, reason) +{ + SDKCall(hTerminateRound, delay, reason); +} + +SetPlayerModel(client, const String:model[]) +{ + PrecacheModel(model); + SetEntityModel(client, model); +} \ No newline at end of file diff --git a/src/zr/overlays.inc b/src/zr/overlays.inc new file mode 100644 index 0000000..fa54c36 --- /dev/null +++ b/src/zr/overlays.inc @@ -0,0 +1,47 @@ +/** + * ==================== + * Zombie:Reloaded + * File: overlays.inc + * Author: Greyscale + * ==================== + */ + +ShowOverlays(Float:time, ZTeam:winner) +{ + new bool:overlays = GetConVarBool(gCvars[CVAR_OVERLAYS]); + if (overlays) + { + decl String:overlay[64]; + if (winner == Human) + { + GetConVarString(gCvars[CVAR_OVERLAYS_HUMAN], overlay, sizeof(overlay)); + } + else if (winner == Zombie) + { + GetConVarString(gCvars[CVAR_OVERLAYS_ZOMBIE], overlay, sizeof(overlay)); + } + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (IsClientInGame(x)) + { + DisplayClientOverlay(x, overlay); + } + } + + CreateTimer(time, KillOverlays); + } +} + +public Action:KillOverlays(Handle:timer) +{ + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (IsClientInGame(x)) + { + ClientCommand(x, "r_screenoverlay \"\""); + } + } +} \ No newline at end of file diff --git a/src/zr/sayhooks.inc b/src/zr/sayhooks.inc new file mode 100644 index 0000000..7212e95 --- /dev/null +++ b/src/zr/sayhooks.inc @@ -0,0 +1,346 @@ +/** + * ==================== + * Zombie:Reloaded + * File: sayhooks.inc + * Author: Greyscale + * ==================== + */ + +HookChatCmds() +{ + RegConsoleCmd("say", SayCommand); + RegConsoleCmd("say_team", SayCommand); +} + +public Action:SayCommand(client, argc) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (!client || !enabled) + { + return Plugin_Continue; + } + + decl String:args[192]; + + GetCmdArgString(args, sizeof(args)); + ReplaceString(args, sizeof(args), "\"", ""); + + if (StrEqual(args, "!zmenu", false)) + { + MainMenu(client); + return Plugin_Handled; + } + + else if (StrEqual(args, "!zclass", false)) + { + ZClass(client); + return Plugin_Handled; + } + + else if (StrEqual(args, "!zmarket", false)) + { + ZMarket(client); + return Plugin_Handled; + } + + else if (StrEqual(args, "!zspawn", false)) + { + ZSpawn(client); + return Plugin_Handled; + } + + else if (StrEqual(args, "!ztele", false)) + { + ZTele(client); + return Plugin_Handled; + } + + else if (StrEqual(args, "!zstuck", false)) + { + ZStuck(client); + return Plugin_Handled; + } + + else if (StrEqual(args, "!zhp", false)) + { + ZHP(client); + return Plugin_Handled; + } + + return Plugin_Continue; +} + +bool:ZClass(client) +{ + new bool:classes = GetConVarBool(gCvars[CVAR_CLASSES]); + if (!classes) + { + ZR_PrintToChat(client, "Feature is disabled"); + + return false; + } + + new bool:randomclass = GetConVarBool(gCvars[CVAR_CLASSES_RANDOM]); + if (randomclass) + { + ZR_PrintToChat(client, "Random class is enabled"); + + return false; + } + + ClassMenu(client); + + return true; +} + +bool:ZMarket(client) +{ + if (!market) + { + ZR_PrintToChat(client, "Feature is disabled"); + + return false; + } + + if (!IsPlayerAlive(client)) + { + ZR_PrintToChat(client, "Must be alive"); + + return false; + } + + new bool:buyzone = GetConVarBool(gCvars[CVAR_ZMARKET_BUYZONE]); + if (!IsClientInBuyZone(client) && buyzone) + { + ZR_PrintCenterText(client, "Market out of buyzone"); + + return false; + } + + SetGlobalTransTarget(client); + + decl String:title[64]; + decl String:rebuy[64]; + + Format(title, sizeof(title), "%t\n ", "Market title"); + Format(rebuy, sizeof(rebuy), "%t\n ", "Market rebuy"); + + Market_Send(client, title, rebuy); + + return true; +} + +public bool:Market_OnWeaponSelected(client, String:weaponid[]) +{ + if (!weaponid[0] || !IsPlayerAlive(client)) + { + return false; + } + + if (IsPlayerZombie(client)) + { + ZR_PrintToChat(client, "Zombie cant use weapon"); + + return false; + } + + if (StrEqual(weaponid, "rebuy")) + { + return true; + } + + decl String:display[64]; + decl String:weapon[32]; + new price; + + if (!Market_GetWeaponIDInfo(weaponid, display, weapon, price)) + { + return false; + } + + ReplaceString(weapon, sizeof(weapon), "weapon_", ""); + + if (IsWeaponRestricted(weapon)) + { + ZR_PrintToChat(client, "Weapon is restricted", weapon); + + return false; + } + + new bool:buyzone = GetConVarBool(gCvars[CVAR_ZMARKET_BUYZONE]); + if (!IsClientInBuyZone(client) && buyzone) + { + ZR_PrintCenterText(client, "Market out of buyzone"); + + return false; + } + + return true; +} + +public Market_PostOnWeaponSelected(client, &bool:allowed) +{ + if (!allowed) + { + return; + } + + ZMarket(client); +} + +ZSpawn(client) +{ + new bool:spawn = GetConVarBool(gCvars[CVAR_ZSPAWN]); + if (!spawn) + { + ZR_PrintToChat(client, "Feature is disabled"); + + return; + } + + new team = GetClientTeam(client); + if (team != CS_TEAM_T && team != CS_TEAM_CT) + { + return; + } + + if (IsPlayerAlive(client) || IsPlayerInList(client)) + { + return; + } + + RespawnPlayer(client); + + AddPlayerToList(client); +} + +ZTele(client) +{ + new bool:tele = GetConVarBool(gCvars[CVAR_ZTELE]); + if (!tele) + { + ZR_PrintToChat(client, "Feature is disabled"); + + return; + } + + if (!IsPlayerZombie(client) && zombieSpawned) + { + ZR_PrintToChat(client, "!ztele humans restricted"); + + return; + } + + if (!IsPlayerAlive(client) || tHandles[client][TTELE] != INVALID_HANDLE) + { + return; + } + + new count = teleCount[client]; + new limit = GetConVarInt(gCvars[CVAR_ZTELE_LIMIT]); + + if (limit > 0) + { + if (count < limit) + { + tHandles[client][TTELE] = CreateTimer(3.0, Teleport, client, TIMER_FLAG_NO_MAPCHANGE); + teleCount[client]++; + ZR_PrintToChat(client, "!ztele amount", limit - teleCount[client]); + } + else + { + ZR_PrintToChat(client, "!ztele limit reached"); + } + } + else + { + tHandles[client][TTELE] = CreateTimer(3.0, Teleport, client, TIMER_FLAG_NO_MAPCHANGE); + ZR_PrintToChat(client, "!ztele amount unlimited"); + } +} + +public Action:Teleport(Handle:timer, any:index) +{ + TeleportEntity(index, spawnLoc[index], NULL_VECTOR, NULL_VECTOR); + + tHandles[index][TTELE] = INVALID_HANDLE; +} + +ZStuck(client) +{ + new bool:stuck = GetConVarBool(gCvars[CVAR_ZTELE]); + if (!stuck) + { + ZR_PrintToChat(client, "Feature is disabled"); + + return; + } + + if (!IsPlayerAlive(client)) + { + return; + } + + new Float:clientloc[3]; + new Float:stuckloc[3]; + + GetClientAbsOrigin(client, clientloc); + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x) || !IsPlayerAlive(x)) + { + continue; + } + + GetClientAbsOrigin(x, stuckloc); + if (GetVectorDistance(clientloc, stuckloc) <= 60) + { + NoCollide(x, true); + CreateTimer(0.5, CollisionOn, x, TIMER_FLAG_NO_MAPCHANGE); + } + } + + new Float:x = GetRandomFloat(-150.0, 150.0); + new Float:y = GetRandomFloat(-150.0, 150.0); + + new Float:nudge[3]; + + nudge[0] = x; + nudge[1] = y; + + SetPlayerVelocity(client, nudge); +} + +public Action:CollisionOn(Handle:timer, any:index) +{ + if (!IsClientInGame(index)) + { + return; + } + + NoCollide(index, false); +} + +ZHP(client) +{ + new bool:zhp = GetConVarBool(gCvars[CVAR_ZHP]); + if (!zhp) + { + ZR_PrintToChat(client, "Feature is disabled"); + + return; + } + + if (dispHP[client]) + { + ZR_PrintToChat(client, "!zhp disabled"); + } + else + { + ZR_PrintToChat(client, "!zhp enabled"); + UpdateHPDisplay(client); + } + + dispHP[client] = !dispHP[client]; +} \ No newline at end of file diff --git a/src/zr/translation.inc b/src/zr/translation.inc new file mode 100644 index 0000000..a808552 --- /dev/null +++ b/src/zr/translation.inc @@ -0,0 +1,118 @@ +/** + * ==================== + * Zombie:Reloaded + * File: translations.inc + * Author: Greyscale + * ==================== + */ + +FormatTextString(String:text[], maxlen) +{ + Format(text, maxlen, "@green[%t] @default%s", "ZR", text); + + ReplaceString(text, maxlen, "@default","\x01"); + ReplaceString(text, maxlen, "@lgreen","\x03"); + ReplaceString(text, maxlen, "@green","\x04"); +} + +stock ZR_PrintToChat(client, any:...) +{ + decl String:phrase[192]; + + if (client) + { + SetGlobalTransTarget(client); + + VFormat(phrase, sizeof(phrase), "%t", 2); + FormatTextString(phrase, sizeof(phrase)); + + PrintToChat(client, phrase); + } + else + { + SetGlobalTransTarget(client); + + VFormat(phrase, sizeof(phrase), "%t", 2); + FormatTextString(phrase, sizeof(phrase)); + + PrintToServer(phrase); + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (IsClientInGame(x)) + { + SetGlobalTransTarget(x); + + VFormat(phrase, sizeof(phrase), "%t", 2); + FormatTextString(phrase, sizeof(phrase)); + + PrintToChat(x, phrase); + } + } + } +} + +stock ZR_PrintCenterText(client, any:...) +{ + SetGlobalTransTarget(client); + + decl String:phrase[192]; + + VFormat(phrase, sizeof(phrase), "%t", 2); + + PrintCenterText(client, phrase); +} + +stock ZR_HudHint(client, any:...) +{ + SetGlobalTransTarget(client); + + decl String:phrase[192]; + + VFormat(phrase, sizeof(phrase), "%t", 2); + + new Handle:hHintText = StartMessageOne("HintText", client); + if (hHintText != INVALID_HANDLE) + { + BfWriteByte(hHintText, -1); + BfWriteString(hHintText, phrase); + EndMessage(); + } +} + +stock ZR_PrintToServer(any:...) +{ + SetGlobalTransTarget(LANG_SERVER); + + decl String:phrase[192]; + decl String:buffer[192]; + + VFormat(phrase, sizeof(phrase), "%t", 1); + Format(buffer, sizeof(buffer), "[%t] %s", "ZR", phrase); + + PrintToServer(buffer); +} + +stock ZR_LogMessage(any:...) +{ + SetGlobalTransTarget(LANG_SERVER); + + decl String:phrase[192]; + + VFormat(phrase, sizeof(phrase), "%t", 1); + + LogMessage(phrase); +} + +stock ZR_ReplyToCommand(client, any:...) +{ + decl String:phrase[192]; + + SetGlobalTransTarget(client); + + VFormat(phrase, sizeof(phrase), "%t", 2); + FormatTextString(phrase, sizeof(phrase)); + + ReplyToCommand(client, phrase); +} \ No newline at end of file diff --git a/src/zr/weaponrestrict.inc b/src/zr/weaponrestrict.inc new file mode 100644 index 0000000..669f9b0 --- /dev/null +++ b/src/zr/weaponrestrict.inc @@ -0,0 +1,261 @@ +/** + * ==================== + * Zombie:Reloaded + * File: weaponrestrict.inc + * Author: Greyscale + * ==================== + */ + +new Handle:restrictedWeapons = INVALID_HANDLE; + +new gRestrict[MAXPLAYERS+1]; + +enum WepRestrictQuery +{ + Successful, /** Weapon (un)restrict query was successful */ + Invalid, /** Weapon invalid */ + Existing, /** Already restricted */ +} + +InitWeaponRestrict() +{ + RegConsoleCmd("buy", BuyHook); + + restrictedWeapons = CreateArray(32, 0); +} + +ClientHookUse(client) +{ + gRestrict[client] = Hacks_Hook(client, HACKS_HTYPE_WEAPON_CANUSE, Weapon_CanUse, false); +} + +ClientUnHookUse(client) +{ + Hacks_Unhook(gRestrict[client]); +} + +public Action:BuyHook(client, argc) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (!enabled) + { + return Plugin_Continue; + } + + if (IsPlayerHuman(client)) + { + decl String:weapon[64]; + GetCmdArg(1, weapon, sizeof(weapon)); + + ReplaceString(weapon, sizeof(weapon), "weapon_", ""); + + if (IsWeaponRestricted(weapon)) + { + ZR_PrintToChat(client, "Weapon is restricted", weapon); + return Plugin_Handled; + } + } + else + { + ZR_PrintToChat(client, "Zombie cant use weapon"); + return Plugin_Handled; + } + + return Plugin_Continue; +} + +WepRestrictQuery:RestrictWeapon(const String:weapon[]) +{ + if (IsWeaponGroup(weapon)) + { + RestrictWeaponGroup(weapon); + + ZR_PrintToChat(0, "Weapon group has been restricted", weapon); + + return Successful; + } + + if (!IsWeaponRestricted(weapon)) + { + PushArrayString(restrictedWeapons, weapon); + + ZR_PrintToChat(0, "Weapon has been restricted", weapon); + + return Successful; + } + + return Existing; +} + +RestrictWeaponGroup(const String:group[]) +{ + if (StrEqual(group, "pistols", false)) + { + PushArrayString(restrictedWeapons, "glock"); + PushArrayString(restrictedWeapons, "usp"); + PushArrayString(restrictedWeapons, "p228"); + PushArrayString(restrictedWeapons, "deagle"); + PushArrayString(restrictedWeapons, "elite"); + PushArrayString(restrictedWeapons, "fiveseven"); + } + else if (StrEqual(group, "shotguns", false)) + { + PushArrayString(restrictedWeapons, "m3"); + PushArrayString(restrictedWeapons, "xm1014"); + } + else if (StrEqual(group, "smgs", false)) + { + PushArrayString(restrictedWeapons, "tmp"); + PushArrayString(restrictedWeapons, "mac10"); + PushArrayString(restrictedWeapons, "mp5navy"); + PushArrayString(restrictedWeapons, "ump45"); + PushArrayString(restrictedWeapons, "p90"); + } + else if (StrEqual(group, "rifles", false)) + { + PushArrayString(restrictedWeapons, "galil"); + PushArrayString(restrictedWeapons, "famas"); + PushArrayString(restrictedWeapons, "ak47"); + PushArrayString(restrictedWeapons, "m4a1"); + PushArrayString(restrictedWeapons, "sg552"); + PushArrayString(restrictedWeapons, "bullpup"); + } + else if (StrEqual(group, "snipers", false)) + { + PushArrayString(restrictedWeapons, "scout"); + PushArrayString(restrictedWeapons, "sg550"); + PushArrayString(restrictedWeapons, "g3sg1"); + PushArrayString(restrictedWeapons, "awp"); + } +} + +WepRestrictQuery:UnRestrictWeapon(const String:weapon[]) +{ + if (IsWeaponGroup(weapon)) + { + UnRestrictWeaponGroup(weapon); + + ZR_PrintToChat(0, "Weapon group has been unrestricted", weapon); + + return Successful; + } + + new index = GetRestrictedWeaponIndex(weapon); + + if (index > -1) + { + RemoveFromArray(restrictedWeapons, index); + + ZR_PrintToChat(0, "Weapon has been unrestricted", weapon); + + return Successful; + } + + return Invalid; +} + +UnRestrictWeaponGroup(const String:group[]) +{ + if (StrEqual(group, "pistols", false)) + { + UnRestrictWeapon("glock"); + UnRestrictWeapon("usp"); + UnRestrictWeapon("p228"); + UnRestrictWeapon("deagle"); + UnRestrictWeapon("elite"); + UnRestrictWeapon("fiveseven"); + } + else if (StrEqual(group, "shotguns", false)) + { + UnRestrictWeapon("m3"); + UnRestrictWeapon("xm1014"); + } + else if (StrEqual(group, "smgs", false)) + { + UnRestrictWeapon("tmp"); + UnRestrictWeapon("mac10"); + UnRestrictWeapon("mp5navy"); + UnRestrictWeapon("ump45"); + UnRestrictWeapon("p90"); + } + else if (StrEqual(group, "rifles", false)) + { + UnRestrictWeapon("galil"); + UnRestrictWeapon("famas"); + UnRestrictWeapon("ak47"); + UnRestrictWeapon("m4a1"); + UnRestrictWeapon("sg552"); + UnRestrictWeapon("bullpup"); + } + else if (StrEqual(group, "snipers", false)) + { + UnRestrictWeapon("scout"); + UnRestrictWeapon("sg550"); + UnRestrictWeapon("g3sg1"); + UnRestrictWeapon("awp"); + } +} + +bool:IsWeaponRestricted(const String:weapon[]) +{ + for (new x = 0; x < GetArraySize(restrictedWeapons); x++) + { + decl String:restrictedweapon[32]; + GetArrayString(restrictedWeapons, x, restrictedweapon, sizeof(restrictedweapon)); + + if (StrEqual(weapon, restrictedweapon, false)) + { + return true; + } + } + + return false; +} + +GetRestrictedWeaponIndex(const String:weapon[]) +{ + for (new x = 0; x < GetArraySize(restrictedWeapons); x++) + { + decl String:restrictedweapon[32]; + GetArrayString(restrictedWeapons, x, restrictedweapon, sizeof(restrictedweapon)); + ReplaceString(restrictedweapon, sizeof(restrictedweapon), "weapon_", ""); + + if (StrEqual(weapon, restrictedweapon, false)) + { + return x; + } + } + + return -1; +} + +bool:IsWeaponGroup(const String:weapon[]) +{ + return (StrEqual(weapon, "pistols", false) || StrEqual(weapon, "shotguns", false) || StrEqual(weapon, "smgs", false) || StrEqual(weapon, "rifles", false) || StrEqual(weapon, "snipers", false)); +} + +public Weapon_CanUse(client, weapon, dummy1, dummy2, dummy3, dummy4) +{ + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (!enabled) + { + return Hacks_Continue; + } + + new String:weaponname[32]; + GetEdictClassname(weapon, weaponname, sizeof(weaponname)); + + ReplaceString(weaponname, sizeof(weaponname), "weapon_", ""); + + if (IsWeaponRestricted(weaponname)) + { + return 0; + } + + if (IsPlayerZombie(client) && !StrEqual(weaponname, "knife")) + { + return 0; + } + + return Hacks_Continue; +} \ No newline at end of file diff --git a/src/zr/zombie.inc b/src/zr/zombie.inc new file mode 100644 index 0000000..210ccf7 --- /dev/null +++ b/src/zr/zombie.inc @@ -0,0 +1,887 @@ +/** + * ==================== + * Zombie:Reloaded + * File: zombie.inc + * Author: Greyscale + * ==================== + */ + +#define EXP_NODAMAGE 1 +#define EXP_REPEATABLE 2 +#define EXP_NOFIREBALL 4 +#define EXP_NOSMOKE 8 +#define EXP_NODECAL 16 +#define EXP_NOSPARKS 32 +#define EXP_NOSOUND 64 +#define EXP_RANDOMORIENTATION 128 +#define EXP_NOFIREBALLSMOKE 256 +#define EXP_NOPARTICLES 512 +#define EXP_NODLIGHTS 1024 +#define EXP_NOCLAMPMIN 2048 +#define EXP_NOCLAMPMAX 4096 + +new String:skyname[32]; + +HookCommands() +{ + RegConsoleCmd("nightvision", Command_NightVision); +} + +public Action:Command_NightVision(client, argc) +{ + new bool:allow_disable = GetConVarBool(gCvars[CVAR_ZVISION_ALLOW_DISABLE]); + if (!allow_disable) + { + return; + } + + new bool:enabled = GetConVarBool(gCvars[CVAR_ENABLE]); + if (!enabled) + { + return; + } + + if (!IsPlayerZombie(client)) + { + return; + } + + bZVision[client] = !bZVision[client]; + + if (bZVision[client]) + { + StartZVision(client); + } + else + { + StopZVision(client); + } +} + +FindMapSky() +{ + GetConVarString(FindConVar("sv_skyname"), skyname, sizeof(skyname)); +} + +ChangeLightStyle() +{ + new bool:dark = GetConVarBool(gCvars[CVAR_DARK]); + if (dark) + { + decl String:darkness[2]; + decl String:sky[32]; + + GetConVarString(gCvars[CVAR_DARK_LEVEL], darkness, sizeof(darkness)); + GetConVarString(gCvars[CVAR_DARK_SKY], sky, sizeof(sky)); + + SetLightStyle(0, darkness); + SetConVarString(FindConVar("sv_skyname"), sky, true, false); + } + else + { + SetLightStyle(0, "n"); + SetConVarString(FindConVar("sv_skyname"), skyname, true, false); + } +} + +public RestartGameHook(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + SetConVarInt(FindConVar("mp_restartgame"), 0); + TerminateRound(StringToFloat(newValue), Round_Draw); +} + +public Action:MotherZombie(Handle:timer) +{ + RefreshList(); + + new size = GetArraySize(pList); + new immune = 0; + + for (new x = 0; x < size; x++) + { + new index = GetArrayCell(pList, x); + + if (gBlockMotherInfect[index]) + { + immune++; + } + + if (!IsPlayerAlive(index) || IsPlayerZombie(index)) + { + continue; + } + + CS_SwitchTeam(index, CS_TEAM_CT); + } + + if (!(size - immune)) + { + tInfect = INVALID_HANDLE; + + return; + } + + new randclient; + new ratio = GetConVarInt(gCvars[CVAR_MOTHER_ZOMBIE_RATIO]); + + if (ratio <= 0) + { + do + { + randclient = RandomPlayerFromList(); + } while (!IsPlayerAlive(randclient) || gBlockMotherInfect[randclient]); + + Zombify_Mother(randclient); + } + else + { + new mothercount = RoundToCeil(float(size) / ratio); + + for (new x = 0; x < mothercount; x++) + { + do + { + randclient = RandomPlayerFromList(); + } while (IsPlayerZombie(randclient) || !IsPlayerAlive(randclient) || gBlockMotherInfect[randclient]); + + Zombify_Mother(randclient); + } + } + + tInfect = INVALID_HANDLE; + + zombieSpawned = true; +} + +Zombify_Mother(client) +{ + Call_StartForward(hZombify); + Call_PushCell(client); + Call_PushCell(true); + Call_Finish(); + + gZombie[client] = true; + motherZombie[client] = true; + + ApplyZombieHealth(client,true); + + ApplyZombieSpeed(client); + + CS_SwitchTeam(client, CS_TEAM_T); + + RemoveAllPlayersWeapons(client); + GivePlayerItem(client, "weapon_knife"); + + ApplyZombieNightVision(client); + + ApplyZombieFOV(client); + + ApplyZombieModel(client); + + InfectionEffects(client); + + ZR_PrintToChat(client, "You are a zombie"); + + if (bZVision[client]) + { + StartZVision(client); + } + + new bool:mother_zombie_respawn = GetConVarBool(gCvars[CVAR_MOTHER_ZOMBIE_RESPAWN]); + if (mother_zombie_respawn) + { + TeleportEntity(client, spawnLoc[client], NULL_VECTOR, NULL_VECTOR); + } + + new Float:interval = GetConVarFloat(gCvars[CVAR_EMITSOUNDS]); + if (tHandles[client][TMOAN] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][TMOAN]); + } + tHandles[client][TMOAN] = CreateTimer(interval, ZombieMoanTimer, client, TIMER_REPEAT); + + if (tHandles[client][TPROTECT] != INVALID_HANDLE) + { + TriggerTimer(tHandles[client][TPROTECT]); + tHandles[client][TPROTECT] = INVALID_HANDLE; + } + + if (tHandles[client][TZHP] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][TZHP]); + tHandles[client][TZHP] = INVALID_HANDLE; + } + + new bool:zhp = GetConVarBool(gCvars[CVAR_ZHP]); + if (zhp) + { + UpdateHPDisplay(client); + + tHandles[client][TZHP] = CreateTimer(5.0, ZHPTimer, client, TIMER_REPEAT); + } +} + +Zombify(client, attacker) +{ + if (attacker != 0) + { + new Handle:event = CreateEvent("player_death"); + if (event != INVALID_HANDLE) + { + SetEventInt(event, "userid", GetClientUserId(client)); + SetEventInt(event, "attacker", GetClientUserId(attacker)); + SetEventString(event, "weapon", "zombie_claws_of_death"); + FireEvent(event, false); + } + } + + Call_StartForward(hZombify); + Call_PushCell(client); + Call_PushCell(false); + Call_Finish(); + + gZombie[client] = true; + + ApplyZombieHealth(client, false); + + ApplyZombieSpeed(client); + + RemoveAllPlayersWeapons(client); + GivePlayerItem(client, "weapon_knife"); + + ApplyZombieNightVision(client); + + ApplyZombieFOV(client); + + ApplyZombieModel(client); + + if (bZVision[client]) + { + StartZVision(client); + } + + InfectionEffects(client); + + new ZTeam:team = IsRoundOver(); + RoundWin(team); + + CS_SwitchTeam(client, CS_TEAM_T); + + new Float:interval = GetConVarFloat(gCvars[CVAR_EMITSOUNDS]); + if (tHandles[client][TMOAN] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][TMOAN]); + } + tHandles[client][TMOAN] = CreateTimer(interval, ZombieMoanTimer, client, TIMER_REPEAT); + + if (tHandles[client][TPROTECT] != INVALID_HANDLE) + { + TriggerTimer(tHandles[client][TPROTECT]); + tHandles[client][TPROTECT] = INVALID_HANDLE; + } + + if (tHandles[client][TZHP] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][TZHP]); + tHandles[client][TZHP] = INVALID_HANDLE; + } + + new bool:zhp = GetConVarBool(gCvars[CVAR_ZHP]); + if (zhp) + { + UpdateHPDisplay(client); + + tHandles[client][TZHP] = CreateTimer(5.0, ZHPTimer, client, TIMER_REPEAT); + } + + AntiStick(attacker, client); +} + +InfectionEffects(client) +{ + new Float:clientloc[3]; + new Float:direction[3] = {0.0, 0.0, 0.0}; + + GetClientAbsOrigin(client, clientloc); + clientloc[2] += 30; + + decl String:sound[128]; + GetConVarString(gCvars[CVAR_INFECT_SOUND], sound, sizeof(sound)); + if (sound[0]) + { + PrecacheSound(sound); + EmitSoundToAll(sound, client, SNDCHAN_AUTO, SNDLEVEL_SCREAMING); + } + + new bool:esplash = GetConVarBool(gCvars[CVAR_INFECT_ESPLASH]); + if (esplash) + { + TE_SetupEnergySplash(clientloc, direction, true); + TE_SendToAll(); + } + + new explosion = CreateEntityByName("env_explosion"); + + if (explosion != -1) + { + new flags = GetEntProp(explosion, Prop_Data, "m_spawnflags"); + flags = flags | EXP_NODAMAGE | EXP_NODECAL; + + new bool:fireball = GetConVarBool(gCvars[CVAR_INFECT_FIREBALL]); + if (!fireball) + { + flags = flags | EXP_NOFIREBALL; + } + + new bool:smoke = GetConVarBool(gCvars[CVAR_INFECT_SMOKE]); + if (!smoke) + { + flags = flags | EXP_NOSMOKE; + } + + new bool:sparks = GetConVarBool(gCvars[CVAR_INFECT_SPARKS]); + if (!sparks) + { + flags = flags | EXP_NOSPARKS; + } + + SetEntProp(explosion, Prop_Data, "m_spawnflags", flags); + + DispatchSpawn(explosion); + + PrecacheModel("materials/sprites/xfireball3.vmt"); + + DispatchKeyValueVector(explosion, "origin", clientloc); + DispatchKeyValue(explosion, "fireballsprite", "materials/sprites/xfireball3.vmt"); + + AcceptEntityInput(explosion, "Explode"); + } + + new bool:shake = GetConVarBool(gCvars[CVAR_INFECT_SHAKE]); + if (shake) + { + new Handle:hShake = StartMessageOne("Shake", client); + if (hShake != INVALID_HANDLE) + { + BfWriteByte(hShake, 0); + BfWriteFloat(hShake, GetConVarFloat(gCvars[CVAR_INFECT_SHAKE_AMP])); + BfWriteFloat(hShake, GetConVarFloat(gCvars[CVAR_INFECT_SHAKE_FREQUENCY])); + BfWriteFloat(hShake, GetConVarFloat(gCvars[CVAR_INFECT_SHAKE_DURATION])); + + EndMessage(); + } + } +} + +ApplyZombieHealth(client, bool:mother) +{ + new health = GetClassHealth(pClass[client]); + + if (mother) + { + health *= 2; + } + + SetEntityHealth(client, health); +} + +ApplyZombieSpeed(client) +{ + new Float:speed = GetClassSpeed(pClass[client]); + SetPlayerSpeed(client, speed); +} + +ApplyZombieNightVision(client) +{ + new bool:nvgs = GetClassNVGs(pClass[client]); + NightVision(client, nvgs); + NightVisionOn(client, nvgs); +} + +ApplyZombieFOV(client) +{ + new fov = GetClassFOV(pClass[client]); + SetPlayerFOV(client, fov); +} + +KnockBack(client, const Float:clientloc[3], const Float:attackerloc[3], Float:power, dmg, bool:boost) +{ + if (!IsPlayerZombie(client)) + { + return; + } + + new Float:vector[3]; + + MakeVectorFromPoints(attackerloc, clientloc, vector); + NormalizeVector(vector, vector); + + vector[0] *= power * (float(dmg) * 1.5); + vector[1] *= power * (float(dmg) * 1.5); + vector[2] *= power * (float(dmg) * 1.5); + + if (boost) + { + ScaleVector(vector, 4.0); + } + + SetPlayerVelocity(client, vector); +} + +JumpBoost(client, Float:distance, Float:height) +{ + new Float:vel[3]; + + GetPlayerVelocity(client, vel); + + vel[0] *= distance; + vel[1] *= distance; + vel[2] = height; + + SetPlayerVelocity(client, vel); +} + +PlayerLeft(client) +{ + if (!IsClientInGame(client)) + { + return; + } + + new ZTeam:team = IsRoundOver(); + if (team == Zombie) + { + RoundWin(team); + return; + } + + if (!IsPlayerAlive(client) || !IsPlayerZombie(client)) + { + return; + } + + new zombiecount = GetZTeamCount(Zombie); + if (zombiecount > 1) + { + return; + } + + new count = GetTeamClientCount(CS_TEAM_CT); + if (count <= 1) + { + return; + } + + new Handle:aClients = CreateArray(); + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x) || !IsPlayerAlive(x) || client == x || GetClientTeam(x) != CS_TEAM_CT || gBlockMotherInfect[x]) + { + continue; + } + + PushArrayCell(aClients, x); + } + + new size = GetArraySize(aClients); + if (!size) + { + return; + } + + new randclient = GetArrayCell(aClients, GetRandomInt(0, size-1)); + Zombify_Mother(randclient); + + ZR_PrintToChat(randclient, "Zombie replacement"); + + CloseHandle(aClients); +} + +GetZTeamCount(ZTeam:team) +{ + new count = 0; + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x) || !IsPlayerAlive(x)) + { + continue; + } + + new ZTeam:pTeam = GetPlayerZTeam(x); + if (pTeam == team) + { + count++; + } + } + + return count; +} + +ZTeam:IsRoundOver() +{ + new bool:zombies = false; + new bool:humans = false; + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x) || !IsPlayerAlive(x)) + { + continue; + } + + if (IsPlayerZombie(x)) + { + zombies = true; + } + else + { + humans = true; + } + } + + if (zombies && !humans) + { + return Zombie; + } + + if (humans && !zombies) + { + if (zombieSpawned) + { + return Human; + } + } + + return Neither; +} + +RoundWin(ZTeam:team) +{ + if (team == Human) + { + TerminateRound(5.0, CTs_PreventEscape); + } + else if (team == Zombie) + { + TerminateRound(5.0, Terrorists_Escaped); + } +} + +BalanceTeams() +{ + new count = 0; + new cPlayers[MAXPLAYERS]; + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (!IsClientInGame(x) || GetClientTeam(x) <= 1) + { + continue; + } + + CS_SwitchTeam(x, CS_TEAM_T); + cPlayers[count++] = x; + } + + for (new x = 0; x < count; x++) + { + if (!IsClientInGame(cPlayers[x]) || GetClientTeam(cPlayers[x]) <= 1) + { + continue; + } + + CS_SwitchTeam(cPlayers[x], CS_TEAM_CT); + x++; + } +} + +RemoveObjectives() +{ + decl String:classname[64]; + + new maxentities = GetMaxEntities(); + for (new x = 0; x <= maxentities; x++) + { + if(!IsValidEdict(x)) + { + continue; + } + + GetEdictClassname(x, classname, sizeof(classname)); + if( StrEqual(classname, "func_bomb_target") || + StrEqual(classname, "func_hostage_rescue") || + StrEqual(classname, "c4") || + StrEqual(classname, "hostage_entity")) + { + RemoveEdict(x); + } + } +} + +AntiStick(attacker, client) +{ + if (!attacker || !IsClientInGame(attacker)) + { + return; + } + + new Float:vector[3]; + + new Float:attackerloc[3]; + new Float:clientloc[3]; + + GetClientAbsOrigin(attacker, attackerloc); + GetClientAbsOrigin(client, clientloc); + + MakeVectorFromPoints(attackerloc, clientloc, vector); + + NormalizeVector(vector, vector); + ScaleVector(vector, -160.0); + + TeleportEntity(attacker, NULL_VECTOR, NULL_VECTOR, vector); +} + +StartZVision(client) +{ + if (tHandles[client][TZVISION] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][TZVISION]); + } + + new bool:zvision = ZVision(client); + if (zvision) + { + new Float:redisplay = GetConVarFloat(gCvars[CVAR_ZVISION_REDISPLAY]); + tHandles[client][TZVISION] = CreateTimer(redisplay, ZVisionTimer, client, TIMER_REPEAT); + } +} + +StopZVision(client) +{ + if (tHandles[client][TZVISION] != INVALID_HANDLE) + { + CloseHandle(tHandles[client][TZVISION]); + tHandles[client][TZVISION] = INVALID_HANDLE; + } + + ClientCommand(client, "r_screenoverlay \"\""); +} + +bool:ZVision(client) +{ + if (IsFakeClient(client)) + { + return false; + } + + decl String:zvision[64]; + GetClassZVision(pClass[client], zvision, sizeof(zvision)); + + if (zvision[0]) + { + DisplayClientOverlay(client, zvision); + + return true; + } + + return false; +} + +public Action:ZVisionTimer(Handle:timer, any:index) +{ + if (!IsClientInGame(index) || !IsPlayerZombie(index)) + { + tHandles[index][TZVISION] = INVALID_HANDLE; + + return Plugin_Stop; + } + + ZVision(index); + + return Plugin_Continue; +} + +ZombieMoan(client) +{ + decl String:sound[64]; + + new randsound = GetRandomInt(1, 14); + Format(sound, sizeof(sound), "npc/zombie/zombie_voice_idle%d.wav", randsound); + + PrecacheSound(sound); + EmitSoundToAll(sound, client, SNDCHAN_AUTO, SNDLEVEL_SCREAMING); +} + +public Action:ZombieMoanTimer(Handle:timer, any:index) +{ + if (!IsClientInGame(index) || !IsPlayerZombie(index)) + { + tHandles[index][TMOAN] = INVALID_HANDLE; + + return Plugin_Stop; + } + + ZombieMoan(index); + + return Plugin_Continue; +} + +public Action:Regenerate(Handle:timer, any:index) +{ + if (!IsClientInGame(index) || !IsPlayerZombie(index)) + { + tHandles[index][TREGEN] = INVALID_HANDLE; + + return Plugin_Stop; + } + + new health = GetClassRegenHealth(pClass[index]); + new maxhealth = GetClassHealth(pClass[index]); + + if (motherZombie[index]) + { + maxhealth *= 2; + } + + new newhealth = GetClientHealth(index) + health; + if (newhealth > maxhealth) + { + newhealth = maxhealth; + + tHandles[index][TREGEN] = INVALID_HANDLE; + + return Plugin_Stop; + } + + SetEntityHealth(index, newhealth); + + UpdateHPDisplay(index); + + return Plugin_Continue; +} + +UpdateHPDisplay(client) +{ + new bool:zhp = GetConVarBool(gCvars[CVAR_ZHP]); + if (!zhp) + { + return; + } + + if (!IsPlayerZombie(client) || !dispHP[client]) + { + return; + } + + new health = GetClientHealth(client); + if (health < 0) + { + health = 0; + } + + ZR_HudHint(client, "Display HP", health); +} + +public Action:ZHPTimer(Handle:timer, any:index) +{ + if (!IsClientInGame(index)) + { + tHandles[index][TZHP] = INVALID_HANDLE; + + return Plugin_Stop; + } + + UpdateHPDisplay(index); + + return Plugin_Continue; +} + +public Action:EndProtect(Handle:timer, any:index) +{ + if (!IsClientInGame(index)) + { + tHandles[index][TPROTECT] = INVALID_HANDLE; + + return; + } + + pProtect[index] = false; + + if (IsPlayerHuman(index)) + { + ZR_PrintCenterText(index, "Spawn protection end"); + } + + tHandles[index][TPROTECT] = INVALID_HANDLE; +} + +RespawnPlayer(client) +{ + if (!IsClientInGame(client)) + { + return; + } + + CS_RespawnPlayer(client); + + if (!zombieSpawned) + { + return; + } + + decl String:team[32]; + GetConVarString(gCvars[CVAR_RESPAWN_TEAM], team, sizeof(team)); + + if (StrEqual(team, "zombie", false)) + { + Zombify(client, 0); + } +} + +public Action:RespawnTimer(Handle:timer, any:index) +{ + new team = GetClientTeam(index); + if (!IsClientInGame(index) || IsPlayerAlive(index) || team != CS_TEAM_T && team != CS_TEAM_CT) + { + tHandles[index][TZHP] = INVALID_HANDLE; + + return; + } + + RespawnPlayer(index); + + tHandles[index][TRESPAWN] = INVALID_HANDLE; +} + +public Action:RoundOver(Handle:timer) +{ + tRound = INVALID_HANDLE; + + RoundWin(Human); +} + +bool:IsPlayerZombie(client) +{ + return gZombie[client]; +} + +bool:IsPlayerHuman(client) +{ + return !gZombie[client]; +} + +ZTeam:GetPlayerZTeam(client) +{ + if (IsPlayerZombie(client)) + { + return Zombie; + } + + return Human; +} \ No newline at end of file diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc new file mode 100644 index 0000000..1e8a1fa --- /dev/null +++ b/src/zr/zombiereloaded.inc @@ -0,0 +1,177 @@ +/** + * ==================== + * Zombie:Reloaded + * File: zombiereloaded.inc + * Author: Greyscale + * ==================== + */ + +enum ZTeam +{ + Neither, /** Round is not over */ + Zombie, /** Round is over because zombies win */ + Human, /** Round is over because humans wins */ +} + +#define Target_Bombed 1 // Target Successfully Bombed! +#define VIP_Escaped 2 // The VIP has escaped! +#define VIP_Assassinated 3 // VIP has been assassinated! +#define Terrorists_Escaped 4 // The terrorists have escaped! +#define CTs_PreventEscape 5 // The CT's have prevented most of the terrorists from escaping! +#define Escaping_Terrorists_Neutralized 6 // Escaping terrorists have all been neutralized! +#define Bomb_Defused 7 // The bomb has been defused! +#define CTs_Win 8 // Counter-Terrorists Win! +#define Terrorists_Win 9 // Terrorists Win! +#define Round_Draw 10 // Round Draw! +#define All_Hostages_Rescued 11 // All Hostages have been rescued! +#define Target_Saved 12 // Target has been saved! +#define Hostages_Not_Rescued 13 // Hostages have not been rescued! +#define Terrorists_Not_Escaped 14 // Terrorists have not escaped! +#define VIP_Not_Escaped 15 // VIP has not escaped! +#define Game_Commencing 16 // Game Commencing! + +#define DXLEVEL_MIN 90 + +#define DEFAULT_FOV 90 + +new bool:market; + +new dxLevel[MAXPLAYERS+1]; + +new bool:zombieSpawned; +new bool:motherZombie[MAXPLAYERS+1]; +new bool:gZombie[MAXPLAYERS+1]; +new bool:gBlockMotherInfect[MAXPLAYERS+1]; +new bool:bZVision[MAXPLAYERS+1]; +new bool:dispHP[MAXPLAYERS+1]; +new bool:pProtect[MAXPLAYERS+1]; + +new pClass[MAXPLAYERS+1]; +new pNextClass[MAXPLAYERS+1]; + +new teleCount[MAXPLAYERS+1]; +new Float:spawnLoc[MAXPLAYERS+1][3]; + +new Handle:tRound = INVALID_HANDLE; +new Handle:tInfect = INVALID_HANDLE; + +new Handle:pList = INVALID_HANDLE; + +#define MAXTIMERS 7 + +#define TMOAN 0 +#define TREGEN 1 +#define TTELE 2 +#define TZHP 3 +#define TPROTECT 4 +#define TRESPAWN 5 +#define TZVISION 6 + +new Handle:tHandles[MAXPLAYERS+1][MAXTIMERS]; + +new QueryCookie:mat_dxlevel; + +FindClientDXLevel(client) +{ + if (IsFakeClient(client)) + { + return; + } + + mat_dxlevel = QueryClientConVar(client, "mat_dxlevel", DXLevelClientQuery); +} + +public DXLevelClientQuery(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[]) +{ + if (cookie != mat_dxlevel) + { + return; + } + + dxLevel[client] = 0; + + if (result != ConVarQuery_Okay) + { + return; + } + + dxLevel[client] = StringToInt(cvarValue); +} + +DisplayClientOverlay(client, const String:overlay[]) +{ + if (!dxLevel[client]) + { + FindClientDXLevel(client); + + return; + } + + if (dxLevel[client] >= DXLEVEL_MIN) + { + ClientCommand(client, "r_screenoverlay \"%s\"", overlay); + } + else + { + ZR_PrintCenterText(client, "DX90 not supported", dxLevel[client], DXLEVEL_MIN); + } +} + +RefreshList() +{ + ClearList(); + + pList = CreateArray(); + + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + if (IsClientInGame(x)) + { + new team = GetClientTeam(x); + if (team == CS_TEAM_T || team == CS_TEAM_CT) + { + PushArrayCell(pList, x); + } + } + } +} + +AddPlayerToList(client) +{ + if (pList != INVALID_HANDLE) + { + PushArrayCell(pList, client); + } +} + +ClearList() +{ + if (pList != INVALID_HANDLE) + { + ClearArray(pList); + } +} + +RandomPlayerFromList() +{ + if (pList != INVALID_HANDLE) + { + new size = GetArraySize(pList); + new index = GetRandomInt(0, size - 1); + + return GetArrayCell(pList, index); + } + + return -1; +} + +bool:IsPlayerInList(client) +{ + if (pList != INVALID_HANDLE) + { + return (FindValueInArray(pList, client) != -1); + } + + return false; +}