Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

build*/
bin/
.directory
.mailmap
*.orig
Expand Down
10 changes: 5 additions & 5 deletions src/common/Collision/Maps/MapDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ struct MmapTileHeader
// All padding fields must be handled and initialized to ensure mmaps_generator will produce binary-identical *.mmtile files
static_assert(sizeof(MmapTileHeader) == 20, "MmapTileHeader size is not correct, adjust the padding field size");
static_assert(sizeof(MmapTileHeader) == (sizeof(MmapTileHeader::mmapMagic) +
sizeof(MmapTileHeader::dtVersion) +
sizeof(MmapTileHeader::mmapVersion) +
sizeof(MmapTileHeader::size) +
sizeof(MmapTileHeader::usesLiquids) +
sizeof(MmapTileHeader::padding)), "MmapTileHeader has uninitialized padding fields");
sizeof(MmapTileHeader::dtVersion) +
sizeof(MmapTileHeader::mmapVersion) +
sizeof(MmapTileHeader::size) +
sizeof(MmapTileHeader::usesLiquids) +
sizeof(MmapTileHeader::padding)), "MmapTileHeader has uninitialized padding fields");

enum NavArea
{
Expand Down
1 change: 1 addition & 0 deletions src/server/game/AI/ScriptedAI/ScriptedGossip.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum eTradeskill
TRADESKILL_SKINNING = 13,
TRADESKILL_JEWLCRAFTING = 14,
TRADESKILL_INSCRIPTION = 15,
TRADESKILL_LINGUISTICS = 16,

TRADESKILL_LEVEL_NONE = 0,
TRADESKILL_LEVEL_APPRENTICE = 1,
Expand Down
90 changes: 88 additions & 2 deletions src/server/game/Entities/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_grou
m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(),
m_creatureInfo(nullptr), m_creatureData(nullptr), m_stringIds(), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), m_assistCheckTime(0), _lastDamagedTime(0),
_regenerateHealth(true), _regenerateHealthLock(false), _isMissingCanSwimFlagOutOfCombat(false)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
Expand Down Expand Up @@ -804,6 +804,34 @@ void Creature::Update(uint32 diff)
m_boundaryCheckTime -= diff;
}

if (IsEngaged() && IsAlive() && !IsPet() && !IsCharmed() && !IsTotem() && !IsTrigger())
{
// Only check for creatures that can actually call for help
if (!GetCharmerOrOwnerGUID().IsPlayer())
{
if (diff >= m_assistCheckTime)
{
float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS);
if (radius > 0.0f)
{
// Use CallForHelp which directly engages nearby creatures
CallForHelp(radius);
}

// Set next assistance check time
m_assistCheckTime = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY);
}
else
m_assistCheckTime -= diff;
}
}
Comment on lines +807 to +827
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This periodic CallForHelp check in the Update loop could cause significant performance issues. The function is called every CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY milliseconds for every engaged creature, which performs radius-based grid searches. This is expensive when there are many creatures in combat. Consider whether this periodic assistance calling is necessary, given that CallAssistance() is already called in AtEngage() at line 3349.

Suggested change
if (IsEngaged() && IsAlive() && !IsPet() && !IsCharmed() && !IsTotem() && !IsTrigger())
{
// Only check for creatures that can actually call for help
if (!GetCharmerOrOwnerGUID().IsPlayer())
{
if (diff >= m_assistCheckTime)
{
float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS);
if (radius > 0.0f)
{
// Use CallForHelp which directly engages nearby creatures
CallForHelp(radius);
}
// Set next assistance check time
m_assistCheckTime = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY);
}
else
m_assistCheckTime -= diff;
}
}
// Removed periodic CallForHelp check for performance reasons. Assistance is now only called at engagement.

Copilot uses AI. Check for mistakes.
else
{
// Reset timer when not in combat
if (m_assistCheckTime > 0)
m_assistCheckTime = 0;
}

// if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
if (m_combatPulseDelay > 0 && IsEngaged() && GetMap()->IsDungeon())
{
Expand Down Expand Up @@ -2330,6 +2358,7 @@ void Creature::CallAssistance()

float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS);


Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Unnecessary extra blank line. There should only be one blank line here for consistency with the rest of the codebase.

