Merge remote-tracking branch 'scrawl/master'

deque
Marc Zinnschlag 11 years ago
commit 30f8e279ed

@ -335,7 +335,7 @@ int main(int argc, char**argv)
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL);
cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL);
std::cout << "Installing crash catcher" << std::endl;
}
else

@ -120,6 +120,7 @@ namespace MWBase
/**
* @brief Commit a crime. If any actors witness the crime and report it,
* reportCrime will be called automatically.
* @note victim may be empty
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
* @return was the crime reported?
*/
@ -191,6 +192,10 @@ namespace MWBase
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
virtual void clear() = 0;
/// @param bias Can be used to add an additional aggression bias towards the target,
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0;
};
}

@ -407,7 +407,7 @@ namespace MWBase
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
///< get all items in active cells owned by this Npc
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0;
virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) = 0;
///< get Line of Sight (morrowind stupid implementation)
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
@ -470,7 +470,7 @@ namespace MWBase
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;

@ -286,7 +286,12 @@ namespace MWClass
{
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
ensureCustomData (ptr);
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<ContainerCustomData> data (new ContainerCustomData);
ptr.getRefData().setCustomData (data.release());
}
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory);

@ -59,35 +59,38 @@ namespace
namespace MWClass
{
const Creature::GMST& Creature::getGmst()
{
static GMST gmst;
static bool inited = false;
if (!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature");
gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature");
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
gmst.fSwimRunBase = store.find("fSwimRunBase");
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
gmst.fKnockDownMult = store.find("fKnockDownMult");
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
inited = true;
}
return gmst;
}
void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const
{
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
static bool inited = false;
if(!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature");
fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
inited = true;
}
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
// creature stats
@ -345,7 +348,7 @@ namespace MWClass
getCreatureStats(ptr).setAttacked(true);
// Self defense
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80
if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures
// (they have no movement or attacks anyway)
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
@ -376,9 +379,9 @@ namespace MWClass
if (damage > 0.f)
{
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
* getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
@ -522,9 +525,10 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat());
float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat());
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
@ -533,8 +537,8 @@ namespace MWClass
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
float runSpeed = walkSpeed*6;
runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
@ -544,8 +548,8 @@ namespace MWClass
{
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed;
}
@ -555,8 +559,8 @@ namespace MWClass
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
fSwimRunAthleticsMult->getFloat();
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
gmst.fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(running)
@ -798,7 +802,27 @@ namespace MWClass
{
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData (ptr);
ensureCustomData(ptr);
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
/*
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Weapon)
data->mContainerStore = new MWWorld::InventoryStore();
else
data->mContainerStore = new MWWorld::ContainerStore();
ptr.getRefData().setCustomData (data.release());
}
*/
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
@ -851,19 +875,4 @@ namespace MWClass
MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;
const ESM::GameSetting *Creature::fSneakSpeedMultiplier;
const ESM::GameSetting *Creature::fAthleticsRunBonus;
const ESM::GameSetting *Creature::fBaseRunMultiplier;
const ESM::GameSetting *Creature::fMinFlySpeed;
const ESM::GameSetting *Creature::fMaxFlySpeed;
const ESM::GameSetting *Creature::fSwimRunBase;
const ESM::GameSetting *Creature::fSwimRunAthleticsMult;
const ESM::GameSetting *Creature::fKnockDownMult;
const ESM::GameSetting *Creature::iKnockDownOddsMult;
const ESM::GameSetting *Creature::iKnockDownOddsBase;
}

@ -19,20 +19,25 @@ namespace MWClass
static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);
static const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature;
static const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
// cached GMSTs
struct GMST
{
const ESM::GameSetting *fMinWalkSpeedCreature;
const ESM::GameSetting *fMaxWalkSpeedCreature;
const ESM::GameSetting *fEncumberedMoveEffect;
const ESM::GameSetting *fSneakSpeedMultiplier;
const ESM::GameSetting *fAthleticsRunBonus;
const ESM::GameSetting *fBaseRunMultiplier;
const ESM::GameSetting *fMinFlySpeed;
const ESM::GameSetting *fMaxFlySpeed;
const ESM::GameSetting *fSwimRunBase;
const ESM::GameSetting *fSwimRunAthleticsMult;
const ESM::GameSetting *fKnockDownMult;
const ESM::GameSetting *iKnockDownOddsMult;
const ESM::GameSetting *iKnockDownOddsBase;
};
static const GMST& getGmst();
public:

