Added basic class API: ZR_IsValidClassIndex, ZR_GetActiveClass, ZR_SelectClientClass, ZR_GetClassByName, ZR_GetClassDisplayName

This commit is contained in:
Richard Helgeby 2012-09-08 22:34:31 +02:00
parent 393044aa87
commit 8bec3be02d
10 changed files with 562 additions and 68 deletions

View File

@ -33,3 +33,4 @@
#include <zr/infect.zr> #include <zr/infect.zr>
#include <zr/respawn.zr> #include <zr/respawn.zr>
#include <zr/class.zr>

109
src/include/zr/class.zr.inc Normal file
View File

@ -0,0 +1,109 @@
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: class.zr.inc
* Type: Include
* Description: Player class API.
*
* Copyright (C) 2009-2012 Greyscale, Richard Helgeby
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*
* ============================================================================
*/
/**
* @section Internal class cache types. Specifies which class data to access.
*/
#define ZR_CLASS_CACHE_ORIGINAL 0 /** Original class data loaded from file. */
#define ZR_CLASS_CACHE_MODIFIED 1 /** Default cache. Class data modified by eventual multipliers, map configs, commands, etc. */
#define ZR_CLASS_CACHE_PLAYER 2 /** Current player class attributes. The class index parameter is used as client index when reading from this cache. */
/**
* @endsection
*/
/**
* Results when selecting a class for a player.
*/
enum ClassSelectResult
{
ClassSelected_NoChange, /** No class change was necessary (class already selected). */
ClassSelected_Instant, /** Class was instantly changed. */
ClassSelected_NextSpawn /** Class will be used next spawn. */
}
/**
* Returns whether a class index is valid or not.
*
* @param classIndex Class index to validate.
*
* @return True if valid, false otherwise.
*/
native bool:ZR_IsValidClassIndex(classIndex);
/**
* Gets the currently active class index that the player is using.
*
* @param client The client index.
*
* @return The active class index.
*/
native bool:ZR_GetActiveClass(client);
/**
* Selects a class for a player.
*
* Human class attribute may be instantly applied if player is alive, human and
* instant class change is enabled. Otherwise only the selected index will be
* updated for next spawn.
*
* Class selection will be saved in client cookies if enabled.
*
* @param client Client index.
* @param classIndex Class index.
* @param applyIfPossible Optional. Apply class attributes if conditions allow
* it. Default is true.
* @param saveIfEnabled Optional. Save class selection in client cookies if
* enabled. Default is true.
*
* @return Class selection result. See enum ClassSelectResult.
*/
native ClassSelectResult:ZR_SelectClientClass(client, classIndex, bool:applyIfPossible = true, bool:saveIfEnabled = true);
/**
* Gets the class index of the class with the specified name.
*
* Note: This search is linear and probably won't perform well in large loops.
*
* @param className Class name to search for.
* @param cacheType Optional. Specifies which class cache to read from,
* except player cache.
*
* @return Class index, or -1 if none found.
*/
native ZR_GetClassByName(const String:className[], cacheType = ZR_CLASS_CACHE_MODIFIED);
/**
* Gets the class name displayed in the class menu.
*
* @param index Index of the class in a class cache or a client index,
* depending on the cache type specified.
* @param buffer The destination string buffer.
* @param maxlen The length of the destination string buffer.
* @param cacheType Optional. Specifies which class cache to read from.
* @return Number of cells written. -1 on error.
*/
native ZR_GetClassDisplayName(index, String:buffer[], maxlen, cacheType = ZR_CLASS_CACHE_MODIFIED);

View File

