npc.cpp 24.6 KB
Newer Older
1 2 3

#include "npc.hpp"

4 5
#include <memory>

scrawl's avatar
scrawl committed
6 7
#include <boost/algorithm/string.hpp>

8 9
#include <OgreSceneNode.h>

10 11
#include <components/esm/loadnpc.hpp>

12 13
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
14
#include "../mwbase/mechanicsmanager.hpp"
15
#include "../mwbase/windowmanager.hpp"
16

Marc Zinnschlag's avatar
Marc Zinnschlag committed
17
#include "../mwmechanics/creaturestats.hpp"
Marc Zinnschlag's avatar
Marc Zinnschlag committed
18
#include "../mwmechanics/npcstats.hpp"
19
#include "../mwmechanics/movement.hpp"
Marc Zinnschlag's avatar
Marc Zinnschlag committed
20

21
#include "../mwworld/ptr.hpp"
22
#include "../mwworld/actiontalk.hpp"
scrawl's avatar
scrawl committed
23
#include "../mwworld/actionopen.hpp"
24
#include "../mwworld/inventorystore.hpp"
Marc Zinnschlag's avatar
Marc Zinnschlag committed
25
#include "../mwworld/customdata.hpp"
26
#include "../mwworld/physicssystem.hpp"
27

28
#include "../mwrender/actors.hpp"
29
#include "../mwrender/renderinginterface.hpp"
30

31
#include "../mwgui/tooltips.hpp"
32

Marc Zinnschlag's avatar
Marc Zinnschlag committed
33
namespace
34 35 36
{
    const Ogre::Radian kOgrePi (Ogre::Math::PI);
    const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0));
37 38 39 40

    struct CustomData : public MWWorld::CustomData
    {
        MWMechanics::NpcStats mNpcStats;
41
        MWMechanics::CreatureStats mCreatureStats;
42
        MWMechanics::Movement mMovement;
43
        MWWorld::InventoryStore mInventoryStore;
44 45 46 47 48 49 50 51

        virtual MWWorld::CustomData *clone() const;
    };

    MWWorld::CustomData *CustomData::clone() const
    {
        return new CustomData (*this);
    }
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

    void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
    {
        // race bonus
        const ESM::Race *race =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc->mRace);

        bool male = (npc->mFlags & ESM::NPC::Female) == 0;

        int level = creatureStats.getLevel();

        for (int i=0; i<ESM::Attribute::Length; ++i)
        {
            const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];
            creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale);
        }

        // class bonus
        const ESM::Class *class_ =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);

        for (int i=0; i<2; ++i)
        {
            int attribute = class_->mData.mAttribute[i];
            if (attribute>=0 && attribute<8)
            {
                creatureStats.getAttribute(attribute).setBase (
                    creatureStats.getAttribute(attribute).getBase() + 10);
            }
        }

        // skill bonus
        for (int attribute=0; attribute<ESM::Attribute::Length; ++attribute)
        {
            float modifierSum = 0;

            for (int j=0; j<ESM::Skill::Length; ++j)
            {
                const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(j);

                if (skill->mData.mAttribute != attribute)
                    continue;

                // is this a minor or major skill?
                float add=0.2;
                for (int k=0; k<5; ++k)
                {
                    if (class_->mData.mSkills[k][0] == j)
                        add=0.5;
                }
                for (int k=0; k<5; ++k)
                {
                    if (class_->mData.mSkills[k][1] == j)
                        add=1.0;
                }
                modifierSum += add;
            }
            creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase()
                + static_cast<int>((level-1) * modifierSum+0.5), 100) );
        }
    }
113 114
}

