mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 07:53:51 +00:00
Merged pull request #1904
This commit is contained in:
commit
7e419dc34e
4 changed files with 111 additions and 67 deletions
|
@ -138,6 +138,9 @@
|
||||||
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
|
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
|
||||||
Feature #4579: Add option for applying Strength into hand to hand damage
|
Feature #4579: Add option for applying Strength into hand to hand damage
|
||||||
Feature #4581: Use proper logging system
|
Feature #4581: Use proper logging system
|
||||||
|
Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment
|
||||||
|
Feature #4625: Weapon priority: use weighted mean for melee damage rating
|
||||||
|
Feature #4626: Weapon priority: account for weapon speed
|
||||||
Task #2490: Don't open command prompt window on Release-mode builds automatically
|
Task #2490: Don't open command prompt window on Release-mode builds automatically
|
||||||
Task #4545: Enable is_pod string test
|
Task #4545: Enable is_pod string test
|
||||||
Task #4605: Optimize skinning
|
Task #4605: Optimize skinning
|
||||||
|
|
|
@ -268,24 +268,27 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
text += "\n#{sType} ";
|
text += "\n#{sType} ";
|
||||||
|
|
||||||
std::map <int, std::pair <std::string, std::string> > mapping;
|
static std::map <int, std::pair <std::string, std::string> > mapping;
|
||||||
mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded");
|
if (mapping.empty())
|
||||||
mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded");
|
{
|
||||||
mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded");
|
mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded");
|
||||||
mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded");
|
mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded");
|
||||||
mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
|
mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded");
|
||||||
mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
|
mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded");
|
||||||
mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded");
|
mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
|
||||||
mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded");
|
mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
|
||||||
mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded");
|
mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded");
|
||||||
mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", "");
|
mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded");
|
||||||
mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", "");
|
mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded");
|
||||||
mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", "");
|
mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", "");
|
||||||
mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", "");
|
mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", "");
|
||||||
mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", "");
|
mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", "");
|
||||||
|
mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", "");
|
||||||
|
mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", "");
|
||||||
|
}
|
||||||
|
|
||||||
std::string type = mapping[ref->mBase->mData.mType].first;
|
const std::string type = mapping[ref->mBase->mData.mType].first;
|
||||||
std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second;
|
const std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second;
|
||||||
|
|
||||||
text += store.get<ESM::GameSetting>().find(type)->mValue.getString() +
|
text += store.get<ESM::GameSetting>().find(type)->mValue.getString() +
|
||||||
((oneOrTwoHanded != "") ? ", " + store.get<ESM::GameSetting>().find(oneOrTwoHanded)->mValue.getString() : "");
|
((oneOrTwoHanded != "") ? ", " + store.get<ESM::GameSetting>().find(oneOrTwoHanded)->mValue.getString() : "");
|
||||||
|
|
|
@ -232,20 +232,59 @@ namespace MWMechanics
|
||||||
case ESM::MagicEffect::CommandHumanoid:
|
case ESM::MagicEffect::CommandHumanoid:
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
|
case ESM::MagicEffect::Blind:
|
||||||
|
{
|
||||||
|
if (enemy.isEmpty())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||||
|
|
||||||
|
// Enemy can't attack
|
||||||
|
if (stats.isParalyzed() || stats.getKnockedDown())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
// Enemy doesn't attack
|
||||||
|
if (stats.getDrawState() != MWMechanics::DrawState_Weapon)
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case ESM::MagicEffect::Sound:
|
case ESM::MagicEffect::Sound:
|
||||||
{
|
{
|
||||||
if (enemy.isEmpty())
|
if (enemy.isEmpty())
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
// there is no need to cast sound if enemy is not able to cast spells
|
const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
|
||||||
|
|
||||||
|
// Enemy can't cast spells
|
||||||
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0)
|
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0)
|
if (stats.isParalyzed() || stats.getKnockedDown())
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
|
// Enemy doesn't cast spells
|
||||||
|
if (stats.getDrawState() != MWMechanics::DrawState_Spell)
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESM::MagicEffect::Silence:
|
||||||
|
{
|
||||||
|
if (enemy.isEmpty())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||||
|
|
||||||
|
// Enemy can't cast spells
|
||||||
|
if (stats.isParalyzed() || stats.getKnockedDown())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
// Enemy doesn't cast spells
|
||||||
|
if (stats.getDrawState() != MWMechanics::DrawState_Spell)
|
||||||
|
return 0.f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,9 +392,10 @@ namespace MWMechanics
|
||||||
int priority = 1;
|
int priority = 1;
|
||||||
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
||||||
priority = 10;
|
priority = 10;
|
||||||
const DynamicStat<float>& current = actor.getClass().getCreatureStats(actor).
|
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||||
getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
const DynamicStat<float>& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
||||||
float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration;
|
const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f;
|
||||||
|
const float toHeal = magnitude * effect.mDuration;
|
||||||
// Effect doesn't heal more than we need, *or* we are below 1/2 health
|
// Effect doesn't heal more than we need, *or* we are below 1/2 health
|
||||||
if (current.getModified() - current.getCurrent() > toHeal
|
if (current.getModified() - current.getCurrent() > toHeal
|
||||||
|| current.getCurrent() < current.getModified()*0.5)
|
|| current.getCurrent() < current.getModified()*0.5)
|
||||||
|
|
|
@ -21,7 +21,10 @@ namespace MWMechanics
|
||||||
float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type,
|
float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type,
|
||||||
float arrowRating, float boltRating)
|
float arrowRating, float boltRating)
|
||||||
{
|
{
|
||||||
if (item.getTypeName() != typeid(ESM::Weapon).name())
|
if (enemy.isEmpty() || item.getTypeName() != typeid(ESM::Weapon).name())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
|
if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) == 0)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
const ESM::Weapon* weapon = item.get<ESM::Weapon>()->mBase;
|
const ESM::Weapon* weapon = item.get<ESM::Weapon>()->mBase;
|
||||||
|
@ -29,6 +32,9 @@ namespace MWMechanics
|
||||||
if (type != -1 && weapon->mData.mType != type)
|
if (type != -1 && weapon->mData.mType != type)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
|
const MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt))
|
if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt))
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
|
@ -37,54 +43,37 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
|
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
|
||||||
{
|
{
|
||||||
// Range weapon is useless under water
|
// Underwater ranged combat is impossible
|
||||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f))
|
if (world->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)
|
||||||
return 0.f;
|
|| world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
|
||||||
|
|
||||||
if (enemy.isEmpty())
|
|
||||||
return 0.f;
|
|
||||||
|
|
||||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
|
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy))
|
if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy))
|
||||||
rangedMult = 1.5f;
|
{
|
||||||
|
static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat();
|
||||||
|
static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat();
|
||||||
|
if (fAIMeleeWeaponMult != 0)
|
||||||
|
rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult;
|
||||||
|
else
|
||||||
|
rangedMult = fAIRangeMeleeWeaponMult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;
|
||||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
||||||
{
|
rating = chop;
|
||||||
float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1];
|
|
||||||
MWMechanics::adjustWeaponDamage(rangedDamage, item, actor);
|
|
||||||
|
|
||||||
rating = rangedDamage / 2.f;
|
|
||||||
|
|
||||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown)
|
|
||||||
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float meleeDamage = 0.f;
|
const float slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1]) / 2.f;
|
||||||
|
const float thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1]) / 2.f;
|
||||||
for (int i=0; i<2; ++i)
|
rating = (slash * slash + thrust * thrust + chop * chop) / (slash + thrust + chop);
|
||||||
{
|
|
||||||
meleeDamage += weapon->mData.mSlash[i];
|
|
||||||
meleeDamage += weapon->mData.mThrust[i];
|
|
||||||
meleeDamage += weapon->mData.mChop[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
MWMechanics::adjustWeaponDamage(meleeDamage, item, actor);
|
|
||||||
rating = meleeDamage / 6.f;
|
|
||||||
|
|
||||||
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getClass().hasItemHealth(item))
|
adjustWeaponDamage(rating, item, actor);
|
||||||
{
|
|
||||||
if (item.getClass().getItemHealth(item) == 0)
|
|
||||||
return 0.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
|
if (weapon->mData.mType != ESM::Weapon::MarksmanBow && weapon->mData.mType != ESM::Weapon::MarksmanCrossbow)
|
||||||
|
resistNormalWeapon(enemy, actor, item, rating);
|
||||||
|
else if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
|
||||||
{
|
{
|
||||||
if (arrowRating <= 0.f)
|
if (arrowRating <= 0.f)
|
||||||
rating = 0.f;
|
rating = 0.f;
|
||||||
|
@ -101,7 +90,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (!weapon->mEnchant.empty())
|
if (!weapon->mEnchant.empty())
|
||||||
{
|
{
|
||||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);
|
const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);
|
||||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||||
{
|
{
|
||||||
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
|
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
|
||||||
|
@ -111,21 +100,30 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enemy.getClass().isNpc())
|
||||||
|
{
|
||||||
|
static const float fCombatArmorMinMult = gmst.find("fCombatArmorMinMult")->mValue.getFloat();
|
||||||
|
rating *= std::max(fCombatArmorMinMult, rating / (rating + enemy.getClass().getArmorRating(enemy)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int value = 50.f;
|
||||||
if (actor.getClass().isNpc())
|
if (actor.getClass().isNpc())
|
||||||
{
|
{
|
||||||
int skill = item.getClass().getEquipmentSkill(item);
|
int skill = item.getClass().getEquipmentSkill(item);
|
||||||
if (skill != -1)
|
if (skill != -1)
|
||||||
{
|
value = actor.getClass().getSkill(actor, skill);
|
||||||
int value = actor.getClass().getSkill(actor, skill);
|
|
||||||
rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MWWorld::LiveCellRef<ESM::Creature> *ref = actor.get<ESM::Creature>();
|
MWWorld::LiveCellRef<ESM::Creature> *ref = actor.get<ESM::Creature>();
|
||||||
rating *= MWMechanics::getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f;
|
value = ref->mBase->mData.mCombat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rating *= getHitChance(actor, enemy, value) / 100.f;
|
||||||
|
|
||||||
|
if (weapon->mData.mType < ESM::Weapon::MarksmanBow)
|
||||||
|
rating *= weapon->mData.mSpeed;
|
||||||
|
|
||||||
return rating * rangedMult;
|
return rating * rangedMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue