Initial commit
This commit is contained in:
887
engines/ags/engine/ac/game_state.cpp
Normal file
887
engines/ags/engine/ac/game_state.cpp
Normal file
@@ -0,0 +1,887 @@
|
||||
/* 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 "common/std/algorithm.h"
|
||||
#include "ags/engine/ac/draw.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/engine/ac/game_state.h"
|
||||
#include "ags/shared/ac/game_setup_struct.h"
|
||||
#include "ags/engine/ac/timer.h"
|
||||
#include "ags/engine/ac/dynobj/script_camera.h"
|
||||
#include "ags/engine/ac/dynobj/script_system.h"
|
||||
#include "ags/engine/ac/dynobj/script_viewport.h"
|
||||
#include "ags/engine/ac/dynobj/dynobj_manager.h"
|
||||
#include "ags/engine/debugging/debug_log.h"
|
||||
#include "ags/engine/device/mouse_w32.h"
|
||||
#include "ags/shared/game/custom_properties.h"
|
||||
#include "ags/shared/game/room_struct.h"
|
||||
#include "ags/engine/game/savegame_internal.h"
|
||||
#include "ags/engine/main/engine.h"
|
||||
#include "ags/engine/media/audio/audio_system.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
using namespace AGS::Engine;
|
||||
|
||||
GameState::GameState() {
|
||||
}
|
||||
|
||||
bool GameState::IsAutoRoomViewport() const {
|
||||
return _isAutoRoomViewport;
|
||||
}
|
||||
|
||||
void GameState::SetAutoRoomViewport(bool on) {
|
||||
_isAutoRoomViewport = on;
|
||||
}
|
||||
|
||||
void GameState::SetMainViewport(const Rect &viewport) {
|
||||
_mainViewport = viewport;
|
||||
_GP(mouse).UpdateGraphicArea();
|
||||
_GP(scsystem).viewport_width = game_to_data_coord(_mainViewport.GetWidth());
|
||||
_GP(scsystem).viewport_height = game_to_data_coord(_mainViewport.GetHeight());
|
||||
_mainViewportHasChanged = true;
|
||||
}
|
||||
|
||||
const Rect &GameState::GetMainViewport() const {
|
||||
return _mainViewport;
|
||||
}
|
||||
|
||||
const Rect &GameState::GetUIViewport() const {
|
||||
return _uiViewport;
|
||||
}
|
||||
|
||||
SpriteTransform GameState::GetGlobalTransform(bool full_frame_rend) const {
|
||||
// NOTE: shake_screen is not applied to the sprite batches,
|
||||
// but only as a final render factor (optimization)
|
||||
// TODO: also add global flip to the same transform, instead of passing separately?
|
||||
return SpriteTransform(_mainViewport.Left, _mainViewport.Top +
|
||||
shake_screen_yoff * static_cast<int>(full_frame_rend));
|
||||
}
|
||||
|
||||
PViewport GameState::GetRoomViewport(int index) const {
|
||||
return _roomViewports[index];
|
||||
}
|
||||
|
||||
const std::vector<PViewport> &GameState::GetRoomViewportsZOrdered() const {
|
||||
return _roomViewportsSorted;
|
||||
}
|
||||
|
||||
PViewport GameState::GetRoomViewportAt(int x, int y) const {
|
||||
// We iterate backwards, because in AGS low z-order means bottom
|
||||
for (auto it = _roomViewportsSorted.rbegin(); it != _roomViewportsSorted.rend(); ++it)
|
||||
if ((*it)->IsVisible() && (*it)->GetRect().IsInside(x, y))
|
||||
return *it;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Rect GameState::GetRoomViewportAbs(int index) const {
|
||||
return Rect::MoveBy(_roomViewports[index]->GetRect(), _mainViewport.Left, _mainViewport.Top);
|
||||
}
|
||||
|
||||
void GameState::SetUIViewport(const Rect &viewport) {
|
||||
_uiViewport = viewport;
|
||||
}
|
||||
|
||||
static bool ViewportZOrder(const PViewport e1, const PViewport e2) {
|
||||
return e1->GetZOrder() < e2->GetZOrder();
|
||||
}
|
||||
|
||||
void GameState::UpdateViewports() {
|
||||
if (_mainViewportHasChanged) {
|
||||
on_mainviewport_changed();
|
||||
_mainViewportHasChanged = false;
|
||||
}
|
||||
if (_roomViewportZOrderChanged) {
|
||||
auto old_sort = _roomViewportsSorted;
|
||||
_roomViewportsSorted = _roomViewports;
|
||||
std::sort(_roomViewportsSorted.begin(), _roomViewportsSorted.end(), ViewportZOrder);
|
||||
for (size_t i = 0; i < _roomViewportsSorted.size(); ++i) {
|
||||
if (i >= old_sort.size() || _roomViewportsSorted[i] != old_sort[i])
|
||||
_roomViewportsSorted[i]->SetChangedVisible();
|
||||
}
|
||||
_roomViewportZOrderChanged = false;
|
||||
}
|
||||
size_t vp_changed = SIZE_MAX;
|
||||
for (size_t i = _roomViewportsSorted.size(); i-- > 0;) {
|
||||
auto vp = _roomViewportsSorted[i];
|
||||
if (vp->HasChangedSize() || vp->HasChangedPosition() || vp->HasChangedVisible()) {
|
||||
vp_changed = i;
|
||||
on_roomviewport_changed(vp.get());
|
||||
vp->ClearChangedFlags();
|
||||
}
|
||||
}
|
||||
if (vp_changed != SIZE_MAX)
|
||||
detect_roomviewport_overlaps(vp_changed);
|
||||
for (auto cam : _roomCameras) {
|
||||
if (cam->HasChangedSize() || cam->HasChangedPosition()) {
|
||||
on_roomcamera_changed(cam.get());
|
||||
cam->ClearChangedFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::InvalidateViewportZOrder() {
|
||||
_roomViewportZOrderChanged = true;
|
||||
}
|
||||
|
||||
PCamera GameState::GetRoomCamera(int index) const {
|
||||
return _roomCameras[index];
|
||||
}
|
||||
|
||||
void GameState::UpdateRoomCameras() {
|
||||
for (size_t i = 0; i < _roomCameras.size(); ++i)
|
||||
UpdateRoomCamera(i);
|
||||
}
|
||||
|
||||
void GameState::UpdateRoomCamera(int index) {
|
||||
auto cam = _roomCameras[index];
|
||||
const Rect &rc = cam->GetRect();
|
||||
const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
|
||||
if ((real_room_sz.Width > rc.GetWidth()) || (real_room_sz.Height > rc.GetHeight())) {
|
||||
// TODO: split out into Camera Behavior
|
||||
if (!cam->IsLocked()) {
|
||||
int x = data_to_game_coord(_G(playerchar)->x) - rc.GetWidth() / 2;
|
||||
int y = data_to_game_coord(_G(playerchar)->y) - rc.GetHeight() / 2;
|
||||
cam->SetAt(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Point GameState::RoomToScreen(int roomx, int roomy) {
|
||||
return _roomViewports[0]->RoomToScreen(roomx, roomy, false).first;
|
||||
}
|
||||
|
||||
int GameState::RoomToScreenX(int roomx) {
|
||||
return _roomViewports[0]->RoomToScreen(roomx, 0, false).first.X;
|
||||
}
|
||||
|
||||
int GameState::RoomToScreenY(int roomy) {
|
||||
return _roomViewports[0]->RoomToScreen(0, roomy, false).first.Y;
|
||||
}
|
||||
|
||||
VpPoint GameState::ScreenToRoomImpl(int scrx, int scry, int view_index, bool clip_viewport, bool convert_cam_to_data) {
|
||||
PViewport view;
|
||||
if (view_index < 0) {
|
||||
view = GetRoomViewportAt(scrx, scry);
|
||||
if (!view) {
|
||||
if (clip_viewport)
|
||||
return std::make_pair(Point(), -1);
|
||||
view = _roomViewports[0]; // use primary viewport
|
||||
}
|
||||
} else {
|
||||
view = _roomViewports[view_index];
|
||||
}
|
||||
return view->ScreenToRoom(scrx, scry, clip_viewport, convert_cam_to_data);
|
||||
}
|
||||
|
||||
VpPoint GameState::ScreenToRoom(int scrx, int scry, bool restrict) {
|
||||
if (_GP(game).options[OPT_BASESCRIPTAPI] >= kScriptAPI_v3507)
|
||||
return ScreenToRoomImpl(scrx, scry, -1, restrict, false);
|
||||
return ScreenToRoomImpl(scrx, scry, 0, false, false);
|
||||
}
|
||||
|
||||
VpPoint GameState::ScreenToRoomDivDown(int scrx, int scry) {
|
||||
if (_GP(game).options[OPT_BASESCRIPTAPI] >= kScriptAPI_v3507)
|
||||
return ScreenToRoomImpl(scrx, scry, -1, true, true);
|
||||
return ScreenToRoomImpl(scrx, scry, 0, false, true);
|
||||
}
|
||||
|
||||
void GameState::CreatePrimaryViewportAndCamera() {
|
||||
if (_roomViewports.size() == 0) {
|
||||
_GP(play).CreateRoomViewport();
|
||||
_GP(play).RegisterRoomViewport(0);
|
||||
}
|
||||
if (_roomCameras.size() == 0) {
|
||||
_GP(play).CreateRoomCamera();
|
||||
_GP(play).RegisterRoomCamera(0);
|
||||
}
|
||||
_roomViewports[0]->LinkCamera(_roomCameras[0]);
|
||||
_roomCameras[0]->LinkToViewport(_roomViewports[0]);
|
||||
}
|
||||
|
||||
PViewport GameState::CreateRoomViewport() {
|
||||
int index = (int)_roomViewports.size();
|
||||
PViewport viewport(new Viewport());
|
||||
viewport->SetID(index);
|
||||
viewport->SetRect(_mainViewport);
|
||||
_roomViewports.push_back(viewport);
|
||||
_scViewportHandles.push_back(0);
|
||||
_roomViewportsSorted.push_back(viewport);
|
||||
_roomViewportZOrderChanged = true;
|
||||
on_roomviewport_created(index);
|
||||
return viewport;
|
||||
}
|
||||
|
||||
ScriptViewport *GameState::RegisterRoomViewport(int index, int32_t handle) {
|
||||
if (index < 0 || (size_t)index >= _roomViewports.size())
|
||||
return nullptr;
|
||||
auto scview = new ScriptViewport(index);
|
||||
if (handle == 0) {
|
||||
handle = ccRegisterManagedObject(scview, scview);
|
||||
ccAddObjectReference(handle); // one reference for the GameState
|
||||
} else {
|
||||
ccRegisterUnserializedObject(handle, scview, scview);
|
||||
}
|
||||
_scViewportHandles[index] = handle; // save handle for us
|
||||
return scview;
|
||||
}
|
||||
|
||||
void GameState::DeleteRoomViewport(int index) {
|
||||
if (index < 0 || (size_t)index >= _roomViewports.size())
|
||||
return;
|
||||
auto handle = _scViewportHandles[index];
|
||||
auto scobj = const_cast<ScriptViewport*>((const ScriptViewport*)ccGetObjectAddressFromHandle(handle));
|
||||
if (scobj) {
|
||||
scobj->Invalidate();
|
||||
ccReleaseObjectReference(handle);
|
||||
}
|
||||
auto cam = _roomViewports[index]->GetCamera();
|
||||
if (cam)
|
||||
cam->UnlinkFromViewport(index);
|
||||
_roomViewports.erase(_roomViewports.begin() + index);
|
||||
_scViewportHandles.erase(_scViewportHandles.begin() + index);
|
||||
for (size_t i = index; i < _roomViewports.size(); ++i) {
|
||||
_roomViewports[i]->SetID(i);
|
||||
handle = _scViewportHandles[index];
|
||||
scobj = const_cast<ScriptViewport*>((const ScriptViewport*)ccGetObjectAddressFromHandle(handle));
|
||||
if (scobj)
|
||||
scobj->SetID(i);
|
||||
}
|
||||
for (size_t i = 0; i < _roomViewportsSorted.size(); ++i) {
|
||||
if (_roomViewportsSorted[i]->GetID() == index) {
|
||||
_roomViewportsSorted.erase(_roomViewportsSorted.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
on_roomviewport_deleted(index);
|
||||
}
|
||||
|
||||
int GameState::GetRoomViewportCount() const {
|
||||
return (int)_roomViewports.size();
|
||||
}
|
||||
|
||||
PCamera GameState::CreateRoomCamera() {
|
||||
int index = (int)_roomCameras.size();
|
||||
PCamera camera(new Camera());
|
||||
camera->SetID(index);
|
||||
camera->SetAt(0, 0);
|
||||
camera->SetSize(_mainViewport.GetSize());
|
||||
_scCameraHandles.push_back(0);
|
||||
_roomCameras.push_back(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
ScriptCamera *GameState::RegisterRoomCamera(int index, int32_t handle) {
|
||||
if (index < 0 || (size_t)index >= _roomCameras.size())
|
||||
return nullptr;
|
||||
auto sccamera = new ScriptCamera(index);
|
||||
if (handle == 0) {
|
||||
handle = ccRegisterManagedObject(sccamera, sccamera);
|
||||
ccAddObjectReference(handle); // one reference for the GameState
|
||||
} else {
|
||||
ccRegisterUnserializedObject(handle, sccamera, sccamera);
|
||||
}
|
||||
_scCameraHandles[index] = handle;
|
||||
return sccamera;
|
||||
}
|
||||
|
||||
void GameState::DeleteRoomCamera(int index) {
|
||||
if (index < 0 || (size_t)index >= _roomCameras.size())
|
||||
return;
|
||||
auto handle = _scCameraHandles[index];
|
||||
auto scobj = const_cast<ScriptCamera*>((const ScriptCamera*)ccGetObjectAddressFromHandle(handle));
|
||||
if (scobj) {
|
||||
scobj->Invalidate();
|
||||
ccReleaseObjectReference(handle);
|
||||
}
|
||||
for (auto &viewref : _roomCameras[index]->GetLinkedViewports()) {
|
||||
auto view = viewref.lock();
|
||||
if (view)
|
||||
view->LinkCamera(nullptr);
|
||||
}
|
||||
_roomCameras.erase(_roomCameras.begin() + index);
|
||||
_scCameraHandles.erase(_scCameraHandles.begin() + index);
|
||||
for (size_t i = index; i < _roomCameras.size(); ++i) {
|
||||
_roomCameras[i]->SetID(i);
|
||||
handle = _scCameraHandles[index];
|
||||
scobj = const_cast<ScriptCamera*>((const ScriptCamera*)ccGetObjectAddressFromHandle(handle));
|
||||
if (scobj)
|
||||
scobj->SetID(i);
|
||||
}
|
||||
}
|
||||
|
||||
int GameState::GetRoomCameraCount() const {
|
||||
return (int)_roomCameras.size();
|
||||
}
|
||||
|
||||
ScriptViewport *GameState::GetScriptViewport(int index) {
|
||||
if (index < 0 || (size_t)index >= _roomViewports.size())
|
||||
return nullptr;
|
||||
return const_cast<ScriptViewport*>((const ScriptViewport*)ccGetObjectAddressFromHandle(_scViewportHandles[index]));
|
||||
}
|
||||
|
||||
ScriptCamera *GameState::GetScriptCamera(int index) {
|
||||
if (index < 0 || (size_t)index >= _roomCameras.size())
|
||||
return nullptr;
|
||||
return const_cast<ScriptCamera*>((const ScriptCamera*)ccGetObjectAddressFromHandle(_scCameraHandles[index]));
|
||||
}
|
||||
|
||||
bool GameState::IsIgnoringInput() const {
|
||||
return AGS_Clock::now() < _ignoreUserInputUntilTime;
|
||||
}
|
||||
|
||||
void GameState::SetIgnoreInput(int timeout_ms) {
|
||||
if (AGS_Clock::now() + std::chrono::milliseconds(timeout_ms) > _ignoreUserInputUntilTime)
|
||||
_ignoreUserInputUntilTime = AGS_Clock::now() + std::chrono::milliseconds(timeout_ms);
|
||||
}
|
||||
|
||||
void GameState::ClearIgnoreInput() {
|
||||
_ignoreUserInputUntilTime = AGS_Clock::now();
|
||||
}
|
||||
|
||||
void GameState::SetWaitSkipResult(int how, int data) {
|
||||
wait_counter = 0;
|
||||
wait_skipped_by = how;
|
||||
wait_skipped_by_data = data;
|
||||
}
|
||||
|
||||
int GameState::GetWaitSkipResult() const {
|
||||
// NOTE: we remove timer flag to make timeout reason = 0
|
||||
return ((wait_skipped_by & ~SKIP_AUTOTIMER) << SKIP_RESULT_TYPE_SHIFT)
|
||||
| (wait_skipped_by_data & SKIP_RESULT_DATA_MASK);
|
||||
}
|
||||
|
||||
bool GameState::IsBlockingVoiceSpeech() const {
|
||||
return speech_has_voice && speech_voice_blocking;
|
||||
}
|
||||
|
||||
bool GameState::IsNonBlockingVoiceSpeech() const {
|
||||
return speech_has_voice && !speech_voice_blocking;
|
||||
}
|
||||
|
||||
bool GameState::ShouldPlayVoiceSpeech() const {
|
||||
return !_GP(play).fast_forward &&
|
||||
(_GP(play).speech_mode != kSpeech_TextOnly) && (_GP(play).voice_avail);
|
||||
}
|
||||
|
||||
void GameState::ReadFromSavegame(Stream *in, GameDataVersion data_ver, GameStateSvgVersion svg_ver, RestoredData &r_data) {
|
||||
const bool old_save = svg_ver < kGSSvgVersion_Initial;
|
||||
const bool extended_old_save = old_save && (data_ver >= kGameVersion_340_4);
|
||||
const bool do_align_pad = old_save;
|
||||
|
||||
score = in->ReadInt32();
|
||||
usedmode = in->ReadInt32();
|
||||
disabled_user_interface = in->ReadInt32();
|
||||
gscript_timer = in->ReadInt32();
|
||||
debug_mode = in->ReadInt32();
|
||||
in->ReadArrayOfInt32(globalvars, MAXGLOBALVARS);
|
||||
messagetime = in->ReadInt32();
|
||||
usedinv = in->ReadInt32();
|
||||
inv_top = in->ReadInt32();
|
||||
inv_numdisp = in->ReadInt32();
|
||||
inv_numorder = in->ReadInt32();
|
||||
inv_numinline = in->ReadInt32();
|
||||
text_speed = in->ReadInt32();
|
||||
sierra_inv_color = in->ReadInt32();
|
||||
talkanim_speed = in->ReadInt32();
|
||||
inv_item_wid = in->ReadInt32();
|
||||
inv_item_hit = in->ReadInt32();
|
||||
speech_text_shadow = in->ReadInt32();
|
||||
swap_portrait_side = in->ReadInt32();
|
||||
speech_textwindow_gui = in->ReadInt32();
|
||||
follow_change_room_timer = in->ReadInt32();
|
||||
totalscore = in->ReadInt32();
|
||||
skip_display = in->ReadInt32();
|
||||
no_multiloop_repeat = in->ReadInt32();
|
||||
roomscript_finished = in->ReadInt32();
|
||||
used_inv_on = in->ReadInt32();
|
||||
no_textbg_when_voice = in->ReadInt32();
|
||||
max_dialogoption_width = in->ReadInt32();
|
||||
no_hicolor_fadein = in->ReadInt32();
|
||||
bgspeech_game_speed = in->ReadInt32();
|
||||
bgspeech_stay_on_display = in->ReadInt32();
|
||||
unfactor_speech_from_textlength = in->ReadInt32();
|
||||
mp3_loop_before_end = in->ReadInt32();
|
||||
speech_music_drop = in->ReadInt32();
|
||||
in_cutscene = in->ReadInt32();
|
||||
fast_forward = in->ReadInt32();
|
||||
room_width = in->ReadInt32();
|
||||
room_height = in->ReadInt32();
|
||||
game_speed_modifier = in->ReadInt32();
|
||||
score_sound = in->ReadInt32();
|
||||
takeover_data = in->ReadInt32();
|
||||
replay_hotkey_unused = in->ReadInt32();
|
||||
dialog_options_x = in->ReadInt32();
|
||||
dialog_options_y = in->ReadInt32();
|
||||
narrator_speech = in->ReadInt32();
|
||||
ambient_sounds_persist = in->ReadInt32();
|
||||
lipsync_speed = in->ReadInt32();
|
||||
close_mouth_speech_time = in->ReadInt32();
|
||||
disable_antialiasing = in->ReadInt32();
|
||||
text_speed_modifier = in->ReadInt32();
|
||||
if (svg_ver < kGSSvgVersion_350)
|
||||
text_align = ConvertLegacyScriptAlignment((LegacyScriptAlignment)in->ReadInt32());
|
||||
else
|
||||
text_align = (HorAlignment)in->ReadInt32();
|
||||
speech_bubble_width = in->ReadInt32();
|
||||
min_dialogoption_width = in->ReadInt32();
|
||||
disable_dialog_parser = in->ReadInt32();
|
||||
anim_background_speed = in->ReadInt32(); // the setting for this room
|
||||
top_bar_backcolor = in->ReadInt32();
|
||||
top_bar_textcolor = in->ReadInt32();
|
||||
top_bar_bordercolor = in->ReadInt32();
|
||||
top_bar_borderwidth = in->ReadInt32();
|
||||
top_bar_ypos = in->ReadInt32();
|
||||
screenshot_width = in->ReadInt32();
|
||||
screenshot_height = in->ReadInt32();
|
||||
top_bar_font = in->ReadInt32();
|
||||
if (svg_ver < kGSSvgVersion_350)
|
||||
speech_text_align = ConvertLegacyScriptAlignment((LegacyScriptAlignment)in->ReadInt32());
|
||||
else
|
||||
speech_text_align = (HorAlignment)in->ReadInt32();
|
||||
auto_use_walkto_points = in->ReadInt32();
|
||||
inventory_greys_out = in->ReadInt32();
|
||||
skip_speech_specific_key = in->ReadInt32();
|
||||
abort_key = in->ReadInt32();
|
||||
fade_to_red = in->ReadInt32();
|
||||
fade_to_green = in->ReadInt32();
|
||||
fade_to_blue = in->ReadInt32();
|
||||
show_single_dialog_option = in->ReadInt32();
|
||||
keep_screen_during_instant_transition = in->ReadInt32();
|
||||
read_dialog_option_colour = in->ReadInt32();
|
||||
stop_dialog_at_end = in->ReadInt32();
|
||||
speech_portrait_placement = in->ReadInt32();
|
||||
speech_portrait_x = in->ReadInt32();
|
||||
speech_portrait_y = in->ReadInt32();
|
||||
speech_display_post_time_ms = in->ReadInt32();
|
||||
dialog_options_highlight_color = in->ReadInt32();
|
||||
if (old_save)
|
||||
in->ReadArrayOfInt32(reserved, GAME_STATE_RESERVED_INTS);
|
||||
// ** up to here is referenced in the script "game." object
|
||||
if (old_save) {
|
||||
in->ReadInt32(); // recording
|
||||
in->ReadInt32(); // playback
|
||||
in->ReadInt16(); // gamestep
|
||||
in->ReadInt16(); // alignment padding to int32
|
||||
}
|
||||
randseed = in->ReadInt32(); // random seed
|
||||
player_on_region = in->ReadInt32(); // player's current region
|
||||
if (old_save)
|
||||
in->ReadInt32(); // screen_is_faded_out
|
||||
check_interaction_only = in->ReadInt32();
|
||||
bg_frame = in->ReadInt32();
|
||||
bg_anim_delay = in->ReadInt32(); // for animating backgrounds
|
||||
music_vol_was = in->ReadInt32(); // before the volume drop
|
||||
wait_counter = in->ReadInt16();
|
||||
mboundx1 = in->ReadInt16();
|
||||
mboundx2 = in->ReadInt16();
|
||||
mboundy1 = in->ReadInt16();
|
||||
mboundy2 = in->ReadInt16();
|
||||
if (do_align_pad)
|
||||
in->ReadInt16(); // alignment padding to int32
|
||||
fade_effect = in->ReadInt32();
|
||||
bg_frame_locked = in->ReadInt32();
|
||||
in->ReadArrayOfInt32(globalscriptvars, MAXGSVALUES);
|
||||
cur_music_number = in->ReadInt32();
|
||||
music_repeat = in->ReadInt32();
|
||||
music_master_volume = in->ReadInt32();
|
||||
digital_master_volume = in->ReadInt32();
|
||||
in->Read(walkable_areas_on, MAX_WALK_AREAS);
|
||||
screen_flipped = in->ReadInt16();
|
||||
if (svg_ver < kGSSvgVersion_350_10) {
|
||||
short offsets_locked = in->ReadInt16();
|
||||
if (offsets_locked != 0)
|
||||
r_data.Camera0_Flags = kSvgCamPosLocked;
|
||||
}
|
||||
entered_at_x = in->ReadInt32();
|
||||
entered_at_y = in->ReadInt32();
|
||||
entered_edge = in->ReadInt32();
|
||||
speech_mode = (SpeechMode)in->ReadInt32();
|
||||
speech_skip_style = in->ReadInt32();
|
||||
in->ReadArrayOfInt32(script_timers, MAX_TIMERS);
|
||||
sound_volume = in->ReadInt32();
|
||||
speech_volume = in->ReadInt32();
|
||||
normal_font = in->ReadInt32();
|
||||
speech_font = in->ReadInt32();
|
||||
key_skip_wait = in->ReadInt8();
|
||||
if (do_align_pad)
|
||||
in->Seek(3); // alignment padding to int32
|
||||
swap_portrait_lastchar = in->ReadInt32();
|
||||
separate_music_lib = in->ReadInt32() != 0;
|
||||
in_conversation = in->ReadInt32();
|
||||
screen_tint = in->ReadInt32();
|
||||
num_parsed_words = in->ReadInt32();
|
||||
in->ReadArrayOfInt16(parsed_words, MAX_PARSED_WORDS);
|
||||
in->Read(bad_parsed_word, 100);
|
||||
if (do_align_pad)
|
||||
in->ReadInt16(); // alignment padding to int32 (15 int16 + 100 int8 = 65 int16 -> 66)
|
||||
raw_color = in->ReadInt32();
|
||||
if (old_save)
|
||||
in->ReadArrayOfInt32(raw_modified, MAX_ROOM_BGFRAMES);
|
||||
in->ReadArrayOfInt16(filenumbers, MAXSAVEGAMES);
|
||||
if (old_save)
|
||||
in->ReadInt32(); // room_changes
|
||||
mouse_cursor_hidden = in->ReadInt32();
|
||||
silent_midi = in->ReadInt32();
|
||||
silent_midi_channel = in->ReadInt32();
|
||||
current_music_repeating = in->ReadInt32();
|
||||
shakesc_delay = in->ReadInt32();
|
||||
shakesc_amount = in->ReadInt32();
|
||||
shakesc_length = in->ReadInt32();
|
||||
rtint_red = in->ReadInt32();
|
||||
rtint_green = in->ReadInt32();
|
||||
rtint_blue = in->ReadInt32();
|
||||
rtint_level = in->ReadInt32();
|
||||
rtint_light = in->ReadInt32();
|
||||
if (!old_save || extended_old_save) {
|
||||
rtint_enabled = in->ReadBool();
|
||||
if (do_align_pad)
|
||||
in->Seek(3); // alignment padding to int32
|
||||
} else {
|
||||
rtint_enabled = rtint_level > 0;
|
||||
}
|
||||
end_cutscene_music = in->ReadInt32();
|
||||
skip_until_char_stops = in->ReadInt32();
|
||||
get_loc_name_last_time = in->ReadInt32();
|
||||
get_loc_name_save_cursor = in->ReadInt32();
|
||||
restore_cursor_mode_to = in->ReadInt32();
|
||||
restore_cursor_image_to = in->ReadInt32();
|
||||
music_queue_size = in->ReadInt16();
|
||||
in->ReadArrayOfInt16(music_queue, MAX_QUEUED_MUSIC);
|
||||
new_music_queue_size = in->ReadInt16();
|
||||
if (!old_save) {
|
||||
for (int i = 0; i < MAX_QUEUED_MUSIC; ++i) {
|
||||
new_music_queue[i].ReadFromSavegame(in);
|
||||
}
|
||||
}
|
||||
|
||||
crossfading_out_channel = in->ReadInt16();
|
||||
crossfade_step = in->ReadInt16();
|
||||
crossfade_out_volume_per_step = in->ReadInt16();
|
||||
crossfade_initial_volume_out = in->ReadInt16();
|
||||
crossfading_in_channel = in->ReadInt16();
|
||||
crossfade_in_volume_per_step = in->ReadInt16();
|
||||
crossfade_final_volume_in = in->ReadInt16();
|
||||
|
||||
if (old_save) {
|
||||
in->ReadInt16(); // alignment padding to int32 (before array of structs)
|
||||
for (int i = 0; i < MAX_QUEUED_MUSIC; ++i) {
|
||||
new_music_queue[i].ReadFromSavegame_v321(in);
|
||||
}
|
||||
}
|
||||
|
||||
in->Read(takeover_from, 50);
|
||||
playmp3file_name.ReadCount(in, PLAYMP3FILE_MAX_FILENAME_LEN);
|
||||
in->Read(globalstrings, MAXGLOBALSTRINGS * MAX_MAXSTRLEN);
|
||||
in->Read(lastParserEntry, MAX_MAXSTRLEN);
|
||||
if (svg_ver < kGSSvgVersion_361_14)
|
||||
game_name.ReadCount(in, LEGACY_GAMESTATE_GAMENAMELENGTH);
|
||||
else
|
||||
game_name = StrUtil::ReadString(in);
|
||||
ground_level_areas_disabled = in->ReadInt32();
|
||||
next_screen_transition = in->ReadInt32();
|
||||
in->ReadInt32(); // gamma_adjustment -- do not apply gamma level from savegame
|
||||
temporarily_turned_off_character = in->ReadInt16();
|
||||
inv_backwards_compatibility = in->ReadInt16();
|
||||
if (old_save) {
|
||||
in->ReadInt32(); // gui_draw_order
|
||||
in->ReadInt32(); // do_once_tokens;
|
||||
}
|
||||
r_data.DoOnceCount = static_cast<uint32_t>(in->ReadInt32());
|
||||
if (!old_save) {
|
||||
for (size_t i = 0; i < r_data.DoOnceCount; ++i) {
|
||||
do_once_tokens.insert(StrUtil::ReadString(in));
|
||||
}
|
||||
}
|
||||
text_min_display_time_ms = in->ReadInt32();
|
||||
ignore_user_input_after_text_timeout_ms = in->ReadInt32();
|
||||
if (svg_ver < kGSSvgVersion_350_9)
|
||||
in->ReadInt32(); // ignore_user_input_until_time -- do not apply from savegame
|
||||
if (old_save)
|
||||
in->ReadArrayOfInt32(default_audio_type_volumes, MAX_AUDIO_TYPES);
|
||||
if (svg_ver >= kGSSvgVersion_350_9) {
|
||||
int voice_speech_flags = in->ReadInt32();
|
||||
speech_has_voice = voice_speech_flags != 0;
|
||||
speech_voice_blocking = (voice_speech_flags & 0x02) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::WriteForSavegame(Stream *out) const {
|
||||
// NOTE: following parameters are never saved:
|
||||
// recording, playback, gamestep, screen_is_faded_out, room_changes
|
||||
out->WriteInt32(score);
|
||||
out->WriteInt32(usedmode);
|
||||
out->WriteInt32(disabled_user_interface);
|
||||
out->WriteInt32(gscript_timer);
|
||||
out->WriteInt32(debug_mode);
|
||||
out->WriteArrayOfInt32(globalvars, MAXGLOBALVARS);
|
||||
out->WriteInt32(messagetime);
|
||||
out->WriteInt32(usedinv);
|
||||
out->WriteInt32(inv_top);
|
||||
out->WriteInt32(inv_numdisp);
|
||||
out->WriteInt32(inv_numorder);
|
||||
out->WriteInt32(inv_numinline);
|
||||
out->WriteInt32(text_speed);
|
||||
out->WriteInt32(sierra_inv_color);
|
||||
out->WriteInt32(talkanim_speed);
|
||||
out->WriteInt32(inv_item_wid);
|
||||
out->WriteInt32(inv_item_hit);
|
||||
out->WriteInt32(speech_text_shadow);
|
||||
out->WriteInt32(swap_portrait_side);
|
||||
out->WriteInt32(speech_textwindow_gui);
|
||||
out->WriteInt32(follow_change_room_timer);
|
||||
out->WriteInt32(totalscore);
|
||||
out->WriteInt32(skip_display);
|
||||
out->WriteInt32(no_multiloop_repeat);
|
||||
out->WriteInt32(roomscript_finished);
|
||||
out->WriteInt32(used_inv_on);
|
||||
out->WriteInt32(no_textbg_when_voice);
|
||||
out->WriteInt32(max_dialogoption_width);
|
||||
out->WriteInt32(no_hicolor_fadein);
|
||||
out->WriteInt32(bgspeech_game_speed);
|
||||
out->WriteInt32(bgspeech_stay_on_display);
|
||||
out->WriteInt32(unfactor_speech_from_textlength);
|
||||
out->WriteInt32(mp3_loop_before_end);
|
||||
out->WriteInt32(speech_music_drop);
|
||||
out->WriteInt32(in_cutscene);
|
||||
out->WriteInt32(fast_forward);
|
||||
out->WriteInt32(room_width);
|
||||
out->WriteInt32(room_height);
|
||||
out->WriteInt32(game_speed_modifier);
|
||||
out->WriteInt32(score_sound);
|
||||
out->WriteInt32(takeover_data);
|
||||
out->WriteInt32(replay_hotkey_unused); // StartRecording: not supported
|
||||
out->WriteInt32(dialog_options_x);
|
||||
out->WriteInt32(dialog_options_y);
|
||||
out->WriteInt32(narrator_speech);
|
||||
out->WriteInt32(ambient_sounds_persist);
|
||||
out->WriteInt32(lipsync_speed);
|
||||
out->WriteInt32(close_mouth_speech_time);
|
||||
out->WriteInt32(disable_antialiasing);
|
||||
out->WriteInt32(text_speed_modifier);
|
||||
out->WriteInt32(text_align);
|
||||
out->WriteInt32(speech_bubble_width);
|
||||
out->WriteInt32(min_dialogoption_width);
|
||||
out->WriteInt32(disable_dialog_parser);
|
||||
out->WriteInt32(anim_background_speed); // the setting for this room
|
||||
out->WriteInt32(top_bar_backcolor);
|
||||
out->WriteInt32(top_bar_textcolor);
|
||||
out->WriteInt32(top_bar_bordercolor);
|
||||
out->WriteInt32(top_bar_borderwidth);
|
||||
out->WriteInt32(top_bar_ypos);
|
||||
out->WriteInt32(screenshot_width);
|
||||
out->WriteInt32(screenshot_height);
|
||||
out->WriteInt32(top_bar_font);
|
||||
out->WriteInt32(speech_text_align);
|
||||
out->WriteInt32(auto_use_walkto_points);
|
||||
out->WriteInt32(inventory_greys_out);
|
||||
out->WriteInt32(skip_speech_specific_key);
|
||||
out->WriteInt32(abort_key);
|
||||
out->WriteInt32(fade_to_red);
|
||||
out->WriteInt32(fade_to_green);
|
||||
out->WriteInt32(fade_to_blue);
|
||||
out->WriteInt32(show_single_dialog_option);
|
||||
out->WriteInt32(keep_screen_during_instant_transition);
|
||||
out->WriteInt32(read_dialog_option_colour);
|
||||
out->WriteInt32(stop_dialog_at_end);
|
||||
out->WriteInt32(speech_portrait_placement);
|
||||
out->WriteInt32(speech_portrait_x);
|
||||
out->WriteInt32(speech_portrait_y);
|
||||
out->WriteInt32(speech_display_post_time_ms);
|
||||
out->WriteInt32(dialog_options_highlight_color);
|
||||
// ** up to here is referenced in the script "game." object
|
||||
out->WriteInt32(randseed); // random seed
|
||||
out->WriteInt32(player_on_region); // player's current region
|
||||
out->WriteInt32(check_interaction_only);
|
||||
out->WriteInt32(bg_frame);
|
||||
out->WriteInt32(bg_anim_delay); // for animating backgrounds
|
||||
out->WriteInt32(music_vol_was); // before the volume drop
|
||||
out->WriteInt16(wait_counter);
|
||||
out->WriteInt16(mboundx1);
|
||||
out->WriteInt16(mboundx2);
|
||||
out->WriteInt16(mboundy1);
|
||||
out->WriteInt16(mboundy2);
|
||||
out->WriteInt32(fade_effect);
|
||||
out->WriteInt32(bg_frame_locked);
|
||||
out->WriteArrayOfInt32(globalscriptvars, MAXGSVALUES);
|
||||
out->WriteInt32(cur_music_number);
|
||||
out->WriteInt32(music_repeat);
|
||||
out->WriteInt32(music_master_volume);
|
||||
out->WriteInt32(digital_master_volume);
|
||||
out->Write(walkable_areas_on, MAX_WALK_AREAS);
|
||||
out->WriteInt16(screen_flipped);
|
||||
out->WriteInt32(entered_at_x);
|
||||
out->WriteInt32(entered_at_y);
|
||||
out->WriteInt32(entered_edge);
|
||||
out->WriteInt32(speech_mode);
|
||||
out->WriteInt32(speech_skip_style);
|
||||
out->WriteArrayOfInt32(script_timers, MAX_TIMERS);
|
||||
out->WriteInt32(sound_volume);
|
||||
out->WriteInt32(speech_volume);
|
||||
out->WriteInt32(normal_font);
|
||||
out->WriteInt32(speech_font);
|
||||
out->WriteInt8(key_skip_wait);
|
||||
out->WriteInt32(swap_portrait_lastchar);
|
||||
out->WriteInt32(separate_music_lib ? 1 : 0);
|
||||
out->WriteInt32(in_conversation);
|
||||
out->WriteInt32(screen_tint);
|
||||
out->WriteInt32(num_parsed_words);
|
||||
out->WriteArrayOfInt16(parsed_words, MAX_PARSED_WORDS);
|
||||
out->Write(bad_parsed_word, 100);
|
||||
out->WriteInt32(raw_color);
|
||||
out->WriteArrayOfInt16(filenumbers, MAXSAVEGAMES);
|
||||
out->WriteInt32(mouse_cursor_hidden);
|
||||
out->WriteInt32(silent_midi);
|
||||
out->WriteInt32(silent_midi_channel);
|
||||
out->WriteInt32(current_music_repeating);
|
||||
out->WriteInt32(shakesc_delay);
|
||||
out->WriteInt32(shakesc_amount);
|
||||
out->WriteInt32(shakesc_length);
|
||||
out->WriteInt32(rtint_red);
|
||||
out->WriteInt32(rtint_green);
|
||||
out->WriteInt32(rtint_blue);
|
||||
out->WriteInt32(rtint_level);
|
||||
out->WriteInt32(rtint_light);
|
||||
out->WriteBool(rtint_enabled);
|
||||
out->WriteInt32(end_cutscene_music);
|
||||
out->WriteInt32(skip_until_char_stops);
|
||||
out->WriteInt32(get_loc_name_last_time);
|
||||
out->WriteInt32(get_loc_name_save_cursor);
|
||||
out->WriteInt32(restore_cursor_mode_to);
|
||||
out->WriteInt32(restore_cursor_image_to);
|
||||
out->WriteInt16(music_queue_size);
|
||||
out->WriteArrayOfInt16(music_queue, MAX_QUEUED_MUSIC);
|
||||
out->WriteInt16(new_music_queue_size);
|
||||
for (int i = 0; i < MAX_QUEUED_MUSIC; ++i) {
|
||||
new_music_queue[i].WriteToSavegame(out);
|
||||
}
|
||||
|
||||
out->WriteInt16(crossfading_out_channel);
|
||||
out->WriteInt16(crossfade_step);
|
||||
out->WriteInt16(crossfade_out_volume_per_step);
|
||||
out->WriteInt16(crossfade_initial_volume_out);
|
||||
out->WriteInt16(crossfading_in_channel);
|
||||
out->WriteInt16(crossfade_in_volume_per_step);
|
||||
out->WriteInt16(crossfade_final_volume_in);
|
||||
|
||||
out->Write(takeover_from, 50);
|
||||
playmp3file_name.WriteCount(out, PLAYMP3FILE_MAX_FILENAME_LEN);
|
||||
out->Write(globalstrings, MAXGLOBALSTRINGS * MAX_MAXSTRLEN);
|
||||
out->Write(lastParserEntry, MAX_MAXSTRLEN);
|
||||
StrUtil::WriteString(game_name, out);
|
||||
out->WriteInt32(ground_level_areas_disabled);
|
||||
out->WriteInt32(next_screen_transition);
|
||||
out->WriteInt32(gamma_adjustment);
|
||||
out->WriteInt16(temporarily_turned_off_character);
|
||||
out->WriteInt16(inv_backwards_compatibility);
|
||||
out->WriteInt32(static_cast<uint32_t>(do_once_tokens.size()));
|
||||
for (const auto &token : do_once_tokens) {
|
||||
StrUtil::WriteString(token, out);
|
||||
}
|
||||
out->WriteInt32(text_min_display_time_ms);
|
||||
out->WriteInt32(ignore_user_input_after_text_timeout_ms);
|
||||
|
||||
int voice_speech_flags = speech_has_voice ? 0x01 : 0;
|
||||
if (speech_voice_blocking)
|
||||
voice_speech_flags |= 0x02;
|
||||
out->WriteInt32(voice_speech_flags);
|
||||
}
|
||||
|
||||
void GameState::FreeProperties() {
|
||||
for (auto &p : charProps)
|
||||
p.clear();
|
||||
for (auto &p : invProps)
|
||||
p.clear();
|
||||
}
|
||||
|
||||
void GameState::FreeViewportsAndCameras() {
|
||||
_roomViewports.clear();
|
||||
_roomViewportsSorted.clear();
|
||||
for (auto handle : _scViewportHandles) {
|
||||
auto scview = const_cast<ScriptViewport*>((const ScriptViewport*)ccGetObjectAddressFromHandle(handle));
|
||||
if (scview) {
|
||||
scview->Invalidate();
|
||||
ccReleaseObjectReference(handle);
|
||||
}
|
||||
}
|
||||
_scViewportHandles.clear();
|
||||
_roomCameras.clear();
|
||||
for (auto handle : _scCameraHandles) {
|
||||
auto sccam = const_cast<ScriptCamera*>((const ScriptCamera*)ccGetObjectAddressFromHandle(handle));
|
||||
if (sccam) {
|
||||
sccam->Invalidate();
|
||||
ccReleaseObjectReference(handle);
|
||||
}
|
||||
}
|
||||
_scCameraHandles.clear();
|
||||
}
|
||||
|
||||
void GameState::ReadCustomProperties_v340(Stream *in, GameDataVersion data_ver) {
|
||||
if (data_ver >= kGameVersion_340_4) {
|
||||
// After runtime property values were read we also copy missing default,
|
||||
// because we do not keep defaults in the saved game, and also in case
|
||||
// this save is made by an older game version which had different
|
||||
// properties.
|
||||
for (int i = 0; i < _GP(game).numcharacters; ++i)
|
||||
Properties::ReadValues(charProps[i], in);
|
||||
for (int i = 0; i < _GP(game).numinvitems; ++i)
|
||||
Properties::ReadValues(invProps[i], in);
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::WriteCustomProperties_v340(Stream *out, GameDataVersion data_ver) const {
|
||||
if (data_ver >= kGameVersion_340_4) {
|
||||
// We temporarily remove properties that kept default values
|
||||
// just for the saving data time to avoid getting lots of
|
||||
// redundant data into saved games
|
||||
for (int i = 0; i < _GP(game).numcharacters; ++i)
|
||||
Properties::WriteValues(charProps[i], out);
|
||||
for (int i = 0; i < _GP(game).numinvitems; ++i)
|
||||
Properties::WriteValues(invProps[i], out);
|
||||
}
|
||||
}
|
||||
|
||||
// Converts legacy alignment type used in script API
|
||||
HorAlignment ConvertLegacyScriptAlignment(LegacyScriptAlignment align) {
|
||||
switch (align) {
|
||||
case kLegacyScAlignLeft:
|
||||
return kHAlignLeft;
|
||||
case kLegacyScAlignCentre:
|
||||
return kHAlignCenter;
|
||||
case kLegacyScAlignRight:
|
||||
return kHAlignRight;
|
||||
}
|
||||
return kHAlignNone;
|
||||
}
|
||||
|
||||
// Reads legacy alignment type from the value set in script depending on the
|
||||
// current Script API level. This is made to make it possible to change
|
||||
// Alignment constants in the Script API and still support old version.
|
||||
HorAlignment ReadScriptAlignment(int32_t align) {
|
||||
return _GP(game).options[OPT_BASESCRIPTAPI] < kScriptAPI_v350 ?
|
||||
ConvertLegacyScriptAlignment((LegacyScriptAlignment)align) :
|
||||
(HorAlignment)align;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
Reference in New Issue
Block a user