@ -228,38 +228,44 @@ namespace
namespace MWClass
{
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
const Npc::GMST& Npc::getGmst()
{
static GMST gmst;
static bool inited = false;
if(!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeed = gmst.find("fMinWalkSpeed");
fMaxWalkSpeed = gmst.find("fMaxWalkSpeed");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase");
fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier");
fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase");
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
fWereWolfRunMult = gmst.find("fWereWolfRunMult");
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
fDamageStrengthBase = gmst.find("fDamageStrengthBase");
fDamageStrengthMult = gmst.find("fDamageStrengthMult");
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
gmst.fMinWalkSpeed = store.find("fMinWalkSpeed");
gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed");
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
gmst.fSwimRunBase = store.find("fSwimRunBase");
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase");
gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier");
gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase");
gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier");
gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier");
gmst.fWereWolfRunMult = store.find("fWereWolfRunMult");
gmst.fKnockDownMult = store.find("fKnockDownMult");
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
gmst.fDamageStrengthBase = store.find("fDamageStrengthBase");
gmst.fDamageStrengthMult = store.find("fDamageStrengthMult");
inited = true;
}
return gmst;
}
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
{
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<NpcCustomData> data(new NpcCustomData);
@ -404,11 +410,6 @@ namespace MWClass
ptr.get<ESM::NPC>();
assert(ref->mBase != NULL);
//std::string headID = ref->mBase->mHead;
//int end = headID.find_last_of("head_") - 4;
//std::string bodyRaceID = headID.substr(0, end);
std::string model = "meshes\\base_anim.nif";
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
if(race->mData.mFlags & ESM::Race::Beast)
@ -423,9 +424,9 @@ namespace MWClass
if(getNpcStats(ptr).isWerewolf())
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
return gmst.find("sWerewolfPopup")->getString();
return store.find("sWerewolfPopup")->getString();
}
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -450,7 +451,9 @@ namespace MWClass
void Npc::hit(const MWWorld::Ptr& ptr, int type) const
{
MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const GMST& gmst = getGmst();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
// Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
@ -461,9 +464,9 @@ namespace MWClass
// Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
@ -472,10 +475,10 @@ namespace MWClass
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
getCreatureStats(ptr).setFatigue(fatigue);
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
const float fCombatDistance = store.find("fCombatDistance")->getFloat();
float dist = fCombatDistance * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat());
store.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
@ -522,8 +525,8 @@ namespace MWClass
if(attack)
{
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1);
damage *= gmst.fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
if(weaphashealth)
{
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
@ -535,7 +538,7 @@ namespace MWClass
{
// Reduce weapon charge by at least one, but cap at 0
weaphealth -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth);
(int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
weapon.getCellRef().setCharge(weaphealth);
}
@ -552,8 +555,8 @@ namespace MWClass
// Note: MCP contains an option to include Strength in hand-to-hand damage
// calculations. Some mods recommend using it, so we may want to include am
// option for it.
float minstrike = gmst.find("fMinHandToHandMult")->getFloat();
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
float minstrike = store.find("fMinHandToHandMult")->getFloat();
float maxstrike = store.find("fMaxHandToHandMult")->getFloat();
damage = stats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
@ -567,7 +570,7 @@ namespace MWClass
damage *= glob.find("WerewolfClawMult")->mValue.getFloat();
}
if(healthdmg)
damage *= gmst.find("fHandtoHandHealthPer")->getFloat();
damage *= store.find("fHandtoHandHealthPer")->getFloat();
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(stats.isWerewolf())
@ -585,12 +588,12 @@ namespace MWClass
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
if(!detected)
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
damage *= store.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
if (othercls.getCreatureStats(victim).getKnockedDown())
damage *= gmst.find("fCombatKODamageMult")->getFloat();
damage *= store.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
@ -624,11 +627,12 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime
// anything below 80 is considered peaceful (see Actors::updateActor)
if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() &&
ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80)
!MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
bool wasDead = getCreatureStats(ptr).isDead();
getCreatureStats(ptr).setAttacked(true);
if(!successful)
@ -660,6 +664,8 @@ namespace MWClass
// something, alert the character controller, scripts, etc.
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const GMST& gmst = getGmst();
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (roll < chance)
@ -668,9 +674,9 @@ namespace MWClass
}
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
* gmst.iKnockDownOddsMult->getInt() * 0.01 + gmst.iKnockDownOddsBase->getInt();
roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
@ -752,6 +758,22 @@ namespace MWClass
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
getCreatureStats(ptr).setFatigue(fatigue);
}
if (!wasDead && getCreatureStats(ptr).isDead())
{
// NPC was killed
if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf())
{
attacker.getClass().getNpcStats(attacker).addWerewolfKill();
}
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
// for bystanders it is not possible to tell who attacked first, anyway.
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (attacker == player && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player)
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
}
}
void Npc::block(const MWWorld::Ptr &ptr) const
@ -853,6 +875,8 @@ namespace MWClass
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const GMST& gmst = getGmst();
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
@ -861,17 +885,17 @@ namespace MWClass
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= fSneakSpeedMultiplier->getFloat();
walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat();
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
if(npcdata->mNpcStats.isWerewolf())
runSpeed *= fWereWolfRunMult->getFloat();
runSpeed *= gmst.fWereWolfRunMult->getFloat();
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
@ -881,8 +905,8 @@ namespace MWClass
{
float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed;
}
@ -892,8 +916,8 @@ namespace MWClass
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
fSwimRunAthleticsMult->getFloat();
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
gmst.fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(running && !sneaking)
@ -909,9 +933,10 @@ namespace MWClass
float Npc::getJump(const MWWorld::Ptr &ptr) const
{
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
fJumpEncumbranceMultiplier->getFloat() *
const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() +
gmst.fJumpEncumbranceMultiplier->getFloat() *
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
@ -922,14 +947,14 @@ namespace MWClass
a = 50.0f;
}
float x = fJumpAcrobaticsBase->getFloat() +
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
x += 3.0f * b * fJumpAcroMultiplier->getFloat();
float x = gmst.fJumpAcrobaticsBase->getFloat() +
std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat());
x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat();
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
x *= encumbranceTerm;
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
x *= fJumpRunMultiplier->getFloat();
x *= gmst.fJumpRunMultiplier->getFloat();
x *= npcdata->mNpcStats.getFatigueTerm();
x -= -627.2f;/*gravity constant*/
x /= 3.0f;
@ -940,19 +965,19 @@ namespace MWClass
float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const
{
MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
const float fallDistanceMin = gmst.find("fFallDamageDistanceMin")->getFloat();
const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat();
if (fallHeight >= fallDistanceMin)
{
const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat();
const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat();
const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat();
const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat();
const float fallAcroBase = store.find("fFallAcroBase")->getFloat();
const float fallAcroMult = store.find("fFallAcroMult")->getFloat();
const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat();
const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat();
float x = fallHeight - fallDistanceMin;
x -= (1.5 * acrobaticsSkill) + jumpSpellBonus;
@ -1087,14 +1112,14 @@ namespace MWClass
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
MWMechanics::NpcStats &stats = getNpcStats(ptr);
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
int iBaseArmorSkill = store.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat();
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
int ratings[MWWorld::InventoryStore::Slots];
@ -1276,7 +1301,18 @@ namespace MWClass
{
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData (ptr);
ensureCustomData(ptr);
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
/*
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<NpcCustomData> data (new NpcCustomData);
ptr.getRefData().setCustomData (data.release());
}
*/
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
@ -1339,27 +1375,4 @@ namespace MWClass
MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
}
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
const ESM::GameSetting *Npc::fSneakSpeedMultiplier;
const ESM::GameSetting *Npc::fAthleticsRunBonus;
const ESM::GameSetting *Npc::fBaseRunMultiplier;
const ESM::GameSetting *Npc::fMinFlySpeed;
const ESM::GameSetting *Npc::fMaxFlySpeed;
const ESM::GameSetting *Npc::fSwimRunBase;
const ESM::GameSetting *Npc::fSwimRunAthleticsMult;
const ESM::GameSetting *Npc::fJumpEncumbranceBase;
const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier;
const ESM::GameSetting *Npc::fJumpAcrobaticsBase;
const ESM::GameSetting *Npc::fJumpAcroMultiplier;
const ESM::GameSetting *Npc::fJumpRunMultiplier;
const ESM::GameSetting *Npc::fWereWolfRunMult;
const ESM::GameSetting *Npc::fKnockDownMult;
const ESM::GameSetting *Npc::iKnockDownOddsMult;
const ESM::GameSetting *Npc::iKnockDownOddsBase;
const ESM::GameSetting *Npc::fDamageStrengthBase;
const ESM::GameSetting *Npc::fDamageStrengthMult;
}

@ -17,27 +17,32 @@ namespace MWClass
virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static const ESM::GameSetting *fMinWalkSpeed;
static const ESM::GameSetting *fMaxWalkSpeed;
static const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fJumpEncumbranceBase;
static const ESM::GameSetting *fJumpEncumbranceMultiplier;
static const ESM::GameSetting *fJumpAcrobaticsBase;
static const ESM::GameSetting *fJumpAcroMultiplier;
static const ESM::GameSetting *fJumpRunMultiplier;
static const ESM::GameSetting *fWereWolfRunMult;
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
static const ESM::GameSetting *fDamageStrengthBase;
static const ESM::GameSetting *fDamageStrengthMult;
struct GMST
{
const ESM::GameSetting *fMinWalkSpeed;
const ESM::GameSetting *fMaxWalkSpeed;
const ESM::GameSetting *fEncumberedMoveEffect;
const ESM::GameSetting *fSneakSpeedMultiplier;
const ESM::GameSetting *fAthleticsRunBonus;
const ESM::GameSetting *fBaseRunMultiplier;
const ESM::GameSetting *fMinFlySpeed;
const ESM::GameSetting *fMaxFlySpeed;
const ESM::GameSetting *fSwimRunBase;
const ESM::GameSetting *fSwimRunAthleticsMult;
const ESM::GameSetting *fJumpEncumbranceBase;
const ESM::GameSetting *fJumpEncumbranceMultiplier;
const ESM::GameSetting *fJumpAcrobaticsBase;
const ESM::GameSetting *fJumpAcroMultiplier;
const ESM::GameSetting *fJumpRunMultiplier;
const ESM::GameSetting *fWereWolfRunMult;
const ESM::GameSetting *fKnockDownMult;
const ESM::GameSetting *iKnockDownOddsMult;
const ESM::GameSetting *iKnockDownOddsBase;
const ESM::GameSetting *fDamageStrengthBase;
const ESM::GameSetting *fDamageStrengthMult;
};
static const GMST& getGmst();
public:

@ -166,13 +166,8 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Potion> *ref =
ptr.get<ESM::Potion>();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr();
// remove used potion (assume it is present in inventory)
ptr.getContainerStore()->remove(ptr, 1, actor);
boost::shared_ptr<MWWorld::Action> action (
new MWWorld::ActionApply (actor, ref->mBase->mId));
new MWWorld::ActionApply (ptr, ref->mBase->mId));
action->setSound ("Drink");

