Commit 7814bd1b authored by Capostrophic's avatar Capostrophic

Support eight possible blood types (feature #4958)

parent 4d633fd3
......@@ -78,6 +78,7 @@
Feature #4859: Make water reflections more configurable
Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable
Feature #4958: Support eight blood types
Feature #4962: Add casting animations for magic items
Feature #4968: Scalable UI widget skins
Task #4686: Upgrade media decoder to a more current FFmpeg API
......
......@@ -704,29 +704,25 @@ std::string creatureFlags(int flags)
{
std::string properties;
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::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
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|
int unused = (0xFF ^
(ESM::Creature::Base|
ESM::Creature::Walks|
ESM::Creature::Swims|
ESM::Creature::Flies|
ESM::Creature::Bipedal|
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);
properties += str(boost::format("(0x%02X)") % flags);
return properties;
}
......@@ -828,33 +824,21 @@ 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 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::Base) properties += "Base ";
if (flags & ESM::NPC::Autocalc) 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 ^
(0x00000008|
// however relatively few NPCs have this bit set.
int unused = (0xFF ^
(ESM::NPC::Base|
ESM::NPC::Autocalc|
ESM::NPC::Female|
ESM::NPC::Respawn|
ESM::NPC::Essential|
ESM::NPC::Skeleton|
ESM::NPC::Metal));
ESM::NPC::Essential));
if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags);
properties += str(boost::format("(0x%02X)") % flags);
return properties;
}
......
......@@ -618,7 +618,8 @@ void Record<ESM::Creature>::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 << " 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 << " Scale: " << mData.mScale << std::endl;
......@@ -1022,7 +1023,9 @@ void Record<ESM::NPC>::print()
std::cout << " Script: " << mData.mScript << std::endl;
if (!mData.mFaction.empty())
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)
{
......
......@@ -230,9 +230,19 @@ MwIniImporter::MwIniImporter()
"Blood:Texture 0",
"Blood:Texture 1",
"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 1",
"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:Company Logo",
......@@ -624,17 +634,6 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish",
"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)
"General:Werewolf FOV",
......
#include "columns.hpp"
#include <components/fallback/fallback.hpp>
#include <components/misc/stringops.hpp>
#include "universalid.hpp"
......@@ -573,11 +574,6 @@ namespace
"Book", "Scroll", 0
};
static const char *sBloodType[] =
{
"Default (Red)", "Skeleton Blood (White)", "Metal Blood (Golden)", 0
};
static const char *sEmitterType[] =
{
"<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_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
case CSMWorld::Columns::ColumnId_BookType: return sBookType;
case CSMWorld::Columns::ColumnId_BloodType: return sBloodType;
case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType;
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))
for (int i=0; table[i]; ++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)
{
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);
if (column == mColumns.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;
}
return record.get().mBloodType;
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
......@@ -527,16 +517,7 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa
else if (column==mColumns.mOriginal)
creature.mOriginal = value.toString().toUtf8().constData();
else if (column == mColumns.mBloodType)
{
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;
}
creature.mBloodType = value.toInt();
else
{
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);
if (column == mColumns.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;
}
return record.get().mBloodType;
if (column == mColumns.mGender)
{
......@@ -846,16 +817,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
else if (column==mColumns.mHead)
npc.mHead = value.toString().toUtf8().constData();
else if (column == mColumns.mBloodType)
{
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;
}
npc.mBloodType = value.toInt();
else if (column == mColumns.mGender)
{
// 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 flags = ptr.get<ESM::Creature>()->mBase->mFlags;
if (flags & ESM::Creature::Skeleton)
return 1;
if (flags & ESM::Creature::Metal)
return 2;
return 0;
return ptr.get<ESM::Creature>()->mBase->mBloodType;
}
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
{
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
return ptr.get<ESM::NPC>()->mBase->mBloodType;
}
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
......
......@@ -3585,7 +3585,11 @@ namespace MWWorld
return;
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]
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
}
......
......@@ -55,7 +55,10 @@ namespace ESM {
hasNpdt = true;
break;
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;
break;
case ESM::FourCC<'X','S','C','L'>::value:
......@@ -121,7 +124,7 @@ namespace ESM {
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNT("NPDT", mData, 96);
esm.writeHNT("FLAG", mFlags);
esm.writeHNT("FLAG", ((mBloodType << 10) + mFlags));
if (mScale != 1.0) {
esm.writeHNT("XSCL", mScale);
}
......@@ -144,6 +147,7 @@ namespace ESM {
mData.mCombat = mData.mMagic = mData.mStealth = 0;
for (int i=0; i<6; ++i) mData.mAttack[i] = 0;
mData.mGold = 0;
mBloodType = 0;
mFlags = 0;
mScale = 1.f;
mModel.clear();
......
......@@ -28,20 +28,14 @@ struct Creature
// Default is 0x48?
enum Flags
{
// Movement types
Bipedal = 0x001,
Swims = 0x010,
Flies = 0x020, // Don't know what happens if several
Walks = 0x040, // of these are set
Respawn = 0x002,
Weapon = 0x004, // Has weapon and shield
None = 0x008, // ?? This flag appears set for every creature in Morrowind.esm
Essential = 0x080,
// Blood types
Skeleton = 0x400,
Metal = 0x800
Bipedal = 0x01,
Respawn = 0x02,
Weapon = 0x04, // Has weapon and shield
Base = 0x08, // This flag is set for every actor in Bethesda ESMs
Swims = 0x10,
Flies = 0x20, // Don't know what happens if several
Walks = 0x40, // of these are set
Essential = 0x80
};
enum Type
......@@ -79,7 +73,8 @@ struct Creature
NPDTstruct mData;
int mFlags;
int mBloodType;
unsigned char mFlags;
bool mPersistent;
......
......@@ -86,7 +86,10 @@ namespace ESM
break;
case ESM::FourCC<'F','L','A','G'>::value:
hasFlags = true;
esm.getHT(mFlags);
int flags;
esm.getHT(flags);
mFlags = flags & 0xFF;
mBloodType = ((flags >> 8) & 0xFF) >> 2;
break;
case ESM::FourCC<'N','P','C','S'>::value:
mSpells.add(esm);
......@@ -160,7 +163,7 @@ namespace ESM
esm.writeHNT("NPDT", npdt12, 12);
}
esm.writeHNT("FLAG", mFlags);
esm.writeHNT("FLAG", ((mBloodType << 10) + mFlags));
mInventory.save(esm);
mSpells.save(esm);
......@@ -186,6 +189,7 @@ namespace ESM
{
mNpdtType = NPC_DEFAULT;
blankNpdt();
mBloodType = 0;
mFlags = 0;
mInventory.mList.clear();
mSpells.mList.clear();
......
......@@ -56,12 +56,11 @@ struct NPC
enum Flags
{
Female = 0x0001,
Essential = 0x0002,
Respawn = 0x0004,
Autocalc = 0x0010,
Skeleton = 0x0400, // Skeleton blood effect (white)
Metal = 0x0800 // Metal blood effect (golden?)
Female = 0x01,
Essential = 0x02,
Respawn = 0x04,
Base = 0x08,
Autocalc = 0x10
};
enum NpcType
......@@ -114,7 +113,8 @@ struct NPC
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
int mFlags;
int mBloodType;
unsigned char mFlags;
bool mPersistent;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment