Skip to content

Commit

Permalink
Implement creature group based selection of creature ids.
Browse files Browse the repository at this point in the history
  • Loading branch information
ratkosrb committed Sep 4, 2023
1 parent 05d8f8a commit 59f7270
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 9 deletions.
26 changes: 26 additions & 0 deletions sql/migrations/20230904190328_world.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
DROP PROCEDURE IF EXISTS add_migration;
delimiter ??
CREATE PROCEDURE `add_migration`()
BEGIN
DECLARE v INT DEFAULT 1;
SET v = (SELECT COUNT(*) FROM `migrations` WHERE `id`='20230904190328');
IF v=0 THEN
INSERT INTO `migrations` VALUES ('20230904190328');
-- Add your query below.


-- Add table to allow limiting number of times a creature id is selected in group.
CREATE TABLE IF NOT EXISTS `creature_groups_entry_limit` (
`leader_guid` int(11) unsigned NOT NULL,
`creature_id` int(11) unsigned NOT NULL,
`min_count` int(11) unsigned NOT NULL DEFAULT '0',
`max_count` int(11) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`leader_guid`,`creature_id`)
) ENGINE=MyISAM;

-- End of migration.
END IF;
END??
delimiter ;
CALL add_migration();
DROP PROCEDURE IF EXISTS add_migration;
156 changes: 152 additions & 4 deletions src/game/CreatureGroups.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,93 @@ void CreatureGroup::SaveToDb()
m_originalLeaderGuid.GetCounter(), itr.first.GetCounter(), itr.second.followDistance, itr.second.followAngle, itr.second.memberFlags);
}

uint32 CreatureGroup::ChooseCreatureId(ObjectGuid guid, CreatureData const* pData, Map* pMap) const
{
if (m_entryLimits.empty())
return pData->ChooseCreatureId();

ObjectGuidSet nonSpawnedMembers; // at first load of spawn the other members might not be loaded yet
std::map<uint32 /*entry*/, std::pair<int32 /*min*/, int32 /*max*/>> currentLimits = m_entryLimits;
for (auto const& itr : m_members)
{
if (itr.first == guid)
continue;

if (Creature* pOtherMember = pMap->GetCreature(itr.first))
{
auto itr2 = currentLimits.find(pOtherMember->GetEntry());
if (itr2 != currentLimits.end())
{
--itr2->second.first;
--itr2->second.second;
}
}
else
nonSpawnedMembers.insert(itr.first);
}
if (guid != GetOriginalLeaderGuid())
{
if (Creature* pLeader = pMap->GetCreature(GetOriginalLeaderGuid()))
{
auto itr2 = currentLimits.find(pLeader->GetEntry());
if (itr2 != currentLimits.end())
{
--itr2->second.first;
--itr2->second.second;
}
}
else
nonSpawnedMembers.insert(GetOriginalLeaderGuid());
}

std::set<uint32> availableIds;
for (int i = 0; i < MAX_CREATURE_IDS_PER_SPAWN; ++i)
{
uint32 creatureId = pData->creature_id[i];
if (!creatureId)
break;

auto itr2 = currentLimits.find(creatureId);
if (itr2 == currentLimits.end() || itr2->second.second > 0)
availableIds.insert(creatureId);
}

if (availableIds.empty())
{
sLog.Out(LOG_DBERROR, LOG_LVL_MINIMAL, "CREATURE GROUPS: Unable to select creature id for %s", guid.GetString().c_str());
return pData->ChooseCreatureId();
}

// check minimum count
for (auto const& itr : currentLimits)
{
if (itr.second.first > 0 && availableIds.find(itr.first) != availableIds.end())
{
if (nonSpawnedMembers.empty())
return itr.first;

uint32 otherSpawnsWithEntryCount = 0;
for (auto const& memberGuid : nonSpawnedMembers)
{
if (CreatureData const* pMemberData = sObjectMgr.GetCreatureData(memberGuid.GetCounter()))
{
if (pMemberData->HasCreatureId(itr.first))
otherSpawnsWithEntryCount++;
}
}

// Not enough unspawned points left to satisfy min limit. We must select this creature id.
if (otherSpawnsWithEntryCount <= itr.second.first)
return itr.first;
}
}

uint32 selectedIndex = urand(0, availableIds.size() - 1);
auto itr3 = availableIds.begin();
std::advance(itr3, selectedIndex);
return *itr3;
}