@ -0,0 +1,177 @@
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: classapitest.sp
* Type: Test plugin
* Description: Tests the class API.
*
* Copyright (C) 2009-2012 Greyscale, Richard Helgeby
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*
* ============================================================================
*/
#pragma semicolon 1
#include <sourcemod>
#include <zombiereloaded>
public Plugin:myinfo =
{
name = "Zombie:Reloaded Class API Test",
author = "Greyscale | Richard Helgeby",
description = "Tests the class API for ZR",
version = "1.0.0",
url = "http://code.google.com/p/zombiereloaded/"
};
public OnPluginStart()
{
LoadTranslations("common.phrases");
RegConsoleCmd("zrtest_is_valid_class_index", IsValidClassCommand, "Returns whether the specified class index is valid or not. Usage: zrtest_is_valid_class_index <class index>");
RegConsoleCmd("zrtest_get_active_class", GetActiveClassCommand, "Gets the current class the specified player is using. Usage: zrtest_get_active_class <target>");
RegConsoleCmd("zrtest_select_class", SelectClassCommand, "Selects a class for a player. Usage: zrtest_select_class <target> <class index>");
RegConsoleCmd("zrtest_get_class_by_name", GetClassCommand, "Gets class index by class name. Usage: zrtest_get_class_by_name <class name>");
RegConsoleCmd("zrtest_get_class_display_name", GetNameCommand, "Gets class display name. Usage: zrtest_get_class_display_name <class index>");
}
public Action:IsValidClassCommand(client, argc)
{
new classIndex = -1;
new String:valueString[64];
if (argc >= 1)
{
GetCmdArg(1, valueString, sizeof(valueString));
classIndex = StringToInt(valueString);
}
else
{
ReplyToCommand(client, "Returns whether the specified class index is valid or not. Usage: zrtest_is_valid_class_index <class index>");
return Plugin_Handled;
}
ReplyToCommand(client, "Class %d is valid: %d", classIndex, ZR_IsValidClassIndex(classIndex));
return Plugin_Handled;
}
public Action:GetActiveClassCommand(client, argc)
{
new target = -1;
new String:valueString[64];
if (argc >= 1)
{
GetCmdArg(1, valueString, sizeof(valueString));
target = FindTarget(client, valueString);
}
else
{
ReplyToCommand(client, "Gets the current class the specified player is using. Usage: zrtest_get_active_class <target>");
return Plugin_Handled;
}
ReplyToCommand(client, "Active class of client %d: %d", target, ZR_GetActiveClass(target));
return Plugin_Handled;
}
public Action:SelectClassCommand(client, argc)
{
new target = -1;
new classIndex = -1;
new bool:applyIfPossible = true;
new bool:saveIfEnabled = true;
new String:valueString[64];
if (argc >= 1)
{
GetCmdArg(1, valueString, sizeof(valueString));
target = FindTarget(client, valueString);
GetCmdArg(2, valueString, sizeof(valueString));
classIndex = StringToInt(valueString);
if (argc >= 4)
{
GetCmdArg(3, valueString, sizeof(valueString));
applyIfPossible = bool:StringToInt(valueString);
GetCmdArg(4, valueString, sizeof(valueString));
saveIfEnabled = bool:StringToInt(valueString);
}
}
else
{
ReplyToCommand(client, "Selects a class for a player. Usage: zrtest_select_class <target> <class index> [<apply if possible> <save if enabled>]");
return Plugin_Handled;
}
ReplyToCommand(client, "Selected class %d for client %d. Result: %d", classIndex, target, ZR_SelectClientClass(target, classIndex, applyIfPossible, saveIfEnabled));
return Plugin_Handled;
}
public Action:GetClassCommand(client, argc)
{
new String:className[64];
if (argc >= 1)
{
GetCmdArg(1, className, sizeof(className));
}
else
{
ReplyToCommand(client, "Gets class index by class name. Usage: zrtest_get_class_by_name <class name>");
return Plugin_Handled;
}
ReplyToCommand(client, "Class index of \"%s\": %d", className, ZR_GetClassByName(className));
return Plugin_Handled;
}
public Action:GetNameCommand(client, argc)
{
new classIndex = -1;
new String:valueString[64];
new String:displayName[64];
if (argc >= 1)
{
GetCmdArg(1, valueString, sizeof(valueString));
classIndex = StringToInt(valueString);
if (!ZR_IsValidClassIndex(classIndex))
{
ReplyToCommand(client, "Invalid class index: %d", classIndex);
return Plugin_Handled;
}
}
else
{
ReplyToCommand(client, "Gets class display name. Usage: zrtest_get_class_display_name <class index>");
return Plugin_Handled;
}
ZR_GetClassDisplayName(classIndex, displayName, sizeof(displayName));
ReplyToCommand(client, "Display name of class %d: \"%s\"", classIndex, displayName);
return Plugin_Handled;
}

View File

