Added XP Chart & LA Buy-off journal entries

Added XP Chart & LA Buy-off journal entries.  Added release folder.  Full compile.
This commit is contained in:
Jaysyn904
2023-09-25 17:12:44 -04:00
parent e0ec0015e6
commit daac612685
63 changed files with 228958 additions and 39 deletions

View File

@@ -0,0 +1,4 @@
void main()
{
ExecuteScript("prc_cutabort", OBJECT_SELF);
}

View File

@@ -0,0 +1,4 @@
void main()
{
ExecuteScript("prc_onplayerchat", OBJECT_SELF);
}

137
_module/nss/nwnx.nss Normal file
View File

@@ -0,0 +1,137 @@
/// @addtogroup nwnx NWNX
/// @brief Functions for plugin developers.
/// @{
/// @file nwnx.nss
const string NWNX_Core = "NWNX_Core"; ///< @private
/// @brief Scripting interface to NWNX.
/// @param pluginName The plugin name.
/// @param functionName The function name (do not include NWNX_Plugin_).
void NWNX_CallFunction(string pluginName, string functionName);
/// @brief Pushes the specified type to the c++ side
/// @param value The value of specified type to push.
void NWNX_PushArgumentInt(int value);
/// @copydoc NWNX_PushArgumentInt()
void NWNX_PushArgumentFloat(float value);
/// @copydoc NWNX_PushArgumentInt()
void NWNX_PushArgumentObject(object value);
/// @copydoc NWNX_PushArgumentInt()
void NWNX_PushArgumentString(string value);
/// @copydoc NWNX_PushArgumentInt()
void NWNX_PushArgumentEffect(effect value);
/// @copydoc NWNX_PushArgumentInt()
void NWNX_PushArgumentItemProperty(itemproperty value);
/// @copydoc NWNX_PushArgumentInt()
void NWNX_PushArgumentJson(json value);
/// @brief Returns the specified type from the c++ side
/// @return The value of specified type.
int NWNX_GetReturnValueInt();
/// @copydoc NWNX_GetReturnValueInt()
float NWNX_GetReturnValueFloat();
/// @copydoc NWNX_GetReturnValueInt()
object NWNX_GetReturnValueObject();
/// @copydoc NWNX_GetReturnValueInt()
string NWNX_GetReturnValueString();
/// @copydoc NWNX_GetReturnValueInt()
effect NWNX_GetReturnValueEffect();
/// @copydoc NWNX_GetReturnValueInt()
itemproperty NWNX_GetReturnValueItemProperty();
/// @copydoc NWNX_GetReturnValueInt()
json NWNX_GetReturnValueJson();
/// @brief Determines if the given plugin exists and is enabled.
/// @param sPlugin The name of the plugin to check. This is the case sensitive plugin name as used by NWNX_CallFunction, NWNX_PushArgumentX
/// @note Example usage: NWNX_PluginExists("NWNX_Creature");
/// @return TRUE if the plugin exists and is enabled, otherwise FALSE.
int NWNX_PluginExists(string sPlugin);
/// @private
const string NWNX_PUSH = "NWNXEE!ABIv2!X!Y!PUSH";
const string NWNX_POP = "NWNXEE!ABIv2!X!Y!POP";
/// @}
void NWNX_CallFunction(string pluginName, string functionName)
{
PlaySound("NWNXEE!ABIv2!" + pluginName + "!" + functionName + "!CALL");
}
void NWNX_PushArgumentInt(int value)
{
SetLocalInt(OBJECT_INVALID, NWNX_PUSH, value);
}
void NWNX_PushArgumentFloat(float value)
{
SetLocalFloat(OBJECT_INVALID, NWNX_PUSH, value);
}
void NWNX_PushArgumentObject(object value)
{
SetLocalObject(OBJECT_INVALID, NWNX_PUSH, value);
}
void NWNX_PushArgumentString(string value)
{
SetLocalString(OBJECT_INVALID, NWNX_PUSH, value);
}
void NWNX_PushArgumentEffect(effect value)
{
TagEffect(value, NWNX_PUSH);
}
void NWNX_PushArgumentItemProperty(itemproperty value)
{
TagItemProperty(value, NWNX_PUSH);
}
void NWNX_PushArgumentJson(json value)
{
SetLocalJson(OBJECT_INVALID, NWNX_PUSH, value);
}
int NWNX_GetReturnValueInt()
{
return GetLocalInt(OBJECT_INVALID, NWNX_POP);
}
float NWNX_GetReturnValueFloat()
{
return GetLocalFloat(OBJECT_INVALID, NWNX_POP);
}
object NWNX_GetReturnValueObject()
{
return GetLocalObject(OBJECT_INVALID, NWNX_POP);
}
string NWNX_GetReturnValueString()
{
return GetLocalString(OBJECT_INVALID, NWNX_POP);
}
effect NWNX_GetReturnValueEffect()
{
effect e;
return TagEffect(e, NWNX_POP);
}
itemproperty NWNX_GetReturnValueItemProperty()
{
itemproperty ip;
return TagItemProperty(ip, NWNX_POP);
}
json NWNX_GetReturnValueJson()
{
return GetLocalJson(OBJECT_INVALID, NWNX_POP);
}
int NWNX_PluginExists(string sPlugin)
{
string sFunc = "PluginExists";
NWNX_PushArgumentString(sPlugin);
NWNX_CallFunction(NWNX_Core, sFunc);
return NWNX_GetReturnValueInt();
}