bool CreatureGroupMember::ComputeRelativePosition(float leaderAngle, float &x, float &y) const
{
x = cos(followAngle + leaderAngle) * followDistance;
Expand Down Expand Up @@ -372,18 +459,79 @@ void CreatureGroupsManager::Load()
}
while (result->NextRow());

result.reset(WorldDatabase.Query("SELECT `leader_guid`, `creature_id`, `min_count`, `max_count` FROM `creature_groups_entry_limit` ORDER BY `leader_guid`"));

if (result)
{
Field* fields;
CreatureGroup *currentGroup = nullptr;

do
{
fields = result->Fetch();

//Load group member data
ObjectGuid leaderGuid = ConvertDBGuid(fields[0].GetUInt32());
uint32 creatureId = fields[1].GetUInt32();
int32 minCount = fields[2].GetInt32();
int32 maxCount = fields[3].GetInt32();

if (maxCount <= 0)
maxCount = INT_MAX;
else if (minCount > maxCount)
{
sLog.Out(LOG_DBERROR, LOG_LVL_MINIMAL, "CREATURE GROUPS: Min count %u is bigger than Max count %u for id %u in group with leader guid %u", minCount, maxCount, creatureId, fields[0].GetUInt32());
continue;
}

if (!sObjectMgr.GetCreatureTemplate(creatureId))
{
if (!sObjectMgr.IsExistingCreatureId(creatureId))
sLog.Out(LOG_DBERROR, LOG_LVL_MINIMAL, "CREATURE GROUPS: Bad creature id %u", creatureId);
continue;
}

if (leaderGuid.IsEmpty())
{
if (!sObjectMgr.IsExistingCreatureGuid(fields[0].GetUInt32()))
sLog.Out(LOG_DBERROR, LOG_LVL_MINIMAL, "CREATURE GROUPS: Bad leader guid %u", fields[0].GetUInt32());
}
else
{
if (!currentGroup || leaderGuid != currentGroup->GetOriginalLeaderGuid())
{
currentGroup = nullptr;
for (const auto& itr : m_groups)
{
if (itr.first == leaderGuid)
{
currentGroup = itr.second;
break;
}
}
}

if (!currentGroup)
{
sLog.Out(LOG_DBERROR, LOG_LVL_MINIMAL, "CREATURE GROUPS: Bad leader guid %u", fields[0].GetUInt32());
continue;
}

currentGroup->m_entryLimits[creatureId] = std::make_pair(minCount, maxCount);
}
} while (result->NextRow());
}

sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, "");
sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, ">> Loaded %u creature groups in %u ms", count, WorldTimer::getMSTime() - oldMSTime);
}

