Core/Vehicle: Added RideSpellID field to override npc_spellclick_spells (#30198)

This commit is contained in:
ModoX
2025-02-02 00:42:51 +01:00
committed by GitHub
parent fa75f796d5
commit 46251b5655
7 changed files with 131 additions and 78 deletions
@@ -0,0 +1,2 @@
ALTER TABLE `vehicle_template_accessory` ADD COLUMN `RideSpellID` int NULL AFTER `summontimer`;
ALTER TABLE `vehicle_accessory` ADD COLUMN `RideSpellID` int NULL AFTER `summontimer`;
+87 -66
View File
@@ -12413,74 +12413,9 @@ void Unit::HandleSpellClick(Unit* clicker, int8 seatId /*= -1*/)
if (!sConditionMgr->IsObjectMeetingSpellClickConditions(spellClickEntry, clickPair.second.spellId, clicker, this))
continue;
Unit* caster = (clickPair.second.castFlags & NPC_CLICK_CAST_CASTER_CLICKER) ? clicker : this;
Unit* target = (clickPair.second.castFlags & NPC_CLICK_CAST_TARGET_CLICKER) ? clicker : this;
ObjectGuid origCasterGUID = (clickPair.second.castFlags & NPC_CLICK_CAST_ORIG_CASTER_OWNER) ? GetOwnerGUID() : clicker->GetGUID();
spellClickHandled = HandleSpellClick(clicker, seatId, clickPair.second.spellId, flags, &clickPair.second);
SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(clickPair.second.spellId, caster->GetMap()->GetDifficultyID());
// if (!spellEntry) should be checked at npc_spellclick load
SpellCastResult castResult = SPELL_FAILED_SUCCESS;
if (seatId > -1)
{
uint8 i = 0;
bool valid = false;
for (SpellEffectInfo const& spellEffectInfo : spellEntry->GetEffects())
{
if (spellEffectInfo.ApplyAuraName == SPELL_AURA_CONTROL_VEHICLE)
{
valid = true;
break;
}
++i;
}
if (!valid)
{
TC_LOG_ERROR("sql.sql", "Spell {} specified in npc_spellclick_spells is not a valid vehicle enter aura!", clickPair.second.spellId);
continue;
}
if (IsInMap(caster))
{
CastSpellExtraArgs args(flags);
args.OriginalCaster = origCasterGUID;
args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), seatId + 1);
castResult = caster->CastSpell(target, clickPair.second.spellId, args);
}
else // This can happen during Player::_LoadAuras
{
int32 bp[MAX_SPELL_EFFECTS] = { };
for (SpellEffectInfo const& spellEffectInfo : spellEntry->GetEffects())
bp[spellEffectInfo.EffectIndex] = int32(spellEffectInfo.BasePoints);
bp[i] = seatId;
AuraCreateInfo createInfo(ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), spellEntry, GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, this);
createInfo
.SetCaster(clicker)
.SetBaseAmount(bp)
.SetCasterGUID(origCasterGUID);
Aura::TryRefreshStackOrCreate(createInfo);
}
}
else
{
if (IsInMap(caster))
castResult = caster->CastSpell(target, spellEntry->Id, CastSpellExtraArgs().SetOriginalCaster(origCasterGUID));
else
{
AuraCreateInfo createInfo(ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), spellEntry, GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, this);
createInfo
.SetCaster(clicker)
.SetCasterGUID(origCasterGUID);
Aura::TryRefreshStackOrCreate(createInfo);
}
}
spellClickHandled = castResult == SPELL_FAILED_SUCCESS;
}
Creature* creature = ToCreature();
@@ -12488,6 +12423,92 @@ void Unit::HandleSpellClick(Unit* clicker, int8 seatId /*= -1*/)
creature->AI()->OnSpellClick(clicker, spellClickHandled);
}
bool Unit::HandleSpellClick(Unit* clicker, int8 seatId, uint32 spellId, TriggerCastFlags flags /*= TRIGGERED_NONE*/, SpellClickInfo const* spellClickInfo /*= nullptr*/)
{
Unit* caster = clicker;
Unit* target = this;
ObjectGuid origCasterGUID = caster->GetGUID();
SpellCastResult castResult = SPELL_FAILED_SUCCESS;
if (spellClickInfo)
{
caster = (spellClickInfo->castFlags & NPC_CLICK_CAST_CASTER_CLICKER) ? clicker : this;
target = (spellClickInfo->castFlags & NPC_CLICK_CAST_TARGET_CLICKER) ? clicker : this;
origCasterGUID = (spellClickInfo->castFlags & NPC_CLICK_CAST_ORIG_CASTER_OWNER) ? GetOwnerGUID() : clicker->GetGUID();
}
if (!spellId)
{
TC_LOG_ERROR("sql.sql", "No valid spell specified for clickee {} and clicker {}!", target->GetGUID(), caster->GetGUID());
return false;
}
SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(spellId, caster->GetMap()->GetDifficultyID());
uint8 effectIndex = 0;
bool hasControlVehicleAura = false;
for (SpellEffectInfo const& spellEffectInfo : spellEntry->GetEffects())
{
if (spellEffectInfo.ApplyAuraName == SPELL_AURA_CONTROL_VEHICLE)
{
hasControlVehicleAura = true;
break;
}
++effectIndex;
}
if (seatId > -1)
{
if (!hasControlVehicleAura)
{
if (!spellClickInfo)
TC_LOG_ERROR("sql.sql", "RideSpell {} specified in vehicle_accessory or vehicle_template_accessory is not a valid vehicle enter aura!", spellId);
else
TC_LOG_ERROR("sql.sql", "Spell {} specified in npc_spellclick_spells is not a valid vehicle enter aura!", spellId);
return false;
}
if (IsInMap(caster))
{
CastSpellExtraArgs args(flags);
args.OriginalCaster = origCasterGUID;
args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + effectIndex), seatId + 1);
castResult = caster->CastSpell(target, spellId, args);
}
else // This can happen during Player::_LoadAuras
{
int32 bp[MAX_SPELL_EFFECTS] = { };
for (SpellEffectInfo const& spellEffectInfo : spellEntry->GetEffects())
bp[spellEffectInfo.EffectIndex] = int32(spellEffectInfo.BasePoints);
bp[effectIndex] = seatId;
AuraCreateInfo createInfo(ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), spellEntry, GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, this);
createInfo
.SetCaster(clicker)
.SetBaseAmount(bp)
.SetCasterGUID(origCasterGUID);
Aura::TryRefreshStackOrCreate(createInfo);
}
}
else
{
if (IsInMap(caster))
castResult = caster->CastSpell(target, spellEntry->Id, CastSpellExtraArgs().SetOriginalCaster(origCasterGUID));
else
{
AuraCreateInfo createInfo(ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), spellEntry, GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, this);
createInfo
.SetCaster(clicker)
.SetCasterGUID(origCasterGUID);
Aura::TryRefreshStackOrCreate(createInfo);
}
}
return castResult == SPELL_FAILED_SUCCESS;
}
void Unit::EnterVehicle(Unit* base, int8 seatId /*= -1*/)
{
CastSpellExtraArgs args(TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE);
+2
View File
@@ -70,6 +70,7 @@ struct FactionTemplateEntry;
struct LiquidData;
struct LiquidTypeEntry;
struct MountCapabilityEntry;
struct SpellClickInfo;
struct SpellValue;
struct TeleportLocation;
@@ -1751,6 +1752,7 @@ class TC_GAME_API Unit : public WorldObject
TransportBase* GetDirectTransport() const;
void HandleSpellClick(Unit* clicker, int8 seatId = -1);
bool HandleSpellClick(Unit* clicker, int8 seatId, uint32 spellId, TriggerCastFlags flags = TRIGGERED_NONE, SpellClickInfo const* spellClickInfo = nullptr);
void EnterVehicle(Unit* base, int8 seatId = -1);
virtual void ExitVehicle(Position const* exitPosition = nullptr);
void ChangeSeat(int8 seatId, bool next = true);
+6 -3
View File
@@ -93,7 +93,7 @@ void Vehicle::InstallAllAccessories(bool evading)
for (VehicleAccessoryList::const_iterator itr = accessories->begin(); itr != accessories->end(); ++itr)
if (!evading || itr->IsMinion) // only install minions on evade mode
InstallAccessory(itr->AccessoryEntry, itr->SeatId, itr->IsMinion, itr->SummonedType, itr->SummonTime);
InstallAccessory(itr->AccessoryEntry, itr->SeatId, itr->IsMinion, itr->SummonedType, itr->SummonTime, itr->RideSpellID);
}
/**
@@ -378,7 +378,7 @@ VehicleSeatAddon const* Vehicle::GetSeatAddonForSeatOfPassenger(Unit const* pass
* @param summonTime Time after which the minion is despawned in case of a timed despawn @type specified.
*/
void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type, uint32 summonTime)
void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type, uint32 summonTime, Optional<uint32> rideSpellId /*= {}*/)
{
/// @Prevent adding accessories when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.)
if (_status == STATUS_UNINSTALLING)
@@ -398,7 +398,10 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 typ
if (minion)
accessory->AddUnitTypeMask(UNIT_MASK_ACCESSORY);
_me->HandleSpellClick(accessory, seatId);
if (rideSpellId)
_me->HandleSpellClick(accessory, seatId, *rideSpellId);
else
_me->HandleSpellClick(accessory, seatId);
/// If for some reason adding accessory to vehicle fails it will unsummon in
/// @VehicleJoinEvent::Abort
+1 -1
View File
@@ -44,7 +44,7 @@ class TC_GAME_API Vehicle final : public TransportBase
void Reset(bool evading = false);
void InstallAllAccessories(bool evading);
void ApplyAllImmunities();
void InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type, uint32 summonTime); // May be called from scripts
void InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type, uint32 summonTime, Optional<uint32> rideSpellId = {}); // May be called from scripts
Unit* GetBase() const { return _me; }
VehicleEntry const* GetVehicleInfo() const { return _vehicleInfo; }
@@ -131,13 +131,14 @@ struct VehicleSeat
struct VehicleAccessory
{
VehicleAccessory(uint32 entry, int8 seatId, bool isMinion, uint8 summonType, uint32 summonTime) :
AccessoryEntry(entry), IsMinion(isMinion), SummonTime(summonTime), SeatId(seatId), SummonedType(summonType) { }
VehicleAccessory(uint32 entry, int8 seatId, bool isMinion, uint8 summonType, uint32 summonTime, Optional<uint32> rideSpellID) :
AccessoryEntry(entry), IsMinion(isMinion), SummonTime(summonTime), SeatId(seatId), SummonedType(summonType), RideSpellID(rideSpellID) { }
uint32 AccessoryEntry;
bool IsMinion;
uint32 SummonTime;
int8 SeatId;
uint8 SummonedType;
Optional<uint32> RideSpellID;
};
struct VehicleTemplate
+30 -6
View File
@@ -3462,8 +3462,8 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
uint32 count = 0;
// 0 1 2 3 4 5
QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
// 0 1 2 3 4 5 6
QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_template_accessory`");
if (!result)
{
@@ -3482,6 +3482,18 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
uint8 summonType = fields[4].GetUInt8();
uint32 summonTimer = fields[5].GetUInt32();
Optional<uint32> rideSpellId;
if (!fields[6].IsNull())
{
rideSpellId = fields[6].GetUInt32();
if (!sSpellMgr->GetSpellInfo(*rideSpellId, DIFFICULTY_NONE))
{
TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: rideSpellId {} does not exist for entry {}.", *rideSpellId, entry);
continue;
}
}
if (!GetCreatureTemplate(entry))
{
TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", entry);
@@ -3500,7 +3512,7 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
continue;
}
_vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
_vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer, rideSpellId));
++count;
}
@@ -3552,8 +3564,8 @@ void ObjectMgr::LoadVehicleAccessories()
uint32 count = 0;
// 0 1 2 3 4 5
QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
// 0 1 2 3 4 5 6
QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_accessory`");
if (!result)
{
@@ -3572,13 +3584,25 @@ void ObjectMgr::LoadVehicleAccessories()
uint8 uiSummonType = fields[4].GetUInt8();
uint32 uiSummonTimer= fields[5].GetUInt32();
Optional<uint32> rideSpellId;
if (!fields[6].IsNull())
{
rideSpellId = fields[6].GetUInt32();
if (!sSpellMgr->GetSpellInfo(*rideSpellId, DIFFICULTY_NONE))
{
TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: rideSpellId {} does not exist for guid {}.", *rideSpellId, uiGUID);
continue;
}
}
if (!GetCreatureTemplate(uiAccessory))
{
TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
continue;
}
_vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
_vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer, rideSpellId));
++count;
}