npc.cpp 8.09 KB
Newer Older
1 2 3

#include "npc.hpp"

4 5
#include <memory>

6 7
#include <components/esm/loadnpc.hpp>

Marc Zinnschlag's avatar
Marc Zinnschlag committed
8
#include "../mwmechanics/creaturestats.hpp"
Marc Zinnschlag's avatar
Marc Zinnschlag committed
9
#include "../mwmechanics/npcstats.hpp"
Marc Zinnschlag's avatar
Marc Zinnschlag committed
10

11
#include "../mwworld/ptr.hpp"
12
#include "../mwworld/actiontalk.hpp"
13 14 15
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"

16
#include "../mwmechanics/mechanicsmanager.hpp"
Jason Hooks's avatar
Jason Hooks committed
17
#include <OgreSceneNode.h>
18

Marc Zinnschlag's avatar
Marc Zinnschlag committed
19
namespace
20 21 22
{
    const Ogre::Radian kOgrePi (Ogre::Math::PI);
    const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0));
23 24 25 26 27 28 29 30 31 32 33 34

    struct CustomData : public MWWorld::CustomData
    {
        MWMechanics::NpcStats mNpcStats;

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

    MWWorld::CustomData *CustomData::clone() const
    {
        return new CustomData (*this);
    }
35 36
}

37
namespace MWClass
38
{
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
    void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
    {
        if (!ptr.getRefData().getCustomData())
        {
            std::auto_ptr<CustomData> data (new CustomData);

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

            if (!ref->base->faction.empty())
            {
                // TODO research how initial rank is stored. The information in loadnpc.hpp are at
                // best very unclear.
                data->mNpcStats.mFactionRank[ref->base->faction] = 0;
            }

            for (int i=0; i<27; ++i)
                data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]);

            ptr.getRefData().setCustomData (data.release());
        }
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
61 62 63 64 65 66 67 68
    std::string Npc::getId (const MWWorld::Ptr& ptr) const
    {
        ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
            ptr.get<ESM::NPC>();

        return ref->base->mId;
    }

Jason Hooks's avatar
Jason Hooks committed
69
    void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
70
    {
Jason Hooks's avatar
Jason Hooks committed
71
        renderingInterface.getActors().insertNPC(ptr);
Jason Hooks's avatar
Jason Hooks committed
72
    }
Jason Hooks's avatar
Jason Hooks committed
73

Jason Hooks's avatar
Jason Hooks committed
74 75
    void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const
    {
Jason Hooks's avatar
Jason Hooks committed
76

Marc Zinnschlag's avatar
Marc Zinnschlag committed
77

Jason Hooks's avatar
Jason Hooks committed
78 79
        ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
            ptr.get<ESM::NPC>();
Marc Zinnschlag's avatar
Marc Zinnschlag committed
80

Jason Hooks's avatar
Jason Hooks committed
81

Jason Hooks's avatar
Jason Hooks committed
82
        assert (ref->base != NULL);
83 84 85 86
		 std::string headID = ref->base->head;
		 std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4);
		 bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_";

Marc Zinnschlag's avatar
Marc Zinnschlag committed
87

88 89 90
        std::string smodel = "meshes\\base_anim.nif";
		if(beast)
			smodel = "meshes\\base_animkna.nif";
Jason Hooks's avatar
Jason Hooks committed
91
		physics.insertActorPhysics(ptr, smodel);
92

93

94
    }
95

