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_common.h"
30 #include "engines/icb/common/px_linkeddatafile.h"
31 #include "engines/icb/common/ptr_util.h"
32 #include "engines/icb/mission.h"
33 #include "engines/icb/session.h"
34 #include "engines/icb/object_structs.h"
35 #include "engines/icb/debug.h"
36 #include "engines/icb/player.h"
37 #include "engines/icb/direct_input.h"
38 #include "engines/icb/barriers.h"
39 #include "engines/icb/common/px_route_barriers.h"
40 #include "engines/icb/global_objects.h"
41 #include "engines/icb/animation_mega_set.h"
42 #include "engines/icb/mission.h"
43 #include "engines/icb/common/px_scriptengine.h"
44 #include "engines/icb/session.h"
45 #include "engines/icb/global_switches.h"
46 #include "engines/icb/res_man.h"
47 #include "engines/icb/floors.h"
48 
49 namespace ICB {
50 
fn_generic_prop_interact(int32 & result,int32 * params)51 mcodeFunctionReturnCodes fn_generic_prop_interact(int32 &result, int32 *params) { return (MS->fn_generic_prop_interact(result, params)); }
52 
fn_custom_prop_interact(int32 & result,int32 * params)53 mcodeFunctionReturnCodes fn_custom_prop_interact(int32 &result, int32 *params) { return (MS->fn_custom_prop_interact(result, params)); }
54 
fn_is_there_interact_object(int32 & result,int32 * params)55 mcodeFunctionReturnCodes fn_is_there_interact_object(int32 &result, int32 *params) { return (MS->fn_is_there_interact_object(result, params)); }
56 
fn_get_interact_object_id(int32 & result,int32 * params)57 mcodeFunctionReturnCodes fn_get_interact_object_id(int32 &result, int32 *params) { return (MS->fn_get_interact_object_id(result, params)); }
58 
fn_is_object_interact_object(int32 & result,int32 * params)59 mcodeFunctionReturnCodes fn_is_object_interact_object(int32 &result, int32 *params) { return (MS->fn_is_object_interact_object(result, params)); }
60 
fn_register_for_auto_interaction(int32 & result,int32 * params)61 mcodeFunctionReturnCodes fn_register_for_auto_interaction(int32 &result, int32 *params) { return (MS->fn_register_for_auto_interaction(result, params)); }
62 
fn_route_to_custom_prop_interact(int32 & result,int32 * params)63 mcodeFunctionReturnCodes fn_route_to_custom_prop_interact(int32 &result, int32 *params) { return (MS->fn_route_to_custom_prop_interact(result, params)); }
64 
fn_route_to_generic_prop_interact(int32 & result,int32 * params)65 mcodeFunctionReturnCodes fn_route_to_generic_prop_interact(int32 &result, int32 *params) { return (MS->fn_route_to_generic_prop_interact(result, params)); }
66 
fn_sony_door_interact(int32 & result,int32 * params)67 mcodeFunctionReturnCodes fn_sony_door_interact(int32 &result, int32 *params) { return (MS->fn_sony_door_interact(result, params)); }
68 
fn_unregister_for_auto_interaction(int32 & result,int32 * params)69 mcodeFunctionReturnCodes fn_unregister_for_auto_interaction(int32 &result, int32 *params) { return (MS->fn_unregister_for_auto_interaction(result, params)); }
70 
fn_wandering_custom_prop_interact(int32 & result,int32 * params)71 mcodeFunctionReturnCodes fn_wandering_custom_prop_interact(int32 &result, int32 *params) { return (MS->fn_wandering_custom_prop_interact(result, params)); }
72 
fn_wandering_generic_prop_interact(int32 & result,int32 * params)73 mcodeFunctionReturnCodes fn_wandering_generic_prop_interact(int32 &result, int32 *params) { return (MS->fn_wandering_generic_prop_interact(result, params)); }
74 
75 #define SONY_DOOR_STEP_BACK_DIST ((50 * REAL_ONE) * (50 * REAL_ONE))
76 #define SONY_DOOR_PRESS_DIST ((100 * REAL_ONE) * (100 * REAL_ONE))
77 
fn_set_interacting(int32 &,int32 * params)78 mcodeFunctionReturnCodes _game_session::fn_set_interacting(int32 &, int32 *params) {
79 	// set interting and id of target
80 	// so we can run prop interact type animation functions outside of an interaction
81 
82 	// params    0   name of target
83 
84 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
85 
86 	uint32 id = objects->Fetch_item_number_by_name(object_name);
87 	if (id == 0xffffffff)
88 		Fatal_error("fn_set_interacting - illegal object [%s]", object_name);
89 
90 	M->target_id = id;
91 
92 	M->interacting = TRUE8;
93 
94 	return (IR_CONT);
95 }
96 
fn_clear_interacting(int32 &,int32 *)97 mcodeFunctionReturnCodes _game_session::fn_clear_interacting(int32 &, int32 *) {
98 	M->interacting = FALSE8;
99 
100 	return (IR_STOP); // this is vital as currently the object will be
101 }
102 
fn_route_to_generic_prop_interact(int32 & result,int32 * params)103 mcodeFunctionReturnCodes _game_session::fn_route_to_generic_prop_interact(int32 &result, int32 *params) {
104 	// WALK-TO interact with a prop BUT DOESNT play a generic animation
105 	// will call a trigger script if finds marker and script
106 
107 	// params        0   name of generic animation
108 
109 	if (L->looping == 2) {
110 		L->looping = FALSE8;
111 		L->pan = logic_structs[M->target_id]->prop_interact_pan;
112 
113 		// force to stand, frame 0, restore pre anim coordinates
114 		POST_INTERACTION // fix coords and set to stand
115 
116 		    return (IR_CONT);
117 	}
118 
119 	return (Core_prop_interact(result, params, FALSE8, FALSE8));
120 }
121 
fn_route_to_custom_prop_interact(int32 & result,int32 * params)122 mcodeFunctionReturnCodes _game_session::fn_route_to_custom_prop_interact(int32 &result, int32 *params) {
123 	// WALK-TO interact with a prop BUT DOESNT play a custom non generic animation
124 	// then return to script
125 
126 	// params        0   name of custom animation
127 
128 	if (L->looping == 2) {
129 		L->looping = FALSE8;
130 		L->pan = logic_structs[M->target_id]->prop_interact_pan;
131 
132 		// force to stand, frame 0, restore pre anim coordinates
133 		POST_INTERACTION // fix coords and set to stand
134 
135 		    Reset_cur_megas_custom_type();
136 
137 		return (IR_CONT);
138 	}
139 
140 	return (Core_prop_interact(result, params, TRUE8, FALSE8));
141 }
142 
fn_sony_door_interact(int32 & result,int32 * params)143 mcodeFunctionReturnCodes _game_session::fn_sony_door_interact(int32 &result, int32 *params) {
144 	// special door situation whereby we are passed the names of two buttons and we need to work out which one to interact with
145 
146 	// params        0   name of first button
147 	//				1 name of second button
148 	//				2  number of buttons
149 
150 	PXfloat new_pan, diff;
151 	uint32 id;
152 	uint32 but_floor;
153 
154 	const char *button1_name = (const char *)MemoryUtil::resolvePtr(params[0]);
155 	const char *button2_name = (const char *)MemoryUtil::resolvePtr(params[1]);
156 
157 	if ((!params[2]) || (params[2] > 2))
158 		Fatal_error("fn_sony_door_interact - %d is illegal number of buttons, can be 1 or 2", params[2]);
159 
160 	result = FALSE8; // no button was pressed
161 
162 	if (!L->looping) {
163 		// work out which button to interact with
164 
165 		id = objects->Fetch_item_number_by_name(button1_name);
166 		if (id == 0xffffffff)
167 			Fatal_error("fn_sony_door_interact - illegal object [%s]", button1_name);
168 
169 		but_floor = floor_def->Return_floor_rect(logic_structs[id]->prop_xyz.x, logic_structs[id]->prop_xyz.z, M->actor_xyz.y, 0);
170 
171 		// angle
172 		new_pan = logic_structs[id]->prop_interact_pan; // get targets pan
173 
174 		// get difference between the two
175 		diff = L->pan - new_pan;
176 
177 		// correct
178 		if (diff > HALF_TURN)
179 			diff -= FULL_TURN;
180 		else if (diff < -HALF_TURN)
181 			diff += FULL_TURN;
182 
183 		if ((L->owner_floor_rect == but_floor) && (PXfabs(diff) < (FULL_TURN / 5))) { // 36 deg = +/- 18 deg
184 			// facing the same so this must be the button
185 			M->target_id = id; // change the target
186 
187 			if (prop_interact_dist < SONY_DOOR_STEP_BACK_DIST)
188 				M->reverse_route = TRUE8;
189 
190 			result = TRUE8; // button 1
191 		} else {
192 			// wanst button 1 - so do nothing if that was only button
193 			if (params[2] == 1) {
194 				return IR_CONT;
195 			}
196 
197 			// there is another button so lets take a look to see it is named correctly
198 			id = objects->Fetch_item_number_by_name(button2_name);
199 			if (id == 0xffffffff)
200 				Fatal_error("fn_sony_door_interact - illegal object [%s]", button2_name);
201 
202 			but_floor = floor_def->Return_floor_rect(logic_structs[id]->prop_xyz.x, logic_structs[id]->prop_xyz.z, M->actor_xyz.y, 0);
203 
204 			if (L->owner_floor_rect != but_floor)
205 				return IR_CONT;
206 
207 			M->target_id = id; // change the target
208 
209 			if (prop_interact_dist < SONY_DOOR_STEP_BACK_DIST)
210 				M->reverse_route = TRUE8;
211 
212 			result = TRUE8; // button 2
213 		}
214 	}
215 
216 	return IR_CONT;
217 }
218 
fn_custom_prop_interact(int32 & result,int32 * params)219 mcodeFunctionReturnCodes _game_session::fn_custom_prop_interact(int32 &result, int32 *params) {
220 	// interact with a prop and play a custom non generic animation
221 	return (Core_prop_interact(result, params, TRUE8, TRUE8));
222 }
223 
fn_generic_prop_interact(int32 & result,int32 * params)224 mcodeFunctionReturnCodes _game_session::fn_generic_prop_interact(int32 &result, int32 *params) {
225 	// interact with a prop and play a generic animation
226 	// will call a trigger script if finds marker and script
227 
228 	// params        0   name of generic animation
229 
230 	return (Core_prop_interact(result, params, FALSE8, TRUE8));
231 }
232 
fn_wandering_custom_prop_interact(int32 & result,int32 * params)233 mcodeFunctionReturnCodes _game_session::fn_wandering_custom_prop_interact(int32 &result, int32 *params) {
234 	// interact with a prop and play a custom non generic animation
235 
236 	return (Core_prop_interact(result, params, TRUE8, FALSE8));
237 }
238 
fn_wandering_generic_prop_interact(int32 & result,int32 * params)239 mcodeFunctionReturnCodes _game_session::fn_wandering_generic_prop_interact(int32 &result, int32 *params) {
240 	// interact with a prop and play a generic animation
241 	// will call a trigger script if finds marker and script
242 
243 	// params        0   name of generic animation
244 
245 	return (Core_prop_interact(result, params, FALSE8, FALSE8));
246 }
247 
Core_prop_interact(int32 &,int32 * params,bool8 custom,bool8 coord_correction)248 mcodeFunctionReturnCodes _game_session::Core_prop_interact(int32 & /*result*/, int32 *params, bool8 custom, bool8 coord_correction) {
249 	//bool8 initial_turn;
250 	bool8 res = FALSE8;
251 	__mega_set_names anim;
252 	PXreal destx, destz;
253 	PXfloat diff;
254 	int32 retval;
255 	PXreal sub1, sub2, len, len2;
256 	uint32 j;
257 
258 	// looping   0 init route
259 	//				1 process route
260 	//				2 init turn to pan
261 	//				3 async wait
262 	//				4 play target anim
263 	//				5
264 
265 	const char *anim_name = NULL;
266 	if (params && params[0]) {
267 		anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
268 	}
269 
270 	// set up first time in
271 
272 	if (!L->looping) {
273 
274 		// setup autoroute to coordinate
275 
276 		if (!custom) {
277 			Zdebug("calc *generic* target anim [%s]", anim_name);
278 
279 			// get anim type
280 			res = I->Find_anim_type(&anim, anim_name);
281 			if (!res)
282 				Fatal_error("Core_prop_interact cant indentify animation %s", anim_name);
283 
284 			if (!I->IsAnimTable(anim))
285 				Fatal_error("Core_prop_interact finds [%s] doesnt have a [%s] animation", object->GetName(), params[0]);
286 		} else {
287 			Zdebug("calc *custom* target anim [%s]", anim_name);
288 			I->Init_custom_animation(anim_name);
289 			anim = __NON_GENERIC;
290 		}
291 
292 		// start psx asyncing the anim - may already be doing so if scripts are written properly!
293 		if (rs_anims->Res_open(I->get_info_name(anim), I->info_name_hash[anim], I->base_path, I->base_path_hash) == 0)
294 			return IR_REPEAT;
295 
296 		// we are now looping, having done the init
297 		L->looping = 1;
298 
299 		// calculate the coordinate
300 		Compute_target_interaction_coordinate(anim, &destx, &destz); // uses target_id to derive initial target coord
301 
302 		// save target coord for later post animation correction
303 		M->target_xyz.x = destx;
304 		M->target_xyz.z = destz;
305 
306 		// first lets see if we are really quite close to the interact coordinate - if we are we'll snap
307 		sub1 = (PXreal)(destx - L->mega->actor_xyz.x);
308 		sub2 = (PXreal)(destz - L->mega->actor_xyz.z);
309 		len = (PXreal)((sub1 * sub1) + (sub2 * sub2)); // dist
310 		if (len < (35 * 35)) {
311 			L->mega->actor_xyz.x = destx;
312 			L->mega->actor_xyz.z = destz;
313 			L->looping = 2;
314 			return (IR_REPEAT);
315 		}
316 
317 		// lets see if the interact coordinate is further away than we are - which is bad news
318 
319 		// first, our coordinate to the prop
320 		sub1 = (PXreal)(logic_structs[M->target_id]->prop_xyz.x - L->mega->actor_xyz.x);
321 		sub2 = (PXreal)(logic_structs[M->target_id]->prop_xyz.z - L->mega->actor_xyz.z);
322 		len = (PXreal)((sub1 * sub1) + (sub2 * sub2)); // dist
323 
324 		// second, the interact point to the prop
325 		sub1 = (PXreal)(destx - logic_structs[M->target_id]->prop_xyz.x);
326 		sub2 = (PXreal)(destz - logic_structs[M->target_id]->prop_xyz.z);
327 		len2 = (PXreal)((sub1 * sub1) + (sub2 * sub2)); // dist
328 
329 		M->m_main_route.___init();
330 
331 		// set motion type
332 		if ((len2 > len) || (M->reverse_route == TRUE8)) { // if further away OR already set to reverse - must have been by fn-sony-door
333 			M->m_main_route.request_form.anim_type = __STEP_BACKWARD;
334 			M->reverse_route = TRUE8;
335 			//initial_turn = FALSE8;
336 		} else {
337 			//initial_turn = TRUE8;
338 
339 			if (M->motion == __MOTION_WALK)
340 				M->m_main_route.request_form.anim_type = __WALK;
341 			else
342 				M->m_main_route.request_form.anim_type = __RUN; // form.anim_type=__RUN;
343 		}
344 
345 		// new route do prepare a route request form!
346 		// initial x,z
347 		M->m_main_route.request_form.initial_x = M->actor_xyz.x;
348 		M->m_main_route.request_form.initial_z = M->actor_xyz.z;
349 
350 		// target x,z
351 		M->m_main_route.request_form.dest_x = (PXreal)destx;
352 		M->m_main_route.request_form.dest_z = (PXreal)destz;
353 
354 		Zdebug("PLAYER INTERACT to %3.2f,%3.2f from %3.2f,%3.2f", destx, destz, M->actor_xyz.x, M->actor_xyz.z);
355 
356 		// need characters y coordinate also
357 		M->m_main_route.request_form.character_y = M->actor_xyz.y;
358 
359 		// this function attempts to finish on stand
360 		M->m_main_route.request_form.finish_on_null_stand = TRUE8;
361 		M->m_main_route.request_form.finish_on_stand = FALSE8;
362 
363 		// set type
364 		M->m_main_route.request_form.rtype = ROUTE_points_only;
365 
366 		// now log and create the initial route
367 		// set a barrier mask :(
368 		session_barriers->Set_route_barrier_mask((int32)destx - 500, (int32)destx + 500, (int32)destz - 500, (int32)destz + 500);
369 		Create_initial_route(__FULL);
370 		session_barriers->Clear_route_barrier_mask();
371 
372 		// only one of these per cycle - we may have cheated and done a second route here but at least we can stop another if we
373 		// were first
374 		Set_router_busy();
375 
376 		// if the route could not be built
377 		if (M->m_main_route.request_form.error == __ROUTE_REQUEST_PRIM_FAILED) {
378 			Create_initial_route(__LASER); // lets get out of this the easy way!
379 		}
380 
381 		// we may not actually need a route if we are very close
382 		if (M->m_main_route.request_form.error == __RR_NO_ROUTE_REQUIRED) {
383 			Zdebug("skipping route");
384 			L->looping = 2; // bypass the route
385 			return (IR_REPEAT);
386 		}
387 	}
388 
389 	// routing
390 	if (L->looping == 1) {
391 		if (Process_route()) {
392 			// not looping any longer
393 			// set to turn phase
394 			L->looping = 2;
395 			return (IR_REPEAT);
396 		}
397 	}
398 
399 	// set up auto turning ready for anim play
400 	if (L->looping == 2) {
401 		diff = logic_structs[M->target_id]->prop_interact_pan - L->pan;
402 
403 		// work out which way to turn
404 		if (diff > HALF_TURN)
405 			diff -= FULL_TURN;
406 		else if (diff < -HALF_TURN)
407 			diff += FULL_TURN;
408 
409 		// diff is now the distance to turn by and its sign denotes direction
410 
411 		if (diff < FLOAT_ZERO)
412 			M->turn_dir = 0; // right
413 		else
414 			M->turn_dir = 1; // left
415 
416 		M->target_pan = (PXfloat)PXfabs(diff);                               // save positive pan distance
417 		M->auto_target_pan = logic_structs[M->target_id]->prop_interact_pan; // actual target which we may clip to
418 		L->auto_display_pan = L->pan;                                        // start where we currently are
419 		L->auto_panning = TRUE8;
420 
421 		L->looping = 3; // go straight to play anim
422 		return (IR_REPEAT);
423 	}
424 
425 	// check anim in memory
426 	if (L->looping == 3) {
427 
428 		if (custom)
429 			anim = __NON_GENERIC;
430 		else
431 			anim = Fetch_generic_anim_from_ascii(anim_name);
432 
433 		// in memory yet?
434 		if (rs_anims->Res_open(I->get_info_name(anim), I->info_name_hash[anim], I->base_path, I->base_path_hash)) {
435 			L->cur_anim_type = anim;
436 			L->anim_pc = 0;
437 			L->looping = 4; // go straight to play anim
438 		}
439 
440 		return IR_REPEAT;
441 	}
442 
443 	// running target animation
444 	if (L->looping == 4) {
445 		// get animation
446 		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); //
447 
448 		// last frame is currently displayed?
449 		if ((int32)(L->anim_pc + M->anim_speed) >= (pAnim->frame_qty - 1)) {
450 			L->looping = FALSE8;
451 			M->reverse_route = FALSE8;
452 
453 			// force to stand, frame 0
454 			if (coord_correction) {
455 				POST_INTERACTION // fix coords and set to stand
456 			} else {                 // was a wandering finish-where-we-finish interaction
457 				L->cur_anim_type = __STAND;
458 				L->anim_pc = 0;
459 			}
460 
461 			Reset_cur_megas_custom_type();
462 			return (IR_CONT);
463 		}
464 
465 		// shift character and frame forward by the amount appropriate
466 		if (!MS->Easy_frame_and_motion(L->cur_anim_type, 0, M->anim_speed)) {
467 			L->looping = FALSE8;
468 
469 			M->reverse_route = FALSE8;
470 
471 			// force to stand, frame 0, restore pre anim coordinates
472 			if (coord_correction) {
473 				POST_INTERACTION // fix coords and set to stand
474 			} else {
475 				L->cur_anim_type = __STAND;
476 				L->anim_pc = 0;
477 			}
478 
479 			Reset_cur_megas_custom_type();
480 			return (IR_CONT);
481 		}
482 
483 		// is the interact marker on this frame ?
484 		for (j = 0; j < M->anim_speed; j++) {
485 			PXframe *frame = PXFrameEnOfAnim(L->anim_pc + j, pAnim);
486 
487 			if ((frame->marker_qty > INT_POS) && (INT_TYPE == (frame->markers[INT_POS].GetType()))) {
488 				//          run the trigger anim
489 				if (!MS->Call_socket(M->target_id, "trigger", &retval)) {
490 					Message_box("[%s] interact marker but no trigger script", (const char *)L->GetName());
491 					Message_box("anim %s Target ID %d [%s]", master_anim_name_table[L->cur_anim_type].name, M->target_id, Fetch_object_name(M->target_id));
492 				}
493 
494 				break; // done it
495 			}
496 		}
497 	}
498 
499 	// not finished, so see you next cycle
500 	return (IR_REPEAT);
501 }
502 
fn_is_there_interact_object(int32 & result,int32 *)503 mcodeFunctionReturnCodes _game_session::fn_is_there_interact_object(int32 &result, int32 *) {
504 	// return yes or no for whether or not an interact object exists
505 
506 	result = player.Fetch_player_interact_status();
507 
508 	return (IR_CONT);
509 }
510 
fn_get_interact_object_id(int32 & result,int32 *)511 mcodeFunctionReturnCodes _game_session::fn_get_interact_object_id(int32 &result, int32 *) {
512 	// return yes or no for whether or not an interact object exists
513 
514 	result = player.Fetch_player_interact_id();
515 
516 	return (IR_CONT);
517 }
518 
fn_is_object_interact_object(int32 & result,int32 * params)519 mcodeFunctionReturnCodes _game_session::fn_is_object_interact_object(int32 &result, int32 *params) {
520 	// return yes or no for whether or not an interact object exists
521 
522 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
523 
524 	uint32 id = objects->Fetch_item_number_by_name(object_name);
525 	if (id == 0xffffffff)
526 		Fatal_error("fn_is_object_interact_object - object [%s] does not exist", object_name);
527 
528 	if (id == player.Fetch_player_interact_id())
529 		result = TRUE8;
530 	else
531 		result = FALSE8;
532 
533 	return (IR_CONT);
534 }
535 
fn_unregister_for_auto_interaction(int32 &,int32 *)536 mcodeFunctionReturnCodes _game_session::fn_unregister_for_auto_interaction(int32 &, int32 *) {
537 	// as the name says - for stairs, session joins, etc.
538 	uint32 j;
539 
540 	for (j = 0; j < MAX_auto_interact; j++) {
541 		if (auto_interact_list[j] == (uint8)(cur_id + 1)) {
542 			Tdebug("auto_interact.txt", "- [%s] %d", object->GetName(), j);
543 			auto_interact_list[j] = 0; // slot not empty
544 			return IR_CONT;
545 		}
546 	}
547 
548 	Fatal_error("fn_unregister_for_auto_interaction cant unregister non registered object [%s]", object->GetName());
549 
550 	return IR_CONT;
551 }
552 
fn_register_for_auto_interaction(int32 &,int32 *)553 mcodeFunctionReturnCodes _game_session::fn_register_for_auto_interaction(int32 &, int32 *) {
554 	// as the name says - for stairs, session joins, etc.
555 
556 	uint32 j;
557 
558 	for (j = 0; j < MAX_auto_interact; j++) {
559 		if (auto_interact_list[j] == (uint8)(cur_id + 1))
560 			Fatal_error("fn_register_for_auto_interaction finds double registration of %s", object->GetName());
561 
562 		if (!auto_interact_list[j]) { // empty slot
563 			auto_interact_list[j] = (uint8)(cur_id + 1);
564 			Tdebug("auto_interact.txt", "+ [%s] %d", object->GetName(), j);
565 			return IR_CONT;
566 		}
567 	}
568 
569 	Fatal_error("fn_register_for_auto_interaction - list full - [%s]", object->GetName());
570 
571 	return IR_CONT;
572 }
573 
574 } // End of namespace ICB
575