1 #include "class.hpp"
2 
3 #include <stdexcept>
4 
5 #include <components/esm/defs.hpp>
6 
7 #include "../mwbase/environment.hpp"
8 #include "../mwbase/windowmanager.hpp"
9 #include "../mwbase/world.hpp"
10 #include "../mwworld/esmstore.hpp"
11 
12 #include "ptr.hpp"
13 #include "refdata.hpp"
14 #include "nullaction.hpp"
15 #include "failedaction.hpp"
16 #include "actiontake.hpp"
17 #include "containerstore.hpp"
18 
19 #include "../mwgui/tooltips.hpp"
20 
21 #include "../mwmechanics/creaturestats.hpp"
22 #include "../mwmechanics/npcstats.hpp"
23 
24 namespace MWWorld
25 {
26     std::map<std::string, std::shared_ptr<Class> > Class::sClasses;
27 
Class()28     Class::Class() {}
29 
~Class()30     Class::~Class() {}
31 
insertObjectRendering(const Ptr & ptr,const std::string & mesh,MWRender::RenderingInterface & renderingInterface) const32     void Class::insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const
33     {
34 
35     }
36 
insertObject(const Ptr & ptr,const std::string & mesh,MWPhysics::PhysicsSystem & physics) const37     void Class::insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const
38     {
39 
40     }
41 
apply(const MWWorld::Ptr & ptr,const std::string & id,const MWWorld::Ptr & actor) const42     bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id,  const MWWorld::Ptr& actor) const
43     {
44         return false;
45     }
46 
skillUsageSucceeded(const MWWorld::Ptr & ptr,int skill,int usageType,float extraFactor) const47     void Class::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const
48     {
49         throw std::runtime_error ("class does not represent an actor");
50     }
51 
canSell(const MWWorld::ConstPtr & item,int npcServices) const52     bool Class::canSell (const MWWorld::ConstPtr& item, int npcServices) const
53     {
54         return false;
55     }
56 
getServices(const ConstPtr & actor) const57     int Class::getServices(const ConstPtr &actor) const
58     {
59         throw std::runtime_error ("class does not have services");
60     }
61 
getCreatureStats(const Ptr & ptr) const62     MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const
63     {
64         throw std::runtime_error ("class does not have creature stats");
65     }
66 
getNpcStats(const Ptr & ptr) const67     MWMechanics::NpcStats& Class::getNpcStats (const Ptr& ptr) const
68     {
69         throw std::runtime_error ("class does not have NPC stats");
70     }
71 
hasItemHealth(const ConstPtr & ptr) const72     bool Class::hasItemHealth (const ConstPtr& ptr) const
73     {
74         return false;
75     }
76 
getItemHealth(const ConstPtr & ptr) const77     int Class::getItemHealth(const ConstPtr &ptr) const
78     {
79         if (ptr.getCellRef().getCharge() == -1)
80             return getItemMaxHealth(ptr);
81         else
82             return ptr.getCellRef().getCharge();
83     }
84 
getItemNormalizedHealth(const ConstPtr & ptr) const85     float Class::getItemNormalizedHealth (const ConstPtr& ptr) const
86     {
87         if (getItemMaxHealth(ptr) == 0)
88         {
89             return 0.f;
90         }
91         else
92         {
93             return getItemHealth(ptr) / static_cast<float>(getItemMaxHealth(ptr));
94         }
95     }
96 
getItemMaxHealth(const ConstPtr & ptr) const97     int Class::getItemMaxHealth (const ConstPtr& ptr) const
98     {
99         throw std::runtime_error ("class does not have item health");
100     }
101 
hit(const Ptr & ptr,float attackStrength,int type) const102     void Class::hit(const Ptr& ptr, float attackStrength, int type) const
103     {
104         throw std::runtime_error("class cannot hit");
105     }
106 
block(const Ptr & ptr) const107     void Class::block(const Ptr &ptr) const
108     {
109         throw std::runtime_error("class cannot block");
110     }
111 
onHit(const Ptr & ptr,float damage,bool ishealth,const Ptr & object,const Ptr & attacker,const osg::Vec3f & hitPosition,bool successful) const112     void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
113     {
114         throw std::runtime_error("class cannot be hit");
115     }
116 
activate(const Ptr & ptr,const Ptr & actor) const117     std::shared_ptr<Action> Class::activate (const Ptr& ptr, const Ptr& actor) const
118     {
119         return std::shared_ptr<Action> (new NullAction);
120     }
121 
use(const Ptr & ptr,bool force) const122     std::shared_ptr<Action> Class::use (const Ptr& ptr, bool force) const
123     {
124         return std::shared_ptr<Action> (new NullAction);
125     }
126 
getContainerStore(const Ptr & ptr) const127     ContainerStore& Class::getContainerStore (const Ptr& ptr) const
128     {
129         throw std::runtime_error ("class does not have a container store");
130     }
131 
getInventoryStore(const Ptr & ptr) const132     InventoryStore& Class::getInventoryStore (const Ptr& ptr) const
133     {
134         throw std::runtime_error ("class does not have an inventory store");
135     }
136 
hasInventoryStore(const Ptr & ptr) const137     bool Class::hasInventoryStore(const Ptr &ptr) const
138     {
139         return false;
140     }
141 
canLock(const ConstPtr & ptr) const142     bool Class::canLock(const ConstPtr &ptr) const
143     {
144         return false;
145     }
146 
setRemainingUsageTime(const Ptr & ptr,float duration) const147     void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const
148     {
149         throw std::runtime_error ("class does not support time-based uses");
150     }
151 
getRemainingUsageTime(const ConstPtr & ptr) const152     float Class::getRemainingUsageTime (const ConstPtr& ptr) const
153     {
154         return -1;
155     }
156 
getScript(const ConstPtr & ptr) const157     std::string Class::getScript (const ConstPtr& ptr) const
158     {
159         return "";
160     }
161 
getMaxSpeed(const Ptr & ptr) const162     float Class::getMaxSpeed (const Ptr& ptr) const
163     {
164         return 0;
165     }
166 
getCurrentSpeed(const Ptr & ptr) const167     float Class::getCurrentSpeed (const Ptr& ptr) const
168     {
169         return 0;
170     }
171 
getJump(const Ptr & ptr) const172     float Class::getJump (const Ptr& ptr) const
173     {
174         return 0;
175     }
176 
getEnchantmentPoints(const MWWorld::ConstPtr & ptr) const177     int Class::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const
178     {
179         throw std::runtime_error ("class does not support enchanting");
180     }
181 
getMovementSettings(const Ptr & ptr) const182     MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const
183     {
184         throw std::runtime_error ("movement settings not supported by class");
185     }
186 
getRotationVector(const Ptr & ptr) const187     osg::Vec3f Class::getRotationVector (const Ptr& ptr) const
188     {
189         return osg::Vec3f (0, 0, 0);
190     }
191 
getEquipmentSlots(const ConstPtr & ptr) const192     std::pair<std::vector<int>, bool> Class::getEquipmentSlots (const ConstPtr& ptr) const
193     {
194         return std::make_pair (std::vector<int>(), false);
195     }
196 
getEquipmentSkill(const ConstPtr & ptr) const197     int Class::getEquipmentSkill (const ConstPtr& ptr) const
198     {
199         return -1;
200     }
201 
getValue(const ConstPtr & ptr) const202     int Class::getValue (const ConstPtr& ptr) const
203     {
204         throw std::logic_error ("value not supported by this class");
205     }
206 
getCapacity(const MWWorld::Ptr & ptr) const207     float Class::getCapacity (const MWWorld::Ptr& ptr) const
208     {
209         throw std::runtime_error ("capacity not supported by this class");
210     }
211 
getWeight(const ConstPtr & ptr) const212     float Class::getWeight(const ConstPtr &ptr) const
213     {
214         throw std::runtime_error ("weight not supported by this class");
215     }
216 
getEncumbrance(const MWWorld::Ptr & ptr) const217     float Class::getEncumbrance (const MWWorld::Ptr& ptr) const
218     {
219         throw std::runtime_error ("encumbrance not supported by class");
220     }
221 
isEssential(const MWWorld::ConstPtr & ptr) const222     bool Class::isEssential (const MWWorld::ConstPtr& ptr) const
223     {
224         return false;
225     }
226 
getArmorRating(const MWWorld::Ptr & ptr) const227     float Class::getArmorRating (const MWWorld::Ptr& ptr) const
228     {
229         throw std::runtime_error("Class does not support armor rating");
230     }
231 
get(const std::string & key)232     const Class& Class::get (const std::string& key)
233     {
234         if (key.empty())
235             throw std::logic_error ("Class::get(): attempting to get an empty key");
236 
237         std::map<std::string, std::shared_ptr<Class> >::const_iterator iter = sClasses.find (key);
238 
239         if (iter==sClasses.end())
240             throw std::logic_error ("Class::get(): unknown class key: " + key);
241 
242         return *iter->second;
243     }
244 
isPersistent(const ConstPtr & ptr) const245     bool Class::isPersistent(const ConstPtr &ptr) const
246     {
247         throw std::runtime_error ("class does not support persistence");
248     }
249 
registerClass(const std::string & key,std::shared_ptr<Class> instance)250     void Class::registerClass(const std::string& key,  std::shared_ptr<Class> instance)
251     {
252         instance->mTypeName = key;
253         sClasses.insert(std::make_pair(key, instance));
254     }
255 
getUpSoundId(const ConstPtr & ptr) const256     std::string Class::getUpSoundId (const ConstPtr& ptr) const
257     {
258         throw std::runtime_error ("class does not have an up sound");
259     }
260 
getDownSoundId(const ConstPtr & ptr) const261     std::string Class::getDownSoundId (const ConstPtr& ptr) const
262     {
263         throw std::runtime_error ("class does not have an down sound");
264     }
265 
getSoundIdFromSndGen(const Ptr & ptr,const std::string & type) const266     std::string Class::getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const
267     {
268         throw std::runtime_error("class does not support soundgen look up");
269     }
270 
getInventoryIcon(const MWWorld::ConstPtr & ptr) const271     std::string Class::getInventoryIcon (const MWWorld::ConstPtr& ptr) const
272     {
273         throw std::runtime_error ("class does not have any inventory icon");
274     }
275 
getToolTipInfo(const ConstPtr & ptr,int count) const276     MWGui::ToolTipInfo Class::getToolTipInfo (const ConstPtr& ptr, int count) const
277     {
278         throw std::runtime_error ("class does not have a tool tip");
279     }
280 
showsInInventory(const ConstPtr & ptr) const281     bool Class::showsInInventory (const ConstPtr& ptr) const
282     {
283         // NOTE: Don't show WerewolfRobe objects in the inventory, or allow them to be taken.
284         // Vanilla likely uses a hack like this since there's no other way to prevent it from
285         // being shown or taken.
286         return (ptr.getCellRef().getRefId() != "werewolfrobe");
287     }
288 
hasToolTip(const ConstPtr & ptr) const289     bool Class::hasToolTip (const ConstPtr& ptr) const
290     {
291         return true;
292     }
293 
getEnchantment(const ConstPtr & ptr) const294     std::string Class::getEnchantment (const ConstPtr& ptr) const
295     {
296         return "";
297     }
298 
adjustScale(const MWWorld::ConstPtr & ptr,osg::Vec3f & scale,bool rendering) const299     void Class::adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const
300     {
301     }
302 
getModel(const MWWorld::ConstPtr & ptr) const303     std::string Class::getModel(const MWWorld::ConstPtr &ptr) const
304     {
305         return "";
306     }
307 
useAnim() const308     bool Class::useAnim() const
309     {
310         return false;
311     }
312 
getModelsToPreload(const Ptr & ptr,std::vector<std::string> & models) const313     void Class::getModelsToPreload(const Ptr &ptr, std::vector<std::string> &models) const
314     {
315         std::string model = getModel(ptr);
316         if (!model.empty())
317             models.push_back(model);
318     }
319 
applyEnchantment(const MWWorld::ConstPtr & ptr,const std::string & enchId,int enchCharge,const std::string & newName) const320     std::string Class::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const
321     {
322         throw std::runtime_error ("class can't be enchanted");
323     }
324 
canBeEquipped(const MWWorld::ConstPtr & ptr,const MWWorld::Ptr & npc) const325     std::pair<int, std::string> Class::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const
326     {
327         return std::make_pair (1, "");
328     }
329 
adjustPosition(const MWWorld::Ptr & ptr,bool force) const330     void Class::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
331     {
332     }
333 
defaultItemActivate(const Ptr & ptr,const Ptr & actor) const334     std::shared_ptr<Action> Class::defaultItemActivate(const Ptr &ptr, const Ptr &actor) const
335     {
336         if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
337             return std::shared_ptr<Action>(new NullAction());
338 
339         if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
340         {
341             const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
342             const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfItem");
343 
344             std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
345             if(sound) action->setSound(sound->mId);
346 
347             return action;
348         }
349 
350         std::shared_ptr<MWWorld::Action> action(new ActionTake(ptr));
351         action->setSound(getUpSoundId(ptr));
352 
353         return action;
354     }
355 
356     MWWorld::Ptr
copyToCellImpl(const ConstPtr & ptr,CellStore & cell) const357     Class::copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const
358     {
359         throw std::runtime_error("unable to copy class to cell");
360     }
361 
362     MWWorld::Ptr
copyToCell(const ConstPtr & ptr,CellStore & cell,int count) const363     Class::copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const
364     {
365         Ptr newPtr = copyToCellImpl(ptr, cell);
366         newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference
367         newPtr.getRefData().setCount(count);
368         return newPtr;
369     }
370 
371     MWWorld::Ptr
copyToCell(const ConstPtr & ptr,CellStore & cell,const ESM::Position & pos,int count) const372     Class::copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const
373     {
374         Ptr newPtr = copyToCell(ptr, cell, count);
375         newPtr.getRefData().setPosition(pos);
376 
377         return newPtr;
378     }
379 
isBipedal(const ConstPtr & ptr) const380     bool Class::isBipedal(const ConstPtr &ptr) const
381     {
382         return false;
383     }
384 
canFly(const ConstPtr & ptr) const385     bool Class::canFly(const ConstPtr &ptr) const
386     {
387         return false;
388     }
389 
canSwim(const ConstPtr & ptr) const390     bool Class::canSwim(const ConstPtr &ptr) const
391     {
392         return false;
393     }
394 
canWalk(const ConstPtr & ptr) const395     bool Class::canWalk(const ConstPtr &ptr) const
396     {
397         return false;
398     }
399 
isPureWaterCreature(const ConstPtr & ptr) const400     bool Class::isPureWaterCreature(const ConstPtr& ptr) const
401     {
402         return canSwim(ptr)
403                 && !isBipedal(ptr)
404                 && !canFly(ptr)
405                 && !canWalk(ptr);
406     }
407 
isPureFlyingCreature(const ConstPtr & ptr) const408     bool Class::isPureFlyingCreature(const ConstPtr& ptr) const
409     {
410         return canFly(ptr)
411                 && !isBipedal(ptr)
412                 && !canSwim(ptr)
413                 && !canWalk(ptr);
414     }
415 
isPureLandCreature(const Ptr & ptr) const416     bool Class::isPureLandCreature(const Ptr& ptr) const
417     {
418         return canWalk(ptr)
419                 && !isBipedal(ptr)
420                 && !canFly(ptr)
421                 && !canSwim(ptr);
422     }
423 
isMobile(const MWWorld::Ptr & ptr) const424     bool Class::isMobile(const MWWorld::Ptr& ptr) const
425     {
426         return canSwim(ptr) || canWalk(ptr) || canFly(ptr);
427     }
428 
getSkill(const MWWorld::Ptr & ptr,int skill) const429     float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const
430     {
431         throw std::runtime_error("class does not support skills");
432     }
433 
getBloodTexture(const MWWorld::ConstPtr & ptr) const434     int Class::getBloodTexture (const MWWorld::ConstPtr& ptr) const
435     {
436         throw std::runtime_error("class does not support gore");
437     }
438 
readAdditionalState(const MWWorld::Ptr & ptr,const ESM::ObjectState & state) const439     void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {}
440 
writeAdditionalState(const MWWorld::ConstPtr & ptr,ESM::ObjectState & state) const441     void Class::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const {}
442 
getBaseGold(const MWWorld::ConstPtr & ptr) const443     int Class::getBaseGold(const MWWorld::ConstPtr& ptr) const
444     {
445         throw std::runtime_error("class does not support base gold");
446     }
447 
isClass(const MWWorld::ConstPtr & ptr,const std::string & className) const448     bool Class::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const
449     {
450         return false;
451     }
452 
getDoorState(const MWWorld::ConstPtr & ptr) const453     MWWorld::DoorState Class::getDoorState (const MWWorld::ConstPtr &ptr) const
454     {
455         throw std::runtime_error("this is not a door");
456     }
457 
setDoorState(const MWWorld::Ptr & ptr,MWWorld::DoorState state) const458     void Class::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const
459     {
460         throw std::runtime_error("this is not a door");
461     }
462 
getNormalizedEncumbrance(const Ptr & ptr) const463     float Class::getNormalizedEncumbrance(const Ptr &ptr) const
464     {
465         float capacity = getCapacity(ptr);
466         float encumbrance = getEncumbrance(ptr);
467 
468         if (encumbrance == 0)
469             return 0.f;
470 
471         if (capacity == 0)
472             return 1.f;
473 
474         return encumbrance / capacity;
475     }
476 
getSound(const MWWorld::ConstPtr &) const477     std::string Class::getSound(const MWWorld::ConstPtr&) const
478     {
479       return std::string();
480     }
481 
getBaseFightRating(const ConstPtr & ptr) const482     int Class::getBaseFightRating(const ConstPtr &ptr) const
483     {
484         throw std::runtime_error("class does not support fight rating");
485     }
486 
getPrimaryFaction(const MWWorld::ConstPtr & ptr) const487     std::string Class::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const
488     {
489         return std::string();
490     }
getPrimaryFactionRank(const MWWorld::ConstPtr & ptr) const491     int Class::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const
492     {
493         return -1;
494     }
495 
getEffectiveArmorRating(const ConstPtr & armor,const Ptr & actor) const496     float Class::getEffectiveArmorRating(const ConstPtr &armor, const Ptr &actor) const
497     {
498         throw std::runtime_error("class does not support armor ratings");
499     }
500 
getEnchantmentColor(const MWWorld::ConstPtr & item) const501     osg::Vec4f Class::getEnchantmentColor(const MWWorld::ConstPtr& item) const
502     {
503         osg::Vec4f result(1,1,1,1);
504         std::string enchantmentName = item.getClass().getEnchantment(item);
505         if (enchantmentName.empty())
506             return result;
507 
508         const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentName);
509         if (!enchantment)
510             return result;
511 
512         assert (enchantment->mEffects.mList.size());
513 
514         const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().search(
515                 enchantment->mEffects.mList.front().mEffectID);
516         if (!magicEffect)
517             return result;
518 
519         result.x() = magicEffect->mData.mRed / 255.f;
520         result.y() = magicEffect->mData.mGreen / 255.f;
521         result.z() = magicEffect->mData.mBlue / 255.f;
522         return result;
523     }
524 
setBaseAISetting(const std::string & id,MWMechanics::CreatureStats::AiSetting setting,int value) const525     void Class::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const
526     {
527         throw std::runtime_error ("class does not have creature stats");
528     }
529 
modifyBaseInventory(const std::string & actorId,const std::string & itemId,int amount) const530     void Class::modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const
531     {
532         throw std::runtime_error ("class does not have an inventory store");
533     }
534 
getWalkSpeed(const Ptr &) const535     float Class::getWalkSpeed(const Ptr& /*ptr*/) const
536     {
537         return 0;
538     }
539 
getRunSpeed(const Ptr &) const540     float Class::getRunSpeed(const Ptr& /*ptr*/) const
541     {
542         return 0;
543     }
544 
getSwimSpeed(const Ptr &) const545     float Class::getSwimSpeed(const Ptr& /*ptr*/) const
546     {
547         return 0;
548     }
549 }
550