From ab2a26accb9ce32e39901cc2bb4815d3597e3dd1 Mon Sep 17 00:00:00 2001 From: sun-alf Date: Sun, 21 May 2023 18:48:16 +0300 Subject: [PATCH] [Fix] A bunch of fixes for artillery strike * Avoid using hard-coded Signal and HE mortar shell indices. This caused art.strikes malfunctioning in item mods. * Trim max nubers of waves to duration of signal smoke. Otherwise art.strike turns into unnatural stupidity. * If enemy AI tried to get art.strike but lacked of resources, player will not a warning anymore. --- .gitignore | 3 + GameSettings.cpp | 2 +- Tactical/Inventory Choosing.cpp | 2 +- Tactical/Items.cpp | 18 ++++ Tactical/Items.h | 1 + Tactical/Soldier Control.cpp | 156 +++++++++++++------------------- Utils/Text.h | 1 + Utils/_ChineseText.cpp | 1 + Utils/_DutchText.cpp | 1 + Utils/_EnglishText.cpp | 1 + Utils/_FrenchText.cpp | 1 + Utils/_GermanText.cpp | 1 + Utils/_ItalianText.cpp | 1 + Utils/_PolishText.cpp | 1 + Utils/_RussianText.cpp | 1 + 15 files changed, 97 insertions(+), 94 deletions(-) diff --git a/.gitignore b/.gitignore index 07d506ecd..8aca271a4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ /out/ /CMakeSettings.json /CMakeUserPresets.json + +# My shelves +.shelve/ diff --git a/GameSettings.cpp b/GameSettings.cpp index 778046307..8516a802a 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2845,7 +2845,7 @@ void LoadSkillTraitsExternalSettings() // Flugente: RADIO OPERATOR gSkillTraitValues.fROAllowArtillery = iniReader.ReadBoolean("Radio Operator","RADIO_OPERATOR_ARTILLERY", TRUE); gSkillTraitValues.fROArtilleryDistributedOverTurns = iniReader.ReadBoolean("Radio Operator","RADIO_OPERATOR_ARTILLERY_DISTRIBUTED_OVER_TURNS", FALSE); - gSkillTraitValues.bVOArtillerySectorFrequency = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_ARTILLERY_SECTOR_FREQUENCY", 120, 20, 1440); + gSkillTraitValues.bVOArtillerySectorFrequency = iniReader.ReadInteger("Radio Operator","RADIO_OPERATOR_ARTILLERY_SECTOR_FREQUENCY", 120, 5, 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); diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index 2d9bd7144..e6c1220e6 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -4121,7 +4121,7 @@ void TakeMilitiaEquipmentfromSector( INT16 sMapX, INT16 sMapY, INT8 sMapZ, SOLDI if ( uiTotalNumberOfRealItems == 0 ) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no items to equip, uses harsh langugage instead!" ); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no items to equip, uses harsh language instead!" ); return; } diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 55a457fef..c1aff0435 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -16015,3 +16015,21 @@ bool HasScopeMagFactorForGun( UINT16 ausItemGun, FLOAT aFactor ) return false; } + +UINT16 GetLaunchableOfExplosionType(UINT16 launcher, UINT8 explosionType) +{ + for (int i = 0; i < MAXITEMS; i++) + { + UINT16 launchable = Launchable[i][0]; + + if (launchable == 0) // if reached end of Launchable list, then stop + break; + + if (Launchable[i][1] == launcher) + { + if (Explosive[Item[launchable].ubClassIndex].ubType == explosionType) + return launchable; + } + } + return NOTHING; +} diff --git a/Tactical/Items.h b/Tactical/Items.h index d0e2649bf..bc0c49c69 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -576,5 +576,6 @@ FLOAT GetAttackAPTraitMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, UINT8 // sevenfm: check if this type of grenade can use delayed mode BOOLEAN CanDelayGrenadeExplosion( UINT16 usItem ); +UINT16 GetLaunchableOfExplosionType(UINT16 launcher, UINT8 explosionType); #endif diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 8f7f38c54..d6c3738c6 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -18643,14 +18643,42 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN return FALSE; } + // Locate item indices for Signal and HE shells defined by the active MOD. Evade usage of hard-code values. + static UINT16 usSignalShellIndex = NOTHING; + static UINT16 usHeShellIndex = NOTHING; + if (usSignalShellIndex == NOTHING || usHeShellIndex == NOTHING) + { + UINT16 findSignalShellIndex = 1700; // try default Signal Shell item in 1.13 + UINT16 findHeShellIndex = 140; // try default HE Shell item in 1.13 + if (HasItemFlag(findSignalShellIndex, SIGNAL_SHELL) == FALSE && GetFirstItemWithFlag(&findSignalShellIndex, SIGNAL_SHELL) == FALSE) + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NO_SIGNAL_SHELL]); + return FALSE; + } + UINT16 mortarIndex = GetLauncherFromLaunchable(findSignalShellIndex); + if (mortarIndex != GetLauncherFromLaunchable(findHeShellIndex)) + { + findHeShellIndex = GetLaunchableOfExplosionType(mortarIndex, EXPLOSV_NORMAL); + } + if (findHeShellIndex == NOTHING) + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NO_DEFAULT_SHELL]); + return FALSE; + } + // at this point both shells were found and are OK, so set it to static variables and never touch anymore: + usSignalShellIndex = findSignalShellIndex; + usHeShellIndex = findHeShellIndex; + } + // 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 + INT16 nummortars = 0; // number of mortars determines size of wave (1 - 4) + INT16 numwaves = 0; // number of waves + INT16 numshells = 0; // number of shells + INT16 numwavesMax = (INT16) Explosive[Item[usSignalShellIndex].ubClassIndex].ubDuration; SECTORINFO *pSector = &SectorInfo[SECTOR( sSectorX, sSectorY )]; @@ -18682,35 +18710,36 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN numshells = gSkillTraitValues.usVOMortarPointsAdmin * militia_green + gSkillTraitValues.usVOMortarPointsTroop * militia_troop + gSkillTraitValues.usVOMortarPointsElite * militia_elite; } - if ( gSkillTraitValues.usVOMortarShellDivisor * nummortars < 1 ) + // turn number of mortar points into number of shells; in case of "militia use sector ammo" option, numshells + // represents max potential shells militia can shot for this artillery strike. + numshells = numshells / gSkillTraitValues.usVOMortarShellDivisor; + + if (numshells <= 0) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NOT_ENOUGH_MORTAR_SHELLS] ); + if (bTeam == MILITIA_TEAM) // player does not care if enemy team has not enough points to strike + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NOT_ENOUGH_MORTAR_SHELLS] ); return FALSE; } - numwaves = numshells / (gSkillTraitValues.usVOMortarShellDivisor * nummortars); - - if ( !numwaves ) + if (nummortars <= 0) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NOT_ENOUGH_MORTAR_SHELLS] ); + if (bTeam == MILITIA_TEAM) // player does not care if enemy team has not enough men to strike + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NO_MORTARS]); return FALSE; } + numwaves = max(1, numshells / nummortars); + if (gSkillTraitValues.fROArtilleryDistributedOverTurns) // if delay between waves is enabled, we shouldn't overextend, so trim to + numwaves = min(numwaves, numwavesMax); // signal duration; it doesn't matter if delay is disabled. + // 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, this->ubID + 2, sStartingGridNo, sTargetGridNo ); - else - { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NO_SIGNAL_SHELL] ); - return FALSE; - } + ArtilleryStrike(usSignalShellIndex, this->ubID + 2, sStartingGridNo, sTargetGridNo); // 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 + CreateItem(usHeShellIndex, 100, &shellobj ); shellobj.fFlags |= OBJECT_ARMED_BOMB; shellobj[0]->data.misc.bDetonatorType = BOMB_TIMED; @@ -18772,17 +18801,15 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN // 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; + const UINT8 maxFiringMortarsAmount = 5; + UINT8 radiooperatorID = 0; UINT8 mortaritemcnt = 0; - UINT16 mortararray[5]; - for ( UINT8 i = 0; i < 5; ++i ) - mortararray[i] = 0; + UINT16 mortararray[maxFiringMortarsAmount] = { 0 }; SOLDIERTYPE* pSoldier = NULL; INT32 cnt = gTacticalStatus.Team[bTeam].bFirstID; INT32 lastid = gTacticalStatus.Team[bTeam].bLastID; - for ( pSoldier = MercPtrs[cnt]; cnt < lastid; ++cnt, ++pSoldier ) + for ( pSoldier = MercPtrs[cnt]; (cnt < lastid) && (mortaritemcnt < maxFiringMortarsAmount); ++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 ) @@ -18791,70 +18818,25 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN 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 ); - } + INT8 invsize = (INT8)pSoldier->inv.size( ); // remember inventorysize, so we don't call size() repeatedly - 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_INTERFACE, 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 ) + for ( INT8 bLoop = 0; (bLoop < invsize) && (mortaritemcnt < maxFiringMortarsAmount); ++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; - } + bool alreadyInList = false; + for (INT8 i = 0; i < mortaritemcnt; i++) + if (mortararray[i] == pSoldier->inv[bLoop].usItem) + { + alreadyInList = true; + break; + } - if ( mortaritemcnt >= 5 ) - break; + if (alreadyInList == false) + mortararray[mortaritemcnt++] = pSoldier->inv[bLoop].usItem; + } } - - if ( mortaritemcnt >= 5 ) - break; } // safety check, this shouldn't be happening @@ -18864,16 +18846,6 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN return FALSE; } - // no signal shell -> no barrage - /*if ( !signalshellfired ) - { - if ( radiooperatorID ) - DelayedTacticalCharacterDialogue( MercPtrs[ radiooperatorID ], QUOTE_OUT_OF_AMMO ); - - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, 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; @@ -18901,7 +18873,7 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN // 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) ) + if ( pAttObj && HasItemFlag(pAttObj->usItem, SIGNAL_SHELL) == FALSE ) { // if option is set, delay each wave by one turn if ( gSkillTraitValues.fROArtilleryDistributedOverTurns ) diff --git a/Utils/Text.h b/Utils/Text.h index 25dabaeb1..74bd5f6b7 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -2549,6 +2549,7 @@ enum MSG113_RADIO_ACTION_FAILED, MSG113_NOT_ENOUGH_MORTAR_SHELLS, MSG113_NO_SIGNAL_SHELL, + MSG113_NO_DEFAULT_SHELL, MSG113_NO_MORTARS, MSG113_ALREADY_JAMMING, MSG113_ALREADY_LISTENING, diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index f83c5ed19..a36aa426c 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -7667,6 +7667,7 @@ STR16 New113Message[] = L"无线电操作失败!", L"迫击炮弹不足,无法在分区发动密集轰炸!", L"Items.xml里没有定义信号弹物品!", + L"No High-Explosive shell item found in Items.xml!", L"未发现迫击炮,无法执行密集轰炸!", L"干扰信号成功,不需要重复操作!", L"正在监听周围声音,无需重复操作!", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index db1d6907e..97a920f15 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -7673,6 +7673,7 @@ STR16 New113Message[] = L"Radio action failed!", L"Not enough mortar shells in sector to start a barrage!", L"No signal shell item found in Items.xml!", + L"No High-Explosive shell item found in Items.xml!", L"No mortars found, cannot commence barrage!", L"Already jamming signal, no need to do so again!", L"Already listening for nearby sounds, no need to do so again!", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 2d775e574..55cb244e6 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -7667,6 +7667,7 @@ STR16 New113Message[] = L"Radio action failed!", L"Not enough mortar shells in sector to start a barrage!", L"No signal shell item found in Items.xml!", + L"No High-Explosive shell item found in Items.xml!", L"No mortars found, cannot commence barrage!", L"Already jamming signal, no need to do so again!", L"Already listening for nearby sounds, no need to do so again!", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 1b495017a..e8aee2e78 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -7670,6 +7670,7 @@ STR16 New113Message[] = L"L'action radio a échoué !", L"Pas assez d'obus de mortier dans le secteur pour un tir de barrage !", L"Aucun obus éclairant trouvé dans Items.xml !", + L"No High-Explosive shell item found in Items.xml!", L"Aucun mortier trouvé, tir de barrage impossible !", L"Brouillage radio déjà en cours, inutile d'en lancer un autre !", L"Écoute des sons alentour déjà en cours, inutile d'en lancer une autre !", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 3db0fd143..d6ffc764d 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -7524,6 +7524,7 @@ STR16 New113Message[] = L"Radio action failed!", L"Not enough mortar shells in sector to start a barrage!", L"No signal shell item found in Items.xml!", + L"No High-Explosive shell item found in Items.xml!", L"No mortars found, cannot commence barrage!", L"Already jamming signal, no need to do so again!", L"Already listening for nearby sounds, no need to do so again!", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 6727f801a..6a2e72905 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -7664,6 +7664,7 @@ STR16 New113Message[] = L"Radio action failed!", L"Not enough mortar shells in sector to start a barrage!", L"No signal shell item found in Items.xml!", + L"No High-Explosive shell item found in Items.xml!", L"No mortars found, cannot commence barrage!", L"Already jamming signal, no need to do so again!", L"Already listening for nearby sounds, no need to do so again!", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 917505fd0..444638324 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -7673,6 +7673,7 @@ STR16 New113Message[] = L"Radio action failed!", L"Not enough mortar shells in sector to start a barrage!", L"No signal shell item found in Items.xml!", + L"No High-Explosive shell item found in Items.xml!", L"No mortars found, cannot commence barrage!", L"Already jamming signal, no need to do so again!", L"Already listening for nearby sounds, no need to do so again!", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index c1d100e13..ac2f0a991 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -7662,6 +7662,7 @@ STR16 New113Message[] = L"Не удалось использовать радиостанцию!", L"Недостаточно миномётных снарядов в секторе для постановки огня!", L"Не обнаружены сигнальные мины в Items.xml!", + L"Не обнаружены осколочно-фугасные мины в Items.xml!", L"Нет миномётов, невозможно организовать артналет!", L"Режим радиопомех уже включен, нет необходимости делать это снова!", L"Режим прослушивания звуков уже включен, нет необходимости делать это снова!",