Skip to content

Commit

Permalink
[Core/Model] Modernize Creature model System (#322)
Browse files Browse the repository at this point in the history
  • Loading branch information
leelf00 authored Aug 11, 2024
1 parent 3cd41b3 commit 9b96e8f
Show file tree
Hide file tree
Showing 75 changed files with 548 additions and 518 deletions.
24 changes: 24 additions & 0 deletions sql/updates/world/2024_08_11_00_world.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--
DROP TABLE IF EXISTS `creature_template_model`;
CREATE TABLE `creature_template_model`(
`CreatureID` int(10) unsigned NOT NULL,
`Idx` int(10) unsigned NOT NULL DEFAULT '0',
`CreatureDisplayID` int(10) unsigned NOT NULL,
`DisplayScale` float NOT NULL DEFAULT '1',
`Probability` float NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT 0,
PRIMARY KEY (`CreatureID`,`CreatureDisplayID`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT IGNORE INTO `creature_template_model` (`CreatureID`,`Idx`,`CreatureDisplayID`,`DisplayScale`,`Probability`,`VerifiedBuild`) SELECT `entry`,0,`modelid1`,`scale`,1,`VerifiedBuild` FROM `creature_template` WHERE `modelid1`!=0;
INSERT IGNORE INTO `creature_template_model` (`CreatureID`,`Idx`,`CreatureDisplayID`,`DisplayScale`,`Probability`,`VerifiedBuild`) SELECT `entry`,1,`modelid2`,`scale`,1,`VerifiedBuild` FROM `creature_template` WHERE `modelid2`!=0;
INSERT IGNORE INTO `creature_template_model` (`CreatureID`,`Idx`,`CreatureDisplayID`,`DisplayScale`,`Probability`,`VerifiedBuild`) SELECT `entry`,2,`modelid3`,`scale`,1,`VerifiedBuild` FROM `creature_template` WHERE `modelid3`!=0;
INSERT IGNORE INTO `creature_template_model` (`CreatureID`,`Idx`,`CreatureDisplayID`,`DisplayScale`,`Probability`,`VerifiedBuild`) SELECT `entry`,3,`modelid4`,`scale`,1,`VerifiedBuild` FROM `creature_template` WHERE `modelid4`!=0;

UPDATE `creature_template` SET `scale`=1;

ALTER TABLE `creature_template`
DROP `modelid1`,
DROP `modelid2`,
DROP `modelid3`,
DROP `modelid4`;
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, difficulty_entry_4, difficulty_entry_5, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, npcflag2, speed_walk, speed_run, scale, `rank`, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, BaseAttackTime, RangeAttackTime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, difficulty_entry_4, difficulty_entry_5, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, npcflag2, speed_walk, speed_run, scale, `rank`, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, BaseAttackTime, RangeAttackTime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
Expand Down
10 changes: 5 additions & 5 deletions src/server/game/AI/SmartScripts/SmartScript.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down Expand Up @@ -275,10 +275,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature))
{
uint32 displayId = ObjectMgr::ChooseDisplayId(ci);
(*itr)->ToCreature()->SetDisplayId(displayId);
CreatureModel const* model = ObjectMgr::ChooseDisplayId(ci);
(*itr)->ToCreature()->SetDisplayId(model->CreatureDisplayID, model->DisplayScale);
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry %u, GuidLow %u set displayid to %u",
(*itr)->GetEntry(), (*itr)->GetGUID().GetCounter(), displayId);
(*itr)->GetEntry(), (*itr)->GetGUID().GetCounter(), model->CreatureDisplayID);
}
}
//if no param1, then use value from param2 (modelId)
Expand Down Expand Up @@ -1036,7 +1036,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.action.morphOrMount.creature > 0)
{
if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature))
(*itr)->ToUnit()->Mount(ObjectMgr::ChooseDisplayId(cInfo));
(*itr)->ToUnit()->Mount(ObjectMgr::ChooseDisplayId(cInfo)->CreatureDisplayID);
}
else
(*itr)->ToUnit()->Mount(e.action.morphOrMount.model);
Expand Down
4 changes: 2 additions & 2 deletions src/server/game/BattlePet/BattlePet.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down Expand Up @@ -33,7 +33,7 @@ void BattlePet::Initialise(bool newBattlePet)
{
// existence is checked before this, no problem should arise
m_npc = sBattlePetSpeciesStore.LookupEntry(m_species)->NpcId;
m_displayId = sObjectMgr->GetCreatureTemplate(m_npc)->Modelid1;
m_displayId = sObjectMgr->GetCreatureTemplate(m_npc)->GetModelByIdx(0)->CreatureDisplayID;

// setup initial battle pet states
InitialiseStates(newBattlePet);
Expand Down
6 changes: 3 additions & 3 deletions src/server/game/BattlePet/BattlePetMgr.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down Expand Up @@ -642,7 +642,7 @@ void BattlePetMgr::SendBattlePetJournal()
journalData << uint16(battlePet->GetFlags());

journalData << uint32(creatureTemplate->Entry);
journalData << uint32(creatureTemplate->Modelid1);
journalData << uint32(creatureTemplate->GetModelByIdx(0)->CreatureDisplayID);
journalData << uint32(battlePet->GetSpeed());
journalData.WriteString(battlePet->GetNickname());
journalData.WriteByteSeq(petEntry[6]);
Expand Down Expand Up @@ -792,7 +792,7 @@ void BattlePetMgr::SendBattlePetUpdate(BattlePet* battlePet, bool notification)
data << uint16(battlePet->GetFlags());

data.WriteByteSeq(petEntry[5]);
data << uint32(creatureTemplate->Modelid1);
data << uint32(creatureTemplate->GetModelByIdx(0)->CreatureDisplayID);
data << uint16(battlePet->GetLevel());

m_owner->GetSession()->SendPacket(&data);
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/DataStores/DBCStores.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down
4 changes: 2 additions & 2 deletions src/server/game/DataStores/DBCStructure.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down Expand Up @@ -892,7 +892,7 @@ struct CreatureModelDataEntry
{
uint32 Id;
uint32 Flags;
DbcStr ModelPath;
char* ModelName; // 2
//uint32 Unk1;
float ModelScale; // Used in calculation of unit collision data
//int32 Unk2
Expand Down
6 changes: 3 additions & 3 deletions src/server/game/DataStores/DBCfmt.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand All @@ -18,7 +18,7 @@
#ifndef TRINITY_DBCSFRM_H
#define TRINITY_DBCSFRM_H

// x - skip<uint32>, X - skip<uint8>, s - char*, f - float, i - uint32, b - uint8, d - index (not included)
// x - skip<uint32>, X - skip<uint8>, s - char*, S - char*, f - float, i - uint32, b - uint8, d - index (not included)
// n - index (included), l - bool, p - field present in sql dbc, a - field absent in sql dbc

char const Achievementfmt[] = "niiisxiixixxiii";
Expand All @@ -45,7 +45,7 @@ char const ChrClassesXPowerTypesfmt[] = "nii";
char const CinematicCameraEntryfmt[] = "nSiffff";
char const CinematicSequencesEntryfmt[] = "nxiiiiiiii";
char const CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxxxxxx";
char const CreatureModelDatafmt[] = "nisxfxxxxxxxxxxffxxxxxxxxxxxxxxxxx";
char const CreatureModelDatafmt[] = "niSxfxxxxxxxxxxffxxxxxxxxxxxxxxxxx";
char const CreatureFamilyfmt[] = "nfifiiiiixsx";
char const CreatureImmunitiesfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
char const CreatureSpellDatafmt[] = "niiiixxxx";
Expand Down
124 changes: 84 additions & 40 deletions src/server/game/Entities/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,26 +113,67 @@ VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extend
return NULL;
}

uint32 CreatureTemplate::GetRandomValidModelId() const
CreatureModel const CreatureModel::DefaultInvisibleModel(11686, 1.0f, 1.0f);
CreatureModel const CreatureModel::DefaultVisibleModel(17519, 1.0f, 1.0f);

CreatureModel const* CreatureTemplate::GetModelByIdx(uint32 idx) const
{
return idx < Models.size() ? &Models[idx] : nullptr;
}

CreatureModel const* CreatureTemplate::GetRandomValidModel() const
{
if (!Models.size())
return nullptr;

// If only one element, ignore the Probability (even if 0)
if (Models.size() == 1)
return &Models[0];

auto selectedItr = Trinity::Containers::SelectRandomWeightedContainerElement(Models, [](CreatureModel const& model)
{
return model.Probability;
});

return &(*selectedItr);
}

CreatureModel const* CreatureTemplate::GetFirstValidModel() const
{
uint8 c = 0;
uint32 modelIDs[4];
for (CreatureModel const& model : Models)
if (model.CreatureDisplayID)
return &model;

return nullptr;
}

if (Modelid1) modelIDs[c++] = Modelid1;
if (Modelid2) modelIDs[c++] = Modelid2;
if (Modelid3) modelIDs[c++] = Modelid3;
if (Modelid4) modelIDs[c++] = Modelid4;
CreatureModel const* CreatureTemplate::GetModelWithDisplayId(uint32 displayId) const
{
for (CreatureModel const& model : Models)
if (displayId == model.CreatureDisplayID)
return &model;

return ((c>0) ? modelIDs[urand(0, c-1)] : 0);
return nullptr;
}

uint32 CreatureTemplate::GetFirstValidModelId() const
CreatureModel const* CreatureTemplate::GetFirstInvisibleModel() const
{
if (Modelid1) return Modelid1;
if (Modelid2) return Modelid2;
if (Modelid3) return Modelid3;
if (Modelid4) return Modelid4;
return 0;
for (CreatureModel const& model : Models)
if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
if (modelInfo && modelInfo->is_trigger)
return &model;

return &CreatureModel::DefaultInvisibleModel;
}

CreatureModel const* CreatureTemplate::GetFirstVisibleModel() const
{
for (CreatureModel const& model : Models)
if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
if (modelInfo && !modelInfo->is_trigger)
return &model;

return &CreatureModel::DefaultVisibleModel;
}

bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
Expand Down Expand Up @@ -340,22 +381,22 @@ bool Creature::InitEntry(uint32 entry, uint32 /*team*/, const CreatureData* data
SetClass(uint8(cinfo->unit_class));

// Cancel load if no model defined
if (!(cinfo->GetFirstValidModelId()))
if (!(cinfo->GetFirstValidModel()))
{
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ", entry);
return false;
}

uint32 displayID = ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data);
CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID);
CreatureModel model = *ObjectMgr::ChooseDisplayId(cinfo, data);
CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, cinfo);
if (!minfo) // Cancel load if no model defined
{
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ", entry);
return false;
}