115
namespace MWClass
116
{
117 118
    void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
    {
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
        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");
135 136 137 138 139
            fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase");
            fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier");
            fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase");
            fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
            fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
140
            fWereWolfRunMult = gmst.find("fWereWolfRunMult");
141 142 143

            inited = true;
        }
144 145
        if (!ptr.getRefData().getCustomData())
        {
146
            std::auto_ptr<CustomData> data(new CustomData);
147

148
            MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
149

150
            // NPC stats
greye's avatar
greye committed
151
            if (!ref->mBase->mFaction.empty())
152
            {
greye's avatar
greye committed
153
                std::string faction = ref->mBase->mFaction;
eduard's avatar
eduard committed
154
                Misc::StringUtils::toLower(faction);
greye's avatar
greye committed
155
                if(ref->mBase->mNpdt52.mGold != -10)
156
                {
greye's avatar
greye committed
157
                    data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt52.mRank;
158 159 160
                }
                else
                {
greye's avatar
greye committed
161
                    data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt12.mRank;
162
                }
163 164
            }

165
            // creature stats
greye's avatar
greye committed
166
            if(ref->mBase->mNpdt52.mGold != -10)
167 168
            {
                for (int i=0; i<27; ++i)
greye's avatar
greye committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
                    data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]);

                data->mCreatureStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength);
                data->mCreatureStats.getAttribute(1).set (ref->mBase->mNpdt52.mIntelligence);
                data->mCreatureStats.getAttribute(2).set (ref->mBase->mNpdt52.mWillpower);
                data->mCreatureStats.getAttribute(3).set (ref->mBase->mNpdt52.mAgility);
                data->mCreatureStats.getAttribute(4).set (ref->mBase->mNpdt52.mSpeed);
                data->mCreatureStats.getAttribute(5).set (ref->mBase->mNpdt52.mEndurance);
                data->mCreatureStats.getAttribute(6).set (ref->mBase->mNpdt52.mPersonality);
                data->mCreatureStats.getAttribute(7).set (ref->mBase->mNpdt52.mLuck);
                data->mCreatureStats.setHealth (ref->mBase->mNpdt52.mHealth);
                data->mCreatureStats.setMagicka (ref->mBase->mNpdt52.mMana);
                data->mCreatureStats.setFatigue (ref->mBase->mNpdt52.mFatigue);

                data->mCreatureStats.setLevel(ref->mBase->mNpdt52.mLevel);
scrawl's avatar
scrawl committed
184
                data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt52.mDisposition);
185
                data->mNpcStats.setReputation(ref->mBase->mNpdt52.mReputation);
186 187 188
            }
            else
            {
189 190 191
                for (int i=0; i<3; ++i)
                    data->mCreatureStats.setDynamic (i, 10);

192 193 194
                data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel);
                data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition);
                data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
195 196

                autoCalculateAttributes(ref->mBase, data->mCreatureStats);
197
            }
198

199 200 201 202
            data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
            data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
            data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
            data->mCreatureStats.setAiSetting (3, ref->mBase->mAiData.mAlarm);
203

204
            // spells
greye's avatar
greye committed
205 206
            for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
                iter!=ref->mBase->mSpells.mList.end(); ++iter)
207 208
                data->mCreatureStats.getSpells().add (*iter);

209
            // store
210 211 212 213
            ptr.getRefData().setCustomData (data.release());
        }
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
214 215
    std::string Npc::getId (const MWWorld::Ptr& ptr) const
    {
216
        MWWorld::LiveCellRef<ESM::NPC> *ref =
Marc Zinnschlag's avatar
Marc Zinnschlag committed
217 218
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
219
        return ref->mBase->mId;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
220 221
    }

222 223 224 225 226
    void Npc::adjustPosition(const MWWorld::Ptr& ptr) const
    {
        MWBase::Environment::get().getWorld()->adjustPosition(ptr);
    }

Jason Hooks's avatar
Jason Hooks committed
227
    void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
228
    {
Jason Hooks's avatar
Jason Hooks committed
229
        renderingInterface.getActors().insertNPC(ptr, getInventoryStore(ptr));
Jason Hooks's avatar
Jason Hooks committed
230
    }
Jason Hooks's avatar
Jason Hooks committed
231

232
    void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
Jason Hooks's avatar
Jason Hooks committed
233
    {
234
        physics.addActor(ptr);
235
        MWBase::Environment::get().getMechanicsManager()->add(ptr);
greye's avatar
greye committed
236
    }
