Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2023-08-01 23:21:36 +08:00
10 changed files with 556 additions and 54 deletions

View File

@@ -2875,6 +2875,18 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
}
case SMART_ACTION_PLAY_SPELL_VISUAL:
{
for (WorldObject* target : targets)
{
if (IsUnit(target))
{
if (e.action.spellVisual.visualId)
target->ToUnit()->SendPlaySpellVisual(e.action.spellVisual.visualId);
}
}
break;
}
default:
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
@@ -3679,7 +3691,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
ProcessTimedAction(e, e.event.rangeRepeat.repeatMin, e.event.rangeRepeat.repeatMax, me->GetVictim());
}
else
RecalcTimer(e, 500, 500); // make it predictable
RecalcTimer(e, 1200, 1200); // make it predictable
break;
}
@@ -4240,7 +4252,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
playerCount++;
}
if (playerCount <= e.event.nearPlayerNegation.maxCount)
if (playerCount < e.event.nearPlayerNegation.maxCount)
ProcessAction(e, unit);
}
RecalcTimer(e, e.event.nearPlayerNegation.repeatMin, e.event.nearPlayerNegation.repeatMax);
@@ -4277,6 +4289,37 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer);
break;
}
case SMART_EVENT_NEAR_UNIT_NEGATION:
{
uint32 unitCount = 0;
ObjectVector targets;
GetWorldObjectsInDist(targets, static_cast<float>(e.event.nearUnitNegation.range));
if (!targets.empty())
{
if (e.event.nearUnitNegation.type)
{
for (WorldObject* target : targets)
{
if (IsGameObject(target) && target->GetEntry() == e.event.nearUnitNegation.entry)
unitCount++;
}
}
else
{
for (WorldObject* target : targets)
{
if (IsCreature(target) && target->GetEntry() == e.event.nearUnitNegation.entry)
unitCount++;
}
}
if (unitCount < e.event.nearUnitNegation.count)
ProcessAction(e, unit);
}
RecalcTimer(e, e.event.nearUnitNegation.timer, e.event.nearUnitNegation.timer);
break;
}
case SMART_EVENT_AREA_CASTING:
{
if (!me || !me->IsEngaged())
@@ -4288,25 +4331,45 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
{
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
{
if (!target || target->IsPet() || target->IsTotem() || !target->IsNonMeleeSpellCast(false, false, true))
continue;
if (e.event.areaCasting.range && !me->IsWithinDistInMap(target, range))
continue;
if (!target || !target->IsNonMeleeSpellCast(false, false, true))
ProcessAction(e, target);
RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax);
return;
}
// If no targets are found and it's off cooldown, check again in 1200ms
RecalcTimer(e, 1200, 1200);
break;
}
break;
}
case SMART_EVENT_AREA_RANGE:
{
if (!me || !me->IsEngaged())
return;
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
{
if (!(me->IsInRange(target, 0.f, (float)e.event.areaRange.range)))
continue;
if (e.event.areaCasting.spellId > 0)
if (Spell* currSpell = target->GetCurrentSpell(CURRENT_GENERIC_SPELL))
if (currSpell->m_spellInfo->Id != e.event.areaCasting.spellId)
continue;
ProcessAction(e, target);
RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMin);
RecalcTimer(e, e.event.areaRange.repeatMin, e.event.areaRange.repeatMax);
return;
}
}
// If no targets are found and it's off cooldown, check again
RecalcTimer(e, e.event.areaCasting.checkTimer, e.event.areaCasting.checkTimer);
RecalcTimer(e, 1200, 1200);
break;
}
default:
@@ -4325,7 +4388,10 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
if (e.event.rangeRepeat.onlyFireOnRepeat == 1)
e.event.rangeRepeat.onlyFireOnRepeat = 2;
// make it predictable
RecalcTimer(e, 500, 500);
RecalcTimer(e, 1200, 1200);
break;
case SMART_EVENT_AREA_RANGE:
RecalcTimer(e, e.event.areaRange.min, e.event.areaRange.max);
break;
case SMART_EVENT_NEAR_PLAYERS:
case SMART_EVENT_NEAR_PLAYERS_NEGATION:
@@ -4346,10 +4412,11 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
RecalcTimer(e, e.event.distance.repeat, e.event.distance.repeat);
break;
case SMART_EVENT_NEAR_UNIT:
case SMART_EVENT_NEAR_UNIT_NEGATION:
RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer);
break;
case SMART_EVENT_AREA_CASTING:
RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax);
RecalcTimer(e, e.event.areaCasting.min, e.event.areaCasting.max);
break;
default:
e.active = true;
@@ -4405,6 +4472,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
case SMART_EVENT_NEAR_PLAYERS:
case SMART_EVENT_NEAR_PLAYERS_NEGATION:
case SMART_EVENT_NEAR_UNIT:
case SMART_EVENT_NEAR_UNIT_NEGATION:
case SMART_EVENT_UPDATE:
case SMART_EVENT_UPDATE_OOC:
case SMART_EVENT_UPDATE_IC:
@@ -4413,6 +4481,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
case SMART_EVENT_MANA_PCT:
case SMART_EVENT_TARGET_MANA_PCT:
case SMART_EVENT_RANGE:
case SMART_EVENT_AREA_RANGE:
case SMART_EVENT_VICTIM_CASTING:
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_FRIENDLY_HEALTH:

