//:://////////////////////////////////////////////
//:: Conversation Charactor Creator
//:: prc_ccc.nss
//:://////////////////////////////////////////////
/** @file
    Long description


    @author Primogenitor, modified by fluffyamoeba
    @date   Created  - 2006.09.18
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////

// includes

#include "ccc_inc_convo"
#include "prc_inc_spells"

//////////////////////////////////////////////////
/* Constant defintions + functions              */
//////////////////////////////////////////////////

// see includes

//////////////////////////////////////////////////
/* Main function                                */
//////////////////////////////////////////////////

void main()
{
    object oPC = GetPCSpeaker();
    int bBoot; // used by this script to mark if the PC should be booted

    if(!GetIsObjectValid(oPC)) // if no valid speaker, then not part of the convo
    {
        // no valid speaker so need to get the PC properly this time
        oPC = GetEnteringObject();
        //oPC = GetLastUsedBy();
        // if called for a NPC, DM or the switch isn't enabled
        if(!GetIsPC(oPC) || GetIsDM(oPC) || !GetPRCSwitch(PRC_CONVOCC_ENABLE))
            return;
        
        // check letoscript is setup correctly
        // if letoscript can't find the bic, the convoCC won't work. 
        // will boot the PC later if they should go through the convoCC but can't
        // otherwise ignore
        bBoot = DoLetoscriptTest(oPC);
        
        // encryption stuff
        /* TODO proper comment */
        string sEncrypt;
        sEncrypt= Encrypt(oPC);
        
        // reset
        DeleteLocalString(oPC, "CONVOCC_ENTER_BOOT_MESSAGE");
        int nPCStatus = CONVOCC_ENTER_BOOT_PC;
        if(GetPRCSwitch(PRC_CONVOCC_CUSTOM_ENTER_SCRIPT)) // custom entry script stuff here
        {
            DeleteLocalInt(oPC,"CONVOCC_LAST_STATUS");
            /**
             * The custom script must:
             * - be called "ccc_custom_enter"
             * - set the local int "CONVOCC_LAST_STATUS" on the PC (OBJECT_SELF)
             * - include prc_ccc_const (for the constants the local int can be set to)
             * otherwise the PC will always be booted
             *
             * possible values for CONVOCC_LAST_STATUS:
             * CONVOCC_ENTER_BOOT_PC (causes the PC to get kicked)
             * CONVOCC_ENTER_NEW_PC (causes the PC to go through the convoCC)
             * CONVOCC_ENTER_RETURNING_PC (causes the PC to skip the convoCC)
             */
            ExecuteScript("ccc_custom_enter", oPC);
            nPCStatus = GetLocalInt(oPC,"CONVOCC_LAST_STATUS");
            DeleteLocalInt(oPC,"CONVOCC_LAST_STATUS");
        }
        else // default handling of whether to send PCs through the convoCC
        {
            // if a new character...
            if((GetPRCSwitch(PRC_CONVOCC_USE_XP_FOR_NEW_CHAR) && GetXP(oPC) == 0)
            || (!GetPRCSwitch(PRC_CONVOCC_USE_XP_FOR_NEW_CHAR) && GetTag(oPC) != sEncrypt))
            {
                nPCStatus = CONVOCC_ENTER_NEW_PC;
            }
            else // returning PC
            {
                nPCStatus = CONVOCC_ENTER_RETURNING_PC;
            }
        }
        if (DEBUG) { DoDebug("**** nPCStatus: "+ IntToString(nPCStatus));}
        /* Now to deal with some special cases that always override the custom script
         * These only apply if the PC was supposed to be going through the convoCC, ie.
         * where nPCStatus == CONVOCC_ENTER_NEW_PC.
         */
        if(nPCStatus == CONVOCC_ENTER_NEW_PC)
        {
            if(bBoot) // if singleplayer or letoscript not set up correctly
                nPCStatus = CONVOCC_ENTER_BOOT_PC;
            else if (GetIsObjectValid(GetLocalObject(GetModule(), "ccc_active_pc"))) // next see if someone has already started the convo
            {
                nPCStatus = CONVOCC_ENTER_BOOT_PC;
                SetLocalString(oPC, "CONVOCC_ENTER_BOOT_MESSAGE", 
                    "The conversation Character Creator is in use, please try later.");
            }
        }
        if (DEBUG) { DoDebug("**** nPCStatus: "+ IntToString(nPCStatus));}
        // end of decision making

        /* kick the PC */
        if (nPCStatus == CONVOCC_ENTER_BOOT_PC)
        {
            CheckAndBootNicely(oPC);
            return;
        }
        /* new character */
        else if (nPCStatus == CONVOCC_ENTER_NEW_PC)
        {
            // now reserve the conversation slot - only one at a time
            SetLocalObject(GetModule(), "ccc_active_pc", oPC);
            // remove equipped items and clear out inventory
            DoStripPC(oPC);
            //rest them so that they loose cutscene invisible
            //from previous logons
            ForceRest(oPC);
            //Take their Gold
            AssignCommand(oPC,TakeGoldFromCreature(GetGold(oPC),oPC,TRUE));
            //DISABLE FOR DEBUGGING
            if (!DEBUG)
            {
                // start the cutscene
                // off for debugging to see the debug text in the client
                SetCutsceneMode(oPC, TRUE);
            }
            SetCameraMode(oPC, CAMERA_MODE_TOP_DOWN);
            //make sure the PC stays put
            effect eParal = EffectCutsceneImmobilize();
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eParal, oPC, 9999.9);
            //start the ConvoCC conversation
            DelayCommand(10.0, StartDynamicConversation("prc_ccc_main", oPC, FALSE, FALSE, TRUE));
            // sets up the cutscene to the point in the convo at which the player logged out
            DelayCommand(11.0, DoCutscene(oPC, TRUE));
            // mark that stage 1 of the cutscene is set up
            SetLocalInt(oPC, "CutsceneStage", 1);
            SetExecutedScriptReturnValue(X2_EXECUTE_SCRIPT_END);
        }
        /* returning character */
        else if (nPCStatus == CONVOCC_ENTER_RETURNING_PC)
        {
            // if using XP or your own script for new characters, this sets returning characters' tags 
            // to encrypted then you can turn off using XP after all characters have logged on.
            if((GetPRCSwitch(PRC_CONVOCC_USE_XP_FOR_NEW_CHAR) || GetPRCSwitch(PRC_CONVOCC_CUSTOM_ENTER_SCRIPT))
                && GetTag(oPC) != sEncrypt
                && GetXP(oPC) > 0)
            {
                string sScript = LetoSet("Tag", sEncrypt, "string");
                SetLocalString(oPC, "LetoScript", GetLocalString(oPC, "LetoScript")+sScript);
            }
            SetExecutedScriptReturnValue(X2_EXECUTE_SCRIPT_CONTINUE);
        }
        else /* ooops - should never get here */
        {
            if (DEBUG) { DoDebug("Error: invalid value for var nPCStatus");}
            // kick just in case
            SetLocalString(oPC, "CONVOCC_ENTER_BOOT_MESSAGE", "Error:I'm confused.");
            CheckAndBootNicely(oPC);
            return;
        }
    }
    else // in convo
    {
        /* dynamic convo from here*/
        // double check it's a PC
        if(!GetIsPC(oPC) || GetIsDM(oPC) || !GetPRCSwitch(PRC_CONVOCC_ENABLE))
            return;
        // The stage is used to determine the active conversation node.
        // 0 is the entry node.
        int nStage = GetStage(oPC);
        /* Get the value of the local variable set by the conversation script calling
         * this script. Values:
         * DYNCONV_ABORTED     Conversation aborted
         * DYNCONV_EXITED      Conversation exited via the exit node
         * DYNCONV_SETUP_STAGE System's reply turn
         * 0                   The script was called by prc_onenter
         * Other               The user made a choice
         */
        int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE);
        
        if(DEBUG) DoDebug("prc_ccc running.\n"
                    + "oPC = " + DebugObject2Str(oPC) + "\n"
                    + "nValue = " + IntToString(nValue)
                      );
    
        // Check which of the conversation scripts called the scripts
        if(nValue == 0) // All of them set the DynConv_Var to non-zero value, so something is wrong -> abort
            return;
    
        if(nValue == DYNCONV_SETUP_STAGE)
        {
            // Check if this stage is marked as already set up
            // This stops list duplication when scrolling
            if(!GetIsStageSetUp(nStage, oPC))
            {
                // variable named nStage determines the current conversation node
                // this function sets up the text displayed and the response options
                // for the current conversation node
                if(DEBUG)
                {
                    DoDebug("prc_ccc: Stage was not already set up");
                    DoDebug("Pass to DoHeaderAndChoices(): " + IntToString(nStage));
                }
                   // SpawnScriptDebugger();
                DoHeaderAndChoices(nStage);
                if(DEBUG) DoDebug("DoHeaderAndChoices() finished");
            }
    
            // Do token setup
            SetupTokens();
            if(DEBUG) ExecuteScript("prc_ccc_debug", oPC);
        }
        // End of conversation cleanup
        else if(nValue == DYNCONV_EXITED)
        {
            if(DEBUG) DoDebug("prc_ccc: Conversation exited");
            // cleanup is done in prc_ccc_make_pc
            // deletes local variables used to create the character
        }
        // Abort conversation cleanup.
        // NOTE: This section is only run when the conversation is aborted
        // while aborting is allowed. When it isn't, the dynconvo infrastructure
        // handles restoring the conversation in a transparent manner
        else if(nValue == DYNCONV_ABORTED)
        {
            // shouldn't reach this stage as aborting isn't allowed
            AssignCommand(oPC, DelayCommand(1.0, CheckAndBoot(oPC)));
        }
        // Handle PC responses
        else
        {
            // variable named nChoice is the value of the player's choice as stored when building the choice list
            // variable named nStage determines the current conversation node
            int nChoice = GetChoice(oPC);
            
            // check if the second stage of the cutscene needs setting up
            // note: this is an *ugly hack*
            // basically, if the player has made a choice, we know the area has fully loaded
            // so we can set up the camera facing here
            if (GetLocalInt(oPC, "CutsceneStage") == 1)
            {
                // do the camera stuff
                DoRotatingCamera(oPC);
                // mark that stage 2 of the cutscene is set up
                SetLocalInt(oPC, "CutsceneStage", 2);
            }
            
            // get nStage back so SetStage() actually changes the stage
            if(DEBUG) DoDebug("nStage before HandleChoice: " + IntToString(nStage));
            nStage = HandleChoice(nStage, nChoice);
            if(DEBUG) DoDebug("nStage after HandleChoice: " + IntToString(nStage));
    
            // Store the stage value. If it has been changed, this clears out the choices
            SetStage(nStage, oPC);
        }
    }
}