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