diff --git a/addons/sourcemod/scripting/include/zcontracts/zcontracts.inc b/addons/sourcemod/scripting/include/zcontracts/zcontracts.inc index bcf86f6..82ebc96 100644 --- a/addons/sourcemod/scripting/include/zcontracts/zcontracts.inc +++ b/addons/sourcemod/scripting/include/zcontracts/zcontracts.inc @@ -4,12 +4,39 @@ #define __zcontracts_included +#define ZCONTRACTS_PLUGIN_VERSION "0.9.0" +// This value should be incremented with every breaking version made to the +// database so saves can be easily converted. For developers who fork this project and +// wish to merge changes, do not increment this number until merge. +// Other plugins should use the GetContrackerVersion() native to get this value, but +// this main plugin and subplugins are free to use the define name in its place. +#define CONTRACKER_VERSION 2 +// How often the HUD will refresh itself. +#define HUD_REFRESH_RATE 0.5 + #define MAX_UUID_SIZE 64 #define MAX_CONTRACT_NAME_SIZE 64 #define MAX_OBJECTIVE_DESC_SIZE 128 #define MAX_EVENT_SIZE 256 #define MAX_DIRECTORY_SIZE 128 +#include +#include + +/* Okay, quick developer story time. +I originally developed this project with enum structs in mind. I learnt a lot of my SourceMod +practices when developing Creators.TF, and the codebase used a lot of enum structs to store data in +a clean way that is similar to other programming languiages. SourcePawn is notorious for not having +the best OOP capabilities. With this in mind, I wanted to make the project as clean as possible from +a codebase perspective. I started this project before I became aware of the black hole that exists with +enum structs: they ***shouldn't*** be passed through with natives. If they are not handled carefully, +server crashes will happen (see https://github.com/alliedmodders/sourcepawn/issues/547). +This is the biggest coding flaw with ZContracts. In the far future, I would like to rewrite part of +the code to use Methodmaps or some other sane way to transport data between plugins, but for now - this is +my preferred approach. For developers: PLEASE recompile any plugin that deals with ZContracts when you +install a new patch. +*/ + stock int Int_Min(int a, int b) { return a < b ? a : b; } stock bool IsClientValid(int client) @@ -20,6 +47,30 @@ stock bool IsClientValid(int client) return true; } +// Methodmap that represents a Contract. This should be passed between plugins +// INSTEAD of using the enum structs below. +/* TODO: Come back to this later. This feels ugly. +methodmap ZContract +{ + public ZContract(int client) + { + if (!IsClientValid(client)) + { + ThrowError("Invalid client index (%d) when constructing methodmap.", client); + } + return view_as(client); + } + property int Client + { + public get() { return view_as(this); } + } + public bool GetUUID(char[] uuidbuffer, int buffersize) + { + return GetClientContract(view_as(this), uuidbuffer, buffersize); + } + +};*/ + enum ContractType { // Each objective has it's own progress bar. All objectives must be completed @@ -195,11 +246,12 @@ enum struct Contract char m_sRequiredGameRulesEntity[64]; // TF2 int m_iGameModeRestriction; // CSGO - // FOR CSGO: game_type convar. - // FOR TF2: gamemode extension (see contracts_tf2.sp) int m_iGameTypeRestriction; int m_iSkirmishIDRestriction; // CSGO + // Other plugins can mark this Contract as uncompletable on player switch. + bool m_bIsCompletableOverride; + // What type of Contract are we handling? (see ContractType) ContractType m_iContractType; @@ -386,186 +438,4 @@ enum struct CompletedContractInfo { int m_iCompletions; bool m_bReset; -} - -/** - * The Contracker version is used to determine the minimum Contract that should be - * loaded from the database. This is intended to be used when a change is made to the - * database structure or there is a breaking change in ZContracts. - * @return The value of CONTRACKER_VERSION from zcontracts_main.sp - */ -native int GetContrackerVersion(); - -/** - * Set a client's contract. - * - * @param client Client index. - * @param UUID The UUID of the contract. - * @param dont_save Optional argument: doesn't save this as the active Contract in the database. - * @param dont_notify Optional argument: don't notify the player that we've set their contract. - * @error Client index is invalid or UUID is invalid. - */ -native bool SetClientContract(int client, char UUID[MAX_UUID_SIZE], bool dont_save = false, bool dont_notify = false); - -/** - * Set a client's contract with an enum struct. - * - * @param client Client index. - * @param new_contract The new contract enum struct. - * @param dont_save Optional argument: doesn't save this as the active Contract in the database. - * @param dont_notify Optional argument: don't notify the player that we've set their contract. - * @error Client index is invalid or UUID is invalid. - */ -native bool SetClientContractStruct(int client, any new_contract[sizeof(Contract)], bool dont_save = false, bool dont_notify = false); - -/** - * Obtains a client's active Contract. - * - * @param client Client index. - * @param buffer Buffer to store the client's contract. - * @error Client index is invalid. - */ -native bool GetClientContract(int client, any buffer[sizeof(Contract)]); - -/** - * Processes an event for the client's active Contract. - * - * @param client Client index. - * @param event Event to process. - * @param value Value to send alongside this event. - * @param can_combine If true, if this event was recently sent to the event queue, the value from this function will be added to the first event. - * @return True if an event is successfully called, false if the client's contract isn't active. - * @error Client index is invalid or is a bot. - */ -native bool CallContrackerEvent(int client, char event[MAX_EVENT_SIZE], int value, bool can_combine = false); - -// =========================== DATABASE SAVING =========================== - -/** - * Sets the active session for a user in the database. - * @param steamid64 SteamID64 of the user. - * @param UUID Contract UUID to save. - * @error Invalid UUID. - */ -native bool SetSessionDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE]); - -/** - * Saves a Contract to the database for a client. - * - * @param client Client index. - * @param ClientContract The enum struct of the contract to save. - * @error Client index is invalid or the Contract is invalid. - */ -native bool SaveClientContractProgress(int client, any ClientContract[sizeof(Contract)]); - -/** - * Saves an Objective to the database for a client. - * - * @param client Client index. - * @param UUID UUID of the Contract that contains this objective. - * @param ClientObjective The enum struct of the objective to save. - * @error Client index is invalid or the ClientObjective is invalid. - */ -native bool SaveClientObjectiveProgress(int client, char UUID[MAX_UUID_SIZE], any ClientObjective[sizeof(ContractObjective)]); - -/** - * Sets the progress of a Contract in the database. - * - * @param steamid64 SteamID64 of the user. - * @param UUID The UUID of the contract to modify. - * @param value The value to save to the database. - */ -native bool SetContractProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], int value); - -/** - * Sets the progress of an Objective in the database. - * - * @param steamid64 SteamID64 of the user. - * @param UUID The UUID of the contract to modify. - * @param objective_id The ID of the objective to modify. - * @param value The value to save to the database. - */ -native bool SetObjectiveProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], int objective_id, int value); - -/** - * Marks the contract as complete in the database. - * @param steamid64 SteamID64 of the user. - * @param UUID The UUID of the contract. - * @param data Contract competion data. -*/ -native bool SetCompletedContractInfoDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], any data[sizeof(CompletedContractInfo)]); - -/** - * Deletes all client progress for a contract. - * @param steamid64 SteamID64 of the user. - * @param UUID The UUID of the contract. -*/ -native bool DeleteContractProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE]); - -/** - * Deletes all client progress for an objective. - * @param steamid64 SteamID64 of the user. - * @param UUID The UUID of the contract. - * @param objective_id The objective ID. -*/ -native bool DeleteObjectiveProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], int objective_id); - -/** - * Deletes all client progress for all objectives. - * @param steamid64 SteamID64 of the user. - * @param UUID The UUID of the contract. -*/ -native bool DeleteAllObjectiveProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE]); - -// =========================== FORWARDS =========================== - -/** - * Sent on client completion of a Contract objective. - * - * @param client Client index. - * @param uuid The UUID of the original Contract. - * @param hObjective Handle to the completed objective. - */ -forward void OnContractObjectiveCompleted(int client, char UUID[MAX_UUID_SIZE], ContractObjective hObjective); -/** - * Sent on client completion of a Contract. - * - * @param client Client index. - * @param uuid The UUID of the original Contract. - * @param hObjective Handle to the completed Contract. - */ -forward void OnContractCompleted(int client, char UUID[MAX_UUID_SIZE], Contract hContract); - -/** - * Called before a contract is saved to the database. This is not called for any - * low-level value setting functions (e.g SetContractProgressDatabase). - * - * @param client Client index. - * @param uuid The UUID of the original Contract. - * @param hContract Handle to the objective. - */ -forward bool OnContractPreSave(int client, char UUID[MAX_UUID_SIZE], Contract hContract); - -/** - * Called before an objective is saved to the database. This is not called for any - * low-level value setting functions (e.g SetObjectiveProgressDatabase). - * - * @param client Client index. - * @param uuid The UUID of the original Contract. - * @param hObjective Handle to the objective. - */ -forward bool OnObjectivePreSave(int client, char UUID[MAX_UUID_SIZE], ContractObjective hObjective); - -/** - * Called when an event is about to potentially add progress to a Contract. - * - * @param client Client index. - * @param UUID UUID of the Contract. - * @param event Name of the event being processed. - * @param value Value passed by the event. - * @param hContract Enum struct of the Contract. - * @param hObjective Enum struct of the Objective. - * @return A value higher than Plugin_Continue will prevent potential progress being added to the Contract. - */ -forward Action OnProcessContractLogic(int client, char UUID[MAX_UUID_SIZE], char event[MAX_EVENT_SIZE], -int value, Contract hContract, ContractObjective hObjective); \ No newline at end of file +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/zcontracts/zcontracts_forwards.inc b/addons/sourcemod/scripting/include/zcontracts/zcontracts_forwards.inc new file mode 100644 index 0000000..ac284d1 --- /dev/null +++ b/addons/sourcemod/scripting/include/zcontracts/zcontracts_forwards.inc @@ -0,0 +1,84 @@ +// =========================== FORWARDS =========================== + +/** + * Sent on client completion of a Contract objective. + * + * @param client Client index. + * @param uuid The UUID of the original Contract. + * @param objective Objective index. + */ +forward void OnContractObjectiveCompleted(int client, char UUID[MAX_UUID_SIZE], int objective); +/** + * Sent on client completion of a Contract. + * + * @param client Client index. + * @param uuid The UUID of the original Contract. + */ +forward void OnContractCompleted(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Called before a contract is saved to the database. This is not called for any + * low-level value setting functions (e.g SetContractProgressDatabase). + * + * @param client Client index. + * @param uuid The UUID of the original Contract. + */ +forward bool OnContractPreSave(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Called before an objective is saved to the database. This is not called for any + * low-level value setting functions (e.g SetObjectiveProgressDatabase). + * + * @param client Client index. + * @param uuid The UUID of the original Contract. + * @param objective Objective index. + */ +forward bool OnObjectivePreSave(int client, char UUID[MAX_UUID_SIZE], int objective); + +/** + * Called when an event is about to potentially add progress to a Contract. + * + * @param client Client index. + * @param UUID UUID of the Contract. + * @param objective Objective index. + * @param event Name of the event being processed. + * @param value Value passed by the event. + * @return A value higher than Plugin_Continue will prevent potential progress being added to the Contract. + */ +forward Action OnProcessContractLogic(int client, char UUID[MAX_UUID_SIZE], int objective, char event[MAX_EVENT_SIZE], +int value); + +/** + * Called when a client selects a Contract. + * + * @param client Client index. + * @param UUID UUID of the Contract. + */ +forward void OnClientActivatedContract(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Called when a client selects a Contract and data from the database has been retrieved. + * + * @param client Client index. + * @param UUID UUID of the Contract. + */ +forward void OnClientActivatedContractPost(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Called when the database returns progress data about a Contract. + * + * @param client Client index. + * @param UUID UUID of the Contract. + * @param progress Progress value from the database. + */ +forward void OnContractProgressReceived(int client, char UUID[MAX_UUID_SIZE], int progress); + +/** + * Called when the database returns progress data about a Contract Objective. + * + * @param client Client index. + * @param UUID UUID of the Contract. + * @param objective ID of the Objective. + * @param progress Progress value from the database. + */ +forward void OnObjectiveProgressReceived(int client, char UUID[MAX_UUID_SIZE], int objective, int progress); diff --git a/addons/sourcemod/scripting/include/zcontracts/zcontracts_natives.inc b/addons/sourcemod/scripting/include/zcontracts/zcontracts_natives.inc new file mode 100644 index 0000000..9e9d521 --- /dev/null +++ b/addons/sourcemod/scripting/include/zcontracts/zcontracts_natives.inc @@ -0,0 +1,268 @@ +#if defined __zcontracts_natives_included + #endinput +#endif + +#define __zcontracts_natives_included + + +/** + * The Contracker version is used to determine the minimum Contract that should be + * loaded from the database. This is intended to be used when a change is made to the + * database structure or there is a breaking change in ZContracts. + * @return The value of CONTRACKER_VERSION from zcontracts_main.sp + */ +native int GetContrackerVersion(); + +/** + * Set a client's contract. + * + * @param client Client index. + * @param UUID The UUID of the contract. + * @error Client index is invalid or UUID is invalid. + */ +native bool SetClientContract(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Set a client's contract (with extended functionality) + * + * @param client Client index. + * @param UUID The UUID of the contract. + * @param dont_save Optional argument: doesn't save this as the active Contract in the database. + * @param dont_notify Optional argument: don't notify the player that we've set their contract. + * @error Client index is invalid or UUID is invalid. + */ +native bool SetClientContractEx(int client, char UUID[MAX_UUID_SIZE], bool dont_save = false, bool dont_notify = false); + +/** + * Obtain a client's active Contract UUID. + * + * @param client Client index. + * @param uuidbuffer Buffer to store the UUID. + * @param uuidsize Size of UUID buffer. + * @return A valid UUID will be stored in the buffer and structured with two brackets (e.g {ea20dcca-81c3-41f2-8f3d-a757b2b85765}). + * An empty string will be stored in the buffer and false will be returned if the client has no active contract. + * @error Client index is invalid. + */ +native bool GetClientContract(int client, char[] uuidbuffer, int uuidsize); + +/** + * Obtains a client's active Contract enum struct. + * + * @param client Client index. + * @param buffer Buffer to store the client's contract. + * @error Client index is invalid. + * @note Please make sure your plugins are updated before using this function to prevent crashes. + */ +native bool GetClientContractStruct(int client, any buffer[sizeof(Contract)]); + +/** + * Processes an event for the client's active Contract. + * + * @param client Client index. + * @param event Event to process. + * @param value Value to send alongside this event. + * @param can_combine If true, if this event was recently sent to the event queue, the value from this function will be added to the first event. + * @return True if an event is successfully called, false if the client's contract isn't active. + * @error Client index is invalid or is a bot. + */ +native bool CallContrackerEvent(int client, char event[MAX_EVENT_SIZE], int value, bool can_combine = false); + +// =========================== SCHEMA =========================== + +/** + * Grabs the Keyvalues schema for a Contract. + * + * @param UUID Contract UUID. + * @return KeyValues object of a Contract. + * @error Contract could not be found in the schema. + */ +native KeyValues GetContractSchema(char UUID[MAX_UUID_SIZE]); + +/** + * Grabs the Keyvalues schema for an Objective. + * + * @param UUID Contract UUID. + * @param objective Objective ID. + * @return KeyValues object of an Objective. + * @error Contract or Objective could not be found in the schema. + */ +native KeyValues GetObjectiveSchema(char UUID[MAX_UUID_SIZE], int objective); + +/** + * Grabs the amount of objectives in a Contract. + * + * @param UUID Contract UUID. + * @return Amount of objectives in a Contract. + * @error Contract could not be found in the schema. + */ +native int GetContractObjectiveCount(char UUID[MAX_UUID_SIZE]); + +// =========================== PROGRESS =========================== + +/** + * Grabs the progress of the clients active Contract. + * + * @param client Client index. + * @return Progress value. If the client does not have an active Contract, -1 is returned. + * @error Invalid client index. + */ +native int GetActiveContractProgress(int client); + +/** + * Grabs the progress of an Objective from the clients active Contract. + * + * @param client Client index. + * @param objective Objective ID. + * @return Progress value. If the client does not have an active Contract, -1 is returned. + * @error Invalid client or objective index. + */ +native int GetActiveObjectiveProgress(int client, int objective); + +/** + * Sets the progress of a clients active Contract. This does not automatically + * save the progress to the database (see SaveActiveContractToDatabase). + * + * @param client Client index. + * @param value New progress value. + * @error Invalid client index. + */ +native void SetActiveContractProgress(int client, int value); + +/** + * Sets the progress of an objective in a clients active Contract. This does not automatically + * save the progress to the database (see SaveActiveObjectiveToDatabase). + * + * @param client Client index. + * @param objective Objective ID. + * @param value New progress value. + * @error Invalid client or objective index. + */ +native void SetActiveObjectiveProgress(int client, int objective, int value); + +// =========================== CONTRACT COMPLETION =========================== + +/** + * Returns a list of all completed contracts. + * + * @param client Client index. + * @return StringMap sorted by UUID as key and completion data as the info. + * @note This function is partially unsafe as enum structs are still used inside. + * @error Invalid client index. + */ +native StringMap GetClientCompletedContracts(int client); + +/** + * Checks to see if the client can activate a Contract. + * + * @param client Client index. + * @param UUID UUID of Contract to check. + * @return True if the client can activate a contract, false otherwise. + * @error Invalid client index or invalid UUID. + */ +native bool CanClientActivateContract(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Checks to see if the client can complete a Contract at the current time. + * + * @param client Client index. + * @param UUID UUID of Contract to check. + * @return True if the client can complete the contract, false otherwise. + * @error Invalid client index or invalid UUID. + */ +native bool CanClientCompleteContract(int client, char UUID[MAX_UUID_SIZE]); + +/** + * Checks to see if the client has completed their active Contract. + * + * @param client Client index. + * @return True if a client has completed their active Contract. + * False if the client has not finished their contract or has no contract active. + * @error Invalid client index. + */ +native bool IsActiveContractComplete(int client); + +/** + * Checks to see if a client has already completed a Contract. + * + * @param client Client index. + * @param UUID UUID of Contract to check. + * @return True if the client has completed the contract, false otherwise. + * @error Invalid client index or invalid UUID. + */ +native bool HasClientCompletedContract(int client, char UUID[MAX_UUID_SIZE]); + +// =========================== DATABASE SAVING =========================== + +/** + * Sets the active session for a user in the database. + * @param steamid64 SteamID64 of the user. + * @param UUID Contract UUID to save. + * @error Invalid UUID. + */ +native bool SetSessionDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE]); + +/** + * Saves a Contract to the database for a client. + * + * @param client Client index. + * @error Client index is invalid. + */ +native bool SaveActiveContractToDatabase(int client); + +/** + * Saves an Objective to the database for a client. + * + * @param client Client index. + * @param objective Objective ID. + * @error Client index is invalid. + */ +native bool SaveActiveObjectiveToDatabase(int client, int objective); + +/** + * Sets the progress of a Contract in the database. + * + * @param steamid64 SteamID64 of the user. + * @param UUID The UUID of the contract to modify. + * @param value The value to save to the database. + */ +native bool SetContractProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], int value); + +/** + * Sets the progress of an Objective in the database. + * + * @param steamid64 SteamID64 of the user. + * @param UUID The UUID of the contract to modify. + * @param objective_id The ID of the objective to modify. + * @param value The value to save to the database. + */ +native bool SetObjectiveProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], int objective_id, int value); + +/** + * Marks the contract as complete in the database. + * @param steamid64 SteamID64 of the user. + * @param UUID The UUID of the contract. + * @param data Contract competion data. +*/ +native bool SetCompletedContractInfoDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], any data[sizeof(CompletedContractInfo)]); + +/** + * Deletes all client progress for a contract. + * @param steamid64 SteamID64 of the user. + * @param UUID The UUID of the contract. +*/ +native bool DeleteContractProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE]); + +/** + * Deletes all client progress for an objective. + * @param steamid64 SteamID64 of the user. + * @param UUID The UUID of the contract. + * @param objective_id The objective ID. +*/ +native bool DeleteObjectiveProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE], int objective_id); + +/** + * Deletes all client progress for all objectives. + * @param steamid64 SteamID64 of the user. + * @param UUID The UUID of the contract. +*/ +native bool DeleteAllObjectiveProgressDatabase(char steamid64[64], char UUID[MAX_UUID_SIZE]); diff --git a/addons/sourcemod/scripting/zcontracts/contracts_database.sp b/addons/sourcemod/scripting/zcontracts/contracts_database.sp index d6fd2f4..147c908 100644 --- a/addons/sourcemod/scripting/zcontracts/contracts_database.sp +++ b/addons/sourcemod/scripting/zcontracts/contracts_database.sp @@ -79,34 +79,31 @@ public Action Timer_SaveAllToDB(Handle hTimer) if (!IsClientValid(i) || IsFakeClient(i)) continue; // Save this contract to the database. - Contract ClientContract; - GetClientContract(i, ClientContract); - if (!ClientContract.IsContractInitalized()) continue; + if (!ActiveContract[i].IsContractInitalized()) continue; if (g_DB != null) { // Save the Contract. - if (ClientContract.m_bNeedsDBSave) + if (ActiveContract[i].m_bNeedsDBSave) { - SaveClientContractProgress(i, ClientContract); - ClientContract.m_bNeedsDBSave = false; + SaveActiveContractToDatabase(i); + ActiveContract[i].m_bNeedsDBSave = false; } // Save each of our objectives. - for (int j = 0; j < ClientContract.m_hObjectives.Length; j++) + for (int j = 0; j < ActiveContract[i].m_hObjectives.Length; j++) { - ContractObjective ClientContractObjective; - ClientContract.GetObjective(j, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; + ContractObjective ActiveContractObjective; + ActiveContract[i].GetObjective(j, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) continue; - if (ClientContractObjective.m_bNeedsDBSave) + if (ActiveContractObjective.m_bNeedsDBSave) { - SaveClientObjectiveProgress(i, ClientContract.m_sUUID, ClientContractObjective); - ClientContractObjective.m_bNeedsDBSave = false; - ClientContract.SaveObjective(j, ClientContractObjective); + SaveActiveObjectiveToDatabase(i, j); + ActiveContractObjective.m_bNeedsDBSave = false; + ActiveContract[i].SaveObjective(j, ActiveContractObjective); } } } - ClientContracts[i] = ClientContract; } return Plugin_Continue; } @@ -245,7 +242,7 @@ public void CB_SetObjectiveProgressDatabase(Database db, DBResultSet results, co { if (results.AffectedRows < 1 && g_DebugQuery.BoolValue) { - LogMessage("[ZContracts] %s SAVE: No progress inserted for Contract Objective %d [SQL ERR: %s]", steamid64, error); + LogMessage("[ZContracts] %s SAVE: No progress inserted for Contract Objective %d [SQL ERR: %s]", steamid64, objective_id, error); } else if (results.AffectedRows == 1 && g_DebugQuery.BoolValue) { @@ -260,30 +257,28 @@ public void CB_SetObjectiveProgressDatabase(Database db, DBResultSet results, co * Saves a Contract to the database for a client. * * @param client Client index. - * @param ClientContract The enum struct of the contract to save. - * @error Client index is invalid or the Contract is invalid. + * @error Client index is invalid. */ -public any Native_SaveClientContractProgress(Handle plugin, int numParams) +public any Native_SaveActiveContractToDatabase(Handle plugin, int numParams) { int client = GetNativeCell(1); - Contract ClientContract; - GetNativeArray(2, ClientContract, sizeof(Contract)); - if (IsFakeClient(client) && g_BotContracts.BoolValue) return false; if (!IsClientValid(client) || IsFakeClient(client)) { ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client); } - if (ClientContract.m_sUUID[0] != '{') + + char UUID[MAX_UUID_SIZE]; + if (!GetClientContract(client, UUID, sizeof(UUID))) { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID passed. (%s)", ClientContract.m_sUUID); + LogMessage("SaveActiveContractToDatabase: Client does not have an active contract to save! (%d)", client); + return false; } // Call pre-save forward. Call_StartForward(g_fOnContractPreSave); Call_PushCell(client); - Call_PushString(ClientContract.m_sUUID); - Call_PushArray(ClientContract, sizeof(Contract)); + Call_PushString(UUID); bool ShouldBlock = false; Call_Finish(ShouldBlock); @@ -293,9 +288,10 @@ public any Native_SaveClientContractProgress(Handle plugin, int numParams) // If noone responded or we got a positive response, save to database. if (GetForwardFunctionCount(g_fOnContractPreSave) == 0 || !ShouldBlock) { - if (ClientContract.m_iProgress > 0) + int ActiveProgress = GetActiveContractProgress(client); + if (ActiveProgress > 0) { - SetContractProgressDatabase(steamid64, ClientContract.m_sUUID, ClientContract.m_iProgress); + SetContractProgressDatabase(steamid64, UUID, ActiveProgress); } return true; } @@ -314,33 +310,37 @@ public any Native_SaveClientContractProgress(Handle plugin, int numParams) * Saves an Objective to the database for a client. * * @param client Client index. - * @param UUID UUID of the Contract that contains this objective. - * @param ClientObjective The enum struct of the objective to save. - * @error Client index is invalid or the ClientObjective is invalid. + * @param objective Objective ID. + * @error Client index is invalid. */ -public any Native_SaveClientObjectiveProgress(Handle plugin, int numParams) +public any Native_SaveActiveObjectiveToDatabase(Handle plugin, int numParams) { int client = GetNativeCell(1); - char UUID[MAX_UUID_SIZE]; - GetNativeString(2, UUID, sizeof(UUID)); - ContractObjective ClientContractObjective; - GetNativeArray(3, ClientContractObjective, sizeof(ContractObjective)); - if (IsFakeClient(client) && g_BotContracts.BoolValue) return false; if (!IsClientValid(client) || IsFakeClient(client)) { ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client); } - if (UUID[0] != '{') + + char UUID[MAX_UUID_SIZE]; + if (!GetClientContract(client, UUID, sizeof(UUID))) { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID passed. (%s)", UUID); + LogMessage("SaveActiveObjectiveToDatabase: Client %N does not have an active contract to save!", client); + return false; + } + + int objective = GetNativeCell(2); + if (objective > /*zero indexed*/ GetContractObjectiveCount(UUID)-1) + { + LogMessage("SaveActiveObjectiveToDatabase: Invalid contract objective ID passed (%N: %d)", client, objective); + return false; } // Call pre-save forward. Call_StartForward(g_fOnObjectivePreSave); Call_PushCell(client); Call_PushString(UUID); - Call_PushArray(ClientContractObjective, sizeof(ContractObjective)); + Call_PushCell(objective); bool ShouldBlock = false; Call_Finish(ShouldBlock); @@ -350,9 +350,10 @@ public any Native_SaveClientObjectiveProgress(Handle plugin, int numParams) // If noone responded or we got a positive response, save to database. if (GetForwardFunctionCount(g_fOnObjectivePreSave) == 0 || !ShouldBlock) { - if (ClientContractObjective.m_iProgress > 0) + int ActiveProgress = GetActiveObjectiveProgress(client, objective); + if (ActiveProgress > 0) { - SetObjectiveProgressDatabase(steamid64, UUID, ClientContractObjective.m_iInternalID, ClientContractObjective.m_iProgress); + SetObjectiveProgressDatabase(steamid64, UUID, objective, ActiveProgress); } return true; } @@ -369,11 +370,8 @@ public any Native_SaveClientObjectiveProgress(Handle plugin, int numParams) */ public void CB_SetClientContract_Contract(Database db, DBResultSet results, const char[] error, int client) { - Contract ClientContract; - GetClientContract(client, ClientContract); - // This may need to change from CONTRACKER_VERSION in the future. - const int MinimumRequiredVersion = CONTRACKER_VERSION; + const int MinimumRequiredVersion = 1; if (results == INVALID_HANDLE) return; while (results.FetchRow()) @@ -385,20 +383,27 @@ public void CB_SetClientContract_Contract(Database db, DBResultSet results, cons ThrowError("Missing required database key: \"version\" while fetching client %d contract data.", client); } int DataVersion = results.FetchInt(VersionField); - if (DataVersion < MinimumRequiredVersion) + if (MinimumRequiredVersion > DataVersion) { ThrowError("Outdated contract data. Minimum version: %d, data version: %d", MinimumRequiredVersion, DataVersion); } - ClientContract.m_iProgress = results.FetchInt(2); + ActiveContract[client].m_iProgress = results.FetchInt(2); + + Call_StartForward(g_fOnContractProgressReceived); + Call_PushCell(client); + Call_PushString(ActiveContract[client].m_sUUID); + Call_PushCell(ActiveContract[client].m_iProgress); + Call_Finish(); + if (g_DebugQuery.BoolValue && IsClientValid(client)) { - LogMessage("[ZContracts] %N LOAD: Successfully grabbed Contract progress from database (%d/%d).", client, ClientContract.m_iProgress, ClientContract.m_iMaxProgress); + LogMessage("[ZContracts] %N LOAD: Successfully grabbed Contract progress from database (%d/%d).", + client, ActiveContract[client].m_iProgress, ActiveContract[client].m_iMaxProgress); } } - ClientContract.m_bLoadedFromDatabase = true; - ClientContracts[client] = ClientContract; + ActiveContract[client].m_bLoadedFromDatabase = true; } /** @@ -406,11 +411,8 @@ public void CB_SetClientContract_Contract(Database db, DBResultSet results, cons */ public void CB_SetClientContract_Objective(Database db, DBResultSet results, const char[] error, int client) { - Contract ClientContract; - GetClientContract(client, ClientContract); - // This may need to change from CONTRACKER_VERSION in the future. - const int MinimumRequiredVersion = CONTRACKER_VERSION; + const int MinimumRequiredVersion = 1; if (results == INVALID_HANDLE) return; while (results.FetchRow()) @@ -422,16 +424,23 @@ public void CB_SetClientContract_Objective(Database db, DBResultSet results, con ThrowError("Missing required database key: \"version\" while fetching client %d contract data.", client); } int DataVersion = results.FetchInt(VersionField); - if (DataVersion < MinimumRequiredVersion) + if (MinimumRequiredVersion > DataVersion) { ThrowError("Outdated contract objective data. Minimum version: %d, data version: %d", MinimumRequiredVersion, DataVersion); } ContractObjective hObj; int db_id = results.FetchInt(2); - ClientContract.GetObjective(db_id, hObj); + ActiveContract[client].GetObjective(db_id, hObj); hObj.m_iProgress = results.FetchInt(3); - ClientContract.SaveObjective(db_id, hObj); + ActiveContract[client].SaveObjective(db_id, hObj); + + Call_StartForward(g_fOnObjectiveProgressReceived); + Call_PushCell(client); + Call_PushString(ActiveContract[client].m_sUUID); + Call_PushCell(db_id); + Call_PushCell(hObj.m_iProgress); + Call_Finish(); if (g_DebugQuery.BoolValue) { @@ -440,8 +449,7 @@ public void CB_SetClientContract_Objective(Database db, DBResultSet results, con } } - ClientContract.m_bLoadedFromDatabase = true; - ClientContracts[client] = ClientContract; + ActiveContract[client].m_bLoadedFromDatabase = true; } /** diff --git a/addons/sourcemod/scripting/zcontracts/contracts_menu.sp b/addons/sourcemod/scripting/zcontracts/contracts_menu.sp index 1dcec93..d22a62a 100644 --- a/addons/sourcemod/scripting/zcontracts/contracts_menu.sp +++ b/addons/sourcemod/scripting/zcontracts/contracts_menu.sp @@ -20,7 +20,7 @@ void CreateContractMenu() gContractMenu = new Menu(ContractMenuHandler, MENU_ACTIONS_ALL); char MenuTitle[128] = "ZContracts %s - Contract Selector"; - Format(MenuTitle, sizeof(MenuTitle), MenuTitle, PLUGIN_VERSION); + Format(MenuTitle, sizeof(MenuTitle), MenuTitle, ZCONTRACTS_PLUGIN_VERSION); gContractMenu.SetTitle(MenuTitle); gContractMenu.OptionFlags = MENUFLAG_NO_SOUND | MENUFLAG_BUTTON_EXIT; @@ -92,9 +92,6 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) // Are we a contract? else if (MenuKey[0] == '{') { - Contract ClientContract; - GetClientContract(param1, ClientContract); - char ContractDirectory[MAX_DIRECTORY_SIZE]; GetContractDirectory(MenuKey, ContractDirectory); // Are we in the right directory? @@ -104,9 +101,9 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) } // Are we currently using this contract? - if (StrEqual(MenuKey, ClientContract.m_sUUID)) + if (StrEqual(MenuKey, ActiveContract[param1].m_sUUID)) { - if (g_RepeatContracts.BoolValue && ClientContract.IsContractComplete()) + if (g_RepeatContracts.BoolValue && ActiveContract[param1].IsContractComplete()) { return ITEMDRAW_DEFAULT; } @@ -140,8 +137,8 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) return style; } - // If we're currently doing a Contract, add "(SELECTED)" - // If the item is a directory, add ">>" + // If we're currently doing a Contract, add "(ACTIVE)" + // If the item is a directory, add ">" case MenuAction_DisplayItem: { char MenuDisplay[MAX_CONTRACT_NAME_SIZE + 16]; @@ -175,9 +172,6 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) // Is this a Contract? else if (MenuKey[0] == '{') { - Contract ClientContract; - GetClientContract(param1, ClientContract); - if (IsContractLockedForClient(param1, MenuKey)) { Format(MenuDisplay, sizeof(MenuDisplay), "[X] %s", MenuDisplay); @@ -200,7 +194,7 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) return RedrawMenuItem(MenuDisplay); } - if (StrEqual(ClientContract.m_sUUID, MenuKey)) + if (StrEqual(ActiveContract[param1].m_sUUID, MenuKey)) { Format(MenuDisplay, sizeof(MenuDisplay), "%s [ACTIVE]", MenuDisplay); return RedrawMenuItem(MenuDisplay); @@ -223,7 +217,7 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) ReplaceString(MenuDisplay, sizeof(MenuDisplay), g_Menu_CurrentDirectory[param1], ""); } - Format(MenuDisplay, sizeof(MenuDisplay), ">> %s", MenuDisplay); + Format(MenuDisplay, sizeof(MenuDisplay), "> %s", MenuDisplay); return RedrawMenuItem(MenuDisplay); } } @@ -246,14 +240,14 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) // If we're allowed to repeat Contracts, allow us to repeat // our currently selected contract if it's completed. else if (g_RepeatContracts.BoolValue && - StrEqual(MenuKey, ClientContracts[param1].m_sUUID) && - ClientContracts[param1].IsContractComplete()) + StrEqual(MenuKey, ActiveContract[param1].m_sUUID) && + ActiveContract[param1].IsContractComplete()) { CompletedContractInfo info; - CompletedContracts[param1].GetArray(ClientContracts[param1].m_sUUID, info, sizeof(CompletedContractInfo)); + CompletedContracts[param1].GetArray(ActiveContract[param1].m_sUUID, info, sizeof(CompletedContractInfo)); if (!info.m_bReset) { - ConstructRepeatContractPanel(param1, ClientContracts[param1].m_sUUID); + ConstructRepeatContractPanel(param1, ActiveContract[param1].m_sUUID); } else { @@ -275,7 +269,7 @@ int ContractMenuHandler(Menu menu, MenuAction action, int param1, int param2) } } // Are we NOT currently using this contract? - else if (!StrEqual(MenuKey, ClientContracts[param1].m_sUUID)) + else if (!StrEqual(MenuKey, ActiveContract[param1].m_sUUID)) { SetClientContract(param1, MenuKey); } @@ -340,14 +334,11 @@ public Action OpenContrackerForClientCmd(int client, int args) void OpenContrackerForClient(int client) { - Contract ClientContract; - GetClientContract(client, ClientContract); - // Are we doing a Contract? - if (ClientContract.IsContractInitalized()) + if (ActiveContract[client].IsContractInitalized()) { // Display our objective display instead. - CreateObjectiveDisplay(client, ClientContract, false); + CreateObjectiveDisplay(client, ActiveContract[client], false); } else { @@ -504,7 +495,7 @@ void ConstructHelpPanel(int client) gHelpDisplay[client].DrawText("Welcome to ZContracts - a custom Contracker implementation."); gHelpDisplay[client].DrawText("To select a Contract, press the corresponding menu option."); gHelpDisplay[client].DrawText("Completed Contracts are marked with [✓], locked Contracts are maked with [X]") - gHelpDisplay[client].DrawText("Directories are notated with \">>\". They contain more Contracts inside."); + gHelpDisplay[client].DrawText("Directories are notated with \">\". They contain more Contracts inside."); gHelpDisplay[client].DrawText("If you wish to disable the HUD or sounds, type \"/zcpref\" in chat."); gHelpDisplay[client].DrawText(" "); gHelpDisplay[client].DrawItem("Take me to the Contracker and never show this again!"); @@ -607,7 +598,7 @@ int LockedContractMenuHandler(Menu menu, MenuAction action, int param1, int para if (MenuKey[0] == '{') { Contract ClientContract; - GetClientContract(param1, ClientContract); + GetClientContractStruct(param1, ClientContract); // Are we currently using this contract? if (StrEqual(MenuKey, ClientContract.m_sUUID)) @@ -633,7 +624,7 @@ int LockedContractMenuHandler(Menu menu, MenuAction action, int param1, int para if (MenuKey[0] == '{') { Contract ClientContract; - GetClientContract(param1, ClientContract); + GetClientContractStruct(param1, ClientContract); // Are we doing this Contract? if (StrEqual(ClientContract.m_sUUID, MenuKey)) @@ -675,7 +666,7 @@ int LockedContractMenuHandler(Menu menu, MenuAction action, int param1, int para delete menu; } // Are we NOT currently using this contract? - else if (!StrEqual(MenuKey, ClientContracts[param1].m_sUUID)) + else if (!StrEqual(MenuKey, ActiveContract[param1].m_sUUID)) { SetClientContract(param1, MenuKey); } diff --git a/addons/sourcemod/scripting/zcontracts/contracts_schema.sp b/addons/sourcemod/scripting/zcontracts/contracts_schema.sp index 5cbdeef..fd30f28 100644 --- a/addons/sourcemod/scripting/zcontracts/contracts_schema.sp +++ b/addons/sourcemod/scripting/zcontracts/contracts_schema.sp @@ -192,33 +192,8 @@ public void CreateContract(KeyValues hContractConf, Contract hContract) // Grab the teams that can do this contract. char sTeamBuffer[64]; hContractConf.GetString("team_restriction", sTeamBuffer, sizeof(sTeamBuffer), "-1"); - // Is this an int? We can easily grab the team index and set it from there. - int iTeamIndex = StringToInt(sTeamBuffer); - // CS:GO and TF2 team indexes don't go any higher than three. If this plugin - // is ported to another Engine with more teams, you will need to change the - // value of MAXIMUM_TEAMS to be max+1. - if (iTeamIndex != 0 && iTeamIndex < MAXIMUM_TEAMS) - { - hContract.m_iTeamRestriction = iTeamIndex; - } - else if (StrEqual(sTeamBuffer, "0")) hContract.m_iTeamRestriction = -1; - else - { - // Set the incoming restriction string to be all lowercase. - for (int i = 0; i < strlen(sTeamBuffer); i++) - { - CharToLower(sTeamBuffer[i]); - } - - // Depending on the engine version, we can specify aliases - // to use instead of intergers. - switch (GetEngineVersion()) - { - case Engine_TF2: iTeamIndex = TF2_GetTeamIndexFromString(sTeamBuffer); - case Engine_CSGO: iTeamIndex = CSGO_GetTeamIndexFromString(sTeamBuffer); - } - hContract.m_iTeamRestriction = iTeamIndex; - } + + hContract.m_iTeamRestriction = GetTeamFromSchema(sTeamBuffer); // Create our objectives. if (hContractConf.JumpToKey("objectives", false)) @@ -296,6 +271,104 @@ public void ProcessContractsSchema() PrintToServer("[ZContracts] Loaded %d contracts.", iContractCount); } +/** + * Grabs the Keyvalues schema for a Contract. + * + * @param UUID Contract UUID. + * @return KeyValues object of a Contract. + * @error Contract could not be found in the schema. +*/ +public any Native_GetContractSchema(Handle plugin, int numParams) +{ + char UUID[MAX_UUID_SIZE]; + GetNativeString(1, UUID, sizeof(UUID)); + if (!g_ContractSchema.JumpToKey(UUID)) + { + // Error out! + ThrowNativeError(SP_ERROR_NOT_FOUND, "Could not find Contract in schema - invalid UUID %s", UUID); + } + + // Clone our handle and return it. + KeyValues NewKV = new KeyValues(UUID); + NewKV.Import(g_ContractSchema); + g_ContractSchema.Rewind(); + Handle Schema = CloneHandle(NewKV, plugin); + return view_as(Schema); +} + +/** + * Grabs the Keyvalues schema for an Objective. + * + * @param UUID Contract UUID. + * @param objective Objective ID. + * @return KeyValues object of an Objective. + * @error Contract or Objective could not be found in the schema. + */ +public any Native_GetObjectiveSchema(Handle plugin, int numParams) +{ + char UUID[MAX_UUID_SIZE]; + GetNativeString(1, UUID, sizeof(UUID)); + int objective = GetNativeCell(2); + if (!g_ContractSchema.JumpToKey(UUID)) + { + // Error out! + ThrowNativeError(SP_ERROR_NOT_FOUND, "Could not find Contract in schema - invalid UUID %s", UUID); + } + if (!g_ContractSchema.JumpToKey("objectives")) + { + // Error out again! + ThrowNativeError(SP_ERROR_NOT_FOUND, "Could not find objectives in %s schema - invalid structure", UUID); + } + char StrObjective[4]; + IntToString(objective, StrObjective, sizeof(StrObjective)); + if (!g_ContractSchema.JumpToKey(StrObjective)) + { + // Error out once more! + ThrowNativeError(SP_ERROR_NOT_FOUND, "Could not find objective %d in %s schema", objective, UUID); + } + + // Clone our handle and return it. + KeyValues NewKV = new KeyValues(StrObjective); + NewKV.Import(g_ContractSchema); + g_ContractSchema.Rewind(); + Handle Schema = CloneHandle(NewKV, plugin); + return view_as(Schema); +} + +/** + * Grabs the amount of objectives in a Contract. + * + * @param UUID Contract UUID. + * @return Amount of objectives in a Contract. + * @error Contract could not be found in the schema. + */ +public any Native_GetContractObjectiveCount(Handle plugin, int numParams) +{ + char UUID[MAX_UUID_SIZE]; + GetNativeString(1, UUID, sizeof(UUID)); + if (!g_ContractSchema.JumpToKey(UUID)) + { + // Error out! + ThrowNativeError(SP_ERROR_NOT_FOUND, "Could not find Contract in schema - invalid UUID %s", UUID); + } + if (!g_ContractSchema.JumpToKey("objectives")) + { + // Error out again! + ThrowNativeError(SP_ERROR_NOT_FOUND, "Could not find objectives in %s schema - invalid structure", UUID); + } + + int count = 0; + if(g_ContractSchema.GotoFirstSubKey()) + { + do + { + count++; + } while (g_ContractSchema.GotoNextKey()); + } + g_ContractSchema.Rewind(); + return count; +} + bool CreateContractFromUUID(const char[] sUUID, Contract hBuffer) { hBuffer.m_bInitalized = false; @@ -318,4 +391,37 @@ bool GetContractDirectory(const char[] sUUID, char buffer[MAX_DIRECTORY_SIZE]) return true; } return false; +} + +int GetTeamFromSchema(const char[] sTeamBuffer) +{ + // Is this an int? We can easily grab the team index and set it from there. + int iTeamIndex = StringToInt(sTeamBuffer); + // CS:GO and TF2 team indexes don't go any higher than three. If this plugin + // is ported to another Engine with more teams, you will need to change the + // value of MAXIMUM_TEAMS to be max+1. + if (iTeamIndex != 0 && iTeamIndex < MAXIMUM_TEAMS) + { + return iTeamIndex; + } + else if (StrEqual(sTeamBuffer, "0")) return -1; + else + { + // Set the incoming restriction string to be all lowercase. + for (int i = 0; i < strlen(sTeamBuffer); i++) + { + CharToLower(sTeamBuffer[i]); + } + + // Depending on the engine version, we can specify aliases + // to use instead of intergers. + switch (GetEngineVersion()) + { + case Engine_TF2: return TF2_GetTeamIndexFromString(sTeamBuffer); + case Engine_CSGO: return CSGO_GetTeamIndexFromString(sTeamBuffer); + } + } + + // Could not determine team. + return -1; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/zcontracts/contracts_timers.sp b/addons/sourcemod/scripting/zcontracts/contracts_timers.sp index 9f313f0..47a5c6e 100644 --- a/addons/sourcemod/scripting/zcontracts/contracts_timers.sp +++ b/addons/sourcemod/scripting/zcontracts/contracts_timers.sp @@ -13,7 +13,7 @@ public Action EventTimer(Handle hTimer, DataPack hPack) // Get our contracts. Contract hContract; - GetClientContract(client, hContract); + GetClientContractStruct(client, hContract); // Grab our objective. ContractObjective hObjective; diff --git a/addons/sourcemod/scripting/zcontracts/contracts_utils.sp b/addons/sourcemod/scripting/zcontracts/contracts_utils.sp index ccb79f6..021d8da 100644 --- a/addons/sourcemod/scripting/zcontracts/contracts_utils.sp +++ b/addons/sourcemod/scripting/zcontracts/contracts_utils.sp @@ -1,12 +1,3 @@ -bool HasClientCompletedContract(int client, char UUID[MAX_UUID_SIZE]) -{ - // Could this be made any faster? I'm not a real programmer. - // The answer is, yes it can. - if (!IsClientValid(client)) return false; - if (IsFakeClient(client) && !g_BotContracts.BoolValue) return false; - return CompletedContracts[client].ContainsKey(UUID); -} - bool IsContractLockedForClient(int client, char UUID[MAX_UUID_SIZE]) { if (g_DebugUnlockContracts.BoolValue) return false; @@ -88,11 +79,11 @@ void GiveRandomContract(int client) // Grant Contract. if (IsFakeClient(client) && !g_BotContracts.BoolValue) { - SetClientContract(client, RandomUUID, false, false); + SetClientContractEx(client, RandomUUID, false, false); } else { - SetClientContract(client, RandomUUID, true, true); + SetClientContractEx(client, RandomUUID, true, true); } } diff --git a/addons/sourcemod/scripting/zcontracts_main.sp b/addons/sourcemod/scripting/zcontracts_main.sp index 6b151b3..1027aaa 100644 --- a/addons/sourcemod/scripting/zcontracts_main.sp +++ b/addons/sourcemod/scripting/zcontracts_main.sp @@ -19,8 +19,8 @@ Handle g_DatabaseRetryTimer; Handle g_HudSync; // Player Contracts. -Contract OldClientContracts[MAXPLAYERS+1]; -Contract ClientContracts[MAXPLAYERS+1]; +Contract OldContract[MAXPLAYERS+1]; +Contract ActiveContract[MAXPLAYERS+1]; StringMap CompletedContracts[MAXPLAYERS+1]; // ConVars. @@ -50,6 +50,10 @@ GlobalForward g_fOnContractCompleted; GlobalForward g_fOnContractPreSave; GlobalForward g_fOnObjectivePreSave; GlobalForward g_fOnProcessContractLogic; +GlobalForward g_fOnClientActivatedContract; +GlobalForward g_fOnClientActivatedContractPost; +GlobalForward g_fOnContractProgressReceived; +GlobalForward g_fOnObjectiveProgressReceived; // This arraylist contains a list of objectives that we need to update. ArrayList g_ObjectiveUpdateQueue; @@ -61,17 +65,6 @@ char ContractCompletedSound[64]; char ProgressLoadedSound[64]; char SelectOptionSound[64]; -// Major version number, feature number, patch number -#define PLUGIN_VERSION "0.8.0" -// This value should be incremented with every breaking version made to the -// database so saves can be easily converted. For developers who fork this project and -// wish to merge changes, do not increment this number until merge. -// Other plugins should use the GetContrackerVersion() native to get this value, but -// this main plugin and subplugins are free to use the define name in its place. -#define CONTRACKER_VERSION 1 -// How often the HUD will refresh itself. -#define HUD_REFRESH_RATE 0.5 - #include "zcontracts/contracts_schema.sp" #include "zcontracts/contracts_utils.sp" #include "zcontracts/contracts_timers.sp" @@ -84,7 +77,7 @@ public Plugin myinfo = name = "ZContracts - Custom Contract Logic", author = "ZoNiCaL", description = "Allows server operators to design their own contracts.", - version = PLUGIN_VERSION, + version = ZCONTRACTS_PLUGIN_VERSION, url = "" }; @@ -93,22 +86,42 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max RegPluginLibrary("zcontracts"); // ================ FORWARDS ================ - g_fOnObjectiveCompleted = new GlobalForward("OnContractObjectiveCompleted", ET_Ignore, Param_Cell, Param_String, Param_Array); - g_fOnContractCompleted = new GlobalForward("OnContractCompleted", ET_Ignore, Param_Cell, Param_String, Param_Array); - g_fOnContractPreSave = new GlobalForward("OnContractPreSave", ET_Event, Param_Cell, Param_String, Param_Array); - g_fOnObjectivePreSave = new GlobalForward("OnObjectivePreSave", ET_Event, Param_Cell, Param_String, Param_Array); - g_fOnProcessContractLogic = new GlobalForward("OnProcessContractLogic", ET_Event, Param_Cell, Param_String, Param_String, Param_Cell, Param_Array, Param_Array); - + g_fOnObjectiveCompleted = new GlobalForward("OnContractObjectiveCompleted", ET_Ignore, Param_Cell, Param_String, Param_Cell); + g_fOnContractCompleted = new GlobalForward("OnContractCompleted", ET_Ignore, Param_Cell, Param_String); + g_fOnContractPreSave = new GlobalForward("OnContractPreSave", ET_Event, Param_Cell, Param_String); + g_fOnObjectivePreSave = new GlobalForward("OnObjectivePreSave", ET_Event, Param_Cell, Param_String, Param_Cell); + g_fOnProcessContractLogic = new GlobalForward("OnProcessContractLogic", ET_Event, Param_Cell, Param_String, Param_Cell, Param_String, Param_Cell); + g_fOnClientActivatedContract = new GlobalForward("OnClientActivatedContract", ET_Ignore, Param_Cell, Param_String); + g_fOnClientActivatedContractPost = new GlobalForward("OnClientActivatedContractPost", ET_Ignore, Param_Cell, Param_String); + g_fOnContractProgressReceived = new GlobalForward("OnContractProgressReceived", ET_Ignore, Param_Cell, Param_String, Param_Cell); + g_fOnObjectiveProgressReceived = new GlobalForward("OnObjectiveProgressReceived", ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_Cell); + // ================ NATIVES ================ CreateNative("GetContrackerVersion", Native_GetContrackerVersion); CreateNative("SetClientContract", Native_SetClientContract); - CreateNative("SetClientContractStruct", Native_SetClientContractStruct); + CreateNative("SetClientContractEx", Native_SetClientContractEx); CreateNative("GetClientContract", Native_GetClientContract); + CreateNative("GetClientContractStruct", Native_GetClientContractStruct); CreateNative("CallContrackerEvent", Native_CallContrackerEvent); - CreateNative("SaveClientContractProgress", Native_SaveClientContractProgress); - CreateNative("SaveClientObjectiveProgress", Native_SaveClientObjectiveProgress); + CreateNative("GetContractSchema", Native_GetContractSchema); + CreateNative("GetObjectiveSchema", Native_GetObjectiveSchema); + CreateNative("GetContractObjectiveCount", Native_GetContractObjectiveCount); + + CreateNative("GetActiveContractProgress", Native_GetActiveContractProgress); + CreateNative("GetActiveObjectiveProgress", Native_GetActiveObjectiveProgress); + CreateNative("SetActiveContractProgress", Native_SetActiveContractProgress); + CreateNative("SetActiveObjectiveProgress", Native_SetActiveObjectiveProgress); + + CreateNative("GetClientCompletedContracts", Native_GetClientCompletedContracts); + CreateNative("CanClientActivateContract", Native_CanClientActivateContract); + CreateNative("CanClientCompleteContract", Native_CanClientCompleteContract); + CreateNative("IsActiveContractComplete", Native_IsActiveContractComplete); + CreateNative("HasClientCompletedContract", Native_HasClientCompletedContract); + + CreateNative("SaveActiveContractToDatabase", Native_SaveActiveContractToDatabase); + CreateNative("SaveActiveObjectiveToDatabase", Native_SetActiveObjectiveProgress); CreateNative("SetContractProgressDatabase", Native_SetContractProgressDatabase); CreateNative("SetObjectiveProgressDatabase", Native_SetObjectiveProgressDatabase); CreateNative("DeleteContractProgressDatabase", Native_DeleteContractProgressDatabase); @@ -122,7 +135,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max public void OnPluginStart() { - PrintToServer("[ZContracts] Initalizing ZContracts %s - Contracker Version: %d", PLUGIN_VERSION, CONTRACKER_VERSION); + PrintToServer("[ZContracts] Initalizing ZContracts %s - Contracker Version: %d", ZCONTRACTS_PLUGIN_VERSION, CONTRACKER_VERSION); // Create our Hud Sync object. g_HudSync = CreateHudSynchronizer(); @@ -222,17 +235,14 @@ public void OnMapEnd() if (!IsClientValid(i) || IsFakeClient(i)) continue; SaveClientPreferences(i); - Contract ClientContract; - GetClientContract(i, ClientContract); - - SaveClientContractProgress(i, ClientContracts[i]); - for (int j = 0; j < ClientContract.m_hObjectives.Length; j++) + SaveActiveContractToDatabase(i); + for (int j = 0; j < ActiveContract[i].m_hObjectives.Length; j++) { - ContractObjective ClientContractObjective; - ClientContract.GetObjective(j, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; + ContractObjective ActiveContractObjective; + ActiveContract[i].GetObjective(j, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) continue; - SaveClientObjectiveProgress(i, ClientContract.m_sUUID, ClientContractObjective); + SaveActiveObjectiveToDatabase(i, j); } } } @@ -271,8 +281,8 @@ public void DelayedLoad(int client) { // Reset variables. Contract BlankContract; - ClientContracts[client] = BlankContract; - OldClientContracts[client] = BlankContract; + ActiveContract[client] = BlankContract; + OldContract[client] = BlankContract; DB_LoadContractFromLastSession(client); DB_LoadAllClientPreferences(client); @@ -286,24 +296,20 @@ public void OnClientDisconnect(int client) && g_DB != null) { SaveClientPreferences(client); - - Contract ClientContract; - GetClientContract(client, ClientContract); - - SaveClientContractProgress(client, ClientContracts[client]); - for (int i = 0; i < ClientContract.m_hObjectives.Length; i++) + SaveActiveContractToDatabase(client); + for (int i = 0; i < ActiveContract[i].m_hObjectives.Length; i++) { - ContractObjective ClientContractObjective; - ClientContract.GetObjective(i, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; + ContractObjective ActiveContractObjective; + ActiveContract[i].GetObjective(i, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) continue; - SaveClientObjectiveProgress(client, ClientContract.m_sUUID, ClientContractObjective); + SaveActiveObjectiveToDatabase(client, i); } } Contract Blank; - ClientContracts[client] = Blank; - OldClientContracts[client] = Blank; + ActiveContract[client] = Blank; + OldContract[client] = Blank; g_Menu_CurrentDirectory[client] = "root"; g_Menu_DirectoryDeepness[client] = 1; @@ -347,20 +353,51 @@ public any Native_GetContrackerVersion(Handle plugin, int numParams) } /** - * Obtains a client's active Contract. + * Obtain a client's active Contract UUID. + * + * @param client Client index. + * @param uuidbuffer Buffer to store the UUID. + * @param uuidsize Size of UUID buffer. + * @return A valid UUID will be stored in the buffer and structured with two brackets (e.g {ea20dcca-81c3-41f2-8f3d-a757b2b85765}). + * An empty string will be stored in the buffer and false will be returned if the client has no active contract. + * @error Client index is invalid. + */ +public any Native_GetClientContract(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); + } + + if (ActiveContract[client].IsContractInitalized()) + { + SetNativeString(2, ActiveContract[client].m_sUUID, GetNativeCell(3)); + return true; + } + else + { + SetNativeString(2, "", GetNativeCell(3)); + return false; + } +} + +/** + * Obtains a client's active Contract enum struct. * * @param client Client index. * @param buffer Buffer to store the client's contract. - * @error Client index is invalid. + * @error Client index is invalid. + * @note Please make sure your plugins are updated before using this function to prevent crashes. */ -public any Native_GetClientContract(Handle plugin, int numParams) +public any Native_GetClientContractStruct(Handle plugin, int numParams) { int client = GetNativeCell(1); if (!IsClientValid(client)) { return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); } - SetNativeArray(2, ClientContracts[client], sizeof(Contract)); + SetNativeArray(2, ActiveContract[client], sizeof(Contract)); return true; } @@ -385,14 +422,10 @@ public any Native_CallContrackerEvent(Handle plugin, int numParams) { return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); } - if (IsFakeClient(client) && !g_BotContracts.BoolValue) return false; - - Contract ClientContract; - GetClientContract(client, ClientContract); // Do we have a contract currently active? - if (!ClientContract.IsContractInitalized() || ClientContract.IsContractComplete()) return false; + if (!ActiveContract[client].IsContractInitalized() || ActiveContract[client].IsContractComplete()) return false; if (g_DebugEvents.BoolValue) { @@ -400,20 +433,20 @@ public any Native_CallContrackerEvent(Handle plugin, int numParams) } // Try to add our objectives to the increment queue. - for (int i = 0; i < ClientContract.m_hObjectives.Length; i++) + for (int i = 0; i < ActiveContract[client].m_hObjectives.Length; i++) { - ContractObjective ClientContractObjective; - ClientContract.GetObjective(i, ClientContractObjective); + ContractObjective ActiveContractObjective; + ActiveContract[client].GetObjective(i, ActiveContractObjective); - if (!ClientContractObjective.m_bInitalized || ClientContractObjective.IsObjectiveComplete()) continue; + if (!ActiveContractObjective.m_bInitalized || ActiveContractObjective.IsObjectiveComplete()) continue; // Check to see if we have this event within our Contract objectives. // This saves on processing time in ProcessLogicForContractObjective() later on. bool EventCheckPassed = false; - for (int j = 0; j < ClientContractObjective.m_hEvents.Length; j++) + for (int j = 0; j < ActiveContractObjective.m_hEvents.Length; j++) { ContractObjectiveEvent ObjEvent; - ClientContractObjective.m_hEvents.GetArray(j, ObjEvent, sizeof(ContractObjectiveEvent)); + ActiveContractObjective.m_hEvents.GetArray(j, ObjEvent, sizeof(ContractObjectiveEvent)); if (StrEqual(ObjEvent.m_sEventName, event)) { @@ -436,8 +469,8 @@ public any Native_CallContrackerEvent(Handle plugin, int numParams) { g_ObjectiveUpdateQueue.GetArray(k, ObjUpdate); if (ObjUpdate.m_iClient != client) continue; - if (ObjUpdate.m_iObjectiveID != ClientContractObjective.m_iInternalID) continue; - if (!StrEqual(ObjUpdate.m_sUUID, ClientContract.m_sUUID)) continue; + if (ObjUpdate.m_iObjectiveID != ActiveContractObjective.m_iInternalID) continue; + if (!StrEqual(ObjUpdate.m_sUUID, ActiveContract[client].m_sUUID)) continue; if (!StrEqual(ObjUpdate.m_sEvent, event)) continue; ObjUpdate.m_iValue += value; @@ -454,8 +487,8 @@ public any Native_CallContrackerEvent(Handle plugin, int numParams) ObjectiveUpdate ObjUpdate; ObjUpdate.m_iClient = client; ObjUpdate.m_iValue = value; - ObjUpdate.m_iObjectiveID = ClientContractObjective.m_iInternalID; - ObjUpdate.m_sUUID = ClientContract.m_sUUID; + ObjUpdate.m_iObjectiveID = ActiveContractObjective.m_iInternalID; + ObjUpdate.m_sUUID = ActiveContract[client].m_sUUID; ObjUpdate.m_sEvent = event; g_ObjectiveUpdateQueue.PushArray(ObjUpdate, sizeof(ObjectiveUpdate)); } @@ -475,8 +508,6 @@ public any Native_SetClientContract(Handle plugin, int numParams) int client = GetNativeCell(1); char UUID[MAX_UUID_SIZE]; GetNativeString(2, UUID, sizeof(UUID)); - bool dont_save = GetNativeCell(3); - bool dont_notify = GetNativeCell(4); if (!IsClientValid(client)) { @@ -490,35 +521,35 @@ public any Native_SetClientContract(Handle plugin, int numParams) } // If we have a Contract already selected, save it's progress to the database. - Contract OldClientContract; - GetClientContract(client, OldClientContract); - OldClientContract.m_bActive = false; - OldClientContracts[client] = OldClientContract; - - if (OldClientContract.IsContractInitalized() && - !OldClientContract.IsContractComplete() && - !StrEqual(OldClientContract.m_sUUID, UUID) && + ActiveContract[client].m_bActive = false; + OldContract[client] = ActiveContract[client]; + + if (OldContract[client].IsContractInitalized() && + !OldContract[client].IsContractComplete() && + !StrEqual(OldContract[client].m_sUUID, UUID) && g_DB != null) { - SaveClientContractProgress(client, OldClientContract); - for (int i = 0; i < OldClientContract.m_hObjectives.Length; i++) + char steamid64[64]; + GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); + SetContractProgressDatabase(steamid64, OldContract[client].m_sUUID, OldContract[client].m_iProgress); + for (int i = 0; i < OldContract[client].m_hObjectives.Length; i++) { - ContractObjective ClientContractObjective; - OldClientContract.GetObjective(i, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; + ContractObjective OldContractObjective; + OldContract[client].GetObjective(i, OldContractObjective); + if (!OldContractObjective.m_bInitalized) continue; - SaveClientObjectiveProgress(client, OldClientContract.m_sUUID, ClientContractObjective); + SetObjectiveProgressDatabase(steamid64, OldContract[client].m_sUUID, i, OldContractObjective.m_iProgress); } } // Get our Contract definition. - Contract ClientContract; - if (!CreateContractFromUUID(UUID, ClientContract)) + Contract NewContract; + if (!CreateContractFromUUID(UUID, NewContract)) { return ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID (%s) for client %d", UUID, client); } - ClientContract.m_bActive = true; - ClientContracts[client] = ClientContract; + NewContract.m_bActive = true; + ActiveContract[client] = NewContract; if (!IsFakeClient(client)) { @@ -529,7 +560,7 @@ public any Native_SetClientContract(Handle plugin, int numParams) // TODO: Can we make this into one query? // TODO: Implement version checking when required! "version" key in SQL char contract_query[1024]; - if (ClientContract.m_iContractType == Contract_ContractProgress) + if (ActiveContract[client].m_iContractType == Contract_ContractProgress) { g_DB.Format(contract_query, sizeof(contract_query), "SELECT * FROM contract_progress WHERE steamid64 = '%s' AND contract_uuid = '%s'", steamid64, UUID); @@ -538,26 +569,25 @@ public any Native_SetClientContract(Handle plugin, int numParams) char objective_query[1024]; g_DB.Format(objective_query, sizeof(objective_query), "SELECT * FROM objective_progress WHERE steamid64 = '%s' AND contract_uuid = '%s' AND (objective_id BETWEEN 0 AND %d) ORDER BY objective_id ASC;", - steamid64, ClientContract.m_sUUID, ClientContract.m_hObjectives.Length); + steamid64, ActiveContract[client].m_sUUID, ActiveContract[client].m_hObjectives.Length); g_DB.Query(CB_SetClientContract_Objective, objective_query, client); } - if (!dont_notify) - { - // Display the Contract to the client when we can. - CreateObjectiveDisplay(client, ClientContract, true); - CreateTimer(1.0, Timer_DisplayContractInfo, client, TIMER_REPEAT); - } + // Display the Contract to the client when we can. + CreateObjectiveDisplay(client, ActiveContract[client], true); + CreateTimer(1.0, Timer_DisplayContractInfo, client, TIMER_REPEAT); // Set this Contract as our current session. - if (!dont_save) - { - char steamid64[64]; - GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); - SetSessionDatabase(steamid64, ClientContract.m_sUUID); - } + char steamid64[64]; + GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); + SetSessionDatabase(steamid64, ActiveContract[client].m_sUUID); - LogMessage("[ZContracts] %N CONTRACT: Set Contract to: %s [ID: %s]", client, ClientContract.m_sContractName, ClientContract.m_sUUID); + Call_StartForward(g_fOnClientActivatedContract); + Call_PushCell(client); + Call_PushString(ActiveContract[client].m_sUUID); + Call_Finish(); + + LogMessage("[ZContracts] %N CONTRACT: Set Contract to: %s [ID: %s]", client, ActiveContract[client].m_sContractName, ActiveContract[client].m_sUUID); // Reset our current directory in the Contracker. g_Menu_CurrentDirectory[client] = "root"; @@ -566,67 +596,101 @@ public any Native_SetClientContract(Handle plugin, int numParams) return true; } -public any Native_SetClientContractStruct(Handle plugin, int numParams) +/** + * Set a client's contract (with extended functionality) + * + * @param client Client index. + * @param UUID The UUID of the contract. + * @param dont_save Optional argument: doesn't save this as the active Contract in the database. + * @param dont_notify Optional argument: don't notify the player that we've set their contract. + * @error Client index is invalid or UUID is invalid. + */ +public any Native_SetClientContractEx(Handle plugin, int numParams) { int client = GetNativeCell(1); - Contract NewContract; - GetNativeArray(2, NewContract, sizeof(Contract)); - bool dont_save = GetNativeCell(3); - bool dont_notify = GetNativeCell(4); + char UUID[MAX_UUID_SIZE]; + GetNativeString(2, UUID, sizeof(UUID)); - // Are we a bot? if (!IsClientValid(client)) { return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); } - if (IsFakeClient(client) && !g_BotContracts.BoolValue) return false; - if (NewContract.m_sUUID[0] != '{') + + if (UUID[0] != '{') { - return ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID passed. (%s)", NewContract.m_sUUID); + ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID passed. (%s)", UUID); } // If we have a Contract already selected, save it's progress to the database. - Contract OldClientContract; - GetClientContract(client, OldClientContract); - OldClientContract.m_bActive = false; - OldClientContracts[client] = OldClientContract; - - if (OldClientContract.IsContractInitalized() && - !OldClientContract.IsContractComplete() && - // weird edge case here with bmod implementation: - !StrEqual(OldClientContract.m_sUUID, NewContract.m_sUUID) && + ActiveContract[client].m_bActive = false; + OldContract[client] = ActiveContract[client]; + + if (OldContract[client].IsContractInitalized() && + !OldContract[client].IsContractComplete() && + !StrEqual(OldContract[client].m_sUUID, UUID) && g_DB != null) { - SaveClientContractProgress(client, OldClientContract); - for (int i = 0; i < OldClientContract.m_hObjectives.Length; i++) + char steamid64[64]; + GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); + SetContractProgressDatabase(steamid64, OldContract[client].m_sUUID, OldContract[client].m_iProgress); + for (int i = 0; i < OldContract[client].m_hObjectives.Length; i++) { - ContractObjective ClientContractObjective; - OldClientContract.GetObjective(i, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; + ContractObjective OldContractObjective; + OldContract[client].GetObjective(i, OldContractObjective); + if (!OldContractObjective.m_bInitalized) continue; - SaveClientObjectiveProgress(client, OldClientContract.m_sUUID, ClientContractObjective); + SetObjectiveProgressDatabase(steamid64, OldContract[client].m_sUUID, i, OldContractObjective.m_iProgress); } } - NewContract.m_bActive = true; - ClientContracts[client] = NewContract; - LogMessage("[ZContracts] %N CONTRACT: Set Contract to: %s [ID: %s]", client, NewContract.m_sContractName, NewContract.m_sUUID); - - if (!dont_notify) + // Get our Contract definition. + Contract NewContract; + if (!CreateContractFromUUID(UUID, NewContract)) { - // Display the Contract to the client when we can. - CreateObjectiveDisplay(client, NewContract, false); + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID (%s) for client %d", UUID, client); } + NewContract.m_bActive = true; + ActiveContract[client] = NewContract; - // Set this Contract as our current session. - if (!dont_save && g_DB != null) + if (!IsFakeClient(client)) { + // Get the client's SteamID64. char steamid64[64]; GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); - SetSessionDatabase(steamid64, NewContract.m_sUUID); + + // TODO: Can we make this into one query? + // TODO: Implement version checking when required! "version" key in SQL + char contract_query[1024]; + if (ActiveContract[client].m_iContractType == Contract_ContractProgress) + { + g_DB.Format(contract_query, sizeof(contract_query), + "SELECT * FROM contract_progress WHERE steamid64 = '%s' AND contract_uuid = '%s'", steamid64, UUID); + g_DB.Query(CB_SetClientContract_Contract, contract_query, client); + } + char objective_query[1024]; + g_DB.Format(objective_query, sizeof(objective_query), + "SELECT * FROM objective_progress WHERE steamid64 = '%s' AND contract_uuid = '%s' AND (objective_id BETWEEN 0 AND %d) ORDER BY objective_id ASC;", + steamid64, ActiveContract[client].m_sUUID, ActiveContract[client].m_hObjectives.Length); + g_DB.Query(CB_SetClientContract_Objective, objective_query, client); } + // Display the Contract to the client when we can. + CreateObjectiveDisplay(client, ActiveContract[client], true); + CreateTimer(1.0, Timer_DisplayContractInfo, client, TIMER_REPEAT); + + Call_StartForward(g_fOnClientActivatedContract); + Call_PushCell(client); + Call_PushString(ActiveContract[client].m_sUUID); + Call_Finish(); + + // Set this Contract as our current session. + char steamid64[64]; + GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); + SetSessionDatabase(steamid64, ActiveContract[client].m_sUUID); + + LogMessage("[ZContracts] %N CONTRACT: Set Contract to: %s [ID: %s]", client, ActiveContract[client].m_sContractName, ActiveContract[client].m_sUUID); + // Reset our current directory in the Contracker. g_Menu_CurrentDirectory[client] = "root"; g_Menu_DirectoryDeepness[client] = 1; @@ -634,18 +698,325 @@ public any Native_SetClientContractStruct(Handle plugin, int numParams) return true; } +/** + * Grabs the progress of the clients active Contract. + * + * @param client Client index. + * @return Progress value. If the client does not have an active Contract, -1 is returned. + * @error Invalid client index. + */ +public any Native_GetActiveContractProgress(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + // Return progress. + if (ActiveContract[client].IsContractInitalized()) + { + return ActiveContract[client].m_iProgress; + } + else + { + return -1; + } +} + +/** + * Grabs the progress of an Objective from the clients active Contract. + * + * @param client Client index. + * @param objective Objective ID. + * @return Progress value. If the client does not have an active Contract, -1 is returned. + * @error Invalid client or objective index. + */ +public any Native_GetActiveObjectiveProgress(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + if (ActiveContract[client].IsContractInitalized()) + { + int objective = GetNativeCell(2); + if (objective > ActiveContract[client].m_hObjectives.Length) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid objective index (UUID: %s, ID: %d).", + ActiveContract[client].m_sUUID, client); + } + + ContractObjective ActiveContractObjective; + ActiveContract[client].GetObjective(objective, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) return -1; + if (ActiveContractObjective.m_bInfinite) return 0; + + return ActiveContractObjective.m_iProgress; + } + else + { + return -1; + } +} + +/** + * Sets the progress of a clients active Contract. This does not automatically + * save the progress to the database (see SaveActiveContractToDatabase). + * + * @param client Client index. + * @param value New progress value. + * @error Invalid client index. + */ +public any Native_SetActiveContractProgress(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + // Set progress. + if (ActiveContract[client].IsContractInitalized()) + { + ActiveContract[client].m_iProgress = GetNativeCell(2); + } + + return 0; +} + +/** + * Sets the progress of an objective in a clients active Contract. This does not automatically + * save the progress to the database (see SaveActiveObjectiveToDatabase). + * + * @param client Client index. + * @param objective Objective ID. + * @param value New progress value. + * @error Invalid client or objective index. + */ +public any Native_SetActiveObjectiveProgress(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + if (ActiveContract[client].IsContractInitalized()) + { + int objective = GetNativeCell(2); + if (objective > ActiveContract[client].m_hObjectives.Length) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid objective index (UUID: %s, ID: %d).", + ActiveContract[client].m_sUUID, client); + } + + ContractObjective ActiveContractObjective; + ActiveContract[client].GetObjective(objective, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) return 0; + if (ActiveContractObjective.m_bInfinite) return 0; + + ActiveContractObjective.m_iProgress = GetNativeCell(3); + ActiveContract[client].SaveObjective(objective, ActiveContractObjective); + } + + return 0; +} + +/** + * Returns a list of all completed contracts. + * + * @param client Client index. + * @return StringMap sorted by UUID as key and completion data as the info. + * @note This function is partially unsafe as enum structs are still used inside. + * @error Invalid client index. + */ +public any Native_GetClientCompletedContracts(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + return CompletedContracts[client].Clone(); +} + +/** + * Checks to see if the client can activate a Contract. + * + * @param client Client index. + * @param UUID UUID of Contract to check. + * @return True if the client can activate a contract, false otherwise. + * @error Invalid client index or invalid UUID. + */ +public any Native_CanClientActivateContract(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + char UUID[MAX_UUID_SIZE]; + GetNativeString(2, UUID, sizeof(UUID)); + + if (g_DebugUnlockContracts.BoolValue) return true; + + // Grab the Contract from the schema. + if (g_ContractSchema.JumpToKey(UUID)) + { + // Construct the required contracts. + if (g_ContractSchema.JumpToKey("required_contracts", false)) + { + int Value = 0; + for (;;) + { + char ContractUUID[MAX_UUID_SIZE]; + char ValueStr[4]; + IntToString(Value, ValueStr, sizeof(ValueStr)); + + g_ContractSchema.GetString(ValueStr, ContractUUID, sizeof(ContractUUID), "{}"); + // If we reach a blank UUID, we're at the end of the list. + if (StrEqual("{}", ContractUUID)) break; + if (CompletedContracts[client].ContainsKey(ContractUUID)) + { + g_ContractSchema.Rewind(); + return true; + } + Value++; + } + g_ContractSchema.GoBack(); + } + else + { + g_ContractSchema.Rewind(); + return true; + } + } + g_ContractSchema.Rewind(); + return false; +} + +/** + * Checks to see if the client can complete a Contract at the current time. + * + * @param client Client index. + * @param UUID UUID of Contract to check. + * @return True if the client can complete the contract, false otherwise. + * @error Invalid client index or invalid UUID. + */ +public any Native_CanClientCompleteContract(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + char UUID[MAX_UUID_SIZE]; + GetNativeString(2, UUID, sizeof(UUID)); + if (UUID[0] != '{') + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID structure (%s).", UUID); + } + + KeyValues Schema = GetContractSchema(UUID); + + // Map check. + char Map[256]; + GetCurrentMap(Map, sizeof(Map)); + char MapRestriction[256]; + Schema.GetString("map_restriction", MapRestriction, sizeof(MapRestriction)); + if (!StrEqual(Map, "") && StrContains(Map, MapRestriction) == -1) return false; + + // Team check. + char TeamString[64]; + Schema.GetString("team_restriction", TeamString, sizeof(TeamString)); + int TeamRestriction = GetTeamFromSchema(TeamString); + if (TeamRestriction != -1 && GetClientTeam(client) != TeamRestriction) return false; + + // Weapon check. + char WeaponClassnameRestriction[64]; + Schema.GetString("weapon_classname_restriction", WeaponClassnameRestriction, sizeof(WeaponClassnameRestriction)); + if (/*!StrEqual("", this.m_sWeaponItemDefRestriction) + || */!StrEqual("", WeaponClassnameRestriction)) + { + int ClientWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon"); + if (IsValidEntity(ClientWeapon)) + { + // Classname check: + if (!StrEqual("", WeaponClassnameRestriction)) + { + char Classname[64]; + GetEntityClassname(ClientWeapon, Classname, sizeof(Classname)); + if (!StrContains(Classname, WeaponClassnameRestriction)) return false; + } + } + } + + return true; +} + +/** + * Checks to see if a client has already completed a Contract. + * + * @param client Client index. + * @param UUID UUID of Contract to check. + * @return True if the client has completed the contract, false otherwise. + * @error Invalid client index or invalid UUID. + */ +public any Native_HasClientCompletedContract(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + char UUID[MAX_UUID_SIZE]; + GetNativeString(2, UUID, sizeof(UUID)); + if (UUID[0] != '{') + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid UUID structure (%s).", UUID); + } + return CompletedContracts[client].ContainsKey(UUID); +} + +/** + * Checks to see if the client has completed their active Contract. + * + * @param client Client index. + * @return True if a client has completed their active Contract. + * False if the client has not finished their contract or has no contract active. + * @error Invalid client index. + */ +public any Native_IsActiveContractComplete(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + if (!IsClientValid(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d).", client); + } + + return ActiveContract[client].IsContractComplete(); +} + // Function for event timers. public Action Timer_DisplayContractInfo(Handle hTimer, int client) { static int Attempts = 0; - - // Get our contracts. - Contract ClientContract; - GetClientContract(client, ClientContract); - if (ClientContract.m_bLoadedFromDatabase || Attempts >= 3) + if (ActiveContract[client].m_bLoadedFromDatabase || Attempts >= 3) { - CreateObjectiveDisplay(client, ClientContract, false); + Call_StartForward(g_fOnClientActivatedContractPost); + Call_PushCell(client); + Call_PushString(ActiveContract[client].m_sUUID); + Call_Finish(); + + CreateObjectiveDisplay(client, ActiveContract[client], false); return Plugin_Stop; } @@ -701,7 +1072,7 @@ public void CB_GetContractFromLastSession(Database db, DBResultSet results, cons while (results.FetchRow()) { results.FetchString(0, UUID, sizeof(UUID)); - SetClientContract(client, UUID, true); + SetClientContract(client, UUID); } } @@ -717,18 +1088,16 @@ public Action Timer_DrawContrackerHud(Handle hTimer) if (g_NextHUDUpdate[i] > GetGameTime()) continue; if (!PlayerHUDEnabled[i]) continue; - Contract ClientContract; - GetClientContract(i, ClientContract); - if (!ClientContract.IsContractInitalized() || !ClientContract.m_bActive) continue; + if (!ActiveContract[i].IsContractInitalized() || !ActiveContract[i].m_bActive) continue; // Prepare our text. SetHudTextParams(1.0, -1.0, HUD_REFRESH_RATE + 0.1, 255, 255, 255, 255); char DisplayText[512] = "\"%s\":\n"; - Format(DisplayText, sizeof(DisplayText), DisplayText, ClientContract.m_sContractName); + Format(DisplayText, sizeof(DisplayText), DisplayText, ActiveContract[i].m_sContractName); // Add the amount of completions. CompletedContractInfo info; - CompletedContracts[i].GetArray(ClientContract.m_sUUID, info, sizeof(CompletedContractInfo)); + CompletedContracts[i].GetArray(ActiveContract[i].m_sUUID, info, sizeof(CompletedContractInfo)); if (PlayerHUDRepeatEnabled[i] && g_DisplayRepeatsInHUD.BoolValue && info.m_iCompletions > 0) { @@ -739,12 +1108,12 @@ public Action Timer_DrawContrackerHud(Handle hTimer) } // Add text if we've completed the Contract. - if (ClientContract.IsContractComplete()) + if (ActiveContract[i].IsContractComplete()) { char CompleteText[] = "CONTRACT COMPLETE - Type /c to\nselect a new Contract."; StrCat(DisplayText, sizeof(DisplayText), CompleteText); } - else if (!ClientContract.IsContractCompletableForClient(i)) + else if (!ActiveContract[i].IsContractCompletableForClient(i)) { char WarningText[] = "This Contract cannot be completed.\nType /c to select a new Contract."; StrCat(DisplayText, sizeof(DisplayText), WarningText); @@ -752,21 +1121,21 @@ public Action Timer_DrawContrackerHud(Handle hTimer) else { // Display the overall Contract progress. - if (ClientContract.m_iContractType == Contract_ContractProgress) + if (ActiveContract[i].m_iContractType == Contract_ContractProgress) { char ProgressText[128] = "Progress: [%d/%d]"; Format(ProgressText, sizeof(ProgressText), ProgressText, - ClientContract.m_iProgress, ClientContract.m_iMaxProgress); + ActiveContract[i].m_iProgress, ActiveContract[i].m_iMaxProgress); // Adds +xCP value to the end of the text. - if (ClientContract.m_bHUD_ContractUpdate) + if (ActiveContract[i].m_bHUD_ContractUpdate) { SetHudTextParams(1.0, -1.0, 1.0, 52, 235, 70, 255, 1); char AddText[16] = " +%dCP"; - Format(AddText, sizeof(AddText), AddText, ClientContract.m_iHUD_UpdateValue); + Format(AddText, sizeof(AddText), AddText, ActiveContract[i].m_iHUD_UpdateValue); StrCat(ProgressText, sizeof(ProgressText), AddText); - ClientContract.m_bHUD_ContractUpdate = false; - ClientContract.m_iHUD_UpdateValue = 0; + ActiveContract[i].m_bHUD_ContractUpdate = false; + ActiveContract[i].m_iHUD_UpdateValue = 0; g_NextHUDUpdate[i] = GetGameTime() + 1.0; } @@ -775,33 +1144,33 @@ public Action Timer_DrawContrackerHud(Handle hTimer) StrCat(DisplayText, sizeof(DisplayText), ProgressText); } - bool DisplaySavingText = ClientContract.m_bNeedsDBSave; + bool DisplaySavingText = ActiveContract[i].m_bNeedsDBSave; // Add our objectives to HUD display. int DisplayID = 1; - for (int j = 0; j < ClientContract.m_hObjectives.Length; j++) + for (int j = 0; j < ActiveContract[i].m_hObjectives.Length; j++) { - ContractObjective ClientContractObjective; - ClientContract.GetObjective(j, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; - if (ClientContractObjective.m_bInfinite) continue; - if (ClientContractObjective.IsObjectiveComplete()) continue; + ContractObjective ActiveContractObjective; + ActiveContract[i].GetObjective(j, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) continue; + if (ActiveContractObjective.m_bInfinite) continue; + if (ActiveContractObjective.IsObjectiveComplete()) continue; - if (ClientContractObjective.m_bNeedsDBSave) DisplaySavingText = true; + if (ActiveContractObjective.m_bNeedsDBSave) DisplaySavingText = true; char ObjectiveText[64] = "#%d: [%d/%d]"; Format(ObjectiveText, sizeof(ObjectiveText), ObjectiveText, - DisplayID, ClientContractObjective.m_iProgress, ClientContractObjective.m_iMaxProgress); + DisplayID, ActiveContractObjective.m_iProgress, ActiveContractObjective.m_iMaxProgress); // Adds +x value to the end of the text. - if (ClientContract.m_iHUD_ObjectiveUpdate == ClientContractObjective.m_iInternalID) + if (ActiveContract[i].m_iHUD_ObjectiveUpdate == ActiveContractObjective.m_iInternalID) { SetHudTextParams(1.0, -1.0, 1.0, 52, 235, 70, 255, 1); char AddText[16] = " +%d"; - Format(AddText, sizeof(AddText), AddText, ClientContract.m_iHUD_UpdateValue); + Format(AddText, sizeof(AddText), AddText, ActiveContract[i].m_iHUD_UpdateValue); StrCat(ObjectiveText, sizeof(ObjectiveText), AddText); - ClientContract.m_bHUD_ContractUpdate = false; - ClientContract.m_iHUD_ObjectiveUpdate = -1; + ActiveContract[i].m_bHUD_ContractUpdate = false; + ActiveContract[i].m_iHUD_ObjectiveUpdate = -1; g_NextHUDUpdate[i] = GetGameTime() + 1.0; } @@ -810,10 +1179,10 @@ public Action Timer_DrawContrackerHud(Handle hTimer) char TimerText[16] = " [TIME: %ds]"; // Display a timer if we have one active. - for (int k = 0; k < ClientContractObjective.m_hEvents.Length; k++) + for (int k = 0; k < ActiveContractObjective.m_hEvents.Length; k++) { ContractObjectiveEvent ObjEvent; - ClientContractObjective.m_hEvents.GetArray(k, ObjEvent); + ActiveContractObjective.m_hEvents.GetArray(k, ObjEvent); // Do we have a timer going? if (ObjEvent.m_hTimer != INVALID_HANDLE) @@ -837,9 +1206,6 @@ public Action Timer_DrawContrackerHud(Handle hTimer) } // Display text to client. ShowSyncHudText(i, g_HudSync, DisplayText); - - // Just in case we modified the Contract earlier, resave it. - ClientContracts[i] = ClientContract; } return Plugin_Continue; @@ -890,25 +1256,25 @@ public Action Timer_ProcessEvents(Handle hTimer) } continue; } - - // Grab the objective from our client's contract. - Contract ClientContract; - GetClientContract(client, ClientContract); // Do our UUID's match? - if (!StrEqual(uuid, ClientContract.m_sUUID) && StrEqual(uuid, OldClientContracts[client].m_sUUID)) + if (!StrEqual(uuid, ActiveContract[client].m_sUUID) && StrEqual(uuid, OldContract[client].m_sUUID)) { - ProcessLogicForContractObjective(OldClientContracts[client], objective_id, client, event, value); + ProcessLogicForContractObjective(OldContract[client], objective_id, client, event, value); // Get the new progress and completion status for the old contract. ContractObjective OldObjective; - OldClientContracts[client].GetObjective(objective_id, OldObjective); - SaveClientObjectiveProgress(client, OldClientContracts[client].m_sUUID, OldObjective); + OldContract[client].GetObjective(objective_id, OldObjective); + + char steamid64[64]; + GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64)); + + SetObjectiveProgressDatabase(steamid64, OldContract[client].m_sUUID, objective_id, OldObjective.m_iProgress); } else { // Update progress. - ProcessLogicForContractObjective(ClientContract, objective_id, client, event, value); + ProcessLogicForContractObjective(ActiveContract[client], objective_id, client, event, value); } iProcessed++; @@ -946,10 +1312,9 @@ void ProcessLogicForContractObjective(Contract ClientContract, int objective_id, Call_StartForward(g_fOnProcessContractLogic); Call_PushCell(client); Call_PushString(ClientContract.m_sUUID); + Call_PushCell(objective_id); Call_PushString(event); Call_PushCell(value); - Call_PushArray(ClientContract, sizeof(Contract)); - Call_PushArray(Objective, sizeof(ContractObjective)); bool ShouldBlock = false; Call_Finish(ShouldBlock); @@ -1044,7 +1409,6 @@ void ProcessLogicForContractObjective(Contract ClientContract, int objective_id, Objective.m_hEvents.SetArray(i, ObjEvent); ClientContract.SaveObjective(objective_id, Objective); - ClientContracts[client] = ClientContract; } if (Objective.IsObjectiveComplete()) @@ -1058,12 +1422,12 @@ void ProcessLogicForContractObjective(Contract ClientContract, int objective_id, Call_StartForward(g_fOnObjectiveCompleted); Call_PushCell(client); Call_PushString(ClientContract.m_sUUID); - Call_PushArray(Objective, sizeof(ContractObjective)); + Call_PushCell(objective_id); Call_Finish(); if (g_DB != null) { - SaveClientObjectiveProgress(client, ClientContract.m_sUUID, Objective); + SaveActiveObjectiveToDatabase(client, objective_id); } break; @@ -1083,7 +1447,6 @@ void ProcessLogicForContractObjective(Contract ClientContract, int objective_id, Call_StartForward(g_fOnContractCompleted); Call_PushCell(client); Call_PushString(ClientContract.m_sUUID); - Call_PushArray(ClientContract, sizeof(Contract)); Call_Finish(); // Increment the amount of times we've completed this Contract. @@ -1099,14 +1462,14 @@ void ProcessLogicForContractObjective(Contract ClientContract, int objective_id, PrintColoredChatAll("%s[ZC]%s %N has completed the contract: %s\"%s\"%s, congratulations!", COLOR_LIGHTSEAGREEN, COLOR_DEFAULT, client, COLOR_YELLOW, ClientContract.m_sContractName, COLOR_DEFAULT); - SaveClientContractProgress(client, ClientContract); + SaveActiveContractToDatabase(client); info.m_bReset = false; } else // Delete all progress from database and reset the Contract. { DeleteContractProgressDatabase(steamid64, ClientContract.m_sUUID); DeleteAllObjectiveProgressDatabase(steamid64, ClientContract.m_sUUID); - SetClientContract(client, ClientContract.m_sUUID, true, true); + SetClientContractEx(client, ClientContract.m_sUUID, true, true); info.m_bReset = true; } @@ -1128,18 +1491,18 @@ void ProcessLogicForContractObjective(Contract ClientContract, int objective_id, * @param client Client index of who triggered the event. * @param value Value passed from CallContrackerEvent. * @param ClientContract Enum struct of the Contract to modify. - * @param ClientContractObjective Enum struct of the Contract Objective to modify. + * @param ActiveContractObjective Enum struct of the Contract Objective to modify. */ -void IncrementContractProgress(int client, int value, Contract ClientContract, ContractObjective ClientContractObjective) +void IncrementContractProgress(int client, int value, Contract ClientContract, ContractObjective ActiveContractObjective) { if (PlayerSoundsEnabled[client] == Sounds_Enabled) EmitGameSoundToClient(client, IncrementProgressSound); int AddValue = 0; // This award value will not be multiplied by the value argument. This may be useful for some Contracts. - if (ClientContract.m_bNoMultiplication) AddValue = ClientContractObjective.m_iAward; - else if (ClientContractObjective.m_bNoMultiplication) AddValue = ClientContractObjective.m_iAward; - else AddValue = ClientContractObjective.m_iAward * value; + if (ClientContract.m_bNoMultiplication) AddValue = ActiveContractObjective.m_iAward; + else if (ActiveContractObjective.m_bNoMultiplication) AddValue = ActiveContractObjective.m_iAward; + else AddValue = ActiveContractObjective.m_iAward * value; ClientContract.m_iProgress += AddValue; ClientContract.m_iProgress = Int_Min(ClientContract.m_iProgress, ClientContract.m_iMaxProgress); @@ -1149,29 +1512,29 @@ void IncrementContractProgress(int client, int value, Contract ClientContract, C // In Contract-Style progression, Objectives are only triggered // once at a time if they are not infinite. - if (!ClientContractObjective.m_bInfinite) + if (!ActiveContractObjective.m_bInfinite) { - ClientContractObjective.m_iProgress++; - ClientContractObjective.m_iProgress = Int_Min(ClientContractObjective.m_iProgress, ClientContractObjective.m_iMaxProgress); + ActiveContractObjective.m_iProgress++; + ActiveContractObjective.m_iProgress = Int_Min(ActiveContractObjective.m_iProgress, ActiveContractObjective.m_iMaxProgress); } // Print HINT text to chat. if (g_DisplayHudMessages.BoolValue && PlayerHintEnabled[client]) { char MessageText[256]; - if (ClientContractObjective.m_bNoMultiplication) + if (ActiveContractObjective.m_bNoMultiplication) { MessageText = "\"%s\" (%s [%d/%dCP]) +%dCP"; - PrintHintText(client, MessageText, ClientContractObjective.m_sDescription, + PrintHintText(client, MessageText, ActiveContractObjective.m_sDescription, ClientContract.m_sContractName, ClientContract.m_iProgress, - ClientContract.m_iMaxProgress, ClientContractObjective.m_iAward); + ClientContract.m_iMaxProgress, ActiveContractObjective.m_iAward); } else { MessageText = "\"%s\" %dx (%s [%d/%dCP]) +%dCP"; - PrintHintText(client, MessageText, ClientContractObjective.m_sDescription, + PrintHintText(client, MessageText, ActiveContractObjective.m_sDescription, value, ClientContract.m_sContractName, ClientContract.m_iProgress, - ClientContract.m_iMaxProgress, ClientContractObjective.m_iAward * value); + ClientContract.m_iMaxProgress, ActiveContractObjective.m_iAward * value); } } if (g_DebugProgress.BoolValue) @@ -1190,60 +1553,60 @@ void IncrementContractProgress(int client, int value, Contract ClientContract, C * @param client Client index of who triggered the event. * @param value Value passed from CallContrackerEvent. * @param ClientContract Enum struct of the Contract to modify. - * @param ClientContractObjective Enum struct of the Contract Objective to modify. + * @param ActiveContractObjective Enum struct of the Contract Objective to modify. */ -void IncrementObjectiveProgress(int client, int value, Contract ClientContract, ContractObjective ClientContractObjective) +void IncrementObjectiveProgress(int client, int value, Contract ClientContract, ContractObjective ActiveContractObjective) { if (PlayerSoundsEnabled[client] == Sounds_Enabled) EmitGameSoundToClient(client, IncrementProgressSound); int AddValue = 0; // This award value will not be multiplied by the value argument. This may be useful for some Contracts. - if (ClientContractObjective.m_bNoMultiplication) AddValue = ClientContractObjective.m_iAward; - else AddValue = ClientContractObjective.m_iAward * value; - ClientContractObjective.m_iProgress += AddValue; + if (ActiveContractObjective.m_bNoMultiplication) AddValue = ActiveContractObjective.m_iAward; + else AddValue = ActiveContractObjective.m_iAward * value; + ActiveContractObjective.m_iProgress += AddValue; // Update HUD. ClientContract.m_iHUD_UpdateValue = AddValue; ClientContract.m_bHUD_ContractUpdate = true; - ClientContract.m_iHUD_ObjectiveUpdate = ClientContractObjective.m_iInternalID; + ClientContract.m_iHUD_ObjectiveUpdate = ActiveContractObjective.m_iInternalID; // In Objective-Style progression, each Objective tracks its own progress // individually. If the Objective is not infinite, its progress will be clamped. // This allows for Contracts to consist of an Objective that can keep ticking up // and up forever. - if (!ClientContractObjective.m_bInfinite) + if (!ActiveContractObjective.m_bInfinite) { - ClientContractObjective.m_iProgress = Int_Min(ClientContractObjective.m_iProgress, ClientContractObjective.m_iMaxProgress); + ActiveContractObjective.m_iProgress = Int_Min(ActiveContractObjective.m_iProgress, ActiveContractObjective.m_iMaxProgress); } // Display HINT message to the client. if (g_DisplayHudMessages.BoolValue && PlayerHintEnabled[client]) { char MessageText[256]; - if (ClientContractObjective.m_bNoMultiplication) + if (ActiveContractObjective.m_bNoMultiplication) { MessageText = "\"%s\" (%s [%d/%d]) +%d"; - PrintHintText(client, MessageText, ClientContractObjective.m_sDescription, - ClientContract.m_sContractName, ClientContractObjective.m_iProgress, - ClientContractObjective.m_iMaxProgress, ClientContractObjective.m_iAward); + PrintHintText(client, MessageText, ActiveContractObjective.m_sDescription, + ClientContract.m_sContractName, ActiveContractObjective.m_iProgress, + ActiveContractObjective.m_iMaxProgress, ActiveContractObjective.m_iAward); } else { MessageText = "\"%s\" %dx (%s [%d/%d]) +%d"; - PrintHintText(client, MessageText, ClientContractObjective.m_sDescription, - value, ClientContract.m_sContractName, ClientContractObjective.m_iProgress, - ClientContractObjective.m_iMaxProgress, ClientContractObjective.m_iAward * value); + PrintHintText(client, MessageText, ActiveContractObjective.m_sDescription, + value, ClientContract.m_sContractName, ActiveContractObjective.m_iProgress, + ActiveContractObjective.m_iMaxProgress, ActiveContractObjective.m_iAward * value); } } if (g_DebugProgress.BoolValue) { LogMessage("[ZContracts] %N PROGRESS: Increment event triggered [ID: %s, OBJ: %d, CP: %d]", - client, ClientContract.m_sUUID, ClientContractObjective.m_iInternalID, ClientContractObjective.m_iProgress); + client, ClientContract.m_sUUID, ActiveContractObjective.m_iInternalID, ActiveContractObjective.m_iProgress); } // Save progress to DB. - ClientContractObjective.m_bNeedsDBSave = true; + ActiveContractObjective.m_bNeedsDBSave = true; } // ============ DEBUG FUNCTIONS ============ @@ -1279,7 +1642,7 @@ public Action DebugSetContract(int client, int args) for (int i = 0; i < target_count; i++) { - SetClientContract(client, UUID, false, false); + SetClientContract(client, UUID); } return Plugin_Handled; @@ -1291,10 +1654,7 @@ public Action DebugSetContract(int client, int args) * will print information about the Objective index. **/ public Action DebugContractInfo(int client, int args) -{ - Contract ClientContract; - GetClientContract(client, ClientContract); - +{ if (args == 0) { PrintToConsole(client, @@ -1309,8 +1669,9 @@ public Action DebugContractInfo(int client, int args) ... "Is Contract Complete: %d\n" ... "[INFO] To debug an objective, type sm_debugcontract [objective_index]" ... "---------------------------------------------", - ClientContract.IsContractInitalized(), ClientContract.m_sContractName, ClientContract.m_sUUID, ClientContract.m_sDirectoryPath, ClientContract.m_iContractType, - ClientContract.m_iProgress, ClientContract.m_iMaxProgress, ClientContract.m_hObjectives.Length, ClientContract.IsContractComplete()); + ActiveContract[client].IsContractInitalized(), ActiveContract[client].m_sContractName, ActiveContract[client].m_sUUID, + ActiveContract[client].m_sDirectoryPath, ActiveContract[client].m_iContractType, ActiveContract[client].m_iProgress, + ActiveContract[client].m_iMaxProgress, ActiveContract[client].m_hObjectives.Length, ActiveContract[client].IsContractComplete()); } if (args == 1) { @@ -1318,8 +1679,8 @@ public Action DebugContractInfo(int client, int args) GetCmdArg(1, sArg, sizeof(sArg)); int iID = StringToInt(sArg); - ContractObjective ClientContractObjective; - ClientContract.GetObjective(iID, ClientContractObjective); + ContractObjective ActiveContractObjective; + ActiveContract[client].GetObjective(iID, ActiveContractObjective); PrintToConsole(client, "---------------------------------------------\n" @@ -1337,11 +1698,11 @@ public Action DebugContractInfo(int client, int args) ... "Needs Database Save %d\n" ... "[INFO] To debug an objective, type sm_debugcontract [objective_index]\n" ... "---------------------------------------------", - ClientContract.m_sContractName, ClientContract.m_sUUID, ClientContract.m_iContractType, ClientContractObjective.m_bInitalized, - ClientContractObjective.m_iInternalID, ClientContractObjective.m_bInfinite, ClientContractObjective.m_iAward, - ClientContractObjective.m_iProgress, ClientContractObjective.m_iMaxProgress, - ClientContractObjective.m_hEvents.Length, ClientContractObjective.m_bNoMultiplication, - ClientContractObjective.IsObjectiveComplete(), ClientContractObjective.m_bNeedsDBSave); + ActiveContract[client].m_sContractName, ActiveContract[client].m_sUUID, ActiveContract[client].m_iContractType, ActiveContractObjective.m_bInitalized, + ActiveContractObjective.m_iInternalID, ActiveContractObjective.m_bInfinite, ActiveContractObjective.m_iAward, + ActiveContractObjective.m_iProgress, ActiveContractObjective.m_iMaxProgress, + ActiveContractObjective.m_hEvents.Length, ActiveContractObjective.m_bNoMultiplication, + ActiveContractObjective.IsObjectiveComplete(), ActiveContractObjective.m_bNeedsDBSave); } return Plugin_Handled; @@ -1499,18 +1860,17 @@ public Action DebugSaveContract(int client, int args) for (int i = 0; i < target_count; i++) { - Contract ClientContract; - GetClientContract(target_list[i], ClientContract); - if (!ClientContract.IsContractInitalized()) continue; + int user = target_list[i]; + if (!ActiveContract[user].IsContractInitalized()) continue; - SaveClientContractProgress(target_list[i], ClientContract); - for (int j = 0; j < ClientContract.m_hObjectives.Length; j++) + SaveActiveContractToDatabase(user); + for (int j = 0; j < ActiveContract[user].m_hObjectives.Length; j++) { - ContractObjective ClientContractObjective; - ClientContract.GetObjective(i, ClientContractObjective); - if (!ClientContractObjective.m_bInitalized) continue; + ContractObjective ActiveContractObjective; + ActiveContract[user].GetObjective(user, ActiveContractObjective); + if (!ActiveContractObjective.m_bInitalized) continue; - SaveClientObjectiveProgress(client, ClientContract.m_sUUID, ClientContractObjective); + SaveActiveObjectiveToDatabase(user, j); } } diff --git a/addons/sourcemod/scripting/zcontracts_test.sp b/addons/sourcemod/scripting/zcontracts_test.sp new file mode 100644 index 0000000..c1faad8 --- /dev/null +++ b/addons/sourcemod/scripting/zcontracts_test.sp @@ -0,0 +1,52 @@ +#include +#include + +public Plugin myinfo = +{ + name = "ZContracts - Testing", + author = "ZoNiCaL", + description = "", + version = "lol what", + url = "" +}; + +public void OnClientActivatedContractPost(int client, char UUID[MAX_UUID_SIZE]) +{ + PrintToChat(client, "Activated Contract %s", UUID); + + // Schema testing. + KeyValues ContractSchema = GetContractSchema(UUID); + + if (ContractSchema != INVALID_HANDLE) + { + char ContractName[MAX_CONTRACT_NAME_SIZE]; + ContractSchema.GetString("name", ContractName, sizeof(ContractName)); + PrintToChat(client, "Grabbed Contract \"%s\" schema successfully", ContractName); + } + + int ObjectiveCount = GetContractObjectiveCount(UUID); + PrintToChat(client, "Amount of objectives: %d", ObjectiveCount); + for (int i = 1; i < ObjectiveCount+1; i++) + { + KeyValues ObjectiveSchema = GetObjectiveSchema(UUID, i); + if (ObjectiveSchema != INVALID_HANDLE) + { + char ObjectiveDesc[MAX_OBJECTIVE_DESC_SIZE]; + ObjectiveSchema.GetString("description", ObjectiveDesc, sizeof(ObjectiveDesc)); + PrintToChat(client, "Grabbed Objective %d \"%s\" schema successfully", i, ObjectiveDesc); + } + } + PrintToChat(client, "Is active contract complete: %d", IsActiveContractComplete(client)); + PrintToChat(client, "Main Contract Progress: %d", GetActiveContractProgress(client)); + + for (int i = 0; i < ObjectiveCount-1; i++) + { + PrintToChat(client, "Objective %d Progress: %d", i, GetActiveObjectiveProgress(client, i+1)); + } + + StringMap CompletedContracts = GetClientCompletedContracts(client); + PrintToChat(client, "Completed Contracts: %d", CompletedContracts.Size); + PrintToChat(client, "Can activate this contract: %d", CanClientActivateContract(client, UUID)); + PrintToChat(client, "Can complete contract: %d", CanClientCompleteContract(client, UUID)); + PrintToChat(client, "Has client completed contract: %d", HasClientCompletedContract(client, UUID)); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/zcontracts_tf2.sp b/addons/sourcemod/scripting/zcontracts_tf2.sp index 793db5a..a7df3c8 100644 --- a/addons/sourcemod/scripting/zcontracts_tf2.sp +++ b/addons/sourcemod/scripting/zcontracts_tf2.sp @@ -141,20 +141,45 @@ public any Native_GetTF2GameModeExt(Handle plugin, int numParams) // ============================= LOGIC ============================= -public Action OnProcessContractLogic(int client, char UUID[MAX_UUID_SIZE], char event[MAX_EVENT_SIZE], -int value, Contract ClientContract, ContractObjective ClientContractObjective) +public Action OnProcessContractLogic(int client, char UUID[MAX_UUID_SIZE], int objective, +char event[MAX_EVENT_SIZE], int value) { if (GetGameTime() >= g_LastValidProgressTime && g_LastValidProgressTime != -1.0) return Plugin_Stop; - // Class check. - TFClassType Class = TF2_GetPlayerClass(client); - if (Class == TFClass_Unknown) return Plugin_Stop; - if (!ClientContract.m_bClass[Class]) return Plugin_Stop; + KeyValues ContractSchema = GetContractSchema(UUID); + + if (ContractSchema.JumpToKey("classes")) + { + // Class check. + TFClassType Class = TF2_GetPlayerClass(client); + if (Class == TFClass_Unknown) return Plugin_Stop; + + // Construct temporary array. + bool ClassCheckArray[10]; + ClassCheckArray[TFClass_Scout] = view_as(ContractSchema.GetNum("scout", 0)); + ClassCheckArray[TFClass_Soldier] = view_as(ContractSchema.GetNum("soldier", 0)); + ClassCheckArray[TFClass_Pyro] = view_as(ContractSchema.GetNum("pyro", 0)); + ClassCheckArray[TFClass_DemoMan] = view_as(ContractSchema.GetNum("demoman", 0)); + ClassCheckArray[TFClass_Heavy] = view_as(ContractSchema.GetNum("heavy", 0)); + ClassCheckArray[TFClass_Engineer] = view_as(ContractSchema.GetNum("engineer", 0)); + ClassCheckArray[TFClass_Sniper] = view_as(ContractSchema.GetNum("sniper", 0)); + ClassCheckArray[TFClass_Medic] = view_as(ContractSchema.GetNum("medic", 0)); + ClassCheckArray[TFClass_Spy] = view_as(ContractSchema.GetNum("spy", 0)); + + if (!ClassCheckArray[Class]) return Plugin_Stop; + ContractSchema.Rewind(); + } // Gamemode extension check. - if (!TF2_ValidGameRulesEntityExists(ClientContract.m_sRequiredGameRulesEntity)) return Plugin_Stop; - if ((ClientContract.m_iGameTypeRestriction != view_as(TGE_NoExtension)) - && (ClientContract.m_iGameTypeRestriction != view_as(g_TF2_GameModeExtension))) return Plugin_Stop; + char RequiredGameRulesEnt[64]; + ContractSchema.GetString("required_gamerules", RequiredGameRulesEnt, sizeof(RequiredGameRulesEnt)); + if (!StrEqual(RequiredGameRulesEnt, "")) + { + if (!TF2_ValidGameRulesEntityExists(RequiredGameRulesEnt)) return Plugin_Stop; + } + + if ((ContractSchema.GetNum("gamemode_extension", view_as(TGE_NoExtension)) != view_as(TGE_NoExtension)) + && (ContractSchema.GetNum("gamemode_extension", view_as(TGE_NoExtension)) != view_as(g_TF2_GameModeExtension))) return Plugin_Stop; // All good! :) return Plugin_Continue;