//::///////////////////////////////////////////////
//:: Custom Module OnPlayerRest Script
//:: Copyright (c) 2003
//:://////////////////////////////////////////////
//
//  Purpose: To limit resting by a configurable amount of time. Provides ability to allow
//          unlimited resting for users under a certain level, or allow unlimited resting
//          in certain areas.
//
//:://////////////////////////////////////////////
//:: Created By:   Diabolist
//:: Created On:   June, 2003
//:://////////////////////////////////////////////

void main()
{
    object oPC = GetLastPCRested();
    // eBad is used to cycle through effects on the user, and remove the blind and deaf effects from resting
    // I'm uncertain why I can't just do a RemoveEffect(oPC, EffectBlindness())...
    effect eBad = GetFirstEffect(oPC);
    // get the hour and day the user last rested
    int nLastRestedHour = GetLocalInt(oPC, "LastRestedHour");
    int nLastRestedDay = GetLocalInt(oPC, "LastRestedDay");
    // Since nLastRestedHour will return 0 if it's NOT set, and 0 is a valid hour, we need another variable to check if the user has ever rested
    int nHasRested = GetLocalInt(oPC, "HasRested");
    // Time difference between last rest and last rest attempt
    int nRestDelta;
    // change nRestTimer to increase/decrease the wait time between resting (with default NWN game time, 5 equates to 10 minutes real-time)
    int nRestTimer = 3;
    int nHour = GetTimeHour();
    int nDay = GetCalendarDay();
    int bCanRest = FALSE;
    // Get the area the PC is in, this can be used to allow unlimited resting in certain areas
    string sArea = GetTag(GetAreaFromLocation(GetLocation(oPC)));
    // change nPCMinLevel to allow users equal to, or less than, this value to rest without waiting
    int nPCMinLevel = 1;
    int nPCLevel = GetLevelByPosition(1, oPC) + GetLevelByPosition(2, oPC) + GetLevelByPosition(3, oPC);
    // Used in output to change the word "hour" to "hours"
    string sPlural = "";
    // String to save the location of the player (so they can restart where they left off)
    string sLocKey = "lLoc_" + GetPCPlayerName(oPC) + "_" + GetName(oPC);
    location lLoc = GetLocation(oPC);

    switch (GetLastRestEventType()) {
        case REST_EVENTTYPE_REST_STARTED:
            // Check if the player has rested this session and that the player is over nPCMinLevel
            // If you want unlimited resting in certain areas, add them to this if statement, example:
            // if ((!nHasRested && nPCLevel > nPCMinLevel)
            //      || sArea != "AspenPalaceSecondFloor"
            //      || sArea != "BakkaInnSecondFloor"
            //      || sArea != "CrossroadsInn"
            //      || sArea != "GwynithirInnSecondFloor"
            //      || sArea != "HiddenOasis"
            //      || sArea != "RykarianMagickGuildSecondFloor"
            //      || sArea != "TempleofTyrAldaris"
            //      || sArea != "TempleofTyrBath"
            //      || sArea != "TempleofTyrMedicalWing"
            //      || sArea != "TheWindburnInnSecondFloor"
            //      || sArea != "ThePhoenixRavenSecondFloor"
            //      || sArea != "YourHome") {
            // local variables will still be updated, but it will allow players a safe area they can flee to ;)
            if ((nHasRested == 1 && nPCLevel > nPCMinLevel)) {
                // Some simple calculation here to determine if we can rest, but we need to account for the "wrapping" effect of time
                // if nHour is less than the last rest attempt, then we've moved into the next day...
                if (nHour < nLastRestedHour) {
                    nRestDelta = 24 - nLastRestedHour + nHour;
                // This check is a little more simple, since the rest attempt is larger than the last rest event, we just need to
                // calculate the difference, we'll check later if it's the next day
                } else if (nHour > nLastRestedHour) {
                    nRestDelta = nHour - nLastRestedHour;
                // looks like they tried to rest in the same hour (same day or next day)
                } else {
                    nRestDelta = 0;
                }
                // These next checks which allow the user to rest look a bit conveluted, but it's necessary to catch everything
                // 1. Check if rest time is greater than the rest timer
                // 2. Check if the last successfull rest day is two days earlier
                // 3. Check if the user waited over 23 hours to rest
                // 4. Check for day 28 anomoly (special situation of check #3)
                if (nRestDelta >= nRestTimer
                    || nLastRestedDay < nDay - 1
                    || (nHour >= nLastRestedHour && nLastRestedDay < nDay)
                    || (nHour >= nLastRestedHour && nLastRestedDay == 28 && nDay == 1)) {
                    bCanRest = TRUE;
                }
            // The player hasn't rested yet this session, they're a low level, or they're in a safe area... so set the rest flag to TRUE
            } else {
                bCanRest = TRUE;
            }

            if (!bCanRest) {
                // can't rest yet, so let's tell the user how long to wait
                nRestDelta = 3 - nRestDelta;
                // quick trick to change the word "hour" to plural: "hours"
                if (nLastRestedHour > 1) { sPlural = "s"; }
                FloatingTextStringOnCreature("You must wait " + IntToString(nRestDelta) + " hour" + sPlural + " before resting again (" + IntToString(FloatToInt(HoursToSeconds(nRestDelta)/60)) + " minutes realtime)...", oPC, FALSE);
                AssignCommand(oPC,ClearAllActions());
            } else {
                // user can rest, so set local int, and apply blind/deaf effects
                SetLocalInt(oPC, "LastRestedHour", nHour);
                SetLocalInt(oPC, "LastRestedDay", nDay);
                SetLocalInt(oPC, "HasRested", 1);
                PlayVoiceChat(VOICE_CHAT_REST, oPC);
                ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBlindness(), oPC);
                ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDeaf(), oPC);
                SetLocalLocation(GetModule(), sLocKey, lLoc);
            }
            break;
        case REST_EVENTTYPE_REST_CANCELLED:
            // rest cancelled, remove effects... again, wish I could just use a simple RemoveEffect(oPC, EffectBlindness())...
            while (GetIsEffectValid(eBad)) {
                if (GetEffectType(eBad) == EFFECT_TYPE_BLINDNESS ||
                   GetEffectType(eBad) == EFFECT_TYPE_DEAF) {
                    RemoveEffect(oPC, eBad);
               }
                eBad = GetNextEffect(oPC);
            }
            break;
        case REST_EVENTTYPE_REST_FINISHED:
            // Status quo...
            while (GetIsEffectValid(eBad)) {
                if (GetEffectType(eBad) == EFFECT_TYPE_BLINDNESS ||
                   GetEffectType(eBad) == EFFECT_TYPE_DEAF) {
                    RemoveEffect(oPC, eBad);
               }
                eBad = GetNextEffect(oPC);
            }
            break;
    }

}