Merge branch 'master' into Playerbot
This commit is contained in:
@@ -62,6 +62,17 @@ void CreatureAI::Talk(uint8 id, WorldObject const* target /*= nullptr*/, Millise
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the summoner creature/object, if the creature is a temporary summon.
|
||||
*/
|
||||
WorldObject* CreatureAI::GetSummoner() const
|
||||
{
|
||||
if (TempSummon* summon = me->ToTempSummon())
|
||||
return summon->GetSummoner();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool IsValidCombatTarget(Creature* source, Player* target)
|
||||
{
|
||||
if (target->IsGameMaster())
|
||||
|
||||
@@ -96,6 +96,8 @@ public:
|
||||
void Talk(uint8 id, WorldObject const* whisperTarget = nullptr, Milliseconds delay = 0s);
|
||||
void Talk(uint8 id, Milliseconds delay) { Talk(id, nullptr, delay); }
|
||||
|
||||
WorldObject* GetSummoner() const;
|
||||
|
||||
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), _negateBoundary(false), m_MoveInLineOfSight_locked(false) { }
|
||||
|
||||
~CreatureAI() override {}
|
||||
|
||||
@@ -624,6 +624,9 @@ void BossAI::_Reset()
|
||||
if (!me->IsAlive())
|
||||
return;
|
||||
|
||||
if (me->IsEngaged())
|
||||
return;
|
||||
|
||||
me->SetCombatPulseDelay(0);
|
||||
me->ResetLootMode();
|
||||
events.Reset();
|
||||
|
||||
@@ -4231,6 +4231,18 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, victim);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE:
|
||||
{
|
||||
if (!me)
|
||||
return;
|
||||
|
||||
if (Unit* victim = me->GetVictim())
|
||||
if ((!e.event.meleeRange.invert && me->IsWithinMeleeRange(victim, static_cast<float>(e.event.meleeRange.dist))) ||
|
||||
(e.event.meleeRange.invert && !me->IsWithinMeleeRange(victim, static_cast<float>(e.event.meleeRange.dist))))
|
||||
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, victim);
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_RECEIVE_EMOTE:
|
||||
@@ -4793,6 +4805,7 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
|
||||
case SMART_EVENT_AREA_CASTING:
|
||||
case SMART_EVENT_IS_BEHIND_TARGET:
|
||||
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE:
|
||||
RecalcTimer(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max);
|
||||
break;
|
||||
case SMART_EVENT_DISTANCE_CREATURE:
|
||||
@@ -4878,6 +4891,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
|
||||
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
|
||||
case SMART_EVENT_DISTANCE_CREATURE:
|
||||
case SMART_EVENT_DISTANCE_GAMEOBJECT:
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE:
|
||||
{
|
||||
ASSERT(executionStack.empty());
|
||||
executionStack.emplace_back(SmartScriptFrame{ e, nullptr, 0, 0, false, nullptr, nullptr });
|
||||
|
||||
@@ -268,6 +268,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
|
||||
case SMART_EVENT_AREA_RANGE:
|
||||
case SMART_EVENT_AREA_CASTING:
|
||||
case SMART_EVENT_IS_BEHIND_TARGET:
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE:
|
||||
if (temp.event.minMaxRepeat.repeatMin == 0 && temp.event.minMaxRepeat.repeatMax == 0)
|
||||
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
|
||||
break;
|
||||
@@ -295,10 +296,89 @@ void SmartAIMgr::LoadSmartAIFromDB()
|
||||
mEventMap[source_type][temp.entryOrGuid].push_back(temp);
|
||||
} while (result->NextRow());
|
||||
|
||||
CheckIfSmartAIInDatabaseExists();
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} SmartAI scripts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void SmartAIMgr::CheckIfSmartAIInDatabaseExists()
|
||||
{
|
||||
// SMART_SCRIPT_TYPE_CREATURE
|
||||
for (auto const& [entry, creatureTemplate] : *sObjectMgr->GetCreatureTemplates())
|
||||
{
|
||||
if (creatureTemplate.AIName != "SmartAI")
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
|
||||
// check template SAI
|
||||
if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].find(creatureTemplate.Entry) != mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].end())
|
||||
found = true;
|
||||
else
|
||||
{
|
||||
// check GUID SAI
|
||||
for (auto const& pair : sObjectMgr->GetAllCreatureData())
|
||||
{
|
||||
if (pair.second.id1 != creatureTemplate.Entry)
|
||||
continue;
|
||||
|
||||
if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].find((-1) * pair.first) != mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].end())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
LOG_ERROR("sql.sql", "Creature entry ({}) has SmartAI enabled but no SmartAI entries in the database.", creatureTemplate.Entry);
|
||||
}
|
||||
|
||||
// SMART_SCRIPT_TYPE_GAMEOBJECT
|
||||
for (auto const& [entry, gameobjectTemplate] : *sObjectMgr->GetGameObjectTemplates())
|
||||
{
|
||||
if (gameobjectTemplate.AIName != "SmartGameObjectAI")
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
|
||||
// check template SAI
|
||||
if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_GAMEOBJECT)].find(gameobjectTemplate.entry) != mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_GAMEOBJECT)].end())
|
||||
found = true;
|
||||
else
|
||||
{
|
||||
// check GUID SAI
|
||||
for (auto const& pair : sObjectMgr->GetAllGOData())
|
||||
{
|
||||
if (pair.second.id != gameobjectTemplate.entry)
|
||||
continue;
|
||||
|
||||
if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_GAMEOBJECT)].find((-1) * pair.first) != mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_GAMEOBJECT)].end())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
LOG_ERROR("sql.sql", "Gameobject entry ({}) has SmartGameobjectAI enabled but no SmartAI entries in the database.", gameobjectTemplate.entry);
|
||||
}
|
||||
|
||||
// SMART_SCRIPT_TYPE_AREATRIGGER
|
||||
uint32 scriptID = sObjectMgr->GetScriptId("SmartTrigger");
|
||||
|
||||
for (auto const& pair : sObjectMgr->GetAllAreaTriggerScriptData())
|
||||
{
|
||||
if (pair.second != scriptID)
|
||||
continue;
|
||||
|
||||
if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_AREATRIGGER)].find(pair.first) == mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_AREATRIGGER)].end())
|
||||
LOG_ERROR("sql.sql", "AreaTrigger entry ({}) has SmartTrigger enabled but no SmartAI entries in the database.", pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool SmartAIMgr::EventHasInvoker(SMART_EVENT event)
|
||||
{
|
||||
switch (event)
|
||||
@@ -347,6 +427,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
|
||||
case SMART_EVENT_NEAR_PLAYERS:
|
||||
case SMART_EVENT_SUMMONED_UNIT_EVADE:
|
||||
case SMART_EVENT_DATA_SET:
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -553,6 +634,7 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
|
||||
case SMART_EVENT_FOLLOW_COMPLETED: return NO_PARAMS;
|
||||
case SMART_EVENT_EVENT_PHASE_CHANGE: return sizeof(SmartEvent::eventPhaseChange);
|
||||
case SMART_EVENT_IS_BEHIND_TARGET: return sizeof(SmartEvent::minMaxRepeat);
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE: return sizeof(SmartEvent::meleeRange);
|
||||
case SMART_EVENT_GAME_EVENT_START: return sizeof(SmartEvent::gameEvent);
|
||||
case SMART_EVENT_GAME_EVENT_END: return sizeof(SmartEvent::gameEvent);
|
||||
case SMART_EVENT_GO_STATE_CHANGED: return sizeof(SmartEvent::goStateChanged);
|
||||
@@ -964,6 +1046,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_EVENT_AREA_CASTING:
|
||||
case SMART_EVENT_IS_BEHIND_TARGET:
|
||||
case SMART_EVENT_RANGE:
|
||||
case SMART_EVENT_IS_IN_MELEE_RANGE:
|
||||
if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -215,8 +215,9 @@ enum SMART_EVENT
|
||||
SMART_EVENT_SUMMONED_UNIT_EVADE = 107, // CreatureId(0 all), CooldownMin, CooldownMax
|
||||
SMART_EVENT_WAYPOINT_DATA_REACHED = 108, // PointId (0: any), pathId (0: any)
|
||||
SMART_EVENT_WAYPOINT_DATA_ENDED = 109, // PointId (0: any), pathId (0: any)
|
||||
SMART_EVENT_IS_IN_MELEE_RANGE = 110, // min, max, repeatMin, repeatMax, dist, invert (0: false, 1: true)
|
||||
|
||||
SMART_EVENT_AC_END = 110
|
||||
SMART_EVENT_AC_END = 111
|
||||
};
|
||||
|
||||
struct SmartEvent
|
||||
@@ -499,6 +500,16 @@ struct SmartEvent
|
||||
uint32 timer;
|
||||
} nearUnit;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 min;
|
||||
uint32 max;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
uint32 dist;
|
||||
uint32 invert;
|
||||
} meleeRange;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 type;
|
||||
@@ -1890,6 +1901,7 @@ const uint32 SmartAIEventMask[SMART_EVENT_AC_END][2] =
|
||||
{SMART_EVENT_SUMMONED_UNIT_EVADE, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
|
||||
{SMART_EVENT_WAYPOINT_DATA_REACHED, SMART_SCRIPT_TYPE_MASK_CREATURE },
|
||||
{SMART_EVENT_WAYPOINT_DATA_ENDED, SMART_SCRIPT_TYPE_MASK_CREATURE },
|
||||
{SMART_EVENT_IS_IN_MELEE_RANGE, SMART_SCRIPT_TYPE_MASK_CREATURE },
|
||||
};
|
||||
|
||||
enum SmartEventFlags
|
||||
@@ -2048,6 +2060,7 @@ public:
|
||||
static SmartAIMgr* instance();
|
||||
|
||||
void LoadSmartAIFromDB();
|
||||
void CheckIfSmartAIInDatabaseExists();
|
||||
|
||||
SmartAIEventList GetScript(int32 entry, SmartScriptType type)
|
||||
{
|
||||
|
||||
@@ -217,7 +217,7 @@ void HostileReference::setOnlineOfflineState(bool isOnline)
|
||||
{
|
||||
iOnline = isOnline;
|
||||
|
||||
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this);
|
||||
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this, isOnline);
|
||||
fireStatusChanged(event);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ void HostileReference::removeReference()
|
||||
{
|
||||
invalidate();
|
||||
|
||||
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this);
|
||||
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this, false);
|
||||
fireStatusChanged(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -2095,6 +2095,7 @@ void Creature::Respawn(bool force)
|
||||
loot.clear();
|
||||
SelectLevel();
|
||||
|
||||
m_respawnedTime = GameTime::GetGameTime().count();
|
||||
setDeathState(DeathState::JustRespawned);
|
||||
|
||||
// MDic - Acidmanifesto: Do not override transform auras
|
||||
@@ -2126,7 +2127,6 @@ void Creature::Respawn(bool force)
|
||||
//Re-initialize reactstate that could be altered by movementgenerators
|
||||
InitializeReactState();
|
||||
|
||||
m_respawnedTime = GameTime::GetGameTime().count();
|
||||
}
|
||||
m_respawnedTime = GameTime::GetGameTime().count();
|
||||
// xinef: relocate notifier, fixes npc appearing in corpse position after forced respawn (instead of spawn)
|
||||
|
||||
@@ -2928,8 +2928,11 @@ bool Player::addTalent(uint32 spellId, uint8 addSpecMask, uint8 oldTalentRank)
|
||||
newTalent->specMask = addSpecMask;
|
||||
newTalent->talentID = talentInfo->TalentID;
|
||||
newTalent->inSpellBook = talentInfo->addToSpellBook && !spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL);
|
||||
|
||||
m_talents[spellId] = newTalent;
|
||||
|
||||
if (GetActiveSpecMask() & addSpecMask)
|
||||
m_usedTalentCount += (talentPos->rank + 1) - oldTalentRank;
|
||||
|
||||
return true;
|
||||
}
|
||||
// xinef: if current mask does not cover addMask, add it to iterator and save changes to DB
|
||||
@@ -2939,6 +2942,9 @@ bool Player::addTalent(uint32 spellId, uint8 addSpecMask, uint8 oldTalentRank)
|
||||
if (itr->second->State != PLAYERSPELL_NEW)
|
||||
itr->second->State = PLAYERSPELL_CHANGED;
|
||||
|
||||
if (GetActiveSpecMask() & addSpecMask)
|
||||
m_usedTalentCount += (talentPos->rank + 1) - oldTalentRank;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14078,9 +14084,6 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank, bool command /*= fa
|
||||
|
||||
addTalent(spellId, GetActiveSpecMask(), currentTalentRank);
|
||||
|
||||
// xinef: update free talent points count
|
||||
m_usedTalentCount += talentPointsChange;
|
||||
|
||||
if (!command)
|
||||
{
|
||||
SetFreeTalentPoints(CurTalentPoints - talentPointsChange);
|
||||
@@ -15032,9 +15035,6 @@ void Player::_LoadTalents(PreparedQueryResult result)
|
||||
TalentSpellPos const* talentPos = GetTalentSpellPos(spellId);
|
||||
ASSERT(talentPos);
|
||||
|
||||
// xinef: increase used talent points count
|
||||
if (GetActiveSpecMask() & specMask)
|
||||
m_usedTalentCount += talentPos->rank + 1;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,6 +885,7 @@ public:
|
||||
[[nodiscard]] AreaTriggerTeleport const* GetGoBackTrigger(uint32 Map) const;
|
||||
[[nodiscard]] AreaTriggerTeleport const* GetMapEntranceTrigger(uint32 Map) const;
|
||||
|
||||
[[nodiscard]] AreaTriggerScriptContainer const& GetAllAreaTriggerScriptData() const { return _areaTriggerScriptStore; }
|
||||
uint32 GetAreaTriggerScriptId(uint32 trigger_id);
|
||||
SpellScriptsBounds GetSpellScriptsBounds(uint32 spell_id);
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ void Graveyard::LoadGraveyardFromDB()
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 ID = fields[0].Get<uint32>();
|
||||
|
||||
GraveyardStruct Graveyard;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
Graveyard.ID = fields[0].Get<uint32>();
|
||||
Graveyard.Map = fields[1].Get<uint32>();
|
||||
Graveyard.x = fields[2].Get<float>();
|
||||
Graveyard.y = fields[3].Get<float>();
|
||||
@@ -59,13 +59,13 @@ void Graveyard::LoadGraveyardFromDB()
|
||||
|
||||
if (!Utf8toWStr(Graveyard.name, Graveyard.wnameLow))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_graveyard` table, ignoring.", ID);
|
||||
LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_graveyard` table, ignoring.", Graveyard.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
wstrToLower(Graveyard.wnameLow);
|
||||
|
||||
_graveyardStore[ID] = Graveyard;
|
||||
_graveyardStore[Graveyard.ID] = std::move(Graveyard);
|
||||
|
||||
++Count;
|
||||
} while (result->NextRow());
|
||||
|
||||
@@ -476,7 +476,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
|
||||
OpcodeHandler const* handler = opcodeTable[opcode];
|
||||
if (!handler)
|
||||
{
|
||||
LOG_ERROR("network.opcode", "No defined handler for opcode {} sent by {}", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet.GetOpcode())), _worldSession->GetPlayerInfo());
|
||||
LOG_ERROR("network.opcode", "No defined handler for opcode {} sent by {}", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packetToQueue->GetOpcode())), _worldSession->GetPlayerInfo());
|
||||
delete packetToQueue;
|
||||
return ReadDataHandlerResult::Error;
|
||||
}
|
||||
|
||||
@@ -1870,7 +1870,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta
|
||||
return SPELL_FAILED_TARGETS_DEAD;
|
||||
|
||||
// check this flag only for implicit targets (chain and area), allow to explicitly target units for spells like Shield of Righteousness
|
||||
if (implicit && AttributesEx6 & SPELL_ATTR6_DO_NOT_CHAIN_TO_CROWD_CONTROLLED_TARGETS && !unitTarget->CanFreeMove())
|
||||
if (implicit && AttributesEx6 & SPELL_ATTR6_DO_NOT_CHAIN_TO_CROWD_CONTROLLED_TARGETS && unitTarget->HasUnitState(UNIT_STATE_CONTROLLED))
|
||||
return SPELL_FAILED_BAD_TARGETS;
|
||||
|
||||
// checked in Unit::IsValidAttack/AssistTarget, shouldn't be checked for ENTRY targets
|
||||
|
||||
Reference in New Issue
Block a user