1 /////////////////////////////////////////////////////////////////////////////// 2 // Copyright (C) 2004-2010 by The Allacrost Project 3 // All Rights Reserved 4 // 5 // This code is licensed under the GNU GPL version 2. It is free software 6 // and you may modify it and/or redistribute it under the terms of this license. 7 // See http://www.gnu.org/copyleft/gpl.html for details. 8 /////////////////////////////////////////////////////////////////////////////// 9 10 /** **************************************************************************** 11 *** \file map_dialogue.h 12 *** \author Tyler Olsen, roots@allacrost.org 13 *** \brief Header file for map mode dialogue 14 *** 15 *** \todo There is A LOT of indexing and id referencing in this code and in the 16 *** MapSprite's dialogue-related code. Currently very little is done to warn of 17 *** bad references (a sprite referencing a dialogue via an invalid ID) or bad 18 *** indexing (indexing a line of dialogue that does not exist/out of bounds errors). 19 *** The reason why things are this way right now is because sprites and dialogues 20 *** can be created in any order (and can even be created after the map has been loaded 21 *** and is in play). Because of this, the code may be referencing things that are 22 *** not yet created but will be shortly, so we don't want to print warnings about 23 *** those types of circumstances. What I think we should do is write a "CheckWarnings" 24 *** function or something similar that can be called after it is determined that 25 *** everything *should* be created and referenced properly. 26 *** ***************************************************************************/ 27 28 #ifndef __MAP_DIALOGUE_HEADER__ 29 #define __MAP_DIALOGUE_HEADER__ 30 31 // Allacrost utilities 32 #include "utils.h" 33 #include "defs.h" 34 35 // Allacrost engines 36 #include "script.h" 37 #include "video.h" 38 #include "gui.h" 39 40 // Local map mode headers 41 #include "map_utils.h" 42 43 namespace hoa_battle { 44 class BattleMode; 45 } 46 47 namespace hoa_map { 48 49 namespace private_map { 50 51 /** **************************************************************************** 52 *** \brief Represents dialogues between characters on a map 53 *** 54 *** Dialogues consist of multiple lines. Each line of a dialogue contains the 55 *** following information: 56 *** 57 *** -# The text of the line 58 *** -# An object ID that indicates who is currently speaking the line 59 *** -# A value that indicates the maximum time that the line should be displayed 60 *** -# A pointer to a script function to execute after the line is finished 61 *** 62 *** Dialogues may also have a set of options attached to it. Each set of options 63 *** in a dialogue are represented by an instance of the MapDialogueOptions class. 64 *** The options are stored in a vector of MapDialogueOptions object pointers. This 65 *** vector is indexed by the lines of dialogue, so options for line 3 would be stored 66 *** in _options[3]. A null value would mean there are no options for that line of 67 *** dialogue. 68 *** 69 *** Both the display time value and the script function pointer are optional and 70 *** do not need to be set for every line of dialogue. Dialogues may also be "blocked", 71 *** which means that they ignore the user's input while the dialogue is executing. 72 *** The map file retains the number of times each dialogue in the map has been 73 *** seen by the player so that subsequent visits to the map do not falsely display 74 *** sprites as having new dialogue. 75 *** 76 *** The standard order of lines in a dialogue begin with the first line and end 77 *** with the last (as stored in the lines container). However this need not be 78 *** the case and quite often is not. After reading a line, you may proceed to any 79 *** other line in a dialogue. The next line can be chosen either by selecting a 80 *** particular option presented on a line, or looking up the next line value in a 81 *** class container. It can also be explicity set when calling the ReadNextLine 82 *** method to finish reading the current line, although this is usually only done 83 *** by the dialogue manager when processing a selected dialogue option. 84 *** 85 *** When a dialogue is finished, usually the state of all speaker sprites is 86 *** restored (uch as the direction they were facing prior to the dialogue) so that 87 *** they can resume their prior activity. Also for dialogues which are "owned" by 88 *** a sprite (where owned simply means that the dialogue instance is retained in 89 *** the MapSprite#_dialogues container), the sprite is informed that the 90 *** dialogue has finished so that the sprite may re-check whether or not all 91 *** dialogues that it contains have been seen by the player. 92 *** 93 *** \todo MapDialogues need to be made more generic. They should not require a 94 *** speaker ID (ie we can have a "narrator" of sorts), they should not require 95 *** a portrait, and they should not be contained within the MapSprite class. 96 *** Dialogues should be retained by the DialogueWindow class and sprites should 97 *** reference these dialogues via a dialogue ID as appropriate. 98 *** ***************************************************************************/ 99 class MapDialogue { 100 public: 101 //! \param id The id number to represent the dialogue by (should be unique to other dialogue ids) 102 MapDialogue(uint32 id); 103 104 ~MapDialogue(); 105 106 /** \brief Adds a new line of text and to the dialogue 107 *** \param text The text to show on the screen 108 *** \param speaker_id The object ID of the speaker of this line of text 109 *** \param next_line The line of dialogue which should follow this one (a negative value indicates to end the dialogue) 110 *** \param time The maximum time in milliseconds to show the line of dialogue (default == infinite) 111 *** \param event The ID of an event to enact after selecting the option. A zero value indicates that no event is to occur. 112 *** 113 *** \todo This should take a ustring instead of a std::string, but we need better support 114 *** for ustring in scripts to do that first. 115 **/ 116 void AddText(std::string text, uint32 speaker_id, int32 next_line, uint32 event = 0, bool display_timer = false); 117 118 /** \brief Adds an option to the most recently added line of text 119 *** \param text The text for this particular option 120 *** \param next_line The index value of the next line of dialogue to display should this option be selected 121 *** (a negative value indicates to end the dialogue immediately after the option is selected) 122 *** \param event The ID of an event to enact after selecting the option. A zero value indicates that no event is to occur. 123 *** 124 *** \todo This should take a ustring instead of a std::string, but we need better support 125 *** for ustring in scripts to do that first. 126 **/ 127 void AddOption(std::string text, int32 next_line, uint32 event = 0); 128 129 /** \brief Proceeds the dialogue forward to display the next line 130 *** \param line Index value of the next line of dialogue to read. A negative value indicates 131 *** that there is no proceeding line and that the dialogue should finish. 132 *** \return False if the dialogue is finished, true otherwise. 133 **/ 134 bool ReadNextLine(int32 line); 135 136 /** \brief Returns the string of the dialogue's event name as it would be stored in the saved game file 137 *** \return The event string in the standard format of "dialogue#ID", where ID is the dialogue ID value 138 **/ GetEventName()139 std::string GetEventName() 140 { return std::string("dialogue#" + hoa_utils::NumberToString(_dialogue_id)); } 141 142 //! \brief Return true if this dialogue is available to be viewed (_times_seen is still less than _max_views) IsAvailable()143 bool IsAvailable() const 144 { if (_max_views < 0) return true; else return (static_cast<int32>(_times_seen) < _max_views); } 145 146 //! \brief Resets the _times_seen counter to zero ResetTimesSeen()147 void ResetTimesSeen() 148 { _times_seen = 0; } 149 150 //! \brief Increments the number of times this dialogue has been seen by the player IncrementTimesSeen()151 void IncrementTimesSeen() 152 { _times_seen++; } 153 154 //! \brief Indicates if this dialogue has already been seen by the player. HasAlreadySeen()155 bool HasAlreadySeen() const 156 { return (_times_seen != 0); } 157 158 // ----- Methods: retrieval of properties of the current line 159 160 //! \brief Returns true if the current line contains options. CurrentLineHasOptions()161 bool CurrentLineHasOptions() const 162 { if (_options[_current_line] != NULL) return true; else return false; } 163 164 //! \brief Returns the set of options for the current line (will be NULL if no options exist for this line) GetCurrentOptions()165 MapDialogueOptions* GetCurrentOptions() const 166 { return _options[_current_line]; } 167 168 //! \brief Returns an integer value of the next line of dialogue to be displayed for the current line GetCurrentNextLine()169 int32 GetCurrentNextLine() const 170 { return _next_lines[_current_line]; } 171 172 //! \brief Returns a reference to the unicode text string of the current line of dialogue GetCurrentText()173 const hoa_utils::ustring& GetCurrentText() const 174 { return _text[_current_line]; } 175 176 //! \brief Returns the object ID of the speaker of the current line of dialogue GetCurrentSpeaker()177 uint32 GetCurrentSpeaker() const 178 { return _speakers[_current_line]; } 179 180 //! \brief Returns the display time of the current line of dialogue GetCurrentTime()181 int32 GetCurrentTime() const 182 { return _display_times[_current_line]; } 183 184 //! \brief Returns the integer ID of the event that will be invoked after the current line of dialogue completes GetCurrentEvent()185 uint32 GetCurrentEvent() 186 { return _events[_current_line]; } 187 188 // ----- Methods: retrieval of properties of a specific line 189 190 //! \brief Returns the text of a specific line GetLineText(uint32 line)191 hoa_utils::ustring GetLineText(uint32 line) const 192 { if (line > _text.size()) return hoa_utils::ustring(); else return _text[line]; } 193 194 //! \brief Returns the object id of the speaker of a specific line GetLineSpeaker(uint32 line)195 uint32 GetLineSpeaker(uint32 line) const 196 { if (line > _speakers.size()) return 0; else return _speakers[line]; } 197 198 //! \brief Returns the display time of a specific line GetLineTime(uint32 line)199 int32 GetLineTime(uint32 line) const 200 { if (line > _display_times.size()) return -1; else return _display_times[line]; } 201 202 //! \brief Returns the ID of the event to execute after a specific line GetLineEvent(uint32 line)203 uint32 GetLineEvent(uint32 line) 204 { if (line > _events.size()) return 0; else return _events[line]; } 205 206 //! \name Class Member Access Functions 207 //@{ GetDialogueID()208 uint32 GetDialogueID() const 209 { return _dialogue_id; } 210 GetMaxViews()211 int32 GetMaxViews() const 212 { return _max_views; } 213 GetTimesSeen()214 int32 GetTimesSeen() const 215 { return _times_seen; } 216 GetLineCount()217 uint32 GetLineCount() const 218 { return _line_count; } 219 GetCurrentLine()220 uint32 GetCurrentLine() 221 { return _current_line;} 222 GetSaveState()223 bool GetSaveState() const 224 { return _save_state; } 225 IsBlocked()226 bool IsBlocked() const 227 { return _blocked; } 228 IsSaveState()229 bool IsSaveState() const 230 { return _save_state; } 231 SetTimesSeen(uint32 times)232 void SetTimesSeen(uint32 times) 233 { _times_seen = times; } 234 SetMaxViews(int32 views)235 void SetMaxViews(int32 views) 236 { _max_views = views; } 237 SetBlocked(bool block)238 void SetBlocked(bool block) 239 { _blocked = block; } 240 SetSaveState(bool state)241 void SetSaveState(bool state) 242 { _save_state = state; } 243 //@} 244 245 private: 246 //! \brief A unique identification number that represents this dialogue 247 uint32 _dialogue_id; 248 249 //! \brief Counts the number of time a player has seen this dialogue. 250 uint32 _times_seen; 251 252 //! \brief Declares the max number of times that this dialogue can be viewed (negative value indicates no limit) 253 int32 _max_views; 254 255 //! \brief Stores the amount of lines in the dialogue. 256 uint32 _line_count; 257 258 //! \brief An index to the current line to read. 259 uint32 _current_line; 260 261 //! \brief If true, dialogue will ignore user input and instead execute independently 262 bool _blocked; 263 264 //! \brief If true, the status of map sprites will be reset after the dialogue completes 265 bool _save_state; 266 267 //! \brief The event name for this dialogue that is stored in the saved game file, of the form "dialogue#" 268 std::string _event_name; 269 270 //! \brief The text of the conversation, split up into multiple lines 271 std::vector<hoa_utils::ustring> _text; 272 273 //! \brief A list of object ID numbers that declare the speaker of each line 274 std::vector<uint32> _speakers; 275 276 //! \brief The maximum display time for each line in the dialogue. A negative value indicates infinite time 277 std::vector<int32> _display_times; 278 279 //! \brief Holds indeces pointing to which line should follow each line of text. A negative value indicates that the dialogue should end. 280 std::vector<int32> _next_lines; 281 282 //! \brief A set of dialogue options indexed according to the line of dialogue that they belong to 283 std::vector<MapDialogueOptions*> _options; 284 285 //! \brief An optional MapEvent that may occur after each line 286 std::vector<uint32> _events; 287 }; // class MapDialogue 288 289 290 /** *************************************************************************************** 291 *** \brief A container class for option sets presented in dialogue 292 *** 293 *** When the player reads a dialogue he or she may be presented with a small number of options, 294 *** one of which the player must select. The selected option determines the path that the 295 *** dialogue will take, which may include an entire series of scripted events and sequences. 296 *** This class is responsible for containing all of the information necessary to make this 297 *** possible. It represents a set of options that the player must choose between. 298 *** 299 *** Instances of this class are populated as needed by the MapDialogue class. For each option, 300 *** the class contains an index to the next line of dialogue that should be read and an optional 301 *** pointer to a script function to execute, should that particular option be selected. 302 *** **************************************************************************************/ 303 class MapDialogueOptions { 304 friend class DialogueSupervisor; 305 306 public: MapDialogueOptions()307 MapDialogueOptions() 308 {} 309 ~MapDialogueOptions()310 ~MapDialogueOptions() 311 {} 312 313 /** \brief Adds a new option to the OptionBox 314 *** \param text The text for the new option 315 *** \param next_line An integer index of the next line of dialogue should this option be selected. 316 *** \param event The ID of an event to enact after selecting the option. Zero indicates that no event is to occur. 317 ***/ 318 void AddOption(hoa_utils::ustring text, int32 next_line, uint32 event = 0); 319 320 private: 321 //! \brief Contains the text of the dialogue, where each entry represents a single line 322 std::vector<hoa_utils::ustring> _text; 323 324 /** \brief A index containing the next line of dialogue that should follow each option 325 *** This is an index into the lines container for the MapDialogue object that is using this set of options. 326 **/ 327 std::vector<int32> _next_lines; 328 329 //! \brief An optional MapEvent that may occur as a result of selecting each option 330 std::vector<uint32> _events; 331 }; // class MapDialogueOptions 332 333 334 /** **************************************************************************** 335 *** \brief A display window for all GUI controls and graphics necessary to execute a dialogue 336 *** 337 *** This class, inheriting from the MenuWindow class, handles all visual control 338 *** and placement of a dialgoue. It serves primarily as a container class for 339 *** dialogue graphics. 340 *** ***************************************************************************/ 341 class DialogueWindow : public hoa_gui::MenuWindow { 342 friend class DialogueSupervisor; 343 344 public: 345 DialogueWindow(); 346 347 ~DialogueWindow(); 348 349 //! \brief Unhides the display window and prepares to begin a new dialogue display 350 void Initialize(); 351 352 //! \brief Clears all GUI structures and hides the display window 353 void Reset(); 354 355 /** \brief Draws the dialogue window and all other visuals 356 *** \param name A pointer to the name of the current speaker 357 *** \param portrait A pointer to the portrait image of the current speaker 358 *** 359 *** It is valid for either function argument to be NULL. This indicates 360 *** that the window should omit drawing this information. 361 **/ 362 void Draw(hoa_utils::ustring* name, hoa_video::StillImage* portrait); 363 364 private: 365 //! \brief A parchment paper image embedded within the dialogue window 366 hoa_video::StillImage _parchment_image; 367 368 //! \brief The nameplate image used along with the dialogue box image 369 hoa_video::StillImage _nameplate_image; 370 371 //! \brief The textbox used for rendering the dialogue text 372 hoa_gui::TextBox _display_textbox; 373 374 //! \brief The option box used for rendering dialogue options where applicable 375 hoa_gui::OptionBox _display_options; 376 }; // class DialogueWindow : public hoa_video::MenuWindow 377 378 379 /** **************************************************************************** 380 *** \brief Manages dialogue execution on maps 381 *** 382 *** The MapMode class creates an instance of this class to handle all dialogue 383 *** processing that occurs on maps. This includes containing the dialogue objects, 384 *** handling user input, processing of script events, and display timing of the 385 *** dialogue. 386 *** 387 *** \todo Add support so that the player may backtrack through lines in a 388 *** dialogue (without re-processing selected options or previous script events). 389 *** ***************************************************************************/ 390 class DialogueSupervisor { 391 public: 392 DialogueSupervisor(); 393 394 ~DialogueSupervisor(); 395 396 //! \brief Updates the state of visual elements such as scrolling text 397 void Update(); 398 399 //! \brief Draws the dialogue window, text, portraits, and other related visuals to the screen 400 void Draw(); 401 402 /** \brief Adds a new dialogue to be managed by the supervisor 403 *** \param dialogue Pointer to a MapDialogue object that was created with the new operator 404 *** 405 *** The dialogue to add must have a unique dialogue ID so that it can be added to the map. 406 *** If a dialogue with the same ID is already found within the map, then the dialogue will 407 *** not be added. All dialogues that are successfully added will be later deleted when this 408 *** class' destructor is invoked, so make sure you only pass in MapDialogue's that were 409 *** created with the "new" operator. 410 **/ 411 void AddDialogue(MapDialogue* dialogue); 412 413 /** \brief Adds a reference of a sprite to a dialogue 414 *** \param dialogue_id The ID number of the dialogue that the sprite wishes to reference 415 *** \param sprite_id The ID number of the sprite requesting to be referenced 416 *** 417 *** Sprites reference a dialogue so that when the dialogue's status is updated (view count incremented, etc), 418 *** the sprite will be informed that the dialogue has changed. 419 **/ 420 void AddSpriteReference(uint32 dialogue_id, uint32 sprite_id); 421 422 /** \brief Prepares the dialogue manager to begin processing a new dialogue 423 *** \param dialogue_id The id number of the dialogue to begin 424 **/ 425 void BeginDialogue(uint32 dialogue_id); 426 427 /** \brief Prepares the dialogue manager to begin processing a new dialogue 428 *** \param sprite A pointer to the map sprite that references the dialogue to begin processing 429 *** 430 *** This function operates the same as the other BeginDialogue function with one exception. It also 431 *** handles the calls necessary to update the map sprite. Specifically, making sure the sprite references a 432 *** valid dialogue and increments its next dialogue pointer. 433 **/ 434 void BeginDialogue(MapSprite* sprite); 435 436 //! \brief Immediately ends any dialogue that is taking place 437 void EndDialogue(); 438 439 /** \brief Returns a pointer to the MapDialogue with the requested ID value 440 *** \param dialogue_id The identification number of the dialogue to retrieve 441 *** \return A pointer to the dialogue requested, or NULL if no such dialogue was found 442 **/ 443 MapDialogue* GetDialogue(uint32 dialogue_id); 444 445 /** \brief Called whenever a map dialogue object's status is updated 446 *** \param dialogue_id The ID number of the dialogue which was updated 447 *** 448 *** The purpose of this function is to inform all map sprites which reference this dialogue 449 *** that it has been updated, and that they should update their associated data accordingly. For example, 450 *** it allows the sprite to re-examine whether or not it references any dialogue that has not been read by the player. 451 *** This function is called automatically by the class every time that this class ends a dialogue that is taking place. 452 **/ 453 void AnnounceDialogueUpdate(uint32 dialogue_id); 454 455 //! \name Class member access functions 456 //@{ GetDialogueState()457 DIALOGUE_STATE GetDialogueState() const 458 { return _state; } 459 GetCurrentDialogue()460 MapDialogue* GetCurrentDialogue() const 461 { return _current_dialogue; } 462 GetCurrentOptions()463 MapDialogueOptions* GetCurrentOptions() const 464 { return _current_options; } 465 GetLineTimer()466 int32 GetLineTimer() const 467 { return _line_timer; } 468 //@} 469 470 private: 471 //! \brief Contains all dialogues used in the map in a std::map structure. The dialogue ID is the map key 472 std::map<uint32, MapDialogue*> _all_dialogues; 473 474 /** \brief A container that stores map sprite IDs that are referenced with map dialogues 475 *** The map key is the MapDialogue ID and the vector of unsigned integers is each sprite that references the dialogue. 476 *** 477 *** \note The reason why these references are stored in this class rather than in the MapDialogue class is because 478 *** it would require that a MapDialogue object exist before a sprite could create a reference to it. This would require 479 *** an unnecessary dependency about which class objects are created first in the map script which should be avoided. 480 **/ 481 std::map<uint32, std::vector<uint32> > _sprite_references; 482 483 //! \brief Retains the current state of the dialogue 484 DIALOGUE_STATE _state; 485 486 //! \brief A pointer to the current piece of dialogue that is active 487 MapDialogue* _current_dialogue; 488 489 //! \brief A pointer to the current set of options for the active dialogue line 490 MapDialogueOptions* _current_options; 491 492 //! \brief A timer that is employed for dialogues which have a display time limit 493 int32 _line_timer; 494 495 //! \brief The window and associated GUI controls where the dialogue text and graphics should be displayed 496 DialogueWindow _dialogue_window; 497 498 // ---------- Private methods ---------- 499 500 //! \brief Updates the state of the dialogue when it is in the line state 501 void _UpdateLine(); 502 503 //! \brief Updates the state of the dialogue when it is in the option state 504 void _UpdateOptions(); 505 506 //! \brief Populates the dialogue window's option box with the current line option text 507 void _ConstructOptions(); 508 509 /** \brief Finishes the current dialogue line and moves the dialogue forward to the next line 510 *** \param next_line The index of the next line to read in the dialogue 511 *** This function will automatically end the dialogue if no line follows the current line 512 **/ 513 void _FinishLine(int32 next_line); 514 515 //! \brief Restores sprites to their states before this dialogue started 516 void _RestoreSprites(); 517 }; // class DialogueSupervisor 518 519 } // namespace private_map 520 521 } // namespace hoa_map 522 523 #endif // __MAP_DIALOGUE_HEADER__ 524