scummvm/engines/icb/fn_animation.cpp
2021-12-26 21:19:38 +01:00

1513 lines
44 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/common/px_common.h"
#include "engines/icb/debug.h"
#include "engines/icb/p4_generic.h"
#include "engines/icb/common/px_scriptengine.h"
#include "engines/icb/common/px_game_object.h"
#include "engines/icb/icb.h"
#include "engines/icb/common/ptr_util.h"
#include "engines/icb/floors.h"
#include "engines/icb/mission.h"
#include "engines/icb/global_objects.h"
#include "engines/icb/object_structs.h"
#include "engines/icb/sound.h"
#include "engines/icb/res_man.h"
namespace ICB {
mcodeFunctionReturnCodes fn_apply_bullet(int32 &result, int32 *params) { return (g_mission->session->fn_apply_bullet(result, params)); }
mcodeFunctionReturnCodes fn_set_to_last_frame_generic_anim(int32 &result, int32 *params) { return (MS->fn_set_to_last_frame_generic_anim(result, params)); }
mcodeFunctionReturnCodes fn_play_custom_anim(int32 &result, int32 *params) { return (MS->fn_play_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_easy_play_generic_anim(int32 &result, int32 *params) { return (MS->fn_easy_play_generic_anim(result, params)); }
mcodeFunctionReturnCodes fn_easy_play_custom_anim(int32 &result, int32 *params) { return (MS->fn_easy_play_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_snap_face_object(int32 &result, int32 *params) { return (MS->fn_snap_face_object(result, params)); }
mcodeFunctionReturnCodes fn_face_coord(int32 &result, int32 *params) { return (MS->fn_face_coord(result, params)); }
mcodeFunctionReturnCodes fn_face_object(int32 &result, int32 *params) { return (MS->fn_face_object(result, params)); }
mcodeFunctionReturnCodes fn_set_to_first_frame_custom_anim(int32 &result, int32 *params) { return (MS->fn_set_to_first_frame_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_set_to_last_frame_custom_anim(int32 &result, int32 *params) { return (MS->fn_set_to_last_frame_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_new_apply_bullet(int32 &result, int32 *params) { return (MS->fn_new_apply_bullet(result, params)); }
mcodeFunctionReturnCodes fn_play_generic_anim(int32 &result, int32 *params) { return (MS->fn_play_generic_anim(result, params)); }
mcodeFunctionReturnCodes fn_reverse_generic_anim(int32 &result, int32 *params) { return (MS->fn_reverse_generic_anim(result, params)); }
mcodeFunctionReturnCodes fn_apply_anim_y(int32 &result, int32 *params) { return (MS->fn_apply_anim_y(result, params)); }
mcodeFunctionReturnCodes fn_set_to_first_frame_generic_anim(int32 &result, int32 *params) { return (MS->fn_set_to_first_frame_generic_anim(result, params)); }
mcodeFunctionReturnCodes fn_easy_play_generic_anim_with_pan(int32 &result, int32 *params) { return (MS->fn_easy_play_generic_anim_with_pan(result, params)); }
mcodeFunctionReturnCodes fn_fast_face_object(int32 &result, int32 *params) { return (MS->fn_fast_face_object(result, params)); }
mcodeFunctionReturnCodes fn_face_nicos_pan(int32 &result, int32 *params) { return (MS->fn_face_nicos_pan(result, params)); }
mcodeFunctionReturnCodes fn_add_y(int32 &result, int32 *params) { return (MS->fn_add_y(result, params)); }
mcodeFunctionReturnCodes fn_reverse_custom_anim(int32 &result, int32 *params) { return (MS->fn_reverse_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_fast_face_coord(int32 &result, int32 *params) { return (MS->fn_fast_face_coord(result, params)); }
mcodeFunctionReturnCodes fn_easy_play_custom_anim_with_pan(int32 &result, int32 *params) { return (MS->fn_easy_play_custom_anim_with_pan(result, params)); }
mcodeFunctionReturnCodes fn_prime_custom_anim(int32 &result, int32 *params) { return (MS->fn_prime_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_sync_with_mega(int32 &result, int32 *params) { return (MS->fn_sync_with_mega(result, params)); }
mcodeFunctionReturnCodes fn_set_feet_to_pan(int32 &result, int32 *params) { return (MS->fn_set_feet_to_pan(result, params)); }
mcodeFunctionReturnCodes fn_hard_load_generic_anim(int32 &result, int32 *params) { return (MS->fn_hard_load_generic_anim(result, params)); }
mcodeFunctionReturnCodes fn_hard_load_custom_anim(int32 &result, int32 *params) { return (MS->fn_hard_load_custom_anim(result, params)); }
mcodeFunctionReturnCodes fn_face_camera(int32 &result, int32 *params) { return (MS->fn_face_camera(result, params)); }
mcodeFunctionReturnCodes _game_session::fn_face_camera(int32 &, int32 *params) {
// params 0 face away from camera
// 1 face toward camera
PXfloat new_pan, diff;
PXcamera currentCamera;
if (!L->looping) {
currentCamera = GetCamera();
new_pan = (PXfloat)currentCamera.pan;
// reverse 180deg?
if (params[1])
new_pan += HALF_TURN;
if (new_pan > HALF_TURN)
new_pan -= FULL_TURN;
else if (new_pan < -HALF_TURN)
new_pan += FULL_TURN;
// 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
L->looping = TRUE8;
} else {
// shallow angle so snap and continue as normal
L->pan = new_pan;
return IR_CONT;
}
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
return (IR_REPEAT);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (IR_CONT);
}
// fn_set_feet_to_pan()
// ensures actual pan is what we are looking at
mcodeFunctionReturnCodes _game_session::fn_set_feet_to_pan(int32 &, int32 *) {
// we face straight ahead
I->lookBone.boneTarget.vz = (int16)(0);
// and our pan is set to the looking_pan
L->pan = M->looking_pan;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_face_coord(int32 &, int32 *params) {
// params 0 target x
// 1 target z
// return IR_CONT or
// IR_REPEAT
if (!L->looping) {
// setup
if (Calc_target_pan((PXreal)params[0], (PXreal)params[1], L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (IR_CONT); // will have possibly made a tiny snap
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
return (IR_REPEAT);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_face_nicos_pan(int32 &, int32 *params) {
// params 0 target nico
// 1 reserved for future use
// return IR_CONT or
// IR_REPEAT
_feature_info *start_pos;
PXfloat new_pan, diff;
const char *nico_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
// setup
start_pos = (_feature_info *)features->Try_fetch_item_by_name(nico_name);
if (!start_pos)
Fatal_error("no NICO marker (fn_face_nico) ob %s, nico %s", object->GetName(), nico_name);
new_pan = start_pos->direction;
// 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
L->looping = TRUE8;
} else {
// shallow angle so snap and continue as normal
L->pan = new_pan;
return IR_CONT;
}
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
return (IR_REPEAT);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_face_object(int32 &, int32 *params) {
// params target object
// return IR_CONT or
// IR_REPEAT
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
// setup
_logic *log;
uint32 id = objects->Fetch_item_number_by_name(object_name);
log = Fetch_object_struct(id);
if (log->image_type == PROP) {
if (Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, M->actor_xyz.x, M->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (IR_CONT); // will have possibly made a tiny snap
} else {
if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (IR_CONT); // will have possibly made a tiny snap
}
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
return (IR_REPEAT);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_fast_face_object(int32 &, int32 *params) {
// params 0 target object
// 1 speed up
// return IR_CONT or
// IR_REPEAT
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
// setup
_logic *log;
uint32 id = objects->Fetch_item_number_by_name(object_name);
log = Fetch_object_struct(id);
if (log->image_type == PROP) {
if (Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, M->actor_xyz.x, M->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (IR_CONT); // will have possibly made a tiny snap
} else {
if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (IR_CONT); // will have possibly made a tiny snap
}
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, params[1]);
return (IR_REPEAT);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_fast_face_coord(int32 &, int32 *params) {
// params 0 x
// 1 z
// 2 speed up
// return IR_CONT or
// IR_REPEAT
if (!L->looping) {
if (Calc_target_pan((PXreal)params[0], (PXreal)params[1], L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (IR_CONT); // will have possibly made a tiny snap
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, params[2]);
return (IR_REPEAT);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (IR_CONT);
}
bool8 _game_session::Need_to_turn_to_face_object(uint32 id) {
// is a turn required to face an object
// used by chi
_logic *log;
log = Fetch_object_struct(id);
if (log->image_type == PROP)
Fatal_error("fast_face_object = target must be mega");
if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
// turn required
return TRUE8;
}
return (FALSE8); // may even have made a tiny snap
}
bool8 _game_session::fast_face_object(uint32 id, uint32 speed) {
// called in engine
// for example from chi logic
if (!L->looping) {
// setup
_logic *log;
log = Fetch_object_struct(id);
if (log->image_type == PROP)
Fatal_error("fast_face_object = target must be mega");
if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
// turn required
L->looping = TRUE8;
} else
return (TRUE8); // will have possibly made a tiny snap
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, speed);
return (FALSE8);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (TRUE8);
}
bool8 _game_session::fast_face_rnd(uint32 speed) {
PXfloat new_pan, diff;
if (!L->looping) {
// pick a random pan
new_pan = (FULL_TURN * (g_icb->getRandomSource()->getRandomNumber(359 - 1))) / 360;
// 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
L->looping = TRUE8;
} else
return TRUE8; // random was too tiny to bother
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, speed);
return (FALSE8);
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (TRUE8);
}
mcodeFunctionReturnCodes _game_session::fn_snap_face_object(int32 &, int32 *params) {
// force pan - no animation
// params 0 target object
// return IR_CONT
_logic *log;
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
Zdebug("fn_snap_face_object [%s]", object_name);
uint32 id = objects->Fetch_item_number_by_name(object_name);
if (id == 0xffffffff)
Fatal_error("fn_snap_face_object cant find target object %s", object_name);
log = Fetch_object_struct(id);
if (log->image_type == PROP) {
if (Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, M->actor_xyz.x, M->actor_xyz.z)) {
L->pan = M->actual_target_pan;
M->actual_target_pan = REAL_ZERO;
}
} else {
if (Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z)) {
L->pan = M->actual_target_pan;
M->actual_target_pan = REAL_ZERO;
}
}
return (IR_CONT);
}
bool8 _game_session::speech_face_object(uint32 tar_id) {
// custom system for speech
// return TRUE8 - more to do
// FALSE - done
bool8 res;
if (!L->looping) {
// setup
_logic *log;
log = Fetch_object_struct(tar_id);
if (log->image_type == VOXEL)
res = Calc_target_pan(log->mega->actor_xyz.x, log->mega->actor_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z);
else
res = Calc_target_pan(log->prop_xyz.x, log->prop_xyz.z, L->mega->actor_xyz.x, L->mega->actor_xyz.z);
if (res) {
// turn required
L->looping = TRUE8;
} else { // will have possibly made a tiny snap
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
return (FALSE8); // done!
}
}
// still got some to go
if (M->target_pan) {
Animate_turn_to_pan(__TURN_ON_THE_SPOT_CLOCKWISE, 1);
return (TRUE8); // more to do
}
// we're done
L->looping = FALSE8;
// set to stand
L->cur_anim_type = __STAND;
L->anim_pc = 0;
Zdebug(" finished");
return (FALSE8); // done
}
mcodeFunctionReturnCodes _game_session::fn_reverse_generic_anim(int32 &, int32 *params) {
// params 0 ascii name of anim
// return IR_CONT or
// IR_REPEAT
bool8 ret;
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
// setup
M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
L->looping = 100;
ANIM_CHECK(M->next_anim_type);
L->list[0] = HashString(anim_name);
}
if (L->looping == 100) {
// setup
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
return IR_REPEAT;
L->cur_anim_type = M->next_anim_type; // anim now in memory
L->looping = TRUE8;
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); //
// set last frame
L->anim_pc = anim->frame_qty - 2;
return (IR_REPEAT);
}
// ok, we are simply animating through the frames
// last frame is currently displayed?
if (!L->anim_pc) {
L->looping = FALSE8;
return (IR_CONT);
}
// shift character and frame forward by the amount appropriate
ret = MS->Reverse_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
if (!ret) { // could not move forward?
L->looping = FALSE8;
return (IR_CONT);
}
return (IR_REPEAT);
}
mcodeFunctionReturnCodes _game_session::fn_play_generic_anim(int32 &, int32 *params) {
// params 0 ascii name of anim
// return IR_CONT or
// IR_REPEAT
bool8 ret;
const char *anim_name = nullptr;
if (params && params[0]) {
anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
}
if (!L->looping) {
// setup
M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
L->looping = 100;
ANIM_CHECK(M->next_anim_type);
L->list[0] = HashString(anim_name);
}
if (L->looping == 100) {
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
return IR_REPEAT;
// anim found and started ok
L->looping = TRUE8;
L->cur_anim_type = M->next_anim_type; // anim now in memory
// get animation
ANIM_CHECK(L->cur_anim_type);
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); //
// advance the frame
L->anim_pc = anim->frame_qty - 2;
Advance_frame_and_motion(L->cur_anim_type, 0, 1);
L->anim_pc = 0;
return (IR_REPEAT);
}
// ok, we are simply animating through the frames
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); //
// last frame is currently displayed?
if ((int32)(L->anim_pc + M->anim_speed) >= (anim->frame_qty - 1)) {
L->looping = FALSE8;
return (IR_CONT);
}
// shift character and frame forward by the amount appropriate
ret = Advance_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
if (!ret) { // could not move forward?
L->looping = FALSE8;
return (IR_CONT);
}
// more to do - come back again next cycle
return (IR_REPEAT);
}
mcodeFunctionReturnCodes _game_session::fn_easy_play_generic_anim(int32 &, int32 *params) {
// barriers are ignored!
// params 0 ascii name of anim
// return IR_CONT or
// IR_REPEAT
const char *anim_name = nullptr;
if (params && params[0]) {
anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
}
if (!L->looping) {
// setup
M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
L->looping = 100;
ANIM_CHECK(M->next_anim_type);
L->list[0] = HashString(anim_name);
}
if (L->looping == 100) {
// setup
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
return IR_REPEAT;
L->cur_anim_type = M->next_anim_type; // anim now in memory
L->looping = TRUE8;
// advance the frame
// get animation
ANIM_CHECK(L->cur_anim_type);
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); //
L->anim_pc = anim->frame_qty - 2;
Easy_frame_and_motion(L->cur_anim_type, 0, 1);
L->anim_pc = 0;
return (IR_REPEAT);
}
// ok, we are simply animating through the frames
// get animation
ANIM_CHECK(L->cur_anim_type);
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);
// last frame is currently displayed?
if ((int32)(L->anim_pc + M->anim_speed) >= (anim->frame_qty - 1)) {
L->looping = FALSE8;
return (IR_CONT);
}
// shift character and frame forward by the amount appropriate
MS->Easy_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
// more to do - come back again next cycle
return (IR_REPEAT);
}
mcodeFunctionReturnCodes _game_session::fn_easy_play_generic_anim_with_pan(int32 &, int32 *params) {
// barriers are ignored!
// params 0 ascii name of anim
// return IR_CONT or
// IR_REPEAT
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
// setup
M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
L->looping = 100;
ANIM_CHECK(M->next_anim_type);
L->list[0] = HashString(anim_name);
}
if (L->looping == 100) {
// setup
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
return IR_REPEAT;
L->cur_anim_type = M->next_anim_type; // anim now in memory
L->looping = TRUE8;
// advance the frame
// get animation
ANIM_CHECK(L->cur_anim_type);
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); //
L->anim_pc = anim->frame_qty - 2;
Easy_frame_motion_and_pan(L->cur_anim_type, 0);
L->anim_pc = 0;
return (IR_REPEAT);
}
// ok, we are simply animating through the frames
// get animation
ANIM_CHECK(L->cur_anim_type);
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); //
// last frame is currently displayed?
if ((int32)(L->anim_pc + 1) == (anim->frame_qty - 1)) {
L->looping = FALSE8;
return (IR_CONT);
}
// shift character and frame forward by the amount appropriate
MS->Easy_frame_motion_and_pan(L->cur_anim_type, 0);
// more to do - come back again next cycle
return (IR_REPEAT);
}
mcodeFunctionReturnCodes _game_session::fn_easy_play_custom_anim_with_pan(int32 &, int32 *params) {
// barriers are ignored!
// pan is updated
// params 0 ascii name of anim
// return IR_CONT or
// IR_REPEAT
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
Zdebug("fn_easy_play_custom_anim_with_pan %s %s", object->GetName(), anim_name);
if (!L->looping) {
// set anim up
I->Init_custom_animation(anim_name);
Reset_cur_megas_custom_type();
L->looping = 100; // have to distinguish between first time in and first cycle with anim in memory
ANIM_CHECK(__NON_GENERIC);
L->list[0] = HashString(anim_name);
}
// anim is in memory so do first frame then pass on to normal logic path
if (L->looping == 100) {
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
L->cur_anim_type = __PROMOTED_NON_GENERIC;
L->anim_pc = 0;
L->looping = TRUE8;
return (IR_REPEAT);
}
// ok, we are simply animating through the frames
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); //
// last frame is currently displayed?
if ((int32)(L->anim_pc + 1) == (anim->frame_qty - 1)) {
L->looping = FALSE8;
return (IR_CONT);
}
// shift character and frame forward by the amount appropriate
MS->Easy_frame_motion_and_pan(L->cur_anim_type, 0);
// more to do - come back again next cycle
return (IR_REPEAT);
}
mcodeFunctionReturnCodes _game_session::fn_set_to_last_frame_generic_anim(int32 &, int32 *params) {
// params 0 ascii name of anim
// return IR_CONT or IR_STOP if error
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
L->looping = 100;
ANIM_CHECK(M->next_anim_type);
L->list[0] = HashString(anim_name);
}
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
return IR_REPEAT;
L->cur_anim_type = M->next_anim_type; // anim now in memory
// ok, set to last frame
// get animation
ANIM_CHECK(L->cur_anim_type);
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); //
// set to last frame
L->anim_pc = anim->frame_qty - 2; // if 10 frames then 10+1 (looper) == 11 meaning 9 is last displayable frame number
L->looping = 0;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_set_to_first_frame_generic_anim(int32 &, int32 *params) {
// params 0 ascii name of anim
// return IR_CONT or IR_STOP if error
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
M->next_anim_type = Fetch_generic_anim_from_ascii(anim_name);
L->looping = 100;
ANIM_CHECK(M->next_anim_type);
}
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(M->next_anim_type), I->info_name_hash[M->next_anim_type], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(M->next_anim_type), I->anim_name_hash[M->next_anim_type], I->base_path, I->base_path_hash)))
return IR_REPEAT;
L->cur_anim_type = M->next_anim_type; // anim now in memory
// set to last frame
L->anim_pc = 0; // first
L->looping = 0;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_set_to_first_frame_custom_anim(int32 &, int32 *params) {
// set to first frame of a custom animation
// must use fn-set-custom before this
// params 0 name of anim
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) { // once
I->Init_custom_animation(anim_name);
L->looping = 1;
ANIM_CHECK(__NON_GENERIC);
}
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) && (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
L->cur_anim_type = __PROMOTED_NON_GENERIC;
L->anim_pc = 0;
L->looping = 0;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_set_to_last_frame_custom_anim(int32 &, int32 *params) {
// set to last frame of a custom animation
// must use fn-set-custom before this
// params 0 name of anim
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) { // once
I->Init_custom_animation(anim_name);
L->looping = 1;
ANIM_CHECK(__NON_GENERIC);
}
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) && (!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
L->cur_anim_type = __PROMOTED_NON_GENERIC;
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);
// set to last frame
L->anim_pc = anim->frame_qty - 2; // if 10 frames then 10+1 (looper) == 11 meaning 9 is last displayable frame number
L->looping = 0;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_hard_load_generic_anim(int32 &, int32 *params) {
// get generic anim into memory NOW
// params 0 name of anim
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
__mega_set_names load;
load = Fetch_generic_anim_from_ascii(anim_name);
ANIM_CHECK(load);
rs_anims->Res_open(I->get_info_name(load), I->info_name_hash[load], I->base_path, I->base_path_hash);
if (Object_visible_to_camera(cur_id))
rs_anims->Res_open(I->get_anim_name(load), I->anim_name_hash[load], I->base_path, I->base_path_hash);
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_hard_load_custom_anim(int32 &, int32 *params) {
// get custom anim into memory NOW
// params 0 name of anim
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
I->Init_custom_animation(anim_name);
Reset_cur_megas_custom_type();
ANIM_CHECK(__NON_GENERIC);
rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash); //
if (Object_visible_to_camera(cur_id))
rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash);
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_prime_custom_anim(int32 &, int32 *params) {
// get custom anim into memory
// params 0 name of anim
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
I->Init_custom_animation(anim_name);
Reset_cur_megas_custom_type();
L->looping = 100;
ANIM_CHECK(__NON_GENERIC);
}
// anim is in memory so do first frame then pass on to normal logic path
if (L->looping == 100) {
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
}
// anim is now in memory
L->looping = 0;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_play_custom_anim(int32 &result, int32 *params) {
// a mega character plays an anim
// the anim IS NOT part of the generic set
// barriers are checked
// params 0 name of anim
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
I->Init_custom_animation(anim_name);
Reset_cur_megas_custom_type();
L->looping = 100;
ANIM_CHECK(__NON_GENERIC);
L->list[0] = HashString(anim_name);
}
// anim is in memory so do first frame then pass on to normal logic path
if (L->looping == 100) {
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
L->cur_anim_type = __PROMOTED_NON_GENERIC;
L->anim_pc = 0;
L->looping = TRUE8;
return IR_REPEAT;
}
return (fn_play_generic_anim(result, nullptr));
}
mcodeFunctionReturnCodes _game_session::fn_reverse_custom_anim(int32 &, int32 *params) {
// a mega character plays an anim backward
// the anim IS NOT part of the generic set
// barriers are checked
// params 0 name of anim
bool8 ret;
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
I->Init_custom_animation(anim_name);
Reset_cur_megas_custom_type();
L->looping = 100;
ANIM_CHECK(__NON_GENERIC);
L->list[0] = HashString(anim_name);
}
// anim is in memory so do first frame then pass on to normal logic path
if (L->looping == 100) {
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
L->cur_anim_type = __PROMOTED_NON_GENERIC;
PXanim *anim = (PXanim *)rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash); //
L->anim_pc = anim->frame_qty - 2;
L->looping = TRUE8;
return IR_REPEAT;
}
// last frame is currently displayed?
if (!L->anim_pc) {
L->looping = FALSE8;
return IR_CONT;
}
// shift character and frame forward by the amount appropriate
ret = MS->Reverse_frame_and_motion(L->cur_anim_type, 0, M->anim_speed);
if (!ret) { // could not move forward?
L->looping = FALSE8;
return IR_CONT;
}
// more to do - come back again next cycle
return IR_REPEAT;
}
mcodeFunctionReturnCodes _game_session::fn_easy_play_custom_anim(int32 &result, int32 *params) {
// a mega character plays an anim
// the anim IS NOT part of the generic set
// barriers are ignored
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
I->Init_custom_animation(anim_name);
Reset_cur_megas_custom_type();
L->looping = 100; // have to distinguish between first time in and first cycle with anim in memory
ANIM_CHECK(__NON_GENERIC);
L->list[0] = HashString(anim_name);
}
// anim is in memory so do first frame then pass on to normal logic path
if (L->looping == 100) {
// psx async loading check - is file in memory
if (!rs_anims->Res_open(I->get_info_name(__NON_GENERIC), I->info_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash))
return IR_REPEAT;
if ((Object_visible_to_camera(cur_id)) &&
(!rs_anims->Res_open(I->get_anim_name(__NON_GENERIC), I->anim_name_hash[__NON_GENERIC], I->base_path, I->base_path_hash)))
return IR_REPEAT;
I->Promote_non_generic(); // swap out the _NON_GENERIC to safe place
L->cur_anim_type = __PROMOTED_NON_GENERIC;
L->anim_pc = 0;
L->looping = TRUE8;
return (IR_REPEAT);
}
return (fn_easy_play_generic_anim(result, nullptr));
}
mcodeFunctionReturnCodes _game_session::fn_apply_bullet(int32 &, int32 *) {
// a bullet is to hit an object
// we simply call its gun_shot socket
// this system circumvents the logic context switch
// this should be used when guards shoot the player... and when chi shoots guards
// params 0 name of target
M->SetDynamicLight(1, 255, 255, 255, 0, 150, 100, 200); // 2 metres
// Hey we are shooting someone (muzzle flash on / cartridge case on (we my want to split this!)
M->is_shooting = TRUE8;
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_sync_with_mega(int32 &, int32 *params) {
// params 0 name of target mega
// 1 nowt
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
if (!L->looping) {
L->list[0] = objects->Fetch_item_number_by_name(mega_name);
L->list[1] = 42; // we are here
L->looping = TRUE8; // dont do this again
}
if (logic_structs[L->list[0]]->list[1] == 42) {
// we are going first
L->list[1] = 43;
L->looping = 0;
return IR_CONT; // byeee
} else if (logic_structs[L->list[0]]->list[1] == 43) {
// its gone first
logic_structs[L->list[0]]->list[1] = 0; // clean it up
L->list[1] = 0; // clean us up
L->looping = 0;
return IR_CONT;
}
return IR_REPEAT;
}
mcodeFunctionReturnCodes _game_session::fn_new_apply_bullet(int32 &, int32 *params) {
// a bullet is to hit an object with a percentage chance of hitting
// on a hit simply call its gun_shot socket
// this system circumvents the logic context switch
// this should be used when guards shoot the player... and when chi shoots guards
// params 0 name of target
// 1 percentage chance
uint32 tid;
int32 retval;
int32 rnd;
PXreal sub1, sub2, len;
bool8 crouched = FALSE8;
const char *target_name = (const char *)MemoryUtil::resolvePtr(params[0]);
// gun sound
if (logic_structs[cur_id]->sfxVars[GUNSHOT_SFX_VAR] != 0)
RegisterSound(cur_id, nullptr, logic_structs[cur_id]->sfxVars[GUNSHOT_SFX_VAR], gunDesc, (int8)127); // have to use full version so we can give hash instead of string
else
RegisterSound(cur_id, defaultGunSfx, gunDesc); // use small version as we have string not hash
// shot types
// 0 - nothing on screen
// 1 - normal light/muzzleflash/cartridge case
// default is 1
int32 shotType = object->GetIntegerValueOrDefault("gun_effects", 1);
// if mega then do dynamic light (only if shotType isnt 0
if ((logic_structs[cur_id]->image_type == VOXEL) && (shotType == 1)) {
// dynamic light
M->SetDynamicLight(1, 255, 255, 255, 0, 150, 100, 200); // 2 metres
// Hey we are shooting someone (muzzle flash on / cartridge case on (we my want to split this!)
M->is_shooting = TRUE8;
}
// get id
tid = objects->Fetch_item_number_by_name(target_name);
// how near
if (L->image_type == PROP) { // we are prop
sub1 = (PXreal)L->prop_xyz.x - logic_structs[tid]->mega->actor_xyz.x;
sub2 = (PXreal)L->prop_xyz.z - logic_structs[tid]->mega->actor_xyz.z;
} else {
crouched = M->Is_crouched();
sub1 = (PXreal)M->actor_xyz.x - logic_structs[tid]->mega->actor_xyz.x;
sub2 = (PXreal)M->actor_xyz.z - logic_structs[tid]->mega->actor_xyz.z;
}
// dist
len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
// hit chance
rnd = g_icb->getRandomSource()->getRandomNumber(100 - 1);
// we didn't miss
int32 missed = 0;
// user hit chance of 0 means always miss
if (params[1]) {
// age chance true, or within distance, or crouching
if ((rnd < params[1]) || (len < (PXreal)(ALWAYS_HIT_DIST * ALWAYS_HIT_DIST)) || (crouched)) { // hit
// if prop is firing then target must be on A floor
// ELSE
// if mega firing then both y coords must be the same
if (((L->image_type == PROP) && (floor_def->On_a_floor(logic_structs[tid]->mega))) ||
((L->image_type == VOXEL) && (M->actor_xyz.y == logic_structs[tid]->mega->actor_xyz.y))) {
// kick kinematic for the player if we are shooting the player
if (tid == player.Fetch_player_id()) {
MS->player.being_shot = 3; // cant shoot for 3 cycles (engine anim over three frames)
MS->player.shot_by_id = (int8)cur_id; // shot by us...!
c_game_object *ob = (c_game_object *)objects->Fetch_item_by_number(player.Fetch_player_id());
int32 ret = ob->GetVariable("hits");
uint32 hits = ob->GetIntegerVariable(ret);
PXreal subp1, subp2;
if (L->image_type == PROP) { // we are prop
subp1 = logic_structs[tid]->mega->actor_xyz.x - L->prop_xyz.x;
subp2 = logic_structs[tid]->mega->actor_xyz.z - L->prop_xyz.z;
} else {
subp1 = logic_structs[tid]->mega->actor_xyz.x - M->actor_xyz.x;
subp2 = logic_structs[tid]->mega->actor_xyz.z - M->actor_xyz.z;
}
PXreal dist = ((subp1 * subp1) + (subp2 * subp2));
if (dist < PXreal(200 * 200)) {
if (hits >= 4)
hits -= 4;
else
hits = 0;
} else if (dist < PXreal(500 * 500)) {
if (hits >= 2)
hits -= 2;
else
hits = 0;
} else if (hits)
hits--;
ob->SetIntegerVariable(ret, hits);
}
MS->Call_socket(tid, "gun_shot", &retval); // the hit takes
// cancel any speech
Exit_speech(tid);
}
} else {
// ricochet sound
missed = 1;
}
} else { // missed
missed = 1;
}
if (missed) {
// gun sound
if (logic_structs[cur_id]->sfxVars[RICOCHET_SFX_VAR] != 0)
RegisterSound(cur_id, nullptr, logic_structs[cur_id]->sfxVars[RICOCHET_SFX_VAR], ricochetDesc,
(int8)127); // have to use full version so we can give hash instead of string
else
RegisterSound(cur_id, defaultRicochetSfx, ricochetDesc); // use small version as we have string not hash
}
// if a guard, adjust the bullet/clip figures
if (logic_structs[cur_id]->image_type == VOXEL)
if (M->is_evil) {
// take chi and player out of the equation
int32 ret = object->GetVariable("cur_bullets");
if (ret != -1) {
int32 result = object->GetIntegerVariable(ret);
if (!result) { // no bullets
int32 clipret = object->GetVariable("number_of_clips");
int32 no_clips = object->GetIntegerVariable(clipret);
if (no_clips == -1)
Fatal_error("object has no 'number_of_clips' variable");
if (no_clips) { // has clips left
no_clips--; // 1 less
object->SetIntegerVariable(clipret, no_clips);
int32 bull_per_clip = MS->player.GetBulletsPerClip();
object->SetIntegerVariable(ret, bull_per_clip); // reload the gun
}
} else { // has bullets, fire one
result--;
object->SetIntegerVariable(ret, result); // reload
}
}
}
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_apply_anim_y(int32 &, int32 *params) {
// add the y offset from frame 0 to end frame
// used for climbing ladders and stairs and so on where the height gets moved
// params 0 generic anim name
uint32 k;
PXreal y_next;
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[0]);
// search for the named generic anim - cant use __ANIM_NAME from script unfortunately
for (k = 0; k < __TOTAL_ANIMS; k++) {
// we must search the table
if (!strcmp(anim_name, master_anim_name_table[k].name)) {
// found!
ANIM_CHECK(k);
PXanim *pAnim = (PXanim *)rs_anims->Res_open(I->get_info_name(k), I->info_name_hash[k], I->base_path, I->base_path_hash); //
PXreal x, z;
PXreal yend;
PXreal ystart;
PXFrameEnOfAnim(pAnim->frame_qty - 1, pAnim)->markers[ORG_POS].GetXYZ(&x, &yend, &z);
PXFrameEnOfAnim(0, pAnim)->markers[ORG_POS].GetXYZ(&x, &ystart, &z);
y_next = yend - ystart;
Tdebug("y_apply.txt", "%s offset - %3.1f", (const char *)I->get_info_name(k), y_next);
M->actor_xyz.y += y_next;
return IR_CONT;
}
}
Fatal_error("fn_apply_anim_y [%s] cant find generic anim [%s]", object->GetName(), anim_name);
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_add_y(int32 &, int32 *params) {
// add y value to a mega
// params 0 value
if (L->image_type == PROP)
Fatal_error("fn_add_y cant be used on a prop - %s", object->GetName());
M->actor_xyz.y += params[0];
Tdebug("fn_add_y.txt", "%s +%d to %3.1f", object->GetName(), params[0], M->actor_xyz.y);
return IR_CONT;
}
} // End of namespace ICB