Files
source/Tactical/Soldier Profile.cpp
T
Overhaul ffc70e9723 Fixed buffer overrun for LUA strings
Made direction variables more consistent as unsigned chars.  Several function prototypes updated for this.
Many memory leaks plugged:  External options, video overlays, laptop file lists, tactical message queue, strategic pathing, autobandage, merc hiring, detailed placements, crate in Drassen, tactical placement
New functions and attributes for soldiers in LUA (still for debugging at best): Soldier.APs (current APs), Soldier.changestance (changes stance, uses game heights)
File catalog ignores .SVN directories (game load speedup)
Fix CTD in mouse regions
JA2 window now refuses to move to negative coords
Checks to prevent DirectX-related infinite loops due to minimizing and task switching
Invading enemies should now appear on the borders even when reinforcements disabled in .ini
Suppression should no longer work on mercs in medium water
Infant/Young creatures use restored spit instead of Molotov
Creatures begin with their 'guns' (spit) 'locked and loaded' (cartridge in chamber)
Further fix to AXP and AlaarDB's weapon ready check:  Now 'firing' is always counted as 'ready'.
Prevent mercs from falling and flying back through obstacles
Check whether battle group is even set before testing its location for battle setup
Burst spread locations now limited to 6, the limit within the soldier struct
Extra burst spread locations zeroed out so that they aren't used unless necessary
Spread code now only shoots at locations in the spread, and will shoot at all 6
Only mercs in the sector where autobandage happens will be made to stand up after it's done
Throwing a grenade at the tail of the plane in Drassen should not result in a CTD
Reset attack busy count when loading a new sector


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1347 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2007-09-11 11:19:03 +00:00