@ -43,6 +43,7 @@
#include "zr/api/infect.api" #include "zr/api/infect.api"
#include "zr/api/respawn.api" #include "zr/api/respawn.api"
#include "zr/api/class.api"
/** /**
* Initializes all main natives and forwards. * Initializes all main natives and forwards.
@ -52,6 +53,7 @@ APIInit()
// Forward event to sub-modules. // Forward event to sub-modules.
APIInfectInit(); APIInfectInit();
APIRespawnInit(); APIRespawnInit();
APIClassInit();
} }
/** /**
@ -68,12 +70,14 @@ stock APIValidateClientIndex(client, EligibleCondition:alive = Condition_Either)
if (client < 1 || client > MaxClients) if (client < 1 || client > MaxClients)
{ {
ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client); ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client);
return;
} }
// Verify that the client is connected. // Verify that the client is connected.
if (!IsClientConnected(client)) if (!IsClientConnected(client))
{ {
ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected.", client); ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected.", client);
return;
} }
// Check if the player must be dead or alive. // Check if the player must be dead or alive.

167
src/zr/api/class.api.inc Normal file
View File

@ -0,0 +1,167 @@
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: class.api.inc
* Type: Core
* Description: Native handlers for the ZR API. (Class module)
*
* Copyright (C) 2009-2012 Greyscale, Richard Helgeby
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*
* ============================================================================
*/
/**
* Initializes all natives and forwards related to infection.
*/
APIClassInit()
{
// Class module natives/forwards (class.zr.inc)
// Natives
CreateNative("ZR_IsValidClassIndex", APIIsValidClassIndex);
CreateNative("ZR_GetActiveClass", APIGetActiveClass);
CreateNative("ZR_SelectClientClass", APISelectClientClass);
CreateNative("ZR_GetClassByName", APIGetClassByName);
CreateNative("ZR_GetClassDisplayName", APIGetClassDisplayName);
}
/**
* Native call function (ZR_IsValidClassIndex)
*
* bool:ZR_IsValidClassIndex(classIndex);
*/
public APIIsValidClassIndex(Handle:plugin, numParams)
{
new classIndex = GetNativeCell(1);
return ClassValidateIndex(classIndex);
}
/**
* Native call function (ZR_GetActiveClass)
*
* native bool:ZR_GetActiveClass(client);
*/
public APIGetActiveClass(Handle:plugin, numParams)
{
new client = GetNativeCell(1);
// Validate the client index. Player must be alive.
APIValidateClientIndex(client, Condition_True);
return ClassGetActiveIndex(client);
}
/**
* Native call function (ZR_SelectClientClass)
*
* native ClassSelectResult:ZR_SelectClientClass(client, classIndex, bool:applyIfPossible = true, bool:saveIfEnabled = true)
*/
public APISelectClientClass(Handle:plugin, numParams)
{
new client = GetNativeCell(1);
new classIndex = GetNativeCell(2);
new bool:applyIfPossible = bool:GetNativeCell(3);
new bool:saveIfEnabled = bool:GetNativeCell(4);
// Validate the client index.
APIValidateClientIndex(client, Condition_Either);
// Validate class index.
if (!ClassValidateIndex(classIndex))
{
ThrowNativeError(SP_ERROR_NATIVE, "Invalid class index. (%d)", classIndex);
return -1;
}
return _:ClassSelectClientClass(client, classIndex, applyIfPossible, saveIfEnabled);
}
/**
* Native call function (ZR_GetClassByName)
*
* native ZR_GetClassByName(const String:className[], cacheType = ZR_CLASS_CACHE_MODIFIED);
*/
public APIGetClassByName(Handle:plugin, numParams)
{
decl String:className[64];
className[0] = 0;
// Get class name.
if (GetNativeString(1, className, sizeof(className)) != SP_ERROR_NONE)
{
ThrowNativeError(SP_ERROR_NATIVE, "Unexpected error when reading className parameter. Possibly corrupt or missing data.");
return -1;
}
new cacheType = GetNativeCell(2);
if (cacheType == ZR_CLASS_CACHE_PLAYER)
{
ThrowNativeError(SP_ERROR_NATIVE, "Invalid cache type. Player cache is not allowed in this function.");
return -1;
}
return ClassGetIndex(className, cacheType);
}
/**
* Native call function (ZR_GetClassDisplayName)
*
* native ZR_GetClassDisplayName(classIndex, String:buffer[], maxlen, cacheType = ZR_CLASS_CACHE_MODIFIED);
*/
public APIGetClassDisplayName(Handle:plugin, numParams)
{
new index = GetNativeCell(1);
new maxlen = GetNativeCell(3);
new cacheType = GetNativeCell(4);
if (maxlen <= 0)
{
// No buffer size.
return 0;
}
// Validate index.
if (cacheType == ZR_CLASS_CACHE_PLAYER)
{
// Client index.
APIValidateClientIndex(index, Condition_Either);
}
else
{
// Class index.
if (!ClassValidateIndex(index))
{
ThrowNativeError(SP_ERROR_NATIVE, "Invalid class index. (%d)", index);
return 0;
}
}
decl String:displayName[maxlen];
new bytes = ClassGetName(index, displayName, maxlen, cacheType);
if (bytes <= 0)
{
// The class doesn't have a name for some reason. Make sure the buffer is empty.
displayName[0] = 0;
}
SetNativeString(2, displayName, maxlen);
return bytes;
}

