forked from teamnwah/openmw-tes3coop
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())
|
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
|
||||||
{
|
{
|
||||||
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
|
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;
|
std::cout << "Installing crash catcher" << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -120,6 +120,7 @@ namespace MWBase
|
||||||
/**
|
/**
|
||||||
* @brief Commit a crime. If any actors witness the crime and report it,
|
* @brief Commit a crime. If any actors witness the crime and report it,
|
||||||
* reportCrime will be called automatically.
|
* 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.
|
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
|
||||||
* @return was the crime reported?
|
* @return was the crime reported?
|
||||||
*/
|
*/
|
||||||
|
@ -191,6 +192,10 @@ namespace MWBase
|
||||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
|
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
|
||||||
|
|
||||||
virtual void clear() = 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;
|
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
|
||||||
///< get all items in active cells owned by this Npc
|
///< 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)
|
///< get Line of Sight (morrowind stupid implementation)
|
||||||
|
|
||||||
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
|
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,
|
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||||
float speed, bool stack, const ESM::EffectList& effects,
|
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,
|
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
|
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);
|
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.
|
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
|
||||||
readState (state2.mInventory);
|
readState (state2.mInventory);
|
||||||
|
|
|
@ -59,35 +59,38 @@ namespace
|
||||||
|
|
||||||
namespace MWClass
|
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
|
void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
{
|
{
|
||||||
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
|
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>();
|
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||||
|
|
||||||
// creature stats
|
// creature stats
|
||||||
|
@ -345,7 +348,7 @@ namespace MWClass
|
||||||
getCreatureStats(ptr).setAttacked(true);
|
getCreatureStats(ptr).setAttacked(true);
|
||||||
|
|
||||||
// Self defense
|
// 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
|
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures
|
||||||
// (they have no movement or attacks anyway)
|
// (they have no movement or attacks anyway)
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
||||||
|
@ -376,9 +379,9 @@ namespace MWClass
|
||||||
if (damage > 0.f)
|
if (damage > 0.f)
|
||||||
{
|
{
|
||||||
// Check for knockdown
|
// 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()
|
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]
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
||||||
{
|
{
|
||||||
|
@ -522,9 +525,10 @@ namespace MWClass
|
||||||
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
||||||
{
|
{
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
|
const GMST& gmst = getGmst();
|
||||||
|
|
||||||
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
||||||
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat());
|
* (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat());
|
||||||
|
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
||||||
|
@ -533,8 +537,8 @@ namespace MWClass
|
||||||
|
|
||||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||||
|
|
||||||
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
|
float runSpeed = walkSpeed*6;
|
||||||
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
|
runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this
|
||||||
|
|
||||||
float moveSpeed;
|
float moveSpeed;
|
||||||
if(normalizedEncumbrance >= 1.0f)
|
if(normalizedEncumbrance >= 1.0f)
|
||||||
|
@ -544,8 +548,8 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
|
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
|
||||||
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
|
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
|
||||||
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
|
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
|
||||||
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
||||||
flySpeed = std::max(0.0f, flySpeed);
|
flySpeed = std::max(0.0f, flySpeed);
|
||||||
moveSpeed = flySpeed;
|
moveSpeed = flySpeed;
|
||||||
}
|
}
|
||||||
|
@ -555,8 +559,8 @@ namespace MWClass
|
||||||
if(running)
|
if(running)
|
||||||
swimSpeed = runSpeed;
|
swimSpeed = runSpeed;
|
||||||
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
|
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
|
||||||
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
|
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
|
||||||
fSwimRunAthleticsMult->getFloat();
|
gmst.fSwimRunAthleticsMult->getFloat();
|
||||||
moveSpeed = swimSpeed;
|
moveSpeed = swimSpeed;
|
||||||
}
|
}
|
||||||
else if(running)
|
else if(running)
|
||||||
|
@ -798,7 +802,27 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
|
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());
|
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
|
||||||
|
|
||||||
|
@ -851,19 +875,4 @@ namespace MWClass
|
||||||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||||
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
|
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 int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);
|
||||||
|
|
||||||
static const ESM::GameSetting *fMinWalkSpeedCreature;
|
// cached GMSTs
|
||||||
static const ESM::GameSetting *fMaxWalkSpeedCreature;
|
struct GMST
|
||||||
static const ESM::GameSetting *fEncumberedMoveEffect;
|
{
|
||||||
static const ESM::GameSetting *fSneakSpeedMultiplier;
|
const ESM::GameSetting *fMinWalkSpeedCreature;
|
||||||
static const ESM::GameSetting *fAthleticsRunBonus;
|
const ESM::GameSetting *fMaxWalkSpeedCreature;
|
||||||
static const ESM::GameSetting *fBaseRunMultiplier;
|
const ESM::GameSetting *fEncumberedMoveEffect;
|
||||||
static const ESM::GameSetting *fMinFlySpeed;
|
const ESM::GameSetting *fSneakSpeedMultiplier;
|
||||||
static const ESM::GameSetting *fMaxFlySpeed;
|
const ESM::GameSetting *fAthleticsRunBonus;
|
||||||
static const ESM::GameSetting *fSwimRunBase;
|
const ESM::GameSetting *fBaseRunMultiplier;
|
||||||
static const ESM::GameSetting *fSwimRunAthleticsMult;
|
const ESM::GameSetting *fMinFlySpeed;
|
||||||
static const ESM::GameSetting *fKnockDownMult;
|
const ESM::GameSetting *fMaxFlySpeed;
|
||||||
static const ESM::GameSetting *iKnockDownOddsMult;
|
const ESM::GameSetting *fSwimRunBase;
|
||||||
static const ESM::GameSetting *iKnockDownOddsBase;
|
const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||||
|
const ESM::GameSetting *fKnockDownMult;
|
||||||
|
const ESM::GameSetting *iKnockDownOddsMult;
|
||||||
|
const ESM::GameSetting *iKnockDownOddsBase;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GMST& getGmst();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -228,38 +228,44 @@ namespace
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
|
const Npc::GMST& Npc::getGmst()
|
||||||
{
|
{
|
||||||
|
static GMST gmst;
|
||||||
static bool inited = false;
|
static bool inited = false;
|
||||||
if(!inited)
|
if(!inited)
|
||||||
{
|
{
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
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");
|
gmst.fMinWalkSpeed = store.find("fMinWalkSpeed");
|
||||||
fMaxWalkSpeed = gmst.find("fMaxWalkSpeed");
|
gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed");
|
||||||
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
|
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
|
||||||
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
|
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
|
||||||
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
|
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
|
||||||
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
|
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
|
||||||
fMinFlySpeed = gmst.find("fMinFlySpeed");
|
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
|
||||||
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
|
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
|
||||||
fSwimRunBase = gmst.find("fSwimRunBase");
|
gmst.fSwimRunBase = store.find("fSwimRunBase");
|
||||||
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
|
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
|
||||||
fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase");
|
gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase");
|
||||||
fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier");
|
gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier");
|
||||||
fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase");
|
gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase");
|
||||||
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
|
gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier");
|
||||||
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
|
gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier");
|
||||||
fWereWolfRunMult = gmst.find("fWereWolfRunMult");
|
gmst.fWereWolfRunMult = store.find("fWereWolfRunMult");
|
||||||
fKnockDownMult = gmst.find("fKnockDownMult");
|
gmst.fKnockDownMult = store.find("fKnockDownMult");
|
||||||
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
|
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
|
||||||
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
|
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
|
||||||
fDamageStrengthBase = gmst.find("fDamageStrengthBase");
|
gmst.fDamageStrengthBase = store.find("fDamageStrengthBase");
|
||||||
fDamageStrengthMult = gmst.find("fDamageStrengthMult");
|
gmst.fDamageStrengthMult = store.find("fDamageStrengthMult");
|
||||||
|
|
||||||
inited = true;
|
inited = true;
|
||||||
}
|
}
|
||||||
|
return gmst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
{
|
{
|
||||||
std::auto_ptr<NpcCustomData> data(new NpcCustomData);
|
std::auto_ptr<NpcCustomData> data(new NpcCustomData);
|
||||||
|
@ -404,11 +410,6 @@ namespace MWClass
|
||||||
ptr.get<ESM::NPC>();
|
ptr.get<ESM::NPC>();
|
||||||
assert(ref->mBase != NULL);
|
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";
|
std::string model = "meshes\\base_anim.nif";
|
||||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||||
if(race->mData.mFlags & ESM::Race::Beast)
|
if(race->mData.mFlags & ESM::Race::Beast)
|
||||||
|
@ -423,9 +424,9 @@ namespace MWClass
|
||||||
if(getNpcStats(ptr).isWerewolf())
|
if(getNpcStats(ptr).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
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>();
|
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
|
void Npc::hit(const MWWorld::Ptr& ptr, int type) const
|
||||||
{
|
{
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
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())
|
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
||||||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||||
|
@ -461,9 +464,9 @@ namespace MWClass
|
||||||
|
|
||||||
// Reduce fatigue
|
// Reduce fatigue
|
||||||
// somewhat of a guess, but using the weapon weight makes sense
|
// somewhat of a guess, but using the weapon weight makes sense
|
||||||
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
|
const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat();
|
||||||
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
|
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
|
||||||
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
|
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
|
||||||
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
|
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
|
||||||
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
|
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
|
||||||
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
||||||
|
@ -472,10 +475,10 @@ namespace MWClass
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
|
||||||
getCreatureStats(ptr).setFatigue(fatigue);
|
getCreatureStats(ptr).setFatigue(fatigue);
|
||||||
|
|
||||||
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
|
const float fCombatDistance = store.find("fCombatDistance")->getFloat();
|
||||||
float dist = fCombatDistance * (!weapon.isEmpty() ?
|
float dist = fCombatDistance * (!weapon.isEmpty() ?
|
||||||
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
||||||
gmst.find("fHandToHandReach")->getFloat());
|
store.find("fHandToHandReach")->getFloat());
|
||||||
|
|
||||||
// TODO: Use second to work out the hit angle
|
// TODO: Use second to work out the hit angle
|
||||||
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
|
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
|
||||||
|
@ -522,8 +525,8 @@ namespace MWClass
|
||||||
if(attack)
|
if(attack)
|
||||||
{
|
{
|
||||||
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
||||||
damage *= fDamageStrengthBase->getFloat() +
|
damage *= gmst.fDamageStrengthBase->getFloat() +
|
||||||
(stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1);
|
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
|
||||||
if(weaphashealth)
|
if(weaphashealth)
|
||||||
{
|
{
|
||||||
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
||||||
|
@ -535,7 +538,7 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
// Reduce weapon charge by at least one, but cap at 0
|
// Reduce weapon charge by at least one, but cap at 0
|
||||||
weaphealth -= std::min(std::max(1,
|
weaphealth -= std::min(std::max(1,
|
||||||
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
(int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
||||||
|
|
||||||
weapon.getCellRef().setCharge(weaphealth);
|
weapon.getCellRef().setCharge(weaphealth);
|
||||||
}
|
}
|
||||||
|
@ -552,8 +555,8 @@ namespace MWClass
|
||||||
// Note: MCP contains an option to include Strength in hand-to-hand damage
|
// 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
|
// calculations. Some mods recommend using it, so we may want to include am
|
||||||
// option for it.
|
// option for it.
|
||||||
float minstrike = gmst.find("fMinHandToHandMult")->getFloat();
|
float minstrike = store.find("fMinHandToHandMult")->getFloat();
|
||||||
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
|
float maxstrike = store.find("fMaxHandToHandMult")->getFloat();
|
||||||
damage = stats.getSkill(weapskill).getModified();
|
damage = stats.getSkill(weapskill).getModified();
|
||||||
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
|
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
|
||||||
|
|
||||||
|
@ -567,7 +570,7 @@ namespace MWClass
|
||||||
damage *= glob.find("WerewolfClawMult")->mValue.getFloat();
|
damage *= glob.find("WerewolfClawMult")->mValue.getFloat();
|
||||||
}
|
}
|
||||||
if(healthdmg)
|
if(healthdmg)
|
||||||
damage *= gmst.find("fHandtoHandHealthPer")->getFloat();
|
damage *= store.find("fHandtoHandHealthPer")->getFloat();
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
if(stats.isWerewolf())
|
if(stats.isWerewolf())
|
||||||
|
@ -585,12 +588,12 @@ namespace MWClass
|
||||||
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
|
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
|
||||||
if(!detected)
|
if(!detected)
|
||||||
{
|
{
|
||||||
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
|
damage *= store.find("fCombatCriticalStrikeMult")->getFloat();
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
||||||
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
if (othercls.getCreatureStats(victim).getKnockedDown())
|
if (othercls.getCreatureStats(victim).getKnockedDown())
|
||||||
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
damage *= store.find("fCombatKODamageMult")->getFloat();
|
||||||
|
|
||||||
// Apply "On hit" enchanted weapons
|
// Apply "On hit" enchanted weapons
|
||||||
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
|
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
|
||||||
|
@ -624,11 +627,12 @@ namespace MWClass
|
||||||
// NOTE: 'object' and/or 'attacker' may be empty.
|
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||||
|
|
||||||
// Attacking peaceful NPCs is a crime
|
// Attacking peaceful NPCs is a crime
|
||||||
// anything below 80 is considered peaceful (see Actors::updateActor)
|
|
||||||
if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() &&
|
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);
|
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
||||||
|
|
||||||
|
bool wasDead = getCreatureStats(ptr).isDead();
|
||||||
|
|
||||||
getCreatureStats(ptr).setAttacked(true);
|
getCreatureStats(ptr).setAttacked(true);
|
||||||
|
|
||||||
if(!successful)
|
if(!successful)
|
||||||
|
@ -660,6 +664,8 @@ namespace MWClass
|
||||||
// something, alert the character controller, scripts, etc.
|
// something, alert the character controller, scripts, etc.
|
||||||
|
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
const GMST& gmst = getGmst();
|
||||||
|
|
||||||
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
|
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
|
||||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
if (roll < chance)
|
if (roll < chance)
|
||||||
|
@ -668,9 +674,9 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for knockdown
|
// 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()
|
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]
|
roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
||||||
{
|
{
|
||||||
|
@ -752,6 +758,22 @@ namespace MWClass
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
||||||
getCreatureStats(ptr).setFatigue(fatigue);
|
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
|
void Npc::block(const MWWorld::Ptr &ptr) const
|
||||||
|
@ -853,6 +875,8 @@ namespace MWClass
|
||||||
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
const GMST& gmst = getGmst();
|
||||||
|
|
||||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
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 sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||||
|
|
||||||
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
||||||
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
|
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
|
||||||
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
|
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
|
||||||
walkSpeed = std::max(0.0f, walkSpeed);
|
walkSpeed = std::max(0.0f, walkSpeed);
|
||||||
if(sneaking)
|
if(sneaking)
|
||||||
walkSpeed *= fSneakSpeedMultiplier->getFloat();
|
walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat();
|
||||||
|
|
||||||
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
|
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())
|
if(npcdata->mNpcStats.isWerewolf())
|
||||||
runSpeed *= fWereWolfRunMult->getFloat();
|
runSpeed *= gmst.fWereWolfRunMult->getFloat();
|
||||||
|
|
||||||
float moveSpeed;
|
float moveSpeed;
|
||||||
if(normalizedEncumbrance >= 1.0f)
|
if(normalizedEncumbrance >= 1.0f)
|
||||||
|
@ -881,8 +905,8 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +
|
float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +
|
||||||
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
|
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
|
||||||
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
|
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
|
||||||
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
|
||||||
flySpeed = std::max(0.0f, flySpeed);
|
flySpeed = std::max(0.0f, flySpeed);
|
||||||
moveSpeed = flySpeed;
|
moveSpeed = flySpeed;
|
||||||
}
|
}
|
||||||
|
@ -892,8 +916,8 @@ namespace MWClass
|
||||||
if(running)
|
if(running)
|
||||||
swimSpeed = runSpeed;
|
swimSpeed = runSpeed;
|
||||||
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
|
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
|
||||||
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
|
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
|
||||||
fSwimRunAthleticsMult->getFloat();
|
gmst.fSwimRunAthleticsMult->getFloat();
|
||||||
moveSpeed = swimSpeed;
|
moveSpeed = swimSpeed;
|
||||||
}
|
}
|
||||||
else if(running && !sneaking)
|
else if(running && !sneaking)
|
||||||
|
@ -909,9 +933,10 @@ namespace MWClass
|
||||||
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
||||||
{
|
{
|
||||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||||
|
const GMST& gmst = getGmst();
|
||||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||||
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
|
const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() +
|
||||||
fJumpEncumbranceMultiplier->getFloat() *
|
gmst.fJumpEncumbranceMultiplier->getFloat() *
|
||||||
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
|
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
|
||||||
|
|
||||||
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
|
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
|
||||||
|
@ -922,14 +947,14 @@ namespace MWClass
|
||||||
a = 50.0f;
|
a = 50.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float x = fJumpAcrobaticsBase->getFloat() +
|
float x = gmst.fJumpAcrobaticsBase->getFloat() +
|
||||||
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
|
std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat());
|
||||||
x += 3.0f * b * fJumpAcroMultiplier->getFloat();
|
x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat();
|
||||||
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
|
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
|
||||||
x *= encumbranceTerm;
|
x *= encumbranceTerm;
|
||||||
|
|
||||||
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
|
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
|
||||||
x *= fJumpRunMultiplier->getFloat();
|
x *= gmst.fJumpRunMultiplier->getFloat();
|
||||||
x *= npcdata->mNpcStats.getFatigueTerm();
|
x *= npcdata->mNpcStats.getFatigueTerm();
|
||||||
x -= -627.2f;/*gravity constant*/
|
x -= -627.2f;/*gravity constant*/
|
||||||
x /= 3.0f;
|
x /= 3.0f;
|
||||||
|
@ -940,19 +965,19 @@ namespace MWClass
|
||||||
float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const
|
float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const
|
||||||
{
|
{
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
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)
|
if (fallHeight >= fallDistanceMin)
|
||||||
{
|
{
|
||||||
const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
|
const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
|
||||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||||
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
|
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
|
||||||
const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat();
|
const float fallAcroBase = store.find("fFallAcroBase")->getFloat();
|
||||||
const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat();
|
const float fallAcroMult = store.find("fFallAcroMult")->getFloat();
|
||||||
const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat();
|
const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat();
|
||||||
const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat();
|
const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat();
|
||||||
|
|
||||||
float x = fallHeight - fallDistanceMin;
|
float x = fallHeight - fallDistanceMin;
|
||||||
x -= (1.5 * acrobaticsSkill) + jumpSpellBonus;
|
x -= (1.5 * acrobaticsSkill) + jumpSpellBonus;
|
||||||
|
@ -1087,14 +1112,14 @@ namespace MWClass
|
||||||
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
|
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
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);
|
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||||
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
|
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
|
||||||
|
|
||||||
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
|
int iBaseArmorSkill = store.find("iBaseArmorSkill")->getInt();
|
||||||
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
|
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat();
|
||||||
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
|
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat();
|
||||||
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
|
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
|
||||||
|
|
||||||
int ratings[MWWorld::InventoryStore::Slots];
|
int ratings[MWWorld::InventoryStore::Slots];
|
||||||
|
@ -1276,7 +1301,18 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
|
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());
|
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
|
||||||
|
|
||||||
|
@ -1339,27 +1375,4 @@ namespace MWClass
|
||||||
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
MWWorld::ContainerStore& store = getContainerStore(ptr);
|
||||||
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
|
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
|
virtual MWWorld::Ptr
|
||||||
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
|
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
|
||||||
|
|
||||||
static const ESM::GameSetting *fMinWalkSpeed;
|
struct GMST
|
||||||
static const ESM::GameSetting *fMaxWalkSpeed;
|
{
|
||||||
static const ESM::GameSetting *fEncumberedMoveEffect;
|
const ESM::GameSetting *fMinWalkSpeed;
|
||||||
static const ESM::GameSetting *fSneakSpeedMultiplier;
|
const ESM::GameSetting *fMaxWalkSpeed;
|
||||||
static const ESM::GameSetting *fAthleticsRunBonus;
|
const ESM::GameSetting *fEncumberedMoveEffect;
|
||||||
static const ESM::GameSetting *fBaseRunMultiplier;
|
const ESM::GameSetting *fSneakSpeedMultiplier;
|
||||||
static const ESM::GameSetting *fMinFlySpeed;
|
const ESM::GameSetting *fAthleticsRunBonus;
|
||||||
static const ESM::GameSetting *fMaxFlySpeed;
|
const ESM::GameSetting *fBaseRunMultiplier;
|
||||||
static const ESM::GameSetting *fSwimRunBase;
|
const ESM::GameSetting *fMinFlySpeed;
|
||||||
static const ESM::GameSetting *fSwimRunAthleticsMult;
|
const ESM::GameSetting *fMaxFlySpeed;
|
||||||
static const ESM::GameSetting *fJumpEncumbranceBase;
|
const ESM::GameSetting *fSwimRunBase;
|
||||||
static const ESM::GameSetting *fJumpEncumbranceMultiplier;
|
const ESM::GameSetting *fSwimRunAthleticsMult;
|
||||||
static const ESM::GameSetting *fJumpAcrobaticsBase;
|
const ESM::GameSetting *fJumpEncumbranceBase;
|
||||||
static const ESM::GameSetting *fJumpAcroMultiplier;
|
const ESM::GameSetting *fJumpEncumbranceMultiplier;
|
||||||
static const ESM::GameSetting *fJumpRunMultiplier;
|
const ESM::GameSetting *fJumpAcrobaticsBase;
|
||||||
static const ESM::GameSetting *fWereWolfRunMult;
|
const ESM::GameSetting *fJumpAcroMultiplier;
|
||||||
static const ESM::GameSetting *fKnockDownMult;
|
const ESM::GameSetting *fJumpRunMultiplier;
|
||||||
static const ESM::GameSetting *iKnockDownOddsMult;
|
const ESM::GameSetting *fWereWolfRunMult;
|
||||||
static const ESM::GameSetting *iKnockDownOddsBase;
|
const ESM::GameSetting *fKnockDownMult;
|
||||||
static const ESM::GameSetting *fDamageStrengthBase;
|
const ESM::GameSetting *iKnockDownOddsMult;
|
||||||
static const ESM::GameSetting *fDamageStrengthMult;
|
const ESM::GameSetting *iKnockDownOddsBase;
|
||||||
|
const ESM::GameSetting *fDamageStrengthBase;
|
||||||
|
const ESM::GameSetting *fDamageStrengthMult;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GMST& getGmst();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -166,13 +166,8 @@ namespace MWClass
|
||||||
MWWorld::LiveCellRef<ESM::Potion> *ref =
|
MWWorld::LiveCellRef<ESM::Potion> *ref =
|
||||||
ptr.get<ESM::Potion>();
|
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 (
|
boost::shared_ptr<MWWorld::Action> action (
|
||||||
new MWWorld::ActionApply (actor, ref->mBase->mId));
|
new MWWorld::ActionApply (ptr, ref->mBase->mId));
|
||||||
|
|
||||||
action->setSound ("Drink");
|
action->setSound ("Drink");
|
||||||
|
|
||||||
|
|
|
@ -530,7 +530,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
|
||||||
|
|
||||||
case SelectWrapper::Function_ShouldAttack:
|
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:
|
case SelectWrapper::Function_CreatureTargetted:
|
||||||
|
|
||||||
|
|
|
@ -384,6 +384,8 @@ namespace MWGui
|
||||||
|
|
||||||
void HUD::onFrame(float dt)
|
void HUD::onFrame(float dt)
|
||||||
{
|
{
|
||||||
|
LocalMapBase::onFrame(dt);
|
||||||
|
|
||||||
mCellNameTimer -= dt;
|
mCellNameTimer -= dt;
|
||||||
mWeaponSpellTimer -= dt;
|
mWeaponSpellTimer -= dt;
|
||||||
if (mCellNameTimer < 0)
|
if (mCellNameTimer < 0)
|
||||||
|
|
|
@ -104,9 +104,10 @@ namespace MWGui
|
||||||
|
|
||||||
int attribute = mSpentAttributes[i];
|
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);
|
image->setPosition(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ namespace MWGui
|
||||||
, mLastDirectionX(0.0f)
|
, mLastDirectionX(0.0f)
|
||||||
, mLastDirectionY(0.0f)
|
, mLastDirectionY(0.0f)
|
||||||
, mCompass(NULL)
|
, mCompass(NULL)
|
||||||
|
, mMarkerUpdateTimer(0.0f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,6 +346,18 @@ namespace MWGui
|
||||||
markerWidget->setUserString("IsMarker", "true");
|
markerWidget->setUserString("IsMarker", "true");
|
||||||
markerWidget->setUserData(markerPos);
|
markerWidget->setUserData(markerPos);
|
||||||
markerWidget->setColour(markerColour);
|
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)
|
void MapWindow::onFrame(float dt)
|
||||||
{
|
{
|
||||||
|
LocalMapBase::onFrame(dt);
|
||||||
|
|
||||||
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
|
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
|
||||||
{
|
{
|
||||||
mGlobalMapRender->exploreCell(it->first, it->second);
|
mGlobalMapRender->exploreCell(it->first, it->second);
|
||||||
|
|
|
@ -35,6 +35,8 @@ namespace MWGui
|
||||||
void setPlayerDir(const float x, const float y);
|
void setPlayerDir(const float x, const float y);
|
||||||
void setPlayerPos(const float x, const float y);
|
void setPlayerPos(const float x, const float y);
|
||||||
|
|
||||||
|
void onFrame(float dt);
|
||||||
|
|
||||||
bool toggleFogOfWar();
|
bool toggleFogOfWar();
|
||||||
|
|
||||||
struct MarkerPosition
|
struct MarkerPosition
|
||||||
|
@ -73,12 +75,14 @@ namespace MWGui
|
||||||
virtual void notifyMapChanged() {}
|
virtual void notifyMapChanged() {}
|
||||||
|
|
||||||
// Update markers (Detect X effects, Mark/Recall effects)
|
// Update markers (Detect X effects, Mark/Recall effects)
|
||||||
// Note, door markers handled in setActiveCell
|
// Note, door markers are handled in setActiveCell
|
||||||
void updateMarkers();
|
void updateMarkers();
|
||||||
void addDetectionMarkers(int type);
|
void addDetectionMarkers(int type);
|
||||||
|
|
||||||
OEngine::GUI::Layout* mLayout;
|
OEngine::GUI::Layout* mLayout;
|
||||||
|
|
||||||
|
float mMarkerUpdateTimer;
|
||||||
|
|
||||||
bool mMapDragAndDrop;
|
bool mMapDragAndDrop;
|
||||||
|
|
||||||
float mLastPositionX;
|
float mLastPositionX;
|
||||||
|
|
|
@ -488,14 +488,16 @@ namespace MWGui
|
||||||
text += "\n#BF9959#{sExpelled}";
|
text += "\n#BF9959#{sExpelled}";
|
||||||
else
|
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
|
// 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* attr1 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[0]);
|
||||||
const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]);
|
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();
|
for (std::map<std::string, std::string>::iterator it = userStrings.begin();
|
||||||
it != userStrings.end(); ++it)
|
it != userStrings.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->first == "ToolTipType"
|
|
||||||
|| it->first == "ToolTipLayout"
|
|
||||||
|| it->first == "IsMarker")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
|
||||||
size_t underscorePos = it->first.find("_");
|
size_t underscorePos = it->first.find("_");
|
||||||
|
if (underscorePos == std::string::npos)
|
||||||
|
continue;
|
||||||
std::string propertyKey = it->first.substr(0, underscorePos);
|
std::string propertyKey = it->first.substr(0, underscorePos);
|
||||||
std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1));
|
std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1));
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/containerstore.hpp"
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
@ -140,6 +141,9 @@ namespace MWGui
|
||||||
if (playerGold<price)
|
if (playerGold<price)
|
||||||
return;
|
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);
|
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
|
||||||
|
|
||||||
|
|
|
@ -630,8 +630,11 @@ namespace MWGui
|
||||||
|
|
||||||
MyGUI::IntSize AutoSizedButton::getRequestedSize()
|
MyGUI::IntSize AutoSizedButton::getRequestedSize()
|
||||||
{
|
{
|
||||||
MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0);
|
MyGUI::IntSize padding(24, 8);
|
||||||
size.height = std::max(24, size.height);
|
if (isUserString("TextPadding"))
|
||||||
|
padding = MyGUI::IntSize::parse(getUserString("TextPadding"));
|
||||||
|
|
||||||
|
MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
|
||||||
#include "../mwdialogue/dialoguemanagerimp.hpp"
|
#include "../mwdialogue/dialoguemanagerimp.hpp"
|
||||||
|
|
||||||
|
@ -826,6 +826,14 @@ namespace MWInput
|
||||||
|
|
||||||
void InputManager::quickKey (int index)
|
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())
|
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||||
MWBase::Environment::get().getWindowManager()->activateQuickKey (index);
|
MWBase::Environment::get().getWindowManager()->activateQuickKey (index);
|
||||||
}
|
}
|
||||||
|
@ -834,7 +842,18 @@ namespace MWInput
|
||||||
{
|
{
|
||||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()
|
if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()
|
||||||
&& MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
|
&& 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);
|
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu);
|
||||||
|
|
||||||
|
}
|
||||||
else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) {
|
else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) {
|
||||||
while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows
|
while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows
|
||||||
MWBase::Environment::get().getWindowManager()->getCurrentModal()->exit();
|
MWBase::Environment::get().getWindowManager()->getCurrentModal()->exit();
|
||||||
|
|
|
@ -40,6 +40,10 @@ namespace MWMechanics
|
||||||
void readState (const ESM::ActiveSpells& state);
|
void readState (const ESM::ActiveSpells& state);
|
||||||
void writeState (ESM::ActiveSpells& state) const;
|
void writeState (ESM::ActiveSpells& state) const;
|
||||||
|
|
||||||
|
TIterator begin() const;
|
||||||
|
|
||||||
|
TIterator end() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
mutable TContainer mSpells;
|
mutable TContainer mSpells;
|
||||||
|
@ -57,10 +61,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
const TContainer& getActiveSpells() const;
|
const TContainer& getActiveSpells() const;
|
||||||
|
|
||||||
TIterator begin() const;
|
|
||||||
|
|
||||||
TIterator end() const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActiveSpells();
|
ActiveSpells();
|
||||||
|
|
|
@ -201,32 +201,28 @@ namespace MWMechanics
|
||||||
|| (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target
|
|| (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float fight;
|
bool aggressive;
|
||||||
|
|
||||||
if (againstPlayer)
|
if (againstPlayer)
|
||||||
fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified();
|
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fight = 0;
|
aggressive = false;
|
||||||
// if one of actors is creature then we should make a decision to start combat or not
|
// 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
|
// NOTE: function doesn't take into account combat between 2 creatures
|
||||||
if (!actor1.getClass().isNpc())
|
if (!actor1.getClass().isNpc())
|
||||||
{
|
{
|
||||||
// if creature is hostile then it is necessarily to start combat
|
// if creature is hostile then it is necessarily to start combat
|
||||||
if (creatureStats.isHostile()) fight = 100;
|
if (creatureStats.isHostile()) aggressive = true;
|
||||||
else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified();
|
else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::Position actor1Pos = actor1.getRefData().getPosition();
|
if(aggressive)
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
|
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))
|
if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d))
|
||||||
{
|
{
|
||||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
||||||
|
@ -346,6 +342,8 @@ namespace MWMechanics
|
||||||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||||
|
|
||||||
|
bool wasDead = creatureStats.isDead();
|
||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
for(int i = 0;i < ESM::Attribute::Length;++i)
|
for(int i = 0;i < ESM::Attribute::Length;++i)
|
||||||
{
|
{
|
||||||
|
@ -458,6 +456,50 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
creatureStats.setHealth(health);
|
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?
|
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
|
||||||
|
|
||||||
// Update bound effects
|
// Update bound effects
|
||||||
|
@ -793,7 +835,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
|
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();
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
|
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
|
||||||
// Force dialogue on sight if bounty is greater than the cutoff
|
// Force dialogue on sight if bounty is greater than the cutoff
|
||||||
|
@ -821,7 +862,6 @@ namespace MWMechanics
|
||||||
creatureStats.getAiSequence().stopCombat();
|
creatureStats.getAiSequence().stopCombat();
|
||||||
|
|
||||||
// Reset factors to attack
|
// Reset factors to attack
|
||||||
// TODO: Not a complete list, disposition changes?
|
|
||||||
creatureStats.setHostile(false);
|
creatureStats.setHostile(false);
|
||||||
creatureStats.setAttacked(false);
|
creatureStats.setAttacked(false);
|
||||||
creatureStats.setAlarmed(false);
|
creatureStats.setAlarmed(false);
|
||||||
|
@ -829,15 +869,6 @@ namespace MWMechanics
|
||||||
// Update witness crime id
|
// Update witness crime id
|
||||||
npcStats.setCrimeId(-1);
|
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;
|
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 is in sneak state see if anyone detects him
|
||||||
if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak))
|
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();
|
const int radius = esmStore.get<ESM::GameSetting>().find("fSneakUseDist")->getInt();
|
||||||
bool detected = false;
|
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
|
// Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress.
|
||||||
continue;
|
bool avoidedNotice = false;
|
||||||
|
|
||||||
// is the player in range and can they be detected
|
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
detected = true;
|
if (iter->first == player) // not the player
|
||||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
|
continue;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!detected)
|
// is the player in range and can they be detected
|
||||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);
|
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
|
else
|
||||||
|
{
|
||||||
|
sneakTimer = 0.f;
|
||||||
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
|
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Actors::restoreDynamicStats(bool sleep)
|
void Actors::restoreDynamicStats(bool sleep)
|
||||||
|
|
|
@ -470,6 +470,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
if(preferShortcut)
|
if(preferShortcut)
|
||||||
{
|
{
|
||||||
|
if (canMoveByZ)
|
||||||
|
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
|
||||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||||
mForceNoShortcut = false;
|
mForceNoShortcut = false;
|
||||||
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
|
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
|
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)
|
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
||||||
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
|
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)
|
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)
|
switch (death)
|
||||||
{
|
{
|
||||||
case CharState_SwimDeath:
|
case CharState_SwimDeath:
|
||||||
|
@ -459,6 +452,13 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
|
||||||
|
|
||||||
void CharacterController::playRandomDeath(float startpoint)
|
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"))
|
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath"))
|
||||||
{
|
{
|
||||||
mDeathState = CharState_SwimDeath;
|
mDeathState = CharState_SwimDeath;
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace MWMechanics
|
||||||
int CreatureStats::sActorId = 0;
|
int CreatureStats::sActorId = 0;
|
||||||
|
|
||||||
CreatureStats::CreatureStats()
|
CreatureStats::CreatureStats()
|
||||||
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
|
: mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
|
||||||
mTalkedTo (false), mAlarmed (false),
|
mTalkedTo (false), mAlarmed (false),
|
||||||
mAttacked (false), mHostile (false),
|
mAttacked (false), mHostile (false),
|
||||||
mAttackingOrSpell(false),
|
mAttackingOrSpell(false),
|
||||||
|
@ -245,6 +245,21 @@ namespace MWMechanics
|
||||||
mDied = false;
|
mDied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreatureStats::hasBeenMurdered() const
|
||||||
|
{
|
||||||
|
return mMurdered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::notifyMurder()
|
||||||
|
{
|
||||||
|
mMurdered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::clearHasBeenMurdered()
|
||||||
|
{
|
||||||
|
mMurdered = false;
|
||||||
|
}
|
||||||
|
|
||||||
void CreatureStats::resurrect()
|
void CreatureStats::resurrect()
|
||||||
{
|
{
|
||||||
if (mDead)
|
if (mDead)
|
||||||
|
@ -479,6 +494,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
state.mDead = mDead;
|
state.mDead = mDead;
|
||||||
state.mDied = mDied;
|
state.mDied = mDied;
|
||||||
|
state.mMurdered = mMurdered;
|
||||||
state.mFriendlyHits = mFriendlyHits;
|
state.mFriendlyHits = mFriendlyHits;
|
||||||
state.mTalkedTo = mTalkedTo;
|
state.mTalkedTo = mTalkedTo;
|
||||||
state.mAlarmed = mAlarmed;
|
state.mAlarmed = mAlarmed;
|
||||||
|
@ -527,6 +543,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
mDead = state.mDead;
|
mDead = state.mDead;
|
||||||
mDied = state.mDied;
|
mDied = state.mDied;
|
||||||
|
mMurdered = state.mMurdered;
|
||||||
mFriendlyHits = state.mFriendlyHits;
|
mFriendlyHits = state.mFriendlyHits;
|
||||||
mTalkedTo = state.mTalkedTo;
|
mTalkedTo = state.mTalkedTo;
|
||||||
mAlarmed = state.mAlarmed;
|
mAlarmed = state.mAlarmed;
|
||||||
|
|
|
@ -35,6 +35,7 @@ namespace MWMechanics
|
||||||
AiSequence mAiSequence;
|
AiSequence mAiSequence;
|
||||||
bool mDead;
|
bool mDead;
|
||||||
bool mDied;
|
bool mDied;
|
||||||
|
bool mMurdered;
|
||||||
int mFriendlyHits;
|
int mFriendlyHits;
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
bool mAlarmed;
|
bool mAlarmed;
|
||||||
|
@ -170,6 +171,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
void clearHasDied();
|
void clearHasDied();
|
||||||
|
|
||||||
|
bool hasBeenMurdered() const;
|
||||||
|
|
||||||
|
void clearHasBeenMurdered();
|
||||||
|
|
||||||
|
void notifyMurder();
|
||||||
|
|
||||||
void resurrect();
|
void resurrect();
|
||||||
|
|
||||||
bool hasCommonDisease() const;
|
bool hasCommonDisease() const;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/aicombat.hpp"
|
#include "../mwmechanics/aicombat.hpp"
|
||||||
|
#include "../mwmechanics/aipursue.hpp"
|
||||||
|
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
|
|
||||||
|
@ -836,7 +837,7 @@ namespace MWMechanics
|
||||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
// What amount of alarm did this crime generate?
|
// What amount of alarm did this crime generate?
|
||||||
int alarm;
|
int alarm = 0;
|
||||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
||||||
else if (type == OT_Pickpocket)
|
else if (type == OT_Pickpocket)
|
||||||
|
@ -847,17 +848,14 @@ namespace MWMechanics
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
||||||
else if (type == OT_Theft)
|
else if (type == OT_Theft)
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Innocent until proven guilty
|
|
||||||
bool reported = false;
|
bool reported = false;
|
||||||
|
|
||||||
// Find all the actors within the alarm radius
|
// Find all the actors within the alarm radius
|
||||||
std::vector<MWWorld::Ptr> neighbors;
|
std::vector<MWWorld::Ptr> neighbors;
|
||||||
|
|
||||||
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
|
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);
|
mActors.getObjectsInRange(from, radius, neighbors);
|
||||||
|
|
||||||
|
@ -865,32 +863,29 @@ namespace MWMechanics
|
||||||
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
||||||
neighbors.push_back(victim);
|
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)
|
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?
|
// 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)
|
if (type == OT_Theft)
|
||||||
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
|
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
|
// Crime reporting only applies to NPCs
|
||||||
if (!it->getClass().isNpc())
|
if (!it->getClass().isNpc())
|
||||||
continue;
|
continue;
|
||||||
|
@ -899,35 +894,25 @@ namespace MWMechanics
|
||||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
||||||
{
|
{
|
||||||
reported = true;
|
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)
|
if (reported)
|
||||||
reportCrime(player, victim, type, arg);
|
reportCrime(player, victim, type, arg);
|
||||||
|
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
||||||
|
startCombat(victim, player);
|
||||||
|
|
||||||
return reported;
|
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>();
|
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
|
// Bounty for each type of crime
|
||||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||||
arg = store.find("iCrimeTresspass")->getInt();
|
arg = store.find("iCrimeTresspass")->getInt();
|
||||||
|
@ -944,7 +929,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
|
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);
|
+ arg);
|
||||||
|
|
||||||
// If committing a crime against a faction member, expell from the faction
|
// If committing a crime against a faction member, expell from the faction
|
||||||
|
@ -953,9 +938,81 @@ namespace MWMechanics
|
||||||
std::string factionID;
|
std::string factionID;
|
||||||
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
||||||
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
|
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();
|
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 readRecord (ESM::ESMReader& reader, int32_t type);
|
||||||
|
|
||||||
virtual void clear();
|
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>();
|
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++)
|
for(size_t i = 0;i < ESM::Attribute::Length;i++)
|
||||||
{
|
{
|
||||||
mWerewolfAttributes[i] = getAttribute(i);
|
mWerewolfAttributes[i] = getAttribute(i);
|
||||||
|
@ -427,6 +429,11 @@ int MWMechanics::NpcStats::getWerewolfKills() const
|
||||||
return mWerewolfKills;
|
return mWerewolfKills;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWMechanics::NpcStats::addWerewolfKill()
|
||||||
|
{
|
||||||
|
++mWerewolfKills;
|
||||||
|
}
|
||||||
|
|
||||||
int MWMechanics::NpcStats::getProfit() const
|
int MWMechanics::NpcStats::getProfit() const
|
||||||
{
|
{
|
||||||
return mProfit;
|
return mProfit;
|
||||||
|
|
|
@ -126,6 +126,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
int getWerewolfKills() const;
|
int getWerewolfKills() const;
|
||||||
|
|
||||||
|
/// Increments mWerewolfKills by 1.
|
||||||
|
void addWerewolfKill();
|
||||||
|
|
||||||
float getTimeToStartDrowning() const;
|
float getTimeToStartDrowning() const;
|
||||||
/// Sets time left for the creature to drown if it stays underwater.
|
/// Sets time left for the creature to drown if it stays underwater.
|
||||||
/// @param time value from [0,20]
|
/// @param time value from [0,20]
|
||||||
|
|
|
@ -148,13 +148,17 @@ namespace MWMechanics
|
||||||
return school;
|
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 =
|
const ESM::MagicEffect *magicEffect =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||||
effectId);
|
effectId);
|
||||||
|
|
||||||
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects();
|
||||||
|
if (effects)
|
||||||
|
magicEffects = effects;
|
||||||
|
|
||||||
float resisted = 0;
|
float resisted = 0;
|
||||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||||
|
@ -165,9 +169,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
float resistance = 0;
|
float resistance = 0;
|
||||||
if (resistanceEffect != -1)
|
if (resistanceEffect != -1)
|
||||||
resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude;
|
resistance += magicEffects->get(resistanceEffect).mMagnitude;
|
||||||
if (weaknessEffect != -1)
|
if (weaknessEffect != -1)
|
||||||
resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude;
|
resistance -= magicEffects->get(weaknessEffect).mMagnitude;
|
||||||
|
|
||||||
|
|
||||||
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||||
|
@ -204,9 +208,10 @@ namespace MWMechanics
|
||||||
return resisted;
|
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)
|
if (resistance >= 0)
|
||||||
return 1 - resistance / 100.f;
|
return 1 - resistance / 100.f;
|
||||||
else
|
else
|
||||||
|
@ -261,6 +266,13 @@ namespace MWMechanics
|
||||||
bool firstAppliedEffect = true;
|
bool firstAppliedEffect = true;
|
||||||
bool anyHarmfulEffect = false;
|
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");
|
bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");
|
||||||
|
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||||
|
@ -328,7 +340,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try reflecting
|
// 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 reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude;
|
||||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
|
@ -346,8 +358,7 @@ namespace MWMechanics
|
||||||
// Try resisting
|
// Try resisting
|
||||||
if (magnitudeMult > 0 && target.getClass().isActor())
|
if (magnitudeMult > 0 && target.getClass().isActor())
|
||||||
{
|
{
|
||||||
|
magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects);
|
||||||
magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell);
|
|
||||||
if (magnitudeMult == 0)
|
if (magnitudeMult == 0)
|
||||||
{
|
{
|
||||||
// Fully resisted, show message
|
// Fully resisted, show message
|
||||||
|
@ -375,6 +386,8 @@ namespace MWMechanics
|
||||||
effect.mDuration = effectIt->mDuration;
|
effect.mDuration = effectIt->mDuration;
|
||||||
effect.mMagnitude = magnitude;
|
effect.mMagnitude = magnitude;
|
||||||
|
|
||||||
|
targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));
|
||||||
|
|
||||||
appliedLastingEffects.push_back(effect);
|
appliedLastingEffects.push_back(effect);
|
||||||
|
|
||||||
// For absorb effects, also apply the effect to the caster - but with a negative
|
// For absorb effects, also apply the effect to the caster - but with a negative
|
||||||
|
@ -452,14 +465,14 @@ namespace MWMechanics
|
||||||
if (!appliedLastingEffects.empty())
|
if (!appliedLastingEffects.empty())
|
||||||
{
|
{
|
||||||
int casterActorId = -1;
|
int casterActorId = -1;
|
||||||
if (caster.getClass().isActor())
|
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||||
mSourceName, casterActorId);
|
mSourceName, casterActorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the target actor they've been hit
|
// 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);
|
target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,7 +657,9 @@ namespace MWMechanics
|
||||||
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
||||||
if (!projectileModel.empty())
|
if (!projectileModel.empty())
|
||||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -729,8 +744,18 @@ namespace MWMechanics
|
||||||
float speed = 0;
|
float speed = 0;
|
||||||
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
|
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
|
||||||
if (!projectileModel.empty())
|
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,
|
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||||
false, spell->mEffects, mCaster, mSourceName);
|
false, spell->mEffects, mCaster, mSourceName, fallbackDirection);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace ESM
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
class EffectKey;
|
class EffectKey;
|
||||||
|
class MagicEffects;
|
||||||
|
|
||||||
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
|
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
|
||||||
|
|
||||||
|
@ -35,15 +36,19 @@ namespace MWMechanics
|
||||||
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
|
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
/// @return >=100 for fully resisted. can also return negative value for damage amplification.
|
/// @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
|
class CastSpell
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
MWWorld::Ptr mCaster;
|
MWWorld::Ptr mCaster; // May be empty
|
||||||
MWWorld::Ptr mTarget;
|
MWWorld::Ptr mTarget; // May be empty
|
||||||
public:
|
public:
|
||||||
bool mStack;
|
bool mStack;
|
||||||
std::string mId; // ID of spell, potion, item etc
|
std::string mId; // ID of spell, potion, item etc
|
||||||
|
@ -54,8 +59,13 @@ namespace MWMechanics
|
||||||
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
||||||
|
|
||||||
bool cast (const ESM::Spell* spell);
|
bool cast (const ESM::Spell* spell);
|
||||||
|
|
||||||
|
/// @note mCaster must be an actor
|
||||||
bool cast (const MWWorld::Ptr& item);
|
bool cast (const MWWorld::Ptr& item);
|
||||||
|
|
||||||
|
/// @note mCaster must be an NPC
|
||||||
bool cast (const ESM::Ingredient* ingredient);
|
bool cast (const ESM::Ingredient* ingredient);
|
||||||
|
|
||||||
bool cast (const ESM::Potion* potion);
|
bool cast (const ESM::Potion* potion);
|
||||||
|
|
||||||
/// @note Auto detects if spell, ingredient or 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);
|
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 =
|
Interpreter::Type_Integer value =
|
||||||
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
|
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
|
||||||
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
|
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
|
||||||
|
@ -392,9 +398,10 @@ namespace MWScript
|
||||||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
|
|
||||||
MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true);
|
MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true);
|
||||||
bool value = false;
|
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);
|
value = MWBase::Environment::get().getWorld()->getLOS(source,dest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor
|
||||||
op 0x2000246: ClearInfoActor, explicit
|
op 0x2000246: ClearInfoActor, explicit
|
||||||
op 0x2000247: BetaComment
|
op 0x2000247: BetaComment
|
||||||
op 0x2000248: BetaComment, explicit
|
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
|
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 either actor is in a non-active cell, return a large value (just like vanilla)
|
||||||
if (ref2.isEmpty())
|
if (ref2.isEmpty())
|
||||||
return std::numeric_limits<float>().max();
|
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())
|
if (ref.isEmpty())
|
||||||
return std::numeric_limits<float>().max();
|
return std::numeric_limits<float>().max();
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ namespace MWScript
|
||||||
virtual void stopScript (const std::string& name);
|
virtual void stopScript (const std::string& name);
|
||||||
|
|
||||||
virtual float getDistance (const std::string& name, const std::string& id = "") const;
|
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);
|
bool hasBeenActivated (const MWWorld::Ptr& ptr);
|
||||||
///< \attention Calling this function for the right reference will mark the action as
|
///< \attention Calling this function for the right reference will mark the action as
|
||||||
|
|
|
@ -587,7 +587,9 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
else
|
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>
|
template <class R>
|
||||||
class OpOnKnockout : public Interpreter::Opcode0
|
class OpOnKnockout : public Interpreter::Opcode0
|
||||||
{
|
{
|
||||||
|
@ -1264,6 +1285,8 @@ namespace MWScript
|
||||||
|
|
||||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
|
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::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);
|
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);
|
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 || 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)
|
if (mFormatCtx->pb != NULL)
|
||||||
{
|
{
|
||||||
av_free(mFormatCtx->pb->buffer);
|
if (mFormatCtx->pb->buffer != NULL)
|
||||||
mFormatCtx->pb->buffer = NULL;
|
{
|
||||||
}
|
av_free(mFormatCtx->pb->buffer);
|
||||||
av_free(mFormatCtx->pb);
|
mFormatCtx->pb->buffer = NULL;
|
||||||
mFormatCtx->pb = NULL;
|
}
|
||||||
|
av_free(mFormatCtx->pb);
|
||||||
|
mFormatCtx->pb = NULL;
|
||||||
|
}
|
||||||
|
avformat_free_context(mFormatCtx);
|
||||||
}
|
}
|
||||||
avformat_free_context(mFormatCtx);
|
|
||||||
mFormatCtx = NULL;
|
mFormatCtx = NULL;
|
||||||
fail("Failed to allocate input stream");
|
fail("Failed to allocate input stream");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,30 +6,36 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
ActionApply::ActionApply (const Ptr& target, const std::string& id)
|
ActionApply::ActionApply (const Ptr& object, const std::string& id)
|
||||||
: Action (false, target), mId (id)
|
: Action (false, object), mId (id)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void ActionApply::executeImp (const Ptr& actor)
|
void ActionApply::executeImp (const Ptr& actor)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->breakInvisibility(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)
|
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)
|
void ActionApplyWithSkill::executeImp (const Ptr& actor)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
|
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
|
||||||
|
|
||||||
if (getTarget().getClass().apply (getTarget(), mId, actor) && mUsageType!=-1)
|
if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1)
|
||||||
getTarget().getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);
|
actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);
|
||||||
|
|
||||||
|
actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace MWWorld
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActionApply (const Ptr& target, const std::string& id);
|
ActionApply (const Ptr& object, const std::string& id);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActionApplyWithSkill : public Action
|
class ActionApplyWithSkill : public Action
|
||||||
|
@ -29,7 +29,7 @@ namespace MWWorld
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActionApplyWithSkill (const Ptr& target, const std::string& id,
|
ActionApplyWithSkill (const Ptr& object, const std::string& id,
|
||||||
int skillIndex, int usageType);
|
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());
|
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
|
||||||
iter!=collection.mList.end(); ++iter)
|
iter!=collection.mList.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
if (iter->mData.getCount() == 0)
|
||||||
|
continue;
|
||||||
ESM::ObjectState state;
|
ESM::ObjectState state;
|
||||||
storeState (*iter, state);
|
storeState (*iter, state);
|
||||||
int slot = equipable ? getSlot (*iter) : -1;
|
int slot = equipable ? getSlot (*iter) : -1;
|
||||||
|
|
|
@ -57,22 +57,32 @@ namespace MWWorld
|
||||||
|
|
||||||
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
|
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
|
||||||
const std::string &spellId, float speed, bool stack,
|
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 = 0;
|
||||||
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
|
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;
|
pos.z += height;
|
||||||
|
|
||||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
Ogre::Quaternion orient;
|
||||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
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;
|
MagicBoltState state;
|
||||||
state.mSourceName = sourceName;
|
state.mSourceName = sourceName;
|
||||||
state.mId = model;
|
state.mId = model;
|
||||||
state.mSpellId = spellId;
|
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.mSpeed = speed;
|
||||||
state.mStack = stack;
|
state.mStack = stack;
|
||||||
state.mSoundId = sound;
|
state.mSoundId = sound;
|
||||||
|
@ -152,7 +162,9 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
|
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)
|
if (!obstacle.isEmpty() && obstacle == caster)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -39,9 +39,10 @@ namespace MWWorld
|
||||||
ProjectileManager (Ogre::SceneManager* sceneMgr,
|
ProjectileManager (Ogre::SceneManager* sceneMgr,
|
||||||
OEngine::Physic::PhysicEngine& engine);
|
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,
|
void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||||
float speed, bool stack, const ESM::EffectList& effects,
|
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,
|
void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||||
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||||
|
@ -64,9 +65,15 @@ namespace MWWorld
|
||||||
NifOgre::ObjectScenePtr mObject;
|
NifOgre::ObjectScenePtr mObject;
|
||||||
Ogre::SceneNode* mNode;
|
Ogre::SceneNode* mNode;
|
||||||
|
|
||||||
// Actor who shot this projectile
|
|
||||||
int mActorId;
|
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
|
// MW-id of this projectile
|
||||||
std::string mId;
|
std::string mId;
|
||||||
};
|
};
|
||||||
|
|
|
@ -989,9 +989,18 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!mWorldScene->isCellActive(*currCell))
|
if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell))
|
||||||
ptr.getClass().copyToCell(ptr, *newCell, pos);
|
{
|
||||||
else if (!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);
|
mWorldScene->removeObjectFromScene(ptr);
|
||||||
mLocalScripts.remove(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
|
return false; // cannot get LOS unless both NPC's are enabled
|
||||||
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents();
|
if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode())
|
||||||
const float* pos1 = npc.getRefData().getPosition().pos;
|
return false; // not in active cell
|
||||||
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents();
|
|
||||||
const float* pos2 = targetNpc.getRefData().getPosition().pos;
|
|
||||||
|
|
||||||
btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z);
|
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents();
|
||||||
btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z);
|
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);
|
std::pair<std::string, float> result = mPhysEngine->rayTest(from, to,false);
|
||||||
if(result.first == "") return true;
|
if(result.first == "") return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2320,9 +2333,9 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||||
float speed, bool stack, const ESM::EffectList& effects,
|
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
|
const std::vector<std::string>& World::getContentFiles() const
|
||||||
|
@ -2335,6 +2348,9 @@ namespace MWWorld
|
||||||
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility);
|
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility);
|
||||||
if (actor.getClass().hasInventoryStore(actor))
|
if (actor.getClass().hasInventoryStore(actor))
|
||||||
actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility);
|
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
|
bool World::isDark() const
|
||||||
|
@ -2436,14 +2452,15 @@ namespace MWWorld
|
||||||
if (!ptr.getRefData().isEnabled())
|
if (!ptr.getRefData().isEnabled())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Consider references inside containers as well
|
// Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers)
|
||||||
if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name())
|
if (mType != World::Detect_Creature &&
|
||||||
|
(ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()))
|
||||||
{
|
{
|
||||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||||
{
|
{
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
if (needToAdd(*it))
|
if (needToAdd(*it, mDetector))
|
||||||
{
|
{
|
||||||
mOut.push_back(ptr);
|
mOut.push_back(ptr);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2452,16 +2469,25 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needToAdd(ptr))
|
if (needToAdd(ptr, mDetector))
|
||||||
mOut.push_back(ptr);
|
mOut.push_back(ptr);
|
||||||
|
|
||||||
return true;
|
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())
|
if (mType == World::Detect_Creature)
|
||||||
return false;
|
{
|
||||||
|
// 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))
|
if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr))
|
||||||
return false;
|
return false;
|
||||||
if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty())
|
if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty())
|
||||||
|
@ -2593,6 +2619,8 @@ namespace MWWorld
|
||||||
int days = std::max(1, bounty / iDaysinPrisonMod);
|
int days = std::max(1, bounty / iDaysinPrisonMod);
|
||||||
|
|
||||||
advanceTime(days * 24);
|
advanceTime(days * 24);
|
||||||
|
for (int i=0; i<days*24; ++i)
|
||||||
|
MWBase::Environment::get().getMechanicsManager ()->rest (true);
|
||||||
|
|
||||||
std::set<int> skills;
|
std::set<int> skills;
|
||||||
for (int day=0; day<days; ++day)
|
for (int day=0; day<days; ++day)
|
||||||
|
@ -2636,8 +2664,6 @@ namespace MWWorld
|
||||||
message += "\n" + skillMsg;
|
message += "\n" + skillMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Sleep the player
|
|
||||||
|
|
||||||
std::vector<std::string> buttons;
|
std::vector<std::string> buttons;
|
||||||
buttons.push_back("#{sOk}");
|
buttons.push_back("#{sOk}");
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(message, buttons);
|
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);
|
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);
|
||||||
///< get all items in active cells owned by this Npc
|
///< 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)
|
///< get Line of Sight (morrowind stupid implementation)
|
||||||
|
|
||||||
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist);
|
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,
|
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||||
float speed, bool stack, const ESM::EffectList& effects,
|
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,
|
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
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.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
|
||||||
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
|
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
|
||||||
extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
|
extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
|
||||||
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting);
|
|
||||||
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting,
|
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting,
|
||||||
opcodeForceGreetingExplicit);
|
opcodeForceGreetingExplicit);
|
||||||
extensions.registerInstruction("goodbye", "", opcodeGoodbye);
|
extensions.registerInstruction("goodbye", "", opcodeGoodbye);
|
||||||
|
@ -442,7 +441,7 @@ namespace Compiler
|
||||||
|
|
||||||
extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace,
|
extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace,
|
||||||
opcodeGetRaceExplicit);
|
opcodeGetRaceExplicit);
|
||||||
extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills);
|
extensions.registerFunction ("getwerewolfkills", 'l', "", opcodeGetWerewolfKills);
|
||||||
extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit);
|
extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit);
|
||||||
extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit);
|
extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit);
|
||||||
extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit);
|
extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit);
|
||||||
|
@ -450,6 +449,7 @@ namespace Compiler
|
||||||
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
||||||
|
|
||||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
||||||
|
extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit);
|
||||||
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
|
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
|
||||||
|
|
||||||
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
||||||
|
|
|
@ -385,6 +385,8 @@ namespace Compiler
|
||||||
const int opcodeLowerRankExplicit = 0x20001eb;
|
const int opcodeLowerRankExplicit = 0x20001eb;
|
||||||
const int opcodeOnDeath = 0x20001fc;
|
const int opcodeOnDeath = 0x20001fc;
|
||||||
const int opcodeOnDeathExplicit = 0x2000205;
|
const int opcodeOnDeathExplicit = 0x2000205;
|
||||||
|
const int opcodeOnMurder = 0x2000249;
|
||||||
|
const int opcodeOnMurderExplicit = 0x200024a;
|
||||||
const int opcodeOnKnockout = 0x2000240;
|
const int opcodeOnKnockout = 0x2000240;
|
||||||
const int opcodeOnKnockoutExplicit = 0x2000241;
|
const int opcodeOnKnockoutExplicit = 0x2000241;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
||||||
mDied = false;
|
mDied = false;
|
||||||
esm.getHNOT (mDied, "DIED");
|
esm.getHNOT (mDied, "DIED");
|
||||||
|
|
||||||
|
mMurdered = false;
|
||||||
|
esm.getHNOT (mMurdered, "MURD");
|
||||||
|
|
||||||
mFriendlyHits = 0;
|
mFriendlyHits = 0;
|
||||||
esm.getHNOT (mFriendlyHits, "FRHT");
|
esm.getHNOT (mFriendlyHits, "FRHT");
|
||||||
|
|
||||||
|
@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
||||||
if (mDied)
|
if (mDied)
|
||||||
esm.writeHNT ("DIED", mDied);
|
esm.writeHNT ("DIED", mDied);
|
||||||
|
|
||||||
|
if (mMurdered)
|
||||||
|
esm.writeHNT ("MURD", mMurdered);
|
||||||
|
|
||||||
if (mFriendlyHits)
|
if (mFriendlyHits)
|
||||||
esm.writeHNT ("FRHT", mFriendlyHits);
|
esm.writeHNT ("FRHT", mFriendlyHits);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace ESM
|
||||||
|
|
||||||
bool mDead;
|
bool mDead;
|
||||||
bool mDied;
|
bool mDied;
|
||||||
|
bool mMurdered;
|
||||||
int mFriendlyHits;
|
int mFriendlyHits;
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
bool mAlarmed;
|
bool mAlarmed;
|
||||||
|
|
|
@ -37,6 +37,8 @@ void ESM::InventoryState::load (ESMReader &esm)
|
||||||
LightState state;
|
LightState state;
|
||||||
int slot;
|
int slot;
|
||||||
read (esm, state, slot);
|
read (esm, state, slot);
|
||||||
|
if (state.mCount == 0)
|
||||||
|
continue;
|
||||||
mLights.push_back (std::make_pair (state, slot));
|
mLights.push_back (std::make_pair (state, slot));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -44,6 +46,8 @@ void ESM::InventoryState::load (ESMReader &esm)
|
||||||
ObjectState state;
|
ObjectState state;
|
||||||
int slot;
|
int slot;
|
||||||
read (esm, state, slot);
|
read (esm, state, slot);
|
||||||
|
if (state.mCount == 0)
|
||||||
|
continue;
|
||||||
mItems.push_back (std::make_pair (state, std::make_pair (id, slot)));
|
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 mwToken = "?mw?";
|
||||||
const char* const localToken = "?local?";
|
const char* const localToken = "?local?";
|
||||||
const char* const userToken = "?user?";
|
const char* const userDataToken = "?userdata?";
|
||||||
const char* const globalToken = "?global?";
|
const char* const globalToken = "?global?";
|
||||||
|
|
||||||
ConfigurationManager::ConfigurationManager()
|
ConfigurationManager::ConfigurationManager()
|
||||||
|
@ -40,7 +40,7 @@ void ConfigurationManager::setupTokensMapping()
|
||||||
{
|
{
|
||||||
mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath));
|
mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath));
|
||||||
mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath));
|
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));
|
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::NiZBufferProperty *zprop,
|
||||||
const Nif::NiSpecularProperty *specprop,
|
const Nif::NiSpecularProperty *specprop,
|
||||||
const Nif::NiWireframeProperty *wireprop,
|
const Nif::NiWireframeProperty *wireprop,
|
||||||
bool &needTangents)
|
bool &needTangents, bool disableLighting)
|
||||||
{
|
{
|
||||||
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
|
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
|
||||||
Ogre::MaterialPtr material = matMgr.getByName(name);
|
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.
|
// Generate a hash out of all properties that can affect the material.
|
||||||
size_t h = 0;
|
size_t h = 0;
|
||||||
|
|
|
@ -49,7 +49,7 @@ public:
|
||||||
const Nif::NiZBufferProperty *zprop,
|
const Nif::NiZBufferProperty *zprop,
|
||||||
const Nif::NiSpecularProperty *specprop,
|
const Nif::NiSpecularProperty *specprop,
|
||||||
const Nif::NiWireframeProperty *wireprop,
|
const Nif::NiWireframeProperty *wireprop,
|
||||||
bool &needTangents);
|
bool &needTangents, bool disableLighting=false);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -850,7 +850,10 @@ class NIFObjectLoader
|
||||||
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
||||||
texprop, matprop, alphaprop,
|
texprop, matprop, alphaprop,
|
||||||
vertprop, zprop, specprop,
|
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->setCullIndividually(false);
|
||||||
partsys->setParticleQuota(particledata->numParticles);
|
partsys->setParticleQuota(particledata->numParticles);
|
||||||
|
|
|
@ -13,10 +13,13 @@
|
||||||
<Property key="FontName" value="MonoFont"/>
|
<Property key="FontName" value="MonoFont"/>
|
||||||
<Property key="TextAlign" value="Left Top"/>
|
<Property key="TextAlign" value="Left Top"/>
|
||||||
<Property key="TextColour" value="1 1 1"/>
|
<Property key="TextColour" value="1 1 1"/>
|
||||||
|
<Property key="InvertSelected" value="false"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<!-- Command line -->
|
<!-- 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>
|
</Widget>
|
||||||
</MyGUI>
|
</MyGUI>
|
||||||
|
|
|
@ -31,17 +31,18 @@
|
||||||
<UserString key="HStretch" value="false"/>
|
<UserString key="HStretch" value="false"/>
|
||||||
<UserString key="VStretch" value="false"/>
|
<UserString key="VStretch" value="false"/>
|
||||||
|
|
||||||
<Widget type="TextBox" skin="SandText" position="0 0 100 24" name="AttribMultiplier1"/>
|
<Widget type="TextBox" skin="SandTextVCenter" position="0 0 100 24" name="AttribMultiplier1"/>
|
||||||
<Widget type="TextBox" skin="SandText" position="0 24 100 24" name="AttribMultiplier2"/>
|
<Widget type="TextBox" skin="SandTextVCenter" position="0 24 100 24" name="AttribMultiplier2"/>
|
||||||
<Widget type="TextBox" skin="SandText" position="0 48 100 24" name="AttribMultiplier3"/>
|
<Widget type="TextBox" skin="SandTextVCenter" position="0 48 100 24" name="AttribMultiplier3"/>
|
||||||
<Widget type="TextBox" skin="SandText" position="0 72 100 24" name="AttribMultiplier4"/>
|
<Widget type="TextBox" skin="SandTextVCenter" position="0 72 100 24" name="AttribMultiplier4"/>
|
||||||
<Widget type="TextBox" skin="SandText" position="200 0 100 24" name="AttribMultiplier5"/>
|
<Widget type="TextBox" skin="SandTextVCenter" position="200 0 100 24" name="AttribMultiplier5"/>
|
||||||
<Widget type="TextBox" skin="SandText" position="200 24 100 24" name="AttribMultiplier6"/>
|
<Widget type="TextBox" skin="SandTextVCenter" position="200 24 100 24" name="AttribMultiplier6"/>
|
||||||
<Widget type="TextBox" skin="SandText" position="200 48 100 24" name="AttribMultiplier7"/>
|
<Widget type="TextBox" skin="SandTextVCenter" 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="200 72 100 24" name="AttribMultiplier8"/>
|
||||||
|
|
||||||
<Widget type="HBox" position="22 0 200 24">
|
<Widget type="HBox" position="22 0 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib1">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib1">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/>
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="22 24 200 24">
|
<Widget type="HBox" position="22 24 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib2">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib2">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/>
|
||||||
|
@ -68,6 +70,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="22 48 200 24">
|
<Widget type="HBox" position="22 48 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib3">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib3">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/>
|
||||||
|
@ -81,6 +84,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="22 72 200 24">
|
<Widget type="HBox" position="22 72 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib4">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib4">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/>
|
||||||
|
@ -95,6 +99,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="222 0 200 24">
|
<Widget type="HBox" position="222 0 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib5">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib5">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/>
|
||||||
|
@ -108,6 +113,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="222 24 200 24">
|
<Widget type="HBox" position="222 24 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib6">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib6">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/>
|
||||||
|
@ -121,6 +127,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="222 48 200 24">
|
<Widget type="HBox" position="222 48 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib7">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib7">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/>
|
||||||
|
@ -134,6 +141,7 @@
|
||||||
|
|
||||||
<Widget type="HBox" position="222 72 200 24">
|
<Widget type="HBox" position="222 72 200 24">
|
||||||
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib8">
|
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib8">
|
||||||
|
<UserString key="TextPadding" value="0 0"/>
|
||||||
<UserString key="ToolTipType" value="Layout"/>
|
<UserString key="ToolTipType" value="Layout"/>
|
||||||
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
|
||||||
<UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/>
|
<UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/>
|
||||||
|
|
|
@ -32,6 +32,13 @@
|
||||||
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch"/>
|
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch"/>
|
||||||
</Skin>
|
</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">
|
<Skin name="SandTextRight" size="16 16">
|
||||||
<Property key="FontName" value="Default"/>
|
<Property key="FontName" value="Default"/>
|
||||||
<Property key="TextAlign" value="Right Bottom"/>
|
<Property key="TextAlign" value="Right Bottom"/>
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
<Widget type="MWScrollBar" skin="MW_HScroll" name="HourSlider" position="0 0 0 18">
|
<Widget type="MWScrollBar" skin="MW_HScroll" name="HourSlider" position="0 0 0 18">
|
||||||
<Property key="MoveToClick" value="true"/>
|
<Property key="MoveToClick" value="true"/>
|
||||||
<Property key="Range" value="24"/>
|
<Property key="Range" value="24"/>
|
||||||
|
<Property key="Page" value="1"/>
|
||||||
|
<Property key="WheelPage" value="1"/>
|
||||||
<UserString key="HStretch" value="true"/>
|
<UserString key="HStretch" value="true"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
data="?global?data"
|
data="?global?data"
|
||||||
data="?mw?Data Files"
|
data="?mw?Data Files"
|
||||||
data-local="?user?data"
|
data-local="?userdata?data"
|
||||||
resources=${OPENMW_RESOURCE_FILES}
|
resources=${OPENMW_RESOURCE_FILES}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
data="?global?data"
|
data="?global?data"
|
||||||
data="?mw?Data Files"
|
data="?mw?Data Files"
|
||||||
data=./data
|
data=./data
|
||||||
data-local="?user?data"
|
data-local="?userdata?data"
|
||||||
resources=./resources
|
resources=./resources
|
||||||
|
|
Loading…
Reference in a new issue