Files
source/Laptop/Encyclopedia_Data_new.cpp
T
majcostaandGitHub ae55496e39 Move main executable source to its own directory (#248)
* Move main executable files to their own directory

* Adapt the build to the new file structure

... and fix some weird #include's that were relying on the project root
being an include directory.
2023-10-23 15:46:51 -03:00

1718 lines
78 KiB
C++

#include "Types.h"
#include "WCheck.h"
#include <string.h>
#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<PDestinationStruct> 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<ENC_DATA_FILTER_LOCATION>(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<ENC_DATA_FILTER_CHARACTER>(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<ENC_DATA_FILTER_ITEM>(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<ENC_DATA_FILTER_QUEST>(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<ENC_DATA_FILTER_LOCATION>(userDataFilterMode);
nextData = GetNextPreviousLocation( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterLocation, guiEncyclopediaDataSubFilter );
break;
case ENC_CHARACTERS :
geEncyclopediaDataFilterCharacter = static_cast<ENC_DATA_FILTER_CHARACTER>(userDataFilterMode);
nextData = GetNextPreviousCharacter( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterCharacter, guiEncyclopediaDataSubFilter );
break;
case ENC_ITEMS :
geEncyclopediaDataFilterItem = static_cast<ENC_DATA_FILTER_ITEM>(userDataFilterMode);
nextData = GetNextPreviousItem( ENC_DATA_NO_DATA, ENC_DATA_NEXT, geEncyclopediaDataFilterItem, guiEncyclopediaDataSubFilter );
break;
case ENC_QUESTS :
geEncyclopediaDataFilterQuest = static_cast<ENC_DATA_FILTER_QUEST>(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<ENC_DATA_FILTER_LOCATION>(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<ENC_DATA_FILTER_CHARACTER>(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<ENC_DATA_FILTER_ITEM>(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<ENC_DATA_FILTER_QUEST>(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