View File

@@ -275,6 +275,10 @@ void SmartAIMgr::LoadSmartAIFromDB()
if (temp.event.rangeRepeat.onlyFireOnRepeat > 1)
temp.event.rangeRepeat.onlyFireOnRepeat = 1;
break;
case SMART_EVENT_AREA_RANGE:
if (temp.event.areaRange.repeatMin == 0 && temp.event.areaRange.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_VICTIM_CASTING:
case SMART_EVENT_IS_BEHIND_TARGET:
if (temp.event.minMaxRepeat.min == 0 && temp.event.minMaxRepeat.max == 0)
@@ -349,6 +353,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
case SMART_EVENT_TARGET_HEALTH_PCT:
case SMART_EVENT_TARGET_MANA_PCT:
case SMART_EVENT_RANGE:
case SMART_EVENT_AREA_RANGE:
case SMART_EVENT_VICTIM_CASTING:
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_TARGET_BUFFED:
@@ -579,7 +584,9 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_NEAR_PLAYERS: return sizeof(SmartEvent::nearPlayer);
case SMART_EVENT_NEAR_PLAYERS_NEGATION: return sizeof(SmartEvent::nearPlayerNegation);
case SMART_EVENT_NEAR_UNIT: return sizeof(SmartEvent::nearUnit);
case SMART_EVENT_NEAR_UNIT_NEGATION: return sizeof(SmartEvent::nearUnitNegation);
case SMART_EVENT_AREA_CASTING: return sizeof(SmartEvent::areaCasting);
case SMART_EVENT_AREA_RANGE: return sizeof(SmartEvent::areaRange);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an event {} with no unused params specified in SmartAIMgr::CheckUnusedEventParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetEventType());
@@ -773,6 +780,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_DISABLE: return sizeof(SmartAction::disable);
case SMART_ACTION_SET_SCALE: return sizeof(SmartAction::setScale);
case SMART_ACTION_SUMMON_RADIAL: return sizeof(SmartAction::radialSummon);
case SMART_ACTION_PLAY_SPELL_VISUAL: return sizeof(SmartAction::spellVisual);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -962,6 +970,14 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (!IsMinMaxValid(e, e.event.rangeRepeat.repeatMin, e.event.rangeRepeat.repeatMax))
return false;
break;
case SMART_EVENT_AREA_RANGE:
if (!IsMinMaxValid(e, e.event.areaRange.min, e.event.areaRange.max))
return false;
if (!IsMinMaxValid(e, e.event.areaRange.repeatMin, e.event.areaRange.repeatMax))
return false;
break;
case SMART_EVENT_SPELLHIT:
case SMART_EVENT_SPELLHIT_TARGET:
if (e.event.spellHit.spell)
@@ -1052,11 +1068,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
break;
case SMART_EVENT_AREA_CASTING:
if (e.event.areaCasting.spellId > 0 && !sSpellMgr->GetSpellInfo(e.event.areaCasting.spellId))
{
LOG_ERROR("scripts.ai.sai", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} uses non-existent Spell entry {}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell);
if (!IsMinMaxValid(e, e.event.areaCasting.min, e.event.areaCasting.max))
return false;
}
if (!IsMinMaxValid(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax))
return false;
@@ -1297,6 +1310,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_EVENT_GO_STATE_CHANGED:
case SMART_EVENT_GO_EVENT_INFORM:
case SMART_EVENT_NEAR_UNIT:
case SMART_EVENT_NEAR_UNIT_NEGATION:
case SMART_EVENT_TIMED_EVENT_TRIGGERED:
case SMART_EVENT_INSTANCE_PLAYER_ENTER:
case SMART_EVENT_TRANSPORT_RELOCATE:
@@ -1949,6 +1963,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_DISABLE:
case SMART_ACTION_SET_SCALE:
case SMART_ACTION_SUMMON_RADIAL:
case SMART_ACTION_PLAY_SPELL_VISUAL:
break;
default:
LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type({}), event_type({}), Entry {} SourceType {} Event {}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);