158
_module/nss/nwnx_chat.nss Normal file
View File

@@ -0,0 +1,158 @@
/// @addtogroup chat Chat
/// @brief Functions related to chat.
/// @{
/// @file nwnx_chat.nss
#include "nwnx"
const string NWNX_Chat = "NWNX_Chat"; ///< @private
/// @name Chat Channels
/// @anchor chat_channels
///
/// Constants defining the various chat channels.
/// @{
const int NWNX_CHAT_CHANNEL_PLAYER_TALK = 1;
const int NWNX_CHAT_CHANNEL_PLAYER_SHOUT = 2;
const int NWNX_CHAT_CHANNEL_PLAYER_WHISPER = 3;
const int NWNX_CHAT_CHANNEL_PLAYER_TELL = 4;
const int NWNX_CHAT_CHANNEL_SERVER_MSG = 5;
const int NWNX_CHAT_CHANNEL_PLAYER_PARTY = 6;
const int NWNX_CHAT_CHANNEL_PLAYER_DM = 14;
const int NWNX_CHAT_CHANNEL_DM_TALK = 17;
const int NWNX_CHAT_CHANNEL_DM_SHOUT = 18;
const int NWNX_CHAT_CHANNEL_DM_WHISPER = 19;
const int NWNX_CHAT_CHANNEL_DM_TELL = 20;
const int NWNX_CHAT_CHANNEL_DM_PARTY = 22;
const int NWNX_CHAT_CHANNEL_DM_DM = 30;
/// @}
/// @brief Sends a chat message.
/// @remark If no target is provided, then it broadcasts to all eligible targets.
/// @param channel The @ref chat_channels "channel" to send the message.
/// @param message The message to send.
/// @param sender The sender of the message.
/// @param target The receiver of the message.
/// @return TRUE if successful, FALSE otherwise.
int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SELF, object target = OBJECT_INVALID);
/// @brief Registers the script which receives all chat messages.
/// @note If a script was previously registered, this one will take over.
/// @param script The script name to handle the chat events.
void NWNX_Chat_RegisterChatScript(string script);
/// @brief Skips a chat message
/// @note Must be called from a chat or system script handler.
void NWNX_Chat_SkipMessage();
/// @brief Gets the chat @ref chat_channels "channel".
/// @note Must be called from a chat or system script handler.
/// @return The @ref chat_channels "channel" the message is sent.
int NWNX_Chat_GetChannel();
/// @brief Gets the message.
/// @note Must be called from a chat or system script handler.
/// @return The message sent.
string NWNX_Chat_GetMessage();
/// @brief Gets the sender of the message.
/// @note Must be called from a chat or system script handler.
/// @return The object sending the message.
object NWNX_Chat_GetSender();
/// @brief Gets the target of the message.
/// @note Must be called from an chat or system script handler.
/// @return The target of the message or OBJECT_INVALID if no target.
object NWNX_Chat_GetTarget();
/// @brief Sets the distance with which the player hears talks or whispers.
/// @remark Per player settings override server wide.
/// @param distance The distance in meters.
/// @param listener The listener, if OBJECT_INVALID then it will be set server wide.
/// @param channel The @ref chat_channels "channel" to modify the distance heard. Only applicable for talk and whisper.
void NWNX_Chat_SetChatHearingDistance(float distance, object listener = OBJECT_INVALID, int channel = NWNX_CHAT_CHANNEL_PLAYER_TALK);
/// @brief Gets the distance with which the player hears talks or whisper
/// @param listener The listener, if OBJECT_INVALID then will return server wide setting.
/// @param channel The @ref chat_channels "channel". Only applicable for talk and whisper.
float NWNX_Chat_GetChatHearingDistance(object listener = OBJECT_INVALID, int channel = NWNX_CHAT_CHANNEL_PLAYER_TALK);
/// @}
int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SELF, object target = OBJECT_INVALID)
{
string sFunc = "SendMessage";
NWNX_PushArgumentObject(target);
NWNX_PushArgumentObject(sender);
NWNX_PushArgumentString(message);
NWNX_PushArgumentInt(channel);
NWNX_CallFunction(NWNX_Chat, sFunc);
return NWNX_GetReturnValueInt();
}
void NWNX_Chat_RegisterChatScript(string script)
{
string sFunc = "RegisterChatScript";
NWNX_PushArgumentString(script);
NWNX_CallFunction(NWNX_Chat, sFunc);
}
void NWNX_Chat_SkipMessage()
{
string sFunc = "SkipMessage";
NWNX_CallFunction(NWNX_Chat, sFunc);
}
int NWNX_Chat_GetChannel()
{
string sFunc = "GetChannel";
NWNX_CallFunction(NWNX_Chat, sFunc);
return NWNX_GetReturnValueInt();
}
string NWNX_Chat_GetMessage()
{
string sFunc = "GetMessage";
NWNX_CallFunction(NWNX_Chat, sFunc);
return NWNX_GetReturnValueString();
}
object NWNX_Chat_GetSender()
{
string sFunc = "GetSender";
NWNX_CallFunction(NWNX_Chat, sFunc);
return NWNX_GetReturnValueObject();
}
object NWNX_Chat_GetTarget()
{
string sFunc = "GetTarget";
NWNX_CallFunction(NWNX_Chat, sFunc);
return NWNX_GetReturnValueObject();
}
void NWNX_Chat_SetChatHearingDistance(float distance, object listener = OBJECT_INVALID, int channel = NWNX_CHAT_CHANNEL_PLAYER_TALK)
{
string sFunc = "SetChatHearingDistance";
NWNX_PushArgumentInt(channel);
NWNX_PushArgumentObject(listener);
NWNX_PushArgumentFloat(distance);
NWNX_CallFunction(NWNX_Chat, sFunc);
}
float NWNX_Chat_GetChatHearingDistance(object listener = OBJECT_INVALID, int channel = NWNX_CHAT_CHANNEL_PLAYER_TALK)
{
string sFunc = "GetChatHearingDistance";
NWNX_PushArgumentInt(channel);
NWNX_PushArgumentObject(listener);
NWNX_CallFunction(NWNX_Chat, sFunc);
return NWNX_GetReturnValueFloat();
}

