diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 411fefdd8..c4b3776ed 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -55,7 +55,7 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanager stat creaturestats magiceffects movement actors drawstate + mechanicsmanager stat creaturestats magiceffects movement actors drawstate spells ) # Main executable diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 83a94d27d..57cc3560a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -61,6 +61,10 @@ namespace MWClass for (int i=0; i<27; ++i) data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]); + for (std::vector::const_iterator iter (ref->base->spells.list.begin()); + iter!=ref->base->spells.list.end(); ++iter) + data->mCreatureStats.mSpells.add (*iter); + // creature stats data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength); data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d2edc031d..ab008da9e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -6,6 +6,7 @@ #include "stat.hpp" #include "magiceffects.hpp" +#include "spells.hpp" namespace MWMechanics { @@ -14,7 +15,7 @@ namespace MWMechanics Stat mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; - std::set mAbilities; + Spells mSpells; MagicEffects mMagicEffects; }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index 3c93857ef..f5711e78e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -23,7 +23,7 @@ namespace MWMechanics // reset creatureStats.mLevel = player->npdt52.level; - creatureStats.mAbilities.clear(); + creatureStats.mSpells.clear(); creatureStats.mMagicEffects = MagicEffects(); for (int i=0; i<27; ++i) @@ -71,7 +71,7 @@ namespace MWMechanics for (std::vector::const_iterator iter (race->powers.list.begin()); iter!=race->powers.list.end(); ++iter) { - insertSpell (*iter, ptr); + creatureStats.mSpells.add (*iter); } } @@ -85,7 +85,7 @@ namespace MWMechanics for (std::vector::const_iterator iter (sign->powers.list.begin()); iter!=sign->powers.list.end(); ++iter) { - insertSpell (*iter, ptr); + creatureStats.mSpells.add (*iter); } } @@ -159,59 +159,14 @@ namespace MWMechanics creatureStats.mDynamic[i].setCurrent (creatureStats.mDynamic[i].getModified()); } - void MechanicsManager::insertSpell (const std::string& id, MWWorld::Ptr& creature) - { - MWMechanics::CreatureStats& creatureStats = - MWWorld::Class::get (creature).getCreatureStats (creature); - - const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (id); - - switch (spell->data.type) - { - case ESM::Spell::ST_Ability: - - if (creatureStats.mAbilities.find (id)==creatureStats.mAbilities.end()) - { - creatureStats.mAbilities.insert (id); - } - - break; - - // TODO ST_SPELL, ST_Blight, ST_Disease, ST_Curse, ST_Power - - default: - - std::cout - << "adding unsupported spell type (" << spell->data.type - << ") to creature: " << id << std::endl; - } - } - void MechanicsManager::adjustMagicEffects (MWWorld::Ptr& creature) { MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (creature).getCreatureStats (creature); - MagicEffects now; - - for (std::set::const_iterator iter (creatureStats.mAbilities.begin()); - iter!=creatureStats.mAbilities.end(); ++iter) - { - const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (*iter); - - for (std::vector::const_iterator iter = spell->effects.list.begin(); - iter!=spell->effects.list.end(); ++iter) - { - if (iter->range==0) // self - { - EffectParam param; - param.mMagnitude = iter->magnMax; // TODO calculate magnitude - now.add (EffectKey (*iter), param); - } - } - } + MagicEffects now = creatureStats.mSpells.getMagicEffects (mEnvironment); - // TODO add effects from other spell types, active spells and equipment + /// \todo add effects from active spells and equipment MagicEffects diff = MagicEffects::diff (creatureStats.mMagicEffects, now); diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanager.hpp index a7defe178..a121507ce 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.hpp @@ -37,8 +37,6 @@ namespace MWMechanics ///< build player according to stored class/race/birthsign information. Will /// default to the values of the ESM::NPC object, if no explicit information is given. - void insertSpell (const std::string& id, MWWorld::Ptr& creature); - void adjustMagicEffects (MWWorld::Ptr& creature); public: diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 7c5d37972..feac5d4d3 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -2,6 +2,7 @@ #define GAME_MWMECHANICS_NPCSTATS_H #include +#include #include "stat.hpp" #include "drawstate.hpp" @@ -11,6 +12,10 @@ namespace MWMechanics /// \brief Additional stats for NPCs /// /// For non-NPC-specific stats, see the CreatureStats struct. + /// + /// \note For technical reasons the spell list and the currently selected spell is also handled by + /// CreatureStats, even though they are actually NPC stats. + struct NpcStats { // NPCs other than the player can only have one faction. But for the sake of consistency diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp new file mode 100644 index 000000000..916239a84 --- /dev/null +++ b/apps/openmw/mwmechanics/spells.cpp @@ -0,0 +1,81 @@ + +#include "spells.hpp" + +#include + +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + +#include "magiceffects.hpp" + +namespace MWMechanics +{ + void Spells::addSpell (const ESM::Spell *spell, MagicEffects& effects) const + { + for (std::vector::const_iterator iter = spell->effects.list.begin(); + iter!=spell->effects.list.end(); ++iter) + { + EffectParam param; + param.mMagnitude = iter->magnMax; /// \todo calculate magnitude + effects.add (EffectKey (*iter), param); + } + } + + Spells::TIterator Spells::begin() const + { + return mSpells.begin(); + } + + Spells::TIterator Spells::end() const + { + return mSpells.end(); + } + + void Spells::add (const std::string& spellId) + { + if (std::find (mSpells.begin(), mSpells.end(), spellId)!=mSpells.end()) + mSpells.push_back (spellId); + } + + void Spells::remove (const std::string& spellId) + { + TContainer::iterator iter = std::find (mSpells.begin(), mSpells.end(), spellId); + + if (iter!=mSpells.end()) + mSpells.erase (iter); + + if (spellId==mSelectedSpell) + mSelectedSpell.clear(); + } + + MagicEffects Spells::getMagicEffects (const MWWorld::Environment& environment) const + { + MagicEffects effects; + + for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) + { + const ESM::Spell *spell = environment.mWorld->getStore().spells.find (*iter); + + if (spell->data.type==ESM::Spell::ST_Ability || spell->data.type==ESM::Spell::ST_Blight || + spell->data.type==ESM::Spell::ST_Disease || spell->data.type==ESM::Spell::ST_Curse) + addSpell (spell, effects); + } + + return effects; + } + + void Spells::clear() + { + mSpells.clear(); + } + + void Spells::setSelectedSpell (const std::string& spellId) + { + mSelectedSpell = spellId; + } + + const std::string Spells::getSelectedSpell() const + { + return mSelectedSpell; + } +} diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp new file mode 100644 index 000000000..f7606c4ac --- /dev/null +++ b/apps/openmw/mwmechanics/spells.hpp @@ -0,0 +1,66 @@ +#ifndef GAME_MWMECHANICS_SPELLS_H +#define GAME_MWMECHANICS_SPELLS_H + +#include +#include + +namespace ESM +{ + struct Spell; +} + +namespace MWWorld +{ + struct Environment; +} + +namespace MWMechanics +{ + class MagicEffects; + + /// \brief Spell list + /// + /// This class manages known spells as well as abilities, powers and permanent negative effects like + /// diseaes. + class Spells + { + public: + + typedef std::vector TContainer; + typedef TContainer::const_iterator TIterator; + + private: + + std::vector mSpells; + std::string mSelectedSpell; + + void addSpell (const ESM::Spell *, MagicEffects& effects) const; + + public: + + TIterator begin() const; + + TIterator end() const; + + void add (const std::string& spell); + ///< Adding a spell that is already listed in *this is a no-op. + + void remove (const std::string& spell); + ///< If the spell to be removed is the selected spell, the selected spell will be changed to + /// no spell (empty string). + + MagicEffects getMagicEffects (const MWWorld::Environment& environment) const; + ///< Return sum of magic effects resulting from abilities, blights, deseases and curses. + + void clear(); + ///< Remove all spells of al types. + + void setSelectedSpell (const std::string& spellId); + ///< This function does not verify, if the spell is available. + + const std::string getSelectedSpell() const; + ///< May return an empty string. + }; +} + +#endif diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index df955ec19..705d65814 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -129,4 +129,8 @@ op 0x2000143: ModWaterLevel op 0x2000144: ToggleWater, twa op 0x2000145: ToggleFogOfWar (tfow) op 0x2000146: TogglePathgrid -opcodes 0x2000147-0x3ffffff unused +op 0x2000147: AddSpell +op 0x2000148: AddSpell, explicit reference +op 0x2000149: RemoveSpell +op 0x200014a: RemoveSpell, explicit reference +opcodes 0x200014b-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 0e97a39cf..10641903e 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -280,6 +280,38 @@ namespace MWScript } }; + template + class OpAddSpell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.add (id); + } + }; + + template + class OpRemoveSpell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.remove (id); + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -311,6 +343,11 @@ namespace MWScript const int opcodeModSkill = 0x20000fa; const int opcodeModSkillExplicit = 0x2000115; + const int opcodeAddSpell = 0x2000147; + const int opcodeAddSpellExplicit = 0x2000148; + const int opcodeRemoveSpell = 0x2000149; + const int opcodeRemoveSpellExplicit = 0x200014a; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -381,6 +418,10 @@ namespace MWScript extensions.registerInstruction (mod + skills[i], "l", opcodeModSkill+i, opcodeModSkillExplicit+i); } + + extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, + opcodeRemoveSpellExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -436,6 +477,12 @@ namespace MWScript interpreter.installSegment5 (opcodeModSkill+i, new OpModSkill (i)); interpreter.installSegment5 (opcodeModSkillExplicit+i, new OpModSkill (i)); } + + interpreter.installSegment5 (opcodeAddSpell, new OpAddSpell); + interpreter.installSegment5 (opcodeAddSpellExplicit, new OpAddSpell); + interpreter.installSegment5 (opcodeRemoveSpell, new OpRemoveSpell); + interpreter.installSegment5 (opcodeRemoveSpellExplicit, + new OpRemoveSpell); } } }