//::///////////////////////////////////////////////
//:: Generic Journal Methods
//:: journal_include
//:://////////////////////////////////////////////
/*

*/
//:://////////////////////////////////////////////
//::    Constants
//:://////////////////////////////////////////////

// The bHideFinishedQuests boolean variable controls if
// completed quests are loaded into the journal when a
// player logs back in.  If set to TRUE, journal entries
// will not be added.  Otherwise, they will be loaded.
const int bHideFinishedQuests = FALSE;

//:://////////////////////////////////////////////
//::     Method:    dhJournalModuleLoad
//:: Created By:    Dauvis
//:: Created On:    6/29/03
//:: Modified By:   Dauvis
//:: Modified On:   7/3/03
//::
//:: This funciton is currently obsolete but is included
//:: for backward compatibility.
//:://////////////////////////////////////////////
void dhJournalModuleLoad()
{
    // do nothing
}

#include "journal_db_name"

//:://////////////////////////////////////////////
//::     Method:  dhAddJournalQuestEntry
//:: Created By:  Dauvis
//:: Created On:  6/29/03
//::
//:: This method is a replacement for the AddJournalQuestEntry
//:: standard function.  It operates identically to the standard
//:: function except that it will perform actions necessary
//:: to give a persistant store to the journal entries.
//:: The bMarkAsFinished arguments, which is not standard, is a
//:: flag to indicate that the quest is finished.  If there is
//:: a way to figure this out from the module, I would like to know.
//:://////////////////////////////////////////////
void dhAddJournalQuestEntry(string strCategoryTag, int iEntryId, object oCreature,
        int bAllPartyMembers = TRUE, int bAllPlayers = FALSE, int bAllowOverrideHigher = FALSE,
        int bMarkAsFinished = FALSE)
{
    object oPlayer;

    // if bAllPlayers is true, make a call for each player
    if (bAllPlayers) {
        oPlayer = GetFirstPC();
        while (GetIsObjectValid(oPlayer)) {
            dhAddJournalQuestEntry(strCategoryTag, iEntryId, oPlayer, FALSE, FALSE,
                    bAllowOverrideHigher, bMarkAsFinished);
            oPlayer = GetNextPC();
        }
        return;
    }

    // if bAllPartyMembers is true, make a call for each player in the party
    if (bAllPartyMembers) {
        oPlayer = GetFirstFactionMember(oCreature, TRUE);
        while (GetIsObjectValid(oPlayer)) {
            dhAddJournalQuestEntry(strCategoryTag, iEntryId, oPlayer, FALSE, FALSE,
                    bAllowOverrideHigher, bMarkAsFinished);
            oPlayer = GetNextFactionMember(oCreature, TRUE);
        }
        return;
    }

    // perform the standard function call
    AddJournalQuestEntry(strCategoryTag, iEntryId, oCreature, FALSE, FALSE, bAllowOverrideHigher);

    // setup processing
    int iMaxIndex = GetLocalInt(oCreature, "iJournalMaxIndex");
    int bQuestFound = FALSE;
    int idx;
    string strTag;
    string strSerialized = "";
    int iState;

    // loop through all of the quests loaded on the player
    // to find out if this is a new quest. build the
    // serialized string as we go.
    for (idx = 0; idx < iMaxIndex; idx++) {
        strTag = "strQuestTag" + IntToString(idx);
        strTag = GetLocalString(oCreature, strTag);

        if (strTag == "") {
            continue;
        }
        if (strTag == strCategoryTag) {
            bQuestFound = TRUE;

            // if marked as finished, clear the array element and do
            // not put it in the serialized string.
            if (bHideFinishedQuests && bMarkAsFinished) {
                strTag = "strQuestTag"+IntToString(idx);
                SetLocalString(oCreature, strTag, "");
                continue;
            }
        }
        iState = GetLocalInt(oCreature, "NW_JOURNAL_ENTRY"+strTag);

        strSerialized += strTag;
        strSerialized += ".";
        strSerialized += IntToString(iState);
        strSerialized += ".";
    }

    // if quest wasn't found, add it to the serialized string
    // and record it on the PC.  if marked as finished don't do
    // this
    if (!bQuestFound && (!bHideFinishedQuests || !bMarkAsFinished)) {
        strTag = "strQuestTag" + IntToString(iMaxIndex);
        SetLocalInt(oCreature, "iJournalMaxIndex", iMaxIndex+1);
        SetLocalString(oCreature, strTag, strCategoryTag);

        iState = GetLocalInt(oCreature, "NW_JOURNAL_ENTRY"+strCategoryTag);
        strSerialized += strCategoryTag;
        strSerialized += ".";
        strSerialized += IntToString(iState);
        strSerialized += ".";
    }

    // if marked as finished, set a flag in the database and add it to the
    // list of "completed" quests
    if (bMarkAsFinished) {
        strTag = "bQuestFinished_" + strCategoryTag;
        SetCampaignInt(dhGetJournalDatabaseName(oCreature), strTag, TRUE, oCreature);
        strTag = GetCampaignString(dhGetJournalDatabaseName(oCreature), "strCompletedQuests", oCreature);
        strTag += (strCategoryTag + ".");
        SetCampaignString(dhGetJournalDatabaseName(oCreature), "strCompletedQuests", strTag, oCreature);
    }

    // store the serialized string
    SetCampaignString(dhGetJournalDatabaseName(oCreature), "strQuestStates", strSerialized, oCreature);
}