View File

@@ -0,0 +1,43 @@
/// @addtogroup webhook Webhook
/// @brief Send messages to external entities through web hooks.
/// @{
/// @file nwnx_webhook.nss
#include "nwnx"
const string NWNX_WebHook = "NWNX_WebHook"; ///< @private
/// @brief Send a slack compatible webhook.
/// @param host The web server to send the hook.
/// @param path The path to the hook.
/// @param message The message to dispatch.
/// @param username The username to display as the originator of the hook.
/// @param mrkdwn Set to false if you do not wish your message's markdown be parsed.
void NWNX_WebHook_SendWebHookHTTPS(string host, string path, string message, string username = "", int mrkdwn = 1);
/// @brief Resends a webhook message after a defined delay.
///
/// Handy when a submission is rate limited, since the message that the event sends in NWNX_Events_GetEventData
/// is already constructed. So it just resends the WebHook with an optional delay.
/// @param host The web server to send the hook.
/// @param path The path to the hook.
/// @param sMessage The message to dispatch.
/// @param delay The delay in seconds to send the message again.
void NWNX_WebHook_ResendWebHookHTTPS(string host, string path, string sMessage, float delay = 0.0f);
/// @}
void NWNX_WebHook_SendWebHookHTTPS(string host, string path, string message, string username = "", int mrkdwn = 1)
{
string sFunc = "SendWebHookHTTPS";
NWNX_PushArgumentInt(mrkdwn);
NWNX_PushArgumentString(username);
NWNX_PushArgumentString(message);
NWNX_PushArgumentString(path);
NWNX_PushArgumentString(host);
NWNX_CallFunction(NWNX_WebHook, sFunc);
}
void NWNX_WebHook_ResendWebHookHTTPS(string host, string path, string sMessage, float delay = 0.0f)
{
DelayCommand(delay, NWNX_WebHook_SendWebHookHTTPS(host, path, sMessage));
}

