From d0df25fd8c6f016b2c3690e2cf8581450de620d2 Mon Sep 17 00:00:00 2001 From: NoFantasy Date: Thu, 19 Aug 2010 16:57:48 +0200 Subject: [PATCH] [10381] Implement generic system for racial model selection Table creature_model_info store creature entry to use model from (or explicit model). The selection is based on a base modelId and racemask. Hacks for shapeshift models removed (data included in SQL update) Dropped no longer needed creature_model_info.modelid_other_team, as creature_model_info can and should be used instead (sorry, this is what happen when author doesn't do full research :) ) Signed-off-by: NoFantasy --- sql/mangos.sql | 67 +++++--- .../10381_01_mangos_creature_model_race.sql | 16 ++ sql/updates/Makefile.am | 2 + src/game/Creature.cpp | 9 +- src/game/Creature.h | 9 +- src/game/ObjectMgr.cpp | 157 ++++++++++++++++-- src/game/ObjectMgr.h | 8 +- src/game/SpellAuras.cpp | 41 ++--- src/game/SpellEffects.cpp | 7 + src/game/World.cpp | 3 + src/shared/Database/SQLStorage.cpp | 2 +- src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 13 files changed, 243 insertions(+), 82 deletions(-) create mode 100644 sql/updates/10381_01_mangos_creature_model_race.sql diff --git a/sql/mangos.sql b/sql/mangos.sql index 74cdd21f2..75ded4bb2 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_10365_01_mangos_creature_ai_scripts` bit(1) default NULL + `required_10381_01_mangos_creature_model_race` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -956,7 +956,6 @@ CREATE TABLE `creature_model_info` ( `gender` tinyint(3) unsigned NOT NULL default '2', `modelid_other_gender` mediumint(8) unsigned NOT NULL default '0', `modelid_alternative` mediumint(8) unsigned NOT NULL default '0', - `modelid_other_team` mediumint(8) unsigned NOT NULL default '0', PRIMARY KEY (`modelid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Creature System (Model related info)'; @@ -967,30 +966,52 @@ CREATE TABLE `creature_model_info` ( LOCK TABLES `creature_model_info` WRITE; /*!40000 ALTER TABLE `creature_model_info` DISABLE KEYS */; INSERT INTO `creature_model_info` VALUES -(49, 0.3060, 1.5, 0, 50, 0, 0), -(50, 0.2080, 1.5, 1, 49, 0, 0), -(51, 0.3720, 1.5, 0, 52, 0, 0), -(52, 0.2360, 1.5, 1, 51, 0, 0), -(53, 0.3470, 1.5, 0, 54, 0, 0), -(54, 0.3470, 1.5, 1, 53, 0, 0), -(55, 0.3890, 1.5, 0, 56, 0, 0), -(56, 0.3060, 1.5, 1, 55, 0, 0), -(57, 0.3830, 1.5, 0, 58, 0, 0), -(58, 0.3830, 1.5, 1, 57, 0, 0), -(59, 0.9747, 1.5, 0, 60, 0, 0), -(60, 0.8725, 1.5, 1, 59, 0, 0), -(1478, 0.3060, 1.5, 0, 1479, 0, 0), -(1479, 0.3060, 1.5, 1, 1478, 0, 0), -(1563, 0.3519, 1.5, 0, 1564, 0, 0), -(1564, 0.3519, 1.5, 1, 1563, 0, 0), -(10045, 1.0000, 1.5, 2, 0, 0, 0), -(15475, 0.3830, 1.5, 1, 15476, 0, 0), -(15476, 0.3830, 1.5, 0, 15475, 0, 0), -(16125, 1.0000, 1.5, 0, 16126, 0, 0), -(16126, 1.0000, 1.5, 1, 16125, 0, 0); +(49, 0.3060, 1.5, 0, 50, 0), +(50, 0.2080, 1.5, 1, 49, 0), +(51, 0.3720, 1.5, 0, 52, 0), +(52, 0.2360, 1.5, 1, 51, 0), +(53, 0.3470, 1.5, 0, 54, 0), +(54, 0.3470, 1.5, 1, 53, 0), +(55, 0.3890, 1.5, 0, 56, 0), +(56, 0.3060, 1.5, 1, 55, 0), +(57, 0.3830, 1.5, 0, 58, 0), +(58, 0.3830, 1.5, 1, 57, 0), +(59, 0.9747, 1.5, 0, 60, 0), +(60, 0.8725, 1.5, 1, 59, 0), +(1478, 0.3060, 1.5, 0, 1479, 0), +(1479, 0.3060, 1.5, 1, 1478, 0), +(1563, 0.3519, 1.5, 0, 1564, 0), +(1564, 0.3519, 1.5, 1, 1563, 0), +(10045, 1.0000, 1.5, 2, 0, 0), +(15475, 0.3830, 1.5, 1, 15476, 0), +(15476, 0.3830, 1.5, 0, 15475, 0), +(16125, 1.0000, 1.5, 0, 16126, 0), +(16126, 1.0000, 1.5, 1, 16125, 0); /*!40000 ALTER TABLE `creature_model_info` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `creature_model_race` +-- + +DROP TABLE IF EXISTS `creature_model_race`; +CREATE TABLE `creature_model_race` ( + `modelid` mediumint(8) unsigned NOT NULL default '0', + `racemask` mediumint(8) unsigned NOT NULL default '0', + `creature_entry` mediumint(8) unsigned NOT NULL default '0' COMMENT 'option 1, modelid_N from creature_template', + `modelid_racial` mediumint(8) unsigned NOT NULL default '0' COMMENT 'option 2, explicit modelid', + PRIMARY KEY (`modelid`,`racemask`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Model system'; + +-- +-- Dumping data for table `creature_model_race` +-- + +LOCK TABLES `creature_model_race` WRITE; +/*!40000 ALTER TABLE `creature_model_race` DISABLE KEYS */; +/*!40000 ALTER TABLE `creature_model_race` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `creature_movement` -- diff --git a/sql/updates/10381_01_mangos_creature_model_race.sql b/sql/updates/10381_01_mangos_creature_model_race.sql new file mode 100644 index 000000000..25b3f7e23 --- /dev/null +++ b/sql/updates/10381_01_mangos_creature_model_race.sql @@ -0,0 +1,16 @@ +ALTER TABLE db_version CHANGE COLUMN required_10365_01_mangos_creature_ai_scripts required_10381_01_mangos_creature_model_race bit; + +DROP TABLE IF EXISTS `creature_model_race`; +CREATE TABLE `creature_model_race` ( + `modelid` mediumint(8) unsigned NOT NULL default '0', + `racemask` mediumint(8) unsigned NOT NULL default '0', + `creature_entry` mediumint(8) unsigned NOT NULL default '0' COMMENT 'option 1, modelid_N from creature_template', + `modelid_racial` mediumint(8) unsigned NOT NULL default '0' COMMENT 'option 2, explicit modelid', + PRIMARY KEY (`modelid`,`racemask`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Model system'; + +-- Data from removed hard coded shapeshift models. Mangos will not provide updates in the future. +INSERT INTO creature_model_race VALUES (892, 690, 0, 8571),(2281, 690, 0, 2289),(21243, 690, 0, 21244),(20857, 690, 0, 20872); + +-- Drop no longer needed field +ALTER TABLE creature_model_info DROP COLUMN modelid_other_team; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index f591aeb9b..0f9c2bda2 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -82,6 +82,7 @@ pkgdata_DATA = \ 10353_02_mangos_command.sql \ 10362_01_mangos_creature_movement_template.sql \ 10365_01_mangos_creature_ai_scripts.sql \ + 10381_01_mangos_creature_model_race.sql \ README ## Additional files to include when running 'make dist' @@ -144,4 +145,5 @@ EXTRA_DIST = \ 10353_02_mangos_command.sql \ 10362_01_mangos_creature_movement_template.sql \ 10365_01_mangos_creature_ai_scripts.sql \ + 10381_01_mangos_creature_model_race.sql \ README diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 0e0748893..ab588a8fa 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -247,14 +247,7 @@ bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data ) SetNativeDisplayId(display_id); - // special case for totems (model for team==HORDE is stored in creature_template as the default) - if (team == ALLIANCE && cinfo->type == CREATURE_TYPE_TOTEM) - { - uint32 modelid_tmp = sObjectMgr.GetCreatureModelOtherTeamModel(display_id); - display_id = modelid_tmp ? modelid_tmp : display_id; - } - - // normally the same as native, see above for the exeption + // normally the same as native, but some has exceptions (Spell::DoSummonTotem) SetDisplayId(display_id); SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender); diff --git a/src/game/Creature.h b/src/game/Creature.h index fafdf22e0..a822c7300 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -232,7 +232,14 @@ struct CreatureModelInfo uint8 gender; uint32 modelid_other_gender; // The oposite gender for this modelid (male/female) uint32 modelid_alternative; // An alternative model. Generally same gender(2) - uint32 modelid_other_team; // The oposite team. Generally for alliance totem +}; + +struct CreatureModelRace +{ + uint32 modelid; // Native model/base model the selection is for + uint32 racemask; // Races it applies to (and then a player source must exist for selection) + uint32 creature_entry; // Modelid from creature_template.entry will be selected + uint32 modelid_racial; // Explicit modelid. Used if creature_template entry is not defined }; enum InhabitTypeValues diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 83bdf28a9..676d432e4 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -911,15 +911,6 @@ uint32 ObjectMgr::GetCreatureModelAlternativeModel(uint32 modelId) 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); @@ -942,6 +933,29 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_ return minfo; } +uint32 ObjectMgr::GetModelForRace(uint32 sourceModelId, uint32 racemask) +{ + uint32 modelId = 0; + + for(CreatureModelRaceMap::const_iterator itr = m_mCreatureModelRaceMap.lower_bound(sourceModelId); itr != m_mCreatureModelRaceMap.upper_bound(sourceModelId); ++itr) + { + if (!(itr->second.racemask & racemask)) + continue; + + if (itr->second.creature_entry) + { + const CreatureInfo *cInfo = sObjectMgr.GetCreatureTemplate(itr->second.creature_entry); + modelId = Creature::ChooseDisplayId(cInfo); + } + else + { + modelId = itr->second.modelid_racial; + } + } + + return modelId; +} + void ObjectMgr::LoadCreatureModelInfo() { sCreatureModelStorage.Load(); @@ -973,12 +987,6 @@ void ObjectMgr::LoadCreatureModelInfo() sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_alternative model (%u) defined for model id %u.", minfo->modelid_alternative, minfo->modelid); const_cast(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(minfo)->modelid_other_team = 0; - } } // character races expected have model info data in table @@ -1043,6 +1051,125 @@ void ObjectMgr::LoadCreatureModelInfo() sLog.outString(); } +void ObjectMgr::LoadCreatureModelRace() +{ + m_mCreatureModelRaceMap.clear(); // can be used for reload + + QueryResult* result = WorldDatabase.Query("SELECT modelid, racemask, creature_entry, modelid_racial FROM creature_model_race"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded creature_model_race, table is empty!"); + return; + } + + barGoLink bar( (int)result->GetRowCount() ); + + uint32 count = 0; + + // model, racemask + std::map model2raceMask; + + do + { + bar.step(); + + Field* fields = result->Fetch(); + + CreatureModelRace raceData; + + raceData.modelid = fields[0].GetUInt32(); + raceData.racemask = fields[1].GetUInt32(); + raceData.creature_entry = fields[2].GetUInt32(); + raceData.modelid_racial = fields[3].GetUInt32(); + + if (!sCreatureDisplayInfoStore.LookupEntry(raceData.modelid)) + { + sLog.outErrorDb("Table `creature_model_race` has model for nonexistent model id (%u), skipping", raceData.modelid); + continue; + } + + if (!sCreatureModelStorage.LookupEntry(raceData.modelid)) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u does not exist in creature_model_info, skipping", raceData.modelid); + continue; + } + + if (!raceData.racemask) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u has no racemask defined, skipping", raceData.modelid); + continue; + } + + if (!(raceData.racemask & RACEMASK_ALL_PLAYABLE)) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u include invalid racemask, skipping", raceData.modelid); + continue; + } + + std::map::const_iterator model2Race = model2raceMask.find(raceData.modelid); + + // can't have same mask for same model several times + if (model2Race != model2raceMask.end()) + { + if (model2Race->second & raceData.racemask) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u with racemask %u has mask already included for same modelid, skipping", raceData.modelid, raceData.racemask); + continue; + } + } + + model2raceMask[raceData.modelid] |= raceData.racemask; + + // creature_entry is the prefered way + if (raceData.creature_entry) + { + if (raceData.modelid_racial) + sLog.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial for modelid %u but a creature_entry are already defined, modelid_racial will never be used.", raceData.modelid); + + if (!sCreatureStorage.LookupEntry(raceData.creature_entry)) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u has creature_entry for nonexistent creature_template (%u), skipping", raceData.modelid, raceData.creature_entry); + continue; + } + } + else if (raceData.modelid_racial) + { + if (!sCreatureDisplayInfoStore.LookupEntry(raceData.modelid_racial)) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial for nonexistent model id (%u), skipping", raceData.modelid, raceData.modelid_racial); + continue; + } + + if (!sCreatureModelStorage.LookupEntry(raceData.modelid_racial)) + { + sLog.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial %u, but are not defined in creature_model_info, skipping", raceData.modelid, raceData.modelid_racial); + continue; + } + } + else + { + sLog.outErrorDb("Table `creature_model_race` modelid %u does not have either creature_entry or modelid_racial defined, skipping", raceData.modelid); + continue; + } + + m_mCreatureModelRaceMap.insert(CreatureModelRaceMap::value_type(raceData.modelid, raceData)); + + ++count; + } + while(result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u creature_model_race entries", count); +} + void ObjectMgr::LoadCreatures() { uint32 count = 0; diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 2a7effb5b..fc0a7b3c6 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -520,7 +520,6 @@ class ObjectMgr CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid ); CreatureModelInfo const* GetCreatureModelRandomGender(uint32 display_id); uint32 GetCreatureModelAlternativeModel(uint32 modelId); - uint32 GetCreatureModelOtherTeamModel(uint32 modelId); EquipmentInfo const *GetEquipmentInfo( uint32 entry ); static CreatureDataAddon const *GetCreatureAddon( uint32 lowguid ) @@ -694,6 +693,7 @@ class ObjectMgr void LoadCreatureRespawnTimes(); void LoadCreatureAddons(); void LoadCreatureModelInfo(); + void LoadCreatureModelRace(); void LoadEquipmentTemplates(); void LoadGameObjectLocales(); void LoadGameobjects(); @@ -1016,6 +1016,8 @@ class ObjectMgr return GossipMenuItemsMapBounds(m_mGossipMenuItemsMap.lower_bound(uiMenuId),m_mGossipMenuItemsMap.upper_bound(uiMenuId)); } + uint32 GetModelForRace(uint32 sourceModelId, uint32 racemask); + protected: // first free id for selected id type @@ -1041,6 +1043,8 @@ class ObjectMgr typedef std::set TavernAreaTriggerSet; typedef std::set GameObjectForQuestSet; + typedef std::multimap CreatureModelRaceMap; + GroupMap mGroupMap; GuildMap mGuildMap; ArenaTeamMap mArenaTeamMap; @@ -1135,6 +1139,8 @@ class ObjectMgr typedef std::vector ConditionStore; ConditionStore mConditions; + CreatureModelRaceMap m_mCreatureModelRaceMap; + CacheNpcTextIdMap m_mCacheNpcTextIdMap; CacheVendorItemMap m_mCacheVendorItemMap; CacheTrainerSpellMap m_mCacheTrainerSpellMap; diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 24c36df37..a6dceb9b0 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -2779,39 +2779,18 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) modelid = ssEntry->modelID_A; else { - // players are a bit difficult since the dbc has seldomly an horde modelid - // so we add hacks here to set the right model - if (Player::TeamForRace(target->getRace()) == ALLIANCE) - modelid = ssEntry->modelID_A; - else // 3.2.3 only the moonkin form has this information - modelid = ssEntry->modelID_H; - - // no model found, if player is horde we look here for our hardcoded modelids - if (!modelid && Player::TeamForRace(target->getRace()) == HORDE) + // players are a bit different since the dbc has seldomly an horde modelid + if (Player::TeamForRace(target->getRace()) == HORDE) { - - switch(form) - { - case FORM_CAT: - modelid = 8571; - break; - case FORM_BEAR: - case FORM_DIREBEAR: - modelid = 2289; - break; - case FORM_FLIGHT: - modelid = 20872; - break; - case FORM_FLIGHT_EPIC: - modelid = 21244; - break; - // per default use alliance modelid - // mostly horde and alliance share the same - default: - modelid = ssEntry->modelID_A; - break; - } + if (ssEntry->modelID_H) + modelid = ssEntry->modelID_H; // 3.2.3 only the moonkin form has this information + else // get model for race + modelid = sObjectMgr.GetModelForRace(ssEntry->modelID_A, target->getRaceMask()); } + + // nothing found in above, so use default + if (!modelid) + modelid = ssEntry->modelID_A; } } diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index be31f3ae7..a37dc5e79 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -6830,6 +6830,13 @@ void Spell::DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc) return; } + // special model selection case for totems + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + if (uint32 modelid_race = sObjectMgr.GetModelForRace(pTotem->GetNativeDisplayId(), m_caster->getRaceMask())) + pTotem->SetDisplayId(modelid_race); + } + float angle = slot < MAX_TOTEM_SLOT ? M_PI_F/MAX_TOTEM_SLOT - (slot*2*M_PI_F/MAX_TOTEM_SLOT) : 0; float x, y, z; diff --git a/src/game/World.cpp b/src/game/World.cpp index f45f20c52..6d2f002ee 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1004,6 +1004,9 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Creature templates..." ); sObjectMgr.LoadCreatureTemplates(); + sLog.outString( "Loading Creature Model for race..." ); // must be after creature templates + sObjectMgr.LoadCreatureModelRace(); + sLog.outString( "Loading SpellsScriptTarget..."); sSpellMgr.LoadSpellScriptTarget(); // must be after LoadCreatureTemplates and LoadGameobjectInfo diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp index d90d30916..a797cb52e 100644 --- a/src/shared/Database/SQLStorage.cpp +++ b/src/shared/Database/SQLStorage.cpp @@ -28,7 +28,7 @@ extern DatabaseMysql WorldDatabase; const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis"; const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii"; const char CreatureDataAddonInfofmt[]="iiiiiis"; -const char CreatureModelfmt[]="iffbiii"; +const char CreatureModelfmt[]="iffbii"; const char CreatureInfoAddonInfofmt[]="iiiiiis"; const char EquipmentInfofmt[]="iiii"; const char GameObjectInfosrcfmt[]="iiissssiifiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis"; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 934abe674..870ad4239 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10380" + #define REVISION_NR "10381" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 777768186..3ce36b987 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_10332_02_characters_pet_aura" - #define REVISION_DB_MANGOS "required_10365_01_mangos_creature_ai_scripts" + #define REVISION_DB_MANGOS "required_10381_01_mangos_creature_model_race" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__