Jason Hooks's avatar
Jason Hooks committed
237

greye's avatar
greye committed
238 239
    std::string Npc::getModel(const MWWorld::Ptr &ptr) const
    {
240
        MWWorld::LiveCellRef<ESM::NPC> *ref =
Jason Hooks's avatar
Jason Hooks committed
241
            ptr.get<ESM::NPC>();
greye's avatar
greye committed
242
        assert(ref->mBase != NULL);
243

greye's avatar
greye committed
244
        std::string headID = ref->mBase->mHead;
245

greye's avatar
greye committed
246 247 248 249
        int end = headID.find_last_of("head_") - 4;
        std::string bodyRaceID = headID.substr(0, end);

        std::string model = "meshes\\base_anim.nif";
250 251
        const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
        if(race->mData.mFlags & ESM::Race::Beast)
greye's avatar
greye committed
252
            model = "meshes\\base_animkna.nif";
253

greye's avatar
greye committed
254
        return model;
255 256 257

    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
258 259
    std::string Npc::getName (const MWWorld::Ptr& ptr) const
    {
260
        MWWorld::LiveCellRef<ESM::NPC> *ref =
Marc Zinnschlag's avatar
Marc Zinnschlag committed
261 262
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
263
        return ref->mBase->mName;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
264 265
    }

266
    MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const
267
    {
268
        ensureCustomData (ptr);
269

270
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
271 272
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
273 274
    MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const
    {
275
        ensureCustomData (ptr);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
276

277
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
278 279
    }

280
    boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
281
        const MWWorld::Ptr& actor) const
282
    {
scrawl's avatar
scrawl committed
283
        if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead())
284 285 286
            return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr, true));
        else if (MWWorld::Class::get(actor).getStance(actor, MWWorld::Class::Sneak))
            return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr)); // stealing
scrawl's avatar
scrawl committed
287 288
        else
            return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
289
    }
Marc Zinnschlag's avatar
Marc Zinnschlag committed
290

291
    MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)
292
        const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
293
    {
294
        ensureCustomData (ptr);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
295

296 297 298 299 300 301 302 303 304
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
    }

    MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr)
        const
    {
        ensureCustomData (ptr);

        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
305 306
    }

307 308
    std::string Npc::getScript (const MWWorld::Ptr& ptr) const
    {
309
        MWWorld::LiveCellRef<ESM::NPC> *ref =
310 311
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
312
        return ref->mBase->mScript;
313 314
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
315 316 317 318 319 320 321 322
    void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const
    {
        MWMechanics::NpcStats& stats = getNpcStats (ptr);

        switch (stance)
        {
            case Run:

323
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceRun, force);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
324 325 326 327
                break;

            case Sneak:

328
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
                break;

            case Combat:

                throw std::runtime_error ("combat stance not enforcable for NPCs");
        }
    }

    void Npc::setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const
    {
        MWMechanics::NpcStats& stats = getNpcStats (ptr);

        switch (stance)
        {
            case Run:

345
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_Run, set);
346
                break;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
347 348 349

            case Sneak:

350
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
351 352 353 354
                break;

            case Combat:

355 356
                // Combat stance ignored for now; need to be determined based on draw state instead of
                // being maunally set.
Marc Zinnschlag's avatar
Marc Zinnschlag committed
357 358 359 360 361 362 363 364 365 366 367 368
                break;
        }
    }

    bool Npc::getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce) const
    {
        MWMechanics::NpcStats& stats = getNpcStats (ptr);

        switch (stance)
        {
            case Run:

369
                if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun))
370 371
                    return true;

372
                return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Run);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
373 374 375

            case Sneak:

376
                if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak))
Marc Zinnschlag's avatar
Marc Zinnschlag committed
377 378
                    return true;

379
                return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
380 381 382

            case Combat:

383
                return false;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
384 385 386 387 388
        }

        return false;
    }

389
    float Npc::getSpeed(const MWWorld::Ptr& ptr) const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
