/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "cyMisc.h"

#include "plgDispatch.h"
#include "hsResMgr.h"
#include "plResMgr/plKeyFinder.h"

#include "pnKeyedObject/plKey.h"
#include "pnKeyedObject/plFixedKey.h"
#include "plMessage/plLinkToAgeMsg.h"
#include "plMessage/plConsoleMsg.h"
#include "plMessage/plAnimCmdMsg.h"
#include "plMessage/plExcludeRegionMsg.h"
#include "plMessage/plInputEventMsg.h"
#include "plMessage/plInputIfaceMgrMsg.h"
#include "pnMessage/plCmdIfaceModMsg.h"
#include "pnMessage/plAttachMsg.h"
#include "plMessage/plTimerCallbackMsg.h"
#include "plMessage/plNetVoiceListMsg.h"
#include "pnMessage/plClientMsg.h"
#include "pnMessage/plCameraMsg.h"
#include "pnTimer/plTimerCallbackManager.h"
#include "plVault/plVault.h"
#include "plNetClient/plNetClientMgr.h"
#include "plNetClient/plNetLinkingMgr.h"
#include "plNetTransport/plNetTransport.h"
#include "plNetTransport/plNetTransportMember.h"
#include "plResMgr/plKeyFinder.h"
#include "plAvatar/plAvatarMgr.h"
#include "plAvatar/plMultistageBehMod.h"
#include "plAvatar/plAvBrainCritter.h"
#include "pyCritterBrain.h"
#include "cyPythonInterface.h"
#include "pyKey.h"
#include "pySceneObject.h"
#include "pyPlayer.h"
#include "pyImage.h"
#include "pyDniCoordinates.h"
#include "pyDniInfoSource.h"
#include "pyColor.h"
#include "pyNetLinkingMgr.h"
#include "pyAgeInfoStruct.h"
#include "pyAgeLinkStruct.h"
#include "pyAlarm.h"
#include "pfMessage/pfKIMsg.h"
#include "plNetMessage/plNetMessage.h"
#include "pfCamera/plVirtualCamNeu.h"
#include "plPipeline/plDynamicEnvMap.h"

#include "pfGameGUIMgr/pfGameGUIMgr.h"
#include "pfGameGUIMgr/pfGUIDialogMod.h"
#include "pyGUIDialog.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"

#include "plMessage/plCCRMsg.h"
#include "plAgeLoader/plAgeLoader.h"

#include "plResMgr/plLocalization.h"
#include "plGLight/plLightInfo.h"

#include "plInputCore/plAvatarInputInterface.h"
#include "plInputCore/plInputDevice.h"

#include "plVault/plAgeInfoSource.h"

#include "pfLocalizationMgr/pfLocalizationMgr.h"

//// Static Class Stuff //////////////////////////////////////////////////////
plPipeline* cyMisc::fPipeline = nil;
UInt32  cyMisc::fUniqueNumber = 0;

#ifdef PLASMA_EXTERNAL_RELEASE
UInt32  cyMisc::fPythonLoggingLevel = cyMisc::kErrorLevel;
#else
UInt32  cyMisc::fPythonLoggingLevel = cyMisc::kWarningLevel;
#endif

