npc.cpp 27.5 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

scrawl's avatar
scrawl committed
238 239 240 241 242 243
    bool Npc::isPersistent(const MWWorld::Ptr &actor) const
    {
        MWWorld::LiveCellRef<ESM::NPC>* ref = actor.get<ESM::NPC>();
        return ref->mBase->mPersistent;
    }

greye's avatar
greye committed
244 245
    std::string Npc::getModel(const MWWorld::Ptr &ptr) const
    {
246
        MWWorld::LiveCellRef<ESM::NPC> *ref =
Jason Hooks's avatar
Jason Hooks committed
247
            ptr.get<ESM::NPC>();
greye's avatar
greye committed
248
        assert(ref->mBase != NULL);
249

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

greye's avatar
greye committed
252 253 254 255
        int end = headID.find_last_of("head_") - 4;
        std::string bodyRaceID = headID.substr(0, end);

        std::string model = "meshes\\base_anim.nif";
256 257
        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
258
            model = "meshes\\base_animkna.nif";
259

greye's avatar
greye committed
260
        return model;
261 262 263

    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
264 265
    std::string Npc::getName (const MWWorld::Ptr& ptr) const
    {
266
        MWWorld::LiveCellRef<ESM::NPC> *ref =
Marc Zinnschlag's avatar
Marc Zinnschlag committed
267 268
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
269
        return ref->mBase->mName;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
270 271
    }

272
    MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const
273
    {
274
        ensureCustomData (ptr);
275

276
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
277 278
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
279 280
    MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const
    {
281
        ensureCustomData (ptr);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
282

283
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
284 285
    }

286
    boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
287
        const MWWorld::Ptr& actor) const
288
    {
scrawl's avatar
scrawl committed
289
        if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead())
290 291 292
            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
293 294
        else
            return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
295
    }
Marc Zinnschlag's avatar
Marc Zinnschlag committed
296

297
    MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)
298
        const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
299
    {
300
        ensureCustomData (ptr);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
301

302 303 304 305 306 307 308 309 310
        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
311 312
    }

313 314
    std::string Npc::getScript (const MWWorld::Ptr& ptr) const
    {
315
        MWWorld::LiveCellRef<ESM::NPC> *ref =
316 317
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
318
        return ref->mBase->mScript;
319 320
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
321 322 323 324 325 326 327 328
    void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const
    {
        MWMechanics::NpcStats& stats = getNpcStats (ptr);

        switch (stance)
        {
            case Run:

329
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceRun, force);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
330 331 332 333
                break;

            case Sneak:

334
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
                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:

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

            case Sneak:

356
                stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
357 358 359 360
                break;

            case Combat:

361 362
                // 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
363 364 365 366 367 368 369 370 371 372 373 374
                break;
        }
    }

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

        switch (stance)
        {
            case Run:

375
                if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun))
376 377
                    return true;

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

            case Sneak:

382
                if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak))
Marc Zinnschlag's avatar
Marc Zinnschlag committed
383 384
                    return true;

385
                return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
386 387 388

            case Combat:

389
                return false;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
390 391 392 393 394
        }

        return false;
    }

395
    float Npc::getSpeed(const MWWorld::Ptr& ptr) const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
396
    {
397 398
        const MWBase::World *world = MWBase::Environment::get().getWorld();
        const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
399 400
        const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();

401 402 403 404 405 406 407 408
        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();
409

410 411
        float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
                                    fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
412 413
        if(npcdata->mNpcStats.isWerewolf())
            runSpeed *= fWereWolfRunMult->getFloat();
414 415

        float moveSpeed;
416
        if(normalizedEncumbrance >= 1.0f)
417
            moveSpeed = 0.0f;
418
        else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0)
419 420
        {
            float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() +
421
                                    mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude);
422 423 424 425 426 427 428 429 430 431
            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;
432
            swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude;
433 434 435 436
            swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
                                                    fSwimRunAthleticsMult->getFloat();
            moveSpeed = swimSpeed;
        }
scrawl's avatar
scrawl committed
437
        else if(Npc::getStance(ptr, Run, false) && !Npc::getStance(ptr, Sneak, false))
438 439 440
            moveSpeed = runSpeed;
        else
            moveSpeed = walkSpeed;
441
        if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
442 443 444
            moveSpeed *= 0.75f;

        return moveSpeed;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
445 446
    }

447 448 449 450 451 452
    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() *
453
                                          (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477

        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;
    }

478 479
    MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const
    {
480
        ensureCustomData (ptr);
481

482
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mMovement;
483 484 485 486
    }

    Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
    {
487 488 489 490 491 492
        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;
493
    }