390
    {
391 392
        const MWBase::World *world = MWBase::Environment::get().getWorld();
        const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
393 394
        const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();

395 396 397 398 399 400 401 402
        const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);

        float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified()*
                                                      (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
        walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
        walkSpeed = std::max(0.0f, walkSpeed);
        if(Npc::getStance(ptr, Sneak, false))
            walkSpeed *= fSneakSpeedMultiplier->getFloat();
403

404 405
        float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
                                    fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
406 407
        if(npcdata->mNpcStats.isWerewolf())
            runSpeed *= fWereWolfRunMult->getFloat();
408 409

        float moveSpeed;
410
        if(normalizedEncumbrance >= 1.0f)
411
            moveSpeed = 0.0f;
412
        else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0)
413 414
        {
            float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() +
415
                                    mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude);
416 417 418 419 420 421 422 423 424 425
            flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
            flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
            flySpeed = std::max(0.0f, flySpeed);
            moveSpeed = flySpeed;
        }
        else if(world->isSwimming(ptr))
        {
            float swimSpeed = walkSpeed;
            if(Npc::getStance(ptr, Run, false))
                swimSpeed = runSpeed;
426
            swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude;
427 428 429 430
            swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
                                                    fSwimRunAthleticsMult->getFloat();
            moveSpeed = swimSpeed;
        }
scrawl's avatar
scrawl committed
431
        else if(Npc::getStance(ptr, Run, false) && !Npc::getStance(ptr, Sneak, false))
432 433 434
            moveSpeed = runSpeed;
        else
            moveSpeed = walkSpeed;
435
        if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
436 437 438
            moveSpeed *= 0.75f;

        return moveSpeed;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
439 440
    }

441 442 443 444 445 446
    float Npc::getJump(const MWWorld::Ptr &ptr) const
    {
        const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
        const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();
        const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
                                          fJumpEncumbranceMultiplier->getFloat() *
447
                                          (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

        float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
        float b = 0.0f;
        if(a > 50.0f)
        {
            b = a - 50.0f;
            a = 50.0f;
        }

        float x = fJumpAcrobaticsBase->getFloat() +
                  std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
        x += 3 * b * fJumpAcroMultiplier->getFloat();
        x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64;
        x *= encumbranceTerm;

        if(Npc::getStance(ptr, Run, false))
            x *= fJumpRunMultiplier->getFloat();
        x *= 1.25f;//fatigueTerm;
        x -= -627.2/*gravity constant*/;
        x /= 3;

        return x;
    }

472 473
    MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const
    {
474
        ensureCustomData (ptr);
475

476
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mMovement;
477 478 479 480
    }

    Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
    {
481 482 483 484 485 486
        MWMechanics::Movement &movement = getMovementSettings(ptr);
        Ogre::Vector3 vec(movement.mPosition);
        movement.mPosition[0] = 0.0f;
        movement.mPosition[1] = 0.0f;
        movement.mPosition[2] = 0.0f;
        return vec;
487
    }
488 489 490

    Ogre::Vector3 Npc::getRotationVector (const MWWorld::Ptr& ptr) const
    {
491 492 493 494 495 496
        MWMechanics::Movement &movement = getMovementSettings(ptr);
        Ogre::Vector3 vec(movement.mRotation);
        movement.mRotation[0] = 0.0f;
        movement.mRotation[1] = 0.0f;
        movement.mRotation[2] = 0.0f;
        return vec;
497 498
    }

499 500 501 502
    bool Npc::isEssential (const MWWorld::Ptr& ptr) const
    {
        MWWorld::LiveCellRef<ESM::NPC> *ref =
            ptr.get<ESM::NPC>();
503

greye's avatar
greye committed
504
        return ref->mBase->mFlags & ESM::NPC::Essential;
505 506
    }
    
507 508 509 510 511
    void Npc::registerSelf()
    {
        boost::shared_ptr<Class> instance (new Npc);
        registerClass (typeid (ESM::NPC).name(), instance);
    }
512 513 514 515 516 517 518 519

    bool Npc::hasToolTip (const MWWorld::Ptr& ptr) const
    {
        /// \todo We don't want tooltips for NPCs in combat mode.

        return true;
    }

520
    MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::Ptr& ptr) const
