deargui-vpl/ref/virtools/Samples/Behaviors/Narratives/CallBehavior.cpp

666 lines
19 KiB
C++

//TODO big update
/*
add setting bool freeze => stop remapping parameters, and check that target is compatible
add setting bool execute in real time => does not execute in 1 frame as now (return CKBR_ACTIVATENEXTFRAME)
put all these settings in a flag - register flag param hidden
put target in input parameter so that user can then change it on the fly
reset,wait for completion,stop at exit,no parameter restore,freeze io mapping,real time execute,
*/
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
//
// CallBehavior
//
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
#include "CKALL.h"
#include "CallBehaviorCallback.h"
#define CALLBEHAVIOR_NAME "Call Behavior"
#define CALLBEHAVIOR_GUID CKGUID(0x360a720d,0x7fa42f94)
CKObjectDeclaration *FillBehaviorCallBehaviorDecl();
CKERROR CreateCallBehaviorProto(CKBehaviorPrototype **);
int CallBehavior(const CKBehaviorContext& context);
CKERROR CallBehaviorCallback(const CKBehaviorContext& context);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// enums & defines
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
enum INS
{
eINS,
};
enum OUTS
{
eOUTS,
};
enum PINS
{
eRESET,
eWAITFORCOMPLETION,
eSTOP_AT_EXIT,
eINPUTS
};
enum POUTS
{
eOUTPUTS,
};
enum PLOCALS
{
eTARGET_BEHAVIOR, //setting
eMAX_RECURSION_DEPTH, //setting
eDO_NOT_RESTORE_PARAMETERS, //setting, optimisation
eRECURSION_DEPTH, //local
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// save parameter links
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
struct BehaviorsParameters
{
int sourcesCount;
CK_ID* sources;
int destinationsCount;
CK_ID** destinations;
BehaviorsParameters() {
memset(this,0,sizeof(BehaviorsParameters));
destinations = 0;
}
~BehaviorsParameters(){Clear();}
void Clear()
{
if (sources)
delete [] sources;
if (destinations)
{
for (int i=0;i<destinationsCount;i++)
{
delete [] destinations[i];
}
delete [] destinations;
}
memset(this,0,sizeof(this));
}
void SaveBehaviorParameters(CKBehavior* beh)
{
if (!beh)
return;
Clear();
//save pins
sourcesCount = beh->GetInputParameterCount();
sources = new CK_ID[sourcesCount*2];
for (int i=0;i<sourcesCount;++i)
{
CKParameterIn* pin = beh->GetInputParameter(i);
if (pin)
{
if (CKParameterIn* shared = pin->GetSharedSource())
{
sources[i*2] = 1;
sources[i*2+1] = shared->GetID();
}
else if (CKParameter* p = pin->GetDirectSource())
{
sources[i*2] = 0;
sources[i*2+1] = p->GetID();
}
else
{
sources[i*2] = 0;
sources[i*2+1] = 0;
}
}
}
//save pouts
destinationsCount = beh->GetOutputParameterCount();
destinations = new CK_ID*[destinationsCount];
for (int i=0;i<destinationsCount;++i)
{
CKParameterOut* pout = beh->GetOutputParameter(i);
if (pout)
{
if (int destCount = pout->GetDestinationCount())
{
destinations[i] = new CK_ID[destCount+1];
destinations[i][0] = destCount;
for (int j=0;j<destCount;++j)
{
CKParameter* dest = pout->GetDestination(j);
destinations[i][j+1] = dest?dest->GetID():0;
}
}
else
destinations[i] = 0;
}
}
}
void LoadBehaviorParameters(CKBehavior* beh)
{
if (!beh)
return;
//restore pins
for (int i=0;i<sourcesCount;++i)
{
CKParameterIn* pin = beh->GetInputParameter(i);
if (pin)
{
CK_ID id = sources[2*i+1];
if (id==0)
{
pin->ShareSourceWith(0);
pin->SetDirectSource(0);
}
else
{
CKObject* obj = beh->GetCKContext()->GetObject(id);
if (sources[2*i]==0)
{
pin->ShareSourceWith(0);
pin->SetDirectSource((CKParameter*)obj);
}
else
{
pin->SetDirectSource(0);
pin->ShareSourceWith((CKParameterIn*)obj);
}
}
}
}
//restore pouts
for (int i=0;i<destinationsCount;++i)
{
CKParameterOut* pout = beh->GetOutputParameter(i);
if (pout)
{
pout->RemoveAllDestinations();
if (destinations[i])
{
int count = destinations[i][0];
for (int j=0;j<count;++j)
{
CKParameter* p = (CKParameter*)beh->GetCKContext()->GetObject(destinations[i][j+1]);
if (CKIsChildClassOf(p,CKCID_PARAMETER))
pout->AddDestination(p);
}
}
}
}
}
static void SetBehaviorParameters(CKBehavior* callBB,CKBehavior* calledBeh)
{
if (!callBB || !calledBeh)
return;
int inCountTarget = calledBeh->GetInputParameterCount();
int outCountTarget = calledBeh->GetOutputParameterCount();
//set sources & dest
for (int i=0;i<inCountTarget;++i)
{
CKParameterIn* pin1 = calledBeh->GetInputParameter(i);
CKParameterIn* pin2 = callBB->GetInputParameter(i+eINPUTS);
if (pin1 && pin2)
pin1->SetDirectSource(pin2->GetDirectSource());
}
for (int i=0;i<outCountTarget;++i)
{
CKParameterOut* pout1 = calledBeh->GetOutputParameter(i);
CKParameterOut* pout2 = callBB->GetOutputParameter(i+eOUTPUTS);
if (pout1 && pout2)
{
pout1->RemoveAllDestinations();
pout1->AddDestination(pout2);
}
}
}
static void NameBehaviorParameters(CKBehavior* callBB,CKBehavior* calledBeh)
{
if (!calledBeh)
return;
int count = calledBeh->GetInputParameterCount();
for (int i=0;i<count;++i)
{
CKParameterIn* p1 = calledBeh->GetInputParameter(i);
CKParameterIn* p2 = callBB->GetInputParameter(i+eINPUTS);
if (p1 && p2)
{
p2->SetName(p1->GetName());
if (p1->GetType()!=p2->GetType())
{
p2->SetType(p1->GetType());
//CKParameter* psource = p2->GetRealSource();
//if (psource)
// psource->SetType(p1->GetType());
}
}
}
count = calledBeh->GetOutputParameterCount();
for (int i=0;i<count;++i)
{
CKParameter* p1 = calledBeh->GetOutputParameter(i);
CKParameter* p2 = callBB->GetOutputParameter(i+eOUTPUTS);
if (p1 && p2)
{
p2->SetName(p1->GetName());
if (p1->GetType()!=p2->GetType())
{
p2->SetType(p1->GetType());
}
}
}
count = calledBeh->GetInputCount();
for (int i=0;i<count;++i)
{
CKBehaviorIO* p1 = calledBeh->GetInput(i);
CKBehaviorIO* p2 = callBB->GetInput(i+eINS);
if (p1 && p2)
p2->SetName(p1->GetName());
}
count = calledBeh->GetOutputCount();
for (int i=0;i<count;++i)
{
CKBehaviorIO* p1 = calledBeh->GetOutput(i);
CKBehaviorIO* p2 = callBB->GetOutput(i+eOUTS);
if (p1 && p2)
p2->SetName(p1->GetName());
}
}
static void ResetBehaviorParameters(CKBehavior* behavior)
{
if (!behavior)
return;
int inCountTarget = behavior->GetInputParameterCount();
int outCountTarget = behavior->GetOutputParameterCount();
//set sources & dest
for (int i=0;i<inCountTarget;++i)
{
CKParameterIn* pin1 = behavior->GetInputParameter(i);
if (pin1)
{
pin1->SetDirectSource(0);
pin1->ShareSourceWith(0);
}
}
for (int i=0;i<outCountTarget;++i)
{
CKParameterOut* pout1 = behavior->GetOutputParameter(i);
if (pout1)
pout1->RemoveAllDestinations();
}
}
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// the behavior
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
CKObjectDeclaration *FillBehaviorCallBehaviorDecl()
{
CKObjectDeclaration *od = CreateCKObjectDeclaration(CALLBEHAVIOR_NAME);
od->SetDescription("Calls immediatly a behavior.");
/* rem:
<SPAN CLASS=in>In: </SPAN>triggers the process.<BR>
<SPAN CLASS=out>Out: </SPAN>is activated when the activated script's process is completed.<BR>
<BR>
<SPAN CLASS=pin>Reset?: </SPAN>if TRUE, the script will be reset; if FALSE the script will be resumed from its previous state.<BR>
<SPAN CLASS=pin>Script: </SPAN>script to be performed.<BR>
<SPAN CLASS=pin>Wait For Completion?: </SPAN>if TRUE, the script will be executed until completion; if FALSE the script will be executed for one frame.<BR>
<BR>
The script called is executed immediatly, stopping the execution of the current script.
*/
/* warning:
The script must not contain a infinite loop if you set Wait For Completion.<BR>
*/
od->SetType(CKDLL_BEHAVIORPROTOTYPE);
od->SetGuid(CALLBEHAVIOR_GUID);
od->SetAuthorGuid(VIRTOOLS_GUID);
od->SetAuthorName("Virtools");
od->SetVersion(0x00010000);
od->SetCreationFunction(CreateCallBehaviorProto);
od->SetCompatibleClassId(CKCID_BEOBJECT);
od->SetCategory("Narratives/Script Management");
return od;
}
CKERROR CreateCallBehaviorProto(CKBehaviorPrototype **pproto)
{
CKBehaviorPrototype *proto = CreateCKBehaviorPrototype(CALLBEHAVIOR_NAME);
if(!proto) return CKERR_OUTOFMEMORY;
proto->DeclareInParameter("Reset", CKPGUID_BOOL, "FALSE");
proto->DeclareInParameter("Wait For Completion", CKPGUID_BOOL, "FALSE");
proto->DeclareInParameter("Stop At Exit", CKPGUID_BOOL, "FALSE");
proto->DeclareSetting("Behavior",CKPGUID_BEHAVIOR);
proto->DeclareSetting("Max Recursion",CKPGUID_INT,"100");//0 means no protection
proto->DeclareSetting("Do not restore parameters",CKPGUID_BOOL);//eDO_NOT_RESTORE_PARAMETERS
proto->DeclareLocalParameter("",CKPGUID_INT);//eRECURSION_DEPTH
proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL);
proto->SetFunction(CallBehavior);
proto->SetBehaviorCallbackFct(CallBehaviorCallback);
proto->SetBehaviorFlags((CK_BEHAVIOR_FLAGS)(
CKBEHAVIOR_INTERNALLYCREATEDINPUTS|
CKBEHAVIOR_INTERNALLYCREATEDOUTPUTS|
CKBEHAVIOR_INTERNALLYCREATEDINPUTPARAMS|
CKBEHAVIOR_INTERNALLYCREATEDOUTPUTPARAMS
));
*pproto = proto;
return CK_OK;
}
int CallBehavior(const CKBehaviorContext& behcontext)
{
CKBehavior* beh = behcontext.Behavior;
//Get target behavior
CKBehavior* target = (CKBehavior*) beh->GetLocalParameterObject(eTARGET_BEHAVIOR);
if (!target)
return CKBR_OK;
//reset
BOOL reset=FALSE;
beh->GetInputParameterValue(eRESET, &reset);
if (reset)
target->Activate(FALSE,TRUE);
//Activate Inputs
//target->ActivateInput(0);
int ioCount = target->GetInputCount();
for (int i=eINS;i<eINS+ioCount;++i)
{
if (beh->IsInputActive(i))
target->ActivateInput(i-eINS);
}
// Get waitcomp
BOOL wc=TRUE;
beh->GetInputParameterValue(eWAITFORCOMPLETION, &wc);
//stop at exit
BOOL stopAtExit = FALSE;
beh->GetInputParameterValue(eSTOP_AT_EXIT, &stopAtExit);
//max depth
int maxDepth = 100;
beh->GetLocalParameterValue(eMAX_RECURSION_DEPTH,&maxDepth);
//current depth
int currentDepth = 0;
beh->GetLocalParameterValue(eRECURSION_DEPTH,&currentDepth);
currentDepth++;
if (maxDepth>=0 && currentDepth>maxDepth)
{
behcontext.Context->OutputToConsole("CallBehavior, recursion>max recursion depth, BB break");
return CKBR_OK;
}
beh->SetLocalParameterValue(eRECURSION_DEPTH,&currentDepth);
//optim
int noRestoreParam = FALSE;
beh->GetLocalParameterValue(eDO_NOT_RESTORE_PARAMETERS,&noRestoreParam);
if (wc) {
int loop = 0;
int maxloop = behcontext.Context->GetBehaviorManager()->GetBehaviorMaxIteration();
for(;;++loop)
{
if (loop > maxloop) {
behcontext.Context->OutputToConsoleExBeep("Execute Behavior : Behavior %s Executed too much times",target?target->GetName():"NA");
target->Activate(FALSE,FALSE);
break;
}
BehaviorsParameters behParams;
if (noRestoreParam==FALSE)
behParams.SaveBehaviorParameters(target);
BehaviorsParameters::SetBehaviorParameters(beh,target);
CKERROR result = target->Execute(behcontext.DeltaTime);
if (noRestoreParam==FALSE)
behParams.LoadBehaviorParameters(target);
// The target loop on itself too much times
if (result == CKBR_INFINITELOOP) {
target->Activate(FALSE,FALSE);
break;
}
if(!target->IsActive())
break;
}
} else { // we execute only one frame of the target
BehaviorsParameters behParams;
if (noRestoreParam==FALSE)
behParams.SaveBehaviorParameters(target);
BehaviorsParameters::SetBehaviorParameters(beh,target);
target->Execute(behcontext.DeltaTime);
if (noRestoreParam==FALSE)
behParams.LoadBehaviorParameters(target);
target->Activate(FALSE,FALSE);
}
//recursive depth update
currentDepth--;
beh->SetLocalParameterValue(eRECURSION_DEPTH,&currentDepth);
// IO Activation
//beh->ActivateInput(0,FALSE);
//beh->ActivateOutput(0);
for (int i=eINS;i<eINS+ioCount;++i)
beh->ActivateInput(i,FALSE);
ioCount = target->GetOutputCount();
for (int i=eOUTS;i<eOUTS+ioCount;++i)
beh->ActivateOutput(i,target->IsOutputActive(i-eOUTS));
if (stopAtExit)
{
for (int i=0;i<ioCount;++i)
target->ActivateOutput(i,FALSE);
}
return CKBR_OK;
}
void RemoveIOxParams(CKBehavior* beh)
{
CKContext* ctx = beh->GetCKContext();
int count = beh->GetInputParameterCount()-eINPUTS;
for (int i=0;i<count;++i)
ctx->DestroyObject(beh->RemoveInputParameter(eINPUTS),CK_DESTROY_NONOTIFY,0);
count = beh->GetOutputParameterCount()-eOUTPUTS;
for (int i=0;i<count;++i)
ctx->DestroyObject(beh->RemoveOutputParameter(eOUTPUTS),CK_DESTROY_NONOTIFY,0);
count = beh->GetInputCount()-eINS;
for (int i=0;i<count;++i)
beh->DeleteInput(eINS);
count = beh->GetOutputCount()-eOUTS;
for (int i=0;i<count;++i)
beh->DeleteOutput(eOUTS);
}
CKERROR CallBehaviorCallback(const CKBehaviorContext& behcontext)
{
CKBehavior* beh = behcontext.Behavior;
switch( behcontext.CallbackMessage )
{
//create same ios as in target behavior
case CKM_BEHAVIOREDITED:
{
CKBehavior* target = (CKBehavior*) beh->GetLocalParameterObject(eTARGET_BEHAVIOR);
//update behavior ios & types just in case
BehaviorsParameters::NameBehaviorParameters(beh,target);
}
break;
case CKM_BEHAVIORSETTINGSEDITED:
{
CKBehavior* target = (CKBehavior*) beh->GetLocalParameterObject(eTARGET_BEHAVIOR);
BOOL bb = FALSE;
if (target)
bb = (target->GetFlags() & CKBEHAVIOR_BUILDINGBLOCK)?TRUE:FALSE;
if (target==beh)// || bb) allow bb finally
{
//if (bb)
// behcontext.Context->OutputToConsole("CallBehavior BB cannot target another BB");
//else
behcontext.Context->OutputToConsole("CallBehavior BB cannot target itself");
beh->SetLocalParameterObject(eTARGET_BEHAVIOR,0);
beh->SetName(CALLBEHAVIOR_NAME);
RemoveIOxParams(beh);
behcontext.Context->SendInterfaceMessage(CKUIM_CALLBEHAVIORBB_CHANGE,(CKDWORD)beh,eCallBehavior_Edit,0);//warn interface
return CKBR_PARAMETERERROR;
}
////////////////////////////////////////////////////////////////////////////////
//Fit IOs & Params
if (CKIsChildClassOf(target,CKCID_BEHAVIOR) && target->GetOwner())
{
////////////////////////////////////////////////////////////////////////////////
//Params
int inCountTarget = target->GetInputParameterCount();
int outCountTarget = target->GetOutputParameterCount();
int inCountCaller = beh->GetInputParameterCount();
int outCountCaller = beh->GetOutputParameterCount();
////////////////////////////////////////////////////////////////////////////////
//remove params if too much
for (int i=inCountCaller-1;i>=eINPUTS+inCountTarget;i--)
behcontext.Context->DestroyObject(beh->RemoveInputParameter(i),CK_DESTROY_NONOTIFY,0);
for (int i=outCountCaller-1;i>=eOUTPUTS+outCountTarget;i--)
behcontext.Context->DestroyObject(beh->RemoveOutputParameter(i),CK_DESTROY_NONOTIFY,0);
////////////////////////////////////////////////////////////////////////////////
//create params if not enough
inCountCaller = beh->GetInputParameterCount();
outCountCaller = beh->GetOutputParameterCount();
for (int i=inCountCaller-eINPUTS;i<inCountTarget;i++)
{
CKParameterIn* pin = target->GetInputParameter(i);
beh->CreateInputParameter(pin->GetName(),pin->GetGUID());
}
for (int i=outCountCaller-eOUTPUTS;i<outCountTarget;i++)
{
CKParameterOut* pout = target->GetOutputParameter(i);
beh->CreateOutputParameter(pout->GetName(),pout->GetGUID());
}
////////////////////////////////////////////////////////////////////////////////
//IOs
inCountTarget = target->GetInputCount();
outCountTarget = target->GetOutputCount();
inCountCaller = beh->GetInputCount();
outCountCaller = beh->GetOutputCount();
////////////////////////////////////////////////////////////////////////////////
//remove IOS if too much
for (int i=inCountCaller-1;i>=eINS+inCountTarget;i--)
beh->DeleteInput(i);
for (int i=outCountCaller-1;i>=eOUTS+outCountTarget;i--)
beh->DeleteOutput(i);
////////////////////////////////////////////////////////////////////////////////
//create IOS if not enough
inCountCaller = beh->GetInputCount();
outCountCaller = beh->GetOutputCount();
for (int i=inCountCaller-eINS;i<inCountTarget;i++)
{
CKBehaviorIO* io = target->GetInput(i);
beh->CreateInput(io->GetName());
}
for (int i=outCountCaller-eOUTS;i<outCountTarget;i++)
{
CKBehaviorIO* io = target->GetOutput(i);
beh->CreateOutput(io->GetName());
}
//rename
BehaviorsParameters::NameBehaviorParameters(beh,target);
if (target && target->GetName())
{
XString finalName;
finalName.Format("%s:%s",CALLBEHAVIOR_NAME,target->GetName());
beh->SetName(finalName.Str());
}
}
////////////////////////////////////////////////////////////////////////////////
else //remove all supplementary pins & pouts from BB
{
RemoveIOxParams(beh);
beh->SetName(CALLBEHAVIOR_NAME);
}
{
CKBehavior* target = (CKBehavior*) beh->GetLocalParameterObject(eTARGET_BEHAVIOR);
behcontext.Context->SendInterfaceMessage(CKUIM_CALLBEHAVIORBB_CHANGE,(CKDWORD)beh,eCallBehavior_Edit,(CKDWORD)target);//warn interface
}
break;
}
break;
case CKM_BEHAVIORDELETE:
case CKM_BEHAVIORDETACH:
{
CKBehavior* target = (CKBehavior*) beh->GetLocalParameterObject(eTARGET_BEHAVIOR);
behcontext.Context->SendInterfaceMessage(CKUIM_CALLBEHAVIORBB_CHANGE,(CKDWORD)beh,eCallBehavior_Detach,(CKDWORD)target);//warn interface
}
break;
case CKM_BEHAVIORLOAD:
case CKM_BEHAVIORATTACH:
{
CKBehavior* target = (CKBehavior*) beh->GetLocalParameterObject(eTARGET_BEHAVIOR);
behcontext.Context->SendInterfaceMessage(CKUIM_CALLBEHAVIORBB_CHANGE,(CKDWORD)beh,eCallBehavior_Attach,(CKDWORD)target);//warn interface
if (behcontext.Context->IsInInterfaceMode())
{
if (target && target->GetName())
{
XString finalName;
finalName.Format("%s:%s",CALLBEHAVIOR_NAME,target->GetName());
beh->SetName(finalName.Str());
}
}
}
break;
}
return CK_OK;
}