Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize Creature model System #322

Merged
merged 3 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading