From 008e216b17d8f52d6e1307098bc415305af7f2b9 Mon Sep 17 00:00:00 2001 From: MaddMugsy Date: Sat, 10 Jun 2006 07:22:16 +0000 Subject: [PATCH] -added underslung/attached GLs to AI inventory and enabled AI to use them :) -rewrote the AI sniper code to take advantage of readying weapons. Added new SNIPER order for soldiers that's assigned when a soldier has a sniper rifle (80% chance) or a scope (40% chance). -spreadsheet update git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@216 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameVersion.cpp | 2 +- Tactical/Handle UI.cpp | 2 +- Tactical/Inventory Choosing.cpp | 6 +- Tactical/Overhead Types.h | 2 +- Tactical/Soldier Control.cpp | 2 + Tactical/Soldier Create.cpp | 37 ++- Tactical/bullets.cpp | 1 + Tactical/opplist.cpp | 3 +- TacticalAI/AIMain.cpp | 15 +- TacticalAI/Attacks.cpp | 22 +- TacticalAI/DecideAction.cpp | 438 ++++++++++++++++++-------------- TacticalAI/ai.h | 1 + TileEngine/Tile Animation.cpp | 11 +- TileEngine/physics.cpp | 2 +- 14 files changed, 334 insertions(+), 210 deletions(-) diff --git a/GameVersion.cpp b/GameVersion.cpp index 0ac25a980..155fb61bb 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -23,7 +23,7 @@ INT16 zVersionLabel[256] = { L"Beta v. 0.98" }; #else //RELEASE BUILD VERSION - INT16 zVersionLabel[256] = { L"Release v1.13.215" }; + INT16 zVersionLabel[256] = { L"Release v1.13.216" }; #endif diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index f5b01feb4..3201ee1f5 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -4630,7 +4630,7 @@ BOOLEAN MakeSoldierTurn( SOLDIERTYPE *pSoldier, INT16 sXPos, INT16 sYPos ) pSoldier->sLastTarget = sXPos + (MAXCOL * sYPos); DeductPoints( pSoldier, (INT16)(sAPCost - sAPCostToReady), 0 ); - + HandleSight( pSoldier, SIGHT_LOOK ); return( TRUE ); diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index e8caa3274..503a7a37b 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -1534,7 +1534,11 @@ void ChooseSpecialWeaponsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 // CreateItem( 298, (INT8)(50 + Random( 51 )), &Object ); // PlaceObjectInSoldierCreateStruct( pp, &Object ); - if (fGrenadeLauncher) + if ( IsGrenadeLauncherAttached(&pp->Inv[HANDPOS]) ) //Madd: if there's a grenade launcher attached to the gun, then use that instead of giving him another one + { + itemGrenadeLauncher = GetAttachedGrenadeLauncher(&pp->Inv[HANDPOS]); + } + else if (fGrenadeLauncher) { itemGrenadeLauncher = PickARandomItem ( GRENADELAUNCHER ); // give grenade launcher diff --git a/Tactical/Overhead Types.h b/Tactical/Overhead Types.h index 4f18a1435..c74d31d3c 100644 --- a/Tactical/Overhead Types.h +++ b/Tactical/Overhead Types.h @@ -125,7 +125,7 @@ enum ONCALL, // helps buddies anywhere within the sector SEEKENEMY, // not tied down to any one particular spot RNDPTPATROL, // patrols randomly using patrolGrids - //SNIPER, // snipes + SNIPER, // snipes MAXORDERS }; diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index af05e2d2c..b766fb93d 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -3253,6 +3253,8 @@ BOOLEAN InternalSoldierReadyWeapon( SOLDIERTYPE *pSoldier, UINT8 sFacingDir, BOO } + HandleSight(pSoldier, SIGHT_LOOK ); + return( fReturnVal ); } diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index e25c272b6..d34e540ff 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -123,6 +123,26 @@ void RandomizeNewSoldierStats( SOLDIERCREATE_STRUCT *pCreateStruct ) pCreateStruct->bAIMorale = MORALE_FEARLESS; } +void DecideToAssignSniperOrders( SOLDIERCREATE_STRUCT * pp ) +{ + //Madd: decide if soldier should be a sniper + + // 30% of anybody w/a scope and decent range being a sniper, 80% chance of a guy w/a sniper rifle being a sniper + if ( (GunRange(&pp->Inv[HANDPOS]) >= 40 && IsScoped(&pp->Inv[HANDPOS]) && Random(100) < 30) || ( Weapon[pp->Inv[HANDPOS].usItem].ubWeaponType == GUN_SN_RIFLE && Random(100) < 80 ) ) + { + pp->bOrders = SNIPER; + + if (pp->bAttitude == BRAVEAID) + pp->bAttitude = BRAVESOLO; + + if (pp->bAttitude == CUNNINGAID) + pp->bAttitude = CUNNINGSOLO; + + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Sniper Created") ); + } + +} + SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 *pubID ) { SOLDIERTYPE Soldier; @@ -1651,6 +1671,8 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B if( !bp->fDetailedPlacement && ubSoldierClass != SOLDIER_CLASS_NONE && ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_MINER ) GenerateRandomEquipment( pp, ubSoldierClass, bp->bRelativeEquipmentLevel); + DecideToAssignSniperOrders(pp); + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenBasicPlacementInfo done")); } @@ -1810,13 +1832,14 @@ void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( //return; } } - if ( !gGameOptions.fGunNut ) - { - ReplaceExtendedGuns( pp, bp->ubSoldierClass ); - } + //if ( !gGameOptions.fGunNut ) + //{ + // ReplaceExtendedGuns( pp, bp->ubSoldierClass ); + //} if( bp->ubSoldierClass != SOLDIER_CLASS_NONE && bp->ubSoldierClass != SOLDIER_CLASS_CREATURE && bp->ubSoldierClass != SOLDIER_CLASS_MINER ) { GenerateRandomEquipment( pp, bp->ubSoldierClass, bp->bRelativeEquipmentLevel); + DecideToAssignSniperOrders(pp); } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo done")); @@ -2103,6 +2126,7 @@ SOLDIERTYPE* TacticalCreateArmyTroop() pSoldier->sNoiseGridno = (INT16) (CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS); pSoldier->ubNoiseVolume = MAX_MISC_NOISE_DURATION; } + return( pSoldier ); } @@ -2146,6 +2170,7 @@ SOLDIERTYPE* TacticalCreateEliteEnemy() pSoldier->sNoiseGridno = (INT16)(CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS); pSoldier->ubNoiseVolume = MAX_MISC_NOISE_DURATION; } + return( pSoldier ); } @@ -2208,7 +2233,9 @@ SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass ) //bp.bAttitude = AGGRESSIVE; bp.bBodyType = -1; CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp ); - return TacticalCreateSoldier( &pp, &ubID ); + pSoldier = TacticalCreateSoldier( &pp, &ubID ); + + return pSoldier; } SOLDIERTYPE* TacticalCreateCreature( INT8 bCreatureBodyType ) diff --git a/Tactical/bullets.cpp b/Tactical/bullets.cpp index bf3b583c2..11518a461 100644 --- a/Tactical/bullets.cpp +++ b/Tactical/bullets.cpp @@ -410,6 +410,7 @@ void AddMissileTrail( BULLET *pBullet, FIXEDPT qCurrX, FIXEDPT qCurrY, FIXEDPT q { strcpy( AniParams.zCachedFile, "TILECACHE\\BULLET_TRACER.STI" ); AniParams.uiFlags |= ANITILE_LIGHT; + AniParams.sDelay = 0; } CreateAnimationTile( &AniParams ); diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index c770e834f..43874d885 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -830,7 +830,8 @@ void HandleSight(SOLDIERTYPE *pSoldier, UINT8 ubSightFlags) else if ( gGameOptions.ubDifficultyLevel >= DIF_LEVEL_MEDIUM ) { // don't allow admins to radio - if ( pSoldier->bTeam == ENEMY_TEAM && gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition && pSoldier->ubSoldierClass != SOLDIER_CLASS_ADMINISTRATOR ) + //Madd: Huh? why not admins? removed. + if ( pSoldier->bTeam == ENEMY_TEAM && gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition ) //&& pSoldier->ubSoldierClass != SOLDIER_CLASS_ADMINISTRATOR ) { RadioSightings(pSoldier,EVERYBODY, pSoldier->bTeam ); } diff --git a/TacticalAI/AIMain.cpp b/TacticalAI/AIMain.cpp index d0c07a2aa..93e89e475 100644 --- a/TacticalAI/AIMain.cpp +++ b/TacticalAI/AIMain.cpp @@ -2000,6 +2000,8 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier) // reset this field, too pSoldier->bLastAttackHit = FALSE; + UINT16 usHandItem = pSoldier->inv[HANDPOS].usItem; + #ifdef TESTAICONTROL if (gfTurnBasedAI || gTacticalStatus.fAutoBandageMode) { @@ -2348,7 +2350,11 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier) SimulMessage(tempstr,3000,NODECRYPT); } #endif - iRetCode = HandleItem( pSoldier, pSoldier->usActionData, pSoldier->bTargetLevel, pSoldier->inv[HANDPOS].usItem, FALSE ); + + if ( pSoldier->bAction == AI_ACTION_TOSS_PROJECTILE && IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS]) ) + usHandItem = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS]); + + iRetCode = HandleItem( pSoldier, pSoldier->usActionData, pSoldier->bTargetLevel, usHandItem, FALSE ); if ( iRetCode != ITEM_HANDLE_OK) { if ( iRetCode != ITEM_HANDLE_BROKEN ) // if the item broke, this is 'legal' and doesn't need reporting @@ -2546,6 +2552,11 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier) ActionDone( pSoldier ); break; + case AI_ACTION_RAISE_GUN: //Madd: action added for snipers to ready weapon and use vision range bonuses + SoldierReadyWeapon(pSoldier); + HandleSight(pSoldier, SIGHT_LOOK | SIGHT_RADIO); + break; + case AI_ACTION_CLIMB_ROOF: DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Executing: AI_ACTION_CLIMB_ROOF"); @@ -2632,7 +2643,7 @@ void CheckForChangingOrders(SOLDIERTYPE *pSoldier) // crank up ONGUARD to CLOSEPATROL, and CLOSEPATROL to FARPATROL pSoldier->bOrders++; // increase roaming range by 1 category } - else if ( pSoldier->bTeam == MILITIA_TEAM ) + else if ( pSoldier->bTeam == MILITIA_TEAM && pSoldier->bOrders != SNIPER ) { // go on alert! pSoldier->bOrders = SEEKENEMY; diff --git a/TacticalAI/Attacks.cpp b/TacticalAI/Attacks.cpp index be6c22384..aa4442a87 100644 --- a/TacticalAI/Attacks.cpp +++ b/TacticalAI/Attacks.cpp @@ -40,6 +40,9 @@ void LoadWeaponIfNeeded(SOLDIERTYPE *pSoldier) usInHand = pSoldier->inv[HANDPOS].usItem; + if ( IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS]) ) + usInHand = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS]); + // if he's got a MORTAR in his hand, make sure he has a MORTARSHELL avail. if (Item[usInHand].mortar ) { @@ -129,6 +132,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns ubBestChanceToHit = ubBestAimTime = ubChanceToHit = 0; pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem; + pSoldier->bWeaponMode = WM_NORMAL; ubBurstAPs = CalcAPsToBurst( CalcActionPoints( pSoldier ), &(pSoldier->inv[HANDPOS]) ); @@ -469,6 +473,9 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow) usInHand = pSoldier->inv[HANDPOS].usItem; usGrenade = NOTHING; + if ( IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS]) ) + usInHand = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS]); + if ( EXPLOSIVE_GUN( usInHand ) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"explosive gun"); @@ -1001,7 +1008,7 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow) else { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: checking chance for launcher to beat cover"); - ubChanceToGetThrough = 100 * CalculateLaunchItemChanceToGetThrough( pSoldier, &(pSoldier->inv[ HANDPOS ] ), sGridNo, bOpponentLevel[ubLoop], 0, &sEndGridNo, TRUE, &bEndLevel, FALSE ); //NumMessage("Chance to get through = ",ubChanceToGetThrough); + ubChanceToGetThrough = 100 * CalculateLaunchItemChanceToGetThrough( pSoldier, &(pSoldier->inv[ bPayloadPocket ] ), sGridNo, bOpponentLevel[ubLoop], 0, &sEndGridNo, TRUE, &bEndLevel, FALSE ); //NumMessage("Chance to get through = ",ubChanceToGetThrough); // if we can't possibly get through all the cover if (ubChanceToGetThrough == 0 ) { @@ -1979,6 +1986,8 @@ void CheckIfTossPossible(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow) DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CheckIfTossPossible"); UINT8 ubMinAPcost; + pSoldier->bWeaponMode = WM_NORMAL; + if ( TANK( pSoldier ) ) { pBestThrow->bWeaponIn = FindCannon(pSoldier);//FindObj( pSoldier, TANK_CANNON ); @@ -1993,8 +2002,15 @@ void CheckIfTossPossible(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow) pBestThrow->bWeaponIn = FindRocketLauncherOrCannon( pSoldier ); if ( pBestThrow->bWeaponIn == NO_SLOT ) { - // no rocket launcher, consider grenades - pBestThrow->bWeaponIn = FindThrowableGrenade( pSoldier ); + //no rocket launcher -- let's look for an underslung/attached GL + INT8 bGunSlot = FindAIUsableObjClass( pSoldier, IC_GUN ); + if ( bGunSlot != NO_SLOT && IsGrenadeLauncherAttached(&pSoldier->inv[bGunSlot]) ) + { + pBestThrow->bWeaponIn = bGunSlot; + pSoldier->bWeaponMode = WM_ATTACHED_GL; + } + else // no rocket launcher or attached GL, consider grenades + pBestThrow->bWeaponIn = FindThrowableGrenade( pSoldier ); } else { diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index e9fd539da..080271f7f 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -47,6 +47,8 @@ UINT32 guiRedSeekCounter = 0, guiRedHelpCounter = 0; guiRedHideCounter = 0; #define MIN_FLANK_DIST_RED 10 * STRAIGHT_RATIO #define MAX_FLANK_DIST_RED 40 * STRAIGHT_RATIO +extern UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady ); + void DoneScheduleAction( SOLDIERTYPE * pSoldier ) { pSoldier->fAIFlags &= (~AI_CHECK_SCHEDULE); @@ -667,13 +669,17 @@ INT8 DecideActionNamedNPC( SOLDIERTYPE * pSoldier ) INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) { - DOUBLE iChance, iSneaky = 10; - INT8 bInWater,bInGas; + DOUBLE iChance, iSneaky = 10; + INT16 sBestCover; + INT32 iCoverPercentBetter; + INT8 bInWater,bInGas; #ifdef DEBUGDECISIONS STR16 tempstr; #endif - BOOLEAN fCivilian = (PTR_CIVILIAN && (pSoldier->ubCivilianGroup == NON_CIV_GROUP || pSoldier->bNeutral || (pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) ); +DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen")); + +BOOLEAN fCivilian = (PTR_CIVILIAN && (pSoldier->ubCivilianGroup == NON_CIV_GROUP || pSoldier->bNeutral || (pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) ); BOOLEAN fCivilianOrMilitia = PTR_CIV_OR_MILITIA; gubNPCPathCount = 0; @@ -820,7 +826,11 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) } - //////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// // POINT PATROL: move towards next point unless getting a bit winded //////////////////////////////////////////////////////////////////////////// @@ -836,7 +846,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) pSoldier->bNextAction = AI_ACTION_WAIT; pSoldier->usNextActionData = RealtimeDelay( pSoldier ); } - return(AI_ACTION_POINT_PATROL); + return(AI_ACTION_POINT_PATROL); } else { @@ -869,6 +879,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) // WHEN LEFT IN WATER OR GAS, GO TO NEAREST REACHABLE SPOT OF UNGASSED LAND //////////////////////////////////////////////////////////////////////////// + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: get out of water and gas")); if (bInWater || bInGas) { @@ -891,6 +902,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) // REST IF RUNNING OUT OF BREATH //////////////////////////////////////////////////////////////////////// + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: rest if running out of breath")); // if our breath is running a bit low, and we're not in the way or in water if ((pSoldier->bBreath < 75) && !bInWater) { @@ -902,7 +914,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) //////////////////////////////////////////////////////////////////////////// - // TAKE SNIPER POSITION: determine % chance to climb onto a building + // CLIMB A BUILDING //////////////////////////////////////////////////////////////////////////// if (!fCivilian) @@ -920,6 +932,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) case POINTPATROL: iChance = -20; break; case FARPATROL: iChance += -30; break; case SEEKENEMY: iChance += -20; break; + case SNIPER: iChance += 70; break; } // modify for attitude @@ -942,13 +955,6 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) // reduce chance for any injury, less likely to hop up if hurt iChance -= (pSoldier->bLifeMax - pSoldier->bLife); - INT8 bSlot = FindAIUsableObjClass( pSoldier, IC_GUN ); - if ( GunRange(&pSoldier->inv[bSlot]) >= 30 && IsScoped(&pSoldier->inv[bSlot]) ) - iChance += 40; - - if ( Weapon[pSoldier->inv[bSlot].usItem].ubWeaponType == GUN_SN_RIFLE ) - iChance *= 2; - // reduce chance if breath is down //iChance -= (100 - pSoldier->bBreath); // don't care @@ -963,17 +969,6 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) if ( CanClimbFromHere ( pSoldier, fUp ) ) { DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing roof",pSoldier->ubID) ); - if ( GunRange(&pSoldier->inv[bSlot]) >= 30 && IsScoped(&pSoldier->inv[bSlot]) ) - { - pSoldier->sniper = 1; - if ( !sniperwarning ) - { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Watch out for snipers..." ); - //SayQuoteFromAnyBodyInSector( QUOTE_WEARY_SLASH_SUSPUCIOUS ); - sniperwarning = TRUE; - } - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is now a sniper",pSoldier->ubID) ); - } return( AI_ACTION_CLIMB_ROOF ); } else @@ -988,165 +983,199 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) } } + + ///////////////////////////////////////////////////////////////////////////// + // SNIPERS GET TO COVER, EVERYONE INTO THE DARK + ///////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////// + if ( (pSoldier->bOrders == SNIPER || InLightAtNight( pSoldier->sGridNo, pSoldier->bLevel )) && pSoldier->bActionPoints >= MinPtsToMove(pSoldier) ) + { + sBestCover = FindBestNearbyCover(pSoldier,pSoldier->bAIMorale,&iCoverPercentBetter); + if( sBestCover != NOWHERE) + { + pSoldier->usActionData = sBestCover; + return(AI_ACTION_TAKE_COVER); + } + } + + +//////////////////////////////////////////////////////////////////////////// // RANDOM PATROL: determine % chance to start a new patrol route //////////////////////////////////////////////////////////////////////////// - - if (!gubNPCPathCount) // try to limit pathing in Green AI - { - - iChance = 25 + pSoldier->bBypassToGreen; - - // set base chance according to orders - switch (pSoldier->bOrders) + if (!gubNPCPathCount) // try to limit pathing in Green AI { - case STATIONARY: iChance += -20; break; - case ONGUARD: iChance += -15; break; - case ONCALL: break; - case CLOSEPATROL: iChance += +15; break; - case RNDPTPATROL: - case POINTPATROL: iChance = 0; break; - /* - if ( !gfTurnBasedAI ) - { - // realtime deadlock... increase chance! - iChance = 110;// more than 100 in case person is defensive - } - else if ( pSoldier->bInitialActionPoints < pSoldier->bActionPoints ) // could be less because of carried-over points - { - // CJC: allow pt patrol guys to do a random move in case - // of a deadlock provided they haven't done anything yet this turn - iChance= 0; - } - break; - */ - case FARPATROL: iChance += +25; break; - case SEEKENEMY: iChance += -10; break; - } - // modify chance of patrol (and whether it's a sneaky one) by attitude - switch (pSoldier->bAttitude) - { - case DEFENSIVE: iChance += -10; break; - case BRAVESOLO: iChance += 5; break; - case BRAVEAID: break; - case CUNNINGSOLO: iChance += 5; iSneaky += 10; break; - case CUNNINGAID: iSneaky += 5; break; - case AGGRESSIVE: iChance += 10; iSneaky += -5; break; - case ATTACKSLAYONLY: iChance += 10; iSneaky += -5; break; - } + iChance = 25 + pSoldier->bBypassToGreen; - // reduce chance for any injury, less likely to wander around when hurt - iChance -= (pSoldier->bLifeMax - pSoldier->bLife); - - // reduce chance if breath is down, less likely to wander around when tired - iChance -= (100 - pSoldier->bBreath); - - - // if we're in water with land miles (> 25 tiles) away, - // OR if we roll under the chance calculated - if (bInWater || ((INT16) PreRandom(100) < iChance)) - { - pSoldier->usActionData = RandDestWithinRange(pSoldier); - - if (pSoldier->usActionData != NOWHERE) - { - pSoldier->usActionData = GoAsFarAsPossibleTowards( pSoldier, pSoldier->usActionData, AI_ACTION_RANDOM_PATROL ); - } - - if (pSoldier->usActionData != NOWHERE) + // set base chance according to orders + switch (pSoldier->bOrders) { - #ifdef DEBUGDECISIONS - sprintf((CHAR *)tempstr,"%s - RANDOM PATROL to grid %d",pSoldier->name,pSoldier->usActionData); - AIPopMessage(tempstr); - #endif - - if (!gfTurnBasedAI) + case STATIONARY: iChance += -20; break; + case ONGUARD: iChance += -15; break; + case ONCALL: break; + case CLOSEPATROL: iChance += +15; break; + case RNDPTPATROL: + case POINTPATROL: iChance = 0; break; + /* + if ( !gfTurnBasedAI ) + { + // realtime deadlock... increase chance! + iChance = 110;// more than 100 in case person is defensive + } + else if ( pSoldier->bInitialActionPoints < pSoldier->bActionPoints ) // could be less because of carried-over points + { + // CJC: allow pt patrol guys to do a random move in case + // of a deadlock provided they haven't done anything yet this turn + iChance= 0; + } + break; + */ + case FARPATROL: iChance += +25; break; + case SEEKENEMY: iChance += -10; break; + case SNIPER: iChance += -90; break; + } + + // modify chance of patrol (and whether it's a sneaky one) by attitude + switch (pSoldier->bAttitude) + { + case DEFENSIVE: iChance += -10; break; + case BRAVESOLO: iChance += 5; break; + case BRAVEAID: break; + case CUNNINGSOLO: iChance += 5; iSneaky += 10; break; + case CUNNINGAID: iSneaky += 5; break; + case AGGRESSIVE: iChance += 10; iSneaky += -5; break; + case ATTACKSLAYONLY: iChance += 10; iSneaky += -5; break; + } + + // reduce chance for any injury, less likely to wander around when hurt + iChance -= (pSoldier->bLifeMax - pSoldier->bLife); + + // reduce chance if breath is down, less likely to wander around when tired + iChance -= (100 - pSoldier->bBreath); + + + // if we're in water with land miles (> 25 tiles) away, + // OR if we roll under the chance calculated + if (bInWater || ((INT16) PreRandom(100) < iChance)) + { + pSoldier->usActionData = RandDestWithinRange(pSoldier); + + if (pSoldier->usActionData != NOWHERE) + { + pSoldier->usActionData = GoAsFarAsPossibleTowards( pSoldier, pSoldier->usActionData, AI_ACTION_RANDOM_PATROL ); + } + + if (pSoldier->usActionData != NOWHERE) { - // wait after this... - pSoldier->bNextAction = AI_ACTION_WAIT; - pSoldier->usNextActionData = RealtimeDelay( pSoldier ); - } - return(AI_ACTION_RANDOM_PATROL); - } - } - } - - if (!gubNPCPathCount) // try to limit pathing in Green AI - { - //////////////////////////////////////////////////////////////////////////// - // SEEK FRIEND: determine %chance for man to pay a friendly visit - //////////////////////////////////////////////////////////////////////////// - - iChance = 25 + pSoldier->bBypassToGreen; - - // set base chance and maximum seeking distance according to orders - switch (pSoldier->bOrders) - { - case STATIONARY: iChance += -20; break; - case ONGUARD: iChance += -15; break; - case ONCALL: break; - case CLOSEPATROL: iChance += +10; break; - case RNDPTPATROL: - case POINTPATROL: iChance = -10; break; - case FARPATROL: iChance += +20; break; - case SEEKENEMY: iChance += -10; break; - } - - // modify for attitude - switch (pSoldier->bAttitude) - { - case DEFENSIVE: break; - case BRAVESOLO: iChance /= 2; break; // loners - case BRAVEAID: iChance += 10; break; // friendly - case CUNNINGSOLO: iChance /= 2; break; // loners - case CUNNINGAID: iChance += 10; break; // friendly - case AGGRESSIVE: break; - case ATTACKSLAYONLY: break; - } - - // reduce chance for any injury, less likely to wander around when hurt - iChance -= (pSoldier->bLifeMax - pSoldier->bLife); - - // reduce chance if breath is down - iChance -= (100 - pSoldier->bBreath); // very likely to wait when exhausted - - - if ((INT16) PreRandom(100) < iChance) - { - if (RandomFriendWithin(pSoldier)) - { - if ( pSoldier->usActionData == GoAsFarAsPossibleTowards( pSoldier, pSoldier->usActionData, AI_ACTION_SEEK_FRIEND ) ) - { - - #ifdef DEBUGDECISIONS - sprintf((CHAR *)tempstr,"%s - SEEK FRIEND at grid %d",pSoldier->name,pSoldier->usActionData); - AIPopMessage(tempstr); - #endif - - if (fCivilianOrMilitia && !gfTurnBasedAI) - { - // pause at the end of the walk! + #ifdef DEBUGDECISIONS + sprintf((CHAR *)tempstr,"%s - RANDOM PATROL to grid %d",pSoldier->name,pSoldier->usActionData); + AIPopMessage(tempstr); + #endif + + if (!gfTurnBasedAI) + { + // wait after this... pSoldier->bNextAction = AI_ACTION_WAIT; - pSoldier->usNextActionData = (UINT16) REALTIME_CIV_AI_DELAY; - } - - return(AI_ACTION_SEEK_FRIEND); + pSoldier->usNextActionData = RealtimeDelay( pSoldier ); + } + return(AI_ACTION_RANDOM_PATROL); } } } + + if (!gubNPCPathCount) // try to limit pathing in Green AI + { + //////////////////////////////////////////////////////////////////////////// + // SEEK FRIEND: determine %chance for man to pay a friendly visit + //////////////////////////////////////////////////////////////////////////// + + iChance = 25 + pSoldier->bBypassToGreen; + + // set base chance and maximum seeking distance according to orders + switch (pSoldier->bOrders) + { + case STATIONARY: iChance += -20; break; + case ONGUARD: iChance += -15; break; + case ONCALL: break; + case CLOSEPATROL: iChance += +10; break; + case RNDPTPATROL: + case POINTPATROL: iChance = -10; break; + case FARPATROL: iChance += +20; break; + case SEEKENEMY: iChance += -10; break; + case SNIPER: iChance += -100; break; // Madd: Snipers have no friends :( + } + + // modify for attitude + switch (pSoldier->bAttitude) + { + case DEFENSIVE: break; + case BRAVESOLO: iChance /= 2; break; // loners + case BRAVEAID: iChance += 10; break; // friendly + case CUNNINGSOLO: iChance /= 2; break; // loners + case CUNNINGAID: iChance += 10; break; // friendly + case AGGRESSIVE: break; + case ATTACKSLAYONLY: break; + } + + // reduce chance for any injury, less likely to wander around when hurt + iChance -= (pSoldier->bLifeMax - pSoldier->bLife); + + // reduce chance if breath is down + iChance -= (100 - pSoldier->bBreath); // very likely to wait when exhausted + + + if ((INT16) PreRandom(100) < iChance) + { + if (RandomFriendWithin(pSoldier)) + { + if ( pSoldier->usActionData == GoAsFarAsPossibleTowards( pSoldier, pSoldier->usActionData, AI_ACTION_SEEK_FRIEND ) ) + { + + #ifdef DEBUGDECISIONS + sprintf((CHAR *)tempstr,"%s - SEEK FRIEND at grid %d",pSoldier->name,pSoldier->usActionData); + AIPopMessage(tempstr); + #endif + + if (fCivilianOrMilitia && !gfTurnBasedAI) + { + // pause at the end of the walk! + pSoldier->bNextAction = AI_ACTION_WAIT; + pSoldier->usNextActionData = (UINT16) REALTIME_CIV_AI_DELAY; + } + + return(AI_ACTION_SEEK_FRIEND); + } + } + } + } + + +//////////////////////////////////////////////////////////////////////////// + // SNIPERS LIKE TO CROUCH (even in green) + //////////////////////////////////////////////////////////////////////////// + + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Snipers like to crouch")); + // if not in water and not already crouched, try to crouch down first + if (pSoldier->bOrders == SNIPER && !PTR_CROUCHED && IsValidStance( pSoldier, ANIM_CROUCH ) ) + { + if (!gfTurnBasedAI || (GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints)) + { + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Sniper is crouching")); + pSoldier->usActionData = ANIM_CROUCH; + pSoldier->sniper = 0; + return(AI_ACTION_CHANGE_STANCE); } + } //////////////////////////////////////////////////////////////////////////// // LOOK AROUND: determine %chance for man to turn in place //////////////////////////////////////////////////////////////////////////// + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Soldier deciding to turn")); if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) { // avoid 2 consecutive random turns in a row - if (pSoldier->bLastAction != AI_ACTION_CHANGE_FACING) + if (pSoldier->bLastAction != AI_ACTION_CHANGE_FACING) { iChance = 25 + pSoldier->bBypassToGreen; @@ -1160,6 +1189,8 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) if (pSoldier->bAttitude == DEFENSIVE) iChance += 25; + if ( pSoldier->bOrders == SNIPER ) + iChance += 60; if ((INT16)PreRandom(100) < iChance) { @@ -1195,11 +1226,28 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) pSoldier->usNextActionData = RealtimeDelay( pSoldier ); } - return(AI_ACTION_CHANGE_FACING); + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Soldier is turning")); + return(AI_ACTION_CHANGE_FACING); } } } } + + //////////////////////////////////////////////////////////////////////////// + // SNIPER - RAISE WEAPON TO SCAN AREA + //////////////////////////////////////////////////////////////////////////// + +DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Snipers like to raise weapons")); + if ( pSoldier->bOrders == SNIPER && pSoldier->sniper == 0 ) //for some reason this check doesn't work: && pSoldier->usAnimState != PickSoldierReadyAnimation( pSoldier, FALSE ) ) + { + if (!gfTurnBasedAI || GetAPsToReadyWeapon( pSoldier, READY_RIFLE_CROUCH ) <= pSoldier->bActionPoints) + { + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Sniper is raising weapon")); + pSoldier->sniper = 1; + return(AI_ACTION_RAISE_GUN); + } + } + //////////////////////////////////////////////////////////////////////////// @@ -1291,6 +1339,8 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) // set base chance according to orders if ((pSoldier->bOrders == STATIONARY) || (pSoldier->bOrders == ONGUARD)) iChance = 50; + else if ( pSoldier->bOrders == SNIPER ) + iChance = 80; else // all other orders iChance = 25; @@ -1345,6 +1395,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) case POINTPATROL: break; case FARPATROL: iChance += -10; break; case SEEKENEMY: iChance += -20; break; + case SNIPER: iChance += -80; break; //Madd: sniper contacts are supposed to be automatically reported } // modify base chance according to attitude @@ -1487,6 +1538,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) case POINTPATROL: break; case FARPATROL: iChance += 10; break; case SEEKENEMY: iChance += 25; break; + case SNIPER: iChance += -50; break; } // modify chance of patrol (and whether it's a sneaky one) by attitude @@ -1522,7 +1574,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) AIPopMessage(tempstr); #endif - if (fClimb && pSoldier->sniper == 0)//&& pSoldier->usActionData == sNoiseGridNo) + if ( fClimb )//&& pSoldier->usActionData == sNoiseGridNo) { // need to climb AND have enough APs to get there this turn BOOLEAN fUp = TRUE; @@ -1530,17 +1582,12 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) fUp = FALSE; if (!fUp) - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d, (sniper = %d), is climbing down",pSoldier->ubID,pSoldier->sniper) ); + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d, is climbing down",pSoldier->ubID) ); if ( CanClimbFromHere ( pSoldier, fUp ) ) { if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( AP_CLIMBROOF + MinAPsToAttack( pSoldier, sNoiseGridNo, ADDTURNCOST))) { - if ( pSoldier->bAttitude == DEFENSIVE ) - { - pSoldier->sniper = 1; - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is now a sniper",pSoldier->ubID) ); - } return( AI_ACTION_CLIMB_ROOF ); } } @@ -1637,6 +1684,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) case POINTPATROL: iChance += -10; break; case FARPATROL: break; case SEEKENEMY: iChance += 10; break; + case SNIPER: iChance += -70; break; } // modify chance of patrol (and whether it's a sneaky one) by attitude @@ -1666,7 +1714,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) AIPopMessage(tempstr); #endif - if (fClimb && pSoldier->sniper == 0)//&& pSoldier->usActionData == sClosestFriend) + if ( fClimb )//&& pSoldier->usActionData == sClosestFriend) { // need to climb AND have enough APs to get there this turn BOOLEAN fUp = TRUE; @@ -1674,17 +1722,12 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) fUp = FALSE; if (!fUp) - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d, (sniper = %d), is climbing down",pSoldier->ubID,pSoldier->sniper) ); + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); if ( CanClimbFromHere ( pSoldier, fUp ) ) { if (IsActionAffordable(pSoldier) ) { - if ( pSoldier->bAttitude == DEFENSIVE ) - { - pSoldier->sniper = 1; - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is now a sniper",pSoldier->ubID) ); - } return( AI_ACTION_CLIMB_ROOF ); } } @@ -1731,6 +1774,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) case POINTPATROL: break; case FARPATROL: iChance += -5; break; case SEEKENEMY: iChance += -20; break; + case SNIPER: iChance += 70; break; } // modify chance (and whether it's sneaky) by attitude @@ -1774,7 +1818,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) //////////////////////////////////////////////////////////////////////////// // SWITCH TO GREEN: determine if soldier acts as if nothing at all was wrong //////////////////////////////////////////////////////////////////////////// - if ((INT16)PreRandom(100) < 50 ) + if ((INT16)PreRandom(100) < 50 || pSoldier->bOrders == SNIPER ) { #ifdef RECORDNET fprintf(NetDebugFile,"\tDecideActionYellow: guynum %d ignores noise, switching to GREEN AI...\n",pSoldier->ubID); @@ -2156,6 +2200,9 @@ INT32 iCoverPercentBetter; } } + + + // SNIPER! CheckIfShotPossible(pSoldier,&BestShot,FALSE); @@ -2184,7 +2231,7 @@ INT32 iCoverPercentBetter; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: weapon in slot #%d",BestShot.bWeaponIn)); OBJECTTYPE * gun = &pSoldier->inv[BestShot.bWeaponIn]; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: men in sector %d, ubspotters called by %d, nobody %d",gTacticalStatus.Team[pSoldier->bTeam].bMenInSector,gTacticalStatus.ubSpottersCalledForBy,NOBODY )); - if ( ( IsScoped(gun) && GunRange(gun) > 30 ) && + if ( (( IsScoped(gun) && GunRange(gun) > 30 ) || pSoldier->bOrders == SNIPER ) && (gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) && (gTacticalStatus.ubSpottersCalledForBy == NOBODY)) @@ -2206,7 +2253,7 @@ INT32 iCoverPercentBetter; CheckIfShotPossible(pSoldier,&BestShot,TRUE); //must have a small chance to hit and the opponent must be on the ground (can't suppress guys on the roof) - if ( BestShot.ubPossible && BestShot.ubChanceToReallyHit < 50 && Menptr[BestShot.ubOpponent].bLevel == 0 ) + if ( BestShot.ubPossible && BestShot.ubChanceToReallyHit < 50 && Menptr[BestShot.ubOpponent].bLevel == 0 && pSoldier->bOrders != SNIPER ) { // then do it! @@ -2410,6 +2457,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); case POINTPATROL: iChance += -5; break; case FARPATROL: iChance += -10; break; case SEEKENEMY: iChance += -20; break; + case SNIPER: iChance += -40; break; // Sniper contacts should be reported automatically } // modify base chance according to attitude @@ -2595,6 +2643,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); case FARPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break; case ONCALL: bSeekPts += 0; bHelpPts += +1; bHidePts += -1; bWatchPts += 0; break; case SEEKENEMY: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += -1; break; + case SNIPER: bSeekPts += -3; bHelpPts += -1; bHidePts += +2; bWatchPts += +1; break; } // modify tendencies according to attitude @@ -2612,7 +2661,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); //Madd: make militia less likely to go running headlong into trouble if ( pSoldier->bTeam == MILITIA_TEAM ) { - bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; + bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +0; } } @@ -2641,8 +2690,8 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); // if there is an opponent reachable if (sClosestDisturbance != NOWHERE && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) { - ////////////////////////////////////////////////////////////////////// DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: seek opponent"); + ////////////////////////////////////////////////////////////////////// // SEEK CLOSEST DISTURBANCE: GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT ////////////////////////////////////////////////////////////////////// @@ -2672,7 +2721,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); AIPopMessage(tempstr); #endif - if (fClimb && pSoldier->sniper == 0)//&& pSoldier->usActionData == sClosestDisturbance) + if (fClimb)//&& pSoldier->usActionData == sClosestDisturbance) { // need to climb AND have enough APs to get there this turn BOOLEAN fUp = TRUE; @@ -2680,7 +2729,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); fUp = FALSE; if (!fUp) - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d, (sniper = %d), is climbing down",pSoldier->ubID,pSoldier->sniper) ); + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); if ( CanClimbFromHere ( pSoldier, fUp ) ) { @@ -2888,7 +2937,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); AIPopMessage(tempstr); #endif - if (fClimb && pSoldier->sniper == 0)//&& pSoldier->usActionData == sClosestFriend) + if ( fClimb )//&& pSoldier->usActionData == sClosestFriend) { // need to climb AND have enough APs to get there this turn BOOLEAN fUp = TRUE; @@ -2896,7 +2945,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); fUp = FALSE; if (!fUp) - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d, (sniper = %d), is climbing down",pSoldier->ubID,pSoldier->sniper) ); + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); if ( CanClimbFromHere ( pSoldier, fUp ) ) { @@ -4063,9 +4112,9 @@ bCanAttack = FALSE; // smart guys more likely to try to stay alive, dolts more likely to shoot! - if (pSoldier->bWisdom >= 80) + if (pSoldier->bWisdom >= 50) //Madd: reduced the wisdom required to want to live... iDefense += 10; - else if (pSoldier->bWisdom < 50) + else if (pSoldier->bWisdom < 30) iDefense -= 10; // some orders are more offensive, others more defensive @@ -4073,6 +4122,8 @@ bCanAttack = FALSE; iOffense += 10; else if ((pSoldier->bOrders == STATIONARY) || (pSoldier->bOrders == ONGUARD)) iDefense += 10; + else if (pSoldier->bOrders == SNIPER) + iDefense += 30; switch (pSoldier->bAttitude) { @@ -4407,7 +4458,7 @@ bCanAttack = FALSE; DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( pSoldier, BestAttack.bWeaponIn, FALSE ),pSoldier->bRTPCombat,ubBestAttackAction )); - if (ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE && IsGunBurstCapable( pSoldier, BestAttack.bWeaponIn, FALSE ) && + if (ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE && (Item[pSoldier->inv[BestAttack.bWeaponIn].usItem].usItemClass == IC_LAUNCHER && IsGunBurstCapable( pSoldier, BestAttack.bWeaponIn, FALSE )) && (pSoldier->bTeam != gbPlayerNum || pSoldier->bRTPCombat == RTP_COMBAT_AGGRESSIVE) ) { @@ -4706,6 +4757,7 @@ bCanAttack = FALSE; case POINTPATROL: iChance += -5; break; case FARPATROL: iChance += -10; break; case SEEKENEMY: iChance += -20; break; + case SNIPER: iChance += -80; break; } // modify base chance according to attitude @@ -4787,10 +4839,16 @@ INT8 DecideAction(SOLDIERTYPE *pSoldier) UINT32 uiStartTime, uiEndTime; #endif + if ( !sniperwarning && pSoldier->bOrders == SNIPER ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Watch out for snipers..." ); + sniperwarning = TRUE; + } + if (!biggunwarning && FindRocketLauncherOrCannon(pSoldier) != NO_SLOT ) { biggunwarning = TRUE; - //TODO: does this work ok here? + //TODO: don't say this again after reloading a savegame SayQuoteFromAnyBodyInSector( QUOTE_WEARY_SLASH_SUSPUCIOUS ); } diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index e8cc01383..ac8b840eb 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -95,6 +95,7 @@ typedef enum AI_ACTION_END_COWER_AND_MOVE, // sort of dummy value, special for civilians who are to go somewhere at end of battle AI_ACTION_TRAVERSE_DOWN, // move down a level AI_ACTION_OFFER_SURRENDER, // offer surrender to the player + AI_ACTION_RAISE_GUN, } ActionType; diff --git a/TileEngine/Tile Animation.cpp b/TileEngine/Tile Animation.cpp index 12e80697a..ff83a5dec 100644 --- a/TileEngine/Tile Animation.cpp +++ b/TileEngine/Tile Animation.cpp @@ -316,6 +316,7 @@ void DeleteAniTiles( ) DeleteAniTile( pNode ); } + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("DeleteAniTiles done") ); } @@ -382,7 +383,9 @@ void DeleteAniTile( ANITILE *pAniTile ) } if ( pAniNode->uiFlags & ANITILE_LIGHT && pAniNode->lightSprite >= 0 ) { + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@1 Destroying light sprite %d", pAniNode->lightSprite) ); LightSpriteDestroy(pAniNode->lightSprite); + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@1 Light sprite destroyed") ); } if ( ( pAniNode->uiFlags & ANITILE_CACHEDTILE ) ) @@ -453,7 +456,9 @@ void DeleteAniTile( ANITILE *pAniTile ) } } + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ freeing up animation memory") ); MemFree( pAniNode ); + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ DeleteAniTile: done") ); return; } @@ -613,16 +618,13 @@ void UpdateAniTiles( ) } else { - if ( pNode->uiFlags & ANITILE_LIGHT && pNode->lightSprite >= 0 ) - { - LightSpriteDestroy(pAniNode->lightSprite); - } // Delete from world! DeleteAniTile( pNode ); // Turn back on redunency checks! gTacticalStatus.uiFlags &= (~NOHIDE_REDUNDENCY); + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("UpdateAniTiles: tile deleted, flags updated - done") ); return; } @@ -714,6 +716,7 @@ void UpdateAniTiles( ) { // Delete from world! DeleteAniTile( pNode ); + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("UpdateAniTiles: tile deleted - done") ); return; } diff --git a/TileEngine/physics.cpp b/TileEngine/physics.cpp index b209d5c7a..c791d58c3 100644 --- a/TileEngine/physics.cpp +++ b/TileEngine/physics.cpp @@ -1846,7 +1846,7 @@ DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("physics.cpp line 1750")); fGLauncher = TRUE; sMinRange = MIN_MORTAR_RANGE/2; - if (gGameSettings.fOptions[TOPTION_GL_HIGH_ANGLE]) + if ( (gGameSettings.fOptions[TOPTION_GL_HIGH_ANGLE] && pSoldier->bTeam == OUR_TEAM) || ( pSoldier->bTeam == ENEMY_TEAM && gGameOptions.ubDifficultyLevel >= DIF_LEVEL_HARD ) ) dDegrees *= 2; //fLauncher = TRUE; }