void CreatureGroupsManager::LoadCreatureGroup(Creature* creature, CreatureGroup*& group)
void CreatureGroupsManager::LoadCreatureGroup(ObjectGuid guid, CreatureGroup*& group)
{
group = nullptr;
if (!creature->HasStaticDBSpawnData())
return;
for (const auto& itr : m_groups)
{
if (itr.first == creature->GetObjectGuid() || itr.second->ContainsGuid(creature->GetObjectGuid()))
if (itr.first == guid || itr.second->ContainsGuid(guid))
{
group = itr.second;
break;
Expand Down
8 changes: 7 additions & 1 deletion src/game/CreatureGroups.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
class Unit;
class Creature;
class CreatureGroup;
struct CreatureData;

enum OptionFlags
{
Expand All @@ -50,8 +51,11 @@ struct CreatureGroupMember
uint32 memberFlags = 0;
};

class CreatureGroupsManager;

class CreatureGroup
{
friend class CreatureGroupsManager;
public:
CreatureGroup(ObjectGuid leader) : m_leaderGuid(leader), m_originalLeaderGuid(leader), m_options(0), m_assistGuard(false), m_respawnGuard(false), m_deleted(false), m_lastReachedWaypoint(0)
{
Expand All @@ -70,6 +74,7 @@ class CreatureGroup
bool IsFormation() const { return m_options & OPTION_FORMATION_MOVE; }
bool HasGroupFlag(uint32 flag) const { return m_options & flag; }
void SetLastReachedWaypoint(uint32 point) { m_lastReachedWaypoint = point; }
uint32 ChooseCreatureId(ObjectGuid guid, CreatureData const* pData, Map* pMap) const;

void OnMemberAttackStart(Creature* member, Unit* target);
void OnMemberDied(Creature* member);
Expand All @@ -87,6 +92,7 @@ class CreatureGroup
bool m_deleted;
uint32 m_lastReachedWaypoint;
std::map<ObjectGuid, CreatureGroupMember> m_members;
std::map<uint32 /*entry*/, std::pair<int32 /*min*/, int32 /*max*/>> m_entryLimits;
};

class CreatureGroupsManager
Expand All @@ -97,7 +103,7 @@ class CreatureGroupsManager
static CreatureGroupsManager* i = new CreatureGroupsManager();
return i;
}
void LoadCreatureGroup(Creature* creature, CreatureGroup*& group);
void LoadCreatureGroup(ObjectGuid guid, CreatureGroup*& group);
void RegisterNewGroup(CreatureGroup* group) { m_groups[group->GetOriginalLeaderGuid()] = group; }
void Load();
void EraseCreatureGroup(ObjectGuid leaderGuid) { m_groups.erase(leaderGuid); }
Expand Down
12 changes: 8 additions & 4 deletions src/game/Objects/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ void Creature::AddToWorld()
if (!IsInWorld() && GetObjectGuid().GetHigh() == HIGHGUID_UNIT)
GetMap()->InsertObject<Creature>(GetObjectGuid(), this);

sCreatureGroupsManager->LoadCreatureGroup(this, m_creatureGroup);
if (!m_creatureGroup && HasStaticDBSpawnData())
sCreatureGroupsManager->LoadCreatureGroup(GetObjectGuid(), m_creatureGroup);

if (m_creatureGroup)
{
if (m_creatureGroup->IsFormation())
Expand Down Expand Up @@ -652,7 +654,7 @@ void Creature::Update(uint32 update_diff, uint32 diff)
RemoveAllAuras();

// pick a new creature id if db spawn has multiple
uint32 const newCreatureId = m_creatureData ? m_creatureData->ChooseCreatureId() : m_originalEntry;
uint32 const newCreatureId = m_creatureData ? (m_creatureGroup ? m_creatureGroup->ChooseCreatureId(GetObjectGuid(), m_creatureData, GetMap()) : m_creatureData->ChooseCreatureId()) : m_originalEntry;

if (newCreatureId != m_originalEntry)
m_originalEntry = newCreatureId;
Expand Down Expand Up @@ -1724,10 +1726,12 @@ bool Creature::LoadFromDB(uint32 guidlow, Map* map, bool force)
if (!force && (data->spawn_flags & SPAWN_FLAG_DISABLED))
return false;

ObjectGuid fullGuid = ObjectGuid(HIGHGUID_UNIT, data->creature_id[0], guidlow);
m_creatureData = data;
m_creatureDataAddon = sObjectMgr.GetCreatureAddon(guidlow);
sCreatureGroupsManager->LoadCreatureGroup(fullGuid, m_creatureGroup);

uint32 const creatureId = data->ChooseCreatureId();
uint32 const creatureId = m_creatureGroup ? m_creatureGroup->ChooseCreatureId(fullGuid, data, map) : data->ChooseCreatureId();
CreatureInfo const* cinfo = ObjectMgr::GetCreatureTemplate(creatureId);
if (!cinfo)
{
Expand All @@ -1738,7 +1742,7 @@ bool Creature::LoadFromDB(uint32 guidlow, Map* map, bool force)
GameEventCreatureData const* eventData = sGameEventMgr.GetCreatureUpdateDataForActiveEvent(guidlow);

// Creature can be loaded already in map if grid has been unloaded while creature walk to another grid
if (map->GetCreature(cinfo->GetObjectGuid(guidlow)))
if (map->GetCreature(fullGuid))
return false;

CreatureCreatePos pos(map, data->position.x, data->position.y, data->position.z, data->position.o);
Expand Down
4 changes: 4 additions & 0 deletions src/game/Objects/CreatureDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ struct CreatureData

return creatureId;
}
bool HasCreatureId(uint32 id) const
{
return std::find(creature_id.begin(), creature_id.end(), id) != creature_id.end();
}
uint32 GetCreatureIdCount() const
{
uint32 creatureIdCount = 0;
Expand Down

0 comments on commit 59f7270

Please sign in to comment.