/*==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 .
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 "plComponentBase.h"
#include "plComponentReg.h"
#include "../MaxMain/plPlasmaRefMsgs.h"
#include "../MaxMain/plMaxNodeBase.h"
#include "plAutoUIComp.h"
plComponentBase::plComponentBase() : fClassDesc(nil), fCompPB(nil), fTargsPB(nil)
{
}
plComponentBase::~plComponentBase()
{
DeleteAllRefsFromMe();
}
CreateMouseCallBack* plComponentBase::GetCreateMouseCallBack()
{
return NULL;
}
void plComponentBase::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev)
{
fClassDesc->BeginEditParams(ip, this, flags, prev);
}
void plComponentBase::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next)
{
fClassDesc->EndEditParams(ip, this, flags, next);
}
int plComponentBase::NumParamBlocks()
{
return 2;
}
IParamBlock2 *plComponentBase::GetParamBlock(int i)
{
if (i == kRefComp)
return fCompPB;
else if (i == kRefTargs)
return fTargsPB;
return nil;
}
IParamBlock2 *plComponentBase::GetParamBlockByID(BlockID id)
{
if (fCompPB && fCompPB->ID() == id)
return fCompPB;
else if (fTargsPB && fTargsPB->ID() == id)
return fTargsPB;
return nil;
}
// So our animatables will show up in the trackview
int plComponentBase::NumSubs()
{
return 1;
}
Animatable *plComponentBase::SubAnim(int i)
{
return fCompPB;
}
TSTR plComponentBase::SubAnimName(int i)
{
return fClassDesc->ClassName();
}
RefTargetHandle plComponentBase::Clone(RemapDir &remap)
{
plComponentBase *obj = (plComponentBase*)fClassDesc->Create(false);
// Do the base clone
BaseClone(this, obj, remap);
// Copy our references
if (fCompPB)
obj->ReplaceReference(kRefComp, fCompPB->Clone(remap));
if (fTargsPB)
obj->ReplaceReference(kRefTargs, fTargsPB->Clone(remap));
return obj;
}
void plComponentBase::BuildMesh(TimeValue t)
{
}
void plComponentBase::FreeCaches()
{
}
void plComponentBase::GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box)
{
box.MakeCube(Point3(0,0,0), 0);
}
void plComponentBase::GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box)
{
box.MakeCube(Point3(0,0,0), 0);
}
int plComponentBase::Display(TimeValue t, INode *node, ViewExp *vpt, int flags)
{
return 0;
}
int plComponentBase::HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt)
{
return 0;
}
int plComponentBase::NumRefs()
{
return 2;
}
RefTargetHandle plComponentBase::GetReference(int i)
{
if (i == kRefComp)
return fCompPB;
else if (i == kRefTargs)
return fTargsPB;
return nil;
}
void plComponentBase::SetReference(int i, RefTargetHandle rtarg)
{
if (i == kRefTargs)
fTargsPB = (IParamBlock2*)rtarg;
else if (i == kRefComp)
fCompPB = (IParamBlock2*)rtarg;
}
RefResult plComponentBase::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message)
{
return REF_SUCCEED;
}
IOResult plComponentBase::Save(ISave* isave)
{
return IO_OK;
}
IOResult plComponentBase::Load(ILoad* iload)
{
return IO_OK;
}
void plComponentBase::AddTargetsToList(INodeTab& list)
{
int i;
for( i = 0; i < NumTargets(); i++ )
{
INode* targ = GetTarget(i);
if( targ )
list.Append(1, &targ);
}
}
UInt32 plComponentBase::NumTargets()
{
if (fTargsPB)
return fTargsPB->Count(kTargs);
return 0;
}
plMaxNodeBase *plComponentBase::GetTarget(UInt32 i)
{
if (fTargsPB && i < NumTargets())
return (plMaxNodeBase*)fTargsPB->GetINode(kTargs, 0, i);
return nil;
}
void plComponentBase::AddTarget(plMaxNodeBase *target)
{
if (!target)
return;
// Make sure we don't already ref this
UInt32 count = fTargsPB->Count(kTargs);
for (UInt32 i = 0; i < count; i++)
{
if (fTargsPB->GetINode(kTargs, 0, i) == target)
return;
}
// Add it to our list
fTargsPB->Append(kTargs, 1, (INode**)&target);
NotifyDependents(FOREVER, (PartID)target, REFMSG_USER_TARGET_ADD);
}
void plComponentBase::DeleteTarget(plMaxNodeBase *target)
{
if (!target)
return;
UInt32 count = fTargsPB->Count(kTargs);
for (UInt32 i = 0; i < count; i++)
{
if (fTargsPB->GetINode(kTargs, 0, i) == target)
{
fTargsPB->Delete(kTargs, i, 1);
NotifyDependents(FOREVER, (PartID)target, REFMSG_USER_TARGET_DELETE);
return;
}
}
}
void plComponentBase::DeleteAllTargets()
{
while (fTargsPB->Count(kTargs) > 0)
{
INode *node = fTargsPB->GetINode(kTargs);
if (node)
NotifyDependents(FOREVER, (PartID)node, REFMSG_USER_TARGET_DELETE);
fTargsPB->Delete(kTargs, 0, 1);
}
}
bool plComponentBase::IsTarget(plMaxNodeBase *node)
{
if (!node)
return false;
for (int i = 0; i < fTargsPB->Count(kTargs); i++)
if (fTargsPB->GetINode(kTargs, 0, i) == node)
return true;
return false;
}
const char* plComponentBase::IGetUniqueName(plMaxNodeBase* target)
{
static char nameBuf[256];
// Make sure we've actually got multiple *used* targets. (Some could be nil)
int numUsedTargs = 0;
int thisTargIdx = -1;
for (int i = 0; i < fTargsPB->Count(kTargs); i++)
{
plMaxNodeBase* thisTarg = (plMaxNodeBase*)fTargsPB->GetINode(kTargs, 0, i);
if (thisTarg)
numUsedTargs++;
if (thisTarg == target)
thisTargIdx = i;
// If we've figured out whether or not we have multiple targets, and we
// found which target this one is, we can break out early
if (numUsedTargs > 1 && thisTargIdx != -1)
break;
}
hsAssert(thisTargIdx != -1, "Bad target for IGetUniqueName");
if (numUsedTargs > 1)
_snprintf(nameBuf, sizeof(nameBuf), "%s_%d", GetINode()->GetName(), thisTargIdx);
else
strncpy(nameBuf, GetINode()->GetName(), sizeof(nameBuf));
return nameBuf;
}
plMaxNodeBase *plComponentBase::GetINode()
{
// Go through the reflist looking for RefMakers with a ref to this component.
// There should only be one INode in this list.
RefList &refList = GetRefList();
RefListItem *item = refList.FirstItem();
while (item)
{
if (item->maker->SuperClassID() == BASENODE_CLASS_ID)
return (plMaxNodeBase*)item->maker;
item = item->next;
}
return nil;
}
hsBool plComponentBase::IsExternal()
{
return CanConvertToType(EXT_COMPONENT_CLASSID);
}
bool plComponentBase::IsCurMsgLocal()
{
// Was the last notification a message propagated up from a target?
// Ignore it if so.
if (fTargsPB->LastNotifyParamID() != -1)
return false;
// Was the last notification from a ref in the component PB? If so, assume
// it was a propagated message from the ref. Real changes to that ref
// parameter will be signalled with a user ref message.
if (fCompPB->LastNotifyParamID() != -1)
{
ParamID id = fCompPB->LastNotifyParamID();
if (is_ref(fCompPB->GetParamDef(id)))
return false;
}
return true;
}
bool plComponentBase::IsObsolete()
{
return ((plComponentClassDesc*)fClassDesc)->IsObsolete();
}
bool DoesPBReferenceNode(IParamBlock2* pb, INode* node);
static bool CheckRef(ReferenceTarget* targ, INode* node)
{
if (!targ)
return false;
if (targ->SuperClassID() == BASENODE_CLASS_ID)
{
if ((INode*)targ == node)
return true;
}
else if (targ->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID)
{
return DoesPBReferenceNode((IParamBlock2*)targ, node);
}
return false;
}
static bool DoesPBReferenceNode(IParamBlock2* pb, INode* node)
{
for (int i = 0; i < pb->NumParams(); i++)
{
ParamID id = pb->IndextoID(i);
ParamType2 type = pb->GetParameterType(id);
if (type == TYPE_INODE)
{
if (pb->GetINode(id) == node)
return true;
}
if (type == TYPE_INODE_TAB)
{
for (int iNode = 0; iNode < pb->Count(id); iNode++)
{
if (pb->GetINode(id, 0, iNode) == node)
return true;
}
}
if (type == TYPE_REFTARG)
{
ReferenceTarget* targ = pb->GetReferenceTarget(id);
bool ret = CheckRef(targ, node);
if (ret)
return true;
}
if (type == TYPE_REFTARG_TAB)
{
for (int iRef = 0; iRef < pb->Count(id); iRef++)
{
ReferenceTarget* targ = pb->GetReferenceTarget(id, 0, iRef);
bool ret = CheckRef(targ, node);
if (ret)
return true;
}
}
}
return false;
}
bool plComponentBase::DoReferenceNode(INode* node)
{
if (!fCompPB)
return false;
return DoesPBReferenceNode(fCompPB, node);
}
void plComponentBase::CreateRollups()
{
if (!fCompPB)
return;
ParamBlockDesc2 *pd = fCompPB->GetDesc();
plComponentClassDesc *cd = (plComponentClassDesc*)pd->cd;
// This is a plAutoUIComp, we need to treat it differently
if (cd->IsAutoUI())
{
plAutoUIClassDesc *cd = (plAutoUIClassDesc*)pd->cd;
cd->CreateAutoRollup(fCompPB);
}
// We're using a normal param block with auto UI
else if (pd->flags & P_AUTO_UI)
{
if (pd->flags & P_MULTIMAP)
{
int nMaps = pd->map_specs.Count();
for (int i = 0; i < nMaps; i++)
{
ParamBlockDesc2::map_spec spec = pd->map_specs[i];
// Create the rollout
IParamMap2 *map = CreateCPParamMap2(spec.map_id,
fCompPB,
GetCOREInterface(),
hInstance,
MAKEINTRESOURCE(spec.dlg_template),
GetString(spec.title),
spec.rollup_flags,
spec.dlgProc,
NULL,
ROLLUP_CAT_STANDARD);
// Save the rollout in the paramblock
fCompPB->SetMap(map, spec.map_id);
}
}
else
{
// Create the rollout
IParamMap2 *map = CreateCPParamMap2(0,
fCompPB,
GetCOREInterface(),
hInstance,
MAKEINTRESOURCE(pd->dlg_template),
GetString(pd->title),
pd->flags,
pd->dlgProc,
NULL,
ROLLUP_CAT_STANDARD);
// Save the rollout in the paramblock
fCompPB->SetMap(map);
}
}
fCompPB->ReleaseDesc();
}
void plComponentBase::DestroyRollups()
{
if (!fCompPB)
return;
ParamBlockDesc2 *pd = fCompPB->GetDesc();
plComponentClassDesc *cd = (plComponentClassDesc*)pd->cd;
// This is a plAutoUIComp, we need to treat it differently
if (cd->IsAutoUI())
{
plAutoUIClassDesc *autoCD = (plAutoUIClassDesc*)pd->cd;
autoCD->DestroyAutoRollup();
}
// We're using a normal param block with auto UI
else if (pd->flags & P_AUTO_UI)
{
if (pd->flags & P_MULTIMAP)
{
int nMaps = pd->map_specs.Count();
for (int i = 0; i < nMaps; i++)
{
MapID id = pd->map_specs[i].map_id;
// Destroy any parammap saved in the rollup
IParamMap2 *map = fCompPB->GetMap(id);
fCompPB->SetMap(nil, id);
if (map)
DestroyCPParamMap2(map);
}
}
else
{
// Destroy any parammap saved in the rollup
IParamMap2 *map = fCompPB->GetMap();
fCompPB->SetMap(nil);
if (map)
DestroyCPParamMap2(map);
}
}
fCompPB->ReleaseDesc();
}
////////////////////////////////////////////////////////////////////////////////
static bool INodeHasComponent(plMaxNodeBase *node, plMaxNodeBase *compNode)
{
UInt32 count = node->NumAttachedComponents();
for (UInt32 i = 0; i < count; i++)
{
if (node->GetAttachedComponent(i)->GetINode() == compNode)
return true;
}
return false;
}
int plSharedComponents(INodeTab& nodes, INodeTab& components)
{
components.ZeroCount();
if (nodes.Count() == 0)
return 0;
plMaxNodeBase *firstNode = (plMaxNodeBase*)nodes[0];
int num = firstNode->NumAttachedComponents();
// Resize the list to it's max size to be more efficient
components.SetCount(num);
int i;
// Put all the components on the first node into a list
for (i = 0; i < num; i++)
components[i] = firstNode->GetAttachedComponent(i)->GetINode();
// Delete any components that aren't on all the other nodes
for (i = 1; i < nodes.Count(); i++)
{
plMaxNodeBase *node = (plMaxNodeBase*)nodes[i];
UInt32 count = node->NumAttachedComponents();
for (int j = components.Count()-1; j >= 0; j--)
{
if (!INodeHasComponent(node, (plMaxNodeBase*)components[j]))
components.Delete(j, 1);
}
}
return components.Count();
}
///////////////////////////////////////////////////////////////////////////////////////////
#include "notify.h"
static void UpdateComponentVisibility(plMaxNodeBase *node)
{
plComponentBase *comp = node->ConvertToComponent();
if (comp)
{
node->Freeze(TRUE);
node->Hide(!comp->AllowUnhide());
}
for (int i = 0; i < node->NumberOfChildren(); i++)
{
plMaxNodeBase *childNode = (plMaxNodeBase*)node->GetChildNode(i);
UpdateComponentVisibility(childNode);
}
}
static void UnlinkComponents(plMaxNodeBase *node)
{
plComponentBase *comp = node->ConvertToComponent();
if (comp)
comp->DeleteAllTargets();
for (int i = 0; i < node->NumberOfChildren(); i++)
{
plMaxNodeBase *child = (plMaxNodeBase*)node->GetChildNode(i);
UnlinkComponents(child);
}
}
static void FindObsoleteComponents(plMaxNodeBase *node, std::vector& obsoleteComps)
{
plComponentBase *comp = node->ConvertToComponent();
if (comp && comp->IsObsolete())
obsoleteComps.push_back(comp);
for (int i = 0; i < node->NumberOfChildren(); i++)
{
plMaxNodeBase *childNode = (plMaxNodeBase*)node->GetChildNode(i);
FindObsoleteComponents(childNode, obsoleteComps);
}
}
static bool gUpdatingComponents = false;
#include
#include "hsUtils.h"
static void ComponentNotify(void *param, NotifyInfo *info)
{
if (info->intcode == NOTIFY_NODE_UNHIDE)
{
if (!gUpdatingComponents)
{
plMaxNodeBase *node = (plMaxNodeBase*)info->callParam;
plComponentBase *comp = node ? node->ConvertToComponent() : nil;
if (comp)
{
node->Hide(!comp->AllowUnhide());
node->Freeze(TRUE);
}
}
}
else if (info->intcode == NOTIFY_FILE_POST_OPEN)
{
plComponentShow::Update();
GetCOREInterface()->ForceCompleteRedraw();
// Also checking bitmap silent mode, since the export script uses that
if (!hsMessageBox_SuppressPrompts && !TheManager->SilentMode())
{
// Get all the obsolete components in the scene
std::vector obsoleteComps;
FindObsoleteComponents((plMaxNodeBase*)GetCOREInterface()->GetRootNode(), obsoleteComps);
// For now, just get the categories (could get the component names)
std::set names;
for (int i = 0; i < obsoleteComps.size(); i++)
{
const char *name = obsoleteComps[i]->GetObjectName();
names.insert(name);
}
if (obsoleteComps.size() > 0)
{
char buf[1024];
strcpy(buf, "Components of the following obsolete types\nwere found in this scene. Please delete them.\n\n");
std::set::iterator it = names.begin();
for (; it != names.end(); it++)
{
strcat(buf, (*it));
strcat(buf, "\n");
}
hsMessageBox(buf, "Obsolete Components", hsMBoxOk);
}
}
}
// WEIRD HACK - If lots of components are linked to objects when a scene is closed
// the closing goes really slow. I assume it is just repeatedly calling some
// unoptimized section of code (it would probably happen for any large number of
// references). So, at this point if the scene was changed the user would have already
// been prompted to save it, so we can modify the scene by deleting all the component
// references. -Colin
else if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN ||
info->intcode == NOTIFY_FILE_PRE_OPEN)
{
UnlinkComponents((plMaxNodeBase*)GetCOREInterface()->GetRootNode());
}
}
void plComponentShow::Init()
{
RegisterNotification(ComponentNotify, 0, NOTIFY_FILE_POST_OPEN);
RegisterNotification(ComponentNotify, 0, NOTIFY_NODE_UNHIDE);
RegisterNotification(ComponentNotify, 0, NOTIFY_SYSTEM_SHUTDOWN);
RegisterNotification(ComponentNotify, 0, NOTIFY_FILE_PRE_OPEN);
}
void plComponentShow::Update()
{
gUpdatingComponents = true;
UpdateComponentVisibility((plMaxNodeBase*)GetCOREInterface()->GetRootNode());
gUpdatingComponents = false;
}