@ -530,7 +530,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_ShouldAttack:
return mActor.getClass().getCreatureStats(mActor).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() >= 80;
return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor,
MWBase::Environment::get().getWorld()->getPlayerPtr());
case SelectWrapper::Function_CreatureTargetted:

@ -384,6 +384,8 @@ namespace MWGui
void HUD::onFrame(float dt)
{
LocalMapBase::onFrame(dt);
mCellNameTimer -= dt;
mWeaponSpellTimer -= dt;
if (mCellNameTimer < 0)

@ -104,9 +104,10 @@ namespace MWGui
int attribute = mSpentAttributes[i];
int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30;
int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 20;
MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4);
MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0);
pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2;
image->setPosition(pos);
}

@ -36,6 +36,7 @@ namespace MWGui
, mLastDirectionX(0.0f)
, mLastDirectionY(0.0f)
, mCompass(NULL)
, mMarkerUpdateTimer(0.0f)
{
}
@ -345,6 +346,18 @@ namespace MWGui
markerWidget->setUserString("IsMarker", "true");
markerWidget->setUserData(markerPos);
markerWidget->setColour(markerColour);
mMarkerWidgets.push_back(markerWidget);
}
}
void LocalMapBase::onFrame(float dt)
{
mMarkerUpdateTimer += dt;
if (mMarkerUpdateTimer >= 0.25)
{
mMarkerUpdateTimer = 0;
updateMarkers();
}
}
@ -471,6 +484,8 @@ namespace MWGui
void MapWindow::onFrame(float dt)
{
LocalMapBase::onFrame(dt);
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
{
mGlobalMapRender->exploreCell(it->first, it->second);

@ -35,6 +35,8 @@ namespace MWGui
void setPlayerDir(const float x, const float y);
void setPlayerPos(const float x, const float y);
void onFrame(float dt);
bool toggleFogOfWar();
struct MarkerPosition
@ -73,12 +75,14 @@ namespace MWGui
virtual void notifyMapChanged() {}
// Update markers (Detect X effects, Mark/Recall effects)
// Note, door markers handled in setActiveCell
// Note, door markers are handled in setActiveCell
void updateMarkers();
void addDetectionMarkers(int type);
OEngine::GUI::Layout* mLayout;
float mMarkerUpdateTimer;
bool mMapDragAndDrop;
float mLastPositionX;

@ -488,14 +488,16 @@ namespace MWGui
text += "\n#BF9959#{sExpelled}";
else
{
text += std::string("\n#BF9959") + faction->mRanks[it->second];
int rank = it->second;
rank = std::max(0, std::min(9, rank));
text += std::string("\n#BF9959") + faction->mRanks[rank];
if (it->second < 9)
if (rank < 9)
{
// player doesn't have max rank yet
text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1];
text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[rank+1];
ESM::RankData rankData = faction->mData.mRankData[it->second+1];
ESM::RankData rankData = faction->mData.mRankData[rank+1];
const ESM::Attribute* attr1 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[0]);
const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]);

@ -233,13 +233,9 @@ namespace MWGui
for (std::map<std::string, std::string>::iterator it = userStrings.begin();
it != userStrings.end(); ++it)
{
if (it->first == "ToolTipType"
|| it->first == "ToolTipLayout"
|| it->first == "IsMarker")
continue;
size_t underscorePos = it->first.find("_");
if (underscorePos == std::string::npos)
continue;
std::string propertyKey = it->first.substr(0, underscorePos);
std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1));

@ -10,6 +10,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
@ -140,6 +141,9 @@ namespace MWGui
if (playerGold<price)
return;
if (!mPtr.getCell()->isExterior())
// Interior cell -> mages guild transport
MWBase::Environment::get().getSoundManager()->playSound("mysticism cast", 1, 1);
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);

@ -630,8 +630,11 @@ namespace MWGui
MyGUI::IntSize AutoSizedButton::getRequestedSize()
{
MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0);
size.height = std::max(24, size.height);
MyGUI::IntSize padding(24, 8);
if (isUserString("TextPadding"))
padding = MyGUI::IntSize::parse(getUserString("TextPadding"));
MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height);
return size;
}