Suggested change

Copilot uses AI. Check for mistakes.
if (radius > 0)
{
std::list<Creature*> assistList;
Expand Down Expand Up @@ -2420,8 +2449,49 @@ bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /
}

// skip non hostile to caster enemy creatures
// Modified: Allow assistance against enemies who are either hostile OR have attacked faction members
// This allows NPCs to defend against neutral players who initiate combat
if (!IsHostileTo(enemy))
return false;
{
// If enemy is a player and has recently damaged faction members, allow assistance
if (enemy->GetTypeId() == TYPEID_PLAYER)
{
// Check if the player is in combat with faction members
if (!enemy->IsInCombat())
return false;

// Additional check: ensure the enemy has actually attacked our faction
bool hasAttackedFaction = false;
if (Unit* victim = enemy->GetVictim())
{
if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->GetFaction() == GetFaction())
hasAttackedFaction = true;
}

// Also check combat manager for any faction targets
if (!hasAttackedFaction)
{
std::vector<Unit*> combatTargets;
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable combatTargets is declared but never used. It's created as an empty vector and then the code directly iterates over enemy->GetCombatManager().GetPvECombatRefs() without populating or using this vector. This unused variable should be removed.

Suggested change
std::vector<Unit*> combatTargets;

Copilot uses AI. Check for mistakes.
for (auto const& pair : enemy->GetCombatManager().GetPvECombatRefs())
{
if (Unit* target = pair.second->GetOther(enemy))
{
if (target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->GetFaction() == GetFaction())
{
hasAttackedFaction = true;
break;
}
}
}
}

if (!hasAttackedFaction)
return false;
}
else
// For non-player enemies, keep the original hostile check
return false;
}

return true;
}
Expand Down Expand Up @@ -3266,6 +3336,19 @@ void Creature::AtEngage(Unit* target)
}
}

// Initialize assistance check timer to trigger first check after 5 seconds
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "trigger first check after 5 seconds", but the actual delay is determined by CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY which may not be 5 seconds. The comment should either be generic (e.g., "Initialize assistance check timer based on config") or be updated to match the actual config value.

Suggested change
// Initialize assistance check timer to trigger first check after 5 seconds
// Initialize assistance check timer based on config value

Copilot uses AI. Check for mistakes.
if (!IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger())
{
m_assistCheckTime = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY);
}

// Call for assistance from nearby creatures
// Only do this for non-player controlled creatures in normal world content
if (!IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger())
{
Comment on lines +3340 to +3348
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated conditional check: Lines 3340 and 3347 contain the exact same complex condition !IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger(). These two blocks should be combined into a single if statement to improve code maintainability and readability.

Suggested change
if (!IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger())
{
m_assistCheckTime = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY);
}
// Call for assistance from nearby creatures
// Only do this for non-player controlled creatures in normal world content
if (!IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger())
{
// Call for assistance from nearby creatures
// Only do this for non-player controlled creatures in normal world content
if (!IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger())
{
m_assistCheckTime = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY);

Copilot uses AI. Check for mistakes.
CallAssistance();
}
Comment on lines +3347 to +3350
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CallAssistance() is being called here when a creature engages in combat. However, with the new periodic assistance check added in the Update() method (lines 807-833), creatures will continuously call for help throughout combat. This could result in duplicate or excessive assistance calls. Consider whether both the initial call here and the periodic calls in Update() are both necessary, or if one should be removed to avoid redundant behavior.

Suggested change
if (!IsPet() && !IsCharmed() && !GetCharmerOrOwnerGUID().IsPlayer() && !IsTotem() && !IsTrigger())
{
CallAssistance();
}
// Assistance is now handled by the periodic check in Update(), so no immediate call here.

Copilot uses AI. Check for mistakes.

if (CreatureAI* ai = AI())
ai->JustEngagedWith(target);
if (CreatureGroup* formation = GetFormation())
Expand All @@ -3280,6 +3363,9 @@ void Creature::AtDisengage()
if (IsAlive() && HasDynamicFlag(UNIT_DYNFLAG_TAPPED))
ReplaceAllDynamicFlags(GetCreatureTemplate()->dynamicflags);

