diff --git a/GameSettings.cpp b/GameSettings.cpp index 4a82e7ec..698e0add 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2349,6 +2349,18 @@ void LoadSkillTraitsExternalSettings() 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); gSkillTraitValues.fCOElitesDetectNextTile = iniReader.ReadBoolean("Covert Ops","COVERT_ELITES_DETECT_NEXTTILE", TRUE); + + // Flugente: RADIO OPERATOR + gSkillTraitValues.bVOArtillerySectorFrequency = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_ARTILLERY_SECTOR_FREQUENCY", 120, 20, 1440); + gSkillTraitValues.usVOMortarCountDivisor = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_COUNT_DIVISOR", 6, 5, 20); + gSkillTraitValues.usVOMortarShellDivisor = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_SHELL_DIVISOR", 30, 2, 100); + gSkillTraitValues.usVOMortarPointsAdmin = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_POINTS_ADMIN", 10, 0, 100); + gSkillTraitValues.usVOMortarPointsTroop = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_POINTS_TROOP", 15, 0, 100); + gSkillTraitValues.usVOMortarPointsElite = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_POINTS_ELITE", 25, 0, 100); + gSkillTraitValues.usVOMortarRadius = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_RADIUS", 10, 5, 100); + gSkillTraitValues.usVOMortarSignalShellRadius = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_MORTAR_SIGNAL_SHELL_RADIUS", 2, 2, 100); + gSkillTraitValues.sVOScanAssignmentBaseRange = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_ASSIGNMENT_SCAN_BASE_RANGE", 5, 0, 20); + gSkillTraitValues.sVOListeningHearingBonus = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_LISTENING_HEARING_BONUS", 20, 0, 100); } //DBrot: Grids void LoadModSettings(){ @@ -2724,6 +2736,11 @@ void LoadItemSettings() gItemSettings.fDamageHealthMoveModifierExplosive = iniReader.ReadFloat ("Explosives Settings","DAMAGE_HEALTH_MOVE_EXPLOSIVE_MODIFIER", 0.1f, 0.0f, 1.0f); gItemSettings.fDamageBreathMoveModifierExplosive = iniReader.ReadFloat ("Explosives Settings","DAMAGE_BREATH_MOVE_EXPLOSIVE_MODIFIER", 0.1f, 0.0f, 1.0f); + // ------------ ENERGY MODIFIERS ----------------- + gItemSettings.energy_cost_radioset_activate = iniReader.ReadFloat ("Energy Settings","ENERGY_COST_RADIOSET_ACTIVATE", 100.00f, 1.00f, 1000.0f); + gItemSettings.energy_cost_radioset_jam = iniReader.ReadFloat ("Energy Settings","ENERGY_COST_RADIOSET_JAM", 10.00f, 1.00f, 1000.0f); + gItemSettings.energy_cost_radioset_scan = iniReader.ReadFloat ("Energy Settings","ENERGY_COST_RADIOSET_SCAN", 3.00f, 1.00f, 1000.0f); + gItemSettings.energy_cost_radioset_scan_assignment = iniReader.ReadFloat ("Energy Settings","ENERGY_COST_RADIOSET_SCAN_ASSIGNMENT", 3000.00f, 1.00f, 30000.0f); } INT16 DynamicAdjustAPConstants(INT16 iniReadValue, INT16 iniDefaultValue, BOOLEAN reverse) diff --git a/GameSettings.h b/GameSettings.h index 68546fbc..5e5bb7c6 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1575,6 +1575,18 @@ typedef struct INT16 sCOCloseDetectionRangeSoldierCorpse; BOOLEAN fCOElitesDetectNextTile; + // RADIO OPERATOR + INT16 bVOArtillerySectorFrequency; // ability to call an a artillery strike from a sector every '_' minutes. Only applies if ordered from enemy/militia + UINT8 usVOMortarCountDivisor; + UINT8 usVOMortarShellDivisor; + UINT8 usVOMortarPointsAdmin; + UINT8 usVOMortarPointsTroop; + UINT8 usVOMortarPointsElite; + UINT8 usVOMortarRadius; + UINT8 usVOMortarSignalShellRadius; + INT8 sVOScanAssignmentBaseRange; + INT8 sVOListeningHearingBonus; + } SKILL_TRAIT_VALUES; // HEADROCK HAM 4: Constants used as coefficients by the various parts of the new CTH system. @@ -1949,6 +1961,13 @@ typedef struct FLOAT fDamageHealthMoveModifierExplosive; // applies when character moves through gas cloud without a gas mask FLOAT fDamageBreathMoveModifierExplosive; // applies when character moves through gas cloud without a gas mask + // ------------ ENERGY MODIFIERS ----------------- + // Flugente: energy consumption settings (energy-consumption of attached non-vanilla batteries) + FLOAT energy_cost_radioset_activate; + FLOAT energy_cost_radioset_jam; + FLOAT energy_cost_radioset_scan; + FLOAT energy_cost_radioset_scan_assignment; + }ITEM_SETTINGS; extern MOD_SETTINGS gModSettings; diff --git a/GameVersion.h b/GameVersion.h index 7775384c..327b63f3 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -21,6 +21,7 @@ extern CHAR16 zTrackingNumber[16]; // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file +#define TRAIT_RADIO_OPERATOR 147 // Flugente: new trait radio operator #define BACKGROUNDS_FIX_UINT8 146 // Flugente: had to change usBackground-vars from UINT8 to UINT16 #define BACKGROUNDS 145 // Flugente: background feature #define NO_VEHICLE_SAVE 144 // Flugente: vehicle data isn't saved anymore, as it has long been externalised @@ -65,7 +66,7 @@ extern CHAR16 zTrackingNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system -#define SAVE_GAME_VERSION BACKGROUNDS_FIX_UINT8 +#define SAVE_GAME_VERSION TRAIT_RADIO_OPERATOR //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/Laptop/IMP Confirm.cpp b/Laptop/IMP Confirm.cpp index fb9a4a86..e7c76bd5 100644 --- a/Laptop/IMP Confirm.cpp +++ b/Laptop/IMP Confirm.cpp @@ -778,7 +778,11 @@ void GiveItemsToPC( UINT8 ubProfileId ) if (PROFILE_HAS_SKILL_TRAIT(ubProfileId, SCOUTING_NT)) { GiveIMPRandomItems(pProfile,IMP_SCOUTING); - } + } + if (PROFILE_HAS_SKILL_TRAIT(ubProfileId, RADIO_OPERATOR_NT)) + { + GiveIMPRandomItems(pProfile,IMP_RADIO_OPERATOR); + } } else { diff --git a/Laptop/IMP Minor Trait.cpp b/Laptop/IMP Minor Trait.cpp index e2c0e330..62e2774d 100644 --- a/Laptop/IMP Minor Trait.cpp +++ b/Laptop/IMP Minor Trait.cpp @@ -45,7 +45,7 @@ #define IMP_MINOR_TRAIT__SPACE_BTN_BUTTONS ( 38 + 5 ) -#define IMP_MINOR_TRAIT__TRAITS_TO_START_RIGHT_COL 4 +#define IMP_MINOR_TRAIT__TRAITS_TO_START_RIGHT_COL 5 #define IMP_MINOR_TRAIT__NONE_BTN_LOC_X ( LAPTOP_SCREEN_UL_X + 136 ) #define IMP_MINOR_TRAIT__NONE_BTN_LOC_Y ( LAPTOP_SCREEN_WEB_UL_Y + 306 ) @@ -78,7 +78,7 @@ INT32 giIMPMinorTraitAnswerButtonImage[ IMP_SKILL_TRAITS_NEW_NUMBER_MINOR_SKILLS INT32 giIMPMinorlTraitFinsihButton; INT32 giIMPMinorlTraitFinsihButtonImage; -INT8 gbLastSelectedMinorTrait[ 10 ]; +INT8 gbLastSelectedMinorTrait[ NUM_MINOR_TRAITS ]; //image handle UINT32 guiIMT_GreyGoldBox; @@ -191,7 +191,7 @@ void EnterIMPMinorTrait( void ) } //reset last selected trait - memset( gbLastSelectedMinorTrait, -1, 10 ); + memset( gbLastSelectedMinorTrait, -1, NUM_MINOR_TRAITS ); } @@ -512,14 +512,17 @@ void BtnIMPMinorTraitFinishCallback(GUI_BUTTON *btn,INT32 reason) { iCurrentImpPage = IMP_MAIN_PAGE; - if( CameBackToMinorTraitPageButNotFinished() ) + fButtonPendingFlag = TRUE; + + /*if( CameBackToMinorTraitPageButNotFinished() ) { + } else { //We are finished on this page iCurrentProfileMode = IMP__ATTRIBUTES; - } + }*/ } } } @@ -652,6 +655,10 @@ void AddSelectedMinorTraitsToSkillsList() case IMP_SKILL_TRAITS_NEW_SCOUTING: AddSkillToSkillList( SCOUTING_NT ); break; + + case IMP_SKILL_TRAITS_NEW_RADIO_OPERATOR: + AddSkillToSkillList( RADIO_OPERATOR_NT ); + break; default: break; @@ -668,7 +675,7 @@ void HandleLastSelectedMinorTrait( INT8 bNewTrait ) if (bNewTrait == -1) { // we only want to delete the last selected trait from list - for( iCnt=9; iCnt>=0; iCnt-- ) + for( iCnt=NUM_MINOR_TRAITS-1; iCnt>=0; iCnt-- ) { if ( gbLastSelectedMinorTrait[ iCnt ] > 0 ) { @@ -680,7 +687,7 @@ void HandleLastSelectedMinorTrait( INT8 bNewTrait ) } else { - for( iCnt=0; iCnt<9; iCnt++ ) + for( iCnt=0; iCnt=0; iCnt-- ) + for( iCnt=NUM_MINOR_TRAITS-1; iCnt>=0; iCnt-- ) { if ( gbLastSelectedMinorTrait[ iCnt ] > 0 ) { @@ -1432,6 +1439,23 @@ void AssignMinorTraitHelpText( UINT8 ubNumber ) } break; } + case IMP_SKILL_TRAITS_NEW_RADIO_OPERATOR: + { + swprintf( apStr, L"" ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[0]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[1]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[2]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[3]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[4]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[5]); + wcscat( apStr, atStr ); + break; + } case IMP_SKILL_TRAITS_NEW_MINOR_NONE: { swprintf( apStr, gzIMPMinorTraitsHelpTextsNone[0] ); diff --git a/Laptop/IMP Minor Trait.h b/Laptop/IMP Minor Trait.h index f99267fc..b2c64e3b 100644 --- a/Laptop/IMP Minor Trait.h +++ b/Laptop/IMP Minor Trait.h @@ -21,6 +21,7 @@ enum IMP_SKILL_TRAITS_NEW_DEMOLITIONS, IMP_SKILL_TRAITS_NEW_TEACHING, IMP_SKILL_TRAITS_NEW_SCOUTING, + IMP_SKILL_TRAITS_NEW_RADIO_OPERATOR, // added by Flugente ////// IMP_SKILL_TRAITS_NEW_MINOR_NONE, IMP_SKILL_TRAITS_NEW_NUMBER_MINOR_SKILLS, diff --git a/Laptop/email.cpp b/Laptop/email.cpp index 492aedc6..cb0f900b 100644 --- a/Laptop/email.cpp +++ b/Laptop/email.cpp @@ -4699,6 +4699,12 @@ void HandleIMPCharProfileResultsMessage( void) wcscpy(pString, MissingIMPSkillsDescriptions[11]); AddEmailRecordToList( pString ); } + // Radio Operator + if ( ProfileHasSkillTrait( iCurrentIMPSlot, RADIO_OPERATOR_NT ) > 0 ) + { + wcscpy(pString, MissingIMPSkillsDescriptions[15]); + AddEmailRecordToList( pString ); + } } else { diff --git a/Laptop/personnel.cpp b/Laptop/personnel.cpp index dc3b2612..3e43c3a8 100644 --- a/Laptop/personnel.cpp +++ b/Laptop/personnel.cpp @@ -8084,7 +8084,27 @@ void AssignPersonnelSkillTraitHelpText( UINT8 ubTraitNumber, BOOLEAN fExpertLeve swprintf( atStr, gzIMPMajorTraitsHelpTextsCovertOps[6], apreduction, L"%"); wcscat( apStr, atStr ); break; - } + } + + // Flugente: Radio Operator + case RADIO_OPERATOR_NT: + { + swprintf( apStr, L"" ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[0]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[1]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[2]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[3]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[4]); + wcscat( apStr, atStr ); + swprintf( atStr, gzIMPMajorTraitsHelpTextsRadioOperator[5]); + wcscat( apStr, atStr ); + break; + } + case NO_SKILLTRAIT_NT: { swprintf( apStr, L"" ); diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index b2f3fcbc..8acfde6a 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -2054,8 +2054,148 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) numBytesRead = ReadFieldByField(hFile, &this->bExtraWisdom, sizeof(bExtraWisdom), sizeof(INT16), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->bExtraExpLevel, sizeof(bExtraExpLevel), sizeof(INT8), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->bSoldierFlagMask, sizeof(bSoldierFlagMask), sizeof(INT32), numBytesRead); - + if ( guiCurrentSaveGameVersion >= FOOD_CHANGES ) + { + numBytesRead = ReadFieldByField(hFile, &this->bFoodLevel, sizeof(bFoodLevel), sizeof(INT32), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->bDrinkLevel, sizeof(bDrinkLevel), sizeof(INT32), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usStarveDamageHealth, sizeof(usStarveDamageHealth), sizeof(UINT8), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usStarveDamageHealth, sizeof(usStarveDamageStrength), sizeof(UINT8), numBytesRead); + } + else + { + this->bFoodLevel = 0; + this->bDrinkLevel = 0; + this->usStarveDamageHealth = 0; + this->usStarveDamageHealth = 0; + + for(int i = 0; i < sizeof(bFoodLevel); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(bDrinkLevel); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(usStarveDamageHealth); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(usStarveDamageStrength); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + } + + if ( guiCurrentSaveGameVersion >= MULTITURN_ACTIONS ) + { + numBytesRead = ReadFieldByField(hFile, &this->bOverTurnAPS, sizeof(bOverTurnAPS), sizeof(INT16), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->sMTActionGridNo, sizeof(sMTActionGridNo), sizeof(INT32), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usMultiTurnAction, sizeof(usMultiTurnAction), sizeof(UINT8), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->bAIIndex, sizeof(bAIIndex), sizeof(INT16), numBytesRead); + } + else + { + this->bOverTurnAPS = 0; + this->sMTActionGridNo = 0; + this->usMultiTurnAction = 0; + this->bAIIndex = 0; + + for(int i = 0; i < sizeof(bOverTurnAPS); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(sMTActionGridNo); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(usMultiTurnAction); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(bAIIndex); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + } + + if ( guiCurrentSaveGameVersion >= SOLDIER_PROFILES ) + { + numBytesRead = ReadFieldByField(hFile, &this->usSoldierProfile, sizeof(usSoldierProfile), sizeof(UINT16), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usItemMoveSectorID, sizeof(usItemMoveSectorID), sizeof(UINT8), numBytesRead); + } + else + { + this->usSoldierProfile = 0; + this->usItemMoveSectorID = 0; + + for(int i = 0; i < sizeof(usSoldierProfile); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(usItemMoveSectorID); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + } + + if ( guiCurrentSaveGameVersion >= TRAIT_RADIO_OPERATOR ) + { + numBytesRead = ReadFieldByField(hFile, &this->usAISkillUse, sizeof(usAISkillUse), sizeof(UINT8), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usSkillCounter, sizeof(usSkillCounter), sizeof(UINT16), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usSkillCooldown, sizeof(usSkillCooldown), sizeof(UINT32), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + } + else + { + this->usAISkillUse = 0; + + for(UINT8 i = 0; i < SOLDIER_COUNTER_MAX; ++i) + this->usSkillCounter[i] = 0; + + for(UINT8 i = 0; i < SOLDIER_COOLDOWN_MAX; ++i) + this->usSkillCooldown[i] = 0; + + // Flugente: okay, this part will be a bit weird, so bear with me + // ubFiller had length 7 before this revision - we still need to read those. We have to do that, otherwise the next read functions will read the filler + UINT8 blarg[7]; + numBytesRead = ReadFieldByField(hFile, &blarg, 7, sizeof(UINT8), numBytesRead); + + // usAISkillUse forms one block with usSoldierProfile and usItemMoveSectorID and thus does not need additional padding + buffer += 3; + + for(int i = 0; i < sizeof(usAISkillUse); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(usSkillCounter)-6; ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(usSkillCooldown); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + for(int i = 0; i < sizeof(ubFiller); ++i) + buffer++; + while((buffer%4) > 0) + buffer++; + + // we now have to substract the 7 bytes we read from the old ubFiller, but have to account for usAISkillUse + buffer -= 6; + } + + /*if ( guiCurrentSaveGameVersion >= FOOD_CHANGES ) { numBytesRead = ReadFieldByField(hFile, &this->bFoodLevel, sizeof(bFoodLevel), sizeof(INT32), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->bDrinkLevel, sizeof(bDrinkLevel), sizeof(INT32), numBytesRead); @@ -2073,8 +2213,31 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) { numBytesRead = ReadFieldByField(hFile, &this->usSoldierProfile, sizeof(usSoldierProfile), sizeof(UINT16), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->usItemMoveSectorID, sizeof(usItemMoveSectorID), sizeof(UINT8), numBytesRead); - - numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + + if ( guiCurrentSaveGameVersion >= SOLDIER_PROFILES ) + { + numBytesRead = ReadFieldByField(hFile, &this->usSkillCounter, sizeof(usSkillCounter), sizeof(UINT16), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usSkillCooldown, sizeof(usSkillCooldown), sizeof(UINT32), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usAISkillUse, sizeof(usAISkillUse), sizeof(UINT8), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + } + else + { + for(UINT8 i = 0; i < SOLDIER_COUNTER_MAX; ++i) + this->usSkillCounter[i] = 0; + + for(UINT8 i = 0; i < SOLDIER_COOLDOWN_MAX; ++i) + this->usSkillCooldown[i] = 0; + + this->usAISkillUse = 0; + + // as we added new variables, fillersize was reduced, here we account for that. We have to also read the existing fillers that now do not exist anymore + const UINT8 tmp = sizeof(usSkillCounter) + sizeof(usSkillCooldown) + sizeof(usAISkillUse); + UINT8 blarg[tmp]; + numBytesRead = ReadFieldByField(hFile, &blarg, tmp, sizeof(UINT8), numBytesRead); + + numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + } } else { @@ -2164,7 +2327,7 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) buffer++; while((buffer%4) > 0) buffer++; - } + }*/ #ifdef JA2UB numBytesRead = ReadFieldByField(hFile, &this->fIgnoreGetupFromCollapseCheck, sizeof(fIgnoreGetupFromCollapseCheck), sizeof(BOOLEAN), numBytesRead); diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index 5a767361..306069fa 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -64,6 +64,7 @@ #include "Morale.h" #include "Food.h" #include "Tactical Save.h" // added by Flugente + #include "Campaign Types.h" // added by Flugente #endif #include #include @@ -91,6 +92,9 @@ class SOLDIERTYPE; #include "ub_config.h" #endif +// Flugente: external sector data +extern SECTOR_EXT_DATA SectorExternalData[256][4]; + // various reason an assignment can be aborted before completion enum{ NO_MORE_MED_KITS = 40, @@ -427,6 +431,12 @@ void HandlePrison( INT16 sMapX, INT16 sMapY, INT8 bZ ); // Flugente: assigned mercs can move equipemnt in city sectors void HandleEquipmentMove( INT16 sMapX, INT16 sMapY, INT8 bZ ); +// Flugente: handle radio scanning assignments +void HandleRadioScanInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ); + +// reset scan flags in all sectors +void ClearSectorScanResults(); + // is the character between secotrs in mvt BOOLEAN CharacterIsBetweenSectors( SOLDIERTYPE *pSoldier ); @@ -2249,6 +2259,9 @@ void UpdateAssignments() // HEADROCK HAM 3.6: See what effect, if any, our Facility Staffers have on global variables. UpdateGlobalVariablesFromFacilities(); + // reset scan flags in all sectors + ClearSectorScanResults(); + // run through sectors and handle each type in sector for(sX = 0 ; sX < MAP_WORLD_X; sX++ ) { @@ -2272,6 +2285,9 @@ void UpdateAssignments() // handle any training HandleTrainingInSector( sX, sY, bZ ); + // handle training of character in sector + HandleRadioScanInSector( sX, sY, bZ ); + // handle processing of prisoners HandlePrisonerProcessingInSector( sX, sY, bZ ); @@ -4872,6 +4888,152 @@ void HandleTrainingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) } +// handle radio scanning assignments +void HandleRadioScanInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) +{ + SOLDIERTYPE *pSoldier = NULL; + UINT32 uiCnt=0; + UINT8 numberofradiooperators = 0; + + // no underground scanning + if (bZ != 0) + return; + + // if sector not under our control, has enemies in it, or is currently in combat mode + if (!SectorOursAndPeaceful( sMapX, sMapY, bZ )) + return; + + // we will count the number of radio operators in this sector that have scanned successfully this hour. The higher this number, the higher the chance to detect enemy patrols! + // search team for radio operators in this sector that performed this assignemnt successfully + for ( uiCnt = 0, pSoldier = MercPtrs[ uiCnt ]; uiCnt <= gTacticalStatus.Team[ MercPtrs[0]->bTeam ].bLastID; uiCnt++, pSoldier++) + { + if( pSoldier->bActive && ( pSoldier->sSectorX == sMapX ) && ( pSoldier->sSectorY == sMapY ) && ( pSoldier->bSectorZ == bZ) ) + { + if( ( pSoldier->bAssignment == RADIO_SCAN ) && ( EnoughTimeOnAssignment( pSoldier ) ) && ( pSoldier->flags.fMercAsleep == FALSE ) ) + { + ++numberofradiooperators; + + // use up radio energy + pSoldier->DepleteActiveRadioSetEnergy(FALSE, TRUE); + } + } + } + + if ( !numberofradiooperators ) + return; + + INT8 range = gSkillTraitValues.sVOScanAssignmentBaseRange; + + UINT8 ubSectorId = SECTOR(sMapX, sMapY); + if ( ubSectorId >= 0 && ubSectorId < 256 ) + range += SectorExternalData[ubSectorId][0].sRadioScanModifier; + + if ( range < 1 ) + return; + + UINT8 normalgroupsize = 0; + switch( gGameOptions.ubDifficultyLevel ) + { + case DIF_LEVEL_EASY: + normalgroupsize = gGameExternalOptions.ubMinEnemyGroupSizeNovice; + break; + case DIF_LEVEL_MEDIUM: + normalgroupsize = gGameExternalOptions.ubMinEnemyGroupSizeExperienced; + break; + case DIF_LEVEL_HARD: + normalgroupsize = gGameExternalOptions.ubMinEnemyGroupSizeExpert; + break; + case DIF_LEVEL_INSANE: + default: + normalgroupsize = gGameExternalOptions.ubMinEnemyGroupSizeInsane; + break; + } + + normalgroupsize *= 2; + + FLOAT detect_basechance = 1.0f - pow(0.5f, numberofradiooperators); + FLOAT detect_rangefactor = .0f; + FLOAT detect_sizefactor = .0f; + FLOAT detect_chance = .0f; + FLOAT scandirectionmalus = .2f; + FLOAT scanexactnumbermalus = .3f; + UINT8 patrolsize = 0; + + // run through sectors and handle each type in sector + for(INT16 sX = 1; sX < MAP_WORLD_X - 1; ++sX ) + { + for(INT16 sY = 1; sY < MAP_WORLD_X - 1; ++sY ) + { + // is this sector within range? + FLOAT euklid_dist = (sX - sMapX)*(sX - sMapX) + (sY - sMapY)*(sY - sMapY); + detect_rangefactor = euklid_dist / (range*range); + //detect_rangefactor *= detect_rangefactor; + + if ( detect_rangefactor > 1.0f ) + continue; + + detect_rangefactor = 1.01f - detect_rangefactor; + + patrolsize = NumEnemiesInSector( sX, sY ); + + // no or single enemies -> skip this + if ( patrolsize < 2 ) + continue; + + detect_sizefactor = sqrt((FLOAT)(patrolsize) / (FLOAT)(normalgroupsize)); + + detect_chance = detect_basechance * detect_rangefactor * detect_sizefactor; + + UINT32 scanresult = Random(100); + if ( scanresult < 100 * detect_chance ) + { + // enemy patrol detected + SectorInfo[ SECTOR( sX, sY ) ].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_HERE; + + if ( scanresult < 100 * (detect_chance - scanexactnumbermalus) ) + { + // our scan was very good, we even got the exact enemy numbers + SectorInfo[ SECTOR( sX, sY ) ].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER; + } + + // we roll again to decide wether we know the direction this patrol is moving in + scanresult = Random(100); + + if ( scanresult < 100 * (detect_chance - scandirectionmalus) ) + { + // we were succesful in deducting the direction they are moving in + SectorInfo[ SECTOR( sX, sY ) ].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_DIRECTION; + } + } + } + } + + // award experience to all radio operators + for ( uiCnt = 0, pSoldier = MercPtrs[ uiCnt ]; uiCnt <= gTacticalStatus.Team[ MercPtrs[0]->bTeam ].bLastID; uiCnt++, pSoldier++) + { + if( pSoldier->bActive && ( pSoldier->sSectorX == sMapX ) && ( pSoldier->sSectorY == sMapY ) && ( pSoldier->bSectorZ == bZ) ) + { + if( ( pSoldier->bAssignment == RADIO_SCAN ) && ( EnoughTimeOnAssignment( pSoldier ) ) && ( pSoldier->flags.fMercAsleep == FALSE ) ) + { + StatChange( pSoldier, WISDOMAMT, 5, TRUE ); + StatChange( pSoldier, EXPERAMT, 3, TRUE ); + } + } + } +} + +// reset scan flags in all sectors +void ClearSectorScanResults() +{ + for(INT16 sX = 1; sX < MAP_WORLD_X - 1; ++sX ) + { + for(INT16 sY = 1; sY < MAP_WORLD_X - 1; ++sY ) + { + SectorInfo[ SECTOR( sX, sY ) ].uiFlags &= ~(SF_ASSIGN_NOTICED_ENEMIES_HERE|SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER); + } + } +} + int TownTrainerQsortCompare(const void *pArg1, const void *pArg2) { if (((TOWN_TRAINER_TYPE *)pArg1)->sTrainingPts > ((TOWN_TRAINER_TYPE *)pArg2)->sTrainingPts) @@ -7799,6 +7961,17 @@ void HandleShadingOfLinesForAssignmentMenus( void ) } } + // radio scan + if( pSoldier->CanUseRadio() ) + { + // unshade line + UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_RADIO_SCAN ); + } + else + { + // shade line + ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_RADIO_SCAN ); + } // patient if( CanCharacterPatient( pSoldier ) ) @@ -10267,7 +10440,42 @@ void AssignmentMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) DoScreenIndependantMessageBox( sString , MSG_BOX_FLAG_OK, NULL ); } - break; + break; + case( ASSIGN_MENU_RADIO_SCAN ): + if( pSoldier->CanUseRadio() ) + { + // stop showing menu + fShowAssignmentMenu = FALSE; + giAssignHighLine = -1; + + pSoldier->bOldAssignment = pSoldier->bAssignment; + + if( ( pSoldier->bAssignment != RADIO_SCAN ) ) + { + SetTimeOfAssignmentChangeForMerc( pSoldier ); + } + + // remove from squad + if( pSoldier->bOldAssignment == VEHICLE ) + { + TakeSoldierOutOfVehicle( pSoldier ); + } + RemoveCharacterFromSquads( pSoldier ); + + ChangeSoldiersAssignment( pSoldier, RADIO_SCAN ); + + AssignMercToAMovementGroup( pSoldier ); + + MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); + + // set dirty flag + fTeamPanelDirty = TRUE; + fMapScreenBottomDirty = TRUE; + + // set assignment for group + SetAssignmentForList( ( INT8 ) RADIO_SCAN, 0 ); + } + break; case( ASSIGN_MENU_TRAIN ): if( CanCharacterPractise( pSoldier ) ) { @@ -12498,6 +12706,30 @@ void SetSoldierAssignment( SOLDIERTYPE *pSoldier, INT8 bAssignment, INT32 iParam } break; + case( RADIO_SCAN ): + if( pSoldier->CanUseRadio() ) + { + pSoldier->bOldAssignment = pSoldier->bAssignment; + + // remove from squad + RemoveCharacterFromSquads( pSoldier ); + + // remove from any vehicle + if( pSoldier->bOldAssignment == VEHICLE ) + { + TakeSoldierOutOfVehicle( pSoldier ); + } + + if ( pSoldier->bAssignment != RADIO_SCAN ) + { + SetTimeOfAssignmentChangeForMerc( pSoldier ); + } + + ChangeSoldiersAssignment( pSoldier, RADIO_SCAN ); + AssignMercToAMovementGroup( pSoldier ); + } + break; + case( FACILITY_INTERROGATE_PRISONERS ): if( pSoldier->CanProcessPrisoners() ) { @@ -13598,6 +13830,10 @@ void ReEvaluateEveryonesNothingToDo() fNothingToDo = !CanCharacterRepair( pSoldier ) || HasCharacterFinishedRepairing( pSoldier ); break; + case RADIO_SCAN: + fNothingToDo = !pSoldier->CanUseRadio(); + break; + case FACILITY_INTERROGATE_PRISONERS: fNothingToDo = !pSoldier->CanProcessPrisoners(); break; @@ -13778,6 +14014,14 @@ void SetAssignmentForList( INT8 bAssignment, INT8 bParam ) } } break; + case ( RADIO_SCAN ): + if ( pSoldier->CanUseRadio() ) + { + pSoldier->bOldAssignment = pSoldier->bAssignment; + SetSoldierAssignment( pSoldier, RADIO_SCAN, bParam, 0,0 ); + fItWorked = TRUE; + } + break; case( TRAIN_SELF ): if( CanCharacterTrainStat( pSoldier, bParam , TRUE, FALSE ) ) { diff --git a/Strategic/Assignments.h b/Strategic/Assignments.h index 5d73a298..0e1f7866 100644 --- a/Strategic/Assignments.h +++ b/Strategic/Assignments.h @@ -56,6 +56,7 @@ enum VEHICLE, IN_TRANSIT, REPAIR, + RADIO_SCAN, // added by Flugente TRAIN_SELF, TRAIN_TOWN, TRAIN_MOBILE, // HEADROCK HAM 3.6: Training mobile militia. diff --git a/Strategic/Campaign Types.h b/Strategic/Campaign Types.h index 92ca18b9..d2d07723 100644 --- a/Strategic/Campaign Types.h +++ b/Strategic/Campaign Types.h @@ -491,8 +491,10 @@ typedef struct SECTORINFO UINT8 uiNumberOfPrisonersOfWar[PRISONER_MAX]; UINT8 uiInterrogationHundredsLeft; + + UINT32 uiTimeAIArtillerywasOrdered; // Flugente: updated every time an artillery strike is ordered from the militia - INT8 bPadding[ 36 ]; + INT8 bPadding[ 29 ]; }SECTORINFO; diff --git a/Strategic/Map Screen Interface.h b/Strategic/Map Screen Interface.h index 4a8538e2..8ac145c3 100644 --- a/Strategic/Map Screen Interface.h +++ b/Strategic/Map Screen Interface.h @@ -125,6 +125,7 @@ enum { ASSIGN_MENU_PATIENT, ASSIGN_MENU_VEHICLE, ASSIGN_MENU_REPAIR, + ASSIGN_MENU_RADIO_SCAN, // added by Flugente ASSIGN_MENU_TRAIN, ASSIGN_MENU_MOVE_ITEMS, // added by Flugente ASSIGN_MENU_FACILITY, // HEAROCK HAM 3.6: Facility List menu diff --git a/Strategic/MilitiaSquads.cpp b/Strategic/MilitiaSquads.cpp index e5aee8d4..e8ce6f49 100644 --- a/Strategic/MilitiaSquads.cpp +++ b/Strategic/MilitiaSquads.cpp @@ -33,6 +33,7 @@ #include "MilitiaSquads.h" #include "Reinforcement.h" #include "Inventory Choosing.h" // added by Flugente for MoveOneMilitiaEquipmentSet() and MoveMilitiaEquipment() +#include "message.h" // added by Flugente for ScreenMsg() // Debug defines @@ -1813,6 +1814,105 @@ void DoMilitiaHelpFromAdjacentSectors( INT16 sMapX, INT16 sMapY ) gfStrategicMilitiaChangesMade = FALSE; } +// Flugente: order sNumber reinforcements from src sector to target sector +BOOLEAN CallMilitiaReinforcements( INT16 sTargetMapX, INT16 sTargetMapY, INT16 sSrcMapX, INT16 sSrcMapY, UINT16 sNumber ) +{ + UINT8 uiNumGreen = 0, uiNumReg = 0, uiNumElite = 0; + SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sTargetMapX, sTargetMapY ) ] ); + + guiDirNumber = 0; + + UINT8 insertioncode = INSERTION_CODE_CENTER; + UINT8 movetype = THROUGH_STRATEGIC_MOVE; + + // determine from which direction militia should enter (and exit if this isn't possible) + if ( sTargetMapX == sSrcMapX + 1 && sTargetMapY == sSrcMapY ) + { + insertioncode = INSERTION_CODE_WEST; + movetype = EAST_STRATEGIC_MOVE; + } + else if ( sTargetMapX == sSrcMapX - 1 && sTargetMapY == sSrcMapY ) + { + insertioncode = INSERTION_CODE_EAST; + movetype = WEST_STRATEGIC_MOVE; + } + else if ( sTargetMapX == sSrcMapX && sTargetMapY == sSrcMapY + 1 ) + { + insertioncode = INSERTION_CODE_NORTH; + movetype = SOUTH_STRATEGIC_MOVE; + } + else if ( sTargetMapX == sSrcMapX && sTargetMapY == sSrcMapY - 1 ) + { + insertioncode = INSERTION_CODE_SOUTH; + movetype = NORTH_STRATEGIC_MOVE; + } + else + { + // no proper direction here... get out + return FALSE; + } + + // test wether travel from src to target is possible ( we cannot open the src map information, we'll rely on the xml data instead + if( SectorInfo[ SECTOR(sSrcMapX,sSrcMapY) ].ubTraversability[ movetype ] == GROUNDBARRIER || SectorInfo[ SECTOR(sSrcMapX,sSrcMapY) ].ubTraversability[ movetype ] == EDGEOFWORLD ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia cannot traverse to this sector due to the terrain." ); + return FALSE; + } + + guiDirNumber = 0; + + ZeroMemory( gpAttackDirs, sizeof( gpAttackDirs ) ); + + gpAttackDirs[ guiDirNumber ][0] = uiNumGreen = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]; + gpAttackDirs[ guiDirNumber ][1] = uiNumReg = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; + gpAttackDirs[ guiDirNumber ][2] = uiNumElite = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; + gpAttackDirs[ guiDirNumber ][3] = INSERTION_CODE_CENTER; + + guiDirNumber = insertioncode + 1; + UINT16 sMilitiaMoved = 0; + while ( sMilitiaMoved < sNumber && CountMilitia(pSectorInfo ) < gGameExternalOptions.iMaxMilitiaPerSector && MoveOneBestMilitiaMan( sSrcMapX, sSrcMapY, sTargetMapX, sTargetMapY ) ) + { + ++sMilitiaMoved; + + gpAttackDirs[ guiDirNumber ][0] += pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] - uiNumGreen; + gpAttackDirs[ guiDirNumber ][1] += pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA] - uiNumReg; + gpAttackDirs[ guiDirNumber ][2] += pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] - uiNumElite; + gpAttackDirs[ guiDirNumber ][3] = insertioncode; + + uiNumGreen = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]; + uiNumReg = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; + uiNumElite = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; + } + + guiDirNumber = 5; + + if ( !sMilitiaMoved ) + return FALSE; + + // we need to se this falg. If it wasn't set prior to this, we remove it again afterwards, otherwise all militia will join us if we are in combat + BOOLEAN wantreinforcements = (gTacticalStatus.uiFlags & WANT_MILITIA_REINFORCEMENTS); + gTacticalStatus.uiFlags |= WANT_MILITIA_REINFORCEMENTS; + + if (is_networked) + { + if (gfStrategicMilitiaChangesMade) + { + RemoveMilitiaFromTactical(); + if(is_server && gMilitiaEnabled == 1) + PrepareMilitiaForTactical(FALSE); + } + } + else + PrepareMilitiaForTactical(FALSE); + + if ( !wantreinforcements ) + gTacticalStatus.uiFlags &= ~WANT_MILITIA_REINFORCEMENTS; + + gfStrategicMilitiaChangesMade = FALSE; + + return TRUE; +} + void MSCallBack( UINT8 ubResult ) { if( ubResult == MSG_BOX_RETURN_YES ) diff --git a/Strategic/XML_SectorNames.cpp b/Strategic/XML_SectorNames.cpp index 02bbab19..bd94fc21 100644 --- a/Strategic/XML_SectorNames.cpp +++ b/Strategic/XML_SectorNames.cpp @@ -48,6 +48,7 @@ typedef struct UINT8 sWaterType; // Food System UINT16 usNaturalDirt; // Dirt System UINT8 usCurfewValue; // Covert Ops + INT8 sRadioScanModifier; // Radio Operator UINT16 usPrisonRoomNumber[MAX_PRISON_ROOMS]; // Prisoner System UINT32 currentDepth; UINT32 maxReadDepth; @@ -145,6 +146,7 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char ** strcmp(name, "sWaterType") == 0 || strcmp(name, "usNaturalDirt") == 0 || strcmp(name, "usCurfewValue") == 0 || + strcmp(name, "sRadioScanModifier") == 0 || strcmp(name, "usPrisonRoomNumber00") == 0 || strcmp(name, "usPrisonRoomNumber01") == 0 || strcmp(name, "usPrisonRoomNumber02") == 0 || @@ -284,6 +286,13 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) SectorExternalData[ubSectorId][2].usCurfewValue = pData->usCurfewValue; SectorExternalData[ubSectorId][3].usCurfewValue = pData->usCurfewValue; + INT8 radioscanmod = max(-3, pData->sRadioScanModifier); + radioscanmod = min(3, pData->sRadioScanModifier); + SectorExternalData[ubSectorId][0].sRadioScanModifier = pData->sRadioScanModifier; + SectorExternalData[ubSectorId][1].sRadioScanModifier = pData->sRadioScanModifier; + SectorExternalData[ubSectorId][2].sRadioScanModifier = pData->sRadioScanModifier; + SectorExternalData[ubSectorId][3].sRadioScanModifier = pData->sRadioScanModifier; + for(UINT8 i = 0; i usPrisonRoomNumber[i]; @@ -294,6 +303,7 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) pData->sWaterType = 0; pData->usNaturalDirt = 100; pData->usCurfewValue = 0; + pData->sRadioScanModifier = 0; } } @@ -358,6 +368,11 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) pData->curElement = SECTORNAME_ELEMENT_SECTOR; pData->usCurfewValue = (UINT8) atoi(pData->szCharData); } + else if(strcmp(name, "sRadioScanModifier") == 0) + { + pData->curElement = SECTORNAME_ELEMENT_SECTOR; + pData->sRadioScanModifier = (INT8) atoi(pData->szCharData); + } else if(strcmp(name, "usPrisonRoomNumber00") == 0) { pData->curElement = SECTORNAME_ELEMENT_SECTOR; diff --git a/Tactical/DisplayCover.cpp b/Tactical/DisplayCover.cpp index 1d7d6770..56251237 100644 --- a/Tactical/DisplayCover.cpp +++ b/Tactical/DisplayCover.cpp @@ -67,6 +67,15 @@ enum MINES_VALUES MAX_MINES }; +// Flugente: trait display +enum TRAIT_VALUES +{ + TRAIT_ALL = MAX_MINES + 1, + TRAIT_1, + TRAIT_2, + MAX_TRAIT +}; + struct CoverCell { @@ -75,8 +84,9 @@ struct CoverCell BOOLEAN fInverseColor; INT8 bMines; + INT8 bTrait; - CoverCell() : sGridNo(NOWHERE), bCover(INVALID_COVER), fInverseColor(FALSE), bMines(MINES_ALL) {} + CoverCell() : sGridNo(NOWHERE), bCover(INVALID_COVER), fInverseColor(FALSE), bMines(MINES_ALL), bTrait(TRAIT_ALL) {} }; @@ -111,6 +121,13 @@ enum MINES_DRAW_MODE { MINES_DRAW_MAX }; +// Flugente: trait draw mode +enum TRAIT_DRAW_MODE { + TRAIT_DRAW_OFF, + TRAIT_DRAW_RANGE, + TRAIT_DRAW_MAX +}; + //****** Local Variables ********************************************* @@ -124,6 +141,8 @@ COVER_DRAW_MODE gubDrawMode = COVER_DRAW_OFF; MINES_DRAW_MODE gubDrawModeMine = MINES_DRAW_OFF; // Flugente: mines display +TRAIT_DRAW_MODE gubDrawModeTrait = TRAIT_DRAW_OFF; // Flugente: mines display + //******* Local Function Prototypes *********************************** @@ -138,7 +157,7 @@ void AddCoverObjectsToViewArea(); void RemoveCoverObjectsFromViewArea(); void RemoveMinesObjectsFromViewArea(); // added by Flugente, has to be declared here - +void RemoveTraitObjectsFromViewArea(); void CalculateCover(); void CalculateCoverForSoldier( SOLDIERTYPE* pForSoldier, const INT32& sTargetGridNo, const BOOLEAN& fRoof, INT8& bCover ); @@ -746,11 +765,6 @@ void ToggleHostileTrapsView() void ToggleTrapNetworkView() { SwitchMinesDrawModeForNetworks(); - /*if (gubDrawModeMine == MINES_DRAW_PLAYERTEAM_NETWORKS) { - SwitchMineViewOff(); - } else { - SwitchToTrapNetworkView(); - }*/ } void SwitchMinesDrawModeForNetworks() @@ -797,19 +811,6 @@ void SwitchMinesDrawModeForNetworks() DisplayMines(TRUE); break; } - - /*switch(gubDrawModeMine) - { - case MINES_DRAW_OFF: - SwitchToTrapNetworkView(); - break; - case MINES_DRAW_PLAYERTEAM_NETWORKS: - SwitchToHostileTrapsView(); - break; - default: - SwitchMineViewOff(); - break; - }*/ } ///END key binding functions @@ -1254,3 +1255,270 @@ void DetermineMineDisplayInTile( INT32 sGridNo, INT8 bLevel, INT8& bMines, BOOLE } } +// ----------------------------- trait range display after this ---------------------------------------- +// added by Flugente + +//******* Local Function Prototypes *********************************** + +TileDefines GetTileTraitIndex( const INT8& bTrait ); + +void AddTraitObjectsToViewArea(); + +void CalculateTraitRange(); + +BOOLEAN TraitTileHasAdjTile( const INT32& ubX, const INT32& ubY, const INT32& ubZ ); + +UINT32 usDisplayTrait = NO_SKILLTRAIT_NT; +INT32 sTraitgridNo = NOWHERE; + +///BEGIN key binding functions +void ToggleTraitRangeView(BOOLEAN fOn) +{ + if ( fOn ) + { + gubDrawModeTrait = TRAIT_DRAW_RANGE; + gubDrawMode = COVER_DRAW_OFF; + gubDrawModeMine = MINES_DRAW_OFF; + } + else + { + gubDrawModeTrait = TRAIT_DRAW_OFF; + SetTraitToDisplay( NO_SKILLTRAIT_NT ); + SetGridNoForTraitDisplay( NOWHERE ); + } + + //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trait ranges"); + DisplayTraitRange(TRUE); +} + +void SetTraitToDisplay( UINT32 aTrait ) +{ + usDisplayTrait = aTrait; +} + +void SetGridNoForTraitDisplay( INT32 sGridNo ) +{ + sTraitgridNo = sGridNo; +} + +void DisplayTraitRange( const BOOLEAN& forceUpdate ) +{ + if ( gGameExternalOptions.ubCoverDisplayUpdateWait == -1 ) + { + return; + } + + if ( forceUpdate || ( !gfScrollPending && !gfScrollInertia && GetTickCount() > guiMinesNextUpdateTime ) ) + { + CalculateTraitRange(); + guiMinesNextUpdateTime = GetTickCount() + gGameExternalOptions.ubCoverDisplayUpdateWait; + } +} + +void CalculateTraitRange() +{ + INT32 ubX, ubY, ubZ; + SOLDIERTYPE* pSoldier; + + RemoveTraitObjectsFromViewArea(); + + if ( gubDrawModeTrait == TRAIT_DRAW_OFF ) + return; + + if( gusSelectedSoldier == NOBODY || !GetSoldier( &pSoldier, gusSelectedSoldier ) ) + return; + + UINT16 range1 = 0; + UINT16 range2 = 0; + switch ( usDisplayTrait ) + { + case RADIO_OPERATOR_NT: + { + // we 'mark' the map position with which we called the menu, so that the player sees where he is targetting + + } + break; + + default: + return; + } + + //remove other displays + if( gubDrawModeMine != MINES_DRAW_OFF ) + RemoveMinesObjectsFromViewArea(); + + if ( gubDrawMode != COVER_DRAW_OFF ) + RemoveCoverObjectsFromViewArea(); + + const INT32& sSelectedSoldierGridNo = MercPtrs[ gusSelectedSoldier ]->sGridNo; + + INT16 usTmp; + GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_START_Y, &gsMineMinCellX, &usTmp ); + GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_END_Y, &gsMineMaxCellX, &usTmp ); + + GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_START_Y, &usTmp, &gsMineMinCellY ); + GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_END_Y, &usTmp, &gsMineMaxCellY ); + for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX ) + { + for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY ) + { + for ( ubZ=0; ubZ WORLD_COLS ) + { + continue; + } + + for ( ubTY = ubY-1; ubTY <= ubY+1; ++ubTY ) + { + if ( ubTY < 0 || ubTY > WORLD_ROWS ) + { + continue; + } + + INT8& bTrait = gCoverViewArea[ ubTX ][ ubTY ][ ubZ ].bTrait; + + if ( bTrait == TRAIT_1 || bTrait == TRAIT_2 ) + { + return TRUE; + } + } + } + + return FALSE; +} + +void RemoveTraitObjectsFromViewArea() +{ + if (gsMineMaxCellY == -1) + { + return; + } + INT32 ubX, ubY, ubZ; + BOOLEAN fChanged = FALSE; + + for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX ) + { + for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY ) + { + for ( ubZ=0; ubZubSoldierID ]->bAssignment != RADIO_SCAN ) + { + if ( MercPtrs[ pFace->ubSoldierID ]->bSoldierFlagMask & (SOLDIER_RADIO_OPERATOR_SCANNING|SOLDIER_RADIO_OPERATOR_LISTENING) ) + { + DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 19 ); + bNumRightIcons++; + } + else if ( MercPtrs[ pFace->ubSoldierID ]->bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_JAMMING ) + { + DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 20 ); + bNumRightIcons++; + } + } + switch( pSoldier->bAssignment ) { case DOCTOR: @@ -2270,6 +2285,11 @@ void HandleRenderFaceAdjustments( FACETYPE *pFace, BOOLEAN fDisplayBuffer, BOOLE break; + case RADIO_SCAN: + sIconIndex_Assignment = 19; + fDoIcon_Assignment = TRUE; + break; + case MOVE_EQUIPMENT: { sIconIndex_Assignment = 15; diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index a3745054..6286c501 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -3946,6 +3946,23 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec } } + // Flugente: display energy level of power packs + if ( HasItemFlag(pObject->usItem, POWER_PACK) ) + { + if ( OVERHEATING_MAX_TEMPERATURE > 0 ) + { + FLOAT energylevel = (*pObject)[0]->data.bTemperature / (FLOAT)OVERHEATING_MAX_TEMPERATURE; + + UINT32 red = (UINT32) ( 255 * ( 1.0 - max(0, 2*energylevel - 1.0) ) ); + UINT32 green = (UINT32) ( 255 * ( min(1.0, 2*energylevel) ) ); + UINT32 blue = 0; + + UINT16 colour = Get16BPPColor( FROMRGB( red, green, blue ) ); + + DrawItemUIBarEx( pObject, DRAW_ITEM_TEMPERATURE, sX, sY + sHeight-1, ITEMDESC_ITEM_STATUS_WIDTH, sHeight-1, colour, colour, TRUE, guiSAVEBUFFER); + } + } + // display symbol if we are leaning our weapon on something // display only if eapon resting is allowed, display is allowed, item is a gun/launcher, we are a person, we hold the gun in our hand, and we are resting the gun if ( gGameExternalOptions.fWeaponResting && gGameExternalOptions.fDisplayWeaponRestingIndicator && pItem->usItemClass & (IC_GUN | IC_LAUNCHER) && pSoldier && &(pSoldier->inv[pSoldier->ubAttackingHand]) == pObject && pSoldier->IsWeaponMounted() ) @@ -6771,6 +6788,22 @@ void RenderItemDescriptionBox( ) DrawItemUIBarEx( &(*iter), DRAW_ITEM_TEMPERATURE, sCenX+4, sCenY, ITEM_BAR_WIDTH, ITEM_BAR_HEIGHT, colour, colour, TRUE, guiSAVEBUFFER ); } + // Flugente: display energy level of power packs + else if ( HasItemFlag(iter->usItem, POWER_PACK) ) + { + if ( OVERHEATING_MAX_TEMPERATURE > 0 ) + { + FLOAT energylevel = (*iter)[0]->data.bTemperature / (FLOAT)OVERHEATING_MAX_TEMPERATURE; + + UINT32 red = (UINT32) ( 255 * ( 1.0 - max(0, 2*energylevel - 1.0) ) ); + UINT32 green = (UINT32) ( 255 * ( min(1.0, 2*energylevel) ) ); + UINT32 blue = 0; + + UINT16 colour = Get16BPPColor( FROMRGB( red, green, blue ) ); + + DrawItemUIBarEx( &(*iter), DRAW_ITEM_TEMPERATURE, sCenX+4, sCenY, ITEM_BAR_WIDTH, ITEM_BAR_HEIGHT, colour, colour, TRUE, guiSAVEBUFFER ); + } + } } } @@ -7017,6 +7050,54 @@ void RenderItemDescriptionBox( ) mprintf( usX, usY, pStr ); } } + else if ( HasItemFlag(gpItemDescObject->usItem, POWER_PACK) ) + { + if ( OVERHEATING_MAX_TEMPERATURE > 0 ) + { + FLOAT energylevel = (*gpItemDescObject)[0]->data.bTemperature / (FLOAT)OVERHEATING_MAX_TEMPERATURE; + + UINT32 red = (UINT32) ( 255 * ( 1.0 - max(0, 2*energylevel - 1.0) ) ); + UINT32 green = (UINT32) ( 255 * ( min(1.0, 2*energylevel) ) ); + UINT32 blue = 0; + + UINT8 stringNum = 5; + if ( energylevel > 0.8f ) + stringNum = 1; + else if ( energylevel > 0.6f ) + stringNum = 2; + else if ( energylevel > 0.4f ) + stringNum = 3; + else if ( energylevel > 0.2f ) + stringNum = 4; + + // UDB system displays a string with colored condition text. + int regionindex = 7; + SetFontForeground( ForegroundColor ); + swprintf( pStr, L"%s", gPowerPackDesc[STR_POWERPACK_BEGIN] ); // "Temperature is " + mprintf( gItemDescTextRegions[regionindex].sLeft, gItemDescTextRegions[regionindex].sTop, pStr ); + // Record length + INT16 indent = StringPixLength( gPowerPackDesc[STR_POWERPACK_BEGIN], ITEMDESC_FONT ); + + swprintf( pStr, L"%s", gPowerPackDesc[stringNum] ); + + SetRGBFontForeground( red, green, blue ); + + mprintf( gItemDescTextRegions[regionindex].sLeft+indent+2, gItemDescTextRegions[regionindex].sTop, pStr ); + // Record length + indent += StringPixLength( gPowerPackDesc[stringNum], ITEMDESC_FONT ); + + SetFontForeground( ForegroundColor ); + swprintf( pStr, L"%s", gPowerPackDesc[STR_POWERPACK_END] ); // "." + mprintf( gItemDescTextRegions[regionindex].sLeft + indent + 2, gItemDescTextRegions[regionindex].sTop, pStr ); + + // to get the text to the left side... + swprintf( pStr, L""); + + FindFontRightCoordinates( gItemDescTextRegions[regionindex].sLeft, gItemDescTextRegions[regionindex].sTop, gItemDescTextRegions[regionindex].sRight - gItemDescTextRegions[regionindex].sLeft ,gItemDescTextRegions[regionindex].sBottom - gItemDescTextRegions[regionindex].sTop ,pStr, BLOCKFONT2, &usX, &usY); + + mprintf( usX, usY, pStr ); + } + } // UDB system displays a string with colored condition text. SetFontForeground( ForegroundColor ); diff --git a/Tactical/Interface Utils.cpp b/Tactical/Interface Utils.cpp index 9333e2f1..8c868103 100644 --- a/Tactical/Interface Utils.cpp +++ b/Tactical/Interface Utils.cpp @@ -508,6 +508,13 @@ void DrawItemUIBarEx( OBJECTTYPE *pObject, UINT8 ubStatus, INT16 sXPos, INT16 sY { sValue = 100; } + else if ( HasItemFlag(pObject->usItem, POWER_PACK) ) + { + if ( OVERHEATING_MAX_TEMPERATURE > 0 ) + { + sValue = (INT16) ( 100 * (*pObject)[0]->data.bTemperature / (FLOAT)OVERHEATING_MAX_TEMPERATURE ); + } + } else { sValue = (INT16) (100 * GetGunOverheatJamPercentage( pObject) ); diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index 0a98ffd5..9cda2e6f 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -714,10 +714,10 @@ extern OBJECTTYPE gTempObject; #define TRIPWIRE_NETWORK_LVL_3 0x00400000 //4194304 #define TRIPWIRE_NETWORK_LVL_4 0x00800000 //8388608 -/*#define TRIPWIRE_NETWORK_LVL_1 0x01000000 //16777216 -#define TRIPWIRE_NETWORK_LVL_2 0x02000000 //33554432 -#define TRIPWIRE_NETWORK_LVL_3 0x04000000 //67108864 -#define TRIPWIRE_NETWORK_LVL_4 0x08000000 //134217728 +#define ARTILLERY_STRIKE_COUNT_1 0x01000000 //16777216 +#define ARTILLERY_STRIKE_COUNT_2 0x02000000 //33554432 +#define ARTILLERY_STRIKE_COUNT_4 0x04000000 //67108864 +/*#define TRIPWIRE_NETWORK_LVL_4 0x08000000 //134217728 #define PLAYER_NET_1_LVL_4 0x10000000 //268435456 #define PLAYER_NET_2_LVL_4 0x20000000 //536870912 @@ -725,6 +725,8 @@ extern OBJECTTYPE gTempObject; #define PLAYER_NET_4_LVL_4 0x80000000 //2147483648*/ // ---------------------------------------------------------------- +#define ANY_ARTILLERY_FLAG (ARTILLERY_STRIKE_COUNT_1|ARTILLERY_STRIKE_COUNT_2|ARTILLERY_STRIKE_COUNT_4) + // -------- added by Flugente: various item flags -------- // flags used for various item properties (easier than adding 32 differently named variables). DO NOT CHANGE THEM, UNLESS YOU KNOW WHAT YOU ARE DOING!!! // note that these should not be used to determine what kind of an attachment an item is, that is determined by attachmentclass and the AC_xxx flags above @@ -759,9 +761,9 @@ extern OBJECTTYPE gTempObject; #define SCUBA_FINS 0x00800000 //8388608 // this item speed up swimming, but slows walking and running #define TRIPWIREROLL 0x01000000 //16777216 // this item is a tripwire roll -/*#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 RADIO_SET 0x02000000 //33554432 // item can be used to radio militia/squads in other sectors +#define SIGNAL_SHELL 0x04000000 //67108864 // this is a signal shell that precedes artillery barrages +#define POWER_PACK 0x08000000 //134217728 // item continously powers an item it is attached to #define PLAYER_NET_1_LVL_4 0x10000000 //268435456 #define PLAYER_NET_2_LVL_4 0x20000000 //536870912 @@ -1856,6 +1858,9 @@ enum IMP_COVERT, IMP_COVERT_EXP, + // Flugente: added radio operator + IMP_RADIO_OPERATOR, + MAX_IMP_ITEM_TYPES }; diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index acade455..e346d9ca 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -7822,6 +7822,12 @@ BOOLEAN CreateItem( UINT16 usItem, INT16 bStatus, OBJECTTYPE * pObj ) (*pObj)[0]->data.bTemperature = OVERHEATING_MAX_TEMPERATURE; } + // Flugente: certain items get the maximum temperature when they are created + if ( HasItemFlag(usItem, POWER_PACK) ) + { + (*pObj)[0]->data.bTemperature = (FLOAT)(OVERHEATING_MAX_TEMPERATURE); + } + //ADB ubWeight has been removed, see comments in OBJECTTYPE //pObj->ubWeight = CalculateObjectWeight( pObj ); fRet = TRUE; diff --git a/Tactical/Items.h b/Tactical/Items.h index bb45433b..faf214c0 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -494,6 +494,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) + INT8 sRadioScanModifier; // modifies the scan range of the radio scan assignment in this sector UINT16 usPrisonRoomNumber[MAX_PRISON_ROOMS]; // room numbers of prisons } SECTOR_EXT_DATA; diff --git a/Tactical/Militia Control.cpp b/Tactical/Militia Control.cpp index 1d90cd98..b77eee29 100644 --- a/Tactical/Militia Control.cpp +++ b/Tactical/Militia Control.cpp @@ -1009,7 +1009,8 @@ void HandleShadingOfLinesForMilitiaControlMenu( void ) if ( GetSoldier( &pSoldier, gusSelectedSoldier ) ) { // Check LOS! - if ( SoldierTo3DLocationLineOfSightTest( pSoldier, pTMilitiaSoldier->sGridNo, pTMilitiaSoldier->pathing.bLevel, 3, TRUE, CALC_FROM_ALL_DIRS ) ) + // Flugente: active radio sets allows us to give individual orders even without a line of sight + if ( pSoldier->CanUseRadio() || SoldierTo3DLocationLineOfSightTest( pSoldier, pTMilitiaSoldier->sGridNo, pTMilitiaSoldier->pathing.bLevel, 3, TRUE, CALC_FROM_ALL_DIRS ) ) { UnShadeStringInBox( ghMilitiaControlBox, MILCON_MENU_ATTACK ); UnShadeStringInBox( ghMilitiaControlBox, MILCON_MENU_HOLD ); @@ -1069,7 +1070,11 @@ BOOLEAN CheckIfRadioIsEquipped( void ) //pSoldier = GetSelectedAssignSoldier( FALSE ); //do not use if ( GetSoldier( &pSoldier, gusSelectedSoldier ) ) - { + { + // Flugente: active radio sets also count as radio + if ( pSoldier->CanUseRadio() ) + return TRUE; + //bSlot = FindObj( pSoldier, EXTENDEDEAR ); bSlot = FindHearingAid(pSoldier); //ScreenMsg( FONT_WHITE, MSG_INTERFACE, L"Position: %d", bSlot ); diff --git a/Tactical/SkillMenu.cpp b/Tactical/SkillMenu.cpp new file mode 100644 index 00000000..ce31c908 --- /dev/null +++ b/Tactical/SkillMenu.cpp @@ -0,0 +1,677 @@ +/** + * @file + * @author Flugente (bears-pit.com) + */ + +#include "SkillMenu.h" +#include "Soldier Profile Type.h" +#include "Overhead.h" +#include "Text.h" +#include "Isometric Utils.h" +#include "DisplayCover.h" +#include "worldman.h" + +UINT16 usTraitMenuPosX = 0; +UINT16 usTraitMenuPosY = 0; + +INT32 sTraitsMenuTargetGridNo = NOWHERE; + +void Hide( void ) +{ + // set off display of trait range/target gridno + ToggleTraitRangeView(FALSE); + + // Dirty! + fMapPanelDirty = TRUE; +} + +// Turns an option off by always returning FALSE +BOOLEAN Popup_OptionOff( void ) { return FALSE; } + +void +SkillMenuItem::SetupPopup(CHAR* name) +{ + // create a popup + gPopup = new POPUP(name); + + // add a callback that lets the keyboard handler know we're done (and ready to pop up again) + gPopup->setCallback(POPUP_CALLBACK_HIDE, new popupCallbackFunction( &Hide ) ); +} + +// Different SkillItem classes. We need wrappers for each one, as we cannot pass a pointer to a member function (we need a concrete object) + +/////////////////////////////// Trait Selection //////////////////////////////////////////// +TraitSelection gTraitSelection; + +//void Wrapper_Function_TraitSelection( UINT32 aVal) { gTraitSelection.Functions(aVal); } +void Wrapper_Setup_TraitSelection( UINT32 aVal) { gTraitSelection.Setup(aVal); } +void Wrapper_Cancel_TraitSelection( UINT32 aVal ) { gTraitSelection.Cancel(); } +/////////////////////////////// Trait Selection //////////////////////////////////////////// + +/////////////////////////////// Skill Selection //////////////////////////////////////////// +SkillSelection gSkillSelection; + +void Wrapper_Function_SkillSelection( UINT32 aVal) { gSkillSelection.Functions(aVal); } +void Wrapper_Setup_SkillSelection( UINT32 aVal) { gSkillSelection.Setup(aVal); } +void Wrapper_Cancel_SkillSelection( UINT32 aVal ) { gSkillSelection.Cancel(); } +/////////////////////////////// Skill Selection //////////////////////////////////////////// + +/////////////////////////////// Artillery Sector Selection //////////////////////////////////////////// +ArtillerySector gArtillerySector; + +//void Wrapper_Function_ArtillerySector( UINT32 aVal) { gArtillerySector.Functions(aVal); } +void Wrapper_Setup_ArtillerySector( UINT32 aVal) { gArtillerySector.Setup(aVal); } +void Wrapper_Cancel_ArtillerySector( UINT32 aVal ) { gArtillerySector.Cancel(); } +/////////////////////////////// Artillery Sector Selection //////////////////////////////////////////// + +/////////////////////////////// Artillery Team Selection //////////////////////////////////////////// +ArtilleryTeam gArtilleryTeam; + +void Wrapper_Function_ArtilleryTeam( UINT32 aVal) { gArtilleryTeam.Functions(aVal); } +void Wrapper_Setup_ArtilleryTeam( UINT32 aVal) { gArtilleryTeam.Setup(aVal); } +void Wrapper_Cancel_ArtilleryTeam( UINT32 aVal ) { gArtilleryTeam.Cancel(); } +/////////////////////////////// Artillery Team Selection //////////////////////////////////////////// + +/////////////////////////////// Reinforcement Sector Selection //////////////////////////////////////////// +ReinforcementSector gReinforcementSector; + +//void Wrapper_Function_ReinforcementSector( UINT32 aVal) { gReinforcementSector.Functions(aVal); } +void Wrapper_Setup_ReinforcementSector( UINT32 aVal) { gReinforcementSector.Setup(aVal); } +void Wrapper_Cancel_ReinforcementSector( UINT32 aVal ) { gReinforcementSector.Cancel(); } +/////////////////////////////// Reinforcement Sector Selection //////////////////////////////////////////// + +/////////////////////////////// Reinforcement Number Selection //////////////////////////////////////////// +ReinforcementNumber gReinforcementNumber; + +void Wrapper_Function_ReinforcementNumber( UINT32 aVal) { gReinforcementNumber.Functions(aVal); } +void Wrapper_Setup_ReinforcementNumber( UINT32 aVal) { gReinforcementNumber.Setup(aVal); } +void Wrapper_Cancel_ReinforcementNumber( UINT32 aVal ) { gReinforcementNumber.Cancel(); } +/////////////////////////////// Reinforcement Number Selection //////////////////////////////////////////// + +/////////////////////////////// Soldier Target Selection //////////////////////////////////////////// +SoldierSelection gSoldierSelection; + +void Wrapper_Function_SoldierSelection( UINT32 aVal) { gSoldierSelection.Functions(aVal); } +void Wrapper_Setup_SoldierSelection( UINT32 aVal) { gSoldierSelection.Setup(aVal); } +void Wrapper_Cancel_SoldierSelection( UINT32 aVal ) { gSoldierSelection.Cancel(); } +/////////////////////////////// Soldier Target Selection //////////////////////////////////////////// + + +/////////////////////////////// Trait Selection //////////////////////////////////////////// +void +TraitSelection::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + SetupPopup("TraitSelection"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + // create entries for the sub-menus for each trait + const UINT8 num = 1; + UINT8 traitarray[num]; + traitarray[0] = RADIO_OPERATOR_NT; + for ( int i = 0; i < num; ++i) + { + swprintf( pStr, gzMercSkillTextNew[traitarray[i]] ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Setup_SkillSelection, traitarray[i] ) ); + + // if we cannot perform this skill, grey it out + if ( !HAS_SKILL_TRAIT(pSoldier, traitarray[i]) ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + + GetPopup()->addOption( *pOption ); + } + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_TraitSelection, 0 ) ); + GetPopup()->addOption( *pOption ); + + // grab soldier's x,y screen position + INT16 sX, sY; + GetSoldierScreenPos( pSoldier, &sX, &sY ); + + if( sX < 0 ) sX = 0; + if( sY < 0 ) sY = 0; + + usTraitMenuPosX = sX + 30; + usTraitMenuPosY = sY; + + if ( ( usTraitMenuPosX + 400 ) > SCREEN_WIDTH ) + usTraitMenuPosX = SCREEN_WIDTH - 400; + + if ( ( usTraitMenuPosY + 130 ) > SCREEN_HEIGHT ) + usTraitMenuPosY = SCREEN_HEIGHT - 190; + + SetPos(usTraitMenuPosX, usTraitMenuPosY); +} +/////////////////////////////// Trait Selection //////////////////////////////////////////// + + +/////////////////////////////// Skill Selection //////////////////////////////////////////// +void +SkillSelection::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + if ( HAS_SKILL_TRAIT(pSoldier, aVal) ) + { + SetupPopup("SkillSelection"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + SetTraitToDisplay(aVal); + SetGridNoForTraitDisplay(sTraitsMenuTargetGridNo); + + // this switch isn't really necessary. But dividing the skills into menus for each trait gives a better overview, and looks way better than a huge list + switch ( aVal ) + { + case RADIO_OPERATOR_NT: + { + for(UINT32 uiCounter = SKILLS_RADIO_FIRST; uiCounter <= SKILLS_RADIO_LAST; ++uiCounter) + { + swprintf( pStr, pTraitSkillsMenuStrings[uiCounter] ); + + if ( uiCounter == SKILLS_RADIO_ARTILLERY) + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Setup_ArtillerySector, uiCounter ) ); + else if ( uiCounter == SKILLS_RADIO_CALLREINFORCEMENTS) + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Setup_ReinforcementSector, uiCounter ) ); + else + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_SkillSelection, uiCounter ) ); + + // if we cannot perform this skill, grey it out + if ( !(pSoldier->CanUseSkill(uiCounter, TRUE)) ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + + GetPopup()->addOption( *pOption ); + } + } + break; + + default: + break; + } + + // certain traits have skills whose effects depend on wether eomeone is near to us (squadleader, commissar). We therefore display our radius of effect while this display is open + ToggleTraitRangeView(TRUE); + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_SkillSelection, 0 ) ); + GetPopup()->addOption( *pOption ); + } + + // same y, different x + SetPos(gTraitSelection.GetMaxPosX(), usTraitMenuPosY); + + if ( HAS_SKILL_TRAIT(pSoldier, aVal) ) + { + UINT8 cnt = 0; + switch ( aVal ) + { + case RADIO_OPERATOR_NT: + { + for(UINT32 uiCounter = SKILLS_RADIO_FIRST; uiCounter <= SKILLS_RADIO_LAST; ++uiCounter) + { + SetRegionFastHelpText( &(GetPopup()->MenuRegion[cnt++]), pSoldier->PrintSkillDesc(uiCounter) ); + } + } + break; + } + } +} + +void +SkillSelection::Functions( UINT32 aVal ) +{ + Cancel(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + UINT8 ubID = WhoIsThere2(sTraitsMenuTargetGridNo, 0 ); + + pSoldier->UseSkill(aVal, sTraitsMenuTargetGridNo, ubID); + + Cancel(); + gTraitSelection.Cancel(); +} +/////////////////////////////// Skill Selection //////////////////////////////////////////// + +/////////////////////////////// Artillery Sector Selection //////////////////////////////////////////// +void +ArtillerySector::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + if ( pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY) ) + { + SetupPopup("ArtillerySector"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + // check wether we can call artillery from the 4 adjacent sectors + for (UINT8 i = 0; i < 4; ++i) + { + INT16 loopX = pSoldier->sSectorX; + INT16 loopY = pSoldier->sSectorY; + + if ( i == 0 ) ++loopY; + else if ( i == 1 ) ++loopX; + else if ( i == 2 ) --loopY; + else if ( i == 3 ) --loopX; + + if ( loopX < 1 || loopX >= MAP_WORLD_X - 1 || loopY < 1 || loopY >= MAP_WORLD_Y - 1 ) + continue; + + UINT32 sectornr = (UINT32)SECTOR( loopX, loopY ); + + swprintf( pStr, L"%c%d", loopY + 'A' - 1, loopX ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Setup_ArtilleryTeam, sectornr ) ); + + // grey out if no artillery can be called from this sector + if ( !IsValidArtilleryOrderSector( loopX, loopY, pSoldier->bSectorZ, pSoldier->bTeam ) && !IsValidArtilleryOrderSector( loopX, loopY, pSoldier->bSectorZ, MILITIA_TEAM ) ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + GetPopup()->addOption( *pOption ); + } + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_ArtillerySector, 0 ) ); + GetPopup()->addOption( *pOption ); + } + + // same y, different x + SetPos(gSkillSelection.GetMaxPosX(), usTraitMenuPosY); +} +/////////////////////////////// Artillery Sector Selection //////////////////////////////////////////// + +/////////////////////////////// Artillery Team Selection //////////////////////////////////////////// +void +ArtilleryTeam::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + if ( pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY) && pSoldier->bSectorZ == 0) + { + usSector = aVal; + + SetupPopup("ArtilleryTeam"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + // determine sector coordinates + INT16 sSectorX = SECTORX(usSector); + INT16 sSectorY = SECTORY(usSector); + + // order artillery from militia + swprintf( pStr, L"Militia" ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ArtilleryTeam, MILITIA_TEAM ) ); + + // grey out if no ArtilleryTeam can be called from this sector + if ( !IsValidArtilleryOrderSector( sSectorX, sSectorY, pSoldier->bSectorZ, MILITIA_TEAM ) ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + GetPopup()->addOption( *pOption ); + + // order artillery from our mercs + swprintf( pStr, L"Other Squads" ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ArtilleryTeam, pSoldier->bTeam ) ); + + // grey out if no ArtilleryTeam can be called from this sector + if ( !IsValidArtilleryOrderSector( sSectorX, sSectorY, pSoldier->bSectorZ, pSoldier->bTeam ) ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + GetPopup()->addOption( *pOption ); + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_ArtilleryTeam, 0 ) ); + GetPopup()->addOption( *pOption ); + } + + // same y, different x + SetPos(gArtillerySector.GetMaxPosX(), usTraitMenuPosY); +} + +void +ArtilleryTeam::Functions( UINT32 aVal ) +{ + Cancel(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL || !pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY) ) + return; + + pSoldier->OrderArtilleryStrike(usSector, sTraitsMenuTargetGridNo, (UINT8)(aVal)); + + Cancel(); + gArtillerySector.Cancel(); + gSkillSelection.Cancel(); + gTraitSelection.Cancel(); +} +/////////////////////////////// Artillery Team Selection //////////////////////////////////////////// + +/////////////////////////////// Reinforcement Sector Selection //////////////////////////////////////////// +extern UINT8 CountAllMilitiaInSector(INT16 sMapX, INT16 sMapY); + +void +ReinforcementSector::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + if ( pSoldier->CanUseSkill(aVal) ) + { + SetupPopup("Reinforcements sector"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + // check wether we can call artillery from the 4 adjacent sectors + for (UINT8 i = 0; i < 4; ++i) + { + INT16 loopX = pSoldier->sSectorX; + INT16 loopY = pSoldier->sSectorY; + + if ( i == 0 ) ++loopY; + else if ( i == 1 ) ++loopX; + else if ( i == 2 ) --loopY; + else if ( i == 3 ) --loopX; + + if ( loopX < 1 || loopX >= MAP_WORLD_X - 1 || loopY < 1 || loopY >= MAP_WORLD_Y - 1 ) + continue; + + UINT32 sectornr = (UINT32)SECTOR( loopX, loopY ); + + swprintf( pStr, L"%c%d", loopY + 'A' - 1, loopX ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Setup_ReinforcementNumber, sectornr ) ); + + // grey out if no reinforcements can be called from this sector + if( !CountAllMilitiaInSector( loopX, loopY ) ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + GetPopup()->addOption( *pOption ); + } + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_ReinforcementSector, 0 ) ); + GetPopup()->addOption( *pOption ); + } + + // same y, different x + SetPos(gSkillSelection.GetMaxPosX(), usTraitMenuPosY); +} +/////////////////////////////// Reinforcement Sector Selection //////////////////////////////////////////// + +/////////////////////////////// Reinforcement Number Selection //////////////////////////////////////////// +void +ReinforcementNumber::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + if ( pSoldier->CanUseSkill(SKILLS_RADIO_CALLREINFORCEMENTS) ) + { + usSector = aVal; + + SetupPopup("Reinforcements number submenu"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + UINT8 numberofmilitia = CountAllMilitiaInSector( SECTORX(usSector), SECTORY(usSector) ); + + // 5 militia option + swprintf( pStr, L"5 Militia" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ReinforcementNumber, 5 ) ); + + if ( numberofmilitia < 5 ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + + GetPopup()->addOption( *pOption ); + + // 10 militia option + swprintf( pStr, L"10 Militia" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ReinforcementNumber, 10 ) ); + + if ( numberofmilitia < 10 ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + + GetPopup()->addOption( *pOption ); + + // 15 militia option + swprintf( pStr, L"15 Militia" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ReinforcementNumber, 15 ) ); + + if ( numberofmilitia < 15 ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + + GetPopup()->addOption( *pOption ); + + // 20 militia option + swprintf( pStr, L"20 Militia" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ReinforcementNumber, 20 ) ); + + if ( numberofmilitia < 20 ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + + GetPopup()->addOption( *pOption ); + + // all militia option + swprintf( pStr, L"All Militia" ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_ReinforcementNumber, numberofmilitia ) ); + GetPopup()->addOption( *pOption ); + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_ReinforcementNumber, 0 ) ); + GetPopup()->addOption( *pOption ); + } + + // same y, different x + SetPos(gReinforcementSector.GetMaxPosX(), usTraitMenuPosY); +} + +void +ReinforcementNumber::Functions( UINT32 aVal ) +{ + Cancel(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL || !pSoldier->CanUseSkill(SKILLS_RADIO_CALLREINFORCEMENTS) ) + return; + + pSoldier->RadioCallReinforcements(usSector, aVal); + + Cancel(); + gReinforcementSector.Cancel(); + gSkillSelection.Cancel(); + gTraitSelection.Cancel(); +} +/////////////////////////////// Reinforcement Number Selection //////////////////////////////////////////// + +/////////////////////////////// Soldier Target Selection //////////////////////////////////////////// +void +SoldierSelection::Setup( UINT32 aVal ) +{ + Destroy(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + if ( pSoldier->CanUseSkill(aVal) ) + { + usSkill = aVal; + + SetupPopup("SoldierSelection"); + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + // pretty simple: we find every soldier in a radius around the target position and add him to the list + // loop through all soldiers around + for ( UINT32 cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID ; cnt <= gTacticalStatus.Team[ CIV_TEAM ].bLastID ; ++cnt ) + { + INT32 iRange = GetRangeInCellCoordsFromGridNoDiff( sTraitsMenuTargetGridNo, MercPtrs[ cnt ]->sGridNo ); + + if ( iRange < 100 ) + { + if ( cnt != pSoldier->ubID ) + { + swprintf( pStr, L"%s", MercPtrs[ cnt ]->GetName() ); + + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_SoldierSelection, cnt ) ); + + // grey out if no artillery can be called from this sector + if ( 0 ) + { + // Set this option off. + pOption->setAvail(new popupCallbackFunction( &Popup_OptionOff, NULL )); + } + GetPopup()->addOption( *pOption ); + } + } + } + + // cancel option + swprintf( pStr, L"Cancel" ); + pOption = new POPUP_OPTION(&std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_SoldierSelection, 0 ) ); + GetPopup()->addOption( *pOption ); + } + + // same y, different x + SetPos(gSkillSelection.GetMaxPosX(), usTraitMenuPosY); +} + +void +SoldierSelection::Functions( UINT32 aVal ) +{ + Cancel(); + + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier == NULL ) + return; + + pSoldier->UseSkill(usSkill, sTraitsMenuTargetGridNo, aVal); + + Cancel(); + gSkillSelection.Cancel(); + gTraitSelection.Cancel(); +} +/////////////////////////////// Soldier Target Selection //////////////////////////////////////////// + + +/** + * Flugente: traits menu popup box + * This will create other boxes when needed + */ +void TraitsMenu( INT32 usMapPos ) +{ + sTraitsMenuTargetGridNo = usMapPos; + + gTraitSelection.Setup(0); +} \ No newline at end of file diff --git a/Tactical/SkillMenu.h b/Tactical/SkillMenu.h new file mode 100644 index 00000000..a788448a --- /dev/null +++ b/Tactical/SkillMenu.h @@ -0,0 +1,165 @@ +#ifndef _SKILL_MENU_ +#define _SKILL_MENU_ + +/** + * @file + * @author Flugente (bears-pit.com) + */ + +#include "popup_class.h" + +/** + * @brief A basic class for popup menus + * Use this to create mensu calling other menus, each having various sub-options + * I tried to make this as orderly as possible. It's ugly, but much more clean than the first solution, which was a hardcoded mess. + * You will, however, still need wrapper functions (we use callbacks on member functions), see Implementation. + */ +class SkillMenuItem +{ +public: + SkillMenuItem() : + fInitialized(FALSE), usPosX(0), gPopup(NULL) + { + } + + virtual void Cancel() + { + // Hide the Filter Menu pop-up. + if (gPopup != NULL && fInitialized ) + { + gPopup->hide(); + } + } + + virtual void Destroy() + { + // If we already have a popup, destroy it first. This ensures we get a fresh menu each time. + if ( fInitialized ) + { + delete(gPopup); + gPopup = NULL; + fInitialized = FALSE; + } + } + + UINT16 GetMaxPosX() + { + if ( gPopup ) + return (usPosX + gPopup->getBoxDimensions().iRight); + + return usPosX; + } + + virtual void SetPos(UINT16 usX, UINT16 usY) + { + // same y, different x + usPosX = usX; + gPopup->setPosition( usPosX, usY ); + + fInitialized = TRUE; + gPopup->show(); + } + + void SetupPopup(CHAR* name); + + POPUP* GetPopup() + { + return gPopup; + } + + virtual void Setup( UINT32 aVal ) {} + virtual void Functions( UINT32 aVal ) {} + +private: + BOOLEAN fInitialized; + UINT16 usPosX; + POPUP* gPopup; +}; + +// select a trait +class TraitSelection : public SkillMenuItem +{ +public: + TraitSelection() {} + + void Setup( UINT32 aVal ); +}; + +// select a skill +class SkillSelection : public SkillMenuItem +{ +public: + SkillSelection() : usSector(0) {} + + void Setup( UINT32 aVal ); + void Functions( UINT32 aVal ); + +private: + UINT32 usSector; +}; + +// select sector to call artillery from +class ArtillerySector : public SkillMenuItem +{ +public: + ArtillerySector() {} + + void Setup( UINT32 aVal ); +}; + +// select team we request artillery from +class ArtilleryTeam : public SkillMenuItem +{ +public: + ArtilleryTeam() : usSector(0) {} + + void Setup( UINT32 aVal ); + void Functions( UINT32 aVal ); + +private: + UINT32 usSector; +}; + +// select the sector we call reinforcements from +class ReinforcementSector : public SkillMenuItem +{ +public: + ReinforcementSector() {} + + void Setup( UINT32 aVal ); +}; + +// select the number of milita that should reinforce +class ReinforcementNumber : public SkillMenuItem +{ +public: + ReinforcementNumber() : usSector(0) {} + + void Setup( UINT32 aVal ); + void Functions( UINT32 aVal ); + +private: + UINT32 usSector; +}; + +// select an individual soldier as target of a skill +class SoldierSelection : public SkillMenuItem +{ +public: + SoldierSelection() : usSkill(0) {} + + void Setup( UINT32 aVal ); + void Functions( UINT32 aVal ); + +private: + UINT32 usSkill; +}; + + +/** + * Flugente: traits menu popup box + * This will create other boxes when needed + */ +void TraitsMenu( INT32 usMapPos ); + +#endif \ No newline at end of file diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 9c78f795..2262fb00 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -1022,6 +1022,10 @@ SOLDIERTYPE& SOLDIERTYPE::operator=(const OLDSOLDIERTYPE_101& src) this->usStarveDamageStrength = 0; this->bAIIndex = 0; this->usSoldierProfile = 0; + + this->usAISkillUse = 0; + for (UINT8 i = 0; i < SOLDIER_COUNTER_MAX; ++i) this->usSkillCounter[i] = 0; + for (UINT8 i = 0; i < SOLDIER_COOLDOWN_MAX; ++i) this->usSkillCooldown[i] = 0; } return *this; } @@ -16897,6 +16901,994 @@ void SOLDIERTYPE::SoldierPropertyUpkeep() // remove in a few revisions - the code has already been fixed, so this won't be necessary anymore soon^^ this->bPoisonLife = min(this->bPoisonLife, this->bPoisonSum ); this->bPoisonBleeding = min(this->bPoisonBleeding, this->bBleeding); + + // if we are dead or dying, we cannot continue radio work + if ( this->stats.bLife < OKLIFE ) + this->SwitchOffRadio(); + + // effects eventually run out + for (UINT8 counter = 0; counter < SOLDIER_COUNTER_MAX; ++counter) + { + usSkillCounter[counter] = max(0, usSkillCounter[counter] - 1 ); + } +} + +// check if Soldier can use the spell skillwise, with fAPCheck = TRUE also check current APs +BOOLEAN SOLDIERTYPE::CanUseSkill( INT8 iSkill, BOOLEAN fAPCheck ) +{ + if ( fAPCheck ) + { + if ( this->bCollapsed ) + return FALSE; + } + + BOOLEAN canuse = FALSE; + + switch ( iSkill ) + { + // radio operator + case SKILLS_RADIO_ARTILLERY: + case SKILLS_RADIO_JAM: + case SKILLS_RADIO_SCAN_FOR_JAM: + case SKILLS_RADIO_LISTEN: + case SKILLS_RADIO_CALLREINFORCEMENTS: + if ( (!fAPCheck || EnoughPoints( this, APBPConstants[AP_RADIO], APBPConstants[BP_RADIO], FALSE ) ) && CanUseRadio() ) + canuse = TRUE; + break; + + case SKILLS_RADIO_TURNOFF: + if ( (!fAPCheck || EnoughPoints( this, APBPConstants[AP_RADIO], APBPConstants[BP_RADIO], FALSE ) ) && (IsJamming() || IsScanning() || IsRadioListening()) ) + canuse = TRUE; + break; + + default: + break; + } + + return( canuse ); +} + +// use a skill. For safety reasons, this calls CanUseSkill again +BOOLEAN SOLDIERTYPE::UseSkill( UINT8 iSkill, INT32 usMapPos, UINT8 ID ) +{ + if ( !CanUseSkill(iSkill, TRUE) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Cannot use skill" ); + return FALSE; + } + + switch ( iSkill ) + { + // radio operator + // the call for SKILLS_RADIO_ARTILLERY is only used by the AI + case SKILLS_RADIO_ARTILLERY: + { + UINT32 sector = 0; + if ( this->CanAnyArtilleryStrikeBeOrdered(§or) ) + { + return OrderArtilleryStrike(sector, usMapPos, this->bTeam ); + } + } + break; + + case SKILLS_RADIO_JAM: + return JamCommunications(); + break; + + case SKILLS_RADIO_SCAN_FOR_JAM: + return ScanForJam(); + break; + + case SKILLS_RADIO_LISTEN: + return RadioListen(); + break; + + case SKILLS_RADIO_CALLREINFORCEMENTS: + // called separately + //return RadioCallReinforcements(); + break; + + case SKILLS_RADIO_TURNOFF: + return SwitchOffRadio(); + break; + + default: + break; + } + + return FALSE; +} + +// is the AI allowed to use a skill? we have to check how much breath and life using this skill would cost, as otherwise the AI might commit suicide by casting +BOOLEAN SOLDIERTYPE::IsAIAllowedtoUseSkill( INT8 iSkill ) +{ + if ( CanUseSkill(iSkill, TRUE) ) + { + return TRUE; + } + + return FALSE; +} + +static CHAR16 skilldescarray[500]; + +// print a small description of the skill if we can use it, or its requirements if we cannot +STR16 SOLDIERTYPE::PrintSkillDesc( INT8 iSkill ) +{ + CHAR16 atStr[ 200 ]; + swprintf(skilldescarray, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_REQ] ); + + if ( CanUseSkill(iSkill, TRUE) ) + { + return pTraitSkillsMenuDescStrings[iSkill]; + } + else + { + if ( iSkill >= SKILLS_RADIO_FIRST && iSkill <= SKILLS_RADIO_LAST ) + { + swprintf(atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_AP], APBPConstants[AP_RADIO] ); + wcscat( skilldescarray, atStr ); + } + + switch ( iSkill ) + { + // radio operator + case SKILLS_RADIO_ARTILLERY: + case SKILLS_RADIO_JAM: + case SKILLS_RADIO_SCAN_FOR_JAM: + case SKILLS_RADIO_LISTEN: + case SKILLS_RADIO_CALLREINFORCEMENTS: + case SKILLS_RADIO_TURNOFF: + + swprintf(atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_TXT], gzMercSkillTextNew[RADIO_OPERATOR_NT] ); + wcscat( skilldescarray, atStr ); + + swprintf(atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_TXT], L"a working radio set" ); + wcscat( skilldescarray, atStr ); + + break; + + default: + break; + } + } + + return skilldescarray; +} + +BOOLEAN SOLDIERTYPE::CanUseRadio() +{ + // new inventory system required, as the radio set has to be in a specific slot + if ( !UsingNewInventorySystem() ) + return FALSE; + + // only radio operators can use this equipment + if ( !NUM_SKILL_TRAITS( this, RADIO_OPERATOR_NT ) ) + return FALSE; + + if ( !EnoughPoints( this, APBPConstants[AP_RADIO], 0, FALSE ) ) + return FALSE; + + // only player mercs use new inventory system + if ( this->bTeam == OUR_TEAM ) + { + OBJECTTYPE* pObj = &(inv[CPACKPOCKPOS]); + + if ( pObj && HasItemFlag(pObj->usItem, RADIO_SET) ) + { + //search power pack + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + // do we have a power pack on our armor? + if ( iter->exists() && HasItemFlag(iter->usItem, POWER_PACK) ) + { + // Hack (or clever use of unused memory, depending on your view): + // data.bTemperature has to exist on all items, as it is used on weapons and certain attachments (barrels) + // It isn't used on armor... that's why we can use it now. The idea is that the temperature of the power pack represents its available energy. + // The cooling down represents its energy depleting. + if ( (*iter)[0]->data.bTemperature > 0.0f ) + return( TRUE ); + } + } + } + } + else + { + INT8 invsize = (INT8)inv.size(); // remember inventorysize, so we don't call size() repeatedly + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (inv[bLoop].exists() ) + { + OBJECTTYPE* pObj = &(inv[bLoop]); + + if ( pObj && HasItemFlag(pObj->usItem, RADIO_SET) ) + { + //search power pack + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + // do we have a power pack on our armor? + if ( iter->exists() && HasItemFlag(iter->usItem, POWER_PACK) ) + { + // Hack (or clever use of unused memory, depending on your view): + // data.bTemperature has to exist on all items, as it is used on weapons and certain attachments (barrels) + // It isn't used on armor... that's why we can use it now. The idea is that the temperature of the power pack represents its available energy. + // The cooling down represents its energy depleting. + if ( (*iter)[0]->data.bTemperature > 0.0f ) + return( TRUE ); + } + } + } + } + } + } + + return FALSE; +} + +BOOLEAN SOLDIERTYPE::UseRadio() +{ + BOOLEAN success = FALSE; + + // new inventory system required, as the radio set has to be in a specific slot + if ( UsingNewInventorySystem() ) + { + // only player mercs use new inventory system + if ( this->bTeam == OUR_TEAM ) + { + // check for fail depending on status of radio set + OBJECTTYPE* pObj = &(inv[CPACKPOCKPOS]); + + if ( pObj && HasItemFlag(pObj->usItem, RADIO_SET) ) + { + // status % chance of success + if ( Chance( (*pObj)[0]->data.objectStatus ) ) + success = TRUE; + } + } + else + { + INT8 invsize = (INT8)inv.size(); // remember inventorysize, so we don't call size() repeatedly + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (inv[bLoop].exists() ) + { + OBJECTTYPE* pObj = &(inv[bLoop]); + + if ( pObj && HasItemFlag(pObj->usItem, RADIO_SET) ) + { + // status % chance of success + if ( Chance( (*pObj)[0]->data.objectStatus ) ) + success = TRUE; + + break; + } + } + } + } + } + + // even if we fail, we still use up AP, use animation an use up batteries + DepleteActiveRadioSetEnergy(TRUE); + + if ( this->bInSector && (this->ubBodyType == REGMALE || this->ubBodyType == BIGMALE) ) + { + switch( gAnimControl[ this->usAnimState ].ubEndHeight ) + { + case ANIM_STAND: + this->EVENT_InitNewSoldierAnim( AI_RADIO, 0 , FALSE ); + break; + + case ANIM_CROUCH: + this->EVENT_InitNewSoldierAnim( AI_CR_RADIO, 0 , FALSE ); + break; + } + } + + DeductPoints(this, APBPConstants[AP_RADIO], APBPConstants[BP_RADIO], 0); + + // we gain a bit of experience... - even more if we are the one who began the communication + StatChange( this, EXPERAMT, this->bInSector ? 8 : 4, TRUE ); + StatChange( this, MECHANAMT, 1, TRUE ); + + if ( !success ) + { + RadioFail(); + + return FALSE; + } + + return TRUE; +} + +BOOLEAN SOLDIERTYPE::HasMortar() +{ + UINT16 mortaritem = 0; + + INT8 invsize = (INT8)inv.size(); // remember inventorysize, so we don't call size() repeatedly + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (inv[bLoop].exists() == true && Item[inv[bLoop].usItem].mortar ) + { + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN SOLDIERTYPE::GetSlotOfSignalShellIfMortar(UINT8* pbLoop) +{ + UINT16 mortaritem = 0; + + INT8 invsize = (INT8)inv.size(); // remember inventorysize, so we don't call size() repeatedly + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (inv[bLoop].exists() == true && Item[inv[bLoop].usItem].mortar ) + { + mortaritem = inv[bLoop].usItem; + break; + } + } + + if ( mortaritem ) + { + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (inv[bLoop].exists() == true ) + { + if ( Item[inv[bLoop].usItem].usItemClass == IC_BOMB && HasItemFlag(inv[bLoop].usItem, SIGNAL_SHELL) && ValidLaunchable( inv[bLoop].usItem, mortaritem ) ) + { + (*pbLoop) = bLoop; + return TRUE; + } + + if ( Item[inv[bLoop].usItem].mortar ) + { + OBJECTTYPE* pAttObj = FindAttachmentByClass( &(inv[bLoop]), IC_BOMB ); + + if ( pAttObj ) + { + if ( HasItemFlag(inv[bLoop].usItem, SIGNAL_SHELL) ) + { + (*pbLoop) = bLoop; + return TRUE; + } + } + } + } + } + } + + return FALSE; +} + +BOOLEAN SOLDIERTYPE::CanAnyArtilleryStrikeBeOrdered(UINT32* pSectorID) // can any artillery strikes be ordered by this guy's team from the neighbouring sectors? +{ + if ( this->bSectorZ ) + return FALSE; + + // if we are AI-controlled, we have to wait for our timer to run out + if ( this->bTeam != gbPlayerNum && this->usSkillCounter[SOLDIER_COUNTER_RADIO_ARTILLERY] ) + return FALSE; + + // check wether we can call artillery from the 4 adjacent sectors + for (UINT8 i = 0; i < 4; ++i) + { + INT16 loopX = this->sSectorX; + INT16 loopY = this->sSectorY; + + if ( i == 0 ) ++loopY; + else if ( i == 1 ) ++loopX; + else if ( i == 2 ) --loopY; + else if ( i == 3 ) --loopX; + + if ( loopX < 1 || loopX >= MAP_WORLD_X - 1 || loopY < 1 || loopY >= MAP_WORLD_Y - 1 ) + continue; + + if ( IsValidArtilleryOrderSector( loopX, loopY, this->bSectorZ, this->bTeam ) ) + { + *pSectorID = (UINT32)SECTOR( loopX, loopY ); + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridNo, UINT8 bTeam ) +{ + if ( !CanUseSkill(SKILLS_RADIO_ARTILLERY, TRUE) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Cannot use skill" ); + return FALSE; + } + + // check wether radio frequencies are jammed. Not possible to do this in CanUseSkill(), as CanUseRadio() only checks if we theoretically + if ( SectorJammed() ) + { + // only display message and play sound on our team - no need to signify to player that AI is trying to call in artillery + if ( bTeam == OUR_TEAM || bTeam == MILITIA_TEAM ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Radio frequencies are jammed. No communication possible." ); + + PlayJA2SampleFromFile( "Sounds\\radioerror.wav", RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + } + + return FALSE; + } + + // sector number is in UINT32, even though INT16 would be normal + INT16 sSectorX = SECTORX( (UINT8)usSectorNr ); + INT16 sSectorY = SECTORY( (UINT8)usSectorNr ); + + // just to make sure... + if ( !IsValidArtilleryOrderSector( sSectorX, sSectorY, this->bSectorZ, bTeam ) ) + return FALSE; + + // use the radio, this handles animation, batteries etc. + if ( !UseRadio() ) + return FALSE; + + // determine from where the shells will come + INT32 sStartingGridNo = gMapInformation.sNorthGridNo; + if ( sSectorX < this->sSectorX ) + sStartingGridNo = gMapInformation.sWestGridNo; + else if ( sSectorX > this->sSectorX ) + sStartingGridNo = gMapInformation.sEastGridNo; + else if ( sSectorY > this->sSectorY ) + sStartingGridNo = gMapInformation.sSouthGridNo; + + if (sStartingGridNo == -1) + sStartingGridNo = gMapInformation.sCenterGridNo; + + if ( TileIsOutOfBounds(sStartingGridNo) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Incorrect GridNo for firing Artillery!"); + return FALSE; + } + + // if a strike is ordered from the ENEMY_TEAM or MILITIA_TEAM, the number of mortars depends on the number of enemies/militia in that sector + // number of waves depends on the number and quality of enemies/soldiers + // only HE shells will be fired this way + if ( bTeam == ENEMY_TEAM || bTeam == MILITIA_TEAM ) + { + INT16 nummortars = 0; // number of mortars determines size of wave (1 - 4) + INT16 numwaves = 0; // number of waves + INT16 numshells = 0; // number of shells + + SECTORINFO *pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; + + if ( bTeam == ENEMY_TEAM ) + { + // we also have to account for mobile groups + GROUP *pGroup = gpGroupList; + while( pGroup ) + { + if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) + { + nummortars += pGroup->ubGroupSize; + numshells += gSkillTraitValues.usVOMortarPointsTroop * pGroup->ubGroupSize; + } + pGroup = pGroup->next; + } + + nummortars += pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites; + nummortars /= gSkillTraitValues.usVOMortarCountDivisor; + numshells += gSkillTraitValues.usVOMortarPointsAdmin * pSector->ubNumAdmins + gSkillTraitValues.usVOMortarPointsTroop * pSector->ubNumTroops + gSkillTraitValues.usVOMortarPointsElite * pSector->ubNumElites; + } + else if ( bTeam == MILITIA_TEAM ) + { + nummortars = (SectorInfo[usSectorNr].ubNumberOfCivsAtLevel[ GREEN_MILITIA ] + SectorInfo[usSectorNr].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] + SectorInfo[usSectorNr].ubNumberOfCivsAtLevel[ ELITE_MILITIA ]) / gSkillTraitValues.usVOMortarCountDivisor; + numshells = gSkillTraitValues.usVOMortarPointsAdmin * SectorInfo[usSectorNr].ubNumberOfCivsAtLevel[ GREEN_MILITIA ] + gSkillTraitValues.usVOMortarPointsTroop * SectorInfo[usSectorNr].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] + gSkillTraitValues.usVOMortarPointsElite * SectorInfo[usSectorNr].ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; + } + + if ( gSkillTraitValues.usVOMortarShellDivisor * nummortars < 1 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Not enough mortar shells in sector to start a barrage!"); + return FALSE; + } + + numwaves = numshells / (gSkillTraitValues.usVOMortarShellDivisor * nummortars); + + if ( !numwaves ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Not enough mortar shells in sector to start a barrage!"); + return FALSE; + } + + // send a signal shell at first. This marks the area that the shells will come in + static UINT16 usSignalShellIndex = 1700; + if ( HasItemFlag(usSignalShellIndex, SIGNAL_SHELL) || GetFirstItemWithFlag(&usSignalShellIndex, SIGNAL_SHELL) ) + ArtilleryStrike(usSignalShellIndex, sStartingGridNo, sTargetGridNo); + else + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"No signal shell item found in Items.xml!"); + return FALSE; + } + + // we just 'plant' the mortar shells as bombs. We time them so that they will be fired at the beginning of the next turn + // for every 'wave' of shells, we just plant one and then clone them when firing + // create mortar shell item + OBJECTTYPE shellobj; + CreateItem( 140, 100, &shellobj ); // 140 is mortar HE shell + + shellobj.fFlags |= OBJECT_ARMED_BOMB; + shellobj[0]->data.misc.bDetonatorType = BOMB_TIMED; + shellobj[0]->data.misc.usBombItem = shellobj.usItem; + shellobj[0]->data.misc.ubBombOwner = this->ubID + 2; + + // delay in RT is one turn. In TB we have to make that 2 turns, as otherwise the attack can happen instantly. + // Also use 2 if we are AI, otherwise the shells will fly immediately at the player's turn, giving him no chance to react (blame the way turns are handled) + + shellobj[0]->data.misc.bDelay = 1; + if ( bTeam == ENEMY_TEAM || !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) ) + shellobj[0]->data.misc.bDelay += 1; + + // now set special flags - we simply abuse the ubWireNetworkFlag + switch ( nummortars ) + { + case 1: + shellobj[0]->data.ubWireNetworkFlag = ARTILLERY_STRIKE_COUNT_1; + break; + + case 2: + shellobj[0]->data.ubWireNetworkFlag = ARTILLERY_STRIKE_COUNT_2; + break; + + case 3: + shellobj[0]->data.ubWireNetworkFlag = (ARTILLERY_STRIKE_COUNT_1|ARTILLERY_STRIKE_COUNT_2); + break; + + case 4: + default: + shellobj[0]->data.ubWireNetworkFlag = ARTILLERY_STRIKE_COUNT_4; + break; + } + + for (INT16 i = 0; i < numwaves; ++i) + { + AddItemToPool( sStartingGridNo, &shellobj, HIDDEN_ITEM, 1, WORLD_ITEM_ARMED_BOMB, 0 ); + } + + // update the sector Artillery time + pSector->uiTimeAIArtillerywasOrdered = GetWorldTotalMin(); + + // extra xp for succesfully ordering an artillery strike + StatChange( this, EXPERAMT, 10, TRUE ); + + // we add a bit to the counter, thus the AI has to wait a bit between ordering strikes (otherwise they'll instantly order all available strikes) + this->usSkillCounter[SOLDIER_COUNTER_RADIO_ARTILLERY] = 2; + } + else if ( bTeam == OUR_TEAM ) + { + // if we call a strike from our mercs, everything gets more complicated. We don't calculate the number of mortars or shells as an estimate, we have to search the inventory + // of every merc fit for shelling in that sector for mortars and shells. But thanks to this, we can also other shell-types, like mustard or phosphor + // we already know from IsValidArtilleryOrderSector(..) that someone in there must have a radio set and a mortar, no need to check that again + // sadly, we have to run over this 2 times. On the first run, we have to search for all mortar items and remember them (there can be different mortar systems, can't fire a 40mm shell with a 60mm mortar) + + // as of 2013-09-25, I say it is no longer necessary to fire a signal shell first. The player can fire a signal shell (by mortar or hand) manually to mark one or more targets if he wants + // if he does not do so, active vox operators will be targetted. Who knows, the vox operator might be doing a heroic last stand for all we know... + UINT8 radiooperatorID = 0; + //BOOLEAN signalshellfired = FALSE; + UINT8 mortaritemcnt = 0; + UINT16 mortararray[5]; + for (UINT8 i = 0; i < 5; ++i) + mortararray[i] = 0; + + SOLDIERTYPE* pSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ bTeam ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ bTeam ].bLastID; + for ( pSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pSoldier) + { + // check if soldier exists in this sector + if ( !pSoldier || !pSoldier->bActive || pSoldier->sSectorX != sSectorX || pSoldier->sSectorY != sSectorY || pSoldier->bSectorZ != bSectorZ|| pSoldier->bAssignment > ON_DUTY ) + continue; + + if ( pSoldier->CanUseRadio() ) + radiooperatorID = cnt; + + /*if ( !signalshellfired ) + { + UINT8 bSlot = 0; + if ( pSoldier->GetSlotOfSignalShellIfMortar(&bSlot) ) + { + OBJECTTYPE* pSlotObj = &(pSoldier->inv[bSlot]); + + if ( Item[pSlotObj->usItem].mortar ) + { + pSlotObj = FindAttachmentByClass( &(pSoldier->inv[bSlot]), IC_BOMB ); + + if ( pSlotObj ) + { + ArtilleryStrike(pSlotObj->usItem, sStartingGridNo, sTargetGridNo); + + DeductAmmo( pSoldier, bSlot ); + + signalshellfired = TRUE; + } + } + else if ( HasItemFlag(pSoldier->inv[bSlot].usItem, SIGNAL_SHELL) ) + { + ArtilleryStrike(pSlotObj->usItem, sStartingGridNo, sTargetGridNo); + + pSlotObj->ubNumberOfObjects--; + + if ( !pSlotObj->exists() ) + { + // Delete object + DeleteObj( pSlotObj ); + } + + signalshellfired = TRUE; + } + else + { + // somethings wrong... we were promised either a signal shell or a mortar with one loaded, but there is none... betrayal! + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"No signal shell found even though there should be one, cannot commence barrage!"); + return FALSE; + } + } + }*/ + + INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly + + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (pSoldier->inv[bLoop].exists() == true && Item[pSoldier->inv[bLoop].usItem].mortar ) + { + // if not already in list, remember this mortar + if ( mortararray[0] != pSoldier->inv[bLoop].usItem && + mortararray[1] != pSoldier->inv[bLoop].usItem && + mortararray[2] != pSoldier->inv[bLoop].usItem && + mortararray[3] != pSoldier->inv[bLoop].usItem && + mortararray[4] != pSoldier->inv[bLoop].usItem ) + mortararray[mortaritemcnt++] = pSoldier->inv[bLoop].usItem; + } + + if ( mortaritemcnt >= 5) + break; + } + + if ( mortaritemcnt >= 5) + break; + } + + // safety check, this shouldn't be happening + if ( !mortaritemcnt ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"No mortars found, cannot commence barrage!"); + return FALSE; + } + + // no signal shell -> no barrage + /*if ( !signalshellfired ) + { + if ( radiooperatorID ) + DelayedTacticalCharacterDialogue( MercPtrs[ radiooperatorID ], QUOTE_OUT_OF_AMMO ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"No signal shell object found, cannot commence barrage!"); + return FALSE; + }*/ + + // depending on wether the mortars have ammunition, a radio operator will give a different dialogue + BOOLEAN shellsfired = FALSE; + + // second loop: check for all mortar shells and 'fire' them + cnt = gTacticalStatus.Team[ bTeam ].bFirstID; + for ( pSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pSoldier) + { + // check if soldier exists in this sector + if ( !pSoldier || !pSoldier->bActive || pSoldier->sSectorX != sSectorX || pSoldier->sSectorY != sSectorY || pSoldier->bSectorZ != bSectorZ|| pSoldier->bAssignment > ON_DUTY ) + continue; + + INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly + for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) + { + if (pSoldier->inv[bLoop].exists() == true ) + { + if ( Item[pSoldier->inv[bLoop].usItem].mortar ) + { + OBJECTTYPE* pAttObj = FindAttachmentByClass( &(pSoldier->inv[bLoop]), IC_BOMB ); + + // as of 2013-09-25, also fire these, as they are no longer necessary for a barrage + // only fire if not signal shell, we already fired one, no need to do so again + if ( pAttObj )//&& !HasItemFlag(pAttObj->usItem, SIGNAL_SHELL) ) + { + // create mortar shell item + OBJECTTYPE shellobj; + CreateItem( pAttObj->usItem, 100, &shellobj ); + + // plant bomb data + shellobj.fFlags |= OBJECT_ARMED_BOMB; + shellobj[0]->data.misc.bDetonatorType = BOMB_TIMED; + shellobj[0]->data.misc.usBombItem = shellobj.usItem; + shellobj[0]->data.misc.ubBombOwner = this->ubID + 2; + + // In realtime the player could choose to put down a bomb right before a turn expires, so add 1 to the setting in RT + shellobj[0]->data.misc.bDelay = 1; + if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) ) + shellobj[0]->data.misc.bDelay++; + + shellobj[0]->data.ubWireNetworkFlag = ARTILLERY_STRIKE_COUNT_1; + + AddItemToPool( sStartingGridNo, &shellobj, HIDDEN_ITEM, 1, WORLD_ITEM_ARMED_BOMB, 0 ); + + shellsfired = TRUE; + + DeductAmmo( pSoldier, bLoop ); + } + } + + if ( Item[pSoldier->inv[bLoop].usItem].usItemClass == IC_BOMB ) + { + // found a bomb - if this fits any found mortar, fire it + for ( UINT8 i = 0; i < mortaritemcnt; ++i) + { + if ( ValidLaunchable( pSoldier->inv[bLoop].usItem, mortararray[i] ) ) + { + OBJECTTYPE* pShellObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ... + + // only fire if not signal shell, we already fired one, no need to do so again + if ( pShellObj && !HasItemFlag(pShellObj->usItem, SIGNAL_SHELL) ) + { + // create mortar shell item + OBJECTTYPE shellobj; + CreateItem( pShellObj->usItem, 100, &shellobj ); + + // plant bomb data + shellobj.fFlags |= OBJECT_ARMED_BOMB; + shellobj[0]->data.misc.bDetonatorType = BOMB_TIMED; + shellobj[0]->data.misc.usBombItem = shellobj.usItem; + shellobj[0]->data.misc.ubBombOwner = this->ubID + 2; + + // In realtime the player could choose to put down a bomb right before a turn expires, so add 1 to the setting in RT + shellobj[0]->data.misc.bDelay = 1; + if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) ) + shellobj[0]->data.misc.bDelay++; + + shellobj[0]->data.ubWireNetworkFlag = ARTILLERY_STRIKE_COUNT_1; + + for(INT16 j = 0; j < pShellObj->ubNumberOfObjects; ++j) + { + AddItemToPool( sStartingGridNo, &shellobj, HIDDEN_ITEM, 1, WORLD_ITEM_ARMED_BOMB, 0 ); + + shellsfired = TRUE; + } + + // remove the shells: Delete object + DeleteObj( pShellObj ); + } + } + } + } + } + } + } + + if ( radiooperatorID ) + { + pSoldier = MercPtrs[ radiooperatorID ]; + + // also drain the other guy's radio batteries + pSoldier->UseRadio(); + + if ( shellsfired ) + TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, BATTLE_SOUND_OK2 , 500 ); + else + DelayedTacticalCharacterDialogue( pSoldier, QUOTE_OUT_OF_AMMO ); + } + + if ( shellsfired ) + { + // extra xp for succesfully ordering an artillery strike + StatChange( this, EXPERAMT, 10, TRUE ); + + // we add a bit to the counter, thus the AI has to wait a bit between ordering strikes (otherwise they'll instantly order all available strikes) + this->usSkillCounter[SOLDIER_COUNTER_RADIO_ARTILLERY] = 2; + } + } + else + // how did this even happen? + return FALSE; + + return TRUE; +} + +BOOLEAN SOLDIERTYPE::IsJamming() +{ + return ( (bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_JAMMING) && CanUseRadio() ); +} + +BOOLEAN SOLDIERTYPE::JamCommunications() +{ + // not possible if already jamming + if ( bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_JAMMING ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Already jamming signal, no need to do so again!"); + return FALSE; + } + + // use the radio, this handles animation, batteries etc. + if ( !UseRadio() ) + return FALSE; + + // stop other radio activities + SwitchOffRadio(); + + // add flag + bSoldierFlagMask |= SOLDIER_RADIO_OPERATOR_JAMMING; + + // play sound + PlayJA2SampleFromFile( "Sounds\\radioerror2.wav", RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + + return TRUE; +} + +BOOLEAN SOLDIERTYPE::IsScanning() +{ + return ( (bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_SCANNING) && CanUseRadio() ); +} + +BOOLEAN SOLDIERTYPE::ScanForJam() +{ + // not possible if already scanning + if ( bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_SCANNING ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Already scanning for jam signals, no need to do so again!"); + return FALSE; + } + + // use the radio, this handles animation, batteries etc. + if ( !UseRadio() ) + return FALSE; + + // stop other radio activities + SwitchOffRadio(); + + // add flag + bSoldierFlagMask |= SOLDIER_RADIO_OPERATOR_SCANNING; + + // play sound + PlayJA2SampleFromFile( "Sounds\\scan1.wav", RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + + return TRUE; +} + +BOOLEAN SOLDIERTYPE::IsRadioListening() +{ + return ( (bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_LISTENING) && CanUseRadio() ); +} + +BOOLEAN SOLDIERTYPE::RadioListen() +{ + // not possible if already scanning + if ( bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_LISTENING ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Already listening for nearby sounds, no need to do so again!"); + return FALSE; + } + + // use the radio, this handles animation, batteries etc. + if ( !UseRadio() ) + return FALSE; + + // stop other radio activities + SwitchOffRadio(); + + // add flag + bSoldierFlagMask |= SOLDIER_RADIO_OPERATOR_LISTENING; + + // play sound + PlayJA2SampleFromFile( "Sounds\\scan1.wav", RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + + return TRUE; +} + +// Flugente: order reinforcements from src sector to target sector +extern BOOLEAN CallMilitiaReinforcements( INT16 sTargetMapX, INT16 sTargetMapY, INT16 sSrcMapX, INT16 sSrcMapY, UINT16 sNumber ); + +BOOLEAN SOLDIERTYPE::RadioCallReinforcements( UINT32 usSector, UINT16 sNumber ) +{ + if( !gGameExternalOptions.gfAllowReinforcements ) + return FALSE; + + // use the radio, this handles animation, batteries etc. + if ( !UseRadio() ) + return FALSE; + + // check wether radio frequencies are jammed. Not possible to do this in CanUseSkill(), as CanUseRadio() only checks if we can theoretically order + if ( SectorJammed() ) + { + // error message and sound is handled in UseRadio() + return FALSE; + } + + // Flugente: order reinforcements from src sector to target sector + if ( CallMilitiaReinforcements( this->sSectorX, this->sSectorY, SECTORX( usSector ), SECTORY( usSector ), sNumber ) ) + { + CHAR16 pStr2[128]; + GetSectorIDString( SECTORX( usSector ), SECTORY( usSector ), 0, pStr2, FALSE ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"%s orders reinforcements from %s", this->name, pStr2 ); + + // play sound + PlayJA2SampleFromFile( "Sounds\\scan1.wav", RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + + return TRUE; + } + + return FALSE; +} + +BOOLEAN SOLDIERTYPE::SwitchOffRadio() +{ + // erasing the flags is enough + bSoldierFlagMask &= ~(SOLDIER_RADIO_OPERATOR_JAMMING|SOLDIER_RADIO_OPERATOR_SCANNING|SOLDIER_RADIO_OPERATOR_LISTENING); + + return TRUE; +} + +// display and error sound used either when the radio set fails or the sector is jammed - the player knows of the error, but cannot be sure of the cause +void +SOLDIERTYPE::RadioFail() +{ + // only display message and play sound if on player team + if ( this->bTeam == gbPlayerNum && this->bInSector ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Radio action failed!" ); + + PlayJA2SampleFromFile( "Sounds\\radioerror.wav", RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + } +} + +void SOLDIERTYPE::DepleteActiveRadioSetEnergy(BOOLEAN fActivation, BOOLEAN fAssignment) +{ + if ( !CanUseRadio() ) + return; + + FLOAT cost = 0; + if ( fActivation ) + cost = gItemSettings.energy_cost_radioset_activate; + else if ( fAssignment ) + cost = gItemSettings.energy_cost_radioset_scan_assignment; + else if ( bSoldierFlagMask & SOLDIER_RADIO_OPERATOR_JAMMING ) + cost = gItemSettings.energy_cost_radioset_jam; + else if ( bSoldierFlagMask & (SOLDIER_RADIO_OPERATOR_SCANNING|SOLDIER_RADIO_OPERATOR_LISTENING) ) + cost = gItemSettings.energy_cost_radioset_scan; + else + // nothing to do here.. + return; + + OBJECTTYPE* pObj = &(inv[CPACKPOCKPOS]); + + if ( pObj && HasItemFlag(pObj->usItem, RADIO_SET) ) + { + //search power pack + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + // do we have a power pack on our armor? + if ( iter->exists() && HasItemFlag(iter->usItem, POWER_PACK) ) + { + (*iter)[0]->data.bTemperature = max(0.0f, (*iter)[0]->data.bTemperature - cost); + + if ( (*iter)[0]->data.bTemperature <= 0.0f ) + { + // destroy batteries + iter->RemoveObjectsFromStack(1); + if (iter->exists() == false) + pObj->RemoveAttachment( &(*iter) ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"%s radio set is out of energy.", this->GetName() ); + } + + // there can only be one battery on a radio set + return; + } + } + } } INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) @@ -20004,3 +20996,133 @@ BOOLEAN TwoStagedTrait( UINT8 uiSkillTraitNumber ) return( uiSkillTraitNumber > 0 && (uiSkillTraitNumber <= NUM_ORIGINAL_MAJOR_TRAITS || uiSkillTraitNumber == COVERT_NT) ); } +BOOLEAN GetRadioOperatorSignal(UINT8 usOwner, INT32* psTargetGridNo) +{ + // get the 'real owner' + if ( usOwner > 1 ) + { + // a merc planted this - if he's a radio operator, use his gridno + SOLDIERTYPE* pSoldier = MercPtrs[usOwner - 2]; + + if ( pSoldier && pSoldier->CanUseRadio() && pSoldier->bActive && pSoldier->bInSector && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) ) + { + *psTargetGridNo = pSoldier->sGridNo; + pSoldier->bSide; + return TRUE; + } + } + // check for the side that ordered this + else + { + UINT8 bTeam = MILITIA_TEAM; + if ( usOwner ) + bTeam = ENEMY_TEAM; + + SOLDIERTYPE* pSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ bTeam ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ bTeam ].bLastID; + for ( pSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pSoldier) + { + if ( pSoldier && pSoldier->CanUseRadio() && pSoldier->bActive && pSoldier->bInSector && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) ) + { + *psTargetGridNo = pSoldier->sGridNo; + pSoldier->bSide; + return TRUE; + } + } + } + + return FALSE; +} + +BOOLEAN IsValidArtilleryOrderSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 bTeam ) +{ + // is the sector valid? + if ( bSectorZ > 0 ||sSectorX < 1 || sSectorX >= MAP_WORLD_X - 1 || sSectorY < 1 || sSectorY >= MAP_WORLD_Y - 1 ) + return FALSE; + + BOOLEAN fEnemies = (NumEnemiesInAnySector( sSectorX, sSectorY, bSectorZ ) > 0); + BOOLEAN fMilitia = (GetNumberOfMilitiaInSector( sSectorX, sSectorY, (INT16) bSectorZ ) > 0); + BOOLEAN fMercs = (PlayerMercsInSector( (UINT8) sSectorX, (UINT8) sSectorY, (UINT8) bSectorZ ) > 0); + + SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sSectorX, sSectorY ) ] ); + + // sector must be free of members of an opposing team + if ( bTeam == ENEMY_TEAM ) + { + if ( !fEnemies || fMilitia || fMercs ) + return FALSE; + + // cannot fire if artillery was used recently + if ( GetWorldTotalMin() < pSectorInfo->uiTimeAIArtillerywasOrdered + gSkillTraitValues.bVOArtillerySectorFrequency ) + return FALSE; + } + else if ( bTeam == MILITIA_TEAM ) + { + if ( fEnemies || !fMilitia ) + return FALSE; + + // cannot fire if artillery was used recently + if ( GetWorldTotalMin() < pSectorInfo->uiTimeAIArtillerywasOrdered + gSkillTraitValues.bVOArtillerySectorFrequency ) + return FALSE; + } + else if ( bTeam == OUR_TEAM ) + { + if ( fEnemies || !fMercs ) + return FALSE; + + // we can relay orders only if someone in the sector has a working radio set and a mortar + BOOLEAN activeradio = FALSE; + BOOLEAN mortarfound = FALSE; + SOLDIERTYPE* pSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ bTeam ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ bTeam ].bLastID; + for ( pSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pSoldier) + { + // check if soldier exists in this sector, and is on duty + if ( !pSoldier || !pSoldier->bActive || pSoldier->sSectorX != sSectorX || pSoldier->sSectorY != sSectorY || pSoldier->bSectorZ != bSectorZ || pSoldier->bAssignment > ON_DUTY ) + continue; + + if ( pSoldier->CanUseRadio() ) + activeradio = TRUE; + + if ( pSoldier->HasMortar() ) + mortarfound = TRUE; + } + + if ( !activeradio || !mortarfound ) + return FALSE; + } + + return TRUE; +} + +BOOLEAN SectorJammed() +{ + // check every soldier: are we jamming frequencies? + SOLDIERTYPE* pSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; + INT32 lastid = MAX_NUM_SOLDIERS; + for ( pSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pSoldier) + { + if ( pSoldier->sSectorX == gWorldSectorX && pSoldier->sSectorY == gWorldSectorY && pSoldier->bSectorZ == gbWorldSectorZ && pSoldier->IsJamming() ) + return TRUE; + } + + return FALSE; +} + +BOOLEAN PlayerTeamIsScanning() +{ + // check every soldier: are we jamming frequencies? + SOLDIERTYPE* pSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ OUR_TEAM ].bLastID; + for ( pSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pSoldier) + { + if ( pSoldier->sSectorX == gWorldSectorX && pSoldier->sSectorY == gWorldSectorY && pSoldier->bSectorZ == gbWorldSectorZ && pSoldier->IsScanning() ) + return TRUE; + } + + return FALSE; +} diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 13d4953f..ce513d86 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -353,34 +353,34 @@ enum // -------- added by Flugente: various flags for soldiers -------- // 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 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 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 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 SOLDIER_DAMAGED_VEST 0x00000010 //16 // Soldier's vest is damaged (and thus can't be taken off) -#define SOLDIER_COVERT_NPC_SPECIAL 0x00000020 //32 // Special flag for NPCs when recruited (used for covert stuff) -#define SOLDIER_NEW_VEST 0x00000040 //64 // Soldier is wearing new vest. if having both vest and pants, he can disguise -#define SOLDIER_NEW_PANTS 0x00000080 //128 // Soldier is wearing new pants +#define SOLDIER_DAMAGED_VEST 0x00000010 //16 // Soldier's vest is damaged (and thus can't be taken off) +#define SOLDIER_COVERT_NPC_SPECIAL 0x00000020 //32 // Special flag for NPCs when recruited (used for covert stuff) +#define SOLDIER_NEW_VEST 0x00000040 //64 // Soldier is wearing new vest. if having both vest and pants, he can disguise +#define SOLDIER_NEW_PANTS 0x00000080 //128 // Soldier is wearing new pants -#define SOLDIER_DAMAGED_PANTS 0x00000100 //256 // Soldier's vest is damaged (and thus can't be taken off) -#define SOLDIER_HEADSHOT 0x00000200 //512 // last hit received was a headshot (attack to the head, so knifes/punches also work) -#define SOLDIER_POW 0x00000400 //1024 // we are a prisoner of war -#define SOLDIER_ASSASSIN 0x00000800 //2048 // we are an enemy assassin, and thus we will behave very different from normal enemies +#define SOLDIER_DAMAGED_PANTS 0x00000100 //256 // Soldier's vest is damaged (and thus can't be taken off) +#define SOLDIER_HEADSHOT 0x00000200 //512 // last hit received was a headshot (attack to the head, so knifes/punches also work) +#define SOLDIER_POW 0x00000400 //1024 // we are a prisoner of war +#define SOLDIER_ASSASSIN 0x00000800 //2048 // we are an enemy assassin, and thus we will behave very different from normal enemies -#define SOLDIER_POW_PRISON 0x00001000 //4096 // this guy is a prisoner of war in a prison sector. SOLDIER_POW refers to people we capture, this refers to people we hold captive -#define SOLDIER_EQUIPMENT_DROPPED 0x00002000 //8192 // under certain circumstances, militia can be ordered to drop their gear twice. Thus we set a marker to avoid that. -#define SOLDIER_ACCESSTEAMMEMBER 0x00004000 //16384 // this merc is accessing another team member'S inventory (via abusing the stealing mechanic) -#define SOLDIER_REDOFLASHLIGHT 0x00008000 //32768 // this flag signifies that we somehow interacted with the items in our hands. Thus we have to possible redo lighting from flashlights +#define SOLDIER_POW_PRISON 0x00001000 //4096 // this guy is a prisoner of war in a prison sector. SOLDIER_POW refers to people we capture, this refers to people we hold captive +#define SOLDIER_EQUIPMENT_DROPPED 0x00002000 //8192 // under certain circumstances, militia can be ordered to drop their gear twice. Thus we set a marker to avoid that. +#define SOLDIER_ACCESSTEAMMEMBER 0x00004000 //16384 // this merc is accessing another team member'S inventory (via abusing the stealing mechanic) +#define SOLDIER_REDOFLASHLIGHT 0x00008000 //32768 // this flag signifies that we somehow interacted with the items in our hands. Thus we have to possible redo lighting from flashlights -#define SOLDIER_LIGHT_OWNER 0x00010000 //65536 // we 'own' at least one light source (via flashlights) -#define SOLDIER_AIRDROP_BONUS 0x00020000 //131072 // backgrounds: special AP bonus during the first turn of an airdrop -#define SOLDIER_ASSAULT_BONUS 0x00040000 //262144 // backgrounds: our first turn in an assault -/*#define PLAYER_NET_4_LVL_1 0x00080000 //524288 +#define SOLDIER_LIGHT_OWNER 0x00010000 //65536 // we 'own' at least one light source (via flashlights) +#define SOLDIER_AIRDROP_BONUS 0x00020000 //131072 // backgrounds: special AP bonus during the first turn of an airdrop +#define SOLDIER_ASSAULT_BONUS 0x00040000 //262144 // backgrounds: our first turn in an assault +#define SOLDIER_RADIO_OPERATOR_LISTENING 0x00080000 //524288 // radio operator is listening with his set -#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 SOLDIER_RADIO_OPERATOR_JAMMING 0x00100000 //1048576 // radio operator is jamming frequencies +#define SOLDIER_RADIO_OPERATOR_SCANNING 0x00200000 //2097152 // radio operator is scanning for jammers +/*#define PLAYER_NET_3_LVL_2 0x00400000 //4194304 #define PLAYER_NET_4_LVL_2 0x00800000 //8388608 #define PLAYER_NET_1_LVL_3 0x01000000 //16777216 @@ -423,6 +423,36 @@ enum NUM_MTA, }; +//Flugente skills from traits and other sources +enum{ + // psyker skills + SKILLS_FIRST = 0, + + // radio operator + SKILLS_RADIO_FIRST = SKILLS_FIRST, + SKILLS_RADIO_ARTILLERY = SKILLS_RADIO_FIRST, + SKILLS_RADIO_JAM, + SKILLS_RADIO_SCAN_FOR_JAM, + SKILLS_RADIO_LISTEN, + SKILLS_RADIO_CALLREINFORCEMENTS, + SKILLS_RADIO_TURNOFF, + SKILLS_RADIO_LAST = SKILLS_RADIO_TURNOFF, + + SKILLS_MAX, +}; + +// Flugente: certain skills/traits/taints add effects that require a counter, here are enums for these +enum { + SOLDIER_COUNTER_RADIO_ARTILLERY, // there is actually no need for an artillery timer, but we use one to forbid the same AI guy ordering multiple strikes at once + + SOLDIER_COUNTER_MAX = 20, // enough space for fillers +}; + +// Flugente: certain skills/traits/taints require a cooldown timer +enum { + SOLDIER_COOLDOWN_MAX = 20, // enough space for fillers +}; + // enum of uniform pieces typedef struct { @@ -1297,12 +1327,17 @@ public: UINT16 usSoldierProfile; // Flugente: allow linking to a xml-based profile specifiying name, visuals, traits etc. - // Flugente: sector ID for move items assignemnt + // Flugente: sector ID for move items assignment UINT8 usItemMoveSectorID; + // Flugente: skill stuff + UINT8 usAISkillUse; // this variable allows the AI to remember which skill it wants to use + UINT16 usSkillCounter[SOLDIER_COUNTER_MAX]; // counters used for various skill/trait/taint effects + UINT32 usSkillCooldown[SOLDIER_COOLDOWN_MAX]; // cooldown used for various skill/trait/taint effects + // Flugente: Decrease this filler by 1 for each new UINT8 / BOOLEAN variable, so we can maintain savegame compatibility!! // Note that we also have to account for padding, so you might need to substract more than just the size of the new variables - UINT8 ubFiller[7]; + UINT8 ubFiller[20]; #ifdef JA2UB //ja25 @@ -1610,6 +1645,38 @@ public: void SoldierPropertyUpkeep(); // update functions for various properties (updating counters, resetting flags etc.) + // Flugente: functions for skill usage + // traits can allow use of certain skills + // check if Soldier can use the spell skillwise, with fAPCheck = TRUE also check current APs + BOOLEAN CanUseSkill( INT8 iSkill, BOOLEAN fAPCheck = TRUE ); + + // use a skill. For safety reasons, this calls CanUseSkill again (it is possible to switch the soldier while the menu is open) + BOOLEAN UseSkill( UINT8 iSkill, INT32 usMapPos, UINT8 ID ); + + // is the AI allowed to use a skill? we have to check how much breath and life using this skill would cost, as otherwise the AI might commit suicide by casting + BOOLEAN IsAIAllowedtoUseSkill( INT8 iSkill ); + + // print a small description of the skill if we can use it, or its requirements if we cannot + STR16 PrintSkillDesc( INT8 iSkill); + + // Flugente: functions for the radio operator trait + BOOLEAN CanUseRadio(); // can we use radio, if we even have one? + BOOLEAN UseRadio(); // simply drain the batteries + BOOLEAN HasMortar(); + BOOLEAN GetSlotOfSignalShellIfMortar(UINT8* pbLoop); // if we have a mortar and a fitting signal shell, return the inventory slot it is in + BOOLEAN CanAnyArtilleryStrikeBeOrdered(UINT32* pSectorID); // can any artillery strikes be ordered by this guy's team from the neighbouring sectors? + BOOLEAN OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridNo, UINT8 bTeam ); + BOOLEAN IsJamming(); // are we currently jamming communication frequencies? + BOOLEAN JamCommunications(); + BOOLEAN IsScanning(); + BOOLEAN ScanForJam(); + BOOLEAN IsRadioListening(); + BOOLEAN RadioListen(); + BOOLEAN RadioCallReinforcements( UINT32 usSector, UINT16 sNumber ); + BOOLEAN SwitchOffRadio(); + void RadioFail(); // display and error sound used either when the radio set fails or the sector is jammed - the player knows of the error, but cannot be sure of the cause + void DepleteActiveRadioSetEnergy(BOOLEAN fActivation = FALSE, BOOLEAN fAssignment = FALSE); + ////////////////////////////////////////////////////////////////////////////// }; // SOLDIERTYPE; @@ -1737,7 +1804,12 @@ BOOLEAN ResolvePendingInterrupt( SOLDIERTYPE * pSoldier, UINT8 ubInterruptType ) BOOLEAN AIDecideHipOrShoulderStance( SOLDIERTYPE * pSoldier, INT32 iGridNo ); BOOLEAN DecideAltAnimForBigMerc( SOLDIERTYPE * pSoldier ); -BOOLEAN TwoStagedTrait( UINT8 uiSkillTraitNumber ); // Flugente: determine if this (new) trait has two stages +// added by Flugente +BOOLEAN TwoStagedTrait( UINT8 uiSkillTraitNumber ); // determine if this (new) trait has two stages +BOOLEAN GetRadioOperatorSignal(UINT8 usOwner, INT32* psTargetGridNo); // retrieve the gridno of a radio operator who (or whose team) ordered an artillery strike +BOOLEAN IsValidArtilleryOrderSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 bTeam ); // can an artillery strike be ordered FROM this sector +BOOLEAN SectorJammed(); +BOOLEAN PlayerTeamIsScanning(); //typedef struct class OLDSOLDIERTYPE_101 diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 4b1d1dc9..dfd42937 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -4630,6 +4630,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre BOOLEAN foundThrowing = FALSE; BOOLEAN foundHtH = FALSE; BOOLEAN foundMelee = FALSE; + BOOLEAN fRadioSetFound = FALSE; // FIRST FIND OUT THE COMPOSITION OF OUR GEAR for (bLoop = 0; bLoop < (INT8) pSoldier->inv.size(); bLoop++) @@ -4653,6 +4654,8 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre else if (Item[pCreateStruct->Inv[bLoop].usItem].usItemClass == 128 && // 128 is an identifier of blunt melee weapons Item[pCreateStruct->Inv[bLoop].usItem].uiIndex != 0 ) foundMelee = TRUE; + else if ( HasItemFlag(pCreateStruct->Inv[bLoop].usItem, RADIO_SET) ) + fRadioSetFound = TRUE; } } @@ -5191,6 +5194,31 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre } } } + + // Flugente: new traits + if ( gGameOptions.fNewTraitSystem && (!ATraitAssigned || !BTraitAssigned || !CTraitAssigned ) ) + { + // if we have a radio set, give us the corresponding trait so we can use it... + if ( fRadioSetFound ) + { + if ( !ATraitAssigned ) + { + pSoldier->stats.ubSkillTraits[0] = RADIO_OPERATOR_NT; + ATraitAssigned = TRUE; + } + else if ( !BTraitAssigned ) + { + pSoldier->stats.ubSkillTraits[1] = RADIO_OPERATOR_NT; + BTraitAssigned = TRUE; + } + else if ( ubSolClass == SOLDIER_CLASS_ELITE || ubSolClass == SOLDIER_CLASS_ELITE_MILITIA ) + { + pSoldier->stats.ubSkillTraits[2] = RADIO_OPERATOR_NT; + CTraitAssigned = TRUE; + return( TRUE ); // We no longer need to continue from here + } + } + } } // RETURN TRUE IF ALL TRAITS ASSIGNED OTHERWISE FALSE if (( !gGameOptions.fNewTraitSystem && ATraitAssigned && BTraitAssigned ) || ( gGameOptions.fNewTraitSystem && ATraitAssigned && BTraitAssigned && CTraitAssigned ) ) diff --git a/Tactical/Tactical_VS2005.vcproj b/Tactical/Tactical_VS2005.vcproj index 1d16e069..1670d318 100644 --- a/Tactical/Tactical_VS2005.vcproj +++ b/Tactical/Tactical_VS2005.vcproj @@ -578,6 +578,10 @@ RelativePath=".\SkillCheck.h" > + + @@ -904,6 +908,10 @@ RelativePath=".\SkillCheck.cpp" > + + diff --git a/Tactical/Tactical_VS2008.vcproj b/Tactical/Tactical_VS2008.vcproj index 9b06a448..6540d9fb 100644 --- a/Tactical/Tactical_VS2008.vcproj +++ b/Tactical/Tactical_VS2008.vcproj @@ -582,6 +582,10 @@ RelativePath="SkillCheck.h" > + + @@ -906,6 +910,10 @@ RelativePath="SkillCheck.cpp" > + + diff --git a/Tactical/Tactical_VS2010.vcxproj b/Tactical/Tactical_VS2010.vcxproj index f1b5e036..453e0707 100644 --- a/Tactical/Tactical_VS2010.vcxproj +++ b/Tactical/Tactical_VS2010.vcxproj @@ -83,6 +83,7 @@ + @@ -165,6 +166,7 @@ + diff --git a/Tactical/Tactical_VS2010.vcxproj.filters b/Tactical/Tactical_VS2010.vcxproj.filters index 350abdfb..239aca0c 100644 --- a/Tactical/Tactical_VS2010.vcxproj.filters +++ b/Tactical/Tactical_VS2010.vcxproj.filters @@ -264,6 +264,9 @@ Header Files + + Header Files + @@ -647,5 +650,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index dc89afb4..6a45c11a 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -114,7 +114,8 @@ #include "fresh_header.h" -#include "IMP Skill Trait.h" // added by Flugente +#include "IMP Skill Trait.h" // added by Flugente +#include "SkillMenu.h" // added by Flugente //forward declarations of common classes to eliminate includes class OBJECTTYPE; @@ -2495,7 +2496,15 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) break; case '$': - ChangeCurrentSquad( 13 ); + { + // Flugente: trait skill selection menu. Yes, screw squad 13 + INT32 usMapPos; + if ( GetMouseMapPos( &usMapPos ) ) + { + TraitsMenu(usMapPos); + } + } + //ChangeCurrentSquad( 13 ); break; case '%': diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 5108ea41..c5c268f5 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -13019,4 +13019,85 @@ FLOAT CalcNewChanceToHitAimTraitBonus(SOLDIERTYPE *pSoldier, FLOAT fAimCap, FLOA } return fAimChance; +} + +extern BOOLEAN IsRoofPresentAtGridNo( INT32 sGridNo ); + +// Flugente: fire item from A to B (intended for mortarshells and launchable grenades) +BOOLEAN ArtilleryStrike( UINT16 usItem, UINT32 usStartingGridNo, UINT32 usTargetMapPos ) +{ + FLOAT dForce, dDegrees; + INT16 sDestX, sDestY, sSrcX, sSrcY; + vector_3 vForce, vDirNormal; + INT16 sStartZ = ( 1 * 256 ) + 50; + INT16 sEndZ = 0; + + // radius is way smaller for signal shells, otherwise the inaccuraccy would increase too much + INT8 bRadius = gSkillTraitValues.usVOMortarRadius; + if ( HasItemFlag(usItem, SIGNAL_SHELL) ) + bRadius = gSkillTraitValues.usVOMortarSignalShellRadius; + + INT32 sTargetGridNo = GetArtilleryTargetGridNo( usTargetMapPos, bRadius ); + + if ( TileIsOutOfBounds(sTargetGridNo) ) + return FALSE; + + UINT8 ubLevel = 0; + if ( IsRoofPresentAtGridNo( sTargetGridNo ) ) + ubLevel = 1; + + // create shell + OBJECTTYPE shellobj; + CreateItem( usItem, 100, &shellobj ); + + // Get basic launch params... + if ( !GetArtilleryLaunchParams(usStartingGridNo, sTargetGridNo, sStartZ, sEndZ, usItem, &shellobj, &dForce, &dDegrees) ) + return FALSE; + + // Get XY from gridno + ConvertGridNoToCenterCellXY( sTargetGridNo, &sDestX, &sDestY ); + ConvertGridNoToCenterCellXY( usStartingGridNo, &sSrcX, &sSrcY ); + + // OK, get direction normal + vDirNormal.x = (float)(sDestX - sSrcX); + vDirNormal.y = (float)(sDestY - sSrcY); + vDirNormal.z = 0; + + // NOmralize + vDirNormal = VGetNormal( &vDirNormal ); + + // From degrees, calculate Z portion of normal + vDirNormal.z = (float)sin( dDegrees ); + + // Do force.... + vForce.x = dForce * vDirNormal.x; + vForce.y = dForce * vDirNormal.y; + vForce.z = dForce * vDirNormal.z; + + float dX = (float)sSrcX; + float dY = (float)sSrcY; + float dZ = (float)sStartZ; + float dForceX = vForce.x; + float dForceY = vForce.y; + float dForceZ = vForce.z; + + if ( Weapon[ usItem ].sSound != NO_WEAPON_SOUND ) + { + PlayJA2Sample( Weapon[ usItem ].sSound, RATE_11025, SoundVolume( HIGHVOLUME, usStartingGridNo ), 1, SoundDir( usStartingGridNo ) ); + } + + INT32 iID = CreatePhysicalObject( &shellobj, -1, dX, dY, dZ, dForceX, dForceY, dForceZ, 0, THROW_ARM_ITEM, 0, FALSE ); + + // OJW - 20091002 - Explosives + /*if (is_networked && is_client) + { + if (pSoldier->bTeam == 0 || (pSoldier->bTeam == 1 && is_server)) + { + send_grenade( &shellobj , dLifeSpan,dX, dY, dZ, dForceX, dForceY, dForceZ, sTargetGridNo, pSoldier->ubID, ubActionCode, uiActionData, iID , false); + } + }*/ + + //REAL_OBJECT* pObject = &( ObjectSlots[ iID ] ); + + return TRUE; } \ No newline at end of file diff --git a/Tactical/Weapons.h b/Tactical/Weapons.h index a57dd034..52308a6a 100644 --- a/Tactical/Weapons.h +++ b/Tactical/Weapons.h @@ -283,6 +283,7 @@ enum EXPLOSV_CREATUREGAS, EXPLOSV_BURNABLEGAS, EXPLOSV_FLASHBANG, + EXPLOSV_SIGNAL_SMOKE, }; #define AMMO_DAMAGE_ADJUSTMENT_BUCKSHOT( x ) (x / 4) @@ -539,4 +540,6 @@ FLOAT GetOverheatJamThreshold( OBJECTTYPE *pObj ); FLOAT GetOverheatDamageThresholdModifier( OBJECTTYPE *pObj ); FLOAT GetOverheatDamageThreshold( OBJECTTYPE *pObj ); +BOOLEAN ArtilleryStrike( UINT16 usItem, UINT32 usStartingGridNo, UINT32 usTargetMapPos ); + #endif diff --git a/Tactical/XML_Explosive.cpp b/Tactical/XML_Explosive.cpp index 12c3a0a1..ff097431 100644 --- a/Tactical/XML_Explosive.cpp +++ b/Tactical/XML_Explosive.cpp @@ -136,6 +136,9 @@ explosiveEndElementHandle(void *userData, const XML_Char *name) { pData->curElement = ELEMENT; pData->curExplosive.ubRadius = (UINT8) atol(pData->szCharData); + + // Flugente: variable is UINT16, but is always used as UINT8, so better keep it in line + pData->curExplosive.ubRadius = max( 0, min(255, pData->curExplosive.ubRadius)); } else if(strcmp(name, "ubVolume") == 0) { diff --git a/Tactical/soldier profile type.h b/Tactical/soldier profile type.h index a3b85346..935eda3f 100644 --- a/Tactical/soldier profile type.h +++ b/Tactical/soldier profile type.h @@ -161,19 +161,20 @@ typedef enum // 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 + RADIO_OPERATOR_NT, //21 // a minor trait NUM_SKILLTRAITS_NT } SkillTraitNew; #define NUM_MAJOR_TRAITS 10 -#define NUM_MINOR_TRAITS 10 +#define NUM_MINOR_TRAITS 11 #define NUM_ORIGINAL_MAJOR_TRAITS 9 // 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 +#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/AIInternals.h b/TacticalAI/AIInternals.h index bfed7605..38ebd7f7 100644 --- a/TacticalAI/AIInternals.h +++ b/TacticalAI/AIInternals.h @@ -298,3 +298,24 @@ FLOAT AICalcRecoilForShot( SOLDIERTYPE *pSoldier, OBJECTTYPE *pWeapon, UINT8 ubS // HEADROCK HAM 4: Calculate average recoil offset for a shot. FLOAT AICalcRecoilForShot( SOLDIERTYPE *pSoldier, OBJECTTYPE *pWeapon, UINT8 ubShotNum); + +// Flugente AI functions +typedef BOOLEAN (*SOLDIER_CONDITION)( SOLDIERTYPE *pSoldier ); + +// determine a gridno that would allow us to hit as many enemies as possible given an effect with radius aRadius tiles +// return true if sufficent gridno is found +// pGridNo will be the GridNo +// aRadius is the area effect radius to use +// uCheckFriends: 0 - do not consider friends at all 1 - consider with negative weight else: ignore any location that might also hit friends +// sucess only if at a rating of at least aMinRating can be achieved +// any enemy soldiers not fulfilling cond will be excluded from this calculation +// if an enemy soldier fulfils taboo, make sure to not hit him at all! +BOOLEAN GetBestAoEGridNo(SOLDIERTYPE *pSoldier, INT32* pGridNo, INT16 aRadius, UINT8 uCheckFriends, UINT8 aMinRating, SOLDIER_CONDITION cond, SOLDIER_CONDITION taboo); + +// Get the ID of the farthest opponent we can see, with an optional minimum range +// puID - ID of the farthest opponent pSoldier can see +// sRange - only return true and give an idea if opponent found is further away than this +BOOLEAN GetFarthestOpponent(SOLDIERTYPE *pSoldier, UINT8* puID, INT16 sRange); + +// are there more allies than friends in adjacent sectors? +BOOLEAN MoreFriendsThanEnemiesinNearbysectors(UINT8 ausTeam, INT16 aX, INT16 aY, INT8 aZ); \ No newline at end of file diff --git a/TacticalAI/AIMain.cpp b/TacticalAI/AIMain.cpp index 86fae854..1551a974 100644 --- a/TacticalAI/AIMain.cpp +++ b/TacticalAI/AIMain.cpp @@ -2286,6 +2286,15 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier) } break; + case AI_ACTION_USE_SKILL: + { + UINT8 ubID = WhoIsThere2( pSoldier->aiData.usActionData, 0 ); + + pSoldier->UseSkill(pSoldier->usAISkillUse, pSoldier->aiData.usActionData, ubID); + ActionDone( pSoldier ); + } + break; + default: #ifdef BETAVERSION NumMessage("ExecuteAction - Illegal action type = ",pSoldier->aiData.bAction); @@ -2365,6 +2374,10 @@ void ATTACKTYPE::InitAttackType(ATTACKTYPE *pAttack)//dnl ch69 140913 void HandleInitialRedAlert( INT8 bTeam, UINT8 ubCommunicate) { + // Flugente radio operator: if the sector is jammed, no radio communication possible + if ( SectorJammed() ) + return; + /* if (ubCommunicate) { diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 742478c6..b10dbd88 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -589,6 +589,10 @@ BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier) bMinPointsNeeded = APBPConstants[AP_HANDCUFF]; break; + case AI_ACTION_USE_SKILL: + bMinPointsNeeded = 10; // TODO + break; + default: #ifdef BETAVERSION //NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction); @@ -1909,9 +1913,8 @@ BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo ) BOOLEAN InGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { - // smoke - if (gpWorldLevelData[sGridNo].ubExtFlags[ pSoldier->pathing.bLevel ] & MAPELEMENT_EXT_SMOKE) + if (gpWorldLevelData[sGridNo].ubExtFlags[ pSoldier->pathing.bLevel ] & (MAPELEMENT_EXT_SMOKE|MAPELEMENT_EXT_SIGNAL_SMOKE) ) return TRUE; return InGas(pSoldier,sGridNo); diff --git a/TacticalAI/Attacks.cpp b/TacticalAI/Attacks.cpp index 6dfa3bfc..dccc6675 100644 --- a/TacticalAI/Attacks.cpp +++ b/TacticalAI/Attacks.cpp @@ -24,6 +24,9 @@ #include "message.h" #include "Vehicles.h" #include "Soldier Functions.h"//dnl ch69 140913 +#include "Reinforcement.h" // added by Flugente +#include "Town Militia.h" // added by Flugente +#include "Queen Command.h" // added by Flugente #endif extern INT16 DirIncrementer[8]; @@ -3191,3 +3194,284 @@ UINT8 UnderFire::Chance(INT8 bTeam) } return(cth); } + +// Flugente AI functions +// determine a gridno that would allow us to hit as many enemies as possible given an effect with radius aRadius tiles +// return true if sufficent gridno is found +// pGridNo will be the GridNo +// aRadius is the area effect radius to use +// uCheckFriends: 0 - do not consider friends at all 1 - consider with negative weight else: ignore any location that might also hit friends +// sucess only if at a rating of at least aMinRating can be achieved +// any enemy soldiers not fulfilling cond will be excluded from this calculation +// if an enemy soldier fulfils taboo, make sure to not hit him at all! +BOOLEAN GetBestAoEGridNo(SOLDIERTYPE *pSoldier, INT32* pGridNo, INT16 aRadius, UINT8 uCheckFriends, UINT8 aMinRating, SOLDIER_CONDITION cond, SOLDIER_CONDITION taboo) +{ + UINT8 ubLoop, ubLoop2; + INT32 sGridNo, sFriendTile[MAXMERCS], sOpponentTile[MAXMERCS], sTabooTile[MAXMERCS]; + UINT8 ubFriendCnt = 0,ubOpponentCnt = 0, ubTabooCnt = 0, ubOpponentID[MAXMERCS]; + INT32 bMaxLeft,bMaxRight,bMaxUp,bMaxDown, i, j; + INT8 bPersOL, bPublOL; + SOLDIERTYPE *pFriend; + static INT16 sExcludeTile[100]; // This array is for storing tiles that we have + UINT8 ubNumExcludedTiles = 0; // already considered, to prevent duplication of effort + + INT32 lowestX = 999999; + INT32 highestX = 0; + INT32 lowestY = 999999; + INT32 highestY = 0; + + // make lists of enemies and friends + for (ubLoop = 0; ubLoop < guiNumMercSlots; ++ubLoop) + { + pFriend = MercSlots[ubLoop]; + + if ( !pFriend || !pFriend->bActive || !pFriend->bInSector ) + continue; + + if (pFriend->stats.bLife == 0) + continue; + + // dying or captured friends are 'helpless' anyway, we are willing to sacrifice them :-) + if ( uCheckFriends && pSoldier->bSide == pFriend->bSide && pFriend->stats.bLife > OKLIFE && !(pFriend->bSoldierFlagMask & SOLDIER_POW) ) + { + // active friend, remember where he is so that we DON'T blow him up! + // this includes US, since we don't want to blow OURSELVES up either + sFriendTile[ubFriendCnt] = pFriend->sGridNo; + ubFriendCnt++; + } + else + { + // if an enemy fulfills taboo, we will remember his tile and be careful not to ever hit it! + if ( taboo(pFriend) ) + { + sTabooTile[ubTabooCnt] = pFriend->sGridNo; + ++ubTabooCnt; + continue; + } + + // Special stuff for Carmen the bounty hunter + if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pFriend->ubProfile != 64) + continue; + + // check wether this guy fulfills the target condition + if ( !cond(pFriend) ) + continue; + + bPersOL = pSoldier->aiData.bOppList[pFriend->ubID]; + bPublOL = gbPublicOpplist[pSoldier->bTeam][pFriend->ubID]; + + if ( bPersOL == SEEN_CURRENTLY || bPublOL == SEEN_CURRENTLY ) + { + // active KNOWN opponent, remember where he is so that we DO blow him up! + sOpponentTile[ubOpponentCnt] = pFriend->sGridNo; + } + else if ( bPersOL == SEEN_LAST_TURN || bPersOL == HEARD_LAST_TURN ) + { + // cheat; only allow throw if person is REALLY within 2 tiles of where last seen + if ( SpacesAway( pFriend->sGridNo, gsLastKnownOppLoc[ pSoldier->ubID ][ pFriend->ubID ] ) < 3 ) + { + sOpponentTile[ubOpponentCnt] = gsLastKnownOppLoc[ pSoldier->ubID ][ pFriend->ubID ]; + } + } + else + { + continue; + } + + // also remember who he is (which soldier #) + ubOpponentID[ubOpponentCnt] = pFriend->ubID; + + // update lowest and highest x and y values + lowestX = min(lowestX, sOpponentTile[ubOpponentCnt] % MAXCOL ); + highestX = max(highestX, sOpponentTile[ubOpponentCnt] % MAXCOL ); + lowestY = min(lowestY, sOpponentTile[ubOpponentCnt] / MAXCOL ); + highestY = max(highestY, sOpponentTile[ubOpponentCnt] / MAXCOL ); + + ubOpponentCnt++; + } + } + + // no/not enough enemies found -> no area effect location advisable + if ( !ubOpponentCnt || ubOpponentCnt < aMinRating ) + return FALSE; + + BOOLEAN fGridNoFound = FALSE; + INT32 bestGridNo = -1; + INT8 bestGridNoCnt = aMinRating; + + INT32 currentSoldierGridNo = -1; + + INT8 enemiesnear = 0; + INT8 friendsnear = 0; + + // look at the squares near each known opponent and try to find the one + // place where a tossed projectile would do the most harm to the opponents + // while avoiding one's friends + for (ubLoop = 0; ubLoop < ubOpponentCnt; ++ubLoop) + { + currentSoldierGridNo = sOpponentTile[ubLoop]; + + // determine maximum horizontal limits + bMaxLeft = max(currentSoldierGridNo % MAXCOL - aRadius, lowestX); + bMaxRight = min(currentSoldierGridNo % MAXCOL + aRadius, highestX); + + // determine maximum vertical limits + bMaxDown = max(currentSoldierGridNo / MAXCOL - aRadius, lowestY); + bMaxUp = min(currentSoldierGridNo / MAXCOL + aRadius, highestY); + + // evaluate every tile for its opponent-damaging potential + for (i = bMaxLeft; i <= bMaxRight; ++i) + { + for (j = bMaxDown; j <= bMaxUp; ++j) + { + // calculate the next potential gridno near this opponent + sGridNo = i + (MAXCOL * j); + + // this shouldn't ever happen + if ((sGridNo < 0) || (sGridNo >= GRIDSIZE)) + continue; + + if ( PythSpacesAway( currentSoldierGridNo, sGridNo ) > aRadius ) + continue; + + // if this tile is taboo, don't even think about targetting it! + for (ubLoop2 = 0; ubLoop2 < ubTabooCnt; ++ubLoop2) + { + if (sTabooTile[ubLoop2] == sGridNo) + continue; + } + + // Check to see if we have considered this tile before: + for (ubLoop2 = 0; ubLoop2 < ubNumExcludedTiles; ++ubLoop2) + { + if (sExcludeTile[ubLoop2] == sGridNo) + continue; + } + + // add this tile to the list of alreay checked tiles + if ( ubNumExcludedTiles < 100 ) + { + sExcludeTile[ubNumExcludedTiles] = sGridNo; + ++ubNumExcludedTiles; + } + + // loop over all enemies and friends to determine how many are in range + enemiesnear = 0; + friendsnear = 0; + + // check whether there are any friends near this gridno + for (ubLoop2 = 0; ubLoop2 < ubFriendCnt; ++ubLoop2) + { + if ( PythSpacesAway(sFriendTile[ubLoop2], sGridNo) <= aRadius ) + ++friendsnear; + } + + // ignore this location if friends are found and we want to absolutely ignore friendly fire + if ( friendsnear && uCheckFriends > 1 ) + continue; + + // check whether there are any enemies near this gridno + for (ubLoop2 = 0; ubLoop2 < ubOpponentCnt; ++ubLoop2) + { + if ( PythSpacesAway(sOpponentTile[ubLoop2], sGridNo) <= aRadius ) + { + ++enemiesnear; + } + } + + if ( enemiesnear - friendsnear >= bestGridNoCnt ) + { + bestGridNoCnt = enemiesnear - friendsnear; + + bestGridNo = sGridNo; + fGridNoFound = TRUE; + } + } + } + } + + *pGridNo = bestGridNo; + + return fGridNoFound; +} + +// Get the ID of the farthest opponent we can see, with an optional minimum range +// puID - ID of the farthest opponent pSoldier can see +// sRange - only return an true and give an idea if opponent found is further away than this +BOOLEAN GetFarthestOpponent(SOLDIERTYPE *pSoldier, UINT8* puID, INT16 sRange) +{ + INT32 sGridNo; + UINT32 uiLoop; + INT32 iRange = 0;; + INT8 *pbPersOL; + SOLDIERTYPE * pOpp; + BOOLEAN found = FALSE; + + *puID = NOBODY; + + // look through this man's personal & public opplists for opponents known + for (uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop) + { + pOpp = MercSlots[ uiLoop ]; + + // if this merc is inactive, at base, on assignment, or dead + if (!pOpp) + { + continue; // next merc + } + + // if this merc is neutral/on same side, he's not an opponent + if ( CONSIDERED_NEUTRAL( pSoldier, pOpp ) || (pSoldier->bSide == pOpp->bSide)) + { + continue; // next merc + } + + // Special stuff for Carmen the bounty hunter + if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpp->ubProfile != 64) + { + continue; // next opponent + } + + pbPersOL = pSoldier->aiData.bOppList + pOpp->ubID; + + // if this opponent is not seen personally + if (*pbPersOL != SEEN_CURRENTLY) + { + continue; // next merc + } + + // since we're dealing with seen people, use exact gridnos + sGridNo = pOpp->sGridNo; + + // if we are standing at that gridno(!, obviously our info is old...) + if (sGridNo == pSoldier->sGridNo) + { + continue; // next merc + } + + // I hope this will be good enough; otherwise we need a fractional/world-units-based 2D distance function + //sRange = PythSpacesAway( pSoldier->sGridNo, sGridNo); + iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ); + + if (iRange > sRange) + { + sRange = iRange; + *puID = uiLoop; + found = TRUE; + } + } + + return( found ); +} + +// are there more allies than friends in adjacent sectors? +BOOLEAN MoreFriendsThanEnemiesinNearbysectors(UINT8 ausTeam, INT16 aX, INT16 aY, INT8 aZ) +{ + UINT16 enemyteam = NumEnemiesInFiveSectors(aX, aY) - NumEnemiesInAnySector(aX, aY, aZ); + UINT16 militiateam = CountAllMilitiaInFiveSectors(aX, aY) - CountAllMilitiaInSector(aX, aY); + + if ( ausTeam == ENEMY_TEAM ) + return (enemyteam > militiateam); + + return (militiateam > enemyteam); +} \ No newline at end of file diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 840c7ef5..f7b35dbe 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -2609,6 +2609,43 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) return(AI_ACTION_NONE); } } + + // Flugente: trait skills + // if we are a radio operator + if ( HAS_SKILL_TRAIT( pSoldier, RADIO_OPERATOR_NT ) > 0 && pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY, TRUE) ) + { + UINT32 tmp; + INT32 skilltargetgridno = 0; + + // call reinforcements if we haven't yet done so + if ( !gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition && MoreFriendsThanEnemiesinNearbysectors(pSoldier->bTeam, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ) ) + { + // if frequencies are jammed... + if ( SectorJammed() ) + { + // if we are jamming, turn it off, otherwise, bad luck... + if ( pSoldier->IsJamming() ) + { + pSoldier->usAISkillUse = SKILLS_RADIO_TURNOFF; + pSoldier->aiData.usActionData = skilltargetgridno; + return(AI_ACTION_USE_SKILL); + } + } + // frequencies are clear, lets call for help + else + { + // raise alarm! + return( AI_ACTION_RED_ALERT ); + } + } + // if we can't call in artillery, jam frequencies, so that the palyer can't use radio skills + else if ( !pSoldier->IsJamming() && !pSoldier->CanAnyArtilleryStrikeBeOrdered(&tmp) ) + { + pSoldier->usAISkillUse = SKILLS_RADIO_JAM; + pSoldier->aiData.usActionData = skilltargetgridno; + return(AI_ACTION_USE_SKILL); + } + } } //RELOADING @@ -3990,6 +4027,10 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) return(AI_ACTION_NONE); } +// Flugente: dummies if we do not want to check for any conditions or taboos +BOOLEAN SoldierCondTrue(SOLDIERTYPE *pSoldier) { return TRUE; } +BOOLEAN SoldierCondFalse(SOLDIERTYPE *pSoldier) { return FALSE; } + INT8 DecideActionBlack(SOLDIERTYPE *pSoldier) { INT32 iCoverPercentBetter, iOffense, iDefense, iChance; @@ -4363,6 +4404,64 @@ INT16 ubMinAPCost; } } + // Flugente: trait skills + // if we are a radio operator + if ( HAS_SKILL_TRAIT( pSoldier, RADIO_OPERATOR_NT ) > 0 && pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY, TRUE) ) + { + // check: would it be possible to call in artillery from neighbouring sectors? + UINT32 tmp; + INT32 skilltargetgridno = 0; + // can we call in artillery? + if ( pSoldier->CanAnyArtilleryStrikeBeOrdered(&tmp) ) + { + // if frequencies are jammed... + if ( SectorJammed() ) + { + // if we are jamming, turn it off, otherwise, bad luck... + if ( pSoldier->IsJamming() ) + { + pSoldier->usAISkillUse = SKILLS_RADIO_TURNOFF; + pSoldier->aiData.usActionData = skilltargetgridno; + return(AI_ACTION_USE_SKILL); + } + } + // frequencies are clear, order a strike + else if ( GetBestAoEGridNo(pSoldier, &skilltargetgridno, max(1, gSkillTraitValues.usVOMortarRadius - 2), 1, 2, SoldierCondTrue, SoldierCondFalse) ) + { + pSoldier->usAISkillUse = SKILLS_RADIO_ARTILLERY; + pSoldier->aiData.usActionData = skilltargetgridno; + return(AI_ACTION_USE_SKILL); + } + } + // no access to artillery... we can still call reinforcements if we haven't yet done so + else if ( !gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition && MoreFriendsThanEnemiesinNearbysectors(pSoldier->bTeam, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ) ) + { + // if frequencies are jammed... + if ( SectorJammed() ) + { + // if we are jamming, turn it off, otherwise, bad luck... + if ( pSoldier->IsJamming() ) + { + pSoldier->usAISkillUse = SKILLS_RADIO_TURNOFF; + pSoldier->aiData.usActionData = skilltargetgridno; + return(AI_ACTION_USE_SKILL); + } + } + // frequencies are clear, lets call for help + else + { + // raise alarm! + return( AI_ACTION_RED_ALERT ); + } + } + // if we can't call in artillery or reinforcements, then nobody else from our team can. So we better jam communications, so that the player cannot use these skills either + else if ( !pSoldier->IsJamming() ) + { + pSoldier->usAISkillUse = SKILLS_RADIO_JAM; + pSoldier->aiData.usActionData = skilltargetgridno; + return(AI_ACTION_USE_SKILL); + } + } BestShot.ubPossible = FALSE; // by default, assume Shooting isn't possible BestThrow.ubPossible = FALSE; // by default, assume Throwing isn't possible diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index 8cc8b111..f7f97ab9 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -103,6 +103,7 @@ typedef enum AI_ACTION_JUMP_WINDOW, // added by Flugente: jump through a window AI_ACTION_FREE_PRISONER, // added by Flugente: free a prisoner + AI_ACTION_USE_SKILL, // added by Flugente: perform a skill, which one is stored in usAISkillUse } ActionType; diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index 4dd5f8a9..38728071 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -374,6 +374,9 @@ void InternalIgniteExplosion( UINT8 ubOwner, INT16 sX, INT16 sY, INT16 sZ, INT32 // HEADROCK HAM 5: Deactivated until the release of HAM 5.1. FireFragments( MercPtrs[ubOwner], sX, sY, sZ, usItem, ubDirection ); } + + // Flugente: Items can have secondary explosions + HandleBuddyExplosions(ubOwner, sX, sY, sZ, sGridNo, usItem, fLocate, bLevel, ubDirection ); } @@ -2225,6 +2228,13 @@ BOOLEAN ExpAffect( INT32 sBombGridNo, INT32 sGridNo, UINT32 uiDist, UINT16 usIte bSmokeEffectType = CREATURE_SMOKE_EFFECT; fBlastEffect = FALSE; break; + + case EXPLOSV_SIGNAL_SMOKE: + + fSmokeEffect = TRUE; + bSmokeEffectType = SIGNAL_SMOKE_EFFECT; + fBlastEffect = FALSE; + break; } } @@ -2872,6 +2882,7 @@ void SpreadEffect( INT32 sGridNo, UINT8 ubRadius, UINT16 usItem, UINT8 ubOwner, case EXPLOSV_TEARGAS: case EXPLOSV_SMOKE: case EXPLOSV_CREATUREGAS: + case EXPLOSV_SIGNAL_SMOKE: fSmokeEffect = TRUE; break; @@ -3993,6 +4004,25 @@ void HandleExplosionQueue( void ) // now add a tripwire item to the floor, simulating that activating tripwire deactivates it AddItemToPool( sGridNo, &newtripwireObject, 1, ubLevel, 0, -1 ); } + else if ( (*pObj)[0]->data.ubWireNetworkFlag & ANY_ARTILLERY_FLAG ) + { + UINT8 cnt = 0; + if ( (*pObj)[0]->data.ubWireNetworkFlag & ARTILLERY_STRIKE_COUNT_1 ) cnt += 1; + if ( (*pObj)[0]->data.ubWireNetworkFlag & ARTILLERY_STRIKE_COUNT_2 ) cnt += 2; + if ( (*pObj)[0]->data.ubWireNetworkFlag & ARTILLERY_STRIKE_COUNT_4 ) cnt += 4; + + // determine gridno to attack - smoke signal required. Otherwise, it is assumed the radio operator ordered the bombing of his OWN position + // if we cannot even find a radio operator, all bets are off - target a random gridno + INT32 sTargetGridNo = -1; + if ( GetRandomSignalSmokeGridNo(&sTargetGridNo) || GetRadioOperatorSignal((*pObj)[0]->data.misc.ubBombOwner, &sTargetGridNo) || (sTargetGridNo = RandomGridNo()) ) + { + for ( UINT8 i = 0; i < cnt; ++i) + ArtilleryStrike(pObj->usItem, sGridNo, sTargetGridNo); + } + + // not needed anymore + RemoveItemFromPool( sGridNo, gWorldBombs[ uiWorldBombIndex ].iItemIndex, ubLevel ); + } else { gfExplosionQueueMayHaveChangedSight = TRUE; @@ -5376,3 +5406,23 @@ void HandleSeeingPowerGenFan( UINT32 sGridNo ) } } #endif + +void HandleBuddyExplosions(UINT8 ubOwner, INT16 sX, INT16 sY, INT16 sZ, INT32 sGridNo, UINT16 usItem, BOOLEAN fLocate, INT8 bLevel, UINT8 ubDirection ) +{ + // Flugente: Items can have secondary explosions + if ( Item[usItem].usBuddyItem ) + { + if ( Item[Item[usItem].usBuddyItem].flare ) + { + if( !sZ || !FindBuilding(sGridNo) ) + { + // Add a light effect... + NewLightEffect( sGridNo, (UINT8)Explosive[Item[Item[usItem].usBuddyItem].ubClassIndex].ubDuration , (UINT8)Explosive[Item[Item[usItem].usBuddyItem].ubClassIndex].ubStartRadius ); + } + } + else if ( Item[Item[usItem].usBuddyItem ].usItemClass & (IC_GRENADE|IC_BOMB) ) + { + IgniteExplosion( ubOwner, sX, sY, sZ, sGridNo, Item[usItem].usBuddyItem, bLevel, ubDirection ); + } + } +} diff --git a/TileEngine/Explosion Control.h b/TileEngine/Explosion Control.h index 66583216..9e37dc9b 100644 --- a/TileEngine/Explosion Control.h +++ b/TileEngine/Explosion Control.h @@ -159,4 +159,8 @@ extern void HandleSwitchToOpenFortifiedDoor( UINT32 sGridNo ); extern void HandleSeeingPowerGenFan( UINT32 sGridNo ); extern void HandleSeeingFortifiedDoor( UINT32 sGridNo );//Ja25 UB #endif + +// Flugente: handle secondary explosive effects +void HandleBuddyExplosions(UINT8 ubOwner, INT16 sX, INT16 sY, INT16 sZ, INT32 sGridNo, UINT16 usItem, BOOLEAN fLocate, INT8 bLevel, UINT8 ubDirection ); + #endif diff --git a/TileEngine/SmokeEffects.cpp b/TileEngine/SmokeEffects.cpp index 0444dde4..2e059ef1 100644 --- a/TileEngine/SmokeEffects.cpp +++ b/TileEngine/SmokeEffects.cpp @@ -126,6 +126,10 @@ INT8 FromWorldFlagsToSmokeType( UINT16 ubWorldFlags ) { return( BURNABLEGAS_SMOKE_EFFECT ); } + else if ( ubWorldFlags & MAPELEMENT_EXT_SIGNAL_SMOKE ) + { + return( SIGNAL_SMOKE_EFFECT ); + } else { return( NO_SMOKE_EFFECT ); @@ -157,10 +161,15 @@ UINT16 FromSmokeTypeToWorldFlags( INT8 bType ) return( MAPELEMENT_EXT_BURNABLEGAS ); break; - case CREATURE_SMOKE_EFFECT: + case CREATURE_SMOKE_EFFECT: - return( MAPELEMENT_EXT_CREATUREGAS ); - break; + return( MAPELEMENT_EXT_CREATUREGAS ); + break; + + case SIGNAL_SMOKE_EFFECT: + + return( MAPELEMENT_EXT_SIGNAL_SMOKE ); + break; default: @@ -250,7 +259,8 @@ INT32 NewSmokeEffect( INT32 sGridNo, UINT16 usItem, INT8 bLevel, UINT8 ubOwner, //ubStartRadius = 1; //break; - case EXPLOSV_CREATUREGAS: + case EXPLOSV_CREATUREGAS: + bSmokeEffectType = CREATURE_SMOKE_EFFECT; break; @@ -259,11 +269,14 @@ INT32 NewSmokeEffect( INT32 sGridNo, UINT16 usItem, INT8 bLevel, UINT8 ubOwner, //bSmokeEffectType = CREATURE_SMOKE_EFFECT; //ubDuration = 2; //ubStartRadius = 0; - break; + //break; + + case EXPLOSV_SIGNAL_SMOKE: + + bSmokeEffectType = SIGNAL_SMOKE_EFFECT; + break; } - - pSmoke->ubDuration = (UINT8)Explosive[ Item[ usItem ].ubClassIndex ].ubDuration; pSmoke->ubRadius = (UINT8)Explosive[ Item[ usItem ].ubClassIndex ].ubStartRadius; pSmoke->bAge = 0; @@ -372,99 +385,117 @@ void AddSmokeEffectToTile( INT32 iSmokeEffectID, INT8 bType, INT32 sGridNo, INT8 { case NORMAL_SMOKE_EFFECT: - if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\smkechze.STI" ); - } - else - { - if ( fDissipating ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\smalsmke.STI" ); - } - else - { - strcpy( AniParams.zCachedFile, "TILECACHE\\SMOKE.STI" ); - } - } + if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\smkechze.STI" ); + } + else + { + if ( fDissipating ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\smalsmke.STI" ); + } + else + { + strcpy( AniParams.zCachedFile, "TILECACHE\\SMOKE.STI" ); + } + } break; case TEARGAS_SMOKE_EFFECT: - if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\tearchze.STI" ); - } - else - { - if ( fDissipating ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\smaltear.STI" ); - } - else - { - strcpy( AniParams.zCachedFile, "TILECACHE\\TEARGAS.STI" ); - } - } + if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\tearchze.STI" ); + } + else + { + if ( fDissipating ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\smaltear.STI" ); + } + else + { + strcpy( AniParams.zCachedFile, "TILECACHE\\TEARGAS.STI" ); + } + } break; case MUSTARDGAS_SMOKE_EFFECT: - if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\mustchze.STI" ); - } - else - { - if ( fDissipating ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\smalmust.STI" ); - } - else - { - strcpy( AniParams.zCachedFile, "TILECACHE\\MUSTARD2.STI" ); - } - } + if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\mustchze.STI" ); + } + else + { + if ( fDissipating ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\smalmust.STI" ); + } + else + { + strcpy( AniParams.zCachedFile, "TILECACHE\\MUSTARD2.STI" ); + } + } break; case BURNABLEGAS_SMOKE_EFFECT: - if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\FLAMCHZE.STI" ); - } - else - { - if ( fDissipating ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\smalflam.STI" ); - } - else - { - strcpy( AniParams.zCachedFile, "TILECACHE\\FLAMETH2.STI" ); - } - } + if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\FLAMCHZE.STI" ); + } + else + { + if ( fDissipating ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\smalflam.STI" ); + } + else + { + strcpy( AniParams.zCachedFile, "TILECACHE\\FLAMETH2.STI" ); + } + } break; case CREATURE_SMOKE_EFFECT: - if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" ); - } - else - { - if ( fDissipating ) - { - strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" ); - } - else - { - strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" ); - } - } + if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" ); + } + else + { + if ( fDissipating ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" ); + } + else + { + strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" ); + } + } break; + case SIGNAL_SMOKE_EFFECT: + + if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" ); + } + else + { + if ( fDissipating ) + { + strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" ); + } + else + { + strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" ); + } + } + break; } // Create tile... @@ -944,18 +975,52 @@ void UpdateSmokeEffectGraphics( ) //if the smoke is active if( gSmokeEffectData[ uiCnt ].fAllocated ) { - if ( gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF ) - { - bLevel = 1; - } - else - { - bLevel = 0; - } + if ( gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF ) + { + bLevel = 1; + } + else + { + bLevel = 0; + } SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, ERASE_SPREAD_EFFECT, bLevel, uiCnt ); SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, TRUE, bLevel, uiCnt ); - } + } } } + +BOOL GetRandomSignalSmokeGridNo(INT32* psGridNo) +{ + UINT32 uiCnt; + //SMOKEEFFECT *pSmoke; + //INT8 bLevel; + + INT32 smokearray[50]; + for (UINT8 i = 0; i < 50; ++i) + smokearray[i] = -1; + + UINT8 cnt = 0; + + //loop through and save the number of smoke effects + for( uiCnt=0; uiCnt < guiNumSmokeEffects; uiCnt++) + { + if ( gSmokeEffectData[ uiCnt ].fAllocated && gSmokeEffectData[ uiCnt ].bType == SIGNAL_SMOKE_EFFECT ) + { + smokearray[cnt++] = uiCnt; + + if ( cnt >= 50 ) + break; + } + } + + if ( !cnt ) + return FALSE; + + UINT8 target = Random(cnt); + + (*psGridNo) = gSmokeEffectData[ smokearray[target] ].sGridNo; + + return TRUE; +} diff --git a/TileEngine/SmokeEffects.h b/TileEngine/SmokeEffects.h index 32f5643d..9dd173f0 100644 --- a/TileEngine/SmokeEffects.h +++ b/TileEngine/SmokeEffects.h @@ -11,6 +11,7 @@ enum MUSTARDGAS_SMOKE_EFFECT, CREATURE_SMOKE_EFFECT, BURNABLEGAS_SMOKE_EFFECT, + SIGNAL_SMOKE_EFFECT, // added by Flugente for artillery }; #define SMOKE_EFFECT_INDOORS 0x01 @@ -68,5 +69,7 @@ void ResetSmokeEffects(); void UpdateSmokeEffectGraphics( ); +// Flugente: get the gridno and blevel of a random smoke signal, if one exists +BOOL GetRandomSignalSmokeGridNo(INT32* psGridNo); #endif \ No newline at end of file diff --git a/TileEngine/Tile Animation.cpp b/TileEngine/Tile Animation.cpp index 9a0bf003..f97a9311 100644 --- a/TileEngine/Tile Animation.cpp +++ b/TileEngine/Tile Animation.cpp @@ -573,7 +573,7 @@ void UpdateAniTiles( ) // if ( ubExpType == EXPLOSV_TEARGAS || ubExpType == EXPLOSV_MUSTGAS || // ubExpType == EXPLOSV_SMOKE ) if ( ubExpType == EXPLOSV_TEARGAS || ubExpType == EXPLOSV_MUSTGAS || - ubExpType == EXPLOSV_SMOKE || ubExpType == EXPLOSV_BURNABLEGAS ) + ubExpType == EXPLOSV_SMOKE || ubExpType == EXPLOSV_BURNABLEGAS || ubExpType == EXPLOSV_SIGNAL_SMOKE ) { // Do sound.... // PlayJA2Sample( AIR_ESCAPING_1, RATE_11025, SoundVolume( HIGHVOLUME, pNode->sGridNo ), 1, SoundDir( pNode->sGridNo ) ); diff --git a/TileEngine/overhead map.cpp b/TileEngine/overhead map.cpp index e4331ac3..483d252a 100644 --- a/TileEngine/overhead map.cpp +++ b/TileEngine/overhead map.cpp @@ -1307,13 +1307,24 @@ void RenderOverheadOverlays() { //loop through all soldiers. end = MAX_NUM_SOLDIERS; } - if(is_networked)end = MAX_NUM_SOLDIERS; - + if(is_networked) + end = MAX_NUM_SOLDIERS; + SGPRect HostileArea = {0,0,0,0}; + + // Flugente: is one of the player's mercs scanning for jam signals while someone is actually jamming signals? + BOOLEAN showjammers = FALSE; + if ( PlayerTeamIsScanning() && SectorJammed() ) + showjammers = TRUE; + + UINT16 jamcolour = Get16BPPColor( FROMRGB( 36, 219, 151 ) ); + + BOOLEAN marklastenemy = FALSE; + if ( gGameSettings.fOptions[TOPTION_SHOW_LAST_ENEMY] && gGameExternalOptions.ubMarkerMode && gTacticalStatus.Team[ ENEMY_TEAM ].bMenInSector <= gGameExternalOptions.ubSoldiersLeft ) + marklastenemy = TRUE; - - for( i = 0; i < end; i++ ) + for( i = 0; i < end; ++i ) { //First, check to see if the soldier exists and is in the sector. pSoldier = MercPtrs[ i ]; @@ -1325,14 +1336,16 @@ void RenderOverheadOverlays() continue; //DBrot: mark his general area as hostile - if(!gfEditMode && gGameSettings.fOptions[TOPTION_SHOW_LAST_ENEMY] /*&& gfUseBiggerOverview */&& gGameExternalOptions.ubMarkerMode && gTacticalStatus.Team[ ENEMY_TEAM ].bMenInSector <= gGameExternalOptions.ubSoldiersLeft){ - if(pSoldier->bTeam == ENEMY_TEAM){ + // Flugente: also do that if the we scanned a jamming person + if(!gfEditMode && (showjammers || marklastenemy ) ) + { + if ( ( marklastenemy && pSoldier->bTeam == ENEMY_TEAM ) || ( showjammers && pSoldier->IsJamming() ) ) + { UINT8 ubGridSquareX, ubGridSquareY; ubGridSquareX = sX / (gusGridFrameX / gubGridDivisor); //( pSoldier->sGridNo / WORLD_COLS ) / ( WORLD_COLS / ubResolutionTable[gGameExternalOptions.ubGridResolution]); ubGridSquareY = sY / (gusGridFrameY / gubGridDivisor); //( pSoldier->sGridNo - ( ( pSoldier->sGridNo / WORLD_COLS ) * WORLD_COLS ) ) / ( WORLD_COLS / ubResolutionTable[gGameExternalOptions.ubGridResolution]); - - + HostileArea.iLeft = iOffsetHorizontal + (((gusGridFrameX / gubGridDivisor) * ubGridSquareX)); HostileArea.iTop = iOffsetVertical + (((gusGridFrameY / gubGridDivisor) * ubGridSquareY)); HostileArea.iRight = iOffsetHorizontal + (((gusGridFrameX / gubGridDivisor) * (ubGridSquareX + 1))); @@ -1340,9 +1353,14 @@ void RenderOverheadOverlays() if(gGameExternalOptions.ubMarkerMode == SHARPBORDER) RectangleDraw(TRUE, HostileArea.iLeft, HostileArea.iTop, HostileArea.iRight, HostileArea.iBottom, 255, pDestBuf); - if(gGameExternalOptions.ubMarkerMode == HATCHED){ - RectangleDraw(TRUE, HostileArea.iLeft, HostileArea.iTop, HostileArea.iRight, HostileArea.iBottom, 0xF000, pDestBuf); - Blt16BPPBufferLooseHatchRectWithColor( (UINT16*)pDestBuf, uiDestPitchBYTES, &HostileArea, 0xF000 ); + if(gGameExternalOptions.ubMarkerMode == HATCHED) + { + UINT16 colour = 0xF000; + if ( showjammers && pSoldier->IsJamming() ) + colour = jamcolour; + + RectangleDraw(TRUE, HostileArea.iLeft, HostileArea.iTop, HostileArea.iRight, HostileArea.iBottom, colour, pDestBuf); + Blt16BPPBufferLooseHatchRectWithColor( (UINT16*)pDestBuf, uiDestPitchBYTES, &HostileArea, colour ); } } } @@ -1352,10 +1370,7 @@ void RenderOverheadOverlays() sX += 2; sY -= 5; //sScreenY -= 7; //height of doll - - - - + if( !gfTacticalPlacementGUIActive && pSoldier->bLastRenderVisibleValue == -1 && !(gTacticalStatus.uiFlags&SHOW_ALL_MERCS) ) { diff --git a/TileEngine/physics.cpp b/TileEngine/physics.cpp index cb4f1349..4f76a57e 100644 --- a/TileEngine/physics.cpp +++ b/TileEngine/physics.cpp @@ -2867,3 +2867,38 @@ INT32 RandomGridFromRadius( INT32 sSweetGridNo, INT8 ubMinRadius, INT8 ubMaxRadi return( sGridNo ); } + +UINT32 GetArtilleryTargetGridNo( UINT32 sTargetGridNo, INT8 bRadius ) +{ + return RandomGridFromRadius( sTargetGridNo, 1, bRadius ); +} + +BOOLEAN GetArtilleryLaunchParams( UINT32 sStartingGridNo, UINT32 sTargetGridNo, INT16 sStartZ, INT16 sEndZ, UINT16 usLauncher, OBJECTTYPE* pObj, FLOAT* pdForce, FLOAT* pdDegrees) +{ + FLOAT dMagForce, dMaxForce, dMinForce; + FLOAT dDegrees = OUTDOORS_START_ANGLE; + INT16 sMinRange = MIN_MORTAR_RANGE; + + sStartZ = 256; + + // Find force for basic + INT32 sFinalGridNo = 0; + FindBestForceForTrajectory( sStartingGridNo, sTargetGridNo, sStartZ, sEndZ, dDegrees, pObj, &sFinalGridNo, &dMagForce ); + + INT32 uiMaxRange = GetModifiedGunRange(usLauncher) / CELL_X_SIZE; + + dMaxForce = CalculateForceFromRange( NULL, (INT16) uiMaxRange, (FLOAT)( PI/4 ) ); + + if ( dMagForce > dMaxForce ) + dMagForce = dMaxForce; + + dMinForce = CalculateForceFromRange( NULL, (INT16)( sMinRange / 10 ), (FLOAT)( PI / 4 ) ); + + if ( dMagForce < dMinForce ) + dMagForce = dMinForce; + + (*pdForce) = dMagForce; + (*pdDegrees) = dDegrees; + + return TRUE; +} diff --git a/TileEngine/physics.h b/TileEngine/physics.h index 45973374..aa870220 100644 --- a/TileEngine/physics.h +++ b/TileEngine/physics.h @@ -181,5 +181,10 @@ BOOLEAN SavePhysicsTableToSaveGameFile( HWFILE hFile ); BOOLEAN LoadPhysicsTableFromSavedGameFile( HWFILE hFile ); +// Flugente: artillery functions +INT32 RandomGridFromRadius( INT32 sSweetGridNo, INT8 ubMinRadius, INT8 ubMaxRadius ); +UINT32 GetArtilleryTargetGridNo( UINT32 sTargetGridNo, INT8 bRadius ); +BOOLEAN GetArtilleryLaunchParams( UINT32 sStartingGridNo, UINT32 sTargetGridNo, INT16 sStartZ, INT16 sEndZ, UINT16 usLauncher, OBJECTTYPE* pObj, FLOAT* pdForce, FLOAT* pdDegrees); + #endif \ No newline at end of file diff --git a/TileEngine/worlddef.h b/TileEngine/worlddef.h index 07184b59..538f44fe 100644 --- a/TileEngine/worlddef.h +++ b/TileEngine/worlddef.h @@ -134,12 +134,13 @@ class SOLDIERTYPE; #define MAPELEMENT_EXT_CREATUREGAS 0x0080 //0x80 #define MAPELEMENT_EXT_BURNABLEGAS 0x0100 //0x100 #define MAPELEMENT_EXT_CLIMBPOINT 0x0200 //0x200 +#define MAPELEMENT_EXT_SIGNAL_SMOKE 0x0400 //0x400 // added by Flugente #define FIRST_LEVEL 0 #define SECOND_LEVEL 1 //#define ANY_SMOKE_EFFECT ( MAPELEMENT_EXT_CREATUREGAS | MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS ) -#define ANY_SMOKE_EFFECT ( MAPELEMENT_EXT_CREATUREGAS | MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS | MAPELEMENT_EXT_BURNABLEGAS ) +#define ANY_SMOKE_EFFECT ( MAPELEMENT_EXT_CREATUREGAS | MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS | MAPELEMENT_EXT_BURNABLEGAS | MAPELEMENT_EXT_SIGNAL_SMOKE ) // WDS - Clean up inventory handling diff --git a/Utils/Text.h b/Utils/Text.h index baeecbb6..09a9e9a8 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -145,6 +145,9 @@ extern CHAR16 WeaponType[MAXITEMS][30]; extern CHAR16 Message[][STRING_LENGTH]; extern CHAR16 TeamTurnString[][STRING_LENGTH]; extern STR16 pMilitiaControlMenuStrings[]; //lal +extern STR16 pTraitSkillsMenuStrings[]; //Flugente +extern STR16 pTraitSkillsMenuDescStrings[]; //Flugente +extern STR16 pTraitSkillsDenialStrings[]; //Flugente //extern STR16 pTalkToAllMenuStrings[]; extern STR16 pAssignMenuStrings[]; extern STR16 pTrainingStrings[]; @@ -704,6 +707,21 @@ enum extern STR16 szCovertTextStr[]; +enum +{ + STR_POWERPACK_BEGIN, + STR_POWERPACK_FULL, + STR_POWERPACK_GOOD, + STR_POWERPACK_HALF, + STR_POWERPACK_LOW, + STR_POWERPACK_EMPTY, + STR_POWERPACK_END, + + TEXT_POWERPACK_STR +}; + +extern STR16 gPowerPackDesc[]; + enum { STR_CORPSE_NO_HEAD_ITEM, @@ -2415,6 +2433,20 @@ extern STR16 szCareLevelText[]; extern STR16 szRacistText[]; extern STR16 szSexistText[]; +enum +{ + TEXT_SKILL_DENIAL_REQ, + TEXT_SKILL_DENIAL_X_AP, + TEXT_SKILL_DENIAL_X_TXT, + TEXT_SKILL_DENIAL_X_TXT_ORHIGHER, + TEXT_SKILL_DENIAL_X_TXT_ORHIGHER_OR, + TEXT_SKILL_DENIAL_X_MINUTES, + TEXT_SKILL_DENIAL_NOMORTAR, + TEXT_SKILL_DENIAL_ITSCOMPLICATED, + TEXT_SKILL_DENIAL_NODEMON, + + TEXT_SKILL_DENIAL_MAX, +}; // Enumeration support typedef struct Str8EnumLookupType { diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index e80a64f6..9205e352 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -1646,6 +1646,7 @@ STR16 pAssignmentStrings[] = L"交通工具", // in a vehicle L"在途中",// in transit - abbreviated form L"修理", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"锻炼", // training themselves L"民兵", // training a town to revolt L"游击队", //L"M.Militia", //training moving militia units //ham3.6 @@ -1734,6 +1735,7 @@ STR16 pPersonnelAssignmentStrings[] = L"交通工具", L"在途中", L"修理", + L"Radio Scan", // radio scan // TODO.Translate L"锻炼", L"训练民兵", L"训练游击队", @@ -1782,6 +1784,7 @@ STR16 pLongAssignmentStrings[] = L"交通工具", L"在途中", L"修理", + L"Radio Scan", // radio scan // TODO.Translate L"练习", L"训练民兵", L"训练游击队", //L"Train Mobiles", @@ -1909,8 +1912,9 @@ STR16 pAssignMenuStrings[] = L"编队", L"医生", L"病人", - L"交通工具", + L"交通工具", L"修理", + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"训练", L"搬运物品", // move items // TODO.Translate L"设施", // L"Facility", // the merc is using/staffing a facility //ham3.6 @@ -1939,6 +1943,43 @@ STR16 pMilitiaControlMenuStrings[] = L"取消", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attack", // set militia to aggresive @@ -2143,36 +2184,41 @@ STR16 gzMercSkillTextNew[] = L"近战", L"投掷", L"夜战", - L"潜行", // 15 + L"潜行", // 14 L"运动员", L"健身", L"爆破", L"教学", - L"侦察", // 20 + L"侦察", // 19 // covert ops is a major trait that was added later L"特工", // L"Covert Ops", + + // new minor traits + L"Radio Operator", // 21 + // second names for major skills L"机枪手", L"掷弹兵", L"狙击手", - L"游骑兵", - L"枪斗术", // 25 + L"游骑兵", // 25 + L"枪斗术", L"武术家", L"班长", L"工兵", L"军医", // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"间谍", // 30 + L"Placeholder", // 40 + L"间谍", // 41 + L"Placeholder", // for radio operator (minor trait) L"更多...", }; ////////////////////////////////////////////////////////// @@ -6356,6 +6402,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"侦查:没有什么东西你觉察不到! ± ", // Covert ops L"特工: 你让詹姆斯邦德甘拜下风! ± ", // L"Covert Operations: You make 007 look like an amateur! ± ", + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7843,6 +7891,18 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + // WANNE: Some Chinese specific strings that needs to be in unicode! STR16 ChineseSpecString1 = L"%%"; //defined in _ChineseText.cpp as this file is already unicode STR16 ChineseSpecString2 = L"*%3d%%%%"; //defined in _ChineseText.cpp as this file is already unicode diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index aa1a4e1c..55facc2d 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -1642,6 +1642,7 @@ STR16 pAssignmentStrings[] = L"Voertuig", // in a vehicle L"Onderweg", // in transit - abbreviated form L"Repareer", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Oefenen", // training themselves L"Militie", // training a town to revolt L"M.Militia", //training moving militia units // TODO.Translate @@ -1730,6 +1731,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Voertuig", // in a vehicle L"Onderweg", // in transit - abbreviated form L"Repareer", // repairing + L"Radio Scan", // radio scan // TODO.Translate L"Oefenen", // training themselves L"Militie", // training a town to revolt L"Training Mobile Militia", // TODO.Translate @@ -1778,6 +1780,7 @@ STR16 pLongAssignmentStrings[] = L"Voertuig", // in a vehicle L"Onderweg", // in transit - abbreviated form L"Repareer", // repairing + L"Radio Scan", // radio scan // TODO.Translate L"Oefenen", // training themselves L"Militie", // training a town to revolt L"Train Mobiles", // TODO.Translate @@ -1907,6 +1910,7 @@ STR16 pAssignMenuStrings[] = L"Patient", // the merc is receiving medical attention L"Vehicle", // the merc is in a vehicle L"Repair", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Train", // the merc is training L"Move Item", // move items // TODO.Translate L"Facility", // the merc is using/staffing a facility // TODO.Translate @@ -1935,6 +1939,44 @@ STR16 pMilitiaControlMenuStrings[] = L"Cancel", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attack", // set militia to aggresive @@ -2147,28 +2189,31 @@ STR16 gzMercSkillTextNew[] = L"Scouting", // 19 // covert ops is a major trait that was added later L"Covert Ops", // 20 + // new minor traits + L"Radio Operator", // 21 // second names for major skills - L"Machinegunner", // 21 + L"Machinegunner", // 22 L"Bombardier", L"Sniper", L"Ranger", - L"Gunfighter", // 25 + L"Gunfighter", // 26 L"Martial Arts", L"Squadleader", L"Engineer", - L"Doctor", // 20 + L"Doctor", // 30 // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Spy", // 40 + L"Placeholder", // 40 + L"Spy", // 41 + L"Placeholder", // for radio operator (minor trait) L"More...", }; ////////////////////////////////////////////////////////// @@ -6364,6 +6409,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Scouting: Nothing can escape your notice! ± ", // Covert ops L"Covert Operations: You make 007 look like an amateur! ± ", // TODO.Translate + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7860,4 +7907,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 0e70370e..b68d3c43 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -1646,6 +1646,7 @@ STR16 pAssignmentStrings[] = L"Vehicle", // in a vehicle L"In Trans", // in transit - abbreviated form L"Repair", // repairing + L"Radio Scan", // scanning for nearby patrols L"Practice", // training themselves L"Militia", // training a town to revolt L"M.Militia", //training moving militia units @@ -1655,7 +1656,7 @@ STR16 pAssignmentStrings[] = L"Staff", // operating a strategic facility L"Eat", // eating at a facility (cantina etc.) L"Rest", // Resting at a facility - L"Prison", // Flugente: interrogate prisoners + L"Prison", // interrogate prisoners L"Dead", // dead L"Incap.", // abbreviation for incapacitated L"POW", // Prisoner of war - captured @@ -1734,6 +1735,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Vehicle", L"In Transit", L"Repair", + L"Radio Scan", L"Practice", L"Training Militia", L"Training Mobile Militia", // Missing @@ -1782,6 +1784,7 @@ STR16 pLongAssignmentStrings[] = L"Vehicle", L"In Transit", L"Repair", + L"Radio Scan", L"Practice", L"Train Militia", L"Train Mobiles", // Missing @@ -1911,6 +1914,7 @@ STR16 pAssignMenuStrings[] = L"Patient", // the merc is receiving medical attention L"Vehicle", // the merc is in a vehicle L"Repair", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Train", // the merc is training L"Move Item", // move items L"Facility", // the merc is using/staffing a facility @@ -1939,6 +1943,43 @@ STR16 pMilitiaControlMenuStrings[] = L"Cancel", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attack", // set militia to aggresive @@ -2151,28 +2192,33 @@ STR16 gzMercSkillTextNew[] = L"Scouting", // 19 // covert ops is a major trait that was added later L"Covert Ops", // 20 + + // new minor traits + L"Radio Operator", // 21 + // second names for major skills - L"Machinegunner", // 21 + L"Machinegunner", // 22 L"Bombardier", L"Sniper", L"Ranger", - L"Gunfighter", // 25 + L"Gunfighter", // 26 L"Martial Arts", L"Squadleader", L"Engineer", - L"Doctor", // 20 + L"Doctor", // 30 // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Spy", // 40 + L"Placeholder", // 40 + L"Spy", // 41 + L"Placeholder", // for radio operator (minor trait) L"More...", }; ////////////////////////////////////////////////////////// @@ -6356,6 +6402,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Scouting: Nothing can escape your notice! ± ", // Covert ops L"Covert Operations: You make 007 look like an amateur! ± ", + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", }; STR16 NewInvMessage[] = @@ -7844,4 +7892,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index fa1d6dbb..57de20f1 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -1650,6 +1650,7 @@ STR16 pAssignmentStrings[] = L"Transport", // in a vehicle L"Transit", // in transit - abbreviated form L"Réparation", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Formation", // training themselves L"Milice", // training a town to revolt L"Milice M.", //training moving militia units @@ -1737,6 +1738,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Transport", L"Transit", L"Réparation", + L"Radio Scan", // radio scan // TODO.Translate L"Formation", L"Milice", L"Forme la milice mobile",//!!! Too long ? -> It is OK @@ -1785,6 +1787,7 @@ STR16 pLongAssignmentStrings[] = L"Transport", L"Transit", L"Réparation", + L"Radio Scan", // radio scan // TODO.Translate L"Formation", L"Milice", L"Milice mobile", @@ -1914,6 +1917,7 @@ STR16 pAssignMenuStrings[] = L"Patient(e)", // the merc is receiving medical attention L"Transport", // the merc is in a vehicle L"Réparation", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Formation", // the merc is training L"Move Item", // move items // TODO.Translate L"Construction", // the merc is using/staffing a facility @@ -1942,6 +1946,43 @@ STR16 pMilitiaControlMenuStrings[] = L"Annuler", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attaquez", // set militia to aggresive @@ -2154,7 +2195,10 @@ STR16 gzMercSkillTextNew[] = L"Instructeur", L"Reconnaissance", // covert ops is a major trait that was added later - L"Espionnage", // 10 // TODO.Translate !!! "Infiltrateur" ? Char limit ? + L"Espionnage", // 20 // TODO.Translate !!! "Infiltrateur" ? Char limit ? + + // new minor traits + L"Radio Operator", // 21 // second names for major skills L"Mitrailleur", L"Bombardier", @@ -2166,17 +2210,18 @@ STR16 gzMercSkillTextNew[] = L"Ingénieur", L"Chirurgien", // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Espion", // 30 + L"Placeholder", // 40 + L"Espion", // 41 + L"Placeholder", // for radio operator (minor trait) L"Plus...", }; ////////////////////////////////////////////////////////// @@ -6356,6 +6401,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Reconnaissance : Rien n'échappe à votre vigilance ! ± ", // Covert ops L"Déguisement : Vous ferez passer 007 pour un amateur ! ± ", // TODO.Translate + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7845,4 +7892,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 6809ed2a..c4c13dac 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -1655,6 +1655,7 @@ STR16 pAssignmentStrings[] = L"Fahrzeug", // in a vehicle L"Transit", // in transit - abbreviated form L"Repar.", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Üben", // training themselves L"Miliz", // training a town to revolt L"M.Miliz", //training moving militia units @@ -1739,6 +1740,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Fahrzeug", L"Transit", L"Reparieren", + L"Radio Scan", // radio scan // TODO.Translate L"Üben", L"Miliz", L"Trainiere Mobile Miliz", @@ -1785,6 +1787,7 @@ STR16 pLongAssignmentStrings[] = L"Fahrzeug", L"Transit", L"Reparieren", + L"Radio Scan", // radio scan // TODO.Translate L"Üben", L"Miliz", L"Trainiere Mobile", @@ -1910,6 +1913,7 @@ STR16 pAssignMenuStrings[] = L"Patient", // the merc is receiving medical attention L"Fahrzeug", // the merc is in a vehicle L"Repar.", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Training", // the merc is training L"Umzug", // move items L"Betrieb", // the merc is using/staffing a facility @@ -1938,6 +1942,44 @@ STR16 pMilitiaControlMenuStrings[] = L"Abbrechen", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attack", // set militia to aggresive @@ -2162,6 +2204,8 @@ STR16 gzMercSkillTextNew[] = L"Aufklärer", // covert ops is a major trait that was added later L"Geheimagent", + // new minor traits + L"Funker", // 21 // second names for major skills L"MG-Veteran", L"Artillerist", @@ -2173,17 +2217,18 @@ STR16 gzMercSkillTextNew[] = L"Ingenieur", L"Arzt", // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Spion", // 30 + L"Placeholder", // 40 + L"Spion", // 41 + L"Placeholder", // for radio operator (minor trait) L"Mehr...", }; @@ -6181,6 +6226,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Aufklärer: Sie sind über die Maßen aufmerksam, haben ein sehr reges Auge und einen nimmermüden Geist. ± ", // Covert ops L"Geheimagent: Neben Ihnen schaut 007 wie der reinste Amateur aus! ± ", + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7669,4 +7716,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 530d9683..44f1f6cf 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -1638,6 +1638,7 @@ STR16 pAssignmentStrings[] = L"Veicolo", // in a vehicle L"Transito", // in transit - abbreviated form L"Riparare", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Esercit.", // training themselves L"Esercit.", // training a town to revolt L"M.Militia", //training moving militia units // TODO.Translate @@ -1726,6 +1727,7 @@ STR16 pPersonnelAssignmentStrings[] = L"veicolo", L"In transito", L"Riparare", + L"Radio Scan", // radio scan // TODO.Translate L"Esercitarsi", L"Allenamento Esercito", L"Training Mobile Militia", // TODO.Translate @@ -1774,6 +1776,7 @@ STR16 pLongAssignmentStrings[] = L"Veicolo", L"In transito", L"Ripara", + L"Radio Scan", // radio scan // TODO.Translate L"Esercitarsi", L"Allenatore esercito", L"Train Mobiles", // TODO.Translate @@ -1903,6 +1906,7 @@ STR16 pAssignMenuStrings[] = L"Paziente", // the merc is receiving medical attention L"Veicolo", // the merc is in a vehicle L"Ripara", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Si esercita", // the merc is training L"Move Item", // move items // TODO.Translate L"Facility", // the merc is using/staffing a facility // TODO.Translate @@ -1931,6 +1935,44 @@ STR16 pMilitiaControlMenuStrings[] = L"Cancel", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attack", // set militia to aggresive @@ -2143,28 +2185,31 @@ STR16 gzMercSkillTextNew[] = L"Scouting", // 19 // covert ops is a major trait that was added later L"Covert Ops", // 20 + // new minor traits + L"Radio Operator", // 21 // second names for major skills - L"Machinegunner", // 21 + L"Machinegunner", // 22 L"Bombardier", L"Sniper", L"Ranger", - L"Gunfighter", // 25 + L"Gunfighter", // 26 L"Martial Arts", L"Squadleader", L"Engineer", - L"Doctor", // 20 + L"Doctor", // 30 // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Spy", // 40 + L"Placeholder", // 40 + L"Spy", // 41 + L"Placeholder", // for radio operator (minor trait) L"More...", }; ////////////////////////////////////////////////////////// @@ -6351,6 +6396,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Scouting: Nothing can escape your notice! ± ", // Covert ops L"Covert Operations: You make 007 look like an amateur! ± ", // TODO.Translate + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7847,4 +7894,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //ITALIAN diff --git a/Utils/_Ja25ChineseText.cpp b/Utils/_Ja25ChineseText.cpp index ac27e30f..56781e18 100644 --- a/Utils/_Ja25ChineseText.cpp +++ b/Utils/_Ja25ChineseText.cpp @@ -88,6 +88,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"爆破",// L"Demolitions", L"教学",// L"Teaching", L"侦察",// L"Scouting", + L"Radio Operator", L"无",// L"None", L"I.M.P 副技能",// L"I.M.P. Minor Traits", @@ -255,6 +256,17 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"伪装状态下使用近战武器攻击时,秒杀几率增加 +%d%s\n",//L"+%d%s chance of instakill with covert melee weapons\n", L"伪装动作消耗的AP降低 %d%s\n",//L"Disguise AP cost lowered by %d%s.\n", }; + +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"无奖励",// L"No bonuses", diff --git a/Utils/_Ja25DutchText.cpp b/Utils/_Ja25DutchText.cpp index 1fd5246f..0f7c87d2 100644 --- a/Utils/_Ja25DutchText.cpp +++ b/Utils/_Ja25DutchText.cpp @@ -88,6 +88,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Demolitions", L"Teaching", L"Scouting", + L"Radio Operator", L"None", L"I.M.P. Minor Traits", @@ -257,6 +258,16 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"Disguise AP cost lowered by %d%s.\n", }; +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25DutchText.h b/Utils/_Ja25DutchText.h index 4b8243e9..96adfeee 100644 --- a/Utils/_Ja25DutchText.h +++ b/Utils/_Ja25DutchText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25EnglishText.cpp b/Utils/_Ja25EnglishText.cpp index c1fbf94d..77c0cf31 100644 --- a/Utils/_Ja25EnglishText.cpp +++ b/Utils/_Ja25EnglishText.cpp @@ -88,6 +88,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Demolitions", L"Teaching", L"Scouting", + L"Radio Operator", L"None", L"I.M.P. Minor Traits", @@ -257,6 +258,16 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"Disguise AP cost lowered by %d%s.\n", }; +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25EnglishText.h b/Utils/_Ja25EnglishText.h index 43a450d2..3b61a519 100644 --- a/Utils/_Ja25EnglishText.h +++ b/Utils/_Ja25EnglishText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25FrenchText.cpp b/Utils/_Ja25FrenchText.cpp index d9625217..92e2e961 100644 --- a/Utils/_Ja25FrenchText.cpp +++ b/Utils/_Ja25FrenchText.cpp @@ -88,6 +88,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Sabotage", L"Instructeur", L"Reconnaissance", + L"Radio Operator", L"Personne", L"Traits mineurs IMP", @@ -256,6 +257,17 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= // TODO.Translate L"+%d%s chance of instakill with covert melee weapons\n", L"Disguise AP cost lowered by %d%s.\n", }; + +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"Pas de bonus", diff --git a/Utils/_Ja25FrenchText.h b/Utils/_Ja25FrenchText.h index 4b8243e9..96adfeee 100644 --- a/Utils/_Ja25FrenchText.h +++ b/Utils/_Ja25FrenchText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25GermanText.cpp b/Utils/_Ja25GermanText.cpp index eb9c6a0f..ef88c10d 100644 --- a/Utils/_Ja25GermanText.cpp +++ b/Utils/_Ja25GermanText.cpp @@ -85,6 +85,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Sprengmeister", // alt. "Kampfmittel" L"Ausbilder", // alt. "Lehren" L"Aufklärer", // alt. "Spähen" + L"Radio Operator", L"Keine", L"B.S.E. Nebenfertigkeiten", @@ -253,6 +254,17 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"+%d%s Trefferwahrscheinlichkeit für sofortigen Totschlag mit Covert-Ops Nahkampfwaffen\n", L"Verringerte AP Kosten für verdeckte Operationen durch %d%s.\n", }; + +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"Keine Boni", diff --git a/Utils/_Ja25GermanText.h b/Utils/_Ja25GermanText.h index b7e83d85..1a43b826 100644 --- a/Utils/_Ja25GermanText.h +++ b/Utils/_Ja25GermanText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25ItalianText.cpp b/Utils/_Ja25ItalianText.cpp index 24344695..db55189d 100644 --- a/Utils/_Ja25ItalianText.cpp +++ b/Utils/_Ja25ItalianText.cpp @@ -86,6 +86,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Demolitions", L"Teaching", L"Scouting", + L"Radio Operator", L"None", L"I.M.P. Minor Traits", @@ -255,6 +256,16 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"Disguise AP cost lowered by %d%s.\n", }; +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25ItalianText.h b/Utils/_Ja25ItalianText.h index 4b8243e9..96adfeee 100644 --- a/Utils/_Ja25ItalianText.h +++ b/Utils/_Ja25ItalianText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25PolishText.cpp b/Utils/_Ja25PolishText.cpp index b4647f42..770191b9 100644 --- a/Utils/_Ja25PolishText.cpp +++ b/Utils/_Ja25PolishText.cpp @@ -88,6 +88,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"£adunki wybuchowe", L"Nauczanie", L"Zwiad", + L"Radio Operator", L"Brak", L"Pomniejsze cechy I.M.P", @@ -257,6 +258,16 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"Disguise AP cost lowered by %d%s.\n", }; +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25PolishText.h b/Utils/_Ja25PolishText.h index 4b8243e9..96adfeee 100644 --- a/Utils/_Ja25PolishText.h +++ b/Utils/_Ja25PolishText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25RussianText.cpp b/Utils/_Ja25RussianText.cpp index 89f3f8b3..ba1fdee1 100644 --- a/Utils/_Ja25RussianText.cpp +++ b/Utils/_Ja25RussianText.cpp @@ -88,6 +88,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Подрывник", //Demolitions L"Инструктор", //Teaching L"Разведчик", //Scouting + L"Radio Operator", L"Нет", L"I.M.P.: Дополнительные навыки", //I.M.P. Minor Traits @@ -254,6 +255,17 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"+%d%s к шансу на смертельный удар скрытным оружием ближнего боя.\n", L"ОД на переодевание снижено на %d%s.\n", }; + +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"Нет преимуществ", diff --git a/Utils/_Ja25RussianText.h b/Utils/_Ja25RussianText.h index 4b8243e9..96adfeee 100644 --- a/Utils/_Ja25RussianText.h +++ b/Utils/_Ja25RussianText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_Ja25TaiwaneseText.cpp b/Utils/_Ja25TaiwaneseText.cpp index 6f03cf16..c0a38003 100644 --- a/Utils/_Ja25TaiwaneseText.cpp +++ b/Utils/_Ja25TaiwaneseText.cpp @@ -89,6 +89,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"Demolitions", L"Teaching", L"Scouting", + L"Radio Operator", L"None", L"I.M.P. Minor Traits", @@ -258,6 +259,16 @@ STR16 gzIMPMajorTraitsHelpTextsCovertOps[]= L"Disguise AP cost lowered by %d%s.\n", }; +STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]= // TODO.Translate +{ + L"Can use communications equipment\n", + L"Can call in artillery strikes from allies in neighbouring sectors.\n", + L"Via Frequency Scan assignment, enemy patrols can be located.\n", + L"Communications can be jammed sector-wide.\n", + L"If communications are jammed, a operator can scan for the jamming device.\n", + L"Can call in militia reinforcements from neighbouring sectors.\n", +}; + STR16 gzIMPMajorTraitsHelpTextsNone[]= { L"No bonuses", diff --git a/Utils/_Ja25TaiwaneseText.h b/Utils/_Ja25TaiwaneseText.h index 4b8243e9..96adfeee 100644 --- a/Utils/_Ja25TaiwaneseText.h +++ b/Utils/_Ja25TaiwaneseText.h @@ -18,6 +18,7 @@ extern STR16 gzIMPMajorTraitsHelpTextsSquadleader[]; extern STR16 gzIMPMajorTraitsHelpTextsTechnician[]; extern STR16 gzIMPMajorTraitsHelpTextsDoctor[]; extern STR16 gzIMPMajorTraitsHelpTextsCovertOps[]; // added by Flugente +extern STR16 gzIMPMajorTraitsHelpTextsRadioOperator[]; // added by Flugente extern STR16 gzIMPMajorTraitsHelpTextsNone[]; extern STR16 gzIMPMinorTraitsHelpTextsAmbidextrous[]; diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index ad7f5af8..f3c09f79 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -1654,6 +1654,7 @@ STR16 pAssignmentStrings[] = L"Pojazd", // in a vehicle L"Podróż", // in transit - abbreviated form L"Naprawa", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Praktyka", // training themselves L"Samoobr.", // training a town to revolt L"R.Samoobr.", //training moving militia units @@ -1742,6 +1743,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Pojazd", L"Podróż", L"Naprawa", + L"Radio Scan", // radio scan // TODO.Translate L"Praktyka", L"Trenuje samoobronę", L"Training Mobile Militia", // TODO.Translate @@ -1790,6 +1792,7 @@ STR16 pLongAssignmentStrings[] = L"Pojazd", L"W podróży", L"Naprawa", + L"Radio Scan", // radio scan // TODO.Translate L"Praktyka", L"Trenuj samoobronę", L"Train Mobiles", // TODO.Translate @@ -1919,6 +1922,7 @@ STR16 pAssignMenuStrings[] = L"Pacjent", // the merc is receiving medical attention L"Pojazd", // the merc is in a vehicle L"Naprawa", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Szkolenie", // the merc is training L"Move Item", // move items // TODO.Translate L"Facility", // the merc is using/staffing a facility // TODO.Translate @@ -1947,6 +1951,44 @@ STR16 pMilitiaControlMenuStrings[] = L"Anuluj", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + + //STR16 pTalkToAllMenuStrings[] = //{ // L"Atak", // set militia to aggresive @@ -2158,7 +2200,9 @@ STR16 gzMercSkillTextNew[] = L"Uczenie", L"Zwiad", // covert ops is a major trait that was added later - L"Covert Ops", // 10 // TODO.Translate + L"Covert Ops", // 20 // TODO.Translate + // new minor traits + L"Radio Operator", // 21 // second names for major skills L"Strzelec RKMu", L"Bombardier", @@ -2169,17 +2213,18 @@ STR16 gzMercSkillTextNew[] = L"Dowódca Drużyny", L"Inżynier", L"Doktor", - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Spy", // 40 // TODO.Translate + L"Placeholder", // 40 + L"Spy", // 41 // TODO.Translate + L"Placeholder", // for radio operator (minor trait) L"Więcej...", }; ////////////////////////////////////////////////////////// @@ -6371,6 +6416,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Zwiadowca: Nic nie umknie twej uwadze! ± ", // Covert ops L"Covert Operations: You make 007 look like an amateur! ± ", // TODO.Translate + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7871,4 +7918,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 364d2f37..6789b86d 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -1646,6 +1646,7 @@ STR16 pAssignmentStrings[] = L"Транспорт", // in a vehicle L"В пути", // in transit - abbreviated form L"Ремонт", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Практика", // training themselves L"Ополчение", // training a town to revolt L"Мобил.гр.", //training moving militia units //M.Militia @@ -1734,6 +1735,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Транспорт", L"В пути", L"Ремонт", + L"Radio Scan", // radio scan // TODO.Translate L"Практика", L"Ополчение", L"Тренирует мобильную группу", //Training Mobile Militia @@ -1782,6 +1784,7 @@ STR16 pLongAssignmentStrings[] = L"В транспорте", L"В пути", L"Ремонтирует", + L"Radio Scan", // radio scan // TODO.Translate L"Практикуется", L"Тренирует ополчение", L"Тренирует мобильную группу", //Train Mobiles @@ -1911,6 +1914,7 @@ STR16 pAssignMenuStrings[] = L"Пациент", // the merc is receiving medical attention L"Машина", // the merc is in a vehicle L"Ремонт", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Обучение", // the merc is training L"Move Item", // move items // TODO.Translate L"Удобства", // the merc is using/staffing a facility //Facility @@ -1939,6 +1943,44 @@ STR16 pMilitiaControlMenuStrings[] = L"Отмена", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + + //STR16 pTalkToAllMenuStrings[] = //{ // L"В атаку", // set militia to aggresive @@ -2151,6 +2193,8 @@ STR16 gzMercSkillTextNew[] = L"Разведчик", //Scouting // covert ops is a major trait that was added later L"Covert Ops", // 20 // TODO.Translate + // new minor traits + L"Radio Operator", // 21 // second names for major skills L"Пулемётчик", //Machinegunner L"Артиллерист", //Bombardier @@ -2161,17 +2205,18 @@ STR16 gzMercSkillTextNew[] = L"Командир", //Squadleader L"Инженер", //Engineer L"Доктор", //Doctor - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Шпион", // 30 + L"Placeholder", // 40 + L"Шпион", // 41 + L"Placeholder", // for radio operator (minor trait) L"Ещё...", }; ////////////////////////////////////////////////////////// @@ -6356,6 +6401,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Разведчик: Ничто не скроется от вашего зоркого взгляда! ± ", //Scouting: Nothing can escape your notice! // Covert ops L"Covert Operations: You make 007 look like an amateur! ± ", // TODO.Translate + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7845,4 +7892,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //RUSSIAN diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 17f0234a..eb8820c5 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -1646,6 +1646,7 @@ STR16 pAssignmentStrings[] = L"Vehicle", // in a vehicle L"In Trans", // in transit - abbreviated form L"Repair", // repairing + L"Radio Scan", // scanning for nearby patrols // TODO.Translate L"Practice", // training themselves L"Militia", // training a town to revolt L"M.Militia", //training moving militia units // TODO.Translate @@ -1734,6 +1735,7 @@ STR16 pPersonnelAssignmentStrings[] = L"Vehicle", L"In Transit", L"Repair", + L"Radio Scan", // radio scan // TODO.Translate L"Practice", L"Training Militia", L"Training Mobile Militia", // TODO.Translate @@ -1782,6 +1784,7 @@ STR16 pLongAssignmentStrings[] = L"Vehicle", L"In Transit", L"Repair", + L"Radio Scan", // radio scan // TODO.Translate L"Practice", L"Train Militia", L"Train Mobiles", // TODO.Translate @@ -1911,6 +1914,7 @@ STR16 pAssignMenuStrings[] = L"Patient", // the merc is receiving medical attention L"Vehicle", // the merc is in a vehicle L"Repair", // the merc is repairing items + L"Radio Scan", // Flugente: the merc is scanning for patrols in neighbouring sectors L"Train", // the merc is training L"Move Item", // move items // TODO.Translate L"Facility", // the merc is using/staffing a facility // TODO.Translate @@ -1939,6 +1943,44 @@ STR16 pMilitiaControlMenuStrings[] = L"Cancel", // cancel this menu }; +//Flugente +STR16 pTraitSkillsMenuStrings[] = // TODO.Translate +{ + // radio operator + L"Artillery Strike", + L"Jam communications", + L"Scan frequencies", + L"Eavesdrop", + L"Call reinforcements", + L"Switch off radio set", +}; + +//Flugente: short description of the above skills for the skill selection menu +STR16 pTraitSkillsMenuDescStrings[] = +{ + // radio operator + L"Order an artillery strike from sector...", + L"Fill all radio frequencies with white noise, making communications impossible.", + L"Scan for jamming signals.", + L"Use your radio equipment to continously listen for enemy movement", + L"Call in reinforcements from neighbouring sectors.", + L"Turn off radio set to save batteries.", +}; + +STR16 pTraitSkillsDenialStrings[] = +{ + L"Requires:\n", + L" - %d AP\n", + L" - %s\n", + L" - %s or higher\n", + L" - %s or higher or\n", + L" - %d minutes to be ready\n", + L" - mortar positions in neighbouring sectors\n", + L" - %s |o|r %s |a|n|d %s or %s or higher\n" + L" - posession by a demon", +}; + + //STR16 pTalkToAllMenuStrings[] = //{ // L"Attack", // set militia to aggresive @@ -2151,28 +2193,32 @@ STR16 gzMercSkillTextNew[] = L"Scouting", // 19 // covert ops is a major trait that was added later L"Covert Ops", // 20 + // new minor traits + L"Radio Operator", // 21 + // second names for major skills - L"Machinegunner", // 21 + L"Machinegunner", // 22 L"Bombardier", L"Sniper", L"Ranger", - L"Gunfighter", // 25 + L"Gunfighter", // 26 L"Martial Arts", L"Squadleader", L"Engineer", - L"Doctor", // 20 + L"Doctor", // 30 // placeholders for minor traits - L"Placeholder", // 30 + L"Placeholder", // 31 L"Placeholder", L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 35 + L"Placeholder", // 36 L"Placeholder", L"Placeholder", L"Placeholder", - L"Placeholder", // 39 - L"Spy", // 40 + L"Placeholder", // 40 + L"Spy", // 41 + L"Placeholder", // for radio operator (minor trait) L"More...", }; ////////////////////////////////////////////////////////// @@ -6366,6 +6412,8 @@ STR16 MissingIMPSkillsDescriptions[] = L"Scouting: Nothing can escape your notice! ± ", // Covert ops L"Covert Operations: You make 007 look like an amateur! ± ", // TODO.Translate + // Radio Operator + L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // TOO.Translate }; STR16 NewInvMessage[] = @@ -7862,4 +7910,16 @@ STR16 szSexistText[]= L"a Gentleman", }; +// Flugente: power pack texts +STR16 gPowerPackDesc[] = +{ + L"Batteries are ", + L"full", + L"good", + L"at half", + L"low", + L"depleted", + L"." +}; + #endif //TAIWANESE