[10289] Adjust creature models system

This change will:
* make it easier to use cached data directly without any modifications
* correct issues regarding invisible models
* simplify certain aspects of model selection itself and make it somehow easier to control and maintain special cases.

Two new fields added to creature_model_info, to store modelid_alternative and modelid_other_team
* _alternative holds an alt. model, for cases where gender are the same, or is not male/female.
* _other_team is generally used for totem models, but may have future use.

This commit will possibly break a few things (visually) and will require DB projects to update their creature_template models data.
It is advised to use cache data as-is, and in addition fill creature_model_info for certain models, totems in particular, for expected appearance.

Signed-off-by: NoFantasy <nofantasy@nf.no>
This commit is contained in:
NoFantasy 2010-07-30 16:39:11 +02:00
parent 3099a99f9a
commit 2ae0badf48
15 changed files with 207 additions and 135 deletions

View file

@ -592,72 +592,72 @@ void ObjectMgr::LoadCreatureTemplates()
// used later for scale
CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
if (cInfo->DisplayID_A[0])
if (cInfo->ModelId[0])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[0]);
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->ModelId[0]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_A (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[0]);
const_cast<CreatureInfo*>(cInfo)->DisplayID_A[0] = 0;
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_1 (%u), can crash client", cInfo->Entry, cInfo->ModelId[0]);
const_cast<CreatureInfo*>(cInfo)->ModelId[0] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A[0]);
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->ModelId[0]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) are using modelid_A (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->DisplayID_A[0]);
sLog.outErrorDb("Creature (Entry: %u) are using modelid_1 (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->ModelId[0]);
}
if (cInfo->DisplayID_A[1])
if (cInfo->ModelId[1])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[1]);
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->ModelId[1]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_A2 (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[1]);
const_cast<CreatureInfo*>(cInfo)->DisplayID_A[1] = 0;
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_2 (%u), can crash client", cInfo->Entry, cInfo->ModelId[1]);
const_cast<CreatureInfo*>(cInfo)->ModelId[1] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A[1]);
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->ModelId[1]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) are using modelid_A2 (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->DisplayID_A[1]);
sLog.outErrorDb("Creature (Entry: %u) are using modelid_2 (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->ModelId[1]);
}
if (cInfo->DisplayID_H[0])
if (cInfo->ModelId[2])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[0]);
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->ModelId[2]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_H (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[0]);
const_cast<CreatureInfo*>(cInfo)->DisplayID_H[0] = 0;
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_3 (%u), can crash client", cInfo->Entry, cInfo->ModelId[2]);
const_cast<CreatureInfo*>(cInfo)->ModelId[2] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H[0]);
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->ModelId[2]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) are using modelid_H (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->DisplayID_H[0]);
sLog.outErrorDb("Creature (Entry: %u) are using modelid_3 (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->ModelId[2]);
}
if (cInfo->DisplayID_H[1])
if (cInfo->ModelId[3])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[1]);
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->ModelId[3]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_H2 (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[1]);
const_cast<CreatureInfo*>(cInfo)->DisplayID_H[1] = 0;
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_4 (%u), can crash client", cInfo->Entry, cInfo->ModelId[3]);
const_cast<CreatureInfo*>(cInfo)->ModelId[3] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H[1]);
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->ModelId[3]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) are using modelid_H2 (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->DisplayID_H[1]);
sLog.outErrorDb("Creature (Entry: %u) are using modelid_4 (%u), but creature_model_info are missing for this model.", cInfo->Entry, cInfo->ModelId[3]);
}
if (!displayScaleEntry)
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid in modelid_A/modelid_A2/modelid_H/modelid_A2", cInfo->Entry);
sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid in modelid_1/modelid_2/modelid_3/modelid_4", cInfo->Entry);
for(int k = 0; k < MAX_KILL_CREDIT; ++k)
{
@ -949,38 +949,73 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data /*= NULL*/)
{
// Load creature model (display id)
if (data && data->displayid)
return data->displayid;
// Use creature model explicit, override template (creature.modelid)
if (data && data->modelid_override)
return data->modelid_override;
// use defaults from the template
uint32 display_id;
uint32 display_id = 0;
// DisplayID_A is used if no team is given
if (team == HORDE)
// models may be categorized as (in this order):
// if mod4 && mod3 && mod2 && mod1 use any, by 25%-chance (other gender is selected and replaced after this function)
// if mod3 && mod2 && mod1 use mod3 unless mod2 has modelid_alt_model (then all by 33%-chance)
// if mod2 use mod2 unless mod2 has modelid_alt_model (then both by 50%-chance)
// if mod1 use mod1
// model selected here may be replaced with other_gender using own function
if (cinfo->ModelId[3] && cinfo->ModelId[2] && cinfo->ModelId[1] && cinfo->ModelId[0])
{
if(cinfo->DisplayID_H[0])
display_id = cinfo->DisplayID_H[1] ? cinfo->DisplayID_H[urand(0,1)] : cinfo->DisplayID_H[0];
else
display_id = cinfo->DisplayID_H[1];
if(!display_id)
display_id = cinfo->DisplayID_A[0] ? cinfo->DisplayID_A[0] : cinfo->DisplayID_A[1];
display_id = cinfo->ModelId[urand(0,3)];
}
else
else if (cinfo->ModelId[2] && cinfo->ModelId[1] && cinfo->ModelId[0])
{
if(cinfo->DisplayID_A[0])
display_id = cinfo->DisplayID_A[1] ? cinfo->DisplayID_A[urand(0,1)] : cinfo->DisplayID_A[0];
else
display_id = cinfo->DisplayID_A[1];
uint32 modelid_tmp = GetCreatureModelAlternativeModel(cinfo->ModelId[1]);
display_id = modelid_tmp ? cinfo->ModelId[urand(0,2)] : cinfo->ModelId[2];
}
else if (cinfo->ModelId[1])
{
// We use this to eliminate invisible models vs. "dummy" models (infernals, etc).
// Where it's expected to select one of two, model must have a alternative model defined (alternative model is normally the same as defined in ModelId1).
// Same pattern is used in the above model selection, but the result may be ModelId3 and not ModelId2 as here.
uint32 modelid_tmp = GetCreatureModelAlternativeModel(cinfo->ModelId[1]);
display_id = modelid_tmp ? modelid_tmp : cinfo->ModelId[1];
}
else if (cinfo->ModelId[0])
{
display_id = cinfo->ModelId[0];
}
if(!display_id)
display_id = cinfo->DisplayID_H[0] ? cinfo->DisplayID_H[0] : cinfo->DisplayID_H[1];
// fail safe, we use creature entry 1 and make error
if (!display_id)
{
sLog.outErrorDb("Call customer support, ChooseDisplayId can not select native model for creature entry %u, model from creature entry 1 will be used instead.", cinfo->Entry);
if (const CreatureInfo *creatureDefault = GetCreatureTemplate(1))
display_id = creatureDefault->ModelId[0];
}
return display_id;
}
// generally models that does not have a gender(2), or has alternative model for same gender
uint32 ObjectMgr::GetCreatureModelAlternativeModel(uint32 modelId)
{
if (const CreatureModelInfo *modelInfo = GetCreatureModelInfo(modelId))
return modelInfo->modelid_alternative;
return 0;
}
// generally for models having another model for the other team (totems)
uint32 ObjectMgr::GetCreatureModelOtherTeamModel(uint32 modelId)
{
if (const CreatureModelInfo *modelInfo = GetCreatureModelInfo(modelId))
return modelInfo->modelid_other_team;
return 0;
}
CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
{
CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
@ -1015,19 +1050,31 @@ void ObjectMgr::LoadCreatureModelInfo()
continue;
if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid))
sLog.outErrorDb("Table `creature_model_info` has model for not existed display id (%u).", minfo->modelid);
sLog.outErrorDb("Table `creature_model_info` has model for nonexistent model id (%u).", minfo->modelid);
if (minfo->gender >= MAX_GENDER)
{
sLog.outErrorDb("Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(minfo->gender), minfo->modelid);
sLog.outErrorDb("Table `creature_model_info` has invalid gender (%u) for model id (%u).", uint32(minfo->gender), minfo->modelid);
const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE;
}
if (minfo->modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender))
{
sLog.outErrorDb("Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", minfo->modelid_other_gender, minfo->modelid);
sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_other_gender model (%u) defined for model id %u.", minfo->modelid_other_gender, minfo->modelid);
const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
}
if (minfo->modelid_alternative && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_alternative))
{
sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_alternative model (%u) defined for model id %u.", minfo->modelid_alternative, minfo->modelid);
const_cast<CreatureModelInfo*>(minfo)->modelid_alternative = 0;
}
if (minfo->modelid_other_team && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_team))
{
sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_other_team model (%u) defined for model id %u.", minfo->modelid_other_team, minfo->modelid);
const_cast<CreatureModelInfo*>(minfo)->modelid_other_team = 0;
}
}
// character races expected have model info data in table
@ -1150,25 +1197,25 @@ void ObjectMgr::LoadCreatures()
CreatureData& data = mCreatureDataMap[guid];
data.id = entry;
data.mapid = fields[ 2].GetUInt32();
data.displayid = fields[ 3].GetUInt32();
data.equipmentId = fields[ 4].GetUInt32();
data.posX = fields[ 5].GetFloat();
data.posY = fields[ 6].GetFloat();
data.posZ = fields[ 7].GetFloat();
data.orientation = fields[ 8].GetFloat();
data.spawntimesecs = fields[ 9].GetUInt32();
data.spawndist = fields[10].GetFloat();
data.currentwaypoint= fields[11].GetUInt32();
data.curhealth = fields[12].GetUInt32();
data.curmana = fields[13].GetUInt32();
data.is_dead = fields[14].GetBool();
data.movementType = fields[15].GetUInt8();
data.spawnMask = fields[16].GetUInt8();
data.phaseMask = fields[17].GetUInt16();
int16 gameEvent = fields[18].GetInt16();
int16 PoolId = fields[19].GetInt16();
data.id = entry;
data.mapid = fields[ 2].GetUInt32();
data.modelid_override = fields[ 3].GetUInt32();
data.equipmentId = fields[ 4].GetUInt32();
data.posX = fields[ 5].GetFloat();
data.posY = fields[ 6].GetFloat();
data.posZ = fields[ 7].GetFloat();
data.orientation = fields[ 8].GetFloat();
data.spawntimesecs = fields[ 9].GetUInt32();
data.spawndist = fields[10].GetFloat();
data.currentwaypoint = fields[11].GetUInt32();
data.curhealth = fields[12].GetUInt32();
data.curmana = fields[13].GetUInt32();
data.is_dead = fields[14].GetBool();
data.movementType = fields[15].GetUInt8();
data.spawnMask = fields[16].GetUInt8();
data.phaseMask = fields[17].GetUInt16();
int16 gameEvent = fields[18].GetInt16();
int16 PoolId = fields[19].GetInt16();
MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
if(!mapEntry)
@ -1193,6 +1240,12 @@ void ObjectMgr::LoadCreatures()
if (!ok)
continue;
if (data.modelid_override > 0 && !sCreatureDisplayInfoStore.LookupEntry(data.modelid_override))
{
sLog.outErrorDb("Table `creature` GUID %u (entry %u) has model for nonexistent model id (%u), set to 0.", guid, data.id, data.modelid_override);
data.modelid_override = 0;
}
if(data.equipmentId > 0) // -1 no equipment, 0 use default
{
if(!GetEquipmentInfo(data.equipmentId))