//Strategic AI Viewer #include "builddefines.h" #include #include "types.h" #include "english.h" #include "Timer Control.h" #include "vsurface.h" #include "Button System.h" #include "Font Control.h" #include "line.h" #include "input.h" #include "vobject_blitters.h" #include "Text Input.h" #include "mousesystem.h" #include "strategicmap.h" #include "FileMan.h" #include "Render Dirty.h" #include "Game Clock.h" #include "Campaign Types.h" #include "Queen Command.h" #include "Overhead.h" #include "Strategic Movement.h" #include "GameSettings.h" #include "Game Event Hook.h" #include "Creature Spreading.h" #include "message.h" #include "Game Init.h" #include "Strategic AI.h" #include "Campaign.h" #include "Auto Resolve.h" #include "Strategic Status.h" #include "WordWrap.h" #include "Town Militia.h" // added by Flugente #ifdef JA2BETAVERSION #define VIEWER_LEFT 15 #define VIEWER_TOP 15 #define VIEWER_WIDTH 417 #define VIEWER_HEIGHT 353 #define VIEWER_CELLW 26 #define VIEWER_CELLH 22 #define VIEWER_RIGHT (VIEWER_LEFT+VIEWER_WIDTH) #define VIEWER_BOTTOM (VIEWER_TOP+VIEWER_HEIGHT) enum{ VIEWER_EXIT, VIEWER_TIMEPANEL, VIEWER_RESET, RESET_EASY, RESET_NORMAL, RESET_HARD, COMPRESSION0, COMPRESSION5, COMPRESSION15, COMPRESSION60, //COMPRESSION6H, TEST_INCOMING_4SIDES, START_CREATURE_QUEST, SPREAD_CREATURES, CREATURE_ATTACK, VIEW_ENEMIES, VIEW_CREATURES, BASEMENT1_BTN, BASEMENT2_BTN, BASEMENT3_BTN, RELOAD_SECTOR, QUEEN_AWAKE_TOGGLE, NUM_VIEWER_BUTTONS }; enum { MOVE_RED_ICON, MOVE_BLUE_ICON, MOVE_YELLOW_ICON, MOVE_GRAY_ICON, MOVE_BURGUNDY_ICON, MOVE_ORANGE_ICON, MOVE_GREEN_ICON, STOP_RED_ICON, STOP_BLUE_ICON, STOP_YELLOW_ICON, STOP_GRAY_ICON, STOP_BURGUNDY_ICON, STOP_ORANGE_ICON, STOP_GREEN_ICON, REIN_RED_ICON, REIN_BLUE_ICON, REIN_YELLOW_ICON, REIN_GRAY_ICON, REIN_BURGUNDY_ICON, REIN_ORANGE_ICON, REIN_GREEN_ICON, STAGE_RED_ICON, STAGE_BLUE_ICON, STAGE_YELLOW_ICON, STAGE_GRAY_ICON, STAGE_BURGUNDY_ICON, STAGE_ORANGE_ICON, STAGE_GREEN_ICON, SAM_ICON, MINING_ICON, GROUP_ANCHOR, }; enum { ICON_TYPE_PATROL, ICON_TYPE_STOPPED, ICON_TYPE_REINFORCEMENT, ICON_TYPE_ASSAULT, NUM_ICON_TYPES }; enum { ICON_COLOR_RED, ICON_COLOR_BLUE, ICON_COLOR_YELLOW, ICON_COLOR_GRAY, ICON_COLOR_BURGUNDY, ICON_COLOR_ORANGE, ICON_COLOR_GREEN, NUM_ICON_COLORS }; void ClearViewerRegion( INT16 sLeft, INT16 sTop, INT16 sRight, INT16 sBottom ); void HandleViewerInput(); void RenderViewer(); void ViewerMapMoveCallback( MOUSE_REGION *reg, INT32 reason ); void ViewerMapClickCallback( MOUSE_REGION *reg, INT32 reason ); void ViewerExitCallback( GUI_BUTTON *btn, INT32 reason ); void Compression0Callback( GUI_BUTTON *btn, INT32 reason ); void Compression5Callback( GUI_BUTTON *btn, INT32 reason ); void Compression15Callback( GUI_BUTTON *btn, INT32 reason ); void Compression60Callback( GUI_BUTTON *btn, INT32 reason ); void Compression6HCallback( GUI_BUTTON *btn, INT32 reason ); void EasyCallback( GUI_BUTTON *btn, INT32 reason ); void NormalCallback( GUI_BUTTON *btn, INT32 reason ); void HardCallback( GUI_BUTTON *btn, INT32 reason ); void TestIncoming4SidesCallback( GUI_BUTTON *btn, INT32 reason ); void StartCreatureQuestCallback( GUI_BUTTON *btn, INT32 reason ); void SpreadCreaturesCallback( GUI_BUTTON *btn, INT32 reason ); void CreatureAttackCallback( GUI_BUTTON *btn, INT32 reason ); void B1Callback( GUI_BUTTON *btn, INT32 reason ); void B2Callback( GUI_BUTTON *btn, INT32 reason ); void B3Callback( GUI_BUTTON *btn, INT32 reason ); void ReloadSectorCallback( GUI_BUTTON *btn, INT32 reason ); void ToggleQueenAwake( GUI_BUTTON *btn, INT32 reason ); void ViewEnemiesCallback( GUI_BUTTON *btn, INT32 reason ); void ViewCreaturesCallback( GUI_BUTTON *btn, INT32 reason ); void ExtractAndUpdatePopulations(); void PrintEnemyPopTable(); void PrintEnemiesKilledTable(); UINT8 ChooseEnemyIconColor( UINT16 ubAdmins, UINT16 ubTroops, UINT16 ubElites, UINT16 ubTanks, UINT16 ubJeeps ); void BlitGroupIcon( UINT8 ubIconType, UINT8 ubIconColor, UINT32 uiX, UINT32 uiY, HVOBJECT hVObject ); void PrintDetailedEnemiesInSectorInfo( INT32 iScreenX, INT32 iScreenY, UINT8 ubSectorX, UINT8 ubSectorY ); MOUSE_REGION ViewerRegion; UINT32 guiMapGraphicID; UINT32 guiMapIconsID; BOOLEAN gfViewerEntry; BOOLEAN gfExitViewer; BOOLEAN gfRenderViewer; BOOLEAN gfRenderMap; BOOLEAN gfViewEnemies = TRUE; INT8 gbViewLevel = 0; UINT16 gusBlue; UINT16 gusLtBlue; UINT16 gusDkBlue; INT16 gsAINumAdmins = -1; INT16 gsAINumTroops = -1; INT16 gsAINumElites = -1; INT16 gsAINumTanks = -1; INT16 gsAINumCreatures = -1; BOOLEAN gfOverrideSector = FALSE; UINT32 guiLastTime; INT32 giSaveTCMode; //time compression mode; //The sector coordinates of the mouse position (yellow) INT16 gsHiSectorX = 0; INT16 gsHiSectorY = 0; //The sector coordinates of the selected sector (red) INT16 gsSelSectorX = 0, gsSelSectorY = 0; INT32 iViewerButton[ NUM_VIEWER_BUTTONS ]; extern BOOLEAN gfQueenAIAwake; extern INT32 giReinforcementPool; extern UINT32 guiEventListCurrNodes, guiEventListPeekNodes; extern INT32 giReinforcementPoints, giRequestPoints; extern ARMY_COMPOSITION gArmyComp[ MAX_ARMY_COMPOSITIONS ]; extern GARRISON_GROUP *gGarrisonGroup; extern INT32 giGarrisonArraySize; extern void Ensure_RepairedGarrisonGroup( GARRISON_GROUP **ppGarrison, INT32 *pGarraySize ); CHAR16 gwGroupTypeString[ NUM_ENEMY_INTENTIONS ][ 20 ] = { L"RETREAT", L"ASSAULT", L"STAGING", L"PATROL", L"REINFORCE" }; void StringFromValue( STR16 str, INT32 iValue, UINT32 uiMax ) { if( iValue < 0 ) //a blank string is determined by a negative value. str[0] = '\0'; else if( (UINT32)iValue > uiMax ) //higher than max attribute value, so convert it to the max. swprintf( str, L"%d", uiMax ); else //this is a valid static value, so convert it to a string. swprintf( str, L"%d", iValue ); } BOOLEAN CreateAIViewer() { VOBJECT_DESC VObjectDesc; CHAR16 str[6]; //Kaiden: Loading INI file to read Values... // Here's another one of those INI reads // killing the Map Editor> //CIniReader iniReader("..\\Ja2_Options.ini"); //INT32 iMaxEnemyGroupSize = iniReader.ReadInteger("Options","MAX_STRATEGIC_TEAM_SIZE",20); //Check to see if data exists. if( !FileExists( "DevTools\\arulco.sti" ) || !FileExists( "DevTools\\icons.sti" ) || !FileExists( "DevTools\\SmCheckbox.sti" ) ) { ScreenMsg( FONT_WHITE, MSG_BETAVERSION, L"AIViewer missing data. Aborted." ); gfExitViewer = FALSE; gfViewerEntry = TRUE; return FALSE; } DisableScrollMessages(); giSaveTCMode = giTimeCompressMode; VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; sprintf( VObjectDesc.ImageFile, "DevTools\\arulco.sti" ); if( !AddVideoObject( &VObjectDesc, &guiMapGraphicID ) ) AssertMsg( 0, "Failed to load data\\DevTools\\arulco.sti" ); sprintf( VObjectDesc.ImageFile, "DevTools\\icons.sti" ); if( !AddVideoObject( &VObjectDesc, &guiMapIconsID ) ) AssertMsg( 0, "Failed to load data\\DevTools\\icons.sti" ); gfRenderViewer = TRUE; //Create all of the buttons here iViewerButton[ VIEWER_EXIT ] = CreateTextButton(L"Exit", BLOCKFONT2, FONT_RED, FONT_BLACK, BUTTON_USE_DEFAULT, 585, 425, 50, 30, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, ViewerExitCallback ); iViewerButton[ VIEWER_TIMEPANEL ] = CreateTextButton( WORLDTIMESTR, FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 3, 0, 88, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, BUTTON_NO_CALLBACK, BUTTON_NO_CALLBACK ); DisableButton( iViewerButton[ VIEWER_TIMEPANEL ] ); SpecifyDisabledButtonStyle( iViewerButton[ VIEWER_TIMEPANEL ], DISABLED_STYLE_NONE ); iViewerButton[ COMPRESSION0 ] = CreateTextButton( L"0", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 3, 20, 17, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, Compression0Callback ); iViewerButton[ COMPRESSION5 ] = CreateTextButton( L"5", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 20, 20, 17, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, Compression5Callback ); iViewerButton[ COMPRESSION15 ] = CreateTextButton( L"15", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 37, 20, 18, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, Compression15Callback ); iViewerButton[ COMPRESSION60 ] = CreateTextButton( L"60", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 55, 20, 18, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, Compression60Callback ); /* iViewerButton[ COMPRESSION6H ] = CreateTextButton( L"6H", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 73, 20, 18, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, Compression6HCallback ); */ iViewerButton[ VIEWER_RESET ] = CreateTextButton( L"Reset Enemies", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, 526, 0, 114, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, BUTTON_NO_CALLBACK, BUTTON_NO_CALLBACK ); DisableButton( iViewerButton[ VIEWER_RESET ] ); SpecifyDisabledButtonStyle( iViewerButton[ VIEWER_RESET ], DISABLED_STYLE_NONE ); iViewerButton[ RESET_EASY ] = CreateTextButton( L"Easy", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, 526, 20, 35, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, EasyCallback ); iViewerButton[ RESET_NORMAL ] = CreateTextButton( L"Normal", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, 561, 20, 44, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, NormalCallback ); iViewerButton[ RESET_HARD ] = CreateTextButton( L"Hard", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, 605, 20, 35, 16, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, HardCallback ); iViewerButton[ TEST_INCOMING_4SIDES ] = CreateTextButton( L"Incoming 4 Sides", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 20, 100, 120, 18, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, TestIncoming4SidesCallback ); iViewerButton[ START_CREATURE_QUEST ] = CreateTextButton( L"Start Creature Quest", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 20, 125, 120, 18, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, StartCreatureQuestCallback ); iViewerButton[ SPREAD_CREATURES ] = CreateTextButton( L"Spread Creatures", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 20, 150, 120, 18, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, SpreadCreaturesCallback ); iViewerButton[ CREATURE_ATTACK ] = CreateTextButton( L"Creature Attack", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 20, 175, 120, 18, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, CreatureAttackCallback ); iViewerButton[ QUEEN_AWAKE_TOGGLE ] = CreateCheckBoxButton( 104, VIEWER_BOTTOM + 22, "DevTools//SmCheckbox.sti", MSYS_PRIORITY_HIGH, ToggleQueenAwake ); if( gfQueenAIAwake ) { ButtonList[ iViewerButton[ QUEEN_AWAKE_TOGGLE ] ]->uiFlags |= BUTTON_CLICKED_ON; } iViewerButton[ RELOAD_SECTOR ] = CreateTextButton( L"Override Sector", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, 10, VIEWER_BOTTOM + 5, 90, 18, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, ReloadSectorCallback ); iViewerButton[ VIEW_ENEMIES ] = CreateTextButton( L"View Enemies", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 13, 40, 90, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, ViewEnemiesCallback ); iViewerButton[ VIEW_CREATURES ] = CreateTextButton( L"View Creatures", FONT12POINT1, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 103, 40, 90, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, ViewCreaturesCallback ); iViewerButton[ BASEMENT1_BTN ] = CreateTextButton( L"B1", FONT16ARIAL, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 58, 60, 30, 24, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, B1Callback ); iViewerButton[ BASEMENT2_BTN ] = CreateTextButton( L"B2", FONT16ARIAL, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 88, 60, 30, 24, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, B2Callback ); iViewerButton[ BASEMENT3_BTN ] = CreateTextButton( L"B3", FONT16ARIAL, FONT_BLACK, FONT_BLACK, BUTTON_USE_DEFAULT, VIEWER_RIGHT + 118, 60, 30, 24, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, B3Callback ); if( gfViewEnemies ) ButtonList[ iViewerButton[ VIEW_ENEMIES ] ]->uiFlags |= BUTTON_CLICKED_ON; else ButtonList[ iViewerButton[ VIEW_CREATURES ] ]->uiFlags |= BUTTON_CLICKED_ON; MSYS_DefineRegion( &ViewerRegion, VIEWER_LEFT, VIEWER_TOP, VIEWER_RIGHT, VIEWER_BOTTOM, MSYS_PRIORITY_HIGH, 0, ViewerMapMoveCallback, ViewerMapClickCallback ); //Add the enemy population override fields InitTextInputModeWithScheme( DEFAULT_SCHEME ); StringFromValue( str, gsAINumAdmins, gGameExternalOptions.iMaxEnemyGroupSize ); AddTextInputField( 10, VIEWER_BOTTOM + 30, 25, 15, MSYS_PRIORITY_NORMAL, str, 2, INPUTTYPE_NUMERICSTRICT ); StringFromValue( str, gsAINumTroops, gGameExternalOptions.iMaxEnemyGroupSize ); AddTextInputField( 10, VIEWER_BOTTOM + 50, 25, 15, MSYS_PRIORITY_NORMAL, str, 2, INPUTTYPE_NUMERICSTRICT ); StringFromValue( str, gsAINumElites, gGameExternalOptions.iMaxEnemyGroupSize ); AddTextInputField( 10, VIEWER_BOTTOM + 70, 25, 15, MSYS_PRIORITY_NORMAL, str, 2, INPUTTYPE_NUMERICSTRICT ); StringFromValue( str, gsAINumCreatures, gGameExternalOptions.iMaxEnemyGroupSize ); AddTextInputField( 10, VIEWER_BOTTOM + 90, 25, 15, MSYS_PRIORITY_NORMAL, str, 2, INPUTTYPE_NUMERICSTRICT ); //Press buttons in based on current settings if (gGameOptions.ubDifficultyLevel >= DIF_LEVEL_EASY && gGameOptions.ubDifficultyLevel <= DIF_LEVEL_HARD) ButtonList[ iViewerButton[ RESET_EASY + gGameOptions.ubDifficultyLevel - DIF_LEVEL_EASY ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ COMPRESSION0 ] ]->uiFlags |= BUTTON_CLICKED_ON; if( !GamePaused() ) SetGameMinutesPerSecond( 0 ); ClearViewerRegion( 0, 0, 640, 480 ); return TRUE; } void DestroyAIViewer() { INT32 i; gfExitViewer = FALSE; gfViewerEntry = TRUE; for( i = 0; i < NUM_VIEWER_BUTTONS; i++ ) { RemoveButton( iViewerButton[ i ] ); } DeleteVideoObjectFromIndex( guiMapGraphicID ); DeleteVideoObjectFromIndex( guiMapIconsID ); MSYS_RemoveRegion( &ViewerRegion ); KillTextInputMode(); SetGameTimeCompressionLevel( giSaveTCMode ); EnableScrollMessages(); } void ClearViewerRegion( INT16 sLeft, INT16 sTop, INT16 sRight, INT16 sBottom ) { ColorFillVideoSurfaceArea( ButtonDestBuffer, sLeft, sTop, sRight, sBottom, gusBlue ); InvalidateRegion( sLeft, sTop, sRight, sBottom ); if( !sLeft ) { ColorFillVideoSurfaceArea( ButtonDestBuffer, 0, sTop, 1, sBottom, gusLtBlue ); sLeft++; } if( !sTop ) { ColorFillVideoSurfaceArea( ButtonDestBuffer, sLeft, 0, sRight, 1, gusLtBlue ); sTop++; } if( sBottom == 480 ) ColorFillVideoSurfaceArea( ButtonDestBuffer, sLeft, 479, sRight, 480, gusDkBlue ); if( sRight == 640 ) ColorFillVideoSurfaceArea( ButtonDestBuffer, 639, sTop, 640, sBottom, gusDkBlue ); } void RenderStationaryGroups() { HVOBJECT hVObject; SECTORINFO *pSector; INT32 xp, yp; CHAR16 str[20]; INT32 iSector = 0; UINT8 ubIconColor = 0; UINT8 ubGroupSize = 0; SetFont( FONT10ARIAL ); SetFontShadow( FONT_NEARBLACK ); GetVideoObject( &hVObject, guiMapIconsID ); Ensure_RepairedGarrisonGroup( &gGarrisonGroup, &giGarrisonArraySize ); /* added NULL fix, 2007-03-03, Sgt. Kolja */ //Render groups that are stationary... for ( INT32 y = 0; y < 16; ++y ) { yp = VIEWER_TOP + VIEWER_CELLH * y + 1; for ( INT32 x = 0; x < 16; ++x ) { SetFontForeground( FONT_YELLOW ); xp = VIEWER_LEFT + VIEWER_CELLW * x + 1; iSector = SECTOR(x, y); pSector = &SectorInfo[ iSector ]; if( pSector->uiFlags & SF_MINING_SITE ) BltVideoObject( FRAME_BUFFER, hVObject, MINING_ICON, xp + 25, yp - 1, VO_BLT_SRCTRANSPARENCY, NULL ); if( pSector->uiFlags & SF_SAM_SITE ) BltVideoObject( FRAME_BUFFER, hVObject, SAM_ICON, xp + 20, yp + 4, VO_BLT_SRCTRANSPARENCY, NULL ); UINT8 nummilitia = NumNonPlayerTeamMembersInSector( x, y, MILITIA_TEAM ); if ( nummilitia ) { // show militia ubIconColor = ICON_COLOR_BLUE; ubGroupSize = nummilitia; } else if( pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumJeeps + pSector->ubNumTanks ) { // show enemies ubIconColor = ChooseEnemyIconColor( pSector->ubNumAdmins, pSector->ubNumTroops, pSector->ubNumElites, pSector->ubNumTanks, pSector->ubNumJeeps ); ubGroupSize = pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumJeeps + pSector->ubNumTanks; if( pSector->ubGarrisonID != NO_GARRISON ) { if( gGarrisonGroup[ pSector->ubGarrisonID ].ubPendingGroupID ) { if( GetJA2Clock() % 1000 < 333 ) { SetFontForeground( FONT_LTRED ); } } } else { if( GetJA2Clock() % 1000 < 333 ) { SetFontForeground( FONT_LTKHAKI ); } } } else { ubGroupSize = 0; } if ( ubGroupSize > 0 ) { // draw the icon BlitGroupIcon( ICON_TYPE_STOPPED, ubIconColor, xp, yp, hVObject ); //Print the group size swprintf( str, L"%d", ubGroupSize ); mprintf( xp + 2, yp + 2, str ); } ++iSector; } } } void RenderMovingGroupsAndMercs() { GROUP *pGroup; HVOBJECT hVObject; INT32 x, y; UINT16 ubNumTroops, ubNumAdmins, ubNumElites, ubNumTanks, ubNumJeeps; float ratio; INT32 minX, maxX, minY, maxY; UINT8 ubIconType; UINT8 ubIconColor; UINT8 ubFontColor; SetFont( FONT10ARIAL ); SetFontShadow( FONT_NEARBLACK ); GetVideoObject( &hVObject, guiMapIconsID ); //Render groups that are moving... pGroup = gpGroupList; while( pGroup ) { if( pGroup->ubGroupSize && !pGroup->fVehicle) { if( pGroup->uiTraverseTime ) { // display how far along to the next sector they are ratio = (pGroup->uiTraverseTime-pGroup->uiArrivalTime + GetWorldTotalMin())/(float)pGroup->uiTraverseTime; minX = VIEWER_LEFT + VIEWER_CELLW * (pGroup->ubSectorX-1); maxX = VIEWER_LEFT + VIEWER_CELLW * (pGroup->ubNextX-1); x = (UINT32)(minX + ratio *(maxX-minX)); minY = VIEWER_TOP + VIEWER_CELLH * (pGroup->ubSectorY-1); maxY = VIEWER_TOP + VIEWER_CELLH * (pGroup->ubNextY-1); y = (UINT32)(minY + ratio *(maxY-minY)); } else { x = VIEWER_LEFT + VIEWER_CELLW * (pGroup->ubSectorX-1); y = VIEWER_TOP + VIEWER_CELLH * (pGroup->ubSectorY-1); } if ( pGroup->usGroupTeam == OUR_TEAM ) { ubIconType = (UINT8) (( pGroup->uiTraverseTime ) ? ICON_TYPE_ASSAULT : ICON_TYPE_STOPPED); ubIconColor = ICON_COLOR_GREEN; ubFontColor = FONT_YELLOW; } else { //if the group was moving, then draw the anchor to visually indicate the sector of //influence for enemy patrol groups. if( pGroup->uiTraverseTime ) { BltVideoObject( FRAME_BUFFER, hVObject, GROUP_ANCHOR, VIEWER_LEFT + VIEWER_CELLW * (pGroup->ubSectorX - 1), VIEWER_TOP + VIEWER_CELLH * (pGroup->ubSectorY - 1), VO_BLT_SRCTRANSPARENCY, NULL ); } ubNumAdmins = pGroup->pEnemyGroup->ubNumAdmins;//+ pGroup->pEnemyGroup->ubAdminsInBattle; ubNumTroops = pGroup->pEnemyGroup->ubNumTroops;//+ pGroup->pEnemyGroup->ubTroopsInBattle; ubNumElites = pGroup->pEnemyGroup->ubNumElites;// + pGroup->pEnemyGroup->ubElitesInBattle; ubNumTanks = pGroup->pEnemyGroup->ubNumTanks; ubNumJeeps = pGroup->pEnemyGroup->ubNumJeeps; // must have one of the three, already checked groupsize! Assert( ubNumAdmins || ubNumTroops || ubNumElites || ubNumTanks || ubNumJeeps ); //determine icon color ubIconColor = ChooseEnemyIconColor( ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps ); // must have a valid intention Assert( pGroup->pEnemyGroup->ubIntention < NUM_ENEMY_INTENTIONS ); // determine icon type - shows the groups intentions switch ( pGroup->pEnemyGroup->ubIntention ) { case REINFORCEMENTS: ubIconType = ICON_TYPE_REINFORCEMENT; ubFontColor = FONT_YELLOW; break; case PATROL: ubIconType = ICON_TYPE_PATROL; ubFontColor = FONT_YELLOW; break; case STAGING: ubIconType = ICON_TYPE_PATROL; ubFontColor = FONT_LTBLUE; break; case PURSUIT: ubIconType = ICON_TYPE_ASSAULT; ubFontColor = FONT_YELLOW; break; case ASSAULT: ubIconType = ICON_TYPE_ASSAULT; ubFontColor = FONT_LTBLUE; break; default: Assert(0); return; } } // draw the icon BlitGroupIcon( ubIconType, ubIconColor, x, y, hVObject ); // set color SetFontForeground( ubFontColor ); //Print the group size if ( pGroup->usGroupTeam == OUR_TEAM && !pGroup->uiTraverseTime ) // stationary player - count all mercs there, not just the group mprintf( x+11, y+11, L"%d", PlayerMercsInSector( pGroup->ubSectorX, pGroup->ubSectorY, pGroup->ubSectorZ ) ); else { if( !pGroup->pWaypoints ) { if( GetJA2Clock() % 1000 < 750 ) { SetFontForeground( FONT_WHITE ); } } mprintf( x+7, y+7, L"%d", pGroup->ubGroupSize ); } } pGroup = pGroup->next; } } void RenderInfoInSector() { UINT8 ubSectorX, ubSectorY; UINT16 ubMercs=0, ubActive=0, ubUnconcious=0, ubCollapsed=0; INT32 xp, yp; if( gfViewEnemies && !gbViewLevel ) { RenderStationaryGroups(); RenderMovingGroupsAndMercs(); SetFontForeground( FONT_LTRED ); xp = VIEWER_LEFT + VIEWER_CELLW * ( gModSettings.ubSAISpawnSectorX - 1 ) + 6; yp = VIEWER_TOP + VIEWER_CELLH * ( gModSettings.ubSAISpawnSectorY - 1 ) + 13; //mprintf( 78, 358, L"%3d", giReinforcementPool ); mprintf( xp, yp, L"%3d", giReinforcementPool ); //Render general enemy statistics ClearViewerRegion( 105, VIEWER_BOTTOM + 10, 265, VIEWER_BOTTOM + 66 ); SetFontForeground( FONT_YELLOW ); mprintf( 105, VIEWER_BOTTOM + 10, L"GLOBAL INFO" ); SetFontForeground( FONT_GRAY2 ); mprintf( 118, VIEWER_BOTTOM + 24, L"Strategic AI Awake" ); mprintf( 105, VIEWER_BOTTOM + 36, L"Total Request Points: %d", giRequestPoints ); mprintf( 105, VIEWER_BOTTOM + 46, L"Total Reinforcement Points: %d", giReinforcementPoints ); mprintf( 105, VIEWER_BOTTOM + 56, L"Progress (Current/Highest): %d%%/%d%%", CurrentPlayerProgressPercentage(), HighestPlayerProgressPercentage() ); PrintEnemyPopTable(); PrintEnemiesKilledTable(); } if( gsHiSectorX && gsHiSectorY && ( gsHiSectorX != gsSelSectorX || gsHiSectorY != gsSelSectorY ) ) { //Render sector info for the hilighted sector SetFontForeground( FONT_YELLOW ); ubSectorX = (UINT8)gsHiSectorX; ubSectorY = (UINT8)gsHiSectorY; } else if( gsSelSectorX && gsSelSectorY ) { //Render sector info for the selected sector SetFontForeground( FONT_RED ); ubSectorX = (UINT8)gsSelSectorX; ubSectorY = (UINT8)gsSelSectorY; } else { return; } //Count the number of mercs and their states (even for underground sectors) for( SoldierID id = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; id <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++id ) { SOLDIERTYPE *pSoldier = id; if( pSoldier->bActive && pSoldier->sSectorX == ubSectorX && pSoldier->sSectorY == ubSectorY && pSoldier->bSectorZ == gbViewLevel ) { if( pSoldier->stats.bLife ) { ubMercs++; if( pSoldier->stats.bLife >= OKLIFE ) { if( pSoldier->bBreath < OKBREATH ) ubCollapsed++; else ubActive++; } else ubUnconcious++; } } } yp = 375; if( !gbViewLevel ) { SECTORINFO *pSector; GROUP *pGroup; UINT16 ubNumAdmins = 0, ubNumTroops = 0, ubNumElites = 0, ubNumTanks = 0, ubNumJeeps = 0, ubAdminsInBattle = 0, ubTroopsInBattle = 0, ubElitesInBattle = 0, ubTanksInBattle = 0, ubJeepsInBattle = 0,ubNumGroups = 0; pSector = &SectorInfo[ SECTOR( ubSectorX, ubSectorY ) ]; //Now count the number of mobile groups in the sector. pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == ubSectorX && pGroup->ubSectorY == ubSectorY ) { ubNumTroops += pGroup->pEnemyGroup->ubNumTroops; ubNumElites += pGroup->pEnemyGroup->ubNumElites; ubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins; ubNumTanks += pGroup->pEnemyGroup->ubNumTanks; ubNumJeeps += pGroup->pEnemyGroup->ubNumJeeps; ubTroopsInBattle += pGroup->pEnemyGroup->ubTroopsInBattle; ubElitesInBattle += pGroup->pEnemyGroup->ubElitesInBattle; ubAdminsInBattle += pGroup->pEnemyGroup->ubAdminsInBattle; ubTanksInBattle += pGroup->pEnemyGroup->ubTanksInBattle; ubJeepsInBattle += pGroup->pEnemyGroup->ubJeepsInBattle; ubNumGroups++; } pGroup = pGroup->next; } ClearViewerRegion( 280, 375, 640, 480 ); mprintf( 280, yp, L"SECTOR INFO: %c%d (ID: %d)", ubSectorY + 'A' - 1, ubSectorX, SECTOR( ubSectorX, ubSectorY ) ); yp += 10; SetFontForeground( FONT_LTGREEN ); mprintf( 280, yp, L"%d Player Mercs: (%d Active, %d Unconcious, %d Collapsed)", ubMercs, ubActive, ubUnconcious, ubCollapsed ); yp += 10; SetFontForeground( FONT_LTBLUE ); mprintf( 280, yp, L"Militia: (%d Green, %d Regular, %d Elite)", MilitiaInSectorOfRank( ubSectorX, ubSectorY, GREEN_MILITIA ), MilitiaInSectorOfRank( ubSectorX, ubSectorY, REGULAR_MILITIA ), MilitiaInSectorOfRank( ubSectorX, ubSectorY, ELITE_MILITIA ) ); yp += 10; SetFontForeground( FONT_ORANGE ); mprintf( 280, yp, L"Garrison: (%d:%d Admins, %d:%d Troops, %d:%d Elites, %d:%d Tanks, %d:%d Jeeps)", pSector->ubAdminsInBattle, pSector->ubNumAdmins, pSector->ubTroopsInBattle, pSector->ubNumTroops, pSector->ubElitesInBattle, pSector->ubNumElites, pSector->ubTanksInBattle, pSector->ubNumTanks, pSector->ubJeepsInBattle, pSector->ubNumJeeps ); yp += 10; mprintf( 280, yp, L"%d Groups: (%d:%d Admins, %d:%d Troops, %d:%d Elites, %d:%d Tanks, %d:%d Jeeps)", ubNumGroups, ubAdminsInBattle, ubNumAdmins, ubTroopsInBattle, ubNumTroops, ubElitesInBattle, ubNumElites, ubTanksInBattle, ubNumTanks, ubJeepsInBattle, ubNumJeeps ); yp += 10; SetFontForeground( FONT_WHITE ); if( gfViewEnemies ) { PrintDetailedEnemiesInSectorInfo( 280, yp, ubSectorX, ubSectorY ); yp += 10; } else { SetFontForeground( FONT_YELLOW ); mprintf( 280, yp, L"Monsters: (%d:%d)", pSector->ubCreaturesInBattle, pSector->ubNumCreatures ); yp += 10; } } else { UNDERGROUND_SECTORINFO *pSector; ClearViewerRegion( 280, 375, 640, 480 ); pSector = FindUnderGroundSector( ubSectorX, ubSectorY, gbViewLevel ); if( !pSector ) { return; } mprintf( 280, yp, L"SECTOR INFO: %c%d_b%d", ubSectorY + 'A' - 1, ubSectorX, gbViewLevel ); yp += 10; SetFontForeground( FONT_LTGREEN ); mprintf( 280, yp, L"%d Player Mercs: (%d Active, %d Unconcious, %d Collapsed)", ubMercs, ubActive, ubUnconcious, ubCollapsed ); yp += 10; SetFontForeground( FONT_YELLOW ); mprintf( 280, yp, L"Monsters: (%d:%d)", pSector->ubCreaturesInBattle, pSector->ubNumCreatures ); yp += 10; if( pSector->uiFlags & SF_PENDING_ALTERNATE_MAP ) { mprintf( 280, yp, L"SF_PENDING_ALTERNATE_MAP", pSector->ubCreaturesInBattle, pSector->ubNumCreatures ); yp += 10; } if( pSector->uiFlags & SF_USE_ALTERNATE_MAP ) { mprintf( 280, yp, L"SF_USE_ALTERNATE_MAP", pSector->ubCreaturesInBattle, pSector->ubNumCreatures ); yp += 10; } } } void RenderViewer() { UINT8 *pDestBuf; UINT32 uiDestPitchBYTES; SGPRect ClipRect; INT32 i, x, y, xp, yp; if( gfRenderViewer ) { ClearViewerRegion( VIEWER_LEFT, VIEWER_TOP, VIEWER_RIGHT, VIEWER_BOTTOM ); gfRenderViewer = FALSE; gfRenderMap = TRUE; for( i = 0; i < NUM_VIEWER_BUTTONS; i++ ) { MarkAButtonDirty( iViewerButton[ i ] ); } SetFont( FONT10ARIAL ); SetFontForeground( FONT_YELLOW ); SetFontShadow( FONT_NEARBLACK ); mprintf( 38, VIEWER_BOTTOM + 33, L"Admins" ); mprintf( 38, VIEWER_BOTTOM + 53, L"Troops" ); mprintf( 38, VIEWER_BOTTOM + 73, L"Elites" ); mprintf( 38, VIEWER_BOTTOM + 93, L"Creatures" ); } if( gfRenderMap ) { gfRenderMap = FALSE; BltVideoObjectFromIndex( FRAME_BUFFER, guiMapGraphicID, 0, VIEWER_LEFT, VIEWER_TOP, VO_BLT_SRCTRANSPARENCY, NULL ); InvalidateRegion( VIEWER_LEFT, VIEWER_TOP, VIEWER_RIGHT, VIEWER_BOTTOM ); //Draw the coordinates ClearViewerRegion( 0, 0, VIEWER_RIGHT, 14 ); ClearViewerRegion( 0, 0, 14, VIEWER_BOTTOM ); SetFont( FONT10ARIAL ); SetFontShadow( FONT_NEARBLACK ); for( y = 0; y < 16; y++ ) { if( y+1 == gsSelSectorY ) SetFontForeground( FONT_RED ); else if( y+1 == gsHiSectorY ) SetFontForeground( FONT_YELLOW ); else SetFontForeground( FONT_GRAY1 ); mprintf( VIEWER_LEFT-10, VIEWER_TOP+7+y*22, L"%c", 'A' + y ); } for( x = 1; x <= 16; x++ ) { CHAR16 str[3]; if( x == gsSelSectorX ) SetFontForeground( FONT_RED ); else if( x == gsHiSectorX ) SetFontForeground( FONT_YELLOW ); else SetFontForeground( FONT_GRAY1 ); swprintf( str, L"%d", x ); mprintf( VIEWER_LEFT+x*26-(26+StringPixLength( str, FONT12POINT1 ))/2, VIEWER_TOP-12, str ); } if( gbViewLevel ) { UNDERGROUND_SECTORINFO *pUnder; SetFont( FONT10ARIAL ); SetFontForeground( FONT_YELLOW ); SetFontShadow( FONT_NEARBLACK ); for( y = 0; y < 16; y++ ) { ClipRect.iTop = VIEWER_TOP + y*VIEWER_CELLH; ClipRect.iBottom = ClipRect.iTop + VIEWER_CELLH - 1; for( x = 0; x < 16; x++ ) { pUnder = FindUnderGroundSector( (INT16)(x + 1), (INT16)(y + 1), gbViewLevel ); if( pUnder ) { xp = VIEWER_LEFT+x*VIEWER_CELLW+2; yp = VIEWER_TOP+y*VIEWER_CELLH+2; mprintf( xp, yp, L"%d", pUnder->ubNumCreatures ); } else { //not found, so visually shade it darker. pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); ClipRect.iLeft = VIEWER_LEFT + x*VIEWER_CELLW; ClipRect.iRight = ClipRect.iLeft + VIEWER_CELLW - 1; Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); UnLockVideoSurface( FRAME_BUFFER ); } } } } RenderInfoInSector(); } pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, 640, 480 ); //Render the grid for the sector if the mouse is over it (yellow). if( gsHiSectorX > 0 ) { x = VIEWER_LEFT + (gsHiSectorX-1) * 26; y = VIEWER_TOP + (gsHiSectorY-1) * 22; RectangleDraw( TRUE, x, y, x+26, y+22, Get16BPPColor( FROMRGB( 200, 200, 50 ) ), pDestBuf ); } //Render the grid for the sector currently in focus (red). if( gsSelSectorX > 0 ) { x = VIEWER_LEFT + (gsSelSectorX-1) * 26 ; y = VIEWER_TOP + (gsSelSectorY-1) * 22 ; RectangleDraw( TRUE, x, y, x+26, y+22, Get16BPPColor( FROMRGB( 200, 50, 50 ) ), pDestBuf ); } UnLockVideoSurface( FRAME_BUFFER ); } void ViewerExitCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfExitViewer = TRUE; } } void HandleViewerInput() { SECTORINFO *pSector; InputAtom Event; while( DequeueSpecificEvent(&Event, KEY_DOWN|KEY_UP) ) { if( !HandleTextInput( &Event ) && Event.usEvent == KEY_DOWN ) { switch( Event.usParam ) { case ESC: gfExitViewer = TRUE; break; case ENTER: // this means GO! for doing overrides ExtractAndUpdatePopulations(); break; case 'x': if( Event.usKeyState & ALT_DOWN ) { gfExitViewer = TRUE; gfProgramIsRunning = FALSE; } break; case 'm': //Kill all enemies and add militia if( Event.usKeyState & ALT_DOWN ) { pSector = NULL; if( gsSelSectorX && gsSelSectorY ) { StrategicAddMilitiaToSector( gsSelSectorX, gsSelSectorY, GREEN_MILITIA, 15 ); StrategicAddMilitiaToSector( gsSelSectorX, gsSelSectorY, REGULAR_MILITIA, 4 ); StrategicAddMilitiaToSector( gsSelSectorX, gsSelSectorY, ELITE_MILITIA, 1 ); gfRenderMap = TRUE; EliminateAllEnemies( (UINT8)gsSelSectorX, (UINT8)gsSelSectorY ); } else if( gsHiSectorX && gsHiSectorY ) { StrategicAddMilitiaToSector( gsHiSectorX, gsHiSectorY, GREEN_MILITIA, 15 ); StrategicAddMilitiaToSector( gsHiSectorX, gsHiSectorY, REGULAR_MILITIA, 4 ); StrategicAddMilitiaToSector( gsHiSectorX, gsHiSectorY, ELITE_MILITIA, 1 ); gfRenderMap = TRUE; EliminateAllEnemies( (UINT8)gsHiSectorX, (UINT8)gsHiSectorY ); } } break; case 'o': // kill all enemies! if( Event.usKeyState & ALT_DOWN ) { pSector = NULL; if( gsSelSectorX && gsSelSectorY ) { gfRenderMap = TRUE; EliminateAllEnemies( (UINT8)gsSelSectorX, (UINT8)gsSelSectorY ); } else if( gsHiSectorX && gsHiSectorY ) { gfRenderMap = TRUE; EliminateAllEnemies( (UINT8)gsHiSectorX, (UINT8)gsHiSectorY ); } } break; case 'g': //Add a group of 8 stationary enemies if( gsSelSectorX && gsSelSectorY ) { pSector = &SectorInfo[ SECTOR( gsSelSectorX, gsSelSectorY ) ]; pSector->ubNumElites += 1; pSector->ubNumTroops += 7; } else if( gsHiSectorX && gsHiSectorY ) { pSector = &SectorInfo[ SECTOR( gsHiSectorX, gsHiSectorY ) ]; pSector->ubNumElites += 1; pSector->ubNumTroops += 7; } break; case 'c': //Add a group of 8 creatures. if( gsSelSectorX && gsSelSectorY ) { pSector = &SectorInfo[ SECTOR( gsSelSectorX, gsSelSectorY ) ]; pSector->ubNumCreatures += 8; } else if( gsHiSectorX && gsHiSectorY ) { pSector = &SectorInfo[ SECTOR( gsHiSectorX, gsHiSectorY ) ]; pSector->ubNumCreatures += 8; } break; } } } } void EasyCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ RESET_EASY ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ RESET_NORMAL ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ RESET_HARD ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ QUEEN_AWAKE_TOGGLE ] ]->uiFlags &= (~BUTTON_CLICKED_ON); gfRenderViewer = TRUE; gGameOptions.ubDifficultyLevel = DIF_LEVEL_EASY; ShutdownStrategicLayer(); InitStrategicLayer(); Compression0Callback( ButtonList[ iViewerButton[ COMPRESSION0 ] ], MSYS_CALLBACK_REASON_LBUTTON_UP ); MarkButtonsDirty(); WarpGameTime( 45 * 60, WARPTIME_PROCESS_EVENTS_NORMALLY ); } } void NormalCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ RESET_EASY ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ RESET_NORMAL ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ RESET_HARD ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ QUEEN_AWAKE_TOGGLE ] ]->uiFlags &= (~BUTTON_CLICKED_ON); gfRenderViewer = TRUE; gGameOptions.ubDifficultyLevel = DIF_LEVEL_MEDIUM; ShutdownStrategicLayer(); InitStrategicLayer(); Compression0Callback( ButtonList[ iViewerButton[ COMPRESSION0 ] ], MSYS_CALLBACK_REASON_LBUTTON_UP ); MarkButtonsDirty(); WarpGameTime( 45 * 60, WARPTIME_PROCESS_EVENTS_NORMALLY ); } } void HardCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ RESET_EASY ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ RESET_NORMAL ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ RESET_HARD ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ QUEEN_AWAKE_TOGGLE ] ]->uiFlags &= (~BUTTON_CLICKED_ON); gfRenderViewer = TRUE; gGameOptions.ubDifficultyLevel = DIF_LEVEL_HARD; ShutdownStrategicLayer(); InitStrategicLayer(); Compression0Callback( ButtonList[ iViewerButton[ COMPRESSION0 ] ], MSYS_CALLBACK_REASON_LBUTTON_UP ); MarkButtonsDirty(); WarpGameTime( 45 * 60, WARPTIME_PROCESS_EVENTS_NORMALLY ); } } void ViewerMapMoveCallback( MOUSE_REGION *reg, INT32 reason ) { static INT16 gsPrevX = 0, gsPrevY = 0; //calc current sector highlighted. if( reason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { gsPrevX = gsHiSectorX = 0; gsPrevY = gsHiSectorY = 0; gfRenderViewer = TRUE; return; } gsHiSectorX = min( (reg->RelativeXPos / 26) + 1, 16 ); gsHiSectorY = min( (reg->RelativeYPos / 22) + 1, 16 ); if( gsPrevX != gsHiSectorX || gsPrevY != gsHiSectorY ) { gsPrevX = gsHiSectorX; gsPrevY = gsHiSectorY; gfRenderViewer = TRUE; } } void ViewerMapClickCallback( MOUSE_REGION *reg, INT32 reason ) { static INT16 sLastX = -1, sLastY = -1; //calc current sector selected. if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gsSelSectorX = min( (reg->RelativeXPos / 26) + 1, 16 ); gsSelSectorY = min( (reg->RelativeYPos / 22) + 1, 16 ); if( gsSelSectorX != sLastX || gsSelSectorY != sLastY ) { //clicked in a new sector sLastX = gsSelSectorX; sLastY = gsSelSectorY; gfRenderViewer = TRUE; } } } UINT32 AIViewerScreenInit() { gfViewerEntry = TRUE; gusBlue = Get16BPPColor( FROMRGB( 65, 79, 94 ) ); gusLtBlue = Get16BPPColor( FROMRGB( 122, 124, 121 ) ); gusDkBlue = Get16BPPColor( FROMRGB( 22, 55, 73 ) ); return TRUE; } UINT32 AIViewerScreenHandle() { StartFrameBufferRender(); RestoreBackgroundRects(); if( gfViewerEntry ) { gfViewerEntry = FALSE; if( !CreateAIViewer() ) { return MAP_SCREEN; } } if( GetWorldTotalSeconds() != guiLastTime ) { guiLastTime = GetWorldTotalSeconds(); gfRenderViewer = TRUE; SpecifyButtonText( iViewerButton[ VIEWER_TIMEPANEL ], WORLDTIMESTR ); } HandleViewerInput(); RenderViewer(); RenderAllTextFields(); RenderButtons(); SaveBackgroundRects( ); RenderButtonsFastHelp(); ExecuteBaseDirtyRectQueue(); EndFrameBufferRender(); if( gfExitViewer ) { DestroyAIViewer(); return MAP_SCREEN; } return AIVIEWER_SCREEN; } UINT32 AIViewerScreenShutdown() { return TRUE; } void Compression0Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ COMPRESSION0 ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ COMPRESSION5 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION15 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION60 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); // ButtonList[ iViewerButton[ COMPRESSION6H ] ]->uiFlags &= (~BUTTON_CLICKED_ON); SetGameMinutesPerSecond( 0 ); } } void Compression5Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ COMPRESSION0 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION5 ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ COMPRESSION15 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION60 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); // ButtonList[ iViewerButton[ COMPRESSION6H ] ]->uiFlags &= (~BUTTON_CLICKED_ON); SetGameMinutesPerSecond( 5 ); SetFactTimeCompressHasOccured( ); } } void Compression15Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ COMPRESSION0 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION5 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION15 ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ COMPRESSION60 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); // ButtonList[ iViewerButton[ COMPRESSION6H ] ]->uiFlags &= (~BUTTON_CLICKED_ON); SetGameMinutesPerSecond( 15 ); SetFactTimeCompressHasOccured( ); } } void Compression60Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ COMPRESSION0 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION5 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION15 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION60 ] ]->uiFlags |= BUTTON_CLICKED_ON; // ButtonList[ iViewerButton[ COMPRESSION6H ] ]->uiFlags &= (~BUTTON_CLICKED_ON); SetGameHoursPerSecond( 1 ); SetFactTimeCompressHasOccured( ); } } void Compression6HCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { ButtonList[ iViewerButton[ COMPRESSION0 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION5 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION15 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); ButtonList[ iViewerButton[ COMPRESSION60 ] ]->uiFlags &= (~BUTTON_CLICKED_ON); // ButtonList[ iViewerButton[ COMPRESSION6H ] ]->uiFlags |= BUTTON_CLICKED_ON; SetGameHoursPerSecond( 6 ); SetFactTimeCompressHasOccured( ); } } void TestIncoming4SidesCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { GROUP *pGroup; UINT8 ubSector; UINT32 uiWorldMin; Compression0Callback( ButtonList[ iViewerButton[ COMPRESSION0 ] ], MSYS_CALLBACK_REASON_LBUTTON_UP ); if( ( gsSelSectorX == 0 ) || ( gsSelSectorY == 0 ) ) // HEADROCK HAM 3.5: Not sure what this function does at all... But this is externalized now anyway. gsSelSectorX = gGameExternalOptions.ubDefaultArrivalSectorX, gsSelSectorY = gGameExternalOptions.ubDefaultArrivalSectorY; ubSector = SECTOR( gsSelSectorX, gsSelSectorY ); uiWorldMin = GetWorldTotalMin(); gfRenderViewer = TRUE; if( gsSelSectorY > 1 ) { pGroup = CreateNewEnemyGroupDepartingFromSector( ubSector-16, 0, 11, 5, 0, 0, 0 ); pGroup->ubNextX = (UINT8)gsSelSectorX; pGroup->ubNextY = (UINT8)gsSelSectorY; pGroup->uiTraverseTime = 10; pGroup->pEnemyGroup->ubIntention = ASSAULT; SetGroupArrivalTime( pGroup, uiWorldMin + 10 ); pGroup->ubMoveType = ONE_WAY; pGroup->fDebugGroup = TRUE; AddStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->uiArrivalTime, pGroup->ubGroupID ); } if( gsSelSectorY < 16 ) { pGroup = CreateNewEnemyGroupDepartingFromSector( ubSector+16, 0, 8, 8, 0, 0, 0 ); pGroup->ubNextX = (UINT8)gsSelSectorX; pGroup->ubNextY = (UINT8)gsSelSectorY; pGroup->uiTraverseTime = 12; pGroup->pEnemyGroup->ubIntention = ASSAULT; SetGroupArrivalTime( pGroup, uiWorldMin + 12 ); pGroup->ubMoveType = ONE_WAY; pGroup->fDebugGroup = TRUE; AddStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->uiArrivalTime, pGroup->ubGroupID ); } if( gsSelSectorX > 1 ) { pGroup = CreateNewEnemyGroupDepartingFromSector( ubSector-1, 0, 11, 5, 0, 0, 0 ); pGroup->ubNextX = (UINT8)gsSelSectorX; pGroup->ubNextY = (UINT8)gsSelSectorY; pGroup->uiTraverseTime = 11; pGroup->pEnemyGroup->ubIntention = ASSAULT; SetGroupArrivalTime( pGroup, uiWorldMin + 11 ); pGroup->ubMoveType = ONE_WAY; pGroup->fDebugGroup = TRUE; AddStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->uiArrivalTime, pGroup->ubGroupID ); } if( gsSelSectorX < 16 ) { pGroup = CreateNewEnemyGroupDepartingFromSector( ubSector+1, 0, 14, 0, 0, 0, 0 ); pGroup->ubNextX = (UINT8)gsSelSectorX; pGroup->ubNextY = (UINT8)gsSelSectorY; pGroup->uiTraverseTime = 13; pGroup->pEnemyGroup->ubIntention = ASSAULT; SetGroupArrivalTime( pGroup, uiWorldMin + 13 ); pGroup->ubMoveType = ONE_WAY; pGroup->fDebugGroup = TRUE; AddStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->uiArrivalTime, pGroup->ubGroupID ); } } } void StartCreatureQuestCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { //Madd: wtf? gGameOptions.ubGameStyle == STYLE_PLATINUM; gfRenderMap = TRUE; ClearCreatureQuest(); InitCreatureQuest(); } } void SpreadCreaturesCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfRenderMap = TRUE; if( _KeyDown( ALT ) ) { INT32 i; // spread 10 times for( i = 0; i < 10; i++ ) { SpreadCreatures(); } } SpreadCreatures(); } } void CreatureAttackCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { if( ( gsSelSectorX != 0 ) && ( gsSelSectorY != 0 ) ) { if( _KeyDown( ALT ) ) { AddStrategicEventUsingSeconds( EVENT_CREATURE_ATTACK, GetWorldTotalSeconds()+4, SECTOR( gsSelSectorX, gsSelSectorY ) ); } else { CreatureAttackTown( (UINT8)SECTOR( gsSelSectorX, gsSelSectorY ), TRUE ); } } } } void ViewEnemiesCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfViewEnemies = TRUE; gfRenderMap = TRUE; ButtonList[ iViewerButton[ VIEW_CREATURES ] ]->uiFlags &= ~BUTTON_CLICKED_ON; gbViewLevel = 0; ButtonList[ iViewerButton[ BASEMENT1_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT2_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT3_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; } } void ViewCreaturesCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfViewEnemies = FALSE; gfRenderMap = TRUE; ButtonList[ iViewerButton[ VIEW_ENEMIES] ]->uiFlags &= ~BUTTON_CLICKED_ON; if( !gbViewLevel ) { gbViewLevel = 1; ButtonList[ iViewerButton[ BASEMENT1_BTN ] ]->uiFlags |= BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT2_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT3_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; } } } void B1Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfRenderMap = TRUE; gbViewLevel = 1; ButtonList[ iViewerButton[ BASEMENT2_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT3_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; } } void B2Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfRenderMap = TRUE; gbViewLevel = 2; ButtonList[ iViewerButton[ BASEMENT1_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT3_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; } } void B3Callback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfRenderMap = TRUE; gbViewLevel = 3; ButtonList[ iViewerButton[ BASEMENT1_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ iViewerButton[ BASEMENT2_BTN ] ]->uiFlags &= ~BUTTON_CLICKED_ON; } } void ReloadSectorCallback( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { gfOverrideSector = TRUE; ExtractAndUpdatePopulations(); SetCurrentWorldSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); gfOverrideSector = FALSE; DestroyAIViewer(); } } void ExtractAndUpdatePopulations() { //Kaiden: Loading INI file to read Values... //Tag for later - this is one of the reasons the // Map Editor won't compile. And I feel stupid about it. // Pointing to a static location for the INI file. // Not only bad, the file is no longer there. //CIniReader iniReader("..\\Ja2_Options.ini"); //INT32 iMaxEnemyGroupSize = iniReader.ReadInteger("Options","MAX_STRATEGIC_TEAM_SIZE",20); gsAINumAdmins = (INT16) min( GetNumericStrictValueFromField( 0 ), gGameExternalOptions.iMaxEnemyGroupSize ); SetInputFieldStringWithNumericStrictValue( 0, gsAINumAdmins ); gsAINumTroops = (INT16) min( GetNumericStrictValueFromField( 1 ), gGameExternalOptions.iMaxEnemyGroupSize ); SetInputFieldStringWithNumericStrictValue( 1, gsAINumTroops ); gsAINumElites = (INT16) min( GetNumericStrictValueFromField( 2 ), gGameExternalOptions.iMaxEnemyGroupSize ); SetInputFieldStringWithNumericStrictValue( 2, gsAINumElites ); gsAINumCreatures = (INT16) min( GetNumericStrictValueFromField( 3 ), gGameExternalOptions.iMaxEnemyGroupSize ); SetInputFieldStringWithNumericStrictValue( 3, gsAINumCreatures ); } void ToggleQueenAwake( GUI_BUTTON *btn, INT32 reason ) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { if( btn->uiFlags & BUTTON_CLICKED_ON ) { WakeUpQueen(); } else { gfQueenAIAwake = FALSE; } } } enum { ENEMY_RANK_TOTAL = NUM_ENEMY_RANKS, ENEMY_RANK_PERCENT, TABLE_ENEMY_RANKS }; CHAR16 EnemyRankString[ TABLE_ENEMY_RANKS ][ 10 ] = { L"Adm", L"Trp", L"Elt", L"TOT", L"%%", }; enum { ENEMY_TYPE_POOL, ENEMY_TYPE_GARRISON, ENEMY_TYPE_PATROL, ENEMY_TYPE_REINFORCEMENTS, ENEMY_TYPE_ASSAULT, ENEMY_TYPE_STAGING, ENEMY_TYPE_RETREATING, ENEMY_TYPE_TOTAL, ENEMY_TYPE_PERCENT, POP_TABLE_ENEMY_TYPES }; CHAR16 EnemyTypeString[ POP_TABLE_ENEMY_TYPES ][ 10 ] = { L"Pool", L"Garr", L"Ptrl", L"Rein", L"Aslt", L"Stag", L"Rtrt", L" TOT", L" %%", }; #define POP_TABLE_X_OFFSET 30 #define POP_TABLE_X_GAP 30 #define POP_TABLE_Y_GAP 10 void PrintEnemyPopTable() { UINT16 usX, usY; UINT16 usEnemyPopTable[ TABLE_ENEMY_RANKS ][ POP_TABLE_ENEMY_TYPES ]; UINT32 uiSector = 0; UINT8 ubEnemyRank; UINT8 ubEnemyType; SECTORINFO *pSector; GROUP *pGroup; CHAR16 wPrintSpec[ 10 ]; CHAR16 wTempString [10 ]; memset( &usEnemyPopTable, 0, sizeof ( usEnemyPopTable ) ); // count how many enemies of each type & rank there are // this is quite inaccurate, since elites can also come from the pool usEnemyPopTable[ ENEMY_RANK_TROOP ][ ENEMY_TYPE_POOL ] += ( UINT16 ) giReinforcementPool; // count stationary enemies (garrisons) for ( uiSector = 0; uiSector < 256; uiSector++ ) { pSector = &SectorInfo[ uiSector ]; usEnemyPopTable[ ENEMY_RANK_ADMIN ][ ENEMY_TYPE_GARRISON ] += pSector->ubNumAdmins; usEnemyPopTable[ ENEMY_RANK_TROOP ][ ENEMY_TYPE_GARRISON ] += pSector->ubNumTroops; usEnemyPopTable[ ENEMY_RANK_ELITE ][ ENEMY_TYPE_GARRISON ] += pSector->ubNumElites; //usEnemyPopTable[ ENEMY_RANK_TANK ][ ENEMY_TYPE_GARRISON ] += pSector->ubNumTanks; } // count moving enemies pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && !pGroup->fDebugGroup ) { Assert( pGroup->pEnemyGroup != NULL ); switch ( pGroup->pEnemyGroup->ubIntention ) { case REINFORCEMENTS: ubEnemyType = ENEMY_TYPE_REINFORCEMENTS; break; case ASSAULT: ubEnemyType = ENEMY_TYPE_ASSAULT; break; case STAGING: ubEnemyType = ENEMY_TYPE_STAGING; break; case PATROL: ubEnemyType = ENEMY_TYPE_PATROL; break; case PURSUIT: ubEnemyType = ENEMY_TYPE_ASSAULT; break; default: AssertMsg( 0, String( "Unknown moving group intention %d", pGroup->pEnemyGroup->ubIntention ) ); continue; } usEnemyPopTable[ ENEMY_RANK_ADMIN ][ ubEnemyType ] += pGroup->pEnemyGroup->ubNumAdmins; usEnemyPopTable[ ENEMY_RANK_TROOP ][ ubEnemyType ] += pGroup->pEnemyGroup->ubNumTroops; usEnemyPopTable[ ENEMY_RANK_ELITE ][ ubEnemyType ] += pGroup->pEnemyGroup->ubNumElites; //usEnemyPopTable[ ENEMY_RANK_TANK ][ ubEnemyType ] += pGroup->pEnemyGroup->ubNumTanks; } pGroup = pGroup->next; } // add up totals across rows (ranks) by type for ( ubEnemyType = 0; ubEnemyType < ENEMY_TYPE_TOTAL; ubEnemyType++ ) { for ( ubEnemyRank = 0; ubEnemyRank < ENEMY_RANK_TOTAL; ubEnemyRank++ ) { usEnemyPopTable[ ENEMY_RANK_TOTAL ][ ubEnemyType ] += usEnemyPopTable[ ubEnemyRank ][ ubEnemyType ]; } } // add up totals by rank across columns (types) for ( ubEnemyRank = 0; ubEnemyRank <= ENEMY_RANK_TOTAL; ubEnemyRank++ ) { for ( ubEnemyType = 0; ubEnemyType < ENEMY_TYPE_TOTAL; ubEnemyType++ ) { usEnemyPopTable[ ubEnemyRank ][ ENEMY_TYPE_TOTAL ] += usEnemyPopTable[ ubEnemyRank ][ ubEnemyType ]; } } // avoid division by zero if ( usEnemyPopTable[ ENEMY_RANK_TOTAL ][ ENEMY_TYPE_TOTAL ] > 0 ) { // calculate rank percentages for ( ubEnemyRank = 0; ubEnemyRank < ENEMY_RANK_PERCENT; ubEnemyRank++ ) { usEnemyPopTable[ ubEnemyRank ][ ENEMY_TYPE_PERCENT ] = ( ( 100 * usEnemyPopTable[ ubEnemyRank ][ ENEMY_TYPE_TOTAL ] ) / usEnemyPopTable[ ENEMY_RANK_TOTAL ][ ENEMY_TYPE_TOTAL ] ); } // calculate type percentages for ( ubEnemyType = 0; ubEnemyType < ENEMY_TYPE_PERCENT; ubEnemyType++ ) { usEnemyPopTable[ ENEMY_RANK_PERCENT ][ ubEnemyType ] = ( ( 100 * usEnemyPopTable[ ENEMY_RANK_TOTAL ][ ubEnemyType ] ) / usEnemyPopTable[ ENEMY_RANK_TOTAL ][ ENEMY_TYPE_TOTAL ] ); } } usX = VIEWER_RIGHT + 10; usY = 200; // titles and headings mean 2 extra rows ClearViewerRegion( usX, usY, 640, ( INT16 ) ( usY + ( POP_TABLE_Y_GAP * ( POP_TABLE_ENEMY_TYPES + 2 )) + 11 ) ); // print table title SetFontForeground( FONT_RED ); mprintf( usX, usY, L"ENEMY POPULATION:" ); usY += POP_TABLE_Y_GAP; // print horizontal labels for ( ubEnemyRank = 0; ubEnemyRank < TABLE_ENEMY_RANKS; ubEnemyRank++ ) { DrawTextToScreen( EnemyRankString[ ubEnemyRank ], ( UINT16 ) ( usX + POP_TABLE_X_OFFSET + ( POP_TABLE_X_GAP * ubEnemyRank ) ), usY, POP_TABLE_X_GAP, FONT10ARIAL, FONT_LTBLUE, 0, FALSE, RIGHT_JUSTIFIED ); } // print vertical labels for ( ubEnemyType = 0; ubEnemyType < POP_TABLE_ENEMY_TYPES; ubEnemyType++ ) { DrawTextToScreen( EnemyTypeString[ ubEnemyType ], usX, ( UINT16 ) ( usY + POP_TABLE_Y_GAP + ( POP_TABLE_Y_GAP * ubEnemyType ) ), POP_TABLE_X_OFFSET, FONT10ARIAL, FONT_LTBLUE, 0, FALSE, RIGHT_JUSTIFIED ); } // over to first column, and down 1 line usY += POP_TABLE_Y_GAP; usX += POP_TABLE_X_OFFSET; SetFontForeground( FONT_YELLOW ); // print table values for ( ubEnemyRank = 0; ubEnemyRank < TABLE_ENEMY_RANKS; ubEnemyRank++ ) { for ( ubEnemyType = 0; ubEnemyType < POP_TABLE_ENEMY_TYPES; ubEnemyType++ ) { // an exclusive OR operator, how often do ya see that, huh? :-) if ( ( ubEnemyRank == ENEMY_RANK_PERCENT ) ^ ( ubEnemyType == ENEMY_TYPE_PERCENT ) ) { wcscpy( wPrintSpec, L"%3d%%%%" ); } else if ( ( ubEnemyRank == ENEMY_RANK_PERCENT ) && ( ubEnemyType == ENEMY_TYPE_PERCENT ) ) { wcscpy( wPrintSpec, L"" ); } else { wcscpy( wPrintSpec, L"%4d" ); } swprintf( wTempString, wPrintSpec, usEnemyPopTable[ ubEnemyRank ][ ubEnemyType ] ); DrawTextToScreen( wTempString, ( UINT16 ) ( usX + ( POP_TABLE_X_GAP * ubEnemyRank ) ), ( UINT16 ) ( usY + ( POP_TABLE_Y_GAP * ubEnemyType ) ), POP_TABLE_X_GAP, FONT10ARIAL, FONT_YELLOW, 0, FALSE, RIGHT_JUSTIFIED ); } } } enum { ENEMIES_KILLED_IN_TACTICAL, ENEMIES_KILLED_IN_AUTO_RESOLVE, ENEMIES_KILLED_TOTAL, ENEMIES_KILLED_PERCENT, KILLED_TABLE_ROWS }; CHAR16 EnemiesKilledString[ KILLED_TABLE_ROWS ][ 10 ] = { L"Tact", L"Auto", L" TOT", L" %%", }; #define KILLED_TABLE_X_OFFSET 30 #define KILLED_TABLE_X_GAP 30 #define KILLED_TABLE_Y_GAP 10 void PrintEnemiesKilledTable() { UINT16 usX, usY; UINT16 usEnemiesKilledTable[ TABLE_ENEMY_RANKS ][ KILLED_TABLE_ROWS ]; UINT8 ubEnemyRank; UINT8 ubKillType; CHAR16 wPrintSpec[ 10 ]; CHAR16 wTempString [10 ]; memset( &usEnemiesKilledTable, 0, sizeof ( usEnemiesKilledTable ) ); // fill table with raw data for ( ubKillType = 0; ubKillType < ENEMIES_KILLED_TOTAL; ubKillType++ ) { for ( ubEnemyRank = 0; ubEnemyRank < ENEMY_RANK_TOTAL; ubEnemyRank++ ) { usEnemiesKilledTable[ ubEnemyRank ][ ubKillType ] = gStrategicStatus.usEnemiesKilled[ ubKillType ][ ubEnemyRank ]; } } // count how many enemies of each type & rank there are // add up totals across rows (ranks) by type for ( ubKillType = 0; ubKillType < ENEMIES_KILLED_TOTAL; ubKillType++ ) { for ( ubEnemyRank = 0; ubEnemyRank < ENEMY_RANK_TOTAL; ubEnemyRank++ ) { usEnemiesKilledTable[ ENEMY_RANK_TOTAL ][ ubKillType ] += usEnemiesKilledTable[ ubEnemyRank ][ ubKillType ]; } } // add up totals by rank across columns (types) for ( ubEnemyRank = 0; ubEnemyRank <= ENEMY_RANK_TOTAL; ubEnemyRank++ ) { for ( ubKillType = 0; ubKillType < ENEMIES_KILLED_TOTAL; ubKillType++ ) { usEnemiesKilledTable[ ubEnemyRank ][ ENEMIES_KILLED_TOTAL ] += usEnemiesKilledTable[ ubEnemyRank ][ ubKillType ]; } } // avoid division by zero if ( usEnemiesKilledTable[ ENEMY_RANK_TOTAL ][ ENEMIES_KILLED_TOTAL ] > 0 ) { // calculate rank percentages for ( ubEnemyRank = 0; ubEnemyRank < ENEMY_RANK_PERCENT; ubEnemyRank++ ) { usEnemiesKilledTable[ ubEnemyRank ][ ENEMIES_KILLED_PERCENT ] = ( ( 100 * usEnemiesKilledTable[ ubEnemyRank ][ ENEMIES_KILLED_TOTAL ] ) / usEnemiesKilledTable[ ENEMY_RANK_TOTAL ][ ENEMIES_KILLED_TOTAL ] ); } // calculate kill type percentages for ( ubKillType = 0; ubKillType < ENEMIES_KILLED_PERCENT; ubKillType++ ) { usEnemiesKilledTable[ ENEMY_RANK_PERCENT ][ ubKillType ] = ( ( 100 * usEnemiesKilledTable[ ENEMY_RANK_TOTAL ][ ubKillType ] ) / usEnemiesKilledTable[ ENEMY_RANK_TOTAL ][ ENEMIES_KILLED_TOTAL ] ); } } usX = VIEWER_RIGHT + 10; usY = 310; // titles and headings mean 2 extra rows ClearViewerRegion( usX, usY, 640, ( INT16 ) ( usY + ( KILLED_TABLE_Y_GAP * ( KILLED_TABLE_ROWS + 2 ) ) + 11) ); // print table title SetFontForeground( FONT_RED ); mprintf( usX, usY, L"ENEMIES KILLED:" ); usY += KILLED_TABLE_Y_GAP; // print horizontal labels for ( ubEnemyRank = 0; ubEnemyRank < TABLE_ENEMY_RANKS; ubEnemyRank++ ) { DrawTextToScreen( EnemyRankString[ ubEnemyRank ], ( UINT16 ) ( usX + KILLED_TABLE_X_OFFSET + ( KILLED_TABLE_X_GAP * ubEnemyRank ) ), usY, KILLED_TABLE_X_GAP, FONT10ARIAL, FONT_LTBLUE, 0, FALSE, RIGHT_JUSTIFIED ); } // print vertical labels for ( ubKillType = 0; ubKillType < KILLED_TABLE_ROWS; ubKillType++ ) { DrawTextToScreen( EnemiesKilledString[ ubKillType ], usX, ( UINT16 ) ( usY + KILLED_TABLE_Y_GAP + ( KILLED_TABLE_Y_GAP * ubKillType ) ), KILLED_TABLE_X_OFFSET, FONT10ARIAL, FONT_LTBLUE, 0, FALSE, RIGHT_JUSTIFIED ); } // over to first column, and down 1 line usY += KILLED_TABLE_Y_GAP; usX += KILLED_TABLE_X_OFFSET; SetFontForeground( FONT_YELLOW ); // print table values for ( ubEnemyRank = 0; ubEnemyRank < TABLE_ENEMY_RANKS; ubEnemyRank++ ) { for ( ubKillType = 0; ubKillType < KILLED_TABLE_ROWS; ubKillType++ ) { // an exclusive OR operator, how often do ya see that, huh? :-) if ( ( ubEnemyRank == ENEMY_RANK_PERCENT ) ^ ( ubKillType == ENEMIES_KILLED_PERCENT ) ) { wcscpy( wPrintSpec, L"%3d%%%%" ); } else if ( ( ubEnemyRank == ENEMY_RANK_PERCENT ) && ( ubKillType == ENEMIES_KILLED_PERCENT ) ) { wcscpy( wPrintSpec, L"" ); } else { wcscpy( wPrintSpec, L"%4d" ); } swprintf( wTempString, wPrintSpec, usEnemiesKilledTable[ ubEnemyRank ][ ubKillType ] ); DrawTextToScreen( wTempString, ( UINT16 ) ( usX + ( KILLED_TABLE_X_GAP * ubEnemyRank ) ), ( UINT16 ) ( usY + ( KILLED_TABLE_Y_GAP * ubKillType ) ), KILLED_TABLE_X_GAP, FONT10ARIAL, FONT_YELLOW, 0, FALSE, RIGHT_JUSTIFIED ); } } } UINT8 ChooseEnemyIconColor( UINT16 ubAdmins, UINT16 ubTroops, UINT16 ubElites, UINT16 ubTanks, UINT16 ubJeeps ) { UINT8 ubIconColor; // The colors are: // Yellow Admins only // Red Troops only // Gray Elites only // Orange Mixed, no elites (Admins + Troops) // Burgundy Mixed, with elites (Elites + (Admins OR Troops)) Assert( ubAdmins || ubTroops || ubElites || ubTanks || ubJeeps ); if ( ubElites ) { if ( ubTroops || ubAdmins ) ubIconColor = ICON_COLOR_BURGUNDY; else ubIconColor = ICON_COLOR_GRAY; } else // no elites { if ( ubTroops ) { if ( ubAdmins ) ubIconColor = ICON_COLOR_ORANGE; else ubIconColor = ICON_COLOR_RED; } else // admins only { ubIconColor = ICON_COLOR_YELLOW; } } return ( ubIconColor ); } void BlitGroupIcon( UINT8 ubIconType, UINT8 ubIconColor, UINT32 uiX, UINT32 uiY, HVOBJECT hVObject ) { UINT8 ubObjectIndex; Assert( ubIconType < NUM_ICON_TYPES ); Assert( ubIconColor < NUM_ICON_COLORS ); ubObjectIndex = (UINT8)( ubIconType * NUM_ICON_COLORS ) + ubIconColor; BltVideoObject( FRAME_BUFFER, hVObject, ubObjectIndex, uiX, uiY, VO_BLT_SRCTRANSPARENCY, NULL ); } void PrintDetailedEnemiesInSectorInfo( INT32 iScreenX, INT32 iScreenY, UINT8 ubSectorX, UINT8 ubSectorY ) { SECTORINFO *pSector; GROUP *pGroup; INT32 iDesired, iSurplus; UINT8 ubGroupCnt = 0; UINT8 ubSectorID; CHAR16 wString[ 120 ]; CHAR16 wSubString[ 120 ]; INT16 iGarrisonIndex; INT16 iPatrolIndex; WAYPOINT *pFinalWaypoint; pSector = &SectorInfo[ SECTOR( ubSectorX, ubSectorY ) ]; Ensure_RepairedGarrisonGroup( &gGarrisonGroup, &giGarrisonArraySize ); /* added NULL fix, 2007-03-03, Sgt. Kolja */ // handle garrisoned enemies if( pSector->ubGarrisonID != NO_GARRISON ) { iDesired = gArmyComp[ gGarrisonGroup[ pSector->ubGarrisonID ].ubComposition ].bDesiredPopulation; iSurplus = pSector->ubNumTroops + pSector->ubNumAdmins + pSector->ubNumElites + pSector->ubNumTanks - iDesired; SetFontForeground( FONT_WHITE ); swprintf( wString, L"Garrison #%d: %d desired, ", pSector->ubGarrisonID, iDesired ); if( iSurplus >= 0 ) { swprintf( wSubString, L"%d surplus troops", iSurplus ); wcscat( wString, wSubString ); } else { swprintf( wSubString, L"%d reinforcements requested", -iSurplus ); wcscat( wString, wSubString ); } mprintf( iScreenX, iScreenY, wString ); iScreenY += 10; if( gGarrisonGroup[ pSector->ubGarrisonID ].ubPendingGroupID ) { pGroup = GetGroup( gGarrisonGroup[ pSector->ubGarrisonID ].ubPendingGroupID ); if( pGroup ) { mprintf( iScreenX, iScreenY, L"%d reinforcements on route from group %d in %c%d", pGroup->ubGroupSize, pGroup->ubGroupID, pGroup->ubSectorY + 'A' - 1, pGroup->ubSectorX ); } else { //ERROR! Should be a valid group... } } else { mprintf( iScreenX, iScreenY, L"No pending reinforcements for this sector." ); } iScreenY += 10; } else { SetFontForeground( FONT_GRAY2 ); mprintf( iScreenX, iScreenY, L"No garrison information for this sector." ); iScreenY += 10; } // handle mobile enemies anchored in this sector pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && !pGroup->fVehicle ) { if ( ( pGroup->ubSectorX == ubSectorX ) && ( pGroup->ubSectorY == ubSectorY ) ) { Assert( pGroup->pEnemyGroup->ubIntention < NUM_ENEMY_INTENTIONS ); swprintf( wString, L"Group %c: %s", 'A' + ubGroupCnt, gwGroupTypeString[ pGroup->pEnemyGroup->ubIntention ] ); switch ( pGroup->pEnemyGroup->ubIntention ) { case ASSAULT: case PURSUIT: iGarrisonIndex = FindGarrisonIndexForGroupIDPending( pGroup->ubGroupID ); if ( iGarrisonIndex != -1 ) { ubSectorID = gGarrisonGroup[ iGarrisonIndex ].ubSectorID; swprintf( wSubString, L", target sector %c%d", SECTORY( ubSectorID ) + 'A' - 1, SECTORX( ubSectorID ) ); } else { pFinalWaypoint = GetFinalWaypoint( pGroup ); if( pFinalWaypoint ) { if( pFinalWaypoint->x == gModSettings.ubSAISpawnSectorX && pFinalWaypoint->y == gModSettings.ubSAISpawnSectorY ) { swprintf( wSubString, L" - group returning to pool."); } else { swprintf( wSubString, L" - moving to %c%d", pFinalWaypoint->y + 'A' - 1, pFinalWaypoint->x ); } } else { swprintf( wSubString, L" - can't determine target sector" ); } } wcscat( wString, wSubString ); break; case REINFORCEMENTS: case STAGING: // check if it's reinforcing a garrison iGarrisonIndex = FindGarrisonIndexForGroupIDPending( pGroup->ubGroupID ); if ( iGarrisonIndex != -1 ) { ubSectorID = gGarrisonGroup[ iGarrisonIndex ].ubSectorID; swprintf( wSubString, L", dest sector %c%d", SECTORY( ubSectorID ) + 'A' - 1, SECTORX( ubSectorID ) ); wcscat( wString, wSubString ); } else // must be reinforcing a patrol { iPatrolIndex = FindPatrolGroupIndexForGroupIDPending( pGroup->ubGroupID ); if( iPatrolIndex != -1 ) { pFinalWaypoint = GetFinalWaypoint( pGroup ); Assert( pFinalWaypoint ); swprintf( wSubString, L", Patrol #%d, dest sector %c%d", iPatrolIndex, pFinalWaypoint->y + 'A' - 1, pFinalWaypoint->x ); wcscat( wString, wSubString ); } else { pFinalWaypoint = GetFinalWaypoint( pGroup ); if( pFinalWaypoint ) { if( pFinalWaypoint->x == gModSettings.ubSAISpawnSectorX && pFinalWaypoint->y == gModSettings.ubSAISpawnSectorY ) { swprintf( wSubString, L" - group returning to pool."); } else { swprintf( wSubString, L" - lost group moving to %c%d", pFinalWaypoint->y + 'A' - 1, pFinalWaypoint->x ); } } else { swprintf( wSubString, L" (LOST GROUP!)" ); } wcscat( wString, wSubString ); } } break; case PATROL: iPatrolIndex = FindPatrolGroupIndexForGroupID( pGroup->ubGroupID ); if( iPatrolIndex != -1 ) { swprintf( wSubString, L"#%d, next sector %c%d", iPatrolIndex, pGroup->ubNextY + 'A' - 1, pGroup->ubNextX ); } else { swprintf( wSubString, L"#err, FLOATING GROUP!" ); } wcscat( wString, wSubString ); break; } mprintf( iScreenX, iScreenY, wString ); iScreenY += 10; ubGroupCnt++; // no room on screen to display info for more than 3 groups in one sector! if ( ubGroupCnt >= 3 ) { break; } } } pGroup = pGroup->next; } } #endif