//:://////////////////////////////////////////////
//::     Method:  dhRemoveJournalQuestEntry
//:: Created By:  Dauvis
//:: Created On:  6/29/03
//::
//:: This method is a replacement for the RemoveJournalQuestEntry
//:: standard function.  It operates identically to the standard
//:: function except that it will perform actions necessary
//:: to give a persistant store to the journal entries.
//:: The bRemoveIfFinished argument, which is not standard, indicates
//:: if the quest should be removed even if it was flagged as
//:: finish.  By default, finished quests will not be removed.
//:://////////////////////////////////////////////
void dhRemoveJournalQuestEntry(string strPlotId, object oCreature, int bAllPartyMembers = TRUE,
        int bAllPlayers = FALSE, int bRemoveIfFinished = FALSE)
{
    object oPlayer;

    // if bAllPlayers is true, make a call for each player
    if (bAllPlayers) {
        oPlayer = GetFirstPC();
        while (GetIsObjectValid(oPlayer)) {
            dhRemoveJournalQuestEntry(strPlotId, oCreature, FALSE, FALSE, bRemoveIfFinished);
            oPlayer = GetNextPC();
        }
        return;
    }

    // if bAllPartyMembers is true, make a call for each player in the party
    if (bAllPartyMembers) {
        oPlayer = GetFirstFactionMember(oCreature, TRUE);
        while (GetIsObjectValid(oPlayer)) {
            dhRemoveJournalQuestEntry(strPlotId, oCreature, FALSE, FALSE, bRemoveIfFinished);
            oPlayer = GetNextFactionMember(oCreature, TRUE);
        }
        return;
    }

    // perform the standard function call
    RemoveJournalQuestEntry(strPlotId, oCreature, FALSE, FALSE);

    // setup processing
    int iMaxIndex = GetLocalInt(oCreature, "iJournalMaxIndex");
    int idx;
    string strTag;
    string strSerialized = "";
    int iState;

    // loop through all of the quests loaded on the player
    // to find this quest and remove it. build the
    // serialized string as we go.
    for (idx = 0; idx < iMaxIndex; idx++) {
        strTag = "strQuestTag" + IntToString(idx);
        strTag = GetLocalString(oCreature, strTag);

        if (strTag == "") {
            continue;
        }
        if (strTag == strPlotId) {
            strTag = "strQuestTag"+IntToString(idx);
            SetLocalString(oCreature, strTag, "");
            continue;
        }
        iState = GetLocalInt(oCreature, "NW_JOURNAL_ENTRY"+strTag);

        strSerialized += strTag;
        strSerialized += ".";
        strSerialized += IntToString(iState);
        strSerialized += ".";
    }

    // if the remove if finished flag is set, see if the quest has been
    // marked as finished and unset it if it is (Don't just set the flag
    // because it will create an unnecessary database entry).
    if (bRemoveIfFinished) {
        strTag = "bQuestFinished_" + strPlotId;
        iState = GetCampaignInt(dhGetJournalDatabaseName(oCreature), strTag, oCreature);
        if (iState) {
            SetCampaignInt(dhGetJournalDatabaseName(oCreature), strTag, FALSE, oCreature);
        }
    }

    // store the serialized string
    SetCampaignString(dhGetJournalDatabaseName(oCreature), "strQuestStates", strSerialized, oCreature);
}