1558 lines
43 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include <stdio.h>
#include <string.h>
#include "wcheck.h"
#include "stdlib.h"
#include "debug.h"
#include "math.h"
#include "worlddef.h"
#include "worldman.h"
#include "renderworld.h"
#include "Soldier Control.h"
#include "Animation Control.h"
#include "Animation Data.h"
#include "Isometric Utils.h"
#include "Event Pump.h"
#include "Timer Control.h"
#include "Render Fun.h"
#include "Render Dirty.h"
#include "mousesystem.h"
#include "interface.h"
#include "sysutil.h"
#include "FileMan.h"
#include "points.h"
#include "Random.h"
#include "ai.h"
#include "Interactive Tiles.h"
#include "soldier ani.h"
#include "english.h"
#include "overhead.h"
#include "Soldier Profile.h"
#include "Game Clock.h"
#include "assignments.h"
#include "Dialogue Control.h"
#include "soldier create.h"
#include "soldier add.h"
#include "opplist.h"
#include "weapons.h"
#include "Strategic Town Loyalty.h"
#include "squads.h"
#include "Tactical Save.h"
#include "Quests.h"
#include "aim.h"
#include "Interface Dialogue.h"
#include "GameSettings.h"
#include "strategic town reputation.h"
#include "interface utils.h"
#include "Game Event Hook.h"
#include "Map Information.h"
#include "history.h"
#include "personnel.h"
#include "environment.h"
#include "Player Command.h"
#endif
#ifdef JA2EDITOR
extern BOOLEAN gfProfileDataLoaded;
#endif
BOOLEAN gfPotentialTeamChangeDuringDeath = FALSE;
#define MIN_BLINK_FREQ 3000
#define MIN_EXPRESSION_FREQ 2000
#define SET_PROFILE_GAINS2 500, 500, 500, 500, 500, 500, 500, 500, 500
MERCPROFILESTRUCT gMercProfiles[ NUM_PROFILES ];
INT8 gbSkillTraitBonus[NUM_SKILLTRAITS] =
{
0, //NO_SKILLTRAIT
25, //LOCKPICKING
15, //HANDTOHAND
15, //ELECTRONICS
15, //NIGHTOPS
12, //THROWING
15, //TEACHING
15, //HEAVY_WEAPS
0, //AUTO_WEAPS
15, //STEALTHY
0, //AMBIDEXT
0, //THIEF // UNUSED!
30, //MARTIALARTS
30, //KNIFING
15, //ONROOF
0, //CAMOUFLAGED
0, //CAMOUFLAGED_URBAN
0, //CAMOUFLAGED_DESERT
0, //CAMOUFLAGED_SNOW
};
UINT8 gubBasicInventoryPositions[] = {
HELMETPOS,
VESTPOS,
LEGPOS,
HANDPOS,
BIGPOCK1POS,
BIGPOCK2POS,
BIGPOCK3POS,
BIGPOCK4POS
};
#define NUM_TERRORISTS 6
UINT8 gubTerrorists[NUM_TERRORISTS + 1] =
{
DRUGGIST,
SLAY,
ANNIE,
CHRIS,
TIFFANY,
T_REX,
0
};
UINT8 gubNumTerrorists = 0;
#define NUM_TERRORIST_POSSIBLE_LOCATIONS 5
INT16 gsTerroristSector[NUM_TERRORISTS][NUM_TERRORIST_POSSIBLE_LOCATIONS][2] =
{
// Elgin... preplaced
{
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 }
},
// Slay
{
{ 9, MAP_ROW_F },
{ 14, MAP_ROW_I },
{ 1, MAP_ROW_G },
{ 2, MAP_ROW_G },
{ 8, MAP_ROW_G }
},
// Matron
{
{ 14, MAP_ROW_I },
{ 6, MAP_ROW_C },
{ 2, MAP_ROW_B },
{ 11, MAP_ROW_L },
{ 8, MAP_ROW_G }
},
// Imposter
{
{ 1, MAP_ROW_G },
{ 9, MAP_ROW_F },
{ 11, MAP_ROW_L },
{ 8, MAP_ROW_G },
{ 2, MAP_ROW_G }
},
// Tiffany
{
{ 14, MAP_ROW_I },
{ 2, MAP_ROW_G },
{ 14, MAP_ROW_H },
{ 6, MAP_ROW_C },
{ 2, MAP_ROW_B }
},
// Rexall
{
{ 9, MAP_ROW_F },
{ 14, MAP_ROW_H },
{ 2, MAP_ROW_H },
{ 1, MAP_ROW_G },
{ 2, MAP_ROW_B }
}
};
INT16 gsRobotGridNo;
#define NUM_ASSASSINS 6
UINT8 gubAssassins[NUM_ASSASSINS] =
{
JIM,
JACK,
OLAF,
RAY,
OLGA,
TYRONE
};
#define NUM_ASSASSIN_POSSIBLE_TOWNS 5
INT8 gbAssassinTown[NUM_ASSASSINS][NUM_ASSASSIN_POSSIBLE_TOWNS] =
{
// Jim
{ CAMBRIA, DRASSEN, ALMA, BALIME, GRUMM },
// Jack
{ CHITZENA, ESTONI, ALMA, BALIME, GRUMM },
// Olaf
{ DRASSEN, ESTONI, ALMA, CAMBRIA, BALIME },
// Ray
{ CAMBRIA, OMERTA, BALIME, GRUMM, DRASSEN },
// Olga
{ CHITZENA, OMERTA, CAMBRIA, ALMA, GRUMM },
// Tyrone
{ CAMBRIA, BALIME, ALMA, GRUMM, DRASSEN },
};
UINT16 CalcCompetence( MERCPROFILESTRUCT * pProfile );
INT16 CalcMedicalDeposit( MERCPROFILESTRUCT * pProfile );
extern void HandleEndDemoInCreatureLevel( );
void DecideActiveTerrorists( void );
extern SOLDIERTYPE *gpSMCurrentMerc;
extern BOOLEAN gfRerenderInterfaceFromHelpText;
BOOLEAN LoadMercProfiles(void)
{
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"LoadMercProfiles");
// FILE *fptr;
HWFILE fptr;
STR8 pFileName = "BINARYDATA\\Prof.dat";
STR8 pFileName1_Normal = "BINARYDATA\\Prof_Novice_NormalGuns.dat";
STR8 pFileName2_Normal = "BINARYDATA\\Prof_Experienced_NormalGuns.dat";
STR8 pFileName3_Normal = "BINARYDATA\\Prof_Expert_NormalGuns.dat";
STR8 pFileName4_Normal = "BINARYDATA\\Prof_Insane_NormalGuns.dat";
STR8 pFileName1_Tons = "BINARYDATA\\Prof_Novice_TonsOfGuns.dat";
STR8 pFileName2_Tons = "BINARYDATA\\Prof_Experienced_TonsOfGuns.dat";
STR8 pFileName3_Tons = "BINARYDATA\\Prof_Expert_TonsOfGuns.dat";
STR8 pFileName4_Tons = "BINARYDATA\\Prof_Insane_TonsOfGuns.dat";
UINT32 uiLoop, uiLoop2;//, uiLoop3;
UINT16 usItem;//, usNewGun, usAmmo, usNewAmmo;
UINT32 uiNumBytesRead;
switch ( gGameOptions.ubDifficultyLevel)
{
case DIF_LEVEL_EASY:
if ( gGameOptions.fGunNut )
fptr = FileOpen(pFileName1_Tons, FILE_ACCESS_READ, FALSE );
else
fptr = FileOpen(pFileName1_Normal, FILE_ACCESS_READ, FALSE );
break;
case DIF_LEVEL_MEDIUM:
if ( gGameOptions.fGunNut )
fptr = FileOpen(pFileName2_Tons, FILE_ACCESS_READ, FALSE );
else
fptr = FileOpen(pFileName2_Normal, FILE_ACCESS_READ, FALSE );
break;
case DIF_LEVEL_HARD:
if ( gGameOptions.fGunNut )
fptr = FileOpen(pFileName3_Tons, FILE_ACCESS_READ, FALSE );
else
fptr = FileOpen(pFileName3_Normal, FILE_ACCESS_READ, FALSE );
break;
case DIF_LEVEL_INSANE:
if ( gGameOptions.fGunNut )
fptr = FileOpen(pFileName4_Tons, FILE_ACCESS_READ, FALSE );
else
fptr = FileOpen(pFileName4_Normal, FILE_ACCESS_READ, FALSE );
break;
default:
fptr = FileOpen(pFileName, FILE_ACCESS_READ, FALSE );
break;
}
if( !fptr )
{
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("FAILED to LoadMercProfiles from file %s", pFileName) );
return(FALSE);
}
for(uiLoop=0; uiLoop< NUM_PROFILES; uiLoop++)
{
// WDS - Clean up inventory handling
if( JA2EncryptedFileRead( fptr, &gMercProfiles[uiLoop], SIZEOF_MERCPROFILESTRUCT_POD, &uiNumBytesRead ) != 1)
{
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("FAILED to Read Merc Profiles from File %d %s",uiLoop, pFileName) );
FileClose( fptr );
return(FALSE);
}
gMercProfiles[ uiLoop ].CopyOldInventoryToNew();
//if the Dialogue exists for the merc, allow the merc to be hired
if( DialogueDataFileExistsForProfile( (UINT8)uiLoop, 0, FALSE, NULL ) )
{
gMercProfiles[uiLoop].bMercStatus = 0;
}
else
gMercProfiles[uiLoop].bMercStatus = MERC_HAS_NO_TEXT_FILE;
// if the merc has a medical deposit
if( gMercProfiles[ uiLoop ].bMedicalDeposit )
{
gMercProfiles[uiLoop].sMedicalDepositAmount = CalcMedicalDeposit( &gMercProfiles[uiLoop]);
}
else
gMercProfiles[uiLoop].sMedicalDepositAmount = 0;
// ATE: New, face display indipendent of ID num now
// Setup face index value
// Default is the ubCharNum
gMercProfiles[uiLoop].ubFaceIndex = (UINT8)uiLoop;
/*
if ( !gGameOptions.fGunNut )
{
// CJC: replace guns in profile if they aren't available
for ( uiLoop2 = 0; uiLoop2 < NUM_INV_SLOTS; uiLoop2++ )
{
usItem = gMercProfiles[uiLoop].inv[ uiLoop2 ];
if ( ( Item[ usItem ].usItemClass & IC_GUN ) && ExtendedGunListGun( usItem ) )
{
usNewGun = StandardGunListReplacement( usItem );
if ( usNewGun != NOTHING )
{
gMercProfiles[uiLoop].inv[ uiLoop2 ] = usNewGun;
// must search through inventory and replace ammo accordingly
for ( uiLoop3 = 0; uiLoop3 < NUM_INV_SLOTS; uiLoop3++ )
{
usAmmo = gMercProfiles[ uiLoop ].inv[ uiLoop3 ];
if ( (Item[ usAmmo ].usItemClass & IC_AMMO) )
{
usNewAmmo = FindReplacementMagazineIfNecessary( usItem, usAmmo, usNewGun );
if (usNewAmmo != NOTHING )
{
// found a new magazine, replace...
gMercProfiles[ uiLoop ].inv[ uiLoop3 ] = usNewAmmo;
}
}
}
}
}
}
} // end of if not gun nut
*/
//ATE: Calculate some inital attractiveness values for buddy's inital equipment...
// Look for gun and armour
gMercProfiles[uiLoop].bMainGunAttractiveness = -1;
gMercProfiles[uiLoop].bArmourAttractiveness = -1;
for ( uiLoop2 = 0; uiLoop2 < NUM_INV_SLOTS; uiLoop2++ )
{
usItem = gMercProfiles[uiLoop].inv[ uiLoop2 ];
if ( usItem != NOTHING )
{
// Check if it's a gun
if ( Item[ usItem ].usItemClass & IC_GUN )
{
gMercProfiles[uiLoop].bMainGunAttractiveness = Weapon[ usItem ].ubDeadliness;
}
// If it's armour
if ( Item[ usItem ].usItemClass & IC_ARMOUR )
{
gMercProfiles[uiLoop].bArmourAttractiveness = min(128,Armour[ Item[ usItem ].ubClassIndex ].ubProtection);
}
}
}
// OK, if we are a created slot, this will get overriden at some time..
//add up the items the merc has for the usOptionalGearCost
gMercProfiles[ uiLoop ].usOptionalGearCost = 0;
for ( uiLoop2 = 0; uiLoop2< NUM_INV_SLOTS; uiLoop2++ )
{
if ( gMercProfiles[ uiLoop ].inv[ uiLoop2 ] != NOTHING )
{
//get the item
usItem = gMercProfiles[ uiLoop ].inv[ uiLoop2 ];
//add the cost
gMercProfiles[ uiLoop ].usOptionalGearCost += Item[ usItem ].usPrice;
}
}
//These variables to get loaded in
gMercProfiles[ uiLoop ].fUseProfileInsertionInfo = FALSE;
gMercProfiles[ uiLoop ].sGridNo = 0;
// ARM: this is also being done inside the profile editor, but put it here too, so this project's code makes sense
gMercProfiles[ uiLoop ].bHatedCount[0] = gMercProfiles[ uiLoop ].bHatedTime[0];
gMercProfiles[ uiLoop ].bHatedCount[1] = gMercProfiles[ uiLoop ].bHatedTime[1];
gMercProfiles[ uiLoop ].bLearnToHateCount = gMercProfiles[ uiLoop ].bLearnToHateTime;
gMercProfiles[ uiLoop ].bLearnToLikeCount = gMercProfiles[ uiLoop ].bLearnToLikeTime;
}
// SET SOME DEFAULT LOCATIONS FOR STARTING NPCS
FileClose( fptr );
// decide which terrorists are active
DecideActiveTerrorists();
// initialize mercs' status
StartSomeMercsOnAssignment( );
// initial recruitable mercs' reputation in each town
InitializeProfilesForTownReputation( );
#ifdef JA2EDITOR
gfProfileDataLoaded = TRUE;
#endif
// no better place..heh?.. will load faces for profiles that are 'extern'.....won't have soldiertype instances
InitalizeStaticExternalNPCFaces( );
// car portrait values
LoadCarPortraitValues( );
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"LoadMercProfiles done");
return(TRUE);
}
#define MAX_ADDITIONAL_TERRORISTS 4
void DecideActiveTerrorists( void )
{
UINT8 ubLoop, ubLoop2;
UINT8 ubTerrorist;
UINT8 ubNumAdditionalTerrorists, ubNumTerroristsAdded = 0;
UINT32 uiChance, uiLocationChoice;
BOOLEAN fFoundSpot;
INT16 sTerroristPlacement[MAX_ADDITIONAL_TERRORISTS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
#ifdef CRIPPLED_VERSION
return;
#endif
// one terrorist will always be Elgin
// determine how many more terrorists - 2 to 4 more
// using this stochastic process(!), the chances for terrorists are:
// EASY: 3, 9% 4, 42% 5, 49%
// MEDIUM: 3, 25% 4, 50% 5, 25%
// HARD: 3, 49% 4, 42% 5, 9%
switch( gGameOptions.ubDifficultyLevel )
{
case DIF_LEVEL_EASY:
uiChance = 70;
break;
case DIF_LEVEL_INSANE:
uiChance = 30;
break;
default:
uiChance = 50;
break;
}
if ( gGameExternalOptions.fEnableAllTerrorists )
uiChance = 100;
// add at least 2 more
ubNumAdditionalTerrorists = 2;
for (ubLoop = 0; ubLoop < (MAX_ADDITIONAL_TERRORISTS - 2); ubLoop++)
{
if (Random( 100 ) < uiChance)
{
ubNumAdditionalTerrorists++;
}
}
// ifdefs added by CJC
#ifdef JA2TESTVERSION
ubNumAdditionalTerrorists = 4;
#endif
while ( ubNumTerroristsAdded < ubNumAdditionalTerrorists )
{
ubLoop = 1; // start at beginning of array (well, after Elgin)
// NB terrorist ID of 0 indicates end of array
while ( ubNumTerroristsAdded < ubNumAdditionalTerrorists && gubTerrorists[ ubLoop ] != 0 )
{
ubTerrorist = gubTerrorists[ ubLoop ];
// random 40% chance of adding this terrorist if not yet placed
if ( ( gMercProfiles[ ubTerrorist ].sSectorX == 0 ) && ( Random( 100 ) < 40 ) )
{
fFoundSpot = FALSE;
// Since there are 5 spots per terrorist and a maximum of 5 terrorists, we
// are guaranteed to be able to find a spot for each terrorist since there
// aren't enough other terrorists to use up all the spots for any one
// terrorist
do
{
// pick a random spot, see if it's already been used by another terrorist
uiLocationChoice = Random( NUM_TERRORIST_POSSIBLE_LOCATIONS );
for (ubLoop2 = 0; ubLoop2 < ubNumTerroristsAdded; ubLoop2++)
{
if (sTerroristPlacement[ubLoop2][0] == gsTerroristSector[ubLoop][uiLocationChoice][0] )
{
if (sTerroristPlacement[ubLoop2][1] == gsTerroristSector[ubLoop][uiLocationChoice][1] )
{
continue;
}
}
}
fFoundSpot = TRUE;
} while( !fFoundSpot );
// place terrorist!
gMercProfiles[ ubTerrorist ].sSectorX = gsTerroristSector[ ubLoop ][ uiLocationChoice ][ 0 ];
gMercProfiles[ ubTerrorist ].sSectorY = gsTerroristSector[ ubLoop ][ uiLocationChoice ][ 1 ];
gMercProfiles[ ubTerrorist ].bSectorZ = 0;
sTerroristPlacement[ ubNumTerroristsAdded ][ 0 ] = gMercProfiles[ ubTerrorist ].sSectorX;
sTerroristPlacement[ ubNumTerroristsAdded ][ 1 ] = gMercProfiles[ ubTerrorist ].sSectorY;
ubNumTerroristsAdded++;
}
ubLoop++;
}
// start over if necessary
}
// set total terrorists outstanding in Carmen's info byte
gMercProfiles[ 78 ].bNPCData = 1 + ubNumAdditionalTerrorists;
// store total terrorists
gubNumTerrorists = 1 + ubNumAdditionalTerrorists;
}
void MakeRemainingTerroristsTougher( void )
{
UINT8 ubRemainingTerrorists = 0, ubLoop;
UINT16 usNewItem, usOldItem;
OBJECTTYPE Object;
UINT8 ubRemainingDifficulty;
for ( ubLoop = 0; ubLoop < NUM_TERRORISTS; ubLoop++ )
{
if ( gMercProfiles[ gubTerrorists[ ubLoop ] ].bMercStatus != MERC_IS_DEAD && gMercProfiles[ gubTerrorists[ ubLoop ] ].sSectorX != 0 && gMercProfiles[ gubTerrorists[ ubLoop ] ].sSectorY != 0 )
{
if ( gubTerrorists[ ubLoop ] == SLAY )
{
if ( FindSoldierByProfileID( SLAY, TRUE ) != NULL )
{
// Slay on player's team, doesn't count towards remaining terrorists
continue;
}
}
ubRemainingTerrorists++;
}
}
ubRemainingDifficulty = (60 / gubNumTerrorists) * (gubNumTerrorists - ubRemainingTerrorists);
switch( gGameOptions.ubDifficultyLevel )
{
case DIF_LEVEL_MEDIUM:
ubRemainingDifficulty = (ubRemainingDifficulty * 13) / 10;
break;
case DIF_LEVEL_HARD:
ubRemainingDifficulty = (ubRemainingDifficulty * 16) / 10;
break;
case DIF_LEVEL_INSANE:
ubRemainingDifficulty = (ubRemainingDifficulty * 19) / 10;
break;
default:
break;
}
if ( ubRemainingDifficulty < 14 )
{
// nothing
return;
}
else if ( ubRemainingDifficulty < 28 )
{
// mini grenade
usOldItem = NOTHING;
usNewItem = MINI_GRENADE;
}
else if ( ubRemainingDifficulty < 42)
{
// hand grenade
usOldItem = MINI_GRENADE;
usNewItem = HAND_GRENADE;
}
else if ( ubRemainingDifficulty < 56)
{
// mustard
usOldItem = HAND_GRENADE;
usNewItem = MUSTARD_GRENADE;
}
else if ( ubRemainingDifficulty < 70)
{
// LAW
usOldItem = MUSTARD_GRENADE;
usNewItem = ROCKET_LAUNCHER;
}
else
{
// LAW and hand grenade
usOldItem = NOTHING;
usNewItem = HAND_GRENADE;
}
DeleteObj( &Object );
Object.usItem = usNewItem;
Object.ItemData.Generic.bStatus[ 0 ] = 100;
for ( ubLoop = 0; ubLoop < NUM_TERRORISTS; ubLoop++ )
{
if ( gMercProfiles[ gubTerrorists[ ubLoop ] ].bMercStatus != MERC_IS_DEAD && gMercProfiles[ gubTerrorists[ ubLoop ] ].sSectorX != 0 && gMercProfiles[ gubTerrorists[ ubLoop ] ].sSectorY != 0 )
{
if ( gubTerrorists[ ubLoop ] == SLAY )
{
if ( FindSoldierByProfileID( SLAY, TRUE ) != NULL )
{
// Slay on player's team, doesn't count towards remaining terrorists
continue;
}
}
if ( usOldItem != NOTHING )
{
RemoveObjectFromSoldierProfile( gubTerrorists[ ubLoop ], usOldItem );
}
PlaceObjectInSoldierProfile( gubTerrorists[ ubLoop ], &Object );
}
}
}
void DecideOnAssassin( void )
{
UINT8 ubAssassinPossibility[NUM_ASSASSINS] = { NO_PROFILE, NO_PROFILE, NO_PROFILE, NO_PROFILE, NO_PROFILE, NO_PROFILE };
UINT8 ubAssassinsPossible = 0;
UINT8 ubLoop, ubLoop2;
UINT8 ubTown;
#ifdef CRIPPLED_VERSION
return;
#endif
ubTown = GetTownIdForSector( gWorldSectorX, gWorldSectorY );
for ( ubLoop = 0; ubLoop < NUM_ASSASSINS; ubLoop++ )
{
// make sure alive and not placed already
if ( gMercProfiles[ gubAssassins[ ubLoop ] ].bMercStatus != MERC_IS_DEAD && gMercProfiles[ gubAssassins[ ubLoop ] ].sSectorX == 0 && gMercProfiles[ gubAssassins[ ubLoop ] ].sSectorY == 0 )
{
// check this merc to see if the town is a possibility
for ( ubLoop2 = 0; ubLoop2 < NUM_ASSASSIN_POSSIBLE_TOWNS; ubLoop2++ )
{
if ( gbAssassinTown[ ubLoop ][ ubLoop2 ] == ubTown )
{
ubAssassinPossibility[ ubAssassinsPossible ] = gubAssassins[ ubLoop ];
ubAssassinsPossible++;
}
}
}
}
if ( ubAssassinsPossible != 0 )
{
ubLoop = ubAssassinPossibility[ Random( ubAssassinsPossible ) ];
gMercProfiles[ ubLoop ].sSectorX = gWorldSectorX;
gMercProfiles[ ubLoop ].sSectorY = gWorldSectorY;
AddStrategicEvent( EVENT_REMOVE_ASSASSIN, GetWorldTotalMin() + 60 * ( 3 + Random( 3 ) ), ubLoop );
}
}
void MakeRemainingAssassinsTougher( void )
{
UINT8 ubRemainingAssassins = 0, ubLoop;
UINT16 usNewItem, usOldItem;
OBJECTTYPE Object;
UINT8 ubRemainingDifficulty;
for ( ubLoop = 0; ubLoop < NUM_ASSASSINS; ubLoop++ )
{
if ( gMercProfiles[ gubAssassins[ ubLoop ] ].bMercStatus != MERC_IS_DEAD )
{
ubRemainingAssassins++;
}
}
ubRemainingDifficulty = (60 / NUM_ASSASSINS) * (NUM_ASSASSINS - ubRemainingAssassins);
switch( gGameOptions.ubDifficultyLevel )
{
case DIF_LEVEL_MEDIUM:
ubRemainingDifficulty = (ubRemainingDifficulty * 13) / 10;
break;
case DIF_LEVEL_HARD:
ubRemainingDifficulty = (ubRemainingDifficulty * 16) / 10;
break;
case DIF_LEVEL_INSANE:
ubRemainingDifficulty = (ubRemainingDifficulty * 19) / 10;
break;
default:
break;
}
if ( ubRemainingDifficulty < 14 )
{
// nothing
return;
}
else if ( ubRemainingDifficulty < 28 )
{
// mini grenade
usOldItem = NOTHING;
usNewItem = MINI_GRENADE;
}
else if ( ubRemainingDifficulty < 42)
{
// hand grenade
usOldItem = MINI_GRENADE;
usNewItem = HAND_GRENADE;
}
else if ( ubRemainingDifficulty < 56)
{
// mustard
usOldItem = HAND_GRENADE;
usNewItem = MUSTARD_GRENADE;
}
else if ( ubRemainingDifficulty < 70)
{
// LAW
usOldItem = MUSTARD_GRENADE;
usNewItem = ROCKET_LAUNCHER;
}
else
{
// LAW and hand grenade
usOldItem = NOTHING;
usNewItem = HAND_GRENADE;
}
DeleteObj( &Object );
Object.usItem = usNewItem;
Object.ItemData.Generic.bStatus[ 0 ] = 100;
for ( ubLoop = 0; ubLoop < NUM_ASSASSINS; ubLoop++ )
{
if ( gMercProfiles[ gubAssassins[ ubLoop ] ].bMercStatus != MERC_IS_DEAD )
{
if ( usOldItem != NOTHING )
{
RemoveObjectFromSoldierProfile( gubAssassins[ ubLoop ], usOldItem );
}
PlaceObjectInSoldierProfile( gubAssassins[ ubLoop ], &Object );
}
}
}
void StartSomeMercsOnAssignment(void)
{
UINT32 uiCnt;
MERCPROFILESTRUCT *pProfile;
UINT32 uiChance;
// some randomly picked A.I.M. mercs will start off "on assignment" at the beginning of each new game
for( uiCnt = 0; uiCnt < AIM_AND_MERC_MERCS; uiCnt++)
{
if( !IsProfileIdAnAimOrMERCMerc( (UINT8)uiCnt ) )
{
continue;
}
pProfile = &(gMercProfiles[ uiCnt ]);
// calc chance to start on assignment
uiChance = 5 * pProfile->bExpLevel;
if (Random(100) < uiChance)
{
pProfile->bMercStatus = MERC_WORKING_ELSEWHERE;
pProfile->uiDayBecomesAvailable = 1 + Random(6 + (pProfile->bExpLevel / 2) ); // 1-(6 to 11) days
}
else
{
pProfile->bMercStatus = MERC_OK;
pProfile->uiDayBecomesAvailable = 0;
}
pProfile->uiPrecedentQuoteSaid = 0;
pProfile->ubDaysOfMoraleHangover = 0;
}
}
void SetProfileFaceData( UINT8 ubCharNum, UINT8 ubFaceIndex, UINT16 usEyesX, UINT16 usEyesY, UINT16 usMouthX, UINT16 usMouthY )
{
gMercProfiles[ ubCharNum ].ubFaceIndex = ubFaceIndex;
gMercProfiles[ ubCharNum ].usEyesX = usEyesX;
gMercProfiles[ ubCharNum ].usEyesY = usEyesY;
gMercProfiles[ ubCharNum ].usMouthX = usMouthX;
gMercProfiles[ ubCharNum ].usMouthY = usMouthY;
}
UINT16 CalcCompetence( MERCPROFILESTRUCT * pProfile )
{
UINT32 uiStats, uiSkills, uiActionPoints, uiSpecialSkills;
UINT16 usCompetence;
// count life twice 'cause it's also hit points
// mental skills are halved 'cause they're actually not that important within the game
uiStats = ((2 * pProfile->bLifeMax) + pProfile->bStrength + pProfile->bAgility + pProfile->bDexterity + ((pProfile->bLeadership + pProfile->bWisdom) / 2)) / 3;
// marksmanship is very important, count it double
uiSkills = (UINT32) ((2 * (pow((double)pProfile->bMarksmanship, 3) / 10000)) +
1.5 * (pow((double)pProfile->bMedical, 3) / 10000) +
(pow((double)pProfile->bMechanical, 3) / 10000) +
(pow((double)pProfile->bExplosive, 3) / 10000));
// action points
uiActionPoints = 5 + (((10 * pProfile->bExpLevel +
3 * pProfile->bAgility +
2 * pProfile->bLifeMax +
2 * pProfile->bDexterity) + 20) / 40);
// count how many he has, don't care what they are
uiSpecialSkills = ((pProfile->bSkillTrait != 0) ? 1 : 0) + ((pProfile->bSkillTrait2 != 0) ? 1 : 0);
usCompetence = (UINT16) ((pow((double)pProfile->bExpLevel, 0.2) * uiStats * uiSkills * (uiActionPoints - 6) * (1 + (0.05 * (FLOAT)uiSpecialSkills))) / 1000);
// this currently varies from about 10 (Flo) to 1200 (Gus)
return(usCompetence);
}
INT16 CalcMedicalDeposit( MERCPROFILESTRUCT * pProfile )
{
UINT16 usDeposit;
// this rounds off to the nearest hundred
usDeposit = (((5 * CalcCompetence(pProfile)) + 50) / 100) * 100;
return(usDeposit);
}
SOLDIERTYPE * FindSoldierByProfileID( UINT8 ubProfileID, BOOLEAN fPlayerMercsOnly )
{
UINT8 ubLoop, ubLoopLimit;
SOLDIERTYPE * pSoldier;
if (fPlayerMercsOnly)
{
ubLoopLimit = gTacticalStatus.Team[0].bLastID;
}
else
{
ubLoopLimit = MAX_NUM_SOLDIERS;
}
for (ubLoop = 0, pSoldier = MercPtrs[0]; ubLoop < ubLoopLimit; ubLoop++, pSoldier++)
{
if (pSoldier->bActive && pSoldier->ubProfile == ubProfileID)
{
return( pSoldier );
}
}
return( NULL );
}
SOLDIERTYPE *ChangeSoldierTeam( SOLDIERTYPE *pSoldier, UINT8 ubTeam )
{
UINT8 ubID;
SOLDIERTYPE *pNewSoldier = NULL;
SOLDIERCREATE_STRUCT MercCreateStruct;
UINT32 cnt;
INT16 sOldGridNo;
UINT8 ubOldID;
UINT32 uiOldUniqueId;
UINT32 uiSlot;
SOLDIERTYPE *pGroupMember;
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ChangeSoldierTeam"));
if (gfInTalkPanel)
{
DeleteTalkingMenu();
}
// Save merc id for this guy...
ubID = pSoldier->ubID;
ubOldID = ubID;
uiOldUniqueId = pSoldier->uiUniqueSoldierIdValue;
sOldGridNo = pSoldier->sGridNo;
// Remove him from the game!
InternalTacticalRemoveSoldier( ubID, FALSE );
// WDS - Clean up inventory handling
// Create a new one!
MercCreateStruct.initialize();
MercCreateStruct.bTeam = ubTeam;
MercCreateStruct.ubProfile = pSoldier->ubProfile;
MercCreateStruct.bBodyType = pSoldier->ubBodyType;
MercCreateStruct.sSectorX = pSoldier->sSectorX;
MercCreateStruct.sSectorY = pSoldier->sSectorY;
MercCreateStruct.bSectorZ = pSoldier->bSectorZ;
MercCreateStruct.sInsertionGridNo = pSoldier->sGridNo;
MercCreateStruct.ubDirection = pSoldier->ubDirection;
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
{
MercCreateStruct.ubProfile = NO_PROFILE;
MercCreateStruct.fUseGivenVehicle = TRUE;
MercCreateStruct.bUseGivenVehicleID = pSoldier->bVehicleID;
}
if ( ubTeam == gbPlayerNum )
{
MercCreateStruct.fPlayerMerc = TRUE;
}
if ( TacticalCreateSoldier( &MercCreateStruct, &ubID ) )
{
pNewSoldier = MercPtrs[ ubID ];
// Copy vital stats back!
pNewSoldier->bLife = pSoldier->bLife;
pNewSoldier->bLifeMax = pSoldier->bLifeMax;
pNewSoldier->bAgility = pSoldier->bAgility;
pNewSoldier->bLeadership = pSoldier->bLeadership;
pNewSoldier->bDexterity = pSoldier->bDexterity;
pNewSoldier->bStrength = pSoldier->bStrength;
pNewSoldier->bWisdom = pSoldier->bWisdom;
pNewSoldier->bExpLevel = pSoldier->bExpLevel;
pNewSoldier->bMarksmanship = pSoldier->bMarksmanship;
pNewSoldier->bMedical = pSoldier->bMedical;
pNewSoldier->bMechanical = pSoldier->bMechanical;
pNewSoldier->bExplosive = pSoldier->bExplosive;
pNewSoldier->bScientific = pSoldier->bScientific;
pNewSoldier->bLastRenderVisibleValue = pSoldier->bLastRenderVisibleValue;
pNewSoldier->bVisible = pSoldier->bVisible;
if ( ubTeam == gbPlayerNum )
{
pNewSoldier->bVisible = 1;
}
// Copy over any items....
for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
{
pNewSoldier->inv[ cnt ] = pSoldier->inv[ cnt ];
}
// OK, loop through all active merc slots, change
// Change ANY attacker's target if they were once on this guy.....
for ( uiSlot = 0; uiSlot < guiNumMercSlots; uiSlot++ )
{
pGroupMember = MercSlots[ uiSlot ];
if ( pGroupMember != NULL )
{
if ( pGroupMember->ubTargetID == pSoldier->ubID )
{
pGroupMember->ubTargetID = pNewSoldier->ubID;
}
}
}
// Set insertion gridNo
pNewSoldier->sInsertionGridNo = sOldGridNo;
if ( gfPotentialTeamChangeDuringDeath )
{
HandleCheckForDeathCommonCode( pSoldier );
}
if ( gfWorldLoaded && pSoldier->bInSector
//pSoldier->sSectorX == gWorldSectorX && pSoldier->sSectorY == gWorldSectorY && pSoldier->bSectorZ == gbWorldSectorZ
)
{
AddSoldierToSectorNoCalculateDirectionUseAnimation( ubID, pSoldier->usAnimState, pSoldier->usAniCode );
HandleSight(pNewSoldier, SIGHT_LOOK | SIGHT_RADIO);
}
// fix up the event queue...
// ChangeSoldierIDInQueuedEvents( ubOldID, uiOldUniqueId, pNewSoldier->ubID, pNewSoldier->uiUniqueSoldierIdValue );
if ( pNewSoldier->ubProfile != NO_PROFILE )
{
if ( ubTeam == gbPlayerNum )
{
gMercProfiles[ pNewSoldier->ubProfile ].ubMiscFlags |= PROFILE_MISC_FLAG_RECRUITED;
}
else
{
gMercProfiles[ pNewSoldier->ubProfile ].ubMiscFlags &= (~PROFILE_MISC_FLAG_RECRUITED);
}
}
}
// AT the low level check if this poor guy is in inv panel, else
// remove....
if ( gsCurInterfacePanel == SM_PANEL && gpSMCurrentMerc == pSoldier )
{
// Switch....
SetCurrentInterfacePanel( TEAM_PANEL );
}
return( pNewSoldier );
}
BOOLEAN RecruitRPC( UINT8 ubCharNum )
{
SOLDIERTYPE *pSoldier, *pNewSoldier;
// Get soldier pointer
pSoldier = FindSoldierByProfileID( ubCharNum, FALSE );
if (!pSoldier)
{
return( FALSE );
}
// OK, set recruit flag..
gMercProfiles[ ubCharNum ].ubMiscFlags |= PROFILE_MISC_FLAG_RECRUITED;
// Add this guy to our team!
pNewSoldier = ChangeSoldierTeam( pSoldier, gbPlayerNum );
// handle set up any RPC's that will leave us in time
if ( ubCharNum == SLAY )
{
// slay will leave in a week
pNewSoldier->iEndofContractTime = GetWorldTotalMin() + ( 7 * 24 * 60 );
KickOutWheelchair( pNewSoldier );
}
else if ( ubCharNum == DYNAMO && gubQuest[ QUEST_FREE_DYNAMO ] == QUESTINPROGRESS )
{
EndQuest( QUEST_FREE_DYNAMO, pSoldier->sSectorX, pSoldier->sSectorY );
}
// handle town loyalty adjustment
HandleTownLoyaltyForNPCRecruitment( pNewSoldier );
// Try putting them into the current squad
if ( AddCharacterToSquad( pNewSoldier, (INT8)CurrentSquad( ) ) == FALSE )
{
AddCharacterToAnySquad( pNewSoldier );
}
ResetDeadSquadMemberList( pNewSoldier->bAssignment );
DirtyMercPanelInterface( pNewSoldier, DIRTYLEVEL2 );
if ( pNewSoldier->inv[ HANDPOS ].usItem == NOTHING )
{
// empty handed - swap in first available weapon
INT8 bSlot;
bSlot = FindObjClass( pNewSoldier, IC_WEAPON );
if ( bSlot != NO_SLOT )
{
// if ( Item[ pNewSoldier->inv[ bSlot ].usItem ].fFlags & ITEM_TWO_HANDED )
if ( Item[ pNewSoldier->inv[ bSlot ].usItem ].twohanded )
{
if ( bSlot != SECONDHANDPOS && pNewSoldier->inv[ SECONDHANDPOS ].usItem != NOTHING )
{
// need to move second hand item out first
AutoPlaceObject( pNewSoldier, &(pNewSoldier->inv[ SECONDHANDPOS ]), FALSE );
}
}
// swap item to hand
SwapObjs( &(pNewSoldier->inv[ bSlot ]), &(pNewSoldier->inv[ HANDPOS ]) );
}
}
if ( ubCharNum == IRA )
{
// trigger 0th PCscript line
TriggerNPCRecord( IRA, 0 );
}
// Set whatkind of merc am i
pNewSoldier->ubWhatKindOfMercAmI = MERC_TYPE__NPC;
//
//add a history log that tells the user that a npc has joined the team
//
// ( pass in pNewSoldier->sSectorX cause if its invalid, -1, n/a will appear as the sector in the history log )
AddHistoryToPlayersLog( HISTORY_RPC_JOINED_TEAM, pNewSoldier->ubProfile, GetWorldTotalMin(), pNewSoldier->sSectorX, pNewSoldier->sSectorY );
//remove the merc from the Personnel screens departed list ( if they have never been hired before, its ok to call it )
RemoveNewlyHiredMercFromPersonnelDepartedList( pSoldier->ubProfile );
return( TRUE );
}
BOOLEAN RecruitEPC( UINT8 ubCharNum )
{
SOLDIERTYPE *pSoldier, *pNewSoldier;
// Get soldier pointer
pSoldier = FindSoldierByProfileID( ubCharNum, FALSE );
if (!pSoldier)
{
return( FALSE );
}
// OK, set recruit flag..
gMercProfiles[ ubCharNum ].ubMiscFlags |= PROFILE_MISC_FLAG_EPCACTIVE;
gMercProfiles[ ubCharNum ].ubMiscFlags3 &= ~PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE;
// Add this guy to our team!
pNewSoldier = ChangeSoldierTeam( pSoldier, gbPlayerNum );
pNewSoldier->ubWhatKindOfMercAmI = MERC_TYPE__EPC;
// Try putting them into the current squad
if ( AddCharacterToSquad( pNewSoldier, (INT8)CurrentSquad( ) ) == FALSE )
{
AddCharacterToAnySquad( pNewSoldier );
}
ResetDeadSquadMemberList( pNewSoldier->bAssignment );
DirtyMercPanelInterface( pNewSoldier, DIRTYLEVEL2 );
// Make the interface panel dirty..
// This will dirty the panel next frame...
gfRerenderInterfaceFromHelpText = TRUE;
// If we are a robot, look to update controller....
if ( pNewSoldier->uiStatusFlags & SOLDIER_ROBOT )
{
UpdateRobotControllerGivenRobot( pNewSoldier );
}
// Set whatkind of merc am i
pNewSoldier->ubWhatKindOfMercAmI = MERC_TYPE__EPC;
UpdateTeamPanelAssignments( );
return( TRUE );
}
BOOLEAN UnRecruitEPC( UINT8 ubCharNum )
{
SOLDIERTYPE *pSoldier, *pNewSoldier;
// Get soldier pointer
pSoldier = FindSoldierByProfileID( ubCharNum, FALSE );
if (!pSoldier)
{
return( FALSE );
}
if (pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__EPC)
{
return( FALSE );
}
if ( pSoldier->bAssignment < ON_DUTY )
{
ResetDeadSquadMemberList( pSoldier->bAssignment );
}
// Rmeove from squad....
RemoveCharacterFromSquads( pSoldier );
// O< check if this is the only guy in the sector....
if ( gusSelectedSoldier == pSoldier->ubID )
{
gusSelectedSoldier = NOBODY;
}
// OK, UN set recruit flag..
gMercProfiles[ ubCharNum ].ubMiscFlags &= (~PROFILE_MISC_FLAG_EPCACTIVE);
// update sector values to current
// check to see if this person should disappear from the map after this
if ( (ubCharNum == JOHN || ubCharNum == MARY) && pSoldier->sSectorX == 13 && pSoldier->sSectorY == MAP_ROW_B && pSoldier->bSectorZ == 0 )
{
gMercProfiles[ ubCharNum ].sSectorX = 0;
gMercProfiles[ ubCharNum ].sSectorY = 0;
gMercProfiles[ ubCharNum ].bSectorZ = 0;
}
else
{
gMercProfiles[ ubCharNum ].sSectorX = pSoldier->sSectorX;
gMercProfiles[ ubCharNum ].sSectorY = pSoldier->sSectorY;
gMercProfiles[ ubCharNum ].bSectorZ = pSoldier->bSectorZ;
}
// how do we decide whether or not to set this?
gMercProfiles[ ubCharNum ].fUseProfileInsertionInfo = TRUE;
gMercProfiles[ ubCharNum ].ubMiscFlags3 |= PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE;
// Add this guy to CIV team!
pNewSoldier = ChangeSoldierTeam( pSoldier, CIV_TEAM );
UpdateTeamPanelAssignments( );
return( TRUE );
}
INT8 WhichBuddy( UINT8 ubCharNum, UINT8 ubBuddy )
{
MERCPROFILESTRUCT * pProfile;
INT8 bLoop;
pProfile = &( gMercProfiles[ ubCharNum ] );
for (bLoop = 0; bLoop < 3; bLoop++)
{
if ( pProfile->bBuddy[bLoop] == ubBuddy )
{
return( bLoop );
}
}
return( -1 );
}
INT8 WhichHated( UINT8 ubCharNum, UINT8 ubHated )
{
MERCPROFILESTRUCT * pProfile;
INT8 bLoop;
pProfile = &( gMercProfiles[ ubCharNum ] );
for (bLoop = 0; bLoop < 3; bLoop++)
{
if ( pProfile->bHated[bLoop] == ubHated )
{
return( bLoop );
}
}
return( -1 );
}
BOOLEAN IsProfileATerrorist( UINT8 ubProfile )
{
if ( ubProfile == 83 || ubProfile == 111 ||
ubProfile == 64 || ubProfile == 112 ||
ubProfile == 82 || ubProfile == 110 )
{
return( TRUE );
}
else
{
return( FALSE );
}
}
BOOLEAN IsProfileAHeadMiner( UINT8 ubProfile )
{
if ( ubProfile == 106 || ubProfile == 148 ||
ubProfile == 156 || ubProfile == 157 ||
ubProfile == 158 )
{
return( TRUE );
}
else
{
return( FALSE );
}
}
void UpdateSoldierPointerDataIntoProfile( BOOLEAN fPlayerMercs )
{
UINT32 uiCount;
SOLDIERTYPE *pSoldier = NULL;
MERCPROFILESTRUCT * pProfile;
BOOLEAN fDoCopy = FALSE;
for( uiCount=0; uiCount < guiNumMercSlots; uiCount++)
{
pSoldier = MercSlots[ uiCount ];
if ( pSoldier != NULL )
{
if ( pSoldier->ubProfile != NO_PROFILE )
{
fDoCopy = FALSE;
// If we are above player mercs
if ( fPlayerMercs )
{
if ( pSoldier->ubProfile < FIRST_RPC || pSoldier->ubProfile >= GASTON )
{
fDoCopy = TRUE;
}
}
else
{
if ( pSoldier->ubProfile >= FIRST_RPC && pSoldier->ubProfile < GASTON )
{
fDoCopy = TRUE;
}
}
if ( fDoCopy )
{
// get profile...
pProfile = &( gMercProfiles[ pSoldier->ubProfile ] );
// Copy....
pProfile->bLife = pSoldier->bLife;
pProfile->bLifeMax = pSoldier->bLifeMax;
pProfile->bAgility = pSoldier->bAgility;
pProfile->bLeadership = pSoldier->bLeadership;
pProfile->bDexterity = pSoldier->bDexterity;
pProfile->bStrength = pSoldier->bStrength;
pProfile->bWisdom = pSoldier->bWisdom;
pProfile->bExpLevel = pSoldier->bExpLevel;
pProfile->bMarksmanship = pSoldier->bMarksmanship;
pProfile->bMedical = pSoldier->bMedical;
pProfile->bMechanical = pSoldier->bMechanical;
pProfile->bExplosive = pSoldier->bExplosive;
pProfile->bScientific = pSoldier->bScientific;
}
}
}
}
}
BOOLEAN DoesMercHaveABuddyOnTheTeam( UINT8 ubMercID )
{
UINT8 ubCnt;
INT8 bBuddyID;
// loop through the list of people the merc is buddies with
for(ubCnt=0; ubCnt< 3; ubCnt++)
{
//see if the merc has a buddy on the team
bBuddyID = gMercProfiles[ ubMercID ].bBuddy[ubCnt];
//If its not a valid 'buddy'
if( bBuddyID < 0 )
continue;
if( IsMercOnTeam( bBuddyID ) )
{
if( !IsMercDead( bBuddyID ) )
{
return( TRUE );
}
}
}
return( FALSE );
}
BOOLEAN MercIsHot( SOLDIERTYPE * pSoldier )
{
if ( pSoldier->ubProfile != NO_PROFILE && gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait == HEAT_INTOLERANT )
{
if ( SectorTemperature( GetWorldMinutesInDay(), pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) > 0 )
{
return( TRUE );
}
}
return( FALSE );
}
SOLDIERTYPE * SwapLarrysProfiles( SOLDIERTYPE * pSoldier )
{
UINT8 ubSrcProfile;
UINT8 ubDestProfile;
MERCPROFILESTRUCT * pNewProfile;
ubSrcProfile = pSoldier->ubProfile;
if ( ubSrcProfile == LARRY_NORMAL)
{
ubDestProfile = LARRY_DRUNK;
}
else if ( ubSrcProfile == LARRY_DRUNK )
{
ubDestProfile = LARRY_NORMAL;
}
else
{
// I don't think so!
return( pSoldier );
}
pNewProfile = &gMercProfiles[ ubDestProfile ];
pNewProfile->ubMiscFlags2 = gMercProfiles[ ubSrcProfile ].ubMiscFlags2;
pNewProfile->ubMiscFlags = gMercProfiles[ ubSrcProfile ].ubMiscFlags;
pNewProfile->sSectorX = gMercProfiles[ ubSrcProfile ].sSectorX;
pNewProfile->sSectorY = gMercProfiles[ ubSrcProfile ].sSectorY;
pNewProfile->uiDayBecomesAvailable = gMercProfiles[ ubSrcProfile ].uiDayBecomesAvailable;
pNewProfile->usKills = gMercProfiles[ ubSrcProfile ].usKills;
pNewProfile->usAssists = gMercProfiles[ ubSrcProfile ].usAssists;
pNewProfile->usShotsFired = gMercProfiles[ ubSrcProfile ].usShotsFired;
pNewProfile->usShotsHit = gMercProfiles[ ubSrcProfile ].usShotsHit;
pNewProfile->usBattlesFought = gMercProfiles[ ubSrcProfile ].usBattlesFought;
pNewProfile->usTimesWounded = gMercProfiles[ ubSrcProfile ].usTimesWounded;
pNewProfile->usTotalDaysServed = gMercProfiles[ ubSrcProfile ].usTotalDaysServed;
pNewProfile->bResigned = gMercProfiles[ ubSrcProfile ].bResigned;
pNewProfile->bActive = gMercProfiles[ ubSrcProfile ].bActive;
pNewProfile->fUseProfileInsertionInfo = gMercProfiles[ ubSrcProfile ].fUseProfileInsertionInfo;
pNewProfile->sGridNo = gMercProfiles[ ubSrcProfile ].sGridNo;
pNewProfile->ubQuoteActionID = gMercProfiles[ ubSrcProfile ].ubQuoteActionID;
pNewProfile->ubLastQuoteSaid = gMercProfiles[ ubSrcProfile ].ubLastQuoteSaid;
pNewProfile->ubStrategicInsertionCode = gMercProfiles[ ubSrcProfile ].ubStrategicInsertionCode;
pNewProfile->bMercStatus = gMercProfiles[ ubSrcProfile ].bMercStatus;
pNewProfile->bSectorZ = gMercProfiles[ ubSrcProfile ].bSectorZ;
pNewProfile->usStrategicInsertionData = gMercProfiles[ ubSrcProfile ].usStrategicInsertionData;
pNewProfile->sTrueSalary = gMercProfiles[ ubSrcProfile ].sTrueSalary;
pNewProfile->ubMiscFlags3 = gMercProfiles[ ubSrcProfile ].ubMiscFlags3;
pNewProfile->ubDaysOfMoraleHangover = gMercProfiles[ ubSrcProfile ].ubDaysOfMoraleHangover;
pNewProfile->ubNumTimesDrugUseInLifetime = gMercProfiles[ ubSrcProfile ].ubNumTimesDrugUseInLifetime;
pNewProfile->uiPrecedentQuoteSaid = gMercProfiles[ ubSrcProfile ].uiPrecedentQuoteSaid;
pNewProfile->sPreCombatGridNo = gMercProfiles[ ubSrcProfile ].sPreCombatGridNo;
// CJC: this is causing problems so just skip the transfer of exp...
/*
pNewProfile->sLifeGain = gMercProfiles[ ubSrcProfile ].sLifeGain;
pNewProfile->sAgilityGain = gMercProfiles[ ubSrcProfile ].sAgilityGain;
pNewProfile->sDexterityGain = gMercProfiles[ ubSrcProfile ].sDexterityGain;
pNewProfile->sStrengthGain = gMercProfiles[ ubSrcProfile ].sStrengthGain;
pNewProfile->sLeadershipGain = gMercProfiles[ ubSrcProfile ].sLeadershipGain;
pNewProfile->sWisdomGain = gMercProfiles[ ubSrcProfile ].sWisdomGain;
pNewProfile->sExpLevelGain = gMercProfiles[ ubSrcProfile ].sExpLevelGain;
pNewProfile->sMarksmanshipGain = gMercProfiles[ ubSrcProfile ].sMarksmanshipGain;
pNewProfile->sMedicalGain = gMercProfiles[ ubSrcProfile ].sMedicalGain;
pNewProfile->sMechanicGain = gMercProfiles[ ubSrcProfile ].sMechanicGain;
pNewProfile->sExplosivesGain = gMercProfiles[ ubSrcProfile ].sExplosivesGain;
pNewProfile->bLifeDelta = gMercProfiles[ ubSrcProfile ].bLifeDelta;
pNewProfile->bAgilityDelta = gMercProfiles[ ubSrcProfile ].bAgilityDelta;
pNewProfile->bDexterityDelta = gMercProfiles[ ubSrcProfile ].bDexterityDelta;
pNewProfile->bStrengthDelta = gMercProfiles[ ubSrcProfile ].bStrengthDelta;
pNewProfile->bLeadershipDelta = gMercProfiles[ ubSrcProfile ].bLeadershipDelta;
pNewProfile->bWisdomDelta = gMercProfiles[ ubSrcProfile ].bWisdomDelta;
pNewProfile->bExpLevelDelta = gMercProfiles[ ubSrcProfile ].bExpLevelDelta;
pNewProfile->bMarksmanshipDelta = gMercProfiles[ ubSrcProfile ].bMarksmanshipDelta;
pNewProfile->bMedicalDelta = gMercProfiles[ ubSrcProfile ].bMedicalDelta;
pNewProfile->bMechanicDelta = gMercProfiles[ ubSrcProfile ].bMechanicDelta;
pNewProfile->bExplosivesDelta = gMercProfiles[ ubSrcProfile ].bExplosivesDelta;
*/
// WDS - Clean up inventory handling
pNewProfile->bInvStatus = gMercProfiles[ ubSrcProfile ].bInvStatus;
pNewProfile->bInvNumber = gMercProfiles[ ubSrcProfile ].bInvNumber;
pNewProfile->inv = gMercProfiles[ ubSrcProfile ].inv;
memcpy( pNewProfile->bMercTownReputation , gMercProfiles[ ubSrcProfile ].bMercTownReputation , sizeof( UINT8 ) * 20 );
// remove face
DeleteSoldierFace( pSoldier );
pSoldier->ubProfile = ubDestProfile;
// create new face
pSoldier->iFaceIndex = InitSoldierFace( pSoldier );
// replace profile in group
ReplaceSoldierProfileInPlayerGroup( pSoldier->ubGroupID, ubSrcProfile, ubDestProfile );
pSoldier->bStrength = pNewProfile->bStrength + pNewProfile->bStrengthDelta;
pSoldier->bDexterity = pNewProfile->bDexterity + pNewProfile->bDexterityDelta;
pSoldier->bAgility = pNewProfile->bAgility + pNewProfile->bAgilityDelta;
pSoldier->bWisdom = pNewProfile->bWisdom + pNewProfile->bWisdomDelta;
pSoldier->bExpLevel = pNewProfile->bExpLevel + pNewProfile->bExpLevelDelta;
pSoldier->bLeadership = pNewProfile->bLeadership + pNewProfile->bLeadershipDelta;
pSoldier->bMarksmanship = pNewProfile->bMarksmanship + pNewProfile->bMarksmanshipDelta;
pSoldier->bMechanical = pNewProfile->bMechanical + pNewProfile->bMechanicDelta;
pSoldier->bMedical = pNewProfile->bMedical + pNewProfile->bMedicalDelta;
pSoldier->bExplosive = pNewProfile->bExplosive + pNewProfile->bExplosivesDelta;
if ( pSoldier->ubProfile == LARRY_DRUNK )
{
SetFactTrue( FACT_LARRY_CHANGED );
}
else
{
SetFactFalse( FACT_LARRY_CHANGED );
}
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
return( pSoldier );
}
BOOLEAN DoesNPCOwnBuilding( SOLDIERTYPE *pSoldier, INT16 sGridNo )
{
UINT8 ubRoomInfo;
// Get room info
ubRoomInfo = gubWorldRoomInfo[ sGridNo ];
if ( ubRoomInfo == NO_ROOM )
{
return( FALSE );
}
// Are we an NPC?
if ( pSoldier->bTeam != CIV_TEAM )
{
return( FALSE );
}
// OK, check both ranges
if ( ubRoomInfo >= gMercProfiles[ pSoldier->ubProfile ].ubRoomRangeStart[ 0 ] &&
ubRoomInfo <= gMercProfiles[ pSoldier->ubProfile ].ubRoomRangeEnd[ 0 ] )
{
return( TRUE );
}
if ( ubRoomInfo >= gMercProfiles[ pSoldier->ubProfile ].ubRoomRangeStart[ 1 ] &&
ubRoomInfo <= gMercProfiles[ pSoldier->ubProfile ].ubRoomRangeEnd[ 1 ] )
{
return( TRUE );
}
return( FALSE );
}
BOOLEAN IsProfileIdAnAimOrMERCMerc( UINT8 ubProfileID )
{
if( ubProfileID < BIFF ||
( ubProfileID >= BIFF && ubProfileID <= BUBBA ) ||
ubProfileID >= GASTON )
{
return( TRUE );
}
return( FALSE );
}