/////////////////////////////////////////////////////////////////////////////
// static
void cyMisc::Update( double secs )
{
    // only update once per 1/2 sec
    static double lastUpdateTime = 0.0;
    if ( secs-lastUpdateTime>=0.5 )
    {
        lastUpdateTime = secs;
        pyAlarmMgr::GetInstance()->Update( secs );
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Get/SetDebugPrintLevel
//
//  PURPOSE    : gets and sets the python debug print level
//
UInt32 cyMisc::GetPythonLoggingLevel()
{
    return fPythonLoggingLevel;
}
void cyMisc::SetPythonLoggingLevel(UInt32 new_level)
{
    fPythonLoggingLevel = new_level;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Console
//  PARAMETERS : command   - string of console commmand to execute
//
//  PURPOSE    : Execute a console command from a python script
//
void cyMisc::Console(const char* command)
{
    if ( command != nil )
    {
        // create message to send to the console
        plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
        pMsg->SetBCastFlag(plMessage::kBCastByType);
        pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
        pMsg->SetControlActivated(true);
        pMsg->SetCmdString(command);
        plgDispatch::MsgSend( pMsg );   // whoosh... off it goes
    }
}

void cyMisc::ConsoleNet(const char* command, hsBool netForce)
{
    if ( command != nil )
    {
        // create message to send to the console
        plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
        pMsg->SetBCastFlag(plMessage::kBCastByType);
        pMsg->SetBCastFlag(plMessage::kNetPropagate);
        if ( netForce )
            pMsg->SetBCastFlag(plMessage::kNetForce);
        pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
        pMsg->SetControlActivated(true);
        pMsg->SetCmdString(command);
        plgDispatch::MsgSend( pMsg );   // whoosh... off it goes
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : FindSceneObject
//  PARAMETERS : name   - string of name of the sceneobject
//             : ageName - string of the name of the age to look in
//
//  PURPOSE    : Execute a console command from a python script,
//                  optionally propagate over the net
//
PyObject* cyMisc::FindSceneObject(const char* name, const char* ageName)
{
    // assume that we won't find the sceneobject (key is equal to nil)
    plKey key=nil;

    if ( name || name[0] != 0)
    {
        const char* theAge = ageName;
        if ( ageName[0] == 0 )
            theAge = nil;
        key=plKeyFinder::Instance().StupidSearch(theAge,nil,plSceneObject::Index(), name, false);
    }

    if ( key == nil )
    {
        char errmsg[256];
        sprintf(errmsg,"Sceneobject %s not found",name);
        PyErr_SetString(PyExc_NameError, errmsg);
        return nil; // return nil cause we errored
    }
    return pySceneObject::New(key);
}

PyObject* cyMisc::FindActivator(const char* name)
{
    plKey key = nil;
    if (name && strlen(name) > 0)
    {
        std::vector<plKey> keylist;
        plKeyFinder::Instance().ReallyStupidActivatorSearch(name, keylist);

        if (keylist.size() == 1)
            key = keylist[0];
    }

    if (key == nil)
    {
        PYTHON_RETURN_NONE;
    }
    else
        return pyKey::New(key);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : PopUpConsole
//  PARAMETERS : command   - string of console commmand to execute
//
//  PURPOSE    : Execute a console command from a python script
//
void cyMisc::PopUpConsole(const char* command)
{
    if ( command != nil )
    {
        // create message to send to the console
        plControlEventMsg* pMsg1 = TRACKED_NEW plControlEventMsg;
        pMsg1->SetBCastFlag(plMessage::kBCastByType);
        pMsg1->SetControlCode(B_SET_CONSOLE_MODE);
        pMsg1->SetControlActivated(true);
        plgDispatch::MsgSend( pMsg1 );  // whoosh... off it goes
        // create message to send to the console
        plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg;
        pMsg2->SetBCastFlag(plMessage::kBCastByType);
        pMsg2->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
        pMsg2->SetControlActivated(true);
        pMsg2->SetCmdString(command);
        plgDispatch::MsgSend( pMsg2 );  // whoosh... off it goes
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : TimerCallback
//  PARAMETERS : command   - string of console commmand to execute
//
//  PURPOSE    : Execute a console command from a python script
//
void cyMisc::TimerCallback(pyKey& selfkey, hsScalar time, UInt32 id)
{
    // setup the message to come back to whoever the pyKey is pointing to
    plTimerCallbackMsg* pTimerMsg = TRACKED_NEW plTimerCallbackMsg(selfkey.getKey(),id);
    plgTimerCallbackMgr::NewTimer( time, pTimerMsg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearTimerCallbacks
//  PARAMETERS : key of object to clear callbacks to
//
//  PURPOSE    : clear timer callbacks to a certain key
//
void cyMisc::ClearTimerCallbacks(pyKey& selfkey)
{
    plgTimerCallbackMgr::CancelCallbacksToKey(selfkey.getKey());
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AttachObject
//  PARAMETERS : child object
//             :   to be attached to parent object
//
//  PURPOSE    : Attach an object to another object, knowing only their pyKeys
//
void cyMisc::AttachObject(pyKey& ckey, pyKey& pkey)
{
    plKey childKey = ckey.getKey();
    plKey parentKey = pkey.getKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to attach the child
        plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRequest);
        plgDispatch::MsgSend( pMsg );
    }
}
void cyMisc::AttachObjectSO(pySceneObject& cobj, pySceneObject& pobj)
{
    plKey childKey = cobj.getObjKey();
    plKey parentKey = pobj.getObjKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to attach the child
        plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRequest);
        plgDispatch::MsgSend( pMsg );
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : DetachObject
//  PARAMETERS : child object
//             :   to be attached to parent object
//
//  PURPOSE    : Attach an object to another object, knowing only their pyKeys
//
void cyMisc::DetachObject(pyKey& ckey, pyKey& pkey)
{
    plKey childKey = ckey.getKey();
    plKey parentKey = pkey.getKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to detach the child
        plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRemove);
        plgDispatch::MsgSend( pMsg );
    }
}
void cyMisc::DetachObjectSO(pySceneObject& cobj, pySceneObject& pobj)
{
    plKey childKey = cobj.getObjKey();
    plKey parentKey = pobj.getObjKey();

    // make sure that there was a child ket
    if ( childKey )
    {
        // create the attach message to detach the child
        plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRemove);
        plgDispatch::MsgSend( pMsg );
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LinkToAge
//  PARAMETERS : 
//
//  PURPOSE    : LinkToAge
//
//  STATUS     : Depreciated. Use plNetLinkingMgr or pyNetLinkingMgr instead.
//

//void cyMisc::LinkToAge(pyKey &selfkey, const char *AgeName,const char *SpawnPointName)
//{
//  // find the Modifier that called us
//  hsStatusMessage("PY: LinkToAge\n");
//      // Ask the Modifier if it was Local or Network
//  if (selfkey.WasLocalNotify())
//  {
//      hsStatusMessage("PY:LOCAL NOTIFY\n");
//      plNetLinkingMgr::GetInstance()->LinkToPublicAge( AgeName, SpawnPointName );
//  }
//  else
//  {
//      hsStatusMessage("PY:REMOTE NOTIFY\n");
//  }
//}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetDirtySyncStateServer
//  PARAMETERS : 
//
//  PURPOSE    : set the Python modifier to be dirty and asked to be saved out
//
void cyMisc::SetDirtySyncState(pyKey &selfkey, const char* SDLStateName, UInt32 sendFlags)
{
    selfkey.DirtySynchState(SDLStateName, sendFlags);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetDirtySyncStateClients
//  PARAMETERS : 
//
//  PURPOSE    : set the Python modifier to be dirty and asked to be saved out
//
void cyMisc::SetDirtySyncStateWithClients(pyKey &selfkey, const char* SDLStateName, UInt32 sendFlags)
{
    selfkey.DirtySynchState(SDLStateName, sendFlags|plSynchedObject::kBCastToClients);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : EnableControlKeyEvents & DisableControlKeyEvents
//  PARAMETERS : none
//
//  PURPOSE    : register and unregister for control key events
//
void cyMisc::EnableControlKeyEvents(pyKey &selfkey)
{
    selfkey.EnableControlKeyEvents();
}

void cyMisc::DisableControlKeyEvents(pyKey &selfkey)
{
    selfkey.DisableControlKeyEvents();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetClientName
//  PARAMETERS : avatar key
//
//  PURPOSE    : Return the net client (account) name of the player whose avatar
//              key is provided.
//
hsBool cyMisc::WasLocallyNotified(pyKey &selfkey)
{
    return selfkey.WasLocalNotify();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetClientName
//  PARAMETERS : avatar key
//
//  PURPOSE    : Return the net client (account) name of the player whose avatar
//              key is provided.
//
const char* cyMisc::GetClientName(pyKey &avKey)
{
    const char* ret=plNetClientMgr::GetInstance()->GetPlayerName(avKey.getKey());
    return (ret==nil) ? "" : ret;
}

PyObject* cyMisc::GetAvatarKeyFromClientID(int clientID)
{
    PyObject* keyObj = NULL;

    if (clientID == plNetClientMgr::GetInstance()->GetPlayerID())
    {
        keyObj = pyKey::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    }
    else
    {
        plNetTransportMember **members = nil;
        plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );

        if( members != nil)
        {
            for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
            {
                plNetTransportMember *mbr = members[ i ];
                if( mbr != nil && mbr->GetAvatarKey() != nil && mbr->GetPlayerID() == clientID)
                {   
                    keyObj = pyKey::New(mbr->GetAvatarKey());
                    break;
                }
            }
        }

        delete [] members;
    }

    if (keyObj)
        return keyObj;
    else
        PYTHON_RETURN_NONE;
}


int cyMisc::GetClientIDFromAvatarKey(pyKey& avatar)
{
    int ret = -1;

    if (plNetClientMgr::GetInstance()->GetLocalPlayerKey() == avatar.getKey())
    {
        return (plNetClientMgr::GetInstance()->GetPlayerID());
    }
    plNetTransportMember **members = nil;
    plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );
    if( members != nil)
    {
        for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
        {
            plNetTransportMember *mbr = members[ i ];

            if( mbr != nil && mbr->GetAvatarKey() == avatar.getKey())
            {   
                ret = mbr->GetPlayerID();
                break;
            }
        }
    }

    delete [] members;
    return ret;
}

int cyMisc::GetLocalClientID()
{
    return (plNetClientMgr::GetInstance()->GetPlayerID());
}

hsBool cyMisc::ValidateKey(pyKey& key)
{
    plKey pKey = key.getKey();
    
    if (pKey && pKey->ObjectIsLoaded())
        return true;
        
    return false;
}



/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetClientName
//  PARAMETERS : 
//
//  PURPOSE    : Return the local net client (account) name
//
const char* cyMisc::GetLocalClientName()
{
    return plNetClientMgr::GetInstance()->GetPlayerName();
}


//
// Get Current age information - DEPRECIATED. Use ptDniInfoSource() object instead
//
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetAgeName
//  Function   : GetAgeTime
//  Function   : GetAgeGuid
//  PARAMETERS : 
//
//  PURPOSE    : Return the age name of the current age the local player is in
//             : Return the current coordinates of the player within this age
//             : Return the current time with the current age the player is in
//             : Return the current guid of the instance of the age the player is in
//

const char * cyMisc::GetAgeName()
{
    return NetCommGetAge()->ageDatasetName;
}

PyObject* cyMisc::GetAgeInfo()
{
    plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance();
    if (nmgr)
    {
        plAgeLinkStruct* als = nmgr->GetAgeLink();
        if (als)
            return pyAgeInfoStruct::New(als->GetAgeInfo());
    }
    PYTHON_RETURN_NONE; // return none, not nil (cause it isn't really an error... or is it?)
}


const char* cyMisc::GetPrevAgeName()
{
    plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance();
    if (nmgr)
    {
        plAgeLinkStruct* als = nmgr->GetPrevAgeLink();
        if (als)
            return als->GetAgeInfo()->GetAgeFilename();
    }
    return nil;
}

PyObject* cyMisc::GetPrevAgeInfo()
{
    plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance();
    if (nmgr)
    {
        plAgeLinkStruct* als = nmgr->GetPrevAgeLink();
        if (als)
            return pyAgeInfoStruct::New(als->GetAgeInfo());
    }
    PYTHON_RETURN_NONE; // return none, not nil (cause it isn't really an error... or is it?)
}

// current time in current age
UInt32 cyMisc::GetAgeTime( void )
{
    return VaultAgeGetAgeTime();
}



UInt32 cyMisc::GetDniTime(void)
{
    const plUnifiedTime utime = plNetClientMgr::GetInstance()->GetServerTime();
    if ( utime.GetSecs() != 0)
        return ConvertGMTtoDni(utime.GetSecs());
    else
        return 0;
}

UInt32 cyMisc::GetServerTime(void)
{
    const plUnifiedTime utime = plNetClientMgr::GetInstance()->GetServerTime();
    return utime.GetSecs();
}

float cyMisc::GetAgeTimeOfDayPercent(void)
{
    return plNetClientMgr::GetInstance()->GetCurrentAgeTimeOfDayPercent();
}

#define kMST (UInt32)25200
#define kOneHour (UInt32)3600
#define kOneDay (UInt32)86400

UInt32 cyMisc::ConvertGMTtoDni(UInt32 gtime)
{
    // convert to mountain time
    UInt32 dtime = gtime - kMST;
    plUnifiedTime utime = plUnifiedTime();
    utime.SetSecs(dtime);
    // check for daylight savings time in New Mexico and adjust
    if ( utime.GetMonth() >= 4 && utime.GetMonth() < 11 )
    {
        plUnifiedTime dstStart = plUnifiedTime();
        dstStart.SetGMTime(utime.GetYear(),4,1,2,0,0);
        // find first Sunday after 4/1 (first sunday of April)
        UInt32 days_to_go = 7 - dstStart.GetDayOfWeek();
        if (days_to_go == 7)
            days_to_go = 0;
        UInt32 dstStartSecs = dstStart.GetSecs() + days_to_go * kOneDay;

        plUnifiedTime dstEnd = plUnifiedTime();
        dstEnd.SetGMTime(utime.GetYear(),10,25,1,0,0);
        // find first sunday after 10/25 (last sunday of Oct.)
        days_to_go = 7 - dstEnd.GetDayOfWeek();
        if (days_to_go == 7)
            days_to_go = 0;
        UInt32 dstEndSecs = dstEnd.GetSecs() + days_to_go * kOneDay;

        if ( dtime > dstStartSecs && dtime < dstEndSecs )
            // add hour for daylight savings time
            dtime += kOneHour;
    }

    return dtime;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ExcludeRegionSet
//  PARAMETERS : key - of the exclude region, ie. where to send the message
//               state  - what state of to set at:
//                        0 = release
//                        1 = clear
//
//  PURPOSE    : Sets the state of an exclude region
//
void cyMisc::ExcludeRegionSet(pyKey& sender, pyKey& exKey, UInt16 state)
{
    plExcludeRegionMsg *msg = TRACKED_NEW plExcludeRegionMsg;

    switch (state)
    {
        case kExRegClear:
            msg->SetCmd(plExcludeRegionMsg::kClear);
            break;
        case kExRegRelease:
            msg->SetCmd(plExcludeRegionMsg::kRelease);
            break;
    }
    msg->SetSender(sender.getKey());
    msg->AddReceiver(exKey.getKey());
    plgDispatch::MsgSend( msg );    // whoosh... off it goes
}

void cyMisc::ExcludeRegionSetNow(pyKey& sender, pyKey& exKey, UInt16 state)
{
    plExcludeRegionMsg *msg = TRACKED_NEW plExcludeRegionMsg;

    switch (state)
    {
        case kExRegClear:
            msg->SetCmd(plExcludeRegionMsg::kClear);
            break;
        case kExRegRelease:
            msg->SetCmd(plExcludeRegionMsg::kRelease);
            break;
    }
    msg->SetSender(sender.getKey());
    msg->AddReceiver(exKey.getKey());
    msg->fSynchFlags = plSynchedObject::kSendImmediately;
    plgDispatch::MsgSend( msg );    // whoosh... off it goes
}

#include "hsTimer.h"
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetSeconds
//  PARAMETERS : 
//
//  PURPOSE    : Return the nunber of seconds elapsed
//
double cyMisc::GetSeconds()
{
    return hsTimer::GetSeconds();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetSysSeconds
//  PARAMETERS : 
//
//  PURPOSE    : Return the number of system seconds elapsed
//
double cyMisc::GetSysSeconds()
{
    return hsTimer::GetSysSeconds();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetDelSysSeconds
//  PARAMETERS : 
//
//  PURPOSE    : Return the frame delta seconds
//
hsScalar cyMisc::GetDelSysSeconds()
{
    return hsTimer::GetDelSysSeconds();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LoadDialog
//  PARAMETERS : 
//
//  PURPOSE    : Return the frame delta seconds
//
void cyMisc::LoadDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        if ( !mgr->IsDialogLoaded(name) )
            mgr->LoadDialog( name );
    }
}

// Load dialog and set the GUINotifyMsg receiver key
void cyMisc::LoadDialogK(const char* name, pyKey& rKey)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // has the dialog been loaded yet?
        if ( !mgr->IsDialogLoaded(name) )
            // no then load and set handler
            mgr->LoadDialog( name, rKey.getKey() );
        else
            // yes then just set the handler
            mgr->SetDialogToNotify(name,rKey.getKey());
    }
}

// Load dialog and set the GUINotifyMsg receiver key
void cyMisc::LoadDialogKA(const char* name, pyKey& rKey, const char* ageName)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // has the dialog been loaded yet?
        if ( !mgr->IsDialogLoaded(name) )
            // no then load and set handler
            mgr->LoadDialog( name, rKey.getKey(), ageName );
        else
            // yes then just set the handler
            mgr->SetDialogToNotify(name,rKey.getKey());
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : UnLoadDialog
//  PARAMETERS : 
//
//  PURPOSE    : UnLoads the dialog by name
//             : optionally sets the receiver key for the GUINotifyMsg
//
void cyMisc::UnloadDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        if ( mgr->IsDialogLoaded(name) )
            mgr->UnloadDialog( name );
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IsDialogLoaded
//  PARAMETERS : 
//
//  PURPOSE    : Test to see if a dialog is loaded (according to the dialog manager)
//
hsBool cyMisc::IsDialogLoaded(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
        return mgr->IsDialogLoaded(name);
    return false;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ShowDialog
//  Function   : HideDialog
//  PARAMETERS : 
//
//  PURPOSE    : Show or Hide a dialog by name
//
void cyMisc::ShowDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
        mgr->ShowDialog(name);
}
void cyMisc::HideDialog(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
        mgr->HideDialog(name);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetDialogFromTagID
//  PARAMETERS : 
//
//  PURPOSE    : Return the frame delta seconds
//
PyObject* cyMisc::GetDialogFromTagID(UInt32 tag)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // get the owner dialog modifier pointer
        pfGUIDialogMod* pdialog = mgr->GetDialogFromTag(tag);
        if ( pdialog )
            return pyGUIDialog::New(pdialog->GetKey());
    }

    char errmsg[256];
    sprintf(errmsg,"GUIDialog TagID %d not found",tag);
    PyErr_SetString(PyExc_KeyError, errmsg);
    return nil; // return nil, cause we threw an error
}

PyObject* cyMisc::GetDialogFromString(const char* name)
{
    pfGameGUIMgr    *mgr = pfGameGUIMgr::GetInstance();
    if ( mgr )
    {
        // get the owner dialog modifier pointer
        pfGUIDialogMod* pdialog = mgr->GetDialogFromString(name);
        if ( pdialog )
            return pyGUIDialog::New(pdialog->GetKey());
    }

    char errmsg[256];
    sprintf(errmsg,"GUIDialog %s not found",name);
    PyErr_SetString(PyExc_KeyError, errmsg);
    return nil; // return nil, cause we threw an error
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IsGUIModal
//  PARAMETERS : 
//
//  PURPOSE    : Returns true if the GUI is currently modal (and therefore blocking input)
//
bool cyMisc::IsGUIModal()
{
    pfGameGUIMgr* mgr = pfGameGUIMgr::GetInstance();
    if (mgr)
        return mgr->IsModalBlocking();
    return false;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetLocalAvatar
//  Function   : GetLocalPlayer
//  PARAMETERS : 
//
//  PURPOSE    : Return a pySceneobject of the local Avatar
//             : Player - returns ptPlayer object
//
PyObject* cyMisc::GetLocalAvatar()
{
    plSceneObject *so = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer());
    if ( so )
        return pySceneObject::New(so->GetKey());

    char errmsg[256];
    sprintf(errmsg,"Local avatar not found");
    PyErr_SetString(PyExc_NameError, errmsg);
    return nil; // returns nil, cause we threw an error
}

PyObject* cyMisc::GetLocalPlayer()
{
    return pyPlayer::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(),
                        plNetClientMgr::GetInstance()->GetPlayerName(),
                        plNetClientMgr::GetInstance()->GetPlayerID(),
                        0.0 );
}



#if 1
#include "plStatusLog/plStatusLog.h"
//
// TEMP SCREEN PRINT CODE FOR NON-DBG TEXT DISPLAY
//
void cyMisc::PrintToScreen(const char* msg)
{
    static plStatusLog* gStatusLog=nil;
    if (gStatusLog==nil)
    {
        gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog( 32, "", 
            plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground );
        plStatusLogMgr::GetInstance().ToggleStatusLog(gStatusLog);
    }
    gStatusLog->AddLine(msg, plStatusLog::kBlue);   
}
#endif

#include "plPipeline.h"
#include "plGImage/plMipmap.h"

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetPlayerList
//  Function   : GetPlayerListDistanceSorted
//  PARAMETERS : 
//
//  PURPOSE    : Get a list of players (other than self) that are playing the game
//             : optionally get it sorted by distance
//
//  RETURNS    : Python object list of pyPlayer s
//
std::vector<PyObject*> cyMisc::GetPlayerList()
{
    std::vector<PyObject*> pyPL;
    plNetClientMgr* nc=plNetClientMgr::GetInstance();

    if (!nc) // only ever really happens if they try to call us in max... I hope
        return pyPL;

    int i;
    for( i = 0; i < nc->TransportMgr().GetNumMembers(); i++ )
    {
        plNetTransportMember *mbr = nc->TransportMgr().GetMember(i);
        plKey avkey = mbr->GetAvatarKey();
        if (avkey)
        {
            // only non-ignored people in list and not in ignore list
            if ( !VaultAmIgnoringPlayer ( mbr->GetPlayerID()) )
            {
                PyObject* playerObj = pyPlayer::New(avkey, mbr->GetPlayerName(), mbr->GetPlayerID(), mbr->GetDistSq());
                pyPlayer* player = pyPlayer::ConvertFrom(playerObj); // accesses internal pyPlayer object

                // modifies playerObj
                if ( mbr->IsCCR() )
                    player->SetCCRFlag(true);
                if ( mbr->IsServer() )
                    player->SetServerFlag(true);
                
                pyPL.push_back(playerObj);
            }
        }
    }
    return pyPL;
}

std::vector<PyObject*> cyMisc::GetPlayerListDistanceSorted()
{
    std::vector<PyObject*> pyPL;

    // get the sorted member list from the Net transport manager
    plNetTransportMember **members = nil;
    plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members );
    if( members != nil )
    {
        int i;
        for( i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ )
        {
            plNetTransportMember *mbr = members[ i ];
            plKey avkey = mbr->GetAvatarKey();
            if (avkey)
            {
                // only non-ignored people in list and not in ignore list
                if ( !VaultAmIgnoringPlayer ( mbr->GetPlayerID()) )
                {
                    PyObject* playerObj = pyPlayer::New(avkey, mbr->GetPlayerName(), mbr->GetPlayerID(), mbr->GetDistSq());
                    pyPlayer* player = pyPlayer::ConvertFrom(playerObj); // accesses internal pyPlayer object

                    // modifies playerObj
                    if ( mbr->IsCCR() )
                        player->SetCCRFlag(true);
                    if ( mbr->IsServer() )
                        player->SetServerFlag(true);

                    pyPL.push_back(playerObj);
                }
            }
        }
        delete [] members;
    }
    return pyPL;
}

UInt32 cyMisc::GetMaxListenListSize()
{
    return plNetListenList::kMaxListenListSize;
}

hsScalar cyMisc::GetMaxListenDistSq()
{
    return plNetListenList::kMaxListenDistSq;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendRTChat
//  PARAMETERS : from   - is a pyPlayer of the person who is sending this
//             : tolist  - is a python list object, if empty then broadcast message
//             : message  - text string to send to others
//             : flags   - the flags of destiny... whatever that means
//
//  PURPOSE    : To send a real time chat message to a particualr user, a list of users
//             : or broadcast it to everyone (within hearing distance?)
//
//  RETURNS    : the flags that were sent with the message (may be modified)
//
UInt32 cyMisc::SendRTChat(pyPlayer& from, const std::vector<pyPlayer*> & tolist, const char* message, UInt32 flags)
{
    // create the messge that will contain the chat message
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kHACKChatMsg );
    msg->SetString( message );
    msg->SetUser( from.GetPlayerName(), from.GetPlayerID() );
    msg->SetFlags( flags );
    msg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce);
    msg->SetBCastFlag(plMessage::kLocalPropagate, 0);

    if (tolist.size() > 0)
    {
        if (flags & 8/* kRTChatInterAge in PlasmaKITypes.py */)
        {
            // allow inter-age routing of this msg
            msg->SetBCastFlag( plMessage::kNetAllowInterAge );
        }
        // add net rcvrs to msg
        int i;
        for ( i=0 ; i<tolist.size() ; i++ )
        {
            if ( !VaultAmIgnoringPlayer( tolist[i]->GetPlayerID() ) )
                msg->AddNetReceiver(tolist[i]->GetPlayerID());
        }
    }

    UInt32 msgFlags = msg->GetFlags();

    if (tolist.size() == 0 || (msg->GetNetReceivers() && msg->GetNetReceivers()->size() > 0))
        msg->Send();

    return msgFlags;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessage
//  PARAMETERS : command   - the command type
//             : value     - extra value
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void cyMisc::SendKIMessage(UInt32 command, hsScalar value)
{
    // create the mesage to send
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( (UInt8)command );

    // check to see if the value makes any sense
    if ( command == pfKIMsg::kSetChatFadeDelay )
    {
        msg->SetDelay(value);
    }
    else if ( command == pfKIMsg::kSetTextChatAdminMode )
    {
        msg->SetFlags( value==1.0f ? pfKIMsg::kAdminMsg : 0 );
    }
    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessageS
//  PARAMETERS : command   - the command type
//             : value     - extra value as a string
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void cyMisc::SendKIMessageS(UInt32 command, const wchar_t* value)
{
    // create the mesage to send
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( (UInt8)command );

    msg->SetString( value );

    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessageI
//  PARAMETERS : command   - the command type
//             : value     - extra value as an Int32
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void  cyMisc::SendKIMessageI(UInt32 command, Int32 value)
{
    // create the mesage to send
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( (UInt8)command );

    msg->SetIntValue(value);

    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendKIMessageIReply
//  PARAMETERS : command   - the command type
//             : value     - extra value as an Int32
//
//  PURPOSE    : Send message to the KI, to tell it things to do
//
//  RETURNS    : nothing
//
void  cyMisc::SendKIGZMarkerMsg(Int32 markerNumber, pyKey& sender)
{
    // create the mesage to send
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kGZInRange );

    msg->SetIntValue(markerNumber);
    msg->SetSender(sender.getKey());

    // send it off
    plgDispatch::MsgSend( msg );
}

void cyMisc::SendKIRegisterImagerMsg(const char* imagerName, pyKey& sender)
{
    pfKIMsg *msg = TRACKED_NEW pfKIMsg(pfKIMsg::kRegisterImager);

    msg->SetString(imagerName);
    msg->SetSender(sender.getKey());

    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : YesNoDialog
//  PARAMETERS : sender    - who set this and will get the notify
//             : message   - message to put up in YesNo dialog
//
//  PURPOSE    : Put up Yes/No dialog
//
//  RETURNS    : nothing
//

void cyMisc::YesNoDialog(pyKey& sender, const char* thestring)
{
    // create the mesage to send
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kYesNoDialog );

    msg->SetSender(sender.getKey());
    msg->SetString(thestring);
    // send it off
    plgDispatch::MsgSend( msg );
}

void cyMisc::YesNoDialog(pyKey& sender, std::wstring thestring)
{
    char *temp = hsWStringToString(thestring.c_str());
    YesNoDialog(sender,temp);
    delete [] temp;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : RateIt
//  PARAMETERS : chonicleName    - where to store the rating
//             : thestring       - the message in the RateIt dialog
//             : onceFlag        - flag to tell whether this is a onetime thing or ask everytime
//
//  PURPOSE    : Send message to the KI to tell it to ask the user to Rate something
//
//  RETURNS    : nothing
//
void cyMisc::RateIt(const char* chronicleName, const char* thestring, hsBool onceFlag)
{
    // create the mesage to send
    pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kRateIt );

    msg->SetUser(chronicleName,0);
    msg->SetString(thestring);
    msg->SetIntValue(onceFlag);
    // send it off
    plgDispatch::MsgSend( msg );
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetPrivateChatList
//  PARAMETERS : key            - who's joining
//
//  PURPOSE    : Lock the local avatar into private vox messaging, and / or add new memebers to his chat list
//
//  RETURNS    : nothing
//

void cyMisc::SetPrivateChatList(const std::vector<pyPlayer*> & tolist)
{
    if (tolist.size() > 0)
    {
        plNetVoiceListMsg* pMsg = TRACKED_NEW plNetVoiceListMsg(plNetVoiceListMsg::kForcedListenerMode);
        for (int i=0 ; i<tolist.size() ; i++ )
            pMsg->GetClientList()->Append(tolist[i]->GetPlayerID());
        
        pMsg->SetRemovedKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
        pMsg->Send();
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearPrivateChatList
//  PARAMETERS : key            - who's leaving
//
//  PURPOSE    : Remove the local avatar from private vox messaging, and / or clear memebers from his chat list
//
//  RETURNS    : nothing
//
void cyMisc::ClearPrivateChatList(pyKey& member)
{
    plNetVoiceListMsg* pMsg = TRACKED_NEW plNetVoiceListMsg(plNetVoiceListMsg::kDistanceMode);
    pMsg->SetRemovedKey( member.getKey() );
    pMsg->Send();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearCameraStack
//  PURPOSE    : clear the camera stack
//
//  RETURNS    : nothing
//

void cyMisc::ClearCameraStack()
{
    plVirtualCam1::Instance()->ClearStack();
}

bool cyMisc::IsFirstPerson()
{
    return (plVirtualCam1::Instance()->Is1stPersonCamera()!=0);
}
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendPetitionToCCR
//  PARAMETERS : message  - message to send to the CCR ("please help me!")
//
//  PURPOSE    : Send a petition to the CCR for help or questions
//
void cyMisc::SendPetitionToCCR(const char* message)
{
    SendPetitionToCCRI(message,plNetCommon::PetitionTypes::kGeneralHelp,nil);
}
void cyMisc::SendPetitionToCCRI(const char* message, UInt8 reason,const char* title)
{
    // create the mesage to send
    plCCRPetitionMsg *msg = TRACKED_NEW plCCRPetitionMsg();
    msg->SetNote(message);
    msg->SetType(reason);
    if (title)
        msg->SetTitle(title);
    // send it off
    plgDispatch::MsgSend( msg );
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SendChatToCCR
//  PARAMETERS : message  - message to send to the CCR ("please help me!")
//
//  PURPOSE    : Send a chat message to the CCR for help or questions
//
void cyMisc::SendChatToCCR(const char* message,Int32 CCRPlayerID)
{
    // create the mesage to send
    plCCRCommunicationMsg *msg = TRACKED_NEW plCCRCommunicationMsg();
    msg->SetMessage(message);
    msg->SetType(plCCRCommunicationMsg::kReturnChatMsg);
    msg->SetBCastFlag(plMessage::kNetAllowInterAge);
    msg->SetBCastFlag(plMessage::kNetPropagate);
    msg->SetBCastFlag(plMessage::kNetForce);
    msg->SetBCastFlag(plMessage::kLocalPropagate,0);
    msg->AddNetReceiver( CCRPlayerID );
    msg->Send();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetNumRemotePlayers
//  
//  PURPOSE    : return the number of remote players connected
//
int cyMisc::GetNumRemotePlayers()
{
    return plNetClientMgr::GetInstance()->RemotePlayerKeys().size();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Paging functions
//  PARAMETERS : nodeName  - name of the page to load
//  
//  PURPOSE    : page in, hold or out a particular node
//

void cyMisc::PageInNodes(const std::vector<std::string> & nodeNames, const char* age)
{
    if (hsgResMgr::ResMgr())
    {
        plSynchEnabler ps(false);   // disable dirty tracking while paging in
        plClientMsg* msg = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom);
        plKey clientKey = hsgResMgr::ResMgr()->FindKey(kClient_KEY);
        msg->AddReceiver(clientKey);

        int numNames = nodeNames.size();
        for (int i = 0; i < numNames; i++)
            msg->AddRoomLoc(plKeyFinder::Instance().FindLocation(age ? age : NetCommGetAge()->ageDatasetName, nodeNames[i].c_str()));

        msg->Send();
    }
}

void cyMisc::PageOutNode(const char* nodeName)
{
    if ( hsgResMgr::ResMgr() )
    {
        plSynchEnabler ps(false);   // disable dirty tracking while paging out
        plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kUnloadRoom);
        plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
        pMsg1->AddReceiver( clientKey );
        pMsg1->AddRoomLoc(plKeyFinder::Instance().FindLocation(nil, nodeName));
        plgDispatch::MsgSend(pMsg1);
    }
}


#include "plAvatar/plArmatureMod.h"
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LimitAvatarLOD
//  PARAMETERS : LODlimit - number of to limit the LOD to
//  
//  PURPOSE    : sets the avatar LOD limit
//
void cyMisc::LimitAvatarLOD(int LODlimit)
{
    if(LODlimit >= 0 && LODlimit <= 2)
        plArmatureLODMod::fMinLOD = LODlimit;
}



#include "plPipeline/plFogEnvironment.h"

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Set fog default functions
//  PARAMETERS : floats  - the parameters
//  
//  PURPOSE    : sets the fog defaults
//
void cyMisc::FogSetDefColor(pyColor& color)
{
    if ( fPipeline )
    {
        hsColorRGBA hcolor = color.getColor();
        hcolor.a = 1.0f;        // make sure that alpha is 1
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.SetColor( hcolor );
        fPipeline->SetDefaultFogEnviron( &env );
    }
}

void cyMisc::FogSetDefLinear(float start, float end, float density)
{
    if ( fPipeline )
    {
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.Set( start, end, density ); 
        fPipeline->SetDefaultFogEnviron( &env );
    }
}

void cyMisc::FogSetDefExp(float end, float density)
{
    if ( fPipeline )
    {
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.SetExp( plFogEnvironment::kExpFog, end, density ); 
        fPipeline->SetDefaultFogEnviron( &env );
    }
}

void cyMisc::FogSetDefExp2(float end, float density)
{
    if ( fPipeline )
    {
        plFogEnvironment env = fPipeline->GetDefaultFogEnviron();
        env.SetExp( plFogEnvironment::kExp2Fog, end, density ); 
        fPipeline->SetDefaultFogEnviron( &env );
    }
}


void cyMisc::SetClearColor(float red, float green, float blue)
{
    // do this command via the console to keep the maxplugins from barfing
    char command[256];
    sprintf(command,"Graphics.Renderer.SetClearColor %f %f %f",red,green,blue);
    // create message to send to the console
    plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
    pMsg->SetBCastFlag(plMessage::kBCastByType);
    pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND);
    pMsg->SetControlActivated(true);
    pMsg->SetCmdString(command);
    plgDispatch::MsgSend( pMsg );   // whoosh... off it goes
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Enable / disable cursor fade for avatar
//  PARAMETERS : 
//
//  PURPOSE    : turns avatar fade out on / off 
//

void cyMisc::EnableAvatarCursorFade()
{
    plNetClientMgr* nmgr = plNetClientMgr::GetInstance();
    if (nmgr)
    {
        plIfaceFadeAvatarMsg* iMsg = TRACKED_NEW plIfaceFadeAvatarMsg;
        iMsg->SetSubjectKey(nmgr->GetLocalPlayerKey());
        iMsg->SetBCastFlag(plMessage::kBCastByExactType);
        iMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE);
        iMsg->Enable();
        iMsg->Send();
    }
}

void cyMisc::DisableAvatarCursorFade()
{
    plNetClientMgr* nmgr = plNetClientMgr::GetInstance();
    if (nmgr)
    {
        plIfaceFadeAvatarMsg* iMsg = TRACKED_NEW plIfaceFadeAvatarMsg;
        iMsg->SetSubjectKey(nmgr->GetLocalPlayerKey());
        iMsg->SetBCastFlag(plMessage::kBCastByExactType);
        iMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE);
        iMsg->Disable();
        iMsg->Send();
    }
}

void cyMisc::FadeLocalPlayer(hsBool fade)
{
    plNetClientMgr* nmgr = plNetClientMgr::GetInstance();
    if (nmgr)
    {
        plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg;
        pMsg->SetFadeOut(fade);
        pMsg->SetSubjectKey(nmgr->GetLocalPlayerKey());
        pMsg->SetBCastFlag(plMessage::kBCastByExactType);
        pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE);
        pMsg->AddReceiver(nmgr->GetLocalPlayerKey());
        plgDispatch::MsgSend(pMsg);
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : offer linking book functions
//  PARAMETERS : 
//
//  PURPOSE    : manage offering public (pedestal) books
//

void cyMisc::EnableOfferBookMode(pyKey& selfkey, const char* ageFilename, const char* ageInstanceName)
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetOfferBookMode);
    pMsg->SetSender(selfkey.getKey());
    pMsg->SetAgeFileName(ageFilename);
    pMsg->SetAgeName(ageInstanceName);  
    pMsg->Send();
}

void cyMisc::DisableOfferBookMode()
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kClearOfferBookMode);
    pMsg->Send();
}

void cyMisc::NotifyOffererPublicLinkCompleted(UInt32 offerer)
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferCompleted, plNetClientMgr::GetInstance()->GetPlayerID());
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    if (offerer != plNetClientMgr::GetInstance()->GetPlayerID())
    {
        pMsg->SetBCastFlag( plMessage::kNetPropagate );
        pMsg->SetBCastFlag( plMessage::kNetForce );
        pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 );
        pMsg->AddNetReceiver( offerer );
    }

    pMsg->Send();
}

void cyMisc::NotifyOffererPublicLinkRejected(UInt32 offerer)
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferRejected);
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    if (offerer != plNetClientMgr::GetInstance()->GetPlayerID())
    {
        pMsg->SetBCastFlag( plMessage::kNetPropagate );
        pMsg->SetBCastFlag( plMessage::kNetForce );
        pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 );
        pMsg->AddNetReceiver( offerer );
    }

    pMsg->Send();
    ToggleAvatarClickability(true);
}

void cyMisc::NotifyOffererPublicLinkAccepted(UInt32 offerer)
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferAccepted);
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    if (offerer != plNetClientMgr::GetInstance()->GetPlayerID())
    {
        pMsg->SetBCastFlag( plMessage::kNetPropagate );
        pMsg->SetBCastFlag( plMessage::kNetForce );
        pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 );
        pMsg->AddNetReceiver( offerer );
    }

    pMsg->Send();
}

void cyMisc::ToggleAvatarClickability(hsBool on)
{
    plInputIfaceMgrMsg* pMsg = 0;
    if (on)
        pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIEnableAvatarClickable);
    else
        pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable);
    pMsg->SetAvKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    pMsg->SetBCastFlag(plMessage::kNetPropagate);
    pMsg->SetBCastFlag(plMessage::kNetForce);
    pMsg->Send();

}

