From 11729a132e8d36b415e4d73ffc3668bb6084964e Mon Sep 17 00:00:00 2001 From: Flugente Date: Sat, 29 Jul 2017 21:18:13 +0000 Subject: [PATCH] New feature: riot shields protect against damage from some directions. For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&th=23452&start=0& git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8434 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 3 + GameSettings.h | 4 + GameVersion.cpp | 18 ++-- Tactical/Interface Enhanced.cpp | 17 ++++ Tactical/LOS.cpp | 154 +++++++++++++++++++++++++++++++ Tactical/LOS.h | 3 + Tactical/PATHAI.cpp | 31 +++++++ Tactical/Points.cpp | 29 +++++- Tactical/Soldier Ani.cpp | 3 +- Tactical/Soldier Control.cpp | 111 +++++++++++++++++++++- Tactical/Soldier Control.h | 6 ++ TileEngine/Explosion Control.cpp | 44 +++++++++ TileEngine/renderworld.cpp | 59 ++++++++++++ Utils/XML_Items.cpp | 19 +++- Utils/_ChineseText.cpp | 2 + Utils/_DutchText.cpp | 2 + Utils/_EnglishText.cpp | 2 + Utils/_FrenchText.cpp | 2 + Utils/_GermanText.cpp | 2 + Utils/_ItalianText.cpp | 2 + Utils/_PolishText.cpp | 2 + Utils/_RussianText.cpp | 2 + 22 files changed, 498 insertions(+), 19 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index fccd1445..fafb1b30 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -3148,6 +3148,9 @@ void LoadItemSettings() gItemSettings.fDamageBreathModifierExplosive = iniReader.ReadFloat ("Explosives Settings","DAMAGE_BREATH_EXPLOSIVE_MODIFIER", 1.0f, 0.1f, 5.0f); 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); + + // ------------ MISC MODIFIERS -------------- + gItemSettings.fShieldMovementAPCostModifier = iniReader.ReadFloat ("Misc Settings", "SHIELD_MOVEMENT_APCOST_MODIFIER", 1.7f, 1.0f, 10.0f ); } INT16 DynamicAdjustAPConstants(INT16 iniReadValue, INT16 iniDefaultValue, BOOLEAN reverse) diff --git a/GameSettings.h b/GameSettings.h index 86a69254..e93964f6 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -2376,6 +2376,10 @@ 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 + // ------------ MISC MODIFIERS -------------- + // Flugente + FLOAT fShieldMovementAPCostModifier; // movement AP costs are multiplied with this while a shield is equipped + }ITEM_SETTINGS; extern MOD_SETTINGS gModSettings; diff --git a/GameVersion.cpp b/GameVersion.cpp index 54f49c22..353c05a3 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8434 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8434 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8434 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8434 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8434 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8434 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8434 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8405 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8434 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 17.06.12" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 17.07.29" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Tactical/Interface Enhanced.cpp b/Tactical/Interface Enhanced.cpp index a7da8015..58d61c90 100644 --- a/Tactical/Interface Enhanced.cpp +++ b/Tactical/Interface Enhanced.cpp @@ -2714,6 +2714,15 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr cnt++; } } + + //////////////////// SHIELD + if ( Item[gpItemDescObject->usItem].usRiotShieldStrength > 0 ) + { + swprintf( pStr, L"%s%s", szUDBGenSecondaryStatsTooltipText[40], szUDBGenSecondaryStatsExplanationsTooltipText[40] ); + SetRegionFastHelpText( &(gUDBFasthelpRegions[iFirstDataRegion + cnt]), pStr ); + MSYS_EnableRegion( &gUDBFasthelpRegions[iFirstDataRegion + cnt] ); + ++cnt; + } } ////////////////////////////////////////////////////// @@ -6255,6 +6264,14 @@ void DrawSecondaryStats( OBJECTTYPE * gpItemDescObject ) ++cnt; } } + + //////////////////// SHIELD + if ( (Item[gpItemDescObject->usItem].usRiotShieldStrength > 0 && !fComparisonMode) || + (fComparisonMode && Item[gpComparedItemDescObject->usItem].usRiotShieldStrength > 0) ) + { + BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoSecondaryIcon, 38, gItemDescGenSecondaryRegions[cnt].sLeft + sOffsetX, gItemDescGenSecondaryRegions[cnt].sTop + sOffsetY, VO_BLT_SRCTRANSPARENCY, NULL ); + ++cnt; + } } void DrawPropertyValueInColour( INT16 iValue, UINT8 ubNumLine, UINT8 ubNumRegion, BOOLEAN fComparisonMode, BOOLEAN fModifier, BOOLEAN fHigherBetter, UINT16 uiOverwriteColour = 0, BOOLEAN fPercentSign = FALSE ) diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp index b44a7b3d..964a841c 100644 --- a/Tactical/LOS.cpp +++ b/Tactical/LOS.cpp @@ -2571,6 +2571,105 @@ return( iImpact ); } */ +// Flugente: riot shields +void DamageRiotShield( SOLDIERTYPE* pSoldier, INT32& rsDamage, INT32& rsSecondaryDamage ) +{ + if ( !pSoldier ) + return; + + OBJECTTYPE* pObj = pSoldier->GetEquippedRiotShield( ); + if ( pObj && rsDamage > 0 && Item[pObj->usItem].usRiotShieldStrength > 0 ) + { + INT32 damage_original = rsDamage; + + UINT16 shieldstrength = Item[pObj->usItem].usRiotShieldStrength; + + UINT16 damagetoshield = 2 * damage_original / shieldstrength; + + pSoldier->RiotShieldTakeDamage( damagetoshield ); + + INT32 damage_new = max( 0, min( damage_original, ((100.0f - shieldstrength) / 100.0f) * (damage_original - shieldstrength) ) ); + + if ( rsSecondaryDamage > 0 ) + rsSecondaryDamage = max( 0, rsSecondaryDamage * damage_new / damage_original ); + + rsDamage = damage_new; + } +} + +// Flugente: handle bullet impact on riot shield. Returns true if bullet should be removed +BOOLEAN DamageRiotShield_Bullet( SOLDIERTYPE* pSoldier, BULLET* pBullet ) +{ + UINT16 ubAmmoType = 0; + + SOLDIERTYPE * pFirer = NULL; + if ( pBullet->ubFirerID != NOBODY ) + pFirer = pBullet->pFirer; + + // HEADROCK HAM 5: Fragments read attacking weapon from explosive, not from firer. Theoretically, + // all bullets should read this value... + UINT16 usAttackingWeapon = 0; + if ( pBullet->ubFirerID == NOBODY || pBullet->fFragment ) + { + usAttackingWeapon = pBullet->fromItem; + } + else + { + usAttackingWeapon = pFirer->usAttackingWeapon; + } + + if ( pBullet->usFlags & BULLET_FLAG_KNIFE ) + { + UINT16 usItem = (Item[pBullet->fromItem].bloodieditem>0 ? Item[pBullet->fromItem].bloodieditem : pBullet->fromItem); + UINT16 usItemStatus = (pBullet->ubItemStatus>1 ? pBullet->ubItemStatus - Random( 2 ) : pBullet->ubItemStatus); + + // Add item + CreateItem( usItem, usItemStatus, &gTempObject ); + AddItemToPool( pSoldier->sGridNo, &gTempObject, -1, pSoldier->pathing.bLevel, 0, 0 ); + + // Make team look for items + NotifySoldiersToLookforItems( ); + + // Flugente: if this guy has the disease, or the blade was already infected, the new one will be too + if ( pSoldier->sDiseasePoints[0] > 0 || pBullet->usFlags & BULLET_FLAG_INFECTED ) + (*&gTempObject)[0]->data.sObjectFlag |= INFECTED; + + ubAmmoType = AMMO_KNIFE; + + // as we've already stopped the bullet, get out of here + return TRUE; + } + else if ( pBullet->fFragment ) + { + // Read ammo type from the explosive data. + ubAmmoType = Explosive[Item[usAttackingWeapon].ubClassIndex].ubFragType; + } + else + { + ubAmmoType = AMMO_REGULAR; + + if ( pFirer ) + ubAmmoType = pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType; + } + + INT32 impact_original = max(0, pBullet->iImpact - pBullet->iImpactReduction); + + FLOAT damagepercentage = (FLOAT)((FLOAT)AmmoTypes[ubAmmoType].armourImpactReductionDivisor / (FLOAT)max( 1, AmmoTypes[ubAmmoType].armourImpactReductionMultiplier )); + + damagepercentage *= 1.0f + max( AmmoTypes[ubAmmoType].dDamageModifierArmouredVehicle, AmmoTypes[ubAmmoType].dDamageModifierTank ); + + INT32 impact_new = impact_original * damagepercentage; + + INT32 tmp = 0; + DamageRiotShield( pSoldier, impact_new, tmp ); + + INT32 impactreduction = impact_original - impact_new / damagepercentage; + + pBullet->iImpactReduction += impactreduction; + + return (pBullet->iImpact <= pBullet->iImpactReduction); +} + BOOLEAN BulletHitMerc( BULLET * pBullet, STRUCTURE * pStructure, BOOLEAN fIntended ) { INT32 iImpact, iDamage; @@ -6365,6 +6464,8 @@ void MoveBullet( INT32 iBullet ) FIXEDPT qWindowBottomHeight; FIXEDPT qWindowTopHeight; + UINT16 lastriotshieldholder = NOBODY; // added by Flugente + pBullet = GetBulletPtr( iBullet ); // CHECK MIN TIME ELAPSED @@ -6913,6 +7014,33 @@ void MoveBullet( INT32 iBullet ) pStructure = gpLocalStructure[iStructureLoop]; if (pStructure && pStructure->sCubeOffset == sDesiredLevel) { + // Flugente: a riot shield would cover the entire front of a tile. We thus cannot check whether the person would be hit, as the bullet might miss a soldier + // Instead, we check whether the structure is indeed a person, whether that person has a riot shield equipped, and whether that shield faces the bullet before it would hit the soldier + // the same bullet cannot hit a shield multiple times + if ( lastriotshieldholder != pStructure->usStructureID && pStructure->fFlags & STRUCTURE_PERSON ) + { + SOLDIERTYPE* pTarget = MercPtrs[pStructure->usStructureID]; + + lastriotshieldholder = pStructure->usStructureID; + + // check for riot shield contact + if ( pTarget && pTarget->IsRiotShieldEquipped( ) ) + { + UINT8 bulletdir_inverse = GetDirectionToGridNoFromGridNo( pTarget->sGridNo, pBullet->sOrigGridNo ); + + // check whether the opposite direction of the bullet is the direction the soldier looks, or adjacant to that. That would mean the shield is between bullet and soldier + if ( bulletdir_inverse == pTarget->ubDirection || bulletdir_inverse == gOneCCDirection[pTarget->ubDirection] || bulletdir_inverse == gOneCDirection[pTarget->ubDirection] ) + { + if ( DamageRiotShield_Bullet( pTarget, pBullet ) ) + { + RemoveBullet( pBullet->iBullet ); + + return; + } + } + } + } + if (((*(pStructure->pShape))[pBullet->bLOSIndexX][pBullet->bLOSIndexY] & AtHeight[iCurrCubesAboveLevelZ]) > 0) { if (pStructure->fFlags & STRUCTURE_PERSON) @@ -7103,6 +7231,32 @@ void MoveBullet( INT32 iBullet ) } } } + + // Flugente: a riot shield would cover the entire front of a tile. We thus cannot check whether the person would be hit, as the bullet might miss a soldier + // Instead, we check whether the structure is indeed a person, whether that person has a riot shield equipped, and whether that shield faces the bullet before it would hit the soldier + if ( lastriotshieldholder != pStructure->usStructureID && pStructure->fFlags & STRUCTURE_PERSON ) + { + SOLDIERTYPE* pTarget = MercPtrs[pStructure->usStructureID]; + + lastriotshieldholder = pStructure->usStructureID; + + // check for riot shield contact + if ( pTarget && pTarget->IsRiotShieldEquipped( ) ) + { + UINT8 bulletdir = GetDirectionToGridNoFromGridNo( pBullet->sOrigGridNo, pTarget->sGridNo ); + + // check whether the direction of the bullet is the direction the soldier looks, or adjacant to that. That would mean the shield is hit after the and soldier + if ( bulletdir == pTarget->ubDirection || bulletdir == gOneCCDirection[pTarget->ubDirection] || bulletdir == gOneCDirection[pTarget->ubDirection] ) + { + if ( DamageRiotShield_Bullet( pTarget, pBullet ) ) + { + RemoveBullet( pBullet->iBullet ); + + return; + } + } + } + } } } } diff --git a/Tactical/LOS.h b/Tactical/LOS.h index f414c6c5..bdb7578f 100644 --- a/Tactical/LOS.h +++ b/Tactical/LOS.h @@ -248,4 +248,7 @@ void CalcRecoilOffset( SOLDIERTYPE *pShooter, FLOAT *dMuzzleOffsetX, FLOAT *dMuz // HEADROCK HAM 5: New function, completely replaces the Counter Force Frequency check. FLOAT CalcCounterForceChange( SOLDIERTYPE * pShooter, UINT32 uiCounterForceAccuracy, FLOAT dCounterForceMax, FLOAT dMuzzleOffset, FLOAT bRecoil, FLOAT dPrevCounterForce, UINT32 uiIntendedBullets ); +// Flugente: riot shields +void DamageRiotShield( SOLDIERTYPE* pSoldier, INT32& rsDamage, INT32& rsSecondaryDamage ); + #endif \ No newline at end of file diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index 1f9b706a..a418fe1a 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -1404,6 +1404,12 @@ INT16 AStarPathfinder::CalcAP(int const terrainCost, UINT8 const direction) } /////////////////////////////////////////////////////////////////////////////////////////////// + // Flugente: riot shields lower movement speed + if ( pSoldier->IsRiotShieldEquipped( ) ) + { + movementAPCost *= gItemSettings.fShieldMovementAPCostModifier; + } + if (terrainCost == TRAVELCOST_FENCE) { switch( movementModeToUseForAPs ) @@ -3629,6 +3635,12 @@ if(!GridNoOnVisibleWorldTile(iDestination)) if ( TERRAIN_IS_HIGH_WATER( gpWorldLevelData[ newLoc ].ubTerrainID ) ) ubAPCost = (ubAPCost * (100 + s->GetBackgroundValue(BG_SWIMMING))) / 100; + // Flugente: riot shields lower movement speed + if ( s->IsRiotShieldEquipped( ) ) + { + ubAPCost *= gItemSettings.fShieldMovementAPCostModifier; + } + // SANDRO - moved backpack check to here // Moa: backpack penalty //if((UsingNewInventorySystem() == true) && FindBackpackOnSoldier( s ) != ITEM_NOT_FOUND ) @@ -4699,6 +4711,13 @@ INT32 PlotPath( SOLDIERTYPE *pSold, INT32 sDestGridNo, INT8 bCopyRoute, INT8 bPl sMovementAPsCost += APBPConstants[AP_STEALTH_MODIFIER]; } } + + // Flugente: riot shields lower movement speed + if ( pSold->IsRiotShieldEquipped( ) ) + { + sMovementAPsCost *= gItemSettings.fShieldMovementAPCostModifier; + } + // Check for backpack //if((UsingNewInventorySystem() == true) && FindBackpackOnSoldier( pSold ) != ITEM_NOT_FOUND ) // sMovementAPsCost += APBPConstants[AP_MODIFIER_PACK]; @@ -4747,11 +4766,13 @@ INT32 PlotPath( SOLDIERTYPE *pSold, INT32 sDestGridNo, INT8 bCopyRoute, INT8 bPl //////////////////////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - This part was modified "a bit" sMovementAPsCost = sTileCost; + // Check for reverse mode if ( pSold->bReverse || bReverse ) { sMovementAPsCost += APBPConstants[AP_REVERSE_MODIFIER]; } + // STOMP traits - Athletics trait decreases movement cost if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSold, ATHLETICS_NT )) { @@ -4818,6 +4839,16 @@ INT32 PlotPath( SOLDIERTYPE *pSold, INT32 sDestGridNo, INT8 bCopyRoute, INT8 bPl sPointsRun += APBPConstants[AP_STEALTH_MODIFIER]; } } + + // Flugente: riot shields lower movement speed + if ( pSold->IsRiotShieldEquipped( ) ) + { + sPointsWalk *= gItemSettings.fShieldMovementAPCostModifier; + sPointsCrawl *= gItemSettings.fShieldMovementAPCostModifier; + sPointsSwat *= gItemSettings.fShieldMovementAPCostModifier; + sPointsRun *= gItemSettings.fShieldMovementAPCostModifier; + } + // Check for backpack //if((UsingNewInventorySystem() == true) && FindBackpackOnSoldier( pSold ) != ITEM_NOT_FOUND ) //{ diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index be005dcf..e21d55e4 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -474,6 +474,12 @@ INT16 ActionPointCost( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bDir, UINT16 u sPoints = max(1, (INT16)((sPoints * (100 - gSkillTraitValues.ubATAPsMovementReduction) / 100) + 0.5)); } + // Flugente: riot shields lower movement speed + if ( pSoldier->IsRiotShieldEquipped( ) ) + { + sPoints *= gItemSettings.fShieldMovementAPCostModifier; + } + // Flugente: scuba fins reduce movement cost in water, but increase cost on land if ( pSoldier->inv[LEGPOS].exists() && HasItemFlag( pSoldier->inv[LEGPOS].usItem, SCUBA_FINS ) ) { @@ -715,6 +721,13 @@ INT16 EstimateActionPointCost( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bDir, { sPoints = max(1, (INT16)((sPoints * (100 - gSkillTraitValues.ubATAPsMovementReduction) / 100) + 0.5)); } + + // Flugente: riot shields lower movement speed + if ( pSoldier->IsRiotShieldEquipped( ) ) + { + sPoints *= gItemSettings.fShieldMovementAPCostModifier; + } + // Check if doors if not player's merc (they have to open them manually) if ( sSwitchValue == TRAVELCOST_DOOR && pSoldier->bTeam != gbPlayerNum ) { @@ -4209,17 +4222,23 @@ INT16 GetAPsProne( SOLDIERTYPE *pSoldier, BOOLEAN fBackpackCheck ) // SANDRO - Added feature to calculate start run cost (for Athletics trait) INT16 GetAPsStartRun( SOLDIERTYPE *pSoldier ) { + INT16 val = APBPConstants[AP_START_RUN_COST]; + + // Flugente: riot shields lower movement speed + if ( pSoldier->IsRiotShieldEquipped( ) ) + val *= gItemSettings.fShieldMovementAPCostModifier; + // Athletics trait - if ( HAS_SKILL_TRAIT( pSoldier, ATHLETICS_NT ) && gGameOptions.fNewTraitSystem ) - return( max( 1, (INT16)((APBPConstants[AP_START_RUN_COST] * (100 - gSkillTraitValues.ubATAPsMovementReduction)/100) + 0.5 )) ); - else - return( APBPConstants[AP_START_RUN_COST] ); + if( HAS_SKILL_TRAIT( pSoldier, ATHLETICS_NT ) && gGameOptions.fNewTraitSystem ) + val = max( 1, (INT16)((val * (100 - gSkillTraitValues.ubATAPsMovementReduction) / 100) + 0.5) ); + + return val; } // SANDRO - Added feature to calculate APs for handling doors (for Ambidextrous trait) INT16 GetAPsToOpenDoor( SOLDIERTYPE *pSoldier ) { - if( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT ) ) + if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT ) ) return( max( 1, (INT16)((APBPConstants[AP_OPEN_DOOR] * (100 - gSkillTraitValues.ubAMHandleDoorsAPsReduction)/100) + 0.5 )) ); else return( APBPConstants[AP_OPEN_DOOR] ); diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 265e26ad..c4a0f379 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -1510,7 +1510,8 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) // ONLY DO THIS IF CERTAIN CONDITIONS ARISE! // For one, only do for mercs! - if ( pSoldier->ubBodyType <= REGFEMALE ) + // Flugente: don't do this while equipping a shield, as this renders them almost useless + if ( pSoldier->ubBodyType <= REGFEMALE && !pSoldier->IsRiotShieldEquipped( ) ) { // Secondly, don't if we are going to collapse if ( pSoldier->stats.bLife >= OKLIFE && pSoldier->bBreath > 0 && pSoldier->pathing.bLevel == 0 ) diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 0db485cf..31ce8e61 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -8917,7 +8917,7 @@ void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier pSoldier->sAniDelay = (INT16)uiTerrainDelay; - // If a moving animation and w/re on drugs, increase speed.... + // If a moving animation and we're on drugs, increase speed.... if ( gAnimControl[pSoldier->usAnimState].uiFlags & ANIM_MOVING ) { if ( pSoldier->newdrugs.size[DRUG_EFFECT_AP] ) @@ -8949,6 +8949,7 @@ void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier pSoldier->sAniDelay = (INT16)(pSoldier->sAniDelay * 2); } } + // SANDRO - STOMP traits - bonus to movement speed for Athletics if ( gGameOptions.fNewTraitSystem && (gAnimControl[pSoldier->usAnimState].uiFlags & ANIM_MOVING) ) { @@ -8958,6 +8959,12 @@ void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier } } + // Flugente: riot shields lower movement speed + if ( pSoldier->IsRiotShieldEquipped( ) ) + { + pSoldier->sAniDelay = gItemSettings.fShieldMovementAPCostModifier * pSoldier->sAniDelay; + } + //pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 ); } @@ -9900,6 +9907,33 @@ UINT8 SOLDIERTYPE::SoldierTakeDamage( INT8 bHeight, INT16 sLifeDeduct, INT16 sBr default: break; } + + // Flugente: do we have a riot shield equipped? + if ( this->IsRiotShieldEquipped( ) ) + { + // if we have equipped a riot shield and are being attacked in melee, ignore damage from some directions + if ( ubReason == TAKE_DAMAGE_BLADE || ubReason == TAKE_DAMAGE_HANDTOHAND || ubReason == TAKE_DAMAGE_TENTACLES ) + { + if ( ubAttacker != NOBODY && MercPtrs[ubAttacker] ) + { + UINT8 attackdir_inverse = GetDirectionToGridNoFromGridNo( this->sGridNo, MercPtrs[ubAttacker]->sGridNo ); + + // if the shield faces the direction of the attacker, we block the attack + if ( attackdir_inverse == this->ubDirection || attackdir_inverse == gOneCCDirection[this->ubDirection] || attackdir_inverse == gOneCDirection[this->ubDirection] ) + { + // damaging even a wooden shield is hard. For that reason we lower the initial damage. + INT32 damage = sLifeDeduct / 3; + INT32 breathdamage = sBreathLoss; + DamageRiotShield( this, damage, breathdamage ); + + sLifeDeduct = damage; + sBreathLoss = breathdamage; + + PlayJA2Sample( (UINT32)(S_WOOD_IMPACT1 + Random(3)), RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + } + } + } + } // Deduct life!, Show damage if we want! bOldLife = this->stats.bLife; @@ -17360,7 +17394,7 @@ BOOLEAN SOLDIERTYPE::UseSkill( UINT8 iSkill, INT32 usMapPos, UINT8 ID ) case SKILLS_RADIO_JAM: return JamCommunications( ); break; - + case SKILLS_RADIO_SCAN_FOR_JAM: return ScanForJam( ); break; @@ -19244,6 +19278,79 @@ UINT16 SOLDIERTYPE::GetInteractiveActionSkill( INT32 sGridNo, UINT8 usLevel, UIN return 0; } +// Flugente: riot shields +OBJECTTYPE* SOLDIERTYPE::GetEquippedRiotShield() +{ + OBJECTTYPE* pObj = NULL; + + if ( this->inv[HANDPOS].exists( ) && Item[this->inv[HANDPOS].usItem].usRiotShieldStrength > 0 ) + pObj = &(this->inv[HANDPOS]); + + if ( this->inv[SECONDHANDPOS].exists( ) && Item[this->inv[SECONDHANDPOS].usItem].usRiotShieldStrength > 0 ) + pObj = &(this->inv[SECONDHANDPOS]); + + return pObj; +} + + +BOOLEAN SOLDIERTYPE::IsRiotShieldEquipped() +{ + // shield is not erect if prone + if ( gAnimControl[this->usAnimState].ubEndHeight == ANIM_PRONE ) + return FALSE; + + // no shield while swimming + if ( TERRAIN_IS_HIGH_WATER(this->sGridNo) ) + return FALSE; + + return (GetEquippedRiotShield() != NULL); +} + +void SOLDIERTYPE::DestroyEquippedRiotShield( ) +{ + // create graphic (destroyed shield item?) + OBJECTTYPE* pObj = GetEquippedRiotShield( ); + + if ( pObj ) + { + if ( Item[pObj->usItem].usBuddyItem ) + { + CreateItem( Item[pObj->usItem].usBuddyItem, 100, pObj ); + + // Flugente: why would we keep a piece of scrap in our hands in the first place? just drop it to the ground + AddItemToPool( this->sGridNo, pObj, 1, this->pathing.bLevel, 0, -1 ); + + NotifySoldiersToLookforItems( ); + } + + // Delete object + DeleteObj( pObj ); + } + + // dirty interface panel + DirtyMercPanelInterface( this, DIRTYLEVEL2 ); + + this->DoMercBattleSound( BATTLE_SOUND_CURSE1 ); +} + +void SOLDIERTYPE::RiotShieldTakeDamage( INT32 sDamage ) +{ + OBJECTTYPE* pObj = GetEquippedRiotShield(); + + if ( pObj ) + { + PlayJA2Sample( (UINT32)(S_METAL_IMPACT1 + +Random( 3 )), RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + + (*pObj)[0]->data.objectStatus -= sDamage; + + // if shield should have been destroyed, do so + if ( (*pObj)[0]->data.objectStatus < 1 ) + { + DestroyEquippedRiotShield( ); + } + } +} + INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; //,savedOurTurn; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index a9c8e570..8360625f 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1943,6 +1943,12 @@ public: // Flugente: interactive actions UINT16 GetInteractiveActionSkill( INT32 sGridNo, UINT8 usLevel, UINT16 usType ); + + // Flugente: riot shields + OBJECTTYPE* GetEquippedRiotShield(); + BOOLEAN IsRiotShieldEquipped(); + void DestroyEquippedRiotShield(); + void RiotShieldTakeDamage(INT32 sDamage); ////////////////////////////////////////////////////////////////////////////// }; // SOLDIERTYPE; diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index 11d0ccf5..f5d65d37 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -1571,6 +1571,28 @@ BOOLEAN DamageSoldierFromBlast( UINT8 ubPerson, UINT8 ubOwner, INT32 sBombGridNo return( FALSE ); } + // Flugente: do we have a riot shield equipped? + if ( pSoldier->IsRiotShieldEquipped( ) ) + { + UINT8 attackdir_inverse = GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, sBombGridNo ); + + // if the shield faces the direction of the attack, block it + if ( attackdir_inverse == pSoldier->ubDirection || attackdir_inverse == gOneCCDirection[pSoldier->ubDirection] || attackdir_inverse == gOneCDirection[pSoldier->ubDirection] ) + { + INT32 damage = sWoundAmt; + INT32 breathdamage = sBreathAmt; + DamageRiotShield( pSoldier, damage, breathdamage ); + + sWoundAmt = damage; + sBreathAmt = breathdamage; + + if ( sWoundAmt == 0 && sBreathAmt == 0 ) + { + return FALSE; + } + } + } + // Lesh: if flashbang // check if soldier is outdoor and situated farther that half explosion radius and not underground usHalfExplosionRadius = Explosive[Item[usItem].ubClassIndex].ubRadius / 2; @@ -4238,6 +4260,9 @@ void HandleExplosionQueue( void ) } } +// Flugente: riot shields +extern void ShowRiotShield( SOLDIERTYPE* pSoldier ); + // Flugente: show warnings around armed timebombs both in map and inventories void HandleExplosionWarningAnimations( ) { @@ -4315,6 +4340,25 @@ void HandleExplosionWarningAnimations( ) } } } + + // show riot shields + SOLDIERTYPE* pSoldier; + UINT16 cnt = gTacticalStatus.Team[0].bFirstID; + for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[CIV_TEAM].bLastID; ++cnt, pSoldier++ ) + { + if ( pSoldier && pSoldier->bActive && pSoldier->bInSector ) + { + if ( (pSoldier->ubDirection == EAST || + pSoldier->ubDirection == SOUTHEAST || + pSoldier->ubDirection == SOUTH || + pSoldier->ubDirection == SOUTHWEST || + pSoldier->ubDirection == NORTHEAST) + && pSoldier->IsRiotShieldEquipped( ) ) + { + ShowRiotShield( pSoldier ); + } + } + } } void DecayBombTimers( void ) diff --git a/TileEngine/renderworld.cpp b/TileEngine/renderworld.cpp index 413fe50f..0802cb49 100644 --- a/TileEngine/renderworld.cpp +++ b/TileEngine/renderworld.cpp @@ -53,6 +53,11 @@ #include "Render Z.h" /////////////////////////// +#include "Utilities.h" + +UINT32 guiShieldGraphic = 0; +BOOLEAN fShieldGraphicInit = FALSE; + extern CHAR8 gDebugStr[128]; extern BOOLEAN fLandLayerDirty = TRUE; @@ -603,6 +608,50 @@ void DeleteFromWorld( UINT16 usTileIndex, UINT32 uiRenderTiles, UINT16 usIndex ) void RenderHighlight( INT16 sMouseX_M, INT16 sMouseY_M, INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); BOOLEAN CheckRenderCenter( INT16 sNewCenterX, INT16 sNewCenterY ); +// Flugente: display a riot shield +void ShowRiotShield( SOLDIERTYPE* pSoldier ) +{ + if (pSoldier) + { + if (!fShieldGraphicInit) + { + VOBJECT_DESC VObjectDesc; + + // Flugente: enemy role symbols + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + FilenameForBPP("Tilecache\\riotshield.sti", VObjectDesc.ImageFile); + if (!AddVideoObject(&VObjectDesc, &guiShieldGraphic)) + AssertMsg(0, "Missing Tilecache\\riotshield.sti"); + + fShieldGraphicInit = TRUE; + } + + // Get screen pos of gridno...... + INT16 sScreenX, sScreenY; + GetGridNoScreenXY(pSoldier->sGridNo, &sScreenX, &sScreenY); + + // redraw background to stop weird graphic remnants remaining + // but don*t do so while scrolling, because that looks weird + if ( !gfScrollPending && !gfScrollInertia) + { + INT32 iBack = RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sScreenX - 50, sScreenY - 60, sScreenX + 50, sScreenY + 35); + + if (iBack != -1) + { + SetBackgroundRectFilled(iBack); + } + } + + UINT16 offset = 0; + OBJECTTYPE* pObj = pSoldier->GetEquippedRiotShield(); + + if (pObj) + offset = Item[pObj->usItem].usRiotShieldGraphic; + + BltVideoObjectFromIndex( FRAME_BUFFER, guiShieldGraphic, offset * 8 + pSoldier->ubDirection, sScreenX - 20, sScreenY - 60, VO_BLT_TRANSSHADOW, NULL ); + } +} + BOOLEAN RevealWalls(INT16 sX, INT16 sY, INT16 sRadius) { LEVELNODE *pStruct; @@ -1837,6 +1886,16 @@ void RenderTiles(UINT32 uiFlags, INT32 iStartPointX_M, INT32 iStartPointY_M, INT usImageIndex = pSoldier->CryoAniFrame( ); } + // Flugente: riot shields + if ( pSoldier && + (pSoldier->ubDirection == NORTH || + pSoldier->ubDirection == NORTHWEST || + pSoldier->ubDirection == WEST) + && pSoldier->IsRiotShieldEquipped( ) ) + { + ShowRiotShield( pSoldier ); + } + uiDirtyFlags=BGND_FLAG_SINGLE|BGND_FLAG_ANIMATED| BGND_FLAG_MERC; break; } diff --git a/Utils/XML_Items.cpp b/Utils/XML_Items.cpp index 6dcb8763..7a458879 100644 --- a/Utils/XML_Items.cpp +++ b/Utils/XML_Items.cpp @@ -291,7 +291,9 @@ itemStartElementHandle(void *userData, const XML_Char *name, const XML_Char **at strcmp(name, "cigarette" ) == 0 || strcmp(name, "usPortionSize" ) == 0 || strcmp(name, "diseaseprotectionface" ) == 0 || - strcmp(name, "diseaseprotectionhand" ) == 0)) + strcmp(name, "diseaseprotectionhand" ) == 0|| + strcmp(name, "usRiotShieldStrength" ) == 0 || + strcmp(name, "usRiotShieldGraphic" ) == 0)) { pData->curElement = ELEMENT_PROPERTY; //DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("itemStartElementHandle: going into element, name = %s",name) ); @@ -1499,6 +1501,16 @@ itemEndElementHandle(void *userData, const XML_Char *name) if ( val ) pData->curItem.usItemFlag |= DISEASEPROTECTION_2; } + else if ( strcmp( name, "usRiotShieldStrength" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curItem.usRiotShieldStrength = min( 100, (UINT16)atol( pData->szCharData ) ); + } + else if ( strcmp( name, "usRiotShieldGraphic" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curItem.usRiotShieldGraphic = (UINT16)atol( pData->szCharData ); + } --pData->maxReadDepth; } @@ -2128,7 +2140,7 @@ BOOLEAN WriteItemStats() FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].usSpotting ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].sBackpackWeightModifier); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].fAllowClimbing); - FilePrintf(hFile, "\t\t%d\r\n", Item[cnt].cigarette ); + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].cigarette ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].usPortionSize ); if ( Item[cnt].usItemFlag & DISEASEPROTECTION_1 ) @@ -2136,6 +2148,9 @@ BOOLEAN WriteItemStats() if ( Item[cnt].usItemFlag & DISEASEPROTECTION_2 ) FilePrintf( hFile, "\t\t%d\r\n", 1 ); + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].usRiotShieldStrength ); + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].usRiotShieldGraphic ); + FilePrintf(hFile,"\t\r\n"); } FilePrintf(hFile,"\r\n"); diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 681130d0..0fe9717e 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -8388,6 +8388,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|反|器|材|弹|药", //L"|A|n|t|i|-|M|a|t|e|r|i|a|l |A|m|m|o", L"|面|部|防|护", //L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|感|染|防|护", //L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8432,6 +8433,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \n这种弹药可以破坏发光的墙\n或者其他不同种类的物品。", //L"\n \nThis ammo can destroy light walls\nand various other objects.", L"\n \n如果你脸上带了这个,这就将降低\n传播给其他人的几率。", //L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \n如果保存在物品栏\n降低\n传染给其他人的几率。", //L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 4731b4ed..ff30936b 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -8405,6 +8405,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|n|t|i|-|M|a|t|e|r|i|a|l |A|m|m|o", // TODO.Translate L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8449,6 +8450,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nThis ammo can destroy light walls\nand various other objects.", // TODO.Translate L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index dc349bf5..8a3ed6bf 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -8386,6 +8386,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|n|t|i|-|M|a|t|e|r|i|e|l |A|m|m|o", L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8430,6 +8431,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nThis ammo can destroy light walls\nand various other objects.", L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 9d84d251..95134007 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -8392,6 +8392,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|n|t|i|-|M|a|t|e|r|i|a|l |A|m|m|o", // TODO.Translate L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8436,6 +8437,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nThis ammo can destroy light walls\nand various other objects.", // TODO.Translate L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index bf3d0506..05ab0bab 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -8221,6 +8221,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|n|t|i|-|M|a|t|e|r|i|a|l |A|m|m|o", // TODO.Translate L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8265,6 +8266,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nThis ammo can destroy light walls\nand various other objects.", // TODO.Translate L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 84a2e16d..3a786e93 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -8395,6 +8395,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|n|t|i|-|M|a|t|e|r|i|a|l |A|m|m|o", // TODO.Translate L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8439,6 +8440,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nThis ammo can destroy light walls\nand various other objects.", // TODO.Translate L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index bb8a0e6c..11dfdb86 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -8407,6 +8407,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|A|n|t|i|-|M|a|t|e|r|i|a|l |A|m|m|o", // TODO.Translate L"|F|a|c|e |P|r|o|t|e|c|t|i|o|n", L"|I|n|f|e|c|t|i|o|n |P|r|o|t|e|c|t|i|o|n", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8451,6 +8452,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nThis ammo can destroy light walls\nand various other objects.", // TODO.Translate L"\n \nIf worn on your face, this will lower\nthe chance to be infected by other people.", L"\n \nIf kept in your inventory, this will\nlower\nthe chance to be infected by other people.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]= diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 0ee0904c..709066d9 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -8390,6 +8390,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|С|н|а|р|я|д |п|р|о|т|и|в |б|р|о|н|и", L"|З|а|щ|и|т|а |д|л|я |л|и|ц|а", L"|И|н|ф|е|к|ц|и|о|н|н|а|я |з|а|щ|и|т|а", // 39 + L"|S|h|i|e|l|d", // TODO.Translate }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8434,6 +8435,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \nЭтот боеприпас может уничтожать тонкие стены\nи некоторые другие объекты.", L"\n \nЕсли одето на лицо, понижает шанс\nзаражения от других людей.", L"\n \nЕсли хранить в своём кармане,\nпонижается шанс заражения\n от других людей.", + L"\n \nIf equipped in a hand, this will block incoming damage.", // TODO.Translate }; STR16 szUDBAdvStatsTooltipText[]=