Support eight possible blood types (feature #4958)

pull/541/head
Capostrophic 6 years ago
parent 4d633fd3e6
commit 7814bd1b76

@ -78,6 +78,7 @@
Feature #4859: Make water reflections more configurable Feature #4859: Make water reflections more configurable
Feature #4887: Add openmw command option to set initial random seed Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable Feature #4890: Make Distant Terrain configurable
Feature #4958: Support eight blood types
Feature #4962: Add casting animations for magic items Feature #4962: Add casting animations for magic items
Feature #4968: Scalable UI widget skins Feature #4968: Scalable UI widget skins
Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4686: Upgrade media decoder to a more current FFmpeg API

@ -704,29 +704,25 @@ std::string creatureFlags(int flags)
{ {
std::string properties; std::string properties;
if (flags == 0) properties += "[None] "; if (flags == 0) properties += "[None] ";
if (flags & ESM::Creature::None) properties += "All "; if (flags & ESM::Creature::Base) properties += "Base ";
if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies "; if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Bipedal) properties += "Bipedal "; if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon "; 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 "; if (flags & ESM::Creature::Essential) properties += "Essential ";
int unused = (0xFFFFFFFF ^ int unused = (0xFF ^
(ESM::Creature::None| (ESM::Creature::Base|
ESM::Creature::Walks| ESM::Creature::Walks|
ESM::Creature::Swims| ESM::Creature::Swims|
ESM::Creature::Flies| ESM::Creature::Flies|
ESM::Creature::Bipedal| ESM::Creature::Bipedal|
ESM::Creature::Respawn| ESM::Creature::Respawn|
ESM::Creature::Weapon| ESM::Creature::Weapon|
ESM::Creature::Skeleton|
ESM::Creature::Metal|
ESM::Creature::Essential)); ESM::Creature::Essential));
if (flags & unused) properties += "Invalid "; if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags); properties += str(boost::format("(0x%02X)") % flags);
return properties; return properties;
} }
@ -828,33 +824,21 @@ std::string npcFlags(int flags)
{ {
std::string properties; std::string properties;
if (flags == 0) properties += "[None] "; if (flags == 0) properties += "[None] ";
// Mythicmods and the ESM component differ. Mythicmods says if (flags & ESM::NPC::Base) properties += "Base ";
// 0x8=None and 0x10=AutoCalc, while our code previously defined
// 0x8 as AutoCalc. The former seems to be correct. All Bethesda
// records have bit 0x8 set. Previously, suspiciously large portion
// of females had autocalc turned off.
if (flags & 0x00000008) properties += "Unknown ";
if (flags & ESM::NPC::Autocalc) properties += "Autocalc "; if (flags & ESM::NPC::Autocalc) properties += "Autocalc ";
if (flags & ESM::NPC::Female) properties += "Female "; if (flags & ESM::NPC::Female) properties += "Female ";
if (flags & ESM::NPC::Respawn) properties += "Respawn "; if (flags & ESM::NPC::Respawn) properties += "Respawn ";
if (flags & ESM::NPC::Essential) properties += "Essential "; 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, // Whether corpses persist is a bit that is unaccounted for,
// however the only unknown bit occurs on ALL records, and // however relatively few NPCs have this bit set.
// relatively few NPCs have this bit set. int unused = (0xFF ^
int unused = (0xFFFFFFFF ^ (ESM::NPC::Base|
(0x00000008|
ESM::NPC::Autocalc| ESM::NPC::Autocalc|
ESM::NPC::Female| ESM::NPC::Female|
ESM::NPC::Respawn| ESM::NPC::Respawn|
ESM::NPC::Essential| ESM::NPC::Essential));
ESM::NPC::Skeleton|
ESM::NPC::Metal));
if (flags & unused) properties += "Invalid "; if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags); properties += str(boost::format("(0x%02X)") % flags);
return properties; return properties;
} }