void cyMisc::SetShareSpawnPoint(const char* spawnPoint)
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetShareSpawnPoint);
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    pMsg->SetSpawnPoint(spawnPoint);
    pMsg->Send();
}

void cyMisc::SetShareAgeInstanceGuid(const Uuid& guid)
{
    plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetShareAgeInstanceGuid);
    pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey());
    pMsg->SetAgeInstanceGuid(guid);
    pMsg->Send();
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : GetCCRAwayStatus
// PARAMETERS :
//
// PURPOSE    : Returns current status of CCR dept
//
hsBool cyMisc::IsCCRAwayStatus()
{
    return !VaultGetCCRStatus();
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : AmCCR
// PARAMETERS :
//
// PURPOSE    : Returns true if local player is a CCR
//
hsBool cyMisc::AmCCR()
{
    if ( plNetClientApp::GetInstance() )
        return plNetClientApp::GetInstance()->AmCCR();
    return false;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function   : AcceptInviteInGame
// PARAMETERS : Friend's Name, Invite Key
//
// PURPOSE    : Send's a VaultTask to the server to perform the invite
//
void cyMisc::AcceptInviteInGame(const char* friendName, const char* inviteKey)
{
    hsAssert(false, "eric, implement me");
#if 0
    plNetClientMgr* nc = plNetClientMgr::GetInstance();
    if (nc)
    {
        plNetMsgVaultTask msg;
        msg.SetNetProtocol(kNetProtocolCli2Auth);
        msg.SetTopic(plNetCommon::VaultTasks::kFriendInvite);
        msg.GetArgs()->AddString(plNetCommon::VaultTaskArgs::kFriendName,friendName);
        msg.GetArgs()->AddString(plNetCommon::VaultTaskArgs::kInviteKey,inviteKey);
        nc->SendMsg(&msg);
    }
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : GetLanguage
// PARAMETERS :
//
// PURPOSE    : Returns the current language the game is in
//
int cyMisc::GetLanguage()
{
    return int(plLocalization::GetLanguage());
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : UsingUnicode
// PARAMETERS :
//
// PURPOSE    : Returns true if the current language uses Unicode (like Japanese)
//
bool cyMisc::UsingUnicode()
{
    return plLocalization::UsingUnicode();
}

//////////////////////////////////////////////////////////////////////////////
//
//
//  particle system management
//
//
//
#include "plMessage/plParticleUpdateMsg.h"
#include "plParticleSystem/plParticleSystem.h"
#include "plParticleSystem/plParticleEffect.h"
void cyMisc::TransferParticlesToKey(pyKey& fromKey, pyKey& toKey, int numParticles)
{
    plKey frKey = fromKey.getKey();
    plSceneObject* so = plSceneObject::ConvertNoRef(toKey.getKey()->ObjectIsLoaded());
    if (so == nil) 
        return;
    
    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
    
    plParticleTransferMsg* pMsg = TRACKED_NEW plParticleTransferMsg(nil, avMod->GetKey(), 0, frKey, numParticles);
    pMsg->SetBCastFlag(plMessage::kNetPropagate);
    pMsg->SetBCastFlag(plMessage::kNetForce);
    pMsg->Send();
}

void cyMisc::SetParticleDissentPoint(float x, float y, float z, pyKey& particles)
{
    plKey frKey = particles.getKey();
    plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;
    const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));
    if (sys == nil) 
    {
        const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
        if (pArm)
        {
            // it's the avatar
            const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
            plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
            pObj = pNonConstArm->GetFollowerParticleSystemSO();
            if (!pObj)
                return;
            else
                sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));

        }
        if (sys == nil)
            return;
        
    }
    plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index());
    if (flock)
    {
        (TRACKED_NEW plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetDissentPoint, x, y, z))->Send();
    }
}