//:://////////////////////////////////////////////
//::     Method:  dhGetJournalQuestState
//:: Created By:  Dauvis
//:: Created On:  6/29/03
//::
//:: This function will return the current state of the specified
//:: quest.  If there isn't such a quest on the player's list,
//:: it will return 0.  If the quest is marked as finished,
//:: it will return -1.  Otherwise, it will return the quest's
//:: state.
//:://////////////////////////////////////////////
int dhGetJournalQuestState(string strPlotId, object oCreature)
{
    // check the database to see if the flag is set.
    string strTag = "bQuestFinished_" + strPlotId;
    int bFlag = GetCampaignInt(dhGetJournalDatabaseName(oCreature), strTag, oCreature);
    if (bFlag) return -1;

    // return the state stored on the player
    strTag = "NW_JOURNAL_ENTRY" + strPlotId;
    int iState = GetLocalInt(oCreature, strTag);
    return iState;
}

//:://////////////////////////////////////////////
//::     Method:  dhNextToken
//:: Created By:  Dauvis
//:: Created On:  6/29/03
//::
//:: This function is used in the parsing of a string into
//:: tokens.
//:://////////////////////////////////////////////
string dhNextToken(string strSerialized, int iStart)
{
    int idx = 0;
    string strChar;

    if (GetStringLength(strSerialized) <= iStart) return "";

    strChar = GetSubString(strSerialized, iStart+idx, 1);
    while (strChar != ".") {
        idx++;
        strChar = GetSubString(strSerialized, iStart+idx, 1);
    }
    return GetSubString(strSerialized, iStart, idx);
}

//:://////////////////////////////////////////////
//::     Method:  dhClearJournal
//:: Created By:  Dauvis
//:: Created On:  6/29/03
//::
//:: This method clears the persistant information for
//:: the player's journal.  It is intended to be
//:: called for new characters so that a new character
//:: won't "inherit" values from a former character.
//::
//:: WARNING:  if a character is recreated from one
//:: that had a very large number of quests completed,
//:: there will be a moment of lag as the information
//:: is cleared.
//:://////////////////////////////////////////////
void dhClearJournal(object oPlayer)
{
    string strCompleted;
    int iStart = 0;
    string strToken;
    string strTag;

    strCompleted = GetCampaignString(dhGetJournalDatabaseName(oPlayer), "strCompletedQuests", oPlayer);
    while (TRUE) {
        strToken = dhNextToken(strCompleted, iStart);
        iStart += (GetStringLength(strToken) + 1);

        if (strToken == "") break;

        strTag = "bQuestFinished_" + strToken;
        SetCampaignInt(dhGetJournalDatabaseName(oPlayer), strTag, FALSE, oPlayer);
    }

    // clear the pending quests and completed quests
    SetCampaignString(dhGetJournalDatabaseName(oPlayer), "strCompletedQuests", "", oPlayer);
    SetCampaignString(dhGetJournalDatabaseName(oPlayer), "strQuestStates", "", oPlayer);
}

//:://////////////////////////////////////////////
//::     Method:  dhLoadJournalQuestStates
//:: Created By:  Dauvis
//:: Created On:  6/29/03
//::
//:: This method will load the quest states from the database
//:: and store them on the player.
//:://////////////////////////////////////////////
void dhLoadJournalQuestStates(object oPlayer)
{
    string strSerialized;
    int idx, iMaxIndex = 0;
    int iStart = 0;
    string strToken1;
    string strToken2;
    string strTag;

    // error checking
    if (!GetIsPC(oPlayer)) return;

    // check to make sure this is not a new character
    // because a player can recreate a character of the
    // same name
    if (GetXP(oPlayer) == 0) {
        dhClearJournal(oPlayer);
        return;
    }

    // get serialized quest states
    strSerialized = GetCampaignString(dhGetJournalDatabaseName(oPlayer), "strQuestStates", oPlayer);
    while (TRUE) {
        strToken1 = dhNextToken(strSerialized, iStart);
        iStart += (GetStringLength(strToken1) + 1);
        strToken2 = dhNextToken(strSerialized, iStart);
        iStart += (GetStringLength(strToken2) + 1);

        if (strToken1 == "" || strToken2 == "") {
            break;
        }

        AddJournalQuestEntry(strToken1, StringToInt(strToken2), oPlayer, FALSE);
        strTag = "strQuestTag"+IntToString(iMaxIndex);
        SetLocalString(oPlayer, strTag, strToken1);
        iMaxIndex++;
    }

    SetLocalInt(oPlayer, "iJournalMaxIndex", iMaxIndex+1);
}

