////////////////////////////////////////////////////////////////////////////////
// lib_hos2_PC - PC Library Functions
// By Deva B. Winblood    March 23rd, 2008
////////////////////////////////////////////////////////////////////////////////
#include "prc_inc_racial"
#include "nbde_inc"
#include "lib_nais_tool"


////////////////////////////////
// PROTOTYPES
////////////////////////////////


string PC_GetPCID(object oPC);

string PC_GetTeamID(object oPC);

void PC_SetTeamID(object oPC,string sTeamID);

void PC_SetRace(object oPC,string sRace="");

string PC_GetRace(object oPC);

int PC_GetTeamNum(object oPC);

void PC_SetTeamNum(object oPC,int nNum);

void PC_SetPCState(object oPC,int nState);

int PC_GetPCState(object oPC);

void PC_SavePC(object oPC,int bQuiet=TRUE);

void PC_LoadPC(object oPC);

void PC_SaveInventory(object oPC);

void PC_RestoreInventory(object oPC);


// FILE: lib_hos2_pc           FUNCTION: Msg_Team()
// This will send a message to all team members. sColor will cause it to
// display in a specific color.  sSound will cause it to play a sound.
// If sTeamID is "" then it will use oSpeaker's team.
void Msg_Team(object oSpeaker,string sMsg,string sColor="",string sSound="",string sTeamID="",int bNameSpeaker=TRUE);


// FILE: lib_hos2_pc           FUNCTION: Msg_Players()
// This will send a message to all players.   sColor will cause it to display
// in a specific color.  sSound will cause it to play a sound.
void Msg_Players(object oSpeaker,string sMsg,string sColor="",string sSound="",int bNameSpeaker=FALSE);


// FILE: lib_hos2_pc            FUNCTION: PC_SetFriend()
// This will set the faction of nTeamNum to be a friend to this PC.
void PC_SetFriend(object oPC,int nTeamNum);


// FILE: lib_hos2_pc            FUNCTION: PC_SetEnemy()
// This will set the faction of nTeamNum to be an enemy to this PC.
void PC_SetEnemy(object oPC,int nTeamNum);


// FILE: lib_hos2_pc            FUNCTION: PC_SetNeutral()
// This will set the faction of nTeamNum to be neutral to this PC.
void PC_SetNeutral(object oPC,int nTeamNum);



////////////////////////////////
// FUNCTIONS
////////////////////////////////


string PC_GetPCID(object oPC)
{ // PURPOSE: Return PC ID
    return GetPCPublicCDKey(oPC,TRUE)+"_"+GetName(oPC);
} // PC_GetPCID()


string PC_GetTeamID(object oPC)
{ // PURPOSE: Return TeamID for PC
    if (!GetIsPC(oPC)) return GetLocalString(oPC,"sTeamID");
    string sDB=GetTag(GetModule())+"PC";
    string sTID=NBDE_GetCampaignString(sDB,PC_GetPCID(oPC)+"_TID",oPC);
    if (GetStringLength(sTID)<1)
    { // check local
        sTID=GetLocalString(oPC,"sTeamID");
        if (GetStringLength(sTID)>0)
        { // write to DB
            NBDE_SetCampaignString(sDB,PC_GetPCID(oPC)+"_TID",sTID,oPC);
        } // write to DB
    } // check local
    return sTID;
} // PC_GetTeamID()


void PC_SetTeamID(object oPC,string sTeamID)
{ // PURPOSE: Set the TeamID
    string sDB=GetTag(GetModule())+"PC";
    NBDE_SetCampaignString(sDB,PC_GetPCID(oPC)+"_TID",sTeamID,oPC);
} // PC_SetTeamID()


string PC_GetRace(object oPC)
{ // PURPOSE: Get Race
    string sDB=GetTag(GetModule())+"PC";
    string sRace=NBDE_GetCampaignString(sDB,PC_GetPCID(oPC)+"_RACE",oPC);
    if (GetStringLength(sRace)<1)
    { // set default race
        PC_SetRace(oPC);
        sRace=NBDE_GetCampaignString(sDB,PC_GetPCID(oPC)+"_RACE",oPC);
    } // set default race
    return sRace;
} // PC_GetRace()


