void CasinoNotification(string sMessage) 
{
    object oPC = GetFirstPC(); // Start with the first PC on the server

    // Loop through all PCs on the server
    while (GetIsObjectValid(oPC)) 
    {
        // Check if the player has the item
        object oItem = GetItemPossessedBy(oPC, "I_CAS_CASINOHANDLE");

        // Check if the item has the variable set to 1
        if (GetIsObjectValid(oItem) && GetLocalInt(oItem, "CAS_NOTIFICATIONS") == 1) 
        {
            // Send the message to the player
            SendMessageToPC(oPC, sMessage);
        }

        // Move to the next PC in the loop
        oPC = GetNextPC();
        
        // Example usage
// CasinoNotification("Attention all casino players: Special event starting now!");
    }
}


void LogPotentialAbuse(object oPC, int nCost, int nTokens, int nTokensPurchased, int nTokensSpent, string sGameName) 
{
    string sCharacterName  = GetName(oPC);
    string sPlayerUsername = GetPCPlayerName(oPC);
    string sPCId           = IntToString(PLAYER_GetId(oPC)); // short player id

    object oSrc            = OBJECT_SELF;                    // usually the placeable calling this
    string sSrcTag         = GetTag(oSrc);
    string sSrcName        = GetName(oSrc);

    object oArea           = GetArea(oSrc);
    string sAreaTag        = GetTag(oArea);
    string sAreaName       = GetName(oArea);

    vector vPos            = GetPosition(oSrc);
    string sPos            = "X=" + FloatToString(vPos.x, 2) + ",Y=" + FloatToString(vPos.y, 2) + ",Z=" + FloatToString(vPos.z, 2);

    int nYear   = GetCalendarYear();
    int nMonth  = GetCalendarMonth();
    int nDay    = GetCalendarDay();
    int nHour   = GetTimeHour();
    int nMinute = GetTimeMinute();
    string sStamp = "Date=" + IntToString(nYear) + "-" + IntToString(nMonth) + "-" + IntToString(nDay) +
                    " " + IntToString(nHour) + ":" + IntToString(nMinute);

    string sLogMessage;

    if (sGameName == "ANNOUNCER_SEX")
    {
        // CHANNEL_CUSTOM_PUBLIC_SEX = 0x00000008
        sLogMessage = "CAS|ANNOUNCER_SEX|Char=" + sCharacterName +
                      "|Player=" + sPlayerUsername +
                      "|PlayerID=" + sPCId +
                      "|PlaceableTag=" + sSrcTag +
                      "|PlaceableName=" + sSrcName +
                      "|AreaTag=" + sAreaTag +
                      "|Area=" + sAreaName +
                      "|Pos=" + sPos +
                      "|Channel=0x00000008|" + sStamp;
    }
    else
    {
        sLogMessage = "CAS|TXN|Char=" + sCharacterName +
                      "|Player=" + sPlayerUsername +
                      "|PlayerID=" + sPCId +
                      "|Game=" + sGameName +
                      "|Cost=" + IntToString(nCost) +
                      "|TokensPurchased=" + IntToString(nTokensPurchased) +
                      "|TokensSpent=" + IntToString(nTokensSpent) +
                      "|TokensNow=" + IntToString(nTokens) +
                      "|" + sStamp;
    }

    Log(sLogMessage, "cas");
}




// Function to log the floor tile purchase
void LogFloorTilePurchase(object oPC, int nGoldSpent, string sFloorType) 
{
    string sCharacterName = GetName(oPC);
    string sPlayerUsername = GetPCPlayerName(oPC);
    string sPCId = IntToString(PLAYER_GetId(oPC));
    string sLogMessage = "Character: " + sCharacterName + ", Player Name: " + sPlayerUsername + " (PlayerID: " + sPCId + ") spent " + IntToString(nGoldSpent) + " gold on a " + sFloorType + " floor tile.";
    Log(sLogMessage, "cas");
}

void SpawnFloor(string sResRef, string sSpawnTag, object oArea) 
{
    object oWaypoint = GetWaypointByTag(sSpawnTag);
    if (GetIsObjectValid(oWaypoint)) 
    {
        CreateObject(OBJECT_TYPE_PLACEABLE, sResRef, GetLocation(oWaypoint));
    }
}


