INT16 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT16 sDesGrid, INT8 bReserveAPs, INT8 bAction, INT8 fFlags ) { INT16 sLoop,sAPCost; INT16 sTempDest,sGoToGrid; INT16 sOrigin; UINT16 usMaxDist; UINT8 ubDirection,ubDirsLeft,ubDirChecked[8],fFound = FALSE; INT8 bAPsLeft, fPathFlags; UINT8 ubRoomRequired = 0, ubTempRoom; if ( bReserveAPs == -1 ) { // default reserve points if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { bReserveAPs = 0; } else { bReserveAPs = MAX_AP_CARRIED; } } sTempDest = -1; // obtain maximum roaming distance from soldier's sOrigin usMaxDist = RoamingRange(pSoldier,&sOrigin); if ( pSoldier->bOrders <= CLOSEPATROL && (pSoldier->bTeam == CIV_TEAM || pSoldier->ubProfile != NO_PROFILE ) ) { if ( InARoom( pSoldier->usPatrolGrid[0], &ubRoomRequired ) ) { // make sure this doesn't interfere with pathing for scripts if ( pSoldier->sAbsoluteFinalDestination != NOWHERE ) { ubRoomRequired = 0; } } } pSoldier->usUIMovementMode = DetermineMovementMode(pSoldier, bAction ); if ( pSoldier->usUIMovementMode == RUNNING && fFlags & FLAG_CAUTIOUS ) { pSoldier->usUIMovementMode = WALKING; } #ifdef DEBUGDECISIONS sprintf(tempstr,"%s wants to go towards %d (has range %d)",pSoldier->name,sDesGrid,usMaxDist); AIPopMessage(tempstr); #endif // if soldier is ALREADY at the desired destination, quit right away if (sDesGrid == pSoldier->sGridNo) { return(NOWHERE); } // don't try to approach go after noises or enemies actually in water // would be too easy to throw rocks in water, etc. & distract the AI if (Water(sDesGrid)) { return(NOWHERE); } fPathFlags = 0; if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { /* if ( PythSpacesAway( pSoldier->sGridNo, sDesGrid ) <= PATH_CLOSE_RADIUS ) { // then do a limited range path search and see if we can get there gubNPCDistLimit = 10; if ( !LegalNPCDestination( pSoldier, sDesGrid, ENSURE_PATH, NOWATER, fPathFlags) ) { gubNPCDistLimit = 0; return( NOWHERE ); } else { // allow attempt to path without 'good enough' flag on gubNPCDistLimit = 0; } } else { */ fPathFlags = PATH_CLOSE_GOOD_ENOUGH; //} } // first step: try to find an OK destination at or near the desired gridno if (!LegalNPCDestination(pSoldier,sDesGrid,ENSURE_PATH,NOWATER,fPathFlags)) { #ifdef DEBUGDECISIONS AIPopMessage("destination Grid # itself not valid, looking around it"); #endif if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { // we tried to get close, failed; abort! return( NOWHERE ); } else { // else look at the 8 nearest gridnos to sDesGrid for a valid destination // clear ubDirChecked flag for all 8 directions for (ubDirection = 0; ubDirection < 8; ubDirection++) ubDirChecked[ubDirection] = FALSE; ubDirsLeft = 8; // examine all 8 spots around 'sDesGrid' // keep looking while directions remain and a satisfactory one not found for (ubDirsLeft = 8; ubDirsLeft != 0; ubDirsLeft--) { if (fFound) { break; } // randomly select a direction which hasn't been 'checked' yet do { ubDirection = (UINT8) Random(8); } while (ubDirChecked[ubDirection]); ubDirChecked[ubDirection] = TRUE; // determine the gridno 1 tile away from current friend in this direction sTempDest = NewGridNo(sDesGrid,DirectionInc( (INT16)(ubDirection + 1) )); // if that's out of bounds, ignore it & check next direction if (sTempDest == sDesGrid) continue; if (LegalNPCDestination(pSoldier,sTempDest,ENSURE_PATH,NOWATER,0)) { fFound = TRUE; // found a spot #ifdef DEBUGDECISIONS AINumMessage("Found a spot! ubDirection = ",ubDirection + 1); #endif break; // stop checking in other directions } } if (!fFound) { #ifdef DEBUGDECISIONS AINumMessage("Couldn't find OK destination around grid #",sDesGrid); #endif return(NOWHERE); } // found a good grid #, this becomes our actual desired grid # sDesGrid = sTempDest; } } // HAVE FOUND AN OK destination AND PLOTTED A VALID BEST PATH TO IT #ifdef DEBUGDECISIONS AINumMessage("Chosen legal destination is gridno ",sDesGrid); AINumMessage("Tracing along path, pathRouteToGo = ",pSoldier->pathRouteToGo); #endif sGoToGrid = pSoldier->sGridNo; // start back where soldier is standing now sAPCost = 0; // initialize path cost counter // we'll only go as far along the plotted route as is within our // permitted roaming range, and we'll stop as soon as we're down to <= 5 APs for (sLoop = 0; sLoop < (pSoldier->usPathDataSize - pSoldier->usPathIndex); sLoop++) { // what is the next gridno in the path? //sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->usPathingData[sLoop] + 1) ) ); sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->usPathingData[sLoop]) ) ); //NumMessage("sTempDest = ",sTempDest); // this should NEVER be out of bounds if (sTempDest == sGoToGrid) { #ifdef BETAVERSION sprintf(tempstr,"GoAsFarAsPossibleTowards: ERROR - gridno along valid route is invalid! guynum %d, sTempDest = %d",pSoldier->ubID,sTempDest); #ifdef RECORDNET fprintf(NetDebugFile,"\n\t%s\n",tempstr); #endif PopMessage(tempstr); SaveGame(ERROR_SAVE); #endif break; // quit here, sGoToGrid is where we are going } // if this takes us beyond our permitted "roaming range" if (SpacesAway(sOrigin,sTempDest) > usMaxDist) break; // quit here, sGoToGrid is where we are going if ( ubRoomRequired ) { if ( !( InARoom( sTempDest, &ubTempRoom ) && ubTempRoom == ubRoomRequired ) ) { // quit here, limited by room! break; } } if ( (fFlags & FLAG_STOPSHORT) && SpacesAway( sDesGrid, sTempDest ) <= STOPSHORTDIST ) { break; // quit here, sGoToGrid is where we are going } // if this gridno is NOT a legal NPC destination // DONT'T test path again - that would replace the traced path! - Ian // NOTE: It's OK to go *THROUGH* water to try and get to the destination! if (!LegalNPCDestination(pSoldier,sTempDest,IGNORE_PATH,WATEROK,0)) break; // quit here, sGoToGrid is where we are going // CAN'T CALL PathCost() HERE! IT CALLS findBestPath() and overwrites // pathRouteToGo !!! Gotta calculate the cost ourselves - Ian // //ubAPsLeft = pSoldier->bActionPoints - PathCost(pSoldier,sTempDest,FALSE,FALSE,FALSE,FALSE,FALSE); if (gfTurnBasedAI) { // if we're just starting the "costing" process (first gridno) if (sLoop == 0) { /* // first, add any additional costs - such as intermediate animations, etc. switch(pSoldier->anitype[pSoldier->anim]) { // in theory, no NPC should ever be in one of these animations as // things stand (they don't medic anyone), but leave it for robustness case START_AID : case GIVING_AID : sAnimCost = AP_STOP_FIRST_AID; break; case TWISTOMACH : case COLLAPSED : sAnimCost = AP_GET_UP; break; case TWISTBACK : case UNCONSCIOUS : sAnimCost = (AP_ROLL_OVER + AP_GET_UP); break; default : sAnimCost = 0; } // this is our first cost sAPCost += sAnimCost; */ if (pSoldier->usUIMovementMode == RUNNING) { sAPCost += AP_START_RUN_COST; } } // ATE: Direction here? sAPCost += EstimateActionPointCost( pSoldier, sTempDest, (INT8) pSoldier->usPathingData[sLoop], pSoldier->usUIMovementMode, (INT8) sLoop, (INT8) pSoldier->usPathDataSize ); bAPsLeft = pSoldier->bActionPoints - sAPCost; } // if after this, we have <= 5 APs remaining, that's far enough, break out // (the idea is to preserve APs so we can crouch or react if // necessary, and benefit from the carry-over next turn if not needed) // This routine is NOT used by any GREEN AI, so such caution is warranted! if ( gfTurnBasedAI && (bAPsLeft < bReserveAPs) ) break; else { sGoToGrid = sTempDest; // we're OK up to here // if exactly 5 APs left, don't bother checking any further if ( gfTurnBasedAI && (bAPsLeft == bReserveAPs) ) break; } } // if it turned out we couldn't go even 1 tile towards the desired gridno if (sGoToGrid == pSoldier->sGridNo) { #ifdef DEBUGDECISIONS sprintf(tempstr,"%s will go NOWHERE, path doesn't meet criteria",pSoldier->name); AIPopMessage(tempstr); #endif return(NOWHERE); // then go nowhere } else { // possible optimization - stored path IS good if we're going all the way if (sGoToGrid == sDesGrid) { pSoldier->bPathStored = TRUE; pSoldier->sFinalDestination = sGoToGrid; } else if ( pSoldier->usPathIndex == 0 ) { // we can hack this surely! -- CJC pSoldier->bPathStored = TRUE; pSoldier->sFinalDestination = sGoToGrid; pSoldier->usPathDataSize = sLoop + 1; } #ifdef DEBUGDECISIONS ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"%d to %d with %d APs left", pSoldier->ubID, sGoToGrid, pSoldier->bActionPoints ); #endif return( sGoToGrid ); } }
INT32 SkillCheck( SOLDIERTYPE * pSoldier, INT8 bReason, INT8 bChanceMod ) { INT32 iSkill; INT32 iChance, iReportChance; INT32 iRoll, iMadeItBy; INT8 bSlot; INT32 iLoop; SOLDIERTYPE * pTeamSoldier; INT8 bBuddyIndex; BOOLEAN fForceDamnSound = FALSE; iReportChance = -1; switch (bReason) { case LOCKPICKING_CHECK: case ELECTRONIC_LOCKPICKING_CHECK: fForceDamnSound = TRUE; iSkill = EffectiveMechanical( pSoldier ); if (iSkill == 0) { break; } // adjust skill based on wisdom (knowledge) iSkill = iSkill * (EffectiveWisdom( pSoldier ) + 100) / 200; // and dexterity (clumsy?) iSkill = iSkill * (EffectiveDexterity( pSoldier ) + 100) / 200; // factor in experience iSkill = iSkill + EffectiveExpLevel( pSoldier ) * 3; if (HAS_SKILL_TRAIT( pSoldier, LOCKPICKING ) ) { // if we specialize in picking locks... iSkill += gbSkillTraitBonus[LOCKPICKING] * NUM_SKILL_TRAITS( pSoldier, LOCKPICKING ); } if (bReason == ELECTRONIC_LOCKPICKING_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS)) ) { // if we are unfamiliar with electronics... iSkill /= 2; } // adjust chance based on status of kit bSlot = FindObj( pSoldier, LOCKSMITHKIT ); if (bSlot == NO_SLOT) { // this should never happen, but might as well check... iSkill = 0; } iSkill = iSkill * pSoldier->inv[bSlot].bStatus[0] / 100; break; case ATTACHING_DETONATOR_CHECK: case ATTACHING_REMOTE_DETONATOR_CHECK: iSkill = EffectiveExplosive( pSoldier ); if (iSkill == 0) { break; } iSkill = (iSkill * 3 + EffectiveDexterity( pSoldier ) ) / 4; if ( bReason == ATTACHING_REMOTE_DETONATOR_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS )) ) { iSkill /= 2; } break; case PLANTING_BOMB_CHECK: case PLANTING_REMOTE_BOMB_CHECK: iSkill = EffectiveExplosive( pSoldier ) * 7; iSkill += EffectiveWisdom( pSoldier ) * 2; iSkill += EffectiveExpLevel( pSoldier ) * 10; iSkill = iSkill / 10; // bring the value down to a percentage if ( bReason == PLANTING_REMOTE_BOMB_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS)) ) { // deduct only a bit... iSkill = (iSkill * 3) / 4; } // Ok, this is really damn easy, so skew the values... // e.g. if calculated skill is 84, skewed up to 96 // 51 to 84 // 22 stays as is iSkill = (iSkill + 100 * (iSkill / 25) ) / (iSkill / 25 + 1); break; case DISARM_TRAP_CHECK: fForceDamnSound = TRUE; iSkill = EffectiveExplosive( pSoldier ) * 7; if ( iSkill == 0 ) { break; } iSkill += EffectiveDexterity( pSoldier ) * 2; iSkill += EffectiveExpLevel( pSoldier ) * 10; iSkill = iSkill / 10; // bring the value down to a percentage // penalty based on poor wisdom iSkill -= (100 - EffectiveWisdom( pSoldier ) ) / 5; break; case DISARM_ELECTRONIC_TRAP_CHECK: fForceDamnSound = TRUE; iSkill = __max( EffectiveMechanical( pSoldier ) , EffectiveExplosive( pSoldier ) ) * 7; if ( iSkill == 0 ) { break; } iSkill += EffectiveDexterity( pSoldier ) * 2; iSkill += EffectiveExpLevel( pSoldier ) * 10; iSkill = iSkill / 10; // bring the value down to a percentage // penalty based on poor wisdom iSkill -= (100 - EffectiveWisdom( pSoldier ) ) / 5; if ( !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS )) ) { iSkill = (iSkill * 3) / 4; } break; case OPEN_WITH_CROWBAR: // Add for crowbar... iSkill = EffectiveStrength( pSoldier ) + 20; fForceDamnSound = TRUE; break; case SMASH_DOOR_CHECK: iSkill = EffectiveStrength( pSoldier ); break; case UNJAM_GUN_CHECK: iSkill = 30 + EffectiveMechanical( pSoldier ) / 2; break; case NOTICE_DART_CHECK: // only a max of ~20% chance iSkill = EffectiveWisdom( pSoldier ) / 10 + EffectiveExpLevel( pSoldier ); break; case LIE_TO_QUEEN_CHECK: // competitive check vs the queen's wisdom and leadership... poor guy! iSkill = 50 * ( EffectiveWisdom( pSoldier ) + EffectiveLeadership( pSoldier ) ) / ( gMercProfiles[ QUEEN ].bWisdom + gMercProfiles[ QUEEN ].bLeadership ); break; case ATTACHING_SPECIAL_ITEM_CHECK: case ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK: iSkill = EffectiveMechanical( pSoldier ); if (iSkill == 0) { break; } // adjust skill based on wisdom (knowledge) iSkill = iSkill * (EffectiveWisdom( pSoldier ) + 100) / 200; // and dexterity (clumsy?) iSkill = iSkill * (EffectiveDexterity( pSoldier ) + 100) / 200; // factor in experience iSkill = iSkill + EffectiveExpLevel( pSoldier ) * 3; if (bReason == ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS)) ) { // if we are unfamiliar with electronics... iSkill /= 2; } break; default: iSkill = 0; break; } iSkill -= GetSkillCheckPenaltyForFatigue( pSoldier, iSkill ); iChance = iSkill + bChanceMod; switch (bReason) { case LOCKPICKING_CHECK: case ELECTRONIC_LOCKPICKING_CHECK: case DISARM_TRAP_CHECK: case DISARM_ELECTRONIC_TRAP_CHECK: case OPEN_WITH_CROWBAR: case SMASH_DOOR_CHECK: case ATTACHING_SPECIAL_ITEM_CHECK: case ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK: // for lockpicking and smashing locks, if the chance isn't reasonable // we set it to 0 so they can never get through the door if they aren't // good enough if (iChance < 30) { iChance = 0; break; } // else fall through default: iChance += GetMoraleModifier( pSoldier ); break; } if (iChance > 99) { iChance = 99; } else if (iChance < 0) { iChance = 0; } iRoll = PreRandom( 100 ); iMadeItBy = iChance - iRoll; if (iMadeItBy < 0) { if ( (pSoldier->bLastSkillCheck == bReason) && (pSoldier->sGridNo == pSoldier->sSkillCheckGridNo) ) { pSoldier->ubSkillCheckAttempts++; if (pSoldier->ubSkillCheckAttempts > 2) { if (iChance == 0) { // do we realize that we just can't do this? if ( (100 - (pSoldier->ubSkillCheckAttempts - 2) * 20) < EffectiveWisdom( pSoldier ) ) { // say "I can't do this" quote TacticalCharacterDialogue( pSoldier, QUOTE_DEFINITE_CANT_DO ); return( iMadeItBy ); } } } } else { pSoldier->bLastSkillCheck = bReason; pSoldier->ubSkillCheckAttempts = 1; pSoldier->sSkillCheckGridNo = pSoldier->sGridNo; } if ( fForceDamnSound || Random( 100 ) < 40 ) { switch( bReason ) { case UNJAM_GUN_CHECK: case NOTICE_DART_CHECK: case LIE_TO_QUEEN_CHECK: // silent check break; default: DoMercBattleSound( pSoldier, BATTLE_SOUND_CURSE1 ); break; } } } else { // A buddy might make a positive comment based on our success; // Increase the chance for people with higher skill and for more difficult tasks iChance = 15 + iSkill / 20 + (-bChanceMod) / 20; if (iRoll < iChance) { // If a buddy of this merc is standing around nearby, they'll make a positive comment. iLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; for ( pTeamSoldier = MercPtrs[ iLoop ]; iLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; iLoop++,pTeamSoldier++ ) { if ( OK_INSECTOR_MERC( pTeamSoldier ) ) { bBuddyIndex = WhichBuddy( pTeamSoldier->ubProfile, pSoldier->ubProfile ); if (bBuddyIndex >= 0 && SpacesAway( pSoldier->sGridNo, pTeamSoldier->sGridNo ) < 15) { switch( bBuddyIndex ) { case 0: // buddy #1 did something good! TacticalCharacterDialogue( pTeamSoldier, QUOTE_BUDDY_1_GOOD ); break; case 1: // buddy #2 did something good! TacticalCharacterDialogue( pTeamSoldier, QUOTE_BUDDY_2_GOOD ); break; case 2: // learn to like buddy did something good! TacticalCharacterDialogue( pTeamSoldier, QUOTE_LEARNED_TO_LIKE_WITNESSED ); break; default: break; } } } } } } return( iMadeItBy ); }
int LegalNPCDestination(SOLDIERTYPE *pSoldier, INT16 sGridno, UINT8 ubPathMode, UINT8 ubWaterOK, UINT8 fFlags) { BOOLEAN fSkipTilesWithMercs; if ((sGridno < 0) || (sGridno >= GRIDSIZE)) { #ifdef RECORDNET fprintf(NetDebugFile,"LegalNPC->sDestination: ERROR - rcvd invalid gridno %d",gridno); #endif #ifdef BETAVERSION NumMessage("LegalNPC->sDestination: ERROR - rcvd invalid gridno ",gridno); #endif return(FALSE); } // return false if gridno on different level from merc if ( GridNoOnVisibleWorldTile( pSoldier->sGridNo ) && gpWorldLevelData[ pSoldier->sGridNo ].sHeight != gpWorldLevelData[ sGridno ].sHeight ) { return( FALSE ); } // skip mercs if turnbased and adjacent AND not doing an IGNORE_PATH check (which is used almost exclusively by GoAsFarAsPossibleTowards) fSkipTilesWithMercs = (gfTurnBasedAI && ubPathMode != IGNORE_PATH && SpacesAway( pSoldier->sGridNo, sGridno ) == 1 ); // if this gridno is an OK destination // AND the gridno is NOT in a tear-gassed tile when we have no gas mask // AND someone is NOT already standing there // AND we're NOT already standing at that gridno // AND the gridno hasn't been black-listed for us // Nov 28 98: skip people in destination tile if in turnbased if ( ( NewOKDestination(pSoldier, sGridno, fSkipTilesWithMercs, pSoldier->bLevel ) ) && ( !InGas( pSoldier, sGridno ) ) && ( sGridno != pSoldier->sGridNo ) && ( sGridno != pSoldier->sBlackList ) ) /* if ( ( NewOKDestination(pSoldier, sGridno, FALSE, pSoldier->bLevel ) ) && ( !(gpWorldLevelData[ sGridno ].ubExtFlags[0] & (MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS)) || ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK || pSoldier->inv[ HEAD2POS ].usItem == GASMASK ) ) && ( sGridno != pSoldier->sGridNo ) && ( sGridno != pSoldier->sBlackList ) )*/ /* if ( ( NewOKDestination(pSoldier,sGridno,ALLPEOPLE, pSoldier->bLevel ) ) && ( !(gpWorldLevelData[ sGridno ].ubExtFlags[0] & (MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS)) || ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK || pSoldier->inv[ HEAD2POS ].usItem == GASMASK ) ) && ( sGridno != pSoldier->sGridNo ) && ( sGridno != pSoldier->sBlackList ) ) */ { // if water's a problem, and gridno is in a water tile (bridges are OK) if (!ubWaterOK && Water(sGridno)) return(FALSE); // passed all checks, now try to make sure we can get there! switch (ubPathMode) { // if finding a path wasn't asked for (could have already been done, // for example), don't bother case IGNORE_PATH : return(TRUE); case ENSURE_PATH : if ( FindBestPath( pSoldier, sGridno, pSoldier->bLevel, WALKING, COPYROUTE, fFlags ) ) { return(TRUE); // legal destination } else // got this far, but found no clear path, { // so test fails return(FALSE); } // *** NOTE: movement mode hardcoded to WALKING !!!!! case ENSURE_PATH_COST: return(PlotPath(pSoldier,sGridno,FALSE,FALSE,FALSE,WALKING,FALSE,FALSE,0)); default : #ifdef BETAVERSION NumMessage("LegalNPC->sDestination: ERROR - illegal pathMode = ",ubPathMode); #endif return(FALSE); } } else // something failed - didn't even have to test path return(FALSE); // illegal destination }
INT16 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN * pfClimbingNecessary, BOOLEAN * pfReachable ) { UINT32 uiLoop; INT8 * pbPersOL, * pbPublOL; INT16 *psLastLoc,*psNoiseGridNo; INT8 * pbNoiseLevel; INT8 *pbLastLevel; UINT8 *pubNoiseVolume; INT32 iDistAway; INT32 iNoiseValue, iBestValue = -10000; INT16 sBestGridNo = NOWHERE; INT8 bBestLevel = 0; INT16 sClimbingGridNo; BOOLEAN fClimbingNecessary = FALSE; SOLDIERTYPE * pTemp; pubNoiseVolume = &gubPublicNoiseVolume[pSoldier->bTeam]; psNoiseGridNo = &gsPublicNoiseGridno[pSoldier->bTeam]; pbNoiseLevel = &gbPublicNoiseLevel[pSoldier->bTeam]; psLastLoc = gsLastKnownOppLoc[pSoldier->ubID]; // hang pointers at start of this guy's personal and public opponent opplists pbPersOL = pSoldier->bOppList; pbPublOL = gbPublicOpplist[pSoldier->bTeam]; // look through this man's personal & public opplists for opponents heard for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++) { pTemp = MercSlots[ uiLoop ]; // if this merc is inactive, at base, on assignment, or dead if (!pTemp || !pTemp->bLife) continue; // next merc // if this merc is neutral/on same side, he's not an opponent if ( CONSIDERED_NEUTRAL( pSoldier, pTemp ) || (pSoldier->bSide == pTemp->bSide)) continue; // next merc pbPersOL = pSoldier->bOppList + pTemp->ubID; pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pTemp->ubID; psLastLoc = gsLastKnownOppLoc[pSoldier->ubID] + pTemp->ubID; pbLastLevel = gbLastKnownOppLevel[pSoldier->ubID] + pTemp->ubID; // if this guy's been personally heard within last 3 turns if (*pbPersOL < NOT_HEARD_OR_SEEN) { // calculate how far this noise was, and its relative "importance" iDistAway = SpacesAway(pSoldier->sGridNo,*psLastLoc); iNoiseValue = (*pbPersOL) * iDistAway; // always a negative number! if (iNoiseValue > iBestValue) { iBestValue = iNoiseValue; sBestGridNo = *psLastLoc; bBestLevel = *pbLastLevel; } } // if this guy's been publicly heard within last 3 turns if (*pbPublOL < NOT_HEARD_OR_SEEN) { // calculate how far this noise was, and its relative "importance" iDistAway = SpacesAway(pSoldier->sGridNo,gsPublicLastKnownOppLoc[pSoldier->bTeam][pTemp->ubID]); iNoiseValue = (*pbPublOL) * iDistAway; // always a negative number! if (iNoiseValue > iBestValue) { iBestValue = iNoiseValue; sBestGridNo = gsPublicLastKnownOppLoc[pSoldier->bTeam][pTemp->ubID]; bBestLevel = gbPublicLastKnownOppLevel[pSoldier->bTeam][pTemp->ubID]; } } } // if any "misc. noise" was also heard recently if (pSoldier->sNoiseGridno != NOWHERE) { if ( pSoldier->bNoiseLevel != pSoldier->bLevel || PythSpacesAway( pSoldier->sGridNo, pSoldier->sNoiseGridno ) >= 6 || SoldierTo3DLocationLineOfSightTest( pSoldier, pSoldier->sNoiseGridno, pSoldier->bNoiseLevel, 0, (UINT8) MaxDistanceVisible(), FALSE ) == 0 ) { // calculate how far this noise was, and its relative "importance" iDistAway = SpacesAway(pSoldier->sGridNo,pSoldier->sNoiseGridno); iNoiseValue = ((pSoldier->ubNoiseVolume / 2) - 6) * iDistAway; if (iNoiseValue > iBestValue) { iBestValue = iNoiseValue; sBestGridNo = pSoldier->sNoiseGridno; bBestLevel = pSoldier->bNoiseLevel; } } else { // we are there or near pSoldier->sNoiseGridno = NOWHERE; // wipe it out, not useful anymore pSoldier->ubNoiseVolume = 0; } } // if any recent PUBLIC "misc. noise" is also known if ( (pSoldier->bTeam != CIV_TEAM) || ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) ) { if (*psNoiseGridNo != NOWHERE) { // if we are NOT there (at the noise gridno) if ( *pbNoiseLevel != pSoldier->bLevel || PythSpacesAway( pSoldier->sGridNo, *psNoiseGridNo ) >= 6 || SoldierTo3DLocationLineOfSightTest( pSoldier, *psNoiseGridNo, *pbNoiseLevel, 0, (UINT8) MaxDistanceVisible(), FALSE ) == 0 ) { // calculate how far this noise was, and its relative "importance" iDistAway = SpacesAway(pSoldier->sGridNo,*psNoiseGridNo); iNoiseValue = ((*pubNoiseVolume / 2) - 6) * iDistAway; if (iNoiseValue > iBestValue) { iBestValue = iNoiseValue; sBestGridNo = *psNoiseGridNo; bBestLevel = *pbNoiseLevel; } } } } if (sBestGridNo != NOWHERE && pfReachable ) { *pfReachable = TRUE; // make civs not walk to noises outside their room if on close patrol/onguard if ( pSoldier->bOrders <= CLOSEPATROL && (pSoldier->bTeam == CIV_TEAM || pSoldier->ubProfile != NO_PROFILE ) ) { UINT8 ubRoom, ubNewRoom; // any other combo uses the default of ubRoom == 0, set above if ( InARoom( pSoldier->usPatrolGrid[0], &ubRoom ) ) { if ( !InARoom( pSoldier->usPatrolGrid[0], &ubNewRoom ) || ubRoom != ubNewRoom ) { *pfReachable = FALSE; } } } if ( *pfReachable ) { // if there is a climb involved then we should store the location // of where we have to climb to instead sClimbingGridNo = GetInterveningClimbingLocation( pSoldier, sBestGridNo, bBestLevel, &fClimbingNecessary ); if ( fClimbingNecessary ) { if ( sClimbingGridNo == NOWHERE ) { // can't investigate! *pfReachable = FALSE; } else { sBestGridNo = sClimbingGridNo; fClimbingNecessary = TRUE; } } else { fClimbingNecessary = FALSE; } } } if ( piRetValue ) { *piRetValue = iBestValue; } if ( pfClimbingNecessary ) { *pfClimbingNecessary = fClimbingNecessary; } #ifdef DEBUGDECISIONS if (sBestGridNo != NOWHERE) AINumMessage("MOST IMPORTANT NOISE HEARD FROM GRID #",sBestGridNo); #endif return(sBestGridNo); }