Files
scummvm-cursorfix/engines/ags/shared/game/room_struct.cpp
2026-02-02 04:50:13 +01:00

287 lines
7.2 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/ac/common.h" // quit
#include "ags/shared/game/room_file.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/gfx/bitmap.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
RoomOptions::RoomOptions()
: StartupMusic(0)
, SaveLoadDisabled(false)
, PlayerCharOff(false)
, PlayerView(0)
, MusicVolume(kRoomVolumeNormal)
, Flags(0) {
}
RoomBgFrame::RoomBgFrame()
: IsPaletteShared(false) {
memset(Palette, 0, sizeof(Palette));
}
RoomEdges::RoomEdges()
: Left(0)
, Right(0)
, Top(0)
, Bottom(0) {
}
RoomEdges::RoomEdges(int l, int r, int t, int b)
: Left(l)
, Right(r)
, Top(t)
, Bottom(b) {
}
RoomObjectInfo::RoomObjectInfo()
: Sprite(0)
, X(0)
, Y(0)
, Room(-1)
, IsOn(false)
, Baseline(0xFF)
, Flags(0) {
}
RoomRegion::RoomRegion()
: Light(0)
, Tint(0) {
}
WalkArea::WalkArea()
: CharacterView(0)
, ScalingFar(0)
, ScalingNear(NOT_VECTOR_SCALED)
, PlayerView(0)
, Top(-1)
, Bottom(-1) {
}
WalkBehind::WalkBehind()
: Baseline(0) {
}
MessageInfo::MessageInfo()
: DisplayAs(0)
, Flags(0) {
}
RoomStruct::RoomStruct() {
InitDefaults();
}
RoomStruct::~RoomStruct() {
Free();
}
void RoomStruct::Free() {
for (size_t i = 0; i < (size_t)MAX_ROOM_BGFRAMES; ++i)
BgFrames[i].Graphic.reset();
HotspotMask.reset();
RegionMask.reset();
WalkAreaMask.reset();
WalkBehindMask.reset();
LocalVariables.clear();
Interaction.reset();
Properties.clear();
for (size_t i = 0; i < (size_t)MAX_ROOM_HOTSPOTS; ++i) {
Hotspots[i].Interaction.reset();
Hotspots[i].Properties.clear();
}
Objects.clear();
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i) {
Regions[i].Interaction.reset();
Regions[i].Properties.clear();
}
FreeMessages();
FreeScripts();
}
void RoomStruct::FreeMessages() {
for (size_t i = 0; i < MessageCount; ++i) {
Messages[i].Free();
MessageInfos[i] = MessageInfo();
}
MessageCount = 0;
}
void RoomStruct::FreeScripts() {
CompiledScript.reset();
EventHandlers.reset();
for (size_t i = 0; i < HotspotCount; ++i)
Hotspots[i].EventHandlers.reset();
for (auto &obj : Objects)
obj.EventHandlers.reset();
for (size_t i = 0; i < RegionCount; ++i)
Regions[i].EventHandlers.reset();
}
void RoomStruct::InitDefaults() {
DataVersion = kRoomVersion_Current;
GameID = NO_GAME_ID_IN_ROOM_FILE;
_resolution = kRoomRealRes;
MaskResolution = 1;
Width = 320;
Height = 200;
Options = RoomOptions();
Edges = RoomEdges(0, 317, 40, 199);
BgFrameCount = 1;
HotspotCount = 0;
RegionCount = 0;
WalkAreaCount = 0;
WalkBehindCount = 0;
MessageCount = 0;
for (size_t i = 0; i < (size_t)MAX_ROOM_HOTSPOTS; ++i)
Hotspots[i] = RoomHotspot();
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
Regions[i] = RoomRegion();
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
WalkAreas[i] = WalkArea();
for (size_t i = 0; i < (size_t)MAX_WALK_BEHINDS; ++i)
WalkBehinds[i] = WalkBehind();
BackgroundBPP = 1;
BgAnimSpeed = 5;
memset(Palette, 0, sizeof(Palette));
}
void RoomStruct::SetResolution(RoomResolutionType type) {
_resolution = type;
}
Bitmap *RoomStruct::GetMask(RoomAreaMask mask) const {
switch (mask) {
case kRoomAreaHotspot: return HotspotMask.get();
case kRoomAreaWalkBehind: return WalkBehindMask.get();
case kRoomAreaWalkable: return WalkAreaMask.get();
case kRoomAreaRegion: return RegionMask.get();
default: return nullptr;
}
}
float RoomStruct::GetMaskScale(RoomAreaMask mask) const {
switch (mask) {
case kRoomAreaWalkBehind: return 1.f; // walk-behinds always 1:1 with room size
case kRoomAreaHotspot:
case kRoomAreaWalkable:
case kRoomAreaRegion:
return 1.f / MaskResolution;
default:
return 0.f;
}
}
bool RoomStruct::HasRegionLightLevel(int id) const {
if (id >= 0 && id < MAX_ROOM_REGIONS)
return Regions[id].Tint == 0;
return false;
}
bool RoomStruct::HasRegionTint(int id) const {
if (id >= 0 && id < MAX_ROOM_REGIONS)
return Regions[id].Tint != 0;
return false;
}
int RoomStruct::GetRegionLightLevel(int id) const {
if (id >= 0 && id < MAX_ROOM_REGIONS)
return HasRegionLightLevel(id) ? Regions[id].Light : 0;
return 0;
}
int RoomStruct::GetRegionTintLuminance(int id) const {
if (id >= 0 && id < MAX_ROOM_REGIONS)
return HasRegionTint(id) ? (Regions[id].Light * 10) / 25 : 0;
return 0;
}
void load_room(const String &filename, RoomStruct *room, bool game_is_hires, const std::vector<SpriteInfo> &sprinfos) {
room->Free();
room->InitDefaults();
RoomDataSource src;
HRoomFileError err = OpenRoomFileFromAsset(filename, src);
if (err) {
err = ReadRoomData(room, src.InputStream.get(), src.DataVersion);
if (err)
err = UpdateRoomData(room, src.DataVersion, game_is_hires, sprinfos);
}
if (!err)
quitprintf("Unable to load the room file '%s'.\n%s.", filename.GetCStr(), err->FullMessage().GetCStr());
}
PBitmap FixBitmap(PBitmap bmp, int width, int height) {
Bitmap *new_bmp = BitmapHelper::AdjustBitmapSize(bmp.get(), width, height);
if (new_bmp != bmp.get())
return PBitmap(new_bmp);
return bmp;
}
void UpscaleRoomBackground(RoomStruct *room, bool game_is_hires) {
if (room->DataVersion >= kRoomVersion_303b || !game_is_hires)
return;
for (size_t i = 0; i < room->BgFrameCount; ++i)
room->BgFrames[i].Graphic = FixBitmap(room->BgFrames[i].Graphic, room->Width, room->Height);
FixRoomMasks(room);
}
void FixRoomMasks(RoomStruct *room) {
if (room->MaskResolution <= 0)
return;
Bitmap *bkg = room->BgFrames[0].Graphic.get();
if (bkg == nullptr)
return;
// TODO: this issue is somewhat complicated. Original code was relying on
// room->Width and Height properties. But in the engine these are saved
// already converted to data resolution which may be "low-res". Since this
// function is shared between engine and editor we do not know if we need
// to upscale them.
// For now room width/height is always equal to background bitmap.
int base_width = bkg->GetWidth();
int base_height = bkg->GetHeight();
int low_width = base_width / room->MaskResolution;
int low_height = base_height / room->MaskResolution;
// Walk-behinds are always 1:1 of the primary background.
// Other masks are 1:x where X is MaskResolution.
room->WalkBehindMask = FixBitmap(room->WalkBehindMask, base_width, base_height);
room->HotspotMask = FixBitmap(room->HotspotMask, low_width, low_height);
room->RegionMask = FixBitmap(room->RegionMask, low_width, low_height);
room->WalkAreaMask = FixBitmap(room->WalkAreaMask, low_width, low_height);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3