diff --git a/Makefile b/Makefile
index 95f5cee..f9f6f47 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ SPCOMP=env/linux/bin/spcomp-1.3.4
VERSIONDUMP=./updateversion.sh
vpath %.sp $(SOURCEDIR)
-vpath %.inc $(SOURCEDIR)/include
+vpath %.inc $(SOURCEDIR)/zr
vpath %.smx $(BUILDDIR)
SOURCEFILES=$(SOURCEDIR)/*.sp
@@ -19,7 +19,7 @@ prepare: prepare_newlines prepare_builddir
prepare_newlines:
@echo "Removing windows newlines"
- @find $(SOURCEDIR) -name \*.inc -exec dos2unix -p '{}' \;
+ @find $(SOURCEDIR)/zr -name \*.inc -exec dos2unix -p '{}' \;
@find $(SOURCEDIR) -name \*.sp -exec dos2unix -p '{}' \;
prepare_builddir:
diff --git a/buildtests.sh b/buildtests.sh
new file mode 100755
index 0000000..8fa166d
--- /dev/null
+++ b/buildtests.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+BUILD_DIR=build
+SRC=src/testsuite/zr
+SM_INCLUDE=env/include
+ZR_INCLUDE=src/include
+SPCOMP=env/linux/bin/spcomp-1.3.4
+
+mkdir -p $BUILD_DIR
+
+$SPCOMP -i$SM_INCLUDE -i$ZR_INCLUDE -o$BUILD_DIR/respawnapitest.smx $SRC/respawnapitest.sp
+$SPCOMP -i$SM_INCLUDE -i$ZR_INCLUDE -o$BUILD_DIR/infectapitest.smx $SRC/infectapitest.sp
diff --git a/src/include/zombiereloaded.inc b/src/include/zombiereloaded.inc
new file mode 100644
index 0000000..afbd748
--- /dev/null
+++ b/src/include/zombiereloaded.inc
@@ -0,0 +1,35 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: zombiereloaded.inc
+ * Type: Include
+ * Description: Main API include file.
+ * Notes: Include this file to include the whole ZR API.
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+#if defined _zr_included
+ #endinput
+#endif
+#define _zr_included
+
+#include
+#include
diff --git a/src/include/zr/infect.zr.inc b/src/include/zr/infect.zr.inc
new file mode 100644
index 0000000..e3b9ce3
--- /dev/null
+++ b/src/include/zr/infect.zr.inc
@@ -0,0 +1,123 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: infect.zr.inc
+ * Type: Include
+ * Description: Infect-related natives/forwards.
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+/**
+ * Returns true if the player is a zombie, false if not.
+ *
+ * @param client The client index.
+ *
+ * @return True if zombie, false if not.
+ * @error Invalid client index, not connected or not alive.
+ */
+native bool:ZR_IsClientZombie(client);
+
+/**
+ * Returns true if the player is a human, false if not.
+ *
+ * @param client The client index.
+ *
+ * @return True if human, false if not.
+ * @error Invalid client index, not connected or not alive.
+ */
+native bool:ZR_IsClientHuman(client);
+
+/**
+ * Infects a player.
+ *
+ * Note: If the player already is a zombie, the player will be re-infected.
+ *
+ * @param client The client to infect.
+ * @param attacker (Optional) The attacker who did the infect.
+ * @param motherInfect (Optional) Infect as a mother zombie.
+ * @param respawnOverride (Optional) Set to true to override respawn cvar.
+ * @param respawn (Optional) Value to override with.
+ *
+ * @error Invalid client index, not connected or not alive.
+ */
+native ZR_InfectClient(client, attacker = -1, bool:motherInfect = false, bool:respawnOverride = false, bool:respawn = false);
+
+/**
+ * Turns a zombie back into a human.
+ *
+ * Note: If the player already is a human, this code will still run as the
+ * player was a zombie.
+ *
+ * @param client The client to make human.
+ * @param respawn Teleport client back to spawn.
+ * @param protect Start spawn protection on client.
+ *
+ * @error Invalid client index, not connected or not alive.
+ */
+native ZR_HumanClient(client, bool:respawn = false, bool:protect = false);
+
+/**
+ * Called when a player is about to become a zombie.
+ * Here you can modify any variable or block the infection entirely.
+ *
+ * @param client The client index.
+ * @param attacker The the infecter. (-1 if there is no infecter)
+ * @param motherInfect If the client is becoming a mother zombie.
+ * @param respawnOverride True if the respawn cvar is being overridden.
+ * @param respawn The value that respawn is being overridden with.
+ *
+ * @return Plugin_Handled to block infection. Anything else
+ * (like Plugin_Continue) to allow infection.
+ */
+forward Action:ZR_OnClientInfect(&client, &attacker, &bool:motherInfect, &bool:respawnOverride, &bool:respawn);
+
+/**
+ * Called after a player has become a zombie.
+ *
+ * @param client The client that was infected.
+ * @param attacker The the infecter. (-1 if there is no infecter)
+ * @param motherInfect If the client is a mother zombie.
+ * @param respawnOverride True if the respawn cvar was overridden.
+ * @param respawn The value that respawn was overridden with.
+ */
+forward ZR_OnClientInfected(client, attacker, bool:motherInfect, bool:respawnOverride, bool:respawn);
+
+/**
+ * Called when a player is about to become a human. (Through an admin command).
+ * Here you can modify any variable or block the action entirely.
+ *
+ * @param client The client index.
+ * @param respawn True if the client was respawned, false if not.
+ * @param protect True if the client spawn protected, false if not.
+ *
+ * @return Plugin_Handled to block infection. Anything else
+ * (like Plugin_Continue) to allow acion.
+ */
+forward Action:ZR_OnClientHuman(&client, &bool:respawn, &bool:protect);
+
+/**
+ * Called after a player has become a human. (Through an admin command.)
+ *
+ * @param client The client index.
+ * @param respawn Whether the client was respawned.
+ * @param protect Whether the client has spawn protection.
+ */
+forward ZR_OnClientHumanPost(client, bool:respawn, bool:protect);
diff --git a/src/include/zr/respawn.zr.inc b/src/include/zr/respawn.zr.inc
new file mode 100644
index 0000000..f27c1ee
--- /dev/null
+++ b/src/include/zr/respawn.zr.inc
@@ -0,0 +1,95 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: respawn.zr.inc
+ * Type: Include
+ * Description: Infect-related natives/forwards.
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+/**
+ * Conditions for respawning players.
+ */
+enum ZR_RespawnCondition
+{
+ ZR_Repsawn_Default = -1, /** Let ZR decide according to its settings. */
+ ZR_Respawn_Human = 0, /** Respawn as a human. */
+ ZR_Respawn_Zombie, /** Respawn as a zombie. */
+ ZR_Respawn_ZombieIfSuicide /** Respawn as a zombie if killed by world damage. */
+}
+
+/**
+ * Spawns a player into the round.
+ *
+ * @param client The client index.
+ * @param condition Optional. Set respawn condition, defaults to current
+ * ZR settings. See ZR_RespawnCondition for details.
+ * @error Invalid client index, not connected or already alive.
+ */
+native ZR_RespawnClient(client, ZR_RespawnCondition:condition = ZR_Repsawn_Default);
+
+/**
+ * Called right before ZR is about to respawn a player.
+ * Here you can modify any variable or stop the action entirely.
+ *
+ * @param client The client index.
+ * @param condition Respawn condition. See ZR_RespawnCondition for
+ * details.
+ *
+ * @return Plugin_Handled to block respawn.
+ */
+forward Action:ZR_OnClientRespawn(&client, &ZR_RespawnCondition:condition);
+
+/**
+ * Called after ZR respawned a player.
+ *
+ * @param client The client index.
+ * @param condition Current condition of the respawned player. See
+ * ZR_RespawnCondition for details.
+ */
+forward ZR_OnClientRespawned(client, ZR_RespawnCondition:condition);
+
+/**
+ * Set if a player died by a suicide or world damage.
+
+ * Note: This will change the respawn condition.
+ * Note: This value is reset to default by ZR when a zombie player dies.
+ *
+ * @param client The client index.
+ * @param suicide True to say the player suicided, false if killed by another
+ * player.
+ *
+ * @error Invalid client index or not connected.
+ */
+native ZR_SetKilledByWorld(client, bool:suicide);
+
+/**
+ * Get whether the player died by a suicide or world damage.
+ *
+ * Note: This value is only valid after death event, and before respawn.
+ *
+ * @param client The client index.
+ *
+ * @return True if the player died by suicide, false if killed by
+ * another player.
+ * @error Invalid client index or not connected.
+ */
+native bool:ZR_GetKilledByWorld(client);
diff --git a/src/testsuite/zr/infectapitest.sp b/src/testsuite/zr/infectapitest.sp
new file mode 100644
index 0000000..4b7a620
--- /dev/null
+++ b/src/testsuite/zr/infectapitest.sp
@@ -0,0 +1,176 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: respawnapi.sp
+ * Type: Test plugin
+ * Description: Tests the infection API.
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+#pragma semicolon 1
+#include
+#include
+
+public Plugin:myinfo =
+{
+ name = "Zombie:Reloaded Infection API Test",
+ author = "Greyscale | Richard Helgeby",
+ description = "Tests the infection API for ZR",
+ version = "1.0.0",
+ url = "http://code.google.com/p/zombiereloaded/"
+};
+
+new Handle:cvarBlockInfect;
+new Handle:cvarBlockHuman;
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+
+ RegConsoleCmd("zrtest_iszombie", IsZombieCommand, "Returns whether a player is a zombie or not. Usage: zrtest_iszombie ");
+ RegConsoleCmd("zrtest_ishuman", IsHumanCommand, "Returns whether a player is a human or not. Usage: zrtest_ishuman ");
+
+ RegConsoleCmd("zrtest_infect", InfectClientCommand, "Infects a player. Usage: zrtest_infect ");
+ RegConsoleCmd("zrtest_human", HumanClientCommand, "Turns a player back into a human. Usage: zrtest_human ");
+
+ cvarBlockInfect = CreateConVar("zrtest_block_infect", "0", "Block infection.");
+ cvarBlockHuman = CreateConVar("zrtest_block_human", "0", "Block turning players back into humans.");
+}
+
+public Action:IsZombieCommand(client, argc)
+{
+ new target = -1;
+ new String:valueString[64];
+
+ if (argc >= 1)
+ {
+ GetCmdArg(1, valueString, sizeof(valueString));
+ target = FindTarget(client, valueString);
+ }
+ else
+ {
+ ReplyToCommand(client, "Returns whether a player is a zombie or not. Usage: zrtest_iszombie ");
+ return Plugin_Handled;
+ }
+
+ ReplyToCommand(client, "Client %d is a zombie: %d", client, ZR_IsClientZombie(target));
+
+ return Plugin_Handled;
+}
+
+public Action:IsHumanCommand(client, argc)
+{
+ new target = -1;
+ new String:valueString[64];
+
+ if (argc >= 1)
+ {
+ GetCmdArg(1, valueString, sizeof(valueString));
+ target = FindTarget(client, valueString);
+ }
+ else
+ {
+ ReplyToCommand(client, "Returns whether a player is a human or not. Usage: zrtest_ishuman ");
+ return Plugin_Handled;
+ }
+
+ ReplyToCommand(client, "Client %d is a human: %d", client, ZR_IsClientHuman(target));
+
+ return Plugin_Handled;
+}
+
+public Action:InfectClientCommand(client, argc)
+{
+ new target = -1;
+ new String:valueString[64];
+
+ if (argc >= 1)
+ {
+ GetCmdArg(1, valueString, sizeof(valueString));
+ target = FindTarget(client, valueString);
+ }
+
+ if (target < 0)
+ {
+ ReplyToCommand(client, "Infects a player. Usage: zrtest_infect ");
+ return Plugin_Handled;
+ }
+
+ ZR_InfectClient(target);
+
+ return Plugin_Handled;
+}
+
+public Action:HumanClientCommand(client, argc)
+{
+ new target = -1;
+ new String:valueString[64];
+
+ if (argc >= 1)
+ {
+ GetCmdArg(1, valueString, sizeof(valueString));
+ target = FindTarget(client, valueString);
+ }
+
+ if (target < 0)
+ {
+ ReplyToCommand(client, "Turns a player back into a human. Usage: zrtest_human ");
+ return Plugin_Handled;
+ }
+
+ ZR_HumanClient(target);
+
+ return Plugin_Handled;
+}
+
+public Action:ZR_OnClientInfect(&client, &attacker, &bool:motherInfect, &bool:respawnOverride, &bool:respawn)
+{
+ if (GetConVarBool(cvarBlockInfect))
+ {
+ PrintToChatAll("Infection blocked on client %d.", client);
+ return Plugin_Handled;
+ }
+
+ PrintToChatAll("Client %d is about to be infected. Attacker: %d, Mother zombie: %d, Respawn override: %d, Respawn: %d.", client, attacker, motherInfect, respawnOverride, respawn);
+ return Plugin_Continue;
+}
+
+public ZR_OnClientInfected(client, attacker, bool:motherInfect, bool:respawnOverride, bool:respawn)
+{
+ PrintToChatAll("Client %d was infected. Attacker: %d, Mother zombie: %d, Respawn override: %d, Respawn: %d.", client, attacker, motherInfect, respawnOverride, respawn);
+}
+
+public Action:ZR_OnClientHuman(&client, &bool:respawn, &bool:protect)
+{
+ if (GetConVarBool(cvarBlockHuman))
+ {
+ PrintToChatAll("Turning human blocked on client %d.", client);
+ return Plugin_Handled;
+ }
+
+ PrintToChatAll("Client %d is about to become a human. Respawn: %d, Spawn protect: %d", client, respawn, protect);
+ return Plugin_Continue;
+}
+
+public ZR_OnClientHumanPost(client, bool:respawn, bool:protect)
+{
+ PrintToChatAll("Client %d turned a human. Respawn: %d, Spawn protect: %d", client, respawn, protect);
+}
diff --git a/src/testsuite/zr/respawnapitest.sp b/src/testsuite/zr/respawnapitest.sp
new file mode 100644
index 0000000..c5192be
--- /dev/null
+++ b/src/testsuite/zr/respawnapitest.sp
@@ -0,0 +1,132 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: respawnapi.sp
+ * Type: Test plugin
+ * Description: Tests the respawn API.
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+#pragma semicolon 1
+#include
+#include
+
+public Plugin:myinfo =
+{
+ name = "Zombie:Reloaded Respawn API Test",
+ author = "Greyscale | Richard Helgeby",
+ description = "Tests the respawn API for ZR",
+ version = "1.0.0",
+ url = "http://code.google.com/p/zombiereloaded/"
+};
+
+new Handle:cvarBlockRespawn;
+new Handle:cvarAllSuicide;
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+
+ RegConsoleCmd("zrtest_killed_by_world", KilledByWorldCommand, "Gets or sets the killed by world value. Usage: zrtest_killed_by_world [1|0]");
+ RegConsoleCmd("zrtest_respawn", RespawnClientCommand, "Respawn a player. Usage: zrtest_respawn ");
+
+ cvarBlockRespawn = CreateConVar("zrtest_block_respawn", "0", "Block respawning.");
+ cvarAllSuicide = CreateConVar("zrtest_all_suicide", "0", "Treat all deaths as suicide.");
+}
+
+public Action:KilledByWorldCommand(client, argc)
+{
+ new target = -1;
+ new String:valueString[64];
+
+ if (argc >= 1)
+ {
+ GetCmdArg(1, valueString, sizeof(valueString));
+ target = FindTarget(client, valueString);
+ }
+
+ if (target <= 0)
+ {
+ ReplyToCommand(client, "Gets or sets the killed by world value. Usage: zrtest_killed_by_world [value]");
+ return Plugin_Handled;
+ }
+
+ if (argc > 1)
+ {
+ // Set value.
+ GetCmdArg(2, valueString, sizeof(valueString));
+ ZR_SetKilledByWorld(target, bool:StringToInt(valueString));
+ }
+ else
+ {
+ // Print value.
+ ReplyToCommand(client, "Killed by world: %d", ZR_GetKilledByWorld(target));
+ }
+
+ return Plugin_Handled;
+}
+
+public Action:RespawnClientCommand(client, argc)
+{
+ new target = -1;
+ new String:valueString[64];
+
+ if (argc >= 1)
+ {
+ GetCmdArg(1, valueString, sizeof(valueString));
+ target = FindTarget(client, valueString);
+ }
+
+ if (target < 0)
+ {
+ ReplyToCommand(client, "Respawn a player. Usage: zrtest_respawn ");
+ return Plugin_Handled;
+ }
+
+ ZR_RespawnClient(target, ZR_Repsawn_Default);
+
+ return Plugin_Handled;
+}
+
+public Action:ZR_OnClientRespawn(&client, &ZR_RespawnCondition:condition)
+{
+ if (GetConVarBool(cvarBlockRespawn))
+ {
+ PrintToChatAll("Respawn blocked on client %d.", client);
+ return Plugin_Handled;
+ }
+
+ PrintToChatAll("Client %d is about to respawn. Condition: %d", client, condition);
+
+ if (GetConVarBool(cvarAllSuicide))
+ {
+ // Set client suicide.
+ ZR_SetKilledByWorld(client, true);
+ PrintToChatAll("Client %d is marked as suicide victim.");
+ }
+
+ return Plugin_Continue;
+}
+
+public ZR_OnClientRespawned(client, ZR_RespawnCondition:condition)
+{
+ PrintToChatAll("Client %d respawned.", client);
+}
diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp
index b64bd90..bfa1122 100644
--- a/src/zombiereloaded.sp
+++ b/src/zombiereloaded.sp
@@ -113,6 +113,8 @@
#include "zr/volfeatures/volfeatures"
#include "zr/debugtools"
+#include "zr/api/api"
+
/**
* Record plugin info.
*/
@@ -137,7 +139,8 @@ public Plugin:myinfo =
*/
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
- // TODO: EXTERNAL API
+ // Load API.
+ APIInit();
// Let plugin load.
return APLRes_Success;
diff --git a/src/zr/api/api.inc b/src/zr/api/api.inc
new file mode 100644
index 0000000..4bb4cea
--- /dev/null
+++ b/src/zr/api/api.inc
@@ -0,0 +1,92 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: api.inc
+ * Type: Core
+ * Description: Native handlers for the ZR API.
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+/**
+ * Application Programming Interface (API)
+ *
+ * To allow other plugins or extensions to interact directly with Zombie:Reloaded we need to implement
+ * an API. SourceMod allows us to do this by creating global "natives" or "forwards."
+ *
+ * Natives are basically functions that can be called from any plugin that includes its definition.
+ * Forwards are functions that are called on a given event. (Such as OnClientPutInServer)
+ * ZR's API files are located in sourcemod/scripting/include/zr/category.zr.inc. We chose to create multiple
+ * files simply for organization. Including zr.inc will automatically include the rest of the files as well.
+ *
+ * To better understand how natives and forwards are created, go here:
+ * http://wiki.alliedmods.net/Creating_Natives_(SourceMod_Scripting)
+ * http://wiki.alliedmods.net/Function_Calling_API_(SourceMod_Scripting)
+ */
+
+#include "zr/api/infect.api"
+#include "zr/api/respawn.api"
+
+/**
+ * Initializes all main natives and forwards.
+ */
+APIInit()
+{
+ // Forward event to sub-modules.
+ APIInfectInit();
+ APIRespawnInit();
+}
+
+/**
+ * Validates a client index and when it fails, an error is thrown.
+ *
+ * @param client The client index to validate.
+ * @param alive Set to true to validate that the client is alive, false to ignore.
+ *
+ * @error Throws an error when the client isn't valid.
+ */
+stock APIValidateClientIndex(client, EligibleCondition:alive = Condition_Either)
+{
+ // Verify client index.
+ if (client < 1 || client > MaxClients)
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client);
+ }
+
+ // Verify that the client is connected.
+ if (!IsClientConnected(client))
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected.", client);
+ }
+
+ // Check if the player must be dead or alive.
+ new bool:bAlive = bool:alive;
+ if (alive != Condition_Either)
+ {
+ if (bAlive && !IsPlayerAlive(client))
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "Client %d must be alive.", client);
+ }
+ else if (!bAlive && IsPlayerAlive(client))
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "Client %d must be dead.", client);
+ }
+ }
+}
diff --git a/src/zr/api/infect.api.inc b/src/zr/api/infect.api.inc
new file mode 100644
index 0000000..8c86470
--- /dev/null
+++ b/src/zr/api/infect.api.inc
@@ -0,0 +1,229 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: infect.api.inc
+ * Type: Core
+ * Description: Native handlers for the ZR API. (Infect module)
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+/**
+ * @section Global forward handles.
+ */
+new Handle:g_hAPIFwdOnClientInfect = INVALID_HANDLE;
+new Handle:g_hAPIFwdOnClientInfected = INVALID_HANDLE;
+new Handle:g_hAPIFwdOnClientHuman = INVALID_HANDLE;
+new Handle:g_hAPIFwdOnClientHumanPost = INVALID_HANDLE;
+/**
+ * @endsection
+ */
+
+/**
+ * Initializes all natives and forwards related to infection.
+ */
+APIInfectInit()
+{
+ // Infect module natives/forwards (infect.zr.inc)
+
+ // Natives
+ CreateNative("ZR_IsClientZombie", APIIsClientZombie);
+ CreateNative("ZR_IsClientHuman", APIIsClientHuman);
+ CreateNative("ZR_InfectClient", APIInfectClient);
+ CreateNative("ZR_HumanClient", APIHumanClient);
+
+ // Forwards
+ g_hAPIFwdOnClientInfect = CreateGlobalForward("ZR_OnClientInfect", ET_Hook, Param_CellByRef, Param_CellByRef, Param_CellByRef, Param_CellByRef, Param_CellByRef);
+ g_hAPIFwdOnClientInfected = CreateGlobalForward("ZR_OnClientInfected", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
+ g_hAPIFwdOnClientHuman = CreateGlobalForward("ZR_OnClientHuman", ET_Hook, Param_CellByRef, Param_CellByRef, Param_CellByRef);
+ g_hAPIFwdOnClientHumanPost = CreateGlobalForward("ZR_OnClientHumanPost", ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
+}
+
+/**
+ * Native call function (ZR_IsClientInfected)
+ * Returns true if the client is a zombie, false if not.
+ */
+public APIIsClientZombie(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+
+ // Validate the client index.
+ APIValidateClientIndex(client, Condition_True);
+
+ return InfectIsClientInfected(client);
+}
+
+/**
+ * Native call function (ZR_IsClientHuman)
+ * Returns true if the client is a human, false if not.
+ *
+ * bool:InfectIsClientHuman(client)
+ */
+public APIIsClientHuman(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+
+ // Validate the client index.
+ APIValidateClientIndex(client, Condition_True);
+
+ return InfectIsClientHuman(client);
+}
+
+/**
+ * Native call function (ZR_InfectClient)
+ * Infects a client.
+ *
+ * native ZR_InfectClient(client, attacker = -1, bool:motherInfect = false, bool:respawnOverride = false, bool:respawn = false);
+ */
+public APIInfectClient(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+ new attacker = GetNativeCell(2);
+ new bool:motherInfect = bool:GetNativeCell(3);
+ new bool:respawnOverride = bool:GetNativeCell(4);
+ new bool:respawn = bool:GetNativeCell(5);
+
+ // Validate the client index.
+ APIValidateClientIndex(client, Condition_True);
+
+ InfectHumanToZombie(client, attacker, motherInfect, respawnOverride, respawn);
+}
+
+/**
+ * Native call function (ZR_HumanClient)
+ * Turns a zombie back into a human.
+ *
+ * native ZR_HumanClient(client, bool:respawn = false, bool:protect = false);
+ */
+public APIHumanClient(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+ new bool:respawn = bool:GetNativeCell(2);
+ new bool:protect = bool:GetNativeCell(2);
+
+ // Validate the client index.
+ APIValidateClientIndex(client, Condition_True);
+
+ InfectZombieToHuman(client, respawn, protect);
+}
+
+/**
+ * Called when a client is about to become a zombie.
+ *
+ * @param client The client to infect.
+ * @param attacker The attacker who did the infect.
+ * @param motherinfect Indicates a mother zombie infect.
+ * @param respawnoverride Set to true to override respawn cvar.
+ * @param respawn Value to override with.
+ *
+ * InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respawnoverride = false, bool:respawn = false)
+ */
+Action:APIOnClientInfect(&client, &attacker, &bool:motherinfect, &bool:respawnoverride, &bool:respawn)
+{
+ // Start forward call.
+ Call_StartForward(g_hAPIFwdOnClientInfect);
+
+ // Push the parameters.
+ Call_PushCellRef(client);
+ Call_PushCellRef(attacker);
+ Call_PushCellRef(motherinfect);
+ Call_PushCellRef(respawnoverride);
+ Call_PushCellRef(respawn);
+
+ // Get what they returned.
+ new Action:result;
+ Call_Finish(result);
+ return result;
+}
+
+/**
+ * Called after a client has become a zombie.
+ *
+ * @param client The client to infect.
+ * @param attacker The attacker who did the infect.
+ * @param motherinfect Indicates a mother zombie infect.
+ * @param respawnoverride Set to true to override respawn cvar.
+ * @param respawn Value to override with.
+ *
+ * InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respawnoverride = false, bool:respawn = false)
+ */
+APIOnClientInfected(client, attacker, bool:motherinfect, bool:respawnoverride, bool:respawn)
+{
+ // Start forward call.
+ Call_StartForward(g_hAPIFwdOnClientInfected);
+
+ // Push the parameters.
+ Call_PushCell(client);
+ Call_PushCell(attacker);
+ Call_PushCell(motherinfect);
+ Call_PushCell(respawnoverride);
+ Call_PushCell(respawn);
+
+ // Finish the call.
+ Call_Finish();
+}
+
+/**
+ * Called when a client is about to become a human. (Through either zr_human or admin menu)
+ *
+ * @param client The client index.
+ * @param respawn True if the client was respawned, false if not.
+ * @param protect True if the client spawn protected, false if not.
+ *
+ * InfectZombieToHuman(client, bool:respawn = false, bool:protect = false)
+ */
+Action:APIOnClientHuman(&client, &bool:respawn, &bool:protect)
+{
+ // Start forward call.
+ Call_StartForward(g_hAPIFwdOnClientHuman);
+
+ // Push the parameters.
+ Call_PushCellRef(client);
+ Call_PushCellRef(respawn);
+ Call_PushCellRef(protect);
+
+ // Get what they returned.
+ new Action:result;
+ Call_Finish(result);
+ return result;
+}
+
+/**
+ * Called after a client has become a human. (Through either zr_human or admin menu)
+ *
+ * @param client The client index.
+ * @param respawn True if the client was respawned, false if not.
+ * @param protect True if the client spawn protected, false if not.
+ *
+ * InfectZombieToHuman(client, bool:respawn = false, bool:protect = false)
+ */
+APIOnClientHumanPost(client, bool:respawn, bool:protect)
+{
+ // Start forward call.
+ Call_StartForward(g_hAPIFwdOnClientHumanPost);
+
+ // Push the parameters.
+ Call_PushCell(client);
+ Call_PushCell(respawn);
+ Call_PushCell(protect);
+
+ // Finish the call.
+ Call_Finish();
+}
diff --git a/src/zr/api/respawn.api.inc b/src/zr/api/respawn.api.inc
new file mode 100644
index 0000000..8bcdeed
--- /dev/null
+++ b/src/zr/api/respawn.api.inc
@@ -0,0 +1,149 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: respawn.api.inc
+ * Type: Core
+ * Description: Native handlers for the ZR API. (Respawn module)
+ *
+ * Copyright (C) 2009-2010 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 .
+ *
+ * ============================================================================
+ */
+
+/**
+ * @section Global forward handles.
+ */
+new Handle:g_hAPIFwdOnClientRespawn = INVALID_HANDLE;
+new Handle:g_hAPIFwdOnClientRespawned = INVALID_HANDLE;
+/**
+ * @endsection
+ */
+
+/**
+ * Initializes all natives and forwards related to respawn.
+ */
+APIRespawnInit()
+{
+ // Respawn module natives/forwards (respawn.zr.inc)
+
+ // Natives
+ CreateNative("ZR_RespawnClient", APIRespawnClient);
+ CreateNative("ZR_SetKilledByWorld", APISetKilledByWorld);
+ CreateNative("ZR_GetKilledByWorld", APIGetKilledByWorld);
+
+ // Forwards
+ g_hAPIFwdOnClientRespawn = CreateGlobalForward("ZR_OnClientRespawn", ET_Hook, Param_CellByRef, Param_CellByRef);
+ g_hAPIFwdOnClientRespawned = CreateGlobalForward("ZR_OnClientRespawned", ET_Ignore, Param_Cell, Param_Cell);
+}
+
+/**
+ * Native call function (ZR_RespawnClient)
+ * Spawns a player into the round following rules set by cvars.
+ *
+ * RespawnSpawnClient(client, bool:zombie = false, bool:zombie_if_suicide = false)
+ */
+public APIRespawnClient(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+
+ // Validate index and that player isn't alive.
+ APIValidateClientIndex(client, Condition_False);
+
+ // Restore respawn condition.
+ new RespawnCondition:condition = RespawnCondition:GetNativeCell(2);
+ new bool:zombie;
+ new bool:zombieIfSuicide;
+ RespawnRestoreCondition(condition, zombie, zombieIfSuicide);
+
+ // Respawn the client.
+ RespawnSpawnClient(client, zombie, zombieIfSuicide);
+}
+
+/**
+ * Called right before ZR is about to respawn a player.
+ *
+ * @param client The client index.
+ * @param condition Respawn condition. See RespawnCondition for
+ * details.
+ */
+Action:APIOnClientRespawn(&client, &RespawnCondition:condition)
+{
+ // Start forward call.
+ Call_StartForward(g_hAPIFwdOnClientRespawn);
+
+ // Push the parameters.
+ Call_PushCellRef(client);
+ Call_PushCellRef(condition);
+
+ // Get what they returned.
+ new Action:result;
+ Call_Finish(result);
+ return result;
+}
+
+/**
+ * Called after a player was respawned.
+ *
+ * @param client The client index.
+ * @param condition Current condition of the respawned player. See
+ * RespawnCondition for details.
+ */
+Action:APIOnClientRespawned(client, RespawnCondition:condition)
+{
+ // Start forward call.
+ Call_StartForward(g_hAPIFwdOnClientRespawned);
+
+ // Push the parameters.
+ Call_PushCell(client);
+ Call_PushCell(condition);
+
+ // Finish the call.
+ Call_Finish();
+}
+
+/**
+ * Native call function (ZR_SetKilledByWorld)
+ * Override if the client died by a suicide.
+ */
+public APISetKilledByWorld(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+
+ // Validate the client index.
+ APIValidateClientIndex(client, Condition_Either);
+
+ new bool:suicide = bool:GetNativeCell(2);
+
+ // Override the old value.
+ bKilledByWorld[client] = suicide;
+}
+
+/**
+ * Native call function (ZR_GetKilledByWorld)
+ * Read the value of if the client died by a suicide.
+ */
+public APIGetKilledByWorld(Handle:plugin, numParams)
+{
+ new client = GetNativeCell(1);
+
+ // Validate the client index.
+ APIValidateClientIndex(client, Condition_Either);
+
+ // Return the value.
+ return _:bKilledByWorld[client];
+}
diff --git a/src/zr/infect.inc b/src/zr/infect.inc
index 06df5e8..ee2732b 100644
--- a/src/zr/infect.inc
+++ b/src/zr/infect.inc
@@ -563,6 +563,15 @@ public Action:InfectMotherZombie(Handle:timer)
*/
InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respawnoverride = false, bool:respawn = false)
{
+ // Forward pre-event to modules.
+ new Action:result = APIOnClientInfect(client, attacker, motherinfect, respawnoverride, respawn);
+
+ // Check if infection should be blocked.
+ if (result == Plugin_Handled)
+ {
+ return;
+ }
+
// Mark player as zombie.
bZombie[client] = true;
@@ -650,6 +659,7 @@ InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respa
SEffectsOnClientInfected(client);
ZTeleOnClientInfected(client);
ZHPOnClientInfected(client);
+ APIOnClientInfected(client, attacker, motherinfect, respawnoverride, respawn);
}
/**
@@ -662,6 +672,15 @@ InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respa
*/
InfectZombieToHuman(client, bool:respawn = false, bool:protect = false)
{
+ // Forward pre-event to modules.
+ new Action:result = APIOnClientHuman(client, respawn, protect);
+
+ // Check if action should be blocked.
+ if (result == Plugin_Handled)
+ {
+ return;
+ }
+
// Mark player as human.
bZombie[client] = false;
@@ -702,6 +721,7 @@ InfectZombieToHuman(client, bool:respawn = false, bool:protect = false)
// Forward event to modules.
SEffectsOnClientHuman(client);
+ APIOnClientHumanPost(client, respawn, protect);
}
/**
diff --git a/src/zr/respawn.inc b/src/zr/respawn.inc
index 2c67ab2..833be31 100644
--- a/src/zr/respawn.inc
+++ b/src/zr/respawn.inc
@@ -25,6 +25,17 @@
* ============================================================================
*/
+/**
+ * Conditions for respawning players.
+ */
+enum RespawnCondition
+{
+ Repsawn_Default = -1, /** Let ZR decide according to its settings. */
+ Respawn_Human = 0, /** Respawn as a human. */
+ Respawn_Zombie, /** Respawn as a zombie. */
+ Respawn_ZombieIfSuicide /** Respawn as a zombie if killed by world damage. */
+}
+
/**
* Array for storing respawn timer handles per client.
*/
@@ -133,37 +144,59 @@ RespawnOnRoundEnd()
/**
* Spawns a player into the round.
*
- * @param client The client index.
+ * @param client The client index.
+ * @param zombie Respawn as zombie.
+ * @param zombieIfSuicide Respawn as zombie if killed by world damage.
+ *
+ * @return True if the player was spawned, false otherwise.
*/
-RespawnSpawnClient(client, bool:zombie = false)
+bool:RespawnSpawnClient(client, bool:zombie = false, bool:zombieIfSuicide = false)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(client))
{
- return;
+ return false;
}
+ // Get respawn condition.
+ //new bool:zombieIfSuicide = GetConVarBool(g_hCvarsList[CVAR_RESPAWN_TEAM_ZOMBIE_WORLD]);
+ new RespawnCondition:condition = RespawnToContition(zombie, zombieIfSuicide);
+
+ // Trigger API forward.
+ new Action:result = APIOnClientRespawn(client, condition);
+
+ // Check if respawn should be stopped.
+ if (result == Plugin_Handled)
+ {
+ return false;
+ }
+
+ // Restore respawn condition.
+ RespawnRestoreCondition(condition, zombie, zombieIfSuicide);
+
// Spawn player.
CS_RespawnPlayer(client);
- // Stop here if the first zombie hasn't spawned yet.
- if (!g_bZombieSpawned)
+ // Check if first zombie has spawned.
+ if (g_bZombieSpawned)
{
- return;
+ // Infect if player should spawn as zombie.
+ if (zombie)
+ {
+ InfectHumanToZombie(client);
+ }
+ // Infect if player committed suicide by world damage.
+ else if (zombieIfSuicide && bKilledByWorld[client])
+ {
+ InfectHumanToZombie(client);
+ bKilledByWorld[client] = false;
+ }
}
- // Infect if zombie is true.
- if (zombie)
- {
- InfectHumanToZombie(client);
- return;
- }
+ // Forward event to modules.
+ APIOnClientRespawned(client, condition);
- if (GetConVarBool(g_hCvarsList[CVAR_RESPAWN_TEAM_ZOMBIE_WORLD]) && bKilledByWorld[client])
- {
- InfectHumanToZombie(client);
- bKilledByWorld[client] = false;
- }
+ return true;
}
/**
@@ -183,7 +216,7 @@ public Action:RespawnTimer(Handle:timer, any:client)
return;
}
- // If player isn't alive, then stop.
+ // If player already is alive, then stop.
if (IsPlayerAlive(client))
{
return;
@@ -202,5 +235,52 @@ public Action:RespawnTimer(Handle:timer, any:client)
new bool:respawn_zombie = GetConVarBool(g_hCvarsList[CVAR_RESPAWN_TEAM_ZOMBIE]);
// Spawn player.
- RespawnSpawnClient(client, respawn_zombie);
+ RespawnSpawnClient(client, respawn_zombie, true);
+}
+
+/**
+ * Converts separate conditions into a single condition value.
+ */
+RespawnCondition:RespawnToContition(bool:zombie, bool:zombieIfSuicide)
+{
+ if (zombieIfSuicide)
+ {
+ return Respawn_ZombieIfSuicide;
+ }
+ else if (zombie)
+ {
+ return Respawn_Zombie;
+ }
+
+ return Respawn_Human;
+}
+
+/**
+ * Restores respawn condition to individual variables. If the condition invalid,
+ * nothing will be changed.
+ *
+ * @param condition Condition to restore.
+ * @param zombie Output.
+ * @param zombieIfSucidie Output.
+ */
+RespawnRestoreCondition(RespawnCondition:condition, &bool:zombie, &bool:zombieIfSuicide)
+{
+ switch (condition)
+ {
+ case Respawn_Human:
+ {
+ zombie = false;
+ zombieIfSuicide = false;
+ }
+ case Respawn_Zombie:
+ {
+ zombie = true;
+ zombieIfSuicide = false;
+ }
+ case Respawn_ZombieIfSuicide:
+ {
+ zombie = false;
+ zombieIfSuicide = true;
+ }
+ }
}
diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc
index 25f603e..4c16e0b 100644
--- a/src/zr/zombiereloaded.inc
+++ b/src/zr/zombiereloaded.inc
@@ -39,6 +39,16 @@
* @endsection
*/
+/**
+ * Options that a condition must pass to be eligible.
+ */
+enum EligibleCondition
+{
+ Condition_False = 0, /** Condition must be false. */
+ Condition_True = 1, /** Condition must be true. */
+ Condition_Either = -1 /** Condition can be either true or false. */
+}
+
/**
* Global variable set to true when the first zombie(s) is/are spawned.
*/
diff --git a/src/zr/zspawn.inc b/src/zr/zspawn.inc
index c6e9fa0..ed11d21 100644
--- a/src/zr/zspawn.inc
+++ b/src/zr/zspawn.inc
@@ -263,9 +263,7 @@ bool:ZSpawnClient(client, bool:force = false, bool:zombie = false)
}
// Tell respawn module to respawn client.
- RespawnSpawnClient(client, teamzombie);
-
- return true;
+ return RespawnSpawnClient(client, teamzombie, false);
}
/**