convert line ends
[canaan.git] / prj / cam / src / ai / aimovmot.cpp
blob2f9cb9873f46d901c47f3cac2bd8160c2c6491a2
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 ///////////////////////////////////////////////////////////////////////////////
7 // $Header: r:/t2repos/thief2/src/ai/aimovmot.cpp,v 1.60 2000/02/11 18:27:52 bfarquha Exp $
8 //
9 // Motion components of cAIMoveEnactor
12 // #define PROFILE_ON 1
14 #include <lg.h>
15 #include <appagg.h>
17 #include <cfgdbg.h>
18 #include <creatapi.h>
19 #include <creatext.h>
20 #include <mclntapi.h>
21 #include <motdesc.h>
22 #include <motorapi.h>
23 #include <motprop.h>
24 #include <mtagvals.h>
25 #include <phoprop.h>
26 #include <physapi.h>
27 #ifdef INCORRECT_AI_NETWORKING
28 //include <netman.h>
29 //include <iobjnet.h>
30 #endif
31 #include <rendobj.h>
32 #include <ctagnet.h>
34 #include <aiapinet.h>
35 #include <aiactmot.h>
36 #include <aiactmov.h>
37 #include <aimove.h>
38 #include <aimovsug.h>
39 #include <aiprcore.h>
40 #include <aitagtyp.h>
42 #include <creatext.h>
43 #include <creature.h>
44 #include <creatur_.h>
46 // Must be last header
47 #include <dbmem.h>
49 ///////////////////////////////////////////////////////////////////////////////
51 // CLASS: cAIMoveEnactor
54 void cAIMoveEnactor::MotCleanup()
56 if (m_pMotionCoord)
58 delete m_pMotionCoord;
59 m_pMotionCoord = NULL;
63 ///////////////////////////////////////
65 STDMETHODIMP_(void) cAIMoveEnactor::OnSimStart()
67 #ifdef INCORRECT_AI_NETWORKING
68 if (!AccessOuterAI()->IsNetworkProxy())
69 g_pPhysAICollideProp->Set(GetID(), FALSE);
70 #endif
73 ///////////////////////////////////////
75 void cAIMoveEnactor::MotOnModeChange(eAIMode previous, eAIMode mode)
77 IMotor * pMotor = CreatureGetMotorInterface(GetID());
79 // If we're entering combat, turn on collidability on,
80 // otherwise if it was on, turn it off
81 // (For the sake of network message bandwidth, we don't always turn it off).
82 if (mode == kAIM_Combat)
83 g_pPhysAICollideProp->Set(GetID(), TRUE);
84 else if (previous == kAIM_Combat)
85 g_pPhysAICollideProp->Set(GetID(), FALSE);
87 if (previous == kAIM_Efficient)
89 cCreature* pCreature = CreatureFromObj(GetID());
90 Assert_(pCreature!=NULL);
91 CreatureMakeNonBallistic(GetID());
92 // @HACK: yep, this is a total hack. We don't want our ballistic creatures falling
93 // to the floor when we go out of efficiency mode
94 if ((!pCreature->GetCreatureDesc()->alwaysBallistic) && pCreature->IsPhysical())
96 // @TBD (toml 08-27-98): get gravity from room
97 PhysSetGravity(GetID(), 1.0);
98 PhysSetBaseFriction(GetID(), 0.0);
100 //mprint("Non ballistic!\n");
103 if (pMotor)
105 #ifndef NO_NON_PHYSICAL_SLEEP
106 if (mode <= kAIM_SuperEfficient)
108 pMotor->MakeNonPhysical();
109 //mprint("Non phys!\n");
111 else if (mode != kAIM_Dead)
113 pMotor->MakePhysical();
114 //mprint("Phys!\n");
116 #else
117 // if (mode == kAIM_Dead)
118 // pMotor->MakeNonPhysical();
119 #endif
122 if (mode == kAIM_Efficient)
124 CreatureMakeBallistic(GetID(), kCMB_Efficient);
125 cCreature* pCreature = CreatureFromObj(GetID());
126 if (pCreature->IsPhysical())
128 SetObjImpulse(GetID(), 0, 0, 0, 0);
129 if (!pCreature->GetCreatureDesc()->alwaysBallistic)
131 PhysSetGravity(GetID(), 0);
132 PhysSetBaseFriction(GetID(), 1.0);
135 //mprint("Ballistic!\n");
139 ///////////////////////////////////////
141 void cAIMoveEnactor::MotorStateChangeCallback(void * p)
145 ///////////////////////////////////////
147 BOOL cAIMoveEnactor::MotSave(ITagFile *pTagFile)
149 if (m_pMotionCoord)
150 m_pMotionCoord->Save(pTagFile);
151 return TRUE;
154 ///////////////////////////////////////
156 BOOL cAIMoveEnactor::MotLoad(ITagFile *pTagFile)
158 if (m_pMotionCoord)
159 m_pMotionCoord->Load(pTagFile);
160 return TRUE;
163 ///////////////////////////////////////
165 STDMETHODIMP_(BOOL) cAIMoveEnactor::SupportsMotionActions()
167 return !!m_pMotionCoord;
170 ///////////////////////////////////////
172 HRESULT cAIMoveEnactor::SetMotor(IMotor * pMotor)
174 // this assertion is here just to sanity-check that
175 // motion system enum and ai enums are in synch, since
176 // one gets cast to the other. Would have this in
177 // constructor, but constructor is inline, and this
178 // function doesn't get called very often. KJ 5/20/98
179 AssertMsg(kAIME_Error==kMCoord_Error,"mcoord and ai status enums out of synch");
181 if (!pMotor)
183 if (m_pMotionCoord)
185 m_pMotionCoord->SetMotor(NULL);
186 delete m_pMotionCoord;
187 m_pMotionCoord = NULL;
190 else
192 if (!m_pMotionCoord)
194 m_pMotionCoord = MotSysCreateMotionCoordinator();
195 m_pMotionCoord->SetMotorStateChangeCallback(MotorStateChangeCallback,(void *)this);
196 ResetMotionTags();
198 m_pMotionCoord->SetMotor(pMotor);
200 return S_OK;
203 ///////////////////////////////////////
205 HRESULT cAIMoveEnactor::ResetMotionTags()
207 if (!m_pMotionCoord)
208 return S_FALSE;
210 sMotActorTagList *actorTagList;
211 cTagSet tags;
213 if(!ObjGetActorTagList(m_pAIState->GetID(), &actorTagList))
215 m_pMotionCoord->SetPersistentTags(NULL);
216 return S_OK;
219 tags.FromString(actorTagList->m_TagStrings);
220 m_pMotionCoord->SetPersistentTags(&tags);
222 return S_OK;
225 ///////////////////////////////////////
230 eAIMEStatus cAIMoveEnactor::MotStatus()
232 eAIMEStatus status = eAIMEStatus(m_pMotionCoord->GetStatus());
233 if (!CreatureSelfPropelled(GetID()))
235 if (status == kAIME_ActiveBusy && m_ImpulsePeriod.Expired())
236 status = kAIME_ActiveInterrupt;
238 return status;
241 ///////////////////////////////////////
243 HRESULT cAIMoveEnactor::MotNoAction(ulong deltaTime)
245 eAIMEStatus status;
246 sAIMoveGoal nullGoal;
247 BOOL newStandGoal;
248 BOOL refreshBallistic = FALSE;
250 if (m_pAIState->GetMode() <= kAIM_Efficient)
251 return S_OK;
253 status = MotStatus();
254 // We only play "stand" motions if not efficient, and the coordinator
255 // is idle, and we either didn't play one as our last act or the
256 // AI is actually being rendered (toml 11-03-98)
257 newStandGoal = (status == kMCoord_Idle) && (!(m_flags&kLastWasNoAction) || (m_StandTimer.Expired() && rendobj_object_visible(GetID())));
258 // If we're not ready to "re-stand" then check if we need to update our ballistic creature
259 if (!newStandGoal)
260 refreshBallistic = !CreatureSelfPropelled(GetID()) && (status == kAIME_ActiveInterrupt);
261 if (newStandGoal || refreshBallistic)
263 nullGoal.dir = m_pAIState->GetFacingAng();
264 nullGoal.dest = *m_pAIState->GetLocation();
266 if (newStandGoal)
268 m_flags |= kLastWasNoAction;
269 m_StandTimer.Reset();
270 return MotEnactMoveGoal(nullGoal, cTagSet(), deltaTime);
272 else if (refreshBallistic)
273 return NonMotEnactMoveGoal(nullGoal, deltaTime);
274 return S_OK;
277 ///////////////////////////////////////
279 // Enact a locomotion
282 DECLARE_TIMER(cAIMoveEnactor_MotEnact, Average);
284 HRESULT cAIMoveEnactor::MotEnact(cAIMoveAction * pAction, ulong deltaTime)
286 AUTO_TIMER(cAIMoveEnactor_MotEnact);
288 if (m_pAIState->GetMode() <= kAIM_Efficient)
289 return S_FALSE;
291 #if 0
292 if (m_pMotionCoord->GetStatus() == kMCoord_ActiveBusy)
293 return S_OK;
294 #endif
295 m_flags &= ~kLastWasNoAction;
296 m_StandTimer.Force();
297 return MotEnactMoveGoal(pAction->GetMoveGoal(), pAction->GetTags(), deltaTime);
300 ///////////////////////////////////////
302 // Locomotive goal primitive
305 #define STATIONARY_ERROR (MX_ANG_PI / 4)
306 #define STATIONARY_POS_ERROR_SQ 4.0
308 DECLARE_TIMER(AI_AIME_MEMG, Average);
309 DECLARE_TIMER(AI_AIME_BP_LOC, Average);
311 HRESULT cAIMoveEnactor::MotEnactMoveGoal(const sAIMoveGoal & inputGoal, const cTagSet & tags, ulong deltaTime)
313 eAIMEStatus motionStatus = (eAIMEStatus)m_pMotionCoord->GetStatus();
314 sAIMoveGoal goal = inputGoal;
316 if (motionStatus == kAIME_Idle || motionStatus == kAIME_ActiveInterrupt)
318 AUTO_TIMER(AI_AIME_MEMG);
320 for (int i = 0; i < m_MoveRegulators.Size(); i++)
321 m_MoveRegulators[i]->AssistGoal(inputGoal, &goal);
323 #ifdef INCORRECT_AI_NETWORKING
324 #ifdef NEW_NETWORK_ENABLED
325 AutoAppIPtr(NetManager);
326 AutoAppIPtr_(ObjectNetworking, pObjNet);
327 if (pNetManager->Networking() && pObjNet->ObjHostedHere(m_pAIState->GetID())) // @TODO && !m_pAIState->NetworkingComplexAction()
328 BroadcastMove(goal, tags, deltaTime);
329 #endif
330 #endif
332 const cMxsVector & loc = *m_pAIState->GetLocation();
333 sMcMoveParams params;
335 ConfigSpew("AIEnactTrace",("MotEnactMoveGoal"));
337 params.mask = 0;
338 params.direction.tz = (mxs_ang)((goal.dir.value/TWO_PI)*0xffff);
339 params.direction.tx = params.direction.ty=0;
340 params.duration = deltaTime;
342 if (goal.speed > kAIS_Stopped)
344 params.tags.Add(cTag("Locomote", kMTV_set));
346 if (goal.speed >= kAIS_Fast)
347 params.tags.Add(cTag("LocoUrgent", kMTV_set));
349 mxs_vector tmp;
350 mx_copy_vec(&tmp,&goal.dest);
351 ConfigSpew("CerebllmStride",("%d dest: %g, %g\n",m_pAIState->GetID(),tmp.x,tmp.y));
352 mx_subeq_vec(&tmp,&loc);
353 tmp.z=0;
354 params.distance=mx_mag_vec(&tmp);
355 params.mask|=kMotParmFlag_Distance;
356 ConfigSpew("CerebllmStride",("%d curp: %g, %g\n%d delt: %g, %g\n",\
357 m_pAIState->GetID(),loc.x,loc.y,m_pAIState->GetID(),tmp.x,tmp.y));
359 else
361 cTagSet standTags(AIStandTags(GetID()));
362 if (standTags.Size())
363 params.tags.Append(standTags);
365 params.tags.Append(tags);
367 // only set distance if velocity non-zero
368 params.mask|=(kMotParmFlag_Direction|kMotParmFlag_Duration);
370 if (goal.facing.type != kAIF_Any)
372 params.mask|=kMotParmFlag_Facing;
373 params.facing.tx=params.facing.ty=0;
374 params.facing.tz=(mxs_ang)((ComputeFacing(goal).value/TWO_PI)*0xffff);
377 const char * pszTagsProp = AIGetMotionTags(GetID());
379 if (pszTagsProp)
380 params.tags.Append(cTagSet(pszTagsProp));
382 TIMER_Start(AI_AIME_BP_LOC);
384 IMotionPlan * pPlan = m_pMotionCoord->BuildPlan(m_pMotionCoord->GetNextEndState(), &params);
386 if (pPlan)
388 IManeuver *pManeuver = pPlan->PopFirstManeuver();
389 m_pMotionCoord->SetCurrentManeuver(pManeuver);
390 ConfigSpew("CerebellumTrace",("%d: setting maneuver\n",m_pAIState->GetID()));
391 delete pPlan;
393 TIMER_MarkStop(AI_AIME_BP_LOC);
396 else
398 cStr tagString;
400 #ifndef SHIP
401 if (params.tags.Size())
403 params.tags.ToString(&tagString);
404 Warning(("Unable to build motion plan for obj %d with tags {%s}\n",GetID(),tagString));
406 #endif
407 TIMER_MarkStop(AI_AIME_BP_LOC);
409 return E_FAIL;
413 // If the motion system wont move us, use wedge movement
414 if (m_ImpulsePeriod.Expired() && !CreatureSelfPropelled(GetID()))
415 // @TBD (toml 05-21-99): this appears to cause a double assist goal?
416 return NonMotEnactMoveGoal(inputGoal, deltaTime);
417 else
419 AutoAppIPtr(AINetServices);
420 if (pAINetServices->Networking())
422 sAIImpulse impulse;
423 CalculateImpulse(goal, deltaTime, &impulse);
424 CalculateTargetVel(goal, deltaTime, &impulse);
425 memcpy(m_pTargetVel, &impulse, sizeof(impulse));
429 return S_OK;
432 //////////////////////////////////////////
434 #ifdef INCORRECT_AI_NETWORKING
435 // Non-guaranteed (NG) move message structure. Only used by the following two routines.
436 struct sAINetMsg_Move_NG {
437 sAINetMsg_Header aihdr;
438 ulong deltaTime;
439 sAIMoveGoal goal;
440 char tagsetBlock[1]; // Raw block for tagset
444 // Broadcast a move for the given goal and tags (not based on an action object).
445 void cAIMoveEnactor::BroadcastMove(const sAIMoveGoal &goal, const cTagSet &tags, ulong deltaTime)
447 ConfigSpew("net_ai_spew", ("NET AI: Send move for %d\n", m_pAIState->GetID()));
449 // Figure out the size of the tagset block, allocate an appropriate
450 // sized message, and fill in the tagset block.
451 void *pTagBlock = NULL;
452 int size = TagSetToNetBlock(&tags, &pTagBlock);
453 AssertMsg(pTagBlock, "Tag block is empty!");
454 // msgSize is the size of the message. We subtract 1 for the byte that
455 // we have to declare for tagsetBlock:
456 ulong msgSize = sizeof(sAINetMsg_Move_NG) + size - 1;
457 sAINetMsg_Move_NG *netmsg = (sAINetMsg_Move_NG *)malloc(msgSize);
458 memcpy(netmsg->tagsetBlock, pTagBlock, size);
459 free(pTagBlock);
461 AutoAppIPtr(AINetManager);
462 sAINetMsg_Header hdr = {pAINetManager->NetMsgHandlerID(),
463 m_pAIState->GetID(),
464 FALSE,
465 kAIAT_Move};
466 netmsg->aihdr = hdr;
467 netmsg->deltaTime = deltaTime;
468 netmsg->goal = goal;
470 AutoAppIPtr(NetManager);
471 pNetManager->Broadcast(netmsg, msgSize, FALSE);
472 free(netmsg);
475 // Enact a move received off the network. No action is created for it.
476 STDMETHODIMP_(void) cAIMoveEnactor::HandleMoveMessage(void *pMsg,
477 ObjID fromPlayer)
479 ConfigSpew("net_ai_spew",
480 ("NET AI: Receive move for %d\n", m_pAIState->GetID()));
482 sAINetMsg_Move_NG *netmsg = (sAINetMsg_Move_NG *)pMsg;
483 // Initialize with a string, because the stupid compiler chokes if
484 // we use the empty constructor:
485 cTagSet tags("");
486 NetBlockToTagSet(netmsg->tagsetBlock, fromPlayer, &tags);
488 // Get the goal, tags & delta Time out of netmsg.
489 MotEnactMoveGoal(netmsg->goal, tags, netmsg->deltaTime);
491 #endif
493 ///////////////////////////////////////
495 // Play a motion
497 // @Note (toml 05-19-98): Right now, all motions interrupt
499 DECLARE_TIMER(AI_AIME_ME_M, Average);
500 DECLARE_TIMER(AI_AIME_BP_MOT, Average);
502 HRESULT cAIMoveEnactor::MotEnact(cAIMotionAction * pAction, ulong deltaTime)
504 if ((m_pAIState->GetMode() > kAIM_Efficient) && !pAction->Started())
506 if (!pAction->GetName() && !pAction->GetTags().Size())
507 return MotNoAction(deltaTime);
509 AUTO_TIMER(AI_AIME_ME_M);
511 m_flags &= ~kLastWasNoAction;
512 m_StandTimer.Force();
514 sMcMoveParams params;
516 params.tags = pAction->GetTags();
517 params.mask = 0;
519 if (pAction->IsFacingSet())
521 params.facing.tz = (mxs_ang)((pAction->GetFacing().value/TWO_PI)*0xffff);
522 params.facing.tx = params.facing.ty = 0;
523 params.mask |= kMotParmFlag_Facing;
526 if(pAction->GetFocus(&params.focus, &params.turnspeed))
528 params.mask |= kMotParmFlag_FocusObj;
531 if (pAction->GetName())
533 params.motionNum = MotDescNameGetNum((char *)pAction->GetName());
534 params.mask |= kMotParmFlag_MotNum;
535 if (params.motionNum < 0)
536 return E_FAIL;
537 params.tags.Append(cTag("PlaySpecMotion",kMTV_true));
540 const char * pszTagsProp = AIGetMotionTags(GetID());
542 if (pszTagsProp)
543 params.tags.Append(cTagSet(pszTagsProp));
545 #ifdef INCORRECT_AI_NETWORKING
546 #ifdef NEW_NETWORK_ENABLED
547 AutoAppIPtr(NetManager);
548 AutoAppIPtr_(ObjectNetworking, pObjNet);
549 if (pNetManager->Networking() && pObjNet->ObjHostedHere(m_pAIState->GetID())) // @TODO && !m_pAIState->NetworkingComplexAction()
550 BroadcastMotion(params);
551 #endif
552 #endif
554 return MotEnactMotion(params);
556 if (m_ImpulsePeriod.Expired() && !CreatureSelfPropelled(GetID()))
558 sAIMoveGoal nullGoal;
560 if (pAction->IsFacingSet())
561 nullGoal.dir = pAction->GetFacing();
562 else
563 nullGoal.dir = m_pAIState->GetFacingAng();
564 nullGoal.dest = *m_pAIState->GetLocation();
565 return NonMotEnactMoveGoal(nullGoal, deltaTime);
567 return S_OK;
571 HRESULT cAIMoveEnactor::MotEnactMotion(const sMcMoveParams &params)
573 TIMER_Start(AI_AIME_BP_MOT);
575 const sMcMoveState * pState = m_pMotionCoord->GetNextEndState();
576 IMotionPlan *pPlan = m_pMotionCoord->BuildPlan(pState, &params);
578 if (!pPlan)
580 cStr tagString;
582 params.tags.ToString(&tagString);
583 Warning(("Unable to build motion plan for obj %d with tags {%s}\n",m_pAIState->GetID(),tagString));
585 TIMER_MarkStop(AI_AIME_BP_MOT);
587 return E_FAIL;
590 IManeuver *pManeuver = pPlan->PopFirstManeuver();
591 m_pMotionCoord->SetCurrentManeuver(pManeuver);
592 delete pPlan;
594 TIMER_MarkStop(AI_AIME_BP_MOT);
596 return S_OK;
599 ///////////////////////////////////////
601 #ifdef INCORRECT_AI_NETWORKING
602 // Non-guaranteed (NG) motion message structure. Only used by the following two routines.
603 struct sAINetMsg_Motion_NG {
604 sAINetMsg_Header aihdr;
605 ulong mask;
606 mxs_ang facing_tz;
607 sGlobalObjID focusGID;
608 int motionNum;
609 char tagsetBlock[1]; // Raw block for tagset
612 // Broadcast a motion for the given params.
613 void cAIMoveEnactor::BroadcastMotion(const sMcMoveParams params)
615 ConfigSpew("net_ai_spew", ("NET AI: Send motion for %d\n", m_pAIState->GetID()));
617 // Figure out the size of the tagset block, allocate an appropriate
618 // sized message, and fill in the tagset block.
619 void *pTagBlock = NULL;
620 int size = TagSetToNetBlock(&(params.tags), &pTagBlock);
621 AssertMsg(pTagBlock, "Tag block is empty!");
622 // msgSize is the size of the message. We subtract 1 for the bytes that
623 // we have to declare for tagsetBlock:
624 ulong msgSize = sizeof(sAINetMsg_Motion_NG) + size - 1;
625 sAINetMsg_Motion_NG *netmsg = (sAINetMsg_Motion_NG *)malloc(msgSize);
626 memcpy(netmsg->tagsetBlock, pTagBlock, size);
627 free(pTagBlock);
629 AutoAppIPtr(AINetManager);
630 AutoAppIPtr(NetManager);
631 sAINetMsg_Header hdr = {pAINetManager->NetMsgHandlerID(), m_pAIState->GetID(), FALSE, kAIAT_Motion};
632 netmsg->aihdr = hdr;
633 netmsg->mask = params.mask;
634 if (params.mask & kMotParmFlag_Facing)
635 netmsg->facing_tz = params.facing.tz;
636 if (params.mask & kMotParmFlag_FocusObj)
637 netmsg->focusGID = pNetManager->ToGlobalObjID(params.focus);
638 if (params.mask & kMotParmFlag_MotNum)
639 netmsg->motionNum = params.motionNum;
641 pNetManager->Broadcast(netmsg, msgSize, FALSE);
642 free(netmsg);
645 ///////////////////////////////////////
647 // Enact a motion based on the net message. No action is created.
648 STDMETHODIMP_(void) cAIMoveEnactor::HandleMotionMessage(void *pMsg,
649 ObjID fromPlayer)
651 ConfigSpew("net_ai_spew", ("NET AI: Receive motion for %d\n", m_pAIState->GetID()));
653 sAINetMsg_Motion_NG *netmsg = (sAINetMsg_Motion_NG *)pMsg;
655 // Get the motion parameters out of netmsg.
656 // First the tags.
657 sMcMoveParams params;
658 cTagSet tags("");
659 NetBlockToTagSet(netmsg->tagsetBlock, fromPlayer, &tags);
660 params.tags = tags;
661 // Copy the mask and the optional fields.
662 params.mask = netmsg->mask;
663 if (params.mask & kMotParmFlag_Facing)
665 params.facing.tz = netmsg->facing_tz;
666 params.facing.tx = params.facing.ty = 0;
668 if (params.mask & kMotParmFlag_FocusObj)
670 AutoAppIPtr(NetManager);
671 params.focus = pNetManager->FromGlobalObjID(&netmsg->focusGID);
673 if (params.mask & kMotParmFlag_MotNum)
674 params.motionNum = netmsg->motionNum;
676 // Now do the motion on the proxy ai.
677 MotEnactMotion(params);
679 #endif
681 ///////////////////////////////////////
686 HRESULT cAIMoveEnactor::MotEnact(cAIOrientAction * pAction, ulong deltaTime)
688 m_flags &= ~kLastWasNoAction;
689 m_StandTimer.Force();
690 // @TBD (toml 04-10-98):
691 return 0;