mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 05:34:00 +00:00
889 lines
28 KiB
C++
889 lines
28 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "engines/icb/p4.h"
|
|
#include "engines/icb/common/px_rccommon.h"
|
|
#include "engines/icb/common/px_anims.h"
|
|
#include "engines/icb/session.h"
|
|
#include "engines/icb/animation_mega_set.h"
|
|
#include "engines/icb/global_objects.h"
|
|
#include "engines/icb/debug.h"
|
|
#include "engines/icb/player.h"
|
|
#include "engines/icb/res_man.h"
|
|
|
|
namespace ICB {
|
|
|
|
bool8 _game_session::Easy_frame_motion_and_pan(__mega_set_names anim_type, bool8 /*player*/) {
|
|
// advances frame and motion AND PAN but does not check for barrier collisions
|
|
PXreal xnext, znext;
|
|
PXreal x, z;
|
|
|
|
ANIM_CHECK(anim_type);
|
|
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
|
|
|
|
if (L->anim_pc + 1 >= pAnim->frame_qty)
|
|
Fatal_error("Easy_frame_and_motion finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type),
|
|
L->anim_pc, pAnim->frame_qty);
|
|
|
|
// add the change in pan from the last frame to the current frame of the anim to the logic objects pan
|
|
PXreal pan1, pan2;
|
|
|
|
// Get next frame from the anim
|
|
PXframe *nextFrame = PXFrameEnOfAnim(L->anim_pc + 1, pAnim);
|
|
|
|
// Get current frame from the anim
|
|
PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
|
|
|
|
nextFrame->markers[ORG_POS].GetPan(&pan1);
|
|
currentFrame->markers[ORG_POS].GetPan(&pan2);
|
|
|
|
L->pan += (pan1 - pan2); // update by difference
|
|
|
|
// get motion displacement from currently displayed frame to next one
|
|
// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
|
|
PXreal x1, x2, z1, z2, unused;
|
|
|
|
nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
|
|
currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
|
|
|
|
xnext = x2 - x1;
|
|
znext = z2 - z1;
|
|
|
|
// advance the frame
|
|
L->anim_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1);
|
|
|
|
// get the pan unwind value of the frame to be printed
|
|
PXreal pan;
|
|
|
|
// Get the new current frame, using a new reference !
|
|
// as GCC got it all wrong if the same reference was re-used
|
|
currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
|
|
|
|
currentFrame->markers[ORG_POS].GetPan(&pan);
|
|
|
|
L->pan_adjust = pan; // this value will be unwound from the orientation of the frame at render time in stage draw
|
|
|
|
// calculate the new x and z coordinate from this frames motion offset
|
|
// do the z and x together
|
|
PXfloat ang = (L->pan - L->pan_adjust) * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
|
|
z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
|
|
|
|
// x and z are the new coordinates
|
|
|
|
// record the new true actor position
|
|
M->actor_xyz.x = x;
|
|
M->actor_xyz.z = z;
|
|
|
|
// check for new parent box and if so bring barriers
|
|
if (L->pan >= HALF_TURN)
|
|
L->pan -= FULL_TURN;
|
|
else if (L->pan <= -HALF_TURN)
|
|
L->pan += FULL_TURN;
|
|
|
|
return (TRUE8);
|
|
}
|
|
|
|
bool8 _game_session::Easy_frame_and_motion(__mega_set_names anim_type, bool8 /*player*/, uint8 nFrames) {
|
|
// advances frame and motion but does not check for barrier collisions
|
|
PXreal xnext, znext;
|
|
PXreal x, z;
|
|
|
|
ANIM_CHECK(anim_type);
|
|
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
|
|
|
|
if ((L->anim_pc + nFrames) >= pAnim->frame_qty)
|
|
Fatal_error("Easy_frame_and_motion finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type),
|
|
L->anim_pc, pAnim->frame_qty);
|
|
|
|
// get motion displacement from currently displayed frame to next one
|
|
// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
|
|
PXreal x1, x2, z1, z2, unused;
|
|
|
|
// Get next frame from the anim
|
|
PXframe *nextFrame = PXFrameEnOfAnim(L->anim_pc + nFrames, pAnim);
|
|
// Get current frame from the anim
|
|
PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
|
|
|
|
nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
|
|
currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
|
|
|
|
xnext = x2 - x1;
|
|
znext = z2 - z1;
|
|
|
|
// calculate the new x and z coordinate from this frames motion offset
|
|
// do the z and x together
|
|
PXfloat ang = L->pan * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
|
|
z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
|
|
// x and z are the new coordinates
|
|
|
|
// advance the frame
|
|
L->anim_pc = (L->anim_pc + nFrames);
|
|
|
|
// record the new true actor position
|
|
M->actor_xyz.x = x;
|
|
M->actor_xyz.z = z;
|
|
|
|
return (TRUE8);
|
|
}
|
|
|
|
bool8 _game_session::Advance_frame_and_motion(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
|
|
// attempts to move the frame forward and move the character
|
|
// returns fail and frame is not changed if the way forward is blocked by a barrier
|
|
// the frame counter will be looped
|
|
// handles shallow barrier bump correction!
|
|
|
|
__barrier_result ret;
|
|
uint32 initial_pc = L->anim_pc;
|
|
|
|
ANIM_CHECK(anim_type);
|
|
|
|
ret = Core_advance(anim_type, pl, nFrames);
|
|
|
|
if (ret == __BLOCKED)
|
|
return (FALSE8);
|
|
|
|
if ((ret == __OK) || (ret == __NUDGED))
|
|
return (TRUE8);
|
|
|
|
// pan was corrected - go around again
|
|
|
|
L->anim_pc = initial_pc;
|
|
ret = Core_advance(anim_type, pl, nFrames);
|
|
|
|
if (ret == __BLOCKED) // v odd indeed
|
|
return (FALSE8);
|
|
|
|
if ((ret == __OK) || (ret == __NUDGED))
|
|
return (TRUE8); // good
|
|
|
|
// corrected again - this is strange
|
|
|
|
return (FALSE8);
|
|
}
|
|
|
|
__barrier_result _game_session::Core_advance(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
|
|
PXreal xnext, znext;
|
|
PXreal x, z;
|
|
__barrier_result ret;
|
|
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
|
|
|
|
if ((L->anim_pc + nFrames) >= pAnim->frame_qty)
|
|
Fatal_error("Core_advance finds [%s] has illegal frame in anim [%s] %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type), L->anim_pc,
|
|
pAnim->frame_qty);
|
|
|
|
// get motion displacement from currently displayed frame to next one
|
|
// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
|
|
PXreal x1, x2, z1, z2, unused;
|
|
|
|
// Get next frame from the anim
|
|
PXframe *nextFrame = PXFrameEnOfAnim(L->anim_pc + nFrames, pAnim);
|
|
// Get current frame from the anim
|
|
PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
|
|
|
|
nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
|
|
currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
|
|
|
|
xnext = x2 - x1;
|
|
znext = z2 - z1;
|
|
|
|
// calculate the new x and z coordinate from this frames motion offset
|
|
// do the z and x together
|
|
PXfloat ang = L->pan * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
|
|
z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
|
|
// x and z are the new coordinates
|
|
|
|
// but, are we walking into a barrier?
|
|
ret = Check_barrier_bump_and_bounce(x, M->actor_xyz.y, z, // new position
|
|
M->actor_xyz.x, M->actor_xyz.y, M->actor_xyz.z, // old position
|
|
pl);
|
|
|
|
L->anim_pc = (L->anim_pc + nFrames);
|
|
|
|
// did we move forward without a problem?
|
|
if (ret == __OK) {
|
|
// advance the frame
|
|
|
|
// record the new true actor position
|
|
M->actor_xyz.x = x;
|
|
M->actor_xyz.z = z;
|
|
|
|
// check for new parent box and if so bring barriers
|
|
Prepare_megas_route_barriers(pl);
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
void _game_session::Normalise_anim_pc() {
|
|
// routines that make raw calls to advance_frame_and_motion need to call this to reset anim pc
|
|
// this is because of changes required by the multi-speed capabilities
|
|
|
|
PXanim *pAnim = (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); //
|
|
L->anim_pc = (L->anim_pc) % (pAnim->frame_qty - 1);
|
|
}
|
|
|
|
bool8 _game_session::Reverse_frame_and_motion(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
|
|
// as Advance_frame_and_motion but frames work backward
|
|
// the frame counter will be looped
|
|
// handles shallow barrier bump correction!
|
|
|
|
__barrier_result ret;
|
|
|
|
ANIM_CHECK(anim_type);
|
|
|
|
ret = Core_reverse(anim_type, pl, nFrames);
|
|
|
|
if (ret == __BLOCKED)
|
|
return (FALSE8);
|
|
|
|
if ((ret == __OK) || (ret == __NUDGED))
|
|
return (TRUE8);
|
|
|
|
// pan was corrected - go around again
|
|
ret = Core_reverse(anim_type, pl, nFrames);
|
|
|
|
if (ret == __BLOCKED) // v odd indeed
|
|
return (FALSE8);
|
|
|
|
if ((ret == __OK) || (ret == __NUDGED))
|
|
return (TRUE8); // good
|
|
|
|
// corrected again - this is strange
|
|
|
|
return (FALSE8);
|
|
}
|
|
|
|
__barrier_result _game_session::Core_reverse(__mega_set_names anim_type, bool8 pl, uint8 nFrames) {
|
|
// as Advance_frame_and_motion but frames work backward
|
|
// the frame counter will be looped
|
|
// handles shallow barrier bump correction!
|
|
|
|
PXreal xnext, znext;
|
|
PXreal x, z;
|
|
__barrier_result ret;
|
|
uint32 next_pc;
|
|
|
|
ANIM_CHECK(anim_type);
|
|
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anim_type), I->info_name_hash[anim_type], I->base_path, I->base_path_hash);
|
|
|
|
// if we are on frame 0 then shift up to pretend we're coming in off the dummy frame
|
|
if (!L->anim_pc) {
|
|
next_pc = pAnim->frame_qty - 2;
|
|
L->anim_pc = pAnim->frame_qty - 1;
|
|
} else if (L->anim_pc < nFrames)
|
|
next_pc = 0;
|
|
else
|
|
next_pc = L->anim_pc - nFrames;
|
|
|
|
if ((next_pc >= pAnim->frame_qty) || (L->anim_pc >= pAnim->frame_qty))
|
|
Fatal_error("Core_reverse finds [%s] has illegal frame in anim [%s] %d %d %d", (const char *)L->GetName(), (const char *)I->get_info_name(anim_type), next_pc,
|
|
L->anim_pc, pAnim->frame_qty);
|
|
|
|
// get motion displacement from currently displayed frame to next one
|
|
// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
|
|
PXreal x1, x2, z1, z2, unused;
|
|
|
|
// Get next frame from the anim
|
|
PXframe *nextFrame = PXFrameEnOfAnim(next_pc, pAnim);
|
|
// Get current frame from the anim
|
|
PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
|
|
|
|
nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
|
|
currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
|
|
|
|
xnext = x2 - x1;
|
|
znext = z2 - z1;
|
|
|
|
// calculate the new x and z coordinate from this frames motion offset
|
|
// do the z and x together
|
|
PXfloat ang = L->pan * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
|
|
z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
|
|
// x and z are the new coordinates
|
|
|
|
// but, are we walking into a barrier?
|
|
ret = Check_barrier_bump_and_bounce(x, M->actor_xyz.y, z, // new position
|
|
M->actor_xyz.x, M->actor_xyz.y, M->actor_xyz.z, // old position
|
|
pl);
|
|
|
|
L->anim_pc = next_pc;
|
|
|
|
// did we move forward without a problem?
|
|
if (ret == __OK) {
|
|
// record the new true actor position
|
|
M->actor_xyz.x = x;
|
|
M->actor_xyz.z = z;
|
|
|
|
// check for new parent box and if so bring barriers
|
|
Prepare_megas_route_barriers(pl);
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
void _game_session::Animate_turn_to_pan(__mega_set_names anim_type, uint32 speedup) {
|
|
// route manager turns character on the spot until facing route->target_pan
|
|
// final pan may need to be snapped to avoid overrun
|
|
// once done, x,z are restored to pre-turn animations - this is essential if character is to follow route correctly
|
|
// there is no barrier check!
|
|
|
|
// do a frames turn anim
|
|
// have we finished
|
|
// snap pan
|
|
// restore x,z
|
|
|
|
PXreal xnext, znext;
|
|
PXreal x, z;
|
|
uint32 next_pc, info_pc;
|
|
PXfloat this_pan_change;
|
|
|
|
// set type
|
|
L->cur_anim_type = anim_type;
|
|
|
|
ANIM_CHECK(anim_type);
|
|
|
|
// get anim set
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(L->voxel_info->get_info_name(anim_type), L->voxel_info->info_name_hash[anim_type], L->voxel_info->base_path,
|
|
L->voxel_info->base_path_hash); //
|
|
|
|
// anim pc may be illegal so nutralise it
|
|
L->anim_pc = (L->anim_pc) % (pAnim->frame_qty - 1);
|
|
|
|
// adjust the frame PC in the direction we're going to turn
|
|
if (!M->turn_dir) {
|
|
// turning right (clockwise)
|
|
// anims are clockwise to run naturally through frames
|
|
next_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1); // next frame to display
|
|
info_pc = L->anim_pc + 1; // from to take movement info from
|
|
} else { // pos
|
|
// turning left
|
|
// reverse the anim
|
|
|
|
if (!L->anim_pc) { // pc==0
|
|
info_pc = next_pc = pAnim->frame_qty - 2; // take movement info from dummy marker frame
|
|
L->anim_pc = pAnim->frame_qty - 1; // display last legal frame - have to force this for math to work
|
|
} else { // pc is non zero
|
|
info_pc = next_pc = L->anim_pc - 1; // display and movement info from next frame down
|
|
}
|
|
}
|
|
|
|
if ((info_pc >= pAnim->frame_qty) || (next_pc >= pAnim->frame_qty))
|
|
Fatal_error("Animate_turn_to_pan [%s] using illegal frame", object->GetName());
|
|
|
|
// update engine pan with the difference between pan of previous frame and next frame
|
|
PXreal pan1, pan2;
|
|
|
|
// Get info_pc frame from the anim
|
|
PXframe *infoFrame = PXFrameEnOfAnim(info_pc, pAnim);
|
|
// Get current frame from the anim
|
|
PXframe *currentFrame = PXFrameEnOfAnim(L->anim_pc, pAnim);
|
|
|
|
infoFrame->markers[ORG_POS].GetPan(&pan1);
|
|
currentFrame->markers[ORG_POS].GetPan(&pan2);
|
|
|
|
this_pan_change = (pan1 - pan2); // update by difference
|
|
|
|
// times the speedup for fast turn functions - normally 0
|
|
this_pan_change *= speedup;
|
|
|
|
if (this_pan_change >= HALF_TURN) {
|
|
this_pan_change -= FULL_TURN;
|
|
} else if (this_pan_change <= -HALF_TURN) {
|
|
this_pan_change += FULL_TURN; // stop the wrap
|
|
}
|
|
|
|
if (PXfabs(this_pan_change) > M->target_pan) {
|
|
// we are going to turn too much this go so clip
|
|
|
|
L->pan = M->actual_target_pan; // clip to actual left
|
|
|
|
M->target_pan = ZERO_TURN; // we're done
|
|
|
|
} else {
|
|
// still further to go
|
|
|
|
L->pan += this_pan_change;
|
|
|
|
M->target_pan = M->target_pan - (PXfloat)PXfabs(this_pan_change);
|
|
}
|
|
|
|
// get motion displacement from currently displayed frame to next one
|
|
// note that we always read frame+1 for motion of next frame even though the voxel frame itself will be looped back to 0
|
|
PXreal x1, x2, z1, z2, unused;
|
|
|
|
// Get the next frame from the anim
|
|
PXframe *nextFrame = PXFrameEnOfAnim(next_pc, pAnim);
|
|
|
|
nextFrame->markers[ORG_POS].GetXYZ(&x2, &unused, &z2);
|
|
|
|
// Note, assumes current frame hasn't changed i.e L->info_pc is same
|
|
currentFrame->markers[ORG_POS].GetXYZ(&x1, &unused, &z1);
|
|
|
|
xnext = x2 - x1;
|
|
znext = z2 - z1;
|
|
|
|
// update pc
|
|
L->anim_pc = next_pc; // allready computed
|
|
|
|
// get the pan unwind value of the frame to be printed
|
|
PXreal pan;
|
|
|
|
// Note, L->anim_pc = next_pc
|
|
nextFrame->markers[ORG_POS].GetPan(&pan);
|
|
|
|
L->pan_adjust = pan;
|
|
|
|
// calculate the new x and z coordinate from this frames motion offset
|
|
// do the z and x together
|
|
PXfloat ang = (L->pan - L->pan_adjust) * TWO_PI;
|
|
PXfloat cang = (PXfloat)PXcos(ang);
|
|
PXfloat sang = (PXfloat)PXsin(ang);
|
|
|
|
x = M->actor_xyz.x + PXfloat2PXreal(xnext * cang + znext * sang);
|
|
z = M->actor_xyz.z + PXfloat2PXreal(znext * cang - xnext * sang);
|
|
// x and z are the new coordinates
|
|
|
|
if (L->pan >= HALF_TURN)
|
|
L->pan -= FULL_TURN;
|
|
else if (L->pan <= -HALF_TURN)
|
|
L->pan += FULL_TURN; // stop the wrap
|
|
}
|
|
|
|
bool8 _game_session::Calc_target_pan(PXreal x, PXreal z, PXreal x2, PXreal z2) {
|
|
// will LOOK TOWARDS a shallow angle or set M->target_pan
|
|
// not to be used by routing routines!
|
|
|
|
// x,z are source coordinate
|
|
// x2,z2 are target coordinate
|
|
|
|
// returns FALSE no turn required
|
|
// TRUE turn required
|
|
|
|
PXfloat new_pan, diff;
|
|
|
|
new_pan = PXAngleOfVector(z - z2, x - x2); // work out vector
|
|
|
|
M->looking_pan = new_pan;
|
|
|
|
// get difference between the two
|
|
diff = new_pan - L->pan;
|
|
|
|
if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
|
|
// work out which way to turn
|
|
if (diff > HALF_TURN)
|
|
diff -= FULL_TURN;
|
|
|
|
else if (diff < -HALF_TURN)
|
|
diff += FULL_TURN;
|
|
|
|
// diff is now the distance to turn by and its sign denotes direction
|
|
|
|
if (diff < FLOAT_ZERO) {
|
|
M->turn_dir = 0; // right
|
|
} else {
|
|
M->turn_dir = 1; // left
|
|
}
|
|
|
|
M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
|
|
|
|
M->actual_target_pan = new_pan; // actual target which we may clip to
|
|
|
|
L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
|
|
|
|
// we face straight ahead
|
|
I->lookBone.boneTarget.vz = (short)(0);
|
|
|
|
return (TRUE8);
|
|
} else {
|
|
// face towards object or whatever with upper body
|
|
I->lookBone.boneNumber = 1;
|
|
I->lookBone.boneSpeed = 128;
|
|
I->lookBone.boneTarget.vz = (short)(diff * (4096 / FULL_TURN)); // on psx diff is 0-4096 (full_turn), so we want diff*1 = diff * (4096/4096)
|
|
// on pc diff is 0-1 (full_turn), we we want diff*4096 = diff * (4096/1)
|
|
|
|
return (FALSE8);
|
|
}
|
|
}
|
|
|
|
bool8 _game_session::Calc_target_pan_no_bones(PXreal x, PXreal z, PXreal x2, PXreal z2) {
|
|
// will snap a shallow angle or set M->target_pan
|
|
|
|
// x,z are source coordinate
|
|
// x2,z2 are target coordinate
|
|
|
|
// returns FALSE no turn required
|
|
// TRUE turn required
|
|
|
|
PXfloat new_pan, diff;
|
|
|
|
new_pan = PXAngleOfVector(z - z2, x - x2); // work out vector
|
|
|
|
// get difference between the two
|
|
diff = new_pan - L->pan;
|
|
|
|
if (PXfabs(diff) > (FULL_TURN / 10)) { // 0.1f
|
|
// work out which way to turn
|
|
if (diff > HALF_TURN)
|
|
diff -= FULL_TURN;
|
|
|
|
else if (diff < -HALF_TURN)
|
|
diff += FULL_TURN;
|
|
|
|
// diff is now the distance to turn by and its sign denotes direction
|
|
|
|
if (diff < FLOAT_ZERO) {
|
|
M->turn_dir = 0; // right
|
|
} else {
|
|
M->turn_dir = 1; // left
|
|
}
|
|
|
|
M->target_pan = (PXfloat)PXfabs(diff); // save positive pan distance
|
|
|
|
M->actual_target_pan = new_pan; // actual target which we may clip to
|
|
|
|
L->anim_pc = 0; // legal pc for first frame in turn anim - this needs thought
|
|
|
|
// we face straight ahead
|
|
I->lookBone.boneTarget.vz = (short)(0);
|
|
|
|
return (TRUE8);
|
|
} else {
|
|
// shallow angle so snap and continue as normal
|
|
L->pan = new_pan;
|
|
|
|
return FALSE8;
|
|
}
|
|
}
|
|
|
|
void _game_session::Soft_start_with_double_link(__mega_set_names link_one, __mega_set_names link_two, __mega_set_names next_anim) {
|
|
// soft start from best os 2 link anims (if CAPS available) and push follow on animation
|
|
|
|
int32 diff = 1000000; // a big number
|
|
|
|
// is the link anim available?
|
|
if (I->IsAnimTable(link_one)) {
|
|
// push hard start follow on animation
|
|
M->next_anim_type = next_anim;
|
|
|
|
// will pick best
|
|
diff = Soften_up_anim_file(link_one, diff);
|
|
|
|
if (I->IsAnimTable(link_two)) {
|
|
// may pick best
|
|
Soften_up_anim_file(link_two, diff);
|
|
}
|
|
} else { // first link anim requested not available so skip the second and jump straight in
|
|
// set anim type
|
|
L->anim_pc = 0;
|
|
L->cur_anim_type = next_anim;
|
|
}
|
|
}
|
|
|
|
void _game_session::Soft_start_with_single_link(__mega_set_names link_anim, __mega_set_names next_anim) {
|
|
// soft start a link anim (if CAPS available) and push follow on animation
|
|
|
|
int32 diff = 1000000; // a big number
|
|
|
|
// is the link anim available?
|
|
if (I->IsAnimTable(link_anim)) {
|
|
// will pick best
|
|
Soften_up_anim_file(link_anim, diff);
|
|
|
|
// push hard start follow on animation
|
|
M->next_anim_type = next_anim;
|
|
} else { // link anim requested but not available
|
|
// set anim type
|
|
L->anim_pc = 0;
|
|
L->cur_anim_type = next_anim;
|
|
}
|
|
}
|
|
|
|
void _game_session::Soft_start_single_anim(__mega_set_names next_anim) {
|
|
// select best leg position for specified animation
|
|
// there is no optional link
|
|
// it HAS to be ok to skip past frames in the new anim
|
|
// also sets cur_anim_type
|
|
|
|
int32 diff = 1000000; // a big number
|
|
|
|
// next anim not in CAPS
|
|
if ((!L->voxel_info->IsAnimTable(next_anim))) {
|
|
// cant go on without a major anim - this is not a link so cant just be skipped
|
|
Shut_down_object("by Soft_start_single_anim next anim dont exist");
|
|
return;
|
|
}
|
|
|
|
// will pick best
|
|
Soften_up_anim_file(next_anim, diff);
|
|
|
|
// set anim type
|
|
L->cur_anim_type = next_anim;
|
|
|
|
// nothing to move onto
|
|
M->next_anim_type = __NO_ANIM;
|
|
}
|
|
|
|
void _game_session::Hard_start_single_anim(__mega_set_names next_anim) {
|
|
// jumps straight into new anim on frame 0
|
|
// sets cur_anim_type
|
|
|
|
// next anim not in CAPS
|
|
if ((!L->voxel_info->IsAnimTable(next_anim))) {
|
|
// cant go on without a major anim - this is not a link so cant just be skipped
|
|
Shut_down_object("by Hard_start_single_anim next anim dont exist");
|
|
return;
|
|
}
|
|
|
|
// hard start on frame 0
|
|
L->anim_pc = 0;
|
|
|
|
// set anim type
|
|
L->cur_anim_type = next_anim;
|
|
|
|
// nothing to move onto
|
|
M->next_anim_type = __NO_ANIM;
|
|
}
|
|
|
|
int32 _game_session::Soften_up_anim_file(__mega_set_names link, int32 diff) {
|
|
// pick best frame in passed anim compared to best so far passed in 'diff'
|
|
uint32 old_leg_pos;
|
|
int j;
|
|
|
|
// Jake check the anim exists / make its name
|
|
ANIM_CHECK(L->cur_anim_type);
|
|
|
|
// Do this first to make the whole res_open defrag thing work nicely
|
|
PXanim *pCur_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);
|
|
|
|
// find out leg position for current frame
|
|
old_leg_pos = PXFrameEnOfAnim(L->anim_pc, pCur_Anim)->left_foot_distance;
|
|
|
|
// Jake check the link anim exists / make its name
|
|
ANIM_CHECK(link);
|
|
PXanim *pLnk_Anim = (PXanim *)rs_anims->Res_open(I->get_info_name(link), I->info_name_hash[link], I->base_path, I->base_path_hash); //
|
|
// special check for only one frame
|
|
if (pLnk_Anim->frame_qty == 1) {
|
|
L->cur_anim_type = link; // set type
|
|
L->anim_pc = 0; // first frame
|
|
return (diff);
|
|
}
|
|
|
|
// see which has the closest leg position
|
|
for (j = 0; j < (pLnk_Anim->frame_qty - 1); j++) {
|
|
int32 foot = PXFrameEnOfAnim(j, pLnk_Anim)->left_foot_distance;
|
|
int32 d = twabs(foot - old_leg_pos);
|
|
|
|
if (d < diff) {
|
|
diff = d;
|
|
L->cur_anim_type = link; // set type
|
|
L->anim_pc = j; // this frame is best so far
|
|
}
|
|
}
|
|
|
|
// return best diff so far incase we're coming back on a multiple link anim scheme
|
|
return (diff);
|
|
}
|
|
|
|
bool8 _game_session::Play_anim() {
|
|
// plays anim until finished
|
|
// keeps playing anim even if motion has been stopped
|
|
// when current is done it will play the queued M->next_anim_type if it exists. When thats done we're done
|
|
|
|
// returns 1 when done
|
|
// 0 we need to come back next cycle
|
|
|
|
// get animation
|
|
PXanim *pAnim = (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);
|
|
|
|
// last frame currently displayed?
|
|
if ((int)(L->anim_pc + 1) == (pAnim->frame_qty - 1)) {
|
|
// we displayed the last frame last cycle - so display first of new mode this cycle
|
|
// we may have been playing a link anim - so check for a new anim waiting in M->next_anim_type
|
|
if (M->next_anim_type == __NO_ANIM) {
|
|
// we're finished
|
|
return (TRUE8);
|
|
}
|
|
|
|
// reset pc ready for whatever starts next
|
|
// current must be link so we go into new on frame 0
|
|
L->anim_pc = 0;
|
|
|
|
// ok, there is a link anim
|
|
L->cur_anim_type = M->next_anim_type;
|
|
M->next_anim_type = __NO_ANIM; // must clear this or it'll play forever
|
|
|
|
// need to come back
|
|
return (FALSE8);
|
|
}
|
|
|
|
// shift character and frame forward by the amount appropriate
|
|
Advance_frame_and_motion(L->cur_anim_type, 0, 1);
|
|
|
|
// need to come back
|
|
return (FALSE8);
|
|
}
|
|
|
|
bool8 _game_session::Play_reverse_anim() {
|
|
// plays anim until finished
|
|
// keeps playing anim even if motion has been stopped
|
|
// when current is done it will play the queued M->next_anim_type if it exists. When thats done we're done
|
|
|
|
// returns 1 when done
|
|
// 0 we need to come back next cycle
|
|
Zdebug("Play_reverse_anim");
|
|
|
|
// last frame currently displayed?
|
|
if (!L->anim_pc) {
|
|
// we displayed the last frame last cycle - so display first of new mode this cycle
|
|
|
|
// we may have been playing a link anim - so check for a new anim waiting in M->next_anim_type
|
|
if (M->next_anim_type == __NO_ANIM) {
|
|
Zdebug("done");
|
|
// we're finished
|
|
return (TRUE8);
|
|
}
|
|
|
|
// reset pc ready for whatever starts next
|
|
// current must be link so we go into new on frame 0
|
|
L->anim_pc = 0;
|
|
|
|
// ok, there is a link anim
|
|
L->cur_anim_type = M->next_anim_type;
|
|
M->next_anim_type = __NO_ANIM; // must clear this or it'll play forever
|
|
|
|
// need to come back
|
|
return (FALSE8);
|
|
}
|
|
|
|
// shift character and frame forward by the amount appropriate
|
|
Reverse_frame_and_motion(L->cur_anim_type, 0, 1);
|
|
|
|
Zdebug("~Reverse");
|
|
|
|
// need to come back
|
|
return (FALSE8);
|
|
}
|
|
|
|
bool8 _game_session::Play_anim_with_no_movement() {
|
|
// plays anim until finished
|
|
// keeps playing anim even if motion has been stopped
|
|
// when current is done it will play the queued M->next_anim_type if it exists. When thats done we're done
|
|
|
|
// returns 1 when done
|
|
// 0 we need to come back next cycle
|
|
|
|
// get animation
|
|
PXanim *pAnim = (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); //
|
|
|
|
// last frame currently displayed?
|
|
if ((int)(L->anim_pc + 1) == (pAnim->frame_qty - 1)) {
|
|
// we displayed the last frame last cycle - so display first of new mode this cycle
|
|
|
|
// we may have been playing a link anim - so check for a new anim waiting in M->next_anim_type
|
|
if (M->next_anim_type == __NO_ANIM) {
|
|
// we're finished
|
|
return (TRUE8);
|
|
}
|
|
|
|
// reset pc ready for whatever starts next
|
|
// current must be link so we go into new on frame 0
|
|
L->anim_pc = 0;
|
|
|
|
// ok, there is a link anim
|
|
L->cur_anim_type = M->next_anim_type;
|
|
M->next_anim_type = __NO_ANIM; // must clear this or it'll play forever
|
|
|
|
// need to come back
|
|
return (FALSE8);
|
|
}
|
|
|
|
// shift character and frame forward by the amount appropriate
|
|
|
|
// ok, advance frame but not position
|
|
L->anim_pc = (L->anim_pc + 1) % (pAnim->frame_qty - 1);
|
|
|
|
// need to come back
|
|
return (FALSE8);
|
|
}
|
|
|
|
void _game_session::Set_motion(__motion motion) {
|
|
// set motion type for cur object
|
|
M->motion = motion;
|
|
}
|
|
|
|
__motion _game_session::Get_motion() {
|
|
// get motion type for cur object
|
|
return (M->motion);
|
|
}
|
|
|
|
void _game_session::Set_pose(__weapon weapon) {
|
|
// set weapon type for cur object
|
|
M->weapon = weapon;
|
|
}
|
|
|
|
void _game_session::Change_pose_in_current_anim_set() {
|
|
// change the weapon set
|
|
// delete an old one
|
|
// we very much expect a set to already have been picked by the script
|
|
// its likely that this will only be called by the player arm/unarm code
|
|
|
|
// create _vox_image object
|
|
logic_structs[cur_id]->voxel_info->___init(M->chr_name, M->anim_set, logic_structs[cur_id]->mega->Fetch_pose()); // we pass the person, set names through
|
|
}
|
|
|
|
uint32 _game_session::Fetch_last_frame(__mega_set_names anima) {
|
|
// return final frame number in animation
|
|
if ((!I->IsAnimTable(anima))) {
|
|
Fatal_error("Fetch_last_frame cant access illegal anim [%s]", master_anim_name_table[anima].name);
|
|
}
|
|
|
|
PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(anima), I->info_name_hash[anima], I->base_path, I->base_path_hash);
|
|
|
|
return (pAnim->frame_qty - 1);
|
|
}
|
|
|
|
} // End of namespace ICB
|