494 495 496

    Ogre::Vector3 Npc::getRotationVector (const MWWorld::Ptr& ptr) const
    {
497 498 499 500 501 502
        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;
503 504
    }

505 506 507 508
    bool Npc::isEssential (const MWWorld::Ptr& ptr) const
    {
        MWWorld::LiveCellRef<ESM::NPC> *ref =
            ptr.get<ESM::NPC>();
509

greye's avatar
greye committed
510
        return ref->mBase->mFlags & ESM::NPC::Essential;
511 512
    }
    
513 514 515 516 517
    void Npc::registerSelf()
    {
        boost::shared_ptr<Class> instance (new Npc);
        registerClass (typeid (ESM::NPC).name(), instance);
    }
518 519 520 521 522 523 524 525

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

        return true;
    }

526
    MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::Ptr& ptr) const
527
    {
528
        MWWorld::LiveCellRef<ESM::NPC> *ref =
529 530 531
            ptr.get<ESM::NPC>();

        MWGui::ToolTipInfo info;
greye's avatar
greye committed
532
        info.caption = ref->mBase->mName;
533 534

        std::string text;
535
        if (MWBase::Environment::get().getWindowManager()->getFullHelp())
greye's avatar
greye committed
536
            text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
537 538 539 540
        info.text = text;

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

scrawl's avatar
scrawl committed
542
    float Npc::getCapacity (const MWWorld::Ptr& ptr) const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
543 544
    {
        const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
greye's avatar
greye committed
545
        return stats.getAttribute(0).getModified()*5;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
546
    }
547 548 549 550 551 552 553

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

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

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

556
        weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude;
557 558 559 560 561 562

        if (weight<0)
            weight = 0;

        return weight;
    }
563 564 565 566 567 568 569 570

    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

571
        return stats.getActiveSpells().addSpell (id, actor);
572 573 574 575 576 577 578 579
    }

    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>();

580 581 582 583
        const ESM::Class *class_ =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find (
                ref->mBase->mClass
            );
584 585 586

        stats.useSkill (skill, *class_, usageType);
    }
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 631 632 633 634 635 636
    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
637 638 639 640 641
    void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const
    {
        y = 0;
        x = 0;
    }
642

scrawl's avatar
scrawl committed
643 644 645 646 647 648 649 650 651 652 653 654 655 656
    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
657 658 659 660 661 662 663 664 665
    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;
    }

666 667 668 669 670 671 672 673 674 675

    std::string Npc::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
    {
        if(name == "left")
        {
            MWBase::World *world = MWBase::Environment::get().getWorld();
            Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
            if(world->isUnderwater(ptr.getCell(), pos))
                return "FootWaterLeft";
            if(world->isOnGround(ptr))
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
            {
                MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
                MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
                if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())
                    return "FootBareLeft";

                switch(Class::get(*boots).getEquipmentSkill(*boots))
                {
                    case ESM::Skill::LightArmor:
                        return "FootLightLeft";
                    case ESM::Skill::MediumArmor:
                        return "FootMediumLeft";
                    case ESM::Skill::HeavyArmor:
                        return "FootHeavyLeft";
                }
            }
692 693 694 695 696 697 698 699 700
            return "";
        }
        if(name == "right")
        {
            MWBase::World *world = MWBase::Environment::get().getWorld();
            Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
            if(world->isUnderwater(ptr.getCell(), pos))
                return "FootWaterRight";
            if(world->isOnGround(ptr))
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
            {
                MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
                MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
                if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())
                    return "FootBareRight";

                switch(Class::get(*boots).getEquipmentSkill(*boots))
                {
                    case ESM::Skill::LightArmor:
                        return "FootLightRight";
                    case ESM::Skill::MediumArmor:
                        return "FootMediumRight";
                    case ESM::Skill::HeavyArmor:
                        return "FootHeavyRight";
                }
            }
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
            return "";
        }
        // TODO: I have no idea what these are supposed to do for NPCs since they use
        // voiced dialog for various conditions like health loss and combat taunts. Maybe
        // only for biped creatures?
        if(name == "moan")
            return "";
        if(name == "roar")
            return "";
        if(name == "scream")
            return "";

        throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
    }

732
    MWWorld::Ptr
733
    Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
734 735 736 737
    {
        MWWorld::LiveCellRef<ESM::NPC> *ref =
            ptr.get<ESM::NPC>();

greye's avatar
greye committed
738
        return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell);
739
    }
740 741 742 743 744 745 746 747 748 749 750

    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;
751 752 753 754 755
    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;
756
    const ESM::GameSetting *Npc::fWereWolfRunMult;
757
}