diff --git a/CMakeLists.txt b/CMakeLists.txt index 614a840c4a..b10562eef2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -306,7 +306,7 @@ endif() # Compiler settings if (CMAKE_COMPILER_IS_GNUCC) - add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder) + add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++0x -pedantic) # Silence warnings in OGRE headers. Remove once OGRE got fixed! add_definitions (-Wno-ignored-qualifiers) diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index f48aa41bf8..5c588fb296 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -1,5 +1,7 @@ set(ESMTOOL esmtool.cpp + labels.hpp + labels.cpp record.hpp record.cpp ) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 88709f58b1..4f6d9dbfc0 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -13,7 +13,7 @@ #include "record.hpp" -#define ESMTOOL_VERSION 1.1 +#define ESMTOOL_VERSION 1.2 // Create a local alias for brevity namespace bpo = boost::program_options; @@ -57,6 +57,8 @@ struct Arguments std::string encoding; std::string filename; std::string outname; + + std::vector types; ESMData data; ESM::ESMReader reader; @@ -70,7 +72,12 @@ bool parseOptions (int argc, char** argv, Arguments &info) desc.add_options() ("help,h", "print help message.") ("version,v", "print version information and quit.") - ("raw,r", "Show an unformattet list of all records and subrecords.") + ("raw,r", "Show an unformatted list of all records and subrecords.") + // The intention is that this option would interact better + // with other modes including clone, dump, and raw. + ("type,t", bpo::value< std::vector >(), + "Show only records of this type (four character record code). May " + "be specified multiple times. Only affects dump mode.") ("quiet,q", "Supress all record information. Useful for speed tests.") ("loadcells,C", "Browse through contents of all cells.") @@ -122,6 +129,9 @@ bool parseOptions (int argc, char** argv, Arguments &info) return false; } + if (variables.count("type") > 0) + info.types = variables["type"].as< std::vector >(); + info.mode = variables["mode"].as(); if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp")) { @@ -306,14 +316,23 @@ int load(Arguments& info) uint32_t flags; esm.getRecHeader(flags); + // Is the user interested in this record type? + bool interested = true; + if (info.types.size() > 0) + { + std::vector::iterator match; + match = std::find(info.types.begin(), info.types.end(), + n.toString()); + if (match == info.types.end()) interested = false; + } + std::string id = esm.getHNOString("NAME"); - if(!quiet) + if(!quiet && interested) std::cout << "\nRecord: " << n.toString() << " '" << id << "'\n"; - EsmTool::RecordBase *record = - EsmTool::RecordBase::create(n.val); + EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); if (record == 0) { if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end()) @@ -326,18 +345,16 @@ int load(Arguments& info) if (quiet) break; std::cout << " Skipping\n"; } else { - if (record->getType() == ESM::REC_GMST) { + if (record->getType().val == ESM::REC_GMST) { // preset id for GameSetting record record->cast()->get().mId = id; } record->setId(id); record->setFlags((int) flags); record->load(esm); - if (!quiet) { - record->print(); - } + if (!quiet && interested) record->print(); - if (record->getType() == ESM::REC_CELL && loadCells) { + if (record->getType().val == ESM::REC_CELL && loadCells) { loadCell(record->cast()->get(), esm, info); } @@ -434,7 +451,7 @@ int clone(Arguments& info) { EsmTool::RecordBase *record = *it; - name.val = record->getType(); + name.val = record->getType().val; esm.startRecord(name.toString(), record->getFlags()); diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp new file mode 100644 index 0000000000..9fb2332379 --- /dev/null +++ b/apps/esmtool/labels.cpp @@ -0,0 +1,881 @@ +#include "labels.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +std::string bodyPartLabel(char idx) +{ + const char *bodyPartLabels[] = { + "Head", + "Hair", + "Neck", + "Cuirass", + "Groin", + "Skirt", + "Right Hand", + "Left Hand", + "Right Wrist", + "Left Wrist", + "Shield", + "Right Forearm", + "Left Forearm", + "Right Upperarm", + "Left Upperarm", + "Right Foot", + "Left Foot", + "Right Ankle", + "Left Ankle", + "Right Knee", + "Left Knee", + "Right Leg", + "Left Leg", + "Right Shoulder", + "Left Shoulder", + "Weapon", + "Tail" + }; + + if ((int)idx >= 0 && (int)(idx) <= 26) + return bodyPartLabels[(int)(idx)]; + else + return "Invalid"; +} + +std::string meshPartLabel(char idx) +{ + const char *meshPartLabels[] = { + "Head", + "Hair", + "Neck", + "Chest", + "Groin", + "Hand", + "Wrist", + "Forearm", + "Upperarm", + "Foot", + "Ankle", + "Knee", + "Upper Leg", + "Clavicle", + "Tail" + }; + + if ((int)(idx) >= 0 && (int)(idx) <= ESM::BodyPart::MP_Tail) + return meshPartLabels[(int)(idx)]; + else + return "Invalid"; +} + +std::string meshTypeLabel(char idx) +{ + const char *meshTypeLabels[] = { + "Skin", + "Clothing", + "Armor" + }; + + if ((int)(idx) >= 0 && (int)(idx) <= ESM::BodyPart::MT_Armor) + return meshTypeLabels[(int)(idx)]; + else + return "Invalid"; +} + +std::string clothingTypeLabel(int idx) +{ + const char *clothingTypeLabels[] = { + "Pants", + "Shoes", + "Shirt", + "Belt", + "Robe", + "Right Glove", + "Left Glove", + "Skirt", + "Ring", + "Amulet" + }; + + if (idx >= 0 && idx <= 9) + return clothingTypeLabels[idx]; + else + return "Invalid"; +} + +std::string armorTypeLabel(int idx) +{ + const char *armorTypeLabels[] = { + "Helmet", + "Cuirass", + "Left Pauldron", + "Right Pauldron", + "Greaves", + "Boots", + "Left Gauntlet", + "Right Gauntlet", + "Shield", + "Left Bracer", + "Right Bracer" + }; + + if (idx >= 0 && idx <= 10) + return armorTypeLabels[idx]; + else + return "Invalid"; +} + +std::string dialogTypeLabel(int idx) +{ + const char *dialogTypeLabels[] = { + "Topic", + "Voice", + "Greeting", + "Persuasion", + "Journal" + }; + + if (idx >= 0 && idx <= 4) + return dialogTypeLabels[idx]; + else if (idx == -1) + return "Deleted"; + else + return "Invalid"; +} + +std::string questStatusLabel(int idx) +{ + const char *questStatusLabels[] = { + "None", + "Name", + "Finished", + "Restart", + "Deleted" + }; + + if (idx >= 0 && idx <= 4) + return questStatusLabels[idx]; + else + return "Invalid"; +} + +std::string creatureTypeLabel(int idx) +{ + const char *creatureTypeLabels[] = { + "Creature", + "Daedra", + "Undead", + "Humanoid", + }; + + if (idx >= 0 && idx <= 3) + return creatureTypeLabels[idx]; + else + return "Invalid"; +} + +std::string soundTypeLabel(int idx) +{ + const char *soundTypeLabels[] = { + "Left Foot", + "Right Foot", + "Swim Left", + "Swim Right", + "Moan", + "Roar", + "Scream", + "Land" + }; + + if (idx >= 0 && idx <= 7) + return soundTypeLabels[idx]; + else + return "Invalid"; +} + +std::string weaponTypeLabel(int idx) +{ + const char *weaponTypeLabels[] = { + "Short Blade One Hand", + "Long Blade One Hand", + "Long Blade Two Hand", + "Blunt One Hand", + "Blunt Two Close", + "Blunt Two Wide", + "Spear Two Wide", + "Axe One Hand", + "Axe Two Hand", + "Marksman Bow", + "Marksman Crossbow", + "Marksman Thrown", + "Arrow", + "Bolt" + }; + + if (idx >= 0 && idx <= 13) + return weaponTypeLabels[idx]; + else + return "Invalid"; +} + +std::string aiTypeLabel(int type) +{ + if (type == ESM::AI_Wander) return "Wander"; + else if (type == ESM::AI_Travel) return "Travel"; + else if (type == ESM::AI_Follow) return "Follow"; + else if (type == ESM::AI_Escort) return "Escort"; + else if (type == ESM::AI_Activate) return "Activate"; + else return "Invalid"; +} + +std::string magicEffectLabel(int idx) +{ + const char* magicEffectLabels [] = { + "Water Breathing", + "Swift Swim", + "Water Walking", + "Shield", + "Fire Shield", + "Lightning Shield", + "Frost Shield", + "Burden", + "Feather", + "Jump", + "Levitate", + "SlowFall", + "Lock", + "Open", + "Fire Damage", + "Shock Damage", + "Frost Damage", + "Drain Attribute", + "Drain Health", + "Drain Magicka", + "Drain Fatigue", + "Drain Skill", + "Damage Attribute", + "Damage Health", + "Damage Magicka", + "Damage Fatigue", + "Damage Skill", + "Poison", + "Weakness to Fire", + "Weakness to Frost", + "Weakness to Shock", + "Weakness to Magicka", + "Weakness to Common Disease", + "Weakness to Blight Disease", + "Weakness to Corprus Disease", + "Weakness to Poison", + "Weakness to Normal Weapons", + "Disintegrate Weapon", + "Disintegrate Armor", + "Invisibility", + "Chameleon", + "Light", + "Sanctuary", + "Night Eye", + "Charm", + "Paralyze", + "Silence", + "Blind", + "Sound", + "Calm Humanoid", + "Calm Creature", + "Frenzy Humanoid", + "Frenzy Creature", + "Demoralize Humanoid", + "Demoralize Creature", + "Rally Humanoid", + "Rally Creature", + "Dispel", + "Soultrap", + "Telekinesis", + "Mark", + "Recall", + "Divine Intervention", + "Almsivi Intervention", + "Detect Animal", + "Detect Enchantment", + "Detect Key", + "Spell Absorption", + "Reflect", + "Cure Common Disease", + "Cure Blight Disease", + "Cure Corprus Disease", + "Cure Poison", + "Cure Paralyzation", + "Restore Attribute", + "Restore Health", + "Restore Magicka", + "Restore Fatigue", + "Restore Skill", + "Fortify Attribute", + "Fortify Health", + "Fortify Magicka", + "Fortify Fatigue", + "Fortify Skill", + "Fortify Maximum Magicka", + "Absorb Attribute", + "Absorb Health", + "Absorb Magicka", + "Absorb Fatigue", + "Absorb Skill", + "Resist Fire", + "Resist Frost", + "Resist Shock", + "Resist Magicka", + "Resist Common Disease", + "Resist Blight Disease", + "Resist Corprus Disease", + "Resist Poison", + "Resist Normal Weapons", + "Resist Paralysis", + "Remove Curse", + "Turn Undead", + "Summon Scamp", + "Summon Clannfear", + "Summon Daedroth", + "Summon Dremora", + "Summon Ancestral Ghost", + "Summon Skeletal Minion", + "Summon Bonewalker", + "Summon Greater Bonewalker", + "Summon Bonelord", + "Summon Winged Twilight", + "Summon Hunger", + "Summon Golden Saint", + "Summon Flame Atronach", + "Summon Frost Atronach", + "Summon Storm Atronach", + "Fortify Attack", + "Command Creature", + "Command Humanoid", + "Bound Dagger", + "Bound Longsword", + "Bound Mace", + "Bound Battle Axe", + "Bound Spear", + "Bound Longbow", + "EXTRA SPELL", + "Bound Cuirass", + "Bound Helm", + "Bound Boots", + "Bound Shield", + "Bound Gloves", + "Corprus", + "Vampirism", + "Summon Centurion Sphere", + "Sun Damage", + "Stunted Magicka", + "Summon Fabricant", + "sEffectSummonCreature01", + "sEffectSummonCreature02", + "sEffectSummonCreature03", + "sEffectSummonCreature04", + "sEffectSummonCreature05" + }; + if (idx >= 0 && idx <= 143) + return magicEffectLabels[idx]; + else + return "Invalid"; +} + +std::string attributeLabel(int idx) +{ + const char* attributeLabels [] = { + "Strength", + "Intelligence", + "Willpower", + "Agility", + "Speed", + "Endurance", + "Personality", + "Luck" + }; + if (idx >= 0 && idx <= 7) + return attributeLabels[idx]; + else + return "Invalid"; +} + +std::string spellTypeLabel(int idx) +{ + const char* spellTypeLabels [] = { + "Spells", + "Abilities", + "Blight Disease", + "Disease", + "Curse", + "Powers" + }; + if (idx >= 0 && idx <= 5) + return spellTypeLabels[idx]; + else + return "Invalid"; +} + +std::string specializationLabel(int idx) +{ + const char* specializationLabels [] = { + "Combat", + "Magic", + "Stealth" + }; + if (idx >= 0 && idx <= 2) + return specializationLabels[idx]; + else + return "Invalid"; +} + +std::string skillLabel(int idx) +{ + const char* skillLabels [] = { + "Block", + "Armorer", + "Medium Armor", + "Heavy Armor", + "Blunt Weapon", + "Long Blade", + "Axe", + "Spear", + "Athletics", + "Enchant", + "Destruction", + "Alteration", + "Illusion", + "Conjuration", + "Mysticism", + "Restoration", + "Alchemy", + "Unarmored", + "Security", + "Sneak", + "Acrobatics", + "Light Armor", + "Short Blade", + "Marksman", + "Mercantile", + "Speechcraft", + "Hand-to-hand" + }; + if (idx >= 0 && idx <= 27) + return skillLabels[idx]; + else + return "Invalid"; +} + +std::string apparatusTypeLabel(int idx) +{ + const char* apparatusTypeLabels [] = { + "Mortar", + "Alembic", + "Calcinator", + "Retort", + }; + if (idx >= 0 && idx <= 3) + return apparatusTypeLabels[idx]; + else + return "Invalid"; +} + +std::string rangeTypeLabel(int idx) +{ + const char* rangeTypeLabels [] = { + "Self", + "Touch", + "Target" + }; + if (idx >= 0 && idx <= 3) + return rangeTypeLabels[idx]; + else + return "Invalid"; +} + +std::string schoolLabel(int idx) +{ + const char* schoolLabels [] = { + "Alteration", + "Conjuration", + "Destruction", + "Illusion", + "Mysticism", + "Restoration" + }; + if (idx >= 0 && idx <= 5) + return schoolLabels[idx]; + else + return "Invalid"; +} + +std::string enchantTypeLabel(int idx) +{ + const char* enchantTypeLabels [] = { + "Cast Once", + "Cast When Strikes", + "Cast When Used", + "Constant Effect" + }; + if (idx >= 0 && idx <= 3) + return enchantTypeLabels[idx]; + else + return "Invalid"; +} + +std::string ruleFunction(int idx) +{ + std::string ruleFunctions[] = { + "Reaction Low", + "Reaction High", + "Rank Requirement", + "NPC? Reputation", + "Health Percent", + "Player Reputation", + "NPC Level", + "Player Health Percent", + "Player Magicka", + "Player Fatigue", + "Player Attribute Strength", + "Player Skill Block", + "Player Skill Armorer", + "Player Skill Medium Armor", + "Player Skill Heavy Armor", + "Player Skill Blunt Weapon", + "Player Skill Long Blade", + "Player Skill Axe", + "Player Skill Spear", + "Player Skill Athletics", + "Player Skill Enchant", + "Player Skill Destruction", + "Player Skill Alteration", + "Player Skill Illusion", + "Player Skill Conjuration", + "Player Skill Mysticism", + "Player SKill Restoration", + "Player Skill Alchemy", + "Player Skill Unarmored", + "Player Skill Security", + "Player Skill Sneak", + "Player Skill Acrobatics", + "Player Skill Light Armor", + "Player Skill Short Blade", + "Player Skill Marksman", + "Player Skill Mercantile", + "Player Skill Speechcraft", + "Player Skill Hand to Hand", + "Player Gender", + "Player Expelled from Faction", + "Player Diseased (Common)", + "Player Diseased (Blight)", + "Player Clothing Modifier", + "Player Crime Level", + "Player Same Sex", + "Player Same Race", + "Player Same Faction", + "Faction Rank Difference", + "Player Detected", + "Alarmed", + "Choice Selected", + "Player Attribute Intelligence", + "Player Attribute Willpower", + "Player Attribute Agility", + "Player Attribute Speed", + "Player Attribute Endurance", + "Player Attribute Personality", + "Player Attribute Luck", + "Player Diseased (Corprus)", + "Weather", + "Player is a Vampire", + "Player Level", + "Attacked", + "NPC Talked to Player", + "Player Health", + "Creature Target", + "Friend Hit", + "Fight", + "Hello", + "Alarm", + "Flee", + "Should Attack", + //Unkown but causes NPCs to growl and roar. + "UNKNOWN 72" + }; + if (idx >= 0 && idx <= 72) + return ruleFunctions[idx]; + else + return "Invalid"; +} + +// The "unused flag bits" should probably be defined alongside the +// defined bits in the ESM component. The names of the flag bits are +// very inconsistent. + +std::string bodyPartFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; + if (flags & ESM::BodyPart::BPF_Playable) properties += "Playable "; + int unused = (0xFFFFFFFF ^ + (ESM::BodyPart::BPF_Female| + ESM::BodyPart::BPF_Playable)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string cellFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::Cell::HasWater) properties += "HasWater "; + if (flags & ESM::Cell::Interior) properties += "Interior "; + if (flags & ESM::Cell::NoSleep) properties += "NoSleep "; + if (flags & ESM::Cell::QuasiEx) properties += "QuasiEx "; + // This used value is not in the ESM component. + if (flags & 0x00000040) properties += "Unknown "; + int unused = (0xFFFFFFFF ^ + (ESM::Cell::HasWater| + ESM::Cell::Interior| + ESM::Cell::NoSleep| + ESM::Cell::QuasiEx| + 0x00000040)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string containerFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::Container::Unknown) properties += "Unknown "; + if (flags & ESM::Container::Organic) properties += "Organic "; + if (flags & ESM::Container::Respawn) properties += "Respawn "; + int unused = (0xFFFFFFFF ^ + (ESM::Container::Unknown| + ESM::Container::Organic| + ESM::Container::Respawn)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string creatureFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::Creature::None) properties += "All "; + if (flags & ESM::Creature::Walks) properties += "Walks "; + if (flags & ESM::Creature::Swims) properties += "Swims "; + if (flags & ESM::Creature::Flies) properties += "Flies "; + if (flags & ESM::Creature::Biped) properties += "Biped "; + if (flags & ESM::Creature::Respawn) properties += "Respawn "; + if (flags & ESM::Creature::Weapon) properties += "Weapon "; + if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; + if (flags & ESM::Creature::Metal) properties += "Metal "; + if (flags & ESM::Creature::Essential) properties += "Essential "; + int unused = (0xFFFFFFFF ^ + (ESM::Creature::None| + ESM::Creature::Walks| + ESM::Creature::Swims| + ESM::Creature::Flies| + ESM::Creature::Biped| + ESM::Creature::Respawn| + ESM::Creature::Weapon| + ESM::Creature::Skeleton| + ESM::Creature::Metal| + ESM::Creature::Essential)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string landFlags(int flags) +{ + std::string properties = ""; + // The ESM component says that this first four bits are used, but + // only the first three bits are used as far as I can tell. + // There's also no enumeration of the bit in the ESM component. + if (flags == 0) properties += "[None] "; + if (flags & 0x00000001) properties += "Unknown1 "; + if (flags & 0x00000004) properties += "Unknown3 "; + if (flags & 0x00000002) properties += "Unknown2 "; + if (flags & 0xFFFFFFF8) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string leveledListFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels "; + // This flag apparently not present on creature lists... + if (flags & ESM::LeveledListBase::Each) properties += "Each "; + int unused = (0xFFFFFFFF ^ + (ESM::LeveledListBase::AllLevels| + ESM::LeveledListBase::Each)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string lightFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::Light::Dynamic) properties += "Dynamic "; + if (flags & ESM::Light::Fire) properties += "Fire "; + if (flags & ESM::Light::Carry) properties += "Carry "; + if (flags & ESM::Light::Flicker) properties += "Flicker "; + if (flags & ESM::Light::FlickerSlow) properties += "FlickerSlow "; + if (flags & ESM::Light::Pulse) properties += "Pulse "; + if (flags & ESM::Light::PulseSlow) properties += "PulseSlow "; + if (flags & ESM::Light::Negative) properties += "Negative "; + if (flags & ESM::Light::OffDefault) properties += "OffDefault "; + int unused = (0xFFFFFFFF ^ + (ESM::Light::Dynamic| + ESM::Light::Fire| + ESM::Light::Carry| + ESM::Light::Flicker| + ESM::Light::FlickerSlow| + ESM::Light::Pulse| + ESM::Light::PulseSlow| + ESM::Light::Negative| + ESM::Light::OffDefault)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string magicEffectFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + // Enchanting & SpellMaking occur on the same list of effects. + // "EXTRA SPELL" appears in the construction set under both the + // spell making and enchanting tabs as an allowed effect. Since + // most of the effects without this flags are defective in various + // ways, it's still very unclear what these flag bits are. + if (flags & ESM::MagicEffect::SpellMaking) properties += "SpellMaking "; + if (flags & ESM::MagicEffect::Enchanting) properties += "Enchanting "; + if (flags & 0x00000040) properties += "RangeNoSelf "; + if (flags & 0x00000080) properties += "RangeTouch "; + if (flags & 0x00000100) properties += "RangeTarget "; + if (flags & 0x00001000) properties += "Unknown2 "; + if (flags & 0x00000001) properties += "AffectSkill "; + if (flags & 0x00000002) properties += "AffectAttribute "; + if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration "; + if (flags & 0x00000008) properties += "NoMagnitude "; + if (flags & 0x00000010) properties += "Negative "; + if (flags & 0x00000020) properties += "Unknown1 "; + // ESM componet says 0x800 is negative, but none of the magic + // effects have this flags set. + if (flags & ESM::MagicEffect::Negative) properties += "Unused "; + // Since only Chameleon has this flag it could be anything + // that uniquely distinguishes Chameleon. + if (flags & 0x00002000) properties += "Chameleon "; + if (flags & 0x00004000) properties += "Bound "; + if (flags & 0x00008000) properties += "Summon "; + // Calm, Demoralize, Frenzy, Lock, Open, Rally, Soultrap, Turn Unded + if (flags & 0x00010000) properties += "Unknown3 "; + if (flags & 0x00020000) properties += "Absorb "; + if (flags & 0xFFFC0000) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string npcFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + // Mythicmods and the ESM component differ. Mythicmods says + // 0x8=None and 0x10=AutoCalc, while our code defines 0x8 as + // AutoCalc. The former seems to be correct. All Bethesda + // records have bit 0x8 set. A suspiciously large portion of + // females have autocalc turned off. + if (flags & ESM::NPC::Autocalc) properties += "Unknown "; + if (flags & 0x00000010) properties += "Autocalc "; + if (flags & ESM::NPC::Female) properties += "Female "; + if (flags & ESM::NPC::Respawn) properties += "Respawn "; + if (flags & ESM::NPC::Essential) properties += "Essential "; + // These two flags do not appear on any NPCs and may have been + // confused with the flags for creatures. + if (flags & ESM::NPC::Skeleton) properties += "Skeleton "; + if (flags & ESM::NPC::Metal) properties += "Metal "; + // Whether corpses persist is a bit that is unaccounted for, + // however the only unknown bit occurs on ALL records, and + // relatively few NPCs have this bit set. + int unused = (0xFFFFFFFF ^ + (ESM::NPC::Autocalc| + 0x00000010| + ESM::NPC::Female| + ESM::NPC::Respawn| + ESM::NPC::Essential| + ESM::NPC::Skeleton| + ESM::NPC::Metal)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string raceFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + // All races have the playable flag set in Bethesda files. + if (flags & ESM::Race::Playable) properties += "Playable "; + if (flags & ESM::Race::Beast) properties += "Beast "; + int unused = (0xFFFFFFFF ^ + (ESM::Race::Playable| + ESM::Race::Beast)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string spellFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::Spell::F_Autocalc) properties += "Autocalc "; + if (flags & ESM::Spell::F_PCStart) properties += "PCStart "; + if (flags & ESM::Spell::F_Always) properties += "Always "; + int unused = (0xFFFFFFFF ^ + (ESM::Spell::F_Autocalc| + ESM::Spell::F_PCStart| + ESM::Spell::F_Always)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string weaponFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + // The interpretation of the flags are still unclear to me. + // Apparently you can't be Silver without being Magical? Many of + // the "Magical" weapons don't have enchantments of any sort. + if (flags & ESM::Weapon::Magical) properties += "Magical "; + if (flags & ESM::Weapon::Silver) properties += "Silver "; + int unused = (0xFFFFFFFF ^ + (ESM::Weapon::Magical| + ESM::Weapon::Silver)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} diff --git a/apps/esmtool/labels.hpp b/apps/esmtool/labels.hpp new file mode 100644 index 0000000000..7f1ff79865 --- /dev/null +++ b/apps/esmtool/labels.hpp @@ -0,0 +1,64 @@ +#ifndef OPENMW_ESMTOOL_LABELS_H +#define OPENMW_ESMTOOL_LABELS_H + +#include + +std::string bodyPartLabel(char idx); +std::string meshPartLabel(char idx); +std::string meshTypeLabel(char idx); +std::string clothingTypeLabel(int idx); +std::string armorTypeLabel(int idx); +std::string dialogTypeLabel(int idx); +std::string questStatusLabel(int idx); +std::string creatureTypeLabel(int idx); +std::string soundTypeLabel(int idx); +std::string weaponTypeLabel(int idx); + +// This function's a bit different because the types are record types, +// not consecutive values. +std::string aiTypeLabel(int type); + +// This one's also a bit different, because it enumerates dialog +// select rule functions, not types. Structurally, it still converts +// indexes to strings for display. +std::string ruleFunction(int idx); + +// The labels below here can all be loaded from GMSTs, but are not +// currently because among other things, that requires loading the +// GMSTs before dumping any of the records. + +// If the data format supported ordered lists of GMSTs (post 1.0), the +// lists could define the valid values, their localization strings, +// and the indexes for referencing the types in other records in the +// database. Then a single label function could work for all types. + +std::string magicEffectLabel(int idx); +std::string attributeLabel(int idx); +std::string spellTypeLabel(int idx); +std::string specializationLabel(int idx); +std::string skillLabel(int idx); +std::string apparatusTypeLabel(int idx); +std::string rangeTypeLabel(int idx); +std::string schoolLabel(int idx); +std::string enchantTypeLabel(int idx); + +// The are the flag functions that convert a bitmask into a list of +// human readble strings representing the set bits. + +std::string bodyPartFlags(int flags); +std::string cellFlags(int flags); +std::string containerFlags(int flags); +std::string creatureFlags(int flags); +std::string landFlags(int flags); +std::string leveledListFlags(int flags); +std::string lightFlags(int flags); +std::string magicEffectFlags(int flags); +std::string npcFlags(int flags); +std::string raceFlags(int flags); +std::string spellFlags(int flags); +std::string weaponFlags(int flags); + +// Missing flags functions: +// aiServicesFlags, possibly more + +#endif diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 9609e52420..856700f5e3 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -1,15 +1,161 @@ #include "record.hpp" +#include "labels.hpp" #include +#include + +void printAIPackage(ESM::AIPackage p) +{ + std::cout << " AI Type: " << aiTypeLabel(p.mType) + << " (" << boost::format("0x%08X") % p.mType << ")" << std::endl; + if (p.mType == ESM::AI_Wander) + { + std::cout << " Distance: " << p.mWander.mDistance << std::endl; + std::cout << " Duration: " << p.mWander.mDuration << std::endl; + std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl; + if (p.mWander.mUnk != 1) + std::cout << " Unknown: " << (int)p.mWander.mUnk << std::endl; + + std::cout << " Idle: "; + for (int i = 0; i != 8; i++) + std::cout << (int)p.mWander.mIdle[i] << " "; + std::cout << std::endl; + } + else if (p.mType == ESM::AI_Travel) + { + std::cout << " Travel Coordinates: (" << p.mTravel.mX << "," + << p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl; + std::cout << " Travel Unknown: " << (int)p.mTravel.mUnk << std::endl; + } + else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort) + { + std::cout << " Follow Coordinates: (" << p.mTarget.mX << "," + << p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl; + std::cout << " Duration: " << p.mTarget.mDuration << std::endl; + std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl; + std::cout << " Unknown: " << (int)p.mTarget.mUnk << std::endl; + } + else if (p.mType == ESM::AI_Activate) + { + std::cout << " Name: " << p.mActivate.mName.toString() << std::endl; + std::cout << " Activate Unknown: " << (int)p.mActivate.mUnk << std::endl; + } + else { + std::cout << " BadPackage: " << boost::format("0x%08x") % p.mType << std::endl; + } + + if (p.mCellName != "") + std::cout << " Cell Name: " << p.mCellName << std::endl; +} + +std::string ruleString(ESM::DialInfo::SelectStruct ss) +{ + std::string rule = ss.mSelectRule; + + if (rule.length() < 5) + return "INVALID"; + + char type = rule[1]; + char indicator = rule[2]; + + std::string type_str = "INVALID"; + std::string func_str = str(boost::format("INVALID=%s") % rule.substr(1,3)); + int func; + std::istringstream iss(rule.substr(2,2)); + iss >> func; + + switch(type) + { + case '1': + type_str = "Function"; + func_str = ruleFunction(func); + break; + case '2': + if (indicator == 's') type_str = "Global short"; + else if (indicator == 'l') type_str = "Global long"; + else if (indicator == 'f') type_str = "Global float"; + break; + case '3': + if (indicator == 's') type_str = "Local short"; + else if (indicator == 'l') type_str = "Local long"; + else if (indicator == 'f') type_str = "Local float"; + break; + case '4': if (indicator == 'J') type_str = "Journal"; break; + case '5': if (indicator == 'I') type_str = "Item type"; break; + case '6': if (indicator == 'D') type_str = "NPC Dead"; break; + case '7': if (indicator == 'X') type_str = "Not ID"; break; + case '8': if (indicator == 'F') type_str = "Not Faction"; break; + case '9': if (indicator == 'C') type_str = "Not Class"; break; + case 'A': if (indicator == 'R') type_str = "Not Race"; break; + case 'B': if (indicator == 'L') type_str = "Not Cell"; break; + case 'C': if (indicator == 's') type_str = "Not Local"; break; + } + + // Append the variable name to the function string if any. + if (type != '1') func_str = rule.substr(5); + + // In the previous switch, we assumed that the second char was X + // for all types not qual to one. If this wasn't true, go back to + // the error message. + if (type != '1' && rule[3] != 'X') + func_str = str(boost::format("INVALID=%s") % rule.substr(1,3)); + + char oper = rule[4]; + std::string oper_str = "??"; + switch (oper) + { + case '0': oper_str = "=="; break; + case '1': oper_str = "!="; break; + case '2': oper_str = "< "; break; + case '3': oper_str = "<="; break; + case '4': oper_str = "> "; break; + case '5': oper_str = ">="; break; + } + + std::string value_str = "??"; + if (ss.mType == ESM::VT_Int) + value_str = str(boost::format("%d") % ss.mI); + else if (ss.mType == ESM::VT_Float) + value_str = str(boost::format("%f") % ss.mF); + + std::string result = str(boost::format("%-12s %-32s %2s %s") + % type_str % func_str % oper_str % value_str); + return result; +} + +void printEffectList(ESM::EffectList effects) +{ + int i = 0; + std::vector::iterator eit; + for (eit = effects.mList.begin(); eit != effects.mList.end(); eit++) + { + std::cout << " Effect[" << i << "]: " << magicEffectLabel(eit->mEffectID) + << " (" << eit->mEffectID << ")" << std::endl; + if (eit->mSkill != -1) + std::cout << " Skill: " << skillLabel(eit->mSkill) + << " (" << (int)eit->mSkill << ")" << std::endl; + if (eit->mAttribute != -1) + std::cout << " Attribute: " << attributeLabel(eit->mAttribute) + << " (" << (int)eit->mAttribute << ")" << std::endl; + std::cout << " Range: " << rangeTypeLabel(eit->mRange) + << " (" << eit->mRange << ")" << std::endl; + // Area is always zero if range type is "Self" + if (eit->mRange != ESM::RT_Self) + std::cout << " Area: " << eit->mArea << std::endl; + std::cout << " Duration: " << eit->mDuration << std::endl; + std::cout << " Magnitude: " << eit->mMagnMin << "-" << eit->mMagnMax << std::endl; + i++; + } +} namespace EsmTool { RecordBase * -RecordBase::create(int type) +RecordBase::create(ESM::NAME type) { RecordBase *record = 0; - switch (type) { + switch (type.val) { case ESM::REC_ACTI: { record = new EsmTool::Record; @@ -233,7 +379,7 @@ template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Mesh: " << mData.mModel << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Script: " << mData.mScript << std::endl; } @@ -241,38 +387,92 @@ template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl; + printEffectList(mData.mEffects); } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Mesh: " << mData.mModel << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; - std::cout << " Script: " << mData.mScript << std::endl; - std::cout << " Enchantment: " << mData.mEnchant << std::endl; - std::cout << " Type: " << mData.mData.mType << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + if (mData.mEnchant != "") + std::cout << " Enchantment: " << mData.mEnchant << std::endl; + std::cout << " Type: " << armorTypeLabel(mData.mData.mType) + << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " Health: " << mData.mData.mHealth << std::endl; + std::cout << " Armor: " << mData.mData.mArmor << std::endl; + std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; + std::vector::iterator pit; + for (pit = mData.mParts.mParts.begin(); pit != mData.mParts.mParts.end(); pit++) + { + std::cout << " Body Part: " << bodyPartLabel(pit->mPart) + << " (" << (int)(pit->mPart) << ")" << std::endl; + std::cout << " Male Name: " << pit->mMale << std::endl; + if (pit->mFemale != "") + std::cout << " Female Name: " << pit->mFemale << std::endl; + } } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType) + << " (" << (int)mData.mData.mType << ")" << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " Quality: " << mData.mData.mQuality << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Mesh: " << mData.mModel << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Type: " << meshTypeLabel(mData.mData.mType) + << " (" << (int)mData.mData.mType << ")" << std::endl; + std::cout << " Flags: " << bodyPartFlags(mData.mData.mFlags) << std::endl; + std::cout << " Part: " << meshPartLabel(mData.mData.mPart) + << " (" << (int)mData.mData.mPart << ")" << std::endl; + std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Mesh: " << mData.mModel << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + if (mData.mEnchant != "") + std::cout << " Enchantment: " << mData.mEnchant << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; + std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; + std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; + std::cout << " Text: [skipped]" << std::endl; + // Skip until multi-line fields is controllable by a command line option. + // Mildly problematic because there are no parameter to print() currently. + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << mData.mText << std::endl; + // std::cout << "-------------------------------------------" << std::endl; } template<> @@ -281,13 +481,38 @@ void Record::print() std::cout << " Name: " << mData.mName << std::endl; std::cout << " Texture: " << mData.mTexture << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; + std::vector::iterator pit; + for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); pit++) + std::cout << " Power: " << *pit << std::endl; } template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Region: " << mData.mRegion << std::endl; + // None of the cells have names... + if (mData.mName != "") + std::cout << " Name: " << mData.mName << std::endl; + if (mData.mRegion != "") + std::cout << " Region: " << mData.mRegion << std::endl; + std::cout << " Flags: " << cellFlags(mData.mData.mFlags) << std::endl; + + std::cout << " Coordinates: " << " (" << mData.getGridX() << "," + << mData.getGridY() << ")" << std::endl; + + if (mData.mData.mFlags & ESM::Cell::Interior && + !(mData.mData.mFlags & ESM::Cell::QuasiEx)) + { + std::cout << " Ambient Light Color: " << mData.mAmbi.mAmbient << std::endl; + std::cout << " Sunlight Color: " << mData.mAmbi.mSunlight << std::endl; + std::cout << " Fog Color: " << mData.mAmbi.mFog << std::endl; + std::cout << " Fog Density: " << mData.mAmbi.mFogDensity << std::endl; + std::cout << " Water Level: " << mData.mWater << std::endl; + } + else + std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl; + std::cout << " Water Level Int: " << mData.mWaterInt << std::endl; + std::cout << " NAM0: " << mData.mNAM0 << std::endl; + } template<> @@ -295,37 +520,145 @@ void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; + std::cout << " Playable: " << mData.mData.mIsPlayable << std::endl; + std::cout << " AutoCalc: " << mData.mData.mCalc << std::endl; + std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) + << " (" << mData.mData.mAttribute[0] << ")" << std::endl; + std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) + << " (" << mData.mData.mAttribute[1] << ")" << std::endl; + std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization) + << " (" << mData.mData.mSpecialization << ")" << std::endl; + for (int i = 0; i != 5; i++) + std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][0]) + << " (" << mData.mData.mSkills[i][0] << ")" << std::endl; + for (int i = 0; i != 5; i++) + std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][1]) + << " (" << mData.mData.mSkills[i][1] << ")" << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + if (mData.mEnchant != "") + std::cout << " Enchantment: " << mData.mEnchant << std::endl; + std::cout << " Type: " << clothingTypeLabel(mData.mData.mType) + << " (" << mData.mData.mType << ")" << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; + std::vector::iterator pit; + for (pit = mData.mParts.mParts.begin(); pit != mData.mParts.mParts.end(); pit++) + { + std::cout << " Body Part: " << bodyPartLabel(pit->mPart) + << " (" << (int)(pit->mPart) << ")" << std::endl; + std::cout << " Male Name: " << pit->mMale << std::endl; + if (pit->mFemale != "") + std::cout << " Female Name: " << pit->mFemale << std::endl; + } } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Flags: " << containerFlags(mData.mFlags) << std::endl; + std::cout << " Weight: " << mData.mWeight << std::endl; + std::vector::iterator cit; + for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); cit++) + std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount + << " Item: " << cit->mItem.toString() << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Flags: " << creatureFlags(mData.mFlags) << std::endl; + std::cout << " Original: " << mData.mOriginal << std::endl; + std::cout << " Scale: " << mData.mScale << std::endl; + + std::cout << " Type: " << creatureTypeLabel(mData.mData.mType) + << " (" << mData.mData.mType << ")" << std::endl; + std::cout << " Level: " << mData.mData.mLevel << std::endl; + + std::cout << " Attributes:" << std::endl; + std::cout << " Strength: " << mData.mData.mStrength << std::endl; + std::cout << " Intelligence: " << mData.mData.mIntelligence << std::endl; + std::cout << " Willpower: " << mData.mData.mWillpower << std::endl; + std::cout << " Agility: " << mData.mData.mAgility << std::endl; + std::cout << " Speed: " << mData.mData.mSpeed << std::endl; + std::cout << " Endurance: " << mData.mData.mEndurance << std::endl; + std::cout << " Personality: " << mData.mData.mPersonality << std::endl; + std::cout << " Luck: " << mData.mData.mLuck << std::endl; + + std::cout << " Health: " << mData.mData.mHealth << std::endl; + std::cout << " Magicka: " << mData.mData.mMana << std::endl; + std::cout << " Fatigue: " << mData.mData.mFatigue << std::endl; + std::cout << " Soul: " << mData.mData.mSoul << std::endl; + std::cout << " Combat: " << mData.mData.mCombat << std::endl; + std::cout << " Magic: " << mData.mData.mMagic << std::endl; + std::cout << " Stealth: " << mData.mData.mStealth << std::endl; + std::cout << " Attack1: " << mData.mData.mAttack[0] + << "-" << mData.mData.mAttack[1] << std::endl; + std::cout << " Attack2: " << mData.mData.mAttack[2] + << "-" << mData.mData.mAttack[3] << std::endl; + std::cout << " Attack3: " << mData.mData.mAttack[4] + << "-" << mData.mData.mAttack[5] << std::endl; + std::cout << " Gold: " << mData.mData.mGold << std::endl; + + std::vector::iterator cit; + for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); cit++) + std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount + << " Item: " << cit->mItem.toString() << std::endl; + + std::vector::iterator sit; + for (sit = mData.mSpells.mList.begin(); sit != mData.mSpells.mList.end(); sit++) + std::cout << " Spell: " << *sit << std::endl; + + std::cout << " Artifical Intelligence: " << mData.mHasAI << std::endl; + std::cout << " AI Hello:" << (int)mData.mAiData.mHello << std::endl; + std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl; + std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl; + std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl; + std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl; + std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl; + std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl; + std::cout << " AI U4:" << (int)mData.mAiData.mU4 << std::endl; + std::cout << " AI Services:" << boost::format("0x%08X") % mData.mAiData.mServices << std::endl; + + std::vector::iterator pit; + for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); pit++) + printAIPackage(*pit); } template<> void Record::print() { - // nothing to print + std::cout << " Type: " << dialogTypeLabel(mData.mType) + << " (" << (int)mData.mType << ")" << std::endl; + // Sadly, there are no DialInfos, because the loader dumps as it + // loads, rather than loading and then dumping. :-( Anyone mind if + // I change this? + std::vector::iterator iit; + for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); iit++) + std::cout << "INFO!" << iit->mId << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Mesh: " << mData.mModel << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Script: " << mData.mScript << std::endl; std::cout << " OpenSound: " << mData.mOpenSound << std::endl; std::cout << " CloseSound: " << mData.mCloseSound << std::endl; @@ -334,22 +667,55 @@ void Record::print() template<> void Record::print() { - // nothing to print + std::cout << " Type: " << enchantTypeLabel(mData.mData.mType) + << " (" << mData.mData.mType << ")" << std::endl; + std::cout << " Cost: " << mData.mData.mCost << std::endl; + std::cout << " Charge: " << mData.mData.mCharge << std::endl; + std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl; + printEffectList(mData.mEffects); } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Attr1: " << mData.mData.mAttribute1 << std::endl; - std::cout << " Attr2: " << mData.mData.mAttribute2 << std::endl; std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; + if (mData.mData.mUnknown != -1) + std::cout << " Unknown: " << mData.mData.mUnknown << std::endl; + std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute1) + << " (" << mData.mData.mAttribute1 << ")" << std::endl; + std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute2) + << " (" << mData.mData.mAttribute2 << ")" << std::endl; + for (int i = 0; i != 6; i++) + if (mData.mData.mSkillID[i] != -1) + std::cout << " Skill: " << skillLabel(mData.mData.mSkillID[i]) + << " (" << mData.mData.mSkillID[i] << ")" << std::endl; + for (int i = 0; i != 10; i++) + if (mData.mRanks[i] != "") + { + std::cout << " Rank: " << mData.mRanks[i] << std::endl; + std::cout << " Attribute1 Requirement: " + << mData.mData.mRankData[i].mAttribute1 << std::endl; + std::cout << " Attribute2 Requirement: " + << mData.mData.mRankData[i].mAttribute2 << std::endl; + std::cout << " One Skill at Level: " + << mData.mData.mRankData[i].mSkill1 << std::endl; + std::cout << " Two Skills at Level: " + << mData.mData.mRankData[i].mSkill2 << std::endl; + std::cout << " Faction Reaction: " + << mData.mData.mRankData[i].mFactReaction << std::endl; + } + std::vector::iterator rit; + for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); rit++) + std::cout << " Reaction: " << rit->mReaction << " = " << rit->mFaction << std::endl; } template<> void Record::print() { - // nothing to print + // nothing to print (well, nothing that's correct anyway) + std::cout << " Type: " << mData.mType << std::endl; + std::cout << " Value: " << mData.mValue << std::endl; } template<> @@ -379,139 +745,511 @@ template<> void Record::print() { std::cout << " Id: " << mData.mId << std::endl; + if (mData.mPrev != "") + std::cout << " Previous ID: " << mData.mPrev << std::endl; + if (mData.mNext != "") + std::cout << " Next ID: " << mData.mNext << std::endl; std::cout << " Text: " << mData.mResponse << std::endl; + if (mData.mActor != "") + std::cout << " Actor: " << mData.mActor << std::endl; + if (mData.mRace != "") + std::cout << " Race: " << mData.mRace << std::endl; + if (mData.mClass != "") + std::cout << " Class: " << mData.mClass << std::endl; + std::cout << " Factionless: " << mData.mFactionLess << std::endl; + if (mData.mNpcFaction != "") + std::cout << " NPC Faction: " << mData.mNpcFaction << std::endl; + if (mData.mData.mRank != -1) + std::cout << " NPC Rank: " << (int)mData.mData.mRank << std::endl; + if (mData.mPcFaction != "") + std::cout << " PC Faction: " << mData.mPcFaction << std::endl; + // CHANGE? non-standard capitalization mPCrank -> mPCRank (mPcRank?) + if (mData.mData.mPCrank != -1) + std::cout << " PC Rank: " << (int)mData.mData.mPCrank << std::endl; + if (mData.mCell != "") + std::cout << " Cell: " << mData.mCell << std::endl; + if (mData.mData.mDisposition > 0) + std::cout << " Disposition: " << mData.mData.mDisposition << std::endl; + if (mData.mData.mGender != ESM::DialInfo::NA) + std::cout << " Gender: " << mData.mData.mGender << std::endl; + if (mData.mSound != "") + std::cout << " Sound File: " << mData.mSound << std::endl; + + if (mData.mResultScript != "") + { + std::cout << " Result Script: [skipped]" << std::endl; + // Skip until multi-line fields is controllable by a command line option. + // Mildly problematic because there are no parameter to print() currently. + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << mData.mResultScript << std::endl; + // std::cout << "-------------------------------------------" << std::endl; + } + + std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus) + << " (" << mData.mQuestStatus << ")" << std::endl; + std::cout << " Unknown1: " << mData.mData.mUnknown1 << std::endl; + std::cout << " Unknown2: " << (int)mData.mData.mUnknown2 << std::endl; + + std::vector::iterator sit; + for (sit = mData.mSelects.begin(); sit != mData.mSelects.end(); sit++) + std::cout << " Select Rule: " << ruleString(*sit) << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; + for (int i = 0; i !=4; i++) + { + // A value of -1 means no effect + if (mData.mData.mEffectID[i] == -1) continue; + std::cout << " Effect: " << magicEffectLabel(mData.mData.mEffectID[i]) + << " (" << mData.mData.mEffectID[i] << ")" << std::endl; + std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) + << " (" << mData.mData.mSkills[i] << ")" << std::endl; + std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i]) + << " (" << mData.mData.mAttributes[i] << ")" << std::endl; + } } template<> void Record::print() { - std::cout << " Coords: [" << mData.mX << "," << mData.mY << "]" << std::endl; + std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl; + std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl; + std::cout << " HasData: " << mData.mHasData << std::endl; + std::cout << " DataTypes: " << mData.mDataTypes << std::endl; + + // Seems like this should done with reference counting in the + // loader to me. But I'm not really knowledgable about this + // record type yet. --Cory + bool wasLoaded = mData.mDataLoaded; + if (mData.mDataTypes) mData.loadData(mData.mDataTypes); + if (mData.mDataLoaded) + { + std::cout << " Height Offset: " << mData.mLandData->mHeightOffset << std::endl; + // Lots of missing members. + std::cout << " Unknown1: " << mData.mLandData->mUnk1 << std::endl; + std::cout << " Unknown2: " << mData.mLandData->mUnk2 << std::endl; + } + if (!wasLoaded) mData.unloadData(); } template<> void Record::print() { + std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; + std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; + std::vector::iterator iit; + for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) + std::cout << " Creature: Level: " << iit->mLevel + << " Creature: " << iit->mId << std::endl; } template<> void Record::print() { + std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; + std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; + std::vector::iterator iit; + for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) + std::cout << " Inventory: Count: " << iit->mLevel + << " Item: " << iit->mId << std::endl; } template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; + if (mData.mName != "") + std::cout << " Name: " << mData.mName << std::endl; + if (mData.mModel != "") + std::cout << " Model: " << mData.mModel << std::endl; + if (mData.mIcon != "") + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Flags: " << lightFlags(mData.mData.mFlags) << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " Sound: " << mData.mSound << std::endl; + std::cout << " Duration: " << mData.mData.mTime << std::endl; + std::cout << " Radius: " << mData.mData.mRadius << std::endl; + std::cout << " Color: " << mData.mData.mColor << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Type: " << mData.mType << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; + std::cout << " Uses: " << mData.mData.mUses << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + // BUG? No Type Label? + std::cout << " Type: " << mData.mType << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; + std::cout << " Uses: " << mData.mData.mUses << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Type: " << mData.mType << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; + std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; + std::cout << " Uses: " << mData.mData.mUses << std::endl; } template<> void Record::print() { std::cout << " Id: " << mData.mId << std::endl; + std::cout << " Index: " << mData.mIndex << std::endl; std::cout << " Texture: " << mData.mTexture << std::endl; } template<> void Record::print() { - std::cout << " Index: " << mData.mIndex << std::endl; - - const char *text = "Positive"; - if (mData.mData.mFlags & ESM::MagicEffect::Negative) { - text = "Negative"; - } - std::cout << " " << text << std::endl; + std::cout << " Index: " << magicEffectLabel(mData.mIndex) + << " (" << mData.mIndex << ")" << std::endl; + std::cout << " Description: " << mData.mDescription << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + std::cout << " Flags: " << magicEffectFlags(mData.mData.mFlags) << std::endl; + std::cout << " Particle Texture: " << mData.mParticle << std::endl; + if (mData.mCasting != "") + std::cout << " Casting Static: " << mData.mCasting << std::endl; + if (mData.mCastSound != "") + std::cout << " Casting Sound: " << mData.mCastSound << std::endl; + if (mData.mBolt != "") + std::cout << " Bolt Static: " << mData.mBolt << std::endl; + if (mData.mBoltSound != "") + std::cout << " Bolt Sound: " << mData.mBoltSound << std::endl; + if (mData.mHit != "") + std::cout << " Hit Static: " << mData.mHit << std::endl; + if (mData.mHitSound != "") + std::cout << " Hit Sound: " << mData.mHitSound << std::endl; + if (mData.mArea != "") + std::cout << " Area Static: " << mData.mArea << std::endl; + if (mData.mAreaSound != "") + std::cout << " Area Sound: " << mData.mAreaSound << std::endl; + std::cout << " School: " << schoolLabel(mData.mData.mSchool) + << " (" << mData.mData.mSchool << ")" << std::endl; + std::cout << " Base Cost: " << mData.mData.mBaseCost << std::endl; + std::cout << " Speed: " << mData.mData.mSpeed << std::endl; + std::cout << " Size: " << mData.mData.mSize << std::endl; + std::cout << " Size Cap: " << mData.mData.mSizeCap << std::endl; + std::cout << " RGB Color: " << "(" + << mData.mData.mRed << "," + << mData.mData.mGreen << "," + << mData.mData.mGreen << ")" << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " Is Key: " << mData.mData.mIsKey << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Animation: " << mData.mModel << std::endl; + std::cout << " Hair Model: " << mData.mHair << std::endl; + std::cout << " Head Model: " << mData.mHead << std::endl; std::cout << " Race: " << mData.mRace << std::endl; + std::cout << " Class: " << mData.mClass << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + if (mData.mFaction != "") + std::cout << " Faction: " << mData.mFaction << std::endl; + std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl; + + // Seriously? + if (mData.mNpdt52.mGold == -10) + { + std::cout << " Level: " << mData.mNpdt12.mLevel << std::endl; + std::cout << " Reputation: " << (int)mData.mNpdt12.mReputation << std::endl; + std::cout << " Disposition: " << (int)mData.mNpdt12.mDisposition << std::endl; + std::cout << " Faction: " << (int)mData.mNpdt52.mFactionID << std::endl; + std::cout << " Rank: " << (int)mData.mNpdt12.mRank << std::endl; + std::cout << " Unknown1: " + << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown1) << std::endl; + std::cout << " Unknown2: " + << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl; + std::cout << " Unknown3: " + << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl; + std::cout << " Gold: " << (int)mData.mNpdt12.mGold << std::endl; + } + else { + std::cout << " Level: " << mData.mNpdt52.mLevel << std::endl; + std::cout << " Reputation: " << (int)mData.mNpdt52.mReputation << std::endl; + std::cout << " Disposition: " << (int)mData.mNpdt52.mDisposition << std::endl; + std::cout << " Rank: " << (int)mData.mNpdt52.mRank << std::endl; + + std::cout << " Attributes:" << std::endl; + std::cout << " Strength: " << (int)mData.mNpdt52.mStrength << std::endl; + std::cout << " Intelligence: " << (int)mData.mNpdt52.mIntelligence << std::endl; + std::cout << " Willpower: " << (int)mData.mNpdt52.mWillpower << std::endl; + std::cout << " Agility: " << (int)mData.mNpdt52.mAgility << std::endl; + std::cout << " Speed: " << (int)mData.mNpdt52.mSpeed << std::endl; + std::cout << " Endurance: " << (int)mData.mNpdt52.mEndurance << std::endl; + std::cout << " Personality: " << (int)mData.mNpdt52.mPersonality << std::endl; + std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl; + + std::cout << " Skills:" << std::endl; + for (int i = 0; i != 27; i++) + std::cout << " " << skillLabel(i) << ": " + << (int)((unsigned char)mData.mNpdt52.mSkills[i]) << std::endl; + + std::cout << " Health: " << mData.mNpdt52.mHealth << std::endl; + std::cout << " Magicka: " << mData.mNpdt52.mMana << std::endl; + std::cout << " Fatigue: " << mData.mNpdt52.mFatigue << std::endl; + std::cout << " Unknown: " << (int)mData.mNpdt52.mUnknown << std::endl; + std::cout << " Gold: " << mData.mNpdt52.mGold << std::endl; + } + + std::vector::iterator cit; + for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); cit++) + std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount + << " Item: " << cit->mItem.toString() << std::endl; + + std::vector::iterator sit; + for (sit = mData.mSpells.mList.begin(); sit != mData.mSpells.mList.end(); sit++) + std::cout << " Spell: " << *sit << std::endl; + + std::vector::iterator dit; + for (dit = mData.mTransport.begin(); dit != mData.mTransport.end(); dit++) + { + std::cout << " Destination Position: " + << boost::format("%12.3f") % dit->mPos.pos[0] << "," + << boost::format("%12.3f") % dit->mPos.pos[1] << "," + << boost::format("%12.3f") % dit->mPos.pos[2] << ")" << std::endl; + std::cout << " Destination Rotation: " + << boost::format("%9.6f") % dit->mPos.rot[0] << "," + << boost::format("%9.6f") % dit->mPos.rot[1] << "," + << boost::format("%9.6f") % dit->mPos.rot[2] << ")" << std::endl; + if (dit->mCellName != "") + std::cout << " Destination Cell: " << dit->mCellName << std::endl; + } + + std::cout << " Artifical Intelligence: " << mData.mHasAI << std::endl; + std::cout << " AI Hello:" << (int)mData.mAiData.mHello << std::endl; + std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl; + std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl; + std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl; + std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl; + std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl; + std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl; + std::cout << " AI U4:" << (int)mData.mAiData.mU4 << std::endl; + std::cout << " AI Services:" << boost::format("0x%08X") % mData.mAiData.mServices << std::endl; + + std::vector::iterator pit; + for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); pit++) + printAIPackage(*pit); } template<> void Record::print() { std::cout << " Cell: " << mData.mCell << std::endl; - std::cout << " Point count: " << mData.mPoints.size() << std::endl; - std::cout << " Edge count: " << mData.mEdges.size() << std::endl; + std::cout << " Coordinates: (" << mData.mData.mX << "," << mData.mData.mY << ")" << std::endl; + std::cout << " Unknown S1: " << mData.mData.mS1 << std::endl; + if ((unsigned int)mData.mData.mS2 != mData.mPoints.size()) + std::cout << " Reported Point Count: " << mData.mData.mS2 << std::endl; + std::cout << " Point Count: " << mData.mPoints.size() << std::endl; + std::cout << " Edge Count: " << mData.mEdges.size() << std::endl; + + int i = 0; + ESM::Pathgrid::PointList::iterator pit; + for (pit = mData.mPoints.begin(); pit != mData.mPoints.end(); pit++) + { + std::cout << " Point[" << i << "]:" << std::endl; + std::cout << " Coordinates: (" << pit->mX << "," + << pit->mY << "," << pit->mZ << ")" << std::endl; + std::cout << " Auto-Generated: " << (int)pit->mAutogenerated << std::endl; + std::cout << " Connections: " << (int)pit->mConnectionNum << std::endl; + std::cout << " Unknown: " << pit->mUnknown << std::endl; + i++; + } + i = 0; + ESM::Pathgrid::EdgeList::iterator eit; + for (eit = mData.mEdges.begin(); eit != mData.mEdges.end(); eit++) + { + std::cout << " Edge[" << i << "]: " << eit->mV0 << " -> " << eit->mV1 << std::endl; + if (eit->mV0 >= mData.mData.mS2 || eit->mV1 >= mData.mData.mS2) + std::cout << " BAD POINT IN EDGE!" << std::endl; + i++; + } } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Length: " << mData.mData.mHeight.mMale << "m " << mData.mData.mHeight.mFemale << "f" << std::endl; + std::cout << " Description: " << mData.mDescription << std::endl; + std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl; + + std::cout << " Male:" << std::endl; + std::cout << " Strength: " + << mData.mData.mStrength.mMale << std::endl; + std::cout << " Intelligence: " + << mData.mData.mIntelligence.mMale << std::endl; + std::cout << " Willpower: " + << mData.mData.mWillpower.mMale << std::endl; + std::cout << " Agility: " + << mData.mData.mAgility.mMale << std::endl; + std::cout << " Speed: " + << mData.mData.mSpeed.mMale << std::endl; + std::cout << " Endurance: " + << mData.mData.mEndurance.mMale << std::endl; + std::cout << " Personality: " + << mData.mData.mPersonality.mMale << std::endl; + std::cout << " Luck: " + << mData.mData.mLuck.mMale << std::endl; + std::cout << " Height: " + << mData.mData.mHeight.mMale << std::endl; + std::cout << " Weight: " + << mData.mData.mWeight.mMale << std::endl; + + std::cout << " Female:" << std::endl; + std::cout << " Strength: " + << mData.mData.mStrength.mFemale << std::endl; + std::cout << " Intelligence: " + << mData.mData.mIntelligence.mFemale << std::endl; + std::cout << " Willpower: " + << mData.mData.mWillpower.mFemale << std::endl; + std::cout << " Agility: " + << mData.mData.mAgility.mFemale << std::endl; + std::cout << " Speed: " + << mData.mData.mSpeed.mFemale << std::endl; + std::cout << " Endurance: " + << mData.mData.mEndurance.mFemale << std::endl; + std::cout << " Personality: " + << mData.mData.mPersonality.mFemale << std::endl; + std::cout << " Luck: " + << mData.mData.mLuck.mFemale << std::endl; + std::cout << " Height: " + << mData.mData.mHeight.mFemale << std::endl; + std::cout << " Weight: " + << mData.mData.mWeight.mFemale << std::endl; + + for (int i = 0; i != 7; i++) + // Not all races have 7 skills. + if (mData.mData.mBonus[i].mSkill != -1) + std::cout << " Skill: " + << skillLabel(mData.mData.mBonus[i].mSkill) + << " (" << mData.mData.mBonus[i].mSkill << ") = " + << mData.mData.mBonus[i].mBonus << std::endl; + + std::vector::iterator sit; + for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); sit++) + std::cout << " Power: " << *sit << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + + std::cout << " Weather:" << std::endl; + std::cout << " Clear: " << (int)mData.mData.mClear << std::endl; + std::cout << " Cloudy: " << (int)mData.mData.mCloudy << std::endl; + std::cout << " Foggy: " << (int)mData.mData.mFoggy << std::endl; + std::cout << " Overcast: " << (int)mData.mData.mOvercast << std::endl; + std::cout << " Rain: " << (int)mData.mData.mOvercast << std::endl; + std::cout << " Thunder: " << (int)mData.mData.mThunder << std::endl; + std::cout << " Ash: " << (int)mData.mData.mAsh << std::endl; + std::cout << " Blight: " << (int)mData.mData.mBlight << std::endl; + std::cout << " UnknownA: " << (int)mData.mData.mA << std::endl; + std::cout << " UnknownB: " << (int)mData.mData.mB << std::endl; + std::cout << " Map Color: " << mData.mMapColor << std::endl; + if (mData.mSleepList != "") + std::cout << " Sleep List: " << mData.mSleepList << std::endl; + std::vector::iterator sit; + for (sit = mData.mSoundList.begin(); sit != mData.mSoundList.end(); sit++) + std::cout << " Sound: " << (int)sit->mChance << " = " << sit->mSound.toString() << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mData.mName.toString() << std::endl; + + std::cout << " Num Shorts: " << mData.mData.mNumShorts << std::endl; + std::cout << " Num Longs: " << mData.mData.mNumLongs << std::endl; + std::cout << " Num Floats: " << mData.mData.mNumFloats << std::endl; + std::cout << " Script Data Size: " << mData.mData.mScriptDataSize << std::endl; + std::cout << " Table Size: " << mData.mData.mStringTableSize << std::endl; + + std::cout << " Script: [skipped]" << std::endl; + // Skip until multi-line fields is controllable by a command line option. + // Mildly problematic because there are no parameter to print() currently. + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << s->scriptText << std::endl; + // std::cout << "-------------------------------------------" << std::endl; + std::vector::iterator vit; + for (vit = mData.mVarNames.begin(); vit != mData.mVarNames.end(); vit++) + std::cout << " Variable: " << *vit << std::endl; + + std::cout << " ByteCode: "; + std::vector::iterator cit; + for (cit = mData.mScriptData.begin(); cit != mData.mScriptData.end(); cit++) + std::cout << boost::format("%02X") % (int)(*cit); + std::cout << std::endl; } template<> void Record::print() { - std::cout << " ID: " << mData.mIndex << std::endl; - - const char *spec = 0; - int specId = mData.mData.mSpecialization; - if (specId == 0) { - spec = "Combat"; - } else if (specId == 1) { - spec = "Magic"; - } else { - spec = "Stealth"; - } - std::cout << " Type: " << spec << std::endl; + std::cout << " ID: " << skillLabel(mData.mIndex) + << " (" << mData.mIndex << ")" << std::endl; + std::cout << " Description: " << mData.mDescription << std::endl; + std::cout << " Governing Attribute: " << attributeLabel(mData.mData.mAttribute) + << " (" << mData.mData.mAttribute << ")" << std::endl; + std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization) + << " (" << mData.mData.mSpecialization << ")" << std::endl; + for (int i = 0; i != 4; i++) + std::cout << " UseValue[" << i << "]:" << mData.mData.mUseValue[i] << std::endl; } template<> @@ -519,25 +1257,36 @@ void Record::print() { std::cout << " Creature: " << mData.mCreature << std::endl; std::cout << " Sound: " << mData.mSound << std::endl; + std::cout << " Type: " << soundTypeLabel(mData.mType) + << " (" << mData.mType << ")" << std::endl; } template<> void Record::print() { std::cout << " Sound: " << mData.mSound << std::endl; - std::cout << " Volume: " << mData.mData.mVolume << std::endl; + std::cout << " Volume: " << (int)mData.mData.mVolume << std::endl; + if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0) + std::cout << " Range: " << (int)mData.mData.mMinRange << " - " + << (int)mData.mData.mMaxRange << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Type: " << spellTypeLabel(mData.mData.mType) + << " (" << mData.mData.mType << ")" << std::endl; + std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl; + std::cout << " Cost: " << mData.mData.mCost << std::endl; + printEffectList(mData.mEffects); } template<> void Record::print() { - std::cout << "Start script: " << mData.mScript << std::endl; + std::cout << "Start Script: " << mData.mScript << std::endl; + std::cout << "Start Data: " << mData.mData << std::endl; } template<> @@ -549,11 +1298,35 @@ void Record::print() template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; - std::cout << " Chop: " << mData.mData.mChop[0] << "-" << mData.mData.mChop[1] << std::endl; - std::cout << " Slash: " << mData.mData.mSlash[0] << "-" << mData.mData.mSlash[1] << std::endl; - std::cout << " Thrust: " << mData.mData.mThrust[0] << "-" << mData.mData.mThrust[1] << std::endl; + // No names on VFX bolts + if (mData.mName != "") + std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Model: " << mData.mModel << std::endl; + // No icons on VFX bolts or magic bolts + if (mData.mIcon != "") + std::cout << " Icon: " << mData.mIcon << std::endl; + if (mData.mScript != "") + std::cout << " Script: " << mData.mScript << std::endl; + if (mData.mEnchant != "") + std::cout << " Enchantment: " << mData.mEnchant << std::endl; + std::cout << " Type: " << weaponTypeLabel(mData.mData.mType) + << " (" << mData.mData.mType << ")" << std::endl; + std::cout << " Flags: " << weaponFlags(mData.mData.mFlags) << std::endl; + std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; + std::cout << " Health: " << mData.mData.mHealth << std::endl; + std::cout << " Speed: " << mData.mData.mSpeed << std::endl; + std::cout << " Reach: " << mData.mData.mReach << std::endl; + std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; + if (mData.mData.mChop[0] != 0 && mData.mData.mChop[1] != 0) + std::cout << " Chop: " << (int)mData.mData.mChop[0] << "-" + << (int)mData.mData.mChop[1] << std::endl; + if (mData.mData.mSlash[0] != 0 && mData.mData.mSlash[1] != 0) + std::cout << " Slash: " << (int)mData.mData.mSlash[0] << "-" + << (int)mData.mData.mSlash[1] << std::endl; + if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0) + std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-" + << (int)mData.mData.mThrust[1] << std::endl; } template<> diff --git a/apps/esmtool/record.hpp b/apps/esmtool/record.hpp index 3cd7f5b54b..c3daa9d0c3 100644 --- a/apps/esmtool/record.hpp +++ b/apps/esmtool/record.hpp @@ -20,7 +20,7 @@ namespace EsmTool protected: std::string mId; int mFlags; - int mType; + ESM::NAME mType; public: RecordBase () {} @@ -42,7 +42,7 @@ namespace EsmTool mFlags = flags; } - int getType() const { + ESM::NAME getType() const { return mType; } @@ -50,7 +50,7 @@ namespace EsmTool virtual void save(ESM::ESMWriter &esm) = 0; virtual void print() = 0; - static RecordBase *create(int type); + static RecordBase *create(ESM::NAME type); // just make it a bit shorter template diff --git a/apps/launcher/pluginsview.hpp b/apps/launcher/pluginsview.hpp index b3dfb79ffb..484351e33a 100644 --- a/apps/launcher/pluginsview.hpp +++ b/apps/launcher/pluginsview.hpp @@ -24,6 +24,6 @@ public slots: }; -Q_DECLARE_METATYPE(QVector); +Q_DECLARE_METATYPE(QVector) -#endif \ No newline at end of file +#endif diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index d396df648f..19b69794f2 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -78,7 +78,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { multistrmap::iterator it; if((it = map.find(key)) == map.end()) { - map.insert( std::make_pair > (key, std::vector() ) ); + map.insert( std::make_pair (key, std::vector() ) ); } map[key].push_back(value); } @@ -115,7 +115,7 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) { multistrmap::iterator it; if((it = map.find(key)) == map.end()) { - map.insert( std::make_pair > (key, std::vector() ) ); + map.insert( std::make_pair (key, std::vector() ) ); } map[key].push_back(value); } @@ -152,12 +152,12 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) { } } } -}; +} void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::string value) { multistrmap::iterator it = cfg.find(key); if(it == cfg.end()) { - cfg.insert(std::make_pair >(key, std::vector() )); + cfg.insert(std::make_pair (key, std::vector() )); } cfg[key].push_back(value); } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index bc4e10501e..538f63dc96 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -30,7 +30,7 @@ add_openmw_dir (mwgui formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog - enchantingdialog + enchantingdialog trainingwindow travelwindow ) add_openmw_dir (mwdialogue @@ -62,7 +62,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells - activespells npcstats aipackage aisequence + activespells npcstats aipackage aisequence alchemy ) add_openmw_dir (mwbase diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index adbfca129d..617689bc63 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -163,9 +163,8 @@ void OMW::Engine::loadBSA() std::cout << "Data dir " << dataDirectory << std::endl; Bsa::addDir(dataDirectory, mFSStrict); - // Workaround: Mygui does not find textures in non-BSA subfolders, _unless_ they are explicitely added like this - // For splash screens, this is OK to do, but eventually we will need an investigation why this is necessary - Bsa::addDir(dataDirectory + "/Splash", mFSStrict); + // Workaround until resource listing capabilities are added to DirArchive, we need those to list available splash screens + addResourcesDirectory (dataDirectory); } } @@ -424,21 +423,10 @@ void OMW::Engine::activate() if (handle.empty()) return; - // the faced handle is not updated immediately, so on a cell change it might - // point to an object that doesn't exist anymore - // therefore, we are catching the "Unknown Ogre handle" exception that occurs in this case - MWWorld::Ptr ptr; - try - { - ptr = MWBase::Environment::get().getWorld()->getPtrViaHandle (handle); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle (handle); - if (ptr.isEmpty()) - return; - } - catch (std::runtime_error&) - { + if (ptr.isEmpty()) return; - } MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 5c2ba2f80b..2da52311fd 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -68,7 +68,7 @@ void validate(boost::any &v, std::vector const &tokens, FallbackMap if((mapIt = map->mMap.find(key)) == map->mMap.end()) { - map->mMap.insert(std::make_pair(key,value)); + map->mMap.insert(std::make_pair (key,value)); } } } diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 745832583c..92c177ff30 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -37,7 +37,7 @@ namespace MWBase Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ }; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 462aef014c..c177912d4f 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -42,6 +42,7 @@ namespace MWGui class Console; class SpellWindow; class TradeWindow; + class TravelWindow; class SpellBuyingWindow; class ConfirmationDialog; class CountDialog; @@ -108,6 +109,7 @@ namespace MWBase virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; virtual MWGui::SpellBuyingWindow* getSpellBuyingWindow() = 0; + virtual MWGui::TravelWindow* getTravelWindow() = 0; virtual MWGui::SpellWindow* getSpellWindow() = 0; virtual MWGui::Console* getConsole() = 0; @@ -230,6 +232,7 @@ namespace MWBase virtual void startSpellMaking(MWWorld::Ptr actor) = 0; virtual void startEnchanting(MWWorld::Ptr actor) = 0; + virtual void startTraining(MWWorld::Ptr actor) = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 1cb120b5c3..d29e23c18b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -143,6 +143,9 @@ namespace MWBase virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0; ///< Return a pointer to a liveCellRef with the given Ogre handle. + virtual MWWorld::Ptr searchPtrViaHandle (const std::string& handle) = 0; + ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found + /// \todo enable reference in the OGRE scene virtual void enable (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 3a51ed2b64..62f7df679b 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -795,12 +795,18 @@ namespace MWDialogue || services & ESM::NPC::Misc) windowServices |= MWGui::DialogueWindow::Service_Trade; + if( !mActor.get()->base->mTransport.empty()) + windowServices |= MWGui::DialogueWindow::Service_Travel; + if (services & ESM::NPC::Spells) windowServices |= MWGui::DialogueWindow::Service_BuySpells; if (services & ESM::NPC::Spellmaking) windowServices |= MWGui::DialogueWindow::Service_CreateSpells; + if (services & ESM::NPC::Training) + windowServices |= MWGui::DialogueWindow::Service_Training; + if (services & ESM::NPC::Enchanting) windowServices |= MWGui::DialogueWindow::Service_Enchant; diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 4a419c0c03..56db47a0f5 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -28,25 +28,25 @@ namespace MWGui { AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_alchemy_window.layout", parWindowManager) - , ContainerBase(0) + , ContainerBase(0), mApparatus (4), mIngredients (4) { getWidget(mCreateButton, "CreateButton"); getWidget(mCancelButton, "CancelButton"); - getWidget(mIngredient1, "Ingredient1"); - getWidget(mIngredient2, "Ingredient2"); - getWidget(mIngredient3, "Ingredient3"); - getWidget(mIngredient4, "Ingredient4"); - getWidget(mApparatus1, "Apparatus1"); - getWidget(mApparatus2, "Apparatus2"); - getWidget(mApparatus3, "Apparatus3"); - getWidget(mApparatus4, "Apparatus4"); + getWidget(mIngredients[0], "Ingredient1"); + getWidget(mIngredients[1], "Ingredient2"); + getWidget(mIngredients[2], "Ingredient3"); + getWidget(mIngredients[3], "Ingredient4"); + getWidget(mApparatus[0], "Apparatus1"); + getWidget(mApparatus[1], "Apparatus2"); + getWidget(mApparatus[2], "Apparatus3"); + getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); getWidget(mNameEdit, "NameEdit"); - mIngredient1->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); - mIngredient2->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); - mIngredient3->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); - mIngredient4->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); + mIngredients[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); + mIngredients[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); + mIngredients[2]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); + mIngredients[3]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); @@ -62,157 +62,52 @@ namespace MWGui void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { + mAlchemy.clear(); + mWindowManager.removeGuiMode(GM_Alchemy); mWindowManager.removeGuiMode(GM_Inventory); } void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { + std::string name = mNameEdit->getCaption(); + boost::algorithm::trim(name); + + MWMechanics::Alchemy::Result result = mAlchemy.create (mNameEdit->getCaption ()); + + if (result == MWMechanics::Alchemy::Result_NoName) + { + mWindowManager.messageBox("#{sNotifyMessage37}", std::vector()); + return; + } + // check if mortar & pestle is available (always needed) - /// \todo check albemic, calcinator, retort (sometimes needed) - if (!mApparatus1->isUserString("ToolTipType")) + if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle) { mWindowManager.messageBox("#{sNotifyMessage45}", std::vector()); return; } // make sure 2 or more ingredients were selected - int numIngreds = 0; - if (mIngredient1->isUserString("ToolTipType")) - ++numIngreds; - if (mIngredient2->isUserString("ToolTipType")) - ++numIngreds; - if (mIngredient3->isUserString("ToolTipType")) - ++numIngreds; - if (mIngredient4->isUserString("ToolTipType")) - ++numIngreds; - if (numIngreds < 2) + if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients) { mWindowManager.messageBox("#{sNotifyMessage6a}", std::vector()); return; } - // make sure a name was entered - std::string name = mNameEdit->getCaption(); - boost::algorithm::trim(name); - if (name == "") - { - mWindowManager.messageBox("#{sNotifyMessage37}", std::vector()); - return; - } - - // if there are no created effects, the potion will always fail (but the ingredients won't be destroyed) - if (mEffects.empty()) + if (result == MWMechanics::Alchemy::Result_NoEffects) { mWindowManager.messageBox("#{sNotifyMessage8}", std::vector()); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); return; } - if (rand() % 2 == 0) /// \todo + if (result == MWMechanics::Alchemy::Result_Success) { - ESM::Potion newPotion; - newPotion.mName = mNameEdit->getCaption(); - ESM::EffectList effects; - for (unsigned int i=0; i<4; ++i) - { - if (mEffects.size() >= i+1) - { - ESM::ENAMstruct effect; - effect.mEffectID = mEffects[i].mEffectID; - effect.mArea = 0; - effect.mRange = ESM::RT_Self; - effect.mSkill = mEffects[i].mSkill; - effect.mAttribute = mEffects[i].mAttribute; - effect.mMagnMin = 1; /// \todo - effect.mMagnMax = 10; /// \todo - effect.mDuration = 60; /// \todo - effects.mList.push_back(effect); - } - } - - // UESP Wiki / Morrowind:Alchemy - // "The weight of a potion is an average of the weight of the ingredients, rounded down." - // note by scrawl: not rounding down here, I can't imagine a created potion to - // have 0 weight when using ingredients with 0.1 weight respectively - float weight = 0; - if (mIngredient1->isUserString("ToolTipType")) - weight += mIngredient1->getUserData()->get()->base->mData.mWeight; - if (mIngredient2->isUserString("ToolTipType")) - weight += mIngredient2->getUserData()->get()->base->mData.mWeight; - if (mIngredient3->isUserString("ToolTipType")) - weight += mIngredient3->getUserData()->get()->base->mData.mWeight; - if (mIngredient4->isUserString("ToolTipType")) - weight += mIngredient4->getUserData()->get()->base->mData.mWeight; - newPotion.mData.mWeight = weight / float(numIngreds); - - newPotion.mData.mValue = 100; /// \todo - newPotion.mEffects = effects; - // pick a random mesh and icon - std::vector names; - /// \todo is the mesh/icon dependent on alchemy skill? - names.push_back("standard"); - names.push_back("bargain"); - names.push_back("cheap"); - names.push_back("fresh"); - names.push_back("exclusive"); - names.push_back("quality"); - int random = rand() % names.size(); - newPotion.mModel = "m\\misc_potion_" + names[random ] + "_01.nif"; - newPotion.mIcon = "m\\tx_potion_" + names[random ] + "_01.dds"; - - // check if a similiar potion record exists already - bool found = false; - std::string objectId; - typedef std::map PotionMap; - PotionMap potions = MWBase::Environment::get().getWorld()->getStore().potions.list; - for (PotionMap::const_iterator it = potions.begin(); it != potions.end(); ++it) - { - if (found) break; - - if (it->second.mData.mValue == newPotion.mData.mValue - && it->second.mData.mWeight == newPotion.mData.mWeight - && it->second.mName == newPotion.mName - && it->second.mEffects.mList.size() == newPotion.mEffects.mList.size()) - { - // check effects - for (unsigned int i=0; i < it->second.mEffects.mList.size(); ++i) - { - const ESM::ENAMstruct& a = it->second.mEffects.mList[i]; - const ESM::ENAMstruct& b = newPotion.mEffects.mList[i]; - if (a.mEffectID == b.mEffectID - && a.mArea == b.mArea - && a.mRange == b.mRange - && a.mSkill == b.mSkill - && a.mAttribute == b.mAttribute - && a.mMagnMin == b.mMagnMin - && a.mMagnMax == b.mMagnMax - && a.mDuration == b.mDuration) - { - found = true; - objectId = it->first; - break; - } - } - } - } - - if (!found) - { - std::pair result = MWBase::Environment::get().getWorld()->createRecord(newPotion); - objectId = result.first; - } - - // create a reference and add it to player inventory - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), objectId); - MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - ref.getPtr().getRefData().setCount(1); - store.add(ref.getPtr()); - mWindowManager.messageBox("#{sPotionSuccess}", std::vector()); MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f); } - else + else if (result == MWMechanics::Alchemy::Result_RandomFailure) { // potion failed mWindowManager.messageBox("#{sNotifyMessage8}", std::vector()); @@ -220,155 +115,53 @@ namespace MWGui } // reduce count of the ingredients - if (mIngredient1->isUserString("ToolTipType")) - { - MWWorld::Ptr ingred = *mIngredient1->getUserData(); - ingred.getRefData().setCount(ingred.getRefData().getCount()-1); - if (ingred.getRefData().getCount() == 0) - removeIngredient(mIngredient1); - } - if (mIngredient2->isUserString("ToolTipType")) - { - MWWorld::Ptr ingred = *mIngredient2->getUserData(); - ingred.getRefData().setCount(ingred.getRefData().getCount()-1); - if (ingred.getRefData().getCount() == 0) - removeIngredient(mIngredient2); - } - if (mIngredient3->isUserString("ToolTipType")) - { - MWWorld::Ptr ingred = *mIngredient3->getUserData(); - ingred.getRefData().setCount(ingred.getRefData().getCount()-1); - if (ingred.getRefData().getCount() == 0) - removeIngredient(mIngredient3); - } - if (mIngredient4->isUserString("ToolTipType")) - { - MWWorld::Ptr ingred = *mIngredient4->getUserData(); - ingred.getRefData().setCount(ingred.getRefData().getCount()-1); - if (ingred.getRefData().getCount() == 0) - removeIngredient(mIngredient4); + for (int i=0; i<4; ++i) + if (mIngredients[i]->isUserString("ToolTipType")) + { + MWWorld::Ptr ingred = *mIngredients[i]->getUserData(); + ingred.getRefData().setCount(ingred.getRefData().getCount()-1); + if (ingred.getRefData().getCount() == 0) + removeIngredient(mIngredients[i]); } + update(); } void AlchemyWindow::open() { - openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - setFilter(ContainerBase::Filter_Ingredients); + openContainer (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); // this sets mPtr + setFilter (ContainerBase::Filter_Ingredients); - // pick the best available apparatus - MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + mAlchemy.setAlchemist (mPtr); - MWWorld::Ptr bestAlbemic; - MWWorld::Ptr bestMortarPestle; - MWWorld::Ptr bestCalcinator; - MWWorld::Ptr bestRetort; + int index = 0; - for (MWWorld::ContainerStoreIterator it(store.begin(MWWorld::ContainerStore::Type_Apparatus)); - it != store.end(); ++it) + for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy.beginTools()); + iter!=mAlchemy.endTools() && index (mApparatus.size()); ++iter, ++index) { - MWWorld::LiveCellRef* ref = it->get(); - if (ref->base->mData.mType == ESM::Apparatus::Albemic - && (bestAlbemic.isEmpty() || ref->base->mData.mQuality > bestAlbemic.get()->base->mData.mQuality)) - bestAlbemic = *it; - else if (ref->base->mData.mType == ESM::Apparatus::MortarPestle - && (bestMortarPestle.isEmpty() || ref->base->mData.mQuality > bestMortarPestle.get()->base->mData.mQuality)) - bestMortarPestle = *it; - else if (ref->base->mData.mType == ESM::Apparatus::Calcinator - && (bestCalcinator.isEmpty() || ref->base->mData.mQuality > bestCalcinator.get()->base->mData.mQuality)) - bestCalcinator = *it; - else if (ref->base->mData.mType == ESM::Apparatus::Retort - && (bestRetort.isEmpty() || ref->base->mData.mQuality > bestRetort.get()->base->mData.mQuality)) - bestRetort = *it; + if (!iter->isEmpty()) + { + mApparatus.at (index)->setUserString ("ToolTipType", "ItemPtr"); + mApparatus.at (index)->setUserData (*iter); + mApparatus.at (index)->setImageTexture (getIconPath (*iter)); + } } - if (!bestMortarPestle.isEmpty()) - { - mApparatus1->setUserString("ToolTipType", "ItemPtr"); - mApparatus1->setUserData(bestMortarPestle); - mApparatus1->setImageTexture(getIconPath(bestMortarPestle)); - } - if (!bestAlbemic.isEmpty()) - { - mApparatus2->setUserString("ToolTipType", "ItemPtr"); - mApparatus2->setUserData(bestAlbemic); - mApparatus2->setImageTexture(getIconPath(bestAlbemic)); - } - if (!bestCalcinator.isEmpty()) - { - mApparatus3->setUserString("ToolTipType", "ItemPtr"); - mApparatus3->setUserData(bestCalcinator); - mApparatus3->setImageTexture(getIconPath(bestCalcinator)); - } - if (!bestRetort.isEmpty()) - { - mApparatus4->setUserString("ToolTipType", "ItemPtr"); - mApparatus4->setUserData(bestRetort); - mApparatus4->setImageTexture(getIconPath(bestRetort)); - } + update(); } void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender) { removeIngredient(_sender); - drawItems(); update(); } void AlchemyWindow::onSelectedItemImpl(MWWorld::Ptr item) { - MyGUI::ImageBox* add = NULL; + int res = mAlchemy.addIngredient(item); - // don't allow to add an ingredient that is already added - // (which could happen if two similiar ingredients don't stack because of script / owner) - bool alreadyAdded = false; - std::string name = MWWorld::Class::get(item).getName(item); - if (mIngredient1->isUserString("ToolTipType")) + if (res != -1) { - MWWorld::Ptr item2 = *mIngredient1->getUserData(); - std::string name2 = MWWorld::Class::get(item2).getName(item2); - if (name == name2) - alreadyAdded = true; - } - if (mIngredient2->isUserString("ToolTipType")) - { - MWWorld::Ptr item2 = *mIngredient2->getUserData(); - std::string name2 = MWWorld::Class::get(item2).getName(item2); - if (name == name2) - alreadyAdded = true; - } - if (mIngredient3->isUserString("ToolTipType")) - { - MWWorld::Ptr item2 = *mIngredient3->getUserData(); - std::string name2 = MWWorld::Class::get(item2).getName(item2); - if (name == name2) - alreadyAdded = true; - } - if (mIngredient4->isUserString("ToolTipType")) - { - MWWorld::Ptr item2 = *mIngredient4->getUserData(); - std::string name2 = MWWorld::Class::get(item2).getName(item2); - if (name == name2) - alreadyAdded = true; - } - if (alreadyAdded) - return; - - if (!mIngredient1->isUserString("ToolTipType")) - add = mIngredient1; - if (add == NULL && !mIngredient2->isUserString("ToolTipType")) - add = mIngredient2; - if (add == NULL && !mIngredient3->isUserString("ToolTipType")) - add = mIngredient3; - if (add == NULL && !mIngredient4->isUserString("ToolTipType")) - add = mIngredient4; - - if (add != NULL) - { - add->setUserString("ToolTipType", "ItemPtr"); - add->setUserData(item); - add->setImageTexture(getIconPath(item)); - drawItems(); update(); std::string sound = MWWorld::Class::get(item).getUpSoundId(item); @@ -380,54 +173,40 @@ namespace MWGui { std::vector ignore; // don't show ingredients that are currently selected in the "available ingredients" box. - if (mIngredient1->isUserString("ToolTipType")) - ignore.push_back(*mIngredient1->getUserData()); - if (mIngredient2->isUserString("ToolTipType")) - ignore.push_back(*mIngredient2->getUserData()); - if (mIngredient3->isUserString("ToolTipType")) - ignore.push_back(*mIngredient3->getUserData()); - if (mIngredient4->isUserString("ToolTipType")) - ignore.push_back(*mIngredient4->getUserData()); + for (int i=0; i<4; ++i) + if (mIngredients[i]->isUserString("ToolTipType")) + ignore.push_back(*mIngredients[i]->getUserData()); return ignore; } void AlchemyWindow::update() { - Widgets::SpellEffectList effects; - + MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients (); for (int i=0; i<4; ++i) { - MyGUI::ImageBox* ingredient; - if (i==0) - ingredient = mIngredient1; - else if (i==1) - ingredient = mIngredient2; - else if (i==2) - ingredient = mIngredient3; - else if (i==3) - ingredient = mIngredient4; + MyGUI::ImageBox* ingredient = mIngredients[i]; - if (!ingredient->isUserString("ToolTipType")) - continue; - - // add the effects of this ingredient to list of effects - MWWorld::LiveCellRef* ref = ingredient->getUserData()->get(); - for (int i=0; i<4; ++i) + MWWorld::Ptr item; + if (it != mAlchemy.endIngredients ()) { - if (ref->base->mData.mEffectID[i] < 0) - continue; - MWGui::Widgets::SpellEffectParams params; - params.mEffectID = ref->base->mData.mEffectID[i]; - params.mAttribute = ref->base->mData.mAttributes[i]; - params.mSkill = ref->base->mData.mSkills[i]; - effects.push_back(params); + item = *it; + ++it; } - // update ingredient count labels if (ingredient->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0)); + ingredient->setImageTexture(""); + ingredient->clearUserStrings (); + + if (item.isEmpty ()) + continue; + + ingredient->setUserString("ToolTipType", "ItemPtr"); + ingredient->setUserData(item); + ingredient->setImageTexture(getIconPath(item)); + MyGUI::TextBox* text = ingredient->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); text->setTextAlign(MyGUI::Align::Right); text->setNeedMouseFocus(false); @@ -436,49 +215,15 @@ namespace MWGui text->setCaption(getCountString(ingredient->getUserData()->getRefData().getCount())); } - // now remove effects that are only present once - Widgets::SpellEffectList::iterator it = effects.begin(); - while (it != effects.end()) - { - Widgets::SpellEffectList::iterator next = it; - ++next; - bool found = false; - for (; next != effects.end(); ++next) - { - if (*next == *it) - found = true; - } + drawItems(); - if (!found) - it = effects.erase(it); - else - ++it; - } - - // now remove duplicates, and don't allow more than 4 effects - Widgets::SpellEffectList old = effects; - effects.clear(); - int i=0; - for (Widgets::SpellEffectList::iterator it = old.begin(); - it != old.end(); ++it) + std::vector effects; + ESM::EffectList list; + list.mList = effects; + for (MWMechanics::Alchemy::TEffectsIterator it = mAlchemy.beginEffects (); it != mAlchemy.endEffects (); ++it) { - bool found = false; - for (Widgets::SpellEffectList::iterator it2 = effects.begin(); - it2 != effects.end(); ++it2) - { - // MW considers all "foritfy attribute" effects as the same effect. See the - // "Can't create multi-state boost potions" discussion on http://www.uesp.net/wiki/Morrowind_talk:Alchemy - // thus, we are only checking effectID here and not attribute or skill - if (it2->mEffectID == it->mEffectID) - found = true; - } - if (!found && i<4) - { - ++i; - effects.push_back(*it); - } + list.mList.push_back(*it); } - mEffects = effects; while (mEffectsBox->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mEffectsBox->getChildAt(0)); @@ -487,7 +232,9 @@ namespace MWGui Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget ("MW_StatName", coord, MyGUI::Align::Left | MyGUI::Align::Top); effectsWidget->setWindowManager(&mWindowManager); - effectsWidget->setEffectList(effects); + + Widgets::SpellEffectList _list = Widgets::MWEffectList::effectListFromESM(&list); + effectsWidget->setEffectList(_list); std::vector effectItems; effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0); @@ -496,9 +243,10 @@ namespace MWGui void AlchemyWindow::removeIngredient(MyGUI::Widget* ingredient) { - ingredient->clearUserStrings(); - static_cast(ingredient)->setImageTexture(""); - if (ingredient->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0)); + for (int i=0; i<4; ++i) + if (mIngredients[i] == ingredient) + mAlchemy.removeIngredient (i); + + update(); } } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 53e7178d50..5f84e73e9b 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -1,6 +1,10 @@ #ifndef MWGUI_ALCHEMY_H #define MWGUI_ALCHEMY_H +#include + +#include "../mwmechanics/alchemy.hpp" + #include "window_base.hpp" #include "container.hpp" #include "widgets.hpp" @@ -18,22 +22,10 @@ namespace MWGui MyGUI::Button* mCreateButton; MyGUI::Button* mCancelButton; - MyGUI::ImageBox* mIngredient1; - MyGUI::ImageBox* mIngredient2; - MyGUI::ImageBox* mIngredient3; - MyGUI::ImageBox* mIngredient4; - - MyGUI::ImageBox* mApparatus1; - MyGUI::ImageBox* mApparatus2; - MyGUI::ImageBox* mApparatus3; - MyGUI::ImageBox* mApparatus4; - MyGUI::Widget* mEffectsBox; MyGUI::EditBox* mNameEdit; - Widgets::SpellEffectList mEffects; // effects of created potion - void onCancelButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); @@ -46,6 +38,13 @@ namespace MWGui virtual void onReferenceUnavailable() { ; } void update(); + + private: + + MWMechanics::Alchemy mAlchemy; + + std::vector mApparatus; + std::vector mIngredients; }; } diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index c8ef35be39..284653aee3 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -14,6 +14,16 @@ using namespace MWGui; using namespace Widgets; +namespace +{ + +bool sortBirthSigns(const std::pair& left, const std::pair& right) +{ + return left.second->mName.compare (right.second->mName) < 0; +} + +} + BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_birth.layout", parWindowManager) { @@ -115,11 +125,21 @@ void BirthDialog::updateBirths() ESMS::RecListT::MapType::const_iterator it = store.birthSigns.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.birthSigns.list.end(); int index = 0; - for (; it != end; ++it) + + // sort by name + std::vector < std::pair > birthSigns; + for (; it!=end; ++it) { - const ESM::BirthSign &birth = it->second; - mBirthList->addItem(birth.mName, it->first); - if (boost::iequals(it->first, mCurrentBirthId)) + std::string id = it->first; + const ESM::BirthSign* sign = &it->second; + birthSigns.push_back(std::make_pair(id, sign)); + } + std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); + + for (std::vector < std::pair >::const_iterator it2 = birthSigns.begin(); it2 != birthSigns.end(); ++it2) + { + mBirthList->addItem(it2->second->mName, it2->first); + if (boost::iequals(it2->first, mCurrentBirthId)) mBirthList->setIndexSelected(index); ++index; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index d03724628f..23d2197b70 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -19,6 +19,7 @@ #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" #include "inventorywindow.hpp" +#include "travelwindow.hpp" using namespace MWGui; using namespace Widgets; @@ -137,6 +138,11 @@ void DialogueWindow::onSelectTopic(std::string topic) mWindowManager.pushGuiMode(GM_SpellBuying); mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); } + else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sTravel")->getString()) + { + mWindowManager.pushGuiMode(GM_Travel); + mWindowManager.getTravelWindow()->startTravel(mPtr); + } else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sSpellMakingMenuTitle")->getString()) { mWindowManager.pushGuiMode(GM_SpellCreation); @@ -147,6 +153,11 @@ void DialogueWindow::onSelectTopic(std::string topic) mWindowManager.pushGuiMode(GM_Enchanting); mWindowManager.startEnchanting (mPtr); } + else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sServiceTrainingTitle")->getString()) + { + mWindowManager.pushGuiMode(GM_Training); + mWindowManager.startTraining (mPtr); + } else MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); } @@ -175,12 +186,18 @@ void DialogueWindow::setKeywords(std::list keyWords) if (mServices & Service_BuySpells) mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sSpells")->getString()); + if (mServices & Service_Travel) + mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sTravel")->getString()); + if (mServices & Service_CreateSpells) mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sSpellmakingMenuTitle")->getString()); if (mServices & Service_Enchant) mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sEnchanting")->getString()); + if (mServices & Service_Training) + mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sServiceTrainingTitle")->getString()); + if (anyService) mTopicsList->addSeparator(); diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index acbe75eed3..3a89409cac 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -48,7 +48,6 @@ namespace MWGui void askQuestion(std::string question); void goodbye(); - // various service button visibilities, depending if the npc/creature talked to has these services // make sure to call these before setKeywords() void setServices(int services) { mServices = services; } @@ -57,7 +56,9 @@ namespace MWGui Service_Trade = 0x01, Service_BuySpells = 0x02, Service_CreateSpells = 0x04, - Service_Enchant = 0x08 + Service_Enchant = 0x08, + Service_Training = 0x10, + Service_Travel = 0x20 }; protected: @@ -76,10 +77,6 @@ namespace MWGui */ std::string parseText(std::string text); - // various service button visibilities, depending if the npc/creature talked to has these services - bool mShowTrade; - bool mShowSpells; - int mServices; bool mEnabled; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index ed0e592269..66cc6b21ad 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -245,15 +245,7 @@ void HUD::onWorldClicked(MyGUI::Widget* _sender) return; std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); - MWWorld::Ptr object; - try - { - object = MWBase::Environment::get().getWorld()->getPtrViaHandle(handle); - } - catch (std::exception& /* e */) - { - return; - } + MWWorld::Ptr object = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); if (mode == GM_Console) MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 170fc3bc5f..8bcaa6ce06 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -6,10 +6,13 @@ #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -106,6 +109,7 @@ namespace MWGui if (mTimer.getMilliseconds () > mLastRenderTime + (1.f/loadingScreenFps) * 1000.f) { + float dt = mTimer.getMilliseconds () - mLastRenderTime; mLastRenderTime = mTimer.getMilliseconds (); if (mFirstLoad && mTimer.getMilliseconds () > mLastWallpaperChangeTime + 3000*1) @@ -151,6 +155,8 @@ namespace MWGui } } + MWBase::Environment::get().getWorld ()->getFader ()->update (dt); + mWindow->update(); if (!hasCompositor) @@ -207,20 +213,22 @@ namespace MWGui void LoadingScreen::changeWallpaper () { - /// \todo use a directory listing here std::vector splash; - splash.push_back ("Splash_Bonelord.tga"); - splash.push_back ("Splash_ClannDaddy.tga"); - splash.push_back ("Splash_Clannfear.tga"); - splash.push_back ("Splash_Daedroth.tga"); - splash.push_back ("Splash_Hunger.tga"); - splash.push_back ("Splash_KwamaWarrior.tga"); - splash.push_back ("Splash_Netch.tga"); - splash.push_back ("Splash_NixHound.tga"); - splash.push_back ("Splash_Siltstriker.tga"); - splash.push_back ("Splash_Skeleton.tga"); - splash.push_back ("Splash_SphereCenturion.tga"); - mBackgroundImage->setImageTexture (splash[rand() % splash.size()]); + Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); + for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) + { + if (it->size() < 6) + continue; + std::string start = it->substr(0, 6); + boost::to_lower(start); + + if (start == "splash") + splash.push_back (*it); + } + std::string randomSplash = splash[rand() % splash.size()]; + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); + mBackgroundImage->setImageTexture (randomSplash); } } diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 7fd033f5ee..d791a1c99d 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -22,8 +22,10 @@ namespace MWGui GM_Rest, GM_RestBed, GM_SpellBuying, + GM_Travel, GM_SpellCreation, GM_Enchanting, + GM_Training, GM_Levelup, diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index c1a181de0d..ece19cdc38 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -54,7 +54,7 @@ namespace MWGui MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", + (price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 39afba7497..35224613ce 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -79,14 +79,10 @@ void ToolTips::onFrame(float frameDuration) || (mWindowManager->getMode() == GM_Inventory))) { std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); - try - { - mFocusObject = MWBase::Environment::get().getWorld()->getPtrViaHandle(handle); - } - catch (std::exception /* & e */) - { + + mFocusObject = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); + if (mFocusObject.isEmpty ()) return; - } MyGUI::IntSize tooltipSize = getToolTipViaPtr(true); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp new file mode 100644 index 0000000000..af61b3487b --- /dev/null +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -0,0 +1,159 @@ +#include "trainingwindow.hpp" + +#include + +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "inventorywindow.hpp" +#include "tradewindow.hpp" +#include "tooltips.hpp" + +namespace MWGui +{ + + TrainingWindow::TrainingWindow(MWBase::WindowManager &parWindowManager) + : WindowBase("openmw_trainingwindow.layout", parWindowManager) + , mFadeTimeRemaining(0) + { + getWidget(mTrainingOptions, "TrainingOptions"); + getWidget(mCancelButton, "CancelButton"); + getWidget(mPlayerGold, "PlayerGold"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked); + } + + void TrainingWindow::open() + { + center(); + } + + void TrainingWindow::startTraining (MWWorld::Ptr actor) + { + mPtr = actor; + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats (actor); + + // NPC can train you in his best 3 skills + std::vector< std::pair > bestSkills; + bestSkills.push_back (std::make_pair(-1, -1)); + bestSkills.push_back (std::make_pair(-1, -1)); + bestSkills.push_back (std::make_pair(-1, -1)); + + for (int i=0; i bestSkills[j].second) + { + if (j<2) + { + bestSkills[j+1] = bestSkills[j]; + } + bestSkills[j] = std::make_pair(i, value); + break; + } + } + } + + MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); + MyGUI::Gui::getInstance ().destroyWidgets (widgets); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + for (int i=0; i<3; ++i) + { + /// \todo mercantile skill + int price = pcStats.getSkill (bestSkills[i].first).getBase() * MWBase::Environment::get().getWorld ()->getStore ().gameSettings.find("iTrainingMod")->getInt (); + + std::string skin = (price > mWindowManager.getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; + + MyGUI::Button* button = mTrainingOptions->createWidget(skin, + MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); + + button->setUserData(bestSkills[i].first); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); + + button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[bestSkills[i].first] + "} - " + boost::lexical_cast(price)); + + button->setSize(button->getTextSize ().width+12, button->getSize().height); + + ToolTips::createSkillToolTip (button, bestSkills[i].first); + } + + center(); + } + + void TrainingWindow::onReferenceUnavailable () + { + mWindowManager.removeGuiMode(GM_Training); + } + + void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender) + { + mWindowManager.removeGuiMode (GM_Training); + } + + void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender) + { + int skillId = *sender->getUserData(); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + /// \todo mercantile skill + int price = pcStats.getSkill (skillId).getBase() * MWBase::Environment::get().getWorld ()->getStore ().gameSettings.find("iTrainingMod")->getInt (); + + if (mWindowManager.getInventoryWindow()->getPlayerGold()()); + return; + } + + // increase skill + MWWorld::LiveCellRef *playerRef = player.get(); + const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find ( + playerRef->base->mClass); + pcStats.increaseSkill (skillId, *class_, true); + + // remove gold + mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + + // go back to game mode + mWindowManager.removeGuiMode (GM_Training); + mWindowManager.removeGuiMode (GM_Dialogue); + + // advance time + MWBase::Environment::get().getWorld ()->advanceTime (2); + + MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25); + mFadeTimeRemaining = 0.5; + } + + void TrainingWindow::onFrame(float dt) + { + if (mFadeTimeRemaining <= 0) + return; + + mFadeTimeRemaining -= dt; + + if (mFadeTimeRemaining <= 0) + MWBase::Environment::get().getWorld ()->getFader()->fadeIn(0.25); + } +} diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp new file mode 100644 index 0000000000..f2ef1714ee --- /dev/null +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -0,0 +1,36 @@ +#ifndef MWGUI_TRAININGWINDOW_H +#define MWGUI_TRAININGWINDOW_H + +#include "window_base.hpp" +#include "referenceinterface.hpp" + +namespace MWGui +{ + + class TrainingWindow : public WindowBase, public ReferenceInterface + { + public: + TrainingWindow(MWBase::WindowManager& parWindowManager); + + virtual void open(); + + void startTraining(MWWorld::Ptr actor); + + void onFrame(float dt); + + protected: + virtual void onReferenceUnavailable (); + + void onCancelButtonClicked (MyGUI::Widget* sender); + void onTrainingSelected(MyGUI::Widget* sender); + + MyGUI::Widget* mTrainingOptions; + MyGUI::Button* mCancelButton; + MyGUI::TextBox* mPlayerGold; + + float mFadeTimeRemaining; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp new file mode 100644 index 0000000000..c19639aa6a --- /dev/null +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -0,0 +1,187 @@ +#include "travelwindow.hpp" + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" + +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" + +#include "inventorywindow.hpp" +#include "tradewindow.hpp" + +namespace MWGui +{ + const int TravelWindow::sLineHeight = 18; + + TravelWindow::TravelWindow(MWBase::WindowManager& parWindowManager) : + WindowBase("openmw_travel_window.layout", parWindowManager) + , mCurrentY(0) + , mLastPos(0) + { + setCoord(0, 0, 450, 300); + + getWidget(mCancelButton, "CancelButton"); + getWidget(mPlayerGold, "PlayerGold"); + getWidget(mSelect, "Select"); + getWidget(mDestinations, "Travel"); + getWidget(mDestinationsView, "DestinationsView"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onCancelButtonClicked); + + mDestinations->setCoord(450/2-mDestinations->getTextSize().width/2, + mDestinations->getTop(), + mDestinations->getTextSize().width, + mDestinations->getHeight()); + mSelect->setCoord(8, + mSelect->getTop(), + mSelect->getTextSize().width, + mSelect->getHeight()); + } + + void TravelWindow::addDestination(const std::string& travelId,ESM::Position pos,bool interior) + { + int price = 0; + + if(interior) + { + price = MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fMagesGuildTravel")->getFloat(); + } + else + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Position PlayerPos = player.getRefData().getPosition(); + float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); + price = d/MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fTravelMult")->getFloat(); + } + + MyGUI::Button* toAdd = mDestinationsView->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + mCurrentY += sLineHeight; + /// \todo price adjustment depending on merchantile skill + if(interior) + toAdd->setUserString("interior","y"); + else + toAdd->setUserString("interior","n"); + + std::ostringstream oss; + oss << price; + toAdd->setUserString("price",oss.str()); + + toAdd->setCaptionWithReplacing(travelId+" - "+boost::lexical_cast(price)+"#{sgp}"); + toAdd->setSize(toAdd->getTextSize().width,sLineHeight); + toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); + toAdd->setUserString("Destination", travelId); + toAdd->setUserData(pos); + toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onTravelButtonClick); + mDestinationsWidgetMap.insert(std::make_pair (toAdd, travelId)); + } + + void TravelWindow::clearDestinations() + { + mDestinationsView->setViewOffset(MyGUI::IntPoint(0,0)); + mCurrentY = 0; + while (mDestinationsView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0)); + mDestinationsWidgetMap.clear(); + } + + void TravelWindow::startTravel(const MWWorld::Ptr& actor) + { + center(); + mPtr = actor; + clearDestinations(); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + for(unsigned int i = 0;i()->base->mTransport.size();i++) + { + std::string cellname = mPtr.get()->base->mTransport[i].mCellName; + bool interior = true; + int x,y; + MWBase::Environment::get().getWorld()->positionToIndex(mPtr.get()->base->mTransport[i].mPos.pos[0], + mPtr.get()->base->mTransport[i].mPos.pos[1],x,y); + if(cellname == "") {cellname = MWBase::Environment::get().getWorld()->getExterior(x,y)->cell->mName; interior= false;} + addDestination(cellname,mPtr.get()->base->mTransport[i].mPos,interior); + } + + updateLabels(); + mDestinationsView->setCanvasSize (MyGUI::IntSize(mDestinationsView->getWidth(), std::max(mDestinationsView->getHeight(), mCurrentY))); + } + + void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender) + { + std::istringstream iss(_sender->getUserString("price")); + int price; + iss >> price; + + if (mWindowManager.getInventoryWindow()->getPlayerGold()getFader ()->fadeOut(1); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Position pos = *_sender->getUserData(); + std::string cellname = _sender->getUserString("Destination"); + int x,y; + bool interior = _sender->getUserString("interior") == "y"; + MWBase::Environment::get().getWorld()->positionToIndex(pos.pos[0],pos.pos[1],x,y); + MWWorld::CellStore* cell; + if(interior) cell = MWBase::Environment::get().getWorld()->getInterior(cellname); + else + { + cell = MWBase::Environment::get().getWorld()->getExterior(x,y); + ESM::Position PlayerPos = player.getRefData().getPosition(); + float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); + int time = int(d /MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fTravelTimeMult")->getFloat()); + for(int i = 0;i < time;i++) + { + MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); + } + MWBase::Environment::get().getWorld()->advanceTime(time); + } + MWBase::Environment::get().getWorld()->moveObject(player,*cell,pos.pos[0],pos.pos[1],pos.pos[2]); + mWindowManager.removeGuiMode(GM_Travel); + mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0); + MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1); + } + + void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender) + { + mWindowManager.removeGuiMode(GM_Travel); + } + + void TravelWindow::updateLabels() + { + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCoord(8, + mPlayerGold->getTop(), + mPlayerGold->getTextSize().width, + mPlayerGold->getHeight()); + } + + void TravelWindow::onReferenceUnavailable() + { + mWindowManager.removeGuiMode(GM_Travel); + mWindowManager.removeGuiMode(GM_Dialogue); + } + + void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mDestinationsView->getViewOffset().top + _rel*0.3 > 0) + mDestinationsView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mDestinationsView->setViewOffset(MyGUI::IntPoint(0, mDestinationsView->getViewOffset().top + _rel*0.3)); + } +} + diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp new file mode 100644 index 0000000000..cc3d6a31f7 --- /dev/null +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -0,0 +1,55 @@ +#ifndef MWGUI_TravelWINDOW_H +#define MWGUI_TravelWINDOW_H + +#include "container.hpp" +#include "window_base.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; +} + +namespace MWGui +{ + class WindowManager; +} + + +namespace MWGui +{ + class TravelWindow : public ReferenceInterface, public WindowBase + { + public: + TravelWindow(MWBase::WindowManager& parWindowManager); + + void startTravel(const MWWorld::Ptr& actor); + + protected: + MyGUI::Button* mCancelButton; + MyGUI::TextBox* mPlayerGold; + MyGUI::TextBox* mDestinations; + MyGUI::TextBox* mSelect; + + MyGUI::ScrollView* mDestinationsView; + + std::map mDestinationsWidgetMap; + + void onCancelButtonClicked(MyGUI::Widget* _sender); + void onTravelButtonClick(MyGUI::Widget* _sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void addDestination(const std::string& destinationID,ESM::Position pos,bool interior); + void clearDestinations(); + int mLastPos,mCurrentY; + + static const int sLineHeight; + + void updateLabels(); + + virtual void onReferenceUnavailable(); + }; +} + +#endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 906bb2ca7e..0350581e45 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -38,6 +38,7 @@ #include "countdialog.hpp" #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" +#include "travelwindow.hpp" #include "settingswindow.hpp" #include "confirmationdialog.hpp" #include "alchemywindow.hpp" @@ -48,6 +49,7 @@ #include "waitdialog.hpp" #include "spellcreationdialog.hpp" #include "enchantingdialog.hpp" +#include "trainingwindow.hpp" using namespace MWGui; @@ -69,6 +71,7 @@ WindowManager::WindowManager( , mCountDialog(NULL) , mTradeWindow(NULL) , mSpellBuyingWindow(NULL) + , mTravelWindow(NULL) , mSettingsWindow(NULL) , mConfirmationDialog(NULL) , mAlchemyWindow(NULL) @@ -79,6 +82,7 @@ WindowManager::WindowManager( , mWaitDialog(NULL) , mSpellCreationDialog(NULL) , mEnchantingDialog(NULL) + , mTrainingWindow(NULL) , mPlayerClass() , mPlayerName() , mPlayerRaceId() @@ -145,6 +149,7 @@ WindowManager::WindowManager( mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); mTradeWindow = new TradeWindow(*this); mSpellBuyingWindow = new SpellBuyingWindow(*this); + mTravelWindow = new TravelWindow(*this); mDialogueWindow = new DialogueWindow(*this); mContainerWindow = new ContainerWindow(*this,mDragAndDrop); mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); @@ -161,6 +166,7 @@ WindowManager::WindowManager( mWaitDialog = new WaitDialog(*this); mSpellCreationDialog = new SpellCreationDialog(*this); mEnchantingDialog = new EnchantingDialog(*this); + mTrainingWindow = new TrainingWindow(*this); mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); mLoadingScreen->onResChange (w,h); @@ -209,6 +215,7 @@ WindowManager::~WindowManager() delete mScrollWindow; delete mTradeWindow; delete mSpellBuyingWindow; + delete mTravelWindow; delete mSettingsWindow; delete mConfirmationDialog; delete mAlchemyWindow; @@ -218,6 +225,7 @@ WindowManager::~WindowManager() delete mWaitDialog; delete mSpellCreationDialog; delete mEnchantingDialog; + delete mTrainingWindow; cleanupGarbage(); @@ -261,6 +269,7 @@ void WindowManager::updateVisible() mBookWindow->setVisible(false); mTradeWindow->setVisible(false); mSpellBuyingWindow->setVisible(false); + mTravelWindow->setVisible(false); mSettingsWindow->setVisible(false); mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); @@ -269,6 +278,7 @@ void WindowManager::updateVisible() mWaitDialog->setVisible(false); mSpellCreationDialog->setVisible(false); mEnchantingDialog->setVisible(false); + mTrainingWindow->setVisible(false); mHud->setVisible(true); @@ -369,12 +379,18 @@ void WindowManager::updateVisible() case GM_SpellBuying: mSpellBuyingWindow->setVisible(true); break; + case GM_Travel: + mTravelWindow->setVisible(true); + break; case GM_SpellCreation: mSpellCreationDialog->setVisible(true); break; case GM_Enchanting: mEnchantingDialog->setVisible(true); break; + case GM_Training: + mTrainingWindow->setVisible(true); + break; case GM_InterMessageBox: break; case GM_Journal: @@ -574,6 +590,9 @@ void WindowManager::onFrame (float frameDuration) mHud->onFrame(frameDuration); + mTrainingWindow->onFrame (frameDuration); + + mTrainingWindow->checkReferenceAvailable(); mDialogueWindow->checkReferenceAvailable(); mTradeWindow->checkReferenceAvailable(); mSpellBuyingWindow->checkReferenceAvailable(); @@ -862,6 +881,7 @@ MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } +MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } MWGui::Console* WindowManager::getConsole() { return mConsole; } @@ -993,3 +1013,8 @@ void WindowManager::startEnchanting (MWWorld::Ptr actor) { mEnchantingDialog->startEnchanting (actor); } + +void WindowManager::startTraining(MWWorld::Ptr actor) +{ + mTrainingWindow->startTraining(actor); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index d3890f6351..aa796343e9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -66,6 +66,7 @@ namespace MWGui class WaitDialog; class SpellCreationDialog; class EnchantingDialog; + class TrainingWindow; class WindowManager : public MWBase::WindowManager { @@ -113,6 +114,7 @@ namespace MWGui virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::TradeWindow* getTradeWindow(); virtual MWGui::SpellBuyingWindow* getSpellBuyingWindow(); + virtual MWGui::TravelWindow* getTravelWindow(); virtual MWGui::SpellWindow* getSpellWindow(); virtual MWGui::Console* getConsole(); @@ -215,6 +217,7 @@ namespace MWGui virtual void startSpellMaking(MWWorld::Ptr actor); virtual void startEnchanting(MWWorld::Ptr actor); + virtual void startTraining(MWWorld::Ptr actor); private: OEngine::GUI::MyGUIManager *mGuiManager; @@ -235,6 +238,7 @@ namespace MWGui CountDialog* mCountDialog; TradeWindow* mTradeWindow; SpellBuyingWindow* mSpellBuyingWindow; + TravelWindow* mTravelWindow; SettingsWindow* mSettingsWindow; ConfirmationDialog* mConfirmationDialog; AlchemyWindow* mAlchemyWindow; @@ -245,6 +249,7 @@ namespace MWGui WaitDialog* mWaitDialog; SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; + TrainingWindow* mTrainingWindow; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 87a0583948..ab332eb960 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -118,18 +118,14 @@ namespace MWMechanics + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified ()); } } - - - } - void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); // attributes - for (int i=0; i<5; ++i) + for (int i=0; i<8; ++i) { int modifier = creatureStats.getMagicEffects().get (EffectKey (79, i)).mMagnitude; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp new file mode 100644 index 0000000000..df7c786b1e --- /dev/null +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -0,0 +1,490 @@ + +#include "alchemy.hpp" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/manualref.hpp" + +#include "magiceffects.hpp" +#include "creaturestats.hpp" +#include "npcstats.hpp" + +std::set MWMechanics::Alchemy::listEffects() const +{ + std::set effects; + + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) + { + if (!iter->isEmpty()) + { + const MWWorld::LiveCellRef *ingredient = iter->get(); + + for (int i=0; i<4; ++i) + if (ingredient->base->mData.mEffectID[i]!=-1) + effects.insert (EffectKey ( + ingredient->base->mData.mEffectID[i], ingredient->base->mData.mSkills[i]!=-1 ? + ingredient->base->mData.mSkills[i] : ingredient->base->mData.mAttributes[i])); + } + } + + return effects; +} + +void MWMechanics::Alchemy::filterEffects (std::set& effects) const +{ + std::set::iterator iter = effects.begin(); + + while (iter!=effects.end()) + { + bool remove = false; + + const EffectKey& key = *iter; + + { // dodge pointless g++ warning + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) + { + bool found = false; + + if (iter->isEmpty()) + continue; + + const MWWorld::LiveCellRef *ingredient = iter->get(); + + for (int i=0; i<4; ++i) + if (key.mId==ingredient->base->mData.mEffectID[i] && + (key.mArg==ingredient->base->mData.mSkills[i] || + key.mArg==ingredient->base->mData.mAttributes[i])) + { + found = true; + break; + } + + if (!found) + { + remove = true; + break; + } + } + } + + if (remove) + effects.erase (iter++); + else + ++iter; + } +} + +void MWMechanics::Alchemy::applyTools (int flags, float& value) const +{ + bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude); + bool duration = !(flags & ESM::MagicEffect::NoDuration); + bool negative = flags & (ESM::MagicEffect::Negative | ESM::MagicEffect::Harmful); + + int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic; + + int setup = 0; + + if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty()) + setup = 1; + else if (!mTools[tool].isEmpty()) + setup = 2; + else if (!mTools[ESM::Apparatus::Calcinator].isEmpty()) + setup = 3; + else + return; + + float toolQuality = setup==1 || setup==2 ? mTools[tool].get()->base->mData.mQuality : 0; + float calcinatorQuality = setup==1 || setup==3 ? + mTools[ESM::Apparatus::Calcinator].get()->base->mData.mQuality : 0; + + float quality = 1; + + switch (setup) + { + case 1: + + quality = negative ? 2 * toolQuality + 3 * calcinatorQuality : + (magnitude && duration ? + 2 * toolQuality + calcinatorQuality : 2/3.0 * (toolQuality + calcinatorQuality) + 0.5); + break; + + case 2: + + quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5); + break; + + case 3: + + quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5; + break; + } + + if (setup==3 || !negative) + { + value += quality; + } + else + { + if (quality==0) + throw std::runtime_error ("invalid derived alchemy apparatus quality"); + + value /= quality; + } +} + +void MWMechanics::Alchemy::updateEffects() +{ + mEffects.clear(); + mValue = 0; + + if (countIngredients()<2 || mAlchemist.isEmpty() || mTools[ESM::Apparatus::MortarPestle].isEmpty()) + return; + + // find effects + std::set effects (listEffects()); + filterEffects (effects); + + // general alchemy factor + float x = getChance(); + + x *= mTools[ESM::Apparatus::MortarPestle].get()->base->mData.mQuality; + x *= MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionStrengthMult")->getFloat(); + + // value + mValue = static_cast ( + x * MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("iAlchemyMod")->getFloat()); + + // build quantified effect list + for (std::set::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) + { + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().magicEffects.find (iter->mId); + + if (magicEffect->mData.mBaseCost<=0) + throw std::runtime_error ("invalid base cost for magic effect " + iter->mId); + + float fPotionT1MagMul = + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionT1MagMult")->getFloat(); + + if (fPotionT1MagMul<=0) + throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); + + float fPotionT1DurMult = + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionT1DurMult")->getFloat(); + + if (fPotionT1DurMult<=0) + throw std::runtime_error ("invalid gmst: fPotionT1DurMult"); + + float magnitude = magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude ? + 1 : (x / fPotionT1MagMul) / magicEffect->mData.mBaseCost; + float duration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ? + 1 : (x / fPotionT1DurMult) / magicEffect->mData.mBaseCost; + + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + applyTools (magicEffect->mData.mFlags, magnitude); + + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + applyTools (magicEffect->mData.mFlags, duration); + + duration = static_cast (duration+0.5); + magnitude = static_cast (magnitude+0.5); + + if (magnitude>0 && duration>0) + { + ESM::ENAMstruct effect; + effect.mEffectID = iter->mId; + + effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work + + effect.mRange = 0; + effect.mArea = 0; + + effect.mDuration = duration; + effect.mMagnMin = effect.mMagnMax = magnitude; + + mEffects.push_back (effect); + } + } +} + +const ESM::Potion *MWMechanics::Alchemy::getRecord() const +{ + for (ESMS::RecListWithIDT::MapType::const_iterator iter ( + MWBase::Environment::get().getWorld()->getStore().potions.list.begin()); + iter!=MWBase::Environment::get().getWorld()->getStore().potions.list.end(); ++iter) + { + if (iter->second.mEffects.mList.size()!=mEffects.size()) + continue; + + bool mismatch = false; + + for (int i=0; i (iter->second.mEffects.mList.size()); ++iter) + { + const ESM::ENAMstruct& first = iter->second.mEffects.mList[i]; + const ESM::ENAMstruct& second = mEffects[i]; + + if (first.mEffectID!=second.mEffectID || + first.mArea!=second.mArea || + first.mRange!=second.mRange || + first.mSkill!=second.mSkill || + first.mAttribute!=second.mAttribute || + first.mMagnMin!=second.mMagnMin || + first.mMagnMax!=second.mMagnMax || + first.mDuration!=second.mDuration) + { + mismatch = true; + break; + } + } + + if (!mismatch) + return &iter->second; + } + + return 0; +} + +void MWMechanics::Alchemy::removeIngredients() +{ + bool needsUpdate = false; + + for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) + if (!iter->isEmpty()) + { + iter->getRefData().setCount (iter->getRefData().getCount()-1); + if (iter->getRefData().getCount()<1) + { + needsUpdate = true; + *iter = MWWorld::Ptr(); + } + } + + if (needsUpdate) + updateEffects(); +} + +void MWMechanics::Alchemy::addPotion (const std::string& name) +{ + const ESM::Potion *record = getRecord(); + + if (!record) + { + ESM::Potion newRecord; + + newRecord.mData.mWeight = 0; + + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) + if (!iter->isEmpty()) + newRecord.mData.mWeight += iter->get()->base->mData.mWeight; + + newRecord.mData.mWeight /= countIngredients(); + + newRecord.mData.mValue = mValue; + newRecord.mData.mAutoCalc = 0; + + newRecord.mName = name; + + int index = static_cast (std::rand()/static_cast (RAND_MAX)*6); + assert (index>=0 && index<6); + + static const char *name[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" }; + + newRecord.mModel = "m\\misc_potion_" + std::string (name[index]) + "_01.nif"; + newRecord.mIcon = "m\\tx_potion_" + std::string (name[index]) + "_01.dds"; + + newRecord.mEffects.mList = mEffects; + + record = MWBase::Environment::get().getWorld()->createRecord (newRecord).second; + } + + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), record->mId); + MWWorld::Class::get (mAlchemist).getContainerStore (mAlchemist).add (ref.getPtr()); +} + +void MWMechanics::Alchemy::increaseSkill() +{ + MWWorld::Class::get (mAlchemist).skillUsageSucceeded (mAlchemist, ESM::Skill::Alchemy, 0); +} + +float MWMechanics::Alchemy::getChance() const +{ + const CreatureStats& creatureStats = MWWorld::Class::get (mAlchemist).getCreatureStats (mAlchemist); + const NpcStats& npcStats = MWWorld::Class::get (mAlchemist).getNpcStats (mAlchemist); + + return + (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + 0.1 * creatureStats.getAttribute (1).getModified() + + 0.1 * creatureStats.getAttribute (7).getModified()); +} + +int MWMechanics::Alchemy::countIngredients() const +{ + int ingredients = 0; + + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) + if (!iter->isEmpty()) + ++ingredients; + + return ingredients; +} + +void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) +{ + mAlchemist = npc; + + mIngredients.resize (4); + + std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr()); + + mTools.resize (4); + + std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr()); + + mEffects.clear(); + + MWWorld::ContainerStore& store = MWWorld::Class::get (npc).getContainerStore (npc); + + for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus)); + iter!=store.end(); ++iter) + { + MWWorld::LiveCellRef* ref = iter->get(); + + int type = ref->base->mData.mType; + + if (type<0 || type>=static_cast (mTools.size())) + throw std::runtime_error ("invalid apparatus type"); + + if (!mTools[type].isEmpty()) + if (ref->base->mData.mQuality<=mTools[type].get()->base->mData.mQuality) + continue; + + mTools[type] = *iter; + } +} + +MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::beginTools() const +{ + return mTools.begin(); +} + +MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::endTools() const +{ + return mTools.end(); +} + +MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::beginIngredients() const +{ + return mIngredients.begin(); +} + +MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients() const +{ + return mIngredients.end(); +} + +void MWMechanics::Alchemy::clear() +{ + mAlchemist = MWWorld::Ptr(); + mTools.clear(); + mIngredients.clear(); + mEffects.clear(); +} + +int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) +{ + // find a free slot + int slot = -1; + + for (int i=0; i (mIngredients.size()); ++i) + if (mIngredients[i].isEmpty()) + { + slot = i; + break; + } + + if (slot==-1) + return -1; + + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) + if (!iter->isEmpty() && ingredient.get()==iter->get()) + return -1; + + mIngredients[slot] = ingredient; + + updateEffects(); + + return slot; +} + +void MWMechanics::Alchemy::removeIngredient (int index) +{ + if (index>=0 && index (mIngredients.size())) + { + mIngredients[index] = MWWorld::Ptr(); + updateEffects(); + } +} + +MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::beginEffects() const +{ + return mEffects.begin(); +} + +MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const +{ + return mEffects.end(); +} + +std::string MWMechanics::Alchemy::getPotionName() const +{ + if (const ESM::Potion *potion = getRecord()) + return potion->mName; + + return ""; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) +{ + if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) + return Result_NoMortarAndPestle; + + if (countIngredients()<2) + return Result_LessThanTwoIngredients; + + if (name.empty() && getPotionName().empty()) + return Result_NoName; + + if (beginEffects()==endEffects()) + return Result_NoEffects; + + if (getChance() (RAND_MAX)*100) + { + removeIngredients(); + return Result_RandomFailure; + } + + addPotion (name); + + removeIngredients(); + + increaseSkill(); + + return Result_Success; +} diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp new file mode 100644 index 0000000000..7f3e2c0eaa --- /dev/null +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -0,0 +1,121 @@ +#ifndef GAME_MWMECHANICS_ALCHEMY_H +#define GAME_MWMECHANICS_ALCHEMY_H + +#include +#include + +#include + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + struct EffectKey; + + /// \brief Potion creation via alchemy skill + class Alchemy + { + public: + + typedef std::vector TToolsContainer; + typedef TToolsContainer::const_iterator TToolsIterator; + + typedef std::vector TIngredientsContainer; + typedef TIngredientsContainer::const_iterator TIngredientsIterator; + + typedef std::vector TEffectsContainer; + typedef TEffectsContainer::const_iterator TEffectsIterator; + + enum Result + { + Result_Success, + + Result_NoMortarAndPestle, + Result_LessThanTwoIngredients, + Result_NoName, + Result_NoEffects, + Result_RandomFailure + }; + + private: + + MWWorld::Ptr mAlchemist; + TToolsContainer mTools; + TIngredientsContainer mIngredients; + TEffectsContainer mEffects; + int mValue; + + std::set listEffects() const; + ///< List all effects of all used ingredients. + + void filterEffects (std::set& effects) const; + ///< Filter out effects not shared by all ingredients. + + void applyTools (int flags, float& value) const; + + void updateEffects(); + + const ESM::Potion *getRecord() const; + ///< Return existing recrod for created potion (may return 0) + + void removeIngredients(); + ///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and + /// update effect list accordingly. + + void addPotion (const std::string& name); + ///< Add a potion to the alchemist's inventory. + + void increaseSkill(); + ///< Increase alchemist's skill. + + float getChance() const; + ///< Return chance of success. + + int countIngredients() const; + + public: + + void setAlchemist (const MWWorld::Ptr& npc); + ///< Set alchemist and configure alchemy setup accordingly. \a npc may be empty to indicate that + /// there is no alchemist (alchemy session has ended). + + TToolsIterator beginTools() const; + ///< \attention Iterates over tool slots, not over tools. Some of the slots may be empty. + + TToolsIterator endTools() const; + + TIngredientsIterator beginIngredients() const; + ///< \attention Iterates over ingredient slots, not over ingredients. Some of the slots may be empty. + + TIngredientsIterator endIngredients() const; + + void clear(); + ///< Remove alchemist, tools and ingredients. + + int addIngredient (const MWWorld::Ptr& ingredient); + ///< Add ingredient into the next free slot. + /// + /// \return Slot index or -1, if adding failed because of no free slot or the ingredient type being + /// listed already. + + void removeIngredient (int index); + ///< Remove ingredient from slot (calling this function on an empty slot is a no-op). + + TEffectsIterator beginEffects() const; + + TEffectsIterator endEffects() const; + + std::string getPotionName() const; + ///< Return the name of the potion that would be created when calling create (if a record for such + /// a potion already exists) or return an empty string. + + Result create (const std::string& name); + ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + /// adjust the skills of the alchemist accordingly. + /// \param name must not be an empty string, unless there is already a potion record ( + /// getPotionName() does not return an empty string). + }; +} + +#endif + diff --git a/apps/openmw/mwmechanics/drawstate.hpp b/apps/openmw/mwmechanics/drawstate.hpp index 112b6e4f90..5be00505c2 100644 --- a/apps/openmw/mwmechanics/drawstate.hpp +++ b/apps/openmw/mwmechanics/drawstate.hpp @@ -8,7 +8,7 @@ namespace MWMechanics { DrawState_Weapon = 0, DrawState_Spell = 1, - DrawState_Nothing = 2, + DrawState_Nothing = 2 }; } diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index c23fdc1a3b..7a799a2e43 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -53,7 +53,7 @@ namespace MWRender { Ogre::Image image; - Ogre::uchar data[mWidth * mHeight * 3]; + std::vector data (mWidth * mHeight * 3); for (int x = mMinX; x <= mMaxX; ++x) { @@ -150,7 +150,7 @@ namespace MWRender } } - image.loadDynamicImage (data, mWidth, mHeight, Ogre::PF_B8G8R8); + image.loadDynamicImage (&data[0], mWidth, mHeight, Ogre::PF_B8G8R8); //image.save (mCacheDir + "/GlobalMap.png"); diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index dcb76533b0..f1e3d7a3be 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -23,7 +23,7 @@ namespace Ogre class Entity; class Vector3; struct RenderTargetEvent; -}; +} namespace MWRender { diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index bacc232b2c..a97310ae5c 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -74,6 +74,6 @@ namespace MWScript ///< Return index of the variable of the given name and type in the given script. Will /// throw an exception, if there is no such script or variable or the type does not match. }; -}; +} #endif diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 4344397c70..7b028e1d0b 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -54,6 +54,6 @@ namespace MWSound #ifndef DEFAULT_DECODER #define DEFAULT_DECODER (::MWSound::FFmpeg_Decoder) #endif -}; +} #endif diff --git a/apps/openmw/mwsound/mpgsnd_decoder.hpp b/apps/openmw/mwsound/mpgsnd_decoder.hpp index 870773edc5..09082c2f47 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.hpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.hpp @@ -52,6 +52,6 @@ namespace MWSound #ifndef DEFAULT_DECODER #define DEFAULT_DECODER (::MWSound::MpgSnd_Decoder) #endif -}; +} #endif diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 90917c53c7..fecffa5752 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -66,6 +66,6 @@ namespace MWSound #ifndef DEFAULT_OUTPUT #define DEFAULT_OUTPUT (::MWSound::OpenAL_Output) #endif -}; +} #endif diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f91e291efc..a84aa3b9a7 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -26,7 +26,7 @@ namespace MWSound enum Environment { Env_Normal, - Env_Underwater, + Env_Underwater }; class SoundManager : public MWBase::SoundManager diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 495deb1ad8..c7f0de245e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -326,6 +326,14 @@ namespace MWWorld } Ptr World::getPtrViaHandle (const std::string& handle) + { + Ptr res = searchPtrViaHandle (handle); + if (res.isEmpty ()) + throw std::runtime_error ("unknown Ogre handle: " + handle); + return res; + } + + Ptr World::searchPtrViaHandle (const std::string& handle) { if (mPlayer->getPlayer().getRefData().getHandle()==handle) return mPlayer->getPlayer(); @@ -339,7 +347,7 @@ namespace MWWorld return ptr; } - throw std::runtime_error ("unknown Ogre handle: " + handle); + return MWWorld::Ptr(); } void World::enable (const Ptr& reference) @@ -864,12 +872,13 @@ namespace MWWorld mWeatherManager->update (duration); // inform the GUI about focused object - try - { - MWWorld::Ptr object = getPtrViaHandle(mFacedHandle); - MWBase::Environment::get().getWindowManager()->setFocusObject(object); + MWWorld::Ptr object = searchPtrViaHandle(mFacedHandle); - // retrieve object dimensions so we know where to place the floating label + MWBase::Environment::get().getWindowManager()->setFocusObject(object); + + // retrieve object dimensions so we know where to place the floating label + if (!object.isEmpty ()) + { Ogre::SceneNode* node = object.getRefData().getBaseNode(); Ogre::AxisAlignedBox bounds; int i; @@ -885,11 +894,6 @@ namespace MWWorld screenCoords[0], screenCoords[1], screenCoords[2], screenCoords[3]); } } - catch (std::runtime_error&) - { - MWWorld::Ptr null; - MWBase::Environment::get().getWindowManager()->setFocusObject(null); - } if (!mRendering->occlusionQuerySupported()) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 830a299187..527a608a6f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -163,6 +163,9 @@ namespace MWWorld virtual Ptr getPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle. + virtual Ptr searchPtrViaHandle (const std::string& handle); + ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found + virtual void enable (const Ptr& ptr); virtual void disable (const Ptr& ptr); diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 081207b0c8..8380b08388 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -375,7 +375,7 @@ void addBSA(const std::string& name, const std::string& group) { insertBSAFactory(); ResourceGroupManager::getSingleton(). - addResourceLocation(name, "BSA", group); + addResourceLocation(name, "BSA", group, true); } void addDir(const std::string& name, const bool& fs, const std::string& group) @@ -384,7 +384,7 @@ void addDir(const std::string& name, const bool& fs, const std::string& group) insertDirFactory(); ResourceGroupManager::getSingleton(). - addResourceLocation(name, "Dir", group); + addResourceLocation(name, "Dir", group, true); } } diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 3046a6a98f..3128fe0c61 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -18,8 +18,6 @@ namespace ESM { // These are probabilities char mHello, mU1, mFight, mFlee, mAlarm, mU2, mU3, mU4; - // The last u's might be the skills that this NPC can train you - // in? int mServices; // See the Services enum }; // 12 bytes @@ -60,7 +58,7 @@ namespace ESM AI_Travel = 0x545f4941, AI_Follow = 0x465f4941, AI_Escort = 0x455f4941, - AI_Activate = 0x415f4941, + AI_Activate = 0x415f4941 }; /// \note Used for storaging packages in a single container diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 24658a40b5..c1ae064903 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -138,11 +138,11 @@ void ESMWriter::writeHNString(const std::string& name, const std::string& data) void ESMWriter::writeHNString(const std::string& name, const std::string& data, int size) { - assert(data.size() <= size); + assert(static_cast (data.size()) <= size); startSubRecord(name); writeHString(data); - if (data.size() < size) + if (static_cast (data.size()) < size) { for (int i = data.size(); i < size; ++i) write("\0",1); diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 00349a3557..93c59f9cbe 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -26,7 +26,6 @@ struct MagicEffect NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target. Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally. CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. - SpellMaking = 0x0200, Enchanting = 0x0400, Negative = 0x0800 // A harmful effect. Will determine whether diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 24580a79c4..0b265a5666 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -331,7 +331,7 @@ namespace ESMS // Find land for the given coordinates. Return null if no mData. Land *search(int x, int y) const { - LandMap::const_iterator itr = lands.find(std::make_pair(x, y)); + LandMap::const_iterator itr = lands.find(std::make_pair (x, y)); if ( itr == lands.end() ) { return NULL; @@ -350,7 +350,7 @@ namespace ESMS land->load(esm); // Store the structure - lands[std::make_pair(land->mX, land->mY)] = land; + lands[std::make_pair (land->mX, land->mY)] = land; } }; diff --git a/credits.txt b/credits.txt index 2968e3487f..45611a2d6f 100644 --- a/credits.txt +++ b/credits.txt @@ -16,6 +16,7 @@ Alexander Olofsson (Ace) Artem Kotsynyak (greye) athile BrotherBrick +Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) Douglas Diniz (Dgdiniz) Eli2 diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 525e4ab636..5ea076342e 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -157,10 +157,15 @@ shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) #endif -#if UNDERWATER +#if (UNDERWATER) || (FOG) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) - shUniform(float, waterLevel) @shSharedParameter(waterLevel) shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) +#endif + +#if UNDERWATER + + shUniform(float, waterLevel) @shSharedParameter(waterLevel) + shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0) shSampler2D(causticMap) @@ -211,8 +216,12 @@ float3 caustics = float3(1,1,1); -#if UNDERWATER + +#if (UNDERWATER) || (FOG) float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)).xyz; +#endif + +#if UNDERWATER float3 waterEyePos = float3(1,1,1); // NOTE: this calculation would be wrong for non-uniform scaling float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 35ce403307..dee7332630 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -187,11 +187,13 @@ shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) #endif +#if (UNDERWATER) || (FOG) + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) + shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) +#endif #if UNDERWATER - shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) shUniform(float, waterLevel) @shSharedParameter(waterLevel) - shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0) shSampler2D(causticMap) @@ -222,9 +224,12 @@ float3 caustics = float3(1,1,1); +#if (UNDERWATER) || (FOG) + float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)).xyz; +#endif + #if UNDERWATER - float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)).xyz; float3 waterEyePos = float3(1,1,1); // NOTE: this calculation would be wrong for non-uniform scaling float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); @@ -373,7 +378,6 @@ shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); - #if MRT shOutputColour(1) = float4(depth / far,1,1,1); #endif diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index a33d59ef6c..da8fba62c0 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -78,6 +78,8 @@ set(MYGUI_FILES openmw_spellcreation_dialog.layout openmw_edit_effect.layout openmw_enchanting_dialog.layout + openmw_trainingwindow.layout + openmw_travel_window.layout smallbars.png VeraMono.ttf markers.png diff --git a/files/mygui/openmw_trainingwindow.layout b/files/mygui/openmw_trainingwindow.layout new file mode 100644 index 0000000000..c58bd0ab3a --- /dev/null +++ b/files/mygui/openmw_trainingwindow.layout @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout new file mode 100644 index 0000000000..07a6daf489 --- /dev/null +++ b/files/mygui/openmw_travel_window.layout @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/files/openmw.desktop b/files/openmw.desktop index 234f660c62..118cd3bbe6 100644 --- a/files/openmw.desktop +++ b/files/openmw.desktop @@ -1,5 +1,4 @@ [Desktop Entry] -Version=${OPENMW_VERSION} Type=Application Name=OpenMW Launcher GenericName=Role Playing Game diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp index dc28d9e5f0..6be615dfb0 100644 --- a/libs/openengine/bullet/CMotionState.cpp +++ b/libs/openengine/bullet/CMotionState.cpp @@ -16,7 +16,7 @@ namespace Physic pEng = eng; tr.setIdentity(); pName = name; - }; + } void CMotionState::getWorldTransform(btTransform &worldTrans) const { diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index b42ffb84c1..4f16a41438 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -626,4 +626,4 @@ namespace Physic shape->Shape->getAabb(trans, min, max); } -}}; +}} diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 6965ffc78b..9390d06647 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -40,7 +40,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); node->attachObject(mRectangle); mRectangle->setVisible(false); - mRectangle->setVisibilityFlags (0x01); + mRectangle->setVisibilityFlags (2048); } Fader::~Fader()