Files
source/Tactical/TeamTurns.cpp
T
Wanne eb2ca0ba70 - Small fixes and tweaks (by Sandro)
o Minor feature added: Shift+RMB click on a gun with ammo inside, will now unload its magazine and place it on your cursor (if not holding anything already). You don't have to go to the weapon description box to unload it.
o Added Throwing trait support for NCTH (was left out before) and added a minor tweak to STOMP+NCTH compatibility.
o Fixed a bug in NCTH when aiming levels of very fast-to-aim guns could be lowered below zero during calculation, which resulted in suddenly giving the gun 8 aim levels making it extremely slow to aim instead. This occurs when you use a pistol with reflex sights with a Gunslinger merc. In the "AllowedAimingLevelsNCTH" the value "aimLevels" was defined as UINT8, so when it goes below 0, it becomes 255 and is then capped at 8. Changed it to signed.

- IIS fix (by Sandro)
o Interrupts on hearing now occur only in meaningful situations - either when enemy attacks us or comes very close to us.
o Added some other IIS tweaks and fixes (first of all the top status bar should now work properly).

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5289 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-05-20 16:30:14 +00:00

2581 lines
74 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include "types.h"
#include "overhead.h"
#include "animation control.h"
#include "points.h"
#include "opplist.h"
#include "timer.h"
#include "event pump.h"
#include "Sound Control.h"
#include "interface.h"
#include "Isometric Utils.h"
#include "Font Control.H"
#include "ai.h"
#include "interface.h"
#include "message.h"
#include "text.h"
#include "TeamTurns.h"
#include "Smell.h"
#include "game clock.h"
#include "Soldier Functions.h"
#include "cursors.h"
#include "Queen Command.h"
#include "Pathai.h"
#include "Music Control.h"
#include "Strategic Turns.h"
#include "lighting.h"
#include "environment.h"
#include "Explosion Control.h"
#include "dialogue control.h"
#include "Soldier Profile Type.h"
#include "SmokeEffects.h"
#include "lighteffects.h"
#include "air raid.h"
#include "meanwhile.h"
#include "SkillCheck.h"
#include "AIInternals.h"
#include "AIList.h"
#ifdef DEBUG_INTERRUPTS
#include "debug.h"
#endif
#include "renderworld.h"
#include "rotting corpses.h"
#include "Squads.h"
#include "Soldier macros.h"
#include "Soldier Profile.h"
#include "NPC.h"
#include "drugs and alcohol.h" // added by Flugente
#endif
#ifdef JA2UB
#include "Ja25_Tactical.h"
#include "Ja25 Strategic Ai.h"
#endif
// HEADROCK HAM 3.2: Gamesettings.h for external modifications to team turns.
#include "GameSettings.h"
#include "Reinforcement.h"
#include "fresh_header.h"
//forward declarations of common classes to eliminate includes
class OBJECTTYPE;
class SOLDIERTYPE;
#include "connect.h"
extern INT8 STRAIGHT;
//extern UINT8 gubSpeedUpAnimationFactor;
void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier );
void RecalculateSoldiersAniSpeed()
{
UINT32 uiLoop;
SOLDIERTYPE *pSoldier;
// if( gubSpeedUpAnimationFactor == 1 )return;
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pSoldier = MercSlots[ uiLoop ];
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pSoldier)
{
continue; // next merc
}
SetSoldierAniSpeed( pSoldier );
}
}
extern void DecayPublicOpplist(INT8 bTeam);
extern void VerifyAndDecayOpplist(SOLDIERTYPE *pSoldier);
void EndInterrupt( BOOLEAN fMarkInterruptOccurred );
void DeleteFromIntList( UINT8 ubIndex, BOOLEAN fCommunicate);
#define END_OF_INTERRUPTS 255
UINT8 gubOutOfTurnOrder[MAXMERCS] = { END_OF_INTERRUPTS, 0 };
UINT8 gubOutOfTurnPersons = 0;
#define LATEST_INTERRUPT_GUY (gubOutOfTurnOrder[gubOutOfTurnPersons])
#define REMOVE_LATEST_INTERRUPT_GUY() (DeleteFromIntList( (UINT8) (gubOutOfTurnPersons), TRUE ))
#define INTERRUPTS_OVER (gubOutOfTurnPersons == 1)
INT16 InterruptOnlyGuynum = NOBODY;
BOOLEAN InterruptsAllowed = TRUE;
BOOLEAN gfHiddenInterrupt = FALSE;
UINT8 gubLastInterruptedGuy = 0;
extern UINT16 gsWhoThrewRock;
extern UINT8 gubSightFlags;
typedef struct
{
UINT8 ubOutOfTurnPersons;
INT16 InterruptOnlyGuynum;
INT16 sWhoThrewRock;
BOOLEAN InterruptsAllowed;
BOOLEAN fHiddenInterrupt;
UINT8 ubLastInterruptedGuy;
UINT8 ubFiller[16];
} TEAM_TURN_SAVE_STRUCT;
// WANNE: Moved to APBPConstants
//#define MIN_APS_TO_INTERRUPT 4
void ClearIntList( void )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"ClearIntList");
memset( &gubOutOfTurnOrder, 0, MAXMERCS );
gubOutOfTurnOrder[0] = END_OF_INTERRUPTS;
gubOutOfTurnPersons = 0;
}
BOOLEAN BloodcatsPresent( void )
{
INT32 iLoop;
SOLDIERTYPE * pSoldier;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"BloodcatsPresent");
if ( gTacticalStatus.Team[ CREATURE_TEAM ].bTeamActive == FALSE )
{
return( FALSE );
}
for ( iLoop = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; iLoop <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; iLoop++ )
{
pSoldier = MercPtrs[ iLoop ];
if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 && pSoldier->ubBodyType == BLOODCAT )
{
return( TRUE );
}
}
return( FALSE );
}
void StartPlayerTeamTurn( BOOLEAN fDoBattleSnd, BOOLEAN fEnteringCombatMode )
{
INT32 cnt;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"StartPlayerTeamTurn");
// SOLDIERTYPE *pSoldier;
// EV_S_BEGINTURN SBeginTurn;
SetFastForwardMode(FALSE);
// Start the turn of player charactors
//
// PATCH 1.06:
//
// make sure set properly in gTacticalStatus:
gTacticalStatus.ubCurrentTeam = OUR_TEAM;
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
InitPlayerUIBar( FALSE );
if ( gTacticalStatus.uiFlags & TURNBASED )
{
// Are we in combat already?
if ( gTacticalStatus.uiFlags & INCOMBAT )
{
PlayJA2Sample( ENDTURN_1, RATE_11025, MIDVOLUME, 1, MIDDLEPAN );
}
// Remove deadlock message
EndDeadlockMsg( );
// Check for victory conditions
// ATE: Commented out - looks like this message is called earlier for our team
// look for all mercs on the same team,
//for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
//{
// if ( pSoldier->bActive && pSoldier->stats.bLife > 0 )
// {
// SBeginTurn.usSoldierID = (UINT16)cnt;
// AddGameEvent( S_BEGINTURN, 0, &SBeginTurn );
// }
//}
// Are we in combat already?
if ( gTacticalStatus.uiFlags & INCOMBAT )
{
if ( gusSelectedSoldier != NOBODY )
{
// Check if this guy is able to be selected....
if ( MercPtrs[ gusSelectedSoldier ]->stats.bLife < OKLIFE )
{
DebugMsg(TOPIC_JA2INTERRUPT,DBG_LEVEL_3,String("StartPlayerTeamTurn: SelectNextAvailSoldier"));
SelectNextAvailSoldier( MercPtrs[ gusSelectedSoldier ] );
}
else
{
SelectSoldier( gusSelectedSoldier, FALSE, FALSE);
}
// Slide to selected guy...
if ( gusSelectedSoldier != NOBODY )
{
SlideTo( NOWHERE, gusSelectedSoldier, NOBODY ,SETLOCATOR);
if ( fDoBattleSnd )
{
// Say ATTENTION SOUND...
MercPtrs[ gusSelectedSoldier ]->DoMercBattleSound( BATTLE_SOUND_ATTN1 );
}
if ( gsInterfaceLevel == 1 )
{
gTacticalStatus.uiFlags |= SHOW_ALL_ROOFS;
InvalidateWorldRedundency( );
SetRenderFlags(RENDER_FLAG_FULL);
ErasePath(FALSE);
}
}
}
}
// Dirty panel interface!
fInterfacePanelDirty = DIRTYLEVEL2;
// Adjust time now!
UpdateClock( );
if ( !fEnteringCombatMode )
{
CheckForEndOfCombatMode( TRUE );
}
}
// Signal UI done enemy's turn
guiPendingOverrideEvent = LU_ENDUILOCK;
if (is_networked)
guiPendingOverrideEvent = LA_ENDUIOUTURNLOCK;
// ATE: Reset killed on attack variable.. this is because sometimes timing is such
/// that a baddie can die and still maintain it's attacker ID
gTacticalStatus.fKilledEnemyOnAttack = FALSE;
gTacticalStatus.ubInterruptPending = DISABLED_INTERRUPT;
HandleTacticalUI( );
}
void FreezeInterfaceForEnemyTurn( void )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"FreezeInterfaceForEnemyTurn");
// Reset flags
gfPlotNewMovement = TRUE;
// Erase path
ErasePath( TRUE );
// Setup locked UI
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
// Remove any UI messages!
if ( giUIMessageOverlay != -1 )
{
EndUIMessage( );
}
}
void EndTurn( UINT8 ubNextTeam )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"EndTurn");
SOLDIERTYPE * pSoldier;
INT32 cnt;
//Check for enemy pooling (add enemies if there happens to be more than the max in the
//current battle. If one or more slots have freed up, we can add them now.
EndDeadlockMsg( );
/*
if ( CheckForEndOfCombatMode( FALSE ) )
{
return;
}
*/
if (INTERRUPT_QUEUED)
{
if(is_networked)
{
end_interrupt( FALSE );//this tells other client to go on from where he was
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"ending interrupt" );
}
EndInterrupt( FALSE );
}
else
{
// HEADROCK HAM 3.2: Experimental fix to force reinforcements enter battle with 0 APs.
if (gGameExternalOptions.ubReinforcementsFirstTurnFreeze != 1 && gGameExternalOptions.ubReinforcementsFirstTurnFreeze != 2)
{
AddPossiblePendingEnemiesToBattle();
}
if (gGameExternalOptions.ubReinforcementsFirstTurnFreeze != 1 && gGameExternalOptions.ubReinforcementsFirstTurnFreeze != 3)
{
AddPossiblePendingMilitiaToBattle();
}
// InitEnemyUIBar( );
FreezeInterfaceForEnemyTurn();
// Loop through all mercs and set to moved
cnt = gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; cnt++,pSoldier++)
{
if ( pSoldier->bActive )
{
pSoldier->aiData.bMoved = TRUE;
}
}
gTacticalStatus.ubCurrentTeam = ubNextTeam;
gTacticalStatus.ubInterruptPending = DISABLED_INTERRUPT;
if(is_server || !is_client) BeginTeamTurn( gTacticalStatus.ubCurrentTeam );
// WANNE: Disabled Headrocks Experimental fix, because it causes assertion in AddPossiblePendingMilitiaToBattle();
// HEADROCK HAM 3.2: Experimental fix to force reinforcements enter battle with 0 APs.
/*
if (gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 1 || gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 2)
{
AddPossiblePendingEnemiesToBattle();
}
if (gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 1 || gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 3)
{
AddPossiblePendingMilitiaToBattle();
}
*/
BetweenTurnsVisibilityAdjustments();
}
}
void EndAITurn( void )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"EndAITurn");
SOLDIERTYPE * pSoldier;
INT32 cnt;
// Remove any deadlock message
EndDeadlockMsg( );
if (INTERRUPT_QUEUED)
{
if(is_networked)
{
end_interrupt( FALSE );//this tells other client to go on from where he was
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"ending interrupt" );
}
EndInterrupt( FALSE );
}
else
{
cnt = gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; cnt++,pSoldier++)
{
if ( pSoldier->bActive )
{
pSoldier->aiData.bMoved = TRUE;
// record old life value... for creature AI; the human AI might
// want to use this too at some point
pSoldier->bOldLife = pSoldier->stats.bLife;
}
}
gTacticalStatus.ubCurrentTeam++;
BeginTeamTurn( gTacticalStatus.ubCurrentTeam );
}
}
void EndAllAITurns( void )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"EndAllAITurns");
// warp turn to the player's turn
SOLDIERTYPE * pSoldier;
INT32 cnt;
// Remove any deadlock message
EndDeadlockMsg( );
if (INTERRUPT_QUEUED)
{
EndInterrupt( FALSE );
}
if ( gTacticalStatus.ubCurrentTeam != gbPlayerNum )
{
cnt = gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; cnt++,pSoldier++)
{
if ( pSoldier->bActive )
{
pSoldier->aiData.bMoved = TRUE;
pSoldier->flags.uiStatusFlags &= (~SOLDIER_UNDERAICONTROL);
// record old life value... for creature AI; the human AI might
// want to use this too at some point
pSoldier->bOldLife = pSoldier->stats.bLife;
}
}
gTacticalStatus.ubCurrentTeam = gbPlayerNum;
//BeginTeamTurn( gTacticalStatus.ubCurrentTeam );
gTacticalStatus.ubInterruptPending = DISABLED_INTERRUPT;
}
}
void EndTurnEvents( void )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"EndTurnEvents");
// HANDLE END OF TURN EVENTS
// handle team services like healing
HandleTeamServices( gbPlayerNum );
// handle smell and blood decay
DecaySmells();
// decay bomb timers and maybe set some off!
DecayBombTimers();
DecaySmokeEffects( GetWorldTotalSeconds( ) );
DecayLightEffects( GetWorldTotalSeconds( ) );
// decay AI warning values from corpses
DecayRottingCorpseAIWarnings();
#ifdef JA2UB
//Ja25 UB
//increment the number of tactical turns that have gone by in turn based mode
gJa25SaveStruct.uiTacticalTurnCounter++;
//if the fan should start up
HandleStartingFanBackUp();
#endif
}
//rain
BOOLEAN LightningEndOfTurn( UINT8 ubTeam );
//end rain
void BeginTeamTurn( UINT8 ubTeam )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"BeginTeamTurn");
INT32 cnt;
UINT8 ubID;
SOLDIERTYPE *pSoldier;
//rain
if( !LightningEndOfTurn( ubTeam ) )return;
//end rain
// disable for our turn and enable for other teams
if ( gGameSettings.fOptions[TOPTION_AUTO_FAST_FORWARD_MODE] )
{
if (is_networked)
{
// Only allow fast forward mode on enemy team!
SetFastForwardMode( (ubTeam == ENEMY_TEAM) );
}
else
{
// Allow fast forward mode on all teams except our team!
SetFastForwardMode( (ubTeam != OUR_TEAM) );
}
}
while( 1 )
{
if ( ubTeam > LAST_TEAM )
{
if ( HandleAirRaidEndTurn( ubTeam ) )
{
// End turn!!
ubTeam = gbPlayerNum;
gTacticalStatus.ubCurrentTeam = gbPlayerNum;
EndTurnEvents();
if(is_server)
{
numreadyteams =0;//beginning round
memset( &readyteamreg , 0 , sizeof (int) * 10);
}
}
else
{
break;
}
}
else if (!(gTacticalStatus.Team[ ubTeam ].bTeamActive))
{
// inactive team, skip to the next one
ubTeam++;
gTacticalStatus.ubCurrentTeam++;
// skip back to the top, as we are processing another team now.
continue;
}
if ((!(gTacticalStatus.Team[ ubTeam ].bTeamActive))&& is_networked)
{
// inactive team, skip to the next one
ubTeam++;
gTacticalStatus.ubCurrentTeam++;
// skip back to the top, as we are processing another team now.
continue;
}
if ( gTacticalStatus.uiFlags & TURNBASED )
{
BeginLoggingForBleedMeToos( TRUE );
// decay team's public opplist
DecayPublicOpplist( ubTeam );
cnt = gTacticalStatus.Team[ ubTeam ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ubTeam ].bLastID; cnt++,pSoldier++)
{
if ( pSoldier->bActive && pSoldier->stats.bLife > 0)
{
// decay personal opplist, and refresh APs and BPs
pSoldier->EVENT_BeginMercTurn( FALSE, 0 );
}
}
if (gTacticalStatus.bBoxingState == LOST_ROUND || gTacticalStatus.bBoxingState == WON_ROUND || gTacticalStatus.bBoxingState == DISQUALIFIED )
{
// we have no business being in here any more!
return;
}
BeginLoggingForBleedMeToos( FALSE );
}
RecalculateSoldiersAniSpeed();
if (ubTeam == gbPlayerNum )
{
// ATE: Check if we are still in a valid battle...
// ( they could have blead to death above )
if ( ( gTacticalStatus.uiFlags & INCOMBAT ) )
{
StartPlayerTeamTurn( TRUE, FALSE );
/* if(is_server && !net_turn) send_EndTurn(ubTeam);
if(net_turn == true) net_turn = false;*/
if(is_server)
{
numreadyteams =0;//beginning round
memset( &readyteamreg , 0 , sizeof (int) * 10);
send_EndTurn(netbTeam);
}
}
break;
}
else if (ubTeam > 4 || (is_client && !is_server )) //hayden
{
InitEnemyUIBar( 0, 0 );
fInterfacePanelDirty = DIRTYLEVEL2;
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ ubTeam ] );
/*if(is_server && !net_turn) send_EndTurn(ubTeam);
if(net_turn == true) net_turn = false;*/
gTacticalStatus.ubCurrentTeam = ubTeam;
if(is_server) send_EndTurn(ubTeam);
//return;
break;
}
else
{
#ifdef NETWORKED
// Only the host should do this
if(!gfAmIHost)
break;
#endif
if( is_client && !is_server ) //hayden //disable independant client AI
break;
// Set First enemy merc to AI control
if ( BuildAIListForTeam( ubTeam ) )
{
ubID = RemoveFirstAIListEntry();
if (ubID != NOBODY)
{
// Dirty panel interface!
fInterfacePanelDirty = DIRTYLEVEL2;
if ( ubTeam == CREATURE_TEAM && BloodcatsPresent() )
{
AddTopMessage( COMPUTER_TURN_MESSAGE, Message[ STR_BLOODCATS_TURN ] );
}
else
{
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ ubTeam ] );
}
StartNPCAI( MercPtrs[ ubID ] );
/*if(is_server && !net_turn) send_EndTurn(ubTeam);
if(net_turn == true) net_turn = false;*/
if(is_server) send_EndTurn(ubTeam);
return;
}
}
// This team is dead/inactive/being skipped in boxing
// skip back to the top to process the next team
ubTeam++;
gTacticalStatus.ubCurrentTeam++;
}
}
}
void DisplayHiddenInterrupt( SOLDIERTYPE * pSoldier )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"DisplayHiddenInterrupt");
// If the AI got an interrupt but this has been hidden from the player until this point,
// this code will display the interrupt
if (!gfHiddenInterrupt)
{
return;
}
EndDeadlockMsg( );
if (pSoldier->bVisible != -1 )
{
SlideTo( NOWHERE, pSoldier->ubID, NOBODY ,SETLOCATOR);
}
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
// Dirty panel interface!
fInterfacePanelDirty = DIRTYLEVEL2;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"about to call ErasePath");
// Erase path!
ErasePath( TRUE );
// Reset flags
gfPlotNewMovement = TRUE;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"about to call AdjustNoAPToFinishMove");
// Stop our guy....
MercPtrs[ LATEST_INTERRUPT_GUY ]->AdjustNoAPToFinishMove( TRUE );
// Stop him from going to prone position if doing a turn while prone
MercPtrs[ LATEST_INTERRUPT_GUY ]->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"about to call AddTopMessage");
// get rid of any old overlay message
if ( pSoldier->bTeam == MILITIA_TEAM )
{
AddTopMessage( MILITIA_INTERRUPT_MESSAGE, Message[ STR_INTERRUPT ] );
}
else
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[ STR_INTERRUPT ] );
}
gfHiddenInterrupt = FALSE;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"DisplayHiddenInterrupt completed");
}
void DisplayHiddenTurnbased( SOLDIERTYPE * pActingSoldier )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"DisplayHiddenTurnBased");
// This code should put the game in turn-based and give control to the AI-controlled soldier
// whose pointer has been passed in as an argument (we were in non-combat and the AI is doing
// something visible, i.e. making an attack)
#if (defined JA2UB || defined JA113NODEMO)
//Ja25 No meanwhiles
#else
if ( AreInMeanwhile( ) )
{
return;
}
#endif
if (gTacticalStatus.uiFlags & REALTIME || gTacticalStatus.uiFlags & INCOMBAT)
{
// pointless call here; do nothing
return;
}
// Enter combat mode starting with this side's turn
gTacticalStatus.ubCurrentTeam = pActingSoldier->bTeam;
CommonEnterCombatModeCode( );
//JA2Gold: use function to make sure flags turned off everywhere else
//pActingSoldier->flags.uiStatusFlags |= SOLDIER_UNDERAICONTROL;
pActingSoldier->SetSoldierAsUnderAiControl( );
DebugAI( String( "Giving AI control to %d", pActingSoldier->ubID ) );
pActingSoldier->flags.fTurnInProgress = TRUE;
gTacticalStatus.uiTimeSinceMercAIStart = GetJA2Clock();
if ( gTacticalStatus.ubTopMessageType != COMPUTER_TURN_MESSAGE)
{
// Dirty panel interface!
fInterfacePanelDirty = DIRTYLEVEL2;
if ( gTacticalStatus.ubCurrentTeam == CREATURE_TEAM && BloodcatsPresent() )
{
AddTopMessage( COMPUTER_TURN_MESSAGE, Message[ STR_BLOODCATS_TURN ] );
}
else
{
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ gTacticalStatus.ubCurrentTeam ] );
}
}
// freeze the user's interface
FreezeInterfaceForEnemyTurn();
}
BOOLEAN EveryoneInInterruptListOnSameTeam( void )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"EveryoneInInterruptListOnSameTeam");
UINT8 ubLoop;
UINT8 ubTeam = 255;
for (ubLoop = 1; ubLoop <= gubOutOfTurnPersons; ubLoop++)
{
if ( ubTeam == 255 )
{
ubTeam = MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->bTeam;
}
else
{
if ( MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->bTeam != ubTeam )
{
return( FALSE );
}
}
}
return( TRUE );
}
void StartInterrupt( void )
{
UINT8 ubFirstInterrupter;
INT8 bTeam;
SOLDIERTYPE * pSoldier;
SOLDIERTYPE * pTempSoldier;
UINT8 ubInterrupter;
INT32 cnt;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"StartInterrupt");
ubFirstInterrupter = LATEST_INTERRUPT_GUY;
AssertLE(ubFirstInterrupter, MAX_NUM_SOLDIERS);
pSoldier = MercPtrs[ubFirstInterrupter];
AssertNotNIL(pSoldier);
bTeam = pSoldier->bTeam;
ubInterrupter = ubFirstInterrupter;
#ifdef _DEBUG
// display everyone on int queue!
for ( cnt = gubOutOfTurnPersons; cnt > 0; cnt-- )
{
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("STARTINT: Q position %d: %d", cnt, gubOutOfTurnOrder[ cnt ] ) );
}
#endif
//DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: %d is now on top of the interrupt queue", ubFirstInterrupter ) );
gTacticalStatus.fInterruptOccurred = TRUE;
cnt = 0;
for ( pTempSoldier = MercPtrs[ cnt ]; cnt <= MAX_NUM_SOLDIERS; cnt++,pTempSoldier++)
{
if ( pTempSoldier->bActive )
{
pTempSoldier->bMovedPriorToInterrupt = pTempSoldier->aiData.bMoved;
pTempSoldier->aiData.bMoved = TRUE;
}
}
if (pSoldier->bTeam == OUR_TEAM)
{
// start interrupts for everyone on our side at once
CHAR16 sTemp[ 255 ];
UINT8 ubInterrupters = 0;
INT32 iSquad, iCounter;
// disable ff mode
SetFastForwardMode(FALSE);
// build string for display of who gets interrupt
//while( 1 )
for( iCounter = 0; iCounter <= MAX_NUM_SOLDIERS; iCounter++ )
{
MercPtrs[ubInterrupter]->aiData.bMoved = FALSE;
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: popping %d off of the interrupt queue", ubInterrupter ) );
REMOVE_LATEST_INTERRUPT_GUY();
// now LATEST_INTERRUPT_GUY is the guy before the previous
ubInterrupter = LATEST_INTERRUPT_GUY;
if (ubInterrupter == NOBODY) // previously emptied slot!
{
continue;
}
else if (MercPtrs[ubInterrupter]->bTeam != bTeam)
{
break;
}
}
// TODO: check here to see if we really can do anything with available mercs on this team
// if not then end interrupt. Probably should check if actions for for the interrupter but then we
// would lose potential XP for being able to radio rest of team about enemy from the interrupt
// Theoretically ubInterrupter is enemy causing interrupt
BOOL handleInterrupt = TRUE;
if (ubInterrupter != NOBODY)
{
handleInterrupt = FALSE;
// build string in separate loop here, want to linearly process squads...
SOLDIERTYPE *pInterruptedSoldier = MercPtrs[ubInterrupter];
for ( iSquad = 0; iSquad < NUMBER_OF_SQUADS; iSquad++ )
{
for ( iCounter = 0; iCounter < NUMBER_OF_SOLDIERS_PER_SQUAD; iCounter++ )
{
pTempSoldier = Squad[ iSquad ][ iCounter ];
if ( pTempSoldier && pTempSoldier->bActive && pTempSoldier->bInSector && !pTempSoldier->aiData.bMoved )
{
INT16 ubMinAPcost = MinAPsToAttack(pSoldier,pInterruptedSoldier->sGridNo,ADDTURNCOST);
// if we don't have enough APs left to shoot even a snap-shot at this guy
if (ubMinAPcost < pSoldier->bActionPoints)
{
handleInterrupt = TRUE;
}
}
}
}
}
if (!handleInterrupt)
{
// no mercs can take even a snapshot at the guy
EndInterrupt(TRUE);
}
else
{
wcscpy( sTemp, Message[ STR_INTERRUPT_FOR ] );
// build string in separate loop here, want to linearly process squads...
ubInterrupters = 0;
for ( iSquad = 0; iSquad < NUMBER_OF_SQUADS; iSquad++ )
{
for ( iCounter = 0; iCounter < NUMBER_OF_SOLDIERS_PER_SQUAD; iCounter++ )
{
pTempSoldier = Squad[ iSquad ][ iCounter ];
if ( pTempSoldier && pTempSoldier->bActive && pTempSoldier->bInSector && !pTempSoldier->aiData.bMoved )
{
// then this guy got an interrupt...
ubInterrupters++;
if ( ubInterrupters > 6 )
{
// flush... display string, then clear it (we could have 20 names!)
// add comma to end, we know we have another person after this...
wcscat( sTemp, L", " );
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, sTemp );
wcscpy( sTemp, L"" );
ubInterrupters = 1;
}
if ( ubInterrupters > 1 )
{
wcscat( sTemp, L", " );
}
wcscat( sTemp, pTempSoldier->name );
}
}
}
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, sTemp );
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: starting interrupt for %d", ubFirstInterrupter ) );
// gusSelectedSoldier should become the topmost guy on the interrupt list
//gusSelectedSoldier = ubFirstInterrupter;
// Remove deadlock message
EndDeadlockMsg( );
// Select guy....
SelectSoldier( ubFirstInterrupter, TRUE, TRUE );
// ATE; Slide to guy who got interrupted!
SlideTo( NOWHERE, gubLastInterruptedGuy, NOBODY, SETLOCATOR);
// Dirty panel interface!
fInterfacePanelDirty = DIRTYLEVEL2;
gTacticalStatus.ubCurrentTeam = pSoldier->bTeam;
// Signal UI done enemy's turn
guiPendingOverrideEvent = LU_ENDUILOCK;
if (is_networked)
guiPendingOverrideEvent = LA_ENDUIOUTURNLOCK;
HandleTacticalUI( );
InitPlayerUIBar( TRUE );
//AddTopMessage( PLAYER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
PlayJA2Sample( ENDTURN_1, RATE_11025, MIDVOLUME, 1, MIDDLEPAN );
// report any close call quotes for us here
for ( iCounter = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; iCounter <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; iCounter++ )
{
if ( OK_INSECTOR_MERC( MercPtrs[ iCounter ] ) )
{
if ( MercPtrs[ iCounter ]->flags.fCloseCall )
{
if ( MercPtrs[ iCounter ]->bNumHitsThisTurn == 0 && !(MercPtrs[ iCounter ]->usQuoteSaidExtFlags & SOLDIER_QUOTE_SAID_EXT_CLOSE_CALL) && Random( 3 ) == 0 )
{
// say close call quote!
TacticalCharacterDialogue( MercPtrs[ iCounter ], QUOTE_CLOSE_CALL );
MercPtrs[ iCounter ]->usQuoteSaidExtFlags |= SOLDIER_QUOTE_SAID_EXT_CLOSE_CALL;
}
MercPtrs[ iCounter ]->flags.fCloseCall = FALSE;
}
}
}
}
}
else
{
// start interrupts for everyone on that side at once... and start AI with the lowest # guy
// what we do is set everyone to moved except for people with interrupts at the moment
/*
cnt = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID;
for ( pTempSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTempSoldier++)
{
if ( pTempSoldier->bActive )
{
pTempSoldier->bMovedPriorToInterrupt = pTempSoldier->aiData.bMoved;
pTempSoldier->aiData.bMoved = TRUE;
}
}
*/
//while( 1 )
UINT16 usCounter;
for( usCounter = 0; usCounter <= MAX_NUM_SOLDIERS; usCounter++ )
{
MercPtrs[ubInterrupter]->aiData.bMoved = FALSE;
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: popping %d off of the interrupt queue", ubInterrupter ) );
REMOVE_LATEST_INTERRUPT_GUY();
// now LATEST_INTERRUPT_GUY is the guy before the previous
ubInterrupter = LATEST_INTERRUPT_GUY;
if (ubInterrupter == NOBODY) // previously emptied slot!
{
continue;
}
else if (MercPtrs[ubInterrupter]->bTeam != bTeam)
{
break;
}
else if (ubInterrupter < ubFirstInterrupter)
{
ubFirstInterrupter = ubInterrupter;
}
}
// here we have to rebuilt the AI list!
BuildAIListForTeam( bTeam );
// set to the new first interrupter
cnt = RemoveFirstAIListEntry();
pTempSoldier = MercPtrs[ cnt ];
// pSoldier = MercPtrs[ubFirstInterrupter];
//if ( gTacticalStatus.ubCurrentTeam == OUR_TEAM )//hayden
// if ( pSoldier->bTeam > OUR_TEAM && pSoldier->bTeam < 6) // cheap disable
// SANDRO - we don't use the "hidden interrupt" feature with IIS
if (!is_networked && gTacticalStatus.ubCurrentTeam == OUR_TEAM && !gGameOptions.fImprovedInterruptSystem
&& MercPtrs[ LATEST_INTERRUPT_GUY ]->aiData.bOppList[pTempSoldier->ubID] != SEEN_CURRENTLY
&& MercPtrs[ LATEST_INTERRUPT_GUY ]->aiData.bOppList[pTempSoldier->ubID] != SEEN_THIS_TURN )
{
// we're being interrupted by the computer!
// we delay displaying any interrupt message until the computer
// does something...
gfHiddenInterrupt = TRUE;
gTacticalStatus.fUnLockUIAfterHiddenInterrupt = FALSE;
}
// otherwise it's the AI interrupting another AI team
if (pTempSoldier != NULL)
gTacticalStatus.ubCurrentTeam = pTempSoldier->bTeam;
if (is_networked)
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Interrupt ( could be hidden )" );
#endif
}
// SANDRO - show correct top message
if (pTempSoldier->bTeam == MILITIA_TEAM )
AddTopMessage( MILITIA_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
else
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
if (pTempSoldier != NULL)
StartNPCAI( pTempSoldier );
}
if ( !gfHiddenInterrupt )
{
// Stop this guy....
if (MercPtrs[LATEST_INTERRUPT_GUY]->exists() )//MM: this was crashing if the LATEST_INTERRUPT_GUY wasn't set
{
MercPtrs[ LATEST_INTERRUPT_GUY ]->AdjustNoAPToFinishMove( TRUE );
MercPtrs[ LATEST_INTERRUPT_GUY ]->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
}
}
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"StartInterrupt done");
}
void EndInterrupt( BOOLEAN fMarkInterruptOccurred )
{
UINT8 ubInterruptedSoldier;
SOLDIERTYPE * pSoldier;
SOLDIERTYPE * pTempSoldier;
INT32 cnt;
BOOLEAN fFound;
INT16 ubMinAPsToAttack;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"EndInterrupt");
for ( cnt = gubOutOfTurnPersons; cnt > 0; cnt-- )
{
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("ENDINT: Q position %d: %d", cnt, gubOutOfTurnOrder[ cnt ] ) );
}
// ATE: OK, now if this all happended on one frame, we may not have to stop
// guy from walking... so set this flag to false if so...
if ( fMarkInterruptOccurred )
{
// flag as true if an int occurs which ends an interrupt (int loop)
gTacticalStatus.fInterruptOccurred = TRUE;
}
else
{
gTacticalStatus.fInterruptOccurred = FALSE;
}
// Loop through all mercs and see if any passed on this interrupt
cnt = gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bFirstID;
for ( pTempSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; cnt++,pTempSoldier++)
{
if ( pTempSoldier->bActive && pTempSoldier->bInSector && !pTempSoldier->aiData.bMoved && (pTempSoldier->bActionPoints == pTempSoldier->aiData.bIntStartAPs))
{
ubMinAPsToAttack = MinAPsToAttack( pTempSoldier, pTempSoldier->sLastTarget, FALSE );
if ( (ubMinAPsToAttack <= pTempSoldier->bActionPoints) && (ubMinAPsToAttack > 0) )
{
pTempSoldier->aiData.bPassedLastInterrupt = TRUE;
}
}
}
if ( !EveryoneInInterruptListOnSameTeam() )
{
gfHiddenInterrupt = FALSE;
// resume interrupted interrupt
//hayden
if (!is_networked)
{
StartInterrupt();
}
else
{
UINT8 nubFirstInterrupter;
INT8 nbTeam;
SOLDIERTYPE * npSoldier;
nubFirstInterrupter = LATEST_INTERRUPT_GUY;
npSoldier = MercPtrs[nubFirstInterrupter];
nbTeam = npSoldier->bTeam;
#ifdef BETAVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"unchecked interrupt call area:(resume interrupted interrupt)...");
#endif
if ((nbTeam > 0) && (nbTeam <6 ) && is_server) //is AI and are server
{
send_interrupt( npSoldier );
StartInterrupt();
}
else if(is_server && gTacticalStatus.ubCurrentTeam == 1)// against ai
{
//hayden
send_interrupt( npSoldier );
if(nbTeam !=0)
intAI(npSoldier);
else
StartInterrupt();
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"AI is interrupted");
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"interrupt for another team"); //may need more work.
//ClearIntList();
//hayden
StartInterrupt();
send_interrupt( npSoldier ); //
}
}
}
else
{
ubInterruptedSoldier = LATEST_INTERRUPT_GUY;
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: interrupt over, %d's team regains control", ubInterruptedSoldier ) );
pSoldier = MercPtrs[ubInterruptedSoldier];
cnt = 0;
for ( pTempSoldier = MercPtrs[ cnt ]; cnt <= MAX_NUM_SOLDIERS; cnt++,pTempSoldier++)
{
if ( pTempSoldier->bActive )
{
// AI guys only here...
if ( pTempSoldier->bActionPoints == 0 )
{
pTempSoldier->aiData.bMoved = TRUE;
}
else if ( pTempSoldier->bTeam != gbPlayerNum && pTempSoldier->aiData.bNewSituation == IS_NEW_SITUATION )
{
pTempSoldier->aiData.bMoved = FALSE;
}
else
{
pTempSoldier->aiData.bMoved = pTempSoldier->bMovedPriorToInterrupt;
}
}
}
// change team
gTacticalStatus.ubCurrentTeam = pSoldier->bTeam;
// switch appropriate messages & flags
if ( pSoldier->bTeam == OUR_TEAM)
{
// set everyone on the team to however they were set moved before the interrupt
// must do this before selecting soldier...
/*
cnt = gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bFirstID;
for ( pTempSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; cnt++,pTempSoldier++)
{
if ( pTempSoldier->bActive )
{
pTempSoldier->aiData.bMoved = pTempSoldier->bMovedPriorToInterrupt;
}
}
*/
ClearIntList();
// Select soldier....
if ( MercPtrs[ ubInterruptedSoldier ]->stats.bLife < OKLIFE )
{
DebugMsg(TOPIC_JA2INTERRUPT,DBG_LEVEL_3,String("EndInterrupt: SelectNextAvailSoldier"));
SelectNextAvailSoldier( MercPtrs[ ubInterruptedSoldier ] );
}
else
{
SelectSoldier( ubInterruptedSoldier, FALSE, FALSE );
}
if (gfHiddenInterrupt)
{
// Try to make things look like nothing happened at all.
gfHiddenInterrupt = FALSE;
// If we can continue a move, do so!
if ( MercPtrs[ gusSelectedSoldier ]->flags.fNoAPToFinishMove && pSoldier->ubReasonCantFinishMove != REASON_STOPPED_SIGHT )
{
// Continue
MercPtrs[ gusSelectedSoldier ]->AdjustNoAPToFinishMove( FALSE );
if ( MercPtrs[ gusSelectedSoldier ]->sGridNo != MercPtrs[ gusSelectedSoldier ]->pathing.sFinalDestination )
{
MercPtrs[ gusSelectedSoldier ]->EVENT_GetNewSoldierPath( MercPtrs[ gusSelectedSoldier ]->pathing.sFinalDestination, MercPtrs[ gusSelectedSoldier ]->usUIMovementMode );
}
else
{
UnSetUIBusy( pSoldier->ubID );
}
}
else
{
UnSetUIBusy( pSoldier->ubID );
}
if ( gTacticalStatus.fUnLockUIAfterHiddenInterrupt )
{
gTacticalStatus.fUnLockUIAfterHiddenInterrupt = FALSE;
UnSetUIBusy( pSoldier->ubID );
}
}
else
{
// Signal UI done enemy's turn
/// ATE: This used to be ablow so it would get done for
// both hidden interrupts as well - NOT good because
// hidden interrupts should leave it locked if it was already...
guiPendingOverrideEvent = LU_ENDUILOCK;
if (is_networked)
guiPendingOverrideEvent = LA_ENDUIOUTURNLOCK;
HandleTacticalUI( );
if ( gusSelectedSoldier != NOBODY )
{
SlideTo( NOWHERE, gusSelectedSoldier, NOBODY ,SETLOCATOR);
// Say ATTENTION SOUND...
MercPtrs[ gusSelectedSoldier ]->DoMercBattleSound( BATTLE_SOUND_ATTN1 );
if ( gsInterfaceLevel == 1 )
{
gTacticalStatus.uiFlags |= SHOW_ALL_ROOFS;
InvalidateWorldRedundency( );
SetRenderFlags(RENDER_FLAG_FULL);
ErasePath(FALSE);
}
}
// 2 indicates that we're ending an interrupt and going back to
// normal player's turn without readjusting time left in turn (for
// timed turns)
InitPlayerUIBar( 2 );
// SANDRO - shouldn't we unset ui here too?
UnSetUIBusy( pSoldier->ubID );
}
}
else if (!is_networked || (pSoldier->bTeam < 6))//hayden : is Ai or LAN ?
{
// this could be set to true for AI-vs-AI interrupts
gfHiddenInterrupt = FALSE;
// Dirty panel interface!
fInterfacePanelDirty = DIRTYLEVEL2;
// Erase path!
ErasePath( TRUE );
// Reset flags
gfPlotNewMovement = TRUE;
// restart AI with first available soldier
fFound = FALSE;
// rebuild list for this team if anyone on the team is still available
cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
for ( pTempSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; cnt++,pTempSoldier++)
{
if ( pTempSoldier->bActive && pTempSoldier->bInSector && pTempSoldier->stats.bLife >= OKLIFE )
{
fFound = TRUE;
break;
}
}
if ( fFound )
{
// reset found flag because we are rebuilding the AI list
fFound = FALSE;
if ( BuildAIListForTeam( gTacticalStatus.ubCurrentTeam ) )
{
// now bubble up everyone left in the interrupt queue, starting
// at the front of the array
for (cnt = 1; cnt <= gubOutOfTurnPersons; cnt++)
{
MoveToFrontOfAIList( gubOutOfTurnOrder[ cnt ] );
}
cnt = RemoveFirstAIListEntry();
if (cnt != NOBODY)
{
fFound = TRUE;
StartNPCAI( MercPtrs[ cnt ] );
}
}
}
if (fFound)
{
// back to the computer!
if ( gTacticalStatus.ubCurrentTeam == CREATURE_TEAM && BloodcatsPresent() )
{
AddTopMessage( COMPUTER_TURN_MESSAGE, Message[ STR_BLOODCATS_TURN ] );
}
else
{
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ gTacticalStatus.ubCurrentTeam ] );
}
// Signal UI done enemy's turn
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
ClearIntList();
}
else
{
// back to the computer!
if ( gTacticalStatus.ubCurrentTeam == CREATURE_TEAM && BloodcatsPresent() )
{
AddTopMessage( COMPUTER_TURN_MESSAGE, Message[ STR_BLOODCATS_TURN ] );
}
else
{
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ gTacticalStatus.ubCurrentTeam ] );
}
// Signal UI done enemy's turn
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
// must clear int list before ending turn
ClearIntList();
EndAITurn();
}
}
else if (is_networked) //its going to another Lan client..//hayden
{
gTacticalStatus.ubCurrentTeam = pSoldier->bTeam;
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ gTacticalStatus.ubCurrentTeam ] );
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
// must clear int list before ending turn
ClearIntList();
}
// Reset our interface!
fInterfacePanelDirty = DIRTYLEVEL2;
}
if ( gGameSettings.fOptions[TOPTION_AUTO_FAST_FORWARD_MODE] )
{
if (is_networked)
{
// Only allow fast forward mode on enemy team!
SetFastForwardMode( (gTacticalStatus.ubCurrentTeam == ENEMY_TEAM) );
}
else
{
// Allow fast forward mode on all teams except our team!
SetFastForwardMode( (gTacticalStatus.ubCurrentTeam != OUR_TEAM) );
}
}
}
BOOLEAN StandardInterruptConditionsMet( SOLDIERTYPE * pSoldier, UINT8 ubOpponentID, INT8 bOldOppList)
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"StandardInterruptConditionsMet");
// UINT8 ubAniType;
INT16 ubMinPtsNeeded;
INT8 bDir;
SOLDIERTYPE * pOpponent;
if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) && !(gubSightFlags & SIGHT_INTERRUPT) )
{
return( FALSE );
}
if ( gTacticalStatus.ubAttackBusyCount > 0 )
{
return( FALSE );
}
if (ubOpponentID < TOTAL_SOLDIERS)
{
/*
// only the OPPONENT'S controller's decision matters
if (Menptr[ubOpponentID].controller != Net.pnum)
{
return(FALSE);
}
*/
// ALEX
// if interrupts are restricted to a particular opponent only & he's not it
if ((InterruptOnlyGuynum != NOBODY) && (ubOpponentID != InterruptOnlyGuynum))
{
return(FALSE);
}
pOpponent = MercPtrs[ ubOpponentID ];
}
else // no opponent, so controller of 'ptr' makes the call instead
{
// ALEX
if (gsWhoThrewRock >= TOTAL_SOLDIERS)
{
#ifdef BETAVERSION
NumMessage("StandardInterruptConditions: ERROR - ubOpponentID is NOBODY, don't know who threw rock, guynum = ",pSoldier->guynum);
#endif
return(FALSE);
}
// the machine that controls the guy who threw the rock makes the decision
/*
if (Menptr[WhoThrewRock].controller != Net.pnum)
return(FALSE);
*/
pOpponent = NULL;
}
// if interrupts have been disabled for any reason
if (!InterruptsAllowed)
{
return(FALSE);
}
// in non-combat allow interrupt points to be calculated freely (everyone's in control!)
// also allow calculation for storing in AllTeamsLookForAll
if ( (gTacticalStatus.uiFlags & INCOMBAT) && ( gubBestToMakeSightingSize != BEST_SIGHTING_ARRAY_SIZE_ALL_TEAMS_LOOK_FOR_ALL ) )
{
// if his team's already in control
if (pSoldier->bTeam == gTacticalStatus.ubCurrentTeam )
{
// if this is a player's a merc or civilian
if ((pSoldier->flags.uiStatusFlags & SOLDIER_PC) || PTR_CIVILIAN)
{
// then they are not allowed to interrupt their own team
return(FALSE);
}
else
{
// enemies, MAY interrupt each other, but NOT themselves!
//if ( pSoldier->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL )
//{
return(FALSE);
//}
}
// CJC, July 9 1998
// NO ONE EVER interrupts his own team
//return( FALSE );
}
else if ( gTacticalStatus.bBoxingState != NOT_BOXING )
{
// while anything to do with boxing is going on, skip interrupts!
return( FALSE );
}
}
if ( !(pSoldier->bActive) || !(pSoldier->bInSector ) )
{
return( FALSE );
}
// soldiers at less than OKLIFE can't perform any actions
if (pSoldier->stats.bLife < OKLIFE)
{
return(FALSE);
}
// soldiers out of breath are about to fall over, no interrupt
if (pSoldier->bBreath < OKBREATH || pSoldier->bCollapsed )
{
return(FALSE);
}
// if soldier doesn't have enough APs
if ( pSoldier->bActionPoints < APBPConstants[MIN_APS_TO_INTERRUPT] )
{
return( FALSE );
}
// soldiers gagging on gas are too busy about holding their cookies down...
if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED )
{
return(FALSE);
}
// a soldier already engaged in a life & death battle is too busy doing his
// best to survive to worry about "getting the jump" on additional threats
if (pSoldier->aiData.bUnderFire)
{
return(FALSE);
}
if (pSoldier->bCollapsed)
{
return( FALSE );
}
// don't allow neutral folks to get interrupts
if (pSoldier->aiData.bNeutral)
{
return( FALSE );
}
// no EPCs allowed to get interrupts
if ( AM_AN_EPC( pSoldier ) && !AM_A_ROBOT( pSoldier ) )
{
return( FALSE );
}
// don't let mercs on assignment get interrupts
if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bAssignment >= ON_DUTY)
{
return( FALSE );
}
// the bare minimum default is enough APs left to TURN
ubMinPtsNeeded = APBPConstants[AP_CHANGE_FACING];
// if the opponent is SOMEBODY
if (ubOpponentID < TOTAL_SOLDIERS)
{
// if the soldiers are on the same side
if (pSoldier->bSide == pOpponent->bSide)
{
// human/civilians on same side can't interrupt each other
if ((pSoldier->flags.uiStatusFlags & SOLDIER_PC) || PTR_CIVILIAN)
{
return(FALSE);
}
else // enemy
{
// enemies can interrupt EACH OTHER, but enemies and civilians on the
// same side (but different teams) can't interrupt each other.
if (pSoldier->bTeam != pOpponent->bTeam)
{
return(FALSE);
}
}
}
// if the interrupted opponent is not the selected character, then the only
// people eligible to win an interrupt are those on the SAME SIDE AS
// the selected character, ie. his friends...
if ( pOpponent->bTeam == gbPlayerNum )
{
if ((ubOpponentID != gusSelectedSoldier) && (pSoldier->bSide != Menptr[gusSelectedSoldier].bSide))
{
return( FALSE );
}
}
else
{
if (!is_networked) {
if ( !(pOpponent->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL) && (pSoldier->bSide != pOpponent->bSide))
{
return( FALSE );
}
} else {
if ( !(is_client || (pOpponent->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL)) && (pSoldier->bSide != pOpponent->bSide))
{
return( FALSE );
}
}
}
/* old DG code for same:
if ((ubOpponentID != gusSelectedSoldier) && (pSoldier->bSide != Menptr[gusSelectedSoldier].bSide))
{
return(FALSE);
}
*/
// an non-active soldier can't interrupt a soldier who is also non-active!
if ((pOpponent->bTeam != gTacticalStatus.ubCurrentTeam) && (pSoldier->bTeam != gTacticalStatus.ubCurrentTeam))
{
return(FALSE);
}
// if this is a "SEEING" interrupt
if (pSoldier->aiData.bOppList[ubOpponentID] == SEEN_CURRENTLY)
{
// if pSoldier already saw the opponent last "look" or at least this turn
if ((bOldOppList == SEEN_CURRENTLY) || (bOldOppList == SEEN_THIS_TURN))
{
return(FALSE); // no interrupt is possible
}
// if the soldier is behind him and not very close, forget it
bDir = atan8( pSoldier->sX, pSoldier->sY, pOpponent->sX, pOpponent->sY );
if ( gOppositeDirection[ pSoldier->pathing.bDesiredDirection ] == bDir )
{
// directly behind; allow interrupts only within # of tiles equal to level
if ( PythSpacesAway( pSoldier->sGridNo, pOpponent->sGridNo ) > EffectiveExpLevel( pSoldier ) )
{
return( FALSE );
}
}
// if the soldier isn't currently crouching
if (!PTR_CROUCHED)
{
ubMinPtsNeeded = GetAPsCrouch(pSoldier, TRUE); // Changed from APBPConstants[AP_CROUCH] - SANDRO
}
else
{
ubMinPtsNeeded = MinPtsToMove(pSoldier);
}
}
else // this is a "HEARING" interrupt
{
// if the opponent can't see the "interrupter" either, OR
// if the "interrupter" already has any opponents already in sight, OR
// if the "interrupter" already heard the active soldier this turn
if ((pOpponent->aiData.bOppList[pSoldier->ubID] != SEEN_CURRENTLY) || (pSoldier->aiData.bOppCnt > 0) || (bOldOppList == HEARD_THIS_TURN))
{
return(FALSE); // no interrupt is possible
}
}
}
// soldiers without sufficient APs to do something productive can't interrupt
if (pSoldier->bActionPoints < ubMinPtsNeeded)
{
return(FALSE);
}
// soldier passed on the chance to react during previous interrupt this turn
if (pSoldier->aiData.bPassedLastInterrupt)
{
#ifdef RECORDNET
fprintf(NetDebugFile,"\tStandardInterruptConditionsMet: FAILING because PassedLastInterrupt %d(%s)\n",
pSoldier->guynum,ExtMen[pSoldier->guynum].name);
#endif
// return(FALSE);
}
#ifdef RECORDINTERRUPT
// this usually starts a new series of logs, so that's why the blank line
fprintf(InterruptFile,"\nStandardInterruptConditionsMet by %d vs. %d\n",pSoldier->guynum,ubOpponentID);
#endif
return(TRUE);
}
INT8 CalcInterruptDuelPts( SOLDIERTYPE * pSoldier, UINT8 ubOpponentID, BOOLEAN fUseWatchSpots )
{
INT32 iPoints;
INT8 bLightLevel;
UINT8 ubDistance;
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"CalcInterruptDuelPts");
// extra check to make sure neutral folks never get interrupts
if (pSoldier->aiData.bNeutral)
{
return( NO_INTERRUPT );
}
// Old: BASE is one point for each experience level.
// Snap: Agility should be a factor, since it is a measure of
// a person's reactions. We'll give more weight to experience
// though, since combat initiative is too important to be left up
// to an ordinary skill like agility.
// BASE = (2*lev + agi/10) / 3
// Robot has interrupt points based on the controller...
// Controller's interrupt points are reduced by 2 for being distracted...
if ( pSoldier->flags.uiStatusFlags & SOLDIER_ROBOT && pSoldier->CanRobotBeControlled( ) )
{
//iPoints = EffectiveExpLevel( MercPtrs[ pSoldier->ubRobotRemoteHolderID ] ) - 2;
// Snap: (do some proper rounding here)
iPoints = ( 20*EffectiveExpLevel( MercPtrs[ pSoldier->ubRobotRemoteHolderID ] )
+ EffectiveAgility( MercPtrs[ pSoldier->ubRobotRemoteHolderID ] ) + 15 ) / 30 - 2;
}
else
{
//iPoints = EffectiveExpLevel( pSoldier );
// Snap:
iPoints = ( 20*EffectiveExpLevel( pSoldier ) + EffectiveAgility( pSoldier ) + 15 ) / 30;
/*
if ( pSoldier->bTeam == ENEMY_TEAM )
{
// modify by the difficulty level setting
iPoints += gbDiff[ DIFF_ENEMY_INTERRUPT_MOD ][ SoldierDifficultyLevel( pSoldier ) ];
iPoints = __max( iPoints, 9 );
}
*/
if ( pSoldier->ControllingRobot( ) )
{
iPoints -= 2;
}
}
if (fUseWatchSpots)
{
// if this is a previously noted spot of enemies, give bonus points!
iPoints += GetWatchedLocPoints( pSoldier->ubID, MercPtrs[ ubOpponentID ]->sGridNo, MercPtrs[ ubOpponentID ]->pathing.bLevel );
}
// LOSE one point for each 2 additional opponents he currently sees, above 2
if (pSoldier->aiData.bOppCnt > 2)
{
// subtract 1 here so there is a penalty of 1 for seeing 3 enemies
iPoints -= (pSoldier->aiData.bOppCnt - 1) / 2;
}
// LOSE one point if he's trying to interrupt only by hearing
if (pSoldier->aiData.bOppList[ubOpponentID] == HEARD_THIS_TURN)
{
iPoints--;
}
//hayden, multiplayer add advantage for a ready'd reapon
if(is_networked)
{
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags &( ANIM_FIREREADY | ANIM_FIRE ) ))
{
iPoints=(iPoints + cWeaponReadyBonus);
}
}
// if soldier is still in shock from recent injuries, that penalizes him
iPoints -= pSoldier->aiData.bShock;
ubDistance = (UINT8) PythSpacesAway( pSoldier->sGridNo, MercPtrs[ ubOpponentID ]->sGridNo );
// if we are in combat mode - thus doing an interrupt rather than determine who gets first turn -
// then give bonus
if ( (gTacticalStatus.uiFlags & INCOMBAT) && (pSoldier->bTeam != gTacticalStatus.ubCurrentTeam) )
{
// passive player gets penalty due to range
iPoints -= (ubDistance / 10);
}
else
{
// either non-combat or the player with the current turn... i.e. active...
// unfortunately we can't use opplist here to record whether or not we saw this guy before, because at this point
// the opplist has been updated to seen. But we can use gbSeenOpponents ...
// this soldier is moving, so give them a bonus for crawling or swatting at long distances
if ( !gbSeenOpponents[ ubOpponentID ][ pSoldier->ubID ] )
{
if (pSoldier->usAnimState == SWATTING && ubDistance > (MaxNormalDistanceVisible() / 2) ) // more than 1/2 sight distance
{
iPoints++;
}
else if (pSoldier->usAnimState == CRAWLING && ubDistance > (MaxNormalDistanceVisible() / 4) ) // more than 1/4 sight distance
{
iPoints += ubDistance / STRAIGHT;
}
}
}
// whether active or not, penalize people who are running
if ( pSoldier->usAnimState == RUNNING && !gbSeenOpponents[ pSoldier->ubID ][ ubOpponentID ] )
{
iPoints -= 2;
}
if (pSoldier->ubServicePartner != NOBODY)
{
// distracted by being bandaged/doing bandaging
iPoints -= 2;
}
if (gGameOptions.fNewTraitSystem) // new/old traits check - SANDRO
{
if ( HAS_SKILL_TRAIT( pSoldier, NIGHT_OPS_NT ) )
{
bLightLevel = LightTrueLevel(pSoldier->sGridNo, pSoldier->pathing.bLevel);
if (bLightLevel > NORMAL_LIGHTLEVEL_DAY + 3)
{
// it's dark, give a bonus for interrupts
iPoints += gSkillTraitValues.ubNOIterruptsBonusInDark;
}
}
// Phlegmatics get a small penalty to interrupts
if ( gMercProfiles[ pSoldier->ubProfile ].bCharacterTrait == CHAR_TRAIT_PHLEGMATIC )
{
iPoints -= 1;
}
}
else
{
if ( HAS_SKILL_TRAIT( pSoldier, NIGHTOPS_OT ) )
{
bLightLevel = LightTrueLevel(pSoldier->sGridNo, pSoldier->pathing.bLevel);
if (bLightLevel > NORMAL_LIGHTLEVEL_DAY + 3)
{
// it's dark, give a bonus for interrupts
iPoints += 1 * NUM_SKILL_TRAITS( pSoldier, NIGHTOPS_OT );
}
}
}
// Flugente: drugs can alter our perception
if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_PERCEPTION ] )
{
iPoints += 1;
}
else if ( pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_PERCEPTION ] )
{
iPoints -= 1;
}
// if he's a computer soldier
// CJC note: this will affect friendly AI as well...
if ( pSoldier->flags.uiStatusFlags & SOLDIER_PC )
{
if ( pSoldier->bAssignment >= ON_DUTY )
{
// make sure don't get interrupts!
iPoints = -10;
}
// GAIN one point if he's previously seen the opponent
// check for TRUE because -1 means we JUST saw him (always so here)
if (gbSeenOpponents[pSoldier->ubID][ubOpponentID] == TRUE)
{
iPoints++; // seen him before, easier to react to him
}
}
else if ( pSoldier->bTeam == ENEMY_TEAM )
{
// GAIN one point if he's previously seen the opponent
// check for TRUE because -1 means we JUST saw him (always so here)
if (gbSeenOpponents[pSoldier->ubID][ubOpponentID] == TRUE)
{
iPoints++; // seen him before, easier to react to him
}
else if (gbPublicOpplist[pSoldier->bTeam][ubOpponentID] != NOT_HEARD_OR_SEEN)
{
// GAIN one point if opponent has been recently radioed in by his team
iPoints++;
}
}
if ( TANK( pSoldier ) )
{
// reduce interrupt possibilities for tanks!
iPoints /= 2;
}
if (iPoints >= AUTOMATIC_INTERRUPT)
{
#ifdef BETAVERSION
NumMessage("CalcInterruptDuelPts: ERROR - Invalid bInterruptDuelPts calculated for soldier ",pSoldier->guynum);
#endif
iPoints = AUTOMATIC_INTERRUPT - 1; // hack it to one less than max so its legal
}
#ifdef DEBUG_INTERRUPTS
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("Calculating int pts for %d vs %d, number is %d", pSoldier->ubID, ubOpponentID, iPoints ) );
#endif
if(is_networked)
{
SOLDIERTYPE *pOpp = &Menptr[ubOpponentID];
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_MPSYSTEM, L"Interrupt: '%s' vs '%s' = %d points.",pSoldier->name,pOpp->name, iPoints );
#endif
}
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"CalcInterruptDuelPts done");
return( (INT8)iPoints );
}
BOOLEAN InterruptDuel( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOpponent)
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"InterruptDuel");
BOOLEAN fResult = FALSE;
// if opponent can't currently see us and we can see them
if ( pSoldier->aiData.bOppList[ pOpponent->ubID ] == SEEN_CURRENTLY && pOpponent->aiData.bOppList[pSoldier->ubID] != SEEN_CURRENTLY )
{
fResult = TRUE; // we automatically interrupt
// fix up our interrupt duel pts if necessary
if (pSoldier->aiData.bInterruptDuelPts < pOpponent->aiData.bInterruptDuelPts)
{
pSoldier->aiData.bInterruptDuelPts = pOpponent->aiData.bInterruptDuelPts;
}
}
else
{
// If our total points is HIGHER, then we interrupt him anyway
if (pSoldier->aiData.bInterruptDuelPts > pOpponent->aiData.bInterruptDuelPts)
{
fResult = TRUE;
}
}
// ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Interrupt duel %d (%d pts) vs %d (%d pts)", pSoldier->ubID, pSoldier->aiData.bInterruptDuelPts, pOpponent->ubID, pOpponent->aiData.bInterruptDuelPts );
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"InterruptDuel done");
return( fResult );
}
void DeleteFromIntList( UINT8 ubIndex, BOOLEAN fCommunicate)
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"DeleteFromIntList");
UINT8 ubLoop;
UINT8 ubID;
if ( ubIndex > gubOutOfTurnPersons)
{
return;
}
// remember who we're getting rid of
ubID = gubOutOfTurnOrder[ubIndex];
#ifdef DEBUG_INTERRUPTS
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: removing ID %d", ubID ) );
#endif
// ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%d removed from int list", ubID );
// if we're NOT deleting the LAST entry in the int list
if (ubIndex < gubOutOfTurnPersons)
{
// not the last entry, must move all those behind it over to fill the gap
for (ubLoop = ubIndex; ubLoop < gubOutOfTurnPersons; ubLoop++)
{
gubOutOfTurnOrder[ubLoop] = gubOutOfTurnOrder[ubLoop + 1];
}
}
// either way, whack the last entry to NOBODY and decrement the list size
gubOutOfTurnOrder[gubOutOfTurnPersons] = NOBODY;
gubOutOfTurnPersons--;
// once the last interrupted guy gets deleted from the list, he's no longer
// the last interrupted guy!
/*
if (Status.lastInterruptedWas == ubID)
{
Status.lastInterruptedWas = NOBODY;
}
*/
}
void AddToIntList( UINT8 ubID, BOOLEAN fGainControl, BOOLEAN fCommunicate )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"AddToIntList");
UINT8 ubLoop;
// ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%d added to int list", ubID );
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: adding ID %d who %s", ubID, fGainControl ? "gains control" : "loses control" ) );
// check whether 'who' is already anywhere on the queue after the first index
// which we want to preserve so we can restore turn order
for (ubLoop = 2; ubLoop <= gubOutOfTurnPersons; ubLoop++)
{
if (gubOutOfTurnOrder[ubLoop] == ubID)
{
if (!fGainControl)
{
// he's LOSING control; that's it, we're done, DON'T add him to the queue again
gubLastInterruptedGuy = ubID;
return;
}
else
{
// GAINING control, so delete him from this slot (because later he'll
// get added to the end and we don't want him listed more than once!)
DeleteFromIntList( ubLoop, FALSE );
}
}
}
// increment total (making index valid) and add him to list
gubOutOfTurnPersons++;
gubOutOfTurnOrder[gubOutOfTurnPersons] = ubID;
/*
// the guy being interrupted HAS to be the currently selected character
if (Status.lastInterruptedWas != CharacterSelected)
{
// if we don't already do so, remember who that was
Status.lastInterruptedWas = CharacterSelected;
}
*/
// if the guy is gaining control
if (fGainControl)
{
// record his initial APs at the start of his interrupt at this time
// this is not the ideal place for this, but it's the best I could do...
Menptr[ubID].aiData.bIntStartAPs = Menptr[ubID].bActionPoints;
}
else
{
gubLastInterruptedGuy = ubID;
// turn off AI control flag if they lost control
if (Menptr[ubID].flags.uiStatusFlags & SOLDIER_UNDERAICONTROL)
{
DebugAI( String( "Taking away AI control from %d", ubID ) );
Menptr[ubID].flags.uiStatusFlags &= (~SOLDIER_UNDERAICONTROL);
}
}
}
void VerifyOutOfTurnOrderArray()
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"VerifyOutOfTurnOrderArray");
UINT8 ubTeamHighest[ MAXTEAMS ] = { 0 };
UINT8 ubTeamsInList;
UINT8 ubNextInArrayOnTeam, ubNextIndex;
UINT8 ubTeam;
UINT8 ubLoop, ubLoop2;
BOOLEAN fFoundLoop = FALSE;
for (ubLoop = 1; ubLoop <= gubOutOfTurnPersons; ubLoop++)
{
ubTeam = Menptr[ gubOutOfTurnOrder[ ubLoop ] ].bTeam;
if (ubTeamHighest[ ubTeam ] > 0)
{
// check the other teams to see if any of them are between our last team's mention in
// the array and this
for (ubLoop2 = 0; ubLoop2 < MAXTEAMS; ubLoop2++)
{
if (ubLoop2 == ubTeam)
{
continue;
}
else
{
if (ubTeamHighest[ ubLoop2 ] > ubTeamHighest[ ubTeam ])
{
// there's a loop!! delete it!
ubNextInArrayOnTeam = gubOutOfTurnOrder[ ubLoop ];
ubNextIndex = ubTeamHighest[ ubTeam ] + 1;
while( gubOutOfTurnOrder[ ubNextIndex ] != ubNextInArrayOnTeam )
{
// Pause them...
MercPtrs[ gubOutOfTurnOrder[ ubNextIndex ] ]->AdjustNoAPToFinishMove( TRUE );
// If they were turning from prone, stop them
MercPtrs[ gubOutOfTurnOrder[ ubNextIndex ] ]->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
DeleteFromIntList( ubNextIndex, FALSE );
}
fFoundLoop = TRUE;
break;
}
}
}
if (fFoundLoop)
{
// at this point we should restart our outside loop (ugh)
fFoundLoop = FALSE;
for (ubLoop2 = 0; ubLoop2 < MAXTEAMS; ubLoop2++)
{
ubTeamHighest[ ubLoop2 ] = 0;
}
ubLoop = 0;
continue;
}
}
ubTeamHighest[ ubTeam ] = ubLoop;
}
// Another potential problem: the player is interrupted by the enemy who is interrupted by
// the militia. In this situation the enemy should just lose their interrupt.
// (Or, the militia is interrupted by the enemy who is interrupted by the player.)
// Check for 3+ teams in the interrupt queue. If three exist then abort all interrupts (return
// control to the first team)
ubTeamsInList = 0;
for ( ubLoop = 0; ubLoop < MAXTEAMS; ubLoop++ )
{
if ( ubTeamHighest[ ubLoop ] > 0 )
{
ubTeamsInList++;
}
}
if ( ubTeamsInList >= 3 )
{
// This is bad. Loop through everyone but the first person in the INT list and remove 'em
for (ubLoop = 2; ubLoop <= gubOutOfTurnPersons; )
{
if ( MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->bTeam != MercPtrs[ gubOutOfTurnOrder[ 1 ] ]->bTeam )
{
// remove!
// Pause them...
MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->AdjustNoAPToFinishMove( TRUE );
// If they were turning from prone, stop them
MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
DeleteFromIntList( ubLoop, FALSE );
// since we deleted someone from the list, we want to check the same index in the
// array again, hence we DON'T increment.
}
else
{
ubLoop++;
}
}
}
}
void DoneAddingToIntList( SOLDIERTYPE * pSoldier, BOOLEAN fChange, UINT8 ubInterruptType)
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"DoneAddingToIntList");
if (fChange)
{
VerifyOutOfTurnOrderArray();
if ( EveryoneInInterruptListOnSameTeam() )
{
EndInterrupt( TRUE );
}
else
{
if (!is_networked)
{
StartInterrupt();
}
else
{
UINT8 nubFirstInterrupter;
INT8 nbTeam;
SOLDIERTYPE * npSoldier;
nubFirstInterrupter = LATEST_INTERRUPT_GUY;
npSoldier = MercPtrs[nubFirstInterrupter];
nbTeam = npSoldier->bTeam;
// INTERRUPT is calculated on the server
if ((nbTeam > 0) && (nbTeam <6 ) && is_server) //is for AI and are server
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"interrupt for AI team");
// Only display the top message if we (the server) got interrupted
if (pSoldier->bTeam == 0)
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, TeamTurnString[ nbTeam ] );
send_interrupt( npSoldier );
StartInterrupt();
}
// INTERRUPT is calculated on the server
else if(is_server && gTacticalStatus.ubCurrentTeam == 1)// against ai
{
//hayden
send_interrupt( npSoldier ); //
if(nbTeam !=0)
intAI(npSoldier);
else
StartInterrupt();//
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"AI is interrupted");
}
// INTERRUPT is calculated on the pure client
else if(gTacticalStatus.ubCurrentTeam == 0)//its our turn (we are moving)
{
ScreenMsg( FONT_MCOLOR_LTRED, MSG_INTERFACE, MPClientMessage[79]);
send_interrupt( npSoldier );
SOLDIERTYPE* pMerc = MercPtrs[ gusSelectedSoldier ];
//AdjustNoAPToFinishMove( pMerc, TRUE );
pMerc->HaultSoldierFromSighting(TRUE);
//pMerc->fTurningFromPronePosition = FALSE;// hmmm ??
FreezeInterfaceForEnemyTurn();
InitEnemyUIBar( 0, 0 );
fInterfacePanelDirty = DIRTYLEVEL2;
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, TeamTurnString[ nbTeam ] );
gTacticalStatus.fInterruptOccurred = TRUE;
}
else
{
ClearIntList();//no interrupt to be awarded, clear generated list.
}
}
}
}
}
void ResolveInterruptsVs( SOLDIERTYPE * pSoldier, UINT8 ubInterruptType)
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,String("ResolveInterruptsVs: Soldier ID = %d, APs = %d (interrupt type = %d)",pSoldier->ubID,pSoldier->bActionPoints, ubInterruptType));
UINT8 ubTeam, ubOpp;
UINT8 ubIntCnt;
UINT8 ubIntList[MAXMERCS];
UINT8 ubIntDiff[MAXMERCS];
UINT8 ubSmallestDiff;
UINT8 ubSlot, ubSmallestSlot;
UINT8 ubLoop;
BOOLEAN fIntOccurs;
SOLDIERTYPE * pOpponent;
BOOLEAN fControlChanged = FALSE;
AssertNotNIL(pSoldier);
if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
{
ubIntCnt = 0;
for (ubTeam = 0; ubTeam < MAXTEAMS; ubTeam++)
{
// WDS fix broken interrupts (I hope...)
if (/*gTacticalStatus.Team[ubTeam].bTeamActive &&*/ (gTacticalStatus.Team[ubTeam].bSide != pSoldier->bSide) && ubTeam != CIV_TEAM)
{
for ( ubOpp = gTacticalStatus.Team[ ubTeam ].bFirstID; ubOpp <= gTacticalStatus.Team[ ubTeam ].bLastID; ubOpp++)
{
pOpponent = MercPtrs[ubOpp];
AssertNotNIL(pOpponent);
if ( pOpponent->bActive && pOpponent->bInSector && (pOpponent->stats.bLife >= OKLIFE) && !(pOpponent->bCollapsed) )
{
if ( ubInterruptType == NOISEINTERRUPT )
{
// don't grant noise interrupts at greater than max. visible distance
if ( PythSpacesAway( pSoldier->sGridNo, pOpponent->sGridNo ) > MaxNormalDistanceVisible() )
{
pOpponent->aiData.bInterruptDuelPts = NO_INTERRUPT;
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("Resetting int pts for %d - NOISE BEYOND SIGHT DISTANCE!?", pOpponent->ubID ) );
continue;
}
}
else if ( pOpponent->aiData.bOppList[pSoldier->ubID] != SEEN_CURRENTLY )
{
pOpponent->aiData.bInterruptDuelPts = NO_INTERRUPT;
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("Resetting int pts for %d - DOESN'T SEE ON SIGHT INTERRUPT!?", pOpponent->ubID ) );
continue;
}
switch (pOpponent->aiData.bInterruptDuelPts)
{
case NO_INTERRUPT: // no interrupt possible, no duel necessary
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("ResolveInterruptsVs: No interrupt for opponent %d", pOpponent->ubID ) );
fIntOccurs = FALSE;
break;
case AUTOMATIC_INTERRUPT: // interrupts occurs automatically
pSoldier->aiData.bInterruptDuelPts = 0; // just to have a valid intDiff later
fIntOccurs = TRUE;
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: automatic interrupt on %d by %d", pSoldier->ubID, pOpponent->ubID ) );
break;
default: // interrupt is possible, run a duel
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, "Calculating int duel pts for onlooker in ResolveInterruptsVs" );
pSoldier->aiData.bInterruptDuelPts = CalcInterruptDuelPts(pSoldier,pOpponent->ubID,TRUE);
fIntOccurs = InterruptDuel(pOpponent,pSoldier);
#ifdef DEBUG_INTERRUPTS
if (fIntOccurs)
{
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: standard interrupt on %d (%d pts) by %d (%d pts)", pSoldier->ubID, pSoldier->aiData.bInterruptDuelPts, pOpponent->ubID, pOpponent->aiData.bInterruptDuelPts) );
}
#endif
break;
}
if (fIntOccurs)
{
// remember that this opponent's scheduled to interrupt us
ubIntList[ubIntCnt] = pOpponent->ubID;
// and by how much he beat us in the duel
ubIntDiff[ubIntCnt] = pOpponent->aiData.bInterruptDuelPts - pSoldier->aiData.bInterruptDuelPts;
// increment counter of interrupts lost
ubIntCnt++;
}
else
{
/*
if (pOpponent->aiData.bInterruptDuelPts != NO_INTERRUPT)
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%d fails to interrupt %d (%d vs %d pts)", pOpponent->ubID, pSoldier->ubID, pOpponent->aiData.bInterruptDuelPts, pSoldier->aiData.bInterruptDuelPts);
}
*/
}
// either way, clear out both sides' bInterruptDuelPts field to prepare next one
if (pSoldier->aiData.bInterruptDuelPts != NO_INTERRUPT)
{
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("Resetting int pts for %d", pSoldier->ubID ) );
}
pSoldier->aiData.bInterruptDuelPts = NO_INTERRUPT;
if (pOpponent->aiData.bInterruptDuelPts != NO_INTERRUPT)
{
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("Resetting int pts for %d", pOpponent->ubID ) );
}
pOpponent->aiData.bInterruptDuelPts = NO_INTERRUPT;
}
}
}
}
// if any interrupts are scheduled to occur (ie. I lost at least once)
if (ubIntCnt)
{
// First add currently active character to the interrupt queue. This is
// USUALLY pSoldier->guynum, but NOT always, because one enemy can
// "interrupt" on another enemy's turn if he hears another team's wound
// victim's screaming... the guy screaming is pSoldier here, it's not his turn!
//AddToIntList( (UINT8) gusSelectedSoldier, FALSE, TRUE);
if ( (gTacticalStatus.ubCurrentTeam != pSoldier->bTeam) && !(gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bHuman) )
{
// if anyone on this team is under AI control, remove
// their AI control flag and put them on the queue instead of this guy
for ( ubLoop = gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bFirstID; ubLoop <= gTacticalStatus.Team[ gTacticalStatus.ubCurrentTeam ].bLastID; ubLoop++ )
{
if ( (MercPtrs[ ubLoop ]->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL) )
{
// this guy lost control
MercPtrs[ ubLoop ]->flags.uiStatusFlags &= (~SOLDIER_UNDERAICONTROL);
AddToIntList( ubLoop, FALSE, TRUE);
break;
}
}
}
else
{
// this guy lost control
AddToIntList( pSoldier->ubID, FALSE, TRUE);
}
// loop once for each opponent who interrupted
for (ubLoop = 0; ubLoop < ubIntCnt; ubLoop++)
{
// find the smallest intDiff still remaining in the list
ubSmallestDiff = NO_INTERRUPT;
ubSmallestSlot = NOBODY;
for (ubSlot = 0; ubSlot < ubIntCnt; ubSlot++)
{
if (ubIntDiff[ubSlot] < ubSmallestDiff)
{
ubSmallestDiff = ubIntDiff[ubSlot];
ubSmallestSlot = ubSlot;
}
}
if (ubSmallestSlot < TOTAL_SOLDIERS)
{
// add this guy to everyone's interrupt queue
AddToIntList(ubIntList[ubSmallestSlot],TRUE,TRUE);
// SANDRO - for IIS, reset counter if we got here
if ( gGameOptions.fImprovedInterruptSystem )
{
// reset the counter
MercPtrs[ ubIntList[ubSmallestSlot] ]->aiData.ubInterruptCounter[pSoldier->ubID] = 0;
}
if (INTERRUPTS_OVER)
{
// a loop was created which removed all the people in the interrupt queue!
EndInterrupt( TRUE );
return;
}
ubIntDiff[ubSmallestSlot] = NO_INTERRUPT; // mark slot as been handled
}
}
fControlChanged = TRUE;
}
// sends off an end-of-list msg telling everyone whether to switch control,
// unless it's a MOVEMENT interrupt, in which case that is delayed til later
DoneAddingToIntList(pSoldier,fControlChanged,ubInterruptType);
}
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"ResolveInterruptsVs done");
}
BOOLEAN SaveTeamTurnsToTheSaveGameFile( HWFILE hFile )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"SaveTeamTurnsToTheSaveGameFile");
UINT32 uiNumBytesWritten;
TEAM_TURN_SAVE_STRUCT TeamTurnStruct;
//Save the gubTurn Order Array
FileWrite( hFile, gubOutOfTurnOrder, sizeof( UINT8 ) * MAXMERCS, &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) * MAXMERCS )
{
return( FALSE );
}
TeamTurnStruct.ubOutOfTurnPersons = gubOutOfTurnPersons;
TeamTurnStruct.InterruptOnlyGuynum = InterruptOnlyGuynum;
TeamTurnStruct.sWhoThrewRock = gsWhoThrewRock;
TeamTurnStruct.InterruptsAllowed = InterruptsAllowed;
TeamTurnStruct.fHiddenInterrupt = gfHiddenInterrupt;
TeamTurnStruct.ubLastInterruptedGuy = gubLastInterruptedGuy;
//Save the Team turn save structure
FileWrite( hFile, &TeamTurnStruct, sizeof( TEAM_TURN_SAVE_STRUCT ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( TEAM_TURN_SAVE_STRUCT ) )
{
return( FALSE );
}
return( TRUE );
}
BOOLEAN LoadTeamTurnsFromTheSavedGameFile( HWFILE hFile )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"LoadTeamTurnsFromTheSavedGameFile");
UINT32 uiNumBytesRead;
TEAM_TURN_SAVE_STRUCT TeamTurnStruct;
//Load the gubTurn Order Array
FileRead( hFile, gubOutOfTurnOrder, sizeof( UINT8 ) * MAXMERCS, &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT8 ) * MAXMERCS )
{
return( FALSE );
}
//Load the Team turn save structure
FileRead( hFile, &TeamTurnStruct, sizeof( TEAM_TURN_SAVE_STRUCT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( TEAM_TURN_SAVE_STRUCT ) )
{
return( FALSE );
}
gubOutOfTurnPersons = TeamTurnStruct.ubOutOfTurnPersons;
InterruptOnlyGuynum = TeamTurnStruct.InterruptOnlyGuynum;
gsWhoThrewRock = TeamTurnStruct.sWhoThrewRock;
InterruptsAllowed = TeamTurnStruct.InterruptsAllowed;
gfHiddenInterrupt = TeamTurnStruct.fHiddenInterrupt;
gubLastInterruptedGuy = TeamTurnStruct.ubLastInterruptedGuy;
return( TRUE );
}
BOOLEAN NPCFirstDraw( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTargetSoldier )
{
DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"NPCFirstDraw");
// if attacking an NPC check to see who draws first!
if ( pTargetSoldier->ubProfile != NO_PROFILE && pTargetSoldier->ubProfile != SLAY && pTargetSoldier->aiData.bNeutral && pTargetSoldier->aiData.bOppList[ pSoldier->ubID ] == SEEN_CURRENTLY && ( FindAIUsableObjClass( pTargetSoldier, IC_WEAPON ) != NO_SLOT ) )
{
UINT8 ubLargerHalf, ubSmallerHalf, ubTargetLargerHalf, ubTargetSmallerHalf;
// roll the dice!
// e.g. if level 5, roll Random( 3 + 1 ) + 2 for result from 2 to 5
// if level 4, roll Random( 2 + 1 ) + 2 for result from 2 to 4
ubSmallerHalf = EffectiveExpLevel( pSoldier ) / 2;
ubLargerHalf = EffectiveExpLevel( pSoldier ) - ubSmallerHalf;
ubTargetSmallerHalf = EffectiveExpLevel( pTargetSoldier ) / 2;
ubTargetLargerHalf = EffectiveExpLevel( pTargetSoldier ) - ubTargetSmallerHalf;
if ( gMercProfiles[ pTargetSoldier->ubProfile ].bApproached & gbFirstApproachFlags[ APPROACH_THREATEN - 1 ] )
{
// gains 1 to 2 points
ubTargetSmallerHalf += 1;
ubTargetLargerHalf += 1;
}
if ( Random( ubTargetSmallerHalf + 1) + ubTargetLargerHalf > Random( ubSmallerHalf + 1) + ubLargerHalf )
{
return( TRUE );
}
}
return( FALSE );
}