View File

@@ -0,0 +1,159 @@
/// @ingroup webhook
/// @file nwnx_webhook_rch.nss
/// @brief Create richer webhook messages suitable for Discord
#include "nwnx_webhook"
/// @ingroup webhook
/// @brief For more information on these fields see https://birdie0.github.io/discord-webhooks-guide/
/// @note URL fields may require NWNX_Util_EncodeStringForURL().
struct NWNX_WebHook_Message {
string sUsername; ///< https://birdie0.github.io/discord-webhooks-guide/structure/username.html
string sText; ///< https://birdie0.github.io/discord-webhooks-guide/structure/content.html
string sAvatarURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/avatar_url.html
string sColor; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/color.html
string sAuthorName; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/author.html
string sAuthorURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/author.html
string sAuthorIconURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/author.html
string sTitle; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/title.html
string sURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/url.html
string sDescription; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/description.html
string sThumbnailURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/thumbnail.html
string sImageURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/image.html
string sFooterText; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/footer.html
string sFooterURL; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/footer.html
int iTimestamp; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/timestamp.html
string sField1Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField1Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField1Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField2Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField2Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField2Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField3Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField3Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField3Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField4Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField4Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField4Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField5Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField5Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField5Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField6Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField6Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField6Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField7Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField7Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField7Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField8Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField8Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField8Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField9Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField9Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField9Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField10Name; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
string sField10Value; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
int iField10Inline; ///< https://birdie0.github.io/discord-webhooks-guide/structure/embed/fields.html
};
/// @private We don't need this to be a part of the docs.
/// @brief Helper function to convert 0 or 1 to false or true.
/// @param iBool The integer representation of the boolean.
/// @return The string representation (true or false) of the boolean.
string IntToBoolString(int iBool);
/// @ingroup webhook
/// @brief Builds and sends a rich webhook message based on the constructed NWNX_WebHook_Message.
/// @param host The web server to send the hook.
/// @param path The path to the hook.
/// @param stMessage A constructed NWNX_Webhook_Message.
/// @param mrkdwn Set to false if you do not wish your message's markdown be parsed.
/// @warning Your path must end with /slack if using a Discord webhook.
string NWNX_WebHook_BuildMessageForWebHook(string host, string path, struct NWNX_WebHook_Message stMessage, int mrkdwn = 1);
string IntToBoolString(int iBool)
{
return iBool == 0 ? "false" : "true";
}
string NWNX_WebHook_BuildMessageForWebHook(string host, string path, struct NWNX_WebHook_Message stMessage, int mrkdwn = 1)
{
if (host == "discordapp.com" && GetStringRight(path, 6) != "/slack")
{
PrintString("Discord WebHook specified but path does not end with /slack");
return "";
}
// Open JSON
string message = "{";
string sMainText = "";
// The only way to turn off markdown for discord is to surround the text in backticks
if (stMessage.sText != "")
{
if (host == "discordapp.com" && !mrkdwn)
sMainText = "```text\\n" + stMessage.sText + "```";
else
sMainText = stMessage.sText;
}
message = message + "\"text\": \"" + sMainText + "\"";
// Slack will turn off markdown
if (host != "discordapp.com" && !mrkdwn)
message = message + ",\"mrkdwn\": false";
// Set the user attributes for the poster
if (stMessage.sUsername != "")
message = message + ",\"username\": \"" + stMessage.sUsername + "\"";
if (stMessage.sAvatarURL != "")
message = message + ",\"icon_url\": \"" + stMessage.sAvatarURL + "\"";
// We need to construct an attachment (embed) object
if (stMessage.sAuthorName != "" || stMessage.sAuthorURL != "" || stMessage.sAuthorIconURL != "" ||
stMessage.sTitle != "" || stMessage.sURL != "" || stMessage.sDescription != "" ||
stMessage.sFooterText != "" || stMessage.sFooterURL != "" || stMessage.iTimestamp > 0 ||
stMessage.sColor != "" || stMessage.sThumbnailURL != "" || stMessage.sImageURL != "" || stMessage.sField1Name != "")
{
message = message + ",\"attachments\": [{\"author_name\": \"" + stMessage.sAuthorName + "\",\"author_link\": \"" + stMessage.sAuthorURL +
"\",\"author_icon\": \"" + stMessage.sAuthorIconURL + "\",\"title\": \"" + stMessage.sTitle + "\",\"title_link\": \"" + stMessage.sURL +
"\",\"text\": \"" + stMessage.sDescription + "\",\"footer\": \"" + stMessage.sFooterText + "\",\"footer_icon\": \"" + stMessage.sFooterURL +
"\",\"color\": \"" + stMessage.sColor + "\",\"thumb_url\": \"" + stMessage.sThumbnailURL +
"\",\"image_url\": \"" + stMessage.sImageURL + "\"";
// Dont post an empty timestamp
if (stMessage.iTimestamp > 0)
message = message + ",\"ts\": \"" + IntToString(stMessage.iTimestamp) + "\"";
// Fields to handle
if (stMessage.sField1Name != "")
{
message = message + ",\"fields\": [";
message = message + "{\"title\": \"" + stMessage.sField1Name + "\",\"value\": \"" + stMessage.sField1Value + "\",\"short\": " + IntToBoolString(stMessage.iField1Inline) + "}";
if (stMessage.sField2Name != "")
message = message + ",{\"title\": \"" + stMessage.sField2Name + "\",\"value\": \"" + stMessage.sField2Value + "\",\"short\": " + IntToBoolString(stMessage.iField2Inline) + "}";
if (stMessage.sField3Name != "")
message = message + ",{\"title\": \"" + stMessage.sField3Name + "\",\"value\": \"" + stMessage.sField3Value + "\",\"short\": " + IntToBoolString(stMessage.iField3Inline) + "}";
if (stMessage.sField4Name != "")
message = message + ",{\"title\": \"" + stMessage.sField4Name + "\",\"value\": \"" + stMessage.sField4Value + "\",\"short\": " + IntToBoolString(stMessage.iField4Inline) + "}";
if (stMessage.sField5Name != "")
message = message + ",{\"title\": \"" + stMessage.sField5Name + "\",\"value\": \"" + stMessage.sField5Value + "\",\"short\": " + IntToBoolString(stMessage.iField5Inline) + "}";
if (stMessage.sField6Name != "")
message = message + ",{\"title\": \"" + stMessage.sField6Name + "\",\"value\": \"" + stMessage.sField6Value + "\",\"short\": " + IntToBoolString(stMessage.iField6Inline) + "}";
if (stMessage.sField7Name != "")
message = message + ",{\"title\": \"" + stMessage.sField7Name + "\",\"value\": \"" + stMessage.sField7Value + "\",\"short\": " + IntToBoolString(stMessage.iField7Inline) + "}";
if (stMessage.sField8Name != "")
message = message + ",{\"title\": \"" + stMessage.sField8Name + "\",\"value\": \"" + stMessage.sField8Value + "\",\"short\": " + IntToBoolString(stMessage.iField8Inline) + "}";
if (stMessage.sField9Name != "")
message = message + ",{\"title\": \"" + stMessage.sField9Name + "\",\"value\": \"" + stMessage.sField9Value + "\",\"short\": " + IntToBoolString(stMessage.iField9Inline) + "}";
if (stMessage.sField10Name != "")
message = message + ",{\"title\": \"" + stMessage.sField10Name + "\",\"value\": \"" + stMessage.sField10Value + "\",\"short\": " + IntToBoolString(stMessage.iField10Inline) + "}";
// Close fields array
message = message + "]";
}
// Close attachments array
message = message + "}]";
}
// Close JSON
message = message + "}";
return message;
}

View File

@@ -1,4 +1,12 @@
void main()
{
AddJournalQuestEntry("Category000", 1, GetEnteringObject());
object oPC = GetEnteringObject();
if (!GetIsPC(oPC)) return;
AddJournalQuestEntry("Category000", 1, oPC, FALSE, FALSE);
AddJournalQuestEntry("xprules", 1, oPC, FALSE, FALSE, FALSE);
AddJournalQuestEntry("lvl_adj", 1, oPC, FALSE, FALSE, FALSE);
}