/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
//
// CharacterGoToNode
//
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
#include "CKAll.h"
#include "N3dGraph.h"
#define CKPGUID_FRONTAL_DIRECTION CKDEFINEGUID(0x286652d,0x5ea709c2)
CKERROR CreateCharacterGoToNodeBehaviorProto(CKBehaviorPrototype **);
int DoCharacterGoToNode(const CKBehaviorContext& behcontext);
CKObjectDeclaration *FillBehaviorGoToNodeDecl()
{
CKObjectDeclaration *od = CreateCKObjectDeclaration("Character Go To Node");
od->SetDescription("Makes the target character walk to the selected node, by taking the shortest path possible.");
/* rem:
In: triggers the process.
Loop In: triggers the next step in the process loop.
Stop: deactivates the building block and free the links the character was currently occupying.
Arrived: is activated when the distance between the character and the "Target Object" is lower than the "Distance" input parameter.
Blocked: is activated if the character is unable to get to the selected node.
Loop Out: is activated when the process needs to loop.
Nodal Path: nodal path to go through.
Target Object: destination to reach.
Guard Distance: distance just before destination node where character should stop.
Character Direction: defines the character's frontal direction.
Limit Angle: limits the angle between character's direction and destination node. Value should not be less than 20 degrees.
Mark Occupation: if TRUE, node or link character currently occupied is marked as
occupied, and no other character can use it.
Turn With Messages: character will respond to messages from a character controller (e.g. Joystick) if this checkbox is crossed.
Rotation Speed: speed at which character turns, if this is not handled by messages. Range is between 1 = slow, and 100 = instantaneous.
*/
od->SetCategory("3D Transformations/Nodal Path");
od->SetType(CKDLL_BEHAVIORPROTOTYPE);
od->SetGuid(CKGUID(0x4c710f65,0x32ed4ea6));
od->SetAuthorGuid(CKGUID());
od->SetAuthorName("Virtools");
od->SetVersion(0x00010000);
od->SetCreationFunction(CreateCharacterGoToNodeBehaviorProto);
od->SetCompatibleClassId(CKCID_CHARACTER);
return od;
}
CKERROR DaCharacterGoToNodeCB(const CKBehaviorContext& behcontext)
{
CKBehavior *beh = behcontext.Behavior;
CKContext* ctx = beh->GetCKContext();
switch(behcontext.CallbackMessage)
{
case CKM_BEHAVIORCREATE:
case CKM_BEHAVIORLOAD:
case CKM_BEHAVIORREADSTATE:
{
CKMessageManager* mm = ctx->GetMessageManager();
// TODO : mettre les messages en settings....
int tab[4];
tab[0] = mm->AddMessageType("Joy_Up");
tab[1] = mm->AddMessageType("Joy_Down");
tab[2] = mm->AddMessageType("Joy_Left");
tab[3] = mm->AddMessageType("Joy_Right");
beh->SetLocalParameterValue(0, (LPVOID) tab, 4 * sizeof(int));
return CKBR_OK;
}
break;
case CKM_BEHAVIORDELETE:
{
CKDWORD d = 0;
beh->SetLocalParameterValue(0,&d,sizeof(CKDWORD));
return CKBR_OK;
}
break;
}
return CKBR_OK;
}
CKERROR CreateCharacterGoToNodeBehaviorProto(CKBehaviorPrototype **pproto)
{
CKBehaviorPrototype *proto = CreateCKBehaviorPrototype("Character Go To Node");
if(!proto) return CKERR_OUTOFMEMORY;
proto->DeclareInput("In");
proto->DeclareInput("Loop In");
proto->DeclareInput("Stop");
proto->DeclareOutput("Arrived");
proto->DeclareOutput("Can't Go");
proto->DeclareOutput("Loop Out");
proto->DeclareInParameter("Nodal Path",CKPGUID_GROUP);
proto->DeclareInParameter("Goal Node",CKPGUID_3DENTITY);
proto->DeclareInParameter("Guard Distance",CKPGUID_FLOAT,"1");
proto->DeclareInParameter("Character Direction",CKPGUID_FRONTAL_DIRECTION,"X");
proto->DeclareInParameter("Limit Angle",CKPGUID_ANGLE,"0:70");
proto->DeclareInParameter("Mark Occupation",CKPGUID_BOOL,"FALSE");
// Message array
proto->DeclareLocalParameter(NULL,CKPGUID_VOIDBUF);
// node to go
proto->DeclareLocalParameter(NULL,CKPGUID_3DENTITY);
// last node
proto->DeclareLocalParameter(NULL,CKPGUID_3DENTITY);
proto->DeclareSetting("Turn With Messages",CKPGUID_BOOL,"TRUE");
proto->DeclareSetting("Rotation Speed",CKPGUID_PERCENTAGE,"100");
proto->SetBehaviorFlags(CKBEHAVIOR_TARGETABLE);
proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL);
proto->SetFunction(DoCharacterGoToNode);
proto->SetBehaviorCallbackFct(DaCharacterGoToNodeCB);
*pproto = proto;
return CK_OK;
}
int DoCharacterGoToNode(const CKBehaviorContext& behcontext)
{
CKBehavior* beh = behcontext.Behavior;
CKCharacter* caracter = (CKCharacter*)beh->GetTarget();
if(!caracter) {
beh->ActivateOutput(1);
return CKBR_OWNERERROR;
}
CKContext* ctx = behcontext.Context;
CKAttributeManager* attman = ctx->GetAttributeManager();
CKGroup* group = (CKGroup*)beh->GetInputParameterObject(0);
if(!group) VXTHROW "No group given";
CKParameterOut* param = group->GetAttributeParameter(attman->GetAttributeTypeByName(Network3dName));
if(!param) VXTHROW "Given Group isn't a Network";
N3DGraph* graph;
param->GetValue(&graph);
if(!graph) VXTHROW "There is no Graph attached";
CKBOOL occupation = FALSE;
beh->GetInputParameterValue(5,&occupation);
// we get the character position
VxVector carpos;
CK3dEntity *carac=caracter->GetFloorReferenceObject();
if (!carac) { // the character doesn't possess a reference object : we use the root
carac = caracter->GetRootBodyPart();
if(!carac) return CKBR_OWNERERROR;
}
carac->GetPosition(&carpos);
carpos.y = 0;
// we get the goal node
CK3dEntity* goalnode = (CK3dEntity*)beh->GetInputParameterObject(1);
if(beh->IsInputActive(2)) { // We entered by stop
beh->ActivateInput(1,FALSE);
beh->ActivateInput(2,FALSE);
CK3dEntity* nodetogo = (CK3dEntity*)beh->GetLocalParameterObject(1);
CK3dEntity* oldnode = (CK3dEntity*)beh->GetLocalParameterObject(2);
// we reactivates links currently occupied
if(oldnode && nodetogo) {
graph->ActivateEdge(oldnode,nodetogo,TRUE);
graph->ActivateEdge(nodetogo,oldnode,TRUE);
}
return CKBR_OK;
}
if(beh->IsInputActive(0)) {
CK3dEntity* oldnode = (CK3dEntity*)beh->GetLocalParameterObject(2);
if(oldnode && occupation) { // we have to check if we are far enough away from the old node
graph->SetOccupier(oldnode,NULL);
}
beh->ActivateInput(0,FALSE);
// we find the first node where we must go
// for now the node the nearest
// TODO : find a best method (cone of vision...)
VxVector pos;
CK3dEntity* node;
CK3dEntity* nearestnode;
float dist,distmin=100000000000000000.0f;
for(int i=0;iGetStatesNumber();i++) {
node = (CK3dEntity*)graph->GetSafeEntity(graph->GetState(i));
node->GetPosition(&pos);
pos.y = 0;
// TODO :optimize this line may be
dist = SquareMagnitude(carpos-pos);
if(dist < distmin) {
// we test if it is a valid node
if((goalnode == node) || graph->FindPath(node,goalnode,NULL,TRUE)) {
distmin = dist;
nearestnode = node;
}
}
}
if(!nearestnode) {
beh->ActivateOutput(1);
return CKBR_OK;
}
beh->SetLocalParameterObject(1,nearestnode);
beh->SetLocalParameterObject(2,NULL);
beh->ActivateOutput(2);
return CKBR_OK;
}
// so we're in by the loop in input
beh->ActivateInput(1,FALSE);
float prox = 1.0f;
beh->GetInputParameterValue(2,&prox);
CK3dEntity* oldnode = (CK3dEntity*)beh->GetLocalParameterObject(2);
if(oldnode) { // we have to check if we are far enough away from the old node
VxVector nodepos;
oldnode->GetPosition(&nodepos);
VxVector dist = nodepos - carpos;
dist.y = 0.0f;
float distance = Magnitude(dist);
if(occupation && distance > prox) { // we can deoccupy it
graph->SetOccupier(oldnode,NULL);
}
}
CK3dEntity* nodetogo = (CK3dEntity*)beh->GetLocalParameterObject(1);
if(!nodetogo) return CKBR_PARAMETERERROR;
// if the nodetogo is currently occupied
if(oldnode && occupation) {
CK3dEntity* occ;
if(occ = graph->GetOccupier(nodetogo)) {
if(occ != caracter) { // it's not the character itself...
if(nodetogo != goalnode) {
CK3dEntity *temp=nodetogo;// it's not the final node so we can go another way
nodetogo = oldnode; // graph->FindPath(oldnode,goalnode,NULL,TRUE,nodetogo);
oldnode = temp;
}
}
}
}
VxVector nodepos;
nodetogo->GetPosition(&nodepos);
VxVector dist = nodepos - carpos;
dist.y = 0.0f;
float distance = Magnitude(dist);
if(distance < prox) {
// we leave a link : we must reactivate it
if(oldnode && occupation) {
graph->ActivateEdge(oldnode,nodetogo,TRUE);
graph->ActivateEdge(nodetogo,oldnode,TRUE);
}
if(nodetogo == goalnode) { // we are arrived
beh->ActivateOutput(0);
return CKBR_OK;
}
// we reached a node : we try to occupy it
if(occupation && !graph->SetOccupier(nodetogo,caracter)) {
// the node is already occupied...
// we do nothing but wait
beh->ActivateOutput(2);
return CKBR_OK;
}
// the old node to go is now the old node
oldnode = nodetogo;
beh->SetLocalParameterObject(2,oldnode);
// we must find the next node to go
nodetogo = graph->FindPath(oldnode,goalnode,NULL);
if(!nodetogo) { // no possibility to go to the end node
nodetogo = graph->FindPath(oldnode,goalnode,NULL,TRUE);
if(!nodetogo) { // there is really no possibility to go there
beh->ActivateOutput(1);
return CKBR_OK;
} else { // I must go to nodetogo, but can I ?
if(!graph->IsEdgeActive(oldnode,nodetogo)) nodetogo=oldnode;
}
}
// if the nodetogo is currently occupied
if(occupation) {
CK3dEntity* occ;
if(occ = graph->GetOccupier(nodetogo)) {
if(occ != caracter) { // it's not the character itself...
if(nodetogo != goalnode) { // it's not the final node so we can go another way
nodetogo = graph->FindPath(oldnode,goalnode,NULL,TRUE,TRUE);
if(!nodetogo) nodetogo = oldnode;
if(!graph->IsEdgeActive(oldnode,nodetogo)) nodetogo=oldnode;
}
}
}
}
// we disable the link that we're going to walk on
if (occupation) {
graph->ActivateEdge(oldnode,nodetogo,FALSE);
graph->ActivateEdge(nodetogo,oldnode,FALSE);
}
nodetogo->GetPosition(&nodepos);
dist = nodepos - carpos;
dist.y = 0.0f;
distance = Magnitude(dist);
beh->SetLocalParameterObject(1,nodetogo);
}
// we now walk to the node to go
int *tab=(int *)beh->GetLocalParameterReadDataPtr(0);
VxVector dirx,dir;
float angle,angle1;
int CharacterDirection=1;
beh->GetInputParameterValue(3,&CharacterDirection);
switch(CharacterDirection){
case 1:
caracter->GetOrientation(&dirx,NULL,&dir);
dirx = -dirx;
break;
case 2:
{
caracter->GetOrientation(&dirx,NULL,&dir);
dir = -dir;
}
break;
case 3:
caracter->GetOrientation(NULL,&dir,&dirx);
break;
case 4:
{
caracter->GetOrientation(NULL,&dir,&dirx);
dir = -dir;
dirx = -dirx;
}
break;
case 5:
caracter->GetOrientation(&dir,NULL,&dirx);
break;
case 6:
{
caracter->GetOrientation(&dir,NULL,&dirx);
dir = -dir;
dirx = -dirx;
}
break;
}
VxVector up;
up = CrossProduct(dir,dirx);
dist/=distance;
angle1=DotProduct(dir,dist);
angle=DotProduct(dirx,dist);
float anglelimit = 1.22f;
beh->GetInputParameterValue(4,&anglelimit);
float anglelimitcos = cosf(anglelimit);
CKMessageManager* mm = behcontext.Context->GetMessageManager();
if (distance>prox) {
if (angle1>anglelimitcos) {
mm->SendMessageSingle(tab[0],carac,carac);
}
}
// if (angle<0.02f) CKSendMessageSingle(tab[3],carac,carac);
// else if (angle>0.02f) CKSendMessageSingle(tab[2],carac,carac);
BOOL twm = TRUE;
beh->GetLocalParameterValue(3,&twm);
float speed=1.0f;
beh->GetLocalParameterValue(4,&speed);
if(twm) {
if (angle1<0.999f) {
if (angle<0.0f) {
mm->SendMessageSingle(tab[2],carac,carac);
} else {
mm->SendMessageSingle(tab[3],carac,carac);
}
}
} else {
if (angle1<0.999f) {
caracter->Rotate(&up,speed*-angle);
/* if (angle<0.0f) {
carac->Rotate(&up,angle);
} else {
}
*/
}
}
beh->ActivateOutput(2);
return CKBR_OK;
}