@ -618,7 +618,8 @@ void Record<ESM::Creature>::print()
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Flags: " << creatureFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << creatureFlags((int)mData.mFlags) << std::endl;
std::cout << " Blood Type: " << mData.mBloodType+1 << std::endl;
std::cout << " Original: " << mData.mOriginal << std::endl; std::cout << " Original: " << mData.mOriginal << std::endl;
std::cout << " Scale: " << mData.mScale << std::endl; std::cout << " Scale: " << mData.mScale << std::endl;
@ -1022,7 +1023,9 @@ void Record<ESM::NPC>::print()
std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Script: " << mData.mScript << std::endl;
if (!mData.mFaction.empty()) if (!mData.mFaction.empty())
std::cout << " Faction: " << mData.mFaction << std::endl; std::cout << " Faction: " << mData.mFaction << std::endl;
std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << npcFlags((int)mData.mFlags) << std::endl;
if (mData.mBloodType != 0)
std::cout << " Blood Type: " << mData.mBloodType+1 << std::endl;
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{ {

@ -230,9 +230,19 @@ MwIniImporter::MwIniImporter()
"Blood:Texture 0", "Blood:Texture 0",
"Blood:Texture 1", "Blood:Texture 1",
"Blood:Texture 2", "Blood:Texture 2",
"Blood:Texture 3",
"Blood:Texture 4",
"Blood:Texture 5",
"Blood:Texture 6",
"Blood:Texture 7",
"Blood:Texture Name 0", "Blood:Texture Name 0",
"Blood:Texture Name 1", "Blood:Texture Name 1",
"Blood:Texture Name 2", "Blood:Texture Name 2",
"Blood:Texture Name 3",
"Blood:Texture Name 4",
"Blood:Texture Name 5",
"Blood:Texture Name 6",
"Blood:Texture Name 7",
// movies // movies
"Movies:Company Logo", "Movies:Company Logo",
@ -624,17 +634,6 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish", "Moons:Masser Fade Out Finish",
"Moons:Script Color", "Moons:Script Color",
// blood
"Blood:Model 0",
"Blood:Model 1",
"Blood:Model 2",
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
// werewolf (Bloodmoon) // werewolf (Bloodmoon)
"General:Werewolf FOV", "General:Werewolf FOV",

@ -1,5 +1,6 @@
#include "columns.hpp" #include "columns.hpp"
#include <components/fallback/fallback.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "universalid.hpp" #include "universalid.hpp"
@ -573,11 +574,6 @@ namespace
"Book", "Scroll", 0 "Book", "Scroll", 0
}; };
static const char *sBloodType[] =
{
"Default (Red)", "Skeleton Blood (White)", "Metal Blood (Golden)", 0
};
static const char *sEmitterType[] = static const char *sEmitterType[] =
{ {
"<None>", "Flickering", "Flickering (Slow)", "Pulsing", "Pulsing (Slow)", 0 "<None>", "Flickering", "Flickering (Slow)", "Pulsing", "Pulsing (Slow)", 0
@ -613,7 +609,6 @@ namespace
case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings; case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings; case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
case CSMWorld::Columns::ColumnId_BookType: return sBookType; case CSMWorld::Columns::ColumnId_BookType: return sBookType;
case CSMWorld::Columns::ColumnId_BloodType: return sBloodType;
case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType; case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType;
default: return 0; default: return 0;
@ -633,6 +628,15 @@ std::vector<std::pair<int,std::string>>CSMWorld::Columns::getEnums (ColumnId col
if (const char **table = getEnumNames (column)) if (const char **table = getEnumNames (column))
for (int i=0; table[i]; ++i) for (int i=0; table[i]; ++i)
enums.emplace_back(i, table[i]); enums.emplace_back(i, table[i]);
else if (column==ColumnId_BloodType)
{
for (int i=0; i<8; i++)
{
const std::string& bloodName = Fallback::Map::getString("Blood_Texture_Name_" + std::to_string(i));
if (!bloodName.empty())
enums.emplace_back(i, bloodName);
}
}
else if (column==ColumnId_RecordType) else if (column==ColumnId_RecordType)
{ {
enums.emplace_back(UniversalId::Type_None, ""); // none enums.emplace_back(UniversalId::Type_None, ""); // none

@ -491,17 +491,7 @@ QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, con
return QVariant::fromValue(ColumnBase::TableEdit_Full); return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column == mColumns.mBloodType) if (column == mColumns.mBloodType)
{ return record.get().mBloodType;
int mask = ESM::Creature::Skeleton | ESM::Creature::Metal;
if ((record.get().mFlags & mask) == ESM::Creature::Skeleton)
return 1;
if ((record.get().mFlags & mask) == ESM::Creature::Metal)
return 2;
return 0;
}
std::map<const RefIdColumn *, unsigned int>::const_iterator iter = std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column); mColumns.mFlags.find (column);
@ -527,16 +517,7 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa
else if (column==mColumns.mOriginal) else if (column==mColumns.mOriginal)
creature.mOriginal = value.toString().toUtf8().constData(); creature.mOriginal = value.toString().toUtf8().constData();
else if (column == mColumns.mBloodType) else if (column == mColumns.mBloodType)
{ creature.mBloodType = value.toInt();
int mask = ~(ESM::Creature::Skeleton | ESM::Creature::Metal);
if (value.toInt() == 1)
creature.mFlags = (creature.mFlags & mask) | ESM::Creature::Skeleton;
else if (value.toInt() == 2)
creature.mFlags = (creature.mFlags & mask) | ESM::Creature::Metal;
else
creature.mFlags = creature.mFlags & mask;
}
else else
{ {
std::map<const RefIdColumn *, unsigned int>::const_iterator iter = std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
@ -797,17 +778,7 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re
return QVariant::fromValue(ColumnBase::TableEdit_Full); return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column == mColumns.mBloodType) if (column == mColumns.mBloodType)
{ return record.get().mBloodType;
int mask = ESM::NPC::Skeleton | ESM::NPC::Metal;
if ((record.get().mFlags & mask) == ESM::NPC::Skeleton)
return 1;
if ((record.get().mFlags & mask) == ESM::NPC::Metal)
return 2;
return 0;
}
if (column == mColumns.mGender) if (column == mColumns.mGender)
{ {
@ -846,16 +817,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
else if (column==mColumns.mHead) else if (column==mColumns.mHead)
npc.mHead = value.toString().toUtf8().constData(); npc.mHead = value.toString().toUtf8().constData();
else if (column == mColumns.mBloodType) else if (column == mColumns.mBloodType)
{ npc.mBloodType = value.toInt();
int mask = ~(ESM::NPC::Skeleton | ESM::NPC::Metal);
if (value.toInt() == 1)
npc.mFlags = (npc.mFlags & mask) | ESM::NPC::Skeleton;
else if (value.toInt() == 2)
npc.mFlags = (npc.mFlags & mask) | ESM::NPC::Metal;
else
npc.mFlags = npc.mFlags & mask;
}
else if (column == mColumns.mGender) else if (column == mColumns.mGender)
{ {
// Implemented this way to allow additional gender types in the future. // Implemented this way to allow additional gender types in the future.

@ -766,13 +766,7 @@ namespace MWClass
int Creature::getBloodTexture(const MWWorld::ConstPtr &ptr) const int Creature::getBloodTexture(const MWWorld::ConstPtr &ptr) const
{ {
int flags = ptr.get<ESM::Creature>()->mBase->mFlags; return ptr.get<ESM::Creature>()->mBase->mBloodType;
if (flags & ESM::Creature::Skeleton)
return 1;
if (flags & ESM::Creature::Metal)
return 2;
return 0;
} }
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

@ -1286,13 +1286,7 @@ namespace MWClass
int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); return ptr.get<ESM::NPC>()->mBase->mBloodType;
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
} }
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

@ -3585,7 +3585,11 @@ namespace MWWorld
return; return;
std::string texture = Fallback::Map::getString("Blood_Texture_" + std::to_string(ptr.getClass().getBloodTexture(ptr))); std::string texture = Fallback::Map::getString("Blood_Texture_" + std::to_string(ptr.getClass().getBloodTexture(ptr)));
if (texture.empty())
texture = Fallback::Map::getString("Blood_Texture_0");
std::string model = "meshes\\" + Fallback::Map::getString("Blood_Model_" + std::to_string(Misc::Rng::rollDice(3))); // [0, 2] std::string model = "meshes\\" + Fallback::Map::getString("Blood_Model_" + std::to_string(Misc::Rng::rollDice(3))); // [0, 2]
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false); mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
} }

@ -55,7 +55,10 @@ namespace ESM {
hasNpdt = true; hasNpdt = true;
break; break;
case ESM::FourCC<'F','L','A','G'>::value: case ESM::FourCC<'F','L','A','G'>::value:
esm.getHT(mFlags); int flags;
esm.getHT(flags);
mFlags = flags & 0xFF;
mBloodType = ((flags >> 8) & 0xFF) >> 2;
hasFlags = true; hasFlags = true;
break; break;
case ESM::FourCC<'X','S','C','L'>::value: case ESM::FourCC<'X','S','C','L'>::value:
@ -121,7 +124,7 @@ namespace ESM {
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
esm.writeHNT("NPDT", mData, 96); esm.writeHNT("NPDT", mData, 96);
esm.writeHNT("FLAG", mFlags); esm.writeHNT("FLAG", ((mBloodType << 10) + mFlags));
if (mScale != 1.0) { if (mScale != 1.0) {
esm.writeHNT("XSCL", mScale); esm.writeHNT("XSCL", mScale);
} }
@ -144,6 +147,7 @@ namespace ESM {
mData.mCombat = mData.mMagic = mData.mStealth = 0; mData.mCombat = mData.mMagic = mData.mStealth = 0;
for (int i=0; i<6; ++i) mData.mAttack[i] = 0; for (int i=0; i<6; ++i) mData.mAttack[i] = 0;
mData.mGold = 0; mData.mGold = 0;
mBloodType = 0;
mFlags = 0; mFlags = 0;
mScale = 1.f; mScale = 1.f;
mModel.clear(); mModel.clear();

@ -28,20 +28,14 @@ struct Creature
// Default is 0x48? // Default is 0x48?
enum Flags enum Flags
{ {
// Movement types Bipedal = 0x01,
Bipedal = 0x001, Respawn = 0x02,
Swims = 0x010, Weapon = 0x04, // Has weapon and shield
Flies = 0x020, // Don't know what happens if several Base = 0x08, // This flag is set for every actor in Bethesda ESMs
Walks = 0x040, // of these are set Swims = 0x10,
Flies = 0x20, // Don't know what happens if several
Respawn = 0x002, Walks = 0x40, // of these are set
Weapon = 0x004, // Has weapon and shield Essential = 0x80
None = 0x008, // ?? This flag appears set for every creature in Morrowind.esm
Essential = 0x080,
// Blood types
Skeleton = 0x400,
Metal = 0x800
}; };
enum Type enum Type
@ -79,7 +73,8 @@ struct Creature
NPDTstruct mData; NPDTstruct mData;
int mFlags; int mBloodType;
unsigned char mFlags;
bool mPersistent; bool mPersistent;

@ -86,7 +86,10 @@ namespace ESM
break; break;
case ESM::FourCC<'F','L','A','G'>::value: case ESM::FourCC<'F','L','A','G'>::value:
hasFlags = true; hasFlags = true;
esm.getHT(mFlags); int flags;
esm.getHT(flags);
mFlags = flags & 0xFF;
mBloodType = ((flags >> 8) & 0xFF) >> 2;
break; break;
case ESM::FourCC<'N','P','C','S'>::value: case ESM::FourCC<'N','P','C','S'>::value:
mSpells.add(esm); mSpells.add(esm);
@ -160,7 +163,7 @@ namespace ESM
esm.writeHNT("NPDT", npdt12, 12); esm.writeHNT("NPDT", npdt12, 12);
} }
esm.writeHNT("FLAG", mFlags); esm.writeHNT("FLAG", ((mBloodType << 10) + mFlags));
mInventory.save(esm); mInventory.save(esm);
mSpells.save(esm); mSpells.save(esm);
@ -186,6 +189,7 @@ namespace ESM
{ {
mNpdtType = NPC_DEFAULT; mNpdtType = NPC_DEFAULT;
blankNpdt(); blankNpdt();
mBloodType = 0;
mFlags = 0; mFlags = 0;
mInventory.mList.clear(); mInventory.mList.clear();
mSpells.mList.clear(); mSpells.mList.clear();

@ -56,12 +56,11 @@ struct NPC
enum Flags enum Flags
{ {
Female = 0x0001, Female = 0x01,
Essential = 0x0002, Essential = 0x02,
Respawn = 0x0004, Respawn = 0x04,
Autocalc = 0x0010, Base = 0x08,
Skeleton = 0x0400, // Skeleton blood effect (white) Autocalc = 0x10
Metal = 0x0800 // Metal blood effect (golden?)
}; };
enum NpcType enum NpcType
@ -114,7 +113,8 @@ struct NPC
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
int mFlags; int mBloodType;
unsigned char mFlags;
bool mPersistent; bool mPersistent;

Loading…
Cancel
Save