/////////////////////////////// Soldier Target Selection //////////////////////////////////////////// void SoldierSelection::Setup( UINT32 aVal ) { Destroy(); SOLDIERTYPE * pSoldier = NULL; GetSoldier( &pSoldier, gusSelectedSoldier ); if ( pSoldier == NULL ) return; if ( pSoldier->CanUseSkill(aVal) ) { usSkill = aVal; SetupPopup("SoldierSelection"); POPUP_OPTION *pOption; CHAR16 pStr[300]; // pretty simple: we find every soldier in a radius around the target position and add him to the list // loop through all soldiers around for ( UINT32 cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID ; cnt <= gTacticalStatus.Team[ CIV_TEAM ].bLastID ; ++cnt ) { INT32 iRange = GetRangeInCellCoordsFromGridNoDiff( sTraitsMenuTargetGridNo, MercPtrs[ cnt ]->sGridNo ); if ( iRange < 100 ) { if ( cnt != pSoldier->ubID ) { swprintf( pStr, L"%s", MercPtrs[ cnt ]->GetName() ); pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction<void, UINT8>( &Wrapper_Function_SoldierSelection, cnt ) ); // grey out if no artillery can be called from this sector if ( 0 ) { // Set this option off. pOption->setAvail(new popupCallbackFunction<bool,void*>( &Popup_OptionOff, NULL )); } GetPopup()->addOption( *pOption ); } } } // cancel option swprintf( pStr, pSkillMenuStrings[SKILLMENU_CANCEL] ); pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction<void,UINT32>( &Wrapper_Cancel_SoldierSelection, 0 ) ); GetPopup()->addOption( *pOption ); } // same y, different x SetPos(gSkillSelection.GetMaxPosX(), usTraitMenuPosY); }
UINT16 FindGridNoFromSweetSpotExcludingSweetSpotInQuardent( SOLDIERTYPE *pSoldier, INT16 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection, INT8 ubQuardentDir ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2; INT16 sGridNo; INT32 uiRange, uiLowestRange = 999999; INT16 sLowestGridNo=-1; INT32 leftmost; BOOLEAN fFound = FALSE; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; // Switch on quadrent if ( ubQuardentDir == SOUTHEAST ) { sBottom = 0; sLeft = 0; } uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if ( sSweetGridNo == sGridNo ) { continue; } if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); if ( uiRange < uiLowestRange ) { sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } if ( fFound ) { // Set direction to center of map! *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sLowestGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) ); return( sLowestGridNo ); } else { return( NOWHERE ); } }
UINT16 FindGridNoFromSweetSpotThroughPeople( SOLDIERTYPE *pSoldier, INT16 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2; INT16 sGridNo; INT32 uiRange, uiLowestRange = 999999; INT16 sLowestGridNo=-1; INT32 leftmost; BOOLEAN fFound = FALSE; SOLDIERTYPE soldier; UINT8 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. memset( &soldier, 0, sizeof( SOLDIERTYPE ) ); soldier.bLevel = 0; soldier.bTeam = pSoldier->bTeam; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) ); uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) && gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); { if ( uiRange < uiLowestRange ) { sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } } gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; if ( fFound ) { // Set direction to center of map! *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sLowestGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) ); return( sLowestGridNo ); } else { return( NOWHERE ); } }
UINT16 FindGridNoFromSweetSpotWithStructDataFromSoldier( SOLDIERTYPE *pSoldier, UINT16 usAnimState, INT8 ubRadius, UINT8 *pubDirection, BOOLEAN fClosestToMerc, SOLDIERTYPE *pSrcSoldier ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2, cnt3; INT16 sGridNo; INT32 uiRange, uiLowestRange = 999999; INT16 sLowestGridNo=-1; INT32 leftmost; BOOLEAN fFound = FALSE; UINT8 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; UINT8 ubBestDirection=0; INT16 sSweetGridNo; SOLDIERTYPE soldier; sSweetGridNo = pSrcSoldier->sGridNo; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. memset( &soldier, 0, sizeof( SOLDIERTYPE ) ); soldier.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) ); uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) && gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) ) { BOOLEAN fDirectionFound = FALSE; UINT16 usOKToAddStructID; STRUCTURE_FILE_REF * pStructureFileRef; UINT16 usAnimSurface; if ( fClosestToMerc != 3 ) { if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) { usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } // Get animation surface... usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); // Get structure ref... pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState ); // Check each struct in each direction for( cnt3 = 0; cnt3 < 8; cnt3++ ) { if (OkayToAddStructureToWorld( (INT16)sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ cnt3 ]]), usOKToAddStructID ) ) { fDirectionFound = TRUE; break; } } } else { fDirectionFound = TRUE; cnt3 = (UINT8)Random( 8 ); } if ( fDirectionFound ) { if ( fClosestToMerc == 1 ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ); } else if ( fClosestToMerc == 2 ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ) + GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); } else { //uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); uiRange = abs((sSweetGridNo / MAXCOL) - (sGridNo / MAXCOL)) + abs((sSweetGridNo % MAXROW) - (sGridNo % MAXROW)); } if ( uiRange < uiLowestRange || (uiRange == uiLowestRange && PythSpacesAway( pSoldier->sGridNo, sGridNo ) < PythSpacesAway( pSoldier->sGridNo, sLowestGridNo ) ) ) { ubBestDirection = (UINT8)cnt3; sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } } gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; if ( fFound ) { // Set direction we chose... *pubDirection = ubBestDirection; return( sLowestGridNo ); } else { return( NOWHERE ); } }
void DisplayRangeToTarget( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo ) { UINT16 usRange=0; CHAR16 zOutputString[512]; UINT8 title = (UsingNewCTHSystem() == true ? DC_MSG__NCTH_GUN_RANGE_INFORMATION : DC_MSG__GUN_RANGE_INFORMATION); if( sTargetGridNo == NOWHERE || sTargetGridNo == 0 ) { return; } { UINT8 ubLightLevel = LightTrueLevel(sTargetGridNo, gsInterfaceLevel); UINT8 ubBrightness = 100 - 100 * (ubLightLevel-SHADE_MAX)/(SHADE_MIN-SHADE_MAX); // percentage UINT8 ubTerrainType = NO_TERRAIN; // anv: additional tile properties ADDITIONAL_TILE_PROPERTIES_VALUES zGivenTileProperties; memset(&zGivenTileProperties,0,sizeof(zGivenTileProperties)); if(gGameExternalOptions.fAdditionalTileProperties) { zGivenTileProperties = GetAllAdditonalTilePropertiesForGrid(sTargetGridNo, gsInterfaceLevel); } else { ubTerrainType = GetTerrainTypeForGrid(sTargetGridNo, gsInterfaceLevel); } INT8 ubCover = - GetSightAdjustment(pSoldier, sTargetGridNo, gsInterfaceLevel); //display a string with cover value of current selected merc and brightness //swprintf( zOutputString, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetTerrainName(ubTerrainType), ubBrightness ); //Display the msg //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); if(gGameExternalOptions.fAdditionalTileProperties) { if(!gGameExternalOptions.fCoverTooltipDetailedTileProperties) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetDetailedTerrainName(zGivenTileProperties), ubBrightness ); } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_INFORMATION_WITH_DETAILED_CAMO], ubCover, ubBrightness ); UINT8 ubApplicableProperties = 0; swprintf( zOutputString, L"" ); if(zGivenTileProperties.bWoodCamoAffinity > 0) { swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__WOOD]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bWoodCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bDesertCamoAffinity > 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DESERT]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bDesertCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bUrbanCamoAffinity > 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__URBAN]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bUrbanCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bSnowCamoAffinity > 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__SNOW]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bSnowCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bSoundModifier != 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DETAILED_SOUND]); if(zGivenTileProperties.bSoundModifier > 0) swprintf( zOutputString + wcslen(zOutputString), L": +%d", zGivenTileProperties.bSoundModifier); else swprintf( zOutputString + wcslen(zOutputString), L": %d", zGivenTileProperties.bSoundModifier); ubApplicableProperties++; } if(zGivenTileProperties.bStealthDifficultyModifer != 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DETAILED_STEALTH]); if(zGivenTileProperties.bStealthDifficultyModifer > 0) swprintf( zOutputString + wcslen(zOutputString), L": +%d/100", zGivenTileProperties.bStealthDifficultyModifer); else swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bStealthDifficultyModifer); ubApplicableProperties++; } if(zGivenTileProperties.bTrapBonus != 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DETAILED_TRAP_LEVEL]); if(zGivenTileProperties.bTrapBonus > 0) swprintf( zOutputString + wcslen(zOutputString), L": +%d", zGivenTileProperties.bTrapBonus); else swprintf( zOutputString + wcslen(zOutputString), L": %d", zGivenTileProperties.bTrapBonus); ubApplicableProperties++; } if( wcslen(zOutputString) > 0 ) ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); } } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetTerrainName(ubTerrainType), ubBrightness ); } } //Get the range to the target location usRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sTargetGridNo ); //if the soldier has a weapon in hand, display gun range and chance to hit if( WeaponInHand( pSoldier ) ) { UINT32 uiHitChance; //AXP 30.03.2007: Fix CtH calculation for first shot after changing aim level (roof/ground) INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8)gsInterfaceLevel; uiHitChance = CalcChanceToHitGun( pSoldier, sTargetGridNo, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } pSoldier->bTargetLevel = bTempTargetLevel; // HEADROCK HAM 3.6: Calculate Gun Range using formula. // Flugente: we might be equipped with an underbarrel gun.... OBJECTTYPE* pObjhand = pSoldier->GetUsedWeapon(&pSoldier->inv[HANDPOS]); UINT16 usGunRange = GunRange(pObjhand, pSoldier ); // SANDRO - added argument swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, usGunRange / 10, uiHitChance ); //Display the msg ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); } else { swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, 0, 0 ); //Display the msg ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); } //increment the display gun range counter ( just seeing how many times people use it ) //gJa25SaveStruct.uiDisplayGunRangeCounter++; }