@ -26,7 +26,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwdialogue/dialoguemanagerimp.hpp"
@ -826,6 +826,14 @@ namespace MWInput
void InputManager::quickKey (int index)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (player.getClass().getNpcStats(player).isWerewolf())
{
// Cannot use items or spells while in werewolf form
MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}");
return;
}
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWindowManager()->activateQuickKey (index);
}
@ -834,7 +842,18 @@ namespace MWInput
{
if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()
&& MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (player.getClass().getNpcStats(player).isWerewolf())
{
// Cannot use items or spells while in werewolf form
MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}");
return;
}
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu);
}
else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) {
while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows
MWBase::Environment::get().getWindowManager()->getCurrentModal()->exit();

@ -40,6 +40,10 @@ namespace MWMechanics
void readState (const ESM::ActiveSpells& state);
void writeState (ESM::ActiveSpells& state) const;
TIterator begin() const;
TIterator end() const;
private:
mutable TContainer mSpells;
@ -57,10 +61,6 @@ namespace MWMechanics
const TContainer& getActiveSpells() const;
TIterator begin() const;
TIterator end() const;
public:
ActiveSpells();

@ -201,32 +201,28 @@ namespace MWMechanics
|| (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target
return;
float fight;
bool aggressive;
if (againstPlayer)
fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified();
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
else
{
fight = 0;
aggressive = false;
// if one of actors is creature then we should make a decision to start combat or not
// NOTE: function doesn't take into account combat between 2 creatures
if (!actor1.getClass().isNpc())
{
// if creature is hostile then it is necessarily to start combat
if (creatureStats.isHostile()) fight = 100;
else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified();
if (creatureStats.isHostile()) aggressive = true;
else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
}
}
ESM::Position actor1Pos = actor1.getRefData().getPosition();
ESM::Position actor2Pos = actor2.getRefData().getPosition();
float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos));
if( (fight == 100 && d <= 5000)
|| (fight >= 95 && d <= 3000)
|| (fight >= 90 && d <= 2000)
|| (fight >= 80 && d <= 1000))
if(aggressive)
{
const ESM::Position& actor1Pos = actor2.getRefData().getPosition();
const ESM::Position& actor2Pos = actor2.getRefData().getPosition();
float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos));
if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d))
{
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
@ -346,6 +342,8 @@ namespace MWMechanics
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
const MagicEffects &effects = creatureStats.getMagicEffects();
bool wasDead = creatureStats.isDead();
// attributes
for(int i = 0;i < ESM::Attribute::Length;++i)
{
@ -458,6 +456,50 @@ namespace MWMechanics
}
creatureStats.setHealth(health);
if (!wasDead && creatureStats.isDead())
{
// The actor was killed by a magic effect. Figure out if the player was responsible for it.
const ActiveSpells& spells = creatureStats.getActiveSpells();
bool killedByPlayer = false;
bool murderedByPlayer = false;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
const ActiveSpells::ActiveSpellParams& spell = it->second;
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
effectIt != spell.mEffects.end(); ++effectIt)
{
int effectId = effectIt->mEffectId;
bool isDamageEffect = false;
for (unsigned int i=0; i<sizeof(damageEffects)/sizeof(int); ++i)
{
if (damageEffects[i] == effectId)
isDamageEffect = true;
}
if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::AbsorbHealth)
isDamageEffect = true;
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
if (isDamageEffect && caster == player)
{
killedByPlayer = true;
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
// for bystanders it is not possible to tell who attacked first, anyway.
if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1
&& ptr != player)
murderedByPlayer = true;
}
}
}
if (murderedByPlayer)
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
if (killedByPlayer && player.getClass().getNpcStats(player).isWerewolf())
player.getClass().getNpcStats(player).addWerewolfKill();
}
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
// Update bound effects
@ -793,7 +835,6 @@ namespace MWMechanics
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
{
/// \todo Move me! I shouldn't be here...
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
// Force dialogue on sight if bounty is greater than the cutoff
@ -821,7 +862,6 @@ namespace MWMechanics
creatureStats.getAiSequence().stopCombat();
// Reset factors to attack
// TODO: Not a complete list, disposition changes?
creatureStats.setHostile(false);
creatureStats.setAttacked(false);
creatureStats.setAlarmed(false);
@ -829,15 +869,6 @@ namespace MWMechanics
// Update witness crime id
npcStats.setCrimeId(-1);
}
else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue)
{
if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
else
{
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
}
}
}
}
}
@ -1089,6 +1120,9 @@ namespace MWMechanics
isBattleMusic = true;
}
static float sneakTimer = 0.f; // times update of sneak icon
static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice"
// if player is in sneak state see if anyone detects him
if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak))
{
@ -1096,27 +1130,54 @@ namespace MWMechanics
const int radius = esmStore.get<ESM::GameSetting>().find("fSneakUseDist")->getInt();
bool detected = false;
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
static float fSneakUseDelay = esmStore.get<ESM::GameSetting>().find("fSneakUseDelay")->getFloat();
if (sneakTimer >= fSneakUseDelay)
sneakTimer = 0.f;
if (sneakTimer == 0.f)
{
if (iter->first == player) // not the player
continue;
// Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress.
bool avoidedNotice = false;
// is the player in range and can they be detected
if ( (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius)
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first)
&& MWBase::Environment::get().getWorld()->getLOS(player, iter->first))
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
detected = true;
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
break;
if (iter->first == player) // not the player
continue;
// is the player in range and can they be detected
if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius
&& MWBase::Environment::get().getWorld()->getLOS(player, iter->first))
{
if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first))
{
detected = true;
avoidedNotice = false;
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
break;
}
else if (!detected)
avoidedNotice = true;
}
}
}
if (!detected)
MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);
if (sneakSkillTimer >= fSneakUseDelay)
sneakSkillTimer = 0.f;
if (avoidedNotice && sneakSkillTimer == 0.f)
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0);
if (!detected)
MWBase::Environment::get().getWindowManager()->setSneakVisibility(true);
}
sneakTimer += duration;
sneakSkillTimer += duration;
}
else
{
sneakTimer = 0.f;
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
}
}
}
void Actors::restoreDynamicStats(bool sleep)

@ -470,6 +470,8 @@ namespace MWMechanics
if(preferShortcut)
{
if (canMoveByZ)
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
mForceNoShortcut = false;
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;

@ -221,6 +221,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
{
return; // target is already pursued
}
if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat
&& static_cast<const AiCombat*>(*iter)->getTarget() == static_cast<const AiCombat*>(&package)->getTarget())
{
return; // already in combat with this actor
}
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
}