View File

@@ -209,9 +209,11 @@ enum SMART_EVENT
SMART_EVENT_NEAR_PLAYERS = 101, // min, radius, first timer, repeatMin, repeatMax
SMART_EVENT_NEAR_PLAYERS_NEGATION = 102, // max, radius, first timer, repeatMin, repeatMax
SMART_EVENT_NEAR_UNIT = 103, // type (0: creature 1: gob), entry, count, range, timer
SMART_EVENT_AREA_CASTING = 104, // spellId (0: any), range (0: any), repeatMin, repeatMax, checkTimer
SMART_EVENT_NEAR_UNIT_NEGATION = 104, // type (0: creature 1: gob), entry, count, range, timer
SMART_EVENT_AREA_CASTING = 105, // min, max, repeatMin, repeatMax, range
SMART_EVENT_AREA_RANGE = 106, // min, max, repeatMin, repeatMax, range
SMART_EVENT_AC_END = 105
SMART_EVENT_AC_END = 107
};
struct SmartEvent
@@ -508,13 +510,31 @@ struct SmartEvent
struct
{
uint32 spellId;
uint32 type;
uint32 entry;
uint32 count;
uint32 range;
uint32 timer;
} nearUnitNegation;
struct
{
uint32 min;
uint32 max;
uint32 repeatMin;
uint32 repeatMax;
uint32 checkTimer;
uint32 range;
} areaCasting;
struct
{
uint32 min;
uint32 max;
uint32 repeatMin;
uint32 repeatMax;
uint32 range;
} areaRange;
struct
{
uint32 param1;
@@ -718,8 +738,9 @@ enum SMART_ACTION
SMART_ACTION_DISABLE = 226, // state
SMART_ACTION_SET_SCALE = 227, // scale
SMART_ACTION_SUMMON_RADIAL = 228, // summonEntry, summonDuration, repetitions, startAngle, stepAngle, dist
SMART_ACTION_PLAY_SPELL_VISUAL = 229, // visualId, visualIdImpact
SMART_ACTION_AC_END = 229, // placeholder
SMART_ACTION_AC_END = 230, // placeholder
};
enum class SmartActionSummonCreatureFlags
@@ -1416,6 +1437,11 @@ struct SmartAction
uint32 stepAngle;
uint32 dist;
} radialSummon;
struct
{
uint32 visualId;
} spellVisual;
//! Note for any new future actions
//! All parameters must have type uint32
@@ -1820,7 +1846,9 @@ const uint32 SmartAIEventMask[SMART_EVENT_AC_END][2] =
{SMART_EVENT_NEAR_PLAYERS, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_NEAR_PLAYERS_NEGATION, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_NEAR_UNIT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_AREA_CASTING, SMART_SCRIPT_TYPE_MASK_CREATURE }
{SMART_EVENT_NEAR_UNIT_NEGATION, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_AREA_CASTING, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_AREA_RANGE, SMART_SCRIPT_TYPE_MASK_CREATURE }
};
enum SmartEventFlags

