mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 02:09:41 +00:00
Support eight possible blood types (feature #4958)
This commit is contained in:
parent
4d633fd3e6
commit
7814bd1b76
13 changed files with 75 additions and 127 deletions
|
@ -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",
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue