mirror of
https://github.com/1dot13/source.git
synced 2026-09-02 14:36:06 +02:00
- New Feature: enemy assassins disguise as militia and can ambush player mercs
- Fix: invalid GridNo for mounting weapons git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5753 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
@@ -953,6 +953,23 @@ void StartCivQuote( SOLDIERTYPE *pCiv )
|
||||
|
||||
}
|
||||
|
||||
// Flugente: if we are an assassin, we speak like the militia we emulate
|
||||
if ( pCiv->bSoldierFlagMask & SOLDIER_ASSASSIN )
|
||||
{
|
||||
switch ( pCiv->GetUniformType() )
|
||||
{
|
||||
case UNIFORM_MILITIA_REGULAR:
|
||||
ubCivQuoteID = CIV_QUOTE_MEDIUM_MILITIA;
|
||||
break;
|
||||
case UNIFORM_MILITIA_ELITE:
|
||||
ubCivQuoteID = CIV_QUOTE_ELITE_MILITIA;
|
||||
break;
|
||||
default:
|
||||
ubCivQuoteID = CIV_QUOTE_GREEN_MILITIA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine location...
|
||||
// Get location of civ on screen.....
|
||||
GetSoldierScreenPos( pCiv, &sScreenX, &sScreenY );
|
||||
|
||||
@@ -2125,7 +2125,7 @@ void HandleNPCDoAction( UINT8 ubTargetNPC, UINT16 usActionCode, UINT8 ubQuoteNum
|
||||
{
|
||||
//MakeCivHostile( pSoldier, 2 );
|
||||
}
|
||||
if (pSoldier->ubProfile != NO_PROFILE && pSoldier->stats.bLife >= OKLIFE)
|
||||
if ( ( pSoldier->ubProfile != NO_PROFILE || pSoldier->IsAssassin() ) && pSoldier->stats.bLife >= OKLIFE )
|
||||
{
|
||||
// trigger quote!
|
||||
//TriggerNPCWithIHateYouQuote( pSoldier->ubProfile );
|
||||
|
||||
@@ -358,7 +358,7 @@ enum
|
||||
UNNAMED_CIV_GROUP_17,
|
||||
UNNAMED_CIV_GROUP_18,
|
||||
UNNAMED_CIV_GROUP_19,
|
||||
UNNAMED_CIV_GROUP_20,
|
||||
ASSASSIN_CIV_GROUP, // Flugente: enemy assassins belong to this group
|
||||
UNNAMED_CIV_GROUP_21,
|
||||
UNNAMED_CIV_GROUP_22,
|
||||
UNNAMED_CIV_GROUP_23,
|
||||
|
||||
+12
-26
@@ -3066,35 +3066,21 @@ BOOLEAN HandleAtNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving )
|
||||
}
|
||||
|
||||
}
|
||||
else if ( pSoldier->bTeam == CIV_TEAM && pSoldier->ubProfile != NO_PROFILE && pSoldier->aiData.bNeutral )
|
||||
else if ( pSoldier->bTeam == CIV_TEAM && pSoldier->IsAssassin() && pSoldier->aiData.bNeutral )
|
||||
{
|
||||
switch( pSoldier->ubProfile )
|
||||
{
|
||||
case JIM:
|
||||
case JACK:
|
||||
case OLAF:
|
||||
case RAY:
|
||||
case OLGA:
|
||||
case TYRONE:
|
||||
{
|
||||
INT32 sDesiredMercDist;
|
||||
INT32 sDesiredMercDist;
|
||||
|
||||
if (!TileIsOutOfBounds(ClosestPC( pSoldier, &sDesiredMercDist )))
|
||||
{
|
||||
if ( sDesiredMercDist <= NPC_TALK_RADIUS * 2 )
|
||||
{
|
||||
// stop
|
||||
CancelAIAction( pSoldier, TRUE );
|
||||
// aaaaaaaaaaaaaaaaaaaaatttaaaack!!!!
|
||||
AddToShouldBecomeHostileOrSayQuoteList( pSoldier->ubID );
|
||||
//MakeCivHostile( pSoldier, 2 );
|
||||
//TriggerNPCWithIHateYouQuote( pSoldier->ubProfile );
|
||||
}
|
||||
}
|
||||
if (!TileIsOutOfBounds(ClosestPC( pSoldier, &sDesiredMercDist )))
|
||||
{
|
||||
if ( sDesiredMercDist <= NPC_TALK_RADIUS * 2 )
|
||||
{
|
||||
// stop
|
||||
CancelAIAction( pSoldier, TRUE );
|
||||
// aaaaaaaaaaaaaaaaaaaaatttaaaack!!!!
|
||||
AddToShouldBecomeHostileOrSayQuoteList( pSoldier->ubID );
|
||||
//MakeCivHostile( pSoldier, 2 );
|
||||
//TriggerNPCWithIHateYouQuote( pSoldier->ubProfile );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2048,7 +2048,7 @@ void TakeCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel )
|
||||
else if ( pCorpse->def.ubBodyType == REGFEMALE || pCorpse->def.ubBodyType == MINICIV || pCorpse->def.ubBodyType == DRESSCIV )
|
||||
gTempObject[0]->data.sObjectFlag |= CORPSE_F;
|
||||
|
||||
if ( pCorpse->def.usFlags & ROTTING_CORPSE_HEAD_TAKEN )
|
||||
if ( pCorpse->def.usFlags & ROTTING_CORPSE_HEAD_TAKEN || pCorpse->def.fHeadTaken )
|
||||
gTempObject[0]->data.sObjectFlag |= CORPSE_NO_HEAD;
|
||||
|
||||
if ( pCorpse->def.usFlags & ROTTING_CORPSE_NO_VEST )
|
||||
|
||||
@@ -13828,6 +13828,10 @@ BOOLEAN SOLDIERTYPE::IsWeaponMounted( void )
|
||||
if ( this->pathing.bLevel == 1 )
|
||||
return( FALSE );
|
||||
|
||||
// this is odd - invalid GridNo... well, not mounted then
|
||||
if ( TileIsOutOfBounds(this->sGridNo) )
|
||||
return( FALSE );
|
||||
|
||||
// we determine the height of the next tile in our direction. Because of the way structures are handled, we sometimes have to take the very tile we're occupying right now
|
||||
INT32 nextGridNoinSight = this->sGridNo;
|
||||
if ( this->ubDirection == NORTH || this->ubDirection == SOUTHWEST || this->ubDirection == WEST || this->ubDirection == NORTHWEST )
|
||||
@@ -14718,17 +14722,23 @@ BOOLEAN SOLDIERTYPE::EquipmentTooGood( BOOLEAN fCloselook )
|
||||
INT8 uniformtype = GetUniformType();
|
||||
|
||||
// adjust max coolness depending on uniform
|
||||
// enemy spies get a small bonus here
|
||||
switch ( uniformtype )
|
||||
{
|
||||
case UNIFORM_ENEMY_ADMIN:
|
||||
maxcoolnessallowed += 1;
|
||||
break;
|
||||
case UNIFORM_ENEMY_TROOP:
|
||||
case UNIFORM_MILITIA_ROOKIE:
|
||||
maxcoolnessallowed += 2;
|
||||
break;
|
||||
case UNIFORM_ENEMY_ELITE:
|
||||
case UNIFORM_MILITIA_REGULAR:
|
||||
maxcoolnessallowed += 3;
|
||||
break;
|
||||
case UNIFORM_MILITIA_ELITE:
|
||||
maxcoolnessallowed += 4;
|
||||
break;
|
||||
default:
|
||||
// we do not wear a proper army uniform, uncover us. Note: This should never happen - if this message shows, somewhere, something is wrong
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNIFORM_NOORDER], this->name );
|
||||
@@ -15108,16 +15118,17 @@ BOOLEAN SOLDIERTYPE::SeemsLegit( UINT8 ubObserverID )
|
||||
// are we targeting a buddy of our observer?
|
||||
if ( this->ubTargetID && MercPtrs[this->ubTargetID] && MercPtrs[this->ubTargetID]->bTeam == pSoldier->bTeam )
|
||||
{
|
||||
// if we aiming at a soldier, others will notice our intent... not covert!
|
||||
// if we are aiming at a soldier, others will notice our intent... not covert!
|
||||
if ( WeaponReady(this) )
|
||||
{
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_TARGETTING_SOLDIER], this->name );
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_TARGETTING_SOLDIER], this->name, MercPtrs[this->ubTargetID]->name );
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// even as a soldier, we will be caught around fresh corpses
|
||||
if (distance < gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse)
|
||||
// assassins will not be uncovered around corpses, as the AI cannot willingly evade them... one could 'ward' against assassins by surrounding yourself with fresh corpses
|
||||
if (distance < gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse && !(this->bSoldierFlagMask & SOLDIER_ASSASSIN) )
|
||||
{
|
||||
// check wether we are around a fresh corpse - this will make us much more suspicious
|
||||
// I deem this necessary, to avoid cheap exploits by nefarious players :-)
|
||||
@@ -15208,6 +15219,11 @@ BOOLEAN SOLDIERTYPE::RecognizeAsCombatant(UINT8 ubTargetID)
|
||||
// we have uncovered a spy! Get alerted, if we aren't already
|
||||
if ( this->aiData.bAlertStatus < STATUS_BLACK )
|
||||
this->aiData.bAlertStatus = STATUS_BLACK;
|
||||
|
||||
// reset our sight of this guy
|
||||
this->aiData.bOppList[pSoldier->ubID] = NOT_HEARD_OR_SEEN;
|
||||
|
||||
ManSeesMan(this, pSoldier, pSoldier->sGridNo, pSoldier->pathing.bLevel, 0, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -15216,11 +15232,22 @@ BOOLEAN SOLDIERTYPE::RecognizeAsCombatant(UINT8 ubTargetID)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// dress up in our natural state
|
||||
// loose covert property
|
||||
void SOLDIERTYPE::LooseDisguise( void )
|
||||
{
|
||||
// loose any covert flags
|
||||
this->bSoldierFlagMask &= ~(SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER);
|
||||
|
||||
// rehandle sight for everybody
|
||||
SOLDIERTYPE* pSoldier;
|
||||
UINT8 iLoop = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
|
||||
for (pSoldier = MercPtrs[iLoop]; iLoop <= gTacticalStatus.Team[ LAST_TEAM ].bLastID; ++iLoop, ++pSoldier )
|
||||
{
|
||||
if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 )
|
||||
{
|
||||
RecalculateOppCntsDueToNoLongerNeutral( pSoldier );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// undisguise or take off any clothes item and switch back to original clothes
|
||||
@@ -15327,7 +15354,7 @@ BOOLEAN SOLDIERTYPE::CanProcessPrisoners()
|
||||
if ( !prisonhere )
|
||||
return FALSE;
|
||||
|
||||
// Are there any prisoners in this prison?
|
||||
// Are there any prisoners in this prison? note that there are no underground prisons
|
||||
if ( !this->bSectorZ )
|
||||
{
|
||||
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( this->sSectorX, this->sSectorY ) ] );
|
||||
@@ -15335,14 +15362,6 @@ BOOLEAN SOLDIERTYPE::CanProcessPrisoners()
|
||||
if ( pSectorInfo->uiNumberOfPrisonersOfWar > 0 )
|
||||
return TRUE;
|
||||
}
|
||||
// no underground prisons anyway
|
||||
/*else
|
||||
{
|
||||
UNDERGROUND_SECTORINFO *pSectorInfo = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
|
||||
|
||||
if ( pSectorInfo->uiNumberOfPrisonersOfWar > 0 )
|
||||
return TRUE;
|
||||
}*/
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@@ -15383,6 +15402,20 @@ BOOLEAN SOLDIERTYPE::UsesScubaGear()
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Flugente: are we an assassin?
|
||||
BOOLEAN SOLDIERTYPE::IsAssassin()
|
||||
{
|
||||
// kingpin's hitmen are assassins
|
||||
if ( this->ubProfile >= JIM && this->ubProfile <= TYRONE )
|
||||
return TRUE;
|
||||
|
||||
// there can be non-NPC assassins too
|
||||
if ( this->bSoldierFlagMask & SOLDIER_ASSASSIN )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
|
||||
{
|
||||
INT8 bBandaged; //,savedOurTurn;
|
||||
@@ -16731,23 +16764,17 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid
|
||||
}
|
||||
else if (pTSoldier->aiData.bNeutral)
|
||||
{
|
||||
switch( pTSoldier->ubProfile )
|
||||
if ( pTSoldier->IsAssassin() )
|
||||
{
|
||||
case JIM:
|
||||
case JACK:
|
||||
case OLAF:
|
||||
case RAY:
|
||||
case OLGA:
|
||||
case TYRONE:
|
||||
// Start combat etc
|
||||
DeleteTalkingMenu();
|
||||
CancelAIAction( pTSoldier, TRUE );
|
||||
AddToShouldBecomeHostileOrSayQuoteList( pTSoldier->ubID );
|
||||
break;
|
||||
default:
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start talking!
|
||||
return( InitiateConversation( pTSoldier, this, NPC_INITIAL_QUOTE, 0 ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -358,9 +358,9 @@ enum
|
||||
#define SOLDIER_DAMAGED_PANTS 0x00000100 //256 // Soldier's vest is damaged (and thus can't be taken off)
|
||||
#define SOLDIER_HEADSHOT 0x00000200 //512 // last hit received was a headshot (attack to the head, so knifes/punches also work)
|
||||
#define SOLDIER_POW 0x00000400 //1024 // we are a prisoner of war
|
||||
/*#define ENEMY_NET_4_LVL_3 0x00000800 //2048
|
||||
#define SOLDIER_ASSASSIN 0x00000800 //2048 // we are an enemy assassin, and thus we will behave very different from normal enemies
|
||||
|
||||
#define ENEMY_NET_1_LVL_4 0x00001000 //4096
|
||||
/*#define ENEMY_NET_1_LVL_4 0x00001000 //4096
|
||||
#define ENEMY_NET_2_LVL_4 0x00002000 //8192
|
||||
#define ENEMY_NET_3_LVL_4 0x00004000 //16384
|
||||
#define ENEMY_NET_4_LVL_4 0x00008000 //32768
|
||||
@@ -1494,7 +1494,7 @@ public:
|
||||
// do we recognize someone else as a combatant?
|
||||
BOOLEAN RecognizeAsCombatant(UINT8 ubTargetID);
|
||||
|
||||
// loose disguise, dress up in our natural state
|
||||
// loose covert property
|
||||
void LooseDisguise( void );
|
||||
|
||||
// lose disguise or take off any clothes item and switch back to original clothes
|
||||
@@ -1506,6 +1506,9 @@ public:
|
||||
|
||||
// Flugente: scuba gear
|
||||
BOOLEAN UsesScubaGear();
|
||||
|
||||
// Flugente: are we an assassin?
|
||||
BOOLEAN IsAssassin();
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}; // SOLDIERTYPE;
|
||||
|
||||
+124
-3
@@ -1249,8 +1249,8 @@ BOOLEAN TacticalCopySoldierFromProfile( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STR
|
||||
//}
|
||||
}
|
||||
|
||||
// Flugente: if playing with the covert trait, the assasins come covert, so they are tougher to find
|
||||
if ( gGameExternalOptions.fAssassinsAreDisguised && gGameOptions.fNewTraitSystem && pSoldier->ubProfile >= JIM && pSoldier->ubProfile <= TYRONE )
|
||||
// Flugente: if playing with the covert trait, the assassins come covert, so they are tougher to find
|
||||
if ( gGameExternalOptions.fAssassinsAreDisguised && gGameOptions.fNewTraitSystem && pSoldier->IsAssassin() )
|
||||
{
|
||||
pSoldier->bSoldierFlagMask |= (SOLDIER_COVERT_SOLDIER|SOLDIER_COVERT_NPC_SPECIAL|SOLDIER_NEW_VEST|SOLDIER_NEW_PANTS);
|
||||
|
||||
@@ -3015,6 +3015,127 @@ SOLDIERTYPE* TacticalCreateCreature( INT8 bCreatureBodyType )
|
||||
return TacticalCreateSoldier( &pp, &ubID );
|
||||
}
|
||||
|
||||
// Flugente: assassins are elite soldiers of the civ team that go hostile on a certain event, otherwsie they jsut blend in
|
||||
SOLDIERTYPE* TacticalCreateEnemyAssassin(UINT8 disguisetype)
|
||||
{
|
||||
BASIC_SOLDIERCREATE_STRUCT bp;
|
||||
SOLDIERCREATE_STRUCT pp;
|
||||
UINT8 ubID;
|
||||
SOLDIERTYPE * pSoldier;
|
||||
|
||||
// this needs the covert ops trait, and thus the new trait system
|
||||
if ( !gGameOptions.fNewTraitSystem )
|
||||
return NULL;
|
||||
|
||||
// not in autoresolve!
|
||||
if( guiCurrentScreen == AUTORESOLVE_SCREEN )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset( &bp, 0, sizeof( BASIC_SOLDIERCREATE_STRUCT ) );
|
||||
RandomizeRelativeLevel( &( bp.bRelativeAttributeLevel ), SOLDIER_CLASS_ELITE );
|
||||
RandomizeRelativeLevel( &( bp.bRelativeEquipmentLevel ), SOLDIER_CLASS_ELITE );
|
||||
bp.bTeam = CIV_TEAM;
|
||||
bp.bOrders = SEEKENEMY;
|
||||
bp.bAttitude = (INT8) Random( MAXATTITUDES );
|
||||
bp.bBodyType = Random(4);
|
||||
bp.ubSoldierClass = SOLDIER_CLASS_ELITE;
|
||||
CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp );
|
||||
|
||||
pSoldier = TacticalCreateSoldier( &pp, &ubID );
|
||||
|
||||
if ( pSoldier )
|
||||
{
|
||||
// set correct stats
|
||||
pSoldier->stats.bLife = pSoldier->stats.bLifeMax = (INT8)( 70 + Random( 26 ) );
|
||||
pSoldier->stats.bAgility = (INT8)( 70 + Random( 16 ) );
|
||||
|
||||
// add assassin flag
|
||||
pSoldier->bSoldierFlagMask |= (SOLDIER_COVERT_SOLDIER|SOLDIER_ASSASSIN);
|
||||
|
||||
// add spy trait lvl2
|
||||
pSoldier->stats.ubSkillTraits[0] = COVERT_NT;
|
||||
pSoldier->stats.ubSkillTraits[1] = COVERT_NT;
|
||||
|
||||
// set correct civ group
|
||||
pSoldier->ubCivilianGroup = ASSASSIN_CIV_GROUP;
|
||||
|
||||
// set militia name to further irritate the player
|
||||
swprintf( pSoldier->name, TacticalStr[ MILITIA_TEAM_MERC_NAME ] );
|
||||
|
||||
// wear a militia uniform
|
||||
UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
||||
|
||||
if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE )
|
||||
{
|
||||
switch( disguisetype )
|
||||
{
|
||||
case ELITE_MILITIA:
|
||||
SET_PALETTEREP_ID( pSoldier->VestPal, gUniformColors[ UNIFORM_MILITIA_ELITE ].vest );
|
||||
SET_PALETTEREP_ID( pSoldier->PantsPal, gUniformColors[ UNIFORM_MILITIA_ELITE ].pants );
|
||||
break;
|
||||
case REGULAR_MILITIA:
|
||||
SET_PALETTEREP_ID( pSoldier->VestPal, gUniformColors[ UNIFORM_MILITIA_REGULAR ].vest );
|
||||
SET_PALETTEREP_ID( pSoldier->PantsPal, gUniformColors[ UNIFORM_MILITIA_REGULAR ].pants );
|
||||
break;
|
||||
default:
|
||||
SET_PALETTEREP_ID( pSoldier->VestPal, gUniformColors[ UNIFORM_MILITIA_ROOKIE ].vest );
|
||||
SET_PALETTEREP_ID( pSoldier->PantsPal, gUniformColors[ UNIFORM_MILITIA_ROOKIE ].pants );
|
||||
break;
|
||||
}
|
||||
|
||||
// Use palette from HVOBJECT, then use substitution for pants, etc
|
||||
memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
|
||||
|
||||
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal );
|
||||
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal );
|
||||
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal );
|
||||
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal );
|
||||
|
||||
pSoldier->CreateSoldierPalettes();
|
||||
}
|
||||
|
||||
// send soldier to centre of map, roughly
|
||||
pSoldier->aiData.sNoiseGridno = (CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS);
|
||||
pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION;
|
||||
}
|
||||
|
||||
return( pSoldier );
|
||||
}
|
||||
|
||||
void CreateAssassin(UINT8 disguisetype)
|
||||
{
|
||||
SOLDIERTYPE* pSoldier = TacticalCreateEnemyAssassin( disguisetype );
|
||||
|
||||
if ( pSoldier )
|
||||
{
|
||||
// find a valid starting gridno
|
||||
if ( pSoldier->sInsertionGridNo <= 0 )
|
||||
{
|
||||
UINT8 tries = 0; // counter for gridno function... if we fail to get a valid starting gridno multiple times, do not place assassin
|
||||
INT32 sGridNo = NOWHERE;
|
||||
do
|
||||
{
|
||||
if ( ++tries > 20 )
|
||||
return;
|
||||
|
||||
sGridNo = RandomGridNo();
|
||||
}
|
||||
// a valid starting gridno must be valid, not in a structure, not in water, and not too near to our mercs
|
||||
while( TileIsOutOfBounds(sGridNo) || FindStructure( sGridNo, STRUCTURE_BLOCKSMOVES ) || TERRAIN_IS_WATER( gpWorldLevelData[ sGridNo ].ubTerrainID) || GridNoNearPlayerMercs(sGridNo, 12) );
|
||||
|
||||
pSoldier->sInsertionGridNo = sGridNo;
|
||||
}
|
||||
|
||||
AddSoldierToSector( pSoldier->ubID );
|
||||
|
||||
// So we can see them!
|
||||
AllTeamsLookForAll(NO_INTERRUPTS);
|
||||
|
||||
gTacticalStatus.fCivGroupHostile[ ASSASSIN_CIV_GROUP ] = CIV_GROUP_WILL_BECOME_HOSTILE;
|
||||
}
|
||||
}
|
||||
|
||||
void RandomizeRelativeLevel( INT8 *pbRelLevel, UINT8 ubSoldierClass )
|
||||
{
|
||||
@@ -4794,4 +4915,4 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre
|
||||
return( TRUE );
|
||||
else
|
||||
return( FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +426,10 @@ SOLDIERTYPE* TacticalCreateArmyTroop();
|
||||
SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass );
|
||||
SOLDIERTYPE* TacticalCreateCreature( INT8 bCreatureBodyType );
|
||||
|
||||
// Flugente: assassins are elite soldiers of the civ team that go hostile on a certain event, otherwise they just blend in
|
||||
SOLDIERTYPE* TacticalCreateEnemyAssassin(UINT8 disguisetype);
|
||||
void CreateAssassin(UINT8 disguisetype);
|
||||
|
||||
// randomly generates a relative level rating (attributes or equipment)
|
||||
void RandomizeRelativeLevel( INT8 *pbRelLevel, UINT8 ubSoldierClass );
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
#include "Queen Command.h"
|
||||
|
||||
#include "Map Edgepoints.h"
|
||||
#include "Campaign.h" // added by Flugente for HighestPlayerProgressPercentage()
|
||||
|
||||
BOOLEAN gfOriginalList = TRUE;
|
||||
|
||||
SOLDIERINITNODE *gSoldierInitHead = NULL;
|
||||
@@ -911,7 +913,12 @@ UINT8 AddSoldierInitListTeamToWorld( INT8 bTeam, UINT8 ubMaxNum )
|
||||
//we now have the number, so compared it to the num we can add, and determine how we will
|
||||
//randomly determine which nodes to add.
|
||||
if( !ubSlotsAvailable )
|
||||
{ //There aren't any basic placements of desired team, so exit.
|
||||
{
|
||||
// Flugente: decide wether to spawn enemy assassins in this sector (not kingpin's hitmen, they are handled elsewhere)
|
||||
if ( bTeam == MILITIA_TEAM )
|
||||
SectorAddAssassins(gWorldSectorX, gWorldSectorY, gbWorldSectorZ);
|
||||
|
||||
//There aren't any basic placements of desired team, so exit.
|
||||
return ubNumAdded;
|
||||
}
|
||||
curr = mark;
|
||||
@@ -953,6 +960,11 @@ UINT8 AddSoldierInitListTeamToWorld( INT8 bTeam, UINT8 ubMaxNum )
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
// Flugente: decide wether to spawn enemy assassins in this sector (not kingpin's hitmen, they are handled elsewhere)
|
||||
if ( bTeam == MILITIA_TEAM )
|
||||
SectorAddAssassins(gWorldSectorX, gWorldSectorY, gbWorldSectorZ);
|
||||
|
||||
return ubNumAdded;
|
||||
}
|
||||
|
||||
@@ -2676,3 +2688,82 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern UINT32 GetWorldTotalMin( );
|
||||
|
||||
// Flugente: decide wether to spawn enemy assassins in this sector (not kingpin's hitmen, they are handled elsewhere)
|
||||
void SectorAddAssassins( INT16 sMapX, INT16 sMapY, INT16 sMapZ )
|
||||
{
|
||||
// this needs to be turned on on, and new trait system needs to be active
|
||||
if ( !gGameExternalOptions.fEnemyAssassins || !gGameOptions.fNewTraitSystem )
|
||||
return;
|
||||
|
||||
// only possible if a certain progress has been reached - the queen only sends out assassins after a while
|
||||
if ( HighestPlayerProgressPercentage() < gGameExternalOptions.usAssassinMinimumProgress )
|
||||
return;
|
||||
|
||||
// not in underground sectors
|
||||
if ( sMapZ > 0 )
|
||||
return;
|
||||
|
||||
SECTORINFO *pSector = &SectorInfo[ SECTOR( sMapX, sMapY ) ];
|
||||
if ( !pSector )
|
||||
return;
|
||||
|
||||
// does not work atm, time gets reset too early
|
||||
// not if we have recently been in this sector
|
||||
//if ( pSector->uiTimeCurrentSectorWasLastLoaded + 10 > GetWorldTotalMin() )
|
||||
//return;
|
||||
|
||||
// do not spawn if militia size is very small (we will be spotted much easier by the player)
|
||||
UINT32 totalmilitia = pSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] + pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] + pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ];
|
||||
if ( totalmilitia < gGameExternalOptions.usAssassinMinimumMilitia )
|
||||
return;
|
||||
|
||||
// count current number of civilians
|
||||
UINT16 numberofcivs = 0;
|
||||
SOLDIERTYPE* pTeamSoldier = NULL;
|
||||
INT32 cnt = gTacticalStatus.Team[ CIV_TEAM ].bFirstID;
|
||||
INT32 lastid = gTacticalStatus.Team[ CIV_TEAM ].bLastID;
|
||||
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pTeamSoldier)
|
||||
{
|
||||
// check if teamsoldier exists in this sector
|
||||
if ( pTeamSoldier && pTeamSoldier->bActive && pTeamSoldier->bInSector && pTeamSoldier->sSectorX == sMapX && pTeamSoldier->sSectorY == sMapY && pTeamSoldier->bSectorZ == sMapZ )
|
||||
++numberofcivs;
|
||||
}
|
||||
|
||||
// we can't spawn if all civilian slots are already taken
|
||||
if ( numberofcivs >= gGameExternalOptions.ubGameMaximumNumberOfCivilians )
|
||||
return;
|
||||
|
||||
// now count militia, and which type (green, regular, elite) is most numerous - that will be the best type to blend in
|
||||
UINT8 militiacnt = pSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ];
|
||||
UINT8 militiadisguise = GREEN_MILITIA;
|
||||
if ( pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] > militiacnt )
|
||||
{
|
||||
militiacnt = pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ];
|
||||
militiadisguise = REGULAR_MILITIA;
|
||||
}
|
||||
if ( pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] > militiacnt )
|
||||
{
|
||||
militiacnt = pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ];
|
||||
militiadisguise = ELITE_MILITIA;
|
||||
}
|
||||
|
||||
// the bigger the militia, the more likely infiltration
|
||||
// if militia is at maximum, there is a 10% chance of infiltration
|
||||
UINT32 resultrange = gGameExternalOptions.ubGameMaximumNumberOfRebels * 10;
|
||||
UINT32 modifiedmilitianumber = totalmilitia * gGameExternalOptions.usAssassinPropabilityModifier / 100;
|
||||
if ( Random(resultrange) >= modifiedmilitianumber )
|
||||
return;
|
||||
|
||||
CreateAssassin( militiadisguise );
|
||||
++numberofcivs;
|
||||
|
||||
// if there is still room for a second assassin, and we are feeling very lucky again, add a second one
|
||||
if ( numberofcivs < gGameExternalOptions.ubGameMaximumNumberOfCivilians && Random(resultrange) < modifiedmilitianumber )
|
||||
{
|
||||
CreateAssassin( militiadisguise );
|
||||
++numberofcivs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,4 +59,7 @@ void RemoveDetailedPlacementInfo( UINT8 ubNodeID );
|
||||
void AddProfilesUsingProfileInsertionData();
|
||||
void AddProfilesNotUsingProfileInsertionData();
|
||||
|
||||
// Flugente: decide wether to spawn enemy assassins in this sector (not kingpin's hitmen, they are handled elsewhere)
|
||||
void SectorAddAssassins( INT16 sMapX, INT16 sMapY, INT16 sMapZ );
|
||||
|
||||
#endif
|
||||
+64
-3
@@ -770,6 +770,11 @@ void CheckHostileOrSayQuoteList( void )
|
||||
{
|
||||
gTacticalStatus.fCivGroupHostile[ pSoldier->ubCivilianGroup ] = CIV_GROUP_HOSTILE;
|
||||
}
|
||||
|
||||
// reevaluate sight - we might already see people that weren't enemies until now
|
||||
ManLooksForOtherTeams(pSoldier);
|
||||
|
||||
pSoldier->aiData.bAlertStatus = STATUS_RED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2370,6 +2375,42 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Flugente: for assassins without profiles
|
||||
else if ( pSoldier->IsAssassin() && pSoldier->bTeam == CIV_TEAM )
|
||||
{
|
||||
// if we are an assassin and still neutral and undercover, approach target and then become hostile
|
||||
if ( pSoldier->aiData.bNeutral && pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) )
|
||||
{
|
||||
// only if this guy isn't disguised himself!
|
||||
if ( (pOpponent->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0)
|
||||
{
|
||||
if ( pSoldier->ubCivilianGroup != NON_CIV_GROUP && gTacticalStatus.fCivGroupHostile[ pSoldier->ubCivilianGroup ] >= CIV_GROUP_WILL_BECOME_HOSTILE )
|
||||
{
|
||||
// measure distance to our opponent, only go hostile if he is close enough
|
||||
if ( PythSpacesAway( pSoldier->sGridNo, pOpponent->sGridNo ) <= NPC_TALK_RADIUS * 2 )
|
||||
{
|
||||
AddToShouldBecomeHostileOrSayQuoteList( pSoldier->ubID );
|
||||
fNotAddedToList = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( fNotAddedToList )
|
||||
{
|
||||
// change orders, reset action!
|
||||
if ( pSoldier->aiData.bOrders != SEEKENEMY )
|
||||
{
|
||||
pSoldier->aiData.bOrders = SEEKENEMY;
|
||||
if ( pSoldier->aiData.bOppCnt == 0 )
|
||||
{
|
||||
// didn't see anyone before!
|
||||
CancelAIAction( pSoldier, TRUE );
|
||||
SetNewSituation( pSoldier );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pSoldier->bTeam == CIV_TEAM )
|
||||
@@ -2515,9 +2556,29 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo,
|
||||
*/
|
||||
}
|
||||
|
||||
// if both of us are not neutral, AND
|
||||
// if this man is actually a true opponent (we're not on the same side)
|
||||
if (!CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) )
|
||||
// Flugente: reworked this to account for covert ops and assassin mechanisms
|
||||
// if we are not neutral against this guy, we are truly opponents (we're not on the same side) and recognize him as an opponent...
|
||||
BOOLEAN fAddAsOpponent = FALSE;
|
||||
if ( !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) )
|
||||
{
|
||||
// ... check wether he is not neutral against us (account for the fact that we might be covert!)
|
||||
// if we are an NPC assassin
|
||||
if ( pSoldier->bSoldierFlagMask & SOLDIER_ASSASSIN && pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) )
|
||||
{
|
||||
// check wether our opponent would see us as an opponent if we weren't covert
|
||||
if ( !( (pSoldier->aiData.bNeutral || pSoldier->bSoldierFlagMask & SOLDIER_POW) && ( pOpponent->bTeam != CREATURE_TEAM || pOpponent->flags.uiStatusFlags & SOLDIER_VEHICLE ) ) )
|
||||
fAddAsOpponent = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply check wether this guy sees us as an opponent too
|
||||
if ( !CONSIDERED_NEUTRAL( pOpponent, pSoldier ) )
|
||||
fAddAsOpponent = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( fAddAsOpponent )
|
||||
{
|
||||
AddOneOpponent(pSoldier);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user