scummvm/engines/icb/bone.cpp

614 lines
19 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/icb/debug.h"
#include "engines/icb/mission.h"
#include "engines/icb/global_objects.h"
#include "engines/icb/common/ptr_util.h"
#include "engines/icb/bone.h"
#include "engines/icb/sound.h"
#include "engines/icb/icb.h"
#include "common/textconsole.h"
namespace ICB {
#define NECK_PERCENT 16
#define NECK_RANGE 96
#define NECK_SPEED 8
#define JAW_PERCENT 40
#define JAW_MAX 256
#define JAW_SPEED 32
#define STANDARD_LOOK_SPEED 128
// useful function to cap a short value at min-max range
void LimitShort(short &v, short min, short max) {
if (v < min)
v = min;
else if (v > max)
v = max;
}
#define BULLET_COL 200
#define BULLET_DC -8
#define BULLET_G -8 // dy+=g
#define BULLET_DX 4 // initial dy
#define BULLET_DY 16 // initial dy
void _mega::InitCartridgeCase(SVECTOR *initPos, short initialHeight) {
bulletInitialHeight = initialHeight;
bulletOn = TRUE8;
bulletPos = *initPos;
bulletDX = BULLET_DX;
bulletDY = BULLET_DY;
bulletColour = (uint8)BULLET_COL;
bulletBounced = (uint8)0;
}
void _game_session::UpdateCartridgeCase() {
if (M->bulletOn) {
// gravity acceleration
M->bulletDY = (int16)(M->bulletDY + BULLET_G);
// movement
M->bulletPos.vx = (int16)(M->bulletPos.vx + M->bulletDX);
M->bulletPos.vy = (int16)(M->bulletPos.vy + M->bulletDY);
// only reduce colour if >0
if (M->bulletColour)
M->bulletColour = (uint8)(M->bulletColour + BULLET_DC);
if ((M->bulletPos.vy < -M->bulletInitialHeight) && (M->bulletBounced >= 1)) { // we are at ground height and have already bounced enough (1) times, turn us off
M->bulletOn = FALSE8;
} else if (M->bulletPos.vy < -M->bulletInitialHeight) { // otherwise if we are at floor height then bounce us back up...
M->bulletPos.vy = (int16)(-M->bulletInitialHeight); // clip
M->bulletDY = (int16)(-M->bulletDY / 2);
M->bulletDX = (int16)(M->bulletDX / 2);
M->bulletBounced++;
// this is where we make the bouncing sound...
RegisterSound(cur_id, CGameObject::GetStringValueOrDefault(object, tinkleSfxVar, defaultTinkleSfx), tinkleDesc);
}
}
}
// update talking character log
// using there rap file to get bones
// for jaw and neck. This sets random
// movement on neck (x,y,z) and random
// movement for jaw (just in x)
void UpdateTalking(_logic *log, RapAPI *rap) {
// check for -1 in rap
if (rap->jawBone == (int8)-1)
Tdebug("bones.txt", "mega %s speaking but has no jaw bone!", log->mega->chr_name);
if (rap->neckBone == (int8)-1)
Tdebug("bones.txt", "mega %s speaking but has no neck bone!", log->mega->chr_name);
// set deformations....
BoneDeformation *neckBone = &(log->voxel_info->neckBone);
BoneDeformation *jawBone = &(log->voxel_info->jawBone);
// set speed
neckBone->boneSpeed = NECK_SPEED;
jawBone->boneSpeed = JAW_SPEED;
// set number of bone from rap file
jawBone->boneNumber = (int16)(rap->jawBone);
neckBone->boneNumber = (int16)(rap->neckBone);
// unless it's -1 (no bone) we want the bone above this...
if (neckBone->boneNumber != (int16)-1) {
neckBone->boneNumber++; // add one to the value
}
// random neck movement (all three planes)
if ((g_icb->getRandomSource()->getRandomNumber(100 - 1)) < NECK_PERCENT) {
neckBone->boneTarget.vx = (int16)((g_icb->getRandomSource()->getRandomNumber(2 * NECK_RANGE - 1)) - NECK_RANGE);
neckBone->boneTarget.vz = (int16)((g_icb->getRandomSource()->getRandomNumber(2 * NECK_RANGE - 1)) - NECK_RANGE);
neckBone->boneTarget.vy = (int16)((g_icb->getRandomSource()->getRandomNumber(2 * NECK_RANGE - 1)) - NECK_RANGE);
}
// random jaw movement (just the v moves)
if ((g_icb->getRandomSource()->getRandomNumber(100 - 1)) < JAW_PERCENT) {
jawBone->boneTarget.vx = (int16)(g_icb->getRandomSource()->getRandomNumber(JAW_MAX - 1));
jawBone->boneTarget.vz = (int16)0;
jawBone->boneTarget.vy = (int16)0;
}
}
// player was shot by obj
// needs some work to get right
void SetPlayerShotBone(int32 obj_id) {
_logic *player_log = MS->player.log;
_logic *obj_log = MS->logic_structs[obj_id];
PXfloat px, py, pz, ox, oy, oz;
// get locations (only really interested in x and z
player_log->GetPosition(px, py, pz);
obj_log->GetPosition(ox, oy, oz);
PXfloat dx, dz;
dx = ox - px;
dz = oz - pz;
// get player facing direction...
PXfloat player_pan;
if (MS->player.log->auto_panning == FALSE8)
player_pan = MS->player.log->pan;
else
player_pan = MS->player.log->auto_display_pan;
int32 direction;
// get direction got shot from...
direction = (int32)(4096.0 * (PXAngleOfVector(-dz, -dx) - player_pan));
// make sure it is -2048 - 2048
if (direction > 2047)
direction -= 4096;
if (direction < -2048)
direction += 4096;
// bone number 2 set to (512,0,512)
if (abs(direction) < 1024) // shot from somewhere behind...
MS->player.shotDeformation.boneValue.vx = 256; // front
else
MS->player.shotDeformation.boneValue.vx = -256; // back
if ((g_icb->getRandomSource()->getRandomNumber(100 - 1)) < 50) { // 50% chance of going either way left or right
MS->player.shotDeformation.boneValue.vy = 32; // turn a bit just for variation
MS->player.shotDeformation.boneValue.vz = 32; // turn a bit just for variation
} else {
MS->player.shotDeformation.boneValue.vy = -32; // turn
MS->player.shotDeformation.boneValue.vz = -32; // turn a bit just for variation
}
MS->player.shotDeformation.Target0();
MS->player.shotDeformation.boneNumber = 1;
MS->player.shotDeformation.boneSpeed = 128;
}
#define STATUS_NONE 0
#define STATUS_BODY 1
#define STATUS_HEAD 2
#define PLAYER_STOOD_EYE 180
#define PLAYER_CROUCH_EYE 65
#define MEGA_STOOD_EYE 170
#define MEGA_CROUCH_EYE 55
#define MEGA_DEAD_EYE 0
#define ROBOT_EYE 40
#define STANDARD_MARKER_HEIGHT 170
void ResetPlayerLook() {
_logic *log = MS->player.log;
BoneDeformation *b = &(log->voxel_info->lookBone);
b->boneTarget.vx = b->boneTarget.vy = b->boneTarget.vz = 0;
}
// update the neck bone
// should only be called with player as logic
void UpdatePlayerLook() {
static int32 status = STATUS_NONE;
_logic *log = MS->player.log;
BoneDeformation *b = &(log->voxel_info->lookBone);
// start off with manually set values
b->boneTarget = log->voxel_info->scriptedLookBoneTarget;
// interact object, or look at object if we don't have an interact one
bool8 has_interact = MS->player.interact_selected;
uint32 sel_id = MS->player.cur_interact_id;
// we have no interact so try getting a look at...
if (!has_interact) {
has_interact = MS->player.look_at_selected;
sel_id = MS->player.look_at_id;
}
// if we have a specific look set then we set bone number to 23
if ((b->boneTarget.vx != 0) || (b->boneTarget.vy != 0) || (b->boneTarget.vz != 0)) {
status = STATUS_NONE;
b->boneNumber = 23;
b->boneSpeed = STANDARD_LOOK_SPEED;
}
// we only override these things if we do not have a bone target set (ie bone target is <0,0,0>)
// also we only do this if the player is looking at something... (otherwise the targets are <0,0,0>
// also we only do this when not playing a custom anim...
if ((b->boneTarget.vx == 0) && (b->boneTarget.vy == 0) && (b->boneTarget.vz == 0) && // bone override not set
(has_interact) && // are looking at something
(log->cur_anim_type != __PROMOTED_NON_GENERIC) && // we are not on a special non-generic (custom)
(log->cur_anim_type != __NON_GENERIC) // we are not on a non-generic (custom)
) {
_logic *target;
PXfloat player_pan;
PXreal ox, oy, oz, px, py, pz;
PXreal dx, dy, dz;
int32 playerEye;
// get postion of players head
// raw position (foot)
log->GetPosition(px, py, pz);
if (log->mega->Is_crouched())
playerEye = PLAYER_CROUCH_EYE; // crouch
else
playerEye = PLAYER_STOOD_EYE; // stand
// height increases to eye...
py += playerEye;
// get position of targetting object
target = MS->logic_structs[sel_id];
// raw position
target->GetPosition(ox, oy, oz);
// target is an actor so need adjustment for eye height...
if (target->image_type == VOXEL) {
CGame *pGameObject = (CGame *)LinkedDataObject::Fetch_item_by_number(MS->objects, sel_id);
int32 dead = CGameObject::GetIntegerVariable(pGameObject, CGameObject::GetVariable(pGameObject, "state"));
if (target->object_type == __NON_ORGANIC_MEGA) { // robot (look down)
oy += ROBOT_EYE;
} else if (dead) { // dead
oy += MEGA_DEAD_EYE;
} else if (target->mega->Is_crouched()) { // crouch
oy += MEGA_CROUCH_EYE;
} else {
oy += MEGA_STOOD_EYE; // standing
}
// difference in x,y,z
dx = (px - ox);
dy = (py - oy);
dz = (pz - oz);
}
// is an interact object, check for height.
else {
int32 height = STANDARD_MARKER_HEIGHT; // standard prop height
// if look_height set for this marker set
if (target->look_height != -1)
height = target->look_height;
dx = (px - ox);
// difference between player eye and marker height
dy = (PXreal)(playerEye - height);
dz = (pz - oz);
}
// get player pan (for calculating how far round the head moves)
if (MS->player.log->auto_panning == FALSE8)
player_pan = MS->player.log->pan;
else
player_pan = MS->player.log->auto_display_pan;
// Now find angles for neck bone...
b->boneTarget.vz = (int16)(4096.0 * (PXAngleOfVector(-dz, -dx) - player_pan));
b->boneTarget.vx = (int16)(4096.0 * PXAngleOfVector((PXfloat)PXsqrt(dx * dx + dz * dz), dy));
// make sure vz is in range -2048 - 2048... this might not be because of subtracting off player_pan
while (b->boneTarget.vz > 2048)
b->boneTarget.vz -= 4096;
while (b->boneTarget.vz < -2048)
b->boneTarget.vz += 4096;
if (b->boneTarget.vz > 1024)
b->boneTarget.vz = 1024;
if (b->boneTarget.vz < -1024)
b->boneTarget.vz = -1024;
// armed unarmed
int32 armed = MS->player.log->mega->Fetch_armed_status();
// from NONE to a status
// if status is none and we are armed and we have returned to central then start looking with upper body...
if ((status == STATUS_NONE) && (armed) && (b->boneValue.vz == 0) && (b->boneValue.vx == 0))
status = STATUS_BODY;
// if status is none and we are not armed and we have returned to central then start looking with head...
if ((status == STATUS_NONE) && (!armed) && (b->boneValue.vz == 0) && (b->boneValue.vx == 0))
status = STATUS_HEAD;
// from wrong status to NONE
// if aiming with body but not armed then status is none (in prep of going to status_body)
if ((status == STATUS_BODY) && (!armed))
status = STATUS_NONE;
// if looking with head but armed then status is none (in preperation for going to STATUS_HEAD)
if ((status == STATUS_HEAD) && (armed))
status = STATUS_NONE;
// now what to do in each status...
// no status, target nothing...
if (status == STATUS_NONE) {
b->boneTarget.vx = 0;
b->boneTarget.vy = 0;
b->boneTarget.vz = 0;
b->boneSpeed = STANDARD_LOOK_SPEED * 2;
}
// look with head...
else if (status == STATUS_HEAD) {
// limit pan and pitch
LimitShort(b->boneTarget.vz, -512, 384);
LimitShort(b->boneTarget.vx, -256, 256);
// for turning it looks better if you look up slightly (vx=vx-abs(vz)/2)
b->boneTarget.vx = (int16)(b->boneTarget.vx - (abs(b->boneTarget.vz) / 3));
// we need to set the speed to be STANDARD_LOOK_SPEED
b->boneSpeed = STANDARD_LOOK_SPEED;
// should not be hard coded, neck bone
b->boneNumber = 23;
}
// look with body...
else if (status == STATUS_BODY) {
// no bend when aimed
b->boneTarget.vy = 0;
// limit pitch (pan can be any value so gun is always pointing at target...)
LimitShort(b->boneTarget.vx, -256, 256);
b->boneTarget.vy = (int16)((b->boneTarget.vx * b->boneTarget.vz) / 1024);
// we need to set the speed to be STANDARD_LOOK_SPEED
b->boneSpeed = STANDARD_LOOK_SPEED * 2;
// should not be hard coded, body bone
b->boneNumber = 1;
}
} else {
// 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
// looks...
if ((b->boneTarget.vx == 0) && (b->boneValue.vz == 0) && (status == STATUS_BODY)) {
status = STATUS_NONE;
b->boneNumber = 23;
b->boneSpeed = STANDARD_LOOK_SPEED;
}
}
// dont do an update here...
}
mcodeFunctionReturnCodes fn_set_neck_bone(int32 &result, int32 *params) { return (MS->fn_set_neck_bone(result, params)); }
mcodeFunctionReturnCodes fn_set_neck_vector(int32 &result, int32 *params) { return (MS->fn_set_neck_vector(result, params)); }
mcodeFunctionReturnCodes speak_set_neck_vector(int32 &result, int32 *params) { return (MS->speak_set_neck_vector(result, params)); }
mcodeFunctionReturnCodes fn_simple_look(int32 &result, int32 *params) { return (MS->fn_simple_look(result, params)); }
mcodeFunctionReturnCodes speak_simple_look(int32 &result, int32 *params) { return (MS->speak_simple_look(result, params)); }
mcodeFunctionReturnCodes fn_set_mega_height(int32 &result, int32 *params) { return (MS->fn_set_mega_height(result, params)); }
mcodeFunctionReturnCodes _game_session::fn_set_mega_height(int32 &, int32 *params) {
if (!L->mega)
Fatal_error("fn_set_mega_height called for %s which is not a mega!", L->GetName());
L->mega->height = params[0];
return IR_CONT;
}
// the array of standard look coords
#define LOOK_RIGHT (int16)(-384)
#define LOOK_UP (int16)(-196)
#define LOOK_LEFT (int16)(-LOOK_RIGHT)
#define LOOK_DOWN (int16)(-LOOK_UP)
const short looks[9][3] = {
{0, 0, 0}, // ahead
{0, 0, LOOK_UP}, // up
{LOOK_RIGHT, 0, LOOK_UP}, // up/right
{LOOK_RIGHT, 0, 0}, // right
{LOOK_RIGHT, 0, LOOK_DOWN}, // down/right
{0, 0, LOOK_DOWN}, // down
{LOOK_LEFT, 0, LOOK_DOWN}, // down/left
{LOOK_LEFT, 0, 0}, // left
{LOOK_LEFT, 0, LOOK_UP} // up/left
};
// look
// 0 - Ahead
// 1 - Up
// 2 - Up/Right
// 3 - Right
// 4 - Right/Down
// 5 - Down
// 6 - Down/Left
// 7 - Left
// 8 - Left/Up
// simple look
mcodeFunctionReturnCodes _game_session::fn_simple_look(int32 &, int32 *params) {
int32 l = params[0]; // which direction
if (!logic_structs[cur_id]->mega)
Fatal_error("fn_simple_look called by non mega %s", L->GetName());
int32 callingParams[2];
callingParams[0] = MemoryUtil::encodePtr((uint8 *)const_cast<char *>(L->GetName()));
callingParams[1] = l;
int32 ret;
return speak_simple_look(ret, callingParams);
}
// simple look from speech scripts...
mcodeFunctionReturnCodes _game_session::speak_simple_look(int32 &, int32 *params) {
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
// object
int32 object_id = LinkedDataObject::Fetch_item_number_by_name(objects, object_name);
// direction
int32 l = params[1];
if (!logic_structs[object_id]->mega)
Fatal_error("speak_simple_look called by non mega %s", logic_structs[object_id]->GetName());
if (logic_structs[object_id]->voxel_info->lookBone.boneNumber == (int8)-1)
Fatal_error("speak_simple_look called but no fn_set_neck_bone() has been called for object %s", logic_structs[object_id]->GetName());
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);
// sorry looks is wrong way round! (z,y,x)
logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vx = looks[l][2];
logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vy = looks[l][1];
logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vz = looks[l][0];
logic_structs[object_id]->voxel_info->lookBone.boneSpeed = STANDARD_LOOK_SPEED;
warning("doing a look direction: %d bone: %d", l, logic_structs[object_id]->voxel_info->lookBone.boneNumber);
return IR_CONT;
}
// set neck bone of current object
mcodeFunctionReturnCodes _game_session::fn_set_neck_bone(int32 &, int32 *params) {
int32 bone = params[0];
if (!logic_structs[cur_id]->mega)
Fatal_error("fn_set_neck_bone called by non mega %s", L->GetName());
Tdebug("bones.txt", "%s: Neck bone is %d", L->GetName(), bone);
logic_structs[cur_id]->voxel_info->lookBone.boneNumber = (int8)bone;
logic_structs[cur_id]->voxel_info->neckBone.boneNumber = (int8)bone;
return IR_CONT;
}
// set neck vector
// params
// 0 - x
// 1 - y
// 2 - z
// 3 - speed
// equiverlant to speech_set_neck_vector(NAME,x,y,z,speed)
// where NAME is object name...
//
mcodeFunctionReturnCodes _game_session::fn_set_neck_vector(int32 &, int32 *params) {
int32 x, y, z, speed;
x = params[0];
y = params[1];
z = params[2];
speed = params[3];
if (!logic_structs[cur_id]->mega)
Fatal_error("fn_set_neck_vector called by non mega %s", L->GetName());
int32 callingParams[5];
callingParams[0] = MemoryUtil::encodePtr((uint8 *)const_cast<char *>(L->GetName()));
callingParams[1] = x;
callingParams[2] = y;
callingParams[3] = z;
callingParams[4] = speed;
int32 ret;
return speak_set_neck_vector(ret, callingParams);
}
// set neck vector
// params
// 0 - "object"
// 1 - x
// 2 - y
// 3 - z
// 4 - speed
//
mcodeFunctionReturnCodes _game_session::speak_set_neck_vector(int32 &, int32 *params) {
int32 object_id;
int32 x, y, z, speed;
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
object_id = LinkedDataObject::Fetch_item_number_by_name(objects, object_name);
x = params[1];
y = params[2];
z = params[3];
speed = params[4];
if (L == player.log) {
warning("player set neck vector...");
logic_structs[object_id]->voxel_info->lookBone.boneNumber = 23;
}
if (!logic_structs[object_id]->mega)
Fatal_error("fn_set_neck_vector called by non mega %s", logic_structs[object_id]->GetName());
if (logic_structs[object_id]->voxel_info->lookBone.boneNumber == (int8)-1)
Fatal_error("fn_set_neck_vector called but no fn_set_neck_bone() has been called for object %s", logic_structs[object_id]->GetName());
Tdebug("bones.txt", "%s: Setting bone <%d,%d,%d> at speed %d", object_name, x, y, z, speed);
logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vx = (int16)x;
logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vy = (int16)y;
logic_structs[object_id]->voxel_info->scriptedLookBoneTarget.vz = (int16)z;
logic_structs[object_id]->voxel_info->lookBone.boneSpeed = (int16)speed;
return IR_CONT;
}
} // End of namespace ICB