From 8ace761e7fc3aeb9041c30f0854c5414a2cabeed Mon Sep 17 00:00:00 2001 From: Flugente Date: Mon, 5 Nov 2012 22:38:52 +0000 Subject: [PATCH] - new ini option ZOMBIE_ONLY_HEADSHOTS_PERMANENTLY_KILL: if a zombie is killed via headshot/melee combat, he can't rise again - new ini option FOOD_MAX_POISONING: determines the maximum amount of food poisoning eating a singel food item can give - fixed a bug that caused tooltips to display the wrong traits - fixed a few zombie-related bugs git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5669 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 3 +++ GameSettings.h | 3 +++ Strategic/Queen Command.cpp | 7 +++++++ Tactical/Food.cpp | 3 +++ Tactical/Overhead.cpp | 15 +++++++++++++++ Tactical/Overhead.h | 2 ++ Tactical/Rotting Corpses.cpp | 6 +++++- Tactical/Soldier Control.cpp | 19 +++++++++++++++++-- Tactical/Soldier Control.h | 4 ++-- Tactical/SoldierTooltips.cpp | 24 ++++++++++++------------ Tactical/Weapons.cpp | 25 +++++++++++++++++++++++-- 11 files changed, 92 insertions(+), 19 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 02028d04..98651b5a 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1401,6 +1401,7 @@ void LoadGameExternalOptions() gGameExternalOptions.fZombieOnlyHeadshotsWork = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_ONLY_HEADSHOTS_WORK", FALSE); gGameExternalOptions.sZombieDifficultyLevel = iniReader.ReadInteger("Tactical Zombie Settings", "ZOMBIE_DIFFICULTY_LEVEL", 2, 1, 4); gGameExternalOptions.fZombieRiseWithArmour = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_RISE_WITH_ARMOUR", TRUE); + gGameExternalOptions.fZombieOnlyHeadShotsPermanentlyKill = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_ONLY_HEADSHOTS_PERMANENTLY_KILL", TRUE); #endif //################# Tactical Poison Settings ################## @@ -1425,6 +1426,8 @@ void LoadGameExternalOptions() gGameExternalOptions.fFoodDecayInSectors = iniReader.ReadBoolean("Tactical Food Settings", "FOOD_DECAY_IN_SECTORS", TRUE); gGameExternalOptions.sFoodDecayModificator = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DECAY_MODIFICATOR", 1.0f, 0.1f, 10.0f); + gGameExternalOptions.usFoodMaxPoisoning = iniReader.ReadInteger("Tactical Food Settings", "FOOD_MAX_POISONING", 5, 0, 100); + //################# Strategic Gamestart Settings ################## //Lalien: Game starting time diff --git a/GameSettings.h b/GameSettings.h index d43d268b..f0b09080 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -383,6 +383,7 @@ typedef struct BOOLEAN fZombieOnlyHeadshotsWork; INT8 sZombieDifficultyLevel; BOOLEAN fZombieRiseWithArmour; + BOOLEAN fZombieOnlyHeadShotsPermanentlyKill; #endif // Flugente: poison settings @@ -406,6 +407,8 @@ typedef struct BOOLEAN fFoodDecayInSectors; FLOAT sFoodDecayModificator; + + UINT8 usFoodMaxPoisoning; //Animation settings FLOAT giPlayerTurnSpeedUpFactor; diff --git a/Strategic/Queen Command.cpp b/Strategic/Queen Command.cpp index a35d76cc..68e47388 100644 --- a/Strategic/Queen Command.cpp +++ b/Strategic/Queen Command.cpp @@ -112,6 +112,13 @@ void ValidateEnemiesHaveWeapons() { continue; } + +#ifdef ENABLE_ZOMBIES + // Flugente: zombies are fine with having no weapons... + if ( pSoldier->IsZombie() ) + continue; +#endif + if( !pSoldier->inv[ HANDPOS ].usItem ) { iNumInvalid++; diff --git a/Tactical/Food.cpp b/Tactical/Food.cpp index b12ee881..2da32ac5 100644 --- a/Tactical/Food.cpp +++ b/Tactical/Food.cpp @@ -142,6 +142,9 @@ BOOLEAN ApplyFood( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObject, BOOLEAN fForce, B // multiply with 0.25 for more reasonable values INT8 poisonadd = (INT8)(0.01 * 0.25 * maxpts * (1.0 - foodcondition) ); + // added a max threshold to food poisoning + poisonadd = min(poisonadd, gGameExternalOptions.usFoodMaxPoisoning), + pSoldier->AddPoison(poisonadd); } diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index d96e963c..48025e4f 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -9767,3 +9767,18 @@ void HandleDisplayingOfPlayerLostDialogue( void ) } } #endif + +// Flugente: the entire new trait to string thingy is a fucked up mess. I've had it. Heres a function that simply handles that crap. +STR16 GetNewTraitStr(UINT8 aTrait) +{ + if ( aTrait <= DOCTOR_NT ) + ; + else if ( aTrait <= SCOUTING_NT ) + ++aTrait; + else if ( aTrait == COVERT_NT ) + aTrait = 10; + else if ( aTrait >= SCOUTING_NT + NUM_MINOR_TRAITS ) + aTrait -= NUM_MINOR_TRAITS; + + return gzMercSkillTextNew[ aTrait ]; +} diff --git a/Tactical/Overhead.h b/Tactical/Overhead.h index 335c5614..b66175cd 100644 --- a/Tactical/Overhead.h +++ b/Tactical/Overhead.h @@ -404,4 +404,6 @@ extern UINT8 NumEnemyInSector(); extern UINT8 NumZombiesInSector(); #endif +STR16 GetNewTraitStr(UINT8 aTrait); // Flugente: deal with the insane new trait to string stuff + #endif \ No newline at end of file diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 7dd4ebe2..72729740 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -893,6 +893,10 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE break; } + // if zombie and headshots are required, forbid rising if killed by headshot + if ( pSoldier->IsZombie() && gGameExternalOptions.fZombieOnlyHeadShotsPermanentlyKill && (pSoldier->bSoldierFlagMask & SOLDIER_HEADSHOT) ) + Corpse.usFlags |= ROTTING_CORPSE_NEVER_RISE_AGAIN; + // Flugente: copy name of soldier... memcpy( &(Corpse.name), &(pSoldier->name), sizeof(CHAR16) * 10 ); Corpse.name[9] = '\0'; @@ -2730,7 +2734,7 @@ UINT8 GetNearestRottingCorpseAIWarning( INT32 sGridNo ) pNewSoldier->bPoisonBleeding = pNewSoldier->bPoisonSum - pNewSoldier->bPoisonLife; // zombies get 200% poison absorption, but no resistance to it, as it would reduce their healing pNewSoldier->bPoisonResistance = 0; - pNewSoldier->bPoisonAbsorption = 200 + Random(100); + pNewSoldier->bPoisonAbsorption = 0; // Flugente: Screw this, we use GetPoisonAbsorption() instead... I declare this variable dead until further notice ////////////////////////////////////////////////////////////////////// #ifdef ENABLE_ZOMBIES diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 5240441b..271cc03a 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -5861,7 +5861,7 @@ void SOLDIERTYPE::EVENT_SoldierGotHit( UINT16 usWeaponIndex, INT16 sDamage, INT1 return; } - INT16 poisondamage = (INT16) ( sDamage * MercPtrs[ubAttackerID]->GetPoisonDamagePercentage() / 100 ); + INT16 poisondamage = (INT16) ( (sDamage * MercPtrs[ubAttackerID]->GetPoisonDamagePercentage()) / 100 ); // DEDUCT LIFE ubCombinedLoss = this->SoldierTakeDamage( ANIM_CROUCH, sDamage, poisondamage, sBreathLoss, ubReason, this->ubAttackerID, NOWHERE, FALSE, TRUE ); @@ -10480,6 +10480,10 @@ BOOLEAN SOLDIERTYPE::CheckSoldierHitRoof( void ) fReturnVal = TRUE; } + + // Flugente: some bodytypes can't do these animations, so skip this + if ( IsAnimationValidForBodyType( this, this->usPendingAnimation ) == FALSE ) + fReturnVal = FALSE; } } @@ -11838,7 +11842,7 @@ void SOLDIERTYPE::EVENT_SoldierBeginPunchAttack( INT32 sGridNo, UINT8 ubDirectio if ( pTSoldier->bBreath - breathdamage < 0 ) breathdamage = pTSoldier->bBreath; - INT16 poisondamage = (INT16) ( sDamage * this->GetPoisonDamagePercentage() / 100 ); + INT16 poisondamage = (INT16) ( (damage * this->GetPoisonDamagePercentage()) / 100 ); // zombies do ~50% poison damage pTSoldier->SoldierTakeDamage( 0, damage, poisondamage, breathdamage, TAKE_DAMAGE_HANDTOHAND, this->ubID, pTSoldier->sGridNo, 0, TRUE ); @@ -13956,6 +13960,11 @@ INT16 SOLDIERTYPE::GetPoisonAbsorption( void ) { // Flugente: absorption can per definition only be >= 0 (at least that's my definition) INT16 val = bPoisonAbsorption; + +#ifdef ENABLE_ZOMBIES + if ( IsZombie() ) + val += 200; +#endif val = max(0, val); @@ -15018,6 +15027,12 @@ BOOLEAN SOLDIERTYPE::RecognizeAsCombatant(UINT8 ubTargetID) if ( !pSoldier ) return TRUE; +#ifdef ENABLE_ZOMBIES + // zombies don't care about disguises + if ( IsZombie() ) + return TRUE; +#endif + // if neither of the 2 persons is covert, always return true if ( ( (pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0 ) && ( (this->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0 ) ) return TRUE; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index dc7dee4c..6868f364 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -354,8 +354,8 @@ enum #define SOLDIER_NEW_PANTS 0x00000080 //128 // Soldier is wearing new pants #define SOLDIER_DAMAGED_PANTS 0x00000100 //256 // Soldier's vest is damaged (and thus can't be taken off) -/*#define WH40K_SEAL 0x00000200 //512 -#define WH40K_POWER_WEAPON 0x00000400 //1024 +#define SOLDIER_HEADSHOT 0x00000200 //512 // last hit received was a headshot (attack to the head, so knifes/punches also work) +/*#define WH40K_POWER_WEAPON 0x00000400 //1024 #define ENEMY_NET_4_LVL_3 0x00000800 //2048 #define ENEMY_NET_1_LVL_4 0x00001000 //4096 diff --git a/Tactical/SoldierTooltips.cpp b/Tactical/SoldierTooltips.cpp index 7f3bd072..10196046 100644 --- a/Tactical/SoldierTooltips.cpp +++ b/Tactical/SoldierTooltips.cpp @@ -236,33 +236,33 @@ void SoldierTooltip( SOLDIERTYPE* pSoldier ) if (( pSoldier->stats.ubSkillTraits[0] == pSoldier->stats.ubSkillTraits[1] ) && pSoldier->stats.ubSkillTraits[0] != 0 ) { CHAR16 pStrAux[50]; - swprintf( pStrAux, L"(%s)", gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]]); + swprintf( pStrAux, L"(%s)", GetNewTraitStr(pSoldier->stats.ubSkillTraits[0])); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, pStrAux ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1] + NEWTRAIT_MERCSKILL_EXPERTOFFSET] ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2]] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[1] + NEWTRAIT_MERCSKILL_EXPERTOFFSET) ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[2]) ); //swprintf( pStrInfo, L"%s\n", pStrInfo ); } else if (( pSoldier->stats.ubSkillTraits[1] == pSoldier->stats.ubSkillTraits[2] ) && pSoldier->stats.ubSkillTraits[1] != 0 ) { CHAR16 pStrAux[50]; - swprintf( pStrAux, L"(%s)", gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1]]); + swprintf( pStrAux, L"(%s)", GetNewTraitStr(pSoldier->stats.ubSkillTraits[1])); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, pStrAux ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2] + NEWTRAIT_MERCSKILL_EXPERTOFFSET] ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[2] + NEWTRAIT_MERCSKILL_EXPERTOFFSET) ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[0]) ); } else if (( pSoldier->stats.ubSkillTraits[0] == pSoldier->stats.ubSkillTraits[2] ) && pSoldier->stats.ubSkillTraits[0] != 0 ) { CHAR16 pStrAux[50]; - swprintf( pStrAux, L"(%s)", gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]]); + swprintf( pStrAux, L"(%s)", GetNewTraitStr(pSoldier->stats.ubSkillTraits[0])); swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, pStrAux ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2] + NEWTRAIT_MERCSKILL_EXPERTOFFSET] ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1]] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[2] + NEWTRAIT_MERCSKILL_EXPERTOFFSET) ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[1]) ); } else { - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[0]] ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[1]] ); - swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, gzMercSkillTextNew[pSoldier->stats.ubSkillTraits[2]] ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_1], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[0]) ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_2], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[1]) ); + swprintf( pStrInfo, gzTooltipStrings[STR_TT_SKILL_TRAIT_3], pStrInfo, GetNewTraitStr(pSoldier->stats.ubSkillTraits[2]) ); } } else diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index bc9155fd..c046aed1 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -8866,11 +8866,20 @@ INT32 BulletImpact( SOLDIERTYPE *pFirer, BULLET *pBullet, SOLDIERTYPE * pTarget, UINT8 ubAmmoType; #ifdef ENABLE_ZOMBIES - if ( pTarget->IsZombie() && gGameExternalOptions.fZombieOnlyHeadshotsWork ) + if ( pTarget->IsZombie() ) { // if bullet does not hits anything other than the head, it doesn't do any damage - if ( ubHitLocation != AIM_SHOT_HEAD ) + if ( gGameExternalOptions.fZombieOnlyHeadshotsWork && ubHitLocation != AIM_SHOT_HEAD ) return 0; + + // set a flag if this was a headshot, unset if it wasn't. Thus we can determine if this was a headshot kill (only if life > 0, ignore if already dead) + if ( gGameExternalOptions.fZombieOnlyHeadShotsPermanentlyKill && pTarget->stats.bLife > 0 ) + { + if ( ubHitLocation == AIM_SHOT_HEAD ) + pTarget->bSoldierFlagMask |= SOLDIER_HEADSHOT; + else + pTarget->bSoldierFlagMask &= ~SOLDIER_HEADSHOT; + } } #endif @@ -9738,6 +9747,18 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO BOOLEAN autoresolve = IsAutoResolveActive(); iImpact = max( 1, (INT32)(iImpact * (100 - pTarget->GetDamageResistance(autoresolve, FALSE)) / 100 ) ); +#ifdef ENABLE_ZOMBIES + // Flugente: if the target is a zombie, any melee attack, regardless of hit location, will set the headshot flag. Thus any zombie killed in melee will stay dead (if you play with that option) + if ( pTarget->IsZombie() ) + { + // set a flag if this was a headshot, unset if it wasn't. Thus we can determine if this was a headshot kill (only if life > 0, ignore if already dead) + if ( gGameExternalOptions.fZombieOnlyHeadShotsPermanentlyKill && pTarget->stats.bLife > 0 ) + { + pTarget->bSoldierFlagMask |= SOLDIER_HEADSHOT; + } + } +#endif + return( iImpact ); }