/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #ifndef _pfJournalBook_h #define _pfJournalBook_h ////////////////////////////////////////////////////////////////////////////// // // // pfJournalBook Class // // A generic, high-level, abstract method of creating various Myst-like // // books within the game with very little effort, while ensuring that they // // all remain consistent in appearance and operability. // // // ////////////////////////////////////////////////////////////////////////////// // // // Journal books are created via a journal book template. The template // // takes the form of an extremely simplified version of HTML. The esHTML // // has the following tags defined: // //

- Start of a new paragraph.

isn't used. // // - Places an image in-line with the text. Options are: // // align=left/right/center // // src= // // link= - Defines the image as clickable. When the user // // clicks the image, it will generate an event // // with the given event ID and send it to the // // calling Python handler. // // blend=none/alpha - Controls the blending method of drawing // // the image // // pos=, - Positions the image absolutely on the page // // using the upper-left pixel given. Absolute- // // placed images have no effect on text flow. // // glow= // // - Defines a glow special effect, which amounts // // to the image oscillating in opacity across // // the time interval specified. Optionally, you // // can also specify the min and max opacity, // // from 0 to 1. // // check= // // - Makes the image act as a checkbox, flipping // // between on and off states (as defined by the // // blending colors) on clicks. Default is either // // 1 for on or 0 for off. Link notifys are sent // // by checkboxes, with the event type // // "kNotifyCheckUnchecked" negative if the // // state is switching to off. Cannot be used // // with "glow". // // resize=yes/no - Defines whether the image will be resized // // with the book or not, defaults to yes // // opacity=0 to 1 - Defines the opacity of the image, 0 is // // completely transparent, 1 is opaque // // - Page break // // - Set the font for the following text. Options are: // // face= // // size= // // style=r/b/i/bi // // color=rrggbb - Hex color // // spacing= - line spacing in pixels // // - Optionally sets the cover mipmap for the book. // // src= - Selects the mipmap to be used, using the // // same search methods as . Unlike // // though, has no restriction on // // mipmap format. // // tint=rrggbb - Hex color for tinting the cover image // // tintfirst=yes/no - Tint the cover before applying decals, // // defaults to yes. A no option overrides the // // individual tint options on the tags // // - Optionally sets the margin size in pixels, default is 16 // // top= // // left= // // bottom= // // right= // // - Optionally sets the size of the book in percent (0-1), // // can be overridden by the SetBookSize funtion. // // height= // // width= // // - Optionally specifies a decal to be drawn on the cover in // // the specified position. Options are: // // src= - Selects the mipmap to be used, using the // // same search methods as . Unlike // // though, has no restrictions on // // mipmap format. // // pos=, - Specifies the position of the decal on the // // cover using the upper-left pixel // // align=left/right/center // // - Aligns the decal horizontally, overriding // // any x coord in the pos option, if no y has // // been specified, then it places it at the // // top of the cover // // resize=yes/no - Defines whether the image will be resized // // with the book or not, defaults to yes // // tint=yes/no - Defines whether or not this decal is tinted // // with the cover. Overridden by the tintfirst // // option on the tag. Defaults to no // // - Places a movie (.bik file) inline with the text. Options: // // src= - Selects the movie to be used. (nead search // // methods here eventually) // // align=left/right/center // // - Aligns the movie horizontally, overriding // // any x coord in the pos option // // link= - Defines the movie as clickable. When the // // User clicks the movie, it will generate an // // event with the given event ID and send it // // to the calling python handler // // pos=, - Specifies the position of the movie on the // // page using the upper left pixel given, does // // not influence text flow // // resize=yes/no - Defines whether the movie will be resized // // with the book or not, defaults to yes // // oncover=yes/no - Defines whether the movie will be placed on // // the cover or not, defaults to no. NOTE: // // setting this to yes causes the link option // // to be ignored since cover movies can't link // // loop=yes/no - Defines whether the movie will loop or not // // defaults to yes // // - Marks this book as editable (if the GUI supports it) // // // // The pages don't render until displayed. As a result, jumping to a given // // page requires each page from the current position to the destination // // to be rendered. Normally, this won't be a problem, because by default // // books open to the first page, at which point each page renders one // // at a time as the user flips through. // // // // The system assumes that no more than one book will ever actually be // // shown at any time. As a result, the internal geometry for displaying // // each book can be shared, reducing overhead and potential for errors. // // // ////////////////////////////////////////////////////////////////////////////// #include "hsTypes.h" #include "hsStlUtils.h" #include "hsTemplates.h" #include "hsColorRGBA.h" #include "../pnKeyedObject/hsKeyedObject.h" #include "../pnKeyedObject/plUoid.h" class pfEsHTMLChunk; class pfGUIDialogMod; class plLocation; class pfGUICheckBoxCtrl; class pfGUIButtonMod; class pfJournalDlgProc; class plDynamicTextMap; class pfGUIClickMapCtrl; class plLayerInterface; class plMipmap; class pfGUIProgressCtrl; class hsGMaterial; class plLayerBink; class pfGUIMultiLineEditCtrl; class pfJournalBook; class pfBookMultiLineEditProc; class pfBookData : public hsKeyedObject { public: enum WhichSide { kLeftSide = 0x01, kRightSide = 0x02, kBothSides = 0x03, kNoSides = 0 }; enum DynDisplayIndex { kLeftPage = 0, kRightPage, kTurnFrontPage, kTurnBackPage }; pfBookData(const char *guiName = nil); virtual ~pfBookData(); void LoadGUI(); // need this seperate because the plKey isn't setup until the constructor is done CLASSNAME_REGISTER(pfBookData); GETINTERFACE_ANY(pfBookData, hsKeyedObject); virtual hsBool MsgReceive(plMessage *pMsg); pfGUIDialogMod *Dialog() const {return fDialog;} pfGUICheckBoxCtrl *CoverButton() const {return fCoverButton;} pfGUICheckBoxCtrl *TurnPageButton() const {return fTurnPageButton;} pfGUIClickMapCtrl *LeftPageMap() const {return fLeftPageMap;} pfGUIClickMapCtrl *RightPageMap() const {return fRightPageMap;} plLayerInterface *CoverLayer() const {return fCoverLayer;} hsGMaterial *CoverMaterial() const {return fCoverMaterial;} hsGMaterial *PageMaterial(int index) const {if ((index<0)||(index>3)) return nil; else return fPageMaterials[index];} pfGUIButtonMod *LeftCorner() const {return fLeftCorner;} pfGUIButtonMod *RightCorner() const {return fRightCorner;} pfGUIProgressCtrl *WidthCtrl() const {return fWidthCtrl;} pfGUIProgressCtrl *HeightCtrl() const {return fHeightCtrl;} plMipmap *DefaultCover() const {return fDefaultCover;} pfJournalBook *CurBook() const {return fCurrBook;} hsBool StartedOpen() {return fStartedOpen;} hsBool CurrentlyOpen() {return fCurrentlyOpen;} hsBool CurrentlyTurning() {return fCurrentlyTurning;} hsBool IsEditable() {return fEditable;} WhichSide CurSFXPages() {return fCurrSFXPages;} void StartedOpen(hsBool startedOpen) {fStartedOpen = startedOpen;} void CurrentlyOpen(hsBool currentlyOpen) {fCurrentlyOpen = currentlyOpen;} void CurrentlyTurning(hsBool currentlyTurning) {fCurrentlyTurning = currentlyTurning;} void CurBook(pfJournalBook *curBook) {fCurrBook = curBook;} // Quick helper plDynamicTextMap *GetDTMap(UInt32 which); pfGUIMultiLineEditCtrl *GetEditCtrl(UInt32 which); // Seeks the width and height animations to set the desired book size. Sizes are in % across the animation void SetCurrSize(hsScalar w, hsScalar h); // Enables or disables the left and right page corners, to indicate current turnage state void UpdatePageCorners(WhichSide which); // Plays our book close animation void PlayBookCloseAnim(hsBool closeIt = true, hsBool immediate = false); // Finishes the start of the triggered page flip (once we're sure the animation is at the new frame) void StartTriggeredFlip(hsBool flipBackwards); // kill the flipping of a page... we are probably closing the book void KillPageFlip(); // Registers (or unregisters) for time messages so we can process special FX if we need to void RegisterForSFX(WhichSide whichSides); void HitEndOfControlList(Int32 cursorPos); void HitBeginningOfControlList(Int32 cursorPos); void EnableEditGUI(hsBool enable=true); void DisableEditGUI() {EnableEditGUI(false);} protected: friend class pfJournalDlgProc; enum Refs { kRefDialog = 0, kRefDefaultCover }; std::string fGUIName; // The pointer to our dialog pfGUIDialogMod *fDialog; // And other interesting pointers pfGUICheckBoxCtrl *fCoverButton; pfGUICheckBoxCtrl *fTurnPageButton; pfGUIClickMapCtrl *fLeftPageMap; pfGUIClickMapCtrl *fRightPageMap; plLayerInterface *fCoverLayer; hsGMaterial *fCoverMaterial; hsGMaterial *fPageMaterials[4]; pfGUIButtonMod *fLeftCorner; pfGUIButtonMod *fRightCorner; pfGUIProgressCtrl *fWidthCtrl; pfGUIProgressCtrl *fHeightCtrl; pfGUIMultiLineEditCtrl *fLeftEditCtrl; pfGUIMultiLineEditCtrl *fRightEditCtrl; pfGUIMultiLineEditCtrl *fTurnFrontEditCtrl; pfGUIMultiLineEditCtrl *fTurnBackEditCtrl; // Pointer to our default (base) cover mipmap plMipmap *fDefaultCover; // The current book using our data pfJournalBook *fCurrBook; // Which side(s) we're currently doing SFX for WhichSide fCurrSFXPages; // Base time to calc SFX anim positions from hsScalar fBaseSFXTime; hsBool fResetSFXFlag; hsBool fSFXUpdateFlip; // So we only update alternating pages every frame, to save processor time // What it says hsBool fCurrentlyTurning, fCurrentlyOpen, fStartedOpen; hsBool fEditable; Int32 fAdjustCursorTo; // Inits our dialog template void IInitTemplate(pfGUIDialogMod *templateDlg); // Process SFX for this frame void IHandleSFX(hsScalar currTime, WhichSide whichSide = kNoSides); // Yet another step in the page flip, to make SURE we're already showing the turning page before we fill in the page behind it void IFillUncoveringPage(hsBool rightSide); // Triggers the start of the page-flipping animation, as well as sets up the callback for when it's finished void ITriggerPageFlip(hsBool flipBackwards, hsBool immediate); // Finishes the triggered page flip, on callback void IFinishTriggeredFlip(hsBool wasBackwards); }; class pfJournalBook : public hsKeyedObject { public: // Enums of event types for the Book plNotifyMsg type enum NotifyTypes { kNotifyImageLink = 0, kNotifyShow, kNotifyHide, kNotifyNextPage, kNotifyPreviousPage, kNotifyCheckUnchecked, kNotifyClose, }; // The constructor takes in the esHTML source for the journal, along with // the name of the mipmap to use as the cover of the book. The callback // key is the keyed object to send event messages to (see tag). pfJournalBook( const char *esHTMLSource, plKey coverImageKey = nil, plKey callbackKey = nil, const plLocation &hintLoc = plLocation::kGlobalFixedLoc, const char *guiName = nil ); pfJournalBook( const wchar_t *esHTMLSource, plKey coverImageKey = nil, plKey callbackKey = nil, const plLocation &hintLoc = plLocation::kGlobalFixedLoc, const char *guiName = nil ); virtual ~pfJournalBook(); CLASSNAME_REGISTER( pfJournalBook ); GETINTERFACE_ANY( pfJournalBook, hsKeyedObject ); // Our required virtual virtual hsBool MsgReceive( plMessage *pMsg ); // Init the singleton, for client startup static void SingletonInit( void ); // Shutdown the singleton static void SingletonShutdown( void ); // loads a gui static void LoadGUI( const char *guiName ); // unloads a gui if we don't need it any more and want to free up memory static void UnloadGUI( const char *guiName ); // unloads all GUIs except for the default static void UnloadAllGUIs(); void SetGUI( const char *guiName ); // Shows the book, optionally starting open or closed void Show( hsBool startOpened = false ); /// NOTE: The following functions expose functionality that is normally /// handled by the book logic itself. So you should only need to use these /// in unusual circumstances. // Book handles hiding itself once someone clicks away. void Hide( void ); // Opens the book, optionally to the given page void Open( UInt32 startingPage = 0 ); // Closes the book. void Close( void ); // Advances forward one page void NextPage( void ); // Same, only back void PreviousPage( void ); // For completeness... void GoToPage( UInt32 pageNumber ); // See below. Just forces a full calc of the cached info void ForceCacheCalculations( void ); // Closes the book, then calls Hide() once it's done closing void CloseAndHide( void ); // Sets the book size scaling. 1,1 would be full size, 0,0 is the smallest size possible void SetBookSize( hsScalar width, hsScalar height ); // What page are we on? UInt32 GetCurrentPage( void ) const { return fCurrentPage; } // Set the margin (defaults to 16 pixels) void SetPageMargin( UInt32 margin ) { fPageTMargin = fPageLMargin = fPageBMargin = fPageRMargin = margin; } // Turns on or off page turning void AllowPageTurning( hsBool allow ) { fAllowTurning = allow; } // grabs a certain movie based on it's index in the source file plKey GetMovie( UInt8 index ); // turns on and off editing of the book void SetEditable( hsBool editable=true ); // returns the text contained by the edit controls std::string GetEditableText(); void SetEditableText(std::string text); protected: struct loadedMovie { pfEsHTMLChunk *movieChunk; plLayerBink *movieLayer; }; friend class pfJournalDlgProc; friend class pfBookData; // Our compiled esHTML source std::wstring fUncompiledSource; plLocation fDefLoc; hsTArray fHTMLSource; hsTArray fCoverDecals; // stored in a separate location so we can draw them all immediately hsTArray fLoadedMovies; // The key of the mipmap to use as the cover image plKey fCoverMipKey; bool fTintCover; hsColorRGBA fCoverTint; bool fTintFirst; // tint before applying decals? // Receiver key to send notifys to, if any plKey fCallbackKey; bool fCoverFromHTML; // Cached array of page starts in the esHTML source. Generated as we flip through // the book, so that going backwards can be done efficiently. hsTArray fPageStarts; // is the book done showing and ready for more page calculations hsBool fAreWeShowing; // Our current page UInt32 fCurrentPage; // are we editing this book? (adjusts how we draw and flip pages) hsBool fAreEditing; hsBool fWantEditing; // the code specifies that we want to edit, but the gui doesn't support it, we will check again if the gui changes hsBool fAllowTurning; // do we allow the user to turn pages? // The ending page. -1 until calculated by flipping to it UInt32 fLastPage; // Per book size hsScalar fWidthScale, fHeightScale; // Per book margin around the edge (defaults to 16 pixels) UInt32 fPageTMargin, fPageLMargin, fPageBMargin, fPageRMargin; // Some animation keys we use plKey fPageTurnAnimKey; // Current list of linkable image chunks we have visible on the screen, for quick hit testing hsTArray fVisibleLinks; static std::map fBookGUIs; std::string fCurBookGUI; enum Refs { kRefImage = 0 }; // Compiles the given string of esHTML source into our compiled chunk list hsBool ICompileSource( const wchar_t *source, const plLocation &hintLoc ); // Frees our source array void IFreeSource( void ); // Compile helpers UInt8 IGetTagType( const wchar_t *string ); hsBool IGetNextOption( const wchar_t *&string, wchar_t *name, wchar_t *option ); plKey IGetMipmapKey( const wchar_t *name, const plLocation &loc ); // Renders one (1) page into the given DTMap void IRenderPage( UInt32 page, UInt32 whichDTMap, hsBool suppressRendering = false ); // moves the movie layers from one material onto another void IMoveMovies( hsGMaterial *source, hsGMaterial *dest); // Starting at the given chunk, works backwards to determine the full set of current // font properties at that point, or assigns defaults if none were specified void IFindFontProps( UInt32 chunkIdx, const wchar_t *&face, UInt8 &size, UInt8 &flags, hsColorRGBA &color, Int16 &spacing ); // Find the last paragraph chunk and thus the last par alignment settings UInt8 IFindLastAlignment( void ) const; // Handle clicks on either side of the book void IHandleLeftSideClick( void ); void IHandleRightSideClick( void ); // Just sends out a notify to our currently set receiver key void ISendNotify( UInt32 type, UInt32 linkID = 0 ); // Close with a notify void ITriggerCloseWithNotify( hsBool closeNotOpen, hsBool immediate ); // Finish showing the book, due to the animation being done seeking void IFinishShow( hsBool startOpened ); // Find the current moused link, if any Int32 IFindCurrVisibleLink( hsBool rightNotLeft, hsBool hoverNotUp ); // Ensures that all the page starts are calced up to the given page (but not including it) void IRecalcPageStarts( UInt32 upToPage ); // Load (or unload) all the images for the book void ILoadAllImages( hsBool unload ); // Purge the DynaTextMaps void IPurgeDynaTextMaps( ); // Process a click on the given "check box" image void IHandleCheckClick( UInt32 idx, pfBookData::WhichSide which ); // Draw me an image! void IDrawMipmap( pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *mip, plDynamicTextMap *dtMap, UInt32 whichDTMap, hsBool dontRender ); // Movie functions loadedMovie *IMovieAlreadyLoaded(pfEsHTMLChunk *chunk); loadedMovie *IGetMovieByIndex(UInt8 index); plLayerBink *IMakeMovieLayer(pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *baseMipmap, UInt32 whichDTMap, hsBool dontRender); // Cover functions plLayerInterface *IMakeBaseLayer(plMipmap *image); plLayerInterface *IMakeDecalLayer(pfEsHTMLChunk *decalChunk, plMipmap *decal, plMipmap *baseMipmap); void ISetDecalLayers(hsGMaterial *material,hsTArray layers); }; #endif //_pfJournalBook_h