1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * Additional copyright for this file:
8  * Copyright (C) 1999-2000 Revolution Software Ltd.
9  * This code is based on source code created by Revolution Software,
10  * used with permission.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  */
27 
28 #include "engines/icb/p4.h"
29 #include "engines/icb/common/px_anims.h"
30 #include "engines/icb/session.h"
31 #include "engines/icb/animation_mega_set.h"
32 #include "engines/icb/global_objects.h"
33 #include "engines/icb/debug.h"
34 #include "engines/icb/player.h"
35 #include "engines/icb/res_man.h"
36 
37 namespace ICB {
38 
Easy_frame_motion_and_pan(__mega_set_names anim_type,bool8)39 bool8 _game_session::Easy_frame_motion_and_pan(__mega_set_names anim_type, bool8 /*player*/) {
40 	// advances frame and motion AND PAN but does not check for barrier collisions
41 	PXreal xnext, znext;
42 	PXreal x, z;
43 
44 	ANIM_CHECK(anim_type);
45 
46 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
47 
48 	if (L->anim_pc + 1 >= pAnim->frame_qty)
49 		Fatal_error("Easy_frame_and_motion finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type),
50 		            L->anim_pc, pAnim->frame_qty);
51 
52 	// add the change in pan from the last frame to the current frame of the anim to the logic objects pan
53 	PXreal pan1, pan2;
54 
55 	// Get next frame from the anim
56 	PXframe *nextFrame = PXFrameEnOfAnim(L->anim_pc + 1, pAnim);
57 
58 	// Get current frame from the anim
59 	PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
60 
61 	nextFrame->markers[ORG_POS].GetPan(&pan1);
62 	currentFrame->markers[ORG_POS].GetPan(&pan2);
63 
64 	L->pan += (pan1 - pan2); // update by difference
65 
66 	// get motion displacement from currently displayed frame to next one
67 	// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
68 	PXreal x1, x2, z1, z2, unused;
69 
70 	nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
71 	currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
72 
73 	xnext = x2 - x1;
74 	znext = z2 - z1;
75 
76 	// advance the frame
77 	L->anim_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1);
78 
79 	// get the pan unwind value of the frame to be printed
80 	PXreal pan;
81 
82 	// Get the new current frame, using a new reference !
83 	// as GCC got it all wrong if the same reference was re-used
84 	currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
85 
86 	currentFrame->markers[ORG_POS].GetPan(&pan);
87 
88 	L->pan_adjust = pan; // this value will be unwound from the orientation of the frame at render time in stage draw
89 
90 	// calculate the new x and z coordinate from this frames motion offset
91 	// do the z and x together
92 	PXfloat ang = (L->pan - L->pan_adjust) * TWO_PI;
93 	PXfloat cang = (PXfloat)PXcos(ang);
94 	PXfloat sang = (PXfloat)PXsin(ang);
95 
96 	x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
97 	z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
98 
99 	// x and z are the new coordinates
100 
101 	// record the new true actor position
102 	M->actor_xyz.x = x;
103 	M->actor_xyz.z = z;
104 
105 	// check for new parent box and if so bring barriers
106 	if (L->pan >= HALF_TURN)
107 		L->pan -= FULL_TURN;
108 	else if (L->pan <= -HALF_TURN)
109 		L->pan += FULL_TURN;
110 
111 	return (TRUE8);
112 }
113 
Easy_frame_and_motion(__mega_set_names anim_type,bool8,uint8 nFrames)114 bool8 _game_session::Easy_frame_and_motion(__mega_set_names anim_type, bool8 /*player*/, uint8 nFrames) {
115 	// advances frame and motion but does not check for barrier collisions
116 	PXreal xnext, znext;
117 	PXreal x, z;
118 
119 	ANIM_CHECK(anim_type);
120 
121 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
122 
123 	if ((L->anim_pc + nFrames) >= pAnim->frame_qty)
124 		Fatal_error("Easy_frame_and_motion finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type),
125 		            L->anim_pc, pAnim->frame_qty);
126 
127 	// get motion displacement from currently displayed frame to next one
128 	// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
129 	PXreal x1, x2, z1, z2, unused;
130 
131 	// Get next frame from the anim
132 	PXframe *nextFrame = PXFrameEnOfAnim(L->anim_pc + nFrames, pAnim);
133 	// Get current frame from the anim
134 	PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
135 
136 	nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
137 	currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
138 
139 	xnext = x2 - x1;
140 	znext = z2 - z1;
141 
142 	// calculate the new x and z coordinate from this frames motion offset
143 	// do the z and x together
144 	PXfloat ang = L->pan * TWO_PI;
145 	PXfloat cang = (PXfloat)PXcos(ang);
146 	PXfloat sang = (PXfloat)PXsin(ang);
147 
148 	x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
149 	z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
150 	// x and z are the new coordinates
151 
152 	// advance the frame
153 	L->anim_pc = (L->anim_pc + nFrames);
154 
155 	// record the new true actor position
156 	M->actor_xyz.x = x;
157 	M->actor_xyz.z = z;
158 
159 	return (TRUE8);
160 }
161 
Advance_frame_and_motion(__mega_set_names anim_type,bool8 pl,uint8 nFrames)162 bool8 _game_session::Advance_frame_and_motion(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
163 	// attempts to move the frame forward and move the character
164 	// returns fail and frame is not changed if the way forward is blocked by a barrier
165 	// the frame counter will be looped
166 	// handles shallow barrier bump correction!
167 
168 	__barrier_result ret;
169 	uint32 initial_pc = L->anim_pc;
170 
171 	ANIM_CHECK(anim_type);
172 
173 	ret = Core_advance(anim_type, pl, nFrames);
174 
175 	if (ret == __BLOCKED)
176 		return (FALSE8);
177 
178 	if ((ret == __OK) || (ret == __NUDGED))
179 		return (TRUE8);
180 
181 	// pan was corrected - go around again
182 
183 	L->anim_pc = initial_pc;
184 	ret = Core_advance(anim_type, pl, nFrames);
185 
186 	if (ret == __BLOCKED) // v odd indeed
187 		return (FALSE8);
188 
189 	if ((ret == __OK) || (ret == __NUDGED))
190 		return (TRUE8); // good
191 
192 	// corrected again - this is strange
193 
194 	return (FALSE8);
195 }
196 
Core_advance(__mega_set_names anim_type,bool8 pl,uint8 nFrames)197 __barrier_result _game_session::Core_advance(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
198 	PXreal xnext, znext;
199 	PXreal x, z;
200 	__barrier_result ret;
201 
202 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
203 
204 	if ((L->anim_pc + nFrames) >= pAnim->frame_qty)
205 		Fatal_error("Core_advance finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type), L->anim_pc,
206 		            pAnim->frame_qty);
207 
208 	// get motion displacement from currently displayed frame to next one
209 	// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
210 	PXreal x1, x2, z1, z2, unused;
211 
212 	// Get next frame from the anim
213 	PXframe *nextFrame = PXFrameEnOfAnim(L->anim_pc + nFrames, pAnim);
214 	// Get current frame from the anim
215 	PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
216 
217 	nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
218 	currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
219 
220 	xnext = x2 - x1;
221 	znext = z2 - z1;
222 
223 	// calculate the new x and z coordinate from this frames motion offset
224 	// do the z and x together
225 	PXfloat ang = L->pan * TWO_PI;
226 	PXfloat cang = (PXfloat)PXcos(ang);
227 	PXfloat sang = (PXfloat)PXsin(ang);
228 
229 	x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
230 	z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
231 	// x and z are the new coordinates
232 
233 	// but, are we walking into a barrier?
234 	ret = Check_barrier_bump_and_bounce(x, M->actor_xyz.y, z,                           // new position
235 	                                    M->actor_xyz.x, M->actor_xyz.y, M->actor_xyz.z, // old position
236 	                                    pl);
237 
238 	L->anim_pc = (L->anim_pc + nFrames);
239 
240 	// did we move forward without a problem?
241 	if (ret == __OK) {
242 		// advance the frame
243 
244 		// record the new true actor position
245 		M->actor_xyz.x = x;
246 		M->actor_xyz.z = z;
247 
248 		// check for new parent box and if so bring barriers
249 		Prepare_megas_route_barriers(pl);
250 	}
251 
252 	return (ret);
253 }
254 
Normalise_anim_pc()255 void _game_session::Normalise_anim_pc() {
256 	// routines that make raw calls to advance_frame_and_motion need to call this to reset anim pc
257 	// this is because of changes required by the multi-speed capabilities
258 
259 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(L->cur_anim_type), I->info_name_hash[L->cur_anim_type], I->base_path, I->base_path_hash); //
260 	L->anim_pc = (L->anim_pc) % (pAnim->frame_qty - 1);
261 }
262 
Reverse_frame_and_motion(__mega_set_names anim_type,bool8 pl,uint8 nFrames)263 bool8 _game_session::Reverse_frame_and_motion(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
264 	// as Advance_frame_and_motion but frames work backward
265 	// the frame counter will be looped
266 	// handles shallow barrier bump correction!
267 
268 	__barrier_result ret;
269 
270 	ANIM_CHECK(anim_type);
271 
272 	ret = Core_reverse(anim_type, pl, nFrames);
273 
274 	if (ret == __BLOCKED)
275 		return (FALSE8);
276 
277 	if ((ret == __OK) || (ret == __NUDGED))
278 		return (TRUE8);
279 
280 	// pan was corrected - go around again
281 	ret = Core_reverse(anim_type, pl, nFrames);
282 
283 	if (ret == __BLOCKED) // v odd indeed
284 		return (FALSE8);
285 
286 	if ((ret == __OK) || (ret == __NUDGED))
287 		return (TRUE8); // good
288 
289 	// corrected again - this is strange
290 
291 	return (FALSE8);
292 }
293 
Core_reverse(__mega_set_names anim_type,bool8 pl,uint8 nFrames)294 __barrier_result _game_session::Core_reverse(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
295 	// as Advance_frame_and_motion but frames work backward
296 	// the frame counter will be looped
297 	// handles shallow barrier bump correction!
298 
299 	PXreal xnext, znext;
300 	PXreal x, z;
301 	__barrier_result ret;
302 	uint32 next_pc;
303 
304 	ANIM_CHECK(anim_type);
305 
306 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
307 
308 	// if we are on frame 0 then shift up to pretend we're coming in off the dummy frame
309 	if (!L->anim_pc) {
310 		next_pc = pAnim->frame_qty - 2;
311 		L->anim_pc = pAnim->frame_qty - 1;
312 	} else if (L->anim_pc < nFrames)
313 		next_pc = 0;
314 	else
315 		next_pc = L->anim_pc - nFrames;
316 
317 	if ((next_pc >= pAnim->frame_qty) || (L->anim_pc >= pAnim->frame_qty))
318 		Fatal_error("Core_reverse finds [%s] has illegal frame in anim [%s] %d %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type), next_pc,
319 		            L->anim_pc, pAnim->frame_qty);
320 
321 	// get motion displacement from currently displayed frame to next one
322 	// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
323 	PXreal x1, x2, z1, z2, unused;
324 
325 	// Get next frame from the anim
326 	PXframe *nextFrame = PXFrameEnOfAnim(next_pc, pAnim);
327 	// Get current frame from the anim
328 	PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
329 
330 	nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
331 	currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
332 
333 	xnext = x2 - x1;
334 	znext = z2 - z1;
335 
336 	// calculate the new x and z coordinate from this frames motion offset
337 	// do the z and x together
338 	PXfloat ang = L->pan * TWO_PI;
339 	PXfloat cang = (PXfloat)PXcos(ang);
340 	PXfloat sang = (PXfloat)PXsin(ang);
341 
342 	x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
343 	z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
344 	// x and z are the new coordinates
345 
346 	// but, are we walking into a barrier?
347 	ret = Check_barrier_bump_and_bounce(x, M->actor_xyz.y, z,                           // new position
348 	                                    M->actor_xyz.x, M->actor_xyz.y, M->actor_xyz.z, // old position
349 	                                    pl);
350 
351 	L->anim_pc = next_pc;
352 
353 	// did we move forward without a problem?
354 	if (ret == __OK) {
355 		// record the new true actor position
356 		M->actor_xyz.x = x;
357 		M->actor_xyz.z = z;
358 
359 		// check for new parent box and if so bring barriers
360 		Prepare_megas_route_barriers(pl);
361 	}
362 
363 	return (ret);
364 }
365 
Animate_turn_to_pan(__mega_set_names anim_type,uint32 speedup)366 void _game_session::Animate_turn_to_pan(__mega_set_names anim_type, uint32 speedup) {
367 	// route manager turns character on the spot until facing route->target_pan
368 	// final pan may need to be snapped to avoid overrun
369 	// once done, x,z are restored to pre-turn animations - this is essential if character is to follow route correctly
370 	// there is no barrier check!
371 
372 	// do a frames turn anim
373 	// have we finished
374 	// snap pan
375 	// restore x,z
376 
377 	uint32 next_pc, info_pc;
378 	PXfloat this_pan_change;
379 
380 	// set type
381 	L->cur_anim_type = anim_type;
382 
383 	ANIM_CHECK(anim_type);
384 
385 	// get anim set
386 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(L->voxel_info->get_info_name(anim_type), L->voxel_info->info_name_hash[anim_type], L->voxel_info->base_path,
387 	                                             L->voxel_info->base_path_hash); //
388 
389 	// anim pc may be illegal so neutralise it
390 	L->anim_pc = (L->anim_pc) % (pAnim->frame_qty - 1);
391 
392 	// adjust the frame PC in the direction we're going to turn
393 	if (!M->turn_dir) {
394 		// turning right (clockwise)
395 		// anims are clockwise to run naturally through frames
396 		next_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1); // next frame to display
397 		info_pc = L->anim_pc + 1;                            // from to take movement info from
398 	} else {                                                     // pos
399 		// turning left
400 		// reverse the anim
401 
402 		if (!L->anim_pc) {                                // pc==0
403 			info_pc = next_pc = pAnim->frame_qty - 2; // take movement info from dummy marker frame
404 			L->anim_pc = pAnim->frame_qty - 1;        // display last legal frame - have to force this for math to work
405 		} else {                                          // pc is non zero
406 			info_pc = next_pc = L->anim_pc - 1;       // display and movement info from next frame down
407 		}
408 	}
409 
410 	if ((info_pc >= pAnim->frame_qty) || (next_pc >= pAnim->frame_qty))
411 		Fatal_error("Animate_turn_to_pan [%s] using illegal frame", object->GetName());
412 
413 	// update engine pan with the difference between pan of previous frame and next frame
414 	PXreal pan1, pan2;
415 
416 	// Get info_pc frame from the anim
417 	PXframe *infoFrame = PXFrameEnOfAnim(info_pc, pAnim);
418 	// Get current frame from the anim
419 	PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
420 
421 	infoFrame->markers[ORG_POS].GetPan(&pan1);
422 	currentFrame->markers[ORG_POS].GetPan(&pan2);
423 
424 	this_pan_change = (pan1 - pan2); // update by difference
425 
426 	// times the speedup for fast turn functions - normally 0
427 	this_pan_change *= speedup;
428 
429 	if (this_pan_change >= HALF_TURN) {
430 		this_pan_change -= FULL_TURN;
431 	} else if (this_pan_change <= -HALF_TURN) {
432 		this_pan_change += FULL_TURN; // stop the wrap
433 	}
434 
435 	if (PXfabs(this_pan_change) > M->target_pan) {
436 		// we are going to turn too much this go so clip
437 
438 		L->pan = M->actual_target_pan; // clip to actual left
439 
440 		M->target_pan = ZERO_TURN; // we're done
441 
442 	} else {
443 		// still further to go
444 
445 		L->pan += this_pan_change;
446 
447 		M->target_pan = M->target_pan - (PXfloat)PXfabs(this_pan_change);
448 	}
449 
450 	// get motion displacement from currently displayed frame to next one
451 	// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
452 	PXreal x1, x2, z1, z2, unused;
453 
454 	// Get the next frame from the anim
455 	PXframe *nextFrame = PXFrameEnOfAnim(next_pc, pAnim);
456 
457 	nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
458 
459 	// Note, assumes current frame hasn't changed i.e L->info_pc is same
460 	currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
461 
462 	// FIXME: xnext and znext are not used currently...
463 	//PXreal xnext = x2 - x1;
464 	//PXreal znext = z2 - z1;
465 
466 	// update pc
467 	L->anim_pc = next_pc; // allready computed
468 
469 	// get the pan unwind value of the frame to be printed
470 	PXreal pan;
471 
472 	// Note, L->anim_pc = next_pc
473 	nextFrame->markers[ORG_POS].GetPan(&pan);
474 
475 	L->pan_adjust = pan;
476 
477 	// FIXME: ang, cang and sang are not used currently...
478 	// calculate the new x and z coordinate from this frames motion offset
479 	// do the z and x together
480 	// PXfloat ang = (L->pan - L->pan_adjust) * TWO_PI;
481 	//PXfloat cang = (PXfloat)PXcos(ang);
482 	//PXfloat sang = (PXfloat)PXsin(ang);
483 
484 	// FIXME: x and z are not used currently...
485 	//PXreal x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
486 	//PXreal z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
487 	// x and z are the new coordinates
488 
489 	if (L->pan >= HALF_TURN)
490 		L->pan -= FULL_TURN;
491 	else if (L->pan <= -HALF_TURN)
492 		L->pan += FULL_TURN; // stop the wrap
493 }
494 
Calc_target_pan(PXreal x,PXreal z,PXreal x2,PXreal z2)495 bool8 _game_session::Calc_target_pan(PXreal x, PXreal z, PXreal x2, PXreal z2) {
496 	// will LOOK TOWARDS a shallow angle or set M->target_pan
497 	// not to be used by routing routines!
498 
499 	// x,z are source coordinate
500 	// x2,z2 are target coordinate
501 
502 	// returns   FALSE no turn required
503 	//				TRUE turn required
504 
505 	PXfloat new_pan, diff;
506 
507 	new_pan = PXAngleOfVector(z - z2, x - x2); // work out vector
508 
509 	M->looking_pan = new_pan;
510 
511 	// get difference between the two
512 	diff = new_pan - L->pan;
513 
514 	if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
515 		// work out which way to turn
516 		if (diff > HALF_TURN)
517 			diff -= FULL_TURN;
518 
519 		else if (diff < -HALF_TURN)
520 			diff += FULL_TURN;
521 
522 		// diff is now the distance to turn by and its sign denotes direction
523 
524 		if (diff < FLOAT_ZERO) {
525 			M->turn_dir = 0; // right
526 		} else {
527 			M->turn_dir = 1; // left
528 		}
529 
530 		M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
531 
532 		M->actual_target_pan = new_pan; // actual target which we may clip to
533 
534 		L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
535 
536 		// we face straight ahead
537 		I->lookBone.boneTarget.vz = (int16)(0);
538 
539 		return (TRUE8);
540 	} else {
541 		// face towards object or whatever with upper body
542 		I->lookBone.boneNumber = 1;
543 		I->lookBone.boneSpeed = 128;
544 		I->lookBone.boneTarget.vz = (int16)(diff * (4096 / FULL_TURN)); // on psx diff is 0-4096 (full_turn), so we want diff*1     = diff * (4096/4096)
545 		// on pc diff is 0-1 (full_turn), we we want diff*4096      = diff * (4096/1)
546 
547 		return (FALSE8);
548 	}
549 }
550 
Calc_target_pan_no_bones(PXreal x,PXreal z,PXreal x2,PXreal z2)551 bool8 _game_session::Calc_target_pan_no_bones(PXreal x, PXreal z, PXreal x2, PXreal z2) {
552 	// will snap a shallow angle or set M->target_pan
553 
554 	//	x,z are source coordinate
555 	// x2,z2 are target coordinate
556 
557 	// returns   FALSE no turn required
558 	//				TRUE turn required
559 
560 	PXfloat new_pan, diff;
561 
562 	new_pan = PXAngleOfVector(z - z2, x - x2); // work out vector
563 
564 	// get difference between the two
565 	diff = new_pan - L->pan;
566 
567 	if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
568 		// work out which way to turn
569 		if (diff > HALF_TURN)
570 			diff -= FULL_TURN;
571 
572 		else if (diff < -HALF_TURN)
573 			diff += FULL_TURN;
574 
575 		// diff is now the distance to turn by and its sign denotes direction
576 
577 		if (diff < FLOAT_ZERO) {
578 			M->turn_dir = 0; // right
579 		} else {
580 			M->turn_dir = 1; // left
581 		}
582 
583 		M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
584 
585 		M->actual_target_pan = new_pan; // actual target which we may clip to
586 
587 		L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
588 
589 		// we face straight ahead
590 		I->lookBone.boneTarget.vz = (int16)(0);
591 
592 		return (TRUE8);
593 	} else {
594 		// shallow angle so snap and continue as normal
595 		L->pan = new_pan;
596 
597 		return FALSE8;
598 	}
599 }
600 
Soft_start_with_double_link(__mega_set_names link_one,__mega_set_names link_two,__mega_set_names next_anim)601 void _game_session::Soft_start_with_double_link(__mega_set_names link_one, __mega_set_names link_two, __mega_set_names next_anim) {
602 	// soft start from best os 2 link anims (if CAPS available) and push follow on animation
603 
604 	int32 diff = 1000000; // a big number
605 
606 	// is the link anim available?
607 	if (I->IsAnimTable(link_one)) {
608 		// push hard start follow on animation
609 		M->next_anim_type = next_anim;
610 
611 		// will pick best
612 		diff = Soften_up_anim_file(link_one, diff);
613 
614 		if (I->IsAnimTable(link_two)) {
615 			// may pick best
616 			Soften_up_anim_file(link_two, diff);
617 		}
618 	} else { // first link anim requested not available so skip the second and jump straight in
619 		// set anim type
620 		L->anim_pc = 0;
621 		L->cur_anim_type = next_anim;
622 	}
623 }
624 
Soft_start_with_single_link(__mega_set_names link_anim,__mega_set_names next_anim)625 void _game_session::Soft_start_with_single_link(__mega_set_names link_anim, __mega_set_names next_anim) {
626 	// soft start a link anim (if CAPS available) and push follow on animation
627 
628 	int32 diff = 1000000; // a big number
629 
630 	// is the link anim available?
631 	if (I->IsAnimTable(link_anim)) {
632 		// will pick best
633 		Soften_up_anim_file(link_anim, diff);
634 
635 		// push hard start follow on animation
636 		M->next_anim_type = next_anim;
637 	} else { // link anim requested but not available
638 		// set anim type
639 		L->anim_pc = 0;
640 		L->cur_anim_type = next_anim;
641 	}
642 }
643 
Soft_start_single_anim(__mega_set_names next_anim)644 void _game_session::Soft_start_single_anim(__mega_set_names next_anim) {
645 	// select best leg position for specified animation
646 	// there is no optional link
647 	// it HAS to be ok to skip past frames in the new anim
648 	// also sets cur_anim_type
649 
650 	int32 diff = 1000000; // a big number
651 
652 	// next anim not in CAPS
653 	if ((!L->voxel_info->IsAnimTable(next_anim))) {
654 		// cant go on without a major anim - this is not a link so cant just be skipped
655 		Shut_down_object("by Soft_start_single_anim next anim dont exist");
656 		return;
657 	}
658 
659 	// will pick best
660 	Soften_up_anim_file(next_anim, diff);
661 
662 	// set anim type
663 	L->cur_anim_type = next_anim;
664 
665 	// nothing to move onto
666 	M->next_anim_type = __NO_ANIM;
667 }
668 
Hard_start_single_anim(__mega_set_names next_anim)669 void _game_session::Hard_start_single_anim(__mega_set_names next_anim) {
670 	// jumps straight into new anim on frame 0
671 	// sets cur_anim_type
672 
673 	// next anim not in CAPS
674 	if ((!L->voxel_info->IsAnimTable(next_anim))) {
675 		// cant go on without a major anim - this is not a link so cant just be skipped
676 		Shut_down_object("by Hard_start_single_anim next anim dont exist");
677 		return;
678 	}
679 
680 	// hard start on frame 0
681 	L->anim_pc = 0;
682 
683 	// set anim type
684 	L->cur_anim_type = next_anim;
685 
686 	// nothing to move onto
687 	M->next_anim_type = __NO_ANIM;
688 }
689 
Soften_up_anim_file(__mega_set_names link,int32 diff)690 int32 _game_session::Soften_up_anim_file(__mega_set_names link, int32 diff) {
691 	// pick best frame in passed anim compared to best so far passed in 'diff'
692 	uint32 old_leg_pos;
693 	int32 j;
694 
695 	// Jake check the anim exists / make its name
696 	ANIM_CHECK(L->cur_anim_type);
697 
698 	// Do this first to make the whole res_open defrag thing work nicely
699 	PXanim *pCur_Anim = (PXanim *)rs_anims->Res_open(I->get_info_name(L->cur_anim_type), I->info_name_hash[L->cur_anim_type], I->base_path, I->base_path_hash);
700 
701 	// find out leg position for current frame
702 	old_leg_pos = PXFrameEnOfAnim(L->anim_pc, pCur_Anim)->left_foot_distance;
703 
704 	// Jake check the link anim exists / make its name
705 	ANIM_CHECK(link);
706 	PXanim *pLnk_Anim = (PXanim *)rs_anims->Res_open(I->get_info_name(link), I->info_name_hash[link], I->base_path, I->base_path_hash); //
707 	// special check for only one frame
708 	if (pLnk_Anim->frame_qty == 1) {
709 		L->cur_anim_type = link; // set type
710 		L->anim_pc = 0;          // first frame
711 		return (diff);
712 	}
713 
714 	// see which has the closest leg position
715 	for (j = 0; j < (pLnk_Anim->frame_qty - 1); j++) {
716 		int32 foot = PXFrameEnOfAnim(j, pLnk_Anim)->left_foot_distance;
717 		int32 d = twabs(foot - old_leg_pos);
718 
719 		if (d < diff) {
720 			diff = d;
721 			L->cur_anim_type = link; // set type
722 			L->anim_pc = j;          // this frame is best so far
723 		}
724 	}
725 
726 	// return best diff so far incase we're coming back on a multiple link anim scheme
727 	return (diff);
728 }
729 
Play_anim()730 bool8 _game_session::Play_anim() {
731 	// plays anim until finished
732 	// keeps playing anim even if motion has been stopped
733 	// when current is done it will play the queued M->next_anim_type if it exists. When thats done we're done
734 
735 	// returns   1 when done
736 	//				0 we need to come back next cycle
737 
738 	// get animation
739 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(L->cur_anim_type), I->info_name_hash[L->cur_anim_type], I->base_path, I->base_path_hash);
740 
741 	// last frame currently displayed?
742 	if ((int32)(L->anim_pc + 1) == (pAnim->frame_qty - 1)) {
743 		// we displayed the last frame last cycle - so display first of new mode this cycle
744 		// we may have been playing a link anim - so check for a new anim waiting in M->next_anim_type
745 		if (M->next_anim_type == __NO_ANIM) {
746 			// we're finished
747 			return (TRUE8);
748 		}
749 
750 		// reset pc ready for whatever starts next
751 		// current must be link so we go into new on frame 0
752 		L->anim_pc = 0;
753 
754 		// ok, there is a link anim
755 		L->cur_anim_type = M->next_anim_type;
756 		M->next_anim_type = __NO_ANIM; // must clear this or it'll play forever
757 
758 		// need to come back
759 		return (FALSE8);
760 	}
761 
762 	// shift character and frame forward by the amount appropriate
763 	Advance_frame_and_motion(L->cur_anim_type, 0, 1);
764 
765 	// need to come back
766 	return (FALSE8);
767 }
768 
Play_reverse_anim()769 bool8 _game_session::Play_reverse_anim() {
770 	// plays anim until finished
771 	// keeps playing anim even if motion has been stopped
772 	// when current is done it will play the queued M->next_anim_type if it exists. When thats done we're done
773 
774 	// returns   1 when done
775 	//				0 we need to come back next cycle
776 	Zdebug("Play_reverse_anim");
777 
778 	// last frame currently displayed?
779 	if (!L->anim_pc) {
780 		// we displayed the last frame last cycle - so display first of new mode this cycle
781 
782 		// we may have been playing a link anim - so check for a new anim waiting in M->next_anim_type
783 		if (M->next_anim_type == __NO_ANIM) {
784 			Zdebug("done");
785 			// we're finished
786 			return (TRUE8);
787 		}
788 
789 		// reset pc ready for whatever starts next
790 		// current must be link so we go into new on frame 0
791 		L->anim_pc = 0;
792 
793 		// ok, there is a link anim
794 		L->cur_anim_type = M->next_anim_type;
795 		M->next_anim_type = __NO_ANIM; // must clear this or it'll play forever
796 
797 		// need to come back
798 		return (FALSE8);
799 	}
800 
801 	// shift character and frame forward by the amount appropriate
802 	Reverse_frame_and_motion(L->cur_anim_type, 0, 1);
803 
804 	Zdebug("~Reverse");
805 
806 	// need to come back
807 	return (FALSE8);
808 }
809 
Play_anim_with_no_movement()810 bool8 _game_session::Play_anim_with_no_movement() {
811 	// plays anim until finished
812 	// keeps playing anim even if motion has been stopped
813 	// when current is done it will play the queued M->next_anim_type if it exists. When thats done we're done
814 
815 	// returns   1 when done
816 	//			0 we need to come back next cycle
817 
818 	// get animation
819 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(L->cur_anim_type), I->info_name_hash[L->cur_anim_type], I->base_path, I->base_path_hash); //
820 
821 	// last frame currently displayed?
822 	if ((int32)(L->anim_pc + 1) == (pAnim->frame_qty - 1)) {
823 		// we displayed the last frame last cycle - so display first of new mode this cycle
824 
825 		// we may have been playing a link anim - so check for a new anim waiting in M->next_anim_type
826 		if (M->next_anim_type == __NO_ANIM) {
827 			// we're finished
828 			return (TRUE8);
829 		}
830 
831 		// reset pc ready for whatever starts next
832 		// current must be link so we go into new on frame 0
833 		L->anim_pc = 0;
834 
835 		// ok, there is a link anim
836 		L->cur_anim_type = M->next_anim_type;
837 		M->next_anim_type = __NO_ANIM; // must clear this or it'll play forever
838 
839 		// need to come back
840 		return (FALSE8);
841 	}
842 
843 	// shift character and frame forward by the amount appropriate
844 
845 	// ok, advance frame but not position
846 	L->anim_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1);
847 
848 	// need to come back
849 	return (FALSE8);
850 }
851 
Set_motion(__motion motion)852 void _game_session::Set_motion(__motion motion) {
853 	// set motion type for cur object
854 	M->motion = motion;
855 }
856 
Get_motion()857 __motion _game_session::Get_motion() {
858 	// get motion type for cur object
859 	return (M->motion);
860 }
861 
Set_pose(__weapon weapon)862 void _game_session::Set_pose(__weapon weapon) {
863 	// set weapon type for cur object
864 	M->weapon = weapon;
865 }
866 
Change_pose_in_current_anim_set()867 void _game_session::Change_pose_in_current_anim_set() {
868 	// change the weapon set
869 	// delete an old one
870 	// we very much expect a set to already have been picked by the script
871 	// its likely that this will only be called by the player arm/unarm code
872 
873 	// create _vox_image object
874 	logic_structs[cur_id]->voxel_info->___init(M->chr_name, M->anim_set, logic_structs[cur_id]->mega->Fetch_pose()); // we pass the person, set names through
875 }
876 
Fetch_last_frame(__mega_set_names anima)877 uint32 _game_session::Fetch_last_frame(__mega_set_names anima) {
878 	// return final frame number in animation
879 	if ((!I->IsAnimTable(anima))) {
880 		Fatal_error("Fetch_last_frame cant access illegal anim [%s]", master_anim_name_table[anima].name);
881 	}
882 
883 	PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anima), I->info_name_hash[anima], I->base_path, I->base_path_hash);
884 
885 	return (pAnim->frame_qty - 1);
886 }
887 
888 } // End of namespace ICB
889