void LogCasinoTransaction(object oPC, int nCost, int nTokens, int nTokensPurchased, int nTokensSpent, string sGameName) 
{
    string sCharacterName = GetName(oPC);
    string sPlayerUsername = GetPCPlayerName(oPC);
    string sPCId = IntToString(PLAYER_GetId(oPC)); // TRUE for short version of CD key
    string sLogMessage = "Character: " + sCharacterName + ", Player Name: " + sPlayerUsername + " (PlayerID: "  + sPCId + ") bought " + IntToString(nTokensPurchased) + " token(s) for " + IntToString(nCost) + " gold and spent " + IntToString(nTokensSpent) + " token(s) in game " + sGameName + ". Total tokens now: " + IntToString(nTokens);
    Log(sLogMessage, "cas");
}


void AdjustCasValues(object oTarget, int nTokenChange, int nTicketChange)
{
    // Check if the target has the item I_CAS_HANDLE
    object oHandle = GetItemPossessedBy(oTarget, "I_CAS_CASINOHANDLE");
    if (GetIsObjectValid(oHandle))
    {
        // Get current values of nCAS_TOKEN and nCAS_TICKET from the I_CAS_CASINOHANDLE item
        int nCurrentTokens = GetLocalInt(oHandle, "nCAS_TOKEN");
        int nCurrentTickets = GetLocalInt(oHandle, "nCAS_TICKET");

        // Check if the new values would be less than zero
        if ((nCurrentTokens + nTokenChange) < 0 || (nCurrentTickets + nTicketChange) < 0)
        {
            SendMessageToPC(oTarget, "Cannot decrease values below zero.");
        }
        else
        {
            // Adjust the values on the I_CAS_CASINOHANDLE item
            nCurrentTokens += nTokenChange;
            nCurrentTickets += nTicketChange;

            SetLocalInt(oHandle, "nCAS_TOKEN", nCurrentTokens);
            SetLocalInt(oHandle, "nCAS_TICKET", nCurrentTickets);
        }
    }
    else
    {
        // Handle the case where the item is not found
        SendMessageToPC(oTarget, "You do not have the required item: I_CAS_CASINOHANDLE.");
    }
}

void EnsureTokenPouch(object oPC)
{
    // Check if the player has the I_CAS_HANDLE item
    object oHandle = GetItemPossessedBy(oPC, "I_CAS_CASINOHANDLE");

    // If the handle is not found, create it and send a message
    if (!GetIsObjectValid(oHandle))
    {
        CreateItemOnObject("cas_casinohandle", oPC); // Replace with the correct ResRef if different
        SendMessageToPC(oPC, "Giving you a token pouch to help you keep track of your winnings/tokens!");
    }
}



void CountTotalTokens(object oPC) 
{
    object oHandle = GetItemPossessedBy(oPC, "I_CAS_CASINOHANDLE");
    if (!GetIsObjectValid(oHandle)) 
    {
        SendMessageToPC(oPC, "You do not have a handle, creating.");
        CreateItemOnObject("cas_casinohandle", oPC);
        return;
    }

    int nTokens = GetLocalInt(oHandle, "nCAS_TOKEN");
    int nTickets = GetLocalInt(oHandle, "nCAS_TICKET");
    SendMessageToPC(oPC, "You currently have " + IntToString(nTokens) + " tokens and " + IntToString(nTickets) + " tickets.");
}


// Function to deduct tokens from multiple stacks
void DeductTokens(object oPC, int nTokensToDeduct) 
{
    object oHandle = GetItemPossessedBy(oPC, "I_CAS_CASINOHANDLE");
    if (!GetIsObjectValid(oHandle)) 
    {
        SendMessageToPC(oPC, "You do not have a handle.");
        return;
    }

    int nCurrentTokens = GetLocalInt(oHandle, "nCAS_TOKEN");
    if (nCurrentTokens >= nTokensToDeduct) 
    {
        SetLocalInt(oHandle, "nCAS_TOKEN", nCurrentTokens - nTokensToDeduct);
       // SendMessageToPC(oPC, "Deducted " + IntToString(nTokensToDeduct) + " tokens.");
    } 
    else 
    {
        SendMessageToPC(oPC, "Not enough tokens to deduct.");
    }
}