//:://////////////////////////////////////////////
//::     Method:  dhGiveQuestItem
//:: Created By:  Dauvis
//:: Created On:  7/3/03
//::
//:: This function is intended to be called in a death script for NPC but it can
//:: be useful in other situations.  When called, the function will check the
//:: specified player to see if he is a specific state in a quest.  If so,
//:: it will award the specified item to him.  This function return TRUE
//:: if an item was awarded.  It will return FALSE if the bAllParty flag is
//:: set to TRUE.
//::
//:: strPlotId - the category tag for the quest
//:: iReqState - the state at which the player must be at to receive the item
//:: oPlayer - the player on which the check will be made
//:: strResRef - the resource reference of the item to award
//:: iQty - the quantity of items to give to the player
//:: iNewState - the next state to which the quest will be changed.  If this is
//::        is set to -1, the quest's state will not be changed.
//:: bAllParty - a flag to indicate if everyone in the player's party
//::        should be checked and be awarded in addition to the player.
//:: bMarkAsFinished - a flag to indicate that the quest should be marked as
//::        finished if the quest state is changed by this function.
//:://////////////////////////////////////////////
int dhGiveQuestItem(string strPlotId, int iReqState, object oPlayer, string strResRef,
        int iQty = 1, int iNewState = -1, int bAllParty = FALSE, int bMarkAsFinished = FALSE)
{
    object oCreature;

    // if the bAllParty is true, apply this function to all players in
    // the party
    if (bAllParty) {
        oCreature = GetFirstFactionMember(oPlayer, TRUE);
        while (GetIsObjectValid(oCreature)) {
            dhGiveQuestItem(strPlotId, iReqState, oCreature, strResRef, iQty, iNewState);
            oCreature = GetNextFactionMember(oPlayer, TRUE);
        }
        return FALSE;
    }

    // check to see if we are at the required state
    int iCurState = dhGetJournalQuestState(strPlotId, oPlayer);
    if (iCurState != iReqState) {
        return FALSE;
    }

    // we are at the required state, award the item.
    CreateItemOnObject(strResRef, oPlayer, iQty);

    // update the state if necessary
    if (iNewState != -1) {
        dhAddJournalQuestEntry(strPlotId, iNewState, oPlayer, FALSE, FALSE, FALSE, bMarkAsFinished);
    }

    return TRUE;
}

//:://////////////////////////////////////////////
//::      Method:   dhHideJournalQuestEntry
//::  Created By:   Dauvis
//::  Created On:   7/13/03
//:: Modified By:   Dauvis
//:: Modified On:   7/13/03
//::
//:: This function will remove a quest from the player's journal
//:: if the quest has been marked as finished.  Its purpose is to
//:: provide a way to remove the journal entry from the journal if the
//:: bHideJournalQuests flag is set to FALSE.
//:://////////////////////////////////////////////
void dhHideJournalQuestEntry(object oPlayer, string strPlotId)
{
    int iMaxIndex = GetLocalInt(oPlayer, "iJournalMaxIndex");
    int idx;
    string strTag;
    string strSerialized = "";
    int iState;

    // make sure the quest is marked as finished
    strTag = "bQuestFinished_" + strPlotId;
    int bFlag = GetCampaignInt(dhGetJournalDatabaseName(oPlayer), strTag, oPlayer);
    if (!bFlag) return;

    // find the quest in the list and exclude from
    // serialize string
    for (idx = 0; idx < iMaxIndex; idx++) {
        strTag = "strQuestTag" + IntToString(idx);
        strTag = GetLocalString(oPlayer, strTag);

        if (strTag == "") {
            continue;
        }
        if (strTag == strPlotId) {
            // clear the array element and do not put it in the serialized string.
            strTag = "strQuestTag"+IntToString(idx);
            SetLocalString(oPlayer, strTag, "");
            continue;
        }
        iState = GetLocalInt(oPlayer, "NW_JOURNAL_ENTRY"+strTag);

        strSerialized += strTag;
        strSerialized += ".";
        strSerialized += IntToString(iState);
        strSerialized += ".";
    }

    // save the new serialized string
    SetCampaignString(dhGetJournalDatabaseName(oPlayer), "strQuestStates", strSerialized, oPlayer);

    // remove from journal for aestetic reasons
    RemoveJournalQuestEntry(strPlotId, oPlayer);
}