mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
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
1558 lines
43 KiB
C++
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 );
|
|
}
|