Files
source/Strategic/Auto Resolve.cpp
T
Wanne 7ca53fadc7 *** MERGED source code from development trunk revision 4890 up to latest current revision 5105 ***
Development Trunk (now obsolete): https://ja2svn.dyndns.org/source/ja2/branches/Wanne/JA2%201.13%20MP
A detailed list of all the features (changes) is visible in the history log of the development trunk:

INFO: There will be no further development in the development source trunk, so is obsolete from now on!

This commit contains the following features (bugfixes are not listed):

- New Feature: Show merc ranks in Strategy Screen before merc name
o Toggle on/off in the ingame options screen
o For each experience level (0-10) there are defined rank names in the language specific text files
o Additional language text that is not translated yet is marked with // TODO.Translate

- New Feature: Faster Bobby Ray Shipments
o This feature is selectable in the ja2_options.ini
o When activated Bobby Ray shipments will be really fast
-> Overnight Express: 5 - 7 hours
-> Air Service: 10, 12 or 14 hours
-> Standard Service: 15, 18 or 21 hours
o In a multiplayer game, this feature is disabled (because shipment arrive immediately in the destination sector)

- New Feature: AP cost for manipulating the merc inventory
o This feature is selectable in the start new game screen
o For example, putting a weapon from hand in the backpack costs APs
o The AP costs are defined in the APBPConstants.ini
o In a multiplayer game, this feature is disabled

- New Feature: Added possibility to set individual Gear Kit names
o Set individual names in the optional new tag <mGearKitName> inside the <GEARKIT> root element (File: TableData\MercStartingGear.xml)

- New Feature: Externalized the maximum purchase amount (JA2 vanilla: 10) for Bobby Rays purchases per shipment
o Define the max. amount in the ja2_options.ini: BOBBY_RAY_MAX_PURCHASE_AMOUNT, Range: 10 - 100)
o Nice scrolling bars to scroll through all the purchases

- New Feature: Now it is possible to walk on "water" tiles or handle those tiles like "normal" tiles 
o (instead of showing the swimming animation and counting the APs for swimming), when the soldier is located on the second level (level > 0)

- New Feature: Externalized AIM Veterans laptop site
o Add maximum of 4 pages
o Add  new AIM to veterans page, and remove AIM from veterans page.
o New XML file: OldAIMArchive.xml (language specific XML files: *.OldAIMArchive.xml)

- New Feature: Added possibility to choose from 254 IMP faces
o Modified IMPPortraits.xml
o Added new GameDir folder "IMPFaces" which contains the IMP face portraits
o Moved IMP faces in the new "IMPFaces" folder

- New Feature: ARSP - All Resolution Support Project
o See here: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=298698&page=1
o Support any kind of screen resolution. This works by choosing the matching full supported resolution (640x480, 800x600, 1024x768) and then centering the graphics
o See ja2.ini for defining the screen resolution

- New Feature: AI Turn Speed Up (FF-MOD)
o see here: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=298699#Post298699
o When this feature is activated it allows fast enemy turns
o New entry in ja2.ini to enable this feature: HIGHSPEED_TIMER = TRUE
o Turn on/off auto fast turns in ingame options
o New ja2_options.ini settings for fine tuning: [Clock Settings] ini block

- New Feature: "Lock" / "Release" mouse cursor in tactical windowed mode, so the mouse can stay within window boundary
o This is very helpful for doing scrolling with the mouse (by moving the mouse to the window edge) in windowed mode
o You can toggle on/off the feature by pressing CTRL + Z. It is also possible to press CTRL + Y. So the user can decide which fits better :)
o Whenever you toggle on/off a screen message appears on the tactical log to notify the player

- New Feature: Weapon overheating
o Basically, every shot fired from a gun increases its temperature. On higher temperatures, the gun jams more frequently
o On/off toggle in the ingame options
o Graphical thermometer indication on the weapon picture
o New ja2_options.ini section with a few options, Section: [Tactical Weapon Overheating Settings]
o XML changes (New Tags)
--> AmmoTypes.xml: <temperatureModificator>
--> Items.xml: <barrel>, <usOverheatingCooldownFactor>
--> Weapons.xml: <usOverheatingJamThreshold>, <usOverheatingDamageThreshold>, <usOverheatingSingleShotTemperature>
o Added a few barells to Items.xml (end of the file)
o more infos: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=301059&page=1

