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 #ifndef SRC_SYSTEMS_BASE_GRAPHICS_SYSTEM_H_ 29 #define SRC_SYSTEMS_BASE_GRAPHICS_SYSTEM_H_ 30 31 #include <boost/filesystem/path.hpp> 32 #include <boost/serialization/access.hpp> 33 #include <boost/serialization/split_member.hpp> 34 #include <boost/serialization/version.hpp> 35 36 #include <iosfwd> 37 #include <map> 38 #include <memory> 39 #include <queue> 40 #include <set> 41 #include <string> 42 #include <utility> 43 #include <vector> 44 45 #include "systems/base/cgm_table.h" 46 #include "systems/base/event_listener.h" 47 #include "systems/base/rect.h" 48 #include "systems/base/tone_curve.h" 49 50 #include "utilities/lazy_array.h" 51 #include "lru_cache.hpp" 52 53 class ColourFilter; 54 class Gameexe; 55 class GraphicsObject; 56 class GraphicsObjectData; 57 class GraphicsStackFrame; 58 class HIKRenderer; 59 class HIKScript; 60 class MouseCursor; 61 class Renderable; 62 class RGBAColour; 63 class RLMachine; 64 class Size; 65 class Surface; 66 class System; 67 struct ObjectSettings; 68 69 template <typename T> 70 class LazyArray; 71 72 // Variables and configuration data that are global across all save 73 // game files in a game. 74 struct GraphicsSystemGlobals { 75 GraphicsSystemGlobals(); 76 explicit GraphicsSystemGlobals(Gameexe& gameexe); 77 78 // ShowObject flags 79 int show_object_1, show_object_2; 80 81 int show_weather; 82 83 // Whether we should skip animations (such as those made by Effect) 84 int skip_animations; 85 86 // screen mode. 1 is windowed (default), 0 is full-screen. 87 int screen_mode; 88 89 // CG Table 90 CGMTable cg_table; 91 92 // tone curve table 93 ToneCurve tone_curves; 94 95 // boost::serialization support 96 template <class Archive> serializeGraphicsSystemGlobals97 void serialize(Archive& ar, const unsigned int version) { 98 ar& show_object_1& show_object_2& show_weather; 99 100 if (version > 0) 101 ar& cg_table; 102 103 if (version > 1) 104 ar& screen_mode; 105 } 106 }; 107 108 BOOST_CLASS_VERSION(GraphicsSystemGlobals, 2) 109 110 // When marking the screen as dirty, we need to know what kind of 111 // operation was done 112 enum GraphicsUpdateType { 113 GUT_DRAW_DC0, 114 GUT_DRAW_HIK, 115 GUT_DISPLAY_OBJ, 116 GUT_TEXTSYS, 117 GUT_MOUSE_MOTION 118 }; 119 120 // Which type of mutually exclusive background should we display? 121 enum GraphicsBackgroundType { BACKGROUND_DC0, BACKGROUND_HIK }; 122 123 // Abstract interface to a graphics system. Specialize this class for 124 // each system you plan on running RLVM on. For now, there's only one 125 // derived class; SDLGraphicsSystem. 126 // 127 // Two device contexts must be allocated during initialization; DC 0, 128 // which should refer to a surface that is (usually) blitted onto the 129 // screen immediatly after it is written to, and DC 1, which is simply 130 // guarenteed to be allocated, and is guarenteed to not be smaller 131 // then the screen. (Many {rec,grp} functions will load data onto DC1 132 // and then copy it onto DC0 with some sort of fancy transition 133 // effect.) 134 class GraphicsSystem : public EventListener { 135 public: 136 // The current display context drawing mode. The Reallive system 137 // will update the screen after certain events in user code 138 // regarding DCs. 139 // 140 // Note that these are not the only times when the screen will be 141 // updated. Most functions that deal with text windows will trigger 142 // screen updates. (Object manipulation functions *don't*.) Having 143 // this fine level of control is why DCs are often used for smooth 144 // animation... 145 enum DCScreenUpdateMode { 146 // The screen will be redrawn after every load or blit to DC 0. 147 SCREENUPDATEMODE_AUTOMATIC, 148 149 // We currently don't understand how this differs from automatic 150 // mode. We declare it anyway for compatibility and the hope that 151 // someday we will. 152 SCREENUPDATEMODE_SEMIAUTOMATIC, 153 154 // The screen is updated after refresh() is called 155 SCREENUPDATEMODE_MANUAL 156 }; 157 158 GraphicsSystem(System& system, Gameexe& gameexe); 159 virtual ~GraphicsSystem(); 160 is_responsible_for_update()161 bool is_responsible_for_update() const { return is_responsible_for_update_; } set_is_responsible_for_update(bool in)162 void set_is_responsible_for_update(bool in) { 163 is_responsible_for_update_ = in; 164 } 165 166 // Sets the default name for certain classes of commands. (Certain graphics 167 // commands set either of these and most graphics commands can pass in '?', 168 // which is substituted with the default name.) default_grp_name()169 const std::string& default_grp_name() const { return default_grp_name_; } set_default_grp_name(const std::string & name)170 void set_default_grp_name(const std::string& name) { 171 default_grp_name_ = name; 172 } default_bgr_name()173 const std::string& default_bgr_name() const { return default_bgr_name_; } set_default_bgr_name(const std::string & name)174 void set_default_bgr_name(const std::string& name) { 175 default_bgr_name_ = name; 176 } 177 178 // Define who is responsible for screen updates. screen_update_mode()179 DCScreenUpdateMode screen_update_mode() const { return screen_update_mode_; } 180 virtual void SetScreenUpdateMode(DCScreenUpdateMode u); 181 set_graphics_background(GraphicsBackgroundType t)182 void set_graphics_background(GraphicsBackgroundType t) { 183 background_type_ = t; 184 } 185 system()186 System& system() { return system_; } 187 188 // Screen Shaking 189 190 // Reads #SHAKE.spec and loads the offsets into the screen shaking queue. 191 void QueueShakeSpec(int spec); 192 193 // Returns the current screen origin. This is used for simple #SHAKE.* based 194 // screen shaking. While the screen is not shaking, this returns (0,0). 195 Point GetScreenOrigin(); 196 197 // Whether we are currently shaking. 198 bool IsShaking() const; 199 200 // How long the current frame in the shaking should last. 10ms if there are 201 // no frames. 202 int CurrentShakingFrameTime() const; 203 204 // Mouse Cursor Management 205 206 // Whether we are using a custom cursor. Verifies that there was a 207 // \#MOUSE_CURSOR entry in the Gameexe.ini file, and that the currently 208 // selected cursor exists. 209 int ShouldUseCustomCursor(); 210 211 // Sets the cursor to the incoming cursor index. 212 virtual void SetCursor(int cursor); 213 214 // Returns the current index. cursor()215 int cursor() const { return cursor_; } 216 217 // Whether we display a cursor at all. set_show_cursor_from_bytecode(const int in)218 void set_show_cursor_from_bytecode(const int in) { 219 show_cursor_from_bytecode_ = in; 220 } 221 222 // Graphics stack implementation 223 // 224 // The RealLive virtual machine keeps track of recent graphics commands so 225 // that when the game is restored, these graphics commands can be replayed to 226 // recreate the screen state. 227 228 // Adds |command|, the serialized form of a bytecode used by calling the 229 // BytecodeElement::data(). 230 void AddGraphicsStackCommand(const std::string& command); 231 232 // Returns the number of entries in the stack. 233 int StackSize() const; 234 235 // Clears the graphics stack. 236 void ClearStack(); 237 238 // Removes (up to) |num_items| from the stack. (Stops when the stack is 239 // empty). 240 void StackPop(int num_items); 241 242 // Replays the graphics stack. This is called after we've reloaded 243 // a saved game and deals with both old style and the new stack system. 244 void ReplayGraphicsStack(RLMachine& machine); 245 246 // Sets the current hik script. GraphicsSystem takes ownership, freeing the 247 // current HIKScript if applicable. |script| can be NULL. hik_renderer()248 HIKRenderer* hik_renderer() const { return hik_renderer_.get(); } 249 void SetHikRenderer(HIKRenderer* script); 250 251 // ----------------------------------------------------------------------- 252 253 // Individual LongOperations can also hook into the end of the rendering 254 // pipeline by injecting Renderables. There should only really be one 255 // Renderable on screen at a time, but the interface allows for multiple 256 // ones. 257 void AddRenderable(Renderable* renderable); 258 void RemoveRenderable(Renderable* renderable); 259 260 // Subtitle management 261 262 // Sets the current value of the subtitle, as set with title(). This 263 // is virtual so that UTF8 or other charset systems can convert for 264 // their own internal copy. 265 virtual void SetWindowSubtitle(const std::string& cp932str, 266 int text_encoding); 267 268 // Returns the current window subtitle, in native encoding. window_subtitle()269 const std::string& window_subtitle() const { return subtitle_; } 270 271 // Wether we should display the subtitle. should_display_subtitle()272 bool should_display_subtitle() const { return display_subtitle_; } 273 274 // Access to the GrapihcsSystem global variables. globals()275 GraphicsSystemGlobals& globals() { return globals_; } 276 277 // The `show object' flags are used to provide a way of enabling or 278 // disabling interface elements from the menu. If an object's 279 // `ObjectOnOff' property is set to 1 or 2, it will be shown or 280 // hidden depending on the corresponding `show object' flag. This is 281 // one of the properties controlled by the \#OBJECT variables in 282 // gameexe.ini. should_show_object1()283 int should_show_object1() const { return globals_.show_object_1; } set_should_show_object1(const int in)284 void set_should_show_object1(const int in) { globals_.show_object_1 = in; } should_show_object2()285 int should_show_object2() const { return globals_.show_object_2; } set_should_show_object2(const int in)286 void set_should_show_object2(const int in) { globals_.show_object_2 = in; } should_show_weather()287 int should_show_weather() const { return globals_.show_weather; } set_should_show_weather(const int in)288 void set_should_show_weather(const int in) { globals_.show_weather = in; } 289 290 // Sets whether we're in fullscreen mode. SetScreenMode() is virtual so we 291 // can tell SDL to switch the screen mode. screen_mode()292 int screen_mode() const { return globals_.screen_mode; } 293 virtual void SetScreenMode(const int in); 294 void ToggleFullscreen(); 295 296 // Toggles whether the interface is shown. Called by 297 // PauseLongOperation and related functors. 298 void ToggleInterfaceHidden(); is_interface_hidden()299 bool is_interface_hidden() { return interface_hidden_; } 300 301 // Whether we should skip animations (such as all the Effect subclasses). should_skip_animations()302 int should_skip_animations() const { return globals_.skip_animations; } set_should_skip_animations(const int in)303 void set_should_skip_animations(const int in) { 304 globals_.skip_animations = in; 305 } 306 307 // Returns the ObjectSettings from the Gameexe for obj_num. The data 308 // from this method should be used by all subclasses of 309 // GraphicsSystem when deciding whether to render an object or not. 310 const ObjectSettings& GetObjectSettings(const int obj_num); 311 312 // Should be called by any of the drawing functions the screen is 313 // invalidated. 314 // 315 // For more information, please see section 5.10.4 of the RLDev 316 // manual, which deals with the behaviour of screen updates, and the 317 // various modes. 318 virtual void MarkScreenAsDirty(GraphicsUpdateType type); 319 320 // Forces a refresh of the screen the next time the graphics system 321 // executes. 322 virtual void ForceRefresh(); 323 screen_needs_refresh()324 bool screen_needs_refresh() const { return screen_needs_refresh_; } 325 void OnScreenRefreshed(); 326 327 // We keep a separate state about whether object state has been modified. We 328 // do this so that background object mutation in automatic mode plays nicely 329 // with LongOperations. mark_object_state_as_dirty()330 void mark_object_state_as_dirty() { object_state_dirty_ = true; } object_state_dirty()331 bool object_state_dirty() const { return object_state_dirty_; } 332 333 virtual void BeginFrame() = 0; 334 virtual void EndFrame() = 0; 335 virtual std::shared_ptr<Surface> EndFrameToSurface() = 0; 336 337 // Performs a full redraw of the screen. 338 void Refresh(std::ostream* tree); 339 340 // Draws the screen (as if refresh() was called), but draw to the returned 341 // surface instead of the screen. 342 std::shared_ptr<Surface> RenderToSurface(); 343 344 // Called from the game loop; Does everything that's needed to keep 345 // things up. 346 virtual void ExecuteGraphicsSystem(RLMachine& machine); 347 348 // Returns the size of the window in pixels. screen_size()349 const Size& screen_size() const { return screen_size_; } 350 351 // Returns a rectangle with an origin of (0,0) and a size returned by 352 // screen_size(). screen_rect()353 const Rect& screen_rect() const { return screen_rect_; } 354 355 virtual void AllocateDC(int dc, Size size) = 0; 356 virtual void SetMinimumSizeForDC(int dc, Size size) = 0; 357 virtual void FreeDC(int dc) = 0; 358 359 // Loads an image, optionally marking that this image has been loaded (if it 360 // is in the game's CGM table). 361 std::shared_ptr<const Surface> GetSurfaceNamedAndMarkViewed( 362 RLMachine& machine, 363 const std::string& short_filename); 364 365 // Just loads an image. This shouldn't be used for images that are destined 366 // for one of the DCs, since those can be CGs. 367 std::shared_ptr<const Surface> GetSurfaceNamed( 368 const std::string& short_filename); 369 370 virtual std::shared_ptr<Surface> GetHaikei() = 0; 371 372 virtual std::shared_ptr<Surface> GetDC(int dc) = 0; 373 374 virtual std::shared_ptr<Surface> BuildSurface(const Size& size) = 0; 375 376 virtual ColourFilter* BuildColourFiller() = 0; 377 378 // Clears and promotes objects. 379 void ClearAndPromoteObjects(); 380 381 // Calls render() on all foreground objects that need to be 382 // rendered. 383 void RenderObjects(std::ostream* tree); 384 385 // Creates rendering data for a graphics object from a G00, PDT or ANM file. 386 // Does not deal with GAN files. Those are built with a separate function. 387 GraphicsObjectData* BuildObjOfFile(const std::string& filename); 388 389 // Object getters 390 // layer == 0 for fg, layer == 1 for bg. 391 GraphicsObject& GetObject(int layer, int obj_number); 392 void SetObject(int layer, int obj_number, GraphicsObject& object); 393 394 // Frees the object data (but not the parameters). 395 void FreeObjectData(int obj_number); 396 void FreeAllObjectData(); 397 398 // Resets/reinitializes all the object parameters without deleting the loaded 399 // graphics object data. 400 void InitializeObjectParams(int obj_number); 401 void InitializeAllObjectParams(); 402 403 // The number of objects in a layer for this game. Defaults to 256 and can be 404 // overridden with #OBJECT_MAX. 405 int GetObjectLayerSize(); 406 407 LazyArray<GraphicsObject>& GetBackgroundObjects(); 408 LazyArray<GraphicsObject>& GetForegroundObjects(); 409 410 // Returns true if there's a currently playing animation. 411 bool AnimationsPlaying() const; 412 413 // Takes a snapshot of the current object state. This snapshot is saved 414 // instead of the current state of the graphics, since RealLive is a savepoint 415 // based system. 416 // 417 // (This operation isn't exceptionally expensive; internally GraphicsObject 418 // has multiple copy-on-write data structs to make this and object promotion a 419 // relativly cheap operation.) 420 void TakeSavepointSnapshot(); 421 422 // Sets DC0 to black and frees up DCs 1 through 16. 423 void ClearAllDCs(); 424 425 // Implementation of MouseMotionListener: 426 virtual void MouseMotion(const Point& new_location) override; 427 428 // Reset the system. Should clear all state for when a user loads a game. 429 virtual void Reset(); 430 431 // Access to the cgtable for the cg* functions. cg_table()432 CGMTable& cg_table() { return globals_.cg_table; } 433 434 // Access to the tone curve effects file tone_curve()435 ToneCurve& tone_curve() { return globals_.tone_curves; } 436 437 // Gets the emoji surface, if any. 438 std::shared_ptr<const Surface> GetEmojiSurface(); 439 440 // We have a cache of HIK scripts. This is done so we can load HIKScripts 441 // outside of loops. 442 void PreloadHIKScript(System& system, 443 int slot, 444 const std::string& name, 445 const boost::filesystem::path& file); 446 void ClearPreloadedHIKScript(int slot); 447 void ClearAllPreloadedHIKScripts(); 448 std::shared_ptr<HIKScript> GetHIKScript( 449 System& system, 450 const std::string& name, 451 const boost::filesystem::path& file); 452 453 // We have a cache of preloaded g00 files. 454 void PreloadG00(int slot, const std::string& name); 455 void ClearPreloadedG00(int slot); 456 void ClearAllPreloadedG00(); 457 std::shared_ptr<const Surface> GetPreloadedG00(const std::string& name); 458 459 protected: 460 typedef std::set<Renderable*> FinalRenderers; 461 renderer_begin()462 FinalRenderers::iterator renderer_begin() { return final_renderers_.begin(); } renderer_end()463 FinalRenderers::iterator renderer_end() { return final_renderers_.end(); } 464 cursor_pos()465 const Point& cursor_pos() const { return cursor_pos_; } 466 467 std::shared_ptr<MouseCursor> GetCurrentCursor(); 468 469 void SetScreenSize(const Size& size); 470 471 void DrawFrame(std::ostream* tree); 472 473 private: 474 // Gets a platform appropriate surface loaded. 475 virtual std::shared_ptr<const Surface> LoadSurfaceFromFile( 476 const std::string& short_filename) = 0; 477 478 // Default grp name (used in grp* and rec* functions where filename 479 // is '???') 480 std::string default_grp_name_; 481 482 // Default bgr name (used in bgr* functions where filename is 483 // '???') 484 std::string default_bgr_name_; 485 486 // Current screen update mode 487 DCScreenUpdateMode screen_update_mode_; 488 489 // Whether we display HIK or DC0. 490 GraphicsBackgroundType background_type_; 491 492 // Flag set to redraw the screen NOW 493 bool screen_needs_refresh_; 494 495 // Whether object state has been mutated since the last screen refresh. 496 bool object_state_dirty_; 497 498 // Whether it is the Graphics system's responsibility to redraw the 499 // screen. Some LongOperations temporarily take this responsibility 500 // to implement pretty fades and wipes 501 bool is_responsible_for_update_; 502 503 // Whether we should try to append subtitle_ in the window 504 // titlebar 505 bool display_subtitle_; 506 507 // cp932 encoded subtitle string 508 std::string subtitle_; 509 510 // Controls whether we render the interface (this can be 511 // temporarily toggled by the user at runtime) 512 bool interface_hidden_; 513 514 // Mutable global data to be saved in the globals file 515 GraphicsSystemGlobals globals_; 516 517 // Size of our display window. 518 Size screen_size_; 519 520 // Rectangle of the screen. 521 Rect screen_rect_; 522 523 // Queued origin/time pairs. The front of the queue shall be the current 524 // screen offset. 525 std::queue<std::pair<Point, int>> screen_shake_queue_; 526 527 // The last time |screen_shake_queue_| was modified. 528 unsigned int time_at_last_queue_change_; 529 530 // Immutable 531 struct GraphicsObjectSettings; 532 // Immutable global data that's constructed from the Gameexe.ini file. 533 std::unique_ptr<GraphicsObjectSettings> graphics_object_settings_; 534 535 struct GraphicsObjectImpl; 536 std::unique_ptr<GraphicsObjectImpl> graphics_object_impl_; 537 538 // Whether we should use a custom mouse cursor. Set while parsing the Gameexe 539 // file, and then left unchanged. We only use a custom mouse cursor if 540 // \#MOUSE_CURSOR is set in the Gameexe 541 bool use_custom_mouse_cursor_; 542 543 // Whether we should render any cursor. Controller by the bytecode. 544 bool show_cursor_from_bytecode_; 545 546 // Current cursor id. Initially set to \#MOUSE_CURSOR if the key exists. 547 int cursor_; 548 549 // Location of the cursor's hotspot 550 Point cursor_pos_; 551 552 // Current mouse cursor 553 std::shared_ptr<MouseCursor> mouse_cursor_; 554 555 // MouseCursor construction is nontrivial so cache everything we 556 // build: 557 typedef std::map<int, std::shared_ptr<MouseCursor>> MouseCursorCache; 558 MouseCursorCache cursor_cache_; 559 560 // A set of renderers 561 FinalRenderers final_renderers_; 562 563 // Our parent system object. 564 System& system_; 565 566 // Preloaded HIKScripts. 567 typedef std::pair<std::string, std::shared_ptr<HIKScript>> HIKArrayItem; 568 typedef LazyArray<HIKArrayItem> HIKScriptList; 569 HIKScriptList preloaded_hik_scripts_; 570 571 // Preloaded G00 images. 572 typedef std::pair<std::string, std::shared_ptr<const Surface>> G00ArrayItem; 573 typedef LazyArray<G00ArrayItem> G00ScriptList; 574 G00ScriptList preloaded_g00_; 575 576 // LRU cache filled with the last fifteen accessed images. 577 // 578 // This cache's contents are assumed to be immutable. 579 LRUCache<std::string, std::shared_ptr<const Surface>> image_cache_; 580 581 // Possible background script which drives graphics to the screen. 582 std::unique_ptr<HIKRenderer> hik_renderer_; 583 584 // Tuple used in RenderObjects(). Causes about a half megabyte of allocator 585 // churn per minute if we try to allocate it every time. 586 // 587 // The tuple is order, layer, depth, objid, GraphicsObject. Tuples are easy 588 // to sort. 589 typedef std::vector<std::tuple<int, int, int, int, GraphicsObject*>> 590 ToRenderVec; 591 ToRenderVec to_render_; 592 593 // boost::serialization support 594 friend class boost::serialization::access; 595 596 // boost::serialization forward declaration 597 template <class Archive> 598 void save(Archive& ar, const unsigned int file_version) const; 599 600 // boost::serialization forward declaration 601 template <class Archive> 602 void load(Archive& ar, const unsigned int file_version); 603 604 BOOST_SERIALIZATION_SPLIT_MEMBER() 605 }; 606 607 BOOST_CLASS_VERSION(GraphicsSystem, 1) 608 609 #endif // SRC_SYSTEMS_BASE_GRAPHICS_SYSTEM_H_ 610