// WANNE 2 #ifdef PRECOMPILEDHEADERS #include "Tactical All.h" #else #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" #endif INT8 EffectiveStrength( SOLDIERTYPE * pSoldier ) { INT8 bBandaged; INT32 iEffStrength; // Effective strength is: // 1/2 full strength // plus 1/2 strength scaled according to how hurt we are bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding; // WANNE 2 if (pSoldier->bStrength > 0) { iEffStrength = pSoldier->bStrength / 2; iEffStrength += (pSoldier->bStrength / 2) * (pSoldier->bLife + bBandaged / 2) / (pSoldier->bLifeMax); } else { iEffStrength = 0; } // ATE: Make sure at least 2... iEffStrength = __max( iEffStrength, 2 ); return( (INT8) iEffStrength ); } INT8 EffectiveWisdom( SOLDIERTYPE * pSoldier ) { INT32 iEffWisdom; iEffWisdom = pSoldier->bWisdom; iEffWisdom = EffectStatForBeingDrunk( pSoldier, iEffWisdom ); return( (INT8) iEffWisdom ); } INT8 EffectiveAgility( SOLDIERTYPE * pSoldier ) { INT32 iEffAgility; iEffAgility = pSoldier->bAgility; iEffAgility = EffectStatForBeingDrunk( pSoldier, iEffAgility ); if ( pSoldier->sWeightCarriedAtTurnStart > 100 ) { iEffAgility = (iEffAgility * 100) / pSoldier->sWeightCarriedAtTurnStart; } return( (INT8) iEffAgility ); } INT8 EffectiveMechanical( SOLDIERTYPE * pSoldier ) { INT32 iEffMechanical; iEffMechanical = pSoldier->bMechanical; iEffMechanical = EffectStatForBeingDrunk( pSoldier, iEffMechanical ); return( (INT8) iEffMechanical ); } INT8 EffectiveExplosive( SOLDIERTYPE * pSoldier ) { INT32 iEffExplosive; iEffExplosive = pSoldier->bExplosive; iEffExplosive = EffectStatForBeingDrunk( pSoldier, iEffExplosive ); return( (INT8) iEffExplosive ); } INT8 EffectiveMedical( SOLDIERTYPE * pSoldier ) { INT32 iEffMedical; iEffMedical = pSoldier->bMedical; iEffMedical = EffectStatForBeingDrunk( pSoldier, iEffMedical ); return( (INT8) iEffMedical ); } INT8 EffectiveLeadership( SOLDIERTYPE * pSoldier ) { INT32 iEffLeadership; INT8 bDrunkLevel; iEffLeadership = pSoldier->bLeadership; // if we are drunk, effect leader ship in a +ve way... bDrunkLevel = GetDrunkLevel( pSoldier ); if ( bDrunkLevel == FEELING_GOOD ) { iEffLeadership = ( iEffLeadership * 120 / 100 ); } return( (INT8) iEffLeadership ); } INT8 EffectiveExpLevel( SOLDIERTYPE * pSoldier ) { INT32 iEffExpLevel; INT8 bDrunkLevel; INT32 iExpModifier[] = { 0, // SOBER 0, // Feeling good -1, // Borderline -2, // Drunk 0, // Hung }; iEffExpLevel = pSoldier->bExpLevel; bDrunkLevel = GetDrunkLevel( pSoldier ); iEffExpLevel = iEffExpLevel + iExpModifier[ bDrunkLevel ]; if (pSoldier->ubProfile != NO_PROFILE) { if ( (gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait == CLAUSTROPHOBIC) && pSoldier->bActive && pSoldier->bInSector && gbWorldSectorZ > 0) { // claustrophobic! iEffExpLevel--; } } if (iEffExpLevel < 1) { // can't go below 1 return( 1 ); } else { return( (INT8) iEffExpLevel ); } } INT8 EffectiveMarksmanship( SOLDIERTYPE * pSoldier ) { INT32 iEffMarksmanship; iEffMarksmanship = pSoldier->bMarksmanship; iEffMarksmanship = EffectStatForBeingDrunk( pSoldier, iEffMarksmanship ); return( (INT8) iEffMarksmanship ); } INT8 EffectiveDexterity( SOLDIERTYPE * pSoldier ) { INT32 iEffDexterity; iEffDexterity = pSoldier->bDexterity; iEffDexterity = EffectStatForBeingDrunk( pSoldier, iEffDexterity ); return( (INT8) 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, UINT16 *pusPoints ) { *pusPoints -= (*pusPoints * 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; INT8 bSlot; INT32 iLoop; SOLDIERTYPE * pTeamSoldier; INT8 bBuddyIndex; BOOLEAN fForceDamnSound = FALSE; iReportChance = -1; switch (bReason) { case LOCKPICKING_CHECK: case ELECTRONIC_LOCKPICKING_CHECK: 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 ) + 100) / 200; // factor in experience iSkill = iSkill + EffectiveExpLevel( pSoldier ) * 3; if (HAS_SKILL_TRAIT( pSoldier, LOCKPICKING ) ) { // if we specialize in picking locks... iSkill += gbSkillTraitBonus[LOCKPICKING] * NUM_SKILL_TRAITS( pSoldier, LOCKPICKING ); } if (bReason == ELECTRONIC_LOCKPICKING_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS)) ) { // 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; } iSkill = iSkill * pSoldier->inv[bSlot].bStatus[0] / 100; break; case ATTACHING_DETONATOR_CHECK: case ATTACHING_REMOTE_DETONATOR_CHECK: iSkill = EffectiveExplosive( pSoldier ); if (iSkill == 0) { break; } iSkill = (iSkill * 3 + EffectiveDexterity( pSoldier ) ) / 4; if ( bReason == ATTACHING_REMOTE_DETONATOR_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS )) ) { 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 if ( bReason == PLANTING_REMOTE_BOMB_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS)) ) { // 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 DISARM_TRAP_CHECK: fForceDamnSound = TRUE; iSkill = EffectiveExplosive( pSoldier ) * 7; if ( iSkill == 0 ) { break; } iSkill += EffectiveDexterity( pSoldier ) * 2; iSkill += EffectiveExpLevel( pSoldier ) * 10; iSkill = iSkill / 10; // bring the value down to a percentage // penalty based on poor wisdom iSkill -= (100 - EffectiveWisdom( pSoldier ) ) / 5; break; case DISARM_ELECTRONIC_TRAP_CHECK: fForceDamnSound = TRUE; iSkill = __max( EffectiveMechanical( pSoldier ) , EffectiveExplosive( pSoldier ) ) * 7; if ( iSkill == 0 ) { break; } iSkill += EffectiveDexterity( pSoldier ) * 2; iSkill += EffectiveExpLevel( pSoldier ) * 10; iSkill = iSkill / 10; // bring the value down to a percentage // penalty based on poor wisdom iSkill -= (100 - EffectiveWisdom( pSoldier ) ) / 5; if ( !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS )) ) { iSkill = (iSkill * 3) / 4; } break; case OPEN_WITH_CROWBAR: // Add for crowbar... iSkill = EffectiveStrength( pSoldier ) + 20; fForceDamnSound = TRUE; break; case SMASH_DOOR_CHECK: iSkill = EffectiveStrength( pSoldier ); break; case UNJAM_GUN_CHECK: iSkill = 30 + EffectiveMechanical( pSoldier ) / 2; 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 ); 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 ) + 100) / 200; // factor in experience iSkill = iSkill + EffectiveExpLevel( pSoldier ) * 3; if (bReason == ATTACHING_SPECIAL_ELECTRONIC_ITEM_CHECK && !(HAS_SKILL_TRAIT( pSoldier, ELECTRONICS)) ) { // if we are unfamiliar with electronics... iSkill /= 2; } break; default: iSkill = 0; break; } iSkill -= GetSkillCheckPenaltyForFatigue( pSoldier, iSkill ); iChance = iSkill + bChanceMod; switch (bReason) { case LOCKPICKING_CHECK: case ELECTRONIC_LOCKPICKING_CHECK: case DISARM_TRAP_CHECK: case DISARM_ELECTRONIC_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 if (iChance < 30) { iChance = 0; break; } // else fall through default: iChance += GetMoraleModifier( pSoldier ); break; } if (iChance > 99) { iChance = 99; } else if (iChance < 0) { iChance = 0; } iRoll = PreRandom( 100 ); iMadeItBy = iChance - iRoll; if (iMadeItBy < 0) { if ( (pSoldier->bLastSkillCheck == bReason) && (pSoldier->sGridNo == pSoldier->sSkillCheckGridNo) ) { pSoldier->ubSkillCheckAttempts++; if (pSoldier->ubSkillCheckAttempts > 2) { if (iChance == 0) { // 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: DoMercBattleSound( pSoldier, 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. iLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; for ( pTeamSoldier = MercPtrs[ iLoop ]; iLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; iLoop++,pTeamSoldier++ ) { if ( OK_INSECTOR_MERC( pTeamSoldier ) ) { bBuddyIndex = WhichBuddy( pTeamSoldier->ubProfile, pSoldier->ubProfile ); if (bBuddyIndex >= 0 && SpacesAway( pSoldier->sGridNo, pTeamSoldier->sGridNo ) < 15) { 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: // learn to like buddy did something good! TacticalCharacterDialogue( pTeamSoldier, QUOTE_LEARNED_TO_LIKE_WITNESSED ); break; default: break; } } } } } } return( iMadeItBy ); } INT8 CalcTrapDetectLevel( SOLDIERTYPE * pSoldier, BOOLEAN fExamining ) { // return the level of trap which the guy is able to detect INT8 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); // 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); } // if substantially bleeding, or still in serious shock, randomly lower value if ((pSoldier->bBleeding > 20) || (pSoldier->bShock > 1)) { bDetectLevel -= (INT8) PreRandom(3); } if (bDetectLevel < 1) { bDetectLevel = 1; } return( bDetectLevel ); }