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/session.h"
30 #include "engines/icb/mission.h"
31 #include "engines/icb/p4.h"
32 #include "engines/icb/object_structs.h"
33 #include "engines/icb/global_objects.h"
34 #include "engines/icb/custom_logics.h"
35 #include "engines/icb/common/px_scriptengine.h"
36 #include "engines/icb/common/px_linkeddatafile.h"
37 #include "engines/icb/common/px_prop_anims.h"
38 #include "engines/icb/common/ptr_util.h"
39 #include "engines/icb/sound.h"
40 
41 namespace ICB {
42 
fn_set_custom_simple_animator(int32 & result,int32 * params)43 mcodeFunctionReturnCodes fn_set_custom_simple_animator(int32 &result, int32 *params) { return (MS->fn_set_custom_simple_animator(result, params)); }
44 
fn_set_custom_button_operated_door(int32 & result,int32 * params)45 mcodeFunctionReturnCodes fn_set_custom_button_operated_door(int32 &result, int32 *params) { return (MS->fn_set_custom_button_operated_door(result, params)); }
46 
fn_set_custom_auto_door(int32 & result,int32 * params)47 mcodeFunctionReturnCodes fn_set_custom_auto_door(int32 &result, int32 *params) { return (MS->fn_set_custom_auto_door(result, params)); }
48 
fn_set_cad_lock_status(int32 & result,int32 * params)49 mcodeFunctionReturnCodes fn_set_cad_lock_status(int32 &result, int32 *params) { return (MS->fn_set_cad_lock_status(result, params)); }
50 
fn_get_cad_state_flag(int32 & result,int32 * params)51 mcodeFunctionReturnCodes fn_get_cad_state_flag(int32 &result, int32 *params) { return (MS->fn_get_cad_state_flag(result, params)); }
52 
fn_set_custom_simple_animator(int32 &,int32 *)53 mcodeFunctionReturnCodes _game_session::fn_set_custom_simple_animator(int32 &, int32 *) {
54 	// chnage the object to be a special simple animator type where all scripts are circumvented
55 	// we need to check here, first, for legality - i.e. we clear the way for run-time assumptions to be made
56 	_animating_prop *index;
57 	_animation_entry *anim;
58 
59 	// find entry for this object via its name
60 	index = (_animating_prop *)prop_anims->Try_fetch_item_by_name(object->GetName());
61 
62 	// get anim
63 	anim = (_animation_entry *)(((char *)index) + index->anims[0]);
64 
65 	// check for no frame
66 	if (!anim->num_frames) {
67 		Tdebug("objects_that_died.txt", "fn_set_custom_simple_animator [%s] loop anim has 0 frames", object->GetName());
68 		Shut_down_object("by fn_set_custom_simple_animator");
69 		return (IR_STOP);
70 	}
71 
72 	// start at frame 0
73 	L->anim_pc = 0;
74 
75 	L->big_mode = __CUSTOM_SIMPLE_ANIMATE;
76 
77 	SA_INDEX = prop_anims->Fetch_item_number_by_name(object->GetName());
78 
79 	// object will pause when off screen
80 	L->hold_mode = prop_camera_hold;
81 
82 	Tdebug("logic_modes.txt", "fn_set_custom_simple_animator switching [%s]", object->GetName());
83 
84 	return (IR_CONT);
85 }
86 
Custom_simple_animator()87 void _game_session::Custom_simple_animator() {
88 	// this special _big_mode logic replaces the logic script of a lib_simple_animator object
89 	// this is a rationalisation designed to aid speed up on psx
90 	_animating_prop *index;
91 	_animation_entry *anim;
92 
93 	// get index for object
94 	index = (_animating_prop *)prop_anims->Fetch_item_by_number(SA_INDEX);
95 
96 	// now then, lets make the assumption that anim 0 will be the 'looping' one
97 	anim = (_animation_entry *)(((char *)index) + index->anims[0]);
98 
99 	if ((uint8)L->anim_pc == (anim->num_frames - 1))
100 		L->anim_pc = 0;
101 	else
102 		L->anim_pc++; // advance current pc
103 
104 	// set frame
105 	prop_state_table[cur_id] = anim->frames[L->anim_pc];
106 }
107 
fn_set_custom_button_operated_door(int32 &,int32 * params)108 mcodeFunctionReturnCodes _game_session::fn_set_custom_button_operated_door(int32 &, int32 *params) {
109 	// set to special custom door logic
110 
111 	// params    0       initial state value
112 	// prime
113 	BOD_STATE = params[0];
114 
115 	// starts non animating
116 	BOD_CONTROL = 0; // not opening or closing
117 
118 	// switch out of script mode
119 	L->big_mode = __CUSTOM_BUTTON_OPERATED_DOOR;
120 
121 	BOD_INDEX = prop_anims->Fetch_item_number_by_name(object->GetName());
122 
123 	BOD_OPEN_NO = Validate_prop_anim("opening");
124 	BOD_CLOSE_NO = Validate_prop_anim("closing");
125 
126 	Tdebug("logic_modes.txt", "fn_set_custom_button_operated_door switching [%s]", object->GetName());
127 
128 	// Set a symbol type so the Remora knows what to draw.
129 	L->object_type = __BUTTON_OPERATED_DOOR;
130 
131 	// push the y up for Cord to look at
132 	L->prop_xyz.y += 179;
133 
134 	return IR_CONT;
135 }
136 
Custom_button_operated_door()137 void _game_session::Custom_button_operated_door() {
138 	// this special _big_mode logic replaces the logic script of a lib_simple_animator object
139 	// this is a rationalisation designed to aid speed up on psx
140 
141 	_animating_prop *index;
142 	_animation_entry *anim;
143 	uint32 var_num;
144 	int32 params, result;
145 
146 	do { // do while because when we switch in to new anim we need to do an anim
147 		if (BOD_CONTROL == BOD_WAITING) {
148 			// we've opened, now we wait until no one in the list is near to us
149 			if (!BOD_WAIT_COUNT) {
150 				params = 300;
151 				fn_near_list(result, &params);
152 
153 				if (result == TRUE8) {
154 					BOD_WAIT_COUNT = 36; // count again
155 					return;
156 				}
157 				// else, close
158 
159 				L->anim_pc = 0;
160 				BOD_CONTROL = BOD_CLOSING;
161 				BOD_STATE = 1; // closed
162 
163 				// set state flag to 1
164 				var_num = object->GetVariable("state");
165 				object->SetIntegerVariable(var_num, 1);
166 			} else {
167 				BOD_WAIT_COUNT--;
168 				return;
169 			}
170 		}
171 
172 		if (BOD_CONTROL == BOD_OPENING) {
173 			// get index for object
174 			index = (_animating_prop *)prop_anims->Fetch_item_by_number(BOD_INDEX);
175 			anim = (_animation_entry *)(((char *)index) + index->anims[BOD_OPEN_NO]);
176 			prop_state_table[cur_id] = anim->frames[L->anim_pc];
177 
178 			if ((uint8)L->anim_pc == (anim->num_frames - 1)) {
179 				BOD_CONTROL = BOD_WAITING; // just opened - set to wait for list of people to not be here
180 				BOD_WAIT_COUNT = 36;
181 				BOD_STATE = 0; // open
182 				// set state flag to 0
183 				var_num = object->GetVariable("state");
184 				object->SetIntegerVariable(var_num, 0);
185 
186 			} else
187 				L->anim_pc++; // frame on
188 			return;
189 		} else if (BOD_CONTROL == BOD_CLOSING) {
190 			// get index for object
191 			index = (_animating_prop *)prop_anims->Try_fetch_item_by_name(object->GetName());
192 			anim = (_animation_entry *)(((char *)index) + index->anims[BOD_CLOSE_NO]);
193 
194 			// set frame
195 			prop_state_table[cur_id] = anim->frames[L->anim_pc];
196 
197 			if ((uint8)L->anim_pc == (anim->num_frames - 1))
198 				BOD_CONTROL = 0; // cancel mode
199 			else
200 				L->anim_pc++; // frame on
201 
202 			return;
203 		}
204 
205 		// ok, we're not animating so just check state
206 		//  what state are we in?
207 		if (!BOD_STATE) { // open
208 			// ok, check to see if opened
209 			// get state variable number
210 			var_num = object->GetVariable("state");
211 			// get value
212 			if (!object->GetIntegerVariable(var_num)) {
213 				BOD_CONTROL = BOD_WAITING; // just opened - set to wait for list of people to not be here
214 				BOD_WAIT_COUNT = 36;
215 				return; // zero so still open
216 			}
217 
218 			L->anim_pc = 0;
219 
220 			BOD_CONTROL = BOD_CLOSING;
221 
222 			BOD_STATE = 1; // closed
223 
224 			// close sound
225 			if (logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR] != 0)
226 				RegisterSound(cur_id, NULL, logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR], closeDesc,
227 				              (int8)127); // have to use full version so we can give hash instead of string
228 			else
229 				RegisterSound(cur_id, defaultCloseSfx, closeDesc); // use small version as we have string not hash
230 		} else {                                                           // closed
231 			// check for
232 			// a. someone pressing button
233 			// b. chi off screen being near
234 
235 			bool8 open = FALSE8;
236 
237 			if (is_there_a_chi) {
238 				// if chi is on a different floor from player then assume she is following and so auto open the door for her
239 				// - sneaky
240 				if (logic_structs[chi_id]->owner_floor_rect != logic_structs[player.Fetch_player_id()]->owner_floor_rect) {
241 					PXreal sub1, sub2;
242 
243 					sub1 = logic_structs[chi_id]->mega->actor_xyz.x - L->prop_xyz.x;
244 					sub2 = logic_structs[chi_id]->mega->actor_xyz.z - L->prop_xyz.z;
245 
246 					// dist
247 					if (((sub1 * sub1) + (sub2 * sub2)) < (200 * 200)) {
248 						open = TRUE8;
249 					}
250 				}
251 			}
252 			// get state variable number
253 			var_num = object->GetVariable("state");
254 
255 			// get value
256 			if (!object->GetIntegerVariable(var_num)) {
257 				open = TRUE8; // now 0 so start opening
258 			}
259 			if (open) {
260 				L->anim_pc = 0;
261 				BOD_CONTROL = BOD_OPENING;
262 				BOD_STATE = 0; // open
263 
264 				// open sound
265 				if (logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR] != 0)
266 					RegisterSound(cur_id, NULL, logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR], openDesc,
267 					              (int8)127); // have to use full version so we can give hash instead of string
268 				else
269 					RegisterSound(cur_id, defaultOpenSfx, openDesc); // use small version as we have string not hash
270 			} else {                                                         // still closed - see if we can go to sleep
271 				if (!L->prop_on_this_screen) {
272 					// closed and no on screen
273 					L->camera_held = TRUE8; // not on screen
274 					L->cycle_time = 0;      // accurate for displays
275 				}
276 				return;
277 			}
278 		}
279 	} while (1);
280 }
281 
fn_set_custom_auto_door(int32 &,int32 * params)282 mcodeFunctionReturnCodes _game_session::fn_set_custom_auto_door(int32 &, int32 *params) {
283 	// set to special custom door logic
284 
285 	// params    0       initial state value
286 	// params 1      dist for detection
287 	// params 2      locked or not
288 	CAD_STATE = params[0];
289 	CAD_DIST = params[1];
290 	CAD_LOCKED = params[2]; // starts not locked
291 	L->list[9] = params[1];
292 	// switch out of script mode
293 	L->big_mode = __CUSTOM_AUTO_DOOR;
294 
295 	// anim presets
296 	CAD_INDEX = prop_anims->Fetch_item_number_by_name(object->GetName());
297 	CAD_OPEN_NO = Validate_prop_anim("opening");
298 	CAD_CLOSE_NO = Validate_prop_anim("closing");
299 
300 	Tdebug("logic_modes.txt", "fn_set_custom_auto_door switching [%s]", object->GetName());
301 
302 	// Set a symbol type so the Remora knows what to draw.
303 	L->object_type = __AUTO_DOOR;
304 
305 	return (IR_CONT);
306 }
307 
Custom_auto_door()308 void _game_session::Custom_auto_door() {
309 	// this special _big_mode logic replaces the logic script of a lib_auto_slide_door object
310 	// this is a rationalisation designed to aid speed up on psx
311 
312 	_animating_prop *index;
313 	_animation_entry *anim;
314 	uint32 j, id;
315 	bool8 sensed = FALSE8;
316 
317 	L->list[8] = 0;
318 
319 	// check if someone is here or not
320 	j = 0;
321 	while ((j < number_of_voxel_ids) && (!sensed)) { // object 0 is used
322 		id = voxel_id_list[j++];
323 
324 		if ((!logic_structs[id]->mega->dead) && (logic_structs[id]->ob_status != OB_STATUS_HELD)) {    // still alive
325 			if (PXfabs(L->prop_xyz.y - logic_structs[id]->mega->actor_xyz.y) < (200 * REAL_ONE)) { // slack for height calc
326 				if ((PXfabs(L->prop_xyz.x - logic_structs[id]->mega->actor_xyz.x) < (PXreal)CAD_DIST) &&
327 				    (PXfabs(L->prop_xyz.z - logic_structs[id]->mega->actor_xyz.z) < (PXreal)CAD_DIST)) {
328 					// yes
329 					sensed = TRUE8;
330 					L->list[8]++;
331 				}
332 			}
333 		}
334 	}
335 
336 	if (CAD_STATE == CAD_OPENING) { // doors opening
337 		// get index for object
338 		index = (_animating_prop *)prop_anims->Fetch_item_by_number(CAD_INDEX);
339 
340 		anim = (_animation_entry *)(((char *)index) + index->anims[CAD_OPEN_NO]);
341 		prop_state_table[cur_id] = anim->frames[L->anim_pc];
342 
343 		if ((uint8)L->anim_pc == (anim->num_frames - 1)) {
344 			CAD_STATE = CAD_OPEN; // cancel mode
345 			CAD_WAIT = CAD_TIMER; // when we sense somewhat we reset the timer
346 		} else
347 			L->anim_pc++; // frame on
348 
349 		return;
350 	} else if (CAD_STATE == CAD_OPEN) {
351 		if ((!CAD_LOCKED) && (!sensed)) { // no one here anymore so switch modes
352 			if (CAD_WAIT) {           // now wait for time up
353 				CAD_WAIT--;       // 1 less
354 				return;
355 			}
356 
357 			CAD_STATE = CAD_CLOSING;
358 			// close sound
359 			if (logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR] != 0)
360 				RegisterSound(cur_id, NULL, logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR], closeDesc,
361 				              (int8)127); // have to use full version so we can give hash instead of string
362 			else
363 				RegisterSound(cur_id, defaultCloseSfx, closeDesc); // use small version as we have string not hash
364 		}
365 
366 		CAD_WAIT = CAD_TIMER; // when we sense somewhat we reset the timer
367 		return;
368 	} else if (CAD_STATE == CAD_CLOSING) { // doors closing
369 		if (sensed) {                  // sensed someone so switch modes
370 			CAD_STATE = CAD_OPENING;
371 			return;
372 		}
373 		// get index for object
374 		index = (_animating_prop *)prop_anims->Fetch_item_by_number(CAD_INDEX);
375 
376 		// when closing we reverse the opening anim - until the done when we set to last frame of closing
377 		anim = (_animation_entry *)(((char *)index) + index->anims[CAD_OPEN_NO]);
378 		prop_state_table[cur_id] = anim->frames[L->anim_pc];
379 
380 		if (!L->anim_pc) {
381 			CAD_STATE = CAD_CLOSED; // cancel mode
382 			anim = (_animation_entry *)(((char *)index) + index->anims[CAD_CLOSE_NO]);
383 			prop_state_table[cur_id] = anim->frames[anim->num_frames - 1];
384 		} else
385 			L->anim_pc--; // frame on
386 
387 		return;
388 	} else if (CAD_STATE == CAD_CLOSED) {
389 		if ((!CAD_LOCKED) && (sensed)) { // sensed someone so switch modes
390 			CAD_STATE = CAD_OPENING;
391 			L->anim_pc = 0;
392 
393 			// open sound
394 			if (logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR] != 0)
395 				RegisterSound(cur_id, NULL, logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR], openDesc,
396 				              (int8)127); // have to use full version so we can give hash instead of string
397 			else
398 				RegisterSound(cur_id, defaultOpenSfx, openDesc); // use small version as we have string not hash
399 		}
400 
401 		if (!L->prop_on_this_screen) {
402 			// closed and no on screen
403 			L->camera_held = TRUE8; // not on screen
404 			L->cycle_time = 0;      // accurate for displays
405 		}
406 
407 		return;
408 	}
409 }
410 
fn_set_cad_lock_status(int32 &,int32 * params)411 mcodeFunctionReturnCodes _game_session::fn_set_cad_lock_status(int32 &, int32 *params) {
412 	// set the locked status of an auto slide door
413 	// only the object should call this
414 
415 	// params    0   0 name of door
416 	// params    0   0 or non zero
417 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
418 	uint32 id;
419 
420 	id = (uint32)objects->Fetch_item_number_by_name(object_name);
421 	logic_structs[id]->list[5] = params[1];
422 
423 	return IR_CONT;
424 }
425 
fn_get_cad_state_flag(int32 & result,int32 * params)426 mcodeFunctionReturnCodes _game_session::fn_get_cad_state_flag(int32 &result, int32 *params) {
427 	// return custom auto door state to button
428 
429 	// params    0   name of door
430 	const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
431 	uint32 id;
432 
433 	id = (uint32)objects->Fetch_item_number_by_name(object_name);
434 	if (logic_structs[id]->EXT_CAD_STATE == CAD_OPEN)
435 		result = 1;
436 	else
437 		result = 0;
438 
439 	return IR_CONT;
440 }
441 
442 } // End of namespace ICB
443