void cyMisc::SetParticleOffset(float x, float y, float z, pyKey& particles)
{
    plKey frKey = particles.getKey();
    plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;
    const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));
    if (sys == nil) 
    {
        const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
        if (pArm)
        {
            // it's the avatar
            const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
            plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
            pObj = pNonConstArm->GetFollowerParticleSystemSO();
            if (!pObj)
                return;
            else
                sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));

        }
        if (sys == nil)
            return;
        
    }

    plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index());
    if (flock)
    {
        (TRACKED_NEW plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetOffset, x, y, z))->Send();
    }
}

void cyMisc::KillParticles(float time, float pct, pyKey& particles)
{
    plKey frKey = particles.getKey();
    plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;
    const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));
    if (sys == nil) 
    {
        const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
        if (pArm)
        {
            // it's the avatar
            const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
            plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
            pObj = pNonConstArm->GetFollowerParticleSystemSO();
            if (!pObj)
                return;
            else
                sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index()));

        }
        if (sys == nil)
            return;
        
    }
    plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index());
    if (flock)
    {
        plParticleKillMsg* pMsg = TRACKED_NEW plParticleKillMsg(nil, frKey, 0, pct, time, plParticleKillMsg::kParticleKillPercentage | plParticleKillMsg::kParticleKillImmortalOnly);
        pMsg->SetBCastFlag(plMessage::kNetPropagate);
        pMsg->SetBCastFlag(plMessage::kNetForce);
        pMsg->SetBCastFlag(plMessage::kPropagateToChildren);
        pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
        pMsg->Send();
    }
}