SetDisplayId(displayID);
SetNativeDisplayId(displayID);
SetDisplayId(model.CreatureDisplayID, true);
SetNativeDisplayId(model.CreatureDisplayID);
SetGender(minfo->gender);

// Load creature equipment
Expand Down Expand Up @@ -804,6 +845,13 @@ bool Creature::Create(uint32 guidlow, Map* map, uint32 phaseMask, uint32 Entry,
return false;
}

{
// area/zone id is needed immediately for ZoneScript::GetCreatureEntry hook before it is known which creature template to load (no model/scale available yet)
PositionFullTerrainStatus data;
GetMap()->GetFullTerrainStatusForPosition(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), data, MAP_ALL_LIQUIDS, DEFAULT_COLLISION_HEIGHT);
ProcessPositionDataChanged(data);
}

if (!CreateFromProto(guidlow, Entry, vehId, team, data))
return false;

Expand Down Expand Up @@ -840,14 +888,8 @@ bool Creature::Create(uint32 guidlow, Map* map, uint32 phaseMask, uint32 Entry,
Relocate(x, y, z, ang);
}

uint32 displayID = GetNativeDisplayId();
CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID);
if (minfo && !IsTotem()) // Cancel load if no model defined or if totem
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
SetGender(minfo->gender);
}
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
m_positionZ += GetHoverOffset();