@ -409,13 +409,6 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
void CharacterController::playDeath(float startpoint, CharacterState death)
{
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
{
// The first-person animations do not include death, so we need to
// force-switch to third person before playing the death animation.
MWBase::Environment::get().getWorld()->useDeathCamera();
}
switch (death)
{
case CharState_SwimDeath:
@ -459,6 +452,13 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
void CharacterController::playRandomDeath(float startpoint)
{
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
{
// The first-person animations do not include death, so we need to
// force-switch to third person before playing the death animation.
MWBase::Environment::get().getWorld()->useDeathCamera();
}
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath"))
{
mDeathState = CharState_SwimDeath;

@ -14,7 +14,7 @@ namespace MWMechanics
int CreatureStats::sActorId = 0;
CreatureStats::CreatureStats()
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
: mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
mTalkedTo (false), mAlarmed (false),
mAttacked (false), mHostile (false),
mAttackingOrSpell(false),
@ -245,6 +245,21 @@ namespace MWMechanics
mDied = false;
}
bool CreatureStats::hasBeenMurdered() const
{
return mMurdered;
}
void CreatureStats::notifyMurder()
{
mMurdered = true;
}
void CreatureStats::clearHasBeenMurdered()
{
mMurdered = false;
}
void CreatureStats::resurrect()
{
if (mDead)
@ -479,6 +494,7 @@ namespace MWMechanics
state.mDead = mDead;
state.mDied = mDied;
state.mMurdered = mMurdered;
state.mFriendlyHits = mFriendlyHits;
state.mTalkedTo = mTalkedTo;
state.mAlarmed = mAlarmed;
@ -527,6 +543,7 @@ namespace MWMechanics
mDead = state.mDead;
mDied = state.mDied;
mMurdered = state.mMurdered;
mFriendlyHits = state.mFriendlyHits;
mTalkedTo = state.mTalkedTo;
mAlarmed = state.mAlarmed;

@ -35,6 +35,7 @@ namespace MWMechanics
AiSequence mAiSequence;
bool mDead;
bool mDied;
bool mMurdered;
int mFriendlyHits;
bool mTalkedTo;
bool mAlarmed;
@ -170,6 +171,12 @@ namespace MWMechanics
void clearHasDied();
bool hasBeenMurdered() const;
void clearHasBeenMurdered();
void notifyMurder();
void resurrect();
bool hasCommonDisease() const;

@ -14,6 +14,7 @@
#include "../mwworld/player.hpp"
#include "../mwmechanics/aicombat.hpp"
#include "../mwmechanics/aipursue.hpp"
#include <OgreSceneNode.h>
@ -836,7 +837,7 @@ namespace MWMechanics
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
// What amount of alarm did this crime generate?
int alarm;
int alarm = 0;
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
else if (type == OT_Pickpocket)
@ -847,17 +848,14 @@ namespace MWMechanics
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
else if (type == OT_Theft)
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
else
return false;
// Innocent until proven guilty
bool reported = false;
// Find all the actors within the alarm radius
std::vector<MWWorld::Ptr> neighbors;
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
int radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getInt();
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
mActors.getObjectsInRange(from, radius, neighbors);
@ -865,32 +863,29 @@ namespace MWMechanics
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
neighbors.push_back(victim);
int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();
bool victimAware = false;
// Find actors who witnessed the crime
// Find actors who directly witnessed the crime
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
{
if (*it == player) continue; // not the player
if (*it == player)
continue; // skip player
if (it->getClass().getCreatureStats(*it).isDead())
continue;
// Was the crime seen?
if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
// Murder crime can be reported even if no one saw it (hearing is enough, I guess).
// TODO: Add mod support for stealth executions!
|| (type == OT_Murder && *it != victim))
{
// TODO: Add more messages
if (*it == victim)
victimAware = true;
// TODO: are there other messages?
if (type == OT_Theft)
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
if (*it == victim)
{
// Self-defense
// The victim is aware of the criminal/assailant. If being assaulted, fight back now
// (regardless of whether the assault is reported or not)
// This applies to both NPCs and creatures
// ... except if this is a guard: then the player is given a chance to pay a fine / go to jail instead
if (type == OT_Assault && !it->getClass().isClass(*it, "guard"))
MWBase::Environment::get().getMechanicsManager()->startCombat(victim, player);
}
// Crime reporting only applies to NPCs
if (!it->getClass().isNpc())
continue;
@ -899,35 +894,25 @@ namespace MWMechanics
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
{
reported = true;
// Tell everyone, including yourself
for (std::vector<MWWorld::Ptr>::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1)
{
if ( *it1 == player
|| !it1->getClass().isNpc()) continue; // not the player and is an NPC
// Will other witnesses paticipate in crime
if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm
|| type == OT_Assault )
{
it1->getClass().getNpcStats(*it1).setCrimeId(id);
}
// Mark as Alarmed for dialogue
it1->getClass().getCreatureStats(*it1).setAlarmed(true);
}
}
}
}
if (reported)
reportCrime(player, victim, type, arg);
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
startCombat(victim, player);
return reported;
}
void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg)
void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
{
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (type == OT_Murder && !victim.isEmpty())
victim.getClass().getCreatureStats(victim).notifyMurder();
// Bounty for each type of crime
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
arg = store.find("iCrimeTresspass")->getInt();
@ -944,7 +929,7 @@ namespace MWMechanics
}
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()
player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()
+ arg);
// If committing a crime against a faction member, expell from the faction
@ -953,9 +938,81 @@ namespace MWMechanics
std::string factionID;
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
if (ptr.getClass().getNpcStats(ptr).isSameFaction(victim.getClass().getNpcStats(victim)))
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
{
ptr.getClass().getNpcStats(ptr).expell(factionID);
player.getClass().getNpcStats(player).expell(factionID);
}
}
// Make surrounding actors within alarm distance respond to the crime
std::vector<MWWorld::Ptr> neighbors;
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
mActors.getObjectsInRange(from, radius, neighbors);
// victim should be considered even beyond alarm radius
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
neighbors.push_back(victim);
int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();
// What amount of provocation did this crime generate?
// Controls whether witnesses will engage combat with the criminal.
int fight = 0;
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
fight = esmStore.get<ESM::GameSetting>().find("iFightTrespass")->getInt();
else if (type == OT_Pickpocket)
fight = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->getInt();
else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses?
fight = esmStore.get<ESM::GameSetting>().find("iFightAttack")->getInt();
else if (type == OT_Murder)
fight = esmStore.get<ESM::GameSetting>().find("iFightKilling")->getInt();
else if (type == OT_Theft)
fight = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getFloat();
const int iFightAttacking = esmStore.get<ESM::GameSetting>().find("iFightAttacking")->getInt();
// Tell everyone (including the original reporter) in alarm range
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
{
if ( *it == player
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
int aggression = fight;
// Note: iFightAttack is used for the victim, iFightAttacking for witnesses?
if (*it != victim && type == OT_Assault)
aggression = iFightAttacking;
if (it->getClass().isClass(*it, "guard"))
{
// Mark as Alarmed for dialogue
it->getClass().getCreatureStats(*it).setAlarmed(true);
// Set the crime ID, which we will use to calm down participants
// once the bounty has been paid.
it->getClass().getNpcStats(*it).setCrimeId(id);
it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it);
}
else
{
bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true);
if (aggressive)
{
startCombat(*it, player);
// Set the crime ID, which we will use to calm down participants
// once the bounty has been paid.
it->getClass().getNpcStats(*it).setCrimeId(id);
// Mark as Alarmed for dialogue
it->getClass().getCreatureStats(*it).setAlarmed(true);
}
}
}
}
@ -1098,4 +1155,32 @@ namespace MWMechanics
{
mActors.clear();
}
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias, bool ignoreDistance)
{
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
Ogre::Vector3 pos2 (target.getRefData().getPosition().pos);
float d = 0;
if (!ignoreDistance)
d = pos1.distance(pos2);
static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->getInt();
static float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDistanceMultiplier")->getFloat();
static float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDispMult")->getFloat();
int disposition = 50;
if (ptr.getClass().isNpc())
disposition = getDerivedDisposition(ptr);
int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
+ (iFightDistanceBase - fFightDistanceMultiplier * d)
+ ((50 - disposition) * fFightDispMult))
+ bias;
return (fight >= 100);
}
}

@ -156,6 +156,10 @@ namespace MWMechanics
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
virtual void clear();
/// @param bias Can be used to add an additional aggression bias towards the target,
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false);
};
}

@ -396,6 +396,8 @@ void MWMechanics::NpcStats::setWerewolf (bool set)
{
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
mWerewolfKills = 0;
for(size_t i = 0;i < ESM::Attribute::Length;i++)
{
mWerewolfAttributes[i] = getAttribute(i);
@ -427,6 +429,11 @@ int MWMechanics::NpcStats::getWerewolfKills() const
return mWerewolfKills;
}
void MWMechanics::NpcStats::addWerewolfKill()
{
++mWerewolfKills;
}
int MWMechanics::NpcStats::getProfit() const
{
return mProfit;

@ -126,6 +126,9 @@ namespace MWMechanics
int getWerewolfKills() const;
/// Increments mWerewolfKills by 1.
void addWerewolfKill();
float getTimeToStartDrowning() const;
/// Sets time left for the creature to drown if it stays underwater.
/// @param time value from [0,20]

@ -148,13 +148,17 @@ namespace MWMechanics
return school;
}
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell)
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell, const MagicEffects* effects)
{
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effectId);
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects();
if (effects)
magicEffects = effects;
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
@ -165,9 +169,9 @@ namespace MWMechanics
float resistance = 0;
if (resistanceEffect != -1)
resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude;
resistance += magicEffects->get(resistanceEffect).mMagnitude;
if (weaknessEffect != -1)
resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude;
resistance -= magicEffects->get(weaknessEffect).mMagnitude;
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
@ -204,9 +208,10 @@ namespace MWMechanics
return resisted;
}
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell)
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell, const MagicEffects* effects)
{
float resistance = getEffectResistance(effectId, actor, caster, spell);
float resistance = getEffectResistance(effectId, actor, caster, spell, effects);
if (resistance >= 0)
return 1 - resistance / 100.f;
else
@ -261,6 +266,13 @@ namespace MWMechanics
bool firstAppliedEffect = true;
bool anyHarmfulEffect = false;
// HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance.
// This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.
// Otherwise, they'd only apply after the whole spell was added.
MagicEffects targetEffects;
if (target.getClass().isActor())
targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();
bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
@ -328,7 +340,7 @@ namespace MWMechanics
}
// Try reflecting
if (!reflected && magnitudeMult > 0 && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
{
int reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude;
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
@ -346,8 +358,7 @@ namespace MWMechanics
// Try resisting
if (magnitudeMult > 0 && target.getClass().isActor())
{
magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell);
magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects);
if (magnitudeMult == 0)
{
// Fully resisted, show message
@ -375,6 +386,8 @@ namespace MWMechanics
effect.mDuration = effectIt->mDuration;
effect.mMagnitude = magnitude;
targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));
appliedLastingEffects.push_back(effect);
// For absorb effects, also apply the effect to the caster - but with a negative
@ -452,14 +465,14 @@ namespace MWMechanics
if (!appliedLastingEffects.empty())
{
int casterActorId = -1;
if (caster.getClass().isActor())
if (!caster.isEmpty() && caster.getClass().isActor())
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
mSourceName, casterActorId);
}
// Notify the target actor they've been hit
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor())
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor())
target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
}
@ -644,7 +657,9 @@ namespace MWMechanics
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty())
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
false, enchantment->mEffects, mCaster, mSourceName);
false, enchantment->mEffects, mCaster, mSourceName,
// Not needed, enchantments can only be cast by actors
Ogre::Vector3(1,0,0));
return true;
}
@ -729,8 +744,18 @@ namespace MWMechanics
float speed = 0;
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty())
{
Ogre::Vector3 fallbackDirection (0,1,0);
// Fall back to a "caster to target" direction if we have no other means of determining it
// (e.g. when cast by a non-actor)
if (!mTarget.isEmpty())
fallbackDirection =
Ogre::Vector3(mTarget.getRefData().getPosition().pos)-
Ogre::Vector3(mCaster.getRefData().getPosition().pos);
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
false, spell->mEffects, mCaster, mSourceName);
false, spell->mEffects, mCaster, mSourceName, fallbackDirection);
}
return true;
}

