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/debug.h"
29 #include "engines/icb/mission.h"
30 #include "engines/icb/global_objects.h"
31 #include "engines/icb/common/ptr_util.h"
32 #include "engines/icb/bone.h"
33 #include "engines/icb/sound.h"
34 #include "engines/icb/icb.h"
35
36 #include "common/textconsole.h"
37
38 namespace ICB {
39
40 #define NECK_PERCENT 16
41 #define NECK_RANGE 96
42 #define NECK_SPEED 8
43
44 #define JAW_PERCENT 40
45 #define JAW_MAX 256
46 #define JAW_SPEED 32
47
48 #define STANDARD_LOOK_SPEED 128
49
50 // useful function to cap a short value at min-max range
LimitShort(short & v,short min,short max)51 void LimitShort(short &v, short min, short max) {
52 if (v < min)
53 v = min;
54 else if (v > max)
55 v = max;
56 }
57
58 #define BULLET_COL 200
59 #define BULLET_DC -8
60
61 #define BULLET_G -8 // dy+=g
62 #define BULLET_DX 4 // initial dy
63 #define BULLET_DY 16 // initial dy
64
InitCartridgeCase(SVECTOR * initPos,short initialHeight)65 void _mega::InitCartridgeCase(SVECTOR *initPos, short initialHeight) {
66 bulletInitialHeight = initialHeight;
67 bulletOn = TRUE8;
68 bulletPos = *initPos;
69 bulletDX = BULLET_DX;
70 bulletDY = BULLET_DY;
71 bulletColour = (uint8)BULLET_COL;
72 bulletBounced = (uint8)0;
73 }
74
UpdateCartridgeCase()75 void _game_session::UpdateCartridgeCase() {
76 if (M->bulletOn) {
77 // gravity acceleration
78 M->bulletDY = (int16)(M->bulletDY + BULLET_G);
79
80 // movement
81 M->bulletPos.vx = (int16)(M->bulletPos.vx + M->bulletDX);
82 M->bulletPos.vy = (int16)(M->bulletPos.vy + M->bulletDY);
83
84 // only reduce colour if >0
85 if (M->bulletColour)
86 M->bulletColour = (uint8)(M->bulletColour + BULLET_DC);
87
88 if ((M->bulletPos.vy < -M->bulletInitialHeight) && (M->bulletBounced >= 1)) { // we are at ground height and have already bounced enough (1) times, turn us off
89 M->bulletOn = FALSE8;
90 } else if (M->bulletPos.vy < -M->bulletInitialHeight) { // otherwise if we are at floor height then bounce us back up...
91 M->bulletPos.vy = (int16)(-M->bulletInitialHeight); // clip
92 M->bulletDY = (int16)(-M->bulletDY / 2);
93 M->bulletDX = (int16)(M->bulletDX / 2);
94 M->bulletBounced++;
95
96 // this is where we make the bouncing sound...
97 RegisterSound(cur_id, object->GetStringValueOrDefault(tinkleSfxVar, defaultTinkleSfx), tinkleDesc);
98 }
99 }
100 }
101
102 // update talking character log
103 // using there rap file to get bones
104 // for jaw and neck. This sets random
105 // movement on neck (x,y,z) and random
106 // movement for jaw (just in x)
UpdateTalking(_logic * log,rap_API * rap)107 void UpdateTalking(_logic *log, rap_API *rap) {
108 // check for -1 in rap
109 if (rap->jawBone == (int8)-1)
110 Tdebug("bones.txt", "mega %s speaking but has no jaw bone!", log->mega->chr_name);
111 if (rap->neckBone == (int8)-1)
112 Tdebug("bones.txt", "mega %s speaking but has no neck bone!", log->mega->chr_name);
113
114 // set deformations....
115 BoneDeformation *neckBone = &(log->voxel_info->neckBone);
116 BoneDeformation *jawBone = &(log->voxel_info->jawBone);
117
118 // set speed
119 neckBone->boneSpeed = NECK_SPEED;
120 jawBone->boneSpeed = JAW_SPEED;
121
122 // set number of bone from rap file
123 jawBone->boneNumber = (int16)(rap->jawBone);
124 neckBone->boneNumber = (int16)(rap->neckBone);
125
126 // unless it's -1 (no bone) we want the bone above this...
127 if (neckBone->boneNumber != (int16)-1) {
128 neckBone->boneNumber++; // add one to the value
129 }
130
131 // random neck movement (all three planes)
132 if ((g_icb->getRandomSource()->getRandomNumber(100 - 1)) < NECK_PERCENT) {
133 neckBone->boneTarget.vx = (int16)((g_icb->getRandomSource()->getRandomNumber(2 * NECK_RANGE - 1)) - NECK_RANGE);
134 neckBone->boneTarget.vz = (int16)((g_icb->getRandomSource()->getRandomNumber(2 * NECK_RANGE - 1)) - NECK_RANGE);
135 neckBone->boneTarget.vy = (int16)((g_icb->getRandomSource()->getRandomNumber(2 * NECK_RANGE - 1)) - NECK_RANGE);
136 }
137
138 // random jaw movement (just the v moves)
139 if ((g_icb->getRandomSource()->getRandomNumber(100 - 1)) < JAW_PERCENT) {
140 jawBone->boneTarget.vx = (int16)(g_icb->getRandomSource()->getRandomNumber(JAW_MAX - 1));
141 jawBone->boneTarget.vz = (int16)0;
142 jawBone->boneTarget.vy = (int16)0;
143 }
144 }
145
146 // player was shot by obj
147 // needs some work to get right
SetPlayerShotBone(int32 obj_id)148 void SetPlayerShotBone(int32 obj_id) {
149 _logic *player_log = MS->player.log;
150 _logic *obj_log = MS->logic_structs[obj_id];
151
152 PXfloat px, py, pz, ox, oy, oz;
153
154 // get locations (only really interested in x and z
155 player_log->GetPosition(px, py, pz);
156 obj_log->GetPosition(ox, oy, oz);
157
158 PXfloat dx, dz;
159
160 dx = ox - px;
161 dz = oz - pz;
162
163 // get player facing direction...
164
165 PXfloat player_pan;
166
167 if (MS->player.log->auto_panning == FALSE8)
168 player_pan = MS->player.log->pan;
169 else
170 player_pan = MS->player.log->auto_display_pan;
171
172 int32 direction;
173
174 // get direction got shot from...
175
176 direction = (int32)(4096.0 * (PXAngleOfVector(-dz, -dx) - player_pan));
177
178 // make sure it is -2048 - 2048
179 if (direction > 2047)
180 direction -= 4096;
181
182 if (direction < -2048)
183 direction += 4096;
184
185 // bone number 2 set to (512,0,512)
186
187 if (abs(direction) < 1024) // shot from somewhere behind...
188 MS->player.shotDeformation.boneValue.vx = 256; // front
189 else
190 MS->player.shotDeformation.boneValue.vx = -256; // back
191
192 if ((g_icb->getRandomSource()->getRandomNumber(100 - 1)) < 50) { // 50% chance of going either way left or right
193 MS->player.shotDeformation.boneValue.vy = 32; // turn a bit just for variation
194 MS->player.shotDeformation.boneValue.vz = 32; // turn a bit just for variation
195 } else {
196 MS->player.shotDeformation.boneValue.vy = -32; // turn
197 MS->player.shotDeformation.boneValue.vz = -32; // turn a bit just for variation
198 }
199
200 MS->player.shotDeformation.Target0();
201
202 MS->player.shotDeformation.boneNumber = 1;
203 MS->player.shotDeformation.boneSpeed = 128;
204 }
205
206 #define STATUS_NONE 0
207 #define STATUS_BODY 1
208 #define STATUS_HEAD 2
209
210 #define PLAYER_STOOD_EYE 180
211 #define PLAYER_CROUCH_EYE 65
212
213 #define MEGA_STOOD_EYE 170
214 #define MEGA_CROUCH_EYE 55
215 #define MEGA_DEAD_EYE 0
216 #define ROBOT_EYE 40
217
218 #define STANDARD_MARKER_HEIGHT 170
219
220 // update the neck bone
221 // should only be called with player as logic
UpdatePlayerLook()222 void UpdatePlayerLook() {
223 static int32 status = STATUS_NONE;
224
225 _logic *log = MS->player.log;
226 BoneDeformation *b = &(log->voxel_info->lookBone);
227
228 // start off with manually set values
229 b->boneTarget = log->voxel_info->scriptedLookBoneTarget;
230
231 // interact object, or look at object if we don't have an interact one
232 bool8 has_interact = MS->player.interact_selected;
233 uint32 sel_id = MS->player.cur_interact_id;
234
235 // we have no interact so try getting a look at...
236 if (!has_interact) {
237 has_interact = MS->player.look_at_selected;
238 sel_id = MS->player.look_at_id;
239 }
240
241 // if we have a specific look set then we set bone number to 23
242 if ((b->boneTarget.vx != 0) || (b->boneTarget.vy != 0) || (b->boneTarget.vz != 0)) {
243 status = STATUS_NONE;
244 b->boneNumber = 23;
245 b->boneSpeed = STANDARD_LOOK_SPEED;
246 }
247
248 // we only override these things if we do not have a bone target set (ie bone target is <0,0,0>)
249 // also we only do this if the player is looking at something... (otherwise the targets are <0,0,0>
250 // also we only do this when not playing a custom anim...
251 if ((b->boneTarget.vx == 0) && (b->boneTarget.vy == 0) && (b->boneTarget.vz == 0) && // bone override not set
252 (has_interact) && // are looking at something
253 (log->cur_anim_type != __PROMOTED_NON_GENERIC) && // we are not on a special non-generic (custom)
254 (log->cur_anim_type != __NON_GENERIC) // we are not on a non-generic (custom)
255 ) {
256 _logic *target;
257 PXfloat player_pan;
258 PXreal ox, oy, oz, px, py, pz;
259 PXreal dx, dy, dz;
260 int32 playerEye;
261
262 // get postion of players head
263
264 // raw position (foot)
265 log->GetPosition(px, py, pz);
266
267 if (log->mega->Is_crouched())
268 playerEye = PLAYER_CROUCH_EYE; // crouch
269 else
270 playerEye = PLAYER_STOOD_EYE; // stand
271
272 // height increases to eye...
273 py += playerEye;
274
275 // get position of targetting object
276
277 target = MS->logic_structs[sel_id];
278
279 // raw position
280 target->GetPosition(ox, oy, oz);
281
282 // target is an actor so need adjustment for eye height...
283 if (target->image_type == VOXEL) {
284 c_game_object *pGameObject = (c_game_object *)MS->objects->Fetch_item_by_number(sel_id);
285 int32 dead = pGameObject->GetIntegerVariable(pGameObject->GetVariable("state"));
286
287 if (target->object_type == __NON_ORGANIC_MEGA) { // robot (look down)
288 oy += ROBOT_EYE;
289 } else if (dead) { // dead
290 oy += MEGA_DEAD_EYE;
291 } else if (target->mega->Is_crouched()) { // crouch
292 oy += MEGA_CROUCH_EYE;
293 } else {
294 oy += MEGA_STOOD_EYE; // standing
295 }
296
297 // difference in x,y,z
298 dx = (px - ox);
299 dy = (py - oy);
300 dz = (pz - oz);
301
302 }
303 // is an interact object, check for height.
304 else {
305 int32 height = STANDARD_MARKER_HEIGHT; // standard prop height
306
307 // if look_height set for this marker set
308 if (target->look_height != -1)
309 height = target->look_height;
310
311 dx = (px - ox);
312 // difference between player eye and marker height
313 dy = (PXreal)(playerEye - height);
314 dz = (pz - oz);
315 }
316
317 // get player pan (for calculating how far round the head moves)
318
319 if (MS->player.log->auto_panning == FALSE8)
320 player_pan = MS->player.log->pan;
321 else
322 player_pan = MS->player.log->auto_display_pan;
323
324 // Now find angles for neck bone...
325
326 b->boneTarget.vz = (int16)(4096.0 * (PXAngleOfVector(-dz, -dx) - player_pan));
327 b->boneTarget.vx = (int16)(4096.0 * PXAngleOfVector((PXfloat)PXsqrt(dx * dx + dz * dz), dy));
328
329 // make sure vz is in range -2048 - 2048... this might not be because of subtracting off player_pan
330
331 while (b->boneTarget.vz > 2048)
332 b->boneTarget.vz -= 4096;
333
334 while (b->boneTarget.vz < -2048)
335 b->boneTarget.vz += 4096;
336
337 if (b->boneTarget.vz > 1024)
338 b->boneTarget.vz = 1024;
339 if (b->boneTarget.vz < -1024)
340 b->boneTarget.vz = -1024;
341
342 // armed unarmed
343
344 int32 armed = MS->player.log->mega->Fetch_armed_status();
345
346 // from NONE to a status
347
348 // if status is none and we are armed and we have returned to central then start looking with upper body...
349 if ((status == STATUS_NONE) && (armed) && (b->boneValue.vz == 0) && (b->boneValue.vx == 0))
350 status = STATUS_BODY;
351
352 // if status is none and we are not armed and we have returned to central then start looking with head...
353 if ((status == STATUS_NONE) && (!armed) && (b->boneValue.vz == 0) && (b->boneValue.vx == 0))
354 status = STATUS_HEAD;
355
356 // from wrong status to NONE
357
358 // if aiming with body but not armed then status is none (in prep of going to status_body)
359 if ((status == STATUS_BODY) && (!armed))
360 status = STATUS_NONE;
361
362 // if looking with head but armed then status is none (in preperation for going to STATUS_HEAD)
363 if ((status == STATUS_HEAD) && (armed))
364 status = STATUS_NONE;
365
366 // now what to do in each status...
367
368 // no status, target nothing...
369 if (status == STATUS_NONE) {
370 b->boneTarget.vx = 0;
371 b->boneTarget.vy = 0;
372 b->boneTarget.vz = 0;
373 b->boneSpeed = STANDARD_LOOK_SPEED * 2;
374 }
375 // look with head...
376 else if (status == STATUS_HEAD) {
377 // limit pan and pitch
378 LimitShort(b->boneTarget.vz, -512, 384);
379 LimitShort(b->boneTarget.vx, -256, 256);
380
381 // for turning it looks better if you look up slightly (vx=vx-abs(vz)/2)
382 b->boneTarget.vx = (int16)(b->boneTarget.vx - (abs(b->boneTarget.vz) / 3));
383
384 // we need to set the speed to be STANDARD_LOOK_SPEED
385 b->boneSpeed = STANDARD_LOOK_SPEED;
386
387 // should not be hard coded, neck bone
388 b->boneNumber = 23;
389 }
390 // look with body...
391 else if (status == STATUS_BODY) {
392 // no bend when aimed
393 b->boneTarget.vy = 0;
394
395 // limit pitch (pan can be any value so gun is always pointing at target...)
396 LimitShort(b->boneTarget.vx, -256, 256);
397
398 b->boneTarget.vy = (int16)((b->boneTarget.vx * b->boneTarget.vz) / 1024);
399
400 // we need to set the speed to be STANDARD_LOOK_SPEED
401 b->boneSpeed = STANDARD_LOOK_SPEED * 2;
402
403 // should not be hard coded, body bone
404 b->boneNumber = 1;
405 }
406
407 } else {
408 // if still in body mode and we have gone back to straight then set mode to none and bone goes to 23 (neck) for specific
409 // looks...
410 if ((b->boneTarget.vx == 0) && (b->boneValue.vz == 0) && (status == STATUS_BODY)) {
411 status = STATUS_NONE;
412 b->boneNumber = 23;
413 b->boneSpeed = STANDARD_LOOK_SPEED;
414 }
415 }
416
417 // dont do an update here...
418 }
419
fn_set_neck_bone(int32 & result,int32 * params)420 mcodeFunctionReturnCodes fn_set_neck_bone(int32 &result, int32 *params) { return (MS->fn_set_neck_bone(result, params)); }
421
fn_set_neck_vector(int32 & result,int32 * params)422 mcodeFunctionReturnCodes fn_set_neck_vector(int32 &result, int32 *params) { return (MS->fn_set_neck_vector(result, params)); }
423
speak_set_neck_vector(int32 & result,int32 * params)424 mcodeFunctionReturnCodes speak_set_neck_vector(int32 &result, int32 *params) { return (MS->speak_set_neck_vector(result, params)); }
425
fn_simple_look(int32 & result,int32 * params)426 mcodeFunctionReturnCodes fn_simple_look(int32 &result, int32 *params) { return (MS->fn_simple_look(result, params)); }
427
speak_simple_look(int32 & result,int32 * params)428 mcodeFunctionReturnCodes speak_simple_look(int32 &result, int32 *params) { return (MS->speak_simple_look(result, params)); }
429
430 // the array of standard look coords
431
432 #define LOOK_RIGHT (int16)(-384)
433 #define LOOK_UP (int16)(-196)
434
435 #define LOOK_LEFT (int16)(-LOOK_RIGHT)
436 #define LOOK_DOWN (int16)(-LOOK_UP)
437
438 const short looks[9][3] = {
439 {0, 0, 0}, // ahead
440 {0, 0, LOOK_UP}, // up
441 {LOOK_RIGHT, 0, LOOK_UP}, // up/right
442 {LOOK_RIGHT, 0, 0}, // right
443 {LOOK_RIGHT, 0, LOOK_DOWN}, // down/right
444 {0, 0, LOOK_DOWN}, // down
445 {LOOK_LEFT, 0, LOOK_DOWN}, // down/left
446 {LOOK_LEFT, 0, 0}, // left
447 {LOOK_LEFT, 0, LOOK_UP} // up/left
448 };
449
450 // look
451 // 0 - Ahead
452 // 1 - Up
453 // 2 - Up/Right
454 // 3 - Right
455 // 4 - Right/Down
456 // 5 - Down
457 // 6 - Down/Left
458 // 7 - Left
459 // 8 - Left/Up
460
461 // simple look
fn_simple_look(int32 &,int32 * params)462 mcodeFunctionReturnCodes _game_session::fn_simple_look(int32 &, int32 *params) {
463 int32 l = params[0]; // which direction
464
465 if (!logic_structs[cur_id]->mega)
466 Fatal_error("fn_set_neck_vector called by non mega %s", L->GetName());
467
468 int32 callingParams[2];
469
470 callingParams[0] = MemoryUtil::encodePtr((uint8 *)const_cast<char *>(L->GetName()));
471 callingParams[1] = l;
472
473 int32 ret;
474 return speak_simple_look(ret, callingParams);
475 }
476
477 // simple look from speech scripts...
478
speak_simple_look(int32 &,int32 * params)479 mcodeFunctionReturnCodes _game_session::speak_simple_look(int32 &, int32 *params) {
480 const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
481
482 // object
483 int32 object_id = objects->Fetch_item_number_by_name(object_name);
484
485 // direction
486 int32 l = params[1];
487
488 if (!logic_structs[object_id]->mega)
489 Fatal_error("fn_set_neck_vector called by non mega %s", logic_structs[object_id]->GetName());
490
491 if (logic_structs[object_id]->voxel_info->lookBone.boneNumber == (int8)-1)
492 Fatal_error("fn_set_neck_vector called but no fn_set_neck_bone() has been called for object %s", logic_structs[object_id]->GetName());
493
494 Tdebug("bones.txt", "%s: Simple look %d <%d,%d,%d> at speed %d", object_name, l, looks[l][2], looks[l][1], looks[l][0], STANDARD_LOOK_SPEED);
495
496 // sorry looks is wrong way round! (z,y,x)
497 logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vx = looks[l][2];
498 logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vy = looks[l][1];
499 logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vz = looks[l][0];
500 logic_structs[object_id]->voxel_info->lookBone.boneSpeed = STANDARD_LOOK_SPEED;
501
502 warning("doing a look direction: %d bone: %d", l, logic_structs[object_id]->voxel_info->lookBone.boneNumber);
503
504 return IR_CONT;
505 }
506
507 // set neck bone of current object
fn_set_neck_bone(int32 &,int32 * params)508 mcodeFunctionReturnCodes _game_session::fn_set_neck_bone(int32 &, int32 *params) {
509 int32 bone = params[0];
510
511 if (!logic_structs[cur_id]->mega)
512 Fatal_error("fn_set_neck_bone called by non mega %s", L->GetName());
513
514 Tdebug("bones.txt", "%s: Neck bone is %d", L->GetName(), bone);
515
516 logic_structs[cur_id]->voxel_info->lookBone.boneNumber = (int8)bone;
517 logic_structs[cur_id]->voxel_info->neckBone.boneNumber = (int8)bone;
518
519 return IR_CONT;
520 }
521
522 // set neck vector
523 // params
524 // 0 - x
525 // 1 - y
526 // 2 - z
527 // 3 - speed
528 // equiverlant to speech_set_neck_vector(NAME,x,y,z,speed)
529 // where NAME is object name...
530 //
fn_set_neck_vector(int32 &,int32 * params)531 mcodeFunctionReturnCodes _game_session::fn_set_neck_vector(int32 &, int32 *params) {
532 int32 x, y, z, speed;
533
534 x = params[0];
535 y = params[1];
536 z = params[2];
537 speed = params[3];
538
539 if (!logic_structs[cur_id]->mega)
540 Fatal_error("fn_set_neck_vector called by non mega %s", L->GetName());
541
542 int32 callingParams[5];
543
544 callingParams[0] = MemoryUtil::encodePtr((uint8 *)const_cast<char *>(L->GetName()));
545 callingParams[1] = x;
546 callingParams[2] = y;
547 callingParams[3] = z;
548 callingParams[4] = speed;
549
550 int32 ret;
551 return speak_set_neck_vector(ret, callingParams);
552 }
553
554 // set neck vector
555 // params
556 // 0 - "object"
557 // 1 - x
558 // 2 - y
559 // 3 - z
560 // 4 - speed
561 //
speak_set_neck_vector(int32 &,int32 * params)562 mcodeFunctionReturnCodes _game_session::speak_set_neck_vector(int32 &, int32 *params) {
563 int32 object_id;
564 int32 x, y, z, speed;
565
566 const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
567
568 object_id = objects->Fetch_item_number_by_name(object_name);
569 x = params[1];
570 y = params[2];
571 z = params[3];
572 speed = params[4];
573
574 if (L == player.log) {
575 warning("player set neck vector...");
576 logic_structs[object_id]->voxel_info->lookBone.boneNumber = 23;
577 }
578
579 if (!logic_structs[object_id]->mega)
580 Fatal_error("fn_set_neck_vector called by non mega %s", logic_structs[object_id]->GetName());
581
582 if (logic_structs[object_id]->voxel_info->lookBone.boneNumber == (int8)-1)
583 Fatal_error("fn_set_neck_vector called but no fn_set_neck_bone() has been called for object %s", logic_structs[object_id]->GetName());
584
585 Tdebug("bones.txt", "%s: Setting bone <%d,%d,%d> at speed %d", object_name, x, y, z, speed);
586
587 logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vx = (int16)x;
588 logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vy = (int16)y;
589 logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vz = (int16)z;
590 logic_structs[object_id]->voxel_info->lookBone.boneSpeed = (int16)speed;
591
592 return IR_CONT;
593 }
594
595 } // End of namespace ICB
596