- New Feature: Resting weapons on a surfaces
o You can now rest your weapon on crates, low walls, rocks, debris, open windows etc. while you are crouched. Resting will give yu the superior gun handling modifiers from PRONE position.
o A little 'A' on your gun indicates if you are resting it or not.
o Both settings can be turned on and off in Ja2_Options.ini under 'Tactical Gameplay Settings'

- New Feature: Externalized option on whether mercs can exit sector using grid exit in turn based mode
o new ja2_options.ini property: GRID_EXIT_IN_TURNBASED
o This ini setting does not affect leaving sectors on map edges

- Rework/Update: Ammo boxes and Ammo crates
o Ammo crate items added for remaining calibers.  Tweaked prices to correspond to magazines, tweaked capacities and weights to be reasonable (~800 or less).
o Shift-R reload now looks at both boxes and crates in sector items and merc's inventory to reload, and tops off magazines in inventory as well.  Only possible outside of combat.
o Ctrl-Shift-A still creates crates as per usual, with some fixes to ensure all ammo is caught on the first press.
o Shift-A now creates ammo boxes in the same loop.  They mostly behave like crates, but fit in mercs inventories.
o Ammo boxes are available to purchase at BR and tony, sam, etc.  Individual magazines/drums/etc are no longer available.
o Removed ammo crate xml tag.  Added ubMagType tag to Magazines array.  This allows us to differentiate between magazines, bullets, boxes and crates.
o Changed ammo filtering in BR's to display boxes appropriately.  Added back the "Mag:" display (changed text to "Size:")
o Added ja2set.dat.xml file (WF map version in WF maps folder) to be used instead of ja2set.dat file (ja2.ini setting added) For now, if you want to play vanilla instead, just change the setting in the ini (see comments in file).
o Added P4-P9Items image files (up to 5k images each) and tileslots.  1.13 core items will continue to use Guns and P1-P3Items for the foreseeable future.  P4-P9 can be used by customizers/modders to avoid conflicts with Guns/P1-P3 items.  TODO: Need to discuss a strategy for this in the forums.
o Added in Ambient/0.bad and 32.bad to avoid an exception in the code.
o Updated the XML Editor
o Standardized the text on all the ammo items.

- New Multiplayer Feature: Sharing same FOV (field of view) in a coop/team-dm (for friendly teams) game
o You will also see enemies, that your teammate sees

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5108 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-03-23 18:46:54 +00:00