96 97 98 99 100 101 102 103 104 105
    void Npc::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
    {
        environment.mMechanicsManager->addActor (ptr);
    }

    void Npc::disable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
    {
        environment.mMechanicsManager->removeActor (ptr);
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
106 107 108 109 110 111 112 113
    std::string Npc::getName (const MWWorld::Ptr& ptr) const
    {
        ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
            ptr.get<ESM::NPC>();

        return ref->base->name;
    }

114
    MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const
115 116 117 118 119 120
    {
        if (!ptr.getRefData().getCreatureStats().get())
        {
            boost::shared_ptr<MWMechanics::CreatureStats> stats (
                new MWMechanics::CreatureStats);

121
            ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref = ptr.get<ESM::NPC>();
122 123 124 125 126 127 128 129 130 131 132 133 134

            stats->mAttributes[0].set (ref->base->npdt52.strength);
            stats->mAttributes[1].set (ref->base->npdt52.intelligence);
            stats->mAttributes[2].set (ref->base->npdt52.willpower);
            stats->mAttributes[3].set (ref->base->npdt52.agility);
            stats->mAttributes[4].set (ref->base->npdt52.speed);
            stats->mAttributes[5].set (ref->base->npdt52.endurance);
            stats->mAttributes[6].set (ref->base->npdt52.personality);
            stats->mAttributes[7].set (ref->base->npdt52.luck);
            stats->mDynamic[0].set (ref->base->npdt52.health);
            stats->mDynamic[1].set (ref->base->npdt52.mana);
            stats->mDynamic[2].set (ref->base->npdt52.fatigue);

135 136
            stats->mLevel = ref->base->npdt52.level;

137 138 139 140 141 142
            ptr.getRefData().getCreatureStats() = stats;
        }

        return *ptr.getRefData().getCreatureStats();
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
143 144
    MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const
    {
145
        ensureCustomData (ptr);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
146

147
        return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
148 149
    }

150 151 152 153
    boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
        const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
    {
        return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
154
    }
Marc Zinnschlag's avatar
Marc Zinnschlag committed
155

156 157
    MWWorld::ContainerStore<MWWorld::RefData>& Npc::getContainerStore (const MWWorld::Ptr& ptr)
        const
Marc Zinnschlag's avatar
Marc Zinnschlag committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171
    {
        if (!ptr.getRefData().getContainerStore().get())
        {
            boost::shared_ptr<MWWorld::ContainerStore<MWWorld::RefData> > store (
                new MWWorld::ContainerStore<MWWorld::RefData>);

            // TODO add initial content

            ptr.getRefData().getContainerStore() = store;
        }

        return *ptr.getRefData().getContainerStore();
    }

172 173 174 175 176 177 178 179
    std::string Npc::getScript (const MWWorld::Ptr& ptr) const
    {
        ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
            ptr.get<ESM::NPC>();

        return ref->base->script;
    }

Marc Zinnschlag's avatar
Marc Zinnschlag committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
    void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const
    {
        MWMechanics::NpcStats& stats = getNpcStats (ptr);

        switch (stance)
        {
            case Run:

                stats.mForceRun = force;
                break;

            case Sneak:

                stats.mForceSneak = force;
                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:

210 211
                stats.mRun = set;
                break;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

            case Sneak:

                stats.mSneak = set;
                break;

            case Combat:

                stats.mCombat = set;
                break;
        }
    }

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

        switch (stance)
        {
            case Run:

233 234 235 236
                if (!ignoreForce && stats.mForceRun)
                    return true;

                return stats.mRun;
Marc Zinnschlag's avatar
Marc Zinnschlag committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

            case Sneak:

                if (!ignoreForce && stats.mForceSneak)
                    return true;

                return stats.mSneak;

            case Combat:

                return stats.mCombat;
        }

        return false;
    }

    float Npc::getSpeed (const MWWorld::Ptr& ptr) const
    {
        return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats
    }

258 259 260 261 262 263
    MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const
    {
        if (!ptr.getRefData().getMovement().get())
        {
            boost::shared_ptr<MWMechanics::Movement> movement (
                new MWMechanics::Movement);
Marc Zinnschlag's avatar
Marc Zinnschlag committed
264 265

            ptr.getRefData().getMovement() = movement;
266 267 268 269 270 271 272 273 274 275 276 277
        }

        return *ptr.getRefData().getMovement();
    }

    Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
    {
        Ogre::Vector3 vector (0, 0, 0);

        if (ptr.getRefData().getMovement().get())
        {
            vector.x = - ptr.getRefData().getMovement()->mLeftRight * 200;
gugus's avatar
gugus committed
278
            vector.y = ptr.getRefData().getMovement()->mForwardBackward * 200;
279 280 281 282 283 284 285 286

            if (getStance (ptr, Run, false))
                vector *= 2;
        }

        return vector;
    }

287 288 289 290 291 292 293
    void Npc::registerSelf()
    {
        boost::shared_ptr<Class> instance (new Npc);

        registerClass (typeid (ESM::NPC).name(), instance);
    }
}