#include "Map Screen Interface.h" #include "popupbox.h" #include "interface control.h" #include "renderworld.h" #include "Text.h" #include "utilities.h" #include "Interface.h" #include "GameSettings.h" #include "Input.h" #include "Console.h" #include "lwstring.h" #include "message.h" #include "mousesystem.h" #include "Lua Interpreter.h" #include "popup_callback.h" #include "popup_class.h" ////////////////////////////////////////////////////////////////// // functions and variable declarations for binding the class with C callbacks ////////////////////////////////////////////////////////////////// vector gPopupRegionIndex; vector gPopupIndex; UINT32 gPopupRegionIndexCounter = 0; UINT32 gPopupIndexCounter = 0; UINT32 POPUP::nextid = 0; UINT32 POPUP::uiPOPUPBORDERS = 0; UINT32 POPUP::uiPOPUPTEX = 0; /////////////////////// // Lua vars ///////////////// BOOLEAN gfPopupInit = FALSE; POPUP * gPopup; // single popup /////////////////////// // Lua Popup functions ///////////////// void initPopupIndex(void) { /* int i; for (i=0; i~POPUP(); gPopupIndex[i] = NULL; } } */ } POPUP * findPopupInIndex( UINT32 cID ) { for(std::vector::iterator cPopup=gPopupIndex.begin(); cPopup != gPopupIndex.end(); ++cPopup) if ((*cPopup)->id == cID){ return (*cPopup); } #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Failed to find popup id: %i \n",cID); OutputDebugString( debugStr ); #endif return NULL; } void rebuildPopupIndex(void) { /*UINT8 i,j; POPUP* TempPopupIndex[MAX_POPUPS]; //cout << "cleaning index"<name = L"unnamed option"; this->action = 0; this->avail = 0; this->hover = 0; // set highlight color this->color_highlight = FONT_WHITE; // unhighlighted color this->color_foreground = FONT_LTGREEN; // background color this->color_background = FONT_BLACK; // shaded color..for darkened text this->color_shade = FONT_GRAY7 ; } POPUP_OPTION::~POPUP_OPTION(void) { if(this->action) delete(this->action); if(this->avail) delete(this->avail); if(this->hover) delete(this->hover); } POPUP_OPTION::POPUP_OPTION(std::wstring *newName, popupCallback * newFunction) { this->name = *newName; this->action = newFunction; this->avail = 0; this->hover = 0; // set highlight color this->color_highlight = FONT_WHITE; // unhighlighted color this->color_foreground = FONT_LTGREEN; // background color this->color_background = FONT_BLACK; // shaded color..for darkened text this->color_shade = FONT_GRAY7 ; } BOOLEAN POPUP_OPTION::setName( std::wstring * newName ) { this->name = *newName; return TRUE; } BOOLEAN POPUP_OPTION::setAction( popupCallback *fun ) { this->action = fun; return TRUE; } BOOLEAN POPUP_OPTION::setAvail( popupCallback *fun ) { this->avail = fun; return TRUE; } BOOLEAN POPUP_OPTION::setHover( popupCallback *fun ) { this->hover = fun; return TRUE; } BOOLEAN POPUP_OPTION::checkAvailability() { if (this->avail != 0) return ( this->avail->call() == true ); else if ( this->action == 0 ) // options with no action are always shaded return false; else return true; } BOOLEAN POPUP_OPTION::runHoverCallback(MOUSE_REGION * pRegion) { // TODO: pass the mouse region data to the function (if it can accept it) if (this->hover != 0) return ( this->hover->call() == true ); else return true; } BOOLEAN POPUP_OPTION::run() { // check if it's available at this time if ( this->action != 0 && this->checkAvailability() ) { this->action->call(); return TRUE; } else return FALSE; } BOOLEAN POPUP_OPTION::forceRun() { if (this->action != 0) this->action->call(); else return FALSE; return TRUE; } ////////////////////////////////////////////////////////////////// // POPUP_SUB_POPUP_OPTION methods ////////////////////////////////////////////////////////////////// // constructor POPUP_SUB_POPUP_OPTION::POPUP_SUB_POPUP_OPTION(void) : POPUP_OPTION(new wstring(L"Unnamed subPopup"),NULL) //TODO: possible memmory leak! { this->parent = NULL; this->initSubPopup(); } POPUP_SUB_POPUP_OPTION::POPUP_SUB_POPUP_OPTION(wstring* name) : POPUP_OPTION(name, NULL) { this->parent = NULL; this->initSubPopup(); } POPUP_SUB_POPUP_OPTION::POPUP_SUB_POPUP_OPTION(wstring* newName, const POPUP * parent) : POPUP_OPTION(newName, NULL) { this->parent = parent; this->initSubPopup(); } // destructor POPUP_SUB_POPUP_OPTION::~POPUP_SUB_POPUP_OPTION(void) { } void POPUP_SUB_POPUP_OPTION::showPopup() { // check if it's available at this time, if there is an avail function set if ( !this->AvailabilityFunctionSet() || this->checkAvailability() ) { if (!this->customPositionSet || this->customRule == POPUP_POSITION_RELATIVE) { this->positionSubPopup(); } this->subPopup->show(); } } void POPUP_SUB_POPUP_OPTION::hidePopup() { this->subPopup->hide(); } void POPUP_SUB_POPUP_OPTION::positionSubPopup() { INT16 x,y; if( this->customPositionSet ) { x = this->parent->X + this->parent->Dimensions.iLeft + this->parent->Dimensions.iRight + this->customX; y = this->parent->Y + this->customY; } else { x = this->parent->X + this->parent->Dimensions.iLeft + this->parent->Dimensions.iRight + 10; y = this->parent->Y + 10; } this->subPopup->setPosition(x,y); } BOOLEAN POPUP_SUB_POPUP_OPTION::run() { //cout << "running function " << utf8string::as_utf8(this->name.c_str()).c_str() << endl; this->showPopup(); return TRUE; } BOOLEAN POPUP_SUB_POPUP_OPTION::setPopupPosition(UINT16 x, UINT16 y, INT8 positioningRule){ this->customPositionSet = true; if (positioningRule != POPUP_POSITION_RELATIVE){ return this->subPopup->setPosition(x,y,positioningRule); } else { this->customX = x; this->customY = y; this->customRule = positioningRule; return this->subPopup->setPosition(this->parent->X + this->parent->Dimensions.iRight + x, this->parent->Y + y,POPUP_POSITION_TOP_LEFT); } } static void shadeOpenSubPopup( POPUP_SUB_POPUP_OPTION * opt ){ // make the option yellow while its open SetCurrentBox( opt->parent->getBoxId() ); SetStringForeground( opt->stringHandle, FONT_YELLOW); } extern void RenderTeamRegionBackground(); static void unShadeOpenSubPopup( POPUP_SUB_POPUP_OPTION * opt ){ // restore the option's color SetCurrentBox( opt->parent->getBoxId() ); SetStringForeground( opt->stringHandle, FONT_LTGREEN); // if in mapscreen, redraw the UI if( guiCurrentScreen == MAP_SCREEN ){ fTeamPanelDirty = TRUE; fMapPanelDirty = TRUE; RenderTeamRegionBackground(); } } void POPUP_SUB_POPUP_OPTION::initSubPopup() { this->subPopup = new POPUP( (CHAR*) wstring(this->name).c_str() ); this->subPopup->setCallback(POPUP_CALLBACK_SHOW,new popupCallbackFunction( &shadeOpenSubPopup, this )); this->subPopup->setCallback(POPUP_CALLBACK_HIDE,new popupCallbackFunction( &unShadeOpenSubPopup, this )); this->customPositionSet = false; // set highlight color this->color_highlight = FONT_WHITE; // unhighlighted color this->color_foreground = FONT_LTGREEN; // background color this->color_background = FONT_BLACK; // shaded color..for darkened text this->color_shade = FONT_GRAY7 ; } void POPUP_SUB_POPUP_OPTION::destroySubPopup() { this->subPopup->~POPUP(); } ////////////////////////////////////////////////////////////////// // POPUP methods ////////////////////////////////////////////////////////////////// // constructors POPUP::POPUP(void) { strcpy((char*) this->name, (char*) "unNamed popup"); this->optionCount = 0; this->addToIndex(); this->setInitialValues(); } POPUP::POPUP(CHAR* name) { strcpy((char*) this->name, (char*) name); this->optionCount = 0; this->addToIndex(); this->setInitialValues(); #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Created popup: %s \n",this->name); OutputDebugString( debugStr ); #endif } // destructor POPUP::~POPUP(void) { if(this->PopupVisible) this->hide(); for (UINT16 i = 0; ioptions.size(); i++) { this->options[i]->~POPUP_OPTION(); } for (UINT16 i = 0; isubPopupOptions.size(); i++) { this->subPopupOptions[i]->subPopup->hide(); this->subPopupOptions[i]->~POPUP_SUB_POPUP_OPTION(); } #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Destroyed popup: %s \n",this->name); OutputDebugString( debugStr ); #endif this->removeFromIndex(); if (this->initCallback) this->initCallback->~popupCallback(); if (this->ShowCallback) this->ShowCallback->~popupCallback(); if (this->HideCallback) this->HideCallback->~popupCallback(); if (this->EndCallback) { this->EndCallback->call(); this->EndCallback->~popupCallback(); } } // callback handlers BOOLEAN POPUP::setCallback(UINT8 type, popupCallback * callback){ if(callback == NULL) return FALSE; switch(type){ case POPUP_CALLBACK_INIT: if (this->initCallback) this->initCallback->~popupCallback(); this->initCallback = callback; break; case POPUP_CALLBACK_END: if (this->EndCallback) this->EndCallback->~popupCallback(); this->EndCallback = callback; break; case POPUP_CALLBACK_SHOW: if (this->ShowCallback) this->ShowCallback->~popupCallback(); this->ShowCallback = callback; break; case POPUP_CALLBACK_HIDE: if (this->HideCallback) this->HideCallback->~popupCallback(); this->HideCallback = callback; break; default: return false; } return true; } BOOLEAN POPUP::isCallbackSet(UINT8 type){ switch(type){ case POPUP_CALLBACK_INIT: if (this->initCallback) return true; break; case POPUP_CALLBACK_END: if (this->EndCallback) return true; break; case POPUP_CALLBACK_SHOW: if (this->ShowCallback) return true; break; case POPUP_CALLBACK_HIDE: if (this->HideCallback) return true; break; default: return false; } return false; } // popup index handlers BOOLEAN POPUP::addToIndex() { /* if (gPopupIndexCounter == MAX_POPUPS) { rebuildPopupIndex(); // if index full, try to rebuild it before giving up if (gPopupIndexCounter == MAX_POPUPS) return FALSE; } */ // assign the ID and increment the counter. this->id = this->nextid; this->nextid++; //gPopupIndex[gPopupIndexCounter] = this; gPopupIndex.push_back( this ); gPopupIndexCounter++; return TRUE; } BOOLEAN POPUP::removeFromIndex() { for(std::vector::iterator cPopup=gPopupIndex.begin(); cPopup != gPopupIndex.end(); ++cPopup) if ((*cPopup)->id == this->id){ gPopupIndex.erase(cPopup); return TRUE; } #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Failed to remove popup from index: %i \n",this->id); OutputDebugString( debugStr ); #endif return FALSE; } // set defaults void POPUP::setInitialValues(void) { this->Position.iX=20; this->Position.iY=20; this->OrigPosition.iX = 0 ; this->OrigPosition.iY = 0 ; this->Dimensions.iLeft=0; this->Dimensions.iTop=0; this->Dimensions.iBottom=100; this->Dimensions.iRight=120; this->BoxesX = 20; this->BoxesY = 20; // the x,y position of the pop up in tactical this->X = 20; this->Y = 20; this->positioningRule = POPUP_POSITION_TOP_LEFT; //int i = 0; //for (i=0; ioptions[i] = NULL; // //this->MenuRegion[i] = NULL; //} this->PopupVisible = FALSE; this->FirstClickInScreenMask = FALSE; this->BoxPersist = FALSE; this->subPopupOptionCount = 0; this->optionCount = 0; this->initCallback = NULL; this->EndCallback = NULL; this->ShowCallback = NULL; this->HideCallback = NULL; } // setup functions POPUP_OPTION * POPUP::addOption(wstring * name, popupCallback* action) { if (this->optionCount < POPUP_MAX_OPTIONS) { this->options.push_back( new POPUP_OPTION ( name, action ) ); this->optionCount++; return this->options.back(); } else return NULL; } /* INT16 POPUP::findFreeOptionIndex() { int i; for (i=0; ioptions[i] == NULL ) return i; return -1; } */ BOOLEAN POPUP::addOption(POPUP_OPTION &option) { if (this->optionCount < POPUP_MAX_OPTIONS) { this->options.push_back( &option ); this->optionCount++; return TRUE; } else return FALSE; } POPUP_OPTION * POPUP::getOption(UINT16 n) { if (n < this->options.size() && this->options[n]) return this->options[n]; return NULL; } POPUP * POPUP::addSubMenuOption(wstring * name) { if (this->subPopupOptionCount < POPUP_MAX_SUB_POPUPS) { this->subPopupOptions.push_back( new POPUP_SUB_POPUP_OPTION ( name, this ) ); this->subPopupOptionCount++; return this->subPopupOptions.back()->subPopup; } else return NULL; } BOOL POPUP::addSubMenuOption( POPUP_SUB_POPUP_OPTION* sub ) { if (this->subPopupOptionCount < POPUP_MAX_SUB_POPUPS) { sub->parent = this; this->subPopupOptions.push_back( sub ); this->subPopupOptionCount++; return TRUE; } else return FALSE; } POPUP_SUB_POPUP_OPTION * POPUP::getSubPopupOption(UINT8 n) { if (n < this->subPopupOptions.size() && this->subPopupOptions[n]) return this->subPopupOptions[n]; else return NULL; } void POPUP::recalculateWidth(void){ this->Dimensions.iRight = this->getCurrentWidth(); } INT16 POPUP::getCurrentWidth(void){ INT16 longestString = 0, longestStringTmp; CHAR16 sString[ 128 ]; if (this->subPopupOptionCount > 0) for(std::vector::iterator cOption=this->subPopupOptions.begin(); cOption != this->subPopupOptions.end(); ++cOption) { swprintf( sString, (*cOption)->name.c_str() ); longestStringTmp = StringPixLength( sString, MAP_SCREEN_FONT); if( longestString < longestStringTmp ) longestString = longestStringTmp; } if (this->optionCount > 0) for(std::vector::iterator cOption=this->options.begin(); cOption != this->options.end(); ++cOption) { swprintf( sString, (*cOption)->name.c_str() ); longestStringTmp = StringPixLength( sString, MAP_SCREEN_FONT); if( longestString < longestStringTmp ) longestString = longestStringTmp; } return longestString + 10; // 10+ marginLeft(6) + marginRight(4) } INT16 POPUP::getCurrentHeight(void){ // linespace = 2; margin = 6+4 return ( this->optionCount + this->subPopupOptionCount ) * ( GetFontHeight(MAP_SCREEN_FONT) + 2 ) +10; } SGPRect POPUP::getBoxDimensions(){ SGPRect tmp; GetBoxSize( this->boxId, &tmp ); return tmp; } SGPPoint POPUP::getBoxPosition(){ SGPPoint tmp; GetBoxPosition( this->boxId, &tmp ); return tmp; } BOOLEAN POPUP::setPosition(UINT16 x, UINT16 y, UINT8 positioningRule ) { //TODO: check if the desired position is valid. // why the hell are there three sets of box positions ? this->Position.iX=x; this->Position.iY=y; this->BoxesX = x; this->BoxesY = y; this->X = x; this->Y = y; // make that four this->OrigPosition.iX = x ; this->OrigPosition.iY = y ; this->positioningRule = positioningRule; #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Positioning popup %i at (x/y) %i / %i ; positioning rule: %i \n",this->id, x, y, positioningRule); OutputDebugString( debugStr ); #endif return TRUE; } ////////////////////////////////// // usage functions BOOLEAN POPUP::show() { if (this->PopupVisible) { // this->RebuildBox(); return false; } if (this->ShowCallback) this->ShowCallback->call(); // can't show an empty popup. if ( (this->optionCount + this->subPopupOptionCount) == 0) return FALSE; // set the box to visible this->PopupVisible = TRUE; // should take care of creating the box this->CreatePopUpBoxes(); // set the position before creating the screen mask and mouse regions this->DetermineBoxPositions(); // create all the GUI areas this->CreateScreenMask(); this->CreateMouseRegions(); // make SURE the regions have been repositioned this->CheckAndUpdateTacticalPopUpPositions(); // shade unavaliable options this->HandleShadingOfLines(); #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Showing popup id: %i \n",this->boxId); OutputDebugString( debugStr ); #endif // showtime ! ShowBox(this->boxId); return TRUE; } void POPUP::hideAfter(){ this->hideAfterRun = TRUE; } BOOLEAN POPUP::hide() { if (!this->PopupVisible) { #ifdef JA2TESTVERSION // __debugbreak(); #else // Assert(false); #endif return false; } for (UINT16 i = 0; isubPopupOptions.size(); i++) { this->subPopupOptions[i]->subPopup->hide(); } #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Hiding popup id: %i \n",this->boxId); OutputDebugString( debugStr ); #endif this->DestroyMouseRegions(); this->DestroyScreenMask(); HideBox(this->boxId); RemoveBox(this->boxId); this->DestroyPopUpBoxes(); this->boxId = -1; this->PopupVisible = FALSE; if (this->HideCallback) this->HideCallback->call(); return TRUE; } BOOLEAN POPUP::callOption(int optIndex) { this->hideAfterRun = FALSE; if ( this->optionCount > 0 && optIndex < POPUP_MAX_OPTIONS && optIndex < optionCount && optIndex >= 0) { BOOLEAN callbackStatus = this->options[optIndex]->run(); if(!this->hideAfterRun) { return callbackStatus; } else { return this->hide(); } } else return FALSE; } BOOLEAN POPUP::forceDraw(){ if (!this->PopupVisible) { // set the box to visible this->PopupVisible = TRUE; // should take care of creating the box this->CreatePopUpBoxes(); // set the position before creating the screen mask and mouse regions this->DetermineBoxPositions(); // create all the GUI areas this->CreateScreenMask(); this->CreateMouseRegions(); // make SURE the regions have been repositioned this->CheckAndUpdateTacticalPopUpPositions(); // shade unavaliable options this->HandleShadingOfLines(); // showtime ! ShowBox(this->boxId); } else { this->RebuildBox(); } DisplayOnePopupBox(this->boxId, FRAME_BUFFER); ForceUpDateOfBox(this->boxId); return true; } //////////////////////////// // private utility functions ////// // external flag access functions ////// BOOLEAN POPUP::TeamPanelDirty(void) { extern BOOLEAN fTeamPanelDirty; return fTeamPanelDirty; } BOOLEAN POPUP::CharacterInfoPanelDirty(void) { extern BOOLEAN fCharacterInfoPanelDirty; return fCharacterInfoPanelDirty; } BOOLEAN POPUP::MapScreenBottomDirty(void) { extern BOOLEAN fMapScreenBottomDirty; return fMapScreenBottomDirty; } BOOLEAN POPUP::RenderPBInterface(void) { extern BOOLEAN gfRenderPBInterface ; return gfRenderPBInterface ; } void POPUP::setTeamPanelDirty(BOOLEAN flag ) { extern BOOLEAN fTeamPanelDirty; fTeamPanelDirty = flag; } void POPUP::setCharacterInfoPanelDirty(BOOLEAN flag ) { extern BOOLEAN fCharacterInfoPanelDirty; fCharacterInfoPanelDirty = flag; } void POPUP::setMapScreenBottomDirty(BOOLEAN flag ) { extern BOOLEAN fMapScreenBottomDirty; fMapScreenBottomDirty = flag; } void POPUP::setRenderPBInterface(BOOLEAN flag ) { extern BOOLEAN gfRenderPBInterface ; gfRenderPBInterface = flag; } ////// // internal box setup methods ////// // rewrites all the text lines in the box and adjusts // the box's size and mouse regions to potential changes // less expensive then hide();show(); void POPUP::RebuildBox( void ) { if ( this->boxId != -1 && this->id < POPUP::nextid ) { // make sure we're working with the right popup! SetCurrentBox( this->boxId ); // remove all the 'old' text from the box RemoveAllCurrentBoxStrings(); // add the sub popups (they go first) this->AddSubPopupStrings(); // add the options this->AddOptionStrings(); // set fonts and what not this->UpdateTextProperties(); // adjust the mouse regions (might've changed substantially) this->AdjustMouseRegions(); // shade/unshade accordingly this->HandleShadingOfLines(); } else if ( this->id > POPUP::nextid ) { // chances are buggered up data will have impossible boxid #ifdef JA2TESTVERSION __debugbreak(); #else Assert(false); #endif } } BOOLEAN POPUP::CreateBox() { // this->Position.iX = this->OrigPosition.iX; if( giBoxY != 0 ) { this->Position.iY = giBoxY; } // create basic box CreatePopUpBox(&this->boxId, this->Dimensions, this->Position, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); // which buffer will box render to SetBoxBuffer(this->boxId, FRAME_BUFFER); // border type? SetBorderType(this->boxId,POPUP::uiPOPUPBORDERS); // background texture SetBackGroundSurface(this->boxId, POPUP::uiPOPUPTEX); // margin sizes SetMargins(this->boxId, 6, 6, 4, 4 ); // space between lines SetLineSpace(this->boxId, 2); // set current box to this one SetCurrentBox( this->boxId ); // add the sub popups (they go first) this->AddSubPopupStrings(); // add the options this->AddOptionStrings(); // set fonts and what not this->UpdateTextProperties(); //UpdateMapScreenthis->Positions(); return TRUE; } void POPUP::AddSubPopupStrings() { UINT32 hStringHandle; CHAR16 sString[ 128 ]; if (this->subPopupOptionCount > 0) for(std::vector::iterator cOption=this->subPopupOptions.begin(); cOption != this->subPopupOptions.end(); ++cOption) { swprintf( sString, (*cOption)->name.c_str() ); AddMonoString((UINT32 *)&hStringHandle, sString ); // make sure it is unhighlighted UnHighLightLine(hStringHandle); // update options string handle for setting colors (*cOption)->stringHandle = hStringHandle; } } void POPUP::AddOptionStrings() { UINT32 hStringHandle; CHAR16 sString[ 128 ]; if (this->optionCount > 0) for(std::vector::iterator cOption=this->options.begin(); cOption != this->options.end(); ++cOption) { swprintf( sString, (*cOption)->name.c_str() ); AddMonoString((UINT32 *)&hStringHandle, sString ); // make sure it is unhighlighted UnHighLightLine(hStringHandle); // update options string handle for setting colors (*cOption)->stringHandle = hStringHandle; } } void POPUP::UpdateTextProperties() { // set font type SetBoxFont(this->boxId, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(this->boxId, FONT_WHITE); // unhighlighted color SetBoxForeground(this->boxId, FONT_LTGREEN); // background color SetBoxBackground(this->boxId, FONT_BLACK); // shaded color..for darkened text SetBoxShade( this->boxId, FONT_GRAY7 ); // alt shaded color SetBoxSecondaryShade( this->boxId, FONT_YELLOW ); if (this->subPopupOptionCount > 0) for(std::vector::iterator cOption=this->subPopupOptions.begin(); cOption != this->subPopupOptions.end(); ++cOption) { SetStringForeground( (*cOption)->stringHandle, (*cOption)->color_foreground); SetStringBackground( (*cOption)->stringHandle, (*cOption)->color_background); SetStringHighLight( (*cOption)->stringHandle, (*cOption)->color_highlight); SetStringShade( (*cOption)->stringHandle, (*cOption)->color_shade); } if (this->optionCount > 0) for(std::vector::iterator cOption=this->options.begin(); cOption != this->options.end(); ++cOption) { SetStringForeground( (*cOption)->stringHandle, (*cOption)->color_foreground); SetStringBackground( (*cOption)->stringHandle, (*cOption)->color_background); SetStringHighLight( (*cOption)->stringHandle, (*cOption)->color_highlight); SetStringShade( (*cOption)->stringHandle, (*cOption)->color_shade); } // resize box to text ResizeBoxToText( this->boxId ); } BOOLEAN POPUP::CreateDestroyPopUpBoxes( void ) { static BOOLEAN fCreated = FALSE; BOOLEAN returnStatus = TRUE; if( ( this->PopupVisible == TRUE ) && ( fCreated == FALSE ) ) { returnStatus = this->CreatePopUpBoxes(); fCreated = TRUE; } else if( ( this->PopupVisible == FALSE ) && ( fCreated == TRUE ) ) { returnStatus = this->DestroyPopUpBoxes(); fCreated = FALSE; } return( returnStatus ); } BOOLEAN POPUP::CreatePopUpBoxes() { VSURFACE_DESC vs_desc; VOBJECT_DESC VObjectDesc; // load textures VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; FilenameForBPP("INTERFACE\\popup.sti", VObjectDesc.ImageFile); CHECKF(AddVideoObject(&VObjectDesc, &POPUP::uiPOPUPBORDERS)); vs_desc.fCreateFlags = VSURFACE_CREATE_FROMFILE | VSURFACE_SYSTEM_MEM_USAGE; strcpy(vs_desc.ImageFile, "INTERFACE\\popupbackground.pcx"); CHECKF(AddVideoSurface(&vs_desc, &POPUP::uiPOPUPTEX)); // does some other stff this->CreateBox(); return TRUE; } BOOLEAN POPUP::DestroyPopUpBoxes() { if ( gPopupIndex.size() < 2 ){ DeleteVideoObjectFromIndex(POPUP::uiPOPUPBORDERS); DeleteVideoSurfaceFromIndex(POPUP::uiPOPUPTEX); } gfIgnoreScrolling = FALSE; return TRUE; } void POPUP::CreateDestroyScreenMask( void ) { static BOOLEAN fCreated = FALSE; // not created, create if( ( fCreated == FALSE ) ) { this->CreateScreenMask(); fCreated = TRUE; } else if( ( fCreated == TRUE ) ) { this->DestroyScreenMask(); fCreated = FALSE; } return; } void POPUP::CreateScreenMask( void ) { /* void (POPUP::*ScreenMaskBtnCallbackMethod) (MOUSE_REGION *, INT32 ); ScreenMaskBtnCallbackMethod = &POPUP::ScreenMaskBtnCallback; */ // will create a screen mask to catch mouse input to disable LPC menus MSYS_DefineRegion( &this->ScreenMaskRegion, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, POPUP_SCREEN_MASK_PRIORITY , MSYS_NO_CURSOR, MSYS_NO_CALLBACK, popupMaskCallback ); // if in tactical... if ( !(guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN) ) MSYS_ChangeRegionCursor( &this->ScreenMaskRegion, 0 ); if (!registerPopupRegion( this->ScreenMaskRegion.IDNumber, this->id)){ #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Failed to register screen mask for box id: %i \n",this->boxId); OutputDebugString( debugStr ); #endif } return; } void POPUP::DestroyScreenMask( void ) { unregisterPopupRegion(this->ScreenMaskRegion.IDNumber); // unregister it before removing it // otherwise the ID might get lost somewhere MSYS_RemoveRegion( &this->ScreenMaskRegion ); return; } void POPUP::CreateDestroyMouseRegions( void ) { static BOOLEAN fCreated = FALSE; if( ( this->PopupVisible == TRUE ) && ( fCreated == FALSE ) ) { this->CreateMouseRegions(); fCreated = TRUE; } else if( ( this->PopupVisible == FALSE ) && ( fCreated == TRUE ) ) { this->DestroyMouseRegions(); fCreated = FALSE; } } void POPUP::CreateMouseRegions( void ) { UINT32 iCounter = 0; UINT32 iTotal = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition={0,0}; INT32 iBoxWidth = 0; SGPRect pDimensions={0,0,0,0}; static BOOLEAN fShowRemoveMenu = FALSE; gfIgnoreScrolling = FALSE; if( ( guiCurrentScreen == MAP_SCREEN ) ) { SetBoxPosition( this->boxId, this->Position ); } // grab height of font iFontHeight = GetLineSpace( this->boxId ) + GetFontHeight( GetBoxFont( this->boxId ) ); // get x.y position of box GetBoxPosition( this->boxId, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( this->boxId, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( this->boxId ); // define regions for sub popups for( iCounter = 0; iCounter < this->subPopupOptionCount ; iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &this->MenuRegion[ iTotal ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * iTotal ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * ( iTotal + 1 ) ), POPUP_OPTION_PRIORITY , MSYS_NO_CURSOR, popupMouseMoveCallback, popupMouseClickCallback ); MSYS_SetRegionUserData( &this->MenuRegion[ iTotal ], 0, iCounter ); MSYS_SetRegionUserData( &this->MenuRegion[ iTotal ], 1, REGION_SUB_POPUP ); MSYS_SetRegionUserData( &this->MenuRegion[ iTotal ], 2, iTotal ); if (!registerPopupRegion( this->MenuRegion[ iTotal ].IDNumber, this->id)){} iTotal++; } // define regions for options for( iCounter = 0; iCounter < this->optionCount ; iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &this->MenuRegion[ iTotal ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * iTotal ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * ( iTotal + 1 ) ), POPUP_OPTION_PRIORITY , MSYS_NO_CURSOR, popupMouseMoveCallback, popupMouseClickCallback ); MSYS_SetRegionUserData( &this->MenuRegion[ iTotal ], 0, iCounter ); MSYS_SetRegionUserData( &this->MenuRegion[ iTotal ], 1, REGION_OPTION ); MSYS_SetRegionUserData( &this->MenuRegion[ iTotal ], 2, iTotal ); if (!registerPopupRegion( this->MenuRegion[ iTotal ].IDNumber, this->id)){} iTotal++; } // unhighlight all strings in box UnHighLightBox( this->boxId ); this->CheckAndUpdateTacticalPopUpPositions(); } void POPUP::DestroyMouseRegions( void ) { UINT32 iCounter = 0; for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( this->boxId ); iCounter++ ) { unregisterPopupRegion(this->MenuRegion[ iCounter ].IDNumber); MSYS_RemoveRegion( &this->MenuRegion[ iCounter ] ); } SetRenderFlags( RENDER_FLAG_FULL ); } void POPUP::AdjustMouseRegions( void ) { UINT32 iCounter = 0; UINT32 iTotal = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; INT32 iBoxWidth = 0; SGPPoint pPosition={0,0}; SGPRect pDimensions={0,0,0,0}; // grab height of font iFontHeight = GetLineSpace( this->boxId ) + GetFontHeight( GetBoxFont( this->boxId ) ); // get x.y position of box GetBoxPosition( this->boxId, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( this->boxId, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; // adjust regions for sub popups for( iCounter = 0; iCounter < this->subPopupOptionCount ; iCounter++ ) { this->MenuRegion[ iTotal ].RegionTopLeftX = ( INT16 )( iBoxXPosition ); this->MenuRegion[ iTotal ].RegionTopLeftY = ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * iTotal ); this->MenuRegion[ iTotal ].RegionBottomRightX = ( INT16 )( iBoxXPosition + iBoxWidth ); this->MenuRegion[ iTotal ].RegionBottomRightY = ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * ( iTotal + 1 ) ); iTotal++; } // adjust regions for options for( iCounter = 0; iCounter < this->optionCount ; iCounter++ ) { this->MenuRegion[ iTotal ].RegionTopLeftX = ( INT16 )( iBoxXPosition ); this->MenuRegion[ iTotal ].RegionTopLeftY = ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * iTotal ); this->MenuRegion[ iTotal ].RegionBottomRightX = ( INT16 )( iBoxXPosition + iBoxWidth ); this->MenuRegion[ iTotal ].RegionBottomRightY = ( INT16 )( iBoxYPosition + GetTopMarginSize( this->boxId ) + ( iFontHeight ) * ( iTotal + 1 ) ); iTotal++; } } void POPUP::ScreenMaskBtnCallback(MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for screen mask region if( ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) || ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) ) { if( this->FirstClickInScreenMask == TRUE ) { this->FirstClickInScreenMask = FALSE; return; } // button event, stop showing menus if (!this->BoxPersist) { this->hide(); } // update mapscreen setTeamPanelDirty(TRUE); setCharacterInfoPanelDirty(TRUE); setMapScreenBottomDirty(TRUE); setRenderPBInterface(TRUE); SetRenderFlags( RENDER_FLAG_FULL ); } return; } void POPUP::MenuBtnCallBack( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for LPC region INT32 iValue = -1; INT32 iType = 0; INT32 iTotal = 0; SOLDIERTYPE * pSoldier = NULL; UINT8 ubVolume = 10; // sanity check #1 if (!this || this->boxId < 0 || this->id > POPUP::nextid){ #ifdef JA2TESTVERSION __debugbreak(); #else Assert(false); #endif } iValue = MSYS_GetRegionUserData( pRegion, 0 ); iType = MSYS_GetRegionUserData( pRegion, 1 ); iTotal = MSYS_GetRegionUserData( pRegion, 2 ); switch (iType) { case REGION_OPTION: if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { UnHighLightBox( this->boxId ); if (iValue < POPUP_MAX_OPTIONS && iValue < this->optionCount && iValue >= 0 && this->options[iValue] != NULL) { this->options[iValue]->run(); // run the option's callback // sanity check #2 // if this popup was fine in check #1 but is broken now, chances are // we got deleted (or just plain broken) by the callback. if (!this || this->id > POPUP::nextid){ #ifdef JA2TESTVERSION __debugbreak(); #else Assert(false); #endif } this->RebuildBox(); // rebuild the box so that the changes can take effect if( GetBoxShadeFlag( this->boxId, iTotal ) == FALSE ) // highlight the line again, if appropriate HighLightBoxLine( this->boxId, iTotal ); } } break; case REGION_SUB_POPUP: if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { UnHighLightBox( this->boxId ); if (iValue < POPUP_MAX_SUB_POPUPS && iValue < this->subPopupOptionCount && iValue >= 0 && this->subPopupOptions[iValue] != NULL) { this->recalculateWidth(); this->subPopupOptions[iValue]->run(); } } break; } } void POPUP::MenuMvtCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for LPC region INT32 iTotal = -1; INT32 iType = 0; INT32 iValue = 0; if (!this || this->boxId < 0){ __debugbreak(); } iTotal = MSYS_GetRegionUserData( pRegion, 2 ); // set this to the total value of all mouse regions iType = MSYS_GetRegionUserData( pRegion, 1 ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // highlight string if( GetBoxShadeFlag( this->boxId, iTotal ) == FALSE ) { // get the string line handle HighLightBoxLine( this->boxId, iTotal ); if ( iType == REGION_OPTION && this->options[iValue]->HoverFunctionSet() ) // if it has been set... this->options[iValue]->runHoverCallback(pRegion); // run the custom mouse callback } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( this->boxId ); } else if (iReason & MSYS_CALLBACK_REASON_MOVE) { // if the hover callback is set then also react to mouse movement within the region (only when available) if( iType == REGION_OPTION && this->options[iValue]->HoverFunctionSet() && GetBoxShadeFlag( this->boxId, iTotal ) == FALSE ) { this->options[iValue]->runHoverCallback(pRegion); // run the custom mouse callback if ( (pRegion->ButtonState & MSYS_LEFT_BUTTON) || (pRegion->ButtonState & MSYS_RIGHT_BUTTON) ) { // if drag detedted,rebuild the boxstrings this->RebuildBox(); } } } } void POPUP::HandleShadingOfLines( void ) { // check if valid if( ( this->PopupVisible == FALSE ) || ( this->boxId == - 1 ) ) { return; } UINT8 i = 0; // subPopups with no options are shaded for(std::vector::iterator cOption=this->subPopupOptions.begin(); cOption != this->subPopupOptions.end(); ++cOption) { if ( (*cOption)->subPopup->optionCount + (*cOption)->subPopup->subPopupOptionCount > 0 ) UnShadeStringInBox( this->boxId, i); else ShadeStringInBox( this->boxId, i); i++; } i = 0; // shade strings past the this->subPopupOptionCount offset for(std::vector::iterator cOption=this->options.begin(); cOption != this->options.end(); ++cOption) { if ((*cOption)->checkAvailability()) { UnShadeStringInBox(this->boxId, i + this->subPopupOptionCount); } else { SetStringShade((*cOption)->stringHandle, (*cOption)->color_shade); // handle checkAvailability() changing string shade color ShadeStringInBox(this->boxId, i + this->subPopupOptionCount); } i++; } return; } void POPUP::DetermineBoxPositions( void ) { // depending on how many boxes there are, reposition as needed SGPPoint pPoint; SGPPoint pNewPoint; SGPRect pDimensions; if( ( this->PopupVisible == FALSE ) || ( this->boxId == -1 ) ) { return; } // get an idea about what size the box ought to be pPoint.iX = 0; pPoint.iY = 0; SetBoxPosition( this->boxId, pPoint ); GetBoxSize( this->boxId, &pDimensions ); INT32 boxWidth = pDimensions.iRight; INT32 boxHeight = pDimensions.iBottom; /* if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { GetBoxPosition( this->boxId, &pPoint ); this->BoxesX = ( INT16 )pPoint.iX; this->BoxesY = ( INT16 )pPoint.iY; } */ switch (this->positioningRule){ case POPUP_POSITION_TOP_LEFT: pPoint.iX = this->OrigPosition.iX; pPoint.iY = this->OrigPosition.iY; break; case POPUP_POSITION_TOP_RIGHT: pPoint.iX = this->OrigPosition.iX - boxWidth; pPoint.iY = this->OrigPosition.iY; break; case POPUP_POSITION_BOTTOM_RIGHT: pPoint.iX = this->OrigPosition.iX - boxWidth; pPoint.iY = this->OrigPosition.iY - boxHeight; break; case POPUP_POSITION_BOTTOM_LEFT: pPoint.iX = this->OrigPosition.iX; pPoint.iY = this->OrigPosition.iY - boxHeight; break; default: #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Incorrect positioning rule for box id: %i \n",this->boxId); OutputDebugString( debugStr ); #endif // assume default pPoint.iX = this->OrigPosition.iX; pPoint.iY = this->OrigPosition.iY; return; } // Flugente: fix obvious errors pPoint.iX = max( pPoint.iX, 0 ); pPoint.iX = min( pPoint.iX, SCREEN_WIDTH ); pPoint.iY = max( pPoint.iY, 0 ); pPoint.iY = min( pPoint.iY, SCREEN_HEIGHT ); // set all the other positions to match this one this->Position = pPoint; this->BoxesX = pPoint.iX; this->BoxesY = pPoint.iY; this->X= pPoint.iX; this->Y= pPoint.iY; SetBoxPosition( this->boxId, pPoint ); GetBoxSize( this->boxId, &pDimensions ); // hang it right beside the LPC/EPC box menu pNewPoint.iX = pPoint.iX + pDimensions.iRight; pNewPoint.iY = pPoint.iY; return; } void POPUP::CheckAndUpdateTacticalPopUpPositions( void ) { SGPRect pDimensions2; SGPPoint pPoint; if( this->PopupVisible == FALSE ) { return; } if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { return; } GetBoxSize( this->boxId, &pDimensions2 ); if( this->BoxesX + pDimensions2.iRight >= SCREEN_WIDTH ) { this->BoxesX = ( INT16 ) ( SCREEN_WIDTH - 1 - ( pDimensions2.iRight ) ); SetRenderFlags( RENDER_FLAG_FULL ); } if( this->BoxesY + pDimensions2.iBottom >= SCREEN_HEIGHT-120 ) { this->BoxesY = ( INT16 )( SCREEN_HEIGHT - 121 - ( pDimensions2.iBottom ) ); SetRenderFlags( RENDER_FLAG_FULL ); } pPoint.iX = this->BoxesX; pPoint.iY = this->BoxesY; SetBoxPosition( this->boxId, pPoint ); this->RepositionMouseRegions( ); } void POPUP::RepositionMouseRegions( void ) { INT16 sDeltaX, sDeltaY; INT32 iCounter = 0; if( this->PopupVisible == TRUE ) { sDeltaX = this->BoxesX - this->MenuRegion[ 0 ].RegionTopLeftX; sDeltaY = ( INT16 ) ( this->BoxesY - this->MenuRegion[ 0 ].RegionTopLeftY + GetTopMarginSize( this->boxId ) ); // find the delta from the old to the new, and alter values accordingly for( iCounter = 0; iCounter < ( INT32 )GetNumberOfLinesOfTextInBox( this->boxId ); iCounter++ ) { this->MenuRegion[ iCounter ].RegionTopLeftX += sDeltaX; this->MenuRegion[ iCounter ].RegionTopLeftY += sDeltaY; this->MenuRegion[ iCounter ].RegionBottomRightX += sDeltaX; this->MenuRegion[ iCounter ].RegionBottomRightY += sDeltaY; } gfPausedTacticalRenderFlags = RENDER_FLAG_FULL; } } void POPUP::PositionCursorForBox(void) { // position cursor over y of on duty in tactical assignments SGPPoint pPosition; SGPRect pDimensions; INT32 iFontHeight; // get x.y position of box GetBoxPosition( this->boxId, &pPosition); // get dimensions..mostly for width GetBoxSize( this->boxId, &pDimensions ); iFontHeight = GetLineSpace( this->boxId ) + GetFontHeight( GetBoxFont( this->boxId ) ); if( gGameSettings.fOptions[ TOPTION_DONT_MOVE_MOUSE ] == FALSE ) { SimulateMouseMovement( pPosition.iX + pDimensions.iRight - 6, pPosition.iY + ( iFontHeight / 2 ) + 2 ); } } ////////////////////////////////////////////////////////////////// // Functions for binding POPUP class with MSYS callbacks ////////////////////////////////////////////////////////////////// PopupIndex * findMouseRegionInIndex(UINT16 regionId) { for(std::vector::iterator cPopupIndex=gPopupRegionIndex.begin(); cPopupIndex != gPopupRegionIndex.end(); ++cPopupIndex) if ((*cPopupIndex).regionId == regionId){ return &(*cPopupIndex); } #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Failed to find callback to mouse region id: %i \n",regionId); OutputDebugString( debugStr ); #endif return NULL; } BOOLEAN registerPopupRegion(UINT16 rID, UINT32 cID) { /* if (gPopupRegionIndexCounter == MAX_REGIONS_IN_INDEX) { // If we can't fit in any more regions, try to rebuild the index before giving up rebuildPopupRegionIndex(); // since there may be some unregistered regions if (gPopupRegionIndexCounter == MAX_REGIONS_IN_INDEX) return FALSE; // if index still full, return false } */ PopupIndex * pi = new PopupIndex; pi->regionId = rID; pi->classId = cID; gPopupRegionIndex.push_back(*pi); // FIXME: memory leak gPopupRegionIndexCounter++; return TRUE; } BOOLEAN unregisterPopupRegion(UINT16 regionId) { for(std::vector::iterator cPopupIndex=gPopupRegionIndex.begin(); cPopupIndex != gPopupRegionIndex.end(); ++cPopupIndex) if ((*cPopupIndex).regionId == regionId){ gPopupRegionIndex.erase(cPopupIndex); return TRUE; } #ifdef JA2TESTVERSION CHAR8 debugStr[120]; wsprintf(debugStr,"Failed to erase mouse region index: %i \n",regionId); OutputDebugString( debugStr ); #endif return FALSE; } void rebuildPopupRegionIndex(void) {/* UINT8 i,j; PopupIndex TempPopupIndex[MAX_REGIONS_IN_INDEX]; // cout << "cleaning index"<IDNumber)->classId; if (cID < 0) return; POPUP * p = findPopupInIndex(cID); if (p) { p->ScreenMaskBtnCallback(pRegion, iReason); } } void popupMouseMoveCallback(MOUSE_REGION *pRegion, INT32 iReason) { UINT32 cID = findMouseRegionInIndex(pRegion->IDNumber)->classId; if (cID < 0) return; POPUP * p = findPopupInIndex(cID); if (p) p->MenuMvtCallBack(pRegion, iReason); } void popupMouseClickCallback(MOUSE_REGION *pRegion, INT32 iReason) { UINT32 cID = findMouseRegionInIndex(pRegion->IDNumber)->classId; if (cID < 0) return; POPUP * p = findPopupInIndex(cID); if (p) { p->MenuBtnCallBack(pRegion, iReason); } }