521
    {
522
        MWWorld::LiveCellRef<ESM::NPC> *ref =
523 524 525
            ptr.get<ESM::NPC>();

        MWGui::ToolTipInfo info;
greye's avatar
greye committed
526
        info.caption = ref->mBase->mName;
527 528

        std::string text;
529
        if (MWBase::Environment::get().getWindowManager()->getFullHelp())
greye's avatar
greye committed
530
            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
531 532 533 534
        info.text = text;

        return info;
    }
Marc Zinnschlag's avatar
Marc Zinnschlag committed
535

scrawl's avatar
scrawl committed
536
    float Npc::getCapacity (const MWWorld::Ptr& ptr) const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
537 538
    {
        const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
greye's avatar
greye committed
539
        return stats.getAttribute(0).getModified()*5;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
540
    }
541 542 543 544 545 546 547

    float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const
    {
        float weight = getContainerStore (ptr).getWeight();

        const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);

548
        weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude;
549

550
        weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude;
551 552 553 554 555 556

        if (weight<0)
            weight = 0;

        return weight;
    }
557 558 559 560 561 562 563 564

    bool Npc::apply (const MWWorld::Ptr& ptr, const std::string& id,
        const MWWorld::Ptr& actor) const
    {
        MWMechanics::CreatureStats& stats = getCreatureStats (ptr);

        /// \todo consider instant effects

565
        return stats.getActiveSpells().addSpell (id, actor);
566 567 568 569 570 571 572 573
    }

    void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const
    {
        MWMechanics::NpcStats& stats = getNpcStats (ptr);

        MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();

574 575 576 577
        const ESM::Class *class_ =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find (
                ref->mBase->mClass
            );
578 579 580

        stats.useSkill (skill, *class_, usageType);
    }
581

582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
    float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
    {
        MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr);
        const MWWorld::Store<ESM::GameSetting> &gmst =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

        int ratings[MWWorld::InventoryStore::Slots];

        int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
        float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
        float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
        int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified();

        for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i)
        {
            MWWorld::ContainerStoreIterator it = invStore.getSlot(i);
            if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())
            {
                // unarmored
                ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
            }
            else
            {
                MWWorld::LiveCellRef<ESM::Armor> *ref =
                    it->get<ESM::Armor>();

                int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it);
                int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified();

                if (ref->mBase->mData.mWeight == 0)
                    ratings[i] = ref->mBase->mData.mArmor;
                else
                    ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill;
            }
        }

        float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;

        return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3
                + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]
                    + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]
                    + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]
                    ) * 0.1
                + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet)
                    * 0.05
                + shield;
    }


gugus's avatar
gugus committed
631 632 633 634 635
    void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const
    {
        y = 0;
        x = 0;
    }
636

scrawl's avatar
scrawl committed
637 638 639 640 641 642 643 644 645 646 647 648 649 650
    void Npc::adjustScale(const MWWorld::Ptr &ptr, float &scale) const
    {
        MWWorld::LiveCellRef<ESM::NPC> *ref =
            ptr.get<ESM::NPC>();

        const ESM::Race* race =
                MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);

        if (ref->mBase->isMale())
            scale *= race->mData.mHeight.mMale;
        else
            scale *= race->mData.mHeight.mFemale;
    }

scrawl's avatar
scrawl committed
651 652 653 654 655 656 657 658 659
    int Npc::getServices(const MWWorld::Ptr &actor) const
    {
        MWWorld::LiveCellRef<ESM::NPC>* ref = actor.get<ESM::NPC>();
        if (ref->mBase->mHasAI)
            return ref->mBase->mAiData.mServices;
        else
            return 0;
    }

660
    MWWorld::Ptr
661
    Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
662 663 664 665
    {
        MWWorld::LiveCellRef<ESM::NPC> *ref =
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
666
        return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell);
667
    }
668 669 670 671 672 673 674 675 676 677 678

    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;
679 680 681 682 683
    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;
684
    const ESM::GameSetting *Npc::fWereWolfRunMult;
685
}