1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ags/lib/std/algorithm.h"
24 #include "ags/engine/ac/draw.h"
25 #include "ags/shared/ac/game_version.h"
26 #include "ags/engine/ac/game_state.h"
27 #include "ags/shared/ac/game_setup_struct.h"
28 #include "ags/engine/ac/timer.h"
29 #include "ags/engine/ac/dynobj/script_camera.h"
30 #include "ags/engine/ac/dynobj/script_system.h"
31 #include "ags/engine/ac/dynobj/script_viewport.h"
32 #include "ags/engine/debugging/debug_log.h"
33 #include "ags/engine/device/mouse_w32.h"
34 #include "ags/shared/game/custom_properties.h"
35 #include "ags/shared/game/room_struct.h"
36 #include "ags/engine/game/savegame_internal.h"
37 #include "ags/engine/main/engine.h"
38 #include "ags/engine/media/audio/audio_system.h"
39 #include "ags/shared/util/aligned_stream.h"
40 #include "ags/shared/util/string_utils.h"
41 #include "ags/globals.h"
42
43 namespace AGS3 {
44
45 using namespace AGS::Shared;
46 using namespace AGS::Engine;
47
GameState()48 GameState::GameState() {
49 Common::fill(&globalvars[0], &globalvars[MAXGLOBALVARS], 0);
50 Common::fill(&reserved[0], &reserved[GAME_STATE_RESERVED_INTS], 0);
51 Common::fill(&globalscriptvars[0], &globalscriptvars[MAXGSVALUES], 0);
52 Common::fill(&walkable_areas_on[0], &walkable_areas_on[MAX_WALK_AREAS + 1], 0);
53 Common::fill(&script_timers[0], &script_timers[MAX_TIMERS], 0);
54 Common::fill(&parsed_words[0], &parsed_words[MAX_PARSED_WORDS], 0);
55 Common::fill(&bad_parsed_word[0], &bad_parsed_word[100], 0);
56 Common::fill(&raw_modified[0], &raw_modified[MAX_ROOM_BGFRAMES], 0);
57 Common::fill(&filenumbers[0], &filenumbers[MAXSAVEGAMES], 0);
58 Common::fill(&music_queue[0], &music_queue[MAX_QUEUED_MUSIC], 0);
59 Common::fill(&takeover_from[0], &takeover_from[50], 0);
60 Common::fill(&playmp3file_name[0], &playmp3file_name[PLAYMP3FILE_MAX_FILENAME_LEN], 0);
61 Common::fill(&globalstrings[0][0], &globalstrings[MAXGLOBALSTRINGS][0], 0);
62 Common::fill(&lastParserEntry[0], &lastParserEntry[MAX_MAXSTRLEN], 0);
63 Common::fill(&game_name[0], &game_name[100], 0);
64 Common::fill(&default_audio_type_volumes[0], &default_audio_type_volumes[MAX_AUDIO_TYPES], 0);
65
66 _isAutoRoomViewport = true;
67 _mainViewportHasChanged = false;
68 }
69
Free()70 void GameState::Free() {
71 raw_drawing_surface.reset();
72 FreeProperties();
73 }
74
IsAutoRoomViewport() const75 bool GameState::IsAutoRoomViewport() const {
76 return _isAutoRoomViewport;
77 }
78
SetAutoRoomViewport(bool on)79 void GameState::SetAutoRoomViewport(bool on) {
80 _isAutoRoomViewport = on;
81 }
82
SetMainViewport(const Rect & viewport)83 void GameState::SetMainViewport(const Rect &viewport) {
84 _mainViewport.SetRect(viewport);
85 _GP(mouse).UpdateGraphicArea();
86 _GP(scsystem).viewport_width = game_to_data_coord(_mainViewport.GetRect().GetWidth());
87 _GP(scsystem).viewport_height = game_to_data_coord(_mainViewport.GetRect().GetHeight());
88 _mainViewportHasChanged = true;
89 }
90
GetMainViewport() const91 const Rect &GameState::GetMainViewport() const {
92 return _mainViewport.GetRect();
93 }
94
GetUIViewport() const95 const Rect &GameState::GetUIViewport() const {
96 return _uiViewport.GetRect();
97 }
98
GetRoomViewport(int index) const99 PViewport GameState::GetRoomViewport(int index) const {
100 return _roomViewports[index];
101 }
102
GetRoomViewportsZOrdered() const103 const std::vector<PViewport> &GameState::GetRoomViewportsZOrdered() const {
104 return _roomViewportsSorted;
105 }
106
GetRoomViewportAt(int x,int y) const107 PViewport GameState::GetRoomViewportAt(int x, int y) const {
108 // We iterate backwards, because in AGS low z-order means bottom
109 for (auto it = _roomViewportsSorted.rbegin(); it != _roomViewportsSorted.rend(); ++it)
110 if ((*it)->IsVisible() && (*it)->GetRect().IsInside(x, y))
111 return *it;
112 return nullptr;
113 }
114
GetUIViewportAbs() const115 Rect GameState::GetUIViewportAbs() const {
116 return Rect::MoveBy(_uiViewport.GetRect(), _mainViewport.GetRect().Left, _mainViewport.GetRect().Top);
117 }
118
GetRoomViewportAbs(int index) const119 Rect GameState::GetRoomViewportAbs(int index) const {
120 return Rect::MoveBy(_roomViewports[index]->GetRect(), _mainViewport.GetRect().Left, _mainViewport.GetRect().Top);
121 }
122
SetUIViewport(const Rect & viewport)123 void GameState::SetUIViewport(const Rect &viewport) {
124 _uiViewport.SetRect(viewport);
125 }
126
ViewportZOrder(const PViewport e1,const PViewport e2)127 static bool ViewportZOrder(const PViewport e1, const PViewport e2) {
128 return e1->GetZOrder() < e2->GetZOrder();
129 }
130
UpdateViewports()131 void GameState::UpdateViewports() {
132 if (_mainViewportHasChanged) {
133 on_mainviewport_changed();
134 _mainViewportHasChanged = false;
135 }
136 if (_roomViewportZOrderChanged) {
137 auto old_sort = _roomViewportsSorted;
138 _roomViewportsSorted = _roomViewports;
139 std::sort(_roomViewportsSorted.begin(), _roomViewportsSorted.end(), ViewportZOrder);
140 for (size_t i = 0; i < _roomViewportsSorted.size(); ++i) {
141 if (i >= old_sort.size() || _roomViewportsSorted[i] != old_sort[i])
142 _roomViewportsSorted[i]->SetChangedVisible();
143 }
144 _roomViewportZOrderChanged = false;
145 }
146 size_t vp_changed = (size_t)-1;
147 for (size_t i = _roomViewportsSorted.size(); i-- > 0;) {
148 auto vp = _roomViewportsSorted[i];
149 if (vp->HasChangedSize() || vp->HasChangedPosition() || vp->HasChangedVisible()) {
150 vp_changed = i;
151 on_roomviewport_changed(vp.get());
152 vp->ClearChangedFlags();
153 }
154 }
155 if (vp_changed != (size_t)-1)
156 detect_roomviewport_overlaps(vp_changed);
157 for (auto cam : _roomCameras) {
158 if (cam->HasChangedSize() || cam->HasChangedPosition()) {
159 on_roomcamera_changed(cam.get());
160 cam->ClearChangedFlags();
161 }
162 }
163 }
164
InvalidateViewportZOrder()165 void GameState::InvalidateViewportZOrder() {
166 _roomViewportZOrderChanged = true;
167 }
168
GetRoomCamera(int index) const169 PCamera GameState::GetRoomCamera(int index) const {
170 return _roomCameras[index];
171 }
172
UpdateRoomCameras()173 void GameState::UpdateRoomCameras() {
174 for (size_t i = 0; i < _roomCameras.size(); ++i)
175 UpdateRoomCamera(i);
176 }
177
UpdateRoomCamera(int index)178 void GameState::UpdateRoomCamera(int index) {
179 auto cam = _roomCameras[index];
180 const Rect &rc = cam->GetRect();
181 const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
182 if ((real_room_sz.Width > rc.GetWidth()) || (real_room_sz.Height > rc.GetHeight())) {
183 // TODO: split out into Camera Behavior
184 if (!cam->IsLocked()) {
185 int x = data_to_game_coord(_G(playerchar)->x) - rc.GetWidth() / 2;
186 int y = data_to_game_coord(_G(playerchar)->y) - rc.GetHeight() / 2;
187 cam->SetAt(x, y);
188 }
189 }
190 }
191
RoomToScreen(int roomx,int roomy)192 Point GameState::RoomToScreen(int roomx, int roomy) {
193 return _roomViewports[0]->RoomToScreen(roomx, roomy, false).first;
194 }
195
RoomToScreenX(int roomx)196 int GameState::RoomToScreenX(int roomx) {
197 return _roomViewports[0]->RoomToScreen(roomx, 0, false).first.X;
198 }
199
RoomToScreenY(int roomy)200 int GameState::RoomToScreenY(int roomy) {
201 return _roomViewports[0]->RoomToScreen(0, roomy, false).first.Y;
202 }
203
ScreenToRoomImpl(int scrx,int scry,int view_index,bool clip_viewport,bool convert_cam_to_data)204 VpPoint GameState::ScreenToRoomImpl(int scrx, int scry, int view_index, bool clip_viewport, bool convert_cam_to_data) {
205 PViewport view;
206 if (view_index < 0) {
207 view = GetRoomViewportAt(scrx, scry);
208 if (!view)
209 return std::make_pair(Point(), -1);
210 } else {
211 view = _roomViewports[view_index];
212 }
213 return view->ScreenToRoom(scrx, scry, clip_viewport, convert_cam_to_data);
214 }
215
ScreenToRoom(int scrx,int scry)216 VpPoint GameState::ScreenToRoom(int scrx, int scry) {
217 if (_GP(game).options[OPT_BASESCRIPTAPI] >= kScriptAPI_v3507)
218 return ScreenToRoomImpl(scrx, scry, -1, true, false);
219 return ScreenToRoomImpl(scrx, scry, 0, false, false);
220 }
221
ScreenToRoomDivDown(int scrx,int scry)222 VpPoint GameState::ScreenToRoomDivDown(int scrx, int scry) {
223 if (_GP(game).options[OPT_BASESCRIPTAPI] >= kScriptAPI_v3507)
224 return ScreenToRoomImpl(scrx, scry, -1, true, true);
225 return ScreenToRoomImpl(scrx, scry, 0, false, true);
226 }
227
CreatePrimaryViewportAndCamera()228 void GameState::CreatePrimaryViewportAndCamera() {
229 if (_roomViewports.size() == 0) {
230 _GP(play).CreateRoomViewport();
231 _GP(play).RegisterRoomViewport(0);
232 }
233 if (_roomCameras.size() == 0) {
234 _GP(play).CreateRoomCamera();
235 _GP(play).RegisterRoomCamera(0);
236 }
237 _roomViewports[0]->LinkCamera(_roomCameras[0]);
238 _roomCameras[0]->LinkToViewport(_roomViewports[0]);
239 }
240
CreateRoomViewport()241 PViewport GameState::CreateRoomViewport() {
242 int index = (int)_roomViewports.size();
243 PViewport viewport(new Viewport());
244 viewport->SetID(index);
245 viewport->SetRect(_mainViewport.GetRect());
246 ScriptViewport *scv = new ScriptViewport(index);
247 _roomViewports.push_back(viewport);
248 _scViewportRefs.push_back(std::make_pair<ScriptViewport*, int32_t>(scv, 0));
249 _roomViewportsSorted.push_back(viewport);
250 _roomViewportZOrderChanged = true;
251 on_roomviewport_created(index);
252 return viewport;
253 }
254
RegisterRoomViewport(int index,int32_t handle)255 ScriptViewport *GameState::RegisterRoomViewport(int index, int32_t handle) {
256 if (index < 0 || (size_t)index >= _roomViewports.size())
257 return nullptr;
258 auto &scobj = _scViewportRefs[index];
259 if (handle == 0) {
260 handle = ccRegisterManagedObject(scobj.first, scobj.first);
261 ccAddObjectReference(handle); // one reference for the GameState
262 } else {
263 ccRegisterUnserializedObject(handle, scobj.first, scobj.first);
264 }
265 scobj.second = handle;
266 return scobj.first;
267 }
268
DeleteRoomViewport(int index)269 void GameState::DeleteRoomViewport(int index) {
270 // NOTE: viewport 0 can not be deleted
271 if (index <= 0 || (size_t)index >= _roomViewports.size())
272 return;
273 auto scobj = _scViewportRefs[index];
274 scobj.first->Invalidate();
275 ccReleaseObjectReference(scobj.second);
276 auto cam = _roomViewports[index]->GetCamera();
277 if (cam)
278 cam->UnlinkFromViewport(index);
279 _roomViewports.erase(_roomViewports.begin() + index);
280 _scViewportRefs.erase(_scViewportRefs.begin() + index);
281 for (size_t i = index; i < _roomViewports.size(); ++i) {
282 _roomViewports[i]->SetID(i);
283 _scViewportRefs[i].first->SetID(i);
284 }
285 for (size_t i = 0; i < _roomViewportsSorted.size(); ++i) {
286 if (_roomViewportsSorted[i]->GetID() == index) {
287 _roomViewportsSorted.erase(_roomViewportsSorted.begin() + i);
288 break;
289 }
290 }
291 on_roomviewport_deleted(index);
292 }
293
GetRoomViewportCount() const294 int GameState::GetRoomViewportCount() const {
295 return (int)_roomViewports.size();
296 }
297
CreateRoomCamera()298 PCamera GameState::CreateRoomCamera() {
299 int index = (int)_roomCameras.size();
300 PCamera camera(new Camera());
301 camera->SetID(index);
302 camera->SetAt(0, 0);
303 camera->SetSize(_mainViewport.GetRect().GetSize());
304 ScriptCamera *scam = new ScriptCamera(index);
305 _scCameraRefs.push_back(std::make_pair<ScriptCamera*, int32_t>(scam, 0));
306 _roomCameras.push_back(camera);
307 return camera;
308 }
309
RegisterRoomCamera(int index,int32_t handle)310 ScriptCamera *GameState::RegisterRoomCamera(int index, int32_t handle) {
311 if (index < 0 || (size_t)index >= _roomCameras.size())
312 return nullptr;
313 auto &scobj = _scCameraRefs[index];
314 if (handle == 0) {
315 handle = ccRegisterManagedObject(scobj.first, scobj.first);
316 ccAddObjectReference(handle); // one reference for the GameState
317 } else {
318 ccRegisterUnserializedObject(handle, scobj.first, scobj.first);
319 }
320 scobj.second = handle;
321 return scobj.first;
322 }
323
DeleteRoomCamera(int index)324 void GameState::DeleteRoomCamera(int index) {
325 // NOTE: camera 0 can not be deleted
326 if (index <= 0 || (size_t)index >= _roomCameras.size())
327 return;
328 auto scobj = _scCameraRefs[index];
329 scobj.first->Invalidate();
330 ccReleaseObjectReference(scobj.second);
331 for (auto &viewref : _roomCameras[index]->GetLinkedViewports()) {
332 auto view = viewref.lock();
333 if (view)
334 view->LinkCamera(nullptr);
335 }
336 _roomCameras.erase(_roomCameras.begin() + index);
337 _scCameraRefs.erase(_scCameraRefs.begin() + index);
338 for (size_t i = index; i < _roomCameras.size(); ++i) {
339 _roomCameras[i]->SetID(i);
340 _scCameraRefs[i].first->SetID(i);
341 }
342 }
343
GetRoomCameraCount() const344 int GameState::GetRoomCameraCount() const {
345 return (int)_roomCameras.size();
346 }
347
GetScriptViewport(int index)348 ScriptViewport *GameState::GetScriptViewport(int index) {
349 if (index < 0 || (size_t)index >= _roomViewports.size())
350 return NULL;
351 return _scViewportRefs[index].first;
352 }
353
GetScriptCamera(int index)354 ScriptCamera *GameState::GetScriptCamera(int index) {
355 if (index < 0 || (size_t)index >= _roomCameras.size())
356 return NULL;
357 return _scCameraRefs[index].first;
358 }
359
IsIgnoringInput() const360 bool GameState::IsIgnoringInput() const {
361 return AGS_Clock::now() < _ignoreUserInputUntilTime;
362 }
363
SetIgnoreInput(int timeout_ms)364 void GameState::SetIgnoreInput(int timeout_ms) {
365 if (AGS_Clock::now() + std::chrono::milliseconds(timeout_ms) > _ignoreUserInputUntilTime)
366 _ignoreUserInputUntilTime = AGS_Clock::now() + std::chrono::milliseconds(timeout_ms);
367 }
368
ClearIgnoreInput()369 void GameState::ClearIgnoreInput() {
370 _ignoreUserInputUntilTime = AGS_Clock::now();
371 }
372
SetWaitSkipResult(int how,int data)373 void GameState::SetWaitSkipResult(int how, int data) {
374 wait_counter = 0;
375 wait_skipped_by = how;
376 wait_skipped_by_data = data;
377 }
378
GetWaitSkipResult() const379 int GameState::GetWaitSkipResult() const {
380 switch (wait_skipped_by) {
381 case SKIP_KEYPRESS: return wait_skipped_by_data;
382 case SKIP_MOUSECLICK: return -(wait_skipped_by_data + 1); // convert to 1-based code and negate
383 default: return 0;
384 }
385 }
386
IsBlockingVoiceSpeech() const387 bool GameState::IsBlockingVoiceSpeech() const {
388 return speech_has_voice && speech_voice_blocking;
389 }
390
IsNonBlockingVoiceSpeech() const391 bool GameState::IsNonBlockingVoiceSpeech() const {
392 return speech_has_voice && !speech_voice_blocking;
393 }
394
ShouldPlayVoiceSpeech() const395 bool GameState::ShouldPlayVoiceSpeech() const {
396 return !_GP(play).fast_forward &&
397 (_GP(play).want_speech >= 1) && (!_GP(ResPaths).SpeechPak.Name.IsEmpty());
398 }
399
ReadFromSavegame(Shared::Stream * in,GameStateSvgVersion svg_ver,RestoredData & r_data)400 void GameState::ReadFromSavegame(Shared::Stream *in, GameStateSvgVersion svg_ver, RestoredData &r_data) {
401 const bool old_save = svg_ver < kGSSvgVersion_Initial;
402 score = in->ReadInt32();
403 usedmode = in->ReadInt32();
404 disabled_user_interface = in->ReadInt32();
405 gscript_timer = in->ReadInt32();
406 debug_mode = in->ReadInt32();
407 in->ReadArrayOfInt32(globalvars, MAXGLOBALVARS);
408 messagetime = in->ReadInt32();
409 usedinv = in->ReadInt32();
410 inv_top = in->ReadInt32();
411 inv_numdisp = in->ReadInt32();
412 obsolete_inv_numorder = in->ReadInt32();
413 inv_numinline = in->ReadInt32();
414 text_speed = in->ReadInt32();
415 sierra_inv_color = in->ReadInt32();
416 talkanim_speed = in->ReadInt32();
417 inv_item_wid = in->ReadInt32();
418 inv_item_hit = in->ReadInt32();
419 speech_text_shadow = in->ReadInt32();
420 swap_portrait_side = in->ReadInt32();
421 speech_textwindow_gui = in->ReadInt32();
422 follow_change_room_timer = in->ReadInt32();
423 totalscore = in->ReadInt32();
424 skip_display = in->ReadInt32();
425 no_multiloop_repeat = in->ReadInt32();
426 roomscript_finished = in->ReadInt32();
427 used_inv_on = in->ReadInt32();
428 no_textbg_when_voice = in->ReadInt32();
429 max_dialogoption_width = in->ReadInt32();
430 no_hicolor_fadein = in->ReadInt32();
431 bgspeech_game_speed = in->ReadInt32();
432 bgspeech_stay_on_display = in->ReadInt32();
433 unfactor_speech_from_textlength = in->ReadInt32();
434 mp3_loop_before_end = in->ReadInt32();
435 speech_music_drop = in->ReadInt32();
436 in_cutscene = in->ReadInt32();
437 fast_forward = in->ReadInt32();
438 room_width = in->ReadInt32();
439 room_height = in->ReadInt32();
440 game_speed_modifier = in->ReadInt32();
441 score_sound = in->ReadInt32();
442 takeover_data = in->ReadInt32();
443 replay_hotkey_unused = in->ReadInt32();
444 dialog_options_x = in->ReadInt32();
445 dialog_options_y = in->ReadInt32();
446 narrator_speech = in->ReadInt32();
447 ambient_sounds_persist = in->ReadInt32();
448 lipsync_speed = in->ReadInt32();
449 close_mouth_speech_time = in->ReadInt32();
450 disable_antialiasing = in->ReadInt32();
451 text_speed_modifier = in->ReadInt32();
452 if (svg_ver < kGSSvgVersion_350)
453 text_align = ConvertLegacyScriptAlignment((LegacyScriptAlignment)in->ReadInt32());
454 else
455 text_align = (HorAlignment)in->ReadInt32();
456 speech_bubble_width = in->ReadInt32();
457 min_dialogoption_width = in->ReadInt32();
458 disable_dialog_parser = in->ReadInt32();
459 anim_background_speed = in->ReadInt32(); // the setting for this room
460 top_bar_backcolor = in->ReadInt32();
461 top_bar_textcolor = in->ReadInt32();
462 top_bar_bordercolor = in->ReadInt32();
463 top_bar_borderwidth = in->ReadInt32();
464 top_bar_ypos = in->ReadInt32();
465 screenshot_width = in->ReadInt32();
466 screenshot_height = in->ReadInt32();
467 top_bar_font = in->ReadInt32();
468 if (svg_ver < kGSSvgVersion_350)
469 speech_text_align = ConvertLegacyScriptAlignment((LegacyScriptAlignment)in->ReadInt32());
470 else
471 speech_text_align = (HorAlignment)in->ReadInt32();
472 auto_use_walkto_points = in->ReadInt32();
473 inventory_greys_out = in->ReadInt32();
474 skip_speech_specific_key = in->ReadInt32();
475 abort_key = in->ReadInt32();
476 fade_to_red = in->ReadInt32();
477 fade_to_green = in->ReadInt32();
478 fade_to_blue = in->ReadInt32();
479 show_single_dialog_option = in->ReadInt32();
480 keep_screen_during_instant_transition = in->ReadInt32();
481 read_dialog_option_colour = in->ReadInt32();
482 stop_dialog_at_end = in->ReadInt32();
483 speech_portrait_placement = in->ReadInt32();
484 speech_portrait_x = in->ReadInt32();
485 speech_portrait_y = in->ReadInt32();
486 speech_display_post_time_ms = in->ReadInt32();
487 dialog_options_highlight_color = in->ReadInt32();
488 if (old_save)
489 in->ReadArrayOfInt32(reserved, GAME_STATE_RESERVED_INTS);
490 // ** up to here is referenced in the script "game." object
491 if (old_save) {
492 in->ReadInt32(); // recording
493 in->ReadInt32(); // playback
494 in->ReadInt16(); // gamestep
495 }
496 randseed = in->ReadInt32(); // random seed
497 player_on_region = in->ReadInt32(); // player's current region
498 if (old_save)
499 in->ReadInt32(); // screen_is_faded_out
500 check_interaction_only = in->ReadInt32();
501 bg_frame = in->ReadInt32();
502 bg_anim_delay = in->ReadInt32(); // for animating backgrounds
503 music_vol_was = in->ReadInt32(); // before the volume drop
504 wait_counter = in->ReadInt16();
505 mboundx1 = in->ReadInt16();
506 mboundx2 = in->ReadInt16();
507 mboundy1 = in->ReadInt16();
508 mboundy2 = in->ReadInt16();
509 fade_effect = in->ReadInt32();
510 bg_frame_locked = in->ReadInt32();
511 in->ReadArrayOfInt32(globalscriptvars, MAXGSVALUES);
512 cur_music_number = in->ReadInt32();
513 music_repeat = in->ReadInt32();
514 music_master_volume = in->ReadInt32();
515 digital_master_volume = in->ReadInt32();
516 in->Read(walkable_areas_on, MAX_WALK_AREAS + 1);
517 screen_flipped = in->ReadInt16();
518 if (svg_ver < kGSSvgVersion_350_10) {
519 short offsets_locked = in->ReadInt16();
520 if (offsets_locked != 0)
521 r_data.Camera0_Flags = kSvgCamPosLocked;
522 }
523 entered_at_x = in->ReadInt32();
524 entered_at_y = in->ReadInt32();
525 entered_edge = in->ReadInt32();
526 want_speech = in->ReadInt32();
527 cant_skip_speech = in->ReadInt32();
528 in->ReadArrayOfInt32(script_timers, MAX_TIMERS);
529 sound_volume = in->ReadInt32();
530 speech_volume = in->ReadInt32();
531 normal_font = in->ReadInt32();
532 speech_font = in->ReadInt32();
533 key_skip_wait = in->ReadInt8();
534 swap_portrait_lastchar = in->ReadInt32();
535 separate_music_lib = in->ReadInt32();
536 in_conversation = in->ReadInt32();
537 screen_tint = in->ReadInt32();
538 num_parsed_words = in->ReadInt32();
539 in->ReadArrayOfInt16(parsed_words, MAX_PARSED_WORDS);
540 in->Read(bad_parsed_word, 100);
541 raw_color = in->ReadInt32();
542 if (old_save)
543 in->ReadArrayOfInt32(raw_modified, MAX_ROOM_BGFRAMES);
544 in->ReadArrayOfInt16(filenumbers, MAXSAVEGAMES);
545 if (old_save)
546 in->ReadInt32(); // room_changes
547 mouse_cursor_hidden = in->ReadInt32();
548 silent_midi = in->ReadInt32();
549 silent_midi_channel = in->ReadInt32();
550 current_music_repeating = in->ReadInt32();
551 shakesc_delay = in->ReadInt32();
552 shakesc_amount = in->ReadInt32();
553 shakesc_length = in->ReadInt32();
554 rtint_red = in->ReadInt32();
555 rtint_green = in->ReadInt32();
556 rtint_blue = in->ReadInt32();
557 rtint_level = in->ReadInt32();
558 rtint_light = in->ReadInt32();
559 if (!old_save || _G(loaded_game_file_version) >= kGameVersion_340_4)
560 rtint_enabled = in->ReadBool();
561 else
562 rtint_enabled = rtint_level > 0;
563 end_cutscene_music = in->ReadInt32();
564 skip_until_char_stops = in->ReadInt32();
565 get_loc_name_last_time = in->ReadInt32();
566 get_loc_name_save_cursor = in->ReadInt32();
567 restore_cursor_mode_to = in->ReadInt32();
568 restore_cursor_image_to = in->ReadInt32();
569 music_queue_size = in->ReadInt16();
570 in->ReadArrayOfInt16(music_queue, MAX_QUEUED_MUSIC);
571 new_music_queue_size = in->ReadInt16();
572 if (!old_save) {
573 for (int i = 0; i < MAX_QUEUED_MUSIC; ++i) {
574 new_music_queue[i].ReadFromFile(in);
575 }
576 }
577
578 crossfading_out_channel = in->ReadInt16();
579 crossfade_step = in->ReadInt16();
580 crossfade_out_volume_per_step = in->ReadInt16();
581 crossfade_initial_volume_out = in->ReadInt16();
582 crossfading_in_channel = in->ReadInt16();
583 crossfade_in_volume_per_step = in->ReadInt16();
584 crossfade_final_volume_in = in->ReadInt16();
585
586 if (old_save)
587 ReadQueuedAudioItems_Aligned(in);
588
589 in->Read(takeover_from, 50);
590 in->Read(playmp3file_name, PLAYMP3FILE_MAX_FILENAME_LEN);
591 in->Read(globalstrings, MAXGLOBALSTRINGS * MAX_MAXSTRLEN);
592 in->Read(lastParserEntry, MAX_MAXSTRLEN);
593 in->Read(game_name, 100);
594 ground_level_areas_disabled = in->ReadInt32();
595 next_screen_transition = in->ReadInt32();
596 in->ReadInt32(); // gamma_adjustment -- do not apply gamma level from savegame
597 temporarily_turned_off_character = in->ReadInt16();
598 inv_backwards_compatibility = in->ReadInt16();
599 if (old_save) {
600 in->ReadInt32(); // gui_draw_order
601 in->ReadInt32(); // do_once_tokens;
602 }
603 int num_do_once_tokens = in->ReadInt32();
604 do_once_tokens.resize(num_do_once_tokens);
605 if (!old_save) {
606 for (int i = 0; i < num_do_once_tokens; ++i) {
607 StrUtil::ReadString(do_once_tokens[i], in);
608 }
609 }
610 text_min_display_time_ms = in->ReadInt32();
611 ignore_user_input_after_text_timeout_ms = in->ReadInt32();
612 if (svg_ver < kGSSvgVersion_350_9)
613 in->ReadInt32(); // ignore_user_input_until_time -- do not apply from savegame
614 if (old_save)
615 in->ReadArrayOfInt32(default_audio_type_volumes, MAX_AUDIO_TYPES);
616 if (svg_ver >= kGSSvgVersion_350_9) {
617 int voice_speech_flags = in->ReadInt32();
618 speech_has_voice = voice_speech_flags != 0;
619 speech_voice_blocking = (voice_speech_flags & 0x02) != 0;
620 }
621 }
622
WriteForSavegame(Shared::Stream * out) const623 void GameState::WriteForSavegame(Shared::Stream *out) const {
624 // NOTE: following parameters are never saved:
625 // recording, playback, gamestep, screen_is_faded_out, room_changes
626 out->WriteInt32(score);
627 out->WriteInt32(usedmode);
628 out->WriteInt32(disabled_user_interface);
629 out->WriteInt32(gscript_timer);
630 out->WriteInt32(debug_mode);
631 out->WriteArrayOfInt32(globalvars, MAXGLOBALVARS);
632 out->WriteInt32(messagetime);
633 out->WriteInt32(usedinv);
634 out->WriteInt32(inv_top);
635 out->WriteInt32(inv_numdisp);
636 out->WriteInt32(obsolete_inv_numorder);
637 out->WriteInt32(inv_numinline);
638 out->WriteInt32(text_speed);
639 out->WriteInt32(sierra_inv_color);
640 out->WriteInt32(talkanim_speed);
641 out->WriteInt32(inv_item_wid);
642 out->WriteInt32(inv_item_hit);
643 out->WriteInt32(speech_text_shadow);
644 out->WriteInt32(swap_portrait_side);
645 out->WriteInt32(speech_textwindow_gui);
646 out->WriteInt32(follow_change_room_timer);
647 out->WriteInt32(totalscore);
648 out->WriteInt32(skip_display);
649 out->WriteInt32(no_multiloop_repeat);
650 out->WriteInt32(roomscript_finished);
651 out->WriteInt32(used_inv_on);
652 out->WriteInt32(no_textbg_when_voice);
653 out->WriteInt32(max_dialogoption_width);
654 out->WriteInt32(no_hicolor_fadein);
655 out->WriteInt32(bgspeech_game_speed);
656 out->WriteInt32(bgspeech_stay_on_display);
657 out->WriteInt32(unfactor_speech_from_textlength);
658 out->WriteInt32(mp3_loop_before_end);
659 out->WriteInt32(speech_music_drop);
660 out->WriteInt32(in_cutscene);
661 out->WriteInt32(fast_forward);
662 out->WriteInt32(room_width);
663 out->WriteInt32(room_height);
664 out->WriteInt32(game_speed_modifier);
665 out->WriteInt32(score_sound);
666 out->WriteInt32(takeover_data);
667 out->WriteInt32(replay_hotkey_unused); // StartRecording: not supported
668 out->WriteInt32(dialog_options_x);
669 out->WriteInt32(dialog_options_y);
670 out->WriteInt32(narrator_speech);
671 out->WriteInt32(ambient_sounds_persist);
672 out->WriteInt32(lipsync_speed);
673 out->WriteInt32(close_mouth_speech_time);
674 out->WriteInt32(disable_antialiasing);
675 out->WriteInt32(text_speed_modifier);
676 out->WriteInt32(text_align);
677 out->WriteInt32(speech_bubble_width);
678 out->WriteInt32(min_dialogoption_width);
679 out->WriteInt32(disable_dialog_parser);
680 out->WriteInt32(anim_background_speed); // the setting for this room
681 out->WriteInt32(top_bar_backcolor);
682 out->WriteInt32(top_bar_textcolor);
683 out->WriteInt32(top_bar_bordercolor);
684 out->WriteInt32(top_bar_borderwidth);
685 out->WriteInt32(top_bar_ypos);
686 out->WriteInt32(screenshot_width);
687 out->WriteInt32(screenshot_height);
688 out->WriteInt32(top_bar_font);
689 out->WriteInt32(speech_text_align);
690 out->WriteInt32(auto_use_walkto_points);
691 out->WriteInt32(inventory_greys_out);
692 out->WriteInt32(skip_speech_specific_key);
693 out->WriteInt32(abort_key);
694 out->WriteInt32(fade_to_red);
695 out->WriteInt32(fade_to_green);
696 out->WriteInt32(fade_to_blue);
697 out->WriteInt32(show_single_dialog_option);
698 out->WriteInt32(keep_screen_during_instant_transition);
699 out->WriteInt32(read_dialog_option_colour);
700 out->WriteInt32(stop_dialog_at_end);
701 out->WriteInt32(speech_portrait_placement);
702 out->WriteInt32(speech_portrait_x);
703 out->WriteInt32(speech_portrait_y);
704 out->WriteInt32(speech_display_post_time_ms);
705 out->WriteInt32(dialog_options_highlight_color);
706 // ** up to here is referenced in the script "game." object
707 out->WriteInt32(randseed); // random seed
708 out->WriteInt32(player_on_region); // player's current region
709 out->WriteInt32(check_interaction_only);
710 out->WriteInt32(bg_frame);
711 out->WriteInt32(bg_anim_delay); // for animating backgrounds
712 out->WriteInt32(music_vol_was); // before the volume drop
713 out->WriteInt16(wait_counter);
714 out->WriteInt16(mboundx1);
715 out->WriteInt16(mboundx2);
716 out->WriteInt16(mboundy1);
717 out->WriteInt16(mboundy2);
718 out->WriteInt32(fade_effect);
719 out->WriteInt32(bg_frame_locked);
720 out->WriteArrayOfInt32(globalscriptvars, MAXGSVALUES);
721 out->WriteInt32(cur_music_number);
722 out->WriteInt32(music_repeat);
723 out->WriteInt32(music_master_volume);
724 out->WriteInt32(digital_master_volume);
725 out->Write(walkable_areas_on, MAX_WALK_AREAS + 1);
726 out->WriteInt16(screen_flipped);
727 out->WriteInt32(entered_at_x);
728 out->WriteInt32(entered_at_y);
729 out->WriteInt32(entered_edge);
730 out->WriteInt32(want_speech);
731 out->WriteInt32(cant_skip_speech);
732 out->WriteArrayOfInt32(script_timers, MAX_TIMERS);
733 out->WriteInt32(sound_volume);
734 out->WriteInt32(speech_volume);
735 out->WriteInt32(normal_font);
736 out->WriteInt32(speech_font);
737 out->WriteInt8(key_skip_wait);
738 out->WriteInt32(swap_portrait_lastchar);
739 out->WriteInt32(separate_music_lib);
740 out->WriteInt32(in_conversation);
741 out->WriteInt32(screen_tint);
742 out->WriteInt32(num_parsed_words);
743 out->WriteArrayOfInt16(parsed_words, MAX_PARSED_WORDS);
744 out->Write(bad_parsed_word, 100);
745 out->WriteInt32(raw_color);
746 out->WriteArrayOfInt16(filenumbers, MAXSAVEGAMES);
747 out->WriteInt32(mouse_cursor_hidden);
748 out->WriteInt32(silent_midi);
749 out->WriteInt32(silent_midi_channel);
750 out->WriteInt32(current_music_repeating);
751 out->WriteInt32(shakesc_delay);
752 out->WriteInt32(shakesc_amount);
753 out->WriteInt32(shakesc_length);
754 out->WriteInt32(rtint_red);
755 out->WriteInt32(rtint_green);
756 out->WriteInt32(rtint_blue);
757 out->WriteInt32(rtint_level);
758 out->WriteInt32(rtint_light);
759 out->WriteBool(rtint_enabled);
760 out->WriteInt32(end_cutscene_music);
761 out->WriteInt32(skip_until_char_stops);
762 out->WriteInt32(get_loc_name_last_time);
763 out->WriteInt32(get_loc_name_save_cursor);
764 out->WriteInt32(restore_cursor_mode_to);
765 out->WriteInt32(restore_cursor_image_to);
766 out->WriteInt16(music_queue_size);
767 out->WriteArrayOfInt16(music_queue, MAX_QUEUED_MUSIC);
768 out->WriteInt16(new_music_queue_size);
769 for (int i = 0; i < MAX_QUEUED_MUSIC; ++i) {
770 new_music_queue[i].WriteToFile(out);
771 }
772
773 out->WriteInt16(crossfading_out_channel);
774 out->WriteInt16(crossfade_step);
775 out->WriteInt16(crossfade_out_volume_per_step);
776 out->WriteInt16(crossfade_initial_volume_out);
777 out->WriteInt16(crossfading_in_channel);
778 out->WriteInt16(crossfade_in_volume_per_step);
779 out->WriteInt16(crossfade_final_volume_in);
780
781 out->Write(takeover_from, 50);
782 out->Write(playmp3file_name, PLAYMP3FILE_MAX_FILENAME_LEN);
783 out->Write(globalstrings, MAXGLOBALSTRINGS * MAX_MAXSTRLEN);
784 out->Write(lastParserEntry, MAX_MAXSTRLEN);
785 out->Write(game_name, 100);
786 out->WriteInt32(ground_level_areas_disabled);
787 out->WriteInt32(next_screen_transition);
788 out->WriteInt32(gamma_adjustment);
789 out->WriteInt16(temporarily_turned_off_character);
790 out->WriteInt16(inv_backwards_compatibility);
791 out->WriteInt32(do_once_tokens.size());
792 for (int i = 0; i < (int)do_once_tokens.size(); ++i) {
793 StrUtil::WriteString(do_once_tokens[i], out);
794 }
795 out->WriteInt32(text_min_display_time_ms);
796 out->WriteInt32(ignore_user_input_after_text_timeout_ms);
797
798 int voice_speech_flags = speech_has_voice ? 0x01 : 0;
799 if (speech_voice_blocking)
800 voice_speech_flags |= 0x02;
801 out->WriteInt32(voice_speech_flags);
802 }
803
ReadQueuedAudioItems_Aligned(Shared::Stream * in)804 void GameState::ReadQueuedAudioItems_Aligned(Shared::Stream *in) {
805 AlignedStream align_s(in, Shared::kAligned_Read);
806 for (int i = 0; i < MAX_QUEUED_MUSIC; ++i) {
807 new_music_queue[i].ReadFromFile(&align_s);
808 align_s.Reset();
809 }
810 }
811
FreeProperties()812 void GameState::FreeProperties() {
813 for (auto &p : charProps)
814 p.clear();
815 for (auto &p : invProps)
816 p.clear();
817 }
818
FreeViewportsAndCameras()819 void GameState::FreeViewportsAndCameras() {
820 _roomViewports.clear();
821 _roomViewportsSorted.clear();
822 for (auto &scobj : _scViewportRefs) {
823 scobj.first->Invalidate();
824 ccReleaseObjectReference(scobj.second);
825 }
826 _scViewportRefs.clear();
827 _roomCameras.clear();
828 for (auto &scobj : _scCameraRefs) {
829 scobj.first->Invalidate();
830 ccReleaseObjectReference(scobj.second);
831 }
832 _scCameraRefs.clear();
833 }
834
ReadCustomProperties_v340(Shared::Stream * in)835 void GameState::ReadCustomProperties_v340(Shared::Stream *in) {
836 if (_G(loaded_game_file_version) >= kGameVersion_340_4) {
837 // After runtime property values were read we also copy missing default,
838 // because we do not keep defaults in the saved game, and also in case
839 // this save is made by an older game version which had different
840 // properties.
841 for (int i = 0; i < _GP(game).numcharacters; ++i)
842 Properties::ReadValues(charProps[i], in);
843 for (int i = 0; i < _GP(game).numinvitems; ++i)
844 Properties::ReadValues(invProps[i], in);
845 }
846 }
847
WriteCustomProperties_v340(Shared::Stream * out) const848 void GameState::WriteCustomProperties_v340(Shared::Stream *out) const {
849 if (_G(loaded_game_file_version) >= kGameVersion_340_4) {
850 // We temporarily remove properties that kept default values
851 // just for the saving data time to avoid getting lots of
852 // redundant data into saved games
853 for (int i = 0; i < _GP(game).numcharacters; ++i)
854 Properties::WriteValues(charProps[i], out);
855 for (int i = 0; i < _GP(game).numinvitems; ++i)
856 Properties::WriteValues(invProps[i], out);
857 }
858 }
859
860 // Converts legacy alignment type used in script API
ConvertLegacyScriptAlignment(LegacyScriptAlignment align)861 HorAlignment ConvertLegacyScriptAlignment(LegacyScriptAlignment align) {
862 switch (align) {
863 case kLegacyScAlignLeft:
864 return kHAlignLeft;
865 case kLegacyScAlignCentre:
866 return kHAlignCenter;
867 case kLegacyScAlignRight:
868 return kHAlignRight;
869 }
870 return kHAlignNone;
871 }
872
873 // Reads legacy alignment type from the value set in script depending on the
874 // current Script API level. This is made to make it possible to change
875 // Alignment constants in the Script API and still support old version.
ReadScriptAlignment(int32_t align)876 HorAlignment ReadScriptAlignment(int32_t align) {
877 return _GP(game).options[OPT_BASESCRIPTAPI] < kScriptAPI_v350 ?
878 ConvertLegacyScriptAlignment((LegacyScriptAlignment)align) :
879 (HorAlignment)align;
880 }
881
882 } // namespace AGS3
883