LastUsedScriptID = GetScriptId();

Expand Down Expand Up @@ -1042,9 +1084,9 @@ void Creature::SaveToDB(uint32 mapid, uint16 spawnMask, uint32 phaseMask)
CreatureTemplate const* cinfo = GetCreatureTemplate();
if (cinfo)
{
if (displayId == cinfo->Modelid1 || displayId == cinfo->Modelid2 ||
displayId == cinfo->Modelid3 || displayId == cinfo->Modelid4)
displayId = 0;
for (CreatureModel model : cinfo->Models)
if (displayId && displayId == model.CreatureDisplayID)
displayId = 0;

if (npcflag == cinfo->npcflag)
npcflag = 0;
Expand Down Expand Up @@ -1719,14 +1761,10 @@ void Creature::Respawn(bool force)

setDeathState(JUST_RESPAWNED);

uint32 displayID = GetNativeDisplayId();
CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID);
if (minfo) // Cancel load if no model defined
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
SetGender(minfo->gender);
}

CreatureModel display(GetNativeDisplayId(), 0, 1.0f);
if (sObjectMgr->GetCreatureModelRandomGender(&display, GetCreatureTemplate()))
SetDisplayId(display.CreatureDisplayID, true);

sBattlePetSpawnMgr->OnRespawn(this);

Expand Down Expand Up @@ -2869,9 +2907,9 @@ void Creature::SetObjectScale(float scale)
}
}

void Creature::SetDisplayId(uint32 modelId)
void Creature::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/)
{
Unit::SetDisplayId(modelId);
Unit::SetDisplayId(modelId, displayScale);

if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
{
Expand All @@ -2880,6 +2918,12 @@ void Creature::SetDisplayId(uint32 modelId)
}
}

void Creature::SetDisplayFromModel(uint32 modelIdx)
{
if (CreatureModel const* model = GetCreatureTemplate()->GetModelByIdx(modelIdx))
SetDisplayId(model->CreatureDisplayID);
}

void Creature::SetTarget(ObjectGuid guid)
{
if (!_focusSpell)
Expand Down
11 changes: 3 additions & 8 deletions src/server/game/Entities/Creature/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,7 @@ struct CreatureData
uint32 gameEventId = 0;
};

struct CreatureModelInfo
{
float bounding_radius;
float combat_reach;
uint8 gender;
uint32 modelid_other_gender;
};


// Benchmarked: Faster than std::map (insert/find)
typedef std::unordered_map<uint16, CreatureModelInfo> CreatureModelContainer;
Expand Down Expand Up @@ -260,7 +254,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void RemoveFromWorld() override;

void SetObjectScale(float scale) override;
void SetDisplayId(uint32 modelId) override;
void SetDisplayId(uint32 displayId, float displayScale = 1.f) override;
void SetDisplayFromModel(uint32 modelIdx);

void DisappearAndDie();

Expand Down
Loading

0 comments on commit 9b96e8f

Please sign in to comment.