5505 lines
181 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
#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;
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
//Combined flags
#define CELL_PLAYER ( CELL_MERC | CELL_MILITIA )
#define CELL_ENEMY ( CELL_ELITE | CELL_TROOP | CELL_ADMIN )
#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,
MILITIA1_FACE,
MILITIA2_FACE,
MILITIA3_FACE,
YM_CREATURE_FACE,
AM_CREATURE_FACE,
YF_CREATURE_FACE,
AF_CREATURE_FACE,
HUMAN_SKULL,
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 ) ];
// 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 ] );
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() )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->ubNumCreatures = 0;
pSector->bLastKnownEnemies = 0;
//Remove the mobile forces here, but only if battle is over.
while( pGroup )
{
if( !pGroup->fPlayer && 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;
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->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;
UINT8 ubISNumAdmins, ubISNumTroops, ubISNumElites;
UINT8 ubNumElitesInGroup, ubNumTroopsInGroup, ubNumAdminsInGroup;
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;
//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_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;
if( !ubNumElites && !ubNumTroops && !ubNumAdmins )
{ //All troops accounted for.
return;
}
//Now assign the rest of the soldiers to groups
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && pGroup->ubSectorX == gpAR->ubSectorX && pGroup->ubSectorY == gpAR->ubSectorY )
{
ubNumElitesInGroup = pGroup->pEnemyGroup->ubNumElites;
ubNumTroopsInGroup = pGroup->pEnemyGroup->ubNumTroops;
ubNumAdminsInGroup = pGroup->pEnemyGroup->ubNumAdmins;
for( i = 0; i < gpAR->ubEnemies; i++ )
{
if( !(gpEnemies[ i ].uiFlags & CELL_ASSIGNED) )
{
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->fPlayer && IsGroupInARightSectorToReinforce( pGroup, gpAR->ubSectorX, gpAR->ubSectorY ) )
{
ubNumElitesInGroup = pGroup->pEnemyGroup->ubNumElites;
ubNumTroopsInGroup = pGroup->pEnemyGroup->ubNumTroops;
ubNumAdminsInGroup = pGroup->pEnemyGroup->ubNumAdmins;
for( i = 0; i < gpAR->ubEnemies; i++ )
{
if( !(gpEnemies[ i ].uiFlags & CELL_ASSIGNED) )
{
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;
for( i = 0; i < gpAR->ubEnemies; i++ )
{
if( ubISNumAdmins + ubISNumTroops + ubISNumElites <= gubReinforcementMinEnemyStaticGroupSize ) break;
if( !(gpEnemies[ i ].uiFlags & CELL_ASSIGNED) )
{
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->ubElites )
gpEnemies[ index ].uiFlags = CELL_ELITE;
else if( index < 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, HUMAN_SKULL, pCell->xp+3+x, pCell->yp+3, VO_BLT_SRCTRANSPARENCY, NULL );
else
BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, CREATURE_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 ) ) );
//pink one for bandaged.
iStartY += 25*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 ) ) );
//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 ) ) );
//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 ) ) / 10; // general "handiness"
}
else
{
uiDressSkill = ( ( 3 * EffectiveMedical( pSoldier ) ) + // medical knowledge
( 2 * sStatus) + // state of medical kit
(10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience
EffectiveDexterity( pSoldier ) ) / 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 ) / 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;
// 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);
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;
// 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);
if (pVictim->bBleeding >= (iLifeReturned/100))
{
pVictim->bBleeding -= (iLifeReturned/100);
uiMedcost += (iLifeReturned / 200); // add medkit points cost for unbandaged part
}
else
{
pVictim->bBleeding = 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->iHealableInjury = 0;
pVictim->bBleeding = 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;
}
// 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;
}
else // bandage what we can
{
pVictim->bBleeding -= bPtsLeft;
bPtsLeft = 0;
}
}
// if there are any ptsLeft now, then we didn't actually get to use them
uiActual = max(0, (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 );
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 );
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;
}
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 );
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 );
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 );
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 ] );
}
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 = 255;
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;
}
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 );
// ARRemoveMilitiaMan( gpAR->ubSectorX, gpAR->ubSectorY, ubCurrentRank );
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++;
}
else if( gpCivs[ i ].pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA )
{
gbGreenToRegPromotions++;
gbMilitiaPromotions++;
}
else if( gpCivs[ i ].pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA )
{
gbRegToElitePromotions++;
gbMilitiaPromotions++;
}
}
}
}
TacticalRemoveSoldierPointer( gpCivs[ i ].pSoldier, FALSE );
memset( &gpCivs[ i ], 0, sizeof( SOLDIERCELL ) );
}
}
//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.
ResetMovementForEnemyGroupsInLocation( 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;
}
}
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->ubEnemies = (UINT8)min( gpAR->ubAdmins + gpAR->ubTroops + gpAR->ubElites, 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->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->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 += NumEnemiesInSector( gpAR->ubSectorX, gpAR->ubSectorY );
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 = gGameExternalOptions.ubGameMaximumNumberOfPlayerMercs;
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 );
}
if( i >= CountAllMilitiaInSector( gpAR->ubSectorX, gpAR->ubSectorY ) )
{ //Extra delay if soldier's from reinforcement
pCell->usNextAttack += REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR * CountAllMilitiaInSector( gpAR->ubSectorX, gpAR->ubSectorY );
}
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 >= NumEnemiesInSector( gpAR->ubSectorX, gpAR->ubSectorY ) && !(pCell->uiFlags & CELL_CREATURE) )
{ //Extra delay if it's a reinforcement
pCell->usNextAttack += REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR * NumEnemiesInSector( gpAR->ubSectorX, gpAR->ubSectorY );
}
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;
}
for( UINT32 i = 0; i < pSoldier->inv.size(); 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 AttackerHasKnife( SOLDIERCELL *pAttacker )
{
for( UINT32 i = 0; i < pAttacker->pSoldier->inv.size(); 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;
for( UINT32 i = 0; i < pSoldier->inv.size(); 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;
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;
}
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 )
{
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;
}
}
//Attacker hits
if( !fMelee )
{
//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);
iImpact = BulletImpact( pAttacker->pSoldier, 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
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;
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++;
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
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 )
{ //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;
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 )
{
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 ((NumEnemiesInSector(sX, sY) > 0) &&
(CountAllMilitiaInSector(sX, sY) > 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);
}
}
}
}
}