void PC_SetRace(object oPC, string sRace = "")
{ 
    // PURPOSE: Set PC Race
    string sSet;
    string sDB = GetTag(GetModule()) + "PC";
    int nR;

    if (GetStringLength(sRace) < 1)
    { 
        // Set to defaults
        nR = MyPRCGetRacialType(oPC);
        
        // Fetch the race name from racialtypes.2da
        sSet = Get2DAString("racialtypes", "Name", nR);

        // If fetching fails, set a default fallback value
        if (GetStringLength(sSet) < 1)
        {
            sSet = "UNKNOWN";
        }

        // Store the race name in the database
        NBDE_SetCampaignString(sDB, PC_GetPCID(oPC) + "_RACE", sSet, oPC);
    }
    else
    { 
        // Set to specific race name provided
        NBDE_SetCampaignString(sDB, PC_GetPCID(oPC) + "_RACE", sRace, oPC);
    }
} 


/* void PC_SetRace(object oPC,string sRace="")
{ // PURPOSE: Set PC Race
    string sSet;
    string sDB=GetTag(GetModule())+"PC";
    int nR;
    if (GetStringLength(sRace)<1)
    { // set to defaults
        nR=MyPRCGetRacialType(oPC);
        if (nR==RACIAL_TYPE_ANIMAL) sSet="ANIMAL";
        else if (nR==RACIAL_TYPE_BEAST) sSet="BEAST";
        else if (nR==RACIAL_TYPE_CONSTRUCT) sSet="CONSTRUCT";
        else if (nR==RACIAL_TYPE_DRAGON) sSet="DRAGON";
        else if (nR==RACIAL_TYPE_DWARF) sSet="DWARF";
        else if (nR==RACIAL_TYPE_ELEMENTAL) sSet="ELEMENTAL";
        else if (nR==RACIAL_TYPE_ELF) sSet="ELF";
        else if (nR==RACIAL_TYPE_FEY) sSet="FEY";
        else if (nR==RACIAL_TYPE_GIANT) sSet="GIANT";
        else if (nR==RACIAL_TYPE_GNOME) sSet="GNOME";
        else if (nR==RACIAL_TYPE_HALFELF) sSet="HALFELF";
        else if (nR==RACIAL_TYPE_HALFLING) sSet="HALFLING";
        else if (nR==RACIAL_TYPE_HALFORC) sSet="HALFORC";
        else if (nR==RACIAL_TYPE_HUMAN) sSet="HUMAN";
        else if (nR==RACIAL_TYPE_HUMANOID_GOBLINOID) sSet="GOBLIN";
        else if (nR==RACIAL_TYPE_HUMANOID_MONSTROUS) sSet="MONSTROUS";
        else if (nR==RACIAL_TYPE_HUMANOID_ORC) sSet="ORC";
        else if (nR==RACIAL_TYPE_HUMANOID_REPTILIAN) sSet="REPTILIAN";
        else if (nR==RACIAL_TYPE_MAGICAL_BEAST) sSet="MAGICAL";
        else if (nR==RACIAL_TYPE_OOZE) sSet="OOZE";
        else if (nR==RACIAL_TYPE_OUTSIDER) sSet="OUTSIDER";
        else if (nR==RACIAL_TYPE_SHAPECHANGER) sSet="SHAPECHANGER";
        else if (nR==RACIAL_TYPE_UNDEAD) sSet="UNDEAD";
		else if (nR==RACIAL_TYPE_PLANT) sSet="PLANT";
        else if (nR==RACIAL_TYPE_VERMIN) sSet="VERMIN";
        NBDE_SetCampaignString(sDB,PC_GetPCID(oPC)+"_RACE",sSet,oPC);
    } // set to defaults
    else
    { // set to specific
        NBDE_SetCampaignString(sDB,PC_GetPCID(oPC)+"_RACE",sRace,oPC);
    } // set to specific
} // PC_SetRace() */


