#include "items.h" #include "weapons.h" #include "Interface Cursors.h" #include "overhead.h" #include "Handle UI.h" #include "Animation Control.h" #include "points.h" #include "Sound Control.h" #include "Sys globals.h" #include "Isometric Utils.h" #include "Animation Data.h" #include "worldman.h" #include "ui cursors.h" #include "los.h" #include "renderworld.h" #include "interface.h" #include "ui cursors.h" #include "cursors.h" #include "cursor control.h" #include "structure wrap.h" #include "physics.h" #include "soldier macros.h" #include "text.h" #include "spread burst.h" #include "GameSettings.h" #include "Interactive Tiles.h" #include "PATHAI.H" #include "SkillCheck.h" // added by SANDRO #include "message.H" //ddd #include "english.h" // added by Flugente //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; // FUNCTIONS FOR ITEM CURSOR HANDLING UINT8 HandleActivatedTargetCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos, BOOLEAN fShowAPs, BOOLEAN fRecalc, UINT32 uiCursorFlags ); UINT8 HandleNonActivatedTargetCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos, BOOLEAN fShowAPs, BOOLEAN fRecalc, UINT32 uiCursorFlags ); UINT8 HandleKnifeCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandlePunchCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlagsl ); UINT8 HandleAidCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleActivatedTossCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubCursor ); UINT8 HandleNonActivatedTossCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fRecalc, UINT32 uiCursorFlags, UINT8 ubCursor ); UINT8 HandleWirecutterCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); UINT8 HandleRepairCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); UINT8 HandleRefuelCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); UINT8 HandleRemoteCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleCameraCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleBloodbagCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleSplintCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleBombCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleJarCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); UINT8 HandleTinCanCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente UINT8 HandleHandcuffCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente UINT8 HandleApplyItemCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente UINT8 HandleHackCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente extern BOOLEAN HandleCheckForBadChangeToGetThrough( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, INT32 sTargetGridNo , INT8 bLevel ); UINT8 DefaultAutofireBulletsByGunClass( SOLDIERTYPE* pSoldier ); // added by Sevenfm BOOLEAN gfCannotGetThrough = FALSE; extern UINT32 guiUITargetSoldierId; BOOLEAN gfDisplayFullCountRing = FALSE; BOOLEAN gfAutofireInitBulletNum = FALSE; UINT8 gubAimRingRanges[] = {30, 55, 75, 90}; // These are the cutoff ranges for the aiming rings extern INT8 gbNumBurstLocations; //number of burst points BOOLEAN GetMouseRecalcAndShowAPFlags( UINT32 *puiCursorFlags, BOOLEAN *pfShowAPs ) { UINT32 uiCursorFlags; static BOOLEAN fDoNewTile = FALSE; BOOLEAN fRecalc = FALSE; BOOLEAN fShowAPs = FALSE; // SET FLAGS FOR CERTAIN MOUSE MOVEMENTS GetCursorMovementFlags( &uiCursorFlags ); // Force if we are currently cycling guys... if ( gfUIForceReExamineCursorData ) { fDoNewTile = TRUE; fRecalc = TRUE; gfUIForceReExamineCursorData = FALSE; } // IF CURSOR IS MOVING if ( uiCursorFlags & MOUSE_MOVING ) { // IF CURSOR WAS PREVIOUSLY STATIONARY, MAKE THE ADDITIONAL CHECK OF GRID POS CHANGE //if ( uiCursorFlags & MOUSE_MOVING_NEW_TILE ) { // Reset counter RESETCOUNTER( PATHFINDCOUNTER ); fDoNewTile = TRUE; } } if ( uiCursorFlags & MOUSE_STATIONARY ) { // ONLY DIPSLAY APS AFTER A DELAY if ( COUNTERDONE( PATHFINDCOUNTER ) ) { // Don't reset counter: One when we move again do we do this! fShowAPs = TRUE; if ( fDoNewTile ) { fDoNewTile = FALSE; fRecalc = TRUE; } } } if ( puiCursorFlags ) { (*puiCursorFlags) = uiCursorFlags; } if ( pfShowAPs ) { (*pfShowAPs) = fShowAPs; } return( fRecalc ); } // FUNCTIONS FOR CURSOR DETERMINATION! UINT8 GetProperItemCursor( UINT8 ubSoldierID, UINT16 ubItemIndex, INT32 usMapPos, BOOLEAN fActivated ) { SOLDIERTYPE *pSoldier; UINT32 uiCursorFlags; BOOLEAN fShowAPs = FALSE; BOOLEAN fRecalc = FALSE; INT32 sTargetGridNo = usMapPos; UINT8 ubCursorID=0; UINT8 ubItemCursor = 0; pSoldier = MercPtrs[ ubSoldierID ]; fRecalc = GetMouseRecalcAndShowAPFlags( &uiCursorFlags, &fShowAPs ); // ATE: Update attacking weapon! // CC has added this attackingWeapon stuff and I need to update it constantly for // CTGH algorithms if ( gTacticalStatus.ubAttackBusyCount == 0 && Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass & IC_WEAPON ) { pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem; } // Calculate target gridno! if ( gfUIFullTargetFound ) { sTargetGridNo = MercPtrs[ gusUIFullTargetID ]->sGridNo; } else { sTargetGridNo = usMapPos; } ubItemCursor = GetActionModeCursor( pSoldier ); switch( ubItemCursor ) { case PUNCHCURS: // Determine whether gray or red! ubCursorID = HandlePunchCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case KNIFECURS: //Madd: quick hack to make wirecutter cursor appear when using a knife that can cut through wire // sevenfm: check that not using bayonet attached to the gun //if ( Item[ubItemIndex].wirecutters && IsCuttableWireFenceAtGridNo( sTargetGridNo ) && pSoldier->pathing.bLevel == 0 ) if (ItemIsWirecutters(ubItemIndex) && IsCuttableWireFenceAtGridNo( sTargetGridNo ) && pSoldier->pathing.bLevel == 0 && pSoldier->bWeaponMode != WM_ATTACHED_BAYONET) { ubCursorID = GOOD_WIRECUTTER_UICURSOR; } else ubCursorID = HandleKnifeCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case AIDCURS: ubCursorID = HandleAidCursor( pSoldier, usMapPos, fActivated, uiCursorFlags ); break; case TARGETCURS: // Set merc glow script SetMercGlowFast( ); if ( fActivated ) { ubCursorID = HandleActivatedTargetCursor( pSoldier, sTargetGridNo, fShowAPs, fRecalc, uiCursorFlags ); } else { ubCursorID = HandleNonActivatedTargetCursor( pSoldier, sTargetGridNo, fShowAPs, fRecalc, uiCursorFlags ); } // ATE: Only do this if we are in combat! if ( gCurrentUIMode == ACTION_MODE && ( gTacticalStatus.uiFlags & INCOMBAT ) ) { // Alrighty, let's change the cursor! if ( fRecalc && gfUIFullTargetFound ) { // ATE: Check for ammo if ( IsValidTargetMerc( (UINT8)gusUIFullTargetID ) && EnoughAmmo( pSoldier, FALSE, HANDPOS ) && (!pSoldier->IsValidSecondHandShotForReloadingPurposes( ) || EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS) ) ) { // IF it's an ememy, goto confirm action mode if ( ( guiUIFullTargetFlags & ENEMY_MERC ) && ( guiUIFullTargetFlags & VISIBLE_MERC ) && !( guiUIFullTargetFlags & DEAD_MERC ) && !gfCannotGetThrough ) { guiPendingOverrideEvent = A_CHANGE_TO_CONFIM_ACTION; } } } } break; case TOSSCURS: case TRAJECTORYCURS: if ( fActivated ) { if ( !gfUIHandlePhysicsTrajectory ) { ubCursorID = HandleNonActivatedTossCursor( pSoldier, sTargetGridNo, fRecalc, uiCursorFlags, ubItemCursor ); } else { ubCursorID = HandleActivatedTossCursor( pSoldier, sTargetGridNo, ubItemCursor ); } } else { ubCursorID = HandleNonActivatedTossCursor( pSoldier, sTargetGridNo, fRecalc, uiCursorFlags, ubItemCursor ); } break; case BOMBCURS: ubCursorID = HandleBombCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case CAMERACURS: ubCursorID = HandleCameraCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case BLOODBAGCURS: ubCursorID = HandleBloodbagCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case SPLINTCURS: ubCursorID = HandleSplintCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case REMOTECURS: ubCursorID = HandleRemoteCursor( pSoldier, sTargetGridNo, fActivated, uiCursorFlags ); break; case WIRECUTCURS: ubCursorID = HandleWirecutterCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case REPAIRCURS: ubCursorID = HandleRepairCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case JARCURS: ubCursorID = HandleJarCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case TINCANCURS: ubCursorID = HandleTinCanCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case REFUELCURS: ubCursorID = HandleRefuelCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case FORTICURS: ubCursorID = HandleFortificationCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case HANDCUFFCURS: ubCursorID = HandleHandcuffCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case APPLYITEMCURS: ubCursorID = HandleApplyItemCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case INTERACTIVEACTIONCURS: ubCursorID = HandleHackCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; case INVALIDCURS: ubCursorID = INVALID_ACTION_UICURSOR; break; } if ( !( gTacticalStatus.uiFlags & INCOMBAT ) ) { if ( gfUIFullTargetFound ) { PauseRT( TRUE ); } else { PauseRT( FALSE ); } } return( ubCursorID ); } // WANNE: Shows the target cursor over the enemy soldier UINT8 HandleActivatedTargetCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos, BOOLEAN fShowAPs, BOOLEAN fRecalc, UINT32 uiCursorFlags ) { UINT8 switchVal; BOOLEAN fEnoughPoints = TRUE; UINT8 bFutureAim; INT16 sAPCosts; INT16 sAPCostsMin; UINT16 usCursor=0; BOOLEAN fMaxPointLimitHit = FALSE; UINT16 usInHand; extern UINT32 guiNewUICursor; UINT16 reverse = 0; // HEADROCK HAM 4: Required for new Aiming Level Limit function UINT8 maxAimLevels = AllowedAimingLevels(pSoldier, usMapPos); usInHand = pSoldier->inv[ HANDPOS ].usItem; if ( Item[ usInHand ].usItemClass != IC_THROWING_KNIFE ) { // If we are in realtime, follow! if ( ( !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { if ( ( gAnimControl[ MercPtrs[ gusSelectedSoldier ]->usAnimState ].uiFlags & ANIM_STATIONARY ) ) { if ( gUITargetShotWaiting ) { guiPendingOverrideEvent = CA_MERC_SHOOT; } } //SoldierFollowGridNo( pSoldier, sMapPos ); } // Check if we are reloading if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { if ( pSoldier->flags.fReloading || pSoldier->flags.fPauseAim ) { return( ACTION_TARGET_RELOADING ); } } } // Determine where we are shooting / aiming //if ( fRecalc ) { DetermineCursorBodyLocation( (UINT8)gusSelectedSoldier, TRUE, TRUE ); } UINT8 ubMaxBullets = 1; INT8 bMaxAim; if ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) { if(pSoldier->bDoAutofire) { // sevenfm: change number of autofire bullets only once when switching cursor to action mode or changing firing mode if( !gfAutofireInitBulletNum ) { INT16 sCurAPCosts, sAPCosts; UINT16 usShotsLeft = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; if (pSoldier->IsValidSecondHandBurst()) { usShotsLeft = min( (pSoldier->inv[ SECONDHANDPOS ][0]->data.gun.ubGunShotsLeft), usShotsLeft ); } bMaxAim = pSoldier->aiData.bShownAimTime; // sevenfm: change default number of bullets in autofire according to ini settings ubMaxBullets = DefaultAutofireBulletsByGunClass( pSoldier ); if( ubMaxBullets == 0 ) { ubMaxBullets = (UINT8)usShotsLeft; bMaxAim = maxAimLevels; } sCurAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, bMaxAim); do { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, bMaxAim); } // sevenfm: removed (sAPCosts <= sCurAPCosts) because EnoughPoints() is enough while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && usShotsLeft >= pSoldier->bDoAutofire && pSoldier->bDoAutofire <= ubMaxBullets ); pSoldier->bDoAutofire--; gfAutofireInitBulletNum = TRUE; } gfUIAutofireBulletCount = TRUE; gsBulletCount = pSoldier->bDoAutofire; gsTotalBulletCount = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; if (pSoldier->IsValidSecondHandBurst()) { gsTotalBulletCount = min( (pSoldier->inv[ SECONDHANDPOS ][0]->data.gun.ubGunShotsLeft), gsTotalBulletCount ); } if(pSoldier->flags.autofireLastStep) //set the orange tint on the numbers if we at the last step gTintBulletCounts = TRUE; else gTintBulletCounts = FALSE; } else gfUIAutofireBulletCount = FALSE; gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(pSoldier->aiData.bShownAimTime ) ); gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; while(!EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) && pSoldier->bDoAutofire > 1) //oops, we don't have enough points if we are in auto-fire try to decrease bulletcount { pSoldier->bDoAutofire--; gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(pSoldier->aiData.bShownAimTime ) ); } const bool isCursorOnTarget = ( guiNewUICursor == ACTION_SHOOT_UICURSOR || guiNewUICursor == ACTION_TARGETBURST_UICURSOR || guiNewUICursor == ACTION_FLASH_SHOOT_UICURSOR || guiNewUICursor == ACTION_FLASH_BURST_UICURSOR || guiNewUICursor == ACTION_NOCHANCE_SHOOT_UICURSOR || guiNewUICursor == ACTION_NOCHANCE_BURST_UICURSOR ); // Start at maximum aiming levels if the option is toggled if (gGameSettings.fOptions[TOPTION_ALT_START_AIM] && isCursorOnTarget) { pSoldier->aiData.bShownAimTime = maxAimLevels; sAPCosts = CalcTotalAPsToAttack(pSoldier, usMapPos, TRUE, pSoldier->aiData.bShownAimTime); // Determine if we can afford! while (!EnoughPoints(pSoldier, sAPCosts, 0, FALSE)) { pSoldier->aiData.bShownAimTime -= 1; if (pSoldier->aiData.bShownAimTime < 0) { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; break; } sAPCosts = CalcTotalAPsToAttack(pSoldier, usMapPos, TRUE, pSoldier->aiData.bShownAimTime); } gsCurrentActionPoints = sAPCosts; } // If we don't have any points and we are at the first refine, do nothing but warn! if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) && (pSoldier->aiData.bShownAimTime == 0)) { gfUIDisplayActionPointsInvalid = TRUE; fMaxPointLimitHit = TRUE; } else { // WANNE: How oft increased? bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + 1 ); if ( bFutureAim <= maxAimLevels ) { if (reverse == 0) { // WANNE: Current AP costs for targeting //sAPCosts = MinAPsToAttack( pSoldier, usMapPos, TRUE, bFutureAim ) + ( bFutureAim ); sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, bFutureAim ); //gsCurrentActionPoints = sAPCosts; // Determine if we can afford! if ( !EnoughPoints( pSoldier, (INT16)sAPCosts, 0 , FALSE ) ) { fEnoughPoints = FALSE; } } else { //sAPCosts = MinAPsToAttack( pSoldier, usMapPos, TRUE, (maxAimLevels - (bFutureAim + 1)) ) + maxAimLevels - (bFutureAim + 1); sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (maxAimLevels - (bFutureAim + 1)) ); gsCurrentActionPoints = sAPCosts; //sAPCostsMin = MinAPsToAttack( pSoldier, usMapPos, TRUE, 0 ); sAPCostsMin = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, 0 ); if (sAPCosts < sAPCostsMin) { fEnoughPoints = FALSE; } } //// Determine if we can afford! //if ( !EnoughPoints( pSoldier, (INT16)sAPCosts, 0 , FALSE ) ) //{ // fEnoughPoints = FALSE; //} } } } if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { if ( !pSoldier->flags.fPauseAim ) { if ( COUNTERDONE( TARGETREFINE ) ) { // Reset counter RESETCOUNTER( TARGETREFINE ); if ( pSoldier->bDoBurst ) { pSoldier->aiData.bShownAimTime = REFINE_AIM_BURST; } else { pSoldier->aiData.bShownAimTime++; if ( pSoldier->aiData.bShownAimTime > maxAimLevels ) { pSoldier->aiData.bShownAimTime = maxAimLevels; } else { if ( pSoldier->aiData.bShownAimTime % 2 ) { PlayJA2Sample( TARG_REFINE_BEEP, RATE_11025, MIDVOLUME, 1, MIDDLEPAN ); } } } } } } if ( fRecalc ) { if ( gusUIFullTargetID != NOBODY ) { if ( SoldierToSoldierBodyPartChanceToGetThrough( pSoldier, MercPtrs[ gusUIFullTargetID ], pSoldier->bAimShotLocation ) < OK_CHANCE_TO_GET_THROUGH ) { gfCannotGetThrough = TRUE; } else { gfCannotGetThrough = FALSE; } } else { if ( SoldierToLocationChanceToGetThrough( pSoldier, usMapPos, (INT8) gsInterfaceLevel, pSoldier->bTargetCubeLevel, NOBODY ) < OK_CHANCE_TO_GET_THROUGH ) { gfCannotGetThrough = TRUE; } else { gfCannotGetThrough = FALSE; } } } // OK, if we begin to move, reset the cursor... if ( uiCursorFlags & MOUSE_MOVING ) { //gfCannotGetThrough = FALSE; } if ( pSoldier->flags.fDoSpread && pSoldier->bDoAutofire) //approximate spread-auto costs { if(gbNumBurstLocations > pSoldier->bDoAutofire) { INT16 sAPCosts; UINT16 usShotsLeft = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; if (pSoldier->IsValidSecondHandBurst()) { usShotsLeft = min( (pSoldier->inv[ SECONDHANDPOS ][0]->data.gun.ubGunShotsLeft), usShotsLeft ); } do { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, gsBurstLocations[0].sGridNo, TRUE, pSoldier->aiData.bShownAimTime); } while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && usShotsLeft >= pSoldier->bDoAutofire && gbNumBurstLocations >= pSoldier->bDoAutofire); pSoldier->bDoAutofire--; gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, gsBurstLocations[0].sGridNo, TRUE, pSoldier->aiData.bShownAimTime ); } } if ( fMaxPointLimitHit ) { // Check if we're in burst mode! if ( pSoldier->bDoBurst ) { usCursor = ACTION_TARGETREDBURST_UICURSOR; } else if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { usCursor = RED_THROW_UICURSOR; } else { usCursor = ACTION_TARGETRED_UICURSOR; } } else if ( pSoldier->bDoBurst ) { if ( pSoldier->flags.fDoSpread ) { usCursor = ACTION_TARGETREDBURST_UICURSOR; } else { //usCursor = ACTION_TARGETREDBURST_UICURSOR; usCursor = ACTION_TARGETCONFIRMBURST_UICURSOR; // HEADROCK HAM B1: Burst cursor now shows multiple CTH bars (one for each bullet) // and also a Targetted Bodypart indicator. //if(pSoldier->bDoAutofire == 0 && gGameSettings.fOptions[ TOPTION_CTH_CURSOR ]) if(pSoldier->bDoAutofire == 0) { if(UsingNewCTHSystem() == true) { //AXP 29.03.2007: Rooftop CtH fix. See below. INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8) gsInterfaceLevel; UINT32 uiHitChance; uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, pSoldier->aiData.bShownAimTime, pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } gfUICtHBar = TRUE; gbCtHBurstCount = 0; gCTHDisplay.MuzzleSwayPercentage = (gCTHDisplay.MuzzleSwayPercentage + uiHitChance)/2; gCTHDisplay.iShooterGridNo = pSoldier->sGridNo; gCTHDisplay.iTargetGridNo = usMapPos; // Calculate distance to target INT16 sX, sY, sXMap, sYMap; ConvertGridNoToCenterCellXY(pSoldier->sGridNo, &sX, &sY); ConvertGridNoToCenterCellXY(usMapPos, &sXMap, &sYMap); FLOAT dDeltaX = (FLOAT)( sX - sXMap ); FLOAT dDeltaY = (FLOAT)( sY - sYMap ); FLOAT d2DDistance = sqrt((dDeltaX*dDeltaX)+(dDeltaY*dDeltaY)); CalcMagFactorSimple( pSoldier, d2DDistance, pSoldier->aiData.bShownAimTime, usMapPos ); pSoldier->bTargetLevel = bTempTargetLevel; } else if(gGameSettings.fOptions[ TOPTION_CTH_CURSOR ]) { if (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 2) { // Burst mode only OBJECTTYPE * pInHand; pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); // Burst size gbCtHBurstCount = GetShotsPerBurst(pInHand); UINT8 i, saveDoBurst; // Save original burst value (should be 1 always, before the burst has been fired, but // this is just a failsafe saveDoBurst = pSoldier->bDoBurst; // For each bullet in the burst for (i=0;ibTargetLevel; pSoldier->bTargetLevel = (INT8) gsInterfaceLevel; // Calculate hit chance (using the current bDoBurst for burst-penalty calculations) UINT32 uiHitChance; uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } pSoldier->bTargetLevel = bTempTargetLevel; // Put result (burst CTH) into the array gbCtH[i] = (gbCtH[i]+uiHitChance)/2; // Increase burst size, so that the next time we run CalcChanceToHitGun we'll get a lower // result, based on our Burst Penalty pSoldier->bDoBurst++; } // Reset original burst status pSoldier->bDoBurst = saveDoBurst; // Activate CTH bar display gfUICtHBar = TRUE; } else // One "BASE CTH" bar, JA2 1.13 vanilla { //AXP 29.03.2007: Rooftop CtH fix. See below. INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8) gsInterfaceLevel; UINT32 uiHitChance; uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } pSoldier->bTargetLevel = bTempTargetLevel; gfUICtHBar = TRUE; gbCtH[0] = (gbCtH[0]+uiHitChance)/2; gbCtHBurstCount = 1; } } } // HEADROCK HAM B2: Now autofire will show both its starting CTH and the CTH of the last // bullet in the volley. //else if(pSoldier->bDoAutofire > 0 && gGameSettings.fOptions[ TOPTION_CTH_CURSOR ]) else if(pSoldier->bDoAutofire > 0) { if(UsingNewCTHSystem() == true) { //AXP 29.03.2007: Rooftop CtH fix. See below. INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8) gsInterfaceLevel; UINT32 uiHitChance; // Calculate CTH for the first bullet uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, pSoldier->aiData.bShownAimTime, pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } // CTH for the first bullet is entered into this array, and later displayed gCTHDisplay.MuzzleSwayPercentage = (gCTHDisplay.MuzzleSwayPercentage + uiHitChance)/2; gCTHDisplay.iShooterGridNo = pSoldier->sGridNo; gCTHDisplay.iTargetGridNo = usMapPos; // Flag to tell the program to draw two CTH bars. gfUICtHBar = TRUE; gbCtHBurstCount = 0; // Calculate distance to target INT16 sX, sY, sXMap, sYMap; ConvertGridNoToCenterCellXY(pSoldier->sGridNo, &sX, &sY); ConvertGridNoToCenterCellXY(usMapPos, &sXMap, &sYMap); FLOAT dDeltaX = (FLOAT)(sX - sXMap); FLOAT dDeltaY = (FLOAT)(sY - sYMap); FLOAT d2DDistance = sqrt((dDeltaX*dDeltaX)+(dDeltaY*dDeltaY)); CalcMagFactorSimple( pSoldier, d2DDistance, pSoldier->aiData.bShownAimTime, usMapPos ); pSoldier->bTargetLevel = bTempTargetLevel; } else if(gGameSettings.fOptions[ TOPTION_CTH_CURSOR ]) { if (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 3) { gbCtHBurstCount = 1; //AXP 29.03.2007: Rooftop CtH fix. See below. INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8) gsInterfaceLevel; UINT32 uiHitChance; // Calculate CTH for the first bullet uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } // CTH for the first bullet is entered into this array, and later displayed gbCtH[0] = (gbCtH[0]+uiHitChance)/2; // Fool the CTH formula into thinking we're already firing the last bullet in the volley, // by artificially altering bDoBurst (which tracks actual burst progression). The CTH formula // calculates Auto Penalty based on bDoBurst - (Autopen * (bDoBurst-1), basically). UINT8 saveDoBurst = pSoldier->bDoBurst; pSoldier->bDoBurst = pSoldier->bDoAutofire; uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } pSoldier->bTargetLevel = bTempTargetLevel; // Return Burst State to original value pSoldier->bDoBurst = saveDoBurst; // CTH for the last bullet is entered into this array and later displayed. gbCtH[1] = (gbCtH[1]+uiHitChance)/2; // Flag to tell the program to draw two CTH bars. gbCtHAutoFire = TRUE; gfUICtHBar = TRUE; } } } } } else { //DIGICRAB: Graphical CtH //if(gGameSettings.fOptions[ TOPTION_CTH_CURSOR ]) { //AXP 29.03.2007: Rooftop CtH fix. Temporarily set soldier targetlevel to the real interface //level (selected with TAB), so the first shot after switching TL is calculated correctly INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8) gsInterfaceLevel; UINT32 uiHitChance; // SANDRO - precise calculation for throwing knives added if(UsingNewCTHSystem() == true) { uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); } else { if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { uiHitChance = CalcThrownChanceToHit( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); } else { uiHitChance = CalcChanceToHitGun( pSoldier, usMapPos, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); } } // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } if(UsingNewCTHSystem() == true) { gfUICtHBar = TRUE; gbCtHBurstCount = 0; // CTH for the first bullet is entered into this array, and later displayed gCTHDisplay.MuzzleSwayPercentage = (gCTHDisplay.MuzzleSwayPercentage + uiHitChance)/2; gCTHDisplay.iShooterGridNo = pSoldier->sGridNo; gCTHDisplay.iTargetGridNo = usMapPos; // Calculate distance to target INT16 sX, sY, sXMap, sYMap; ConvertGridNoToCenterCellXY(pSoldier->sGridNo, &sX, &sY); ConvertGridNoToCenterCellXY(usMapPos, &sXMap, &sYMap); FLOAT dDeltaX = (FLOAT)(sX - sXMap); FLOAT dDeltaY = (FLOAT)(sY - sYMap); FLOAT d2DDistance = sqrt((dDeltaX*dDeltaX)+(dDeltaY*dDeltaY)); CalcMagFactorSimple( pSoldier, d2DDistance, pSoldier->aiData.bShownAimTime, usMapPos ); } pSoldier->bTargetLevel = bTempTargetLevel; if(UsingNewCTHSystem() == false && gGameSettings.fOptions[ TOPTION_CTH_CURSOR ]) { gfUICtHBar = TRUE; gbCtHBurstCount = 1; gbCtH[0] = (gbCtH[0]+uiHitChance)/2; } } switchVal = pSoldier->aiData.bShownAimTime; switch( switchVal ) { case REFINE_AIM_1: if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW1_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM1_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT1_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW1_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM1_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT1_UICURSOR; } } break; case REFINE_AIM_2: if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW2_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM3_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT2_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW2_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM3_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT2_UICURSOR; } } break; case REFINE_AIM_3: if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW3_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM5_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT3_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW3_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM5_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT3_UICURSOR; } } break; case REFINE_AIM_4: if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW4_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM7_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT4_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW4_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM7_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT4_UICURSOR; } } break; case REFINE_AIM_5: if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMFULL_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM9_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT5_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMFULL_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM9_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT5_UICURSOR; } } break; case REFINE_AIM_MID1: //usCursor = ACTION_TARGETAIM2_UICURSOR; if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW1_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM2_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT1_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW1_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM2_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT1_UICURSOR; } } break; case REFINE_AIM_MID2: //usCursor = ACTION_TARGETAIM4_UICURSOR; if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW3_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM4_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT3_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW3_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM4_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT3_UICURSOR; } } break; case REFINE_AIM_MID3: //usCursor = ACTION_TARGETAIM6_UICURSOR; if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMYELLOW4_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM6_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT4_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMYELLOW4_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM6_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT4_UICURSOR; } } break; case REFINE_AIM_MID4: //usCursor = ACTION_TARGETAIM8_UICURSOR; if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { if ( gfDisplayFullCountRing ) { usCursor = ACTION_THROWAIMFULL_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_THROWAIM8_UICURSOR; } else { usCursor = ACTION_THROWAIMCANT5_UICURSOR; } } else { if ( gfDisplayFullCountRing ) { usCursor = ACTION_TARGETAIMFULL_UICURSOR; } else if ( fEnoughPoints ) { usCursor = ACTION_TARGETAIM8_UICURSOR; } else { usCursor = ACTION_TARGETAIMCANT5_UICURSOR; } } break; } } if ( !fMaxPointLimitHit ) { // Remove flash flag! RemoveCursorFlags( gUICursors[ usCursor ].usFreeCursorName, CURSOR_TO_FLASH ); RemoveCursorFlags( gUICursors[ usCursor ].usFreeCursorName, CURSOR_TO_PLAY_SOUND ); if ( gfCannotGetThrough ) { SetCursorSpecialFrame( gUICursors[ usCursor ].usFreeCursorName, 1 ); } else { if ( !InRange( pSoldier, usMapPos ) ) { // OK, make buddy flash! SetCursorFlags( gUICursors[ usCursor ].usFreeCursorName, CURSOR_TO_FLASH ); SetCursorFlags( gUICursors[ usCursor ].usFreeCursorName, CURSOR_TO_PLAY_SOUND ); } else { SetCursorSpecialFrame( gUICursors[ usCursor ].usFreeCursorName, 0 ); } } } return( (UINT8)usCursor ); } // WANNE: Shows the target cursor if we are not hovering over an enemy soldier UINT8 HandleNonActivatedTargetCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos , BOOLEAN fShowAPs, BOOLEAN fRecalc, UINT32 uiCursorFlags ) { UINT16 usInHand; usInHand = pSoldier->inv[ HANDPOS ].usItem; if ( Item[ usInHand ].usItemClass != IC_THROWING_KNIFE ) { if (( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { //DetermineCursorBodyLocation( (UINT8)gusSelectedSoldier, FALSE, fRecalc ); DetermineCursorBodyLocation( (UINT8)gusSelectedSoldier, fShowAPs, fRecalc ); if ( pSoldier->flags.fReloading || pSoldier->flags.fPauseAim ) { return( ACTION_TARGET_RELOADING ); } } //CHRISL: We need to only check the second hand if the weapon in the second hand is onehanded // Check for enough ammo... if ( !EnoughAmmo( pSoldier, FALSE, HANDPOS ) || (pSoldier->IsValidSecondHandShotForReloadingPurposes( ) && !ItemIsTwoHanded(pSoldier->inv[SECONDHANDPOS].usItem) && !EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS) ) ) { // Check if ANY ammo exists..... if ( FindAmmoToReload( pSoldier, HANDPOS, NO_SLOT ) == NO_SLOT ) { // OK, use BAD reload cursor..... return( BAD_RELOAD_UICURSOR ); } else { // Check APs to reload... gsCurrentActionPoints = GetAPsToAutoReload( pSoldier, false ); gfUIDisplayActionPoints = TRUE; //gUIDisplayActionPointsOffX = 14; //gUIDisplayActionPointsOffY = 7; // OK, use GOOD reload cursor..... return( GOOD_RELOAD_UICURSOR ); } } } if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT ) ) { DetermineCursorBodyLocation( (UINT8)gusSelectedSoldier, fShowAPs, fRecalc ); gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(pSoldier->aiData.bShownAimTime ) ); gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; if ( fShowAPs ) { if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) ) { gfUIDisplayActionPointsInvalid = TRUE; } } else { //gfUIDisplayActionPointsBlack = TRUE; gfUIDisplayActionPoints = FALSE; } } if(gusUIFullTargetID == NOBODY && pSoldier->bDoAutofire && !pSoldier->flags.fDoSpread) //reset autofire if we move the mouse off the target, however don't reset it if we are spread-bursting { if(gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT )) { pSoldier->bDoAutofire = 1; // sevenfm: init autofire bullet num next time when the cursor will be on target gfAutofireInitBulletNum = FALSE; } pSoldier->flags.autofireLastStep = FALSE; } //if ( gTacticalStatus.uiFlags & TURNBASED && !(gTacticalStatus.uiFlags & INCOMBAT ) ) { if ( fRecalc ) { if ( SoldierToLocationChanceToGetThrough( pSoldier, usMapPos, (INT8) gsInterfaceLevel, pSoldier->bTargetCubeLevel, NOBODY ) < OK_CHANCE_TO_GET_THROUGH ) { gfCannotGetThrough = TRUE; } else { gfCannotGetThrough = FALSE; } } // OK, if we begin to move, reset the cursor... if ( uiCursorFlags & MOUSE_MOVING ) { gfCannotGetThrough = FALSE; } if ( gfCannotGetThrough ) { if ( pSoldier->bDoBurst ) { return( ACTION_NOCHANCE_BURST_UICURSOR ); } else if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { return( BAD_THROW_UICURSOR ); } else { return( ACTION_NOCHANCE_SHOOT_UICURSOR ); } } } // Determine if good range if ( !InRange( pSoldier, usMapPos ) ) { // Flash cursor! // Check if we're in burst mode! if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { return( FLASH_THROW_UICURSOR ); } else if ( pSoldier->bDoBurst ) { //return( ACTION_FIRSTAID_RED ); return( ACTION_FLASH_BURST_UICURSOR ); } else { //return( ACTION_FIRSTAID_RED ); return( ACTION_FLASH_SHOOT_UICURSOR ); } } else { // Check if we're in burst mode! if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) { return( GOOD_THROW_UICURSOR ); } else if ( pSoldier->bDoBurst ) { //return( ACTION_FIRSTAID_RED ); return( ACTION_TARGETBURST_UICURSOR ); } else { //return( ACTION_FIRSTAID_RED ); return( ACTION_SHOOT_UICURSOR ); } } } void DetermineCursorBodyLocation( UINT8 ubSoldierID, BOOLEAN fDisplay, BOOLEAN fRecalc ) { INT32 usMapPos; SOLDIERTYPE *pTargetSoldier = NULL, *pSoldier; UINT16 usFlags; INT16 sMouseX, sMouseY, sCellX, sCellY, sScreenX, sScreenY; BOOLEAN fOnGuy = FALSE; LEVELNODE *pNode; pSoldier = MercPtrs[ ubSoldierID ]; if ( gTacticalStatus.ubAttackBusyCount > 0 ) { // ATE: Return if attacker busy count > 0, this // helps in RT with re-setting the flag to random... return; } if ( fRecalc ) { // ALWAYS SET AIM LOCATION TO NOTHING pSoldier->bAimShotLocation = AIM_SHOT_RANDOM; if( !GetMouseMapPos( &usMapPos) ) { return; } // Determine which body part it's on pNode = GetAnimProfileFlags( usMapPos, &usFlags, &pTargetSoldier, NULL ); while( pNode != NULL ) { if ( pTargetSoldier != NULL ) { // ATE: Check their stance - if prone - return! if ( gAnimControl[ pTargetSoldier->usAnimState ].ubHeight == ANIM_PRONE ) { return; } fOnGuy = TRUE; // Check if we have a half tile profile if ( usFlags & TILE_FLAG_NORTH_HALF ) { // Check if we are in north half of tile! GetMouseXYWithRemainder( &sMouseX, &sMouseY, &sCellX, &sCellY ); if ( sCellY > (CELL_Y_SIZE / 2) ) { fOnGuy = FALSE; } } // Check if we have a half tile profile if ( usFlags & TILE_FLAG_SOUTH_HALF ) { // Check if we are in north half of tile! GetMouseXYWithRemainder( &sMouseX, &sMouseY, &sCellX, &sCellY ); if ( sCellY <= (CELL_Y_SIZE / 2) ) { fOnGuy = FALSE; } } // Check if we have a half tile profile if ( usFlags & TILE_FLAG_WEST_HALF ) { // Check if we are in north half of tile! GetMouseXYWithRemainder( &sMouseX, &sMouseY, &sCellX, &sCellY ); if ( sCellX > (CELL_X_SIZE / 2) ) { fOnGuy = FALSE; } } if ( usFlags & TILE_FLAG_EAST_HALF ) { // Check if we are in north half of tile! GetMouseXYWithRemainder( &sMouseX, &sMouseY, &sCellX, &sCellY ); if ( sCellX <= (CELL_X_SIZE / 2) ) { fOnGuy = FALSE; } } if ( usFlags & TILE_FLAG_TOP_HALF ) { // Check if we are in north half of tile! GetMouseXYWithRemainder( &sMouseX, &sMouseY, &sCellX, &sCellY ); // Convert these to screen corrdinates FromCellToScreenCoordinates( sCellX, sCellY, &sScreenX, &sScreenY ); // Check for Below... if ( sScreenX > (WORLD_TILE_Y / 2) ) { fOnGuy = FALSE; } } if ( usFlags & TILE_FLAG_BOTTOM_HALF ) { // Check if we are in north half of tile! GetMouseXYWithRemainder( &sMouseX, &sMouseY, &sCellX, &sCellY ); // Convert these to screen corrdinates FromCellToScreenCoordinates( sCellX, sCellY, &sScreenX, &sScreenY ); // Check for Below... if ( sScreenX <= (WORLD_TILE_Y / 2) ) { fOnGuy = FALSE; } } // Check if mouse is iin bounding box of soldier if ( !IsPointInSoldierBoundingBox( pTargetSoldier, gusMouseXPos, gusMouseYPos ) ) { fOnGuy = FALSE; } } if ( fOnGuy ) break; pNode = GetAnimProfileFlags( usMapPos, &usFlags, &pTargetSoldier, pNode ); } if ( !fOnGuy ) { // Check if we can find a soldier here if ( gfUIFullTargetFound ) { pTargetSoldier = MercPtrs[ gusUIFullTargetID ]; if ( FindRelativeSoldierPosition( pTargetSoldier, &usFlags, gusMouseXPos, gusMouseYPos ) ) { fOnGuy = TRUE; } } } if ( fOnGuy ) { if ( IsValidTargetMerc( pTargetSoldier->ubID ) ) { if ( usFlags & TILE_FLAG_FEET ) { pSoldier->bAimShotLocation = AIM_SHOT_LEGS; } if ( usFlags & TILE_FLAG_MID ) { pSoldier->bAimShotLocation = AIM_SHOT_TORSO; } if ( usFlags & TILE_FLAG_HEAD ) { pSoldier->bAimShotLocation = AIM_SHOT_HEAD; } } } } // HEADROCK HAM B2: Now shows targetted bodypart for ALL firing modes. Note that the external toggle is not // taken into account yet. // if ( fDisplay && !pSoldier->bDoAutofire ) if ( fDisplay ) { if ( gfUIFullTargetFound ) { pTargetSoldier = MercPtrs[ gusUIFullTargetID ]; if ( pTargetSoldier->ubBodyType == CROW ) { pSoldier->bAimShotLocation = AIM_SHOT_LEGS; wcscpy( gzLocation, TacticalStr[ CROW_HIT_LOCATION_STR ] ); if ( !pSoldier->bDoBurst ) gfUIBodyHitLocation = TRUE; // HEADROCK: This'll toggle whether the bodypart targetting indicator shows up in the burst/auto // CTH cursors. else if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 2) ) gfUIBodyHitLocation = TRUE; else if ( pSoldier->bDoBurst && pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 3) ) gfUIBodyHitLocation = TRUE; return; } if ( !IS_MERC_BODY_TYPE( pTargetSoldier ) ) { return; } switch( pSoldier->bAimShotLocation ) { case AIM_SHOT_HEAD: // If we have a knife in hand, change string if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass == IC_BLADE ) { wcscpy( gzLocation, TacticalStr[ NECK_HIT_LOCATION_STR ] ); } else { wcscpy( gzLocation, TacticalStr[ HEAD_HIT_LOCATION_STR ] ); } if ( !pSoldier->bDoBurst ) gfUIBodyHitLocation = TRUE; // HEADROCK: This'll toggle whether the bodypart targetting indicator shows up in the burst/auto // CTH cursors. else if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 2) ) gfUIBodyHitLocation = TRUE; else if ( pSoldier->bDoBurst && pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 3) ) gfUIBodyHitLocation = TRUE; break; case AIM_SHOT_TORSO: wcscpy( gzLocation, TacticalStr[ TORSO_HIT_LOCATION_STR ] ); if ( !pSoldier->bDoBurst ) gfUIBodyHitLocation = TRUE; // HEADROCK: This'll toggle whether the bodypart targetting indicator shows up in the burst/auto // CTH cursors. else if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 2) ) gfUIBodyHitLocation = TRUE; else if ( pSoldier->bDoBurst && pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 3) ) gfUIBodyHitLocation = TRUE; break; case AIM_SHOT_LEGS: wcscpy( gzLocation, TacticalStr[ LEGS_HIT_LOCATION_STR ] ); if ( !pSoldier->bDoBurst ) gfUIBodyHitLocation = TRUE; // HEADROCK: This'll toggle whether the bodypart targetting indicator shows up in the burst/auto // CTH cursors. else if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 2) ) gfUIBodyHitLocation = TRUE; else if ( pSoldier->bDoBurst && pSoldier->bDoAutofire && (gGameExternalOptions.ubNewCTHBars == 1 || gGameExternalOptions.ubNewCTHBars == 3) ) gfUIBodyHitLocation = TRUE; break; } } } } UINT8 HandleKnifeCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { INT16 sAPCosts; INT8 bFutureAim; BOOLEAN fEnoughPoints = TRUE; if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return KNIFE_REG_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_MERCS ); // Flugente: if we are using a bayonet, adjust aimtime if ( pSoldier->bWeaponMode == WM_ATTACHED_BAYONET && pSoldier->aiData.bShownAimTime != REFINE_KNIFE_1 ) pSoldier->aiData.bShownAimTime = REFINE_KNIFE_2; if ( fActivated ) { DetermineCursorBodyLocation( pSoldier->ubID, TRUE, TRUE ); //shadooow: this fixes bug where the original aim location is lost if the attack is stopped due to interrupt pSoldier->bAimMeleeLocation = pSoldier->bAimShotLocation; if ( gfUIHandleShowMoveGrid ) { gfUIHandleShowMoveGrid = 2; } // Calculate action points if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) ) { gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, sGridNo, TRUE, (INT8)(pSoldier->aiData.bShownAimTime / 2) ); gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; // If we don't have any points and we are at the first refine, do nothing but warn! if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) ) { gfUIDisplayActionPointsInvalid = TRUE; if ( pSoldier->aiData.bShownAimTime == REFINE_KNIFE_1 ) { return( KNIFE_HIT_UICURSOR ); } } bFutureAim = (INT8)( REFINE_KNIFE_2 ); sAPCosts = CalcTotalAPsToAttack( pSoldier, sGridNo, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( !EnoughPoints( pSoldier, (INT16)sAPCosts, 0 , FALSE ) ) { fEnoughPoints = FALSE; } } if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { if ( !pSoldier->flags.fPauseAim ) { if ( COUNTERDONE( NONGUNTARGETREFINE ) ) { // Reset counter RESETCOUNTER( NONGUNTARGETREFINE ); if ( pSoldier->aiData.bShownAimTime == REFINE_KNIFE_1 ) { PlayJA2Sample( TARG_REFINE_BEEP, RATE_11025, MIDVOLUME, 1, MIDDLEPAN ); } pSoldier->aiData.bShownAimTime = REFINE_KNIFE_2; } } } ////////////////////////////////////////////////////////////// if( pSoldier->aiData.bShownAimTime == REFINE_KNIFE_1 ) { if ( gfDisplayFullCountRing ) { return( KNIFE_YELLOW_AIM1_UICURSOR ); } else if ( fEnoughPoints ) { return( KNIFE_HIT_AIM1_UICURSOR ); } else { return( KNIFE_NOGO_AIM1_UICURSOR ); } } else if( pSoldier->aiData.bShownAimTime == REFINE_KNIFE_2 ) { if ( gfDisplayFullCountRing ) { return( KNIFE_YELLOW_AIM2_UICURSOR ); } else if ( fEnoughPoints ) { return( KNIFE_HIT_AIM2_UICURSOR ); } else { return( KNIFE_NOGO_AIM2_UICURSOR ); } } else { Assert( FALSE ); // no return value! return(0); } ////////////////////////////////////////////////////////////// } else { gfUIDisplayActionPointsCenter = TRUE; // CHECK IF WE ARE ON A GUY ( THAT'S NOT SELECTED )! if ( gfUIFullTargetFound && !( guiUIFullTargetFlags & SELECTED_MERC ) ) { DetermineCursorBodyLocation( pSoldier->ubID, TRUE, TRUE ); return( KNIFE_HIT_UICURSOR ); } else { return( KNIFE_REG_UICURSOR ); } } } UINT8 HandlePunchCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { INT16 sAPCosts; INT8 bFutureAim; BOOLEAN fEnoughPoints = TRUE; if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return ACTION_PUNCH_GRAY; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_MERCS ); if ( fActivated ) { DetermineCursorBodyLocation( pSoldier->ubID, TRUE, TRUE ); //shadooow: this fixes bug where the original aim location is lost if the attack is stopped due to interrupt pSoldier->bAimMeleeLocation = pSoldier->bAimShotLocation; if ( gfUIHandleShowMoveGrid ) { gfUIHandleShowMoveGrid = 2; } // Calculate action points if ( gTacticalStatus.uiFlags & TURNBASED ) { gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, sGridNo, TRUE, (INT8)(pSoldier->aiData.bShownAimTime / 2) ); gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; // If we don't have any points and we are at the first refine, do nothing but warn! if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) ) { gfUIDisplayActionPointsInvalid = TRUE; if ( pSoldier->aiData.bShownAimTime == REFINE_PUNCH_1 ) { return( ACTION_PUNCH_RED ); } } bFutureAim = (INT8)( REFINE_PUNCH_2 ); sAPCosts = CalcTotalAPsToAttack( pSoldier, sGridNo, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( !EnoughPoints( pSoldier, (INT16)sAPCosts, 0 , FALSE ) ) { fEnoughPoints = FALSE; } } if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { if ( !pSoldier->flags.fPauseAim ) { if ( COUNTERDONE( NONGUNTARGETREFINE ) ) { // Reset counter RESETCOUNTER( NONGUNTARGETREFINE ); if ( pSoldier->aiData.bShownAimTime == REFINE_PUNCH_1 ) { PlayJA2Sample( TARG_REFINE_BEEP, RATE_11025, MIDVOLUME, 1, MIDDLEPAN ); } pSoldier->aiData.bShownAimTime = REFINE_PUNCH_2; } } } ////////////////////////////////////////////////////////////// if( pSoldier->aiData.bShownAimTime == REFINE_PUNCH_1) { if ( gfDisplayFullCountRing ) { return( ACTION_PUNCH_YELLOW_AIM1_UICURSOR ); } else if ( fEnoughPoints ) { return( ACTION_PUNCH_RED_AIM1_UICURSOR ); } else { return( ACTION_PUNCH_NOGO_AIM1_UICURSOR ); } } else if ( pSoldier->aiData.bShownAimTime == REFINE_PUNCH_2 ) { if ( gfDisplayFullCountRing ) { return( ACTION_PUNCH_YELLOW_AIM2_UICURSOR ); } else if ( fEnoughPoints ) { return( ACTION_PUNCH_RED_AIM2_UICURSOR ); } else { return( ACTION_PUNCH_NOGO_AIM2_UICURSOR ); } } else { Assert( FALSE ); // no return value! return(0); } ////////////////////////////////////////////////////////////// } else { gfUIDisplayActionPointsCenter = TRUE; // CHECK IF WE ARE ON A GUY ( THAT'S NOT SELECTED )! if ( gfUIFullTargetFound && !( guiUIFullTargetFlags & SELECTED_MERC ) ) { DetermineCursorBodyLocation( pSoldier->ubID, TRUE, TRUE ); return( ACTION_PUNCH_RED ); } else { return( ACTION_PUNCH_GRAY ); } } } UINT8 HandleAidCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return ACTION_FIRSTAID_GRAY; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_MERCSFORAID ); if ( fActivated ) { return( ACTION_FIRSTAID_RED ); } else { // CHECK IF WE ARE ON A GUY ( THAT'S NOT SELECTED )! if ( gfUIFullTargetFound ) // && !( guiUIFullTargetFlags & SELECTED_MERC ) ) { return( ACTION_FIRSTAID_RED ); } else { return( ACTION_FIRSTAID_GRAY ); } } } UINT8 HandleActivatedTossCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubItemCursor ) { return( ACTION_TOSS_UICURSOR ); } UINT8 HandleNonActivatedTossCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fRecalc, UINT32 uiCursorFlags, UINT8 ubItemCursor ) { INT32 sFinalGridNo; static BOOLEAN fBadCTGH = FALSE; BOOLEAN fArmed = FALSE; INT8 bLevel; OBJECTTYPE * pObj; // Check for enough ammo... if ( ubItemCursor == TRAJECTORYCURS ) { fArmed = TRUE; if ( !EnoughAmmo( pSoldier, FALSE, HANDPOS ) ) { // Flugente: if we are using an underbarrel GL, we aren't even supposed to be able to reload via leftclick here // recalculate the correct firing mode and be done if ( (pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO) && IsGrenadeLauncherAttached( &(pSoldier->inv[HANDPOS]) ) ) { ChangeWeaponMode( pSoldier ); return(BAD_RELOAD_UICURSOR); } // Check if ANY ammo exists..... if ( FindAmmoToReload( pSoldier, HANDPOS, NO_SLOT ) == NO_SLOT ) { // OK, use BAD reload cursor..... return( BAD_RELOAD_UICURSOR ); } else { // Check APs to reload... gsCurrentActionPoints = GetAPsToAutoReload( pSoldier, false ); gfUIDisplayActionPoints = TRUE; //gUIDisplayActionPointsOffX = 14; //gUIDisplayActionPointsOffY = 7; // OK, use GOOD reload cursor..... return( GOOD_RELOAD_UICURSOR ); } } } // Add APs.... if ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) { if ( ubItemCursor == TRAJECTORYCURS ) { gsCurrentActionPoints = CalcTotalAPsToAttack( pSoldier, sGridNo, TRUE, (INT8)(pSoldier->aiData.bShownAimTime ) ); } else { gsCurrentActionPoints = MinAPsToThrow( pSoldier, sGridNo, TRUE ); } gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; // If we don't have any points and we are at the first refine, do nothing but warn! if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) ) { gfUIDisplayActionPointsInvalid = TRUE; } } // OK, if we begin to move, reset the cursor... if ( uiCursorFlags & MOUSE_MOVING ) { EndPhysicsTrajectoryUI( ); } gfUIHandlePhysicsTrajectory = TRUE; if ( fRecalc ) { // Calculate chance to throw here..... if ( sGridNo == pSoldier->sGridNo ) { fBadCTGH = FALSE; } else { UINT16 glItem = GetAttachedGrenadeLauncher( &(pSoldier->inv[HANDPOS])); if ((pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )&& glItem != NONE ) { OBJECTTYPE* pAttachment = FindAttachment( &(pSoldier->inv[HANDPOS]), glItem ); if ( pAttachment ) { if ( !CalculateLaunchItemChanceToGetThrough( pSoldier, pAttachment, sGridNo, (INT8)gsInterfaceLevel, (INT16)( gsInterfaceLevel * 256 ), &sFinalGridNo, fArmed, &bLevel, TRUE ) ) { fBadCTGH = TRUE; } else { fBadCTGH = FALSE; } BeginPhysicsTrajectoryUI( sFinalGridNo, bLevel, fBadCTGH ); } } else { OBJECTTYPE* pObject = &(pSoldier->inv[HANDPOS]); // Do we have a launcable? pObj = &(pSoldier->inv[HANDPOS]); //sevenfm this should be checked only for guns because we can throw items with attached explosives if(Item[pObj->usItem].usItemClass & IC_WEAPON ) for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { if ( Item[ iter->usItem ].usItemClass & IC_EXPLOSV && iter->exists()) { pObject = &(*iter); break; } } if ( !CalculateLaunchItemChanceToGetThrough( pSoldier, pObject, sGridNo, (INT8)gsInterfaceLevel, (INT16)( gsInterfaceLevel * 256 ), &sFinalGridNo, fArmed, &bLevel, TRUE ) ) { fBadCTGH = TRUE; } else { fBadCTGH = FALSE; } BeginPhysicsTrajectoryUI( sFinalGridNo, bLevel, fBadCTGH ); } } } if ( fBadCTGH ) { return( BAD_THROW_UICURSOR ); } return( GOOD_THROW_UICURSOR ); } UINT8 HandleWirecutterCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_WIREFENCE ); // Are we over a cuttable fence? if ( IsCuttableWireFenceAtGridNo( sGridNo ) && pSoldier->pathing.bLevel == 0 ) { return( GOOD_WIRECUTTER_UICURSOR ); } else if (IsStructureDeconstructItem(pSoldier->inv[HANDPOS].usItem, sGridNo, pSoldier)) { if (FindStructure(sGridNo, (STRUCTURE_GENERIC | STRUCTURE_WIREFENCE))) { return(GOOD_WIRECUTTER_UICURSOR); } } return( BAD_WIRECUTTER_UICURSOR ); } UINT8 HandleRepairCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_REPAIR ); // Are we over a cuttable fence? if ( IsRepairableStructAtGridNo( sGridNo, NULL ) && pSoldier->pathing.bLevel == 0 ) { return( GOOD_REPAIR_UICURSOR ); } return( BAD_REPAIR_UICURSOR ); } UINT8 HandleRefuelCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_REFUEL ); // Are we over a cuttable fence? if ( IsRefuelableStructAtGridNo( sGridNo, NULL ) && pSoldier->pathing.bLevel == 0 ) { return( REFUEL_RED_UICURSOR ); } return( REFUEL_GREY_UICURSOR ); } UINT8 HandleJarCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return BAD_JAR_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_JAR ); // Are we over a cuttable fence? if ( IsCorpseAtGridNo( sGridNo, pSoldier->pathing.bLevel ) ) { return( GOOD_JAR_UICURSOR ); } return( BAD_JAR_UICURSOR ); } UINT8 HandleTinCanCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { STRUCTURE *pStructure; INT32 sIntTileGridNo; LEVELNODE *pIntTile; if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return PLACE_TINCAN_RED_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_CAN ); // Check if a door exists here..... pIntTile = GetCurInteractiveTileGridNoAndStructure( &sIntTileGridNo, &pStructure ); // We should not have null here if we are given this flag... if ( pIntTile != NULL ) { if (pStructure->fFlags & STRUCTURE_ANYDOOR) { return( PLACE_TINCAN_GREY_UICURSOR ); } } return( PLACE_TINCAN_RED_UICURSOR ); } UINT8 HandleRemoteCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { // Calculate action points if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) ) { gsCurrentActionPoints = GetAPsToUseRemote( pSoldier ); gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; // If we don't have any points and we are at the first refine, do nothing but warn! if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) ) { gfUIDisplayActionPointsInvalid = TRUE; } } if ( fActivated ) { return( PLACE_REMOTE_RED_UICURSOR ); } else { return( PLACE_REMOTE_GREY_UICURSOR ); } } UINT8 HandleCameraCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_CAMERA ); // do we have handcuffs in our hand? if ( HasItemFlag( ( &( pSoldier->inv[HANDPOS] ) )->usItem, CAMERA ) && SoldierTo3DLocationLineOfSightTest( pSoldier, sGridNo, gsInterfaceLevel, 0, TRUE, CALC_FROM_WANTED_DIR, TRUE ) ) { return CAMERA_GREY_UICURSOR; } return CAMERA_RED_UICURSOR; } UINT8 HandleBloodbagCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return BLOODBAG_RED_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_APPLYITEM ); if ( HasItemFlag( ( &( pSoldier->inv[HANDPOS] ) )->usItem, EMPTY_BLOOD_BAG ) ) { // is there a person here? UINT8 usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->pathing.bLevel ); if ( usSoldierIndex != NOBODY ) { if ( usSoldierIndex != pSoldier->ubID && MercPtrs[usSoldierIndex]->IsValidBloodDonor() ) return BLOODBAG_GREY_UICURSOR; } } return BLOODBAG_RED_UICURSOR; } UINT8 HandleSplintCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return SPLINT_RED_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_APPLYITEM ); if ( HasItemFlag( ( &( pSoldier->inv[HANDPOS] ) )->usItem, MEDICAL_SPLINT ) ) { // are we actually qualified to use this? if ( gGameOptions.fNewTraitSystem ) { if ( NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ) == 0 ) return SPLINT_RED_UICURSOR; } else { if ( EffectiveMedical( pSoldier ) < 50 ) return SPLINT_RED_UICURSOR; } // is there a person here? UINT8 usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->pathing.bLevel ); if ( usSoldierIndex != NOBODY ) { if ( usSoldierIndex != pSoldier->ubID && MercPtrs[usSoldierIndex]->CanReceiveSplint() ) return SPLINT_GREY_UICURSOR; } } return SPLINT_RED_UICURSOR; } UINT8 HandleBombCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return PLACE_BOMB_GREY_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_BOMB ); // Calculate action points if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) ) { gsCurrentActionPoints = GetTotalAPsToDropBomb( pSoldier, sGridNo ); gfUIDisplayActionPoints = TRUE; gfUIDisplayActionPointsCenter = TRUE; // If we don't have any points and we are at the first refine, do nothing but warn! if ( !EnoughPoints( pSoldier, gsCurrentActionPoints, 0 , FALSE ) ) { gfUIDisplayActionPointsInvalid = TRUE; } } if ( fActivated ) { return( PLACE_BOMB_RED_UICURSOR ); } else { return( PLACE_BOMB_GREY_UICURSOR ); } } UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return FORTIFICATION_RED_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_FORTIFICATION ); if ( pSoldier->pathing.bLevel != 0 ) return( FORTIFICATION_RED_UICURSOR ); if ( IsFortificationPossibleAtGridNo( sGridNo ) && IsStructureConstructItem( pSoldier->inv[HANDPOS].usItem, sGridNo, pSoldier ) ) { return( FORTIFICATION_GREY_UICURSOR ); } else if ( IsStructureDeconstructItem( pSoldier->inv[HANDPOS].usItem, sGridNo, pSoldier ) ) { STRUCTURE* pStruct = FindStructure( sGridNo, (STRUCTURE_GENERIC | STRUCTURE_WIREFENCE)); if ( pStruct ) { return(FORTIFICATION_GREY_UICURSOR); } } return( FORTIFICATION_RED_UICURSOR ); } UINT8 HandleHandcuffCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return HANDCUFF_RED_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_HANDCUFF ); // do we have handcuffs in our hand? if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, HANDCUFFS ) ) { // is there a person here? UINT8 usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->pathing.bLevel ); if (usSoldierIndex != NOBODY && MercPtrs[usSoldierIndex] && MercPtrs[usSoldierIndex]->bVisible >= 0 && MercPtrs[usSoldierIndex]->CanBeCaptured()) { return( HANDCUFF_GREY_UICURSOR ); } } return( HANDCUFF_RED_UICURSOR ); } UINT8 HandleApplyItemCursor(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return APPLYITEM_RED_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor(pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_APPLYITEM); if (ItemCanBeAppliedToOthers((&(pSoldier->inv[HANDPOS]))->usItem)) { // is there a person here? UINT8 ubPerson = WhoIsThere2(sGridNo, pSoldier->pathing.bLevel); if (ubPerson != NOBODY && MercPtrs[ubPerson] && MercPtrs[ubPerson]->bVisible >= 0) { return(APPLYITEM_GREY_UICURSOR); } } return(APPLYITEM_RED_UICURSOR); } UINT8 HandleHackCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) { if (pSoldier->pathing.bLevel != gsInterfaceLevel) { return NO_UICURSOR; } // DRAW PATH TO GUY HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_INTERACTIVEACTION ); UINT16 structindex; UINT16 possibleaction = InteractiveActionPossibleAtGridNo( sGridNo, pSoldier->pathing.bLevel, structindex ); UINT16 skill = pSoldier->GetInteractiveActionSkill( sGridNo, pSoldier->pathing.bLevel, possibleaction ); if ( possibleaction == INTERACTIVE_STRUCTURE_HACKABLE ) return skill ? HACK_GREY_UICURSOR : HACK_RED_UICURSOR; else if ( possibleaction == INTERACTIVE_STRUCTURE_READFILE ) return skill ? READFILE_GREY_UICURSOR : READFILE_RED_UICURSOR; else if ( possibleaction == INTERACTIVE_STRUCTURE_WATERTAP ) return skill ? WATERTAP_GREY_UICURSOR : WATERTAP_RED_UICURSOR; else if ( possibleaction == INTERACTIVE_STRUCTURE_SODAMACHINE ) return skill ? SODAMACHINE_GREY_UICURSOR : SODAMACHINE_RED_UICURSOR; else if ( possibleaction == INTERACTIVE_STRUCTURE_MINIGAME ) return skill ? MINIGAME_GREY_UICURSOR : MINIGAME_RED_UICURSOR; else if ( possibleaction == INTERACTIVE_STRUCTURE_VARIOUS ) return skill ? GEARWHEEL_GREY_UICURSOR : GEARWHEEL_RED_UICURSOR; return NO_UICURSOR; } void HandleEndConfirmCursor( SOLDIERTYPE *pSoldier ) { UINT16 usInHand; UINT8 ubItemCursor; // LOOK IN GUY'S HAND TO CHECK LOCATION usInHand = pSoldier->inv[HANDPOS].usItem; ubItemCursor = GetActionModeCursor( pSoldier ); if ( ubItemCursor == TOSSCURS ) { // EndAimCubeUI( ); } } void HandleLeftClickCursor( SOLDIERTYPE *pSoldier ) { UINT16 usInHand; UINT8 ubItemCursor; INT32 usGridNo; // LOOK IN GUY'S HAND TO CHECK LOCATION usInHand = pSoldier->inv[HANDPOS].usItem; ubItemCursor = GetActionModeCursor( pSoldier ); // OK, if we are i realtime.. goto directly to shoot if ( ( ( gTacticalStatus.uiFlags & TURNBASED ) && !( gTacticalStatus.uiFlags & INCOMBAT ) ) )//&& ubItemCursor != TOSSCURS && ubItemCursor != TRAJECTORYCURS ) { // GOTO DIRECTLY TO USING ITEM // ( only if not burst mode.. ) if ( !pSoldier->bDoBurst ) { guiPendingOverrideEvent = CA_MERC_SHOOT; } return; } if (!GetMouseMapPos( &usGridNo ) ) { return; } gfUIForceReExamineCursorData = TRUE; gfDisplayFullCountRing = FALSE; switch( ubItemCursor ) { case TARGETCURS: if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT ) ) { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; pSoldier->flags.fPauseAim = FALSE; } else { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; pSoldier->flags.fPauseAim = FALSE; } // Reset counter RESETCOUNTER( TARGETREFINE ); break; case PUNCHCURS: if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT ) ) { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_1; pSoldier->flags.fPauseAim = FALSE; } else { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_1; pSoldier->flags.fPauseAim = FALSE; } // Reset counter RESETCOUNTER( NONGUNTARGETREFINE ); break; case KNIFECURS: if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT ) ) { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_1; pSoldier->flags.fPauseAim = FALSE; } else { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_1; pSoldier->flags.fPauseAim = FALSE; } // Reset counter RESETCOUNTER( NONGUNTARGETREFINE ); break; case TOSSCURS: //BeginAimCubeUI( pSoldier, sGridNo, (INT8)gsInterfaceLevel, 0, 0 ); //break; default: // GOTO DIRECTLY TO USING ITEM guiPendingOverrideEvent = CA_MERC_SHOOT; } } void HandleRightClickAdjustCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos ) { UINT16 usInHand; INT16 sAPCosts; INT8 bFutureAim; UINT8 ubCursor; SOLDIERTYPE *pTSoldier; INT32 sGridNo; INT8 bTargetLevel; usInHand = pSoldier->inv[HANDPOS].usItem; ubCursor = GetActionModeCursor( pSoldier ); // HEADROCK HAM 4: Required for new Aiming Level Limit function UINT8 maxAimLevels = AllowedAimingLevels(pSoldier, usMapPos); // 'snap' cursor to target tile.... if ( gfUIFullTargetFound ) { usMapPos = MercPtrs[ gusUIFullTargetID ]->sGridNo; } switch( ubCursor ) { case TARGETCURS: // CHECK IF GUY HAS IN HAND A WEAPON if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire && !gGameExternalOptions.bAimedBurstEnabled) { // Do nothing for aimed burst deactivated // pSoldier->aiData.bShownAimTime = REFINE_AIM_BURST; } else if(pSoldier->bDoAutofire) { // aiming in auto mode using hotkey for mousewheel not working if( gfKeyState[ COMMA ] && gGameExternalOptions.bAimedBurstEnabled ) { sGridNo = usMapPos; bTargetLevel = (INT8)gsInterfaceLevel; // Look for a target here... if ( gfUIFullTargetFound ) { // Get target soldier, if one exists pTSoldier = MercPtrs[ gusUIFullTargetID ]; sGridNo = pTSoldier->sGridNo; bTargetLevel = pSoldier->pathing.bLevel; if ( !HandleCheckForBadChangeToGetThrough( pSoldier, pTSoldier, sGridNo , bTargetLevel ) ) { return; } } bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + 1 ); if ( bFutureAim <= maxAimLevels ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim ) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= 1; if ( pSoldier->aiData.bShownAimTime > maxAimLevels ) { pSoldier->aiData.bShownAimTime = maxAimLevels; } } // Else - goto first level! else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; gfDisplayFullCountRing = FALSE; } } } else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; gfDisplayFullCountRing = FALSE; } } } // adjust number of bullets in auto mode else { INT16 sCurAPCosts; UINT16 usShotsLeft = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; if (pSoldier->IsValidSecondHandBurst()) { usShotsLeft = min( (pSoldier->inv[ SECONDHANDPOS ][0]->data.gun.ubGunShotsLeft), usShotsLeft ); } if(pSoldier->flags.autofireLastStep) { pSoldier->bDoAutofire = 1; //reset the bullet counter pSoldier->flags.autofireLastStep = FALSE; return; } if(usShotsLeft > pSoldier->bDoAutofire ) { //Calculate how many bullets we need to fire to add at least one more AP sAPCosts = sCurAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, pSoldier->aiData.bShownAimTime); while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts <= sCurAPCosts && usShotsLeft > pSoldier->bDoAutofire) //Increment the bullet count until we run out of APs or we spend the whole AP { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, pSoldier->aiData.bShownAimTime); } //we've stepped over the border and used up one more ap, now let's make sure that it is spent to maximize the bullets sCurAPCosts = sAPCosts; do { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, pSoldier->aiData.bShownAimTime); } while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts == sCurAPCosts && usShotsLeft >= pSoldier->bDoAutofire); pSoldier->bDoAutofire--; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, pSoldier->aiData.bShownAimTime); if(!EnoughPoints( pSoldier, sAPCosts, 0, FALSE )) //We've not enough points to fire those bullets { pSoldier->flags.autofireLastStep = TRUE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = FALSE; //both last step conditions are false } else { pSoldier->flags.autofireLastStep = TRUE; } } } // single fire mode else { sGridNo = usMapPos; bTargetLevel = (INT8)gsInterfaceLevel; // Look for a target here... if ( gfUIFullTargetFound ) { // Get target soldier, if one exists pTSoldier = MercPtrs[ gusUIFullTargetID ]; sGridNo = pTSoldier->sGridNo; bTargetLevel = pSoldier->pathing.bLevel; if ( !HandleCheckForBadChangeToGetThrough( pSoldier, pTSoldier, sGridNo , bTargetLevel ) ) { return; } } bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + 1 ); if ( bFutureAim <= maxAimLevels ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim ) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= 1; if ( pSoldier->aiData.bShownAimTime > maxAimLevels ) { pSoldier->aiData.bShownAimTime = maxAimLevels; } } // Else - goto first level! else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; gfDisplayFullCountRing = FALSE; } } } else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_AIM_1; gfDisplayFullCountRing = FALSE; } } } break; case PUNCHCURS: bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + REFINE_PUNCH_2); if ( bFutureAim <= REFINE_PUNCH_2) { sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= REFINE_PUNCH_2; if ( pSoldier->aiData.bShownAimTime > REFINE_PUNCH_2) { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_2; } } // Else - goto first level! else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_1; gfDisplayFullCountRing = FALSE; } } } else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_1; gfDisplayFullCountRing = FALSE; } } break; case KNIFECURS: bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + REFINE_KNIFE_2); if ( bFutureAim <= REFINE_KNIFE_2) { sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= REFINE_KNIFE_2; if ( pSoldier->aiData.bShownAimTime > REFINE_KNIFE_2) { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_2; } } // Else - goto first level! else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_1; gfDisplayFullCountRing = FALSE; } } } else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_1; gfDisplayFullCountRing = FALSE; } } break; case TOSSCURS: //IncrementAimCubeUI( ); break; default: ErasePath( TRUE ); } } UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier ) { UINT8 ubCursor; UINT16 usInHand; // If we are an EPC, do nothing.... //if ( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) ) //{ // return( INVALIDCURS ); //} // AN EPC is always not - attackable unless they are a robot! if ( AM_AN_EPC( pSoldier ) && !( pSoldier->flags.uiStatusFlags & SOLDIER_ROBOT ) ) { return( INVALIDCURS ); } // ATE: if a vehicle.... same thing if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) { return( INVALIDCURS ); } // If we can't be controlled, returninvalid... if ( pSoldier->flags.uiStatusFlags & SOLDIER_ROBOT ) { if ( !pSoldier->CanRobotBeControlled( ) ) { // Display message that robot cannot be controlled.... return( INVALIDCURS ); } } if ( pSoldier->bWeaponMode == WM_ATTACHED_GL ) { // Flugente: if using a rifle grenade, only allow firing if there is a bullet in the gun's magazine (required for firing) if ( HasAttachmentOfClass( &(pSoldier->inv[HANDPOS]), AC_RIFLEGRENADE) ) { OBJECTTYPE* pObj = &(pSoldier->inv[HANDPOS]); if ( (*pObj)[0]->data.gun.ubGunShotsLeft> 0 ) return( TRAJECTORYCURS ); else return( INVALIDCURS ); } return( TRAJECTORYCURS ); } if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || ( pSoldier->bWeaponMode == WM_BURST && ItemIsGrenadeLauncher(pSoldier->inv[HANDPOS].usItem) ) ) { if ( gGameSettings.fOptions [ TOPTION_GL_BURST_CURSOR ] ) return( TARGETCURS ); else return ( TRAJECTORYCURS ); } else if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO || ( pSoldier->bWeaponMode == WM_AUTOFIRE && ItemIsGrenadeLauncher(pSoldier->inv[HANDPOS].usItem) ) ) { if ( gGameSettings.fOptions [ TOPTION_GL_BURST_CURSOR ] ) return( TARGETCURS ); else return ( TRAJECTORYCURS ); } usInHand = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[HANDPOS] ); // Start off with what is in our hand ubCursor = Item[ usInHand ].ubCursor; // OK, check if what is in our hands has a detonator attachment... // Detonators can only be on invalidcurs things... if ( ubCursor == INVALIDCURS ) { if ( HasAttachmentOfClass( &(pSoldier->inv[HANDPOS]), AC_DETONATOR ) ) { ubCursor = BOMBCURS; } else if ( HasAttachmentOfClass( &(pSoldier->inv[HANDPOS]), AC_REMOTEDET ) ) { ubCursor = BOMBCURS; } else if ( HasAttachmentOfClass( &(pSoldier->inv[HANDPOS]), AC_DEFUSE) ) { ubCursor = BOMBCURS; } } // Flugente: cursor for constructing/deconstructing // at the moment the gridno is not required in these functions, thus 1 suffices if (!ItemIsWirecutters(usInHand) && (IsStructureConstructItem(usInHand, 1, pSoldier) || IsStructureDeconstructItem(usInHand, 1, pSoldier))) { ubCursor = FORTICURS; } // Flugente: cursor for handcuffs if ( gGameExternalOptions.fAllowPrisonerSystem && HasItemFlag(usInHand, HANDCUFFS) ) ubCursor = HANDCUFFCURS; // Flugente: camera cursor if ( HasItemFlag( usInHand, CAMERA ) ) ubCursor = CAMERACURS; // Flugente: blood bag cursor if ( HasItemFlag( usInHand, EMPTY_BLOOD_BAG ) ) ubCursor = BLOODBAGCURS; // Flugente: disease if ( HasItemFlag( usInHand, MEDICAL_SPLINT ) ) ubCursor = SPLINTCURS; // Flugente: interactive actions // we only check whether an action is possible in principle, not whether this particular guy can do it. That way we know an action is possible here even if we can't perform it at the moment. // only do this if the item doesn't already allow us to do something else if ( ubCursor == INVALIDCURS || usInHand == NONE ) { INT32 usMapPos = NOWHERE; GetMouseMapPos( &usMapPos ); UINT16 structindex; UINT16 possibleaction = InteractiveActionPossibleAtGridNo( usMapPos, pSoldier->pathing.bLevel, structindex ); if ( possibleaction > INTERACTIVE_STRUCTURE_NO_ACTION ) ubCursor = INTERACTIVEACTIONCURS; } // Flugente: apply misc items to other soldiers // exception: not for medical items (medkits can have drug effects that are applied during bandaging) if ( ubCursor != AIDCURS && ItemCanBeAppliedToOthers( usInHand ) ) { // if item is a bomb, only allow apply if hovering over a soldier (otherwise we cannot mine anymore) if ( Item[ usInHand ].usItemClass & IC_BOMB ) { INT32 usMapPos = NOWHERE; if ( GetMouseMapPos( &usMapPos ) ) { // is there a person here? UINT8 ubPerson = WhoIsThere2(usMapPos, pSoldier->pathing.bLevel); if (ubPerson != NOBODY && MercPtrs[ubPerson] && MercPtrs[ubPerson]->bVisible >= 0) { ubCursor = APPLYITEMCURS; } } } else { ubCursor = APPLYITEMCURS; } } // WANNE.WATER: Allow shooting if we are on a "water" tile, but on level > 0 // Now check our terrain to see if we cannot do the action now... if ( WaterTooDeepForAttacks( pSoldier->sGridNo, pSoldier->pathing.bLevel) && pSoldier->pathing.bLevel == 0 ) { ubCursor = INVALIDCURS; } // If we are out of breath, no cursor... if ( pSoldier->bBreath < OKBREATH && pSoldier->bCollapsed ) { ubCursor = INVALIDCURS; } return( ubCursor ); } // Switch on item, display appropriate feedback cursor for a click.... void HandleUICursorRTFeedback( SOLDIERTYPE *pSoldier ) { UINT8 ubItemCursor; ubItemCursor = GetActionModeCursor( pSoldier ); switch( ubItemCursor ) { case TARGETCURS: if ( pSoldier->bDoBurst ) { //BeginDisplayTimedCursor( ACTION_TARGETREDBURST_UICURSOR, 500 ); } else { if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass == IC_THROWING_KNIFE ) { BeginDisplayTimedCursor( RED_THROW_UICURSOR, 500 ); } else { BeginDisplayTimedCursor( ACTION_TARGETRED_UICURSOR, 500 ); } } break; default: break; } } /* good code from lesh BOOLEAN CanSoldierAffordAimTime( SOLDIERTYPE *pSoldier, INT16 usMapPos, INT8 bAimTime ) { INT8 maxAimLevels; INT16 sAPCosts; maxAimLevels = AllowedAimingLevels(pSoldier); if ( bAimTime < REFINE_AIM_1 || bAimTime > maxAimLevels ) return FALSE; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, bAimTime ); if ( !EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) return FALSE; return TRUE; } BOOLEAN CanSoldierAffordPunching( SOLDIERTYPE *pSoldier, INT16 usMapPos, INT8 bRefineTime ) { INT16 sAPCosts; if ( bRefineTime < REFINE_PUNCH_1 || bRefineTime > REFINE_PUNCH_2 ) return FALSE; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, bRefineTime ); if ( !EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) return FALSE; return TRUE; } BOOLEAN CanSoldierAffordKnifing( SOLDIERTYPE *pSoldier, INT16 usMapPos, INT8 bRefineTime ) { INT16 sAPCosts; if ( bRefineTime < REFINE_KNIFE_1 || bRefineTime > REFINE_KNIFE_2 ) return FALSE; sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, bRefineTime ); if ( !EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) return FALSE; return TRUE; } BOOLEAN CanSoldierAffordBurstBullets( SOLDIERTYPE *pSoldier, INT16 usMapPos ) { INT16 sAPCosts; INT8 bSaveDoAutofire; if ( pSoldier->bDoAutofire < 1 ) { pSoldier->bDoAutofire = 1; return FALSE; } if ( pSoldier->bDoAutofire > pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft ) { pSoldier->bDoAutofire = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; return FALSE; } sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, 0); if ( !EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) return FALSE; return TRUE; } void HandleWheelAdjustCursor( SOLDIERTYPE *pSoldier, INT16 sMapPos, INT16 sDelta ) { UINT8 ubCursor; INT8 bFutureAim; ubCursor = GetActionModeCursor( pSoldier ); // 'snap' cursor to target tile.... if ( gfUIFullTargetFound ) { sMapPos = MercPtrs[ gusUIFullTargetID ]->sGridNo; } switch( ubCursor ) { case TARGETCURS: // warn if bad chance to get through if ( gfUIFullTargetFound && !HandleCheckForBadChangeToGetThrough( pSoldier, MercPtrs[ gusUIFullTargetID ], MercPtrs[ gusUIFullTargetID ]->sGridNo , MercPtrs[ gusUIFullTargetID ]->pathing.bLevel ) ) { return; } if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire ) { } else if( pSoldier->bDoAutofire ) { // AUTOFIRE section INT16 sCurAPCosts, sFutureAPCost; if(pSoldier->flags.autofireLastStep) { pSoldier->flags.autofireLastStep = FALSE; } // change burst length until AP difference is shown sCurAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); do { pSoldier->bDoAutofire += sDelta; sFutureAPCost = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); } while ( CanSoldierAffordBurstBullets( pSoldier, sMapPos ) && (sFutureAPCost - sCurAPCosts) == 0 ); // complement burst by adding bullets, until next bullet produce AP difference sFutureAPCost = sCurAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); while ( CanSoldierAffordBurstBullets( pSoldier, sMapPos ) && (sFutureAPCost - sCurAPCosts) == 0 ) { pSoldier->bDoAutofire += 1; // now it goes as future burst length sFutureAPCost = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); } // display red numbers, if it is last possible burst ap spenditure pSoldier->flags.autofireLastStep = !CanSoldierAffordBurstBullets( pSoldier, sMapPos ); // restore last iteration bullet count (before ap difference) pSoldier->bDoAutofire--; } else { // SINGLE SHOT section bFutureAim = pSoldier->aiData.bShownAimTime + sDelta; if ( CanSoldierAffordAimTime( pSoldier, sMapPos, bFutureAim) ) { pSoldier->aiData.bShownAimTime += sDelta; // display yellow circle, if it is last possible aim level gfDisplayFullCountRing = !CanSoldierAffordAimTime( pSoldier, sMapPos, bFutureAim + 1); } } break; case PUNCHCURS: // PUNCHING section bFutureAim = pSoldier->aiData.bShownAimTime + sDelta * REFINE_PUNCH_2; // possibly dirty hack if ( CanSoldierAffordPunching( pSoldier, sMapPos, bFutureAim) ) { pSoldier->aiData.bShownAimTime += sDelta * REFINE_PUNCH_2; // display yellow circle, if it is last possible aim level gfDisplayFullCountRing = !CanSoldierAffordPunching( pSoldier, sMapPos, bFutureAim + 1); } break; case KNIFECURS: // KNIFING section bFutureAim = pSoldier->aiData.bShownAimTime + sDelta * REFINE_KNIFE_2; // possibly dirty hack if ( CanSoldierAffordKnifing( pSoldier, sMapPos, bFutureAim) ) { pSoldier->aiData.bShownAimTime += sDelta * REFINE_KNIFE_2; // display yellow circle, if it is last possible aim level gfDisplayFullCountRing = !CanSoldierAffordKnifing( pSoldier, sMapPos, bFutureAim + 1); } break; }; } */ // HEADROCK HAM B2.7: This function gives us an "approximate" CTH bar, that doesn't show the exact value of // CTH unless the character is well-trained. The worse trained a character is, the wilder the speculation. UINT32 ChanceToHitApproximation( SOLDIERTYPE * pSoldier, UINT32 uiChance ) { UINT16 bExpLevelValue = EffectiveExpLevel(pSoldier); // SANDRO - changed to effective level calc UINT16 bMarksmanship = pSoldier->stats.bMarksmanship; UINT16 bWisdom = pSoldier->stats.bWisdom; UINT16 iNumStages, iNumStagesBase, StageSize, SubStageSize; UINT8 iSniper; UINT16 uiNewChance; UINT16 i; if ( pSoldier->ubProfile != NO_PROFILE ) { if ( gGameOptions.fNewTraitSystem ) // old/new traits - SANDRO iSniper = NUM_SKILL_TRAITS( pSoldier, SNIPER_NT ); else iSniper = NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT ); } else { // Dunno, but I get the strong feeling that we don't want CTH bars for non-mercs. return(0); } if (uiChance == 100) { // Sure hit. The player needs to set Maximum_CTH to 100 for this to happen. Normally, the // max chance is only 99. return(100); } if (uiChance == 0) { // Sure Miss. The player needs to set Minimum_CTH to 0 for this to happen. Normally, the // min chance is 1. return(0); } uiNewChance = uiChance; iNumStagesBase = __min(165,__max(0, ((bExpLevelValue * 10) - 30) + (bMarksmanship - 25) + (bWisdom - 50))); //iNumStagesBase = __max(255, iNumStagesBase); iNumStagesBase = __min(165, iNumStagesBase + (10 * iSniper)); iNumStagesBase = (UINT16)((float)iNumStagesBase * (float)((float)iNumStagesBase / 195.0)); // Gives a number between 2 and 51. iNumStages = __max(2,(100 * iNumStagesBase) / 300); // Find out how large the slices are StageSize = 100/iNumStages; // Correct iNumStages to match StageSize, +1 compensates for the rounding down of StageSize. iNumStages = (100/StageSize) + 1; // These sub-stages are used to determine which way to round the results. SubStageSize = __max(1, StageSize / 2); if (StageSize == 1) // CTH assessment is perfect. No need for the rest of this function return (uiChance); // Jump each "stage" and see whether the CTH is within this slice of the scale for (i=0; i (UINT16) uiChance) ) { return (CurrentStage); } else if ( (CurrentStage + SubStageSize <= (UINT16) uiChance) && (NextStage > (UINT16) uiChance)) { return (NextStage); } } // Something went wrong. Return 50, as in "God Knows" return (50); } /* //nearly debugged Headrock code, need to finish aiming for fist and knife void HandleWheelAdjustCursor( SOLDIERTYPE *pSoldier, INT16 sMapPos, INT16 sDelta ) { UINT16 usInHand; INT16 sAPCosts; INT8 bFutureAim; UINT8 ubCursor; SOLDIERTYPE *pTSoldier; INT16 sGridNo; INT8 bTargetLevel; usInHand = pSoldier->inv[HANDPOS].usItem; ubCursor = GetActionModeCursor( pSoldier ); UINT8 maxAimLevels = AllowedAimingLevels(pSoldier); // 'snap' cursor to target tile.... if ( gfUIFullTargetFound ) { sMapPos = MercPtrs[ gusUIFullTargetID ]->sGridNo; } switch( ubCursor ) { case TARGETCURS: // CHECK IF GUY HAS IN HAND A WEAPON if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire) { // Do nothing! // pSoldier->aiData.bShownAimTime = REFINE_AIM_BURST; } else if(pSoldier->bDoAutofire) { INT16 sCurAPCosts; if(pSoldier->bDoAutofire==1 && sDelta<0) return; if(pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft > pSoldier->bDoAutofire ) { //Calculate how many bullets we need to fire to add at least one more AP sAPCosts = sCurAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts <= sCurAPCosts && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft > pSoldier->bDoAutofire) //Increment the bullet count until we run out of APs or we spend the whole AP { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); } //we've stepped over the border and used up one more ap, now let's make sure that it is spent to maximize the bullets sCurAPCosts = sAPCosts; do { pSoldier->bDoAutofire+=sDelta; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); } while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts == sCurAPCosts && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire); pSoldier->bDoAutofire--; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, 0); if(!EnoughPoints( pSoldier, sAPCosts, 0, FALSE )) //We've not enough points to fire those bullets { pSoldier->flags.autofireLastStep = TRUE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = FALSE; //both last step conditions are false } else { if(sDelta<0) { pSoldier->flags.autofireLastStep = FALSE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = TRUE; } } else //single shoot { sGridNo = sMapPos; bTargetLevel = (INT8)gsInterfaceLevel; // Look for a target here... if ( gfUIFullTargetFound ) { // Get target soldier, if one exists pTSoldier = MercPtrs[ gusUIFullTargetID ]; sGridNo = pTSoldier->sGridNo; bTargetLevel = pSoldier->pathing.bLevel; if ( !HandleCheckForBadChangeToGetThrough( pSoldier, pTSoldier, sGridNo , bTargetLevel ) ) return; } bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + sDelta ); if(bFutureAim <0)return; if ( bFutureAim <= maxAimLevels ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim ) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= sDelta; if ( pSoldier->aiData.bShownAimTime > maxAimLevels ) pSoldier->aiData.bShownAimTime = maxAimLevels; gfDisplayFullCountRing = FALSE; } else // Else - goto first level! gfDisplayFullCountRing = TRUE; } else gfDisplayFullCountRing = TRUE; } break; case PUNCHCURS: bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + REFINE_PUNCH_2 ); if ( bFutureAim <= REFINE_PUNCH_2 ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= (sDelta*REFINE_PUNCH_2); if ( pSoldier->aiData.bShownAimTime > REFINE_PUNCH_2 ) { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_2; } } // Else - goto first level! else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_1; gfDisplayFullCountRing = FALSE; } } } else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_PUNCH_1; gfDisplayFullCountRing = FALSE; } } break; case KNIFECURS: bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + (sDelta*REFINE_KNIFE_2) ); if ( bFutureAim <= REFINE_KNIFE_2 ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= REFINE_KNIFE_2; if ( pSoldier->aiData.bShownAimTime > REFINE_KNIFE_2 ) { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_2; } } // Else - goto first level! else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_1; gfDisplayFullCountRing = FALSE; } } } else { if ( !gfDisplayFullCountRing ) { gfDisplayFullCountRing = TRUE; } else { pSoldier->aiData.bShownAimTime = REFINE_KNIFE_1; gfDisplayFullCountRing = FALSE; } } break; case TOSSCURS: //IncrementAimCubeUI( ); break; default: ErasePath( TRUE ); } } */ void HandleWheelAdjustCursor( SOLDIERTYPE *pSoldier, INT32 sMapPos, INT32 sDelta, INT16 brstmode ) { UINT16 usInHand; INT16 sAPCosts; INT8 bFutureAim; UINT8 ubCursor; SOLDIERTYPE *pTSoldier; INT32 sGridNo; INT8 bTargetLevel; UINT16 usShotsLeft = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; if (pSoldier->IsValidSecondHandBurst()) { usShotsLeft = min( (pSoldier->inv[ SECONDHANDPOS ][0]->data.gun.ubGunShotsLeft), usShotsLeft ); } usInHand = pSoldier->inv[HANDPOS].usItem; ubCursor = GetActionModeCursor( pSoldier ); UINT8 maxAimLevels = AllowedAimingLevels(pSoldier, sMapPos); // 'snap' cursor to target tile.... if ( gfUIFullTargetFound ) sMapPos = MercPtrs[ gusUIFullTargetID ]->sGridNo; int aimlevelchange = 1; int maxAimCanAfford = 0; int oldAim = pSoldier->aiData.bShownAimTime; int hitChance = 0; int maxCtH = 0; //maximum chance to hit int minAimForCtH = 0; //minimum AP needed to get maximum CtH switch( ubCursor ) { case TARGETCURS: { if(pSoldier->bDoAutofire && (gfKeyState[ ALT ] || brstmode) ) //add bullets to autofire { INT16 sCurAPCosts; if(pSoldier->bDoAutofire==1 && sDelta<0) return; if(usShotsLeft > pSoldier->bDoAutofire ) { //Calculate how many bullets we need to fire to add at least one more AP sAPCosts = sCurAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts <= sCurAPCosts && usShotsLeft > pSoldier->bDoAutofire) //Increment the bullet count until we run out of APs or we spend the whole AP { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); } //we've stepped over the border and used up one more ap, now let's make sure that it is spent to maximize the bullets sCurAPCosts = sAPCosts; do { pSoldier->bDoAutofire+=sDelta; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); } while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts == sCurAPCosts && usShotsLeft >= pSoldier->bDoAutofire); pSoldier->bDoAutofire--; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); if(!EnoughPoints( pSoldier, sAPCosts, 0, FALSE )) //We've not enough points to fire those bullets { pSoldier->flags.autofireLastStep = TRUE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = FALSE; //both last step conditions are false } else { if(sDelta<0) { pSoldier->flags.autofireLastStep = FALSE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = TRUE; } return; } else { sGridNo = sMapPos; bTargetLevel = (INT8)gsInterfaceLevel; // Look for a target here... if ( gfUIFullTargetFound ) { // Get target soldier, if one exists pTSoldier = MercPtrs[ gusUIFullTargetID ]; sGridNo = pTSoldier->sGridNo; bTargetLevel = pSoldier->pathing.bLevel; if ( !HandleCheckForBadChangeToGetThrough( pSoldier, pTSoldier, sGridNo , bTargetLevel ) ) { return; } } bFutureAim = maxAimLevels; //Find the actual maximum aim that can be done. while( bFutureAim > 0 ) { //Can afford this many? sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, bFutureAim ); if( !maxAimCanAfford && EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { maxAimCanAfford = bFutureAim; } //Find the least amount of aiming needed to get maximum CtH //Give some slack, but don't let the slack accumulate. //dd???{ //hitChance = CalcChanceToHitGun( pSoldier, sMapPos, bFutureAim, pSoldier->bAimShotLocation ); //if( hitChance >= maxCtH - 3){ // minAimForCtH = bFutureAim; // if(hitChance >= maxCtH) maxCtH = hitChance; //} //ddd???} //Loop to 0. --bFutureAim; } bFutureAim = oldAim + sDelta; if( bFutureAim < REFINE_AIM_1 ) return; //Do it. if( bFutureAim < REFINE_AIM_1 ) { //Went too low. Set to max aim. bFutureAim = maxAimCanAfford; gfDisplayFullCountRing = TRUE; } else if( bFutureAim <= maxAimCanAfford ) { //Aim level is doable. No modification needed. gfDisplayFullCountRing = FALSE; } else { //Aim too high. Give indicator that is is maxed, then reset aim refinement. if ( gfDisplayFullCountRing ) return; else{ //Display ring to indicate maximum aim reached. bFutureAim = oldAim; gfDisplayFullCountRing = TRUE; } } //Apply. pSoldier->aiData.bShownAimTime = bFutureAim; //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"caft=%d",CalcChanceToHitGun( pSoldier, sMapPos, pSoldier->aiData.bShownAimTime, pSoldier->bAimShotLocation ) ); } } break; case PUNCHCURS: { bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + (sDelta*REFINE_PUNCH_2) ); if(bFutureAim < 0 ) return; if ( bFutureAim <= REFINE_PUNCH_2 ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= (sDelta*REFINE_PUNCH_2); if ( pSoldier->aiData.bShownAimTime > REFINE_PUNCH_2 ) pSoldier->aiData.bShownAimTime = REFINE_PUNCH_2; gfDisplayFullCountRing = FALSE; } else gfDisplayFullCountRing = TRUE; } else gfDisplayFullCountRing = TRUE; } break; case KNIFECURS: { bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + (sDelta*REFINE_KNIFE_2) ); if(bFutureAim<0) return; if ( bFutureAim <= REFINE_KNIFE_2 ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= (sDelta*REFINE_KNIFE_2); if ( pSoldier->aiData.bShownAimTime > REFINE_KNIFE_2 ) pSoldier->aiData.bShownAimTime = REFINE_KNIFE_2; gfDisplayFullCountRing = FALSE; } else gfDisplayFullCountRing = TRUE; } else gfDisplayFullCountRing = TRUE; } break; case TOSSCURS: //IncrementAimCubeUI( ); break; default: ErasePath( TRUE ); } } void HandleWheelAdjustCursorWOAB( SOLDIERTYPE *pSoldier, INT32 sMapPos, INT32 sDelta ) { UINT16 usInHand; INT16 sAPCosts; INT8 bFutureAim; UINT8 ubCursor; SOLDIERTYPE *pTSoldier; INT32 sGridNo; INT8 bTargetLevel; UINT16 usShotsLeft = pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft; if (pSoldier->IsValidSecondHandBurst()) { usShotsLeft = min( (pSoldier->inv[ SECONDHANDPOS ][0]->data.gun.ubGunShotsLeft), usShotsLeft ); } usInHand = pSoldier->inv[HANDPOS].usItem; ubCursor = GetActionModeCursor( pSoldier ); UINT8 maxAimLevels = AllowedAimingLevels(pSoldier, sMapPos); // 'snap' cursor to target tile.... if ( gfUIFullTargetFound ) { sMapPos = MercPtrs[ gusUIFullTargetID ]->sGridNo; } switch( ubCursor ) { case TARGETCURS: // CHECK IF GUY HAS IN HAND A WEAPON if ( pSoldier->bDoBurst && !pSoldier->bDoAutofire) { // Do nothing! // pSoldier->aiData.bShownAimTime = REFINE_AIM_BURST; } else if(pSoldier->bDoAutofire) { INT16 sCurAPCosts; if(pSoldier->bDoAutofire==1 && sDelta<0) return; if(usShotsLeft > pSoldier->bDoAutofire ) { //Calculate how many bullets we need to fire to add at least one more AP sAPCosts = sCurAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts <= sCurAPCosts && usShotsLeft > pSoldier->bDoAutofire) //Increment the bullet count until we run out of APs or we spend the whole AP { pSoldier->bDoAutofire++; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); } //we've stepped over the border and used up one more ap, now let's make sure that it is spent to maximize the bullets sCurAPCosts = sAPCosts; do { pSoldier->bDoAutofire+=sDelta; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); } while(EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) && sAPCosts == sCurAPCosts && usShotsLeft >= pSoldier->bDoAutofire); pSoldier->bDoAutofire--; sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, pSoldier->aiData.bShownAimTime); if(!EnoughPoints( pSoldier, sAPCosts, 0, FALSE )) //We've not enough points to fire those bullets { pSoldier->flags.autofireLastStep = TRUE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = FALSE; //both last step conditions are false } else { if(sDelta<0) { pSoldier->flags.autofireLastStep = FALSE; pSoldier->bDoAutofire--; } else pSoldier->flags.autofireLastStep = TRUE; } } else //single shoot { sGridNo = sMapPos; bTargetLevel = (INT8)gsInterfaceLevel; // Look for a target here... if ( gfUIFullTargetFound ) { // Get target soldier, if one exists pTSoldier = MercPtrs[ gusUIFullTargetID ]; sGridNo = pTSoldier->sGridNo; bTargetLevel = pSoldier->pathing.bLevel; if ( !HandleCheckForBadChangeToGetThrough( pSoldier, pTSoldier, sGridNo , bTargetLevel ) ) return; } bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + sDelta ); if(bFutureAim <0)return; if ( bFutureAim <= maxAimLevels ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim ) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= sDelta; if ( pSoldier->aiData.bShownAimTime > maxAimLevels ) pSoldier->aiData.bShownAimTime = maxAimLevels; gfDisplayFullCountRing = FALSE; } else // Else - goto first level! gfDisplayFullCountRing = TRUE; } else gfDisplayFullCountRing = TRUE; } break; case PUNCHCURS: bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + (sDelta*REFINE_PUNCH_2) ); if(bFutureAim<0) return; if ( bFutureAim <= REFINE_PUNCH_2 ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= (sDelta*REFINE_PUNCH_2); if ( pSoldier->aiData.bShownAimTime > REFINE_PUNCH_2 ) pSoldier->aiData.bShownAimTime = REFINE_PUNCH_2; gfDisplayFullCountRing = FALSE; } else gfDisplayFullCountRing = TRUE; } else gfDisplayFullCountRing = TRUE; break; case KNIFECURS: bFutureAim = (INT8)( pSoldier->aiData.bShownAimTime + (sDelta*REFINE_KNIFE_2) ); if(bFutureAim<0) return; if ( bFutureAim <= REFINE_KNIFE_2 ) { sAPCosts = CalcTotalAPsToAttack( pSoldier, sMapPos, TRUE, (INT8)(bFutureAim / 2) ); // Determine if we can afford! if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) ) { pSoldier->aiData.bShownAimTime+= (sDelta*REFINE_KNIFE_2); if ( pSoldier->aiData.bShownAimTime > REFINE_KNIFE_2 ) pSoldier->aiData.bShownAimTime = REFINE_KNIFE_2; gfDisplayFullCountRing = FALSE; } else gfDisplayFullCountRing = TRUE; } else gfDisplayFullCountRing = TRUE; break; case TOSSCURS: //IncrementAimCubeUI( ); break; default: ErasePath( TRUE ); } } UINT8 DefaultAutofireBulletsByGunClass( SOLDIERTYPE* pSoldier ) { UINT32 usItemClass; UINT16 usItem; UINT8 ubBullets = 1; if( !pSoldier ) return 1; if( !pSoldier->inv[ pSoldier->ubAttackingHand ].exists() ) return 1; usItem = pSoldier->inv[ pSoldier->ubAttackingHand ].usItem; usItemClass = Item[ usItem ].usItemClass; if( usItemClass != IC_GUN ) return 1; switch( Weapon[ Item[ usItem ].ubClassIndex ].ubWeaponType ) { case GUN_M_PISTOL: case GUN_PISTOL: case GUN_SMG: ubBullets = gGameExternalOptions.ubSetDefaultAutofireBulletsSMG; break; case GUN_AS_RIFLE: case GUN_RIFLE: case GUN_SN_RIFLE : case GUN_SHOTGUN: ubBullets = gGameExternalOptions.ubSetDefaultAutofireBulletsAR; break; case GUN_LMG: ubBullets = gGameExternalOptions.ubSetDefaultAutofireBulletsMG; break; } return ubBullets; }