// Function to handle winning with a delay
void AwardPrize(object oPC, int nTickets, int nVFX, string sAnnouncement) 
{
    object oHandle = GetItemPossessedBy(oPC, "I_CAS_CASINOHANDLE");
    if (!GetIsObjectValid(oHandle)) 
    {
        SendMessageToPC(oPC, "You do not have a handle.");
        return;
    }

    int nCurrentTickets = GetLocalInt(oHandle, "nCAS_TICKET");
    int nNewTotal = nCurrentTickets + nTickets;
    SetLocalInt(oHandle, "nCAS_TICKET", nNewTotal);

    if (nVFX != VFX_NONE) 
    {
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nVFX), oPC);
    }

    if (sAnnouncement != "") 
    {
        SendMessageToPC(oPC, sAnnouncement);
    }

    SendMessageToPC(oPC, "You have been awarded " + IntToString(nTickets) + " tickets. New total: " + IntToString(nNewTotal) + " tickets.");
}




// Function to generate and send a random loss message
void SendRandomLoserMessage(object oPC, int nConsecutiveLosses) 
{
    string sMessage;
    int nMessageIndex = Random(6); // There are 6 regular loss messages

    // Select the message based on the number of consecutive losses
    if (nConsecutiveLosses >= 25) 
    {
        nMessageIndex += 14; // Offset for 25+ losses messages
    } 
    else if (nConsecutiveLosses >= 10) 
    {
        nMessageIndex += 8; // Offset for 10+ losses messages
    }

    // Determine which message to send
    switch (nMessageIndex) 
    {
        case 0: sMessage = "Shucks, try again, maybe your luck will be better!"; break;
        case 1: sMessage = "A portion of all profits are donated to a list of charities throughout the isles!"; break;
        case 2: sMessage = "Are you sure you didn't piss off Beshaba or something?"; break;
        case 3: sMessage = "I am sure a round of alcohol will make the burn of loss easier to bare."; break;
        case 4: sMessage = "I am sorry for your loss."; break;
        case 5: sMessage = "Oops, uh, maybe next?"; break;
        // Messages for 10 or more consecutive losses
        case 8: sMessage = "Are you sure you you wish to keep trying your luck? We got Wheel of Fire, you know.."; break;
        case 9: sMessage = "Are you sure you want to continue being Sinfar's biggest loser?"; break;
        case 10: sMessage = "I get it, you got money to spend, but uh- maybe take a break? Throw some salt over your shoulder?"; break;
        case 11: sMessage = "For someone so confident in winning, you sure are being proved wrong."; break;
        case 12: sMessage = "Ice Cream sounds good about now."; break;
        case 13: sMessage = "Jam, is this you? You can stop now, Arawinla is getting worried about your finances."; break;
        case 14: sMessage = "Circe was right, this was a mistake."; break;
        case 15: sMessage = "Natalya- you can quit now..."; break;
        // Messages for 25 or more consecutive losses
        case 20: sMessage = "Stop! Just stop, please, I beg you!"; break;
        case 21: sMessage = "Whelp...Circe was definitely right."; break;
        case 22: sMessage = "Mei is praying for you."; break;
        default: sMessage = "Better luck next time!"; // Fallback message
    }

    SendMessageToPC(oPC, sMessage);
}

void ExecuteSlotEffects(object oPC, int nSlot, int iCount) 
{
    int iVFX;
    string sPLC;
    switch (nSlot) 
    {
        case 1: // Purple
            iVFX = 409;
            sPLC = "cas_purple";
            break;
        case 2: // Blue
            iVFX = 410;
            sPLC = "cas_blue";
            break;
        case 3: // Red
            iVFX = 411;
            sPLC = "cas_red";
            break;
        case 4: // Yellow
            iVFX = 413;
            sPLC = "cas_yellow";
            break;
        case 5: // Green
            iVFX = 415;
            sPLC = "cas_green";
            break;
        case 6: // Orange
            iVFX = 417;
            sPLC = "cas_orange";
            break;
        default:
            return; // If the slot number is invalid, exit the function
    }

    vector vPos = GetPosition(OBJECT_SELF);
    location lLoc;
    vPos.z += 2.0;

    // Adjust the position based on iCount
    if (iCount == 1) 
    {
        vPos.x -= 0.8;
    } 
    else if (iCount == 3) 
    {
        vPos.x += 0.8;
    }
    lLoc = Location(GetArea(OBJECT_SELF), vPos, GetFacing(OBJECT_SELF));

    // Create the placeable and apply the visual effect
    object oPLC = CreateObject(OBJECT_TYPE_PLACEABLE, sPLC, lLoc);
    // Uncomment the next line if you want to apply the visual effect
    // ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(iVFX), oPLC);
    DestroyObject(oPLC, 10.0 - (iCount * 3.0)); // Destroy the object after a set time
}