int PC_GetTeamNum(object oPC)
{ // PURPOSE: Get the team Number
    string sDB=GetTag(GetModule())+"PC";
    return NBDE_GetCampaignInt(sDB,PC_GetPCID(oPC)+"_TNUM",oPC);
} // PC_GetTeamNum()


void PC_SetTeamNum(object oPC,int nNum)
{ // PURPOSE: Set the team Number
    string sDB=GetTag(GetModule())+"PC";
    NBDE_SetCampaignInt(sDB,PC_GetPCID(oPC)+"_TNUM",nNum,oPC);
} // PC_SetTeamNum()


void PC_SetPCState(object oPC,int nState)
{ // PURPOSE: Set State
    string sDB=GetTag(GetModule())+"PC";
    NBDE_SetCampaignInt(sDB,PC_GetPCID(oPC)+"_PCS",nState,oPC);
} // PC_SetPCState()


int PC_GetPCState(object oPC)
{ // PURPOSE: Get State
    string sDB=GetTag(GetModule())+"PC";
    return NBDE_GetCampaignInt(sDB,PC_GetPCID(oPC)+"_PCS",oPC);
} // PC_GetPCState()


void PC_SavePC(object oPC,int bQuiet=TRUE)
{ // PURPOSE: Preserve the PC
    string sDB=GetTag(GetModule())+"PC";
    string sPCID=PC_GetPCID(oPC);
    NBDE_SetCampaignLocation(sDB,sPCID+"_LOC",GetLocation(oPC),oPC);
    NBDE_SetCampaignInt(sDB,sPCID+"_XP",GetXP(oPC),oPC);
    NBDE_SetCampaignInt(sDB,sPCID+"_HP",GetCurrentHitPoints(oPC),oPC);
    NBDE_SetCampaignInt(sDB,sPCID+"_GP",GetGold(oPC),oPC);
    NBDE_SetCampaignInt(sDB,sPCID+"_TN",PC_GetTeamNum(oPC),oPC);
    NBDE_SetCampaignString(sDB,sPCID+"_TID",PC_GetTeamID(oPC),oPC);
    if (!bQuiet) SendMessageToPC(oPC,"SAVED TO DATABASE...");
} // PC_SavePC()


void PC_LoadPC(object oPC)
{ // PURPOSE: Reset oPC to state in Database
    string sDB=GetTag(GetModule())+"PC";
    string sPCID=PC_GetPCID(oPC);
    location lLoc=NBDE_GetCampaignLocation(sDB,sPCID+"_LOC",oPC);
    object oLoc=CreateObject(OBJECT_TYPE_PLACEABLE,"pathing_object",lLoc);
    int nXP=NBDE_GetCampaignInt(sDB,sPCID+"_XP",oPC);
    int nHP=NBDE_GetCampaignInt(sDB,sPCID+"_HP",oPC);
    int nGP=NBDE_GetCampaignInt(sDB,sPCID+"_GP",oPC);
    int nTeamNum=NBDE_GetCampaignInt(sDB,sPCID+"_TN",oPC);
    string sTeamID=NBDE_GetCampaignString(sDB,sPCID+"_TID",oPC);
    int nN;
    SetLocalObject(oPC,"oShortPathDest",oLoc);
    SetLocalObject(oLoc,"oOwner",oPC);
    PC_SetTeamNum(oPC,nTeamNum);
    PC_SetTeamID(oPC,sTeamID);
    SendMessageToPC(oPC,"LOAD FROM DATABASE: Location '"+GetName(GetAreaFromLocation(lLoc))+"' XP:"+IntToString(nXP)+" HP:"+IntToString(nHP)+" GOLD:"+IntToString(nGP));
    SetXP(oPC,nXP);
    if (GetGold(oPC)!=nGP)
    { // reset gold
        nN=GetGold(oPC);
        if (nN>nGP)
        { // take gold
            AssignCommand(oPC,TakeGoldFromCreature(nN-nGP,oPC,TRUE));
        } // take gold
        else if (nN<nGP)
        { // give gold
            GiveGoldToCreature(oPC,nGP-nN);
        } // give gold
    } // reset gold
    if (GetCurrentHitPoints(oPC)!=nHP)
    { // reset hit points
        nN=GetCurrentHitPoints(oPC);
        if (nN>nHP)
        { // take damage
            ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDamage(nN-nHP),oPC);
        } // take damage
        else if (nN<nHP)
        { // heal damage
            ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectHeal(nHP-nN),oPC);
        } // heal damage
    } // reset hit points
    lib_ForceJump(oPC,oLoc);
} // PC_LoadPC()


