2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/sim/ghostrcv.cpp,v 1.17 1999/08/26 12:53:02 dc Exp $
7 // receive systems for ghosts
14 #include <ghostapi.h> // for EXTERN declare of GhostWeaponObjAttach callback
25 #include <phmods.h> // phys models (all of them) methods (i.e. for the list)
26 #include <phmod.h> // phys model methods (for an individual model)
29 #include <mmanuver.h> // for mocap zaniness
44 #include <plyrmode.h> // umm, player mode
45 #include <wrtype.h> // for Position, for ObjPos stuff
49 ////////////////////////
50 // globals - from ghostapi.h
52 void (*GhostWeaponObjAttach
)(ObjID obj
, int weapon
)=NULL
;
54 ////////////////////////
55 // receiver calls - on actually getting a packet...
57 #define SLEEP_DIST (0.05*0.05)
59 BOOL
_GhostAllowedToSleep(sGhostRemote
*pGR
)
61 cPhysModel
*pModel
= g_PhysModels
.Get(pGR
->obj
);
66 mx_sub_vec(&delta
, &pModel
->GetLocationVec(), &pGR
->info
.pred
.pos
.pos
);
68 if (_GhostIsType(pGR
->cfg
.flags
,IsObj
) && PhysObjOnGround(pGR
->obj
))
71 return (mx_mag2_vec(&delta
) < SLEEP_DIST
);
74 // returns whether to "disable" the ghost
75 BOOL
_GhostPerFrameModeUpdate(sGhostRemote
*pGR
)
77 if (pGR
->info
.last
.pos
.mode
==kGhostModeSleep
)
78 if (_GhostAllowedToSleep(pGR
))
80 if (_ghost_warnings())
81 if (pGR
->cfg
.flags
&kGhostCfDontRun
)
82 _ghost_mprintf(("Hey, sleep for %s, but i was asleep...\n",ObjWarnName(pGR
->obj
)));
83 pGR
->cfg
.flags
|=kGhostCfDontRun
;
85 if (_ghost_watch_events())
86 _ghost_mprintf(("Ghost %s to sleep\n",ObjWarnName(pGR
->obj
)));
92 void _GhostPacketModeUpdate(sGhostRemote
*pGR
)
94 if (pGR
->info
.last
.pos
.mode
==kGhostModeSleep
)
96 pGR
->info
.pred
.pos
.pos
= pGR
->info
.last
.pos
.pos
;
97 mx_zero_vec(&pGR
->info
.pred
.pos
.vel
);
98 _GhostPerFrameModeUpdate(pGR
); // run a "frame" to try and go to sleep
101 if (pGR
->info
.last
.pos
.mode
==kGhostModeRevive
)
103 if (_ghost_warnings())
104 if (pGR
->critter
.r_state
&kGhostRcvDieNext
)
105 _ghost_mprintf(("Revive %s while waiting to die...\n",ObjWarnName(pGR
->obj
)));
106 pGR
->critter
.r_state
=0; // sure, kick its butt, eh?
107 pGR
->info
.last
.pos
.mode
=kGhostModeStand
;
108 if (pGR
->cfg
.flags
&kGhostCfDontRun
)
110 pGR
->cfg
.flags
&=~kGhostCfDontRun
;
111 pGR
->info
.last
.pos
.flags
|=kGhostHBTeleport
;
112 if (_ghost_watch_events())
113 _ghost_mprintf(("Ghost %s revive\n",ObjWarnName(pGR
->obj
)));
115 else if (_ghost_warnings())
116 _ghost_mprintf(("Hey, mode revive update for %s, but i wasnt dont run...\n",ObjWarnName(pGR
->obj
)));
119 if (pGR
->info
.last
.pos
.mode
!=pGR
->info
.pred
.pos
.mode
)
120 if (pGR
->info
.pred
.pos
.mode
==kPM_Jump
)
121 _GhostJumpOff(pGR
->obj
); // we were jumping
122 else if (pGR
->info
.last
.pos
.mode
==kPM_Jump
)
123 _GhostJumpOn(pGR
->obj
); // we are now jumping
126 BOOL
CheckValidSequence(int arrived
, int stored
)
130 if (arrived
+65536-stored
<32768) // handle the wrap case
131 return TRUE
; // we assume we will never get 32K packets behind
132 return FALSE
; // ??? who knows, i guess
135 // on packet recv, just go and update goals in the structures
136 void GhostRecvPacket(ObjID ghost
, int seq_id
, ObjID rel_obj
, sGhostHeartbeat
*pGH
, sGhostMoCap
*pGMC
)
138 sGhostRemote
*pGR
=GhostGetRemote(ghost
);
141 Warning(("GhostRecv called on %s which is not in the remote hash\n",ObjWarnName(ghost
)));
144 _GhostDebugSetupRemote(pGR
);
146 pGR
->cfg
.flags
&=~kGhostCfNoPacket
;
147 sGhostRecvInfo
*pGRP
=&pGR
->info
;
149 // the ugly part (well, ok, an ugly part) here is that even if we get out
150 // of sequence packets... if the newly arrived (that is, the old) one is
151 // guaranteed, we had better deal with its mode/mocap data, since that is
152 // probably why it has been sent guaranteed
154 BOOL valid_packet
=TRUE
, valid_pos
;
155 if (!CheckValidSequence(seq_id
,pGRP
->seq_id
))
157 _ghost_mprintf(("%s out of sequence: arrive %d saved %d\n",ObjWarnName(pGR
->obj
),seq_id
,pGRP
->seq_id
));
158 if ((pGH
->flags
&kGhostHBUseG
)==0)
159 return; // we can just blow this off, as it isnt a G packet
160 if (!CheckValidSequence(seq_id
,pGRP
->last_g_seq
))
162 _ghost_mprintf(("ERROR: %s Guaranteed packet out of sequence: arrive %d saved %d (ng save %d)\n",
163 ObjWarnName(pGR
->obj
),seq_id
,pGRP
->last_g_seq
,pGRP
->seq_id
));
164 return; // we are somehow older than last g packet?
166 valid_packet
=FALSE
; // otherwise, have to do partial update horror
170 if (pGH
->flags
&kGhostHBUseG
)
171 pGRP
->last_g_seq
=seq_id
; // update last G packet as well
173 // these are independant of valid or not, if we are here
174 // we are either a new packet, or most recent G, so these "count"
175 if (pGH
->flags
&kGhostHBObjRel
)
177 _GhostRemoteRelObjPhys(ghost
, rel_obj
, pGRP
->last
.rel_obj
);
178 pGRP
->last
.rel_obj
= rel_obj
;
181 pGRP
->last
.rel_obj
= pGR
->info
.pred
.rel_obj
;
183 // if this packet pos thinks its on a relobj, and we arent, skip our pos
184 valid_pos
=(pGH
->flags
&kGhostHBOnObj
)?
185 pGRP
->last
.rel_obj
!=OBJ_NULL
:pGRP
->last
.rel_obj
==OBJ_NULL
;
189 if (!valid_pos
) // this pos is not valid compared to the rel_obj, so skip it
190 pGH
->pos
=pGRP
->last
.pos
.pos
;
191 pGRP
->last
.pos
= *pGH
;
192 pGRP
->last
.time
= GetSimTime();
193 if (valid_pos
&& ((pGH
->flags
&(kGhostHBNoZPos
|kGhostHBAngOnly
))==0))
194 _ghost_pos_centerconvert(ghost
,&pGRP
->last
.pos
.pos
);
196 else // ok, this is a G packet from before now, keep its mode
197 pGRP
->last
.pos
.mode
=pGH
->mode
;
199 if ((pGH
->flags
&kGhostHBWeap
)==0)
200 pGRP
->last
.pos
.weap
= pGR
->info
.pred
.pos
.weap
;
203 if (_ghost_track_weapons())
204 _ghost_mprintf(("on packet recv %s is attaching weapon %d\n",ObjWarnName(pGR
->obj
),pGH
->weap
));
205 if (GhostWeaponObjAttach
)
206 (*GhostWeaponObjAttach
)(pGR
->obj
,pGH
->weap
-1);
209 pGH
->flags
&=~(kGhostHBAngOnly
|kGhostHBNoZPos
); // for now?
211 // do we need to change mode to react to this packet
212 _GhostPacketModeUpdate(pGR
);
214 #ifdef GHOST_DEBUGGING
215 // talk about rel_objs
216 if (_ghost_track_relobj())
217 if (pGH
->flags
&kGhostHBObjRel
)
218 _ghost_mprintf(("%s recv new relobj %s\n",ObjWarnName(pGR
->obj
),ObjWarnName(rel_obj
)));
220 // warn if getting packets but: not running or no phys and didnt just get that way
221 if (((pGR
->cfg
.flags
&kGhostCfDontRun
)&&(pGR
->info
.last
.pos
.mode
!=kGhostModeSleep
))||
222 (!PhysObjHasPhysics(pGR
->obj
)))
223 if (_ghost_warnings())
224 _ghost_mprintf(("Ghost %s receiving packet but %s\n",ObjWarnName(pGR
->obj
),
225 pGR
->cfg
.flags
&kGhostCfDontRun
?"DontRun":"NoPhys"));
227 if (_ghost_show_recv_pred()) // show the old position on recv
228 _GhostPrintGhostPos(pGR
->obj
,&pGRP
->pred
.pos
,pGR
->cfg
.flags
,"old",-1);
230 // @TBD: compute average "wrongness" when receiving a packet
231 if (_ghost_show_recv_delta())
233 mxs_vector vdiff
, pdiff
;
235 mx_sub_vec(&pdiff
,&pGRP
->last
.pos
.pos
,&pGRP
->pred
.pos
.pos
);
236 mx_sub_vec(&vdiff
,&pGRP
->last
.pos
.vel
,&pGRP
->pred
.pos
.vel
);
237 for (int i
=0; i
<3; i
++)
238 ang_diffs
[i
]=pGRP
->last
.pos
.angle_info
.fac
.el
[i
]-pGRP
->pred
.pos
.angle_info
.fac
.el
[i
];
239 ObjPos
*pPos
=ObjPosGet(pGR
->obj
);
240 mxs_vector cvel
,rpos
=pPos
->loc
.vec
;
241 PhysGetVelocity(pGR
->obj
,&cvel
);
242 mx_subeq_vec(&cvel
,&pGRP
->last
.pos
.vel
);
243 mx_subeq_vec(&rpos
,&pGRP
->last
.pos
.pos
);
245 sprintf(buf
," delv %.4f %.4f %.4f (rv %.4f %.4f %.4f) da %d %d %d\n delp %.4f %.4f %.4f (rp %.4f %.4f %.4f) for %s\n",
246 vdiff
.el
[0],vdiff
.el
[1],vdiff
.el
[2],-cvel
.el
[0],-cvel
.el
[1],-cvel
.el
[2],ang_diffs
[0],ang_diffs
[1],ang_diffs
[2],
247 pdiff
.el
[0],pdiff
.el
[1],pdiff
.el
[2],-rpos
.el
[0],-rpos
.el
[1],-rpos
.el
[2],ObjWarnName(pGR
->obj
));
252 // for now, not sure of real model for pred update? (bet this isnt it, though)
253 // well, in practice, it almost is, the only thing is we want to keep
254 // the old v if new packet has a control vel, dippy dippy dippy, basically
255 mxs_vector old_vel
=pGRP
->pred
.pos
.vel
;
256 pGRP
->pred
= pGRP
->last
;
257 if ((pGRP
->pred
.pos
.flags
&kGhostHBAbsVel
)==0)
258 pGRP
->pred
.pos
.vel
=old_vel
;
259 // may i just say how embarassingly dumb this all is?
261 // and the mocap hookup
263 if ((pGR
->critter
.cur_mocap
.schema_idx
!=pGMC
->schema_idx
)||
264 (pGR
->critter
.cur_mocap
.motion_num
!=pGMC
->motion_num
))
265 { // really need the "irq" flag or something here
266 pGR
->critter
.cur_mocap
=*pGMC
;
267 if (_ghost_track_mocap_pkt())
268 _ghost_mprintf(("%s got packet saying %d %d (%s)\n",
269 ObjWarnName(ghost
),pGMC
->schema_idx
,pGMC
->motion_num
,
270 (pGMC
->motion_num
>0)?(char *)g_pMotionSet
->GetName(pGMC
->motion_num
):"???"));
273 if (_ghost_show_recvs())
274 _ghost_mprintf(("Recv message from %s at time %d\n",ObjWarnName(ghost
),GetSimTime()));
275 if (_ghost_show_full_recvs())
277 _GhostPrintGhostPos(pGR
->obj
,pGH
,pGR
->cfg
.flags
,"r",seq_id
);
278 if (pGMC
|| (pGH
->flags
&kGhostHBObjRel
))
279 _ghost_mprintf((" rel %s mocap %d %d\n",
280 pGRP
->last
.rel_obj
!=OBJ_NULL
?ObjWarnName(pGRP
->last
.rel_obj
):"None",
281 pGMC
?pGMC
->schema_idx
:-1,pGMC
?pGMC
->motion_num
:-1));
285 //////////////////////////////
286 // remote per frame code
288 // updates the position data of pPos, which is the predicted position
289 // using an ultra-simplified physics model
290 // this new target will be aimed at by the "real" ghost model
291 void _UpdateGhostPrediction(sGhostRemote
*pGR
, float dt
)
293 BOOL gravity
= _GhostGravRemote(pGR
->obj
, &pGR
->info
.last
, pGR
->cfg
.flags
);
295 _GhostApproxPhys(pGR
->obj
,&pGR
->info
.last
,&pGR
->info
.pred
,dt
,gravity
);
296 if (!_is_zero_vec(&pGR
->info
.pred
.pos
.vel
))
297 _GhostBleedVelocity(pGR
->obj
, &pGR
->info
.pred
, pGR
->cfg
.flags
, pGR
->info
.last
.time
, dt
);
298 } // currently only do this (bleed) for the recv, which is not right
300 // helper function to smooth the ghosts heading towards the target heading
301 void _SmoothHeadingNonPhysical(ObjID ghost
, sGhostHeartbeat
*pHeart
, float dt
)
304 PhysGetModRotation(ghost
, &ang
);
305 const mxs_ang goal_facing
= pHeart
->angle_info
.tz
; // since tz's are now unified
306 const short delta
= goal_facing
-ang
.tz
;
308 if (delta
==0) return; // since we assume we set this last?
310 const short rot_sign
= (delta
>0)?1:-1; // which way...
311 const int max_rot
= dt
*0xC000, min_rot
= dt
*0x1000; // slower than this is criminal
312 short cmp_rot
= delta
* dt
* 8; // base computed from delta - x by who knows...
314 #ifdef TRY_PACKET_ROT
315 if ((pHeart
->flags
&kGhostHBFullAngs
)==0)
317 short packet_rot
= FIXVEL_TO_DTANG(pHeart
->angle_info
.dtz
,dt
*1000.0);
318 if ((int)packet_rot
*(int)cmp_rot
>0) // same sign, so pay attention
320 if (abs(abs(packet_rot
)-abs(cmp_rot
))>0x400) // they are fairly different
321 cmp_rot
=(short)(((int)cmp_rot
+(int)packet_rot
)/2); // unclear if this is good or bad...
323 // else _ghost_mprintf(("Hey, opposite signs %d %d in packet rot\n",packet_rot,cmp_rot));
328 if (abs(cmp_rot
)<min_rot
)
329 cmp_rot
=rot_sign
*min_rot
; // but which direction?
330 else if (abs(cmp_rot
)>max_rot
)
331 cmp_rot
=rot_sign
*max_rot
;
333 if (abs(delta
) < abs(cmp_rot
))
334 ang
.tz
= goal_facing
;
337 if (_ghost_track_heading())
338 _ghost_mprintf(("%s ang.tz now %x, max_rot %x goal %x\n",ObjWarnName(ghost
),ang
.tz
,cmp_rot
,goal_facing
));
339 PhysSetModRotation(ghost
, &ang
);
340 PhysControlRotation(ghost
, &ang
);
343 float _generate_ghost_rating(sGhostRemote
*pGR
)
348 // recomputes phys forces to drive us towards the target
349 void _ReAimGhostPhysics(sGhostRemote
*pGR
, float dt
)
351 // aim at the ghost position
352 ObjID ghost
=pGR
->obj
;
353 sGhostPos
*pTarg
=&pGR
->info
.pred
;
354 cPhysModel
*pModel
= g_PhysModels
.Get(ghost
);
357 { // since our physmodel is destroyed too soon sometimes
358 Warning(("Reaim %s but no longer has physmodels\n",ObjWarnName(ghost
)));
362 // ok, now lets set the controls for the phys model
363 const mxs_vector
*p_curpos
= &pModel
->GetLocationVec();
365 mxs_vector world_targ_pos
= pTarg
->pos
.pos
;
366 _ghost_pos_reltoworld(ghost
,pTarg
->rel_obj
,&world_targ_pos
);
368 if (_ghost_track_relobj())
369 if (pTarg
->rel_obj
!=OBJ_NULL
)
370 _ghost_mprintf(("%s rel_obj %s\n",ObjWarnName(pGR
->obj
),ObjWarnName(pTarg
->rel_obj
)));
372 mxs_vector dpos
, dvel
;
373 mx_sub_vec(&dpos
,&world_targ_pos
,p_curpos
);
374 if (_ghost_frame_delta())
375 _ghost_mprintf(("%s dp %g %g %g target %g %g %g\n",ObjWarnName(ghost
),dpos
.x
,dpos
.y
,dpos
.z
,pTarg
->pos
.pos
.x
,pTarg
->pos
.pos
.y
,pTarg
->pos
.pos
.z
));
377 // now check our rating, see how we are doing - need to give up and teleport
378 _generate_ghost_rating(pGR
);
380 // @TBD ok, really someday we will have to do something real
381 #define TELEPORT_THRESH 12.0*12.0
382 if ((pGR
->cfg
.flags
&kGhostCfFirstTPort
)||
383 (pTarg
->pos
.flags
&kGhostHBTeleport
)||
384 (mx_mag2_vec(&dpos
)>TELEPORT_THRESH
))
386 if (_GhostTeleport(ghost
,&dpos
,&world_targ_pos
))
387 { // @TBD: this is ugly, really should restuff curpos...
388 const mxs_vector
*p_newpos
= &pModel
->GetLocationVec();
389 mx_sub_vec(&dpos
,&world_targ_pos
,p_newpos
);
391 pTarg
->pos
.flags
&=~kGhostHBTeleport
;
392 pGR
->cfg
.flags
&=~kGhostCfFirstTPort
;
395 BOOL fast_packet_vel
;
397 // if an AI, i dont want to add mag, for now...
398 if (_GhostIsType(pGR
->cfg
.flags
,AI
))
399 fast_packet_vel
=FALSE
;
400 else // some slowdown thing here, maybe?
401 fast_packet_vel
=(mx_mag2_vec(&pTarg
->pos
.vel
)>1.0);
404 if (mx_mag2_vec(&dvel
)>1.0)
405 mx_add_vec(&dvel
,&dpos
,&pTarg
->pos
.vel
);
409 mx_scale_vec(&dvel
,&dpos
,2.0);
411 // @TBD: rewrite to do "slowing down" w/slam
412 // we should care about control v. abs vel
413 BOOL slam_vel
=_GhostIsType(pGR
->cfg
.flags
,IsObj
);
414 slam_vel
= slam_vel
|| (!fast_packet_vel
|| (pTarg
->pos
.mode
==kPM_Jump
));
417 PhysSetVelocity(ghost
,&dvel
);
418 PhysControlVelocity(ghost
,&dvel
);
420 if (!_GhostIsType(pGR
->cfg
.flags
,IsObj
))
421 _SmoothHeadingNonPhysical(ghost
,&pTarg
->pos
,dt
);
428 void _AimGhostHead(sGhostRemote
*pGR
)
430 // doing snazzy head-focus thing
431 if (((pGR
->info
.last
.pos
.flags
& kGhostHBFullAngs
) == 0) &&
432 ((pGR
->cfg
.flags
& kGhostCfNoCret
) == 0))
434 if (pGR
->info
.last
.pos
.angle_info
.p
== 0)
436 CreatureSetFocusObj(pGR
->obj
, OBJ_NULL
);
441 mxs_vector obj_focus_vec
;
442 mxs_vector world_focus_vec
;
444 mx_mk_vec(&unit_vec
, 100, 0, 0);
446 short pitch
= ((short)pGR
->info
.last
.pos
.angle_info
.p
) / 2;
448 mx_rot_y_vec(&obj_focus_vec
, &unit_vec
, (mxs_ang
)pitch
);
451 mx_ang2mat(&rot
, &ObjPosGet(pGR
->obj
)->fac
);
453 mx_mat_mul_vec(&world_focus_vec
, &rot
, &obj_focus_vec
);
454 mx_addeq_vec(&world_focus_vec
, &ObjPosGet(pGR
->obj
)->loc
.vec
);
456 CreatureSetFocusLoc(pGR
->obj
, &world_focus_vec
);
461 // interface with mvr
463 static int gMotionNumber
= -1;
465 int GetGhostMotionNumber(void)
467 int ret_val
=gMotionNumber
;
473 // internal calls/structure
475 // use a tagset to build a high level goal/plan
476 IMotionPlan
*_BuildTagBasedPlan(sGhostRemote
*pGR
, char *motion
)
478 sMcMoveParams params
;
479 cTagSet
SpecMotion(motion
);
480 params
.tags
= SpecMotion
;
482 if (_ghost_track_tag_mocap()||_ghost_track_mocap())
483 _ghost_mprintf(("%s playing %s tag based\n",ObjWarnName(pGR
->obj
),motion
));
485 pGR
->critter
.pMotCoord
->BuildPlan(pGR
->critter
.pMotCoord
->GetNextEndState(),¶ms
);
489 // using the secret number setting method, build a specific motion
490 IMotionPlan
*_BuildSchemaOffsetPlan(sGhostRemote
*pGR
, int schema_idx
, int mot_num
)
492 gMotionNumber
= mot_num
;
494 cMotionSchema
*pMotSchema
;
495 if (!g_pMotionDatabase
->GetSchema(schema_idx
, &pMotSchema
))
497 Warning(("Ghost: No schema at index %d\n", schema_idx
));
502 if (!MSchUGetMotControllerID(pMotSchema
, &factory_id
))
504 Warning(("Ghost: No motor controller for schema %d\n", schema_idx
));
508 sMcMoveParams params
;
511 if (_ghost_track_idx_mocap()||_ghost_track_mocap())
512 _ghost_mprintf(("%s playing %d %d (%s) idx based\n",
513 ObjWarnName(pGR
->obj
),schema_idx
,mot_num
,
514 mot_num
>0?(char *)g_pMotionSet
->GetName(mot_num
):"???"));
516 IMotionCoordinator
*pMotCoord
= pGR
->critter
.pMotCoord
;
518 // wow, this is way ugly, aint it
519 cMotionPlan
*pMotionPlan
=
520 g_ManeuverFactoryList
[factory_id
]->CreatePlan(pMotSchema
,
521 *((cMotionCoordinator
*)pMotCoord
)->GetInterMnvrState(),
522 *pMotCoord
->GetNextEndState(),
524 CreatureGetMotorInterface(pGR
->obj
),
525 (cMotionCoordinator
*)pMotCoord
);
529 IManeuver
*pMnvr
=pMotionPlan
->GetFirstManeuver();
530 pMnvr
->SetAppData(factory_id
); // store this for load/save purposes
535 // go run the next correct mocap for this ghost...
536 // using either the motion tags or the internal numbers sent over the wire
537 void _MocapRunMe(sGhostRemote
*pGR
, char *motion
)
539 IMotionPlan
*pPlan
=NULL
;
541 pPlan
=_BuildTagBasedPlan(pGR
,motion
);
543 pPlan
=_BuildSchemaOffsetPlan(pGR
,pGR
->critter
.cur_mocap
.schema_idx
,
544 pGR
->critter
.cur_mocap
.motion_num
);
548 IManeuver
*pManeuver
= pPlan
->PopFirstManeuver();
549 pGR
->critter
.pMotCoord
->SetCurrentManeuver(pManeuver
);
554 void _AnalyzeVelocity(sGhostRemote
*pGR
, eGhostMotionSpeed
*spd
, int *dir
)
558 // not quite right, want some sort of hybrid or something?
559 PhysGetVelocity(pGR
->obj
,&ovel
);
560 // ovel=pGR->info.last.pos.vel; // lets see how this looks?
562 if (pGR
->info
.last
.pos
.mode
!=kGhostModeClimb
)
564 float raw_spd_2
=mx_mag2_vec(&ovel
);
566 // Find delta of raw speed and the speed of the thing we're standing on
567 sGhostRecvInfo
*pGRP
= &pGR
->info
;
568 if ((pGRP
->last
.rel_obj
!= OBJ_NULL
) && PhysObjHasPhysics(pGRP
->last
.rel_obj
))
571 PhysGetVelocity(pGRP
->last
.rel_obj
, &tvel
);
572 raw_spd_2
-= mx_mag2_vec(&tvel
);
575 *dir
=3; // forward is the default
576 if (raw_spd_2
>0.5) // see if we are moving, really
577 { // if moving, compare facing to velocity vector, choose quadrant we are pointing at
579 PhysGetModRotation(pGR
->obj
, &angvec
);
580 float vel_ang
=atan2(ovel
.y
,ovel
.x
);
581 if (vel_ang
<0) vel_ang
+=MX_REAL_2PI
;
582 fixang vel_fixang
=(vel_ang
*MX_ANG_PI
)/MX_REAL_PI
;
584 static int result_dir
[]={3,3,3,2,2,4,4,4,4,4,4,1,1,3,3,3};
585 // check for sides and back, left=1, right=2, back=4 (front is 3, go figure)
586 fixang ang_diff
=angvec
.tz
-vel_fixang
;
587 int cone_index
=((int)ang_diff
)>>12; // should be 0-15 now
588 *dir
=result_dir
[cone_index
];
592 raw_spd_2
*=2; // was 4 - since backstep and sidestep are slower
593 if (raw_spd_2
>1.2*1.2)
594 if (raw_spd_2
>12.0*12.0)
595 *spd
=kGhostSpeedFast
;
597 *spd
=kGhostSpeedNorm
;
599 *spd
=kGhostSpeedNone
;
602 char *_ChooseCapture(sGhostRemote
*pGR
, BOOL cur_idle
)
604 uchar next_mode
=kGhostModeNone
;
605 eGhostMotionSpeed cur_speed
=kGhostSpeedNone
;
607 int ghost_dir
, init_state
=pGR
->critter
.r_state
;
608 pGR
->critter
.r_state
&=~(kGhostRcvStand
|kGhostRcvLoco
); // always clear them
610 _AnalyzeVelocity(pGR
,&cur_speed
,&ghost_dir
);
612 if (_GhostIsType(pGR
->cfg
.flags
,Player
))
614 switch (pGR
->info
.last
.pos
.mode
)
616 case kGhostModeCrouch
: // hmmm
617 next_mode
=kGhostModeCrouch
;
619 case kGhostModeJumpBase
:
621 BOOL not_jump
=((pGR
->critter
.mmode
!=kGhostModeJumpStart
)&&
622 (pGR
->critter
.mmode
!=kGhostModeJumping
));
624 next_mode
=kGhostModeJumpStart
;
626 next_mode
=kGhostModeJumping
;
632 // return NULL; // is this the right thing? for "do nothing"
634 case kGhostModeCarry
:
635 case kGhostModeSlide
:
636 Warning(("Unsupported mode %d in player ghost rcv\n",pGR
->info
.last
.pos
.mode
));
639 default: // just leave the mode to get through
640 next_mode
=pGR
->info
.last
.pos
.mode
;
646 next_mode
=pGR
->info
.last
.pos
.mode
;
652 case kGhostModeStand
:
653 if (cur_speed
==kGhostSpeedNone
)
655 if ((init_state
&kGhostRcvStand
)&&(!cur_idle
))
658 if (pGR
->critter
.stand_cnt
==0)
659 pGR
->critter
.stand_cnt
=16+(Rand()&0xf);
660 if ((--pGR
->critter
.stand_cnt
)==1)
663 pGR
->critter
.stand_cnt
=0;
667 pGR
->critter
.r_state
|=kGhostRcvStand
;
671 if ((init_state
&kGhostRcvLoco
)&&(!cur_idle
))
676 c_str
="locomote, direction 1";
677 else if (ghost_dir
==2)
678 c_str
="locomote, direction 2";
679 else if (ghost_dir
==4)
680 c_str
="locomote, direction 4";
684 else if (cur_speed
==kGhostSpeedFast
)
685 c_str
="locomote, locourgent";
688 pGR
->critter
.r_state
|=kGhostRcvLoco
;
691 case kGhostModeLeanLeft
:
692 c_str
="stand,leaningleft";
694 case kGhostModeLeanRight
:
695 c_str
="stand,leaningright";
697 case kGhostModeJumpStart
:
699 if (cur_speed
==kGhostSpeedNone
)
702 c_str
="jumping, locourgent, direction 3";
707 case kGhostModeJumping
:
708 c_str
="stand"; // for now, at least
711 c_str
="locomote, climbing";
713 case kGhostModeClimb
:
714 if (cur_speed
==kGhostSpeedNone
)
717 c_str
="locomote, climbing";
719 case kGhostModeCrouch
:
720 if (cur_speed
==kGhostSpeedNone
)
721 c_str
="stand, crouching";
723 c_str
="locomote, crouching";
726 if (_ghost_warnings())
727 _ghost_mprintf(("Next mode %d for %s (in %d idle %d)\n",next_mode
,ObjWarnName(pGR
->obj
),pGR
->info
.last
.pos
.mode
,cur_idle
));
731 pGR
->critter
.mmode
=next_mode
;
735 BOOL
_NewMocapReady(sGhostRemote
*pGR
)
737 return (pGR
->critter
.cur_mocap
.schema_idx
>0);
740 // spin the standing capture in place...
741 void _MocapEval(sGhostRemote
*pGR
, float dt
)
743 if (_ghost_no_mocap())
745 if (_NewMocapReady(pGR
))
748 pGR
->critter
.cur_mocap
.schema_idx
=kGhostMotSchemaNoCustom
;
749 pGR
->critter
.r_state
|= kGhostRcvCustom
;
750 pGR
->critter
.r_state
&=~kGhostRcvDidWeap
;
751 if (pGR
->info
.last
.pos
.flags
&kGhostHBDead
)
753 pGR
->critter
.r_state
|= kGhostRcvDieNext
;
754 if (_ghost_watch_events())
755 _ghost_mprintf(("Ghost %s set dead at moment\n",ObjWarnName(pGR
->obj
)));
758 else // either playing, or need to generate a non-custom
760 BOOL gloco_interrupt
= ((pGR
->critter
.r_state
&kGhostRcvCustom
) &&
761 (pGR
->critter
.cur_mocap
.schema_idx
==kGhostMotSchemaGLoco
));
763 BOOL cur_idle
=gloco_interrupt
||(pGR
->critter
.pMotCoord
->GetStatus()==kMCoord_Idle
);
767 if (_ghost_watch_events())
768 _ghost_mprintf(("Ghost %s forced idle from gloco\n",ObjWarnName(pGR
->obj
)));
769 // clear, so we don't keep interrupting
770 pGR
->critter
.cur_mocap
.schema_idx
=kGhostMotSchemaNoCustom
;
773 if (pGR
->critter
.r_state
&kGhostRcvCustom
)
775 // amazingly horrifying hack to test horribly hypothesis about weaponevents
776 // check to see if we should call StartWeaponSwing here? or just on packet edge trigger?
777 if (!_GhostIsType(pGR
->cfg
.flags
,Player
))
778 if (pGR
->info
.last
.pos
.flags
&kGhostHBStartSw
) // lets start the swing?
779 if ((pGR
->critter
.r_state
&kGhostRcvDidWeap
)==0)
781 if (_ghost_track_weapons())
782 _ghost_mprintf(("delayed set up weapon event for %s\n",ObjWarnName(pGR
->obj
)));
783 WeaponEvent(kStartAttack
, pGR
->obj
, OBJ_NULL
);
784 pGR
->critter
.r_state
|=kGhostRcvDidWeap
;
785 } // i think, though who knows, maybe we should do this, eh?
788 pGR
->critter
.r_state
&=~kGhostRcvCustom
;
789 if (pGR
->critter
.r_state
&kGhostRcvDieNext
)
790 if (pGR
->critter
.r_state
&kGhostRcvHasDied
)
791 _ghost_mprintf(("Hey: Ghost %s already dead\n",ObjWarnName(pGR
->obj
)));
792 else // deregister our phys models locally - and return - only once, though
794 pGR
->critter
.r_state
&=~kGhostRcvDieNext
;
795 pGR
->critter
.r_state
|= kGhostRcvHasDied
;
796 if (_ghost_watch_events())
797 _ghost_mprintf(("Ghost %s dead/dereg\n",ObjWarnName(pGR
->obj
)));
799 if (_GhostIsType(pGR
->cfg
.flags
,Player
))
800 pGR
->cfg
.flags
|=kGhostCfDontRun
; // we should stop doing for a bit, eh?
802 PhysDeregisterModel(pGR
->obj
); // woo woo
806 else // never interrupt a custom capture with a generated
807 return; // at least, not for now
809 if (ObjIsPosed(pGR
->obj
)) // ???? - fix Dr. Watts, basically
811 char *_capture_str
=_ChooseCapture(pGR
,cur_idle
);
812 if (_capture_str
==NULL
)
813 return; // early exit
815 if (strncmp(_capture_str
,pGR
->critter
.mocap_name
,strlen(_capture_str
))==0)
817 if (_ghost_track_tag_mocap()||_ghost_track_mocap())
818 _ghost_mprintf(("Hope to run <%s>, idle %d, on %s (s %d)\n",_capture_str
,cur_idle
,ObjWarnName(pGR
->obj
),pGR
->critter
.stand_cnt
));
819 _MocapRunMe(pGR
,_capture_str
);
820 strncpy(pGR
->critter
.mocap_name
,_capture_str
,MAX_MOCAP_STR_LEN
);
824 // actually do a remote frame
825 void _GhostFrameProcessRemote(sGhostRemote
*pGR
, float dt
)
827 _GhostDebugSetupRemote(pGR
);
828 if (!PhysObjHasPhysics(pGR
->obj
))
829 return; // dont run anything if we are non-physical
830 if (pGR
->cfg
.flags
&(kGhostCfDontRun
|kGhostCfDisable
))
831 return; // and, umm, dont run if we are not supposed to run... duh
833 if (pGR
->cfg
.flags
&kGhostCfNew
)
835 if (!_GhostAllowedToBuildModels(pGR
)) // It's not ready yet
837 _GhostBuildMotionCoord(pGR
);
838 _GhostConfigureRemoteModels(pGR
);
839 pGR
->cfg
.flags
&=~kGhostCfNew
;
842 // for now, we make sure we have some real data and are supposed to do phys
843 if ((pGR
->cfg
.flags
&kGhostCfNoPacket
)==0)
845 if (_ghost_show_recv_prefr())
846 _GhostPrintGhostPos(pGR
->obj
,&pGR
->info
.pred
.pos
,pGR
->cfg
.flags
,"rpre",-1);
848 // actually update local physics setup and so forth
849 _UpdateGhostPrediction(pGR
,dt
);
850 _ReAimGhostPhysics(pGR
,dt
);
852 _GhostPerFrameModeUpdate(pGR
);
854 #ifdef GHOST_DEBUGGING
855 if (_ghost_show_recv_frame())
856 _GhostPrintGhostPos(pGR
->obj
,&pGR
->info
.pred
.pos
,pGR
->cfg
.flags
,"rprd",-1);
858 if (_ghost_show_recv_real())
860 ObjPos
*pPos
=ObjPosGet(pGR
->obj
);
862 PhysGetVelocity(pGR
->obj
,&cvel
);
864 sprintf(buf
," realv: %.4f %.4f %.4f at %d\n realp: %.4f %.4f %.4f fac %x %x %x Obj %d\n",
865 cvel
.x
,cvel
.y
,cvel
.z
,GetSimTime(),
866 pPos
->loc
.vec
.x
,pPos
->loc
.vec
.y
,pPos
->loc
.vec
.z
,
867 pPos
->fac
.tx
,pPos
->fac
.ty
,pPos
->fac
.tz
,pGR
->obj
);
873 // now we need mocap joy
874 // run this always, so we dont get crucifixes at start...
875 if ((pGR
->cfg
.flags
& kGhostCfNoCret
) == 0)