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
This commit is contained in:
Flugente
2017-07-29 21:18:13 +00:00
parent 8a82802932
commit 11729a132e
22 changed files with 498 additions and 19 deletions
+3
View File
@@ -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)
+4
View File
@@ -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;
+9 -9
View File
@@ -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
+17
View File
@@ -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 )
+154
View File
@@ -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;
}
}
}
}
}
}
}
+3
View File
@@ -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
+31
View File
@@ -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 )
//{
+24 -5
View File
@@ -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] );
+2 -1
View File
@@ -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 )
+109 -2
View File
@@ -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;
+6
View File
@@ -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;
+44
View File
@@ -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 )
+59
View File
@@ -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;
}
+17 -2
View File
@@ -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<usSpotting>%d</usSpotting>\r\n", Item[cnt].usSpotting );
FilePrintf(hFile,"\t\t<sBackpackWeightModifier>%d</sBackpackWeightModifier>\r\n", Item[cnt].sBackpackWeightModifier);
FilePrintf(hFile,"\t\t<fAllowClimbing>%d</fAllowClimbing>\r\n", Item[cnt].fAllowClimbing);
FilePrintf(hFile, "\t\t<cigarette>%d</cigarette>\r\n", Item[cnt].cigarette );
FilePrintf(hFile,"\t\t<cigarette>%d</cigarette>\r\n", Item[cnt].cigarette );
FilePrintf(hFile,"\t\t<usPortionSize>%d</usPortionSize>\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<diseaseprotectionhand>%d</diseaseprotectionhand>\r\n", 1 );
FilePrintf(hFile,"\t\t<usRiotShieldStrength>%d</usRiotShieldStrength>\r\n", Item[cnt].usRiotShieldStrength );
FilePrintf(hFile,"\t\t<usRiotShieldGraphic>%d</usRiotShieldGraphic>\r\n", Item[cnt].usRiotShieldGraphic );
FilePrintf(hFile,"\t</ITEM>\r\n");
}
FilePrintf(hFile,"</ITEMLIST>\r\n");
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=
+2
View File
@@ -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[]=