@ -18,6 +18,7 @@ namespace ESM
namespace MWMechanics
{
class EffectKey;
class MagicEffects;
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
@ -35,15 +36,19 @@ namespace MWMechanics
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
/// @return >=100 for fully resisted. can also return negative value for damage amplification.
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL);
/// @param effects Override the actor's current magicEffects. Useful if there are effects currently
/// being applied (but not applied yet) that should also be considered.
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL);
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
class CastSpell
{
private:
MWWorld::Ptr mCaster;
MWWorld::Ptr mTarget;
MWWorld::Ptr mCaster; // May be empty
MWWorld::Ptr mTarget; // May be empty
public:
bool mStack;
std::string mId; // ID of spell, potion, item etc
@ -54,8 +59,13 @@ namespace MWMechanics
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
bool cast (const ESM::Spell* spell);
/// @note mCaster must be an actor
bool cast (const MWWorld::Ptr& item);
/// @note mCaster must be an NPC
bool cast (const ESM::Ingredient* ingredient);
bool cast (const ESM::Potion* potion);
/// @note Auto detects if spell, ingredient or potion

@ -371,6 +371,12 @@ namespace MWScript
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
if(!actor.getClass().isActor() || !observer.getClass().isActor())
{
runtime.push(0);
return;
}
Interpreter::Type_Integer value =
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
@ -392,9 +398,10 @@ namespace MWScript
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true);
bool value = false;
if(dest != MWWorld::Ptr() )
if(dest != MWWorld::Ptr() && source.getClass().isActor() && dest.getClass().isActor())
{
value = MWBase::Environment::get().getWorld()->getLOS(source,dest);
}

@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor
op 0x2000246: ClearInfoActor, explicit
op 0x2000247: BetaComment
op 0x2000248: BetaComment, explicit
op 0x2000249: OnMurder
op 0x200024a: OnMurder, explicit
opcodes 0x2000249-0x3ffffff unused
opcodes 0x200024b-0x3ffffff unused

@ -378,12 +378,20 @@ namespace MWScript
float InterpreterContext::getDistance (const std::string& name, const std::string& id) const
{
const MWWorld::Ptr ref2 = getReference (id, false, false);
// NOTE: id may be empty, indicating an implicit reference
MWWorld::Ptr ref2;
if (id.empty())
ref2 = getReference("", true, true);
else
ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true);
// If either actor is in a non-active cell, return a large value (just like vanilla)
if (ref2.isEmpty())
return std::numeric_limits<float>().max();
const MWWorld::Ptr ref = getReference (name, false, false);
const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->searchPtr(name, true);
if (ref.isEmpty())
return std::numeric_limits<float>().max();

@ -118,6 +118,7 @@ namespace MWScript
virtual void stopScript (const std::string& name);
virtual float getDistance (const std::string& name, const std::string& id = "") const;
///< @note if \a id is empty, assumes an implicit reference
bool hasBeenActivated (const MWWorld::Ptr& ptr);
///< \attention Calling this function for the right reference will mark the action as

@ -587,7 +587,9 @@ namespace MWScript
}
else
{
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1;
player.getClass().getNpcStats(player).getFactionRanks()[factionID] =
std::min(player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1,
9);
}
}
}
@ -1072,6 +1074,25 @@ namespace MWScript
}
};
template <class R>
class OpOnMurder : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value =
ptr.getClass().getCreatureStats (ptr).hasBeenMurdered();
if (value)
ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered();
runtime.push (value);
}
};
template <class R>
class OpOnKnockout : public Interpreter::Opcode0
{
@ -1264,6 +1285,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder<ImplicitRef>);
interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder<ExplicitRef>);
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);

@ -159,17 +159,21 @@ void FFmpeg_Decoder::open(const std::string &fname)
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
{
if (mFormatCtx->pb != NULL)
// "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
{
if (mFormatCtx->pb->buffer != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
if (mFormatCtx->pb != NULL)
{
if (mFormatCtx->pb->buffer != NULL)
{
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
}
avformat_free_context(mFormatCtx);
}
avformat_free_context(mFormatCtx);
mFormatCtx = NULL;
fail("Failed to allocate input stream");
}

@ -6,30 +6,36 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/containerstore.hpp"
namespace MWWorld
{
ActionApply::ActionApply (const Ptr& target, const std::string& id)
: Action (false, target), mId (id)
ActionApply::ActionApply (const Ptr& object, const std::string& id)
: Action (false, object), mId (id)
{}
void ActionApply::executeImp (const Ptr& actor)
{
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
getTarget().getClass().apply (getTarget(), mId, actor);
actor.getClass().apply (actor, mId, actor);
actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);
}
ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id,
ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& object, const std::string& id,
int skillIndex, int usageType)
: Action (false, target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType)
: Action (false, object), mId (id), mSkillIndex (skillIndex), mUsageType (usageType)
{}
void ActionApplyWithSkill::executeImp (const Ptr& actor)
{
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
if (getTarget().getClass().apply (getTarget(), mId, actor) && mUsageType!=-1)
getTarget().getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);
if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1)
actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType);
actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor);
}
}

@ -16,7 +16,7 @@ namespace MWWorld
public:
ActionApply (const Ptr& target, const std::string& id);
ActionApply (const Ptr& object, const std::string& id);
};
class ActionApplyWithSkill : public Action
@ -29,7 +29,7 @@ namespace MWWorld
public:
ActionApplyWithSkill (const Ptr& target, const std::string& id,
ActionApplyWithSkill (const Ptr& object, const std::string& id,
int skillIndex, int usageType);
};
}

