mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 15:39:48 +00:00
Merge branch 'clickbait' into 'master'
Mostly dehardcode skills See merge request OpenMW/openmw!3112
This commit is contained in:
commit
807893eb45
69 changed files with 543 additions and 646 deletions
|
@ -43,20 +43,13 @@ namespace MWClass
|
|||
return;
|
||||
|
||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
switch (shield->getClass().getEquipmentSkill(*shield))
|
||||
{
|
||||
case ESM::Skill::LightArmor:
|
||||
const ESM::RefId skill = shield->getClass().getEquipmentSkill(*shield);
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||
break;
|
||||
case ESM::Skill::MediumArmor:
|
||||
else if (skill == ESM::Skill::MediumArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
||||
break;
|
||||
case ESM::Skill::HeavyArmor:
|
||||
else if (skill == ESM::Skill::HeavyArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const
|
||||
|
|
|
@ -112,7 +112,7 @@ namespace MWClass
|
|||
return std::make_pair(slots_, false);
|
||||
}
|
||||
|
||||
int Armor::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
||||
ESM::RefId Armor::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
|
||||
|
||||
|
@ -150,7 +150,7 @@ namespace MWClass
|
|||
}
|
||||
|
||||
if (typeGmst.empty())
|
||||
return -1;
|
||||
return {};
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||
|
@ -178,7 +178,7 @@ namespace MWClass
|
|||
|
||||
const ESM::RefId& Armor::getUpSoundId(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
int es = getEquipmentSkill(ptr);
|
||||
const ESM::RefId es = getEquipmentSkill(ptr);
|
||||
static const ESM::RefId lightUp = ESM::RefId::stringRefId("Item Armor Light Up");
|
||||
static const ESM::RefId mediumUp = ESM::RefId::stringRefId("Item Armor Medium Up");
|
||||
static const ESM::RefId heavyUp = ESM::RefId::stringRefId("Item Armor Heavy Up");
|
||||
|
@ -193,7 +193,7 @@ namespace MWClass
|
|||
|
||||
const ESM::RefId& Armor::getDownSoundId(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
int es = getEquipmentSkill(ptr);
|
||||
const ESM::RefId es = getEquipmentSkill(ptr);
|
||||
static const ESM::RefId lightDown = ESM::RefId::stringRefId("Item Armor Light Down");
|
||||
static const ESM::RefId mediumDown = ESM::RefId::stringRefId("Item Armor Medium Down");
|
||||
static const ESM::RefId heavyDown = ESM::RefId::stringRefId("Item Armor Heavy Down");
|
||||
|
@ -232,7 +232,7 @@ namespace MWClass
|
|||
}
|
||||
else
|
||||
{
|
||||
int armorType = getEquipmentSkill(ptr);
|
||||
const ESM::RefId armorType = getEquipmentSkill(ptr);
|
||||
if (armorType == ESM::Skill::LightArmor)
|
||||
typeText = "#{sLight}";
|
||||
else if (armorType == ESM::Skill::MediumArmor)
|
||||
|
@ -297,7 +297,7 @@ namespace MWClass
|
|||
{
|
||||
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
|
||||
|
||||
int armorSkillType = getEquipmentSkill(ptr);
|
||||
const ESM::RefId armorSkillType = getEquipmentSkill(ptr);
|
||||
float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
|
||||
|
||||
int iBaseArmorSkill = MWBase::Environment::get()
|
||||
|
|
|
@ -41,9 +41,7 @@ namespace MWClass
|
|||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
/// stay stacked when equipped?
|
||||
|
||||
int getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
||||
/// Return the index of the skill this item corresponds to when equipped or -1, if there is
|
||||
/// no such skill.
|
||||
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override;
|
||||
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
|
||||
|
|
|
@ -101,14 +101,14 @@ namespace MWClass
|
|||
return std::make_pair(slots_, false);
|
||||
}
|
||||
|
||||
int Clothing::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
||||
ESM::RefId Clothing::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Clothing>* ref = ptr.get<ESM::Clothing>();
|
||||
|
||||
if (ref->mBase->mData.mType == ESM::Clothing::Shoes)
|
||||
return ESM::Skill::Unarmored;
|
||||
|
||||
return -1;
|
||||
return {};
|
||||
}
|
||||
|
||||
int Clothing::getValue(const MWWorld::ConstPtr& ptr) const
|
||||
|
|
|
@ -33,9 +33,7 @@ namespace MWClass
|
|||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
/// stay stacked when equipped?
|
||||
|
||||
int getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
||||
/// Return the index of the skill this item corresponds to when equipped or -1, if there is
|
||||
/// no such skill.
|
||||
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override;
|
||||
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
|
||||
|
|
|
@ -762,11 +762,11 @@ namespace MWClass
|
|||
throw std::runtime_error("Unexpected soundgen type: " + std::string(name));
|
||||
}
|
||||
|
||||
float Creature::getSkill(const MWWorld::Ptr& ptr, int skill) const
|
||||
float Creature::getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
||||
|
||||
const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skill);
|
||||
const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(id);
|
||||
|
||||
switch (skillRecord->mData.mSpecialization)
|
||||
{
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace MWClass
|
|||
bool canSwim(const MWWorld::ConstPtr& ptr) const override;
|
||||
bool canWalk(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
float getSkill(const MWWorld::Ptr& ptr, int skill) const override;
|
||||
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
||||
|
||||
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
|
||||
int getBloodTexture(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
|
|
@ -111,20 +111,18 @@ namespace
|
|||
{
|
||||
float modifierSum = 0;
|
||||
|
||||
for (int j = 0; j < ESM::Skill::Length; ++j)
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
{
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(j);
|
||||
|
||||
if (skill->mData.mAttribute != attribute)
|
||||
if (skill.mData.mAttribute != attribute)
|
||||
continue;
|
||||
|
||||
// is this a minor or major skill?
|
||||
float add = 0.2f;
|
||||
for (const auto& skills : class_->mData.mSkills)
|
||||
{
|
||||
if (skills[0] == j)
|
||||
if (skills[0] == skill.mIndex)
|
||||
add = 0.5;
|
||||
if (skills[1] == j)
|
||||
if (skills[1] == skill.mIndex)
|
||||
add = 1.0;
|
||||
}
|
||||
modifierSum += add;
|
||||
|
@ -181,15 +179,15 @@ namespace
|
|||
|
||||
for (const auto& skills : class_->mData.mSkills)
|
||||
{
|
||||
int index = skills[i];
|
||||
if (index >= 0 && index < ESM::Skill::Length)
|
||||
ESM::RefId id = ESM::Skill::indexToRefId(skills[i]);
|
||||
if (!id.empty())
|
||||
{
|
||||
npcStats.getSkill(index).setBase(npcStats.getSkill(index).getBase() + bonus);
|
||||
npcStats.getSkill(id).setBase(npcStats.getSkill(id).getBase() + bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex)
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
{
|
||||
float majorMultiplier = 0.1f;
|
||||
float specMultiplier = 0.0f;
|
||||
|
@ -198,14 +196,14 @@ namespace
|
|||
int specBonus = 0;
|
||||
|
||||
auto bonusIt = std::find_if(race->mData.mBonus.begin(), race->mData.mBonus.end(),
|
||||
[skillIndex](const auto& bonus) { return bonus.mSkill == skillIndex; });
|
||||
[&](const auto& bonus) { return bonus.mSkill == skill.mIndex; });
|
||||
if (bonusIt != race->mData.mBonus.end())
|
||||
raceBonus = bonusIt->mBonus;
|
||||
|
||||
for (const auto& skills : class_->mData.mSkills)
|
||||
{
|
||||
// is this a minor or major skill?
|
||||
if (std::find(skills.begin(), skills.end(), skillIndex) != skills.end())
|
||||
if (std::find(skills.begin(), skills.end(), skill.mIndex) != skills.end())
|
||||
{
|
||||
majorMultiplier = 1.0f;
|
||||
break;
|
||||
|
@ -213,30 +211,25 @@ namespace
|
|||
}
|
||||
|
||||
// is this skill in the same Specialization as the class?
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillIndex);
|
||||
if (skill->mData.mSpecialization == class_->mData.mSpecialization)
|
||||
if (skill.mData.mSpecialization == class_->mData.mSpecialization)
|
||||
{
|
||||
specMultiplier = 0.5f;
|
||||
specBonus = 5;
|
||||
}
|
||||
|
||||
npcStats.getSkill(skillIndex)
|
||||
.setBase(std::min(round_ieee_754(npcStats.getSkill(skillIndex).getBase() + 5 + raceBonus + specBonus
|
||||
npcStats.getSkill(skill.mId).setBase(
|
||||
std::min(round_ieee_754(npcStats.getSkill(skill.mId).getBase() + 5 + raceBonus + specBonus
|
||||
+ (int(level) - 1) * (majorMultiplier + specMultiplier)),
|
||||
100)); // Must gracefully handle level 0
|
||||
}
|
||||
|
||||
int skills[ESM::Skill::Length];
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
skills[i] = npcStats.getSkill(i).getBase();
|
||||
|
||||
int attributes[ESM::Attribute::Length];
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
attributes[i] = npcStats.getAttribute(i).getBase();
|
||||
|
||||
if (!spellsInitialised)
|
||||
{
|
||||
std::vector<ESM::RefId> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
|
||||
std::vector<ESM::RefId> spells = MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), attributes, race);
|
||||
npcStats.getSpells().addAllToInstance(spells);
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +308,7 @@ namespace MWClass
|
|||
gold = ref->mBase->mNpdt.mGold;
|
||||
|
||||
for (size_t i = 0; i < ref->mBase->mNpdt.mSkills.size(); ++i)
|
||||
data->mNpcStats.getSkill(i).setBase(ref->mBase->mNpdt.mSkills[i]);
|
||||
data->mNpcStats.getSkill(ESM::Skill::indexToRefId(i)).setBase(ref->mBase->mNpdt.mSkills[i]);
|
||||
|
||||
data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt.mStrength);
|
||||
data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt.mIntelligence);
|
||||
|
@ -589,7 +582,7 @@ namespace MWClass
|
|||
victim = result.first;
|
||||
hitPosition = result.second;
|
||||
|
||||
int weapskill = ESM::Skill::HandToHand;
|
||||
ESM::RefId weapskill = ESM::Skill::HandToHand;
|
||||
if (!weapon.isEmpty())
|
||||
weapskill = weapon.getClass().getEquipmentSkill(weapon);
|
||||
|
||||
|
@ -658,7 +651,7 @@ namespace MWClass
|
|||
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
{
|
||||
int weapskill = ESM::Skill::HandToHand;
|
||||
ESM::RefId weapskill = ESM::Skill::HandToHand;
|
||||
if (!weapon.isEmpty())
|
||||
weapskill = weapon.getClass().getEquipmentSkill(weapon);
|
||||
skillUsageSucceeded(ptr, weapskill, 0);
|
||||
|
@ -849,21 +842,16 @@ namespace MWClass
|
|||
armor = *inv.unequipItem(armor);
|
||||
}
|
||||
|
||||
ESM::RefId skill = armor.getClass().getEquipmentSkill(armor);
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);
|
||||
skillUsageSucceeded(ptr, skill, 0);
|
||||
|
||||
switch (armor.getClass().getEquipmentSkill(armor))
|
||||
{
|
||||
case ESM::Skill::LightArmor:
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||
break;
|
||||
case ESM::Skill::MediumArmor:
|
||||
else if (skill == ESM::Skill::MediumArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
||||
break;
|
||||
case ESM::Skill::HeavyArmor:
|
||||
else if (skill == ESM::Skill::HeavyArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ptr == MWMechanics::getPlayer())
|
||||
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);
|
||||
|
@ -1161,7 +1149,7 @@ namespace MWClass
|
|||
return cast.cast(recordId);
|
||||
}
|
||||
|
||||
void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const
|
||||
void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const
|
||||
{
|
||||
MWMechanics::NpcStats& stats = getNpcStats(ptr);
|
||||
|
||||
|
@ -1311,18 +1299,13 @@ namespace MWClass
|
|||
if (boots == inv.end() || boots->getType() != ESM::Armor::sRecordId)
|
||||
return (name == "left") ? footBareLeft : footBareRight;
|
||||
|
||||
switch (boots->getClass().getEquipmentSkill(*boots))
|
||||
{
|
||||
case ESM::Skill::LightArmor:
|
||||
ESM::RefId skill = boots->getClass().getEquipmentSkill(*boots);
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
return (name == "left") ? footLightLeft : footLightRight;
|
||||
break;
|
||||
case ESM::Skill::MediumArmor:
|
||||
else if (skill == ESM::Skill::MediumArmor)
|
||||
return (name == "left") ? footMediumLeft : footMediumRight;
|
||||
break;
|
||||
case ESM::Skill::HeavyArmor:
|
||||
else if (skill == ESM::Skill::HeavyArmor)
|
||||
return (name == "left") ? footHeavyLeft : footHeavyRight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ESM::RefId();
|
||||
}
|
||||
|
@ -1355,9 +1338,9 @@ namespace MWClass
|
|||
return MWWorld::Ptr(cell.insert(ref), &cell);
|
||||
}
|
||||
|
||||
float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
|
||||
float Npc::getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const
|
||||
{
|
||||
return getNpcStats(ptr).getSkill(skill).getModified();
|
||||
return getNpcStats(ptr).getSkill(id).getModified();
|
||||
}
|
||||
|
||||
int Npc::getBloodTexture(const MWWorld::ConstPtr& ptr) const
|
||||
|
|
|
@ -120,7 +120,7 @@ namespace MWClass
|
|||
/// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh
|
||||
|
||||
void skillUsageSucceeded(
|
||||
const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor = 1.f) const override;
|
||||
const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor = 1.f) const override;
|
||||
///< Inform actor \a ptr that a skill use has succeeded.
|
||||
|
||||
bool isEssential(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
@ -134,7 +134,7 @@ namespace MWClass
|
|||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
float getSkill(const MWWorld::Ptr& ptr, int skill) const override;
|
||||
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
||||
|
||||
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
|
||||
int getBloodTexture(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace MWClass
|
|||
return std::make_pair(slots_, stack);
|
||||
}
|
||||
|
||||
int Weapon::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
||||
ESM::RefId Weapon::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Weapon>* ref = ptr.get<ESM::Weapon>();
|
||||
int type = ref->mBase->mData.mType;
|
||||
|
|
|
@ -42,9 +42,7 @@ namespace MWClass
|
|||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
/// stay stacked when equipped?
|
||||
|
||||
int getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
||||
/// Return the index of the skill this item corresponds to when equipped or -1, if there is
|
||||
/// no such skill.
|
||||
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
int getValue(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/esm3/loadfact.hpp>
|
||||
#include <components/esm3/loadmgef.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -385,9 +386,10 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
|
|||
return player.getClass().getCreatureStats(player).getAttribute(select.getArgument()).getModified();
|
||||
|
||||
case SelectWrapper::Function_PcSkill:
|
||||
|
||||
return static_cast<int>(player.getClass().getNpcStats(player).getSkill(select.getArgument()).getModified());
|
||||
|
||||
{
|
||||
ESM::RefId skill = ESM::Skill::indexToRefId(select.getArgument());
|
||||
return static_cast<int>(player.getClass().getNpcStats(player).getSkill(skill).getModified());
|
||||
}
|
||||
case SelectWrapper::Function_FriendlyHit:
|
||||
{
|
||||
int hits = mActor.getClass().getCreatureStats(mActor).getFriendlyHits();
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace MWGui
|
|||
mPlayerAttributes.emplace(attribute.mId, MWMechanics::AttributeValue());
|
||||
|
||||
for (const auto& skill : store.get<ESM::Skill>())
|
||||
mPlayerSkillValues.emplace(skill.second.mIndex, MWMechanics::SkillValue());
|
||||
mPlayerSkillValues.emplace(skill.mId, MWMechanics::SkillValue());
|
||||
}
|
||||
|
||||
void CharacterCreation::setValue(std::string_view id, const MWMechanics::AttributeValue& value)
|
||||
|
@ -138,14 +138,14 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
|
||||
void CharacterCreation::setValue(ESM::RefId id, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
mPlayerSkillValues[parSkill] = value;
|
||||
mPlayerSkillValues[id] = value;
|
||||
if (mReviewDialog)
|
||||
mReviewDialog->setSkillValue(parSkill, value);
|
||||
mReviewDialog->setSkillValue(id, value);
|
||||
}
|
||||
|
||||
void CharacterCreation::configureSkills(const SkillList& major, const SkillList& minor)
|
||||
void CharacterCreation::configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor)
|
||||
{
|
||||
if (mReviewDialog)
|
||||
mReviewDialog->configureSkills(major, minor);
|
||||
|
@ -275,10 +275,9 @@ namespace MWGui
|
|||
mReviewDialog->setAttribute(
|
||||
static_cast<ESM::Attribute::AttributeID>(attributePair.first), attributePair.second);
|
||||
}
|
||||
for (auto& skillPair : mPlayerSkillValues)
|
||||
for (const auto& [skill, value] : mPlayerSkillValues)
|
||||
{
|
||||
mReviewDialog->setSkillValue(
|
||||
static_cast<ESM::Skill::SkillEnum>(skillPair.first), skillPair.second);
|
||||
mReviewDialog->setSkillValue(skill, value);
|
||||
}
|
||||
mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills);
|
||||
|
||||
|
@ -476,14 +475,14 @@ namespace MWGui
|
|||
assert(attributes.size() == klass.mData.mAttribute.size());
|
||||
std::copy(attributes.begin(), attributes.end(), klass.mData.mAttribute.begin());
|
||||
|
||||
std::vector<ESM::Skill::SkillEnum> majorSkills = mCreateClassDialog->getMajorSkills();
|
||||
std::vector<ESM::Skill::SkillEnum> minorSkills = mCreateClassDialog->getMinorSkills();
|
||||
std::vector<ESM::RefId> majorSkills = mCreateClassDialog->getMajorSkills();
|
||||
std::vector<ESM::RefId> minorSkills = mCreateClassDialog->getMinorSkills();
|
||||
assert(majorSkills.size() >= klass.mData.mSkills.size());
|
||||
assert(minorSkills.size() >= klass.mData.mSkills.size());
|
||||
for (size_t i = 0; i < klass.mData.mSkills.size(); ++i)
|
||||
{
|
||||
klass.mData.mSkills[i][1] = majorSkills[i];
|
||||
klass.mData.mSkills[i][0] = minorSkills[i];
|
||||
klass.mData.mSkills[i][1] = majorSkills[i].getIf<ESM::IndexRefId>()->getValue();
|
||||
klass.mData.mSkills[i][0] = minorSkills[i].getIf<ESM::IndexRefId>()->getValue();
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass);
|
||||
|
|
|
@ -38,8 +38,6 @@ namespace MWGui
|
|||
class CharacterCreation : public StatsListener
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> SkillList;
|
||||
|
||||
CharacterCreation(osg::Group* parent, Resource::ResourceSystem* resourceSystem);
|
||||
virtual ~CharacterCreation();
|
||||
|
||||
|
@ -48,8 +46,8 @@ namespace MWGui
|
|||
|
||||
void setValue(std::string_view id, const MWMechanics::AttributeValue& value) override;
|
||||
void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override;
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override;
|
||||
void configureSkills(const SkillList& major, const SkillList& minor) override;
|
||||
void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override;
|
||||
void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor) override;
|
||||
|
||||
void onFrame(float duration);
|
||||
|
||||
|
@ -57,9 +55,9 @@ namespace MWGui
|
|||
osg::Group* mParent;
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
|
||||
SkillList mPlayerMajorSkills, mPlayerMinorSkills;
|
||||
std::vector<ESM::RefId> mPlayerMajorSkills, mPlayerMinorSkills;
|
||||
std::map<int, MWMechanics::AttributeValue> mPlayerAttributes;
|
||||
std::map<int, MWMechanics::SkillValue> mPlayerSkillValues;
|
||||
std::map<ESM::RefId, MWMechanics::SkillValue> mPlayerSkillValues;
|
||||
|
||||
// Dialogs
|
||||
std::unique_ptr<TextInputDialog> mNameDialog;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <MyGUI_Gui.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
#include <MyGUI_ListBox.h>
|
||||
#include <MyGUI_ScrollView.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -266,10 +267,12 @@ namespace MWGui
|
|||
|
||||
for (size_t i = 0; i < klass->mData.mSkills.size(); ++i)
|
||||
{
|
||||
mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]);
|
||||
mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]);
|
||||
ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]);
|
||||
ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]);
|
||||
ESM::RefId minor = ESM::Skill::indexToRefId(klass->mData.mSkills[i][0]);
|
||||
ESM::RefId major = ESM::Skill::indexToRefId(klass->mData.mSkills[i][1]);
|
||||
mMinorSkill[i]->setSkillId(minor);
|
||||
mMajorSkill[i]->setSkillId(major);
|
||||
ToolTips::createSkillToolTip(mMinorSkill[i], minor);
|
||||
ToolTips::createSkillToolTip(mMajorSkill[i], major);
|
||||
}
|
||||
|
||||
setClassImage(mClassImage, mCurrentClassId);
|
||||
|
@ -514,24 +517,24 @@ namespace MWGui
|
|||
return v;
|
||||
}
|
||||
|
||||
std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMajorSkills() const
|
||||
std::vector<ESM::RefId> CreateClassDialog::getMajorSkills() const
|
||||
{
|
||||
std::vector<ESM::Skill::SkillEnum> v;
|
||||
v.reserve(5);
|
||||
for (int i = 0; i < 5; i++)
|
||||
std::vector<ESM::RefId> v;
|
||||
v.reserve(mMajorSkill.size());
|
||||
for (const auto& widget : mMajorSkill)
|
||||
{
|
||||
v.push_back(mMajorSkill[i]->getSkillId());
|
||||
v.push_back(widget->getSkillId());
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMinorSkills() const
|
||||
std::vector<ESM::RefId> CreateClassDialog::getMinorSkills() const
|
||||
{
|
||||
std::vector<ESM::Skill::SkillEnum> v;
|
||||
v.reserve(5);
|
||||
for (int i = 0; i < 5; i++)
|
||||
std::vector<ESM::RefId> v;
|
||||
v.reserve(mMinorSkill.size());
|
||||
for (const auto& widget : mMinorSkill)
|
||||
{
|
||||
v.push_back(mMinorSkill[i]->getSkillId());
|
||||
v.push_back(widget->getSkillId());
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
@ -624,7 +627,7 @@ namespace MWGui
|
|||
|
||||
void CreateClassDialog::onSkillSelected()
|
||||
{
|
||||
ESM::Skill::SkillEnum id = mSkillDialog->getSkillId();
|
||||
ESM::RefId id = mSkillDialog->getSkillId();
|
||||
|
||||
// Avoid duplicate skills by swapping any skill field that matches the selected one
|
||||
for (Widgets::MWSkillPtr& skill : mSkills)
|
||||
|
@ -793,43 +796,33 @@ namespace MWGui
|
|||
// Centre dialog
|
||||
center();
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
std::array<std::pair<MyGUI::ScrollView*, MyGUI::IntCoord>, 3> specializations;
|
||||
getWidget(specializations[ESM::Class::Combat].first, "CombatSkills");
|
||||
getWidget(specializations[ESM::Class::Magic].first, "MagicSkills");
|
||||
getWidget(specializations[ESM::Class::Stealth].first, "StealthSkills");
|
||||
for (auto& [widget, coord] : specializations)
|
||||
{
|
||||
char theIndex = '0' + i;
|
||||
getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex));
|
||||
getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex));
|
||||
getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex));
|
||||
coord.width = widget->getCoord().width;
|
||||
coord.height = 18;
|
||||
while (widget->getChildCount() > 0)
|
||||
MyGUI::Gui::getInstance().destroyWidget(widget->getChildAt(0));
|
||||
}
|
||||
|
||||
struct
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
{
|
||||
Widgets::MWSkillPtr widget;
|
||||
ESM::Skill::SkillEnum skillId;
|
||||
} mSkills[3][9]
|
||||
= { { { mCombatSkill[0], ESM::Skill::Block }, { mCombatSkill[1], ESM::Skill::Armorer },
|
||||
{ mCombatSkill[2], ESM::Skill::MediumArmor }, { mCombatSkill[3], ESM::Skill::HeavyArmor },
|
||||
{ mCombatSkill[4], ESM::Skill::BluntWeapon }, { mCombatSkill[5], ESM::Skill::LongBlade },
|
||||
{ mCombatSkill[6], ESM::Skill::Axe }, { mCombatSkill[7], ESM::Skill::Spear },
|
||||
{ mCombatSkill[8], ESM::Skill::Athletics } },
|
||||
{ { mMagicSkill[0], ESM::Skill::Enchant }, { mMagicSkill[1], ESM::Skill::Destruction },
|
||||
{ mMagicSkill[2], ESM::Skill::Alteration }, { mMagicSkill[3], ESM::Skill::Illusion },
|
||||
{ mMagicSkill[4], ESM::Skill::Conjuration }, { mMagicSkill[5], ESM::Skill::Mysticism },
|
||||
{ mMagicSkill[6], ESM::Skill::Restoration }, { mMagicSkill[7], ESM::Skill::Alchemy },
|
||||
{ mMagicSkill[8], ESM::Skill::Unarmored } },
|
||||
{ { mStealthSkill[0], ESM::Skill::Security }, { mStealthSkill[1], ESM::Skill::Sneak },
|
||||
{ mStealthSkill[2], ESM::Skill::Acrobatics }, { mStealthSkill[3], ESM::Skill::LightArmor },
|
||||
{ mStealthSkill[4], ESM::Skill::ShortBlade }, { mStealthSkill[5], ESM::Skill::Marksman },
|
||||
{ mStealthSkill[6], ESM::Skill::Mercantile }, { mStealthSkill[7], ESM::Skill::Speechcraft },
|
||||
{ mStealthSkill[8], ESM::Skill::HandToHand } } };
|
||||
|
||||
for (int spec = 0; spec < 3; ++spec)
|
||||
{
|
||||
for (int i = 0; i < 9; ++i)
|
||||
{
|
||||
mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId);
|
||||
mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked);
|
||||
ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId());
|
||||
auto& [widget, coord] = specializations[skill.mData.mSpecialization];
|
||||
auto* skillWidget
|
||||
= widget->createWidget<Widgets::MWSkill>("MW_StatNameButton", coord, MyGUI::Align::Default);
|
||||
coord.top += coord.height;
|
||||
skillWidget->setSkillId(skill.mId);
|
||||
skillWidget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked);
|
||||
ToolTips::createSkillToolTip(skillWidget, skill.mId);
|
||||
}
|
||||
for (const auto& [widget, coord] : specializations)
|
||||
{
|
||||
widget->setVisibleVScroll(false);
|
||||
widget->setCanvasSize(MyGUI::IntSize(widget->getWidth(), std::max(widget->getHeight(), coord.top)));
|
||||
widget->setVisibleVScroll(true);
|
||||
widget->setViewOffset(MyGUI::IntPoint());
|
||||
}
|
||||
|
||||
MyGUI::Button* cancelButton;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef MWGUI_CLASS_H
|
||||
#define MWGUI_CLASS_H
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include <MyGUI_EditBox.h>
|
||||
|
@ -218,7 +219,7 @@ namespace MWGui
|
|||
|
||||
bool exit() override;
|
||||
|
||||
ESM::Skill::SkillEnum getSkillId() const { return mSkillId; }
|
||||
ESM::RefId getSkillId() const { return mSkillId; }
|
||||
|
||||
// Events
|
||||
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
|
||||
|
@ -238,11 +239,7 @@ namespace MWGui
|
|||
void onCancelClicked(MyGUI::Widget* _sender);
|
||||
|
||||
private:
|
||||
Widgets::MWSkillPtr mCombatSkill[9];
|
||||
Widgets::MWSkillPtr mMagicSkill[9];
|
||||
Widgets::MWSkillPtr mStealthSkill[9];
|
||||
|
||||
ESM::Skill::SkillEnum mSkillId;
|
||||
ESM::RefId mSkillId;
|
||||
};
|
||||
|
||||
class DescriptionDialog : public WindowModal
|
||||
|
@ -278,8 +275,8 @@ namespace MWGui
|
|||
std::string getDescription() const;
|
||||
ESM::Class::Specialization getSpecializationId() const;
|
||||
std::vector<int> getFavoriteAttributes() const;
|
||||
std::vector<ESM::Skill::SkillEnum> getMajorSkills() const;
|
||||
std::vector<ESM::Skill::SkillEnum> getMinorSkills() const;
|
||||
std::vector<ESM::RefId> getMajorSkills() const;
|
||||
std::vector<ESM::RefId> getMinorSkills() const;
|
||||
|
||||
void setNextButtonShow(bool shown);
|
||||
|
||||
|
@ -318,8 +315,8 @@ namespace MWGui
|
|||
MyGUI::EditBox* mEditName;
|
||||
MyGUI::TextBox* mSpecializationName;
|
||||
Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1;
|
||||
Widgets::MWSkillPtr mMajorSkill[5];
|
||||
Widgets::MWSkillPtr mMinorSkill[5];
|
||||
std::array<Widgets::MWSkillPtr, 5> mMajorSkill;
|
||||
std::array<Widgets::MWSkillPtr, 5> mMinorSkill;
|
||||
std::vector<Widgets::MWSkillPtr> mSkills;
|
||||
std::string mDescription;
|
||||
|
||||
|
|
|
@ -93,11 +93,11 @@ namespace MWGui
|
|||
for (int day = 0; day < mDays; ++day)
|
||||
{
|
||||
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||
const ESM::Skill* skill = skillStore.find(Misc::Rng::rollDice(ESM::Skill::Length, prng));
|
||||
const ESM::Skill* skill = skillStore.searchRandom({}, prng);
|
||||
skills.insert(skill);
|
||||
|
||||
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mIndex);
|
||||
if (skill->mIndex == ESM::Skill::Security || skill->mIndex == ESM::Skill::Sneak)
|
||||
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mId);
|
||||
if (skill->mId == ESM::Skill::Security || skill->mId == ESM::Skill::Sneak)
|
||||
value.setBase(std::min(100.f, value.getBase() + 1));
|
||||
else
|
||||
value.setBase(std::max(0.f, value.getBase() - 1));
|
||||
|
@ -116,9 +116,9 @@ namespace MWGui
|
|||
|
||||
for (const ESM::Skill* skill : skills)
|
||||
{
|
||||
int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mIndex).getBase();
|
||||
int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mId).getBase();
|
||||
std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString();
|
||||
if (skill->mIndex == ESM::Skill::Sneak || skill->mIndex == ESM::Skill::Security)
|
||||
if (skill->mId == ESM::Skill::Sneak || skill->mId == ESM::Skill::Security)
|
||||
skillMsg = gmst.find("sNotifyMessage39")->mValue.getString();
|
||||
|
||||
skillMsg = Misc::StringUtils::format(skillMsg, skill->mName, skillValue);
|
||||
|
|
|
@ -414,13 +414,14 @@ namespace MWGui
|
|||
const ESM::Race* race = store.get<ESM::Race>().find(mCurrentRaceId);
|
||||
for (const auto& bonus : race->mData.mBonus)
|
||||
{
|
||||
if (bonus.mSkill < 0 || bonus.mSkill >= ESM::Skill::Length) // Skip unknown skill indexes
|
||||
ESM::RefId skill = ESM::Skill::indexToRefId(bonus.mSkill);
|
||||
if (skill.empty()) // Skip unknown skill indexes
|
||||
continue;
|
||||
|
||||
skillWidget = mSkillList->createWidget<Widgets::MWSkill>("MW_StatNameValue", coord1, MyGUI::Align::Default);
|
||||
skillWidget->setSkillId(ESM::Skill::SkillEnum(bonus.mSkill));
|
||||
skillWidget->setSkillId(skill);
|
||||
skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(static_cast<float>(bonus.mBonus), 0.f));
|
||||
ToolTips::createSkillToolTip(skillWidget, bonus.mSkill);
|
||||
ToolTips::createSkillToolTip(skillWidget, skill);
|
||||
|
||||
mSkillItems.push_back(skillWidget);
|
||||
|
||||
|
|
|
@ -90,10 +90,10 @@ namespace MWGui
|
|||
getWidget(mSkillView, "SkillView");
|
||||
mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
{
|
||||
mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue()));
|
||||
mSkillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::TextBox*>(nullptr)));
|
||||
mSkillValues.emplace(skill.mId, MWMechanics::SkillValue());
|
||||
mSkillWidgetMap.emplace(skill.mId, static_cast<MyGUI::TextBox*>(nullptr));
|
||||
}
|
||||
|
||||
MyGUI::Button* backButton;
|
||||
|
@ -204,13 +204,14 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value)
|
||||
void ReviewDialog::setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
mSkillValues[skillId] = value;
|
||||
MyGUI::TextBox* widget = mSkillWidgetMap[skillId];
|
||||
mSkillValues[id] = value;
|
||||
MyGUI::TextBox* widget = mSkillWidgetMap[id];
|
||||
if (widget)
|
||||
{
|
||||
float modified = static_cast<float>(value.getModified()), base = static_cast<float>(value.getBase());
|
||||
float modified = value.getModified();
|
||||
float base = value.getBase();
|
||||
std::string text = MyGUI::utility::toString(std::floor(modified));
|
||||
std::string state = "normal";
|
||||
if (modified > base)
|
||||
|
@ -225,21 +226,21 @@ namespace MWGui
|
|||
mUpdateSkillArea = true;
|
||||
}
|
||||
|
||||
void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||
void ReviewDialog::configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor)
|
||||
{
|
||||
mMajorSkills = major;
|
||||
mMinorSkills = minor;
|
||||
|
||||
// Update misc skills with the remaining skills not in major or minor
|
||||
std::set<int> skillSet;
|
||||
std::set<ESM::RefId> skillSet;
|
||||
std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
mMiscSkills.clear();
|
||||
const auto& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>();
|
||||
for (const auto& skill : store)
|
||||
for (const ESM::Skill& skill : store)
|
||||
{
|
||||
if (!skillSet.contains(skill.second.mIndex))
|
||||
mMiscSkills.push_back(skill.second.mIndex);
|
||||
if (!skillSet.contains(skill.mId))
|
||||
mMiscSkills.push_back(skill.mId);
|
||||
}
|
||||
|
||||
mUpdateSkillArea = true;
|
||||
|
@ -327,8 +328,8 @@ namespace MWGui
|
|||
coord2.top += lineHeight;
|
||||
}
|
||||
|
||||
void ReviewDialog::addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault,
|
||||
MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2)
|
||||
void ReviewDialog::addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
|
||||
const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2)
|
||||
{
|
||||
// Add a line separator if there are items above
|
||||
if (!mSkillWidgets.empty())
|
||||
|
@ -339,12 +340,12 @@ namespace MWGui
|
|||
addGroup(
|
||||
MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2);
|
||||
|
||||
for (const int& skillId : skills)
|
||||
for (const ESM::RefId& skillId : skills)
|
||||
{
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().search(skillId);
|
||||
if (!skill) // Skip unknown skill indexes
|
||||
if (!skill) // Skip unknown skills
|
||||
continue;
|
||||
const MWMechanics::SkillValue& stat = mSkillValues.find(skillId)->second;
|
||||
const MWMechanics::SkillValue& stat = mSkillValues.find(skill->mId)->second;
|
||||
int base = stat.getBase();
|
||||
int modified = stat.getModified();
|
||||
|
||||
|
@ -358,10 +359,10 @@ namespace MWGui
|
|||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size() - 1 - i], skillId);
|
||||
ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size() - 1 - i], skill->mId);
|
||||
}
|
||||
|
||||
mSkillWidgetMap[skillId] = widget;
|
||||
mSkillWidgetMap[skill->mId] = widget;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,15 +394,11 @@ namespace MWGui
|
|||
if (!mRaceId.empty())
|
||||
race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(mRaceId);
|
||||
|
||||
int skills[ESM::Skill::Length];
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
skills[i] = mSkillValues.find(i)->second.getBase();
|
||||
|
||||
int attributes[ESM::Attribute::Length];
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase();
|
||||
|
||||
std::vector<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(skills, attributes, race);
|
||||
std::vector<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(mSkillValues, attributes, race);
|
||||
for (ESM::RefId& spellId : selectedSpells)
|
||||
{
|
||||
if (std::find(spells.begin(), spells.end(), spellId) == spells.end())
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace MWGui
|
|||
CLASS_DIALOG,
|
||||
BIRTHSIGN_DIALOG
|
||||
};
|
||||
typedef std::vector<int> SkillList;
|
||||
|
||||
ReviewDialog();
|
||||
|
||||
|
@ -41,8 +40,8 @@ namespace MWGui
|
|||
|
||||
void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value);
|
||||
|
||||
void configureSkills(const SkillList& major, const SkillList& minor);
|
||||
void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value);
|
||||
void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor);
|
||||
void setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value);
|
||||
|
||||
void onOpen() override;
|
||||
|
||||
|
@ -76,8 +75,8 @@ namespace MWGui
|
|||
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||
|
||||
private:
|
||||
void addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault,
|
||||
MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
void addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
|
||||
const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
void addSeparator(MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
void addGroup(std::string_view label, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
MyGUI::TextBox* addValueItem(std::string_view text, const std::string& value, const std::string& state,
|
||||
|
@ -93,9 +92,9 @@ namespace MWGui
|
|||
|
||||
std::map<int, Widgets::MWAttributePtr> mAttributeWidgets;
|
||||
|
||||
SkillList mMajorSkills, mMinorSkills, mMiscSkills;
|
||||
std::map<int, MWMechanics::SkillValue> mSkillValues;
|
||||
std::map<int, MyGUI::TextBox*> mSkillWidgetMap;
|
||||
std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills;
|
||||
std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues;
|
||||
std::map<ESM::RefId, MyGUI::TextBox*> mSkillWidgetMap;
|
||||
ESM::RefId mRaceId, mBirthSignId;
|
||||
std::string mName;
|
||||
ESM::Class mKlass;
|
||||
|
|
|
@ -286,9 +286,9 @@ namespace MWGui
|
|||
exit();
|
||||
}
|
||||
|
||||
void EditEffectDialog::setSkill(int skill)
|
||||
void EditEffectDialog::setSkill(ESM::RefId skill)
|
||||
{
|
||||
mEffect.mSkill = skill;
|
||||
mEffect.mSkill = skill.getIf<ESM::IndexRefId>()->getValue();
|
||||
eventEffectModified(mEffect);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace MWGui
|
|||
|
||||
void setConstantEffect(bool constant);
|
||||
|
||||
void setSkill(int skill);
|
||||
void setSkill(ESM::RefId skill);
|
||||
void setAttribute(int attribute);
|
||||
|
||||
void newEffect(const ESM::MagicEffect* effect);
|
||||
|
|
|
@ -83,7 +83,8 @@ namespace MWGui
|
|||
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
{
|
||||
const ESM::Skill* skill = store->get<ESM::Skill>().find(effectInfo.mKey.mArg);
|
||||
const ESM::Skill* skill
|
||||
= store->get<ESM::Skill>().find(ESM::Skill::indexToRefId(effectInfo.mKey.mArg));
|
||||
sourcesDescription += " (" + skill->mName + ')';
|
||||
}
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace MWGui
|
|||
{
|
||||
const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().find(effectId);
|
||||
const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(effect.mAttribute);
|
||||
const ESM::Skill* skill = store.get<ESM::Skill>().search(effect.mSkill);
|
||||
const ESM::Skill* skill = store.get<ESM::Skill>().search(ESM::Skill::indexToRefId(effect.mSkill));
|
||||
|
||||
std::string fullEffectName = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill);
|
||||
std::string convert = Utf8Stream::lowerCaseUtf8(fullEffectName);
|
||||
|
|
|
@ -83,13 +83,13 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
// Loop over ESM::Skill::SkillEnum
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
{
|
||||
if (stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty)
|
||||
const auto& value = stats.getSkill(skill.mId);
|
||||
if (value != mWatchedSkills[skill.mId] || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedSkills[i] = stats.getSkill(i);
|
||||
setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i));
|
||||
mWatchedSkills[skill.mId] = value;
|
||||
setValue(skill.mId, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,13 +125,13 @@ namespace MWGui
|
|||
setValue("class", cls->mName);
|
||||
|
||||
size_t size = cls->mData.mSkills.size();
|
||||
MWBase::WindowManager::SkillList majorSkills(size);
|
||||
MWBase::WindowManager::SkillList minorSkills(size);
|
||||
std::vector<ESM::RefId> majorSkills(size);
|
||||
std::vector<ESM::RefId> minorSkills(size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
minorSkills[i] = cls->mData.mSkills[i][0];
|
||||
majorSkills[i] = cls->mData.mSkills[i][1];
|
||||
minorSkills[i] = ESM::Skill::indexToRefId(cls->mData.mSkills[i][0]);
|
||||
majorSkills[i] = ESM::Skill::indexToRefId(cls->mData.mSkills[i][1]);
|
||||
}
|
||||
|
||||
configureSkills(majorSkills, minorSkills);
|
||||
|
@ -157,12 +157,10 @@ namespace MWGui
|
|||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
|
||||
void StatsWatcher::setValue(ESM::RefId id, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
/// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we
|
||||
/// allow custom skills.
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->setValue(parSkill, value);
|
||||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value)
|
||||
|
@ -183,7 +181,7 @@ namespace MWGui
|
|||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||
void StatsWatcher::configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor)
|
||||
{
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->configureSkills(major, minor);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef MWGUI_STATSWATCHER_H
|
||||
#define MWGUI_STATSWATCHER_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <components/esm/attr.hpp>
|
||||
|
@ -22,8 +23,8 @@ namespace MWGui
|
|||
virtual void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) {}
|
||||
virtual void setValue(std::string_view, const std::string& value) {}
|
||||
virtual void setValue(std::string_view, int value) {}
|
||||
virtual void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) {}
|
||||
virtual void configureSkills(const std::vector<int>& major, const std::vector<int>& minor) {}
|
||||
virtual void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) {}
|
||||
virtual void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor) {}
|
||||
};
|
||||
|
||||
class StatsWatcher
|
||||
|
@ -31,7 +32,7 @@ namespace MWGui
|
|||
MWWorld::Ptr mWatched;
|
||||
|
||||
MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length];
|
||||
MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length];
|
||||
std::map<ESM::RefId, MWMechanics::SkillValue> mWatchedSkills;
|
||||
|
||||
MWMechanics::DynamicStat<float> mWatchedHealth;
|
||||
MWMechanics::DynamicStat<float> mWatchedMagicka;
|
||||
|
@ -53,8 +54,8 @@ namespace MWGui
|
|||
void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value);
|
||||
void setValue(std::string_view id, const std::string& value);
|
||||
void setValue(std::string_view id, int value);
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);
|
||||
void configureSkills(const std::vector<int>& major, const std::vector<int>& minor);
|
||||
void setValue(ESM::RefId id, const MWMechanics::SkillValue& value);
|
||||
void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor);
|
||||
|
||||
public:
|
||||
StatsWatcher();
|
||||
|
|
|
@ -37,17 +37,8 @@ namespace MWGui
|
|||
: WindowPinnableBase("openmw_stats_window.layout")
|
||||
, NoDrop(drag, mMainWidget)
|
||||
, mSkillView(nullptr)
|
||||
, mMajorSkills()
|
||||
, mMinorSkills()
|
||||
, mMiscSkills()
|
||||
, mSkillValues()
|
||||
, mSkillWidgetMap()
|
||||
, mFactionWidgetMap()
|
||||
, mFactions()
|
||||
, mBirthSignId()
|
||||
, mReputation(0)
|
||||
, mBounty(0)
|
||||
, mSkillWidgets()
|
||||
, mChanged(true)
|
||||
, mMinFullWidth(mMainWidget->getSize().width)
|
||||
{
|
||||
|
@ -67,11 +58,10 @@ namespace MWGui
|
|||
getWidget(mLeftPane, "LeftPane");
|
||||
getWidget(mRightPane, "RightPane");
|
||||
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (const ESM::Skill& skill : store.get<ESM::Skill>())
|
||||
{
|
||||
mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue()));
|
||||
mSkillWidgetMap.insert(
|
||||
std::make_pair(i, std::make_pair((MyGUI::TextBox*)nullptr, (MyGUI::TextBox*)nullptr)));
|
||||
mSkillValues.emplace(skill.mId, MWMechanics::SkillValue());
|
||||
mSkillWidgetMap.emplace(skill.mId, std::make_pair<MyGUI::TextBox*, MyGUI::TextBox*>(nullptr, nullptr));
|
||||
}
|
||||
|
||||
MyGUI::Window* t = mMainWidget->castType<MyGUI::Window>();
|
||||
|
@ -237,7 +227,7 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void setSkillProgress(MyGUI::Widget* w, float progress, int skillId)
|
||||
void setSkillProgress(MyGUI::Widget* w, float progress, ESM::RefId skillId)
|
||||
{
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||
|
@ -255,10 +245,10 @@ namespace MWGui
|
|||
w->setUserString("RangePosition_SkillProgress", MyGUI::utility::toString(progressPercent));
|
||||
}
|
||||
|
||||
void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
|
||||
void StatsWindow::setValue(ESM::RefId id, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
mSkillValues[parSkill] = value;
|
||||
std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets = mSkillWidgetMap[(int)parSkill];
|
||||
mSkillValues[id] = value;
|
||||
std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets = mSkillWidgetMap[id];
|
||||
MyGUI::TextBox* valueWidget = widgets.second;
|
||||
MyGUI::TextBox* nameWidget = widgets.first;
|
||||
if (valueWidget && nameWidget)
|
||||
|
@ -296,8 +286,8 @@ namespace MWGui
|
|||
valueWidget->setUserString("Visible_SkillProgressVBox", "true");
|
||||
valueWidget->setUserString("UserData^Hidden_SkillProgressVBox", "false");
|
||||
|
||||
setSkillProgress(nameWidget, value.getProgress(), parSkill);
|
||||
setSkillProgress(valueWidget, value.getProgress(), parSkill);
|
||||
setSkillProgress(nameWidget, value.getProgress(), id);
|
||||
setSkillProgress(valueWidget, value.getProgress(), id);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -314,21 +304,21 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void StatsWindow::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||
void StatsWindow::configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor)
|
||||
{
|
||||
mMajorSkills = major;
|
||||
mMinorSkills = minor;
|
||||
|
||||
// Update misc skills with the remaining skills not in major or minor
|
||||
std::set<int> skillSet;
|
||||
std::set<ESM::RefId> skillSet;
|
||||
std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
mMiscSkills.clear();
|
||||
const auto& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>();
|
||||
for (const auto& skill : store)
|
||||
{
|
||||
if (!skillSet.contains(skill.second.mIndex))
|
||||
mMiscSkills.push_back(skill.second.mIndex);
|
||||
if (!skillSet.contains(skill.mId))
|
||||
mMiscSkills.push_back(skill.mId);
|
||||
}
|
||||
|
||||
updateSkillArea();
|
||||
|
@ -492,8 +482,8 @@ namespace MWGui
|
|||
return skillNameWidget;
|
||||
}
|
||||
|
||||
void StatsWindow::addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault,
|
||||
MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2)
|
||||
void StatsWindow::addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
|
||||
const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2)
|
||||
{
|
||||
// Add a line separator if there are items above
|
||||
if (!mSkillWidgets.empty())
|
||||
|
@ -504,19 +494,18 @@ namespace MWGui
|
|||
addGroup(
|
||||
MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2);
|
||||
|
||||
for (const int skillId : skills)
|
||||
{
|
||||
if (skillId < 0 || skillId >= ESM::Skill::Length) // Skip unknown skill indexes
|
||||
continue;
|
||||
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||
|
||||
const ESM::Skill* skill = esmStore.get<ESM::Skill>().find(skillId);
|
||||
for (const ESM::RefId& skillId : skills)
|
||||
{
|
||||
const ESM::Skill* skill = esmStore.get<ESM::Skill>().search(skillId);
|
||||
if (!skill) // Skip unknown skills
|
||||
continue;
|
||||
|
||||
const ESM::Attribute* attr = esmStore.get<ESM::Attribute>().find(skill->mData.mAttribute);
|
||||
|
||||
std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets
|
||||
= addValueItem(skill->mName, {}, "normal", coord1, coord2);
|
||||
mSkillWidgetMap[skillId] = widgets;
|
||||
mSkillWidgetMap[skill->mId] = widgets;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
|
@ -532,7 +521,7 @@ namespace MWGui
|
|||
mSkillWidgets[mSkillWidgets.size() - 1 - i]->setUserString("Range_SkillProgress", "100");
|
||||
}
|
||||
|
||||
setValue(static_cast<ESM::Skill::SkillEnum>(skillId), mSkillValues.find(skillId)->second);
|
||||
setValue(skill->mId, mSkillValues.find(skill->mId)->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -640,13 +629,13 @@ namespace MWGui
|
|||
bool firstSkill = true;
|
||||
for (int id : faction->mData.mSkills)
|
||||
{
|
||||
if (id != -1)
|
||||
const ESM::Skill* skill = store.get<ESM::Skill>().search(ESM::Skill::indexToRefId(id));
|
||||
if (skill)
|
||||
{
|
||||
if (!firstSkill)
|
||||
text += ", ";
|
||||
|
||||
firstSkill = false;
|
||||
const ESM::Skill* skill = store.get<ESM::Skill>().find(id);
|
||||
text += MyGUI::TextIterator::toTagsString(skill->mName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ namespace MWGui
|
|||
public:
|
||||
typedef std::map<ESM::RefId, int> FactionList;
|
||||
|
||||
typedef std::vector<int> SkillList;
|
||||
|
||||
StatsWindow(DragAndDrop* drag);
|
||||
|
||||
/// automatically updates all the data in the stats window, but only if it has changed.
|
||||
|
@ -27,8 +25,8 @@ namespace MWGui
|
|||
void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override;
|
||||
void setValue(std::string_view id, const std::string& value) override;
|
||||
void setValue(std::string_view id, int value) override;
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override;
|
||||
void configureSkills(const SkillList& major, const SkillList& minor) override;
|
||||
void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override;
|
||||
void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor) override;
|
||||
|
||||
void setReputation(int reputation)
|
||||
{
|
||||
|
@ -47,8 +45,8 @@ namespace MWGui
|
|||
void onOpen() override { onWindowResize(mMainWidget->castType<MyGUI::Window>()); }
|
||||
|
||||
private:
|
||||
void addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault,
|
||||
MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
void addSkills(const std::vector<ESM::RefId>& skills, const std::string& titleId,
|
||||
const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
void addSeparator(MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
void addGroup(std::string_view label, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2);
|
||||
std::pair<MyGUI::TextBox*, MyGUI::TextBox*> addValueItem(std::string_view text, const std::string& value,
|
||||
|
@ -67,9 +65,9 @@ namespace MWGui
|
|||
|
||||
MyGUI::ScrollView* mSkillView;
|
||||
|
||||
SkillList mMajorSkills, mMinorSkills, mMiscSkills;
|
||||
std::map<int, MWMechanics::SkillValue> mSkillValues;
|
||||
std::map<int, std::pair<MyGUI::TextBox*, MyGUI::TextBox*>> mSkillWidgetMap;
|
||||
std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills;
|
||||
std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues;
|
||||
std::map<ESM::RefId, std::pair<MyGUI::TextBox*, MyGUI::TextBox*>> mSkillWidgetMap;
|
||||
std::map<std::string, MyGUI::Widget*> mFactionWidgetMap;
|
||||
FactionList mFactions; ///< Stores a list of factions and the current rank
|
||||
ESM::RefId mBirthSignId;
|
||||
|
|
|
@ -805,9 +805,9 @@ namespace MWGui
|
|||
mFocusToolTipY = min_y;
|
||||
}
|
||||
|
||||
void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId)
|
||||
void ToolTips::createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId)
|
||||
{
|
||||
if (skillId == -1)
|
||||
if (skillId.empty())
|
||||
return;
|
||||
|
||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||
|
@ -846,7 +846,7 @@ namespace MWGui
|
|||
const MWWorld::Store<ESM::Skill>& skills = MWBase::Environment::get().getESMStore()->get<ESM::Skill>();
|
||||
|
||||
bool isFirst = true;
|
||||
for (const auto& [_, skill] : skills)
|
||||
for (const auto& skill : skills)
|
||||
{
|
||||
if (skill.mData.mSpecialization == specId)
|
||||
{
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace MWGui
|
|||
|
||||
// these do not create an actual tooltip, but they fill in the data that is required so the tooltip
|
||||
// system knows what to show in case this widget is hovered
|
||||
static void createSkillToolTip(MyGUI::Widget* widget, int skillId);
|
||||
static void createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId);
|
||||
static void createAttributeToolTip(MyGUI::Widget* widget, int attributeId);
|
||||
static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId);
|
||||
static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId);
|
||||
|
|
|
@ -21,24 +21,6 @@
|
|||
|
||||
#include "tooltips.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Sorts a container descending by skill value. If skill value is equal, sorts ascending by skill ID.
|
||||
// pair <skill ID, skill value>
|
||||
bool sortSkills(const std::pair<int, int>& left, const std::pair<int, int>& right)
|
||||
{
|
||||
if (left == right)
|
||||
return false;
|
||||
|
||||
if (left.second > right.second)
|
||||
return true;
|
||||
else if (left.second < right.second)
|
||||
return false;
|
||||
|
||||
return left.first < right.first;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
@ -80,34 +62,37 @@ namespace MWGui
|
|||
|
||||
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
|
||||
|
||||
const auto& store = MWBase::Environment::get().getESMStore();
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = store->get<ESM::GameSetting>();
|
||||
const MWWorld::Store<ESM::Skill>& skillStore = store->get<ESM::Skill>();
|
||||
|
||||
// NPC can train you in his best 3 skills
|
||||
std::vector<std::pair<int, float>> skills;
|
||||
std::vector<std::pair<const ESM::Skill*, float>> skills;
|
||||
|
||||
MWMechanics::NpcStats const& actorStats(actor.getClass().getNpcStats(actor));
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (const ESM::Skill& skill : skillStore)
|
||||
{
|
||||
float value = getSkillForTraining(actorStats, i);
|
||||
float value = getSkillForTraining(actorStats, skill.mId);
|
||||
|
||||
skills.emplace_back(i, value);
|
||||
skills.emplace_back(&skill, value);
|
||||
}
|
||||
|
||||
std::sort(skills.begin(), skills.end(), sortSkills);
|
||||
std::sort(skills.begin(), skills.end(), [](const auto& left, const auto& right) {
|
||||
return std::tie(right.second, left.first->mId) < std::tie(left.second, right.first->mId);
|
||||
});
|
||||
|
||||
MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator();
|
||||
MyGUI::Gui::getInstance().destroyWidgets(widgets);
|
||||
|
||||
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
|
||||
|
||||
const auto& store = MWBase::Environment::get().getESMStore();
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = store->get<ESM::GameSetting>();
|
||||
const MWWorld::Store<ESM::Skill>& skillStore = store->get<ESM::Skill>();
|
||||
|
||||
const int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
const ESM::Skill* skill = skills[i].first;
|
||||
int price = static_cast<int>(
|
||||
pcStats.getSkill(skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger());
|
||||
pcStats.getSkill(skill->mId).getBase() * gmst.find("iTrainingMod")->mValue.getInteger());
|
||||
price = std::max(1, price);
|
||||
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);
|
||||
|
||||
|
@ -120,13 +105,12 @@ namespace MWGui
|
|||
button->setUserData(skills[i].first);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected);
|
||||
|
||||
const ESM::Skill* skill = skillStore.find(skills[i].first);
|
||||
button->setCaptionWithReplacing(
|
||||
MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price));
|
||||
|
||||
button->setSize(button->getTextSize().width + 12, button->getSize().height);
|
||||
|
||||
ToolTips::createSkillToolTip(button, skills[i].first);
|
||||
ToolTips::createSkillToolTip(button, skill->mId);
|
||||
}
|
||||
|
||||
center();
|
||||
|
@ -144,29 +128,29 @@ namespace MWGui
|
|||
|
||||
void TrainingWindow::onTrainingSelected(MyGUI::Widget* sender)
|
||||
{
|
||||
int skillId = *sender->getUserData<int>();
|
||||
const ESM::Skill* skill = *sender->getUserData<const ESM::Skill*>();
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
|
||||
|
||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||
|
||||
int price = pcStats.getSkill(skillId).getBase()
|
||||
int price = pcStats.getSkill(skill->mId).getBase()
|
||||
* store.get<ESM::GameSetting>().find("iTrainingMod")->mValue.getInteger();
|
||||
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);
|
||||
|
||||
if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId))
|
||||
return;
|
||||
|
||||
if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skillId) <= pcStats.getSkill(skillId).getBase())
|
||||
if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skill->mId)
|
||||
<= pcStats.getSkill(skill->mId).getBase())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sServiceTrainingWords}");
|
||||
return;
|
||||
}
|
||||
|
||||
// You can not train a skill above its governing attribute
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillId);
|
||||
if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase())
|
||||
if (pcStats.getSkill(skill->mId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}");
|
||||
return;
|
||||
|
@ -176,7 +160,7 @@ namespace MWGui
|
|||
MWWorld::LiveCellRef<ESM::NPC>* playerRef = player.get<ESM::NPC>();
|
||||
|
||||
const ESM::Class* class_ = store.get<ESM::Class>().find(playerRef->mBase->mClass);
|
||||
pcStats.increaseSkill(skillId, *class_, true);
|
||||
pcStats.increaseSkill(skill->mId, *class_, true);
|
||||
|
||||
// remove gold
|
||||
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
|
||||
|
@ -212,11 +196,11 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
|
||||
}
|
||||
|
||||
float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const
|
||||
float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, ESM::RefId id) const
|
||||
{
|
||||
if (mTrainingSkillBasedOnBaseSkill)
|
||||
return stats.getSkill(skillId).getBase();
|
||||
return stats.getSkill(skillId).getModified();
|
||||
return stats.getSkill(id).getBase();
|
||||
return stats.getSkill(id).getModified();
|
||||
}
|
||||
|
||||
void TrainingWindow::onFrame(float dt)
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace MWGui
|
|||
|
||||
// Retrieve the base skill value if the setting 'training skills based on base skill' is set;
|
||||
// otherwise returns the modified skill
|
||||
float getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const;
|
||||
float getSkillForTraining(const MWMechanics::NpcStats& stats, ESM::RefId id) const;
|
||||
|
||||
MyGUI::Widget* mTrainingOptions;
|
||||
MyGUI::Button* mCancelButton;
|
||||
|
|
|
@ -28,28 +28,17 @@ namespace MWGui::Widgets
|
|||
/* MWSkill */
|
||||
|
||||
MWSkill::MWSkill()
|
||||
: mSkillId(ESM::Skill::Length)
|
||||
, mSkillNameWidget(nullptr)
|
||||
: mSkillNameWidget(nullptr)
|
||||
, mSkillValueWidget(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void MWSkill::setSkillId(ESM::Skill::SkillEnum skill)
|
||||
void MWSkill::setSkillId(ESM::RefId skill)
|
||||
{
|
||||
mSkillId = skill;
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
void MWSkill::setSkillNumber(int skill)
|
||||
{
|
||||
if (skill < 0)
|
||||
setSkillId(ESM::Skill::Length);
|
||||
else if (skill < ESM::Skill::Length)
|
||||
setSkillId(static_cast<ESM::Skill::SkillEnum>(skill));
|
||||
else
|
||||
throw std::runtime_error("Skill number out of range");
|
||||
}
|
||||
|
||||
void MWSkill::setSkillValue(const SkillValue& value)
|
||||
{
|
||||
mValue = value;
|
||||
|
@ -374,7 +363,7 @@ namespace MWGui::Widgets
|
|||
|
||||
const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().search(mEffectParams.mEffectID);
|
||||
const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(mEffectParams.mAttribute);
|
||||
const ESM::Skill* skill = store.get<ESM::Skill>().search(mEffectParams.mSkill);
|
||||
const ESM::Skill* skill = store.get<ESM::Skill>().search(ESM::Skill::indexToRefId(mEffectParams.mSkill));
|
||||
|
||||
assert(magicEffect);
|
||||
|
||||
|
|
|
@ -100,11 +100,10 @@ namespace MWGui
|
|||
|
||||
typedef MWMechanics::Stat<float> SkillValue;
|
||||
|
||||
void setSkillId(ESM::Skill::SkillEnum skillId);
|
||||
void setSkillNumber(int skillId);
|
||||
void setSkillId(ESM::RefId skillId);
|
||||
void setSkillValue(const SkillValue& value);
|
||||
|
||||
ESM::Skill::SkillEnum getSkillId() const { return mSkillId; }
|
||||
ESM::RefId getSkillId() const { return mSkillId; }
|
||||
const SkillValue& getSkillValue() const { return mValue; }
|
||||
|
||||
// Events
|
||||
|
@ -125,7 +124,7 @@ namespace MWGui
|
|||
private:
|
||||
void updateWidgets();
|
||||
|
||||
ESM::Skill::SkillEnum mSkillId;
|
||||
ESM::RefId mSkillId;
|
||||
SkillValue mValue;
|
||||
MyGUI::TextBox* mSkillNameWidget;
|
||||
MyGUI::TextBox* mSkillValueWidget;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/scriptscontainer.hpp>
|
||||
|
@ -21,11 +22,12 @@ namespace MWLua
|
|||
class CachedStat
|
||||
{
|
||||
public:
|
||||
using Setter = void (*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&);
|
||||
using Index = std::variant<int, ESM::RefId>;
|
||||
using Setter = void (*)(const Index&, std::string_view, const MWWorld::Ptr&, const sol::object&);
|
||||
|
||||
CachedStat(Setter setter, int index, std::string_view prop)
|
||||
CachedStat(Setter setter, Index index, std::string_view prop)
|
||||
: mSetter(setter)
|
||||
, mIndex(index)
|
||||
, mIndex(std::move(index))
|
||||
, mProp(std::move(prop))
|
||||
{
|
||||
}
|
||||
|
@ -42,7 +44,7 @@ namespace MWLua
|
|||
|
||||
private:
|
||||
Setter mSetter; // Function that updates a stat's property
|
||||
int mIndex; // Optional index to disambiguate the stat
|
||||
Index mIndex; // Optional index to disambiguate the stat
|
||||
std::string_view mProp; // Name of the stat's property
|
||||
};
|
||||
|
||||
|
|
|
@ -24,9 +24,10 @@ namespace
|
|||
{
|
||||
using SelfObject = MWLua::SelfObject;
|
||||
using ObjectVariant = MWLua::ObjectVariant;
|
||||
using Index = const SelfObject::CachedStat::Index&;
|
||||
|
||||
template <class T>
|
||||
auto addIndexedAccessor(int index)
|
||||
auto addIndexedAccessor(Index index)
|
||||
{
|
||||
return [index](const sol::object& o) { return T::create(ObjectVariant(o), index); };
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ namespace
|
|||
|
||||
template <class G>
|
||||
sol::object getValue(const MWLua::Context& context, const ObjectVariant& obj, SelfObject::CachedStat::Setter setter,
|
||||
int index, std::string_view prop, G getter)
|
||||
Index index, std::string_view prop, G getter)
|
||||
{
|
||||
if (obj.isSelfObject())
|
||||
{
|
||||
|
@ -99,14 +100,14 @@ namespace MWLua
|
|||
return sol::make_object(context.mLua->sol(), ptr.getClass().getNpcStats(ptr).getLevelProgress());
|
||||
}
|
||||
|
||||
static std::optional<LevelStat> create(ObjectVariant object, int index)
|
||||
static std::optional<LevelStat> create(ObjectVariant object, Index)
|
||||
{
|
||||
if (!object.ptr().getClass().isActor())
|
||||
return {};
|
||||
return LevelStat{ std::move(object) };
|
||||
}
|
||||
|
||||
static void setValue(int, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
static void setValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
{
|
||||
auto& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
if (prop == "current")
|
||||
|
@ -135,10 +136,11 @@ namespace MWLua
|
|||
});
|
||||
}
|
||||
|
||||
static std::optional<DynamicStat> create(ObjectVariant object, int index)
|
||||
static std::optional<DynamicStat> create(ObjectVariant object, Index i)
|
||||
{
|
||||
if (!object.ptr().getClass().isActor())
|
||||
return {};
|
||||
int index = std::get<int>(i);
|
||||
return DynamicStat{ std::move(object), index };
|
||||
}
|
||||
|
||||
|
@ -149,8 +151,9 @@ namespace MWLua
|
|||
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value;
|
||||
}
|
||||
|
||||
static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
{
|
||||
int index = std::get<int>(i);
|
||||
auto& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
auto stat = stats.getDynamic(index);
|
||||
float floatValue = LuaUtil::cast<float>(value);
|
||||
|
@ -193,10 +196,11 @@ namespace MWLua
|
|||
return std::max(0.f, base - damage + modifier); // Should match AttributeValue::getModified
|
||||
}
|
||||
|
||||
static std::optional<AttributeStat> create(ObjectVariant object, int index)
|
||||
static std::optional<AttributeStat> create(ObjectVariant object, Index i)
|
||||
{
|
||||
if (!object.ptr().getClass().isActor())
|
||||
return {};
|
||||
int index = std::get<int>(i);
|
||||
return AttributeStat{ std::move(object), index };
|
||||
}
|
||||
|
||||
|
@ -207,8 +211,9 @@ namespace MWLua
|
|||
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value;
|
||||
}
|
||||
|
||||
static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
{
|
||||
int index = std::get<int>(i);
|
||||
auto& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
auto stat = stats.getAttribute(index);
|
||||
float floatValue = LuaUtil::cast<float>(value);
|
||||
|
@ -228,36 +233,35 @@ namespace MWLua
|
|||
class SkillStat
|
||||
{
|
||||
ObjectVariant mObject;
|
||||
int mIndex;
|
||||
ESM::RefId mId;
|
||||
|
||||
SkillStat(ObjectVariant object, int index)
|
||||
SkillStat(ObjectVariant object, ESM::RefId id)
|
||||
: mObject(std::move(object))
|
||||
, mIndex(index)
|
||||
, mId(id)
|
||||
{
|
||||
}
|
||||
|
||||
static float getProgress(const MWWorld::Ptr& ptr, int index, const MWMechanics::SkillValue& stat)
|
||||
static float getProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat)
|
||||
{
|
||||
float progress = stat.getProgress();
|
||||
if (progress != 0.f)
|
||||
progress /= getMaxProgress(ptr, index, stat);
|
||||
progress /= getMaxProgress(ptr, id, stat);
|
||||
return progress;
|
||||
}
|
||||
|
||||
static float getMaxProgress(const MWWorld::Ptr& ptr, int index, const MWMechanics::SkillValue& stat)
|
||||
static float getMaxProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat)
|
||||
{
|
||||
const auto& store = *MWBase::Environment::get().getESMStore();
|
||||
const auto cl = store.get<ESM::Class>().find(ptr.get<ESM::NPC>()->mBase->mClass);
|
||||
return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(index, *cl);
|
||||
return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(id, *cl);
|
||||
}
|
||||
|
||||
public:
|
||||
template <class G>
|
||||
sol::object get(const Context& context, std::string_view prop, G getter) const
|
||||
{
|
||||
return getValue(
|
||||
context, mObject, &SkillStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) {
|
||||
return (ptr.getClass().getNpcStats(ptr).getSkill(mIndex).*getter)();
|
||||
return getValue(context, mObject, &SkillStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) {
|
||||
return (ptr.getClass().getNpcStats(ptr).getSkill(mId).*getter)();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -271,30 +275,31 @@ namespace MWLua
|
|||
|
||||
sol::object getProgress(const Context& context) const
|
||||
{
|
||||
return getValue(
|
||||
context, mObject, &SkillStat::setValue, mIndex, "progress", [this](const MWWorld::Ptr& ptr) {
|
||||
return getProgress(ptr, mIndex, ptr.getClass().getNpcStats(ptr).getSkill(mIndex));
|
||||
return getValue(context, mObject, &SkillStat::setValue, mId, "progress", [this](const MWWorld::Ptr& ptr) {
|
||||
return getProgress(ptr, mId, ptr.getClass().getNpcStats(ptr).getSkill(mId));
|
||||
});
|
||||
}
|
||||
|
||||
static std::optional<SkillStat> create(ObjectVariant object, int index)
|
||||
static std::optional<SkillStat> create(ObjectVariant object, Index index)
|
||||
{
|
||||
if (!object.ptr().getClass().isNpc())
|
||||
return {};
|
||||
return SkillStat{ std::move(object), index };
|
||||
ESM::RefId id = std::get<ESM::RefId>(index);
|
||||
return SkillStat{ std::move(object), id };
|
||||
}
|
||||
|
||||
void cache(const Context& context, std::string_view prop, const sol::object& value) const
|
||||
{
|
||||
SelfObject* obj = mObject.asSelfObject();
|
||||
addStatUpdateAction(context.mLuaManager, *obj);
|
||||
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value;
|
||||
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = value;
|
||||
}
|
||||
|
||||
static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
|
||||
{
|
||||
ESM::RefId id = std::get<ESM::RefId>(index);
|
||||
auto& stats = ptr.getClass().getNpcStats(ptr);
|
||||
auto stat = stats.getSkill(index);
|
||||
auto stat = stats.getSkill(id);
|
||||
float floatValue = LuaUtil::cast<float>(value);
|
||||
if (prop == "base")
|
||||
stat.setBase(floatValue);
|
||||
|
@ -306,8 +311,8 @@ namespace MWLua
|
|||
else if (prop == "modifier")
|
||||
stat.setModifier(floatValue);
|
||||
else if (prop == "progress")
|
||||
stat.setProgress(floatValue * getMaxProgress(ptr, index, stat));
|
||||
stats.setSkill(index, stat);
|
||||
stat.setProgress(floatValue * getMaxProgress(ptr, id, stat));
|
||||
stats.setSkill(id, stat);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -385,7 +390,8 @@ namespace MWLua
|
|||
[context](const SkillStat& stat, const sol::object& value) { stat.cache(context, "progress", value); });
|
||||
sol::table skills(context.mLua->sol(), sol::create);
|
||||
npcStats["skills"] = LuaUtil::makeReadOnly(skills);
|
||||
for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id)
|
||||
skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id])] = addIndexedAccessor<SkillStat>(id);
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[skill.mIndex])]
|
||||
= addIndexedAccessor<SkillStat>(skill.mId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace MWLua
|
|||
sol::table skill(context.mLua->sol(), sol::create);
|
||||
book["SKILL"] = LuaUtil::makeStrictReadOnly(skill);
|
||||
book["createRecordDraft"] = tableToBook;
|
||||
for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id)
|
||||
for (int id = 0; id < ESM::Skill::Length; ++id)
|
||||
{
|
||||
std::string skillName = Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id]);
|
||||
skill[skillName] = skillName;
|
||||
|
|
|
@ -576,7 +576,7 @@ std::vector<std::string> MWMechanics::Alchemy::effectsDescription(const MWWorld:
|
|||
if (effectID != -1)
|
||||
{
|
||||
const ESM::Attribute* attribute = store->get<ESM::Attribute>().search(data.mAttributes[i]);
|
||||
const ESM::Skill* skill = store->get<ESM::Skill>().search(data.mAttributes[i]);
|
||||
const ESM::Skill* skill = store->get<ESM::Skill>().search(ESM::Skill::indexToRefId(data.mSkills[i]));
|
||||
std::string effect = getMagicEffectString(*mgef.find(effectID), attribute, skill);
|
||||
|
||||
effects.push_back(effect);
|
||||
|
|
|
@ -27,7 +27,8 @@ namespace MWMechanics
|
|||
ESM::RefId mWeakestSpell;
|
||||
};
|
||||
|
||||
std::vector<ESM::RefId> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race)
|
||||
std::vector<ESM::RefId> autoCalcNpcSpells(
|
||||
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||
|
@ -148,7 +149,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
std::vector<ESM::RefId> autoCalcPlayerSpells(
|
||||
const int* actorSkills, const int* actorAttributes, const ESM::Race* race)
|
||||
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race)
|
||||
{
|
||||
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||
|
||||
|
@ -226,7 +227,8 @@ namespace MWMechanics
|
|||
return selectedSpells;
|
||||
}
|
||||
|
||||
bool attrSkillCheck(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
|
||||
bool attrSkillCheck(
|
||||
const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes)
|
||||
{
|
||||
for (const auto& spellEffect : spell->mEffects.mList)
|
||||
{
|
||||
|
@ -240,8 +242,9 @@ namespace MWMechanics
|
|||
|
||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
||||
{
|
||||
assert(spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length);
|
||||
if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin)
|
||||
ESM::RefId skill = ESM::Skill::indexToRefId(spellEffect.mSkill);
|
||||
auto found = actorSkills.find(skill);
|
||||
if (found == actorSkills.end() || found->second.getBase() < iAutoSpellAttSkillMin)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -256,7 +259,8 @@ namespace MWMechanics
|
|||
return true;
|
||||
}
|
||||
|
||||
void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
|
||||
void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
|
||||
int& effectiveSchool, float& skillTerm)
|
||||
{
|
||||
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
||||
float minChance = std::numeric_limits<float>::max();
|
||||
|
@ -294,7 +298,11 @@ namespace MWMechanics
|
|||
if (effect.mRange == ESM::RT_Target)
|
||||
x *= 1.5f;
|
||||
|
||||
float s = 2.f * actorSkills[spellSchoolToSkill(magicEffect->mData.mSchool)];
|
||||
float s = 0.f;
|
||||
ESM::RefId skill = spellSchoolToSkill(magicEffect->mData.mSchool);
|
||||
auto found = actorSkills.find(skill);
|
||||
if (found != actorSkills.end())
|
||||
s = 2.f * found->second.getBase();
|
||||
if (s - x < minChance)
|
||||
{
|
||||
minChance = s - x;
|
||||
|
@ -304,8 +312,8 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
float calcAutoCastChance(
|
||||
const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool)
|
||||
float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
|
||||
const int* actorAttributes, int effectiveSchool)
|
||||
{
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 100.f;
|
||||
|
@ -315,7 +323,12 @@ namespace MWMechanics
|
|||
|
||||
float skillTerm = 0;
|
||||
if (effectiveSchool != -1)
|
||||
skillTerm = 2.f * actorSkills[spellSchoolToSkill(effectiveSchool)];
|
||||
{
|
||||
ESM::RefId skill = spellSchoolToSkill(effectiveSchool);
|
||||
auto found = actorSkills.find(skill);
|
||||
if (found != actorSkills.end())
|
||||
skillTerm = 2.f * found->second.getBase();
|
||||
}
|
||||
else
|
||||
calcWeakestSchool(
|
||||
spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#ifndef OPENMW_AUTOCALCSPELL_H
|
||||
#define OPENMW_AUTOCALCSPELL_H
|
||||
|
||||
#include "creaturestats.hpp"
|
||||
#include <components/esm/refid.hpp>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
|
@ -17,19 +20,21 @@ namespace MWMechanics
|
|||
/// @note We might want to move this code to a component later, so the editor can use it for preview purposes
|
||||
|
||||
std::vector<ESM::RefId> autoCalcNpcSpells(
|
||||
const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
|
||||
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race);
|
||||
|
||||
std::vector<ESM::RefId> autoCalcPlayerSpells(
|
||||
const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
|
||||
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race);
|
||||
|
||||
// Helpers
|
||||
|
||||
bool attrSkillCheck(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);
|
||||
bool attrSkillCheck(
|
||||
const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes);
|
||||
|
||||
void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm);
|
||||
void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
|
||||
int& effectiveSchool, float& skillTerm);
|
||||
|
||||
float calcAutoCastChance(
|
||||
const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool);
|
||||
float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
|
||||
const int* actorAttributes, int effectiveSchool);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ namespace MWMechanics
|
|||
|
||||
bool validVictim = !victim.isEmpty() && victim.getClass().isActor();
|
||||
|
||||
int weaponSkill = ESM::Skill::Marksman;
|
||||
ESM::RefId weaponSkill = ESM::Skill::Marksman;
|
||||
if (!weapon.isEmpty())
|
||||
weaponSkill = weapon.getClass().getEquipmentSkill(weapon);
|
||||
|
||||
|
|
|
@ -53,8 +53,8 @@ namespace MWMechanics
|
|||
{
|
||||
const auto& store = MWBase::Environment::get().getESMStore();
|
||||
const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().search(mId);
|
||||
return getMagicEffectString(
|
||||
*magicEffect, store->get<ESM::Attribute>().search(mArg), store->get<ESM::Skill>().search(mArg));
|
||||
return getMagicEffectString(*magicEffect, store->get<ESM::Attribute>().search(mArg),
|
||||
store->get<ESM::Skill>().search(ESM::Skill::indexToRefId(mArg)));
|
||||
}
|
||||
|
||||
bool operator<(const EffectKey& left, const EffectKey& right)
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace MWMechanics
|
|||
creatureStats.getActiveSpells().clear(ptr);
|
||||
|
||||
for (size_t i = 0; i < player->mNpdt.mSkills.size(); ++i)
|
||||
npcStats.getSkill(i).setBase(player->mNpdt.mSkills[i]);
|
||||
npcStats.getSkill(ESM::Skill::indexToRefId(i)).setBase(player->mNpdt.mSkills[i]);
|
||||
|
||||
creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt.mStrength);
|
||||
creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt.mIntelligence);
|
||||
|
@ -155,16 +155,16 @@ namespace MWMechanics
|
|||
creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; ++i)
|
||||
for (const ESM::Skill& skill : esmStore.get<ESM::Skill>())
|
||||
{
|
||||
int bonus = 0;
|
||||
|
||||
auto bonusIt = std::find_if(race->mData.mBonus.begin(), race->mData.mBonus.end(),
|
||||
[i](const auto& bonus) { return bonus.mSkill == i; });
|
||||
[&](const auto& bonus) { return bonus.mSkill == skill.mIndex; });
|
||||
if (bonusIt != race->mData.mBonus.end())
|
||||
bonus = bonusIt->mBonus;
|
||||
|
||||
npcStats.getSkill(i).setBase(5 + bonus);
|
||||
npcStats.getSkill(skill.mId).setBase(5 + bonus);
|
||||
}
|
||||
|
||||
for (const ESM::RefId& power : race->mPowers.mList)
|
||||
|
@ -205,29 +205,16 @@ namespace MWMechanics
|
|||
|
||||
for (const auto& skills : class_->mData.mSkills)
|
||||
{
|
||||
int index = skills[i];
|
||||
|
||||
if (index >= 0 && index < ESM::Skill::Length)
|
||||
{
|
||||
npcStats.getSkill(index).setBase(npcStats.getSkill(index).getBase() + bonus);
|
||||
}
|
||||
ESM::RefId id = ESM::Skill::indexToRefId(skills[i]);
|
||||
if (!id.empty())
|
||||
npcStats.getSkill(id).setBase(npcStats.getSkill(id).getBase() + bonus);
|
||||
}
|
||||
}
|
||||
|
||||
const MWWorld::Store<ESM::Skill>& skills = esmStore.get<ESM::Skill>();
|
||||
|
||||
MWWorld::Store<ESM::Skill>::iterator iter = skills.begin();
|
||||
for (; iter != skills.end(); ++iter)
|
||||
for (const ESM::Skill& skill : esmStore.get<ESM::Skill>())
|
||||
{
|
||||
if (iter->second.mData.mSpecialization == class_->mData.mSpecialization)
|
||||
{
|
||||
int index = iter->first;
|
||||
|
||||
if (index >= 0 && index < 27)
|
||||
{
|
||||
npcStats.getSkill(index).setBase(npcStats.getSkill(index).getBase() + 5);
|
||||
}
|
||||
}
|
||||
if (skill.mData.mSpecialization == class_->mData.mSpecialization)
|
||||
npcStats.getSkill(skill.mId).setBase(npcStats.getSkill(skill.mId).getBase() + 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,16 +223,12 @@ namespace MWMechanics
|
|||
if (mRaceSelected)
|
||||
race = esmStore.get<ESM::Race>().find(player->mRace);
|
||||
|
||||
int skills[ESM::Skill::Length];
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
skills[i] = npcStats.getSkill(i).getBase();
|
||||
|
||||
int attributes[ESM::Attribute::Length];
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
attributes[i] = npcStats.getAttribute(i).getBase();
|
||||
npcStats.updateHealth();
|
||||
|
||||
std::vector<ESM::RefId> selectedSpells = autoCalcPlayerSpells(skills, attributes, race);
|
||||
std::vector<ESM::RefId> selectedSpells = autoCalcPlayerSpells(npcStats.getSkills(), attributes, race);
|
||||
|
||||
for (const ESM::RefId& spell : selectedSpells)
|
||||
creatureStats.getSpells().add(spell);
|
||||
|
@ -1921,7 +1904,7 @@ namespace MWMechanics
|
|||
const ESM::Skill* acrobatics
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(ESM::Skill::Acrobatics);
|
||||
MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor);
|
||||
auto& skill = stats.getSkill(acrobatics->mIndex);
|
||||
auto& skill = stats.getSkill(acrobatics->mId);
|
||||
skill.setModifier(acrobatics->mWerewolfValue - skill.getModified());
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ MWMechanics::NpcStats::NpcStats()
|
|||
{
|
||||
mSkillIncreases.resize(ESM::Attribute::Length, 0);
|
||||
mSpecIncreases.resize(3, 0);
|
||||
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
|
||||
mSkills.emplace(skill.mId, SkillValue{});
|
||||
}
|
||||
|
||||
int MWMechanics::NpcStats::getBaseDisposition() const
|
||||
|
@ -42,28 +44,28 @@ void MWMechanics::NpcStats::setBaseDisposition(int disposition)
|
|||
mDisposition = disposition;
|
||||
}
|
||||
|
||||
const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(int index) const
|
||||
const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(ESM::RefId id) const
|
||||
{
|
||||
if (index < 0 || index >= ESM::Skill::Length)
|
||||
throw std::runtime_error("skill index out of range");
|
||||
|
||||
return mSkill[index];
|
||||
auto it = mSkills.find(id);
|
||||
if (it == mSkills.end())
|
||||
throw std::runtime_error("skill not found");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(int index)
|
||||
MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(ESM::RefId id)
|
||||
{
|
||||
if (index < 0 || index >= ESM::Skill::Length)
|
||||
throw std::runtime_error("skill index out of range");
|
||||
|
||||
return mSkill[index];
|
||||
auto it = mSkills.find(id);
|
||||
if (it == mSkills.end())
|
||||
throw std::runtime_error("skill not found");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::setSkill(int index, const MWMechanics::SkillValue& value)
|
||||
void MWMechanics::NpcStats::setSkill(ESM::RefId id, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
if (index < 0 || index >= ESM::Skill::Length)
|
||||
throw std::runtime_error("skill index out of range");
|
||||
|
||||
mSkill[index] = value;
|
||||
auto it = mSkills.find(id);
|
||||
if (it == mSkills.end())
|
||||
throw std::runtime_error("skill not found");
|
||||
it->second = value;
|
||||
}
|
||||
|
||||
const std::map<ESM::RefId, int>& MWMechanics::NpcStats::getFactionRanks() const
|
||||
|
@ -154,22 +156,23 @@ void MWMechanics::NpcStats::setFactionReputation(const ESM::RefId& faction, int
|
|||
mFactionReputation[faction] = value;
|
||||
}
|
||||
|
||||
float MWMechanics::NpcStats::getSkillProgressRequirement(int skillIndex, const ESM::Class& class_) const
|
||||
float MWMechanics::NpcStats::getSkillProgressRequirement(ESM::RefId id, const ESM::Class& class_) const
|
||||
{
|
||||
float progressRequirement = static_cast<float>(1 + getSkill(skillIndex).getBase());
|
||||
float progressRequirement = 1.f + getSkill(id).getBase();
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(id);
|
||||
|
||||
float typeFactor = gmst.find("fMiscSkillBonus")->mValue.getFloat();
|
||||
|
||||
for (const auto& skills : class_.mData.mSkills)
|
||||
{
|
||||
if (skills[0] == skillIndex)
|
||||
if (skills[0] == skill->mIndex)
|
||||
{
|
||||
typeFactor = gmst.find("fMinorSkillBonus")->mValue.getFloat();
|
||||
break;
|
||||
}
|
||||
else if (skills[1] == skillIndex)
|
||||
else if (skills[1] == skill->mIndex)
|
||||
{
|
||||
typeFactor = gmst.find("fMajorSkillBonus")->mValue.getFloat();
|
||||
break;
|
||||
|
@ -183,7 +186,6 @@ float MWMechanics::NpcStats::getSkillProgressRequirement(int skillIndex, const E
|
|||
|
||||
float specialisationFactor = 1;
|
||||
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillIndex);
|
||||
if (skill->mData.mSpecialization == class_.mData.mSpecialization)
|
||||
{
|
||||
specialisationFactor = gmst.find("fSpecialSkillBonus")->mValue.getFloat();
|
||||
|
@ -196,9 +198,9 @@ float MWMechanics::NpcStats::getSkillProgressRequirement(int skillIndex, const E
|
|||
return progressRequirement;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::useSkill(int skillIndex, const ESM::Class& class_, int usageType, float extraFactor)
|
||||
void MWMechanics::NpcStats::useSkill(ESM::RefId id, const ESM::Class& class_, int usageType, float extraFactor)
|
||||
{
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillIndex);
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(id);
|
||||
float skillGain = 1;
|
||||
if (usageType >= 4)
|
||||
throw std::runtime_error("skill usage type out of range");
|
||||
|
@ -210,21 +212,21 @@ void MWMechanics::NpcStats::useSkill(int skillIndex, const ESM::Class& class_, i
|
|||
}
|
||||
skillGain *= extraFactor;
|
||||
|
||||
MWMechanics::SkillValue& value = getSkill(skillIndex);
|
||||
MWMechanics::SkillValue& value = getSkill(skill->mId);
|
||||
|
||||
value.setProgress(value.getProgress() + skillGain);
|
||||
|
||||
if (int(value.getProgress()) >= int(getSkillProgressRequirement(skillIndex, class_)))
|
||||
if (int(value.getProgress()) >= int(getSkillProgressRequirement(skill->mId, class_)))
|
||||
{
|
||||
// skill levelled up
|
||||
increaseSkill(skillIndex, class_, false);
|
||||
increaseSkill(skill->mId, class_, false);
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::increaseSkill(
|
||||
int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook)
|
||||
void MWMechanics::NpcStats::increaseSkill(ESM::RefId id, const ESM::Class& class_, bool preserveProgress, bool readBook)
|
||||
{
|
||||
float base = getSkill(skillIndex).getBase();
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(id);
|
||||
float base = getSkill(skill->mId).getBase();
|
||||
|
||||
if (base >= 100.f)
|
||||
return;
|
||||
|
@ -237,13 +239,13 @@ void MWMechanics::NpcStats::increaseSkill(
|
|||
int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo
|
||||
for (const auto& skills : class_.mData.mSkills)
|
||||
{
|
||||
if (skills[0] == skillIndex)
|
||||
if (skills[0] == skill->mIndex)
|
||||
{
|
||||
mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger();
|
||||
increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger();
|
||||
break;
|
||||
}
|
||||
else if (skills[1] == skillIndex)
|
||||
else if (skills[1] == skill->mIndex)
|
||||
{
|
||||
mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger();
|
||||
increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger();
|
||||
|
@ -251,7 +253,6 @@ void MWMechanics::NpcStats::increaseSkill(
|
|||
}
|
||||
}
|
||||
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillIndex);
|
||||
mSkillIncreases[skill->mData.mAttribute] += increase;
|
||||
|
||||
mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger();
|
||||
|
@ -275,9 +276,9 @@ void MWMechanics::NpcStats::increaseSkill(
|
|||
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never);
|
||||
}
|
||||
|
||||
getSkill(skillIndex).setBase(base);
|
||||
getSkill(skill->mId).setBase(base);
|
||||
if (!preserveProgress)
|
||||
getSkill(skillIndex).setProgress(0);
|
||||
getSkill(skill->mId).setProgress(0);
|
||||
}
|
||||
|
||||
int MWMechanics::NpcStats::getLevelProgress() const
|
||||
|
@ -388,9 +389,10 @@ bool MWMechanics::NpcStats::hasSkillsForRank(const ESM::RefId& factionId, int ra
|
|||
|
||||
std::vector<int> skills;
|
||||
|
||||
for (int id : faction.mData.mSkills)
|
||||
for (int index : faction.mData.mSkills)
|
||||
{
|
||||
if (id != -1)
|
||||
ESM::RefId id = ESM::Skill::indexToRefId(index);
|
||||
if (!id.empty())
|
||||
skills.push_back(static_cast<int>(getSkill(id).getBase()));
|
||||
}
|
||||
|
||||
|
@ -470,8 +472,12 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const
|
|||
|
||||
state.mDisposition = mDisposition;
|
||||
|
||||
for (size_t i = 0; i < state.mSkills.size(); ++i)
|
||||
mSkill[i].writeState(state.mSkills[i]);
|
||||
for (const auto& [id, value] : mSkills)
|
||||
{
|
||||
// TODO extend format
|
||||
auto index = id.getIf<ESM::IndexRefId>()->getValue();
|
||||
value.writeState(state.mSkills[index]);
|
||||
}
|
||||
|
||||
state.mIsWerewolf = mIsWerewolf;
|
||||
|
||||
|
@ -524,7 +530,11 @@ void MWMechanics::NpcStats::readState(const ESM::NpcStats& state)
|
|||
mDisposition = state.mDisposition;
|
||||
|
||||
for (size_t i = 0; i < state.mSkills.size(); ++i)
|
||||
mSkill[i].readState(state.mSkills[i]);
|
||||
{
|
||||
// TODO extend format
|
||||
ESM::RefId id = ESM::Skill::indexToRefId(i);
|
||||
mSkills[id].readState(state.mSkills[i]);
|
||||
}
|
||||
|
||||
mIsWerewolf = state.mIsWerewolf;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "creaturestats.hpp"
|
||||
#include <components/esm/refid.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
@ -21,7 +22,7 @@ namespace MWMechanics
|
|||
class NpcStats : public CreatureStats
|
||||
{
|
||||
int mDisposition;
|
||||
SkillValue mSkill[ESM::Skill::Length]; // SkillValue.mProgress used by the player only
|
||||
std::map<ESM::RefId, SkillValue> mSkills; // SkillValue.mProgress used by the player only
|
||||
|
||||
int mReputation;
|
||||
int mCrimeId;
|
||||
|
@ -58,9 +59,9 @@ namespace MWMechanics
|
|||
int getCrimeId() const;
|
||||
void setCrimeId(int id);
|
||||
|
||||
const SkillValue& getSkill(int index) const;
|
||||
SkillValue& getSkill(int index);
|
||||
void setSkill(int index, const SkillValue& value);
|
||||
const SkillValue& getSkill(ESM::RefId id) const;
|
||||
SkillValue& getSkill(ESM::RefId id);
|
||||
void setSkill(ESM::RefId id, const SkillValue& value);
|
||||
|
||||
int getFactionRank(const ESM::RefId& faction) const;
|
||||
const std::map<ESM::RefId, int>& getFactionRanks() const;
|
||||
|
@ -79,12 +80,12 @@ namespace MWMechanics
|
|||
|
||||
bool isInFaction(const ESM::RefId& faction) const;
|
||||
|
||||
float getSkillProgressRequirement(int skillIndex, const ESM::Class& class_) const;
|
||||
float getSkillProgressRequirement(ESM::RefId id, const ESM::Class& class_) const;
|
||||
|
||||
void useSkill(int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor = 1.f);
|
||||
void useSkill(ESM::RefId id, const ESM::Class& class_, int usageType = -1, float extraFactor = 1.f);
|
||||
///< Increase skill by usage.
|
||||
|
||||
void increaseSkill(int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook = false);
|
||||
void increaseSkill(ESM::RefId id, const ESM::Class& class_, bool preserveProgress, bool readBook = false);
|
||||
|
||||
int getLevelProgress() const;
|
||||
|
||||
|
@ -133,6 +134,8 @@ namespace MWMechanics
|
|||
|
||||
void readState(const ESM::CreatureStats& state);
|
||||
void readState(const ESM::NpcStats& state);
|
||||
|
||||
const std::map<ESM::RefId, SkillValue>& getSkills() const { return mSkills; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ namespace
|
|||
void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
|
||||
{
|
||||
auto& npcStats = target.getClass().getNpcStats(target);
|
||||
auto& skill = npcStats.getSkill(effect.mArg);
|
||||
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg));
|
||||
if (effect.mEffectId == ESM::MagicEffect::DamageSkill)
|
||||
magnitude = std::min(skill.getModified(), magnitude);
|
||||
skill.damage(magnitude);
|
||||
|
@ -114,14 +114,14 @@ namespace
|
|||
void restoreSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
|
||||
{
|
||||
auto& npcStats = target.getClass().getNpcStats(target);
|
||||
auto& skill = npcStats.getSkill(effect.mArg);
|
||||
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg));
|
||||
skill.restore(magnitude);
|
||||
}
|
||||
|
||||
void fortifySkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
|
||||
{
|
||||
auto& npcStats = target.getClass().getNpcStats(target);
|
||||
auto& skill = npcStats.getSkill(effect.mArg);
|
||||
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg));
|
||||
skill.setModifier(skill.getModifier() + magnitude);
|
||||
}
|
||||
|
||||
|
@ -668,7 +668,7 @@ namespace MWMechanics
|
|||
if (spellParams.getType() == ESM::ActiveSpells::Type_Ability)
|
||||
{
|
||||
auto& npcStats = target.getClass().getNpcStats(target);
|
||||
SkillValue& skill = npcStats.getSkill(effect.mArg);
|
||||
SkillValue& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg));
|
||||
// Damage Skill abilities reduce base skill :todd:
|
||||
skill.setBase(std::max(skill.getBase() - effect.mMagnitude, 0.f));
|
||||
}
|
||||
|
@ -760,7 +760,7 @@ namespace MWMechanics
|
|||
{
|
||||
// Abilities affect base stats, but not for drain
|
||||
auto& npcStats = target.getClass().getNpcStats(target);
|
||||
auto& skill = npcStats.getSkill(effect.mArg);
|
||||
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg));
|
||||
skill.setBase(skill.getBase() + effect.mMagnitude);
|
||||
}
|
||||
else
|
||||
|
@ -1218,7 +1218,7 @@ namespace MWMechanics
|
|||
if (spellParams.getType() == ESM::ActiveSpells::Type_Ability)
|
||||
{
|
||||
auto& npcStats = target.getClass().getNpcStats(target);
|
||||
auto& skill = npcStats.getSkill(effect.mArg);
|
||||
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg));
|
||||
skill.setBase(skill.getBase() - effect.mMagnitude);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -565,7 +565,7 @@ namespace MWMechanics
|
|||
case ESM::MagicEffect::DrainSkill:
|
||||
if (enemy.isEmpty() || !enemy.getClass().isNpc())
|
||||
return 0.f;
|
||||
if (enemy.getClass().getSkill(enemy, effect.mSkill) <= 0)
|
||||
if (enemy.getClass().getSkill(enemy, ESM::Skill::indexToRefId(effect.mSkill)) <= 0)
|
||||
return 0.f;
|
||||
break;
|
||||
|
||||
|
@ -600,7 +600,7 @@ namespace MWMechanics
|
|||
&& (e != ESM::MagicEffect::BoundLongbow || effect.mEffectID == e
|
||||
|| rateAmmo(actor, enemy, getWeaponType(ESM::Weapon::MarksmanBow)->mAmmoType) <= 0.f))
|
||||
return 0.f;
|
||||
ESM::Skill::SkillEnum skill = ESM::Skill::ShortBlade;
|
||||
ESM::RefId skill = ESM::Skill::ShortBlade;
|
||||
if (effect.mEffectID == ESM::MagicEffect::BoundLongsword)
|
||||
skill = ESM::Skill::LongBlade;
|
||||
else if (effect.mEffectID == ESM::MagicEffect::BoundMace)
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
ESM::Skill::SkillEnum spellSchoolToSkill(int school)
|
||||
ESM::RefId spellSchoolToSkill(int school)
|
||||
{
|
||||
static const std::array<ESM::Skill::SkillEnum, 6> schoolSkillArray{
|
||||
static const std::array<ESM::RefId, 6> schoolSkillArray{
|
||||
ESM::Skill::Alteration,
|
||||
ESM::Skill::Conjuration,
|
||||
ESM::Skill::Destruction,
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace MWWorld
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
|
||||
ESM::RefId spellSchoolToSkill(int school);
|
||||
|
||||
enum class EffectCostMethod
|
||||
{
|
||||
|
|
|
@ -120,8 +120,8 @@ namespace MWMechanics
|
|||
int value = 50.f;
|
||||
if (actor.getClass().isNpc())
|
||||
{
|
||||
int skill = item.getClass().getEquipmentSkill(item);
|
||||
if (skill != -1)
|
||||
ESM::RefId skill = item.getClass().getEquipmentSkill(item);
|
||||
if (!skill.empty())
|
||||
value = actor.getClass().getSkill(actor, skill);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -392,7 +392,7 @@ namespace MWScript
|
|||
return;
|
||||
}
|
||||
|
||||
int skill = it->getClass().getEquipmentSkill(*it);
|
||||
ESM::RefId skill = it->getClass().getEquipmentSkill(*it);
|
||||
if (skill == ESM::Skill::HeavyArmor)
|
||||
runtime.push(2);
|
||||
else if (skill == ESM::Skill::MediumArmor)
|
||||
|
|
|
@ -345,11 +345,11 @@ namespace MWScript
|
|||
template <class R>
|
||||
class OpGetSkill : public Interpreter::Opcode0
|
||||
{
|
||||
int mIndex;
|
||||
ESM::RefId mId;
|
||||
|
||||
public:
|
||||
OpGetSkill(int index)
|
||||
: mIndex(index)
|
||||
OpGetSkill(ESM::RefId id)
|
||||
: mId(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -357,7 +357,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex);
|
||||
Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mId);
|
||||
|
||||
runtime.push(value);
|
||||
}
|
||||
|
@ -366,11 +366,11 @@ namespace MWScript
|
|||
template <class R>
|
||||
class OpSetSkill : public Interpreter::Opcode0
|
||||
{
|
||||
int mIndex;
|
||||
ESM::RefId mId;
|
||||
|
||||
public:
|
||||
OpSetSkill(int index)
|
||||
: mIndex(index)
|
||||
OpSetSkill(ESM::RefId id)
|
||||
: mId(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -383,18 +383,18 @@ namespace MWScript
|
|||
|
||||
MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats(ptr);
|
||||
|
||||
stats.getSkill(mIndex).setBase(value, true);
|
||||
stats.getSkill(mId).setBase(value, true);
|
||||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpModSkill : public Interpreter::Opcode0
|
||||
{
|
||||
int mIndex;
|
||||
ESM::RefId mId;
|
||||
|
||||
public:
|
||||
OpModSkill(int index)
|
||||
: mIndex(index)
|
||||
OpModSkill(ESM::RefId id)
|
||||
: mId(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -405,7 +405,7 @@ namespace MWScript
|
|||
Interpreter::Type_Float value = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::SkillValue& skill = ptr.getClass().getNpcStats(ptr).getSkill(mIndex);
|
||||
MWMechanics::SkillValue& skill = ptr.getClass().getNpcStats(ptr).getSkill(mId);
|
||||
modStat(skill, value);
|
||||
}
|
||||
};
|
||||
|
@ -1364,14 +1364,15 @@ namespace MWScript
|
|||
|
||||
for (int i = 0; i < Compiler::Stats::numberOfSkills; ++i)
|
||||
{
|
||||
interpreter.installSegment5<OpGetSkill<ImplicitRef>>(Compiler::Stats::opcodeGetSkill + i, i);
|
||||
interpreter.installSegment5<OpGetSkill<ExplicitRef>>(Compiler::Stats::opcodeGetSkillExplicit + i, i);
|
||||
ESM::RefId id = ESM::Skill::indexToRefId(i);
|
||||
interpreter.installSegment5<OpGetSkill<ImplicitRef>>(Compiler::Stats::opcodeGetSkill + i, id);
|
||||
interpreter.installSegment5<OpGetSkill<ExplicitRef>>(Compiler::Stats::opcodeGetSkillExplicit + i, id);
|
||||
|
||||
interpreter.installSegment5<OpSetSkill<ImplicitRef>>(Compiler::Stats::opcodeSetSkill + i, i);
|
||||
interpreter.installSegment5<OpSetSkill<ExplicitRef>>(Compiler::Stats::opcodeSetSkillExplicit + i, i);
|
||||
interpreter.installSegment5<OpSetSkill<ImplicitRef>>(Compiler::Stats::opcodeSetSkill + i, id);
|
||||
interpreter.installSegment5<OpSetSkill<ExplicitRef>>(Compiler::Stats::opcodeSetSkillExplicit + i, id);
|
||||
|
||||
interpreter.installSegment5<OpModSkill<ImplicitRef>>(Compiler::Stats::opcodeModSkill + i, i);
|
||||
interpreter.installSegment5<OpModSkill<ExplicitRef>>(Compiler::Stats::opcodeModSkillExplicit + i, i);
|
||||
interpreter.installSegment5<OpModSkill<ImplicitRef>>(Compiler::Stats::opcodeModSkill + i, id);
|
||||
interpreter.installSegment5<OpModSkill<ExplicitRef>>(Compiler::Stats::opcodeModSkillExplicit + i, id);
|
||||
}
|
||||
|
||||
interpreter.installSegment5<OpGetPCCrimeLevel>(Compiler::Stats::opcodeGetPCCrimeLevel);
|
||||
|
|
|
@ -16,19 +16,4 @@ namespace MWWorld
|
|||
{
|
||||
actor.getClass().consume(getTarget(), actor);
|
||||
}
|
||||
|
||||
ActionApplyWithSkill::ActionApplyWithSkill(const Ptr& object, const ESM::RefId& id, int skillIndex, int usageType)
|
||||
: Action(false, object)
|
||||
, mId(id)
|
||||
, mSkillIndex(skillIndex)
|
||||
, mUsageType(usageType)
|
||||
{
|
||||
}
|
||||
|
||||
void ActionApplyWithSkill::executeImp(const Ptr& actor)
|
||||
{
|
||||
bool consumed = actor.getClass().consume(getTarget(), actor);
|
||||
if (consumed && mUsageType != -1 && actor == MWMechanics::getPlayer())
|
||||
actor.getClass().skillUsageSucceeded(actor, mSkillIndex, mUsageType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,6 @@ namespace MWWorld
|
|||
public:
|
||||
ActionApply(const Ptr& object, const ESM::RefId& id);
|
||||
};
|
||||
|
||||
class ActionApplyWithSkill : public Action
|
||||
{
|
||||
ESM::RefId mId;
|
||||
int mSkillIndex;
|
||||
int mUsageType;
|
||||
|
||||
void executeImp(const Ptr& actor) override;
|
||||
|
||||
public:
|
||||
ActionApplyWithSkill(const Ptr& object, const ESM::RefId& id, int skillIndex, int usageType);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <components/esm3/loadbook.hpp>
|
||||
#include <components/esm3/loadclas.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -46,15 +47,15 @@ namespace MWWorld
|
|||
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor);
|
||||
|
||||
// Skill gain from books
|
||||
if (ref->mBase->mData.mSkillId >= 0 && ref->mBase->mData.mSkillId < ESM::Skill::Length
|
||||
&& !npcStats.hasBeenUsed(ref->mBase->mId))
|
||||
ESM::RefId skill = ESM::Skill::indexToRefId(ref->mBase->mData.mSkillId);
|
||||
if (!skill.empty() && !npcStats.hasBeenUsed(ref->mBase->mId))
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::NPC>* playerRef = actor.get<ESM::NPC>();
|
||||
|
||||
const ESM::Class* class_
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::Class>().find(playerRef->mBase->mClass);
|
||||
|
||||
npcStats.increaseSkill(ref->mBase->mData.mSkillId, *class_, true, true);
|
||||
npcStats.increaseSkill(skill, *class_, true, true);
|
||||
|
||||
npcStats.flagAsUsed(ref->mBase->mId);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace MWWorld
|
|||
return false;
|
||||
}
|
||||
|
||||
void Class::skillUsageSucceeded(const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const
|
||||
void Class::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const
|
||||
{
|
||||
throw std::runtime_error("class does not represent an actor");
|
||||
}
|
||||
|
@ -209,9 +209,9 @@ namespace MWWorld
|
|||
return std::make_pair(std::vector<int>(), false);
|
||||
}
|
||||
|
||||
int Class::getEquipmentSkill(const ConstPtr& ptr) const
|
||||
ESM::RefId Class::getEquipmentSkill(const ConstPtr& ptr) const
|
||||
{
|
||||
return -1;
|
||||
return {};
|
||||
}
|
||||
|
||||
int Class::getValue(const ConstPtr& ptr) const
|
||||
|
@ -438,7 +438,7 @@ namespace MWWorld
|
|||
return canSwim(ptr) || canWalk(ptr) || canFly(ptr);
|
||||
}
|
||||
|
||||
float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const
|
||||
float Class::getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const
|
||||
{
|
||||
throw std::runtime_error("class does not support skills");
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "../mwmechanics/aisetting.hpp"
|
||||
#include <components/esm/refid.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -208,10 +209,9 @@ namespace MWWorld
|
|||
///
|
||||
/// Default implementation: return (empty vector, false).
|
||||
|
||||
virtual int getEquipmentSkill(const ConstPtr& ptr) const;
|
||||
/// Return the index of the skill this item corresponds to when equipped or -1, if there is
|
||||
/// no such skill.
|
||||
/// (default implementation: return -1)
|
||||
virtual ESM::RefId getEquipmentSkill(const ConstPtr& ptr) const;
|
||||
/// Return the index of the skill this item corresponds to when equipped.
|
||||
/// (default implementation: return empty ref id)
|
||||
|
||||
virtual int getValue(const ConstPtr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
@ -234,7 +234,7 @@ namespace MWWorld
|
|||
///< Consume an item, e. g. a potion.
|
||||
|
||||
virtual void skillUsageSucceeded(
|
||||
const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor = 1.f) const;
|
||||
const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor = 1.f) const;
|
||||
///< Inform actor \a ptr that a skill use has succeeded.
|
||||
///
|
||||
/// (default implementations: throws an exception)
|
||||
|
@ -340,7 +340,7 @@ namespace MWWorld
|
|||
bool isPureLandCreature(const MWWorld::Ptr& ptr) const;
|
||||
bool isMobile(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;
|
||||
virtual float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const;
|
||||
|
||||
virtual void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const;
|
||||
///< Read additional state from \a state into \a ptr.
|
||||
|
|
|
@ -236,7 +236,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
|
|||
return;
|
||||
}
|
||||
|
||||
static const ESM::Skill::SkillEnum weaponSkills[] = {
|
||||
static const ESM::RefId weaponSkills[] = {
|
||||
ESM::Skill::LongBlade,
|
||||
ESM::Skill::Axe,
|
||||
ESM::Skill::Spear,
|
||||
|
@ -285,7 +285,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
|
|||
|
||||
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
|
||||
{
|
||||
float skillValue = mActor.getClass().getSkill(mActor, static_cast<int>(weaponSkills[j]));
|
||||
float skillValue = mActor.getClass().getSkill(mActor, weaponSkills[j]);
|
||||
if (skillValue > max && !weaponSkillVisited[j])
|
||||
{
|
||||
max = skillValue;
|
||||
|
|
|
@ -221,7 +221,7 @@ namespace MWWorld
|
|||
creatureStats.mAiSettings[i].mMod = 0.f;
|
||||
if (npcStats)
|
||||
{
|
||||
for (std::size_t i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (std::size_t i = 0; i < npcStats->mSkills.size(); ++i)
|
||||
npcStats->mSkills[i].mMod = 0.f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,23 +59,23 @@ namespace MWWorld
|
|||
{
|
||||
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer());
|
||||
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
mSaveSkills[i] = stats.getSkill(i).getModified();
|
||||
for (size_t i = 0; i < mSaveSkills.size(); ++i)
|
||||
mSaveSkills[i] = stats.getSkill(ESM::Skill::indexToRefId(i)).getModified();
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
mSaveAttributes[i] = stats.getAttribute(i).getModified();
|
||||
}
|
||||
|
||||
void Player::restoreStats()
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||
const auto& store = MWBase::Environment::get().getESMStore();
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = store->get<ESM::GameSetting>();
|
||||
MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());
|
||||
MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());
|
||||
MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);
|
||||
creatureStats.setHealth(health.getBase() / gmst.find("fWereWolfHealth")->mValue.getFloat());
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (size_t i = 0; i < mSaveSkills.size(); ++i)
|
||||
{
|
||||
auto& skill = npcStats.getSkill(i);
|
||||
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(i));
|
||||
skill.restore(skill.getDamage());
|
||||
skill.setModifier(mSaveSkills[i] - skill.getBase());
|
||||
}
|
||||
|
@ -103,13 +103,13 @@ namespace MWWorld
|
|||
npcStats.setAttribute(attribute.mId, value);
|
||||
}
|
||||
|
||||
for (const auto& [_, skill] : store->get<ESM::Skill>())
|
||||
for (const auto& skill : store->get<ESM::Skill>())
|
||||
{
|
||||
// Acrobatics is set separately for some reason.
|
||||
if (skill.mIndex == ESM::Skill::Acrobatics)
|
||||
if (skill.mId == ESM::Skill::Acrobatics)
|
||||
continue;
|
||||
|
||||
MWMechanics::SkillValue& value = npcStats.getSkill(skill.mIndex);
|
||||
MWMechanics::SkillValue& value = npcStats.getSkill(skill.mId);
|
||||
value.setModifier(skill.mWerewolfValue - value.getModified());
|
||||
}
|
||||
}
|
||||
|
@ -251,10 +251,7 @@ namespace MWWorld
|
|||
mPreviousItems.clear();
|
||||
mLastKnownExteriorPosition = osg::Vec3f(0, 0, 0);
|
||||
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
{
|
||||
mSaveSkills[i] = 0.f;
|
||||
}
|
||||
mSaveSkills.fill(0.f);
|
||||
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
{
|
||||
|
@ -296,7 +293,7 @@ namespace MWWorld
|
|||
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
player.mSaveAttributes[i] = mSaveAttributes[i];
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (size_t i = 0; i < mSaveSkills.size(); ++i)
|
||||
player.mSaveSkills[i] = mSaveSkills[i];
|
||||
|
||||
player.mPreviousItems = mPreviousItems;
|
||||
|
@ -334,7 +331,7 @@ namespace MWWorld
|
|||
|
||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||
mSaveAttributes[i] = player.mSaveAttributes[i];
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (size_t i = 0; i < mSaveSkills.size(); ++i)
|
||||
mSaveSkills[i] = player.mSaveSkills[i];
|
||||
|
||||
if (player.mObject.mNpcStats.mIsWerewolf)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef GAME_MWWORLD_PLAYER_H
|
||||
#define GAME_MWWORLD_PLAYER_H
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
#include "../mwworld/livecellref.hpp"
|
||||
|
@ -50,7 +51,7 @@ namespace MWWorld
|
|||
PreviousItems mPreviousItems;
|
||||
|
||||
// Saved stats prior to becoming a werewolf
|
||||
float mSaveSkills[ESM::Skill::Length];
|
||||
std::array<float, ESM::Skill::Length> mSaveSkills;
|
||||
float mSaveAttributes[ESM::Attribute::Length];
|
||||
|
||||
bool mJumping;
|
||||
|
|
|
@ -114,7 +114,6 @@ namespace MWWorld
|
|||
|
||||
// Need to instantiate these before they're used
|
||||
template class IndexedStore<ESM::MagicEffect>;
|
||||
template class IndexedStore<ESM::Skill>;
|
||||
|
||||
template <class T, class Id>
|
||||
TypedDynamicStore<T, Id>::TypedDynamicStore()
|
||||
|
@ -169,12 +168,20 @@ namespace MWWorld
|
|||
const T* TypedDynamicStore<T, Id>::searchRandom(const std::string_view prefix, Misc::Rng::Generator& prng) const
|
||||
{
|
||||
if constexpr (std::is_same_v<Id, ESM::RefId>)
|
||||
{
|
||||
if (prefix.empty())
|
||||
{
|
||||
if (!mShared.empty())
|
||||
return mShared[Misc::Rng::rollDice(mShared.size(), prng)];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<const T*> results;
|
||||
std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results),
|
||||
[prefix](const T* item) { return item->mId.startsWith(prefix); });
|
||||
if (!results.empty())
|
||||
return results[Misc::Rng::rollDice(results.size(), prng)];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
|
@ -916,8 +923,6 @@ namespace MWWorld
|
|||
// Skill
|
||||
//=========================================================================
|
||||
|
||||
Store<ESM::Skill>::Store() {}
|
||||
|
||||
void Store<ESM::Skill>::setUp(const MWWorld::Store<ESM::GameSetting>& settings)
|
||||
{
|
||||
constexpr std::string_view skillValues[ESM::Skill::Length][3] = {
|
||||
|
@ -950,15 +955,13 @@ namespace MWWorld
|
|||
{ "sSkillSpeechcraft", "icons\\k\\stealth_speechcraft.dds", "fWerewolfSpeechcraft" },
|
||||
{ "sSkillHandtohand", "icons\\k\\stealth_handtohand.dds", "fWerewolfHandtohand" },
|
||||
};
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
for (ESM::Skill* skill : mShared)
|
||||
{
|
||||
auto found = mStatic.find(i);
|
||||
if (found != mStatic.end())
|
||||
if (skill->mIndex >= 0)
|
||||
{
|
||||
ESM::Skill& skill = found->second;
|
||||
skill.mName = getGMSTString(settings, skillValues[i][0]);
|
||||
skill.mIcon = skillValues[i][1];
|
||||
skill.mWerewolfValue = getGMSTFloat(settings, skillValues[i][2]);
|
||||
skill->mName = getGMSTString(settings, skillValues[skill->mIndex][0]);
|
||||
skill->mIcon = skillValues[skill->mIndex][1];
|
||||
skill->mWerewolfValue = getGMSTFloat(settings, skillValues[skill->mIndex][2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1363,7 +1366,7 @@ template class MWWorld::TypedDynamicStore<ESM::Race>;
|
|||
template class MWWorld::TypedDynamicStore<ESM::Region>;
|
||||
template class MWWorld::TypedDynamicStore<ESM::Repair>;
|
||||
template class MWWorld::TypedDynamicStore<ESM::Script>;
|
||||
// template class MWWorld::Store<ESM::Skill>;
|
||||
template class MWWorld::TypedDynamicStore<ESM::Skill>;
|
||||
template class MWWorld::TypedDynamicStore<ESM::Sound>;
|
||||
template class MWWorld::TypedDynamicStore<ESM::SoundGenerator>;
|
||||
template class MWWorld::TypedDynamicStore<ESM::Spell>;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <components/esm3/loadgmst.hpp>
|
||||
#include <components/esm3/loadland.hpp>
|
||||
#include <components/esm3/loadpgrd.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
#include <components/esm4/loadcell.hpp>
|
||||
#include <components/esm4/loadland.hpp>
|
||||
#include <components/esm4/loadrefr.hpp>
|
||||
|
@ -30,7 +31,6 @@ namespace ESM
|
|||
struct Attribute;
|
||||
struct LandTexture;
|
||||
struct MagicEffect;
|
||||
struct Skill;
|
||||
struct WeaponType;
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
@ -478,10 +478,12 @@ namespace MWWorld
|
|||
};
|
||||
|
||||
template <>
|
||||
class Store<ESM::Skill> : public IndexedStore<ESM::Skill>
|
||||
class Store<ESM::Skill> : public TypedDynamicStore<ESM::Skill>
|
||||
{
|
||||
using TypedDynamicStore<ESM::Skill>::setUp;
|
||||
|
||||
public:
|
||||
Store();
|
||||
Store() = default;
|
||||
|
||||
void setUp(const MWWorld::Store<ESM::GameSetting>& settings);
|
||||
};
|
||||
|
|
|
@ -496,7 +496,7 @@ namespace
|
|||
const RecordType* result = nullptr;
|
||||
if constexpr (std::is_same_v<RecordType, ESM::LandTexture>)
|
||||
result = esmStore.get<RecordType>().search(index, 0);
|
||||
else if constexpr (ESM::hasIndex<RecordType>)
|
||||
else if constexpr (ESM::hasIndex<RecordType> && !std::is_same_v<RecordType, ESM::Skill>)
|
||||
result = esmStore.get<RecordType>().search(index);
|
||||
else
|
||||
result = esmStore.get<RecordType>().search(refId);
|
||||
|
|
|
@ -37,11 +37,11 @@ namespace ESM
|
|||
"Handtohand",
|
||||
};
|
||||
|
||||
Skill::SkillEnum Skill::stringToSkillId(std::string_view skill)
|
||||
int Skill::stringToSkillId(std::string_view skill)
|
||||
{
|
||||
for (int id = 0; id < Skill::Length; ++id)
|
||||
if (Misc::StringUtils::ciEqual(sSkillNames[id], skill))
|
||||
return Skill::SkillEnum(id);
|
||||
return id;
|
||||
|
||||
throw std::logic_error("No such skill: " + std::string(skill));
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ namespace ESM
|
|||
}
|
||||
if (!hasIndex)
|
||||
esm.fail("Missing INDX");
|
||||
else if (mIndex < 0 || mIndex >= Length)
|
||||
esm.fail("Invalid INDX");
|
||||
if (!hasData)
|
||||
esm.fail("Missing SKDT");
|
||||
|
||||
|
@ -101,7 +103,7 @@ namespace ESM
|
|||
|
||||
RefId Skill::indexToRefId(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
if (index < 0 || index >= Length)
|
||||
return RefId();
|
||||
return RefId::index(sRecordId, static_cast<std::uint32_t>(index));
|
||||
}
|
||||
|
|
|
@ -41,47 +41,44 @@ namespace ESM
|
|||
// Skill index. Skils don't have an id ("NAME") like most records,
|
||||
// they only have a numerical index that matches one of the
|
||||
// hard-coded skills in the game.
|
||||
int mIndex;
|
||||
int mIndex{ -1 };
|
||||
|
||||
std::string mDescription;
|
||||
std::string mName;
|
||||
std::string mIcon;
|
||||
float mWerewolfValue{};
|
||||
|
||||
enum SkillEnum
|
||||
{
|
||||
Block = 0,
|
||||
Armorer = 1,
|
||||
MediumArmor = 2,
|
||||
HeavyArmor = 3,
|
||||
BluntWeapon = 4,
|
||||
LongBlade = 5,
|
||||
Axe = 6,
|
||||
Spear = 7,
|
||||
Athletics = 8,
|
||||
Enchant = 9,
|
||||
Destruction = 10,
|
||||
Alteration = 11,
|
||||
Illusion = 12,
|
||||
Conjuration = 13,
|
||||
Mysticism = 14,
|
||||
Restoration = 15,
|
||||
Alchemy = 16,
|
||||
Unarmored = 17,
|
||||
Security = 18,
|
||||
Sneak = 19,
|
||||
Acrobatics = 20,
|
||||
LightArmor = 21,
|
||||
ShortBlade = 22,
|
||||
Marksman = 23,
|
||||
Mercantile = 24,
|
||||
Speechcraft = 25,
|
||||
HandToHand = 26,
|
||||
Length
|
||||
};
|
||||
static constexpr IndexRefId Block{ sRecordId, 0 };
|
||||
static constexpr IndexRefId Armorer{ sRecordId, 1 };
|
||||
static constexpr IndexRefId MediumArmor{ sRecordId, 2 };
|
||||
static constexpr IndexRefId HeavyArmor{ sRecordId, 3 };
|
||||
static constexpr IndexRefId BluntWeapon{ sRecordId, 4 };
|
||||
static constexpr IndexRefId LongBlade{ sRecordId, 5 };
|
||||
static constexpr IndexRefId Axe{ sRecordId, 6 };
|
||||
static constexpr IndexRefId Spear{ sRecordId, 7 };
|
||||
static constexpr IndexRefId Athletics{ sRecordId, 8 };
|
||||
static constexpr IndexRefId Enchant{ sRecordId, 9 };
|
||||
static constexpr IndexRefId Destruction{ sRecordId, 10 };
|
||||
static constexpr IndexRefId Alteration{ sRecordId, 11 };
|
||||
static constexpr IndexRefId Illusion{ sRecordId, 12 };
|
||||
static constexpr IndexRefId Conjuration{ sRecordId, 13 };
|
||||
static constexpr IndexRefId Mysticism{ sRecordId, 14 };
|
||||
static constexpr IndexRefId Restoration{ sRecordId, 15 };
|
||||
static constexpr IndexRefId Alchemy{ sRecordId, 16 };
|
||||
static constexpr IndexRefId Unarmored{ sRecordId, 17 };
|
||||
static constexpr IndexRefId Security{ sRecordId, 18 };
|
||||
static constexpr IndexRefId Sneak{ sRecordId, 19 };
|
||||
static constexpr IndexRefId Acrobatics{ sRecordId, 20 };
|
||||
static constexpr IndexRefId LightArmor{ sRecordId, 21 };
|
||||
static constexpr IndexRefId ShortBlade{ sRecordId, 22 };
|
||||
static constexpr IndexRefId Marksman{ sRecordId, 23 };
|
||||
static constexpr IndexRefId Mercantile{ sRecordId, 24 };
|
||||
static constexpr IndexRefId Speechcraft{ sRecordId, 25 };
|
||||
static constexpr IndexRefId HandToHand{ sRecordId, 26 };
|
||||
static constexpr int Length = 27;
|
||||
static const std::string sSkillNames[Length];
|
||||
|
||||
static SkillEnum stringToSkillId(std::string_view skill);
|
||||
static int stringToSkillId(std::string_view skill);
|
||||
|
||||
void load(ESMReader& esm, bool& isDeleted);
|
||||
void save(ESMWriter& esm, bool isDeleted = false) const;
|
||||
|
|
|
@ -109,13 +109,13 @@ namespace ESM
|
|||
ESM::RefId mSoundIdUp;
|
||||
std::string mAttachBone;
|
||||
std::string mSheathingBone;
|
||||
Skill::SkillEnum mSkill;
|
||||
ESM::RefId mSkill;
|
||||
Class mWeaponClass;
|
||||
int mAmmoType;
|
||||
int mFlags;
|
||||
|
||||
WeaponType(std::string shortGroup, std::string longGroup, const std::string& soundId, std::string attachBone,
|
||||
std::string sheathingBone, Skill::SkillEnum skill, Class weaponClass, int ammoType, int flags)
|
||||
std::string sheathingBone, ESM::RefId skill, Class weaponClass, int ammoType, int flags)
|
||||
: mShortGroup(std::move(shortGroup))
|
||||
, mLongGroup(std::move(longGroup))
|
||||
, mSoundIdDown(ESM::RefId::stringRefId(soundId + " Down"))
|
||||
|
|
|
@ -73,7 +73,8 @@ namespace ESM
|
|||
mSaveSkills[i] = skill.mBase + skill.mMod - skill.mDamage;
|
||||
if (mObject.mNpcStats.mIsWerewolf)
|
||||
{
|
||||
if (i == Skill::Acrobatics)
|
||||
constexpr int Acrobatics = 20;
|
||||
if (i == Acrobatics)
|
||||
mSetWerewolfAcrobatics = mObject.mNpcStats.mSkills[i].mBase != skill.mBase;
|
||||
mObject.mNpcStats.mSkills[i] = skill;
|
||||
}
|
||||
|
|
|
@ -14,45 +14,27 @@
|
|||
<Property key="Caption" value="#{sSpecializationCombat}"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 50 154 18" name="CombatSkill0" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 68 154 18" name="CombatSkill1" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 86 154 18" name="CombatSkill2" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 104 154 18" name="CombatSkill3" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 122 154 18" name="CombatSkill4" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 140 154 18" name="CombatSkill5" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 158 154 18" name="CombatSkill6" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 176 154 18" name="CombatSkill7" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="0 194 154 18" name="CombatSkill8" align="Left Top"/>
|
||||
<Widget type="ScrollView" skin="MW_ScrollView" position="0 50 154 168" name="CombatSkills" align="Left Top">
|
||||
<Property key="CanvasAlign" value="Left"/>
|
||||
</Widget>
|
||||
|
||||
<!-- Magic list -->
|
||||
<Widget type="TextBox" skin="HeaderText" position="158 32 154 18" name="MagicLabelT" align="Left Top">
|
||||
<Property key="Caption" value="#{sSpecializationMagic}"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 50 154 18" name="MagicSkill0" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 68 154 18" name="MagicSkill1" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 86 154 18" name="MagicSkill2" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 104 154 18" name="MagicSkill3" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 122 154 18" name="MagicSkill4" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 140 154 18" name="MagicSkill5" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 158 154 18" name="MagicSkill6" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 176 154 18" name="MagicSkill7" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="158 194 154 18" name="MagicSkill8" align="Left Top"/>
|
||||
<Widget type="ScrollView" skin="MW_ScrollView" position="158 50 154 168" name="MagicSkills" align="Left Top">
|
||||
<Property key="CanvasAlign" value="Left"/>
|
||||
</Widget>
|
||||
|
||||
<!-- Stealth list -->
|
||||
<Widget type="TextBox" skin="HeaderText" position="316 32 131 18" name="StealthLabelT" align="Left Top">
|
||||
<Property key="Caption" value="#{sSpecializationStealth}"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 50 154 18" name="StealthSkill0" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 68 154 18" name="StealthSkill1" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 86 154 18" name="StealthSkill2" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 104 154 18" name="StealthSkill3" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 122 154 18" name="StealthSkill4" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 140 154 18" name="StealthSkill5" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 158 154 18" name="StealthSkill6" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 176 154 18" name="StealthSkill7" align="Left Top"/>
|
||||
<Widget type="MWSkill" skin="MW_StatNameButton" position="316 194 154 18" name="StealthSkill8" align="Left Top"/>
|
||||
<Widget type="ScrollView" skin="MW_ScrollView" position="316 50 154 168" name="StealthSkills" align="Left Top">
|
||||
<Property key="CanvasAlign" value="Left"/>
|
||||
</Widget>
|
||||
|
||||
<!-- Dialog buttons -->
|
||||
<Widget type="HBox" position="0 218 457 28">
|
||||
|
|
Loading…
Reference in a new issue