1 /***** HModel.c *****/
2 /***** CDF 21/10/97 *****/
3 
4 #include "3dc.h"
5 
6 #include "inline.h"
7 #include "module.h"
8 #include "stratdef.h"
9 #include "gamedef.h"
10 #include "comp_shp.h"
11 #include "dynblock.h"
12 #include "dynamics.h"
13 #include "pfarlocs.h"
14 #include "pheromon.h"
15 #include "bh_types.h"
16 #include "pvisible.h"
17 #include "bh_far.h"
18 #include "bh_debri.h"
19 #include "bh_pred.h"
20 #include "bh_paq.h"
21 #include "bh_queen.h"
22 #include "bh_marin.h"
23 #include "lighting.h"
24 #include "bh_weap.h"
25 #include "weapons.h"
26 #include "psnd.h"
27 #include "load_shp.h"
28 #include "plat_shp.h"
29 #include "avp_userprofile.h"
30 #include "maths.h"
31 #include "opengl.h"
32 
33 #define UseLocalAssert Yes
34 #include "ourasert.h"
35 #include "particle.h"
36 #include "kshape.h"
37 #include "sfx.h"
38 #include "dxlog.h"
39 #include <math.h>
40 
41 #define AUTODETECT 1
42 #define ADD_ELEVATION_OFFSETS 1
43 #define SPARKS_FOR_A_SPRAY 15
44 
45 int Simplify_HModel_Rendering=0;
46 
47 extern enum PARTICLE_ID GetBloodType(STRATEGYBLOCK *sbPtr);
48 extern void DoShapeAnimation (DISPLAYBLOCK * dptr);
49 extern void RenderThisHierarchicalDisplayblock(DISPLAYBLOCK *dbPtr);
50 void MatToQuat (MATRIXCH *m, QUAT *quat);
51 
52 /* protos for this file */
53 void New_Preprocess_Keyframe(KEYFRAME_DATA *this_keyframe, KEYFRAME_DATA *next_keyframe,int one);
54 void MulQuat(QUAT *q1,QUAT *q2,QUAT *output);
55 void Slerp(KEYFRAME_DATA *input,int lerp,QUAT *output);
56 void Analyse_Tweening_Data(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,int base_timer,VECTORCH *output_offset,MATRIXCH *output_matrix);
57 static void FindHeatSource_Recursion(HMODELCONTROLLER *controllerPtr, SECTION_DATA *sectionDataPtr);
58 void Budge_HModel(HMODELCONTROLLER *controller,VECTORCH *offset);
59 
60 /* external globals */
61 extern int NormalFrameTime;
62 extern int GlobalFrameCounter;
63 extern VIEWDESCRIPTORBLOCK *Global_VDB_Ptr;
64 
65 int GlobalGoreRate=NORMAL_GORE_RATE;
66 int incIDnum; /* Has to be global. */
67 int GlobalLevelOfDetail_Hierarchical;
68 
69 STRATEGYBLOCK *Global_HModel_Sptr;
70 HMODELCONTROLLER *Global_Controller_Ptr;
71 static DISPLAYBLOCK *Global_HModel_DispPtr;
72 
73 DAMAGEBLOCK Default_Damageblock = {
74 	5,	/* Health */
75 	0,		/* Armour */
76 	0, /* IsOnFire */
77 	{
78 		0,	/* Acid Resistant */
79 		0,	/* Fire Resistant */
80 		0,	/* Electric Resistant */
81 		0,	/* Perfect Armour */
82 		0,	/* Electric Sensitive */
83 		0,	/* Combustability */
84 		0,	/* Indestructable */
85 	},
86 };
87 
88 MATRIXCH Identity_RotMat = {
89 	ONE_FIXED,0,0,
90 	0,ONE_FIXED,0,
91 	0,0,ONE_FIXED,
92 };
93 
QNormalise(QUAT * q)94 void QNormalise(QUAT *q)
95 {
96 	float nw = q->quatw;
97 	float nx = q->quatx;
98 	float ny = q->quaty;
99 	float nz = q->quatz;
100 
101 	float m = sqrt(nw*nw+nx*nx+ny*ny+nz*nz);
102 
103 	if (!m) return;
104 
105 	m = 65536.0/m;
106 
107 	f2i(q->quatw,nw * m);
108 	f2i(q->quatx,nx * m);
109 	f2i(q->quaty,ny * m);
110 	f2i(q->quatz,nz * m);
111 }
112 
113 
GetSequenceID(int sequence_type,int sub_sequence)114 int GetSequenceID(int sequence_type,int sub_sequence) {
115 	return( (sub_sequence<<16)+sequence_type);
116 }
117 
GetSequencePointer(int sequence_type,int sub_sequence,SECTION * this_section)118 SEQUENCE *GetSequencePointer(int sequence_type,int sub_sequence,SECTION *this_section) {
119 
120 	int sequence_id,a;
121 	SEQUENCE *sequence_pointer;
122 
123 	sequence_id=GetSequenceID(sequence_type,sub_sequence);
124 
125 	for (a=0; a<this_section->num_sequences; a++) {
126 		if (this_section->sequence_array[a].sequence_id==sequence_id) {
127 			sequence_pointer=&(this_section->sequence_array[a]);
128 			break;
129 		}
130 	}
131 
132 	if (a==this_section->num_sequences) {
133 		//textprint("Unknown HModel sequence! %d,%d\n",sequence_type,sub_sequence);
134 		return(&(this_section->sequence_array[0]));
135 		//GLOBALASSERT(0);
136 		//return(NULL);
137 	}
138 
139 	return(sequence_pointer);
140 
141 }
142 #if 0
143 void Preprocess_Keyframe(KEYFRAME_DATA *this_keyframe, KEYFRAME_DATA *next_keyframe,int one) {
144 {
145 
146 	New_Preprocess_Keyframe(this_keyframe,next_keyframe,one);
147 }
148 #endif
QDot(QUAT * this_quat,QUAT * next_quat)149 int QDot(QUAT *this_quat, QUAT *next_quat) {
150 
151 	int qdot;
152 
153 	qdot=( (MUL_FIXED(this_quat->quatx,next_quat->quatx)) +
154 		 	(MUL_FIXED(this_quat->quaty,next_quat->quaty)) +
155 		 	(MUL_FIXED(this_quat->quatz,next_quat->quatz)) +
156 		 	(MUL_FIXED(this_quat->quatw,next_quat->quatw)) );
157 
158 	return(qdot);
159 }
160 
New_Preprocess_Keyframe(KEYFRAME_DATA * this_keyframe,KEYFRAME_DATA * next_keyframe,int one)161 void New_Preprocess_Keyframe(KEYFRAME_DATA *this_keyframe, KEYFRAME_DATA *next_keyframe,int one) {
162 
163 	int cosom;
164 	QUAT_SHORT *this_quat, *next_quat;
165 
166 	LOCALASSERT(this_keyframe);
167 	LOCALASSERT(next_keyframe);
168 
169 	this_quat=&this_keyframe->QOrient;
170 	next_quat=&next_keyframe->QOrient;
171 
172 	//QNormalise(this_quat);
173 	//QNormalise(next_quat);
174 
175 
176 	//cosom=QDot(this_quat,next_quat);
177 	cosom=( (MUL_FIXED((int)this_quat->quatx,(int)next_quat->quatx)) +
178 		 	(MUL_FIXED((int)this_quat->quaty,(int)next_quat->quaty)) +
179 		 	(MUL_FIXED((int)this_quat->quatz,(int)next_quat->quatz)) +
180 		 	(MUL_FIXED((int)this_quat->quatw,(int)next_quat->quatw)))<<1;
181 
182 	if (cosom<0) {
183 		this_keyframe->slerp_to_negative_quat=1;
184 		cosom=-cosom;
185 	}
186 	else{
187 		this_keyframe->slerp_to_negative_quat=0;
188 	}
189 
190 
191 	this_keyframe->omega=(unsigned short)ArcCos(cosom);
192 
193 	GLOBALASSERT(this_keyframe->Sequence_Length>0);
194 	this_keyframe->oneoversequencelength=DIV_FIXED(one,(int)this_keyframe->Sequence_Length);
195 
196 }
197 
Preprocess_Section(SECTION * this_section,char * riffname,VECTORCH * offset_to_return)198 void Preprocess_Section(SECTION *this_section, char *riffname ,VECTORCH* offset_to_return) {
199 
200 	KEYFRAME_DATA *this_keyframe;
201 	VECTORCH  cumulative_gore_direction;
202 	VECTORCH  next_one_down;
203 	SECTION_ATTACHMENT *sec_att;
204 	int a;
205 
206 	if (this_section->ShapeName!=NULL) {
207 		this_section->ShapeNum=GetLoadedShapeMSL(this_section->ShapeName);
208 		if (this_section->ShapeNum==-1) {
209 			textprint("\n\n\n\nShape has a name! %s\n",this_section->ShapeName);
210 			GLOBALASSERT(0);
211 		}
212 		this_section->Shape=GetShapeData(this_section->ShapeNum);
213 	} else {
214 		/* Do nothing, as requested by John. */
215 	}
216 
217 	/* Get section attachment... */
218 
219 	sec_att=GetThisSectionAttachment(riffname,this_section->Section_Name,this_section->Hierarchy_Name);
220 	if (sec_att==NULL) {
221 		/* Try for the general one. */
222 		sec_att=GetThisSectionAttachment(riffname,this_section->Section_Name,NULL);
223 	}
224 
225 	if (sec_att) {
226 		this_section->StartingStats=sec_att->StartingStats;
227 		this_section->flags&=~section_is_master_root;
228 		this_section->flags|=sec_att->flags;
229 	} else {
230 		/* Default! */
231 		this_section->StartingStats=Default_Stats.StartingStats;
232 		this_section->flags|=Default_Stats.flags;
233 	}
234 
235 	/* Now ID number. */
236 
237 	this_section->IDnumber=incIDnum;
238 	incIDnum++;
239 
240 	/* Okay. */
241 
242 	for (a=0; a<this_section->num_sequences; a++) {
243 
244 		int one=0;
245 
246 		/* Pass through to find sequence length. */
247 
248 		this_keyframe=this_section->sequence_array[a].first_frame;
249 
250 		while(1){
251 
252 			one+=this_keyframe->Sequence_Length;
253 
254 			/*See if we have got to the end*/
255 			if(this_keyframe->last_frame) break;
256 			/* Next frame */
257 			this_keyframe=this_keyframe->Next_Frame;
258 
259 		}
260 
261 		/* Second pass. */
262 
263 		this_keyframe=this_section->sequence_array[a].first_frame;
264 
265 		while(1) {
266 
267 			New_Preprocess_Keyframe(this_keyframe, this_keyframe->Next_Frame,one);
268 
269 
270 			/*See if we have got to the end*/
271 			if(this_keyframe->last_frame) break;
272 			/* Next frame */
273 			this_keyframe=this_keyframe->Next_Frame;
274 
275 		}
276 
277 	}
278 
279 	cumulative_gore_direction.vx=0;
280 	cumulative_gore_direction.vy=0;
281 	cumulative_gore_direction.vz=0;
282 
283 	/* Now call recursion... */
284 
285 	if (this_section->Children!=NULL) {
286 		SECTION **child_list_ptr;
287 
288 		child_list_ptr=this_section->Children;
289 
290 		while (*child_list_ptr!=NULL) {
291 			Preprocess_Section(*child_list_ptr,riffname,&next_one_down);
292 			child_list_ptr++;
293 
294 			cumulative_gore_direction.vx+=next_one_down.vx;
295 			cumulative_gore_direction.vy+=next_one_down.vy;
296 			cumulative_gore_direction.vz+=next_one_down.vz;
297 		}
298 	}
299 
300 	if ( (cumulative_gore_direction.vx==0)
301 		&& (cumulative_gore_direction.vy==0)
302 		&& (cumulative_gore_direction.vz==0) ) {
303 		/* Darn.  Have a default. */
304 		cumulative_gore_direction.vx=4096;
305 	}
306 
307 	cumulative_gore_direction.vx=-cumulative_gore_direction.vx;
308 	cumulative_gore_direction.vy=-cumulative_gore_direction.vy;
309 	cumulative_gore_direction.vz=-cumulative_gore_direction.vz;
310 
311 	Normalise(&cumulative_gore_direction);
312 
313 	this_section->gore_spray_direction=cumulative_gore_direction;
314 
315 	if(offset_to_return)
316 	{
317 		GetKeyFrameOffset(this_section->sequence_array[0].first_frame,offset_to_return);
318 	}
319 
320 }
321 
Preprocess_HModel(SECTION * root,char * riffname)322 void Preprocess_HModel(SECTION *root, char *riffname) {
323 	/* One-time preprocessor, prerocesses the 'deltas' for all sequences.  */
324 
325 	GLOBALASSERT(root); /* Stop messin' about... */
326 
327 	incIDnum=0;
328 	/* Root can't spray gore... */
329 	Preprocess_Section(root,riffname,0);
330 	root->gore_spray_direction.vx=0;
331 	root->gore_spray_direction.vy=0;
332 	root->gore_spray_direction.vz=0;
333 
334 	/* Shouldn't this be set anyway? */
335 	root->flags|=section_is_master_root;
336 
337 }
338 
Setup_Texture_Animation_For_Section(SECTION_DATA * this_section_data)339 void Setup_Texture_Animation_For_Section(SECTION_DATA *this_section_data)
340 {
341 	GLOBALASSERT(this_section_data);
342 
343 	if(this_section_data->tac_ptr)
344 	{
345 		//get rid of old animation control blocks
346 		TXACTRLBLK *tac_next;
347 		tac_next=this_section_data->tac_ptr;
348 		while (tac_next) {
349 			TXACTRLBLK *tac_temp;
350 
351 			tac_temp=tac_next->tac_next;
352 			DeallocateMem(tac_next);
353 			tac_next=tac_temp;
354 
355 		}
356 		this_section_data->tac_ptr=0;
357 	}
358 
359 	if (this_section_data->Shape) {
360 		if (this_section_data->Shape->shapeflags & ShapeFlag_HasTextureAnimation) {
361 
362 			TXACTRLBLK **pptxactrlblk;
363 			int item_num;
364 			int shape_num = this_section_data->ShapeNum;
365 			SHAPEHEADER *shptr = GetShapeData(shape_num);
366 			pptxactrlblk = &this_section_data->tac_ptr;
367 			for(item_num = 0; item_num < shptr->numitems; item_num ++)
368 			{
369 				POLYHEADER *poly =  (POLYHEADER*)(shptr->items[item_num]);
370 				LOCALASSERT(poly);
371 
372 				SetupPolygonFlagAccessForShape(shptr);
373 
374 				if((Request_PolyFlags((void *)poly)) & iflag_txanim)
375 					{
376 						TXACTRLBLK *pnew_txactrlblk;
377 
378 						pnew_txactrlblk = AllocateMem(sizeof(TXACTRLBLK));
379 						if(pnew_txactrlblk)
380 						{
381 							pnew_txactrlblk->tac_flags = 0;
382 							pnew_txactrlblk->tac_item = item_num;
383 							pnew_txactrlblk->tac_sequence = 0;
384 							pnew_txactrlblk->tac_node = 0;
385 							pnew_txactrlblk->tac_txarray = GetTxAnimArrayZ(shape_num, item_num);
386 							pnew_txactrlblk->tac_txah_s = GetTxAnimHeaderFromShape(pnew_txactrlblk, shape_num);
387 
388 							*pptxactrlblk = pnew_txactrlblk;
389 							pptxactrlblk = &pnew_txactrlblk->tac_next;
390 						}
391 						else *pptxactrlblk = NULL;
392 					}
393 			}
394 			*pptxactrlblk=0;
395 		}
396 	}
397 }
398 
Create_New_Section(SECTION * this_section)399 SECTION_DATA *Create_New_Section(SECTION *this_section) {
400 
401 	SECTION_DATA *this_section_data;
402 	int num_children;
403 
404 	if (this_section->ShapeName!=NULL) {
405 		this_section->ShapeNum=GetLoadedShapeMSL(this_section->ShapeName);
406 		if (this_section->ShapeNum==-1) {
407 			GLOBALASSERT(0);
408 		}
409 		this_section->Shape=GetShapeData(this_section->ShapeNum);
410 	} else {
411 		/* Do nothing, as requested by John. */
412 	}
413 
414 	/* Create SECTION_DATA. */
415 
416 	this_section_data=(SECTION_DATA *)AllocateMem(sizeof(SECTION_DATA));
417 	GLOBALASSERT(this_section_data);
418 
419 	this_section_data->sac_ptr=NULL;
420 	this_section_data->tac_ptr=NULL;
421 	this_section_data->sempai=this_section;
422 
423 	this_section_data->current_damage.Health=this_section_data->sempai->StartingStats.Health<<16;
424 	this_section_data->current_damage.Armour=this_section_data->sempai->StartingStats.Armour<<16;
425 	this_section_data->current_damage.SB_H_flags=this_section_data->sempai->StartingStats.SB_H_flags;
426 
427 	if (this_section_data->current_damage.Health<=0) {
428 		/* Wrong! */
429 
430 		this_section_data->current_damage=Default_Damageblock;
431 	}
432 
433 	this_section_data->my_controller=Global_Controller_Ptr;
434 	/* Note not initialised! */
435 	this_section_data->flags=0;
436 
437 	/* KJL 17:04:41 31/07/98 - Decal support */
438 	this_section_data->NumberOfDecals = 0;
439 	this_section_data->NextDecalToUse = 0;
440 
441 
442 	this_section_data->ShapeNum=this_section->ShapeNum;
443 	this_section_data->Shape=this_section->Shape;
444 
445 	/* This just so it's not uninitialised. */
446 	this_section_data->Prev_Sibling=NULL;
447 	this_section_data->My_Parent=NULL;
448 	this_section_data->Next_Sibling=NULL;
449 
450 	this_section_data->replacement_id=0;
451 
452 	/* Init shape animations... */
453 
454 	#if AUTODETECT
455 	if (this_section->Shape) {
456 		if (this_section->Shape->animation_header) {
457 	#else
458 		if (this_section->flags&section_has_shape_animation) {
459 	#endif
460 
461 			GLOBALASSERT(this_section->Shape->animation_header);
462 
463 			this_section_data->sac_ptr=AllocateMem(sizeof(SHAPEANIMATIONCONTROLLER));
464 
465 			InitShapeAnimationController (this_section_data->sac_ptr, this_section->Shape);
466 
467 		}
468 	#if AUTODETECT
469 	}
470 	#endif
471 
472 	/* Init texture animations. */
473 	this_section_data->tac_ptr=0;
474 	Setup_Texture_Animation_For_Section(this_section_data);
475 
476 	/* Now call recursion... */
477 
478 	num_children=0;
479 
480 	if (this_section->Children!=NULL) {
481 		SECTION **child_list_ptr;
482 		SECTION_DATA *new_child_list_ptr;
483 
484 		SECTION_DATA *last_child,*first_child;
485 
486 		last_child=NULL;
487 		first_child=NULL;
488 
489 		/* Create subsections. */
490 
491 		child_list_ptr=this_section->Children;
492 
493 		while (*child_list_ptr!=NULL) {
494 
495 			(new_child_list_ptr)=Create_New_Section(*child_list_ptr);
496 
497 			(new_child_list_ptr)->Prev_Sibling=last_child;
498 			(new_child_list_ptr)->My_Parent=this_section_data;
499 			(new_child_list_ptr)->Next_Sibling=NULL; /* For now... */
500 
501 			if (first_child==NULL) {
502 				first_child=new_child_list_ptr;
503 			}
504 
505 			if (last_child) {
506 				last_child->Next_Sibling=(new_child_list_ptr);
507 			}
508 			last_child=(new_child_list_ptr);
509 
510 			child_list_ptr++;
511 		}
512 		(new_child_list_ptr)=NULL;
513 
514 		this_section_data->First_Child=first_child;
515 	} else {
516 		this_section_data->First_Child=NULL;
517 	}
518 
519 	return(this_section_data);
520 
521 }
522 
523 void Create_HModel(HMODELCONTROLLER *controller,SECTION *root) {
524 
525 	/* Connects sequence to controller and must generate
526 	the list of section_data structures. */
527 
528 	GLOBALASSERT(root); /* Stop messin' about... */
529 
530 	Global_Controller_Ptr=controller;
531 
532 	controller->Root_Section=root; /* That's a given. */
533 	controller->Seconds_For_Sequence=ONE_FIXED;
534 	controller->timer_increment=ONE_FIXED;
535 	/* Seconds_For_Sequence and timer_increment are dealt with elsewhere. */
536 	controller->sequence_timer=0;
537 
538 	controller->FrameStamp=-1;
539 	controller->View_FrameStamp=-1;
540 	controller->Computed_Position.vx=0;
541 	controller->Computed_Position.vy=0;
542 	controller->Computed_Position.vz=0;
543 
544 	controller->Playing=0;
545 	controller->Reversed=0;
546 	controller->Looped=0;
547 
548 	controller->After_Tweening_Sequence_Type=-1;
549 	controller->After_Tweening_Sub_Sequence=-1;
550 	controller->AT_seconds_for_sequence=ONE_FIXED;
551 	controller->AT_sequence_timer=0;
552 	controller->Tweening=Controller_NoTweening;
553 	controller->LoopAfterTweening=0;
554 	controller->StopAfterTweening=0;
555 
556 	controller->DisableBleeding=0;
557 	controller->LockTopSection=0;
558 	controller->ZeroRootDisplacement=0;
559 	controller->ZeroRootRotation=0;
560 	controller->DisableSounds=0;
561 
562 	/* Controller elevation now removed.  All done through delta sequences, 8/4/98. */
563 	controller->ElevationTweening=0;
564 
565 	controller->Deltas=NULL;
566 
567 	/* Every time a section is preprocessed, it must generate a section_data for
568 	itself, and clip it to the last section_data that was generated. */
569 
570 	controller->section_data=Create_New_Section(controller->Root_Section);
571 
572 	controller->section_data->Prev_Sibling=NULL;
573 	controller->section_data->My_Parent=NULL;
574 	controller->section_data->Next_Sibling=NULL;
575 
576 	if (root->flags&section_is_master_root) {
577 		controller->section_data->flags|=section_data_master_root;
578 	}
579 
580 }
581 
582 void Destructor_Recursion(SECTION_DATA *doomed_section_data) {
583 
584 	/* Remove other bits. */
585 
586 	if (doomed_section_data->sac_ptr) {
587 		DeallocateMem(doomed_section_data->sac_ptr);
588 	}
589 
590 	if (doomed_section_data->tac_ptr) {
591 		TXACTRLBLK *tac_next;
592 		tac_next=doomed_section_data->tac_ptr;
593 		while (tac_next) {
594 			TXACTRLBLK *tac_temp;
595 
596 			tac_temp=tac_next->tac_next;
597 			DeallocateMem(tac_next);
598 			tac_next=tac_temp;
599 
600 		}
601 	}
602 
603 	/* Recurse. */
604 
605 	if (doomed_section_data->First_Child!=NULL) {
606 
607 		SECTION_DATA *child_list_ptr;
608 
609 		child_list_ptr=doomed_section_data->First_Child;
610 
611 		while (child_list_ptr!=NULL) {
612 			/* Remove each child... */
613 			/* JH - 19/2/98 store the next sibling so that we don't access dealloced memory */
614 			SECTION_DATA * next_sibling_ptr = child_list_ptr->Next_Sibling;
615 			Destructor_Recursion(child_list_ptr);
616 			child_list_ptr=next_sibling_ptr;
617 		}
618 
619 	}
620 
621 	/* Now remove the section... */
622 
623 	DeallocateMem(doomed_section_data);
624 
625 }
626 
627 void Prune_Section(SECTION_DATA *doomed_section_data) {
628 
629 	GLOBALASSERT(doomed_section_data);
630 	/* Destroys section, and all children. */
631 
632 	if (doomed_section_data->Prev_Sibling) {
633 		GLOBALASSERT(doomed_section_data->Prev_Sibling->Next_Sibling==doomed_section_data);
634 		doomed_section_data->Prev_Sibling->Next_Sibling=doomed_section_data->Next_Sibling;
635 	}
636 
637 	if (doomed_section_data->Next_Sibling) {
638 		GLOBALASSERT(doomed_section_data->Next_Sibling->Prev_Sibling==doomed_section_data);
639 		doomed_section_data->Next_Sibling->Prev_Sibling=doomed_section_data->Prev_Sibling;
640 	}
641 
642 	if (doomed_section_data->My_Parent) {
643 		if (doomed_section_data->My_Parent->First_Child==doomed_section_data) {
644 			doomed_section_data->My_Parent->First_Child=doomed_section_data->Next_Sibling;
645 		}
646 	}
647 
648 	/* Now destroy. */
649 	Destructor_Recursion(doomed_section_data);
650 
651 }
652 
653 void Delete_Deltas_Recursion(DELTA_CONTROLLER *delta_controller) {
654 
655 	if (delta_controller==NULL) {
656 		return;
657 	}
658 
659 	if (delta_controller->next_controller) {
660 		Delete_Deltas_Recursion(delta_controller->next_controller);
661 	}
662 
663 	DeallocateMem(delta_controller->id);
664 	DeallocateMem(delta_controller);
665 
666 }
667 
668 void Dispel_HModel(HMODELCONTROLLER *controller) {
669 
670 	/* For getting rid of the section_data. */
671 
672 	if (controller->section_data!=NULL) {
673 
674 		Destructor_Recursion(controller->section_data);
675 
676 		controller->section_data=NULL;
677 
678 	}
679 
680 	Delete_Deltas_Recursion(controller->Deltas);
681 
682 }
683 
684 void RemoveAllDeltas(HMODELCONTROLLER *controller) {
685 
686 	/* Pretty self explainatory. */
687 	GLOBALASSERT(controller);
688 
689 	Delete_Deltas_Recursion(controller->Deltas);
690 	controller->Deltas=NULL;
691 
692 }
693 
694 /* SetElevationDeltas removed, 8.4.98. CDF. */
695 
696 void Process_Delta_Controller(SECTION_DATA *this_section_data,DELTA_CONTROLLER *delta_controller,VECTORCH *output_offset,QUAT *output_quat) {
697 
698 	SEQUENCE *delta_sequence;
699 	int a;
700 	int working_timer,lerp,lastframe_working_timer;
701 	KEYFRAME_DATA *this_frame,*next_frame;
702 	int sequence_type,sub_sequence,timer;
703 
704 	if (delta_controller==NULL) {
705 		return;
706 	}
707 
708 	sequence_type=delta_controller->sequence_type;
709 	sub_sequence=delta_controller->sub_sequence;
710 	timer=delta_controller->timer;
711 
712 	GLOBALASSERT(sequence_type>-1);
713 	GLOBALASSERT(sub_sequence>-1);
714 
715 	delta_sequence=GetSequencePointer(sequence_type,sub_sequence,this_section_data->sempai);
716 
717 	GLOBALASSERT(delta_sequence);
718 
719 	/* Final Frame Correction. */
720 
721 	this_frame=delta_sequence->first_frame;
722 
723 	a=0;
724 
725 	while (!this_frame->last_frame) {
726 		a+=this_frame->Sequence_Length;
727 		this_frame=this_frame->Next_Frame;
728 	}
729 
730 	/* Now a is the 'real' sequence length. */
731 
732 	working_timer=MUL_FIXED(timer,a);
733 
734 	lastframe_working_timer=delta_controller->lastframe_timer;
735 	lastframe_working_timer=MUL_FIXED(lastframe_working_timer,a);
736 
737 	/* Now do that thing. */
738 
739 	this_frame=delta_sequence->first_frame;
740 
741 	a=0; /* Status flag... */
742 
743 	while (a==0) {
744 		next_frame=this_frame->Next_Frame;
745 		if (working_timer>=this_frame->Sequence_Length) {
746 			/* We've gone beyond this frame: get next keyframe. */
747 			working_timer-=this_frame->Sequence_Length;
748 			lastframe_working_timer-=this_frame->Sequence_Length;
749 
750 			/* Have we looped? */
751 			if (this_frame->last_frame) {
752 				/* Some deltas are really fast. */
753 				this_frame=delta_sequence->first_frame;
754 			}
755 			else{
756 				/* Advance frame... */
757 				this_frame=next_frame;
758 			}
759 
760 			if (lastframe_working_timer<=0) {
761 				if(this_frame->frame_has_extended_data)
762 				{
763 					KEYFRAME_DATA_EXTENDED* this_frame_extended=(KEYFRAME_DATA_EXTENDED*) this_frame;
764 					/* Check flags... */
765 					this_section_data->my_controller->keyframe_flags|=this_frame_extended->flags;
766 					/* ...And keyframe sounds... */
767 					if (this_frame_extended->sound) {
768 						if (this_section_data->my_controller->DisableSounds==0) {
769 							PlayHierarchySound(this_frame_extended->sound,&this_section_data->Last_World_Offset);
770 						}
771 					}
772 				}
773 			}
774 
775 		} else {
776 			a=1; /* Exit loop with success. */
777 		}
778 		/* Better make sure the last 'frame' has 65536 length... */
779 	}
780 
781 	/* Now, this_frame and next_frame are set, and working_timer<this_frame->Sequence_Length. */
782 	lerp=MUL_FIXED(working_timer,this_frame->oneoversequencelength);
783 
784 	GetKeyFrameOffset(this_frame,output_offset);
785 	if(next_frame->shift_offset)
786 	{
787 		VECTORCH next_offset;
788 		GetKeyFrameOffset(next_frame,&next_offset);
789 		output_offset->vx+=MUL_FIXED(next_offset.vx - output_offset->vx,lerp);
790 		output_offset->vy+=MUL_FIXED(next_offset.vy - output_offset->vy,lerp);
791 		output_offset->vz+=MUL_FIXED(next_offset.vz - output_offset->vz,lerp);
792 	}
793 	else
794 	{
795 		output_offset->vx+=MUL_FIXED((int)next_frame->Offset_x - output_offset->vx,lerp);
796 		output_offset->vy+=MUL_FIXED((int)next_frame->Offset_y - output_offset->vy,lerp);
797 		output_offset->vz+=MUL_FIXED((int)next_frame->Offset_z - output_offset->vz,lerp);
798 	}
799 
800 
801 	/* Now deal with orientation. */
802 
803 
804 	Slerp(this_frame,lerp,output_quat);
805 
806 	delta_controller->lastframe_timer=delta_controller->timer;
807 
808 }
809 
810 void Handle_Section_Timer(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,KEYFRAME_DATA *input_frame,int base_timer, int *working_timer) {
811 
812 	KEYFRAME_DATA *this_frame,*next_frame;
813 	int a;
814 
815 	if (this_section_data->freezeframe_timer!=-1) {
816 		(*working_timer)=this_section_data->freezeframe_timer;
817 	} else {
818 		(*working_timer)=base_timer;
819 	}
820 
821 	if (this_section_data->accumulated_timer>(*working_timer)) {
822 		/* We must have looped. Or be reversed.... ! */
823 		this_section_data->accumulated_timer=0;
824 		this_section_data->current_keyframe=input_frame;
825 		if(input_frame->frame_has_extended_data)
826 		{
827 			/* Check start flags... */
828 			KEYFRAME_DATA_EXTENDED* input_frame_extended=(KEYFRAME_DATA_EXTENDED*) input_frame;
829 			controller->keyframe_flags|=input_frame_extended->flags;
830 			/* And Keyframe Sounds. */
831 			if (input_frame_extended->sound) {
832 				if (controller->DisableSounds==0) {
833 					PlayHierarchySound(input_frame_extended->sound,&this_section_data->World_Offset);
834 				}
835 			}
836 		}
837 	}
838 
839 	this_frame=this_section_data->current_keyframe;
840 	(*working_timer)-=this_section_data->accumulated_timer;
841 
842 	/* Now, a lot like the old way, but we shouldn't need to loop more than once. */
843 	/* If we do, we have a game framerate slower than the anim framerate. */
844 
845 	a=0; /* Status flag... */
846 
847 	while (a==0) {
848 		if (this_frame==NULL) {
849 			this_frame=input_frame; // Heaven help us.
850 		}
851 		if (this_frame->last_frame) {
852 			/* Low framerate loop? */
853 			next_frame=input_frame;
854 		}
855 		else{
856 			next_frame=this_frame->Next_Frame;
857 		}
858 
859 		if ((*working_timer)>=this_frame->Sequence_Length) {
860 			/* We've gone beyond this frame: get next keyframe. */
861 			(*working_timer)-=this_frame->Sequence_Length;
862 			/* Add sequence length to accumulated_timer. */
863 			this_section_data->accumulated_timer+=this_frame->Sequence_Length;
864 			/* Advance frame... */
865 			this_frame=next_frame;
866 			if (controller->Reversed==0) {
867 				if(this_frame->frame_has_extended_data)
868 				{
869 					KEYFRAME_DATA_EXTENDED* this_frame_extended=(KEYFRAME_DATA_EXTENDED*) this_frame;
870 					/* Check flags... */
871 					controller->keyframe_flags|=this_frame_extended->flags;
872 					/* ...And keyframe sounds... */
873 					if (this_frame_extended->sound) {
874 						if (controller->DisableSounds==0) {
875 							PlayHierarchySound(this_frame_extended->sound,&this_section_data->World_Offset);
876 						}
877 					}
878 				}
879 			}
880 			/* Update current keyframe. */
881 			this_section_data->current_keyframe=this_frame;
882 		} else {
883 			a=1; /* Exit loop with success. */
884 		}
885 		/* Better make sure the last 'frame' has 65536 length... */
886 	}
887 
888 	if (controller->Reversed) {
889 		/* Okay, maybe a bit cheesy.  Trigger flags and sounds... */
890 		KEYFRAME_DATA *cthis_frame,*cnext_frame;
891 		int b,rev_working_timer;
892 
893 		rev_working_timer=this_section_data->lastframe_timer-this_section_data->accumulated_timer;
894 		cthis_frame=this_frame;
895 		b=0;
896 
897 		while (b==0) {
898 			if (cthis_frame==NULL) {
899 				cthis_frame=input_frame; // Heaven help us.
900 			}
901 			if (cthis_frame->last_frame) {
902 				/* Low framerate loop? */
903 				cnext_frame=input_frame;
904 			}
905 			else{
906 				cnext_frame=cthis_frame->Next_Frame;
907 			}
908 			if (rev_working_timer>=cthis_frame->Sequence_Length) {
909 				/* We've gone beyond this frame: get next keyframe. */
910 				rev_working_timer-=cthis_frame->Sequence_Length;
911 				/* Advance frame... */
912 				cthis_frame=cnext_frame;
913 				/* Check flags... */
914 				if(this_frame->frame_has_extended_data)
915 				{
916 					KEYFRAME_DATA_EXTENDED* this_frame_extended=(KEYFRAME_DATA_EXTENDED*) this_frame;
917 					controller->keyframe_flags|=this_frame_extended->flags;
918 					/* ...And keyframe sounds... */
919 					if (this_frame_extended->sound) {
920 						if (controller->DisableSounds==0) {
921 							PlayHierarchySound(this_frame_extended->sound,&this_section_data->World_Offset);
922 						}
923 					}
924 				}
925 			} else {
926 				b=1; /* Exit loop with success. */
927 			}
928 			/* Better make sure the last 'frame' has 65536 length... */
929 		}
930 
931 	}
932 
933 	/* Check for looping? */
934 	if (this_frame->last_frame) {
935 		if (controller->Looped==0) {
936 			/* Stop at last frame. */
937 			(*working_timer)=0;
938 		}
939 	}
940 
941 	this_section_data->lastframe_timer=base_timer;
942 }
943 
944 void New_Analyse_Keyframe_Data(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,KEYFRAME_DATA *input_frame,int base_timer,VECTORCH *output_offset,MATRIXCH *output_matrix) {
945 
946 	KEYFRAME_DATA *this_frame;
947 	QUAT output_quat;
948 	int working_timer,lerp;
949 
950 	/* This *should* never fire, should it? */
951 
952 	GLOBALASSERT(input_frame);
953 
954 	/* Check the input is in a sensible place. */
955 	#if 0 //this can't occur anymore with the new way of storing offsets
956 	if ( !(	(input_frame->Offset.vx<1000000 && input_frame->Offset.vx>-1000000)
957 		 &&	(input_frame->Offset.vy<1000000 && input_frame->Offset.vy>-1000000)
958 		 &&	(input_frame->Offset.vz<1000000 && input_frame->Offset.vz>-1000000)
959 		 ) ) {
960 
961 		LOGDXFMT(("First Tests in NEW_ANALYSE_KEYFRAME_DATA.\n"));
962 		if (Global_HModel_Sptr) {
963 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
964 			if (Global_HModel_Sptr->SBdptr) {
965 				LOGDXFMT(("Object is Near.\n"));
966 			} else {
967 				LOGDXFMT(("Object is Far.\n"));
968 			}
969 		} else {
970 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
971 		}
972 		LOGDXFMT(("Name of section: %s\n",this_section_data->sempai->Section_Name));
973 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
974 		LOGDXFMT(("Sequence Timer = %d\n",controller->sequence_timer));
975 		LOGDXFMT(("Tweening flags %d\n",controller->Tweening));
976 
977 		if (this_section_data->My_Parent) {
978 			LOGDXFMT(("Parent Position %d,%d,%d\n",this_section_data->My_Parent->World_Offset.vx,this_section_data->My_Parent->World_Offset.vy,this_section_data->My_Parent->World_Offset.vz));
979 		} else {
980 			LOGDXFMT(("No parent.\n"));
981 		}
982 		LOGDXFMT(("This Position %d,%d,%d\n",this_section_data->World_Offset.vx,this_section_data->World_Offset.vy,this_section_data->World_Offset.vz));
983 		LOGDXFMT(("This Keyframe Position %d,%d,%d\n",input_frame->Offset.vx,input_frame->Offset.vy,input_frame->Offset.vz));
984 
985 		LOCALASSERT(input_frame->Offset.vx<1000000 && input_frame->Offset.vx>-1000000);
986 		LOCALASSERT(input_frame->Offset.vy<1000000 && input_frame->Offset.vy>-1000000);
987 		LOCALASSERT(input_frame->Offset.vz<1000000 && input_frame->Offset.vz>-1000000);
988 
989 	}
990 	#endif
991 
992 	/* First find the current frame. */
993 
994 	if (input_frame->last_frame) {
995 		/* This is rigid. */
996 		GetKeyFrameOffset(input_frame,output_offset);
997 
998 		CopyShortQuatToInt(&input_frame->QOrient,&output_quat);
999 
1000 		/* Elevation gone! Now deltas? */
1001 
1002 		{
1003 			DELTA_CONTROLLER *dcon;
1004 			QUAT elevation_quat,temp_quat;
1005 			VECTORCH elevation_offset;
1006 
1007 			dcon=controller->Deltas;
1008 
1009 			while (dcon) {
1010 				if (dcon->Active) {
1011 					Process_Delta_Controller(this_section_data,dcon,&elevation_offset,&elevation_quat);
1012 					output_offset->vx+=elevation_offset.vx;
1013 					output_offset->vy+=elevation_offset.vy;
1014 					output_offset->vz+=elevation_offset.vz;
1015 
1016 					temp_quat.quatw=output_quat.quatw;
1017 					temp_quat.quatx=output_quat.quatx;
1018 					temp_quat.quaty=output_quat.quaty;
1019 					temp_quat.quatz=output_quat.quatz;
1020 
1021 					MulQuat(&elevation_quat,&temp_quat,&output_quat);
1022 				}
1023 				dcon=dcon->next_controller;
1024 			}
1025 		}
1026 
1027 		QuatToMat(&output_quat,output_matrix);
1028 
1029 		/* Check the output is in a sensible place. */
1030 		if ( !(	(output_offset->vx<1000000 && output_offset->vx>-1000000)
1031 			 &&	(output_offset->vy<1000000 && output_offset->vy>-1000000)
1032 			 &&	(output_offset->vz<1000000 && output_offset->vz>-1000000)
1033 			 ) ) {
1034 			VECTORCH frame_offset;
1035 			GetKeyFrameOffset(input_frame,&frame_offset);
1036 
1037 			LOGDXFMT(("Second Tests in NEW_ANALYSE_KEYFRAME_DATA.\n"));
1038 			if (Global_HModel_Sptr) {
1039 				LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
1040 				if (Global_HModel_Sptr->SBdptr) {
1041 					LOGDXFMT(("Object is Near.\n"));
1042 				} else {
1043 					LOGDXFMT(("Object is Far.\n"));
1044 				}
1045 			} else {
1046 				LOGDXFMT(("Misplaced object has no SBptr.\n"));
1047 			}
1048 			LOGDXFMT(("Name of section: %s\n",this_section_data->sempai->Section_Name));
1049 			LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
1050 			LOGDXFMT(("Sequence Timer = %d\n",controller->sequence_timer));
1051 			LOGDXFMT(("Tweening flags %d\n",controller->Tweening));
1052 
1053 			if (this_section_data->My_Parent) {
1054 				LOGDXFMT(("Parent Position %d,%d,%d\n",this_section_data->My_Parent->World_Offset.vx,this_section_data->My_Parent->World_Offset.vy,this_section_data->My_Parent->World_Offset.vz));
1055 				} else {
1056 				LOGDXFMT(("No parent.\n"));
1057 			}
1058 			LOGDXFMT(("This Position %d,%d,%d\n",this_section_data->World_Offset.vx,this_section_data->World_Offset.vy,this_section_data->World_Offset.vz));
1059 			LOGDXFMT(("This Keyframe Position %d,%d,%d\n",frame_offset.vx,frame_offset.vy,frame_offset.vz));
1060 			LOGDXFMT(("Output Offset %d,%d,%d\n",output_offset->vx,output_offset->vy,output_offset->vz));
1061 
1062 			LOCALASSERT(output_offset->vx<1000000 && output_offset->vx>-1000000);
1063 			LOCALASSERT(output_offset->vy<1000000 && output_offset->vy>-1000000);
1064 			LOCALASSERT(output_offset->vz<1000000 && output_offset->vz>-1000000);
1065 
1066 		}
1067 
1068 		return;
1069 	}
1070 
1071 	/* New way. */
1072 
1073 	Handle_Section_Timer(controller,this_section_data,input_frame,base_timer,&working_timer);
1074 	this_frame=this_section_data->current_keyframe;
1075 
1076 	/* Now, this_frame and next_frame are set, and working_timer<this_frame->Sequence_Length. */
1077 
1078 
1079 	lerp=MUL_FIXED(working_timer,this_frame->oneoversequencelength);
1080 
1081 	{
1082 		KEYFRAME_DATA* next_frame=this_frame->Next_Frame;
1083 
1084 		GetKeyFrameOffset(this_frame,output_offset);
1085 		if(next_frame->shift_offset)
1086 		{
1087 			VECTORCH next_offset;
1088 			GetKeyFrameOffset(next_frame,&next_offset);
1089 			output_offset->vx+=MUL_FIXED(next_offset.vx - output_offset->vx,lerp);
1090 			output_offset->vy+=MUL_FIXED(next_offset.vy - output_offset->vy,lerp);
1091 			output_offset->vz+=MUL_FIXED(next_offset.vz - output_offset->vz,lerp);
1092 		}
1093 		else
1094 		{
1095 			output_offset->vx+=MUL_FIXED((int)next_frame->Offset_x - output_offset->vx,lerp);
1096 			output_offset->vy+=MUL_FIXED((int)next_frame->Offset_y - output_offset->vy,lerp);
1097 			output_offset->vz+=MUL_FIXED((int)next_frame->Offset_z - output_offset->vz,lerp);
1098 		}
1099 	}
1100 	/* Now deal with orientation. */
1101 
1102 
1103 	Slerp(this_frame,lerp,&output_quat);
1104 
1105 	/* Elevation gone! Now deltas? */
1106 	{
1107 		DELTA_CONTROLLER *dcon;
1108 		QUAT elevation_quat,temp_quat;
1109 		VECTORCH elevation_offset;
1110 
1111 		dcon=controller->Deltas;
1112 
1113 		while (dcon) {
1114 			if (dcon->Active) {
1115 				Process_Delta_Controller(this_section_data,dcon,&elevation_offset,&elevation_quat);
1116 				output_offset->vx+=elevation_offset.vx;
1117 				output_offset->vy+=elevation_offset.vy;
1118 				output_offset->vz+=elevation_offset.vz;
1119 
1120 				temp_quat.quatw=output_quat.quatw;
1121 				temp_quat.quatx=output_quat.quatx;
1122 				temp_quat.quaty=output_quat.quaty;
1123 				temp_quat.quatz=output_quat.quatz;
1124 
1125 				MulQuat(&elevation_quat,&temp_quat,&output_quat);
1126 			}
1127 			dcon=dcon->next_controller;
1128 		}
1129 	}
1130 
1131 	QuatToMat(&output_quat,output_matrix);
1132 
1133 	/* Check the output is in a sensible place. */
1134 	if ( !(	(output_offset->vx<1000000 && output_offset->vx>-1000000)
1135 		 &&	(output_offset->vy<1000000 && output_offset->vy>-1000000)
1136 		 &&	(output_offset->vz<1000000 && output_offset->vz>-1000000)
1137 		 ) ) {
1138 
1139 		VECTORCH frame_offset;
1140 		GetKeyFrameOffset(input_frame,&frame_offset);
1141 
1142 		LOGDXFMT(("Third Tests in NEW_ANALYSE_KEYFRAME_DATA.\n"));
1143 		if (Global_HModel_Sptr) {
1144 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
1145 			if (Global_HModel_Sptr->SBdptr) {
1146 				LOGDXFMT(("Object is Near.\n"));
1147 			} else {
1148 				LOGDXFMT(("Object is Far.\n"));
1149 			}
1150 		} else {
1151 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
1152 		}
1153 		LOGDXFMT(("Name of section: %s\n",this_section_data->sempai->Section_Name));
1154 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
1155 		LOGDXFMT(("Sequence Timer = %d\n",controller->sequence_timer));
1156 		LOGDXFMT(("Tweening flags %d\n",controller->Tweening));
1157 
1158 		if (this_section_data->My_Parent) {
1159 			LOGDXFMT(("Parent Position %d,%d,%d\n",this_section_data->My_Parent->World_Offset.vx,this_section_data->My_Parent->World_Offset.vy,this_section_data->My_Parent->World_Offset.vz));
1160 		} else {
1161 			LOGDXFMT(("No parent.\n"));
1162 		}
1163 		LOGDXFMT(("This Position %d,%d,%d\n",this_section_data->World_Offset.vx,this_section_data->World_Offset.vy,this_section_data->World_Offset.vz));
1164 		LOGDXFMT(("This Keyframe Position %d,%d,%d\n",frame_offset.vx,frame_offset.vy,frame_offset.vz));
1165 		LOGDXFMT(("Output Offset %d,%d,%d\n",output_offset->vx,output_offset->vy,output_offset->vz));
1166 
1167 		LOCALASSERT(output_offset->vx<1000000 && output_offset->vx>-1000000);
1168 		LOCALASSERT(output_offset->vy<1000000 && output_offset->vy>-1000000);
1169 		LOCALASSERT(output_offset->vz<1000000 && output_offset->vz>-1000000);
1170 
1171 	}
1172 
1173 }
1174 
1175 SHAPEHEADER *Get_Degraded_Shape(SHAPEHEADER *base_shape)
1176 {
1177 	VECTORCH *worldposition,viewposition;
1178 	ADAPTIVE_DEGRADATION_DESC *array_ptr;
1179 	int lodScale;
1180 	extern float CameraZoomScale;
1181 
1182 	if ((base_shape->shape_degradation_array==NULL)||(Global_HModel_Sptr==NULL)) {
1183 		return(base_shape);
1184 	}
1185 
1186 	worldposition=&Global_HModel_Sptr->DynPtr->Position;
1187 
1188 	MakeVector(worldposition, &Global_VDB_Ptr->VDB_World, &viewposition);
1189 	RotateVector(&viewposition, &Global_VDB_Ptr->VDB_Mat);
1190 
1191 
1192 	array_ptr=base_shape->shape_degradation_array;
1193 
1194 	{
1195 		lodScale = (float)GlobalLevelOfDetail_Hierarchical*CameraZoomScale;
1196 
1197 	}
1198 	/* Now walk array. */
1199 	{
1200 
1201 		int objectDistance = viewposition.vz;
1202 
1203 		if (lodScale!=ONE_FIXED)
1204 		{
1205 			objectDistance = MUL_FIXED(objectDistance,lodScale);
1206 		}
1207 		if (objectDistance<=0) objectDistance=1;
1208 		f2i(viewposition.vz, viewposition.vz*CameraZoomScale);
1209 
1210 		/* KJL 12:30:37 09/06/98 - the object distance is scaled by a global variable
1211 		so that the level of detail can be changed on the fly.
1212 
1213 		N.B. The last distance stored in the shape_degradation_array is always zero, so that
1214 		the while loop below will always terminate. */
1215 		if (!array_ptr->shapeCanBeUsedCloseUp)
1216 		{
1217 			if(array_ptr->distance<viewposition.vz)
1218 			{
1219 				return (array_ptr->shape);
1220 			}
1221 			else
1222 			{
1223 				array_ptr++;
1224 			}
1225 
1226 		}
1227 		while (array_ptr->distance>objectDistance) array_ptr++;
1228 	}
1229 	/* Should have a valid entry now. */
1230 	return(array_ptr->shape);
1231 }
1232 
1233 void Process_Section(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,VECTORCH *parent_position,MATRIXCH *parent_orientation,int frame_timer, int sequence_type, int subsequence, int render) {
1234 
1235 	KEYFRAME_DATA *sequence_start;
1236 	SECTION *this_section;
1237 	SEQUENCE *this_sequence;
1238 
1239 	VECTORCH diagnostic_vector;
1240 	int Use_GoreRate;
1241 
1242 	/* Work out which SECTION to use. */
1243 
1244 	this_section=this_section_data->sempai;
1245 
1246 	if (controller!=this_section_data->my_controller) {
1247 		LOGDXFMT(("Wrong Controller assert in PROCESS_SECTION.w\n"));
1248 		LOGDXFMT(("Name of section: %s\n",this_section_data->sempai->Section_Name));
1249 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
1250 		LOGDXFMT(("Sequence Timer = %d\n",controller->sequence_timer));
1251 		LOGDXFMT(("Tweening flags %d\n",controller->Tweening));
1252 		GLOBALASSERT(controller==this_section_data->my_controller);
1253 	}
1254 
1255 	/* We can't just stop at a terminate_here, because that will
1256 	do over the section_data list. */
1257 
1258 	/* Well, actually, now we can. */
1259 
1260 	/* Put in a sequence switcher!!! */
1261 
1262 	/* There now is one. */
1263 
1264 	/* Quick auto-correction... */
1265 	if (frame_timer<0) {
1266 		frame_timer=0;
1267 	}
1268 
1269 	if (frame_timer>=65536) {
1270 		frame_timer=65535;
1271 	}
1272 
1273 	diagnostic_vector.vx=0;
1274 	diagnostic_vector.vy=0;
1275 	diagnostic_vector.vz=0;
1276 
1277 	if ((controller->FrameStamp!=GlobalFrameCounter)
1278 		||((this_section_data->flags&section_data_initialised)==0)) {
1279 
1280 		int fake_frame_timer;
1281 
1282 		/* Positions not computed yet this frame. */
1283 
1284 		this_sequence=GetSequencePointer(sequence_type,subsequence,this_section);
1285 		sequence_start=this_sequence->first_frame;
1286 
1287 		if ((controller->LockTopSection)&&(this_section_data==controller->section_data)) {
1288 			fake_frame_timer=0;
1289 		} else {
1290 			fake_frame_timer=frame_timer;
1291 		}
1292 
1293 		/* For this section, find the interpolated offset and eulers. */
1294 		this_section_data->Last_World_Offset=this_section_data->World_Offset;
1295 
1296 		if (this_section_data->Tweening) {
1297 			Analyse_Tweening_Data(controller,this_section_data,frame_timer,&(this_section_data->World_Offset),&(this_section_data->RelSecMat));
1298 		} else {
1299 			New_Analyse_Keyframe_Data(controller,this_section_data,sequence_start,fake_frame_timer,&(this_section_data->World_Offset),&(this_section_data->RelSecMat));
1300 		}
1301 
1302 		if ((controller->ZeroRootDisplacement)&&(this_section_data==controller->section_data)) {
1303 			this_section_data->World_Offset.vx=0;
1304 			this_section_data->World_Offset.vy=0;
1305 			this_section_data->World_Offset.vz=0;
1306 		}
1307 
1308 		if ((controller->ZeroRootRotation)&&(this_section_data==controller->section_data)) {
1309 			this_section_data->RelSecMat=Identity_RotMat;
1310 		}
1311 
1312 		this_section_data->Offset=this_section_data->World_Offset;
1313 
1314 		diagnostic_vector=this_section_data->World_Offset;
1315 
1316 		/* The parent's position will be used with the offset value and rotation
1317 		 matrix to determine the position of the new section. */
1318 
1319 		RotateVector(&this_section_data->World_Offset,parent_orientation);
1320 		AddVector(parent_position,&(this_section_data->World_Offset));
1321 
1322 		/* Create the absolute rotation matrix for this section. */
1323 		MatrixMultiply(parent_orientation,&(this_section_data->RelSecMat),&(this_section_data->SecMat));
1324 
1325 		/* Set the initialised flag... */
1326 		this_section_data->flags|=section_data_initialised;
1327 	}
1328 
1329 	/* Check the object is in a sensible place. */
1330 	if ( !(	(this_section_data->World_Offset.vx<1000000 && this_section_data->World_Offset.vx>-1000000)
1331 		 &&	(this_section_data->World_Offset.vy<1000000 && this_section_data->World_Offset.vy>-1000000)
1332 		 &&	(this_section_data->World_Offset.vz<1000000 && this_section_data->World_Offset.vz>-1000000)
1333 		 ) ) {
1334 
1335 		LOGDXFMT(("Tests in PROCESS_SECTION.\n"));
1336 		if (Global_HModel_Sptr) {
1337 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
1338 			if (Global_HModel_Sptr->SBdptr) {
1339 				LOGDXFMT(("Object is Near.\n"));
1340 			} else {
1341 				LOGDXFMT(("Object is Far.\n"));
1342 			}
1343 		} else {
1344 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
1345 		}
1346 		LOGDXFMT(("Name of section: %s\n",this_section_data->sempai->Section_Name));
1347 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
1348 		LOGDXFMT(("Sequence Timer = %d\n",controller->sequence_timer));
1349 		LOGDXFMT(("Tweening flags %d\n",controller->Tweening));
1350 
1351 		LOGDXFMT(("Diagnostic Vector %d,%d,%d\n",diagnostic_vector.vx,diagnostic_vector.vy,diagnostic_vector.vz));
1352 		LOGDXFMT(("Parent Orientation Matrix: %d,%d,%d\n",parent_orientation->mat11,parent_orientation->mat12,parent_orientation->mat13));
1353 		LOGDXFMT(("Parent Orientation Matrix: %d,%d,%d\n",parent_orientation->mat21,parent_orientation->mat22,parent_orientation->mat23));
1354 		LOGDXFMT(("Parent Orientation Matrix: %d,%d,%d\n",parent_orientation->mat31,parent_orientation->mat32,parent_orientation->mat33));
1355 
1356 		LOGDXFMT(("Parent Position %d,%d,%d\n",parent_position->vx,parent_position->vy,parent_position->vz));
1357 		LOGDXFMT(("This Position %d,%d,%d\n",this_section_data->World_Offset.vx,this_section_data->World_Offset.vy,this_section_data->World_Offset.vz));
1358 
1359 		LOCALASSERT(this_section_data->World_Offset.vx<1000000 && this_section_data->World_Offset.vx>-1000000);
1360 		LOCALASSERT(this_section_data->World_Offset.vy<1000000 && this_section_data->World_Offset.vy>-1000000);
1361 		LOCALASSERT(this_section_data->World_Offset.vz<1000000 && this_section_data->World_Offset.vz>-1000000);
1362 
1363 	}
1364 
1365 	/* Now call recursion... */
1366 
1367 	if ((this_section_data->First_Child!=NULL)
1368 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
1369 
1370 		SECTION_DATA *child_ptr;
1371 
1372 		child_ptr=this_section_data->First_Child;
1373 
1374 		while (child_ptr!=NULL) {
1375 
1376 			LOCALASSERT(child_ptr->My_Parent==this_section_data);
1377 
1378 			Process_Section(controller,child_ptr,&(this_section_data->World_Offset),&(this_section_data->SecMat),frame_timer,sequence_type,subsequence,render);
1379 			child_ptr=child_ptr->Next_Sibling;
1380 		}
1381 
1382 	}
1383 
1384 	/* Finally, if this section has a shape, and we are rendering, render it. */
1385 
1386 	if ((this_section_data->Shape!=NULL)&&((this_section_data->flags&section_data_notreal)==0)
1387 		&&(render)) {
1388 		/* Unreal things don't get plotted, either. */
1389 
1390 		DISPLAYBLOCK dummy_displayblock;
1391 		SHAPEHEADER *shape_to_use;
1392 
1393 		GLOBALASSERT(this_section_data->ShapeNum);
1394 
1395 		/* Decide what shape to use. */
1396 
1397 		if (this_section_data->Shape) {
1398 			if (this_section_data->Shape->shape_degradation_array==NULL) {
1399 				shape_to_use=this_section_data->Shape;
1400 			} else {
1401 				shape_to_use=Get_Degraded_Shape(this_section_data->Shape);
1402 			}
1403 		} else {
1404 			shape_to_use=NULL;
1405 		}
1406 
1407 		/* This really needs to be pre-zeroed, a static
1408 		 in this file... but not yet. */
1409 
1410 		if (Simplify_HModel_Rendering) {
1411 			dummy_displayblock.ObShape=GetLoadedShapeMSL("Shell");
1412 			dummy_displayblock.ObShapeData=GetShapeData(dummy_displayblock.ObShape);
1413 		} else {
1414 			dummy_displayblock.ObShape=this_section_data->ShapeNum;
1415 			dummy_displayblock.ObShapeData=shape_to_use;
1416 		}
1417 		dummy_displayblock.name=NULL;
1418 		dummy_displayblock.ObWorld=this_section_data->World_Offset;
1419 		dummy_displayblock.ObEuler.EulerX=0;
1420 		dummy_displayblock.ObEuler.EulerY=0;
1421 		dummy_displayblock.ObEuler.EulerZ=0;
1422 		dummy_displayblock.ObMat=this_section_data->SecMat;
1423 		dummy_displayblock.ObFlags=ObFlag_VertexHazing|ObFlag_MultLSrc;
1424 		dummy_displayblock.ObFlags2=Global_HModel_DispPtr->ObFlags2;
1425 		dummy_displayblock.ObFlags3=0;
1426 		dummy_displayblock.ObNumLights=0;
1427 		dummy_displayblock.ObVDBPtr=NULL;
1428 		if(shape_to_use)
1429 			dummy_displayblock.ObRadius=shape_to_use->shaperadius;
1430 		else
1431 			dummy_displayblock.ObRadius=0;
1432 		dummy_displayblock.ObMaxX=0;
1433 		dummy_displayblock.ObMinX=0;
1434 		dummy_displayblock.ObMaxY=0;
1435 		dummy_displayblock.ObMinY=0;
1436 		dummy_displayblock.ObMaxZ=0;
1437 		dummy_displayblock.ObMinZ=0;
1438 		dummy_displayblock.ObTxAnimCtrlBlks=this_section_data->tac_ptr;
1439 		dummy_displayblock.ObEIDPtr=NULL;
1440 		dummy_displayblock.ObMorphCtrl=NULL;
1441 		dummy_displayblock.ObStrategyBlock=Global_HModel_Sptr;
1442 		dummy_displayblock.ShapeAnimControlBlock=this_section_data->sac_ptr;
1443 		dummy_displayblock.HModelControlBlock=NULL; /* Don't even want to think about that. */
1444 		dummy_displayblock.ObMyModule=NULL;
1445 		dummy_displayblock.SpecialFXFlags = Global_HModel_DispPtr->SpecialFXFlags;
1446 
1447 		MakeVector(&dummy_displayblock.ObWorld, &Global_VDB_Ptr->VDB_World, &dummy_displayblock.ObView);
1448 		RotateVector(&dummy_displayblock.ObView, &Global_VDB_Ptr->VDB_Mat);
1449 
1450 		/* Whilst we're here... */
1451 
1452 		this_section_data->View_Offset=dummy_displayblock.ObView;
1453 		this_section_data->flags|=section_data_view_init;
1454 		controller->View_FrameStamp=GlobalFrameCounter;
1455 
1456 		/* Ho hum. */
1457 
1458 		if (dummy_displayblock.ShapeAnimControlBlock) {
1459 			DoShapeAnimation(&dummy_displayblock);
1460 		}
1461 
1462 		if(this_section->flags&(section_flag_heatsource|section_flag_affectedbyheat))
1463 		{
1464 			dummy_displayblock.SpecialFXFlags|=SFXFLAG_ISAFFECTEDBYHEAT;
1465 		}
1466 
1467 		#if 1
1468 		if (PIPECLEANER_CHEATMODE)
1469 		if (Global_HModel_Sptr)
1470 		if (Global_HModel_Sptr->SBdptr)
1471 		if (! ((Global_HModel_Sptr->SBdptr->ObWorld.vx == parent_position->vx)
1472 			  &&(Global_HModel_Sptr->SBdptr->ObWorld.vy == parent_position->vy)
1473 			  &&(Global_HModel_Sptr->SBdptr->ObWorld.vz == parent_position->vz)) )
1474 		{
1475 			PARTICLE particle;
1476 
1477 			particle.Colour = 0xffffffff;
1478 			particle.Size = 30;
1479 
1480 			particle.ParticleID = PARTICLE_LASERBEAM;
1481 			particle.Position = this_section_data->World_Offset;
1482 			particle.Offset = *parent_position;;
1483 
1484 			D3D_DecalSystem_Setup();
1485 			RenderParticle(&particle);
1486 
1487 			particle.ParticleID=PARTICLE_ANDROID_BLOOD;
1488 
1489 			particle.Size = shape_to_use->shaperadius/8;
1490 			RenderParticle(&particle);
1491 			D3D_DecalSystem_End();
1492 
1493 		}
1494 		#endif
1495 
1496 		RenderThisHierarchicalDisplayblock(&dummy_displayblock);
1497 
1498 	} else if (controller->View_FrameStamp!=GlobalFrameCounter) {
1499 		this_section_data->flags&=(~section_data_view_init);
1500 	}
1501 
1502 	/* Gore spray... */
1503 
1504 	if (GlobalGoreRate==0) return;
1505 	if (NumberOfBloodParticles>=MAX_NO_OF_BLOOD_PARTICLES) return;
1506 	if (controller->DisableBleeding) return;
1507 
1508 	Use_GoreRate=GlobalGoreRate;
1509 	if (SUPERGORE_MODE) {
1510 		Use_GoreRate/=5;
1511 	}
1512 
1513 	/* A terminator must spray (backwards) from the origin, to be the stump. */
1514 
1515 	if (this_section_data->flags&section_data_terminate_here) {
1516 		if (this_section->flags&section_sprays_anything) {
1517 			/* Check for non-zero spray direction... */
1518 			if ( (this_section->gore_spray_direction.vx!=0)
1519 				|| (this_section->gore_spray_direction.vy!=0)
1520 				|| (this_section->gore_spray_direction.vz!=0) ) {
1521 
1522 				this_section_data->gore_timer+=NormalFrameTime;
1523 
1524 				/* New and Special Sparks Code! 16/7/98 */
1525 				if (this_section->flags&section_sprays_sparks) {
1526 					while (this_section_data->gore_timer>(GlobalGoreRate*SPARKS_FOR_A_SPRAY)) {
1527 
1528 						VECTORCH final_spray_direction;
1529 						MATRIXCH spray_orient;
1530 						/* Spray is go! */
1531 
1532 						RotateAndCopyVector(&this_section->gore_spray_direction,&final_spray_direction,&this_section_data->SecMat);
1533 
1534 						/* Reverse direction for stumps. */
1535 
1536 						final_spray_direction.vx=-final_spray_direction.vx;
1537 						final_spray_direction.vy=-final_spray_direction.vy;
1538 						final_spray_direction.vz=-final_spray_direction.vz;
1539 
1540 						/* Scale down. */
1541 
1542 						final_spray_direction.vx>>=5;
1543 						final_spray_direction.vy>>=5;
1544 						final_spray_direction.vz>>=5;
1545 
1546 						/* Add random element. */
1547 
1548 						final_spray_direction.vx+=( (FastRandom()&1023)-512);
1549 						final_spray_direction.vy+=( (FastRandom()&1023)-512);
1550 						final_spray_direction.vz+=( (FastRandom()&1023)-512);
1551 
1552 						Normalise(&final_spray_direction);
1553 						/* Now convert back to a matrix. */
1554 						MakeMatrixFromDirection(&final_spray_direction,&spray_orient);
1555 
1556 						/* Call spray function. */
1557 
1558 						MakeSprayOfSparks(&spray_orient,&this_section_data->World_Offset);
1559 
1560 						Sound_Play(SID_SPARKS,"d",&this_section_data->World_Offset);
1561 
1562 						this_section_data->gore_timer-=(GlobalGoreRate*SPARKS_FOR_A_SPRAY);
1563 					}
1564 					GLOBALASSERT(this_section_data->gore_timer<=(GlobalGoreRate*SPARKS_FOR_A_SPRAY));
1565 				} else {
1566 					while (this_section_data->gore_timer>Use_GoreRate) {
1567 
1568 						VECTORCH final_spray_direction;
1569 						enum PARTICLE_ID blood_type;
1570 						/* Spray is go! */
1571 
1572 						RotateAndCopyVector(&this_section->gore_spray_direction,&final_spray_direction,&this_section_data->SecMat);
1573 
1574 						/* Reverse direction for stumps. */
1575 
1576 						final_spray_direction.vx=-final_spray_direction.vx;
1577 						final_spray_direction.vy=-final_spray_direction.vy;
1578 						final_spray_direction.vz=-final_spray_direction.vz;
1579 
1580 						/* Scale down. */
1581 
1582 						final_spray_direction.vx>>=5;
1583 						final_spray_direction.vy>>=5;
1584 						final_spray_direction.vz>>=5;
1585 
1586 						/* Add random element. */
1587 
1588 						final_spray_direction.vx+=( (FastRandom()&1023)-512);
1589 						final_spray_direction.vy+=( (FastRandom()&1023)-512);
1590 						final_spray_direction.vz+=( (FastRandom()&1023)-512);
1591 
1592 						if (SUPERGORE_MODE) {
1593 							final_spray_direction.vx<<=1;
1594 							final_spray_direction.vy<<=1;
1595 							final_spray_direction.vz<<=1;
1596 						}
1597 
1598 						/* Identify spray type. */
1599 
1600 						if (this_section->flags&section_sprays_blood) {
1601 							blood_type=GetBloodType(Global_HModel_Sptr);
1602 							/* Er... default? */
1603 						} else if (this_section->flags&section_sprays_acid) {
1604 							blood_type=PARTICLE_ALIEN_BLOOD;
1605 						} else if (this_section->flags&section_sprays_predoblood) {
1606 							blood_type=PARTICLE_PREDATOR_BLOOD;
1607 						} else if (this_section->flags&section_sprays_sparks) {
1608 							blood_type=PARTICLE_SPARK;
1609 						} else {
1610 							blood_type=PARTICLE_FLAME;
1611 							/* Distinctive. */
1612 						}
1613 
1614 						/* Call spray function. */
1615 
1616 						MakeParticle(&this_section_data->World_Offset, &final_spray_direction, blood_type);
1617 
1618 						this_section_data->gore_timer-=Use_GoreRate;
1619 					}
1620 				}
1621 			}
1622 		}
1623 	}
1624 
1625 	/* A false root must also spray from the origin... to be the missing part. */
1626 
1627 	if (this_section_data->flags&section_data_false_root) {
1628 		if (this_section->flags&section_sprays_anything) {
1629 			/* Check for non-zero spray direction... */
1630 			if ( (this_section->gore_spray_direction.vx!=0)
1631 				|| (this_section->gore_spray_direction.vy!=0)
1632 				|| (this_section->gore_spray_direction.vz!=0) ) {
1633 
1634 				this_section_data->gore_timer+=NormalFrameTime;
1635 
1636 				/* I don't *think* a section can be both... */
1637 				/* But if it is, we'll just have to live with it. */
1638 
1639 				/* New and Special Sparks Code! 16/7/98 */
1640 				if (this_section->flags&section_sprays_sparks) {
1641 					while (this_section_data->gore_timer>(GlobalGoreRate*SPARKS_FOR_A_SPRAY)) {
1642 
1643 						VECTORCH final_spray_direction;
1644 						MATRIXCH spray_orient;
1645 
1646 						/* Spray is go! */
1647 
1648 						RotateAndCopyVector(&this_section->gore_spray_direction,&final_spray_direction,&this_section_data->SecMat);
1649 
1650 						/* Scale down. */
1651 
1652 						final_spray_direction.vx>>=5;
1653 						final_spray_direction.vy>>=5;
1654 						final_spray_direction.vz>>=5;
1655 
1656 						/* Add random element. */
1657 
1658 						final_spray_direction.vx+=( (FastRandom()&1023)-512);
1659 						final_spray_direction.vy+=( (FastRandom()&1023)-512);
1660 						final_spray_direction.vz+=( (FastRandom()&1023)-512);
1661 						Normalise(&final_spray_direction);
1662 
1663 						/* Now convert back to a matrix. */
1664 						MakeMatrixFromDirection(&final_spray_direction,&spray_orient);
1665 
1666 						/* Call spray function. */
1667 
1668 						MakeSprayOfSparks(&spray_orient,&this_section_data->World_Offset);
1669 
1670 						Sound_Play(SID_SPARKS,"d",&this_section_data->World_Offset);
1671 
1672 						this_section_data->gore_timer-=(GlobalGoreRate*SPARKS_FOR_A_SPRAY);
1673 					}
1674 					GLOBALASSERT(this_section_data->gore_timer<=(GlobalGoreRate*SPARKS_FOR_A_SPRAY));
1675 				} else {
1676 
1677 					while (this_section_data->gore_timer>Use_GoreRate) {
1678 
1679 						enum PARTICLE_ID blood_type;
1680 						VECTORCH final_spray_direction;
1681 						/* Spray is go! */
1682 
1683 						RotateAndCopyVector(&this_section->gore_spray_direction,&final_spray_direction,&this_section_data->SecMat);
1684 
1685 						/* Scale down. */
1686 
1687 						final_spray_direction.vx>>=5;
1688 						final_spray_direction.vy>>=5;
1689 						final_spray_direction.vz>>=5;
1690 
1691 						/* Add random element. */
1692 
1693 						final_spray_direction.vx+=( (FastRandom()&1023)-512);
1694 						final_spray_direction.vy+=( (FastRandom()&1023)-512);
1695 						final_spray_direction.vz+=( (FastRandom()&1023)-512);
1696 
1697 						if (SUPERGORE_MODE) {
1698 							final_spray_direction.vx<<=1;
1699 							final_spray_direction.vy<<=1;
1700 							final_spray_direction.vz<<=1;
1701 						}
1702 
1703 						/* Identify spray type. */
1704 
1705 						if (this_section->flags&section_sprays_blood) {
1706 							blood_type=GetBloodType(Global_HModel_Sptr);
1707 							/* Er... default? */
1708 						} else if (this_section->flags&section_sprays_acid) {
1709 							blood_type=PARTICLE_ALIEN_BLOOD;
1710 						} else if (this_section->flags&section_sprays_predoblood) {
1711 							blood_type=PARTICLE_PREDATOR_BLOOD;
1712 						} else if (this_section->flags&section_sprays_sparks) {
1713 							blood_type=PARTICLE_SPARK;
1714 						} else {
1715 							blood_type=PARTICLE_FLAME;
1716 							/* Distinctive. */
1717 						}
1718 
1719 						/* Call spray function. */
1720 
1721 						MakeParticle(&this_section_data->World_Offset, &final_spray_direction, blood_type);
1722 
1723 						this_section_data->gore_timer-=Use_GoreRate;
1724 					}
1725 				}
1726 			}
1727 		}
1728 	}
1729 
1730 	/* Part three... wounded section. */
1731 
1732 	if (this_section_data->current_damage.Health<(this_section->StartingStats.Health<<ONE_FIXED_SHIFT)) {
1733 		if (this_section->flags&section_sprays_anything) {
1734 			int bleeding_rate;
1735 
1736 			if (this_section_data->current_damage.Health==0) {
1737 				/* Bleed a lot. */
1738 				bleeding_rate=Use_GoreRate;
1739 			} else {
1740 				/* Just bleed a bit. */
1741 				bleeding_rate=(Use_GoreRate<<1);
1742 			}
1743 
1744 			/* Don't care about non-zero spray direction here. */
1745 
1746 			this_section_data->gore_timer+=NormalFrameTime;
1747 
1748 			/* I don't *think* a section can be both... */
1749 			/* But if it is, we'll just have to live with it. */
1750 
1751 			/* New and Special Sparks Code! 16/7/98 */
1752 			if (this_section->flags&section_sprays_sparks) {
1753 				while (this_section_data->gore_timer>(bleeding_rate*SPARKS_FOR_A_SPRAY)) {
1754 
1755 					VECTORCH final_spray_direction;
1756 					MATRIXCH spray_orient;
1757 
1758 					/* Spray is go! */
1759 
1760 					/* Zero base direction. */
1761 
1762 					final_spray_direction.vx=0;
1763 					final_spray_direction.vy=0;
1764 					final_spray_direction.vz=0;
1765 
1766 					/* Add random element. */
1767 
1768 					while ( (final_spray_direction.vx==0)
1769 						&&(final_spray_direction.vy==0)
1770 						&&(final_spray_direction.vz==0)) {
1771 						final_spray_direction.vx+=( (FastRandom()&1023)-512);
1772 						final_spray_direction.vy+=( (FastRandom()&1023)-512);
1773 						final_spray_direction.vz+=( (FastRandom()&1023)-512);
1774 					}
1775 					Normalise(&final_spray_direction);
1776 
1777 					/* Now convert back to a matrix. */
1778 					MakeMatrixFromDirection(&final_spray_direction,&spray_orient);
1779 
1780 					/* Call spray function. */
1781 
1782 					MakeSprayOfSparks(&spray_orient,&this_section_data->World_Offset);
1783 
1784 					Sound_Play(SID_SPARKS,"d",&this_section_data->World_Offset);
1785 
1786 					this_section_data->gore_timer-=(bleeding_rate*SPARKS_FOR_A_SPRAY);
1787 				}
1788 				GLOBALASSERT(this_section_data->gore_timer<=(bleeding_rate*SPARKS_FOR_A_SPRAY));
1789 			} else {
1790 
1791 				while (this_section_data->gore_timer>bleeding_rate) {
1792 
1793 					enum PARTICLE_ID blood_type;
1794 					VECTORCH final_spray_direction;
1795 					/* Spray is go! */
1796 
1797 					/* Zero base direction. */
1798 
1799 					final_spray_direction.vx=0;
1800 					final_spray_direction.vy=0;
1801 					final_spray_direction.vz=0;
1802 
1803 					/* Add random element. */
1804 
1805 					final_spray_direction.vx+=( (FastRandom()&1023)-512);
1806 					final_spray_direction.vy+=( (FastRandom()&1023)-512);
1807 					final_spray_direction.vz+=( (FastRandom()&1023)-512);
1808 
1809 					/* Identify spray type. */
1810 
1811 					if (this_section->flags&section_sprays_blood) {
1812 						blood_type=GetBloodType(Global_HModel_Sptr);
1813 						/* Er... default? */
1814 					} else if (this_section->flags&section_sprays_acid) {
1815 						blood_type=PARTICLE_ALIEN_BLOOD;
1816 					} else if (this_section->flags&section_sprays_predoblood) {
1817 						blood_type=PARTICLE_PREDATOR_BLOOD;
1818 					} else if (this_section->flags&section_sprays_sparks) {
1819 						blood_type=PARTICLE_SPARK;
1820 					} else {
1821 						blood_type=PARTICLE_FLAME;
1822 						/* Distinctive. */
1823 					}
1824 
1825 					/* Call spray function. */
1826 
1827 					MakeParticle(&this_section_data->World_Offset, &final_spray_direction, blood_type);
1828 
1829 					this_section_data->gore_timer-=bleeding_rate;
1830 				}
1831 				GLOBALASSERT(this_section_data->gore_timer<=bleeding_rate);
1832 			}
1833 		}
1834 	}
1835 
1836 }
1837 
1838 void Init_Sequence_Recursion(SECTION_DATA *this_section_data, int sequence_type,int subsequence, int seconds_for_sequence) {
1839 
1840 	SEQUENCE *sequence_ptr;
1841 
1842 	sequence_ptr=GetSequencePointer(sequence_type,subsequence,this_section_data->sempai);
1843 
1844 	/* Init fields. */
1845 
1846 	this_section_data->current_sequence=sequence_ptr;
1847 	this_section_data->current_keyframe=sequence_ptr->first_frame;
1848 	this_section_data->accumulated_timer=0;
1849 	this_section_data->freezeframe_timer=-1;
1850 	this_section_data->lastframe_timer=0;
1851 	this_section_data->gore_timer=0; /* As good a time as any. */
1852 
1853 	/* Zero last world offset, just for the record. */
1854 	this_section_data->Last_World_Offset.vx=0;
1855 	this_section_data->Last_World_Offset.vy=0;
1856 	this_section_data->Last_World_Offset.vz=0;
1857 
1858 	/* Deinit tweening. */
1859 
1860 	this_section_data->Tweening=0;
1861 
1862 	/* Keyframe and Sound Flags. */
1863 	if(sequence_ptr->first_frame->frame_has_extended_data)
1864 	{
1865 		KEYFRAME_DATA_EXTENDED* first_frame_extended=(KEYFRAME_DATA_EXTENDED*) sequence_ptr->first_frame;
1866 		this_section_data->my_controller->keyframe_flags|=first_frame_extended->flags;
1867 		if (first_frame_extended->sound) {
1868 			if (this_section_data->flags&section_data_initialised) {
1869 				if (this_section_data->my_controller->DisableSounds==0) {
1870 					PlayHierarchySound(first_frame_extended->sound,&this_section_data->World_Offset);
1871 				}
1872 			}
1873 		}
1874 	}
1875 	/* Animation. */
1876 
1877 	if (this_section_data->sac_ptr) {
1878 
1879 		SHAPEANIMATIONCONTROLDATA sacd ;
1880 		SHAPEANIMATIONSEQUENCE *sequence_pointer;
1881 		int num_frames,sequence;
1882 
1883 		sequence=(sequence_type<<16)+subsequence;
1884 
1885 		InitShapeAnimationControlData(&sacd);
1886 		/* Compute rate. */
1887 
1888 		sequence_pointer=this_section_data->sac_ptr->anim_header->anim_sequences;
1889 		GLOBALASSERT(sequence<this_section_data->sac_ptr->anim_header->num_sequences);
1890 		num_frames=sequence_pointer[sequence].num_frames;
1891 
1892 		sacd.seconds_per_frame = seconds_for_sequence/num_frames;
1893 		/* Okay. */
1894 		sacd.sequence_no = sequence;
1895 		sacd.default_start_and_end_frames = 1;
1896 		sacd.reversed = 0;
1897 		sacd.stop_at_end = 0;
1898 		SetOrphanedShapeAnimationSequence (this_section_data->sac_ptr, &sacd);
1899 
1900 	}
1901 
1902 	/* Recurse. */
1903 
1904 	if ( (this_section_data->First_Child!=NULL)
1905 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
1906 		/* Respect the terminator! */
1907 
1908 		SECTION_DATA *child_list_ptr;
1909 
1910 		child_list_ptr=this_section_data->First_Child;
1911 
1912 		while (child_list_ptr!=NULL) {
1913 			Init_Sequence_Recursion(child_list_ptr,sequence_type,subsequence,seconds_for_sequence);
1914 			child_list_ptr=child_list_ptr->Next_Sibling;
1915 		}
1916 
1917 	}
1918 
1919 }
1920 
1921 void HModel_ChangeSpeed(HMODELCONTROLLER *controller, int seconds_for_sequence) {
1922 
1923 	/* Simple enough... */
1924 
1925 	controller->Seconds_For_Sequence=seconds_for_sequence;
1926 	controller->timer_increment=DIV_FIXED(ONE_FIXED,controller->Seconds_For_Sequence);
1927 
1928 }
1929 
1930 void HModel_SetToolsRelativeSpeed(HMODELCONTROLLER *controller, int factor) {
1931 
1932 	KEYFRAME_DATA *sequence_start;
1933 	SEQUENCE *this_sequence;
1934 	int real_time;
1935 
1936 	GLOBALASSERT(controller);
1937 	GLOBALASSERT(factor>0);
1938 	/* Find the tools speed for this sequence... */
1939 
1940 	this_sequence=GetSequencePointer(controller->Sequence_Type,controller->Sub_Sequence,controller->Root_Section);
1941 	sequence_start=this_sequence->first_frame;
1942 	GLOBALASSERT(sequence_start);
1943 
1944 	real_time=this_sequence->Time;
1945 	if (real_time<=0) {
1946 		real_time=ONE_FIXED;
1947 		/* Might want to assert here? */
1948 	}
1949 
1950 	real_time=MUL_FIXED(real_time,factor);
1951 	HModel_ChangeSpeed(controller,real_time);
1952 
1953 }
1954 
1955 void InitHModelSequence(HMODELCONTROLLER *controller, int sequence_type,int subsequence, int seconds_for_sequence) {
1956 
1957 	KEYFRAME_DATA *sequence_start;
1958 	SEQUENCE *this_sequence;
1959 	int real_time;
1960 
1961 	GLOBALASSERT(controller);
1962 	GLOBALASSERT(seconds_for_sequence);
1963 
1964 	/* Check this sequence exists... */
1965 
1966 	this_sequence=GetSequencePointer(sequence_type,subsequence,controller->Root_Section);
1967 	sequence_start=this_sequence->first_frame;
1968 	GLOBALASSERT(sequence_start);
1969 
1970 	if (seconds_for_sequence>0) {
1971 		real_time=seconds_for_sequence;
1972 	} else {
1973 		real_time=this_sequence->Time;
1974 		if (real_time<=0) {
1975 			real_time=ONE_FIXED;
1976 			/* Might want to assert here. */
1977 		}
1978 	}
1979 
1980 	/* Now set it up. */
1981 
1982 	controller->Sequence_Type=sequence_type;
1983 	controller->Sub_Sequence=subsequence;
1984 	controller->Seconds_For_Sequence=real_time;
1985 	controller->timer_increment=DIV_FIXED(ONE_FIXED,controller->Seconds_For_Sequence);
1986 	controller->sequence_timer=0;
1987 	controller->Playing=1;
1988 	controller->Reversed=0;
1989 	controller->Looped=1;
1990 
1991 	controller->keyframe_flags=0;
1992 
1993 	controller->After_Tweening_Sequence_Type=-1;
1994 	controller->After_Tweening_Sub_Sequence=-1;
1995 	controller->AT_seconds_for_sequence=ONE_FIXED;
1996 	controller->AT_sequence_timer=0;
1997 	controller->Tweening=Controller_NoTweening;
1998 
1999 	/* Recurse though hierarchy, setting up all the section_data sequence stores? */
2000 
2001 	Init_Sequence_Recursion(controller->section_data, sequence_type, subsequence,seconds_for_sequence);
2002 
2003 }
2004 
2005 static void HMTimer_Kernel(HMODELCONTROLLER *controller) {
2006 
2007 	/* Core of the timer code. */
2008 
2009 	if (controller->Tweening==Controller_EndTweening) {
2010 		int reversed, AT_sequence_timer;
2011 
2012 		reversed=controller->Reversed; /* Remember this! */
2013 		AT_sequence_timer=controller->AT_sequence_timer; /* Remember this, too! */
2014 
2015 		if (controller->AT_seconds_for_sequence==0) {
2016 			GLOBALASSERT(controller->AT_seconds_for_sequence);
2017 		}
2018 
2019 		InitHModelSequence(controller,controller->After_Tweening_Sequence_Type,
2020 			controller->After_Tweening_Sub_Sequence,controller->AT_seconds_for_sequence);
2021 
2022 		if (reversed) {
2023 			controller->Reversed=1;
2024 			#if 0
2025 			controller->sequence_timer=(ONE_FIXED-1);
2026 			#endif
2027 		}
2028 
2029 		/* This should now always be set, though InitHModelSequence zeros it (D'oh!). */
2030 		controller->sequence_timer=AT_sequence_timer;
2031 
2032 		if (controller->LoopAfterTweening) {
2033 			controller->Looped=1;
2034 		} else {
2035 			controller->Looped=0;
2036 		}
2037 		if (controller->StopAfterTweening) {
2038 			/* Hey ho. */
2039 			controller->Playing=0;
2040 		}
2041 		controller->Tweening=Controller_NoTweening;
2042 		controller->ElevationTweening=0;
2043 	}
2044 
2045 	if (controller->Playing) {
2046 		if (controller->Reversed) {
2047 			if (controller->Tweening!=Controller_NoTweening) {
2048 				GLOBALASSERT(controller->Tweening==Controller_Tweening);
2049 				/* Still tween forwards. */
2050 				controller->sequence_timer+=MUL_FIXED(controller->timer_increment,NormalFrameTime);
2051 				if (controller->sequence_timer>=ONE_FIXED) {
2052 					controller->sequence_timer=ONE_FIXED;
2053 					controller->Tweening=Controller_EndTweening;
2054 				}
2055 			} else {
2056 				GLOBALASSERT(controller->Tweening==Controller_NoTweening);
2057 				controller->sequence_timer-=MUL_FIXED(controller->timer_increment,NormalFrameTime);
2058 				if (controller->Looped) {
2059 					while (controller->sequence_timer<0) {
2060 						/* Might lose count of how many times we've looped, but who's counting? */
2061 						controller->sequence_timer+=ONE_FIXED;
2062 					}
2063 				} else {
2064 					if (controller->sequence_timer<0) {
2065 						controller->sequence_timer=0;
2066 					}
2067 				}
2068 			}
2069 		} else {
2070 			controller->sequence_timer+=MUL_FIXED(controller->timer_increment,NormalFrameTime);
2071 
2072 			if (controller->Tweening!=Controller_NoTweening) {
2073 				GLOBALASSERT(controller->Tweening==Controller_Tweening);
2074 				if (controller->sequence_timer>=ONE_FIXED) {
2075 					controller->sequence_timer=ONE_FIXED;
2076 					controller->Tweening=Controller_EndTweening;
2077 				}
2078 
2079 			} else if (controller->Looped) {
2080 				while (controller->sequence_timer>=ONE_FIXED) {
2081 					/* Might lose count of how many times we've looped, but who's counting? */
2082 					controller->sequence_timer-=ONE_FIXED;
2083 				}
2084 			} else {
2085 				if (controller->sequence_timer>=ONE_FIXED) {
2086 					controller->sequence_timer=ONE_FIXED-1;
2087 				}
2088 			}
2089 		}
2090 	}
2091 
2092 	/* Do delta timers, too. */
2093 
2094 	{
2095 		DELTA_CONTROLLER *dcon;
2096 
2097 		dcon=controller->Deltas;
2098 
2099 		while (dcon) {
2100 
2101 			if ((dcon->seconds_for_sequence)&&(dcon->Playing)&&(dcon->Active)) {
2102 				dcon->timer+=MUL_FIXED(dcon->timer_increment,NormalFrameTime);
2103 				if (dcon->Looped) {
2104 					if (dcon->timer>=ONE_FIXED) dcon->timer-=ONE_FIXED;
2105 				} else {
2106 					if (dcon->timer>=ONE_FIXED) dcon->timer=ONE_FIXED-1;
2107 				}
2108 			}
2109 
2110 			dcon=dcon->next_controller;
2111 		}
2112 	}
2113 
2114 }
2115 
2116 void DoHModel(HMODELCONTROLLER *controller, DISPLAYBLOCK *dptr) {
2117 
2118 	GLOBALASSERT(controller);
2119 	GLOBALASSERT(dptr);
2120 
2121 	Global_HModel_Sptr=dptr->ObStrategyBlock;
2122 	Global_HModel_DispPtr=dptr;
2123 
2124 	/* Check the object is in a sensible place. */
2125 	if ( !(	(dptr->ObWorld.vx<1000000 && dptr->ObWorld.vx>-1000000)
2126 		 &&	(dptr->ObWorld.vy<1000000 && dptr->ObWorld.vy>-1000000)
2127 		 &&	(dptr->ObWorld.vz<1000000 && dptr->ObWorld.vz>-1000000)
2128 		 ) ) {
2129 
2130 		LOGDXFMT(("Tests in DOHMODEL.\n"));
2131 		if (Global_HModel_Sptr) {
2132 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
2133 		} else {
2134 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
2135 		}
2136 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
2137 
2138 		LOCALASSERT(dptr->ObWorld.vx<1000000 && dptr->ObWorld.vx>-1000000);
2139 		LOCALASSERT(dptr->ObWorld.vy<1000000 && dptr->ObWorld.vy>-1000000);
2140 		LOCALASSERT(dptr->ObWorld.vz<1000000 && dptr->ObWorld.vz>-1000000);
2141 
2142 	}
2143 
2144 	GLOBALASSERT(controller->section_data->my_controller==controller);
2145 
2146 	/* Only do timer if you're out of date. */
2147 
2148 	if (controller->FrameStamp!=GlobalFrameCounter) {
2149 
2150 		HMTimer_Kernel(controller);
2151 
2152 		controller->keyframe_flags=0;
2153 
2154 	} else {
2155 		VECTORCH offset;
2156 		/* Want to budge? */
2157 
2158 		offset.vx=dptr->ObWorld.vx-controller->Computed_Position.vx;
2159 		offset.vy=dptr->ObWorld.vy-controller->Computed_Position.vy;
2160 		offset.vz=dptr->ObWorld.vz-controller->Computed_Position.vz;
2161 
2162 		if ((offset.vx!=0)||(offset.vy!=0)||(offset.vz!=0)) {
2163 			/* I reckon you'd be better off taking the budge. */
2164 			Budge_HModel(controller,&offset);
2165 		}
2166 
2167 	}
2168 
2169 	/* That handled the timer.  Now render it. */
2170 	{
2171 		int render;
2172 
2173 		if (dptr->ObFlags&ObFlag_NotVis) {
2174 			textprint("HModel NotVis!\n");
2175 			render=0;
2176 		} else {
2177 			render=1;
2178 		}
2179 		Process_Section(controller,controller->section_data,&dptr->ObWorld,&dptr->ObMat,controller->sequence_timer,controller->Sequence_Type,controller->Sub_Sequence,render);
2180 
2181 	}
2182 	/* Note braces!  Process_Section is OUTSIDE, 'cos you might still want to render! */
2183 
2184 	/* Update frame stamp. */
2185 
2186 	controller->FrameStamp=GlobalFrameCounter;
2187 	controller->Computed_Position=dptr->ObWorld;
2188 
2189 }
2190 
2191 void DoHModelTimer_Recursion(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,int frame_timer, int sequence_type, int subsequence) {
2192 
2193 	KEYFRAME_DATA *sequence_start;
2194 	SECTION *this_section;
2195 	SEQUENCE *this_sequence;
2196 
2197 	/* Cut down Process_Section. */
2198 
2199 	this_section=this_section_data->sempai;
2200 
2201 	if (controller->FrameStamp!=GlobalFrameCounter) {
2202 
2203 		/* Positions not computed yet this frame. */
2204 
2205 		this_sequence=GetSequencePointer(sequence_type,subsequence,this_section);
2206 		sequence_start=this_sequence->first_frame;
2207 
2208 		/* For this section, find the interpolated offset and eulers. */
2209 
2210 		if (this_section_data->Tweening) {
2211 			/* Err... do nothing. */
2212 		} else {
2213 			int working_timer;
2214 
2215 			Handle_Section_Timer(controller,this_section_data,sequence_start,frame_timer, &working_timer);
2216 		}
2217 
2218 	}
2219 
2220 	/* Now call recursion... */
2221 
2222 	if ((this_section_data->First_Child!=NULL)
2223 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
2224 
2225 		SECTION_DATA *child_ptr;
2226 
2227 		child_ptr=this_section_data->First_Child;
2228 
2229 		while (child_ptr!=NULL) {
2230 
2231 			LOCALASSERT(child_ptr->My_Parent==this_section_data);
2232 
2233 			DoHModelTimer_Recursion(controller,child_ptr,frame_timer,sequence_type,subsequence);
2234 			child_ptr=child_ptr->Next_Sibling;
2235 		}
2236 
2237 	}
2238 
2239 }
2240 
2241 void DoHModelTimer(HMODELCONTROLLER *controller) {
2242 
2243 	/* Be VERY careful with this function - it can put the timer and the
2244 	position computations out of step.  Once you've called this, call NO
2245 	OTHER HMODEL FUNCTIONS on this model until the next frame! */
2246 
2247 	GLOBALASSERT(controller);
2248 
2249 	if (controller->FrameStamp==GlobalFrameCounter) {
2250 		/* Done a timer this frame already! */
2251 		return;
2252 	}
2253 
2254 	controller->keyframe_flags=0;
2255 
2256 	HMTimer_Kernel(controller);
2257 
2258 	/* That handled the timer.  No rendering this time. */
2259 
2260 	DoHModelTimer_Recursion(controller,controller->section_data,controller->sequence_timer,controller->Sequence_Type,controller->Sub_Sequence);
2261 
2262 }
2263 
2264 void ProveHModel(HMODELCONTROLLER *controller, DISPLAYBLOCK *dptr) {
2265 
2266 	/* Simply to verify a new hmodel, and remove junk. */
2267 
2268 	GLOBALASSERT(controller);
2269 	GLOBALASSERT(dptr);
2270 
2271 	Global_HModel_Sptr=dptr->ObStrategyBlock;
2272 
2273 	/* Check the object is in a sensible place. */
2274 	if ( !(	(dptr->ObWorld.vx<1000000 && dptr->ObWorld.vx>-1000000)
2275 		 &&	(dptr->ObWorld.vy<1000000 && dptr->ObWorld.vy>-1000000)
2276 		 &&	(dptr->ObWorld.vz<1000000 && dptr->ObWorld.vz>-1000000)
2277 		 ) ) {
2278 
2279 		LOGDXFMT(("Tests in PROVEHMODEL.\n"));
2280 		if (Global_HModel_Sptr) {
2281 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
2282 		} else {
2283 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
2284 		}
2285 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
2286 
2287 		LOCALASSERT(dptr->ObWorld.vx<1000000 && dptr->ObWorld.vx>-1000000);
2288 		LOCALASSERT(dptr->ObWorld.vy<1000000 && dptr->ObWorld.vy>-1000000);
2289 		LOCALASSERT(dptr->ObWorld.vz<1000000 && dptr->ObWorld.vz>-1000000);
2290 
2291 	}
2292 
2293 	GLOBALASSERT(controller->section_data->my_controller==controller);
2294 
2295 	if (controller->FrameStamp==GlobalFrameCounter) {
2296 		VECTORCH offset;
2297 		/* Want to budge? */
2298 
2299 		offset.vx=dptr->ObWorld.vx-controller->Computed_Position.vx;
2300 		offset.vy=dptr->ObWorld.vy-controller->Computed_Position.vy;
2301 		offset.vz=dptr->ObWorld.vz-controller->Computed_Position.vz;
2302 
2303 		if ((offset.vx!=0)||(offset.vy!=0)||(offset.vz!=0)) {
2304 			/* I reckon you'd be better off taking the budge. */
2305 			Budge_HModel(controller,&offset);
2306 		}
2307 	}
2308 
2309 	if (controller->FrameStamp!=GlobalFrameCounter) {
2310 		controller->keyframe_flags=0;
2311 		HMTimer_Kernel(controller);
2312 	}
2313 	/* That handled the timer.  Now update positions. */
2314 
2315 	Process_Section(controller,controller->section_data,&dptr->ObWorld,&dptr->ObMat,controller->sequence_timer,controller->Sequence_Type,controller->Sub_Sequence,0);
2316 
2317 	controller->FrameStamp=GlobalFrameCounter;
2318 	controller->Computed_Position=dptr->ObWorld;
2319 
2320 	GLOBALASSERT(controller->section_data->flags&section_data_initialised);
2321 
2322 }
2323 
2324 void ProveHModel_Far(HMODELCONTROLLER *controller, STRATEGYBLOCK *sbPtr) {
2325 
2326 	/* Simply to verify a new hmodel, and remove junk. */
2327 
2328 	GLOBALASSERT(controller);
2329 	GLOBALASSERT(sbPtr);
2330 
2331 	Global_HModel_Sptr=sbPtr;
2332 
2333 	GLOBALASSERT(sbPtr->DynPtr);
2334 
2335 	/* Check the object is in a sensible place. */
2336 	if ( !(	(sbPtr->DynPtr->Position.vx<1000000 && sbPtr->DynPtr->Position.vx>-1000000)
2337 		 &&	(sbPtr->DynPtr->Position.vy<1000000 && sbPtr->DynPtr->Position.vy>-1000000)
2338 		 &&	(sbPtr->DynPtr->Position.vz<1000000 && sbPtr->DynPtr->Position.vz>-1000000)
2339 		 ) ) {
2340 
2341 		LOGDXFMT(("Tests in PROVEHMODEL_FAR.\n"));
2342 		if (Global_HModel_Sptr) {
2343 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
2344 		} else {
2345 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
2346 		}
2347 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
2348 
2349 		LOCALASSERT(sbPtr->DynPtr->Position.vx<1000000 && sbPtr->DynPtr->Position.vx>-1000000);
2350 		LOCALASSERT(sbPtr->DynPtr->Position.vy<1000000 && sbPtr->DynPtr->Position.vy>-1000000);
2351 		LOCALASSERT(sbPtr->DynPtr->Position.vz<1000000 && sbPtr->DynPtr->Position.vz>-1000000);
2352 
2353 	}
2354 
2355 	GLOBALASSERT(controller->section_data->my_controller==controller);
2356 
2357 	if (controller->FrameStamp==GlobalFrameCounter) {
2358 		VECTORCH offset;
2359 		/* Want to budge? */
2360 
2361 		offset.vx=controller->Computed_Position.vx-sbPtr->DynPtr->Position.vx;
2362 		offset.vy=controller->Computed_Position.vy-sbPtr->DynPtr->Position.vy;
2363 		offset.vz=controller->Computed_Position.vz-sbPtr->DynPtr->Position.vz;
2364 
2365 		if ((offset.vx!=0)||(offset.vy!=0)||(offset.vz!=0)) {
2366 			/* I reckon you'd be better off taking the budge. */
2367 			Budge_HModel(controller,&offset);
2368 		}
2369 	}
2370 
2371 	if (controller->FrameStamp!=GlobalFrameCounter) {
2372 		controller->keyframe_flags=0;
2373 		HMTimer_Kernel(controller);
2374 	}
2375    /* That handled the timer.  Now update positions. */
2376 
2377 	Process_Section(controller,controller->section_data,&sbPtr->DynPtr->Position,&sbPtr->DynPtr->OrientMat,controller->sequence_timer,controller->Sequence_Type,controller->Sub_Sequence,0);
2378 
2379 	controller->FrameStamp=GlobalFrameCounter;
2380 	controller->Computed_Position=sbPtr->DynPtr->Position;
2381 
2382 	GLOBALASSERT(controller->section_data->flags&section_data_initialised);
2383 
2384 }
2385 
2386 
2387 int Prune_Recursion_Virtual(SECTION_DATA *this_section_data) {
2388 
2389 	int sol;
2390 	SECTION *this_section;
2391 
2392 	/* Work out which SECTION_DATA to use. */
2393 
2394 	this_section=this_section_data->sempai;
2395 
2396 	sol=0;
2397 
2398 	if (this_section->flags&section_has_sparkoflife) sol=1;
2399 
2400 	this_section_data->flags|=section_data_notreal;
2401 
2402 
2403 	if ( (this_section_data->First_Child!=NULL)
2404 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
2405 
2406 		SECTION_DATA *child_list_ptr;
2407 
2408 		child_list_ptr=this_section_data->First_Child;
2409 
2410 		while (child_list_ptr!=NULL) {
2411 			if (Prune_Recursion_Virtual(child_list_ptr)) sol=1;
2412 			child_list_ptr=child_list_ptr->Next_Sibling;
2413 		}
2414 
2415 	}
2416 
2417 	return(sol);
2418 
2419 }
2420 
2421 int Prune_HModel_Virtual(SECTION_DATA *top_section) {
2422 
2423 	SECTION *this_section;
2424 	int sol;
2425 
2426    /* To make top_section, and everything below it, unreal. */
2427    /* Must pass back up the recursion if any section pruned */
2428    /* has the spark of life. */
2429 
2430 	this_section=top_section->sempai;
2431 	sol=0;
2432 
2433 	if (Prune_Recursion_Virtual(top_section)) sol=1;
2434 	top_section->flags|=section_data_terminate_here;
2435 	top_section->gore_timer=0;
2436 
2437 	return(sol);
2438 
2439 }
2440 
2441 void Correlation_Recursion(SECTION_DATA *this_section_data, SECTION_DATA *alt_section_data) {
2442 
2443 	/* Correlate existance. */
2444 
2445 	if (alt_section_data->flags&section_data_notreal) {
2446 		this_section_data->flags|=section_data_notreal;
2447 	}
2448 
2449 	if (alt_section_data->flags&section_data_terminate_here) {
2450 		this_section_data->flags|=section_data_terminate_here;
2451 		this_section_data->gore_timer=0; /* As good a time as any. */
2452 	}
2453 
2454 	/* Now call recursion... */
2455 
2456 	if ((this_section_data->First_Child!=NULL)
2457 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
2458 
2459 		SECTION_DATA *child_list_ptr,*alt_child_list_ptr;
2460 
2461 		child_list_ptr=this_section_data->First_Child;
2462 		alt_child_list_ptr=alt_section_data->First_Child;
2463 
2464 		while (child_list_ptr!=NULL) {
2465 			Correlation_Recursion(child_list_ptr,alt_child_list_ptr);
2466 			child_list_ptr=child_list_ptr->Next_Sibling;
2467 			alt_child_list_ptr=alt_child_list_ptr->Next_Sibling;
2468 		}
2469 	}
2470 }
2471 
2472 void Correlate_HModel_Instances(SECTION_DATA *victim,SECTION_DATA *templat) {
2473 
2474 	GLOBALASSERT(victim->sempai==templat->sempai);
2475 
2476 	/* You'd better not be being silly. */
2477 
2478 	/* The top section must be a false root. */
2479 
2480 	victim->flags|=section_data_false_root;
2481 
2482 	/* Start recursion. */
2483 
2484 	Correlation_Recursion(victim,templat);
2485 }
2486 
2487 void MulQuat(QUAT *q1,QUAT *q2,QUAT *output) {
2488 
2489 	VECTORCH v1,v2,v3;
2490 	int tw;
2491 
2492 	/* Multiply quats... */
2493 
2494 	v1.vx=q1->quatx;
2495 	v1.vy=q1->quaty;
2496 	v1.vz=q1->quatz;
2497 
2498 	v2.vx=q2->quatx;
2499 	v2.vy=q2->quaty;
2500 	v2.vz=q2->quatz;
2501 
2502 	CrossProduct(&v1,&v2,&v3);
2503 
2504 	tw=MUL_FIXED(q1->quatw,q2->quatw);
2505 	tw-=DotProduct(&v1,&v2);
2506 
2507 	v3.vx+=MUL_FIXED(q1->quatw,v2.vx);
2508 	v3.vx+=MUL_FIXED(q2->quatw,v1.vx);
2509 
2510 	v3.vy+=MUL_FIXED(q1->quatw,v2.vy);
2511 	v3.vy+=MUL_FIXED(q2->quatw,v1.vy);
2512 
2513 	v3.vz+=MUL_FIXED(q1->quatw,v2.vz);
2514 	v3.vz+=MUL_FIXED(q2->quatw,v1.vz);
2515 
2516 	output->quatw=tw;
2517 	output->quatx=v3.vx;
2518 	output->quaty=v3.vy;
2519 	output->quatz=v3.vz;
2520 
2521 	QNormalise(output);
2522 
2523 }
2524 
2525 void Slerp(KEYFRAME_DATA *input,int lerp,QUAT *output) {
2526 
2527 	int sclp,sclq;
2528 	int omega=input->omega; //probably faster copying to an int , rather than using the bitfield
2529 	KEYFRAME_DATA* next_frame=input->Next_Frame;
2530 
2531 	/* First check for special case. */
2532 
2533 	if ((lerp<0)||(lerp>=65536)) {
2534 		GLOBALASSERT(lerp>=0);
2535 		GLOBALASSERT(lerp<65536);
2536 	}
2537 
2538 	if (omega==2048) {
2539 		int t1,t2;
2540 
2541 		output->quatx=((int)-input->QOrient.quaty)<<1;
2542 		output->quaty=((int)input->QOrient.quatx)<<1;
2543 		output->quatz=((int)-input->QOrient.quatw)<<1;
2544 		output->quatw=((int)input->QOrient.quatz)<<1;
2545 
2546 		t1=MUL_FIXED((ONE_FIXED-lerp),1024);
2547 		sclp=GetSin(t1);
2548 
2549 		t2=MUL_FIXED(lerp,1024);
2550 		sclq=GetSin(t2);
2551 
2552 		//multiply sclp and sclq by 2 to make up for short quats
2553 		sclp<<=1;
2554 		sclq<<=1;
2555 
2556 		output->quatx=(MUL_FIXED((int)input->QOrient.quatx,sclp))+(MUL_FIXED(output->quatx,sclq));
2557 		output->quaty=(MUL_FIXED((int)input->QOrient.quaty,sclp))+(MUL_FIXED(output->quaty,sclq));
2558 		output->quatz=(MUL_FIXED((int)input->QOrient.quatz,sclp))+(MUL_FIXED(output->quatz,sclq));
2559 		output->quatw=(MUL_FIXED((int)input->QOrient.quatw,sclp))+(MUL_FIXED(output->quatw,sclq));
2560 
2561 	} else {
2562 		if (omega==0) {
2563 			sclp=ONE_FIXED-lerp;
2564 			sclq=lerp;
2565 		} else {
2566 			int t1,t2;
2567 			int oneoversinomega=GetOneOverSin(omega);
2568 
2569 			t1=MUL_FIXED((ONE_FIXED-lerp),omega);
2570 			t2=GetSin(t1);
2571 			sclp=MUL_FIXED(t2,oneoversinomega);
2572 
2573 			t1=MUL_FIXED(lerp,omega);
2574 			t2=GetSin(t1);
2575 			sclq=MUL_FIXED(t2,oneoversinomega);
2576 
2577 		}
2578 		//multiply sclp and sclq by 2 to make up for short quats
2579 		sclp<<=1;
2580 		sclq<<=1;
2581 
2582 		if(input->slerp_to_negative_quat)
2583 		{
2584 			//instead of actually negating the quaternion , negate sclq
2585 			sclq=-sclq;
2586 		}
2587 
2588 		output->quatx=(MUL_FIXED((int)input->QOrient.quatx,sclp))+(MUL_FIXED((int)next_frame->QOrient.quatx,sclq));
2589 		output->quaty=(MUL_FIXED((int)input->QOrient.quaty,sclp))+(MUL_FIXED((int)next_frame->QOrient.quaty,sclq));
2590 		output->quatz=(MUL_FIXED((int)input->QOrient.quatz,sclp))+(MUL_FIXED((int)next_frame->QOrient.quatz,sclq));
2591 		output->quatw=(MUL_FIXED((int)input->QOrient.quatw,sclp))+(MUL_FIXED((int)next_frame->QOrient.quatw,sclq));
2592 	}
2593 
2594 	QNormalise(output);
2595 
2596 }
2597 
2598 void Slerp2(SECTION_DATA *input,int lerp,QUAT *output) {
2599 
2600 	/* Just a different input structure. */
2601 
2602 	int sclp,sclq;
2603 
2604 	/* First check for special case. */
2605 
2606 	GLOBALASSERT(lerp>=0);
2607 	GLOBALASSERT(lerp<65536);
2608 
2609 	if (input->omega==2048) {
2610 		int t1,t2;
2611 
2612 		output->quatx=-input->stored_quat.quaty;
2613 		output->quaty=input->stored_quat.quatx;
2614 		output->quatz=-input->stored_quat.quatw;
2615 		output->quatw=input->stored_quat.quatz;
2616 
2617 		t1=MUL_FIXED((ONE_FIXED-lerp),1024);
2618 		sclp=GetSin(t1);
2619 
2620 		t2=MUL_FIXED(lerp,1024);
2621 		sclq=GetSin(t2);
2622 
2623 		output->quatx=(MUL_FIXED(input->stored_quat.quatx,sclp))+(MUL_FIXED(output->quatx,sclq));
2624 		output->quaty=(MUL_FIXED(input->stored_quat.quaty,sclp))+(MUL_FIXED(output->quaty,sclq));
2625 		output->quatz=(MUL_FIXED(input->stored_quat.quatz,sclp))+(MUL_FIXED(output->quatz,sclq));
2626 		output->quatw=(MUL_FIXED(input->stored_quat.quatw,sclp))+(MUL_FIXED(output->quatw,sclq));
2627 
2628 	} else {
2629 		if ( (input->omega==0) && (input->oneoversinomega==0) ) {
2630 			sclp=ONE_FIXED-lerp;
2631 			sclq=lerp;
2632 		} else {
2633 			int t1,t2;
2634 
2635 			t1=MUL_FIXED((ONE_FIXED-lerp),input->omega);
2636 			t2=GetSin(t1);
2637 			sclp=MUL_FIXED(t2,input->oneoversinomega);
2638 
2639 			t1=MUL_FIXED(lerp,input->omega);
2640 			t2=GetSin(t1);
2641 			sclq=MUL_FIXED(t2,input->oneoversinomega);
2642 
2643 		}
2644 
2645 		output->quatx=(MUL_FIXED(input->stored_quat.quatx,sclp))+(MUL_FIXED(input->target_quat.quatx,sclq));
2646 		output->quaty=(MUL_FIXED(input->stored_quat.quaty,sclp))+(MUL_FIXED(input->target_quat.quaty,sclq));
2647 		output->quatz=(MUL_FIXED(input->stored_quat.quatz,sclp))+(MUL_FIXED(input->target_quat.quatz,sclq));
2648 		output->quatw=(MUL_FIXED(input->stored_quat.quatw,sclp))+(MUL_FIXED(input->target_quat.quatw,sclq));
2649 	}
2650 
2651 	QNormalise(output);
2652 
2653 }
2654 
2655 void Gibbing_Recursion(STRATEGYBLOCK *sbPtr,SECTION_DATA *this_section_data, int probability) {
2656 
2657 	/* General gibbing function. */
2658 
2659 
2660 	/* Recurse. */
2661 
2662 	if ( (this_section_data->First_Child!=NULL)
2663 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
2664 
2665 
2666 		SECTION_DATA *child_list_ptr;
2667 
2668 		child_list_ptr=this_section_data->First_Child;
2669 
2670 		while (child_list_ptr!=NULL) {
2671 
2672 			if ( (child_list_ptr->flags&section_data_terminate_here)==0) {
2673 
2674 				/* Right. Roll some dice... */
2675 				if (((child_list_ptr->sempai->flags&section_flag_never_frag)==0)
2676 					&&((SeededFastRandom()&65535)<probability)) {
2677 					/* Frag this bit... */
2678 					DISPLAYBLOCK *this_debris;
2679 
2680 					this_debris=MakeHierarchicalDebris(sbPtr,child_list_ptr, &child_list_ptr->World_Offset, &child_list_ptr->SecMat,NULL,4);
2681 					/* Oh Dear!  Every section below and including this one becomes... unreal.
2682 					And if any of them contain the spark of life, we need to know. */
2683 
2684 					/* Now, gibb the debris ;-) */
2685 
2686 					if ( (this_debris) &&
2687 						((this_debris->HModelControlBlock->section_data->sempai->flags&section_flag_nofurthergibbing)==0) )  {
2688 						GLOBALASSERT(this_debris->HModelControlBlock);
2689 						GLOBALASSERT(this_debris->HModelControlBlock->section_data);
2690 						Gibbing_Recursion(sbPtr,this_debris->HModelControlBlock->section_data,probability);
2691 					}
2692 
2693 				} else {
2694 
2695 					Gibbing_Recursion(sbPtr,child_list_ptr,probability);
2696 
2697 				}
2698 			}
2699 			child_list_ptr=child_list_ptr->Next_Sibling;
2700 		}
2701 
2702 	}
2703 
2704 }
2705 
2706 void Extreme_Gibbing(STRATEGYBLOCK *sbPtr,SECTION_DATA *this_section_data, int probability) {
2707 
2708 	/* Shell for gibbing. Do gibbing... */
2709 
2710 	Gibbing_Recursion(sbPtr,this_section_data,probability);
2711 
2712 	/* Now frag the body off. */
2713 
2714 	if ( (SeededFastRandom()&65535)<probability) {
2715 		MakeHierarchicalDebris(sbPtr,this_section_data, &this_section_data->World_Offset, &this_section_data->SecMat,NULL,4);
2716 	}
2717 }
2718 
2719 int Change_Controller_Recursion(HMODELCONTROLLER *new_controller,SECTION_DATA *this_section_data) {
2720 
2721 	int wounds;
2722 
2723 	this_section_data->my_controller=new_controller;
2724 	wounds=this_section_data->sempai->flags&section_flags_wounding;
2725 
2726 	/* Now call recursion... */
2727 
2728 	if (this_section_data->First_Child!=NULL) {
2729 
2730 		SECTION_DATA *child_list_ptr;
2731 
2732 		child_list_ptr=this_section_data->First_Child;
2733 
2734 		while (child_list_ptr!=NULL) {
2735 			wounds|=Change_Controller_Recursion(new_controller,child_list_ptr);
2736 			child_list_ptr=child_list_ptr->Next_Sibling;
2737 		}
2738 	}
2739 
2740 	return(wounds);
2741 
2742 }
2743 
2744 int Splice_HModels(HMODELCONTROLLER *new_controller,SECTION_DATA *top_section_data) {
2745 
2746 	/* Real fragging. */
2747 	int wounds;
2748 	SECTION_DATA *new_top_section;
2749 
2750 	/* Init new controller. */
2751 
2752 	wounds=0;
2753 	Global_Controller_Ptr=new_controller;
2754 
2755 	new_controller->Seconds_For_Sequence=ONE_FIXED;
2756 	new_controller->timer_increment=ONE_FIXED;
2757 	/* Seconds_For_Sequence and timer_increment are dealt with elsewhere. */
2758 	new_controller->sequence_timer=0;
2759 	new_controller->Playing=0;
2760 	new_controller->Reversed=0;
2761 	new_controller->Looped=0;
2762 
2763 	new_controller->FrameStamp=-1;
2764 	new_controller->View_FrameStamp=-1;
2765 
2766 	if (top_section_data->my_controller) {
2767 		new_controller->DisableBleeding=top_section_data->my_controller->DisableBleeding;
2768 		new_controller->DisableSounds=top_section_data->my_controller->DisableSounds;
2769 	} else {
2770 		new_controller->DisableBleeding=0;
2771 		new_controller->DisableSounds=0;
2772 	}
2773 	/* Probably set on return, but never mind. */
2774 	new_controller->LockTopSection=0;
2775 	new_controller->ZeroRootDisplacement=0;
2776 	new_controller->ZeroRootRotation=0;
2777 	new_controller->AT_sequence_timer=0;
2778 
2779 	/* Copy them over, splice them over, or ignore BY HAND. */
2780 	new_controller->Deltas=NULL;
2781 
2782 	new_controller->keyframe_flags=0;
2783 
2784 	/* Every time a section is preprocessed, it must generate a section_data for
2785 	itself, and clip it to the last section_data that was generated. */
2786 
2787 	/* Create a new top section... */
2788 
2789 	new_top_section=(SECTION_DATA *)AllocateMem(sizeof(SECTION_DATA));
2790 	GLOBALASSERT(new_top_section);
2791 
2792 	/* Now.  Copy the old top_section_data into the new top section. */
2793 
2794 	*new_top_section=*top_section_data;
2795 
2796 	top_section_data->tac_ptr=NULL;
2797 
2798 	/* Correct for new parentage. */
2799 	new_top_section->My_Parent=NULL;
2800 
2801 	if (new_top_section->First_Child!=NULL) {
2802 
2803 		SECTION_DATA *child_ptr;
2804 
2805 		child_ptr=new_top_section->First_Child;
2806 
2807 		while (child_ptr!=NULL) {
2808 
2809 			child_ptr->My_Parent=new_top_section;
2810 
2811 			child_ptr=child_ptr->Next_Sibling;
2812 		}
2813 
2814 	}
2815 
2816 
2817 	/* ...and top_section_data gets no children. */
2818 
2819 	top_section_data->First_Child=NULL;
2820 
2821 	/* Set flags. */
2822 
2823 	new_top_section->flags=top_section_data->flags&(~section_data_initialised);
2824 	top_section_data->flags|=section_data_terminate_here;
2825 	top_section_data->gore_timer=0;
2826 	top_section_data->flags|=section_data_notreal;
2827 	new_top_section->flags|=section_data_false_root;
2828 	new_top_section->gore_timer=0;
2829 
2830 	/* Connect to controller. */
2831 
2832 	new_controller->section_data=new_top_section;
2833 	new_controller->Root_Section=new_top_section->sempai;
2834 
2835 	wounds=Change_Controller_Recursion(new_controller,new_top_section);
2836 
2837 	return(wounds);
2838 
2839 }
2840 
2841 SECTION_DATA *GetSectionData_Recursion(SECTION_DATA *this_section_data,char *name) {
2842 
2843 	SECTION_DATA *sdptr;
2844 
2845 	sdptr=NULL;
2846 
2847 	if (strcmp(name,this_section_data->sempai->Section_Name)==0) {
2848 		/* We are that section! */
2849 		return(this_section_data);
2850 	}
2851 
2852 	/* Now call recursion... */
2853 
2854 	if (this_section_data->First_Child!=NULL) {
2855 
2856 		SECTION_DATA *child_list_ptr;
2857 
2858 		child_list_ptr=this_section_data->First_Child;
2859 
2860 		while (child_list_ptr!=NULL) {
2861 			sdptr=GetSectionData_Recursion(child_list_ptr,name);
2862 			child_list_ptr=child_list_ptr->Next_Sibling;
2863 
2864 			if (sdptr) {
2865 				return(sdptr); /* We got one! */
2866 			}
2867 		}
2868 	}
2869 
2870 	return(sdptr);
2871 
2872 }
2873 
2874 SECTION_DATA *GetThisSectionData(SECTION_DATA *root,char *name) {
2875 
2876 	if ((root==NULL)||(name==NULL)) {
2877 		return(NULL);
2878 	}
2879 
2880 	return(GetSectionData_Recursion(root,name));
2881 
2882 }
2883 
2884 SECTION *GetSection_Recursion(SECTION *this_section,char *name) {
2885 
2886 	SECTION *sptr;
2887 
2888 	sptr=NULL;
2889 
2890 	if (strcmp(name,this_section->Section_Name)==0) {
2891 		/* We are that section! */
2892 		return(this_section);
2893 	}
2894 
2895 	/* Now call recursion... */
2896 
2897 	if (this_section->Children!=NULL) {
2898 
2899 		SECTION **child_list_ptr;
2900 
2901 		child_list_ptr=this_section->Children;
2902 
2903 		while (*child_list_ptr!=NULL) {
2904 
2905 			sptr=GetSection_Recursion(*child_list_ptr,name);
2906 			child_list_ptr++;
2907 
2908 			if (sptr) {
2909 				return(sptr); /* We got one! */
2910 			}
2911 		}
2912 	}
2913 
2914 	return(sptr);
2915 
2916 }
2917 
2918 SECTION *GetThisSection(SECTION *root,char *name) {
2919 
2920 	if ((root==NULL)||(name==NULL)) {
2921 		return(NULL);
2922 	}
2923 
2924 	return(GetSection_Recursion(root,name));
2925 
2926 }
2927 
2928 void MatToQuat (MATRIXCH *m, QUAT *quat)
2929 {
2930 	const int X=0;
2931 	const int Y=1;
2932 	const int Z=2;
2933 	const int W=3;
2934 
2935 	double mat[4][4];
2936 	double	q[4];
2937 
2938 	int i,j,k;
2939 	double tr,s;
2940 
2941 	int const nxt[3] =
2942 	{
2943 		//Y,Z,X
2944 		1,2,0
2945 	};
2946 
2947 	// we could try transposing the matrix here
2948 
2949 //	TransposeMatrixCH(m);
2950 
2951 	mat[0][0] = (double) m->mat11 / ONE_FIXED;
2952 	mat[1][0] = (double) m->mat21 / ONE_FIXED;
2953 	mat[2][0] = (double) m->mat31 / ONE_FIXED;
2954 	mat[0][1] = (double) m->mat12 / ONE_FIXED;
2955 	mat[1][1] = (double) m->mat22 / ONE_FIXED;
2956 	mat[2][1] = (double) m->mat32 / ONE_FIXED;
2957 	mat[0][2] = (double) m->mat13 / ONE_FIXED;
2958 	mat[1][2] = (double) m->mat23 / ONE_FIXED;
2959 	mat[2][2] = (double) m->mat33 / ONE_FIXED;
2960 
2961 
2962 
2963 	tr= mat[0][0]+mat[1][1]+mat[2][2];
2964 
2965 	if (tr>0.0)
2966 	{
2967 		s=sqrt(tr+1.0);
2968 		q[W] = s*0.5;
2969 		s = 0.5/s;
2970 
2971 		q[X] = (mat[1][2] - mat[2][1])*s;
2972 		q[Y] = (mat[2][0] - mat[0][2])*s;
2973 		q[Z] = (mat[0][1] - mat[1][0])*s;
2974 	}
2975 	else
2976 	{
2977 		i = X;
2978 		if (mat[Y][Y] > mat[X][X]) i = Y;
2979 		if (mat[Z][Z] > mat[i][i]) i = Z;
2980 		j = nxt[i]; k = nxt[j];
2981 
2982 		s = sqrt((mat[i][i] - (mat[j][j]+mat[k][k])) + 1.0);
2983 
2984 		q[i] = s*0.5;
2985 		s = 0.5/s;
2986 		q[W] = (mat[j][k] - mat[k][j])*s;
2987 		q[j] = (mat[i][j] + mat[j][i])*s;
2988 		q[k] = (mat[i][k] + mat[k][i])*s;
2989 	}
2990 
2991 	quat->quatx = (int) ((double) q[X]*65536.0);
2992 	quat->quaty = (int) ((double) q[Y]*65536.0);
2993 	quat->quatz = (int) ((double) q[Z]*65536.0);
2994 	quat->quatw = (int) ((double) q[W]*65536.0);
2995 
2996 	quat->quatw = -quat->quatw;
2997 
2998 	QNormalise(quat);
2999 
3000 }
3001 
3002 void Init_Tweening_Recursion(SECTION_DATA *this_section_data, int target_sequence_type,int target_subsequence, int seconds_for_tweening, int backwards) {
3003 
3004 	SEQUENCE *sequence_ptr;
3005 
3006 	/* Firstly, store current state. */
3007 
3008 	this_section_data->stored_offset=this_section_data->Offset;
3009 	MatToQuat(&this_section_data->RelSecMat,&this_section_data->stored_quat);
3010 
3011 	/* Now, get target state. */
3012 
3013 	sequence_ptr=GetSequencePointer(target_sequence_type,target_subsequence,this_section_data->sempai);
3014 
3015 	if (backwards) {
3016 		KEYFRAME_DATA *current_frame;
3017 		/* Deduce last frame. */
3018 		current_frame=sequence_ptr->last_frame;
3019 
3020 		/* Must now have the last frame. */
3021 		GetKeyFrameOffset(current_frame,&this_section_data->target_offset);
3022 
3023 		CopyShortQuatToInt(&current_frame->QOrient,&this_section_data->target_quat);
3024 	} else {
3025 		GetKeyFrameOffset(sequence_ptr->first_frame,&this_section_data->target_offset);
3026 		CopyShortQuatToInt(&sequence_ptr->first_frame->QOrient,&this_section_data->target_quat);
3027 	}
3028 
3029 	/* Preprocess slerp values. */
3030 
3031 	{
3032 
3033 		this_section_data->delta_offset.vx=this_section_data->target_offset.vx-this_section_data->stored_offset.vx;
3034 		this_section_data->delta_offset.vy=this_section_data->target_offset.vy-this_section_data->stored_offset.vy;
3035 		this_section_data->delta_offset.vz=this_section_data->target_offset.vz-this_section_data->stored_offset.vz;
3036 
3037 	}
3038 
3039 	{
3040 		int cosom,sinom;
3041 		QUAT *this_quat, *next_quat;
3042 
3043 		this_quat=&this_section_data->stored_quat;
3044 		next_quat=&this_section_data->target_quat;
3045 
3046 
3047 		cosom=QDot(this_quat,next_quat);
3048 
3049 		if (cosom<0) {
3050 			next_quat->quatx=-next_quat->quatx;
3051 			next_quat->quaty=-next_quat->quaty;
3052 			next_quat->quatz=-next_quat->quatz;
3053 			next_quat->quatw=-next_quat->quatw;
3054 
3055 			cosom=-cosom;
3056 		}
3057 
3058 
3059 		this_section_data->omega=ArcCos(cosom);
3060 		sinom=GetSin(this_section_data->omega);
3061 		if (sinom) {
3062 			this_section_data->oneoversinomega=GetOneOverSin(this_section_data->omega);
3063 		} else {
3064 			/* Yuk. */
3065 			this_section_data->omega=0;
3066 			this_section_data->oneoversinomega=0;
3067 		}
3068 
3069 		GLOBALASSERT(seconds_for_tweening>0);
3070 		this_section_data->oneovertweeninglength=DIV_FIXED(ONE_FIXED,seconds_for_tweening);
3071 
3072 	}
3073 
3074 	/* Init fields... I guess. */
3075 
3076 	this_section_data->current_sequence=sequence_ptr;
3077 	this_section_data->current_keyframe=sequence_ptr->first_frame;
3078 	this_section_data->accumulated_timer=0;
3079 	this_section_data->freezeframe_timer=-1;
3080 	this_section_data->lastframe_timer=0;
3081 	this_section_data->gore_timer=0; /* As good a time as any. */
3082 
3083 	this_section_data->Tweening=1;
3084 
3085 	/* Animation? */
3086 
3087 	/* Nah. */
3088 
3089 	/* Recurse. */
3090 
3091 	if ( (this_section_data->First_Child!=NULL)
3092 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
3093 		/* Respect the terminator! */
3094 
3095 		SECTION_DATA *child_list_ptr;
3096 
3097 		child_list_ptr=this_section_data->First_Child;
3098 
3099 		while (child_list_ptr!=NULL) {
3100 			Init_Tweening_Recursion(child_list_ptr,target_sequence_type,target_subsequence,seconds_for_tweening,backwards);
3101 			child_list_ptr=child_list_ptr->Next_Sibling;
3102 		}
3103 
3104 	}
3105 
3106 }
3107 
3108 
3109 void InitHModelTweening(HMODELCONTROLLER *controller, int seconds_for_tweening,
3110 	int target_sequence_type, int target_subsequence, int target_seconds_for_sequence, int loop) {
3111 
3112 	/* Just set it up... */
3113 	GLOBALASSERT(target_seconds_for_sequence);
3114 
3115 	controller->Sequence_Type=target_sequence_type;
3116 	controller->Sub_Sequence=target_subsequence;
3117 	controller->Seconds_For_Sequence=seconds_for_tweening;
3118 	controller->timer_increment=DIV_FIXED(ONE_FIXED,controller->Seconds_For_Sequence);
3119 	controller->sequence_timer=0;
3120 	controller->Playing=1;
3121 	controller->Reversed=0;
3122 	controller->Looped=1;
3123 
3124 	controller->After_Tweening_Sequence_Type=target_sequence_type;
3125 	controller->After_Tweening_Sub_Sequence=target_subsequence;
3126 	controller->AT_seconds_for_sequence=target_seconds_for_sequence;
3127 	controller->AT_sequence_timer=0;
3128 	controller->Tweening=Controller_Tweening;
3129 	if (loop) {
3130 		controller->LoopAfterTweening=1;
3131 	} else {
3132 		controller->LoopAfterTweening=0;
3133 	}
3134 	controller->StopAfterTweening=0;
3135 	controller->ElevationTweening=0;
3136 
3137 	/* Recurse though hierarchy, setting up all the section_data sequence stores? */
3138 
3139 	Init_Tweening_Recursion(controller->section_data, target_sequence_type, target_subsequence,seconds_for_tweening,0);
3140 
3141 }
3142 
3143 void InitHModelTweening_Backwards(HMODELCONTROLLER *controller, int seconds_for_tweening,
3144 	int target_sequence_type, int target_subsequence, int target_seconds_for_sequence, int loop) {
3145 
3146 	/* Ooh, yuck. */
3147 	GLOBALASSERT(target_seconds_for_sequence);
3148 
3149 	controller->Sequence_Type=target_sequence_type;
3150 	controller->Sub_Sequence=target_subsequence;
3151 	controller->Seconds_For_Sequence=seconds_for_tweening;
3152 	controller->timer_increment=DIV_FIXED(ONE_FIXED,controller->Seconds_For_Sequence);
3153 	controller->sequence_timer=0;
3154 	controller->Playing=1;
3155 	controller->Reversed=1;
3156 	controller->Looped=1;
3157 
3158 	controller->After_Tweening_Sequence_Type=target_sequence_type;
3159 	controller->After_Tweening_Sub_Sequence=target_subsequence;
3160 	controller->AT_seconds_for_sequence=target_seconds_for_sequence;
3161 	controller->AT_sequence_timer=(ONE_FIXED-1);
3162 	controller->Tweening=Controller_Tweening;
3163 	if (loop) {
3164 		controller->LoopAfterTweening=1;
3165 	} else {
3166 		controller->LoopAfterTweening=0;
3167 	}
3168 	controller->StopAfterTweening=0;
3169 	controller->ElevationTweening=0;
3170 
3171 	/* Recurse though hierarchy, setting up all the section_data sequence stores? */
3172 
3173 	Init_Tweening_Recursion(controller->section_data, target_sequence_type, target_subsequence,seconds_for_tweening,1);
3174 
3175 }
3176 
3177 void ReSnap(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data, int elevation) {
3178 
3179 	SEQUENCE *sequence_ptr;
3180 	/* In this procedure, we have to get the new target quat and offset. */
3181 
3182 	sequence_ptr=GetSequencePointer(controller->After_Tweening_Sequence_Type,controller->After_Tweening_Sub_Sequence,this_section_data->sempai);
3183 
3184 	#if 0
3185 	if (controller->Reversed) {
3186 		/* We're in a backwards tween. */
3187 		KEYFRAME_DATA *current_frame;
3188 		/* Deduce last frame. */
3189 		current_frame=sequence_ptr->last_frame;
3190 
3191 		/* Must now have the last frame. */
3192 		this_section_data->target_offset=current_frame->Offset;
3193 		CopyShortQuatToInt(&current_frame->QOrient,&this_section_data->target_quat);
3194 	} else {
3195 		this_section_data->target_offset=sequence_ptr->first_frame->Offset;
3196 		CopyShortQuatToInt(&sequence_ptr->first_frame->QOrient,&this_section_data->target_quat);
3197 	}
3198 	#else
3199 	/* Now, irritatingly, we have to put our faith in AT_sequence_timer. */
3200 	if (controller->AT_sequence_timer==(ONE_FIXED-1)) {
3201 		/* We're in a backwards tween. */
3202 		KEYFRAME_DATA *current_frame;
3203 		/* Deduce last frame. */
3204 		current_frame=sequence_ptr->last_frame;
3205 
3206 		/* Must now have the last frame. */
3207 		GetKeyFrameOffset(current_frame,&this_section_data->target_offset);
3208 		CopyShortQuatToInt(&current_frame->QOrient,&this_section_data->target_quat);
3209 	} else if (controller->AT_sequence_timer==0) {
3210 		GetKeyFrameOffset(sequence_ptr->first_frame,&this_section_data->target_offset);
3211 		CopyShortQuatToInt(&sequence_ptr->first_frame->QOrient,&this_section_data->target_quat);
3212 	} else {
3213 		int a,working_timer,lerp;
3214 		KEYFRAME_DATA *this_frame,*next_frame;
3215 
3216 		this_frame=sequence_ptr->first_frame;
3217 		GLOBALASSERT(this_frame);
3218 		working_timer=controller->AT_sequence_timer;
3219 
3220 		a=0; /* Status flag... */
3221 
3222 		while (a==0) {
3223 			if (this_frame->last_frame) {
3224 				/* Low framerate loop? */
3225 				next_frame=sequence_ptr->first_frame;
3226 			}
3227 			else{
3228 				next_frame=this_frame->Next_Frame;
3229 			}
3230 
3231 			if (working_timer>=this_frame->Sequence_Length) {
3232 				/* We've gone beyond this frame: get next keyframe. */
3233 				working_timer-=this_frame->Sequence_Length;
3234 				/* Advance frame... */
3235 				this_frame=next_frame;
3236 			} else {
3237 				a=1; /* Exit loop with success. */
3238 			}
3239 			/* Better make sure the last 'frame' has 65536 length... */
3240 		}
3241 		GLOBALASSERT(working_timer>=0);
3242 		/* Now we should have a frame and a timer. */
3243 
3244 		lerp=MUL_FIXED(working_timer,this_frame->oneoversequencelength);
3245 
3246 		GetKeyFrameOffset(this_frame,&this_section_data->target_offset);
3247 
3248 		if(next_frame->shift_offset)
3249 		{
3250 			VECTORCH next_offset;
3251 			GetKeyFrameOffset(next_frame,&next_offset);
3252 			this_section_data->target_offset.vx+=MUL_FIXED(next_offset.vx - this_section_data->target_offset.vx,lerp);
3253 			this_section_data->target_offset.vy+=MUL_FIXED(next_offset.vy - this_section_data->target_offset.vy,lerp);
3254 			this_section_data->target_offset.vz+=MUL_FIXED(next_offset.vz - this_section_data->target_offset.vz,lerp);
3255 		}
3256 		else
3257 		{
3258 			this_section_data->target_offset.vx+=MUL_FIXED((int)next_frame->Offset_x - this_section_data->target_offset.vx,lerp);
3259 			this_section_data->target_offset.vy+=MUL_FIXED((int)next_frame->Offset_y - this_section_data->target_offset.vy,lerp);
3260 			this_section_data->target_offset.vz+=MUL_FIXED((int)next_frame->Offset_z - this_section_data->target_offset.vz,lerp);
3261 		}
3262 
3263 		/* Now deal with orientation. */
3264 
3265 		Slerp(this_frame,lerp,&this_section_data->target_quat);
3266 
3267 	}
3268 	#endif
3269 
3270 	if (elevation) {
3271 
3272 		/* Elevation gone! Now deltas? */
3273 		{
3274 			DELTA_CONTROLLER *dcon;
3275 			QUAT elevation_quat,temp_quat;
3276 			VECTORCH elevation_offset;
3277 
3278 			dcon=controller->Deltas;
3279 
3280 			while (dcon) {
3281 				if (dcon->Active) {
3282 					Process_Delta_Controller(this_section_data,dcon,&elevation_offset,&elevation_quat);
3283 					this_section_data->target_offset.vx+=elevation_offset.vx;
3284 					this_section_data->target_offset.vy+=elevation_offset.vy;
3285 					this_section_data->target_offset.vz+=elevation_offset.vz;
3286 
3287 					temp_quat.quatw=this_section_data->target_quat.quatw;
3288 					temp_quat.quatx=this_section_data->target_quat.quatx;
3289 					temp_quat.quaty=this_section_data->target_quat.quaty;
3290 					temp_quat.quatz=this_section_data->target_quat.quatz;
3291 
3292 					MulQuat(&elevation_quat,&temp_quat,&this_section_data->target_quat);
3293 				}
3294 				dcon=dcon->next_controller;
3295 			}
3296 		}
3297 
3298 	}
3299 
3300 	/* Now recompute values. */
3301 
3302 	{
3303 
3304 		this_section_data->delta_offset.vx=this_section_data->target_offset.vx-this_section_data->stored_offset.vx;
3305 		this_section_data->delta_offset.vy=this_section_data->target_offset.vy-this_section_data->stored_offset.vy;
3306 		this_section_data->delta_offset.vz=this_section_data->target_offset.vz-this_section_data->stored_offset.vz;
3307 
3308 	}
3309 
3310 	{
3311 		int cosom,sinom;
3312 		QUAT *this_quat, *next_quat;
3313 
3314 		this_quat=&this_section_data->stored_quat;
3315 		next_quat=&this_section_data->target_quat;
3316 
3317 
3318 		cosom=QDot(this_quat,next_quat);
3319 
3320 		if (cosom<0) {
3321 			next_quat->quatx=-next_quat->quatx;
3322 			next_quat->quaty=-next_quat->quaty;
3323 			next_quat->quatz=-next_quat->quatz;
3324 			next_quat->quatw=-next_quat->quatw;
3325 			cosom=-cosom;
3326 		}
3327 
3328 
3329 		this_section_data->omega=ArcCos(cosom);
3330 		sinom=GetSin(this_section_data->omega);
3331 		if (sinom) {
3332 			this_section_data->oneoversinomega=GetOneOverSin(this_section_data->omega);
3333 		} else {
3334 			/* Yuk. */
3335 			this_section_data->omega=0;
3336 			this_section_data->oneoversinomega=0;
3337 		}
3338 
3339 	}
3340 
3341 }
3342 
3343 void Analyse_Tweening_Data(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,int base_timer,VECTORCH *output_offset,MATRIXCH *output_matrix) {
3344 
3345 	QUAT output_quat;
3346 	int working_timer,lerp;
3347 
3348 	/* Go go gadget tweening. */
3349 
3350 	/* There is only one frame. */
3351 
3352 	working_timer=base_timer;
3353 	/* Tests: can't be too careful. */
3354 	if (working_timer>=65536) working_timer=65535;
3355 	if (working_timer<0) working_timer=0;
3356 
3357 	/* Now, a lot like the old way, but we shouldn't need to loop more than once. */
3358 	/* If we do, we have a game framerate slower than the anim framerate. */
3359 
3360 	if (controller->Deltas) {
3361 		/* Resnap with elevation. */
3362 		ReSnap(controller,this_section_data,1);
3363 
3364 		controller->ElevationTweening=1;
3365 
3366 	} else if (controller->ElevationTweening) {
3367 		/* It's all messed up now.  'Resnap' WITHOUT elevation. */
3368 
3369 		ReSnap(controller,this_section_data,0);
3370 
3371 		controller->ElevationTweening=0;
3372 
3373 	}
3374 
3375 	/* Now, this_frame and next_frame are set, and working_timer<this_frame->Sequence_Length. */
3376 
3377 	output_offset->vx=(MUL_FIXED(this_section_data->delta_offset.vx,working_timer))+this_section_data->stored_offset.vx;
3378 	output_offset->vy=(MUL_FIXED(this_section_data->delta_offset.vy,working_timer))+this_section_data->stored_offset.vy;
3379 	output_offset->vz=(MUL_FIXED(this_section_data->delta_offset.vz,working_timer))+this_section_data->stored_offset.vz;
3380 
3381 	/* Now deal with orientation. */
3382 
3383 	lerp=working_timer;
3384 
3385 	Slerp2(this_section_data,lerp,&output_quat);
3386 
3387 	/* NO elevation! */
3388 
3389 	QuatToMat(&output_quat,output_matrix);
3390 
3391 	/* Just to make sure. */
3392 	MNormalise(output_matrix);
3393 }
3394 
3395 
3396 /* KJL 16:51:20 10/02/98 - Heat source stuff */
3397 
3398 int FindHeatSourcesInHModel(DISPLAYBLOCK *dispPtr)
3399 {
3400 	HMODELCONTROLLER *controllerPtr = dispPtr->HModelControlBlock;
3401 
3402 	LOCALASSERT(controllerPtr);
3403 
3404 	/* KJL 16:36:12 10/02/98 - check positions are up to date */
3405 	ProveHModel(controllerPtr,dispPtr);
3406 
3407 	NumberOfHeatSources=0;
3408 
3409 	/* KJL 16:36:25 10/02/98 - process model */
3410 	FindHeatSource_Recursion(controllerPtr,controllerPtr->section_data);
3411 
3412 	return 0;
3413 }
3414 
3415 static void FindHeatSource_Recursion(HMODELCONTROLLER *controllerPtr, SECTION_DATA *sectionDataPtr)
3416 {
3417 	/* KJL 16:29:40 10/02/98 - Recurse through hmodel */
3418 	if ((sectionDataPtr->First_Child!=NULL)&&(!(sectionDataPtr->flags&section_data_terminate_here)))
3419 	{
3420 		SECTION_DATA *childSectionPtr = sectionDataPtr->First_Child;
3421 
3422 		while (childSectionPtr!=NULL)
3423 		{
3424 			LOCALASSERT(childSectionPtr->My_Parent==sectionDataPtr);
3425 
3426 			FindHeatSource_Recursion(controllerPtr,childSectionPtr);
3427 			childSectionPtr=childSectionPtr->Next_Sibling;
3428 		}
3429 	}
3430 
3431 	/* KJL 16:30:03 10/02/98 - record heat source */
3432 	if (sectionDataPtr->sempai->flags&section_flag_heatsource)
3433 	{
3434 		/* KJL 16:36:58 10/02/98 - currently just position; could have size, orientation, etc. */
3435 		HeatSourceList[NumberOfHeatSources].Position.vx = sectionDataPtr->World_Offset.vx;
3436 		HeatSourceList[NumberOfHeatSources].Position.vy = sectionDataPtr->World_Offset.vy;
3437 		HeatSourceList[NumberOfHeatSources].Position.vz = sectionDataPtr->World_Offset.vz;
3438 		TranslatePointIntoViewspace(&(HeatSourceList[NumberOfHeatSources].Position));
3439 		NumberOfHeatSources++;
3440 	}
3441 }
3442 
3443 DELTA_CONTROLLER *Get_Delta_Sequence(HMODELCONTROLLER *controller,char *id) {
3444 
3445 	DELTA_CONTROLLER *delta_controller;
3446 
3447 	/* Get the controller that matches id. */
3448 
3449 	delta_controller=controller->Deltas;
3450 
3451 	while (delta_controller) {
3452 
3453 		if (strcmp(id,delta_controller->id)==0) {
3454 			break;
3455 		}
3456 		delta_controller=delta_controller->next_controller;
3457 
3458 	}
3459 	return(delta_controller);
3460 }
3461 
3462 void Remove_Delta_Sequence(HMODELCONTROLLER *controller,char *id) {
3463 
3464 	DELTA_CONTROLLER *delta_controller;
3465 	DELTA_CONTROLLER **source;
3466 
3467 	delta_controller=controller->Deltas;
3468 	source=&controller->Deltas;
3469 
3470 	while (delta_controller) {
3471 
3472 		if (strcmp(id,delta_controller->id)==0) {
3473 			break;
3474 		}
3475 		source=&delta_controller->next_controller;
3476 		delta_controller=delta_controller->next_controller;
3477 
3478 	}
3479 
3480 	if (delta_controller) {
3481 		/* Remove it. */
3482 		*source=delta_controller->next_controller;
3483 
3484 		DeallocateMem(delta_controller->id);
3485 		DeallocateMem(delta_controller);
3486 	}
3487 }
3488 
3489 DELTA_CONTROLLER *Add_Delta_Sequence(HMODELCONTROLLER *controller,char *id,int sequence_type,int sub_sequence, int seconds_for_sequence) {
3490 
3491 	KEYFRAME_DATA *sequence_start;
3492 	SEQUENCE *this_sequence;
3493 	DELTA_CONTROLLER *delta_controller;
3494 
3495 	/* Create a new delta sequence. */
3496 
3497 	delta_controller=(DELTA_CONTROLLER *)AllocateMem(sizeof(DELTA_CONTROLLER));
3498 
3499 	GLOBALASSERT(delta_controller);
3500 
3501 	delta_controller->next_controller=controller->Deltas;
3502 	controller->Deltas=delta_controller;
3503 
3504 	delta_controller->id = AllocateMem(strlen(id)+1);
3505 	strcpy(delta_controller->id,id);
3506 
3507 	delta_controller->sequence_type=sequence_type;
3508 	delta_controller->sub_sequence=sub_sequence;
3509 	delta_controller->timer=0;
3510 	delta_controller->lastframe_timer=-1;
3511 	delta_controller->Looped=0;
3512 	delta_controller->Playing=0;
3513 	delta_controller->Active=1; /* By default. */
3514 	delta_controller->my_hmodel_controller=controller;
3515 
3516 	this_sequence=GetSequencePointer(sequence_type,sub_sequence,delta_controller->my_hmodel_controller->Root_Section);
3517 	sequence_start=this_sequence->first_frame;
3518 	GLOBALASSERT(sequence_start);
3519 
3520 	if (seconds_for_sequence>=0) {
3521 		delta_controller->seconds_for_sequence=seconds_for_sequence; // Special case, 0 is legal.
3522 	} else {
3523 		delta_controller->seconds_for_sequence=this_sequence->Time;
3524 		if (delta_controller->seconds_for_sequence<=0) {
3525 			delta_controller->seconds_for_sequence=ONE_FIXED;
3526 			/* Might want to assert here? */
3527 		}
3528 	}
3529 
3530 	if (delta_controller->seconds_for_sequence) {
3531 		delta_controller->timer_increment=DIV_FIXED(ONE_FIXED,delta_controller->seconds_for_sequence);
3532 	} else {
3533 		delta_controller->timer_increment=0;
3534 	}
3535 
3536 	return(delta_controller);
3537 
3538 }
3539 
3540 void Start_Delta_Sequence(DELTA_CONTROLLER *delta_controller,int sequence_type,int sub_sequence,int seconds_for_sequence) {
3541 
3542 	KEYFRAME_DATA *sequence_start;
3543 	SEQUENCE *this_sequence;
3544 	GLOBALASSERT(delta_controller);
3545 
3546 	/* Again, you must start it and loop it by hand. */
3547 
3548 	delta_controller->sequence_type=sequence_type;
3549 	delta_controller->sub_sequence=sub_sequence;
3550 	delta_controller->timer=0;
3551 	delta_controller->Looped=0;
3552 	delta_controller->Playing=0;
3553 
3554 	this_sequence=GetSequencePointer(sequence_type,sub_sequence,delta_controller->my_hmodel_controller->Root_Section);
3555 	sequence_start=this_sequence->first_frame;
3556 	GLOBALASSERT(sequence_start);
3557 
3558 	if (seconds_for_sequence>=0) {
3559 		delta_controller->seconds_for_sequence=seconds_for_sequence; // Special case, 0 is legal.
3560 	} else {
3561 		delta_controller->seconds_for_sequence=this_sequence->Time;
3562 		if (delta_controller->seconds_for_sequence<=0) {
3563 			delta_controller->seconds_for_sequence=ONE_FIXED;
3564 			/* Might want to assert here? */
3565 		}
3566 	}
3567 
3568 	if (delta_controller->seconds_for_sequence) {
3569 		delta_controller->timer_increment=DIV_FIXED(ONE_FIXED,delta_controller->seconds_for_sequence);
3570 	} else {
3571 		delta_controller->timer_increment=0;
3572 	}
3573 
3574 }
3575 
3576 void Delta_Sequence_ChangeSpeed(DELTA_CONTROLLER *delta_controller,int seconds_for_sequence) {
3577 
3578 	GLOBALASSERT(delta_controller);
3579 
3580 	delta_controller->seconds_for_sequence=seconds_for_sequence; // Special case.
3581 
3582 	if (delta_controller->seconds_for_sequence) {
3583 		delta_controller->timer_increment=DIV_FIXED(ONE_FIXED,delta_controller->seconds_for_sequence);
3584 	} else {
3585 		delta_controller->timer_increment=0;
3586 	}
3587 
3588 }
3589 
3590 SECTION *Get_Corresponding_Section(SECTION **List_Ptr,char *Name) {
3591 
3592 	SECTION **child_list_ptr=List_Ptr;
3593 
3594 	while (*child_list_ptr!=NULL) {
3595 		if (strcmp((*child_list_ptr)->Section_Name,Name)==0) {
3596 			break;
3597 		}
3598 		child_list_ptr++;
3599 	}
3600 
3601 	return(*child_list_ptr);
3602 }
3603 
3604 SECTION_DATA *GetThisSectionData_FromChildrenOnly(SECTION_DATA *parent,char *name) {
3605 
3606 	SECTION_DATA *sdptr;
3607 
3608 	if ((parent==NULL)||(name==NULL)) {
3609 		return(NULL);
3610 	}
3611 
3612 	sdptr=NULL;
3613 
3614 	if (parent->First_Child!=NULL) {
3615 
3616 		SECTION_DATA *child_list_ptr;
3617 
3618 		child_list_ptr=parent->First_Child;
3619 
3620 		while (child_list_ptr!=NULL) {
3621 
3622 			if (strcmp(name,child_list_ptr->sempai->Section_Name)==0) {
3623 				/* Got it. */
3624 				return(child_list_ptr);
3625 			}
3626 
3627 			child_list_ptr=child_list_ptr->Next_Sibling;
3628 		}
3629 	}
3630 
3631 	return(sdptr);
3632 
3633 }
3634 
3635 void Transmogrification_Recursion(STRATEGYBLOCK *sbPtr,HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,SECTION *new_template, SECTION *old_template, int frag, int newsections, int regrowsections) {
3636 
3637 	/* Doesn't really matter which tree we're walking... does it? */
3638 
3639 	GLOBALASSERT(new_template);
3640 	GLOBALASSERT(old_template);
3641 	GLOBALASSERT(strcmp(new_template->Section_Name,old_template->Section_Name)==0);
3642 
3643 	GLOBALASSERT(this_section_data);
3644 
3645 	if ( (new_template->Children!=NULL) && (old_template->Children!=NULL) )  {
3646 		/* Complex.  I'd really like to walk both at the same time. */
3647 		SECTION **new_child_list_ptr,**old_child_list_ptr;
3648 
3649 		new_child_list_ptr=new_template->Children;
3650 		old_child_list_ptr=old_template->Children;
3651 
3652 		/* First, let's walk the new template. */
3653 		while (*new_child_list_ptr!=NULL) {
3654 
3655 			SECTION *corresponding_section;
3656 
3657 			corresponding_section=Get_Corresponding_Section(old_child_list_ptr,(*new_child_list_ptr)->Section_Name);
3658 
3659 			if (corresponding_section!=NULL) {
3660 				/* Section also exists in old template.  Deal with it and recurse. */
3661 				SECTION_DATA *child_section_data;
3662 
3663 				child_section_data=GetThisSectionData_FromChildrenOnly(this_section_data,(*new_child_list_ptr)->Section_Name);
3664 
3665 				if (child_section_data) {
3666 					/* Hey, it might be fragged off.  Now deal with it. */
3667 
3668 					child_section_data->sempai=*new_child_list_ptr;
3669 
3670 					Transmogrification_Recursion(sbPtr,controller,child_section_data,*new_child_list_ptr, corresponding_section, frag, newsections, regrowsections);
3671 				} else if (regrowsections) {
3672 					/* If it is fragged off, put it back. */
3673 					SECTION_DATA *new_child;
3674 
3675 					new_child=Create_New_Section(*new_child_list_ptr);
3676 
3677 					new_child->My_Parent=this_section_data;
3678 					new_child->Prev_Sibling=NULL;
3679 					new_child->Next_Sibling=this_section_data->First_Child;
3680 					if (this_section_data->First_Child) {
3681 						this_section_data->First_Child->Prev_Sibling=new_child;
3682 					}
3683 					this_section_data->First_Child=new_child;
3684 					Init_Sequence_Recursion(new_child, controller->Sequence_Type, controller->Sub_Sequence,controller->Seconds_For_Sequence);
3685 					/* Prove new positions. */
3686 					Process_Section(controller,new_child,&(this_section_data->World_Offset),&(this_section_data->SecMat),0,controller->Sequence_Type,controller->Sub_Sequence,0);
3687 				}
3688 
3689 			} else if (newsections) {
3690 				/* No corresponding old section: create a new bit. */
3691 				SECTION_DATA *new_child;
3692 
3693 				new_child=Create_New_Section(*new_child_list_ptr);
3694 
3695 				new_child->My_Parent=this_section_data;
3696 				new_child->Prev_Sibling=NULL;
3697 				new_child->Next_Sibling=this_section_data->First_Child;
3698 				if (this_section_data->First_Child) {
3699 					this_section_data->First_Child->Prev_Sibling=new_child;
3700 				}
3701 				this_section_data->First_Child=new_child;
3702 				Init_Sequence_Recursion(new_child, controller->Sequence_Type, controller->Sub_Sequence,controller->Seconds_For_Sequence);
3703 				/* Prove new positions. */
3704 				Process_Section(controller,new_child,&(this_section_data->World_Offset),&(this_section_data->SecMat),0,controller->Sequence_Type,controller->Sub_Sequence,0);
3705 			}
3706 
3707 			new_child_list_ptr++;
3708 		}
3709 		/* Now, let's walk the old template. */
3710 		new_child_list_ptr=new_template->Children;
3711 		while (*old_child_list_ptr!=NULL) {
3712 
3713 			SECTION *corresponding_section;
3714 
3715 			corresponding_section=Get_Corresponding_Section(new_child_list_ptr,(*old_child_list_ptr)->Section_Name);
3716 
3717 			if (corresponding_section!=NULL) {
3718 				/* Section also exists in new template.  Do nothing, it should already have been dealt with. */
3719 			} else {
3720 				/* No corresponding new section: delete this branch. */
3721 				SECTION_DATA *superfluous_section;
3722 
3723 				superfluous_section=GetThisSectionData_FromChildrenOnly(this_section_data,(*old_child_list_ptr)->Section_Name);
3724 				if (superfluous_section) {
3725 					if (frag) {
3726 						MakeHierarchicalDebris(sbPtr,superfluous_section, &superfluous_section->World_Offset, &superfluous_section->SecMat,NULL,2);
3727 						Prune_Section(superfluous_section);
3728 					} else {
3729 						Prune_Section(superfluous_section);
3730 					}
3731 
3732 				}
3733 			}
3734 
3735 			old_child_list_ptr++;
3736 		}
3737 
3738 
3739 	} else if (new_template->Children!=NULL) {
3740 
3741 		if (newsections) {
3742 			/* A whole lotta new branches. */
3743 			SECTION_DATA *new_child;
3744 			SECTION **new_child_list_ptr;
3745 
3746 			new_child_list_ptr=new_template->Children;
3747 
3748 			while (*new_child_list_ptr!=NULL) {
3749 
3750 				new_child=Create_New_Section(*new_child_list_ptr);
3751 
3752 				new_child->My_Parent=this_section_data;
3753 				new_child->Prev_Sibling=NULL;
3754 				new_child->Next_Sibling=this_section_data->First_Child;
3755 				if (this_section_data->First_Child) {
3756 					this_section_data->First_Child->Prev_Sibling=new_child;
3757 				}
3758 				this_section_data->First_Child=new_child;
3759 
3760 				Init_Sequence_Recursion(new_child, controller->Sequence_Type, controller->Sub_Sequence,controller->Seconds_For_Sequence);
3761 
3762 				new_child_list_ptr++;
3763 			}
3764 		}
3765 	} else if (old_template->Children!=NULL) {
3766 		/* Remove all branches. */
3767 		SECTION_DATA *data_child_ptr;
3768 
3769 		data_child_ptr=this_section_data->First_Child;
3770 
3771 		while (data_child_ptr!=NULL) {
3772 
3773 			SECTION_DATA *next_one;
3774 
3775 			LOCALASSERT(data_child_ptr->My_Parent==this_section_data);
3776 			next_one=data_child_ptr->Next_Sibling;
3777 			if (frag) {
3778 				MakeHierarchicalDebris(sbPtr,data_child_ptr, &data_child_ptr->World_Offset, &data_child_ptr->SecMat,NULL,2);
3779 				Prune_Section(data_child_ptr);
3780 			} else {
3781 				Prune_Section(data_child_ptr);
3782 			}
3783 			data_child_ptr=next_one;
3784 		}
3785 
3786 	} else {
3787 		/* Null case. */
3788 	}
3789 
3790 }
3791 
3792 void Transmogrify_HModels(STRATEGYBLOCK *sbPtr,HMODELCONTROLLER *controller,SECTION *new_template, int frag, int newsections, int regrowsections) {
3793 
3794 	SECTION *old_template;
3795 
3796 	/* Convert one HModel to another template... */
3797 	Global_Controller_Ptr=controller;
3798 
3799 	old_template=controller->Root_Section;
3800 
3801 	/* Compare the two templates to each other. */
3802 
3803 	GLOBALASSERT(controller->section_data->sempai==old_template);
3804 
3805 	controller->section_data->sempai=new_template;
3806 
3807 	Transmogrification_Recursion(sbPtr,controller,controller->section_data,new_template,old_template,frag,newsections,regrowsections);
3808 
3809 	controller->Root_Section=new_template;
3810 }
3811 
3812 void TrimToTemplate_Recursion(STRATEGYBLOCK *sbPtr,HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,SECTION *new_template, SECTION *old_template, int frag) {
3813 
3814 	/* Doesn't really matter which tree we're walking... does it? */
3815 
3816 	GLOBALASSERT(new_template);
3817 	GLOBALASSERT(old_template);
3818 	GLOBALASSERT(strcmp(new_template->Section_Name,old_template->Section_Name)==0);
3819 
3820 	GLOBALASSERT(this_section_data);
3821 
3822 	if ( (new_template->Children!=NULL) && (old_template->Children!=NULL) )  {
3823 		/* Complex.  I'd really like to walk both at the same time. */
3824 		SECTION **new_child_list_ptr,**old_child_list_ptr;
3825 
3826 		new_child_list_ptr=new_template->Children;
3827 		old_child_list_ptr=old_template->Children;
3828 
3829 		/* Let's walk the old template. */
3830 		while (*old_child_list_ptr!=NULL) {
3831 
3832 			SECTION *corresponding_section;
3833 
3834 			corresponding_section=Get_Corresponding_Section(new_child_list_ptr,(*old_child_list_ptr)->Section_Name);
3835 
3836 			if (corresponding_section!=NULL) {
3837 				/* Section also exists in new template.  Recurse. */
3838 				SECTION_DATA *child_section_data;
3839 
3840 				child_section_data=GetThisSectionData_FromChildrenOnly(this_section_data,(*old_child_list_ptr)->Section_Name);
3841 
3842 				if (child_section_data) {
3843 					/* Hey, it might be fragged off.  Now deal with it. */
3844 
3845 					TrimToTemplate_Recursion(sbPtr,controller,child_section_data,corresponding_section,*old_child_list_ptr, frag);
3846 				}
3847 			} else {
3848 				/* No corresponding new section: delete this branch. */
3849 				SECTION_DATA *superfluous_section;
3850 
3851 				superfluous_section=GetThisSectionData_FromChildrenOnly(this_section_data,(*old_child_list_ptr)->Section_Name);
3852 				if (superfluous_section) {
3853 					if (frag) {
3854 						MakeHierarchicalDebris(sbPtr,superfluous_section, &superfluous_section->World_Offset, &superfluous_section->SecMat,NULL,2);
3855 						Prune_Section(superfluous_section);
3856 					} else {
3857 						Prune_Section(superfluous_section);
3858 					}
3859 
3860 				}
3861 			}
3862 
3863 			old_child_list_ptr++;
3864 		}
3865 
3866 	} else if (old_template->Children!=NULL) {
3867 		/* Remove all branches. */
3868 		SECTION_DATA *data_child_ptr;
3869 
3870 		data_child_ptr=this_section_data->First_Child;
3871 
3872 		while (data_child_ptr!=NULL) {
3873 
3874 			SECTION_DATA *next_one;
3875 
3876 			LOCALASSERT(data_child_ptr->My_Parent==this_section_data);
3877 			next_one=data_child_ptr->Next_Sibling;
3878 			if (frag) {
3879 				MakeHierarchicalDebris(sbPtr,data_child_ptr, &data_child_ptr->World_Offset, &data_child_ptr->SecMat,NULL,2);
3880 				Prune_Section(data_child_ptr);
3881 			} else {
3882 				Prune_Section(data_child_ptr);
3883 			}
3884 			data_child_ptr=next_one;
3885 		}
3886 
3887 	} else {
3888 		/* Null case. */
3889 	}
3890 
3891 }
3892 
3893 void TrimToTemplate(STRATEGYBLOCK *sbPtr,HMODELCONTROLLER *controller,SECTION *new_template, int frag) {
3894 
3895 	SECTION *old_template;
3896 
3897 	/* Convert one HModel to another template... */
3898 
3899 	old_template=controller->Root_Section;
3900 
3901 	/* Compare the two templates to each other. */
3902 
3903 	GLOBALASSERT(controller->section_data->sempai==old_template);
3904 	if (strcmp(new_template->Section_Name,old_template->Section_Name)) {
3905 		GLOBALASSERT(strcmp(new_template->Section_Name,old_template->Section_Name)==0);
3906 	}
3907 	TrimToTemplate_Recursion(sbPtr,controller,controller->section_data,new_template,old_template,frag);
3908 
3909 }
3910 
3911 int HModelSequence_Exists(HMODELCONTROLLER *controller,int sequence_type,int sub_sequence) {
3912 
3913 	int sequence_id,a;
3914 	SEQUENCE *sequence_pointer;
3915 
3916 	sequence_id=GetSequenceID(sequence_type,sub_sequence);
3917 
3918 	for (a=0; a<controller->Root_Section->num_sequences; a++) {
3919 		if (controller->Root_Section->sequence_array[a].sequence_id==sequence_id) {
3920 			sequence_pointer=&(controller->Root_Section->sequence_array[a]);
3921 			break;
3922 		}
3923 	}
3924 
3925 	if (a==controller->Root_Section->num_sequences) {
3926 		/* No such animal. */
3927 		return(0);
3928 	} else {
3929 		return(1);
3930 	}
3931 
3932 }
3933 
3934 int HModelSequence_Exists_FromRoot(SECTION *root,int sequence_type,int sub_sequence) {
3935 
3936 	int sequence_id,a;
3937 	SEQUENCE *sequence_pointer;
3938 
3939 	if (!root) {
3940 		return(0);
3941 	}
3942 
3943 	sequence_id=GetSequenceID(sequence_type,sub_sequence);
3944 
3945 	for (a=0; a<root->num_sequences; a++) {
3946 		if (root->sequence_array[a].sequence_id==sequence_id) {
3947 			sequence_pointer=&(root->sequence_array[a]);
3948 			break;
3949 		}
3950 	}
3951 
3952 	if (a==root->num_sequences) {
3953 		/* No such animal. */
3954 		return(0);
3955 	} else {
3956 		return(1);
3957 	}
3958 
3959 }
3960 
3961 void KRS_Recursion(SECTION_DATA *this_section_data, int probability) {
3962 
3963 	/* General gibbing function. */
3964 
3965 
3966 	/* Recurse. */
3967 
3968 	if ( (this_section_data->First_Child!=NULL)
3969 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
3970 
3971 
3972 		SECTION_DATA *child_list_ptr;
3973 
3974 		child_list_ptr=this_section_data->First_Child;
3975 
3976 		while (child_list_ptr!=NULL) {
3977 
3978 			if ( (child_list_ptr->flags&section_data_terminate_here)==0) {
3979 
3980 				/* Right. Roll some dice... */
3981 				if ( (SeededFastRandom()&65535)<probability) {
3982 					/* Kill this bit... */
3983 
3984 					child_list_ptr->current_damage.Health=0;
3985 
3986 					KRS_Recursion(child_list_ptr,probability);
3987 
3988 				} else {
3989 
3990 					KRS_Recursion(child_list_ptr,probability);
3991 
3992 				}
3993 			}
3994 			child_list_ptr=child_list_ptr->Next_Sibling;
3995 		}
3996 
3997 	}
3998 
3999 }
4000 
4001 void KillRandomSections(SECTION_DATA *this_section_data, int probability) {
4002 
4003 	/* A bit like gibbing, but less extreme. */
4004 
4005 	KRS_Recursion(this_section_data,probability);
4006 
4007 }
4008 
4009 void Budge_Recursion(SECTION_DATA *this_section_data,VECTORCH *offset) {
4010 
4011 	SECTION_DATA *sdptr;
4012 
4013 	sdptr=NULL;
4014 
4015 	/* Budge! */
4016 	this_section_data->World_Offset.vx+=offset->vx;
4017 	this_section_data->World_Offset.vy+=offset->vy;
4018 	this_section_data->World_Offset.vz+=offset->vz;
4019 
4020 	/* Now call recursion... */
4021 
4022 	if (this_section_data->First_Child!=NULL) {
4023 
4024 		SECTION_DATA *child_list_ptr;
4025 
4026 		child_list_ptr=this_section_data->First_Child;
4027 
4028 		while (child_list_ptr!=NULL) {
4029 			Budge_Recursion(child_list_ptr,offset);
4030 			child_list_ptr=child_list_ptr->Next_Sibling;
4031 		}
4032 	}
4033 
4034 	return;
4035 
4036 }
4037 
4038 void Budge_HModel(HMODELCONTROLLER *controller,VECTORCH *offset) {
4039 
4040 	/* Shift a model. */
4041 
4042 	if ((offset==NULL)||(controller==NULL)) {
4043 		return;
4044 	}
4045 
4046 	/* */
4047 
4048 	Budge_Recursion(controller->section_data,offset);
4049 
4050 }
4051 
4052 void HModelRegen_Recursion(SECTION_DATA *this_section_data, int time) {
4053 
4054 	SECTION_DATA *sdptr;
4055 	int health_increment;
4056 
4057 	sdptr=NULL;
4058 
4059 	/* Regenerate this section. */
4060 	if (this_section_data->current_damage.Health>0) {
4061 
4062 		health_increment=DIV_FIXED((this_section_data->sempai->StartingStats.Health*NormalFrameTime),time);
4063 		this_section_data->current_damage.Health+=health_increment;
4064 
4065 		if (this_section_data->current_damage.Health>(this_section_data->sempai->StartingStats.Health<<ONE_FIXED_SHIFT)) {
4066 			this_section_data->current_damage.Health=(this_section_data->sempai->StartingStats.Health<<ONE_FIXED_SHIFT);
4067 		}
4068 	}
4069 
4070 	/* Now call recursion... */
4071 
4072 	if (this_section_data->First_Child!=NULL) {
4073 
4074 		SECTION_DATA *child_list_ptr;
4075 
4076 		child_list_ptr=this_section_data->First_Child;
4077 
4078 		while (child_list_ptr!=NULL) {
4079 			HModelRegen_Recursion(child_list_ptr,time);
4080 			child_list_ptr=child_list_ptr->Next_Sibling;
4081 		}
4082 	}
4083 
4084 	return;
4085 
4086 }
4087 
4088 void HModel_Regen(HMODELCONTROLLER *controller,int time) {
4089 
4090 	/* Regenerate sections. */
4091 
4092 	HModelRegen_Recursion(controller->section_data,time);
4093 
4094 }
4095 
4096 int HModelAnimation_IsFinished(HMODELCONTROLLER *controller) {
4097 
4098 	/* This now gets used all over the place... */
4099 
4100 	if (controller->Tweening!=Controller_NoTweening) {
4101 		return(0);
4102 	}
4103 	if (controller->Looped) {
4104 		return(0);
4105 	}
4106 	if (controller->Reversed) {
4107 		if (controller->sequence_timer!=0) {
4108 			return(0);
4109 		}
4110 	} else {
4111 		if (controller->sequence_timer!=(ONE_FIXED-1)) {
4112 			return(0);
4113 		}
4114 	}
4115 	return(1);
4116 }
4117 
4118 int DeltaAnimation_IsFinished(DELTA_CONTROLLER *controller) {
4119 
4120 	if (controller->Looped) {
4121 		return(0);
4122 	}
4123 	if (controller->Playing==0) {
4124 		return(1);
4125 	}
4126 	if (controller->Active==0) {
4127 		return(1);
4128 	}
4129 	if (controller->timer!=(ONE_FIXED-1)) {
4130 		return(0);
4131 	}
4132 	return(1);
4133 }
4134 
4135 SECTION_DATA *PointInHModel_Recursion(SECTION_DATA *this_section_data, VECTORCH *point) {
4136 
4137 	SECTION_DATA *hit;
4138 
4139 	hit=NULL;
4140 
4141 	/* Test this section. */
4142 
4143 	/* Use shape data. */
4144 	if (this_section_data->Shape!=NULL) {
4145 		VECTORCH offset;
4146 		int dist;
4147 
4148 		offset.vx=this_section_data->World_Offset.vx-point->vx;
4149 		offset.vy=this_section_data->World_Offset.vy-point->vy;
4150 		offset.vz=this_section_data->World_Offset.vz-point->vz;
4151 		dist=Approximate3dMagnitude(&offset);
4152 		if (dist<this_section_data->Shape->shaperadius) {
4153 			/* Hit! */
4154 			hit=this_section_data;
4155 		}
4156 	}
4157 
4158 	if (hit) {
4159 		return(hit);
4160 	}
4161 
4162 	/* Now call recursion... */
4163 
4164 	if (this_section_data->First_Child!=NULL) {
4165 
4166 		SECTION_DATA *child_list_ptr;
4167 
4168 		child_list_ptr=this_section_data->First_Child;
4169 
4170 		while (child_list_ptr!=NULL) {
4171 			hit=PointInHModel_Recursion(child_list_ptr,point);
4172 			child_list_ptr=child_list_ptr->Next_Sibling;
4173 		}
4174 	}
4175 
4176 	return(hit);
4177 
4178 }
4179 
4180 SECTION_DATA *PointInHModel(HMODELCONTROLLER *controller,VECTORCH *point) {
4181 
4182 	/* Test for point in model. */
4183 
4184 	return(PointInHModel_Recursion(controller->section_data,point));
4185 
4186 }
4187 
4188 SECTION *Get_Corresponding_Section_Recursive(SECTION *this_section,char *Name) {
4189 
4190 	SECTION **child_list_ptr;
4191 
4192 	if (strcmp(this_section->Section_Name,Name)==0) {
4193 		return(this_section);
4194 	}
4195 
4196 	/* Recurse. */
4197 	child_list_ptr=this_section->Children;
4198 
4199 	if (child_list_ptr) {
4200 		while (*child_list_ptr!=NULL) {
4201 			SECTION *cosec;
4202 
4203 			cosec=Get_Corresponding_Section_Recursive(*child_list_ptr,Name);
4204 			if (cosec) {
4205 				/* Back out! */
4206 				return(cosec);
4207 			}
4208 			child_list_ptr++;
4209 		}
4210 	}
4211 
4212 	/* No luck. */
4213 	return(NULL);
4214 }
4215 
4216 SECTION_DATA *GetSectionFromID_Recursion(SECTION_DATA *this_section_data,int IDnumber) {
4217 
4218 	SECTION_DATA *sdptr;
4219 
4220 	sdptr=NULL;
4221 
4222 	if (this_section_data->sempai->IDnumber==IDnumber) {
4223 		/* We are that section! */
4224 		return(this_section_data);
4225 	}
4226 
4227 	/* Now call recursion... */
4228 
4229 	if (this_section_data->First_Child!=NULL) {
4230 
4231 		SECTION_DATA *child_list_ptr;
4232 
4233 		child_list_ptr=this_section_data->First_Child;
4234 
4235 		while (child_list_ptr!=NULL) {
4236 			sdptr=GetSectionFromID_Recursion(child_list_ptr,IDnumber);
4237 			child_list_ptr=child_list_ptr->Next_Sibling;
4238 
4239 			if (sdptr) {
4240 				return(sdptr); /* We got one! */
4241 			}
4242 		}
4243 	}
4244 
4245 	return(sdptr);
4246 
4247 }
4248 
4249 SECTION_DATA *GetThisSectionData_FromID(SECTION_DATA *root,int IDnumber) {
4250 
4251 	if (root==NULL) {
4252 		return(NULL);
4253 	}
4254 
4255 	return(GetSectionFromID_Recursion(root,IDnumber));
4256 
4257 }
4258 
4259 SECTION *GetThisSection_FromID(SECTION *this_section,int IDnumber)
4260 {
4261 	if (this_section==NULL) {
4262 		return(NULL);
4263 	}
4264 
4265 	//is this the section that we're looking for
4266 	if(this_section->IDnumber == IDnumber) return this_section;
4267 
4268 	//try this section's children then
4269 	if(this_section->Children)
4270 	{
4271 		SECTION **child_list_ptr = this_section->Children;
4272 
4273 		while(*child_list_ptr)
4274 		{
4275 			SECTION* return_section = GetThisSection_FromID(*child_list_ptr,IDnumber);
4276 			if(return_section) return return_section;
4277 			child_list_ptr++;
4278 		}
4279 	}
4280 
4281 	//out of luck
4282 	return NULL;
4283 }
4284 
4285 
4286 void PlayHierarchySound(HIERARCHY_SOUND* sound,VECTORCH* location)
4287 {
4288 	GLOBALASSERT(sound);
4289 	GLOBALASSERT(location);
4290 
4291 	sound->s3d.position=*location;
4292 
4293 	if (!Global_VDB_Ptr) {
4294 		return;
4295 	}
4296 
4297 	/* Marine_ignore, to stop them getting alarmed by their own footsteps! */
4298    	Sound_Play (sound->sound_index, "nvpm", &sound->s3d,sound->volume,sound->pitch);
4299 }
4300 
4301 void Init_Tweening_ToTheMiddle_Recursion(SECTION_DATA *this_section_data, int target_sequence_type,int target_subsequence, int seconds_for_tweening, int target_sequence_timer, int backwards) {
4302 
4303 	SEQUENCE *sequence_ptr;
4304 
4305 	/* Firstly, store current state. */
4306 
4307 	this_section_data->stored_offset=this_section_data->Offset;
4308 	MatToQuat(&this_section_data->RelSecMat,&this_section_data->stored_quat);
4309 
4310 	/* Now, get target state. */
4311 
4312 	sequence_ptr=GetSequencePointer(target_sequence_type,target_subsequence,this_section_data->sempai);
4313 
4314 	/* Deduce target positions.  Backwards flag is irrelevant... */
4315 
4316 	{
4317 		int a,working_timer,lerp;
4318 		KEYFRAME_DATA *this_frame,*next_frame;
4319 
4320 		this_frame=sequence_ptr->first_frame;
4321 		GLOBALASSERT(this_frame);
4322 		working_timer=target_sequence_timer;
4323 
4324 		a=0; /* Status flag... */
4325 
4326 		while (a==0) {
4327 			if (this_frame->last_frame) {
4328 				/* Low framerate loop? */
4329 				next_frame=sequence_ptr->first_frame;
4330 			}
4331 			else{
4332 				next_frame=this_frame->Next_Frame;
4333 			}
4334 			if (working_timer>=this_frame->Sequence_Length) {
4335 				/* We've gone beyond this frame: get next keyframe. */
4336 				working_timer-=this_frame->Sequence_Length;
4337 				/* Advance frame... */
4338 				this_frame=next_frame;
4339 			} else {
4340 				a=1; /* Exit loop with success. */
4341 			}
4342 			/* Better make sure the last 'frame' has 65536 length... */
4343 		}
4344 		GLOBALASSERT(working_timer>=0);
4345 		/* Now we should have a frame and a timer. */
4346 		lerp=MUL_FIXED(working_timer,this_frame->oneoversequencelength);
4347 
4348 		GetKeyFrameOffset(this_frame,&this_section_data->target_offset);
4349 
4350 		if(next_frame->shift_offset)
4351 		{
4352 			VECTORCH next_offset;
4353 			GetKeyFrameOffset(next_frame,&next_offset);
4354 			this_section_data->target_offset.vx+=MUL_FIXED(next_offset.vx - this_section_data->target_offset.vx,lerp);
4355 			this_section_data->target_offset.vy+=MUL_FIXED(next_offset.vy - this_section_data->target_offset.vy,lerp);
4356 			this_section_data->target_offset.vz+=MUL_FIXED(next_offset.vz - this_section_data->target_offset.vz,lerp);
4357 		}
4358 		else
4359 		{
4360 			this_section_data->target_offset.vx+=MUL_FIXED((int)next_frame->Offset_x - this_section_data->target_offset.vx,lerp);
4361 			this_section_data->target_offset.vy+=MUL_FIXED((int)next_frame->Offset_y - this_section_data->target_offset.vy,lerp);
4362 			this_section_data->target_offset.vz+=MUL_FIXED((int)next_frame->Offset_z - this_section_data->target_offset.vz,lerp);
4363 		}
4364 
4365 		/* Now deal with orientation. */
4366 
4367 		Slerp(this_frame,lerp,&this_section_data->target_quat);
4368 
4369 	}
4370 
4371 	/* Preprocess slerp values. */
4372 
4373 	{
4374 
4375 		this_section_data->delta_offset.vx=this_section_data->target_offset.vx-this_section_data->stored_offset.vx;
4376 		this_section_data->delta_offset.vy=this_section_data->target_offset.vy-this_section_data->stored_offset.vy;
4377 		this_section_data->delta_offset.vz=this_section_data->target_offset.vz-this_section_data->stored_offset.vz;
4378 
4379 	}
4380 
4381 	{
4382 		int cosom,sinom;
4383 		QUAT *this_quat, *next_quat;
4384 
4385 		this_quat=&this_section_data->stored_quat;
4386 		next_quat=&this_section_data->target_quat;
4387 
4388 
4389 		cosom=QDot(this_quat,next_quat);
4390 
4391 		if (cosom<0) {
4392 			next_quat->quatx=-next_quat->quatx;
4393 			next_quat->quaty=-next_quat->quaty;
4394 			next_quat->quatz=-next_quat->quatz;
4395 			next_quat->quatw=-next_quat->quatw;
4396 			cosom=-cosom;
4397 		}
4398 
4399 
4400 		this_section_data->omega=ArcCos(cosom);
4401 		sinom=GetSin(this_section_data->omega);
4402 		if (sinom) {
4403 			this_section_data->oneoversinomega=GetOneOverSin(this_section_data->omega);
4404 		} else {
4405 			/* Yuk. */
4406 			this_section_data->omega=0;
4407 			this_section_data->oneoversinomega=0;
4408 		}
4409 
4410 		GLOBALASSERT(seconds_for_tweening>0);
4411 		this_section_data->oneovertweeninglength=DIV_FIXED(ONE_FIXED,seconds_for_tweening);
4412 
4413 	}
4414 
4415 	/* Init fields... I guess. */
4416 
4417 	this_section_data->current_sequence=sequence_ptr;
4418 	this_section_data->current_keyframe=sequence_ptr->first_frame;
4419 	this_section_data->accumulated_timer=0;
4420 	this_section_data->freezeframe_timer=-1;
4421 	this_section_data->lastframe_timer=0;
4422 	this_section_data->gore_timer=0; /* As good a time as any. */
4423 
4424 	this_section_data->Tweening=1;
4425 
4426 	/* Animation? */
4427 
4428 	/* Nah. */
4429 
4430 	/* Recurse. */
4431 
4432 	if ( (this_section_data->First_Child!=NULL)
4433 		&&( (this_section_data->flags&section_data_terminate_here)==0)) {
4434 		/* Respect the terminator! */
4435 
4436 		SECTION_DATA *child_list_ptr;
4437 
4438 		child_list_ptr=this_section_data->First_Child;
4439 
4440 		while (child_list_ptr!=NULL) {
4441 			Init_Tweening_ToTheMiddle_Recursion(child_list_ptr,target_sequence_type,target_subsequence,seconds_for_tweening,target_sequence_timer,backwards);
4442 			child_list_ptr=child_list_ptr->Next_Sibling;
4443 		}
4444 
4445 	}
4446 
4447 }
4448 
4449 
4450 void InitHModelTweening_ToTheMiddle(HMODELCONTROLLER *controller, int seconds_for_tweening,
4451 	int target_sequence_type, int target_subsequence, int target_seconds_for_sequence, int target_sequence_timer, int loop) {
4452 
4453 	/* Just set it up... */
4454 	GLOBALASSERT(target_seconds_for_sequence);
4455 
4456 	controller->Sequence_Type=target_sequence_type;
4457 	controller->Sub_Sequence=target_subsequence;
4458 	controller->Seconds_For_Sequence=seconds_for_tweening;
4459 	controller->timer_increment=DIV_FIXED(ONE_FIXED,controller->Seconds_For_Sequence);
4460 	controller->sequence_timer=0;
4461 	controller->Playing=1;
4462 	controller->Reversed=0;
4463 	controller->Looped=1;
4464 
4465 	controller->After_Tweening_Sequence_Type=target_sequence_type;
4466 	controller->After_Tweening_Sub_Sequence=target_subsequence;
4467 	controller->AT_seconds_for_sequence=target_seconds_for_sequence;
4468 
4469 	controller->AT_sequence_timer=target_sequence_timer;
4470 	while (controller->AT_sequence_timer>=ONE_FIXED) {
4471 		controller->AT_sequence_timer-=ONE_FIXED;
4472 	}
4473 
4474 	controller->Tweening=Controller_Tweening;
4475 	if (loop) {
4476 		controller->LoopAfterTweening=1;
4477 	} else {
4478 		controller->LoopAfterTweening=0;
4479 	}
4480 	controller->StopAfterTweening=0;
4481 	controller->ElevationTweening=0;
4482 
4483 	/* Recurse though hierarchy, setting up all the section_data sequence stores? */
4484 
4485 	Init_Tweening_ToTheMiddle_Recursion(controller->section_data, target_sequence_type, target_subsequence,seconds_for_tweening,target_sequence_timer,0);
4486 
4487 }
4488 
4489 void Verify_Positions_Recursion(HMODELCONTROLLER *controller,SECTION_DATA *this_section_data,VECTORCH *parent_position,char *callCode) {
4490 
4491 	/* Verify positions... */
4492 
4493 	if ( !(	(this_section_data->World_Offset.vx<1000000 && this_section_data->World_Offset.vx>-1000000)
4494 		 &&	(this_section_data->World_Offset.vy<1000000 && this_section_data->World_Offset.vy>-1000000)
4495 		 &&	(this_section_data->World_Offset.vz<1000000 && this_section_data->World_Offset.vz>-1000000)
4496 		 ) ) {
4497 
4498 		LOGDXFMT(("Tests in VERIFY_POSITIONS_RECURSION.\n"));
4499 		if (callCode) {
4500 			LOGDXFMT(("Call code %s\n",callCode));
4501 		} else {
4502 			LOGDXFMT(("No call code!\n"));
4503 		}
4504 		if (Global_HModel_Sptr) {
4505 			LOGDXFMT(("Misplaced object is of type %d\n",Global_HModel_Sptr->I_SBtype));
4506 			if (Global_HModel_Sptr->SBdptr) {
4507 				LOGDXFMT(("Object is Near.\n"));
4508 			} else {
4509 				LOGDXFMT(("Object is Far.\n"));
4510 			}
4511 		} else {
4512 			LOGDXFMT(("Misplaced object has no SBptr.\n"));
4513 		}
4514 		LOGDXFMT(("Name of section: %s\n",this_section_data->sempai->Section_Name));
4515 		LOGDXFMT(("It was playing sequence: %d,%d\n",controller->Sequence_Type,controller->Sub_Sequence));
4516 		LOGDXFMT(("Sequence Timer = %d\n",controller->sequence_timer));
4517 		LOGDXFMT(("Tweening flags %d\n",controller->Tweening));
4518 
4519 		LOGDXFMT(("Parent Position %d,%d,%d\n",parent_position->vx,parent_position->vy,parent_position->vz));
4520 		LOGDXFMT(("This Position %d,%d,%d\n",this_section_data->World_Offset.vx,this_section_data->World_Offset.vy,this_section_data->World_Offset.vz));
4521 
4522 		LOCALASSERT(this_section_data->World_Offset.vx<1000000 && this_section_data->World_Offset.vx>-1000000);
4523 		LOCALASSERT(this_section_data->World_Offset.vy<1000000 && this_section_data->World_Offset.vy>-1000000);
4524 		LOCALASSERT(this_section_data->World_Offset.vz<1000000 && this_section_data->World_Offset.vz>-1000000);
4525 
4526 	}
4527 
4528 	/* Now call recursion... */
4529 
4530 	if (this_section_data->First_Child!=NULL) {
4531 
4532 		SECTION_DATA *child_list_ptr;
4533 
4534 		child_list_ptr=this_section_data->First_Child;
4535 
4536 		while (child_list_ptr!=NULL) {
4537 			Verify_Positions_Recursion(controller,child_list_ptr,&this_section_data->World_Offset,callCode);
4538 			child_list_ptr=child_list_ptr->Next_Sibling;
4539 		}
4540 	}
4541 
4542 	return;
4543 
4544 }
4545 
4546 void Verify_Positions_In_HModel(STRATEGYBLOCK *sbPtr,HMODELCONTROLLER *controller,char *callCode) {
4547 
4548 	/* Verify position integrity. */
4549 
4550 	if ((controller==NULL)||(sbPtr==NULL)) {
4551 		return;
4552 	}
4553 
4554 	/* */
4555 
4556 	Global_HModel_Sptr=sbPtr;
4557 	Global_Controller_Ptr=controller;
4558 	Global_HModel_DispPtr=sbPtr->SBdptr;
4559 
4560 	Verify_Positions_Recursion(controller,controller->section_data,&sbPtr->DynPtr->Position,callCode);
4561 
4562 }
4563 
4564 
4565 
4566 
4567 void CopyShortQuatToInt(QUAT_SHORT* qs,QUAT* q)
4568 {
4569 	q->quatx=((int)qs->quatx)<<1;
4570 	q->quaty=((int)qs->quaty)<<1;
4571 	q->quatz=((int)qs->quatz)<<1;
4572 	q->quatw=((int)qs->quatw)<<1;
4573 }
4574 
4575 
4576 void CopyIntQuatToShort(QUAT* q,QUAT_SHORT* qs)
4577 {
4578 	if(q->quatx>65535) qs->quatx=32767;
4579 	else if(q->quatx<-65536) qs->quatx=-32768;
4580 	else qs->quatx=q->quatx>>1;
4581 
4582 	if(q->quaty>65535) qs->quaty=32767;
4583 	else if(q->quaty<-65536) qs->quaty=-32768;
4584 	else qs->quaty=q->quaty>>1;
4585 
4586 	if(q->quatz>65535) qs->quatz=32767;
4587 	else if(q->quatz<-65536) qs->quatz=-32768;
4588 	else qs->quatz=q->quatz>>1;
4589 
4590 	if(q->quatw>65535) qs->quatw=32767;
4591 	else if(q->quatw<-65536) qs->quatw=-32768;
4592  	else qs->quatw=q->quatw>>1;
4593 
4594 }
4595 
4596 void GetKeyFrameOffset(KEYFRAME_DATA* frame,VECTORCH* output_vector)
4597 {
4598 	if(frame->shift_offset)
4599 	{
4600 		output_vector->vx=((int)frame->Offset_x)<<KEYFRAME_VECTOR_SHIFT;
4601 		output_vector->vy=((int)frame->Offset_y)<<KEYFRAME_VECTOR_SHIFT;
4602 		output_vector->vz=((int)frame->Offset_z)<<KEYFRAME_VECTOR_SHIFT;
4603 	}
4604 	else
4605 	{
4606 		output_vector->vx=(int)frame->Offset_x;
4607 		output_vector->vy=(int)frame->Offset_y;
4608 		output_vector->vz=(int)frame->Offset_z;
4609 	}
4610 }
4611 
4612 
4613 void SetKeyFrameOffset(KEYFRAME_DATA* frame,VECTORCH* input_vector)
4614 {
4615 	if(input_vector->vx>=-32768 && input_vector->vx<=32767 &&
4616 	   input_vector->vy>=-32768 && input_vector->vy<=32767 &&
4617 	   input_vector->vz>=-32768 && input_vector->vz<=32767)
4618 	{
4619 		frame->Offset_x=(short)input_vector->vx;
4620 		frame->Offset_y=(short)input_vector->vy;
4621 		frame->Offset_z=(short)input_vector->vz;
4622 
4623 		frame->shift_offset=0;
4624 	}
4625 	else
4626 	{
4627 		frame->Offset_x=(short)(input_vector->vx >> KEYFRAME_VECTOR_SHIFT);
4628 		frame->Offset_y=(short)(input_vector->vy >> KEYFRAME_VECTOR_SHIFT);
4629 		frame->Offset_z=(short)(input_vector->vz >> KEYFRAME_VECTOR_SHIFT);
4630 
4631 		frame->shift_offset=1;
4632 
4633 	}
4634 }
4635 
4636 int HModelDepthTest_Recursion(SECTION_DATA *this_section_data, SECTION_DATA *test_section_data,int depth) {
4637 
4638 	SECTION_DATA *sdptr;
4639 	int totals;
4640 
4641 	sdptr=NULL;
4642 	totals=0;
4643 
4644 	/* Return if too deep. */
4645 	if (depth<0) {
4646 		return(0);
4647 	}
4648 
4649 	/* Test this section? */
4650 	if (this_section_data==test_section_data) {
4651 		/* Success. */
4652 		return(1);
4653 	}
4654 
4655 	/* Now call recursion... */
4656 
4657 	if (this_section_data->First_Child!=NULL) {
4658 
4659 		SECTION_DATA *child_list_ptr;
4660 
4661 		child_list_ptr=this_section_data->First_Child;
4662 
4663 		while (child_list_ptr!=NULL) {
4664 			totals+=HModelDepthTest_Recursion(child_list_ptr,test_section_data,depth-1);
4665 			child_list_ptr=child_list_ptr->Next_Sibling;
4666 		}
4667 	}
4668 
4669 	return(totals);
4670 
4671 }
4672 
4673 int HModel_DepthTest(HMODELCONTROLLER *controller,SECTION_DATA *test_section_data,int depth) {
4674 
4675 	/* Regenerate sections. */
4676 
4677 	return(HModelDepthTest_Recursion(controller->section_data,test_section_data,depth));
4678 
4679 }
4680 
4681 void DeInitialise_Recursion(SECTION_DATA *this_section_data) {
4682 
4683 	SECTION_DATA *sdptr;
4684 
4685 	sdptr=NULL;
4686 
4687 	this_section_data->flags=this_section_data->flags&(~section_data_initialised);
4688 
4689 	/* Now call recursion... */
4690 
4691 	if (this_section_data->First_Child!=NULL) {
4692 
4693 		SECTION_DATA *child_list_ptr;
4694 
4695 		child_list_ptr=this_section_data->First_Child;
4696 
4697 		while (child_list_ptr!=NULL) {
4698 			DeInitialise_Recursion(child_list_ptr);
4699 			child_list_ptr=child_list_ptr->Next_Sibling;
4700 		}
4701 	}
4702 
4703 	return;
4704 
4705 }
4706 
4707 void DeInitialise_HModel(HMODELCONTROLLER *controller) {
4708 
4709 	/* Recursively set all 'initialised' flags to zero. */
4710 
4711 	if (controller==NULL) {
4712 		return;
4713 	}
4714 
4715 	DeInitialise_Recursion(controller->section_data);
4716 
4717 }
4718 
4719 
4720 static void EnsureChildrenAreInAscendingIDOrder(SECTION_DATA* section)
4721 {
4722 	/*
4723 	This checks all the children of a section to make sure that they are placed in ascending Id order.
4724 	This is needed for the saving and loading process to work properly.
4725 	(In the majority of cases, sections will be in the right order , so this should be fairly quick)
4726 	*/
4727 
4728 	SECTION_DATA* child_section = section->First_Child;
4729 
4730 	if(!child_section) return;
4731 
4732 	while(child_section->Next_Sibling)
4733 	{
4734 		SECTION_DATA* next_section = child_section->Next_Sibling;
4735 		if(next_section->sempai->IDnumber<child_section->sempai->IDnumber)
4736 		{
4737 			/*
4738 			These two sections are out of order , so we need to swap them
4739 			*/
4740 
4741 
4742 			/*First correct the children before and adter the ones being swapped*/
4743 
4744 			if(child_section->Prev_Sibling)
4745 			{
4746 				child_section->Prev_Sibling->Next_Sibling = next_section;
4747 			}
4748 			if(next_section->Next_Sibling)
4749 			{
4750 				next_section->Next_Sibling->Prev_Sibling = child_section;
4751 			}
4752 
4753 			/*
4754 			If we are swapping the first child , then we need to alter the parent's pointer
4755 			*/
4756 			if(section->First_Child == child_section)
4757 			{
4758 				section->First_Child = next_section;
4759 			}
4760 
4761 
4762 			/*
4763 			Give the sections being swapped the pointers to the sections before and after them
4764 			*/
4765 			child_section->Next_Sibling = next_section->Next_Sibling;
4766 			next_section->Prev_Sibling = child_section->Prev_Sibling;
4767 
4768 
4769 			child_section->Prev_Sibling = next_section;
4770 			next_section->Next_Sibling = child_section;
4771 
4772 
4773 			/*The next section we will have to consider is the one we have just swapped ealier in the list*/
4774 			child_section = next_section;
4775 
4776 			/*
4777 			If the section has a previous sibling then we need to look back at that one , since the previous sibling's
4778 			if could be higher than that of the section we have just swapped
4779 			*/
4780 
4781 			if(child_section->Prev_Sibling) child_section = child_section->Prev_Sibling;
4782 
4783 		}
4784 		else
4785 		{
4786 			//No problem with this section , go on to the next
4787 			child_section = child_section->Next_Sibling;
4788 		}
4789 	}
4790 }
4791 
4792 /*--------------------**
4793 ** Loading and Saving **
4794 **--------------------*/
4795 #include "savegame.h"
4796 
4797 static void LoadHierarchySection(SECTION_DATA* section);
4798 static void SaveHierarchySectionRecursion(SECTION_DATA* section);
4799 
4800 static void LoadHierarchySectionDecals(SAVE_BLOCK_HEADER* header,SECTION_DATA* section);
4801 static void SaveHierarchySectionDecals(SECTION_DATA* section);
4802 
4803 static void LoadHierarchySectionTween(SAVE_BLOCK_HEADER* header,SECTION_DATA* section);
4804 static void SaveHierarchySectionTween(SECTION_DATA* section);
4805 
4806 static void LoadHierarchyDelta(SAVE_BLOCK_HEADER* header,HMODELCONTROLLER* controller);
4807 static void SaveHierarchyDelta(DELTA_CONTROLLER* delta);
4808 
4809 typedef struct hierarchy_save_block
4810 {
4811 	SAVE_BLOCK_HEADER header;
4812 
4813 	int structure_size;
4814 
4815 	int Seconds_For_Sequence;
4816 	int timer_increment;
4817 	int Sequence_Type;
4818 	int	Sub_Sequence;
4819 	int sequence_timer;
4820 	int FrameStamp;
4821 	VECTORCH Computed_Position;
4822 
4823 	int keyframe_flags;
4824 
4825 	int After_Tweening_Sequence_Type;
4826 	int After_Tweening_Sub_Sequence;
4827 	int AT_seconds_for_sequence;
4828 	int AT_sequence_timer;
4829 
4830 	unsigned int Playing:1;
4831 	unsigned int Reversed:1;
4832 	unsigned int Looped:1;
4833 	unsigned int Tweening:2;
4834 	unsigned int LoopAfterTweening:1;
4835 	unsigned int StopAfterTweening:1;
4836 	unsigned int ElevationTweening:1;
4837 	unsigned int DisableBleeding:1;
4838 	unsigned int LockTopSection:1;
4839 	unsigned int ZeroRootDisplacement:1;
4840 	unsigned int ZeroRootRotation:1;
4841 	unsigned int DisableSounds:1;
4842 
4843 	int root_section_id;
4844 
4845 }HIERARCHY_SAVE_BLOCK;
4846 
4847 
4848 //defines for load/save macros
4849 #define SAVELOAD_BLOCK block
4850 #define SAVELOAD_BEHAV controller
4851 
4852 
4853 void LoadHierarchy(SAVE_BLOCK_HEADER* header,HMODELCONTROLLER* controller)
4854 {
4855 	SECTION *root_section;
4856 	const char *Rif_Name;
4857 	const char *Hierarchy_Name;
4858 	HIERARCHY_SAVE_BLOCK* block = (HIERARCHY_SAVE_BLOCK*) header;
4859 
4860 	//make sure the block is the correct size
4861 	if(block->structure_size != sizeof(*block)) return;
4862 
4863 	//get the names from just after the block
4864 	{
4865 		char* buffer=(char*) header;
4866 		buffer+=sizeof(*block);
4867 
4868 		Hierarchy_Name = buffer;
4869 		buffer+= strlen(Hierarchy_Name)+1;
4870 		Rif_Name = buffer;
4871   	}
4872 
4873 	{
4874 		BOOL needToCreateHModel = FALSE;
4875 
4876 		//make sure that the initial model has been set up , and is using the correct hierarchy
4877 		if(controller->Root_Section)
4878 		{
4879 			if(strcmp(Rif_Name,controller->Root_Section->Rif_Name) || strcmp(Hierarchy_Name,controller->Root_Section->Hierarchy_Name))
4880 			{
4881 				needToCreateHModel = TRUE;
4882 			}
4883 			else if(controller->Root_Section->IDnumber != block->root_section_id)
4884 			{
4885 				needToCreateHModel = TRUE;
4886 			}
4887 		}
4888 		else
4889 		{
4890 			needToCreateHModel = TRUE;
4891 		}
4892 
4893 		//check to see if we need to change hierarchy
4894 		if(needToCreateHModel)
4895 		{
4896 			extern SECTION * GetNamedHierarchyFromLibrary(const char * rif_name, const char * hier_name);
4897 
4898 			root_section = GetNamedHierarchyFromLibrary(Rif_Name,Hierarchy_Name);
4899 			if(!root_section) return;
4900 
4901 			//may not want the 'real' root (hierarchical debris)
4902 			root_section = GetThisSection_FromID(root_section,block->root_section_id);
4903 			if(!root_section) return;
4904 
4905 
4906 			//reinitialise the hierarchy
4907 			Dispel_HModel(controller);
4908 			Create_HModel(controller,root_section);
4909 		}
4910 	}
4911 
4912 	//copy the stuff for the controller
4913 	COPYELEMENT_LOAD(Seconds_For_Sequence)
4914 	COPYELEMENT_LOAD(timer_increment)
4915 	COPYELEMENT_LOAD(Sequence_Type)
4916 	COPYELEMENT_LOAD(Sub_Sequence)
4917 	COPYELEMENT_LOAD(sequence_timer)
4918 	COPYELEMENT_LOAD(FrameStamp)
4919 	COPYELEMENT_LOAD(Computed_Position)
4920 	COPYELEMENT_LOAD(keyframe_flags)
4921 	COPYELEMENT_LOAD(After_Tweening_Sequence_Type)
4922 	COPYELEMENT_LOAD(After_Tweening_Sub_Sequence)
4923 	COPYELEMENT_LOAD(AT_seconds_for_sequence)
4924 	COPYELEMENT_LOAD(AT_sequence_timer)
4925 	COPYELEMENT_LOAD(Playing)
4926 	COPYELEMENT_LOAD(Reversed)
4927 	COPYELEMENT_LOAD(Looped)
4928 	COPYELEMENT_LOAD(Tweening)
4929 	COPYELEMENT_LOAD(LoopAfterTweening)
4930 	COPYELEMENT_LOAD(StopAfterTweening)
4931 	COPYELEMENT_LOAD(ElevationTweening)
4932 	COPYELEMENT_LOAD(DisableBleeding)
4933 	COPYELEMENT_LOAD(LockTopSection)
4934 	COPYELEMENT_LOAD(ZeroRootDisplacement)
4935 	COPYELEMENT_LOAD(ZeroRootRotation)
4936 	COPYELEMENT_LOAD(DisableSounds);
4937 
4938 
4939 	//load the delta sequences
4940 	{
4941 		SAVE_BLOCK_HEADER* delta_header;
4942 
4943 		while((delta_header = GetNextBlockIfOfType(SaveBlock_HierarchyDelta)))
4944 		{
4945 			LoadHierarchyDelta(delta_header,controller);
4946 		}
4947 
4948 	}
4949 
4950 	///load the section data
4951 	LoadHierarchySection(controller->section_data);
4952 
4953 }
4954 
4955 
4956 void SaveHierarchy(HMODELCONTROLLER* controller)
4957 {
4958 	HIERARCHY_SAVE_BLOCK* block;
4959 	if(!controller) return;
4960 	if(!controller->Root_Section) return;
4961 
4962 	GET_SAVE_BLOCK_POINTER(block);
4963 
4964 	//fill in header
4965 	block->header.type = SaveBlock_Hierarchy;
4966 	block->header.size = sizeof(*block);
4967 	block->structure_size =block->header.size;
4968 
4969 	COPYELEMENT_SAVE(Seconds_For_Sequence)
4970 	COPYELEMENT_SAVE(timer_increment)
4971 	COPYELEMENT_SAVE(Sequence_Type)
4972 	COPYELEMENT_SAVE(Sub_Sequence)
4973 	COPYELEMENT_SAVE(sequence_timer)
4974 	COPYELEMENT_SAVE(FrameStamp)
4975 	COPYELEMENT_SAVE(Computed_Position)
4976 	COPYELEMENT_SAVE(keyframe_flags)
4977 	COPYELEMENT_SAVE(After_Tweening_Sequence_Type)
4978 	COPYELEMENT_SAVE(After_Tweening_Sub_Sequence)
4979 	COPYELEMENT_SAVE(AT_seconds_for_sequence)
4980 	COPYELEMENT_SAVE(AT_sequence_timer)
4981 	COPYELEMENT_SAVE(Playing)
4982 	COPYELEMENT_SAVE(Reversed)
4983 	COPYELEMENT_SAVE(Looped)
4984 	COPYELEMENT_SAVE(Tweening)
4985 	COPYELEMENT_SAVE(LoopAfterTweening)
4986 	COPYELEMENT_SAVE(StopAfterTweening)
4987 	COPYELEMENT_SAVE(ElevationTweening)
4988 	COPYELEMENT_SAVE(DisableBleeding)
4989 	COPYELEMENT_SAVE(LockTopSection)
4990 	COPYELEMENT_SAVE(ZeroRootDisplacement)
4991 	COPYELEMENT_SAVE(ZeroRootRotation)
4992 	COPYELEMENT_SAVE(DisableSounds);
4993 
4994 	block->root_section_id = controller->Root_Section->IDnumber;
4995 
4996 	{
4997 		char* buffer;
4998 		char* Hierarchy_Name = controller->Root_Section->Hierarchy_Name;
4999 		char* Rif_Name = controller->Root_Section->Rif_Name;
5000 
5001 		//increase the block size by enough to hold these names
5002 		block->header.size+= strlen(Hierarchy_Name)+strlen(Rif_Name)+2;
5003 
5004 		//alllocate memoey , and copy names;
5005 		buffer = GetPointerForSaveBlock(strlen(Hierarchy_Name)+1);
5006 		strcpy(buffer,Hierarchy_Name);
5007 
5008 		buffer = GetPointerForSaveBlock(strlen(Rif_Name)+1);
5009 		strcpy(buffer,Rif_Name);
5010 
5011 	}
5012 
5013 	//save the delta sequences
5014 	{
5015 		DELTA_CONTROLLER* delta = controller->Deltas;
5016 
5017 		while(delta)
5018 		{
5019 			SaveHierarchyDelta(delta);
5020 			delta=delta->next_controller;
5021 		}
5022 	}
5023 
5024 	//now save the section data
5025 	SaveHierarchySectionRecursion(controller->section_data);
5026 }
5027 
5028 typedef struct hierarchy_delta_save_block
5029 {
5030 	SAVE_BLOCK_HEADER header;
5031 
5032 	int timer;
5033 	int lastframe_timer;
5034 	int sequence_type;
5035 	int sub_sequence;
5036 	int seconds_for_sequence;
5037 	int timer_increment;
5038 	int Looped:1;
5039 	int Playing:1;
5040 	int Active:1;
5041 
5042 }HIERARCHY_DELTA_SAVE_BLOCK;
5043 
5044 #undef SAVELOAD_BEHAV
5045 //defines for load/save macros
5046 #define SAVELOAD_BEHAV delta
5047 
5048 static void LoadHierarchyDelta(SAVE_BLOCK_HEADER* header,HMODELCONTROLLER* controller)
5049 {
5050 	DELTA_CONTROLLER* delta;
5051 	HIERARCHY_DELTA_SAVE_BLOCK* block = (HIERARCHY_DELTA_SAVE_BLOCK*) header;
5052 	char* name = (char*) (block+1);
5053 
5054 	delta = Get_Delta_Sequence(controller,name);
5055 	if(!delta)
5056 	{
5057 		delta = Add_Delta_Sequence(controller,name,block->sequence_type,block->sub_sequence,block->seconds_for_sequence);
5058 	}
5059 
5060 
5061 	COPYELEMENT_LOAD(timer)
5062 	COPYELEMENT_LOAD(lastframe_timer)
5063 	COPYELEMENT_LOAD(timer_increment)
5064 	COPYELEMENT_LOAD(Looped)
5065 	COPYELEMENT_LOAD(Playing)
5066 	COPYELEMENT_LOAD(Active)
5067 	COPYELEMENT_LOAD(sequence_type)
5068 	COPYELEMENT_LOAD(sub_sequence)
5069 	COPYELEMENT_LOAD(seconds_for_sequence)
5070 
5071 
5072 
5073 
5074 }
5075 
5076 static void SaveHierarchyDelta(DELTA_CONTROLLER* delta)
5077 {
5078 	HIERARCHY_DELTA_SAVE_BLOCK* block;
5079 	int size;
5080 
5081 	//work out memory needed
5082 	size=sizeof(*block) + strlen(delta->id) +1;
5083 	block = (HIERARCHY_DELTA_SAVE_BLOCK*) GetPointerForSaveBlock(size);
5084 
5085 	//fill in the header
5086 	block->header.size = size;
5087 	block->header.type = SaveBlock_HierarchyDelta;
5088 
5089 	COPYELEMENT_SAVE(timer)
5090 	COPYELEMENT_SAVE(lastframe_timer)
5091 	COPYELEMENT_SAVE(timer_increment)
5092 	COPYELEMENT_SAVE(Looped)
5093 	COPYELEMENT_SAVE(Playing)
5094 	COPYELEMENT_SAVE(Active)
5095 	COPYELEMENT_SAVE(sequence_type)
5096 	COPYELEMENT_SAVE(sub_sequence)
5097 	COPYELEMENT_SAVE(seconds_for_sequence)
5098 
5099 	//tack the name on the end
5100 	{
5101 		char* name = (char*)(block+1);
5102 		strcpy(name,delta->id);
5103 	}
5104 
5105 
5106 }
5107 
5108 
5109 typedef struct hierarchy_section_save_block
5110 {
5111 	SAVE_BLOCK_HEADER header;
5112 
5113 //from section
5114 	int IDnumber;
5115 
5116 //from section_data
5117 	VECTORCH Offset;
5118 	VECTORCH World_Offset;
5119 	VECTORCH Last_World_Offset;
5120 	MATRIXCH RelSecMat;
5121 	MATRIXCH SecMat;
5122 
5123 	struct damageblock current_damage;
5124 
5125 	int accumulated_timer;
5126 	int freezeframe_timer;
5127 	int lastframe_timer;
5128 	int gore_timer;
5129 
5130 	int flags;
5131 
5132 	int sequence_id;
5133 	int keyframe_time;
5134 
5135 
5136 	int replacement_id;
5137 
5138 }HIERARCHY_SECTION_SAVE_BLOCK;
5139 
5140 
5141 #undef SAVELOAD_BEHAV
5142 //defines for load/save macros
5143 #define SAVELOAD_BEHAV section
5144 
5145 extern HIERARCHY_SHAPE_REPLACEMENT* GetHierarchyAlternateShapeFromId(const char* rif_name,int replacement_id,char* section_name);
5146 
5147 
5148 static void LoadHierarchySection(SECTION_DATA* section)
5149 {
5150 	SAVE_BLOCK_HEADER* header;
5151 	SAVE_BLOCK_HEADER* decal_header;
5152 	SAVE_BLOCK_HEADER* tween_header;
5153 
5154 	HIERARCHY_SECTION_SAVE_BLOCK* block;
5155 
5156 	header=GetNextBlockIfOfType(SaveBlock_HierarchySection);
5157 	decal_header=GetNextBlockIfOfType(SaveBlock_HierarchyDecals);
5158 	tween_header=GetNextBlockIfOfType(SaveBlock_HierarchyTween);
5159 
5160 	/*
5161 	In this bit we go through the hierechy section data , and saved section data in increasing
5162 	ID number order. Whenever we find any sections without saved data , we need to prunce them
5163 	(since they will have been blown off the original hierarchy)
5164 	*/
5165 
5166 	while(header && section)
5167 	{
5168 		block = (HIERARCHY_SECTION_SAVE_BLOCK*) header;
5169 
5170 		if(block->header.size!=sizeof(*block)) return;
5171 
5172 		//compare section id numbers
5173 		if(block->IDnumber == section->sempai->IDnumber)
5174 		{
5175 			//copy stuff for this section then
5176 			COPYELEMENT_LOAD(Offset);
5177 			COPYELEMENT_LOAD(World_Offset);
5178 			COPYELEMENT_LOAD(Last_World_Offset);
5179 			COPYELEMENT_LOAD(RelSecMat);
5180 			COPYELEMENT_LOAD(SecMat);
5181 
5182 			COPYELEMENT_LOAD(current_damage);
5183 
5184 			COPYELEMENT_LOAD(accumulated_timer);
5185 			COPYELEMENT_LOAD(freezeframe_timer);
5186 			COPYELEMENT_LOAD(lastframe_timer);
5187 			COPYELEMENT_LOAD(gore_timer);
5188 
5189 			COPYELEMENT_LOAD(flags);
5190 			COPYELEMENT_LOAD(replacement_id);
5191 
5192 
5193 			//see if this section is using an alternate shape set
5194 			{
5195 				int desiredShapeIndex = section->sempai->ShapeNum;
5196 				HIERARCHY_SHAPE_REPLACEMENT* replacement = GetHierarchyAlternateShapeFromId(section->sempai->Rif_Name,block->replacement_id,section->sempai->Section_Name);
5197 
5198 				if(replacement)
5199 				{
5200 					desiredShapeIndex = replacement->replacement_shape_index;
5201 				}
5202 
5203 				if(section->ShapeNum != desiredShapeIndex)
5204 				{
5205 					section->ShapeNum = desiredShapeIndex;
5206 
5207 					if(desiredShapeIndex>=0)
5208 					{
5209 						section->Shape = mainshapelist[desiredShapeIndex];
5210 					}
5211 					else
5212 					{
5213 						section->Shape = 0;
5214 					}
5215 					Setup_Texture_Animation_For_Section(section);
5216 
5217 				}
5218 			}
5219 
5220 
5221 			//load decals
5222 			if(decal_header)
5223 			{
5224 				LoadHierarchySectionDecals(decal_header,section);
5225 			}
5226 			else
5227 			{
5228 				section->NumberOfDecals = 0;
5229 				section->NextDecalToUse	= 0;
5230 
5231 			}
5232 
5233 			//load tweening data
5234 			if(tween_header)
5235 			{
5236 				LoadHierarchySectionTween(tween_header,section);
5237 			}
5238 			else
5239 			{
5240 				section->Tweening = 0;
5241 			}
5242 
5243 
5244 			//get the current sequence and frame
5245 
5246 			{
5247 				int a;
5248 				int time;
5249 				SECTION* this_section = section->sempai;
5250 				section->current_sequence=&(this_section->sequence_array[0]);
5251 
5252 				for (a=0; a<this_section->num_sequences; a++) {
5253 					if (this_section->sequence_array[a].sequence_id==block->sequence_id) {
5254 						section->current_sequence=&(this_section->sequence_array[a]);
5255 						break;
5256 					}
5257 				}
5258 
5259 				time = block->keyframe_time;
5260 				section->current_keyframe = section->current_sequence->first_frame;
5261 
5262 				while(time>=section->current_keyframe->Sequence_Length)
5263 				{
5264 					time-= section->current_keyframe->Sequence_Length;
5265 
5266 					if(section->current_keyframe->last_frame)
5267 					{
5268 						break;
5269 					}
5270 					else
5271 					{
5272 						section->current_keyframe = section->current_keyframe->Next_Frame;
5273 					}
5274 				}
5275 
5276 
5277 
5278 
5279 
5280 			}
5281 
5282 			//move to the next section data
5283 			if(section->First_Child)
5284 			{
5285 				section = section->First_Child;
5286 			}
5287 			else if (section->Next_Sibling)
5288 			{
5289 				section = section->Next_Sibling;
5290 			}
5291 			else
5292 			{
5293 				BOOL section_found=FALSE;
5294 				while(section && !section_found)
5295 				{
5296 					section = section->My_Parent;
5297 					if(section)
5298 					{
5299 						if(section->Next_Sibling)
5300 						{
5301 							section=section->Next_Sibling;
5302 							section_found = TRUE;
5303 						}
5304 					}
5305 				}
5306 			}
5307 
5308 			//move to next saved section
5309 			header=GetNextBlockIfOfType(SaveBlock_HierarchySection);
5310 			decal_header=GetNextBlockIfOfType(SaveBlock_HierarchyDecals);
5311 			tween_header=GetNextBlockIfOfType(SaveBlock_HierarchyTween);
5312 		}
5313 		else if(block->IDnumber > section->sempai->IDnumber)
5314 		{
5315 			/*
5316 				There was no saved data for this section , so we will need to prune it
5317 			*/
5318 
5319 			SECTION_DATA* pruned_section = section;
5320 
5321 			if (section->Next_Sibling)
5322 			{
5323 				section = section->Next_Sibling;
5324 			}
5325 			else
5326 			{
5327 				BOOL section_found=FALSE;
5328 				while(section && !section_found)
5329 				{
5330 					section = section->My_Parent;
5331 					if(section)
5332 					{
5333 						if(section->Next_Sibling)
5334 						{
5335 							section=section->Next_Sibling;
5336 							section_found = TRUE;
5337 						}
5338 					}
5339 				}
5340 			}
5341 			Prune_Section(pruned_section);
5342 
5343 		}
5344 		else
5345 		{
5346 			//move to next saved section (we will need to advance until the saved id number matches
5347 			//the section id number)
5348 			//Nb. This probably never happens anyway
5349 			header=GetNextBlockIfOfType(SaveBlock_HierarchySection);
5350 			decal_header=GetNextBlockIfOfType(SaveBlock_HierarchyDecals);
5351 			tween_header=GetNextBlockIfOfType(SaveBlock_HierarchyTween);
5352 		}
5353 	}
5354 
5355 	//prune the remaining sections
5356 	while(section)
5357 	{
5358 		SECTION_DATA* pruned_section = section;
5359 
5360 		if (section->Next_Sibling)
5361 		{
5362 			section = section->Next_Sibling;
5363 		}
5364 		else
5365 		{
5366 			BOOL section_found=FALSE;
5367 			while(section && !section_found)
5368 			{
5369 				section = section->My_Parent;
5370 				if(section)
5371 				{
5372 					if(section->Next_Sibling)
5373 					{
5374 						section=section->Next_Sibling;
5375 						section_found = TRUE;
5376 					}
5377 				}
5378 			}
5379 		}
5380 		Prune_Section(pruned_section);
5381 	}
5382 
5383 }
5384 
5385 
5386 static void SaveHierarchySectionRecursion(SECTION_DATA* section)
5387 {
5388 	HIERARCHY_SECTION_SAVE_BLOCK* block;
5389 
5390 	GET_SAVE_BLOCK_POINTER(block);
5391 
5392 	//fill in header
5393 	block->header.type = SaveBlock_HierarchySection;
5394 	block->header.size = sizeof(*block);
5395 
5396 
5397 	block->IDnumber = section->sempai->IDnumber;
5398 
5399 	//copy stuff
5400 	COPYELEMENT_SAVE(Offset);
5401 	COPYELEMENT_SAVE(World_Offset);
5402 	COPYELEMENT_SAVE(Last_World_Offset);
5403 	COPYELEMENT_SAVE(RelSecMat);
5404 	COPYELEMENT_SAVE(SecMat);
5405 
5406 	COPYELEMENT_SAVE(current_damage);
5407 
5408 	COPYELEMENT_SAVE(accumulated_timer);
5409 	COPYELEMENT_SAVE(freezeframe_timer);
5410 	COPYELEMENT_SAVE(lastframe_timer);
5411 	COPYELEMENT_SAVE(gore_timer);
5412 
5413 	COPYELEMENT_SAVE(flags);
5414 	COPYELEMENT_SAVE(replacement_id);
5415 
5416 	//get current sequence id
5417 	block->sequence_id = section->current_sequence->sequence_id;
5418 
5419 	{
5420 		KEYFRAME_DATA* frame = section->current_sequence->first_frame;
5421 		int time=0;
5422 
5423 		while(frame != section->current_keyframe)
5424 		{
5425 			time += frame->Sequence_Length;
5426 
5427 			if(frame->last_frame) break;
5428 
5429 			frame = frame->Next_Frame;
5430 		}
5431 
5432 		block->keyframe_time = time;
5433 
5434 	}
5435 
5436 
5437 	//save decals (if needed)
5438 	if(section->NumberOfDecals)
5439 	{
5440 		SaveHierarchySectionDecals(section);
5441 	}
5442 
5443 	//save tweening	(if needed)
5444 	if(section->Tweening)
5445 	{
5446 		SaveHierarchySectionTween(section);
5447 	}
5448 
5449 	//recurse down hierarchy
5450 	if (section->First_Child!=NULL) {
5451 
5452 		SECTION_DATA * child_section;
5453 		/*
5454 		Must make sure the children are in the right order before saving.
5455 		*/
5456 		EnsureChildrenAreInAscendingIDOrder(section);
5457 
5458 		child_section = section->First_Child;
5459 
5460 		while (child_section)
5461 		{
5462 			SaveHierarchySectionRecursion(child_section);
5463 			child_section = child_section->Next_Sibling;
5464 		}
5465 	}
5466 
5467 
5468 }
5469 
5470 
5471 
5472 //section decal data
5473 typedef struct hierarchy_decal_save_block
5474 {
5475 	SAVE_BLOCK_HEADER header;
5476 
5477 	int	NumberOfDecals;
5478 	int NextDecalToUse;
5479 	OBJECT_DECAL Decals[MAX_NO_OF_DECALS_PER_HIERARCHICAL_SECTION];
5480 }HIERARCHY_DECAL_SAVE_BLOCK;
5481 
5482 
5483 
5484 
5485 
5486 static void LoadHierarchySectionDecals(SAVE_BLOCK_HEADER* header,SECTION_DATA* section)
5487 {
5488 	int i;
5489 
5490 	HIERARCHY_DECAL_SAVE_BLOCK* block = (HIERARCHY_DECAL_SAVE_BLOCK*) header;
5491 
5492 	COPYELEMENT_LOAD(NumberOfDecals);
5493 	COPYELEMENT_LOAD(NextDecalToUse);
5494 
5495 	for(i=0;i<block->NumberOfDecals;i++)
5496 	{
5497 		COPYELEMENT_LOAD(Decals[i]);
5498 	}
5499 
5500 }
5501 
5502 
5503 static void SaveHierarchySectionDecals(SECTION_DATA* section)
5504 {
5505 	HIERARCHY_DECAL_SAVE_BLOCK* block;
5506 	int i;
5507 
5508 	//determine how much space is required for the decals present
5509 	int size = sizeof(HIERARCHY_DECAL_SAVE_BLOCK);
5510 	size -= (MAX_NO_OF_DECALS_PER_HIERARCHICAL_SECTION - section->NumberOfDecals) * sizeof(OBJECT_DECAL);
5511 
5512 	block = (HIERARCHY_DECAL_SAVE_BLOCK*) GetPointerForSaveBlock(size);
5513 
5514 	//fill in the header
5515 	block->header.type = SaveBlock_HierarchyDecals;
5516 	block->header.size = size;
5517 
5518 	//copy the decals
5519 
5520 	COPYELEMENT_SAVE(NumberOfDecals);
5521 	COPYELEMENT_SAVE(NextDecalToUse);
5522 
5523 	for(i=0;i<block->NumberOfDecals;i++)
5524 	{
5525 		COPYELEMENT_SAVE(Decals[i]);
5526 	}
5527 
5528 
5529 }
5530 
5531 
5532 //section tweening data
5533 typedef struct hierarchy_tween_save_block
5534 {
5535 	SAVE_BLOCK_HEADER header;
5536 
5537 	/* Tweening */
5538 	VECTORCH stored_offset;
5539 	VECTORCH target_offset;
5540 	VECTORCH delta_offset;
5541 	QUAT stored_quat;
5542 	QUAT target_quat;
5543 	int omega;
5544 	int oneoversinomega;
5545 	int oneovertweeninglength;
5546 	unsigned int Tweening:1;
5547 
5548 
5549 }HIERARCHY_TWEEN_SAVE_BLOCK;
5550 
5551 static void LoadHierarchySectionTween(SAVE_BLOCK_HEADER* header,SECTION_DATA* section)
5552 {
5553 	//see if this section has tweening data saved
5554 	HIERARCHY_TWEEN_SAVE_BLOCK* block = (HIERARCHY_TWEEN_SAVE_BLOCK*) header;
5555 	if(!block) return;
5556 
5557 	COPYELEMENT_LOAD(stored_offset);
5558    	COPYELEMENT_LOAD(target_offset);
5559    	COPYELEMENT_LOAD(delta_offset);
5560    	COPYELEMENT_LOAD(stored_quat);
5561    	COPYELEMENT_LOAD(target_quat);
5562    	COPYELEMENT_LOAD(omega);
5563    	COPYELEMENT_LOAD(oneoversinomega);
5564    	COPYELEMENT_LOAD(oneovertweeninglength);
5565 	COPYELEMENT_LOAD(Tweening);
5566 }
5567 
5568 static void SaveHierarchySectionTween(SECTION_DATA* section)
5569 {
5570 	HIERARCHY_TWEEN_SAVE_BLOCK* block;
5571 
5572 	GET_SAVE_BLOCK_POINTER(block);
5573 
5574 	//fill in the header
5575 	block->header.type = SaveBlock_HierarchyTween;
5576 	block->header.size = sizeof(*block);
5577 
5578 	//copy stuff
5579 
5580 	COPYELEMENT_SAVE(stored_offset);
5581    	COPYELEMENT_SAVE(target_offset);
5582    	COPYELEMENT_SAVE(delta_offset);
5583    	COPYELEMENT_SAVE(stored_quat);
5584    	COPYELEMENT_SAVE(target_quat);
5585    	COPYELEMENT_SAVE(omega);
5586    	COPYELEMENT_SAVE(oneoversinomega);
5587    	COPYELEMENT_SAVE(oneovertweeninglength);
5588 	COPYELEMENT_SAVE(Tweening);
5589 
5590 }
5591