UINT16 RealtimeDelay( SOLDIERTYPE * pSoldier ) { if ( PTR_CIV_OR_MILITIA && !(pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) ) { return( (UINT16) REALTIME_CIV_AI_DELAY ); } else if ( CREATURE_OR_BLOODCAT( pSoldier ) && !( pSoldier->aiData.bHunting ) ) { return( (UINT16) REALTIME_CREATURE_AI_DELAY ); } else { if ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) { //DBrot: More Rooms //UINT8 ubRoom; UINT16 usRoom; if ( InARoom( pSoldier->sGridNo, &usRoom ) && IN_BROTHEL( usRoom ) ) { return( (UINT16) (REALTIME_AI_DELAY / 3) ); } } return( (UINT16) REALTIME_AI_DELAY ); } }
void BoxingMovementCheck( SOLDIERTYPE * pSoldier ) { //DBrot: More Rooms //UINT8 ubRoom; UINT16 usRoom; if ( InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING) { // someone moving in/into the ring CountPeopleInBoxingRingAndDoActions(); } else if ( ( gTacticalStatus.bBoxingState == BOXING ) && ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) ) { // boxer stepped out of the ring! BoxingPlayerDisqualified( pSoldier, BOXER_OUT_OF_RING ); // add the history record here. AddHistoryToPlayersLog( HISTORY_DISQUALIFIED_BOXING, pSoldier->ubProfile, GetWorldTotalMin(), gWorldSectorX, gWorldSectorY ); // make not a boxer any more pSoldier->DeleteBoxingFlag( ); pSoldier->flags.uiStatusFlags &= (~SOLDIER_PCUNDERAICONTROL); } }
UINT8 CountPeopleInBoxingRing( void ) { SOLDIERTYPE * pSoldier; UINT32 uiLoop; //DBrot: More Rooms //UINT8 ubRoom; UINT16 usRoom; UINT8 ubTotalInRing = 0; for ( uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop ) { pSoldier = MercSlots[ uiLoop ]; if ( pSoldier != NULL ) { if ( InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING) { ++ubTotalInRing; } } } return( ubTotalInRing ); }
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 ); } }
void ExitBoxing( void ) { //DBrot: More Rooms //UINT8 ubRoom; UINT16 usRoom; SOLDIERTYPE* pSoldier; UINT32 uiLoop; UINT8 ubPass; // find boxers and turn them neutral again // first time through loop, look for AI guy, then for PC guy.... for stupid // oppcnt/alert status reasons for( ubPass = 0; ubPass < 2; ++ubPass ) { // because boxer could die, loop through all soldier ptrs for ( uiLoop = 0; uiLoop < gTacticalStatus.Team[ CIV_TEAM ].bLastID; ++uiLoop ) { pSoldier = MercPtrs[ uiLoop ]; if ( pSoldier != NULL ) { if ( ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) && InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING ) { if ( pSoldier->flags.uiStatusFlags & SOLDIER_PC ) { if ( ubPass == 0 ) // pass 0, only handle AI { continue; } // put guy under AI control temporarily pSoldier->flags.uiStatusFlags |= SOLDIER_PCUNDERAICONTROL; //SB: this flag don't allow merc leave the ring pSoldier->flags.uiStatusFlags &= ~SOLDIER_ENGAGEDINACTION; } else { if ( ubPass == 1 ) // pass 1, only handle PCs { continue; } // reset AI boxer to neutral SetSoldierNeutral( pSoldier ); RecalculateOppCntsDueToBecomingNeutral( pSoldier ); } CancelAIAction( pSoldier, TRUE ); pSoldier->aiData.bAlertStatus = STATUS_GREEN; pSoldier->aiData.bUnderFire = 0; // HEADROCK HAM 3.6: Make sure all boxers' APs have been reset to a reasonable number. Otherwise, // the AI combatant may fail several conditions in subsequent functions, and fail to leave the ring // as a result. if (pSoldier->bActionPoints < (APBPConstants[AP_MAXIMUM]*6)/10) { pSoldier->bActionPoints = (APBPConstants[AP_MAXIMUM]*6)/10; } // if necessary, revive boxer so he can leave ring if (pSoldier->stats.bLife > 0 && (pSoldier->stats.bLife < OKLIFE || pSoldier->bBreath < OKBREATH ) ) { pSoldier->stats.bLife = __max( OKLIFE * 2, pSoldier->stats.bLife ); if (pSoldier->bBreath < 100) { // deduct -ve BPs to grant some BPs back (properly) DeductPoints( pSoldier, 0, (INT16) - ( (100 - pSoldier->bBreath) * 100 ) ); } pSoldier->BeginSoldierGetup( ); } } } } } DeleteTalkingMenu(); EndAllAITurns(); if ( CheckForEndOfCombatMode( FALSE ) ) { EndTopMessage(); #ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalNothing[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalNothing[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_NOTHING, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalNothing[gbWorldSectorZ] ); else #endif SetMusicMode( MUSIC_TACTICAL_NOTHING ); // Lock UI until we get out of the ring guiPendingOverrideEvent = LU_BEGINUILOCK; } }
void CountPeopleInBoxingRingAndDoActions( void ) { UINT32 uiLoop; UINT8 ubTotalInRing = 0; //DBrot: More Rooms UINT16 usRoom; UINT16 ubPlayersInRing = 0; SOLDIERTYPE * pSoldier; SOLDIERTYPE * pInRing[2] = { NULL, NULL }; SOLDIERTYPE * pNonBoxingPlayer = NULL; for ( uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop ) { pSoldier = MercSlots[ uiLoop ]; if ( pSoldier != NULL ) { if ( InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING) { if ( ubTotalInRing < 2 ) { pInRing[ ubTotalInRing ] = pSoldier; } ++ubTotalInRing; if ( pSoldier->flags.uiStatusFlags & SOLDIER_PC ) { ++ubPlayersInRing; if ( !pNonBoxingPlayer && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) { pNonBoxingPlayer = pSoldier; } } } } } if ( ubPlayersInRing > 1 ) { // boxing match just became invalid! if ( gTacticalStatus.bBoxingState <= PRE_BOXING ) { BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING ); // set to not in boxing or it won't be handled otherwise SetBoxingState( NOT_BOXING ); } else { BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING ); } return; } if ( gTacticalStatus.bBoxingState == BOXING_WAITING_FOR_PLAYER ) { if ( ubTotalInRing == 1 && ubPlayersInRing == 1 ) { // time to go to pre-boxing SetBoxingState( PRE_BOXING ); PickABoxer(); } } else { // if pre-boxing, check for two people (from different teams!) in the ring if (gTacticalStatus.bBoxingState == PRE_BOXING) { if (ubTotalInRing == 2 && ubPlayersInRing == 1) { // ladieees and gennleman, we have a fight! for (uiLoop = 0; uiLoop < 2; ++uiLoop) { if (!(pInRing[uiLoop]->flags.uiStatusFlags & SOLDIER_BOXER)) { // set as boxer! pInRing[uiLoop]->flags.uiStatusFlags |= SOLDIER_BOXER; } } // start match! SetBoxingState(BOXING); gfLastBoxingMatchWonByPlayer = FALSE; #ifdef JA2TESTVERSION ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Boxer APs %d %d", pInRing[0]->bActionPoints, pInRing[1]->bActionPoints ); #endif // give the first turn to a randomly chosen boxer EnterCombatMode(pInRing[Random(2)]->bTeam); } } /* else { // check to see if the player has more than one person in the ring if ( ubPlayersInRing > 1 ) { // boxing match just became invalid! BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING ); return; } } */ } }
UINT16 FindRandomGridNoFromSweetSpot( SOLDIERTYPE *pSoldier, INT16 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT16 sX, sY; INT16 sGridNo; INT32 leftmost; BOOLEAN fFound = FALSE; UINT32 cnt = 0; SOLDIERTYPE soldier; UINT8 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; UINT8 ubBestDirection=0; INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2; UINT8 ubRoomNum; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. memset( &soldier, 0, sizeof( SOLDIERTYPE ) ); soldier.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) ); do { sX = (UINT16)Random( ubRadius ); sY = (UINT16)Random( ubRadius ); leftmost = ( ( sSweetGridNo + ( WORLD_COLS * sY ) )/ WORLD_COLS ) * WORLD_COLS; sGridNo = sSweetGridNo + ( WORLD_COLS * sY ) + sX; if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) && gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE , pSoldier->bLevel) ) { // If we are a crow, we need this additional check if ( pSoldier->ubBodyType == CROW ) { if ( !InARoom( sGridNo, &ubRoomNum ) ) { fFound = TRUE; } } else { fFound = TRUE; } } } cnt++; if ( cnt > 2000 ) { return( NOWHERE ); } } while( !fFound ); // Set direction to center of map! *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) ); gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; return( sGridNo ); }
void RTHandleAI( SOLDIERTYPE * pSoldier ) { #ifdef AI_PROFILING INT32 iLoop; #endif if ((pSoldier->aiData.bAction != AI_ACTION_NONE) && pSoldier->aiData.bActionInProgress) { // if action should remain in progress if (ActionInProgress(pSoldier)) { #ifdef DEBUGBUSY AINumMessage("Busy with action, skipping guy#",pSoldier->ubID); #endif // let it continue return; } } // Flugente: prisoners of war don't do anything if ( pSoldier->usSoldierFlagMask & SOLDIER_POW ) return; // if man has nothing to do if (pSoldier->aiData.bAction == AI_ACTION_NONE) { if (pSoldier->aiData.bNextAction == AI_ACTION_NONE) { // make sure this flag is turned off (it already should be!) pSoldier->aiData.bActionInProgress = FALSE; // truly nothing to do! RefreshAI( pSoldier ); } // Since we're NEVER going to "continue" along an old path at this point, // then it would be nice place to reinitialize "pathStored" flag for // insurance purposes. // // The "pathStored" variable controls whether it's necessary to call // findNewPath() after you've called NewDest(). Since the AI calls // findNewPath() itself, a speed gain can be obtained by avoiding // redundancy. // // The "normal" way for pathStored to be reset is inside // SetNewCourse() [which gets called after NewDest()]. // // The only reason we would NEED to reinitialize it here is if I've // incorrectly set pathStored to TRUE in a process that doesn't end up // calling NewDest() pSoldier->pathing.bPathStored = FALSE; // decide on the next action #ifdef AI_PROFILING for (iLoop = 0; iLoop < 1000; iLoop++) #endif { if (pSoldier->aiData.bNextAction != AI_ACTION_NONE) { if ( pSoldier->aiData.bNextAction == AI_ACTION_END_COWER_AND_MOVE ) { if ( pSoldier->flags.uiStatusFlags & SOLDIER_COWERING ) { pSoldier->aiData.bAction = AI_ACTION_STOP_COWERING; pSoldier->aiData.usActionData = ANIM_STAND; } else if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight < ANIM_STAND ) { // stand up! pSoldier->aiData.bAction = AI_ACTION_CHANGE_STANCE; pSoldier->aiData.usActionData = ANIM_STAND; } else { pSoldier->aiData.bAction = AI_ACTION_NONE; } if ( pSoldier->sGridNo == pSoldier->aiData.usNextActionData ) { // no need to walk after this pSoldier->aiData.bNextAction = AI_ACTION_NONE; pSoldier->aiData.usNextActionData = NOWHERE; } else { pSoldier->aiData.bNextAction = AI_ACTION_WALK; // leave next-action-data as is since that's where we want to go } } else { // do the next thing we have to do... pSoldier->aiData.bAction = pSoldier->aiData.bNextAction; pSoldier->aiData.usActionData = pSoldier->aiData.usNextActionData; pSoldier->bTargetLevel = pSoldier->aiData.bNextTargetLevel; pSoldier->aiData.bNextAction = AI_ACTION_NONE; pSoldier->aiData.usNextActionData = 0; pSoldier->aiData.bNextTargetLevel = 0; } if (pSoldier->aiData.bAction == AI_ACTION_PICKUP_ITEM) { // the item pool index was stored in the special data field pSoldier->aiData.uiPendingActionData1 = pSoldier->iNextActionSpecialData; } } else if (!TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination)) { if ( ACTING_ON_SCHEDULE( pSoldier ) ) { pSoldier->aiData.bAction = AI_ACTION_SCHEDULE_MOVE; } else { pSoldier->aiData.bAction = AI_ACTION_WALK; } pSoldier->aiData.usActionData = pSoldier->sAbsoluteFinalDestination; } else { if (!(gTacticalStatus.uiFlags & ENGAGED_IN_CONV)) { if(!pSoldier->ai_masterplan_) // if the Soldier has no plan, create one { if(pSoldier->bAIIndex == 0) // not yet initialized, use bTeam+1 as default pSoldier->bAIIndex = pSoldier->bTeam + 1; AI::tactical::AIInputData ai_input; AI::tactical::PlanFactoryLibrary* plan_lib(AI::tactical::PlanFactoryLibrary::instance()); pSoldier->ai_masterplan_ = plan_lib->create_plan(pSoldier->bAIIndex, pSoldier, ai_input); } AI::tactical::PlanInputData plan_input(false, gTacticalStatus); pSoldier->ai_masterplan_->execute(plan_input); } } } // if he chose to continue doing nothing if (pSoldier->aiData.bAction == AI_ACTION_NONE) { #ifdef RECORDNET fprintf(NetDebugFile,"\tMOVED BECOMING TRUE: Chose to do nothing, guynum %d\n",pSoldier->ubID); #endif // do a standard wait before doing anything else! pSoldier->aiData.bAction = AI_ACTION_WAIT; //if (PTR_CIVILIAN && pSoldier->aiData.bAlertStatus != STATUS_BLACK) if ( PTR_CIV_OR_MILITIA && !(pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) ) { pSoldier->aiData.usActionData = (UINT16) REALTIME_CIV_AI_DELAY; } else if ( CREATURE_OR_BLOODCAT( pSoldier ) && !( pSoldier->aiData.bHunting ) ) { pSoldier->aiData.usActionData = (UINT16) REALTIME_CREATURE_AI_DELAY; } else { pSoldier->aiData.usActionData = (UINT16) REALTIME_AI_DELAY; if ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) { //DBrot: More Rooms //UINT8 ubRoom; UINT16 usRoom; if ( InARoom( pSoldier->sGridNo, &usRoom ) && IN_BROTHEL( usRoom ) ) { pSoldier->aiData.usActionData /= 3; } } } } else if (pSoldier->aiData.bAction == AI_ACTION_ABSOLUTELY_NONE) { pSoldier->aiData.bAction = AI_ACTION_NONE; } } // to get here, we MUST have an action selected, but not in progress... NPCDoesAct(pSoldier); // perform the chosen action pSoldier->aiData.bActionInProgress = ExecuteAction(pSoldier); // if started, mark us as busy }
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); }