// Reset assistance timer when leaving combat
m_assistCheckTime = 0;

if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
{
UpdateSpeed(MOVE_RUN);
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Entities/Creature/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
// Regenerate health
bool _regenerateHealth; // Set on creation
bool _regenerateHealthLock; // Dynamically set
uint32 m_assistCheckTime;

bool _isMissingCanSwimFlagOutOfCombat;
};
Expand Down
3 changes: 2 additions & 1 deletion src/server/game/Entities/Item/ItemTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ enum ItemSubclassRecipe
ITEM_SUBCLASS_ENCHANTING_FORMULA = 8,
ITEM_SUBCLASS_FISHING_MANUAL = 9,
ITEM_SUBCLASS_JEWELCRAFTING_RECIPE = 10,
ITEM_SUBCLASS_INSCRIPTION_TECHNIQUE = 11
ITEM_SUBCLASS_INSCRIPTION_TECHNIQUE = 11,
ITEM_SUBCLASS_LINGUISTICS_BOOK = 12
};

#define MAX_ITEM_SUBCLASS_RECIPE 12
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MAX_ITEM_SUBCLASS_RECIPE constant is incorrectly set to 12, but it should be 13 since the highest recipe subclass ID (ITEM_SUBCLASS_LINGUISTICS_BOOK) is now 12. The max constant should be one greater than the highest enum value to accommodate array sizing and iteration.

Suggested change
#define MAX_ITEM_SUBCLASS_RECIPE 12
#define MAX_ITEM_SUBCLASS_RECIPE 13

Copilot uses AI. Check for mistakes.
Expand Down
3 changes: 2 additions & 1 deletion src/server/game/Quests/QuestDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ enum QuestTradeSkill
QUEST_TRSKILL_MINING = 11,
QUEST_TRSKILL_FISHING = 12,
QUEST_TRSKILL_SKINNING = 13,
QUEST_TRSKILL_JEWELCRAFTING = 14
QUEST_TRSKILL_JEWELCRAFTING = 14,
QUEST_TRSKILL_LINGUISTICS = 15
};

enum QuestStatus : uint8
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Spells/SpellMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ bool IsPrimaryProfessionSkill(uint32 skill);

inline bool IsProfessionSkill(uint32 skill)
{
return IsPrimaryProfessionSkill(skill) || skill == SKILL_FISHING || skill == SKILL_COOKING || skill == SKILL_FIRST_AID;
return IsPrimaryProfessionSkill(skill) || skill == SKILL_FISHING || skill == SKILL_COOKING || skill == SKILL_FIRST_AID || skill == SKILL_LINGUISTICS;
}

inline bool IsProfessionOrRidingSkill(uint32 skill)
Expand Down
18 changes: 15 additions & 3 deletions src/server/shared/SharedDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,16 @@ enum SpellCustomErrors
SPELL_CUSTOM_ERROR_MAX_NUMBER_OF_RECRUITS = 96, // You already have the max number of recruits.
SPELL_CUSTOM_ERROR_MAX_NUMBER_OF_VOLUNTEERS = 97, // You already have the max number of volunteers.
SPELL_CUSTOM_ERROR_FROSTMOURNE_RENDERED_RESURRECT = 98, // Frostmourne has rendered you unable to resurrect.
SPELL_CUSTOM_ERROR_CANT_MOUNT_WITH_SHAPESHIFT = 99 // You can't mount while affected by that shapeshift.
SPELL_CUSTOM_ERROR_CANT_MOUNT_WITH_SHAPESHIFT = 99, // You can't mount while affected by that shapeshift.
SPELL_CUSTOM_ERROR_INTRO_QUEST_ACTIVE = 100,// You cannot use that until you've completed the introduction quest.
SPELL_CUSTOM_ERROR_STORMWIND_NEUTRAL = 101,// You must be at least Neutral with Stormwind to use that.
SPELL_CUSTOM_ERROR_IRONFORGEORGNOME_NEUTRAL = 102,// You must be at least Neutral with Ironforge or Gnomeregan Exiles to use that.
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming: IRONFORGEORGNOME should have underscores between the words for better readability, like IRONFORGE_OR_GNOME to match the naming pattern used elsewhere in the codebase.

