57 const DB2Table* imaTbl =
WOWDB.getTable(
"ItemModifiedAppearance");
63 for (
const auto& row : *imaTbl)
65 if (
static_cast<int>(row.getUInt(
"ItemID")) !=
id)
68 const auto curid =
static_cast<int>(row.getUInt(
"ItemAppearanceID"));
78 if (it.second == curid)
106 type_ = itemRcd.type;
140 type_ = itemRcd.type;
161 type_ = itemRcd.type;
175 delete itemModel.second;
224 LOG_ERROR <<
"Impossible to query information for item" <<
name() <<
"(id " <<
id_ <<
"- display id" <<
229 const int geosetGroup[6] = {
230 idiRow.
getInt(
"GeoSetGroup1"), idiRow.
getInt(
"GeoSetGroup2"),
231 idiRow.
getInt(
"GeoSetGroup3"), idiRow.
getInt(
"GeoSetGroup4"),
232 idiRow.
getInt(
"GeoSetGroup5"), idiRow.
getInt(
"GeoSetGroup6")
235 const int attachmentGeosetGroup[6] =
237 idiRow.
getInt(
"AttachmentGeoSetGroup1"), idiRow.
getInt(
"AttachmentGeoSetGroup2"),
238 idiRow.
getInt(
"AttachmentGeoSetGroup3"), idiRow.
getInt(
"AttachmentGeoSetGroup4"),
239 idiRow.
getInt(
"AttachmentGeoSetGroup5"), idiRow.
getInt(
"AttachmentGeoSetGroup6")
251 const DB2Table* matResTbl =
WOWDB.getTable(
"ItemDisplayInfoMaterialRes");
252 const DB2Table* texFileTbl =
WOWDB.getTable(
"TextureFileData");
253 const DB2Table* compTexTbl =
WOWDB.getTable(
"ComponentTextureFileData");
255 if (matResTbl && texFileTbl && compTexTbl)
258 std::vector<uint32_t> materialResIds;
259 for (
const auto& mrRow : *matResTbl)
261 if (
static_cast<int>(mrRow.getUInt(
"ItemDisplayInfoID")) ==
displayId_)
262 materialResIds.push_back(mrRow.getUInt(
"MaterialResourcesID"));
265 if (!materialResIds.empty())
270 struct TexCandidate {
int fileDataID;
int genderIndex;
int classID; };
271 std::vector<TexCandidate> candidates;
273 for (
const auto& tfdRow : *texFileTbl)
275 const uint32_t tfdMatResID = tfdRow.getUInt(
"MaterialResourcesID");
276 bool matchesMat =
false;
277 for (uint32_t mrid : materialResIds)
279 if (tfdMatResID == mrid) { matchesMat =
true;
break; }
284 const int fileDataID =
static_cast<int>(tfdRow.getUInt(
"FileDataID"));
289 const int genderIdx = ctRow.
getInt(
"GenderIndex");
290 const int classID = ctRow.
getInt(
"ClassID");
293 if (genderIdx !=
static_cast<int>(
GENDER_ANY) && genderIdx != charInfos.sexID)
304 if (classID !=
static_cast<int>(
CLASS_ANY))
308 candidates.push_back({fileDataID, genderIdx, classID});
312 std::sort(candidates.begin(), candidates.end(), [](
const TexCandidate& a,
const TexCandidate& b) {
313 if (a.genderIndex != b.genderIndex) return a.genderIndex < b.genderIndex;
314 return a.classID > b.classID;
317 for (
const auto& c : candidates)
363 const DB2Table* cmfTbl =
WOWDB.getTable(
"ComponentModelFileData");
375 const auto modelid =
static_cast<int>(cmfRow.
recordID());
376 const auto position = cmfRow.
getInt(
"PositionIndex");
380 if ((modelid ==
models[0] && position != 0) || (modelid !=
models[0] && position == 0))
388 LOG_ERROR <<
"Impossible to query information for item" <<
name() <<
"(id " <<
id_ <<
"- display id" <<
393 LOG_INFO <<
"leftIndex" << leftIndex <<
"rightIndex" << rightIndex;
409 if (geosetGroup[1] == 0)
411 else if (geosetGroup[1] > 0)
831 static const std::pair<CharRegions, CharRegions> tabardLayers[] = {
839 for (
const auto& layer : tabardLayers)
865 return (
id_ == 5976 ||
872 pugi::xml_node node = parentNode.append_child(
"item");
874 node.append_child(
"slot").append_attribute(
"value") =
static_cast<int>(
slot_);
875 node.append_child(
"id").append_attribute(
"value") =
id_;
876 node.append_child(
"displayId").append_attribute(
"value") =
displayId_;
877 node.append_child(
"level").append_attribute(
"value") =
level_;
885 pugi::xml_document doc;
886 pugi::xml_parse_result result = doc.load_file(f.c_str());
889 LOG_ERROR <<
"Fail to open" << f.c_str();
894 pugi::xml_node root = doc.document_element();
895 for (pugi::xml_node itemNode = root.child(
"item"); itemNode; itemNode = itemNode.next_sibling(
"item"))
897 pugi::xml_node slotNode = itemNode.child(
"slot");
901 const auto slot = slotNode.attribute(
"value").as_uint();
902 if (
slot !=
static_cast<unsigned int>(
slot_))
905 pugi::xml_node idNode = itemNode.child(
"id");
908 const auto id = idNode.attribute(
"value").as_int();
913 pugi::xml_node displayIdNode = itemNode.child(
"displayId");
916 const auto id = displayIdNode.attribute(
"value").as_int();
921 pugi::xml_node levelNode = itemNode.child(
"level");
924 const auto level = levelNode.attribute(
"value").as_int();
930 pugi::xml_node tabardNode = itemNode.child(
"TabardDetails");
951 for (
uint i = 0; i < m->geosets.size(); i++)
952 m->showGeoset(i,
true);
959 LOG_ERROR <<
"Error during item update" <<
id_ <<
"(display id" <<
displayId_ <<
"). Texture" << textureId
960 <<
"can't be loaded";
985 LOG_ERROR <<
"Error during item update" <<
id_ <<
"(display id" <<
displayId_ <<
"). Texture" << textureId
986 <<
"can't be loaded";
1004 std::string fullname = file->
fullname();
1005 std::transform(fullname.begin(), fullname.end(), fullname.begin(),
1006 [](
unsigned char c) { return std::tolower(c); });
1008 if (fullname.find(
"armlowertexture") != std::string::npos)
1012 else if (fullname.find(
"armuppertexture") != std::string::npos)
1016 else if (fullname.find(
"foottexture") != std::string::npos)
1020 else if (fullname.find(
"handtexture") != std::string::npos)
1024 else if (fullname.find(
"leglowertexture") != std::string::npos)
1028 else if (fullname.find(
"leguppertexture") != std::string::npos)
1032 else if (fullname.find(
"torsolowertexture") != std::string::npos)
1036 else if (fullname.find(
"torsouppertexture") != std::string::npos)
1040 else if (fullname.find(
"cape") != std::string::npos)
1046 LOG_ERROR <<
"Unable to determine region for texture" << file->
fullname().c_str() <<
" - item" <<
id_ <<
"displayid" <<
1057 int raceID,
int sexID,
int altGender,
bool isDH,
bool positionDesc)
1059 struct Candidate {
int id;
int genderIndex;
int classID;
int positionIndex; };
1060 std::vector<Candidate> matches;
1062 for (
const auto& row : *cmfTbl)
1064 const int rowID =
static_cast<int>(row.recordID());
1065 if (candidateIds.find(rowID) == candidateIds.end())
1068 const int rowRace = row.getInt(
"RaceID");
1069 if (rowRace != raceID)
1072 const int rowGender = row.getInt(
"GenderIndex");
1073 if (rowGender != sexID && rowGender != altGender)
1076 const int rowClass = row.getInt(
"ClassID");
1084 if (rowClass !=
static_cast<int>(
CLASS_ANY))
1088 matches.push_back({rowID, rowGender, rowClass, row.getInt(
"PositionIndex")});
1091 if (matches.empty())
1095 std::sort(matches.begin(), matches.end(), [positionDesc](
const Candidate& a,
const Candidate& b) {
1096 if (a.genderIndex != b.genderIndex) return a.genderIndex < b.genderIndex;
1097 if (a.classID != b.classID) return a.classID > b.classID;
1098 return positionDesc ? (a.positionIndex > b.positionIndex) : (a.positionIndex < b.positionIndex);
1101 return matches[0].id;
1106 int raceID,
int sexID,
int altGender,
bool isDH)
1108 struct Candidate {
int id;
int genderIndex;
int classID; };
1109 std::vector<Candidate> matches;
1111 for (
const auto& row : *ctfTbl)
1113 const int rowID =
static_cast<int>(row.recordID());
1114 if (candidateIds.find(rowID) == candidateIds.end())
1117 const int rowRace = row.getInt(
"RaceID");
1118 if (rowRace != raceID)
1121 const int rowGender = row.getInt(
"GenderIndex");
1122 if (rowGender != sexID && rowGender != altGender)
1125 const int rowClass = row.getInt(
"ClassID");
1133 if (rowClass !=
static_cast<int>(
CLASS_ANY))
1137 matches.push_back({rowID, rowGender, rowClass});
1140 if (matches.empty())
1144 std::sort(matches.begin(), matches.end(), [](
const Candidate& a,
const Candidate& b) {
1145 if (a.genderIndex != b.genderIndex) return a.genderIndex < b.genderIndex;
1146 return a.classID > b.classID;
1149 return matches[0].id;
1166 const uint32_t modelResID = (index == 0) ? idiRow.
getUInt(
"ModelResourcesID1") : idiRow.
getUInt(
"ModelResourcesID2");
1167 if (modelResID == 0)
1175 std::set<int> candidateIds;
1176 for (
const auto& row : *mfdTbl)
1178 if (row.getUInt(
"ModelResourcesID") == modelResID)
1179 candidateIds.insert(
static_cast<int>(row.getUInt(
"FileDataID")));
1182 if (candidateIds.empty())
1186 if (candidateIds.size() == 1)
1187 return *candidateIds.
begin();
1190 const DB2Table* cmfTbl =
WOWDB.getTable(
"ComponentModelFileData");
1201 const bool positionDesc = (index != 0);
1206 static_cast<int>(
GENDER_ANY), isDH, positionDesc);
1211 if (charInfos.modelFallbackRaceID > 0)
1213 result =
findBestComponentModel(cmfTbl, candidateIds, charInfos.modelFallbackRaceID, charInfos.modelFallbackSexID,
1214 static_cast<int>(
GENDER_NONE), isDH, positionDesc);
1223 static_cast<int>(
GENDER_NONE), isDH, positionDesc);
1244 const uint32_t matResID = (index == 0) ? idiRow.
getUInt(
"ModelMaterialResourcesID1") : idiRow.
getUInt(
"ModelMaterialResourcesID2");
1253 std::set<int> candidateIds;
1254 for (
const auto& row : *tfdTbl)
1256 if (row.getUInt(
"MaterialResourcesID") == matResID)
1257 candidateIds.insert(
static_cast<int>(row.getUInt(
"FileDataID")));
1260 if (candidateIds.empty())
1264 if (candidateIds.size() == 1)
1265 return *candidateIds.
begin();
1268 const DB2Table* ctfTbl =
WOWDB.getTable(
"ComponentTextureFileData");
1283 if (charInfos.textureFallbackRaceID > 0)
1285 result =
findBestComponentTexture(ctfTbl, candidateIds, charInfos.textureFallbackRaceID, charInfos.textureFallbackSexID,
TextureManager TEXTUREMANAGER
static int findBestComponentModel(const DB2Table *cmfTbl, const std::set< int > &candidateIds, int raceID, int sexID, int altGender, bool isDH, bool positionDesc)
static int findBestComponentTexture(const DB2Table *ctfTbl, const std::set< int > &candidateIds, int raceID, int sexID, int altGender, bool isDH)
Attachment * addChild(std::string fn, int id, int slot)
bool isDemonHunter() const
std::map< uint, uint > geosets
void addLayer(GameFile *file, int region, int layer, int blendMode=1)
Base class for all scene-graph nodes in the component hierarchy.
const Component * parent() const
Get the parent component (const).
void setName(const std::string &name)
Lightweight handle to a single row in a DB2Table.
int32_t getInt(const std::string &field, unsigned int arrayIndex=0) const
uint32_t recordID() const
uint32_t getUInt(const std::string &field, unsigned int arrayIndex=0) const
Provides typed, field-name-based access to records in a WDC DB2 file.
DB2Row getRow(uint32_t id) const
Abstract base class representing a file within the game data archive.
const std::string & fullname() const
const ItemRecord & getById(int id) const
void delbyname(const std::string &name)
GameFile * GetBorderTex(int slot)
GameFile * GetIconTex(int slot)
void load(const pugi::xml_node &node)
void save(pugi::xml_node &parentNode)
GameFile * GetBackgroundTex(int slot)
virtual GLuint add(GameFile *)
std::map< CharGeosets, int > itemGeosets_
void setDisplayId(int id)
std::map< int, int > modifierIdDisplayMap_
int getCustomTextureId(size_t index) const
CharRegions getRegionForTexture(GameFile *file) const
bool isCustomizableTabard() const
std::map< int, int > levelDisplayMap_
int getCustomModelId(size_t index) const
void save(pugi::xml_node &parentNode) const
void onParentSet(Component *) override
Called after the parent has been set; override for custom logic.
std::map< POSITION_SLOTS, WoWModel * > models() const
void mergeModel(CharSlots slot, int modelId, int textureId)
std::map< CharRegions, GameFile * > itemTextures_
void setModifierId(int id)
static std::map< CharSlots, int > SLOT_LAYERS_
std::map< POSITION_SLOTS, WoWModel * > itemModels_
void updateItemModel(POSITION_SLOTS pos, int modelId, int textureId)
Core WoW .m2 model: geometry, animation, textures, and character data.
WoWModel * mergeModel(std::string name, int type=1, bool noRefresh=false)
void updateTextureList(GameFile *tex, int special)
CharModelDetails charModelDetails
void unmergeModel(std::string name)
std::vector< ModelGeosetHD * > geosets
void setGeosetGroupDisplay(CharGeosets group, int val)
POSITION_SLOTS
Attachment point positions on a character model.
CharRegions
Texture compositing region IDs for character texture layout.
CharSlots
Character equipment slot indices.