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/shared/core/platform.h"
24 #include "ags/shared/util/string_utils.h" //strlwr()
25 #include "ags/shared/ac/common.h"
26 #include "ags/engine/ac/character.h"
27 #include "ags/engine/ac/character_cache.h"
28 #include "ags/engine/ac/character_extras.h"
29 #include "ags/engine/ac/draw.h"
30 #include "ags/engine/ac/event.h"
31 #include "ags/engine/ac/game.h"
32 #include "ags/engine/ac/game_setup.h"
33 #include "ags/shared/ac/game_setup_struct.h"
34 #include "ags/engine/ac/game_state.h"
35 #include "ags/engine/ac/global_audio.h"
36 #include "ags/engine/ac/global_character.h"
37 #include "ags/engine/ac/global_game.h"
38 #include "ags/engine/ac/global_object.h"
39 #include "ags/engine/ac/global_translation.h"
40 #include "ags/engine/ac/move_list.h"
41 #include "ags/engine/ac/mouse.h"
42 #include "ags/engine/ac/object_cache.h"
43 #include "ags/engine/ac/overlay.h"
44 #include "ags/engine/ac/properties.h"
45 #include "ags/engine/ac/region.h"
46 #include "ags/engine/ac/sys_events.h"
47 #include "ags/engine/ac/room.h"
48 #include "ags/engine/ac/room_object.h"
49 #include "ags/engine/ac/room_status.h"
50 #include "ags/engine/ac/screen.h"
51 #include "ags/engine/ac/string.h"
52 #include "ags/engine/ac/system.h"
53 #include "ags/engine/ac/walkable_area.h"
54 #include "ags/engine/ac/walk_behind.h"
55 #include "ags/engine/ac/dynobj/script_object.h"
56 #include "ags/engine/ac/dynobj/script_hotspot.h"
57 #include "ags/shared/gui/gui_main.h"
58 #include "ags/engine/script/cc_instance.h"
59 #include "ags/engine/debugging/debug_log.h"
60 #include "ags/engine/debugging/debugger.h"
61 #include "ags/shared/debugging/out.h"
62 #include "ags/shared/game/room_version.h"
63 #include "ags/engine/platform/base/ags_platform_driver.h"
64 #include "ags/plugins/ags_plugin.h"
65 #include "ags/plugins/plugin_engine.h"
66 #include "ags/shared/script/cc_error.h"
67 #include "ags/engine/script/script.h"
68 #include "ags/engine/script/script_runtime.h"
69 #include "ags/shared/ac/sprite_cache.h"
70 #include "ags/shared/util/stream.h"
71 #include "ags/engine/gfx/graphics_driver.h"
72 #include "ags/shared/core/asset_manager.h"
73 #include "ags/engine/ac/dynobj/all_dynamic_classes.h"
74 #include "ags/shared/gfx/bitmap.h"
75 #include "ags/engine/gfx/gfxfilter.h"
76 #include "ags/shared/util/math.h"
77 #include "ags/engine/media/audio/audio_system.h"
78 #include "ags/shared/debugging/out.h"
79 #include "ags/engine/script/script_api.h"
80 #include "ags/engine/script/script_runtime.h"
81 #include "ags/engine/ac/dynobj/script_string.h"
82 #include "ags/globals.h"
83 
84 namespace AGS3 {
85 
86 using namespace AGS::Shared;
87 using namespace AGS::Engine;
88 
Room_GetDrawingSurfaceForBackground(int backgroundNumber)89 ScriptDrawingSurface *Room_GetDrawingSurfaceForBackground(int backgroundNumber) {
90 	if (_G(displayed_room) < 0)
91 		quit("!Room.GetDrawingSurfaceForBackground: no room is currently loaded");
92 
93 	if (backgroundNumber == SCR_NO_VALUE) {
94 		backgroundNumber = _GP(play).bg_frame;
95 	}
96 
97 	if ((backgroundNumber < 0) || ((size_t)backgroundNumber >= _GP(thisroom).BgFrameCount))
98 		quit("!Room.GetDrawingSurfaceForBackground: invalid background number specified");
99 
100 
101 	ScriptDrawingSurface *surface = new ScriptDrawingSurface();
102 	surface->roomBackgroundNumber = backgroundNumber;
103 	ccRegisterManagedObject(surface, surface);
104 	return surface;
105 }
106 
Room_GetDrawingSurfaceForMask(RoomAreaMask mask)107 ScriptDrawingSurface *Room_GetDrawingSurfaceForMask(RoomAreaMask mask) {
108 	if (_G(displayed_room) < 0)
109 		quit("!Room_GetDrawingSurfaceForMask: no room is currently loaded");
110 	ScriptDrawingSurface *surface = new ScriptDrawingSurface();
111 	surface->roomMaskType = mask;
112 	ccRegisterManagedObject(surface, surface);
113 	return surface;
114 }
115 
Room_GetObjectCount()116 int Room_GetObjectCount() {
117 	return _G(croom)->numobj;
118 }
119 
Room_GetWidth()120 int Room_GetWidth() {
121 	return _GP(thisroom).Width;
122 }
123 
Room_GetHeight()124 int Room_GetHeight() {
125 	return _GP(thisroom).Height;
126 }
127 
Room_GetColorDepth()128 int Room_GetColorDepth() {
129 	return _GP(thisroom).BgFrames[0].Graphic->GetColorDepth();
130 }
131 
Room_GetLeftEdge()132 int Room_GetLeftEdge() {
133 	return _GP(thisroom).Edges.Left;
134 }
135 
Room_GetRightEdge()136 int Room_GetRightEdge() {
137 	return _GP(thisroom).Edges.Right;
138 }
139 
Room_GetTopEdge()140 int Room_GetTopEdge() {
141 	return _GP(thisroom).Edges.Top;
142 }
143 
Room_GetBottomEdge()144 int Room_GetBottomEdge() {
145 	return _GP(thisroom).Edges.Bottom;
146 }
147 
Room_GetMusicOnLoad()148 int Room_GetMusicOnLoad() {
149 	return _GP(thisroom).Options.StartupMusic;
150 }
151 
Room_GetProperty(const char * property)152 int Room_GetProperty(const char *property) {
153 	return get_int_property(_GP(thisroom).Properties, _G(croom)->roomProps, property);
154 }
155 
Room_GetTextProperty(const char * property)156 const char *Room_GetTextProperty(const char *property) {
157 	return get_text_property_dynamic_string(_GP(thisroom).Properties, _G(croom)->roomProps, property);
158 }
159 
Room_SetProperty(const char * property,int value)160 bool Room_SetProperty(const char *property, int value) {
161 	return set_int_property(_G(croom)->roomProps, property, value);
162 }
163 
Room_SetTextProperty(const char * property,const char * value)164 bool Room_SetTextProperty(const char *property, const char *value) {
165 	return set_text_property(_G(croom)->roomProps, property, value);
166 }
167 
Room_GetMessages(int index)168 const char *Room_GetMessages(int index) {
169 	if ((index < 0) || ((size_t)index >= _GP(thisroom).MessageCount)) {
170 		return nullptr;
171 	}
172 	char buffer[STD_BUFFER_SIZE];
173 	buffer[0] = 0;
174 	replace_tokens(get_translation(_GP(thisroom).Messages[index].GetCStr()), buffer, STD_BUFFER_SIZE);
175 	return CreateNewScriptString(buffer);
176 }
177 
Room_Exists(int room)178 bool Room_Exists(int room) {
179 	String room_filename;
180 	room_filename.Format("room%d.crm", room);
181 	return _GP(AssetMgr)->DoesAssetExist(room_filename);
182 }
183 
184 //=============================================================================
185 
186 // Makes sure that room background and walk-behind mask are matching room size
187 // in game resolution coordinates; in other words makes graphics appropriate
188 // for display in the game.
convert_room_background_to_game_res()189 void convert_room_background_to_game_res() {
190 	if (!_GP(game).AllowRelativeRes() || !_GP(thisroom).IsRelativeRes())
191 		return;
192 
193 	int bkg_width = _GP(thisroom).Width;
194 	int bkg_height = _GP(thisroom).Height;
195 	data_to_game_coords(&bkg_width, &bkg_height);
196 
197 	for (size_t i = 0; i < _GP(thisroom).BgFrameCount; ++i)
198 		_GP(thisroom).BgFrames[i].Graphic = FixBitmap(_GP(thisroom).BgFrames[i].Graphic, bkg_width, bkg_height);
199 
200 	// Fix masks to match resized room background
201 	// Walk-behind is always 1:1 with room background size
202 	_GP(thisroom).WalkBehindMask = FixBitmap(_GP(thisroom).WalkBehindMask, bkg_width, bkg_height);
203 	int mask_width = bkg_width / _GP(thisroom).MaskResolution;
204 	int mask_height = bkg_height / _GP(thisroom).MaskResolution;
205 	_GP(thisroom).HotspotMask = FixBitmap(_GP(thisroom).HotspotMask, mask_width, mask_height);
206 	_GP(thisroom).RegionMask = FixBitmap(_GP(thisroom).RegionMask, mask_width, mask_height);
207 	_GP(thisroom).WalkAreaMask = FixBitmap(_GP(thisroom).WalkAreaMask, mask_width, mask_height);
208 
209 	for (size_t i = 0; i < _GP(thisroom).WalkAreaCount; ++i) {
210 		_GP(thisroom).WalkAreas[i].Top = room_to_mask_coord(_GP(thisroom).WalkAreas[i].Top);
211 		_GP(thisroom).WalkAreas[i].Bottom = room_to_mask_coord(_GP(thisroom).WalkAreas[i].Bottom);
212 	}
213 }
214 
215 
save_room_data_segment()216 void save_room_data_segment() {
217 	_G(croom)->FreeScriptData();
218 
219 	_G(croom)->tsdatasize = _G(roominst)->globaldatasize;
220 	if (_G(croom)->tsdatasize > 0) {
221 		_G(croom)->tsdata = (char *)malloc(_G(croom)->tsdatasize + 10);
222 		memcpy(_G(croom)->tsdata, &_G(roominst)->globaldata[0], _G(croom)->tsdatasize);
223 	}
224 
225 }
226 
unload_old_room()227 void unload_old_room() {
228 	int ff;
229 
230 	// if switching games on restore, don't do this
231 	if (_G(displayed_room) < 0)
232 		return;
233 
234 	debug_script_log("Unloading room %d", _G(displayed_room));
235 
236 	current_fade_out_effect();
237 
238 	dispose_room_drawdata();
239 
240 	for (ff = 0; ff < _G(croom)->numobj; ff++)
241 		_G(objs)[ff].moving = 0;
242 
243 	if (!_GP(play).ambient_sounds_persist) {
244 		for (ff = 1; ff < MAX_SOUND_CHANNELS; ff++)
245 			StopAmbientSound(ff);
246 	}
247 
248 	cancel_all_scripts();
249 	_G(numevents) = 0;  // cancel any pending room events
250 
251 	if (_G(roomBackgroundBmp) != nullptr) {
252 		_G(gfxDriver)->DestroyDDB(_G(roomBackgroundBmp));
253 		_G(roomBackgroundBmp) = nullptr;
254 	}
255 
256 	if (_G(croom) == nullptr) ;
257 	else if (_G(roominst) != nullptr) {
258 		save_room_data_segment();
259 		delete _G(roominstFork);
260 		delete _G(roominst);
261 		_G(roominstFork) = nullptr;
262 		_G(roominst) = nullptr;
263 	} else _G(croom)->tsdatasize = 0;
264 	memset(&_GP(play).walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
265 	_GP(play).bg_frame = 0;
266 	_GP(play).bg_frame_locked = 0;
267 	remove_screen_overlay(-1);
268 	delete _G(raw_saved_screen);
269 	_G(raw_saved_screen) = nullptr;
270 	for (ff = 0; ff < MAX_ROOM_BGFRAMES; ff++)
271 		_GP(play).raw_modified[ff] = 0;
272 	for (size_t i = 0; i < _GP(thisroom).LocalVariables.size() && i < MAX_GLOBAL_VARIABLES; ++i)
273 		_G(croom)->interactionVariableValues[i] = _GP(thisroom).LocalVariables[i].Value;
274 
275 	// wipe the character cache when we change rooms
276 	for (ff = 0; ff < _GP(game).numcharacters; ff++) {
277 		if (_G(charcache)[ff].inUse) {
278 			delete _G(charcache)[ff].image;
279 			_G(charcache)[ff].image = nullptr;
280 			_G(charcache)[ff].inUse = 0;
281 		}
282 		// ensure that any half-moves (eg. with scaled movement) are stopped
283 		_G(charextra)[ff].xwas = INVALID_X;
284 	}
285 
286 	_GP(play).swap_portrait_lastchar = -1;
287 	_GP(play).swap_portrait_lastlastchar = -1;
288 
289 	for (ff = 0; ff < _G(croom)->numobj; ff++) {
290 		// un-export the object's script object
291 		if (_G(objectScriptObjNames)[ff].IsEmpty())
292 			continue;
293 
294 		ccRemoveExternalSymbol(_G(objectScriptObjNames)[ff]);
295 	}
296 
297 	for (ff = 0; ff < MAX_ROOM_HOTSPOTS; ff++) {
298 		if (_GP(thisroom).Hotspots[ff].ScriptName.IsEmpty())
299 			continue;
300 
301 		ccRemoveExternalSymbol(_GP(thisroom).Hotspots[ff].ScriptName);
302 	}
303 
304 	croom_ptr_clear();
305 
306 	// clear the object cache
307 	for (ff = 0; ff < MAX_ROOM_OBJECTS; ff++) {
308 		delete _G(objcache)[ff].image;
309 		_G(objcache)[ff].image = nullptr;
310 	}
311 	// clear the _G(actsps) buffers to save memory, since the
312 	// objects/characters involved probably aren't on the
313 	// new screen. this also ensures all cached data is flushed
314 	for (ff = 0; ff < MAX_ROOM_OBJECTS + _GP(game).numcharacters; ff++) {
315 		delete _G(actsps)[ff];
316 		_G(actsps)[ff] = nullptr;
317 
318 		if (_G(actspsbmp)[ff] != nullptr)
319 			_G(gfxDriver)->DestroyDDB(_G(actspsbmp)[ff]);
320 		_G(actspsbmp)[ff] = nullptr;
321 
322 		delete _G(actspswb)[ff];
323 		_G(actspswb)[ff] = nullptr;
324 
325 		if (_G(actspswbbmp)[ff] != nullptr)
326 			_G(gfxDriver)->DestroyDDB(_G(actspswbbmp)[ff]);
327 		_G(actspswbbmp)[ff] = nullptr;
328 
329 		_G(actspswbcache)[ff].valid = 0;
330 	}
331 
332 	// if Hide Player Character was ticked, restore it to visible
333 	if (_GP(play).temporarily_turned_off_character >= 0) {
334 		_GP(game).chars[_GP(play).temporarily_turned_off_character].on = 1;
335 		_GP(play).temporarily_turned_off_character = -1;
336 	}
337 
338 }
339 
340 // Convert all room objects to the data resolution (only if it's different from game resolution).
341 // TODO: merge this into UpdateRoomData? or this is required for engine only?
convert_room_coordinates_to_data_res(RoomStruct * rstruc)342 void convert_room_coordinates_to_data_res(RoomStruct *rstruc) {
343 	if (_GP(game).GetDataUpscaleMult() == 1)
344 		return;
345 
346 	const int mul = _GP(game).GetDataUpscaleMult();
347 	for (size_t i = 0; i < rstruc->ObjectCount; ++i) {
348 		rstruc->Objects[i].X /= mul;
349 		rstruc->Objects[i].Y /= mul;
350 		if (rstruc->Objects[i].Baseline > 0) {
351 			rstruc->Objects[i].Baseline /= mul;
352 		}
353 	}
354 
355 	for (size_t i = 0; i < rstruc->HotspotCount; ++i) {
356 		rstruc->Hotspots[i].WalkTo.X /= mul;
357 		rstruc->Hotspots[i].WalkTo.Y /= mul;
358 	}
359 
360 	for (size_t i = 0; i < rstruc->WalkBehindCount; ++i) {
361 		rstruc->WalkBehinds[i].Baseline /= mul;
362 	}
363 
364 	rstruc->Edges.Left /= mul;
365 	rstruc->Edges.Top /= mul;
366 	rstruc->Edges.Bottom /= mul;
367 	rstruc->Edges.Right /= mul;
368 	rstruc->Width /= mul;
369 	rstruc->Height /= mul;
370 }
371 
372 
373 
update_letterbox_mode()374 void update_letterbox_mode() {
375 	const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
376 	const Rect game_frame = RectWH(_GP(game).GetGameRes());
377 	Rect new_main_view = game_frame;
378 	// In the original engine the letterbox feature only allowed viewports of
379 	// either 200 or 240 (400 and 480) pixels, if the room height was equal or greater than 200 (400).
380 	// Also, the UI viewport should be matching room viewport in that case.
381 	// NOTE: if "OPT_LETTERBOX" is false, altsize.Height = size.Height always.
382 	const int viewport_height =
383 	    real_room_sz.Height < _GP(game).GetLetterboxSize().Height ? real_room_sz.Height :
384 	    (real_room_sz.Height >= _GP(game).GetLetterboxSize().Height && real_room_sz.Height < _GP(game).GetGameRes().Height) ? _GP(game).GetLetterboxSize().Height :
385 	    _GP(game).GetGameRes().Height;
386 	new_main_view.SetHeight(viewport_height);
387 
388 	_GP(play).SetMainViewport(CenterInRect(game_frame, new_main_view));
389 	_GP(play).SetUIViewport(new_main_view);
390 	on_mainviewport_changed();
391 }
392 
393 // Automatically reset primary room viewport and camera to match the new room size
adjust_viewport_to_room()394 static void adjust_viewport_to_room() {
395 	const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
396 	const Rect main_view = _GP(play).GetMainViewport();
397 	Rect new_room_view = RectWH(Size::Clamp(real_room_sz, Size(1, 1), main_view.GetSize()));
398 
399 	auto view = _GP(play).GetRoomViewport(0);
400 	view->SetRect(new_room_view);
401 	auto cam = view->GetCamera();
402 	if (cam) {
403 		cam->SetSize(new_room_view.GetSize());
404 		cam->SetAt(0, 0);
405 		cam->Release();
406 	}
407 }
408 
409 // Run through all viewports and cameras to make sure they can work in new room's bounds
update_all_viewcams_with_newroom()410 static void update_all_viewcams_with_newroom() {
411 	for (int i = 0; i < _GP(play).GetRoomCameraCount(); ++i) {
412 		auto cam = _GP(play).GetRoomCamera(i);
413 		const Rect old_pos = cam->GetRect();
414 		cam->SetSize(old_pos.GetSize());
415 		cam->SetAt(old_pos.Left, old_pos.Top);
416 	}
417 }
418 
419 // Looks up for the room script available as a separate asset.
420 // This is optional, so no error is raised if one is not found.
421 // If found however, it will replace room script if one had been loaded
422 // from the room file itself.
LoadRoomScript(RoomStruct * room,int newnum)423 HError LoadRoomScript(RoomStruct *room, int newnum) {
424 	String filename = String::FromFormat("room%d.o", newnum);
425 	std::unique_ptr<Stream> in(_GP(AssetMgr)->OpenAsset(filename));
426 	if (in) {
427 		PScript script(ccScript::CreateFromStream(in.get()));
428 		if (!script)
429 			return new Error(String::FromFormat(
430 				"Failed to load a script module: %s", filename.GetCStr()),
431 				_G(ccErrorString));
432 		room->CompiledScript = script;
433 	}
434 	return HError::None();
435 }
436 
437 // forchar = playerchar on NewRoom, or NULL if restore saved game
load_new_room(int newnum,CharacterInfo * forchar)438 void load_new_room(int newnum, CharacterInfo *forchar) {
439 
440 	debug_script_log("Loading room %d", newnum);
441 
442 	String room_filename;
443 	int cc;
444 	_G(done_es_error) = 0;
445 	_GP(play).room_changes ++;
446 	// TODO: find out why do we need to temporarily lower color depth to 8-bit.
447 	// Or do we? There's a serious usability problem in this: if any bitmap is
448 	// created meanwhile it will have this color depth by default, which may
449 	// lead to unexpected errors.
450 	set_color_depth(8);
451 	_G(displayed_room) = newnum;
452 
453 	room_filename.Format("room%d.crm", newnum);
454 	if (newnum == 0) {
455 		// support both room0.crm and intro.crm
456 		// 2.70: Renamed intro.crm to room0.crm, to stop it causing confusion
457 		if ((_G(loaded_game_file_version) < kGameVersion_270 && _GP(AssetMgr)->DoesAssetExist("intro.crm")) ||
458 		        (_G(loaded_game_file_version) >= kGameVersion_270 && !_GP(AssetMgr)->DoesAssetExist(room_filename))) {
459 			room_filename = "intro.crm";
460 		}
461 	}
462 
463 	update_polled_stuff_if_runtime();
464 
465 	// load the room from disk
466 	_G(our_eip) = 200;
467 	_GP(thisroom).GameID = NO_GAME_ID_IN_ROOM_FILE;
468 	load_room(room_filename, &_GP(thisroom), _GP(game).IsLegacyHiRes(), _GP(game).SpriteInfos);
469 
470 	if ((_GP(thisroom).GameID != NO_GAME_ID_IN_ROOM_FILE) &&
471 	        (_GP(thisroom).GameID != _GP(game).uniqueid)) {
472 		quitprintf("!Unable to load '%s'. This room file is assigned to a different _GP(game).", room_filename.GetCStr());
473 	}
474 
475 	HError err = LoadRoomScript(&_GP(thisroom), newnum);
476 	if (!err)
477 		quitprintf("!Unable to load '%s'. Error: %s", room_filename.GetCStr(),
478 			err->FullMessage().GetCStr());
479 
480 	convert_room_coordinates_to_data_res(&_GP(thisroom));
481 
482 	update_polled_stuff_if_runtime();
483 	_G(our_eip) = 201;
484 	/*  // apparently, doing this stops volume spiking between tracks
485 	if (_GP(thisroom).Options.StartupMusic>0) {
486 	stopmusic();
487 	delay(100);
488 	}*/
489 
490 	_GP(play).room_width = _GP(thisroom).Width;
491 	_GP(play).room_height = _GP(thisroom).Height;
492 	_GP(play).anim_background_speed = _GP(thisroom).BgAnimSpeed;
493 	_GP(play).bg_anim_delay = _GP(play).anim_background_speed;
494 
495 	// do the palette
496 	for (cc = 0; cc < 256; cc++) {
497 		if (_GP(game).paluses[cc] == PAL_BACKGROUND)
498 			_G(palette)[cc] = _GP(thisroom).Palette[cc];
499 		else {
500 			// copy the gamewide colours into the room palette
501 			for (size_t i = 0; i < _GP(thisroom).BgFrameCount; ++i)
502 				_GP(thisroom).BgFrames[i].Palette[cc] = _G(palette)[cc];
503 		}
504 	}
505 
506 	for (size_t i = 0; i < _GP(thisroom).BgFrameCount; ++i) {
507 		update_polled_stuff_if_runtime();
508 		_GP(thisroom).BgFrames[i].Graphic = PrepareSpriteForUse(_GP(thisroom).BgFrames[i].Graphic, false);
509 	}
510 
511 	update_polled_stuff_if_runtime();
512 
513 	_G(our_eip) = 202;
514 	// Update game viewports
515 	if (_GP(game).IsLegacyLetterbox())
516 		update_letterbox_mode();
517 	SetMouseBounds(0, 0, 0, 0);
518 
519 	_G(our_eip) = 203;
520 	_G(in_new_room) = 1;
521 
522 	set_color_depth(_GP(game).GetColorDepth());
523 	// Make sure the room gfx and masks are matching game's native res
524 	convert_room_background_to_game_res();
525 
526 	// walkable_areas_temp is used by the pathfinder to generate a
527 	// copy of the walkable areas - allocate it here to save time later
528 	delete _G(walkable_areas_temp);
529 	_G(walkable_areas_temp) = BitmapHelper::CreateBitmap(_GP(thisroom).WalkAreaMask->GetWidth(), _GP(thisroom).WalkAreaMask->GetHeight(), 8);
530 
531 	// Make a backup copy of the walkable areas prior to
532 	// any RemoveWalkableArea commands
533 	delete _G(walkareabackup);
534 	// copy the walls screen
535 	_G(walkareabackup) = BitmapHelper::CreateBitmapCopy(_GP(thisroom).WalkAreaMask.get());
536 
537 	_G(our_eip) = 204;
538 	update_polled_stuff_if_runtime();
539 	redo_walkable_areas();
540 	update_polled_stuff_if_runtime();
541 	recache_walk_behinds();
542 	update_polled_stuff_if_runtime();
543 
544 	_G(our_eip) = 205;
545 	// setup objects
546 	if (forchar != nullptr) {
547 		// if not restoring a game, always reset this room
548 		_GP(troom).beenhere = 0;
549 		_GP(troom).FreeScriptData();
550 		_GP(troom).FreeProperties();
551 		memset(&_GP(troom).hotspot_enabled[0], 1, MAX_ROOM_HOTSPOTS);
552 		memset(&_GP(troom).region_enabled[0], 1, MAX_ROOM_REGIONS);
553 	}
554 	if ((newnum >= 0) & (newnum < MAX_ROOMS))
555 		_G(croom) = getRoomStatus(newnum);
556 	else _G(croom) = &_GP(troom);
557 
558 	if (_G(croom)->beenhere > 0) {
559 		// if we've been here before, save the Times Run information
560 		// since we will overwrite the actual NewInteraction structs
561 		// (cos they have pointers and this might have been loaded from
562 		// a save game)
563 		if (_GP(thisroom).EventHandlers == nullptr) {
564 			// legacy interactions
565 			_GP(thisroom).Interaction->CopyTimesRun(_G(croom)->intrRoom);
566 			for (cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
567 				_GP(thisroom).Hotspots[cc].Interaction->CopyTimesRun(_G(croom)->intrHotspot[cc]);
568 			for (cc = 0; cc < MAX_ROOM_OBJECTS; cc++)
569 				_GP(thisroom).Objects[cc].Interaction->CopyTimesRun(_G(croom)->intrObject[cc]);
570 			for (cc = 0; cc < MAX_ROOM_REGIONS; cc++)
571 				_GP(thisroom).Regions[cc].Interaction->CopyTimesRun(_G(croom)->intrRegion[cc]);
572 		}
573 	}
574 	if (_G(croom)->beenhere == 0) {
575 		_G(croom)->numobj = _GP(thisroom).ObjectCount;
576 		_G(croom)->tsdatasize = 0;
577 		for (cc = 0; cc < _G(croom)->numobj; cc++) {
578 			_G(croom)->obj[cc].x = _GP(thisroom).Objects[cc].X;
579 			_G(croom)->obj[cc].y = _GP(thisroom).Objects[cc].Y;
580 			_G(croom)->obj[cc].num = Math::InRangeOrDef<uint16_t>(_GP(thisroom).Objects[cc].Sprite, 0);
581 			_G(croom)->obj[cc].on = _GP(thisroom).Objects[cc].IsOn;
582 			_G(croom)->obj[cc].view = (uint16) - 1;
583 			_G(croom)->obj[cc].loop = 0;
584 			_G(croom)->obj[cc].frame = 0;
585 			_G(croom)->obj[cc].wait = 0;
586 			_G(croom)->obj[cc].transparent = 0;
587 			_G(croom)->obj[cc].moving = -1;
588 			_G(croom)->obj[cc].flags = _GP(thisroom).Objects[cc].Flags;
589 			_G(croom)->obj[cc].baseline = -1;
590 			_G(croom)->obj[cc].zoom = 100;
591 			_G(croom)->obj[cc].last_width = 0;
592 			_G(croom)->obj[cc].last_height = 0;
593 			_G(croom)->obj[cc].blocking_width = 0;
594 			_G(croom)->obj[cc].blocking_height = 0;
595 			if (_GP(thisroom).Objects[cc].Baseline >= 0)
596 				_G(croom)->obj[cc].baseline = _GP(thisroom).Objects[cc].Baseline;
597 			if (_GP(thisroom).Objects[cc].Sprite > UINT16_MAX)
598 				debug_script_warn("Warning: object's (id %d) sprite %d outside of internal range (%d), reset to 0",
599 				                  cc, _GP(thisroom).Objects[cc].Sprite, UINT16_MAX);
600 		}
601 		for (size_t i = 0; i < (size_t)MAX_WALK_BEHINDS; ++i)
602 			_G(croom)->walkbehind_base[i] = _GP(thisroom).WalkBehinds[i].Baseline;
603 		for (cc = 0; cc < MAX_FLAGS; cc++) _G(croom)->flagstates[cc] = 0;
604 
605 		/*    // we copy these structs for the Score column to work
606 		_G(croom)->misccond=_GP(thisroom).misccond;
607 		for (cc=0;cc<MAX_ROOM_HOTSPOTS;cc++)
608 		_G(croom)->hscond[cc]=_GP(thisroom).hscond[cc];
609 		for (cc=0;cc<MAX_ROOM_OBJECTS;cc++)
610 		_G(croom)->objcond[cc]=_GP(thisroom).objcond[cc];*/
611 
612 		for (cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++) {
613 			_G(croom)->hotspot_enabled[cc] = 1;
614 		}
615 		for (cc = 0; cc < MAX_ROOM_REGIONS; cc++) {
616 			_G(croom)->region_enabled[cc] = 1;
617 		}
618 
619 		_G(croom)->beenhere = 1;
620 		_G(in_new_room) = 2;
621 	} else {
622 		// We have been here before
623 		for (size_t i = 0; i < _GP(thisroom).LocalVariables.size() && i < (size_t)MAX_GLOBAL_VARIABLES; ++i)
624 			_GP(thisroom).LocalVariables[i].Value = _G(croom)->interactionVariableValues[i];
625 	}
626 
627 	update_polled_stuff_if_runtime();
628 
629 	if (_GP(thisroom).EventHandlers == nullptr) {
630 		// legacy interactions
631 		// copy interactions from room file into our temporary struct
632 		_G(croom)->intrRoom = *_GP(thisroom).Interaction;
633 		for (cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
634 			_G(croom)->intrHotspot[cc] = *_GP(thisroom).Hotspots[cc].Interaction;
635 		for (cc = 0; cc < MAX_ROOM_OBJECTS; cc++)
636 			_G(croom)->intrObject[cc] = *_GP(thisroom).Objects[cc].Interaction;
637 		for (cc = 0; cc < MAX_ROOM_REGIONS; cc++)
638 			_G(croom)->intrRegion[cc] = *_GP(thisroom).Regions[cc].Interaction;
639 	}
640 
641 	_G(objs) = &_G(croom)->obj[0];
642 
643 	for (cc = 0; cc < MAX_ROOM_OBJECTS; cc++) {
644 		// 64 bit: Using the id instead
645 		// _G(scrObj)[cc].obj = &_G(croom)->obj[cc];
646 		_G(objectScriptObjNames)[cc].Free();
647 	}
648 
649 	for (cc = 0; cc < _G(croom)->numobj; cc++) {
650 		// export the object's script object
651 		if (_GP(thisroom).Objects[cc].ScriptName.IsEmpty())
652 			continue;
653 		_G(objectScriptObjNames)[cc] = _GP(thisroom).Objects[cc].ScriptName;
654 		ccAddExternalDynamicObject(_G(objectScriptObjNames)[cc], &_G(scrObj)[cc], &_GP(ccDynamicObject));
655 	}
656 
657 	for (cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++) {
658 		if (_GP(thisroom).Hotspots[cc].ScriptName.IsEmpty())
659 			continue;
660 
661 		ccAddExternalDynamicObject(_GP(thisroom).Hotspots[cc].ScriptName, &_G(scrHotspot)[cc], &_GP(ccDynamicHotspot));
662 	}
663 
664 	_G(our_eip) = 206;
665 	/*  THIS IS DONE IN THE EDITOR NOW
666 	_GP(thisroom).BgFrames.IsPaletteShared[0] = 1;
667 	for (dd = 1; dd < _GP(thisroom).BgFrameCount; dd++) {
668 	if (memcmp (&_GP(thisroom).BgFrames.Palette[dd][0], &_G(palette)[0], sizeof(color) * 256) == 0)
669 	_GP(thisroom).BgFrames.IsPaletteShared[dd] = 1;
670 	else
671 	_GP(thisroom).BgFrames.IsPaletteShared[dd] = 0;
672 	}
673 	// only make the first frame shared if the last is
674 	if (_GP(thisroom).BgFrames.IsPaletteShared[_GP(thisroom).BgFrameCount - 1] == 0)
675 	_GP(thisroom).BgFrames.IsPaletteShared[0] = 0;*/
676 
677 	update_polled_stuff_if_runtime();
678 
679 	_G(our_eip) = 210;
680 	if (IS_ANTIALIAS_SPRITES) {
681 		// sometimes the palette has corrupt entries, which crash
682 		// the create_rgb_table call
683 		// so, fix them
684 		for (int ff = 0; ff < 256; ff++) {
685 			if (_G(palette)[ff].r > 63)
686 				_G(palette)[ff].r = 63;
687 			if (_G(palette)[ff].g > 63)
688 				_G(palette)[ff].g = 63;
689 			if (_G(palette)[ff].b > 63)
690 				_G(palette)[ff].b = 63;
691 		}
692 		create_rgb_table(&_GP(rgb_table), _G(palette), nullptr);
693 		_G(rgb_map) = &_GP(rgb_table);
694 	}
695 	_G(our_eip) = 211;
696 	if (forchar != nullptr) {
697 		// if it's not a Restore Game
698 
699 		// if a following character is still waiting to come into the
700 		// previous room, force it out so that the timer resets
701 		for (int ff = 0; ff < _GP(game).numcharacters; ff++) {
702 			if ((_GP(game).chars[ff].following >= 0) && (_GP(game).chars[ff].room < 0)) {
703 				if ((_GP(game).chars[ff].following == _GP(game).playercharacter) &&
704 				        (forchar->prevroom == newnum))
705 					// the player went back to the previous room, so make sure
706 					// the following character is still there
707 					_GP(game).chars[ff].room = newnum;
708 				else
709 					_GP(game).chars[ff].room = _GP(game).chars[_GP(game).chars[ff].following].room;
710 			}
711 		}
712 
713 		forchar->prevroom = forchar->room;
714 		forchar->room = newnum;
715 
716 		// only stop moving if it's a new room, not a restore game
717 		for (cc = 0; cc < _GP(game).numcharacters; cc++)
718 			StopMoving(cc);
719 	}
720 
721 	update_polled_stuff_if_runtime();
722 
723 	_G(roominst) = nullptr;
724 	if (_G(debug_flags) & DBG_NOSCRIPT) ;
725 	else if (_GP(thisroom).CompiledScript != nullptr) {
726 		compile_room_script();
727 		if (_G(croom)->tsdatasize > 0) {
728 			if (_G(croom)->tsdatasize != _G(roominst)->globaldatasize)
729 				quit("room script data segment size has changed");
730 			memcpy(&_G(roominst)->globaldata[0], _G(croom)->tsdata, _G(croom)->tsdatasize);
731 		}
732 	}
733 	_G(our_eip) = 207;
734 	_GP(play).entered_edge = -1;
735 
736 	if ((_G(new_room_x) != SCR_NO_VALUE) && (forchar != nullptr)) {
737 		forchar->x = _G(new_room_x);
738 		forchar->y = _G(new_room_y);
739 		if (_G(new_room_placeonwalkable))
740 			Character_PlaceOnWalkableArea(forchar);
741 
742 		if (_G(new_room_loop) != SCR_NO_VALUE)
743 			forchar->loop = _G(new_room_loop);
744 	}
745 
746 	// reset new_room instructions
747 	_G(new_room_x) = _G(new_room_y) = SCR_NO_VALUE;
748 	_G(new_room_loop) = SCR_NO_VALUE;
749 	_G(new_room_placeonwalkable) = false;
750 
751 	if ((_G(new_room_pos) > 0) & (forchar != nullptr)) {
752 		if (_G(new_room_pos) >= 4000) {
753 			_GP(play).entered_edge = 3;
754 			forchar->y = _GP(thisroom).Edges.Top + get_fixed_pixel_size(1);
755 			forchar->x = _G(new_room_pos) % 1000;
756 			if (forchar->x == 0) forchar->x = _GP(thisroom).Width / 2;
757 			if (forchar->x <= _GP(thisroom).Edges.Left)
758 				forchar->x = _GP(thisroom).Edges.Left + 3;
759 			if (forchar->x >= _GP(thisroom).Edges.Right)
760 				forchar->x = _GP(thisroom).Edges.Right - 3;
761 			forchar->loop = 0;
762 		} else if (_G(new_room_pos) >= 3000) {
763 			_GP(play).entered_edge = 2;
764 			forchar->y = _GP(thisroom).Edges.Bottom - get_fixed_pixel_size(1);
765 			forchar->x = _G(new_room_pos) % 1000;
766 			if (forchar->x == 0) forchar->x = _GP(thisroom).Width / 2;
767 			if (forchar->x <= _GP(thisroom).Edges.Left)
768 				forchar->x = _GP(thisroom).Edges.Left + 3;
769 			if (forchar->x >= _GP(thisroom).Edges.Right)
770 				forchar->x = _GP(thisroom).Edges.Right - 3;
771 			forchar->loop = 3;
772 		} else if (_G(new_room_pos) >= 2000) {
773 			_GP(play).entered_edge = 1;
774 			forchar->x = _GP(thisroom).Edges.Right - get_fixed_pixel_size(1);
775 			forchar->y = _G(new_room_pos) % 1000;
776 			if (forchar->y == 0) forchar->y = _GP(thisroom).Height / 2;
777 			if (forchar->y <= _GP(thisroom).Edges.Top)
778 				forchar->y = _GP(thisroom).Edges.Top + 3;
779 			if (forchar->y >= _GP(thisroom).Edges.Bottom)
780 				forchar->y = _GP(thisroom).Edges.Bottom - 3;
781 			forchar->loop = 1;
782 		} else if (_G(new_room_pos) >= 1000) {
783 			_GP(play).entered_edge = 0;
784 			forchar->x = _GP(thisroom).Edges.Left + get_fixed_pixel_size(1);
785 			forchar->y = _G(new_room_pos) % 1000;
786 			if (forchar->y == 0) forchar->y = _GP(thisroom).Height / 2;
787 			if (forchar->y <= _GP(thisroom).Edges.Top)
788 				forchar->y = _GP(thisroom).Edges.Top + 3;
789 			if (forchar->y >= _GP(thisroom).Edges.Bottom)
790 				forchar->y = _GP(thisroom).Edges.Bottom - 3;
791 			forchar->loop = 2;
792 		}
793 		// if starts on un-walkable area
794 		if (get_walkable_area_pixel(forchar->x, forchar->y) == 0) {
795 			if (_G(new_room_pos) >= 3000) { // bottom or top of screen
796 				int tryleft = forchar->x - 1, tryright = forchar->x + 1;
797 				while (1) {
798 					if (get_walkable_area_pixel(tryleft, forchar->y) > 0) {
799 						forchar->x = tryleft;
800 						break;
801 					}
802 					if (get_walkable_area_pixel(tryright, forchar->y) > 0) {
803 						forchar->x = tryright;
804 						break;
805 					}
806 					int nowhere = 0;
807 					if (tryleft > _GP(thisroom).Edges.Left) {
808 						tryleft--;
809 						nowhere++;
810 					}
811 					if (tryright < _GP(thisroom).Edges.Right) {
812 						tryright++;
813 						nowhere++;
814 					}
815 					if (nowhere == 0) break; // no place to go, so leave him
816 				}
817 			} else if (_G(new_room_pos) >= 1000) { // left or right
818 				int tryleft = forchar->y - 1, tryright = forchar->y + 1;
819 				while (1) {
820 					if (get_walkable_area_pixel(forchar->x, tryleft) > 0) {
821 						forchar->y = tryleft;
822 						break;
823 					}
824 					if (get_walkable_area_pixel(forchar->x, tryright) > 0) {
825 						forchar->y = tryright;
826 						break;
827 					}
828 					int nowhere = 0;
829 					if (tryleft > _GP(thisroom).Edges.Top) {
830 						tryleft--;
831 						nowhere++;
832 					}
833 					if (tryright < _GP(thisroom).Edges.Bottom) {
834 						tryright++;
835 						nowhere++;
836 					}
837 					if (nowhere == 0) break; // no place to go, so leave him
838 				}
839 			}
840 		}
841 		_G(new_room_pos) = 0;
842 	}
843 	if (forchar != nullptr) {
844 		_GP(play).entered_at_x = forchar->x;
845 		_GP(play).entered_at_y = forchar->y;
846 		if (forchar->x >= _GP(thisroom).Edges.Right)
847 			_GP(play).entered_edge = 1;
848 		else if (forchar->x <= _GP(thisroom).Edges.Left)
849 			_GP(play).entered_edge = 0;
850 		else if (forchar->y >= _GP(thisroom).Edges.Bottom)
851 			_GP(play).entered_edge = 2;
852 		else if (forchar->y <= _GP(thisroom).Edges.Top)
853 			_GP(play).entered_edge = 3;
854 	}
855 	if (_GP(thisroom).Options.StartupMusic > 0)
856 		PlayMusicResetQueue(_GP(thisroom).Options.StartupMusic);
857 
858 	_G(our_eip) = 208;
859 	if (forchar != nullptr) {
860 		if (_GP(thisroom).Options.PlayerCharOff == 0) {
861 			forchar->on = 1;
862 			enable_cursor_mode(0);
863 		} else {
864 			forchar->on = 0;
865 			disable_cursor_mode(0);
866 			// remember which character we turned off, in case they
867 			// use SetPlyaerChracter within this room (so we re-enable
868 			// the correct character when leaving the room)
869 			_GP(play).temporarily_turned_off_character = _GP(game).playercharacter;
870 		}
871 		if (forchar->flags & CHF_FIXVIEW) ;
872 		else if (_GP(thisroom).Options.PlayerView == 0) forchar->view = forchar->defview;
873 		else forchar->view = _GP(thisroom).Options.PlayerView - 1;
874 		forchar->frame = 0; // make him standing
875 	}
876 	_G(color_map) = nullptr;
877 
878 	_G(our_eip) = 209;
879 	update_polled_stuff_if_runtime();
880 	generate_light_table();
881 	update_music_volume();
882 
883 	// If we are not restoring a save, update cameras to accomodate for this
884 	// new room; otherwise this is done later when cameras are recreated.
885 	if (forchar != nullptr) {
886 		if (_GP(play).IsAutoRoomViewport())
887 			adjust_viewport_to_room();
888 		update_all_viewcams_with_newroom();
889 		_GP(play).UpdateRoomCameras(); // update auto tracking
890 	}
891 	init_room_drawdata();
892 
893 	_G(our_eip) = 212;
894 	invalidate_screen();
895 	for (cc = 0; cc < _G(croom)->numobj; cc++) {
896 		if (_G(objs)[cc].on == 2)
897 			MergeObject(cc);
898 	}
899 	_G(new_room_flags) = 0;
900 	_GP(play).gscript_timer = -1; // avoid screw-ups with changing screens
901 	_GP(play).player_on_region = 0;
902 	// trash any input which they might have done while it was loading
903 	ags_clear_input_buffer();
904 	// no fade in, so set the palette immediately in case of 256-col sprites
905 	if (_GP(game).color_depth > 1)
906 		setpal();
907 
908 	_G(our_eip) = 220;
909 	update_polled_stuff_if_runtime();
910 	debug_script_log("Now in room %d", _G(displayed_room));
911 	GUI::MarkAllGUIForUpdate();
912 	pl_run_plugin_hooks(AGSE_ENTERROOM, _G(displayed_room));
913 	//  MoveToWalkableArea(_GP(game).playercharacter);
914 	//  MSS_CHECK_ALL_BLOCKS;
915 }
916 
917 // new_room: changes the current room number, and loads the new room from disk
new_room(int newnum,CharacterInfo * forchar)918 void new_room(int newnum, CharacterInfo *forchar) {
919 	EndSkippingUntilCharStops();
920 
921 	debug_script_log("Room change requested to room %d", newnum);
922 
923 	update_polled_stuff_if_runtime();
924 
925 	// we are currently running Leaves Screen scripts
926 	_G(in_leaves_screen) = newnum;
927 
928 	// player leaves screen event
929 	run_room_event(8);
930 	// Run the global OnRoomLeave event
931 	run_on_event(GE_LEAVE_ROOM, RuntimeScriptValue().SetInt32(_G(displayed_room)));
932 
933 	pl_run_plugin_hooks(AGSE_LEAVEROOM, _G(displayed_room));
934 
935 	// update the new room number if it has been altered by OnLeave scripts
936 	newnum = _G(in_leaves_screen);
937 	_G(in_leaves_screen) = -1;
938 
939 	if ((_G(playerchar)->following >= 0) &&
940 	        (_GP(game).chars[_G(playerchar)->following].room != newnum)) {
941 		// the player character is following another character,
942 		// who is not in the new room. therefore, abort the follow
943 		_G(playerchar)->following = -1;
944 	}
945 	update_polled_stuff_if_runtime();
946 
947 	// change rooms
948 	unload_old_room();
949 
950 	if (_G(psp_clear_cache_on_room_change)) {
951 		// Delete all cached sprites
952 		_GP(spriteset).DisposeAll();
953 
954 		// Delete all gui background images
955 		for (int i = 0; i < _GP(game).numgui; i++) {
956 			delete _G(guibg)[i];
957 			_G(guibg)[i] = nullptr;
958 
959 			if (_G(guibgbmp)[i])
960 				_G(gfxDriver)->DestroyDDB(_G(guibgbmp)[i]);
961 			_G(guibgbmp)[i] = nullptr;
962 		}
963 		GUI::MarkAllGUIForUpdate();
964 	}
965 
966 	update_polled_stuff_if_runtime();
967 
968 	load_new_room(newnum, forchar);
969 }
970 
find_highest_room_entered()971 int find_highest_room_entered() {
972 	int qq, fndas = -1;
973 	for (qq = 0; qq < MAX_ROOMS; qq++) {
974 		if (isRoomStatusValid(qq) && (getRoomStatus(qq)->beenhere != 0))
975 			fndas = qq;
976 	}
977 	// This is actually legal - they might start in room 400 and save
978 	//if (fndas<0) quit("find_highest_room: been in no rooms?");
979 	return fndas;
980 }
981 
first_room_initialization()982 void first_room_initialization() {
983 	_G(starting_room) = _G(displayed_room);
984 	set_loop_counter(0);
985 	_G(mouse_z_was) = _G(sys_mouse_z);
986 }
987 
check_new_room()988 void check_new_room() {
989 	// if they're in a new room, run Player Enters Screen and on_event(ENTER_ROOM)
990 	if ((_G(in_new_room) > 0) & (_G(in_new_room) != 3)) {
991 		EventHappened evh;
992 		evh.type = EV_RUNEVBLOCK;
993 		evh.data1 = EVB_ROOM;
994 		evh.data2 = 0;
995 		evh.data3 = 5;
996 		evh.player = _GP(game).playercharacter;
997 		// make sure that any script calls don't re-call enters screen
998 		int newroom_was = _G(in_new_room);
999 		_G(in_new_room) = 0;
1000 		_GP(play).disabled_user_interface ++;
1001 		process_event(&evh);
1002 		_GP(play).disabled_user_interface --;
1003 		_G(in_new_room) = newroom_was;
1004 		//    setevent(EV_RUNEVBLOCK,EVB_ROOM,0,5);
1005 	}
1006 }
1007 
compile_room_script()1008 void compile_room_script() {
1009 	_G(ccError) = 0;
1010 
1011 	_G(roominst) = ccInstance::CreateFromScript(_GP(thisroom).CompiledScript);
1012 
1013 	if ((_G(ccError) != 0) || (_G(roominst) == nullptr)) {
1014 		quitprintf("Unable to create local script: %s", _G(ccErrorString).GetCStr());
1015 	}
1016 
1017 	_G(roominstFork) = _G(roominst)->Fork();
1018 	if (_G(roominstFork) == nullptr)
1019 		quitprintf("Unable to create forked room instance: %s", _G(ccErrorString).GetCStr());
1020 
1021 	_GP(repExecAlways).roomHasFunction = true;
1022 	_GP(lateRepExecAlways).roomHasFunction = true;
1023 	_GP(getDialogOptionsDimensionsFunc).roomHasFunction = true;
1024 }
1025 
on_background_frame_change()1026 void on_background_frame_change() {
1027 
1028 	invalidate_screen();
1029 	mark_current_background_dirty();
1030 	invalidate_cached_walkbehinds();
1031 
1032 	// get the new frame's palette
1033 	memcpy(_G(palette), _GP(thisroom).BgFrames[_GP(play).bg_frame].Palette, sizeof(RGB) * 256);
1034 
1035 	// hi-colour, update the palette. It won't have an immediate effect
1036 	// but will be drawn properly when the screen fades in
1037 	if (_GP(game).color_depth > 1)
1038 		setpal();
1039 
1040 	if (_G(in_enters_screen))
1041 		return;
1042 
1043 	// Don't update the palette if it hasn't changed
1044 	if (_GP(thisroom).BgFrames[_GP(play).bg_frame].IsPaletteShared)
1045 		return;
1046 
1047 	// 256-colours, tell it to update the palette (will actually be done as
1048 	// close as possible to the screen update to prevent flicker problem)
1049 	if (_GP(game).color_depth == 1)
1050 		_G(bg_just_changed) = 1;
1051 }
1052 
croom_ptr_clear()1053 void croom_ptr_clear() {
1054 	_G(croom) = nullptr;
1055 	_G(objs) = nullptr;
1056 }
1057 
1058 
room_to_mask_coord(int coord)1059 AGS_INLINE int room_to_mask_coord(int coord) {
1060 	return coord * _GP(game).GetDataUpscaleMult() / _GP(thisroom).MaskResolution;
1061 }
1062 
mask_to_room_coord(int coord)1063 AGS_INLINE int mask_to_room_coord(int coord) {
1064 	return coord * _GP(thisroom).MaskResolution / _GP(game).GetDataUpscaleMult();
1065 }
1066 
convert_move_path_to_room_resolution(MoveList * ml)1067 void convert_move_path_to_room_resolution(MoveList *ml) {
1068 	if ((_GP(game).options[OPT_WALKSPEEDABSOLUTE] != 0) && _GP(game).GetDataUpscaleMult() > 1) {
1069 		// Speeds are independent from MaskResolution
1070 		for (int i = 0; i < ml->numstage; i++) {
1071 			// ...so they are not multiplied by MaskResolution factor when converted to room coords
1072 			ml->xpermove[i] = ml->xpermove[i] / _GP(game).GetDataUpscaleMult();
1073 			ml->ypermove[i] = ml->ypermove[i] / _GP(game).GetDataUpscaleMult();
1074 		}
1075 	}
1076 
1077 	if (_GP(thisroom).MaskResolution == _GP(game).GetDataUpscaleMult())
1078 		return;
1079 
1080 	ml->fromx = mask_to_room_coord(ml->fromx);
1081 	ml->fromy = mask_to_room_coord(ml->fromy);
1082 	ml->lastx = mask_to_room_coord(ml->lastx);
1083 	ml->lasty = mask_to_room_coord(ml->lasty);
1084 
1085 	for (int i = 0; i < ml->numstage; i++) {
1086 		uint16_t lowPart = mask_to_room_coord(ml->pos[i] & 0x0000ffff);
1087 		uint16_t highPart = mask_to_room_coord((ml->pos[i] >> 16) & 0x0000ffff);
1088 		ml->pos[i] = ((int)highPart << 16) | (lowPart & 0x0000ffff);
1089 	}
1090 
1091 	if (_GP(game).options[OPT_WALKSPEEDABSOLUTE] == 0) {
1092 		// Speeds are scaling with MaskResolution
1093 		for (int i = 0; i < ml->numstage; i++) {
1094 			ml->xpermove[i] = mask_to_room_coord(ml->xpermove[i]);
1095 			ml->ypermove[i] = mask_to_room_coord(ml->ypermove[i]);
1096 		}
1097 	}
1098 }
1099 
1100 //=============================================================================
1101 //
1102 // Script API Functions
1103 //
1104 //=============================================================================
1105 
1106 // ScriptDrawingSurface* (int backgroundNumber)
Sc_Room_GetDrawingSurfaceForBackground(const RuntimeScriptValue * params,int32_t param_count)1107 RuntimeScriptValue Sc_Room_GetDrawingSurfaceForBackground(const RuntimeScriptValue *params, int32_t param_count) {
1108 	API_SCALL_OBJAUTO_PINT(ScriptDrawingSurface, Room_GetDrawingSurfaceForBackground);
1109 }
1110 
1111 // int (const char *property)
Sc_Room_GetProperty(const RuntimeScriptValue * params,int32_t param_count)1112 RuntimeScriptValue Sc_Room_GetProperty(const RuntimeScriptValue *params, int32_t param_count) {
1113 	API_SCALL_INT_POBJ(Room_GetProperty, const char);
1114 }
1115 
1116 // const char* (const char *property)
Sc_Room_GetTextProperty(const RuntimeScriptValue * params,int32_t param_count)1117 RuntimeScriptValue Sc_Room_GetTextProperty(const RuntimeScriptValue *params, int32_t param_count) {
1118 	API_CONST_SCALL_OBJ_POBJ(const char, _GP(myScriptStringImpl), Room_GetTextProperty, const char);
1119 }
1120 
Sc_Room_SetProperty(const RuntimeScriptValue * params,int32_t param_count)1121 RuntimeScriptValue Sc_Room_SetProperty(const RuntimeScriptValue *params, int32_t param_count) {
1122 	API_SCALL_BOOL_POBJ_PINT(Room_SetProperty, const char);
1123 }
1124 
1125 // const char* (const char *property)
Sc_Room_SetTextProperty(const RuntimeScriptValue * params,int32_t param_count)1126 RuntimeScriptValue Sc_Room_SetTextProperty(const RuntimeScriptValue *params, int32_t param_count) {
1127 	API_SCALL_BOOL_POBJ2(Room_SetTextProperty, const char, const char);
1128 }
1129 
1130 // int ()
Sc_Room_GetBottomEdge(const RuntimeScriptValue * params,int32_t param_count)1131 RuntimeScriptValue Sc_Room_GetBottomEdge(const RuntimeScriptValue *params, int32_t param_count) {
1132 	API_SCALL_INT(Room_GetBottomEdge);
1133 }
1134 
1135 // int ()
Sc_Room_GetColorDepth(const RuntimeScriptValue * params,int32_t param_count)1136 RuntimeScriptValue Sc_Room_GetColorDepth(const RuntimeScriptValue *params, int32_t param_count) {
1137 	API_SCALL_INT(Room_GetColorDepth);
1138 }
1139 
1140 // int ()
Sc_Room_GetHeight(const RuntimeScriptValue * params,int32_t param_count)1141 RuntimeScriptValue Sc_Room_GetHeight(const RuntimeScriptValue *params, int32_t param_count) {
1142 	API_SCALL_INT(Room_GetHeight);
1143 }
1144 
1145 // int ()
Sc_Room_GetLeftEdge(const RuntimeScriptValue * params,int32_t param_count)1146 RuntimeScriptValue Sc_Room_GetLeftEdge(const RuntimeScriptValue *params, int32_t param_count) {
1147 	API_SCALL_INT(Room_GetLeftEdge);
1148 }
1149 
1150 // const char* (int index)
Sc_Room_GetMessages(const RuntimeScriptValue * params,int32_t param_count)1151 RuntimeScriptValue Sc_Room_GetMessages(const RuntimeScriptValue *params, int32_t param_count) {
1152 	API_CONST_SCALL_OBJ_PINT(const char, _GP(myScriptStringImpl), Room_GetMessages);
1153 }
1154 
1155 // int ()
Sc_Room_GetMusicOnLoad(const RuntimeScriptValue * params,int32_t param_count)1156 RuntimeScriptValue Sc_Room_GetMusicOnLoad(const RuntimeScriptValue *params, int32_t param_count) {
1157 	API_SCALL_INT(Room_GetMusicOnLoad);
1158 }
1159 
1160 // int ()
Sc_Room_GetObjectCount(const RuntimeScriptValue * params,int32_t param_count)1161 RuntimeScriptValue Sc_Room_GetObjectCount(const RuntimeScriptValue *params, int32_t param_count) {
1162 	API_SCALL_INT(Room_GetObjectCount);
1163 }
1164 
1165 // int ()
Sc_Room_GetRightEdge(const RuntimeScriptValue * params,int32_t param_count)1166 RuntimeScriptValue Sc_Room_GetRightEdge(const RuntimeScriptValue *params, int32_t param_count) {
1167 	API_SCALL_INT(Room_GetRightEdge);
1168 }
1169 
1170 // int ()
Sc_Room_GetTopEdge(const RuntimeScriptValue * params,int32_t param_count)1171 RuntimeScriptValue Sc_Room_GetTopEdge(const RuntimeScriptValue *params, int32_t param_count) {
1172 	API_SCALL_INT(Room_GetTopEdge);
1173 }
1174 
1175 // int ()
Sc_Room_GetWidth(const RuntimeScriptValue * params,int32_t param_count)1176 RuntimeScriptValue Sc_Room_GetWidth(const RuntimeScriptValue *params, int32_t param_count) {
1177 	API_SCALL_INT(Room_GetWidth);
1178 }
1179 
1180 // void (int xx,int yy,int mood)
Sc_RoomProcessClick(const RuntimeScriptValue * params,int32_t param_count)1181 RuntimeScriptValue Sc_RoomProcessClick(const RuntimeScriptValue *params, int32_t param_count) {
1182 	API_SCALL_VOID_PINT3(RoomProcessClick);
1183 }
1184 
Sc_Room_Exists(const RuntimeScriptValue * params,int32_t param_count)1185 RuntimeScriptValue Sc_Room_Exists(const RuntimeScriptValue *params, int32_t param_count) {
1186 	API_SCALL_BOOL_PINT(Room_Exists);
1187 }
1188 
1189 
RegisterRoomAPI()1190 void RegisterRoomAPI() {
1191 	ccAddExternalStaticFunction("Room::GetDrawingSurfaceForBackground^1",   Sc_Room_GetDrawingSurfaceForBackground);
1192 	ccAddExternalStaticFunction("Room::GetProperty^1",      Sc_Room_GetProperty);
1193 	ccAddExternalStaticFunction("Room::GetTextProperty^1",  Sc_Room_GetTextProperty);
1194 	ccAddExternalStaticFunction("Room::SetProperty^2",      Sc_Room_SetProperty);
1195 	ccAddExternalStaticFunction("Room::SetTextProperty^2",  Sc_Room_SetTextProperty);
1196 	ccAddExternalStaticFunction("Room::ProcessClick^3",     Sc_RoomProcessClick);
1197 	ccAddExternalStaticFunction("ProcessClick",             Sc_RoomProcessClick);
1198 	ccAddExternalStaticFunction("Room::get_BottomEdge",     Sc_Room_GetBottomEdge);
1199 	ccAddExternalStaticFunction("Room::get_ColorDepth",     Sc_Room_GetColorDepth);
1200 	ccAddExternalStaticFunction("Room::get_Height",         Sc_Room_GetHeight);
1201 	ccAddExternalStaticFunction("Room::get_LeftEdge",       Sc_Room_GetLeftEdge);
1202 	ccAddExternalStaticFunction("Room::geti_Messages",      Sc_Room_GetMessages);
1203 	ccAddExternalStaticFunction("Room::get_MusicOnLoad",    Sc_Room_GetMusicOnLoad);
1204 	ccAddExternalStaticFunction("Room::get_ObjectCount",    Sc_Room_GetObjectCount);
1205 	ccAddExternalStaticFunction("Room::get_RightEdge",      Sc_Room_GetRightEdge);
1206 	ccAddExternalStaticFunction("Room::get_TopEdge",        Sc_Room_GetTopEdge);
1207 	ccAddExternalStaticFunction("Room::get_Width",          Sc_Room_GetWidth);
1208 	ccAddExternalStaticFunction("Room::Exists",             Sc_Room_Exists);
1209 }
1210 
1211 } // namespace AGS3
1212