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/common/ptr_util.h"
30 #include "engines/icb/icb.h"
31 #include "engines/icb/session.h"
32 #include "engines/icb/mission.h"
33 #include "engines/icb/p4.h"
34 #include "engines/icb/object_structs.h"
35 #include "engines/icb/global_objects.h"
36 #include "engines/icb/sound.h"
37 #include "engines/icb/res_man.h"
38
39 namespace ICB {
40
fn_simple_chi(int32 & result,int32 * params)41 mcodeFunctionReturnCodes fn_simple_chi(int32 &result, int32 *params) { return (MS->fn_simple_chi(result, params)); }
42
fn_chi(int32 & result,int32 * params)43 mcodeFunctionReturnCodes fn_chi(int32 &result, int32 *params) { return (MS->fn_chi(result, params)); }
44
fn_start_chi_following(int32 & result,int32 * params)45 mcodeFunctionReturnCodes fn_start_chi_following(int32 &result, int32 *params) { return (MS->fn_start_chi_following(result, params)); }
46
fn_record_player_interaction(int32 & result,int32 * params)47 mcodeFunctionReturnCodes fn_record_player_interaction(int32 &result, int32 *params) { return (MS->fn_record_player_interaction(result, params)); }
48
fn_fetch_chi_mode(int32 & result,int32 * params)49 mcodeFunctionReturnCodes fn_fetch_chi_mode(int32 &result, int32 *params) { return (MS->fn_fetch_chi_mode(result, params)); }
50
fn_check_for_chi(int32 & result,int32 * params)51 mcodeFunctionReturnCodes fn_check_for_chi(int32 &result, int32 *params) { return (MS->fn_check_for_chi(result, params)); }
52
fn_wait_for_chi(int32 & result,int32 * params)53 mcodeFunctionReturnCodes fn_wait_for_chi(int32 &result, int32 *params) { return (MS->fn_wait_for_chi(result, params)); }
54
fn_send_chi_to_this_object(int32 & result,int32 * params)55 mcodeFunctionReturnCodes fn_send_chi_to_this_object(int32 &result, int32 *params) { return (MS->fn_send_chi_to_this_object(result, params)); }
56
fn_chi_wait_for_player_to_move(int32 & result,int32 * params)57 mcodeFunctionReturnCodes fn_chi_wait_for_player_to_move(int32 &result, int32 *params) { return (MS->fn_chi_wait_for_player_to_move(result, params)); }
58
fn_stop_chi_following(int32 & result,int32 * params)59 mcodeFunctionReturnCodes fn_stop_chi_following(int32 &result, int32 *params) { return (MS->fn_stop_chi_following(result, params)); }
60
fn_register_chi(int32 & result,int32 * params)61 mcodeFunctionReturnCodes fn_register_chi(int32 &result, int32 *params) { return (MS->fn_register_chi(result, params)); }
62
fn_send_chi_to_named_object(int32 & result,int32 * params)63 mcodeFunctionReturnCodes fn_send_chi_to_named_object(int32 &result, int32 *params) { return (MS->fn_send_chi_to_named_object(result, params)); }
64
fn_chi_heard_gunshot(int32 & result,int32 * params)65 mcodeFunctionReturnCodes fn_chi_heard_gunshot(int32 &result, int32 *params) { return (MS->fn_chi_heard_gunshot(result, params)); }
66
fn_calibrate_chi(int32 & result,int32 * params)67 mcodeFunctionReturnCodes fn_calibrate_chi(int32 &result, int32 *params) { return (MS->fn_calibrate_chi(result, params)); }
68
fn_simple_chi(int32 &,int32 *)69 mcodeFunctionReturnCodes _game_session::fn_simple_chi(int32 &, int32 *) {
70 // simple chi that teleports around
71
72 Fatal_error("fn_simple_chi not supported");
73
74 return (IR_CONT);
75 }
76
fn_start_chi_following(int32 &,int32 *)77 mcodeFunctionReturnCodes _game_session::fn_start_chi_following(int32 &, int32 *) {
78 // the real chi
79
80 g_mission->chi_following = TRUE8;
81
82 chi_history = cur_history; // chi gets same point as cord
83
84 chi_think_mode = __FOLLOWING; // set mode
85
86 chi_do_mode = __THINKING; // thinking about following :)
87
88 chi_next_move = 100; // 100 cycles
89
90 Tdebug("chi.txt", "-+fn_start_chi_following - history=%d +-", chi_history);
91
92 permission_to_fire = FALSE8; // reset
93
94 return (IR_CONT);
95 }
96
fn_stop_chi_following(int32 &,int32 *)97 mcodeFunctionReturnCodes _game_session::fn_stop_chi_following(int32 &, int32 *) {
98 // stop her
99 // do this before changing her to custom logics
100
101 g_mission->chi_following = FALSE8;
102
103 chi_think_mode = __NOTHING; // nowt
104
105 Tdebug("chi.txt", "-+fn_stop_chi_following");
106
107 return (IR_CONT);
108 }
109
fn_record_player_interaction(int32 &,int32 *)110 mcodeFunctionReturnCodes _game_session::fn_record_player_interaction(int32 &, int32 *) {
111 // write the id of interact object to player history list
112 // advance history
113 cur_history++;
114 if (cur_history == MAX_player_history)
115 cur_history = 0; // wrap
116
117 // record it
118 history[cur_history].interaction = TRUE8;
119 history[cur_history].id = M->target_id;
120
121 Tdebug("history.txt", "-> [%s] %d", objects->Fetch_items_name_by_number(M->target_id), M->target_id);
122
123 return IR_CONT;
124 }
125
fn_send_chi_to_named_object(int32 &,int32 * params)126 mcodeFunctionReturnCodes _game_session::fn_send_chi_to_named_object(int32 &, int32 *params) {
127 const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
128 uint32 id;
129
130 id = objects->Fetch_item_number_by_name(object_name);
131 if (id == 0xffffffff)
132 Fatal_error("fn_send_chi_to_named_object - illegal object [%s]", object_name);
133
134 // advance history
135 cur_history++;
136 if (cur_history == MAX_player_history)
137 cur_history = 0; // wrap
138
139 // record it
140 history[cur_history].interaction = TRUE8;
141 history[cur_history].id = id;
142
143 Tdebug("history.txt", ">> [%s] %d", object->GetName(), cur_id);
144
145 return IR_CONT;
146 }
147
fn_send_chi_to_this_object(int32 &,int32 *)148 mcodeFunctionReturnCodes _game_session::fn_send_chi_to_this_object(int32 &, int32 *) {
149 // write the id of calling object to player history list
150
151 // advance history
152 cur_history++;
153 if (cur_history == MAX_player_history)
154 cur_history = 0; // wrap
155
156 // record it
157 history[cur_history].interaction = TRUE8;
158 history[cur_history].id = cur_id;
159
160 return IR_CONT;
161 }
162
fn_fetch_chi_mode(int32 & result,int32 *)163 mcodeFunctionReturnCodes _game_session::fn_fetch_chi_mode(int32 &result, int32 *) {
164 // returns the chi mode (__chi_think_mode) to script
165
166 result = (int32)chi_think_mode;
167
168 return IR_CONT;
169 }
170
fn_fetch_chi_mode()171 __chi_think_mode _game_session::fn_fetch_chi_mode() {
172 // returns the chi mode (__chi_think_mode) to engine
173 return chi_think_mode;
174 }
175
fn_chi_heard_gunshot(int32 &,int32 *)176 mcodeFunctionReturnCodes _game_session::fn_chi_heard_gunshot(int32 &, int32 *) {
177 // called when chi hears a gunshot
178
179 // if we can see the player and same history - or, we're already in armed mode
180 if (((chi_history == cur_history) && (g_oLineOfSight->LineOfSight(cur_id, player.Fetch_player_id()))) || (chi_do_mode == __FIGHT_HELP)) {
181 permission_to_fire = 1; // chi can start fighting
182 }
183
184 return IR_CONT;
185 }
186
fn_calibrate_chi(int32 &,int32 * params)187 mcodeFunctionReturnCodes _game_session::fn_calibrate_chi(int32 &, int32 *params) {
188 // set chi's near and far distances
189
190 // params 0 catch up distance
191 // 1 lost player distance
192
193 chi_catch_up_dist = (PXreal)(params[0] * params[0]);
194
195 chi_lost_dist = (PXreal)(params[1] * params[1]);
196
197 return IR_CONT;
198 }
199
Set_chi_permission()200 void _game_session::Set_chi_permission() {
201 // called by players gunshot code
202
203 permission_to_fire = 1; // chi can start fighting
204 }
205
Find_a_chi_target()206 bool8 _game_session::Find_a_chi_target() {
207 // find an ememy for chi
208 uint32 j;
209
210 for (j = 0; j < number_of_voxel_ids; j++) {
211 if ((!logic_structs[voxel_id_list[j]]->mega->dead) && // alive
212 (logic_structs[voxel_id_list[j]]->ob_status != OB_STATUS_HELD) && // not held
213 (logic_structs[voxel_id_list[j]]->mega->is_evil) && // is evil
214 (Object_visible_to_camera(voxel_id_list[j])) && // on screen
215 (g_oLineOfSight->ObjectToObject(cur_id, voxel_id_list[j], LIGHT, 0, _line_of_sight::USE_OBJECT_VALUE,
216 TRUE8))) { // can see
217 chi_target_id = voxel_id_list[j];
218 chi_has_target = TRUE8;
219 Tdebug("chi_targets.txt", "chi selects [%s] as target", logic_structs[chi_target_id]->GetName());
220 return TRUE8;
221 }
222 }
223
224 chi_has_target = FALSE8;
225
226 // no
227 return FALSE8;
228 }
229
Chi_leaves_fight_mode()230 void _game_session::Chi_leaves_fight_mode() {
231 // set animation
232 L->cur_anim_type = __PULL_OUT_WEAPON;
233 I->IsAnimTable(L->cur_anim_type);
234
235 // get animation
236 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); //
237 // set to last frame
238
239 L->anim_pc = anim->frame_qty - 2; // if 10 frames then 10+1 (looper) == 11 meaning 9 is last displayable frame number
240 M->next_anim_type = __NO_ANIM;
241 chi_do_mode = __DISARM_TO_THINK; // back to thinking next cycle
242 }
243
fn_chi(int32 &,int32 *)244 mcodeFunctionReturnCodes _game_session::fn_chi(int32 &, int32 *) {
245 // the real chi
246
247 while (!Process_chi()) {
248 };
249
250 return IR_REPEAT;
251 }
252
Process_chi()253 bool8 _game_session::Process_chi() {
254 uint32 next_room;
255 PXreal x, z;
256 int32 result;
257 bool8 res;
258 PXreal sub1, sub2, cord_dist;
259 bool8 route_res;
260
261 switch (chi_do_mode) {
262 case __ANIMATE_TO_THINK:
263 if (Play_anim()) {
264 chi_do_mode = __THINKING;
265 }
266 return TRUE8;
267 break;
268
269 case __DISARM_TO_THINK:
270 if (Play_reverse_anim()) {
271
272 chi_do_mode = __THINKING;
273 Set_pose(__NOT_ARMED);
274 Change_pose_in_current_anim_set();
275
276 L->cur_anim_type = __STAND;
277 I->IsAnimTable(L->cur_anim_type);
278 }
279 return TRUE8;
280 break;
281
282 case __GET_WEAPON_OUT:
283 if (fight_pause)
284 fight_pause--;
285 else {
286 Set_pose(__GUN);
287 Change_pose_in_current_anim_set();
288
289 L->anim_pc = 0; // start here
290 L->cur_anim_type = __PULL_OUT_WEAPON;
291 M->next_anim_type = __STAND;
292
293 I->IsAnimTable(L->cur_anim_type);
294
295 Find_a_chi_target(); // select a target if there is one available
296
297 chi_do_mode = __ANIMATE_TO_FIGHT_HELP;
298 }
299
300 return TRUE8;
301 break;
302
303 case __ANIMATE_TO_FIGHT_HELP:
304 if (Play_anim()) {
305 chi_do_mode = __FIGHT_HELP;
306 }
307 return TRUE8;
308 break;
309
310 case __TURN_RND:
311 if (fast_face_rnd(2))
312 chi_do_mode = __FIGHT_HELP;
313 return TRUE8;
314 break;
315
316 case __TURN_TO_FACE_OBJECT:
317 if (fast_face_object(chi_target_id, 3)) {
318 chi_do_mode = __FIGHT_HELP;
319 }
320
321 return TRUE8;
322 break;
323
324 case __INTERACT_FOLLOW: // return from interact with ladder, stair, etc., here
325 // we then move the position on and think again
326 // arrived
327 chi_do_mode = __THINKING;
328
329 // move on to next position
330 chi_history += 1;
331 if (chi_history == MAX_player_history)
332 chi_history = 0; // wrap
333 chi_history += 1;
334 if (chi_history == MAX_player_history)
335 chi_history = 0; // wrap
336
337 chi_next_move = 12;
338
339 return TRUE8;
340 break;
341
342 case __ROUTING:
343 // first do a check to see if players wandered back onto our floor
344 // if we're on same floor as we started AND we meet player then stop
345 if ((history[chi_history].id == L->owner_floor_rect) && (L->owner_floor_rect == logic_structs[player.Fetch_player_id()]->owner_floor_rect)) {
346 chi_do_mode = __ANIMATE_TO_THINK;
347 chi_history = cur_history; // chi gets same point as cord
348
349 L->anim_pc = 0; // start here
350 L->cur_anim_type = __WALK_TO_STAND;
351 M->next_anim_type = __STAND;
352
353 return TRUE8;
354 }
355
356 // animate the route
357 if (Process_route()) {
358 // arrived
359 chi_do_mode = __THINKING;
360
361 // in-case last move pick a time to next move
362 chi_next_move = g_icb->getRandomSource()->getRandomNumber(200 - 1);
363 chi_next_move += 50;
364
365 // move on to next position
366 chi_history += 1;
367 if (chi_history == MAX_player_history)
368 chi_history = 0; // wrap
369
370 // cancel looping flag from the routing
371 L->looping = 0;
372
373 return TRUE8;
374 }
375 break;
376
377 case __CHASING:
378 // running after cord but on same floor
379 cord_dist = Cord_dist();
380
381 // animate the route
382 if ((Process_route()) || (chi_history != cur_history) ||
383 (cord_dist < (PXreal)(chi_catch_up_dist))) { // check for route done or player moved on in which case we quit the route and get following
384 // cancel looping flag from the routing
385 L->looping = 0;
386
387 if (chi_history == cur_history) {
388 // still on same floor - route has just finished naturally
389 // if we are within the zone then we come to a stand
390 if (cord_dist < (PXreal)(chi_lost_dist)) {
391 // within distance - so come to a stand
392 chi_do_mode = __ANIMATE_TO_THINK;
393 L->anim_pc = 0; // start here
394 L->cur_anim_type = __WALK_TO_STAND;
395 M->next_anim_type = __STAND;
396 chi_next_move = g_icb->getRandomSource()->getRandomNumber(200 - 1);
397 chi_next_move += 50;
398 return TRUE8;
399 }
400 }
401 // players gone or we're going to keep running
402 chi_do_mode = __THINKING;
403 chi_next_move = 1;
404
405 return FALSE8;
406 }
407 break;
408
409 case __BUMBLING:
410 // just ambling about the current room
411
412 cord_dist = Cord_dist();
413
414 // animate the route
415 if ((Process_route()) || ((chi_history != cur_history)) ||
416 (cord_dist < (PXreal)(chi_catch_up_dist / 2))) { // check for route done or player moved on in which case we quit the route and get following
417 // arrived
418 chi_do_mode = __THINKING;
419
420 // in-case last move pick a time to next move
421 chi_next_move = g_icb->getRandomSource()->getRandomNumber(200 - 1);
422 chi_next_move += 50;
423
424 // cancel looping flag from the routing
425 L->looping = 0;
426
427 if (chi_history == cur_history) {
428 // still on same floor - route has just finished naturally
429 // if we are within the zone then we come to a stand
430 if (cord_dist < (PXreal)(chi_catch_up_dist)) {
431 // within distance - so come to a stand
432 chi_do_mode = __ANIMATE_TO_THINK;
433 L->anim_pc = 0; // start here
434 L->cur_anim_type = __WALK_TO_STAND;
435 M->next_anim_type = __STAND;
436 return TRUE8;
437 }
438 }
439 return TRUE8;
440 }
441 break;
442
443 case __GO_CORD_GO:
444 // wait for player to moveout of a lift
445 sub1 = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.x - M->actor_xyz.x;
446 sub2 = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.z - M->actor_xyz.z;
447
448 // dist
449 if (((chi_history != cur_history)) || (((sub1 * sub1) + (sub2 * sub2)) > (PXreal)(100 * 100))) {
450 // players moved
451 Message_box("bye then");
452 chi_next_move = 1;
453 chi_do_mode = __THINKING;
454 }
455
456 return TRUE8;
457 break;
458
459 case __PAUSING:
460 break;
461
462 case __FIGHT_HELP: // help the player in a fight
463 // first see if players gone
464 if (chi_history != cur_history) {
465 Chi_leaves_fight_mode();
466 return TRUE8;
467 } else {
468 if (!fight_pause) {
469 // not got permission, can see player and player not armed - exit armed mode
470 if ((!permission_to_fire) && (!MS->logic_structs[player.Fetch_player_id()]->mega->Fetch_armed_status()) &&
471 (g_oLineOfSight->ObjectToObject(cur_id, player.Fetch_player_id(), LIGHT, 0, _line_of_sight::USE_OBJECT_VALUE, TRUE8))) {
472 // exit this mode
473 Chi_leaves_fight_mode();
474 return TRUE8;
475 }
476
477 // not on camera - then quit fight mode and catch up with player
478 if (!Object_visible_to_camera(chi_id)) {
479 // exit this mode
480 Chi_leaves_fight_mode();
481 return TRUE8;
482 }
483
484 // if cant see the player but would expect to (he's moved) then forget this and catch him up
485 if (!g_oLineOfSight->ObjectToObject(cur_id, player.Fetch_player_id(), LIGHT, 0, _line_of_sight::USE_OBJECT_VALUE, TRUE8)) {
486 // exit this mode
487 PXreal x2 = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.x;
488 PXreal z2 = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.z;
489 PXreal vect = PXAngleOfVector(z2 - M->actor_xyz.z, x2 - M->actor_xyz.x); // work out vector
490
491 if (PXfabs(L->pan - vect) < (FULL_TURN / 5)) {
492 Chi_leaves_fight_mode();
493 return TRUE8;
494 }
495 }
496
497 // got target but target is dead or not seeable - set no target
498 if (chi_has_target) {
499 if (logic_structs[chi_target_id]->mega->dead)
500 chi_has_target = 0; // dead so no target
501
502 if (!Object_visible_to_camera(chi_target_id))
503 chi_has_target = 0; // target is now off camera so forget it
504 }
505
506 // do something!
507 if ((chi_has_target) && (permission_to_fire)) {
508 // if right angle, shoot
509 // else turn to face
510
511 if (Need_to_turn_to_face_object(chi_target_id))
512 chi_do_mode = __TURN_TO_FACE_OBJECT;
513 else {
514 // shoot!
515
516 // play gun sound
517 if (MS->logic_structs[cur_id]->sfxVars[GUNSHOT_SFX_VAR] != 0)
518 RegisterSound(cur_id, NULL, MS->logic_structs[cur_id]->sfxVars[GUNSHOT_SFX_VAR], gunDesc,
519 (int8)127); // have to use full version so we can give hash instead of string
520 else
521 RegisterSound(cur_id, defaultGunSfx, gunDesc); // use small version as we have string not hash
522
523 // dynamic light
524 M->SetDynamicLight(1, 255, 255, 255, 0, 150, 100, 200);
525
526 // Hey we are shooting
527 M->is_shooting = TRUE8;
528
529 L->anim_pc = 0; // start here
530 L->cur_anim_type = __STAND_AND_SHOOT;
531 M->next_anim_type = __STAND;
532 I->IsAnimTable(L->cur_anim_type);
533 chi_do_mode = __ANIMATE_TO_FIGHT_HELP;
534 fight_pause = (uint8)(1 + g_icb->getRandomSource()->getRandomNumber(10 - 1));
535 Call_socket(chi_target_id, "gun_shot", &result);
536
537 return TRUE8;
538 }
539 } else if (chi_has_target) {
540 // cant shoot
541 chi_do_mode = __TURN_TO_FACE_OBJECT;
542 } else {
543 // no target
544 // try to find new target
545 // yes, then set to turn
546 // no, turn to player
547
548 if (!Find_a_chi_target()) {
549
550 if (!fast_face_rnd(2))
551 chi_do_mode = __TURN_RND;
552
553 // cancel permission
554 permission_to_fire = 0;
555 } else { // found a new target
556 chi_do_mode = __TURN_TO_FACE_OBJECT; // turns to face new target
557 }
558 }
559
560 fight_pause = (uint8)(6 + g_icb->getRandomSource()->getRandomNumber(10 - 1));
561 } else {
562 fight_pause--;
563 }
564 }
565 break;
566
567 case __THINKING:
568 switch (chi_think_mode) {
569 case __FOLLOWING:
570 if (M->reverse_route)
571 Message_box("chi in reverse");
572
573 if (chi_history != cur_history) {
574 // player must have moved on one or more rooms but we can be certain the rooms are properly adjacent
575
576 // get next room
577 next_room = chi_history + 1;
578 if (next_room == MAX_player_history)
579 next_room = 0; // wrap
580
581 // is next entry a new floor or an object to interact with
582 if (history[next_room].interaction == TRUE8) {
583 // players interacted with something - a floor, lift, stairway, etc.
584
585 if (L->looping) {
586 L->looping = 0;
587 }
588
589 Set_motion(__MOTION_RUN);
590
591 res = chi_interacts(history[next_room].id, "chi");
592 if (!res)
593 res = chi_interacts(history[next_room].id, "interact");
594
595 if (!res) { // no interaction portal for chi - actually not possible now she can use the normal
596 // 'interact' script
597 chi_think_mode = __LOST; // oh dear, we must be lost. Not legal but lets handle it for now
598 Tdebug("chi.txt", "chi cant follow player via %d", history[next_room].id);
599 return TRUE8;
600 } else {
601 // ok, back to script
602 // script interpretter shouldnt write a pc back
603
604 chi_do_mode = __INTERACT_FOLLOW;
605
606 return (IR_GOSUB);
607 }
608 } else { // players gone to an adjacent floor
609 x = history[next_room].first_x;
610 z = history[next_room].first_z;
611
612 // if player is running then chi should run
613 // if chi is more than one behind chi should run
614 bool8 run = TRUE8;
615 if (Cord_dist() < (400 * 400))
616 run = FALSE8; // walk if near
617
618 // 0 result
619 // 1 x
620 // 2 z
621 // 3 0='walk', else 'run'
622 // 4 0=no turn-on-spot 1=yes
623 // 5 end on stand
624 if (!Setup_route(result, (int32)x, (int32)z, run, __ENDB, TRUE8)) {
625 // route failed or was no route required which in theory cant happen - so we take it as
626 // route failed to build
627 Tdebug("chi.txt", " route failed");
628 Setup_route(result, (int32)x, (int32)z, 1, __LASER, TRUE8);
629 }
630
631 Tdebug("chi.txt", " route made");
632
633 chi_do_mode = __ROUTING;
634 return TRUE8;
635 }
636 } else {
637 // same room - unless we started incorrectly, which we'll notice when we move
638 // decide on something to do then?
639
640 if (!chi_next_move) {
641 // create a coordinate to route to
642 bool8 ret = Make_floor_coordinate(&x, &z);
643 if (!ret) {
644 chi_next_move = 36; // try again in a second
645 return TRUE8; // make another next cycle and try again
646 }
647
648 session_barriers->Set_route_barrier_mask((int32)x - 300, (int32)x + 300, (int32)z - 300, (int32)z + 300);
649 route_res = Setup_route(result, (int32)x, (int32)z, 0, __FULL, 1); // walk
650 session_barriers->Clear_route_barrier_mask();
651
652 if (!route_res) {
653 // route failed or was no route required which in theory cant happen - so we take it as
654 // route failed to build
655 Tdebug("chi.txt", " bumble route failed");
656
657 chi_next_move = 36; // try again in a second
658 return TRUE8; // make another next cycle and try again
659 } else {
660 if (M->reverse_route)
661 Message_box("chi is reversing! do it - do it now");
662 chi_do_mode = __BUMBLING;
663 return TRUE8;
664 }
665 } else {
666 chi_next_move--; // reduce time to next
667
668 // see if we should fight help
669 // either we just heard player gunshot or we can see him and he's armed
670 if ((Object_visible_to_camera(chi_id)) && (Cord_dist() < (PXreal)(chi_lost_dist)) &&
671 (logic_structs[player.Fetch_player_id()]->mega->actor_xyz.y == M->actor_xyz.y))
672 if ((permission_to_fire) || ((g_oLineOfSight->LineOfSight(cur_id, player.Fetch_player_id())) &&
673 (MS->logic_structs[player.Fetch_player_id()]->mega->Fetch_armed_status()))) {
674 // has he got gun out?
675 chi_do_mode = __GET_WEAPON_OUT;
676
677 if (!permission_to_fire)
678 fight_pause = 3; // shes seen player arming - wait a bit
679 else
680 fight_pause = 0; // heard a shot - arm straight away
681
682 permission_to_fire = 0; // when armed - must see player shoot someone before she will shoot
683
684 return TRUE8;
685 }
686
687 // move if player a distance away
688 if ((Cord_dist() > (PXreal)(chi_lost_dist)) && (logic_structs[player.Fetch_player_id()]->mega->actor_xyz.y == M->actor_xyz.y) &&
689 (prev_save_state)) {
690 // 0 result
691 // 1 x
692 // 2 z
693 // 3 0='walk', else 'run'
694 // 4 0=no turn-on-spot 1=yes
695 // 5 end on stand
696 x = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.x;
697 z = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.z;
698
699 bool8 run = TRUE8;
700 bool8 eos = FALSE8;
701 if (Cord_dist() < (400 * 400)) {
702 run = FALSE8; // walk if near
703 eos = TRUE8; // end on stand
704 }
705
706 // set a barrier mask :(
707 session_barriers->Set_route_barrier_mask((int32)x - 450, (int32)x + 450, (int32)z - 450, (int32)z + 450);
708 route_res = Setup_route(result, (int32)x, (int32)z, run, __FULL, eos);
709 session_barriers->Clear_route_barrier_mask();
710
711 if (!route_res) {
712 // route failed or was no route required which in theory cant happen - so we take it
713 // as route failed to build
714 if (result == FALSE8)
715 Setup_route(result, (int32)x, (int32)z, run, __LASER, eos);
716 else
717 return TRUE8; // make another next cycle and try again
718 } else {
719 Tdebug("chi.txt", " route made");
720 if (M->reverse_route)
721 Message_box("chi is reversing! do it - do it now");
722 chi_do_mode = __CHASING;
723 return FALSE8;
724 }
725 }
726
727 // cancel permission - so we catch player gunshots immediately
728 permission_to_fire = 0;
729 }
730 }
731 break;
732
733 case __LOST:
734 // we're lost - but if the player turns up then we can restart
735 if (logic_structs[player.Fetch_player_id()]->owner_floor_rect == L->owner_floor_rect) {
736 chi_history = cur_history; // chi gets same point as cord
737 chi_think_mode = __FOLLOWING; // set mode
738 Tdebug("chi.txt", "chi is finds cord again - chi=%d, player=%d", chi_history, cur_history);
739 }
740 /* fall through */
741
742 case __NOTHING:
743 // dummy mode for when switched to custom logics
744
745 L->cur_anim_type = __STAND;
746 L->anim_pc = 0;
747
748 return (TRUE8);
749
750 break;
751
752 default:
753 Fatal_error("ilegal chi mode");
754 break;
755 }
756 break;
757 }
758
759 return (TRUE8);
760 }
761
Cord_dist()762 PXreal _game_session::Cord_dist() {
763 // return cords distance from chi (or other mega depending upon where called)
764 PXreal sub1,
765 sub2;
766
767 sub1 = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.x - M->actor_xyz.x;
768 sub2 = logic_structs[player.Fetch_player_id()]->mega->actor_xyz.z - M->actor_xyz.z;
769
770 // dist
771 return ((sub1 * sub1) + (sub2 * sub2));
772 }
773
fn_check_for_chi(int32 & result,int32 *)774 mcodeFunctionReturnCodes _game_session::fn_check_for_chi(int32 &result, int32 *) {
775 // player wants to go down a lift so we must check to see if we need to tell chi to come over
776
777 // returns 0 no need to wait for chi
778 // 1 need to wait for chi
779
780 // default to no
781 result = 0;
782
783 // check there is a following chi
784 if (!is_there_a_chi)
785 return IR_CONT;
786
787 // not if held
788 if (logic_structs[chi_id]->ob_status == OB_STATUS_HELD)
789 return IR_CONT;
790
791 if (g_mission->chi_following) {
792 if (logic_structs[chi_id]->mega->dead) {
793 // chi is dead
794 result = 0;
795 return IR_CONT;
796 }
797
798 result = 1;
799 return IR_CONT;
800 // chi's got to come over and onto the platform
801 }
802
803 // not in follow mode
804 return IR_CONT;
805 }
806
fn_wait_for_chi(int32 &,int32 *)807 mcodeFunctionReturnCodes _game_session::fn_wait_for_chi(int32 &, int32 *) {
808 // waits for chi to register as having arrived
809 // this happens when she resets her following variables
810
811 if (chi_history == cur_history) {
812 // she's arrived
813 return IR_CONT;
814 }
815
816 return IR_REPEAT;
817 }
818
fn_chi_wait_for_player_to_move(int32 &,int32 *)819 mcodeFunctionReturnCodes _game_session::fn_chi_wait_for_player_to_move(int32 &, int32 *) {
820 // set to go-cord-go wait mode
821 // used after lift movement
822
823 chi_history = cur_history; // chi gets same point as cord
824
825 return IR_CONT;
826 }
827
fn_register_chi(int32 &,int32 *)828 mcodeFunctionReturnCodes _game_session::fn_register_chi(int32 &, int32 *) {
829 // tell engine there is a chi object
830
831 if (is_there_a_chi)
832 Fatal_error("double call to fn_register_chi");
833
834 Tdebug("chi.txt", "%s registers as chi", object->GetName());
835
836 is_there_a_chi = TRUE8;
837 chi_id = cur_id;
838
839 return IR_CONT;
840 }
841
Make_floor_coordinate(PXreal * x,PXreal * z)842 bool8 _game_session::Make_floor_coordinate(PXreal *x, PXreal *z) {
843 // make a coordinate for the floor
844
845 if (!local_history_count)
846 return FALSE8;
847
848 int32 choice = g_icb->getRandomSource()->getRandomNumber(local_history_count - 1);
849
850 *x = local_history[choice].x;
851 *z = local_history[choice].z;
852
853 return TRUE8;
854 }
855
856 } // End of namespace ICB
857