void PC_supPreserveInv(object oPC,string sDB,string sPCID,int nC=0)
{ // PURPOSE: Preserve Inventory
    int nN;
    string sS;
    object oItem;
    if (nC==0) oItem=GetFirstItemInInventory(oPC);
    else { oItem=GetNextItemInInventory(oPC); }
    if (GetIsObjectValid(oItem))
    { // store
        NBDE_SetCampaignString(sDB,sPCID+"_sINVC"+IntToString(nC+1),GetResRef(oItem),oPC);
        NBDE_SetCampaignInt(sDB,sPCID+"_nINVCC"+IntToString(nC+1),GetItemCharges(oItem),oPC);
        NBDE_SetCampaignInt(sDB,sPCID+"_nINVCS"+IntToString(nC+1),GetItemStackSize(oItem),oPC);
        DelayCommand(0.1,PC_supPreserveInv(oPC,sDB,sPCID,nC+1));
    } // store
    else
    { // end
        NBDE_SetCampaignInt(sDB,sPCID+"_INVC",nC,oPC);
    } // end
} // PC_supPreservInv()


void PC_supStoreSlot(object oPC,string sDB,string sPCID,int nSlot,string sVar)
{ // PURPOSE: store slotted item
    object oItem;
    NBDE_DeleteCampaignString(sDB,sPCID+"_sIS"+sVar,oPC);
    NBDE_DeleteCampaignInt(sDB,sPCID+"_nIC"+sVar,oPC);
    NBDE_DeleteCampaignInt(sDB,sPCID+"_nIS"+sVar,oPC);
    oItem=GetItemInSlot(nSlot,oPC);
    if (GetIsObjectValid(oItem))
    { // preserve slotted item
        NBDE_SetCampaignString(sDB,sPCID+"_sIS"+sVar,GetResRef(oItem),oPC);
        NBDE_SetCampaignInt(sDB,sPCID+"_nIC"+sVar,GetItemCharges(oItem),oPC);
        NBDE_SetCampaignInt(sDB,sPCID+"_nIS"+sVar,GetItemStackSize(oItem),oPC);
    } // preserve slotted item
} // PC_supStoreSlot()


