convert line ends
[canaan.git] / prj / cam / src / sim / ghostrcv.cpp
blobd11c290608ec9530d237294d048bf90c6f27258f
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
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
9 #include <ghost.h>
10 #include <ghostphy.h>
11 #include <ghostrcv.h>
12 #include <ghostlst.h>
13 #include <ghostmvr.h>
14 #include <ghostapi.h> // for EXTERN declare of GhostWeaponObjAttach callback
15 #include <ghostwep.h>
17 #include <simtime.h>
18 #include <rand.h>
20 #include <minmax.h>
22 #include <objpos.h>
23 #include <physapi.h>
24 #include <phcore.h>
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)
27 #include <phcontct.h>
29 #include <mmanuver.h> // for mocap zaniness
30 #include <mcoord.h>
31 #include <mclntapi.h>
32 #include <motdesc.h>
33 #include <motset.h>
34 #include <motprop.h>
35 #include <mschutil.h>
36 #include <motmngr.h>
37 #include <creatapi.h>
38 #include <creatext.h>
39 #include <cretprop.h>
40 #include <weapcb.h>
42 //#include <objpos.h>
44 #include <plyrmode.h> // umm, player mode
45 #include <wrtype.h> // for Position, for ObjPos stuff
47 #include <dbmem.h>
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);
62 if (pModel == NULL)
63 return TRUE;
65 mxs_vector delta;
66 mx_sub_vec(&delta, &pModel->GetLocationVec(), &pGR->info.pred.pos.pos);
68 if (_GhostIsType(pGR->cfg.flags,IsObj) && PhysObjOnGround(pGR->obj))
69 delta.z = 0;
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;
84 _GhostPhysSleep(pGR);
85 if (_ghost_watch_events())
86 _ghost_mprintf(("Ghost %s to sleep\n",ObjWarnName(pGR->obj)));
87 return TRUE;
89 return FALSE;
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
99 return;
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)));
117 return;
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)
128 if (arrived>stored)
129 return TRUE;
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);
139 if (pGR==NULL)
141 Warning(("GhostRecv called on %s which is not in the remote hash\n",ObjWarnName(ghost)));
142 return;
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
168 else
169 pGRP->seq_id=seq_id;
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;
180 else
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;
187 if (valid_packet)
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;
201 else
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;
234 short ang_diffs[3];
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);
244 char buf[256];
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));
248 mprintf(buf);
250 #endif
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
262 if (pGMC)
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)
303 mxs_angvec ang;
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));
325 #endif
327 if (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;
335 else
336 ang.tz += cmp_rot;
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)
345 return 0.0;
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);
356 if (pModel==NULL)
357 { // since our physmodel is destroyed too soon sometimes
358 Warning(("Reaim %s but no longer has physmodels\n",ObjWarnName(ghost)));
359 return;
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);
403 if (fast_packet_vel)
404 if (mx_mag2_vec(&dvel)>1.0)
405 mx_add_vec(&dvel,&dpos,&pTarg->pos.vel);
406 else
407 dvel=pTarg->pos.vel;
408 else
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));
416 if (slam_vel)
417 PhysSetVelocity(ghost,&dvel);
418 PhysControlVelocity(ghost,&dvel);
420 if (!_GhostIsType(pGR->cfg.flags,IsObj))
421 _SmoothHeadingNonPhysical(ghost,&pTarg->pos,dt);
424 ////////////////
425 // mocap stages
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);
437 return;
440 mxs_vector unit_vec;
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);
450 mxs_matrix rot;
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);
460 ////////////////
461 // interface with mvr
463 static int gMotionNumber = -1;
465 int GetGhostMotionNumber(void)
467 int ret_val=gMotionNumber;
468 gMotionNumber=-1;
469 return ret_val;
472 ////////////////
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;
481 params.mask = 0;
482 if (_ghost_track_tag_mocap()||_ghost_track_mocap())
483 _ghost_mprintf(("%s playing %s tag based\n",ObjWarnName(pGR->obj),motion));
484 IMotionPlan *pPlan =
485 pGR->critter.pMotCoord->BuildPlan(pGR->critter.pMotCoord->GetNextEndState(),&params);
486 return pPlan;
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));
498 return NULL;
501 int factory_id;
502 if (!MSchUGetMotControllerID(pMotSchema, &factory_id))
504 Warning(("Ghost: No motor controller for schema %d\n", schema_idx));
505 return NULL;
508 sMcMoveParams params;
509 params.mask = 0;
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(),
523 params,
524 CreatureGetMotorInterface(pGR->obj),
525 (cMotionCoordinator *)pMotCoord);
527 if (pMotionPlan)
529 IManeuver *pMnvr=pMotionPlan->GetFirstManeuver();
530 pMnvr->SetAppData(factory_id); // store this for load/save purposes
532 return pMotionPlan;
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;
540 if (motion[0]!='\0')
541 pPlan=_BuildTagBasedPlan(pGR,motion);
542 else
543 pPlan=_BuildSchemaOffsetPlan(pGR,pGR->critter.cur_mocap.schema_idx,
544 pGR->critter.cur_mocap.motion_num);
546 if (pPlan)
548 IManeuver *pManeuver = pPlan->PopFirstManeuver();
549 pGR->critter.pMotCoord->SetCurrentManeuver(pManeuver);
550 delete pPlan;
554 void _AnalyzeVelocity(sGhostRemote *pGR, eGhostMotionSpeed *spd, int *dir)
556 mxs_vector ovel;
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)
563 ovel.z=0;
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))
570 mxs_vector tvel;
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
578 mxs_angvec angvec;
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];
591 if ((*dir)!=3)
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;
596 else
597 *spd=kGhostSpeedNorm;
598 else
599 *spd=kGhostSpeedNone;
602 char *_ChooseCapture(sGhostRemote *pGR, BOOL cur_idle)
604 uchar next_mode=kGhostModeNone;
605 eGhostMotionSpeed cur_speed=kGhostSpeedNone;
606 char *c_str=NULL;
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;
618 break;
619 case kGhostModeJumpBase:
621 BOOL not_jump=((pGR->critter.mmode!=kGhostModeJumpStart)&&
622 (pGR->critter.mmode!=kGhostModeJumping));
623 if (not_jump)
624 next_mode=kGhostModeJumpStart;
625 else if (cur_idle)
626 next_mode=kGhostModeJumping;
627 else
628 return NULL;
629 break;
631 case kGhostModeDead:
632 // return NULL; // is this the right thing? for "do nothing"
633 break;
634 case kGhostModeCarry:
635 case kGhostModeSlide:
636 Warning(("Unsupported mode %d in player ghost rcv\n",pGR->info.last.pos.mode));
637 case kGhostModeSwim:
638 break;
639 default: // just leave the mode to get through
640 next_mode=pGR->info.last.pos.mode;
641 break;
644 else
646 next_mode=pGR->info.last.pos.mode;
649 switch (next_mode)
651 case kGhostModeNone:
652 case kGhostModeStand:
653 if (cur_speed==kGhostSpeedNone)
655 if ((init_state&kGhostRcvStand)&&(!cur_idle))
656 return NULL;
657 BOOL idle=FALSE;
658 if (pGR->critter.stand_cnt==0)
659 pGR->critter.stand_cnt=16+(Rand()&0xf);
660 if ((--pGR->critter.stand_cnt)==1)
662 c_str="idlegesture";
663 pGR->critter.stand_cnt=0;
665 else
666 c_str="stand";
667 pGR->critter.r_state|=kGhostRcvStand;
669 else
671 if ((init_state&kGhostRcvLoco)&&(!cur_idle))
672 return NULL;
673 if (ghost_dir!=3)
675 if (ghost_dir==1)
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";
681 else
682 c_str="locomote";
684 else if (cur_speed==kGhostSpeedFast)
685 c_str="locomote, locourgent";
686 else
687 c_str="locomote";
688 pGR->critter.r_state|=kGhostRcvLoco;
690 break;
691 case kGhostModeLeanLeft:
692 c_str="stand,leaningleft";
693 break;
694 case kGhostModeLeanRight:
695 c_str="stand,leaningright";
696 break;
697 case kGhostModeJumpStart:
698 #if 1
699 if (cur_speed==kGhostSpeedNone)
700 c_str="jumping";
701 else
702 c_str="jumping, locourgent, direction 3";
703 #else
704 c_str="stand";
705 #endif
706 break;
707 case kGhostModeJumping:
708 c_str="stand"; // for now, at least
709 break;
710 case kGhostModeSwim:
711 c_str="locomote, climbing";
712 break;
713 case kGhostModeClimb:
714 if (cur_speed==kGhostSpeedNone)
715 c_str="stand";
716 else
717 c_str="locomote, climbing";
718 break;
719 case kGhostModeCrouch:
720 if (cur_speed==kGhostSpeedNone)
721 c_str="stand, crouching";
722 else
723 c_str="locomote, crouching";
724 break;
725 default:
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));
728 break;
731 pGR->critter.mmode=next_mode;
732 return c_str;
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())
744 return;
745 if (_NewMocapReady(pGR))
747 _MocapRunMe(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);
765 if (gloco_interrupt)
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?
786 if (cur_idle)
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?
801 else
802 PhysDeregisterModel(pGR->obj); // woo woo
803 return;
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
810 return;
811 char *_capture_str=_ChooseCapture(pGR,cur_idle);
812 if (_capture_str==NULL)
813 return; // early exit
814 if (!cur_idle)
815 if (strncmp(_capture_str,pGR->critter.mocap_name,strlen(_capture_str))==0)
816 return;
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
836 return;
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);
851 _AimGhostHead(pGR);
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);
861 mxs_vector cvel;
862 PhysGetVelocity(pGR->obj,&cvel);
863 char buf[255];
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);
868 mprint(buf);
870 #endif
873 // now we need mocap joy
874 // run this always, so we dont get crucifixes at start...
875 if ((pGR->cfg.flags & kGhostCfNoCret) == 0)
876 _MocapEval(pGR,dt);