View File

@@ -752,8 +752,10 @@ struct ItemTemplate
[[nodiscard]] int32 getFeralBonus(int32 extraDPS = 0) const
{
constexpr uint32 feralApEnabledInventoryTypeMaks = 1 << INVTYPE_WEAPON | 1 << INVTYPE_2HWEAPON | 1 << INVTYPE_WEAPONMAINHAND | 1 << INVTYPE_WEAPONOFFHAND;
// 0x02A5F3 - is mask for Melee weapon from ItemSubClassMask.dbc
if (Class == ITEM_CLASS_WEAPON && (1 << SubClass) & 0x02A5F3)
if (Class == ITEM_CLASS_WEAPON && (1 << InventoryType) & feralApEnabledInventoryTypeMaks)
{
int32 bonus = int32((extraDPS + getDPS()) * 14.0f) - 767;
if (bonus < 0)

View File

@@ -325,6 +325,10 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
if (!go)
return;
// Prevent use of GameObject if it is not selectable. Fixes hack.
if (go->HasGameObjectFlag(GO_FLAG_NOT_SELECTABLE))
return;
if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;

View File

@@ -187,7 +187,7 @@ class spell_arcane_vacuum : public SpellScript
{
Unit* caster = GetCaster();
Unit* hitUnit = GetHitUnit();
if (caster && hitUnit && hitUnit->ToPlayer())
if (caster && hitUnit)
{
caster->GetThreatMgr().ModifyThreatByPercent(hitUnit, -100);
caster->CastSpell(hitUnit, SPELL_ARCANE_VACUUM_TP, true);

View File

@@ -2016,13 +2016,15 @@ public:
bool OnGossipHello(Player* player, Creature* creature) override
{
auto toggleXpCost = sWorld->getIntConfig(CONFIG_TOGGLE_XP_COST);
if (!player->HasPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN))
{
AddGossipItemFor(player, GOSSIP_MENU_EXP_NPC, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); // "I no longer wish to gain experience."
AddGossipItemFor(player, GOSSIP_MENU_EXP_NPC, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1, toggleXpCost); // "I no longer wish to gain experience."
}
else
{
AddGossipItemFor(player, GOSSIP_MENU_EXP_NPC, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); // "I wish to start gaining experience again."
AddGossipItemFor(player, GOSSIP_MENU_EXP_NPC, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2, toggleXpCost); // "I wish to start gaining experience again."
}
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature);
@@ -2031,43 +2033,29 @@ public:
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
bool noXPGain = player->HasPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
bool doSwitch = false;
auto toggleXpCost = sWorld->getIntConfig(CONFIG_TOGGLE_XP_COST);
ClearGossipMenuFor(player);
if (!player->HasEnoughMoney(toggleXpCost))
{
player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
player->PlayerTalkClass->SendCloseGossip();
return true;
}
player->ModifyMoney(-toggleXpCost);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1://xp off
{
if (!noXPGain)//does gain xp
doSwitch = true;//switch to don't gain xp
}
player->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
break;
case GOSSIP_ACTION_INFO_DEF + 2://xp on
{
if (noXPGain)//doesn't gain xp
doSwitch = true;//switch to gain xp
}
player->RemovePlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
break;
}
if (doSwitch)
{
if (!player->HasEnoughMoney(toggleXpCost))
{
player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
}
else if (noXPGain)
{
player->ModifyMoney(-toggleXpCost);
player->RemovePlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
}
else if (!noXPGain)
{
player->ModifyMoney(-toggleXpCost);
player->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
}
}
player->PlayerTalkClass->SendCloseGossip();
return true;
}