1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006, 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27
28 #include "systems/base/graphics_system.h"
29
30 #include <boost/algorithm/string.hpp>
31 #include <boost/archive/text_iarchive.hpp>
32 #include <boost/archive/text_oarchive.hpp>
33 #include <boost/serialization/deque.hpp>
34 #include <boost/serialization/vector.hpp>
35
36 #include <algorithm>
37 #include <deque>
38 #include <iostream>
39 #include <iterator>
40 #include <list>
41 #include <sstream>
42 #include <string>
43 #include <utility>
44 #include <vector>
45
46 #include "base/notification_details.h"
47 #include "base/notification_service.h"
48 #include "base/notification_source.h"
49 #include "libreallive/expression.h"
50 #include "libreallive/gameexe.h"
51 #include "machine/rlmachine.h"
52 #include "machine/serialization.h"
53 #include "machine/stack_frame.h"
54 #include "modules/module_grp.h"
55 #include "systems/base/anm_graphics_object_data.h"
56 #include "systems/base/cgm_table.h"
57 #include "systems/base/event_system.h"
58 #include "systems/base/graphics_object.h"
59 #include "systems/base/graphics_object_data.h"
60 #include "systems/base/graphics_object_of_file.h"
61 #include "systems/base/graphics_stack_frame.h"
62 #include "systems/base/hik_renderer.h"
63 #include "systems/base/hik_script.h"
64 #include "systems/base/mouse_cursor.h"
65 #include "systems/base/object_mutator.h"
66 #include "systems/base/object_settings.h"
67 #include "systems/base/surface.h"
68 #include "systems/base/system.h"
69 #include "systems/base/system_error.h"
70 #include "systems/base/text_system.h"
71 #include "utilities/exception.h"
72 #include "utilities/lazy_array.h"
73
74 using boost::iends_with;
75 using std::cerr;
76 using std::cout;
77 using std::endl;
78 using std::fill;
79 using std::get;
80 using std::for_each;
81 using std::ostringstream;
82 using std::vector;
83
84 namespace fs = boost::filesystem;
85
86 // -----------------------------------------------------------------------
87 // GraphicsSystem::GraphicsObjectSettings
88 // -----------------------------------------------------------------------
89 // Impl object
90 struct GraphicsSystem::GraphicsObjectSettings {
91 // Number of graphical objects in a layer.
92 int objects_in_a_layer;
93
94 // Each is a valid index into data, associating each object slot with an
95 // ObjectSettings instance.
96 std::unique_ptr<unsigned char[]> position;
97
98 std::vector<ObjectSettings> data;
99
100 explicit GraphicsObjectSettings(Gameexe& gameexe);
101
102 const ObjectSettings& GetObjectSettingsFor(int obj_num);
103 };
104
105 // -----------------------------------------------------------------------
106
GraphicsObjectSettings(Gameexe & gameexe)107 GraphicsSystem::GraphicsObjectSettings::GraphicsObjectSettings(
108 Gameexe& gameexe) {
109 if (gameexe.Exists("OBJECT_MAX"))
110 objects_in_a_layer = gameexe("OBJECT_MAX");
111 else
112 objects_in_a_layer = 256;
113
114 // First we populate everything with the special value
115 position.reset(new unsigned char[objects_in_a_layer]);
116 fill(position.get(), position.get() + objects_in_a_layer, 0);
117
118 if (gameexe.Exists("OBJECT.999"))
119 data.emplace_back(gameexe("OBJECT.999"));
120 else
121 data.emplace_back();
122
123 // Read the #OBJECT.xxx entries from the Gameexe
124 GameexeFilteringIterator it = gameexe.filtering_begin("OBJECT.");
125 GameexeFilteringIterator end = gameexe.filtering_end();
126 for (; it != end; ++it) {
127 string s = it->key().substr(it->key().find_first_of(".") + 1);
128 std::list<int> object_nums;
129 string::size_type poscolon = s.find_first_of(":");
130 if (poscolon != string::npos) {
131 int obj_num_first = std::stoi(s.substr(0, poscolon));
132 int obj_num_last = std::stoi(s.substr(poscolon + 1));
133 while (obj_num_first <= obj_num_last) {
134 object_nums.push_back(obj_num_first++);
135 }
136 } else {
137 object_nums.push_back(std::stoi(s));
138 }
139
140 for (int obj_num : object_nums) {
141 if (obj_num != 999 && obj_num < objects_in_a_layer) {
142 position[obj_num] = data.size();
143 data.emplace_back(*it);
144 }
145 }
146 }
147 }
148
149 // -----------------------------------------------------------------------
150
151 const ObjectSettings&
GetObjectSettingsFor(int obj_num)152 GraphicsSystem::GraphicsObjectSettings::GetObjectSettingsFor(int obj_num) {
153 return data.at(position[obj_num]);
154 }
155
156 // -----------------------------------------------------------------------
157 // GraphicsSystemGlobals
158 // -----------------------------------------------------------------------
GraphicsSystemGlobals()159 GraphicsSystemGlobals::GraphicsSystemGlobals()
160 : show_object_1(false),
161 show_object_2(false),
162 show_weather(false),
163 skip_animations(0),
164 screen_mode(1),
165 cg_table(),
166 tone_curves() {}
167
GraphicsSystemGlobals(Gameexe & gameexe)168 GraphicsSystemGlobals::GraphicsSystemGlobals(Gameexe& gameexe)
169 : show_object_1(gameexe("INIT_OBJECT1_ONOFF_MOD").ToInt(0) ? 0 : 1),
170 show_object_2(gameexe("INIT_OBJECT2_ONOFF_MOD").ToInt(0) ? 0 : 1),
171 show_weather(gameexe("INIT_WEATHER_ONOFF_MOD").ToInt(0) ? 0 : 1),
172 skip_animations(0),
173 screen_mode(1),
174 cg_table(gameexe),
175 tone_curves(gameexe) {}
176
177 // -----------------------------------------------------------------------
178 // GraphicsObjectImpl
179 // -----------------------------------------------------------------------
180 struct GraphicsSystem::GraphicsObjectImpl {
181 explicit GraphicsObjectImpl(int objects_in_layer);
182
183 // Foreground objects
184 LazyArray<GraphicsObject> foreground_objects;
185
186 // Background objects
187 LazyArray<GraphicsObject> background_objects;
188
189 // Foreground objects (at the time of the last save)
190 LazyArray<GraphicsObject> saved_foreground_objects;
191
192 // Background objects (at the time of the last save)
193 LazyArray<GraphicsObject> saved_background_objects;
194
195 // Whether we restore |old_graphics_stack| using the old method instead of
196 // replaying the new graphics stack format.
197 bool use_old_graphics_stack;
198
199 // List of commands in RealLive bytecode to rebuild the graphics stack at the
200 // current moment.
201 std::deque<std::string> graphics_stack;
202
203 // Commands to rebuild the graphics stack (at the time of the last savepoint)
204 std::deque<std::string> saved_graphics_stack;
205
206 // Old style graphics stack implementation.
207 std::vector<GraphicsStackFrame> old_graphics_stack;
208 };
209
210 // -----------------------------------------------------------------------
211
GraphicsObjectImpl(int size)212 GraphicsSystem::GraphicsObjectImpl::GraphicsObjectImpl(int size)
213 : foreground_objects(size),
214 background_objects(size),
215 saved_foreground_objects(size),
216 saved_background_objects(size),
217 use_old_graphics_stack(false) {}
218
219 // -----------------------------------------------------------------------
220 // GraphicsSystem
221 // -----------------------------------------------------------------------
GraphicsSystem(System & system,Gameexe & gameexe)222 GraphicsSystem::GraphicsSystem(System& system, Gameexe& gameexe)
223 : screen_update_mode_(SCREENUPDATEMODE_AUTOMATIC),
224 background_type_(BACKGROUND_DC0),
225 screen_needs_refresh_(false),
226 object_state_dirty_(false),
227 is_responsible_for_update_(true),
228 display_subtitle_(gameexe("SUBTITLE").ToInt(0)),
229 interface_hidden_(false),
230 globals_(gameexe),
231 time_at_last_queue_change_(0),
232 graphics_object_settings_(new GraphicsObjectSettings(gameexe)),
233 graphics_object_impl_(new GraphicsObjectImpl(
234 graphics_object_settings_->objects_in_a_layer)),
235 use_custom_mouse_cursor_(gameexe("MOUSE_CURSOR").Exists()),
236 show_cursor_from_bytecode_(true),
237 cursor_(gameexe("MOUSE_CURSOR").ToInt(0)),
238 system_(system),
239 preloaded_hik_scripts_(32),
240 preloaded_g00_(256),
241 image_cache_(10) {}
242
243 // -----------------------------------------------------------------------
244
~GraphicsSystem()245 GraphicsSystem::~GraphicsSystem() {}
246
247 // -----------------------------------------------------------------------
248
MarkScreenAsDirty(GraphicsUpdateType type)249 void GraphicsSystem::MarkScreenAsDirty(GraphicsUpdateType type) {
250 switch (screen_update_mode()) {
251 case SCREENUPDATEMODE_AUTOMATIC:
252 case SCREENUPDATEMODE_SEMIAUTOMATIC: {
253 // Perform a blit of DC0 to the screen, and update it.
254 screen_needs_refresh_ = true;
255 break;
256 }
257 case SCREENUPDATEMODE_MANUAL: {
258 // Don't really do anything.
259 break;
260 }
261 default: {
262 ostringstream oss;
263 oss << "Invalid screen update mode value: " << screen_update_mode();
264 throw SystemError(oss.str());
265 }
266 }
267 }
268
269 // -----------------------------------------------------------------------
270
ForceRefresh()271 void GraphicsSystem::ForceRefresh() {
272 screen_needs_refresh_ = true;
273
274 if (screen_update_mode_ == SCREENUPDATEMODE_MANUAL) {
275 // Note: SDLEventSystem can also set_force_wait(), in the case of automatic
276 // mode.
277 system().set_force_wait(true);
278 }
279 }
280
OnScreenRefreshed()281 void GraphicsSystem::OnScreenRefreshed() {
282 screen_needs_refresh_ = false;
283 object_state_dirty_ = false;
284 }
285
286 // -----------------------------------------------------------------------
287
SetScreenUpdateMode(DCScreenUpdateMode u)288 void GraphicsSystem::SetScreenUpdateMode(DCScreenUpdateMode u) {
289 screen_update_mode_ = u;
290 }
291
292 // -----------------------------------------------------------------------
293
QueueShakeSpec(int spec)294 void GraphicsSystem::QueueShakeSpec(int spec) {
295 Gameexe& gameexe = system().gameexe();
296
297 if (gameexe("SHAKE", spec).Exists()) {
298 vector<int> spec_vector = gameexe("SHAKE", spec).ToIntVector();
299
300 int x, y, time;
301 vector<int>::const_iterator it = spec_vector.begin();
302 while (it != spec_vector.end()) {
303 x = *it++;
304 if (it != spec_vector.end()) {
305 y = *it++;
306 if (it != spec_vector.end()) {
307 time = *it++;
308 screen_shake_queue_.push(std::make_pair(Point(x, y), time));
309 }
310 }
311 }
312
313 ForceRefresh();
314 time_at_last_queue_change_ = system().event().GetTicks();
315 }
316 }
317
318 // -----------------------------------------------------------------------
319
GetScreenOrigin()320 Point GraphicsSystem::GetScreenOrigin() {
321 if (screen_shake_queue_.empty()) {
322 return Point(0, 0);
323 } else {
324 return screen_shake_queue_.front().first;
325 }
326 }
327
328 // -----------------------------------------------------------------------
329
IsShaking() const330 bool GraphicsSystem::IsShaking() const { return !screen_shake_queue_.empty(); }
331
332 // -----------------------------------------------------------------------
333
CurrentShakingFrameTime() const334 int GraphicsSystem::CurrentShakingFrameTime() const {
335 if (screen_shake_queue_.empty()) {
336 return 10;
337 } else {
338 return screen_shake_queue_.front().second;
339 }
340 }
341
342 // -----------------------------------------------------------------------
343
ShouldUseCustomCursor()344 int GraphicsSystem::ShouldUseCustomCursor() {
345 return use_custom_mouse_cursor_ &&
346 system().gameexe()("MOUSE_CURSOR", cursor_, "NAME").ToString("") != "";
347 }
348
349 // -----------------------------------------------------------------------
350
SetCursor(int cursor)351 void GraphicsSystem::SetCursor(int cursor) {
352 cursor_ = cursor;
353 mouse_cursor_.reset();
354 }
355
356 // -----------------------------------------------------------------------
357
AddGraphicsStackCommand(const std::string & command)358 void GraphicsSystem::AddGraphicsStackCommand(const std::string& command) {
359 graphics_object_impl_->graphics_stack.push_back(command);
360
361 // RealLive only allows 127 commands to be on the stack so game programmers
362 // can be lazy and not clear it.
363 if (graphics_object_impl_->graphics_stack.size() > 127)
364 graphics_object_impl_->graphics_stack.pop_front();
365 }
366
367 // -----------------------------------------------------------------------
368
StackSize() const369 int GraphicsSystem::StackSize() const {
370 // I don't think this will ever be accurate in the face of multi()
371 // commands. I'm not sure if this matters because the only use of StackSize()
372 // appears to be this recurring pattern in RL bytecode:
373 //
374 // x = stackSize()
375 // ... large graphics demo
376 // stackTrunk(x)
377 return graphics_object_impl_->graphics_stack.size();
378 }
379
380 // -----------------------------------------------------------------------
381
ClearStack()382 void GraphicsSystem::ClearStack() {
383 graphics_object_impl_->graphics_stack.clear();
384 }
385
386 // -----------------------------------------------------------------------
387
StackPop(int items)388 void GraphicsSystem::StackPop(int items) {
389 for (int i = 0; i < items; ++i) {
390 if (graphics_object_impl_->graphics_stack.size()) {
391 graphics_object_impl_->graphics_stack.pop_back();
392 }
393 }
394 }
395
396 // -----------------------------------------------------------------------
397
ReplayGraphicsStack(RLMachine & machine)398 void GraphicsSystem::ReplayGraphicsStack(RLMachine& machine) {
399 if (graphics_object_impl_->use_old_graphics_stack) {
400 // The actual act of replaying the graphics stack will recreate the graphics
401 // stack, so clear it.
402 vector<GraphicsStackFrame> stack_to_replay;
403 stack_to_replay.swap(graphics_object_impl_->old_graphics_stack);
404 ReplayDepricatedGraphicsStackVector(machine, stack_to_replay);
405 graphics_object_impl_->use_old_graphics_stack = false;
406 } else {
407 std::deque<std::string> stack_to_replay;
408 stack_to_replay.swap(graphics_object_impl_->graphics_stack);
409
410 machine.set_replaying_graphics_stack(true);
411 ReplayGraphicsStackCommand(machine, stack_to_replay);
412 machine.set_replaying_graphics_stack(false);
413 }
414 }
415
416 // -----------------------------------------------------------------------
417
SetHikRenderer(HIKRenderer * renderer)418 void GraphicsSystem::SetHikRenderer(HIKRenderer* renderer) {
419 hik_renderer_.reset(renderer);
420 }
421
422 // -----------------------------------------------------------------------
423
AddRenderable(Renderable * renderable)424 void GraphicsSystem::AddRenderable(Renderable* renderable) {
425 final_renderers_.insert(renderable);
426 }
427
428 // -----------------------------------------------------------------------
429
RemoveRenderable(Renderable * renderable)430 void GraphicsSystem::RemoveRenderable(Renderable* renderable) {
431 final_renderers_.erase(renderable);
432 }
433
434 // -----------------------------------------------------------------------
435
SetWindowSubtitle(const std::string & cp932str,int text_encoding)436 void GraphicsSystem::SetWindowSubtitle(const std::string& cp932str,
437 int text_encoding) {
438 subtitle_ = cp932str;
439 }
440
441 // -----------------------------------------------------------------------
442
SetScreenMode(const int in)443 void GraphicsSystem::SetScreenMode(const int in) {
444 bool changed = globals_.screen_mode != in;
445
446 globals_.screen_mode = in;
447
448 if (changed) {
449 NotificationService::current()->Notify(
450 NotificationType::FULLSCREEN_STATE_CHANGED,
451 Source<GraphicsSystem>(this),
452 Details<const int>(&in));
453 }
454 }
455
456 // -----------------------------------------------------------------------
457
ToggleFullscreen()458 void GraphicsSystem::ToggleFullscreen() { SetScreenMode(screen_mode() ? 0 : 1); }
459
460 // -----------------------------------------------------------------------
461
ToggleInterfaceHidden()462 void GraphicsSystem::ToggleInterfaceHidden() {
463 interface_hidden_ = !interface_hidden_;
464 }
465
466 // -----------------------------------------------------------------------
467
GetObjectSettings(const int obj_num)468 const ObjectSettings& GraphicsSystem::GetObjectSettings(const int obj_num) {
469 return graphics_object_settings_->GetObjectSettingsFor(obj_num);
470 }
471
472 // -----------------------------------------------------------------------
473
Refresh(std::ostream * tree)474 void GraphicsSystem::Refresh(std::ostream* tree) {
475 BeginFrame();
476 DrawFrame(tree);
477 EndFrame();
478 }
479
RenderToSurface()480 std::shared_ptr<Surface> GraphicsSystem::RenderToSurface() {
481 BeginFrame();
482 DrawFrame(NULL);
483 return EndFrameToSurface();
484 }
485
DrawFrame(std::ostream * tree)486 void GraphicsSystem::DrawFrame(std::ostream* tree) {
487 switch (background_type_) {
488 case BACKGROUND_DC0: {
489 // Display DC0
490 GetDC(0)->RenderToScreen(screen_rect(), screen_rect(), 255);
491 if (tree) {
492 // TODO(erg): How do we print the new graphics stack?
493 *tree << "Graphic Stack: UNDER CONSTRUCTION" << endl;
494 }
495 break;
496 }
497 case BACKGROUND_HIK: {
498 if (hik_renderer_) {
499 hik_renderer_->Render(tree);
500 } else {
501 GetHaikei()->RenderToScreen(screen_rect(), screen_rect(), 255);
502 if (tree) {
503 *tree << "[Haikei bitmap: " << default_bgr_name_ << "]" << endl;
504 }
505 }
506 }
507 }
508
509 RenderObjects(tree);
510
511 // Render text
512 if (!is_interface_hidden())
513 system().text().Render(tree);
514 }
515
516 // -----------------------------------------------------------------------
517
ExecuteGraphicsSystem(RLMachine & machine)518 void GraphicsSystem::ExecuteGraphicsSystem(RLMachine& machine) {
519 // Check to see if any of the graphics objects are reporting that
520 // they want to force a redraw
521 for (GraphicsObject& obj : GetForegroundObjects())
522 obj.Execute(machine);
523
524 if (mouse_cursor_)
525 mouse_cursor_->Execute(system());
526
527 if (hik_renderer_ && background_type_ == BACKGROUND_HIK)
528 hik_renderer_->Execute(machine);
529
530 // Possibly update the screen shaking state
531 if (!screen_shake_queue_.empty()) {
532 unsigned int now = system().event().GetTicks();
533 unsigned int accumulated_ticks = now - time_at_last_queue_change_;
534 while (!screen_shake_queue_.empty() &&
535 accumulated_ticks > screen_shake_queue_.front().second) {
536 int frame_ticks = screen_shake_queue_.front().second;
537 accumulated_ticks -= frame_ticks;
538 time_at_last_queue_change_ += frame_ticks;
539 screen_shake_queue_.pop();
540 ForceRefresh();
541 }
542 }
543 }
544
545 // -----------------------------------------------------------------------
546
Reset()547 void GraphicsSystem::Reset() {
548 graphics_object_impl_->foreground_objects.Clear();
549 graphics_object_impl_->background_objects.Clear();
550
551 ClearAllDCs();
552
553 preloaded_hik_scripts_.Clear();
554 preloaded_g00_.Clear();
555 hik_renderer_.reset();
556 background_type_ = BACKGROUND_DC0;
557
558 // Reset the cursor
559 show_cursor_from_bytecode_ = true;
560 cursor_ = system().gameexe()("MOUSE_CURSOR").ToInt(0);
561 mouse_cursor_.reset();
562
563 default_grp_name_ = "";
564 default_bgr_name_ = "";
565 screen_update_mode_ = SCREENUPDATEMODE_AUTOMATIC;
566 background_type_ = BACKGROUND_DC0;
567 subtitle_ = "";
568 interface_hidden_ = false;
569 }
570
GetEmojiSurface()571 std::shared_ptr<const Surface> GraphicsSystem::GetEmojiSurface() {
572 GameexeFilteringIterator it = system().gameexe().filtering_begin("E_MOJI.");
573 GameexeFilteringIterator end = system().gameexe().filtering_end();
574 for (; it != end; ++it) {
575 // Try to interpret each key as a filename.
576 std::string file_name = it->ToString("");
577 std::shared_ptr<const Surface> surface = GetSurfaceNamed(file_name);
578 if (surface)
579 return surface;
580 }
581
582 return std::shared_ptr<const Surface>();
583 }
584
PreloadHIKScript(System & system,int slot,const std::string & name,const boost::filesystem::path & file_path)585 void GraphicsSystem::PreloadHIKScript(
586 System& system,
587 int slot,
588 const std::string& name,
589 const boost::filesystem::path& file_path) {
590 HIKScript* script = new HIKScript(system, file_path);
591 script->EnsureUploaded();
592
593 preloaded_hik_scripts_[slot] =
594 std::make_pair(name, std::shared_ptr<HIKScript>(script));
595 }
596
ClearPreloadedHIKScript(int slot)597 void GraphicsSystem::ClearPreloadedHIKScript(int slot) {
598 preloaded_hik_scripts_[slot] =
599 std::make_pair("", std::shared_ptr<HIKScript>());
600 }
601
ClearAllPreloadedHIKScripts()602 void GraphicsSystem::ClearAllPreloadedHIKScripts() {
603 preloaded_hik_scripts_.Clear();
604 }
605
GetHIKScript(System & system,const std::string & name,const boost::filesystem::path & file_path)606 std::shared_ptr<HIKScript> GraphicsSystem::GetHIKScript(
607 System& system,
608 const std::string& name,
609 const boost::filesystem::path& file_path) {
610 for (HIKArrayItem& item : preloaded_hik_scripts_) {
611 if (item.first == name)
612 return item.second;
613 }
614
615 return std::shared_ptr<HIKScript>(new HIKScript(system, file_path));
616 }
617
PreloadG00(int slot,const std::string & name)618 void GraphicsSystem::PreloadG00(int slot, const std::string& name) {
619 // We first check our implicit cache just in case so we don't load it twice.
620 std::shared_ptr<const Surface> surface = image_cache_.fetch(name);
621 if (!surface)
622 surface = LoadSurfaceFromFile(name);
623
624 if (surface)
625 surface->EnsureUploaded();
626
627 preloaded_g00_[slot] = std::make_pair(name, surface);
628 }
629
ClearPreloadedG00(int slot)630 void GraphicsSystem::ClearPreloadedG00(int slot) {
631 preloaded_g00_[slot] = std::make_pair("", std::shared_ptr<const Surface>());
632 }
633
ClearAllPreloadedG00()634 void GraphicsSystem::ClearAllPreloadedG00() { preloaded_g00_.Clear(); }
635
GetPreloadedG00(const std::string & name)636 std::shared_ptr<const Surface> GraphicsSystem::GetPreloadedG00(
637 const std::string& name) {
638 for (G00ArrayItem& item : preloaded_g00_) {
639 if (item.first == name)
640 return item.second;
641 }
642
643 return std::shared_ptr<const Surface>();
644 }
645
646 // -----------------------------------------------------------------------
647
GetSurfaceNamedAndMarkViewed(RLMachine & machine,const std::string & short_filename)648 std::shared_ptr<const Surface> GraphicsSystem::GetSurfaceNamedAndMarkViewed(
649 RLMachine& machine,
650 const std::string& short_filename) {
651 // Record that we viewed this CG.
652 cg_table().SetViewed(machine, short_filename);
653
654 return GetSurfaceNamed(short_filename);
655 }
656
657 // -----------------------------------------------------------------------
658
GetSurfaceNamed(const std::string & short_filename)659 std::shared_ptr<const Surface> GraphicsSystem::GetSurfaceNamed(
660 const std::string& short_filename) {
661 // Check if this is in the script controlled cache.
662 std::shared_ptr<const Surface> cached_surface =
663 GetPreloadedG00(short_filename);
664 if (cached_surface)
665 return cached_surface;
666
667 // First check to see if this surface is already in our internal cache
668 cached_surface = image_cache_.fetch(short_filename);
669 if (cached_surface)
670 return cached_surface;
671
672 std::shared_ptr<const Surface> surface_to_ret =
673 LoadSurfaceFromFile(short_filename);
674 image_cache_.insert(short_filename, surface_to_ret);
675 return surface_to_ret;
676 }
677
678 // -----------------------------------------------------------------------
679
ClearAndPromoteObjects()680 void GraphicsSystem::ClearAndPromoteObjects() {
681 typedef LazyArray<GraphicsObject>::full_iterator FullIterator;
682
683 FullIterator bg = graphics_object_impl_->background_objects.full_begin();
684 FullIterator bg_end = graphics_object_impl_->background_objects.full_end();
685 FullIterator fg = graphics_object_impl_->foreground_objects.full_begin();
686 FullIterator fg_end = graphics_object_impl_->foreground_objects.full_end();
687 for (; bg != bg_end && fg != fg_end; bg++, fg++) {
688 if (fg.valid() && !fg->wipe_copy()) {
689 fg->InitializeParams();
690 fg->FreeObjectData();
691 }
692
693 if (bg.valid()) {
694 *fg = *bg;
695 bg->InitializeParams();
696 bg->FreeObjectData();
697 }
698 }
699 }
700
701 // -----------------------------------------------------------------------
702
GetObject(int layer,int obj_number)703 GraphicsObject& GraphicsSystem::GetObject(int layer, int obj_number) {
704 if (layer < 0 || layer > 1)
705 throw rlvm::Exception("Invalid layer number");
706
707 if (layer == OBJ_BG)
708 return graphics_object_impl_->background_objects[obj_number];
709 else
710 return graphics_object_impl_->foreground_objects[obj_number];
711 }
712
713 // -----------------------------------------------------------------------
714
SetObject(int layer,int obj_number,GraphicsObject & obj)715 void GraphicsSystem::SetObject(int layer, int obj_number, GraphicsObject& obj) {
716 if (layer < 0 || layer > 1)
717 throw rlvm::Exception("Invalid layer number");
718
719 if (layer == OBJ_BG)
720 graphics_object_impl_->background_objects[obj_number] = obj;
721 else
722 graphics_object_impl_->foreground_objects[obj_number] = obj;
723 }
724
725 // -----------------------------------------------------------------------
726
FreeObjectData(int obj_number)727 void GraphicsSystem::FreeObjectData(int obj_number) {
728 graphics_object_impl_->foreground_objects[obj_number].FreeObjectData();
729 graphics_object_impl_->background_objects[obj_number].FreeObjectData();
730 }
731
732 // -----------------------------------------------------------------------
733
FreeAllObjectData()734 void GraphicsSystem::FreeAllObjectData() {
735 for (GraphicsObject& object : graphics_object_impl_->foreground_objects)
736 object.FreeObjectData();
737
738 for (GraphicsObject& object : graphics_object_impl_->background_objects)
739 object.FreeObjectData();
740 }
741
742 // -----------------------------------------------------------------------
743
InitializeObjectParams(int obj_number)744 void GraphicsSystem::InitializeObjectParams(int obj_number) {
745 graphics_object_impl_->foreground_objects[obj_number].InitializeParams();
746 graphics_object_impl_->background_objects[obj_number].InitializeParams();
747 }
748
749 // -----------------------------------------------------------------------
750
InitializeAllObjectParams()751 void GraphicsSystem::InitializeAllObjectParams() {
752 for (GraphicsObject& object : graphics_object_impl_->foreground_objects)
753 object.InitializeParams();
754
755 for (GraphicsObject& object : graphics_object_impl_->background_objects)
756 object.InitializeParams();
757 }
758
759 // -----------------------------------------------------------------------
760
GetObjectLayerSize()761 int GraphicsSystem::GetObjectLayerSize() {
762 return graphics_object_settings_->objects_in_a_layer;
763 }
764
765 // -----------------------------------------------------------------------
766
GetBackgroundObjects()767 LazyArray<GraphicsObject>& GraphicsSystem::GetBackgroundObjects() {
768 return graphics_object_impl_->background_objects;
769 }
770
771 // -----------------------------------------------------------------------
772
GetForegroundObjects()773 LazyArray<GraphicsObject>& GraphicsSystem::GetForegroundObjects() {
774 return graphics_object_impl_->foreground_objects;
775 }
776
777 // -----------------------------------------------------------------------
778
AnimationsPlaying() const779 bool GraphicsSystem::AnimationsPlaying() const {
780 for (GraphicsObject& object : graphics_object_impl_->foreground_objects) {
781 if (object.has_object_data()) {
782 GraphicsObjectData& data = object.GetObjectData();
783 if (data.IsAnimation() && data.is_currently_playing())
784 return true;
785 }
786 }
787
788 return false;
789 }
790
791 // -----------------------------------------------------------------------
792
TakeSavepointSnapshot()793 void GraphicsSystem::TakeSavepointSnapshot() {
794 GetForegroundObjects().CopyTo(graphics_object_impl_->saved_foreground_objects);
795 GetBackgroundObjects().CopyTo(graphics_object_impl_->saved_background_objects);
796 graphics_object_impl_->saved_graphics_stack =
797 graphics_object_impl_->graphics_stack;
798 }
799
800 // -----------------------------------------------------------------------
801
ClearAllDCs()802 void GraphicsSystem::ClearAllDCs() {
803 GetDC(0)->Fill(RGBAColour::Black());
804
805 for (int i = 1; i < 16; ++i)
806 FreeDC(i);
807 }
808
809 // -----------------------------------------------------------------------
810
RenderObjects(std::ostream * tree)811 void GraphicsSystem::RenderObjects(std::ostream* tree) {
812 to_render_.clear();
813
814 // Collate all objects that we might want to render.
815 AllocatedLazyArrayIterator<GraphicsObject> it =
816 graphics_object_impl_->foreground_objects.begin();
817 AllocatedLazyArrayIterator<GraphicsObject> end =
818 graphics_object_impl_->foreground_objects.end();
819 for (; it != end; ++it) {
820 const ObjectSettings& settings = GetObjectSettings(it.pos());
821 if (settings.obj_on_off == 1 && should_show_object1() == false)
822 continue;
823 else if (settings.obj_on_off == 2 && should_show_object2() == false)
824 continue;
825 else if (settings.weather_on_off && should_show_weather() == false)
826 continue;
827 else if (settings.space_key && is_interface_hidden())
828 continue;
829
830 to_render_.emplace_back(
831 it->z_order(), it->z_layer(), it->z_depth(), it.pos(), &*it);
832 }
833
834 // Sort by all the ordering values.
835 std::sort(to_render_.begin(), to_render_.end());
836
837 for (ToRenderVec::iterator it = to_render_.begin(); it != to_render_.end();
838 ++it) {
839 get<4>(*it)->Render(get<3>(*it), NULL, tree);
840 }
841 }
842
843 // -----------------------------------------------------------------------
844
GetCurrentCursor()845 std::shared_ptr<MouseCursor> GraphicsSystem::GetCurrentCursor() {
846 if (!use_custom_mouse_cursor_ || !show_cursor_from_bytecode_)
847 return std::shared_ptr<MouseCursor>();
848
849 if (use_custom_mouse_cursor_ && !mouse_cursor_) {
850 MouseCursorCache::iterator it = cursor_cache_.find(cursor_);
851 if (it != cursor_cache_.end()) {
852 mouse_cursor_ = it->second;
853 } else {
854 std::shared_ptr<const Surface> cursor_surface;
855 GameexeInterpretObject cursor =
856 system().gameexe()("MOUSE_CURSOR", cursor_);
857 GameexeInterpretObject name_key = cursor("NAME");
858
859 if (name_key.Exists()) {
860 int count = cursor("CONT").ToInt(1);
861 int speed = cursor("SPEED").ToInt(800);
862
863 cursor_surface = GetSurfaceNamed(name_key);
864 mouse_cursor_.reset(
865 new MouseCursor(system(), cursor_surface, count, speed));
866 cursor_cache_[cursor_] = mouse_cursor_;
867 } else {
868 mouse_cursor_.reset();
869 }
870 }
871 }
872
873 return mouse_cursor_;
874 }
875
876 // -----------------------------------------------------------------------
877
SetScreenSize(const Size & size)878 void GraphicsSystem::SetScreenSize(const Size& size) {
879 screen_size_ = size;
880 screen_rect_ = Rect(Point(0, 0), size);
881 }
882
883 // -----------------------------------------------------------------------
884
MouseMotion(const Point & new_location)885 void GraphicsSystem::MouseMotion(const Point& new_location) {
886 if (use_custom_mouse_cursor_ && show_cursor_from_bytecode_)
887 MarkScreenAsDirty(GUT_MOUSE_MOTION);
888
889 cursor_pos_ = new_location;
890 }
891
892 // -----------------------------------------------------------------------
893
BuildObjOfFile(const std::string & filename)894 GraphicsObjectData* GraphicsSystem::BuildObjOfFile(
895 const std::string& filename) {
896 // Get the path to get the file type (which won't be in filename)
897 fs::path full_path = system().FindFile(filename, OBJ_FILETYPES);
898 if (full_path.empty()) {
899 ostringstream oss;
900 oss << "Could not find Object compatible file \"" << filename << "\".";
901 throw rlvm::Exception(oss.str());
902 }
903
904 string file_str = full_path.string();
905 if (iends_with(file_str, "g00") || iends_with(file_str, "pdt")) {
906 return new GraphicsObjectOfFile(system(), filename);
907 } else if (iends_with(file_str, "anm")) {
908 return new AnmGraphicsObjectData(system(), filename);
909 } else {
910 ostringstream oss;
911 oss << "Don't know how to handle object file: \"" << filename << "\"";
912 throw rlvm::Exception(oss.str());
913 }
914 }
915
916 // -----------------------------------------------------------------------
917
918 template <class Archive>
save(Archive & ar,unsigned int version) const919 void GraphicsSystem::save(Archive& ar, unsigned int version) const {
920 ar& subtitle_& default_grp_name_& default_bgr_name_& graphics_object_impl_
921 ->saved_graphics_stack& graphics_object_impl_->saved_background_objects&
922 graphics_object_impl_->saved_foreground_objects;
923 }
924
925 // -----------------------------------------------------------------------
926
927 template <class Archive>
load(Archive & ar,unsigned int version)928 void GraphicsSystem::load(Archive& ar, unsigned int version) {
929 ar& subtitle_;
930 if (version > 0) {
931 ar& default_grp_name_;
932 ar& default_bgr_name_;
933 graphics_object_impl_->use_old_graphics_stack = false;
934 ar& graphics_object_impl_->graphics_stack;
935 } else {
936 graphics_object_impl_->use_old_graphics_stack = true;
937 ar& graphics_object_impl_->old_graphics_stack;
938 }
939
940 ar& graphics_object_impl_->background_objects& graphics_object_impl_
941 ->foreground_objects;
942
943 // Now alert all subclasses that we've set the subtitle
944 SetWindowSubtitle(subtitle_,
945 Serialization::g_current_machine->GetTextEncoding());
946 }
947
948 // -----------------------------------------------------------------------
949
950 template void GraphicsSystem::load<boost::archive::text_iarchive>(
951 boost::archive::text_iarchive& ar,
952 unsigned int version);
953 template void GraphicsSystem::save<boost::archive::text_oarchive>(
954 boost::archive::text_oarchive& ar,
955 unsigned int version) const;
956