#include "types.h" #include "WCheck.h" #include #include "GameSettings.h" #include "Encyclopedia_Data_new.h" #include "Encyclopedia_new.h" #include "Item Types.h" //item #include "Interface Items.h" //item: load item graphic #include "Weapons.h" //item #include "Queen Command.h" //location: Sectorinfo #include "strategic.h" //location: townIds #include "PostalService.h" //location: airport #include "Campaign Types.h" //location: facility types, descriptions #include "Soldier Profile.h" //char: names, flags and IDs #include "Vehicles.h" //char: vehicle image #include "LocalizedStrings.h" #include "Encrypted File.h" #include "mercs.h" #include "Overhead.h" //char: menpointer for ubWhatKindofMercAmI #include "aim.h" //char: aim availability #include "QuestText.h" //quest: name #include "laptop.h" //ui positions #include "Utilities.h" #include "Cursors.h" #include "sysutil.h" //extra Buffer for scaling image #include "vsurface.h" //fill extra buffer with black color #include "Text.h" #include "WordWrap.h" #include "Quests.h" #ifdef ENCYCLOPEDIA_WORKS /** @ingroup ENCYCLOPEDIA * @file * Encyclopedia data page in laptop. * @author Moa */ /** @addtogroup ENC_DATA * Depending on the selection made in \ref ENC_MAIN of encyclopedia the data page shows \li Locations (\ref ENC_DATA_FILTER_LOCATION) \li Characters (\ref ENC_DATA_FILTER_CHARACTER) \li Items (\ref ENC_DATA_FILTER_ITEM) \li Quests (\ref ENC_DATA_FILTER_QUEST) * * On the left side of www-laptop screen are small buttons to filter the data. These buttons are refered to as filter buttons. * \n Left click on a button will set the filter. Clicking on an active filterbutton will change the subfilter. * \n Rightclick will reset the subfilter and activate the filter. * \n \n Below the image there are two buttons for showing next and previous text page. * \n On the bottom player can view the next or previous data or go back to main menu of encyclopedia. */ /// For switching between data sub pages. enum ENC_DATA_SUBPAGE_T { ENC_DATA_NO_DATA = -1, ///< No data. If there is no data only the buttons and background is shown. ENC_DATA_PREVIOUS = 0, ///< Goto previous data. If there is no previous the 'previous' button gets deactivated. ENC_DATA_NEXT = 1, ///< Goto next data. If there is no next the 'next' button gets deactivated. }; /// Encyclopedia data struct. struct ENC_DATA_ENTRY_T { CHAR16 sDescr[560]; ///< Detailed description (below image). Up to 7 lines of text each with 80 characters. Only printable characters allowed. CHAR16 sName[80]; ///< zerro terminated string for name of data (Headline above image). Only printable characters allowed. CHAR16 sShortDesc[80]; ///< Short description (below image). Up to 80 characters. Only printable characters allowed. UINT32 uiImageID; ///< Video object ID. INT16 iID; ///< Corresponds to the index of items, (modified) index of sector, quest index or profile ID. If no data is available this is -1. UINT8 uiTextPage; ///< Current text page. Number of pages is currently 2 as there is no use for more at the moment. To define different pages modify \ref CreateData() and \ref BtnEncyclopedia_Data_NextPreviousTextBtnCallBack() accordingly. } gstEncyclopediaDataEntry; ///< Current data. #endif UINT8 gbEncyclopediaData_ItemVisible[MAXITEMS];///< Visibility of items. If an item should be visible the value is not 0. Gets loaded from savegame, updated by \ref EncyclopediaSetItemAsVisible() and reset by \ref EncyclopediaInitItemsVisibility(). #ifdef ENCYCLOPEDIA_WORKS /// Various filters for locations. Only filtered locations are shown. Subfilters, if any, are handled by callback function. enum ENC_DATA_FILTER_LOCATION { ENC_DATA_FILTER_L_ALL = 0, ///< All locations visible ENC_DATA_FILTER_L_CITY, ///< Only Cities ENC_DATA_FILTER_L_SAM, ///< Only SAM sites ENC_DATA_FILTER_L_MINE, ///< Only mines ENC_DATA_FILTER_L_AIRPORT, ///< Only airports ENC_DATA_FILTER_L_WILDERNESS, ///< No city, no SAM, no mine, no airport ENC_DATA_FILTER_L_UNDERGROUND, ///< All underground sectors ENC_DATA_FILTER_L_FACILITY, ///< Only facilities, has multiple subfilters ENC_DATA_FILTER_L_OTHER ///< Important other sectors, TBD (posibly creatures lare, gas station, ec.) } geEncyclopediaDataFilterLocation; ///< Current location filter /// Various filters for characters. Only filtered characters are shown. Subfilters, if any, are handled by callback function. enum ENC_DATA_FILTER_CHARACTER { ENC_DATA_FILTER_C_ALL = 0, ///< All characters visible ENC_DATA_FILTER_C_AIM, ///< hireable characters from A.I.M. ENC_DATA_FILTER_C_MERC, ///< hireable characters from M.E.R.C. ENC_DATA_FILTER_C_RPC, ///< Rebels ENC_DATA_FILTER_C_NPC, ///< non hireable characters ENC_DATA_FILTER_C_IMP, ///< player created characters ENC_DATA_FILTER_C_OTHER ///< vehicles and electronic 'chars' } geEncyclopediaDataFilterCharacter;///< Current character filter /// Various filters for items. Only filtered items are shown. Uses Bobby Ray's categories. Subfilters, if any, are handled by callback function. enum ENC_DATA_FILTER_ITEM { ENC_DATA_FILTER_I_ALL = 0, ///< All items visible ENC_DATA_FILTER_I_GUN, ///< Only Guns, has multiple subfilters ENC_DATA_FILTER_I_AMMO, ///< Only Ammunition, has multiple subfilters ENC_DATA_FILTER_I_ARMOUR, ///< Only Armour pieces, has multiple subfilters ENC_DATA_FILTER_I_LBE, ///< Only LBE gear, has multiple subfilters ENC_DATA_FILTER_I_ATTACHMENT, ///< Only attachable items (TBD), has multiple subfilters ENC_DATA_FILTER_I_MISC ///< Everything else, has multiple subfilters } geEncyclopediaDataFilterItem; ///< Current item filter /// @brief Various filters for quests.@details Only filtered quests are shown. Subfilters, if any, are handled by callback function. enum ENC_DATA_FILTER_QUEST { ENC_DATA_FILTER_Q_ALL = 0, ///< All active and completed quests are visible. ENC_DATA_FILTER_Q_ACTIVE, ///< Only started, but not completed quests are shown. ENC_DATA_FILTER_Q_COMPLETE ///< All finished quests are shown. } geEncyclopediaDataFilterQuest; ///< Current quest filter /// @brief SubClass filter for all major filters.@details 0 means no subfilter. Actual meaning depends on major filter. /// @see ENC_DATA_FILTER_CHARACTER, @see ENC_DATA_FILTER_CHARACTER,@see ENC_DATA_FILTER_ITEM,@see ENC_DATA_FILTER_QUEST UINT16 guiEncyclopediaDataSubFilter; /// Gap for name (top), image (top and left), filter buttons (top and left), short description (top and left), detailed description (top and left) #define ENC_DATA_GAP 5 /// position of first filterbutton #define ENC_DATA_FILTER_GAP_TOP 40 /// shadow height of image #define ENC_DATA_IMAGE_HEIGHT 172 /// shadow height of short description #define ENC_DATA_SHORT_DESC_HEIGHT 18 /// shadow height of detailed description #define ENC_DATA_DESC_HEIGHT 100 /// Maximum allowed (major) filters. Change only if one of the four data page categories exceed the limit of (major) filter buttons. #define ENC_DATA_MAX_FILTERS 9 /// Macro to get text for filter buttons. Works only for continuing subfilters (1,2,3..,255) where 0 is 'show all'. Uses the globals geENC_SubPage and guiEncyclopediaDataSubFilter @param defaultTStrP reseted filter texts CHAR16** @param subFilterTStrP Subfilter CHAR16** @param mf Current major filter. #define GetDataFilterText(defaultTStrP,subFilterTStrP,mf) (guiEncyclopediaDataSubFilter?subFilterTStrP[(UINT16)guiEncyclopediaDataSubFilter+guiEncyclopediaSubFilterTextIndexStart[geENC_SubPage-1][mf-1]-1]:defaultTStrP[mf]) UINT8 guiEncyclopediaSubFilterTextIndexStart[ENC_NUM_SUBPAGES][ENC_DATA_MAX_FILTERS]; ///< Holds the indizes of first filter button texts. Used to update text on click. Initialized on gamestart. INT32 giEncyclopedia_DataFilterBtnImage; ///< Image of filter button. INT32 giEncyclopedia_DataFilterBtn[ ENC_DATA_MAX_FILTERS ]; ///< Filter buttons. \see ENC_DATA_MAX_FILTERS. INT32 giEncyclopedia_DataBtnImage; ///< Image of next/previous/main page buttons. INT32 giEncyclopedia_DataBtn[ 5 ]; ///< previousData, mainpage, nextData, previousText, nextText. UINT8 guiEncyclopedia_TextPage; ///< Current text page. ////////////// // external data to save includes extern UINT8 gubMercArray[ NUM_PROFILES ]; extern UINT8 gubQuest[MAX_QUESTS]; //extern STR16 QuestDescText[]; ////////////// // function prototypes extern BOOLEAN IsAmmoMatchinWeaponType(UINT16 usItemIndex, UINT8 ubWeaponType); extern BOOLEAN GetSectorFlagStatus( INT16 sMapX, INT16 sMapY, UINT8 bMapZ, UINT32 uiFlagToSet ); extern BOOLEAN IsThereAMineInThisSector( INT16 sX, INT16 sY ); extern BOOLEAN IsMercMercAvailable( UINT8 ubMercID ); extern BOOLEAN LoadMercBioInfo(UINT8 ubIndex, STR16 pInfoString, STR16 pAddInfo); extern void GetQuestStartedString( UINT8 ubQuestValue, STR16 sQuestString ); extern void GetQuestEndedString( UINT8 ubQuestValue, STR16 sQuestString ); ////////////// // helper functions /** * Calculates x,y and z sectorinfo coords from encyclopedia location ID. * Location ID is encoded as: ([SectorInfo X coordinate - 1 (0..15)] + [SectorInfo Y coordinate - 1 (0..15)] * 16) * 4 + [SectorInfo Z coordinate(0..3)]. * * @param encLocationID [IN]range: 0 to 1023 * @param sSectorX, sSectorY, sSectorZ [OUT] SectorInfo coords compatible to macro SECTOR(x,y) * @return TRUE if valid coords are calculated, FALSE if encLocationID is out of bounds. */ BOOLEAN GetEncyclopediaSectorCoordsFromLocationID( INT16 encLocationID, UINT16 &sSectorX, UINT16 &sSectorY, UINT8 &sSectorZ ) { UINT16 x,y; UINT8 z; CHECKF( encLocationID >= 0 ); z = encLocationID % (MAXIMUM_VALID_Z_COORDINATE - MINIMUM_VALID_Z_COORDINATE + 1); CHECKF( z >= MINIMUM_VALID_Z_COORDINATE && z <= MAXIMUM_VALID_Z_COORDINATE ); encLocationID /= (MAXIMUM_VALID_Z_COORDINATE - MINIMUM_VALID_Z_COORDINATE + 1); x = (encLocationID % (MAXIMUM_VALID_X_COORDINATE - MINIMUM_VALID_X_COORDINATE + 1) + 1); CHECKF( x >= MINIMUM_VALID_X_COORDINATE && x <= MAXIMUM_VALID_X_COORDINATE ); encLocationID /= MAXIMUM_VALID_Y_COORDINATE - MINIMUM_VALID_Y_COORDINATE + 1; y = (encLocationID % MAXIMUM_VALID_Y_COORDINATE) + 1; CHECKF( y >= MINIMUM_VALID_Y_COORDINATE && y <= MAXIMUM_VALID_Y_COORDINATE ); sSectorX = x; sSectorY = y; sSectorZ = z; return (TRUE); } /** * Calculates encyclopedia location ID from x,y and z sectorinfo coords. * Location ID is encoded as: ([SectorInfo X coordinate - 1 (0..15)] + [SectorInfo Y coordinate - 1 (0..15)] * 16) * 4 + [SectorInfo Z coordinate(0..4)]. * * @param sSectorX, sSectorY, sSectorZ [IN] SectorInfo coords compatible to macro SECTOR(x,y) * @param encLocationID [OUT] range: 0..MAXIMUM_VALID_X_COORDINATE*MAXIMUM_VALID_Y_COORDINATE (0..1023) * @return TRUE if valid coords are calculated, FALSE if encLocationID is out of bounds. */ BOOLEAN GetEncyclopediaLocationIDFromSectorCoords( UINT16 sSectorX, UINT16 sSectorY, UINT8 sSectorZ, INT16 &encLocationID ) { CHECKF( sSectorX >= MINIMUM_VALID_X_COORDINATE && sSectorX <= MAXIMUM_VALID_X_COORDINATE ); CHECKF( sSectorY >= MINIMUM_VALID_Y_COORDINATE && sSectorY <= MAXIMUM_VALID_Y_COORDINATE ); CHECKF( sSectorZ >= MINIMUM_VALID_Z_COORDINATE && sSectorZ <= MINIMUM_VALID_Z_COORDINATE ); encLocationID = ((sSectorX-1) + (sSectorY-1) * 16) * 4 + sSectorZ; return (TRUE); } /** * Searches for the next or previous location. * * Takes filter settings into account. * @param curLoc Current location. * @param next If set to TRUE the next otherwise the previous location is selected. * @param filterClass Major filter @see ENC_DATA_FILTER_LOCATION * @param filterSubClass Sub filter. Ignored if the major filter does not support subfilters. * @return Returns a matching Location ID or -1 if none was found. * @see ENC_DATA_FILTER_LOACTION */ INT16 GetNextPreviousLocation( INT16 curLoc, BOOLEAN next, ENC_DATA_FILTER_LOCATION filterClass, UINT16 filterSubClass ) { extern UINT8 gubBloodcatLairSectorId; extern std::vector gDestinationTable; extern CPostalService gPostalService; UINT16 x, y, MAXID = (MAXIMUM_VALID_X_COORDINATE * MAXIMUM_VALID_Y_COORDINATE * 4)-1; UINT8 z; BOOLEAN found = FALSE; CHECKBI( gGameExternalOptions.gEncyclopedia ); CHECKBI( geENC_SubPage == ENC_LOCATIONS ); //reset to 0 if out of bounds if ( curLoc < 0 || curLoc > MAXID ) curLoc = 0; //loop all sectors for ( INT16 newID = curLoc + (next? 1: -1); 0 <= newID && newID <= MAXID; (next? newID++: newID--) ) { //#ifdef _DEBUG // Assert(GetEncyclopediaSectorCoordsFromLocationID( newID, x, y, z )); //#else CHECKF(GetEncyclopediaSectorCoordsFromLocationID( newID, x, y, z )); //#endif //ignore unvisited, !!do not change!! if( !GetSectorFlagStatus( x, y, z, SF_ALREADY_VISITED ) ) continue; switch (filterClass) { case ENC_DATA_FILTER_L_ALL : // All locations visible found = TRUE; break; //found case ENC_DATA_FILTER_L_CITY : // Only Cities if( StrategicMap[CALCULATE_STRATEGIC_INDEX(x,y)].bNameId ) { found = TRUE; // found location. Note that we allready checked for visited, so underground sectors are included as long as their [x,y] position matches. break; } continue;//no town case ENC_DATA_FILTER_L_SAM : // Only SAM sites if( IsThisSectorASAMSector( x, y, z ) ) { break; found = TRUE; } continue; //no SAM case ENC_DATA_FILTER_L_MINE : // Only mines if( IsThereAMineInThisSector( x, y ) ) { found = TRUE; break; } continue; // no mine case ENC_DATA_FILTER_L_AIRPORT : // Only airports if ( gPostalService.IsSectorAShipmentSector((UINT8)x,(UINT8)y,z) ) { found = TRUE; break;// break loop } continue; // no airport case ENC_DATA_FILTER_L_WILDERNESS : // No underground, no city, no SAM (, no mine, no airport) if( z == 0 && !StrategicMap[CALCULATE_STRATEGIC_INDEX(x,y)].bNameId && !IsThisSectorASAMSector( x, y, z ) ) { found = TRUE; break; //found } continue; // no wilderness case ENC_DATA_FILTER_L_UNDERGROUND: // All underground sectors if( z > 0 ) { found = TRUE; break; // found } continue; //no underground case ENC_DATA_FILTER_L_FACILITY : // Only facilities, has multiple subfilters CHECKBI( filterSubClass < NUM_FACILITY_TYPES ); if( z == 0 && filterSubClass == 0 ) {// no subfilter, check all possible facilities for( UINT8 i = 0; i < NUM_FACILITIES; i++ ) { if( gFacilityLocations[SECTOR(x,y)][i].fFacilityHere ) { found = TRUE; break; } } if( found ) break; // found } else if( z == 0 && gFacilityLocations[SECTOR(x,y)][filterSubClass].fFacilityHere ) { found = TRUE; break; // found } continue; // no facility case ENC_DATA_FILTER_L_OTHER : // Important other sectors, TBD (posibly creatures lare, gas station, ec.) if( z == 0 && SECTOR(x,y) == gubBloodcatLairSectorId ) { found = TRUE; break; // found } continue;// no other }//end switch if(found) { return newID; } }//end loop return (ENC_DATA_NO_DATA); } /** * @brief Searches for the next or previous character. * @param curChar current index of gMercProfile * @return index of next or previous gMercProfile * @see GetNextPreviousLocation() * @see ENC_DATA_FILTER_CHARACTER */ INT16 GetNextPreviousCharacter( INT16 curChar, BOOLEAN next, ENC_DATA_FILTER_CHARACTER filterClass, UINT16 filterSubClass ) { extern UINT8 gubMercArray[ NUM_PROFILES ]; BOOLEAN notFound = TRUE; //reset to -1 if out of bounds if( curChar < 0 || curChar > NUM_PROFILES ) curChar = -1; //loop through all profiles, start with current, direction determined by next. This wraps automatically because NUM_PROFILES+1 == sizeof(UINT8) == 256 for( UINT8 i = curChar + (next?1:-1); 0 <= i && i < NUM_PROFILES; (next? i++: i--) ) { //skip index 255 (used for not valid) if( i == (UINT8)-1 ) continue; //todo: find a way to determine if we met that person allready and exclude cutscene // skip profiles we do not have interacted with, ignoring GOODGUY and TOWN_DOESNT_CARE_ABOUT_DEATH flags // NOTE: recruited flag does not get updated correctly at the moment -> needs fix (on dismiss, death, imp creation, merc/aim/npc hire ec.) if( !gMercProfiles[ i ].ubMiscFlags && !gMercProfiles[ i ].ubMiscFlags2 && !(gMercProfiles[ i ].ubMiscFlags3 & ~(PROFILE_MISC_FLAG3_GOODGUY | PROFILE_MISC_FLAG3_TOWN_DOESNT_CARE_ABOUT_DEATH))) continue; /** @details * Resources to gather data from: *| variable name | Type |Index = person? |Usage *|:---------------------------------------------------- | :------------------------------------ | :-------------------------------: | :------------------------------------------------------------ *|1a. gMercProfiles[ NUM_PROFILES ] |MERCPROFILESTRUCT, huge object | yes for most, but not for all | Used to create soldiers, can be used to get Name, Descriptions, Pictures. Each index is potentially a group of people (e.g. 200, vehicles and epc). On the other hand most profiles are unique to a soldier. *|1b. Menptr[ NUM_PROFILES ] |SOLDIERTYPE, huge struct | YES | On tactical those are the people / vehicles / monsters / EPCs. Can provide a reference to a profile. Most likely they are destroyed upon unloading the map, to be recreated with potentially different profiles. Therfore not usable here. *|2a. AimMercArray[ NUM_PROFILES ] |UINT8, IDs for gMercProfiles | YES | Used for AIM website. Aim members are on beginning of array, empty spots have (UNIT8)-1. *|2b. gAimAvailability[ NUM_PROFILES ] |AIM_AVAILABLE, stuct | YES | Used for AIM website to get bio text and profile ID. *|3a. gubMercArray[ NUM_PROFILES ] |UINT8, IDs for gMercProfiles | YES | Used for MERC website. MERC mebers are on beginning of array, empty spots have (UNIT8)-1. *|3b. gConditionsForMercAvailability[ NUM_PROFILES ] |CONTITION_FOR_MERC_AVAILABLE, struct | YES | Used for MERC website to get bio text, profile ID and other data. MERC mebers are on beginning of array, empty spots have ProfilId = 0. *|6a. pVehicleList(variable Size) |VEHICLETYPE, medium struct | YES | Same principle as 1b, but data valid regardless of game state. *|6b. gNewVehicle[ NUM_PROFILES ] |NEW_CAR, medium struct | NO | Used to get vehicle 'face', Snds, name ec. Index is identical to gMercProfiles. */ notFound = FALSE; switch(filterClass) { case ENC_DATA_FILTER_C_ALL : // All characters visible // no filter, found char return i; case ENC_DATA_FILTER_C_AIM : // hireable characters from A.I.M. if ( gMercProfiles[i].Type == PROFILETYPE_AIM ) return i; //if( gAimAvailability[ i ].ProfilId != (UINT8)-1 ) // return gAimAvailability[ i ].ProfilId; notFound = TRUE;//no AIM break; case ENC_DATA_FILTER_C_MERC : // hireable characters from M.E.R.C. //if( gubMercArray[ i ] != 0 ) // return gubMercArray[ i ]; if( gMercProfiles[i].Type == PROFILETYPE_MERC ) return i; notFound = TRUE;//no AIM, no MERC break; case ENC_DATA_FILTER_C_OTHER : // vehicles and electronic 'chars' //if( notFound && IsMercHireable( i ) && i != 254 && Menptr[i].iVehicleId ) // return i; if( (filterSubClass == 0 || filterSubClass == 1) && gMercProfiles[i].Type == PROFILETYPE_VEHICLE ) return i; if( (filterSubClass == 0 || filterSubClass == 2) && (gMercProfiles[i].ubMiscFlags & PROFILE_MISC_FLAG_EPCACTIVE) ) return i; notFound = TRUE; break; case ENC_DATA_FILTER_C_RPC : // Rebels //if( gMercProfiles[ i ].ubCivilianGroup == REBEL_CIV_GROUP && IsMercHireable( i ) ) // return i;//no if ( gMercProfiles[i].Type == PROFILETYPE_RPC ) return i; notFound = TRUE; break; case ENC_DATA_FILTER_C_NPC : // non hireable characters //if( gMercProfiles[ i ].ubCivilianGroup > NON_CIV_GROUP && gMercProfiles[ i ].ubCivilianGroup != REBEL_CIV_GROUP && !IsMercHireable( i ) ) // return i; if ( gMercProfiles[i].Type == PROFILETYPE_NPC ) return i; notFound = TRUE; break; case ENC_DATA_FILTER_C_IMP : // player created characters if ( gMercProfiles[i].Type == PROFILETYPE_IMP ) return i; notFound = TRUE; break; } } return ENC_DATA_NO_DATA; } /** * @brief Searches for the next or previous item. * @see GetNextPreviousLocation() * @see ENC_DATA_FILTER_ITEM */ INT16 GetNextPreviousItem( INT16 curItem, BOOLEAN next, ENC_DATA_FILTER_ITEM filterClass, UINT16 filterSubClass ) { //reset to 1 if out of bounds if ( curItem < 1 || curItem >= MAXITEMS ) curItem = 1; // loop thru items, start with current, direction determined by next for ( INT16 i = curItem + (next? 1 : -1); (next? i < MAXITEMS : i > 0); (next? i++: i--) ) { // last item, break! if ( Item[ i ].uiIndex == 0 ) break; // not discovered yet, go ahead! if ( gbEncyclopediaData_ItemVisible[i] < gGameExternalOptions.ubEncyclopediaItemMask ) continue; switch ( filterClass ) { case ENC_DATA_FILTER_I_ALL : // no filter, found item. return i; case ENC_DATA_FILTER_I_GUN : if ( Item[ i ].usItemClass & IC_BOBBY_GUN ) { // no subClass filter, found item. if ( filterSubClass == 0 ) return i; // subClass filter match, found item else if ( Weapon[ i ].ubWeaponType == (UINT8)filterSubClass || ( filterSubClass == 9 && (Item[ i ].usItemClass & IC_LAUNCHER) ) ) return i; } // not found, go ahead! continue; case ENC_DATA_FILTER_I_AMMO : if ( Item[ i ].usItemClass & IC_AMMO ) { // no subfilters, found item if( filterSubClass == 0 ) return i; // subClass filter match, found item else if( IsAmmoMatchinWeaponType( i, (UINT8)filterSubClass ) ) return i; } else if ( /*(Item[ i ].usItemClass & IC_BOMB) && */filterSubClass == 9 && (Item[ i ].attachmentclass & (AC_ROCKET | AC_GRENADE)) ) return i; // not found, go ahead! continue; case ENC_DATA_FILTER_I_ARMOUR : if( Item[ i ].usItemClass & IC_ARMOUR ) { // no subClass filter, found item. if ( filterSubClass == 0 ) return i; else if( Armour[ Item[ i ].ubClassIndex ].ubArmourClass == (UINT8)filterSubClass-1 ) return i; } // not found, go ahead! continue; case ENC_DATA_FILTER_I_LBE : if( Item[ i ].usItemClass & IC_LBEGEAR ) { if ( filterSubClass == 0) return i; if( LoadBearingEquipment[ Item[ i ].ubClassIndex ].lbeClass == filterSubClass ) return i; } //not found, go ahead! continue; case ENC_DATA_FILTER_I_ATTACHMENT : if ( !Item[ i ].attachment || !(Item[ i ].usItemClass & (IC_MISC)) ) continue;// no attachment, skip switch( filterSubClass ) { case 0 : if( !Item[ i ].attachmentclass || !(Item[ i ].attachmentclass & (AC_ROCKET | AC_GRENADE)) ) return i; break; case BOBBYR_FILTER_MISC_OPTICS_ATTACHMENTS : if( Item[ i ].attachmentclass & (AC_SCOPE | AC_SIGHT | AC_IRONSIGHT | AC_LASER) ) return i; break; case BOBBYR_FILTER_MISC_SIDE_AND_BOTTOM_ATTACHMENTS : if ( Item[ i ].attachmentclass & (AC_FEEDER | AC_FOREGRIP | AC_BIPOD | AC_MAGWELL) ) return i; break; case BOBBYR_FILTER_MISC_MUZZLE_ATTACHMENTS : if ( Item[ i ].attachmentclass & (AC_EXTENDER | AC_BAYONET | AC_RIFLEGRENADE | AC_MUZZLE) ) return i; break; case BOBBYR_FILTER_MISC_STOCK_ATTACHMENTS : if ( Item[ i ].attachmentclass & (AC_STOCK | AC_SLING | AC_EXTERNAL) ) return i; break; case BOBBYR_FILTER_MISC_INTERNAL_ATTACHMENTS : if ( Item[ i ].attachmentclass & (AC_BATTERY | AC_INTERNAL | AC_UNDERBARREL) ) return i; break; case BOBBYR_FILTER_MISC_OTHER_ATTACHMENTS : if ( !Item[ i ].attachmentclass || (Item[ i ].attachmentclass & (AC_DEFUSE | AC_DETONATOR | AC_HELMET | AC_VEST | AC_PANTS | AC_MODPOUCH | AC_MISC16 | AC_MISC17 | AC_MISC18)) ) return i; break; } // not found, go ahead! continue; case ENC_DATA_FILTER_I_MISC : if( (Item[ i ].usItemClass & (IC_BOBBY_GUN | IC_AMMO | IC_LBEGEAR | IC_ARMOUR )) /*|| Item[ i ].attachment*/ || (Item[ i ].attachmentclass /*& (AC_ROCKET | AC_GRENADE)*/) ) continue;// allready covered above, skip switch ( filterSubClass ) { case 0 : if( Item[ i ].usItemClass & (IC_BLADE | IC_THROWING_KNIFE | IC_PUNCH | IC_GRENADE | IC_BOMB | IC_MEDKIT | IC_KIT | IC_FACE | IC_MISC | IC_MONEY ) ) return i; break; case BOBBYR_FILTER_MISC_BLADE : if( Item[ i ].usItemClass & IC_BLADE ) return i; break; case BOBBYR_FILTER_MISC_THROWING_KNIFE : if( Item[ i ].usItemClass & IC_THROWING_KNIFE ) return i; break; case BOBBYR_FILTER_MISC_PUNCH : if( Item[ i ].usItemClass & IC_PUNCH ) return i; break; case BOBBYR_FILTER_MISC_GRENADE : if( Item[ i ].usItemClass & IC_GRENADE ) return i; break; case BOBBYR_FILTER_MISC_BOMB : if( Item[ i ].usItemClass & IC_BOMB ) return i; break; case BOBBYR_FILTER_MISC_MEDKIT : if( Item[ i ].usItemClass & IC_MEDKIT ) return i; break; case BOBBYR_FILTER_MISC_KIT : if( Item[ i ].usItemClass & IC_KIT ) return i; break; case BOBBYR_FILTER_MISC_FACE : if( Item[ i ].usItemClass & IC_FACE ) return i; break; case BOBBYR_FILTER_MISC_MISC : if( Item[ i ].usItemClass & (IC_MISC | IC_MONEY) ) return i; break; } // not found, go ahead! continue; default : Assert(0); } }//end loop //nothing found return ENC_DATA_NO_DATA; } /** * @brief Searches for the next or previous quest. * @see GetNextPreviousLocation() * @see ENC_DATA_FILTER_QUEST * @param curQuest Quest ID matching index of gubQuest in quests.cpp */ INT16 GetNextPreviousQuest( INT16 curQuest, BOOLEAN next, ENC_DATA_FILTER_QUEST filterClass, UINT16 filterSubClass ) { //reset to -1 if out of bounds if ( curQuest < 0 || curQuest >= MAX_QUESTS ) curQuest = -1; // loop thru quests, start with current, direction determined by next for( INT16 i = curQuest + (next? 1: -1); 0 <= i && i < (UINT8)MAX_QUESTS; next? i++: i-- ) { switch( filterClass) { case ENC_DATA_FILTER_Q_ALL : if( gubQuest[ i ] != QUESTNOTSTARTED ) return i; continue;// not found case ENC_DATA_FILTER_Q_ACTIVE : if( gubQuest[ i ] == QUESTINPROGRESS ) return i; continue;//not found case ENC_DATA_FILTER_Q_COMPLETE : if( gubQuest[ i ] == QUESTDONE ) return i; continue;//not found } } return ENC_DATA_NO_DATA; } /** * @brief Fills gstEncyclopediaDataEntry according to the subpage we are in. * @return TRUE if there is data, FALSE if the data is empty * @see ENC_DATA_ENTRY_T */ BOOLEAN CreateData( INT16 newID ) { CHAR8 fileName[80]; VOBJECT_DESC vObjDesc; CHECKF( gGameExternalOptions.gEncyclopedia ); CHECKF( geENC_SubPage); UINT8 textPage = gstEncyclopediaDataEntry.uiTextPage;//want to keep that, otherwise we are forced to set the text outside of this function on text page change. ////// // clear data // unload graphic if still exists if ( gstEncyclopediaDataEntry.uiImageID ) DeleteVideoObjectFromIndex( gstEncyclopediaDataEntry.uiImageID ); // reset remaining data memset( &gstEncyclopediaDataEntry, 0x0, sizeof(ENC_DATA_ENTRY_T) ); ////// // set ID if ( newID < 0 ) { gstEncyclopediaDataEntry.iID = ENC_DATA_NO_DATA; return FALSE; } ////// // recover text page. gstEncyclopediaDataEntry.uiTextPage = textPage; ////// // set descriptions and load image switch (geENC_SubPage) { case ENC_LOCATIONS : //check for valid ID // set name GetSectorIDString( SECTORX( newID/(MAXIMUM_VALID_Z_COORDINATE+1) ), SECTORY( newID/(MAXIMUM_VALID_Z_COORDINATE+1) ), newID%(MAXIMUM_VALID_Z_COORDINATE+1), gstEncyclopediaDataEntry.sName, TRUE ); // no short description TODO // no detailed description TODO // so only image left, its most exciting anyway:) { UINT16 x,y; UINT8 z; GetEncyclopediaSectorCoordsFromLocationID( newID, x, y, z ); GetMapFileName( SECTORX( newID/(MAXIMUM_VALID_Z_COORDINATE+1) ), SECTORY( newID/(MAXIMUM_VALID_Z_COORDINATE+1) ), newID%(MAXIMUM_VALID_Z_COORDINATE+1), fileName, FALSE, TRUE ); //remove file extension for( INT32 cnt = strlen(fileName)-1; cnt>=0; cnt-- ) if( fileName[cnt] == '.' ) { fileName[cnt] = '\0'; break; } sprintf(vObjDesc.ImageFile, "Encyclopedia\\Maps\\%s.STI", fileName); vObjDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; if( !FileExists(vObjDesc.ImageFile) || !AddVideoObject( &vObjDesc, &gstEncyclopediaDataEntry.uiImageID ) )//no file? try loading tiny little radar image... { vObjDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; sprintf(vObjDesc.ImageFile, "RadarMaps\\%s.STI", fileName); if( !FileExists(vObjDesc.ImageFile) || !AddVideoObject( &vObjDesc, &gstEncyclopediaDataEntry.uiImageID ) )//no file? no map... { //if( !LoadMapGraphicForLocation( NULL, &gstEncyclopediaDataEntry.uiImageID ) ) gstEncyclopediaDataEntry.uiImageID = 0; } } } break; case ENC_CHARACTERS : CHECKF( newID < NUM_PROFILES ); // set name std::memcpy( gstEncyclopediaDataEntry.sName, gMercProfiles[ newID ].zNickname, sizeof(gMercProfiles[ newID ].zNickname) ); // set short description std::memcpy( gstEncyclopediaDataEntry.sShortDesc, gMercProfiles[ newID ].zName, sizeof(gMercProfiles[ newID ].zName) ); //set detailed description { CHAR16 szMercInfo[800],szMercInfo2[800]; //UINT16 strLen; szMercInfo[0] = szMercInfo2[0] = 0; if( !g_bUseXML_Strings ) {//read encoded .dat //find aimbio or mercbio index matching gMercProfiles index for( UINT8 i = 0; i < NUM_PROFILES - 1; i++ ) if( gAimAvailability[i].ProfilId == newID ) { LoadEncryptedDataFromFile("BINARYDATA\\AIMBIOS.EDT", szMercInfo, 7*80*2*gAimAvailability[i].AimBio, 5*80*2); LoadEncryptedDataFromFile("BINARYDATA\\AIMBIOS.EDT", szMercInfo2, 7*80*2*gAimAvailability[i].AimBio + 5*80*2, 2*80*2 ); break; } else if ( gConditionsForMercAvailability[i].ProfilId == newID ) { LoadEncryptedDataFromFile("BINARYDATA\\MERCBIOS.EDT", szMercInfo, 7*80*2*gConditionsForMercAvailability[i].MercBio, 5*80*2); LoadEncryptedDataFromFile("BINARYDATA\\MERCBIOS.EDT", szMercInfo2, 7*80*2*gConditionsForMercAvailability[i].MercBio + 5*80*2, 2*80*2 ); break; } //if no description reset to 0 if( szMercInfo[0] == (CHAR16)-1 ) szMercInfo[0] = 0; if( szMercInfo2[0] == (CHAR16)-1 ) szMercInfo2[0] = 0; } else {//read .xml if( !Loc::GetString(Loc::AIM_BIOGRAPHY, L"Bio", newID, szMercInfo, 400) ) szMercInfo[0] = 0; if( !Loc::GetString(Loc::AIM_BIOGRAPHY, L"Add", newID, szMercInfo2, 160) ) szMercInfo2[0] = 0; } //print //strLen = wcslen( szMercInfo ) + wcslen( szMercInfo2 ) + 160; //if( gstEncyclopediaDataEntry.uiTextPage == 0 && (strLen < sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16)) ) //{//both fit on one page // wcscpy_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), szMercInfo ); // wcscat_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), L" " ); // wcscat_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), L" " ); // wcscat_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), szMercInfo2 ); //} //else if( gstEncyclopediaDataEntry.uiTextPage == 0 ) {//aditional info dont fit, print only bio wcscpy_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), szMercInfo ); } else if( gstEncyclopediaDataEntry.uiTextPage == 1 ) {//dont fit on single page, print additional info on page 1 wcscpy_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), szMercInfo2 ); } else if( gstEncyclopediaDataEntry.uiTextPage == 2 ) {//2: debug text misc flag STR16 dbgText = { L"PROFILE=%d, FLAG_RECRUITED=%d, FLAG_HAVESEENCREATURE=%d, FLAG_FORCENPCQUOTE=%d, FLAG_WOUNDEDBYPLAYER=%d, FLAG_TEMP_NPC_QUOTE_DATA_EXISTS=%d, FLAG_SAID_HOSTILE_QUOTE=%d, FLAG_EPCACTIVE=%d, FLAG_ALREADY_USED_ITEMS=%d" }; swprintf( gstEncyclopediaDataEntry.sDescr, dbgText, newID, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_HAVESEENCREATURE, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_FORCENPCQUOTE, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_WOUNDEDBYPLAYER, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_TEMP_NPC_QUOTE_DATA_EXISTS, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_SAID_HOSTILE_QUOTE, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_EPCACTIVE, gMercProfiles[newID].ubMiscFlags & PROFILE_MISC_FLAG_ALREADY_USED_ITEMS ); } else if( gstEncyclopediaDataEntry.uiTextPage == 3 ) {//3: debug text misc flag2 STR16 dbgText = { L"PROFILE=%d, FLAG2_DONT_ADD_TO_SECTOR=%d, FLAG2_LEFT_COUNTRY=%d, FLAG2_BANDAGED_TODAY=%d, FLAG2_SAID_FIRSTSEEN_QUOTE=%d, FLAG2_NEEDS_TO_SAY_HOSTILE_QUOTE=%d, FLAG2_MARRIED_TO_HICKS=%d, FLAG2_ASKED_BY_HICKS=%d, FLAG2_MERC_GEARKIT_UNPAID=%d" }; swprintf( gstEncyclopediaDataEntry.sDescr, dbgText, newID, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_DONT_ADD_TO_SECTOR, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_LEFT_COUNTRY, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_BANDAGED_TODAY, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_SAID_FIRSTSEEN_QUOTE, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_NEEDS_TO_SAY_HOSTILE_QUOTE, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_MARRIED_TO_HICKS, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_ASKED_BY_HICKS, gMercProfiles[newID].ubMiscFlags2 & PROFILE_MISC_FLAG2_MERC_GEARKIT_UNPAID ); } else if( gstEncyclopediaDataEntry.uiTextPage == 4 ) {//4: debug text misc flag3 STR16 dbgText = { L"PROFILE=%d, FLAG3_PLAYER_LEFT_MSG_FOR_MERC_AT_AIM=%d, FLAG3_PERMANENT_INSERTION_CODE=%d, FLAG3_PLAYER_HAD_CHANCE_TO_HIRE=%d, FLAG3_HANDLE_DONE_TRAVERSAL=%d, FLAG3_NPC_PISSED_OFF=%d, FLAG3_MERC_MERC_IS_DEAD_AND_QUOTE_SAID=%d, FLAG3_TOWN_DOESNT_CARE_ABOUT_DEATH=%d, FLAG3_GOODGUY=%d" }; swprintf( gstEncyclopediaDataEntry.sDescr, dbgText, newID, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_PLAYER_LEFT_MSG_FOR_MERC_AT_AIM, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_PLAYER_HAD_CHANCE_TO_HIRE, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_NPC_PISSED_OFF, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_MERC_MERC_IS_DEAD_AND_QUOTE_SAID, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_TOWN_DOESNT_CARE_ABOUT_DEATH, gMercProfiles[newID].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY ); } } // create image if ( gMercProfiles[newID].Type == PROFILETYPE_VEHICLE ) { //this is a vehicle std::strcpy( vObjDesc.ImageFile, gNewVehicle[newID].szIconFace ); vObjDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; if( !AddVideoObject(&vObjDesc, &gstEncyclopediaDataEntry.uiImageID) ) gstEncyclopediaDataEntry.uiImageID = 0; } else { //not vehicle, process normal if(gGameExternalOptions.fReadProfileDataFromXML) { // HEADROCK PROFEX: Do not read direct profile number, instead, look inside the profile for a ubFaceIndex value. sprintf( fileName, "%02d.sti", gMercProfiles[newID].ubFaceIndex); } else { sprintf( fileName, "%02d.sti", newID); } //FilenameForBPP( vObjDesc.ImageFile, fileName); removed: no faces with [ID]_8.sti sprintf( vObjDesc.ImageFile, "faces\\BIGFACES\\%s", fileName ); vObjDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; if( !AddVideoObject(&vObjDesc, &gstEncyclopediaDataEntry.uiImageID) ) { //try with prefix 'B' sprintf( vObjDesc.ImageFile, "faces\\B%s",fileName ); vObjDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; if( !AddVideoObject(&vObjDesc, &gstEncyclopediaDataEntry.uiImageID) ) gstEncyclopediaDataEntry.uiImageID = 0; } } break; case ENC_ITEMS : // set name std::memcpy( gstEncyclopediaDataEntry.sName, Item[ newID ].szItemName, sizeof(Item[ newID ].szItemName) ); // set short description std::memcpy( gstEncyclopediaDataEntry.sShortDesc, Item[ newID ].szLongItemName, sizeof(Item[ newID ].szLongItemName) ); // set detailed description if( gstEncyclopediaDataEntry.uiTextPage == 0 ) std::memcpy( gstEncyclopediaDataEntry.sDescr, Item[ newID ].szItemDesc, sizeof(Item[ newID ].szItemDesc) ); else std::memcpy( gstEncyclopediaDataEntry.sDescr, Item[ newID ].szBRDesc, sizeof(Item[ newID ].szBRDesc) ); // load image if ( !LoadTileGraphicForItem( &Item[ newID ], &gstEncyclopediaDataEntry.uiImageID ) ) gstEncyclopediaDataEntry.uiImageID = 0; break; case ENC_QUESTS : // set name CHAR16 questStartedString[160],questCompletedString[160]; wcscpy_s( gstEncyclopediaDataEntry.sName, sizeof(gstEncyclopediaDataEntry.sName)/sizeof(CHAR16), QuestDescText[ newID ] ); // set description GetQuestStartedString( (UINT8)newID, questStartedString ); wcscpy_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), questStartedString ); if( gubQuest[(UINT8) newID] == QUESTDONE ) { wcscat_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), L" " ); wcscat_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), L" " ); GetQuestEndedString( (UINT8)newID, questCompletedString ); wcscat_s( gstEncyclopediaDataEntry.sDescr, sizeof(gstEncyclopediaDataEntry.sDescr)/sizeof(CHAR16), questCompletedString ); } break; default : return FALSE; } //finally set ID, everything else covered above gstEncyclopediaDataEntry.iID = newID; return TRUE; } /** * Prepares a change to a different subpage. * * Subpage content depends on selected data type (@ref ENC_SUBPAGE_T). * As there are more then UINT8 possible pages this function behaves differently then standart wwwLaptop function: * It uses the parameter to go to first, next or previous data entry. * @param ubDirection previous (0), next (1) or first (255) data entry. * @see ENC_DATA_SUBPAGE_T * @deprecated use CreateData() instead */ void ChangingEncyclopediaDataSubPage( UINT8 ubDirection ) { } ////////////// // Call backs /** @addtogroup CALLBACK Callbacks * @ingroup ENC_DATA * @{ */ extern void BtnEncyclopedia_newSelectDataPageBtnCallBack( GUI_BUTTON *btn, INT32 reason ); /** * @brief Encyclopedia filter button call back. * @details Handles all filter buttons. Uses userdata at index 1 for major filter and index 2 for subfilters. * Subfilter gets reset on rightclick, and updated on leftclick. * If you want to add new subfilters change the getPreviousNextXXXX function accordingly. * To create new (major) filters update the matching filter enum, EnterEncyclopediaData(), ExitEncyclopediaData(). CreateData() and RenderEncyclopediaData() should adapt. * Assumes that there is at least one filter button giEncyclopedia_DataFilterBtn[ 0 ] */ void BtnEncyclopedia_Data_FilterBtnCallBack( GUI_BUTTON *btn, INT32 reason ) { INT16 nextData = ENC_DATA_NO_DATA; CHECKV( gGameExternalOptions.gEncyclopedia ); CHECKV( btn ); UINT8 userDataFilterMode = (UINT8)MSYS_GetBtnUserData( btn, 1 ); if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { if( userDataFilterMode == 0 ) { GUI_BUTTON *tmpButton; // first button clicked (show all), reset filters geEncyclopediaDataFilterLocation = ENC_DATA_FILTER_L_ALL; geEncyclopediaDataFilterCharacter = ENC_DATA_FILTER_C_ALL; geEncyclopediaDataFilterItem = ENC_DATA_FILTER_I_ALL; geEncyclopediaDataFilterQuest = ENC_DATA_FILTER_Q_ALL; guiEncyclopediaDataSubFilter = 0; // data only needs to be updated if a subfilter was selected with no data otherwise it should show up in 'all' if( gstEncyclopediaDataEntry.iID == ENC_DATA_NO_DATA ) { if( geENC_SubPage == ENC_LOCATIONS ) CreateData( GetNextPreviousLocation( ENC_DATA_NO_DATA, ENC_DATA_NEXT, ENC_DATA_FILTER_L_ALL, 0 ) ); else if( geENC_SubPage == ENC_CHARACTERS ) CreateData( GetNextPreviousCharacter( ENC_DATA_NO_DATA, ENC_DATA_NEXT, ENC_DATA_FILTER_C_ALL, 0 ) ); else if( geENC_SubPage == ENC_ITEMS ) CreateData( GetNextPreviousItem( ENC_DATA_NO_DATA, ENC_DATA_NEXT, ENC_DATA_FILTER_I_ALL, 0 ) ); else if( geENC_SubPage == ENC_QUESTS ) CreateData( GetNextPreviousQuest( ENC_DATA_NO_DATA, ENC_DATA_NEXT, ENC_DATA_FILTER_Q_ALL, 0 ) ); // since we changed data render again if( gstEncyclopediaDataEntry.iID != ENC_DATA_NO_DATA ) fPausedReDrawScreenFlag = TRUE; } // deselect all other filter buttons for( UINT8 i = 1; i < sizeof(giEncyclopedia_DataFilterBtn)/sizeof(INT32); i++ ) { if( giEncyclopedia_DataFilterBtn[ i ] != BUTTON_NO_SLOT ) { tmpButton = GetButtonPtr( giEncyclopedia_DataFilterBtn[ i ] ); tmpButton->uiFlags &= (~BUTTON_CLICKED_ON); InvalidateRegion(tmpButton->Area.RegionTopLeftX, tmpButton->Area.RegionTopLeftY, tmpButton->Area.RegionBottomRightX, tmpButton->Area.RegionBottomRightY); } } // Disable 'All' button to mark it as selected DisableButton( giEncyclopedia_DataFilterBtn[ 0 ] ); // activate navigation buttons EnableButton( giEncyclopedia_DataBtn[ 2 ] ); EnableButton( giEncyclopedia_DataBtn[ 4 ] ); } else { // other button then first pressed, check if allready down guiEncyclopediaDataSubFilter = (UINT16)MSYS_GetBtnUserData( btn, 2 ); if( btn->uiFlags & BUTTON_CLICKED_ON ) { // button down // set new button text, userdata and global subfilter switch( geENC_SubPage ) { case ENC_LOCATIONS : // set major filter //geEncyclopediaDataFilterLocation = static_cast(userDataFilterMode); // select next subfilter switch( geEncyclopediaDataFilterLocation ) { case ENC_DATA_FILTER_L_FACILITY : // Only Facility has subfilters: gFacilityTypes (variable defined in xml) using 0 for all facilities if( guiEncyclopediaDataSubFilter >= NUM_FACILITY_TYPES - 1) guiEncyclopediaDataSubFilter = 0; else guiEncyclopediaDataSubFilter++; CHAR16 tempStr[8]; if( guiEncyclopediaDataSubFilter ) wcsncpy( tempStr, gFacilityTypes[ guiEncyclopediaDataSubFilter ].szFacilityShortName, 7 );// limit text to 7 chars //SpecifyButtonText( btn->IDNum, gFacilityTypes[ guiEncyclopediaDataSubFilter ].szFacilityShortName ); else wcsncpy( tempStr, GetDataFilterText( pEncyclopediaFilterLocationText, pEncyclopediaSubFilterLocationText, geEncyclopediaDataFilterLocation ), 7 ); //SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterLocationText, pEncyclopediaSubFilterLocationText, geEncyclopediaDataFilterLocation ) ); tempStr[7] = 0; SpecifyButtonText( btn->IDNum, tempStr ); break; default : guiEncyclopediaDataSubFilter = 0; } MSYS_SetBtnUserData( btn->IDNum, 2, guiEncyclopediaDataSubFilter ); nextData = GetNextPreviousLocation( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterLocation, guiEncyclopediaDataSubFilter ); break; case ENC_CHARACTERS : // set major filter //geEncyclopediaDataFilterCharacter = static_cast(userDataFilterMode); switch( geEncyclopediaDataFilterCharacter ) { case ENC_DATA_FILTER_C_OTHER : // Only OTHER has 3 subfilter: EPC, vehicle and other, switching these states if( guiEncyclopediaDataSubFilter > 1 ) guiEncyclopediaDataSubFilter = 0; else guiEncyclopediaDataSubFilter++; MSYS_SetBtnUserData( btn->IDNum, 2, guiEncyclopediaDataSubFilter ); SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterCharText, pEncyclopediaSubFilterCharText, ENC_DATA_FILTER_C_OTHER ) ); break; default : // All remaining filters have no subfilters, userdata and button text wont change. guiEncyclopediaDataSubFilter = 0; } nextData = GetNextPreviousCharacter( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterCharacter, guiEncyclopediaDataSubFilter ); break; case ENC_ITEMS : // set major filter //geEncyclopediaDataFilterItem = static_cast(userDataFilterMode); // select next sub filter switch( geEncyclopediaDataFilterItem ) { case ENC_DATA_FILTER_I_ALL : //All has no subfilters, userdata and button text wont change as well guiEncyclopediaDataSubFilter = 0; break; case ENC_DATA_FILTER_I_GUN : // guns have 9 subfilters plus one for all guns (using NOT_GUN). Subfilter is 'weapon exact type' see Weapons.h except for heavy weapon (using index 9) if( (guiEncyclopediaDataSubFilter - 1) == GUN_SHOTGUN ) // last subfilter, reset guiEncyclopediaDataSubFilter = NOT_GUN; else // get next subfilter guiEncyclopediaDataSubFilter++; SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterItemText, pEncyclopediaSubFilterItemText, ENC_DATA_FILTER_I_GUN ) ); break; case ENC_DATA_FILTER_I_AMMO : // ammunition has 9 subfilters plus one for all (using NOT_GUN). Subfilter is 'weapon exact type' see Weapons.h, except for grenades (using index 9) if( (guiEncyclopediaDataSubFilter - 1) == GUN_SHOTGUN ) guiEncyclopediaDataSubFilter = NOT_GUN; else guiEncyclopediaDataSubFilter++; //SpecifyButtonText( btn->IDNum, (guiEncyclopediaDataSubFilter == NOT_GUN? BobbyRText[4] : (guiEncyclopediaDataSubFilter < 9? BobbyRFilter[guiEncyclopediaDataSubFilter+8] : BobbyRFilter[28])) ); SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterItemText, pEncyclopediaSubFilterItemText, ENC_DATA_FILTER_I_AMMO ) ); break; case ENC_DATA_FILTER_I_ARMOUR : // armor has 4 subfilters plus one for all (using 0 for all and shifting all others). Subfilter is 'ARMOUR CLASSES' + 1 see Weapons.h if( guiEncyclopediaDataSubFilter == ARMOURCLASS_PLATE + 1 ) guiEncyclopediaDataSubFilter = 0; else guiEncyclopediaDataSubFilter++; SpecifyButtonText( btn->IDNum, (guiEncyclopediaDataSubFilter == 0? pEncyclopediaFilterItemText[3] : pEncyclopediaSubFilterItemText[guiEncyclopediaDataSubFilter + 19]) ); break; case ENC_DATA_FILTER_I_LBE : // LBE has 6 subfilters plus one for all (using 0). Subfilter is enum eLBE_CLASS (1..6) if( guiEncyclopediaDataSubFilter == OTHER_POCKET ) guiEncyclopediaDataSubFilter = 0; else guiEncyclopediaDataSubFilter++; SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterItemText, pEncyclopediaSubFilterItemText, ENC_DATA_FILTER_I_LBE ) ); break; case ENC_DATA_FILTER_I_ATTACHMENT : // attachment has 5 subfilters plus one for all (using 0). Subfilter is BOBBYR_FILTER_MISC_OPTICS_ATTACHMENTS to BOBBYR_FILTER_MISC_OTHER_ATTACHMENTS and 0. if( guiEncyclopediaDataSubFilter == BOBBYR_FILTER_MISC_OTHER_ATTACHMENTS ) guiEncyclopediaDataSubFilter = 0; else if( guiEncyclopediaDataSubFilter < BOBBYR_FILTER_MISC_OPTICS_ATTACHMENTS ) guiEncyclopediaDataSubFilter = BOBBYR_FILTER_MISC_OPTICS_ATTACHMENTS; else guiEncyclopediaDataSubFilter++; SpecifyButtonText( btn->IDNum, (guiEncyclopediaDataSubFilter == 0? pEncyclopediaFilterItemText[5] : pEncyclopediaSubFilterItemText[guiEncyclopediaDataSubFilter - 2 ]) ); break; case ENC_DATA_FILTER_I_MISC : // misc has 9 subfilters plus one for all. Subfilter is BOBBYR_FILTER_MISC_BLADE...:25-32 + 40 + 0, moved 40 to 33 for convinience. if( guiEncyclopediaDataSubFilter == BOBBYR_FILTER_MISC_MISC ) // last subfilter, reset guiEncyclopediaDataSubFilter = 0; else if( guiEncyclopediaDataSubFilter < BOBBYR_FILTER_MISC_BLADE ) guiEncyclopediaDataSubFilter = BOBBYR_FILTER_MISC_BLADE; else if( guiEncyclopediaDataSubFilter == BOBBYR_FILTER_MISC_FACE ) guiEncyclopediaDataSubFilter = BOBBYR_FILTER_MISC_MISC; else guiEncyclopediaDataSubFilter++; SpecifyButtonText( btn->IDNum, (guiEncyclopediaDataSubFilter == 0? pEncyclopediaFilterItemText[6] : (guiEncyclopediaDataSubFilter == BOBBYR_FILTER_MISC_MISC? pEncyclopediaSubFilterItemText[ 47 ] : pEncyclopediaSubFilterItemText[ guiEncyclopediaDataSubFilter + 14 ]) ) ); break; } MSYS_SetBtnUserData( btn->IDNum, 2, guiEncyclopediaDataSubFilter ); nextData = GetNextPreviousItem( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterItem, guiEncyclopediaDataSubFilter ); break; case ENC_QUESTS : //set major filter //geEncyclopediaDataFilterQuest = static_cast(userDataFilterMode); // no subfilters for quests guiEncyclopediaDataSubFilter = 0; MSYS_SetBtnUserData( btn->IDNum, 2, guiEncyclopediaDataSubFilter ); nextData = GetNextPreviousQuest( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterQuest, guiEncyclopediaDataSubFilter ); //TODO break; default : Assert(0); } } //end filter button down (not first) else { // filter button up (not first),... guiEncyclopediaDataSubFilter = (UINT16)MSYS_GetBtnUserData( btn, 2 ); // ... change to down,... btn->uiFlags |= BUTTON_CLICKED_ON; InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); // ... update data... switch( geENC_SubPage ) { case ENC_LOCATIONS : geEncyclopediaDataFilterLocation = static_cast(userDataFilterMode); nextData = GetNextPreviousLocation( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterLocation, guiEncyclopediaDataSubFilter ); break; case ENC_CHARACTERS : geEncyclopediaDataFilterCharacter = static_cast(userDataFilterMode); nextData = GetNextPreviousCharacter( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterCharacter, guiEncyclopediaDataSubFilter ); break; case ENC_ITEMS : geEncyclopediaDataFilterItem = static_cast(userDataFilterMode); nextData = GetNextPreviousItem( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterItem, guiEncyclopediaDataSubFilter ); break; case ENC_QUESTS : geEncyclopediaDataFilterQuest = static_cast(userDataFilterMode); nextData = GetNextPreviousQuest( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterQuest, guiEncyclopediaDataSubFilter ); break; default : Assert(0); } } // end filter button up (not first) CreateData( nextData ); // ...and activate first button. EnableButton( giEncyclopedia_DataFilterBtn[ 0 ] ); // activate navigation buttons EnableButton( giEncyclopedia_DataBtn[ 2 ] ); EnableButton( giEncyclopedia_DataBtn[ 4 ] ); // release all other buttons except this one for( UINT8 i = 0; i < sizeof(giEncyclopedia_DataFilterBtn)/sizeof(INT32); i++ ) { if( giEncyclopedia_DataFilterBtn[i] != btn->IDNum && giEncyclopedia_DataFilterBtn[i] != BUTTON_NO_SLOT ) { GetButtonPtr(giEncyclopedia_DataFilterBtn[i])->uiFlags &= (~BUTTON_CLICKED_ON); } } // render new data fPausedReDrawScreenFlag = TRUE; }//end not first filter button pressed }//end reason & MSYS_CALLBACK_REASON_LBUTTON_UP else if( reason & MSYS_CALLBACK_REASON_RBUTTON_UP && userDataFilterMode != 0 ) { //filterbuttons reset subfilter on rightclick // mark button as pressed... btn->uiFlags |= BUTTON_CLICKED_ON; InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); // ...reset subfilter... guiEncyclopediaDataSubFilter = 0; MSYS_SetBtnUserData( btn->IDNum, 2, guiEncyclopediaDataSubFilter ); // ...get new data and update button text and filters... switch( geENC_SubPage ) { case ENC_LOCATIONS : geEncyclopediaDataFilterLocation = static_cast(userDataFilterMode); SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterLocationText, pEncyclopediaSubFilterLocationText, geEncyclopediaDataFilterLocation ) ); nextData = GetNextPreviousLocation( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterLocation, guiEncyclopediaDataSubFilter ); break; case ENC_CHARACTERS : geEncyclopediaDataFilterCharacter = static_cast(userDataFilterMode); SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterCharText, pEncyclopediaSubFilterCharText, geEncyclopediaDataFilterCharacter ) ); nextData = GetNextPreviousCharacter( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterCharacter, guiEncyclopediaDataSubFilter ); break; case ENC_ITEMS : geEncyclopediaDataFilterItem = static_cast(userDataFilterMode);; SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterItemText, pEncyclopediaSubFilterItemText, geEncyclopediaDataFilterItem ) ); nextData = GetNextPreviousItem( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterItem, guiEncyclopediaDataSubFilter ); break; case ENC_QUESTS : geEncyclopediaDataFilterQuest = static_cast(userDataFilterMode); SpecifyButtonText( btn->IDNum, GetDataFilterText( pEncyclopediaFilterQuestText, pEncyclopediaSubFilterQuestText, geEncyclopediaDataFilterQuest ) ); nextData = GetNextPreviousQuest( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterQuest, guiEncyclopediaDataSubFilter ); break; default : Assert(0); } CreateData( nextData ); // ...and activate first button. EnableButton( giEncyclopedia_DataFilterBtn[ 0 ] ); // activate navigation buttons EnableButton( giEncyclopedia_DataBtn[ 2 ] ); EnableButton( giEncyclopedia_DataBtn[ 4 ] ); // release all other buttons except this one for( UINT8 i = 0; i < sizeof(giEncyclopedia_DataFilterBtn)/sizeof(INT32); i++ ) { if( giEncyclopedia_DataFilterBtn[i] != btn->IDNum && giEncyclopedia_DataFilterBtn[i] != BUTTON_NO_SLOT ) { GetButtonPtr(giEncyclopedia_DataFilterBtn[i])->uiFlags &= (~BUTTON_CLICKED_ON); } } // render new data fPausedReDrawScreenFlag = TRUE; // play button down sound (only required for right mouse click) PlayButtonSound( btn->IDNum, BUTTON_SOUND_CLICKED_OFF ); } } /** * @brief Encyclopedia text page button callback. * @details Handles switching between different text pages within the same data entry. */ void BtnEncyclopedia_Data_NextPreviousTextBtnCallBack( GUI_BUTTON *btn, INT32 reason ) { CHECKV( gGameExternalOptions.gEncyclopedia ); CHECKV( btn ); BOOLEAN next = (BOOLEAN)MSYS_GetBtnUserData( btn, 1 ); if( (reason & MSYS_CALLBACK_REASON_LBUTTON_UP) || (reason & MSYS_CALLBACK_REASON_RBUTTON_UP) ) { btn->uiFlags &= (~BUTTON_CLICKED_ON); InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY ); #ifdef _DEBUG gstEncyclopediaDataEntry.uiTextPage += next? 1 : -1; #else if( gstEncyclopediaDataEntry.uiTextPage == 0 && next ) gstEncyclopediaDataEntry.uiTextPage = 1; else if( gstEncyclopediaDataEntry.uiTextPage == 1 && !next ) gstEncyclopediaDataEntry.uiTextPage = 0; else return; #endif CreateData( gstEncyclopediaDataEntry.iID ); fPausedReDrawScreenFlag = TRUE; } } /** * @brief Encyclopedia data page button callback. * @details Handles traversing thru data either forward or backward. Uses userdata at index 1 to determine which direction to go. */ void BtnEncyclopedia_Data_NextPreviousDataBtnCallBack ( GUI_BUTTON *btn, INT32 reason ) { CHECKV( gGameExternalOptions.gEncyclopedia ); CHECKV( btn ); if ( (reason & MSYS_CALLBACK_REASON_LBUTTON_UP) || (reason & MSYS_CALLBACK_REASON_RBUTTON_UP) ) { INT16 nextData = ENC_DATA_NO_DATA; BOOLEAN next = (BOOLEAN)MSYS_GetBtnUserData( btn, 1 ); // get next or previous data depending on which button was pressed switch (geENC_SubPage) { case ENC_LOCATIONS : nextData = GetNextPreviousLocation( gstEncyclopediaDataEntry.iID, next, geEncyclopediaDataFilterLocation, guiEncyclopediaDataSubFilter ); break; case ENC_CHARACTERS : nextData = GetNextPreviousCharacter( gstEncyclopediaDataEntry.iID, next, geEncyclopediaDataFilterCharacter, guiEncyclopediaDataSubFilter ); break; case ENC_ITEMS : nextData = GetNextPreviousItem( gstEncyclopediaDataEntry.iID, next, geEncyclopediaDataFilterItem, guiEncyclopediaDataSubFilter ); break; case ENC_QUESTS : nextData = GetNextPreviousQuest( gstEncyclopediaDataEntry.iID, next, geEncyclopediaDataFilterQuest, guiEncyclopediaDataSubFilter ); break; default: Assert(0); } // release button btn->uiFlags &= (~BUTTON_CLICKED_ON); InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); // check for changed data if( nextData == ENC_DATA_NO_DATA ) { // no data?... if (next) { // ...disable next button. CHECKV( giEncyclopedia_DataBtn[4] ); DisableButton( giEncyclopedia_DataBtn[4] ); } else { // ...disable previous button. CHECKV( giEncyclopedia_DataBtn[2] ); DisableButton( giEncyclopedia_DataBtn[2] ); } } else if( nextData != gstEncyclopediaDataEntry.iID ) { //new data? CreateData( nextData ); // reenable button if (next) { // ...enable previous button. CHECKV( giEncyclopedia_DataBtn[2] ); EnableButton( giEncyclopedia_DataBtn[2] ); } else { // ...enable next button. CHECKV( giEncyclopedia_DataBtn[4] ); EnableButton( giEncyclopedia_DataBtn[4] ); } // rerender screen since we changed data. fPausedReDrawScreenFlag = TRUE; } } } ///@} ////////////// // laptop functions /** @defgroup ENC_DATA Data page * Encyclopedia data page. * @ingroup ENCYCLOPEDIA * @{*/ /** * Checks for all needed files (graphics, xmls, ini option). * * Called when a new game is started (guiCurrentScreen == 0), entering new game screen/loading game from main menu (guiCurrentScreen == MAINMENU_SCREEN) and quitting game (guiCurrentScreen == MSG_BOX_SCREEN). * Sets gui element initial values. * Shows error message and deactivates the Encyclopedia if a file is missing. * @todo check for all files needed by data page here. */ void GameInitEncyclopediaData_NEW( ) { //initialize UI elements memset( giEncyclopedia_DataBtn, BUTTON_NO_SLOT, sizeof(giEncyclopedia_DataBtn) ); giEncyclopedia_DataBtnImage = BUTTON_NO_IMAGE; memset( giEncyclopedia_DataFilterBtn, BUTTON_NO_SLOT, sizeof(giEncyclopedia_DataFilterBtn) ); giEncyclopedia_DataFilterBtnImage = BUTTON_NO_IMAGE; if( guiCurrentScreen == MAINMENU_SCREEN ) EncyclopediaInitItemsVisibility(); // do following only once at start of JA2 CHECKV( guiCurrentScreen == 0 ); //prepare indexes for subfilter texts defined in _LanguageText.cpp, assuming there are blank separators between filter button texts ("1", "2", "3", "", "1", "", "1", "2", "3", "4") STR16 *pText, testStr; UINT8 failsafe = 1, index; for( UINT8 site = 1; site <= ENC_NUM_SUBPAGES; site++ ) { //get text pointer switch(site) { case ENC_LOCATIONS : pText = pEncyclopediaSubFilterLocationText; break; case ENC_CHARACTERS : pText = pEncyclopediaSubFilterCharText; break; case ENC_ITEMS : pText = pEncyclopediaSubFilterItemText; break; case ENC_QUESTS : pText = pEncyclopediaSubFilterQuestText; break; default : AssertMsg(0, "Unrecognized DataPage in GameInitEncyclopediaData()"); return; } index = 0; for( UINT8 filter = 0; filter < ENC_DATA_MAX_FILTERS-1; filter++ ) if(filter == 0) guiEncyclopediaSubFilterTextIndexStart[site-1][filter] = 0; else { testStr = pText[index],failsafe = 1; while( failsafe && testStr[0] != L'\0' ) testStr = pText[++index], failsafe++; guiEncyclopediaSubFilterTextIndexStart[site-1][filter] = ++index; } } } /** * Helper function gets called AFTER rendering by game loop. * * Can be used to poll keyboard or mouse events. * Can also be used for animations (e.g. showing blinking position of person on a map, simulate loading of commercials, animated zoom ec.). */ void HandleEncyclopediaData_NEW( ) { } /** * Creates video objects, buttons, fasthelp texts, mousregions. * * Called when entering encyclopedia data web page in laptop. Prepares data page of Encyclopedia. * @return FALSE on error. TRUE on success. * @see ExitEncyclopediaData_NEW() */ BOOLEAN EnterEncyclopediaData_NEW( ) { VOBJECT_DESC VObjectDesc; INT16 sNewItem = -1; extern UINT32 guiEncyclopediaBG;//background image from Encyclopedia main page UINT16 filterButtonSizeX, filterButtonSizeY, buttonSizeX, buttonSizeY; UINT8 numFilterButtons = 0, filterButtonHelpTextFirstID; STR16 *filterButtonText, *filterButtonHelpText; //error if not enabled CHECKF( gGameExternalOptions.gEncyclopedia ); //error if the laptop mode does not match encyclopedia CHECKF( LAPTOP_MODE_ENCYCLOPEDIA_DATA == guiCurrentLaptopMode ); SetBookMark( ENCYCLOPEDIA_BOOKMARK );//bookmark should be allways set, just in case it is not. ////// // load the background graphic VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; FilenameForBPP( "ENCYCLOPEDIA\\encyclopediabackground.sti", VObjectDesc.ImageFile ); CHECKF( AddVideoObject( &VObjectDesc, &guiEncyclopediaBG ) ); ////// // get first data, get number of buttons and text guiEncyclopediaDataSubFilter = 0; switch( geENC_SubPage ) { case ENC_LOCATIONS : //reset filter geEncyclopediaDataFilterLocation = ENC_DATA_FILTER_L_ALL; //find first item sNewItem = GetNextPreviousLocation( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterLocation, guiEncyclopediaDataSubFilter ); //get number of buttons numFilterButtons = ENC_DATA_FILTER_L_OTHER + 1; //get button texts and helptexts filterButtonText = pEncyclopediaFilterLocationText; filterButtonHelpText = pEncyclopediaFilterLocationText; filterButtonHelpTextFirstID = numFilterButtons; break; case ENC_CHARACTERS : geEncyclopediaDataFilterCharacter = ENC_DATA_FILTER_C_ALL; sNewItem = GetNextPreviousCharacter( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterCharacter, guiEncyclopediaDataSubFilter ); numFilterButtons = ENC_DATA_FILTER_C_OTHER + 1; filterButtonText = pEncyclopediaFilterCharText; filterButtonHelpText = pEncyclopediaFilterCharText; filterButtonHelpTextFirstID = numFilterButtons; break; case ENC_ITEMS : geEncyclopediaDataFilterItem = ENC_DATA_FILTER_I_ALL; sNewItem = GetNextPreviousItem( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterItem, guiEncyclopediaDataSubFilter ); numFilterButtons = ENC_DATA_FILTER_I_MISC + 1; filterButtonText = pEncyclopediaFilterItemText; filterButtonHelpText = pEncyclopediaFilterItemText; filterButtonHelpTextFirstID = numFilterButtons; break; case ENC_QUESTS : geEncyclopediaDataFilterQuest = ENC_DATA_FILTER_Q_ALL; sNewItem = GetNextPreviousQuest( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterQuest, guiEncyclopediaDataSubFilter ); numFilterButtons = ENC_DATA_FILTER_Q_COMPLETE + 1; filterButtonText = pEncyclopediaFilterQuestText; filterButtonHelpText = pEncyclopediaFilterQuestText; filterButtonHelpTextFirstID = numFilterButtons; break; default : AssertMsg(0, "Unrecognized DataPage in EnterEncyclopediaData()"); return (FALSE); } ////// // load filter button graphic and get size, NOTE: Needs to be loaded to determine size for other UI elements, even when no filter buttons will be created. VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; giEncyclopedia_DataFilterBtnImage = LoadButtonImage( "ENCYCLOPEDIA\\BUTTONF.sti", BUTTON_NO_IMAGE, 0, BUTTON_NO_IMAGE , 1, BUTTON_NO_IMAGE ); CHECKF( giEncyclopedia_DataFilterBtnImage != BUTTON_NO_IMAGE ); filterButtonSizeX = GetWidthOfButtonPic( giEncyclopedia_DataFilterBtnImage, 0 ); filterButtonSizeY = GetHeightOfButtonPic ( giEncyclopedia_DataFilterBtnImage, 0 ); ////// // create filter buttons for( UINT8 i = 0; i < numFilterButtons; i++ ) { giEncyclopedia_DataFilterBtn[ i ] = CreateIconAndTextButton( giEncyclopedia_DataFilterBtnImage,//image filterButtonText[ i ], BLOCKFONTNARROW, FONT_MCOLOR_DKWHITE, NO_SHADOW, FONT_GRAY4, NO_SHADOW, TEXT_CJUSTIFIED,//text LAPTOP_SCREEN_UL_X + ENC_DATA_GAP, //x position: left side LAPTOP_SCREEN_WEB_UL_Y + ENC_DATA_FILTER_GAP_TOP + i * (filterButtonSizeY + ENC_DATA_GAP),//y position: top + gap + previous buttons with gaps BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGH, BUTTON_NO_CALLBACK, BtnEncyclopedia_Data_FilterBtnCallBack ); //created? CHECKF( giEncyclopedia_DataFilterBtn[ i ] != BUTTON_NO_SLOT ); //cursor SetButtonCursor( giEncyclopedia_DataFilterBtn[ i ], CURSOR_LAPTOP_SCREEN ); //user data for filters MSYS_SetBtnUserData( giEncyclopedia_DataFilterBtn[ i ], 1, i );//Filter, fixed per button. MSYS_SetBtnUserData( giEncyclopedia_DataFilterBtn[ i ], 2, 0 );//SubFilter, gets updated on button click. This way we can 'load' the setting of a filter button. //fast help text SetButtonFastHelpText( giEncyclopedia_DataFilterBtn[ i ], filterButtonHelpText[ i + filterButtonHelpTextFirstID ] ); ///TODO: disabling shifted button icon/text does not work //GetButtonPtr( giEncyclopedia_DataFilterBtn[ i ] )->fShiftImage = FALSE; } // set first filter button as selected if( numFilterButtons ) { DisableButton( giEncyclopedia_DataFilterBtn[ 0 ] ); } ////// // load button graphic for next/previous/main page/nextText/previousText and get size VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; giEncyclopedia_DataBtnImage = LoadButtonImage( "ENCYCLOPEDIA\\BOTTOMBUTTONS2.STI", BUTTON_NO_IMAGE, 0, BUTTON_NO_IMAGE , 1, BUTTON_NO_IMAGE ); CHECKF( giEncyclopedia_DataBtnImage != BUTTON_NO_IMAGE ); buttonSizeX = GetWidthOfButtonPic( giEncyclopedia_DataBtnImage, 0 ); buttonSizeY = GetHeightOfButtonPic( giEncyclopedia_DataBtnImage, 0 ); ////// // create previousText button giEncyclopedia_DataBtn[ 0 ] = CreateIconAndTextButton( giEncyclopedia_DataBtnImage,//image pLocationPageText[ 0 ], FONT10ARIAL, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, TEXT_CJUSTIFIED,//text LAPTOP_SCREEN_UL_X + filterButtonSizeX + 2*ENC_DATA_GAP,//x position: left alligned to image box LAPTOP_SCREEN_WEB_UL_Y + GetFontHeight(FONT14ARIAL) + ENC_DATA_IMAGE_HEIGHT + ENC_DATA_SHORT_DESC_HEIGHT + 5*ENC_DATA_GAP,//y position: below short descr box BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, BtnEncyclopedia_Data_NextPreviousTextBtnCallBack ); CHECKF( giEncyclopedia_DataBtn[ 0 ] != BUTTON_NO_SLOT ); MSYS_SetBtnUserData( giEncyclopedia_DataBtn[ 0 ], 1 , FALSE );//previous -> next = FALSE ////// // create nextText button giEncyclopedia_DataBtn[ 1 ] = CreateIconAndTextButton( giEncyclopedia_DataBtnImage,//image pLocationPageText[ 2 ], FONT10ARIAL, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, TEXT_CJUSTIFIED,//text LAPTOP_SCREEN_LR_X - filterButtonSizeX - 2*ENC_DATA_GAP - buttonSizeX,//x position: right alligned to image box LAPTOP_SCREEN_WEB_UL_Y + GetFontHeight(FONT14ARIAL) + ENC_DATA_IMAGE_HEIGHT + ENC_DATA_SHORT_DESC_HEIGHT + 5*ENC_DATA_GAP,//y position: below short descr box BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, BtnEncyclopedia_Data_NextPreviousTextBtnCallBack ); CHECKF( giEncyclopedia_DataBtn[ 1 ] != BUTTON_NO_SLOT ); MSYS_SetBtnUserData( giEncyclopedia_DataBtn[ 1 ], 1 , TRUE );//next -> next = TRUE ////// // create previousData button giEncyclopedia_DataBtn[ 2 ] = CreateIconAndTextButton( giEncyclopedia_DataBtnImage,//image pSectorPageText[ 0 ], FONT10ARIAL, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, TEXT_CJUSTIFIED,//text LAPTOP_SCREEN_UL_X + LAPTOP_SCREEN_WIDTH/2 - 2*buttonSizeX,//x position: center of laptop - 2* button size LAPTOP_SCREEN_WEB_UL_Y + GetFontHeight(FONT14ARIAL) + ENC_DATA_IMAGE_HEIGHT + ENC_DATA_SHORT_DESC_HEIGHT + buttonSizeY + ENC_DATA_DESC_HEIGHT + 7*ENC_DATA_GAP,//y position: below all BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, BtnEncyclopedia_Data_NextPreviousDataBtnCallBack ); CHECKF( giEncyclopedia_DataBtn[ 2 ] != BUTTON_NO_SLOT ); MSYS_SetBtnUserData( giEncyclopedia_DataBtn[ 2 ], 1 , FALSE );//previous -> next = FALSE ////// // create main page button giEncyclopedia_DataBtn[ 3 ] = CreateIconAndTextButton( giEncyclopedia_DataBtnImage,//image pSectorPageText[ 1 ], FONT10ARIAL, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, TEXT_CJUSTIFIED,//text LAPTOP_SCREEN_UL_X + LAPTOP_SCREEN_WIDTH/2 - buttonSizeX/2,//x position: center of laptop LAPTOP_SCREEN_WEB_UL_Y + GetFontHeight(FONT14ARIAL) + ENC_DATA_IMAGE_HEIGHT + ENC_DATA_SHORT_DESC_HEIGHT + buttonSizeY + ENC_DATA_DESC_HEIGHT + 7*ENC_DATA_GAP,//y position: below all BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, BtnEncyclopedia_newSelectDataPageBtnCallBack ); CHECKF( giEncyclopedia_DataBtn[ 3 ] != BUTTON_NO_SLOT ); MSYS_SetBtnUserData( giEncyclopedia_DataBtn[ 3 ], 1 , ENC_MAINPAGE ); ////// // create nextData button giEncyclopedia_DataBtn[ 4 ] = CreateIconAndTextButton( giEncyclopedia_DataBtnImage,//image pSectorPageText[ 2 ], FONT10ARIAL, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, FONT_FCOLOR_WHITE, DEFAULT_SHADOW, TEXT_CJUSTIFIED,//text LAPTOP_SCREEN_UL_X + LAPTOP_SCREEN_WIDTH/2 + buttonSizeX,//x position: center of laptop + buttonsize LAPTOP_SCREEN_WEB_UL_Y + GetFontHeight(FONT14ARIAL) + ENC_DATA_IMAGE_HEIGHT + ENC_DATA_SHORT_DESC_HEIGHT + buttonSizeY + ENC_DATA_DESC_HEIGHT + 7*ENC_DATA_GAP,//y position: below all BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, BtnEncyclopedia_Data_NextPreviousDataBtnCallBack ); CHECKF( giEncyclopedia_DataBtn[ 4 ] != BUTTON_NO_SLOT ); MSYS_SetBtnUserData( giEncyclopedia_DataBtn[ 4 ], 1 , TRUE );//next -> next = TRUE ////// // set cursors and make button down state slightly shifted for( UINT8 i = 0; i < 5; i++ ) { SetButtonCursor( giEncyclopedia_DataBtn[ i ], CURSOR_LAPTOP_SCREEN ); GetButtonPtr( giEncyclopedia_DataBtn[ i ] )->fShiftImage = TRUE; } ////// // fill data, also creates video object for image and deletes old one. CreateData(sNewItem); return (TRUE); } /** * Destroys video objects, buttons, fasthelp texts, mousregions. * * Called on exit of encyclopedia data web page in laptop. Cleans up data page of Encyclopedia. * @return FALSE on error. TRUE on success. * @see EnterEncyclopediaData_NEW() */ BOOLEAN ExitEncyclopediaData_NEW( ) { extern UINT32 guiEncyclopediaBG;//background image from Encyclopedia main page BOOLEAN success = TRUE; // error if not enabled CHECKF( gGameExternalOptions.gEncyclopedia ); // error if the laptop mode does not match encyclopedia CHECKF( LAPTOP_MODE_ENCYCLOPEDIA_DATA == guiPreviousLaptopMode ); // Delete background success &= DeleteVideoObjectFromIndex( guiEncyclopediaBG ); guiEncyclopediaBG = 0; // Reset filters guiEncyclopediaDataSubFilter = 0; geEncyclopediaDataFilterLocation = ENC_DATA_FILTER_L_ALL; geEncyclopediaDataFilterCharacter = ENC_DATA_FILTER_C_ALL; geEncyclopediaDataFilterItem = ENC_DATA_FILTER_I_ALL; geEncyclopediaDataFilterQuest = ENC_DATA_FILTER_Q_ALL; // Reset data and remove data image success &= CreateData( -1 ); // Destroy filter buttons for( UINT8 i = 0; i < (UINT8)(sizeof(giEncyclopedia_DataFilterBtn)/sizeof(INT32)); i++ ) { if( giEncyclopedia_DataFilterBtn[ i ] != BUTTON_NO_SLOT ) { RemoveButton( giEncyclopedia_DataFilterBtn[ i ] ); giEncyclopedia_DataFilterBtn[ i ] = BUTTON_NO_SLOT; } } // Delete filter button image if ( giEncyclopedia_DataFilterBtnImage != BUTTON_NO_IMAGE ) { UnloadButtonImage( giEncyclopedia_DataFilterBtnImage ); giEncyclopedia_DataFilterBtnImage = BUTTON_NO_IMAGE; } // Delete navigation buttons for( UINT8 i = 0; i < (UINT8)(sizeof(giEncyclopedia_DataBtn)/sizeof(INT32)); i++ ) { if ( giEncyclopedia_DataBtn[ i ] != BUTTON_NO_SLOT ) { RemoveButton( giEncyclopedia_DataBtn[ i ] ); giEncyclopedia_DataBtn[ i ] = BUTTON_NO_SLOT; } } // Delete navigation button image if ( giEncyclopedia_DataBtnImage != BUTTON_NO_IMAGE ) { UnloadButtonImage( giEncyclopedia_DataBtnImage ); giEncyclopedia_DataBtnImage = BUTTON_NO_IMAGE; } return success; } /** * @brief Renders data page of encyclopedia. */ void RenderEncyclopediaData_NEW( ) { UINT16 x, y, shadowWidth; extern UINT32 guiEncyclopediaBG;//background image from Encyclopedia main page CHECKV( gGameExternalOptions.gEncyclopedia ); // render background x = LAPTOP_SCREEN_UL_X; y = LAPTOP_SCREEN_WEB_UL_Y; CHECKV( BltVideoObjectFromIndex( FRAME_BUFFER, guiEncyclopediaBG, 0, x, y, VO_BLT_SRCTRANSPARENCY, NULL ) ); y += ENC_DATA_GAP; x += 2*ENC_DATA_GAP + GetWidthOfButtonPic( giEncyclopedia_DataFilterBtnImage, 0 ); shadowWidth = LAPTOP_SCREEN_WIDTH - 4*ENC_DATA_GAP - 2*GetWidthOfButtonPic( giEncyclopedia_DataFilterBtnImage, 0 ); // render name y-centered if( gstEncyclopediaDataEntry.sName[ 0 ] != '\0' ) { CHECKV( DrawTextToScreen( gstEncyclopediaDataEntry.sName, x, y, shadowWidth, FONT14ARIAL, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ) ); } y += 2*ENC_DATA_GAP + GetFontHeight( FONT14ARIAL ); // Shadow image area CHECKV( ShadowVideoSurfaceRect( FRAME_BUFFER, x, y, x + shadowWidth, y + ENC_DATA_IMAGE_HEIGHT ) ); // Render image centered into shadowed surface if( gstEncyclopediaDataEntry.uiImageID ) {//TODO: scaling only works on 16Bit surface, BltStretchVideoSurface needs to support the other bit depths as well. FLOAT scaleRatio; HVOBJECT hVObject; SGPRect sourceRect, destRect; CHECKV( GetVideoObject( &hVObject, gstEncyclopediaDataEntry.uiImageID ) ); CHECKV( hVObject ); CHECKV( hVObject->pETRLEObject); // 'min( xDest/xSrc, yDest/ySrc )' scaleRatio = hVObject->pETRLEObject->usWidth? (FLOAT)(shadowWidth - 2*ENC_DATA_GAP)/hVObject->pETRLEObject->usWidth: 0.0f; scaleRatio = min( scaleRatio, hVObject->pETRLEObject->usHeight? (FLOAT)(ENC_DATA_IMAGE_HEIGHT - 2*ENC_DATA_GAP)/hVObject->pETRLEObject->usHeight: 0.0f ); // blt original image into extraBuffer, as there might be negative offsets it gets rendered 100 pix shifted ColorFillVideoSurfaceArea( guiEXTRABUFFER, 0, 0, 100 + shadowWidth, 100 + ENC_DATA_IMAGE_HEIGHT, 0 ); BltVideoObjectFromIndex( guiEXTRABUFFER, gstEncyclopediaDataEntry.uiImageID, 0, 100, 100, VO_BLT_SRCTRANSPARENCY, NULL ); // now blt extra buffer scalled into FRAME_BUFFER sourceRect.iLeft = 100 + hVObject->pETRLEObject->sOffsetX; sourceRect.iTop = 100 + hVObject->pETRLEObject->sOffsetY; sourceRect.iRight = 100 + hVObject->pETRLEObject->usWidth + hVObject->pETRLEObject->sOffsetX; sourceRect.iBottom = 100 + hVObject->pETRLEObject->usHeight + hVObject->pETRLEObject->sOffsetY; destRect.iLeft = x + ENC_DATA_GAP + shadowWidth/2 - (UINT16)(scaleRatio * hVObject->pETRLEObject->usWidth/2); destRect.iTop = y + ENC_DATA_GAP + ENC_DATA_IMAGE_HEIGHT/2 - (UINT16)(scaleRatio * hVObject->pETRLEObject->usHeight/2); destRect.iRight = x - ENC_DATA_GAP + shadowWidth/2 + (UINT16)(scaleRatio * hVObject->pETRLEObject->usWidth/2); destRect.iBottom = y - ENC_DATA_GAP + ENC_DATA_IMAGE_HEIGHT/2 + (UINT16)(scaleRatio * hVObject->pETRLEObject->usHeight/2); BltStretchVideoSurface( FRAME_BUFFER, guiEXTRABUFFER, 0, 0, VO_BLT_SRCTRANSPARENCY, &sourceRect, &destRect ); } y += ENC_DATA_IMAGE_HEIGHT + ENC_DATA_GAP; // Shadow short desc. area CHECKV( ShadowVideoSurfaceRect( FRAME_BUFFER, x, y, x + shadowWidth, y + ENC_DATA_SHORT_DESC_HEIGHT ) ); y += ENC_DATA_GAP; // Render short description if( gstEncyclopediaDataEntry.sShortDesc[ 0 ] != '\0' ) { CHECKV( DrawTextToScreen( gstEncyclopediaDataEntry.sShortDesc, x + ENC_DATA_GAP, y, shadowWidth, FONT10ARIAL, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ) ); } y += ENC_DATA_SHORT_DESC_HEIGHT + GetButtonHeight( giEncyclopedia_DataBtn[ 0 ] ) + ENC_DATA_GAP; // Shadow descritpion area CHECKV( ShadowVideoSurfaceRect( FRAME_BUFFER, x, y, x + shadowWidth, y + ENC_DATA_DESC_HEIGHT ) ); // Render detailed description using word wrap if ( gstEncyclopediaDataEntry.sDescr[ 0 ] != '\0' ) { CHECKV( DisplayWrappedString( x + ENC_DATA_GAP, y + ENC_DATA_GAP, shadowWidth - 2*ENC_DATA_GAP, ENC_DATA_GAP/2, FONT10ARIAL, FONT_MCOLOR_WHITE, gstEncyclopediaDataEntry.sDescr, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ) ); } // finish render MarkButtonsDirty(); RenderWWWProgramTitleBar(); InvalidateRegion(LAPTOP_SCREEN_UL_X,LAPTOP_SCREEN_WEB_UL_Y,LAPTOP_SCREEN_LR_X,LAPTOP_SCREEN_WEB_LR_Y); //fReDrawScreenFlag = TRUE; //fPausedReDrawScreenFlag = TRUE; ////// // Render buttons //RenderButtons(); } /** @} */ #endif