void PC_SaveInventory(object oPC)
{ // PURPOSE: Inventory Store
    string sDB=GetTag(GetModule())+"PC";
    string sPCID=PC_GetPCID(oPC);
    int nPrev=NBDE_GetCampaignInt(sDB,sPCID+"_INVC",oPC);
    int nN;
    object oItem;
    string sS;
    if (nPrev>0)
    { // delete previous save
        nN++;
        while(nN<=nPrev)
        { // delete older variables
            NBDE_DeleteCampaignString(sDB,sPCID+"_sINVC"+IntToString(nN),oPC);
            NBDE_DeleteCampaignInt(sDB,sPCID+"_nINVCC"+IntToString(nN),oPC);
            NBDE_DeleteCampaignInt(sDB,sPCID+"_nINVCS"+IntToString(nN),oPC);
            nN++;
        } // delete older variables
        NBDE_DeleteCampaignInt(sDB,sPCID+"_INVC",oPC);
    } // delete previous save
    DelayCommand(1.0,PC_supPreserveInv(oPC,sDB,sPCID));
    PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_ARMS,"ARM");
    DelayCommand(0.1,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_ARROWS,"ARR"));
    DelayCommand(0.2,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_BELT,"BEL"));
    DelayCommand(0.3,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_BOLTS,"BOL"));
    DelayCommand(0.4,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_BOOTS,"BOO"));
    DelayCommand(0.5,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_BULLETS,"BUL"));
    DelayCommand(0.6,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_CHEST,"CHE"));
    DelayCommand(0.7,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_CLOAK,"CLO"));
    DelayCommand(0.8,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_HEAD,"HEA"));
    DelayCommand(0.9,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_LEFTHAND,"LHA"));
    DelayCommand(1.0,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_LEFTRING,"LRI"));
    DelayCommand(1.1,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_NECK,"NEC"));
    DelayCommand(1.2,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_RIGHTHAND,"RHA"));
    DelayCommand(1.3,PC_supStoreSlot(oPC,sDB,sPCID,INVENTORY_SLOT_RIGHTRING,"RRI"));
} // PC_SaveInventory()


float PC_supCheckItem(object oPC,string sDB,string sPCID,int nSlot,string sVar)
{ // PURPOSE: Check specific item
    float fRet=0.0;
    object oItem;
    int nCharges;
    int nStack;
    string sResRef;
    sResRef=NBDE_GetCampaignString(sDB,sPCID+"_sIS"+sVar,oPC);
    if (GetStringLength(sResRef)>0)
    { // item exists
        fRet=0.1;
        nCharges=NBDE_GetCampaignInt(sDB,sPCID+"_nIC"+sVar,oPC);
        nStack=NBDE_GetCampaignInt(sDB,sPCID+"_nIS"+sVar,oPC);
        oItem=CreateItemOnObject(sResRef,oPC);
        if (nCharges>0) SetItemCharges(oItem,nCharges);
        if (nStack>0) SetItemStackSize(oItem,nStack);
        DelayCommand(0.03,AssignCommand(oPC,ActionEquipItem(oItem,nSlot)));
    } // item exists
    return fRet;
} // PC_supCheckItem()


void PC_supRestoreNormalInven(object oPC,string sDB,string sPCID,int nC=1)
{ // PURPOSE: Restore non-equipped inventory
    string sResRef;
    int nCharges;
    int nStack;
    object oItem;
    int nCount=NBDE_GetCampaignInt(sDB,sPCID+"_INVC",oPC);
    if (nC<=nCount)
    { // valid item
        sResRef=NBDE_GetCampaignString(sDB,sPCID+"_sINVC"+IntToString(nC),oPC);
        nCharges=NBDE_GetCampaignInt(sDB,sPCID+"_nINVCC"+IntToString(nC),oPC);
        nStack=NBDE_GetCampaignInt(sDB,sPCID+"_nINVCS"+IntToString(nC),oPC);
        oItem=CreateItemOnObject(sResRef,oPC);
        if (nCharges>0) SetItemCharges(oItem,nCharges);
        if (nStack>0) SetItemStackSize(oItem,nStack);
        DelayCommand(0.02,PC_supRestoreNormalInven(oPC,sDB,sPCID,nC+1));
    } // valid item
} // PC_supRestoreNormalInven()


void PC_supRestoreSlots(object oPC,string sDB,string sPCID)
{ // PURPOSE: Restore slots
    float fDelay=1.0;
    AssignCommand(oPC,ClearAllActions());
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_ARMS,"ARM");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_ARROWS,"ARR");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_BELT,"BEL");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_BOLTS,"BOL");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_BOOTS,"BOO");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_BULLETS,"BUL");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_CHEST,"CHE");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_CLOAK,"CLO");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_HEAD,"HEA");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_LEFTHAND,"LHA");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_LEFTRING,"LRI");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_NECK,"NEC");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_RIGHTHAND,"RHA");
    fDelay=fDelay+PC_supCheckItem(oPC,sDB,sPCID,INVENTORY_SLOT_RIGHTRING,"RRI");
    DelayCommand(0.1,SetCommandable(FALSE,oPC));
    DelayCommand(fDelay,SetCommandable(TRUE,oPC));
} // PC_supRestoreSlots()


