mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-10 04:46:08 -04:00
Unify creature/NPC armor autoequip
This commit is contained in:
parent
a6503233a6
commit
df5625a1e3
@ -378,110 +378,111 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
|
|||||||
|
|
||||||
void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
|
void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
|
||||||
{
|
{
|
||||||
// Only NPCs can wear armor for now.
|
|
||||||
// For creatures we equip only shields.
|
|
||||||
const Ptr& actor = getPtr();
|
const Ptr& actor = getPtr();
|
||||||
if (!actor.getClass().isNpc())
|
|
||||||
|
// Creatures only want shields and don't benefit from armor rating or unarmored skill
|
||||||
|
const MWWorld::Class& actorCls = actor.getClass();
|
||||||
|
const bool actorIsNpc = actorCls.isNpc();
|
||||||
|
|
||||||
|
int equipmentTypes = ContainerStore::Type_Armor;
|
||||||
|
float unarmoredRating = 0.f;
|
||||||
|
if (actorIsNpc)
|
||||||
{
|
{
|
||||||
autoEquipShield(slots_);
|
equipmentTypes |= ContainerStore::Type_Clothing;
|
||||||
return;
|
const auto& store = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||||
|
const float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
|
||||||
|
const float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
|
||||||
|
const float unarmoredSkill = actorCls.getSkill(actor, ESM::Skill::Unarmored);
|
||||||
|
unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
|
||||||
|
unarmoredRating = std::max(unarmoredRating, 0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
for (ContainerStoreIterator iter(begin(equipmentTypes)); iter != end(); ++iter)
|
||||||
|
|
||||||
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
|
|
||||||
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
|
|
||||||
|
|
||||||
float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);
|
|
||||||
float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
|
|
||||||
|
|
||||||
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter != end();
|
|
||||||
++iter)
|
|
||||||
{
|
{
|
||||||
Ptr test = *iter;
|
Ptr test = *iter;
|
||||||
|
const MWWorld::Class& testCls = test.getClass();
|
||||||
|
const bool isArmor = iter.getType() == ContainerStore::Type_Armor;
|
||||||
|
|
||||||
switch (test.getClass().canBeEquipped(test, actor).first)
|
// Discard armor that is worse than unarmored for NPCs and non-shields for creatures
|
||||||
|
if (isArmor)
|
||||||
{
|
{
|
||||||
case 0:
|
if (actorIsNpc)
|
||||||
continue;
|
{
|
||||||
default:
|
if (testCls.getEffectiveArmorRating(test, actor) <= unarmoredRating)
|
||||||
break;
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (test.get<ESM::Armor>()->mBase->mData.mType != ESM::Armor::Shield)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter.getType() == ContainerStore::Type_Armor
|
// Don't equip the item if it cannot be equipped
|
||||||
&& test.getClass().getEffectiveArmorRating(test, actor) <= std::max(unarmoredRating, 0.f))
|
if (testCls.canBeEquipped(test, actor).first == 0)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::vector<int>, bool> itemsSlots = iter->getClass().getEquipmentSlots(*iter);
|
const auto [itemSlots, canStack] = testCls.getEquipmentSlots(test);
|
||||||
|
|
||||||
// checking if current item pointed by iter can be equipped
|
// checking if current item pointed by iter can be equipped
|
||||||
for (int slot : itemsSlots.first)
|
for (const int slot : itemSlots)
|
||||||
{
|
{
|
||||||
// if true then it means slot is equipped already
|
|
||||||
// check if slot may require swapping if current item is more valuable
|
// check if slot may require swapping if current item is more valuable
|
||||||
if (slots_.at(slot) != end())
|
if (slots_.at(slot) != end())
|
||||||
{
|
{
|
||||||
Ptr old = *slots_.at(slot);
|
Ptr old = *slots_.at(slot);
|
||||||
|
const MWWorld::Class& oldCls = old.getClass();
|
||||||
|
unsigned int oldType = old.getType();
|
||||||
|
|
||||||
if (iter.getType() == ContainerStore::Type_Armor)
|
if (!isArmor)
|
||||||
{
|
{
|
||||||
if (old.getType() == ESM::Armor::sRecordId)
|
// Armor should replace clothing and weapons, but clothing should only replace clothing
|
||||||
{
|
if (oldType != ESM::Clothing::sRecordId)
|
||||||
if (old.get<ESM::Armor>()->mBase->mData.mType < test.get<ESM::Armor>()->mBase->mData.mType)
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (old.get<ESM::Armor>()->mBase->mData.mType == test.get<ESM::Armor>()->mBase->mData.mType)
|
// If the left ring slot is filled, don't swap if the right ring is cheaper
|
||||||
{
|
|
||||||
if (old.getClass().getEffectiveArmorRating(old, actor)
|
|
||||||
>= test.getClass().getEffectiveArmorRating(test, actor))
|
|
||||||
// old armor had better armor rating
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// suitable armor should replace already equipped clothing
|
|
||||||
}
|
|
||||||
else if (iter.getType() == ContainerStore::Type_Clothing)
|
|
||||||
{
|
|
||||||
// if left ring is equipped
|
|
||||||
if (slot == Slot_LeftRing)
|
if (slot == Slot_LeftRing)
|
||||||
{
|
{
|
||||||
// if there is a place for right ring dont swap it
|
|
||||||
if (slots_.at(Slot_RightRing) == end())
|
if (slots_.at(Slot_RightRing) == end())
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
else // if right ring is equipped too
|
|
||||||
{
|
|
||||||
Ptr rightRing = *slots_.at(Slot_RightRing);
|
|
||||||
|
|
||||||
// we want to swap cheaper ring only if both are equipped
|
Ptr rightRing = *slots_.at(Slot_RightRing);
|
||||||
if (old.getClass().getValue(old) >= rightRing.getClass().getValue(rightRing))
|
if (rightRing.getClass().getValue(rightRing) <= oldCls.getValue(old))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testCls.getValue(test) <= oldCls.getValue(old))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (oldType == ESM::Armor::sRecordId)
|
||||||
|
{
|
||||||
|
const int32_t oldArmorType = old.get<ESM::Armor>()->mBase->mData.mType;
|
||||||
|
const int32_t newArmorType = test.get<ESM::Armor>()->mBase->mData.mType;
|
||||||
|
if (oldArmorType == newArmorType)
|
||||||
|
{
|
||||||
|
// For NPCs, compare armor rating; for creatures, compare condition
|
||||||
|
if (actorIsNpc)
|
||||||
|
{
|
||||||
|
const float rating = testCls.getEffectiveArmorRating(test, actor);
|
||||||
|
const float oldRating = oldCls.getEffectiveArmorRating(old, actor);
|
||||||
|
if (rating <= oldRating)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (testCls.getItemHealth(test) <= oldCls.getItemHealth(old))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (oldArmorType < newArmorType)
|
||||||
if (old.getType() == ESM::Clothing::sRecordId)
|
|
||||||
{
|
|
||||||
// check value
|
|
||||||
if (old.getClass().getValue(old) >= test.getClass().getValue(test))
|
|
||||||
// old clothing was more valuable
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// suitable clothing should NOT replace already equipped armor
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped
|
// unstack the item if required
|
||||||
|
if (!canStack && test.getCellRef().getCount() > 1)
|
||||||
{
|
{
|
||||||
// unstack item pointed to by iterator if required
|
unstack(test);
|
||||||
if (iter->getCellRef().getCount() > 1)
|
|
||||||
{
|
|
||||||
unstack(*iter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are here it means item can be equipped or swapped
|
// if we are here it means item can be equipped or swapped
|
||||||
@ -491,27 +492,6 @@ void MWWorld::InventoryStore::autoEquipArmor(TSlots& slots_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::autoEquipShield(TSlots& slots_)
|
|
||||||
{
|
|
||||||
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter)
|
|
||||||
{
|
|
||||||
if (iter->get<ESM::Armor>()->mBase->mData.mType != ESM::Armor::Shield)
|
|
||||||
continue;
|
|
||||||
if (iter->getClass().canBeEquipped(*iter, getPtr()).first != 1)
|
|
||||||
continue;
|
|
||||||
std::pair<std::vector<int>, bool> shieldSlots = iter->getClass().getEquipmentSlots(*iter);
|
|
||||||
int slot = shieldSlots.first[0];
|
|
||||||
const ContainerStoreIterator& shield = slots_[slot];
|
|
||||||
if (shield != end() && shield.getType() == Type_Armor
|
|
||||||
&& shield->get<ESM::Armor>()->mBase->mData.mType == ESM::Armor::Shield)
|
|
||||||
{
|
|
||||||
if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
slots_[slot] = iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MWWorld::InventoryStore::autoEquip()
|
void MWWorld::InventoryStore::autoEquip()
|
||||||
{
|
{
|
||||||
TSlots slots_;
|
TSlots slots_;
|
||||||
@ -522,8 +502,6 @@ void MWWorld::InventoryStore::autoEquip()
|
|||||||
|
|
||||||
// Autoequip clothing, armor and weapons.
|
// Autoequip clothing, armor and weapons.
|
||||||
// Equipping lights is handled in Actors::updateEquippedLight based on environment light.
|
// Equipping lights is handled in Actors::updateEquippedLight based on environment light.
|
||||||
// Note: creatures ignore equipment armor rating and only equip shields
|
|
||||||
// Use custom logic for them - select shield based on its health instead of armor rating
|
|
||||||
autoEquipWeapon(slots_);
|
autoEquipWeapon(slots_);
|
||||||
autoEquipArmor(slots_);
|
autoEquipArmor(slots_);
|
||||||
|
|
||||||
|
@ -69,7 +69,6 @@ namespace MWWorld
|
|||||||
|
|
||||||
void autoEquipWeapon(TSlots& slots_);
|
void autoEquipWeapon(TSlots& slots_);
|
||||||
void autoEquipArmor(TSlots& slots_);
|
void autoEquipArmor(TSlots& slots_);
|
||||||
void autoEquipShield(TSlots& slots_);
|
|
||||||
|
|
||||||
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
||||||
ContainerStoreIterator mSelectedEnchantItem;
|
ContainerStoreIterator mSelectedEnchantItem;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user