@ -92,6 +92,8 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
{
if (iter->mData.getCount() == 0)
continue;
ESM::ObjectState state;
storeState (*iter, state);
int slot = equipable ? getSlot (*iter) : -1;

@ -57,22 +57,32 @@ namespace MWWorld
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
const std::string &spellId, float speed, bool stack,
const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName)
const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName,
const Ogre::Vector3& fallbackDirection)
{
// Spawn at 0.75 * ActorHeight
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
float height = 0;
if (OEngine::Physic::PhysicActor* actor = mPhysEngine.getCharacter(caster.getRefData().getHandle()))
height = actor->getHalfExtents().z * 2 * 0.75; // Spawn at 0.75 * ActorHeight
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
Ogre::Vector3 pos(caster.getRefData().getPosition().pos);
pos.z += height;
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
Ogre::Quaternion orient;
if (caster.getClass().isActor())
orient = Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
else
orient = Ogre::Vector3::UNIT_Y.getRotationTo(fallbackDirection);
MagicBoltState state;
state.mSourceName = sourceName;
state.mId = model;
state.mSpellId = spellId;
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mCaster = caster;
if (caster.getClass().isActor())
state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();
else
state.mActorId = -1;
state.mSpeed = speed;
state.mStack = stack;
state.mSoundId = sound;
@ -152,7 +162,9 @@ namespace MWWorld
{
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
MWWorld::Ptr caster = it->mCaster;
if (caster.isEmpty())
caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
if (!obstacle.isEmpty() && obstacle == caster)
continue;

@ -39,9 +39,10 @@ namespace MWWorld
ProjectileManager (Ogre::SceneManager* sceneMgr,
OEngine::Physic::PhysicEngine& engine);
/// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used.
void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName);
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection);
void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
@ -64,9 +65,15 @@ namespace MWWorld
NifOgre::ObjectScenePtr mObject;
Ogre::SceneNode* mNode;
// Actor who shot this projectile
int mActorId;
// actorId doesn't work for non-actors, so we also keep track of the Ptr.
// For non-actors, the caster ptr is mainly needed to prevent the projectile
// from colliding with its caster.
// TODO: this will break when the game is saved and reloaded, since there is currently
// no way to write identifiers for non-actors to a savegame.
MWWorld::Ptr mCaster;
// MW-id of this projectile
std::string mId;
};

@ -989,9 +989,18 @@ namespace MWWorld
}
else
{
if (!mWorldScene->isCellActive(*currCell))
ptr.getClass().copyToCell(ptr, *newCell, pos);
else if (!mWorldScene->isCellActive(*newCell))
if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell))
{
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos);
mWorldScene->addObjectToScene(newPtr);
std::string script = newPtr.getClass().getScript(newPtr);
if (!script.empty()) {
mLocalScripts.add(script, newPtr);
}
addContainerScripts(newPtr, newCell);
}
else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell))
{
mWorldScene->removeObjectFromScene(ptr);
mLocalScripts.remove(ptr);
@ -2022,20 +2031,24 @@ namespace MWWorld
}
}
bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc)
bool World::getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor)
{
if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled())
if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled())
return false; // cannot get LOS unless both NPC's are enabled
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents();
const float* pos1 = npc.getRefData().getPosition().pos;
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents();
const float* pos2 = targetNpc.getRefData().getPosition().pos;
if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode())
return false; // not in active cell
btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z);
btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z);
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents();
const float* pos1 = actor.getRefData().getPosition().pos;
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle())->getHalfExtents();
const float* pos2 = targetActor.getRefData().getPosition().pos;
btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9); // eye level
btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z*2*0.9);
std::pair<std::string, float> result = mPhysEngine->rayTest(from, to,false);
if(result.first == "") return true;
return false;
}
@ -2320,9 +2333,9 @@ namespace MWWorld
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName)
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection)
{
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName);
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, caster, sourceName, fallbackDirection);
}
const std::vector<std::string>& World::getContentFiles() const
@ -2335,6 +2348,9 @@ namespace MWWorld
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility);
if (actor.getClass().hasInventoryStore(actor))
actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility);
// Normally updated once per frame, but here it is kinda important to do it right away.
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);
}
bool World::isDark() const
@ -2436,14 +2452,15 @@ namespace MWWorld
if (!ptr.getRefData().isEnabled())
return true;
// Consider references inside containers as well
if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name())
// Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers)
if (mType != World::Detect_Creature &&
(ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()))
{
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
{
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
if (needToAdd(*it))
if (needToAdd(*it, mDetector))
{
mOut.push_back(ptr);
return true;
@ -2452,16 +2469,25 @@ namespace MWWorld
}
}
if (needToAdd(ptr))
if (needToAdd(ptr, mDetector))
mOut.push_back(ptr);
return true;
}
bool needToAdd (MWWorld::Ptr ptr)
bool needToAdd (MWWorld::Ptr ptr, MWWorld::Ptr detector)
{
if (mType == World::Detect_Creature && ptr.getClass().getTypeName() != typeid(ESM::Creature).name())
return false;
if (mType == World::Detect_Creature)
{
// If in werewolf form, this detects only NPCs, otherwise only creatures
if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf())
{
if (ptr.getClass().getTypeName() != typeid(ESM::NPC).name())
return false;
}
else if (ptr.getClass().getTypeName() != typeid(ESM::Creature).name())
return false;
}
if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr))
return false;
if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty())
@ -2593,6 +2619,8 @@ namespace MWWorld
int days = std::max(1, bounty / iDaysinPrisonMod);
advanceTime(days * 24);
for (int i=0; i<days*24; ++i)
MWBase::Environment::get().getMechanicsManager ()->rest (true);
std::set<int> skills;
for (int day=0; day<days; ++day)
@ -2636,8 +2664,6 @@ namespace MWWorld
message += "\n" + skillMsg;
}
// TODO: Sleep the player
std::vector<std::string> buttons;
buttons.push_back("#{sOk}");
MWBase::Environment::get().getWindowManager()->messageBox(message, buttons);

@ -482,7 +482,7 @@ namespace MWWorld
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);
///< get all items in active cells owned by this Npc
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc);
virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor);
///< get Line of Sight (morrowind stupid implementation)
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist);
@ -546,7 +546,7 @@ namespace MWWorld
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName);
const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection);
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);

@ -167,7 +167,6 @@ namespace Compiler
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting);
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting,
opcodeForceGreetingExplicit);
extensions.registerInstruction("goodbye", "", opcodeGoodbye);
@ -442,7 +441,7 @@ namespace Compiler
extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace,
opcodeGetRaceExplicit);
extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills);
extensions.registerFunction ("getwerewolfkills", 'l', "", opcodeGetWerewolfKills);
extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit);
extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit);
extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit);
@ -450,6 +449,7 @@ namespace Compiler
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit);
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);

@ -385,6 +385,8 @@ namespace Compiler
const int opcodeLowerRankExplicit = 0x20001eb;
const int opcodeOnDeath = 0x20001fc;
const int opcodeOnDeathExplicit = 0x2000205;
const int opcodeOnMurder = 0x2000249;
const int opcodeOnMurderExplicit = 0x200024a;
const int opcodeOnKnockout = 0x2000240;
const int opcodeOnKnockoutExplicit = 0x2000241;

