diff --git a/GameSettings.cpp b/GameSettings.cpp index 8f08d9fe..a3f31c3f 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -901,12 +901,12 @@ void LoadGameExternalOptions() gGameExternalOptions.iMilitiaCostModifier = iniReader.ReadInteger("Financial Settings","MILITIA_COST_MULTIPLIER_MOBILE",3, 1, 10); // HEADROCK HAM 3.6: Daily upkeep costs for militia - gGameExternalOptions.usDailyCostTownGreen = iniReader.ReadInteger("Financial Settings","DAILY_MILITIA_UPKEEP_TOWN_GREEN", 0, 0, 10000); - gGameExternalOptions.usDailyCostTownRegular = iniReader.ReadInteger("Financial Settings","DAILY_MILITIA_UPKEEP_TOWN_REGULAR", 0, 0, 10000); - gGameExternalOptions.usDailyCostTownElite = iniReader.ReadInteger("Financial Settings","DAILY_MILITIA_UPKEEP_TOWN_ELITE", 0, 0, 10000); - gGameExternalOptions.usDailyCostMobileGreen = iniReader.ReadInteger("Financial Settings","DAILY_MILITIA_UPKEEP_MOBILE_GREEN", 0, 0, 10000); - gGameExternalOptions.usDailyCostMobileRegular = iniReader.ReadInteger("Financial Settings","DAILY_MILITIA_UPKEEP_MOBILE_REGULAR", 0, 0, 10000); - gGameExternalOptions.usDailyCostMobileElite = iniReader.ReadInteger("Financial Settings","DAILY_MILITIA_UPKEEP_MOBILE_ELITE", 0, 0, 10000); + gGameExternalOptions.usDailyCostTown[GREEN_MILITIA] = iniReader.ReadInteger( "Financial Settings", "DAILY_MILITIA_UPKEEP_TOWN_GREEN", 0, 0, 10000 ); + gGameExternalOptions.usDailyCostTown[REGULAR_MILITIA] = iniReader.ReadInteger( "Financial Settings", "DAILY_MILITIA_UPKEEP_TOWN_REGULAR", 0, 0, 10000 ); + gGameExternalOptions.usDailyCostTown[ELITE_MILITIA] = iniReader.ReadInteger( "Financial Settings", "DAILY_MILITIA_UPKEEP_TOWN_ELITE", 0, 0, 10000 ); + gGameExternalOptions.usDailyCostMobile[GREEN_MILITIA] = iniReader.ReadInteger( "Financial Settings", "DAILY_MILITIA_UPKEEP_MOBILE_GREEN", 0, 0, 10000 ); + gGameExternalOptions.usDailyCostMobile[REGULAR_MILITIA] = iniReader.ReadInteger( "Financial Settings", "DAILY_MILITIA_UPKEEP_MOBILE_REGULAR", 0, 0, 10000 ); + gGameExternalOptions.usDailyCostMobile[ELITE_MILITIA] = iniReader.ReadInteger( "Financial Settings", "DAILY_MILITIA_UPKEEP_MOBILE_ELITE", 0, 0, 10000 ); // HEADROCK HAM 3.5: Helicopter - Base cost per sector. GREEN = Free airspace, RED = Enemy-controlled airspace. gGameExternalOptions.usHelicopterBaseCostPerGreenTile = iniReader.ReadInteger("Financial Settings","HELICOPTER_BASE_COST_PER_GREEN_TILE", 100, 0, 60000); @@ -1370,7 +1370,7 @@ void LoadGameExternalOptions() gGameExternalOptions.fSoldierProfiles_Militia = iniReader.ReadBoolean("Tactical Gameplay Settings", "SOLDIER_PROFILES_MILITIA", TRUE); else gGameExternalOptions.fSoldierProfiles_Militia = FALSE; - + gGameExternalOptions.fAllowDrivingVehiclesInTactical = iniReader.ReadBoolean("Tactical Gameplay Settings","ALLOW_DRIVING_VEHICLES_IN_TACTICAL", TRUE); gGameExternalOptions.fAllowCarsDrivingOverPeople = iniReader.ReadBoolean("Tactical Gameplay Settings","ALLOW_CARS_DRIVING_OVER_PEOPLE", TRUE); @@ -1533,7 +1533,16 @@ void LoadGameExternalOptions() gGameExternalOptions.fEnemyGeneralStrategicDecisionSpeedBonus = iniReader.ReadFloat( "Tactical Enemy Role Settings", "ENEMY_GENERALS_STRATEGIC_DECISION_SPEEDBONUS", 0.05f, 0.00f, 0.1f ); gGameExternalOptions.fEnemyGeneralStrategicMovementSpeedBonus = iniReader.ReadFloat( "Tactical Enemy Role Settings", "ENEMY_GENERALS_STRATEGIC_MOVEMENT_SPEEDBONUS", 0.03f, 0.00f, 0.1f ); - + //################# Individual Militia Settings ################## + // Flugente: individual militia + gGameExternalOptions.fIndividualMilitia = iniReader.ReadBoolean( "Individual Militia Settings", "INDIVIDUAL_MILITIA", FALSE ); + + gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Regular = iniReader.ReadInteger( "Individual Militia Settings", "INDIVIDUAL_MILITIA_PROMOTIONPOINTS_TO_REGULAR", 4, 1, 100 ); + gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Elite = iniReader.ReadInteger( "Individual Militia Settings", "INDIVIDUAL_MILITIA_PROMOTIONPOINTS_TO_ELITE", 10, 1, 100 ); + + gGameExternalOptions.fIndividualMilitia_ManageHealth = iniReader.ReadBoolean( "Individual Militia Settings", "INDIVIDUAL_MILITIA_MANAGE_HEALTH", TRUE ); + gGameExternalOptions.dIndividualMilitiaHourlyHealthPercentageGain = iniReader.ReadFloat( "Individual Militia Settings", "INDIVIDUAL_MILITIA_HOURLYHEALTHPERCENTAGEGAIN", 2.0f, 0.0f, 100.0f ); + //################# Tactical Cover System Settings ################## // CPT: Cover System Settings diff --git a/GameSettings.h b/GameSettings.h index 1257d239..d6a14bc7 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -5,6 +5,7 @@ #include "Strategic Status.h" #include "Morale.h" #include "GameInitOptionsScreen.h" +#include "Campaign Types.h" #define GAME_INI_FILE "Ja2.ini" @@ -1172,12 +1173,8 @@ typedef struct UINT8 ubMaxMessagesTactical; // HEADROCK HAM 3.6: Daily upkeep costs for militia - UINT16 usDailyCostTownGreen; - UINT16 usDailyCostTownRegular; - UINT16 usDailyCostTownElite; - UINT16 usDailyCostMobileGreen; - UINT16 usDailyCostMobileRegular; - UINT16 usDailyCostMobileElite; + UINT16 usDailyCostTown[MAX_MILITIA_LEVELS]; + UINT16 usDailyCostMobile[MAX_MILITIA_LEVELS]; // HEADROCK HAM 3.6: Non-Combat Bodytypes can't become hostile BOOLEAN fCanTrueCiviliansBecomeHostile; @@ -1401,6 +1398,12 @@ typedef struct FLOAT fEnemyGeneralStrategicDecisionSpeedBonus; FLOAT fEnemyGeneralStrategicMovementSpeedBonus; + // Flugente: individual militia + BOOLEAN fIndividualMilitia; + UINT16 usIndividualMilitia_PromotionPoints_To_Regular; + UINT16 usIndividualMilitia_PromotionPoints_To_Elite; + BOOLEAN fIndividualMilitia_ManageHealth; + FLOAT dIndividualMilitiaHourlyHealthPercentageGain; // Sandro: Alternative weapon holding (rifles fired from hip / pistols fired one-handed) UINT8 ubAllowAlternativeWeaponHolding; diff --git a/GameVersion.h b/GameVersion.h index 4f47be51..ceccc42f 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -21,6 +21,7 @@ extern CHAR16 zTrackingNumber[16]; // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file +#define INDIVIDUAL_MILITIA 166 // Flugente: individual militia #define ENEMY_JEEPS 165 // Flugente: enemy jeeps #define ENEMY_HELICOPTERS 164 // Flugente: enemy helicopters #define DRUG_SYSTEM_REDONE 163 // Flugente: reworked the drug system @@ -84,7 +85,7 @@ extern CHAR16 zTrackingNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system -#define SAVE_GAME_VERSION ENEMY_JEEPS +#define SAVE_GAME_VERSION INDIVIDUAL_MILITIA //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/Init.cpp b/Init.cpp index 7cd067ff..1745ebca 100644 --- a/Init.cpp +++ b/Init.cpp @@ -1077,6 +1077,12 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) } #endif + // Flugente: individual militia + strcpy( fileName, directoryName ); + strcat( fileName, INDIVIDUALMILITIAFILENAME ); + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "LoadExternalGameplayData, fileName = %s", fileName ) ); + SGP_THROW_IFFALSE( ReadInMilitiaIndividual( fileName, FALSE ), INDIVIDUALMILITIAFILENAME ); + // Flugente: campaign stats strcpy(fileName, directoryName); strcat(fileName, CAMPAIGNSTATSEVENTSFILENAME); diff --git a/Laptop/BaseTable.cpp b/Laptop/BaseTable.cpp new file mode 100644 index 00000000..5ab2a3b5 --- /dev/null +++ b/Laptop/BaseTable.cpp @@ -0,0 +1,937 @@ +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#include "BaseTable.h" +#include "Font.h" +#include "laptop.h" +#include "Cursors.h" +#include "Utilities.h" +#include "vobject.h" +#include "WCheck.h" +#include "line.h" +#include "WordWrap.h" +#include "input.h" // for gfLeftButtonState +#include "insurance.h" // for DisplaySmallColouredLineWithShadow + +BaseTable::BaseTable( ) +: WidgetBase( ), + mName(L""), + fInit(FALSE), + musHeight(0), + musWidth(0), + musFrameWorkSpace(0), + mActive(TRUE), + mMouseRegionActive(TRUE) +{ +} + +void +BaseTable::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetX( sX ); + SetY( sY ); + + SetWidth( sX_End - sX ); + SetHeight( sY_End - sY ); + + for ( std::vector::iterator it = mRegisteredMembers.begin( ); it != mRegisteredMembers.end( ); ++it ) + { + if ( (*it) ) + (*it)->Init( sX, sY, sX_End, sY_End ); + } + + SetInit( TRUE ); +} + +void +BaseTable::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); +} + +void +BaseTable::Destroy( ) +{ + if ( !IsInit() ) + return; + + DestroyMouseRegions(); + + for ( std::vector::iterator it = mRegisteredMembers.begin( ); it != mRegisteredMembers.end( ); ++it ) + { + if ( (*it) ) + (*it)->Destroy( ); + } + + mRegisteredMembers.clear( ); + + SetInit(FALSE); + + SetRefresh( ); +} + +void +BaseTable::Display( ) +{ + if ( !IsInit() ) + return; + + for ( std::vector::iterator it = mRegisteredMembers.begin( ); it != mRegisteredMembers.end( ); ++it ) + { + if ( (*it) && (*it)->IsActive() ) + (*it)->Display( ); + } +} + +void +BaseTable::DestroyMouseRegions( ) +{ + if ( !IsInit( ) ) + return; + + for ( std::vector::iterator it = mRegisteredMembers.begin( ); it != mRegisteredMembers.end( ); ++it ) + { + if ( (*it) ) + (*it)->DestroyMouseRegions( ); + } +} + +STR16 cdp_string_func_dummy( UINT32 aNum ) +{ + return L"nothing found"; +} + +void cdp_image_func_dummy( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ) +{ + +} + +void cdp_statusbar_func_dummy( UINT32 aId, UINT16& arCol1, UINT16& arVal1, UINT16& arCol2, UINT16& arVal2, UINT16& arCol3, UINT16& arVal3, UINT16& arCol4, UINT16& arVal4 ) +{ + +} + +void +ColumnDataProvider::CalcRequiredLength( ) +{ + mRequiredLength = 0; + + if ( CDP_STRING == GetProviderType() ) + { + for ( UINT32 i = 0; i < mNumberOfEntries; ++i ) + { + mRequiredLength = max( mRequiredLength, 7 * wcslen( GetString( i ) ) ); + } + } + else if ( CDP_IMAGE == GetProviderType() ) + { + for ( UINT32 i = 0; i < mNumberOfEntries; ++i ) + { + // TODO... + mRequiredLength = max( mRequiredLength, 40 ); + } + } + else if ( CDP_STATUSBAR == GetProviderType( ) ) + { + mRequiredLength = 4; + } +} + +extern void callbackmilitialist( INT32 usId ); +extern void callbackmilitiadetail( INT32 usId ); + +void +ColumnDataProvider::RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ) +{ + if ( iReason & MSYS_CALLBACK_REASON_INIT ) + { + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + //gfScrollBoxIsScrolling = FALSE; + //gHelpScreen.iLastMouseClickY = -1; + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + CDP_CALLBACKTYPE chosenentry = (CDP_CALLBACKTYPE)(pRegion->UserData[0]); + + // do stuff + switch ( chosenentry ) + { + case CDP_MILITIA_LIST: + callbackmilitialist( pRegion->UserData[1] ); + break; + + case CDP_MILITIADETAIL: + callbackmilitiadetail( pRegion->UserData[1] ); + break; + + case CDP_DEFAULT: + default: + break; + } + } + else if ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + } +} + +void +ColumnDataProvider::DestroyMouseRegions( ) +{ + for ( int i = 0; i < COLUMNDATAPROVIDER_MOUSEREGIONS; ++i ) + { + if ( mMouseRegion_Defined[i] ) + { + MSYS_RemoveRegion( &mMouseRegion[i] ); + mMouseRegion_Defined[i] = FALSE; + } + } +} + +// Flugente: this unused page will now be a testbed for the BaseTable development +#define TESTTABLE_FONT_MED FONT12ARIAL +#define TESTTABLE_OFFSET_ROW 20 +#define TESTTABLE_FONT_COLOR_REGULAR 2 + +////////////// TestPanel/////////////////////////////////////// +TestPanel::TestPanel( ) +: BaseTable( ) +{ + SetFrameWorkSpace( 4 ); +} + +void +TestPanel::Display( ) +{ + if ( !IsInit( ) ) + return; + + SetColorLine( Get16BPPColor( FROMRGB( 128, 128, 128 ) ) ); + SetColorLineShadow( Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + SetColorMarked(Get16BPPColor( FROMRGB( 200, 169, 87 ) )); + SetColorHighLight(Get16BPPColor( FROMRGB( 235, 222, 171 )) ); + + //Display the background + ColorFillVideoSurfaceArea( FRAME_BUFFER, GetX( ), GetY( ), GetX( ) + GetWidth( ), GetY( ) + GetHeight( ), GetColorMarked( ) ); + + // framework + // top + Display2Line2ShadowHorizontal( GetX( ), GetY( ), GetX( ) + GetWidth( ), GetColorLine( ), GetColorLineShadow( ) ); + + // bottom + Display2Line2ShadowHorizontal( GetX( ), GetY( ) + GetHeight( ) - GetFrameWorkSpace( ) * 3 / 2, GetX( ) + GetWidth( ), GetColorLineShadow( ), GetColorLine( ) ); + + // left + Display2Line2ShadowVertical( GetX( ), GetY( ) + GetFrameWorkSpace( )/2, GetY( ) + GetHeight( ) - 5, GetColorLine( ), GetColorLineShadow( ) ); + + // right + Display2Line2ShadowVertical( GetX( ) + GetWidth( ) - GetFrameWorkSpace( ) * 3 / 2, GetY( ) + GetFrameWorkSpace( ) / 2, GetY( ) + GetHeight( ) - 5, GetColorLineShadow( ), GetColorLine( ) ); + + BaseTable::Display( ); +} + +void +TestPanel::SetRefresh( ) +{ + +} + +TestPanel gTestPanel3; +////////////////////////// TestPanel ///////////////////////////////////// + +////////////////////////// TabBox ///////////////////////////////////// +TabBox::TabBox( ) +: BaseTable( ) +{ + SetFrameWorkSpace(4); +} + +void +TabBox::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetColorLine( Get16BPPColor( FROMRGB( 128, 128, 128 ) ) ); + SetColorLineShadow( Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + SetColorMarked( Get16BPPColor( FROMRGB( 200, 169, 87 ) ) ); + SetColorHighLight( Get16BPPColor( FROMRGB( 235, 222, 171 ) ) ); + + BaseTable::Init( sX, sY, sX_End, sY_End ); +} + + +void +TabBox::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); + + UINT16 usPosX = GetX( ); + + UINT16 panelcnt = 0; + for ( std::vector::iterator it = mRegisteredTables.begin( ); it != mRegisteredTables.end( ); ++it ) + { + if ( !(*it).mMouseRegion_Defined && IsMouseRegionActive() ) + { + // determine required width of mouse region + UINT32 textwidth = 7 * wcslen( (*it).mTestPanel->GetName( ) ); + + MSYS_DefineRegion( &(*it).mMouseRegion, usPosX, GetY( ), usPosX + textwidth, GetY( ) + TESTTABLE_OFFSET_ROW, MSYS_PRIORITY_HIGH, + CURSOR_WWW, + RegionCallBackWrapper( (void*) this, CALLBACK_REGION_MOVE, &RegionDummyfunc ), + RegionCallBackWrapper( (void*) this, CALLBACK_REGION_CLICK, &RegionDummyfunc ) ); + MSYS_AddRegion( &(*it).mMouseRegion ); + + MSYS_SetRegionUserData( &(*it).mMouseRegion, 0, panelcnt ); + (*it).mMouseRegion_Defined = TRUE; + ++panelcnt; + + usPosX += textwidth; + } + } +} + +void +TabBox::Destroy( ) +{ + if ( !IsInit( ) ) + return; + + for ( std::vector::iterator it = mRegisteredTables.begin( ); it != mRegisteredTables.end( ); ++it ) + { + if ( (*it).mMouseRegion_Defined ) + { + MSYS_RemoveRegion( &(*it).mMouseRegion ); + (*it).mMouseRegion_Defined = FALSE; + } + } + + mRegisteredTables.clear( ); + + BaseTable::Destroy( ); +} + +void +TabBox::Display( ) +{ + if ( !IsInit( ) ) + return; + + DestroyMouseRegions(); + + CHAR16 sText[800]; + swprintf( sText, L"" ); + UINT16 usPosX = GetX( ); + + UINT16 panelcnt = 0; + + UINT16 selectedx = 0; + UINT16 selectedwidth = 0; + + SetFontShadow( NO_SHADOW ); + + for ( std::vector::iterator it = mRegisteredTables.begin( ); it != mRegisteredTables.end( ); ++it ) + { + if ( IsActive() ) + { + // determine required width + UINT32 textwidth = 7 * wcslen( (*it).mTestPanel->GetName( ) ); + + if ( (*it).mTestPanel->IsActive( ) ) + { + ColorFillVideoSurfaceArea( FRAME_BUFFER, + usPosX, GetY( ), + usPosX + textwidth, GetY( ) + TESTTABLE_OFFSET_ROW, + GetColorMarked() ); + + selectedx = usPosX; + selectedwidth = textwidth; + } + else + { + ColorFillVideoSurfaceArea( FRAME_BUFFER, + usPosX, GetY( ), + usPosX + textwidth, GetY( ) + TESTTABLE_OFFSET_ROW, + GetColorHighLight() ); + } + + // framework + // top + Display2Line2ShadowHorizontal( usPosX, GetY( ), usPosX + textwidth, GetColorLine( ), GetColorLineShadow( ) ); + + // left + Display2Line2ShadowVertical( usPosX, GetY( ) + GetFrameWorkSpace( ) / 2, GetY( ) + TESTTABLE_OFFSET_ROW - 1, GetColorLine( ), GetColorLineShadow( ) ); + + // right + Display2Line2ShadowVertical( usPosX + textwidth - 3, GetY( ) + GetFrameWorkSpace( ) / 2, GetY( ) + TESTTABLE_OFFSET_ROW, GetColorLineShadow( ), GetColorLine( ) ); + + swprintf( sText, (*it).mTestPanel->GetName( ) ); + DrawTextToScreen( sText, usPosX + 7, GetY( ) + 7, GetWidth( ), TESTTABLE_FONT_MED, TESTTABLE_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + + // mouse region + if ( !(*it).mMouseRegion_Defined && IsMouseRegionActive( ) ) + { + MSYS_DefineRegion( &(*it).mMouseRegion, usPosX, GetY( ), usPosX + textwidth, GetY( ) + TESTTABLE_OFFSET_ROW, MSYS_PRIORITY_HIGH, + CURSOR_WWW, + RegionCallBackWrapper( (void*) this, CALLBACK_REGION_MOVE, &RegionDummyfunc ), + RegionCallBackWrapper( (void*) this, CALLBACK_REGION_CLICK, &RegionDummyfunc ) ); + MSYS_AddRegion( &(*it).mMouseRegion ); + + MSYS_SetRegionUserData( &(*it).mMouseRegion, 0, panelcnt ); + (*it).mMouseRegion_Defined = TRUE; + ++panelcnt; + } + + usPosX += textwidth; + } + } + + BaseTable::Display( ); + + // to indicate which frame is the active one we 'delete' the border to the content below + ColorFillVideoSurfaceArea( FRAME_BUFFER, + selectedx + GetFrameWorkSpace( ), GetY( ) + TESTTABLE_OFFSET_ROW, + selectedx + selectedwidth - 3, GetY( ) + TESTTABLE_OFFSET_ROW + GetFrameWorkSpace( ), + GetColorMarked( ) ); + + // left + Display2Line2ShadowVertical( selectedx, GetY( ) + TESTTABLE_OFFSET_ROW - 3, GetY( ) + TESTTABLE_OFFSET_ROW + 1, GetColorLine( ), GetColorLineShadow( ) ); + + // right + Display2Line2ShadowVertical( selectedx + selectedwidth - 3, GetY( ) + TESTTABLE_OFFSET_ROW - 3, GetY( ) + TESTTABLE_OFFSET_ROW + 1, GetColorLineShadow( ), GetColorLine( ) ); +} + +void +TabBox::DestroyMouseRegions( ) +{ + for ( std::vector::iterator it = mRegisteredTables.begin( ); it != mRegisteredTables.end( ); ++it ) + { + if ( (*it).mMouseRegion_Defined ) + { + MSYS_RemoveRegion( &(*it).mMouseRegion ); + (*it).mMouseRegion_Defined = FALSE; + } + } + + BaseTable::DestroyMouseRegions( ); +} + +void +TabBox::RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ) +{ + if ( iReason & MSYS_CALLBACK_REASON_INIT ) + { + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + //gfScrollBoxIsScrolling = FALSE; + //gHelpScreen.iLastMouseClickY = -1; + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + for ( std::vector::iterator it = mRegisteredTables.begin(); it != mRegisteredTables.end(); ++it ) + { + if ( pRegion->IDNumber == (*it).mMouseRegion.IDNumber ) + (*it).mTestPanel->SetActive( TRUE ); + else + (*it).mTestPanel->SetActive( FALSE ); + } + + SetRefresh( ); + + //gfScrollBoxIsScrolling = TRUE; + //HelpScreenMouseMoveScrollBox( pRegion->MouseYPos ); + } + else if ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + } +} + + +////////////////////////// TabBox ///////////////////////////////////// + +////////////////////////// TestTable ///////////////////////////////////// +#define SCROLLARROW_WIDTH 20 +#define SCROLLARROW_HEIGTH 20 + +TestTable::TestTable( ) +: BaseTable( ), +mScrollBarDefined( FALSE ), +mFirstEntryShown(0), +mLastEntryShown( 0 ) +{ +} + +void +TestTable::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetX( sX ); + SetY( sY ); + + SetHeight( sY_End - sY ); + SetWidth( sX_End - sX ); + + CalcRows( ); + + SetInit( TRUE ); +} + +extern UINT32 guiHelpScreenBackGround; + +void +TestTable::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + VOBJECT_DESC VObjectDesc; + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "INTERFACE\\HelpScreen.sti", VObjectDesc.ImageFile ); + if ( !AddVideoObject( &VObjectDesc, &guiHelpScreenBackGround ) ) + return; + + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); +} + +void +TestTable::Destroy( ) +{ + if ( !IsInit( ) ) + return; + + if ( mScrollBarDefined ) + { + for ( UINT16 i = 0; i < 2; ++i ) + { + RemoveButton( mScrollArrow[i] ); + UnloadButtonImage( mScrollArrowImage[i] ); + } + + MSYS_RemoveRegion( &mScrollRegion ); + + mScrollBarDefined = FALSE; + } + + std::vector::iterator itend = mColumnDataProviderVector.end( ); + for ( std::vector::iterator it = mColumnDataProviderVector.begin( ); it != itend; ++it ) + { + (*it).DestroyMouseRegions(); + } + + ClearColumnDataProvider(); + + DeleteVideoObjectFromIndex( guiHelpScreenBackGround ); + + SetInit( FALSE ); + + SetRefresh( ); +} + +void +TestTable::Display( ) +{ + if ( !IsInit( ) ) + return; + + CHAR16 sText[800]; + swprintf( sText, L"" ); + UINT16 usPosX, usPosY; + + usPosX = GetX( ); + usPosY = GetY( ); + + DestroyMouseRegions(); + + if ( IsActive() ) + { + for ( std::vector::iterator it = mColumnDataProviderVector.begin( ); it != mColumnDataProviderVector.end( ); ++it ) + { + swprintf( sText, (*it).GetName( ) ); + DrawTextToScreen( sText, usPosX, usPosY, GetWidth( ), TESTTABLE_FONT_MED, TESTTABLE_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += (*it).GetRequiredLength( ); + } + + usPosY += TESTTABLE_OFFSET_ROW - 4; + + UINT32 heightperrow = GetRequiredHeigthPerRow( ); + + for ( UINT32 i = mFirstEntryShown; i < mLastEntryShown; ++i ) + { + usPosX = GetX( ); + + DisplaySmallColouredLineWithShadow( usPosX, usPosY - 2, usPosX + GetWidth( ) - SCROLLARROW_WIDTH, usPosY - 2, FROMRGB( 0, 255, 0 ) ); + + std::vector::iterator itend = mColumnDataProviderVector.end( ); + for ( std::vector::iterator it = mColumnDataProviderVector.begin( ); it != itend; ++it ) + { + if ( it->GetProviderType( ) == ColumnDataProvider::CDP_STRING ) + { + swprintf( sText, (*it).GetString( i ) ); + + DrawTextToScreen( sText, usPosX, usPosY + 7, GetWidth( ), TESTTABLE_FONT_MED, TESTTABLE_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + } + else if ( it->GetProviderType( ) == ColumnDataProvider::CDP_IMAGE ) + { + UINT32 imagelib = 0; + UINT16 imageid = 0; + + it->GetImageData( i, imagelib, imageid ); + + BltVideoObjectFromIndex( FRAME_BUFFER, imagelib, imageid, usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL ); + } + else if ( it->GetProviderType( ) == ColumnDataProvider::CDP_STATUSBAR ) + { + UINT16 col1 = 0; + UINT16 col2 = 0; + UINT16 col3 = 0; + UINT16 col4 = 0; + UINT16 val1 = 0; + UINT16 val2 = 0; + UINT16 val3 = 0; + UINT16 val4 = 0; + + it->GetStatusBarData( i, col1, val1, col2, val2, col3, val3, col4, val4 ); + + UINT16 height1 = (heightperrow * val1) / 100; + UINT16 height2 = (heightperrow * val2) / 100; + UINT16 height3 = (heightperrow * val3) / 100; + UINT16 height4 = (heightperrow * val4) / 100; + + UINT16 endY = usPosY + heightperrow - 1; + + // first a grey background (that way we get a grey 'border' for our bar, then the content + UINT16 bg = Get16BPPColor( FROMRGB( 158, 158, 158 ) ); + DisplaySmallLine( usPosX + 0, usPosY, usPosX + 0, endY, bg ); + DisplaySmallLine( usPosX + 1, usPosY, usPosX + 1, endY, bg ); + DisplaySmallLine( usPosX + 2, usPosY, usPosX + 2, endY, bg ); + DisplaySmallLine( usPosX + 3, usPosY, usPosX + 3, endY, bg ); + + DisplaySmallLine( usPosX + 1, endY - height1, usPosX + 1, endY, col1 ); + DisplaySmallLine( usPosX + 2, endY - height1, usPosX + 2, endY, col1 ); + DisplaySmallLine( usPosX + 1, endY - height2, usPosX + 1, endY, col2 ); + DisplaySmallLine( usPosX + 2, endY - height2, usPosX + 2, endY, col2 ); + DisplaySmallLine( usPosX + 1, endY - height3, usPosX + 1, endY, col3 ); + DisplaySmallLine( usPosX + 2, endY - height3, usPosX + 2, endY, col3 ); + DisplaySmallLine( usPosX + 1, endY - height4, usPosX + 1, endY, col4 ); + DisplaySmallLine( usPosX + 2, endY - height4, usPosX + 2, endY, col4 ); + } + + if ( i - mFirstEntryShown < COLUMNDATAPROVIDER_MOUSEREGIONS && IsMouseRegionActive( ) ) + { + MSYS_DefineRegion( &it->mMouseRegion[i - mFirstEntryShown], + usPosX, usPosY, usPosX + it->GetRequiredLength( ), usPosY + heightperrow, + MSYS_PRIORITY_HIGHEST, CURSOR_WWW, + MSYS_NO_CALLBACK, (*it).RegionClickCallBack ); + MSYS_AddRegion( &it->mMouseRegion[i - mFirstEntryShown] ); + + // first entry of userdata is the callback type - we need to know what kind of callback function should be called + MSYS_SetRegionUserData( &it->mMouseRegion[i - mFirstEntryShown], 0, it->GetCallBackType() ); + + // second entry: the id to use as input in the callback + MSYS_SetRegionUserData( &it->mMouseRegion[i - mFirstEntryShown], 1, i ); + + it->mMouseRegion_Defined[i - mFirstEntryShown] = TRUE; + } + + usPosX += (*it).GetRequiredLength( ); + } + + usPosY += heightperrow; + } + + UINT32 shownentries = GetNumberOfDataRowsShown( ); + UINT32 totalentries = GetNumberOfDataRows( ); + + // do we need scrollbars in the first place? + if ( shownentries < totalentries ) + { + // length of scrollbar and length of scrollbar element + UINT32 scrollbarheigth = GetHeight( ) - 2 * SCROLLARROW_HEIGTH; + UINT32 scrollareaheigth = (scrollbarheigth * shownentries) / totalentries; + UINT32 areabegin = (scrollbarheigth * mFirstEntryShown) / totalentries; + + ColorFillVideoSurfaceArea( FRAME_BUFFER, + GetX( ) + GetWidth( ) - SCROLLARROW_WIDTH - 2, + GetY( ) + SCROLLARROW_HEIGTH + areabegin, + GetX( ) + GetWidth( ) - 2, + GetY( ) + SCROLLARROW_HEIGTH + areabegin + scrollareaheigth, + Get16BPPColor( FROMRGB( 20, 250, 50 ) ) ); + + CreateScrollAreaButtons( ); + } + } + + BaseTable::Display( ); +} + +void +TestTable::DestroyMouseRegions() +{ + std::vector::iterator itend = mColumnDataProviderVector.end( ); + for ( std::vector::iterator it = mColumnDataProviderVector.begin( ); it != itend; ++it ) + { + (*it).DestroyMouseRegions( ); + } + + if ( mScrollBarDefined ) + { + for ( UINT16 i = 0; i < 2; ++i ) + { + RemoveButton( mScrollArrow[i] ); + UnloadButtonImage( mScrollArrowImage[i] ); + } + + MSYS_RemoveRegion( &mScrollRegion ); + + mScrollBarDefined = FALSE; + } + + BaseTable::DestroyMouseRegions( ); +} + +void +TestTable::RegionMoveCallBack( MOUSE_REGION * pRegion, INT32 iReason ) +{ + if ( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) + { + // InvalidateRegion(pRegion->RegionTopLeftX, pRegion->RegionTopLeftY, pRegion->RegionBottomRightX, pRegion->RegionBottomRightY); + } + else if ( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) + { + } + else if ( iReason & MSYS_CALLBACK_REASON_MOVE ) + { + if ( gfLeftButtonState ) + { + // if mouse is outside of the bar, simply move bar up or down + if ( pRegion->MouseYPos < mScrollRegion.RegionTopLeftY ) + { + mLastEntryShown = max( 0, mLastEntryShown - 1 ); + } + else if ( pRegion->MouseYPos > mScrollRegion.RegionBottomRightY ) + { + ++mLastEntryShown; + } + // inside the scroll bar, determine where the middle of the slider is to determine where to go + else + { + // length of scrollbar + UINT32 scrollbarheigth = GetHeight( ) - 2 * SCROLLARROW_HEIGTH; + + if ( scrollbarheigth > 0 ) + { + FLOAT rate = (FLOAT)(pRegion->MouseYPos - GetY( ) + SCROLLARROW_HEIGTH) / (FLOAT)(scrollbarheigth); + + mLastEntryShown = rate * GetNumberOfDataRows(); + } + } + + // simply altering the position of mLastEntryShown and the recalculating the rows does the trick + CalcRows(); + + SetRefresh( ); + } + } +} + +void +TestTable::RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ) +{ + iReason = iReason; + + if ( iReason & MSYS_CALLBACK_REASON_INIT ) + { + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + //gfScrollBoxIsScrolling = FALSE; + //gHelpScreen.iLastMouseClickY = -1; + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + //gfScrollBoxIsScrolling = TRUE; + //HelpScreenMouseMoveScrollBox( pRegion->MouseYPos ); + } + else if ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + } +} + +void +TestTable::ButtonClickCallBack( GUI_BUTTON *btn, INT32 reason ) +{ + if ( reason & MSYS_CALLBACK_REASON_INIT ) + { + return; + } + if ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + // up-arrow + if ( btn->IDNum == this->mScrollArrow[0] ) + { + // do things! + if ( mFirstEntryShown ) + --mFirstEntryShown; + + mLastEntryShown = mFirstEntryShown + GetNumberOfDataRowsShown( ); + } + // down-arrow + else if ( btn->IDNum == this->mScrollArrow[1] ) + { + // do slightly different things! + mLastEntryShown = min( mLastEntryShown + 1, GetNumberOfDataRows( ) ); + + mFirstEntryShown = mLastEntryShown - GetNumberOfDataRowsShown( ); + } + // this should not happen + else + { + return; + } + + SetRefresh( ); + } + else if ( reason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + // nothing yet + } +} + +void +TestTable::CalcRows() +{ + // don't show more entries than exist + UINT32 numrows = GetNumberOfDataRows( ); + UINT32 rowsshown = GetNumberOfDataRowsShown( ); + + mLastEntryShown = max( mLastEntryShown, rowsshown ); + mLastEntryShown = min( mLastEntryShown, numrows ); + + if ( mLastEntryShown > rowsshown ) + mFirstEntryShown = mLastEntryShown - rowsshown; + else + mFirstEntryShown = 0; + + mLastEntryShown = mFirstEntryShown + rowsshown; +} + +void +TestTable::CreateScrollAreaButtons( ) +{ + if ( !mScrollBarDefined ) + { + if ( IsMouseRegionActive() ) + { + MSYS_DefineRegion( &mScrollRegion, GetX( ) + GetWidth( ) - SCROLLARROW_WIDTH - 2, GetY( ), GetX( ) + GetWidth( ) - 2, GetY( ) + GetHeight( ) - 2, MSYS_PRIORITY_HIGHEST, + CURSOR_WWW, + RegionCallBackWrapper( (void*) this, CALLBACK_REGION_MOVE, &RegionDummyfunc ), + RegionCallBackWrapper( (void*) this, CALLBACK_REGION_CLICK, &RegionDummyfunc ) ); + MSYS_AddRegion( &mScrollRegion ); + MSYS_SetRegionUserData( &mScrollRegion, 0, 0 ); + + mScrollArrowImage[0] = LoadButtonImage( "INTERFACE\\HelpScreen.sti", 14, 10, 11, 12, 13 ); + mScrollArrowImage[1] = UseLoadedButtonImage( mScrollArrowImage[0], 19, 15, 16, 17, 18 ); + + //Create the scroll arrows + mScrollArrow[0] = QuickCreateButton( mScrollArrowImage[0], GetX( ) + GetWidth( ) - SCROLLARROW_WIDTH - 2, GetY( ), + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + ButtonCallBackWrapper( (void*) this, CALLBACK_BUTTON_MOVE, &ButtonDummyfunc ), + ButtonCallBackWrapper( (void*) this, CALLBACK_BUTTON_CLICK, &ButtonDummyfunc ) ); + MSYS_SetBtnUserData( mScrollArrow[0], 0, 0 ); + + //Create the scroll arrows + mScrollArrow[1] = QuickCreateButton( mScrollArrowImage[1], GetX( ) + GetWidth( ) - SCROLLARROW_WIDTH - 2, GetY( ) + GetHeight( ) - SCROLLARROW_HEIGTH - 2, + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + ButtonCallBackWrapper( (void*) this, CALLBACK_BUTTON_MOVE, &ButtonDummyfunc ), + ButtonCallBackWrapper( (void*) this, CALLBACK_BUTTON_CLICK, &ButtonDummyfunc ) ); + MSYS_SetBtnUserData( mScrollArrow[1], 0, 1 ); + + mScrollBarDefined = TRUE; + } + else + { + // if buttons are disabled, we'll display the images of the buttons instead + HVOBJECT hArrowHandle; + + //get and display the up and down arrows + GetVideoObject( &hArrowHandle, guiHelpScreenBackGround ); + + if ( hArrowHandle ) + { + // top arrow + BltVideoObject( FRAME_BUFFER, hArrowHandle, 10, + GetX( ) + GetWidth( ) - SCROLLARROW_WIDTH - 2, + GetY( ), + VO_BLT_SRCTRANSPARENCY, NULL ); + + // down arrow + BltVideoObject( FRAME_BUFFER, hArrowHandle, 17, + GetX( ) + GetWidth( ) - SCROLLARROW_WIDTH - 2, + GetY( ) + GetHeight( ) - SCROLLARROW_HEIGTH - 2, + VO_BLT_SRCTRANSPARENCY, NULL ); + } + } + } +} + +UINT32 +TestTable::GetNumberOfDataRows( ) +{ + BOOLEAN fFound = FALSE; + UINT32 minrowsinproviders = 1000; + std::vector::iterator itend = mColumnDataProviderVector.end( ); + for ( std::vector::iterator it = mColumnDataProviderVector.begin( ); it != itend; ++it ) + { + minrowsinproviders = min( minrowsinproviders, (*it).GetNumberOfEntries( ) ); + + fFound = TRUE; + } + + if ( fFound && minrowsinproviders ) + return minrowsinproviders; + + return 0; +} + +UINT32 +TestTable::GetNumberOfDataRowsShown( ) +{ + // determine total height that can be used for display - we have to substract space for the header and the column headers + UINT32 heightperrow = GetRequiredHeigthPerRow(); + + if ( !heightperrow ) + return 0; + + UINT32 totalheighttodistribute = GetHeight( ); + + if ( totalheighttodistribute < heightperrow + TESTTABLE_OFFSET_ROW ) + return 0; + + totalheighttodistribute -= TESTTABLE_OFFSET_ROW; + + UINT32 rows = GetNumberOfDataRows( ); + + if ( !rows ) + return 0; + + // we need TESTTABLE_OFFSET_ROW space for each row. We cannot display more than we have + return min( totalheighttodistribute / heightperrow, rows ); +} + +UINT32 +TestTable::GetRequiredHeigthPerRow( ) +{ + UINT32 heigth = 0; + std::vector::iterator itend = mColumnDataProviderVector.end( ); + for ( std::vector::iterator it = mColumnDataProviderVector.begin( ); it != itend; ++it ) + { + heigth = max( heigth, (*it).GetRequiredHeigth( ) ); + } + + return heigth; +} +////////////////////////// TestTable ///////////////////////////////////// diff --git a/Laptop/BaseTable.h b/Laptop/BaseTable.h new file mode 100644 index 00000000..ad7c91cb --- /dev/null +++ b/Laptop/BaseTable.h @@ -0,0 +1,510 @@ +#ifndef __BASETABLE_H +#define __BASETABLE_H + +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#include "DropDown.h" + +/* +* A base class for tables +*/ +class BaseTable : public WidgetBase +{ +public: + BaseTable( ); + + void SetInit( BOOLEAN fVal ) { fInit = fVal ; } + BOOLEAN IsInit() { return fInit ; } + + /* + * set maximum width and height + */ + void SetWidth( UINT16 aVal ) { musWidth = aVal; } + virtual UINT16 GetWidth( ) { return musWidth; } + void SetHeight( UINT16 aVal ) { musHeight = aVal; } + virtual UINT16 GetHeight( ) { return musHeight; } + void SetFrameWorkSpace( UINT16 aVal ) { musFrameWorkSpace = aVal; } + virtual UINT16 GetFrameWorkSpace( ) { return musFrameWorkSpace; } + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + /* + * Sometimes a refresh inside the class isn't enough - best example is closing the dropdown area, after which the previous background cannot be restored by this class. + * In that case, the background and this class has to be redrawn. + * This function is called internally whenever such a refresh is necessary. + * It has to be defined in derived classes and should notify your dialogues to redraw at appropriate times + * + * This function has to be implemented! + */ + virtual void SetRefresh( ) = 0; + + virtual void DestroyMouseRegions(); + + void SetActive( BOOLEAN aVal ) { mActive = aVal; } + BOOLEAN IsActive( ) { return mActive; } + + virtual void SetMouseRegionActive( BOOLEAN aVal ) { mMouseRegionActive = aVal; } + BOOLEAN IsMouseRegionActive( ) { return mMouseRegionActive; } + + virtual void RegisterMemberThingy( BaseTable * arP ) + { + if ( mRegisteredMembers.empty() ) + { + mRegisteredMembers.push_back( arP ); + + arP->SetActive( TRUE ); + } + else + { + mRegisteredMembers.push_back( arP ); + } + } + + void SetName( STR16 aName ) { mName = aName; } + STR16 GetName() { return mName; } + +private: + // declare but don't define + BaseTable( BaseTable const& ); + void operator=(BaseTable const&); + +private: + STR16 mName; + + BOOLEAN fInit; + + UINT16 musHeight; + UINT16 musWidth; + + // as blank square dialogues are ugly, we can a framework on the edges - in this case we need to reserve some space for this + UINT16 musFrameWorkSpace; + + // determines whether or not this widget will currently be shown + BOOLEAN mActive; + + // if FALSE, do not display mouse regions + BOOLEAN mMouseRegionActive; + + std::vector mRegisteredMembers; +}; + + +// a simple panel into which we can embed other widgets +class TestPanel : public BaseTable +{ +public: + TestPanel( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + virtual void SetRefresh( ); +}; + +extern TestPanel gTestPanel3; + +// callback types +enum { + CALLBACK_BUTTON_MOVE = 0, + CALLBACK_BUTTON_CLICK, + CALLBACK_REGION_MOVE, + CALLBACK_REGION_CLICK, +}; + +class WidgetCallBackInstance +{ +public: + WidgetCallBackInstance( ) {} + virtual ~WidgetCallBackInstance( ) {} + + virtual void RegionMoveCallBack( MOUSE_REGION * pRegion, INT32 iReason ) {} + virtual void RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ) {} + + virtual void ButtonMoveCallBack( GUI_BUTTON *btn, INT32 reason ) {} + virtual void ButtonClickCallBack( GUI_BUTTON *btn, INT32 reason ) {} + + /* + * This function is implemented again in DropDownTemplate + */ + virtual MOUSE_CALLBACK RegionCallBackWrapper( void* pt2Object, UINT8 arg, void( *pt2Function )(MOUSE_REGION * pRegion, INT32 iReason) ) { return RegionDummyfunc; } + virtual GUI_CALLBACK ButtonCallBackWrapper( void* pt2Object, UINT8 arg, void( *pt2Function )(GUI_BUTTON * btn, INT32 iReason) ) { return ButtonDummyfunc; } + + /* + * This dummy is needed internally and does nothing when called + */ + static void RegionDummyfunc( MOUSE_REGION * pRegion, INT32 iReason ) {} + static void ButtonDummyfunc( GUI_BUTTON * btn, INT32 iReason ) {} +}; + + +typedef STR16( *cdp_string_func )(UINT32 aNum); +STR16 cdp_string_func_dummy( UINT32 aNum ); + +typedef void( *cdp_image_func )(UINT32 aNum, UINT32& arImageLib, UINT16& arImage ); +void cdp_image_func_dummy( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ); + +typedef void( *cdp_statusbar_func )(UINT32 aId, UINT16& arCol1, UINT16& arVal1, UINT16& arCol2, UINT16& arVal2, UINT16& arCol3, UINT16& arVal3, UINT16& arCol4, UINT16& arVal4); +void cdp_statusbar_func_dummy( UINT32 aId, UINT16& arCol1, UINT16& arVal1, UINT16& arCol2, UINT16& arVal2, UINT16& arCol3, UINT16& arVal3, UINT16& arCol4, UINT16& arVal4 ); + +#define COLUMNDATAPROVIDER_MOUSEREGIONS 50 + +class ColumnDataProvider +{ +public: + ColumnDataProvider( STR16 aName ) : mName( aName ), mNumberOfEntries( 0 ), mRequiredHeigth(20), + mType( CDP_STRING ), mCallbackType( CDP_DEFAULT ), + mFuncString( cdp_string_func_dummy ), mFuncImage( cdp_image_func_dummy ) + { + } + + // general provider functions + void SetNumberOfEntries( UINT32 aVal ) { mNumberOfEntries = aVal; CalcRequiredLength( ); } + UINT32 GetNumberOfEntries( ) { return mNumberOfEntries; } + + void CalcRequiredLength( ); + UINT32 GetRequiredLength( ) { return mRequiredLength; } + void SetRequiredLength( UINT32 aVal ) { mRequiredLength = aVal; } // this overrides the calculated value + UINT32 GetRequiredHeigth( ) { return mRequiredHeigth; } + void SetRequiredHeigth( UINT32 aVal ) { mRequiredHeigth = aVal; } + + STR16 GetName( ) { return mName; } + + typedef enum + { + CDP_DEFAULT = 0, + CDP_MILITIA_LIST, + CDP_MILITIADETAIL, + } CDP_CALLBACKTYPE; + + typedef enum + { + CDP_STRING, + CDP_IMAGE, + CDP_STATUSBAR, + } CDP_TYPE; + + CDP_TYPE GetProviderType( ) { return mType ; } + void SetProviderType( CDP_TYPE aType ) { mType = aType; } + + // functions for handling string contents + void SetMethodString( cdp_string_func afunc ) { mFuncString = afunc; SetProviderType( CDP_STRING ); CalcRequiredLength( ); } + STR16 GetString( UINT32 aNum ) { return mFuncString( aNum ); } + + // functions for handling images + void SetMethodImage( cdp_image_func afunc ) { mFuncImage = afunc; SetProviderType( CDP_IMAGE ); CalcRequiredLength( ); } + void GetImageData( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ) { return mFuncImage( aNum, arImageLib, arImage ); } + + // functions for handling healthbars + void SetMethodStatusBar( cdp_statusbar_func afunc ) { mFuncStatusBar = afunc; SetProviderType( CDP_STATUSBAR ); CalcRequiredLength( ); } + void GetStatusBarData( UINT32 aId, UINT16& arCol1, UINT16& arVal1, UINT16& arCol2, UINT16& arVal2, UINT16& arCol3, UINT16& arVal3, UINT16& arCol4, UINT16& arVal4 ) + { + return mFuncStatusBar( aId, arCol1, arVal1, arCol2, arVal2, arCol3, arVal3, arCol4, arVal4 ); + } + + // callbacks for clicking on a column + void SetCallBackType( CDP_CALLBACKTYPE aVal ) { mCallbackType = aVal; } + CDP_CALLBACKTYPE GetCallBackType( ) { return mCallbackType; } + static void RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ); + + void DestroyMouseRegions(); + +public: + MOUSE_REGION mMouseRegion[COLUMNDATAPROVIDER_MOUSEREGIONS]; + BOOLEAN mMouseRegion_Defined[COLUMNDATAPROVIDER_MOUSEREGIONS]; + +private: + STR16 mName; + + UINT32 mNumberOfEntries; + + // the maximum length required for a column according to its contents + UINT32 mRequiredLength; + UINT32 mRequiredHeigth; + + CDP_TYPE mType; + CDP_CALLBACKTYPE mCallbackType; + + cdp_string_func mFuncString; + cdp_image_func mFuncImage; + cdp_statusbar_func mFuncStatusBar; +}; + + +// a simple panel into which we can embed other widgets +class TabBox : public BaseTable, public WidgetCallBackInstance +{ +public: + TabBox( ); + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + virtual void DestroyMouseRegions( ); + + struct TestPanelButtonStruct + { + BaseTable* mTestPanel; + MOUSE_REGION mMouseRegion; + BOOLEAN mMouseRegion_Defined; + }; + + virtual void RegisterMemberThingy( BaseTable * arP ) + { + if ( !mRegisteredTables.empty( ) ) + arP->SetActive( FALSE ); + + TestPanelButtonStruct tmp; + tmp.mTestPanel = arP; + tmp.mMouseRegion_Defined = FALSE; + mRegisteredTables.push_back( tmp ); + BaseTable::RegisterMemberThingy( arP ); + } + + virtual void RegionMoveCallBack( MOUSE_REGION * pRegion, INT32 iReason ) {} + virtual void RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ); + +private: + std::vector mRegisteredTables; +}; + + +class TestTable : public BaseTable, public WidgetCallBackInstance +{ +public: + TestTable( ); + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + virtual void DestroyMouseRegions( ) ; + + virtual void RegionMoveCallBack( MOUSE_REGION * pRegion, INT32 iReason ); + virtual void RegionClickCallBack( MOUSE_REGION * pRegion, INT32 iReason ); + + virtual void ButtonClickCallBack( GUI_BUTTON *btn, INT32 reason ); + + void AddColumnDataProvider( ColumnDataProvider aCP ) { mColumnDataProviderVector.push_back( aCP ); } + void ClearColumnDataProvider( ) { mColumnDataProviderVector.clear(); } + + void CalcRows(); + +private: + void CreateScrollAreaButtons( ); + + UINT32 GetNumberOfDataRows( ); + UINT32 GetNumberOfDataRowsShown( ); + + UINT32 GetRequiredHeigthPerRow(); + +private: + // parts of the scrollbar + BOOLEAN mScrollBarDefined; + + INT32 mScrollArrow[2]; + UINT32 mScrollArrowImage[2]; + + MOUSE_REGION mScrollRegion; + + UINT32 mFirstEntryShown; + UINT32 mLastEntryShown; + + std::vector mColumnDataProviderVector; +}; + + +template +class TestTableTemplate : public TestTable +{ +public: + static TestTableTemplate& getInstance( ) + { + static TestTableTemplate instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } + + // this function has to be implemented! + virtual void SetRefresh( ); + + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + static void RegionMove( MOUSE_REGION * pRegion, INT32 iReason ) { return static_cast(mpSelf)->RegionMoveCallBack( pRegion, iReason ); } + static void RegionClick( MOUSE_REGION * pRegion, INT32 iReason ) { return static_cast(mpSelf)->RegionClickCallBack( pRegion, iReason ); } + static void ButtonMove( GUI_BUTTON *btn, INT32 iReason ) { return static_cast(mpSelf)->ButtonMoveCallBack( btn, iReason ); } + static void ButtonClick( GUI_BUTTON *btn, INT32 iReason ) { return static_cast(mpSelf)->ButtonClickCallBack( btn, iReason ); } + + MOUSE_CALLBACK + RegionCallBackWrapper( void* pt2Object, UINT8 arg, void( *pt2Function )(MOUSE_REGION * pRegion, INT32 iReason) ) + { + mpSelf = pt2Object; + + switch ( arg ) + { + case CALLBACK_REGION_MOVE: return &RegionMove; break; + case CALLBACK_REGION_CLICK: return &RegionClick; break; + } + + return *pt2Function; + } + + GUI_CALLBACK + ButtonCallBackWrapper( void* pt2Object, UINT8 arg, void( *pt2Function )(GUI_BUTTON *btn, INT32 iReason) ) + { + mpSelf = pt2Object; + + switch ( arg ) + { + case CALLBACK_BUTTON_MOVE: return &ButtonMove; break; + case CALLBACK_BUTTON_CLICK: return &ButtonClick; break; + } + + return *pt2Function; + } + +private: + static void* mpSelf; + +private: + TestTableTemplate( ) {}; // private constructor, so we cannot create more instances + + // declare but don't define + TestTableTemplate( TestTableTemplate const& ); + void operator=(TestTableTemplate const&); +}; + +template +void TestTableTemplate::SetRefresh(); + +template +void TestTableTemplate::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + +template +void* TestTableTemplate::mpSelf = NULL; + + +template +class TabBoxTemplate : public TabBox +{ +public: + static TabBoxTemplate& getInstance( ) + { + static TabBoxTemplate instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } + + static TabBox* GetPSelf( ) { return static_cast(mpSelf); } + + // this function has to be implemented! + virtual void SetRefresh( ); + + static void RegionMove( MOUSE_REGION * pRegion, INT32 iReason ) { return GetPSelf()->RegionMoveCallBack( pRegion, iReason ); } + static void RegionClick( MOUSE_REGION * pRegion, INT32 iReason ) { return GetPSelf()->RegionClickCallBack( pRegion, iReason ); } + static void ButtonMove( GUI_BUTTON *btn, INT32 iReason ) { return GetPSelf()->ButtonMoveCallBack( btn, iReason ); } + static void ButtonClick( GUI_BUTTON *btn, INT32 iReason ) { return GetPSelf()->ButtonClickCallBack( btn, iReason ); } + + void SetPSelf( void* pt2Object ) { mpSelf = pt2Object; } + + MOUSE_CALLBACK + RegionCallBackWrapper( void* pt2Object, UINT8 arg, void( *pt2Function )(MOUSE_REGION * pRegion, INT32 iReason) ) + { + SetPSelf ( pt2Object ); + + switch ( arg ) + { + case CALLBACK_REGION_MOVE: return &RegionMove; break; + case CALLBACK_REGION_CLICK: return &RegionClick; break; + } + + return *pt2Function; + } + + GUI_CALLBACK + ButtonCallBackWrapper( void* pt2Object, UINT8 arg, void( *pt2Function )(GUI_BUTTON *btn, INT32 iReason) ) + { + SetPSelf( pt2Object ); + + switch ( arg ) + { + case CALLBACK_BUTTON_MOVE: return &ButtonMove; break; + case CALLBACK_BUTTON_CLICK: return &ButtonClick; break; + } + + return *pt2Function; + } + +private: + static void* mpSelf; + +private: + TabBoxTemplate( ) {}; // private constructor, so we cannot create more instances + + // declare but don't define + TabBoxTemplate( TabBoxTemplate const& ); + void operator=(TabBoxTemplate const&); +}; + +template +void TabBoxTemplate::SetRefresh( ); + +template +void* TabBoxTemplate::mpSelf = NULL; + +#endif diff --git a/Laptop/CampaignHistory_Summary.cpp b/Laptop/CampaignHistory_Summary.cpp index 40e234df..f77c4c3a 100644 --- a/Laptop/CampaignHistory_Summary.cpp +++ b/Laptop/CampaignHistory_Summary.cpp @@ -437,430 +437,403 @@ void RenderCampaignHistory_MostImportant() usPosX = LAPTOP_SCREEN_UL_X; usPosY = LAPTOP_SCREEN_WEB_UL_Y + 70; - // return if there are no incidents yet - if ( !gCampaignStats.usNumIncidents ) - return; + // fix if bad number + if ( gusMostImportantPage >= gCampaignStats.usNumIncidents ) + gusMostImportantPage = 0; - Incident_Stats incident = gCampaignStats.mIncidentVector[ gusMostImportantPage ]; - - CHAR16 wSectorName_Target[ 100 ]; - GetSectorIDString( SECTORX(incident.usSector), SECTORY(incident.usSector), incident.usLevel, wSectorName_Target, TRUE ); - - INT8 bTownId = GetTownIdForSector( SECTORX(incident.usSector), SECTORY(incident.usSector) ); - - //Calculate the day, hour, and minutes. - UINT32 day = ( incident.usTime / NUM_SEC_IN_DAY ); - UINT32 hour = ( incident.usTime - ( day * NUM_SEC_IN_DAY ) ) / NUM_SEC_IN_HOUR; - UINT32 minute = ( incident.usTime - ( ( day * NUM_SEC_IN_DAY ) + ( hour * NUM_SEC_IN_HOUR ) ) ) / NUM_SEC_IN_MIN; - - // a funny operation name might be more entertaining than a simple number - /*UINT8 importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_MOMENTOUS; - if ( incident.usInterestRating < 500 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_IRRELEVANT; - else if ( incident.usInterestRating < 1000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_INSIGNIFICANT; - else if ( incident.usInterestRating < 2000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_NOTABLE; - else if ( incident.usInterestRating < 3000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_NOTEWORTHY; - else if ( incident.usInterestRating < 4000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_SIGNIFICANT; - else if ( incident.usInterestRating < 5000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_INTERESTING; - else if ( incident.usInterestRating < 6000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_IMPORTANT; - else if ( incident.usInterestRating < 7000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_VERYIMPORTANT; - else if ( incident.usInterestRating < 8000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_GRAVE; - else if ( incident.usInterestRating < 10000 ) - importancenr = TEXT_CAMPAIGNHISTORY_IMPORTANCE_MAJOR; - - swprintf(sText, L"%s %s #%d - %s, %s %d, %02d:%02d", szCampaignHistoryImportanceString[importancenr], szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_INCIDENT], incident.usID, wSectorName_Target, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_DAY], day, hour, minute); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, CAMPHIS_FONT_BIG, CAMPHIS_FONT_COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED );*/ - - UINT32 prefix = (incident.usTime + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usID) % CAMPAIGNSTATS_OPERATION_NUM_PREFIX; - UINT32 suffix = (incident.usTime + incident.usShots[CAMPAIGNHISTORY_SD_MERC] + 7 * incident.usID) % CAMPAIGNSTATS_OPERATION_NUM_SUFFIX; - - CHAR16 operationame[200]; - swprintf( operationame, szCampaignStatsOperationPrefix[prefix], szCampaignStatsOperationSuffix[suffix] ); - - swprintf( sText, L"Operation %s - %s, %s %d, %02d:%02d", operationame, wSectorName_Target, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_DAY], day, hour, minute ); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, CAMPHIS_FONT_BIG, CAMPHIS_FONT_COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); - - usPosY = LAPTOP_SCREEN_WEB_UL_Y + 90; - - if ( gusMostImportantMode == 0 ) + if ( gusMostImportantPage < gCampaignStats.usNumIncidents ) { - usPosX += 2 * CAMPAIGN_HISTORY_TABLE_STEP_X; + Incident_Stats incident = gCampaignStats.mIncidentVector[ gusMostImportantPage ]; - swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_KILLED]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + CHAR16 wSectorName_Target[ 100 ]; + GetSectorIDString( SECTORX(incident.usSector), SECTORY(incident.usSector), incident.usLevel, wSectorName_Target, TRUE ); - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + INT8 bTownId = GetTownIdForSector( SECTORX(incident.usSector), SECTORY(incident.usSector) ); - swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_WOUNDED]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + //Calculate the day, hour, and minutes. + UINT32 day = ( incident.usTime / NUM_SEC_IN_DAY ); + UINT32 hour = ( incident.usTime - ( day * NUM_SEC_IN_DAY ) ) / NUM_SEC_IN_HOUR; + UINT32 minute = ( incident.usTime - ( ( day * NUM_SEC_IN_DAY ) + ( hour * NUM_SEC_IN_HOUR ) ) ) / NUM_SEC_IN_MIN; + + STR16 operationstr = GetIncidentName( incident.usID ); - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + swprintf( sText, L"Operation %s - %s, %s %d, %02d:%02d", operationstr, wSectorName_Target, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_DAY], day, hour, minute ); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, CAMPHIS_FONT_BIG, CAMPHIS_FONT_COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + usPosY = LAPTOP_SCREEN_WEB_UL_Y + 90; - swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_PRISONERS]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - - swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_SHOTSFIRED]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - - swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_PARTICIPANTS]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - - swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_PROMOTIONS]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX = LAPTOP_SCREEN_UL_X; - - usPosY += 20; - - for(int i = CAMPAIGNHISTORY_SD_MERC; i < CAMPAIGNHISTORY_SD_MAX; ++i) + if ( gusMostImportantMode == 0 ) { - // if faction was not involved at all, ignore - if ( !incident.usKills[i] && !incident.usWounds[i] && !incident.usPrisoners[i] && !incident.usShots[i] && !incident.usParticipants[i] && !incident.usPromotions[i] ) - continue; - - usPosX = LAPTOP_SCREEN_UL_X; - swprintf(sText, L"%s", szSoldierClassName[i]); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - usPosX += 2 * CAMPAIGN_HISTORY_TABLE_STEP_X; - swprintf(sText, L"%d", incident.usKills[i]); - if ( incident.usKills[i] ) - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - else - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - - swprintf(sText, L"%d", incident.usWounds[i]); - if ( incident.usWounds[i] ) - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - else - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - - swprintf(sText, L"%d", incident.usPrisoners[i]); - if ( incident.usPrisoners[i] ) - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - else - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_KILLED]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - swprintf(sText, L"%d", incident.usShots[i]); - if ( incident.usShots[i] ) - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - else - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); - + swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_WOUNDED]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - swprintf(sText, L"%d", incident.usParticipants[i]); - if ( incident.usParticipants[i] ) - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - else - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); - + swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_PRISONERS]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - swprintf(sText, L"%d", incident.usPromotions[i]); - if ( incident.usPromotions[i] ) - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - else - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_SHOTSFIRED]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + + swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_PARTICIPANTS]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + + swprintf(sText, szCampaignHistoryWebpageString[WEBPAGE_CAMPAIGNHISTORY_PROMOTIONS]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX = LAPTOP_SCREEN_UL_X; usPosY += 20; - } - } - else - { - UINT8 terrain, type; - incident.GetTerrainandType(terrain, type); - // draw text - // if this is a one-time event, display a special text - if ( incident.usOneTimeEventFlags & 0xFFFFFFFF ) - { - UINT8 eventnr = 0; - if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_OMERTA ) - eventnr = 0; - else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_DEATH_KINGPIN ) - eventnr = 1; - else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_MASSACRE_HICKS ) - eventnr = 2; - else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_MASSACRE_BLOODCATS ) - eventnr = 3; - else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_CITY_RETAKEN ) - eventnr = 4; - else + for(int i = CAMPAIGNHISTORY_SD_MERC; i < CAMPAIGNHISTORY_SD_MAX; ++i) { - for(int i = 0; i < NUM_CAMPAIGNSTATSEVENTS; ++i) - { - if ( bTownId == zCampaignStatsEvent[i].usCityTaken ) - { - eventnr = i; - break; - } - } - } + // if faction was not involved at all, ignore + if ( !incident.usKills[i] && !incident.usWounds[i] && !incident.usPrisoners[i] && !incident.usShots[i] && !incident.usParticipants[i] && !incident.usPromotions[i] ) + continue; - // special picture - use eventnr - GetVideoObject(&hPixHandle, guiCampaignEventImage ); + usPosX = LAPTOP_SCREEN_UL_X; + swprintf(sText, L"%s", szSoldierClassName[i]); + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += 2 * CAMPAIGN_HISTORY_TABLE_STEP_X; - BltVideoObject(FRAME_BUFFER, hPixHandle, eventnr, usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL); + swprintf(sText, L"%d", incident.usKills[i]); + if ( incident.usKills[i] ) + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + else + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - UINT16 picend = usPosY + hPixHandle->pETRLEObject[ eventnr ].usHeight + 3; - usPosX += hPixHandle->pETRLEObject[ eventnr ].usWidth + 3; + swprintf(sText, L"%d", incident.usWounds[i]); + if ( incident.usWounds[i] ) + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + else + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; - for (int i = 0; i < MAX_CAMPAIGNSTATSEVENTS_TEXTS; ++i) - { - swprintf(sText, zCampaignStatsEvent[eventnr].szText[i] ); - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + swprintf(sText, L"%d", incident.usPrisoners[i]); + if ( incident.usPrisoners[i] ) + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + else + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + + swprintf(sText, L"%d", incident.usShots[i]); + if ( incident.usShots[i] ) + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + else + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + + swprintf(sText, L"%d", incident.usParticipants[i]); + if ( incident.usParticipants[i] ) + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + else + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += CAMPAIGN_HISTORY_TABLE_STEP_X; + + swprintf(sText, L"%d", incident.usPromotions[i]); + if ( incident.usPromotions[i] ) + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); + else + DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_PASSIVE, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosY += 20; } } else { - GetVideoObject(&hPixHandle, guiCampaignHistoryPicture[terrain][type] ); + UINT8 terrain, type; + incident.GetTerrainandType(terrain, type); - UINT16 picnum = incident.usID % hPixHandle->usNumberOfObjects; - - BltVideoObject(FRAME_BUFFER, hPixHandle, picnum, usPosX, usPosY, VO_BLT_SRCTRANSPARENCY,NULL); - - UINT16 picend = usPosY + hPixHandle->pETRLEObject[ picnum ].usHeight + 3; - usPosX += hPixHandle->pETRLEObject[ picnum ].usWidth + 3; - - // time string - CHAR16 timestring[ 50 ]; - if ( hour < 3 || hour > 23 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_DEEPNIGHT]); - else if ( hour < 6 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_DAWN]); - else if ( hour < 8 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_EARLYMORNING]); - else if ( hour < 11 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_MORNING]); - else if ( hour < 14 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_NOON]); - else if ( hour < 18 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_AFTERNOON]); - else if ( hour < 21 ) - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_EVENING]); - else - swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_NIGHT]); - - CHAR16 aggressor[ 50 ]; - CHAR16 defender[ 50 ]; - if ( incident.usIncidentFlags & INCIDENT_ATTACK_ENEMY ) + // draw text + // if this is a one-time event, display a special text + if ( incident.usOneTimeEventFlags & 0xFFFFFFFF ) { - swprintf(aggressor, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ARMY]); - swprintf(defender, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REBELFORCES]); + UINT8 eventnr = 0; + if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_OMERTA ) + eventnr = 0; + else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_DEATH_KINGPIN ) + eventnr = 1; + else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_MASSACRE_HICKS ) + eventnr = 2; + else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_MASSACRE_BLOODCATS ) + eventnr = 3; + else if ( incident.usOneTimeEventFlags & INCIDENT_ONETIMEEVENT_CITY_RETAKEN ) + eventnr = 4; + else + { + for(int i = 0; i < NUM_CAMPAIGNSTATSEVENTS; ++i) + { + if ( bTownId == zCampaignStatsEvent[i].usCityTaken ) + { + eventnr = i; + break; + } + } + } + + // special picture - use eventnr + GetVideoObject(&hPixHandle, guiCampaignEventImage ); + + BltVideoObject(FRAME_BUFFER, hPixHandle, eventnr, usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL); + + UINT16 picend = usPosY + hPixHandle->pETRLEObject[ eventnr ].usHeight + 3; + usPosX += hPixHandle->pETRLEObject[ eventnr ].usWidth + 3; + + for (int i = 0; i < MAX_CAMPAIGNSTATSEVENTS_TEXTS; ++i) + { + swprintf(sText, zCampaignStatsEvent[eventnr].szText[i] ); + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } } else { - swprintf(aggressor, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REBELFORCES]); - swprintf(defender, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ARMY]); - } + GetVideoObject(&hPixHandle, guiCampaignHistoryPicture[terrain][type] ); - CHAR16 rebel[ 50 ]; - CHAR16 enemy[ 50 ]; - swprintf(enemy, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ARMY]); - swprintf(rebel, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REBELFORCES]); + UINT16 picnum = incident.usID % hPixHandle->usNumberOfObjects; - CHAR16 attacktype[ 50 ]; - if ( incident.usIncidentFlags & INCIDENT_AIRDROP ) - swprintf(attacktype, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AIRDROPPED]); - else if ( incident.usIncidentFlags & INCIDENT_AMBUSH ) - swprintf(attacktype, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AMBUSHED]); - else - swprintf(attacktype, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ATTACKED]); + BltVideoObject(FRAME_BUFFER, hPixHandle, picnum, usPosX, usPosY, VO_BLT_SRCTRANSPARENCY,NULL); - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SETTING], timestring, aggressor, attacktype, defender, wSectorName_Target); + UINT16 picend = usPosY + hPixHandle->pETRLEObject[ picnum ].usHeight + 3; + usPosX += hPixHandle->pETRLEObject[ picnum ].usWidth + 3; + + // time string + CHAR16 timestring[ 50 ]; + if ( hour < 3 || hour > 23 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_DEEPNIGHT]); + else if ( hour < 6 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_DAWN]); + else if ( hour < 8 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_EARLYMORNING]); + else if ( hour < 11 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_MORNING]); + else if ( hour < 14 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_NOON]); + else if ( hour < 18 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_AFTERNOON]); + else if ( hour < 21 ) + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_EVENING]); + else + swprintf(timestring, szCampaignHistoryTimeString[TEXT_CAMPAIGNHISTORY_TIME_NIGHT]); + + CHAR16 aggressor[ 50 ]; + CHAR16 defender[ 50 ]; + if ( incident.usIncidentFlags & INCIDENT_ATTACK_ENEMY ) + { + swprintf(aggressor, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ARMY]); + swprintf(defender, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REBELFORCES]); + } + else + { + swprintf(aggressor, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REBELFORCES]); + swprintf(defender, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ARMY]); + } + + CHAR16 rebel[ 50 ]; + CHAR16 enemy[ 50 ]; + swprintf(enemy, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ARMY]); + swprintf(rebel, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REBELFORCES]); + + CHAR16 attacktype[ 50 ]; + if ( incident.usIncidentFlags & INCIDENT_AIRDROP ) + swprintf(attacktype, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AIRDROPPED]); + else if ( incident.usIncidentFlags & INCIDENT_AMBUSH ) + swprintf(attacktype, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_AMBUSHED]); + else + swprintf(attacktype, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ATTACKED]); + + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SETTING], timestring, aggressor, attacktype, defender, wSectorName_Target); - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - - if ( incident.usIncidentFlags & (INCIDENT_PLAYER_ALLDIRS|INCIDENT_ENEMY_ALLDIRS) ) - { - if ( incident.usIncidentFlags & INCIDENT_PLAYER_ALLDIRS && incident.usIncidentFlags & INCIDENT_ENEMY_ALLDIRS ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ATTACKERANDDEFENDERDIR], incident.GetAttackerDirString(TRUE), defender, incident.GetAttackerDirString(FALSE) ); - else if ( ( incident.usIncidentFlags & INCIDENT_PLAYER_ALLDIRS && incident.usIncidentFlags & INCIDENT_ATTACK_PLAYERSIDE ) || ( incident.usIncidentFlags & INCIDENT_ENEMY_ALLDIRS && incident.usIncidentFlags & INCIDENT_ATTACK_ENEMY ) ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ATTACKERDIR], incident.GetAttackerDirString(TRUE) ); - else - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_DEFENDERDIR], defender, incident.GetAttackerDirString(FALSE) ); - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - if ( incident.usIncidentFlags & INCIDENT_SAMSITE_SABOTAGED ) - { - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SAMSITESABOTAGED], pCountryNames[COUNTRY_NOUN], pCountryNames[COUNTRY_NAME] ); - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - else if ( incident.usIncidentFlags & INCIDENT_BUILDINGS_DAMAGED ) - { - if ( incident.usKills[CAMPAIGNHISTORY_SD_CIV] + incident.usWounds[CAMPAIGNHISTORY_SD_CIV] > 0 ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_BUILDINGANDCIVDAMAGE], incident.usKills[CAMPAIGNHISTORY_SD_CIV], incident.usWounds[CAMPAIGNHISTORY_SD_CIV] ); - else - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_BUILDINGDAMAGE] ); - - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - - if ( incident.usIncidentFlags & (INCIDENT_REINFORCEMENTS_ENEMY|INCIDENT_REINFORCEMENTS_PLAYERSIDE) ) - { - if ( incident.usIncidentFlags & INCIDENT_REINFORCEMENTS_ENEMY && incident.usIncidentFlags & INCIDENT_REINFORCEMENTS_PLAYERSIDE ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REINFORCE_BOTH], aggressor, defender ); - else if ( incident.usIncidentFlags & INCIDENT_REINFORCEMENTS_ENEMY ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REINFORCE], enemy ); - else - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REINFORCE], rebel ); - - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - - if ( incident.usIncidentFlags & (INCIDENT_MUSTARDGAS_ENEMY|INCIDENT_MUSTARDGAS_PLAYERSIDE) ) - { - if ( incident.usIncidentFlags & INCIDENT_MUSTARDGAS_ENEMY && incident.usIncidentFlags & INCIDENT_MUSTARDGAS_PLAYERSIDE ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_CHEMICAL_BOTH] ); - else if ( incident.usIncidentFlags & INCIDENT_MUSTARDGAS_ENEMY ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_CHEMICAL], enemy ); - else - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_CHEMICAL], rebel ); - - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - - if ( incident.usIncidentFlags & (INCIDENT_TANKS_ENEMY|INCIDENT_TANKS_PLAYERSIDE) ) - { - if ( incident.usIncidentFlags & INCIDENT_TANKS_ENEMY && incident.usIncidentFlags & INCIDENT_TANKS_PLAYERSIDE ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_TANKS_BOTH] ); - else if ( incident.usIncidentFlags & INCIDENT_TANKS_ENEMY ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_TANKS], incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_TANK], enemy, incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_TANK] ); - else - // TODO - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_TANKS], incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_TANK], rebel, incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_TANK] ); - - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - - if ( incident.usIncidentFlags & (INCIDENT_SNIPERS_ENEMY|INCIDENT_SNIPERS_PLAYERSIDE) ) - { - if ( incident.usIncidentFlags & INCIDENT_SNIPERS_ENEMY && incident.usIncidentFlags & INCIDENT_SNIPERS_PLAYERSIDE ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SNIPERS_BOTH] ); - else if ( incident.usIncidentFlags & INCIDENT_SNIPERS_ENEMY ) - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SNIPERS], enemy ); - else - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SNIPERS], rebel ); - - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; - } - - // only report on spies if they were uncovered - if ( incident.usIncidentFlags & INCIDENT_SPYACTION_UNCOVERED ) - { - if ( incident.usIncidentFlags & INCIDENT_SPYACTION_ENEMY ) + if ( incident.usIncidentFlags & (INCIDENT_PLAYER_ALLDIRS|INCIDENT_ENEMY_ALLDIRS) ) { - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SPY_ENEMY] ); + if ( incident.usIncidentFlags & INCIDENT_PLAYER_ALLDIRS && incident.usIncidentFlags & INCIDENT_ENEMY_ALLDIRS ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ATTACKERANDDEFENDERDIR], incident.GetAttackerDirString(TRUE), defender, incident.GetAttackerDirString(FALSE) ); + else if ( ( incident.usIncidentFlags & INCIDENT_PLAYER_ALLDIRS && incident.usIncidentFlags & INCIDENT_ATTACK_PLAYERSIDE ) || ( incident.usIncidentFlags & INCIDENT_ENEMY_ALLDIRS && incident.usIncidentFlags & INCIDENT_ATTACK_ENEMY ) ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_ATTACKERDIR], incident.GetAttackerDirString(TRUE) ); + else + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_DEFENDERDIR], defender, incident.GetAttackerDirString(FALSE) ); + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; } - if ( incident.usIncidentFlags & INCIDENT_SPYACTION_PLAYERSIDE ) + if ( incident.usIncidentFlags & INCIDENT_SAMSITE_SABOTAGED ) { - swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SPY_PLAYER] ); + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SAMSITESABOTAGED], pCountryNames[COUNTRY_NOUN], pCountryNames[COUNTRY_NAME] ); usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; } - } + else if ( incident.usIncidentFlags & INCIDENT_BUILDINGS_DAMAGED ) + { + if ( incident.usKills[CAMPAIGNHISTORY_SD_CIV] + incident.usWounds[CAMPAIGNHISTORY_SD_CIV] > 0 ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_BUILDINGANDCIVDAMAGE], incident.usKills[CAMPAIGNHISTORY_SD_CIV], incident.usWounds[CAMPAIGNHISTORY_SD_CIV] ); + else + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_BUILDINGDAMAGE] ); - { - // team losses - UINT16 rebellosses = incident.usKills[CAMPAIGNHISTORY_SD_MERC] + incident.usKills[CAMPAIGNHISTORY_SD_MILITIA_GREEN] + incident.usKills[CAMPAIGNHISTORY_SD_MILITIA_REGULAR] + incident.usKills[CAMPAIGNHISTORY_SD_MILITIA_ELITE]; - UINT16 armylosses = incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ADMIN] + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ELITE] + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_TANK]; - UINT16 armypows = incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_ADMIN] + incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_ELITE] + incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_TANK]; + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } - // team sizes - UINT16 rebelsize = max(1, incident.usParticipants[CAMPAIGNHISTORY_SD_MERC] + incident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_GREEN] + incident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_REGULAR] + incident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_ELITE]); - UINT16 armysize = max(1, incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_ADMIN] + incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_ELITE] + incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_TANK]); + if ( incident.usIncidentFlags & (INCIDENT_REINFORCEMENTS_ENEMY|INCIDENT_REINFORCEMENTS_PLAYERSIDE) ) + { + if ( incident.usIncidentFlags & INCIDENT_REINFORCEMENTS_ENEMY && incident.usIncidentFlags & INCIDENT_REINFORCEMENTS_PLAYERSIDE ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REINFORCE_BOTH], aggressor, defender ); + else if ( incident.usIncidentFlags & INCIDENT_REINFORCEMENTS_ENEMY ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REINFORCE], enemy ); + else + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_REINFORCE], rebel ); + + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } + + if ( incident.usIncidentFlags & (INCIDENT_MUSTARDGAS_ENEMY|INCIDENT_MUSTARDGAS_PLAYERSIDE) ) + { + if ( incident.usIncidentFlags & INCIDENT_MUSTARDGAS_ENEMY && incident.usIncidentFlags & INCIDENT_MUSTARDGAS_PLAYERSIDE ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_CHEMICAL_BOTH] ); + else if ( incident.usIncidentFlags & INCIDENT_MUSTARDGAS_ENEMY ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_CHEMICAL], enemy ); + else + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_CHEMICAL], rebel ); + + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } + + if ( incident.usIncidentFlags & (INCIDENT_TANKS_ENEMY|INCIDENT_TANKS_PLAYERSIDE) ) + { + if ( incident.usIncidentFlags & INCIDENT_TANKS_ENEMY && incident.usIncidentFlags & INCIDENT_TANKS_PLAYERSIDE ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_TANKS_BOTH] ); + else if ( incident.usIncidentFlags & INCIDENT_TANKS_ENEMY ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_TANKS], incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_TANK], enemy, incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_TANK] ); + else + // TODO + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_TANKS], incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_TANK], rebel, incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_TANK] ); + + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } + + if ( incident.usIncidentFlags & (INCIDENT_SNIPERS_ENEMY|INCIDENT_SNIPERS_PLAYERSIDE) ) + { + if ( incident.usIncidentFlags & INCIDENT_SNIPERS_ENEMY && incident.usIncidentFlags & INCIDENT_SNIPERS_PLAYERSIDE ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SNIPERS_BOTH] ); + else if ( incident.usIncidentFlags & INCIDENT_SNIPERS_ENEMY ) + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SNIPERS], enemy ); + else + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SNIPERS], rebel ); + + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } + + // only report on spies if they were uncovered + if ( incident.usIncidentFlags & INCIDENT_SPYACTION_UNCOVERED ) + { + if ( incident.usIncidentFlags & INCIDENT_SPYACTION_ENEMY ) + { + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SPY_ENEMY] ); + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } + + if ( incident.usIncidentFlags & INCIDENT_SPYACTION_PLAYERSIDE ) + { + swprintf(sText, szCampaignHistoryDetail[TEXT_CAMPAIGNHISTORY_DETAIL_SPY_PLAYER] ); + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } + } + + { + // team losses + UINT16 rebellosses = incident.usKills[CAMPAIGNHISTORY_SD_MERC] + incident.usKills[CAMPAIGNHISTORY_SD_MILITIA_GREEN] + incident.usKills[CAMPAIGNHISTORY_SD_MILITIA_REGULAR] + incident.usKills[CAMPAIGNHISTORY_SD_MILITIA_ELITE]; + UINT16 armylosses = incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ADMIN] + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ELITE] + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_TANK]; + UINT16 armypows = incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_ADMIN] + incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_ELITE] + incident.usPrisoners[CAMPAIGNHISTORY_SD_ENEMY_TANK]; + + // team sizes + UINT16 rebelsize = max(1, incident.usParticipants[CAMPAIGNHISTORY_SD_MERC] + incident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_GREEN] + incident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_REGULAR] + incident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_ELITE]); + UINT16 armysize = max(1, incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_ADMIN] + incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_ELITE] + incident.usParticipants[CAMPAIGNHISTORY_SD_ENEMY_TANK]); - // if rebels won... - if ( incident.usIncidentFlags & INCIDENT_WIN && rebelsize ) - { - FLOAT ratio = (FLOAT)(rebellosses) / (FLOAT)(rebelsize); - if ( ratio < 0.1 ) - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_ONESIDED_REBEL] ); - else if ( ratio < 0.3 ) + // if rebels won... + if ( incident.usIncidentFlags & INCIDENT_WIN && rebelsize ) { - if ( armypows ) - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_REBEL_PRISONER] ); + FLOAT ratio = (FLOAT)(rebellosses) / (FLOAT)(rebelsize); + if ( ratio < 0.1 ) + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_ONESIDED_REBEL] ); + else if ( ratio < 0.3 ) + { + if ( armypows ) + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_REBEL_PRISONER] ); + else + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_REBEL] ); + } + else if ( ratio < 0.6 ) + { + if ( armypows ) + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_REBEL] ); + else + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_REBEL_PRISONER] ); + } else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_REBEL] ); + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_HARD_REBEL] ); } - else if ( ratio < 0.6 ) + else if ( armysize ) // army won... { - if ( armypows ) - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_REBEL] ); - else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_REBEL_PRISONER] ); - } - else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_HARD_REBEL] ); - } - else if ( armysize ) // army won... - { - FLOAT ratio = (FLOAT)(armylosses) / (FLOAT)(armysize); - BOOLEAN armyhadmore = (armysize > rebelsize); + FLOAT ratio = (FLOAT)(armylosses) / (FLOAT)(armysize); + BOOLEAN armyhadmore = (armysize > rebelsize); - if ( ratio < 0.1 ) - { - if ( armyhadmore ) - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_ONESIDED_ARMY_NUMBERS] ); + if ( ratio < 0.1 ) + { + if ( armyhadmore ) + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_ONESIDED_ARMY_NUMBERS] ); + else + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_ONESIDED_ARMY_TRAINING] ); + } + else if ( ratio < 0.3 ) + { + if ( armyhadmore ) + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_ARMY_NUMBERS] ); + else + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_ARMY_TRAINING] ); + } + else if ( ratio < 0.6 ) + { + if ( armyhadmore ) + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_ARMY_NUMBERS] ); + else + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_ARMY_TRAINING] ); + } else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_ONESIDED_ARMY_TRAINING] ); + swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_HARD_ARMY] ); } - else if ( ratio < 0.3 ) - { - if ( armyhadmore ) - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_ARMY_NUMBERS] ); - else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_EASY_ARMY_TRAINING] ); - } - else if ( ratio < 0.6 ) - { - if ( armyhadmore ) - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_ARMY_NUMBERS] ); - else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_MEDIUM_ARMY_TRAINING] ); - } - else - swprintf(sText, szCampaignHistoryResultString[TEXT_CAMPAIGNHISTORY_RESULT_HARD_ARMY] ); - } - usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); - if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + usPosY += DisplayWrappedString( usPosX, usPosY, LAPTOP_SCREEN_LR_X - usPosX, 2, CAMPHIS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR,sText, FONT_MCOLOR_BLACK, FALSE, 0); + if ( picend < usPosY ) usPosX = LAPTOP_SCREEN_UL_X; + } } } } @@ -936,9 +909,11 @@ void SelectCampaignHistoryMostImportantRegionCallBack(MOUSE_REGION * pRegion, IN ////////////////////////// MOST IMPORTANT PAGE ////////////////////////////////// ////////////////////////// NEWS PAGE ////////////////////////////////// +BOOLEAN gfCampaignHistoryNewsRedraw = FALSE; + //link to the various pages MOUSE_REGION gCampaignHistoryNewsLinkRegion[4]; -void SelectCampaignHistoryNewsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); +void SelectCampaignHistoryNewsRegionCallBack( MOUSE_REGION * pRegion, INT32 iReason ); UINT32 gusNewsPage = 0; @@ -976,11 +951,17 @@ void ExitCampaignHistory_News() // previous/next buttons for(int i=0; i<4; ++i) MSYS_RemoveRegion( &gCampaignHistoryNewsLinkRegion[i]); + + gfCampaignHistoryNewsRedraw = FALSE; } void HandleCampaignHistory_News() { - + if ( gfCampaignHistoryNewsRedraw ) + { + RenderCampaignHistory_News( ); + gfCampaignHistoryNewsRedraw = FALSE; + } } void RenderCampaignHistory_News() @@ -996,46 +977,7 @@ void RenderCampaignHistory_News() usPosX = LAPTOP_SCREEN_UL_X; usPosY = LAPTOP_SCREEN_WEB_UL_Y + 80; - - // return if there are no incidents yet - /*if ( !gCampaignStats.usNumIncidents ) - return; - - Incident_Stats incident = gCampaignStats.mIncidentVector[ gusNewsPage ]; - - CHAR16 wSectorName_Target[ 100 ]; - GetSectorIDString( SECTORX(incident.usSector), SECTORY(incident.usSector), incident.usLevel, wSectorName_Target, TRUE ); - - //Calculate the day, hour, and minutes. - UINT32 day = ( incident.usTime / NUM_SEC_IN_DAY ); - UINT32 hour = ( incident.usTime - ( day * NUM_SEC_IN_DAY ) ) / NUM_SEC_IN_HOUR; - UINT32 minute = ( incident.usTime - ( ( day * NUM_SEC_IN_DAY ) + ( hour * NUM_SEC_IN_HOUR ) ) ) / NUM_SEC_IN_MIN;*/ - - //swprintf(sText, L"Incident #%d - %s, Day %d, %02d:%02d", incident.usID, wSectorName_Target, day, hour, minute); - swprintf(sText, L"--- currently under construction ---"); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, CAMPHIS_FONT_BIG, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); - - - - // previous/next buttons - /*usPosX = CAMPAIGN_HISTORY_PAGEBTN_X; - usPosY = CAMPAIGN_HISTORY_PAGEBTN_Y; - swprintf(sText, L"Summary"); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_PAGEBTN_STEP_X; - swprintf(sText, L"Detail"); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_PAGEBTN_STEP_X; - swprintf(sText, L"Previous"); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 ); - - usPosX += CAMPAIGN_HISTORY_PAGEBTN_STEP_X; - swprintf(sText, L"Next"); - DrawTextToScreen( sText, usPosX, usPosY, LAPTOP_SCREEN_LR_X-LAPTOP_SCREEN_UL_X, INS_FONT_SMALL, CAMPAIGN_HISTORY_FONT_COLOR_REGULAR, FONT_MCOLOR_BLACK, FALSE, 0 );*/ - - + SetFontShadow( DEFAULT_SHADOW ); MarkButtonsDirty( ); RenderWWWProgramTitleBar( ); diff --git a/Laptop/CampaignStats.cpp b/Laptop/CampaignStats.cpp index c7976e65..70965d11 100644 --- a/Laptop/CampaignStats.cpp +++ b/Laptop/CampaignStats.cpp @@ -21,8 +21,6 @@ Incident_Stats gCurrentIncident; // we might save during an incident, thus we h CAMPAIGNSTATSEVENT zCampaignStatsEvent[NUM_CAMPAIGNSTATSEVENTS]; -extern INT32 ReadFieldByField(HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount); - void Incident_Stats::clear() { @@ -748,3 +746,48 @@ void StartIncident(INT16 sX, INT16 sY, INT8 sZ) gCurrentIncident.usSector = SECTOR(sX, sY); gCurrentIncident.usLevel = sZ; } + +INT32 GetPositionOfIncident( UINT32 aIncidentId ) +{ + INT32 cnt = 0; + + std::vector::iterator itend = gCampaignStats.mIncidentVector.end( ); + for ( std::vector::iterator it = gCampaignStats.mIncidentVector.begin( ); it != itend; ++it ) + { + if ( (*it).usID == aIncidentId ) + return cnt; + + ++cnt; + } + + return -1; +} + +static CHAR16 gIncidentNameText[100]; +STR16 GetIncidentName( UINT32 aIncidentId ) +{ + swprintf( gIncidentNameText, L"NAME NOT FOUND" ); + + std::vector::iterator itend = gCampaignStats.mIncidentVector.end( ); + for ( std::vector::iterator it = gCampaignStats.mIncidentVector.begin( ); it != itend; ++it ) + { + if ( (*it).usID == aIncidentId ) + { + Incident_Stats incident = (*it); + + UINT32 prefix = (incident.usTime + incident.usKills[CAMPAIGNHISTORY_SD_ENEMY_ARMY] + incident.usID) % CAMPAIGNSTATS_OPERATION_NUM_PREFIX; + UINT32 suffix = (incident.usTime + incident.usShots[CAMPAIGNHISTORY_SD_MERC] + 7 * incident.usID) % CAMPAIGNSTATS_OPERATION_NUM_SUFFIX; + + swprintf( gIncidentNameText, szCampaignStatsOperationPrefix[prefix], szCampaignStatsOperationSuffix[suffix] ); + + break; + } + } + + return gIncidentNameText; +} + +UINT32 GetIdOfCurrentlyOngoingIncident() +{ + return gCampaignStats.usHighestID + 1; +} diff --git a/Laptop/CampaignStats.h b/Laptop/CampaignStats.h index 45011eeb..2721ee3d 100644 --- a/Laptop/CampaignStats.h +++ b/Laptop/CampaignStats.h @@ -327,4 +327,9 @@ void FinishIncident(INT16 sX, INT16 sY, INT8 sZ); // start a new incident void StartIncident(INT16 sX, INT16 sY, INT8 sZ); +INT32 GetPositionOfIncident( UINT32 aIncidentId ); +STR16 GetIncidentName( UINT32 aIncidentId ); + +UINT32 GetIdOfCurrentlyOngoingIncident(); + #endif // __CAMPAIGNSTATS_H \ No newline at end of file diff --git a/Laptop/DropDown.h b/Laptop/DropDown.h index b9c84e63..c354e226 100644 --- a/Laptop/DropDown.h +++ b/Laptop/DropDown.h @@ -136,6 +136,11 @@ enum definedDropDowns DROPDOWNNR_MSGBOX_1, DROPDOWNNR_MSGBOX_2, + + DROPDOWN_MILTIAWEBSITE_FILTER_DEAD, + DROPDOWN_MILTIAWEBSITE_FILTER_RANK, + DROPDOWN_MILTIAWEBSITE_FILTER_ORIGIN, + DROPDOWN_MILTIAWEBSITE_FILTER_SECTOR, }; /* diff --git a/Laptop/IMP Gear.cpp b/Laptop/IMP Gear.cpp index 39b00c84..6a110bca 100644 --- a/Laptop/IMP Gear.cpp +++ b/Laptop/IMP Gear.cpp @@ -136,7 +136,6 @@ extern BOOLEAN fNewIMPGearMethodUsed; void IMPGearDisplay( ); -void DisplayGear( UINT16 usItem, UINT16 usPosX, UINT16 usPosY, BOOLEAN fWithBackGround ); void BtnIMPGearFinishCallback( GUI_BUTTON *btn, INT32 reason ); // determine all items that are selectable according to xml and choices @@ -482,22 +481,11 @@ void IMPGearDisplay( ) INT16 sItem = (UINT16)pIMPGEARDropDown[i]->GetSelectedEntryKey( ); - DisplayGear( (UINT16)sItem, pIMPGEARDropDown[i]->GetX( ) - IMP_GEAR_ITEMDISPLAY_WIDTH, pIMPGEARDropDown[i]->GetY( ), TRUE ); + DisplayGear( (UINT16)sItem, pIMPGEARDropDown[i]->GetX( ) - IMP_GEAR_ITEMDISPLAY_WIDTH, pIMPGEARDropDown[i]->GetY( ), TRUE, gIMPGearCount[i], TRUE ); if ( sItem > 0 ) { - if ( sItem ) - gIMPGearCost += gIMPGearCount[i] * Item[sItem].usPrice; - - // if more than 1? - if ( gIMPGearCount[i] > 1 ) - { - CHAR16 sString[128]; - swprintf( sString, L"%d", gIMPGearCount[i] ); - INT16 sX, sY; - FindFontRightCoordinates( (INT16)(pIMPGEARDropDown[i]->GetX( ) - IMP_GEAR_ITEMDISPLAY_WIDTH), (INT16)(pIMPGEARDropDown[i]->GetY( ) + 6), (INT16)(10), (INT16)(GetFontHeight( FONT10ARIAL )), sString, FONT10ARIAL, &sX, &sY ); - mprintf( sX, sY, sString ); - } + gIMPGearCost += gIMPGearCount[i] * Item[sItem].usPrice; } } else @@ -511,7 +499,7 @@ void IMPGearDisplay( ) DrawTextToScreen( wTemp, LAPTOP_SCREEN_UL_X + 180, LAPTOP_SCREEN_WEB_UL_Y + 360, LAPTOP_TEXT_WIDTH, FONT14ARIAL, IMP_GEAR__COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); } -void DisplayGear( UINT16 usItem, UINT16 usPosX, UINT16 usPosY, BOOLEAN fWithBackGround ) +void DisplayGear( UINT16 usItem, UINT16 usPosX, UINT16 usPosY, BOOLEAN fWithBackGround, UINT8 aNumber, BOOLEAN fDisplayNumber ) { if ( usItem ) { @@ -549,12 +537,17 @@ void DisplayGear( UINT16 usItem, UINT16 usPosX, UINT16 usPosY, BOOLEAN fWithBack //blt the item BltVideoObjectOutlineFromIndex( FRAME_BUFFER, GetInterfaceGraphicForItem( pItem ), usGraphicNum, PosX, PosY, 0, FALSE ); + + // display number if more than 1 + if ( fDisplayNumber && aNumber > 1 ) + { + CHAR16 sString[128]; + swprintf( sString, L"%d", aNumber ); + INT16 sX, sY; + FindFontRightCoordinates( (INT16)(usPosX), (INT16)(usPosY + 6), 10, (INT16)(GetFontHeight( FONT10ARIAL )), sString, FONT10ARIAL, &sX, &sY ); + mprintf( sX, sY, sString ); + } } - /*else - { - // if there is not item to display, use a different colour for the box - ColorFillVideoSurfaceArea( FRAME_BUFFER, usPosX, usPosY, usPosX + IMP_GEAR_ITEMDISPLAY_WIDTH, usPosY + IMP_GEAR_ITEMDISPLAY_HEIGHT, Get16BPPColor( FROMRGB( 230, 84, 43 ) ) ); - }*/ } void BtnIMPGearFinishCallback( GUI_BUTTON *btn, INT32 reason ) diff --git a/Laptop/IMP Gear.h b/Laptop/IMP Gear.h index 15b37b82..01d1971d 100644 --- a/Laptop/IMP Gear.h +++ b/Laptop/IMP Gear.h @@ -14,6 +14,8 @@ void RenderIMPGear( void ); void ExitIMPGear( void ); void HandleIMPGear( void ); +void DisplayGear( UINT16 usItem, UINT16 usPosX, UINT16 usPosY, BOOLEAN fWithBackGround, UINT8 aNumber, BOOLEAN fDisplayNumber ); + void GiveIMPSelectedGear( MERCPROFILESTRUCT *pProfile ); INT32 GetIMPGearCost(); diff --git a/Laptop/Laptop_VS2005.vcproj b/Laptop/Laptop_VS2005.vcproj index 57bec86f..db46f889 100644 --- a/Laptop/Laptop_VS2005.vcproj +++ b/Laptop/Laptop_VS2005.vcproj @@ -370,6 +370,10 @@ RelativePath=".\AimSort.h" > + + @@ -654,6 +658,14 @@ RelativePath=".\mercs.h" > + + + + @@ -720,6 +732,10 @@ RelativePath=".\AimSort.cpp" > + + @@ -984,6 +1000,14 @@ RelativePath=".\mercs.cpp" > + + + + diff --git a/Laptop/Laptop_VS2008.vcproj b/Laptop/Laptop_VS2008.vcproj index 4486db1c..59c51cb4 100644 --- a/Laptop/Laptop_VS2008.vcproj +++ b/Laptop/Laptop_VS2008.vcproj @@ -370,6 +370,10 @@ RelativePath="AimSort.h" > + + @@ -654,6 +658,14 @@ RelativePath="mercs.h" > + + + + @@ -718,6 +730,10 @@ RelativePath="AimSort.cpp" > + + @@ -986,6 +1002,14 @@ RelativePath="mercs.cpp" > + + + + diff --git a/Laptop/Laptop_VS2010.vcxproj b/Laptop/Laptop_VS2010.vcxproj index 8d38f2e5..7ce8eee5 100644 --- a/Laptop/Laptop_VS2010.vcxproj +++ b/Laptop/Laptop_VS2010.vcxproj @@ -31,6 +31,7 @@ + @@ -102,6 +103,8 @@ + + @@ -119,6 +122,7 @@ + @@ -186,6 +190,8 @@ + + diff --git a/Laptop/Laptop_VS2010.vcxproj.filters b/Laptop/Laptop_VS2010.vcxproj.filters index bc001b4e..7db1c7ea 100644 --- a/Laptop/Laptop_VS2010.vcxproj.filters +++ b/Laptop/Laptop_VS2010.vcxproj.filters @@ -32,6 +32,9 @@ Header Files + + + Header Files Header Files @@ -203,6 +206,12 @@ Header Files + + + Header Files + + + Header Files Header Files @@ -292,6 +301,9 @@ Source Files + + + Source Files Source Files @@ -454,6 +466,12 @@ Source Files + + + Source Files + + + Source Files Source Files diff --git a/Laptop/Laptop_VS2013.vcxproj b/Laptop/Laptop_VS2013.vcxproj index 660d76ae..0e7e2dd4 100644 --- a/Laptop/Laptop_VS2013.vcxproj +++ b/Laptop/Laptop_VS2013.vcxproj @@ -31,6 +31,7 @@ + @@ -102,6 +103,8 @@ + + @@ -119,6 +122,7 @@ + @@ -186,6 +190,8 @@ + + diff --git a/Laptop/Laptop_VS2013.vcxproj.filters b/Laptop/Laptop_VS2013.vcxproj.filters index b44db55f..2d4de900 100644 --- a/Laptop/Laptop_VS2013.vcxproj.filters +++ b/Laptop/Laptop_VS2013.vcxproj.filters @@ -267,6 +267,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -548,5 +557,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Laptop/MilitiaInterface.cpp b/Laptop/MilitiaInterface.cpp new file mode 100644 index 00000000..be56d7ef --- /dev/null +++ b/Laptop/MilitiaInterface.cpp @@ -0,0 +1,642 @@ +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#include "MilitiaInterface.h" + +#include "Font.h" +#include "laptop.h" +#include "Cursors.h" +#include "Utilities.h" +#include "vobject.h" +#include "WCheck.h" +#include "line.h" +#include "WordWrap.h" +#include "input.h" // for gfLeftButtonState +#include "personnel.h" +#include "Animation Data.h" +#include "Text.h" +#include "MilitiaWebsite.h" +#include "Game Clock.h" +#include "Militia Control.h" +#include "Town Militia.h" +#include "Interface Items.h" +#include "Interface.h" // for DIRTYLEVEL2 + +extern BOOLEAN gfMilitiaWebsiteMainRedraw; + +UINT32 guTraitImages = 0; + +MilitiaServiceRecordTable::MilitiaServiceRecordTable( ) +: BaseTable( ) +{ +} + +void +MilitiaServiceRecordTable::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetColorLine( Get16BPPColor( FROMRGB( 128, 128, 128 ) ) ); + SetColorLineShadow( Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + SetColorMarked( Get16BPPColor( FROMRGB( 200, 169, 87 ) ) ); + SetColorHighLight( Get16BPPColor( FROMRGB( 235, 222, 171 ) ) ); + + BaseTable::Init( sX, sY, sX_End, sY_End ); +} + +void +MilitiaServiceRecordTable::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); + + TestTableTemplate<2>::getInstance( ).Create( sX + 4, sY + 14, sX_End, sY_End ); + RegisterMemberThingy( &TestTableTemplate<2>::getInstance( ) ); +} + +void +MilitiaServiceRecordTable::Destroy( ) +{ + if ( !IsInit( ) ) + return; + + BaseTable::Destroy( ); +} + +void +MilitiaServiceRecordTable::Display( ) +{ + if ( !IsInit( ) ) + return; + + DestroyMouseRegions( ); + + SetFontShadow( NO_SHADOW ); + + CHAR16 sText[800]; + swprintf( sText, L"" ); + UINT16 usPosX = GetX( ) + 10; + UINT16 usPosY = GetY( ) + 10; + + MILITIA militia; + if ( GetMilitia( mMilitiaId, &militia ) ) + { + TestTableTemplate<2>::getInstance( ).ClearColumnDataProvider( ); + + ColumnDataProvider namcol( szIdividualMilitiaWebsiteText[0] ); + namcol.SetMethodString( OperationText ); + namcol.SetNumberOfEntries( militia.history.size( ) ); + namcol.SetCallBackType( ColumnDataProvider::CDP_MILITIADETAIL ); + + TestTableTemplate<2>::getInstance( ).AddColumnDataProvider( namcol ); + + TestTableTemplate<2>::getInstance( ).CalcRows( ); + + BaseTable::Display(); + } +} + +void +MilitiaServiceRecordTable::SetRefresh( ) +{ + gfMilitiaWebsiteMainRedraw = TRUE; +} + +///////////////////////////////////////////////////// +MilitiaPersonalDataTable::MilitiaPersonalDataTable( ) +: TestPanel( ) +, mButtonFire(-1) +, mButtonFireCreated(FALSE) +{ +} + +void +MilitiaPersonalDataTable::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetColorLine( Get16BPPColor( FROMRGB( 128, 128, 128 ) ) ); + SetColorLineShadow( Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + SetColorMarked( Get16BPPColor( FROMRGB( 200, 169, 87 ) ) ); + SetColorHighLight( Get16BPPColor( FROMRGB( 235, 222, 171 ) ) ); + + TestPanel::Init( sX, sY, sX_End, sY_End ); +} + +////// callback for buttons //////// +void +MilitiaPersonalDataTableFireConfirmationCallback( UINT8 val ) +{ + if ( val == MSG_BOX_RETURN_YES ) + { + MILITIA militia; + if ( GetMilitia( gusCurrentMilitia, &militia ) ) + { + if ( !(militia.flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) ) + { + militia.Fire(); + + UpdateMilitia( militia ); + + INT8 type = SoldierClassToMilitiaRank( militia.soldierclass); + + if ( type > -1 ) + { + StrategicRemoveMilitiaFromSector( SECTORX( militia.sector ), SECTORY( militia.sector ), (UINT8)type, 1 ); + + ResetMilitia(); + } + + militiaindividualmainwidget.SetRefresh(); + } + } + } +} + +void MilitiaPersonalDataTableFireCallback( GUI_BUTTON *btn, INT32 reason ) +{ + if ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MILITIA militia; + if ( GetMilitia( gusCurrentMilitia, &militia ) ) + { + if ( !(militia.flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) ) + { + CHAR16 sString[256]; + swprintf( sString, szIdividualMilitiaWebsiteText[1], militia.GetName( ) ); + + DoLowerScreenIndependantMessageBox( sString, MSG_BOX_FLAG_YESNO, MilitiaPersonalDataTableFireConfirmationCallback ); + } + } + } +} +////// callback for buttons //////// + +void +MilitiaPersonalDataTable::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); +} + +void +MilitiaPersonalDataTable::Destroy( ) +{ + if ( !IsInit( ) ) + return; + + if ( mButtonFireCreated ) + { + RemoveButton( mButtonFire ); + mButtonFireCreated = FALSE; + } + + TestPanel::Destroy( ); +} + +void +MilitiaPersonalDataTable::Display( ) +{ + if ( !IsInit( ) ) + return; + + DestroyMouseRegions( ); + + MILITIA militia; + if ( GetMilitia( mMilitiaId, &militia ) ) + { + CHAR16 sText[800]; + swprintf( sText, L"" ); + UINT16 usPosX = GetX( ) + 10; + UINT16 usPosY = GetY( ) + 10; + + // show image of militia with correct visuals + { + VOBJECT_DESC VObjectDesc; + HVOBJECT hHandle; + UINT32 uiGraphicHandle; + BOOLEAN success = TRUE; + + // load it + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + + // Pick images based on body type + switch ( militia.bodytype ) + { + case REGMALE: FilenameForBPP( "Laptop\\IMPSmallMercImages.sti", VObjectDesc.ImageFile ); break; + case BIGMALE: FilenameForBPP( "Laptop\\IMPBigMercImages.sti", VObjectDesc.ImageFile ); break; + case STOCKYMALE: FilenameForBPP( "Laptop\\IMPBigMercImages.sti", VObjectDesc.ImageFile ); break; + case REGFEMALE: FilenameForBPP( "Laptop\\IMPFemaleMercImages.sti", VObjectDesc.ImageFile ); break; + default: + success = FALSE; + break; + } + + // only show images if they can be loaded + if ( success ) + { + // safety check if loaded + CHECKV( AddVideoObject( &VObjectDesc, &uiGraphicHandle ) ); + + // Get it + GetVideoObject( &hHandle, uiGraphicHandle ); + + if ( hHandle ) + { + UINT8 vest = GREENVEST; + UINT8 pants = BEIGEPANTS; + if ( militia.soldierclass == SOLDIER_CLASS_REG_MILITIA ) + vest = JEANVEST; + else if ( militia.soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + vest = BLUEVEST; + + // show background first + BltVideoObject( FRAME_BUFFER, hHandle, 0, usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL ); + // show skin (1 - 4) + BltVideoObject( FRAME_BUFFER, hHandle, (militia.skin + 1), usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL ); + // show head (5 - 9) + BltVideoObject( FRAME_BUFFER, hHandle, (militia.hair + 5), usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL ); + // show vest (10 - 20) + BltVideoObject( FRAME_BUFFER, hHandle, (vest + 10), usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL ); + // show pants (21 - 26) + BltVideoObject( FRAME_BUFFER, hHandle, (pants + 21), usPosX, usPosY, VO_BLT_SRCTRANSPARENCY, NULL ); + + DeleteVideoObjectFromIndex( uiGraphicHandle ); + } + } + } + + usPosX += 120; + + SetFontShadow( NO_SHADOW ); + + if ( militia.flagmask & MILITIAFLAG_DEAD ) + { + swprintf( sText, szIdividualMilitiaWebsiteText[5] ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, FONT_MCOLOR_RED, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + } + else if ( militia.flagmask & MILITIAFLAG_FIRED ) + { + swprintf( sText, szIdividualMilitiaWebsiteText[6] ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, FONT_MCOLOR_DKGRAY, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + } + else + { + swprintf( sText, szIdividualMilitiaWebsiteText[7] ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, FONT_MCOLOR_LTGREEN, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + } + + if ( gGameExternalOptions.fIndividualMilitia_ManageHealth ) + { + swprintf( sText, szIdividualMilitiaWebsiteText[15], militia.healthratio ); + + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, 2, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + } + + swprintf( sText, szIdividualMilitiaWebsiteText[3], militia.GetWage( ), militia.age ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, 2, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + + swprintf( sText, szIdividualMilitiaWebsiteText[4], militia.kills, militia.assists ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, 2, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + + usPosY += 4; + usPosX -= 2; + + // button for firing this guy + if ( mButtonFireCreated ) + { + RemoveButton( mButtonFire ); + mButtonFireCreated = FALSE; + } + + // if we can still fire this guy, set up the button + if ( !(militia.flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) ) + { + // we can't fire someone if they are in combat + if ( SectorOursAndPeaceful( SECTORX( militia.sector ), SECTORY( militia.sector ), 0 ) ) + { + mButtonFire = CreateTextButton( szIdividualMilitiaWebsiteText[8], FONT12ARIAL, FONT_YELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, + usPosX, usPosY, 120, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, MilitiaPersonalDataTableFireCallback ); + + mButtonFireCreated = TRUE; + } + else + { + mButtonFire = CreateTextButton( szIdividualMilitiaWebsiteText[8], FONT12ARIAL, FONT_RED, FONT_BLACK, BUTTON_USE_DEFAULT, + usPosX, usPosY, 120, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, NULL ); + + mButtonFireCreated = TRUE; + } + } + + SetFontShadow( NO_SHADOW ); + } + + BaseTable::Display( ); +} + +void +MilitiaPersonalDataTable::SetRefresh( ) +{ + gfMilitiaWebsiteMainRedraw = TRUE; +} + +void +MilitiaPersonalDataTable::DestroyMouseRegions( ) +{ + if ( !IsInit( ) ) + return; + + if ( mButtonFireCreated ) + { + RemoveButton( mButtonFire ); + mButtonFireCreated = FALSE; + } + + BaseTable::DestroyMouseRegions( ); +} + +///////////////////////////////////////////// +MilitiaPersonalInventoryTable::MilitiaPersonalInventoryTable( ) +: TestPanel( ) +{ +} + +void +MilitiaPersonalInventoryTable::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetColorLine( Get16BPPColor( FROMRGB( 128, 128, 128 ) ) ); + SetColorLineShadow( Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + SetColorMarked( Get16BPPColor( FROMRGB( 200, 169, 87 ) ) ); + SetColorHighLight( Get16BPPColor( FROMRGB( 235, 222, 171 ) ) ); + + TestPanel::Init( sX, sY, sX_End, sY_End ); +} + +void +MilitiaPersonalInventoryTable::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); +} + +void +MilitiaPersonalInventoryTable::Destroy( ) +{ + if ( !IsInit( ) ) + return; + + TestPanel::Destroy( ); +} + +void +MilitiaPersonalInventoryTable::Display( ) +{ + if ( !IsInit( ) ) + return; + + DestroyMouseRegions( ); + + SetFontShadow( NO_SHADOW ); + + CHAR16 sText[800]; + swprintf( sText, L"" ); + UINT16 usPosX = GetX( ) + 10; + UINT16 usPosY = GetY( ) + 10; + + SOLDIERTYPE* pSoldier = GetUsedSoldierToIndividualMilitia( mMilitiaId ); + + if ( pSoldier ) + { + SetFontShadow( DEFAULT_SHADOW ); + + for ( UINT8 pocketIndex = HELMETPOS; pocketIndex < NUM_INV_SLOTS; ++pocketIndex ) + { + OBJECTTYPE* pObj = &(pSoldier)->inv[pocketIndex]; + + if ( pObj == NULL || pObj->ubNumberOfObjects == NOTHING || pObj->usItem == NOTHING ) + continue; + + ColorFillVideoSurfaceArea( FRAME_BUFFER, usPosX, usPosY, usPosX + 60, usPosY + 23, Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + + // do not use the pSoldier-pointer, as the displays look ugly (and some images might not be initialised here) + INVRenderItem( FRAME_BUFFER, NULL, pObj, usPosX, usPosY, 60, 23, DIRTYLEVEL2, NULL, (UINT8)0, FALSE, 0, 0 ); + + usPosY += 25; + + if ( usPosY > GetY() + GetHeight() - 27 ) + { + usPosX += 62; + usPosY = GetY( ) + 10; + } + } + } + else + { + MILITIA militia; + if( GetMilitia( mMilitiaId, &militia ) ) + { + swprintf( sText, szIdividualMilitiaWebsiteText[16], militia.GetName( ) ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, 2, FONT_MCOLOR_BLACK, FALSE, 0 ); + usPosY += 20; + } + } + + SetFontShadow( NO_SHADOW ); + + BaseTable::Display( ); +} + +void +MilitiaPersonalInventoryTable::SetRefresh( ) +{ + gfMilitiaWebsiteMainRedraw = TRUE; +} + +void +MilitiaPersonalInventoryTable::DestroyMouseRegions( ) +{ + if ( !IsInit( ) ) + return; + + BaseTable::DestroyMouseRegions( ); +} + +///// +extern void GetFaceData( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ); +//extern void ItemImage( UINT32 usItem, UINT32& arImageLib, UINT16& arImage ); +extern STR16 ItemLongNamegetter( UINT32 aNum ); + +MilitiaInidividualMainWidget::MilitiaInidividualMainWidget( ) +: TestPanel( ), + mMilitiaId( 0 ) +{ +} + +void +MilitiaInidividualMainWidget::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + SetColorLine( Get16BPPColor( FROMRGB( 128, 128, 128 ) ) ); + SetColorLineShadow( Get16BPPColor( FROMRGB( 255, 255, 255 ) ) ); + SetColorMarked( Get16BPPColor( FROMRGB( 200, 169, 87 ) ) ); + SetColorHighLight( Get16BPPColor( FROMRGB( 235, 222, 171 ) ) ); + + BaseTable::Init( sX, sY, sX_End, sY_End ); +} + +void +MilitiaInidividualMainWidget::Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + Destroy( ); + + Init( sX, sY, sX_End, sY_End ); + + TabBoxTemplate<1>::getInstance( ).Create( sX, sY + 30, sX_End, sY_End ); + + mPersonalDataPanel.Create( sX, sY + 50, sX_End, sY_End ); + mPersonalDataPanel.SetName( szIdividualMilitiaWebsiteText[9] ); + mPersonalData.Create( sX, sY + 50, sX_End, sY_End ); + mPersonalDataPanel.RegisterMemberThingy( &mPersonalData ); + + mServiceRecordPanel.Create( sX, sY + 50, sX_End, sY_End ); + mServiceRecordPanel.SetName( szIdividualMilitiaWebsiteText[10] ); + mServiceRecord.Create( sX, sY + 50, sX_End, sY_End ); + mServiceRecordPanel.RegisterMemberThingy( &mServiceRecord ); + + mItemPanel.Create( sX, sY + 50, sX_End, sY_End ); + mItemPanel.SetName( szIdividualMilitiaWebsiteText[11] ); + mItemsTable.Create( sX, sY + 50, sX_End, sY_End ); + mItemPanel.RegisterMemberThingy( &mItemsTable ); + + TabBoxTemplate<1>::getInstance( ).RegisterMemberThingy( &mPersonalDataPanel ); + TabBoxTemplate<1>::getInstance( ).RegisterMemberThingy( &mServiceRecordPanel ); + TabBoxTemplate<1>::getInstance( ).RegisterMemberThingy( &mItemPanel ); + + RegisterMemberThingy( &TabBoxTemplate<1>::getInstance( ) ); +} + +template<> void TabBoxTemplate<1>::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; } + +void +MilitiaInidividualMainWidget::Destroy( ) +{ + if ( !IsInit( ) ) + return; + + BaseTable::Destroy( ); +} + +void +MilitiaInidividualMainWidget::Display( ) +{ + if ( !IsInit( ) ) + return; + + DestroyMouseRegions( ); + + TestPanel::Display( ); + + SetFontShadow( NO_SHADOW ); + + MILITIA militia; + if ( GetMilitia( mMilitiaId, &militia ) ) + { + CHAR16 sText[800]; + swprintf( sText, L"" ); + UINT16 usPosX = GetX( ); + UINT16 usPosY = GetY( ); + + // face + UINT32 imagelib = 0; + UINT16 imageid = 0; + GetFaceData( mMilitiaId, imagelib, imageid ); + + BltVideoObjectFromIndex( FRAME_BUFFER, imagelib, imageid, usPosX + 4, usPosY + 4, VO_BLT_SRCTRANSPARENCY, NULL ); + + usPosX += 44; + usPosY += 8; + + // name + swprintf( sText, L"%s - %s", militia.GetName( ), militia.GetSector( ) ); + DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, 2, FONT_MCOLOR_BLACK, FALSE, 0 ); + + usPosX += 7 * wcslen( sText ); + + /*// gun + ItemImage( mMilitiaId, imagelib, imageid ); + BltVideoObjectFromIndex( FRAME_BUFFER, imagelib, imageid, usPosX + 4, usPosY - 3, VO_BLT_SRCTRANSPARENCY, NULL ); + + usPosX += 90;*/ + + //swprintf( sText, ItemLongNamegetter( mMilitiaId ) ); + //DrawTextToScreen( sText, usPosX, usPosY + 5, GetWidth( ), FONT12ARIAL, 2, FONT_MCOLOR_BLACK, FALSE, 0 ); + + //usPosX += 7 * wcslen( sText ); + usPosY += 30; + } + + mCloseImage = LoadButtonImage( "INTERFACE\\skilltree.sti", 71, 71, 71, 71, 71 ); + + // save changes + mCloseButton = QuickCreateButton( mCloseImage, + GetX( ) + GetWidth( ) - 26, GetY( ) + 4, + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + MSYS_NO_CALLBACK, + CloseButtonCallback ); + mCloseButtonDefined = TRUE; +} + +void +MilitiaInidividualMainWidget::SetRefresh( ) +{ + gfMilitiaWebsiteMainRedraw = TRUE; +} + +void +MilitiaInidividualMainWidget::DestroyMouseRegions( ) +{ + if ( mCloseButtonDefined ) + { + RemoveButton( mCloseButton ); + UnloadButtonImage( mCloseImage ); + + mCloseButtonDefined = FALSE; + } + + TestPanel::DestroyMouseRegions( ); +} + +extern BOOLEAN gfMilitiaDetailsOpen; + +void +MilitiaInidividualMainWidget::CloseButtonCallback( GUI_BUTTON * btn, INT32 iReason ) +{ + if ( iReason & MSYS_CALLBACK_REASON_INIT ) + { + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + militiaindividualmainwidget.SetMouseRegionActive( FALSE ); + militiaindividualmainwidget.DestroyMouseRegions( ); + + TestTableTemplate<3>::getInstance( ).SetMouseRegionActive( TRUE ); + + gfMilitiaDetailsOpen = FALSE; + + TestTableTemplate<3>::getInstance( ).SetRefresh( ); + + militiaindividualmainwidget.SetRefresh( ); + } + else if ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + } +} + +MilitiaInidividualMainWidget militiaindividualmainwidget; diff --git a/Laptop/MilitiaInterface.h b/Laptop/MilitiaInterface.h new file mode 100644 index 00000000..2eaa8820 --- /dev/null +++ b/Laptop/MilitiaInterface.h @@ -0,0 +1,233 @@ +#ifndef __MILITIAINTERFACE_H +#define __MILITIAINTERFACE_H + +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#include "BaseTable.h" +#include "MilitiaIndividual.h" + +extern UINT32 gusCurrentMilitia; + +// the data of an individual militia is displayed on its own widget. There are several parts: +// - face, name, traits and gun are displayed on top. This data is also displayed for each militia in a list +// - Below that there is a tabbox with 3 entries: +// - On the service record, we see their current body, stats & health and their history - which battles they took part in, and where and when they were recruited +// Clicking on a history item links us to to campaign history. The history items are in a table. +// - The skilltree is a separate tab. Here we can choose traits and promote militia, provided they have sufficient experience (currently not used) +// - The inventory is a separate tab as well. + +class MilitiaServiceRecordTable : public BaseTable +{ +public: + MilitiaServiceRecordTable( ); + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + /* + * Sometimes a refresh inside the class isn't enough - best example is closing the dropdown area, after which the previous background cannot be restored by this class. + * In that case, the background and this class has to be redrawn. + * This function is called internally whenever such a refresh is necessary. + * It has to be defined in derived classes and should notify your dialogues to redraw at appropriate times + * + * This function has to be implemented! + */ + virtual void SetRefresh( ); + + void SetId( UINT32 usId ) { mMilitiaId = usId; } + +private: + // declare but don't define + MilitiaServiceRecordTable( MilitiaServiceRecordTable const& ); + void operator=(MilitiaServiceRecordTable const&); + +private: + UINT32 mMilitiaId; // the id of the militia whose data we display +}; + +class MilitiaPersonalDataTable : public TestPanel +{ +public: + MilitiaPersonalDataTable( ); + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + /* + * Sometimes a refresh inside the class isn't enough - best example is closing the dropdown area, after which the previous background cannot be restored by this class. + * In that case, the background and this class has to be redrawn. + * This function is called internally whenever such a refresh is necessary. + * It has to be defined in derived classes and should notify your dialogues to redraw at appropriate times + * + * This function has to be implemented! + */ + virtual void SetRefresh( ); + + virtual void DestroyMouseRegions( ); + + void SetId( UINT32 usId ) { mMilitiaId = usId; } + +private: + // declare but don't define + MilitiaPersonalDataTable( MilitiaPersonalDataTable const& ); + void operator=(MilitiaPersonalDataTable const&); + +private: + UINT32 mMilitiaId; // the id of the militia whose data we display + + INT32 mButtonFire; + BOOLEAN mButtonFireCreated; +}; + +class MilitiaPersonalInventoryTable : public TestPanel +{ +public: + MilitiaPersonalInventoryTable( ); + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + /* + * Sometimes a refresh inside the class isn't enough - best example is closing the dropdown area, after which the previous background cannot be restored by this class. + * In that case, the background and this class has to be redrawn. + * This function is called internally whenever such a refresh is necessary. + * It has to be defined in derived classes and should notify your dialogues to redraw at appropriate times + * + * This function has to be implemented! + */ + virtual void SetRefresh( ); + + virtual void DestroyMouseRegions( ); + + void SetId( UINT32 usId ) { mMilitiaId = usId; } + +private: + // declare but don't define + MilitiaPersonalInventoryTable( MilitiaPersonalInventoryTable const& ); + void operator=(MilitiaPersonalInventoryTable const&); + +private: + UINT32 mMilitiaId; // the id of the militia whose data we display +}; + +class MilitiaInidividualMainWidget : public TestPanel +{ +public: + MilitiaInidividualMainWidget( ); + + /* + * Initialise variables. Called after each creation which allows moving a dropdown + */ + virtual void Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Create a Dropdown with upper left coordinates + */ + virtual void Create( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ); + + /* + * Destroy Dropdown, for example once a website isn't displayed anymore + */ + virtual void Destroy( ); + + /* + * Display DropDownBase. Use this when refreshing + */ + virtual void Display( ); + + /* + * Sometimes a refresh inside the class isn't enough - best example is closing the dropdown area, after which the previous background cannot be restored by this class. + * In that case, the background and this class has to be redrawn. + * This function is called internally whenever such a refresh is necessary. + * It has to be defined in derived classes and should notify your dialogues to redraw at appropriate times + * + * This function has to be implemented! + */ + virtual void SetRefresh( ); + + virtual void DestroyMouseRegions( ); + + virtual void SetMouseRegionActive( BOOLEAN aVal ) { TabBoxTemplate<1>::getInstance( ).SetMouseRegionActive( aVal ); } + + void SetId( UINT32 usId ) { mMilitiaId = usId; gusCurrentMilitia = usId; mPersonalData.SetId( usId ); mServiceRecord.SetId( usId ); mItemsTable.SetId( usId ); } + + static void CloseButtonCallback( GUI_BUTTON * btn, INT32 iReason ); + +private: + // declare but don't define + MilitiaInidividualMainWidget( MilitiaInidividualMainWidget const& ); + void operator=(MilitiaInidividualMainWidget const&); + +private: + UINT32 mMilitiaId; // the id of the militia whose data we display + + TestPanel mPersonalDataPanel; + MilitiaPersonalDataTable mPersonalData; + TestPanel mServiceRecordPanel; + MilitiaServiceRecordTable mServiceRecord; + TestPanel mItemPanel; + MilitiaPersonalInventoryTable mItemsTable; + + // close buttons + BOOLEAN mCloseButtonDefined; + INT32 mCloseButton; + UINT32 mCloseImage; +}; + +extern MilitiaInidividualMainWidget militiaindividualmainwidget; + +#endif diff --git a/Laptop/MilitiaWebsite.cpp b/Laptop/MilitiaWebsite.cpp new file mode 100644 index 00000000..c1bf0f18 --- /dev/null +++ b/Laptop/MilitiaWebsite.cpp @@ -0,0 +1,858 @@ +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#ifdef PRECOMPILEDHEADERS +#include "Laptop All.h" +#else +#include "laptop.h" +#include "insurance.h" +#include "insurance Contract.h" +#include "WCheck.h" +#include "Utilities.h" +#include "WordWrap.h" +#include "Cursors.h" +#include "Line.h" +#include "Insurance Text.h" +#include "Encrypted File.h" +#include "Text.h" +#include "Multi Language Graphic Utils.h" +#include "MilitiaWebsite.h" +#include "random.h" +#include "Interface.h" +#include "Soldier Add.h" +#include "Soldier Profile.h" +#include "DropDown.h" +#include "Overhead.h" +#include "Map Screen Interface.h" +#include "Quests.h" +#include "finances.h" +#include "Game Clock.h" +#include "Animation Data.h" + +#include "SaveLoadGame.h" +#include "GameVersion.h" +#include "message.h" +#include "Game Event Hook.h" +#include "militia control.h" +#include "Town Militia.h" +#include "Strategic Town Loyalty.h" + +#include "BaseTable.h" +#include "Soldier Profile.h" +#include "Auto Resolve.h" +#include "MilitiaIndividual.h" +#include "Animation Data.h" +#include "MilitiaInterface.h" +#include "Interface Items.h" +#include "InterfaceItemImages.h" +#include "CampaignStats.h" +#endif + + +#define MERCOMP_FONT_COLOR 2 +#define CAMPHIS_FONT_COLOR_RED FONT_MCOLOR_RED +#define CAMPHIS_FONT_BIG FONT14ARIAL +#define CAMPHIS_FONT_MED FONT12ARIAL +#define CAMPHIS_FONT_SMALL FONT10ARIAL + +#define MERCOMP_FONT_SHADOW FONT_MCOLOR_WHITE + +#define CAMPAIGN_HISTORY_LINK_START_X LAPTOP_SCREEN_UL_X +#define CAMPAIGN_HISTORY_LINK_START_Y LAPTOP_SCREEN_WEB_UL_Y + 5 +#define CAMPAIGN_HISTORY_LINK_TEXT_WIDTH 107 +#define CAMPAIGN_HISTORY_LINK_STEP_Y 14 + +#define BACKGROUND_WIDTH 125 +#define CAMPAIGN_HISTORY_BACKGROUND_HEIGHT 100 + +#define CAMPAIGN_HISTORY_BIG_TITLE_X 115 + LAPTOP_SCREEN_UL_X +#define CAMPAIGN_HISTORY_BIG_TITLE_Y 10 + LAPTOP_SCREEN_WEB_UL_Y + +#define CAMPAIGN_HISTORY_SUBTITLE_X CAMPAIGN_HISTORY_BIG_TITLE_X +#define CAMPAIGN_HISTORY_SUBTITLE_Y CAMPAIGN_HISTORY_BIG_TITLE_Y + 20 + +#define NUM_LINKS 3 +#define MCA_START_CONTENT_Y (LAPTOP_SCREEN_WEB_UL_Y + NUM_LINKS * 17) + + +extern UINT32 guiInsuranceBackGround; +extern UINT32 guiInsuranceSmallTitleImage; +extern UINT32 guiInsuranceBigRedLineImage; +extern UINT32 guiMercCompareBulletImage; +extern UINT32 guiMercCompareLogoImage; + +extern UINT32 gusMostImportantPage; + +//link to the various pages +MOUSE_REGION gLinkRegion_MilitiaWebsite[NUM_LINKS]; + +UINT32 gARFacesLib = 0; + +void SelectLinkRegionCallBack_MilitiaWebsite( MOUSE_REGION * pRegion, INT32 iReason ) +{ + if ( iReason & MSYS_CALLBACK_REASON_INIT ) + { + } + else if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + UINT32 uiLink = MSYS_GetRegionUserData( pRegion, 0 ); + + if ( uiLink == 0 ) + guiCurrentLaptopMode = LAPTOP_MODE_MILITIAROSTER_MAIN; + else if ( uiLink == 1 ) + guiCurrentLaptopMode = LAPTOP_MODE_MILITIAROSTER_ABOUT; + else if ( uiLink == 2 ) + guiCurrentLaptopMode = LAPTOP_MODE_BROKEN_LINK; + } + else if ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + } +} + +void InitDefaults_MilitiaWebsite( ) +{ + VOBJECT_DESC VObjectDesc; + + // load the Insurance bullet graphic and add it + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "LAPTOP\\bullet.sti", VObjectDesc.ImageFile ); + CHECKV( AddVideoObject( &VObjectDesc, &guiMercCompareBulletImage ) ); + + // load the Flower Account Box graphic and add it + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "LAPTOP\\BackGroundTile.sti", VObjectDesc.ImageFile ); + CHECKV( AddVideoObject( &VObjectDesc, &guiInsuranceBackGround ) ); + + // load the red bar on the side of the page and add it + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "LAPTOP\\LargeBar.sti", VObjectDesc.ImageFile ); + CHECKV( AddVideoObject( &VObjectDesc, &guiInsuranceBigRedLineImage ) ); + + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "LAPTOP\\PressLogos.sti", VObjectDesc.ImageFile ); + CHECKV( AddVideoObject( &VObjectDesc, &guiMercCompareLogoImage ) ); + + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "Interface\\SmFaces.sti", VObjectDesc.ImageFile ); + CHECKV( AddVideoObject( &VObjectDesc, &gARFacesLib ) ); + + UINT16 usPosX = CAMPAIGN_HISTORY_LINK_START_X; + UINT16 usPosY = CAMPAIGN_HISTORY_LINK_START_Y; + for ( int i = 0; i > GetMilitiaSectorDropVector( ) +{ + std::vector > sectorvector; + + // 'Everywhere' is always an option + sectorvector.push_back( std::make_pair( -1, szIdividualMilitiaWebsiteFilterText_Sector[0] ) ); + + std::set sectorset; + + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != gIndividualMilitiaVector.end( ); ++it ) + { + sectorset.insert( (*it).sector ); + } + + for ( std::set::iterator it = sectorset.begin( ); it != sectorset.end(); ++it ) + { + GetShortSectorString( SECTORX( (*it) ), SECTORY( (*it) ), gInidividualMilitiaWebsiteSectorNamesstr[(*it)] ); + + sectorvector.push_back( std::make_pair( (INT16)(*it), gInidividualMilitiaWebsiteSectorNamesstr[(*it)] ) ); + } + + return sectorvector; +} + +// later on, make this an array with a vector for each sector +std::vector gIndividualMilitiaFilteredIdsVector; + +void FilterIndividualMilitia( InidivualMilitiaLifeState aLifeState, InidivualMilitiaRankState aRankState, InidivualMilitiaOriginState aOriginState, INT16 aSector ) +{ + gIndividualMilitiaFilteredIdsVector.clear(); + + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( ( aLifeState == IMLS_DEAD && !((*it).flagmask & MILITIAFLAG_DEAD) ) + || (aLifeState == IMLS_ALIVE && ((*it).flagmask & MILITIAFLAG_DEAD)) ) + continue; + + if ( (aLifeState == IMLS_FIRED && !((*it).flagmask & MILITIAFLAG_FIRED)) + || (aLifeState != IMLS_FIRED && aLifeState != IMLS_ALL && ((*it).flagmask & MILITIAFLAG_FIRED)) ) + continue; + + if ( (aRankState == IMRS_GREEN && (*it).soldierclass != SOLDIER_CLASS_GREEN_MILITIA ) || + (aRankState == IMRS_REGULAR && (*it).soldierclass != SOLDIER_CLASS_REG_MILITIA) || + (aRankState == IMRS_ELITE && (*it).soldierclass != SOLDIER_CLASS_ELITE_MILITIA) ) + continue; + + if ( (aOriginState == IMOS_ARULCO && (*it).origin != MO_ARULCO) || + (aOriginState == IMOS_PMC && (*it).origin != MO_PMC) || + (aOriginState == IMOS_DEFECTOR && (*it).origin != MO_DEFECTOR) ) + continue; + + if ( aSector > 0 && (UINT8)aSector != (*it).sector ) + continue; + + gIndividualMilitiaFilteredIdsVector.push_back( (*it).id ); + } +} + +// on dropdown change, redraw and redo filter selection +template<> void DropDownTemplate::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; gfMilitiaWebsiteRedoFilters = TRUE; } +template<> void DropDownTemplate::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; gfMilitiaWebsiteRedoFilters = TRUE; } +template<> void DropDownTemplate::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; gfMilitiaWebsiteRedoFilters = TRUE; } +template<> void DropDownTemplate::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; gfMilitiaWebsiteRedoFilters = TRUE; } + +BOOLEAN EnterMilitiaWebsiteMain( ) +{ + InitDefaults_MilitiaWebsite( ); + + if ( !gGameExternalOptions.fIndividualMilitia ) + return TRUE; + + // we add several filter dropdowns here (otherwise this list will become tedious to use later on when there are hundreds of militia) + { + UINT16 usDropX = LAPTOP_SCREEN_UL_X + 4; + { + std::vector > filtervector; + filtervector.push_back( std::make_pair( (INT16)IMLS_ALL, szIdividualMilitiaWebsiteFilterText_Dead[0] ) ); + filtervector.push_back( std::make_pair( (INT16)IMLS_DEAD, szIdividualMilitiaWebsiteFilterText_Dead[1] ) ); + filtervector.push_back( std::make_pair( (INT16)IMLS_ALIVE, szIdividualMilitiaWebsiteFilterText_Dead[2] ) ); + filtervector.push_back( std::make_pair( (INT16)IMLS_FIRED, szIdividualMilitiaWebsiteFilterText_Dead[3] ) ); + + if ( !filtervector.empty( ) ) + { + DropDownTemplate::getInstance( ).SetEntries( filtervector ); + DropDownTemplate::getInstance().SetSelectedEntryKey( IMLS_ALIVE ); + DropDownTemplate::getInstance( ).Create( usDropX, MCA_START_CONTENT_Y + 4 ); + usDropX = DropDownTemplate::getInstance( ).GetLastX( ) + 10; + } + else + { + DropDownTemplate::getInstance().ClearEntries(); + } + } + + { + std::vector > filtervector; + filtervector.push_back( std::make_pair( (INT16)IMRS_ALL, szIdividualMilitiaWebsiteFilterText_Rank[0] ) ); + filtervector.push_back( std::make_pair( (INT16)IMRS_GREEN, szIdividualMilitiaWebsiteFilterText_Rank[1] ) ); + filtervector.push_back( std::make_pair( (INT16)IMRS_REGULAR, szIdividualMilitiaWebsiteFilterText_Rank[2] ) ); + filtervector.push_back( std::make_pair( (INT16)IMRS_ELITE, szIdividualMilitiaWebsiteFilterText_Rank[3] ) ); + + if ( !filtervector.empty( ) ) + { + DropDownTemplate::getInstance( ).SetEntries( filtervector ); + DropDownTemplate::getInstance( ).SetSelectedEntryKey( IMRS_ALL ); + DropDownTemplate::getInstance( ).Create( usDropX, MCA_START_CONTENT_Y + 4 ); + usDropX = DropDownTemplate::getInstance( ).GetLastX( ) + 10; + } + else + { + DropDownTemplate::getInstance( ).ClearEntries( ); + } + } + + { + std::vector > filtervector; + filtervector.push_back( std::make_pair( (INT16)IMOS_ALL, szIdividualMilitiaWebsiteFilterText_Origin[0] ) ); + filtervector.push_back( std::make_pair( (INT16)IMOS_ARULCO, szIdividualMilitiaWebsiteFilterText_Origin[1] ) ); + filtervector.push_back( std::make_pair( (INT16)IMOS_PMC, szIdividualMilitiaWebsiteFilterText_Origin[2] ) ); + filtervector.push_back( std::make_pair( (INT16)IMOS_DEFECTOR, szIdividualMilitiaWebsiteFilterText_Origin[3] ) ); + + if ( !filtervector.empty( ) ) + { + DropDownTemplate::getInstance( ).SetEntries( filtervector ); + DropDownTemplate::getInstance( ).SetSelectedEntryKey( IMOS_ALL ); + DropDownTemplate::getInstance( ).Create( usDropX, MCA_START_CONTENT_Y + 4 ); + usDropX = DropDownTemplate::getInstance( ).GetLastX( ) + 10; + } + else + { + DropDownTemplate::getInstance( ).ClearEntries( ); + } + } + + { + std::vector > filtervector = GetMilitiaSectorDropVector(); + + if ( !filtervector.empty( ) ) + { + DropDownTemplate::getInstance( ).SetEntries( filtervector ); + DropDownTemplate::getInstance( ).SetSelectedEntryKey( -1 ); + DropDownTemplate::getInstance( ).Create( usDropX, MCA_START_CONTENT_Y + 4 ); + usDropX = DropDownTemplate::getInstance( ).GetLastX( ) + 10; + } + else + { + DropDownTemplate::getInstance( ).ClearEntries( ); + } + } + } + + INT16 key_dead = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + INT16 key_rank = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + INT16 key_origin = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + INT16 key_sector = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + + // as we might filter out militia, we use a vector that contains all currently used militia IDs, and access data via that + FilterIndividualMilitia( (InidivualMilitiaLifeState)key_dead, (InidivualMilitiaRankState)key_rank, (InidivualMilitiaOriginState)key_origin, key_sector ); + + gTestPanel3.Create( LAPTOP_SCREEN_UL_X, MCA_START_CONTENT_Y, LAPTOP_SCREEN_LR_X, LAPTOP_SCREEN_WEB_LR_Y ); + + TestTableTemplate<3>::getInstance( ).Create( LAPTOP_SCREEN_UL_X + 4, MCA_START_CONTENT_Y + 44, LAPTOP_SCREEN_LR_X - 4, LAPTOP_SCREEN_WEB_LR_Y - 4 ); + + gTestPanel3.RegisterMemberThingy( &TestTableTemplate<3>::getInstance( ) ); + + // militia details (opens upon interacting with militia list) + militiaindividualmainwidget.Create( LAPTOP_SCREEN_UL_X + 50, MCA_START_CONTENT_Y + 40, LAPTOP_SCREEN_LR_X, LAPTOP_SCREEN_WEB_LR_Y ); + + gfMilitiaDetailsOpen = FALSE; + + militiaindividualmainwidget.SetMouseRegionActive( FALSE ); + TestTableTemplate<3>::getInstance( ).SetMouseRegionActive( TRUE ); + + return(TRUE); +} + +void ExitMilitiaWebsiteMain( ) +{ + militiaindividualmainwidget.Destroy( ); + + DropDownTemplate::getInstance( ).Destroy( ); + DropDownTemplate::getInstance( ).Destroy( ); + DropDownTemplate::getInstance( ).Destroy( ); + DropDownTemplate::getInstance( ).Destroy( ); + + gTestPanel3.Destroy( ); + + RemoveDefaults_MilitiaWebsite( ); + + DeleteVideoObjectFromIndex( gARFacesLib ); +} + +void HandleMilitiaWebsiteMain( ) +{ + if ( gfMilitiaWebsiteMainRedraw ) + { + if ( gGameExternalOptions.fIndividualMilitia ) + { + if ( gfMilitiaWebsiteRedoFilters ) + { + INT16 key_dead = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + INT16 key_rank = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + INT16 key_origin = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + INT16 key_sector = DropDownTemplate::getInstance( ).GetSelectedEntryKey( ); + + // as we might filter out militia, we use a vector that contains all currently used militia IDs, and access data via that + FilterIndividualMilitia( (InidivualMilitiaLifeState)key_dead, (InidivualMilitiaRankState)key_rank, (InidivualMilitiaOriginState)key_origin, key_sector ); + + TestTableTemplate<3>::getInstance( ).Create( TestTableTemplate<3>::getInstance( ).GetX( ), TestTableTemplate<3>::getInstance( ).GetY( ), + TestTableTemplate<3>::getInstance( ).GetX( ) + TestTableTemplate<3>::getInstance( ).GetWidth( ), + TestTableTemplate<3>::getInstance( ).GetY( ) + TestTableTemplate<3>::getInstance( ).GetHeight() ); + + gfMilitiaWebsiteRedoFilters = FALSE; + } + } + + RenderMilitiaWebsiteMain(); + + gfMilitiaWebsiteMainRedraw = FALSE; + } +} + +void RenderMilitiaWebsiteMain( ) +{ + DisplayDefaults_MilitiaWebsite( ); + + SetFontShadow( NO_SHADOW ); + + if ( gGameExternalOptions.fIndividualMilitia ) + { + gTestPanel3.Display( ); + + DropDownTemplate::getInstance( ).Display( ); + DropDownTemplate::getInstance( ).Display( ); + DropDownTemplate::getInstance( ).Display( ); + DropDownTemplate::getInstance( ).Display( ); + + if ( gfMilitiaDetailsOpen ) + militiaindividualmainwidget.Display( ); + } + + SetFontShadow( NO_SHADOW ); + + MarkButtonsDirty( ); + RenderWWWProgramTitleBar( ); + InvalidateRegion( LAPTOP_SCREEN_UL_X, LAPTOP_SCREEN_WEB_UL_Y, LAPTOP_SCREEN_LR_X, LAPTOP_SCREEN_WEB_LR_Y ); +} +////////////////////////// MAIN PAGE //////////////////////////////// + + +BOOLEAN gMilitiaDetailOn = FALSE; + +STR16 Profilenamegetter( UINT32 aNum ) +{ + if ( aNum < gIndividualMilitiaFilteredIdsVector.size( ) ) + { + UINT32 militiaid = gIndividualMilitiaFilteredIdsVector[aNum]; + + MILITIA militia; + if ( GetMilitia( militiaid, &militia ) ) + return militia.GetName( ); + } + + return L"No entry found in database"; +} + +STR16 Sectornamegetter( UINT32 aNum ) +{ + if ( aNum < gIndividualMilitiaFilteredIdsVector.size( ) ) + { + UINT32 militiaid = gIndividualMilitiaFilteredIdsVector[aNum]; + + MILITIA militia; + if ( GetMilitia( militiaid, &militia ) ) + return militia.GetSector( ); + } + + return L"No entry found in database"; +} + +UINT32 gusCurrentMilitia = 0; +CHAR16 gMilitiaOperationText[100]; +STR16 OperationText( UINT32 aNum ) +{ + MILITIA militia; + if ( GetMilitia( gusCurrentMilitia, &militia ) && aNum < militia.history.size( ) ) + { + MILITIA_BATTLEREPORT battlereport = militia.history[aNum]; + + UINT32 day = battlereport.id / (1440); + UINT32 hours = (battlereport.id - day * 1440) / 60; + UINT32 minutes = battlereport.id - day * 1440 - hours * 60; + + STR16 operationame = GetIncidentName( battlereport.id ); + + if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_RECRUITED ) + { + STR16 originsectorstr = militia.GetOriginSector( ); + + if ( militia.origin == MO_PMC ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[8], day, hours, minutes, originsectorstr ); + else if ( militia.origin == MO_DEFECTOR ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[9], day, hours, minutes, originsectorstr ); + else + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[1], day, hours, minutes, originsectorstr ); + } + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_PROMOTED ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[2], day, hours, minutes ); + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_DIED ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[3], operationame ); + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_WOUNDED_COMA ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[6], operationame ); + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[7], operationame ); + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_WOUNDED_HEAVY ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[5], operationame ); + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_WOUNDED_SMALL ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[4], operationame ); + else if ( battlereport.flagmask & MILITIA_BATTLEREPORT_FLAG_FIRED ) + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[10], day, hours, minutes ); + else + swprintf( gMilitiaOperationText, szIndividualMilitiaBattleReportText[0], operationame ); + + return gMilitiaOperationText; + } + + return L"No entry found in database"; +} + +CHAR16 gItemLongNamegetterText[800]; +STR16 ItemLongNamegetter( UINT32 aNum ) +{ + if ( aNum < gIndividualMilitiaVector.size( ) ) + { + swprintf( gItemLongNamegetterText, L"%s", Item[aNum].szLongItemName ); + + return gItemLongNamegetterText; + } + + return L"unarmed"; +} + +void GetFaceData( UINT32 aMilitiaId, UINT32& arImageLib, UINT16& arImage ) +{ + arImageLib = gARFacesLib; + + arImage = HUMAN_SKULL; + + MILITIA militia; + if ( GetMilitia( aMilitiaId, &militia ) ) + { + if ( militia.flagmask & MILITIAFLAG_DEAD ) + arImage = HUMAN_SKULL; + else if ( militia.soldierclass == SOLDIER_CLASS_GREEN_MILITIA ) + arImage = militia.bodytype == REGFEMALE ? MILITIA1F_FACE : MILITIA1_FACE; + else if ( militia.soldierclass == SOLDIER_CLASS_REG_MILITIA ) + arImage = militia.bodytype == REGFEMALE ? MILITIA2F_FACE : MILITIA2_FACE; + else if ( militia.soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + arImage = militia.bodytype == REGFEMALE ? MILITIA3F_FACE : MILITIA3_FACE; + } +} + +void AutoResolveFaces( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ) +{ + arImageLib = gARFacesLib; + + arImage = HUMAN_SKULL; + + if ( aNum < gIndividualMilitiaFilteredIdsVector.size( ) ) + { + UINT32 militiaid = gIndividualMilitiaFilteredIdsVector[aNum]; + + GetFaceData( militiaid, arImageLib, arImage ); + } +} + +/*void ItemImage( UINT32 usItem, UINT32& arImageLib, UINT16& arImage ) +{ + if ( usItem != NONE ) + { + INVTYPE* pItem = &Item[usItem]; + + arImage = g_bUsePngItemImages ? 0 : pItem->ubGraphicNum; + + arImageLib = GetInterfaceGraphicForItem( pItem ); + } +}*/ + +/*extern UINT32 guTraitImages; + +void TraitImage1( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ) +{ + MILITIA& militia = GetMilitiabyNumber( aNum ); + + arImageLib = guTraitImages; + + arImage = 3 * militia.traits[0]; +} + +void TraitImage2( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ) +{ + MILITIA& militia = GetMilitiabyNumber( aNum ); + + arImageLib = guTraitImages; + + arImage = 3 * militia.traits[1]; +} + +void TraitImage3( UINT32 aNum, UINT32& arImageLib, UINT16& arImage ) +{ + MILITIA& militia = GetMilitiabyNumber( aNum ); + + arImageLib = guTraitImages; + + arImage = 3 * militia.traits[2]; +}*/ + +void MilitiaHealthBar( UINT32 aId, UINT16& arCol1, UINT16& arVal1, UINT16& arCol2, UINT16& arVal2, UINT16& arCol3, UINT16& arVal3, UINT16& arCol4, UINT16& arVal4 ) +{ + if ( aId < gIndividualMilitiaFilteredIdsVector.size() ) + { + UINT32 militiaid = gIndividualMilitiaFilteredIdsVector[aId]; + + MILITIA militia; + if ( GetMilitia( militiaid, &militia ) ) + { + // we create a 'health bar' here + // first a black background + arCol1 = Get16BPPColor( FROMRGB( 0, 0, 0 ) ); + arVal1 = 100; + + // we might be bleeding - draw the potential life here + arCol2 = Get16BPPColor( FROMRGB( 255, 255, 0 ) ); + arVal2 = 100;//militia.stats[HEALTH]; + + // we might be wounded - draw the potential life here + arCol3 = Get16BPPColor( FROMRGB( 255, 125, 125 ) ); + arVal3 = 100;//militia.stats[HEALTH]; + + // the red life part that we really have + arCol4 = Get16BPPColor( FROMRGB( 255, 0, 0 ) ); + arVal4 = UINT16( militia.healthratio ); + } + } +} + + +void callbackmilitialist( INT32 usId ) +{ + if ( usId < gIndividualMilitiaFilteredIdsVector.size() ) + { + UINT32 militiaid = gIndividualMilitiaFilteredIdsVector[usId]; + + militiaindividualmainwidget.SetMouseRegionActive( TRUE ); + militiaindividualmainwidget.SetId( (UINT32)militiaid ); + + TestTableTemplate<3>::getInstance( ).SetMouseRegionActive( FALSE ); + TestTableTemplate<3>::getInstance( ).DestroyMouseRegions( ); + + gfMilitiaDetailsOpen = TRUE; + + TestTableTemplate<3>::getInstance( ).SetRefresh( ); + } +} + +void callbackmilitiadetail( INT32 usId ) +{ + MILITIA militia; + if ( GetMilitia( gusCurrentMilitia, &militia ) && usId < militia.history.size( ) ) + { + MILITIA_BATTLEREPORT battlereport = militia.history[usId]; + + // if this is a valid ID of an incident, go to its website, otherwise, do nothing + INT32 incidentposition = GetPositionOfIncident( battlereport.id ); + if ( incidentposition >= 0 ) + { + militiaindividualmainwidget.SetMouseRegionActive( FALSE ); + militiaindividualmainwidget.DestroyMouseRegions( ); + + TestTableTemplate<3>::getInstance( ).SetMouseRegionActive( TRUE ); + TestTableTemplate<3>::getInstance( ).SetRefresh( ); + + guiCurrentLaptopMode = LAPTOP_MODE_CAMPAIGNHISTORY_MOSTIMPORTANT; + gusMostImportantPage = incidentposition; + } + } +} + +template<> void TestTableTemplate<1>::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; } +template<> void TestTableTemplate<2>::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; } +template<> void TestTableTemplate<3>::SetRefresh( ) { gfMilitiaWebsiteMainRedraw = TRUE; } + +template<> void TestTableTemplate<1>::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + ClearColumnDataProvider( ); + + ColumnDataProvider imagecol( L"" ); + imagecol.SetMethodImage( AutoResolveFaces ); + imagecol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + imagecol.SetRequiredHeigth( 27 ); + imagecol.SetCallBackType( ColumnDataProvider::CDP_MILITIADETAIL ); + + AddColumnDataProvider( imagecol ); + + TestTable::Init( sX, sY, sX_End, sY_End ); +} + +template<> void TestTableTemplate<2>::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + ClearColumnDataProvider( ); + + ColumnDataProvider namcol( szIdividualMilitiaWebsiteText[0] ); + namcol.SetMethodString( OperationText ); + + MILITIA militia; + if ( GetMilitia( gusCurrentMilitia, &militia ) ) + namcol.SetNumberOfEntries( militia.history.size( ) ); + else + namcol.SetNumberOfEntries( 0 ); + + namcol.SetCallBackType( ColumnDataProvider::CDP_MILITIADETAIL ); + + AddColumnDataProvider( namcol ); + + TestTable::Init( sX, sY, sX_End, sY_End ); +} + +template<> void TestTableTemplate<3>::Init( UINT16 sX, UINT16 sY, UINT16 sX_End, UINT16 sY_End ) +{ + ClearColumnDataProvider( ); + + if ( gGameExternalOptions.fIndividualMilitia_ManageHealth ) + { + ColumnDataProvider healthbarcol( L"" ); + healthbarcol.SetMethodStatusBar( MilitiaHealthBar ); + healthbarcol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + healthbarcol.SetRequiredHeigth( 29 ); + healthbarcol.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( healthbarcol ); + } + + // face + ColumnDataProvider imagecol( L"" ); + imagecol.SetMethodImage( AutoResolveFaces ); + imagecol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + imagecol.SetRequiredHeigth( 27 ); + imagecol.SetRequiredLength( 35 ); + imagecol.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( imagecol ); + + // name + ColumnDataProvider namcol( szIdividualMilitiaWebsiteText[12] ); + namcol.SetMethodString( Profilenamegetter ); + namcol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + namcol.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( namcol ); + + // sector + ColumnDataProvider sectorcol( szIdividualMilitiaWebsiteText[13] ); + sectorcol.SetMethodString( Sectornamegetter ); + sectorcol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + sectorcol.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( sectorcol ); + + /*// trait 1,2,3 + ColumnDataProvider traitcol1( L"" ); + traitcol1.SetMethodImage( TraitImage1 ); + traitcol1.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + traitcol1.SetRequiredHeigth( 20 ); + traitcol1.SetRequiredLength( 22 ); + traitcol1.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( traitcol1 ); + + ColumnDataProvider traitcol2( L"" ); + traitcol2.SetMethodImage( TraitImage2 ); + traitcol2.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + traitcol2.SetRequiredHeigth( 20 ); + traitcol2.SetRequiredLength( 26 ); + traitcol2.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( traitcol2 ); + + ColumnDataProvider traitcol3( L"" ); + traitcol3.SetMethodImage( TraitImage3 ); + traitcol3.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + traitcol3.SetRequiredHeigth( 20 ); + traitcol3.SetRequiredLength( 22 ); + traitcol3.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( traitcol3 );*/ + + /*// weapon + ColumnDataProvider itemimagecol( szIdividualMilitiaWebsiteText[14] ); + itemimagecol.SetMethodImage( ItemImage ); + itemimagecol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size( ) ); + itemimagecol.SetRequiredHeigth( 29 ); + itemimagecol.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( itemimagecol );*/ + + // name of weapon + /*ColumnDataProvider itemnamecol( L"" ); + itemnamecol.SetMethodString( ItemLongNamegetter ); + itemnamecol.SetNumberOfEntries( gIndividualMilitiaFilteredIdsVector.size() ); + itemnamecol.SetCallBackType( ColumnDataProvider::CDP_MILITIA_LIST ); + + AddColumnDataProvider( itemnamecol );*/ + + TestTable::Init( sX, sY, sX_End, sY_End ); +} \ No newline at end of file diff --git a/Laptop/MilitiaWebsite.h b/Laptop/MilitiaWebsite.h new file mode 100644 index 00000000..fef5bc1c --- /dev/null +++ b/Laptop/MilitiaWebsite.h @@ -0,0 +1,19 @@ +#ifndef __MILITIA_WEBSITE_H +#define __MILITIA_WEBSITE_H + +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +// main page (explains what this is all about) +BOOLEAN EnterMilitiaWebsiteMain( ); +void ExitMilitiaWebsiteMain( ); +void HandleMilitiaWebsiteMain( ); +void RenderMilitiaWebsiteMain( ); + +STR16 OperationText( UINT32 aNum ); + +extern std::vector gIndividualMilitiaFilteredIdsVector; + +#endif //__MILITIA_WEBSITE_H diff --git a/Laptop/PMC.cpp b/Laptop/PMC.cpp index 35a27616..d30dc574 100644 --- a/Laptop/PMC.cpp +++ b/Laptop/PMC.cpp @@ -38,10 +38,9 @@ #include "militia control.h" #include "Town Militia.h" #include "Strategic Town Loyalty.h" +#include "MilitiaIndividual.h" #endif -extern INT32 ReadFieldByField( HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount ); - std::vector gPMCHiringEvents; PMCGlobalData gPMCData; @@ -274,16 +273,24 @@ PMCPersonnelData pmcdata[2]; // how much does it cost to hire a militia from the PMC? UINT16 GetMilitiaCostPMC( UINT8 aSoldierClass ) { - switch ( aSoldierClass ) + // Flugente: if individual militia is on, the upfront payment is much smaller (a single day's wage). In exchange, we have to pay higher wages for these guys + if ( gGameExternalOptions.fIndividualMilitia ) { - case REGULAR_MILITIA: - return (4 * gGameExternalOptions.iMilitiaTrainingCost * gGameExternalOptions.iRegularCostModifier) / gGameExternalOptions.iTrainingSquadSize + 7 * gGameExternalOptions.usDailyCostTownRegular; - break; + return gMilitiaOriginData[MO_PMC].dailycost[aSoldierClass]; + } + else + { + switch ( aSoldierClass ) + { + case REGULAR_MILITIA: + return (4 * gGameExternalOptions.iMilitiaTrainingCost * gGameExternalOptions.iRegularCostModifier) / gGameExternalOptions.iTrainingSquadSize + 7 * gGameExternalOptions.usDailyCostTown[REGULAR_MILITIA]; + break; - case ELITE_MILITIA: - // elites are upgradded regulars, so we pay for the basic + specail training - return ((4 * gGameExternalOptions.iMilitiaTrainingCost * gGameExternalOptions.iRegularCostModifier) + (2 * gGameExternalOptions.iMilitiaTrainingCost * gGameExternalOptions.iVeteranCostModifier)) / gGameExternalOptions.iTrainingSquadSize + 7 * gGameExternalOptions.usDailyCostTownElite; - break; + case ELITE_MILITIA: + // elites are upgraded regulars, so we pay for the basic + special training + return ((4 * gGameExternalOptions.iMilitiaTrainingCost * gGameExternalOptions.iRegularCostModifier) + (2 * gGameExternalOptions.iMilitiaTrainingCost * gGameExternalOptions.iVeteranCostModifier)) / gGameExternalOptions.iTrainingSquadSize + 7 * gGameExternalOptions.usDailyCostTown[ELITE_MILITIA]; + break; + } } return 0; @@ -752,6 +759,13 @@ void HandlePMCArrival( UINT8 usId ) StrategicAddMilitiaToSector( SECTORX( (*it).usSectorToArrive ), SECTORY( (*it).usSectorToArrive ), REGULAR_MILITIA, (*it).usRegulars ); StrategicAddMilitiaToSector( SECTORX( (*it).usSectorToArrive ), SECTORY( (*it).usSectorToArrive ), ELITE_MILITIA, (*it).usVeterans ); + // Flugente: create individual militia + for ( int i = 0; i < (*it).usRegulars; ++i ) + CreateNewIndividualMilitia( REGULAR_MILITIA, MO_PMC, (*it).usSectorToArrive ); + + for ( int i = 0; i < (*it).usVeterans; ++i ) + CreateNewIndividualMilitia( ELITE_MILITIA, MO_PMC, (*it).usSectorToArrive ); + // if this is in the current sector, militia will be updated ResetMilitia( ); diff --git a/Laptop/email.cpp b/Laptop/email.cpp index 0f951b71..4c75e069 100644 --- a/Laptop/email.cpp +++ b/Laptop/email.cpp @@ -3735,6 +3735,11 @@ BOOLEAN HandleMailSpecialMessages( UINT16 usMessageId, INT32 *iResults, EmailPtr fReDrawScreenFlag = TRUE; break; + case(MILITIAROSTER_INTRO) : + SetBookMark( MILITIAROSTER_BOOKMARK ); + fReDrawScreenFlag = TRUE; + break; + #endif } diff --git a/Laptop/email.h b/Laptop/email.h index 0d33e3e7..ac2faeee 100644 --- a/Laptop/email.h +++ b/Laptop/email.h @@ -381,6 +381,9 @@ #define KINGPIN_BOUNTY_TARGET_GOTAWAY (KINGPIN_BOUNTY_BH_GOTTARGET + KINGPIN_BOUNTY_BH_GOTTARGET_LENGTH) #define KINGPIN_BOUNTY_TARGET_GOTAWAY_LENGTH 4 + #define MILITIAROSTER_INTRO (KINGPIN_BOUNTY_TARGET_GOTAWAY + KINGPIN_BOUNTY_TARGET_GOTAWAY_LENGTH) + #define MILITIAROSTER_INTRO_LENGTH 4 + #endif // WANNE: New 1.13 MERC merc mail text for level up that Speck sends diff --git a/Laptop/laptop.cpp b/Laptop/laptop.cpp index d1019d06..8b73f11a 100644 --- a/Laptop/laptop.cpp +++ b/Laptop/laptop.cpp @@ -90,6 +90,7 @@ #include "MercCompare.h" // added by Flugente #include "WHO.h" // added by Flugente #include "PMC.h" // added by Flugente + #include "MilitiaWebsite.h" // added by Flugente #endif #include "connect.h" @@ -1498,6 +1499,11 @@ void RenderLaptop() case LAPTOP_MODE_PMC_CONTRACT_INDIVIDUAL: RenderPMCContract( ); break; + + case LAPTOP_MODE_MILITIAROSTER_MAIN: + case LAPTOP_MODE_MILITIAROSTER_ABOUT: + RenderMilitiaWebsiteMain( ); + break; } if( guiCurrentLaptopMode >= LAPTOP_MODE_WWW ) @@ -1954,6 +1960,11 @@ void EnterNewLaptopMode() case LAPTOP_MODE_PMC_CONTRACT_INDIVIDUAL: EnterPMCContract( ); break; + + case LAPTOP_MODE_MILITIAROSTER_MAIN: + case LAPTOP_MODE_MILITIAROSTER_ABOUT: + EnterMilitiaWebsiteMain( ); + break; } // first time using webbrowser in this laptop session @@ -2227,6 +2238,11 @@ void HandleLapTopHandles() case LAPTOP_MODE_PMC_CONTRACT_INDIVIDUAL: HandlePMCContract( ); break; + + case LAPTOP_MODE_MILITIAROSTER_MAIN: + case LAPTOP_MODE_MILITIAROSTER_ABOUT: + HandleMilitiaWebsiteMain( ); + break; } } @@ -2809,6 +2825,11 @@ UINT32 ExitLaptopMode(UINT32 uiMode) case LAPTOP_MODE_PMC_CONTRACT_INDIVIDUAL: ExitPMCContract( ); break; + + case LAPTOP_MODE_MILITIAROSTER_MAIN: + case LAPTOP_MODE_MILITIAROSTER_ABOUT: + ExitMilitiaWebsiteMain(); + break; } if( ( uiMode != LAPTOP_MODE_NONE )&&( uiMode < LAPTOP_MODE_WWW ) ) @@ -4444,6 +4465,27 @@ if( (gubQuest[ QUEST_FIX_LAPTOP ] != QUESTINPROGRESS) || (gGameUBOptions.LaptopQ } } break; + + case MILITIAROSTER_BOOKMARK: + { + guiCurrentWWWMode = LAPTOP_MODE_MILITIAROSTER_MAIN; + guiCurrentLaptopMode = LAPTOP_MODE_MILITIAROSTER_MAIN; + + // do we have to have a World Wide Wait + if ( LaptopSaveInfo.fVisitedBookmarkAlready[MILITIAROSTER_BOOKMARK] == FALSE ) + { + // reset flag and set load pending flag + LaptopSaveInfo.fVisitedBookmarkAlready[MILITIAROSTER_BOOKMARK] = TRUE; + fLoadPendingFlag = TRUE; + } + else + { + // fast reload + fLoadPendingFlag = TRUE; + fFastLoadFlag = TRUE; + } + } + break; } #ifdef JA2UB diff --git a/Laptop/laptop.h b/Laptop/laptop.h index 595e7bb3..57a3deea 100644 --- a/Laptop/laptop.h +++ b/Laptop/laptop.h @@ -161,6 +161,10 @@ enum LAPTOP_MODE_PMC_MAIN, LAPTOP_MODE_PMC_CONTRACT_MILITIA, LAPTOP_MODE_PMC_CONTRACT_INDIVIDUAL, + + // Flugente: militia roster + LAPTOP_MODE_MILITIAROSTER_MAIN, + LAPTOP_MODE_MILITIAROSTER_ABOUT, LAPTOP_MODE_BOBBYR_SHIPMENTS, @@ -241,6 +245,7 @@ enum{ MERCCOMPARE_BOOKMARK, // added by Flugente WHO_BOOKMARK, // added by Flugente PMC_BOOKMARK, // added by Flugente + MILITIAROSTER_BOOKMARK, // added by Flugente TEXT_NUM_LAPTOP_BOOKMARKS }; diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index d273241c..ba85a136 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -117,6 +117,7 @@ #include "DynamicDialogue.h" // added by Flugente #include "PMC.h" // added by Flugente #include "ASD.h" // added by Flugente + #include "MilitiaIndividual.h" // added by Flugente #endif #include "BobbyR.h" @@ -524,8 +525,6 @@ extern EmailPtr pEmailList; extern UINT32 guiCurrentUniqueSoldierId; extern BOOLEAN gfHavePurchasedItemsFromTony; -INT32 ReadFieldByField(HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount); - ///////////////////////////////////////////////////// // // Function Prototypes @@ -1772,6 +1771,7 @@ BOOLEAN SOLDIERTYPE::Save(HWFILE hFile) } return TRUE; } + /*CHRISL: This function is designed to allow reading the save game file one field at a time. We currently save structures by saving a block of memory, but variables are stored in memory so that they fit neatly into a WORD resulting in the program automatically adding some padding. This padding is saved during the save game process and this function is designed to calculate where that padding is so that we can account for it during the load process. The @@ -2178,7 +2178,7 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) this->bAssignment = FACILITY_REPAIR; } numBytesRead = ReadFieldByField(hFile, &this->bScopeMode, sizeof(bScopeMode), sizeof(INT8), numBytesRead); - numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT8_1, sizeof(bUnusedINT8_1), sizeof(INT8), numBytesRead ); + numBytesRead = ReadFieldByField(hFile, &this->ubMilitiaAssists, sizeof(ubMilitiaAssists), sizeof(UINT8), numBytesRead ); numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT8_2, sizeof(bUnusedINT8_2), sizeof(INT8), numBytesRead ); numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT8_3, sizeof(bUnusedINT8_3), sizeof(INT8), numBytesRead ); numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT16_4, sizeof(bUnusedINT16_4), sizeof(INT16), numBytesRead ); @@ -2355,6 +2355,18 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) if ( guiCurrentSaveGameVersion >= SNITCH_TRAIT_EXTENDED ) { numBytesRead = ReadFieldByField(hFile, &this->usSoldierFlagMask2, sizeof(usSoldierFlagMask2), sizeof(UINT32), numBytesRead); + + if ( guiCurrentSaveGameVersion >= INDIVIDUAL_MILITIA ) + { + numBytesRead = ReadFieldByField( hFile, &this->usIndividualMilitiaID, sizeof(usIndividualMilitiaID), sizeof(UINT32), numBytesRead ); + } + else + { + this->usIndividualMilitiaID = 0; + + for ( int i = 0; i < sizeof(usIndividualMilitiaID); ++i ) + buffer++; + } } else { @@ -4405,6 +4417,13 @@ BOOLEAN SaveGame( int ubSaveGameID, STR16 pGameDesc ) goto FAILED_TO_SAVE; } + // Flugente: individual militia + if ( !SaveIndividualMilitiaData( hFile ) ) + { + ScreenMsg( FONT_MCOLOR_WHITE, MSG_ERROR, L"ERROR writing individual militia data" ); + goto FAILED_TO_SAVE; + } + //Close the saved game file FileClose( hFile ); @@ -6126,6 +6145,25 @@ BOOLEAN LoadSavedGame( int ubSavedGameID ) InitASD(); } + if ( guiCurrentSaveGameVersion >= INDIVIDUAL_MILITIA ) + { + uiRelEndPerc += 1; + SetRelativeStartAndEndPercentage( 0, uiRelStartPerc, uiRelEndPerc, L"Load individual militia data..." ); + RenderProgressBar( 0, 100 ); + uiRelStartPerc = uiRelEndPerc; + + if ( !LoadIndividualMilitiaData( hFile ) ) + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Individual militia data Load failed" ) ); + FileClose( hFile ); + return(FALSE); + } + } + else + { + InitIndividualMilitiaData(); + } + // //Close the saved game file // diff --git a/SaveLoadGame.h b/SaveLoadGame.h index 5f88a9c7..a1f9664a 100644 --- a/SaveLoadGame.h +++ b/SaveLoadGame.h @@ -101,4 +101,10 @@ void GetBestPossibleSectorXYZValues( INT16 *psSectorX, INT16 *psSectorY, INT8 *p extern UINT32 guiLastSaveGameNum; // The end turn auto save number (0 = Auto00.sav, 1 = Auto01.sav) INT8 GetNumberForAutoSave( BOOLEAN fLatestAutoSave ); +/*CHRISL: This function is designed to allow reading the save game file one field at a time. We currently save structures by saving a block of memory, +but variables are stored in memory so that they fit neatly into a WORD resulting in the program automatically adding some padding. This padding is saved +during the save game process and this function is designed to calculate where that padding is so that we can account for it during the load process. The +use of this function should allow changes to be made to various structures within the designated "POD", while still allowing for save game continuity.*/ +INT32 ReadFieldByField( HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount ); + #endif diff --git a/Strategic/ASD.cpp b/Strategic/ASD.cpp index 4c295329..d804cf49 100644 --- a/Strategic/ASD.cpp +++ b/Strategic/ASD.cpp @@ -55,9 +55,6 @@ #endif - -extern INT32 ReadFieldByField( HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount ); - extern INT32 giReinforcementPool; extern BOOLEAN gfTacticalDoHeliRun; diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index dfc3f855..627be481 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -71,6 +71,7 @@ #include "Queen Command.h" // added by Flugente #include "PMC.h" // added by Flugente #include "Drugs And Alcohol.h" // added by Flugente for DoesMercHaveDisability( ... ) + #include "MilitiaIndividual.h" // added by Flugente #endif #include #include @@ -2550,6 +2551,9 @@ void UpdateAssignments() } } + // Flugente: individual militia + HandleHourlyMilitiaHealing(); + // Flugente: handle militia command HandleMilitiaCommand(); @@ -6946,6 +6950,18 @@ void HandlePrisonerProcessingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) StrategicAddMilitiaToSector( sMapX, sMapY, ELITE_MILITIA, turnedmilitia[PRISONER_ELITE] + turnedmilitia[PRISONER_OFFICER] ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szPrisonerTextStr[STR_PRISONER_TURN_MILITIA], turnedmilitia[PRISONER_OFFICER], turnedmilitia[PRISONER_ELITE], turnedmilitia[PRISONER_REGULAR], turnedmilitia[PRISONER_ADMIN] ); + + // Flugente: create individual militia + for ( int i = 0; i < turnedmilitia[PRISONER_ADMIN]; ++i ) + CreateNewIndividualMilitia( GREEN_MILITIA, MO_DEFECTOR, SECTOR( sMapX, sMapY ) ); + + for ( int i = 0; i < turnedmilitia[PRISONER_REGULAR]; ++i ) + CreateNewIndividualMilitia( REGULAR_MILITIA, MO_DEFECTOR, SECTOR( sMapX, sMapY ) ); + + for ( int i = 0; i < turnedmilitia[PRISONER_ELITE] + turnedmilitia[PRISONER_OFFICER]; ++i ) + CreateNewIndividualMilitia( ELITE_MILITIA, MO_DEFECTOR, SECTOR( sMapX, sMapY ) ); + + AddStrategicEvent( EVENT_MILITIAROSTER_EMAIL, GetWorldTotalMin( ) + 60 * (1 + Random( 4 )), 0 ); } if ( volunteers ) diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 74aedbd2..70ec533a 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -71,6 +71,7 @@ #include "Bullets.h" // HEADROCK HAM 5, for use with Bullet Impact. #include "CampaignStats.h" // added by Flugente #include "DynamicDialogue.h" // added by Flugente + #include "MilitiaIndividual.h" // added by Flugente #endif #include "Reinforcement.h" @@ -268,33 +269,6 @@ enum OTHER_PANEL, }; -//generic face images -enum -{ - ADMIN_FACE, - TROOP_FACE, - ELITE_FACE, - TANK_FACE, - MILITIA1_FACE, - MILITIA2_FACE, - MILITIA3_FACE, - YM_CREATURE_FACE, - AM_CREATURE_FACE, - YF_CREATURE_FACE, - AF_CREATURE_FACE, - HUMAN_SKULL, - TANK_WRECK, - CREATURE_SKULL, - ADMINF_FACE, - TROOPF_FACE, - ELITEF_FACE, - MILITIA1F_FACE, - MILITIA2F_FACE, - MILITIA3F_FACE, - JEEP_FACE, - JEEP_WRECK, -}; - extern void CreateDestroyMapInvButton(); //Autoresolve sets this variable which defaults to -1 when not needed. @@ -446,7 +420,7 @@ void EliminateAllEnemies( UINT8 ubSectorX, UINT8 ubSectorY ) pGroup = gpGroupList; pSector = &SectorInfo[ SECTOR( ubSectorX, ubSectorY ) ]; - + // if we're doing this from the Pre-Battle interface, gpAR is NULL, and RemoveAutoResolveInterface(0 won't happen, so // we must process the enemies killed right here & give out loyalty bonuses as if the battle had been fought & won if( !gpAR ) @@ -616,7 +590,7 @@ void EnterAutoResolveMode( UINT8 ubSectorX, UINT8 ubSectorY ) CreateDestroyMapInvButton(); RenderButtons(); - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve1"); +DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve1"); // WDS - make number of mercenaries, etc. be configurable //Allocate memory for all the globals while we are in this mode. gpAR = (AUTORESOLVE_STRUCT*)MemAlloc( sizeof( AUTORESOLVE_STRUCT ) ); @@ -794,8 +768,8 @@ void AssociateEnemiesWithStrategicGroups() { if ( gpEnemies[i].uiFlags & CELL_TANK && ubNumTanks ) //is this soldier a tank? and we still have some tanks to add? (since there might not be a static tank in sector) { - gpEnemies[i].pSoldier->ubGroupID = 0; - gpEnemies[i].uiFlags |= CELL_ASSIGNED; + gpEnemies[ i ].pSoldier->ubGroupID = 0; + gpEnemies[ i ].uiFlags |= CELL_ASSIGNED; ubNumTanks--; } else if ( gpEnemies[i].uiFlags & CELL_JEEP && ubNumJeeps ) @@ -822,7 +796,7 @@ void AssociateEnemiesWithStrategicGroups() gpEnemies[ i ].uiFlags |= CELL_ASSIGNED; ubNumAdmins--; } - } + } ubNumAdmins = gpAR->ubAdmins - pSector->ubNumAdmins; ubNumTroops = gpAR->ubTroops - pSector->ubNumTroops; @@ -885,9 +859,9 @@ void AssociateEnemiesWithStrategicGroups() ubNumAdmins--; ubNumAdminsInGroup--; } + } } } - } pGroup = pGroup->next; } @@ -910,8 +884,8 @@ void AssociateEnemiesWithStrategicGroups() { if (ubNumTanks && ubNumTanksInGroup && gpEnemies[i].uiFlags & CELL_TANK) { - gpEnemies[i].pSoldier->ubGroupID = pGroup->ubGroupID; - gpEnemies[i].uiFlags |= CELL_ASSIGNED; + gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID; + gpEnemies[ i ].uiFlags |= CELL_ASSIGNED; ubNumTanks--; ubNumTanksInGroup--; } @@ -924,7 +898,7 @@ void AssociateEnemiesWithStrategicGroups() } else if (ubNumElites && ubNumElitesInGroup && gpEnemies[i].uiFlags & CELL_ELITE) { - gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID; + gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID; gpEnemies[ i ].uiFlags |= CELL_ASSIGNED; ubNumElites--; ubNumElitesInGroup--; @@ -943,12 +917,12 @@ void AssociateEnemiesWithStrategicGroups() ubNumAdmins--; ubNumAdminsInGroup--; } + } } } - } pGroup = pGroup->next; } - + // Set GroupID = 0 for the rest (that includes reinforcements) ubDirAmount = GetAdjacentSectors( pSectors, gpAR->ubSectorX, gpAR->ubSectorY ); @@ -970,10 +944,10 @@ void AssociateEnemiesWithStrategicGroups() { if ( gpEnemies[i].uiFlags & CELL_TANK && ubISNumTanks && ubNumTanks ) { - gpEnemies[i].pSoldier->ubGroupID = 0; - gpEnemies[i].uiFlags |= CELL_ASSIGNED; - gpEnemies[i].pSoldier->sSectorX = SECTORX( pSectors[ubCurrSI] ); - gpEnemies[i].pSoldier->sSectorY = SECTORY( pSectors[ubCurrSI] ); + gpEnemies[ i ].pSoldier->ubGroupID = 0; + gpEnemies[ i ].uiFlags |= CELL_ASSIGNED; + gpEnemies[ i ].pSoldier->sSectorX = SECTORX( pSectors[ ubCurrSI ] ); + gpEnemies[ i ].pSoldier->sSectorY = SECTORY( pSectors[ ubCurrSI ] ); ubISNumTanks--; ubNumTanks--; } @@ -1013,14 +987,14 @@ void AssociateEnemiesWithStrategicGroups() ubISNumAdmins--; ubNumAdmins--; } + } } } - } /*at this point, all enemies should have been assigned to their cell and group. If not, there is a bug around Because number and type of cells should be computed for the same composition of enemies as the one we see in this function, it should not happen though*/ AssertMsg( !(ubISNumAdmins & ubISNumTroops & ubISNumElites & ubISNumTanks & ubISNumJeeps), "Mapping between actual enemies and autoresolve cells is wrong." ); -} + } void CalculateSoldierCells( BOOLEAN fReset ) @@ -1129,9 +1103,9 @@ void CalculateSoldierCells( BOOLEAN fReset ) if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE ) { if ( index < gpAR->ubElites ) - gpEnemies[index].uiFlags = CELL_ELITE; + gpEnemies[ index ].uiFlags = CELL_ELITE; else if ( index < gpAR->ubElites + gpAR->ubTroops ) - gpEnemies[index].uiFlags = CELL_TROOP; + gpEnemies[ index ].uiFlags = CELL_TROOP; else if ( index < gpAR->ubElites + gpAR->ubTroops + gpAR->ubAdmins ) gpEnemies[index].uiFlags = CELL_ADMIN; else if ( index < gpAR->ubElites + gpAR->ubTroops + gpAR->ubAdmins + gpAR->ubTanks ) @@ -2202,7 +2176,7 @@ void CreateAutoResolveInterface() } else { - gpEnemies[index].usIndex = TROOP_FACE; + gpEnemies[index].usIndex = TROOP_FACE; } gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; @@ -2218,7 +2192,7 @@ void CreateAutoResolveInterface() } else { - gpEnemies[index].usIndex = ADMIN_FACE; + gpEnemies[index].usIndex = ADMIN_FACE; } gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; @@ -2522,6 +2496,27 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2"); if( fDeleteForGood && gpCivs[ i ].pSoldier->stats.bLife < OKLIFE/2 ) { + // Flugente: individual militia + // we not only handle promotions here, but basically update this guy (if not already counted as dead) + MILITIA militia; + if ( GetMilitia( gpCivs[i].pSoldier->usIndividualMilitiaID, &militia ) && !(militia.flagmask & MILITIAFLAG_DEAD) ) + { + MILITIA_BATTLEREPORT report; + report.id = GetIdOfCurrentlyOngoingIncident( ); + + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_DIED; + + if ( gpCivs[i].pSoldier->ubMilitiaKills ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; + + militia.history.push_back( report ); + + militia.healthratio = 0.0f; + militia.flagmask |= MILITIAFLAG_DEAD; + + UpdateMilitia( militia ); + } + AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpCivs[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT ); StrategicRemoveMilitiaFromSector( gpCivs[ i ].pSoldier->sSectorX, gpCivs[ i ].pSoldier->sSectorY, ubCurrentRank, 1 ); @@ -2530,36 +2525,10 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2"); } else { - // this will check for promotions and handle them for you - if( fDeleteForGood && ( gpCivs[ i ].pSoldier->ubMilitiaKills > 0) && ( ubCurrentRank < ELITE_MILITIA ) ) - { - UINT8 ubPromotions = CheckOneMilitiaForPromotion( gpCivs[i].pSoldier->sSectorX, gpCivs[i].pSoldier->sSectorY, ubCurrentRank, gpCivs[i].pSoldier->ubMilitiaKills ); - if( ubPromotions ) - { - if( ubPromotions == 2 ) - { - ++gbGreenToElitePromotions; - ++gbMilitiaPromotions; - - ubCurrentRank = ELITE_MILITIA; - } - else if( gpCivs[ i ].pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) - { - ++gbGreenToRegPromotions; - ++gbMilitiaPromotions; - - ubCurrentRank = REGULAR_MILITIA; - } - else if( gpCivs[ i ].pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) - { - ++gbRegToElitePromotions; - ++gbMilitiaPromotions; - - ubCurrentRank = ELITE_MILITIA; - } - } - } + // Flugente: take care of promotions and individual militia update + HandlePossibleMilitiaPromotion( gpCivs[i].pSoldier ); } + TacticalRemoveSoldierPointer( gpCivs[ i ].pSoldier, FALSE ); memset( &gpCivs[ i ], 0, sizeof( SOLDIERCELL ) ); } @@ -3045,7 +3014,7 @@ void ResetAutoResolveInterface() { switch( PreRandom( 5 ) ) { - case 0: if ( gpAR->ubElites ) { gpAR->ubElites--; break; } + case 0: if( gpAR->ubElites ) { gpAR->ubElites--; break; } case 1: if ( gpAR->ubAdmins ) { gpAR->ubAdmins--; break; } case 2: if ( gpAR->ubTroops ) { gpAR->ubTroops--; break; } case 3: if ( gpAR->ubTanks ) { gpAR->ubTanks--; break; } @@ -3056,7 +3025,7 @@ void ResetAutoResolveInterface() { switch( PreRandom( 5 ) ) { - case 0: gpAR->ubElites++; break; + case 0: gpAR->ubElites++; break; case 1: case 2: gpAR->ubAdmins++; break; case 3: case 4: gpAR->ubTroops++; break; } @@ -4066,7 +4035,7 @@ BOOLEAN FireAShot( SOLDIERCELL *pAttacker ) SOLDIERTYPE *pSoldier; pSoldier = pAttacker->pSoldier; - + if( pAttacker->uiFlags & CELL_MALECREATURE ) { PlayAutoResolveSample( ACR_SPIT, RATE_11025, 50, 1, MIDDLEPAN ); @@ -4095,7 +4064,7 @@ BOOLEAN FireAShot( SOLDIERCELL *pAttacker ) PlayAutoResolveSample( Weapon[ pItem->usItem ].sLocknLoadSound, RATE_11025, 50, 1, MIDDLEPAN ); } } - if ( (*pItem)[0]->data.gun.ubGunShotsLeft ) + if( (*pItem)[0]->data.gun.ubGunShotsLeft ) { PlayAutoResolveSample( Weapon[ pItem->usItem ].sSound, RATE_11025, 50, 1, MIDDLEPAN ); if( pAttacker->uiFlags & CELL_MERC ) @@ -4399,7 +4368,7 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) if ( fAntiTank ) pTarget->usNextHit[ bAttackIndex ] = (UINT16)( 200 + PreRandom( 400 ) ); else - pTarget->usNextHit[bAttackIndex] = (UINT16)(50 + PreRandom( 400 )); + pTarget->usNextHit[ bAttackIndex ] = (UINT16)( 50 + PreRandom( 400 ) ); pTarget->pAttacker[ bAttackIndex ] = pAttacker; } @@ -4672,6 +4641,26 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) // Flugente: campaign stats gCurrentIncident.AddStat( pTarget->pSoldier, CAMPAIGNHISTORY_TYPE_KILL ); + // Flugente: individual militia + MILITIA militia; + if ( GetMilitia( pTarget->pSoldier->usIndividualMilitiaID, &militia ) && !(militia.flagmask & MILITIAFLAG_DEAD) ) + { + militia.healthratio = 0.0f; + militia.flagmask |= MILITIAFLAG_DEAD; + + // note the current incident (when closing the incident, we only do this for those still alive) + MILITIA_BATTLEREPORT report; + report.id = GetIdOfCurrentlyOngoingIncident( ); + report.flagmask = MILITIA_BATTLEREPORT_FLAG_DIED; + + if ( pTarget->pSoldier->ubMilitiaKills ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; + + militia.history.push_back( report ); + + UpdateMilitia( militia ); + } + // Flugente: disease HandleDeathDiseaseImplications( pTarget->pSoldier ); @@ -4722,7 +4711,7 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) } else if( pAttacker->uiFlags & CELL_MILITIA ) { - pAttacker->pSoldier->ubMilitiaKills += 2; + pAttacker->pSoldier->ubMilitiaKills += 1; } if( pTarget->uiFlags & CELL_MERC && gpAR->fSound ) { @@ -4802,7 +4791,7 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) return; } pAttacker = pTarget->pAttacker[ index ]; - + //creatures get damage reduction bonuses switch( pTarget->pSoldier->ubBodyType ) { @@ -4850,7 +4839,7 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) if ( ARMED_VEHICLE( pTarget->pSoldier ) ) PlayAutoResolveSample( (UINT8)(S_METAL_IMPACT1 + PreRandom( 3 )), RATE_11025, 50, 1, MIDDLEPAN ); else - PlayAutoResolveSample( (UINT8)(BULLET_IMPACT_1+PreRandom(3)), RATE_11025, 50, 1, MIDDLEPAN ); + PlayAutoResolveSample( (UINT8)(BULLET_IMPACT_1+PreRandom(3)), RATE_11025, 50, 1, MIDDLEPAN ); if( pTarget->pSoldier->stats.bLife >= CONSCIOUSNESS ) { @@ -4870,6 +4859,26 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) // Flugente: campaign stats gCurrentIncident.AddStat( pTarget->pSoldier, CAMPAIGNHISTORY_TYPE_KILL ); + // Flugente: individual militia + MILITIA militia; + if ( GetMilitia( pTarget->pSoldier->usIndividualMilitiaID, &militia ) && !(militia.flagmask & MILITIAFLAG_DEAD) ) + { + militia.healthratio = 0.0f; + militia.flagmask |= MILITIAFLAG_DEAD; + + // note the current incident (when closing the incident, we only do this for those still alive) + MILITIA_BATTLEREPORT report; + report.id = GetIdOfCurrentlyOngoingIncident( ); + report.flagmask = MILITIA_BATTLEREPORT_FLAG_DIED; + + if ( pTarget->pSoldier->ubMilitiaKills ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; + + militia.history.push_back( report ); + + UpdateMilitia( militia ); + } + // Flugente: disease HandleDeathDiseaseImplications( pTarget->pSoldier ); @@ -4928,7 +4937,7 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) HandleMoraleEvent( pKiller->pSoldier, MORALE_KILLED_ENEMY, gpAR->ubSectorX, gpAR->ubSectorY, 0 ); } else if( pKiller->uiFlags & CELL_MILITIA ) - pKiller->pSoldier->ubMilitiaKills += 2; + pKiller->pSoldier->ubMilitiaKills += 1; } if( pAssister1 ) { @@ -4954,7 +4963,7 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) StatChange( pAssister1->pSoldier, EXPERAMT, ( UINT16 )( 5 * pTarget->pSoldier->pathing.bLevel ), FALSE ); } else if( pAssister1->uiFlags & CELL_MILITIA ) - pAssister1->pSoldier->ubMilitiaKills++; + pAssister1->pSoldier->ubMilitiaAssists++; } else if( pAssister2 ) { @@ -4980,7 +4989,7 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) StatChange( pAssister2->pSoldier, EXPERAMT, ( UINT16 )( 5 * pTarget->pSoldier->pathing.bLevel ), FALSE ); } else if( pAssister2->uiFlags & CELL_MILITIA ) - pAssister2->pSoldier->ubMilitiaKills++; + pAssister2->pSoldier->ubMilitiaAssists++; } } if( pTarget->uiFlags & CELL_MERC && gpAR->fSound ) @@ -5005,7 +5014,7 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) if ( ARMED_VEHICLE( pTarget->pSoldier ) ) PlayAutoResolveSample( (UINT8)(S_RAID_TB_BOMB), RATE_11025, 50, 1, MIDDLEPAN ); else - pTarget->pSoldier->DoMercBattleSound( BATTLE_SOUND_DIE1 ); + pTarget->pSoldier->DoMercBattleSound( BATTLE_SOUND_DIE1 ); } } #ifdef INVULNERABILITY @@ -5163,7 +5172,7 @@ BOOLEAN IsBattleOver() //DEFEAT if( fOnlyEPCsLeft ) - { + { //Kill the EPCs. for( i = 0; i < gpAR->ubMercs; ++i ) { @@ -5764,45 +5773,36 @@ void AutoResolveMilitiaDropAndPromote() // Flugente: drop sector equipment gpCivs[i].pSoldier->DropSectorEquipment( ); - + if ( gpCivs[i].pSoldier->stats.bLife < OKLIFE / 2 ) { + // Flugente: individual militia + // we not only handle promotions here, but basically update this guy + MILITIA militia; + if ( GetMilitia( gpCivs[i].pSoldier->usIndividualMilitiaID, &militia ) && !(militia.flagmask & MILITIAFLAG_DEAD) ) + { + MILITIA_BATTLEREPORT report; + report.id = GetIdOfCurrentlyOngoingIncident( ); + + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_DIED; + + if ( gpCivs[i].pSoldier->ubMilitiaKills ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; + + militia.history.push_back( report ); + + militia.healthratio = 0.0f; + militia.flagmask |= MILITIAFLAG_DEAD; + + UpdateMilitia( militia ); + } + StrategicRemoveMilitiaFromSector( gpCivs[i].pSoldier->sSectorX, gpCivs[i].pSoldier->sSectorY, ubCurrentRank, 1 ); } else if ( gpCivs[i].pSoldier->stats.bLife >= OKLIFE / 2 ) { - // this will check for promotions and handle them for you - if ( gpCivs[i].pSoldier->ubMilitiaKills && ubCurrentRank < ELITE_MILITIA ) - { - UINT8 ubPromotions = CheckOneMilitiaForPromotion( gpCivs[i].pSoldier->sSectorX, gpCivs[i].pSoldier->sSectorY, ubCurrentRank, gpCivs[i].pSoldier->ubMilitiaKills ); - if ( ubPromotions ) - { - if ( ubPromotions == 2 ) - { - ++gbGreenToElitePromotions; - ++gbMilitiaPromotions; - - ubCurrentRank = ELITE_MILITIA; - } - else if ( gpCivs[i].pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) - { - ++gbGreenToRegPromotions; - ++gbMilitiaPromotions; - - ubCurrentRank = REGULAR_MILITIA; - } - else if ( gpCivs[i].pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) - { - ++gbRegToElitePromotions; - ++gbMilitiaPromotions; - - ubCurrentRank = ELITE_MILITIA; - } - } - } - - // in any case, we had our chance to be promoted, clear the counter - gpCivs[i].pSoldier->ubMilitiaKills = 0; + // Flugente: take care of promotions and individual militia update + HandlePossibleMilitiaPromotion( gpCivs[i].pSoldier ); } // DO NOT DELETE HERE!!!! diff --git a/Strategic/Auto Resolve.h b/Strategic/Auto Resolve.h index b45fa762..a125cd4f 100644 --- a/Strategic/Auto Resolve.h +++ b/Strategic/Auto Resolve.h @@ -3,6 +3,35 @@ #include "types.h" +//generic face images +enum +{ + ADMIN_FACE, + TROOP_FACE, + ELITE_FACE, + TANK_FACE, + MILITIA1_FACE, + MILITIA2_FACE, + MILITIA3_FACE, + YM_CREATURE_FACE, + AM_CREATURE_FACE, + YF_CREATURE_FACE, + AF_CREATURE_FACE, + HUMAN_SKULL, + TANK_WRECK, + CREATURE_SKULL, + ADMINF_FACE, + TROOPF_FACE, + ELITEF_FACE, + MILITIA1F_FACE, + MILITIA2F_FACE, + MILITIA3F_FACE, + JEEP_FACE, + JEEP_WRECK, + + AUTORESOLVEFACES_MAX +}; + void EnterAutoResolveMode( UINT8 ubSectorX, UINT8 ubSectorY ); // is the autoresolve active? diff --git a/Strategic/Game Event Hook.cpp b/Strategic/Game Event Hook.cpp index 341a211e..8bb1145e 100644 --- a/Strategic/Game Event Hook.cpp +++ b/Strategic/Game Event Hook.cpp @@ -523,7 +523,7 @@ BOOLEAN ExecuteStrategicEvent( STRATEGICEVENT *pEvent ) AddEmail( PMC_INTRO, PMC_INTRO_LENGTH, PMC, GetWorldTotalMin( ), -1, -1, TYPE_EMAIL_EMAIL_EDT ); break; #endif - + case EVENT_PMC_REINFORCEMENT_ARRIVAL: HandlePMCArrival( (UINT8)pEvent->uiParam ); break; @@ -624,6 +624,12 @@ BOOLEAN ExecuteStrategicEvent( STRATEGICEVENT *pEvent ) case EVENT_SAMSITE_REPAIRED: RepairSamSite( pEvent->uiParam ); break; + + case EVENT_MILITIAROSTER_EMAIL: + // only send the email if we haven't already visited the site, otherwise continue to spam ;-) + if ( !IsBookMarkSet( MILITIAROSTER_BOOKMARK ) ) + AddEmail( MILITIAROSTER_INTRO, MILITIAROSTER_INTRO_LENGTH, MAIL_ENRICO, GetWorldTotalMin( ), -1, -1, TYPE_EMAIL_EMAIL_EDT ); + break; } gfPreventDeletionOfAnyEvent = fOrigPreventFlag; return TRUE; diff --git a/Strategic/Game Event Hook.h b/Strategic/Game Event Hook.h index 94ff2023..21decff7 100644 --- a/Strategic/Game Event Hook.h +++ b/Strategic/Game Event Hook.h @@ -132,6 +132,8 @@ enum EVENT_SAMSITE_REPAIRED, // Flugente: have a SAM site be fully repaired + EVENT_MILITIAROSTER_EMAIL, // Flugente: we get an email giving us the bookmark to the militia roster webpage + NUMBER_OF_EVENT_TYPES_PLUS_ONE, NUMBER_OF_EVENT_TYPES = NUMBER_OF_EVENT_TYPES_PLUS_ONE - 1 }; diff --git a/Strategic/Game Events.cpp b/Strategic/Game Events.cpp index 640e17a6..2c8969b6 100644 --- a/Strategic/Game Events.cpp +++ b/Strategic/Game Events.cpp @@ -124,6 +124,7 @@ CHAR16 gEventName[NUMBER_OF_EVENT_TYPES_PLUS_ONE][40]={ L"EnemyHeliRepair", L"EnemyHeliRefuel", L"SAMsiteRepaired", + L"MilitiaWebsiteEmail", }; #endif diff --git a/Strategic/Map Screen Interface Map.cpp b/Strategic/Map Screen Interface Map.cpp index c41a0a9a..82613d17 100644 --- a/Strategic/Map Screen Interface Map.cpp +++ b/Strategic/Map Screen Interface Map.cpp @@ -45,6 +45,7 @@ #include "Air Raid.h" #include "Auto Resolve.h" #include "ASD.h" // added by Flugente + #include "MilitiaIndividual.h" // added by Flugente #endif #include "Quests.h" @@ -4804,6 +4805,9 @@ BOOLEAN PickUpATownPersonFromSector( UINT8 ubType, INT16 sX, INT16 sY ) // reduce number in this sector SectorInfo[ SECTOR( sX, sY )].ubNumberOfCivsAtLevel[ ubType ] -= countMovedCivs; + // Flugente: indivdual militia + PickIndividualMilitia( SECTOR( sX, sY ), ubType, countMovedCivs ); + fMapPanelDirty = TRUE; return( TRUE ); @@ -4924,6 +4928,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen2"); // up the number in this sector of this type of militia SectorInfo[ SECTOR( sX, sY )].ubNumberOfCivsAtLevel[ ubType ] += countMovedCivs; + // Flugente: indivdual militia + DropIndividualMilitia( SECTOR( sX, sY ), ubType, countMovedCivs ); + fMapPanelDirty = TRUE; return( TRUE ); @@ -5658,6 +5665,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen3"); StrategicAddMilitiaToSector( sX, sY, GREEN_MILITIA, 1 ); ++iCount; --sGreensOnCursor; + + // Flugente: indivdual militia + DropIndividualMilitia( SECTOR( sX, sY ), GREEN_MILITIA, 1 ); } // regular @@ -5666,6 +5676,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen3"); StrategicAddMilitiaToSector( sX, sY, REGULAR_MILITIA, 1 ); ++iCount; --sRegularsOnCursor; + + // Flugente: indivdual militia + DropIndividualMilitia( SECTOR( sX, sY ), REGULAR_MILITIA, 1 ); } // elite @@ -5674,6 +5687,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen3"); StrategicAddMilitiaToSector( sX, sY, ELITE_MILITIA, 1 ); ++iCount; --sElitesOnCursor; + + // Flugente: indivdual militia + DropIndividualMilitia( SECTOR( sX, sY ), ELITE_MILITIA, 1 ); } } } @@ -5697,6 +5713,11 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen3"); StrategicAddMilitiaToSector( sX, sY, GREEN_MILITIA, (UINT8)(sGreensOnCursor % iNumberUnderControl) ); StrategicAddMilitiaToSector( sX, sY, REGULAR_MILITIA, (UINT8)(sRegularsOnCursor % iNumberUnderControl) ); StrategicAddMilitiaToSector( sX, sY, ELITE_MILITIA, (UINT8)(sElitesOnCursor % iNumberUnderControl) ); + + // Flugente: indivdual militia + DropIndividualMilitia( SECTOR( sX, sY ), GREEN_MILITIA, (UINT8)(sGreensOnCursor % iNumberUnderControl) ); + DropIndividualMilitia( SECTOR( sX, sY ), REGULAR_MILITIA, (UINT8)(sRegularsOnCursor % iNumberUnderControl) ); + DropIndividualMilitia( SECTOR( sX, sY ), ELITE_MILITIA, (UINT8)(sElitesOnCursor % iNumberUnderControl) ); } } @@ -5746,16 +5767,25 @@ void HandleRemovalOfAllTroopsAmongstSectors( void ) if( !StrategicMap[ CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].fEnemyControlled ) { - // Flugente: we no use these handy functions, as this will make altering militia distribution behaviour much easier + // Flugente: we now use these handy functions, as this will make altering militia distribution behaviour much easier // get number of each - iNumberOfGreens += MilitiaInSectorOfRankStationary( sSectorX, sSectorY, GREEN_MILITIA ); - iNumberOfRegulars += MilitiaInSectorOfRankStationary( sSectorX, sSectorY, REGULAR_MILITIA ); - iNumberOfElites += MilitiaInSectorOfRankStationary( sSectorX, sSectorY, ELITE_MILITIA ); + UINT8 green = MilitiaInSectorOfRankStationary( sSectorX, sSectorY, GREEN_MILITIA ); + UINT8 regular = MilitiaInSectorOfRankStationary( sSectorX, sSectorY, REGULAR_MILITIA ); + UINT8 elite = MilitiaInSectorOfRankStationary( sSectorX, sSectorY, ELITE_MILITIA ); + + iNumberOfGreens += green; + iNumberOfRegulars += regular; + iNumberOfElites += elite; //zero out all numbers in sectors StrategicRemoveAllStaticMilitiaFromSector( sSectorX, sSectorY, GREEN_MILITIA ); StrategicRemoveAllStaticMilitiaFromSector( sSectorX, sSectorY, REGULAR_MILITIA ); StrategicRemoveAllStaticMilitiaFromSector( sSectorX, sSectorY, ELITE_MILITIA ); + + // Flugente: indivdual militia + PickIndividualMilitia( SECTOR( sSectorX, sSectorY ), GREEN_MILITIA, green ); + PickIndividualMilitia( SECTOR( sSectorX, sSectorY ), REGULAR_MILITIA, regular ); + PickIndividualMilitia( SECTOR( sSectorX, sSectorY ), ELITE_MILITIA, elite ); } } @@ -5816,9 +5846,18 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen4"); if( !StrategicMap[ CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].fEnemyControlled ) { // get number of each - iNumberOfGreens += MilitiaInSectorOfRankStationary( sSectorX, sSectorY, GREEN_MILITIA ); - iNumberOfRegulars += MilitiaInSectorOfRankStationary( sSectorX, sSectorY, REGULAR_MILITIA ); - iNumberOfElites += MilitiaInSectorOfRankStationary( sSectorX, sSectorY, ELITE_MILITIA ); + UINT8 green = MilitiaInSectorOfRankStationary( sSectorX, sSectorY, GREEN_MILITIA ); + UINT8 regular = MilitiaInSectorOfRankStationary( sSectorX, sSectorY, REGULAR_MILITIA ); + UINT8 elite = MilitiaInSectorOfRankStationary( sSectorX, sSectorY, ELITE_MILITIA ); + + iNumberOfGreens += green; + iNumberOfRegulars += regular; + iNumberOfElites += elite; + + // Flugente: indivdual militia + PickIndividualMilitia( SECTOR( sSectorX, sSectorY ), GREEN_MILITIA, green ); + PickIndividualMilitia( SECTOR( sSectorX, sSectorY ), REGULAR_MILITIA, regular ); + PickIndividualMilitia( SECTOR( sSectorX, sSectorY ), ELITE_MILITIA, elite ); } } @@ -5858,6 +5897,11 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen4"); SectorInfo[ sSector ].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] = ( UINT8 )( iNumberOfRegulars / iNumberUnderControl ); SectorInfo[ sSector ].ubNumberOfCivsAtLevel[ ELITE_MILITIA ] = ( UINT8 )( iNumberOfElites / iNumberUnderControl ); + // Flugente: indivdual militia + DropIndividualMilitia( sSector, GREEN_MILITIA, (UINT8)(iNumberOfGreens / iNumberUnderControl) ); + DropIndividualMilitia( sSector, REGULAR_MILITIA, (UINT8)(iNumberOfRegulars / iNumberUnderControl) ); + DropIndividualMilitia( sSector, ELITE_MILITIA, (UINT8)(iNumberOfElites / iNumberUnderControl) ); + // Flugente: as we do not move the group militia, we have to make sure we don't accidentally overfill a sector sTotalSoFar = NumNonPlayerTeamMembersInSector( sSectorX, sSectorY, MILITIA_TEAM ); @@ -5867,6 +5911,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen4"); SectorInfo[ sSector ].ubNumberOfCivsAtLevel[ GREEN_MILITIA ]++; ++sTotalSoFar; --iNumberLeftOverGreen; + + // Flugente: indivdual militia + DropIndividualMilitia( sSector, GREEN_MILITIA, 1 ); } if( ( iNumberLeftOverRegular )&&( sTotalSoFar < iMaxMilitiaPerSector ) ) @@ -5874,6 +5921,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen4"); SectorInfo[ sSector ].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]++; ++sTotalSoFar; --iNumberLeftOverRegular; + + // Flugente: indivdual militia + DropIndividualMilitia( sSector, REGULAR_MILITIA, 1 ); } if( ( iNumberLeftOverElite )&&( sTotalSoFar < iMaxMilitiaPerSector ) ) @@ -5881,6 +5931,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Map Screen4"); SectorInfo[ sSector ].ubNumberOfCivsAtLevel[ ELITE_MILITIA ]++; ++sTotalSoFar; --iNumberLeftOverElite; + + // Flugente: indivdual militia + DropIndividualMilitia( sSector, ELITE_MILITIA, 1 ); } // if this sector is currently loaded @@ -7386,6 +7439,11 @@ void MilitiaDisbandYesNoBoxCallback( UINT8 bExitValue ) DecrementTownLoyalty( (UINT8)sSelectedMilitiaTown, uiTownLoyaltyBonus ); } + // Flugente: individual militia + INT16 sGlobalMapSector = GetBaseSectorForCurrentTown() + ((sSectorMilitiaMapSector % MILITIA_BOX_ROWS) + (sSectorMilitiaMapSector / MILITIA_BOX_ROWS) * (16)); + + DisbandIndividualMilitia( sGlobalMapSector, sGreensOnCursor, sRegularsOnCursor, sElitesOnCursor ); + // zero out numbers on the cursor sGreensOnCursor = 0; sRegularsOnCursor = 0; diff --git a/Strategic/MilitiaIndividual.cpp b/Strategic/MilitiaIndividual.cpp new file mode 100644 index 00000000..dfeee151 --- /dev/null +++ b/Strategic/MilitiaIndividual.cpp @@ -0,0 +1,872 @@ +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#include "MilitiaIndividual.h" + +#include "random.h" +#include "text.h" +#include "Overhead Types.h" +#include "Game Clock.h" +#include "strategicmap.h" +#include "GameVersion.h" +#include "SaveLoadGame.h" +#include "CampaignStats.h" +#include "Town Militia.h" +#include "message.h" + +MilitiaOriginData gMilitiaOriginData[MO_MAX]; + +// later on, make this an array with a vector for each sector +std::vector gIndividualMilitiaVector; + +MILITIA::MILITIA() +{ + memset( this, 0, sizeof(MILITIA) ); +} + +BOOLEAN +MILITIA::Save( HWFILE hFile ) +{ + UINT32 uiNumBytesWritten = 0; + + if ( !FileWrite( hFile, this, SIZEOF_MILITIA_POD, &uiNumBytesWritten ) ) + return(FALSE); + + // in order to save a vector, we first save its size and then its content + UINT32 size = history.size( ); + if ( !FileWrite( hFile, &size, sizeof(UINT32), &uiNumBytesWritten ) ) + return(FALSE); + + std::vector::iterator itend = history.end( ); + for ( std::vector::iterator it = history.begin( ); it != itend; ++it ) + { + if ( !FileWrite( hFile, &(*it), sizeof(MILITIA_BATTLEREPORT), &uiNumBytesWritten ) ) + return(FALSE); + } + + return(TRUE); +} + +BOOLEAN +MILITIA::Load( HWFILE hwFile ) +{ + if ( guiCurrentSaveGameVersion >= INDIVIDUAL_MILITIA ) + { + UINT32 numBytesRead = 0; + + numBytesRead = ReadFieldByField( hwFile, &id, sizeof(id), sizeof(UINT32), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &flagmask, sizeof(flagmask), sizeof(UINT32), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &origin, sizeof(origin), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &originsector, sizeof(originsector), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, §or, sizeof(sector), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &age, sizeof(age), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &soldierclass, sizeof(soldierclass), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &bodytype, sizeof(bodytype), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &skin, sizeof(skin), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &hair, sizeof(hair), sizeof(UINT8), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &forename, sizeof(forename), sizeof(UINT16), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &surname, sizeof(surname), sizeof(UINT16), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &callsign, sizeof(callsign), sizeof(UINT16), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &healthratio, sizeof(healthratio), sizeof(FLOAT), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &kills, sizeof(kills), sizeof(UINT16), numBytesRead); + numBytesRead = ReadFieldByField( hwFile, &assists, sizeof(assists), sizeof(UINT16), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &promotionpoints, sizeof(promotionpoints), sizeof(UINT16), numBytesRead ); + numBytesRead = ReadFieldByField( hwFile, &filler1, sizeof(filler1), sizeof(UINT16), numBytesRead ); + + if ( numBytesRead != SIZEOF_MILITIA_POD ) + return(FALSE); + + UINT32 size = 0; + numBytesRead = ReadFieldByField( hwFile, &size, sizeof(size), sizeof(UINT32), numBytesRead ); + + for ( UINT32 i = 0; i < size; ++i ) + { + MILITIA_BATTLEREPORT data; + if ( !FileRead( hwFile, &data, sizeof(MILITIA_BATTLEREPORT), &numBytesRead ) ) + return(FALSE); + + history.push_back( data ); + } + } + + return(TRUE); +} + + +static CHAR16 gMilitiaNameText[100]; +STR16 MILITIA::GetName() +{ + swprintf( gMilitiaNameText, L"NAME NOT FOUND" ); + + if ( bodytype == REGFEMALE ) + { + if ( gMilitiaOriginData[origin].szFemale_Forename.size() > forename && gMilitiaOriginData[origin].szFemale_Surname.size() > surname ) + { + swprintf( gMilitiaNameText, L"%s %s", gMilitiaOriginData[origin].szFemale_Forename[forename].c_str( ), gMilitiaOriginData[origin].szFemale_Surname[surname].c_str( ) ); + } + } + else + { + if ( gMilitiaOriginData[origin].szMale_Forename.size( ) > forename && gMilitiaOriginData[origin].szMale_Surname.size( ) > surname ) + { + swprintf( gMilitiaNameText, L"%s %s", gMilitiaOriginData[origin].szMale_Forename[forename].c_str( ), gMilitiaOriginData[origin].szMale_Surname[surname].c_str( ) ); + } + } + + return gMilitiaNameText; +} + +static CHAR16 gMilitiaSectorText[100]; +STR16 MILITIA::GetSector( ) +{ + swprintf( gMilitiaSectorText, L"NAME NOT FOUND" ); + + GetShortSectorString( SECTORX( sector ), SECTORY( sector ), gMilitiaSectorText ); + + return gMilitiaSectorText; +} + +STR16 MILITIA::GetOriginSector( ) +{ + swprintf( gMilitiaSectorText, L"NAME NOT FOUND" ); + + GetShortSectorString( SECTORX( originsector ), SECTORY( originsector ), gMilitiaSectorText ); + + return gMilitiaSectorText; +} + +void MILITIA::AddKills( UINT16 aKills, UINT16 aAssists ) +{ + kills += aKills; + assists += aAssists; + + // important: increase by the NEW points (as militia that start with a higher rank have their points adjusted, promotion points are not always the sum of kills and assists) + promotionpoints += 2 * aKills + aAssists; + + if ( soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + promotionpoints = max( promotionpoints, gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Regular ); + } + else if ( soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + { + promotionpoints = max( promotionpoints, gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Regular + gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Elite ); + } +} + +UINT16 MILITIA::GetWage( ) +{ + UINT8 type = GREEN_MILITIA; + if ( soldierclass == SOLDIER_CLASS_REG_MILITIA ) + type = REGULAR_MILITIA; + else if ( soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + type = ELITE_MILITIA; + + return gMilitiaOriginData[origin].dailycost[type]; +} + +// release militia from our service. Does not take care of actually removing a militia from the sector! +void MILITIA::Fire( ) +{ + flagmask |= MILITIAFLAG_FIRED; + + MILITIA_BATTLEREPORT report; + report.id = GetWorldTotalMin( ); + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_FIRED; + + history.push_back( report ); +} + +BOOLEAN SaveIndividualMilitiaData( HWFILE hwFile ) +{ + UINT32 uiNumBytesWritten = 0; + + // in order to save a vector, we first save its size and then its content + UINT32 size = gIndividualMilitiaVector.size( ); + if ( !FileWrite( hwFile, &size, sizeof(UINT32), &uiNumBytesWritten ) ) + return(FALSE); + + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !(*it).Save( hwFile ) ) + return(FALSE); + } + + return TRUE; +} + +void InitIndividualMilitiaData() +{ + gIndividualMilitiaVector.clear(); +} + +BOOLEAN LoadIndividualMilitiaData( HWFILE hwFile ) +{ + InitIndividualMilitiaData(); + + UINT32 numBytesRead = 0; + + UINT32 size = 0; + numBytesRead = ReadFieldByField( hwFile, &size, sizeof(size), sizeof(UINT32), numBytesRead ); + + for ( UINT32 i = 0; i < size; ++i ) + { + MILITIA data; + if ( !data.Load( hwFile ) ) + return(FALSE); + + gIndividualMilitiaVector.push_back(data); + } + + return TRUE; +} + +UINT32 TotalNumberIndividualMilitia( ) +{ + return gIndividualMilitiaVector.size( ); +} + +UINT32 GetFreeIndividualMilitiaId() +{ + return 1 + TotalNumberIndividualMilitia(); +} + +BOOLEAN IsLegalMilitiaId( UINT32 aId ) +{ + return (aId > 0 && aId < GetFreeIndividualMilitiaId( )); +} + +BOOLEAN GetMilitia( UINT32 aId, MILITIA* apMilitia ) +{ + if ( gGameExternalOptions.fIndividualMilitia && IsLegalMilitiaId( aId ) ) + { + if ( apMilitia ) + *apMilitia = gIndividualMilitiaVector[aId - 1]; + + return TRUE; + } + + return FALSE; +} + +// update data +void UpdateMilitia( MILITIA aMilitia ) +{ + if ( gGameExternalOptions.fIndividualMilitia && IsLegalMilitiaId( aMilitia.id ) ) + { + gIndividualMilitiaVector[aMilitia.id - 1] = aMilitia; + } +} + +SOLDIERTYPE* GetUsedSoldierToIndividualMilitia( UINT32 aMilitiaId ) +{ + for ( UINT32 cnt = gTacticalStatus.Team[MILITIA_TEAM].bFirstID; cnt < gTacticalStatus.Team[MILITIA_TEAM].bLastID; ++cnt ) + { + if ( MercPtrs[cnt] && MercPtrs[cnt]->bActive && MercPtrs[cnt]->usIndividualMilitiaID == aMilitiaId ) + { + return MercPtrs[cnt]; + } + } + + return NULL; +} + +// update the health values of all militia in tactical +void UpdateAllMilitiaHealthInTactical() +{ + if ( !gGameExternalOptions.fIndividualMilitia ) + return; + + SOLDIERTYPE* pSoldier; + UINT32 cnt = gTacticalStatus.Team[MILITIA_TEAM].bFirstID; + INT32 lastid = gTacticalStatus.Team[MILITIA_TEAM].bLastID; + for ( pSoldier = MercPtrs[cnt]; cnt < lastid; ++cnt, ++pSoldier ) + { + MILITIA militia; + if ( pSoldier && pSoldier->bActive && pSoldier->stats.bLifeMax && GetMilitia( pSoldier->usIndividualMilitiaID, &militia ) ) + { + militia.healthratio = 100.0f * pSoldier->stats.bLife / pSoldier->stats.bLifeMax; + + // while we're here, update kills and assists too + militia.AddKills( pSoldier->ubMilitiaKills, pSoldier->ubMilitiaAssists ); + + pSoldier->ubMilitiaKills = 0; + pSoldier->ubMilitiaAssists = 0; + + UpdateMilitia( militia ); + } + } +} + +void HandleHourlyMilitiaHealing( ) +{ + if ( !gGameExternalOptions.fIndividualMilitia || !gGameExternalOptions.fIndividualMilitia_ManageHealth ) + return; + + INT32 cnt = gTacticalStatus.Team[MILITIA_TEAM].bFirstID; + INT32 lastid = gTacticalStatus.Team[MILITIA_TEAM].bLastID; + SOLDIERTYPE* pSoldier = NULL; + + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !((*it).flagmask & MILITIAFLAG_DEAD) ) + { + (*it).healthratio = min( 100.0f, (*it).healthratio + gGameExternalOptions.dIndividualMilitiaHourlyHealthPercentageGain ); + + // if this guy is in the currently loaded sector, heal the soldier instead (and update health ratio while you're there) + if ( !gbWorldSectorZ && (*it).sector == SECTOR( gWorldSectorX, gWorldSectorY ) ) + { + for ( pSoldier = MercPtrs[cnt]; cnt < lastid; ++cnt, ++pSoldier ) + { + if ( pSoldier && pSoldier->bActive && (*it).id == pSoldier->usIndividualMilitiaID && + gWorldSectorX == pSoldier->sSectorX && gWorldSectorY == pSoldier->sSectorY && !pSoldier->bSectorZ && + pSoldier->stats.bLifeMax > 0 ) + { + INT8 oldlife = pSoldier->stats.bLife; + + FLOAT currenthealthratio = 100.0f * oldlife / pSoldier->stats.bLifeMax; + + currenthealthratio = min( 100.0f, currenthealthratio + gGameExternalOptions.dIndividualMilitiaHourlyHealthPercentageGain ); + + pSoldier->stats.bLife = min( pSoldier->stats.bLifeMax, (currenthealthratio / 100.0f) * pSoldier->stats.bLifeMax ); + + // healing done will be displayed the next time the player sees this soldier + pSoldier->flags.fDisplayDamage = TRUE; + pSoldier->sDamage -= pSoldier->stats.bLife - oldlife; + + // update new health ratio + (*it).healthratio = currenthealthratio; + + break; + } + } + } + } + } +} + +UINT32 CreateRandomIndividualMilitia( UINT8 aSoldierClass, UINT8 aOrigin, UINT8 aSector ) +{ + UINT32 militiaid = CreateNewIndividualMilitia( aSoldierClass, aOrigin, aSector ); + + MILITIA newmilitia; + if ( GetMilitia( militiaid, &newmilitia ) ) + { + newmilitia.healthratio = (OKLIFE + Random( 85 )); + + newmilitia.AddKills( Random( 5 ), Random( 10 ) ); + + // make up some history + UINT16 mineventtime = Random( 70000 ); + + int numhistory = Random( 30 ); + for ( int j = 0; j < numhistory; ++j ) + { + MILITIA_BATTLEREPORT report; + report.id = Random( 100 ); + switch ( Random( 20 ) ) + { + case 0: + case 1: + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_PROMOTED; + mineventtime = max( mineventtime + 6, report.id ); + report.id = mineventtime; + break; + + case 2: + case 3: report.flagmask |= MILITIA_BATTLEREPORT_FLAG_WOUNDED_SMALL; break; + case 4: + case 5: report.flagmask |= MILITIA_BATTLEREPORT_FLAG_WOUNDED_HEAVY; break; + case 6: + case 7: report.flagmask |= MILITIA_BATTLEREPORT_FLAG_WOUNDED_COMA; break; + case 8: + case 9: + case 10: report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; break; + + // once dead, stop adding more events and kill of the soldier + case 11: report.flagmask |= MILITIA_BATTLEREPORT_FLAG_DIED; + j = numhistory; + newmilitia.healthratio = 0.0f; + newmilitia.flagmask |= MILITIAFLAG_DEAD; + break; + + case 12: report.flagmask |= MILITIA_BATTLEREPORT_FLAG_FIRED; + j = numhistory; + newmilitia.flagmask |= MILITIAFLAG_FIRED; + break; + default: break; + } + + newmilitia.history.push_back( report ); + } + + UpdateMilitia( newmilitia ); + } + + return militiaid; +} + +UINT32 CreateNewIndividualMilitia( UINT8 aSoldierClass, UINT8 aOrigin, UINT8 aSector ) +{ + if ( !gGameExternalOptions.fIndividualMilitia ) + return 0; + + // make sure we use a valid soldier class + UINT8 soldierclass = aSoldierClass; + if ( soldierclass < SOLDIER_CLASS_GREEN_MILITIA || soldierclass > SOLDIER_CLASS_ELITE_MILITIA ) + soldierclass = SOLDIER_CLASS_GREEN_MILITIA; + + MILITIA newmilitia; + + newmilitia.id = GetFreeIndividualMilitiaId( ); + + newmilitia.soldierclass = soldierclass; + + // according to origin, we choose bodytype, skin, hair, fore- and surname + newmilitia.origin = aOrigin; + + // age is random + newmilitia.age = 16 + Random( 2 ) * (1 + Random( 8 ) ) + + Random( 2 ) * (1 + Random( 8 )) + + Random( 2 ) * (1 + Random( 8 )) + + Random( 2 ) * (1 + Random( 8 )) + + Random( 2 ) * (1 + Random( 8 )) + + Random( 2 ) * (1 + Random( 8 )); + + newmilitia.originsector = aSector; + newmilitia.sector = aSector; + + UINT8 rnd = Random( 100 ); + + newmilitia.bodytype = REGMALE; + for ( int i = 0; i < ADULTFEMALEMONSTER; ++i ) + { + if ( rnd < gMilitiaOriginData[newmilitia.origin].chance_bodytype[i] ) + { + newmilitia.bodytype = i; + break; + } + else + { + rnd -= gMilitiaOriginData[newmilitia.origin].chance_bodytype[i]; + } + } + + rnd = Random( 100 ); + + newmilitia.skin = PINKSKIN; + for ( int i = 0; i < NUMSKINS; ++i ) + { + if ( rnd < gMilitiaOriginData[newmilitia.origin].chance_skin[i] ) + { + newmilitia.skin = i; + break; + } + else + { + rnd -= gMilitiaOriginData[newmilitia.origin].chance_skin[i]; + } + } + + newmilitia.hair = ChooseHairColor( newmilitia.bodytype, newmilitia.skin ); + + if ( newmilitia.age > 50 && Chance( newmilitia.age - 50 ) ) + newmilitia.hair = WHITEHEAD; + + if ( newmilitia.bodytype == REGFEMALE ) + { + newmilitia.forename = Random( gMilitiaOriginData[newmilitia.origin].szFemale_Forename.size( ) ); + newmilitia.surname = Random( gMilitiaOriginData[newmilitia.origin].szFemale_Surname.size( ) ); + } + else + { + newmilitia.forename = Random( gMilitiaOriginData[newmilitia.origin].szMale_Forename.size( ) ); + newmilitia.surname = Random( gMilitiaOriginData[newmilitia.origin].szMale_Surname.size( ) ); + } + + newmilitia.flagmask = 0; + + newmilitia.healthratio = 100.0f; + + newmilitia.kills = 0; + newmilitia.assists = 0; + newmilitia.promotionpoints = 0; + + // promotion points are based on kills and assists. For proper accounting, award points for current rank + newmilitia.AddKills( 0, 0 ); + + // make up some history + MILITIA_BATTLEREPORT report; + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_RECRUITED; + report.id = GetWorldTotalMin( ); + + newmilitia.history.push_back( report ); + + gIndividualMilitiaVector.push_back( newmilitia ); + + return newmilitia.id; +} + +// search for a individual militia that is alive and not currently in use in this sector, and return its id +// if none is found, create new and return that one +UINT32 GetIdOfUnusedindividualMilitia( UINT8 aSoldierClass, UINT8 aSector ) +{ + if ( !gGameExternalOptions.fIndividualMilitia ) + return 0; + + std::vector::iterator itend = gIndividualMilitiaVector.end(); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) && (*it).sector == aSector && (*it).soldierclass == aSoldierClass ) + { + // fitting data found - now we have to make sure this one isn't already in use + BOOLEAN found = FALSE; + + SOLDIERTYPE* pSoldier; + + INT32 cnt = gTacticalStatus.Team[MILITIA_TEAM].bFirstID; + INT32 lastid = gTacticalStatus.Team[MILITIA_TEAM].bLastID; + for ( pSoldier = MercPtrs[cnt]; cnt < lastid; ++cnt, ++pSoldier ) + { + if ( pSoldier && pSoldier->bActive && (*it).id == pSoldier->usIndividualMilitiaID && IsLegalMilitiaId( pSoldier->usIndividualMilitiaID ) ) + { + found = TRUE; + break; + } + } + + if ( !found ) + { + return (*it).id; + } + } + } + + // if this feature is on and we get to this point, then there aren't enough individual militia. This is odd, the player should be informed + if ( gGameExternalOptions.fIndividualMilitia ) + ScreenMsg( FONT_MCOLOR_RED, MSG_INTERFACE, L"Possible error: Not enough individual militia found in GetIdOfUnusedindividualMilitia" ); + + // nobody found. That shouldn't really happen, as we are supposed to create data whenever new militia is created. Create new data and use that + return CreateNewIndividualMilitia( aSoldierClass, MO_ARULCO, aSector ); +} + +// handle possible militia promotion and individual militia update +void HandlePossibleMilitiaPromotion( SOLDIERTYPE* pSoldier ) +{ + // we not only handle promotions here, but basically update this guy + MILITIA militia; + if ( GetMilitia( pSoldier->usIndividualMilitiaID, &militia ) ) + { + militia.AddKills( pSoldier->ubMilitiaKills, pSoldier->ubMilitiaAssists ); + + // if we have enough points, promote + if ( militia.soldierclass == SOLDIER_CLASS_GREEN_MILITIA ) + { + if ( militia.promotionpoints >= gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Regular ) + { + if ( militia.promotionpoints >= gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Regular + gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Elite ) + { + ++gbGreenToElitePromotions; + ++gbMilitiaPromotions; + + pSoldier->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA; + + StrategicPromoteMilitiaInSector( pSoldier->sSectorX, pSoldier->sSectorY, GREEN_MILITIA, 1 ); + StrategicPromoteMilitiaInSector( pSoldier->sSectorX, pSoldier->sSectorY, REGULAR_MILITIA, 1 ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szIdividualMilitiaWebsiteText[18], militia.GetName( ) ); + } + else + { + ++gbGreenToRegPromotions; + ++gbMilitiaPromotions; + + pSoldier->ubSoldierClass = SOLDIER_CLASS_REG_MILITIA; + + StrategicPromoteMilitiaInSector( pSoldier->sSectorX, pSoldier->sSectorY, GREEN_MILITIA, 1 ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szIdividualMilitiaWebsiteText[17], militia.GetName( ) ); + } + } + } + else if ( militia.soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + if ( militia.promotionpoints >= gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Regular + gGameExternalOptions.usIndividualMilitia_PromotionPoints_To_Elite ) + { + ++gbRegToElitePromotions; + ++gbMilitiaPromotions; + + pSoldier->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA; + + StrategicPromoteMilitiaInSector( pSoldier->sSectorX, pSoldier->sSectorY, REGULAR_MILITIA, 1 ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szIdividualMilitiaWebsiteText[18], militia.GetName( ) ); + } + } + + // add a battle report if there was a battle (if there was a battle there will have been participants + if ( gCurrentIncident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_GREEN] + + gCurrentIncident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_REGULAR] + + gCurrentIncident.usParticipants[CAMPAIGNHISTORY_SD_MILITIA_ELITE] ) + { + MILITIA_BATTLEREPORT report; + report.id = GetIdOfCurrentlyOngoingIncident( ); + + if ( pSoldier->stats.bLife < OKLIFE ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_WOUNDED_COMA; + + if ( pSoldier->ubMilitiaKills ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; + + if ( militia.healthratio - 100.0f * pSoldier->stats.bLife / pSoldier->stats.bLifeMax > 50 ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_WOUNDED_HEAVY; + else if ( militia.healthratio - 100.0f * pSoldier->stats.bLife / pSoldier->stats.bLifeMax > 5 ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_WOUNDED_SMALL; + + militia.history.push_back( report ); + } + + // separate report for promotion + if ( militia.soldierclass != pSoldier->ubSoldierClass ) + { + MILITIA_BATTLEREPORT report; + report.id = GetWorldTotalMin( ); + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_PROMOTED; + militia.history.push_back( report ); + } + + militia.healthratio = 100.0f * pSoldier->stats.bLife / pSoldier->stats.bLifeMax; + militia.soldierclass = pSoldier->ubSoldierClass; + + UpdateMilitia( militia ); + } + else if ( pSoldier->ubMilitiaKills + pSoldier->ubMilitiaAssists > 0 ) + { + UINT8 ubMilitiaRank = SoldierClassToMilitiaRank( pSoldier->ubSoldierClass ); + UINT8 ubPromotions = CheckOneMilitiaForPromotion( pSoldier->sSectorX, pSoldier->sSectorY, ubMilitiaRank, 2 * pSoldier->ubMilitiaKills + pSoldier->ubMilitiaAssists ); + if ( ubPromotions ) + { + if ( ubPromotions == 2 ) + { + ++gbGreenToElitePromotions; + ++gbMilitiaPromotions; + + pSoldier->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA; + } + else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) + { + ++gbGreenToRegPromotions; + ++gbMilitiaPromotions; + + pSoldier->ubSoldierClass = SOLDIER_CLASS_REG_MILITIA; + } + else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) + { + ++gbRegToElitePromotions; + ++gbMilitiaPromotions; + + pSoldier->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA; + } + } + } + + pSoldier->ubMilitiaKills = 0; + pSoldier->ubMilitiaAssists = 0; +} + +void MoveIndividualMilitiaProfiles( UINT8 aSourceSector, UINT8 aTargetSector, UINT8 usGreens, UINT8 usRegulars, UINT8 usElites ) +{ + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !usGreens && !usRegulars && !usElites ) + return; + + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) && (*it).sector == aSourceSector ) + { + if ( usGreens && ( *it ).soldierclass == SOLDIER_CLASS_GREEN_MILITIA ) + { + (*it).sector = aTargetSector; + --usGreens; + } + else if ( usRegulars && (*it).soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + (*it).sector = aTargetSector; + --usRegulars; + } + else if ( usElites && (*it).soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + { + (*it).sector = aTargetSector; + --usElites; + } + } + } + + // if this feature is on and we get to this point, then there aren't enough individual militia. This is odd, the player should be informed + if ( gGameExternalOptions.fIndividualMilitia ) + ScreenMsg( FONT_MCOLOR_RED, MSG_INTERFACE, L"Possible error: Not enough individual militia found in MoveIndividualMilitiaProfiles" ); +} + + +// militia are disbanded - we have to take note of that +void DisbandIndividualMilitia( UINT8 aSector, UINT8 usGreens, UINT8 usRegulars, UINT8 usElites ) +{ + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !usGreens && !usRegulars && !usElites ) + return; + + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) && (*it).sector == aSector ) + { + if ( usGreens && (*it).soldierclass == SOLDIER_CLASS_GREEN_MILITIA ) + { + --usGreens; + } + else if ( usRegulars && (*it).soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + --usRegulars; + } + else if ( usElites && (*it).soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + { + --usElites; + } + + (*it).Fire(); + } + } + + // if this feature is on and we get to this point, then there aren't enough individual militia. This is odd, the player should be informed + if ( gGameExternalOptions.fIndividualMilitia ) + ScreenMsg( FONT_MCOLOR_RED, MSG_INTERFACE, L"Possible error: Not enough individual militia found in DisbandIndividualMilitia" ); +} + +void PromoteIndividualMilitia( UINT8 aSector, UINT8 aSoldierClass ) +{ + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) && (*it).sector == aSector && (*it).soldierclass == aSoldierClass ) + { + (*it).soldierclass++; + + // to properly update promotion points + (*it).AddKills(0, 0); + + return; + } + } + + // if this feature is on and we get to this point, then there aren't enough individual militia. This is odd, the player should be informed + if ( gGameExternalOptions.fIndividualMilitia ) + ScreenMsg( FONT_MCOLOR_RED, MSG_INTERFACE, L"Possible error: Not enough individual militia found in PromoteIndividualMilitia" ); +} + +// return the sum of daily wages, and the number of individual militia per class +UINT32 GetDailyUpkeep_IndividualMilitia( UINT32& arNumGreen, UINT32& arNumRegular, UINT32& arNumElite ) +{ + UINT32 cost = 0; + + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) ) + { + if ( (*it).soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + ++arNumRegular; + } + else if ( (*it).soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + { + ++arNumElite; + } + else + { + ++arNumGreen; + } + + cost += (*it).GetWage(); + } + } + + return cost; +} + +// if the player repositions militia in a town in strategic, we need to reposition militia as well +void PickIndividualMilitia( UINT8 aSector, UINT8 ubType, UINT16 aNumber ) +{ + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !aNumber ) + return; + + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED | MILITIAFLAG_NEEDS_SECTOR)) && (*it).sector == aSector ) + { + if ( ubType == GREEN_MILITIA && (*it).soldierclass == SOLDIER_CLASS_GREEN_MILITIA ) + { + (*it).flagmask |= MILITIAFLAG_NEEDS_SECTOR; + + --aNumber; + } + else if ( ubType == REGULAR_MILITIA && (*it).soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + (*it).flagmask |= MILITIAFLAG_NEEDS_SECTOR; + + --aNumber; + } + else if ( ubType == ELITE_MILITIA && (*it).soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + { + (*it).flagmask |= MILITIAFLAG_NEEDS_SECTOR; + + --aNumber; + } + } + } + + // if this feature is on and we get to this point, then there aren't enough individual militia. This is odd, the player should be informed + if ( gGameExternalOptions.fIndividualMilitia ) + ScreenMsg( FONT_MCOLOR_RED, MSG_INTERFACE, L"Possible error: Not enough individual militia found in PickIndividualMilitia" ); +} + +void DropIndividualMilitia( UINT8 aSector, UINT8 ubType, UINT16 aNumber ) +{ + std::vector::iterator itend = gIndividualMilitiaVector.end( ); + for ( std::vector::iterator it = gIndividualMilitiaVector.begin( ); it != itend; ++it ) + { + if ( !aNumber ) + return; + + if ( !((*it).flagmask & (MILITIAFLAG_DEAD | MILITIAFLAG_FIRED)) && ((*it).flagmask & MILITIAFLAG_NEEDS_SECTOR) ) + { + if ( ubType == GREEN_MILITIA && (*it).soldierclass == SOLDIER_CLASS_GREEN_MILITIA ) + { + (*it).flagmask &= ~MILITIAFLAG_NEEDS_SECTOR; + (*it).sector = aSector; + + --aNumber; + } + else if ( ubType == REGULAR_MILITIA && (*it).soldierclass == SOLDIER_CLASS_REG_MILITIA ) + { + (*it).flagmask &= ~MILITIAFLAG_NEEDS_SECTOR; + (*it).sector = aSector; + + --aNumber; + } + else if ( ubType == ELITE_MILITIA && (*it).soldierclass == SOLDIER_CLASS_ELITE_MILITIA ) + { + (*it).flagmask &= ~MILITIAFLAG_NEEDS_SECTOR; + (*it).sector = aSector; + + --aNumber; + } + } + } + + // if this feature is on and we get to this point, then there aren't enough individual militia. This is odd, the player should be informed + if ( gGameExternalOptions.fIndividualMilitia ) + ScreenMsg( FONT_MCOLOR_RED, MSG_INTERFACE, L"Possible error: Not enough individual militia found in DropIndividualMilitia" ); +} diff --git a/Strategic/MilitiaIndividual.h b/Strategic/MilitiaIndividual.h new file mode 100644 index 00000000..53ec4d6b --- /dev/null +++ b/Strategic/MilitiaIndividual.h @@ -0,0 +1,185 @@ +#ifndef _MILITIA_INDIVIDUAL_H +#define _MILITIA_INDIVIDUAL_H + +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +// These functions are used for the individual militia feature + +#include "Assignments.h" +#include "soldier profile type.h" +#include "Animation Data.h" +#include "Soldier Create.h" + +// not used atm +/*typedef struct +{ + UINT16 item; // item id, refers to the ones in Items.xml + UINT16 status; // status of item. In case of ammo items, this instead refers to the ammo count. No bullets lost here. +} MILITIA_ITEM;*/ + +/*enum +{ +WISDOM = NUM_TRAINABLE_STATS, +LEVEL, +HEALTH_CURRENT, +NUM_TOTAL_STATS +};*/ + +#define MILITIA_BATTLEREPORT_FLAG_RECRUITED 0x00000001 // this was not a battle, but we were recruited - use id as timestamp in hours +#define MILITIA_BATTLEREPORT_FLAG_PROMOTED 0x00000002 // this was not a battle, but we were promoted - use id as timestamp in hours +#define MILITIA_BATTLEREPORT_FLAG_DIED 0x00000004 // militia died in this battle +#define MILITIA_BATTLEREPORT_FLAG_WOUNDED_SMALL 0x00000008 // was lightly wounded + +#define MILITIA_BATTLEREPORT_FLAG_WOUNDED_HEAVY 0x00000010 // was heavily wounded +#define MILITIA_BATTLEREPORT_FLAG_WOUNDED_COMA 0x00000020 // wounds were so heavy that we fell into a coma +#define MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY 0x00000040 // killed an enemy +#define MILITIA_BATTLEREPORT_FLAG_FIRED 0x00000080 // militia was fired + +struct MILITIA_BATTLEREPORT +{ + MILITIA_BATTLEREPORT( ) : id( 0 ), flagmask( 0 ) {} + + UINT32 id; // id of battle militia took part in. Ties to campaign history, thus we know with more detail what happened there. + UINT32 flagmask; // we can note up to 16 different properties here - perhaps wether we were a reinforcement, or were wounded and such +}; + +// a militia's origin affects the chances of his makeout +enum MilitiaOrigin +{ + MO_ARULCO, // standard, militia was an aruclan volunteer that has been trained by the player + MO_PMC, // militia was hired from a PMC by the player + MO_DEFECTOR, // militia was an enemy captured by the player and switched to the player side during interrogation + + MO_MAX +}; + +// This struct defines necessary data for a pool of militia origins. +// For example, if you want PMC militia to resemble eastern european buff guys, you only need to define the specific fore- and surnames, +// set the chance for huge male bodytpes high, and give according skin colour chances +typedef struct +{ + // possible name pool + std::vector szMale_Forename; + std::vector szMale_Surname; + std::vector szFemale_Forename; + std::vector szFemale_Surname; + + // chances for bodytypes and skin colours (hair is chosen auomatically according to coded scheme) + UINT8 chance_bodytype[ADULTFEMALEMONSTER]; + UINT8 chance_skin[NUMSKINS]; + UINT16 dailycost[MAX_MILITIA_LEVELS]; +} MilitiaOriginData; + +extern MilitiaOriginData gMilitiaOriginData[MO_MAX]; + +#define MILITIAFLAG_DEAD 0x00000001 // this guy is dead +#define MILITIAFLAG_FIRED 0x00000002 // this guy no longer works for us - we fired him, or didn't have the money to keep them +#define MILITIAFLAG_NEEDS_SECTOR 0x00000004 // as the player currently resets militia in a town's sector in the map, this militia's sector is not clear + +class MILITIA +{ +public: + MILITIA(); + + BOOLEAN Save( HWFILE hFile ); + BOOLEAN Load( HWFILE hwFile ); + + STR16 GetName( ); + STR16 GetSector( ); + STR16 GetOriginSector( ); + void AddKills( UINT16 aKills, UINT16 aAssists ); + UINT16 GetWage(); + void Fire(); // release militia from our service. Does not take care of actually removing a militia from the sector! + + UINT32 id; // number of militia. Tied with one or more separate tables of names, this gives us a name + UINT32 flagmask; // individual flags - we are dead etc. + + // origin + UINT8 origin; // one of MilitiaOrigin + UINT8 originsector; // sector militia was first created in + UINT8 sector; // sector militia currently is in + UINT8 age; + + // visuals + UINT8 soldierclass; // green/veteran/elite + UINT8 bodytype; // male/bigmale1/bigmale2/female + UINT8 skin; + UINT8 hair; + + // name + UINT16 forename; + UINT16 surname; + UINT16 callsign; + + // As stats are highly dependent of game progress and current sector, storing the stats themselves is ill-advised. + // We thus only store the current HP percentage. + FLOAT healthratio; + + UINT16 kills; + UINT16 assists; + + // we don't store the experience level, we store the amount of points in regard to promotion. + // While this is made up of kills and assists, we need this, as we have to account for militia initially starting at regular level + UINT16 promotionpoints; + + UINT16 filler1; // necessary for padding - fill up so that SIZEOF_MILITIA_POD is doubly even + + char endOfPOD; // marker for end of POD (plain old data) + + std::vector history; // reports on all battles this militia took part in +}; + +#define SIZEOF_MILITIA_POD offsetof( MILITIA, endOfPOD ) + +// later on, make this an array with a vector for each sector +extern std::vector gIndividualMilitiaVector; + +void InitIndividualMilitiaData(); +BOOLEAN SaveIndividualMilitiaData( HWFILE hwFile ); +BOOLEAN LoadIndividualMilitiaData( HWFILE hwFile ); + +// wrapper functions to retrieve militia +UINT32 TotalNumberIndividualMilitia( ); +UINT32 GetFreeIndividualMilitiaId(); +BOOLEAN IsLegalMilitiaId( UINT32 aId ); + +// return militia with a certain id +BOOLEAN GetMilitia( UINT32 aId, MILITIA* apMilitia ); + +// update data +void UpdateMilitia( MILITIA aMilitia ); + +SOLDIERTYPE* GetUsedSoldierToIndividualMilitia( UINT32 aMilitiaId ); + +// update the health values of all militia in tactical +void UpdateAllMilitiaHealthInTactical(); + +void HandleHourlyMilitiaHealing( ); + +UINT32 CreateRandomIndividualMilitia( UINT8 aSoldierClass, UINT8 aOrigin, UINT8 aSector ); + +UINT32 CreateNewIndividualMilitia( UINT8 aSoldierClass, UINT8 aOrigin, UINT8 aSector ); + +UINT32 GetIdOfUnusedindividualMilitia( UINT8 aSoldierClass, UINT8 aSector ); + +// handle possible militia promotion and individual militia update +void HandlePossibleMilitiaPromotion( SOLDIERTYPE* pSoldier ); + +void MoveIndividualMilitiaProfiles( UINT8 aSourceSector, UINT8 aTargetSector, UINT8 usGreens, UINT8 usRegulars, UINT8 usElites ); + +// militia are disbanded - we have to take note of that +void DisbandIndividualMilitia( UINT8 aSector, UINT8 usGreens, UINT8 usRegulars, UINT8 usElites ); + +void PromoteIndividualMilitia( UINT8 aSector, UINT8 aSoldierClass ); + +// return the sum of daily wages, and the number of individual militia per class +UINT32 GetDailyUpkeep_IndividualMilitia( UINT32& arNumGreen, UINT32& arNumRegular, UINT32& arNumElite ); + +// if the player repositions militia in a town in strategic, we need to reposition militia as well +void PickIndividualMilitia( UINT8 aSector, UINT8 ubType, UINT16 aNumber ); +void DropIndividualMilitia( UINT8 aSector, UINT8 ubType, UINT16 aNumber ); + +#endif \ No newline at end of file diff --git a/Strategic/MilitiaSquads.cpp b/Strategic/MilitiaSquads.cpp index 253b2687..9e23efe2 100644 --- a/Strategic/MilitiaSquads.cpp +++ b/Strategic/MilitiaSquads.cpp @@ -33,6 +33,7 @@ #include "MilitiaSquads.h" #include "Reinforcement.h" #include "Inventory Choosing.h" // added by Flugente for MoveOneMilitiaEquipmentSet() and MoveMilitiaEquipment() +#include "MilitiaIndividual.h" // added by Flugente // Debug defines @@ -311,7 +312,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, // No elites/regulars - reduce target green! --ubTargetGreen; } - + while ( trained + promotionstodo < ubMilitiaToTrain ) { //////////////////////////////// @@ -325,7 +326,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, // Do we have room in the target sector? if ( usTotalMilitiaAtTarget + trained < gGameExternalOptions.iMaxMilitiaPerSector ) { - if ( ubTargetElite > 0 ) + if (ubTargetElite > 0) { // Add elite. ++numcreated[ELITE_MILITIA]; @@ -334,7 +335,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ++trained; fFoundOne = TRUE; } - else if ( ubTargetRegular > 0 ) + else if (ubTargetRegular > 0) { // Add regular. ++numcreated[REGULAR_MILITIA]; @@ -343,7 +344,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ++trained; fFoundOne = TRUE; } - else if ( ubTargetGreen > 0 ) + else if (ubTargetGreen > 0) { // Add green. ++numcreated[GREEN_MILITIA]; @@ -356,7 +357,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, // Full militia group? See if you can upgrade some. if( !fFoundOne ) - { + { // we need to promote militia then ++promotionstodo; @@ -382,6 +383,16 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, // Flugente: substract volunteers AddVolunteers( -trained ); + + // Flugente: create individual militia + for ( int i = 0; i < numcreated[GREEN_MILITIA]; ++i ) + CreateNewIndividualMilitia( GREEN_MILITIA, MO_ARULCO, SECTOR( sTMapX, sTMapY ) ); + + for ( int i = 0; i < numcreated[REGULAR_MILITIA]; ++i ) + CreateNewIndividualMilitia( REGULAR_MILITIA, MO_ARULCO, SECTOR( sTMapX, sTMapY ) ); + + for ( int i = 0; i < numcreated[ELITE_MILITIA]; ++i ) + CreateNewIndividualMilitia( ELITE_MILITIA, MO_ARULCO, SECTOR( sTMapX, sTMapY ) ); } // This reduces the group back to "maximum" size. It starts by eliminating extra greens, then regulars, then elites. @@ -401,7 +412,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, --pTargetSector->ubNumberOfCivsAtLevel[ELITE_MILITIA]; } } - + // Update the militia if the current sector is affected if (gfStrategicMilitiaChangesMade) { @@ -511,7 +522,7 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL { ubChanceToSpreadOut = 0; } - + if (!gGameExternalOptions.gfAllowMilitiaSpread ) { // Spreading is not allowed by user. Let them fill up the target sector. @@ -559,7 +570,11 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL UINT8 elites = max(0, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]); UINT8 regulars = max(0, bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]); UINT8 greens = max(0, bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, elites, regulars, greens); + + // Flugente: update individual militia data + UpdateAllMilitiaHealthInTactical( ); // Flugente: disease PopulationMove( sMapX, sMapY, sTMapX, sTMapY, elites + regulars + greens ); @@ -572,6 +587,9 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, REGULAR_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, ELITE_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); + // Flugente: move along individual militia data + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), greens, regulars, elites ); + // Add new militia. StrategicAddMilitiaToSector( sMapX, sMapY, GREEN_MILITIA, bGreensSourceTeam ); StrategicAddMilitiaToSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsSourceTeam ); @@ -597,21 +615,28 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL UINT8 elites = max(0, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]); UINT8 regulars = max(0, bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]); UINT8 greens = max(0, bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, elites, regulars, greens); + // Flugente: update individual militia data + UpdateAllMilitiaHealthInTactical( ); + // Flugente: disease PopulationMove( sMapX, sMapY, sTMapX, sTMapY, elites + regulars + greens ); + // Remove half team from source sector + StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, bGreensDestTeam ); + StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsDestTeam ); + StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, bElitesDestTeam ); + + // Flugente: move along individual militia data + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA], pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA], pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] ); + // Add half team to target sector StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, bGreensDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, bRegularsDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, bElitesDestTeam ); - // Remove half team from source sector - StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, bGreensDestTeam ); - StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsDestTeam ); - StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, bElitesDestTeam ); - // Units that move into an empty square do not get to move again. AddToBlockMoveList( sTMapX, sTMapY ); } @@ -683,8 +708,12 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL UINT8 elites = max(0, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]); UINT8 regulars = max(0, bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]); UINT8 greens = max(0, bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, elites, regulars, greens); + // Flugente: update individual militia data + UpdateAllMilitiaHealthInTactical( ); + // Flugente: disease PopulationMove( sMapX, sMapY, sTMapX, sTMapY, elites + regulars + greens ); @@ -696,6 +725,9 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, REGULAR_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, ELITE_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); + // Flugente: move along individual militia data + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), greens, regulars, elites ); + // Add new militia. StrategicAddMilitiaToSector( sMapX, sMapY, GREEN_MILITIA, bGreensSourceTeam ); StrategicAddMilitiaToSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsSourceTeam ); @@ -709,15 +741,21 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL // Flugente: mobiles take along their gear MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ], pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ], pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + // Flugente: update individual militia data + UpdateAllMilitiaHealthInTactical( ); + + StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] ); + StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA] ); + StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] ); + + // Flugente: move along individual militia data + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA], pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA], pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] ); + // Entire group moves from Source to Target, leaving no one behind. StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); - - StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); - StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); - StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); - + // Block target group from moving. It has just assimilated another group. AddToBlockMoveList( sTMapX, sTMapY ); } @@ -754,6 +792,9 @@ BOOLEAN MoveOneBestMilitiaMan(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMa { StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1 ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, 1 ); + + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), 0, 0, 1 ); + return TRUE; } @@ -761,6 +802,9 @@ BOOLEAN MoveOneBestMilitiaMan(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMa { StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, 1 ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, 1 ); + + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), 0, 1, 0 ); + return TRUE; } @@ -768,6 +812,9 @@ BOOLEAN MoveOneBestMilitiaMan(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMa { StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, 1 ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, 1 ); + + MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), 1, 0, 0 ); + return TRUE; } @@ -1894,7 +1941,7 @@ BOOLEAN CallMilitiaReinforcements( INT16 sTargetMapX, INT16 sTargetMapY, INT16 s gpAttackDirs[guiDirNumber][0] = uiNumGreen = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, GREEN_MILITIA ); gpAttackDirs[guiDirNumber][1] = uiNumReg = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, REGULAR_MILITIA ); gpAttackDirs[guiDirNumber][2] = uiNumElite = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, ELITE_MILITIA ); - gpAttackDirs[guiDirNumber][3] = INSERTION_CODE_CENTER; + gpAttackDirs[ guiDirNumber ][3] = INSERTION_CODE_CENTER; guiDirNumber = insertioncode + 1; @@ -1913,7 +1960,7 @@ BOOLEAN CallMilitiaReinforcements( INT16 sTargetMapX, INT16 sTargetMapY, INT16 s gpAttackDirs[guiDirNumber][0] += militia_green - uiNumGreen; gpAttackDirs[guiDirNumber][1] += militia_troop - uiNumReg; gpAttackDirs[guiDirNumber][2] += militia_elite - uiNumElite; - gpAttackDirs[guiDirNumber][3] = insertioncode; + gpAttackDirs[ guiDirNumber ][3] = insertioncode; uiNumGreen = militia_green; uiNumReg = militia_troop; @@ -2202,9 +2249,9 @@ void GenerateDirectionInfosForTraining( INT16 sMapX, INT16 sMapY, UINT8* uiDirNu pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_NORTH; -#ifdef DEBUG_SHOW_RATINGS - if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); -#endif + #ifdef DEBUG_SHOW_RATINGS + if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); + #endif ++(*uiDirNumber); } @@ -2230,9 +2277,9 @@ void GenerateDirectionInfosForTraining( INT16 sMapX, INT16 sMapY, UINT8* uiDirNu pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_WEST; -#ifdef DEBUG_SHOW_RATINGS - if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); -#endif + #ifdef DEBUG_SHOW_RATINGS + if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); + #endif ++(*uiDirNumber); } @@ -2258,9 +2305,9 @@ void GenerateDirectionInfosForTraining( INT16 sMapX, INT16 sMapY, UINT8* uiDirNu pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_SOUTH; -#ifdef DEBUG_SHOW_RATINGS - if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); -#endif + #ifdef DEBUG_SHOW_RATINGS + if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); + #endif ++(*uiDirNumber); } @@ -2286,9 +2333,9 @@ void GenerateDirectionInfosForTraining( INT16 sMapX, INT16 sMapY, UINT8* uiDirNu pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_EAST; -#ifdef DEBUG_SHOW_RATINGS - if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); -#endif + #ifdef DEBUG_SHOW_RATINGS + if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); + #endif ++(*uiDirNumber); } @@ -2516,7 +2563,7 @@ UINT8 GetMobileMilitiaQuota( BOOLEAN printMessage ) if ( MilitiaTrainingAllowedInTown( iCurrT_ID ) == FALSE ) // considered as mobile only if in that town training is taboo (Tixa, Omerta..) iActiveMobiles += NumNonPlayerTeamMembersInSector( SECTORX( iSectorInfoID ), SECTORY( iSectorInfoID ), MILITIA_TEAM ); - } + } //else if ( StrategicMap[ iStrategicMapID ].bSAMCondition > 0 ) //faster then below, but not safe else if ( MilitiaTrainingAllowedInSector( SECTORX(iSectorInfoID), SECTORY(iSectorInfoID), 0 ) == FALSE ) { diff --git a/Strategic/Strategic Movement.cpp b/Strategic/Strategic Movement.cpp index 6e359c6e..51813c7a 100644 --- a/Strategic/Strategic Movement.cpp +++ b/Strategic/Strategic Movement.cpp @@ -51,6 +51,7 @@ #include "Reinforcement.h" // added by Flugente for AddPossiblePendingMilitiaToBattle() #include "Militia Control.h" // added by Flugente for ResetMilitia() #include "Creature Spreading.h" // added by Flugente + #include "MilitiaIndividual.h" // added by Flugente #endif #include "MilitiaSquads.h" @@ -2281,6 +2282,9 @@ void GroupArrivedAtSector( UINT8 ubGroupID, BOOLEAN fCheckForBattle, BOOLEAN fNe if ( gGameExternalOptions.fMilitiaUseSectorInventory ) TeamDropAll( MILITIA_TEAM ); + // Flugente: update individual militia data + UpdateAllMilitiaHealthInTactical( ); + // we now force a resetting of militia - they will be removed and recreated // as the group's coordinates have alreay 'moved' to the next sector, the group militia will now be gone gfStrategicMilitiaChangesMade = TRUE; @@ -2294,6 +2298,9 @@ void GroupArrivedAtSector( UINT8 ubGroupID, BOOLEAN fCheckForBattle, BOOLEAN fNe // move the gear along to the next sector MoveMilitiaEquipment( pGroup->ubPrevX, pGroup->ubPrevY, pGroup->ubSectorX, pGroup->ubSectorY, pGroup->pEnemyGroup->ubNumElites, pGroup->pEnemyGroup->ubNumTroops, pGroup->pEnemyGroup->ubNumAdmins ); + // Flugente: move along individual militia data + MoveIndividualMilitiaProfiles( SECTOR( pGroup->ubPrevX, pGroup->ubPrevY ), SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), pGroup->pEnemyGroup->ubNumAdmins, pGroup->pEnemyGroup->ubNumTroops, pGroup->pEnemyGroup->ubNumElites ); + // if this is the last sector along the path, destroy the group after this and make the militia static if ( !fBattlePending &&GroupAtFinalDestination( pGroup ) ) { @@ -2309,13 +2316,16 @@ void GroupArrivedAtSector( UINT8 ubGroupID, BOOLEAN fCheckForBattle, BOOLEAN fNe // move the gear along to the next sector MoveMilitiaEquipment( pGroup->ubPrevX, pGroup->ubPrevY, pGroup->ubSectorX, pGroup->ubSectorY, pGroup->pEnemyGroup->ubNumElites, pGroup->pEnemyGroup->ubNumTroops, pGroup->pEnemyGroup->ubNumAdmins ); + // Flugente: move along individual militia data + MoveIndividualMilitiaProfiles( SECTOR( pGroup->ubPrevX, pGroup->ubPrevY ), SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), pGroup->pEnemyGroup->ubNumAdmins, pGroup->pEnemyGroup->ubNumTroops, pGroup->pEnemyGroup->ubNumElites ); + // if this is the last sector along the path, destroy the group after this and make the militia static if ( !fBattlePending && GroupAtFinalDestination( pGroup ) ) { // once militia have arrived, move them from the group to the sector DissolveMilitiaGroup( pGroup->ubGroupID ); } - + // for safety, reset if necessary ResetMilitia( ); } diff --git a/Strategic/Strategic_VS2005.vcproj b/Strategic/Strategic_VS2005.vcproj index 2417a4ee..04729006 100644 --- a/Strategic/Strategic_VS2005.vcproj +++ b/Strategic/Strategic_VS2005.vcproj @@ -442,6 +442,10 @@ RelativePath=".\MilitiaSquads.h" > + + @@ -652,6 +656,10 @@ RelativePath=".\MilitiaSquads.cpp" > + + diff --git a/Strategic/Strategic_VS2008.vcproj b/Strategic/Strategic_VS2008.vcproj index 9f6af175..51214934 100644 --- a/Strategic/Strategic_VS2008.vcproj +++ b/Strategic/Strategic_VS2008.vcproj @@ -442,6 +442,10 @@ RelativePath="MilitiaSquads.h" > + + @@ -650,6 +654,10 @@ RelativePath="MilitiaSquads.cpp" > + + diff --git a/Strategic/Strategic_VS2010.vcxproj b/Strategic/Strategic_VS2010.vcxproj index ea126dc8..28afcd61 100644 --- a/Strategic/Strategic_VS2010.vcxproj +++ b/Strategic/Strategic_VS2010.vcxproj @@ -49,6 +49,7 @@ + @@ -102,6 +103,7 @@ + diff --git a/Strategic/Strategic_VS2010.vcxproj.filters b/Strategic/Strategic_VS2010.vcxproj.filters index a5345c5e..d88d387f 100644 --- a/Strategic/Strategic_VS2010.vcxproj.filters +++ b/Strategic/Strategic_VS2010.vcxproj.filters @@ -80,6 +80,9 @@ Header Files + + + Header Files Header Files @@ -235,6 +238,9 @@ Source Files + + + Source Files Source Files diff --git a/Strategic/Strategic_VS2013.vcxproj b/Strategic/Strategic_VS2013.vcxproj index 208a5e51..b0113ce5 100644 --- a/Strategic/Strategic_VS2013.vcxproj +++ b/Strategic/Strategic_VS2013.vcxproj @@ -48,6 +48,7 @@ + @@ -101,6 +102,7 @@ + diff --git a/Strategic/Strategic_VS2013.vcxproj.filters b/Strategic/Strategic_VS2013.vcxproj.filters index 838a6880..3721bc59 100644 --- a/Strategic/Strategic_VS2013.vcxproj.filters +++ b/Strategic/Strategic_VS2013.vcxproj.filters @@ -159,6 +159,9 @@ Header Files + + Header Files + @@ -350,5 +353,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Strategic/Town Militia.cpp b/Strategic/Town Militia.cpp index 0adeb375..274157ef 100644 --- a/Strategic/Town Militia.cpp +++ b/Strategic/Town Militia.cpp @@ -31,6 +31,7 @@ #include "Map Screen Interface Map.h" #include "laptop.h" // added by Flugente #include "Game Event Hook.h" // added by Flugente + #include "MilitiaIndividual.h" // added by Flugente #endif // HEADROCK HAM 3: include these files so that a militia trainer's Effective Leadership can be determined. Used @@ -128,6 +129,8 @@ BOOLEAN TownMilitiaTrainingPromotion( INT16 sMapX, INT16 sMapY ) // great! Promote a GREEN militia guy in the training sector to a REGULAR StrategicPromoteMilitiaInSector( sMapX, sMapY, GREEN_MILITIA, 1 ); + PromoteIndividualMilitia( SECTOR( sMapX, sMapY ), SOLDIER_CLASS_GREEN_MILITIA ); + if ( sMapX == gWorldSectorX && sMapY == gWorldSectorY ) gfStrategicMilitiaChangesMade = TRUE; @@ -149,6 +152,8 @@ BOOLEAN TownMilitiaTrainingPromotion( INT16 sMapX, INT16 sMapY ) // great! Promote a GREEN militia guy in the neighbouring sector to a REGULAR StrategicPromoteMilitiaInSector( sNeighbourX, sNeighbourY, GREEN_MILITIA, 1 ); + PromoteIndividualMilitia( SECTOR( sNeighbourX, sNeighbourY ), SOLDIER_CLASS_GREEN_MILITIA ); + if ( sNeighbourX == gWorldSectorX && sNeighbourY == gWorldSectorY ) gfStrategicMilitiaChangesMade = TRUE; @@ -168,6 +173,8 @@ BOOLEAN TownMilitiaTrainingPromotion( INT16 sMapX, INT16 sMapY ) // great! Promote a REGULAR militia guy in the training sector to a VETERAN StrategicPromoteMilitiaInSector( sMapX, sMapY, REGULAR_MILITIA, 1 ); + PromoteIndividualMilitia( SECTOR( sMapX, sMapY ), SOLDIER_CLASS_REG_MILITIA ); + if ( sMapX == gWorldSectorX && sMapY == gWorldSectorY ) gfStrategicMilitiaChangesMade = TRUE; @@ -189,6 +196,8 @@ BOOLEAN TownMilitiaTrainingPromotion( INT16 sMapX, INT16 sMapY ) // great! Promote a Regular militia guy in the neighbouring sector to a Veteran StrategicPromoteMilitiaInSector( sNeighbourX, sNeighbourY, REGULAR_MILITIA, 1 ); + PromoteIndividualMilitia( SECTOR( sNeighbourX, sNeighbourY ), SOLDIER_CLASS_REG_MILITIA ); + if ( sNeighbourX == gWorldSectorX && sNeighbourY == gWorldSectorY ) gfStrategicMilitiaChangesMade = TRUE; @@ -277,6 +286,8 @@ void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMa // next, please! ++ubMilitiaTrained; + + CreateNewIndividualMilitia( GREEN_MILITIA, MO_ARULCO, SECTOR( sMapX, sMapY ) ); } else { @@ -301,6 +312,8 @@ void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMa // next, please! ++ubMilitiaTrained; + CreateNewIndividualMilitia( GREEN_MILITIA, MO_ARULCO, SECTOR( sNeighbourX, sNeighbourY ) ); + fFoundOne = TRUE; break; } @@ -359,6 +372,8 @@ void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMa // Flugente: if we trained militia, the PMC notices us and offers their services if ( gGameExternalOptions.fPMC ) AddStrategicEvent( EVENT_PMC_EMAIL, GetWorldTotalMin() + 60 * (1 + Random(6)), 0 ); + + AddStrategicEvent( EVENT_MILITIAROSTER_EMAIL, GetWorldTotalMin( ) + 60 * (1 + Random( 4 )), 0 ); } @@ -2232,6 +2247,9 @@ void HandleMilitiaUpkeepPayment( void ) // Find and remove militia across the map. This is done randomly, starting with elites. UINT32 uiNumMilitiaDisbanded = 0; + UINT32 usGreenDisbanded = 0; + UINT32 usRegularsDisbanded = 0; + UINT32 usElitesDisbanded = 0; MILITIA_LIST_TYPE MilitiaList[256]; @@ -2293,56 +2311,66 @@ void HandleMilitiaUpkeepPayment( void ) // Adjust list entry MilitiaList[0].ubNumMobileElites--; // Reduce debt appropriately - uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobileElite; + uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobile[ELITE_MILITIA]; // Increase tally of militia removed ++uiNumMilitiaDisbanded; + ++usElitesDisbanded; } else if (MilitiaList[0].ubNumMobileRegulars > 0) { StrategicRemoveMilitiaFromSector( sX, sY, REGULAR_MILITIA, 1 ); MilitiaList[0].ubNumMobileRegulars--; - uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobileRegular; + uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobile[REGULAR_MILITIA]; ++uiNumMilitiaDisbanded; + ++usRegularsDisbanded; } else if (MilitiaList[0].ubNumMobileGreens > 0) { StrategicRemoveMilitiaFromSector( sX, sY, GREEN_MILITIA, 1 ); MilitiaList[0].ubNumMobileGreens--; - uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobileGreen; + uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobile[GREEN_MILITIA]; ++uiNumMilitiaDisbanded; + ++usGreenDisbanded; } else if (MilitiaList[0].ubNumTownElites > 0) { StrategicRemoveMilitiaFromSector( sX, sY, ELITE_MILITIA, 1 ); MilitiaList[0].ubNumTownElites--; - uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTownElite; + uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTown[ELITE_MILITIA]; ++uiNumMilitiaDisbanded; + ++usElitesDisbanded; } else if (MilitiaList[0].ubNumTownRegulars > 0) { StrategicRemoveMilitiaFromSector( sX, sY, REGULAR_MILITIA, 1 ); MilitiaList[0].ubNumTownRegulars--; - uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTownRegular; + uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTown[REGULAR_MILITIA]; ++uiNumMilitiaDisbanded; + ++usRegularsDisbanded; } else if (MilitiaList[0].ubNumTownGreens > 0) { StrategicRemoveMilitiaFromSector( sX, sY, GREEN_MILITIA, 1 ); MilitiaList[0].ubNumTownGreens--; - uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTownGreen; + uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTown[GREEN_MILITIA]; ++uiNumMilitiaDisbanded; + ++usGreenDisbanded; } else { // Somehow there are none to remove?! Assert(1); } + // Resort the list! qsort( MilitiaList, 256, sizeof(MILITIA_LIST_TYPE), MilitiaListQsortCompare); } // Pay all we can uiPayment = LaptopSaveInfo.iCurrentBalance; + + // Flugente: individual militia + DisbandIndividualMilitia( MilitiaList[0].ubSectorId, usGreenDisbanded, usRegularsDisbanded, usElitesDisbanded ); // Notify player that X militia have been automatically disbanded due to debt. CHAR16 sString[256]; @@ -2421,6 +2449,10 @@ UINT32 CalcMilitiaUpkeep( void ) { UINT32 uiTotalPayment = 0; + UINT32 militia_static[MAX_MILITIA_LEVELS] = {0, 0, 0}; + UINT32 militia_mobile[MAX_MILITIA_LEVELS] = {0, 0, 0}; + UINT32 militia_individual[MAX_MILITIA_LEVELS] = {0, 0, 0}; + for (UINT16 cnt = 0; cnt < 256; ++cnt) { SECTORINFO *pSectorInfo = &( SectorInfo[ cnt ] ); @@ -2431,19 +2463,36 @@ UINT32 CalcMilitiaUpkeep( void ) if ((ubTownId != BLANK_SECTOR && MilitiaTrainingAllowedInTown(ubTownId)) || // Major Town IsThisSectorASAMSector( sMapX, sMapY, 0 ) ) // SAM Site { - uiTotalPayment += MilitiaInSectorOfRank( sMapX, sMapY, GREEN_MILITIA ) * gGameExternalOptions.usDailyCostTownGreen; - uiTotalPayment += MilitiaInSectorOfRank( sMapX, sMapY, REGULAR_MILITIA ) * gGameExternalOptions.usDailyCostTownRegular; - uiTotalPayment += MilitiaInSectorOfRank( sMapX, sMapY, ELITE_MILITIA ) * gGameExternalOptions.usDailyCostTownElite; + for ( int i = GREEN_MILITIA; i < MAX_MILITIA_LEVELS; ++i ) + militia_static[i] += MilitiaInSectorOfRank( sMapX, sMapY, i ); } else { - uiTotalPayment += MilitiaInSectorOfRank( sMapX, sMapY, GREEN_MILITIA ) * gGameExternalOptions.usDailyCostMobileGreen; - uiTotalPayment += MilitiaInSectorOfRank( sMapX, sMapY, REGULAR_MILITIA ) * gGameExternalOptions.usDailyCostMobileRegular; - uiTotalPayment += MilitiaInSectorOfRank( sMapX, sMapY, ELITE_MILITIA ) * gGameExternalOptions.usDailyCostMobileElite; + for ( int i = GREEN_MILITIA; i < MAX_MILITIA_LEVELS; ++i ) + militia_mobile[i] += MilitiaInSectorOfRank( sMapX, sMapY, i ); } } - return (uiTotalPayment); + // Flugente: if indivdual militia is on, calculate costs there and use the above as a fallback option + if ( gGameExternalOptions.fIndividualMilitia ) + { + uiTotalPayment = GetDailyUpkeep_IndividualMilitia( militia_individual[GREEN_MILITIA], militia_individual[REGULAR_MILITIA], militia_individual[ELITE_MILITIA] ); + } + + for ( int i = GREEN_MILITIA; i < MAX_MILITIA_LEVELS; ++i ) + { + if ( militia_static[i] + militia_mobile[i] > militia_individual[i] ) + { + UINT32 exceed = militia_static[i] + militia_mobile[i] - militia_individual[i]; + UINT32 exceed_mobile = min( exceed, militia_mobile[i] ); + UINT32 exced_static = exceed - exceed_mobile; + + uiTotalPayment += exced_static * gGameExternalOptions.usDailyCostTown[i]; + uiTotalPayment += exceed_mobile * gGameExternalOptions.usDailyCostMobile[i]; + } + } + + return uiTotalPayment; } // Flugente: our militia volunteer pool is limited diff --git a/Tactical/DynamicDialogue.cpp b/Tactical/DynamicDialogue.cpp index eb312c85..7a376fee 100644 --- a/Tactical/DynamicDialogue.cpp +++ b/Tactical/DynamicDialogue.cpp @@ -30,8 +30,6 @@ #include "Campaign.h" #include "Points.h" -extern INT32 ReadFieldByField( HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount ); - // event id counter // has to be saved/loaded/reset UINT32 gDynamicDialogueEventCounter = 0; diff --git a/Tactical/Interface.cpp b/Tactical/Interface.cpp index e3db8afc..34a44888 100644 --- a/Tactical/Interface.cpp +++ b/Tactical/Interface.cpp @@ -2207,7 +2207,7 @@ void DrawSelectedUIAboveGuy( UINT16 usSoldierID ) } } // Flugente: soldier profiles - else if ( pSoldier->bTeam == MILITIA_TEAM && gGameExternalOptions.fSoldierProfiles_Militia && pSoldier->usSoldierProfile ) + else if ( pSoldier->bTeam == MILITIA_TEAM && ((gGameExternalOptions.fSoldierProfiles_Militia && pSoldier->usSoldierProfile) || pSoldier->usIndividualMilitiaID) ) { // get a proper chaos name swprintf(NameStr, pSoldier->GetName()); @@ -2362,7 +2362,7 @@ void DrawSelectedUIAboveGuy( UINT16 usSoldierID ) ShowRankIcon( sXPos, sYPos, pSoldier ); } // Flugente: soldier profiles - else if ( pSoldier->bTeam == MILITIA_TEAM && gGameExternalOptions.fSoldierProfiles_Militia && pSoldier->usSoldierProfile ) + else if ( pSoldier->bTeam == MILITIA_TEAM && ((gGameExternalOptions.fSoldierProfiles_Militia && pSoldier->usSoldierProfile) || pSoldier->usIndividualMilitiaID) ) { // get a proper name swprintf(NameStr, pSoldier->GetName()); diff --git a/Tactical/Militia Control.cpp b/Tactical/Militia Control.cpp index 4ae33700..5b1950b1 100644 --- a/Tactical/Militia Control.cpp +++ b/Tactical/Militia Control.cpp @@ -30,10 +30,11 @@ #include "Campaign.h" #include "ai.h" #include "Isometric Utils.h" + #include "MilitiaSquads.h" + #include "MilitiaIndividual.h" // added by Flugente + #include "CampaignStats.h" // added by Flugente #endif -#include "MilitiaSquads.h" - //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; @@ -281,9 +282,7 @@ void PrepareMilitiaForTactical( BOOLEAN fPrepareAll) void HandleMilitiaPromotions( void ) { UINT8 cnt; - UINT8 ubMilitiaRank; SOLDIERTYPE* pTeamSoldier; - UINT8 ubPromotions; gbGreenToElitePromotions = 0; gbGreenToRegPromotions = 0; @@ -296,31 +295,8 @@ void HandleMilitiaPromotions( void ) { if ( pTeamSoldier->bActive && pTeamSoldier->bInSector && pTeamSoldier->stats.bLife > 0 ) { - if ( pTeamSoldier->ubMilitiaKills > 0 ) - { - ubMilitiaRank = SoldierClassToMilitiaRank( pTeamSoldier->ubSoldierClass ); - ubPromotions = CheckOneMilitiaForPromotion( gWorldSectorX, gWorldSectorY, ubMilitiaRank, pTeamSoldier->ubMilitiaKills ); - if( ubPromotions ) - { - if( ubPromotions == 2 ) - { - ++gbGreenToElitePromotions; - ++gbMilitiaPromotions; - } - else if( pTeamSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) - { - ++gbGreenToRegPromotions; - ++gbMilitiaPromotions; - } - else if( pTeamSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) - { - ++gbRegToElitePromotions; - ++gbMilitiaPromotions; - } - } - - pTeamSoldier->ubMilitiaKills = 0; - } + // Flugente: take care of promotions and individual militia update + HandlePossibleMilitiaPromotion( pTeamSoldier ); } } diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index fdd07bf3..317f755e 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -4026,7 +4026,7 @@ void HandleNPCTeamMemberDeath( SOLDIERTYPE *pSoldierOld ) if (pSoldierOld->ubAttackerID != NOBODY && MercPtrs[ pSoldierOld->ubAttackerID ]->bTeam == MILITIA_TEAM ) { - MercPtrs[ pSoldierOld->ubAttackerID ]->ubMilitiaKills++; + MercPtrs[pSoldierOld->ubAttackerID]->ubMilitiaAssists++; } //if the NPC is a dealer, add the dealers items to the ground diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index dfcd36d1..265e26ad 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -63,6 +63,7 @@ #include "Food.h" #include "CampaignStats.h" // added by Flugente #include "DynamicDialogue.h" // added by Flugente +#include "MilitiaIndividual.h" // added by Flugente #endif // anv: for enemy taunts @@ -3995,7 +3996,7 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) else if ( MercPtrs[ ubAttacker ]->bTeam == MILITIA_TEAM ) { // get a kill! 2 points! - MercPtrs[ ubAttacker ]->ubMilitiaKills += 2; + MercPtrs[ ubAttacker ]->ubMilitiaKills += 1; } } @@ -4025,7 +4026,7 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) else if ( MercPtrs[ ubAssister ]->bTeam == MILITIA_TEAM ) { // get an assist - 1 points - MercPtrs[ ubAssister ]->ubMilitiaKills += 1; + MercPtrs[ubAssister]->ubMilitiaAssists += 1; } } /* @@ -4040,7 +4041,7 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) else if ( MercPtrs[ pSoldier->ubPreviousAttackerID ]->bTeam == MILITIA_TEAM ) { // get an assist - 1 points - MercPtrs[ pSoldier->ubPreviousAttackerID ]->ubMilitiaKills += 1; + MercPtrs[ pSoldier->ubPreviousAttackerID ]->ubMilitiaAssists += 1; } } */ @@ -4050,6 +4051,26 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) // Flugente: campaign stats gCurrentIncident.AddStat( pSoldier, CAMPAIGNHISTORY_TYPE_KILL ); + // Flugente: individual militia + MILITIA militia; + if ( GetMilitia( pSoldier->usIndividualMilitiaID, &militia ) && !(militia.flagmask & MILITIAFLAG_DEAD) ) + { + militia.healthratio = 0.0f; + militia.flagmask |= MILITIAFLAG_DEAD; + + // note the current incident (when closing the incident, we only do this for those still alive) + MILITIA_BATTLEREPORT report; + report.id = GetIdOfCurrentlyOngoingIncident( ); + report.flagmask = MILITIA_BATTLEREPORT_FLAG_DIED; + + if ( pSoldier->ubMilitiaKills ) + report.flagmask |= MILITIA_BATTLEREPORT_FLAG_KILLEDENEMY; + + militia.history.push_back( report ); + + UpdateMilitia(militia); + } + // Flugente: disease HandleDeathDiseaseImplications( pSoldier ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 3575c36e..b33956b2 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -98,6 +98,7 @@ #include "Auto Bandage.h" // added by Flugente #include "Facilities.h" // added by Flugente #include "Cheats.h" // added by Flugente +#include "MilitiaIndividual.h" // added by Flugente #endif #include "ub_config.h" @@ -1022,6 +1023,8 @@ SOLDIERTYPE& SOLDIERTYPE::operator=(const OLDSOLDIERTYPE_101& src) this->wornSnowCamo = __min( (100 - gGameExternalOptions.bCamoKitArea), src.wornSnowCamo ); this->bScopeMode = USE_BEST_SCOPE; + + this->ubMilitiaAssists = 0; this->bFoodLevel = 0; this->bDrinkLevel = 0; @@ -1029,6 +1032,7 @@ SOLDIERTYPE& SOLDIERTYPE::operator=(const OLDSOLDIERTYPE_101& src) this->usStarveDamageStrength = 0; this->bAIIndex = 0; this->usSoldierProfile = 0; + this->usIndividualMilitiaID = 0; this->usAISkillUse = 0; for ( UINT8 i = 0; i < SOLDIER_COUNTER_MAX; ++i ) this->usSkillCounter[i] = 0; @@ -16633,6 +16637,12 @@ STR16 SOLDIERTYPE::GetName( ) tmpname[tmpuser][0] = '\0'; wcscat( tmpname[tmpuser], this->name ); + MILITIA militia; + if ( GetMilitia( this->usIndividualMilitiaID, &militia ) ) + { + return militia.GetName( ); + } + if ( this->usSoldierProfile ) { INT8 type = this->GetSoldierProfileType( this->bTeam ); diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 24ada5c0..63306e9e 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1477,7 +1477,7 @@ public: /////////////////////////////////////////////////////// // Flugente: this was the location of required variables required for the now removed poison feature. They can be used again - INT8 bUnusedINT8_1; + UINT8 ubMilitiaAssists; // Flugente: stores militia assists INT8 bUnusedINT8_2; INT8 bUnusedINT8_3; @@ -1528,6 +1528,8 @@ public: UINT32 usSoldierFlagMask2; // anv: another usSoldierFlagMask + UINT32 usIndividualMilitiaID; // Flugente: if this is a militia, this is the ID of the militia data + #ifdef JA2UB //ja25 diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 6457f164..cafb6a5c 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -46,6 +46,7 @@ #include "Game Clock.h" // added this one - SANDRO #include "Interface.h" // added by Flugente #include "Soldier macros.h" // added by Flugente + #include "MilitiaIndividual.h" // added by Flugente #endif #include "connect.h" @@ -630,6 +631,12 @@ SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 * guiCurrentUniqueSoldierId++; + // Flugente: if this miltia, set individual ID + if ( tbTeam == MILITIA_TEAM ) + { + Soldier.usIndividualMilitiaID = GetIdOfUnusedindividualMilitia( pCreateStruct->ubSoldierClass, SECTOR( pCreateStruct->sSectorX, pCreateStruct->sSectorY ) ); + } + // OK, CHECK IF WE HAVE A VALID PROFILE ID! if ( pCreateStruct->ubProfile != NO_PROFILE ) { @@ -1526,7 +1533,13 @@ void GeneratePaletteForSoldier( SOLDIERTYPE *pSoldier, UINT8 ubSoldierClass, UIN //Choose hair color which uses the skin color to limit choices hair = ChooseHairColor( pSoldier->ubBodyType, skin ); - if ( pSoldier->usSoldierProfile ) + MILITIA militia; + if ( GetMilitia( pSoldier->usIndividualMilitiaID, &militia ) ) + { + skin = militia.skin; + hair = militia.hair; + } + else if ( pSoldier->usSoldierProfile ) { INT8 type = pSoldier->GetSoldierProfileType(ubTeam); @@ -1736,27 +1749,41 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT // silversurfer: Don't replace tanks! if ( !ARMED_VEHICLE( pCreateStruct ) ) { - // We have a function for this - INT8 type = pSoldier->GetSoldierProfileType( pCreateStruct->bTeam ); - - if ( type > -1 ) + MILITIA militia; + if ( GetMilitia( pSoldier->usIndividualMilitiaID, &militia ) ) { - INT16 availablenames[NUM_SOLDIER_PROFILES]; - UINT16 cnt = 0; + pSoldier->ubBodyType = militia.bodytype; - for (UINT16 i = 1; i < num_found_soldier_profiles[type]; ++i) + if ( gGameExternalOptions.fIndividualMilitia_ManageHealth ) { - // make sure the name isn't already currently in use! - if ( !IsProfileInUse(pCreateStruct->bTeam, type, i) ) - availablenames[cnt++] = i; + // make sure militia has at least OKLIFE + pSoldier->stats.bLife = max( OKLIFE, (militia.healthratio / 100.0f) * pSoldier->stats.bLifeMax ); } + } + else + { + // We have a function for this + INT8 type = pSoldier->GetSoldierProfileType( pCreateStruct->bTeam ); - if ( cnt > 0 ) + if ( type > -1 ) { - pSoldier->usSoldierProfile = availablenames[Random(cnt)]; + INT16 availablenames[NUM_SOLDIER_PROFILES]; + UINT16 cnt = 0; + + for (UINT16 i = 1; i < num_found_soldier_profiles[type]; ++i) + { + // make sure the name isn't already currently in use! + if ( !IsProfileInUse(pCreateStruct->bTeam, type, i) ) + availablenames[cnt++] = i; + } + + if ( cnt > 0 ) + { + pSoldier->usSoldierProfile = availablenames[Random(cnt)]; - if ( zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType > 0 && zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType < 5 ) - pSoldier->ubBodyType = zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType - 1; + if ( zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType > 0 && zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType < 5 ) + pSoldier->ubBodyType = zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType - 1; + } } } } diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index c3a90eb0..ebf3c8d8 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -41,9 +41,9 @@ enum enum { - WHITEHEAD, - BLACKHEAD, //black skin (only this line ) BROWNHEAD, //dark skin (this line plus all above) + BLACKHEAD, //black skin (only this line ) + WHITEHEAD, BLONDEHEAD, REDHEAD, //pink/tan skin (this line plus all above ) NUMHEADS diff --git a/Tactical/Tactical_VS2005.vcproj b/Tactical/Tactical_VS2005.vcproj index 860370bc..9e4db2f6 100644 --- a/Tactical/Tactical_VS2005.vcproj +++ b/Tactical/Tactical_VS2005.vcproj @@ -1192,6 +1192,10 @@ RelativePath=".\XML_Merge.cpp" > + + diff --git a/Tactical/Tactical_VS2008.vcproj b/Tactical/Tactical_VS2008.vcproj index 0b7299a9..27f9af60 100644 --- a/Tactical/Tactical_VS2008.vcproj +++ b/Tactical/Tactical_VS2008.vcproj @@ -1190,6 +1190,10 @@ RelativePath="XML_MercStartingGear.cpp" > + + diff --git a/Tactical/Tactical_VS2010.vcxproj b/Tactical/Tactical_VS2010.vcxproj index 90f75dfe..e47695cb 100644 --- a/Tactical/Tactical_VS2010.vcxproj +++ b/Tactical/Tactical_VS2010.vcxproj @@ -235,6 +235,7 @@ + diff --git a/Tactical/Tactical_VS2010.vcxproj.filters b/Tactical/Tactical_VS2010.vcxproj.filters index b3502754..1ea1a0be 100644 --- a/Tactical/Tactical_VS2010.vcxproj.filters +++ b/Tactical/Tactical_VS2010.vcxproj.filters @@ -617,6 +617,9 @@ Source Files + + Source Files + Source Files diff --git a/Tactical/Tactical_VS2013.vcxproj b/Tactical/Tactical_VS2013.vcxproj index b5e0ed1c..6622f47e 100644 --- a/Tactical/Tactical_VS2013.vcxproj +++ b/Tactical/Tactical_VS2013.vcxproj @@ -236,6 +236,7 @@ + @@ -248,7 +249,7 @@ - + diff --git a/Tactical/Tactical_VS2013.vcxproj.filters b/Tactical/Tactical_VS2013.vcxproj.filters index a44fe27b..d5d63b26 100644 --- a/Tactical/Tactical_VS2013.vcxproj.filters +++ b/Tactical/Tactical_VS2013.vcxproj.filters @@ -602,10 +602,10 @@ Source Files - + Source Files - + Source Files @@ -698,5 +698,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Tactical/XML.h b/Tactical/XML.h index 34ab93b1..ff7b209f 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -250,6 +250,7 @@ typedef PARSE_STAGE; #define VEHICLESFILENAME "Vehicles.xml" #define SQUADNAMEFILENAME "SquadNames.xml" #define BACKGROUNDSFILENAME "Backgrounds.xml" +#define INDIVIDUALMILITIAFILENAME "MilitiaIndividual.xml" #define CAMPAIGNSTATSEVENTSFILENAME "CampaignStatsEvents.xml" #define DISEASEFILENAME "Disease.xml" @@ -518,6 +519,10 @@ extern void LoadIMPPortraitsTEMP(); extern BOOLEAN ReadInBackgrounds(STR fileName, BOOLEAN localizedVersion ); extern BOOLEAN WriteBackgrounds( STR fileName); +// Flugente: individual militia +extern BOOLEAN ReadInMilitiaIndividual( STR fileName, BOOLEAN localizedVersion ); +extern BOOLEAN WriteMilitiaIndividual( STR fileName ); + // Flugente: campaign stats events extern BOOLEAN ReadInCampaignStatsEvents(STR fileName, BOOLEAN localizedVersion ); extern BOOLEAN WriteCampaignStatsEvents( STR fileName); diff --git a/Tactical/XML_MilitiaIndividual.cpp b/Tactical/XML_MilitiaIndividual.cpp new file mode 100644 index 00000000..7b4a68d8 --- /dev/null +++ b/Tactical/XML_MilitiaIndividual.cpp @@ -0,0 +1,308 @@ +/** +* @file +* @author Flugente (bears-pit.com) +*/ + +#ifdef PRECOMPILEDHEADERS +#include "Tactical All.h" +#else +#include "sgp.h" +#include "Debug Control.h" +#include "expat.h" +#include "XML.h" +#include "MilitiaIndividual.h" +#endif + +struct +{ + PARSE_STAGE curElement; + + CHAR8 szCharData[MAX_CHAR_DATA_LENGTH + 1]; + MilitiaOriginData curBackground; + MilitiaOriginData * curArray; + + UINT32 maxArraySize; + UINT32 curIndex; + UINT32 currentDepth; + UINT32 maxReadDepth; + CHAR16 gzBackground[MAX_ENEMY_NAMES_CHARS]; +} +typedef parseData; + +BOOLEAN localizedTextOnly_MI; + +static void XMLCALL +militiaindividualStartElementHandle( void *userData, const XML_Char *name, const XML_Char **atts ) +{ + parseData * pData = (parseData *)userData; + + if ( pData->currentDepth <= pData->maxReadDepth ) //are we reading this element? + { + if ( strcmp( name, "MILITIAORIGINDATA" ) == 0 && pData->curElement == ELEMENT_NONE ) + { + pData->curElement = ELEMENT_LIST; + + if ( !localizedTextOnly_MI ) + memset( pData->curArray, 0, sizeof(MilitiaOriginData)*pData->maxArraySize ); + + pData->maxReadDepth++; //we are not skipping this element + } + else if ( strcmp( name, "ORIGINDATA" ) == 0 && pData->curElement == ELEMENT_LIST ) + { + pData->curElement = ELEMENT; + + if ( !localizedTextOnly_MI ) + memset( &pData->curBackground, 0, sizeof(MilitiaOriginData) ); + + pData->maxReadDepth++; //we are not skipping this element + } + else if ( pData->curElement == ELEMENT && + (strcmp( name, "male_forename" ) == 0 || + strcmp( name, "male_surname" ) == 0 || + strcmp( name, "female_forename" ) == 0 || + strcmp( name, "female_surname" ) == 0 || + strcmp( name, "chance_bodytype_REGMALE" ) == 0 || + strcmp( name, "chance_bodytype_BIGMALE" ) == 0 || + strcmp( name, "chance_bodytype_STOCKYMALE" ) == 0 || + strcmp( name, "chance_bodytype_REGFEMALE" ) == 0 || + strcmp( name, "chance_skin_PINKSKIN" ) == 0 || + strcmp( name, "chance_skin_TANSKIN" ) == 0 || + strcmp( name, "chance_skin_DARKSKIN" ) == 0 || + strcmp( name, "chance_skin_BLACKSKIN" ) == 0 || + strcmp( name, "dailycost_green" ) == 0 || + strcmp( name, "dailycost_regular" ) == 0 || + strcmp( name, "dailycost_elite" ) == 0) ) + { + pData->curElement = ELEMENT_PROPERTY; + + pData->maxReadDepth++; //we are not skipping this element + } + + pData->szCharData[0] = '\0'; + } + + pData->currentDepth++; +} + +static void XMLCALL +militiaindividualCharacterDataHandle( void *userData, const XML_Char *str, int len ) +{ + parseData * pData = (parseData *)userData; + + if ( (pData->currentDepth <= pData->maxReadDepth) && + (strlen( pData->szCharData ) < MAX_CHAR_DATA_LENGTH) + ){ + strncat( pData->szCharData, str, __min( (unsigned int)len, MAX_CHAR_DATA_LENGTH - strlen( pData->szCharData ) ) ); + } +} + +static void XMLCALL +militiaindividualEndElementHandle( void *userData, const XML_Char *name ) +{ + parseData * pData = (parseData *)userData; + + if ( pData->currentDepth <= pData->maxReadDepth ) + { + if ( strcmp( name, "MILITIAORIGINDATA" ) == 0 ) + { + pData->curElement = ELEMENT_NONE; + } + else if ( strcmp( name, "ORIGINDATA" ) == 0 ) + { + pData->curElement = ELEMENT_LIST; + + if ( pData->curIndex < pData->maxArraySize ) + { + pData->curArray[pData->curIndex] = pData->curBackground; + } + + ++pData->curIndex; + } + else if ( strcmp( name, "male_forename" ) == 0 ) + { + pData->curElement = ELEMENT; + + CHAR16 bla[30]; + + MultiByteToWideChar( CP_UTF8, 0, pData->szCharData, -1, bla, sizeof(bla) / sizeof(bla[0]) ); + bla[sizeof(bla) / sizeof(bla[0]) - 1] = '\0'; + + pData->curBackground.szMale_Forename.push_back( bla ); + } + else if ( strcmp( name, "male_surname" ) == 0 ) + { + pData->curElement = ELEMENT; + + CHAR16 bla[30]; + + MultiByteToWideChar( CP_UTF8, 0, pData->szCharData, -1, bla, sizeof(bla) / sizeof(bla[0]) ); + bla[sizeof(bla) / sizeof(bla[0]) - 1] = '\0'; + + pData->curBackground.szMale_Surname.push_back( bla ); + } + else if ( strcmp( name, "female_forename" ) == 0 ) + { + pData->curElement = ELEMENT; + + CHAR16 bla[30]; + + MultiByteToWideChar( CP_UTF8, 0, pData->szCharData, -1, bla, sizeof(bla) / sizeof(bla[0]) ); + bla[sizeof(bla) / sizeof(bla[0]) - 1] = '\0'; + + pData->curBackground.szFemale_Forename.push_back( bla ); + } + else if ( strcmp( name, "female_surname" ) == 0 ) + { + pData->curElement = ELEMENT; + + CHAR16 bla[30]; + + MultiByteToWideChar( CP_UTF8, 0, pData->szCharData, -1, bla, sizeof(bla) / sizeof(bla[0]) ); + bla[sizeof(bla) / sizeof(bla[0]) - 1] = '\0'; + + pData->curBackground.szFemale_Surname.push_back( bla ); + } + else if ( strcmp( name, "chance_bodytype_REGMALE" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_bodytype[REGMALE] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_bodytype_BIGMALE" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_bodytype[BIGMALE] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_bodytype_STOCKYMALE" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_bodytype[STOCKYMALE] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_bodytype_REGFEMALE" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_bodytype[REGFEMALE] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_skin_PINKSKIN" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_skin[PINKSKIN] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_skin_TANSKIN" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_skin[TANSKIN] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_skin_DARKSKIN" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_skin[DARKSKIN] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "chance_skin_BLACKSKIN" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.chance_skin[BLACKSKIN] = min( 100, max( 0, (UINT8)atol( pData->szCharData ) ) ); + } + else if ( strcmp( name, "dailycost_green" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.dailycost[GREEN_MILITIA] = (UINT16)atol( pData->szCharData ); + } + else if ( strcmp( name, "dailycost_regular" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.dailycost[REGULAR_MILITIA] = (UINT16)atol( pData->szCharData ); + } + else if ( strcmp( name, "dailycost_elite" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curBackground.dailycost[ELITE_MILITIA] = (UINT16)atol( pData->szCharData ); + } + + pData->maxReadDepth--; + } + pData->currentDepth--; +} + +BOOLEAN ReadInMilitiaIndividual( STR fileName, BOOLEAN localizedVersion ) +{ + HWFILE hFile; + UINT32 uiBytesRead; + UINT32 uiFSize; + CHAR8 * lpcBuffer; + XML_Parser parser = XML_ParserCreate( NULL ); + + parseData pData; + + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Loading MilitiaIndividual.xml" ); + + localizedTextOnly_MI = localizedVersion; + + // Open file + hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ); + if ( !hFile ) + return(FALSE); + + uiFSize = FileGetSize( hFile ); + lpcBuffer = (CHAR8 *)MemAlloc( uiFSize + 1 ); + + //Read in block + if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) ) + { + MemFree( lpcBuffer ); + return(FALSE); + } + + lpcBuffer[uiFSize] = 0; //add a null terminator + + FileClose( hFile ); + + XML_SetElementHandler( parser, militiaindividualStartElementHandle, militiaindividualEndElementHandle ); + XML_SetCharacterDataHandler( parser, militiaindividualCharacterDataHandle ); + + memset( &pData, 0, sizeof(pData) ); + pData.curArray = gMilitiaOriginData; + pData.maxArraySize = MO_MAX; + + XML_SetUserData( parser, &pData ); + + if ( !XML_Parse( parser, lpcBuffer, uiFSize, TRUE ) ) + { + CHAR8 errorBuf[511]; + + sprintf( errorBuf, "XML Parser Error in MilitiaIndividual.xml: %s at line %d", XML_ErrorString( XML_GetErrorCode( parser ) ), XML_GetCurrentLineNumber( parser ) ); + LiveMessage( errorBuf ); + + MemFree( lpcBuffer ); + return FALSE; + } + + MemFree( lpcBuffer ); + + XML_ParserFree( parser ); + + return(TRUE); +} + + +BOOLEAN WriteMilitiaIndividual( STR fileName ) +{ + //Debug code; make sure that what we got from the file is the same as what's there + // Open a new file + HWFILE hFile = FileOpen( fileName, FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE ); + if ( !hFile ) + return(FALSE); + + { + FilePrintf( hFile, "\r\n" ); + for ( UINT32 cnt = 0; cnt < NUM_BACKGROUND; ++cnt ) + { + FilePrintf( hFile, "\t\r\n" ); + FilePrintf( hFile, "\t\t%d\r\n", cnt ); + + } + FilePrintf( hFile, "\r\n" ); + } + FileClose( hFile ); + + return(TRUE); +} diff --git a/TacticalAI/NPC.cpp b/TacticalAI/NPC.cpp index 8a3bea84..4da8e15a 100644 --- a/TacticalAI/NPC.cpp +++ b/TacticalAI/NPC.cpp @@ -55,6 +55,7 @@ #include "Map Screen Helicopter.h" #include "Drugs And Alcohol.h" // added by Flugente for DoesMercHavePersonality( ... ) + #include "SaveLoadGame.h" #ifdef JA2UB #include "Ja25_Tactical.h" @@ -3223,8 +3224,6 @@ BOOLEAN SaveNPCInfoToSaveGameFile( HWFILE hFile ) return( TRUE ); } -extern INT32 ReadFieldByField(HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount); - BOOLEAN LoadNPCInfoFromSavedGameFile( HWFILE hFile, UINT32 uiSaveGameVersion ) { UINT32 uiNumBytesRead=0; diff --git a/Utils/Text.h b/Utils/Text.h index f58b8e0a..bdf7c506 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -2935,6 +2935,30 @@ extern STR16 szEnemyHeliText[]; // Flugente: fortification extern STR16 szFortificationText[]; +// Flugente: militia website +enum +{ + // main page + TEXT_MILITIAWEBSITE_WEBSITENAME, + TEXT_MILITIAWEBSITE_SLOGAN, + + // links to other pages + TEXT_MILITIAWEBSITE_SUBSITE1, + + TEXT_MILITIAWEBSITE_MAIN1 = TEXT_PMC_SUBSITE1 + 3, + + TEXT_MILITIAWEBSITE_MAX, +}; + +extern STR16 szMilitiaWebSite[]; +extern STR16 szIndividualMilitiaBattleReportText[]; +extern STR16 szIndividualMilitiaTraitRequirements[]; +extern STR16 szIdividualMilitiaWebsiteText[]; +extern STR16 szIdividualMilitiaWebsiteFilterText_Dead[]; +extern STR16 szIdividualMilitiaWebsiteFilterText_Rank[] ; +extern STR16 szIdividualMilitiaWebsiteFilterText_Origin[]; +extern STR16 szIdividualMilitiaWebsiteFilterText_Sector[]; + #define TACTICAL_INVENTORY_DIALOG_NUM 16 #define TACTICAL_COVER_DIALOG_NUM 16 diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 1b34242c..1e2720a9 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -4979,6 +4979,7 @@ STR16 pBookMarkStrings[] = L"佣兵之家", //L"MeLoDY", L"世界卫生组织", //L"WHO",  L"Kerberus安保公司", //L"Kerberus", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5101,6 +5102,8 @@ STR16 pWebPagesTitles[] = L"Kerberus安保公司 - 关于我们", //L"Kerberus - About Us", L"Kerberus安保公司 - 雇佣队伍", //L"Kerberus - Hire a Team", L"Kerberus安保公司 - 独立协议", //L"Kerberus - Individual Contracts", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Bobby Ray - 最近的运货", L"百科全书", L"百科全书 - 数据", @@ -6895,7 +6898,8 @@ STR16 gzLaptopHelpText[] = L"战役历史", L"佣兵喜欢或不喜欢你", //L"Mercenaries Love or Dislike You", L"世界卫生组织", //L"World Health Organization", - L"Kerberus安保公司 - 最佳的安保工作", //L"Kerberus - Excellence In Security", + L"Kerberus - Experience In Security", + L"Militia Overview", // TODO.Translate }; @@ -10883,6 +10887,118 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + // WANNE: Some Chinese specific strings that needs to be in unicode! STR16 ChineseSpecString1 = L"%%"; //defined in _ChineseText.cpp as this file is already unicode STR16 ChineseSpecString2 = L"*%3d%%%%"; //defined in _ChineseText.cpp as this file is already unicode diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index b9db5c13..2adf0241 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -4983,6 +4983,7 @@ STR16 pBookMarkStrings[] = L"MeLoDY", L"WHO", L"Kerberus", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5102,6 +5103,11 @@ STR16 pWebPagesTitles[] = L"WHO - About WHO", L"WHO - Disease in Arulco", L"WHO - Helpful Tips", + L"Kerberus - About Us", + L"Kerberus - Hire a Team", + L"Kerberus - Individual Contracts", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Bobby Ray's - Recentelijke Zendingen", L"Encyclopedia", L"Encyclopedia - Data", @@ -6906,6 +6912,7 @@ STR16 gzLaptopHelpText[] = L"Mercenaries Love or Dislike You", // TODO.Translate L"World Health Organization", L"Kerberus - Excellence In Security", + L"Militia Overview", // TODO.Translate }; @@ -10897,4 +10904,116 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 8ab6fe9b..dc5bf20f 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -4976,6 +4976,7 @@ STR16 pBookMarkStrings[] = L"MeLoDY", L"WHO", L"Kerberus", + L"Militia Overview", }; STR16 pBookmarkTitle[] = @@ -5098,6 +5099,8 @@ STR16 pWebPagesTitles[] = L"Kerberus - About Us", L"Kerberus - Hire a Team", L"Kerberus - Individual Contracts", + L"Militia Roster", + L"Militia Statistics", L"Bobby Ray's - Recent Shipments", L"Encyclopedia", L"Encyclopedia - Data", @@ -6892,7 +6895,8 @@ STR16 gzLaptopHelpText[] = L"Campaign History", L"Mercenaries Love or Dislike You", L"World Health Organization", - L"Kerberus - Excellence In Security", + L"Kerberus - Experience In Security", + L"Militia Overview", }; @@ -10935,4 +10939,115 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 2c917c60..9242a1f1 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -4988,6 +4988,7 @@ STR16 pBookMarkStrings[] = L"MeLoDY", L"WHO", L"Kerberus", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5110,6 +5111,8 @@ STR16 pWebPagesTitles[] = L"Kerberus - About Us", L"Kerberus - Hire a Team", L"Kerberus - Individual Contracts", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Bobby Ray : Dernières commandes", L"Encyclopédie", L"Encyclopédie : Données", @@ -6905,7 +6908,8 @@ STR16 gzLaptopHelpText[] = L"Comptes rendus", L"Mercenaries Love or Dislike You", // TODO.Translate L"World Health Organization", - L"Kerberus - Excellence In Security", + L"Kerberus - Experience In Security", + L"Militia Overview", // TODO.Translate }; @@ -10882,4 +10886,116 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 4a3ef24a..748e8085 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -4932,6 +4932,7 @@ STR16 pBookMarkStrings[] = L"MeLoDY", L"WHO", L"Kerberus", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5046,6 +5047,8 @@ STR16 pWebPagesTitles[] = L"Kerberus - About Us", L"Kerberus - Hire a Team", L"Kerberus - Individual Contracts", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Bobby Rays - Letzte Lieferungen", L"Enzyklopädie", L"Enzyklopädie - Daten", @@ -6738,7 +6741,8 @@ STR16 gzLaptopHelpText[] = L"Geschichte", L"Mercenaries Love or Dislike You", // TODO.Translate L"World Health Organization", - L"Kerberus - Excellence In Security", + L"Kerberus - Experience In Security", + L"Militia Overview", // TODO.Translate }; STR16 gzHelpScreenText[] = @@ -10712,4 +10716,116 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 769943f8..399cd8f0 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -4970,6 +4970,7 @@ STR16 pBookMarkStrings[] = L"MeLoDY", L"WHO", L"Kerberus", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5092,6 +5093,8 @@ STR16 pWebPagesTitles[] = L"Kerberus - About Us", L"Kerberus - Hire a Team", L"Kerberus - Individual Contracts", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Bobby Ray - Spedizioni recenti", L"Encyclopedia", L"Encyclopedia - Data", @@ -6892,7 +6895,8 @@ STR16 gzLaptopHelpText[] = L"Campaign History", // TODO.Translate L"Mercenaries Love or Dislike You", // TODO.Translate L"World Health Organization", - L"Kerberus - Excellence In Security", + L"Kerberus - Experience In Security", + L"Militia Overview", // TODO.Translate }; @@ -10891,4 +10895,116 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index a2a11007..cf975f0a 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -4985,6 +4985,7 @@ STR16 pBookMarkStrings[] = L"MeLoDY", L"WHO", L"Kerberus", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5107,6 +5108,8 @@ STR16 pWebPagesTitles[] = L"Kerberus - About Us", L"Kerberus - Hire a Team", L"Kerberus - Individual Contracts", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Bobby Ray's - Ostatnie dostawy", L"Encyclopedia", L"Encyclopedia - Dane", @@ -6906,7 +6909,8 @@ STR16 gzLaptopHelpText[] = L"Campaign History", // TODO.Translate L"Mercenaries Love or Dislike You", // TODO.Translate L"World Health Organization", - L"Kerberus - Excellence In Security", + L"Kerberus - Experience In Security", + L"Militia Overview", // TODO.Translate }; @@ -10904,4 +10908,116 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 2d6b6574..8d373680 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -4976,6 +4976,7 @@ STR16 pBookMarkStrings[] = L"БоЛТиК", L"ВОЗ", L"Цербер", + L"Militia Overview", // TODO.Translate }; STR16 pBookmarkTitle[] = @@ -5098,6 +5099,8 @@ STR16 pWebPagesTitles[] = L"Цербер", L"Цербер - Найм команды", L"Цербер - Индивидуальные контракты", + L"Militia Roster", // TODO.Translate + L"Militia Statistics", L"Бобби Рэй - последние поступления", L"Энциклопедия", L"Энциклопедия - данные", @@ -6892,7 +6895,8 @@ STR16 gzLaptopHelpText[] = L"События и факты. Арулькийские новости", L"Бойцы Любят Тебя или Клянут", L"Всемирная организация здравоохранения", - L"Подготовка сотрудников службы безопасности", + L"Kerberus - Experience In Security", + L"Militia Overview", // TODO.Translate }; @@ -10924,7 +10928,7 @@ STR16 szEnemyHeliText[] = L"База ПВО в %s обстреляла вражеский вертолёт в %s.", }; -STR16 szFortificationText[] = +STR16 szFortificationText[] = // TODO.Translate { L"No valid structure selected, nothing added to build plan.", L"No gridno found to create items in %s - created items are lost.", @@ -10935,4 +10939,116 @@ STR16 szFortificationText[] = L"Tileset %d: %s", }; +STR16 szMilitiaWebSite[] = +{ + // main page + L"Militia", + L"Militia Forces Overview", + + // links to other pages + L"Militia Roster", + L"Statistics", + L"About", + +}; + +STR16 szIndividualMilitiaBattleReportText[] = +{ + L"Took part in Operation %s", + L"Recruited on Day %d, %d:%2d in %s", + L"Promoted on Day %d, %d:%2d", + L"KIA, Operation %s", + + L"Lightly wounded during Operation %s", + L"Heavily wounded during Operation %s", + L"Critically wounded during Operation %s", + L"Valiantly fought in Operation %s", + + L"Hired from Kerberus on Day %d, %d:%2d in %s", + L"Defected to us on Day %d, %d:%2d in %s", + L"Contract terminated on Day %d, %d:%2d", +}; + +STR16 szIndividualMilitiaTraitRequirements[] = +{ + L"HP", + L"AGI", + L"DEX", + L"STR", + + L"LDR", + L"MRK", + L"MEC", + L"EXP", + + L"MED", + L"WIS", + L"\nMust have regular or elite rank", + L"\nMust have elite rank", + + L"\n\n|F|u|l|f|i|l|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n\n|F|a|i|l|e|d |R|e|q|u|i|r|e|m|e|n|t|s", + L"\n%d %s", + L"\nBasic version of trait", + + L" (Expert)" +}; + +STR16 szIdividualMilitiaWebsiteText[] = +{ + L"Operations", + L"Are you sure you want to release %s from your service?", + L"HP ratio: %3.0f %% Daily Wage: %3d$ Age: %d years", + L"Daily Wage: %3d$ Age: %d years", + + L"Kills: %d Assists: %d", + L"Status: Deceased", + L"Status: Fired", + L"Status: Active", + + L"Terminate Contract", + L"Personal Data", + L"Service Record", + L"Inventory", + + L"Militia name", + L"Sector name", + L"Weapon", + L"HP ratio: %3.1f%%%%%%%", + + L"%s is not active in the currently loaded sector.", + L"%s has been promoted to regular militia", + L"%s has been promoted to elite militia", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Dead[] = +{ + L"All statuses", + L"Deceased", + L"Active", + L"Fired", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Rank[] = +{ + L"All ranks", + L"Green", + L"Regular", + L"Elite", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Origin[] = +{ + L"All origins", + L"Rebel", + L"PMC", + L"Defector", +}; + +STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = +{ + L"All sectors", +}; + + #endif //RUSSIAN