493 lines
13 KiB
C++
493 lines
13 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.
|
|
*
|
|
* 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 "m4/core/rooms.h"
|
|
#include "m4/core/errors.h"
|
|
#include "m4/core/imath.h"
|
|
#include "m4/adv_r/adv_been.h"
|
|
#include "m4/adv_r/adv_control.h"
|
|
#include "m4/adv_r/adv_file.h"
|
|
#include "m4/adv_r/adv_interface.h"
|
|
#include "m4/adv_r/adv_scale.h"
|
|
#include "m4/adv_r/adv_walk.h"
|
|
#include "m4/adv_r/db_env.h"
|
|
#include "m4/adv_r/other.h"
|
|
#include "m4/fileio/extensions.h"
|
|
#include "m4/graphics/krn_pal.h"
|
|
#include "m4/gui/gui_buffer.h"
|
|
#include "m4/gui/gui_sys.h"
|
|
#include "m4/gui/gui_vmng.h"
|
|
#include "m4/wscript/wst_regs.h"
|
|
#include "m4/vars.h"
|
|
#include "m4/m4.h"
|
|
#include "m4/platform/timer.h"
|
|
|
|
namespace M4 {
|
|
|
|
void Room::preload() {
|
|
_G(player).walker_in_this_scene = true;
|
|
}
|
|
|
|
void Room::parser() {
|
|
_G(kernel).trigger = KT_DAEMON;
|
|
}
|
|
|
|
|
|
void Sections::global_section_constructor() {
|
|
const uint sectionNum = _G(game).new_section;
|
|
assert(sectionNum >= 1 && sectionNum <= 9);
|
|
|
|
_activeSection = _sections[sectionNum - 1];
|
|
assert(_activeSection);
|
|
}
|
|
|
|
void Sections::section_room_constructor() {
|
|
_activeRoom = (*_activeSection)[_G(game).new_room];
|
|
assert(_activeRoom);
|
|
}
|
|
|
|
void Sections::game_daemon_code() {
|
|
_G(kernel).trigger_mode = KT_DAEMON;
|
|
_G(kernel).continue_handling_trigger = false;
|
|
|
|
room_daemon();
|
|
|
|
if (_G(kernel).continue_handling_trigger) {
|
|
_G(kernel).continue_handling_trigger = false;
|
|
daemon();
|
|
}
|
|
|
|
if (_G(kernel).continue_handling_trigger)
|
|
global_daemon();
|
|
|
|
if (_G(kernel).trigger == TRIG_RESTORE_GAME) {
|
|
_G(game).room_id = -1;
|
|
_G(game).section_id = -1;
|
|
_G(game).previous_room = KERNEL_RESTORING_GAME;
|
|
}
|
|
}
|
|
|
|
void Sections::m4SceneLoad() {
|
|
_G(between_rooms) = true;
|
|
_cameraShiftAmount = 0;
|
|
_cameraShift_vert_Amount = 0;
|
|
_G(art_base_override) = nullptr;
|
|
_G(use_alternate_attribute_file) = true;
|
|
_G(shut_down_digi_tracks_between_rooms) = true;
|
|
camera_pan_step = 10;
|
|
_G(camera_reacts_to_player) = true;
|
|
|
|
_G(kernel).force_restart = false;
|
|
player_set_defaults();
|
|
player_set_commands_allowed(false); // Also sets "Wait" cursor
|
|
|
|
// -------------------- SECTION CONSTRUCTOR and ROOM PRELOAD ------------------
|
|
|
|
section_room_constructor();
|
|
_G(kernel).suppress_fadeup = false;
|
|
|
|
room_preload();
|
|
|
|
// -------------------- ROOM LOAD ------------------
|
|
|
|
_GI().cancel_sentence();
|
|
gr_pal_clear_range(_G(master_palette), _G(kernel).first_fade, 255);
|
|
|
|
term_message("Calling kernel_load_room");
|
|
|
|
_G(kernel).going = kernel_load_room(MIN_PAL_ENTRY, MAX_PAL_ENTRY,
|
|
&_G(currentSceneDef), &_G(screenCodeBuff), &_G(game_bgBuff));
|
|
if (!_G(kernel).going)
|
|
error_show(FL, 'IMP!'); // this should never ever happen
|
|
|
|
get_ipl();
|
|
|
|
// Must reset event handler because loading a room re-initalizes gameBuff
|
|
gui_buffer_set_event_handler(_G(gameDrawBuff), intr_EventHandler);
|
|
|
|
if (_G(player).walker_in_this_scene)
|
|
get_walker();
|
|
|
|
_G(kernel).trigger_mode = KT_DAEMON;
|
|
_G(kernel).call_daemon_every_loop = false;
|
|
_G(kernel).fade_up_time = 30;
|
|
|
|
//-------------------- GLOBAL ROOM INIT and ROOM INIT ------------------
|
|
|
|
player_set_commands_allowed(false);
|
|
_G(set_commands_allowed_since_last_checked) = false;
|
|
_G(between_rooms) = false;
|
|
|
|
global_room_init();
|
|
_G(player).walker_trigger = -1;
|
|
|
|
if (_G(game).previous_room == KERNEL_RESTORING_GAME) {
|
|
if (_G(player).walker_in_this_scene) {
|
|
// If restoring game, restore player position and facing
|
|
player_demand_location(_G(player_info).x, _G(player_info).y);
|
|
player_demand_facing(_G(player_info).facing);
|
|
}
|
|
|
|
// Restore camera position
|
|
MoveScreenAbs(_G(game_buff_ptr), _G(player_info).camera_x, _G(player_info).camera_y);
|
|
}
|
|
|
|
_G(player).been_here_before = player_been_here(_G(game).room_id);
|
|
|
|
term_message("calling room_init_code");
|
|
room_init();
|
|
|
|
if (_G(game).previous_room == KERNEL_RESTORING_GAME) {
|
|
_G(game).previous_room = -1;
|
|
}
|
|
|
|
// Init for fade up screen
|
|
if (!_G(kernel).suppress_fadeup) {
|
|
pal_fade_set_start(&_G(master_palette)[0], 0); // Set fade to black instantly (0 ticks)
|
|
pal_fade_init(&_G(master_palette)[0], _G(kernel).first_fade, 255, 100,
|
|
_G(kernel).fade_up_time, 32765); // 30 ticks
|
|
}
|
|
|
|
if (!_G(set_commands_allowed_since_last_checked))
|
|
player_set_commands_allowed(true);
|
|
|
|
//-------------------- PRE-PLAY ROOM ------------------
|
|
|
|
term_message("Off to the races -- %d", timer_read_60());
|
|
}
|
|
|
|
void Sections::m4RunScene() {
|
|
if (!player_been_here(_G(game).room_id))
|
|
player_enters_scene(_G(game).room_id);
|
|
|
|
game_control_cycle();
|
|
}
|
|
|
|
void Sections::m4EndScene() {
|
|
_G(between_rooms) = true;
|
|
hotspot_unhide_and_dump();
|
|
|
|
if (!_G(kernel).going && _GI()._visible && player_commands_allowed())
|
|
other_save_game_for_resurrection();
|
|
|
|
if (_G(kernel).teleported_in) {
|
|
_G(kernel).teleported_in = false;
|
|
pal_fade_set_start(&_G(master_palette)[0], 0); // Set fade to black instantly (0 ticks)
|
|
}
|
|
|
|
//-------------------- cancel all editors ------------------
|
|
|
|
scale_editor_cancel();
|
|
|
|
//-------------------- ROOM SHUTDOWN CODE ------------------
|
|
|
|
term_message("Shuttin' down the scene");
|
|
room_shutdown();
|
|
kernel_unload_room(&_G(currentSceneDef), &_G(screenCodeBuff), &_G(game_bgBuff));
|
|
|
|
pal_cycle_stop();
|
|
|
|
if (_G(shut_down_digi_tracks_between_rooms)) {
|
|
_G(digi).stop(1);
|
|
_G(digi).stop(2);
|
|
_G(digi).stop(3);
|
|
_G(digi).flush_mem();
|
|
}
|
|
|
|
conv_unload(conv_get_handle());
|
|
|
|
ws_KillDeadMachines();
|
|
|
|
//-------------------- DUMP ASSETS AND MINI-ENGINES ------------------
|
|
// Note machines should always be cleared before anything else
|
|
ClearWSAssets(_WS_ASSET_MACH, 0, 255);
|
|
ClearWSAssets(_WS_ASSET_SEQU, 0, 255);
|
|
ClearWSAssets(_WS_ASSET_DATA, 0, 255);
|
|
ClearWSAssets(_WS_ASSET_CELS, 0, 255);
|
|
|
|
// Dump a list of any resources remaining in memory
|
|
_G(resources).dumpResources();
|
|
|
|
// Reload the walker and show scripts.
|
|
if (!LoadWSAssets("walker script", &_G(master_palette)[0]))
|
|
error_show(FL, 'FNF!', "walker script");
|
|
if (!LoadWSAssets("show script", &_G(master_palette)[0]))
|
|
error_show(FL, 'FNF!', "show script");
|
|
if (!LoadWSAssets("stream script", &_G(master_palette)[0]))
|
|
error_show(FL, 'FNF', "stream script");
|
|
|
|
g_vars->global_menu_system_init();
|
|
}
|
|
|
|
void Sections::get_ipl() {
|
|
if (_G(inverse_pal))
|
|
delete _G(inverse_pal);
|
|
_G(inverse_pal) = nullptr;
|
|
|
|
Common::String filename;
|
|
|
|
char *name = env_find(_G(currentSceneDef).art_base);
|
|
if (name) {
|
|
// Means found in database
|
|
filename = f_extension_new(name, "ipl");
|
|
|
|
} else {
|
|
// Concat hag mode
|
|
filename = Common::String::format("%s.IPL", _G(currentSceneDef).art_base);
|
|
}
|
|
|
|
_G(inverse_pal) = new InvPal(filename.c_str());
|
|
}
|
|
|
|
void Sections::get_walker() {
|
|
term_message("Loading walker sprites");
|
|
if (!_GW().walk_load_walker_and_shadow_series())
|
|
error_show(FL, 'WLOD');
|
|
ws_walk_init_system();
|
|
}
|
|
|
|
void Sections::game_control_cycle() {
|
|
while (_G(game).new_room == _G(game).room_id && _G(kernel).going && !_G(kernel).force_restart) {
|
|
krn_pal_game_task();
|
|
|
|
int32 status;
|
|
ScreenContext *screen = vmng_screen_find(_G(gameDrawBuff), &status);
|
|
if (!screen)
|
|
error_show(FL, 'BUF!');
|
|
|
|
if (_G(player).ready_to_walk) {
|
|
if (_G(player).need_to_walk) {
|
|
if (_G(player).noun[0] == '@' || !_G(player).walker_in_this_scene) {
|
|
term_message("parsing0");
|
|
parse_player_command_now();
|
|
term_message("parsed0");
|
|
} else {
|
|
term_message("player: walk to (%d, %d), facing: %d",
|
|
_G(player).walk_x, _G(player).walk_y, _G(player).walk_facing);
|
|
|
|
if (_G(player).walk_x < 0 || _G(player).walk_y < 0) {
|
|
term_message("walk x or y < 0 - player: %s %s %s",
|
|
_G(player).verb, _G(player).noun, _G(player).prep);
|
|
}
|
|
|
|
_G(player).waiting_for_walk = true;
|
|
ws_walk(_G(my_walker), _G(player).walk_x, _G(player).walk_y,
|
|
nullptr, _G(player).walker_trigger, _G(player).walk_facing);
|
|
term_message("walked");
|
|
|
|
_G(player).need_to_walk = false;
|
|
}
|
|
} else if (!_G(player).waiting_for_walk) {
|
|
term_message("parsing1");
|
|
parse_player_command_now();
|
|
term_message("parsed0");
|
|
|
|
_G(player).ready_to_walk = false;
|
|
}
|
|
}
|
|
|
|
if (_G(player).walker_in_this_scene && _G(camera_reacts_to_player) &&
|
|
_G(gameDrawBuff)->w > 640 && _G(my_walker)) {
|
|
const int xp = (_G(my_walker)->myAnim8->myRegs[IDX_X] >> 16) + screen->x1;
|
|
|
|
if (xp > 560 && _cameraShiftAmount >= 0) {
|
|
_cameraShiftAmount += screen->x1 - 427;
|
|
const int xv = _cameraShiftAmount + _G(gameDrawBuff)->w - 1;
|
|
|
|
if (xv < 639)
|
|
_cameraShiftAmount = -(_G(gameDrawBuff)->w - 640);
|
|
_cameraShiftAmount -= screen->x1;
|
|
} else if (xp < 80 && _cameraShiftAmount <= 0) {
|
|
_cameraShiftAmount += screen->x1 + 427;
|
|
|
|
if (_cameraShiftAmount > 0)
|
|
_cameraShiftAmount = 0;
|
|
|
|
_cameraShiftAmount -= screen->x1;
|
|
}
|
|
}
|
|
|
|
// Ensure the screen is updated
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(10);
|
|
g_engine->updateSubtitleOverlay();
|
|
|
|
if (g_engine->shouldQuit())
|
|
_G(kernel).going = false;
|
|
}
|
|
|
|
_GI().cancel_sentence();
|
|
}
|
|
|
|
void Sections::parse_player_command_now() {
|
|
if (_G(player).command_ready) {
|
|
term_message("player: %s %s %s", _G(player).verb, _G(player).noun, _G(player).prep);
|
|
_G(cursor_state) = kARROW;
|
|
_G(kernel).trigger_mode = KT_PARSE;
|
|
|
|
room_parser();
|
|
|
|
if (_G(player).command_ready) {
|
|
section_parser();
|
|
|
|
if (_G(player).command_ready) {
|
|
global_parser();
|
|
|
|
if (_G(player).command_ready) {
|
|
room_error();
|
|
|
|
if (_G(player).command_ready)
|
|
global_error_code();
|
|
}
|
|
}
|
|
}
|
|
|
|
term_message("...parsed");
|
|
}
|
|
}
|
|
|
|
void Sections::pal_game_task() {
|
|
int32 status;
|
|
int delta;
|
|
Common::String line;
|
|
|
|
if (!player_commands_allowed())
|
|
mouse_set_sprite(5);
|
|
|
|
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
|
|
|
|
if (!_G(kernel).pause) {
|
|
if (_G(toggle_cursor) != CURSCHANGE_NONE) {
|
|
const CursorChange change = _G(toggle_cursor);
|
|
_G(toggle_cursor) = CURSCHANGE_NONE;
|
|
g_vars->getHotkeys()->toggle_through_cursors(change);
|
|
}
|
|
|
|
const bool updateVideo = !_cameraShiftAmount && !_cameraShift_vert_Amount;
|
|
|
|
cycleEngines(_G(game_bgBuff)->get_buffer(), &(_G(currentSceneDef).depth_table[0]),
|
|
_G(screenCodeBuff)->get_buffer(), (uint8 *)&_G(master_palette)[0], _G(inverse_pal)->get_ptr(), updateVideo);
|
|
|
|
_G(inverse_pal)->release();
|
|
_G(game_bgBuff)->release();
|
|
|
|
if (!game_buff_ptr)
|
|
error_show(FL, 'BUF!');
|
|
|
|
if _G(please_hyperwalk) {
|
|
_G(please_hyperwalk) = false;
|
|
adv_hyperwalk_to_final_destination(nullptr, nullptr);
|
|
}
|
|
|
|
if (_cameraShiftAmount) {
|
|
if (_G(kernel).camera_pan_instant) {
|
|
delta = _cameraShiftAmount;
|
|
_cameraShiftAmount = 0;
|
|
} else {
|
|
if (_cameraShiftAmount > 0) {
|
|
delta = imath_min(_cameraShiftAmount, camera_pan_step);
|
|
} else {
|
|
delta = imath_max(_cameraShiftAmount, -camera_pan_step);
|
|
}
|
|
|
|
_cameraShiftAmount -= delta;
|
|
}
|
|
|
|
MoveScreenDelta(game_buff_ptr, delta, 0);
|
|
}
|
|
|
|
if (_cameraShift_vert_Amount) {
|
|
if (_G(kernel).camera_pan_instant) {
|
|
delta = _cameraShift_vert_Amount;
|
|
_cameraShift_vert_Amount = 0;
|
|
} else {
|
|
if (_cameraShift_vert_Amount > 0) {
|
|
delta = imath_min(_cameraShift_vert_Amount, camera_pan_step);
|
|
} else {
|
|
delta = imath_max(_cameraShift_vert_Amount, camera_pan_step);
|
|
}
|
|
|
|
_cameraShift_vert_Amount -= delta;
|
|
}
|
|
}
|
|
}
|
|
|
|
pal_fx_update();
|
|
_G(digi).read_another_chunk();
|
|
_G(midi).loop();
|
|
|
|
gui_system_event_handler();
|
|
|
|
if (conv_is_event_ready()) {
|
|
_G(player).command_ready = true;
|
|
term_message("conv parse row");
|
|
parse_player_command_now();
|
|
term_message("conv parse finish");
|
|
|
|
(void)conv_get_event();
|
|
}
|
|
|
|
f_stream_Process(2);
|
|
|
|
if (_G(kernel).call_daemon_every_loop)
|
|
tick();
|
|
|
|
if (_G(editors_in_use) && (_G(editors_in_use) & 1))
|
|
scale_editor_draw();
|
|
|
|
if (_G(showMousePos))
|
|
update_mouse_pos_dialog();
|
|
}
|
|
|
|
void Sections::camera_shift_xy(int32 x, int32 y) {
|
|
int32 status;
|
|
ScreenContext *sc = vmng_screen_find(_G(gameDrawBuff), &status);
|
|
assert(sc);
|
|
|
|
_cameraShiftAmount = -sc->x1 - x + _G(kernel).letter_box_x;
|
|
_cameraShift_vert_Amount = -sc->y1 - y + _G(kernel).letter_box_y;
|
|
}
|
|
|
|
void Sections::set_camera_delta_pan(int32 deltaX, int32 deltaY) {
|
|
_cameraShiftAmount = -deltaX;
|
|
_cameraShift_vert_Amount = -deltaY;
|
|
}
|
|
|
|
void Sections::adv_camera_pan_step(int32 step) {
|
|
camera_pan_step = step;
|
|
}
|
|
|
|
Room *Sections::getRoom(int room) const {
|
|
return (*_sections[(room / 100) - 1])[room];
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
Room *Section::operator[](uint roomNum) {
|
|
Room *room = _rooms[roomNum];
|
|
if (!room)
|
|
error("Unknown room number - %d", roomNum);
|
|
|
|
return room;
|
|
}
|
|
|
|
} // namespace M4
|