// Function to handle the display of slot results with delays
void DisplaySlotResults(object oPC, int nSlot1, int nSlot2, int nSlot3, int nTokensBet, int nVFX, string sAnnouncement) 
{
    // Ensure losing sequences do not match winning combinations
    if (nTokensBet == 0) 
    {
        int bIsWinningCombo;
        do 
        {
            bIsWinningCombo = (nSlot1 == nSlot2) && (nSlot2 == nSlot3);
            if (bIsWinningCombo) 
            {
                nSlot1 = d6();
                nSlot2 = d6();
                nSlot3 = d6();
            }
        } 
        while (bIsWinningCombo);
    }

    // Calculate the ticket reward based on the bet and winning combination
    int nBaseTickets;
    float fMultiplier = 0.75 * nTokensBet; // 1.25 tickets per token bet

    if (nSlot1 == 1 && nSlot2 == 1 && nSlot3 == 1) 
    {
        nBaseTickets = 5; // Base tickets for combination 111
    } 
    else if (nSlot1 == 2 && nSlot2 == 2 && nSlot3 == 2) 
    {
        nBaseTickets = 10; // Base tickets for combination 222
    } 
    else if (nSlot1 == 3 && nSlot2 == 3 && nSlot3 == 3) 
    {
        nBaseTickets = 20; // Base tickets for combination 333
    } 
    else if (nSlot1 == 4 && nSlot2 == 4 && nSlot3 == 4) 
    {
        nBaseTickets = 50; // Base tickets for combination 444
    } 
    else if (nSlot1 == 5 && nSlot2 == 5 && nSlot3 == 5) 
    {
        nBaseTickets = 100; // Base tickets for combination 555
    } 
    else if (nSlot1 == 6 && nSlot2 == 6 && nSlot3 == 6) 
    {
        nBaseTickets = 500; // Base tickets for combination 666
    } 
    else 
    {
        nBaseTickets = 0; // No tickets for losing combinations
    }

    int nTotalTickets = FloatToInt(fMultiplier * nBaseTickets); // Calculate total tickets

    // Update announcement with the number of tokens earned if the player won
    if (nTotalTickets > 0) 
    {
        sAnnouncement += " You earned " + IntToString(nTotalTickets) + " Tickets!";
    }

    // Set the number of tickets awarded before any delays
    SetLocalInt(oPC, "nTicketsAwarded", nTotalTickets);

    // Display initial slot result and handle subsequent results/effects with delay
    string sMessage = "Slot Machine Result: " + IntToString(nSlot1) + " - ";
    SendMessageToPC(oPC, sMessage);
    ExecuteSlotEffects(oPC, nSlot1, 1);
    DelayCommand(3.0, SendMessageToPC(oPC, sMessage + IntToString(nSlot2) + " - "));
    DelayCommand(3.0, ExecuteSlotEffects(oPC, nSlot2, 2));
    DelayCommand(6.0, SendMessageToPC(oPC, sMessage + IntToString(nSlot2) + " - " + IntToString(nSlot3)));
    DelayCommand(6.0, ExecuteSlotEffects(oPC, nSlot3, 3));

    // Handle prize awarding or loss message after a delay
    if (nTotalTickets > 0) 
    {
        DelayCommand(10.0, AwardPrize(oPC, nTotalTickets, nVFX, sAnnouncement));
    } 
    else 
    {
        int nConsecutiveLosses = GetLocalInt(oPC, "CONSECUTIVE_LOSSES") + 1;
        SetLocalInt(oPC, "CONSECUTIVE_LOSSES", nConsecutiveLosses);
        DelayCommand(10.0, SendRandomLoserMessage(oPC, nConsecutiveLosses));
    }
}


/* void main ()
{
    
} 
*/
