From fd445fe5edb3186f5e14c95e8a6a82613bca6105 Mon Sep 17 00:00:00 2001 From: Flugente Date: Fri, 24 Aug 2012 23:52:55 +0000 Subject: [PATCH] New feature: Covert ops - you can disguise members of your team as civilians or soldiers and, if careful, avoid detection by the enemy. - For more info, see http://www.bears-pit.com/board/ubbthreads.php/topics/309312/New_feature_Covert_operations.html#Post309312 - GameDir revision >= 1529 is needed for this feature - fixed a bug that could cause inventory items to be treated like armed bombs - bloodcats can now be skinned git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5529 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 7 + GameSettings.h | 7 + Init.h | 1 + Laptop/IMP Confirm.cpp | 14 +- Laptop/IMP Skill Trait.cpp | 68 ++- Laptop/IMP Skill Trait.h | 1 + Laptop/email.cpp | 6 + Laptop/personnel.cpp | 77 +++- MessageBoxScreen.h | 32 +- Strategic/XML_SectorNames.cpp | 62 +-- Tactical/Drugs And Alcohol.cpp | 94 ++-- Tactical/Faces.cpp | 7 + Tactical/Handle Items.cpp | 57 +++ Tactical/Handle Items.h | 2 + Tactical/Interface Enhanced.cpp | 25 +- Tactical/Interface Items.cpp | 8 +- Tactical/Interface Panels.cpp | 15 + Tactical/Item Types.h | 59 ++- Tactical/Items.cpp | 17 + Tactical/Items.h | 2 + Tactical/Overhead.cpp | 8 +- Tactical/Rotting Corpses.cpp | 549 +++++++++++++++++++++-- Tactical/Rotting Corpses.h | 19 +- Tactical/Soldier Ani.cpp | 5 +- Tactical/Soldier Control.cpp | 760 +++++++++++++++++++++++++++++++- Tactical/Soldier Control.h | 38 +- Tactical/Soldier Profile.cpp | 16 +- Tactical/SoldierTooltips.cpp | 6 +- Tactical/Turn Based Input.cpp | 2 +- Tactical/Weapons.cpp | 70 ++- Tactical/Weapons.h | 48 +- Tactical/XML_AmmoTypes.cpp | 72 +-- Tactical/opplist.cpp | 80 ++-- Tactical/soldier profile type.h | 11 +- TacticalAI/Knowledge.cpp | 9 + Utils/Text.h | 6 +- Utils/_ChineseText.cpp | 2 + Utils/_DutchText.cpp | 2 + Utils/_EnglishText.cpp | 21 +- Utils/_FrenchText.cpp | 2 + Utils/_GermanText.cpp | 2 + Utils/_ItalianText.cpp | 2 + Utils/_Ja25ChineseText.cpp | 12 + Utils/_Ja25DutchText.cpp | 12 + Utils/_Ja25DutchText.h | 1 + Utils/_Ja25EnglishText.cpp | 13 +- Utils/_Ja25EnglishText.h | 1 + Utils/_Ja25FrenchText.cpp | 12 + Utils/_Ja25FrenchText.h | 1 + Utils/_Ja25GermanText.cpp | 12 + Utils/_Ja25GermanText.h | 1 + Utils/_Ja25ItalianText.cpp | 12 + Utils/_Ja25ItalianText.h | 1 + Utils/_Ja25PolishText.cpp | 12 + Utils/_Ja25PolishText.h | 1 + Utils/_Ja25RussianText.cpp | 12 + Utils/_Ja25RussianText.h | 1 + Utils/_Ja25TaiwaneseText.cpp | 12 + Utils/_Ja25TaiwaneseText.h | 1 + Utils/_PolishText.cpp | 2 + Utils/_RussianText.cpp | 2 + Utils/_TaiwaneseText.cpp | 2 + 62 files changed, 2151 insertions(+), 263 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index c12da750..c59d44a3 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2142,6 +2142,12 @@ void LoadSkillTraitsExternalSettings() gSkillTraitValues.fSCPreventsBloodcatsAmbushes = iniReader.ReadBoolean("Scouting","PREVENTS_BLOODCATS_AMBUSHES", TRUE); gSkillTraitValues.fSCThrowMessageIfAmbushPrevented = iniReader.ReadBoolean("Scouting","SHOW_MESSAGE_IF_AMBUSH_PREVENTED", TRUE); + // Flugente: COVERT OPS + gSkillTraitValues.sCOMeleeCTHBonus = iniReader.ReadInteger("Covert Ops","COVERT_MELEE_CTH_BONUS", 40, 0, 200); + gSkillTraitValues.sCoMeleeInstakillBonus = iniReader.ReadInteger("Covert Ops","COVERT_MELEE_INSTAKILL_BONUS", 35, 0, 200); + gSkillTraitValues.sCODisguiseAPReduction = iniReader.ReadInteger("Covert Ops","COVERT_DISGUISE_PERCENT_AP_REDUCTION", 20, 0, 100); + gSkillTraitValues.sCOCloseDetectionRange = iniReader.ReadInteger("Covert Ops","COVERT_CLOSE_DETECTION_RANGE", 10, 0, 100); + gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse = iniReader.ReadInteger("Covert Ops","COVERT_CLOSE_DETECTION_RANGE_SOLDIER_CORPSE", 5, 0, 100); } //DBrot: Grids void LoadModSettings(){ @@ -2441,6 +2447,7 @@ void LoadGameAPBPConstants() APBPConstants[AP_EAT] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_EAT",80),80); APBPConstants[AP_CLEANINGKIT] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_CLEANINGKIT",80),80); APBPConstants[AP_INVENTORY_EXPLOSIVE_ACTIVATE] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_INVENTORY_EXPLOSIVE_ACTIVATE",20),20); + APBPConstants[AP_DISGUISE] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_DISGUISE",80),80); SetupMaxActionPointsAnimation(); #undef ReadInteger diff --git a/GameSettings.h b/GameSettings.h index 17c3f68e..588a97d8 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1418,6 +1418,13 @@ typedef struct BOOLEAN fSCPreventsBloodcatsAmbushes; BOOLEAN fSCThrowMessageIfAmbushPrevented; + // COVERT OPS + INT16 sCOMeleeCTHBonus; + INT16 sCoMeleeInstakillBonus; + INT16 sCODisguiseAPReduction; + INT16 sCOCloseDetectionRange; + INT16 sCOCloseDetectionRangeSoldierCorpse; + } SKILL_TRAIT_VALUES; // HEADROCK HAM 4: Constants used as coefficients by the various parts of the new CTH system. diff --git a/Init.h b/Init.h index 4b18f71b..dafbff07 100644 --- a/Init.h +++ b/Init.h @@ -201,6 +201,7 @@ AP_FORTIFICATION, AP_EAT, AP_CLEANINGKIT, AP_INVENTORY_EXPLOSIVE_ACTIVATE, +AP_DISGUISE, TOTAL_APBP_VALUES }; #endif diff --git a/Laptop/IMP Confirm.cpp b/Laptop/IMP Confirm.cpp index f5faaf61..571907be 100644 --- a/Laptop/IMP Confirm.cpp +++ b/Laptop/IMP Confirm.cpp @@ -731,6 +731,17 @@ void GiveItemsToPC( UINT8 ubProfileId ) GiveIMPRandomItems(pProfile, IMP_DOCTOR); } } + if (PROFILE_HAS_SKILL_TRAIT(ubProfileId, COVERT_NT)) + { + if( gGameExternalOptions.fExpertsGetDifferentChoices && PROFILE_HAS_EXPERT_TRAIT(ubProfileId, COVERT_NT)) + { + GiveIMPRandomItems(pProfile,IMP_COVERT_EXP); + } + else + { + GiveIMPRandomItems(pProfile, IMP_COVERT); + } + } // MINOR TRAITS if (PROFILE_HAS_SKILL_TRAIT(ubProfileId, AMBIDEXTROUS_NT)) { @@ -771,8 +782,7 @@ void GiveItemsToPC( UINT8 ubProfileId ) if (PROFILE_HAS_SKILL_TRAIT(ubProfileId, SCOUTING_NT)) { GiveIMPRandomItems(pProfile,IMP_SCOUTING); - } - + } } else { diff --git a/Laptop/IMP Skill Trait.cpp b/Laptop/IMP Skill Trait.cpp index 13eecbf5..59e30fd7 100644 --- a/Laptop/IMP Skill Trait.cpp +++ b/Laptop/IMP Skill Trait.cpp @@ -62,6 +62,7 @@ #define IMP_SKILL_TRAIT__GREY_BOX_OFFSET_X 5 #define IMP_SKILL_TRAIT__GREY_BOX_OFFSET_Y 7 +#define NEWTRAIT_EXPERT_OFFSET 12 // Flugente: offset between normal and expert trait strings //******************************************************************* // @@ -858,7 +859,7 @@ void IMPSkillTraitDisplaySkills() if( gGameOptions.fNewTraitSystem ) { if ( gfSkillTraitQuestions[ uiCnt ] && gfSkillTraitQuestions2[ uiCnt ] && uiCnt != IMP_SKILL_TRAITS_NEW_MAJOR_NONE ) - DrawTextToScreen( gzIMPSkillTraitsTextNewMajor[ uiCnt+11 ], usPosX, usPosY, 0, IMP_SKILL_TRAIT__FONT, IMP_SKILL_TRAIT__COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + DrawTextToScreen( gzIMPSkillTraitsTextNewMajor[ uiCnt+12 ], usPosX, usPosY, 0, IMP_SKILL_TRAIT__FONT, IMP_SKILL_TRAIT__COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); else DrawTextToScreen( gzIMPSkillTraitsTextNewMajor[ uiCnt ], usPosX, usPosY, 0, IMP_SKILL_TRAIT__FONT, IMP_SKILL_TRAIT__COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); } @@ -1075,6 +1076,10 @@ void AddSelectedSkillsToSkillsList() AddSkillToSkillList( DOCTOR_NT ); break; + case IMP_SKILL_TRAITS_NEW_COVERT: + AddSkillToSkillList( COVERT_NT ); + break; + default: break; } @@ -1190,6 +1195,10 @@ void AddSelectedSkillsToSkillsList() AddSkillToSkillList( DOCTOR_NT ); break; + case IMP_SKILL_TRAITS_NEW_COVERT: + AddSkillToSkillList( COVERT_NT ); + break; + default: break; } @@ -1560,6 +1569,13 @@ INT32 LeadershipRequiredDueToMajorSkills( void ) else return (35); } + else if (gfSkillTraitQuestions[ IMP_SKILL_TRAITS_NEW_COVERT ]) + { + if (gfSkillTraitQuestions2[ IMP_SKILL_TRAITS_NEW_COVERT ]) + return (45); + else + return (30); + } // Check the second skill trait else if (gfSkillTraitQuestions2[ IMP_SKILL_TRAITS_NEW_SQUADLEADER ]) { @@ -1588,6 +1604,13 @@ INT32 WisdomRequiredDueToMajorSkills( void ) else return (60); } + else if (gfSkillTraitQuestions[ IMP_SKILL_TRAITS_NEW_COVERT ]) + { + if (gfSkillTraitQuestions2[ IMP_SKILL_TRAITS_NEW_COVERT ]) + return (70); + else + return (65); + } else if (gfSkillTraitQuestions[ IMP_SKILL_TRAITS_NEW_HEAVY_WEAPONS ]) { if (gfSkillTraitQuestions2[ IMP_SKILL_TRAITS_NEW_HEAVY_WEAPONS ]) @@ -2186,7 +2209,7 @@ void AssignSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLevel ) } if( gSkillTraitValues.ubSLEffectiveLevelInRadius != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[1], ( gSkillTraitValues.ubSLEffectiveLevelInRadius * (fExpertLevel ? 2 : 1)), (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[1], ( gSkillTraitValues.ubSLEffectiveLevelInRadius * (fExpertLevel ? 2 : 1)), (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubSLEffectiveLevelAsStandby != 0 ) @@ -2201,7 +2224,7 @@ void AssignSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLevel ) } if( gSkillTraitValues.ubSLOverallSuppresionBonusPercent != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[3], ( gSkillTraitValues.ubSLOverallSuppresionBonusPercent * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[3], ( gSkillTraitValues.ubSLOverallSuppresionBonusPercent * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubSLMoraleGainBonus != 0 ) @@ -2231,12 +2254,12 @@ void AssignSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLevel ) if( gSkillTraitValues.ubSLFearResistance != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[9], ( gSkillTraitValues.ubSLFearResistance * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[9], ( gSkillTraitValues.ubSLFearResistance * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubSLDeathMoralelossMultiplier != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[10], (1 + ( gSkillTraitValues.ubSLDeathMoralelossMultiplier * (fExpertLevel ? 2 : 1))), (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[10], (1 + ( gSkillTraitValues.ubSLDeathMoralelossMultiplier * (fExpertLevel ? 2 : 1))), (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); } break; @@ -2281,17 +2304,17 @@ void AssignSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLevel ) } if( gSkillTraitValues.ubTECtHControlledRobotBonus != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[7], ( gSkillTraitValues.ubTECtHControlledRobotBonus * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[7], ( gSkillTraitValues.ubTECtHControlledRobotBonus * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubTETraitsNumToRepairRobot == 2 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); } else if( gSkillTraitValues.ubTETraitsNumToRepairRobot == 1 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + 11] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzIMPSkillTraitsTextNewMajor[ubTraitNumber + NEWTRAIT_EXPERT_OFFSET] : gzIMPSkillTraitsTextNewMajor[ubTraitNumber])); wcscat( apStr, atStr ); if( gSkillTraitValues.ubTERepairRobotPenaltyReduction != 0 && fExpertLevel) @@ -2353,6 +2376,35 @@ void AssignSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLevel ) } break; } + case IMP_SKILL_TRAITS_NEW_COVERT: + { + swprintf( apStr, L"" ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[0]); + wcscat( apStr, atStr ); + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[1]); + wcscat( apStr, atStr ); + + if ( !fExpertLevel ) + { + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[2], gSkillTraitValues.sCOCloseDetectionRange); + wcscat( apStr, atStr ); + } + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[3], gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse); + wcscat( apStr, atStr ); + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[4], ( gSkillTraitValues.sCOMeleeCTHBonus * (fExpertLevel ? 2 : 1)), L"%"); + wcscat( apStr, atStr ); + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[5], ( gSkillTraitValues.sCoMeleeInstakillBonus * (fExpertLevel ? 2 : 1)), L"%"); + wcscat( apStr, atStr ); + + INT16 apreduction = ( gSkillTraitValues.sCODisguiseAPReduction * (fExpertLevel ? 2 : 1)); + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[6], apreduction, L"%"); + wcscat( apStr, atStr ); + break; + } case IMP_SKILL_TRAITS_NEW_MAJOR_NONE: { swprintf( apStr, gzIMPMajorTraitsHelpTextsNone[0] ); diff --git a/Laptop/IMP Skill Trait.h b/Laptop/IMP Skill Trait.h index 34bf42f0..7f537213 100644 --- a/Laptop/IMP Skill Trait.h +++ b/Laptop/IMP Skill Trait.h @@ -41,6 +41,7 @@ enum IMP_SKILL_TRAITS_NEW_SQUADLEADER, IMP_SKILL_TRAITS_NEW_TECHNICIAN, IMP_SKILL_TRAITS_NEW_DOCTOR, + IMP_SKILL_TRAITS_NEW_COVERT, IMP_SKILL_TRAITS_NEW_MAJOR_NONE, IMP_SKILL_TRAITS_NEW_NUMBER_MAJOR_SKILLS, }; diff --git a/Laptop/email.cpp b/Laptop/email.cpp index 5fdf97df..bb6af2bb 100644 --- a/Laptop/email.cpp +++ b/Laptop/email.cpp @@ -4650,6 +4650,12 @@ void HandleIMPCharProfileResultsMessage( void) wcscpy(pString, MissingIMPSkillsDescriptions[10]); AddEmailRecordToList( pString ); } + // Covert ops + if ( ProfileHasSkillTrait( iCurrentIMPSlot, COVERT_NT ) > 0 ) + { + wcscpy(pString, MissingIMPSkillsDescriptions[11]); + AddEmailRecordToList( pString ); + } } else { diff --git a/Laptop/personnel.cpp b/Laptop/personnel.cpp index 0de7a775..fc313cae 100644 --- a/Laptop/personnel.cpp +++ b/Laptop/personnel.cpp @@ -1769,7 +1769,7 @@ void DisplayCharStats(INT32 iId, INT32 iSlot) { if ( ProfileHasSkillTrait( Menptr[iId].ubProfile, ubCnt ) == 2 ) { - ubTempSkillArray[bNumSkillTraits] = (ubCnt + 19); + ubTempSkillArray[bNumSkillTraits] = (ubCnt + NEWTRAIT_MERCSKILL_EXPERTOFFSET); bNumSkillTraits++; } else if ( ProfileHasSkillTrait( Menptr[iId].ubProfile, ubCnt ) == 1 ) @@ -1816,9 +1816,18 @@ void DisplayCharStats(INT32 iId, INT32 iSlot) } else { - swprintf( sString, L"%s", gzMercSkillTextNew[ ubTempSkillArray[ubCnt] ] ); + // Flugente: as the whole trait display is fubar, we have to to a special treatment here for new traits + UINT8 display1 = ubTempSkillArray[ubCnt]; + if ( display1 > SCOUTING_NT + NEWTRAIT_MERCSKILL_EXPERTOFFSET ) + display1 -= NUM_MINOR_TRAITS; + else if ( display1 >= AMBIDEXTROUS_NT && display1 <= SCOUTING_NT ) + display1++; + else if ( display1 == NEWTRAIT_MERCSKILL_EXPERTOFFSET ) + display1 -= NUM_MINOR_TRAITS; + + swprintf( sString, L"%s", gzMercSkillTextNew[ display1 ] ); - if ( ubTempSkillArray[ubCnt] > 19 ) + if ( ubTempSkillArray[ubCnt] > NEWTRAIT_MERCSKILL_EXPERTOFFSET ) { FindFontRightCoordinates((INT16)(pPersonnelScreenPoints[19].x+(iSlot*TEXT_BOX_WIDTH)),0,TEXT_BOX_WIDTH-20,0,sString, FONT10ARIALBOLD, &sX, &sY); @@ -1857,15 +1866,28 @@ void DisplayCharStats(INT32 iId, INT32 iSlot) MSYS_NO_CURSOR, MSYS_NO_CALLBACK, NULL ); MSYS_AddRegion( &gSkillTraitHelpTextRegion[ubCnt] ); fAddedTraitRegion[ubCnt] = TRUE; - // Assign the text - AssignPersonnelSkillTraitHelpText( ((ubTempSkillArray[ubCnt] > 19) ? (ubTempSkillArray[ubCnt]-19) : ubTempSkillArray[ubCnt]), ((ubTempSkillArray[ubCnt] > 19) ? TRUE : FALSE), Menptr[iId].ubProfile, ubCnt ); + + // Assign the text + UINT8 traitnr = ubTempSkillArray[ubCnt]; + UINT8 mlas = NEWTRAIT_MERCSKILL_EXPERTOFFSET; + if ( traitnr > mlas ) + { + traitnr = traitnr - mlas; + } + + // Flugente: as the whole trait display is fubar, we have to to a special treatment here for new traits + UINT8 display2 = ubTempSkillArray[ubCnt]; + if ( display2 == NUM_MAJOR_TRAITS ) + display2 += NUM_MINOR_TRAITS; + + AssignPersonnelSkillTraitHelpText( traitnr, ((ubTempSkillArray[ubCnt] > NEWTRAIT_MERCSKILL_EXPERTOFFSET) ? TRUE : FALSE), Menptr[iId].ubProfile, ubCnt ); } } // if we have more skills than we can display, show "more" and create a tooltip box with the rest of them if ( fDisplayMoreTraits ) { - swprintf( sString, L"%s", gzMercSkillTextNew[ 29 ]); // dispaly "More..." + swprintf( sString, L"%s", gzMercSkillTextNew[ NEWTRAIT_MERCSKILL_OFFSET_ALL + 1 ]); // dispaly "More..." FindFontRightCoordinates((INT16)(pPersonnelScreenPoints[19].x+(iSlot*TEXT_BOX_WIDTH)),0,TEXT_BOX_WIDTH-20,0,sString, PERS_FONT, &sX, &sY); if( sX <= iMinimumX ) { @@ -7444,7 +7466,7 @@ void AssignPersonnelSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLeve } if( gSkillTraitValues.ubSLEffectiveLevelInRadius != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[1], ( gSkillTraitValues.ubSLEffectiveLevelInRadius * (fExpertLevel ? 2 : 1)), (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[1], ( gSkillTraitValues.ubSLEffectiveLevelInRadius * (fExpertLevel ? 2 : 1)), (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubSLEffectiveLevelAsStandby != 0 ) @@ -7459,7 +7481,7 @@ void AssignPersonnelSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLeve } if( gSkillTraitValues.ubSLOverallSuppresionBonusPercent != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[3], ( gSkillTraitValues.ubSLOverallSuppresionBonusPercent * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[3], ( gSkillTraitValues.ubSLOverallSuppresionBonusPercent * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubSLMoraleGainBonus != 0 ) @@ -7489,12 +7511,12 @@ void AssignPersonnelSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLeve if( gSkillTraitValues.ubSLFearResistance != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[9], ( gSkillTraitValues.ubSLFearResistance * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[9], ( gSkillTraitValues.ubSLFearResistance * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubSLDeathMoralelossMultiplier != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[10], (1 + ( gSkillTraitValues.ubSLDeathMoralelossMultiplier * (fExpertLevel ? 2 : 1))), (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsSquadleader[10], (1 + ( gSkillTraitValues.ubSLDeathMoralelossMultiplier * (fExpertLevel ? 2 : 1))), (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); } break; @@ -7539,17 +7561,17 @@ void AssignPersonnelSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLeve } if( gSkillTraitValues.ubTECtHControlledRobotBonus != 0 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[7], ( gSkillTraitValues.ubTECtHControlledRobotBonus * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[7], ( gSkillTraitValues.ubTECtHControlledRobotBonus * (fExpertLevel ? 2 : 1)), L"%", (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); } if( gSkillTraitValues.ubTETraitsNumToRepairRobot == 2 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); } else if( gSkillTraitValues.ubTETraitsNumToRepairRobot == 1 ) { - swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + 19] : gzMercSkillTextNew[ubTraitNumber])); + swprintf( atStr, gzIMPMajorTraitsHelpTextsTechnician[8], (fExpertLevel ? gzMercSkillTextNew[ubTraitNumber + NEWTRAIT_MERCSKILL_EXPERTOFFSET] : gzMercSkillTextNew[ubTraitNumber])); wcscat( apStr, atStr ); if( gSkillTraitValues.ubTERepairRobotPenaltyReduction != 0 && fExpertLevel) @@ -7983,6 +8005,35 @@ void AssignPersonnelSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLeve } break; } + case COVERT_NT: + { + swprintf( apStr, L"" ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[0]); + wcscat( apStr, atStr ); + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[1]); + wcscat( apStr, atStr ); + + if ( !fExpertLevel ) + { + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[2], gSkillTraitValues.sCOCloseDetectionRange); + wcscat( apStr, atStr ); + } + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[3], gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse); + wcscat( apStr, atStr ); + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[4], ( gSkillTraitValues.sCOMeleeCTHBonus * (fExpertLevel ? 2 : 1)), L"%"); + wcscat( apStr, atStr ); + + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[5], ( gSkillTraitValues.sCoMeleeInstakillBonus * (fExpertLevel ? 2 : 1)), L"%"); + wcscat( apStr, atStr ); + + INT16 apreduction = ( gSkillTraitValues.sCODisguiseAPReduction * (fExpertLevel ? 2 : 1)); + swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[6], apreduction, L"%"); + wcscat( apStr, atStr ); + break; + } case NO_SKILLTRAIT_NT: { swprintf( apStr, L"" ); diff --git a/MessageBoxScreen.h b/MessageBoxScreen.h index 72e8ebb3..5aa86eeb 100644 --- a/MessageBoxScreen.h +++ b/MessageBoxScreen.h @@ -5,17 +5,17 @@ // Message box flags #define MSG_BOX_FLAG_USE_CENTERING_RECT 0x0001 // Pass in a rect to center in -#define MSG_BOX_FLAG_OK 0x0002 // Displays OK button -#define MSG_BOX_FLAG_YESNO 0x0004 // Displays YES NO buttons -#define MSG_BOX_FLAG_CANCEL 0x0008 // Displays YES NO buttons -#define MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS 0x0010 // Displays four numbered buttons, 1-4 -#define MSG_BOX_FLAG_YESNOCONTRACT 0x0020 // yes no and contract buttons -#define MSG_BOX_FLAG_OKCONTRACT 0x0040 // ok and contract buttons -#define MSG_BOX_FLAG_YESNOLIE 0x0080 // ok and contract buttons -#define MSG_BOX_FLAG_CONTINUESTOP 0x0100 // continue stop box -#define MSG_BOX_FLAG_OKSKIP 0x0200 // Displays ok or skip (meanwhile) buttons -#define MSG_BOX_FLAG_GENERICCONTRACT 0x0400 // displays contract buttoin + 2 user-defined text buttons -#define MSG_BOX_FLAG_GENERIC 0x0800 // 2 user-defined text buttons +#define MSG_BOX_FLAG_OK 0x0002 // Displays OK button +#define MSG_BOX_FLAG_YESNO 0x0004 // Displays YES NO buttons +#define MSG_BOX_FLAG_CANCEL 0x0008 // Displays YES NO buttons +#define MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS 0x0010 // Displays four numbered buttons, 1-4 +#define MSG_BOX_FLAG_YESNOCONTRACT 0x0020 // yes no and contract buttons +#define MSG_BOX_FLAG_OKCONTRACT 0x0040 // ok and contract buttons +#define MSG_BOX_FLAG_YESNOLIE 0x0080 // ok and contract buttons +#define MSG_BOX_FLAG_CONTINUESTOP 0x0100 // continue stop box +#define MSG_BOX_FLAG_OKSKIP 0x0200 // Displays ok or skip (meanwhile) buttons +#define MSG_BOX_FLAG_GENERICCONTRACT 0x0400 // displays contract buttoin + 2 user-defined text buttons +#define MSG_BOX_FLAG_GENERIC 0x0800 // 2 user-defined text buttons // OJW - Adding text chatbox #define MSG_BOX_FLAG_INPUTBOX 0x1000 // has a text input field // Flugente - added boxes for simultaneous defuse/detonate mechanism @@ -23,11 +23,11 @@ #define MSG_BOX_FLAG_SIXTEEN_NUMBERED_BUTTONS 0x4000 // Displays sixteen numbered buttons, 1-16, for setting up a tripwire network and choosing both a detonation and defusing frequency // message box return codes -#define MSG_BOX_RETURN_OK 1 // ENTER or on OK button -#define MSG_BOX_RETURN_YES 2 // ENTER or YES button -#define MSG_BOX_RETURN_NO 3 // ESC, Right Click or NO button -#define MSG_BOX_RETURN_CONTRACT 4 // contract button -#define MSG_BOX_RETURN_LIE 5 // LIE BUTTON +#define MSG_BOX_RETURN_OK 1 // ENTER or on OK button +#define MSG_BOX_RETURN_YES 2 // ENTER or YES button +#define MSG_BOX_RETURN_NO 3 // ESC, Right Click or NO button +#define MSG_BOX_RETURN_CONTRACT 4 // contract button +#define MSG_BOX_RETURN_LIE 5 // LIE BUTTON // message box style flags enum diff --git a/Strategic/XML_SectorNames.cpp b/Strategic/XML_SectorNames.cpp index f69bbb1b..738832e1 100644 --- a/Strategic/XML_SectorNames.cpp +++ b/Strategic/XML_SectorNames.cpp @@ -45,8 +45,9 @@ typedef struct CHAR16 szCurDetailedUnexploredName[MAX_SECTOR_NAME_LENGTH]; CHAR16 szCurExploredName[MAX_SECTOR_NAME_LENGTH]; CHAR16 szCurDetailedExploredName[MAX_SECTOR_NAME_LENGTH]; - UINT8 sWaterType; - UINT16 usNaturalDirt; + UINT8 sWaterType; // Food System + UINT16 usNaturalDirt; // Dirt System + UINT8 usCurfewValue; // Covert Ops UINT32 currentDepth; UINT32 maxReadDepth; } SectorNameParseData; @@ -80,7 +81,12 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char ** SectorExternalData[x][1].usNaturalDirt = 0; SectorExternalData[x][2].usNaturalDirt = 0; SectorExternalData[x][3].usNaturalDirt = 0; - + + SectorExternalData[x][0].usCurfewValue = 0; + SectorExternalData[x][1].usCurfewValue = 0; + SectorExternalData[x][2].usCurfewValue = 0; + SectorExternalData[x][3].usCurfewValue = 0; + if (Sector_Level == 0 ) { gzSectorNames[x][0][0]=0; @@ -128,7 +134,8 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char ** strcmp(name, "szExploredName") == 0 || strcmp(name, "szDetailedExploredName") == 0 || strcmp(name, "sWaterType") == 0 || - strcmp(name, "usNaturalDirt") == 0 )) + strcmp(name, "usNaturalDirt") == 0 || + strcmp(name, "usCurfewValue") == 0 )) { pData->curElement = SECTORNAME_ELEMENT; @@ -180,16 +187,6 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) wcscpy(gzSectorNames[ubSectorId][1], pData->szCurDetailedUnexploredName); wcscpy(gzSectorNames[ubSectorId][2], pData->szCurExploredName); wcscpy(gzSectorNames[ubSectorId][3], pData->szCurDetailedExploredName); - - SectorExternalData[ubSectorId][0].usWaterType = pData->sWaterType; - SectorExternalData[ubSectorId][1].usWaterType = pData->sWaterType; - SectorExternalData[ubSectorId][2].usWaterType = pData->sWaterType; - SectorExternalData[ubSectorId][3].usWaterType = pData->sWaterType; - - SectorExternalData[ubSectorId][0].usNaturalDirt = pData->usNaturalDirt; - SectorExternalData[ubSectorId][1].usNaturalDirt = pData->usNaturalDirt; - SectorExternalData[ubSectorId][2].usNaturalDirt = pData->usNaturalDirt; - SectorExternalData[ubSectorId][3].usNaturalDirt = pData->usNaturalDirt; } // moved to lua //else if (Sector_Level == 1 ) @@ -221,17 +218,7 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) wcscpy(gzSectorNames[ubSectorId][0], pData->szCurUnexploredName); wcscpy(gzSectorNames[ubSectorId][1], pData->szCurDetailedUnexploredName); wcscpy(gzSectorNames[ubSectorId][2], pData->szCurExploredName); - wcscpy(gzSectorNames[ubSectorId][3], pData->szCurDetailedExploredName); - - SectorExternalData[ubSectorId][0].usWaterType = pData->sWaterType; - SectorExternalData[ubSectorId][1].usWaterType = pData->sWaterType; - SectorExternalData[ubSectorId][2].usWaterType = pData->sWaterType; - SectorExternalData[ubSectorId][3].usWaterType = pData->sWaterType; - - SectorExternalData[ubSectorId][0].usNaturalDirt = pData->usNaturalDirt; - SectorExternalData[ubSectorId][1].usNaturalDirt = pData->usNaturalDirt; - SectorExternalData[ubSectorId][2].usNaturalDirt = pData->usNaturalDirt; - SectorExternalData[ubSectorId][3].usNaturalDirt = pData->usNaturalDirt; + wcscpy(gzSectorNames[ubSectorId][3], pData->szCurDetailedExploredName); } // moved to lua //else if (Sector_Level == 1 ) @@ -256,6 +243,26 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) // wcscpy(gzSectorUndergroundNames3[ubSectorId][3], pData->szCurDetailedExploredName); //} } + + SectorExternalData[ubSectorId][0].usWaterType = pData->sWaterType; + SectorExternalData[ubSectorId][1].usWaterType = pData->sWaterType; + SectorExternalData[ubSectorId][2].usWaterType = pData->sWaterType; + SectorExternalData[ubSectorId][3].usWaterType = pData->sWaterType; + + SectorExternalData[ubSectorId][0].usNaturalDirt = pData->usNaturalDirt; + SectorExternalData[ubSectorId][1].usNaturalDirt = pData->usNaturalDirt; + SectorExternalData[ubSectorId][2].usNaturalDirt = pData->usNaturalDirt; + SectorExternalData[ubSectorId][3].usNaturalDirt = pData->usNaturalDirt; + + SectorExternalData[ubSectorId][0].usCurfewValue = pData->usCurfewValue; + SectorExternalData[ubSectorId][1].usCurfewValue = pData->usCurfewValue; + SectorExternalData[ubSectorId][2].usCurfewValue = pData->usCurfewValue; + SectorExternalData[ubSectorId][3].usCurfewValue = pData->usCurfewValue; + + // clean up values afterwards + pData->sWaterType = 0; + pData->usNaturalDirt = 100; + pData->usCurfewValue = 0; } } @@ -315,6 +322,11 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) pData->curElement = SECTORNAME_ELEMENT_SECTOR; pData->usNaturalDirt = (UINT16) atoi(pData->szCharData); } + else if(strcmp(name, "usCurfewValue") == 0) + { + pData->curElement = SECTORNAME_ELEMENT_SECTOR; + pData->usCurfewValue = (UINT16) atoi(pData->szCharData); + } pData->maxReadDepth--; } diff --git a/Tactical/Drugs And Alcohol.cpp b/Tactical/Drugs And Alcohol.cpp index 6d1caf5a..a538d4fa 100644 --- a/Tactical/Drugs And Alcohol.cpp +++ b/Tactical/Drugs And Alcohol.cpp @@ -387,67 +387,65 @@ void HandleEndTurnDrugAdjustments( SOLDIERTYPE *pSoldier ) } // Flugente: always do the following checks. Thereby, if the effect runs out, our stats will be back to normal - // only do the checks for the player team, as soldiers do not have a gMercProfiles - if ( pSoldier->bTeam == gbPlayerNum) + //////////////// STRENGTH //////////////// + pSoldier->bExtraStrength = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_STRENGTH ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_STRENGTH ]; + + //////////////// DEXTERITY //////////////// + pSoldier->bExtraDexterity = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_DEXTERITY ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_DEXTERITY ]; + + //////////////// AGILITY //////////////// + pSoldier->bExtraAgility = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_AGILITY ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_AGILITY ]; + + //////////////// WISDOM //////////////// + pSoldier->bExtraWisdom = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_WISDOM ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_WISDOM ]; + + // if our sideeffect count is 1 (which should occur a while AFTER we took the drug), we suddenly become blind for a few turns... + if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_BLIND ] == 0 && pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_BLIND ] == 1 ) { - //////////////// STRENGTH //////////////// - pSoldier->bExtraStrength = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_STRENGTH ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_STRENGTH ]; + pSoldier->bBlindedCounter = 3; + } - //////////////// DEXTERITY //////////////// - pSoldier->bExtraDexterity = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_DEXTERITY ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_DEXTERITY ]; + // if our sideeffect count is 1 (which should occur a while AFTER we took the drug), we get a heart-attack and get knocked out... + if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_KNOCKOUT ] == 0 && pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_KNOCKOUT ] == 1 ) + { + // Keel over... + DeductPoints( pSoldier, 0, 20000 ); + } - //////////////// AGILITY //////////////// - pSoldier->bExtraAgility = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_AGILITY ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_AGILITY ]; + // if we have a life damaging effect, deduct life points + if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_LIFEDAMAGE ] == 0 && pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_LIFEDAMAGE ] > 0 ) + { + pSoldier->EVENT_SoldierGotHit( 0, 400, 0, pSoldier->ubDirection, 320, NOBODY , FIRE_WEAPON_NO_SPECIAL, pSoldier->bAimShotLocation, 0, -1 ); - //////////////// WISDOM //////////////// - pSoldier->bExtraWisdom = pSoldier->drugs.bDrugEffect[ DRUG_TYPE_WISDOM ] - pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_WISDOM ]; - - // if our sideeffect count is 1 (which should occur a while AFTER we took the drug), we suddenly become blind for a few turns... - if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_BLIND ] == 0 && pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_BLIND ] == 1 ) + /*if ( pSoldier->stats.bLife > OKLIFE ) { - pSoldier->bBlindedCounter = 3; - } - - // if our sideeffect count is 1 (which should occur a while AFTER we took the drug), we get a heart-attack and get knocked out... - if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_KNOCKOUT ] == 0 && pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_KNOCKOUT ] == 1 ) - { - // Keel over... - DeductPoints( pSoldier, 0, 20000 ); - } - - // if we have a life damaging effect, deduct life points - if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_LIFEDAMAGE ] == 0 && pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_LIFEDAMAGE ] > 0 ) - { - if ( pSoldier->stats.bLife > OKLIFE ) - { - INT8 lifepointdamage = pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_LIFEDAMAGE ]; + INT8 lifepointdamage = pSoldier->drugs.bDrugSideEffect[ DRUG_TYPE_LIFEDAMAGE ]; - INT8 applieddamage = pSoldier->stats.bLife; + INT8 applieddamage = pSoldier->stats.bLife; - pSoldier->stats.bLife = max(OKLIFE - 1, pSoldier->stats.bLife - lifepointdamage); + pSoldier->stats.bLife = max(OKLIFE - 8, pSoldier->stats.bLife - lifepointdamage); - applieddamage -= pSoldier->stats.bLife; + applieddamage -= pSoldier->stats.bLife; - pSoldier->iHealableInjury += (applieddamage * 100); - } - } + pSoldier->iHealableInjury += (applieddamage * 100); + }*/ + } - // if we took an antidote, reduce poisoning - if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_CUREPOISON ] > 0 ) + // if we took an antidote, reduce poisoning + if ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_CUREPOISON ] > 0 ) + { + if ( pSoldier->bPoisonSum > 0 ) { - if ( pSoldier->bPoisonSum > 0 ) + if ( pSoldier->bPoisonBleeding > 0 ) { - if ( pSoldier->bPoisonBleeding > 0 ) - { - pSoldier->bPoisonBleeding--; - } - else if ( pSoldier->bPoisonLife > 0 ) - { - pSoldier->bPoisonLife--; - } - - pSoldier->bPoisonSum--; + pSoldier->bPoisonBleeding--; } + else if ( pSoldier->bPoisonLife > 0 ) + { + pSoldier->bPoisonLife--; + } + + pSoldier->bPoisonSum--; } } diff --git a/Tactical/Faces.cpp b/Tactical/Faces.cpp index 4669f464..a4cd5fe4 100644 --- a/Tactical/Faces.cpp +++ b/Tactical/Faces.cpp @@ -2318,6 +2318,13 @@ void HandleRenderFaceAdjustments( FACETYPE *pFace, BOOLEAN fDisplayBuffer, BOOLE } } + // Flugente: add an icon if we are currently in disguise + if ( MercPtrs[ pFace->ubSoldierID ]->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) + { + DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 22 ); + bNumRightIcons++; + } + switch( pSoldier->bAssignment ) { case DOCTOR: diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 15e05c89..e52a1fc7 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -101,6 +101,7 @@ static INT8 bTempFrequency; void BombMessageBoxCallBack( UINT8 ubExitValue ); void TacticalFunctionSelectionMessageBoxCallBack( UINT8 ubExitValue ); // Flugente: callback after deciding what tactical function to use +void CorpseMessageBoxCallBack( UINT8 ubExitValue ); // Flugente: callback after deciding what to do with a corpse void BoobyTrapMessageBoxCallBack( UINT8 ubExitValue ); void SwitchMessageBoxCallBack( UINT8 ubExitValue ); void BoobyTrapDialogueCallBack( void ); @@ -133,6 +134,8 @@ void StartBombMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo ); void StartTacticalFunctionSelectionMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo, INT8 bLevel ); // added by Flugente void CleanWeapons(); +void StartCorpseMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo, INT8 bLevel ); // added by Flugente + BOOLEAN HandleCheckForBadChangeToGetThrough( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, INT32 sTargetGridNo , INT8 bLevel ) { BOOLEAN fBadChangeToGetThrough = FALSE; @@ -1647,6 +1650,11 @@ void HandleTacticalFunctionSelection( SOLDIERTYPE *pSoldier, INT32 sGridNo ) } +void HandleSoldierUseCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) +{ + StartCorpseMessageBox( pSoldier, sGridNo, bLevel ); +} + void SoldierHandleDropItem( SOLDIERTYPE *pSoldier ) { // LOOK IN PANDING DATA FOR ITEM TO DROP, AND LOCATION @@ -2441,6 +2449,12 @@ OBJECTTYPE* InternalAddItemToPool( INT32 *psGridNo, OBJECTTYPE *pObject, INT8 bV bRenderZHeightAboveLevel = 0; } + // Flugente: if this item is a corpse, don't add it to the world. Instead spawn a corpse in its position + if ( HasItemFlag(pObject->usItem, CORPSE) && AddCorpseFromObject(pObject, *psGridNo, ubLevel ) ) + { + return( NULL ); + } + //CHRISL: We need to make sure that item stacks follow the OldInv stacking limits // Copy the object we're working with the gTempObject so that we can use pObject later in this function OBJECTTYPE poolObject = OBJECTTYPE(*pObject); @@ -4418,6 +4432,19 @@ INT32 AdjustGridNoForItemPlacement( SOLDIERTYPE *pSoldier, INT32 sGridNo ) return( sActionGridNo ); } +// Flugente +void StartCorpseMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo, INT8 bLevel ) +{ + gpTempSoldier = pSoldier; + + // determine what is possible with this corpse + BOOLEAN fDecapitate = TRUE; + BOOLEAN fTakeClothes = TRUE; + BOOLEAN fTakeCorpse = TRUE; + + if ( fDecapitate && fTakeClothes ) + DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CORPSE_SELECTION_STR ], GAME_SCREEN, MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS, CorpseMessageBoxCallBack, NULL ); +} void StartBombMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo ) { @@ -4667,6 +4694,36 @@ void TacticalFunctionSelectionMessageBoxCallBack( UINT8 ubExitValue ) } } +// Flugente: callback after deciding what to do with a corpse +void CorpseMessageBoxCallBack( UINT8 ubExitValue ) +{ + if (gpTempSoldier) + { + INT32 nextGridNoinSight = gpTempSoldier->sGridNo; + nextGridNoinSight = NewGridNo( nextGridNoinSight, DirectionInc( gpTempSoldier->ubDirection ) ); + + INT8 level = gpTempSoldier->bTargetLevel; + + switch (ubExitValue) + { + case 1: + DecapitateCorpse( gpTempSoldier, nextGridNoinSight, level ); + break; + case 2: + GutCorpse( gpTempSoldier, nextGridNoinSight, level ); + break; + case 3: + StripCorpse( gpTempSoldier, nextGridNoinSight, level ); + break; + case 4: + TakeCorpse( gpTempSoldier, nextGridNoinSight, level ); + break; + default: + break; + } + } +} + BOOLEAN HandItemWorks( SOLDIERTYPE *pSoldier, INT8 bSlot ) { BOOLEAN fItemJustBroke = FALSE, fItemWorks = TRUE; diff --git a/Tactical/Handle Items.h b/Tactical/Handle Items.h index 09d0d25a..66b6ede7 100644 --- a/Tactical/Handle Items.h +++ b/Tactical/Handle Items.h @@ -135,6 +135,8 @@ void HandleSoldierUseRemote( SOLDIERTYPE *pSoldier, INT32 sGridNo ); void HandleTacticalFunctionSelection( SOLDIERTYPE *pSoldier, INT32 sGridNo ); // Flugente: in tactical, open a selection box for different actions +void HandleSoldierUseCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ); // Flugente: handle corpses + BOOLEAN ItemPoolOKForDisplay( ITEM_POOL *pItemPool, INT8 bZLevel ); INT16 GetNumOkForDisplayItemsInPool( ITEM_POOL *pItemPool, INT8 bZLevel ); diff --git a/Tactical/Interface Enhanced.cpp b/Tactical/Interface Enhanced.cpp index 65813612..1adb735f 100644 --- a/Tactical/Interface Enhanced.cpp +++ b/Tactical/Interface Enhanced.cpp @@ -2523,7 +2523,16 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr SetRegionFastHelpText( &(gUDBFasthelpRegions[ iFirstDataRegion + cnt ]), pStr ); MSYS_EnableRegion( &gUDBFasthelpRegions[ iFirstDataRegion + cnt ] ); cnt++; - } + } + + //////////////////// COVERT ITEM + if ( HasItemFlag(gpItemDescObject->usItem, COVERT) ) + { + swprintf( pStr, L"%s%s", szUDBGenSecondaryStatsTooltipText[ 31 ], szUDBGenSecondaryStatsExplanationsTooltipText[ 31 ]); + SetRegionFastHelpText( &(gUDBFasthelpRegions[ iFirstDataRegion + cnt ]), pStr ); + MSYS_EnableRegion( &gUDBFasthelpRegions[ iFirstDataRegion + cnt ] ); + cnt++; + } } ////////////////////////////////////////////////////// @@ -5532,7 +5541,14 @@ void DrawSecondaryStats( OBJECTTYPE * gpItemDescObject ) { BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoSecondaryIcon, 11, gItemDescGenSecondaryRegions[cnt].sLeft+sOffsetX, gItemDescGenSecondaryRegions[cnt].sTop+sOffsetY, VO_BLT_SRCTRANSPARENCY, NULL ); cnt++; -} + } + + //////////////////// COVERT ITEM + if ( HasItemFlag(gpItemDescObject->usItem, COVERT) ) + { + BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoSecondaryIcon, 30, gItemDescGenSecondaryRegions[cnt].sLeft+sOffsetX, gItemDescGenSecondaryRegions[cnt].sTop+sOffsetY, VO_BLT_SRCTRANSPARENCY, NULL ); + cnt++; + } } void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) @@ -8938,8 +8954,9 @@ void DrawAdvancedValues( OBJECTTYPE *gpItemDescObject ) // Print Values for (UINT8 cnt2 = 0; cnt2 < 3; cnt2++) { - if (UsingNewCTHSystem() == false && cnt2 > 0) - break; + // Flugente: no idea why we would want to skip this in OCTH, commenting this out + //if (UsingNewCTHSystem() == false && cnt2 > 0) + //break; SetFontForeground( 5 ); sLeft = gItemDescAdvRegions[cnt-sFirstLine][cnt2+1].sLeft; sWidth = gItemDescAdvRegions[cnt-sFirstLine][cnt2+1].sRight - sLeft; diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index 6487b190..07787269 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -13148,11 +13148,11 @@ void BombInventoryMessageBoxCallBack( UINT8 ubExitValue ) if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( gpItemDescSoldier, DEMOLITIONS_NT )) { // increase trap level for Demolitions trait - gpItemDescSoldier->inv[ HANDPOS ][0]->data.bTrap = __min( max( 10, (8 + gSkillTraitValues.ubDEPlacedBombLevelBonus)), (( EffectiveExplosive( gpItemDescSoldier ) / 20) + (EffectiveExpLevel( gpItemDescSoldier ) / 3) + gSkillTraitValues.ubDEPlacedBombLevelBonus) ); + (*gpItemDescObject)[0]->data.bTrap = __min( max( 10, (8 + gSkillTraitValues.ubDEPlacedBombLevelBonus)), (( EffectiveExplosive( gpItemDescSoldier ) / 20) + (EffectiveExpLevel( gpItemDescSoldier ) / 3) + gSkillTraitValues.ubDEPlacedBombLevelBonus) ); } else { - gpItemDescSoldier->inv[ HANDPOS ][0]->data.bTrap = __min( 10, ( EffectiveExplosive( gpItemDescSoldier ) / 20) + (EffectiveExpLevel( gpItemDescSoldier ) / 3) ); + (*gpItemDescObject)[0]->data.bTrap = __min( 10, ( EffectiveExplosive( gpItemDescSoldier ) / 20) + (EffectiveExpLevel( gpItemDescSoldier ) / 3) ); } // Flugente: We armed a bomb in our inventory. We will NOT add it to the item pool and the world bombs. @@ -13164,8 +13164,8 @@ void BombInventoryMessageBoxCallBack( UINT8 ubExitValue ) // so we add 2 to all owner IDs passed through here and subtract 2 later //if (gpItemDescSoldier->inv[HANDPOS].MoveThisObjectTo(gTempObject, 1) == 0) { - gTempObject[0]->data.misc.ubBombOwner = gpItemDescSoldier->ubID + 2; - gTempObject[0]->data.ubDirection = gpItemDescSoldier->ubDirection; // Flugente: direction of bomb is direction of soldier + (*gpItemDescObject)[0]->data.misc.ubBombOwner = gpItemDescSoldier->ubID + 2; + (*gpItemDescObject)[0]->data.ubDirection = gpItemDescSoldier->ubDirection; // Flugente: direction of bomb is direction of soldier //AddItemToPool( gsTempGridNo, &gTempObject, VISIBLE, gpItemDescSoldier->pathing.bLevel, WORLD_ITEM_ARMED_BOMB, 0 ); } diff --git a/Tactical/Interface Panels.cpp b/Tactical/Interface Panels.cpp index b1f01f02..c7b12be3 100644 --- a/Tactical/Interface Panels.cpp +++ b/Tactical/Interface Panels.cpp @@ -3357,6 +3357,21 @@ void SMInvClickCamoCallback( MOUSE_REGION * pRegion, INT32 iReason ) // Say OK acknowledge.... gpSMCurrentMerc->DoMercBattleSound( BATTLE_SOUND_COOL1 ); } + else if ( ApplySpyKit( gpSMCurrentMerc, gpItemPointer ) ) + { + // Dirty + fInterfacePanelDirty = DIRTYLEVEL2; + + // Check if it's the same now! + if ( gpItemPointer->exists() == false ) + { + gbCompatibleApplyItem = FALSE; + EndItemPointer( ); + } + + // Say OK acknowledge.... + gpSMCurrentMerc->DoMercBattleSound( BATTLE_SOUND_COOL1 ); + } else { // Send message diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index 70e5190d..577d283e 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -734,13 +734,13 @@ extern OBJECTTYPE gTempObject; #define CAMO_REMOVAL 0x00000400 //1024 // item can be used to remove camo #define CLEANING_KIT 0x00000800 //2048 // weapon cleaning kit -/*#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 +#define CLOTHES_CIVILIAN 0x00001000 //4096 // with this item you can disguise as a civilian +#define GAROTTE 0x00002000 //8192 // this item is a garotte +#define COVERT 0x00004000 //16384 // if LBE, any gun inside will be covert. On a gun, it will covert in any LBE, even if the LBE does not have that tag itself +#define CORPSE 0x00008000 //32768 // a dead body -#define PLAYER_NET_1_LVL_1 0x00010000 //65536 -#define PLAYER_NET_2_LVL_1 0x00020000 //131072 +#define SKIN_BLOODCAT 0x00010000 //65536 // retrieve this by skinning (=decapitating) a bloodcat +/*#define PLAYER_NET_2_LVL_1 0x00020000 //131072 #define PLAYER_NET_3_LVL_1 0x00040000 //262144 #define PLAYER_NET_4_LVL_1 0x00080000 //524288 @@ -760,6 +760,49 @@ extern OBJECTTYPE gTempObject; #define PLAYER_NET_4_LVL_4 0x80000000 //2147483648*/ // ---------------------------------------------------------------- +// -------- added by Flugente: flags for objects -------- +// the numbering of these flags is important. DO NOT CHANGE THEM, UNLESS YOU KNOW WHAT YOU ARE DOING!!! +#define CORPSE_M_SMALL 0x00000001 //1 +#define CORPSE_M_BIG 0x00000002 //2 +#define CORPSE_F 0x00000004 //4 +#define CORPSE_NO_HEAD 0x00000008 //8 + +#define CORPSE_HAIR_BROWN 0x00000010 //16 +#define CORPSE_HAIR_BLACK 0x00000020 //32 +#define CORPSE_HAIR_WHITE 0x00000040 //64 +#define CORPSE_HAIR_BLOND 0x00000080 //128 + +#define CORPSE_HAIR_RED 0x00000100 //256 +#define CORPSE_SKIN_PINK 0x00000200 //512 +#define CORPSE_SKIN_TAN 0x00000400 //1024 +#define CORPSE_SKIN_DARK 0x00000800 //2048 + +#define CORPSE_SKIN_BLACK 0x00001000 //4096 +#define CORPSE_VEST_BROWN 0x00002000 //8192 +#define CORPSE_VEST_grey 0x00004000 //16384 +#define CORPSE_VEST_GREEN 0x00008000 //32768 + +#define CORPSE_VEST_JEAN 0x00010000 //65536 +#define CORPSE_VEST_RED 0x00020000 //131072 +#define CORPSE_VEST_BLUE 0x00040000 //262144 +#define CORPSE_VEST_YELLOW 0x00080000 //524288 + +#define CORPSE_VEST_WHITE 0x00100000 //1048576 +#define CORPSE_VEST_BLACK 0x00200000 //2097152 +#define CORPSE_VEST_GYELLOW 0x00400000 //4194304 +#define CORPSE_VEST_PURPLE 0x00800000 //8388608 + +#define CORPSE_PANTS_GREEN 0x01000000 //16777216 +#define CORPSE_PANTS_JEAN 0x02000000 //33554432 +#define CORPSE_PANTS_TAN 0x04000000 //67108864 +#define CORPSE_PANTS_BLACK 0x08000000 //134217728 + +#define CORPSE_PANTS_BLUE 0x10000000 //268435456 +#define CORPSE_PANTS_BEIGE 0x20000000 //536870912 +#define CORPSE_STRIPPED 0x40000000 //1073741824 +//#define unused 0x80000000 //2147483648*/ +// ---------------------------------------------------------------- + // replaces candamage //#define ITEM_DAMAGEABLE 0x0001 //// replaces canrepair @@ -1703,6 +1746,10 @@ enum IMP_TEACHING_EXP, IMP_LOCKPICKING_EXP, + // Flugente: added covert ops + IMP_COVERT, + IMP_COVERT_EXP, + MAX_IMP_ITEM_TYPES }; diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 8d420637..770faa02 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -9144,6 +9144,23 @@ BOOLEAN ApplyCammo( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, BOOLEAN *pfGoodAP return( TRUE ); } + // Flugente: apply disguise +BOOLEAN ApplySpyKit( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj) +{ + if (pObj->exists() == false || !HasItemFlag(pObj->usItem, (CLOTHES_CIVILIAN)) ) + { + return( FALSE ); + } + + // we get the civilian disguise. AP costs will be checked and deducted here + if ( !pSoldier->DisguiseAsCivilian() ) + return FALSE; + + UseKitPoints( pObj, 100, pSoldier ); + + return( TRUE ); +} + BOOLEAN ApplyCanteen( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, BOOLEAN *pfGoodAPs ) { INT16 sPointsToUse; diff --git a/Tactical/Items.h b/Tactical/Items.h index 048dea4b..5c6cb3a2 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -196,6 +196,7 @@ void WaterDamage( SOLDIERTYPE *pSoldier ); INT8 FindObj( SOLDIERTYPE * pSoldier, UINT16 usItem, INT8 bLower = 0, INT8 bUpper = NUM_INV_SLOTS ); BOOLEAN ApplyCammo( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, BOOLEAN *pfGoodAPs ); +BOOLEAN ApplySpyKit( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj); // Flugente: apply disguise UINT32 ConvertProfileMoneyValueToObjectTypeMoneyValue( UINT8 ubStatus ); UINT8 ConvertObjectTypeMoneyValueToProfileMoneyValue( UINT32 uiMoneyAmount ); @@ -484,6 +485,7 @@ typedef struct { UINT8 usWaterType; // type of water source in this sector UINT16 usNaturalDirt; // extra dirt percentage when firing in this sector + UINT8 usCurfewValue; // determines wether mercs disguising as civilian are automatically discovered (certain sectors are 'restricted' to civilians) } SECTOR_EXT_DATA; // get dirt increase for object with attachments, fConsiderAmmo: with ammo diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 5ceac9c2..921d27d8 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -6104,11 +6104,11 @@ void EnterCombatMode( UINT8 ubStartingTeam ) if (!is_client || is_server) //hayden { - StartPlayerTeamTurn( FALSE, TRUE ); + StartPlayerTeamTurn( FALSE, TRUE ); if(is_client)send_EndTurn( ubStartingTeam ); //hayden - } - else - { + } + else + { //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"client skipped EnterCombatMode"); if(is_client)startCombat(ubStartingTeam);//clients other than server meet and send request for combat for server } diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 657cfb37..bc93058d 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -275,8 +275,8 @@ BOOLEAN gbCorpseValidForDecapitation[ NUM_CORPSES ] = 1, 1, 1, - 1, // Bloodcat - changed to 1 to allow gutting - 1, // Cow - changed to 1 to allow gutting + 1, // Bloodcat - changed to 1 to allow skinning (works like decapitating) + 0, 0, 0, 0, @@ -495,6 +495,9 @@ INT32 AddRottingCorpse( ROTTING_CORPSE_DEFINITION *pCorpseDef ) // Copy elements in memcpy( pCorpse, pCorpseDef, sizeof( ROTTING_CORPSE_DEFINITION ) ); + if ( pCorpseDef->fHeadTaken ) + pCorpse->def.usFlags |= ROTTING_CORPSE_HEAD_TAKEN; + uiDirectionUseFlag = ANITILE_USE_DIRECTION_FOR_START_FRAME; // If we are a soecial type... @@ -818,7 +821,7 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE // Setup some values! memset( &Corpse, 0, sizeof( Corpse ) ); Corpse.ubBodyType = pSoldier->ubBodyType; - Corpse.sGridNo = pSoldier->sGridNo; + Corpse.sGridNo = pSoldier->sGridNo; Corpse.dXPos = pSoldier->dXPos; Corpse.dYPos = pSoldier->dYPos; Corpse.bLevel = pSoldier->pathing.bLevel; @@ -895,6 +898,18 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE Corpse.name[9] = '\0'; #endif + // Flugente: mark if this was an army soldier ( we use this to steal his clothes later on ) + if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) + Corpse.usFlags |= ROTTING_CORPSE_FROM_ADMIN; + else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY ) + Corpse.usFlags |= ROTTING_CORPSE_FROM_TROOP; + else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE ) + Corpse.usFlags |= ROTTING_CORPSE_FROM_ELITE; + + // if this soldier's uniform was damaged (gunfire, blade attacks, explosions) then don't allow to take the uniform. We can't stay hidden if we're covered in blood :-) + if ( pSoldier->bSoldierFlagMask &SOLDIER_DAMAGED_UNIFORM ) + Corpse.usFlags |= ROTTING_CORPSE_CLOTHES_TAKEN; + // Determine corpse type! ubType = (UINT8)gubAnimSurfaceCorpseID[ pSoldier->ubBodyType][ pSoldier->usAnimState ]; @@ -1763,7 +1778,7 @@ INT32 FindNearestAvailableGridNoForCorpse( ROTTING_CORPSE_DEFINITION *pDef, INT8 BOOLEAN IsValidDecapitationCorpse( ROTTING_CORPSE *pCorpse ) { - if ( (pCorpse->def.usFlags & ROTTING_CORPSE_GUTTED) ) + if ( (pCorpse->def.usFlags & ROTTING_CORPSE_HEAD_TAKEN) ) { return( FALSE ); } @@ -1797,14 +1812,12 @@ ROTTING_CORPSE *GetCorpseAtGridNo( INT32 sGridNo, INT8 bLevel ) } -void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) +void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) { ROTTING_CORPSE *pCorpse; ROTTING_CORPSE_DEFINITION CorpseDef; UINT16 usHeadIndex = HEAD_1; - static UINT16 usBloodCatMeatIndex = 1566; - static UINT16 usCowMeatIndex = 1565; - + static UINT16 usBloodCatSkinIndex = 232; pCorpse = GetCorpseAtGridNo( sGridNo, bLevel ); @@ -1812,41 +1825,30 @@ void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) { return; } - + if ( IsValidDecapitationCorpse( pCorpse ) ) { // Decapitate..... // Copy corpse definition... memcpy( &CorpseDef, &(pCorpse->def), sizeof( ROTTING_CORPSE_DEFINITION ) ); - - // Flugente: if the corpse is of a cow or a bloodcat, we don't take its head, we simply gut it and give meat instead of a head - if ( CorpseDef.ubType == BLOODCAT_DEAD || CorpseDef.ubType == COW_DEAD ) - { - pCorpse->def.usFlags |= (ROTTING_CORPSE_HEAD_TAKEN|ROTTING_CORPSE_GUTTED); - - if ( CorpseDef.ubType == BLOODCAT_DEAD ) - { - if ( HasItemFlag(usBloodCatMeatIndex, MEAT_BLOODCAT) || GetFirstItemWithFlag(&usBloodCatMeatIndex, MEAT_BLOODCAT) ) - usHeadIndex = usBloodCatMeatIndex; - } - else - { - if ( HasItemFlag(usCowMeatIndex, COW_MEAT) || GetFirstItemWithFlag(&usCowMeatIndex, COW_MEAT) ) - usHeadIndex = usCowMeatIndex; - } - } - + // Add new one... CorpseDef.ubType = gDecapitatedCorpse[ CorpseDef.ubType ]; - - pCorpse->def.usFlags |= (ROTTING_CORPSE_HEAD_TAKEN|ROTTING_CORPSE_GUTTED); - + + pCorpse->def.usFlags |= ROTTING_CORPSE_HEAD_TAKEN; + CorpseDef.fHeadTaken = TRUE; + if ( CorpseDef.ubType != 0 ) { // Remove old one... RemoveCorpse( pCorpse->iID ); - AddRottingCorpse( &CorpseDef ); + INT32 iCorpseID = AddRottingCorpse( &CorpseDef ); + + if ( iCorpseID != -1 ) + { + gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_HEAD_TAKEN; + } } // Add head item..... @@ -1880,6 +1882,12 @@ void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) } + if ( pCorpse->def.ubType == BLOODCAT_DEAD ) + { + if ( HasItemFlag(usBloodCatSkinIndex, SKIN_BLOODCAT) || GetFirstItemWithFlag(&usBloodCatSkinIndex, SKIN_BLOODCAT) ) + usHeadIndex = usBloodCatSkinIndex; + } + if ( usHeadIndex > 0 ) { CreateItem( usHeadIndex, 100, &gTempObject ); @@ -1889,7 +1897,404 @@ void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) NotifySoldiersToLookforItems( ); } } +} +// Flugente: can this corpse be gutted? +BOOLEAN IsValidGutCorpse( ROTTING_CORPSE *pCorpse ) +{ + if ( pCorpse->def.ubType == ROTTING_STAGE2 || (pCorpse->def.usFlags & ROTTING_CORPSE_GUTTED) ) + return( FALSE ); + + return( pCorpse->def.ubType == BLOODCAT_DEAD || pCorpse->def.ubType == COW_DEAD ); +} + +// Flugente: gut a corpse +void GutCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) +{ + ROTTING_CORPSE *pCorpse = GetCorpseAtGridNo( sGridNo, bLevel ); + + if ( pCorpse == NULL ) + return; + + // can this thing be gutted? + if ( IsValidGutCorpse( pCorpse ) ) + { + // numbers for the meat acquired by gutting. These values respond to the standard GameDir values, if they have other values in the ini, no problem, we'll search for them + static UINT16 usBloodCatMeatIndex = 1566; + static UINT16 usCowMeatIndex = 1565; + UINT16 meatindex = 0; + + pCorpse->def.usFlags |= ROTTING_CORPSE_GUTTED; + + if ( pCorpse->def.ubType == BLOODCAT_DEAD ) + { + if ( HasItemFlag(usBloodCatMeatIndex, MEAT_BLOODCAT) || GetFirstItemWithFlag(&usBloodCatMeatIndex, MEAT_BLOODCAT) ) + meatindex = usBloodCatMeatIndex; + } + else + { + if ( HasItemFlag(usCowMeatIndex, COW_MEAT) || GetFirstItemWithFlag(&usCowMeatIndex, COW_MEAT) ) + meatindex = usCowMeatIndex; + } + + if ( meatindex > 0 ) + { + CreateItem( meatindex, 100, &gTempObject ); + AddItemToPool( sGridNo, &gTempObject, INVISIBLE, 0, 0, 0 ); + + // All teams lok for this... + NotifySoldiersToLookforItems( ); + } + } + else + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Not possible, you sick, sick individual!" ); +} + +// Flugente: can clothes be taken off of this corpse? +BOOLEAN IsValidStripCorpse( ROTTING_CORPSE *pCorpse ) +{ + if ( pCorpse->def.ubType == ROTTING_STAGE2 || (pCorpse->def.usFlags & ROTTING_CORPSE_CLOTHES_TAKEN) ) + return( FALSE ); + + // is there a uniform we can take? + if ( !(pCorpse->def.usFlags & (ROTTING_CORPSE_FROM_ADMIN|ROTTING_CORPSE_FROM_TROOP|ROTTING_CORPSE_FROM_ELITE)) ) + return( FALSE ); + + return( CorpseOkToDress(pCorpse, NULL) ); +} + + +// Flugente: take the clothes off a corpse and wear them +void StripCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) +{ + ROTTING_CORPSE *pCorpse = GetCorpseAtGridNo( sGridNo, bLevel ); + + if ( pCorpse == NULL ) + return; + + // can this thing be stripped? + if ( IsValidStripCorpse( pCorpse ) ) + { + // if this fits our body, try to wear it + if ( CorpseOkToDress(pCorpse, pSoldier) && pSoldier->DisguiseAsSoldierFromCorpse( pCorpse->def.usFlags ) ) + { + // we took the clothes, mark this + pCorpse->def.usFlags |= ROTTING_CORPSE_CLOTHES_TAKEN; + } + } + else + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"No clothes to take!" ); +} + +// Flugente: can clothes be taken off of this corpse? +BOOLEAN IsValidTakeCorpse( ROTTING_CORPSE *pCorpse ) +{ + if ( pCorpse->def.ubType >= BLOODCAT_DEAD || pCorpse->def.ubType == NO_CORPSE ) + return( FALSE ); + + return( TRUE ); +} + +// Flugente: take a corpse into your hand, thereby removing it from the field +void TakeCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) +{ + ROTTING_CORPSE *pCorpse = GetCorpseAtGridNo( sGridNo, bLevel ); + + if ( pCorpse == NULL ) + return; + + // can this corpse be picked up? + if ( IsValidTakeCorpse( pCorpse ) ) + { + // is there a 'corpse' item? + UINT16 corpseitem = 0; + if ( GetFirstItemWithFlag(&corpseitem, CORPSE) ) + { + // we have to make sure this can be placed in our hands, as corpses can only be carried in hands. + // At this point, we will have a knife in our first hand, so check if our second hand is free + if ( !(pSoldier->inv[SECONDHANDPOS].exists()) ) + { + CreateItem( corpseitem, 100, &gTempObject ); + + if(CanItemFitInPosition(pSoldier, &gTempObject, SECONDHANDPOS, FALSE)) + { + // determine Body type + if ( pCorpse->def.ubBodyType == BIGMALE || pCorpse->def.ubBodyType == STOCKYMALE || pCorpse->def.ubBodyType == FATCIV ) + gTempObject[0]->data.sObjectFlag |= CORPSE_M_BIG; + 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 ) + gTempObject[0]->data.sObjectFlag |= CORPSE_NO_HEAD; + + if ( pCorpse->def.usFlags & ROTTING_CORPSE_CLOTHES_TAKEN ) + gTempObject[0]->data.sObjectFlag |= CORPSE_STRIPPED; + + // now we have to get the correct flags for the object from the corpse, so that upon recreating the corpse, it looks the same + UINT8 headpal = 0, skinpal = 0, vestpal = 0, pantspal = 0; + GetPaletteRepIndexFromID(pCorpse->def.HeadPal, &headpal); + GetPaletteRepIndexFromID(pCorpse->def.SkinPal, &skinpal); + GetPaletteRepIndexFromID(pCorpse->def.VestPal, &vestpal); + GetPaletteRepIndexFromID(pCorpse->def.PantsPal, &pantspal); + + switch ( headpal ) + { + case 0: + gTempObject[0]->data.sObjectFlag |= CORPSE_HAIR_BLOND; + break; + case 1: + gTempObject[0]->data.sObjectFlag |= CORPSE_HAIR_BROWN; + break; + case 2: + gTempObject[0]->data.sObjectFlag |= CORPSE_HAIR_BLACK; + break; + case 3: + gTempObject[0]->data.sObjectFlag |= CORPSE_HAIR_WHITE; + break; + case 4: + default: + gTempObject[0]->data.sObjectFlag |= CORPSE_HAIR_RED; + break; + } + + switch ( skinpal ) + { + case 13: + gTempObject[0]->data.sObjectFlag |= CORPSE_SKIN_PINK; + break; + case 14: + gTempObject[0]->data.sObjectFlag |= CORPSE_SKIN_TAN; + break; + case 15: + gTempObject[0]->data.sObjectFlag |= CORPSE_SKIN_DARK; + break; + case 16: + default: + gTempObject[0]->data.sObjectFlag |= CORPSE_SKIN_BLACK; + break; + } + + switch ( vestpal ) + { + case 17: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_BROWN; + break; + case 18: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_grey; + break; + case 19: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_GREEN; + break; + case 20: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_JEAN; + break; + case 21: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_RED; + break; + case 22: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_BLUE; + break; + case 23: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_YELLOW; + break; + case 24: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_WHITE; + break; + case 25: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_BLACK; + break; + case 26: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_GYELLOW; + break; + case 27: + default: + gTempObject[0]->data.sObjectFlag |= CORPSE_VEST_PURPLE; + break; + } + + switch ( pantspal ) + { + case 7: + gTempObject[0]->data.sObjectFlag |= CORPSE_PANTS_GREEN; + break; + case 8: + gTempObject[0]->data.sObjectFlag |= CORPSE_PANTS_JEAN; + break; + case 9: + gTempObject[0]->data.sObjectFlag |= CORPSE_PANTS_TAN; + break; + case 10: + gTempObject[0]->data.sObjectFlag |= CORPSE_PANTS_BLACK; + break; + case 11: + gTempObject[0]->data.sObjectFlag |= CORPSE_PANTS_BLUE; + break; + case 12: + default: + gTempObject[0]->data.sObjectFlag |= CORPSE_PANTS_BEIGE; + break; + } + + gTempObject.MoveThisObjectTo(pSoldier->inv[SECONDHANDPOS], ALL_OBJECTS, pSoldier, SECONDHANDPOS); + + if ( pCorpse->def.ubType != 0 ) + { + // Remove corpse... + RemoveCorpse( pCorpse->iID ); + } + } + } + else + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"No free hand to carry corpse!" ); + } + } +} + +// Flugente: create a corpse from an object and plce it in the world +BOOLEAN AddCorpseFromObject(OBJECTTYPE* pObj, INT32 sGridNo, INT8 bLevel ) +{ + if ( !pObj || !HasItemFlag(pObj->usItem, CORPSE) ) + return FALSE; + + ROTTING_CORPSE_DEFINITION Corpse; + + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_M_BIG ) + { + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_HEAD ) + Corpse.ubType = MMERC_JFK; + else + Corpse.ubType = MMERC_BCK; + + Corpse.ubBodyType = BIGMALE; + } + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_F ) + { + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_HEAD ) + Corpse.ubType = FMERC_JFK; + else + Corpse.ubType = FMERC_BCK; + + Corpse.ubBodyType = REGFEMALE; + } + else + { + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_M_SMALL ) + Corpse.ubType = SMERC_JFK; + else + Corpse.ubType = SMERC_BCK; + + Corpse.ubBodyType = REGMALE; + } + + Corpse.sGridNo = sGridNo; + + Corpse.dXPos = CenterX(Corpse.sGridNo); + Corpse.dYPos = CenterY(Corpse.sGridNo); + + Corpse.sHeightAdjustment = 0; + + // check the objects flagmask and set the corpse palette IDs accordingly + + // Hair + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_HAIR_BROWN ) + SET_PALETTEREP_ID( Corpse.HeadPal, "BROWNHEAD" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_HAIR_BLACK ) + SET_PALETTEREP_ID( Corpse.HeadPal, "BLACKHEAD" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_HAIR_WHITE ) + SET_PALETTEREP_ID( Corpse.HeadPal, "WHITEHEAD" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_HAIR_BLOND ) + SET_PALETTEREP_ID( Corpse.HeadPal, "BLONDHEAD" ); + else + SET_PALETTEREP_ID( Corpse.HeadPal, "REDHEAD" ); + + // Skin + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_SKIN_PINK ) + SET_PALETTEREP_ID( Corpse.SkinPal, "PINKSKIN" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_SKIN_TAN ) + SET_PALETTEREP_ID( Corpse.SkinPal, "TANSKIN" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_SKIN_DARK ) + SET_PALETTEREP_ID( Corpse.SkinPal, "DARKSKIN" ); + else + SET_PALETTEREP_ID( Corpse.SkinPal, "BLACKSKIN" ); + + // Vest + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_BROWN ) + SET_PALETTEREP_ID( Corpse.VestPal, "BROWNVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_grey ) + SET_PALETTEREP_ID( Corpse.VestPal, "greyVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_GREEN ) + SET_PALETTEREP_ID( Corpse.VestPal, "GREENVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_JEAN ) + SET_PALETTEREP_ID( Corpse.VestPal, "JEANVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_RED ) + SET_PALETTEREP_ID( Corpse.VestPal, "REDVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_BLUE ) + SET_PALETTEREP_ID( Corpse.VestPal, "BLUEVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_YELLOW ) + SET_PALETTEREP_ID( Corpse.VestPal, "YELLOWVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_WHITE ) + SET_PALETTEREP_ID( Corpse.VestPal, "WHITEVEST" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_BLACK ) + SET_PALETTEREP_ID( Corpse.VestPal, "BLACKSHIRT" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_GYELLOW ) + SET_PALETTEREP_ID( Corpse.VestPal, "GYELLOWSHIRT" ); + else + SET_PALETTEREP_ID( Corpse.VestPal, "PURPLESHIRT" ); + + // Pants + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_GREEN ) + SET_PALETTEREP_ID( Corpse.PantsPal, "GREENPANTS" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_JEAN ) + SET_PALETTEREP_ID( Corpse.PantsPal, "JEANPANTS" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_TAN ) + SET_PALETTEREP_ID( Corpse.PantsPal, "TANPANTS" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_BLACK ) + SET_PALETTEREP_ID( Corpse.PantsPal, "BLACKPANTS" ); + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_BLUE ) + SET_PALETTEREP_ID( Corpse.PantsPal, "BLUEPANTS" ); + else + SET_PALETTEREP_ID( Corpse.PantsPal, "BEIGEPANTS" ); + + //SET_PALETTEREP_ID( Corpse.VestPal, gUniformColors[ UNIFORM_ENEMY_TROOP ].vest ); + //SET_PALETTEREP_ID( Corpse.PantsPal, gUniformColors[ UNIFORM_ENEMY_TROOP ].pants ); + + Corpse.ubDirection = NORTH; + Corpse.uiTimeOfDeath = GetWorldTotalMin(); + + Corpse.usFlags = 0; // no flags here, at least not the new ones + + Corpse.bLevel = bLevel; + + Corpse.bVisible = 1; + Corpse.bNumServicingCrows = 0; + Corpse.ubProfile = NO_PROFILE; + Corpse.fHeadTaken = FALSE; + Corpse.ubAIWarningValue = 20; + + INT32 iCorpseID = AddRottingCorpse( &Corpse ); + + if ( iCorpseID != -1 ) + { + AllMercsOnTeamLookForCorpse(&( gRottingCorpse[ iCorpseID ] ), OUR_TEAM); + + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_HEAD ) + gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_HEAD_TAKEN; + + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_STRIPPED ) + gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_CLOTHES_TAKEN; + + // TODO: change this so it always fits, not dependant these colours + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_YELLOW && (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_GREEN ) + gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_FROM_ADMIN; + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_RED && (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_GREEN ) + gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_FROM_TROOP; + else if ( (*pObj)[0]->data.sObjectFlag & CORPSE_VEST_BLACK && (*pObj)[0]->data.sObjectFlag & CORPSE_PANTS_BLACK ) + gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_FROM_ELITE; + + return TRUE; + } + + return FALSE; } @@ -2552,3 +2957,85 @@ UINT8 GetNearestRottingCorpseAIWarning( INT32 sGridNo ) return( canbezombie ); } #endif + +// Flugente: can we take the clothes of this corpse? +// calling this with NULL for soldier will give a general answer for any bodytype +BOOLEAN CorpseOkToDress( ROTTING_CORPSE * pCorpse, SOLDIERTYPE* pSoldier ) +{ + if ( !pCorpse ) + return FALSE; + + switch ( pCorpse->def.ubType ) + { + case SMERC_JFK: + case SMERC_BCK: + case SMERC_FWD: + case SMERC_DHD: + case SMERC_PRN: + case SMERC_WTR: + case SMERC_FALL: + case SMERC_FALLF: + case M_DEAD1: + case M_DEAD2: + case H_DEAD1: + case H_DEAD2: + case S_DEAD1: + case S_DEAD2: + case C_DEAD1: + case C_DEAD2: + if ( pSoldier ) + { + if ( pSoldier->ubBodyType == REGMALE ) + return TRUE; + } + else + return TRUE; + + break; + + case MMERC_JFK: + case MMERC_BCK: + case MMERC_FWD: + case MMERC_DHD: + case MMERC_PRN: + case MMERC_WTR: + case MMERC_FALL: + case MMERC_FALLF: + case FT_DEAD1: + case FT_DEAD2: + if ( pSoldier ) + { + if ( pSoldier->ubBodyType == BIGMALE || pSoldier->ubBodyType == STOCKYMALE ) + return TRUE; + } + else + return TRUE; + + break; + + case FMERC_JFK: + case FMERC_BCK: + case FMERC_FWD: + case FMERC_DHD: + case FMERC_PRN: + case FMERC_WTR: + case FMERC_FALL: + case FMERC_FALLF: + case W_DEAD1: + case W_DEAD2: + if ( pSoldier ) + { + if ( pSoldier->ubBodyType == REGFEMALE ) + return TRUE; + } + else + return TRUE; + + break; + + default: + break; + } + + return FALSE; +} diff --git a/Tactical/Rotting Corpses.h b/Tactical/Rotting Corpses.h index 1d2de841..19c9bf91 100644 --- a/Tactical/Rotting Corpses.h +++ b/Tactical/Rotting Corpses.h @@ -91,8 +91,12 @@ enum RottingCorpseDefines // Flugente: corpses can now be gutted after they have been decapitated. Atm there is no corpse that can be both gutted and decapitated (to be done later) #define ROTTING_CORPSE_HEAD_TAKEN 0x00001000 // head has been taken off -#define ROTTING_CORPSE_GUTTED 0x00001000 // corpse has been gutted +#define ROTTING_CORPSE_GUTTED 0x00002000 // corpse has been gutted +#define ROTTING_CORPSE_CLOTHES_TAKEN 0x00004000 // corpse is without clothes (atm not visually) +#define ROTTING_CORPSE_FROM_ADMIN 0x00010000 // corpse is from a admin (used to take his clothes) +#define ROTTING_CORPSE_FROM_TROOP 0x00020000 // corpse is from a regular soldier (used to take his clothes) +#define ROTTING_CORPSE_FROM_ELITE 0x00040000 // corpse is from a elite soldier (used to take his clothes) typedef struct { UINT8 ubType; @@ -199,6 +203,16 @@ ROTTING_CORPSE *GetCorpseAtGridNo( INT32 sGridNo , INT8 bLevel ); BOOLEAN IsValidDecapitationCorpse( ROTTING_CORPSE *pCorpse ); void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ); +// Flugente: actions for the covert opts trait +BOOLEAN IsValidGutCorpse( ROTTING_CORPSE *pCorpse ); +void GutCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ); +BOOLEAN IsValidStripCorpse( ROTTING_CORPSE *pCorpse ); +void StripCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ); +BOOLEAN IsValidTakeCorpse( ROTTING_CORPSE *pCorpse ); +void TakeCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ); + +BOOLEAN AddCorpseFromObject(OBJECTTYPE* pObj, INT32 sGridNo, INT8 bLevel ); + void GetBloodFromCorpse( SOLDIERTYPE *pSoldier ); UINT16 GetCorpseStructIndex( ROTTING_CORPSE_DEFINITION *pCorpseDef, BOOLEAN fForImage ); @@ -221,4 +235,7 @@ UINT8 GetNearestRottingCorpseAIWarning( INT32 sGridNo ); BOOLEAN CorpseOkToSpawnZombie( ROTTING_CORPSE * pCorpse, UINT16* pAnimState ); #endif +// Flugente: can we take the clothes of this corpse? +BOOLEAN CorpseOkToDress( ROTTING_CORPSE * pCorpse, SOLDIERTYPE* pSoldier ); + #endif \ No newline at end of file diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index f9db5c8f..46720fdf 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -2926,7 +2926,10 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) case 752: // Code: decapitate - DecapitateCorpse( pSoldier, pSoldier->sTargetGridNo, pSoldier->bTargetLevel ); + //DecapitateCorpse( pSoldier, pSoldier->sTargetGridNo, pSoldier->bTargetLevel ); + + // Flugente: instead of jsut decapitating, we call a selection window where we can choose what to do with the corpse + HandleSoldierUseCorpse( pSoldier, pSoldier->sTargetGridNo, pSoldier->bTargetLevel ); // Flugente: handle corpses break; case 753: diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 4cdd7f37..a37e6d78 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -6315,6 +6315,10 @@ void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sD } } + // Flugente: if hit in legs or torso, blood will be on our uniform - this unifrom can't be used to disguise someone anymore + if ( ubHitLocation == AIM_SHOT_TORSO || ubHitLocation == AIM_SHOT_LEGS ) + pSoldier->bSoldierFlagMask |= SOLDIER_DAMAGED_UNIFORM; + // IF HERE AND GUY IS DEAD, RETURN! if ( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) { @@ -6361,6 +6365,10 @@ void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sD void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) { + // Flugente: if hit in legs or torso, blood will be on our uniform - this unifrom can't be used to disguise someone anymore + if ( ubHitLocation == AIM_SHOT_TORSO || ubHitLocation == AIM_SHOT_LEGS ) + pSoldier->bSoldierFlagMask |= SOLDIER_DAMAGED_UNIFORM; + INT32 sNewGridNo; // IF HERE AND GUY IS DEAD, RETURN! @@ -6529,6 +6537,9 @@ void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) { + // Flugente: if hit in legs or torso, blood will be on our uniform - this unifrom can't be used to disguise someone anymore + if ( ubHitLocation == AIM_SHOT_TORSO || ubHitLocation == AIM_SHOT_LEGS ) + pSoldier->bSoldierFlagMask |= SOLDIER_DAMAGED_UNIFORM; // IF HERE AND GUY IS DEAD, RETURN! if ( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) @@ -11684,7 +11695,8 @@ void SOLDIERTYPE::EVENT_SoldierBeginBladeAttack( INT32 sGridNo, UINT8 ubDirectio } else { - if ( IsValidDecapitationCorpse( pCorpse ) ) + // Flugente: decapitation action now also performs gutting, stripping and taking corpses + if ( IsValidDecapitationCorpse( pCorpse ) || IsValidGutCorpse( pCorpse ) || IsValidStripCorpse( pCorpse ) || IsValidTakeCorpse( pCorpse ) ) { this->EVENT_InitNewSoldierAnim( DECAPITATE, 0 , FALSE ); } @@ -14393,6 +14405,742 @@ void SOLDIERTYPE::CleanWeapon() } } +extern INT16 uiNIVSlotType[NUM_INV_SLOTS]; + +// do we look like a civilian? +BOOLEAN SOLDIERTYPE::LooksLikeACivilian( void ) +{ + // if we have any camo: not covert + if ( GetWornCamo(this) > 0 || GetWornUrbanCamo(this) > 0 || GetWornDesertCamo(this) > 0 || GetWornSnowCamo(this) > 0 ) + return FALSE; + + if ( UsingNewInventorySystem() ) + { + INT8 invsize = (INT8)this->inv.size(); + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... + { + if ( this->inv[bLoop].exists() ) + { + // if we have a back pack: not covert + if ( bLoop == BPACKPOCKPOS ) + return FALSE; + + // do not check the LBE itself (we already checked for camo above) + if ( bLoop >= VESTPOCKPOS && bLoop <= CPACKPOCKPOS ) + continue; + + BOOLEAN checkfurther = FALSE; + // further checks it item is not covert. This means that a gun that has that tag will not be detectedif if its inside a pocket! + if ( bLoop == HANDPOS || bLoop == SECONDHANDPOS || bLoop == GUNSLINGPOCKPOS || bLoop == HELMETPOS || bLoop == VESTPOS || bLoop == LEGPOS || bLoop == HEAD1POS || bLoop == HEAD2POS ) + checkfurther = TRUE; + else if ( bLoop == KNIFEPOCKPOS && !HasItemFlag(this->inv[bLoop].usItem, COVERT) ) + checkfurther = TRUE; + else if ( !HasItemFlag(this->inv[bLoop].usItem, COVERT) ) + { + checkfurther = TRUE; + + // item will be detected if someone looks - check for the LBE item that gave us this slot. If that one is covert, this item is also covert + UINT8 checkslot = 0; + switch (uiNIVSlotType[bLoop]) + { + case 3: + checkslot = VESTPOCKPOS; + break; + case 4: + if ( bLoop == 23 || bLoop == 35 || bLoop == 36 || bLoop == 37 || bLoop == 38 ) + checkslot = LTHIGHPOCKPOS; + else + checkslot = RTHIGHPOCKPOS; + break; + case 5: + checkslot = CPACKPOCKPOS; + break; + default: + return FALSE; + break; + } + + // found a slot to check for LBE + if ( checkslot > 0 ) + { + // if LBE is covert + if ( this->inv[checkslot].exists() && HasItemFlag(this->inv[checkslot].usItem, COVERT) ) + // pass for this item + checkfurther = FALSE; + } + } + + if ( checkfurther ) + { + // if that item is a gun, explosives, military armour or facewear, we're screwed + if ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_WEAPON|IC_GRENADE|IC_BOMB) ) || + ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_ARMOUR) ) && !Item[this->inv[bLoop].usItem].leatherjacket && Armour[ Item[this->inv[bLoop].usItem].ubClassIndex ].ubProtection > 10 ) || + ( Item[this->inv[bLoop].usItem].nightvisionrangebonus > 0 || Item[this->inv[bLoop].usItem].hearingrangebonus > 0 ) + ) + return FALSE; + } + } + } + } + else // old inventory system. No LBE here, nothing fancy + { + INT8 invsize = (INT8)this->inv.size(); + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... + { + if ( this->inv[bLoop].exists() ) + { + if ( !HasItemFlag(this->inv[bLoop].usItem, COVERT) ) + { + // if that item is a gun, explosives, military armour or facewear, we're screwed + if ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_WEAPON|IC_GRENADE|IC_BOMB) ) || + ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_ARMOUR) ) && !Item[this->inv[bLoop].usItem].leatherjacket && Armour[ Item[this->inv[bLoop].usItem].ubClassIndex ].ubProtection > 10 ) || + ( Item[this->inv[bLoop].usItem].nightvisionrangebonus > 0 || Item[this->inv[bLoop].usItem].hearingrangebonus > 0 ) + ) + return FALSE; + } + } + } + } + + return TRUE; +} + +// do we look like a soldier? +BOOLEAN SOLDIERTYPE::LooksLikeASoldier( void ) +{ + return TRUE; +} + +// is our equipment too good for a soldier? +BOOLEAN SOLDIERTYPE::EquipmentTooGood( BOOLEAN fCloselook ) +{ + // check the guns in our hands an rifle sling + // alert if we have more than 2, any of them has too much attachments or they are way too cool + UINT8 numberofguns = 0; + UINT8 ubCurrentProgress = CurrentPlayerProgressPercentage(); + UINT8 maxcoolnessallowed = 1 + ubCurrentProgress / 10; + maxcoolnessallowed += 2; // TODO adjust for level and uniform + UINT16 maxitemsize = 4; + + if ( UsingNewInventorySystem() ) + { + INT8 invsize = (INT8)this->inv.size(); + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... + { + if ( this->inv[bLoop].exists() ) + { + // if we have a back pack: not covert + if ( bLoop == BPACKPOCKPOS ) + return FALSE; + + // further checks it item is not covert. This means that a gun that has that tag will not be detectedif if its inside a pocket! + if ( bLoop == HANDPOS || bLoop == SECONDHANDPOS || bLoop == GUNSLINGPOCKPOS || bLoop == HELMETPOS || bLoop == VESTPOS || bLoop == LEGPOS || bLoop == HEAD1POS || bLoop == HEAD2POS || + !HasItemFlag(this->inv[bLoop].usItem, COVERT) ) + { + // item will be detected if someone looks - check for the LBE item that gave us this slot. If that one is covert, this item is also covert + UINT8 checkslot = 0; + switch (uiNIVSlotType[bLoop]) + { + case 3: + checkslot = VESTPOCKPOS; + break; + case 4: + if ( bLoop == 23 || bLoop == 35 || bLoop == 36 || bLoop == 37 || bLoop == 38 ) + checkslot = LTHIGHPOCKPOS; + else + checkslot = RTHIGHPOCKPOS; + break; + case 5: + checkslot = CPACKPOCKPOS; + break; + default: + return FALSE; + break; + } + + // found a slot to check for LBE + if ( checkslot > 0 ) + { + // if LBE is covert + if ( this->inv[checkslot].exists() && HasItemFlag(this->inv[checkslot].usItem, COVERT) ) + // pass for this item + continue; + } + + // if that item is a gun, explosives, military armour or facewear, investigate further + if ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_GUN|IC_LAUNCHER|IC_ARMOUR|IC_FACE) ) ) + { + if ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_GUN|IC_LAUNCHER) ) ) + { + ++numberofguns; + + if ( numberofguns > 2 ) + return TRUE; + + OBJECTTYPE * pObj = &(this->inv[bLoop]); // ... get pointer for this item ... + + if ( pObj != NULL ) + { + for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ... + { + // loop over every item and its attachments + if ( Item[pObj->usItem].ubCoolness > maxcoolnessallowed ) + return TRUE; + + UINT8 numberofattachments = 0; + // for every objects, we also have to check wether there are weapon attachments (eg. underbarrel grenade launchers), and cool them down too + attachmentList::iterator iterend = (*pObj)[i]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != iterend; ++iter) + { + if ( iter->exists() ) + { + // loop over every item and its attachments + if ( Item[iter->usItem].ubCoolness > maxcoolnessallowed ) + return TRUE; + + ++numberofattachments; + } + } + + // no ordinary soldier is allowed that many attachemnts > not covert + if ( numberofattachments > gGameExternalOptions.iMaxEnemyAttachments ) + return TRUE; + } + } + } + } + } + } + } + } + else // old inventory system. No LBE here, nothing fancy + { + INT8 invsize = (INT8)this->inv.size(); + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... + { + if ( this->inv[bLoop].exists() ) + { + // if that item is a gun, explosives, military armour or facewear, investigate further + if ( !HasItemFlag(this->inv[bLoop].usItem, COVERT) && (Item[this->inv[bLoop].usItem].usItemClass & (IC_GUN|IC_LAUNCHER|IC_ARMOUR|IC_FACE) ) ) + { + if ( (Item[this->inv[bLoop].usItem].usItemClass & (IC_GUN|IC_LAUNCHER) ) ) + { + ++numberofguns; + + if ( numberofguns > 2 ) + return TRUE; + + OBJECTTYPE * pObj = &(this->inv[bLoop]); // ... get pointer for this item ... + + if ( pObj != NULL ) + { + for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ... + { + // loop over every item and its attachments + if ( Item[pObj->usItem].ubCoolness > maxcoolnessallowed ) + return TRUE; + + UINT8 numberofattachments = 0; + // for every objects, we also have to check wether there are weapon attachments (eg. underbarrel grenade launchers), and cool them down too + attachmentList::iterator iterend = (*pObj)[i]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != iterend; ++iter) + { + if ( iter->exists() ) + { + // loop over every item and its attachments + if ( Item[iter->usItem].ubCoolness > maxcoolnessallowed ) + return TRUE; + + ++numberofattachments; + } + } + + // no ordinary soldier is allowed that many attachments > not covert + if ( numberofattachments > gGameExternalOptions.iMaxEnemyAttachments ) + return TRUE; + } + } + } + } + } + } + } + + return FALSE; +} + + +// are we in covert mode? we need to have the correct flag set, and not wear anything suspicious, or behave in a suspicious way +BOOLEAN SOLDIERTYPE::SeemsLegit( UINT8 ubObserverID ) +{ + SOLDIERTYPE* pSoldier = MercPtrs[ubObserverID]; + + if ( !pSoldier ) + return TRUE; + + // if we don't have the Flag: not covert + // important: no messages up to this point. the function will get called a lot, up to this point there is nothing unusual + if ( !(this->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) ) + return FALSE; + + // if we are in a suspicious stance: not covert + if ( this->usAnimState == NINJA_SPINKICK || + this->usAnimState == NINJA_PUNCH || + this->usAnimState == NINJA_LOWKICK || + this->usAnimState == PUNCH_LOW || + this->usAnimState == DECAPITATE || + this->usAnimState == CROWBAR_ATTACK || + this->usAnimState == THROW_GRENADE_STANCE || + this->usAnimState == THROW_KNIFE_SP_BM || + this->usAnimState == DODGE_ONE || + this->usAnimState == SLICE || + this->usAnimState == STAB || + this->usAnimState == CROUCH_STAB || + this->usAnimState == PUNCH || + this->usAnimState == PUNCH_BREATH || + this->usAnimState == KICK_DOOR || + this->usAnimState == CUTTING_FENCE || + this->usAnimState == THROW_KNIFE || + this->usAnimState == PLANT_BOMB || + this->usAnimState == USE_REMOTE || + this->usAnimState == TAKE_BLOOD_FROM_CORPSE || + this->usAnimState == PICK_LOCK || + this->usAnimState == LOCKPICK_CROUCHED || + this->usAnimState == STEAL_ITEM_CROUCHED || + this->usAnimState == JUMPWINDOWS + ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s was seen performing suspicious activities!", this->name ); + return FALSE; + } + + BOOLEAN looklikeaciv = this->LooksLikeACivilian(); + + // if we are trying to dress like a civilian, but aren't sucessful: not covert + if ( this->bSoldierFlagMask & SOLDIER_COVERT_CIV && !looklikeaciv) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not look like a civilian!", this->name ); + return FALSE; + } + + BOOLEAN looklikeasoldier = this->LooksLikeASoldier(); + + // if we are trying to dress like a soldier, but aren't sucessful: not covert + if ( this->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER && !looklikeasoldier) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not look like a soldier!", this->name ); + return FALSE; + } + + UINT8 covertlevel = NUM_SKILL_TRAITS( this, COVERT_NT ); // our level in covert operations + INT32 distance = GetRangeFromGridNoDiff(this->sGridNo, pSoldier->sGridNo); + + // if we are closer than this, our cover will always break if we do not have the skill + // if we have the skill, our cover will blow if we dress up as a soldier, but not if we are dressed like a civilian + INT32 discoverrange = gSkillTraitValues.sCOCloseDetectionRange; + + if ( distance < discoverrange ) + { + switch ( covertlevel ) + { + case 2: + // a covert ops expert can get as close as he wants, even dressed up as a soldier, without arousing suspicion + // exceptions: we are discovered if we are close and bleeding, or if we are drunk while dressed as a soldier + { + // if we are openly bleeding: not covert + if ( this->bBleeding > 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s bleeding was discovered!", this->name ); + return FALSE; + } + + if ( this->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER && GetDrunkLevel( this ) != SOBER ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s is drunk and doesn't look like a soldier!", this->name ); + return FALSE; + } + } + break; + case 1: + // at lvl covert ops, we can be discovered if we are too close to the enemy and bleed or dressed up as a soldier + // however, if we are dressed up as a civilian, we can get as close as we like, we won't be discovered + { + // if we are openly bleeding: not covert + if ( this->bBleeding > 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s bleeding was discovered!", this->name ); + return FALSE; + } + + if ( this->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"On closer inspection, %s's disguise does not hold!", this->name ); + return FALSE; + } + } + break; + case 0: + default: + // without the covert ops skill, we can only dress up as civilians. We will be discovered if we get too close to the enemy + if ( (this->bSoldierFlagMask & SOLDIER_COVERT_NPC_SPECIAL) == 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"On closer inspection, %s's disguise does not hold!", this->name ); + return FALSE; + } + break; + } + } + + if ( this->bSoldierFlagMask & SOLDIER_COVERT_CIV ) + { + // civilians are suspicious if they are found in certain sectors. Especially at night + // sector specific value: + // 0 - civilians are always ok + // 1 - civilians are suspicious at night + // 2 - civilians are always suspicious + + UINT8 sectordata = 0; + UINT8 ubSectorId = SECTOR(gWorldSectorX, gWorldSectorY); + if ( gbWorldSectorZ > 0 ) + // underground we are always suspicious + sectordata = 2; + else if ( ubSectorId >= 0 && ubSectorId < 256 ) + sectordata = SectorExternalData[ubSectorId][gbWorldSectorZ].usCurfewValue; + + if ( sectordata > 1 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s isn't supposed to be here!", this->name ); + return FALSE; + } + // is it night? + else if ( sectordata == 1 && GetTimeOfDayAmbientLightLevel() < NORMAL_LIGHTLEVEL_DAY + 2 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s isn't supposed to be here at this time!", this->name ); + return FALSE; + } + + // check wether we are around a fresh corpse - this will make us much more suspicious + INT32 cnt; + ROTTING_CORPSE * pCorpse; + for ( cnt = 0; cnt < giNumRottingCorpse; ++cnt ) + { + pCorpse = &(gRottingCorpse[ cnt ] ); + + if ( pCorpse && pCorpse->fActivated && pCorpse->def.ubAIWarningValue > 0 && PythSpacesAway( this->sGridNo, pCorpse->def.sGridNo ) <= 5 ) + { + // a corpse was found near our position. If the soldier observing us can see it, he will be alarmed + if ( SoldierTo3DLocationLineOfSightTest( pSoldier, pCorpse->def.sGridNo, pCorpse->def.bLevel, 3, TRUE, CALC_FROM_WANTED_DIR ) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s was seen near a fresh corpse!", this->name ); + return FALSE; + } + } + } + } + + if ( this->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + { + // if our equipment is too good, that is suspicious... not covert! + if ( this->EquipmentTooGood( (distance < discoverrange) ) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s equipment raises a few eyebrows!", this->name ); + return FALSE; + } + + // 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 ( WeaponReady(this) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s is seen targeting a soldier!", this->name ); + return FALSE; + } + } + + // even as a soldier, we will be caught around fresh corpses + if (distance < gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse) + { + // 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 :-) + INT32 cnt; + ROTTING_CORPSE * pCorpse; + for ( cnt = 0; cnt < giNumRottingCorpse; ++cnt ) + { + pCorpse = &(gRottingCorpse[ cnt ] ); + + if ( pCorpse && pCorpse->fActivated && pCorpse->def.ubAIWarningValue > 0 && PythSpacesAway( this->sGridNo, pCorpse->def.sGridNo ) <= 5 ) + { + // a corpse was found near our position. If the soldier observing us can see it, he will be alarmed + if ( SoldierTo3DLocationLineOfSightTest( pSoldier, pCorpse->def.sGridNo, pCorpse->def.bLevel, 3, TRUE, CALC_FROM_WANTED_DIR ) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s was seen near a fresh corpse!", this->name ); + return FALSE; + } + } + } + } + } + + return TRUE; +} + +// do we recognize someone else as a combatant? +BOOLEAN SOLDIERTYPE::RecognizeAsCombatant(UINT8 ubTargetID) +{ + // this will only work with the new trait system + if (!gGameOptions.fNewTraitSystem) + return TRUE; + + SOLDIERTYPE* pSoldier = MercPtrs[ubTargetID]; + + if ( !pSoldier ) + return TRUE; + + // if neither of the 2 persons is covert, always return true + if ( ( (pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0 ) && ( (this->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0 ) ) + return TRUE; + + // only members of the player team can ever be covert, sorry AI + if ( pSoldier->bTeam != OUR_TEAM ) + return TRUE; + + // neutral characters just dont care + if ( this->aiData.bNeutral ) + return TRUE; + + // check for for vehicles and creatures... weird things happen + if ( IsVehicle(pSoldier) || pSoldier->bTeam == CREATURE_TEAM || this->bTeam == CREATURE_TEAM ) + return TRUE; + + // checking someone from our team... + if ( this->bTeam == pSoldier->bTeam ) + { + // not in covert mode: we recognize him + if ( (pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0 ) + return TRUE; + else + // we don't because we accept that he is covert (to not blow his cover) + return FALSE; + } + + // hack: if this is attacking us at this very moment by punching, do not recognize him... + // this resolves the problem that we attack someone from behind and kill him instantly, but the game mechanic forces him to turn before + // only allow this if we are not yet alerted (we are suprised, so we don't recognize him in the moment of the attack + // also: only allow if he's next to us + if ( this->aiData.bAlertStatus < STATUS_RED && pSoldier->ubTargetID == this->ubID ) + { + INT32 nextGridNoinSight = NewGridNo( pSoldier->sGridNo, DirectionInc( pSoldier->ubDirection ) ); + if ( nextGridNoinSight == this->sGridNo && this->pathing.bLevel == pSoldier->pathing.bLevel ) + { + if ( pSoldier->usAnimState == PUNCH ) + return FALSE; + else if ( pSoldier->usAnimState == PUNCH_BREATH ) + return TRUE; + } + } + + // do we recognize this guy as an enemy? + if ( !pSoldier->SeemsLegit(this->ubID) ) + { + // aha, he/she's a spy! Blow cover + if ( pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) + { + pSoldier->LooseDisguise(); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s has seen through %s disguise!", this->name, pSoldier->name ); + + // we have uncovered a spy! Get alerted, if we aren't already + if ( this->aiData.bAlertStatus < STATUS_BLACK ) + this->aiData.bAlertStatus = STATUS_BLACK; + } + + return TRUE; + } + + return FALSE; +} + +// dress up in our natural state +void SOLDIERTYPE::LooseDisguise( void ) +{ + // show our true colours + UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( this, this->usAnimState ); + + if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + { + UINT8 ubProfileIndex; + MERCPROFILESTRUCT * pProfile; + + ubProfileIndex = this->ubProfile; + pProfile = &(gMercProfiles[ubProfileIndex]); + + SET_PALETTEREP_ID ( this->VestPal, pProfile->VEST ); + SET_PALETTEREP_ID ( this->PantsPal, pProfile->PANTS ); + + // Use palette from HVOBJECT, then use substitution for pants, etc + memcpy( this->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( this->p8BPPPalette ) * 256 ); + + SetPaletteReplacement( this->p8BPPPalette, this->HeadPal ); + SetPaletteReplacement( this->p8BPPPalette, this->VestPal ); + SetPaletteReplacement( this->p8BPPPalette, this->PantsPal ); + SetPaletteReplacement( this->p8BPPPalette, this->SkinPal ); + + this->CreateSoldierPalettes(); + } + + // loose any covert flags + // if we are disguised as a civilian, remove that flag, and give us back a civilian clothes item + if ( this->bSoldierFlagMask & SOLDIER_COVERT_CIV ) + { + this->bSoldierFlagMask &= ~SOLDIER_COVERT_CIV; + + UINT16 civilianclothesitem = 0; + if ( GetFirstItemWithFlag(&civilianclothesitem, CLOTHES_CIVILIAN) ) + { + CreateItem( civilianclothesitem, 100, &gTempObject ); + + if ( !AutoPlaceObject( this, &gTempObject, FALSE ) ) + AddItemToPool( sGridNo, &gTempObject, 1, 0, 0, -1 ); + } + } + + // if we are also disguised as a soldier, remove that flag + if ( this->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + { + this->bSoldierFlagMask &= ~SOLDIER_COVERT_SOLDIER; + } +} + +// dress up like a civilian +BOOLEAN SOLDIERTYPE::DisguiseAsCivilian( void ) +{ + // this will only work with the new trait system + if (!gGameOptions.fNewTraitSystem) + return FALSE; + + UINT8 skilllevel = NUM_SKILL_TRAITS( this, COVERT_NT ); + + INT16 apcost = (APBPConstants[AP_DISGUISE] * ( gSkillTraitValues.sCODisguiseAPReduction * skilllevel))/100; + if ( !EnoughPoints( this, apcost, 0, TRUE ) ) + return( FALSE ); + + this->bSoldierFlagMask |= SOLDIER_COVERT_CIV; + + // wear an enemy uniform + UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( this, this->usAnimState ); + + if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + { + // wear something random + switch( Random( 7 ) ) + { + case 0: SET_PALETTEREP_ID( this->VestPal, "WHITEVEST" ); break; + case 1: SET_PALETTEREP_ID( this->VestPal, "BLACKSHIRT" ); break; + case 2: SET_PALETTEREP_ID( this->VestPal, "PURPLESHIRT" ); break; + case 3: SET_PALETTEREP_ID( this->VestPal, "BLUEVEST" ); break; + case 4: SET_PALETTEREP_ID( this->VestPal, "BROWNVEST" ); break; + case 5: SET_PALETTEREP_ID( this->VestPal, "JEANVEST" ); break; + case 6: SET_PALETTEREP_ID( this->VestPal, "REDVEST" ); break; + } + + switch( Random( 3 ) ) + { + case 0: SET_PALETTEREP_ID( this->PantsPal, "TANPANTS" ); break; + case 1: SET_PALETTEREP_ID( this->PantsPal, "BEIGEPANTS" ); break; + case 2: SET_PALETTEREP_ID( this->PantsPal, "GREENPANTS" ); break; + } + + // Use palette from HVOBJECT, then use substitution for pants, etc + memcpy( this->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( this->p8BPPPalette ) * 256 ); + + SetPaletteReplacement( this->p8BPPPalette, this->HeadPal ); + SetPaletteReplacement( this->p8BPPPalette, this->VestPal ); + SetPaletteReplacement( this->p8BPPPalette, this->PantsPal ); + SetPaletteReplacement( this->p8BPPPalette, this->SkinPal ); + + this->CreateSoldierPalettes(); + } + + // if we are also disguised as a soldier, remove that flag + if ( this->bSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) + { + this->bSoldierFlagMask &= ~SOLDIER_COVERT_SOLDIER; + } + + DeductPoints( this, apcost, 0 ); + + return TRUE; +} + +// dress up like an enemy soldier +BOOLEAN SOLDIERTYPE::DisguiseAsSoldierFromCorpse( UINT32 usFlag ) +{ + // this will only work with the new trait system + if (!gGameOptions.fNewTraitSystem) + return FALSE; + + // action is only possible if we have the covert ops trait + UINT8 skilllevel = NUM_SKILL_TRAITS( this, COVERT_NT ); + if ( skilllevel < 1) + return FALSE; + + INT16 apcost = (APBPConstants[AP_DISGUISE] * ( gSkillTraitValues.sCODisguiseAPReduction * skilllevel))/100; + if ( !EnoughPoints( this, apcost, 0, TRUE ) ) + return( FALSE ); + + UINT8 uniformtype = 0; + + if ( usFlag & ROTTING_CORPSE_FROM_ADMIN ) + uniformtype = UNIFORM_ENEMY_ADMIN; + else if ( usFlag & ROTTING_CORPSE_FROM_TROOP ) + uniformtype = UNIFORM_ENEMY_TROOP; + else if ( usFlag & ROTTING_CORPSE_FROM_ELITE ) + uniformtype = UNIFORM_ENEMY_ELITE; + else + // no uniform here, so nothing to wear + return FALSE; + + this->bSoldierFlagMask |= SOLDIER_COVERT_SOLDIER; + + // wear an enemy uniform + UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( this, this->usAnimState ); + + if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + { + SET_PALETTEREP_ID(this->VestPal, gUniformColors[ uniformtype ].vest ); + SET_PALETTEREP_ID(this->PantsPal, gUniformColors[ uniformtype ].pants ); + + // Use palette from HVOBJECT, then use substitution for pants, etc + memcpy( this->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( this->p8BPPPalette ) * 256 ); + + SetPaletteReplacement( this->p8BPPPalette, this->HeadPal ); + SetPaletteReplacement( this->p8BPPPalette, this->VestPal ); + SetPaletteReplacement( this->p8BPPPalette, this->PantsPal ); + SetPaletteReplacement( this->p8BPPPalette, this->SkinPal ); + + this->CreateSoldierPalettes(); + } + + // if we are disguised as a civilian, remove that flag, and give us back a civilian clothes item + if ( this->bSoldierFlagMask & SOLDIER_COVERT_CIV ) + { + this->bSoldierFlagMask &= ~SOLDIER_COVERT_CIV; + + UINT16 civilianclothesitem = 0; + if ( GetFirstItemWithFlag(&civilianclothesitem, CLOTHES_CIVILIAN) ) + { + CreateItem( civilianclothesitem, 100, &gTempObject ); + AddItemToPool( sGridNo, &gTempObject, 1, 0, 0, -1 ); + } + } + + DeductPoints( this, apcost, 0 ); + + return TRUE; +} + + INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; //,savedOurTurn; @@ -16455,13 +17203,13 @@ BOOLEAN HAS_SKILL_TRAIT( SOLDIERTYPE * pSoldier, UINT8 uiSkillTraitNumber ) { for ( INT8 bCnt = 0; bCnt < min(30,bMaxTraits); bCnt++ ) { - if ( uiSkillTraitNumber > 0 && uiSkillTraitNumber <= NUM_MAJOR_TRAITS ) + if ( uiSkillTraitNumber > 0 && (uiSkillTraitNumber <= NUM_MAJOR_TRAITS || uiSkillTraitNumber == COVERT_NT) ) { if (pSoldier->stats.ubSkillTraits[ bCnt ] == uiSkillTraitNumber) { return( TRUE ); } - else if ( pSoldier->stats.ubSkillTraits[ bCnt ] > 0 && pSoldier->stats.ubSkillTraits[ bCnt ] <= NUM_MAJOR_TRAITS ) + else if ( pSoldier->stats.ubSkillTraits[ bCnt ] > 0 && (pSoldier->stats.ubSkillTraits[ bCnt ] <= NUM_MAJOR_TRAITS || pSoldier->stats.ubSkillTraits[ bCnt ] == COVERT_NT) ) { bNumMajorTraitsCounted++; } @@ -16511,14 +17259,14 @@ INT8 NUM_SKILL_TRAITS( SOLDIERTYPE * pSoldier, UINT8 uiSkillTraitNumber ) { for ( INT8 bCnt = 0; bCnt < min(30,bMaxTraits); bCnt++ ) { - if ( uiSkillTraitNumber > 0 && uiSkillTraitNumber <= NUM_MAJOR_TRAITS ) + if ( uiSkillTraitNumber > 0 && (uiSkillTraitNumber <= NUM_MAJOR_TRAITS || uiSkillTraitNumber == COVERT_NT) ) { if ( pSoldier->stats.ubSkillTraits[ bCnt ] == uiSkillTraitNumber ) { bNumberOfTraits++; bNumMajorTraitsCounted++; } - else if ( pSoldier->stats.ubSkillTraits[ bCnt ] > 0 && pSoldier->stats.ubSkillTraits[ bCnt ] <= NUM_MAJOR_TRAITS ) + else if ( pSoldier->stats.ubSkillTraits[ bCnt ] > 0 && (pSoldier->stats.ubSkillTraits[ bCnt ] <= NUM_MAJOR_TRAITS || pSoldier->stats.ubSkillTraits[ bCnt ] == COVERT_NT ) ) { bNumMajorTraitsCounted++; } @@ -16537,7 +17285,7 @@ INT8 NUM_SKILL_TRAITS( SOLDIERTYPE * pSoldier, UINT8 uiSkillTraitNumber ) } } // cannot have more than one same minor trait - if( uiSkillTraitNumber > NUM_MAJOR_TRAITS ) + if( uiSkillTraitNumber > NUM_MAJOR_TRAITS && uiSkillTraitNumber != COVERT_NT) return ( min(1, bNumberOfTraits) ); else return ( min(2, bNumberOfTraits) ); diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 3bd34f00..7ceefc0e 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -345,12 +345,12 @@ enum // easier than adding 32 differently named variables. DO NOT CHANGE THEM, UNLESS YOU KNOW WHAT YOU ARE DOING!!! #define SOLDIER_DRUGGED 0x00000001 //1 // Soldier is on drugs #define SOLDIER_NO_AP 0x00000002 //2 // Soldier has no APs this turn (fix for reinforcement bug) -/*#define WH40K_BOLTER 0x00000004 //4 -#define WH40K_FLAMER 0x00000008 //8 +#define SOLDIER_COVERT_CIV 0x00000004 //4 // Soldier is currently disguised as a civilian +#define SOLDIER_COVERT_SOLDIER 0x00000008 //8 // Soldier is currently disguised as an enemy soldier -#define WH40K_POWER_ARMOR 0x00000010 //16 -#define WH40K_POWER_PACK 0x00000020 //32 -#define WH40K_JUMPPACK 0x00000040 //64 +#define SOLDIER_DAMAGED_UNIFORM 0x00000010 //16 // Soldier's uniform is damged (and thus can't be taken off of his corpse) +#define SOLDIER_COVERT_NPC_SPECIAL 0x00000020 //32 // Special flag for NPCs wehn recruited (used for covert stuff) +/*#define WH40K_JUMPPACK 0x00000040 //64 #define WH40K_DISPLACER 0x00000080 //128 #define WH40K_ROSARIUS 0x00000100 //256 @@ -1456,6 +1456,34 @@ public: // use any cleaning kits to clean weapons in inventory void CleanWeapon(); + + // Flugente: functions for the covert ops trait + + // are we in covert mode? we need to have the correct flag set, and not wear anything suspicious, or behave in a suspicious way + + // do we look like a civilian? + BOOLEAN LooksLikeACivilian( void ); + + // do we look like a soldier? + BOOLEAN LooksLikeASoldier( void ); + + // is our equipment too good for a soldier? + BOOLEAN EquipmentTooGood( BOOLEAN fCloselook ); + + // does soldier ubObserverID recognize us as his enemy? + BOOLEAN SeemsLegit( UINT8 ubObserverID ); + + // do we recognize someone else as a combatant? + BOOLEAN RecognizeAsCombatant(UINT8 ubTargetID); + + // loose disguise, dress up in our natural state + void LooseDisguise( void ); + + // dress up like a civilian + BOOLEAN DisguiseAsCivilian( void ); + + // dress up like an enemy soldier + BOOLEAN DisguiseAsSoldierFromCorpse( UINT32 usFlag ); ////////////////////////////////////////////////////////////////////////////// }; // SOLDIERTYPE; diff --git a/Tactical/Soldier Profile.cpp b/Tactical/Soldier Profile.cpp index 0e178fe1..ea5fcd0e 100644 --- a/Tactical/Soldier Profile.cpp +++ b/Tactical/Soldier Profile.cpp @@ -1701,6 +1701,16 @@ BOOLEAN RecruitRPC( UINT8 ubCharNum ) GiveQuestRewardPoint( pSoldier->sSectorX, pSoldier->sSectorY, 6, MIGUEL ); } + // Flugente: people recruited in Arulco are known to the enemy as civilians or even soldiers. So they will be covert whn recruiting them. Of course, this is not for the rebels... + if ( ubCharNum == DEVIN || ubCharNum == HAMOUS || ubCharNum == SLAY || ubCharNum == VINCE || ubCharNum == MADDOG || ubCharNum == MICKY ) + { + pNewSoldier->bSoldierFlagMask |= (SOLDIER_COVERT_CIV|SOLDIER_COVERT_NPC_SPECIAL); + } + else if ( ubCharNum == IGGY || ubCharNum == CONRAD ) + { + pNewSoldier->bSoldierFlagMask |= (SOLDIER_COVERT_SOLDIER|SOLDIER_COVERT_NPC_SPECIAL); + } + // handle town loyalty adjustment HandleTownLoyaltyForNPCRecruitment( pNewSoldier ); @@ -2650,14 +2660,14 @@ INT8 ProfileHasSkillTrait( INT32 ubProfileID, INT8 bSkillTrait ) { for ( INT8 bCnt = 0; bCnt < gSkillTraitValues.ubMaxNumberOfTraits; bCnt++ ) { - if ( bSkillTrait > 0 && bSkillTrait <= NUM_MAJOR_TRAITS ) + if ( bSkillTrait > 0 && (bSkillTrait <= DOCTOR_NT || bSkillTrait == COVERT_NT ) ) { if ( gMercProfiles[ubProfileID].bSkillTraits[ bCnt ] == bSkillTrait ) { bNumTraits++; bNumMajorTraitsCounted++; } - else if ( gMercProfiles[ubProfileID].bSkillTraits[ bCnt ] > 0 && gMercProfiles[ubProfileID].bSkillTraits[ bCnt ] <= NUM_MAJOR_TRAITS ) + else if ( gMercProfiles[ubProfileID].bSkillTraits[ bCnt ] > 0 && (gMercProfiles[ubProfileID].bSkillTraits[ bCnt ] <= DOCTOR_NT || gMercProfiles[ubProfileID].bSkillTraits[ bCnt ] <= COVERT_NT) ) { bNumMajorTraitsCounted++; } @@ -2676,7 +2686,7 @@ INT8 ProfileHasSkillTrait( INT32 ubProfileID, INT8 bSkillTrait ) } } // cannot have more than 1 rade of minor traits or 2 grades of a major traits - if( bSkillTrait > NUM_MAJOR_TRAITS ) + if( bSkillTrait > DOCTOR_NT && bSkillTrait != COVERT_NT ) return ( min(1, bNumTraits) ); else return ( min(2, bNumTraits) ); diff --git a/Tactical/SoldierTooltips.cpp b/Tactical/SoldierTooltips.cpp index 249697e9..7f3bd072 100644 --- a/Tactical/SoldierTooltips.cpp +++ b/Tactical/SoldierTooltips.cpp @@ -238,7 +238,7 @@ void SoldierTooltip( SOLDIERTYPE* pSoldier ) CHAR16 pStrAux[50]; swprintf( pStrAux, L"(%s)", gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]]); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, pStrAux ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1] + 19] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1] + NEWTRAIT_MERCSKILL_EXPERTOFFSET] ); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2]] ); //swprintf( pStrInfo, L"%s\n", pStrInfo ); } @@ -247,7 +247,7 @@ void SoldierTooltip( SOLDIERTYPE* pSoldier ) CHAR16 pStrAux[50]; swprintf( pStrAux, L"(%s)", gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1]]); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, pStrAux ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2] + 19] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2] + NEWTRAIT_MERCSKILL_EXPERTOFFSET] ); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]] ); } else if (( pSoldier->stats.ubSkillTraits[0] == pSoldier->stats.ubSkillTraits[2] ) && pSoldier->stats.ubSkillTraits[0] != 0 ) @@ -255,7 +255,7 @@ void SoldierTooltip( SOLDIERTYPE* pSoldier ) CHAR16 pStrAux[50]; swprintf( pStrAux, L"(%s)", gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]]); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, pStrAux ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2] + 19] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2] + NEWTRAIT_MERCSKILL_EXPERTOFFSET] ); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1]] ); } else diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index b6a953df..4e6bf58b 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -4449,7 +4449,7 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) /*// Flugente: spawn items while debugging if ( gusSelectedSoldier != NOBODY ) { - static UINT16 usitem = 1576; + static UINT16 usitem = 142; static INT16 status = 100; static FLOAT temperature = 0.0; diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 2b0964fc..2da65b81 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -8960,8 +8960,21 @@ INT32 BulletImpact( SOLDIERTYPE *pFirer, BULLET *pBullet, SOLDIERTYPE * pTarget, if ( iImpact > 0 && !TANK( pTarget ) ) { + // Flugente: ammo can now add the lifedamage drug effect. This will kill the target in a few turns. + // this is intended to work on darts, but it is possible on any ammo + if ( AmmoTypes[ubAmmoType].ammoflag & AMMO_NEUROTOXIN ) + { + pTarget->bSoldierFlagMask |= SOLDIER_DRUGGED; - if ( AmmoTypes[ubAmmoType].dart && sHitBy > 20 ) + // Add lifedamage effects + pTarget->drugs.bFutureDrugEffect[ DRUG_TYPE_LIFEDAMAGE ] = min(pTarget->drugs.bFutureDrugEffect[ DRUG_TYPE_LIFEDAMAGE ] + 3, 127); + pTarget->drugs.bDrugEffectRate[DRUG_TYPE_LIFEDAMAGE] = 1; + pTarget->drugs.bDrugEffect[DRUG_TYPE_LIFEDAMAGE] = 0; + pTarget->drugs.bDrugSideEffectRate[DRUG_TYPE_LIFEDAMAGE] = 0; + pTarget->drugs.bDrugSideEffect[DRUG_TYPE_LIFEDAMAGE] = 127; + pTarget->drugs.bTimesDrugUsedSinceSleep[ DRUG_TYPE_LIFEDAMAGE ]++; + } + else if ( AmmoTypes[ubAmmoType].dart && sHitBy > 20 ) { if (pubSpecial) { @@ -9434,13 +9447,13 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO INT32 iImpact, iFluke, iBonus; + // Flugente: check for underbarrel weapons and use that object if necessary (think of bayonets) + OBJECTTYPE* pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); + if (fBladeAttack) { iImpact = ( EffectiveExpLevel( pSoldier ) / 2); // 0 to 4 for level - - // Flugente: check for underbarrel weapons and use that object if necessary (think of bayonets) - OBJECTTYPE* pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); - + iImpact += GetDamage(pObj); iImpact += EffectiveStrength( pSoldier, FALSE ) / 20; // 0 to 5 for strength, adjusted by damage taken @@ -9677,6 +9690,34 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO iBonus += 10; // +10% } + // Flugente: if we are using a garotte, there is a chance that we score an instakill + // our level in covert ops and wether the target is aware of us has a huge impact + if ( pObj && HasItemFlag(pObj->usItem, GAROTTE) ) + { + INT32 instakillchance = 0; + INT32 resistchance = 20; + + if ( !SoldierTo3DLocationLineOfSightTest( pSoldier, pTarget->sGridNo, pTarget->pathing.bLevel, 3, TRUE, CALC_FROM_WANTED_DIR ) ) + instakillchance += 30; + + UINT8 skilllevel = NUM_SKILL_TRAITS( pSoldier, COVERT_NT ); + instakillchance += skilllevel * gSkillTraitValues.sCoMeleeInstakillBonus; + + if ( pTarget->aiData.bAlertStatus == STATUS_YELLOW ) + resistchance += 20; + else if ( pTarget->aiData.bAlertStatus >= STATUS_RED ) + resistchance += 50; + + if ( pTarget->bCollapsed ) + resistchance = 0; + + // killchance gets lowered if garotte is in bad shape + instakillchance *= ( (*pObj)[0]->data.objectStatus / 100 ); + + if ( Random(instakillchance) >= Random(resistchance) ) + iImpact += 500; + } + // apply all bonuses iImpact = (iImpact * (100 + iBonus) + 50) / 100; // round it properly @@ -9925,6 +9966,25 @@ UINT32 CalcChanceHTH( SOLDIERTYPE * pAttacker,SOLDIERTYPE *pDefender, INT16 ubAi { iAttRating += gSkillTraitValues.bCtHModifierHtHAttack; // Make HtH attacks a little more problematic for untrained mercs + // Flugente: if we are using a garotte, alter the attack rating on wether we are used in to this weapon, and wether our target can see us + if ( HasItemFlag(usInHand, GAROTTE) ) + { + // using a garotte is pretty hard, we get a malus as default value + INT32 garottemodifier = -30; + + UINT8 skilllevel = NUM_SKILL_TRAITS( pAttacker, COVERT_NT ); + garottemodifier += skilllevel * gSkillTraitValues.sCOMeleeCTHBonus; + + if ( pDefender->bCollapsed ) + garottemodifier += 80; + + // if this guy can see us, get a big malus! + else if ( SoldierTo3DLocationLineOfSightTest( pAttacker, pDefender->sGridNo, pDefender->pathing.bLevel, 3, TRUE, CALC_FROM_WANTED_DIR ) ) + garottemodifier -= 80; + + iAttRating += garottemodifier; + } + // bare hands - bonus for Martial arts if (!pAttacker->usAttackingWeapon && HAS_SKILL_TRAIT( pAttacker, MARTIAL_ARTS_NT )) { diff --git a/Tactical/Weapons.h b/Tactical/Weapons.h index ef68fa71..7f8f79ff 100644 --- a/Tactical/Weapons.h +++ b/Tactical/Weapons.h @@ -172,6 +172,49 @@ enum AMMO_FLAME, }; +// -------- added by Flugente: various ammo flags -------- +// flags used for various ammo properties (easier than adding 32 differently named variables). DO NOT CHANGE THEM, UNLESS YOU KNOW WHAT YOU ARE DOING!!! +#define AMMO_NEUROTOXIN 0x00000001 //1 // this ammo adds the cyanide drug effect to its target, killing it in a few turns +/*#define FULL_SANDBAG 0x00000002 //2 +#define SHOVEL 0x00000004 //4 +#define CONCERTINA 0x00000008 //8 + +#define WATER_DRUM 0x00000010 //16 // water drums allow to refill canteens in the sector they are in +#define MEAT_BLOODCAT 0x00000020 //32 // retrieve this by gutting a bloodcat +#define COW_MEAT 0x00000040 //64 // retrieve this by gutting a cow +#define BELT_FED 0x00000080 //128 // item can be fed externally + +#define AMMO_BELT 0x00000100 //256 // this item can be used to feed externally +#define AMMO_BELT_VEST 0x00000200 //512 // this is a vest that can contain AMMO_BELT items in its medium slots +#define COVERT_OPS_KIT 0x00000400 //1024 // this kit can restore the covert ops disguise +#define RAG 0x00000800 //2048 // this item is a rag and can be used to remove camo + +#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 + +#define PLAYER_NET_1_LVL_1 0x00010000 //65536 +#define PLAYER_NET_2_LVL_1 0x00020000 //131072 +#define PLAYER_NET_3_LVL_1 0x00040000 //262144 +#define PLAYER_NET_4_LVL_1 0x00080000 //524288 + +#define PLAYER_NET_1_LVL_2 0x00100000 //1048576 +#define PLAYER_NET_2_LVL_2 0x00200000 //2097152 +#define PLAYER_NET_3_LVL_2 0x00400000 //4194304 +#define PLAYER_NET_4_LVL_2 0x00800000 //8388608 + +#define PLAYER_NET_1_LVL_3 0x01000000 //16777216 +#define PLAYER_NET_2_LVL_3 0x02000000 //33554432 +#define PLAYER_NET_3_LVL_3 0x04000000 //67108864 +#define PLAYER_NET_4_LVL_3 0x08000000 //134217728 + +#define PLAYER_NET_1_LVL_4 0x10000000 //268435456 +#define PLAYER_NET_2_LVL_4 0x20000000 //536870912 +#define PLAYER_NET_3_LVL_4 0x40000000 //1073741824 +#define PLAYER_NET_4_LVL_4 0x80000000 //2147483648*/ +// ---------------------------------------------------------------- + typedef struct { UINT32 uiIndex; @@ -216,10 +259,13 @@ typedef struct BOOLEAN tracerEffect; FLOAT temperatureModificator; // Flugente: modificator for weapon temperature INT16 poisonPercentage; // Flugente: modificator for weapon temperature - FLOAT dirtModificator; // Flugente: modificator for weapon temperature + FLOAT dirtModificator; // Flugente: modificator for dirt generation //zilpin: pellet spread patterns externalized in XML INT32 spreadPattern; + + // Flugente: item flag for various properties. Way easier than 32 boolean flags + UINT32 ammoflag; } AMMOTYPE; diff --git a/Tactical/XML_AmmoTypes.cpp b/Tactical/XML_AmmoTypes.cpp index d09ac6a3..a3f8fd1a 100644 --- a/Tactical/XML_AmmoTypes.cpp +++ b/Tactical/XML_AmmoTypes.cpp @@ -81,7 +81,8 @@ ammotypeStartElementHandle(void *userData, const XML_Char *name, const XML_Char strcmp(name, "monsterSpit") == 0 || strcmp(name, "temperatureModificator") == 0 || strcmp(name, "PoisonPercentage") == 0 || - strcmp(name, "dirtModificator") == 0 )) + strcmp(name, "dirtModificator") == 0 || + strcmp(name, "ammoflag") == 0 )) { pData->curElement = ELEMENT_PROPERTY; @@ -298,6 +299,11 @@ ammotypeEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->curAmmoType.dirtModificator = (FLOAT) atof(pData->szCharData); } + else if(strcmp(name, "ammoflag") == 0) + { + pData->curElement = ELEMENT; + pData->curAmmoType.ammoflag = (UINT32) atol(pData->szCharData); + } pData->maxReadDepth--; } @@ -390,39 +396,39 @@ BOOLEAN WriteAmmoTypeStats() FilePrintf(hFile,"\t\r\n"); - FilePrintf(hFile,"\t\t%d\r\n", cnt ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].fontColour ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].grayed ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].offNormal ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].onNormal ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].structureImpactReductionMultiplier ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].structureImpactReductionDivisor ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].armourImpactReductionMultiplier ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].armourImpactReductionDivisor ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].beforeArmourDamageMultiplier ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].beforeArmourDamageDivisor ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].afterArmourDamageMultiplier ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].afterArmourDamageDivisor ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].zeroMinimumDamage ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].canGoThrough ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].standardIssue ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].numberOfBullets ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].multipleBulletDamageMultiplier ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].multipleBulletDamageDivisor ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].highExplosive ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].explosionSize ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].antiTank ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].dart ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].knife ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].monsterSpit ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].acidic ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].ignoreArmour ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].lockBustingPower ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].tracerEffect ); - FilePrintf(hFile,"\t\t%4.2f\r\n", AmmoTypes[cnt].temperatureModificator ); - FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].poisonPercentage ); + FilePrintf(hFile,"\t\t%d\r\n", cnt ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].fontColour ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].grayed ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].offNormal ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].onNormal ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].structureImpactReductionMultiplier ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].structureImpactReductionDivisor ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].armourImpactReductionMultiplier ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].armourImpactReductionDivisor ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].beforeArmourDamageMultiplier ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].beforeArmourDamageDivisor ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].afterArmourDamageMultiplier ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].afterArmourDamageDivisor ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].zeroMinimumDamage ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].canGoThrough ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].standardIssue ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].numberOfBullets ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].multipleBulletDamageMultiplier ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].multipleBulletDamageDivisor ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].highExplosive ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].explosionSize ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].antiTank ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].dart ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].knife ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].monsterSpit ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].acidic ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].ignoreArmour ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].lockBustingPower ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].tracerEffect ); + FilePrintf(hFile,"\t\t%4.2f\r\n", AmmoTypes[cnt].temperatureModificator ); + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].poisonPercentage ); FilePrintf(hFile,"\t\t%4.2f\r\n", AmmoTypes[cnt].dirtModificator ); - + FilePrintf(hFile,"\t\t%d\r\n", AmmoTypes[cnt].ammoflag ); FilePrintf(hFile,"\t\r\n"); } diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index fef12729..e6e51457 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -446,7 +446,7 @@ void ReevaluateBestSightingPosition( SOLDIERTYPE * pSoldier, INT8 bInterruptDuel { for ( ubLoop = 0; ubLoop < gubBestToMakeSightingSize; ubLoop++ ) { - if ( (gubBestToMakeSighting[ ubLoop ] == NOBODY) || (bInterruptDuelPts > MercPtrs[ gubBestToMakeSighting[ ubLoop ] ]->aiData.bInterruptDuelPts ) ) + if ( pSoldier->RecognizeAsCombatant(gubBestToMakeSighting[ ubLoop ]) && (gubBestToMakeSighting[ ubLoop ] == NOBODY) || (bInterruptDuelPts > MercPtrs[ gubBestToMakeSighting[ ubLoop ] ]->aiData.bInterruptDuelPts ) ) { if ( gubBestToMakeSighting[ gubBestToMakeSightingSize - 1 ] != NOBODY ) { @@ -509,36 +509,43 @@ void HandleBestSightingPositionInRealtime( void ) if (gubBestToMakeSighting[ 1 ] == NOBODY) { // The_Bob - real time sneaking code 01/06/09 // if real time sneaking conditions are met... - // this is now in the preferences window - SANDRO + // this is now in the preferences window - SANDRO if (gGameSettings.fOptions[TOPTION_ALLOW_REAL_TIME_SNEAK] && MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam == OUR_TEAM && NobodyAlerted() ) - { - // get rid of the item under cursor (we gotta react FAST) - CancelItemPointer(); - // select (and center screen on) the merc who saw the enemy - // HEADROCK HAM 3.6: A much-requested toggle. - if (gusSelectedSoldier != (UINT16)MercPtrs[gubBestToMakeSighting[ 0 ]]->ubID && - !gGameExternalOptions.fNoAutoFocusChangeInRealtimeSneak) - SelectSoldier (MercPtrs[gubBestToMakeSighting[ 0 ]]->ubID, false, true); - // if not quiet, emit a message warning the player - if (!gGameExternalOptions.fQuietRealTimeSneak) - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_ENEMIES_SPOOTED]); + { + // get rid of the item under cursor (we gotta react FAST) + CancelItemPointer(); + // select (and center screen on) the merc who saw the enemy + // HEADROCK HAM 3.6: A much-requested toggle. + if (gusSelectedSoldier != (UINT16)MercPtrs[gubBestToMakeSighting[ 0 ]]->ubID && + !gGameExternalOptions.fNoAutoFocusChangeInRealtimeSneak) + SelectSoldier (MercPtrs[gubBestToMakeSighting[ 0 ]]->ubID, false, true); + // if not quiet, emit a message warning the player + if (!gGameExternalOptions.fQuietRealTimeSneak) + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_ENEMIES_SPOOTED]); - return; // and do nothing - } + return; // and do nothing + } + else if ( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam != OUR_TEAM && MercPtrs[gubBestToMakeSighting[ 0 ]]->aiData.bOppCnt > 0 ) + { + // otherwise, simply award the turn to the team that saw the enemy first + EnterCombatMode( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam ); + } else // otherwise, simply award the turn to the team that saw the enemy first EnterCombatMode( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam ); } else { + + // if 1st and 2nd on same team, or 1st and 3rd on same team, or there IS no 3rd, award turn to 1st - if ( ( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam == MercPtrs[gubBestToMakeSighting[ 1 ]]->bTeam ) || + if ( /*MercPtrs[gubBestToMakeSighting[ 0 ]]->aiData.bOppCnt > 0 &&*/ ( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam == MercPtrs[gubBestToMakeSighting[ 1 ]]->bTeam ) || ( (gubBestToMakeSighting[ 2 ] == NOBODY) || ( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam == MercPtrs[gubBestToMakeSighting[ 2 ]]->bTeam ) ) ) { EnterCombatMode( MercPtrs[gubBestToMakeSighting[ 0 ]]->bTeam ); } - else // give turn to 2nd best but interrupt to 1st + else //if ( MercPtrs[gubBestToMakeSighting[ 1 ]]->aiData.bOppCnt > 0 ) // give turn to 2nd best but interrupt to 1st { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Entering combat mode: turn for 2nd best, int for best" ); @@ -1766,7 +1773,7 @@ void HandleManNoLongerSeen( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOpponent, INT { // if neither side is neutral AND // if this soldier is an opponent (fights for different side) - if (pSoldier->bActive && pOpponent->bActive && !CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide)) + if (pSoldier->bActive && pOpponent->bActive && !CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) ) { RemoveOneOpponent(pSoldier); } @@ -2199,6 +2206,9 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo, return; } + // Flugente: update our sight concerning this guy, otherwise we could get way with open attacks because this does not get updated + pSoldier->RecognizeAsCombatant(pOpponent->ubID); + // if we're seeing a guy we didn't see on our last chance to look for him if (pSoldier->aiData.bOppList[pOpponent->ubID] != SEEN_CURRENTLY) { @@ -2503,7 +2513,7 @@ 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)) + if (!CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) ) { AddOneOpponent(pSoldier); @@ -3002,7 +3012,7 @@ IAN COMMENTED THIS OUT MAY 1997 - DO WE NEED THIS? if (pOpponent) { // check to see if OPPONENT considers US neutral - if ( (pOpponent->aiData.bOppList[ubTarget] == SEEN_CURRENTLY) && !pOpponent->aiData.bNeutral && !CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && (pSoldier->bSide != pOpponent->bSide) ) + if ( (pOpponent->aiData.bOppList[ubTarget] == SEEN_CURRENTLY) && !pOpponent->aiData.bNeutral && !CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && (pSoldier->bSide != pOpponent->bSide) && pOpponent->RecognizeAsCombatant(pSoldier->ubID) ) { RemoveOneOpponent(pOpponent); } @@ -3501,20 +3511,24 @@ void SaySeenQuote( SOLDIERTYPE *pSoldier, BOOLEAN fSeenCreature, BOOLEAN fVirgin } else { + // Flugente: no quotes on seeing enemy when covert + if ( (pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) == 0 ) + { #ifdef ENGLISH - if ( Random( 100 ) < 30 ) - { - pSoldier->DoMercBattleSound( BATTLE_SOUND_ENEMY ); - } - else - { - TacticalCharacterDialogue( pSoldier, QUOTE_SEE_ENEMY ); - } + if ( Random( 100 ) < 30 ) + { + pSoldier->DoMercBattleSound( BATTLE_SOUND_ENEMY ); + } + else + { + TacticalCharacterDialogue( pSoldier, QUOTE_SEE_ENEMY ); + } #else - //ddd TacticalCharacterDialogue( pSoldier, QUOTE_SEE_ENEMY ); - if(Chance(gGameExternalOptions.iChanceSayAnnoyingPhrase) ) - TacticalCharacterDialogue( pSoldier, QUOTE_SEE_ENEMY ); + //ddd TacticalCharacterDialogue( pSoldier, QUOTE_SEE_ENEMY ); + if(Chance(gGameExternalOptions.iChanceSayAnnoyingPhrase) ) + TacticalCharacterDialogue( pSoldier, QUOTE_SEE_ENEMY ); #endif + } } } } @@ -6821,7 +6835,7 @@ void DecayIndividualOpplist(SOLDIERTYPE *pSoldier) { // they are NOT visible now! (*pPersOL)++; - if (!CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide)) + if (!CONSIDERED_NEUTRAL( pOpponent, pSoldier ) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) ) { RemoveOneOpponent(pSoldier); } @@ -7084,7 +7098,7 @@ void RecalculateOppCntsDueToBecomingNeutral( SOLDIERTYPE * pSoldier ) pOpponent = MercSlots[uiLoop]; // for every active, living soldier on ANOTHER team - if (pOpponent && pOpponent->stats.bLife && !pOpponent->aiData.bNeutral && (pOpponent->bTeam != pSoldier->bTeam) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) ) + if (pOpponent && pOpponent->stats.bLife && !pOpponent->aiData.bNeutral && (pOpponent->bTeam != pSoldier->bTeam) && !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) ) { if ( pOpponent->aiData.bOppList[pSoldier->ubID] == SEEN_CURRENTLY ) { diff --git a/Tactical/soldier profile type.h b/Tactical/soldier profile type.h index f5ce42d9..6f961a26 100644 --- a/Tactical/soldier profile type.h +++ b/Tactical/soldier profile type.h @@ -157,12 +157,21 @@ typedef enum DEMOLITIONS_NT, // 17 TEACHING_NT, // 18 SCOUTING_NT, // 19 + + // Flugente: new traits have to go here, even if they are major traits, as otherwise the existing traits in profiles get mixed up + COVERT_NT, // 20 + NUM_SKILLTRAITS_NT } SkillTraitNew; -#define NUM_MAJOR_TRAITS 9 +#define NUM_MAJOR_TRAITS 10 #define NUM_MINOR_TRAITS 10 +// Flugente: I've had it with this hardcoding madness. Without this, adding or removing a new trait would crash anything related to a bubblehelp display of traits +// always check every use of these enums and every use of the skill-strings if you add a new trait +#define NEWTRAIT_MERCSKILL_EXPERTOFFSET NUM_MAJOR_TRAITS + NUM_MINOR_TRAITS +#define NEWTRAIT_MERCSKILL_OFFSET_ALL NEWTRAIT_MERCSKILL_EXPERTOFFSET + NUM_MAJOR_TRAITS + // SANDRO - new set of character traits typedef enum { diff --git a/TacticalAI/Knowledge.cpp b/TacticalAI/Knowledge.cpp index f61c541d..6315a4cc 100644 --- a/TacticalAI/Knowledge.cpp +++ b/TacticalAI/Knowledge.cpp @@ -189,6 +189,15 @@ INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN if ( CONSIDERED_NEUTRAL( pSoldier, pTemp ) || (pSoldier->bSide == pTemp->bSide)) continue; // next merc + // Flugente: chance to ignore the noise if the ceator is covert + if ( pTemp->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) + { + // green AI state: always ignore + // yellow AI state: 50% chance to ignore + if ( pSoldier->aiData.bAlertStatus == STATUS_GREEN || (pSoldier->aiData.bAlertStatus == STATUS_GREEN && Random(2) < 1 ) ) + continue; // next merc + } + pbPersOL = pSoldier->aiData.bOppList + pTemp->ubID; pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pTemp->ubID; psLastLoc = gsLastKnownOppLoc[pSoldier->ubID] + pTemp->ubID; diff --git a/Utils/Text.h b/Utils/Text.h index 2448b60c..1fd879e3 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -640,8 +640,8 @@ extern STR16 szUDBGenAmmoStatsTooltipText[ 6 ]; // Flugente Overheating: 3 extern STR16 szUDBGenAmmoStatsExplanationsTooltipText[ 6 ]; // Flugente Overheating: 3->4 poison: 4->5 dirt: 5->6 extern STR16 szUDBGenExplosiveStatsTooltipText[ 22 ]; extern STR16 szUDBGenExplosiveStatsExplanationsTooltipText[ 22 ]; -extern STR16 szUDBGenSecondaryStatsTooltipText[ 31 ]; // Flugente Food System: 26 -> 28 external feeding: 28->30 JMich_SkillsModifiers: 31 for Defusal kit -extern STR16 szUDBGenSecondaryStatsExplanationsTooltipText[ 31 ]; // Flugente Food System: 26 -> 28 external feeding: 28->30 JMich_SkillsModifiers: 31 for Defusal kit +extern STR16 szUDBGenSecondaryStatsTooltipText[ 32 ]; // Flugente Food System: 26 -> 28 external feeding: 28->30 JMich_SkillsModifiers: 31 for Defusal kit - covert item: 31->32 +extern STR16 szUDBGenSecondaryStatsExplanationsTooltipText[ 32 ]; // Flugente Food System: 26 -> 28 external feeding: 28->30 JMich_SkillsModifiers: 31 for Defusal kit - covert item: 31->32 extern STR16 szUDBAdvStatsTooltipText[ 58 ]; // Flugente Overheating Weapons: 48->56 poison: 56->57 dirt: 57->58 extern STR16 szUDBAdvStatsExplanationsTooltipText[ 58 ]; // Flugente Overheating Weapons: 48->56 poison: 56->57 dirt: 57->58 extern STR16 szUDBAdvStatsExplanationsTooltipTextForWeapons[ 52 ]; // Flugente Overheating Weapons: 48->52 @@ -819,6 +819,8 @@ enum MERC_VITAL_STATS_WITH_POISON_AND_FOOD_POPUPTEXT, FUNCTION_SELECTION_STR, + + CORPSE_SELECTION_STR, TEXT_NUM_TACTICAL_STR }; diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index bb09952c..69bc5287 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -7084,6 +7084,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7119,6 +7120,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index db9af0c5..03414990 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -7075,6 +7075,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7110,6 +7111,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 3ff9ec5d..d924d5c5 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -2104,37 +2104,39 @@ STR16 gzMercSkillText[] = STR16 gzMercSkillTextNew[] = { // Major traits - L"No Skill", + L"No Skill", // 0 L"Auto Weapons", L"Heavy Weapons", L"Marksman", L"Hunter", - L"Gunslinger", + L"Gunslinger", // 5 L"Hand to Hand", L"Deputy", L"Technician", L"Paramedic", + L"Covert Ops", // 10 // Minor traits L"Ambidextrous", L"Melee", L"Throwing", L"Night Ops", - L"Stealthy", + L"Stealthy", // 15 L"Athletics", L"Bodybuilding", L"Demolitions", L"Teaching", - L"Scouting", + L"Scouting", // 20 // second names for major skills L"Machinegunner", L"Bombardier", L"Sniper", L"Ranger", - L"Gunfighter", + L"Gunfighter", // 25 L"Martial Arts", L"Squadleader", L"Engineer", L"Doctor", + L"Spy", // 30 L"More...", }; ////////////////////////////////////////////////////////// @@ -2936,6 +2938,9 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = // added by Flugente: selection of a function to call in tactical L"1 - Fill Canteens 2 - Clean Weapons 3,4 - Nothing", + + // added by Flugente: decide what to do with the corpses + L"1 - Decapitate 2 - Gut 3 - Take Clothes 4 - Take Body", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. @@ -6300,7 +6305,9 @@ STR16 MissingIMPSkillsDescriptions[] = // Demolitions L"Demolitions: Sowing grenades like seeds, planting bombs, watching the limbs flying.. This is what you live for! ± ", // Scouting - L"Scouting: Nothing can escape your notice! ± ", + L"Scouting: Nothing can escape your notice! ± ", + // Covert ops + L"Covert Operations: You make 007 look like an amateur! ± ", }; STR16 NewInvMessage[] = @@ -7067,6 +7074,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7102,6 +7110,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index bfe884c1..951ae453 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -7050,6 +7050,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7085,6 +7086,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index ee5d65bc..33e7f9dd 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -6894,6 +6894,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|M|u|n|i|t|i|o|n|s|g|u|r|t", L"|M|u|n|i|t|i|o|n|s|w|e|s|t|e", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -6929,6 +6930,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nEin externer Munitionsgurt für MGs.", L"\n \nIn diese Weste passen auch Munitionsgurte.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 6785f901..09a084bb 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -7062,6 +7062,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7097,6 +7098,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_Ja25ChineseText.cpp b/Utils/_Ja25ChineseText.cpp index 668e62a5..02bd0128 100644 --- a/Utils/_Ja25ChineseText.cpp +++ b/Utils/_Ja25ChineseText.cpp @@ -58,6 +58,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"班副",// L"Deputy", L"技师",// L"Technician", L"救护员",// L"Paramedic", + L"Covert Ops", // TODO.Translate L"无",// L"None", L"I.M.P 主要特殊技能",// L"I.M.P. Major Traits", @@ -71,6 +72,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"班长",// L"Squadleader", L"工兵",// L"Engineer", L"军医",// L"Doctor", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -241,6 +243,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L"(这种效果最多叠加 %d 次)",// L" (max %d these bonuses per sector)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"无奖励",// L"No bonuses", diff --git a/Utils/_Ja25DutchText.cpp b/Utils/_Ja25DutchText.cpp index 7b1ed558..553f1267 100644 --- a/Utils/_Ja25DutchText.cpp +++ b/Utils/_Ja25DutchText.cpp @@ -58,6 +58,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Deputy", L"Technician", L"Paramedic", + L"Covert Ops", // TODO.Translate L"None", L"I.M.P. Major Traits", @@ -71,6 +72,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Squadleader", L"Engineer", L"Doctor", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -240,6 +242,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (max %d these bonuses per sector)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25DutchText.h b/Utils/_Ja25DutchText.h index ad6e5688..4b8243e9 100644 --- a/Utils/_Ja25DutchText.h +++ b/Utils/_Ja25DutchText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25EnglishText.cpp b/Utils/_Ja25EnglishText.cpp index cea380c5..d800a056 100644 --- a/Utils/_Ja25EnglishText.cpp +++ b/Utils/_Ja25EnglishText.cpp @@ -58,6 +58,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Deputy", L"Technician", L"Paramedic", + L"Covert Ops", L"None", L"I.M.P. Major Traits", @@ -71,6 +72,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Squadleader", L"Engineer", L"Doctor", + L"Spy", }; //added another set of skill texts for new minor traits @@ -238,7 +240,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L"+%d%s bandaging speed\n", L"+%d%s natural regeneration speed for all soldiers in the same sector", // MINTY - Changed "of" to "for" L" (max %d of these bonuses per sector stack)", - +}; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", }; STR16 gzIMPMajorTraitsHelpTextsNone[]= { diff --git a/Utils/_Ja25EnglishText.h b/Utils/_Ja25EnglishText.h index fd29cc75..43a450d2 100644 --- a/Utils/_Ja25EnglishText.h +++ b/Utils/_Ja25EnglishText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25FrenchText.cpp b/Utils/_Ja25FrenchText.cpp index 5e6cbf01..021a5eb4 100644 --- a/Utils/_Ja25FrenchText.cpp +++ b/Utils/_Ja25FrenchText.cpp @@ -58,6 +58,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Manager", L"Technicien", L"Médecin", + L"Covert Ops", // TODO.Translate L"Personne", L"I.M.P. Traits pincipaux", @@ -71,6 +72,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Commandant", L"Ingénieur", L"Chirurgien", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -241,6 +243,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (un maximum de %d bonus par secteur)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"Pas de bonus", diff --git a/Utils/_Ja25FrenchText.h b/Utils/_Ja25FrenchText.h index ad6e5688..4b8243e9 100644 --- a/Utils/_Ja25FrenchText.h +++ b/Utils/_Ja25FrenchText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25GermanText.cpp b/Utils/_Ja25GermanText.cpp index 275d56b0..2881af80 100644 --- a/Utils/_Ja25GermanText.cpp +++ b/Utils/_Ja25GermanText.cpp @@ -54,6 +54,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Gruppenführer", //GrpFhr und ZgFhr sind scheiße, aber mir fällt ohne Dienstgrade nüscht ein L"Mechaniker", //Option: Techniker L"Sanitäter", //Option: Rettungsassistent + L"Covert Ops", // TODO.Translate L"Nichts", L"B.S.E. Hauptfertigkeiten", @@ -68,6 +69,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Zugführer", L"Ingenieur", L"Arzt", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -239,6 +241,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (maximal %d Instanzen dieses Bonus pro Sektor)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"Keine Boni", diff --git a/Utils/_Ja25GermanText.h b/Utils/_Ja25GermanText.h index ba76eaed..b7e83d85 100644 --- a/Utils/_Ja25GermanText.h +++ b/Utils/_Ja25GermanText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25ItalianText.cpp b/Utils/_Ja25ItalianText.cpp index 926f83b3..56b6d936 100644 --- a/Utils/_Ja25ItalianText.cpp +++ b/Utils/_Ja25ItalianText.cpp @@ -56,6 +56,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Deputy", L"Technician", L"Paramedic", + L"Covert Ops", // TODO.Translate L"None", L"I.M.P. Major Traits", @@ -69,6 +70,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Squadleader", L"Engineer", L"Doctor", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -239,6 +241,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (max %d these bonuses per sector)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25ItalianText.h b/Utils/_Ja25ItalianText.h index ad6e5688..4b8243e9 100644 --- a/Utils/_Ja25ItalianText.h +++ b/Utils/_Ja25ItalianText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25PolishText.cpp b/Utils/_Ja25PolishText.cpp index b9d9e017..1c8ec8d0 100644 --- a/Utils/_Ja25PolishText.cpp +++ b/Utils/_Ja25PolishText.cpp @@ -58,6 +58,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Zastępca szeryfa", L"Technik", L"Paramedyk", + L"Covert Ops", // TODO.Translate L"None", L"Główne cechy I.M.P", @@ -71,6 +72,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Dowódca drużyny", L"Inżynier", L"Doktor", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -241,6 +243,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (max %d of these bonuses per sector stack)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25PolishText.h b/Utils/_Ja25PolishText.h index ad6e5688..4b8243e9 100644 --- a/Utils/_Ja25PolishText.h +++ b/Utils/_Ja25PolishText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25RussianText.cpp b/Utils/_Ja25RussianText.cpp index 156af531..e2d1c431 100644 --- a/Utils/_Ja25RussianText.cpp +++ b/Utils/_Ja25RussianText.cpp @@ -57,6 +57,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Старшина", //Deputy L"Механик-электронщик", //Technician L"Санитар", //Paramedic + L"Covert Ops", // TODO.Translate L"Нет", L"I.M.P.: Основные навыки", //I.M.P. Major Traits @@ -70,6 +71,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Командир", //Squadleader L"Инженер", //Engineer L"Доктор", //Doctor + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -240,6 +242,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (максимум %d бонуса на находящихся в секторе)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"Нет преимуществ", diff --git a/Utils/_Ja25RussianText.h b/Utils/_Ja25RussianText.h index ad6e5688..4b8243e9 100644 --- a/Utils/_Ja25RussianText.h +++ b/Utils/_Ja25RussianText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25TaiwaneseText.cpp b/Utils/_Ja25TaiwaneseText.cpp index b82c63c7..c57283d5 100644 --- a/Utils/_Ja25TaiwaneseText.cpp +++ b/Utils/_Ja25TaiwaneseText.cpp @@ -59,6 +59,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Deputy", L"Technician", L"Paramedic", + L"Covert Ops", // TODO.Translate L"None", L"I.M.P. Major Traits", @@ -72,6 +73,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"Squadleader", L"Engineer", L"Doctor", + L"Spy", // TODO.Translate }; //added another set of skill texts for new minor traits @@ -242,6 +244,16 @@ STR16 gzIMPMajorTraitsHelpTextsDoctor[]= L" (max %d these bonuses per sector)", }; +STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate +{ + L"Can disguise as a civilian or soldier to slip behind enemy lines.\n", + L"Will be detected if performing suspicious actions, having\nsuspicious gear or being near fresh corpses.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to the enemy.\n", + L"Will automatically be detected if disguised as a soldier and\ncloser than %d tiles to a fresh corpse.\n", + L"+%d%s CTH with covert melee weapons\n", + L"+%d%s chance of instakill with covert melee weapons\n", + L"Disguise AP cost lowered by %d%s.\n", +}; STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25TaiwaneseText.h b/Utils/_Ja25TaiwaneseText.h index ad6e5688..4b8243e9 100644 --- a/Utils/_Ja25TaiwaneseText.h +++ b/Utils/_Ja25TaiwaneseText.h @@ -17,6 +17,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsMartialArts[]; extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; +extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 693d3dbe..338fec59 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -7074,6 +7074,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7109,6 +7110,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 4c6b07d2..77c60628 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -7042,6 +7042,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7077,6 +7078,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 294b9de5..24e1e3a9 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -7079,6 +7079,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|m|m|o |B|e|l|t", L"|A|m|m|o |V|e|s|t", L"|D|e|f|u|s|a|l |K|i|t", // TODO.Translate + L"|C|o|v|e|r|t |I|t|e|m", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -7114,6 +7115,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nWith this ammo belt you can\nfeed someone else's MG.", L"\n \nYou can feed an MG with ammo\nbelts stored in this vest.", L"\n \nThis item improves your trap disarm chance by ", // TODO.Translate + L"\n \nThis item and everything attached/inside\nit is hidden from curious eyes.", }; STR16 szUDBAdvStatsTooltipText[]=