View File

@ -235,7 +235,7 @@ stock ClassGetGroup(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_P
} }
/** /**
* Gets the class name to be displayed in the class menu. * Gets the class name displayed in the class menu.
* *
* @param index Index of the class in a class cache or a client index, * @param index Index of the class in a class cache or a client index,
* depending on the cache type specified. * depending on the cache type specified.

View File

@ -340,77 +340,22 @@ ClassMenuSelect(client, teamid)
*/ */
public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot) public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot)
{ {
decl String:classname[MENU_LINE_REG_LENGTH]; decl String:className[MENU_LINE_REG_LENGTH];
new classindex; new classIndex;
new teamid;
new bool:autoclose = GetConVarBool(g_hCvarsList[CVAR_CLASSES_MENU_AUTOCLOSE]); new bool:autoclose = GetConVarBool(g_hCvarsList[CVAR_CLASSES_MENU_AUTOCLOSE]);
new bool:iszombie = InfectIsClientInfected(client);
switch (action) switch (action)
{ {
case MenuAction_Select: case MenuAction_Select:
{ {
// Get class name from the information string. // Get class name from the information string.
GetMenuItem(menu, slot, classname, sizeof(classname)); GetMenuItem(menu, slot, className, sizeof(className));
// Solve class index from the name. // Solve class index from the name.
classindex = ClassGetIndex(classname); classIndex = ClassGetIndex(className);
// Solve teamid from the class index. // Select (and eventually apply) class.
teamid = ClassGetTeamID(classindex, ZR_CLASS_CACHE_MODIFIED); ClassSelectClientClass(client, classIndex);
// Allow instant class change if enabled and both class and player is human.
if (ClassAllowInstantChange[client] && !iszombie && teamid == ZR_CLASS_TEAM_HUMANS)
{
// Directly change the selected class index.
ClassSelected[client][teamid] = classindex;
// Update cache and apply attributes.
ClassReloadPlayerCache(client, classindex);
ClassApplyAttributes(client);
}
else
{
// Check if the player is alive.
if (IsPlayerAlive(client))
{
// Set next spawn index if the player is changing the class on
// his active team.
if ((iszombie && teamid == ZR_CLASS_TEAM_ZOMBIES) ||
(!iszombie && teamid == ZR_CLASS_TEAM_HUMANS) ||
(ClassPlayerInAdminMode[client] && teamid == ZR_CLASS_TEAM_ADMINS))
{
// Check if player selected the same class that he already is.
if (ClassSelected[client][teamid] == classindex)
{
// Player is already the specified class. Disable
// next class for the specified team.
ClassSelectedNext[client][teamid] = -1;
}
else
{
// Set class to be used on next spawn.
ClassSelectedNext[client][teamid] = classindex;
}
}
else
{
// Directly change the selected class index.
ClassSelected[client][teamid] = classindex;
}
}
else
{
// Player isn't alive. The class can be directly changed.
ClassSelected[client][teamid] = classindex;
}
}
// Save selected class index in cookie if enabled.
if (GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]))
{
CookiesSetInt(client, g_hClassCookieClassSelected[teamid], classindex + 1);
}
} }
case MenuAction_Cancel: case MenuAction_Cancel:
{ {

View File

@ -587,12 +587,12 @@ stock ClassValidateEditableAttributes(attributes[ClassEditableAttributes])
/** /**
* Checks if the specified class index is a valid index. * Checks if the specified class index is a valid index.
* *
* @param classindex The class index to validate. * @param classIndex The class index to validate.
* @return True if the class exist, false otherwise. * @return True if the class exist, false otherwise.
*/ */
stock bool:ClassValidateIndex(classindex) stock bool:ClassValidateIndex(classIndex)
{ {
if (classindex >= 0 && classindex < ClassCount) if (classIndex >= 0 && classIndex < ClassCount)
{ {
return true; return true;
} }
@ -660,10 +660,10 @@ stock ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
{ {
decl String:current_name[64]; decl String:current_name[64];
// Check if there are no classes. // Check if there are no classes, or reading from player cache.
if (ClassCount == 0) if (ClassCount == 0 || cachetype == ZR_CLASS_CACHE_PLAYER)
{ {
return false; return -1;
} }
// Loop through all classes. // Loop through all classes.

View File

@ -377,6 +377,16 @@ enum ClassSpeedMethods
ClassSpeed_Prop, /** Modifies players' max speed property(m_flMaxspeed). */ ClassSpeed_Prop, /** Modifies players' max speed property(m_flMaxspeed). */
} }
/**
* Results when selecting a class for a player.
*/
enum ClassSelectResult
{
ClassSelected_NoChange, /** No class change was necessary (class already selected). */
ClassSelected_Instant, /** Class was instantly changed. */
ClassSelected_NextSpawn /** Class will be used next spawn. */
}
/** /**
* Empty filter structure. * Empty filter structure.
*/ */
@ -1196,6 +1206,86 @@ ClassClientSetDefaultIndexes(client = -1)
} }
} }
/**
* Selects a class for a player.
*
* Human class attribute may be instantly applied if player is alive, human and
* instant class change is enabled. Otherwise only the selected index will be
* updated for next spawn.
*
* Class selection will be saved in client cookies if enabled.
*
* @param client Client index.
* @param classIndex Class index.
* @param applyIfPossible Optional. Apply class attributes if conditions allow
* it. Default is true.
* @param saveIfEnabled Optional. Save class selection in client cookies if
* enabled. Default is true.
*
* @return Class selection result. See enum ClassSelectResult.
*/
ClassSelectResult:ClassSelectClientClass(client, classIndex, bool:applyIfPossible = true, bool:saveIfEnabled = true)
{
new bool:iszombie = InfectIsClientInfected(client);
new teamid = ClassGetTeamID(classIndex, ZR_CLASS_CACHE_MODIFIED);
new ClassSelectResult:selectResult = ClassSelected_NoChange;
// Allow instant class change if enabled and both class and player is human.
if (applyIfPossible &&
ClassAllowInstantChange[client] &&
!iszombie && teamid == ZR_CLASS_TEAM_HUMANS)
{
// Update selected class index.
ClassSelected[client][teamid] = classIndex;
// Update cache and apply attributes.
ClassReloadPlayerCache(client, classIndex);
ClassApplyAttributes(client);
selectResult = ClassSelected_Instant;
}
else
{
// Set next spawn index if the player is changing the class on
// his active team.
if (IsPlayerAlive(client) &&
(iszombie && teamid == ZR_CLASS_TEAM_ZOMBIES) ||
(!iszombie && teamid == ZR_CLASS_TEAM_HUMANS) ||
(ClassPlayerInAdminMode[client] && teamid == ZR_CLASS_TEAM_ADMINS))
{
// Check if selecting the same class that the player already is.
if (ClassSelected[client][teamid] == classIndex)
{
// Player is already the specified class. No need to change
// class next spawn.
ClassSelectedNext[client][teamid] = -1;
selectResult = ClassSelected_NoChange;
}
else
{
// Set class to be used on next spawn.
ClassSelectedNext[client][teamid] = classIndex;
selectResult = ClassSelected_NextSpawn;
}
}
else
{
// Directly change the selected class index.
ClassSelected[client][teamid] = classIndex;
selectResult = ClassSelected_NextSpawn;
}
}
// Save selected class index in cookie if enabled.
// Note: Saved indexes are increased by one.
if (saveIfEnabled && GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]))
{
CookiesSetInt(client, g_hClassCookieClassSelected[teamid], classIndex + 1);
}
return selectResult;
}
/** /**
* Dump class data into a string. String buffer length should be at about 2048 * Dump class data into a string. String buffer length should be at about 2048
* cells. * cells.

View File

@ -68,6 +68,7 @@ enum Game
* Current game. * Current game.
*/ */
new Game:g_game = Game_Unknown; new Game:g_game = Game_Unknown;
#pragma unused g_game
/** /**
* Updates g_game. Will log a warning if a unsupported game is detected. * Updates g_game. Will log a warning if a unsupported game is detected.