scummvm/engines/icb/logic.cpp
2020-10-06 09:51:20 +02:00

993 lines
29 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/common/px_common.h"
#include "engines/icb/debug.h"
#include "engines/icb/p4_generic.h"
#include "engines/icb/p4_generic.h"
#include "engines/icb/common/px_common.h"
#include "engines/icb/common/px_scriptengine.h"
#include "engines/icb/common/px_game_object.h"
#include "engines/icb/common/px_maths.h"
#include "engines/icb/common/ptr_util.h"
#include "engines/icb/mission.h"
#include "engines/icb/session.h"
#include "engines/icb/global_objects.h"
#include "engines/icb/object_structs.h"
#include "engines/icb/res_man.h"
#include "engines/icb/light.h"
namespace ICB {
mcodeFunctionReturnCodes fn_get_weapon(int32 &result, int32 *params) { return (MS->fn_get_weapon(result, params)); }
mcodeFunctionReturnCodes fn_pass_flag_to_engine(int32 &result, int32 *params) { return (MS->fn_pass_flag_to_engine(result, params)); }
mcodeFunctionReturnCodes fn_restart_object(int32 &result, int32 *params) { return (MS->fn_restart_object(result, params)); }
mcodeFunctionReturnCodes fn_get_persons_weapon(int32 &result, int32 *params) { return (MS->fn_get_persons_weapon(result, params)); }
mcodeFunctionReturnCodes fn_kill_me(int32 &result, int32 *params) { return (MS->fn_kill_me(result, params)); }
mcodeFunctionReturnCodes fn_kill_object(int32 &result, int32 *params) { return (MS->fn_kill_object(result, params)); }
mcodeFunctionReturnCodes fn_new_script(int32 &result, int32 *params) { return (MS->fn_new_script(result, params)); }
mcodeFunctionReturnCodes fn_gosub(int32 &result, int32 *params) { return (MS->fn_gosub(result, params)); }
mcodeFunctionReturnCodes fn_set_to_exlusive_coords(int32 &result, int32 *params) { return (MS->fn_set_to_exlusive_coords(result, params)); }
mcodeFunctionReturnCodes fn_object_rerun_logic_context(int32 &result, int32 *params) { return (MS->fn_object_rerun_logic_context(result, params)); }
mcodeFunctionReturnCodes fn_set_strike_overide(int32 &result, int32 *params) { return (MS->fn_set_strike_overide(result, params)); }
mcodeFunctionReturnCodes fn_set_shoot_overide(int32 &result, int32 *params) { return (MS->fn_set_shoot_overide(result, params)); }
mcodeFunctionReturnCodes fn_is_player_running(int32 &result, int32 *params) { return (MS->fn_is_player_running(result, params)); }
mcodeFunctionReturnCodes fn_is_player_walking(int32 &result, int32 *params) { return (MS->fn_is_player_walking(result, params)); }
mcodeFunctionReturnCodes fn_set_dynamic_light(int32 &result, int32 *params) { return (MS->fn_set_dynamic_light(result, params)); }
mcodeFunctionReturnCodes speak_set_dynamic_light(int32 &result, int32 *params) { return (MS->speak_set_dynamic_light(result, params)); }
mcodeFunctionReturnCodes fn_activate_sparkle(int32 &result, int32 *params) { return (MS->fn_activate_sparkle(result, params)); }
mcodeFunctionReturnCodes fn_deactivate_sparkle(int32 &result, int32 *params) { return (MS->fn_deactivate_sparkle(result, params)); }
// fn_activate_sparkle(x,y,z);
mcodeFunctionReturnCodes _game_session::fn_activate_sparkle(int32 &, int32 *params) {
PXreal rx, ry, rz;
L->GetPosition(rx, ry, rz);
int x, y, z;
x = (int)rx;
y = (int)ry;
z = (int)rz;
L->sparkleX = (short)(params[0] - x);
L->sparkleY = (short)(params[1] - y);
L->sparkleZ = (short)(params[2] - z);
L->sparkleOn = TRUE8;
return IR_CONT;
}
// fn_deactivate_sparkle(x,y,z);
mcodeFunctionReturnCodes _game_session::fn_deactivate_sparkle(int32 &, int32 *) {
L->sparkleOn = FALSE8;
return IR_CONT;
}
// these are the con and des-tructors for the game object objects
// they are homeless and so stay here
void _logic::___init(const char *name) {
int j;
// set objects name
Set_string(name, ob_name);
// This object is active
ob_status = OB_STATUS_NOT_HELD;
// give full run rights
big_mode = __SCRIPT;
// clear the pointer to the voxel object specific structure
// if these are NULL then the object is non mega
voxel_info = NULL;
mega = NULL; // clear mega info
// defaults to a prop
image_type = PROP;
// defaults to no-type-set
object_type = __NO_TYPE_SET;
// defaults to responding to events, LOS etc.
do_not_disturb = FALSE8;
// set crude switch to off
context_request = FALSE8;
// this is important - reset the looping flag
looping = 0;
pause = 0;
// an object must register itself for interaction
player_can_interact = FALSE8;
// reset pan adjust value
pan_adjust = ZERO_TURN;
// reset auto pan stuff
auto_display_pan = ZERO_TURN;
auto_panning = FALSE8;
// clear the logic tree
for (j = 0; j < TREE_SIZE; j++) {
logic[j] = 0;
logic_ref[j] = 0;
}
// set owner rect to something safe - for fn_on_screen calls by props
owner_floor_rect = 0;
cur_anim_type = __NO_ANIM;
// reset the custom script list to 0 items
total_list = 0;
// defualt to tight pan interact type
three_sixty_interact = FALSE8;
// defaul to no prop coords set
prop_coords_set = FALSE8;
// not in a conversation
conversation_uid = NO_SPEECH_REQUEST;
// height is straight ahead...
look_height = -1;
// logic culling
hold_mode = none;
camera_held = FALSE8;
// all sound effects to default
sfxVars[0] = sfxVars[1] = sfxVars[2] = 0;
// sparkle off by default
sparkleOn = FALSE8;
}
void _mega::___init() {
// need to reset target_pan in the same way as we need to reset looping in _logic()
auto_target_pan = ZERO_TURN; // auto target
target_pan = ZERO_TURN; // reset turn-on-spot-to pan
cur_parent = NULL;
cur_slice = 0;
number_of_barriers = 0; // number of local barriers associated with mega position
number_of_nudge = 0; // number of local barriers associated with mega position
number_of_animating = 0; // animating barriers
target_id = 0;
interacting = FALSE8;
// set some chr$ defaults
weapon = __NOT_SET;
motion = __MOTION_WALK;
custom = FALSE8;
has_exclusive_coords = FALSE8; // cord and chi may overide this
is_evil = FALSE8;
make_remora_beep = FALSE8;
m_phase = 0;
m_main_route.total_points = 0; // final route size
m_main_route.diag_bars = 0;
m_main_route.number_of_diag_bars = 0;
use_strike_script = 0;
use_fire_script = 0;
on_players_floor = FALSE8;
anim_speed = 1;
pushed = 0; // coords not pushed
reverse_route = 0;
// Set a default speech colour for megas.
speech_red = 0;
speech_green = 230;
speech_blue = 230;
next_anim_type = __NO_ANIM;
// initialise the dynamic light...
InitDynamicLight();
// Not currently shooting
is_shooting = FALSE8;
drawShadow = TRUE8; // shadows on
// async
asyncing = 0; // not loading file
async_list_pos = 0; // start of list
async_weapon = __NOT_SET;
// generic stair info for shadow correction
on_stairs = TRUE8;
inShadePercentage = DEFAULT_INSHADE_PERCENTAGE; // default shade value
// footstep stuff
footstep_status = 0; // current foot left/right status...
footstep_weight = 100; // weight of footstep (100 is Cord) 255 maximum...
footstep_special = FALSE8; // whether special or not...
// default router extrapolation size
extrap_size = 25;
// set to draw
display_me = TRUE8;
dead = FALSE8; // still alive!
// camera control
y_locked = FALSE8;
// breath off as default
breath.on = 0;
}
mcodeFunctionReturnCodes _game_session::fn_set_to_exlusive_coords(int32 &, int32 *) {
if (!logic_structs[cur_id]->mega)
Fatal_error("terminal misuse of fn_set_to_exclusive_coords");
logic_structs[cur_id]->mega->has_exclusive_coords = TRUE8;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_get_persons_weapon(int32 &result, int32 *params) {
// return the weapon type to the script
// params 0 name of mega
uint32 id;
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
id = objects->Fetch_item_number_by_name(mega_name);
if (id == 0xffffffff)
Fatal_error("fn_get_persons_weapon: object [%s] does not exist", mega_name);
if (!logic_structs[id]->mega)
Fatal_error("fn_get_persons_weapon: object [%s] not a mega", mega_name);
result = logic_structs[id]->mega->Fetch_armed_status();
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_get_weapon(int32 &result, int32 *) {
// return the weapon type to the script
// no params
result = Fetch_cur_megas_armed_status();
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_is_player_running(int32 &result, int32 *) {
if (player.player_status == RUNNING)
result = 1;
else
result = 0;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_is_player_walking(int32 &result, int32 *) {
if (player.player_status == WALKING)
result = 1;
else
result = 0;
return IR_CONT;
}
bool8 _game_session::Fetch_cur_megas_armed_status() {
// return a megas armed status
if (!M)
Fatal_error("%d not a mega but called Fetch_megas_weapon_type", cur_id);
return (M->Fetch_armed_status());
}
__weapon _game_session::Fetch_cur_megas_pose() {
// return a megas weapon type - can be called from outside of _game_session
if (!M)
Fatal_error("%d not a mega but called Fetch_megas_weapon_type", cur_id);
return (M->Fetch_pose());
}
const char *_game_session::Fetch_cur_megas_custom_text() {
// return pointer to megas custom ascii - mine, object, etc
if (!M)
Fatal_error("%d not a mega but called Fetch_megas_weapon_type", cur_id);
return (M->custom_set);
}
bool8 _game_session::Fetch_custom() {
// return a megas weapon type - can be called from outside of _game_session
if (!M)
Fatal_error("%d not a mega but called Fetch_cur_megas_custom_type", cur_id);
return (M->Fetch_custom());
}
bool8 _mega::Fetch_custom(void) {
// return custom anim type from _mega struct
return (custom);
}
void _game_session::Reset_cur_megas_custom_type() {
// resets to __NONE the current custom type
// this is probably desireable and will save scripters doing it - or not and forgetting
if (!M)
Fatal_error("%d not a mega but called Reset_cur_megas_custom_type", cur_id);
M->custom = FALSE8; //__NONE;
}
bool8 _mega::Fetch_armed_status() {
// return weapon type from _mega struct
// see also _game_session::Fetch_cur_megas_weapon_type
return (armed_state_table[weapon]);
}
bool8 _mega::Is_crouched(void) {
// is the mega croucing - called by LOS
return (crouch_state_table[weapon]);
}
__weapon _mega::Fetch_pose(void) {
// fetch custume subset - i.e crouch, crouch_gun, unarmed, etc.
return (weapon);
}
void _game_session::Set_script(const char *script_name) {
// set the script on the current level
char *ad;
ad = (char *)scripts->Fetch_item_by_name(script_name);
L->logic[L->logic_level] = ad;
// write reference for change script checks later - i.e. FN_context_chosen_script
L->logic_ref[L->logic_level] = ad;
}
void _game_session::Context_check(uint32 script_name) {
// we have been passed a script name - we need to check if this script is the same or
// different from the one currently running on logic level 1. If the same then we do
// nothing. If different then we set the level 1 logic to this new script
char *ad;
Zdebug("context check");
ad = (char *)scripts->Try_fetch_item_by_hash(script_name);
Zdebug("context_check ad=%d ref=%d", ad, L->logic_ref[1]);
if (L->logic_ref[1] != ad) {
// write actual offset
L->logic[1] = ad;
// write reference for change script checks later - i.e. FN_context_chosen_script
L->logic_ref[1] = ad;
L->logic_level = 1; // reset to level 1
L->looping = 0; // reset the logic state flag
if (L->mega)
M->custom = FALSE8; // reset
L->pause = 0;
}
}
mcodeFunctionReturnCodes fn_context_chosen_logic(int32 &result, int32 *params) {
// the logic context script has chosen a logic to set up but we do nothing if the script is running already
// this function is used for an immediate logic change - i.e. it wont wait for animations to finish first
// params[0] ascii name of new script
return (MS->fn_context_chosen_logic(result, params));
}
mcodeFunctionReturnCodes _game_session::fn_context_chosen_logic(int32 &, int32 *params) {
// pass the (hashed) name
Context_check(params[0]); // now # number
return (IR_TERMINATE); // get us back
}
void _game_session::Shut_down_object() {
// lock out an object
// Tell the event manager to stop handling events for this object. [PS 09/12/98]
g_oEventManager->ShutDownEventProcessingForObject(cur_id);
logic_structs[cur_id]->ob_status = OB_STATUS_HELD; // lock out
prop_state_table[cur_id] = 0; // set to state 0 - in case killed because of illegal frame
Tdebug("objects_that_died.txt", "**OBJECT '%s' [id=%d] has been shut down**", object->GetName(), cur_id);
}
mcodeFunctionReturnCodes _game_session::fn_kill_me(int32 &, int32 *) {
// kill this object
Shut_down_object("fn_kill_me");
return (IR_STOP);
}
mcodeFunctionReturnCodes _game_session::fn_kill_object(int32 &, int32 *params) {
// kill this object
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
uint32 id = objects->Fetch_item_number_by_name(object_name);
if (id == 0xffffffff)
Fatal_error("fn_kill_object finds [%s] does not exist", object_name);
if (id == cur_id)
Fatal_error("fn_kill_object - dont use this function to shut self down [%s]", object_name);
// Tell the event manager to stop handling events for this object.
g_oEventManager->ShutDownEventProcessingForObject(id);
prop_state_table[id] = 0; // set to state 0 - in case killed because of illegal frame
logic_structs[id]->ob_status = OB_STATUS_HELD; // lock out
Tdebug("objects_that_died.txt", "**OBJECT '%s' shut down by fn_kill_object", object_name);
return (IR_CONT);
}
void _game_session::Shut_down_object(const char *ascii) {
// lock out an object
// Tell the event manager to stop handling events for this object.
g_oEventManager->ShutDownEventProcessingForObject(cur_id);
logic_structs[cur_id]->ob_status = OB_STATUS_HELD; // lock out
prop_state_table[cur_id] = 0; // set to state 0 - in case killed because of illegal frame
Tdebug("objects_that_died.txt", "**OBJECT '%s' [id=%d] has been shut down** %s", object->GetName(), cur_id, ascii);
}
bool8 _game_session::Console_shut_down_object(const char *name) {
// we have name of object
uint32 id = objects->Fetch_item_number_by_name(name);
if (id == 0xffffffff)
return (FALSE8);
// Tell the event manager to stop handling events for this object.
g_oEventManager->ShutDownEventProcessingForObject(id);
prop_state_table[id] = 0; // set to state 0 - in case killed because of illegal frame
logic_structs[id]->ob_status = OB_STATUS_HELD; // lock out
Tdebug("objects_that_died.txt", "**OBJECT '%s' [id=%d] has been shut down** %s", name, id, "Console_shut_down_object");
return (TRUE8);
}
bool8 _game_session::Free_object(const char *name) {
// we have name of object
uint32 id = objects->Fetch_item_number_by_name(name);
if (id == 0xffffffff)
return (FALSE8);
logic_structs[id]->ob_status = OB_STATUS_NOT_HELD; // lock out
Tdebug("objects_that_died.txt", "**OBJECT '%s' [id=%d] has been free'd by user ** %s", name, id, "Free_object");
return (TRUE8);
}
void _game_session::Console_shut_down_all_mega_objects() {
// how many objs in the mission
uint32 tot_obs = Fetch_number_of_objects();
if (tot_obs) {
Tdebug("objects_that_died.txt", "\n\nuser shutting down all mega objects");
for (uint32 j = 0; j < tot_obs; j++)
if (logic_structs[j]->mega)
Shut_down_id(j);
}
Tdebug("objects_that_died.txt", "\n\n");
}
void _game_session::Console_shut_down_all_objects() {
// how many objs in the mission
uint32 tot_obs = Fetch_number_of_objects();
if (tot_obs) {
Tdebug("objects_that_died.txt", "\n\nuser shutting down all objects");
for (uint32 j = 0; j < tot_obs; j++)
Shut_down_id(j);
}
Tdebug("objects_that_died.txt", "\n\n");
}
void _game_session::Shut_down_id(uint32 id) {
// we have id of object
// must be legal id
_ASSERT(id < MS->Fetch_number_of_objects());
logic_structs[id]->ob_status = OB_STATUS_HELD; // lock out
Tdebug("objects_that_died.txt", "**OBJECT %s [id=%d] has been shut down** %s", (const char *)logic_structs[id]->GetName(), id, "Shut_down_id");
}
mcodeFunctionReturnCodes fn_shut_down_object(int32 &result, int32 *params) {
// shut down current object - wont be logic processed any int32er
return (MS->fn_shut_down_object(result, params));
}
mcodeFunctionReturnCodes _game_session::fn_shut_down_object(int32 & /*result*/, int32 * /*params*/) {
// params none
Shut_down_object();
return (IR_STOP);
}
mcodeFunctionReturnCodes fn_pause(int32 &result, int32 *params) {
// shut down current object - wont be logic processed any int32er
return (MS->fn_pause(result, params));
}
mcodeFunctionReturnCodes _game_session::fn_pause(int32 &, int32 *params) {
// params 1 = pause value
// params: 0 pointer to object's logic structure
// 1 number of game-cycles to pause
// NB. Pause-value of 0 causes script to continue, 1 causes a 1-cycle quit, 2 gives 2 cycles, etc.
if (!L->looping) { // start the pause
L->looping = 1;
L->pause = params[0]; // no. of game cycles
}
if (L->pause) { // if non-zero
L->pause--; // decrement the pause count
return (IR_REPEAT); // drop out of script, but call this again next cycle
} else { // pause count is zerp
L->looping = 0;
return (IR_CONT); // continue script
}
}
mcodeFunctionReturnCodes fn_missing_routine(int32 &, int32 *) {
// shut down current object - wont be logic processed any int32er
Message_box("fn_missing_routine shutting down [%s]", MS->Fetch_object_name(MS->Fetch_cur_id()));
MS->Shut_down_object(" - fn_missing_routine");
return (IR_STOP);
}
mcodeFunctionReturnCodes _game_session::fn_pass_flag_to_engine(int32 & /*result*/, int32 *params) {
// script passed a value to the engine
// NOTE this is an fn_function which may be used in either normal scripts or engine called socket style scrips - that is why it is
// fn- not socket_
Fatal_error("never use fn_pass_flag_to_engine");
script_var_value = params[0];
return (IR_CONT);
}
mcodeFunctionReturnCodes _game_session::fn_object_rerun_logic_context(int32 &, int32 *params) {
// reset the named object so it reruns its logic context
// params 0 name of object
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
uint32 id = objects->Fetch_item_number_by_name(object_name);
if (id == 0xffffffff)
Fatal_error("fn_object_rerun_logic_context cant find object [%s]", object_name);
logic_structs[id]->context_request = TRUE8;
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_restart_object(int32 &, int32 *) {
// force an object to return to its logic context
Zdebug("fn_restart_object");
L->logic_level = 0; // force back down
return (IR_TERMINATE); // script to go around
}
void _game_session::Reset_all_objects() {
// force all game objects back to level 0 where they will rerun logic contexts
// if they are held this will have no effect
uint32 tot_obs = Fetch_number_of_objects();
for (uint32 j = 0; j < tot_obs; j++) {
logic_structs[j]->logic_level = 0;
logic_structs[j]->logic_ref[1] = 0;
}
}
mcodeFunctionReturnCodes _game_session::fn_new_script(int32 &, int32 *params) {
// change to a new **local** script
// params 0 name of new script
uint32 k;
char *ad;
uint32 script_hash;
const char *script_name = (const char *)MemoryUtil::resolvePtr(params[0]);
script_hash = HashString(script_name);
// try and find a script with the passed extention i.e. ???::looping
for (k = 0; k < object->GetNoScripts(); k++) {
if (script_hash == object->GetScriptNamePartHash(k)) {
// script k is the one to run
// get the address of the script we want to run
ad = (char *)scripts->Try_fetch_item_by_hash(object->GetScriptNameFullHash(k));
// write actual offset
L->logic[1] = ad;
// write reference for change script checks later - i.e. FN_context_chosen_script
L->logic_ref[1] = ad;
L->logic_level = 1; //
L->looping = 0; // reset to 0 for new logics
if (L->mega)
M->custom = FALSE8; // reset
// script interpretter shouldnt write a pc back
return (IR_TERMINATE);
}
}
Fatal_error("fn_new_script - cant find script [%s] in object [%s]", script_name, object->GetName());
return IR_CONT; // keep daft compiler happy
}
mcodeFunctionReturnCodes _game_session::fn_gosub(int32 &, int32 *params) {
// gosub to a new **local** script
// params 0 name of new script
uint32 k;
char *ad;
uint32 script_hash;
const char *script_name = (const char *)MemoryUtil::resolvePtr(params[0]);
script_hash = HashString(script_name);
if (L->logic_level != 1)
Fatal_error("object [%s] has performed an illegal gosub", object->GetName());
// try and find a script with the passed extention i.e. ???::looping
for (k = 0; k < object->GetNoScripts(); k++) {
// now check for actual script name
if (script_hash == object->GetScriptNamePartHash(k)) {
// script k is the one to run
// get the address of the script we want to run
ad = (char *)scripts->Try_fetch_item_by_hash(object->GetScriptNameFullHash(k));
// write actual offset
L->logic[2] = ad;
L->logic_level = 2; //
L->looping = 0; // reset to 0 for new logics
if (L->mega)
M->custom = FALSE8; // reset
L->old_looping = 0; // gets popped on dropoff
// script interpretter shouldnt write a pc back
return (IR_GOSUB);
}
}
Fatal_error("fn_gosub - cant find script [%s] in object [%s]", script_name, object->GetName());
return IR_CONT; // keep daft compiler happy
}
mcodeFunctionReturnCodes _game_session::fn_set_strike_overide(int32 &, int32 *params) {
// set a mega to strike overide - i.e. run a script instead of hitting them
// params 0 name of mega
// 1 0 off 1 on
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
uint32 tar = MS->objects->Fetch_item_number_by_name(mega_name);
if (tar == 0xffffffff)
Fatal_error("fn_set_strike_overide finds object [%s] does not exist", mega_name);
if (logic_structs[tar]->image_type == PROP)
Fatal_error("fn_set_strike_overide called on non mega");
logic_structs[tar]->mega->use_strike_script = (uint8)params[1];
return IR_CONT;
}
mcodeFunctionReturnCodes _game_session::fn_set_shoot_overide(int32 &, int32 *params) {
// set a mega to strike overide - i.e. run a script instead of hitting them
// params 0 name of mega
// 1 0 off 1 on
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
uint32 tar = MS->objects->Fetch_item_number_by_name(mega_name);
if (tar == 0xffffffff)
Fatal_error("fn_set_shoot_overide finds object [%s] does not exist", mega_name);
if (logic_structs[tar]->image_type == PROP)
Fatal_error("fn_set_shoot_overide called on non mega");
logic_structs[tar]->mega->use_fire_script = (uint8)params[1];
return IR_CONT;
}
void _mega::InitDynamicLight(void) {
dynLight.nStates = 1; // one state
dynLight.w = 0; // zero width
dynLight.b = 0; // zero bounce
dynLight.anu = 0; // don't use it
dynLight.type = OMNI_LIGHT; // OMNI
dynLight.ba = 0; // means nothing for an OMNI
dynLight.bs = 0; // means nothing for an OMNI
dynLight.states[0].ans2 = 0; // dont think these things are used...
dynLight.states[0].ane2 = (100 * 1) * (100 * 1);
dynLight.states[0].m = 128; // no shade...
// direction don't matter it's an OMNI light
dynLight.states[0].vx = 4096; // ignored for an OMNI light
dynLight.states[0].vy = 0; // ignored for an OMNI light
dynLight.states[0].vz = 0; // ignored for an OMNI light }
}
// fn_set_dynamic_light(cycles,r,g,b,x,y,z,falloff);
mcodeFunctionReturnCodes _game_session::fn_set_dynamic_light(int32 &, int32 *params) {
M->SetDynamicLight(params[0], // cycles
params[1], params[2], params[3], // rgb
params[4], params[5], params[6], // xyz
params[7]); // falloff
return IR_CONT;
}
// speak_set_dynamic_light("object",cycles,r,g,b,x,y,z,falloff);
mcodeFunctionReturnCodes _game_session::speak_set_dynamic_light(int32 &, int32 *params) {
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
int obj_id = objects->Fetch_item_number_by_name(object_name);
logic_structs[obj_id]->mega->SetDynamicLight(params[1], // cycles
params[2], params[3], params[4], // rgb
params[5], params[6], params[7], // xyz
params[8]); // falloff
return IR_CONT;
}
// SetDynamicLight(cycles,r,g,b,x,y,z);
// where cycles is number of cycles to stay on (-1 for constant, 0 for off)
void _mega::SetDynamicLight(int32 in_cycles, int32 in_r, int32 in_g, int32 in_b, int32 in_x, int32 in_y, int32 in_z, int32 falloff) {
// set cycles (on)
if (in_cycles != 0)
dynLightOn = in_cycles + 1; // +1 cycles for first update (before drawing...)
else
dynLightOn = 0; // 0 cycles
// check colours are 0-255
if ((in_r > 255) || (in_r < 0) || (in_g > 255) || (in_g < 0) || (in_b > 255) || (in_b < 0))
Fatal_error("Dynamic light rgb %d,%d,%d out of range (0-255)", in_r, in_g, in_b);
// set colours (scale 0-255 to 0-4095)
dynLight.states[0].c.r = (short)((in_r * 4096) / 256);
dynLight.states[0].c.g = (short)((in_g * 4096) / 256);
dynLight.states[0].c.b = (short)((in_b * 4096) / 256);
// set the v field of colour to be the maximum of r,g,b
dynLight.states[0].c.v = dynLight.states[0].c.r; // start at red
if (dynLight.states[0].c.g > dynLight.states[0].c.v) // if green bigger
dynLight.states[0].c.v = dynLight.states[0].c.g; // set to green
if (dynLight.states[0].c.b > dynLight.states[0].c.v) // if blue bigger
dynLight.states[0].c.v = dynLight.states[0].c.b; // set to blue
// setup positions
dynLightX = (int16)in_x;
dynLightY = (int16)in_y;
dynLightZ = (int16)in_z;
// falloff
if (falloff == 0) {
dynLight.afu = 0; // don't use it
} else {
dynLight.states[0].afs2 = (falloff * falloff) / 100; // (d/10)^2 = (d*d)/100
dynLight.states[0].afe2 = falloff * falloff; // d^2 = (d*d)
dynLight.afu = 1; // use it
}
}
void AddDynamicLight(PSXLampList &lamplist, _logic *log) {
_mega *mega = log->mega;
if (mega->dynLightOn == 0)
return;
int xx, yy, zz;
xx = mega->dynLightX;
yy = mega->dynLightY;
zz = mega->dynLightZ;
// rotate around character...
PXfloat ss, cc;
PXfloat angle = -log->pan * TWO_PI;
ss = (PXfloat)PXsin(angle);
cc = (PXfloat)PXcos(angle);
// rotate xx and zz around act.trueRot.vy ONE
mega->dynLight.states[0].pos.vx = (int32)(xx * cc - zz * ss);
mega->dynLight.states[0].pos.vz = (int32)(xx * ss + zz * cc);
mega->dynLight.states[0].pos.vy = (int32)yy; // no rotation
// and add the players position
mega->dynLight.states[0].pos.vx += (int32)mega->actor_xyz.x;
mega->dynLight.states[0].pos.vy += (int32)mega->actor_xyz.y;
mega->dynLight.states[0].pos.vz += (int32)mega->actor_xyz.z;
// for each lamp to add
lamplist.lamps[lamplist.n] = &(mega->dynLight);
lamplist.states[lamplist.n] = 0;
lamplist.n++;
}
void _game_session::UpdateMegaFX() {
// first do things which are done for all megas
// next do things that are only done for visable ones...
// do the check
if (!Object_visible_to_camera(cur_id))
return;
// now do on screen only things
// dynamic light
// if >0 then reduce it (only stay on for certain number of cycles... (THIS NEDS MOVING TO SOME MEGA UPDATE BIT)
if (M->dynLightOn > 0)
M->dynLightOn--;
// breathing
M->breath.Update();
// bullet
UpdateCartridgeCase();
// if talking then update talking
if ((cur_id == (uint)speech_info[CONV_ID].current_talker) && // we are the one talking
(speech_info[CONV_ID].total_subscribers > 1) && // not talking to myself
(speech_info[CONV_ID].state == __SAYING) // are definately saying, not just getting ready to...
) {
// get rap
rap_API *pose = (rap_API *)rs_anims->Res_open(I->get_pose_name(), I->pose_hash, I->base_path, I->base_path_hash);
// use it
UpdateTalking(L, pose); // update jaw and neck bone
} else {
I->neckBone.Target0(); // update towards <0,0,0>
I->jawBone.Target0(); // same
}
I->jawBone.Update();
I->neckBone.Update();
I->lookBone.Update();
// next do ones that are only for player
// return here...
if (cur_id != player.Fetch_player_id())
return;
// now do player only things
// shot deformation
player.shotDeformation.Update();
// if counter is full then jerk body back
if (player.being_shot == 3)
SetPlayerShotBone(player.shot_by_id); // set to maximum push now...
// reduce counter (when it's zero we can shoot again)
if (player.being_shot)
player.being_shot--;
UpdatePlayerLook();
}
} // End of namespace ICB