void PC_supRestoreTools(object oPC)
{ // PURPOSE: Restore Tools
    object oItem=GetItemPossessedBy(oPC,"tool_it_create");
    if (!GetIsObjectValid(oItem)) oItem=CreateItemOnObject("tool_it_create",oPC);
} // PC_supRestoreTools()


void PC_RestoreInventory(object oPC)
{ // PURPOSE: Restore Inventory
    string sDB=GetTag(GetModule())+"PC";
    string sPCID=PC_GetPCID(oPC);
    lib_StripCreature(oPC);
    DelayCommand(3.0,PC_supRestoreSlots(oPC,sDB,sPCID));
    DelayCommand(5.0,PC_supRestoreNormalInven(oPC,sDB,sPCID));
    DelayCommand(10.0,PC_supRestoreTools(oPC));
} // PC_RestoreInventory()


void Msg_Team(object oSpeaker,string sMsg,string sColor="",string sSound="",string sTeamID="",int bNameSpeaker=TRUE)
{ // PURPOSE: The message will be sent to all team members.
    object oPC;
    object oSound;
    string sSend;
    string sTID=sTeamID;
    if (GetIsObjectValid(oSpeaker))
    { // valid speaker
        if (GetStringLength(sTID)<1) sTID=PC_GetTeamID(oSpeaker);
        if (GetStringLength(sTID)>0)
        { // team specified
            if (bNameSpeaker) sSend=lib_SetStringColor(GetName(oSpeaker)+" sends:","555");
            if (GetStringLength(sColor)==3) sSend=sSend+lib_SetStringColor(sMsg,sColor);
            else { sSend=sSend+sMsg; }
            oPC=GetFirstPC();
            while(GetIsObjectValid(oPC))
            { // check all PCs
                if (PC_GetTeamID(oPC)==sTID&&oPC!=oSpeaker)
                { // correct team
                    SendMessageToPC(oPC,sSend);
                    if (GetStringLength(sSound)>0)
                    { // play sound
                        oSound=CreateObject(OBJECT_TYPE_PLACEABLE,"sound_object",GetLocation(oPC));
                        SetLocalString(oSound,"sSound",sSound);
                    } // play sound
                } // correct team
                oPC=GetNextPC();
            } // check all PCs
        } // team specified
    } // valid speaker
} // Msg_Team()


void Msg_Players(object oSpeaker,string sMsg,string sColor="",string sSound="",int bNameSpeaker=FALSE)
{ // PURPOSE: Send a message to all players
    object oPC;
    object oSound;
    string sSend;
    if (GetIsObjectValid(oSpeaker))
    { // valid speaker
        if (bNameSpeaker) sSend=lib_SetStringColor(GetName(oSpeaker)+" sends:","555");
        if (GetStringLength(sColor)==3) sSend=sSend+lib_SetStringColor(sMsg,sColor);
        else { sSend=sSend+sMsg; }
        oPC=GetFirstPC();
        while(GetIsObjectValid(oPC))
        { // check all PCs
            if (oPC!=oSpeaker)
            { // send message here
                SendMessageToPC(oPC,sSend);
                if (GetStringLength(sSound)>0)
                { // play sound
                    oSound=CreateObject(OBJECT_TYPE_PLACEABLE,"sound_object",GetLocation(oPC));
                    SetLocalString(oSound,"sSound",sSound);
                } // play sound
            } // send message here
            oPC=GetNextPC();
        } // check all PCs
    } // valid speaker
} // Msg_Players()

//void main(){}