diff --git a/GameSettings.cpp b/GameSettings.cpp index fe1afbb8..05edb8e0 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1062,7 +1062,12 @@ void LoadGameExternalOptions() // Flugente: the assassins are disguised initially, so it is harder for the player to detect them gGameExternalOptions.fAssassinsAreDisguised = iniReader.ReadBoolean("Tactical Difficulty Settings", "ASSASSINS_DISGUISED", TRUE); - + // Flugente: does the queen send out assassins that mix among your militia? + gGameExternalOptions.fEnemyAssassins = iniReader.ReadBoolean("Tactical Difficulty Settings", "ENEMY_ASSASSINS", TRUE); + gGameExternalOptions.usAssassinMinimumProgress = iniReader.ReadInteger("Tactical Difficulty Settings", "ASSASSIN_MINIMUM_PROGRESS", 30, 0, 100); + gGameExternalOptions.usAssassinMinimumMilitia = iniReader.ReadInteger("Tactical Difficulty Settings", "ASSASSIN_MINIMUM_MILITIA", 10, 0, 64); + gGameExternalOptions.usAssassinPropabilityModifier = iniReader.ReadInteger("Tactical Difficulty Settings", "ASSASSIN_PROPABILITY_MODIFIER", 100, 0, 1000); + //################# Tactical Vision Settings ################# // Sight range diff --git a/GameSettings.h b/GameSettings.h index 3ca503ff..64e892cb 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -361,7 +361,15 @@ typedef struct BOOLEAN fEnableChanceOfEnemyAmbushes; INT8 bChanceModifierEnemyAmbushes; UINT8 usSpecialNPCStronger; - BOOLEAN fAssassinsAreDisguised; // added by Flugente + + // Flugente: should kingpin's hitmen be disguised? This will make them have random clothes among other stuff + BOOLEAN fAssassinsAreDisguised; + + // Flugente: does the queen send out assassins that mix among your militia? + BOOLEAN fEnemyAssassins; + UINT8 usAssassinMinimumProgress; + UINT8 usAssassinMinimumMilitia; + UINT32 usAssassinPropabilityModifier; /////////////////////////////////////// // System settings @@ -522,6 +530,8 @@ typedef struct UINT32 ubGameProgressMikeAvailable; UINT32 ubGameProgressIggyAvaliable; BOOLEAN ubSendTroopsToDrassen; + + // Flugente: new counterattacks and other new AI tactics UINT8 ubAgressiveStrategicAI; UINT32 ubGameProgressOffensiveStage1; UINT32 ubGameProgressOffensiveStage2; diff --git a/Tactical/Civ Quotes.cpp b/Tactical/Civ Quotes.cpp index e09fe84d..c2db386f 100644 --- a/Tactical/Civ Quotes.cpp +++ b/Tactical/Civ Quotes.cpp @@ -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 ); diff --git a/Tactical/Interface Dialogue.cpp b/Tactical/Interface Dialogue.cpp index 906b66c4..b158cec8 100644 --- a/Tactical/Interface Dialogue.cpp +++ b/Tactical/Interface Dialogue.cpp @@ -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 ); diff --git a/Tactical/Overhead Types.h b/Tactical/Overhead Types.h index efd02edc..4af07faa 100644 --- a/Tactical/Overhead Types.h +++ b/Tactical/Overhead Types.h @@ -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, diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 623d5e30..776f25e1 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -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; } } diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 72729740..e072b121 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -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 ) diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index aab29bdd..e595f348 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -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 diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 0a9b39d0..5c3ee955 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -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; diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index eaafc2fb..e5b7e476 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -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 ); -} \ No newline at end of file +} diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index 165cea8c..13bbe5c7 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -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 ); diff --git a/Tactical/Soldier Init List.cpp b/Tactical/Soldier Init List.cpp index 2829eadf..5cdbc65d 100644 --- a/Tactical/Soldier Init List.cpp +++ b/Tactical/Soldier Init List.cpp @@ -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; + } +} diff --git a/Tactical/Soldier Init List.h b/Tactical/Soldier Init List.h index 794b2baa..8e2776d5 100644 --- a/Tactical/Soldier Init List.h +++ b/Tactical/Soldier Init List.h @@ -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 \ No newline at end of file diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index 21d753e7..7d01c0e3 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -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); diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index a52a2e9b..5988b165 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -1459,6 +1459,59 @@ INT32 ClosestPC( SOLDIERTYPE *pSoldier, INT32 * psDistance ) return( sGridNo ); } +// Flugente: like ClosestPC(...), but does not account for covert or not visible mercs +INT32 ClosestUnDisguisedPC( SOLDIERTYPE *pSoldier, INT32 * psDistance ) +{ + // used by NPCs... find the closest PC + // NOTE: skips EPCs! + + UINT8 ubLoop; + SOLDIERTYPE *pTargetSoldier; + INT32 sMinDist = WORLD_MAX; + INT32 sDist; + INT32 sGridNo = NOWHERE; + + // Loop through all mercs on player team + ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; + for ( ; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++) + { + pTargetSoldier = Menptr + ubLoop; + + if (!pTargetSoldier->bActive || !pTargetSoldier->bInSector) + continue; + + // if not conscious, skip him + if (pTargetSoldier->stats.bLife < OKLIFE) + continue; + + if ( AM_AN_EPC( pTargetSoldier ) ) + continue; + + if ( pTargetSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) + continue; + + sDist = PythSpacesAway(pSoldier->sGridNo,pTargetSoldier->sGridNo); + + // if this PC is not visible to the soldier, then add a penalty to the distance + // so that we weight in favour of visible mercs + if ( pTargetSoldier->bTeam != pSoldier->bTeam && pSoldier->aiData.bOppList[ ubLoop ] != SEEN_CURRENTLY ) + continue; + + if (sDist < sMinDist) + { + sMinDist = sDist; + sGridNo = pTargetSoldier->sGridNo; + } + } + + if ( psDistance ) + { + *psDistance = sMinDist; + } + + return( sGridNo ); +} + INT32 FindClosestClimbPointAvailableToAI( SOLDIERTYPE * pSoldier, INT32 sStartGridNo, INT32 sDesiredGridNo, BOOLEAN fClimbUp ) { INT32 sGridNo; diff --git a/TacticalAI/Attacks.cpp b/TacticalAI/Attacks.cpp index f572969b..0c5049ad 100644 --- a/TacticalAI/Attacks.cpp +++ b/TacticalAI/Attacks.cpp @@ -440,7 +440,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns //NumMessage("SHOT AttackValue = ",iAttackValue / 1000); // special stuff for assassins to ignore militia more - if ( pSoldier->ubProfile >= JIM && pSoldier->ubProfile <= TYRONE && pOpponent->bTeam == MILITIA_TEAM ) + if ( pSoldier->IsAssassin() && pOpponent->bTeam == MILITIA_TEAM ) { iAttackValue /= 2; } diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 289f7e19..954f385d 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -662,14 +662,8 @@ INT8 DecideActionNamedNPC( SOLDIERTYPE * pSoldier ) } } - switch( pSoldier->ubProfile ) + if ( pSoldier->IsAssassin() ) { - case JIM: - case JACK: - case OLAF: - case RAY: - case OLGA: - case TYRONE: sDesiredMercLoc = ClosestPC( pSoldier, &sDesiredMercDist ); if (!TileIsOutOfBounds(sDesiredMercLoc)) @@ -691,9 +685,6 @@ INT8 DecideActionNamedNPC( SOLDIERTYPE * pSoldier ) } } } - break; - default: - break; } return( AI_ACTION_NONE ); @@ -838,9 +829,36 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) } } - if (pSoldier->ubProfile != NO_PROFILE) + if ( pSoldier->ubProfile != NO_PROFILE || pSoldier->IsAssassin() ) { - pSoldier->aiData.bAction = DecideActionNamedNPC( pSoldier ); + if ( pSoldier->ubProfile != NO_PROFILE ) + pSoldier->aiData.bAction = DecideActionNamedNPC( pSoldier ); + else + { + INT32 sDesiredMercDist; + INT32 sDesiredMercLoc = ClosestUnDisguisedPC( pSoldier, &sDesiredMercDist ); + + if (!TileIsOutOfBounds(sDesiredMercLoc)) + { + if ( sDesiredMercDist <= NPC_TALK_RADIUS * 2 ) + { + AddToShouldBecomeHostileOrSayQuoteList( pSoldier->ubID ); + // now wait a bit! + pSoldier->aiData.usActionData = 5000; + pSoldier->aiData.bAction = AI_ACTION_WAIT; + } + else + { + pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards( pSoldier, sDesiredMercLoc, AI_ACTION_APPROACH_MERC ); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { + pSoldier->aiData.bAction = AI_ACTION_APPROACH_MERC; + } + } + } + } + if ( pSoldier->aiData.bAction != AI_ACTION_NONE ) { return( pSoldier->aiData.bAction ); @@ -852,9 +870,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) } // turn off counter so we don't check it again pSoldier->uiTimeSinceLastSpoke = 0; - } - } // if not in the way, do nothing most of the time @@ -1440,18 +1456,44 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier) // ****************** // REAL TIME NPC CODE // ****************** - if (pSoldier->ubProfile != NO_PROFILE) + if (pSoldier->ubProfile != NO_PROFILE || pSoldier->IsAssassin() ) { - pSoldier->aiData.bAction = DecideActionNamedNPC( pSoldier ); + if ( pSoldier->ubProfile != NO_PROFILE ) + pSoldier->aiData.bAction = DecideActionNamedNPC( pSoldier ); + else + { + INT32 sDesiredMercDist; + INT32 sDesiredMercLoc = ClosestUnDisguisedPC( pSoldier, &sDesiredMercDist ); + + // Flugente: if this guy is disguised, do not consider him + + if (!TileIsOutOfBounds(sDesiredMercLoc)) + { + if ( sDesiredMercDist <= NPC_TALK_RADIUS * 2 ) + { + AddToShouldBecomeHostileOrSayQuoteList( pSoldier->ubID ); + // now wait a bit! + pSoldier->aiData.usActionData = 5000; + pSoldier->aiData.bAction = AI_ACTION_WAIT; + } + else + { + pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards( pSoldier, sDesiredMercLoc, AI_ACTION_APPROACH_MERC ); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { + pSoldier->aiData.bAction = AI_ACTION_APPROACH_MERC; + } + } + } + } + if ( pSoldier->aiData.bAction != AI_ACTION_NONE ) { return( pSoldier->aiData.bAction ); } - } - } - } // determine the most important noise heard, and its relative value diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index 72b194a4..e7252ad3 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -162,6 +162,7 @@ INT8 ClosestPanicTrigger( SOLDIERTYPE * pSoldier ); INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel); INT32 ClosestPC( SOLDIERTYPE *pSoldier, INT32 * psDistance ); +INT32 ClosestUnDisguisedPC( SOLDIERTYPE *pSoldier, INT32 * psDistance ); // Flugente: like ClosestPC(...), but does not account for covert or not visible mercs BOOLEAN CanAutoBandage( BOOLEAN fDoFullCheck ); void DebugAI( STR szOutput ); diff --git a/TileEngine/Isometric Utils.cpp b/TileEngine/Isometric Utils.cpp index e84ab0c1..8efa52ee 100644 --- a/TileEngine/Isometric Utils.cpp +++ b/TileEngine/Isometric Utils.cpp @@ -1422,3 +1422,21 @@ INT32 RandomGridNo() }while( !GridNoOnVisibleWorldTile( iMapIndex ) ); return iMapIndex; } + +// Flugente: is this gridno near a player merc? +BOOLEAN GridNoNearPlayerMercs( INT32 sGridNo, INT16 sRadius ) +{ + SOLDIERTYPE* pTeamSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ OUR_TEAM ].bLastID; + for ( pTeamSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pTeamSoldier) + { + if ( pTeamSoldier && pTeamSoldier->bActive && pTeamSoldier->bInSector ) + { + if ( PythSpacesAway(sGridNo, pTeamSoldier->sGridNo) < sRadius ) + return( TRUE ); + } + } + + return( FALSE ); +} diff --git a/TileEngine/Isometric Utils.h b/TileEngine/Isometric Utils.h index 26467607..eaa5f2b3 100644 --- a/TileEngine/Isometric Utils.h +++ b/TileEngine/Isometric Utils.h @@ -151,5 +151,7 @@ BOOLEAN FindWallJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStart BOOLEAN FindLowerLevelWall( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection ); BOOLEAN FindWindowJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection ); +// Flugente: is this gridno near a player merc? +BOOLEAN GridNoNearPlayerMercs( INT32 sGridNo, INT16 sRadius ); #endif \ No newline at end of file diff --git a/TileEngine/Radar Screen.cpp b/TileEngine/Radar Screen.cpp index 49b608ba..a1009d57 100644 --- a/TileEngine/Radar Screen.cpp +++ b/TileEngine/Radar Screen.cpp @@ -614,26 +614,30 @@ void RenderRadarScreen( ) } else { - // If on roof, make darker.... - if ( pSoldier->pathing.bLevel > 0 ) - { - usLineColor = Get16BPPColor( FROMRGB( 150, 150, 0 ) ); - } - else - { - usLineColor = Get16BPPColor( gTacticalStatus.Team[ pSoldier->bTeam ].RadarColor ); - } + // If on roof, make darker.... + if ( pSoldier->pathing.bLevel > 0 ) + { + usLineColor = Get16BPPColor( FROMRGB( 150, 150, 0 ) ); + } + else + { + usLineColor = Get16BPPColor( gTacticalStatus.Team[ pSoldier->bTeam ].RadarColor ); + } } } else { usLineColor = Get16BPPColor( gTacticalStatus.Team[ pSoldier->bTeam ].RadarColor ); - - // Override civ team with red if hostile... - if ( pSoldier->bTeam == CIV_TEAM && !pSoldier->aiData.bNeutral && ( pSoldier->bSide != gbPlayerNum ) ) - { + + // Override civ team with red if hostile... + if ( pSoldier->bTeam == CIV_TEAM && !pSoldier->aiData.bNeutral && ( pSoldier->bSide != gbPlayerNum ) ) + { usLineColor = Get16BPPColor( FROMRGB( 255, 0, 0 ) ); - } + } + + // Flugente: if we are a (still covert) enemy assassin, colour us like militia, so that the player wont notice us + if ( pSoldier->bSoldierFlagMask & SOLDIER_ASSASSIN && pSoldier->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + usLineColor = Get16BPPColor( gTacticalStatus.Team[ MILITIA_TEAM ].RadarColor ); // Render different color if an enemy and he's unconscious if ( pSoldier->bTeam != gbPlayerNum && pSoldier->stats.bLife < OKLIFE ) @@ -641,11 +645,11 @@ void RenderRadarScreen( ) usLineColor = Get16BPPColor( FROMRGB( 128, 128, 128 ) ); } - // If on roof, make darker.... - if ( pSoldier->bTeam == gbPlayerNum && pSoldier->pathing.bLevel > 0 ) - { - usLineColor = Get16BPPColor( FROMRGB( 150, 150, 0 ) ); - } + // If on roof, make darker.... + if ( pSoldier->bTeam == gbPlayerNum && pSoldier->pathing.bLevel > 0 ) + { + usLineColor = Get16BPPColor( FROMRGB( 150, 150, 0 ) ); + } } RectangleDraw( TRUE, sXSoldRadar, sYSoldRadar, sXSoldRadar+1, sYSoldRadar+1, usLineColor, pDestBuf ); diff --git a/TileEngine/overhead map.cpp b/TileEngine/overhead map.cpp index 6f758ebd..b0e819e4 100644 --- a/TileEngine/overhead map.cpp +++ b/TileEngine/overhead map.cpp @@ -1425,16 +1425,22 @@ void RenderOverheadOverlays() { //normal if(is_networked) { - if(pSoldier->bTeam!=0) + if(pSoldier->bTeam != OUR_TEAM) { if(pSoldier->bSide==1) { // Civ (white) - if (pSoldier->bTeam == 4) - Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 4 ); + if (pSoldier->bTeam == CIV_TEAM) + { + // Flugente: if we are a (still covert) enemy assassin, colour us like militia, so that the player wont notice us + if ( pSoldier->bSoldierFlagMask & SOLDIER_ASSASSIN && pSoldier->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, MILITIA_TEAM ); + else + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, CIV_TEAM ); + } // Enemy (red) else - Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 1 ); + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, ENEMY_TEAM ); } // Other clients @@ -1449,8 +1455,15 @@ void RenderOverheadOverlays() Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); } else - Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); - RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sX, sY, (INT16)(sX + 3), (INT16)(sY + 9)); + { + // Flugente: if we are a (still covert) enemy assassin, colour us like militia, so that the player wont notice us + if ( pSoldier->bSoldierFlagMask & SOLDIER_ASSASSIN && pSoldier->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, MILITIA_TEAM ); + else + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + + RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sX, sY, (INT16)(sX + 3), (INT16)(sY + 9)); + } } else if( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) { //vehicle diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 8a4af64e..b2b3db70 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -7426,11 +7426,11 @@ STR16 szCovertTextStr[]= L"%s 有迷彩油的痕迹!", //L"%s has camo!", L"%s 有不合身份的背包!", //L"%s has a backpack!", L"%s 被发现携带尸体!", //L"%s is seen carrying a corpse!", - L"%s's %s is suspicious!", - L"%s's %s is considered military hardware!", + L"%s's %s is suspicious!",// TODO.Translate + L"%s's %s is considered military hardware!",// TODO.Translate L"%s 携带了太多的枪支!", //L"%s carries too many guns!", - L"%s's %s is too advanced for an arulcan soldier!", - L"%s's %s has too many attachments!", + L"%s's %s is too advanced for an arulcan soldier!",// TODO.Translate + L"%s's %s has too many attachments!",// TODO.Translate L"%s 被发现有可疑举动!", //L"%s was seen performing suspicious activities!", L"%s 被发现不像个平民!", //L"%s does not look like a civilian!", L"%s 被发现流血不止了!", //L"%s bleeding was discovered!", @@ -7439,7 +7439,7 @@ STR16 szCovertTextStr[]= L"%s 不应该出现在这里!", //L"%s isn't supposed to be here!", L"%s 不应该在这个时候出现在这里!", //L"%s isn't supposed to be here at this time!", L"%s 被发现在新鲜尸体旁边行踪诡秘!", //L"%s was seen near a fresh corpse!", - L"%s 的装备让人惊疑!", //L"%s equipment raises a few eyebrows!", + L"%s is seen targetting %s!", // TODO.Translate L"%s 被发现瞄准了其他士兵!", //L"%s is seen targeting a soldier!", L"%s 被识破了 %s 伪装!", //L"%s has seen through %s disguise!", L"无法找到对应的衣服信息在 Items.xml 中!", //L"No clothes item found in Items.xml!", @@ -7450,7 +7450,7 @@ STR16 szCovertTextStr[]= L"未发现制服!", //L"No uniform found!", L"%s 已伪装成平民", //L"%s is now disguised as a civilian.", L"%s 已伪装成士兵", //L"%s is now disguised as a soldier.", - L"%s wears a disorderly uniform!", + L"%s wears a disorderly uniform!",// TODO.Translate }; STR16 szCorpseTextStr[]= diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 75e29ee6..523f7dee 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -7431,14 +7431,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index cae74ea1..b4ecaa88 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -7419,14 +7419,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 5ef72bf6..5d1830d8 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -7405,14 +7405,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 830c287b..61c4bdb5 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -7246,14 +7246,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index f113b3b7..fab2958d 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -7418,14 +7418,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index be03fca5..8d85c763 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -7432,14 +7432,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 076e64bc..1da883a0 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -7384,14 +7384,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!", diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 35269383..0040c714 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -7435,14 +7435,14 @@ STR16 szCovertTextStr[]= L"%s was seen performing suspicious activities!", L"%s does not look like a civilian!", L"%s bleeding was discovered!", - L"%s is drunk and doesn't look like a soldier!", + L"%s is drunk and doesn't behave like a soldier!", L"On closer inspection, %s's disguise does not hold!", L"%s isn't supposed to be here!", L"%s isn't supposed to be here at this time!", L"%s was seen near a fresh corpse!", L"%s equipment raises a few eyebrows!", - L"%s is seen targeting a soldier!", - L"%s has seen through %s disguise!", + L"%s is seen targetting %s!", + L"%s has seen through %s's disguise!", L"No clothes item found in Items.xml!", L"This does not work with the old trait system!", L"Not enough APs!",