forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'scrawl/master'
This commit is contained in:
commit
30f8e279ed
60 changed files with 845 additions and 393 deletions
|
@ -335,7 +335,7 @@ int main(int argc, char**argv)
|
|||
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
|
||||
{
|
||||
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
|
||||
cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL);
|
||||
cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL);
|
||||
std::cout << "Installing crash catcher" << std::endl;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -120,6 +120,7 @@ namespace MWBase
|
|||
/**
|
||||
* @brief Commit a crime. If any actors witness the crime and report it,
|
||||
* reportCrime will be called automatically.
|
||||
* @note victim may be empty
|
||||
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
|
||||
* @return was the crime reported?
|
||||
*/
|
||||
|
@ -191,6 +192,10 @@ namespace MWBase
|
|||
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
|
||||
|
||||
virtual void clear() = 0;
|
||||
|
||||
/// @param bias Can be used to add an additional aggression bias towards the target,
|
||||
/// making it more likely for the function to return true.
|
||||
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -407,7 +407,7 @@ namespace MWBase
|
|||
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
|
||||
///< get all items in active cells owned by this Npc
|
||||
|
||||
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0;
|
||||
virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) = 0;
|
||||
///< get Line of Sight (morrowind stupid implementation)
|
||||
|
||||
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
|
||||
|
@ -470,7 +470,7 @@ namespace MWBase
|
|||
|
||||
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) = 0;
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
|
||||
|
||||
|
|
|
@ -286,7 +286,12 @@ namespace MWClass
|
|||
{
|
||||
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
|
||||
|
||||
ensureCustomData (ptr);
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
std::auto_ptr<ContainerCustomData> data (new ContainerCustomData);
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
}
|
||||
|
||||
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
|
||||
readState (state2.mInventory);
|
||||
|
|
|
@ -59,35 +59,38 @@ namespace
|
|||
|
||||
namespace MWClass
|
||||
{
|
||||
const Creature::GMST& Creature::getGmst()
|
||||
{
|
||||
static GMST gmst;
|
||||
static bool inited = false;
|
||||
if (!inited)
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||
gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature");
|
||||
gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature");
|
||||
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
|
||||
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
|
||||
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
|
||||
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
|
||||
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
|
||||
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
|
||||
gmst.fSwimRunBase = store.find("fSwimRunBase");
|
||||
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
|
||||
gmst.fKnockDownMult = store.find("fKnockDownMult");
|
||||
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
|
||||
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
|
||||
inited = true;
|
||||
}
|
||||
return gmst;
|
||||
}
|
||||
|
||||
void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||
|
||||
static bool inited = false;
|
||||
if(!inited)
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature");
|
||||
fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature");
|
||||
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
|
||||
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
|
||||
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
|
||||
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
|
||||
fMinFlySpeed = gmst.find("fMinFlySpeed");
|
||||
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
|
||||
fSwimRunBase = gmst.find("fSwimRunBase");
|
||||
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
|
||||
fKnockDownMult = gmst.find("fKnockDownMult");
|
||||
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
|
||||
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
|
||||
|
||||
inited = true;
|
||||
}
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
|
||||
// creature stats
|
||||
|
@ -345,7 +348,7 @@ namespace MWClass
|
|||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
||||
// Self defense
|
||||
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80
|
||||
if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)
|
||||
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures
|
||||
// (they have no movement or attacks anyway)
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
||||
|
@ -376,9 +379,9 @@ namespace MWClass
|
|||
if (damage > 0.f)
|
||||
{
|
||||
// Check for knockdown
|
||||
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
|
||||
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
|
||||
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
|
||||
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
|
||||
* getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
||||
{
|
||||
|
@ -522,9 +525,10 @@ namespace MWClass
|
|||
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
const GMST& gmst = getGmst();
|
||||
|
||||
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
||||
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat());
|
||||
float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
||||
* (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat());
|
||||
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
||||
|
@ -533,8 +537,8 @@ namespace MWClass
|
|||
|
||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
|
||||
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
|
||||
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
|
||||
float runSpeed = walkSpeed*6;
|
||||
runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this
|
||||
|
||||
float moveSpeed;
|
||||
if(normalizedEncumbrance >= 1.0f)
|
||||
|
@ -544,8 +548,8 @@ namespace MWClass
|
|||
{
|
||||
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
|
||||
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
|
||||
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
|
||||
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
||||
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
|
||||
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
||||
flySpeed = std::max(0.0f, flySpeed);
|
||||
moveSpeed = flySpeed;
|
||||
}
|
||||
|
@ -555,8 +559,8 @@ namespace MWClass
|
|||
if(running)
|
||||
swimSpeed = runSpeed;
|
||||
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
|
||||
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
|
||||
fSwimRunAthleticsMult->getFloat();
|
||||
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
|
||||
gmst.fSwimRunAthleticsMult->getFloat();
|
||||
moveSpeed = swimSpeed;
|
||||
}
|
||||
else if(running)
|
||||
|
@ -798,7 +802,27 @@ namespace MWClass
|
|||
{
|
||||
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
|
||||
|
||||
ensureCustomData (ptr);
|
||||
ensureCustomData(ptr);
|
||||
|
||||
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
|
||||
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
|
||||
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
|
||||
/*
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
|
||||
if (ref->mBase->mFlags & ESM::Creature::Weapon)
|
||||
data->mContainerStore = new MWWorld::InventoryStore();
|
||||
else
|
||||
data->mContainerStore = new MWWorld::ContainerStore();
|
||||
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
}
|
||||
*/
|
||||
|
||||
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
|
||||
|
||||
|
@ -851,19 +875,4 @@ namespace MWClass
|
|||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
|
||||
}
|
||||
|
||||
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
|
||||
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
|
||||
const ESM::GameSetting *Creature::fEncumberedMoveEffect;
|
||||
const ESM::GameSetting *Creature::fSneakSpeedMultiplier;
|
||||
const ESM::GameSetting *Creature::fAthleticsRunBonus;
|
||||
const ESM::GameSetting *Creature::fBaseRunMultiplier;
|
||||
const ESM::GameSetting *Creature::fMinFlySpeed;
|
||||
const ESM::GameSetting *Creature::fMaxFlySpeed;
|
||||
const ESM::GameSetting *Creature::fSwimRunBase;
|
||||
const ESM::GameSetting *Creature::fSwimRunAthleticsMult;
|
||||
const ESM::GameSetting *Creature::fKnockDownMult;
|
||||
const ESM::GameSetting *Creature::iKnockDownOddsMult;
|
||||
const ESM::GameSetting *Creature::iKnockDownOddsBase;
|
||||
|
||||
}
|
||||
|
|
|
@ -19,20 +19,25 @@ namespace MWClass
|
|||
|
||||
static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);
|
||||
|
||||
static const ESM::GameSetting *fMinWalkSpeedCreature;
|
||||
static const ESM::GameSetting *fMaxWalkSpeedCreature;
|
||||
static const ESM::GameSetting *fEncumberedMoveEffect;
|
||||
static const ESM::GameSetting *fSneakSpeedMultiplier;
|
||||
static const ESM::GameSetting *fAthleticsRunBonus;
|
||||
static const ESM::GameSetting *fBaseRunMultiplier;
|
||||
static const ESM::GameSetting *fMinFlySpeed;
|
||||
static const ESM::GameSetting *fMaxFlySpeed;
|
||||
static const ESM::GameSetting *fSwimRunBase;
|
||||
static const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||
static const ESM::GameSetting *fKnockDownMult;
|
||||
static const ESM::GameSetting *iKnockDownOddsMult;
|
||||
static const ESM::GameSetting *iKnockDownOddsBase;
|
||||
// cached GMSTs
|
||||
struct GMST
|
||||
{
|
||||
const ESM::GameSetting *fMinWalkSpeedCreature;
|
||||
const ESM::GameSetting *fMaxWalkSpeedCreature;
|
||||
const ESM::GameSetting *fEncumberedMoveEffect;
|
||||
const ESM::GameSetting *fSneakSpeedMultiplier;
|
||||
const ESM::GameSetting *fAthleticsRunBonus;
|
||||
const ESM::GameSetting *fBaseRunMultiplier;
|
||||
const ESM::GameSetting *fMinFlySpeed;
|
||||
const ESM::GameSetting *fMaxFlySpeed;
|
||||
const ESM::GameSetting *fSwimRunBase;
|
||||
const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||
const ESM::GameSetting *fKnockDownMult;
|
||||
const ESM::GameSetting *iKnockDownOddsMult;
|
||||
const ESM::GameSetting *iKnockDownOddsBase;
|
||||
};
|
||||
|
||||
static const GMST& getGmst();
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -228,38 +228,44 @@ namespace
|
|||
|
||||
namespace MWClass
|
||||
{
|
||||
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||
const Npc::GMST& Npc::getGmst()
|
||||
{
|
||||
static GMST gmst;
|
||||
static bool inited = false;
|
||||
if(!inited)
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
fMinWalkSpeed = gmst.find("fMinWalkSpeed");
|
||||
fMaxWalkSpeed = gmst.find("fMaxWalkSpeed");
|
||||
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
|
||||
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
|
||||
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
|
||||
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
|
||||
fMinFlySpeed = gmst.find("fMinFlySpeed");
|
||||
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
|
||||
fSwimRunBase = gmst.find("fSwimRunBase");
|
||||
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
|
||||
fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase");
|
||||
fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier");
|
||||
fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase");
|
||||
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
|
||||
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
|
||||
fWereWolfRunMult = gmst.find("fWereWolfRunMult");
|
||||
fKnockDownMult = gmst.find("fKnockDownMult");
|
||||
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
|
||||
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
|
||||
fDamageStrengthBase = gmst.find("fDamageStrengthBase");
|
||||
fDamageStrengthMult = gmst.find("fDamageStrengthMult");
|
||||
gmst.fMinWalkSpeed = store.find("fMinWalkSpeed");
|
||||
gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed");
|
||||
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
|
||||
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
|
||||
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
|
||||
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
|
||||
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
|
||||
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
|
||||
gmst.fSwimRunBase = store.find("fSwimRunBase");
|
||||
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
|
||||
gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase");
|
||||
gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier");
|
||||
gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase");
|
||||
gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier");
|
||||
gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier");
|
||||
gmst.fWereWolfRunMult = store.find("fWereWolfRunMult");
|
||||
gmst.fKnockDownMult = store.find("fKnockDownMult");
|
||||
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
|
||||
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
|
||||
gmst.fDamageStrengthBase = store.find("fDamageStrengthBase");
|
||||
gmst.fDamageStrengthMult = store.find("fDamageStrengthMult");
|
||||
|
||||
inited = true;
|
||||
}
|
||||
return gmst;
|
||||
}
|
||||
|
||||
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<NpcCustomData> data(new NpcCustomData);
|
||||
|
@ -404,11 +410,6 @@ namespace MWClass
|
|||
ptr.get<ESM::NPC>();
|
||||
assert(ref->mBase != NULL);
|
||||
|
||||
//std::string headID = ref->mBase->mHead;
|
||||
|
||||
//int end = headID.find_last_of("head_") - 4;
|
||||
//std::string bodyRaceID = headID.substr(0, end);
|
||||
|
||||
std::string model = "meshes\\base_anim.nif";
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||
if(race->mData.mFlags & ESM::Race::Beast)
|
||||
|
@ -423,9 +424,9 @@ namespace MWClass
|
|||
if(getNpcStats(ptr).isWerewolf())
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
return gmst.find("sWerewolfPopup")->getString();
|
||||
return store.find("sWerewolfPopup")->getString();
|
||||
}
|
||||
|
||||
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
|
@ -450,7 +451,9 @@ namespace MWClass
|
|||
void Npc::hit(const MWWorld::Ptr& ptr, int type) const
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
const GMST& gmst = getGmst();
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
||||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||
|
@ -461,9 +464,9 @@ namespace MWClass
|
|||
|
||||
// Reduce fatigue
|
||||
// somewhat of a guess, but using the weapon weight makes sense
|
||||
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
|
||||
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
|
||||
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
|
||||
const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat();
|
||||
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
|
||||
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
|
||||
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
|
||||
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
|
||||
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
||||
|
@ -472,10 +475,10 @@ namespace MWClass
|
|||
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
|
||||
getCreatureStats(ptr).setFatigue(fatigue);
|
||||
|
||||
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
|
||||
const float fCombatDistance = store.find("fCombatDistance")->getFloat();
|
||||
float dist = fCombatDistance * (!weapon.isEmpty() ?
|
||||
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
||||
gmst.find("fHandToHandReach")->getFloat());
|
||||
store.find("fHandToHandReach")->getFloat());
|
||||
|
||||
// TODO: Use second to work out the hit angle
|
||||
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
|
||||
|
@ -522,8 +525,8 @@ namespace MWClass
|
|||
if(attack)
|
||||
{
|
||||
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
||||
damage *= fDamageStrengthBase->getFloat() +
|
||||
(stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1);
|
||||
damage *= gmst.fDamageStrengthBase->getFloat() +
|
||||
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
||||
|
@ -535,7 +538,7 @@ namespace MWClass
|
|||
{
|
||||
// Reduce weapon charge by at least one, but cap at 0
|
||||
weaphealth -= std::min(std::max(1,
|
||||
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
||||
(int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
||||
|
||||
weapon.getCellRef().setCharge(weaphealth);
|
||||
}
|
||||
|
@ -552,8 +555,8 @@ namespace MWClass
|
|||
// Note: MCP contains an option to include Strength in hand-to-hand damage
|
||||
// calculations. Some mods recommend using it, so we may want to include am
|
||||
// option for it.
|
||||
float minstrike = gmst.find("fMinHandToHandMult")->getFloat();
|
||||
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
|
||||
float minstrike = store.find("fMinHandToHandMult")->getFloat();
|
||||
float maxstrike = store.find("fMaxHandToHandMult")->getFloat();
|
||||
damage = stats.getSkill(weapskill).getModified();
|
||||
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
|
||||
|
||||
|
@ -567,7 +570,7 @@ namespace MWClass
|
|||
damage *= glob.find("WerewolfClawMult")->mValue.getFloat();
|
||||
}
|
||||
if(healthdmg)
|
||||
damage *= gmst.find("fHandtoHandHealthPer")->getFloat();
|
||||
damage *= store.find("fHandtoHandHealthPer")->getFloat();
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
if(stats.isWerewolf())
|
||||
|
@ -585,12 +588,12 @@ namespace MWClass
|
|||
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
|
||||
if(!detected)
|
||||
{
|
||||
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
|
||||
damage *= store.find("fCombatCriticalStrikeMult")->getFloat();
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
||||
}
|
||||
if (othercls.getCreatureStats(victim).getKnockedDown())
|
||||
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
||||
damage *= store.find("fCombatKODamageMult")->getFloat();
|
||||
|
||||
// Apply "On hit" enchanted weapons
|
||||
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
|
||||
|
@ -624,11 +627,12 @@ namespace MWClass
|
|||
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||
|
||||
// Attacking peaceful NPCs is a crime
|
||||
// anything below 80 is considered peaceful (see Actors::updateActor)
|
||||
if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() &&
|
||||
ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80)
|
||||
!MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
||||
|
||||
bool wasDead = getCreatureStats(ptr).isDead();
|
||||
|
||||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
||||
if(!successful)
|
||||
|
@ -660,6 +664,8 @@ namespace MWClass
|
|||
// something, alert the character controller, scripts, etc.
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const GMST& gmst = getGmst();
|
||||
|
||||
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (roll < chance)
|
||||
|
@ -668,9 +674,9 @@ namespace MWClass
|
|||
}
|
||||
|
||||
// Check for knockdown
|
||||
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
|
||||
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat();
|
||||
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
|
||||
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
|
||||
* gmst.iKnockDownOddsMult->getInt() * 0.01 + gmst.iKnockDownOddsBase->getInt();
|
||||
roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
||||
{
|
||||
|
@ -752,6 +758,22 @@ namespace MWClass
|
|||
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
||||
getCreatureStats(ptr).setFatigue(fatigue);
|
||||
}
|
||||
|
||||
if (!wasDead && getCreatureStats(ptr).isDead())
|
||||
{
|
||||
// NPC was killed
|
||||
if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf())
|
||||
{
|
||||
attacker.getClass().getNpcStats(attacker).addWerewolfKill();
|
||||
}
|
||||
|
||||
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
|
||||
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
|
||||
// for bystanders it is not possible to tell who attacked first, anyway.
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (attacker == player && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player)
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
|
||||
}
|
||||
}
|
||||
|
||||
void Npc::block(const MWWorld::Ptr &ptr) const
|
||||
|
@ -853,6 +875,8 @@ namespace MWClass
|
|||
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const GMST& gmst = getGmst();
|
||||
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||
|
||||
|
@ -861,17 +885,17 @@ namespace MWClass
|
|||
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
|
||||
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
||||
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
|
||||
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
|
||||
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
||||
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
|
||||
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
|
||||
walkSpeed = std::max(0.0f, walkSpeed);
|
||||
if(sneaking)
|
||||
walkSpeed *= fSneakSpeedMultiplier->getFloat();
|
||||
walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat();
|
||||
|
||||
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
|
||||
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
|
||||
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
|
||||
if(npcdata->mNpcStats.isWerewolf())
|
||||
runSpeed *= fWereWolfRunMult->getFloat();
|
||||
runSpeed *= gmst.fWereWolfRunMult->getFloat();
|
||||
|
||||
float moveSpeed;
|
||||
if(normalizedEncumbrance >= 1.0f)
|
||||
|
@ -881,8 +905,8 @@ namespace MWClass
|
|||
{
|
||||
float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +
|
||||
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
|
||||
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
|
||||
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
||||
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
|
||||
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
||||
flySpeed = std::max(0.0f, flySpeed);
|
||||
moveSpeed = flySpeed;
|
||||
}
|
||||
|
@ -892,8 +916,8 @@ namespace MWClass
|
|||
if(running)
|
||||
swimSpeed = runSpeed;
|
||||
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
|
||||
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
|
||||
fSwimRunAthleticsMult->getFloat();
|
||||
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
|
||||
gmst.fSwimRunAthleticsMult->getFloat();
|
||||
moveSpeed = swimSpeed;
|
||||
}
|
||||
else if(running && !sneaking)
|
||||
|
@ -909,9 +933,10 @@ namespace MWClass
|
|||
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const GMST& gmst = getGmst();
|
||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
|
||||
fJumpEncumbranceMultiplier->getFloat() *
|
||||
const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() +
|
||||
gmst.fJumpEncumbranceMultiplier->getFloat() *
|
||||
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
|
||||
|
||||
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
|
||||
|
@ -922,14 +947,14 @@ namespace MWClass
|
|||
a = 50.0f;
|
||||
}
|
||||
|
||||
float x = fJumpAcrobaticsBase->getFloat() +
|
||||
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
|
||||
x += 3.0f * b * fJumpAcroMultiplier->getFloat();
|
||||
float x = gmst.fJumpAcrobaticsBase->getFloat() +
|
||||
std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat());
|
||||
x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat();
|
||||
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
|
||||
x *= encumbranceTerm;
|
||||
|
||||
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
|
||||
x *= fJumpRunMultiplier->getFloat();
|
||||
x *= gmst.fJumpRunMultiplier->getFloat();
|
||||
x *= npcdata->mNpcStats.getFatigueTerm();
|
||||
x -= -627.2f;/*gravity constant*/
|
||||
x /= 3.0f;
|
||||
|
@ -940,19 +965,19 @@ namespace MWClass
|
|||
float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
const float fallDistanceMin = gmst.find("fFallDamageDistanceMin")->getFloat();
|
||||
const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat();
|
||||
|
||||
if (fallHeight >= fallDistanceMin)
|
||||
{
|
||||
const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
|
||||
const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat();
|
||||
const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat();
|
||||
const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat();
|
||||
const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat();
|
||||
const float fallAcroBase = store.find("fFallAcroBase")->getFloat();
|
||||
const float fallAcroMult = store.find("fFallAcroMult")->getFloat();
|
||||
const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat();
|
||||
const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat();
|
||||
|
||||
float x = fallHeight - fallDistanceMin;
|
||||
x -= (1.5 * acrobaticsSkill) + jumpSpellBonus;
|
||||
|
@ -1087,14 +1112,14 @@ namespace MWClass
|
|||
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
|
||||
|
||||
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
|
||||
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
|
||||
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
|
||||
int iBaseArmorSkill = store.find("iBaseArmorSkill")->getInt();
|
||||
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat();
|
||||
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat();
|
||||
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
|
||||
|
||||
int ratings[MWWorld::InventoryStore::Slots];
|
||||
|
@ -1276,7 +1301,18 @@ namespace MWClass
|
|||
{
|
||||
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
|
||||
|
||||
ensureCustomData (ptr);
|
||||
ensureCustomData(ptr);
|
||||
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
|
||||
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
|
||||
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
|
||||
/*
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
std::auto_ptr<NpcCustomData> data (new NpcCustomData);
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
}
|
||||
*/
|
||||
|
||||
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
|
||||
|
||||
|
@ -1339,27 +1375,4 @@ namespace MWClass
|
|||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
|
||||
}
|
||||
|
||||
const ESM::GameSetting *Npc::fMinWalkSpeed;
|
||||
const ESM::GameSetting *Npc::fMaxWalkSpeed;
|
||||
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
|
||||
const ESM::GameSetting *Npc::fSneakSpeedMultiplier;
|
||||
const ESM::GameSetting *Npc::fAthleticsRunBonus;
|
||||
const ESM::GameSetting *Npc::fBaseRunMultiplier;
|
||||
const ESM::GameSetting *Npc::fMinFlySpeed;
|
||||
const ESM::GameSetting *Npc::fMaxFlySpeed;
|
||||
const ESM::GameSetting *Npc::fSwimRunBase;
|
||||
const ESM::GameSetting *Npc::fSwimRunAthleticsMult;
|
||||
const ESM::GameSetting *Npc::fJumpEncumbranceBase;
|
||||
const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier;
|
||||
const ESM::GameSetting *Npc::fJumpAcrobaticsBase;
|
||||
const ESM::GameSetting *Npc::fJumpAcroMultiplier;
|
||||
const ESM::GameSetting *Npc::fJumpRunMultiplier;
|
||||
const ESM::GameSetting *Npc::fWereWolfRunMult;
|
||||
const ESM::GameSetting *Npc::fKnockDownMult;
|
||||
const ESM::GameSetting *Npc::iKnockDownOddsMult;
|
||||
const ESM::GameSetting *Npc::iKnockDownOddsBase;
|
||||
const ESM::GameSetting *Npc::fDamageStrengthBase;
|
||||
const ESM::GameSetting *Npc::fDamageStrengthMult;
|
||||
|
||||
}
|
||||
|
|
|
@ -17,27 +17,32 @@ namespace MWClass
|
|||
virtual MWWorld::Ptr
|
||||
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
|
||||
|
||||
static const ESM::GameSetting *fMinWalkSpeed;
|
||||
static const ESM::GameSetting *fMaxWalkSpeed;
|
||||
static const ESM::GameSetting *fEncumberedMoveEffect;
|
||||
static const ESM::GameSetting *fSneakSpeedMultiplier;
|
||||
static const ESM::GameSetting *fAthleticsRunBonus;
|
||||
static const ESM::GameSetting *fBaseRunMultiplier;
|
||||
static const ESM::GameSetting *fMinFlySpeed;
|
||||
static const ESM::GameSetting *fMaxFlySpeed;
|
||||
static const ESM::GameSetting *fSwimRunBase;
|
||||
static const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||
static const ESM::GameSetting *fJumpEncumbranceBase;
|
||||
static const ESM::GameSetting *fJumpEncumbranceMultiplier;
|
||||
static const ESM::GameSetting *fJumpAcrobaticsBase;
|
||||
static const ESM::GameSetting *fJumpAcroMultiplier;
|
||||
static const ESM::GameSetting *fJumpRunMultiplier;
|
||||
static const ESM::GameSetting *fWereWolfRunMult;
|
||||
static const ESM::GameSetting *fKnockDownMult;
|
||||
static const ESM::GameSetting *iKnockDownOddsMult;
|
||||
static const ESM::GameSetting *iKnockDownOddsBase;
|
||||
static const ESM::GameSetting *fDamageStrengthBase;
|
||||
static const ESM::GameSetting *fDamageStrengthMult;
|
||||
struct GMST
|
||||
{
|
||||
const ESM::GameSetting *fMinWalkSpeed;
|
||||
const ESM::GameSetting *fMaxWalkSpeed;
|
||||
const ESM::GameSetting *fEncumberedMoveEffect;
|
||||
const ESM::GameSetting *fSneakSpeedMultiplier;
|
||||
const ESM::GameSetting *fAthleticsRunBonus;
|
||||
const ESM::GameSetting *fBaseRunMultiplier;
|
||||
const ESM::GameSetting *fMinFlySpeed;
|
||||
const ESM::GameSetting *fMaxFlySpeed;
|
||||
const ESM::GameSetting *fSwimRunBase;
|
||||
const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||
const ESM::GameSetting *fJumpEncumbranceBase;
|
||||
const ESM::GameSetting *fJumpEncumbranceMultiplier;
|
||||
const ESM::GameSetting *fJumpAcrobaticsBase;
|
||||
const ESM::GameSetting *fJumpAcroMultiplier;
|
||||
const ESM::GameSetting *fJumpRunMultiplier;
|
||||
const ESM::GameSetting *fWereWolfRunMult;
|
||||
const ESM::GameSetting *fKnockDownMult;
|
||||
const ESM::GameSetting *iKnockDownOddsMult;
|
||||
const ESM::GameSetting *iKnockDownOddsBase;
|
||||
const ESM::GameSetting *fDamageStrengthBase;
|
||||
const ESM::GameSetting *fDamageStrengthMult;
|
||||
};
|
||||
|
||||
static const GMST& getGmst();
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -166,13 +166,8 @@ namespace MWClass
|
|||
MWWorld::LiveCellRef<ESM::Potion> *ref =
|
||||
ptr.get<ESM::Potion>();
|
||||
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
// remove used potion (assume it is present in inventory)
|
||||
ptr.getContainerStore()->remove(ptr, 1, actor);
|
||||
|
||||
boost::shared_ptr<MWWorld::Action> action (
|
||||
new MWWorld::ActionApply (actor, ref->mBase->mId));
|
||||
new MWWorld::ActionApply (ptr, ref->mBase->mId));
|
||||
|
||||
action->setSound ("Drink");
|
||||
|
||||
|
|
|
@ -530,7 +530,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
|
|||
|
||||
case SelectWrapper::Function_ShouldAttack:
|
||||
|
||||
return mActor.getClass().getCreatureStats(mActor).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() >= 80;
|
||||
return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor,
|
||||
MWBase::Environment::get().getWorld()->getPlayerPtr());
|
||||
|
||||
case SelectWrapper::Function_CreatureTargetted:
|
||||
|
||||
|
|
|
@ -384,6 +384,8 @@ namespace MWGui
|
|||
|
||||
void HUD::onFrame(float dt)
|
||||
{
|
||||
LocalMapBase::onFrame(dt);
|
||||
|
||||
mCellNameTimer -= dt;
|
||||
mWeaponSpellTimer -= dt;
|
||||
if (mCellNameTimer < 0)
|
||||
|
|
|
@ -104,9 +104,10 @@ namespace MWGui
|
|||
|
||||
int attribute = mSpentAttributes[i];
|
||||
|
||||
int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30;
|
||||
int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 20;
|
||||
|
||||
MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4);
|
||||
MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0);
|
||||
pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2;
|
||||
image->setPosition(pos);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace MWGui
|
|||
, mLastDirectionX(0.0f)
|
||||
, mLastDirectionY(0.0f)
|
||||
, mCompass(NULL)
|
||||
, mMarkerUpdateTimer(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -345,6 +346,18 @@ namespace MWGui
|
|||
markerWidget->setUserString("IsMarker", "true");
|
||||
markerWidget->setUserData(markerPos);
|
||||
markerWidget->setColour(markerColour);
|
||||
mMarkerWidgets.push_back(markerWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalMapBase::onFrame(float dt)
|
||||
{
|
||||
mMarkerUpdateTimer += dt;
|
||||
|
||||
if (mMarkerUpdateTimer >= 0.25)
|
||||
{
|
||||
mMarkerUpdateTimer = 0;
|
||||
updateMarkers();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,6 +484,8 @@ namespace MWGui
|
|||
|
||||
void MapWindow::onFrame(float dt)
|
||||
{
|
||||
LocalMapBase::onFrame(dt);
|
||||
|
||||
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
|
||||
{
|
||||
mGlobalMapRender->exploreCell(it->first, it->second);
|
||||
|
|
|
@ -35,6 +35,8 @@ namespace MWGui
|
|||
void setPlayerDir(const float x, const float y);
|
||||
void setPlayerPos(const float x, const float y);
|
||||
|
||||
void onFrame(float dt);
|
||||
|
||||
bool toggleFogOfWar();
|
||||
|
||||
struct MarkerPosition
|
||||
|
@ -73,12 +75,14 @@ namespace MWGui
|
|||
virtual void notifyMapChanged() {}
|
||||
|
||||
// Update markers (Detect X effects, Mark/Recall effects)
|
||||
// Note, door markers handled in setActiveCell
|
||||
// Note, door markers are handled in setActiveCell
|
||||
void updateMarkers();
|
||||
void addDetectionMarkers(int type);
|
||||
|
||||
OEngine::GUI::Layout* mLayout;
|
||||
|
||||
float mMarkerUpdateTimer;
|
||||
|
||||
bool mMapDragAndDrop;
|
||||
|
||||
float mLastPositionX;
|
||||
|
|
|
@ -488,14 +488,16 @@ namespace MWGui
|
|||
text += "\n#BF9959#{sExpelled}";
|
||||
else
|
||||
{
|
||||
text += std::string("\n#BF9959") + faction->mRanks[it->second];
|
||||
int rank = it->second;
|
||||
rank = std::max(0, std::min(9, rank));
|
||||
text += std::string("\n#BF9959") + faction->mRanks[rank];
|
||||
|
||||
if (it->second < 9)
|
||||
if (rank < 9)
|
||||
{
|
||||
// player doesn't have max rank yet
|
||||
text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1];
|
||||
text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[rank+1];
|
||||
|
||||
ESM::RankData rankData = faction->mData.mRankData[it->second+1];
|
||||
ESM::RankData rankData = faction->mData.mRankData[rank+1];
|
||||
const ESM::Attribute* attr1 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[0]);
|
||||
const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]);
|
||||
|
||||
|
|
|
@ -233,13 +233,9 @@ namespace MWGui
|
|||
for (std::map<std::string, std::string>::iterator it = userStrings.begin();
|
||||
it != userStrings.end(); ++it)
|
||||
{
|
||||
if (it->first == "ToolTipType"
|
||||
|| it->first == "ToolTipLayout"
|
||||
|| it->first == "IsMarker")
|
||||
continue;
|
||||
|
||||
|
||||
size_t underscorePos = it->first.find("_");
|
||||
if (underscorePos == std::string::npos)
|
||||
continue;
|
||||
std::string propertyKey = it->first.substr(0, underscorePos);
|
||||
std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1));
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
@ -140,6 +141,9 @@ namespace MWGui
|
|||
if (playerGold<price)
|
||||
return;
|
||||
|
||||
if (!mPtr.getCell()->isExterior())
|
||||
// Interior cell -> mages guild transport
|
||||
MWBase::Environment::get().getSoundManager()->playSound("mysticism cast", 1, 1);
|
||||
|
||||
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
|
||||
|
||||
|
|
|
@ -630,8 +630,11 @@ namespace MWGui
|
|||
|
||||
MyGUI::IntSize AutoSizedButton::getRequestedSize()
|
||||
{
|
||||
MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0);
|
||||
size.height = std::max(24, size.height);
|
||||
MyGUI::IntSize padding(24, 8);
|
||||
if (isUserString("TextPadding"))
|
||||
padding = MyGUI::IntSize::parse(getUserString("TextPadding"));
|
||||
|
||||
MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include "../mwdialogue/dialoguemanagerimp.hpp"
|
||||
|
||||
|
@ -826,6 +826,14 @@ namespace MWInput
|
|||
|
||||
void InputManager::quickKey (int index)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (player.getClass().getNpcStats(player).isWerewolf())
|
||||
{
|
||||
// Cannot use items or spells while in werewolf form
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
MWBase::Environment::get().getWindowManager()->activateQuickKey (index);
|
||||
}
|
||||
|
@ -834,7 +842,18 @@ namespace MWInput
|
|||
{
|
||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()
|
||||
&& MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (player.getClass().getNpcStats(player).isWerewolf())
|
||||
{
|
||||
// Cannot use items or spells while in werewolf form
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}");
|
||||
return;
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu);
|
||||
|
||||
}
|
||||
else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) {
|
||||
while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows
|
||||
MWBase::Environment::get().getWindowManager()->getCurrentModal()->exit();
|
||||
|
|
|
@ -40,6 +40,10 @@ namespace MWMechanics
|
|||
void readState (const ESM::ActiveSpells& state);
|
||||
void writeState (ESM::ActiveSpells& state) const;
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
private:
|
||||
|
||||
mutable TContainer mSpells;
|
||||
|
@ -57,10 +61,6 @@ namespace MWMechanics
|
|||
|
||||
const TContainer& getActiveSpells() const;
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
public:
|
||||
|
||||
ActiveSpells();
|
||||
|
|
|
@ -201,32 +201,28 @@ namespace MWMechanics
|
|||
|| (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target
|
||||
return;
|
||||
|
||||
float fight;
|
||||
bool aggressive;
|
||||
|
||||
if (againstPlayer)
|
||||
fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
||||
else
|
||||
{
|
||||
fight = 0;
|
||||
aggressive = false;
|
||||
// if one of actors is creature then we should make a decision to start combat or not
|
||||
// NOTE: function doesn't take into account combat between 2 creatures
|
||||
if (!actor1.getClass().isNpc())
|
||||
{
|
||||
// if creature is hostile then it is necessarily to start combat
|
||||
if (creatureStats.isHostile()) fight = 100;
|
||||
else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||
if (creatureStats.isHostile()) aggressive = true;
|
||||
else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
||||
}
|
||||
}
|
||||
|
||||
ESM::Position actor1Pos = actor1.getRefData().getPosition();
|
||||
ESM::Position actor2Pos = actor2.getRefData().getPosition();
|
||||
float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos));
|
||||
|
||||
if( (fight == 100 && d <= 5000)
|
||||
|| (fight >= 95 && d <= 3000)
|
||||
|| (fight >= 90 && d <= 2000)
|
||||
|| (fight >= 80 && d <= 1000))
|
||||
if(aggressive)
|
||||
{
|
||||
const ESM::Position& actor1Pos = actor2.getRefData().getPosition();
|
||||
const ESM::Position& actor2Pos = actor2.getRefData().getPosition();
|
||||
float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos));
|
||||
if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d))
|
||||
{
|
||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
||||
|
@ -346,6 +342,8 @@ namespace MWMechanics
|
|||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||
|
||||
bool wasDead = creatureStats.isDead();
|
||||
|
||||
// attributes
|
||||
for(int i = 0;i < ESM::Attribute::Length;++i)
|
||||
{
|
||||
|
@ -458,6 +456,50 @@ namespace MWMechanics
|
|||
}
|
||||
creatureStats.setHealth(health);
|
||||
|
||||
if (!wasDead && creatureStats.isDead())
|
||||
{
|
||||
// The actor was killed by a magic effect. Figure out if the player was responsible for it.
|
||||
const ActiveSpells& spells = creatureStats.getActiveSpells();
|
||||
bool killedByPlayer = false;
|
||||
bool murderedByPlayer = false;
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
||||
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
||||
effectIt != spell.mEffects.end(); ++effectIt)
|
||||
{
|
||||
int effectId = effectIt->mEffectId;
|
||||
bool isDamageEffect = false;
|
||||
for (unsigned int i=0; i<sizeof(damageEffects)/sizeof(int); ++i)
|
||||
{
|
||||
if (damageEffects[i] == effectId)
|
||||
isDamageEffect = true;
|
||||
}
|
||||
|
||||
|
||||
if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::AbsorbHealth)
|
||||
isDamageEffect = true;
|
||||
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
||||
if (isDamageEffect && caster == player)
|
||||
{
|
||||
killedByPlayer = true;
|
||||
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
|
||||
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
|
||||
// for bystanders it is not possible to tell who attacked first, anyway.
|
||||
if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1
|
||||
&& ptr != player)
|
||||
murderedByPlayer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (murderedByPlayer)
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
|
||||
if (killedByPlayer && player.getClass().getNpcStats(player).isWerewolf())
|
||||
player.getClass().getNpcStats(player).addWerewolfKill();
|
||||
}
|
||||
|
||||
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
|
||||
|
||||
// Update bound effects
|
||||
|
@ -793,7 +835,6 @@ namespace MWMechanics
|
|||
|
||||
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
|
||||
{
|
||||
/// \todo Move me! I shouldn't be here...
|
||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
|
||||
// Force dialogue on sight if bounty is greater than the cutoff
|
||||
|
@ -821,7 +862,6 @@ namespace MWMechanics
|
|||
creatureStats.getAiSequence().stopCombat();
|
||||
|
||||
// Reset factors to attack
|
||||
// TODO: Not a complete list, disposition changes?
|
||||
creatureStats.setHostile(false);
|
||||
creatureStats.setAttacked(false);
|
||||
creatureStats.setAlarmed(false);
|
||||
|
@ -829,15 +869,6 @@ namespace MWMechanics
|
|||
// Update witness crime id
|
||||
npcStats.setCrimeId(-1);
|
||||
}
|
||||
else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue)
|
||||
{
|
||||
if (ptr.getClass().isClass(ptr, "Guard"))
|
||||
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1089,6 +1120,9 @@ namespace MWMechanics
|
|||
isBattleMusic = true;
|
||||
}
|
||||
|
||||
static float sneakTimer = 0.f; // times update of sneak icon
|
||||
static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice"
|
||||
|
||||
// if player is in sneak state see if anyone detects him
|
||||
if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak))
|
||||
{
|
||||
|
@ -1096,27 +1130,54 @@ namespace MWMechanics
|
|||
const int radius = esmStore.get<ESM::GameSetting>().find("fSneakUseDist")->getInt();
|
||||
bool detected = false;
|
||||
|
||||
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
static float fSneakUseDelay = esmStore.get<ESM::GameSetting>().find("fSneakUseDelay")->getFloat();
|
||||
|
||||
if (sneakTimer >= fSneakUseDelay)
|
||||
sneakTimer = 0.f;
|
||||
|
||||
if (sneakTimer == 0.f)
|
||||
{
|
||||
if (iter->first == player) // not the player
|
||||
continue;
|
||||
// Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress.
|
||||
bool avoidedNotice = false;
|
||||
|
||||
// is the player in range and can they be detected
|
||||
if ( (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius)
|
||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first)
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(player, iter->first))
|
||||
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
detected = true;
|
||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iter->first == player) // not the player
|
||||
continue;
|
||||
|
||||
if (!detected)
|
||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);
|
||||
// is the player in range and can they be detected
|
||||
if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(player, iter->first))
|
||||
{
|
||||
if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first))
|
||||
{
|
||||
detected = true;
|
||||
avoidedNotice = false;
|
||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
|
||||
break;
|
||||
}
|
||||
else if (!detected)
|
||||
avoidedNotice = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sneakSkillTimer >= fSneakUseDelay)
|
||||
sneakSkillTimer = 0.f;
|
||||
|
||||
if (avoidedNotice && sneakSkillTimer == 0.f)
|
||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0);
|
||||
|
||||
if (!detected)
|
||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);
|
||||
}
|
||||
sneakTimer += duration;
|
||||
sneakSkillTimer += duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
sneakTimer = 0.f;
|
||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
void Actors::restoreDynamicStats(bool sleep)
|
||||
|
|
|
@ -470,6 +470,8 @@ namespace MWMechanics
|
|||
|
||||
if(preferShortcut)
|
||||
{
|
||||
if (canMoveByZ)
|
||||
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mForceNoShortcut = false;
|
||||
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
|
||||
|
|
|
@ -221,6 +221,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
|||
{
|
||||
return; // target is already pursued
|
||||
}
|
||||
if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat
|
||||
&& static_cast<const AiCombat*>(*iter)->getTarget() == static_cast<const AiCombat*>(&package)->getTarget())
|
||||
{
|
||||
return; // already in combat with this actor
|
||||
}
|
||||
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
||||
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
|
||||
}
|
||||
|
|
|
@ -409,13 +409,6 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
|
|||
|
||||
void CharacterController::playDeath(float startpoint, CharacterState death)
|
||||
{
|
||||
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
// The first-person animations do not include death, so we need to
|
||||
// force-switch to third person before playing the death animation.
|
||||
MWBase::Environment::get().getWorld()->useDeathCamera();
|
||||
}
|
||||
|
||||
switch (death)
|
||||
{
|
||||
case CharState_SwimDeath:
|
||||
|
@ -459,6 +452,13 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
|
|||
|
||||
void CharacterController::playRandomDeath(float startpoint)
|
||||
{
|
||||
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
// The first-person animations do not include death, so we need to
|
||||
// force-switch to third person before playing the death animation.
|
||||
MWBase::Environment::get().getWorld()->useDeathCamera();
|
||||
}
|
||||
|
||||
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath"))
|
||||
{
|
||||
mDeathState = CharState_SwimDeath;
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace MWMechanics
|
|||
int CreatureStats::sActorId = 0;
|
||||
|
||||
CreatureStats::CreatureStats()
|
||||
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
|
||||
: mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
|
||||
mTalkedTo (false), mAlarmed (false),
|
||||
mAttacked (false), mHostile (false),
|
||||
mAttackingOrSpell(false),
|
||||
|
@ -245,6 +245,21 @@ namespace MWMechanics
|
|||
mDied = false;
|
||||
}
|
||||
|
||||
bool CreatureStats::hasBeenMurdered() const
|
||||
{
|
||||
return mMurdered;
|
||||
}
|
||||
|
||||
void CreatureStats::notifyMurder()
|
||||
{
|
||||
mMurdered = true;
|
||||
}
|
||||
|
||||
void CreatureStats::clearHasBeenMurdered()
|
||||
{
|
||||
mMurdered = false;
|
||||
}
|
||||
|
||||
void CreatureStats::resurrect()
|
||||
{
|
||||
if (mDead)
|
||||
|
@ -479,6 +494,7 @@ namespace MWMechanics
|
|||
|
||||
state.mDead = mDead;
|
||||
state.mDied = mDied;
|
||||
state.mMurdered = mMurdered;
|
||||
state.mFriendlyHits = mFriendlyHits;
|
||||
state.mTalkedTo = mTalkedTo;
|
||||
state.mAlarmed = mAlarmed;
|
||||
|
@ -527,6 +543,7 @@ namespace MWMechanics
|
|||
|
||||
mDead = state.mDead;
|
||||
mDied = state.mDied;
|
||||
mMurdered = state.mMurdered;
|
||||
mFriendlyHits = state.mFriendlyHits;
|
||||
mTalkedTo = state.mTalkedTo;
|
||||
mAlarmed = state.mAlarmed;
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace MWMechanics
|
|||
AiSequence mAiSequence;
|
||||
bool mDead;
|
||||
bool mDied;
|
||||
bool mMurdered;
|
||||
int mFriendlyHits;
|
||||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
|
@ -170,6 +171,12 @@ namespace MWMechanics
|
|||
|
||||
void clearHasDied();
|
||||
|
||||
bool hasBeenMurdered() const;
|
||||
|
||||
void clearHasBeenMurdered();
|
||||
|
||||
void notifyMurder();
|
||||
|
||||
void resurrect();
|
||||
|
||||
bool hasCommonDisease() const;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "../mwmechanics/aicombat.hpp"
|
||||
#include "../mwmechanics/aipursue.hpp"
|
||||
|
||||
#include <OgreSceneNode.h>
|
||||
|
||||
|
@ -836,7 +837,7 @@ namespace MWMechanics
|
|||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
// What amount of alarm did this crime generate?
|
||||
int alarm;
|
||||
int alarm = 0;
|
||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
||||
else if (type == OT_Pickpocket)
|
||||
|
@ -847,17 +848,14 @@ namespace MWMechanics
|
|||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
||||
else if (type == OT_Theft)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
||||
else
|
||||
return false;
|
||||
|
||||
// Innocent until proven guilty
|
||||
bool reported = false;
|
||||
|
||||
// Find all the actors within the alarm radius
|
||||
std::vector<MWWorld::Ptr> neighbors;
|
||||
|
||||
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
|
||||
int radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getInt();
|
||||
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
||||
|
||||
mActors.getObjectsInRange(from, radius, neighbors);
|
||||
|
||||
|
@ -865,32 +863,29 @@ namespace MWMechanics
|
|||
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
||||
neighbors.push_back(victim);
|
||||
|
||||
int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();
|
||||
bool victimAware = false;
|
||||
|
||||
// Find actors who witnessed the crime
|
||||
// Find actors who directly witnessed the crime
|
||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||
{
|
||||
if (*it == player) continue; // not the player
|
||||
if (*it == player)
|
||||
continue; // skip player
|
||||
if (it->getClass().getCreatureStats(*it).isDead())
|
||||
continue;
|
||||
|
||||
// Was the crime seen?
|
||||
if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
|
||||
if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
|
||||
// Murder crime can be reported even if no one saw it (hearing is enough, I guess).
|
||||
// TODO: Add mod support for stealth executions!
|
||||
|| (type == OT_Murder && *it != victim))
|
||||
{
|
||||
// TODO: Add more messages
|
||||
if (*it == victim)
|
||||
victimAware = true;
|
||||
|
||||
// TODO: are there other messages?
|
||||
if (type == OT_Theft)
|
||||
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
|
||||
|
||||
if (*it == victim)
|
||||
{
|
||||
// Self-defense
|
||||
// The victim is aware of the criminal/assailant. If being assaulted, fight back now
|
||||
// (regardless of whether the assault is reported or not)
|
||||
// This applies to both NPCs and creatures
|
||||
|
||||
// ... except if this is a guard: then the player is given a chance to pay a fine / go to jail instead
|
||||
if (type == OT_Assault && !it->getClass().isClass(*it, "guard"))
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(victim, player);
|
||||
}
|
||||
|
||||
// Crime reporting only applies to NPCs
|
||||
if (!it->getClass().isNpc())
|
||||
continue;
|
||||
|
@ -899,35 +894,25 @@ namespace MWMechanics
|
|||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
||||
{
|
||||
reported = true;
|
||||
|
||||
// Tell everyone, including yourself
|
||||
for (std::vector<MWWorld::Ptr>::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1)
|
||||
{
|
||||
if ( *it1 == player
|
||||
|| !it1->getClass().isNpc()) continue; // not the player and is an NPC
|
||||
|
||||
// Will other witnesses paticipate in crime
|
||||
if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm
|
||||
|| type == OT_Assault )
|
||||
{
|
||||
it1->getClass().getNpcStats(*it1).setCrimeId(id);
|
||||
}
|
||||
|
||||
// Mark as Alarmed for dialogue
|
||||
it1->getClass().getCreatureStats(*it1).setAlarmed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reported)
|
||||
reportCrime(player, victim, type, arg);
|
||||
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
||||
startCombat(victim, player);
|
||||
|
||||
return reported;
|
||||
}
|
||||
|
||||
void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
||||
void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
if (type == OT_Murder && !victim.isEmpty())
|
||||
victim.getClass().getCreatureStats(victim).notifyMurder();
|
||||
|
||||
// Bounty for each type of crime
|
||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||
arg = store.find("iCrimeTresspass")->getInt();
|
||||
|
@ -944,7 +929,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
|
||||
ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()
|
||||
player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()
|
||||
+ arg);
|
||||
|
||||
// If committing a crime against a faction member, expell from the faction
|
||||
|
@ -953,9 +938,81 @@ namespace MWMechanics
|
|||
std::string factionID;
|
||||
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
||||
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
|
||||
if (ptr.getClass().getNpcStats(ptr).isSameFaction(victim.getClass().getNpcStats(victim)))
|
||||
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
|
||||
{
|
||||
ptr.getClass().getNpcStats(ptr).expell(factionID);
|
||||
player.getClass().getNpcStats(player).expell(factionID);
|
||||
}
|
||||
}
|
||||
|
||||
// Make surrounding actors within alarm distance respond to the crime
|
||||
std::vector<MWWorld::Ptr> neighbors;
|
||||
|
||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
|
||||
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
||||
|
||||
mActors.getObjectsInRange(from, radius, neighbors);
|
||||
|
||||
// victim should be considered even beyond alarm radius
|
||||
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
||||
neighbors.push_back(victim);
|
||||
|
||||
int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();
|
||||
|
||||
// What amount of provocation did this crime generate?
|
||||
// Controls whether witnesses will engage combat with the criminal.
|
||||
int fight = 0;
|
||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||
fight = esmStore.get<ESM::GameSetting>().find("iFightTrespass")->getInt();
|
||||
else if (type == OT_Pickpocket)
|
||||
fight = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->getInt();
|
||||
else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses?
|
||||
fight = esmStore.get<ESM::GameSetting>().find("iFightAttack")->getInt();
|
||||
else if (type == OT_Murder)
|
||||
fight = esmStore.get<ESM::GameSetting>().find("iFightKilling")->getInt();
|
||||
else if (type == OT_Theft)
|
||||
fight = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getFloat();
|
||||
|
||||
const int iFightAttacking = esmStore.get<ESM::GameSetting>().find("iFightAttacking")->getInt();
|
||||
|
||||
// Tell everyone (including the original reporter) in alarm range
|
||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||
{
|
||||
if ( *it == player
|
||||
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
|
||||
|
||||
int aggression = fight;
|
||||
|
||||
// Note: iFightAttack is used for the victim, iFightAttacking for witnesses?
|
||||
if (*it != victim && type == OT_Assault)
|
||||
aggression = iFightAttacking;
|
||||
|
||||
if (it->getClass().isClass(*it, "guard"))
|
||||
{
|
||||
// Mark as Alarmed for dialogue
|
||||
it->getClass().getCreatureStats(*it).setAlarmed(true);
|
||||
|
||||
// Set the crime ID, which we will use to calm down participants
|
||||
// once the bounty has been paid.
|
||||
it->getClass().getNpcStats(*it).setCrimeId(id);
|
||||
|
||||
it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true);
|
||||
if (aggressive)
|
||||
{
|
||||
startCombat(*it, player);
|
||||
|
||||
// Set the crime ID, which we will use to calm down participants
|
||||
// once the bounty has been paid.
|
||||
it->getClass().getNpcStats(*it).setCrimeId(id);
|
||||
|
||||
// Mark as Alarmed for dialogue
|
||||
it->getClass().getCreatureStats(*it).setAlarmed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1098,4 +1155,32 @@ namespace MWMechanics
|
|||
{
|
||||
mActors.clear();
|
||||
}
|
||||
|
||||
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias, bool ignoreDistance)
|
||||
{
|
||||
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 pos2 (target.getRefData().getPosition().pos);
|
||||
|
||||
float d = 0;
|
||||
if (!ignoreDistance)
|
||||
d = pos1.distance(pos2);
|
||||
|
||||
static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"iFightDistanceBase")->getInt();
|
||||
static float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"fFightDistanceMultiplier")->getFloat();
|
||||
static float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"fFightDispMult")->getFloat();
|
||||
|
||||
int disposition = 50;
|
||||
if (ptr.getClass().isNpc())
|
||||
disposition = getDerivedDisposition(ptr);
|
||||
|
||||
int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
|
||||
+ (iFightDistanceBase - fFightDistanceMultiplier * d)
|
||||
+ ((50 - disposition) * fFightDispMult))
|
||||
+ bias;
|
||||
|
||||
return (fight >= 100);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,6 +156,10 @@ namespace MWMechanics
|
|||
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
|
||||
virtual void clear();
|
||||
|
||||
/// @param bias Can be used to add an additional aggression bias towards the target,
|
||||
/// making it more likely for the function to return true.
|
||||
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -396,6 +396,8 @@ void MWMechanics::NpcStats::setWerewolf (bool set)
|
|||
{
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
mWerewolfKills = 0;
|
||||
|
||||
for(size_t i = 0;i < ESM::Attribute::Length;i++)
|
||||
{
|
||||
mWerewolfAttributes[i] = getAttribute(i);
|
||||
|
@ -427,6 +429,11 @@ int MWMechanics::NpcStats::getWerewolfKills() const
|
|||
return mWerewolfKills;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::addWerewolfKill()
|
||||
{
|
||||
++mWerewolfKills;
|
||||
}
|
||||
|
||||
int MWMechanics::NpcStats::getProfit() const
|
||||
{
|
||||
return mProfit;
|
||||
|
|
|
@ -126,6 +126,9 @@ namespace MWMechanics
|
|||
|
||||
int getWerewolfKills() const;
|
||||
|
||||
/// Increments mWerewolfKills by 1.
|
||||
void addWerewolfKill();
|
||||
|
||||
float getTimeToStartDrowning() const;
|
||||
/// Sets time left for the creature to drown if it stays underwater.
|
||||
/// @param time value from [0,20]
|
||||
|
|
|
@ -148,13 +148,17 @@ namespace MWMechanics
|
|||
return school;
|
||||
}
|
||||
|
||||
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell)
|
||||
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell, const MagicEffects* effects)
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
effectId);
|
||||
|
||||
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects();
|
||||
if (effects)
|
||||
magicEffects = effects;
|
||||
|
||||
float resisted = 0;
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
|
@ -165,9 +169,9 @@ namespace MWMechanics
|
|||
|
||||
float resistance = 0;
|
||||
if (resistanceEffect != -1)
|
||||
resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude;
|
||||
resistance += magicEffects->get(resistanceEffect).mMagnitude;
|
||||
if (weaknessEffect != -1)
|
||||
resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude;
|
||||
resistance -= magicEffects->get(weaknessEffect).mMagnitude;
|
||||
|
||||
|
||||
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
|
@ -204,9 +208,10 @@ namespace MWMechanics
|
|||
return resisted;
|
||||
}
|
||||
|
||||
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell)
|
||||
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell, const MagicEffects* effects)
|
||||
{
|
||||
float resistance = getEffectResistance(effectId, actor, caster, spell);
|
||||
float resistance = getEffectResistance(effectId, actor, caster, spell, effects);
|
||||
if (resistance >= 0)
|
||||
return 1 - resistance / 100.f;
|
||||
else
|
||||
|
@ -261,6 +266,13 @@ namespace MWMechanics
|
|||
bool firstAppliedEffect = true;
|
||||
bool anyHarmfulEffect = false;
|
||||
|
||||
// HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance.
|
||||
// This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.
|
||||
// Otherwise, they'd only apply after the whole spell was added.
|
||||
MagicEffects targetEffects;
|
||||
if (target.getClass().isActor())
|
||||
targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();
|
||||
|
||||
bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||
|
@ -328,7 +340,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Try reflecting
|
||||
if (!reflected && magnitudeMult > 0 && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
|
||||
if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
|
||||
{
|
||||
int reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude;
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
|
@ -346,8 +358,7 @@ namespace MWMechanics
|
|||
// Try resisting
|
||||
if (magnitudeMult > 0 && target.getClass().isActor())
|
||||
{
|
||||
|
||||
magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell);
|
||||
magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects);
|
||||
if (magnitudeMult == 0)
|
||||
{
|
||||
// Fully resisted, show message
|
||||
|
@ -375,6 +386,8 @@ namespace MWMechanics
|
|||
effect.mDuration = effectIt->mDuration;
|
||||
effect.mMagnitude = magnitude;
|
||||
|
||||
targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));
|
||||
|
||||
appliedLastingEffects.push_back(effect);
|
||||
|
||||
// For absorb effects, also apply the effect to the caster - but with a negative
|
||||
|
@ -452,14 +465,14 @@ namespace MWMechanics
|
|||
if (!appliedLastingEffects.empty())
|
||||
{
|
||||
int casterActorId = -1;
|
||||
if (caster.getClass().isActor())
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||
mSourceName, casterActorId);
|
||||
}
|
||||
|
||||
// Notify the target actor they've been hit
|
||||
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor())
|
||||
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor())
|
||||
target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
|
||||
}
|
||||
|
||||
|
@ -644,7 +657,9 @@ namespace MWMechanics
|
|||
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
||||
if (!projectileModel.empty())
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||
false, enchantment->mEffects, mCaster, mSourceName);
|
||||
false, enchantment->mEffects, mCaster, mSourceName,
|
||||
// Not needed, enchantments can only be cast by actors
|
||||
Ogre::Vector3(1,0,0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -729,8 +744,18 @@ namespace MWMechanics
|
|||
float speed = 0;
|
||||
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
|
||||
if (!projectileModel.empty())
|
||||
{
|
||||
Ogre::Vector3 fallbackDirection (0,1,0);
|
||||
// Fall back to a "caster to target" direction if we have no other means of determining it
|
||||
// (e.g. when cast by a non-actor)
|
||||
if (!mTarget.isEmpty())
|
||||
fallbackDirection =
|
||||
Ogre::Vector3(mTarget.getRefData().getPosition().pos)-
|
||||
Ogre::Vector3(mCaster.getRefData().getPosition().pos);
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||
false, spell->mEffects, mCaster, mSourceName);
|
||||
false, spell->mEffects, mCaster, mSourceName, fallbackDirection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace ESM
|
|||
namespace MWMechanics
|
||||
{
|
||||
class EffectKey;
|
||||
class MagicEffects;
|
||||
|
||||
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
|
||||
|
||||
|
@ -35,15 +36,19 @@ namespace MWMechanics
|
|||
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
|
||||
|
||||
/// @return >=100 for fully resisted. can also return negative value for damage amplification.
|
||||
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL);
|
||||
/// @param effects Override the actor's current magicEffects. Useful if there are effects currently
|
||||
/// being applied (but not applied yet) that should also be considered.
|
||||
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
|
||||
|
||||
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL);
|
||||
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
|
||||
|
||||
class CastSpell
|
||||
{
|
||||
private:
|
||||
MWWorld::Ptr mCaster;
|
||||
MWWorld::Ptr mTarget;
|
||||
MWWorld::Ptr mCaster; // May be empty
|
||||
MWWorld::Ptr mTarget; // May be empty
|
||||
public:
|
||||
bool mStack;
|
||||
std::string mId; // ID of spell, potion, item etc
|
||||
|
@ -54,8 +59,13 @@ namespace MWMechanics
|
|||
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
||||
|
||||
bool cast (const ESM::Spell* spell);
|
||||
|
||||
/// @note mCaster must be an actor
|
||||
bool cast (const MWWorld::Ptr& item);
|
||||
|
||||
/// @note mCaster must be an NPC
|
||||
bool cast (const ESM::Ingredient* ingredient);
|
||||
|
||||
bool cast (const ESM::Potion* potion);
|
||||
|
||||
/// @note Auto detects if spell, ingredient or potion
|
||||
|
|
|
@ -371,6 +371,12 @@ namespace MWScript
|
|||
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||
|
||||
if(!actor.getClass().isActor() || !observer.getClass().isActor())
|
||||
{
|
||||
runtime.push(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
|
||||
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
|
||||
|
@ -392,9 +398,10 @@ namespace MWScript
|
|||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
|
||||
MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true);
|
||||
bool value = false;
|
||||
if(dest != MWWorld::Ptr() )
|
||||
if(dest != MWWorld::Ptr() && source.getClass().isActor() && dest.getClass().isActor())
|
||||
{
|
||||
value = MWBase::Environment::get().getWorld()->getLOS(source,dest);
|
||||
}
|
||||
|
|
|
@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor
|
|||
op 0x2000246: ClearInfoActor, explicit
|
||||
op 0x2000247: BetaComment
|
||||
op 0x2000248: BetaComment, explicit
|
||||
op 0x2000249: OnMurder
|
||||
op 0x200024a: OnMurder, explicit
|
||||
|
||||
opcodes 0x2000249-0x3ffffff unused
|
||||
opcodes 0x200024b-0x3ffffff unused
|
||||
|
|
|
@ -378,12 +378,20 @@ namespace MWScript
|
|||
|
||||
float InterpreterContext::getDistance (const std::string& name, const std::string& id) const
|
||||
{
|
||||
const MWWorld::Ptr ref2 = getReference (id, false, false);
|
||||
// NOTE: id may be empty, indicating an implicit reference
|
||||
|
||||
MWWorld::Ptr ref2;
|
||||
|
||||
if (id.empty())
|
||||
ref2 = getReference("", true, true);
|
||||
else
|
||||
ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true);
|
||||
|
||||
// If either actor is in a non-active cell, return a large value (just like vanilla)
|
||||
if (ref2.isEmpty())
|
||||
return std::numeric_limits<float>().max();
|
||||
|
||||
const MWWorld::Ptr ref = getReference (name, false, false);
|
||||
const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->searchPtr(name, true);
|
||||
if (ref.isEmpty())
|
||||
return std::numeric_limits<float>().max();
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ namespace MWScript
|
|||
virtual void stopScript (const std::string& name);
|
||||
|
||||
virtual float getDistance (const std::string& name, const std::string& id = "") const;
|
||||
///< @note if \a id is empty, assumes an implicit reference
|
||||
|
||||
bool hasBeenActivated (const MWWorld::Ptr& ptr);
|
||||
///< \attention Calling this function for the right reference will mark the action as
|
||||
|
|
|
@ -587,7 +587,9 @@ namespace MWScript
|
|||
}
|
||||
else
|
||||
{
|
||||
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1;
|
||||
player.getClass().getNpcStats(player).getFactionRanks()[factionID] =
|
||||
std::min(player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1,
|
||||
9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1072,6 +1074,25 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpOnMurder : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
ptr.getClass().getCreatureStats (ptr).hasBeenMurdered();
|
||||
|
||||
if (value)
|
||||
ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered();
|
||||
|
||||
runtime.push (value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpOnKnockout : public Interpreter::Opcode0
|
||||
{
|
||||
|
@ -1264,6 +1285,8 @@ namespace MWScript
|
|||
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder<ExplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);
|
||||
|
||||
|
|
|
@ -159,17 +159,21 @@ void FFmpeg_Decoder::open(const std::string &fname)
|
|||
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
|
||||
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
|
||||
{
|
||||
if (mFormatCtx->pb != NULL)
|
||||
// "Note that a user-supplied AVFormatContext will be freed on failure".
|
||||
if (mFormatCtx)
|
||||
{
|
||||
if (mFormatCtx->pb->buffer != NULL)
|
||||
{
|
||||
av_free(mFormatCtx->pb->buffer);
|
||||
mFormatCtx->pb->buffer = NULL;
|
||||
}
|
||||
av_free(mFormatCtx->pb);
|
||||
mFormatCtx->pb = NULL;
|
||||
if (mFormatCtx->pb != NULL)
|
||||
{
|
||||
if (mFormatCtx->pb->buffer != NULL)
|
||||
{
|
||||
av_free(mFormatCtx->pb->buffer);
|
||||
mFormatCtx->pb->buffer = NULL;
|
||||
}
|
||||
av_free(mFormatCtx->pb);
|
||||
mFormatCtx->pb = NULL;
|
||||
}
|
||||
avformat_free_context(mFormatCtx);
|
||||
}
|
||||
avformat_free_context(mFormatCtx);
|
||||
mFormatCtx = NULL;
|
||||
fail("Failed to allocate input stream");
|
||||
}
|
||||
|
|
|
@ -6,30 +6,36 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
ActionApply::ActionApply (const Ptr& target, const std::string& id)
|
||||
: Action (false, target), mId (id)
|
||||
ActionApply::ActionApply (const Ptr& object, const std::string& id)
|
||||
: Action (false, object), mId (id)
|
||||
{}
|
||||
|
||||
void ActionApply::executeImp (const Ptr& actor)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
|
||||
|
||||
getTarget().getClass().apply (getTarget(), mId, actor);
|
||||
actor.getClass().apply (actor, mId, actor);
|
||||
|
||||
actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);
|
||||
}
|
||||
|
||||
|
||||
ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id,
|
||||
ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& object, const std::string& id,
|
||||
int skillIndex, int usageType)
|
||||
: Action (false, target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType)
|
||||
: Action (false, object), mId (id), mSkillIndex (skillIndex), mUsageType (usageType)
|
||||
{}
|
||||
|
||||
void ActionApplyWithSkill::executeImp (const Ptr& actor)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
|
||||
|
||||
if (getTarget().getClass().apply (getTarget(), mId, actor) && mUsageType!=-1)
|
||||
getTarget().getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);
|
||||
if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1)
|
||||
actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);
|
||||
|
||||
actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace MWWorld
|
|||
|
||||
public:
|
||||
|
||||
ActionApply (const Ptr& target, const std::string& id);
|
||||
ActionApply (const Ptr& object, const std::string& id);
|
||||
};
|
||||
|
||||
class ActionApplyWithSkill : public Action
|
||||
|
@ -29,7 +29,7 @@ namespace MWWorld
|
|||
|
||||
public:
|
||||
|
||||
ActionApplyWithSkill (const Ptr& target, const std::string& id,
|
||||
ActionApplyWithSkill (const Ptr& object, const std::string& id,
|
||||
int skillIndex, int usageType);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
|
|||
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
|
||||
iter!=collection.mList.end(); ++iter)
|
||||
{
|
||||
if (iter->mData.getCount() == 0)
|
||||
continue;
|
||||
ESM::ObjectState state;
|
||||
storeState (*iter, state);
|
||||
int slot = equipable ? getSlot (*iter) : -1;
|
||||
|
|
|
@ -57,22 +57,32 @@ namespace MWWorld
|
|||
|
||||
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
|
||||
const std::string &spellId, float speed, bool stack,
|
||||
const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName)
|
||||
const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName,
|
||||
const Ogre::Vector3& fallbackDirection)
|
||||
{
|
||||
// Spawn at 0.75 * ActorHeight
|
||||
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
|
||||
float height = 0;
|
||||
if (OEngine::Physic::PhysicActor* actor = mPhysEngine.getCharacter(caster.getRefData().getHandle()))
|
||||
height = actor->getHalfExtents().z * 2 * 0.75; // Spawn at 0.75 * ActorHeight
|
||||
|
||||
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 pos(caster.getRefData().getPosition().pos);
|
||||
pos.z += height;
|
||||
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
Ogre::Quaternion orient;
|
||||
if (caster.getClass().isActor())
|
||||
orient = Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
else
|
||||
orient = Ogre::Vector3::UNIT_Y.getRotationTo(fallbackDirection);
|
||||
|
||||
MagicBoltState state;
|
||||
state.mSourceName = sourceName;
|
||||
state.mId = model;
|
||||
state.mSpellId = spellId;
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mCaster = caster;
|
||||
if (caster.getClass().isActor())
|
||||
state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
else
|
||||
state.mActorId = -1;
|
||||
state.mSpeed = speed;
|
||||
state.mStack = stack;
|
||||
state.mSoundId = sound;
|
||||
|
@ -152,7 +162,9 @@ namespace MWWorld
|
|||
{
|
||||
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
|
||||
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
MWWorld::Ptr caster = it->mCaster;
|
||||
if (caster.isEmpty())
|
||||
caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
|
||||
if (!obstacle.isEmpty() && obstacle == caster)
|
||||
continue;
|
||||
|
|
|
@ -39,9 +39,10 @@ namespace MWWorld
|
|||
ProjectileManager (Ogre::SceneManager* sceneMgr,
|
||||
OEngine::Physic::PhysicEngine& engine);
|
||||
|
||||
/// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used.
|
||||
void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection);
|
||||
|
||||
void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
|
@ -64,9 +65,15 @@ namespace MWWorld
|
|||
NifOgre::ObjectScenePtr mObject;
|
||||
Ogre::SceneNode* mNode;
|
||||
|
||||
// Actor who shot this projectile
|
||||
int mActorId;
|
||||
|
||||
// actorId doesn't work for non-actors, so we also keep track of the Ptr.
|
||||
// For non-actors, the caster ptr is mainly needed to prevent the projectile
|
||||
// from colliding with its caster.
|
||||
// TODO: this will break when the game is saved and reloaded, since there is currently
|
||||
// no way to write identifiers for non-actors to a savegame.
|
||||
MWWorld::Ptr mCaster;
|
||||
|
||||
// MW-id of this projectile
|
||||
std::string mId;
|
||||
};
|
||||
|
|
|
@ -989,9 +989,18 @@ namespace MWWorld
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*currCell))
|
||||
ptr.getClass().copyToCell(ptr, *newCell, pos);
|
||||
else if (!mWorldScene->isCellActive(*newCell))
|
||||
if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell))
|
||||
{
|
||||
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos);
|
||||
mWorldScene->addObjectToScene(newPtr);
|
||||
|
||||
std::string script = newPtr.getClass().getScript(newPtr);
|
||||
if (!script.empty()) {
|
||||
mLocalScripts.add(script, newPtr);
|
||||
}
|
||||
addContainerScripts(newPtr, newCell);
|
||||
}
|
||||
else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell))
|
||||
{
|
||||
mWorldScene->removeObjectFromScene(ptr);
|
||||
mLocalScripts.remove(ptr);
|
||||
|
@ -2022,20 +2031,24 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc)
|
||||
bool World::getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor)
|
||||
{
|
||||
if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled())
|
||||
if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled())
|
||||
return false; // cannot get LOS unless both NPC's are enabled
|
||||
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents();
|
||||
const float* pos1 = npc.getRefData().getPosition().pos;
|
||||
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents();
|
||||
const float* pos2 = targetNpc.getRefData().getPosition().pos;
|
||||
if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode())
|
||||
return false; // not in active cell
|
||||
|
||||
btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z);
|
||||
btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z);
|
||||
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents();
|
||||
const float* pos1 = actor.getRefData().getPosition().pos;
|
||||
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle())->getHalfExtents();
|
||||
const float* pos2 = targetActor.getRefData().getPosition().pos;
|
||||
|
||||
btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9); // eye level
|
||||
btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z*2*0.9);
|
||||
|
||||
std::pair<std::string, float> result = mPhysEngine->rayTest(from, to,false);
|
||||
if(result.first == "") return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2320,9 +2333,9 @@ namespace MWWorld
|
|||
|
||||
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName)
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection)
|
||||
{
|
||||
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName);
|
||||
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, caster, sourceName, fallbackDirection);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& World::getContentFiles() const
|
||||
|
@ -2335,6 +2348,9 @@ namespace MWWorld
|
|||
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility);
|
||||
if (actor.getClass().hasInventoryStore(actor))
|
||||
actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility);
|
||||
|
||||
// Normally updated once per frame, but here it is kinda important to do it right away.
|
||||
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);
|
||||
}
|
||||
|
||||
bool World::isDark() const
|
||||
|
@ -2436,14 +2452,15 @@ namespace MWWorld
|
|||
if (!ptr.getRefData().isEnabled())
|
||||
return true;
|
||||
|
||||
// Consider references inside containers as well
|
||||
if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name())
|
||||
// Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers)
|
||||
if (mType != World::Detect_Creature &&
|
||||
(ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()))
|
||||
{
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
{
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
if (needToAdd(*it))
|
||||
if (needToAdd(*it, mDetector))
|
||||
{
|
||||
mOut.push_back(ptr);
|
||||
return true;
|
||||
|
@ -2452,16 +2469,25 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
if (needToAdd(ptr))
|
||||
if (needToAdd(ptr, mDetector))
|
||||
mOut.push_back(ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needToAdd (MWWorld::Ptr ptr)
|
||||
bool needToAdd (MWWorld::Ptr ptr, MWWorld::Ptr detector)
|
||||
{
|
||||
if (mType == World::Detect_Creature && ptr.getClass().getTypeName() != typeid(ESM::Creature).name())
|
||||
return false;
|
||||
if (mType == World::Detect_Creature)
|
||||
{
|
||||
// If in werewolf form, this detects only NPCs, otherwise only creatures
|
||||
if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf())
|
||||
{
|
||||
if (ptr.getClass().getTypeName() != typeid(ESM::NPC).name())
|
||||
return false;
|
||||
}
|
||||
else if (ptr.getClass().getTypeName() != typeid(ESM::Creature).name())
|
||||
return false;
|
||||
}
|
||||
if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr))
|
||||
return false;
|
||||
if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty())
|
||||
|
@ -2593,6 +2619,8 @@ namespace MWWorld
|
|||
int days = std::max(1, bounty / iDaysinPrisonMod);
|
||||
|
||||
advanceTime(days * 24);
|
||||
for (int i=0; i<days*24; ++i)
|
||||
MWBase::Environment::get().getMechanicsManager ()->rest (true);
|
||||
|
||||
std::set<int> skills;
|
||||
for (int day=0; day<days; ++day)
|
||||
|
@ -2636,8 +2664,6 @@ namespace MWWorld
|
|||
message += "\n" + skillMsg;
|
||||
}
|
||||
|
||||
// TODO: Sleep the player
|
||||
|
||||
std::vector<std::string> buttons;
|
||||
buttons.push_back("#{sOk}");
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(message, buttons);
|
||||
|
|
|
@ -482,7 +482,7 @@ namespace MWWorld
|
|||
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);
|
||||
///< get all items in active cells owned by this Npc
|
||||
|
||||
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc);
|
||||
virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor);
|
||||
///< get Line of Sight (morrowind stupid implementation)
|
||||
|
||||
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist);
|
||||
|
@ -546,7 +546,7 @@ namespace MWWorld
|
|||
|
||||
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection);
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
|
||||
|
|
|
@ -167,7 +167,6 @@ namespace Compiler
|
|||
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
|
||||
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
|
||||
extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
|
||||
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting);
|
||||
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting,
|
||||
opcodeForceGreetingExplicit);
|
||||
extensions.registerInstruction("goodbye", "", opcodeGoodbye);
|
||||
|
@ -442,7 +441,7 @@ namespace Compiler
|
|||
|
||||
extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace,
|
||||
opcodeGetRaceExplicit);
|
||||
extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills);
|
||||
extensions.registerFunction ("getwerewolfkills", 'l', "", opcodeGetWerewolfKills);
|
||||
extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit);
|
||||
extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit);
|
||||
extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit);
|
||||
|
@ -450,6 +449,7 @@ namespace Compiler
|
|||
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
||||
|
||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
||||
extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit);
|
||||
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
|
||||
|
||||
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
||||
|
|
|
@ -385,6 +385,8 @@ namespace Compiler
|
|||
const int opcodeLowerRankExplicit = 0x20001eb;
|
||||
const int opcodeOnDeath = 0x20001fc;
|
||||
const int opcodeOnDeathExplicit = 0x2000205;
|
||||
const int opcodeOnMurder = 0x2000249;
|
||||
const int opcodeOnMurderExplicit = 0x200024a;
|
||||
const int opcodeOnKnockout = 0x2000240;
|
||||
const int opcodeOnKnockoutExplicit = 0x2000241;
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
|||
mDied = false;
|
||||
esm.getHNOT (mDied, "DIED");
|
||||
|
||||
mMurdered = false;
|
||||
esm.getHNOT (mMurdered, "MURD");
|
||||
|
||||
mFriendlyHits = 0;
|
||||
esm.getHNOT (mFriendlyHits, "FRHT");
|
||||
|
||||
|
@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
if (mDied)
|
||||
esm.writeHNT ("DIED", mDied);
|
||||
|
||||
if (mMurdered)
|
||||
esm.writeHNT ("MURD", mMurdered);
|
||||
|
||||
if (mFriendlyHits)
|
||||
esm.writeHNT ("FRHT", mFriendlyHits);
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace ESM
|
|||
|
||||
bool mDead;
|
||||
bool mDied;
|
||||
bool mMurdered;
|
||||
int mFriendlyHits;
|
||||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
|
|
|
@ -37,6 +37,8 @@ void ESM::InventoryState::load (ESMReader &esm)
|
|||
LightState state;
|
||||
int slot;
|
||||
read (esm, state, slot);
|
||||
if (state.mCount == 0)
|
||||
continue;
|
||||
mLights.push_back (std::make_pair (state, slot));
|
||||
}
|
||||
else
|
||||
|
@ -44,6 +46,8 @@ void ESM::InventoryState::load (ESMReader &esm)
|
|||
ObjectState state;
|
||||
int slot;
|
||||
read (esm, state, slot);
|
||||
if (state.mCount == 0)
|
||||
continue;
|
||||
mItems.push_back (std::make_pair (state, std::make_pair (id, slot)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ static const char* const openmwCfgFile = "openmw.cfg";
|
|||
|
||||
const char* const mwToken = "?mw?";
|
||||
const char* const localToken = "?local?";
|
||||
const char* const userToken = "?user?";
|
||||
const char* const userDataToken = "?userdata?";
|
||||
const char* const globalToken = "?global?";
|
||||
|
||||
ConfigurationManager::ConfigurationManager()
|
||||
|
@ -40,7 +40,7 @@ void ConfigurationManager::setupTokensMapping()
|
|||
{
|
||||
mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath));
|
||||
mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath));
|
||||
mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath));
|
||||
mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath));
|
||||
mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
const Nif::NiZBufferProperty *zprop,
|
||||
const Nif::NiSpecularProperty *specprop,
|
||||
const Nif::NiWireframeProperty *wireprop,
|
||||
bool &needTangents)
|
||||
bool &needTangents, bool disableLighting)
|
||||
{
|
||||
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
|
||||
Ogre::MaterialPtr material = matMgr.getByName(name);
|
||||
|
@ -245,6 +245,14 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
}
|
||||
}
|
||||
|
||||
if (disableLighting)
|
||||
{
|
||||
ambient = Ogre::Vector3(0.f);
|
||||
diffuse = Ogre::Vector3(0.f);
|
||||
specular = Ogre::Vector3(0.f);
|
||||
emissive = Ogre::Vector3(1.f);
|
||||
}
|
||||
|
||||
{
|
||||
// Generate a hash out of all properties that can affect the material.
|
||||
size_t h = 0;
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
const Nif::NiZBufferProperty *zprop,
|
||||
const Nif::NiSpecularProperty *specprop,
|
||||
const Nif::NiWireframeProperty *wireprop,
|
||||
bool &needTangents);
|
||||
bool &needTangents, bool disableLighting=false);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -850,7 +850,10 @@ class NIFObjectLoader
|
|||
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
||||
texprop, matprop, alphaprop,
|
||||
vertprop, zprop, specprop,
|
||||
wireprop, needTangents));
|
||||
wireprop, needTangents,
|
||||
// MW doesn't light particles, but the MaterialProperty
|
||||
// used still has lighting, so that must be ignored.
|
||||
true));
|
||||
|
||||
partsys->setCullIndividually(false);
|
||||
partsys->setParticleQuota(particledata->numParticles);
|
||||
|
|
|
@ -13,10 +13,13 @@
|
|||
<Property key="FontName" value="MonoFont"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<Property key="TextColour" value="1 1 1"/>
|
||||
<Property key="InvertSelected" value="false"/>
|
||||
</Widget>
|
||||
|
||||
<!-- Command line -->
|
||||
<Widget type="EditBox" skin="MW_ConsoleCommand" position="0 338 384 28" align="HStretch Bottom" name="edit_Command"/>
|
||||
<Widget type="EditBox" skin="MW_ConsoleCommand" position="0 338 384 28" align="HStretch Bottom" name="edit_Command">
|
||||
<Property key="InvertSelected" value="false"/>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
</MyGUI>
|
||||
|
|
|
@ -31,17 +31,18 @@
|
|||
<UserString key="HStretch" value="false"/>
|
||||
<UserString key="VStretch" value="false"/>
|
||||
|
||||
<Widget type="TextBox" skin="SandText" position="0 0 100 24" name="AttribMultiplier1"/>
|
||||
<Widget type="TextBox" skin="SandText" position="0 24 100 24" name="AttribMultiplier2"/>
|
||||
<Widget type="TextBox" skin="SandText" position="0 48 100 24" name="AttribMultiplier3"/>
|
||||
<Widget type="TextBox" skin="SandText" position="0 72 100 24" name="AttribMultiplier4"/>
|
||||
<Widget type="TextBox" skin="SandText" position="200 0 100 24" name="AttribMultiplier5"/>
|
||||
<Widget type="TextBox" skin="SandText" position="200 24 100 24" name="AttribMultiplier6"/>
|
||||
<Widget type="TextBox" skin="SandText" position="200 48 100 24" name="AttribMultiplier7"/>
|
||||
<Widget type="TextBox" skin="SandText" position="200 72 100 24" name="AttribMultiplier8"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="0 0 100 24" name="AttribMultiplier1"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="0 24 100 24" name="AttribMultiplier2"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="0 48 100 24" name="AttribMultiplier3"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="0 72 100 24" name="AttribMultiplier4"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="200 0 100 24" name="AttribMultiplier5"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="200 24 100 24" name="AttribMultiplier6"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="200 48 100 24" name="AttribMultiplier7"/>
|
||||
<Widget type="TextBox" skin="SandTextVCenter" position="200 72 100 24" name="AttribMultiplier8"/>
|
||||
|
||||
<Widget type="HBox" position="22 0 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib1">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/>
|
||||
|
@ -55,6 +56,7 @@
|
|||
|
||||
<Widget type="HBox" position="22 24 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib2">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/>
|
||||
|
@ -68,6 +70,7 @@
|
|||
|
||||
<Widget type="HBox" position="22 48 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib3">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/>
|
||||
|
@ -81,6 +84,7 @@
|
|||
|
||||
<Widget type="HBox" position="22 72 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib4">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/>
|
||||
|
@ -95,6 +99,7 @@
|
|||
|
||||
<Widget type="HBox" position="222 0 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib5">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/>
|
||||
|
@ -108,6 +113,7 @@
|
|||
|
||||
<Widget type="HBox" position="222 24 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib6">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/>
|
||||
|
@ -121,6 +127,7 @@
|
|||
|
||||
<Widget type="HBox" position="222 48 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib7">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/>
|
||||
|
@ -134,6 +141,7 @@
|
|||
|
||||
<Widget type="HBox" position="222 72 200 24">
|
||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib8">
|
||||
<UserString key="TextPadding" value="0 0"/>
|
||||
<UserString key="ToolTipType" value="Layout"/>
|
||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||
<UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/>
|
||||
|
|
|
@ -32,6 +32,13 @@
|
|||
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name="SandTextVCenter" size="16 16">
|
||||
<Property key="FontName" value="Default"/>
|
||||
<Property key="TextAlign" value="Left VCenter"/>
|
||||
<Property key="TextColour" value="0.75 0.6 0.35"/>
|
||||
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name="SandTextRight" size="16 16">
|
||||
<Property key="FontName" value="Default"/>
|
||||
<Property key="TextAlign" value="Right Bottom"/>
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
<Widget type="MWScrollBar" skin="MW_HScroll" name="HourSlider" position="0 0 0 18">
|
||||
<Property key="MoveToClick" value="true"/>
|
||||
<Property key="Range" value="24"/>
|
||||
<Property key="Page" value="1"/>
|
||||
<Property key="WheelPage" value="1"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
data="?global?data"
|
||||
data="?mw?Data Files"
|
||||
data-local="?user?data"
|
||||
data-local="?userdata?data"
|
||||
resources=${OPENMW_RESOURCE_FILES}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
data="?global?data"
|
||||
data="?mw?Data Files"
|
||||
data=./data
|
||||
data-local="?user?data"
|
||||
data-local="?userdata?data"
|
||||
resources=./resources
|
||||
|
|
Loading…
Reference in a new issue