@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
mDied = false;
esm.getHNOT (mDied, "DIED");
mMurdered = false;
esm.getHNOT (mMurdered, "MURD");
mFriendlyHits = 0;
esm.getHNOT (mFriendlyHits, "FRHT");
@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mDied)
esm.writeHNT ("DIED", mDied);
if (mMurdered)
esm.writeHNT ("MURD", mMurdered);
if (mFriendlyHits)
esm.writeHNT ("FRHT", mFriendlyHits);

@ -38,6 +38,7 @@ namespace ESM
bool mDead;
bool mDied;
bool mMurdered;
int mFriendlyHits;
bool mTalkedTo;
bool mAlarmed;

@ -37,6 +37,8 @@ void ESM::InventoryState::load (ESMReader &esm)
LightState state;
int slot;
read (esm, state, slot);
if (state.mCount == 0)
continue;
mLights.push_back (std::make_pair (state, slot));
}
else
@ -44,6 +46,8 @@ void ESM::InventoryState::load (ESMReader &esm)
ObjectState state;
int slot;
read (esm, state, slot);
if (state.mCount == 0)
continue;
mItems.push_back (std::make_pair (state, std::make_pair (id, slot)));
}
}

@ -18,7 +18,7 @@ static const char* const openmwCfgFile = "openmw.cfg";
const char* const mwToken = "?mw?";
const char* const localToken = "?local?";
const char* const userToken = "?user?";
const char* const userDataToken = "?userdata?";
const char* const globalToken = "?global?";
ConfigurationManager::ConfigurationManager()
@ -40,7 +40,7 @@ void ConfigurationManager::setupTokensMapping()
{
mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath));
mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath));
mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath));
mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath));
mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));
}

@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
const Nif::NiZBufferProperty *zprop,
const Nif::NiSpecularProperty *specprop,
const Nif::NiWireframeProperty *wireprop,
bool &needTangents)
bool &needTangents, bool disableLighting)
{
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
Ogre::MaterialPtr material = matMgr.getByName(name);
@ -245,6 +245,14 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
}
}
if (disableLighting)
{
ambient = Ogre::Vector3(0.f);
diffuse = Ogre::Vector3(0.f);
specular = Ogre::Vector3(0.f);
emissive = Ogre::Vector3(1.f);
}
{
// Generate a hash out of all properties that can affect the material.
size_t h = 0;

@ -49,7 +49,7 @@ public:
const Nif::NiZBufferProperty *zprop,
const Nif::NiSpecularProperty *specprop,
const Nif::NiWireframeProperty *wireprop,
bool &needTangents);
bool &needTangents, bool disableLighting=false);
};
}

@ -850,7 +850,10 @@ class NIFObjectLoader
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
texprop, matprop, alphaprop,
vertprop, zprop, specprop,
wireprop, needTangents));
wireprop, needTangents,
// MW doesn't light particles, but the MaterialProperty
// used still has lighting, so that must be ignored.
true));
partsys->setCullIndividually(false);
partsys->setParticleQuota(particledata->numParticles);

@ -13,10 +13,13 @@
<Property key="FontName" value="MonoFont"/>
<Property key="TextAlign" value="Left Top"/>
<Property key="TextColour" value="1 1 1"/>
<Property key="InvertSelected" value="false"/>
</Widget>
<!-- Command line -->
<Widget type="EditBox" skin="MW_ConsoleCommand" position="0 338 384 28" align="HStretch Bottom" name="edit_Command"/>
<Widget type="EditBox" skin="MW_ConsoleCommand" position="0 338 384 28" align="HStretch Bottom" name="edit_Command">
<Property key="InvertSelected" value="false"/>
</Widget>
</Widget>
</MyGUI>

@ -31,17 +31,18 @@
<UserString key="HStretch" value="false"/>
<UserString key="VStretch" value="false"/>
<Widget type="TextBox" skin="SandText" position="0 0 100 24" name="AttribMultiplier1"/>
<Widget type="TextBox" skin="SandText" position="0 24 100 24" name="AttribMultiplier2"/>
<Widget type="TextBox" skin="SandText" position="0 48 100 24" name="AttribMultiplier3"/>
<Widget type="TextBox" skin="SandText" position="0 72 100 24" name="AttribMultiplier4"/>
<Widget type="TextBox" skin="SandText" position="200 0 100 24" name="AttribMultiplier5"/>
<Widget type="TextBox" skin="SandText" position="200 24 100 24" name="AttribMultiplier6"/>
<Widget type="TextBox" skin="SandText" position="200 48 100 24" name="AttribMultiplier7"/>
<Widget type="TextBox" skin="SandText" position="200 72 100 24" name="AttribMultiplier8"/>
<Widget type="TextBox" skin="SandTextVCenter" position="0 0 100 24" name="AttribMultiplier1"/>
<Widget type="TextBox" skin="SandTextVCenter" position="0 24 100 24" name="AttribMultiplier2"/>
<Widget type="TextBox" skin="SandTextVCenter" position="0 48 100 24" name="AttribMultiplier3"/>
<Widget type="TextBox" skin="SandTextVCenter" position="0 72 100 24" name="AttribMultiplier4"/>
<Widget type="TextBox" skin="SandTextVCenter" position="200 0 100 24" name="AttribMultiplier5"/>
<Widget type="TextBox" skin="SandTextVCenter" position="200 24 100 24" name="AttribMultiplier6"/>
<Widget type="TextBox" skin="SandTextVCenter" position="200 48 100 24" name="AttribMultiplier7"/>
<Widget type="TextBox" skin="SandTextVCenter" position="200 72 100 24" name="AttribMultiplier8"/>
<Widget type="HBox" position="22 0 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib1">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/>
@ -55,6 +56,7 @@
<Widget type="HBox" position="22 24 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib2">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/>
@ -68,6 +70,7 @@
<Widget type="HBox" position="22 48 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib3">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/>
@ -81,6 +84,7 @@
<Widget type="HBox" position="22 72 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib4">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/>
@ -95,6 +99,7 @@
<Widget type="HBox" position="222 0 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib5">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/>
@ -108,6 +113,7 @@
<Widget type="HBox" position="222 24 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib6">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/>
@ -121,6 +127,7 @@
<Widget type="HBox" position="222 48 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib7">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/>
@ -134,6 +141,7 @@
<Widget type="HBox" position="222 72 200 24">
<Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib8">
<UserString key="TextPadding" value="0 0"/>
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="AttributeToolTip"/>
<UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/>

@ -32,6 +32,13 @@
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch"/>
</Skin>
<Skin name="SandTextVCenter" size="16 16">
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Left VCenter"/>
<Property key="TextColour" value="0.75 0.6 0.35"/>
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch"/>
</Skin>
<Skin name="SandTextRight" size="16 16">
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Right Bottom"/>

@ -19,6 +19,8 @@
<Widget type="MWScrollBar" skin="MW_HScroll" name="HourSlider" position="0 0 0 18">
<Property key="MoveToClick" value="true"/>
<Property key="Range" value="24"/>
<Property key="Page" value="1"/>
<Property key="WheelPage" value="1"/>
<UserString key="HStretch" value="true"/>
</Widget>

@ -1,4 +1,4 @@
data="?global?data"
data="?mw?Data Files"
data-local="?user?data"
data-local="?userdata?data"
resources=${OPENMW_RESOURCE_FILES}

@ -1,5 +1,5 @@
data="?global?data"
data="?mw?Data Files"
data=./data
data-local="?user?data"
data-local="?userdata?data"
resources=./resources

Loading…
Cancel
Save