mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 07:14:59 +00:00
1185 lines
40 KiB
C++
1185 lines
40 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 "ags/shared/core/platform.h"
|
|
#include "ags/shared/util/string_utils.h" //strlwr()
|
|
#include "ags/shared/ac/common.h"
|
|
#include "ags/engine/ac/character.h"
|
|
#include "ags/engine/ac/character_extras.h"
|
|
#include "ags/engine/ac/draw.h"
|
|
#include "ags/engine/ac/event.h"
|
|
#include "ags/engine/ac/game.h"
|
|
#include "ags/engine/ac/game_setup.h"
|
|
#include "ags/shared/ac/game_setup_struct.h"
|
|
#include "ags/engine/ac/game_state.h"
|
|
#include "ags/engine/ac/global_audio.h"
|
|
#include "ags/engine/ac/global_character.h"
|
|
#include "ags/engine/ac/global_game.h"
|
|
#include "ags/engine/ac/global_object.h"
|
|
#include "ags/engine/ac/global_translation.h"
|
|
#include "ags/engine/ac/move_list.h"
|
|
#include "ags/engine/ac/mouse.h"
|
|
#include "ags/engine/ac/overlay.h"
|
|
#include "ags/engine/ac/properties.h"
|
|
#include "ags/engine/ac/region.h"
|
|
#include "ags/engine/ac/sys_events.h"
|
|
#include "ags/engine/ac/room.h"
|
|
#include "ags/engine/ac/room_object.h"
|
|
#include "ags/engine/ac/room_status.h"
|
|
#include "ags/engine/ac/screen.h"
|
|
#include "ags/engine/ac/string.h"
|
|
#include "ags/engine/ac/system.h"
|
|
#include "ags/engine/ac/walkable_area.h"
|
|
#include "ags/engine/ac/walk_behind.h"
|
|
#include "ags/engine/ac/dynobj/script_object.h"
|
|
#include "ags/engine/ac/dynobj/script_hotspot.h"
|
|
#include "ags/shared/gui/gui_main.h"
|
|
#include "ags/engine/script/cc_instance.h"
|
|
#include "ags/engine/debugging/debug_log.h"
|
|
#include "ags/engine/debugging/debugger.h"
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/shared/game/room_version.h"
|
|
#include "ags/engine/platform/base/ags_platform_driver.h"
|
|
#include "ags/plugins/ags_plugin.h"
|
|
#include "ags/plugins/plugin_engine.h"
|
|
#include "ags/shared/script/cc_common.h"
|
|
#include "ags/engine/script/script.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/shared/ac/sprite_cache.h"
|
|
#include "ags/shared/util/stream.h"
|
|
#include "ags/engine/gfx/graphics_driver.h"
|
|
#include "ags/shared/core/asset_manager.h"
|
|
#include "ags/engine/ac/dynobj/all_dynamic_classes.h"
|
|
#include "ags/shared/gfx/bitmap.h"
|
|
#include "ags/engine/gfx/gfxfilter.h"
|
|
#include "ags/shared/util/math.h"
|
|
#include "ags/engine/media/audio/audio_system.h"
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/engine/script/script_api.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/engine/ac/dynobj/script_string.h"
|
|
#include "ags/globals.h"
|
|
|
|
namespace AGS3 {
|
|
|
|
using namespace AGS::Shared;
|
|
using namespace AGS::Engine;
|
|
|
|
ScriptDrawingSurface *Room_GetDrawingSurfaceForBackground(int backgroundNumber) {
|
|
if (_G(displayed_room) < 0)
|
|
quit("!Room.GetDrawingSurfaceForBackground: no room is currently loaded");
|
|
|
|
if (backgroundNumber == SCR_NO_VALUE) {
|
|
backgroundNumber = _GP(play).bg_frame;
|
|
}
|
|
|
|
if ((backgroundNumber < 0) || ((size_t)backgroundNumber >= _GP(thisroom).BgFrameCount))
|
|
quit("!Room.GetDrawingSurfaceForBackground: invalid background number specified");
|
|
|
|
|
|
ScriptDrawingSurface *surface = new ScriptDrawingSurface();
|
|
surface->roomBackgroundNumber = backgroundNumber;
|
|
ccRegisterManagedObject(surface, surface);
|
|
return surface;
|
|
}
|
|
|
|
ScriptDrawingSurface *Room_GetDrawingSurfaceForMask(RoomAreaMask mask) {
|
|
if (_G(displayed_room) < 0)
|
|
quit("!Room_GetDrawingSurfaceForMask: no room is currently loaded");
|
|
ScriptDrawingSurface *surface = new ScriptDrawingSurface();
|
|
surface->roomMaskType = mask;
|
|
ccRegisterManagedObject(surface, surface);
|
|
return surface;
|
|
}
|
|
|
|
int Room_GetObjectCount() {
|
|
return _G(croom)->numobj;
|
|
}
|
|
|
|
int Room_GetWidth() {
|
|
return _GP(thisroom).Width;
|
|
}
|
|
|
|
int Room_GetHeight() {
|
|
return _GP(thisroom).Height;
|
|
}
|
|
|
|
int Room_GetColorDepth() {
|
|
return _GP(thisroom).BgFrames[0].Graphic->GetColorDepth();
|
|
}
|
|
|
|
int Room_GetLeftEdge() {
|
|
return _GP(thisroom).Edges.Left;
|
|
}
|
|
|
|
int Room_GetRightEdge() {
|
|
return _GP(thisroom).Edges.Right;
|
|
}
|
|
|
|
int Room_GetTopEdge() {
|
|
return _GP(thisroom).Edges.Top;
|
|
}
|
|
|
|
int Room_GetBottomEdge() {
|
|
return _GP(thisroom).Edges.Bottom;
|
|
}
|
|
|
|
int Room_GetMusicOnLoad() {
|
|
return _GP(thisroom).Options.StartupMusic;
|
|
}
|
|
|
|
int Room_GetProperty(const char *property) {
|
|
return get_int_property(_GP(thisroom).Properties, _G(croom)->roomProps, property);
|
|
}
|
|
|
|
const char *Room_GetTextProperty(const char *property) {
|
|
return get_text_property_dynamic_string(_GP(thisroom).Properties, _G(croom)->roomProps, property);
|
|
}
|
|
|
|
bool Room_SetProperty(const char *property, int value) {
|
|
return set_int_property(_G(croom)->roomProps, property, value);
|
|
}
|
|
|
|
bool Room_SetTextProperty(const char *property, const char *value) {
|
|
return set_text_property(_G(croom)->roomProps, property, value);
|
|
}
|
|
|
|
const char *Room_GetMessages(int index) {
|
|
if ((index < 0) || ((size_t)index >= _GP(thisroom).MessageCount)) {
|
|
return nullptr;
|
|
}
|
|
char buffer[STD_BUFFER_SIZE];
|
|
buffer[0] = 0;
|
|
replace_tokens(get_translation(_GP(thisroom).Messages[index].GetCStr()), buffer, STD_BUFFER_SIZE);
|
|
return CreateNewScriptString(buffer);
|
|
}
|
|
|
|
bool Room_Exists(int room) {
|
|
String room_filename;
|
|
room_filename.Format("room%d.crm", room);
|
|
return _GP(AssetMgr)->DoesAssetExist(room_filename);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
// Makes sure that room background and walk-behind mask are matching room size
|
|
// in game resolution coordinates; in other words makes graphics appropriate
|
|
// for display in the game.
|
|
void convert_room_background_to_game_res() {
|
|
if (!_GP(game).AllowRelativeRes() || !_GP(thisroom).IsRelativeRes())
|
|
return;
|
|
|
|
int bkg_width = _GP(thisroom).Width;
|
|
int bkg_height = _GP(thisroom).Height;
|
|
data_to_game_coords(&bkg_width, &bkg_height);
|
|
|
|
for (size_t i = 0; i < _GP(thisroom).BgFrameCount; ++i)
|
|
_GP(thisroom).BgFrames[i].Graphic = FixBitmap(_GP(thisroom).BgFrames[i].Graphic, bkg_width, bkg_height);
|
|
|
|
// Fix masks to match resized room background
|
|
// Walk-behind is always 1:1 with room background size
|
|
_GP(thisroom).WalkBehindMask = FixBitmap(_GP(thisroom).WalkBehindMask, bkg_width, bkg_height);
|
|
int mask_width = bkg_width / _GP(thisroom).MaskResolution;
|
|
int mask_height = bkg_height / _GP(thisroom).MaskResolution;
|
|
_GP(thisroom).HotspotMask = FixBitmap(_GP(thisroom).HotspotMask, mask_width, mask_height);
|
|
_GP(thisroom).RegionMask = FixBitmap(_GP(thisroom).RegionMask, mask_width, mask_height);
|
|
_GP(thisroom).WalkAreaMask = FixBitmap(_GP(thisroom).WalkAreaMask, mask_width, mask_height);
|
|
|
|
for (size_t i = 0; i < _GP(thisroom).WalkAreaCount; ++i) {
|
|
_GP(thisroom).WalkAreas[i].Top = room_to_mask_coord(_GP(thisroom).WalkAreas[i].Top);
|
|
_GP(thisroom).WalkAreas[i].Bottom = room_to_mask_coord(_GP(thisroom).WalkAreas[i].Bottom);
|
|
}
|
|
}
|
|
|
|
|
|
void save_room_data_segment() {
|
|
_G(croom)->FreeScriptData();
|
|
|
|
_G(croom)->tsdatasize = _G(roominst)->globaldatasize;
|
|
if (_G(croom)->tsdatasize > 0) {
|
|
_G(croom)->tsdata = (char *)malloc(_G(croom)->tsdatasize + 10);
|
|
memcpy(_G(croom)->tsdata, &_G(roominst)->globaldata[0], _G(croom)->tsdatasize);
|
|
}
|
|
|
|
}
|
|
|
|
void unload_old_room() {
|
|
// if switching games on restore, don't do this
|
|
if (_G(displayed_room) < 0)
|
|
return;
|
|
|
|
debug_script_log("Unloading room %d", _G(displayed_room));
|
|
|
|
current_fade_out_effect();
|
|
|
|
dispose_room_drawdata();
|
|
|
|
for (uint32_t ff = 0; ff < _G(croom)->numobj; ff++)
|
|
_G(objs)[ff].moving = 0;
|
|
|
|
if (!_GP(play).ambient_sounds_persist) {
|
|
for (int ff = NUM_SPEECH_CHANS; ff < _GP(game).numGameChannels; ff++)
|
|
StopAmbientSound(ff);
|
|
}
|
|
|
|
cancel_all_scripts();
|
|
_GP(events).clear(); // cancel any pending room events
|
|
|
|
if (_G(roomBackgroundBmp) != nullptr) {
|
|
_G(gfxDriver)->DestroyDDB(_G(roomBackgroundBmp));
|
|
_G(roomBackgroundBmp) = nullptr;
|
|
}
|
|
|
|
if (_G(croom) == nullptr) ;
|
|
else if (_G(roominst) != nullptr) {
|
|
save_room_data_segment();
|
|
delete _G(roominstFork);
|
|
delete _G(roominst);
|
|
_G(roominstFork) = nullptr;
|
|
_G(roominst) = nullptr;
|
|
} else _G(croom)->tsdatasize = 0;
|
|
memset(&_GP(play).walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
|
|
_GP(play).bg_frame = 0;
|
|
_GP(play).bg_frame_locked = 0;
|
|
remove_screen_overlay(-1);
|
|
delete _G(raw_saved_screen);
|
|
_G(raw_saved_screen) = nullptr;
|
|
for (int ff = 0; ff < MAX_ROOM_BGFRAMES; ff++)
|
|
_GP(play).raw_modified[ff] = 0;
|
|
for (size_t i = 0; i < _GP(thisroom).LocalVariables.size() && i < MAX_GLOBAL_VARIABLES; ++i)
|
|
_G(croom)->interactionVariableValues[i] = _GP(thisroom).LocalVariables[i].Value;
|
|
|
|
// ensure that any half-moves (eg. with scaled movement) are stopped
|
|
for (int ff = 0; ff < _GP(game).numcharacters; ff++) {
|
|
_GP(charextra)[ff].xwas = INVALID_X;
|
|
}
|
|
|
|
_GP(play).swap_portrait_lastchar = -1;
|
|
_GP(play).swap_portrait_lastlastchar = -1;
|
|
|
|
for (uint32_t ff = 0; ff < _G(croom)->numobj; ff++) {
|
|
// un-export the object's script object
|
|
if (_GP(thisroom).Objects[ff].ScriptName.IsEmpty())
|
|
continue;
|
|
|
|
ccRemoveExternalSymbol(_GP(thisroom).Objects[ff].ScriptName);
|
|
}
|
|
|
|
for (int ff = 0; ff < MAX_ROOM_HOTSPOTS; ff++) {
|
|
if (_GP(thisroom).Hotspots[ff].ScriptName.IsEmpty())
|
|
continue;
|
|
|
|
ccRemoveExternalSymbol(_GP(thisroom).Hotspots[ff].ScriptName);
|
|
}
|
|
|
|
croom_ptr_clear();
|
|
|
|
// clear the _GP(actsps) buffers to save memory, since the
|
|
// objects/characters involved probably aren't on the
|
|
// new screen. this also ensures all cached data is flushed
|
|
clear_drawobj_cache();
|
|
|
|
// if Hide Player Character was ticked, restore it to visible
|
|
if (_GP(play).temporarily_turned_off_character >= 0) {
|
|
_GP(game).chars[_GP(play).temporarily_turned_off_character].on = 1;
|
|
_GP(play).temporarily_turned_off_character = -1;
|
|
}
|
|
|
|
}
|
|
|
|
// Convert all room objects to the data resolution (only if it's different from game resolution).
|
|
// TODO: merge this into UpdateRoomData? or this is required for engine only?
|
|
void convert_room_coordinates_to_data_res(RoomStruct *rstruc) {
|
|
if (_GP(game).GetDataUpscaleMult() == 1)
|
|
return;
|
|
|
|
const int mul = _GP(game).GetDataUpscaleMult();
|
|
for (auto &obj : rstruc->Objects) {
|
|
obj.X /= mul;
|
|
obj.Y /= mul;
|
|
if (obj.Baseline > 0) {
|
|
obj.Baseline /= mul;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < rstruc->HotspotCount; ++i) {
|
|
rstruc->Hotspots[i].WalkTo.X /= mul;
|
|
rstruc->Hotspots[i].WalkTo.Y /= mul;
|
|
}
|
|
|
|
for (size_t i = 0; i < rstruc->WalkBehindCount; ++i) {
|
|
rstruc->WalkBehinds[i].Baseline /= mul;
|
|
}
|
|
|
|
rstruc->Edges.Left /= mul;
|
|
rstruc->Edges.Top /= mul;
|
|
rstruc->Edges.Bottom /= mul;
|
|
rstruc->Edges.Right /= mul;
|
|
rstruc->Width /= mul;
|
|
rstruc->Height /= mul;
|
|
}
|
|
|
|
|
|
|
|
void update_letterbox_mode() {
|
|
const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
|
|
const Rect game_frame = RectWH(_GP(game).GetGameRes());
|
|
Rect new_main_view = game_frame;
|
|
// In the original engine the letterbox feature only allowed viewports of
|
|
// either 200 or 240 (400 and 480) pixels, if the room height was equal or greater than 200 (400).
|
|
// Also, the UI viewport should be matching room viewport in that case.
|
|
// NOTE: if "OPT_LETTERBOX" is false, altsize.Height = size.Height always.
|
|
const int viewport_height =
|
|
real_room_sz.Height < _GP(game).GetLetterboxSize().Height ? real_room_sz.Height :
|
|
(real_room_sz.Height >= _GP(game).GetLetterboxSize().Height && real_room_sz.Height < _GP(game).GetGameRes().Height) ? _GP(game).GetLetterboxSize().Height :
|
|
_GP(game).GetGameRes().Height;
|
|
new_main_view.SetHeight(viewport_height);
|
|
|
|
_GP(play).SetMainViewport(CenterInRect(game_frame, new_main_view));
|
|
_GP(play).SetUIViewport(new_main_view);
|
|
on_mainviewport_changed();
|
|
}
|
|
|
|
// Automatically reset primary room viewport and camera to match the new room size
|
|
static void adjust_viewport_to_room() {
|
|
const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
|
|
const Rect main_view = _GP(play).GetMainViewport();
|
|
Rect new_room_view = RectWH(Size::Clamp(real_room_sz, Size(1, 1), main_view.GetSize()));
|
|
|
|
auto view = _GP(play).GetRoomViewport(0);
|
|
view->SetRect(new_room_view);
|
|
auto cam = view->GetCamera();
|
|
if (cam) {
|
|
cam->SetSize(new_room_view.GetSize());
|
|
cam->SetAt(0, 0);
|
|
cam->Release();
|
|
}
|
|
}
|
|
|
|
// Run through all viewports and cameras to make sure they can work in new room's bounds
|
|
static void update_all_viewcams_with_newroom() {
|
|
for (int i = 0; i < _GP(play).GetRoomCameraCount(); ++i) {
|
|
auto cam = _GP(play).GetRoomCamera(i);
|
|
const Rect old_pos = cam->GetRect();
|
|
cam->SetSize(old_pos.GetSize());
|
|
cam->SetAt(old_pos.Left, old_pos.Top);
|
|
}
|
|
}
|
|
|
|
// Looks up for the room script available as a separate asset.
|
|
// This is optional, so no error is raised if one is not found.
|
|
// If found however, it will replace room script if one had been loaded
|
|
// from the room file itself.
|
|
HError LoadRoomScript(RoomStruct *room, int newnum) {
|
|
String filename = String::FromFormat("room%d.o", newnum);
|
|
std::unique_ptr<Stream> in(_GP(AssetMgr)->OpenAsset(filename));
|
|
if (in) {
|
|
PScript script(ccScript::CreateFromStream(in.get()));
|
|
if (!script)
|
|
return new Error(String::FromFormat(
|
|
"Failed to load a script module: %s", filename.GetCStr()),
|
|
cc_get_error().ErrorString);
|
|
room->CompiledScript = script;
|
|
}
|
|
return HError::None();
|
|
}
|
|
|
|
static void reset_temp_room() {
|
|
_GP(troom) = RoomStatus();
|
|
}
|
|
|
|
// forchar = playerchar on NewRoom, or NULL if restore saved game
|
|
void load_new_room(int newnum, CharacterInfo *forchar) {
|
|
|
|
debug_script_log("Loading room %d", newnum);
|
|
|
|
String room_filename;
|
|
_G(done_es_error) = 0;
|
|
_GP(play).room_changes ++;
|
|
// TODO: find out why do we need to temporarily lower color depth to 8-bit.
|
|
// Or do we? There's a serious usability problem in this: if any bitmap is
|
|
// created meanwhile it will have this color depth by default, which may
|
|
// lead to unexpected errors.
|
|
set_color_depth(8);
|
|
_G(displayed_room) = newnum;
|
|
|
|
room_filename.Format("room%d.crm", newnum);
|
|
if (newnum == 0) {
|
|
// support both room0.crm and intro.crm
|
|
// 2.70: Renamed intro.crm to room0.crm, to stop it causing confusion
|
|
if ((_G(loaded_game_file_version) < kGameVersion_270 && _GP(AssetMgr)->DoesAssetExist("intro.crm")) ||
|
|
(_G(loaded_game_file_version) >= kGameVersion_270 && !_GP(AssetMgr)->DoesAssetExist(room_filename))) {
|
|
room_filename = "intro.crm";
|
|
}
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
// load the room from disk
|
|
_G(our_eip) = 200;
|
|
_GP(thisroom).GameID = NO_GAME_ID_IN_ROOM_FILE;
|
|
load_room(room_filename, &_GP(thisroom), _GP(game).IsLegacyHiRes(), _GP(game).SpriteInfos);
|
|
|
|
if ((_GP(thisroom).GameID != NO_GAME_ID_IN_ROOM_FILE) &&
|
|
(_GP(thisroom).GameID != _GP(game).uniqueid)) {
|
|
quitprintf("!Unable to load '%s'. This room file is assigned to a different _GP(game).", room_filename.GetCStr());
|
|
}
|
|
|
|
HError err = LoadRoomScript(&_GP(thisroom), newnum);
|
|
if (!err)
|
|
quitprintf("!Unable to load '%s'. Error: %s", room_filename.GetCStr(),
|
|
err->FullMessage().GetCStr());
|
|
|
|
convert_room_coordinates_to_data_res(&_GP(thisroom));
|
|
|
|
update_polled_stuff_if_runtime();
|
|
_G(our_eip) = 201;
|
|
|
|
_GP(play).room_width = _GP(thisroom).Width;
|
|
_GP(play).room_height = _GP(thisroom).Height;
|
|
_GP(play).anim_background_speed = _GP(thisroom).BgAnimSpeed;
|
|
_GP(play).bg_anim_delay = _GP(play).anim_background_speed;
|
|
|
|
// Fixup the frame index, in case the new room does not have enough background frames
|
|
if (_GP(play).bg_frame < 0 || static_cast<size_t>(_GP(play).bg_frame) >= _GP(thisroom).BgFrameCount)
|
|
_GP(play).bg_frame = 0;
|
|
|
|
// do the palette
|
|
for (size_t cc = 0; cc < 256; cc++) {
|
|
if (_GP(game).paluses[cc] == PAL_BACKGROUND)
|
|
_G(palette)[cc] = _GP(thisroom).Palette[cc];
|
|
else {
|
|
// copy the gamewide colours into the room palette
|
|
for (size_t i = 0; i < _GP(thisroom).BgFrameCount; ++i)
|
|
_GP(thisroom).BgFrames[i].Palette[cc] = _G(palette)[cc];
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < _GP(thisroom).BgFrameCount; ++i) {
|
|
update_polled_stuff_if_runtime();
|
|
_GP(thisroom).BgFrames[i].Graphic = PrepareSpriteForUse(_GP(thisroom).BgFrames[i].Graphic, false);
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
_G(our_eip) = 202;
|
|
// Update game viewports
|
|
if (_GP(game).IsLegacyLetterbox())
|
|
update_letterbox_mode();
|
|
SetMouseBounds(0, 0, 0, 0);
|
|
|
|
_G(our_eip) = 203;
|
|
_G(in_new_room) = 1;
|
|
|
|
set_color_depth(_GP(game).GetColorDepth());
|
|
// Make sure the room gfx and masks are matching game's native res
|
|
convert_room_background_to_game_res();
|
|
|
|
// walkable_areas_temp is used by the pathfinder to generate a
|
|
// copy of the walkable areas - allocate it here to save time later
|
|
delete _G(walkable_areas_temp);
|
|
_G(walkable_areas_temp) = BitmapHelper::CreateBitmap(_GP(thisroom).WalkAreaMask->GetWidth(), _GP(thisroom).WalkAreaMask->GetHeight(), 8);
|
|
|
|
// Make a backup copy of the walkable areas prior to
|
|
// any RemoveWalkableArea commands
|
|
delete _G(walkareabackup);
|
|
// copy the walls screen
|
|
_G(walkareabackup) = BitmapHelper::CreateBitmapCopy(_GP(thisroom).WalkAreaMask.get());
|
|
|
|
_G(our_eip) = 204;
|
|
update_polled_stuff_if_runtime();
|
|
redo_walkable_areas();
|
|
update_polled_stuff_if_runtime();
|
|
walkbehinds_recalc();
|
|
update_polled_stuff_if_runtime();
|
|
|
|
_G(our_eip) = 205;
|
|
// setup objects
|
|
if (forchar != nullptr) {
|
|
// if not restoring a game, always reset this room
|
|
reset_temp_room();
|
|
}
|
|
if ((newnum >= 0) & (newnum < MAX_ROOMS))
|
|
_G(croom) = getRoomStatus(newnum);
|
|
else _G(croom) = &_GP(troom);
|
|
|
|
// Decide what to do if we have been or not in this room before
|
|
if (_G(croom)->beenhere > 0) {
|
|
// if we've been here before, save the Times Run information
|
|
// since we will overwrite the actual NewInteraction structs
|
|
// (cos they have pointers and this might have been loaded from
|
|
// a save game)
|
|
if (_GP(thisroom).EventHandlers == nullptr) {
|
|
// legacy interactions
|
|
_GP(thisroom).Interaction->CopyTimesRun(_G(croom)->intrRoom);
|
|
for (int cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
|
|
_GP(thisroom).Hotspots[cc].Interaction->CopyTimesRun(_G(croom)->intrHotspot[cc]);
|
|
for (size_t cc = 0; cc < _GP(thisroom).Objects.size(); cc++)
|
|
_GP(thisroom).Objects[cc].Interaction->CopyTimesRun(_G(croom)->intrObject[cc]);
|
|
for (int cc = 0; cc < MAX_ROOM_REGIONS; cc++)
|
|
_GP(thisroom).Regions[cc].Interaction->CopyTimesRun(_G(croom)->intrRegion[cc]);
|
|
}
|
|
for (size_t i = 0; i < _GP(thisroom).LocalVariables.size() && i < (size_t)MAX_GLOBAL_VARIABLES; ++i)
|
|
_GP(thisroom).LocalVariables[i].Value = _G(croom)->interactionVariableValues[i];
|
|
|
|
// Always copy object and hotspot names for < 3.6.0 games, because they were not settable
|
|
if (_G(loaded_game_file_version) < kGameVersion_360_16) {
|
|
for (size_t cc = 0; cc < _GP(thisroom).Objects.size(); ++cc)
|
|
_G(croom)->obj[cc].name = _GP(thisroom).Objects[cc].Name;
|
|
for (int cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
|
|
_G(croom)->hotspot[cc].Name = _GP(thisroom).Hotspots[cc].Name;
|
|
}
|
|
} else {
|
|
// If we have not been in this room before, then copy necessary fields from _GP(thisroom)
|
|
_G(croom)->numobj = _GP(thisroom).Objects.size();
|
|
_G(croom)->tsdatasize = 0;
|
|
_G(croom)->obj.resize(_G(croom)->numobj);
|
|
_G(croom)->objProps.resize(_G(croom)->numobj);
|
|
_G(croom)->intrObject.resize(_G(croom)->numobj);
|
|
for (size_t cc = 0; cc < _G(croom)->numobj; cc++) {
|
|
const auto &trobj = _GP(thisroom).Objects[cc];
|
|
auto &crobj = _G(croom)->obj[cc];
|
|
crobj.x = trobj.X;
|
|
crobj.y = trobj.Y;
|
|
crobj.num = Math::InRangeOrDef<uint16_t>(trobj.Sprite, 0);
|
|
crobj.on = trobj.IsOn;
|
|
crobj.view = RoomObject::NoView;
|
|
crobj.loop = 0;
|
|
crobj.frame = 0;
|
|
crobj.wait = 0;
|
|
crobj.transparent = 0;
|
|
crobj.moving = -1;
|
|
crobj.flags = trobj.Flags;
|
|
crobj.baseline = -1;
|
|
crobj.zoom = 100;
|
|
crobj.last_width = 0;
|
|
crobj.last_height = 0;
|
|
crobj.blocking_width = 0;
|
|
crobj.blocking_height = 0;
|
|
crobj.name = trobj.Name;
|
|
if (trobj.Baseline >= 0)
|
|
crobj.baseline = trobj.Baseline;
|
|
if (trobj.Sprite > UINT16_MAX)
|
|
debug_script_warn("Warning: object's (id %d) sprite %d outside of internal range (%d), reset to 0",
|
|
cc, trobj.Sprite, UINT16_MAX);
|
|
}
|
|
for (size_t i = 0; i < (size_t)MAX_WALK_BEHINDS; ++i)
|
|
_G(croom)->walkbehind_base[i] = _GP(thisroom).WalkBehinds[i].Baseline;
|
|
|
|
for (int cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++) {
|
|
_G(croom)->hotspot[cc].Enabled = true;
|
|
_G(croom)->hotspot[cc].Name = _GP(thisroom).Hotspots[cc].Name;
|
|
}
|
|
for (int cc = 0; cc < MAX_ROOM_REGIONS; cc++) {
|
|
_G(croom)->region_enabled[cc] = 1;
|
|
}
|
|
|
|
#if defined (OBSOLETE)
|
|
for (uint cc = 0; cc < MAX_LEGACY_ROOM_FLAGS; cc++) _G(croom)->flagstates[cc] = 0;
|
|
// we copy these structs for the Score column to work
|
|
_G(croom)->misccond = _GP(thisroom).misccond;
|
|
for (uint cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
|
|
_G(croom)->hscond[cc] = _GP(thisroom).hscond[cc];
|
|
for (uint cc = 0; cc < MAX_ROOM_OBJECTS; cc++)
|
|
_G(croom)->objcond[cc] = _GP(thisroom).objcond[cc];
|
|
#endif
|
|
|
|
_G(croom)->beenhere = 1;
|
|
_G(in_new_room) = 2;
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
if (_GP(thisroom).EventHandlers == nullptr) {
|
|
// legacy interactions
|
|
// copy interactions from room file into our temporary struct
|
|
_G(croom)->intrRoom = *_GP(thisroom).Interaction;
|
|
for (int cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
|
|
_G(croom)->intrHotspot[cc] = *_GP(thisroom).Hotspots[cc].Interaction;
|
|
for (size_t cc = 0; cc < _GP(thisroom).Objects.size(); cc++)
|
|
_G(croom)->intrObject[cc] = *_GP(thisroom).Objects[cc].Interaction;
|
|
for (int cc = 0; cc < MAX_ROOM_REGIONS; cc++)
|
|
_G(croom)->intrRegion[cc] = *_GP(thisroom).Regions[cc].Interaction;
|
|
}
|
|
|
|
_G(objs) = _G(croom)->obj.size() > 0 ? &_G(croom)->obj[0] : nullptr;
|
|
|
|
for (size_t cc = 0; cc < _G(croom)->numobj; cc++) {
|
|
// export the object's script object
|
|
if (_GP(thisroom).Objects[cc].ScriptName.IsEmpty())
|
|
continue;
|
|
ccAddExternalDynamicObject(_GP(thisroom).Objects[cc].ScriptName, &_G(scrObj)[cc], &_GP(ccDynamicObject));
|
|
}
|
|
|
|
for (int cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++) {
|
|
if (_GP(thisroom).Hotspots[cc].ScriptName.IsEmpty())
|
|
continue;
|
|
|
|
ccAddExternalDynamicObject(_GP(thisroom).Hotspots[cc].ScriptName, &_G(scrHotspot)[cc], &_GP(ccDynamicHotspot));
|
|
}
|
|
|
|
_G(our_eip) = 206;
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
_G(our_eip) = 210;
|
|
if (IS_ANTIALIAS_SPRITES) {
|
|
// sometimes the palette has corrupt entries, which crash
|
|
// the create_rgb_table call
|
|
// so, fix them
|
|
for (int ff = 0; ff < 256; ff++) {
|
|
if (_G(palette)[ff].r > 63)
|
|
_G(palette)[ff].r = 63;
|
|
if (_G(palette)[ff].g > 63)
|
|
_G(palette)[ff].g = 63;
|
|
if (_G(palette)[ff].b > 63)
|
|
_G(palette)[ff].b = 63;
|
|
}
|
|
create_rgb_table(&_GP(rgb_table), _G(palette), nullptr);
|
|
_G(rgb_map) = &_GP(rgb_table);
|
|
}
|
|
_G(our_eip) = 211;
|
|
if (forchar != nullptr) {
|
|
// if it's not a Restore Game
|
|
|
|
// if a following character is still waiting to come into the
|
|
// previous room, force it out so that the timer resets
|
|
for (int ff = 0; ff < _GP(game).numcharacters; ff++) {
|
|
if ((_GP(game).chars[ff].following >= 0) && (_GP(game).chars[ff].room < 0)) {
|
|
if ((_GP(game).chars[ff].following == _GP(game).playercharacter) &&
|
|
(forchar->prevroom == newnum))
|
|
// the player went back to the previous room, so make sure
|
|
// the following character is still there
|
|
_GP(game).chars[ff].room = newnum;
|
|
else
|
|
_GP(game).chars[ff].room = _GP(game).chars[_GP(game).chars[ff].following].room;
|
|
}
|
|
}
|
|
|
|
forchar->prevroom = forchar->room;
|
|
forchar->room = newnum;
|
|
|
|
// only stop moving if it's a new room, not a restore game
|
|
for (int cc = 0; cc < _GP(game).numcharacters; cc++)
|
|
StopMoving(cc);
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
_G(roominst) = nullptr;
|
|
if (_G(debug_flags) & DBG_NOSCRIPT) ;
|
|
else if (_GP(thisroom).CompiledScript != nullptr) {
|
|
compile_room_script();
|
|
if (_G(croom)->tsdatasize > 0) {
|
|
if (_G(croom)->tsdatasize != _G(roominst)->globaldatasize)
|
|
quit("room script data segment size has changed");
|
|
memcpy(&_G(roominst)->globaldata[0], _G(croom)->tsdata, _G(croom)->tsdatasize);
|
|
}
|
|
}
|
|
_G(our_eip) = 207;
|
|
_GP(play).entered_edge = -1;
|
|
|
|
if ((_G(new_room_x) != SCR_NO_VALUE) && (forchar != nullptr)) {
|
|
forchar->x = _G(new_room_x);
|
|
forchar->y = _G(new_room_y);
|
|
if (_G(new_room_placeonwalkable))
|
|
Character_PlaceOnWalkableArea(forchar);
|
|
|
|
if (_G(new_room_loop) != SCR_NO_VALUE)
|
|
forchar->loop = _G(new_room_loop);
|
|
}
|
|
|
|
// reset new_room instructions
|
|
_G(new_room_x) = _G(new_room_y) = SCR_NO_VALUE;
|
|
_G(new_room_loop) = SCR_NO_VALUE;
|
|
_G(new_room_placeonwalkable) = false;
|
|
|
|
if ((_G(new_room_pos) > 0) & (forchar != nullptr)) {
|
|
if (_G(new_room_pos) >= 4000) {
|
|
_GP(play).entered_edge = 3;
|
|
forchar->y = _GP(thisroom).Edges.Top + get_fixed_pixel_size(1);
|
|
forchar->x = _G(new_room_pos) % 1000;
|
|
if (forchar->x == 0) forchar->x = _GP(thisroom).Width / 2;
|
|
if (forchar->x <= _GP(thisroom).Edges.Left)
|
|
forchar->x = _GP(thisroom).Edges.Left + 3;
|
|
if (forchar->x >= _GP(thisroom).Edges.Right)
|
|
forchar->x = _GP(thisroom).Edges.Right - 3;
|
|
forchar->loop = 0;
|
|
} else if (_G(new_room_pos) >= 3000) {
|
|
_GP(play).entered_edge = 2;
|
|
forchar->y = _GP(thisroom).Edges.Bottom - get_fixed_pixel_size(1);
|
|
forchar->x = _G(new_room_pos) % 1000;
|
|
if (forchar->x == 0) forchar->x = _GP(thisroom).Width / 2;
|
|
if (forchar->x <= _GP(thisroom).Edges.Left)
|
|
forchar->x = _GP(thisroom).Edges.Left + 3;
|
|
if (forchar->x >= _GP(thisroom).Edges.Right)
|
|
forchar->x = _GP(thisroom).Edges.Right - 3;
|
|
forchar->loop = 3;
|
|
} else if (_G(new_room_pos) >= 2000) {
|
|
_GP(play).entered_edge = 1;
|
|
forchar->x = _GP(thisroom).Edges.Right - get_fixed_pixel_size(1);
|
|
forchar->y = _G(new_room_pos) % 1000;
|
|
if (forchar->y == 0) forchar->y = _GP(thisroom).Height / 2;
|
|
if (forchar->y <= _GP(thisroom).Edges.Top)
|
|
forchar->y = _GP(thisroom).Edges.Top + 3;
|
|
if (forchar->y >= _GP(thisroom).Edges.Bottom)
|
|
forchar->y = _GP(thisroom).Edges.Bottom - 3;
|
|
forchar->loop = 1;
|
|
} else if (_G(new_room_pos) >= 1000) {
|
|
_GP(play).entered_edge = 0;
|
|
forchar->x = _GP(thisroom).Edges.Left + get_fixed_pixel_size(1);
|
|
forchar->y = _G(new_room_pos) % 1000;
|
|
if (forchar->y == 0) forchar->y = _GP(thisroom).Height / 2;
|
|
if (forchar->y <= _GP(thisroom).Edges.Top)
|
|
forchar->y = _GP(thisroom).Edges.Top + 3;
|
|
if (forchar->y >= _GP(thisroom).Edges.Bottom)
|
|
forchar->y = _GP(thisroom).Edges.Bottom - 3;
|
|
forchar->loop = 2;
|
|
}
|
|
// if starts on un-walkable area
|
|
if (get_walkable_area_pixel(forchar->x, forchar->y) == 0) {
|
|
if (_G(new_room_pos) >= 3000) { // bottom or top of screen
|
|
int tryleft = forchar->x - 1, tryright = forchar->x + 1;
|
|
while (1) {
|
|
if (get_walkable_area_pixel(tryleft, forchar->y) > 0) {
|
|
forchar->x = tryleft;
|
|
break;
|
|
}
|
|
if (get_walkable_area_pixel(tryright, forchar->y) > 0) {
|
|
forchar->x = tryright;
|
|
break;
|
|
}
|
|
int nowhere = 0;
|
|
if (tryleft > _GP(thisroom).Edges.Left) {
|
|
tryleft--;
|
|
nowhere++;
|
|
}
|
|
if (tryright < _GP(thisroom).Edges.Right) {
|
|
tryright++;
|
|
nowhere++;
|
|
}
|
|
if (nowhere == 0) break; // no place to go, so leave him
|
|
}
|
|
} else if (_G(new_room_pos) >= 1000) { // left or right
|
|
int tryleft = forchar->y - 1, tryright = forchar->y + 1;
|
|
while (1) {
|
|
if (get_walkable_area_pixel(forchar->x, tryleft) > 0) {
|
|
forchar->y = tryleft;
|
|
break;
|
|
}
|
|
if (get_walkable_area_pixel(forchar->x, tryright) > 0) {
|
|
forchar->y = tryright;
|
|
break;
|
|
}
|
|
int nowhere = 0;
|
|
if (tryleft > _GP(thisroom).Edges.Top) {
|
|
tryleft--;
|
|
nowhere++;
|
|
}
|
|
if (tryright < _GP(thisroom).Edges.Bottom) {
|
|
tryright++;
|
|
nowhere++;
|
|
}
|
|
if (nowhere == 0) break; // no place to go, so leave him
|
|
}
|
|
}
|
|
}
|
|
_G(new_room_pos) = 0;
|
|
}
|
|
if (forchar != nullptr) {
|
|
_GP(play).entered_at_x = forchar->x;
|
|
_GP(play).entered_at_y = forchar->y;
|
|
if (forchar->x >= _GP(thisroom).Edges.Right)
|
|
_GP(play).entered_edge = 1;
|
|
else if (forchar->x <= _GP(thisroom).Edges.Left)
|
|
_GP(play).entered_edge = 0;
|
|
else if (forchar->y >= _GP(thisroom).Edges.Bottom)
|
|
_GP(play).entered_edge = 2;
|
|
else if (forchar->y <= _GP(thisroom).Edges.Top)
|
|
_GP(play).entered_edge = 3;
|
|
}
|
|
if (_GP(thisroom).Options.StartupMusic > 0)
|
|
PlayMusicResetQueue(_GP(thisroom).Options.StartupMusic);
|
|
|
|
_G(our_eip) = 208;
|
|
if (forchar != nullptr) {
|
|
if (_GP(thisroom).Options.PlayerCharOff == 0) {
|
|
forchar->on = 1;
|
|
enable_cursor_mode(0);
|
|
} else {
|
|
forchar->on = 0;
|
|
disable_cursor_mode(0);
|
|
// remember which character we turned off, in case they
|
|
// use SetPlyaerChracter within this room (so we re-enable
|
|
// the correct character when leaving the room)
|
|
_GP(play).temporarily_turned_off_character = _GP(game).playercharacter;
|
|
}
|
|
if (forchar->flags & CHF_FIXVIEW) ;
|
|
else if (_GP(thisroom).Options.PlayerView == 0) forchar->view = forchar->defview;
|
|
else forchar->view = _GP(thisroom).Options.PlayerView - 1;
|
|
forchar->frame = 0; // make him standing
|
|
}
|
|
_G(color_map) = nullptr;
|
|
|
|
_G(our_eip) = 209;
|
|
update_polled_stuff_if_runtime();
|
|
generate_light_table();
|
|
update_music_volume();
|
|
|
|
// If we are not restoring a save, update cameras to accommodate for this
|
|
// new room; otherwise this is done later when cameras are recreated.
|
|
if (forchar != nullptr) {
|
|
if (_GP(play).IsAutoRoomViewport())
|
|
adjust_viewport_to_room();
|
|
update_all_viewcams_with_newroom();
|
|
_GP(play).UpdateRoomCameras(); // update auto tracking
|
|
}
|
|
init_room_drawdata();
|
|
|
|
_G(our_eip) = 212;
|
|
invalidate_screen();
|
|
for (size_t cc = 0; cc < _G(croom)->numobj; cc++) {
|
|
if (_G(objs)[cc].on == 2)
|
|
MergeObject(cc);
|
|
}
|
|
_G(new_room_flags) = 0;
|
|
_GP(play).gscript_timer = -1; // avoid screw-ups with changing screens
|
|
_GP(play).player_on_region = 0;
|
|
// trash any input which they might have done while it was loading
|
|
ags_clear_input_buffer();
|
|
// no fade in, so set the palette immediately in case of 256-col sprites
|
|
if (_GP(game).color_depth > 1)
|
|
setpal();
|
|
|
|
_G(our_eip) = 220;
|
|
update_polled_stuff_if_runtime();
|
|
debug_script_log("Now in room %d", _G(displayed_room));
|
|
GUI::MarkAllGUIForUpdate();
|
|
pl_run_plugin_hooks(AGSE_ENTERROOM, _G(displayed_room));
|
|
}
|
|
|
|
// new_room: changes the current room number, and loads the new room from disk
|
|
void new_room(int newnum, CharacterInfo *forchar) {
|
|
EndSkippingUntilCharStops();
|
|
|
|
debug_script_log("Room change requested to room %d", newnum);
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
// we are currently running Leaves Screen scripts
|
|
_G(in_leaves_screen) = newnum;
|
|
|
|
// player leaves screen event
|
|
run_room_event(8);
|
|
// Run the global OnRoomLeave event
|
|
run_on_event(GE_LEAVE_ROOM, RuntimeScriptValue().SetInt32(_G(displayed_room)));
|
|
|
|
pl_run_plugin_hooks(AGSE_LEAVEROOM, _G(displayed_room));
|
|
|
|
// update the new room number if it has been altered by OnLeave scripts
|
|
newnum = _G(in_leaves_screen);
|
|
_G(in_leaves_screen) = -1;
|
|
|
|
if ((_G(playerchar)->following >= 0) &&
|
|
(_GP(game).chars[_G(playerchar)->following].room != newnum)) {
|
|
// the player character is following another character,
|
|
// who is not in the new room. therefore, abort the follow
|
|
_G(playerchar)->following = -1;
|
|
}
|
|
update_polled_stuff_if_runtime();
|
|
|
|
// change rooms
|
|
unload_old_room();
|
|
|
|
if (_GP(usetup).clear_cache_on_room_change) {
|
|
// Delete all cached sprites
|
|
_GP(spriteset).DisposeAll();
|
|
|
|
GUI::MarkAllGUIForUpdate();
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
load_new_room(newnum, forchar);
|
|
|
|
// Update background frame state (it's not a part of the RoomStatus currently)
|
|
_GP(play).bg_frame = 0;
|
|
_GP(play).bg_frame_locked = (_GP(thisroom).Options.Flags & kRoomFlag_BkgFrameLocked) != 0;
|
|
}
|
|
|
|
void set_room_placeholder() {
|
|
_GP(thisroom).InitDefaults();
|
|
std::shared_ptr<Bitmap> dummy_bg(new Bitmap(1, 1, 8));
|
|
_GP(thisroom).BgFrames[0].Graphic = dummy_bg;
|
|
_GP(thisroom).HotspotMask = dummy_bg;
|
|
_GP(thisroom).RegionMask = dummy_bg;
|
|
_GP(thisroom).WalkAreaMask = dummy_bg;
|
|
_GP(thisroom).WalkBehindMask = dummy_bg;
|
|
reset_temp_room();
|
|
_G(croom) = &_GP(troom);
|
|
}
|
|
|
|
int find_highest_room_entered() {
|
|
int qq, fndas = -1;
|
|
for (qq = 0; qq < MAX_ROOMS; qq++) {
|
|
if (isRoomStatusValid(qq) && (getRoomStatus(qq)->beenhere != 0))
|
|
fndas = qq;
|
|
}
|
|
return fndas;
|
|
}
|
|
|
|
void first_room_initialization() {
|
|
_G(starting_room) = _G(displayed_room);
|
|
_G(playerchar)->prevroom = -1;
|
|
set_loop_counter(0);
|
|
_G(mouse_z_was) = _G(sys_mouse_z);
|
|
// Reset background frame state
|
|
_GP(play).bg_frame = 0;
|
|
_GP(play).bg_frame_locked = (_GP(thisroom).Options.Flags & kRoomFlag_BkgFrameLocked) != 0;
|
|
}
|
|
|
|
void check_new_room() {
|
|
// if they're in a new room, run Player Enters Screen and on_event(ENTER_ROOM)
|
|
if ((_G(in_new_room) > 0) & (_G(in_new_room) != 3)) {
|
|
EventHappened evh;
|
|
evh.type = EV_RUNEVBLOCK;
|
|
evh.data1 = EVB_ROOM;
|
|
evh.data2 = 0;
|
|
evh.data3 = EVROM_BEFOREFADEIN;
|
|
evh.player = _GP(game).playercharacter;
|
|
// make sure that any script calls don't re-call enters screen
|
|
int newroom_was = _G(in_new_room);
|
|
_G(in_new_room) = 0;
|
|
_GP(play).disabled_user_interface ++;
|
|
process_event(&evh);
|
|
_GP(play).disabled_user_interface --;
|
|
_G(in_new_room) = newroom_was;
|
|
}
|
|
}
|
|
|
|
void compile_room_script() {
|
|
cc_clear_error();
|
|
|
|
_G(roominst) = ccInstance::CreateFromScript(_GP(thisroom).CompiledScript);
|
|
if (cc_has_error() || (_G(roominst) == nullptr)) {
|
|
quitprintf("Unable to create local script:\n%s", cc_get_error().ErrorString.GetCStr());
|
|
}
|
|
|
|
if (!_G(roominst)->ResolveScriptImports(_G(roominst)->instanceof.get()))
|
|
quitprintf("Unable to resolve imports in room script:\n%s", cc_get_error().ErrorString.GetCStr());
|
|
|
|
if (!_G(roominst)->ResolveImportFixups(_G(roominst)->instanceof.get()))
|
|
quitprintf("Unable to resolve import fixups in room script:\n%s", cc_get_error().ErrorString.GetCStr());
|
|
|
|
_G(roominstFork) = _G(roominst)->Fork();
|
|
if (_G(roominstFork) == nullptr)
|
|
quitprintf("Unable to create forked room instance:\n%s", cc_get_error().ErrorString.GetCStr());
|
|
|
|
_GP(repExecAlways).roomHasFunction = true;
|
|
_GP(lateRepExecAlways).roomHasFunction = true;
|
|
_GP(getDialogOptionsDimensionsFunc).roomHasFunction = true;
|
|
}
|
|
|
|
void on_background_frame_change() {
|
|
invalidate_screen();
|
|
mark_current_background_dirty();
|
|
|
|
// get the new frame's palette
|
|
memcpy(_G(palette), _GP(thisroom).BgFrames[_GP(play).bg_frame].Palette, sizeof(RGB) * 256);
|
|
|
|
// hi-colour, update the palette. It won't have an immediate effect
|
|
// but will be drawn properly when the screen fades in
|
|
if (_GP(game).color_depth > 1)
|
|
setpal();
|
|
|
|
if (_G(in_enters_screen))
|
|
return;
|
|
|
|
// Don't update the palette if it hasn't changed
|
|
if (_GP(thisroom).BgFrames[_GP(play).bg_frame].IsPaletteShared)
|
|
return;
|
|
|
|
// 256-colours, tell it to update the palette (will actually be done as
|
|
// close as possible to the screen update to prevent flicker problem)
|
|
if (_GP(game).color_depth == 1)
|
|
_G(bg_just_changed) = 1;
|
|
}
|
|
|
|
void croom_ptr_clear() {
|
|
_G(croom) = nullptr;
|
|
_G(objs) = nullptr;
|
|
}
|
|
|
|
|
|
AGS_INLINE int room_to_mask_coord(int coord) {
|
|
return coord * _GP(game).GetDataUpscaleMult() / _GP(thisroom).MaskResolution;
|
|
}
|
|
|
|
AGS_INLINE int mask_to_room_coord(int coord) {
|
|
return coord * _GP(thisroom).MaskResolution / _GP(game).GetDataUpscaleMult();
|
|
}
|
|
|
|
void convert_move_path_to_room_resolution(MoveList *ml) {
|
|
if ((_GP(game).options[OPT_WALKSPEEDABSOLUTE] != 0) && _GP(game).GetDataUpscaleMult() > 1) {
|
|
// Speeds are independent from MaskResolution
|
|
for (int i = 0; i < ml->numstage; i++) {
|
|
// ...so they are not multiplied by MaskResolution factor when converted to room coords
|
|
ml->xpermove[i] = ml->xpermove[i] / _GP(game).GetDataUpscaleMult();
|
|
ml->ypermove[i] = ml->ypermove[i] / _GP(game).GetDataUpscaleMult();
|
|
}
|
|
}
|
|
|
|
if (_GP(thisroom).MaskResolution == _GP(game).GetDataUpscaleMult())
|
|
return;
|
|
|
|
ml->fromx = mask_to_room_coord(ml->fromx);
|
|
ml->fromy = mask_to_room_coord(ml->fromy);
|
|
ml->lastx = mask_to_room_coord(ml->lastx);
|
|
ml->lasty = mask_to_room_coord(ml->lasty);
|
|
|
|
for (int i = 0; i < ml->numstage; i++) {
|
|
uint16_t lowPart = mask_to_room_coord(ml->pos[i] & 0x0000ffff);
|
|
uint16_t highPart = mask_to_room_coord((ml->pos[i] >> 16) & 0x0000ffff);
|
|
ml->pos[i] = ((int)highPart << 16) | (lowPart & 0x0000ffff);
|
|
}
|
|
|
|
if (_GP(game).options[OPT_WALKSPEEDABSOLUTE] == 0) {
|
|
// Speeds are scaling with MaskResolution
|
|
for (int i = 0; i < ml->numstage; i++) {
|
|
ml->xpermove[i] = mask_to_room_coord(ml->xpermove[i]);
|
|
ml->ypermove[i] = mask_to_room_coord(ml->ypermove[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Script API Functions
|
|
//
|
|
//=============================================================================
|
|
|
|
// ScriptDrawingSurface* (int backgroundNumber)
|
|
RuntimeScriptValue Sc_Room_GetDrawingSurfaceForBackground(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJAUTO_PINT(ScriptDrawingSurface, Room_GetDrawingSurfaceForBackground);
|
|
}
|
|
|
|
// int (const char *property)
|
|
RuntimeScriptValue Sc_Room_GetProperty(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT_POBJ(Room_GetProperty, const char);
|
|
}
|
|
|
|
// const char* (const char *property)
|
|
RuntimeScriptValue Sc_Room_GetTextProperty(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_POBJ(const char, _GP(myScriptStringImpl), Room_GetTextProperty, const char);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Room_SetProperty(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_BOOL_POBJ_PINT(Room_SetProperty, const char);
|
|
}
|
|
|
|
// const char* (const char *property)
|
|
RuntimeScriptValue Sc_Room_SetTextProperty(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_BOOL_POBJ2(Room_SetTextProperty, const char, const char);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetBottomEdge(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetBottomEdge);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetColorDepth(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetColorDepth);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetHeight(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetHeight);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetLeftEdge(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetLeftEdge);
|
|
}
|
|
|
|
// const char* (int index)
|
|
RuntimeScriptValue Sc_Room_GetMessages(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Room_GetMessages);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetMusicOnLoad(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetMusicOnLoad);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetObjectCount(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetObjectCount);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetRightEdge(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetRightEdge);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetTopEdge(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetTopEdge);
|
|
}
|
|
|
|
// int ()
|
|
RuntimeScriptValue Sc_Room_GetWidth(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_INT(Room_GetWidth);
|
|
}
|
|
|
|
// void (int xx,int yy,int mood)
|
|
RuntimeScriptValue Sc_RoomProcessClick(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_VOID_PINT3(RoomProcessClick);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Room_Exists(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_BOOL_PINT(Room_Exists);
|
|
}
|
|
|
|
|
|
void RegisterRoomAPI() {
|
|
ccAddExternalStaticFunction("Room::GetDrawingSurfaceForBackground^1", Sc_Room_GetDrawingSurfaceForBackground);
|
|
ccAddExternalStaticFunction("Room::GetProperty^1", Sc_Room_GetProperty);
|
|
ccAddExternalStaticFunction("Room::GetTextProperty^1", Sc_Room_GetTextProperty);
|
|
ccAddExternalStaticFunction("Room::SetProperty^2", Sc_Room_SetProperty);
|
|
ccAddExternalStaticFunction("Room::SetTextProperty^2", Sc_Room_SetTextProperty);
|
|
ccAddExternalStaticFunction("Room::ProcessClick^3", Sc_RoomProcessClick);
|
|
ccAddExternalStaticFunction("ProcessClick", Sc_RoomProcessClick);
|
|
ccAddExternalStaticFunction("Room::get_BottomEdge", Sc_Room_GetBottomEdge);
|
|
ccAddExternalStaticFunction("Room::get_ColorDepth", Sc_Room_GetColorDepth);
|
|
ccAddExternalStaticFunction("Room::get_Height", Sc_Room_GetHeight);
|
|
ccAddExternalStaticFunction("Room::get_LeftEdge", Sc_Room_GetLeftEdge);
|
|
ccAddExternalStaticFunction("Room::geti_Messages", Sc_Room_GetMessages);
|
|
ccAddExternalStaticFunction("Room::get_MusicOnLoad", Sc_Room_GetMusicOnLoad);
|
|
ccAddExternalStaticFunction("Room::get_ObjectCount", Sc_Room_GetObjectCount);
|
|
ccAddExternalStaticFunction("Room::get_RightEdge", Sc_Room_GetRightEdge);
|
|
ccAddExternalStaticFunction("Room::get_TopEdge", Sc_Room_GetTopEdge);
|
|
ccAddExternalStaticFunction("Room::get_Width", Sc_Room_GetWidth);
|
|
ccAddExternalStaticFunction("Room::Exists", Sc_Room_Exists);
|
|
}
|
|
|
|
} // namespace AGS3
|