int cyMisc::GetNumParticles(pyKey& host)
{
    plSceneObject* pObj = plSceneObject::ConvertNoRef(host.getKey()->ObjectIsLoaded());
    if (!pObj)
        return 0;
    const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index());
    if (pArm)
    {
        // it's the avatar
        const plArmatureMod* pCArm = (const plArmatureMod*)pArm;
        plArmatureMod* pNonConstArm = const_cast<plArmatureMod*>(pCArm);
        pObj = pNonConstArm->GetFollowerParticleSystemSO();
        if (!pObj)
            return 0;
    }
    const plModifier* pPart = pObj->GetModifierByType(plParticleSystem::Index());
    if (!pPart)
        return 0;
    return ((const plParticleSystem*)pPart)->GetNumValidParticles(true);
}


void cyMisc::SetLightColorValue(pyKey& light, std::string lightName, hsScalar r, hsScalar g, hsScalar b, hsScalar a)
{
    // lightName is the name of the light object attached to the light that we want to talk to
    // for the bug lights, this would be "RTOmni-BugLightTest"
    plSceneObject* pObj = plSceneObject::ConvertNoRef(light.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;

    plObjInterface* pIface = pObj->GetGenericInterface(plLightInfo::Index());
    if (pIface)
    {
        // we are a light ourselves... are we the one they are looking for?
        if (lightName != pObj->GetKeyName())
            pIface = nil; // not the one they want, check our children
    }

    if (!pIface)
    {
        // recurse through our children...
        for (int i = 0; i < pObj->GetCoordinateInterface()->GetNumChildren(); i++)
        {
            const plSceneObject* child = pObj->GetCoordinateInterface()->GetChild(i)->GetOwner();
            if (child)
            {
                pIface = child->GetGenericInterface(plLightInfo::Index());
                if (pIface)
                {
                    // found a child... is it the one we want?
                    if (lightName != child->GetKeyName())
                        pIface = nil; // not the child we want, keep looking
                }
            }
            if (pIface)
                break;
        }
    }
    if (pIface)
    {
        plLightInfo* pLight = (plLightInfo*)pIface;
        hsColorRGBA c;
        c.Set(r,g,b,a);
        pLight->SetDiffuse(c);
        pLight->SetDiffuse(c);
        pLight->SetSpecular(c);
    }
}

#include "pnMessage/plEnableMsg.h"
void cyMisc::SetLightAnimationOn(pyKey& light, std::string lightName, hsBool start)
{
    // lightName is the name of the light object attached to the light that we want to talk to
    // for the bug lights, this would be "RTOmni-BugLightTest"
    plSceneObject* pObj = plSceneObject::ConvertNoRef(light.getKey()->ObjectIsLoaded());
    if (!pObj)
        return;

    plObjInterface* pIface = pObj->GetGenericInterface(plLightInfo::Index());
    if (pIface)
    {
        // we are a light ourselves... are we the one they are looking for?
        if (lightName != pObj->GetKeyName())
            pIface = nil; // not the one they want, check our children
    }

    if (!pIface)
    {
        // recurse through our children...
        for (int i = 0; i < pObj->GetCoordinateInterface()->GetNumChildren(); i++)
        {
            const plSceneObject* child = pObj->GetCoordinateInterface()->GetChild(i)->GetOwner();
            if (child)
            {
                pIface = child->GetGenericInterface(plLightInfo::Index());
                if (pIface)
                {
                    // found a child... is it the one we want?
                    if (lightName != child->GetKeyName())
                        pIface = nil; // not the child we want, keep looking
                }
            }
            if (pIface)
                break;
        }
    }
    if (pIface)
    {
        plEnableMsg* enableMsg = TRACKED_NEW plEnableMsg;
        enableMsg->AddReceiver(pIface->GetKey());
        enableMsg->SetBCastFlag(plMessage::kNetPropagate);
        enableMsg->SetBCastFlag(plMessage::kNetForce);

        plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg;
        animMsg->AddReceiver(pIface->GetOwnerKey());
        animMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
        animMsg->SetBCastFlag(plMessage::kNetPropagate);
        animMsg->SetBCastFlag(plMessage::kNetForce);

        if (start)
        {
            enableMsg->SetCmd(plEnableMsg::kEnable);
            animMsg->SetCmd(plAnimCmdMsg::kContinue);
        }
        else
        {
            enableMsg->SetCmd(plEnableMsg::kDisable);
            animMsg->SetCmd(plAnimCmdMsg::kStop);
            animMsg->SetCmd(plAnimCmdMsg::kGoToBegin);
        }

        enableMsg->Send();
        animMsg->Send();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : RegisterForControlEventMessages
// PARAMETERS : switch on or off, registrant
//
// PURPOSE    : let you get control event messages at will (for pseudo-GUI's like the psnl bookshelf or clft imager)

void cyMisc::RegisterForControlEventMessages(hsBool on, pyKey& k)
{
    plCmdIfaceModMsg* pMsg = TRACKED_NEW plCmdIfaceModMsg;
    pMsg->SetSender(k.getKey());
    if (on)
        pMsg->SetCmd(plCmdIfaceModMsg::kAdd);
    else
        pMsg->SetCmd(plCmdIfaceModMsg::kRemove);
    pMsg->SetBCastFlag(plMessage::kBCastByExactType);
    pMsg->Send();
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : RequestLOSScreen
// PARAMETERS : lots...
//
// PURPOSE    : To request an LOS from a point on the screen
//
#include "plMessage/plLOSRequestMsg.h"
bool cyMisc::RequestLOSScreen(pyKey &selfkey, Int32 ID, hsScalar xPos, hsScalar yPos, hsScalar distance, int what, int reportType)
{
    plPipeline* pipe = selfkey.GetPipeline();
    if (pipe)
    {
        Int32 x=(Int32) ( xPos * pipe->Width() );
        Int32 y=(Int32) ( yPos * pipe->Height() );

        hsPoint3 endPos, startPos;
        
        pipe->ScreenToWorldPoint( 1,0, &x, &y, distance, 0, &endPos );
        startPos = pipe->GetViewPositionWorld();

        // move the start pos out a little to avoid backing up against physical objects...
        hsVector3 view(endPos - startPos);
        view.Normalize();
        startPos = startPos + (view * 1.5f);

        plLOSRequestMsg* pMsg = nil;
        switch (what)
        {
            case kClickables:
                pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBUIItems, plLOSRequestMsg::kTestClosest );
                pMsg->SetCullDB(plSimDefs::kLOSDBUIBlockers);
                break;
            case kCameraBlockers:
                pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBCameraBlockers, plLOSRequestMsg::kTestClosest );
                break;
            case kCustom:
                pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestClosest );
                break;
            case kShootable:
                pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBShootableItems, plLOSRequestMsg::kTestClosest );
                break;
        }

        if ( pMsg )
        {
            pMsg->SetReportType( (plLOSRequestMsg::ReportType)reportType );

            pMsg->SetRequestID( ID );

            plgDispatch::MsgSend( pMsg );
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : CheckVisLOS,CheckVisLOSFromCursor
// PARAMETERS : StartPoint, EndPoint
//
// PURPOSE    : Check is there is something visible in the path from StartPoint to EndPoint
//
#include "plDrawable/plVisLOSMgr.h"
PyObject* cyMisc::CheckVisLOS(pyPoint3 startPoint, pyPoint3 endPoint)
{
    if (plVisLOSMgr::Instance())
    {
        plVisHit hit;
        if( plVisLOSMgr::Instance()->Check(startPoint.fPoint,endPoint.fPoint,hit) )
        {
            return pyPoint3::New(hit.fPos);
        }
    }
    PYTHON_RETURN_NONE;
}

PyObject* cyMisc::CheckVisLOSFromCursor()
{
    if (plVisLOSMgr::Instance())
    {
        plVisHit hit;
        if( plVisLOSMgr::Instance()->CursorCheck(hit) )
        {
            return pyPoint3::New(hit.fPos);
        }
    }
    PYTHON_RETURN_NONE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsSinglePlayerMode
// PARAMETERS :
//
// PURPOSE    : Returns whether the game is in Single Player mode
//
bool cyMisc::IsSinglePlayerMode()
{
    return false;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsDemoMode
// PARAMETERS :
//
// PURPOSE    : Returns whether the game is in Single Player mode
//
bool cyMisc::IsDemoMode()
{
    plNetClientApp* nc = plNetClientApp::GetInstance();
    if (nc)
        return nc->InDemoMode();
    // if we couldn't find the net client app, maybe it was because we are single player mode
    return true;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsInternalRelease
// PARAMETERS :
//
// PURPOSE    : Returns true if we are running an internal build
//
bool cyMisc::IsInternalRelease()
{
#ifdef PLASMA_EXTERNAL_RELEASE
    return false;
#else
    return true;
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : IsEnterChatModeKeyBound
// PARAMETERS :
//
// PURPOSE    : Returns whether the EnterChatMode is bound to a key
//
bool cyMisc::IsEnterChatModeKeyBound()
{
    plAvatarInputInterface* aii = plAvatarInputInterface::GetInstance();
    if (aii)
        return aii->IsEnterChatModeBound();
    // if we couldn't find the net client app, maybe it was because we are single player mode
    return true;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : ShootBulletFromScreen
// PARAMETERS : lots...
//
// PURPOSE    : Shoots from screen coordinates, a bullet and makes a mark on objects that know about bullet holes
//
#include "plMessage/plBulletMsg.h"
void cyMisc::ShootBulletFromScreen(pyKey &selfkey, hsScalar xPos, hsScalar yPos, hsScalar radius, hsScalar range)
{
    plPipeline* pipe = selfkey.GetPipeline();
    if (pipe)
    {
        Int32 x=(Int32) ( xPos * pipe->Width() );
        Int32 y=(Int32) ( yPos * pipe->Height() );

        hsPoint3 endPos, startPos;
        
        pipe->ScreenToWorldPoint( 1,0, &x, &y, range, 0, &endPos );
        startPos = pipe->GetViewPositionWorld();

        // move the start pos out a little to avoid backing up against physical objects...
        hsVector3 view(endPos - startPos);
        view.Normalize();
        startPos = startPos + (view * 1.5f);

        plBulletMsg* bull = TRACKED_NEW plBulletMsg( selfkey.getKey(), nil, nil );
        bull->FireShot(startPos, view, radius, range);
        bull->Send();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : ShootBulletFromObject
// PARAMETERS : lots...
//
// PURPOSE    : Shoots from an object, a bullet and makes a mark on objects that know about bullet holes
//
void cyMisc::ShootBulletFromObject(pyKey &selfkey, pySceneObject* sobj, hsScalar radius, hsScalar range)
{
    plSceneObject* so = plSceneObject::ConvertNoRef(sobj->getObjKey()->ObjectIsLoaded());
    if( so )
    {
        // find the direction

        hsMatrix44 l2w = so->GetLocalToWorld();
        hsVector3 dir(-l2w.fMap[0][0], -l2w.fMap[1][0], -l2w.fMap[2][0]);
        dir.Normalize();
        hsPoint3 pos = l2w.GetTranslate();

        plBulletMsg* bull = TRACKED_NEW plBulletMsg(selfkey.getKey(), nil, nil);
        bull->FireShot(pos, dir, radius, range);

        bull->Send();
    }
}


//////////////////////////////////////////////////////////////////////////////
class NetClientCommCallback : public plNetClientComm::Callback
{
public:
    enum Contexts
    {
        kInvalid,
        kGetPublicAgeList,
        kCreatePublicAge,
        kRemovePublicAge,
    };

    PyObject * fPyObject;
    NetClientCommCallback( PyObject * pyObject )
        : fPyObject( pyObject )
    {
        Py_XINCREF( fPyObject );
    }
    ~NetClientCommCallback()
    {
        Py_XDECREF( fPyObject );
    }
    void OperationStarted( UInt32 context )
    {}
    void OperationComplete( UInt32 context, int resultCode )
    {
        if ( !fPyObject )
            return;



        PyObject* func = nil;

        switch ( context )
        {
        case kGetPublicAgeList:
            // Call the callback.
            func = PyObject_GetAttrString( fPyObject, "gotPublicAgeList" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    plCreatableStream * ageInfoStream = plCreatableStream::ConvertNoRef( fCbArgs.GetItem( 0 ) );
                    plCreatableStream * nPlayersStream = plCreatableStream::ConvertNoRef( fCbArgs.GetItem( 1 ) );

                    if ( ageInfoStream && nPlayersStream )
                    {
                        UInt16 nAgeInfoEntries;
                        ageInfoStream->GetStream()->ReadSwap( &nAgeInfoEntries );

                        UInt16 nPlayerCountEntries;
                        nPlayersStream->GetStream()->ReadSwap( &nPlayerCountEntries );

                        hsAssert( nAgeInfoEntries==nPlayerCountEntries, "huh?" );

                        // convert callback args to a list of tuple(ageInfo,nPlayers)
                        PyObject* pyEL = PyList_New(nAgeInfoEntries);

                        for ( int i=0; i<nAgeInfoEntries; i++ )
                        {
                            plAgeInfoStruct ageInfo;
                            UInt32 nPlayers;
                            ageInfo.Read( ageInfoStream->GetStream(), nil );
                            nPlayersStream->GetStream()->ReadSwap( &nPlayers );
                            PyObject* t = PyTuple_New(2);
                            PyTuple_SetItem(t, 0, pyAgeInfoStruct::New(&ageInfo));
                            PyTuple_SetItem(t, 1, PyLong_FromUnsignedLong(nPlayers));
                            PyList_SetItem(pyEL, i, t); // steals the ref
                        }
                        PyObject* retVal = PyObject_CallMethod(fPyObject, "gotPublicAgeList", "O", pyEL);
                        Py_XDECREF(retVal);
                    }
                }
            }
            break;
        case kCreatePublicAge:
            // Call the callback.
            func = PyObject_GetAttrString( fPyObject, "publicAgeCreated" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    plAgeInfoStruct * ageInfo = plAgeInfoStruct::ConvertNoRef( fCbArgs.GetItem( 0 ) );

                    if ( ageInfo )
                    {
                        PyObject* ageInfoObj = pyAgeInfoStruct::New(ageInfo);
                        PyObject* retVal = PyObject_CallMethod(fPyObject, "publicAgeCreated", "O", ageInfoObj);
                        Py_XDECREF(retVal);
                        Py_DECREF(ageInfoObj);
                    }
                }
            }
            break;
        case kRemovePublicAge:
            // Call the callback.
            func = PyObject_GetAttrString( fPyObject, "publicAgeRemoved" );
            if ( func )
            {
                if ( PyCallable_Check(func)>0 )
                {
                    plCreatableUuid * guid = plCreatableUuid::ConvertNoRef( fCbArgs.GetItem( 0 ) );

                    if ( guid )
                    {
                        PyObject* retVal = PyObject_CallMethod(fPyObject, "publicAgeRemoved", "s", guid->AsString());
                        Py_XDECREF(retVal);
                    }
                }
            }
            break;
        }

        delete this;
    }
};


//////////////////////////////////////////////////////////////////////////////
//
// Function   : GetPublicAgeList
// PARAMETERS : ageName, callback object
//
// PURPOSE    : Get the list of public ages for the given age name.
//
void cyMisc::GetPublicAgeList( const char * ageName, PyObject * cbObject )
{
    if (cbObject)
        Py_XINCREF(cbObject);
    NetCommGetPublicAgeList(
        ageName,
        cbObject,
        plNetCommReplyMsg::kParamTypePython
    );
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : CreatePublicAge
// PARAMETERS : ageInfo, callback object
//
// PURPOSE    : Add a public age to the list of available ones.
//
void cyMisc::CreatePublicAge( pyAgeInfoStruct * ageInfo, PyObject * cbObject )
{
    VaultSetOwnedAgePublicAndWait(ageInfo->GetAgeInfo(), true);
    // TODO: make the callback here
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : RemovePublicAge
// PARAMETERS : ageInstanceGuid, callback object
//
// PURPOSE    : Remove a public age from the list of available ones.
//
void cyMisc::RemovePublicAge( const char * ageInstanceGuid, PyObject * cbObject/*=nil */)
{
    plAgeInfoStruct info;
    plUUID uuid(ageInstanceGuid);
    info.SetAgeInstanceGuid(&uuid);
    VaultSetOwnedAgePublicAndWait(&info, false);
    // TODO: make the callback here
}

int cyMisc::GetKILevel()
{
    int result = pfKIMsg::kNanoKI;
    
    wchar wStr[MAX_PATH];
    StrToUnicode(wStr, pfKIMsg::kChronicleKILevel, arrsize(wStr));
    if (RelVaultNode * rvn = VaultFindChronicleEntryIncRef(wStr)) {
        VaultChronicleNode chron(rvn);
        result = _wtoi(chron.entryValue);
        rvn->DecRef();
    }
    
    return result;
}

//////////////////////////////////////////////////////////////////////////////
//
// the following are for recording and rebuilding the camera stack

#include "pfCamera/plCameraModifier.h"

int cyMisc::GetNumCameras()
{
    return (plVirtualCam1::Instance()->GetNumCameras());
}

const char* cyMisc::GetCameraNumber(int number)
{
    plCameraModifier1* pCam = plVirtualCam1::Instance()->GetCameraNumber(number-1);
    if (pCam->GetTarget())
    {
        const char* ret = pCam->GetTarget()->GetKeyName();
        (ret==nil) ? "empty" : ret;
        char str[256];
        sprintf(str, "saving camera named %s to chronicle\n",ret);
        plVirtualCam1::Instance()->AddMsgToLog(str);
        return ret;
    }
    plVirtualCam1::Instance()->AddMsgToLog("sending empty to camera chronicle\n");
    return "empty";
}

void cyMisc::RebuildCameraStack(const char* name, const char* ageName)
{
    plKey key=nil;
    char str[256];
    sprintf(str, "attempting to restore camera named %s from chronicle\n",name);
    plVirtualCam1::Instance()->AddMsgToLog(str);
        
    if (strcmp(name, "empty") == 0)
        return;

    if ( name || name[0] != 0)
    {
        key=plKeyFinder::Instance().StupidSearch(nil,nil,plSceneObject::Index(), name, false);
    }
    if ( key == nil )
    {
        // try and use this new hack method to find it
        if (!plVirtualCam1::Instance()->RestoreFromName(name))
        {
            // give up and force built in 3rd person
            plVirtualCam1::Instance()->PushThirdPerson();
            char errmsg[256];
            sprintf(errmsg,"Sceneobject %s not found",name);
            PyErr_SetString(PyExc_NameError, errmsg);
        }
    }
    else
    {
        // now we have the scene object, look for it's camera modifier
        const plCameraModifier1* pMod = nil;
        plSceneObject* pObj = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
        if (pObj)
        {
            for (int i = 1; i < pObj->GetNumModifiers(); i++)
            {
                pMod = plCameraModifier1::ConvertNoRef(pObj->GetModifier(i));
                if (pMod)
                    break;
            }
            if (pMod)
            {   
                plVirtualCam1::Instance()->RebuildStack(pMod->GetKey());
                return;
            }
        }
        plVirtualCam1::Instance()->PushThirdPerson();
        char errmsg[256];
        sprintf(errmsg,"Sceneobject %s has no camera modifier",name);
        PyErr_SetString(PyExc_NameError, errmsg);
    }
    
}

void cyMisc::PyClearCameraStack()
{
    plVirtualCam1::Instance()->ClearStack();
}

void cyMisc::RecenterCamera()
{
    plCameraMsg* pCam = TRACKED_NEW plCameraMsg;
    pCam->SetBCastFlag(plMessage::kBCastByExactType);
    pCam->SetCmd(plCameraMsg::kResetPanning);
    pCam->Send();
}

#include "plMessage/plTransitionMsg.h"

void cyMisc::FadeIn(float lenTime, bool holdFlag, bool noSound)
{
    plTransitionMsg *msg = TRACKED_NEW plTransitionMsg( noSound ? plTransitionMsg::kFadeInNoSound : plTransitionMsg::kFadeIn, lenTime, holdFlag );
    plgDispatch::MsgSend( msg );
}

void cyMisc::FadeOut(float lenTime, bool holdFlag, bool noSound)
{
    plTransitionMsg *msg = TRACKED_NEW plTransitionMsg( noSound ? plTransitionMsg::kFadeOutNoSound : plTransitionMsg::kFadeOut, lenTime, holdFlag );
    plgDispatch::MsgSend( msg );
}

void cyMisc::SetClickability(hsBool b)
{
    plInputIfaceMgrMsg* msg = TRACKED_NEW plInputIfaceMgrMsg(b ? plInputIfaceMgrMsg::kEnableClickables : plInputIfaceMgrMsg::kDisableClickables );
    plgDispatch::MsgSend(msg);
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Debug build only: Assert if condition is false.
//
// PURPOSE    : debugging
//
void cyMisc::DebugAssert( bool cond, const char * msg )
{
    hsAssert( cond, msg );
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Set a python object to be called back after a certain amount of time.
//
// PURPOSE    : script can trigger itself over time w/o having to specify it in the dataset.
//
void cyMisc::SetAlarm( float secs, PyObject * cb, UInt32 cbContext )
{
    pyAlarmMgr::GetInstance()->SetAlarm( secs, cb, cbContext );
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Save Screen Shot
//
// PURPOSE    : captures the screen and saves it as a jpeg
//
#include "plJPEG/plJPEG.h"
void cyMisc::SaveScreenShot(const char* fileName, int x, int y, int quality)
{
    if ( cyMisc::GetPipeline() )
    {
        if (quality <= 0 || quality > 100)
            quality = 75;

        plMipmap mipmap;
        cyMisc::GetPipeline()->CaptureScreen( &mipmap, false, x, y );

        plJPEG::Instance().SetWriteQuality( quality );
        plJPEG::Instance().WriteToFile( fileName, &mipmap );
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Start a screen capture
//
// PURPOSE    : This starts a screen capture in motion. It will be capture on the next
//              update and a plCaptureRenderMsg when its ready
//
#include "plPipeline/plCaptureRender.h"
void cyMisc::StartScreenCapture(pyKey& selfkey)
{
    cyMisc::StartScreenCaptureWH(selfkey, 800, 600);
}

void cyMisc::StartScreenCaptureWH(pyKey& selfkey, UInt16 width, UInt16 height)
{
    plCaptureRender::Capture(selfkey.getKey(), width, height);
}


#include "plAvatar/plAvatarClothing.h"
void cyMisc::WearMaintainerSuit(pyKey& key, hsBool wear)
{
    // run on all machines, but only affects us if we call it on our local guy (who props it to others himself)
    if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
        return;

    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
    
    if (avMod)
    {
        if (wear)
            avMod->GetClothingOutfit()->WearMaintainerOutfit();
        else
            avMod->GetClothingOutfit()->RemoveMaintainerOutfit();
    }

}

void cyMisc::WearDefaultClothing(pyKey& key)
{
    if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
        return;

    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
    
    if (avMod)
    {
        avMod->GetClothingOutfit()->WearDefaultClothing();
    }
}

void cyMisc::WearDefaultClothingType(pyKey& key, UInt32 type)
{
    if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
        return;

    plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();

    if (avMod)
    {
        avMod->GetClothingOutfit()->WearDefaultClothingType(type);
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Function   : Fake link to object
//
// PURPOSE    : takes an avatar key and an object key and fake-links the avatar
//              to that object's position.  appears to be a link to other players
//

void cyMisc::FakeLinkToObject(pyKey& avatar, pyKey& object)
{
    plPseudoLinkEffectMsg* msg = TRACKED_NEW plPseudoLinkEffectMsg;
    msg->fAvatarKey = avatar.getKey();
    msg->fLinkObjKey = object.getKey();
    plgDispatch::MsgSend(msg);
}

void cyMisc::FakeLinkToObjectNamed(const char* name)
{
    plKey key = nil;
    if ( name || name[0] != 0)
    {
        key = plKeyFinder::Instance().StupidSearch(nil,nil,plSceneObject::Index(), name, false);
    }

    if (!key)
        return;
    plPseudoLinkEffectMsg* msg = TRACKED_NEW plPseudoLinkEffectMsg;
    msg->fAvatarKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey();
    msg->fLinkObjKey = key;
    plgDispatch::MsgSend(msg);
}

PyObject* cyMisc::LoadAvatarModel(const char* modelName, pyKey& spawnPoint, const char* userStr)
{
    plKey SpawnedKey = plAvatarMgr::GetInstance()->LoadAvatar(modelName, "", false, spawnPoint.getKey(), nil, userStr);
    return pyKey::New(SpawnedKey);
}

void cyMisc::UnLoadAvatarModel(pyKey& avatar)
{
    plAvatarMgr::GetInstance()->UnLoadAvatar(avatar.getKey(), false);
}

void cyMisc::ForceCursorHidden()
{
    plMouseDevice::HideCursor(true);
}

void cyMisc::ForceCursorShown()
{
    plMouseDevice::ShowCursor(true);
}

///////////////////////////////////////////////////////////////////////////////
//
// Function   : GetLocalizedString
//
// PURPOSE    : Returns the specified localized string with the parameters
//              properly replaced (the list is a list of unicode strings) Name
//              is in "Age.Set.Name" format
//
std::wstring cyMisc::GetLocalizedString(std::wstring name, const std::vector<std::wstring> & arguments)
{
    if (pfLocalizationMgr::InstanceValid())
        return pfLocalizationMgr::Instance().GetString(name, arguments);
    return L"";
}

void cyMisc::EnablePlanarReflections(bool enable)
{
    plDynamicCamMap::SetEnabled(enable);
}

void cyMisc::GetSupportedDisplayModes(std::vector<plDisplayMode> *res)
{
    fPipeline->GetSupportedDisplayModes(res);
}

int cyMisc::GetDesktopWidth()
{
    return fPipeline->GetDesktopWidth();
}

int cyMisc::GetDesktopHeight()
{
    return fPipeline->GetDesktopHeight();
}

int cyMisc::GetDesktopColorDepth()
{
    return fPipeline->GetDesktopColorDepth();
}

PipelineParams *cyMisc::GetDefaultDisplayParams()
{
    return fPipeline->GetDefaultParams();
}

void cyMisc::SetGraphicsOptions(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync)
{
    // This has to send a message to plClient because python is loaded in the max plugins

    plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
    plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kResetGraphicsDevice);
    clientMsg->AddReceiver(clientKey);
    //clientMsg->SetBCastFlag(plMessage::kBCastByType);
    clientMsg->fGraphicsSettings.fWidth = Width;
    clientMsg->fGraphicsSettings.fHeight = Height;
    clientMsg->fGraphicsSettings.fColorDepth = ColorDepth;
    clientMsg->fGraphicsSettings.fWindowed = Windowed;
    clientMsg->fGraphicsSettings.fNumAASamples = NumAASamples;
    clientMsg->fGraphicsSettings.fMaxAnisoSamples = MaxAnisotropicSamples;
    clientMsg->fGraphicsSettings.fVSync = VSync;
    clientMsg->Send();

    //plClient::GetInstance()->ResetDisplayDevice(Width, Height, ColorDepth, Windowed, NumAASamples, MaxAnisotropicSamples);
}

bool cyMisc::DumpLogs(const std::wstring & folder)
{
    char* temp = hsWStringToString(folder.c_str());
    bool retVal = plStatusLogMgr::GetInstance().DumpLogs(temp);
    delete [] temp;
    return retVal;
}

bool cyMisc::FileExists(const std::wstring & filename)
{
    return PathDoesFileExist(filename.c_str());
}

bool cyMisc::CreateDir(const std::wstring & directory)
{
    return PathCreateDirectory(directory.c_str(), kPathCreateDirFlagEntireTree) == kPathCreateDirSuccess;
}

std::wstring cyMisc::GetUserPath()
{
    wchar_t path[MAX_PATH];
    PathGetUserDirectory(path, arrsize(path));
    return path;
}

std::wstring cyMisc::GetInitPath()
{
    wchar_t path[MAX_PATH];
    PathGetInitDirectory(path, arrsize(path));
    return path;
}

void cyMisc::SetBehaviorNetFlags(pyKey & behKey, hsBool netForce, hsBool netProp)
{
    if (plMultistageBehMod * behMod = plMultistageBehMod::ConvertNoRef(behKey.getKey()->ObjectIsLoaded()))
    {
        behMod->SetNetForce(netForce);
        behMod->SetNetProp(netProp);
    }
}

void cyMisc::SendFriendInvite(const wchar email[], const wchar toName[])
{
    if (RelVaultNode* pNode = VaultGetPlayerNodeIncRef())
    {
        VaultPlayerNode player(pNode);
        Uuid inviteUuid = player.inviteUuid;

        // If we don't have an invite UUID set then make a new one
        if (GuidIsNil(inviteUuid))
        {
            inviteUuid = GuidGenerate();
            player.SetInviteUuid(inviteUuid);
        }

        NetCommSendFriendInvite(email, toName, inviteUuid);
        pNode->DecRef();
    }
}

PyObject* cyMisc::PyGuidGenerate()
{
    char guidStr[64];
    Uuid newGuid = GuidGenerate();
    GuidToString(newGuid, guidStr, arrsize(guidStr));

    return PyString_FromString(guidStr);
}

PyObject* cyMisc::GetAIAvatarsByModelName(const char* name)
{
    plAvatarMgr::plArmatureModPtrVec armVec;
    plAvatarMgr::GetInstance()->FindAllAvatarsByModelName(name, armVec);

    PyObject* avList = PyList_New(0);

    for (plAvatarMgr::plArmatureModPtrVec::iterator it = armVec.begin(); it != armVec.end(); ++it)
    {
        plArmatureMod* armMod = (*it);
        plAvBrainCritter* critterBrain = plAvBrainCritter::ConvertNoRef(armMod->FindBrainByClass(plAvBrainCritter::Index()));
        if (critterBrain)
        {
            PyObject* tuple = PyTuple_New(2);
            PyTuple_SetItem(tuple, 0, pyCritterBrain::New(critterBrain));
            PyTuple_SetItem(tuple, 1, PyString_FromString(armMod->GetUserStr()));

            PyList_Append(avList, tuple);
            Py_DECREF(tuple);
        }
    }

    if (PyList_Size(avList) > 0)
        return avList;
    else
    {
        Py_DECREF(avList);
        PYTHON_RETURN_NONE;
    }
}

void cyMisc::ForceVaultNodeUpdate(unsigned nodeId)
{
    VaultFetchNodesAndWait(&nodeId, 1, true);
}

void cyMisc::VaultDownload(unsigned nodeId)
{
    VaultDownloadAndWait(
        L"PyVaultDownload",
        nodeId,
        nil,
        nil
    );
}