You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1328 lines
38 KiB
1328 lines
38 KiB
4 years ago
|
/*==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/>.
|
||
|
|
||
|
Additional permissions under GNU GPL version 3 section 7
|
||
|
|
||
|
If you modify this Program, or any covered work, by linking or
|
||
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||
|
(or a modified version of those libraries),
|
||
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||
|
licensors of this Program grant you additional
|
||
|
permission to convey the resulting work. Corresponding Source for a
|
||
|
non-source form of such a combination shall include the source code for
|
||
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||
|
work.
|
||
|
|
||
|
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 "HeadSpin.h"
|
||
|
#include "plPythonFileComponent.h"
|
||
|
|
||
|
#include "resource.h"
|
||
|
#include "plComponent.h"
|
||
|
#include "plComponentReg.h"
|
||
|
#include "../MaxMain/plMaxNode.h"
|
||
|
#include "hsUtils.h"
|
||
|
#include "plAutoUIBlock.h"
|
||
|
#include "plAutoUIParams.h"
|
||
|
#include "../pnSceneObject/plSceneObject.h"
|
||
|
#include "../MaxMain/plPluginResManager.h"
|
||
|
#include "../pnMessage/plObjRefMsg.h"
|
||
|
#include "plVolumeGadgetComponent.h"
|
||
|
#include "plGUIComponents.h"
|
||
|
#include "pfGUISkinComp.h"
|
||
|
#include "plExcludeRegionComponent.h"
|
||
|
#include "plAnimComponent.h"
|
||
|
#include "plNotetrackAnim.h"
|
||
|
#include "plOneShotComponent.h"
|
||
|
#include "plMultistageBehComponent.h"
|
||
|
#include "../plInterp/plAnimEaseTypes.h"
|
||
|
#include "../plAgeDescription/plAgeDescription.h"
|
||
|
#include "plWaterComponent.h"
|
||
|
#include "../plDrawable/plWaveSetBase.h"
|
||
|
#include "plClusterComponent.h"
|
||
|
#include "../plDrawable/plClusterGroup.h"
|
||
|
#include "plPhysicalComponents.h"
|
||
|
//#include "../plHavok1/plHKPhysical.h"
|
||
|
#include "../plAvatar/plSwimRegion.h"
|
||
|
#include "../../PubUtilLib/plSurface/plGrassShaderMod.h"
|
||
|
#include "plGrassComponent.h"
|
||
|
|
||
|
#include "notify.h"
|
||
|
|
||
|
#include <vector>
|
||
|
#include <set>
|
||
|
#include <map>
|
||
|
#include <string>
|
||
|
|
||
|
#include "../pfPython/plPythonFileMod.h"
|
||
|
#include "../pfPython/plPythonParameter.h"
|
||
|
|
||
|
// for DynamicText hack to get the plKeys (could probably be removed later)
|
||
|
#include "../plGImage/plDynamicTextMap.h"
|
||
|
|
||
|
//// plCommonPythonLib ///////////////////////////////////////////////////////
|
||
|
// Derived class for our global python fileMods, since they go in to the
|
||
|
// BuiltIn page
|
||
|
// 2.4.03 mcn - Added sceneObjects to the list, so that we can have an
|
||
|
// associated sceneObject for our mod
|
||
|
|
||
|
#include "../MaxMain/plCommonObjLib.h"
|
||
|
#include "../plSDL/plSDL.h"
|
||
|
|
||
|
class plCommonPythonLib : public plCommonObjLib
|
||
|
{
|
||
|
public:
|
||
|
virtual hsBool IsInteresting( const plKey &objectKey )
|
||
|
{
|
||
|
if( objectKey->GetUoid().GetClassType() == plPythonFileMod::Index() )
|
||
|
return true;
|
||
|
if( objectKey->GetUoid().GetClassType() == plSceneObject::Index() &&
|
||
|
strcmp( objectKey->GetUoid().GetObjectName(), plSDL::kAgeSDLObjectName ) == 0 )
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static plCommonPythonLib sCommonPythonLib;
|
||
|
|
||
|
|
||
|
|
||
|
static void NotifyProc(void *param, NotifyInfo *info);
|
||
|
|
||
|
// Notify us on file open so we can check for bad Python components
|
||
|
void DummyCodeIncludePythonFileFunc()
|
||
|
{
|
||
|
RegisterNotification(NotifyProc, nil, NOTIFY_FILE_POST_OPEN);
|
||
|
RegisterNotification(NotifyProc, nil, NOTIFY_FILE_PRE_SAVE);
|
||
|
RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_POST_RESET);
|
||
|
RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_POST_NEW);
|
||
|
RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_SHUTDOWN2);
|
||
|
}
|
||
|
|
||
|
// Param block ID's
|
||
|
enum
|
||
|
{
|
||
|
kPythonFileType_DEAD,
|
||
|
kPythonFilePB,
|
||
|
kPythonVersion,
|
||
|
kPythonFileIsGlobal
|
||
|
};
|
||
|
|
||
|
class plPythonFileComponent : public plComponent
|
||
|
{
|
||
|
public:
|
||
|
typedef std::map<plMaxNode*, plKey> PythonKeys;
|
||
|
|
||
|
// When we're auto-exporting and can't pop up an error dialog, we cache our
|
||
|
// errors here and blab about it at export time
|
||
|
static std::string fPythonError;
|
||
|
|
||
|
protected:
|
||
|
PythonKeys fModKeys;
|
||
|
hsTArray<plKey> fOthersKeys;
|
||
|
|
||
|
public:
|
||
|
plPythonFileComponent();
|
||
|
|
||
|
virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
virtual void AddReceiverKey(plKey key, plMaxNode* node=nil) { fOthersKeys.Append(key); }
|
||
|
|
||
|
// Returns false if the Python file for this component wasn't loaded, and clears
|
||
|
// the data in the PB to prevent Max from crashing.
|
||
|
enum Validate
|
||
|
{
|
||
|
kPythonOk,
|
||
|
kPythonNoFile,
|
||
|
kPythonBadVer,
|
||
|
kPythonNoVer,
|
||
|
};
|
||
|
Validate ValidateFile();
|
||
|
|
||
|
const char* GetPythonName();
|
||
|
|
||
|
virtual PythonKeys& GetKeys() { return fModKeys; }
|
||
|
|
||
|
virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
fModKeys.clear();
|
||
|
fOthersKeys.Reset();
|
||
|
|
||
|
return plComponent::DeInit(node, pErrMsg);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CLASS_DESC(plPythonFileComponent, gPythonFileComponentDesc, "Python File", "PythonFile", COMP_TYPE_LOGIC, PYTHON_FILE_CID);
|
||
|
|
||
|
std::string plPythonFileComponent::fPythonError;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Called by the Python File manager
|
||
|
//
|
||
|
|
||
|
// Storage for all the registered Python file types
|
||
|
static std::vector<plAutoUIBlock*> gAutoUIBlocks;
|
||
|
|
||
|
void PythonFile::AddAutoUIBlock(plAutoUIBlock *block)
|
||
|
{
|
||
|
for (int i = 0; i < gAutoUIBlocks.size(); i++)
|
||
|
{
|
||
|
if (gAutoUIBlocks[i]->GetBlockID() == block->GetBlockID())
|
||
|
{
|
||
|
char buf[256];
|
||
|
sprintf(buf,
|
||
|
"Duplicate Python File ID\n\n"
|
||
|
"%s and %s use ID %d\n\n"
|
||
|
"%s will be ignored.",
|
||
|
gAutoUIBlocks[i]->GetName(),
|
||
|
block->GetName(),
|
||
|
block->GetBlockID(),
|
||
|
block->GetName());
|
||
|
hsMessageBox(buf, "Warning", hsMBoxOk);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gAutoUIBlocks.push_back(block);
|
||
|
}
|
||
|
|
||
|
plComponentClassDesc *PythonFile::GetClassDesc()
|
||
|
{
|
||
|
return &gPythonFileComponentDesc;
|
||
|
}
|
||
|
|
||
|
static plAutoUIBlock *FindAutoUI(IParamBlock2 *pb)
|
||
|
{
|
||
|
if (!pb)
|
||
|
return nil;
|
||
|
|
||
|
for (int i = 0; i < gAutoUIBlocks.size(); i++)
|
||
|
{
|
||
|
if (gAutoUIBlocks[i]->GetBlockID() == pb->ID())
|
||
|
return gAutoUIBlocks[i];
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
plPythonFileComponent::plPythonFileComponent()
|
||
|
{
|
||
|
fClassDesc = &gPythonFileComponentDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
hsBool plPythonFileComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if (fPythonError.length() > 0)
|
||
|
{
|
||
|
pErrMsg->Set(true, "Python Error", fPythonError.c_str()).Show();
|
||
|
pErrMsg->Set();
|
||
|
fPythonError = "";
|
||
|
}
|
||
|
|
||
|
fModKeys.clear();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hsBool plPythonFileComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB);
|
||
|
plAutoUIBlock *block = FindAutoUI(pb);
|
||
|
|
||
|
if (!block)
|
||
|
return false;
|
||
|
|
||
|
// if this is a multi-modifier python file component then is this the main (or first) maxnode
|
||
|
bool mainMultiModierNode = false;
|
||
|
|
||
|
plPythonFileMod *mod = TRACKED_NEW plPythonFileMod;
|
||
|
|
||
|
// create the modifier key ourselves so that we can get the name of the modifier in the name
|
||
|
plSceneObject *obj = node->GetSceneObject();
|
||
|
plKey modKey;
|
||
|
if( fCompPB->GetInt( kPythonFileIsGlobal ) )
|
||
|
{
|
||
|
// Do we already have this guy?
|
||
|
plPythonFileMod *existingOne = plPythonFileMod::ConvertNoRef( sCommonPythonLib.FindObject( plPythonFileMod::kGlobalNameKonstant ) );
|
||
|
if( existingOne != nil )
|
||
|
{
|
||
|
// This component already exists, which can happen since it's in a common page. So we need
|
||
|
// to nuke the key and its object so we can reuse it.
|
||
|
modKey = existingOne->GetKey();
|
||
|
|
||
|
// But first detach it from its target sceneObject
|
||
|
if( existingOne->GetTarget( 0 ) != nil )
|
||
|
existingOne->GetTarget( 0 )->GetKey()->Release( modKey );
|
||
|
|
||
|
if( !sCommonPythonLib.RemoveObjectAndKey( modKey ) )
|
||
|
{
|
||
|
pErrMsg->Set( true, "Python File Component Error",
|
||
|
"The global Python File Component %s is attempting to export over an already "
|
||
|
"existing component of the same name, and the exporter is unable to delete the "
|
||
|
"old object to replace it. This would be a good time to call mcn for help.", GetINode()->GetName() ).Show();
|
||
|
pErrMsg->Set( false );
|
||
|
}
|
||
|
|
||
|
// Pointer is invalid now anyways...
|
||
|
existingOne = nil;
|
||
|
}
|
||
|
|
||
|
// Also make sure we have an age SDL object to attach to (currently only used for python, hence why it's safe here)
|
||
|
obj = plSceneObject::ConvertNoRef( sCommonPythonLib.FindObject( plSDL::kAgeSDLObjectName ) );
|
||
|
if( obj != nil )
|
||
|
{
|
||
|
plKey foo = obj->GetKey();
|
||
|
if( !sCommonPythonLib.RemoveObjectAndKey( foo ) )
|
||
|
{
|
||
|
pErrMsg->Set( true, "Python File Component Error",
|
||
|
"The global Python File Component %s is attempting to export over an already "
|
||
|
"existing component of the same name, and the exporter is unable to delete the "
|
||
|
"old sceneObject to replace it. This would be a good time to call mcn for help.", GetINode()->GetName() ).Show();
|
||
|
pErrMsg->Set( false );
|
||
|
}
|
||
|
|
||
|
// Pointer is invalid now anyways...
|
||
|
obj = nil;
|
||
|
}
|
||
|
|
||
|
// Create us a new sceneObject to attach to
|
||
|
obj = TRACKED_NEW plSceneObject;
|
||
|
|
||
|
const plLocation &globalLoc = plPluginResManager::ResMgr()->GetCommonPage( node->GetLocation(), plAgeDescription::kGlobal );
|
||
|
hsAssert( globalLoc.IsValid(), "Invalid common page location!!!" );
|
||
|
modKey = hsgResMgr::ResMgr()->NewKey(plPythonFileMod::kGlobalNameKonstant, mod, globalLoc );
|
||
|
|
||
|
// Make a key for our special sceneObject too
|
||
|
plKey sdlObjectKey = hsgResMgr::ResMgr()->NewKey( plSDL::kAgeSDLObjectName, obj, globalLoc );
|
||
|
plPluginResManager::ResMgr()->AddLooseEnd(sdlObjectKey);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (block->IsMultiModifier())
|
||
|
{
|
||
|
// yup, then only create one modifier that will all the objects will attach to
|
||
|
// in other words, one python module with many attached sceneobjects
|
||
|
if ( fModKeys.empty())
|
||
|
{
|
||
|
// its empty so create the first and only one
|
||
|
modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation());
|
||
|
mainMultiModierNode = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// else we just take the first one (should be the only one)
|
||
|
PythonKeys::iterator pos;
|
||
|
pos = fModKeys.begin();
|
||
|
modKey = pos->second;
|
||
|
|
||
|
// Guess we won't be using that modifier we new'd up there. Does that mean we should delete it? Sure.
|
||
|
delete mod;
|
||
|
mod = nil;
|
||
|
}
|
||
|
}
|
||
|
else // else, nope... create a modifier for each object its attached to
|
||
|
{
|
||
|
modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation());
|
||
|
}
|
||
|
}
|
||
|
hsgResMgr::ResMgr()->AddViaNotify(modKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef);
|
||
|
fModKeys[node] = modKey;
|
||
|
|
||
|
// only let non-multimodifier or multimodifier then only the main node register for notifies
|
||
|
if ( !block->IsMultiModifier() || mainMultiModierNode )
|
||
|
{
|
||
|
int nParams = block->NumParams();
|
||
|
for (int i = 0; i < nParams; i++)
|
||
|
{
|
||
|
plAutoUIParam *param = block->GetParam(i);
|
||
|
|
||
|
if (param->GetParamType() == plAutoUIParam::kTypeActivator)
|
||
|
{
|
||
|
// Register the modifier to recieve notifies from this activator.
|
||
|
// We'll let the modifier know the activator key when it's available,
|
||
|
// in the convert pass.
|
||
|
int numcomps = param->GetCount(pb);
|
||
|
for (int j=0; j< numcomps; j++ )
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb,j);
|
||
|
if (comp)
|
||
|
comp->AddReceiverKey(modKey);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (param->GetParamType() == plAutoUIParam::kTypeGUIDialog)
|
||
|
{
|
||
|
// Register the modifier to recieve notifies from this activator.
|
||
|
// We'll let the modifier know the activator key when it's available,
|
||
|
// in the convert pass.
|
||
|
int numcomps = param->GetCount(pb);
|
||
|
for (int j=0; j< numcomps; j++ )
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb,j);
|
||
|
if (comp && comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID )
|
||
|
{
|
||
|
// convert the comp to a GUIDialog component, so we can talk to it
|
||
|
plGUIDialogComponent *dialog_comp = (plGUIDialogComponent*)comp;
|
||
|
dialog_comp->SetNotifyReceiver(modKey);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#include "plActivatorBaseComponent.h"
|
||
|
#include "../pnKeyedObject/plKey.h"
|
||
|
#include "plResponderComponent.h"
|
||
|
|
||
|
hsBool plPythonFileComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB);
|
||
|
plAutoUIBlock *block = FindAutoUI(pb);
|
||
|
|
||
|
if (!block)
|
||
|
return false;
|
||
|
|
||
|
plKey modKey = fModKeys[node];
|
||
|
plPythonFileMod *mod = plPythonFileMod::ConvertNoRef(modKey->GetObjectPtr());
|
||
|
|
||
|
// add in all the receivers that want to be notified from the Python coded script
|
||
|
int j;
|
||
|
for (j = 0; j < fOthersKeys.Count(); j++)
|
||
|
{
|
||
|
mod->AddToNotifyList(fOthersKeys[j]);
|
||
|
}
|
||
|
// remove all the Keys we know about to be re-built on the next convert
|
||
|
fOthersKeys.Reset();
|
||
|
|
||
|
// set the name of the source file
|
||
|
mod->SetSourceFile(block->GetName());
|
||
|
|
||
|
int nParams = block->NumParams();
|
||
|
for (int i = 0; i < nParams; i++)
|
||
|
{
|
||
|
plAutoUIParam *param = block->GetParam(i);
|
||
|
|
||
|
plPythonParameter pyParam;
|
||
|
pyParam.fID = param->GetID();
|
||
|
|
||
|
// Get the data for the param
|
||
|
switch (param->GetParamType())
|
||
|
{
|
||
|
case plAutoUIParam::kTypeBool:
|
||
|
pyParam.SetToBoolean(param->GetBool(pb));
|
||
|
mod->AddParameter(pyParam);
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeInt:
|
||
|
pyParam.SetToInt(param->GetInt(pb));
|
||
|
mod->AddParameter(pyParam);
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeFloat:
|
||
|
pyParam.SetToFloat(param->GetFloat(pb));
|
||
|
mod->AddParameter(pyParam);
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeString:
|
||
|
pyParam.SetToString(param->GetString(pb));
|
||
|
mod->AddParameter(pyParam);
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeSceneObj:
|
||
|
{
|
||
|
int numKeys = param->GetCount(pb);
|
||
|
hsBool found_atleast_one_good_one = false;
|
||
|
for (int i = 0; i < numKeys; i++)
|
||
|
{
|
||
|
plKey skey = param->GetKey(pb, i);
|
||
|
if ( skey != nil )
|
||
|
{
|
||
|
pyParam.SetToSceneObject(skey, true);
|
||
|
// make sure that there really was a sceneobject
|
||
|
mod->AddParameter(pyParam);
|
||
|
found_atleast_one_good_one = true;
|
||
|
}
|
||
|
}
|
||
|
if ( !found_atleast_one_good_one )
|
||
|
{
|
||
|
char buf[512];
|
||
|
sprintf(buf,"The sceneobject attribute (ID=%d) that was selected in %s PythonFile, somehow does not exist!?",
|
||
|
pyParam.fID,this->GetINode()->GetName());
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeComponent:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
hsBool found_atleast_one_good_one = false;
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp)
|
||
|
{
|
||
|
for (int j = 0; j < comp->NumTargets(); j++)
|
||
|
{
|
||
|
plKey responderKey = Responder::GetKey(comp, comp->GetTarget(j));
|
||
|
if ( responderKey != nil )
|
||
|
{
|
||
|
pyParam.SetToResponder(responderKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
found_atleast_one_good_one = true;
|
||
|
}
|
||
|
}
|
||
|
if ( !found_atleast_one_good_one )
|
||
|
{
|
||
|
char buf[512];
|
||
|
sprintf(buf,"The responder attribute %s that was selected in %s PythonFile, somehow does not exist!?",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeActivator:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
// make sure we found a comp and then see if it is an activator type
|
||
|
if (comp && comp->CanConvertToType(ACTIVATOR_BASE_CID))
|
||
|
{
|
||
|
plActivatorBaseComponent *activator = (plActivatorBaseComponent*)comp;
|
||
|
const plActivatorBaseComponent::LogicKeys& logicKeys = activator->GetLogicKeys();
|
||
|
plActivatorBaseComponent::LogicKeys::const_iterator it;
|
||
|
for (it = logicKeys.begin(); it != logicKeys.end(); it++)
|
||
|
{
|
||
|
pyParam.SetToActivator(it->second);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
// do special stuff for Volume sensors because they are stupid turds
|
||
|
if (comp->ClassID() == VOLUMEGADGET_CID)
|
||
|
{
|
||
|
plVolumeGadgetComponent* pClick = (plVolumeGadgetComponent*)comp;
|
||
|
const plVolumeGadgetComponent::LogicKeys &logicKeys2 = pClick->GetLogicOutKeys();
|
||
|
plVolumeGadgetComponent::LogicKeys::const_iterator VGit;
|
||
|
for (VGit = logicKeys2.begin(); VGit != logicKeys2.end(); VGit++)
|
||
|
{
|
||
|
pyParam.SetToActivator(VGit->second);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// now see if it is a PythonFile kinda activator thingy
|
||
|
else if (comp && comp->ClassID() == PYTHON_FILE_CID)
|
||
|
{
|
||
|
plPythonFileComponent *pyfact = (plPythonFileComponent*)comp;
|
||
|
const plPythonFileComponent::PythonKeys& pythonKeys = pyfact->GetKeys();
|
||
|
plPythonFileComponent::PythonKeys::const_iterator it;
|
||
|
for (it = pythonKeys.begin(); it != pythonKeys.end(); it++)
|
||
|
{
|
||
|
pyParam.SetToActivator(it->second);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeDynamicText:
|
||
|
{
|
||
|
int numKeys = param->GetCount(pb);
|
||
|
for (int i = 0; i < numKeys; i++)
|
||
|
{
|
||
|
plKey key = param->GetKey(pb, i);
|
||
|
// make sure we got a key and that it is a DynamicTextMap
|
||
|
if (key && plDynamicTextMap::ConvertNoRef(key->GetObjectPtr()) )
|
||
|
{
|
||
|
pyParam.SetToDynamicText(key);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeGUIDialog:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp)
|
||
|
{
|
||
|
if (comp && comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID )
|
||
|
{
|
||
|
// convert the comp to a GUIDialog component, so we can talk to it
|
||
|
plGUIDialogComponent *dialog_comp = (plGUIDialogComponent*)comp;
|
||
|
plKey dialogKey = dialog_comp->GetModifierKey();
|
||
|
pyParam.SetToGUIDialog(dialogKey);
|
||
|
if ( pyParam.fObjectKey == nil )
|
||
|
{
|
||
|
char buf[512];
|
||
|
sprintf(buf,"The GUIDialog attribute %s that was selected in %s PythonFile, somehow does not exist!?",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
else
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeGUIPopUpMenu:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp)
|
||
|
{
|
||
|
if (comp && comp->ClassID() == GUI_MENUANCHOR_CLASSID )
|
||
|
{
|
||
|
// convert the comp to a GUIPopUpMenu component, so we can talk to it
|
||
|
plGUIMenuComponent *guiComp = (plGUIMenuComponent*)comp;
|
||
|
plKey key = guiComp->GetConvertedMenuKey();
|
||
|
pyParam.SetToGUIPopUpMenu( key );
|
||
|
if ( pyParam.fObjectKey == nil )
|
||
|
{
|
||
|
char buf[512];
|
||
|
sprintf(buf,"The GUIPopUpMenu attribute %s that was selected in %s PythonFile, somehow does not exist!?",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
else
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeGUISkin:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp)
|
||
|
{
|
||
|
if (comp && comp->ClassID() == GUI_SKIN_CLASSID )
|
||
|
{
|
||
|
// convert the comp to a GUISkin component, so we can talk to it
|
||
|
plGUISkinComp *guiComp = (plGUISkinComp *)comp;
|
||
|
plKey key = guiComp->GetConvertedSkinKey();
|
||
|
pyParam.SetToGUISkin( key );
|
||
|
if ( pyParam.fObjectKey == nil )
|
||
|
{
|
||
|
char buf[512];
|
||
|
sprintf(buf,"The GUISkin attribute %s that was selected in %s PythonFile, somehow does not exist!?",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
else
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeExcludeRegion:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
int number_of_real_targets_found = 0;
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp && comp->ClassID() == XREGION_CID )
|
||
|
{
|
||
|
for (int j = 0; j < comp->NumTargets(); j++)
|
||
|
{
|
||
|
plExcludeRegionComponent *excomp = (plExcludeRegionComponent*)comp;
|
||
|
plKey exKey = excomp->GetKey((plMaxNode*)(comp->GetTarget(j)));
|
||
|
if ( exKey != nil )
|
||
|
{
|
||
|
// only get one real target, just count the rest
|
||
|
if ( number_of_real_targets_found == 0 )
|
||
|
{
|
||
|
pyParam.SetToExcludeRegion(exKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
number_of_real_targets_found += 1;
|
||
|
}
|
||
|
}
|
||
|
if ( number_of_real_targets_found != 1 )
|
||
|
{
|
||
|
// there is zero or more than one node attached to this exclude region
|
||
|
char buf[512];
|
||
|
if ( number_of_real_targets_found == 0 )
|
||
|
sprintf(buf,"The ExcludeRegion %s that was selected as an attribute in %s PythonFile, has no scene nodes attached.",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
else
|
||
|
sprintf(buf,"The ExcludeRegion %s that was selected as an attribute in %s PythonFile, has more than one scene node attached (using first one found).",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeWaterComponent:
|
||
|
{
|
||
|
plComponentBase* comp = param->GetComponent(pb, 0);
|
||
|
plWaveSetBase* wsb = nil;
|
||
|
|
||
|
if (comp)
|
||
|
{
|
||
|
wsb = plWaterComponent::GetWaveSet(comp->GetINode());
|
||
|
|
||
|
if (wsb != nil)
|
||
|
{
|
||
|
plKey waterKey = wsb->GetKey();
|
||
|
if ( waterKey != nil )
|
||
|
{
|
||
|
pyParam.SetToWaterComponent(waterKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeSwimCurrentInterface:
|
||
|
{
|
||
|
plComponentBase* comp = param->GetComponent(pb, 0);
|
||
|
plSwimRegionInterface* sri = nil;
|
||
|
|
||
|
if (comp && comp->ClassID() == PHYS_SWIMSURFACE_CID)
|
||
|
{
|
||
|
plSwim2DComponent* swimcomp = (plSwim2DComponent*)comp;
|
||
|
std::map<plMaxNode*, plSwimRegionInterface*>::const_iterator containsNode;
|
||
|
plMaxNode* mnode = nil;
|
||
|
|
||
|
for (int i = 0; i < swimcomp->NumTargets(); i++)
|
||
|
{
|
||
|
mnode = (plMaxNode*)swimcomp->GetTarget(i);
|
||
|
containsNode = swimcomp->fSwimRegions.find(mnode);
|
||
|
if ( containsNode != swimcomp->fSwimRegions.end() )
|
||
|
{
|
||
|
sri = swimcomp->fSwimRegions[mnode];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sri != nil)
|
||
|
{
|
||
|
plKey swimKey = sri->GetKey();
|
||
|
if ( swimKey != nil )
|
||
|
{
|
||
|
pyParam.SetToSwimCurrentInterface(swimKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeClusterComponent:
|
||
|
{
|
||
|
plComponentBase* comp = param->GetComponent(pb, 0);
|
||
|
plClusterGroup* clusterGroup = nil;
|
||
|
|
||
|
if (comp && comp->ClassID() == CLUSTER_COMP_CID)
|
||
|
{
|
||
|
plClusterComponent* clusterComp = (plClusterComponent*)comp;
|
||
|
int numGroups = clusterComp->GetNumGroups();
|
||
|
int i;
|
||
|
for (i=0; i<numGroups; i++)
|
||
|
{
|
||
|
plClusterGroup* group = clusterComp->GetGroup(i);
|
||
|
plKey groupKey = group->GetKey();
|
||
|
if (groupKey != nil)
|
||
|
{
|
||
|
pyParam.SetToClusterComponent(groupKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeAnimation:
|
||
|
{
|
||
|
int count = param->GetCount(pb);
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp && ( comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID ) )
|
||
|
{
|
||
|
plAnimComponentBase *animcomp = (plAnimComponentBase*)comp;
|
||
|
// save out the animation name first
|
||
|
const char *tempAnimName = animcomp->GetAnimName();
|
||
|
if (tempAnimName == nil)
|
||
|
pyParam.SetToAnimationName(ENTIRE_ANIMATION_NAME);
|
||
|
else
|
||
|
pyParam.SetToAnimationName(tempAnimName);
|
||
|
mod->AddParameter(pyParam);
|
||
|
// gather up all the modkeys for all the targets attached to this animation component
|
||
|
int j;
|
||
|
for ( j=0; j<comp->NumTargets(); j++ )
|
||
|
{
|
||
|
pyParam.SetToAnimation(animcomp->GetModKey((plMaxNode*)(comp->GetTarget(j))));
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeBehavior:
|
||
|
{
|
||
|
// The Behavior attribute is One-Shots and Multi-stage behaviors
|
||
|
// For Python usage: we will only allow using behaviors that are
|
||
|
// attached to one position node. In other words, Python will only
|
||
|
// be used for special cases.
|
||
|
int count = param->GetCount(pb);
|
||
|
int number_of_real_targets_found = 0;
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plComponentBase *comp = param->GetComponent(pb, i);
|
||
|
if (comp && comp->ClassID() == ONESHOTCLASS_ID )
|
||
|
{
|
||
|
// gather up all the modkeys for all the targets attached to this animation component
|
||
|
int j;
|
||
|
for ( j=0; j<comp->NumTargets(); j++ )
|
||
|
{
|
||
|
plKey behKey = OneShotComp::GetOneShotKey(comp,(plMaxNode*)(comp->GetTarget(j)));
|
||
|
if ( behKey != nil )
|
||
|
{
|
||
|
// only get one real target, just count the rest
|
||
|
if ( number_of_real_targets_found == 0 )
|
||
|
{
|
||
|
pyParam.SetToBehavior(behKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
number_of_real_targets_found += 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (comp && comp->ClassID() == MULTISTAGE_BEH_CID )
|
||
|
{
|
||
|
// gather up all the modkeys for all the targets attached to this animation component
|
||
|
int j;
|
||
|
for ( j=0; j<comp->NumTargets(); j++ )
|
||
|
{
|
||
|
plKey behKey = MultiStageBeh::GetMultiStageBehKey(comp,(plMaxNode*)(comp->GetTarget(j)));
|
||
|
if ( behKey != nil )
|
||
|
{
|
||
|
// only get one real target, just count the rest
|
||
|
if ( number_of_real_targets_found == 0 )
|
||
|
{
|
||
|
pyParam.SetToBehavior(behKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
number_of_real_targets_found += 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( number_of_real_targets_found != 1 )
|
||
|
{
|
||
|
// there is zero or more than one node attached to this exclude region
|
||
|
char buf[512];
|
||
|
if ( number_of_real_targets_found == 0 )
|
||
|
sprintf(buf,"The Behavior component %s that was selected as an attribute in %s PythonFile, has no scene nodes attached.",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
else
|
||
|
sprintf(buf,"The Behavior component %s that was selected as an attribute in %s PythonFile, has more than one scene node attached (using first one found).",
|
||
|
comp->GetINode()->GetName(),this->GetINode()->GetName());
|
||
|
|
||
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeMaterial:
|
||
|
{
|
||
|
int numKeys = param->GetCount(pb);
|
||
|
for (int i = 0; i < numKeys; i++)
|
||
|
{
|
||
|
plKey key = param->GetKey(pb, i);
|
||
|
// make sure we got a key and that it is a plMipmap
|
||
|
if (key && plMipmap::ConvertNoRef(key->GetObjectPtr()) )
|
||
|
{
|
||
|
pyParam.SetToMaterial(key);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeMaterialAnimation:
|
||
|
{
|
||
|
plPickMaterialAnimationButtonParam* matAnim = (plPickMaterialAnimationButtonParam*)param;
|
||
|
matAnim->CreateKeyArray(pb);
|
||
|
|
||
|
int numKeys = param->GetCount(pb);
|
||
|
for (int i = 0; i < numKeys; i++)
|
||
|
{
|
||
|
plKey key = param->GetKey(pb, i);
|
||
|
|
||
|
if ( key )
|
||
|
{
|
||
|
pyParam.SetToMaterialAnimation(key);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
matAnim->DestroyKeyArray();
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeDropDownList:
|
||
|
pyParam.SetToString(param->GetString(pb));
|
||
|
mod->AddParameter(pyParam);
|
||
|
break;
|
||
|
|
||
|
case plAutoUIParam::kTypeGrassComponent:
|
||
|
{
|
||
|
plComponentBase* comp = param->GetComponent(pb, 0);
|
||
|
plGrassShaderMod* shader = nil;
|
||
|
|
||
|
if (comp)
|
||
|
{
|
||
|
shader = plGrassComponent::GetShader(comp->GetINode());
|
||
|
|
||
|
if (shader != nil)
|
||
|
{
|
||
|
plKey shaderKey = shader->GetKey();
|
||
|
if ( shaderKey != nil )
|
||
|
{
|
||
|
pyParam.SetToGrassShaderComponent(shaderKey);
|
||
|
mod->AddParameter(pyParam);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
plPythonFileComponent::Validate plPythonFileComponent::ValidateFile()
|
||
|
{
|
||
|
// Make sure the type is in the valid range
|
||
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB);
|
||
|
plAutoUIBlock *block = FindAutoUI(pb);
|
||
|
if (!block || fCompPB->GetInt(kPythonVersion) > block->GetVersion())
|
||
|
{
|
||
|
// Bad type, clear out the PB so this won't blow up during a save
|
||
|
fCompPB->SetValue(kPythonFilePB, 0, (ReferenceTarget*)nil);
|
||
|
|
||
|
if (!block)
|
||
|
return kPythonNoFile;
|
||
|
else
|
||
|
return kPythonBadVer;
|
||
|
}
|
||
|
|
||
|
// Got a newer version of the Python file, update our version
|
||
|
if (fCompPB->GetInt(kPythonVersion) < block->GetVersion())
|
||
|
fCompPB->SetValue(kPythonVersion, 0, block->GetVersion());
|
||
|
|
||
|
if (block->GetVersion() == 0)
|
||
|
return kPythonNoVer;
|
||
|
|
||
|
return kPythonOk;
|
||
|
}
|
||
|
|
||
|
const char* plPythonFileComponent::GetPythonName()
|
||
|
{
|
||
|
// Make sure the type is in the valid range
|
||
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB);
|
||
|
plAutoUIBlock *block = FindAutoUI(pb);
|
||
|
if (block)
|
||
|
return block->GetName();
|
||
|
|
||
|
return "(unknown)";
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Verify that all Python File components in the scene are OK, and warn the user
|
||
|
// if any aren't
|
||
|
//
|
||
|
|
||
|
class plPythonError
|
||
|
{
|
||
|
public:
|
||
|
plMaxNode* node;
|
||
|
const char* pythonName;
|
||
|
plPythonFileComponent::Validate error;
|
||
|
|
||
|
bool operator< (const plPythonError& rhs) const { return rhs.node < node; }
|
||
|
};
|
||
|
|
||
|
typedef std::set<plPythonError> ErrorSet;
|
||
|
|
||
|
static void CheckPythonFileCompsRecur(plMaxNode *node, ErrorSet& badNodes)
|
||
|
{
|
||
|
plComponentBase *comp = node->ConvertToComponent();
|
||
|
if (comp && comp->ClassID() == PYTHON_FILE_CID)
|
||
|
{
|
||
|
plPythonFileComponent* pyComp = (plPythonFileComponent*)comp;
|
||
|
|
||
|
const char* pythonName = pyComp->GetPythonName();
|
||
|
|
||
|
plPythonFileComponent::Validate valid = pyComp->ValidateFile();
|
||
|
if (valid != plPythonFileComponent::kPythonOk)
|
||
|
{
|
||
|
plPythonError err;
|
||
|
err.node = node;
|
||
|
err.pythonName = pythonName;
|
||
|
err.error = valid;
|
||
|
badNodes.insert(err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < node->NumberOfChildren(); i++)
|
||
|
CheckPythonFileCompsRecur((plMaxNode*)node->GetChildNode(i), badNodes);
|
||
|
}
|
||
|
|
||
|
static BOOL CALLBACK WarnDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
if (msg == WM_INITDIALOG)
|
||
|
{
|
||
|
HWND hList = GetDlgItem(hDlg, IDC_COMP_LIST);
|
||
|
|
||
|
LVCOLUMN lvc;
|
||
|
lvc.mask = LVCF_TEXT;
|
||
|
lvc.pszText = "Component";
|
||
|
ListView_InsertColumn(hList, 0, &lvc);
|
||
|
lvc.pszText = "Python File";
|
||
|
ListView_InsertColumn(hList, 1, &lvc);
|
||
|
lvc.pszText = "Error";
|
||
|
ListView_InsertColumn(hList, 2, &lvc);
|
||
|
|
||
|
ErrorSet *badNodes = (ErrorSet*)lParam;
|
||
|
ErrorSet::iterator it = badNodes->begin();
|
||
|
for (; it != badNodes->end(); it++)
|
||
|
{
|
||
|
plPythonError& err = *it;
|
||
|
|
||
|
plMaxNode* node = err.node;
|
||
|
plPythonFileComponent* comp = (plPythonFileComponent*)node->ConvertToComponent();
|
||
|
|
||
|
LVITEM lvi;
|
||
|
lvi.mask = LVIF_TEXT;
|
||
|
lvi.pszText = (char*)node->GetName();
|
||
|
lvi.iItem = 0;
|
||
|
lvi.iSubItem = 0;
|
||
|
int idx = ListView_InsertItem(hList, &lvi);
|
||
|
|
||
|
ListView_SetItemText(hList, idx, 1, (char*)err.pythonName);
|
||
|
|
||
|
switch (err.error)
|
||
|
{
|
||
|
case plPythonFileComponent::Validate::kPythonBadVer:
|
||
|
ListView_SetItemText(hList, idx, 2, "Old Version");
|
||
|
break;
|
||
|
case plPythonFileComponent::Validate::kPythonNoVer:
|
||
|
ListView_SetItemText(hList, idx, 2, "No Version");
|
||
|
break;
|
||
|
case plPythonFileComponent::Validate::kPythonNoFile:
|
||
|
ListView_SetItemText(hList, idx, 2, "No File/Python Error");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE);
|
||
|
ListView_SetColumnWidth(hList, 1, LVSCW_AUTOSIZE);
|
||
|
ListView_SetColumnWidth(hList, 2, LVSCW_AUTOSIZE);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (msg == WM_COMMAND)
|
||
|
{
|
||
|
if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL))
|
||
|
{
|
||
|
DestroyWindow(hDlg);
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void WriteBadPythonText(ErrorSet& badNodes)
|
||
|
{
|
||
|
ErrorSet::iterator it = badNodes.begin();
|
||
|
for (; it != badNodes.end(); it++)
|
||
|
{
|
||
|
plPythonError& err = *it;
|
||
|
|
||
|
const char* compName = err.node->GetName();
|
||
|
const char* pythonFile = err.pythonName;
|
||
|
const char* errorText = "";
|
||
|
switch (err.error)
|
||
|
{
|
||
|
case plPythonFileComponent::kPythonBadVer:
|
||
|
errorText = "Old Version";
|
||
|
break;
|
||
|
case plPythonFileComponent::Validate::kPythonNoVer:
|
||
|
errorText = "No Version";
|
||
|
break;
|
||
|
case plPythonFileComponent::Validate::kPythonNoFile:
|
||
|
errorText = "No File/Python Error";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
std::string& pythonError = plPythonFileComponent::fPythonError;
|
||
|
pythonError += "Python component ";
|
||
|
pythonError += compName;
|
||
|
pythonError += " (file ";
|
||
|
pythonError += pythonFile;
|
||
|
pythonError += ") is bad. Reason: ";
|
||
|
pythonError += errorText;
|
||
|
pythonError += "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void NotifyProc(void *param, NotifyInfo *info)
|
||
|
{
|
||
|
static bool gotBadPython = false;
|
||
|
|
||
|
if (info->intcode == NOTIFY_FILE_POST_OPEN)
|
||
|
{
|
||
|
ErrorSet badNodes;
|
||
|
CheckPythonFileCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), badNodes);
|
||
|
|
||
|
if (badNodes.size() > 0)
|
||
|
{
|
||
|
gotBadPython = true;
|
||
|
|
||
|
if (hsMessageBox_SuppressPrompts)
|
||
|
WriteBadPythonText(badNodes);
|
||
|
else
|
||
|
CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_PYTHON_FILE_WARN), GetCOREInterface()->GetMAXHWnd(), WarnDialogProc, (LPARAM)&badNodes);
|
||
|
}
|
||
|
else
|
||
|
gotBadPython = false;
|
||
|
}
|
||
|
else if (info->intcode == NOTIFY_FILE_PRE_SAVE)
|
||
|
{
|
||
|
if (gotBadPython)
|
||
|
{
|
||
|
hsMessageBox("This file has bad Python components in it, you REALLY shouldn't save it.",
|
||
|
"Python Component Warning",
|
||
|
hsMBoxOk);
|
||
|
}
|
||
|
}
|
||
|
else if (info->intcode == NOTIFY_SYSTEM_POST_RESET ||
|
||
|
info->intcode == NOTIFY_SYSTEM_POST_NEW)
|
||
|
{
|
||
|
gotBadPython = false;
|
||
|
}
|
||
|
// Have to do this at system shutdown 2 (really final shutdown) because it deletes the
|
||
|
// descriptor, which Max may still try to use in between shutdown 1 and 2.
|
||
|
else if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN2)
|
||
|
{
|
||
|
int count = gAutoUIBlocks.size();
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
delete gAutoUIBlocks[i];
|
||
|
}
|
||
|
gAutoUIBlocks.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
class plPythonFileComponentProc : public ParamMap2UserDlgProc
|
||
|
{
|
||
|
public:
|
||
|
plPythonFileComponentProc() : fAutoUI(nil) {}
|
||
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||
|
void DeleteThis() { DestroyAutoUI(); }
|
||
|
|
||
|
protected:
|
||
|
plAutoUIBlock *fAutoUI;
|
||
|
|
||
|
void CreateAutoUI(plAutoUIBlock *autoUI, IParamBlock2 *pb);
|
||
|
void DestroyAutoUI();
|
||
|
};
|
||
|
static plPythonFileComponentProc gPythonFileProc;
|
||
|
|
||
|
ParamBlockDesc2 gPythonFileBlk
|
||
|
(
|
||
|
plComponent::kBlkComp, _T("PythonFile"), 0, &gPythonFileComponentDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
||
|
|
||
|
//rollout
|
||
|
IDD_COMP_PYTHON_FILE, IDS_COMP_PYTHON, 0, 0, &gPythonFileProc,
|
||
|
|
||
|
kPythonFilePB, _T("pb"), TYPE_REFTARG, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kPythonVersion, _T("version"), TYPE_INT, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kPythonFileIsGlobal, _T("isGlobal"), TYPE_BOOL, 0, 0,
|
||
|
p_ui, TYPE_SINGLECHEKBOX, IDC_PYTHON_GLOBAL,
|
||
|
p_default, FALSE,
|
||
|
end,
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
#define WM_LOAD_AUTO_UI WM_APP+1
|
||
|
|
||
|
BOOL plPythonFileComponentProc::DlgProc(TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
IParamBlock2 *pb = pmap->GetParamBlock();
|
||
|
|
||
|
HWND hCombo = GetDlgItem(hWnd, IDC_PYTHON_FILE);
|
||
|
ComboBox_ResetContent(hCombo);
|
||
|
|
||
|
SetDlgItemText(hWnd, IDC_VER_TEXT, "");
|
||
|
|
||
|
IParamBlock2 *pythonPB = (IParamBlock2*)pb->GetReferenceTarget(kPythonFilePB);
|
||
|
plAutoUIBlock *pythonBlock = FindAutoUI(pythonPB);
|
||
|
|
||
|
int numPythonFiles = gAutoUIBlocks.size();
|
||
|
for (int i = 0; i < numPythonFiles; i++)
|
||
|
{
|
||
|
plAutoUIBlock *block = gAutoUIBlocks[i];
|
||
|
const char *name = block->GetName();
|
||
|
|
||
|
int idx = ComboBox_AddString(hCombo, name);
|
||
|
ComboBox_SetItemData(hCombo, idx, i);
|
||
|
if (block == pythonBlock)
|
||
|
{
|
||
|
ComboBox_SetCurSel(hCombo, idx);
|
||
|
SetDlgItemInt(hWnd, IDC_VER_TEXT, block->GetVersion(), TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Crappy hack, see WM_LOAD_AUTO_UI
|
||
|
PostMessage(hWnd, WM_LOAD_AUTO_UI, 0, 0);
|
||
|
}
|
||
|
return TRUE;
|
||
|
|
||
|
// Crappy hack. If we put up the python file UI before returning from WM_INITDIALOG
|
||
|
// it will show up ABOVE the main UI. To get around this we post a message that won't
|
||
|
// get processed until after the main UI is put up.
|
||
|
case WM_LOAD_AUTO_UI:
|
||
|
{
|
||
|
IParamBlock2 *pb = pmap->GetParamBlock();
|
||
|
|
||
|
IParamBlock2 *pythonPB = (IParamBlock2*)pb->GetReferenceTarget(kPythonFilePB);
|
||
|
plAutoUIBlock *pythonBlock = FindAutoUI(pythonPB);
|
||
|
|
||
|
if (pythonBlock && pythonPB)
|
||
|
CreateAutoUI(pythonBlock, pythonPB);
|
||
|
}
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_PYTHON_FILE)
|
||
|
{
|
||
|
HWND hCombo = (HWND)lParam;
|
||
|
int sel = ComboBox_GetCurSel(hCombo);
|
||
|
|
||
|
int type = ComboBox_GetItemData(hCombo, sel);
|
||
|
|
||
|
plAutoUIBlock *block = gAutoUIBlocks[type];
|
||
|
IParamBlock2 *autoPB = block->CreatePB();
|
||
|
|
||
|
CreateAutoUI(block, autoPB);
|
||
|
|
||
|
IParamBlock2 *pb = pmap->GetParamBlock();
|
||
|
pb->SetValue(kPythonFilePB, 0, (ReferenceTarget*)autoPB);
|
||
|
|
||
|
SetDlgItemInt(hWnd, IDC_VER_TEXT, block->GetVersion(), TRUE);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void plPythonFileComponentProc::CreateAutoUI(plAutoUIBlock *autoUI, IParamBlock2 *pb)
|
||
|
{
|
||
|
DestroyAutoUI();
|
||
|
|
||
|
if (autoUI && pb)
|
||
|
{
|
||
|
fAutoUI = autoUI;
|
||
|
autoUI->CreateAutoRollup(pb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void plPythonFileComponentProc::DestroyAutoUI()
|
||
|
{
|
||
|
if (fAutoUI)
|
||
|
{
|
||
|
fAutoUI->DestroyAutoRollup();
|
||
|
fAutoUI = nil;
|
||
|
}
|
||
|
}
|