Files
source/Tactical/SkillCheck.cpp
T
2024-12-01 18:23:52 +02:00

1058 lines
32 KiB
C++

#include "SkillCheck.h"
#include "Soldier Profile.h"
#include "Random.h"
#include "Items.h"
#include "Dialogue Control.h"
#include "Overhead.h"
#include "Soldier macros.h"
#include "Isometric Utils.h"
#include "Morale.h"
#include "drugs and alcohol.h"
#include "strategicmap.h"
// added by SANDRO
#include "GameSettings.h"
#include "Animation Data.h"
#include "Soldier Control.h"
#include "Interface.h" // added by Flugente for zBackground
#include "DynamicDialogue.h" // added by Flugente
extern void ReducePointsForHunger( SOLDIERTYPE *pSoldier, UINT32 *pusPoints );
INT16 EffectiveStrength( SOLDIERTYPE *pSoldier, BOOLEAN fTrainer )
{
INT8 bBandaged;
INT32 iEffStrength;
// Effective strength is:
// 1/2 full strength
// plus 1/2 strength scaled according to how hurt we are
bBandaged = pSoldier->stats.bLifeMax - pSoldier->stats.bLife - pSoldier->bBleeding;
if (pSoldier->stats.bStrength > 0)
{
if ( fTrainer )
{
iEffStrength = pSoldier->stats.bStrength / 2;
iEffStrength += ( pSoldier->stats.bStrength / 2) * (pSoldier->stats.bLife + bBandaged / 2) / (pSoldier->stats.bLifeMax);
}
else
{
iEffStrength = ( pSoldier->stats.bStrength + pSoldier->bExtraStrength )/ 2;
iEffStrength += ( (pSoldier->stats.bStrength + pSoldier->bExtraStrength) / 2) * (pSoldier->stats.bLife + bBandaged / 2) / (pSoldier->stats.bLifeMax);
}
}
else
{
iEffStrength = 0;
}
if (AM_A_ROBOT(pSoldier))
{
if (Item[pSoldier->inv[ROBOT_CHASSIS_SLOT].usItem].bRobotStrBonus > 0)
{
iEffStrength += Item[pSoldier->inv[ROBOT_CHASSIS_SLOT].usItem].bRobotStrBonus;
}
}
// Flugente: diseases can affect stat effectivity
INT16 diseaseeffect = 0;
if ( gGameExternalOptions.fDisease )
{
for ( int i = 0; i < NUM_DISEASES; ++i )
diseaseeffect += Disease[i].sEffStat[INFST_STR] * pSoldier->GetDiseaseMagnitude( i );
}
iEffStrength = (iEffStrength * (100 + diseaseeffect + pSoldier->GetBackgroundValue( BG_STRENGTH ))) / 100;
// ATE: Make sure at least 2...
iEffStrength = __max( iEffStrength, 2 );
return( (INT16) iEffStrength );
}
INT16 EffectiveWisdom( SOLDIERTYPE * pSoldier)
{
INT32 iEffWisdom;
iEffWisdom = pSoldier->stats.bWisdom;
iEffWisdom += pSoldier->bExtraWisdom;
iEffWisdom = EffectStatForBeingDrunk( pSoldier, iEffWisdom );
// Flugente: diseases can affect stat effectivity
INT16 diseaseeffect = 0;
for ( int i = 0; i < NUM_DISEASES; ++i )
diseaseeffect += Disease[i].sEffStat[INFST_WIS] * pSoldier->GetDiseaseMagnitude( i );
iEffWisdom = (iEffWisdom * (100 + diseaseeffect + pSoldier->GetBackgroundValue( BG_WISDOM ))) / 100;
return( (INT16) iEffWisdom );
}
INT16 EffectiveAgility( SOLDIERTYPE * pSoldier, BOOLEAN fTrainer )
{
INT32 iEffAgility;
iEffAgility = pSoldier->stats.bAgility;
if (AM_A_ROBOT(pSoldier))
{
if (Item[pSoldier->inv[ROBOT_CHASSIS_SLOT].usItem].bRobotAgiBonus > 0)
{
iEffAgility += Item[pSoldier->inv[ROBOT_CHASSIS_SLOT].usItem].bRobotAgiBonus;
}
}
if ( !fTrainer )
iEffAgility += pSoldier->bExtraAgility;
iEffAgility = EffectStatForBeingDrunk( pSoldier, iEffAgility );
if ( pSoldier->sWeightCarriedAtTurnStart > 100 )
{
iEffAgility = (iEffAgility * 100) / pSoldier->sWeightCarriedAtTurnStart;
}
// Flugente: diseases can affect stat effectivity
INT16 diseaseeffect = 0;
for ( int i = 0; i < NUM_DISEASES; ++i )
diseaseeffect += Disease[i].sEffStat[INFST_AGI] * pSoldier->GetDiseaseMagnitude( i );
iEffAgility = (iEffAgility * (100 + diseaseeffect + pSoldier->GetBackgroundValue( BG_AGILITY ))) / 100;
return( (INT16) iEffAgility );
}
INT8 EffectiveMechanical( SOLDIERTYPE * pSoldier )
{
INT32 iEffMechanical;
iEffMechanical = pSoldier->stats.bMechanical;
iEffMechanical = EffectStatForBeingDrunk( pSoldier, iEffMechanical );
iEffMechanical = (iEffMechanical * (100 + pSoldier->GetBackgroundValue(BG_MECHANICAL))) / 100;
return( (INT8) iEffMechanical );
}
INT8 EffectiveExplosive( SOLDIERTYPE * pSoldier )
{
INT32 iEffExplosive;
iEffExplosive = pSoldier->stats.bExplosive;
iEffExplosive = EffectStatForBeingDrunk( pSoldier, iEffExplosive );
iEffExplosive = (iEffExplosive * (100 + pSoldier->GetBackgroundValue(BG_EXPLOSIVE_ASSIGN))) / 100;
return( (INT8) iEffExplosive );
}
INT8 EffectiveMedical( SOLDIERTYPE * pSoldier )
{
INT32 iEffMedical;
iEffMedical = pSoldier->stats.bMedical;
iEffMedical = EffectStatForBeingDrunk( pSoldier, iEffMedical );
iEffMedical = (iEffMedical * (100 + pSoldier->GetBackgroundValue(BG_MEDICAL))) / 100;
return( (INT8) iEffMedical );
}
INT8 EffectiveLeadership( SOLDIERTYPE * pSoldier )
{
INT32 iEffLeadership;
INT8 bDrunkLevel;
iEffLeadership = pSoldier->stats.bLeadership;
// if we are drunk, effect leader ship in a +ve way...
bDrunkLevel = GetDrunkLevel( pSoldier );
if ( bDrunkLevel == FEELING_GOOD )
{
iEffLeadership = ( iEffLeadership * 120 / 100 );
}
iEffLeadership = (iEffLeadership * (100 + pSoldier->GetBackgroundValue(BG_LEADERSHIP))) / 100;
return( (INT8) iEffLeadership );
}
// Flugente: modifiers that depend on physical position (squadleader, afraid of heights) only apply if fTactical is TRUE
INT8 EffectiveExpLevel( SOLDIERTYPE * pSoldier, BOOLEAN fTactical )
{
INT32 iEffExpLevel;
INT8 bDrunkLevel;
INT32 iExpModifier[] =
{ 0, // SOBER
0, // Feeling good
-1, // Borderline
-2, // Drunk
0, // Hung
};
iEffExpLevel = pSoldier->stats.bExpLevel;
// SANDRO - STOMP traits - Squadleader bonus to effective level
if ( fTactical && gGameOptions.fNewTraitSystem && IS_MERC_BODY_TYPE( pSoldier ))
{
iEffExpLevel += (gSkillTraitValues.ubSLEffectiveLevelInRadius * GetSquadleadersCountInVicinity( pSoldier, TRUE, FALSE ));
}
bDrunkLevel = GetDrunkLevel( pSoldier );
iEffExpLevel = iEffExpLevel + iExpModifier[ bDrunkLevel ];
if (pSoldier->ubProfile != NO_PROFILE)
{
// Flugente: drugs can temporarily cause a merc to be claustrophobic
if ( DoesMercHaveDisability( pSoldier, CLAUSTROPHOBIC ) && pSoldier->bActive && pSoldier->bInSector )
{
INT8 sectorz = pSoldier->bSectorZ;
if ( SPY_LOCATION( pSoldier->bAssignment ) )
sectorz -= 10;
// claustrophobic!
if ( sectorz > 0 )
iEffExpLevel -= 2;
}
if ( DoesMercHaveDisability( pSoldier, FEAR_OF_INSECTS ) && MercIsInTropicalSector( pSoldier ) )
{
// SANDRO - fear of insects, and we are in tropical sector
iEffExpLevel -= 1;
}
else if ( fTactical & DoesMercHaveDisability( pSoldier, AFRAID_OF_HEIGHTS ) && pSoldier->pathing.bLevel > 0 )
{
// Flugente: fear of heights
iEffExpLevel -= 2;
}
}
// Flugente: diseases can affect stat effectivity
INT16 diseaseeffect = 0;
for ( int i = 0; i < NUM_DISEASES; ++i )
diseaseeffect += Disease[i].sEffStat[INFST_EXP] * pSoldier->GetDiseaseMagnitude( i );
iEffExpLevel += diseaseeffect + pSoldier->bExtraExpLevel;
if (iEffExpLevel > 10)
{
// can't go over 10 - SANDRO
return( 10 );
}
else if (iEffExpLevel < 1)
{
// can't go below 1
return( 1 );
}
return( (INT8) iEffExpLevel );
}
INT8 EffectiveMarksmanship( SOLDIERTYPE * pSoldier )
{
INT32 iEffMarksmanship;
iEffMarksmanship = pSoldier->stats.bMarksmanship;
iEffMarksmanship = EffectStatForBeingDrunk( pSoldier, iEffMarksmanship );
iEffMarksmanship = (iEffMarksmanship * (100 + pSoldier->GetBackgroundValue(BG_MARKSMANSHIP))) / 100;
return( (INT8) iEffMarksmanship );
}
INT16 EffectiveDexterity( SOLDIERTYPE * pSoldier, BOOLEAN fTrainer )
{
INT32 iEffDexterity;
iEffDexterity = pSoldier->stats.bDexterity;
if (AM_A_ROBOT(pSoldier))
{
if (Item[pSoldier->inv[ROBOT_CHASSIS_SLOT].usItem].bRobotDexBonus > 0)
{
iEffDexterity += Item[pSoldier->inv[ROBOT_CHASSIS_SLOT].usItem].bRobotDexBonus;
}
}
if ( !fTrainer )
iEffDexterity += pSoldier->bExtraDexterity;
iEffDexterity = EffectStatForBeingDrunk( pSoldier, iEffDexterity );
// Flugente: diseases can affect stat effectivity
INT16 diseaseeffect = 0;
for ( int i = 0; i < NUM_DISEASES; ++i )
diseaseeffect += Disease[i].sEffStat[INFST_DEX] * pSoldier->GetDiseaseMagnitude( i );
iEffDexterity = (iEffDexterity * (100 + diseaseeffect + pSoldier->GetBackgroundValue( BG_DEXTERITY ))) / 100;
return( (INT16) iEffDexterity );
}
UINT8 GetPenaltyForFatigue( SOLDIERTYPE *pSoldier )
{
UINT8 ubPercentPenalty;
if ( pSoldier->bBreathMax >= 85 ) ubPercentPenalty = 0;
else if ( pSoldier->bBreathMax >= 70 ) ubPercentPenalty = 10;
else if ( pSoldier->bBreathMax >= 50 ) ubPercentPenalty = 25;
else if ( pSoldier->bBreathMax >= 30 ) ubPercentPenalty = 50;
else if ( pSoldier->bBreathMax >= 15 ) ubPercentPenalty = 75;
else if ( pSoldier->bBreathMax > 0 ) ubPercentPenalty = 90;
else ubPercentPenalty = 100;
return( ubPercentPenalty );
}
void ReducePointsForFatigue( SOLDIERTYPE *pSoldier, UINT32 *pusPoints )
{
*pusPoints -= (*pusPoints * GetPenaltyForFatigue( pSoldier )) / 100;
}
void ReducePointsForFatigue( SOLDIERTYPE *pSoldier, INT32 *psPoints )
{
*psPoints -= ( *psPoints * GetPenaltyForFatigue( pSoldier ) ) / 100;
}
INT32 GetSkillCheckPenaltyForFatigue( SOLDIERTYPE *pSoldier, INT32 iSkill )
{
// use only half the full effect of fatigue for skill checks
return( ( (iSkill * GetPenaltyForFatigue( pSoldier ) ) / 100) / 2 );
}
INT32 SkillCheck( SOLDIERTYPE * pSoldier, INT8 bReason, INT8 bChanceMod )
{
INT32 iSkill;
INT32 iChance, iReportChance;
INT32 iRoll, iMadeItBy;
SOLDIERTYPE * pTeamSoldier;
INT8 bBuddyIndex;
BOOLEAN fForceDamnSound = FALSE;
iReportChance = -1;
switch (bReason)
{
case LOCKPICKING_CHECK:
case ELECTRONIC_LOCKPICKING_CHECK:
INT8 bSlot;
fForceDamnSound = TRUE;
iSkill = EffectiveMechanical( pSoldier );
if (iSkill == 0)
{
break;
}
// adjust skill based on wisdom (knowledge)
iSkill = iSkill * (EffectiveWisdom( pSoldier ) + 100) / 200;
// and dexterity (clumsy?)
iSkill = iSkill * (EffectiveDexterity( pSoldier, FALSE ) + 100) / 200;
// factor in experience
iSkill = iSkill + EffectiveExpLevel( pSoldier ) * 3;
// SANDRO - STOMP traits - Technician trait lockpicking bonus
if ( gGameOptions.fNewTraitSystem )
{
iSkill += gSkillTraitValues.bCheckModifierLockpicking; // reduce skill value for untrained mercs
// Simply reduce the skill value for this as usual
if (bReason == ELECTRONIC_LOCKPICKING_CHECK)
{
iSkill /= 2;
}
// then add a flat bonus (undivided by the electronic lock penalty above)
if ( HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ))
{
iSkill += gSkillTraitValues.usTELockpickingBonus * NUM_SKILL_TRAITS( pSoldier, TECHNICIAN_NT );
}
}
else // original code
{
if (HAS_SKILL_TRAIT( pSoldier, LOCKPICKING_OT ) )
{
// if we specialize in picking locks...
iSkill += gbSkillTraitBonus[LOCKPICKING_OT] * NUM_SKILL_TRAITS( pSoldier, LOCKPICKING_OT );
}
if (bReason == ELECTRONIC_LOCKPICKING_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS_OT)) )
{
// if we are unfamiliar with electronics...
iSkill /= 2;
}
}
// adjust chance based on status of kit
bSlot = FindLocksmithKit( pSoldier );
if (bSlot == NO_SLOT)
{
// this should never happen, but might as well check...
iSkill = 0;
}
else
{
//JMich_SkillsModifiers: Modifying the skill by the bonus from the lockpick, then reducing due to status
iSkill += Item[pSoldier->inv[bSlot].usItem].LockPickModifier;
}
//JMich_SkillsModifiers: If skill goes negative due to crappy lockpicks, the status of them doesn't matter.
if (iSkill > 0)
{
iSkill = (iSkill * pSoldier->inv[bSlot][0]->data.objectStatus) / 100;
}
break;
case ATTACHING_DETONATOR_CHECK:
case ATTACHING_REMOTE_DETONATOR_CHECK:
iSkill = EffectiveExplosive( pSoldier );
if (iSkill == 0)
{
break;
}
iSkill = (iSkill * 3 + EffectiveDexterity( pSoldier, FALSE ) ) / 4;
// SANDRO - STOMP traits - Demolitions trait detonator attaching bonus
if ( gGameOptions.fNewTraitSystem )
{
iSkill += gSkillTraitValues.bCheckModifierAttachDetonators; // reduce skill value for untrained mercs
if ( bReason == ATTACHING_REMOTE_DETONATOR_CHECK )
{
iSkill /= 2;
}
// add a flat bonus instead of eliminating the penalty above
if ( HAS_SKILL_TRAIT( pSoldier, DEMOLITIONS_NT ))
{
iSkill += gSkillTraitValues.ubDEAttachDetonatorCheckBonus;
}
}
else // original code
{
if ( bReason == ATTACHING_REMOTE_DETONATOR_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS_OT )) )
{
iSkill /= 2;
}
}
break;
case PLANTING_BOMB_CHECK:
case PLANTING_REMOTE_BOMB_CHECK:
iSkill = EffectiveExplosive( pSoldier ) * 7;
iSkill += EffectiveWisdom( pSoldier ) * 2;
iSkill += EffectiveExpLevel( pSoldier ) * 10;
iSkill = iSkill / 10; // bring the value down to a percentage
// SANDRO - STOMP traits - Demolitions trait planting bombs bonus
if ( gGameOptions.fNewTraitSystem )
{
iSkill += gSkillTraitValues.bCheckModifierSetExplosives; // reduce skill for untrained mercs
if ( bReason == PLANTING_REMOTE_BOMB_CHECK )
{
iSkill = (iSkill * 3) / 4;
}
// add a flat bonus instead of eliminating the penalty above
if ( HAS_SKILL_TRAIT( pSoldier, DEMOLITIONS_NT ))
{
iSkill += gSkillTraitValues.ubDEPlantAndRemoveBombCheckBonus;
}
}
else // original code
{
if ( bReason == PLANTING_REMOTE_BOMB_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS_OT)) )
{
// deduct only a bit...
iSkill = (iSkill * 3) / 4;
}
}
// Ok, this is really damn easy, so skew the values...
// e.g. if calculated skill is 84, skewed up to 96
// 51 to 84
// 22 stays as is
iSkill = (iSkill + 100 * (iSkill / 25) ) / (iSkill / 25 + 1);
break;
case PLANTING_MECHANICAL_BOMB_CHECK:
iSkill = EffectiveMechanical( pSoldier ) * 3;
iSkill += EffectiveStrength(pSoldier, FALSE) * 5;
iSkill += EffectiveDexterity( pSoldier, FALSE ) * 1.5f;
iSkill += EffectiveExpLevel( pSoldier ) * 5;
iSkill = iSkill / 10; // bring the value down to a percentage
break;
case DISARM_TRAP_CHECK:
fForceDamnSound = TRUE;
// SANDRO - normal(exploding) traps should be based at least a little on mechanical skill as well
if ( gGameOptions.fNewTraitSystem)
{
// It is impossible without both skills
if ( (EffectiveMechanical( pSoldier ) <= 0) || (EffectiveExplosive( pSoldier ) <= 0) )
{
iSkill = 0;
break;
}
else
{
// The part that Mechanical plays is a bit too high. A merc with 90+ explosives will still fail
// a lot when his Mechanical is not in the 90+ as well. Even when disarming own mines which provides a bonus
// he will fail a lot which doesn't make sense. Let's change this from 5/2 to 6/1.
//iSkill = EffectiveExplosive( pSoldier ) * 5;
//iSkill += EffectiveMechanical( pSoldier ) * 2;
iSkill = EffectiveExplosive( pSoldier ) * 6;
iSkill += EffectiveMechanical( pSoldier );
}
}
else
{
iSkill = EffectiveExplosive( pSoldier ) * 7;
if ( iSkill == 0 )
{
break;
}
}
iSkill += EffectiveDexterity( pSoldier, FALSE ) * 2;
iSkill += EffectiveExpLevel( pSoldier ) * 10;
// Flugente: backgrounds
iSkill += pSoldier->GetBackgroundValue( BG_PERC_DISARM );
iSkill = iSkill / 10; // bring the value down to a percentage
//JMich_SkillModifiers: Adding a Disarm Trap bonus
bSlot = FindDisarmKit( pSoldier);
if (bSlot != NO_SLOT)
{
iSkill += Item[pSoldier->inv[bSlot].usItem].DisarmModifier * pSoldier->inv[bSlot][0]->data.objectStatus / 100;
}
// SANDRO - STOMP traits - Demolitions trait removing traps bonus
if ( gGameOptions.fNewTraitSystem )
{
iSkill += gSkillTraitValues.bCheckModifierDisarmExplosiveTraps; // -15% for untrained mercs
if (HAS_SKILL_TRAIT( pSoldier, DEMOLITIONS_NT ))
iSkill += gSkillTraitValues.ubDEPlantAndRemoveBombCheckBonus; // +50% bonus for Demolitions
}
// penalty based on poor wisdom
iSkill -= (100 - EffectiveWisdom( pSoldier ) ) / 5;
break;
case DISARM_ELECTRONIC_TRAP_CHECK:
fForceDamnSound = TRUE;
// SANDRO - electronic traps are based more on mechanical skill
if ( gGameOptions.fNewTraitSystem)
{
// It is impossible without both skills
if ( (EffectiveMechanical( pSoldier ) <= 0) || (EffectiveExplosive( pSoldier ) <= 0) )
{
iSkill = 0;
break;
}
else
{
iSkill = EffectiveExplosive( pSoldier ) * 2;
iSkill += EffectiveMechanical( pSoldier ) * 5;
}
}
else
{
iSkill = __max( EffectiveMechanical( pSoldier ) , EffectiveExplosive( pSoldier ) ) * 7;
if ( iSkill == 0 )
{
break;
}
}
iSkill += EffectiveDexterity( pSoldier, FALSE ) * 2;
iSkill += EffectiveExpLevel( pSoldier ) * 10;
iSkill = iSkill / 10; // bring the value down to a percentage
//JMich_SkillModifiers: Adding a Disarm Trap bonus
bSlot = FindDisarmKit( pSoldier);
if (bSlot != NO_SLOT)
{
iSkill += Item[pSoldier->inv[bSlot].usItem].DisarmModifier * pSoldier->inv[bSlot][0]->data.objectStatus / 100;
}
// penalty based on poor wisdom
iSkill -= (100 - EffectiveWisdom( pSoldier ) ) / 5;
// SANDRO - STOMP traits - Technician trait removing traps bonus
if ( gGameOptions.fNewTraitSystem )
{
iSkill += gSkillTraitValues.bCheckModifierDisarmElectronicTraps; // penalty for untrained mercs
iSkill = (iSkill * 3) / 4; // original penalty
// add a flat bonus instead of eliminating the penalty above
if ( HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ))
{
iSkill += gSkillTraitValues.usTEDisarmElTrapBonus * NUM_SKILL_TRAITS( pSoldier, TECHNICIAN_NT );
}
}
else // original code
{
if ( !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS_OT)) )
{
iSkill = (iSkill * 3) / 4;
}
}
break;
case DISARM_MECHANICAL_TRAP_CHECK:
fForceDamnSound = TRUE;
// SANDRO - electronic traps are based more on mechanical skill
if ( gGameOptions.fNewTraitSystem)
{
// It is impossible without both skills
if ( (EffectiveMechanical( pSoldier ) <= 0) || (EffectiveStrength( pSoldier, FALSE ) <= 0) )
{
iSkill = 0;
break;
}
else
{
iSkill = EffectiveMechanical( pSoldier ) * 3;
iSkill += EffectiveStrength( pSoldier, FALSE ) * 5;
}
}
else
{
iSkill = __max( EffectiveMechanical( pSoldier ) , EffectiveStrength( pSoldier, FALSE ) ) * 7;
if ( iSkill == 0 )
{
break;
}
}
iSkill += EffectiveDexterity( pSoldier, FALSE ) * 1.5f;
iSkill += EffectiveExpLevel( pSoldier ) * 5;
iSkill = iSkill / 10; // bring the value down to a percentage
//JMich_SkillModifiers: Adding a Disarm Trap bonus
bSlot = FindDisarmKit( pSoldier);
if (bSlot != NO_SLOT)
{
iSkill += Item[pSoldier->inv[bSlot].usItem].DisarmModifier * pSoldier->inv[bSlot][0]->data.objectStatus / 100;
}
break;
case OPEN_WITH_CROWBAR:
// Add for crowbar...
//JMich_SkillModifiers: Changing the +20 to the crowbar's bonus.
bSlot = FindUsableCrowbar( pSoldier );
if (bSlot != NO_SLOT)
{
iSkill = EffectiveStrength( pSoldier, FALSE ) + Item[pSoldier->inv[bSlot].usItem].CrowbarModifier;
}
else //how are we trying to force with a crowbar, without having one??
{
iSkill = 0;
}
fForceDamnSound = TRUE;
break;
case SMASH_DOOR_CHECK:
iSkill = EffectiveStrength( pSoldier, FALSE );
// SANDRO - STOMP traits - Martial Arts trait kick doors bonus
if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT ))
{
iSkill += gSkillTraitValues.ubMAChanceToCkickDoors * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT );
}
break;
case UNJAM_GUN_CHECK:
iSkill = 30 + EffectiveMechanical( pSoldier ) / 2;
// SANDRO - STOMP traits - Technician trait unjamming guns bonus
if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ))
{
iSkill += gSkillTraitValues.ubTEUnjamGunBonus * NUM_SKILL_TRAITS( pSoldier, TECHNICIAN_NT );
}
break;
case NOTICE_DART_CHECK:
// only a max of ~20% chance
iSkill = EffectiveWisdom( pSoldier ) / 10 + EffectiveExpLevel( pSoldier );
break;
case LIE_TO_QUEEN_CHECK:
// competitive check vs the queen's wisdom and leadership... poor guy!
iSkill = 50 * ( EffectiveWisdom( pSoldier ) + EffectiveLeadership( pSoldier ) ) / ( gMercProfiles[ QUEEN ].bWisdom + gMercProfiles[ QUEEN ].bLeadership );
// squadleader has bonus for this, muhehe - SANDRO
if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, SQUADLEADER_NT ))
{
iSkill += (20 * NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT ));
}
break;
case ATTACHING_SPECIAL_ITEM_CHECK:
case ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK:
iSkill = EffectiveMechanical( pSoldier );
if (iSkill == 0)
{
break;
}
// adjust skill based on wisdom (knowledge)
iSkill = iSkill * (EffectiveWisdom( pSoldier ) + 100) / 200;
// and dexterity (clumsy?)
iSkill = iSkill * (EffectiveDexterity( pSoldier, FALSE ) + 100) / 200;
// factor in experience
iSkill = iSkill + EffectiveExpLevel( pSoldier ) * 3;
// SANDRO - STOMP traits - Technician trait attaching special items bonus
if ( gGameOptions.fNewTraitSystem )
{
iSkill += gSkillTraitValues.bCheckModifierAttachSpecialItems; // penalty for untrained mercs
if ( bReason == ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK )
{
iSkill /= 2;
}
// add a flat bonus instead of eliminating the penalty above
if ( HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ))
{
iSkill += gSkillTraitValues.usTEAttachingItemsBonus * NUM_SKILL_TRAITS( pSoldier, TECHNICIAN_NT );
}
}
else // original code
{
if (bReason == ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS_OT)) )
{
// if we are unfamiliar with electronics...
iSkill /= 2;
}
}
break;
case ATTACH_POWER_PACK: // added by Flugente
// technicians can attach it, other people can't
if ( HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ))
iSkill = 100;
else
iSkill = 0;
break;
default:
iSkill = 0;
break;
}
iSkill -= GetSkillCheckPenaltyForFatigue( pSoldier, iSkill );
iChance = iSkill + bChanceMod;
////////////////////////////////////////////////////////////////////////
// SANDRO - STOMP - character traits and disabilities modifiers
//added heat intolerant penalty
if ( MercIsHot( pSoldier ) )
{
iChance -= 15;
}
// also added a small penalty for fear of insects in tropical sectors
else if ( DoesMercHaveDisability( pSoldier, FEAR_OF_INSECTS ) && MercIsInTropicalSector( pSoldier ) )
{
// fear of insects, and we are in tropical sector
iChance -= 5;
}
// character traits influence
// Sociable - better performance in groups
if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_SOCIABLE ) )
{
INT8 bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_SOCIABLE );
if ( bNumMercs > 2 )
iChance += 5;
else if ( bNumMercs > 0 )
iChance += 2;
}
// Loner - better performance when alone
else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_LONER ) )
{
INT8 bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_LONER );
if ( bNumMercs == 0 )
iChance += 5;
else if ( bNumMercs <= 1 )
iChance += 2;
}
// Aggressive - penalty for actions needing patience
else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_AGGRESSIVE ) )
{
switch ( bReason )
{
case LOCKPICKING_CHECK:
case ELECTRONIC_LOCKPICKING_CHECK:
case PLANTING_BOMB_CHECK:
case PLANTING_REMOTE_BOMB_CHECK:
case PLANTING_MECHANICAL_BOMB_CHECK:
case DISARM_TRAP_CHECK:
case DISARM_ELECTRONIC_TRAP_CHECK:
case DISARM_MECHANICAL_TRAP_CHECK:
case ATTACHING_SPECIAL_ITEM_CHECK:
case ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK:
iChance -= 10;
break;
}
}
// Phlegmatic - bonus for actions needing patience
else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_PHLEGMATIC ) )
{
switch ( bReason )
{
case LOCKPICKING_CHECK:
case ELECTRONIC_LOCKPICKING_CHECK:
case PLANTING_BOMB_CHECK:
case PLANTING_REMOTE_BOMB_CHECK:
case PLANTING_MECHANICAL_BOMB_CHECK:
case DISARM_TRAP_CHECK:
case DISARM_ELECTRONIC_TRAP_CHECK:
case DISARM_MECHANICAL_TRAP_CHECK:
case ATTACHING_SPECIAL_ITEM_CHECK:
case ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK:
iChance += 5;
break;
}
}
// Show-off - better performance if some babes around to impress
else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_SHOWOFF ) )
{
INT8 bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_SHOWOFF );
if ( bNumMercs > 1 )
iChance += 5;
else if ( bNumMercs > 0 )
iChance += 2;
}
//////////////////////////////////////////////////////////////////////
// CHRISL: If lock is flagged OPENING_NOT_POSSIBLE, then reset iChance to 0 regardless of mercs skills. Also, moved this check to after personality and
// disability checks since an unopenable door isn't going to suddenly be openable just cause we have some other bonus.
if(bChanceMod == -100)
iChance = 0;
// silversurfer: moved this down here where it belongs
// if this wasn't the last check a merc with certain disability modifiers would never completely fail
// to do something and would never do his speech telling that he can't do this
switch (bReason)
{
case LOCKPICKING_CHECK:
case ELECTRONIC_LOCKPICKING_CHECK:
case DISARM_TRAP_CHECK:
case DISARM_ELECTRONIC_TRAP_CHECK:
case DISARM_MECHANICAL_TRAP_CHECK:
case OPEN_WITH_CROWBAR:
case SMASH_DOOR_CHECK:
case ATTACHING_SPECIAL_ITEM_CHECK:
case ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK:
// for lockpicking and smashing locks, if the chance isn't reasonable
// we set it to 0 so they can never get through the door if they aren't
// good enough
//CHRISL: with this set to 30, some "hard" to open doors become basically impossible. Case in point, Drassen "dentist office" which imposes a
// -75 penalty on lockpicking/forcing chances. You find this near the start of the game but would need an 80 STR + Crowbar to open. It's
// impossible to force with just strength or lockpicking skills, regardless of how skilled you are.
if (iChance < 15)
{
iChance = 0;
break;
}
// else fall through
default:
iChance += GetMoraleModifier( pSoldier );
break;
}
if (iChance > 99)
{
iChance = 99;
}
else if (iChance < 0)
{
iChance = 0;
}
// silversurfer: changed this a little. I think this is supposed to represent "luck".
// A soldier that has low skills, attributes and experience should have to rely more on luck
// than someone that knows exactly what he is doing. This will produce more stable results
// for those that are good at their job.
// iRoll = PreRandom( 100 );
iRoll = PreRandom( UINT32( 115 - iChance / 3 ) );
iMadeItBy = iChance - iRoll;
if (iMadeItBy < 0)
{
if ( (pSoldier->bLastSkillCheck == bReason) && (pSoldier->sGridNo == pSoldier->sSkillCheckGridNo) )
{
pSoldier->ubSkillCheckAttempts++;
if (pSoldier->ubSkillCheckAttempts > 2)
{
if (iChance == 0)
{
if ( MercIsHot( pSoldier ) && pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__PLAYER_CHARACTER) // SANDRO - added this
{
TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
// Flugente: dynamic opinions
if (gGameExternalOptions.fDynamicOpinions)
{
HandleDynamicOpinionChange(pSoldier, OPINIONEVENT_ANNOYINGDISABILITY, TRUE, TRUE);
}
}
// do we realize that we just can't do this?
if ( (100 - (pSoldier->ubSkillCheckAttempts - 2) * 20) < EffectiveWisdom( pSoldier ) )
{
// say "I can't do this" quote
TacticalCharacterDialogue( pSoldier, QUOTE_DEFINITE_CANT_DO );
return( iMadeItBy );
}
}
}
}
else
{
pSoldier->bLastSkillCheck = bReason;
pSoldier->ubSkillCheckAttempts = 1;
pSoldier->sSkillCheckGridNo = pSoldier->sGridNo;
}
if ( fForceDamnSound || Random( 100 ) < 40 )
{
switch( bReason )
{
case UNJAM_GUN_CHECK:
case NOTICE_DART_CHECK:
case LIE_TO_QUEEN_CHECK:
// silent check
break;
default:
pSoldier->DoMercBattleSound( BATTLE_SOUND_CURSE1 );
break;
}
}
}
else
{
// A buddy might make a positive comment based on our success;
// Increase the chance for people with higher skill and for more difficult tasks
iChance = 15 + iSkill / 20 + (-bChanceMod) / 20;
if (iRoll < iChance)
{
// If a buddy of this merc is standing around nearby, they'll make a positive comment.
SoldierID iLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( ; iLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++iLoop )
{
pTeamSoldier = iLoop;
if ( OK_INSECTOR_MERC( pTeamSoldier ) )
{
if ( SpacesAway( pSoldier->sGridNo, pTeamSoldier->sGridNo ) < 15 )
{
bBuddyIndex = WhichBuddy( pTeamSoldier->ubProfile, pSoldier->ubProfile );
if ( bBuddyIndex >= 0 )
{
switch ( bBuddyIndex )
{
case 0:
// buddy #1 did something good!
TacticalCharacterDialogue( pTeamSoldier, QUOTE_BUDDY_1_GOOD );
break;
case 1:
// buddy #2 did something good!
TacticalCharacterDialogue( pTeamSoldier, QUOTE_BUDDY_2_GOOD );
break;
case 2:
// buddy #3 did something good!
if ( pTeamSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC )
TacticalCharacterDialogue( pTeamSoldier, QUOTE_AIM_BUDDY_3_GOOD );
else
TacticalCharacterDialogue( pTeamSoldier, QUOTE_NON_AIM_BUDDY_3_GOOD );
break;
case 3:
// buddy #4 did something good!
if ( pTeamSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC )
TacticalCharacterDialogue( pTeamSoldier, QUOTE_AIM_BUDDY_4_GOOD );
else
TacticalCharacterDialogue( pTeamSoldier, QUOTE_NON_AIM_BUDDY_4_GOOD );
break;
case 4:
// buddy #5 did something good!
if ( pTeamSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC )
TacticalCharacterDialogue( pTeamSoldier, QUOTE_AIM_BUDDY_5_GOOD );
else
TacticalCharacterDialogue( pTeamSoldier, QUOTE_NON_AIM_BUDDY_5_GOOD );
break;
case 5:
// learn to like buddy did something good!
TacticalCharacterDialogue( pTeamSoldier, QUOTE_LEARNED_TO_LIKE_WITNESSED );
break;
default:
break;
}
}
// Flugente: additional dialogue
AdditionalTacticalCharacterDialogue_CallsLua( pTeamSoldier, ADE_WITNESS_GOOD, pSoldier->ubProfile, 1 );
}
}
}
}
}
return( iMadeItBy );
}
INT16 CalcTrapDetectLevel( SOLDIERTYPE * pSoldier, BOOLEAN fExamining )
{
// return the level of trap which the guy is able to detect
INT16 bDetectLevel;
// formula: 1 pt for every exp_level
// plus 1 pt for every 40 explosives
// less 1 pt for every 20 wisdom MISSING
bDetectLevel = EffectiveExpLevel( pSoldier );
bDetectLevel += (EffectiveExplosive( pSoldier ) / 40);
bDetectLevel -= ((100 - EffectiveWisdom( pSoldier ) ) / 20);
// STOMP traits - Technician trait detecting traps bonus - SANDRO
if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ))
{
bDetectLevel += (gSkillTraitValues.ubTEChanceToDetectTrapsBonus * NUM_SKILL_TRAITS( pSoldier, TECHNICIAN_NT )); // +2 trap level per trait
}
// if the examining flag is true, this isn't just a casual glance
// and the merc should have a higher chance
if (fExamining)
{
bDetectLevel += (INT8) PreRandom(bDetectLevel / 3 + 2);
}
// SANDRO - optimists just don't even think about the traps, randomly ignoring this all
else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_OPTIMIST ) )
{
if ( Chance(50) )
bDetectLevel = 1;
else
bDetectLevel -= 2;
}
// if substantially bleeding, or still in serious shock, randomly lower value
if ((pSoldier->bBleeding > 20) || (pSoldier->aiData.bShock > 1))
{
bDetectLevel -= (INT8) PreRandom(3);
}
if (bDetectLevel < 1)
{
bDetectLevel = 1;
}
return( bDetectLevel );
}