mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7729 3b4a5df2-a311-0410-b5c6-a8a6f20db521
5963 lines
197 KiB
C++
5963 lines
197 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Strategic All.h"
|
|
#include "GameSettings.h"
|
|
#else
|
|
#include "builddefines.h"
|
|
#include <stdio.h>
|
|
#include "types.h"
|
|
#include "Auto Resolve.h"
|
|
#include "Strategic Movement.h"
|
|
#include "Queen Command.h"
|
|
#include "Music Control.h"
|
|
#include "PreBattle Interface.h"
|
|
#include "Player Command.h"
|
|
#include "mousesystem.h"
|
|
#include "Button System.h"
|
|
#include "gameloop.h"
|
|
#include "screenids.h"
|
|
#include "mapscreen.h"
|
|
#include "vobject.h"
|
|
#include "video.h"
|
|
#include "input.h"
|
|
#include "gamescreen.h"
|
|
#include "render dirty.h"
|
|
#include "vObject_Blitters.h"
|
|
#include "sysutil.h"
|
|
#include "Font Control.h"
|
|
#include "Soldier Create.h"
|
|
#include "Overhead.h"
|
|
#include "Interface.h"
|
|
#include "Items.h"
|
|
#include "Weapons.h"
|
|
#include "Sound Control.h"
|
|
#include "Game Clock.h"
|
|
#include "Soldier Profile.h"
|
|
#include "Campaign.h"
|
|
#include "Tactical Save.h"
|
|
#include "Strategic Status.h"
|
|
#include "Map Screen Interface.h"
|
|
#include "text.h"
|
|
#include "WordWrap.h"
|
|
#include "Squads.h"
|
|
#include "Random.h"
|
|
#include "line.h"
|
|
#include "english.h"
|
|
#include "Strategic Pathing.h"
|
|
#include "Strategic Merc Handler.h"
|
|
#include "strategic.h"
|
|
#include "message.h"
|
|
#include "Town Militia.h"
|
|
#include "Animation Data.h"
|
|
#include "Creature Spreading.h"
|
|
#include "Strategic AI.h"
|
|
#include "SkillCheck.h"
|
|
#include "rt time defines.h"
|
|
#include "morale.h"
|
|
#include "Strategic Town Loyalty.h"
|
|
#include "GameSettings.h"
|
|
#include "Soldier macros.h"
|
|
#include "strategicmap.h"
|
|
#include "Quests.h"
|
|
#include "meanwhile.h"
|
|
#include "Inventory Choosing.h"
|
|
#include "Game Event Hook.h"
|
|
#include "Assignments.h"
|
|
#include "cheats.h"
|
|
#include "Map Information.h"
|
|
#include "MilitiaSquads.h"
|
|
// #include "Strategic AI.h"
|
|
#include "interface Dialogue.h"
|
|
#include "AIInternals.h" // added by SANDRO
|
|
#include "Bullets.h" // HEADROCK HAM 5, for use with Bullet Impact.
|
|
#include "CampaignStats.h" // added by Flugente
|
|
#include "DynamicDialogue.h" // added by Flugente
|
|
#endif
|
|
|
|
#include "Reinforcement.h"
|
|
|
|
//#define INVULNERABILITY
|
|
|
|
// default = 7 (%)
|
|
#define EXP_BONUS 7
|
|
|
|
#define MAX_AR_TEAM_SIZE 255 // Must fit in 8 bits
|
|
|
|
#define REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR 1000
|
|
|
|
INT32 giMaxEnemiesToRender = 40;
|
|
INT32 giMaxMilitiaToRender = 20;//Changes depending on merc amount
|
|
|
|
extern UINT8 gubReinforcementMinEnemyStaticGroupSize;
|
|
extern BOOLEAN gfStrategicMilitiaChangesMade;
|
|
|
|
extern void ResetMilitia();
|
|
extern BOOLEAN AutoReload( SOLDIERTYPE *pSoldier );
|
|
extern HVSURFACE ghFrameBuffer;
|
|
BOOLEAN gfTransferTacticalOppositionToAutoResolve = FALSE;
|
|
|
|
//button images
|
|
enum
|
|
{
|
|
PAUSE_BUTTON,
|
|
PLAY_BUTTON,
|
|
FAST_BUTTON,
|
|
FINISH_BUTTON,
|
|
YES_BUTTON,
|
|
NO_BUTTON,
|
|
BANDAGE_BUTTON,
|
|
RETREAT_BUTTON,
|
|
DONEWIN_BUTTON,
|
|
DONELOSE_BUTTON,
|
|
NUM_AR_BUTTONS
|
|
};
|
|
|
|
|
|
typedef struct SOLDIERCELL
|
|
{
|
|
SOLDIERTYPE *pSoldier;
|
|
MOUSE_REGION *pRegion; //only used for player mercs.
|
|
UINT32 uiVObjectID;
|
|
UINT16 usIndex;
|
|
UINT32 uiFlags;
|
|
UINT16 usFrame;
|
|
INT16 xp, yp;
|
|
UINT16 usAttack, usDefence;
|
|
UINT32 usNextAttack;
|
|
UINT16 usNextHit[3];
|
|
UINT16 usHitDamage[3];
|
|
struct SOLDIERCELL *pAttacker[3];
|
|
UINT32 uiFlashTime;
|
|
INT8 bWeaponSlot;
|
|
}SOLDIERCELL;
|
|
|
|
typedef struct AUTORESOLVE_STRUCT
|
|
{
|
|
SOLDIERCELL *pRobotCell;
|
|
|
|
//IDs into the graphic images
|
|
INT32 iPanelImages;
|
|
INT32 iButton[ NUM_AR_BUTTONS ];
|
|
INT32 iButtonImage[ NUM_AR_BUTTONS ];
|
|
INT32 iFaces; //for generic civs and enemies
|
|
// WDS - make number of mercenaries, etc. be configurable
|
|
INT32 iMercFaces[CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS]; //for each merc face
|
|
INT32 iIndent;
|
|
INT32 iInterfaceBuffer;
|
|
INT32 iNumMercFaces;
|
|
INT32 iActualMercFaces; //this represents the real number of merc faces. Because
|
|
//my debug mode allows to freely add and subtract mercs, we
|
|
//can add/remove temp mercs, but we don't want to remove the
|
|
//actual mercs.
|
|
UINT32 uiTimeSlice;
|
|
UINT32 uiTotalElapsedBattleTimeInMilliseconds;
|
|
UINT32 uiPrevTime, uiCurrTime;
|
|
UINT32 uiStartExpanding;
|
|
UINT32 uiEndExpanding;
|
|
UINT32 uiPreRandomIndex;
|
|
|
|
SGPRect Rect, ExRect;
|
|
|
|
UINT32 usPlayerAttack;
|
|
UINT32 usPlayerDefence;
|
|
UINT32 usEnemyAttack;
|
|
UINT32 usEnemyDefence;
|
|
INT16 sWidth, sHeight;
|
|
INT16 sCenterStartX;
|
|
|
|
UINT8 ubEnemyLeadership;
|
|
UINT8 ubPlayerLeadership;
|
|
UINT8 ubMercs, ubCivs, ubEnemies;
|
|
UINT8 ubAdmins, ubTroops, ubElites, ubTanks;
|
|
UINT8 ubYMCreatures, ubYFCreatures, ubAMCreatures, ubAFCreatures;
|
|
UINT8 ubAliveMercs, ubAliveCivs, ubAliveEnemies;
|
|
UINT8 ubMercCols, ubMercRows;
|
|
UINT8 ubEnemyCols, ubEnemyRows;
|
|
UINT8 ubCivCols, ubCivRows;
|
|
UINT8 ubTimeModifierPercentage;
|
|
UINT8 ubSectorX, ubSectorY;
|
|
INT8 bVerticalOffset;
|
|
|
|
BOOLEAN fRenderAutoResolve;
|
|
BOOLEAN fExitAutoResolve;
|
|
BOOLEAN fPaused;
|
|
BOOLEAN fDebugInfo;
|
|
BOOLEAN ubBattleStatus;
|
|
BOOLEAN fUnlimitedAmmo;
|
|
BOOLEAN fSound;
|
|
BOOLEAN ubPlayerDefenceAdvantage;
|
|
BOOLEAN ubEnemyDefenceAdvantage;
|
|
BOOLEAN fInstantFinish;
|
|
BOOLEAN fAllowCapture;
|
|
BOOLEAN fPlayerRejectedSurrenderOffer;
|
|
BOOLEAN fPendingSurrender;
|
|
BOOLEAN fExpanding;
|
|
BOOLEAN fShowInterface;
|
|
BOOLEAN fEnteringAutoResolve;
|
|
BOOLEAN fMoraleEventsHandled;
|
|
BOOLEAN fCaptureNotPermittedDueToEPCs;
|
|
|
|
MOUSE_REGION AutoResolveRegion;
|
|
|
|
}AUTORESOLVE_STRUCT;
|
|
|
|
//Classifies the type of soldier the soldier cell is
|
|
#define CELL_MERC 0x00000001
|
|
#define CELL_MILITIA 0x00000002
|
|
#define CELL_ELITE 0x00000004
|
|
#define CELL_TROOP 0x00000008
|
|
#define CELL_ADMIN 0x00000010
|
|
#define CELL_AF_CREATURE 0x00000020
|
|
#define CELL_AM_CREATURE 0x00000040
|
|
#define CELL_YF_CREATURE 0x00000080
|
|
#define CELL_YM_CREATURE 0x00000100
|
|
//The team leader is the one with the highest leadership.
|
|
//There can only be one teamleader per side (mercs/civs and enemies)
|
|
#define CELL_TEAMLEADER 0x00000200
|
|
//Combat flags
|
|
#define CELL_FIREDATTARGET 0x00000400
|
|
#define CELL_DODGEDATTACK 0x00000800
|
|
#define CELL_HITBYATTACKER 0x00001000
|
|
#define CELL_HITLASTFRAME 0x00002000
|
|
//Cell statii
|
|
#define CELL_SHOWRETREATTEXT 0x00004000
|
|
#define CELL_RETREATING 0x00008000
|
|
#define CELL_RETREATED 0x00010000
|
|
#define CELL_DIRTY 0x00020000
|
|
#define CELL_PROCESSED 0x00040000
|
|
#define CELL_ASSIGNED 0x00080000
|
|
#define CELL_EPC 0x00100000
|
|
#define CELL_ROBOT 0x00200000
|
|
#define CELL_TANK 0x00400000
|
|
|
|
//Combined flags
|
|
#define CELL_PLAYER ( CELL_MERC | CELL_MILITIA )
|
|
#define CELL_ENEMY ( CELL_ELITE | CELL_TROOP | CELL_ADMIN | CELL_TANK )
|
|
#define CELL_CREATURE ( CELL_AF_CREATURE | CELL_AM_CREATURE | CELL_YF_CREATURE | CELL_YM_CREATURE )
|
|
#define CELL_FEMALECREATURE ( CELL_AF_CREATURE | CELL_YF_CREATURE )
|
|
#define CELL_MALECREATURE ( CELL_AM_CREATURE | CELL_YM_CREATURE )
|
|
#define CELL_YOUNGCREATURE ( CELL_YF_CREATURE | CELL_YM_CREATURE )
|
|
#define CELL_INVOLVEDINCOMBAT ( CELL_FIREDATTARGET | CELL_DODGEDATTACK | CELL_HITBYATTACKER )
|
|
|
|
enum
|
|
{
|
|
BATTLE_IN_PROGRESS,
|
|
BATTLE_VICTORY,
|
|
BATTLE_DEFEAT,
|
|
BATTLE_RETREAT,
|
|
BATTLE_SURRENDERED,
|
|
BATTLE_CAPTURED
|
|
};
|
|
|
|
//panel pieces
|
|
enum
|
|
{
|
|
TL_BORDER,
|
|
T_BORDER,
|
|
TR_BORDER,
|
|
L_BORDER,
|
|
C_TEXTURE,
|
|
R_BORDER,
|
|
BL_BORDER,
|
|
B_BORDER,
|
|
BR_BORDER,
|
|
TOP_MIDDLE,
|
|
AUTO_MIDDLE,
|
|
BOT_MIDDLE,
|
|
MERC_PANEL,
|
|
OTHER_PANEL,
|
|
};
|
|
|
|
//generic face images
|
|
enum
|
|
{
|
|
ADMIN_FACE,
|
|
TROOP_FACE,
|
|
ELITE_FACE,
|
|
TANK_FACE,
|
|
MILITIA1_FACE,
|
|
MILITIA2_FACE,
|
|
MILITIA3_FACE,
|
|
YM_CREATURE_FACE,
|
|
AM_CREATURE_FACE,
|
|
YF_CREATURE_FACE,
|
|
AF_CREATURE_FACE,
|
|
HUMAN_SKULL,
|
|
TANK_WRECK,
|
|
CREATURE_SKULL,
|
|
ELITEF_FACE,
|
|
MILITIA1F_FACE,
|
|
MILITIA2F_FACE,
|
|
MILITIA3F_FACE,
|
|
};
|
|
|
|
extern void CreateDestroyMapInvButton();
|
|
|
|
//Autoresolve sets this variable which defaults to -1 when not needed.
|
|
INT16 gsEnemyGainedControlOfSectorID = -1;
|
|
INT16 gsCiviliansEatenByMonsters = -1;
|
|
|
|
BOOLEAN ProcessLoyalty();
|
|
//Autoresolve handling -- keyboard input, callbacks
|
|
void HandleAutoResolveInput();
|
|
void PauseButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void PlayButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void FastButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void FinishButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void RetreatButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void BandageButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void DoneButtonCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void MercCellMouseMoveCallback( MOUSE_REGION *reg, INT32 reason );
|
|
void MercCellMouseClickCallback( MOUSE_REGION *reg, INT32 reason );
|
|
|
|
void DetermineBandageButtonState();
|
|
|
|
//Surrender interface
|
|
void SetupDoneInterface();
|
|
void SetupSurrenderInterface();
|
|
void HideSurrenderInterface();
|
|
void AcceptSurrenderCallback( GUI_BUTTON *btn, INT32 reason );
|
|
void RejectSurrenderCallback( GUI_BUTTON *btn, INT32 reason );
|
|
|
|
//Precalculations for interface positioning and the calculation routines to do so.
|
|
void CalculateAutoResolveInfo();
|
|
void CalculateSoldierCells( BOOLEAN fReset );
|
|
void CalculateRowsAndColumns();
|
|
void CreateAutoResolveInterface();
|
|
void RemoveAutoResolveInterface( BOOLEAN fDeleteForGood );
|
|
INT32 CalcIndexFromColRowsXY( INT32 iMaxCols, INT32 iMaxRows, INT32 iCol, INT32 iRow );
|
|
|
|
//Battle system routines
|
|
void DetermineTeamLeader( BOOLEAN fFriendlyTeam );
|
|
void CalculateAttackValues();
|
|
void ProcessBattleFrame();
|
|
BOOLEAN IsBattleOver();
|
|
BOOLEAN AttemptPlayerCapture();
|
|
|
|
void AutoBandageFinishedCallback( UINT8 ubResult );
|
|
|
|
//Debug utilities
|
|
void ResetAutoResolveInterface();
|
|
void CreateTempPlayerMerc();
|
|
void DrawDebugText( SOLDIERCELL *pCell );
|
|
|
|
//Rendering routines
|
|
void RenderAutoResolve();
|
|
void RenderSoldierCellHealth( SOLDIERCELL *pCell );
|
|
void RenderSoldierCell( SOLDIERCELL *pCell );
|
|
void RenderSoldierCellBars( SOLDIERCELL *pCell );
|
|
|
|
// HEADROCK HAM 3.4: New argument configuration to make use of revamped movement controls
|
|
void GenerateDirectionInfos( INT16 sMapX, INT16 sMapY, UINT8* uiDirNumber, UINT16 pMoveDir[4][3], BOOLEAN fForTraining, BOOLEAN fForBattle );
|
|
|
|
|
|
//Dynamic globals -- to conserve memory, all global variables are allocated upon entry
|
|
//and deleted before we leave.
|
|
AUTORESOLVE_STRUCT *gpAR = NULL;
|
|
SOLDIERCELL *gpMercs = NULL;
|
|
SOLDIERCELL *gpCivs = NULL;
|
|
SOLDIERCELL *gpEnemies = NULL;
|
|
|
|
//Simple wrappers for autoresolve sounds that are played.
|
|
void PlayAutoResolveSample( UINT32 usNum, UINT32 usRate, UINT32 ubVolume, UINT32 ubLoops, UINT32 uiPan )
|
|
{
|
|
if( gpAR->fSound )
|
|
{
|
|
PlayJA2Sample( usNum, usRate, ubVolume, ubLoops, uiPan );
|
|
}
|
|
}
|
|
|
|
void PlayAutoResolveSampleFromFile( STR8 szFileName, UINT32 usRate, UINT32 ubVolume, UINT32 ubLoops, UINT32 uiPan )
|
|
{
|
|
if( gpAR->fSound )
|
|
{
|
|
PlayJA2SampleFromFile( szFileName, usRate, ubVolume, ubLoops, uiPan );
|
|
}
|
|
}
|
|
|
|
|
|
extern void ClearPreviousAIGroupAssignment( GROUP *pGroup );
|
|
|
|
void EliminateAllMercs()
|
|
{
|
|
SOLDIERCELL *pAttacker = NULL;
|
|
INT32 i, iNum = 0;
|
|
if( gpAR )
|
|
{
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( gpEnemies[ i ].pSoldier->stats.bLife )
|
|
{
|
|
pAttacker = &gpEnemies[ i ];
|
|
break;
|
|
}
|
|
}
|
|
if( pAttacker )
|
|
{
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->stats.bLife )
|
|
{
|
|
iNum++;
|
|
gpMercs[ i ].pSoldier->stats.bLife = 1;
|
|
gpMercs[ i ].usNextHit[0] = (UINT16)(250 * iNum);
|
|
gpMercs[ i ].usHitDamage[0] = 100;
|
|
gpMercs[ i ].pAttacker[0] = pAttacker;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EliminateAllFriendlies()
|
|
{
|
|
INT32 i;
|
|
if( gpAR )
|
|
{
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
gpMercs[ i ].pSoldier->stats.bLife = 0;
|
|
}
|
|
gpAR->ubAliveMercs = 0;
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
{
|
|
gpCivs[ i ].pSoldier->stats.bLife = 0;
|
|
}
|
|
gpAR->ubAliveCivs = 0;
|
|
}
|
|
}
|
|
|
|
void EliminateAllEnemies( UINT8 ubSectorX, UINT8 ubSectorY )
|
|
{
|
|
GROUP *pGroup, *pDeleteGroup;
|
|
SECTORINFO *pSector;
|
|
INT32 i;
|
|
UINT8 ubNumEnemies[ NUM_ENEMY_RANKS ];
|
|
UINT8 ubRankIndex;
|
|
|
|
//Clear any possible battle locator
|
|
gfBlitBattleSectorLocator = FALSE;
|
|
|
|
pGroup = gpGroupList;
|
|
pSector = &SectorInfo[ SECTOR( ubSectorX, ubSectorY ) ];
|
|
|
|
UINT8 ubNumTanks = 0;
|
|
|
|
// if we're doing this from the Pre-Battle interface, gpAR is NULL, and RemoveAutoResolveInterface(0 won't happen, so
|
|
// we must process the enemies killed right here & give out loyalty bonuses as if the battle had been fought & won
|
|
if( !gpAR )
|
|
{
|
|
GetNumberOfEnemiesInSector( ubSectorX, ubSectorY, &ubNumEnemies[ 0 ], &ubNumEnemies[ 1 ], &ubNumEnemies[ 2 ], &ubNumTanks );
|
|
|
|
for ( ubRankIndex = 0; ubRankIndex < NUM_ENEMY_RANKS; ++ubRankIndex )
|
|
{
|
|
for ( i = 0; i < ubNumEnemies[ ubRankIndex ]; ++i )
|
|
{
|
|
if( ProcessLoyalty() )
|
|
HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ENEMY_KILLED, ubSectorX, ubSectorY, 0 );
|
|
|
|
TrackEnemiesKilled( ENEMY_KILLED_IN_AUTO_RESOLVE, RankIndexToSoldierClass( ubRankIndex ) );
|
|
}
|
|
}
|
|
|
|
if ( ProcessLoyalty( ) )
|
|
{
|
|
for ( i = 0; i < ubNumTanks; ++i )
|
|
{
|
|
HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ENEMY_KILLED, ubSectorX, ubSectorY, 0 );
|
|
}
|
|
|
|
HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_BATTLE_WON, ubSectorX, ubSectorY, 0 );
|
|
}
|
|
}
|
|
|
|
if( !gpAR || gpAR->ubBattleStatus != BATTLE_IN_PROGRESS )
|
|
{
|
|
//Remove the defend force here.
|
|
pSector->ubNumTroops = 0;
|
|
pSector->ubNumElites = 0;
|
|
pSector->ubNumAdmins = 0;
|
|
pSector->ubNumTanks = 0;
|
|
pSector->ubNumCreatures = 0;
|
|
pSector->bLastKnownEnemies = 0;
|
|
//Remove the mobile forces here, but only if battle is over.
|
|
while( pGroup )
|
|
{
|
|
if ( pGroup->usGroupTeam != OUR_TEAM && pGroup->ubSectorX == ubSectorX && pGroup->ubSectorY == ubSectorY )
|
|
{
|
|
ClearPreviousAIGroupAssignment( pGroup );
|
|
pDeleteGroup = pGroup;
|
|
pGroup = pGroup->next;
|
|
if( gpBattleGroup == pDeleteGroup )
|
|
gpBattleGroup = NULL;
|
|
RemovePGroup( pDeleteGroup );
|
|
}
|
|
else
|
|
pGroup = pGroup->next;
|
|
}
|
|
if( gpBattleGroup )
|
|
{
|
|
CalculateNextMoveIntention( gpBattleGroup );
|
|
}
|
|
// set this sector as taken over
|
|
SetThisSectorAsPlayerControlled( ubSectorX, ubSectorY, 0, TRUE );
|
|
RecalculateSectorWeight( (UINT8)SECTOR( ubSectorX, ubSectorY ) );
|
|
|
|
// dirty map panel
|
|
fMapPanelDirty = TRUE;
|
|
}
|
|
|
|
if( gpAR )
|
|
{
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
gpEnemies[ i ].pSoldier->stats.bLife = 0;
|
|
}
|
|
gpAR->ubAliveEnemies = 0;
|
|
}
|
|
gpBattleGroup = NULL;
|
|
}
|
|
|
|
#define ORIG_LEFT 26
|
|
#define ORIG_TOP 53
|
|
#define ORIG_RIGHT 92
|
|
#define ORIG_BOTTOM 84
|
|
|
|
void DoTransitionFromPreBattleInterfaceToAutoResolve()
|
|
{
|
|
SGPRect SrcRect, DstRect;
|
|
UINT32 uiStartTime, uiCurrTime;
|
|
INT32 iPercentage, iFactor;
|
|
UINT32 uiTimeRange;
|
|
INT16 sStartLeft, sEndLeft, sStartTop, sEndTop;
|
|
INT32 iLeft, iTop, iWidth, iHeight;
|
|
|
|
PauseTime( FALSE );
|
|
|
|
gpAR->fShowInterface = TRUE;
|
|
|
|
SrcRect.iLeft = gpAR->Rect.iLeft;
|
|
SrcRect.iTop = gpAR->Rect.iTop;
|
|
SrcRect.iRight = gpAR->Rect.iRight;
|
|
SrcRect.iBottom = gpAR->Rect.iBottom;
|
|
|
|
iWidth = SrcRect.iRight - SrcRect.iLeft + 1;
|
|
iHeight = SrcRect.iBottom - SrcRect.iTop + 1;
|
|
|
|
uiTimeRange = 1000;
|
|
iPercentage = 0;
|
|
uiStartTime = GetJA2Clock();
|
|
|
|
sStartLeft = 59 + xResOffset;
|
|
sStartTop = 69 + yResOffset;
|
|
sEndLeft = SrcRect.iLeft + gpAR->sWidth / 2;
|
|
sEndTop = SrcRect.iTop + gpAR->sHeight / 2;
|
|
|
|
//save the prebattle/mapscreen interface background
|
|
BlitBufferToBuffer( FRAME_BUFFER, guiEXTRABUFFER, 0 + xResOffset, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
|
|
|
|
//render the autoresolve panel
|
|
RenderAutoResolve();
|
|
RenderButtons();
|
|
RenderButtonsFastHelp();
|
|
//save it
|
|
BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, (UINT16)SrcRect.iLeft, (UINT16)SrcRect.iTop, (UINT16)SrcRect.iRight, (UINT16)SrcRect.iBottom );
|
|
|
|
//hide the autoresolve
|
|
BlitBufferToBuffer( guiEXTRABUFFER, FRAME_BUFFER, (UINT16)SrcRect.iLeft, (UINT16)SrcRect.iTop, (UINT16)SrcRect.iRight, (UINT16)SrcRect.iBottom );
|
|
|
|
PlayJA2SampleFromFile( "SOUNDS\\Laptop power up (8-11).wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
while( iPercentage < 100 )
|
|
{
|
|
uiCurrTime = GetJA2Clock();
|
|
iPercentage = (uiCurrTime-uiStartTime) * 100 / uiTimeRange;
|
|
iPercentage = min( iPercentage, 100 );
|
|
|
|
//Factor the percentage so that it is modified by a gravity falling acceleration effect.
|
|
iFactor = (iPercentage - 50) * 2;
|
|
if( iPercentage < 50 )
|
|
iPercentage = (UINT32)(iPercentage + iPercentage * iFactor * 0.01 + 0.5);
|
|
else
|
|
iPercentage = (UINT32)(iPercentage + (100-iPercentage) * iFactor * 0.01 + 0.05);
|
|
|
|
//Calculate the center point.
|
|
iLeft = sStartLeft + (sEndLeft-sStartLeft+1) * iPercentage / 100;
|
|
iTop = sStartTop + (sEndTop-sStartTop+1) * iPercentage / 100;
|
|
|
|
DstRect.iLeft = iLeft - iWidth * iPercentage / 200;
|
|
DstRect.iRight = DstRect.iLeft + max( iWidth * iPercentage / 100, 1 );
|
|
DstRect.iTop = iTop - iHeight * iPercentage / 200;
|
|
DstRect.iBottom = DstRect.iTop + max( iHeight * iPercentage / 100, 1 );
|
|
|
|
BltStretchVideoSurface( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, 0, &SrcRect, &DstRect );
|
|
InvalidateScreen();
|
|
RefreshScreen( NULL );
|
|
|
|
//Restore the previous rect.
|
|
BlitBufferToBuffer( guiEXTRABUFFER, FRAME_BUFFER, (UINT16)DstRect.iLeft, (UINT16)DstRect.iTop,
|
|
(UINT16)(DstRect.iRight-DstRect.iLeft+1), (UINT16)(DstRect.iBottom-DstRect.iTop+1) );
|
|
}
|
|
//BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, 640, 480 );
|
|
}
|
|
|
|
extern UINT8 guiDirNumber;
|
|
|
|
void EnterAutoResolveMode( UINT8 ubSectorX, UINT8 ubSectorY )
|
|
{
|
|
guiDirNumber = 0;
|
|
|
|
//Set up mapscreen for removal
|
|
SetPendingNewScreen( AUTORESOLVE_SCREEN );
|
|
CreateDestroyMapInvButton();
|
|
RenderButtons();
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve1");
|
|
// WDS - make number of mercenaries, etc. be configurable
|
|
//Allocate memory for all the globals while we are in this mode.
|
|
gpAR = (AUTORESOLVE_STRUCT*)MemAlloc( sizeof( AUTORESOLVE_STRUCT ) );
|
|
Assert( gpAR );
|
|
memset( gpAR, 0, sizeof( AUTORESOLVE_STRUCT ) );
|
|
//Mercs
|
|
gpMercs = (SOLDIERCELL*)MemAlloc( sizeof( SOLDIERCELL) * MAX_AR_TEAM_SIZE );
|
|
Assert( gpMercs );
|
|
memset( gpMercs, 0, sizeof( SOLDIERCELL ) * MAX_AR_TEAM_SIZE );
|
|
//Militia
|
|
gpCivs = (SOLDIERCELL*)MemAlloc( sizeof( SOLDIERCELL) * MAX_AR_TEAM_SIZE );
|
|
Assert( gpCivs );
|
|
memset( gpCivs, 0, sizeof( SOLDIERCELL ) * MAX_AR_TEAM_SIZE );
|
|
//Enemies
|
|
gpEnemies = (SOLDIERCELL*)MemAlloc( sizeof( SOLDIERCELL) * MAX_AR_TEAM_SIZE );
|
|
Assert( gpEnemies );
|
|
memset( gpEnemies, 0, sizeof( SOLDIERCELL ) * MAX_AR_TEAM_SIZE );
|
|
|
|
//Set up autoresolve
|
|
gpAR->fEnteringAutoResolve = TRUE;
|
|
gpAR->ubSectorX = ubSectorX;
|
|
gpAR->ubSectorY = ubSectorY;
|
|
gpAR->ubBattleStatus = BATTLE_IN_PROGRESS;
|
|
gpAR->uiTimeSlice = 1000;
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds = 0;
|
|
gpAR->fSound = TRUE;
|
|
gpAR->fMoraleEventsHandled = FALSE;
|
|
gpAR->uiPreRandomIndex = guiPreRandomIndex;
|
|
|
|
//Determine who gets the defensive advantage
|
|
switch( gubEnemyEncounterCode )
|
|
{
|
|
case ENEMY_ENCOUNTER_CODE:
|
|
gpAR->ubPlayerDefenceAdvantage = 21; //Skewed to the player's advantage for convenience purposes.
|
|
break;
|
|
case ENEMY_INVASION_CODE:
|
|
gpAR->ubPlayerDefenceAdvantage = 0;
|
|
break;
|
|
case CREATURE_ATTACK_CODE:
|
|
gpAR->ubPlayerDefenceAdvantage = 0;
|
|
break;
|
|
default:
|
|
//shouldn't happen
|
|
#ifdef JA2BETAVERSION
|
|
ScreenMsg( FONT_RED, MSG_ERROR, L"Autoresolving with entering enemy sector code %d -- illegal KM:1", gubEnemyEncounterCode );
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
UINT32 AutoResolveScreenInit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
UINT32 AutoResolveScreenShutdown()
|
|
{
|
|
gpBattleGroup = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
UINT32 AutoResolveScreenHandle()
|
|
{
|
|
RestoreBackgroundRects();
|
|
|
|
if( !gpAR )
|
|
{
|
|
gfEnteringMapScreen = TRUE;
|
|
return MAP_SCREEN;
|
|
}
|
|
if( gpAR->fEnteringAutoResolve )
|
|
{
|
|
UINT8 *pDestBuf;
|
|
UINT32 uiDestPitchBYTES;
|
|
SGPRect ClipRect;
|
|
gpAR->fEnteringAutoResolve = FALSE;
|
|
//Take the framebuffer, shade it, and save it to the SAVEBUFFER.
|
|
ClipRect.iLeft = 0 + xResOffset;
|
|
ClipRect.iTop = 0;
|
|
/*ClipRect.iRight = 640;
|
|
ClipRect.iBottom = 480;*/
|
|
ClipRect.iRight = SCREEN_WIDTH;
|
|
ClipRect.iBottom = SCREEN_HEIGHT;
|
|
|
|
pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
|
|
Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect );
|
|
UnLockVideoSurface( FRAME_BUFFER );
|
|
//BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, 640, 480 );
|
|
BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0 + xResOffset, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
|
|
KillPreBattleInterface();
|
|
CalculateAutoResolveInfo();
|
|
CalculateSoldierCells( FALSE );
|
|
CreateAutoResolveInterface();
|
|
DetermineTeamLeader( TRUE ); //friendly team
|
|
DetermineTeamLeader( FALSE ); //enemy team
|
|
CalculateAttackValues();
|
|
if( gfExtraBuffer )
|
|
{
|
|
DoTransitionFromPreBattleInterfaceToAutoResolve();
|
|
}
|
|
else
|
|
gpAR->fExpanding = TRUE;
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
}
|
|
if( gpAR->fExitAutoResolve )
|
|
{
|
|
gfEnteringMapScreen = TRUE;
|
|
RemoveAutoResolveInterface( TRUE );
|
|
return MAP_SCREEN;
|
|
}
|
|
if( gpAR->fPendingSurrender )
|
|
{
|
|
gpAR->uiPrevTime = gpAR->uiCurrTime = GetJA2Clock();
|
|
|
|
}
|
|
else if( gpAR->ubBattleStatus == BATTLE_IN_PROGRESS && !gpAR->fExpanding )
|
|
{
|
|
ProcessBattleFrame();
|
|
}
|
|
HandleAutoResolveInput();
|
|
RenderAutoResolve();
|
|
|
|
|
|
SaveBackgroundRects();
|
|
RenderButtons();
|
|
RenderButtonsFastHelp();
|
|
ExecuteBaseDirtyRectQueue();
|
|
EndFrameBufferRender();
|
|
return AUTORESOLVE_SCREEN;
|
|
}
|
|
|
|
void RefreshMerc( SOLDIERTYPE *pSoldier )
|
|
{
|
|
pSoldier->stats.bLife = pSoldier->stats.bLifeMax;
|
|
pSoldier->bBleeding = 0;
|
|
pSoldier->bPoisonSum = 0;
|
|
pSoldier->bPoisonLife = 0;
|
|
pSoldier->bPoisonBleeding = 0;
|
|
pSoldier->iHealableInjury = 0; // added by SANDRO
|
|
pSoldier->bBreath = pSoldier->bBreathMax = 100;
|
|
pSoldier->sBreathRed = 0;
|
|
if( gpAR->pRobotCell)
|
|
{
|
|
gpAR->pRobotCell->pSoldier->UpdateRobotControllerGivenRobot( );
|
|
}
|
|
//gpAR->fUnlimitedAmmo = TRUE;
|
|
}
|
|
|
|
//Now assign the pSoldier->ubGroupIDs for the enemies, so we know where to remove them. Start with
|
|
//stationary groups first.
|
|
void AssociateEnemiesWithStrategicGroups()
|
|
{
|
|
SECTORINFO *pSector;
|
|
GROUP *pGroup;
|
|
UINT8 ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks;
|
|
UINT8 ubISNumAdmins, ubISNumTroops, ubISNumElites, ubISNumTanks;
|
|
UINT8 ubNumElitesInGroup, ubNumTroopsInGroup, ubNumAdminsInGroup, ubNumTanksInGroup;
|
|
INT32 i;
|
|
UINT8 pSectors[4];
|
|
UINT8 ubDirAmount;
|
|
UINT8 ubCurrSI;
|
|
|
|
if( gubEnemyEncounterCode == CREATURE_ATTACK_CODE )
|
|
return;
|
|
|
|
pSector = &SectorInfo[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ];
|
|
|
|
//grab the number of each type in the stationary sector
|
|
ubNumAdmins = pSector->ubNumAdmins;
|
|
ubNumTroops = pSector->ubNumTroops;
|
|
ubNumElites = pSector->ubNumElites;
|
|
ubNumTanks = pSector->ubNumTanks;
|
|
|
|
//Now go through our enemies in the autoresolve array, and assign the ubGroupID to the soldier
|
|
//Stationary groups have a group ID of 0
|
|
for( i = 0; i < gpAR->ubEnemies; ++i )
|
|
{
|
|
if ( gpEnemies[i].uiFlags & CELL_TANK && ubNumTanks )
|
|
{
|
|
gpEnemies[i].pSoldier->ubGroupID = 0;
|
|
gpEnemies[i].uiFlags |= CELL_ASSIGNED;
|
|
ubNumTanks--;
|
|
}
|
|
else if ( gpEnemies[i].uiFlags & CELL_ELITE && ubNumElites )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = 0;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumElites--;
|
|
}
|
|
else if( gpEnemies[ i ].uiFlags & CELL_TROOP && ubNumTroops )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = 0;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumTroops--;
|
|
}
|
|
else if( gpEnemies[ i ].uiFlags & CELL_ADMIN && ubNumAdmins )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = 0;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumAdmins--;
|
|
}
|
|
}
|
|
|
|
ubNumAdmins = gpAR->ubAdmins - pSector->ubNumAdmins;
|
|
ubNumTroops = gpAR->ubTroops - pSector->ubNumTroops;
|
|
ubNumElites = gpAR->ubElites - pSector->ubNumElites;
|
|
ubNumTanks = gpAR->ubTanks - pSector->ubNumTanks;
|
|
|
|
if( !ubNumElites && !ubNumTroops && !ubNumAdmins && !ubNumTanks )
|
|
{ //All troops accounted for.
|
|
return;
|
|
}
|
|
|
|
//Now assign the rest of the soldiers to groups
|
|
pGroup = gpGroupList;
|
|
while( pGroup )
|
|
{
|
|
if ( pGroup->usGroupTeam == ENEMY_TEAM && pGroup->ubSectorX == gpAR->ubSectorX && pGroup->ubSectorY == gpAR->ubSectorY )
|
|
{
|
|
ubNumElitesInGroup = pGroup->pEnemyGroup->ubNumElites;
|
|
ubNumTroopsInGroup = pGroup->pEnemyGroup->ubNumTroops;
|
|
ubNumAdminsInGroup = pGroup->pEnemyGroup->ubNumAdmins;
|
|
ubNumTanksInGroup = pGroup->pEnemyGroup->ubNumTanks;
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( !(gpEnemies[ i ].uiFlags & CELL_ASSIGNED) )
|
|
{
|
|
if( ubNumTanks && ubNumTanksInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumTanks--;
|
|
ubNumTanksInGroup--;
|
|
}
|
|
else if( ubNumElites && ubNumElitesInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumElites--;
|
|
ubNumElitesInGroup--;
|
|
}
|
|
else if( ubNumTroops && ubNumTroopsInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumTroops--;
|
|
ubNumTroopsInGroup--;
|
|
}
|
|
else if( ubNumAdmins && ubNumAdminsInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumAdmins--;
|
|
ubNumAdminsInGroup--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pGroup = pGroup->next;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////// Reinforcement
|
|
|
|
pGroup = gpGroupList;
|
|
while( pGroup )
|
|
{
|
|
// Don't process road block. It'll be processed as static
|
|
if ( pGroup->ubGroupID && pGroup->usGroupTeam == ENEMY_TEAM && IsGroupInARightSectorToReinforce( pGroup, gpAR->ubSectorX, gpAR->ubSectorY ) )
|
|
{
|
|
ubNumElitesInGroup = pGroup->pEnemyGroup->ubNumElites;
|
|
ubNumTroopsInGroup = pGroup->pEnemyGroup->ubNumTroops;
|
|
ubNumAdminsInGroup = pGroup->pEnemyGroup->ubNumAdmins;
|
|
ubNumTanksInGroup = pGroup->pEnemyGroup->ubNumTanks;
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( !(gpEnemies[ i ].uiFlags & CELL_ASSIGNED) )
|
|
{
|
|
if ( ubNumTanks && ubNumTanksInGroup )
|
|
{
|
|
gpEnemies[i].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[i].uiFlags |= CELL_ASSIGNED;
|
|
ubNumTanks--;
|
|
ubNumTanksInGroup--;
|
|
}
|
|
else if ( ubNumElites && ubNumElitesInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumElites--;
|
|
ubNumElitesInGroup--;
|
|
}
|
|
else if( ubNumTroops && ubNumTroopsInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumTroops--;
|
|
ubNumTroopsInGroup--;
|
|
}
|
|
else if( ubNumAdmins && ubNumAdminsInGroup )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = pGroup->ubGroupID;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
ubNumAdmins--;
|
|
ubNumAdminsInGroup--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pGroup = pGroup->next;
|
|
}
|
|
|
|
// Set GroupID = 0 for the rest
|
|
ubDirAmount = GetAdjacentSectors( pSectors, gpAR->ubSectorX, gpAR->ubSectorY );
|
|
|
|
for( ubCurrSI = 0; ubCurrSI < ubDirAmount; ++ubCurrSI )
|
|
{
|
|
pSector = &SectorInfo[ pSectors[ ubCurrSI ] ];
|
|
|
|
ubISNumAdmins = pSector->ubNumAdmins;
|
|
ubISNumTroops = pSector->ubNumTroops;
|
|
ubISNumElites = pSector->ubNumElites;
|
|
ubISNumTanks = pSector->ubNumTanks;
|
|
|
|
for( i = 0; i < gpAR->ubEnemies; ++i )
|
|
{
|
|
if( ubISNumAdmins + ubISNumTroops + ubISNumElites + ubISNumTanks <= gubReinforcementMinEnemyStaticGroupSize ) break;
|
|
|
|
if( !(gpEnemies[ i ].uiFlags & CELL_ASSIGNED) )
|
|
{
|
|
if ( gpEnemies[i].uiFlags & CELL_TANK && ubISNumTanks && ubNumTanks )
|
|
{
|
|
gpEnemies[i].pSoldier->ubGroupID = 0;
|
|
gpEnemies[i].uiFlags |= CELL_ASSIGNED;
|
|
gpEnemies[i].pSoldier->sSectorX = SECTORX( pSectors[ubCurrSI] );
|
|
gpEnemies[i].pSoldier->sSectorY = SECTORY( pSectors[ubCurrSI] );
|
|
ubISNumTanks--;
|
|
ubNumTanks--;
|
|
}
|
|
else if ( gpEnemies[i].uiFlags & CELL_ELITE && ubISNumElites && ubNumElites )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = 0;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
gpEnemies[ i ].pSoldier->sSectorX = SECTORX( pSectors[ ubCurrSI ] );
|
|
gpEnemies[ i ].pSoldier->sSectorY = SECTORY( pSectors[ ubCurrSI ] );
|
|
ubISNumElites--;
|
|
ubNumElites--;
|
|
}
|
|
else if( gpEnemies[ i ].uiFlags & CELL_TROOP && ubISNumTroops && ubNumTroops )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = 0;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
gpEnemies[ i ].pSoldier->sSectorX = SECTORX( pSectors[ ubCurrSI ] );
|
|
gpEnemies[ i ].pSoldier->sSectorY = SECTORY( pSectors[ ubCurrSI ] );
|
|
ubISNumTroops--;
|
|
ubNumTroops--;
|
|
}
|
|
else if( gpEnemies[ i ].uiFlags & CELL_ADMIN && ubISNumAdmins && ubNumAdmins )
|
|
{
|
|
gpEnemies[ i ].pSoldier->ubGroupID = 0;
|
|
gpEnemies[ i ].uiFlags |= CELL_ASSIGNED;
|
|
gpEnemies[ i ].pSoldier->sSectorX = SECTORX( pSectors[ ubCurrSI ] );
|
|
gpEnemies[ i ].pSoldier->sSectorY = SECTORY( pSectors[ ubCurrSI ] );
|
|
ubISNumAdmins--;
|
|
ubNumAdmins--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CalculateSoldierCells( BOOLEAN fReset )
|
|
{
|
|
INT32 i, x, y;
|
|
INT32 index, iStartY, iTop, gapStartRow;
|
|
INT32 iMaxTeamSize;
|
|
|
|
gpAR->ubAliveMercs = gpAR->ubMercs;
|
|
gpAR->ubAliveCivs = gpAR->ubCivs;
|
|
gpAR->ubAliveEnemies = gpAR->ubEnemies;
|
|
|
|
// WDS - make number of mercenaries, etc. be configurable
|
|
// WDS TO BE FIXED -- Why is this 40 here? (18 April 2008)
|
|
//iMaxTeamSize = max( gpAR->ubMercs + gpAR->ubCivs, gpAR->ubEnemies );
|
|
iMaxTeamSize = max( min( 40, gpAR->ubMercs + gpAR->ubCivs ), min( 40, gpAR->ubEnemies ) );
|
|
|
|
|
|
if( iMaxTeamSize > 12 )
|
|
{
|
|
gpAR->ubTimeModifierPercentage = (UINT8)(118 - iMaxTeamSize*1.5);
|
|
}
|
|
else
|
|
{
|
|
gpAR->ubTimeModifierPercentage = 100;
|
|
}
|
|
gpAR->uiTimeSlice = gpAR->uiTimeSlice * gpAR->ubTimeModifierPercentage / 100;
|
|
|
|
iTop = iScreenHeightOffset + (240 - gpAR->sHeight/2);
|
|
if( iTop > (iScreenHeightOffset + 120) )
|
|
iTop -= 40;
|
|
|
|
if( gpAR->ubMercs )
|
|
{
|
|
iStartY = iTop + (gpAR->sHeight - (min(10,gpAR->ubMercRows+gpAR->ubCivRows)*47+7))/2 + 6;
|
|
//iStartY = iTop + (gpAR->sHeight - ((gpAR->ubMercRows+gpAR->ubCivRows)*47+7))/2 + 6;
|
|
y = gpAR->ubMercRows;
|
|
x = gpAR->ubMercCols;
|
|
i = gpAR->ubMercs;
|
|
gapStartRow = gpAR->ubMercRows - gpAR->ubMercRows * gpAR->ubMercCols + gpAR->ubMercs;
|
|
for( x = 0; x < gpAR->ubMercCols; x++ ) for( y = 0; i && y < gpAR->ubMercRows; y++, i-- )
|
|
{
|
|
index = y * gpAR->ubMercCols + gpAR->ubMercCols - x - 1;
|
|
if( y >= gapStartRow )
|
|
index -= y - gapStartRow + 1;
|
|
Assert( index >= 0 && index < gpAR->ubMercs );
|
|
|
|
gpMercs[ index ].xp = gpAR->sCenterStartX + 3 - 55*(x+1);
|
|
gpMercs[ index ].yp = iStartY + y*47;
|
|
gpMercs[ index ].uiFlags = CELL_MERC;
|
|
if( AM_AN_EPC( gpMercs[ index ].pSoldier ) )
|
|
{
|
|
if( AM_A_ROBOT( gpMercs[ index ].pSoldier ) )
|
|
{ //treat robot as a merc for the purpose of combat.
|
|
gpMercs[ index ].uiFlags |= CELL_ROBOT;
|
|
}
|
|
else
|
|
{
|
|
gpMercs[ index ].uiFlags |= CELL_EPC;
|
|
}
|
|
}
|
|
gpMercs[ index ].pRegion = (MOUSE_REGION*)MemAlloc( sizeof( MOUSE_REGION ) );
|
|
Assert( gpMercs[ index ].pRegion );
|
|
memset( gpMercs[ index ].pRegion, 0, sizeof( MOUSE_REGION ) );
|
|
MSYS_DefineRegion( gpMercs[ index ].pRegion, gpMercs[ index ].xp, gpMercs[ index ].yp,
|
|
(UINT16)(gpMercs[ index ].xp + 50), (UINT16)(gpMercs[ index ].yp + 44), MSYS_PRIORITY_HIGH, 0,
|
|
MercCellMouseMoveCallback, MercCellMouseClickCallback );
|
|
if( fReset )
|
|
RefreshMerc( gpMercs[ index ].pSoldier );
|
|
if( !gpMercs[ index ].pSoldier->stats.bLife )
|
|
gpAR->ubAliveMercs--;
|
|
}
|
|
}
|
|
if( gpAR->ubCivs )
|
|
{
|
|
iStartY = iTop + (gpAR->sHeight - (min(10,gpAR->ubMercRows+gpAR->ubCivRows)*47+7))/2 + gpAR->ubMercRows*47 + 5;
|
|
y = gpAR->ubCivRows;
|
|
x = gpAR->ubCivCols;
|
|
i = gpAR->ubCivs;
|
|
|
|
for( index = 0; index < gpAR->ubCivs ; ++index )
|
|
{
|
|
x = gpAR->ubCivCols - 1 - index % gpAR->ubCivCols;
|
|
y = index / gpAR->ubCivCols;
|
|
|
|
gpCivs[ index ].xp = gpAR->sCenterStartX + 3 - 55*(x+1);
|
|
gpCivs[ index ].yp = iStartY + y*47;
|
|
gpCivs[ index ].uiFlags |= CELL_MILITIA;
|
|
}
|
|
}
|
|
if( gpAR->ubEnemies )
|
|
{
|
|
iStartY = iTop + (gpAR->sHeight - (min(10,gpAR->ubEnemyRows)*47+7))/2 + 5;
|
|
y = gpAR->ubEnemyRows;
|
|
x = gpAR->ubEnemyCols;
|
|
i = gpAR->ubEnemies;
|
|
|
|
for( index = 0; index < gpAR->ubEnemies ; ++index )
|
|
{
|
|
x = index % gpAR->ubEnemyCols;
|
|
y = index / gpAR->ubEnemyCols;
|
|
|
|
gpEnemies[ index ].xp = (UINT16)(gpAR->sCenterStartX + 141 + 55*x);
|
|
gpEnemies[ index ].yp = iStartY + y*47;
|
|
|
|
if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
|
|
{
|
|
if ( index < gpAR->ubTanks )
|
|
gpEnemies[index].uiFlags = CELL_TANK;
|
|
else if ( index < gpAR->ubTanks + gpAR->ubElites )
|
|
gpEnemies[ index ].uiFlags = CELL_ELITE;
|
|
else if ( index < gpAR->ubTanks + gpAR->ubElites + gpAR->ubTroops )
|
|
gpEnemies[ index ].uiFlags = CELL_TROOP;
|
|
else
|
|
gpEnemies[index].uiFlags = CELL_ADMIN;
|
|
}
|
|
else
|
|
{
|
|
if( index < gpAR->ubAFCreatures )
|
|
gpEnemies[ index ].uiFlags = CELL_AF_CREATURE;
|
|
else if( index < gpAR->ubAMCreatures + gpAR->ubAFCreatures )
|
|
gpEnemies[ index ].uiFlags = CELL_AM_CREATURE;
|
|
else if( index < gpAR->ubYFCreatures + gpAR->ubAMCreatures + gpAR->ubAFCreatures )
|
|
gpEnemies[ index ].uiFlags = CELL_YF_CREATURE;
|
|
else
|
|
gpEnemies[ index ].uiFlags = CELL_YM_CREATURE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
INT32 DetermineCellID( SOLDIERCELL *pCell )
|
|
{
|
|
INT32 iIndex;
|
|
|
|
if( pCell->pSoldier->bTeam == ENEMY_TEAM )
|
|
{
|
|
for( iIndex = 0 ; iIndex < gpAR->ubEnemies ; iIndex++ )
|
|
if( &gpEnemies[ iIndex ] == pCell )
|
|
return iIndex;
|
|
}else if( pCell->pSoldier->bTeam == MILITIA_TEAM )
|
|
{
|
|
for( iIndex = 0 ; iIndex < gpAR->ubCivs ; iIndex++ )
|
|
if( &gpCivs[ iIndex ] == pCell )
|
|
return iIndex;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOLEAN IsItAllowedToRender( SOLDIERCELL *pCell )
|
|
{
|
|
INT32 iID = DetermineCellID( pCell );
|
|
|
|
switch( pCell->pSoldier->bTeam )
|
|
{
|
|
case ENEMY_TEAM:
|
|
if( iID >= giMaxEnemiesToRender ) return FALSE;
|
|
break;
|
|
case MILITIA_TEAM:
|
|
if( iID >= giMaxMilitiaToRender ) return FALSE;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void RenderSoldierCell( SOLDIERCELL *pCell )
|
|
{
|
|
UINT8 x;
|
|
|
|
if( !IsItAllowedToRender( pCell ) ) return;
|
|
|
|
if( pCell->uiFlags & CELL_MERC )
|
|
{
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+36, pCell->yp+2, pCell->xp+44, pCell->yp+30, 0 );
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iPanelImages, MERC_PANEL, pCell->xp, pCell->yp, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
RenderSoldierCellBars( pCell );
|
|
x = 0;
|
|
}
|
|
else
|
|
{
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iPanelImages, OTHER_PANEL, pCell->xp, pCell->yp, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
x = 6;
|
|
}
|
|
if( !pCell->pSoldier->stats.bLife )
|
|
{
|
|
SetObjectHandleShade( pCell->uiVObjectID, 0 );
|
|
|
|
if ( (pCell->uiFlags & CELL_CREATURE) )
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, CREATURE_SKULL, pCell->xp + 3 + x, pCell->yp + 3, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
else if ( (pCell->uiFlags & CELL_TANK) )
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, TANK_WRECK, pCell->xp + 3 + x, pCell->yp + 3, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
else
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, HUMAN_SKULL, pCell->xp+3+x, pCell->yp+3, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
}
|
|
else
|
|
{
|
|
if( pCell->uiFlags & CELL_HITBYATTACKER )
|
|
{
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+3+x, pCell->yp+3, pCell->xp+33+x, pCell->yp+29, 65535 );
|
|
}
|
|
else if( pCell->uiFlags & CELL_HITLASTFRAME )
|
|
{
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+3+x, pCell->yp+3, pCell->xp+33+x, pCell->yp+29, 0 );
|
|
SetObjectHandleShade( pCell->uiVObjectID, 1 );
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, pCell->uiVObjectID, pCell->usIndex, pCell->xp+3+x, pCell->yp+3, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
}
|
|
else
|
|
{
|
|
SetObjectHandleShade( pCell->uiVObjectID, 0 );
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, pCell->uiVObjectID, pCell->usIndex, pCell->xp+3+x, pCell->yp+3, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
}
|
|
}
|
|
|
|
if( pCell->pSoldier->stats.bLife > 0 && pCell->pSoldier->stats.bLife < OKLIFE && !(pCell->uiFlags & (CELL_HITBYATTACKER|CELL_HITLASTFRAME|CELL_CREATURE)) )
|
|
{ //Merc is unconcious (and not taking damage), so darken his portrait.
|
|
UINT8 *pDestBuf;
|
|
UINT32 uiDestPitchBYTES;
|
|
SGPRect ClipRect;
|
|
ClipRect.iLeft = pCell->xp+3+x;
|
|
ClipRect.iTop = pCell->yp+3;
|
|
ClipRect.iRight = pCell->xp+33+x;
|
|
ClipRect.iBottom = pCell->yp+29;
|
|
pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
|
|
Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect );
|
|
UnLockVideoSurface( FRAME_BUFFER );
|
|
}
|
|
|
|
//Draw the health text
|
|
RenderSoldierCellHealth( pCell );
|
|
|
|
DrawDebugText( pCell );
|
|
|
|
if( !(pCell->uiFlags & CELL_RETREATING) )
|
|
pCell->uiFlags &= ~CELL_DIRTY;
|
|
|
|
InvalidateRegion( pCell->xp, pCell->yp, pCell->xp+50, pCell->yp+44 );
|
|
|
|
//Adjust flags accordingly
|
|
if( pCell->uiFlags & CELL_HITBYATTACKER )
|
|
{
|
|
pCell->uiFlags &= ~CELL_HITBYATTACKER;
|
|
pCell->uiFlags |= CELL_HITLASTFRAME | CELL_DIRTY;
|
|
pCell->uiFlashTime = GetJA2Clock() + 150;
|
|
}
|
|
else if( pCell->uiFlags & CELL_HITLASTFRAME )
|
|
{
|
|
if( pCell->uiFlashTime < GetJA2Clock() )
|
|
{
|
|
pCell->uiFlags &= ~CELL_HITLASTFRAME;
|
|
}
|
|
pCell->uiFlags |= CELL_DIRTY;
|
|
}
|
|
}
|
|
|
|
void RenderSoldierCellBars( SOLDIERCELL *pCell )
|
|
{
|
|
INT32 iStartY;
|
|
//HEALTH BAR
|
|
if( !pCell->pSoldier->stats.bLife )
|
|
return;
|
|
//yellow one for bleeding
|
|
iStartY = pCell->yp + 29 - 25*pCell->pSoldier->stats.bLifeMax/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+37, iStartY, pCell->xp+38, pCell->yp+29, Get16BPPColor( FROMRGB( 107, 107, 57 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+38, iStartY, pCell->xp+39, pCell->yp+29, Get16BPPColor( FROMRGB( 222, 181, 115 ) ) );
|
|
|
|
// poisoned bleeding in purple
|
|
if ( pCell->pSoldier->bPoisonBleeding )
|
|
{
|
|
iStartY = pCell->yp + 29 - 25*(pCell->pSoldier->stats.bLifeMax - pCell->pSoldier->bBleeding + pCell->pSoldier->bPoisonBleeding)/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+37, iStartY, pCell->xp+38, pCell->yp+29, Get16BPPColor( FROMRGB( 107, 57, 107 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+38, iStartY, pCell->xp+39, pCell->yp+29, Get16BPPColor( FROMRGB( 222, 115, 181 ) ) );
|
|
}
|
|
|
|
//pink one for bandaged.
|
|
iStartY = pCell->yp + 29 - 25*(pCell->pSoldier->stats.bLifeMax - pCell->pSoldier->bBleeding)/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+37, iStartY, pCell->xp+38, pCell->yp+29, Get16BPPColor( FROMRGB( 156, 57, 57 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+38, iStartY, pCell->xp+39, pCell->yp+29, Get16BPPColor( FROMRGB( 222, 132, 132 ) ) );
|
|
|
|
// get amount of poisoned bandage
|
|
INT8 bPoisonBandage = pCell->pSoldier->bPoisonSum - pCell->pSoldier->bPoisonBleeding - pCell->pSoldier->bPoisonLife;
|
|
if ( bPoisonBandage )
|
|
{
|
|
// poisoned bandage in bright green
|
|
iStartY = pCell->yp + 29 - 25*(pCell->pSoldier->stats.bLife + bPoisonBandage)/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+37, iStartY, pCell->xp+38, pCell->yp+29, Get16BPPColor( FROMRGB( 57, 156, 57 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+38, iStartY, pCell->xp+39, pCell->yp+29, Get16BPPColor( FROMRGB( 132, 222, 132 ) ) );
|
|
}
|
|
|
|
//red one for actual health
|
|
iStartY = pCell->yp + 29 - 25*pCell->pSoldier->stats.bLife/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+37, iStartY, pCell->xp+38, pCell->yp+29, Get16BPPColor( FROMRGB( 107, 8, 8 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+38, iStartY, pCell->xp+39, pCell->yp+29, Get16BPPColor( FROMRGB( 206, 0, 0 ) ) );
|
|
|
|
// poisoned life
|
|
if ( pCell->pSoldier->bPoisonLife )
|
|
{
|
|
iStartY = pCell->yp + 29 - 25*pCell->pSoldier->bPoisonLife/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+37, iStartY, pCell->xp+38, pCell->yp+29, Get16BPPColor( FROMRGB( 8, 107, 8 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+38, iStartY, pCell->xp+39, pCell->yp+29, Get16BPPColor( FROMRGB( 0, 206, 0 ) ) );
|
|
}
|
|
|
|
//BREATH BAR
|
|
iStartY = pCell->yp + 29 - 25*pCell->pSoldier->bBreathMax/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+41, iStartY, pCell->xp+42, pCell->yp+29, Get16BPPColor( FROMRGB( 8, 8, 132 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+42, iStartY, pCell->xp+43, pCell->yp+29, Get16BPPColor( FROMRGB( 8, 8, 107 ) ) );
|
|
//MORALE BAR
|
|
iStartY = pCell->yp + 29 - 25*pCell->pSoldier->aiData.bMorale/100;
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+45, iStartY, pCell->xp+46, pCell->yp+29, Get16BPPColor( FROMRGB( 8, 156, 8 ) ) );
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, pCell->xp+46, iStartY, pCell->xp+47, pCell->yp+29, Get16BPPColor( FROMRGB( 8, 107, 8 ) ) );
|
|
}
|
|
|
|
void BuildInterfaceBuffer()
|
|
{
|
|
VSURFACE_DESC vs_desc;
|
|
UINT16 usUselessWidth, usUselessHeight;
|
|
UINT8 ubBitDepth;
|
|
SGPRect ClipRect;
|
|
SGPRect DestRect;
|
|
INT32 x,y;
|
|
|
|
//Setup the blitting clip regions, so we don't draw outside of the region (for excess panelling)
|
|
gpAR->Rect.iLeft = iScreenWidthOffset + (320 - gpAR->sWidth/2);
|
|
gpAR->Rect.iRight = gpAR->Rect.iLeft + gpAR->sWidth;
|
|
gpAR->Rect.iTop = iScreenHeightOffset + (240 - gpAR->sHeight/2);
|
|
if( gpAR->Rect.iTop > (iScreenHeightOffset + 120) )
|
|
gpAR->Rect.iTop -= 40;
|
|
gpAR->Rect.iBottom = gpAR->Rect.iTop + gpAR->sHeight;
|
|
|
|
DestRect.iLeft = 0;
|
|
DestRect.iTop = 0;
|
|
DestRect.iRight = gpAR->sWidth;
|
|
DestRect.iBottom = gpAR->sHeight;
|
|
|
|
//create buffer for the transition slot for merc items. This slot contains the newly
|
|
//selected item graphic in it's inventory size version. This buffer is then scaled down
|
|
//into the associated merc inventory panel slot buffer which is approximately 20% smaller.
|
|
GetCurrentVideoSettings( &usUselessWidth, &usUselessHeight, &ubBitDepth );
|
|
vs_desc.fCreateFlags = VSURFACE_CREATE_DEFAULT | VSURFACE_SYSTEM_MEM_USAGE;
|
|
vs_desc.usWidth = gpAR->sWidth;
|
|
vs_desc.usHeight = gpAR->sHeight;
|
|
vs_desc.ubBitDepth = ubBitDepth;
|
|
if( !AddVideoSurface( &vs_desc, (UINT32 *)&gpAR->iInterfaceBuffer ) )
|
|
AssertMsg( 0, "Failed to allocate memory for autoresolve interface buffer." );
|
|
|
|
GetClippingRect( &ClipRect );
|
|
SetClippingRect( &DestRect );
|
|
|
|
//Blit the back panels...
|
|
for( y = DestRect.iTop; y < DestRect.iBottom; y += 40 )
|
|
{
|
|
for( x = DestRect.iLeft; x < DestRect.iRight; x += 50 )
|
|
{
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, C_TEXTURE, x, y, VO_BLT_SRCTRANSPARENCY, 0 );
|
|
}
|
|
}
|
|
//Blit the left and right edges
|
|
for( y = DestRect.iTop; y < DestRect.iBottom; y += 40 )
|
|
{
|
|
x = DestRect.iLeft;
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, L_BORDER, x, y, VO_BLT_SRCTRANSPARENCY, 0 );
|
|
x = DestRect.iRight - 3;
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, R_BORDER, x, y, VO_BLT_SRCTRANSPARENCY, 0 );
|
|
}
|
|
//Blit the top and bottom edges
|
|
for( x = DestRect.iLeft; x < DestRect.iRight; x += 50 )
|
|
{
|
|
y = DestRect.iTop;
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, T_BORDER, x, y, VO_BLT_SRCTRANSPARENCY, 0 );
|
|
y = DestRect.iBottom - 3;
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, B_BORDER, x, y, VO_BLT_SRCTRANSPARENCY, 0 );
|
|
}
|
|
//Blit the 4 corners
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, TL_BORDER, DestRect.iLeft, DestRect.iTop, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, TR_BORDER, DestRect.iRight-10, DestRect.iTop, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, BL_BORDER, DestRect.iLeft, DestRect.iBottom-9, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, BR_BORDER, DestRect.iRight-10, DestRect.iBottom-9, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
|
|
//Blit the center pieces
|
|
x = gpAR->sCenterStartX - gpAR->Rect.iLeft;
|
|
y = 0;
|
|
//Top
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, TOP_MIDDLE, x, y, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
//Middle
|
|
for( y = 40; y < gpAR->sHeight - 40; y += 40 )
|
|
{
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, AUTO_MIDDLE, x, y, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
}
|
|
y = gpAR->sHeight - 40;
|
|
BltVideoObjectFromIndex( gpAR->iInterfaceBuffer, gpAR->iPanelImages, BOT_MIDDLE, x, y, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
|
|
SetClippingRect( &ClipRect );
|
|
}
|
|
|
|
void ExpandWindow()
|
|
{
|
|
SGPRect OldRect;
|
|
UINT32 uiDestPitchBYTES;
|
|
UINT32 uiCurrentTime, uiTimeRange, uiPercent;
|
|
UINT8 *pDestBuf;
|
|
INT32 i;
|
|
|
|
if( !gpAR->ExRect.iLeft && !gpAR->ExRect.iRight )
|
|
{ //First time
|
|
gpAR->ExRect.iLeft = ORIG_LEFT;
|
|
gpAR->ExRect.iTop = ORIG_TOP;
|
|
gpAR->ExRect.iRight = ORIG_RIGHT;
|
|
gpAR->ExRect.iBottom = ORIG_BOTTOM;
|
|
gpAR->uiStartExpanding = GetJA2Clock();
|
|
gpAR->uiEndExpanding = gpAR->uiStartExpanding + 333;
|
|
for( i = 0; i < DONEWIN_BUTTON; i++ )
|
|
HideButton( gpAR->iButton[ i ] );
|
|
}
|
|
else
|
|
{
|
|
//Restore the previous area
|
|
//left
|
|
BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, (UINT16)gpAR->ExRect.iLeft, (UINT16)gpAR->ExRect.iTop, 1, (UINT16)(gpAR->ExRect.iBottom-gpAR->ExRect.iTop+1) );
|
|
InvalidateRegion( gpAR->ExRect.iLeft, gpAR->ExRect.iTop, gpAR->ExRect.iLeft+1, gpAR->ExRect.iBottom+1 );
|
|
//right
|
|
BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, (UINT16)gpAR->ExRect.iRight, (UINT16)gpAR->ExRect.iTop, 1, (UINT16)(gpAR->ExRect.iBottom-gpAR->ExRect.iTop+1) );
|
|
InvalidateRegion( gpAR->ExRect.iRight, gpAR->ExRect.iTop, gpAR->ExRect.iRight+1, gpAR->ExRect.iBottom+1 );
|
|
//top
|
|
BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, (UINT16)gpAR->ExRect.iLeft, (UINT16)gpAR->ExRect.iTop, (UINT16)(gpAR->ExRect.iRight-gpAR->ExRect.iLeft+1), 1 );
|
|
InvalidateRegion( gpAR->ExRect.iLeft, gpAR->ExRect.iTop, gpAR->ExRect.iRight+1, gpAR->ExRect.iTop+1 );
|
|
//bottom
|
|
BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, (UINT16)gpAR->ExRect.iLeft, (UINT16)gpAR->ExRect.iBottom, (UINT16)(gpAR->ExRect.iRight-gpAR->ExRect.iLeft+1), 1 );
|
|
InvalidateRegion( gpAR->ExRect.iLeft, gpAR->ExRect.iBottom, gpAR->ExRect.iRight+1, gpAR->ExRect.iBottom+1 );
|
|
|
|
uiCurrentTime = GetJA2Clock();
|
|
if( uiCurrentTime >= gpAR->uiStartExpanding && uiCurrentTime <= gpAR->uiEndExpanding )
|
|
{
|
|
//Debug purposes
|
|
OldRect.iLeft = ORIG_LEFT;
|
|
OldRect.iTop = ORIG_TOP;
|
|
OldRect.iRight = ORIG_RIGHT;
|
|
OldRect.iBottom = ORIG_BOTTOM;
|
|
|
|
uiTimeRange = gpAR->uiEndExpanding - gpAR->uiStartExpanding;
|
|
uiPercent = (uiCurrentTime - gpAR->uiStartExpanding ) * 100 / uiTimeRange;
|
|
|
|
//Left
|
|
if( OldRect.iLeft <= gpAR->Rect.iLeft )
|
|
gpAR->ExRect.iLeft = OldRect.iLeft + ( gpAR->Rect.iLeft - OldRect.iLeft ) * uiPercent / 100;
|
|
else
|
|
gpAR->ExRect.iLeft = gpAR->Rect.iLeft + ( OldRect.iLeft - gpAR->Rect.iLeft ) * uiPercent / 100;
|
|
//Top
|
|
if( OldRect.iTop <= gpAR->Rect.iTop )
|
|
gpAR->ExRect.iTop = OldRect.iTop + ( gpAR->Rect.iTop - OldRect.iTop ) * uiPercent / 100;
|
|
else
|
|
gpAR->ExRect.iTop = gpAR->Rect.iTop + ( OldRect.iTop - gpAR->Rect.iTop ) * uiPercent / 100;
|
|
//Right
|
|
if( OldRect.iRight <= gpAR->Rect.iRight )
|
|
gpAR->ExRect.iRight = OldRect.iRight + ( gpAR->Rect.iRight - OldRect.iRight ) * uiPercent / 100;
|
|
else
|
|
gpAR->ExRect.iRight = gpAR->Rect.iRight + ( OldRect.iRight - gpAR->Rect.iRight ) * uiPercent / 100;
|
|
//Bottom
|
|
if( OldRect.iBottom <= gpAR->Rect.iBottom )
|
|
gpAR->ExRect.iBottom = OldRect.iBottom + ( gpAR->Rect.iBottom - OldRect.iBottom ) * uiPercent / 100;
|
|
else
|
|
gpAR->ExRect.iBottom = gpAR->Rect.iBottom + ( OldRect.iBottom - gpAR->Rect.iBottom ) * uiPercent / 100;
|
|
}
|
|
else
|
|
{ //expansion done -- final frame
|
|
gpAR->ExRect.iLeft = gpAR->Rect.iLeft;
|
|
gpAR->ExRect.iTop = gpAR->Rect.iTop;
|
|
gpAR->ExRect.iRight = gpAR->Rect.iRight;
|
|
gpAR->ExRect.iBottom = gpAR->Rect.iBottom;
|
|
gpAR->fExpanding = FALSE;
|
|
gpAR->fShowInterface = TRUE;
|
|
}
|
|
}
|
|
|
|
//The new rect now determines the state of the current rectangle.
|
|
pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
|
|
SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0 + xResOffset, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
|
|
RectangleDraw( TRUE, gpAR->ExRect.iLeft, gpAR->ExRect.iTop, gpAR->ExRect.iRight, gpAR->ExRect.iBottom, Get16BPPColor( FROMRGB( 200, 200, 100 ) ), pDestBuf );
|
|
UnLockVideoSurface( FRAME_BUFFER );
|
|
//left
|
|
InvalidateRegion( gpAR->ExRect.iLeft, gpAR->ExRect.iTop, gpAR->ExRect.iLeft+1, gpAR->ExRect.iBottom+1 );
|
|
//right
|
|
InvalidateRegion( gpAR->ExRect.iRight, gpAR->ExRect.iTop, gpAR->ExRect.iRight+1, gpAR->ExRect.iBottom+1 );
|
|
//top
|
|
InvalidateRegion( gpAR->ExRect.iLeft, gpAR->ExRect.iTop, gpAR->ExRect.iRight+1, gpAR->ExRect.iTop+1 );
|
|
//bottom
|
|
InvalidateRegion( gpAR->ExRect.iLeft, gpAR->ExRect.iBottom, gpAR->ExRect.iRight+1, gpAR->ExRect.iBottom+1 );
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - This whole procedure was merged with the surgery ability of the doctor trait
|
|
UINT32 VirtualSoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, OBJECTTYPE *pKit, INT16 sKitPts, INT16 sStatus, BOOLEAN fOnSurgery )
|
|
{
|
|
UINT32 uiDressSkill, uiPossible, uiActual, uiMedcost, uiDeficiency, uiAvailAPs, uiUsedAPs;
|
|
UINT8 bBelowOKlife, bPtsLeft;
|
|
INT8 bInitialBleeding;
|
|
|
|
if( pVictim->bBleeding < 1 && !fOnSurgery )
|
|
return 0; // nothing to do, shouldn't have even been called!
|
|
if ( pVictim->stats.bLife == 0 )
|
|
return 0;
|
|
if (fOnSurgery && pVictim->ubID == pSoldier->ubID) // cannot make surgery on self
|
|
return 0;
|
|
|
|
bInitialBleeding = pVictim->bBleeding;
|
|
|
|
if ( !gGameOptions.fNewTraitSystem && fOnSurgery) // cannot make surgery if not new traits
|
|
fOnSurgery = FALSE;
|
|
|
|
// calculate wound-dressing skill (3x medical, 2x equip, 1x level, 1x dex)
|
|
if ( gGameOptions.fNewTraitSystem )
|
|
{
|
|
uiDressSkill = ( ( 7 * EffectiveMedical( pSoldier ) ) + // medical knowledge
|
|
( sStatus) + // state of medical kit
|
|
(10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience
|
|
EffectiveDexterity( pSoldier, FALSE ) ) / 10; // general "handiness"
|
|
}
|
|
else
|
|
{
|
|
uiDressSkill = ( ( 3 * EffectiveMedical( pSoldier ) ) + // medical knowledge
|
|
( 2 * sStatus) + // state of medical kit
|
|
(10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience
|
|
EffectiveDexterity( pSoldier, FALSE ) ) / 7; // general "handiness"
|
|
}
|
|
|
|
// try to use every AP that the merc has left
|
|
uiAvailAPs = pSoldier->bActionPoints;
|
|
|
|
// OK, If we are in real-time, use another value...
|
|
if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{ // Set to a value which looks good based on out tactical turns duration
|
|
uiAvailAPs = RT_FIRST_AID_GAIN_MODIFIER;
|
|
}
|
|
|
|
// calculate how much bandaging CAN be done this turn
|
|
uiPossible = ( uiAvailAPs * uiDressSkill ) / 50; // max rate is 2 * fullAPs
|
|
|
|
// if no healing is possible (insufficient APs or insufficient dressSkill)
|
|
if (!uiPossible)
|
|
return 0;
|
|
|
|
if (Item[pSoldier->inv[0].usItem].medicalkit && !(fOnSurgery) ) // using the GOOD medic stuff
|
|
uiPossible += ( uiPossible / 2); // add extra 50 %
|
|
|
|
// Doctor trait improves basic bandaging ability
|
|
if (!(fOnSurgery) && gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, DOCTOR_NT ))
|
|
{
|
|
uiPossible = uiPossible * (100 - gSkillTraitValues.bSpeedModifierBandaging) / 100;
|
|
uiPossible += ( uiPossible * gSkillTraitValues.ubDOBandagingSpeedPercent * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ) + pSoldier->GetBackgroundValue(BG_PERC_BANDAGING) ) / 100;
|
|
}
|
|
|
|
uiActual = uiPossible; // start by assuming maximum possible
|
|
|
|
// figure out how far below OKLIFE the victim is
|
|
if (pVictim->stats.bLife >= OKLIFE)
|
|
bBelowOKlife = 0;
|
|
else
|
|
bBelowOKlife = OKLIFE - pVictim->stats.bLife;
|
|
|
|
// figure out how many healing pts we need to stop dying (2x cost)
|
|
uiDeficiency = (2 * bBelowOKlife );
|
|
|
|
// if, after that, the patient will still be bleeding
|
|
if ( (pVictim->bBleeding - bBelowOKlife ) > 0)
|
|
{ // then add how many healing pts we need to stop bleeding (1x cost)
|
|
uiDeficiency += ( pVictim->bBleeding - bBelowOKlife );
|
|
}
|
|
// On surgery, alter this by amount of life we can heal
|
|
if ( fOnSurgery )
|
|
{
|
|
uiDeficiency += (pVictim->iHealableInjury / 100);
|
|
}
|
|
// now, make sure we weren't going to give too much
|
|
if ( uiActual > uiDeficiency) // if we were about to apply too much
|
|
uiActual = uiDeficiency; // reduce actual not to waste anything
|
|
|
|
// now make sure we HAVE that much
|
|
if( Item[pKit->usItem].medicalkit )
|
|
{
|
|
if (fOnSurgery)
|
|
uiMedcost = (uiActual * gSkillTraitValues.usDOSurgeryMedBagConsumption ) /100; // surgery drains the kit a lot
|
|
else
|
|
uiMedcost = (uiActual + 1) / 2; // cost is only half, rounded up
|
|
|
|
if ( uiMedcost == 0 && uiActual > 0)
|
|
uiMedcost = 1;
|
|
if ( uiMedcost > (UINT32)sKitPts ) // if we can't afford this
|
|
{
|
|
uiMedcost = sKitPts; // what CAN we afford?
|
|
if (fOnSurgery) // surgery check
|
|
uiActual = (uiMedcost * 100 )/ gSkillTraitValues.usDOSurgeryMedBagConsumption;
|
|
else
|
|
uiActual = uiMedcost * 2; // give double this as aid
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiMedcost = uiActual;
|
|
if ( uiMedcost == 0 && uiActual > 0)
|
|
uiMedcost = 1;
|
|
if ( uiMedcost > (UINT32)sKitPts) // can't afford it
|
|
uiMedcost = uiActual = sKitPts; // recalc cost AND aid
|
|
}
|
|
|
|
bPtsLeft = (INT8)uiActual;
|
|
// heal real life points first (if below OKLIFE) because we don't want the
|
|
// patient still DYING if bandages run out, or medic is disabled/distracted!
|
|
// NOTE: Dressing wounds for life below OKLIFE now costs 2 pts/life point!
|
|
if ( bPtsLeft && pVictim->stats.bLife < OKLIFE)
|
|
{
|
|
// if we have enough points to bring him all the way to OKLIFE this turn
|
|
if ( bPtsLeft >= (2 * bBelowOKlife ) )
|
|
{
|
|
// insta-healable injury check
|
|
if (pVictim->iHealableInjury > 0)
|
|
{
|
|
pVictim->iHealableInjury -= ((OKLIFE - pVictim->stats.bLife) * 100);
|
|
if (pVictim->iHealableInjury < 0)
|
|
pVictim->iHealableInjury = 0;
|
|
}
|
|
// raise life to OKLIFE
|
|
pVictim->stats.bLife = OKLIFE;
|
|
// reduce bleeding by the same number of life points healed up
|
|
pVictim->bBleeding -= bBelowOKlife;
|
|
|
|
// Flugente: increase bPoisonLife, decrease bPoisonBleeding
|
|
pVictim->bPoisonLife = min(pVictim->bPoisonSum, pVictim->bPoisonLife + bBelowOKlife);
|
|
pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - bBelowOKlife);
|
|
|
|
// use up appropriate # of actual healing points
|
|
bPtsLeft -= (2 * bBelowOKlife);
|
|
}
|
|
else
|
|
{
|
|
// insta-healable injury check
|
|
if (pVictim->iHealableInjury > 0)
|
|
{
|
|
pVictim->iHealableInjury -= (( bPtsLeft / 2) * 100);
|
|
if (pVictim->iHealableInjury < 0)
|
|
pVictim->iHealableInjury = 0;
|
|
}
|
|
pVictim->stats.bLife += ( bPtsLeft / 2);
|
|
pVictim->bBleeding -= ( bPtsLeft / 2);
|
|
|
|
// Flugente: increase bPoisonLife, decrease bPoisonBleeding
|
|
pVictim->bPoisonLife = min(pVictim->bPoisonSum, pVictim->bPoisonLife + ( bPtsLeft / 2));
|
|
pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - ( bPtsLeft / 2));
|
|
|
|
bPtsLeft = bPtsLeft % 2; // if ptsLeft was odd, ptsLeft = 1
|
|
}
|
|
|
|
// this should never happen any more, but make sure bleeding not negative
|
|
if (pVictim->bBleeding < 0)
|
|
{
|
|
pVictim->bBleeding = 0;
|
|
pVictim->bPoisonBleeding = 0;
|
|
}
|
|
|
|
// if this healing brought the patient out of the worst of it, cancel dying
|
|
if (pVictim->stats.bLife >= OKLIFE )
|
|
{ // turn off merc QUOTE flags
|
|
pVictim->flags.fDyingComment = FALSE;
|
|
}
|
|
|
|
if ( pVictim->bBleeding <= MIN_BLEEDING_THRESHOLD )
|
|
{
|
|
pVictim->flags.fWarnedAboutBleeding = FALSE;
|
|
}
|
|
}
|
|
|
|
// SURGERY
|
|
// first return the real life back, then bandage the rest if possible
|
|
if (fOnSurgery && gGameOptions.fNewTraitSystem) // double check for new traits
|
|
{
|
|
INT32 iLifeReturned = 0;
|
|
UINT16 usReturnDamagedStatRate = 0;
|
|
// find out if we will repair any stats...
|
|
if ( NumberOfDamagedStats( pVictim ) > 0 )
|
|
{
|
|
usReturnDamagedStatRate = ((gSkillTraitValues.usDORepairStatsRateBasic + gSkillTraitValues.usDORepairStatsRateOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT )));
|
|
usReturnDamagedStatRate -= max( 0, ((usReturnDamagedStatRate * gSkillTraitValues.ubDORepStPenaltyIfAlsoHealing ) / 100));
|
|
|
|
// ... in which case, reduce the points
|
|
bPtsLeft = max(0, ((bPtsLeft * ( 100 - gSkillTraitValues.ubDOHealingPenaltyIfAlsoStatRepair )) / 100));
|
|
}
|
|
|
|
// Important note! : HealableInjury is always stores the total HPs the victim is missing, not the amount which we will heal,
|
|
// so we always take a portion of patient's damage here, reduce the HealableInjury by this portion, while only healing a portion of this portion in actual HPs;
|
|
// this means the rest of HPs will remain as "unhealable", the patient will miss X HPs but has no HealableInjury on self..
|
|
if (bPtsLeft >= (pVictim->iHealableInjury/100))
|
|
{
|
|
iLifeReturned = pVictim->iHealableInjury * (gSkillTraitValues.ubDOSurgeryHealPercentBase + gSkillTraitValues.ubDOSurgeryHealPercentOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ))/100;
|
|
|
|
pVictim->iHealableInjury = 0;
|
|
// keep the rest of the points to bandaging if neccessary
|
|
if (pVictim->bBleeding > 0)
|
|
{
|
|
bPtsLeft = max(0, (bPtsLeft - (iLifeReturned / 100)));
|
|
bPtsLeft += (bPtsLeft/2); // we use medical bag so add the bonus for that.
|
|
}
|
|
else
|
|
{
|
|
bPtsLeft = 0;
|
|
}
|
|
|
|
// add to record - another surgery undergoed
|
|
if ( pVictim->ubProfile != NO_PROFILE && iLifeReturned >= 100 )
|
|
gMercProfiles[ pVictim->ubProfile ].records.usTimesSurgeryUndergoed++;
|
|
|
|
// add to record - another surgery made
|
|
if ( pSoldier->ubProfile != NO_PROFILE && iLifeReturned >= 100 )
|
|
gMercProfiles[ pSoldier->ubProfile ].records.usSurgeriesMade++;
|
|
}
|
|
else
|
|
{
|
|
iLifeReturned = bPtsLeft * (gSkillTraitValues.ubDOSurgeryHealPercentBase + gSkillTraitValues.ubDOSurgeryHealPercentOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ));
|
|
|
|
pVictim->iHealableInjury -= (bPtsLeft * 100);
|
|
bPtsLeft = 0;
|
|
}
|
|
// repair the stats here!
|
|
if ( usReturnDamagedStatRate > 0 )
|
|
{
|
|
RegainDamagedStats( pVictim, (iLifeReturned * usReturnDamagedStatRate / 100) );
|
|
}
|
|
|
|
// some paranoya checks for sure
|
|
if ((pVictim->stats.bLife + (iLifeReturned / 100)) <= pVictim->stats.bLifeMax)
|
|
{
|
|
pVictim->stats.bLife += (iLifeReturned/100);
|
|
pVictim->bPoisonLife = min(pVictim->bPoisonSum, pVictim->bPoisonLife + (iLifeReturned/100));
|
|
if (pVictim->bBleeding >= (iLifeReturned/100))
|
|
{
|
|
pVictim->bBleeding -= (iLifeReturned/100);
|
|
pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - (iLifeReturned/100));
|
|
uiMedcost += (iLifeReturned / 200); // add medkit points cost for unbandaged part
|
|
}
|
|
else
|
|
{
|
|
pVictim->bBleeding = 0;
|
|
pVictim->bPoisonBleeding = 0;
|
|
uiMedcost += max( 0, (((iLifeReturned/100) - pVictim->bBleeding) / 2)); // add medkit points cost for unbandaged part
|
|
}
|
|
}
|
|
else // this shouldn't even happen, but we still want to have it here for sure
|
|
{
|
|
pVictim->stats.bLife = pVictim->stats.bLifeMax;
|
|
pVictim->bPoisonLife = pVictim->bPoisonSum;
|
|
pVictim->iHealableInjury = 0;
|
|
pVictim->bBleeding = 0;
|
|
pVictim->bPoisonBleeding = 0;
|
|
}
|
|
// Reduce max breath based on life returned
|
|
if ( (pVictim->bBreathMax - (((iLifeReturned/100) * gSkillTraitValues.usDOSurgeryMaxBreathLoss )/ 100)) <= BREATHMAX_ABSOLUTE_MINIMUM )
|
|
{
|
|
pVictim->bBreathMax = BREATHMAX_ABSOLUTE_MINIMUM;
|
|
}
|
|
else
|
|
{
|
|
pVictim->bBreathMax -= (((iLifeReturned/100) * gSkillTraitValues.usDOSurgeryMaxBreathLoss )/ 100);
|
|
}
|
|
|
|
if (pVictim->iHealableInjury > ((pVictim->stats.bLifeMax - pVictim->stats.bLife)*100))
|
|
pVictim->iHealableInjury = ((pVictim->stats.bLifeMax - pVictim->stats.bLife)*100);
|
|
else if (pVictim->iHealableInjury < 0)
|
|
pVictim->iHealableInjury = 0;
|
|
|
|
// Flugente: campaign stats
|
|
gCurrentIncident.usIncidentFlags |= INCIDENT_SURGERY;
|
|
}
|
|
|
|
// if any healing points remain, apply that to any remaining bleeding (1/1)
|
|
// DON'T spend any APs/kit pts to cure bleeding until merc is no longer dying
|
|
//if ( bPtsLeft && pVictim->bBleeding && !pVictim->dying)
|
|
if ( bPtsLeft && pVictim->bBleeding )
|
|
{ // if we have enough points to bandage all remaining bleeding this turn
|
|
if (bPtsLeft >= pVictim->bBleeding )
|
|
{
|
|
bPtsLeft -= pVictim->bBleeding;
|
|
pVictim->bBleeding = 0;
|
|
pVictim->bPoisonBleeding = 0;
|
|
}
|
|
else // bandage what we can
|
|
{
|
|
pVictim->bBleeding -= bPtsLeft;
|
|
pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - bPtsLeft);
|
|
bPtsLeft = 0;
|
|
}
|
|
}
|
|
|
|
//CHRISL: If by some chance ubPtsLeft ends up being higher then uiActual, we'll end up with a huge value since uiActual is an unsigned variable.
|
|
// if there are any ptsLeft now, then we didn't actually get to use them
|
|
uiActual = max(0, (INT32)(uiActual - bPtsLeft));
|
|
|
|
// usedAPs equals (actionPts) * (%of possible points actually used)
|
|
uiUsedAPs = ( uiActual * uiAvailAPs ) / uiPossible;
|
|
|
|
if (Item[pSoldier->inv[0].usItem].medicalkit && !(fOnSurgery)) // using the GOOD medic stuff
|
|
uiUsedAPs = ( uiUsedAPs * 2) / 3; // reverse 50% bonus by taking 2/3rds
|
|
|
|
// surgery is harder so cost more BPs
|
|
if (fOnSurgery)
|
|
DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( uiUsedAPs * 15 ) );
|
|
else
|
|
DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( ( uiUsedAPs * APBPConstants[BP_PER_AP_LT_EFFORT]) ) );
|
|
|
|
// surgery is harder so gives more exp
|
|
if (fOnSurgery)
|
|
{
|
|
// MEDICAL GAIN (actual / 2): Helped someone by giving first aid
|
|
StatChange(pSoldier, MEDICALAMT, (UINT16)(uiActual + 2), FALSE);
|
|
|
|
// DEXTERITY GAIN (actual / 6): Helped someone by giving first aid
|
|
StatChange(pSoldier, DEXTAMT, (UINT16)((uiActual / 3) + 2), FALSE);
|
|
}
|
|
else
|
|
{
|
|
if ( uiActual / 2)
|
|
// MEDICAL GAIN (actual / 2): Helped someone by giving first aid
|
|
StatChange(pSoldier,MEDICALAMT,( (UINT16)(uiActual / 2) ),FALSE);
|
|
|
|
if ( uiActual / 4)
|
|
// DEXTERITY GAIN (actual / 4): Helped someone by giving first aid
|
|
StatChange(pSoldier,DEXTAMT,(UINT16)( ( uiActual / 4) ),FALSE);
|
|
}
|
|
|
|
// merc records - bandaging
|
|
if( bInitialBleeding > 1 && pVictim->bBleeding == 0 && pSoldier->ubProfile != NO_PROFILE )
|
|
gMercProfiles[ pSoldier->ubProfile ].records.usMercsBandaged++;
|
|
|
|
return uiMedcost;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
OBJECTTYPE* FindMedicalKit()
|
|
{
|
|
INT32 i;
|
|
INT32 iSlot;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
iSlot = FindObjClass( gpMercs[ i ].pSoldier, IC_MEDKIT );
|
|
if( iSlot != NO_SLOT )
|
|
{
|
|
return( &gpMercs[ i ].pSoldier->inv[ iSlot ] );
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
UINT32 AutoBandageMercs()
|
|
{
|
|
INT32 i, iBest;
|
|
UINT32 uiPointsUsed, uiCurrPointsUsed, uiMaxPointsUsed, uiParallelPointsUsed;
|
|
UINT16 usKitPts;
|
|
OBJECTTYPE *pKit = NULL;
|
|
BOOLEAN fFound = FALSE;
|
|
BOOLEAN fComplete = TRUE;
|
|
INT8 bSlot, cnt;
|
|
|
|
//Do we have any doctors? If so, bandage selves first.
|
|
fFound = FALSE;
|
|
uiMaxPointsUsed = uiParallelPointsUsed = 0;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->stats.bLife >= OKLIFE &&
|
|
!gpMercs[ i ].pSoldier->bCollapsed &&
|
|
gpMercs[ i ].pSoldier->stats.bMedical > 0 &&
|
|
( bSlot = FindObjClass( gpMercs[ i ].pSoldier, IC_MEDKIT ) ) != NO_SLOT )
|
|
{
|
|
fFound = TRUE;
|
|
//bandage self first!
|
|
uiCurrPointsUsed = 0;
|
|
cnt = 0;
|
|
while( gpMercs[ i ].pSoldier->bBleeding )
|
|
{
|
|
pKit = &gpMercs[ i ].pSoldier->inv[ bSlot ];
|
|
usKitPts = TotalPoints( pKit );
|
|
if( !usKitPts )
|
|
{ //attempt to find another kit before stopping
|
|
if( ( bSlot = FindObjClass( gpMercs[ i ].pSoldier, IC_MEDKIT ) ) != NO_SLOT )
|
|
continue;
|
|
break;
|
|
}
|
|
uiPointsUsed = VirtualSoldierDressWound( gpMercs[ i ].pSoldier, gpMercs[ i ].pSoldier, pKit, usKitPts, usKitPts, FALSE ); // changed by SANDRO
|
|
UseKitPoints( pKit, (UINT16)uiPointsUsed, gpMercs[ i ].pSoldier );
|
|
uiCurrPointsUsed += uiPointsUsed;
|
|
cnt++;
|
|
if( cnt > 50 )
|
|
break;
|
|
}
|
|
if( uiCurrPointsUsed > uiMaxPointsUsed )
|
|
uiMaxPointsUsed = uiCurrPointsUsed;
|
|
if( !pKit )
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//Find the best rated doctor to do all of the bandaging.
|
|
iBest = 0;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->stats.bLife >= OKLIFE && !gpMercs[ i ].pSoldier->bCollapsed && gpMercs[ i ].pSoldier->stats.bMedical > 0 )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->stats.bMedical > gpMercs[ iBest ].pSoldier->stats.bMedical )
|
|
{
|
|
iBest = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
cnt = 0; // SANDRO - added safety check
|
|
while( gpMercs[ i ].pSoldier->bBleeding && gpMercs[ i ].pSoldier->stats.bLife )
|
|
{ //This merc needs medical attention
|
|
if( !pKit )
|
|
{
|
|
pKit = FindMedicalKit();
|
|
if( !pKit )
|
|
{
|
|
fComplete = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
usKitPts = TotalPoints( pKit );
|
|
if( !usKitPts )
|
|
{
|
|
pKit = NULL;
|
|
fComplete = FALSE;
|
|
continue;
|
|
}
|
|
uiPointsUsed = VirtualSoldierDressWound( gpMercs[ iBest ].pSoldier, gpMercs[ i ].pSoldier, pKit, usKitPts, usKitPts, FALSE );
|
|
UseKitPoints( pKit, (UINT16)uiPointsUsed, gpMercs[ i ].pSoldier );
|
|
uiParallelPointsUsed += uiPointsUsed;
|
|
fComplete = TRUE;
|
|
|
|
// SANDRO - added safety check
|
|
cnt++;
|
|
if( cnt > 500 )
|
|
break;
|
|
}
|
|
}
|
|
if( fComplete )
|
|
{
|
|
DoScreenIndependantMessageBox( gzLateLocalizedString[ 13 ], MSG_BOX_FLAG_OK, AutoBandageFinishedCallback );
|
|
}
|
|
else
|
|
{
|
|
DoScreenIndependantMessageBox( gzLateLocalizedString[ 10 ], MSG_BOX_FLAG_OK, AutoBandageFinishedCallback );
|
|
}
|
|
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds += uiParallelPointsUsed * 200;
|
|
return 1;
|
|
}
|
|
|
|
void RenderAutoResolve()
|
|
{
|
|
INT32 i;
|
|
HVSURFACE hVSurface;
|
|
INT32 xp, yp;
|
|
CHAR16 str[100];
|
|
UINT8 ubGood, ubBad;
|
|
|
|
if( gpAR->fExpanding )
|
|
{ //animate the expansion of the window.
|
|
ExpandWindow();
|
|
return;
|
|
}
|
|
else if( gpAR->fShowInterface )
|
|
{ //After expanding the window, we now show the interface
|
|
if( gpAR->ubBattleStatus == BATTLE_IN_PROGRESS && !gpAR->fPendingSurrender )
|
|
{
|
|
for( i = 0 ; i < DONEWIN_BUTTON; i++ )
|
|
ShowButton( gpAR->iButton[ i ] );
|
|
HideButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ YES_BUTTON ] );
|
|
HideButton( gpAR->iButton[ NO_BUTTON ] );
|
|
gpAR->fShowInterface = FALSE;
|
|
}
|
|
else if( gpAR->ubBattleStatus == BATTLE_VICTORY )
|
|
{
|
|
ShowButton( gpAR->iButton[ DONEWIN_BUTTON ] );
|
|
ShowButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
}
|
|
else
|
|
{
|
|
ShowButton( gpAR->iButton[ DONELOSE_BUTTON ] );
|
|
}
|
|
}
|
|
|
|
if( !gpAR->fRenderAutoResolve && !gpAR->fDebugInfo )
|
|
{ //update the dirty cells only
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].uiFlags & CELL_DIRTY )
|
|
{
|
|
RenderSoldierCell( &gpMercs[ i ] );
|
|
}
|
|
}
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
{
|
|
if( gpCivs[ i ].uiFlags & CELL_DIRTY )
|
|
{
|
|
RenderSoldierCell( &gpCivs[ i ] );
|
|
}
|
|
}
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( gpEnemies[ i ].uiFlags & CELL_DIRTY )
|
|
{
|
|
RenderSoldierCell( &gpEnemies[ i ] );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
gpAR->fRenderAutoResolve = FALSE;
|
|
|
|
GetVideoSurface( &hVSurface, gpAR->iInterfaceBuffer );
|
|
BltVideoSurfaceToVideoSurface( ghFrameBuffer, hVSurface, 0, gpAR->Rect.iLeft, gpAR->Rect.iTop, VO_BLT_SRCTRANSPARENCY, 0 );
|
|
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
RenderSoldierCell( &gpMercs[ i ] );
|
|
}
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
{
|
|
RenderSoldierCell( &gpCivs[ i ] );
|
|
}
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
RenderSoldierCell( &gpEnemies[ i ] );
|
|
}
|
|
|
|
//Render the titles
|
|
SetFont( FONT10ARIALBOLD );
|
|
SetFontForeground( FONT_WHITE );
|
|
SetFontShadow( FONT_NEARBLACK );
|
|
|
|
switch( gubEnemyEncounterCode )
|
|
{
|
|
case NO_ENCOUNTER_CODE:
|
|
swprintf( str, gpStrategicString[STR_AR_ATTACK_HEADER] );
|
|
break;
|
|
case ENEMY_ENCOUNTER_CODE:
|
|
swprintf( str, gpStrategicString[STR_AR_ENCOUNTER_HEADER] );
|
|
break;
|
|
case ENEMY_INVASION_CODE:
|
|
case CREATURE_ATTACK_CODE:
|
|
swprintf( str, gpStrategicString[STR_AR_DEFEND_HEADER] );
|
|
break;
|
|
default:
|
|
swprintf( str, gpStrategicString[STR_AR_ATTACK_HEADER] );
|
|
break;
|
|
}
|
|
|
|
xp = gpAR->sCenterStartX + 70 - StringPixLength( str, FONT10ARIALBOLD )/2;
|
|
yp = gpAR->Rect.iTop + 15;
|
|
mprintf( xp, yp, str );
|
|
|
|
SetFont( FONT10ARIAL );
|
|
SetFontForeground( FONT_GRAY2 );
|
|
SetFontShadow( FONT_NEARBLACK );
|
|
|
|
GetSectorIDString( gpAR->ubSectorX, gpAR->ubSectorY, 0, str, TRUE );
|
|
xp = gpAR->sCenterStartX + 70 - StringPixLength( str, FONT10ARIAL )/2;
|
|
yp += 11;
|
|
mprintf( xp, yp, str );
|
|
|
|
//Display the remaining forces
|
|
ubGood = (UINT8)(gpAR->ubAliveMercs + gpAR->ubAliveCivs);
|
|
ubBad = gpAR->ubAliveEnemies;
|
|
swprintf( str, gzLateLocalizedString[ 17 ], ubGood, ubBad );
|
|
|
|
SetFont( FONT14ARIAL );
|
|
if( ubGood * 3 <= ubBad * 2 )
|
|
{
|
|
SetFontForeground( FONT_LTRED );
|
|
}
|
|
else if( ubGood * 2 >= ubBad * 3 )
|
|
{
|
|
SetFontForeground( FONT_LTGREEN );
|
|
}
|
|
else
|
|
{
|
|
SetFontForeground( FONT_YELLOW );
|
|
}
|
|
|
|
xp = gpAR->sCenterStartX + 70 - StringPixLength( str, FONT14ARIAL )/2;
|
|
yp += 11;
|
|
mprintf( xp, yp, str );
|
|
|
|
#ifdef JA2BETAVERSION
|
|
if( gpAR->fAllowCapture )
|
|
{
|
|
mprintf( 2, 2, L"Enemy capture enabled." );
|
|
}
|
|
#endif
|
|
|
|
if( gpAR->fPendingSurrender )
|
|
{
|
|
DisplayWrappedString( (UINT16)(gpAR->sCenterStartX+16), (UINT16)(iScreenHeightOffset + 230+gpAR->bVerticalOffset), 108, 2,
|
|
(UINT8)FONT10ARIAL, FONT_YELLOW, gpStrategicString[ STR_ENEMY_SURRENDER_OFFER ], FONT_BLACK, FALSE, LEFT_JUSTIFIED );
|
|
}
|
|
|
|
if( gpAR->ubBattleStatus != BATTLE_IN_PROGRESS )
|
|
{
|
|
// Handle merc morale, Global loyalty, and change of sector control
|
|
if( !gpAR->fMoraleEventsHandled )
|
|
{
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds *= 3;
|
|
gpAR->fMoraleEventsHandled = TRUE;
|
|
if ( CheckFact( FACT_FIRST_BATTLE_FOUGHT, 0 ) == FALSE )
|
|
{
|
|
// this was the first battle against the army
|
|
SetFactTrue( FACT_FIRST_BATTLE_FOUGHT );
|
|
if ( gpAR->ubBattleStatus == BATTLE_VICTORY)
|
|
{
|
|
SetFactTrue( FACT_FIRST_BATTLE_WON );
|
|
}
|
|
SetTheFirstBattleSector( ( INT16 ) (gpAR->ubSectorX + gpAR->ubSectorY * MAP_WORLD_X ) );
|
|
#ifdef JA2UB
|
|
//Ja25: no loyalty
|
|
#else
|
|
HandleFirstBattleEndingWhileInTown( gpAR->ubSectorX, gpAR->ubSectorY, 0, TRUE );
|
|
#endif
|
|
}
|
|
|
|
switch( gpAR->ubBattleStatus )
|
|
{
|
|
case BATTLE_VICTORY:
|
|
HandleMoraleEvent( NULL, MORALE_BATTLE_WON, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
if( ProcessLoyalty() )HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_BATTLE_WON, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
|
|
SectorInfo[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].bLastKnownEnemies = 0;
|
|
SetThisSectorAsPlayerControlled( gpAR->ubSectorX, gpAR->ubSectorY, 0, TRUE );
|
|
#ifdef NEWMUSIC
|
|
GlobalSoundID = MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalVictory[0];
|
|
if ( MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalVictory[0] != -1 )
|
|
SetMusicModeID( MUSIC_TACTICAL_VICTORY, MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalVictory[0] );
|
|
else
|
|
#endif
|
|
SetMusicMode( MUSIC_TACTICAL_VICTORY );
|
|
|
|
LogBattleResults( LOG_VICTORY );
|
|
break;
|
|
|
|
case BATTLE_SURRENDERED:
|
|
case BATTLE_CAPTURED:
|
|
for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ )
|
|
{
|
|
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE) && !AM_A_ROBOT( MercPtrs[ i ] ) )
|
|
{ //Merc is active and alive, and not a vehicle or robot
|
|
if ( PlayerMercInvolvedInThisCombat( MercPtrs[ i ] ) )
|
|
{
|
|
// This morale event is PER INDIVIDUAL SOLDIER
|
|
HandleMoraleEvent( MercPtrs[ i ], MORALE_MERC_CAPTURED, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
}
|
|
}
|
|
}
|
|
HandleMoraleEvent( NULL, MORALE_HEARD_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
if( ProcessLoyalty() )HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
#ifdef NEWMUSIC
|
|
GlobalSoundID = MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalDeath[0];
|
|
if ( MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalDeath[0] != -1 )
|
|
SetMusicModeID( MUSIC_TACTICAL_DEATH, MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalDeath[0] );
|
|
else
|
|
#endif
|
|
SetMusicMode( MUSIC_TACTICAL_DEATH );
|
|
|
|
gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
break;
|
|
case BATTLE_DEFEAT:
|
|
HandleMoraleEvent( NULL, MORALE_HEARD_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
if( ProcessLoyalty() )HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
|
|
{
|
|
gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
}
|
|
else
|
|
{
|
|
gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
gsCiviliansEatenByMonsters = gpAR->ubAliveEnemies;
|
|
}
|
|
#ifdef NEWMUSIC
|
|
GlobalSoundID = MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalDeath[0];
|
|
if ( MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalDeath[0] != -1 )
|
|
SetMusicModeID( MUSIC_TACTICAL_DEATH, MusicSoundValues[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ].SoundTacticalDeath[0] );
|
|
else
|
|
#endif
|
|
SetMusicMode( MUSIC_TACTICAL_DEATH );
|
|
|
|
LogBattleResults( LOG_DEFEAT );
|
|
break;
|
|
|
|
case BATTLE_RETREAT:
|
|
|
|
//Tack on 5 minutes for retreat.
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds += 300000;
|
|
|
|
if( ProcessLoyalty() )HandleLoyaltyImplicationsOfMercRetreat( RETREAT_AUTORESOLVE, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
|
|
{
|
|
gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
}
|
|
else if( gpAR->ubAliveEnemies )
|
|
{
|
|
gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
gsCiviliansEatenByMonsters = gpAR->ubAliveEnemies;
|
|
}
|
|
LogBattleResults( LOG_DEFEAT );
|
|
break;
|
|
}
|
|
}
|
|
//Render the end battle condition.
|
|
switch( gpAR->ubBattleStatus )
|
|
{
|
|
case BATTLE_VICTORY:
|
|
SetFontForeground( FONT_LTGREEN );
|
|
swprintf( str, gpStrategicString[ STR_AR_OVER_VICTORY ] );
|
|
break;
|
|
case BATTLE_SURRENDERED:
|
|
case BATTLE_CAPTURED:
|
|
if( gpAR->ubBattleStatus == BATTLE_SURRENDERED )
|
|
{
|
|
swprintf( str, gpStrategicString[ STR_AR_OVER_SURRENDERED ] );
|
|
}
|
|
else
|
|
{
|
|
DisplayWrappedString( (UINT16)(gpAR->sCenterStartX+16), iScreenHeightOffset + 310, 108, 2,
|
|
FONT10ARIAL, FONT_YELLOW, gpStrategicString[ STR_ENEMY_CAPTURED ], FONT_BLACK, FALSE, LEFT_JUSTIFIED );
|
|
swprintf( str, gpStrategicString[ STR_AR_OVER_CAPTURED ] );
|
|
}
|
|
SetFontForeground( FONT_RED );
|
|
break;
|
|
case BATTLE_DEFEAT:
|
|
SetFontForeground( FONT_RED );
|
|
swprintf( str, gpStrategicString[ STR_AR_OVER_DEFEAT ] );
|
|
break;
|
|
case BATTLE_RETREAT:
|
|
SetFontForeground( FONT_YELLOW );
|
|
swprintf( str, gpStrategicString[ STR_AR_OVER_RETREATED ] );
|
|
break;
|
|
}
|
|
//Render the results of the battle.
|
|
SetFont( BLOCKFONT2 );
|
|
xp = gpAR->sCenterStartX + 12;
|
|
yp = iScreenHeightOffset + 218 + gpAR->bVerticalOffset;
|
|
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iIndent, 0, xp, yp, VO_BLT_SRCTRANSPARENCY, NULL );
|
|
xp = gpAR->sCenterStartX + 70 - StringPixLength( str, BLOCKFONT2 )/2;
|
|
yp = iScreenHeightOffset + 227 + gpAR->bVerticalOffset;
|
|
mprintf( xp, yp, str );
|
|
|
|
//Render the total battle time elapsed.
|
|
SetFont( FONT10ARIAL );
|
|
swprintf( str, L"%s: %dm %02ds",
|
|
gpStrategicString[ STR_AR_TIME_ELAPSED ],
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds/60000,
|
|
(gpAR->uiTotalElapsedBattleTimeInMilliseconds%60000)/1000 );
|
|
xp = gpAR->sCenterStartX + 70 - StringPixLength( str, FONT10ARIAL )/2;
|
|
yp = iScreenHeightOffset + 290 + gpAR->bVerticalOffset;
|
|
SetFontForeground( FONT_YELLOW );
|
|
mprintf( xp, yp, str );
|
|
}
|
|
|
|
MarkButtonsDirty();
|
|
InvalidateScreen();
|
|
}
|
|
|
|
static void ARCreateMilitia( UINT8 mclass, INT32 i, INT16 sX, INT16 sY)
|
|
{
|
|
// reset counter of how many mortars this team has rolled
|
|
ResetMortarsOnTeamCount();
|
|
ResetNumSquadleadersInArmyGroup(); // added by SANDRO
|
|
|
|
if( !gpBattleGroup ) {
|
|
//AssertMsg(0, "No battle group set while creating militia");
|
|
}
|
|
|
|
if( mclass == SOLDIER_CLASS_ELITE_MILITIA )
|
|
{
|
|
gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_ELITE_MILITIA, sX, sY );
|
|
if( gpCivs[i].pSoldier->ubBodyType == REGFEMALE )
|
|
{
|
|
gpCivs[i].usIndex = MILITIA3F_FACE;
|
|
}
|
|
else
|
|
{
|
|
gpCivs[i].usIndex = MILITIA3_FACE;
|
|
}
|
|
}
|
|
else if( mclass == SOLDIER_CLASS_REG_MILITIA )
|
|
{
|
|
gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_REG_MILITIA, sX, sY );
|
|
if( gpCivs[i].pSoldier->ubBodyType == REGFEMALE )
|
|
{
|
|
gpCivs[i].usIndex = MILITIA2F_FACE;
|
|
}
|
|
else
|
|
{
|
|
gpCivs[i].usIndex = MILITIA2_FACE;
|
|
}
|
|
}
|
|
else if( mclass == SOLDIER_CLASS_GREEN_MILITIA )
|
|
{
|
|
gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_GREEN_MILITIA, sX, sY );
|
|
if( gpCivs[i].pSoldier->ubBodyType == REGFEMALE )
|
|
{
|
|
gpCivs[i].usIndex = MILITIA1F_FACE;
|
|
}
|
|
else
|
|
{
|
|
gpCivs[i].usIndex = MILITIA1_FACE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertMsg( 0, "Attempting to illegally create a militia soldier." );
|
|
}
|
|
if( !gpCivs[ i ].pSoldier )
|
|
{
|
|
AssertMsg( 0, "Failed to create militia soldier for autoresolve." );
|
|
}
|
|
gpCivs[i].uiVObjectID = gpAR->iFaces;
|
|
gpCivs[i].pSoldier->sSectorX = sX;
|
|
gpCivs[i].pSoldier->sSectorY = sY;
|
|
swprintf( gpCivs[i].pSoldier->name, gpStrategicString[ STR_AR_MILITIA_NAME ] );
|
|
}
|
|
|
|
static void ARCreateMilitiaSquad( UINT8 *cnt, UINT8 ubEliteMilitia, UINT8 ubRegMilitia, UINT8 ubGreenMilitia, INT16 sX, INT16 sY)
|
|
{
|
|
while( *cnt < gpAR->ubCivs && (ubEliteMilitia || ubRegMilitia || ubGreenMilitia) )
|
|
{
|
|
if (ubEliteMilitia)
|
|
{
|
|
ARCreateMilitia( SOLDIER_CLASS_ELITE_MILITIA, *cnt, sX, sY);
|
|
ubEliteMilitia--;
|
|
}
|
|
else if (ubRegMilitia)
|
|
{
|
|
ARCreateMilitia( SOLDIER_CLASS_REG_MILITIA, *cnt, sX, sY);
|
|
ubRegMilitia--;
|
|
}
|
|
else if (ubGreenMilitia)
|
|
{
|
|
ARCreateMilitia( SOLDIER_CLASS_GREEN_MILITIA, *cnt, sX, sY);
|
|
ubGreenMilitia--;
|
|
}
|
|
|
|
(*cnt)++;
|
|
}
|
|
}
|
|
|
|
void CreateAutoResolveInterface()
|
|
{
|
|
VOBJECT_DESC VObjectDesc;
|
|
INT32 i, index;
|
|
HVOBJECT hVObject;
|
|
UINT8 ubGreenMilitia, ubRegMilitia, ubEliteMilitia;
|
|
UINT16 pMoveDir[4][3];
|
|
UINT8 uiDirNumber = 0;
|
|
UINT8 cnt;
|
|
|
|
//Setup new autoresolve blanket interface.
|
|
MSYS_DefineRegion( &gpAR->AutoResolveRegion, 0 + xResOffset, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGH-1, 0,
|
|
MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
gpAR->fExitAutoResolve = FALSE;
|
|
|
|
//Load the general panel image pieces, to be combined to make the dynamically sized window.
|
|
VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE;
|
|
sprintf( VObjectDesc.ImageFile, "Interface\\AutoResolve.sti" );
|
|
if( !AddVideoObject( &VObjectDesc, (UINT32 *)&gpAR->iPanelImages ) )
|
|
{
|
|
AssertMsg( 0, "Failed to load Interface\\AutoResolve.sti" );
|
|
}
|
|
|
|
//Load the button images file, and assign it to the first button.
|
|
/* OLD BEFORE THE MEDICAL BUTTON WAS ADDED
|
|
gpAR->iButtonImage[ PAUSE_BUTTON ] = LoadButtonImage( "Interface\\AutoBtns.sti", -1, 0, -1, 6, -1 );
|
|
if( gpAR->iButtonImage[ PAUSE_BUTTON ] == -1 )
|
|
{
|
|
AssertMsg( 0, "Failed to load Interface\\AutoBtns.sti" );
|
|
}
|
|
|
|
//Have the other buttons hook into the first button containing the images.
|
|
gpAR->iButtonImage[ PLAY_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 1, -1, 7, -1 );
|
|
gpAR->iButtonImage[ FAST_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 2, -1, 8, -1 );
|
|
gpAR->iButtonImage[ FINISH_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 3, -1, 9, -1 );
|
|
gpAR->iButtonImage[ YES_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 4, -1, 10, -1 );
|
|
gpAR->iButtonImage[ NO_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 5, -1, 11, -1 );
|
|
gpAR->iButtonImage[ RETREAT_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 12, -1, 13, -1 );
|
|
gpAR->iButtonImage[ DONE_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 14, -1, 15, -1 );
|
|
*/
|
|
gpAR->iButtonImage[ PAUSE_BUTTON ] = LoadButtonImage( "Interface\\AutoBtns.sti", -1, 0, -1, 7, -1 );
|
|
if( gpAR->iButtonImage[ PAUSE_BUTTON ] == -1 )
|
|
{
|
|
AssertMsg( 0, "Failed to load Interface\\AutoBtns.sti" );
|
|
}
|
|
|
|
//Have the other buttons hook into the first button containing the images.
|
|
gpAR->iButtonImage[ PLAY_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 1, -1, 8, -1 );
|
|
gpAR->iButtonImage[ FAST_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 2, -1, 9, -1 );
|
|
gpAR->iButtonImage[ FINISH_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 3, -1, 10, -1 );
|
|
gpAR->iButtonImage[ YES_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 4, -1, 11, -1 );
|
|
gpAR->iButtonImage[ NO_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 5, -1, 12, -1 );
|
|
gpAR->iButtonImage[ BANDAGE_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 6, -1, 13, -1 );
|
|
gpAR->iButtonImage[ RETREAT_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 14, -1, 15, -1 );
|
|
gpAR->iButtonImage[ DONEWIN_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 14, -1, 15, -1 );
|
|
gpAR->iButtonImage[ DONELOSE_BUTTON ] = UseLoadedButtonImage( gpAR->iButtonImage[ PAUSE_BUTTON ], -1, 16, -1, 17, -1 );
|
|
|
|
//Load the generic faces for civs and enemies
|
|
sprintf( VObjectDesc.ImageFile, "Interface\\SmFaces.sti" );
|
|
if( !AddVideoObject( &VObjectDesc, (UINT32 *)&gpAR->iFaces ) )
|
|
{
|
|
AssertMsg( 0, "Failed to load Interface\\SmFaces.sti" );
|
|
}
|
|
if( GetVideoObject( &hVObject, gpAR->iFaces ) )
|
|
{
|
|
hVObject->pShades[ 0 ] = Create16BPPPaletteShaded( hVObject->pPaletteEntry, 255, 255, 255, FALSE );
|
|
hVObject->pShades[ 1 ] = Create16BPPPaletteShaded( hVObject->pPaletteEntry, 250, 25, 25, TRUE );
|
|
}
|
|
|
|
//Add the battle over panels
|
|
sprintf( VObjectDesc.ImageFile, "Interface\\indent.sti" );
|
|
if( !AddVideoObject( &VObjectDesc, (UINT32 *)&gpAR->iIndent ) )
|
|
{
|
|
AssertMsg( 0, "Failed to load Interface\\indent.sti" );
|
|
}
|
|
|
|
//add all the faces now
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
VOBJECT_DESC VObjectDesc;
|
|
//Load the face
|
|
VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE;
|
|
|
|
/*
|
|
sprintf( VObjectDesc.ImageFile, "Faces\\65Face\\%02d.sti", gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].ubFaceIndex );
|
|
*/
|
|
|
|
if ( ( gpMercs[ i ].pSoldier->ubProfile >= 0 ) && ( gpMercs[ i ].pSoldier->ubProfile < 100 ) && ( gProfilesIMP[ gpMercs[ i ].pSoldier->ubProfile ].ProfilId == gpMercs[ i ].pSoldier->ubProfile ) )
|
|
{
|
|
sprintf( VObjectDesc.ImageFile, "IMPFaces\\65Face\\%02d.sti", gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].ubFaceIndex );
|
|
}
|
|
else if ( ( gpMercs[ i ].pSoldier->ubProfile > 99 ) && ( gProfilesIMP[ gpMercs[ i ].pSoldier->ubProfile ].ProfilId == gpMercs[ i ].pSoldier->ubProfile ) )
|
|
{
|
|
sprintf( VObjectDesc.ImageFile, "IMPFaces\\65Face\\%02d.sti", gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].ubFaceIndex );
|
|
}
|
|
else if ( ( gpMercs[ i ].pSoldier->ubProfile >= 0 ) && ( gpMercs[ i ].pSoldier->ubProfile < 100 ) )
|
|
{
|
|
sprintf( VObjectDesc.ImageFile, "Faces\\65Face\\%02d.sti", gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].ubFaceIndex );
|
|
}
|
|
else
|
|
{
|
|
sprintf( VObjectDesc.ImageFile, "Faces\\65Face\\%02d.sti", gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].ubFaceIndex );
|
|
}
|
|
|
|
if( !AddVideoObject( &VObjectDesc, &gpMercs[ i ].uiVObjectID ) )
|
|
{
|
|
sprintf( VObjectDesc.ImageFile, "Faces\\65Face\\speck.sti" );
|
|
if( !AddVideoObject( &VObjectDesc, &gpMercs[ i ].uiVObjectID ) )
|
|
{
|
|
AssertMsg( 0, String("Failed to load %Faces\\65Face\\%02d.sti or it's placeholder, speck.sti", gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].ubFaceIndex) );
|
|
}
|
|
}
|
|
|
|
|
|
if( GetVideoObject( &hVObject, gpMercs[ i ].uiVObjectID ) )
|
|
{
|
|
hVObject->pShades[ 0 ] = Create16BPPPaletteShaded( hVObject->pPaletteEntry, 255, 255, 255, FALSE );
|
|
hVObject->pShades[ 1 ] = Create16BPPPaletteShaded( hVObject->pPaletteEntry, 250, 25, 25, TRUE );
|
|
}
|
|
}
|
|
|
|
// 0verhaul: The following code was modified so that militia bookkeeping could be handled.
|
|
// In the previous auto-resolve method, promotions were often lost because the sector did not
|
|
// actually have the militia to be promoted--they came from surrounding sectors. The
|
|
// sector X and Y are not actually used for auto-resolve itself, so I can use it to adjust
|
|
// both the actual sector counts and the promotions, by using the origin sectors of each
|
|
// soldier.
|
|
ubEliteMilitia = MilitiaInSectorOfRank( gpAR->ubSectorX, gpAR->ubSectorY, ELITE_MILITIA );
|
|
ubRegMilitia = MilitiaInSectorOfRank( gpAR->ubSectorX, gpAR->ubSectorY, REGULAR_MILITIA );
|
|
ubGreenMilitia = MilitiaInSectorOfRank( gpAR->ubSectorX, gpAR->ubSectorY, GREEN_MILITIA );
|
|
|
|
// This block should be unnecessary. If the counts do not line up, there is a bug.
|
|
#if 0
|
|
while( ubEliteMilitia + ubRegMilitia + ubGreenMilitia < gpAR->ubCivs )
|
|
{
|
|
switch( PreRandom( 3 ) )
|
|
{
|
|
case 0: ubEliteMilitia++; break;
|
|
case 1: ubRegMilitia++; break;
|
|
case 2: ubGreenMilitia++; break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
cnt = 0;
|
|
// Add the militia in this sector
|
|
ARCreateMilitiaSquad( &cnt, ubEliteMilitia, ubRegMilitia, ubGreenMilitia, gpAR->ubSectorX, gpAR->ubSectorY);
|
|
|
|
// Add the militia in the surrounding sectors
|
|
// HEADROCK HAM 3.4: Changed argument configuration.
|
|
GenerateDirectionInfos( gpAR->ubSectorX, gpAR->ubSectorY, &uiDirNumber, pMoveDir, FALSE, TRUE );
|
|
for( i=0; i<uiDirNumber; i++)
|
|
{
|
|
INT16 sX = SECTORX( pMoveDir[ i ][0] );
|
|
INT16 sY = SECTORY( pMoveDir[ i ][0] );
|
|
|
|
ubEliteMilitia = MilitiaInSectorOfRank( sX, sY, ELITE_MILITIA );
|
|
ubRegMilitia = MilitiaInSectorOfRank( sX, sY, REGULAR_MILITIA );
|
|
ubGreenMilitia = MilitiaInSectorOfRank( sX, sY, GREEN_MILITIA );
|
|
|
|
ARCreateMilitiaSquad( &cnt, ubEliteMilitia, ubRegMilitia, ubGreenMilitia, sX, sY );
|
|
}
|
|
if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
|
|
{
|
|
for( i = 0, index = 0; i < gpAR->ubElites; ++i, ++index )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateEliteEnemy();
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
if( gpEnemies[i].pSoldier->ubBodyType == REGFEMALE )
|
|
{
|
|
gpEnemies[index].usIndex = ELITEF_FACE;
|
|
}
|
|
else
|
|
{
|
|
gpEnemies[index].usIndex = ELITE_FACE;
|
|
}
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_ELITE_NAME ] );
|
|
}
|
|
for( i = 0; i < gpAR->ubTroops; ++i, ++index )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateArmyTroop();
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = TROOP_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_TROOP_NAME ] );
|
|
}
|
|
for ( i = 0; i < gpAR->ubAdmins; ++i, ++index )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateAdministrator();
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = ADMIN_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_ADMINISTRATOR_NAME ] );
|
|
}
|
|
for ( i = 0; i < gpAR->ubTanks; ++i, ++index )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateEnemyTank();
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = TANK_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[STR_AR_TANK_NAME] );
|
|
}
|
|
AssociateEnemiesWithStrategicGroups();
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0, index = 0; i < gpAR->ubAFCreatures; ++i, ++index )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateCreature( ADULTFEMALEMONSTER );
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = AF_CREATURE_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] );
|
|
}
|
|
for( i = 0; i < gpAR->ubAMCreatures; i++, index++ )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateCreature( AM_MONSTER );
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = AM_CREATURE_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] );
|
|
}
|
|
for( i = 0; i < gpAR->ubYFCreatures; i++, index++ )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateCreature( YAF_MONSTER );
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = YF_CREATURE_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] );
|
|
}
|
|
for( i = 0; i < gpAR->ubYMCreatures; i++, index++ )
|
|
{
|
|
gpEnemies[index].pSoldier = TacticalCreateCreature( YAM_MONSTER );
|
|
gpEnemies[index].uiVObjectID = gpAR->iFaces;
|
|
gpEnemies[index].usIndex = YM_CREATURE_FACE;
|
|
gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX;
|
|
gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY;
|
|
swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] );
|
|
}
|
|
}
|
|
|
|
if( gpAR->ubSectorX == gWorldSectorX && gpAR->ubSectorY == gWorldSectorY && !gbWorldSectorZ )
|
|
{
|
|
CheckAndHandleUnloadingOfCurrentWorld();
|
|
}
|
|
else
|
|
{
|
|
gfBlitBattleSectorLocator = FALSE;
|
|
}
|
|
|
|
//Build the interface buffer, and blit the "shaded" background. This info won't
|
|
//change from now on, but will be used to restore text.
|
|
BuildInterfaceBuffer();
|
|
BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, 0 + xResOffset, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
|
|
|
|
//If we are bumping up the interface, then also use that piece of info to
|
|
//move the buttons up by the same amount.
|
|
|
|
gpAR->bVerticalOffset = (240 - gpAR->sHeight/2) > 120 ? -40 : 0;
|
|
|
|
//Create the buttons -- subject to relocation
|
|
gpAR->iButton[ PLAY_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ PLAY_BUTTON ] , (INT16)(gpAR->sCenterStartX+11), (INT16)(iScreenHeightOffset + 240+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, PlayButtonCallback );
|
|
gpAR->iButton[ FAST_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ FAST_BUTTON ] , (INT16)(gpAR->sCenterStartX+51), (INT16)(iScreenHeightOffset + 240+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, FastButtonCallback );
|
|
gpAR->iButton[ FINISH_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ FINISH_BUTTON ] , (INT16)(gpAR->sCenterStartX+91), (INT16)(iScreenHeightOffset + 240+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, FinishButtonCallback );
|
|
gpAR->iButton[ PAUSE_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ PAUSE_BUTTON ] , (INT16)(gpAR->sCenterStartX+11), (INT16)(iScreenHeightOffset + 274+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, PauseButtonCallback );
|
|
|
|
gpAR->iButton[ RETREAT_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ RETREAT_BUTTON ], (INT16)(gpAR->sCenterStartX+51), (INT16)(iScreenHeightOffset + 274+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, RetreatButtonCallback );
|
|
if( !gpAR->ubMercs )
|
|
{
|
|
DisableButton( gpAR->iButton[ RETREAT_BUTTON ] );
|
|
}
|
|
SpecifyGeneralButtonTextAttributes( gpAR->iButton[ RETREAT_BUTTON ], gpStrategicString[STR_AR_RETREAT_BUTTON], BLOCKFONT2, 169, FONT_NEARBLACK );
|
|
|
|
gpAR->iButton[ BANDAGE_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ BANDAGE_BUTTON ] , (INT16)(gpAR->sCenterStartX+11), (INT16)(iScreenHeightOffset + 245+gpAR->bVerticalOffset), BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, BandageButtonCallback );
|
|
|
|
gpAR->iButton[ DONEWIN_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ DONEWIN_BUTTON ], (INT16)(gpAR->sCenterStartX+51), (INT16)(iScreenHeightOffset + 245+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, DoneButtonCallback );
|
|
SpecifyGeneralButtonTextAttributes( gpAR->iButton[ DONEWIN_BUTTON ], gpStrategicString[STR_AR_DONE_BUTTON], BLOCKFONT2, 169, FONT_NEARBLACK );
|
|
|
|
gpAR->iButton[ DONELOSE_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ DONELOSE_BUTTON ], (INT16)(gpAR->sCenterStartX+25), (INT16)(iScreenHeightOffset + 245+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, DoneButtonCallback );
|
|
SpecifyGeneralButtonTextAttributes( gpAR->iButton[ DONELOSE_BUTTON ], gpStrategicString[STR_AR_DONE_BUTTON], BLOCKFONT2, 169, FONT_NEARBLACK );
|
|
gpAR->iButton[ YES_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ YES_BUTTON ], (INT16)(gpAR->sCenterStartX+21), (INT16)(iScreenHeightOffset + 257+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, AcceptSurrenderCallback );
|
|
gpAR->iButton[ NO_BUTTON ] =
|
|
QuickCreateButton( gpAR->iButtonImage[ NO_BUTTON ], (INT16)(gpAR->sCenterStartX+81), (INT16)(iScreenHeightOffset + 257+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
|
|
DEFAULT_MOVE_CALLBACK, RejectSurrenderCallback );
|
|
HideButton( gpAR->iButton[ YES_BUTTON ] );
|
|
HideButton( gpAR->iButton[ NO_BUTTON ] );
|
|
HideButton( gpAR->iButton[ DONEWIN_BUTTON ] );
|
|
HideButton( gpAR->iButton[ DONELOSE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
ButtonList[ gpAR->iButton[ PLAY_BUTTON ] ]->uiFlags |= BUTTON_CLICKED_ON;
|
|
}
|
|
|
|
void RemoveAutoResolveInterface( BOOLEAN fDeleteForGood )
|
|
{
|
|
INT32 i;
|
|
UINT8 ubCurrentRank;
|
|
UINT8 ubCurrentGroupID = 0;
|
|
BOOLEAN fFirstGroup = TRUE;
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2");
|
|
//VtResumeSampling();
|
|
|
|
MSYS_RemoveRegion( &gpAR->AutoResolveRegion );
|
|
DeleteVideoObjectFromIndex( gpAR->iPanelImages );
|
|
DeleteVideoObjectFromIndex( gpAR->iFaces );
|
|
DeleteVideoObjectFromIndex( gpAR->iIndent );
|
|
DeleteVideoSurfaceFromIndex( gpAR->iInterfaceBuffer );
|
|
|
|
if( fDeleteForGood )
|
|
{ //Delete the soldier instances -- done when we are completely finished.
|
|
|
|
//KM: By request of AM, I have added this bleeding event in cases where autoresolve is
|
|
// complete and there are bleeding mercs remaining. AM coded the internals
|
|
// of the strategic event.
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->bBleeding && gpMercs[ i ].pSoldier->stats.bLife )
|
|
{
|
|
// ARM: only one event is needed regardless of how many are bleeding
|
|
AddStrategicEvent( EVENT_BANDAGE_BLEEDING_MERCS, GetWorldTotalMin() + 1, 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ARM: Update assignment flashing: Doctors may now have new patients or lost them all, etc.
|
|
gfReEvaluateEveryonesNothingToDo = TRUE;
|
|
|
|
|
|
if( gpAR->pRobotCell)
|
|
{
|
|
gpAR->pRobotCell->pSoldier->UpdateRobotControllerGivenRobot( );
|
|
}
|
|
for( i = 0; i < gpAR->iNumMercFaces; i++ )
|
|
{
|
|
if( i >= gpAR->iActualMercFaces )
|
|
TacticalRemoveSoldierPointer( gpMercs[ i ].pSoldier, FALSE );
|
|
else
|
|
{ //Record finishing information for our mercs
|
|
if( !gpMercs[ i ].pSoldier->stats.bLife )
|
|
{
|
|
StrategicHandlePlayerTeamMercDeath( gpMercs[ i ].pSoldier );
|
|
|
|
// now remove character from a squad
|
|
RemoveCharacterFromSquads( gpMercs[ i ].pSoldier );
|
|
ChangeSoldiersAssignment( gpMercs[ i ].pSoldier, ASSIGNMENT_DEAD );
|
|
|
|
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpMercs[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
|
}
|
|
else if( gpAR->ubBattleStatus == BATTLE_SURRENDERED || gpAR->ubBattleStatus == BATTLE_CAPTURED )
|
|
{
|
|
EnemyCapturesPlayerSoldier( gpMercs[ i ].pSoldier );
|
|
}
|
|
else if( gpAR->ubBattleStatus == BATTLE_VICTORY )
|
|
{ //merc is alive, so group them at the center gridno.
|
|
gpMercs[ i ].pSoldier->ubStrategicInsertionCode = INSERTION_CODE_CENTER;
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - records - autoresolve battles
|
|
if( gpMercs[ i ].pSoldier->ubProfile != NO_PROFILE )
|
|
{
|
|
// if we ran before the battle was over but the others won in the end, we still get retreated counter, no victory
|
|
if ( gpMercs[ i ].uiFlags & CELL_RETREATED || gpMercs[ i ].uiFlags & CELL_RETREATING )
|
|
{
|
|
gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].records.usBattlesRetreated++;
|
|
}
|
|
else
|
|
{
|
|
gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].records.usBattlesAutoresolve++;
|
|
if ( gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].records.usLargestBattleFought < gpAR->ubEnemies )
|
|
{
|
|
gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].records.usLargestBattleFought = gpAR->ubEnemies;
|
|
}
|
|
}
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - merc records - times retreated counter
|
|
else if( gpAR->ubBattleStatus == BATTLE_RETREAT )
|
|
{
|
|
if( gpMercs[ i ].uiFlags & CELL_RETREATED || gpMercs[ i ].uiFlags & CELL_RETREATING )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->ubProfile != NO_PROFILE && !AM_A_ROBOT( gpMercs[ i ].pSoldier ) )
|
|
{
|
|
gMercProfiles[ gpMercs[ i ].pSoldier->ubProfile ].records.usBattlesRetreated++;
|
|
}
|
|
}
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}
|
|
}
|
|
for( i = 0; i < gpAR->iNumMercFaces; i++ )
|
|
{
|
|
if( gpAR->ubBattleStatus == BATTLE_VICTORY && gpMercs[ i ].pSoldier->stats.bLife >= OKLIFE )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->ubGroupID != ubCurrentGroupID )
|
|
{
|
|
ubCurrentGroupID = gpMercs[ i ].pSoldier->ubGroupID;
|
|
|
|
// look for NPCs to stop for, anyone is too tired to keep going, if all OK rebuild waypoints & continue movement
|
|
// NOTE: Only the first group found will stop for NPCs, it's just too much hassle to stop them all
|
|
PlayerGroupArrivedSafelyInSector( GetGroup( gpMercs[ i ].pSoldier->ubGroupID ), fFirstGroup );
|
|
fFirstGroup = FALSE;
|
|
}
|
|
}
|
|
gpMercs[ i ].pSoldier = NULL;
|
|
}
|
|
|
|
// End capture squence....
|
|
if( gpAR->ubBattleStatus == BATTLE_SURRENDERED || gpAR->ubBattleStatus == BATTLE_CAPTURED )
|
|
{
|
|
EndCaptureSequence( );
|
|
}
|
|
|
|
}
|
|
//Delete all of the faces.
|
|
for( i = 0; i < gpAR->iNumMercFaces; i++ )
|
|
{
|
|
if( gpMercs[ i ].uiVObjectID != -1 )
|
|
DeleteVideoObjectFromIndex( gpMercs[ i ].uiVObjectID );
|
|
gpMercs[ i ].uiVObjectID = -1;
|
|
if( gpMercs[ i ].pRegion )
|
|
{
|
|
MSYS_RemoveRegion( gpMercs[ i ].pRegion );
|
|
MemFree( gpMercs[ i ].pRegion );
|
|
gpMercs[ i ].pRegion = NULL;
|
|
}
|
|
}
|
|
//Delete all militia
|
|
gbGreenToElitePromotions = 0;
|
|
gbGreenToRegPromotions = 0;
|
|
gbRegToElitePromotions = 0;
|
|
gbMilitiaPromotions = 0;
|
|
for( i = 0; i < MAX_AR_TEAM_SIZE; ++i )
|
|
{
|
|
if( gpCivs[ i ].pSoldier )
|
|
{
|
|
ubCurrentRank = GREEN_MILITIA;
|
|
switch( gpCivs[ i ].pSoldier->ubSoldierClass )
|
|
{
|
|
case SOLDIER_CLASS_GREEN_MILITIA: ubCurrentRank = GREEN_MILITIA; break;
|
|
case SOLDIER_CLASS_REG_MILITIA: ubCurrentRank = REGULAR_MILITIA; break;
|
|
case SOLDIER_CLASS_ELITE_MILITIA: ubCurrentRank = ELITE_MILITIA; break;
|
|
default:
|
|
#ifdef JA2BETAVERSION
|
|
ScreenMsg( FONT_RED, MSG_ERROR, L"Removing autoresolve militia with invalid ubSoldierClass %d.",gpCivs[ i ].pSoldier->ubSoldierClass );
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
// Flugente: drop sector equipment
|
|
gpCivs[ i ].pSoldier->DropSectorEquipment();
|
|
|
|
if( fDeleteForGood && gpCivs[ i ].pSoldier->stats.bLife < OKLIFE/2 )
|
|
{
|
|
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpCivs[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
|
StrategicRemoveMilitiaFromSector( gpCivs[ i ].pSoldier->sSectorX, gpCivs[ i ].pSoldier->sSectorY, ubCurrentRank, 1 );
|
|
|
|
if( ProcessLoyalty() )
|
|
HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_NATIVE_KILLED, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
}
|
|
else
|
|
{
|
|
UINT8 ubPromotions;
|
|
// this will check for promotions and handle them for you
|
|
if( fDeleteForGood && ( gpCivs[ i ].pSoldier->ubMilitiaKills > 0) && ( ubCurrentRank < ELITE_MILITIA ) )
|
|
{
|
|
ubPromotions = CheckOneMilitiaForPromotion( gpCivs[ i ].pSoldier->sSectorX, gpCivs[ i ].pSoldier->sSectorY, ubCurrentRank, gpCivs[ i ].pSoldier->ubMilitiaKills );
|
|
if( ubPromotions )
|
|
{
|
|
if( ubPromotions == 2 )
|
|
{
|
|
++gbGreenToElitePromotions;
|
|
++gbMilitiaPromotions;
|
|
|
|
ubCurrentRank = ELITE_MILITIA;
|
|
}
|
|
else if( gpCivs[ i ].pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA )
|
|
{
|
|
++gbGreenToRegPromotions;
|
|
++gbMilitiaPromotions;
|
|
|
|
ubCurrentRank = REGULAR_MILITIA;
|
|
}
|
|
else if( gpCivs[ i ].pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA )
|
|
{
|
|
++gbRegToElitePromotions;
|
|
++gbMilitiaPromotions;
|
|
|
|
ubCurrentRank = ELITE_MILITIA;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TacticalRemoveSoldierPointer( gpCivs[ i ].pSoldier, FALSE );
|
|
memset( &gpCivs[ i ], 0, sizeof( SOLDIERCELL ) );
|
|
}
|
|
}
|
|
|
|
// Flugente: after we fought, move any militia in groups into the sector garrison - the player will likely want to rethink orders for the survivors
|
|
// Flugente: in case militia died we have to lower the group sizes
|
|
// loop over all militia groups that are in this sector and alter their size. If a group has size 0 delete it. If any militia are left place them into this sector
|
|
{
|
|
GROUP* pGroup = gpGroupList;
|
|
while ( pGroup )
|
|
{
|
|
if ( pGroup->usGroupTeam == MILITIA_TEAM && pGroup->ubSectorX == gpAR->ubSectorX && pGroup->ubSectorY == gpAR->ubSectorY )
|
|
{
|
|
GROUP* pDeleteGroup = pGroup;
|
|
pGroup = pGroup->next;
|
|
|
|
DissolveMilitiaGroup( pDeleteGroup->ubGroupID );
|
|
}
|
|
else
|
|
pGroup = pGroup->next;
|
|
}
|
|
}
|
|
|
|
//Record and process all enemy deaths
|
|
for( i = 0; i < MAX_AR_TEAM_SIZE; ++i )
|
|
{
|
|
if( gpEnemies[ i ].pSoldier )
|
|
{
|
|
if( fDeleteForGood && gpEnemies[ i ].pSoldier->stats.bLife < OKLIFE )
|
|
{
|
|
TrackEnemiesKilled( ENEMY_KILLED_IN_AUTO_RESOLVE, gpEnemies[ i ].pSoldier->ubSoldierClass );
|
|
if( ProcessLoyalty() )HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ENEMY_KILLED, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
ProcessQueenCmdImplicationsOfDeath( gpEnemies[ i ].pSoldier );
|
|
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpEnemies[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
|
}
|
|
}
|
|
}
|
|
//Eliminate all excess soldiers
|
|
if( fDeleteForGood )
|
|
{ //Warp the game time accordingly
|
|
if( gpAR->ubBattleStatus == BATTLE_VICTORY )
|
|
{ //Get rid of any extra enemies that could be here.
|
|
EliminateAllEnemies( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
}
|
|
else
|
|
{ //The enemy won, so repoll movement.
|
|
ResetMovementForNonPlayerGroupsInLocation( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
}
|
|
}
|
|
//Physically delete the soldiers now.
|
|
for( i = 0; i < MAX_AR_TEAM_SIZE; ++i )
|
|
{
|
|
if( gpEnemies[ i ].pSoldier )
|
|
{
|
|
TacticalRemoveSoldierPointer( gpEnemies[ i ].pSoldier, FALSE );
|
|
memset( &gpEnemies[ i ], 0, sizeof( SOLDIERCELL ) );
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < NUM_AR_BUTTONS; ++i )
|
|
{
|
|
UnloadButtonImage( gpAR->iButtonImage[ i ] );
|
|
RemoveButton( gpAR->iButton[ i ] );
|
|
}
|
|
|
|
if( fDeleteForGood )
|
|
{
|
|
//Warp the game time accordingly
|
|
WarpGameTime( gpAR->uiTotalElapsedBattleTimeInMilliseconds/1000, WARPTIME_NO_PROCESSING_OF_EVENTS );
|
|
|
|
//Deallocate all of the global memory.
|
|
//Everything internal to them, should have already been deleted.
|
|
MemFree( gpAR );
|
|
gpAR = NULL;
|
|
|
|
MemFree( gpMercs );
|
|
gpMercs = NULL;
|
|
|
|
MemFree( gpCivs );
|
|
gpCivs = NULL;
|
|
|
|
MemFree( gpEnemies );
|
|
gpEnemies = NULL;
|
|
}
|
|
|
|
//KM : Aug 09, 1999 Patch fix -- Would break future dialog while time compressing
|
|
gTacticalStatus.ubCurrentTeam = gbPlayerNum;
|
|
|
|
gpBattleGroup = NULL;
|
|
|
|
if( gubEnemyEncounterCode == CREATURE_ATTACK_CODE )
|
|
{
|
|
gubNumCreaturesAttackingTown = 0;
|
|
gubSectorIDOfCreatureAttack = 0;
|
|
}
|
|
//VtPauseSampling();
|
|
}
|
|
|
|
void PauseButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
ButtonList[ gpAR->iButton[ PLAY_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FAST_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FINISH_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
gpAR->fPaused = TRUE;
|
|
}
|
|
}
|
|
|
|
void PlayButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
ButtonList[ gpAR->iButton[ PAUSE_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FAST_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FINISH_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
gpAR->uiTimeSlice = 1000 * gpAR->ubTimeModifierPercentage / 100;
|
|
gpAR->fPaused = FALSE;
|
|
}
|
|
}
|
|
|
|
void FastButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
ButtonList[ gpAR->iButton[ PAUSE_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ PLAY_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FINISH_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
gpAR->uiTimeSlice = 4000;
|
|
gpAR->fPaused = FALSE;
|
|
}
|
|
}
|
|
|
|
void FinishButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
ButtonList[ gpAR->iButton[ PAUSE_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ PLAY_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FAST_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
gpAR->uiTimeSlice = 0xffffffff;
|
|
gpAR->fSound = FALSE;
|
|
gpAR->fPaused = FALSE;
|
|
PlayJA2StreamingSample( AUTORESOLVE_FINISHFX, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
|
|
void RetreatButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
INT32 i;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( !(gpMercs[ i ].uiFlags & (CELL_RETREATING|CELL_RETREATED)) )
|
|
{
|
|
gpMercs[ i ].uiFlags |= CELL_RETREATING | CELL_DIRTY;
|
|
//Gets to retreat after a total of 2 attacks.
|
|
gpMercs[ i ].usNextAttack = (UINT16)((1000 + gpMercs[ i ].usNextAttack * 2 + PreRandom( 2000 - gpMercs[ i ].usAttack ))*2);
|
|
gpAR->usPlayerAttack -= gpMercs[ i ].usAttack;
|
|
gpMercs[ i ].usAttack = 0;
|
|
}
|
|
}
|
|
if( gpAR->pRobotCell )
|
|
{ //if robot is retreating, set the retreat time to be the same as the robot's controller.
|
|
UINT8 ubRobotControllerID;
|
|
|
|
ubRobotControllerID = gpAR->pRobotCell->pSoldier->ubRobotRemoteHolderID;
|
|
|
|
if( ubRobotControllerID == NOBODY )
|
|
{
|
|
gpAR->pRobotCell->uiFlags &= ~CELL_RETREATING;
|
|
gpAR->pRobotCell->uiFlags |= CELL_DIRTY;
|
|
gpAR->pRobotCell->usNextAttack = 0xffff;
|
|
return;
|
|
}
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( ubRobotControllerID == gpMercs[ i ].pSoldier->ubID )
|
|
{ //Found the controller, make the robot's retreat time match the contollers.
|
|
gpAR->pRobotCell->usNextAttack = gpMercs[ i ].usNextAttack;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DetermineBandageButtonState()
|
|
{
|
|
INT32 i;
|
|
OBJECTTYPE *pKit = NULL;
|
|
BOOLEAN fFound = FALSE;
|
|
|
|
//Does anyone need bandaging?
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->bBleeding && gpMercs[ i ].pSoldier->stats.bLife )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( !fFound )
|
|
{
|
|
DisableButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
SetButtonFastHelpText( gpAR->iButton[ BANDAGE_BUTTON ], gzLateLocalizedString[ 11 ] );
|
|
return;
|
|
}
|
|
|
|
//Do we have any doctors?
|
|
fFound = FALSE;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->stats.bLife >= OKLIFE &&
|
|
!gpMercs[ i ].pSoldier->bCollapsed &&
|
|
gpMercs[ i ].pSoldier->stats.bMedical > 0 )
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
if( !fFound )
|
|
{ //No doctors
|
|
DisableButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
SetButtonFastHelpText( gpAR->iButton[ BANDAGE_BUTTON ], gzLateLocalizedString[ 8 ] );
|
|
return;
|
|
}
|
|
|
|
//Do have a kit?
|
|
pKit = FindMedicalKit();
|
|
if( !pKit )
|
|
{ //No kits
|
|
DisableButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
SetButtonFastHelpText( gpAR->iButton[ BANDAGE_BUTTON ], gzLateLocalizedString[ 9 ] );
|
|
return;
|
|
}
|
|
|
|
//Allow bandaging.
|
|
EnableButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
SetButtonFastHelpText( gpAR->iButton[ BANDAGE_BUTTON ], gzLateLocalizedString[ 12 ] );
|
|
}
|
|
|
|
void BandageButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
AutoBandageMercs();
|
|
SetupDoneInterface();
|
|
}
|
|
}
|
|
|
|
void DoneButtonCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
gpAR->fExitAutoResolve = TRUE;
|
|
}
|
|
}
|
|
|
|
void MercCellMouseMoveCallback( MOUSE_REGION *reg, INT32 reason )
|
|
{
|
|
//Find the merc with the same region.
|
|
INT32 i;
|
|
SOLDIERCELL *pCell = NULL;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pRegion == reg )
|
|
{
|
|
pCell = &gpMercs[ i ];
|
|
break;
|
|
}
|
|
}
|
|
Assert( pCell );
|
|
if( gpAR->fPendingSurrender )
|
|
{ //Can't setup retreats when pending surrender.
|
|
pCell->uiFlags &= ~CELL_SHOWRETREATTEXT;
|
|
pCell->uiFlags |= CELL_DIRTY;
|
|
return;
|
|
}
|
|
if( reg->uiFlags & MSYS_MOUSE_IN_AREA )
|
|
{
|
|
if( !(pCell->uiFlags & CELL_SHOWRETREATTEXT) )
|
|
pCell->uiFlags |= CELL_SHOWRETREATTEXT | CELL_DIRTY;
|
|
}
|
|
else
|
|
{
|
|
if( pCell->uiFlags & CELL_SHOWRETREATTEXT )
|
|
{
|
|
pCell->uiFlags &= ~CELL_SHOWRETREATTEXT;
|
|
pCell->uiFlags |= CELL_DIRTY;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MercCellMouseClickCallback( MOUSE_REGION *reg, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
//Find the merc with the same region.
|
|
INT32 i;
|
|
SOLDIERCELL *pCell = NULL;
|
|
|
|
if( gpAR->fPendingSurrender )
|
|
{ //Can't setup retreats when pending surrender.
|
|
return;
|
|
}
|
|
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pRegion == reg )
|
|
{
|
|
pCell = &gpMercs[ i ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert(pCell);
|
|
if( pCell->uiFlags & ( CELL_RETREATING | CELL_RETREATED ) )
|
|
{ //already retreated/retreating.
|
|
return;
|
|
}
|
|
|
|
Assert( pCell );
|
|
|
|
if( pCell == gpAR->pRobotCell )
|
|
{ //robot retreats only when controller retreats
|
|
return;
|
|
}
|
|
|
|
pCell->uiFlags |= CELL_RETREATING | CELL_DIRTY;
|
|
//Gets to retreat after a total of 2 attacks.
|
|
pCell->usNextAttack = (UINT16)((1000 + pCell->usNextAttack * 5 + PreRandom( 2000 - pCell->usAttack ))*2);
|
|
gpAR->usPlayerAttack -= pCell->usAttack;
|
|
pCell->usAttack = 0;
|
|
|
|
if( gpAR->pRobotCell )
|
|
{ //if controller is retreating, make the robot retreat too.
|
|
UINT8 ubRobotControllerID;
|
|
|
|
ubRobotControllerID = gpAR->pRobotCell->pSoldier->ubRobotRemoteHolderID;
|
|
|
|
if( ubRobotControllerID == NOBODY )
|
|
{
|
|
gpAR->pRobotCell->uiFlags &= ~CELL_RETREATING;
|
|
gpAR->pRobotCell->uiFlags |= CELL_DIRTY;
|
|
gpAR->pRobotCell->usNextAttack = 0xffff;
|
|
}
|
|
else if( ubRobotControllerID == pCell->pSoldier->ubID )
|
|
{ //Found the controller, make the robot's retreat time match the contollers.
|
|
gpAR->pRobotCell->uiFlags |= CELL_RETREATING | CELL_DIRTY;
|
|
gpAR->pRobotCell->usNextAttack = pCell->usNextAttack;
|
|
gpAR->usPlayerAttack -= gpAR->pRobotCell->usAttack;
|
|
gpAR->pRobotCell->usAttack = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Determine how many players, militia, and enemies that are going at it, and use these values
|
|
//to figure out how many rows and columns we can use. The will effect the size of the panel.
|
|
void CalculateAutoResolveInfo()
|
|
{
|
|
VOBJECT_DESC VObjectDesc;
|
|
GROUP *pGroup;
|
|
PLAYERGROUP *pPlayer;
|
|
Assert( gpAR->ubSectorX >= 1 && gpAR->ubSectorX <= 16 );
|
|
Assert( gpAR->ubSectorY >= 1 && gpAR->ubSectorY <= 16 );
|
|
|
|
if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
|
|
{
|
|
// GetNumberOfEnemiesInSector( gpAR->ubSectorX, gpAR->ubSectorY,
|
|
GetNumberOfEnemiesInFiveSectors( gpAR->ubSectorX, gpAR->ubSectorY,
|
|
&gpAR->ubAdmins, &gpAR->ubTroops, &gpAR->ubElites, &gpAR->ubTanks );
|
|
gpAR->ubEnemies = (UINT8)min( gpAR->ubAdmins + gpAR->ubTroops + gpAR->ubElites + gpAR->ubTanks, MAX_AR_TEAM_SIZE );
|
|
}
|
|
else
|
|
{
|
|
if( gfTransferTacticalOppositionToAutoResolve )
|
|
{
|
|
DetermineCreatureTownCompositionBasedOnTacticalInformation( &gubNumCreaturesAttackingTown,
|
|
&gpAR->ubYMCreatures, &gpAR->ubYFCreatures,
|
|
&gpAR->ubAMCreatures, &gpAR->ubAFCreatures );
|
|
}
|
|
else
|
|
{
|
|
DetermineCreatureTownComposition( gubNumCreaturesAttackingTown,
|
|
&gpAR->ubYMCreatures, &gpAR->ubYFCreatures,
|
|
&gpAR->ubAMCreatures, &gpAR->ubAFCreatures );
|
|
}
|
|
|
|
// WANNE: Quickfix: If we have 0 creatues call the DetermineCreatureTownCompositionBasedOnTacticalInformation(),
|
|
// because we called the DetermineCreatureTownComposition() method before which was wrong.
|
|
if (gubNumCreaturesAttackingTown == 0)
|
|
{
|
|
DetermineCreatureTownCompositionBasedOnTacticalInformation( &gubNumCreaturesAttackingTown,
|
|
&gpAR->ubYMCreatures, &gpAR->ubYFCreatures,
|
|
&gpAR->ubAMCreatures, &gpAR->ubAFCreatures );
|
|
}
|
|
|
|
gpAR->ubEnemies = (UINT8)min( gpAR->ubYMCreatures + gpAR->ubYFCreatures + gpAR->ubAMCreatures + gpAR->ubAFCreatures, MAX_AR_TEAM_SIZE );
|
|
}
|
|
gfTransferTacticalOppositionToAutoResolve = FALSE;
|
|
// gpAR->ubCivs = CountAllMilitiaInSector( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
gpAR->ubCivs = CountAllMilitiaInFiveSectors( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
gpAR->ubMercs = 0;
|
|
VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE;
|
|
pGroup = gpGroupList;
|
|
while( pGroup )
|
|
{
|
|
if ( PlayerGroupInvolvedInThisCombat( pGroup ) )
|
|
{
|
|
pPlayer = pGroup->pPlayerList;
|
|
while( pPlayer )
|
|
{
|
|
// NOTE: Must check each merc individually, e.g. Robot without controller is an uninvolved merc on an involved group!
|
|
if ( PlayerMercInvolvedInThisCombat( pPlayer->pSoldier ) )
|
|
{
|
|
gpMercs[ gpAR->ubMercs ].pSoldier = pPlayer->pSoldier;
|
|
|
|
//!!! CLEAR OPPCOUNT HERE. All of these soldiers are guaranteed to not be in tactical anymore.
|
|
//ClearOppCount( pPlayer->pSoldier );
|
|
|
|
gpAR->ubMercs++;
|
|
if( AM_AN_EPC( pPlayer->pSoldier ) )
|
|
{
|
|
gpAR->fCaptureNotPermittedDueToEPCs = TRUE;
|
|
}
|
|
if( AM_A_ROBOT( pPlayer->pSoldier ) )
|
|
{
|
|
gpAR->pRobotCell = &gpMercs[ gpAR->ubMercs - 1 ];
|
|
gpAR->pRobotCell->pSoldier->UpdateRobotControllerGivenRobot( );
|
|
}
|
|
}
|
|
pPlayer = pPlayer->next;
|
|
}
|
|
}
|
|
pGroup = pGroup->next;
|
|
}
|
|
gpAR->iNumMercFaces = gpAR->ubMercs;
|
|
gpAR->iActualMercFaces = gpAR->ubMercs;
|
|
|
|
giMaxMilitiaToRender = 50 - ( (gpAR->ubMercs + 4) / 5 ) * 5;
|
|
|
|
CalculateRowsAndColumns();
|
|
}
|
|
|
|
void ResetAutoResolveInterface()
|
|
{
|
|
guiPreRandomIndex = gpAR->uiPreRandomIndex;
|
|
|
|
RemoveAutoResolveInterface( FALSE );
|
|
|
|
gpAR->ubBattleStatus = BATTLE_IN_PROGRESS;
|
|
|
|
if( !gpAR->ubCivs && !gpAR->ubMercs )
|
|
gpAR->ubCivs = 1;
|
|
|
|
//Make sure the number of enemy portraits is the same as needed.
|
|
//The debug keypresses may add or remove more than one at a time.
|
|
while ( gpAR->ubElites + gpAR->ubAdmins + gpAR->ubTroops + gpAR->ubTanks > gpAR->ubEnemies )
|
|
{
|
|
switch( PreRandom( 5 ) )
|
|
{
|
|
case 0: if( gpAR->ubElites ) { gpAR->ubElites--; break; }
|
|
case 1: case 2: if( gpAR->ubAdmins ) { gpAR->ubAdmins--; break; }
|
|
case 3: case 4: if( gpAR->ubTroops ) { gpAR->ubTroops--; break; }
|
|
}
|
|
}
|
|
while ( gpAR->ubElites + gpAR->ubAdmins + gpAR->ubTroops + gpAR->ubTanks < gpAR->ubEnemies )
|
|
{
|
|
switch( PreRandom( 5 ) )
|
|
{
|
|
case 0: gpAR->ubElites++; break;
|
|
case 1: case 2: gpAR->ubAdmins++; break;
|
|
case 3: case 4: gpAR->ubTroops++; break;
|
|
}
|
|
}
|
|
|
|
|
|
//Do the same for the player mercs.
|
|
while( gpAR->iNumMercFaces > gpAR->ubMercs && gpAR->iNumMercFaces > gpAR->iActualMercFaces )
|
|
{ //Removing temp mercs
|
|
gpAR->iNumMercFaces--;
|
|
TacticalRemoveSoldierPointer( gpMercs[gpAR->iNumMercFaces].pSoldier, FALSE );
|
|
gpMercs[gpAR->iNumMercFaces].pSoldier = NULL;
|
|
}
|
|
while( gpAR->iNumMercFaces < gpAR->ubMercs && gpAR->iNumMercFaces >= gpAR->iActualMercFaces )
|
|
{
|
|
CreateTempPlayerMerc();
|
|
}
|
|
|
|
if( gpAR->uiTimeSlice == 0xffffffff )
|
|
{
|
|
gpAR->fSound = TRUE;
|
|
}
|
|
gpAR->uiTimeSlice = 1000;
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds = 0;
|
|
gpAR->uiCurrTime = 0;
|
|
gpAR->fPlayerRejectedSurrenderOffer = FALSE;
|
|
gpAR->fPendingSurrender = FALSE;
|
|
CalculateRowsAndColumns();
|
|
CalculateSoldierCells( TRUE);
|
|
CreateAutoResolveInterface();
|
|
DetermineTeamLeader( TRUE ); //friendly team
|
|
DetermineTeamLeader( FALSE ); //enemy team
|
|
CalculateAttackValues();
|
|
}
|
|
|
|
void CalculateRowsAndColumns()
|
|
{
|
|
//now that we have the number on each team, calculate the number of rows and columns to be used on
|
|
//the player's sides. NOTE: Militia won't appear on the same row as mercs.
|
|
if( !gpAR->ubMercs )
|
|
{ //0
|
|
gpAR->ubMercCols = gpAR->ubMercRows = 0;
|
|
}
|
|
else if( gpAR->ubMercs < 5 )
|
|
{ //1-4
|
|
gpAR->ubMercCols = 1;
|
|
gpAR->ubMercRows = gpAR->ubMercs;
|
|
}
|
|
else if( gpAR->ubMercs < 9 || gpAR->ubMercs == 10 )
|
|
{ //5-8, 10
|
|
gpAR->ubMercCols = 2;
|
|
gpAR->ubMercRows = (gpAR->ubMercs+1)/2;
|
|
}
|
|
else if( gpAR->ubMercs < 16 )
|
|
{ //9, 11-15
|
|
gpAR->ubMercCols = 3;
|
|
gpAR->ubMercRows = (gpAR->ubMercs+2)/3;
|
|
}
|
|
else
|
|
{ //16-MAX_STRATEGIC_TEAM_SIZE
|
|
gpAR->ubMercCols = 4;
|
|
gpAR->ubMercRows = (gpAR->ubMercs+3)/4;
|
|
}
|
|
|
|
if( !gpAR->ubCivs )
|
|
{
|
|
gpAR->ubCivCols = gpAR->ubCivRows = 0;
|
|
}
|
|
else if( gpAR->ubCivs < 5 )
|
|
{ //1-4
|
|
gpAR->ubCivCols = 1;
|
|
gpAR->ubCivRows = gpAR->ubCivs;
|
|
}
|
|
else if( gpAR->ubCivs < 9 || gpAR->ubCivs == 10 )
|
|
{ //5-8, 10
|
|
gpAR->ubCivCols = 2;
|
|
gpAR->ubCivRows = (gpAR->ubCivs+1)/2;
|
|
}
|
|
else if( gpAR->ubCivs < 16 )
|
|
{ //9, 11-15
|
|
gpAR->ubCivCols = 3;
|
|
gpAR->ubCivRows = (gpAR->ubCivs+2)/3;
|
|
}
|
|
else
|
|
{ //16-MAX_ALLOWABLE_MILITIA_PER_SECTOR
|
|
gpAR->ubCivCols = 4;
|
|
gpAR->ubCivRows = (gpAR->ubCivs+3)/4;
|
|
}
|
|
|
|
if( !gpAR->ubEnemies )
|
|
{
|
|
gpAR->ubEnemyCols = gpAR->ubEnemyRows = 0;
|
|
}
|
|
else if( gpAR->ubEnemies < 5 )
|
|
{ //1-4
|
|
gpAR->ubEnemyCols = 1;
|
|
gpAR->ubEnemyRows = gpAR->ubEnemies;
|
|
}
|
|
else if( gpAR->ubEnemies < 9 || gpAR->ubEnemies == 10 )
|
|
{ //5-8, 10
|
|
gpAR->ubEnemyCols = 2;
|
|
gpAR->ubEnemyRows = (gpAR->ubEnemies+1)/2;
|
|
}
|
|
else if( gpAR->ubEnemies < 16 )
|
|
{ //9, 11-15
|
|
gpAR->ubEnemyCols = 3;
|
|
gpAR->ubEnemyRows = (gpAR->ubEnemies+2)/3;
|
|
}
|
|
else
|
|
{ //16+
|
|
gpAR->ubEnemyCols = 4;
|
|
gpAR->ubEnemyRows = (gpAR->ubEnemies+3)/4;
|
|
}
|
|
|
|
|
|
//Now, that we have the number of mercs, militia, and enemies, it is possible that there
|
|
//may be some conflicts. Our goal is to make the window as small as possible. Bumping up
|
|
//the number of columns to 5 or rows to 10 will force one or both axes to go full screen. If we
|
|
//have high numbers of both, then we will have to.
|
|
|
|
//Step one: equalize the number of columns for both the mercs and civs.
|
|
if( gpAR->ubMercs && gpAR->ubCivs && gpAR->ubMercCols != gpAR->ubCivCols )
|
|
{
|
|
if( gpAR->ubMercCols < gpAR->ubCivCols )
|
|
{
|
|
gpAR->ubMercCols = gpAR->ubCivCols;
|
|
gpAR->ubMercRows = (gpAR->ubMercs+gpAR->ubMercCols-1)/gpAR->ubMercCols;
|
|
}
|
|
else
|
|
{
|
|
gpAR->ubCivCols = gpAR->ubMercCols;
|
|
gpAR->ubCivRows = (gpAR->ubCivs+gpAR->ubCivCols-1)/gpAR->ubCivCols;
|
|
}
|
|
}
|
|
//If we have both mercs and militia, we must make sure that the height to width ratio is never higher than
|
|
//a factor of two.
|
|
if( gpAR->ubMercs && gpAR->ubCivs && gpAR->ubMercRows + gpAR->ubCivRows > 4 )
|
|
{
|
|
if( gpAR->ubMercCols * 2 < gpAR->ubMercRows + gpAR->ubCivRows )
|
|
{
|
|
gpAR->ubMercCols++;
|
|
gpAR->ubMercRows = (gpAR->ubMercs+gpAR->ubMercCols-1)/gpAR->ubMercCols;
|
|
gpAR->ubCivCols++;
|
|
gpAR->ubCivRows = (gpAR->ubCivs+gpAR->ubCivCols-1)/gpAR->ubCivCols;
|
|
}
|
|
}
|
|
|
|
|
|
if( gpAR->ubMercRows + gpAR->ubCivRows > 9 )
|
|
{
|
|
if( gpAR->ubMercCols < 5 )
|
|
{ //bump it up
|
|
gpAR->ubMercCols++;
|
|
gpAR->ubMercRows = (gpAR->ubMercs+gpAR->ubMercCols-1)/gpAR->ubMercCols;
|
|
}
|
|
if( gpAR->ubCivCols < 5 )
|
|
{ //match it up with the mercs
|
|
gpAR->ubCivCols++; // = gpAR->ubMercCols;
|
|
gpAR->ubCivRows = (gpAR->ubCivs+gpAR->ubCivCols-1)/gpAR->ubCivCols;
|
|
}
|
|
}
|
|
|
|
if( gpAR->ubMercCols + gpAR->ubEnemyCols == 9 )
|
|
gpAR->sWidth = SCREEN_WIDTH;
|
|
else
|
|
gpAR->sWidth = 146 + 55 * (max( max( gpAR->ubMercCols, gpAR->ubCivCols ), 2 ) + max( gpAR->ubEnemyCols, 2 ));
|
|
|
|
//gpAR->sCenterStartX = 323 - gpAR->sWidth/2 + max( max( gpAR->ubMercCols, 2), max( gpAR->ubCivCols, 2 ) ) *55;
|
|
gpAR->sCenterStartX = iScreenWidthOffset + (323 - gpAR->sWidth/2 + max( max( gpAR->ubMercCols, 2), max( gpAR->ubCivCols, 2 ) ) *55);
|
|
|
|
|
|
//Anywhere from 48*3 to 48*10
|
|
//gpAR->sHeight = 48 * max( 3, max( gpAR->ubMercRows + gpAR->ubCivRows, gpAR->ubEnemyRows ) );
|
|
gpAR->sHeight = 48 * max( 3, max( min( 10, gpAR->ubMercRows + gpAR->ubCivRows ), min( 10, gpAR->ubEnemyRows ) ) );
|
|
//Make it an even multiple of 40 (rounding up).
|
|
gpAR->sHeight += 39;
|
|
gpAR->sHeight /= 40;
|
|
gpAR->sHeight *= 40;
|
|
|
|
//Here is a extremely bitchy case. The formulae throughout this module work for most cases.
|
|
//However, when combining mercs and civs, the column must be the same. However, we retract that
|
|
//in cases where there are less mercs than available to fill up *most* of the designated space.
|
|
if( gpAR->ubMercs && gpAR->ubCivs )
|
|
{
|
|
if( gpAR->ubMercRows * gpAR->ubMercCols > gpAR->ubMercs + gpAR->ubMercRows )
|
|
gpAR->ubMercCols--;
|
|
if( gpAR->ubCivRows * gpAR->ubCivCols > gpAR->ubCivs + gpAR->ubCivRows )
|
|
gpAR->ubCivCols--;
|
|
}
|
|
}
|
|
|
|
void HandleAutoResolveInput()
|
|
{
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve3");
|
|
|
|
InputAtom InputEvent;
|
|
BOOLEAN fResetAutoResolve = FALSE;
|
|
|
|
while( DequeueSpecificEvent(&InputEvent, KEY_DOWN|KEY_UP|KEY_REPEAT) )
|
|
{
|
|
if( InputEvent.usEvent == KEY_DOWN || InputEvent.usEvent == KEY_REPEAT )
|
|
{
|
|
switch( InputEvent.usParam )
|
|
{
|
|
case SPACE:
|
|
gpAR->fPaused ^= TRUE;
|
|
if( gpAR->fPaused )
|
|
{
|
|
ButtonList[ gpAR->iButton[ PAUSE_BUTTON ] ]->uiFlags |= BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ PLAY_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FAST_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ FINISH_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
}
|
|
else
|
|
{
|
|
ButtonList[ gpAR->iButton[ PAUSE_BUTTON ] ]->uiFlags &= ~BUTTON_CLICKED_ON;
|
|
ButtonList[ gpAR->iButton[ PLAY_BUTTON ] ]->uiFlags |= BUTTON_CLICKED_ON;
|
|
}
|
|
break;
|
|
case 'x':
|
|
if( _KeyDown( ALT ) )
|
|
{
|
|
HandleShortCutExitState( );
|
|
}
|
|
break;
|
|
#ifdef JA2BETAVERSION
|
|
case 'c':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->fAllowCapture ^= TRUE;
|
|
gpAR->fPlayerRejectedSurrenderOffer = FALSE;
|
|
gStrategicStatus.uiFlags &= ~STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE;
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case F5:
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->fDebugInfo ^= TRUE;
|
|
}
|
|
break;
|
|
case F6:
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->fSound ^= TRUE;
|
|
}
|
|
break;
|
|
case F7:
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->fInstantFinish ^= TRUE;
|
|
}
|
|
break;
|
|
// WDS - Debug "Drassen" battles
|
|
case F12:
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
UINT8 ubMapX, ubMapY;
|
|
for ( ubMapX = 1; ubMapX < MAP_WORLD_X - 1; ubMapX++ ) {
|
|
for ( ubMapY = 1; ubMapY < MAP_WORLD_Y - 1; ubMapY++ ) {
|
|
SetSectorFlag( ubMapX, ubMapY, 0, SF_ALREADY_VISITED ); //hayden
|
|
}
|
|
}
|
|
ExecuteStrategicAIAction( NPC_ACTION_SEND_SOLDIERS_TO_DRASSEN, 13, 4 );
|
|
}
|
|
break;
|
|
case BACKSPACE:
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case 'd':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( _KeyDown( ALT ) )
|
|
{
|
|
if ( gpAR->ubBattleStatus == BATTLE_IN_PROGRESS )
|
|
{
|
|
PlayAutoResolveSample( EXPLOSION_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
EliminateAllFriendlies();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'z':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( _KeyDown( ALT ) )
|
|
{
|
|
if ( gpAR->ubBattleStatus == BATTLE_IN_PROGRESS )
|
|
{
|
|
PlayAutoResolveSample( EXPLOSION_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
EliminateAllMercs();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'o':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( _KeyDown( ALT ) )
|
|
{
|
|
if ( gpAR->ubBattleStatus == BATTLE_IN_PROGRESS )
|
|
{
|
|
PlayAutoResolveSample( EXPLOSION_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
// this is not very accurate, any enemies already dead will be counted as killed twice
|
|
gStrategicStatus.usPlayerKills += NumNonPlayerTeamMembersInSector( gpAR->ubSectorX, gpAR->ubSectorY, ENEMY_TEAM );
|
|
EliminateAllEnemies( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case '{':
|
|
if (_KeyDown( ALT) )
|
|
{
|
|
}
|
|
else if (_KeyDown( CTRL) )
|
|
{
|
|
}
|
|
else if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubMercs = 0;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case '}':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubMercs = MAX_AR_TEAM_SIZE;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case '[':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( gpAR->ubMercs )
|
|
{
|
|
gpAR->ubMercs--;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case ']':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( gpAR->ubMercs < MAX_AR_TEAM_SIZE )
|
|
{
|
|
gpAR->ubMercs++;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case ':':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubCivs = 0;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case '"':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubCivs = MAX_AR_TEAM_SIZE;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case ';':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( gpAR->ubCivs )
|
|
{
|
|
gpAR->ubCivs--;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case 39: // ' quote
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( gpAR->ubCivs < MAX_AR_TEAM_SIZE )
|
|
{
|
|
gpAR->ubCivs++;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case '<':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubEnemies = 1;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case '>':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubEnemies = gGameExternalOptions.ubGameMaximumNumberOfEnemies;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case ',':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( gpAR->ubEnemies > 1 )
|
|
{
|
|
gpAR->ubEnemies--;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case '.':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
if( gpAR->ubEnemies < gGameExternalOptions.ubGameMaximumNumberOfEnemies )
|
|
{
|
|
gpAR->ubEnemies++;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case '/':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubMercs = 1;
|
|
gpAR->ubCivs = 0;
|
|
gpAR->ubEnemies = 1;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
case '?':
|
|
if( CHEATER_CHEAT_LEVEL() )
|
|
{
|
|
gpAR->ubMercs = OUR_TEAM_SIZE_NO_VEHICLE;
|
|
gpAR->ubCivs = MAX_AR_TEAM_SIZE;
|
|
gpAR->ubEnemies = gGameExternalOptions.ubGameMaximumNumberOfEnemies;
|
|
fResetAutoResolve = TRUE;
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if( fResetAutoResolve )
|
|
{
|
|
ResetAutoResolveInterface();
|
|
}
|
|
}
|
|
|
|
void RenderSoldierCellHealth( SOLDIERCELL *pCell )
|
|
{
|
|
INT32 cnt, cntStart;
|
|
INT32 xp, yp;
|
|
STR16 pStr;
|
|
CHAR16 str[20];
|
|
UINT8 *pDestBuf, *pSrcBuf;
|
|
UINT32 uiSrcPitchBYTES, uiDestPitchBYTES;
|
|
UINT16 usColor;
|
|
|
|
SetFont( SMALLCOMPFONT );
|
|
//Restore the background before drawing text.
|
|
pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
|
|
pSrcBuf = LockVideoSurface( gpAR->iInterfaceBuffer, &uiSrcPitchBYTES );
|
|
xp = pCell->xp + 2;
|
|
yp = pCell->yp + 32;
|
|
Blt16BPPTo16BPP( (UINT16*)pDestBuf, uiDestPitchBYTES,
|
|
(UINT16*)pSrcBuf, uiSrcPitchBYTES, xp, yp, xp-gpAR->Rect.iLeft, yp-gpAR->Rect.iTop, 46, 10 );
|
|
UnLockVideoSurface( gpAR->iInterfaceBuffer );
|
|
UnLockVideoSurface( FRAME_BUFFER );
|
|
|
|
if( pCell->pSoldier->stats.bLife )
|
|
{
|
|
if( pCell->pSoldier->stats.bLife == pCell->pSoldier->stats.bLifeMax )
|
|
{
|
|
cntStart = 4;
|
|
}
|
|
else
|
|
{
|
|
cntStart = 0;
|
|
}
|
|
for( cnt = cntStart; cnt < 6; cnt ++ )
|
|
{
|
|
if( pCell->pSoldier->stats.bLife < bHealthStrRanges[ cnt ] )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
switch( cnt )
|
|
{
|
|
case 0: //DYING
|
|
case 1: //CRITICAL
|
|
usColor = FONT_RED;
|
|
break;
|
|
case 2: //WOUNDED
|
|
case 3: //POOR
|
|
usColor = FONT_YELLOW;
|
|
break;
|
|
default: //REST
|
|
usColor = FONT_GRAY1;
|
|
break;
|
|
}
|
|
if( cnt > 3 && pCell->pSoldier->stats.bLife != pCell->pSoldier->stats.bLifeMax )
|
|
{ //Merc has taken damage, even though his life if good.
|
|
usColor = FONT_YELLOW;
|
|
}
|
|
if( pCell->pSoldier->stats.bLife == pCell->pSoldier->stats.bLifeMax )
|
|
usColor = FONT_GRAY1;
|
|
pStr = zHealthStr[ cnt ];
|
|
}
|
|
else
|
|
{
|
|
wcscpy( str, pCell->pSoldier->name );
|
|
pStr = _wcsupr( str );
|
|
usColor = FONT_BLACK;
|
|
}
|
|
|
|
//Draw the retreating text, if applicable
|
|
if( pCell->uiFlags & CELL_RETREATED && gpAR->ubBattleStatus != BATTLE_VICTORY )
|
|
{
|
|
usColor = FONT_LTGREEN;
|
|
swprintf( str, gpStrategicString[ STR_AR_MERC_RETREATED ] );
|
|
pStr = str;
|
|
}
|
|
else if( pCell->uiFlags & CELL_RETREATING && gpAR->ubBattleStatus == BATTLE_IN_PROGRESS )
|
|
{
|
|
if( pCell->pSoldier->stats.bLife >= OKLIFE )
|
|
{ //Retreating is shared with the status string. Alternate between the
|
|
//two every 450 milliseconds
|
|
if( GetJA2Clock() % 900 < 450 )
|
|
{ //override the health string with the retreating string.
|
|
usColor = FONT_LTRED;
|
|
swprintf( str, gpStrategicString[ STR_AR_MERC_RETREATING ] );
|
|
pStr = str;
|
|
}
|
|
}
|
|
}
|
|
else if( pCell->uiFlags & CELL_SHOWRETREATTEXT && gpAR->ubBattleStatus == BATTLE_IN_PROGRESS )
|
|
{
|
|
if( pCell->pSoldier->stats.bLife >= OKLIFE )
|
|
{
|
|
SetFontForeground( FONT_YELLOW );
|
|
swprintf( str, gpStrategicString[ STR_AR_MERC_RETREAT ] );
|
|
xp = pCell->xp + 25 - StringPixLength( pStr, SMALLCOMPFONT ) / 2;
|
|
yp = pCell->yp + 12;
|
|
mprintf( xp, yp, str );
|
|
}
|
|
}
|
|
SetFontForeground( (UINT8)usColor );
|
|
xp = pCell->xp + 25 - StringPixLength( pStr, SMALLCOMPFONT ) / 2;
|
|
yp = pCell->yp + 33;
|
|
mprintf( xp, yp, pStr );
|
|
}
|
|
|
|
UINT8 GetUnusedMercProfileID()
|
|
{
|
|
UINT8 ubRandom=0;
|
|
INT32 i;
|
|
BOOLEAN fUnique = FALSE;
|
|
while( !fUnique )
|
|
{
|
|
ubRandom = (UINT8)PreRandom( 40 );
|
|
for( i = 0; i < 19; i++ )
|
|
{
|
|
fUnique = TRUE;
|
|
if( Menptr[ i ].ubProfile == ubRandom )
|
|
{
|
|
fUnique = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ubRandom;
|
|
}
|
|
|
|
void CreateTempPlayerMerc()
|
|
{
|
|
SOLDIERCREATE_STRUCT MercCreateStruct;
|
|
static INT32 iSoldierCount=0;
|
|
UINT8 ubID;
|
|
|
|
//Init the merc create structure with basic information
|
|
MercCreateStruct.bTeam = SOLDIER_CREATE_AUTO_TEAM;
|
|
MercCreateStruct.ubProfile = GetUnusedMercProfileID();
|
|
MercCreateStruct.sSectorX = gpAR->ubSectorX;
|
|
MercCreateStruct.sSectorY = gpAR->ubSectorY;
|
|
MercCreateStruct.bSectorZ = 0;
|
|
MercCreateStruct.fPlayerMerc = TRUE;
|
|
MercCreateStruct.fCopyProfileItemsOver = TRUE;
|
|
|
|
//Create the player soldier
|
|
|
|
gpMercs[ gpAR->iNumMercFaces ].pSoldier = TacticalCreateSoldier( &MercCreateStruct, &ubID );
|
|
if( gpMercs[ gpAR->iNumMercFaces ].pSoldier )
|
|
{
|
|
gpAR->iNumMercFaces++;
|
|
}
|
|
}
|
|
|
|
void DetermineTeamLeader( BOOLEAN fFriendlyTeam )
|
|
{
|
|
INT32 i;
|
|
SOLDIERCELL *pBestLeaderCell = NULL;
|
|
//For each team (civs and players count as same team), find the merc with the best
|
|
//leadership ability.
|
|
if( fFriendlyTeam )
|
|
{
|
|
gpAR->ubPlayerLeadership = 0;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].pSoldier->stats.bLeadership > gpAR->ubPlayerLeadership )
|
|
{
|
|
gpAR->ubPlayerLeadership = gpMercs[ i ].pSoldier->stats.bLeadership;
|
|
pBestLeaderCell = &gpMercs[ i ];
|
|
}
|
|
}
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
{
|
|
if( gpCivs[ i ].pSoldier->stats.bLeadership > gpAR->ubPlayerLeadership )
|
|
{
|
|
gpAR->ubPlayerLeadership = gpCivs[ i ].pSoldier->stats.bLeadership;
|
|
pBestLeaderCell = &gpCivs[ i ];
|
|
}
|
|
}
|
|
|
|
if( pBestLeaderCell )
|
|
{
|
|
//Assign the best leader the honour of team leader.
|
|
pBestLeaderCell->uiFlags |= CELL_TEAMLEADER;
|
|
}
|
|
return;
|
|
}
|
|
//ENEMY TEAM
|
|
gpAR->ubEnemyLeadership = 0;
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( gpEnemies[ i ].pSoldier->stats.bLeadership > gpAR->ubEnemyLeadership )
|
|
{
|
|
gpAR->ubEnemyLeadership = gpEnemies[ i ].pSoldier->stats.bLeadership;
|
|
pBestLeaderCell = &gpEnemies[ i ];
|
|
}
|
|
}
|
|
if( pBestLeaderCell )
|
|
{
|
|
//Assign the best enemy leader the honour of team leader
|
|
pBestLeaderCell->uiFlags |= CELL_TEAMLEADER;
|
|
}
|
|
}
|
|
|
|
void ResetNextAttackCounter( SOLDIERCELL *pCell )
|
|
{
|
|
pCell->usNextAttack = min( 1000 - pCell->usAttack, 800 );
|
|
pCell->usNextAttack = (UINT16)(1000 + pCell->usNextAttack * 5 + PreRandom( 2000 - pCell->usAttack ) );
|
|
if( pCell->uiFlags & CELL_CREATURE )
|
|
{
|
|
pCell->usNextAttack = pCell->usNextAttack * 8 / 10;
|
|
}
|
|
}
|
|
|
|
FLOAT CalcClassBonusOrPenalty( SOLDIERTYPE *pSoldier )
|
|
{
|
|
switch( pSoldier->ubSoldierClass )
|
|
{
|
|
case SOLDIER_CLASS_ELITE:
|
|
case SOLDIER_CLASS_ELITE_MILITIA:
|
|
return 1.3f;
|
|
case SOLDIER_CLASS_ARMY:
|
|
case SOLDIER_CLASS_REG_MILITIA:
|
|
return 1.0f;
|
|
case SOLDIER_CLASS_ADMINISTRATOR:
|
|
case SOLDIER_CLASS_GREEN_MILITIA:
|
|
return 0.7f;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
void CalculateAttackValues()
|
|
{
|
|
INT32 i;
|
|
SOLDIERCELL *pCell;
|
|
SOLDIERTYPE *pSoldier;
|
|
UINT16 usBonus;
|
|
UINT16 usBestAttack = 0xffff;
|
|
UINT16 usBreathStrengthPercentage;
|
|
//INT16 sOutnumberBonus = 0;
|
|
//PLAYER TEAM
|
|
gpAR->usPlayerAttack = 0;
|
|
gpAR->usPlayerDefence = 0;
|
|
|
|
UINT8 uiEffectiveLevelExp = 0; // added by SANDRO
|
|
|
|
//if( gpAR->ubEnemies )
|
|
//{
|
|
// //bonus equals 20 if good guys outnumber bad guys 2 to 1.
|
|
// sMaxBonus = 20;
|
|
// sOutnumberBonus = (INT16)(gpAR->ubMercs + gpAR->ubCivs) * sMaxBonus / gpAR->ubEnemies - sMaxBonus;
|
|
// sOutnumberBonus = (INT16)min( sOutnumberBonus, max( sMaxBonus, 0 ) );
|
|
//}
|
|
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
pCell = &gpMercs[ i ];
|
|
pSoldier = pCell->pSoldier;
|
|
if( !pSoldier->stats.bLife )
|
|
continue;
|
|
pCell->usAttack = pSoldier->stats.bStrength +
|
|
pSoldier->stats.bDexterity +
|
|
pSoldier->stats.bWisdom +
|
|
pSoldier->stats.bMarksmanship +
|
|
pSoldier->aiData.bMorale;
|
|
//Give player controlled mercs a significant bonus to compensate for lack of control
|
|
//as the player would typically do much better in tactical.
|
|
if( pCell->usAttack < 1000 )
|
|
{ //A player with 500 attack will be augmented to 625
|
|
//A player with 600 attack will be augmented to 700
|
|
pCell->usAttack = (UINT16)(pCell->usAttack + (1000 - pCell->usAttack) / 4);
|
|
}
|
|
usBreathStrengthPercentage = 100 - ( 100 - pCell->pSoldier->bBreathMax ) / 3;
|
|
pCell->usAttack = pCell->usAttack * usBreathStrengthPercentage / 100;
|
|
pCell->usDefence = pSoldier->stats.bAgility +
|
|
pSoldier->stats.bWisdom +
|
|
pSoldier->bBreathMax +
|
|
pSoldier->stats.bMedical +
|
|
pSoldier->aiData.bMorale;
|
|
//100 team leadership adds a bonus of 10%,
|
|
usBonus = 100 + gpAR->ubPlayerLeadership/10;// + sOutnumberBonus;
|
|
|
|
//bExpLevel adds a bonus of 7% per level after 2, level 1 soldiers get a 7% decrease
|
|
//usBonus += 7 * (pSoldier->stats.bExpLevel-2);
|
|
// SANDRO - STOMP traits - Squadleaders bonus to effective level
|
|
uiEffectiveLevelExp = pSoldier->stats.bExpLevel;
|
|
if ( gGameOptions.fNewTraitSystem )
|
|
uiEffectiveLevelExp = min(10,(uiEffectiveLevelExp + (gSkillTraitValues.ubSLEffectiveLevelInRadius * GetSquadleadersCountInVicinity( pSoldier, TRUE, TRUE ))));
|
|
usBonus += EXP_BONUS * (uiEffectiveLevelExp-5);
|
|
|
|
usBonus += gpAR->ubPlayerDefenceAdvantage;
|
|
pCell->usAttack = pCell->usAttack * usBonus / 100;
|
|
pCell->usDefence = pCell->usDefence * usBonus / 100;
|
|
|
|
if( pCell->uiFlags & CELL_EPC )
|
|
{ //strengthen the defense (seeing the mercs will keep them safe).
|
|
pCell->usAttack = 0;
|
|
pCell->usDefence = 1000;
|
|
}
|
|
|
|
pCell->usAttack = min( pCell->usAttack, 1000 );
|
|
pCell->usDefence = min( pCell->usDefence, 1000 );
|
|
|
|
gpAR->usPlayerAttack += pCell->usAttack;
|
|
gpAR->usPlayerDefence += pCell->usDefence;
|
|
ResetNextAttackCounter( pCell );
|
|
if( i > 8 )
|
|
{ //Too many mercs, delay attack entry of extra mercs.
|
|
pCell->usNextAttack += (UINT16)( ( i - 8 ) * 2000 );
|
|
}
|
|
if( pCell->usNextAttack < usBestAttack )
|
|
usBestAttack = pCell->usNextAttack;
|
|
}
|
|
//CIVS
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
{
|
|
pCell = &gpCivs[ i ];
|
|
pSoldier = pCell->pSoldier;
|
|
pCell->usAttack = pSoldier->stats.bStrength +
|
|
pSoldier->stats.bDexterity +
|
|
pSoldier->stats.bWisdom +
|
|
pSoldier->stats.bMarksmanship +
|
|
pSoldier->aiData.bMorale;
|
|
pCell->usAttack = pCell->usAttack * pSoldier->bBreath / 100;
|
|
pCell->usDefence = pSoldier->stats.bAgility +
|
|
pSoldier->stats.bWisdom +
|
|
pSoldier->bBreathMax +
|
|
pSoldier->stats.bMedical +
|
|
pSoldier->aiData.bMorale;
|
|
//100 team leadership adds a bonus of 10%
|
|
usBonus = 100 + gpAR->ubPlayerLeadership/10;// + sOutnumberBonus;
|
|
//bExpLevel adds a bonus of 7% per level after 2, level 1 soldiers get a 7% decrease
|
|
//usBonus += 7 * (pSoldier->stats.bExpLevel-2);
|
|
// SANDRO - STOMP traits - Squadleaders bonus to effective level
|
|
uiEffectiveLevelExp = pSoldier->stats.bExpLevel;
|
|
if ( gGameOptions.fNewTraitSystem )
|
|
uiEffectiveLevelExp = min(10,(uiEffectiveLevelExp + (gSkillTraitValues.ubSLEffectiveLevelInRadius * GetSquadleadersCountInVicinity( pSoldier, TRUE, TRUE ))));
|
|
usBonus += EXP_BONUS * (uiEffectiveLevelExp-5);
|
|
|
|
usBonus += gpAR->ubPlayerDefenceAdvantage;
|
|
pCell->usAttack = pCell->usAttack * usBonus / 100;
|
|
pCell->usDefence = pCell->usDefence * usBonus / 100;
|
|
|
|
pCell->usAttack = (UINT16)( pCell->usAttack * CalcClassBonusOrPenalty( pCell->pSoldier ) );
|
|
pCell->usDefence =(UINT16)( pCell->usDefence * CalcClassBonusOrPenalty( pCell->pSoldier ) );
|
|
|
|
pCell->usAttack = min( pCell->usAttack, 1000 );
|
|
pCell->usDefence = min( pCell->usDefence, 1000 );
|
|
|
|
gpAR->usPlayerAttack += pCell->usAttack;
|
|
gpAR->usPlayerDefence += pCell->usDefence;
|
|
ResetNextAttackCounter( pCell );
|
|
if( i > 6 )
|
|
{ //Too many militia, delay attack entry of extra mercs.
|
|
pCell->usNextAttack += (UINT32)(( i - 4 ) * 2000 );
|
|
}
|
|
|
|
UINT8 militiainsector = NumNonPlayerTeamMembersInSector( gpAR->ubSectorX, gpAR->ubSectorY, MILITIA_TEAM );
|
|
if ( i >= militiainsector )
|
|
{
|
|
//Extra delay if soldier's from reinforcement
|
|
pCell->usNextAttack += REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR * militiainsector;
|
|
}
|
|
|
|
|
|
if( pCell->usNextAttack < usBestAttack )
|
|
usBestAttack = pCell->usNextAttack;
|
|
}
|
|
//ENEMIES
|
|
gpAR->usEnemyAttack = 0;
|
|
gpAR->usEnemyDefence = 0;
|
|
//if( gpAR->ubMercs + gpAR->ubCivs )
|
|
//{
|
|
// //bonus equals 20 if good guys outnumber bad guys 2 to 1.
|
|
// sMaxBonus = 20;
|
|
// sOutnumberBonus = (INT16)gpAR->ubEnemies * sMaxBonus / (gpAR->ubMercs + gpAR->ubCivs) - sMaxBonus;
|
|
// sOutnumberBonus = (INT16)min( sOutnumberBonus, max( sMaxBonus, 0 ) );
|
|
//}
|
|
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
pCell = &gpEnemies[ i ];
|
|
pSoldier = pCell->pSoldier;
|
|
pCell->usAttack = pSoldier->stats.bStrength +
|
|
pSoldier->stats.bDexterity +
|
|
pSoldier->stats.bWisdom +
|
|
pSoldier->stats.bMarksmanship +
|
|
pSoldier->aiData.bMorale;
|
|
pCell->usAttack = pCell->usAttack * pSoldier->bBreath / 100;
|
|
pCell->usDefence = pSoldier->stats.bAgility +
|
|
pSoldier->stats.bWisdom +
|
|
pSoldier->bBreathMax +
|
|
pSoldier->stats.bMedical +
|
|
pSoldier->aiData.bMorale;
|
|
//100 team leadership adds a bonus of 10%
|
|
usBonus = 100 + gpAR->ubPlayerLeadership/10;// + sOutnumberBonus;
|
|
//bExpLevel adds a bonus of 7% per level after 2, level 1 soldiers get a 7% decrease
|
|
//usBonus += 7 * (pSoldier->stats.bExpLevel-2);
|
|
// SANDRO - STOMP traits - Squadleaders bonus to effective level
|
|
uiEffectiveLevelExp = pSoldier->stats.bExpLevel;
|
|
if ( gGameOptions.fNewTraitSystem )
|
|
uiEffectiveLevelExp = min(10,(uiEffectiveLevelExp + (gSkillTraitValues.ubSLEffectiveLevelInRadius * GetSquadleadersCountInVicinity( pSoldier, TRUE, TRUE ))));
|
|
usBonus += EXP_BONUS * (uiEffectiveLevelExp-5);
|
|
|
|
usBonus += gpAR->ubEnemyDefenceAdvantage;
|
|
pCell->usAttack = pCell->usAttack * usBonus / 100;
|
|
pCell->usDefence = pCell->usDefence * usBonus / 100;
|
|
pCell->usAttack = (UINT16)( pCell->usAttack * CalcClassBonusOrPenalty( pCell->pSoldier ) );
|
|
pCell->usDefence = (UINT16)( pCell->usDefence * CalcClassBonusOrPenalty( pCell->pSoldier ) );
|
|
|
|
pCell->usAttack = min( pCell->usAttack, 1000 );
|
|
pCell->usDefence = min( pCell->usDefence, 1000 );
|
|
|
|
gpAR->usEnemyAttack += pCell->usAttack;
|
|
gpAR->usEnemyDefence += pCell->usDefence;
|
|
ResetNextAttackCounter( pCell );
|
|
|
|
if( i > 4 && !(pCell->uiFlags & CELL_CREATURE) )
|
|
{ //Too many enemies, delay attack entry of extra mercs.
|
|
pCell->usNextAttack += (UINT32)( ( i - 4 ) * 1000 );
|
|
}
|
|
|
|
if ( i >= NumNonPlayerTeamMembersInSector( gpAR->ubSectorX, gpAR->ubSectorY, ENEMY_TEAM ) && !(pCell->uiFlags & CELL_CREATURE) )
|
|
{ //Extra delay if it's a reinforcement
|
|
pCell->usNextAttack += REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR * NumNonPlayerTeamMembersInSector( gpAR->ubSectorX, gpAR->ubSectorY, ENEMY_TEAM );
|
|
}
|
|
|
|
|
|
if( pCell->usNextAttack < usBestAttack )
|
|
usBestAttack = pCell->usNextAttack;
|
|
}
|
|
//Now, because we are starting a new battle, we want to get the ball rolling a bit earlier. So,
|
|
//we will take the usBestAttack value and subtract 60% of it from everybody's next attack.
|
|
usBestAttack = usBestAttack * 60 / 100;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
gpMercs[ i ].usNextAttack -= usBestAttack;
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
gpCivs[ i ].usNextAttack -= usBestAttack;
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
gpEnemies[ i ].usNextAttack -= usBestAttack;
|
|
}
|
|
|
|
void DrawDebugText( SOLDIERCELL *pCell )
|
|
{
|
|
INT32 xp, yp;
|
|
if( !gpAR->fDebugInfo )
|
|
return;
|
|
SetFont( SMALLCOMPFONT );
|
|
SetFontForeground( FONT_WHITE );
|
|
xp = pCell->xp + 4;
|
|
yp = pCell->yp + 4;
|
|
if( pCell->uiFlags & CELL_TEAMLEADER )
|
|
{
|
|
//debug str
|
|
mprintf( xp, yp, L"LEADER" );
|
|
yp += 9;
|
|
}
|
|
mprintf( xp, yp, L"AT: %d", pCell->usAttack );
|
|
yp += 9;
|
|
mprintf( xp, yp, L"DF: %d", pCell->usDefence );
|
|
yp += 9;
|
|
|
|
xp = pCell->xp;
|
|
yp = pCell->yp - 4;
|
|
SetFont( LARGEFONT1 );
|
|
SetFontShadow( FONT_NEARBLACK );
|
|
if( pCell->uiFlags & CELL_FIREDATTARGET )
|
|
{
|
|
SetFontForeground( FONT_YELLOW );
|
|
mprintf( xp, yp, L"FIRE" );
|
|
pCell->uiFlags &= ~CELL_FIREDATTARGET;
|
|
yp += 13;
|
|
}
|
|
if( pCell->uiFlags & CELL_DODGEDATTACK )
|
|
{
|
|
SetFontForeground( FONT_BLUE );
|
|
mprintf( xp, yp, L"MISS" );
|
|
pCell->uiFlags &= ~CELL_DODGEDATTACK;
|
|
yp += 13;
|
|
}
|
|
if( pCell->uiFlags & CELL_HITBYATTACKER )
|
|
{
|
|
SetFontForeground( FONT_RED );
|
|
mprintf( xp, yp, L"HIT" );
|
|
pCell->uiFlags &= ~CELL_HITBYATTACKER;
|
|
yp += 13;
|
|
}
|
|
}
|
|
|
|
SOLDIERCELL* ChooseTarget( SOLDIERCELL *pAttacker )
|
|
{
|
|
INT32 iAvailableTargets;
|
|
INT32 index;
|
|
INT32 iRandom = -1;
|
|
SOLDIERCELL *pTarget = NULL;
|
|
UINT32 usSavedDefence;
|
|
//Determine what team we are attacking
|
|
if( pAttacker->uiFlags & (CELL_ENEMY | CELL_CREATURE) )
|
|
{ //enemy team attacking a player
|
|
iAvailableTargets = gpAR->ubMercs + gpAR->ubCivs;
|
|
index = 0;
|
|
usSavedDefence = gpAR->usPlayerDefence;
|
|
while( iAvailableTargets )
|
|
{
|
|
pTarget = ( index < gpAR->ubMercs ) ? &gpMercs[ index ] : &gpCivs[ index - gpAR->ubMercs ];
|
|
if( !pTarget->pSoldier->stats.bLife || pTarget->uiFlags & CELL_RETREATED )
|
|
{
|
|
index++;
|
|
iAvailableTargets--;
|
|
continue;
|
|
}
|
|
iRandom = PreRandom( gpAR->usPlayerDefence );
|
|
gpAR->usPlayerDefence -= pTarget->usDefence;
|
|
if( iRandom < pTarget->usDefence )
|
|
{
|
|
gpAR->usPlayerDefence = usSavedDefence;
|
|
return pTarget;
|
|
}
|
|
index++;
|
|
iAvailableTargets--;
|
|
}
|
|
if( !IsBattleOver() )
|
|
{
|
|
AssertMsg( 0, String("***Please send PRIOR save and screenshot of this message*** iAvailableTargets %d, index %d, iRandom %d, defence %d. ",
|
|
iAvailableTargets, index, iRandom, gpAR->usPlayerDefence) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//player team attacking an enemy
|
|
iAvailableTargets = gpAR->ubEnemies;
|
|
index = 0;
|
|
usSavedDefence = gpAR->usEnemyDefence;
|
|
while( iAvailableTargets )
|
|
{
|
|
pTarget = &gpEnemies[ index ];
|
|
if( !pTarget->pSoldier->stats.bLife )
|
|
{
|
|
index++;
|
|
iAvailableTargets--;
|
|
continue;
|
|
}
|
|
iRandom = PreRandom( gpAR->usEnemyDefence );
|
|
gpAR->usEnemyDefence -= pTarget->usDefence;
|
|
if( iRandom < pTarget->usDefence )
|
|
{
|
|
gpAR->usEnemyDefence = usSavedDefence;
|
|
return pTarget;
|
|
}
|
|
index++;
|
|
iAvailableTargets--;
|
|
}
|
|
}
|
|
AssertMsg( 0, "Error in ChooseTarget logic for choosing enemy target." );
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN FireAShot( SOLDIERCELL *pAttacker )
|
|
{
|
|
OBJECTTYPE *pItem;
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
pSoldier = pAttacker->pSoldier;
|
|
|
|
if( pAttacker->uiFlags & CELL_MALECREATURE )
|
|
{
|
|
PlayAutoResolveSample( ACR_SPIT, RATE_11025, 50, 1, MIDDLEPAN );
|
|
pAttacker->bWeaponSlot = SECONDHANDPOS;
|
|
return TRUE;
|
|
}
|
|
|
|
UINT8 invsize = pSoldier->inv.size();
|
|
for( UINT8 i = 0; i < invsize; ++i )
|
|
{
|
|
pItem = &pSoldier->inv[ i ];
|
|
|
|
if( Item[ pItem->usItem ].usItemClass == IC_GUN )
|
|
{
|
|
pAttacker->bWeaponSlot = (INT8)i;
|
|
if( gpAR->fUnlimitedAmmo )
|
|
{
|
|
PlayAutoResolveSample( Weapon[ pItem->usItem ].sSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
return TRUE;
|
|
}
|
|
if( !(*pItem)[0]->data.gun.ubGunShotsLeft )
|
|
{
|
|
AutoReload( pSoldier );
|
|
if ( (*pItem)[0]->data.gun.ubGunShotsLeft && Weapon[ pItem->usItem ].sLocknLoadSound )
|
|
{
|
|
PlayAutoResolveSample( Weapon[ pItem->usItem ].sLocknLoadSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
if ( (*pItem)[0]->data.gun.ubGunShotsLeft )
|
|
{
|
|
PlayAutoResolveSample( Weapon[ pItem->usItem ].sSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
if( pAttacker->uiFlags & CELL_MERC )
|
|
{
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usShotsFired++;
|
|
// MARKSMANSHIP GAIN: Attacker fires a shot
|
|
|
|
StatChange( pAttacker->pSoldier, MARKAMT, 3, FALSE );
|
|
}
|
|
(*pItem)[0]->data.gun.ubGunShotsLeft--;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
pAttacker->bWeaponSlot = -1;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN FireTankCannon( SOLDIERCELL *pAttacker )
|
|
{
|
|
OBJECTTYPE *pItem;
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
pSoldier = pAttacker->pSoldier;
|
|
|
|
// we need to be a tank to do this...
|
|
if ( !pSoldier || (pAttacker->uiFlags & CELL_TANK) == 0 || pSoldier->ubSoldierClass != SOLDIER_CLASS_TANK )
|
|
return FALSE;
|
|
|
|
// a tank has several guns, don't fire the main gun all the time
|
|
//if ( Chance(33) )
|
|
//return FALSE;
|
|
|
|
UINT8 invsize = pSoldier->inv.size();
|
|
for ( UINT8 i = 0; i < invsize; ++i )
|
|
{
|
|
pItem = &pSoldier->inv[i];
|
|
|
|
if ( Item[pItem->usItem].cannon )
|
|
{
|
|
PlayAutoResolveSample( Weapon[pItem->usItem].sSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
|
|
if ( pAttacker->uiFlags & CELL_MERC )
|
|
{
|
|
gMercProfiles[pAttacker->pSoldier->ubProfile].records.usShotsFired++;
|
|
// MARKSMANSHIP GAIN: Attacker fires a shot
|
|
|
|
StatChange( pAttacker->pSoldier, MARKAMT, 3, FALSE );
|
|
}
|
|
|
|
pAttacker->bWeaponSlot = (INT8)i;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
pAttacker->bWeaponSlot = -1;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN FireAntiTankWeapon( SOLDIERCELL *pAttacker )
|
|
{
|
|
OBJECTTYPE *pItem;
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
pSoldier = pAttacker->pSoldier;
|
|
|
|
UINT8 invsize = pSoldier->inv.size( );
|
|
for ( UINT8 i = 0; i < invsize; ++i )
|
|
{
|
|
pItem = &pSoldier->inv[i];
|
|
|
|
if ( Item[pItem->usItem].usItemClass == IC_LAUNCHER || Item[pItem->usItem].cannon )
|
|
{
|
|
pAttacker->bWeaponSlot = (INT8)i;
|
|
if ( gpAR->fUnlimitedAmmo )
|
|
{
|
|
PlayAutoResolveSample( Weapon[pItem->usItem].sSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
return TRUE;
|
|
}
|
|
|
|
if ( FindLaunchable( pSoldier, pSoldier->inv[pAttacker->bWeaponSlot].usItem ) == NO_SLOT )
|
|
continue;
|
|
|
|
DeductAmmo( pSoldier, pAttacker->bWeaponSlot );
|
|
|
|
if ( !(*pItem)[0]->data.gun.ubGunShotsLeft )
|
|
{
|
|
INT8 ammoslot = FindAmmoToReload( pSoldier, pAttacker->bWeaponSlot, NO_SLOT );
|
|
|
|
BOOLEAN fRet = ReloadGun( pSoldier, pItem, &(pSoldier->inv[ammoslot]) );
|
|
|
|
if ( fRet && ( *pItem )[0]->data.gun.ubGunShotsLeft && Weapon[pItem->usItem].sLocknLoadSound )
|
|
{
|
|
PlayAutoResolveSample( Weapon[pItem->usItem].sLocknLoadSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
|
|
PlayAutoResolveSample( Weapon[pItem->usItem].sSound, RATE_11025, 50, 1, MIDDLEPAN );
|
|
if ( pAttacker->uiFlags & CELL_MERC )
|
|
{
|
|
gMercProfiles[pAttacker->pSoldier->ubProfile].records.usShotsFired++;
|
|
// MARKSMANSHIP GAIN: Attacker fires a shot
|
|
|
|
StatChange( pAttacker->pSoldier, MARKAMT, 3, FALSE );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
pAttacker->bWeaponSlot = -1;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN AttackerHasKnife( SOLDIERCELL *pAttacker )
|
|
{
|
|
UINT8 invsize = pAttacker->pSoldier->inv.size();
|
|
for( UINT8 i = 0; i < invsize; ++i )
|
|
{
|
|
if( Item[ pAttacker->pSoldier->inv[ i ].usItem ].usItemClass == IC_BLADE )
|
|
{
|
|
pAttacker->bWeaponSlot = (INT8)i;
|
|
return TRUE;
|
|
}
|
|
}
|
|
pAttacker->bWeaponSlot = -1;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN TargetHasLoadedGun( SOLDIERTYPE *pSoldier )
|
|
{
|
|
OBJECTTYPE *pItem;
|
|
UINT8 invsize = pSoldier->inv.size();
|
|
for( UINT8 i = 0; i < invsize; ++i )
|
|
{
|
|
pItem = &pSoldier->inv[ i ];
|
|
if( Item[ pItem->usItem ].usItemClass == IC_GUN )
|
|
{
|
|
if( gpAR->fUnlimitedAmmo )
|
|
{
|
|
return TRUE;
|
|
}
|
|
if( (*pItem)[0]->data.gun.ubGunShotsLeft )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget )
|
|
{
|
|
UINT16 usAttack;
|
|
UINT16 usDefence;
|
|
UINT8 ubImpact;
|
|
UINT8 ubLocation;
|
|
UINT8 ubAccuracy;
|
|
INT32 iRandom;
|
|
INT32 iImpact;
|
|
INT32 iNewLife;
|
|
BOOLEAN fMelee = FALSE;
|
|
BOOLEAN fKnife = FALSE;
|
|
BOOLEAN fClaw = FALSE;
|
|
BOOLEAN fCannon = FALSE;
|
|
BOOLEAN fAntiTank = FALSE;
|
|
INT8 bAttackIndex = -1;
|
|
|
|
pAttacker->uiFlags |= CELL_FIREDATTARGET | CELL_DIRTY;
|
|
if( pAttacker->usAttack < 950 )
|
|
usAttack = (UINT16)(pAttacker->usAttack + PreRandom((INT16)(gGameExternalOptions.iAutoResolveLuckFactor*1000.0) - pAttacker->usAttack ));
|
|
else
|
|
usAttack = (UINT16)(950 + PreRandom( 50 ));
|
|
if( pTarget->uiFlags & CELL_RETREATING && !(pAttacker->uiFlags & CELL_FEMALECREATURE) )
|
|
{ //Attacking a retreating merc is harder. Modify the attack value to 70% of it's value.
|
|
//This allows retreaters to have a better chance of escaping.
|
|
usAttack = usAttack * 7 / 10;
|
|
}
|
|
if( pTarget->usDefence < 950 )
|
|
usDefence = (UINT16)(pTarget->usDefence + PreRandom((INT16)(gGameExternalOptions.iAutoResolveLuckFactor*1000.0) - pTarget->usDefence ));
|
|
else
|
|
usDefence = (UINT16)(950 + PreRandom( 50 ));
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - Iincrease Militia Strength in autoresolve battles
|
|
// because the attack and defense is limited to max 1000, rather than only increasing the attack of milita,
|
|
// decrease the defense of target - so +100% bonus means +50% attack of attacker and -50% defense of target
|
|
|
|
//if militita is attacking increase attack by half and decrease the defense of enemy
|
|
if (pAttacker->pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA && gGameExternalOptions.sGreenMilitiaAutoresolveStrength != 0)
|
|
{
|
|
usAttack += (usAttack * gGameExternalOptions.sGreenMilitiaAutoresolveStrength /200);
|
|
usDefence -= (usDefence * gGameExternalOptions.sGreenMilitiaAutoresolveStrength /200);
|
|
}
|
|
else if (pAttacker->pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA && gGameExternalOptions.sRegularMilitiaAutoresolveStrength != 0)
|
|
{
|
|
usAttack += (usAttack * gGameExternalOptions.sRegularMilitiaAutoresolveStrength /200);
|
|
usDefence -= (usDefence * gGameExternalOptions.sRegularMilitiaAutoresolveStrength /200);
|
|
}
|
|
else if (pAttacker->pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaAutoresolveStrength != 0)
|
|
{
|
|
usAttack += (usAttack * gGameExternalOptions.sVeteranMilitiaAutoresolveStrength /200);
|
|
usDefence -= (usDefence * gGameExternalOptions.sVeteranMilitiaAutoresolveStrength /200);
|
|
}
|
|
else if (pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0)
|
|
{
|
|
usAttack += (usAttack * gGameExternalOptions.sMercsAutoresolveOffenseBonus /200);
|
|
usDefence -= (usDefence * gGameExternalOptions.sMercsAutoresolveOffenseBonus /200);
|
|
}
|
|
|
|
//if enemy is attacking decrease his attack by half and increase the defense of militia by half
|
|
if (pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA && gGameExternalOptions.sGreenMilitiaAutoresolveStrength != 0)
|
|
{
|
|
usDefence += (usDefence * gGameExternalOptions.sGreenMilitiaAutoresolveStrength /200);
|
|
usAttack -= (usAttack * gGameExternalOptions.sGreenMilitiaAutoresolveStrength /200);
|
|
}
|
|
else if (pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA && gGameExternalOptions.sRegularMilitiaAutoresolveStrength != 0)
|
|
{
|
|
usDefence += (usDefence * gGameExternalOptions.sRegularMilitiaAutoresolveStrength /200);
|
|
usAttack -= (usAttack * gGameExternalOptions.sRegularMilitiaAutoresolveStrength /200);
|
|
}
|
|
else if (pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaAutoresolveStrength != 0)
|
|
{
|
|
usDefence += (usDefence * gGameExternalOptions.sVeteranMilitiaAutoresolveStrength /200);
|
|
usAttack -= (usAttack * gGameExternalOptions.sVeteranMilitiaAutoresolveStrength /200);
|
|
}
|
|
else if (pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0)
|
|
{
|
|
usDefence += (usDefence * gGameExternalOptions.sMercsAutoresolveDeffenseBonus /200);
|
|
usAttack -= (usAttack * gGameExternalOptions.sMercsAutoresolveDeffenseBonus /200);
|
|
}
|
|
|
|
// reapair values
|
|
usAttack = max(0, min(1000, usAttack ));
|
|
usDefence = max(0, min(1000, usDefence ));
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if( pAttacker->uiFlags & CELL_FEMALECREATURE )
|
|
{
|
|
pAttacker->bWeaponSlot = HANDPOS;
|
|
fMelee = TRUE;
|
|
fClaw = TRUE;
|
|
}
|
|
// Flugente: if we are a tank, we might fire the main cannon...
|
|
else if ( FireTankCannon( pAttacker ) )
|
|
{
|
|
fCannon = TRUE;
|
|
|
|
// cannons have a huge splash zone, so they are much more likely to hit
|
|
usAttack *= 1.5;
|
|
}
|
|
// if our target is a tank, we use heavy weapons if we have any
|
|
else if ( TANK( pTarget->pSoldier ) && FireAntiTankWeapon( pAttacker ) )
|
|
{
|
|
fAntiTank = TRUE;
|
|
|
|
// hitting a tank with an rpg isn't easy
|
|
usAttack *= 0.8;
|
|
}
|
|
else if( !FireAShot( pAttacker ) )
|
|
{
|
|
//Maybe look for a weapon, such as a knife or grenade?
|
|
fMelee = TRUE;
|
|
fKnife = AttackerHasKnife( pAttacker );
|
|
if( TargetHasLoadedGun( pTarget->pSoldier ) )
|
|
{
|
|
//Penalty to attack with melee weapons against target with loaded gun.
|
|
if( !(pAttacker->uiFlags & CELL_CREATURE ) )
|
|
{
|
|
//except for creatures
|
|
if( fKnife )
|
|
usAttack = usAttack * 6 / 10;
|
|
else
|
|
usAttack = usAttack * 4 / 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Set up a random delay for the hit or miss.
|
|
if( !fMelee )
|
|
{
|
|
if( !pTarget->usNextHit[0] )
|
|
{
|
|
bAttackIndex = 0;
|
|
}
|
|
else if( !pTarget->usNextHit[1] )
|
|
{
|
|
bAttackIndex = 1;
|
|
}
|
|
else if( !pTarget->usNextHit[2] )
|
|
{
|
|
bAttackIndex = 2;
|
|
}
|
|
|
|
if ( bAttackIndex != -1 )
|
|
{
|
|
if ( fAntiTank )
|
|
pTarget->usNextHit[ bAttackIndex ] = (UINT16)( 200 + PreRandom( 400 ) );
|
|
else
|
|
pTarget->usNextHit[bAttackIndex] = (UINT16)(50 + PreRandom( 400 ));
|
|
|
|
pTarget->pAttacker[ bAttackIndex ] = pAttacker;
|
|
}
|
|
}
|
|
|
|
if( usAttack < usDefence )
|
|
{
|
|
if( pTarget->pSoldier->stats.bLife >= OKLIFE || !PreRandom( 5 ) )
|
|
{
|
|
//Attacker misses -- use up a round of ammo. If target is unconcious, then 80% chance of hitting.
|
|
pTarget->uiFlags |= CELL_DODGEDATTACK | CELL_DIRTY;
|
|
|
|
if( fMelee )
|
|
{
|
|
if( fKnife )
|
|
PlayAutoResolveSample( MISS_KNIFE, RATE_11025, 50, 1, MIDDLEPAN );
|
|
else if( fClaw )
|
|
{
|
|
if( Chance( 50 ) )
|
|
{
|
|
PlayAutoResolveSample( ACR_SWIPE, RATE_11025, 50, 1, MIDDLEPAN );
|
|
}
|
|
else
|
|
{
|
|
PlayAutoResolveSample( ACR_LUNGE, RATE_11025, 50, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
else
|
|
PlayAutoResolveSample( SWOOSH_1 + PreRandom( 6 ), RATE_11025, 50, 1, MIDDLEPAN );
|
|
|
|
if( pTarget->uiFlags & CELL_MERC )
|
|
// AGILITY GAIN: Target "dodged" an attack
|
|
StatChange( pTarget->pSoldier, AGILAMT, 5, FALSE );
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// hit with a tank
|
|
if ( fCannon )
|
|
{
|
|
ubImpact = (UINT8)GetDamage( &pAttacker->pSoldier->inv[pAttacker->bWeaponSlot] );
|
|
|
|
ubLocation = AIM_SHOT_TORSO;
|
|
|
|
ubAccuracy = (UINT8)((usAttack - usDefence + PreRandom( usDefence - pTarget->usDefence )) / 10);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - increased mercs' offense/deffense rating
|
|
if ( pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0 )
|
|
{
|
|
ubImpact += (iImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus / 150);
|
|
}
|
|
else if ( pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (iImpact > 3) )
|
|
{
|
|
ubImpact = max( 3, ((iImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) / 100) );
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if ( bAttackIndex == -1 )
|
|
{
|
|
// tack damage on to end of last hit
|
|
pTarget->usHitDamage[2] += (UINT16)ubImpact;
|
|
}
|
|
else
|
|
{
|
|
pTarget->usHitDamage[bAttackIndex] = (UINT16)ubImpact;
|
|
}
|
|
|
|
// tanks might do splash damage to other troops as well...
|
|
UINT8 numtries = min(4, Random( gpAR->ubMercs + gpAR->ubCivs ) );
|
|
for ( int tries = 0; tries < numtries; ++tries )
|
|
{
|
|
SOLDIERCELL* pAnotherTarget = ChooseTarget( pAttacker );
|
|
|
|
if ( pAnotherTarget != pTarget )
|
|
{
|
|
INT8 bNewAttackIndex = -1;
|
|
|
|
if ( !pTarget->usNextHit[0] )
|
|
{
|
|
bNewAttackIndex = 0;
|
|
}
|
|
else if ( !pTarget->usNextHit[1] )
|
|
{
|
|
bNewAttackIndex = 1;
|
|
}
|
|
else if ( !pTarget->usNextHit[2] )
|
|
{
|
|
bNewAttackIndex = 2;
|
|
}
|
|
|
|
if ( bAttackIndex != -1 )
|
|
{
|
|
pAnotherTarget->usNextHit[bNewAttackIndex] = pTarget->usNextHit[bAttackIndex];
|
|
pAnotherTarget->pAttacker[bNewAttackIndex] = pAttacker;
|
|
|
|
pAnotherTarget->usHitDamage[bNewAttackIndex] = ubImpact / (numtries + 1) + Random( 20 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// attack a tank with heavy weapons
|
|
else if ( fAntiTank )
|
|
{
|
|
ubImpact = (UINT8)GetDamage( &pAttacker->pSoldier->inv[pAttacker->bWeaponSlot] ) / 3;
|
|
|
|
UINT8 ubAmmoType = Magazine[Item[(&pAttacker->pSoldier->inv[pAttacker->bWeaponSlot])->usItem].ubClassIndex].ubAmmoType;
|
|
|
|
if ( AmmoTypes[ubAmmoType].antiTank )
|
|
{
|
|
ubImpact *= 2;
|
|
}
|
|
|
|
iRandom = Random( 100 );
|
|
if ( iRandom < 15 )
|
|
ubLocation = AIM_SHOT_HEAD;
|
|
else if ( iRandom < 50 )
|
|
ubLocation = AIM_SHOT_LEGS;
|
|
else
|
|
ubLocation = AIM_SHOT_TORSO;
|
|
|
|
ubAccuracy = (UINT8)((usAttack - usDefence + PreRandom( usDefence - pTarget->usDefence )) / 10);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - increased mercs' offense/deffense rating
|
|
if ( pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0 )
|
|
{
|
|
iImpact += (iImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus / 150);
|
|
}
|
|
else if ( pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (iImpact > 3) )
|
|
{
|
|
iImpact = max( 3, ((iImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) / 100) );
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if ( bAttackIndex == -1 )
|
|
{
|
|
// tack damage on to end of last hit
|
|
pTarget->usHitDamage[2] += (UINT16)ubImpact;
|
|
}
|
|
else
|
|
{
|
|
pTarget->usHitDamage[bAttackIndex] = (UINT16)ubImpact;
|
|
}
|
|
}
|
|
//Attacker hits
|
|
else if( !fMelee )
|
|
{
|
|
// silversurfer: We want to use the function instead of the raw value because it applies the modifiers that we configured in the ini.
|
|
ubImpact = (UINT8) GetDamage(&pAttacker->pSoldier->inv[ pAttacker->bWeaponSlot ]);
|
|
//ubImpact = Weapon[ pAttacker->pSoldier->inv[ pAttacker->bWeaponSlot ].usItem ].ubImpact;
|
|
iRandom = PreRandom( 100 );
|
|
if( iRandom < 15 )
|
|
ubLocation = AIM_SHOT_HEAD;
|
|
else if( iRandom < 30 )
|
|
ubLocation = AIM_SHOT_LEGS;
|
|
else
|
|
ubLocation = AIM_SHOT_TORSO;
|
|
|
|
ubAccuracy = (UINT8)((usAttack - usDefence + PreRandom( usDefence - pTarget->usDefence ) )/10);
|
|
// HEADROCK HAM 5: Added argument
|
|
iImpact = BulletImpact( pAttacker->pSoldier, NULL, pTarget->pSoldier, ubLocation, ubImpact, ubAccuracy, NULL );
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - increased mercs' offense/deffense rating
|
|
if (pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0)
|
|
{
|
|
iImpact += (iImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus /150);
|
|
}
|
|
else if (pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (iImpact > 3))
|
|
{
|
|
iImpact = max( 3, ((iImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) /100));
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if ( bAttackIndex == -1 )
|
|
{
|
|
// tack damage on to end of last hit
|
|
pTarget->usHitDamage[2] += (UINT16) iImpact;
|
|
}
|
|
else
|
|
{
|
|
pTarget->usHitDamage[ bAttackIndex ] = (UINT16) iImpact;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OBJECTTYPE *pItem;
|
|
OBJECTTYPE tempItem;
|
|
|
|
PlayAutoResolveSample( (UINT8)(BULLET_IMPACT_1+PreRandom(3)), RATE_11025, 50, 1, MIDDLEPAN );
|
|
if( !pTarget->pSoldier->stats.bLife )
|
|
{ //Soldier already dead (can't kill him again!)
|
|
return;
|
|
}
|
|
|
|
ubAccuracy = (UINT8)((usAttack - usDefence + PreRandom( usDefence - pTarget->usDefence ) )/10);
|
|
|
|
//Determine attacking weapon.
|
|
pAttacker->pSoldier->usAttackingWeapon = 0;
|
|
if( pAttacker->bWeaponSlot != -1 )
|
|
{
|
|
pItem = &pAttacker->pSoldier->inv[ pAttacker->bWeaponSlot ];
|
|
if( Item[ pItem->usItem ].usItemClass & IC_WEAPON )
|
|
pAttacker->pSoldier->usAttackingWeapon = pAttacker->pSoldier->inv[ pAttacker->bWeaponSlot ].usItem;
|
|
}
|
|
|
|
// WDS - make number of mercenaries, etc. be configurable
|
|
// WANNE: Does this lead to a CTD -> no I did not get any CTD, so I reenabled it
|
|
if( pAttacker->bWeaponSlot != HANDPOS && pAttacker->bWeaponSlot != -1)
|
|
{
|
|
//switch items
|
|
tempItem = pAttacker->pSoldier->inv[ HANDPOS ];
|
|
pAttacker->pSoldier->inv[ HANDPOS ] = pAttacker->pSoldier->inv[ pAttacker->bWeaponSlot ]; //CTD
|
|
iImpact = HTHImpact( pAttacker->pSoldier, pTarget->pSoldier, ubAccuracy, (BOOLEAN)(fKnife || fClaw) );
|
|
pAttacker->pSoldier->inv[ pAttacker->bWeaponSlot ] = pAttacker->pSoldier->inv[ HANDPOS ];
|
|
pAttacker->pSoldier->inv[ HANDPOS ] = tempItem;
|
|
}
|
|
else
|
|
{
|
|
iImpact = HTHImpact( pAttacker->pSoldier, pTarget->pSoldier, ubAccuracy, (BOOLEAN)(fKnife || fClaw) );
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - increased mercs' offense/deffense rating
|
|
if (pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0)
|
|
{
|
|
iImpact += (iImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus /150);
|
|
}
|
|
else if (pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (iImpact > 3))
|
|
{
|
|
iImpact = max( 3, ((iImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) /100));
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// WANNE: Why is impact here always set to 0? The impact was calculated a few lines before!
|
|
//iImpact = 0;
|
|
|
|
// WANNE: Just for safty.
|
|
if (iImpact < 0)
|
|
iImpact = 0;
|
|
|
|
iNewLife = pTarget->pSoldier->stats.bLife - iImpact;
|
|
|
|
if( pAttacker->uiFlags & CELL_MERC )
|
|
{ //Attacker is a player, so increment the number of shots that hit.
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usShotsHit++;
|
|
// MARKSMANSHIP GAIN: Attacker's shot hits
|
|
StatChange( pAttacker->pSoldier, MARKAMT, 6, FALSE ); // in addition to 3 for taking a shot
|
|
}
|
|
if( pTarget->uiFlags & CELL_MERC )
|
|
{ //Target is a player, so increment the times he has been wounded.
|
|
if( iImpact > 1 )
|
|
gMercProfiles[ pTarget->pSoldier->ubProfile ].records.usTimesWoundedStabbed++;
|
|
// EXPERIENCE GAIN: Took some damage
|
|
StatChange( pTarget->pSoldier, EXPERAMT, ( UINT16 )( 5 * ( iImpact / 10 ) ), FALSE );
|
|
}
|
|
if( pTarget->pSoldier->stats.bLife >= CONSCIOUSNESS || pTarget->uiFlags & CELL_CREATURE )
|
|
{
|
|
if( gpAR->fSound )
|
|
pTarget->pSoldier->DoMercBattleSound( (INT8)( BATTLE_SOUND_HIT1 + PreRandom( 2 ) ) );
|
|
}
|
|
if( !(pTarget->uiFlags & CELL_CREATURE) && iNewLife < OKLIFE && pTarget->pSoldier->stats.bLife >= OKLIFE )
|
|
{ //the hit caused the merc to fall. Play the falling sound
|
|
PlayAutoResolveSample( (UINT8)FALL_1, RATE_11025, 50, 1, MIDDLEPAN );
|
|
pTarget->uiFlags &= ~CELL_RETREATING;
|
|
}
|
|
if( iNewLife <= 0 )
|
|
{ //soldier has been killed
|
|
|
|
// Flugente: campaign stats
|
|
gCurrentIncident.AddStat( pTarget->pSoldier, CAMPAIGNHISTORY_TYPE_KILL );
|
|
|
|
// Flugente: disease
|
|
HandleDeathDiseaseImplications( pTarget->pSoldier );
|
|
|
|
if( pAttacker->uiFlags & CELL_MERC )
|
|
{ //Player killed the enemy soldier -- update his stats as well as any assisters.
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - experimental - more specific statistics of mercs
|
|
switch(pTarget->pSoldier->ubSoldierClass)
|
|
{
|
|
case SOLDIER_CLASS_ELITE :
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsElites++;
|
|
break;
|
|
case SOLDIER_CLASS_ARMY :
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsRegulars++;
|
|
break;
|
|
case SOLDIER_CLASS_ADMINISTRATOR :
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsAdmins++;
|
|
break;
|
|
case SOLDIER_CLASS_CREATURE :
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsCreatures++;
|
|
break;
|
|
#ifdef ENABLE_ZOMBIES
|
|
case SOLDIER_CLASS_ZOMBIE :
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsZombies++;
|
|
break;
|
|
#endif
|
|
default :
|
|
if ( CREATURE_OR_BLOODCAT( pTarget->pSoldier ) )
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsCreatures++;
|
|
else if ( TANK( pTarget->pSoldier ) ) // we hardly can kill a tank in autoresolve, but well.. who knows..
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsTanks++;
|
|
else if ( pTarget->pSoldier->bTeam == CIV_TEAM && !pTarget->pSoldier->aiData.bNeutral && pTarget->pSoldier->bSide != gbPlayerNum )
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsHostiles++;
|
|
else
|
|
{
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsOthers++;
|
|
|
|
// Flugente: dynamic opinions: if this guy is not hostile towards us, then some mercs will complain about killing civilians
|
|
if ( pTarget->pSoldier->aiData.bNeutral || pTarget->pSoldier->bSide == pAttacker->pSoldier->bSide )
|
|
HandleDynamicOpinionChange( pAttacker->pSoldier, OPINIONEVENT_CIVKILLER, TRUE, TRUE );
|
|
}
|
|
break;
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
gStrategicStatus.usPlayerKills++;
|
|
}
|
|
else if( pAttacker->uiFlags & CELL_MILITIA )
|
|
{
|
|
pAttacker->pSoldier->ubMilitiaKills += 2;
|
|
}
|
|
if( pTarget->uiFlags & CELL_MERC && gpAR->fSound )
|
|
{
|
|
PlayAutoResolveSample( (UINT8)DOORCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
PlayAutoResolveSample( (UINT8)HEADCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
///////////////////////////////////////////////////////////////
|
|
// SANDRO - added the insta-healable value for doctor trait
|
|
else if ((IS_MERC_BODY_TYPE( pTarget->pSoldier ) || IS_CIV_BODY_TYPE( pTarget->pSoldier )) && ( gGameOptions.fNewTraitSystem ))
|
|
{
|
|
pTarget->pSoldier->iHealableInjury += ((pTarget->pSoldier->stats.bLife - iNewLife) * 100);
|
|
if (pTarget->pSoldier->iHealableInjury > ((pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->stats.bLife) * 100))
|
|
pTarget->pSoldier->iHealableInjury = ((pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->stats.bLife) * 100);
|
|
}
|
|
///////////////////////////////////////////////////////////////
|
|
//Adjust the soldiers stats based on the damage.
|
|
pTarget->pSoldier->stats.bLife = (INT8)max( iNewLife, 0 );
|
|
if( pTarget->uiFlags & CELL_MERC && gpAR->pRobotCell)
|
|
{
|
|
gpAR->pRobotCell->pSoldier->UpdateRobotControllerGivenRobot( );
|
|
}
|
|
if( fKnife || fClaw )
|
|
{
|
|
if( pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->bBleeding - iImpact >= pTarget->pSoldier->stats.bLife )
|
|
pTarget->pSoldier->bBleeding += (INT8)iImpact;
|
|
else
|
|
pTarget->pSoldier->bBleeding = (INT8)(pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->stats.bLife);
|
|
}
|
|
if( !pTarget->pSoldier->stats.bLife )
|
|
{
|
|
// SANDRO - added check to erase insta-healable amount of HPs if dead
|
|
if (pTarget->pSoldier->iHealableInjury > 0)
|
|
{ pTarget->pSoldier->iHealableInjury = 0; }
|
|
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
#ifdef INVULNERABILITY
|
|
if( 1 )
|
|
RefreshMerc( pTarget->pSoldier );
|
|
else
|
|
#endif
|
|
if( pTarget->uiFlags & CELL_MERC )
|
|
{
|
|
gpAR->usPlayerAttack -= pTarget->usAttack;
|
|
gpAR->usPlayerDefence -= pTarget->usDefence;
|
|
gpAR->ubAliveMercs--;
|
|
pTarget->usAttack = 0;
|
|
pTarget->usDefence = 0;
|
|
}
|
|
else if( pTarget->uiFlags & CELL_MILITIA )
|
|
{
|
|
gpAR->usPlayerAttack -= pTarget->usAttack;
|
|
gpAR->usPlayerDefence -= pTarget->usDefence;
|
|
gpAR->ubAliveCivs--;
|
|
pTarget->usAttack = 0;
|
|
pTarget->usDefence = 0;
|
|
}
|
|
else if( pTarget->uiFlags & (CELL_ENEMY|CELL_CREATURE) )
|
|
{
|
|
gpAR->usEnemyAttack -= pTarget->usAttack;
|
|
gpAR->usEnemyDefence -= pTarget->usDefence;
|
|
gpAR->ubAliveEnemies--;
|
|
pTarget->usAttack = 0;
|
|
pTarget->usDefence = 0;
|
|
}
|
|
}
|
|
pTarget->uiFlags |= CELL_HITBYATTACKER | CELL_DIRTY;
|
|
}
|
|
}
|
|
|
|
void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index )
|
|
{
|
|
INT32 iNewLife;
|
|
SOLDIERCELL *pAttacker;
|
|
if( !pTarget->pSoldier->stats.bLife )
|
|
{ //Soldier already dead (can't kill him again!)
|
|
return;
|
|
}
|
|
pAttacker = pTarget->pAttacker[ index ];
|
|
|
|
//creatures get damage reduction bonuses
|
|
switch( pTarget->pSoldier->ubBodyType )
|
|
{
|
|
case LARVAE_MONSTER:
|
|
case INFANT_MONSTER:
|
|
break;
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
pTarget->usHitDamage[index] = (pTarget->usHitDamage[index] + 2) / 4;
|
|
break;
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
pTarget->usHitDamage[index] = (pTarget->usHitDamage[index] + 3) / 6;
|
|
break;
|
|
case QUEENMONSTER:
|
|
pTarget->usHitDamage[index] = (pTarget->usHitDamage[index] + 4) / 8;
|
|
break;
|
|
}
|
|
|
|
iNewLife = pTarget->pSoldier->stats.bLife - pTarget->usHitDamage[index];
|
|
if( !pTarget->usHitDamage[index] )
|
|
{ //bullet missed -- play a ricochet sound.
|
|
if( pTarget->uiFlags & CELL_MERC )
|
|
// AGILITY GAIN: Target "dodged" an attack
|
|
StatChange( pTarget->pSoldier, AGILAMT, 5, FALSE );
|
|
PlayAutoResolveSample( MISS_1 + PreRandom(8), RATE_11025, 50, 1, MIDDLEPAN );
|
|
return;
|
|
}
|
|
|
|
if( pAttacker->uiFlags & CELL_MERC )
|
|
{ //Attacker is a player, so increment the number of shots that hit.
|
|
gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usShotsHit++;
|
|
// MARKSMANSHIP GAIN: Attacker's shot hits
|
|
StatChange( pAttacker->pSoldier, MARKAMT, 6, FALSE ); // in addition to 3 for taking a shot
|
|
}
|
|
if( pTarget->uiFlags & CELL_MERC && pTarget->usHitDamage[ index ] )
|
|
{ //Target is a player, so increment the times he has been wounded.
|
|
if( pTarget->usHitDamage[index] > 1 )
|
|
gMercProfiles[ pTarget->pSoldier->ubProfile ].records.usTimesWoundedShot++;
|
|
// EXPERIENCE GAIN: Took some damage
|
|
StatChange( pTarget->pSoldier, EXPERAMT, ( UINT16 )( 5 * ( pTarget->usHitDamage[ index ] / 10 ) ), FALSE );
|
|
}
|
|
|
|
//bullet hit -- play an impact sound and a merc hit sound
|
|
if ( TANK( pTarget->pSoldier ) )
|
|
PlayAutoResolveSample( (UINT8)(S_METAL_IMPACT1 + PreRandom( 3 )), RATE_11025, 50, 1, MIDDLEPAN );
|
|
else
|
|
PlayAutoResolveSample( (UINT8)(BULLET_IMPACT_1+PreRandom(3)), RATE_11025, 50, 1, MIDDLEPAN );
|
|
|
|
if( pTarget->pSoldier->stats.bLife >= CONSCIOUSNESS )
|
|
{
|
|
if( gpAR->fSound )
|
|
pTarget->pSoldier->DoMercBattleSound( (INT8)( BATTLE_SOUND_HIT1 + PreRandom( 2 ) ) );
|
|
}
|
|
|
|
if( iNewLife < OKLIFE && pTarget->pSoldier->stats.bLife >= OKLIFE )
|
|
{
|
|
//the hit caused the merc to fall. Play the falling sound
|
|
PlayAutoResolveSample( (UINT8)FALL_1, RATE_11025, 50, 1, MIDDLEPAN );
|
|
pTarget->uiFlags &= ~CELL_RETREATING;
|
|
}
|
|
|
|
if( iNewLife <= 0 )
|
|
{
|
|
// Flugente: campaign stats
|
|
gCurrentIncident.AddStat( pTarget->pSoldier, CAMPAIGNHISTORY_TYPE_KILL );
|
|
|
|
// Flugente: disease
|
|
HandleDeathDiseaseImplications( pTarget->pSoldier );
|
|
|
|
//soldier has been killed
|
|
if( pTarget->pAttacker[ index ]->uiFlags & CELL_PLAYER )
|
|
{ //Player killed the enemy soldier -- update his stats as well as any assisters.
|
|
SOLDIERCELL *pKiller;
|
|
SOLDIERCELL *pAssister1, *pAssister2;
|
|
pKiller = pTarget->pAttacker[ index ];
|
|
pAssister1 = pTarget->pAttacker[ index < 2 ? index + 1 : 0 ];
|
|
pAssister2 = pTarget->pAttacker[ index > 0 ? index - 1 : 2 ];
|
|
if( pKiller == pAssister1 )
|
|
pAssister1 = NULL;
|
|
if( pKiller == pAssister2 )
|
|
pAssister2 = NULL;
|
|
if( pAssister1 == pAssister2 )
|
|
pAssister2 = NULL;
|
|
if( pKiller )
|
|
{
|
|
if( pKiller->uiFlags & CELL_MERC )
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - new mercs' records
|
|
switch(pTarget->pSoldier->ubSoldierClass)
|
|
{
|
|
case SOLDIER_CLASS_ELITE :
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsElites++;
|
|
break;
|
|
case SOLDIER_CLASS_ARMY :
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsRegulars++;
|
|
break;
|
|
case SOLDIER_CLASS_ADMINISTRATOR :
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsAdmins++;
|
|
break;
|
|
case SOLDIER_CLASS_CREATURE :
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsCreatures++;
|
|
break;
|
|
#ifdef ENABLE_ZOMBIES
|
|
case SOLDIER_CLASS_ZOMBIE :
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsZombies++;
|
|
break;
|
|
#endif
|
|
default :
|
|
if ( CREATURE_OR_BLOODCAT( pTarget->pSoldier ) )
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsCreatures++;
|
|
else if ( TANK( pTarget->pSoldier ) ) // we hardly can kill a tank in autoresolve, but well.. who knows..
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsTanks++;
|
|
else if ( pTarget->pSoldier->bTeam == CIV_TEAM && !pTarget->pSoldier->aiData.bNeutral && pTarget->pSoldier->bSide != gbPlayerNum )
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsHostiles++;
|
|
else
|
|
gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsOthers++;
|
|
break;
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
gStrategicStatus.usPlayerKills++;
|
|
// EXPERIENCE CLASS GAIN: Earned a kill
|
|
StatChange( pKiller->pSoldier, EXPERAMT, ( UINT16 )( 10 * pTarget->pSoldier->pathing.bLevel ), FALSE );
|
|
HandleMoraleEvent( pKiller->pSoldier, MORALE_KILLED_ENEMY, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
|
}
|
|
else if( pKiller->uiFlags & CELL_MILITIA )
|
|
pKiller->pSoldier->ubMilitiaKills += 2;
|
|
}
|
|
if( pAssister1 )
|
|
{
|
|
if( pAssister1->uiFlags & CELL_MERC )
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - new mercs' records
|
|
if( pKiller )
|
|
{
|
|
if( pKiller->uiFlags & CELL_MERC && pKiller->pSoldier->bTeam == gbPlayerNum )
|
|
gMercProfiles[ pAssister1->pSoldier->ubProfile ].records.usAssistsMercs++;
|
|
else if ( pKiller->pSoldier->bTeam == MILITIA_TEAM )
|
|
gMercProfiles[ pAssister1->pSoldier->ubProfile ].records.usAssistsMilitia++;
|
|
else
|
|
gMercProfiles[ pAssister1->pSoldier->ubProfile ].records.usAssistsOthers++;
|
|
}
|
|
else
|
|
{
|
|
gMercProfiles[ pAssister1->pSoldier->ubProfile ].records.usAssistsOthers++;
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// EXPERIENCE CLASS GAIN: Earned an assist
|
|
StatChange( pAssister1->pSoldier, EXPERAMT, ( UINT16 )( 5 * pTarget->pSoldier->pathing.bLevel ), FALSE );
|
|
}
|
|
else if( pAssister1->uiFlags & CELL_MILITIA )
|
|
pAssister1->pSoldier->ubMilitiaKills++;
|
|
}
|
|
else if( pAssister2 )
|
|
{
|
|
if( pAssister2->uiFlags & CELL_MERC )
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// SANDRO - new mercs' records
|
|
if( pKiller )
|
|
{
|
|
if( pKiller->uiFlags & CELL_MERC && pKiller->pSoldier->bTeam == gbPlayerNum )
|
|
gMercProfiles[ pAssister2->pSoldier->ubProfile ].records.usAssistsMercs++;
|
|
else if ( pKiller->pSoldier->bTeam == MILITIA_TEAM )
|
|
gMercProfiles[ pAssister2->pSoldier->ubProfile ].records.usAssistsMilitia++;
|
|
else
|
|
gMercProfiles[ pAssister2->pSoldier->ubProfile ].records.usAssistsOthers++;
|
|
}
|
|
else
|
|
{
|
|
gMercProfiles[ pAssister2->pSoldier->ubProfile ].records.usAssistsOthers++;
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// EXPERIENCE CLASS GAIN: Earned an assist
|
|
StatChange( pAssister2->pSoldier, EXPERAMT, ( UINT16 )( 5 * pTarget->pSoldier->pathing.bLevel ), FALSE );
|
|
}
|
|
else if( pAssister2->uiFlags & CELL_MILITIA )
|
|
pAssister2->pSoldier->ubMilitiaKills++;
|
|
}
|
|
}
|
|
if( pTarget->uiFlags & CELL_MERC && gpAR->fSound )
|
|
{
|
|
PlayAutoResolveSample( (UINT8)DOORCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
PlayAutoResolveSample( (UINT8)HEADCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
if( iNewLife < -60 && !(pTarget->uiFlags & CELL_CREATURE) )
|
|
{ //High damage death
|
|
if( gpAR->fSound )
|
|
{
|
|
if( PreRandom( 3 ) )
|
|
PlayAutoResolveSample( (UINT8)BODY_SPLAT_1, RATE_11025, 50, 1, MIDDLEPAN );
|
|
else
|
|
PlayAutoResolveSample( (UINT8)HEADSPLAT_1, RATE_11025, 50, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
else
|
|
{ //Normal death
|
|
if( gpAR->fSound )
|
|
{
|
|
if ( TANK( pTarget->pSoldier ) )
|
|
PlayAutoResolveSample( (UINT8)(S_RAID_TB_BOMB), RATE_11025, 50, 1, MIDDLEPAN );
|
|
else
|
|
pTarget->pSoldier->DoMercBattleSound( BATTLE_SOUND_DIE1 );
|
|
}
|
|
}
|
|
#ifdef INVULNERABILITY
|
|
RefreshMerc( pTarget->pSoldier );
|
|
return;
|
|
#endif
|
|
}
|
|
///////////////////////////////////////////////////////////////
|
|
// SANDRO - added the insta-healable value for doctor trait
|
|
else if ((IS_MERC_BODY_TYPE( pTarget->pSoldier ) || IS_CIV_BODY_TYPE( pTarget->pSoldier )) && ( gGameOptions.fNewTraitSystem ))
|
|
{
|
|
pTarget->pSoldier->iHealableInjury += ((pTarget->pSoldier->stats.bLife - iNewLife) * 100);
|
|
if (pTarget->pSoldier->iHealableInjury > ((pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->stats.bLife) * 100))
|
|
pTarget->pSoldier->iHealableInjury = ((pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->stats.bLife) * 100);
|
|
}
|
|
///////////////////////////////////////////////////////////////
|
|
//Adjust the soldiers stats based on the damage.
|
|
pTarget->pSoldier->stats.bLife = (INT8)max( iNewLife, 0 );
|
|
if( pTarget->uiFlags & CELL_MERC && gpAR->pRobotCell)
|
|
{
|
|
gpAR->pRobotCell->pSoldier->UpdateRobotControllerGivenRobot( );
|
|
}
|
|
|
|
if( pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->bBleeding - pTarget->usHitDamage[index] >= pTarget->pSoldier->stats.bLife )
|
|
pTarget->pSoldier->bBleeding += (INT8)pTarget->usHitDamage[index];
|
|
else
|
|
pTarget->pSoldier->bBleeding = (INT8)(pTarget->pSoldier->stats.bLifeMax - pTarget->pSoldier->stats.bLife);
|
|
if( !pTarget->pSoldier->stats.bLife )
|
|
{
|
|
// SANDRO - added check to erase insta-healable amount of HPs if dead
|
|
if (pTarget->pSoldier->iHealableInjury > 0)
|
|
{ pTarget->pSoldier->iHealableInjury = 0; }
|
|
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
if( pTarget->uiFlags & CELL_MERC )
|
|
{
|
|
gpAR->usPlayerAttack -= pTarget->usAttack;
|
|
gpAR->usPlayerDefence -= pTarget->usDefence;
|
|
gpAR->ubAliveMercs--;
|
|
pTarget->usAttack = 0;
|
|
pTarget->usDefence = 0;
|
|
}
|
|
else if( pTarget->uiFlags & CELL_MILITIA )
|
|
{
|
|
gpAR->usPlayerAttack -= pTarget->usAttack;
|
|
gpAR->usPlayerDefence -= pTarget->usDefence;
|
|
gpAR->ubAliveCivs--;
|
|
pTarget->usAttack = 0;
|
|
pTarget->usDefence = 0;
|
|
}
|
|
else if( pTarget->uiFlags & (CELL_ENEMY|CELL_CREATURE) )
|
|
{
|
|
gpAR->usEnemyAttack -= pTarget->usAttack;
|
|
gpAR->usEnemyDefence -= pTarget->usDefence;
|
|
gpAR->ubAliveEnemies--;
|
|
pTarget->usAttack = 0;
|
|
pTarget->usDefence = 0;
|
|
}
|
|
}
|
|
pTarget->uiFlags |= CELL_HITBYATTACKER | CELL_DIRTY;
|
|
}
|
|
|
|
void Delay( UINT32 uiMilliseconds )
|
|
{
|
|
INT32 iTime;
|
|
iTime = GetJA2Clock();
|
|
while( GetJA2Clock() < iTime + uiMilliseconds );
|
|
}
|
|
|
|
BOOLEAN IsBattleOver()
|
|
{
|
|
INT32 i;
|
|
INT32 iNumInvolvedMercs = 0;
|
|
INT32 iNumMercsRetreated = 0;
|
|
BOOLEAN fOnlyEPCsLeft = TRUE;
|
|
if( gpAR->ubBattleStatus != BATTLE_IN_PROGRESS )
|
|
return TRUE;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( !(gpMercs[ i ].uiFlags & CELL_RETREATED) && gpMercs[ i ].pSoldier->stats.bLife )
|
|
{
|
|
if( !(gpMercs[ i ].uiFlags & CELL_EPC) )
|
|
{
|
|
fOnlyEPCsLeft = FALSE;
|
|
iNumInvolvedMercs++;
|
|
}
|
|
}
|
|
if( gpMercs[ i ].uiFlags & CELL_RETREATED )
|
|
{
|
|
iNumMercsRetreated++;
|
|
}
|
|
}
|
|
if( gpAR->pRobotCell )
|
|
{ //Do special robot checks
|
|
SOLDIERTYPE *pRobot;
|
|
pRobot = gpAR->pRobotCell->pSoldier;
|
|
if( pRobot->ubRobotRemoteHolderID == NOBODY )
|
|
{ //Robot can't fight anymore.
|
|
gpAR->usPlayerAttack -= gpAR->pRobotCell->usAttack;
|
|
gpAR->pRobotCell->usAttack = 0;
|
|
if( iNumInvolvedMercs == 1 && !gpAR->ubAliveCivs )
|
|
{ //Robot is the only one left in battle, so instantly kill him.
|
|
pRobot->DoMercBattleSound( BATTLE_SOUND_DIE1 );
|
|
pRobot->stats.bLife = 0;
|
|
gpAR->ubAliveMercs--;
|
|
iNumInvolvedMercs = 0;
|
|
}
|
|
}
|
|
}
|
|
if( !gpAR->ubAliveCivs && !iNumInvolvedMercs && iNumMercsRetreated )
|
|
{ //RETREATED
|
|
gpAR->ubBattleStatus = BATTLE_RETREAT;
|
|
|
|
// wake everyone up
|
|
WakeUpAllMercsInSectorUnderAttack( );
|
|
|
|
RetreatAllInvolvedPlayerGroups( );
|
|
}
|
|
else if( !gpAR->ubAliveCivs && !iNumInvolvedMercs )
|
|
{ //DEFEAT
|
|
if( fOnlyEPCsLeft )
|
|
{ //Kill the EPCs.
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
if( gpMercs[ i ].uiFlags & CELL_EPC )
|
|
{
|
|
gpMercs[ i ].pSoldier->DoMercBattleSound( BATTLE_SOUND_DIE1 );
|
|
gpMercs[ i ].pSoldier->stats.bLife = 0;
|
|
gpMercs[ i ].pSoldier->iHealableInjury = 0; // added by SANDRO
|
|
gpAR->ubAliveMercs--;
|
|
}
|
|
}
|
|
}
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( gpEnemies[ i ].pSoldier->stats.bLife )
|
|
{
|
|
if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
|
|
{
|
|
gpEnemies[ i ].pSoldier->DoMercBattleSound( BATTLE_SOUND_LAUGH1 );
|
|
}
|
|
else
|
|
{
|
|
PlayJA2Sample( ACR_EATFLESH, RATE_11025, 50, 1, MIDDLEPAN );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
gpAR->ubBattleStatus = BATTLE_DEFEAT;
|
|
}
|
|
else if( !gpAR->ubAliveEnemies )
|
|
{ //VICTORY
|
|
gpAR->ubBattleStatus = BATTLE_VICTORY;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
SetupDoneInterface();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//#define TESTSURRENDER
|
|
|
|
BOOLEAN AttemptPlayerCapture()
|
|
{
|
|
INT32 i;
|
|
BOOLEAN fConcious;
|
|
INT32 iConciousEnemies;
|
|
|
|
#ifndef TESTSURRENDER
|
|
|
|
//Only attempt capture if day is less than four.
|
|
if( GetWorldDay() < STARTDAY_ALLOW_PLAYER_CAPTURE_FOR_RESCUE && !gpAR->fAllowCapture )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if( gpAR->fPlayerRejectedSurrenderOffer )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if( gpAR->fCaptureNotPermittedDueToEPCs )
|
|
{ //EPCs make things much more difficult when considering capture. Simply don't allow it.
|
|
return FALSE;
|
|
}
|
|
//Only attempt capture of mercs if there are 2 or 3 of them alive
|
|
if( gpAR->ubAliveCivs || gpAR->ubAliveMercs < 2 || gpAR->ubAliveMercs > 3 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
//if the number of alive enemies doesn't double the number of alive mercs, don't offer surrender.
|
|
if( gpAR->ubAliveEnemies < gpAR->ubAliveMercs*2 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
//make sure that these enemies are actually concious!
|
|
iConciousEnemies = 0;
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
{
|
|
if( gpEnemies[ i ].pSoldier->stats.bLife >= OKLIFE )
|
|
{
|
|
iConciousEnemies++;
|
|
}
|
|
}
|
|
if( iConciousEnemies < gpAR->ubAliveMercs * 2 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//So far, the conditions are right. Now, we will determine if the the remaining players are
|
|
//wounded and/or unconcious. If any are concious, we will prompt for a surrender, otherwise,
|
|
//it is automatic.
|
|
fConcious = FALSE;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{
|
|
//if any of the 2 or 3 mercs has more than 60% life, then return.
|
|
if( gpMercs[ i ].uiFlags & CELL_ROBOT )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if( gpMercs[ i ].pSoldier->stats.bLife*100 > gpMercs[ i ].pSoldier->stats.bLifeMax*60 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if( gpMercs[ i ].pSoldier->stats.bLife >= OKLIFE )
|
|
{
|
|
fConcious = TRUE;
|
|
}
|
|
}
|
|
if( fConcious )
|
|
{
|
|
if( PreRandom( 100 ) < 2 )
|
|
{
|
|
SetupSurrenderInterface();
|
|
}
|
|
}
|
|
else if( PreRandom( 100 ) < 25 )
|
|
{
|
|
#else
|
|
{
|
|
#endif
|
|
|
|
BeginCaptureSquence( );
|
|
|
|
gpAR->ubBattleStatus = BATTLE_CAPTURED;
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
SetupDoneInterface();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void SetupDoneInterface()
|
|
{
|
|
INT32 i;
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
|
|
HideButton( gpAR->iButton[ PAUSE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ PLAY_BUTTON ] );
|
|
HideButton( gpAR->iButton[ FAST_BUTTON ] );
|
|
HideButton( gpAR->iButton[ FINISH_BUTTON ] );
|
|
HideButton( gpAR->iButton[ RETREAT_BUTTON ] );
|
|
HideButton( gpAR->iButton[ YES_BUTTON ] );
|
|
HideButton( gpAR->iButton[ NO_BUTTON ] );
|
|
if( gpAR->ubBattleStatus == BATTLE_VICTORY && gpAR->ubAliveMercs )
|
|
{
|
|
ShowButton( gpAR->iButton[ DONEWIN_BUTTON ] );
|
|
ShowButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
}
|
|
else
|
|
{
|
|
ShowButton( gpAR->iButton[ DONELOSE_BUTTON ] );
|
|
}
|
|
DetermineBandageButtonState();
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
{ //So they can't retreat!
|
|
MSYS_DisableRegion( gpMercs[ i ].pRegion );
|
|
}
|
|
}
|
|
|
|
void SetupSurrenderInterface()
|
|
{
|
|
HideButton( gpAR->iButton[ PAUSE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ PLAY_BUTTON ] );
|
|
HideButton( gpAR->iButton[ FAST_BUTTON ] );
|
|
HideButton( gpAR->iButton[ FINISH_BUTTON ] );
|
|
HideButton( gpAR->iButton[ RETREAT_BUTTON ] );
|
|
HideButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ DONEWIN_BUTTON ] );
|
|
HideButton( gpAR->iButton[ DONELOSE_BUTTON ] );
|
|
ShowButton( gpAR->iButton[ YES_BUTTON ] );
|
|
ShowButton( gpAR->iButton[ NO_BUTTON ] );
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
gpAR->fPendingSurrender = TRUE;
|
|
}
|
|
|
|
void HideSurrenderInterface()
|
|
{
|
|
HideButton( gpAR->iButton[ PAUSE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ PLAY_BUTTON ] );
|
|
HideButton( gpAR->iButton[ FAST_BUTTON ] );
|
|
HideButton( gpAR->iButton[ FINISH_BUTTON ] );
|
|
HideButton( gpAR->iButton[ RETREAT_BUTTON ] );
|
|
HideButton( gpAR->iButton[ BANDAGE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ DONEWIN_BUTTON ] );
|
|
HideButton( gpAR->iButton[ DONELOSE_BUTTON ] );
|
|
HideButton( gpAR->iButton[ YES_BUTTON ] );
|
|
HideButton( gpAR->iButton[ NO_BUTTON ] );
|
|
gpAR->fPendingSurrender = FALSE;
|
|
gpAR->fRenderAutoResolve = TRUE;
|
|
}
|
|
|
|
void AcceptSurrenderCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
BeginCaptureSquence( );
|
|
|
|
gpAR->ubBattleStatus = BATTLE_SURRENDERED;
|
|
gpAR->fPendingSurrender = FALSE;
|
|
SetupDoneInterface();
|
|
}
|
|
}
|
|
|
|
void RejectSurrenderCallback( GUI_BUTTON *btn, INT32 reason )
|
|
{
|
|
if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
|
|
{
|
|
gpAR->fPlayerRejectedSurrenderOffer = TRUE;
|
|
HideSurrenderInterface();
|
|
}
|
|
}
|
|
|
|
|
|
void ProcessBattleFrame()
|
|
{
|
|
INT32 iRandom;
|
|
INT32 i;
|
|
SOLDIERCELL *pAttacker, *pTarget;
|
|
UINT32 uiDiff;
|
|
static INT32 iTimeSlice = 0;
|
|
static BOOLEAN fContinue = FALSE;
|
|
static UINT32 uiSlice = 0;
|
|
static INT32 iTotal = 0;
|
|
static INT32 iMercs = 0;
|
|
static INT32 iCivs = 0;
|
|
static INT32 iEnemies = 0;
|
|
static INT32 iMercsLeft = 0;
|
|
static INT32 iCivsLeft = 0;
|
|
static INT32 iEnemiesLeft = 0;
|
|
BOOLEAN found = FALSE;
|
|
INT32 iTime, iAttacksThisFrame;
|
|
|
|
pAttacker = NULL;
|
|
iAttacksThisFrame = 0;
|
|
|
|
if( fContinue )
|
|
{
|
|
gpAR->uiCurrTime = GetJA2Clock();
|
|
fContinue = FALSE;
|
|
goto CONTINUE_BATTLE;
|
|
}
|
|
//determine how much real-time has passed since the last frame
|
|
if( gpAR->uiCurrTime )
|
|
{
|
|
gpAR->uiPrevTime = gpAR->uiCurrTime;
|
|
gpAR->uiCurrTime = GetJA2Clock();
|
|
}
|
|
else
|
|
{
|
|
gpAR->uiCurrTime = GetJA2Clock();
|
|
return;
|
|
}
|
|
if( gpAR->fPaused )
|
|
return;
|
|
|
|
uiDiff = gpAR->uiCurrTime - gpAR->uiPrevTime;
|
|
if( gpAR->uiTimeSlice < 0xffffffff )
|
|
{
|
|
iTimeSlice = uiDiff*gpAR->uiTimeSlice/1000;
|
|
}
|
|
else
|
|
{ //largest positive signed value
|
|
iTimeSlice = 0x7fffffff;
|
|
}
|
|
|
|
while( iTimeSlice > 0 )
|
|
{
|
|
uiSlice = min( iTimeSlice, 1000 );
|
|
if( gpAR->ubBattleStatus == BATTLE_IN_PROGRESS )
|
|
gpAR->uiTotalElapsedBattleTimeInMilliseconds += uiSlice;
|
|
|
|
//Now process each of the players
|
|
iTotal = gpAR->ubMercs + gpAR->ubCivs + gpAR->ubEnemies + 1;
|
|
iMercs = iMercsLeft = gpAR->ubMercs;
|
|
iCivs = iCivsLeft = gpAR->ubCivs;
|
|
iEnemies = iEnemiesLeft = gpAR->ubEnemies;
|
|
for( i = 0; i < gpAR->ubMercs; i++ )
|
|
gpMercs[ i ].uiFlags &= ~CELL_PROCESSED;
|
|
for( i = 0; i < gpAR->ubCivs; i++ )
|
|
gpCivs[ i ].uiFlags &= ~CELL_PROCESSED;
|
|
for( i = 0; i < gpAR->ubEnemies; i++ )
|
|
gpEnemies[ i ].uiFlags &= ~CELL_PROCESSED;
|
|
while( --iTotal )
|
|
{
|
|
INT32 cnt;
|
|
if( iTimeSlice != 0x7fffffff && GetJA2Clock() > gpAR->uiCurrTime+17 ||
|
|
!gpAR->fInstantFinish && iAttacksThisFrame > (gpAR->ubMercs+gpAR->ubCivs+gpAR->ubEnemies)/4 )
|
|
{ //We have spent too much time in here. In order to maintain 60FPS, we will
|
|
//leave now, which will allow for updating of the graphics (and mouse cursor),
|
|
//and all of the necessary locals are saved via static variables. It'll check
|
|
//the fContinue flag, and goto the CONTINUE_BATTLE label the next time this function
|
|
//is called.
|
|
fContinue = TRUE;
|
|
return;
|
|
}
|
|
CONTINUE_BATTLE:
|
|
if( IsBattleOver() || gubEnemyEncounterCode != CREATURE_ATTACK_CODE && AttemptPlayerCapture() )
|
|
return;
|
|
|
|
iRandom = PreRandom( iTotal );
|
|
found = FALSE;
|
|
if( iMercs && iRandom < iMercsLeft )
|
|
{
|
|
iMercsLeft--;
|
|
while( !found )
|
|
{
|
|
iRandom = Random( iMercs );
|
|
pAttacker = &gpMercs[ iRandom ];
|
|
if( !(pAttacker->uiFlags & CELL_PROCESSED ) )
|
|
{
|
|
pAttacker->uiFlags |= CELL_PROCESSED;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else if( iCivs && iRandom < iMercsLeft + iCivsLeft )
|
|
{
|
|
iCivsLeft--;
|
|
while( !found )
|
|
{
|
|
iRandom = Random( iCivs );
|
|
pAttacker = &gpCivs[ iRandom ];
|
|
if( !(pAttacker->uiFlags & CELL_PROCESSED ) )
|
|
{
|
|
pAttacker->uiFlags |= CELL_PROCESSED;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else if( iEnemies && iEnemiesLeft )
|
|
{
|
|
iEnemiesLeft--;
|
|
while( !found )
|
|
{
|
|
iRandom = Random( iEnemies );
|
|
pAttacker = &gpEnemies[ iRandom ];
|
|
if( !(pAttacker->uiFlags & CELL_PROCESSED ) )
|
|
{
|
|
pAttacker->uiFlags |= CELL_PROCESSED;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
AssertMsg( 0, "Logic error in ProcessBattleFrame()" );
|
|
//Apply damage and play miss/hit sounds if delay between firing and hit has expired.
|
|
if( !(pAttacker->uiFlags & CELL_RETREATED ) )
|
|
{
|
|
for( cnt = 0; cnt < 3; ++cnt )
|
|
{ //Check if any incoming bullets have hit the target.
|
|
if( pAttacker->usNextHit[ cnt ] )
|
|
{
|
|
iTime = pAttacker->usNextHit[ cnt ];
|
|
iTime -= uiSlice;
|
|
if( iTime >= 0 )
|
|
{ //Bullet still on route.
|
|
pAttacker->usNextHit[ cnt ] = (UINT16)iTime;
|
|
}
|
|
else
|
|
{ //Bullet is going to hit/miss.
|
|
TargetHitCallback( pAttacker, cnt );
|
|
pAttacker->usNextHit[ cnt ] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( pAttacker->pSoldier->stats.bLife < OKLIFE || pAttacker->uiFlags & CELL_RETREATED )
|
|
{
|
|
if( !(pAttacker->uiFlags & CELL_CREATURE) || !pAttacker->pSoldier->stats.bLife )
|
|
continue; //can't attack if you are unconcious or not around (Or a live creature)
|
|
}
|
|
iTime = pAttacker->usNextAttack;
|
|
iTime -= uiSlice;
|
|
if( iTime > 0 )
|
|
{
|
|
pAttacker->usNextAttack = (UINT32)iTime;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if( pAttacker->uiFlags & CELL_RETREATING )
|
|
{ //The merc has successfully retreated. Remove the stats, and continue on.
|
|
if( pAttacker == gpAR->pRobotCell )
|
|
{
|
|
if( gpAR->pRobotCell->pSoldier->ubRobotRemoteHolderID == NOBODY )
|
|
{
|
|
gpAR->pRobotCell->uiFlags &= ~CELL_RETREATING;
|
|
gpAR->pRobotCell->uiFlags |= CELL_DIRTY;
|
|
gpAR->pRobotCell->usNextAttack = 0xffff;
|
|
continue;
|
|
}
|
|
}
|
|
gpAR->usPlayerDefence -= pAttacker->usDefence;
|
|
pAttacker->usDefence = 0;
|
|
pAttacker->uiFlags |= CELL_RETREATED;
|
|
continue;
|
|
}
|
|
if( pAttacker->usAttack )
|
|
{
|
|
pTarget = ChooseTarget( pAttacker );
|
|
if( pAttacker->uiFlags & CELL_CREATURE && PreRandom( 100 ) < 7 )
|
|
PlayAutoResolveSample( ACR_SMELL_THREAT + PreRandom( 2 ), RATE_11025, 50, 1, MIDDLEPAN );
|
|
else
|
|
AttackTarget( pAttacker, pTarget );
|
|
ResetNextAttackCounter( pAttacker );
|
|
pAttacker->usNextAttack += (UINT32)iTime; //tack on the remainder
|
|
iAttacksThisFrame++;
|
|
}
|
|
}
|
|
}
|
|
if( iTimeSlice != 0x7fffffff )//|| !gpAR->fInstantFinish )
|
|
{
|
|
iTimeSlice -= 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN IsAutoResolveActive()
|
|
{
|
|
//is the autoresolve up or not?
|
|
if( gpAR )
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
UINT8 GetAutoResolveSectorID()
|
|
{
|
|
if( gpAR )
|
|
{
|
|
return (UINT8)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY );
|
|
}
|
|
return 0xff;
|
|
}
|
|
|
|
//Returns TRUE if a battle is happening or sector is loaded
|
|
BOOLEAN GetCurrentBattleSectorXYZ( INT16 *psSectorX, INT16 *psSectorY, INT16 *psSectorZ )
|
|
{
|
|
if( gpAR )
|
|
{
|
|
*psSectorX = gpAR->ubSectorX;
|
|
*psSectorY = gpAR->ubSectorY;
|
|
*psSectorZ = 0;
|
|
return TRUE;
|
|
}
|
|
else if( gfPreBattleInterfaceActive )
|
|
{
|
|
*psSectorX = gubPBSectorX;
|
|
*psSectorY = gubPBSectorY;
|
|
*psSectorZ = gubPBSectorZ;
|
|
return TRUE;
|
|
}
|
|
else if( gfWorldLoaded )
|
|
{
|
|
*psSectorX = gWorldSectorX;
|
|
*psSectorY = gWorldSectorY;
|
|
*psSectorZ = gbWorldSectorZ;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
*psSectorX = 0;
|
|
*psSectorY = 0;
|
|
*psSectorZ = -1;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//Returns TRUE if a battle is happening ONLY
|
|
BOOLEAN GetCurrentBattleSectorXYZAndReturnTRUEIfThereIsABattle( INT16 *psSectorX, INT16 *psSectorY, INT16 *psSectorZ )
|
|
{
|
|
if ( ( psSectorX == NULL )||(psSectorY == NULL )||(psSectorZ == NULL ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if( gpAR )
|
|
{
|
|
*psSectorX = gpAR->ubSectorX;
|
|
*psSectorY = gpAR->ubSectorY;
|
|
*psSectorZ = 0;
|
|
return TRUE;
|
|
}
|
|
else if( gfPreBattleInterfaceActive )
|
|
{
|
|
*psSectorX = gubPBSectorX;
|
|
*psSectorY = gubPBSectorY;
|
|
*psSectorZ = gubPBSectorZ;
|
|
return TRUE;
|
|
}
|
|
else if( gfWorldLoaded )
|
|
{
|
|
*psSectorX = gWorldSectorX;
|
|
*psSectorY = gWorldSectorY;
|
|
*psSectorZ = gbWorldSectorZ;
|
|
if( gTacticalStatus.fEnemyInSector )
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
*psSectorX = 0;
|
|
*psSectorY = 0;
|
|
*psSectorZ = -1;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void AutoBandageFinishedCallback( UINT8 ubResult )
|
|
{
|
|
SetupDoneInterface();
|
|
}
|
|
|
|
BOOLEAN ProcessLoyalty()
|
|
{
|
|
if( !gpAR ) return FALSE;
|
|
|
|
if( PlayerMercsInSector( gpAR->ubSectorX, gpAR->ubSectorY, 0 ) )
|
|
return TRUE;
|
|
|
|
if( GetTownIdForSector( gpAR->ubSectorX, gpAR->ubSectorY ) != BLANK_SECTOR ||
|
|
IsThisSectorASAMSector( gpAR->ubSectorX, gpAR->ubSectorY, 0 ) )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void CheckForSoldiersWhoRetreatedIntoMilitiaHeldSectors()
|
|
{
|
|
for(int sX = 1; sX < ( MAP_WORLD_X - 1 ); sX++ ) {
|
|
for(int sY = 1; sY < ( MAP_WORLD_Y - 1); sY++ ) {
|
|
// Check if there is a sector where enemies retreated to and there are also militia present
|
|
if ( (NumNonPlayerTeamMembersInSector( sX, sY, ENEMY_TEAM ) > 0) &&
|
|
(NumNonPlayerTeamMembersInSector( sX, sY, MILITIA_TEAM ) > 0) &&
|
|
(!gTacticalStatus.fEnemyInSector)) {
|
|
unsigned mercCnt = 0;
|
|
for( int i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) {
|
|
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE) && !AM_A_ROBOT( MercPtrs[ i ] ) )
|
|
{ //Merc is active and alive, and not a vehicle or robot
|
|
if ((MercPtrs[ i ]->sSectorX == sX) &&(MercPtrs[ i ]->sSectorY == sY) && (MercPtrs[ i ]->bSectorZ == 0)) {
|
|
++mercCnt;
|
|
}
|
|
}
|
|
}
|
|
// If there are PC mercs here the player will have to handle the battle
|
|
if (mercCnt == 0) {
|
|
// EnterAutoResolveMode ((UINT8)sX, (UINT8)sY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|