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, ¶ms);
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