/* 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 . * */ #include "engines/icb/common/px_common.h" #include "engines/icb/session.h" #include "engines/icb/mission.h" #include "engines/icb/p4.h" #include "engines/icb/object_structs.h" #include "engines/icb/global_objects.h" #include "engines/icb/custom_logics.h" #include "engines/icb/common/px_scriptengine.h" #include "engines/icb/common/px_linkeddatafile.h" #include "engines/icb/common/px_prop_anims.h" #include "engines/icb/common/ptr_util.h" #include "engines/icb/sound.h" namespace ICB { mcodeFunctionReturnCodes fn_set_custom_simple_animator(int32 &result, int32 *params) { return (MS->fn_set_custom_simple_animator(result, params)); } mcodeFunctionReturnCodes fn_set_custom_button_operated_door(int32 &result, int32 *params) { return (MS->fn_set_custom_button_operated_door(result, params)); } mcodeFunctionReturnCodes fn_set_custom_auto_door(int32 &result, int32 *params) { return (MS->fn_set_custom_auto_door(result, params)); } mcodeFunctionReturnCodes fn_set_cad_lock_status(int32 &result, int32 *params) { return (MS->fn_set_cad_lock_status(result, params)); } mcodeFunctionReturnCodes fn_get_cad_state_flag(int32 &result, int32 *params) { return (MS->fn_get_cad_state_flag(result, params)); } mcodeFunctionReturnCodes _game_session::fn_set_custom_simple_animator(int32 &, int32 *) { // chnage the object to be a special simple animator type where all scripts are circumvented // we need to check here, first, for legality - i.e. we clear the way for run-time assumptions to be made _animating_prop *index; _animation_entry *anim; // find entry for this object via its name index = (_animating_prop *)LinkedDataObject::Try_fetch_item_by_name(prop_anims, CGameObject::GetName(object)); // get anim anim = (_animation_entry *)(((char *)index) + index->anims[0]); // check for no frame if (!anim->num_frames) { Tdebug("objects_that_died.txt", "fn_set_custom_simple_animator [%s] loop anim has 0 frames", CGameObject::GetName(object)); Shut_down_object("by fn_set_custom_simple_animator"); return (IR_STOP); } // start at frame 0 L->anim_pc = 0; L->big_mode = __CUSTOM_SIMPLE_ANIMATE; SA_INDEX = LinkedDataObject::Fetch_item_number_by_name(prop_anims, CGameObject::GetName(object)); // object will pause when off screen L->hold_mode = prop_camera_hold; Tdebug("logic_modes.txt", "fn_set_custom_simple_animator switching [%s]", CGameObject::GetName(object)); return (IR_CONT); } void _game_session::Custom_simple_animator() { // this special _big_mode logic replaces the logic script of a lib_simple_animator object // this is a rationalisation designed to aid speed up on psx _animating_prop *index; _animation_entry *anim; // get index for object index = (_animating_prop *)LinkedDataObject::Fetch_item_by_number(prop_anims, SA_INDEX); // now then, lets make the assumption that anim 0 will be the 'looping' one anim = (_animation_entry *)(((char *)index) + index->anims[0]); if ((uint8)L->anim_pc == (anim->num_frames - 1)) L->anim_pc = 0; else L->anim_pc++; // advance current pc // set frame prop_state_table[cur_id] = anim->frames[L->anim_pc]; } mcodeFunctionReturnCodes _game_session::fn_set_custom_button_operated_door(int32 &, int32 *params) { // set to special custom door logic // params 0 initial state value // prime BOD_STATE = params[0]; // starts non animating BOD_CONTROL = 0; // not opening or closing // switch out of script mode L->big_mode = __CUSTOM_BUTTON_OPERATED_DOOR; BOD_INDEX = LinkedDataObject::Fetch_item_number_by_name(prop_anims, CGameObject::GetName(object)); BOD_OPEN_NO = Validate_prop_anim("opening"); BOD_CLOSE_NO = Validate_prop_anim("closing"); Tdebug("logic_modes.txt", "fn_set_custom_button_operated_door switching [%s]", CGameObject::GetName(object)); // Set a symbol type so the Remora knows what to draw. L->object_type = __BUTTON_OPERATED_DOOR; // push the y up for Cord to look at L->prop_xyz.y += 179; return IR_CONT; } void _game_session::Custom_button_operated_door() { // this special _big_mode logic replaces the logic script of a lib_simple_animator object // this is a rationalisation designed to aid speed up on psx _animating_prop *index; _animation_entry *anim; uint32 var_num; int32 params, result; do { // do while because when we switch in to new anim we need to do an anim if (BOD_CONTROL == BOD_WAITING) { // we've opened, now we wait until no one in the list is near to us if (!BOD_WAIT_COUNT) { params = 300; fn_near_list(result, ¶ms); if (result == TRUE8) { BOD_WAIT_COUNT = 36; // count again return; } // else, close L->anim_pc = 0; BOD_CONTROL = BOD_CLOSING; BOD_STATE = 1; // closed // set state flag to 1 var_num = CGameObject::GetVariable(object, "state"); CGameObject::SetIntegerVariable(object, var_num, 1); } else { BOD_WAIT_COUNT--; return; } } if (BOD_CONTROL == BOD_OPENING) { // get index for object index = (_animating_prop *)LinkedDataObject::Fetch_item_by_number(prop_anims, BOD_INDEX); anim = (_animation_entry *)(((char *)index) + index->anims[BOD_OPEN_NO]); prop_state_table[cur_id] = anim->frames[L->anim_pc]; if ((uint8)L->anim_pc == (anim->num_frames - 1)) { BOD_CONTROL = BOD_WAITING; // just opened - set to wait for list of people to not be here BOD_WAIT_COUNT = 36; BOD_STATE = 0; // open // set state flag to 0 var_num = CGameObject::GetVariable(object, "state"); CGameObject::SetIntegerVariable(object, var_num, 0); } else L->anim_pc++; // frame on return; } else if (BOD_CONTROL == BOD_CLOSING) { // get index for object index = (_animating_prop *)LinkedDataObject::Try_fetch_item_by_name(prop_anims, CGameObject::GetName(object)); anim = (_animation_entry *)(((char *)index) + index->anims[BOD_CLOSE_NO]); // set frame prop_state_table[cur_id] = anim->frames[L->anim_pc]; if ((uint8)L->anim_pc == (anim->num_frames - 1)) BOD_CONTROL = 0; // cancel mode else L->anim_pc++; // frame on return; } // ok, we're not animating so just check state // what state are we in? if (!BOD_STATE) { // open // ok, check to see if opened // get state variable number var_num = CGameObject::GetVariable(object, "state"); // get value if (!CGameObject::GetIntegerVariable(object, var_num)) { BOD_CONTROL = BOD_WAITING; // just opened - set to wait for list of people to not be here BOD_WAIT_COUNT = 36; return; // zero so still open } L->anim_pc = 0; BOD_CONTROL = BOD_CLOSING; BOD_STATE = 1; // closed // close sound if (logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR] != 0) RegisterSound(cur_id, nullptr, logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR], closeDesc, (int8)127); // have to use full version so we can give hash instead of string else RegisterSound(cur_id, defaultCloseSfx, closeDesc); // use small version as we have string not hash } else { // closed // check for // a. someone pressing button // b. chi off screen being near bool8 open = FALSE8; if (is_there_a_chi) { // if chi is on a different floor from player then assume she is following and so auto open the door for her // - sneaky if (logic_structs[chi_id]->owner_floor_rect != logic_structs[player.Fetch_player_id()]->owner_floor_rect) { PXreal sub1, sub2; sub1 = logic_structs[chi_id]->mega->actor_xyz.x - L->prop_xyz.x; sub2 = logic_structs[chi_id]->mega->actor_xyz.z - L->prop_xyz.z; // dist if (((sub1 * sub1) + (sub2 * sub2)) < (200 * 200)) { open = TRUE8; } } } // get state variable number var_num = CGameObject::GetVariable(object, "state"); // get value if (!CGameObject::GetIntegerVariable(object, var_num)) { open = TRUE8; // now 0 so start opening } if (open) { L->anim_pc = 0; BOD_CONTROL = BOD_OPENING; BOD_STATE = 0; // open // open sound if (logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR] != 0) RegisterSound(cur_id, nullptr, logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR], openDesc, (int8)127); // have to use full version so we can give hash instead of string else RegisterSound(cur_id, defaultOpenSfx, openDesc); // use small version as we have string not hash } else { // still closed - see if we can go to sleep if (!L->prop_on_this_screen) { // closed and no on screen L->camera_held = TRUE8; // not on screen L->cycle_time = 0; // accurate for displays } return; } } } while (1); } mcodeFunctionReturnCodes _game_session::fn_set_custom_auto_door(int32 &, int32 *params) { // set to special custom door logic // params 0 initial state value // params 1 dist for detection // params 2 locked or not CAD_STATE = params[0]; CAD_DIST = params[1]; CAD_LOCKED = params[2]; // starts not locked L->list[9] = params[1]; // switch out of script mode L->big_mode = __CUSTOM_AUTO_DOOR; // anim presets CAD_INDEX = LinkedDataObject::Fetch_item_number_by_name(prop_anims, CGameObject::GetName(object)); CAD_OPEN_NO = Validate_prop_anim("opening"); CAD_CLOSE_NO = Validate_prop_anim("closing"); Tdebug("logic_modes.txt", "fn_set_custom_auto_door switching [%s]", CGameObject::GetName(object)); // Set a symbol type so the Remora knows what to draw. L->object_type = __AUTO_DOOR; return (IR_CONT); } void _game_session::Custom_auto_door() { // this special _big_mode logic replaces the logic script of a lib_auto_slide_door object // this is a rationalisation designed to aid speed up on psx _animating_prop *index; _animation_entry *anim; uint32 j, id; bool8 sensed = FALSE8; L->list[8] = 0; // check if someone is here or not j = 0; while ((j < number_of_voxel_ids) && (!sensed)) { // object 0 is used id = voxel_id_list[j++]; if ((!logic_structs[id]->mega->dead) && (logic_structs[id]->ob_status != OB_STATUS_HELD)) { // still alive if (PXfabs(L->prop_xyz.y - logic_structs[id]->mega->actor_xyz.y) < (200 * REAL_ONE)) { // slack for height calc if ((PXfabs(L->prop_xyz.x - logic_structs[id]->mega->actor_xyz.x) < (PXreal)CAD_DIST) && (PXfabs(L->prop_xyz.z - logic_structs[id]->mega->actor_xyz.z) < (PXreal)CAD_DIST)) { // yes sensed = TRUE8; L->list[8]++; } } } } if (CAD_STATE == CAD_OPENING) { // doors opening // get index for object index = (_animating_prop *)LinkedDataObject::Fetch_item_by_number(prop_anims, CAD_INDEX); anim = (_animation_entry *)(((char *)index) + index->anims[CAD_OPEN_NO]); prop_state_table[cur_id] = anim->frames[L->anim_pc]; if ((uint8)L->anim_pc == (anim->num_frames - 1)) { CAD_STATE = CAD_OPEN; // cancel mode CAD_WAIT = CAD_TIMER; // when we sense somewhat we reset the timer } else L->anim_pc++; // frame on return; } else if (CAD_STATE == CAD_OPEN) { if ((!CAD_LOCKED) && (!sensed)) { // no one here anymore so switch modes if (CAD_WAIT) { // now wait for time up CAD_WAIT--; // 1 less return; } CAD_STATE = CAD_CLOSING; // close sound if (logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR] != 0) RegisterSound(cur_id, nullptr, logic_structs[cur_id]->sfxVars[CLOSE_SFX_VAR], closeDesc, (int8)127); // have to use full version so we can give hash instead of string else RegisterSound(cur_id, defaultCloseSfx, closeDesc); // use small version as we have string not hash } CAD_WAIT = CAD_TIMER; // when we sense somewhat we reset the timer return; } else if (CAD_STATE == CAD_CLOSING) { // doors closing if (sensed) { // sensed someone so switch modes CAD_STATE = CAD_OPENING; return; } // get index for object index = (_animating_prop *)LinkedDataObject::Fetch_item_by_number(prop_anims, CAD_INDEX); // when closing we reverse the opening anim - until the done when we set to last frame of closing anim = (_animation_entry *)(((char *)index) + index->anims[CAD_OPEN_NO]); prop_state_table[cur_id] = anim->frames[L->anim_pc]; if (!L->anim_pc) { CAD_STATE = CAD_CLOSED; // cancel mode anim = (_animation_entry *)(((char *)index) + index->anims[CAD_CLOSE_NO]); prop_state_table[cur_id] = anim->frames[anim->num_frames - 1]; } else L->anim_pc--; // frame on return; } else if (CAD_STATE == CAD_CLOSED) { if ((!CAD_LOCKED) && (sensed)) { // sensed someone so switch modes CAD_STATE = CAD_OPENING; L->anim_pc = 0; // open sound if (logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR] != 0) RegisterSound(cur_id, nullptr, logic_structs[cur_id]->sfxVars[OPEN_SFX_VAR], openDesc, (int8)127); // have to use full version so we can give hash instead of string else RegisterSound(cur_id, defaultOpenSfx, openDesc); // use small version as we have string not hash } if (!L->prop_on_this_screen) { // closed and no on screen L->camera_held = TRUE8; // not on screen L->cycle_time = 0; // accurate for displays } return; } } mcodeFunctionReturnCodes _game_session::fn_set_cad_lock_status(int32 &, int32 *params) { // set the locked status of an auto slide door // only the object should call this // params 0 0 name of door // params 0 0 or non zero const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]); uint32 id; id = (uint32)LinkedDataObject::Fetch_item_number_by_name(objects, object_name); logic_structs[id]->list[5] = params[1]; return IR_CONT; } mcodeFunctionReturnCodes _game_session::fn_get_cad_state_flag(int32 &result, int32 *params) { // return custom auto door state to button // params 0 name of door const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]); uint32 id; id = (uint32)LinkedDataObject::Fetch_item_number_by_name(objects, object_name); if (logic_structs[id]->EXT_CAD_STATE == CAD_OPEN) result = 1; else result = 0; return IR_CONT; } } // End of namespace ICB