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