Suggested change
SPELL_CUSTOM_ERROR_IRONFORGEORGNOME_NEUTRAL = 102,// You must be at least Neutral with Ironforge or Gnomeregan Exiles to use that.
SPELL_CUSTOM_ERROR_IRONFORGE_OR_GNOME_NEUTRAL = 102,// You must be at least Neutral with Ironforge or Gnomeregan Exiles to use that.

Copilot uses AI. Check for mistakes.
SPELL_CUSTOM_ERROR_DARNASSUS_NEUTRAL = 103,// You must be at least Neutral with Darnassus to use that.
SPELL_CUSTOM_ERROR_EXODAR_NEUTRAL = 104,// You must be at least Neutral with Exodar to use that.
SPELL_CUSTOM_ERROR_ORGRIMMARORDARKSPEAR_NEUTRAL = 105,// You must be at least Neutral with Orgrimmar or Darkspear Trolls to use that.
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming: ORGRIMMARORDARKSPEAR should have underscores between the words for better readability, like ORGRIMMAR_OR_DARKSPEAR to match the naming pattern used elsewhere in the codebase.

Suggested change
SPELL_CUSTOM_ERROR_ORGRIMMARORDARKSPEAR_NEUTRAL = 105,// You must be at least Neutral with Orgrimmar or Darkspear Trolls to use that.
SPELL_CUSTOM_ERROR_ORGRIMMAR_OR_DARKSPEAR_NEUTRAL = 105,// You must be at least Neutral with Orgrimmar or Darkspear Trolls to use that.

Copilot uses AI. Check for mistakes.
SPELL_CUSTOM_ERROR_THUNDERBLUFF_NEUTRAL = 106,// You must be at least Neutral with Thunder Bluff to use that.
SPELL_CUSTOM_ERROR_UNDERCITY_NEUTRAL = 107,// You must be at least Neutral with Undercity to use that.
SPELL_CUSTOM_ERROR_SILVERMOONCITY_NEUTRAL = 108 // You must be at least Neutral with Silvermoon City to use that.
};

enum StealthType
Expand Down Expand Up @@ -2925,7 +2934,8 @@ enum QuestSort
QUEST_SORT_JEWELCRAFTING = 373,
QUEST_SORT_NOBLEGARDEN = 374,
QUEST_SORT_PILGRIMS_BOUNTY = 375,
QUEST_SORT_LOVE_IS_IN_THE_AIR = 376
QUEST_SORT_LOVE_IS_IN_THE_AIR = 376,
QUEST_SORT_LINGUISTICS = 377
};

constexpr uint8 ClassByQuestSort(int32 QuestSort)
Expand Down Expand Up @@ -3099,7 +3109,8 @@ enum SkillType
SKILL_PET_WASP = 785,
SKILL_PET_EXOTIC_RHINO = 786,
SKILL_PET_EXOTIC_CORE_HOUND = 787,
SKILL_PET_EXOTIC_SPIRIT_BEAST = 788
SKILL_PET_EXOTIC_SPIRIT_BEAST = 788,
SKILL_LINGUISTICS = 789
};

#define MAX_SKILL_TYPE 789
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MAX_SKILL_TYPE constant is incorrectly set to 789, but it should be 790 since the highest skill ID (SKILL_LINGUISTICS) is now 789. The max constant should be one greater than the highest enum value to accommodate array sizing and iteration.

Suggested change
#define MAX_SKILL_TYPE 789
#define MAX_SKILL_TYPE 790

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -3133,6 +3144,7 @@ constexpr uint32 SkillByQuestSort(int32 QuestSort)
case QUEST_SORT_FIRST_AID: return SKILL_FIRST_AID;
case QUEST_SORT_JEWELCRAFTING: return SKILL_JEWELCRAFTING;
case QUEST_SORT_INSCRIPTION: return SKILL_INSCRIPTION;
case QUEST_SORT_LINGUISTICS: return SKILL_LINGUISTICS;
}
return 0;
}
Expand Down