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/common/px_common.h"
29 #include "engines/icb/debug.h"
30 #include "engines/icb/p4_generic.h"
31 #include "engines/icb/common/px_scriptengine.h"
32 #include "engines/icb/common/px_game_object.h"
33 #include "engines/icb/icb.h"
34 #include "engines/icb/common/ptr_util.h"
35 #include "engines/icb/floors.h"
36 #include "engines/icb/mission.h"
37 #include "engines/icb/global_objects.h"
38 #include "engines/icb/object_structs.h"
39 #include "engines/icb/sound.h"
40 #include "engines/icb/res_man.h"
41 
42 namespace ICB {
43 
fn_apply_bullet(int32 & result,int32 * params)44 mcodeFunctionReturnCodes fn_apply_bullet(int32 &result, int32 *params) { return (g_mission->session->fn_apply_bullet(result, params)); }
45 
fn_set_to_last_frame_generic_anim(int32 & result,int32 * params)46 mcodeFunctionReturnCodes fn_set_to_last_frame_generic_anim(int32 &result, int32 *params) { return (MS->fn_set_to_last_frame_generic_anim(result, params)); }
47 
fn_play_custom_anim(int32 & result,int32 * params)48 mcodeFunctionReturnCodes fn_play_custom_anim(int32 &result, int32 *params) { return (MS->fn_play_custom_anim(result, params)); }
49 
fn_easy_play_generic_anim(int32 & result,int32 * params)50 mcodeFunctionReturnCodes fn_easy_play_generic_anim(int32 &result, int32 *params) { return (MS->fn_easy_play_generic_anim(result, params)); }
51 
fn_easy_play_custom_anim(int32 & result,int32 * params)52 mcodeFunctionReturnCodes fn_easy_play_custom_anim(int32 &result, int32 *params) { return (MS->fn_easy_play_custom_anim(result, params)); }
53 
fn_snap_face_object(int32 & result,int32 * params)54 mcodeFunctionReturnCodes fn_snap_face_object(int32 &result, int32 *params) { return (MS->fn_snap_face_object(result, params)); }
55 
fn_face_coord(int32 & result,int32 * params)56 mcodeFunctionReturnCodes fn_face_coord(int32 &result, int32 *params) { return (MS->fn_face_coord(result, params)); }
57 
fn_face_object(int32 & result,int32 * params)58 mcodeFunctionReturnCodes fn_face_object(int32 &result, int32 *params) { return (MS->fn_face_object(result, params)); }
59 
fn_set_to_first_frame_custom_anim(int32 & result,int32 * params)60 mcodeFunctionReturnCodes fn_set_to_first_frame_custom_anim(int32 &result, int32 *params) { return (MS->fn_set_to_first_frame_custom_anim(result, params)); }
61 
fn_set_to_last_frame_custom_anim(int32 & result,int32 * params)62 mcodeFunctionReturnCodes fn_set_to_last_frame_custom_anim(int32 &result, int32 *params) { return (MS->fn_set_to_last_frame_custom_anim(result, params)); }
63 
fn_new_apply_bullet(int32 & result,int32 * params)64 mcodeFunctionReturnCodes fn_new_apply_bullet(int32 &result, int32 *params) { return (MS->fn_new_apply_bullet(result, params)); }
65 
fn_play_generic_anim(int32 & result,int32 * params)66 mcodeFunctionReturnCodes fn_play_generic_anim(int32 &result, int32 *params) { return (MS->fn_play_generic_anim(result, params)); }
67 
fn_reverse_generic_anim(int32 & result,int32 * params)68 mcodeFunctionReturnCodes fn_reverse_generic_anim(int32 &result, int32 *params) { return (MS->fn_reverse_generic_anim(result, params)); }
69 
fn_apply_anim_y(int32 & result,int32 * params)70 mcodeFunctionReturnCodes fn_apply_anim_y(int32 &result, int32 *params) { return (MS->fn_apply_anim_y(result, params)); }
71 
fn_set_to_first_frame_generic_anim(int32 & result,int32 * params)72 mcodeFunctionReturnCodes fn_set_to_first_frame_generic_anim(int32 &result, int32 *params) { return (MS->fn_set_to_first_frame_generic_anim(result, params)); }
73 
fn_easy_play_generic_anim_with_pan(int32 & result,int32 * params)74 mcodeFunctionReturnCodes fn_easy_play_generic_anim_with_pan(int32 &result, int32 *params) { return (MS->fn_easy_play_generic_anim_with_pan(result, params)); }
75 
fn_fast_face_object(int32 & result,int32 * params)76 mcodeFunctionReturnCodes fn_fast_face_object(int32 &result, int32 *params) { return (MS->fn_fast_face_object(result, params)); }
77 
fn_face_nicos_pan(int32 & result,int32 * params)78 mcodeFunctionReturnCodes fn_face_nicos_pan(int32 &result, int32 *params) { return (MS->fn_face_nicos_pan(result, params)); }
79 
fn_add_y(int32 & result,int32 * params)80 mcodeFunctionReturnCodes fn_add_y(int32 &result, int32 *params) { return (MS->fn_add_y(result, params)); }
81 
fn_reverse_custom_anim(int32 & result,int32 * params)82 mcodeFunctionReturnCodes fn_reverse_custom_anim(int32 &result, int32 *params) { return (MS->fn_reverse_custom_anim(result, params)); }
83 
fn_fast_face_coord(int32 & result,int32 * params)84 mcodeFunctionReturnCodes fn_fast_face_coord(int32 &result, int32 *params) { return (MS->fn_fast_face_coord(result, params)); }
85 
fn_easy_play_custom_anim_with_pan(int32 & result,int32 * params)86 mcodeFunctionReturnCodes fn_easy_play_custom_anim_with_pan(int32 &result, int32 *params) { return (MS->fn_easy_play_custom_anim_with_pan(result, params)); }
87 
fn_prime_custom_anim(int32 & result,int32 * params)88 mcodeFunctionReturnCodes fn_prime_custom_anim(int32 &result, int32 *params) { return (MS->fn_prime_custom_anim(result, params)); }
89 
fn_sync_with_mega(int32 & result,int32 * params)90 mcodeFunctionReturnCodes fn_sync_with_mega(int32 &result, int32 *params) { return (MS->fn_sync_with_mega(result, params)); }
91 
fn_set_feet_to_pan(int32 & result,int32 * params)92 mcodeFunctionReturnCodes fn_set_feet_to_pan(int32 &result, int32 *params) { return (MS->fn_set_feet_to_pan(result, params)); }
93 
fn_hard_load_generic_anim(int32 & result,int32 * params)94 mcodeFunctionReturnCodes fn_hard_load_generic_anim(int32 &result, int32 *params) { return (MS->fn_hard_load_generic_anim(result, params)); }
95 
fn_hard_load_custom_anim(int32 & result,int32 * params)96 mcodeFunctionReturnCodes fn_hard_load_custom_anim(int32 &result, int32 *params) { return (MS->fn_hard_load_custom_anim(result, params)); }
97 
fn_face_camera(int32 & result,int32 * params)98 mcodeFunctionReturnCodes fn_face_camera(int32 &result, int32 *params) { return (MS->fn_face_camera(result, params)); }
99 
fn_face_camera(int32 &,int32 * params)100 mcodeFunctionReturnCodes _game_session::fn_face_camera(int32 &, int32 *params) {
101 	//	params   0   face away from camera
102 	//				1 face toward camera
103 
104 	PXfloat new_pan, diff;
105 	PXcamera currentCamera;
106 
107 	if (!L->looping) {
108 		currentCamera = GetCamera();
109 		new_pan = (PXfloat)currentCamera.pan;
110 
111 		// reverse 180deg?
112 		if (params[1])
113 			new_pan += HALF_TURN;
114 
115 		if (new_pan > HALF_TURN)
116 			new_pan -= FULL_TURN;
117 
118 		else if (new_pan < -HALF_TURN)
119 			new_pan += FULL_TURN;
120 
121 		// get difference between the two
122 		diff = new_pan - L->pan;
123 
124 		if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
125 			// work out which way to turn
126 			if (diff > HALF_TURN)
127 				diff -= FULL_TURN;
128 
129 			else if (diff < -HALF_TURN)
130 				diff += FULL_TURN;
131 
132 			// diff is now the distance to turn by and its sign denotes direction
133 
134 			if (diff < FLOAT_ZERO) {
135 				M->turn_dir = 0; // right
136 			} else {
137 				M->turn_dir = 1; // left
138 			}
139 
140 			M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
141 
142 			M->actual_target_pan = new_pan; // actual target which we may clip to
143 
144 			L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
145 
146 			L->looping = TRUE8;
147 		} else {
148 			// shallow angle so snap and continue as normal
149 			L->pan = new_pan;
150 			return IR_CONT;
151 		}
152 	}
153 
154 	// still got some to go
155 	if (M->target_pan) {
156 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
157 		return (IR_REPEAT);
158 	}
159 
160 	// we're done
161 	L->looping = FALSE8;
162 
163 	// set to stand
164 	L->cur_anim_type = __STAND;
165 	L->anim_pc = 0;
166 
167 	return (IR_CONT);
168 }
169 
170 // fn_set_feet_to_pan()
171 // ensures actual pan is what we are looking at
fn_set_feet_to_pan(int32 &,int32 *)172 mcodeFunctionReturnCodes _game_session::fn_set_feet_to_pan(int32 &, int32 *) {
173 	// we face straight ahead
174 	I->lookBone.boneTarget.vz = (int16)(0);
175 
176 	// and our pan is set to the looking_pan
177 	L->pan = M->looking_pan;
178 
179 	return IR_CONT;
180 }
181 
fn_face_coord(int32 &,int32 * params)182 mcodeFunctionReturnCodes _game_session::fn_face_coord(int32 &, int32 *params) {
183 	//	params   0 target x
184 	//				1 target z
185 
186 	// return    IR_CONT or
187 	//			IR_REPEAT
188 
189 	if (!L->looping) {
190 		// setup
191 
192 		if (Calc_target_pan((PXreal)params[0], (PXreal)params[1], L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
193 			// turn required
194 
195 			L->looping = TRUE8;
196 		} else
197 			return (IR_CONT); // will have possibly made a tiny snap
198 	}
199 
200 	// still got some to go
201 	if (M->target_pan) {
202 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
203 		return (IR_REPEAT);
204 	}
205 
206 	// we're done
207 	L->looping = FALSE8;
208 
209 	// set to stand
210 	L->cur_anim_type = __STAND;
211 	L->anim_pc = 0;
212 
213 	return (IR_CONT);
214 }
215 
fn_face_nicos_pan(int32 &,int32 * params)216 mcodeFunctionReturnCodes _game_session::fn_face_nicos_pan(int32 &, int32 *params) {
217 	//	params   0   target nico
218 	//				1 reserved for future use
219 
220 	// return    IR_CONT or
221 	//			IR_REPEAT
222 
223 	_feature_info *start_pos;
224 	PXfloat new_pan, diff;
225 
226 	const char *nico_name = (const char *)MemoryUtil::resolvePtr(params[0]);
227 
228 	if (!L->looping) {
229 		// setup
230 		start_pos = (_feature_info *)features->Try_fetch_item_by_name(nico_name);
231 		if (!start_pos)
232 			Fatal_error("no NICO marker (fn_face_nico) ob %s, nico %s", object->GetName(), nico_name);
233 
234 		new_pan = start_pos->direction;
235 
236 		// get difference between the two
237 		diff = new_pan - L->pan;
238 
239 		if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
240 			// work out which way to turn
241 			if (diff > HALF_TURN)
242 				diff -= FULL_TURN;
243 			else if (diff < -HALF_TURN)
244 				diff += FULL_TURN;
245 
246 			// diff is now the distance to turn by and its sign denotes direction
247 
248 			if (diff < FLOAT_ZERO) {
249 				M->turn_dir = 0; // right
250 			} else {
251 				M->turn_dir = 1; // left
252 			}
253 
254 			M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
255 
256 			M->actual_target_pan = new_pan; // actual target which we may clip to
257 
258 			L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
259 
260 			L->looping = TRUE8;
261 		} else {
262 			//      shallow angle so snap and continue as normal
263 			L->pan = new_pan;
264 			return IR_CONT;
265 		}
266 	}
267 
268 	// still got some to go
269 	if (M->target_pan) {
270 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
271 		return (IR_REPEAT);
272 	}
273 
274 	// we're done
275 	L->looping = FALSE8;
276 
277 	// set to stand
278 	L->cur_anim_type = __STAND;
279 	L->anim_pc = 0;
280 
281 	return (IR_CONT);
282 }
283 
fn_face_object(int32 &,int32 * params)284 mcodeFunctionReturnCodes _game_session::fn_face_object(int32 &, int32 *params) {
285 	//	params   target object
286 
287 	// return    IR_CONT or
288 	//			IR_REPEAT
289 
290 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
291 
292 	if (!L->looping) {
293 		// setup
294 		_logic *log;
295 
296 		uint32 id = objects->Fetch_item_number_by_name(object_name);
297 
298 		log = Fetch_object_struct(id);
299 
300 		if (log->image_type == PROP) {
301 			if (Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, M->actor_xyz.x, M->actor_xyz.z)) {
302 				// turn required
303 				L->looping = TRUE8;
304 			} else
305 				return (IR_CONT); // will have possibly made a tiny snap
306 
307 		} else {
308 			if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
309 				// turn required
310 				L->looping = TRUE8;
311 			} else
312 				return (IR_CONT); // will have possibly made a tiny snap
313 		}
314 	}
315 
316 	// still got some to go
317 	if (M->target_pan) {
318 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
319 		return (IR_REPEAT);
320 	}
321 
322 	// we're done
323 	L->looping = FALSE8;
324 
325 	// set to stand
326 	L->cur_anim_type = __STAND;
327 	L->anim_pc = 0;
328 
329 	return (IR_CONT);
330 }
331 
fn_fast_face_object(int32 &,int32 * params)332 mcodeFunctionReturnCodes _game_session::fn_fast_face_object(int32 &, int32 *params) {
333 	//	params   0 target object
334 	//				1 speed up
335 
336 	// return    IR_CONT or
337 	//			IR_REPEAT
338 
339 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
340 
341 	if (!L->looping) {
342 		// setup
343 		_logic *log;
344 
345 		uint32 id = objects->Fetch_item_number_by_name(object_name);
346 
347 		log = Fetch_object_struct(id);
348 
349 		if (log->image_type == PROP) {
350 			if (Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, M->actor_xyz.x, M->actor_xyz.z)) {
351 				//          turn required
352 				L->looping = TRUE8;
353 			} else
354 				return (IR_CONT); // will have possibly made a tiny snap
355 		} else {
356 			if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
357 				//          turn required
358 				L->looping = TRUE8;
359 			} else
360 				return (IR_CONT); // will have possibly made a tiny snap
361 		}
362 	}
363 
364 	// still got some to go
365 	if (M->target_pan) {
366 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, params[1]);
367 		return (IR_REPEAT);
368 	}
369 
370 	// we're done
371 	L->looping = FALSE8;
372 
373 	// set to stand
374 	L->cur_anim_type = __STAND;
375 	L->anim_pc = 0;
376 
377 	return (IR_CONT);
378 }
379 
fn_fast_face_coord(int32 &,int32 * params)380 mcodeFunctionReturnCodes _game_session::fn_fast_face_coord(int32 &, int32 *params) {
381 	//	params   0 x
382 	//				1 z
383 	//				2 speed up
384 
385 	// return    IR_CONT or
386 	//			IR_REPEAT
387 
388 	if (!L->looping) {
389 		if (Calc_target_pan((PXreal)params[0], (PXreal)params[1], L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
390 			// turn required
391 			L->looping = TRUE8;
392 		} else
393 			return (IR_CONT); // will have possibly made a tiny snap
394 	}
395 
396 	// still got some to go
397 	if (M->target_pan) {
398 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, params[2]);
399 		return (IR_REPEAT);
400 	}
401 
402 	// we're done
403 	L->looping = FALSE8;
404 
405 	// set to stand
406 	L->cur_anim_type = __STAND;
407 	L->anim_pc = 0;
408 
409 	return (IR_CONT);
410 }
411 
Need_to_turn_to_face_object(uint32 id)412 bool8 _game_session::Need_to_turn_to_face_object(uint32 id) {
413 	// is a turn required to face an object
414 	// used by chi
415 
416 	_logic *log;
417 
418 	log = Fetch_object_struct(id);
419 
420 	if (log->image_type == PROP)
421 		Fatal_error("fast_face_object = target must be mega");
422 
423 	if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
424 		// turn required
425 		return TRUE8;
426 	}
427 
428 	return (FALSE8); // may even have made a tiny snap
429 }
430 
fast_face_object(uint32 id,uint32 speed)431 bool8 _game_session::fast_face_object(uint32 id, uint32 speed) {
432 	// called in engine
433 	// for example from chi logic
434 
435 	if (!L->looping) {
436 		// setup
437 		_logic *log;
438 
439 		log = Fetch_object_struct(id);
440 
441 		if (log->image_type == PROP)
442 			Fatal_error("fast_face_object = target must be mega");
443 
444 		if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
445 			// turn required
446 			L->looping = TRUE8;
447 		} else
448 			return (TRUE8); // will have possibly made a tiny snap
449 	}
450 
451 	// still got some to go
452 	if (M->target_pan) {
453 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, speed);
454 		return (FALSE8);
455 	}
456 
457 	// we're done
458 	L->looping = FALSE8;
459 
460 	// set to stand
461 	L->cur_anim_type = __STAND;
462 	L->anim_pc = 0;
463 
464 	return (TRUE8);
465 }
466 
fast_face_rnd(uint32 speed)467 bool8 _game_session::fast_face_rnd(uint32 speed) {
468 	PXfloat new_pan, diff;
469 
470 	if (!L->looping) {
471 		// pick a random pan
472 		new_pan = (FULL_TURN * (g_icb->getRandomSource()->getRandomNumber(359 - 1))) / 360;
473 
474 		// get difference between the two
475 		diff = new_pan - L->pan;
476 
477 		if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
478 			// work out which way to turn
479 			if (diff > HALF_TURN)
480 				diff -= FULL_TURN;
481 
482 			else if (diff < -HALF_TURN)
483 				diff += FULL_TURN;
484 
485 			// diff is now the distance to turn by and its sign denotes direction
486 			if (diff < FLOAT_ZERO) {
487 				M->turn_dir = 0; // right
488 			} else {
489 				M->turn_dir = 1; // left
490 			}
491 
492 			M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
493 
494 			M->actual_target_pan = new_pan; // actual target which we may clip to
495 
496 			L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
497 
498 			L->looping = TRUE8;
499 
500 		} else
501 			return TRUE8; // random was too tiny to bother
502 	}
503 
504 	// still got some to go
505 	if (M->target_pan) {
506 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, speed);
507 		return (FALSE8);
508 	}
509 
510 	// we're done
511 	L->looping = FALSE8;
512 
513 	// set to stand
514 	L->cur_anim_type = __STAND;
515 	L->anim_pc = 0;
516 
517 	return (TRUE8);
518 }
519 
fn_snap_face_object(int32 &,int32 * params)520 mcodeFunctionReturnCodes _game_session::fn_snap_face_object(int32 &, int32 *params) {
521 	// force pan - no animation
522 
523 	// params        0 target object
524 	// return        IR_CONT
525 
526 	_logic *log;
527 
528 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
529 
530 	Zdebug("fn_snap_face_object [%s]", object_name);
531 
532 	uint32 id = objects->Fetch_item_number_by_name(object_name);
533 
534 	if (id == 0xffffffff)
535 		Fatal_error("fn_snap_face_object cant find target object %s", object_name);
536 
537 	log = Fetch_object_struct(id);
538 
539 	if (log->image_type == PROP) {
540 		if (Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, M->actor_xyz.x, M->actor_xyz.z)) {
541 			L->pan = M->actual_target_pan;
542 			M->actual_target_pan = REAL_ZERO;
543 		}
544 	} else {
545 		if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
546 			L->pan = M->actual_target_pan;
547 			M->actual_target_pan = REAL_ZERO;
548 		}
549 	}
550 
551 	return (IR_CONT);
552 }
553 
speech_face_object(uint32 tar_id)554 bool8 _game_session::speech_face_object(uint32 tar_id) {
555 	// custom system for speech
556 
557 	// return    TRUE8 - more to do
558 	//			FALSE - done
559 	bool8 res;
560 
561 	if (!L->looping) {
562 		// setup
563 		_logic *log;
564 
565 		log = Fetch_object_struct(tar_id);
566 
567 		if (log->image_type == VOXEL)
568 			res = Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z);
569 
570 		else
571 			res = Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z);
572 
573 		if (res) {
574 			// turn required
575 			L->looping = TRUE8;
576 		} else { // will have possibly made a tiny snap
577 			// set to stand
578 			L->cur_anim_type = __STAND;
579 			L->anim_pc = 0;
580 
581 			return (FALSE8); // done!
582 		}
583 	}
584 
585 	// still got some to go
586 	if (M->target_pan) {
587 		Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
588 		return (TRUE8); // more to do
589 	}
590 
591 	// we're done
592 	L->looping = FALSE8;
593 
594 	// set to stand
595 	L->cur_anim_type = __STAND;
596 	L->anim_pc = 0;
597 
598 	Zdebug(" finished");
599 
600 	return (FALSE8); // done
601 }
602 
fn_reverse_generic_anim(int32 &,int32 * params)603 mcodeFunctionReturnCodes _game_session::fn_reverse_generic_anim(int32 &, int32 *params) {
604 	//	params   0 ascii name of anim
605 
606 	// return    IR_CONT or
607 	//			IR_REPEAT
608 
609 	bool8 ret;
610 
611 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
612 
613 	if (!L->looping) {
614 		// setup
615 		M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
616 		L->looping = 100;
617 		ANIM_CHECK(M->next_anim_type);
618 		L->list[0] = HashString(anim_name);
619 	}
620 
621 	if (L->looping == 100) {
622 		// setup
623 		// psx async loading check - is file in memory
624 		if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
625 			return IR_REPEAT;
626 
627 		if ((Object_visible_to_camera(cur_id)) &&
628 		    (!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
629 			return IR_REPEAT;
630 
631 		L->cur_anim_type = M->next_anim_type; // anim now in memory
632 
633 		L->looping = TRUE8;
634 
635 		PXanim *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); //
636 
637 		// set last frame
638 		L->anim_pc = anim->frame_qty - 2;
639 
640 		return (IR_REPEAT);
641 	}
642 
643 	// ok, we are simply animating through the frames
644 	// last frame is currently displayed?
645 	if (!L->anim_pc) {
646 		L->looping = FALSE8;
647 		return (IR_CONT);
648 	}
649 
650 	// shift character and frame forward by the amount appropriate
651 	ret = MS->Reverse_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
652 	if (!ret) { // could not move forward?
653 		L->looping = FALSE8;
654 		return (IR_CONT);
655 	}
656 
657 	return (IR_REPEAT);
658 }
659 
fn_play_generic_anim(int32 &,int32 * params)660 mcodeFunctionReturnCodes _game_session::fn_play_generic_anim(int32 &, int32 *params) {
661 	//	params   0 ascii name of anim
662 
663 	// return    IR_CONT or
664 	//			IR_REPEAT
665 
666 	bool8 ret;
667 
668 	const char *anim_name = NULL;
669 	if (params && params[0]) {
670 		anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
671 	}
672 
673 	if (!L->looping) {
674 		// setup
675 		M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
676 		L->looping = 100;
677 		ANIM_CHECK(M->next_anim_type);
678 		L->list[0] = HashString(anim_name);
679 	}
680 
681 	if (L->looping == 100) {
682 		// psx async loading check - is file in memory
683 		if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
684 			return IR_REPEAT;
685 
686 		if ((Object_visible_to_camera(cur_id)) &&
687 		    (!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
688 			return IR_REPEAT;
689 
690 		// anim found and started ok
691 		L->looping = TRUE8;
692 
693 		L->cur_anim_type = M->next_anim_type; // anim now in memory
694 
695 		// get animation
696 		ANIM_CHECK(L->cur_anim_type);
697 
698 		PXanim *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); //
699 
700 		// advance the frame
701 		L->anim_pc = anim->frame_qty - 2;
702 		Advance_frame_and_motion(L->cur_anim_type, 0, 1);
703 		L->anim_pc = 0;
704 
705 		return (IR_REPEAT);
706 	}
707 
708 	// ok, we are simply animating through the frames
709 
710 	PXanim *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); //
711 
712 	// last frame is currently displayed?
713 	if ((int32)(L->anim_pc + M->anim_speed) >= (anim->frame_qty - 1)) {
714 		L->looping = FALSE8;
715 		return (IR_CONT);
716 	}
717 
718 	// shift character and frame forward by the amount appropriate
719 	ret = Advance_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
720 
721 	if (!ret) { // could not move forward?
722 		L->looping = FALSE8;
723 		return (IR_CONT);
724 	}
725 
726 	// more to do - come back again next cycle
727 	return (IR_REPEAT);
728 }
729 
fn_easy_play_generic_anim(int32 &,int32 * params)730 mcodeFunctionReturnCodes _game_session::fn_easy_play_generic_anim(int32 &, int32 *params) {
731 	// barriers are ignored!
732 
733 	//	params   0 ascii name of anim
734 
735 	// return    IR_CONT or
736 	//			IR_REPEAT
737 
738 	const char *anim_name = NULL;
739 	if (params && params[0]) {
740 		anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
741 	}
742 
743 	if (!L->looping) {
744 		// setup
745 		M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
746 		L->looping = 100;
747 		ANIM_CHECK(M->next_anim_type);
748 		L->list[0] = HashString(anim_name);
749 	}
750 
751 	if (L->looping == 100) {
752 		// setup
753 		// psx async loading check - is file in memory
754 		if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
755 			return IR_REPEAT;
756 
757 		if ((Object_visible_to_camera(cur_id)) &&
758 		    (!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
759 			return IR_REPEAT;
760 
761 		L->cur_anim_type = M->next_anim_type; // anim now in memory
762 		L->looping = TRUE8;
763 
764 		// advance the frame
765 		// get animation
766 		ANIM_CHECK(L->cur_anim_type);
767 
768 		PXanim *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); //
769 
770 		L->anim_pc = anim->frame_qty - 2;
771 		Easy_frame_and_motion(L->cur_anim_type, 0, 1);
772 		L->anim_pc = 0;
773 
774 		return (IR_REPEAT);
775 	}
776 
777 	// ok, we are simply animating through the frames
778 
779 	// get animation
780 	ANIM_CHECK(L->cur_anim_type);
781 
782 	PXanim *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);
783 
784 	// last frame is currently displayed?
785 	if ((int32)(L->anim_pc + M->anim_speed) >= (anim->frame_qty - 1)) {
786 		L->looping = FALSE8;
787 		return (IR_CONT);
788 	}
789 
790 	// shift character and frame forward by the amount appropriate
791 	MS->Easy_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
792 
793 	// more to do - come back again next cycle
794 	return (IR_REPEAT);
795 }
796 
fn_easy_play_generic_anim_with_pan(int32 &,int32 * params)797 mcodeFunctionReturnCodes _game_session::fn_easy_play_generic_anim_with_pan(int32 &, int32 *params) {
798 	// barriers are ignored!
799 
800 	//	params   0 ascii name of anim
801 
802 	// return    IR_CONT or
803 	//			IR_REPEAT
804 
805 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
806 
807 	if (!L->looping) {
808 		// setup
809 		M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
810 		L->looping = 100;
811 		ANIM_CHECK(M->next_anim_type);
812 		L->list[0] = HashString(anim_name);
813 	}
814 
815 	if (L->looping == 100) {
816 		// setup
817 
818 		// psx async loading check - is file in memory
819 		if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
820 			return IR_REPEAT;
821 
822 		if ((Object_visible_to_camera(cur_id)) &&
823 		    (!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
824 			return IR_REPEAT;
825 
826 		L->cur_anim_type = M->next_anim_type; // anim now in memory
827 
828 		L->looping = TRUE8;
829 
830 		// advance the frame
831 		// get animation
832 		ANIM_CHECK(L->cur_anim_type);
833 
834 		PXanim *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); //
835 
836 		L->anim_pc = anim->frame_qty - 2;
837 		Easy_frame_motion_and_pan(L->cur_anim_type, 0);
838 		L->anim_pc = 0;
839 
840 		return (IR_REPEAT);
841 	}
842 
843 	// ok, we are simply animating through the frames
844 
845 	// get animation
846 	ANIM_CHECK(L->cur_anim_type);
847 
848 	PXanim *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); //
849 
850 	// last frame is currently displayed?
851 	if ((int32)(L->anim_pc + 1) == (anim->frame_qty - 1)) {
852 		L->looping = FALSE8;
853 		return (IR_CONT);
854 	}
855 
856 	// shift character and frame forward by the amount appropriate
857 	MS->Easy_frame_motion_and_pan(L->cur_anim_type, 0);
858 
859 	// more to do - come back again next cycle
860 	return (IR_REPEAT);
861 }
862 
fn_easy_play_custom_anim_with_pan(int32 &,int32 * params)863 mcodeFunctionReturnCodes _game_session::fn_easy_play_custom_anim_with_pan(int32 &, int32 *params) {
864 	// barriers are ignored!
865 	// pan is updated
866 
867 	//	params   0 ascii name of anim
868 
869 	// return    IR_CONT or
870 	//			IR_REPEAT
871 
872 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
873 
874 	Zdebug("fn_easy_play_custom_anim_with_pan %s %s", object->GetName(), anim_name);
875 
876 	if (!L->looping) {
877 		// set anim up
878 		I->Init_custom_animation(anim_name);
879 		Reset_cur_megas_custom_type();
880 		L->looping = 100; // have to distinguish between first time in and first cycle with anim in memory
881 		ANIM_CHECK(__NON_GENERIC);
882 		L->list[0] = HashString(anim_name);
883 	}
884 
885 	// anim is in memory so do first frame then pass on to normal logic path
886 	if (L->looping == 100) {
887 		// psx async loading check - is file in memory
888 		if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
889 			return IR_REPEAT;
890 
891 		if ((Object_visible_to_camera(cur_id)) &&
892 		    (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
893 			return IR_REPEAT;
894 
895 		I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
896 		L->cur_anim_type = __PROMOTED_NON_GENERIC;
897 
898 		L->anim_pc = 0;
899 		L->looping = TRUE8;
900 
901 		return (IR_REPEAT);
902 	}
903 
904 	// ok, we are simply animating through the frames
905 
906 	PXanim *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); //
907 
908 	// last frame is currently displayed?
909 	if ((int32)(L->anim_pc + 1) == (anim->frame_qty - 1)) {
910 		L->looping = FALSE8;
911 
912 		return (IR_CONT);
913 	}
914 
915 	// shift character and frame forward by the amount appropriate
916 	MS->Easy_frame_motion_and_pan(L->cur_anim_type, 0);
917 
918 	// more to do - come back again next cycle
919 	return (IR_REPEAT);
920 }
921 
fn_set_to_last_frame_generic_anim(int32 &,int32 * params)922 mcodeFunctionReturnCodes _game_session::fn_set_to_last_frame_generic_anim(int32 &, int32 *params) {
923 	//	params   0 ascii name of anim
924 
925 	// return    IR_CONT or IR_STOP if error
926 
927 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
928 
929 	if (!L->looping) {
930 		M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
931 		L->looping = 100;
932 		ANIM_CHECK(M->next_anim_type);
933 		L->list[0] = HashString(anim_name);
934 	}
935 
936 	// psx async loading check - is file in memory
937 	if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
938 		return IR_REPEAT;
939 
940 	if ((Object_visible_to_camera(cur_id)) &&
941 	    (!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
942 		return IR_REPEAT;
943 
944 	L->cur_anim_type = M->next_anim_type; // anim now in memory
945 
946 	// ok, set to last frame
947 	// get animation
948 	ANIM_CHECK(L->cur_anim_type);
949 
950 	PXanim *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); //
951 
952 	// set to last frame
953 	L->anim_pc = anim->frame_qty - 2; // if 10 frames then 10+1 (looper) == 11 meaning 9 is last displayable frame number
954 	L->looping = 0;
955 
956 	return IR_CONT;
957 }
958 
fn_set_to_first_frame_generic_anim(int32 &,int32 * params)959 mcodeFunctionReturnCodes _game_session::fn_set_to_first_frame_generic_anim(int32 &, int32 *params) {
960 	//	params   0 ascii name of anim
961 
962 	// return    IR_CONT or IR_STOP if error
963 
964 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
965 
966 	if (!L->looping) {
967 		M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
968 		L->looping = 100;
969 		ANIM_CHECK(M->next_anim_type);
970 	}
971 
972 	// psx async loading check - is file in memory
973 	if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
974 		return IR_REPEAT;
975 
976 	if ((Object_visible_to_camera(cur_id)) &&
977 	    (!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
978 		return IR_REPEAT;
979 
980 	L->cur_anim_type = M->next_anim_type; // anim now in memory
981 
982 	// set to last frame
983 	L->anim_pc = 0; // first
984 	L->looping = 0;
985 
986 	return (IR_CONT);
987 }
988 
fn_set_to_first_frame_custom_anim(int32 &,int32 * params)989 mcodeFunctionReturnCodes _game_session::fn_set_to_first_frame_custom_anim(int32 &, int32 *params) {
990 	// set to first frame of a custom animation
991 	// must use fn-set-custom before this
992 
993 	// params        0   name of anim
994 
995 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
996 
997 	if (!L->looping) { // once
998 		I->Init_custom_animation(anim_name);
999 		L->looping = 1;
1000 		ANIM_CHECK(__NON_GENERIC);
1001 	}
1002 
1003 	// psx async loading check - is file in memory
1004 	if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
1005 		return IR_REPEAT;
1006 
1007 	if ((Object_visible_to_camera(cur_id)) && (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
1008 		return IR_REPEAT;
1009 
1010 	I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
1011 	L->cur_anim_type = __PROMOTED_NON_GENERIC;
1012 
1013 	L->anim_pc = 0;
1014 	L->looping = 0;
1015 
1016 	return (IR_CONT);
1017 }
1018 
fn_set_to_last_frame_custom_anim(int32 &,int32 * params)1019 mcodeFunctionReturnCodes _game_session::fn_set_to_last_frame_custom_anim(int32 &, int32 *params) {
1020 	// set to last frame of a custom animation
1021 	// must use fn-set-custom before this
1022 
1023 	// params        0   name of anim
1024 
1025 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1026 
1027 	if (!L->looping) { // once
1028 		I->Init_custom_animation(anim_name);
1029 		L->looping = 1;
1030 		ANIM_CHECK(__NON_GENERIC);
1031 	}
1032 
1033 	// psx async loading check - is file in memory
1034 	if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
1035 		return IR_REPEAT;
1036 
1037 	if ((Object_visible_to_camera(cur_id)) && (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
1038 		return IR_REPEAT;
1039 
1040 	I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
1041 	L->cur_anim_type = __PROMOTED_NON_GENERIC;
1042 
1043 	PXanim *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);
1044 
1045 	// set to last frame
1046 	L->anim_pc = anim->frame_qty - 2; // if 10 frames then 10+1 (looper) == 11 meaning 9 is last displayable frame number
1047 
1048 	L->looping = 0;
1049 
1050 	return IR_CONT;
1051 }
1052 
fn_hard_load_generic_anim(int32 &,int32 * params)1053 mcodeFunctionReturnCodes _game_session::fn_hard_load_generic_anim(int32 &, int32 *params) {
1054 	// get generic anim into memory NOW
1055 
1056 	// params    0   name of anim
1057 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1058 
1059 	__mega_set_names load;
1060 
1061 	load = Fetch_generic_anim_from_ascii(anim_name);
1062 	ANIM_CHECK(load);
1063 	rs_anims->Res_open(I->get_info_name(load), I->info_name_hash[load], I->base_path, I->base_path_hash);
1064 
1065 	if (Object_visible_to_camera(cur_id))
1066 		rs_anims->Res_open(I->get_anim_name(load), I->anim_name_hash[load], I->base_path, I->base_path_hash);
1067 
1068 	return IR_CONT;
1069 }
1070 
fn_hard_load_custom_anim(int32 &,int32 * params)1071 mcodeFunctionReturnCodes _game_session::fn_hard_load_custom_anim(int32 &, int32 *params) {
1072 	// get custom anim into memory NOW
1073 
1074 	// params    0   name of anim
1075 
1076 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1077 
1078 	I->Init_custom_animation(anim_name);
1079 	Reset_cur_megas_custom_type();
1080 
1081 	ANIM_CHECK(__NON_GENERIC);
1082 	rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash); //
1083 
1084 	if (Object_visible_to_camera(cur_id))
1085 		rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash);
1086 
1087 	return IR_CONT;
1088 }
1089 
fn_prime_custom_anim(int32 &,int32 * params)1090 mcodeFunctionReturnCodes _game_session::fn_prime_custom_anim(int32 &, int32 *params) {
1091 	// get custom anim into memory
1092 
1093 	// params    0   name of anim
1094 
1095 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1096 
1097 	if (!L->looping) {
1098 		I->Init_custom_animation(anim_name);
1099 		Reset_cur_megas_custom_type();
1100 
1101 		L->looping = 100;
1102 
1103 		ANIM_CHECK(__NON_GENERIC);
1104 	}
1105 
1106 	// anim is in memory so do first frame then pass on to normal logic path
1107 	if (L->looping == 100) {
1108 		// psx async loading check - is file in memory
1109 		if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
1110 			return IR_REPEAT;
1111 
1112 		if ((Object_visible_to_camera(cur_id)) &&
1113 		    (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
1114 			return IR_REPEAT;
1115 	}
1116 
1117 	// anim is now in memory
1118 	L->looping = 0;
1119 	return IR_CONT;
1120 }
1121 
fn_play_custom_anim(int32 & result,int32 * params)1122 mcodeFunctionReturnCodes _game_session::fn_play_custom_anim(int32 &result, int32 *params) {
1123 	// a mega character plays an anim
1124 	// the anim IS NOT part of the generic set
1125 	// barriers are checked
1126 
1127 	// params    0   name of anim
1128 
1129 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1130 
1131 	if (!L->looping) {
1132 		I->Init_custom_animation(anim_name);
1133 		Reset_cur_megas_custom_type();
1134 		L->looping = 100;
1135 		ANIM_CHECK(__NON_GENERIC);
1136 		L->list[0] = HashString(anim_name);
1137 	}
1138 
1139 	// anim is in memory so do first frame then pass on to normal logic path
1140 	if (L->looping == 100) {
1141 		// psx async loading check - is file in memory
1142 		if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
1143 			return IR_REPEAT;
1144 
1145 		if ((Object_visible_to_camera(cur_id)) &&
1146 		    (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
1147 			return IR_REPEAT;
1148 
1149 		I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
1150 		L->cur_anim_type = __PROMOTED_NON_GENERIC;
1151 
1152 		L->anim_pc = 0;
1153 		L->looping = TRUE8;
1154 
1155 		return IR_REPEAT;
1156 	}
1157 
1158 	return (fn_play_generic_anim(result, 0));
1159 }
1160 
fn_reverse_custom_anim(int32 &,int32 * params)1161 mcodeFunctionReturnCodes _game_session::fn_reverse_custom_anim(int32 &, int32 *params) {
1162 	// a mega character plays an anim backward
1163 	// the anim IS NOT part of the generic set
1164 	// barriers are checked
1165 
1166 	// params    0   name of anim
1167 
1168 	bool8 ret;
1169 
1170 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1171 
1172 	if (!L->looping) {
1173 		I->Init_custom_animation(anim_name);
1174 		Reset_cur_megas_custom_type();
1175 		L->looping = 100;
1176 		ANIM_CHECK(__NON_GENERIC);
1177 		L->list[0] = HashString(anim_name);
1178 	}
1179 
1180 	// anim is in memory so do first frame then pass on to normal logic path
1181 	if (L->looping == 100) {
1182 		// psx async loading check - is file in memory
1183 		if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
1184 			return IR_REPEAT;
1185 
1186 		if ((Object_visible_to_camera(cur_id)) &&
1187 		    (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
1188 			return IR_REPEAT;
1189 
1190 		I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
1191 		L->cur_anim_type = __PROMOTED_NON_GENERIC;
1192 
1193 		PXanim *anim = (PXanim *)rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash); //
1194 		L->anim_pc = anim->frame_qty - 2;
1195 		L->looping = TRUE8;
1196 
1197 		return IR_REPEAT;
1198 	}
1199 
1200 	// last frame is currently displayed?
1201 	if (!L->anim_pc) {
1202 		L->looping = FALSE8;
1203 		return IR_CONT;
1204 	}
1205 
1206 	// shift character and frame forward by the amount appropriate
1207 	ret = MS->Reverse_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
1208 	if (!ret) { // could not move forward?
1209 		L->looping = FALSE8;
1210 		return IR_CONT;
1211 	}
1212 
1213 	// more to do - come back again next cycle
1214 	return IR_REPEAT;
1215 }
1216 
fn_easy_play_custom_anim(int32 & result,int32 * params)1217 mcodeFunctionReturnCodes _game_session::fn_easy_play_custom_anim(int32 &result, int32 *params) {
1218 	// a mega character plays an anim
1219 	// the anim IS NOT part of the generic set
1220 	// barriers are ignored
1221 
1222 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1223 
1224 	if (!L->looping) {
1225 		I->Init_custom_animation(anim_name);
1226 		Reset_cur_megas_custom_type();
1227 		L->looping = 100; // have to distinguish between first time in and first cycle with anim in memory
1228 		ANIM_CHECK(__NON_GENERIC);
1229 		L->list[0] = HashString(anim_name);
1230 	}
1231 
1232 	// anim is in memory so do first frame then pass on to normal logic path
1233 	if (L->looping == 100) {
1234 		// psx async loading check - is file in memory
1235 		if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
1236 			return IR_REPEAT;
1237 
1238 		if ((Object_visible_to_camera(cur_id)) &&
1239 		    (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
1240 			return IR_REPEAT;
1241 
1242 		I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
1243 		L->cur_anim_type = __PROMOTED_NON_GENERIC;
1244 
1245 		L->anim_pc = 0;
1246 		L->looping = TRUE8;
1247 		return (IR_REPEAT);
1248 	}
1249 
1250 	return (fn_easy_play_generic_anim(result, 0));
1251 }
1252 
fn_apply_bullet(int32 &,int32 *)1253 mcodeFunctionReturnCodes _game_session::fn_apply_bullet(int32 &, int32 *) {
1254 	// a bullet is to hit an object
1255 	// we simply call its gun_shot socket
1256 	// this system circumvents the logic context switch
1257 	// this should be used when guards shoot the player... and when chi shoots guards
1258 
1259 	// params        0 name of target
1260 
1261 	M->SetDynamicLight(1, 255, 255, 255, 0, 150, 100, 200); // 2 metres
1262 
1263 	// Hey we are shooting someone (muzzle flash on / cartridge case on (we my want to split this!)
1264 	M->is_shooting = TRUE8;
1265 
1266 	return (IR_CONT);
1267 }
1268 
fn_sync_with_mega(int32 &,int32 * params)1269 mcodeFunctionReturnCodes _game_session::fn_sync_with_mega(int32 &, int32 *params) {
1270 	//	params   0   name of target mega
1271 	//				1 nowt
1272 
1273 	const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1274 
1275 	if (!L->looping) {
1276 		L->list[0] = objects->Fetch_item_number_by_name(mega_name);
1277 		L->list[1] = 42;    // we are here
1278 		L->looping = TRUE8; // dont do this again
1279 	}
1280 
1281 	if (logic_structs[L->list[0]]->list[1] == 42) {
1282 		// we are going first
1283 		L->list[1] = 43;
1284 		L->looping = 0;
1285 		return IR_CONT; // byeee
1286 	} else if (logic_structs[L->list[0]]->list[1] == 43) {
1287 		// its gone first
1288 		logic_structs[L->list[0]]->list[1] = 0; // clean it up
1289 		L->list[1] = 0;                         // clean us up
1290 		L->looping = 0;
1291 		return IR_CONT;
1292 	}
1293 
1294 	return IR_REPEAT;
1295 }
1296 
fn_new_apply_bullet(int32 &,int32 * params)1297 mcodeFunctionReturnCodes _game_session::fn_new_apply_bullet(int32 &, int32 *params) {
1298 	// a bullet is to hit an object with a percentage chance of hitting
1299 	// on a hit simply call its gun_shot socket
1300 	// this system circumvents the logic context switch
1301 	// this should be used when guards shoot the player... and when chi shoots guards
1302 
1303 	// params        0 name of target
1304 	//				1 percentage chance
1305 
1306 	uint32 tid;
1307 	int32 retval;
1308 	int32 rnd;
1309 	PXreal sub1, sub2, len;
1310 	bool8 crouched = FALSE8;
1311 
1312 	const char *target_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1313 
1314 	// gun sound
1315 	if (logic_structs[cur_id]->sfxVars[GUNSHOT_SFX_VAR] != 0)
1316 		RegisterSound(cur_id, NULL, logic_structs[cur_id]->sfxVars[GUNSHOT_SFX_VAR], gunDesc, (int8)127); // have to use full version so we can give hash instead of string
1317 	else
1318 		RegisterSound(cur_id, defaultGunSfx, gunDesc); // use small version as we have string not hash
1319 
1320 	// shot types
1321 	// 0 - nothing on screen
1322 	// 1 - normal light/muzzleflash/cartridge case
1323 	// default is 1
1324 	int32 shotType = object->GetIntegerValueOrDefault("gun_effects", 1);
1325 
1326 	// if mega then do dynamic light (only if shotType isnt 0
1327 	if ((logic_structs[cur_id]->image_type == VOXEL) && (shotType == 1)) {
1328 		// dynamic light
1329 		M->SetDynamicLight(1, 255, 255, 255, 0, 150, 100, 200); // 2 metres
1330 		// Hey we are shooting someone (muzzle flash on / cartridge case on (we my want to split this!)
1331 		M->is_shooting = TRUE8;
1332 	}
1333 
1334 	// get id
1335 	tid = objects->Fetch_item_number_by_name(target_name);
1336 
1337 	// how near
1338 	if (L->image_type == PROP) { // we are prop
1339 		sub1 = (PXreal)L->prop_xyz.x - logic_structs[tid]->mega->actor_xyz.x;
1340 		sub2 = (PXreal)L->prop_xyz.z - logic_structs[tid]->mega->actor_xyz.z;
1341 	} else {
1342 		crouched = M->Is_crouched();
1343 		sub1 = (PXreal)M->actor_xyz.x - logic_structs[tid]->mega->actor_xyz.x;
1344 		sub2 = (PXreal)M->actor_xyz.z - logic_structs[tid]->mega->actor_xyz.z;
1345 	}
1346 
1347 	// dist
1348 	len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
1349 
1350 	// hit chance
1351 	rnd = g_icb->getRandomSource()->getRandomNumber(100 - 1);
1352 
1353 	// we didn't miss
1354 	int32 missed = 0;
1355 
1356 	// user hit chance of 0 means always miss
1357 	if (params[1]) {
1358 		// age chance true, or within distance, or crouching
1359 		if ((rnd < params[1]) || (len < (PXreal)(ALWAYS_HIT_DIST * ALWAYS_HIT_DIST)) || (crouched)) { // hit
1360 
1361 			// if prop is firing then target must be on A floor
1362 			// ELSE
1363 			// if mega firing then both y coords must be the same
1364 			if (((L->image_type == PROP) && (floor_def->On_a_floor(logic_structs[tid]->mega))) ||
1365 			    ((L->image_type == VOXEL) && (M->actor_xyz.y == logic_structs[tid]->mega->actor_xyz.y))) {
1366 
1367 				// kick kinematic for the player if we are shooting the player
1368 				if (tid == player.Fetch_player_id()) {
1369 					MS->player.being_shot = 3;            // cant shoot for 3 cycles (engine anim over three frames)
1370 					MS->player.shot_by_id = (int8)cur_id; // shot by us...!
1371 
1372 					c_game_object *ob = (c_game_object *)objects->Fetch_item_by_number(player.Fetch_player_id());
1373 					int32 ret = ob->GetVariable("hits");
1374 					uint32 hits = ob->GetIntegerVariable(ret);
1375 
1376 					PXreal subp1, subp2;
1377 					if (L->image_type == PROP) { // we are prop
1378 						subp1 = logic_structs[tid]->mega->actor_xyz.x - L->prop_xyz.x;
1379 						subp2 = logic_structs[tid]->mega->actor_xyz.z - L->prop_xyz.z;
1380 					} else {
1381 						subp1 = logic_structs[tid]->mega->actor_xyz.x - M->actor_xyz.x;
1382 						subp2 = logic_structs[tid]->mega->actor_xyz.z - M->actor_xyz.z;
1383 					}
1384 					PXreal dist = ((subp1 * subp1) + (subp2 * subp2));
1385 
1386 					if (dist < PXreal(200 * 200)) {
1387 						if (hits >= 4)
1388 							hits -= 4;
1389 						else
1390 							hits = 0;
1391 					} else if (dist < PXreal(500 * 500)) {
1392 						if (hits >= 2)
1393 							hits -= 2;
1394 						else
1395 							hits = 0;
1396 					} else if (hits)
1397 						hits--;
1398 
1399 					ob->SetIntegerVariable(ret, hits);
1400 				}
1401 
1402 				MS->Call_socket(tid, "gun_shot", &retval); // the hit takes
1403 
1404 				// cancel any speech
1405 				Exit_speech(tid);
1406 			}
1407 		} else {
1408 			// ricochet sound
1409 			missed = 1;
1410 		}
1411 	} else { // missed
1412 		missed = 1;
1413 	}
1414 
1415 	if (missed) {
1416 		// gun sound
1417 		if (logic_structs[cur_id]->sfxVars[RICOCHET_SFX_VAR] != 0)
1418 			RegisterSound(cur_id, NULL, logic_structs[cur_id]->sfxVars[RICOCHET_SFX_VAR], ricochetDesc,
1419 			              (int8)127); // have to use full version so we can give hash instead of string
1420 		else
1421 			RegisterSound(cur_id, defaultRicochetSfx, ricochetDesc); // use small version as we have string not hash
1422 	}
1423 
1424 	// if a guard, adjust the bullet/clip figures
1425 	if (logic_structs[cur_id]->image_type == VOXEL)
1426 		if (M->is_evil) {
1427 			// take chi and player out of the equation
1428 
1429 			int32 ret = object->GetVariable("cur_bullets");
1430 			if (ret != -1) {
1431 				int32 result = object->GetIntegerVariable(ret);
1432 
1433 				if (!result) { // no bullets
1434 					int32 clipret = object->GetVariable("number_of_clips");
1435 					int32 no_clips = object->GetIntegerVariable(clipret);
1436 					if (no_clips == -1)
1437 						Fatal_error("object has no 'number_of_clips' variable");
1438 
1439 					if (no_clips) {     // has clips left
1440 						no_clips--; // 1 less
1441 						object->SetIntegerVariable(clipret, no_clips);
1442 
1443 						int32 bull_per_clip = MS->player.GetBulletsPerClip();
1444 
1445 						object->SetIntegerVariable(ret, bull_per_clip); // reload the gun
1446 					}
1447 				} else { // has bullets, fire one
1448 					result--;
1449 					object->SetIntegerVariable(ret, result); // reload
1450 				}
1451 			}
1452 		}
1453 
1454 	return (IR_CONT);
1455 }
1456 
fn_apply_anim_y(int32 &,int32 * params)1457 mcodeFunctionReturnCodes _game_session::fn_apply_anim_y(int32 &, int32 *params) {
1458 	// add the y offset from frame 0 to end frame
1459 	// used for climbing ladders and stairs and so on where the height gets moved
1460 
1461 	// params        0 generic anim name
1462 
1463 	uint32 k;
1464 	PXreal y_next;
1465 
1466 	const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
1467 
1468 	// search for the named generic anim - cant use __ANIM_NAME from script unfortunately
1469 	for (k = 0; k < __TOTAL_ANIMS; k++) {
1470 		// we must search the table
1471 		if (!strcmp(anim_name, master_anim_name_table[k].name)) {
1472 			// found!
1473 			ANIM_CHECK(k);
1474 
1475 			PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(k), I->info_name_hash[k], I->base_path, I->base_path_hash); //
1476 			PXreal x, z;
1477 			PXreal yend;
1478 			PXreal ystart;
1479 
1480 			PXFrameEnOfAnim(pAnim->frame_qty - 1, pAnim)->markers[ORG_POS].GetXYZ(&x, &yend, &z);
1481 
1482 			PXFrameEnOfAnim(0, pAnim)->markers[ORG_POS].GetXYZ(&x, &ystart, &z);
1483 
1484 			y_next = yend - ystart;
1485 
1486 			Tdebug("y_apply.txt", "%s offset - %3.1f", (const char *)I->get_info_name(k), y_next);
1487 
1488 			M->actor_xyz.y += y_next;
1489 			return IR_CONT;
1490 		}
1491 	}
1492 
1493 	Fatal_error("fn_apply_anim_y [%s] cant find generic anim [%s]", object->GetName(), anim_name);
1494 
1495 	return IR_CONT;
1496 }
1497 
fn_add_y(int32 &,int32 * params)1498 mcodeFunctionReturnCodes _game_session::fn_add_y(int32 &, int32 *params) {
1499 	// add y value to a mega
1500 
1501 	// params        0   value
1502 
1503 	if (L->image_type == PROP)
1504 		Fatal_error("fn_add_y cant be used on a prop - %s", object->GetName());
1505 
1506 	M->actor_xyz.y += params[0];
1507 
1508 	Tdebug("fn_add_y.txt", "%s +%d to %3.1f", object->GetName(), params[0], M->actor_xyz.y);
1509 
1510 	return IR_CONT;
1511 }
1512 
1513 } // End of namespace ICB
1514