mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-16 01:01:30 +00:00
[Feature] Change #scribespells to be aware of spellgroups & ranks (#2501)
* Change #scribespells to be aware of spellgroups & ranks * Formatting * Fix Formatting, and change stored return data type to match function return type. * Compact If Statements * Implemented SQL Query to reduce number of iterations required. * Cleaned up Query, and improved performance * Cleaned up SQL Queries * Formatting * Indenting fix. * Update client.cpp * Fix Formatting in spells.cpp * Fix ValueWithin. Co-authored-by: Kinglykrab <kinglykrab@gmail.com> Co-authored-by: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com>
This commit is contained in:
parent
e01ac39887
commit
33b95c42c2
@ -10513,6 +10513,8 @@ std::vector<int> Client::GetMemmedSpells() {
|
||||
|
||||
std::vector<int> Client::GetScribeableSpells(uint8 min_level, uint8 max_level) {
|
||||
std::vector<int> scribeable_spells;
|
||||
std::unordered_map<uint32, std::vector<uint16>> spell_group_cache = LoadSpellGroupCache(min_level, max_level);
|
||||
|
||||
for (uint16 spell_id = 0; spell_id < SPDAT_RECORDS; ++spell_id) {
|
||||
bool scribeable = true;
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
@ -10547,13 +10549,33 @@ std::vector<int> Client::GetScribeableSpells(uint8 min_level, uint8 max_level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RuleB(Spells, EnableSpellGlobals) && !SpellGlobalCheck(spell_id, CharacterID())) {
|
||||
if (
|
||||
RuleB(Spells, EnableSpellGlobals) &&
|
||||
!SpellGlobalCheck(spell_id, CharacterID())
|
||||
) {
|
||||
scribeable = false;
|
||||
} else if (RuleB(Spells, EnableSpellBuckets) && !SpellBucketCheck(spell_id, CharacterID())) {
|
||||
} else if (
|
||||
RuleB(Spells, EnableSpellBuckets) &&
|
||||
!SpellBucketCheck(spell_id, CharacterID())
|
||||
) {
|
||||
scribeable = false;
|
||||
}
|
||||
|
||||
if (scribeable) {
|
||||
if (spells[spell_id].spell_group) {
|
||||
const auto& g = spell_group_cache.find(spells[spell_id].spell_group);
|
||||
if (g != spell_group_cache.end()) {
|
||||
for (const auto& s : g->second) {
|
||||
if (
|
||||
EQ::ValueWithin(spells[s].classes[m_pp.class_ - 1], min_level, max_level) &&
|
||||
s == spell_id &&
|
||||
scribeable
|
||||
) {
|
||||
scribeable_spells.push_back(spell_id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (scribeable) {
|
||||
scribeable_spells.push_back(spell_id);
|
||||
}
|
||||
}
|
||||
@ -10562,7 +10584,7 @@ std::vector<int> Client::GetScribeableSpells(uint8 min_level, uint8 max_level) {
|
||||
|
||||
std::vector<int> Client::GetScribedSpells() {
|
||||
std::vector<int> scribed_spells;
|
||||
for(int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) {
|
||||
for (int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) {
|
||||
if (IsValidSpell(m_pp.spell_book[index])) {
|
||||
scribed_spells.push_back(m_pp.spell_book[index]);
|
||||
}
|
||||
|
||||
@ -1062,6 +1062,7 @@ public:
|
||||
inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; }
|
||||
inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; }
|
||||
uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group);
|
||||
std::unordered_map<uint32, std::vector<uint16>> LoadSpellGroupCache(uint8 min_level, uint8 max_level);
|
||||
uint16 GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill);
|
||||
void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0);
|
||||
void SendFullPopup(const char *Title, const char *Text, uint32 PopupID = 0, uint32 NegativeID = 0, uint32 Buttons = 0, uint32 Duration = 0, const char *ButtonName0 = 0, const char *ButtonName1 = 0, uint32 SoundControls = 0);
|
||||
|
||||
@ -182,7 +182,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
|
||||
//Goal of Spells:UseSpellImpliedTargeting is to replicate the EQ2 feature where spells will 'pass through' invalid targets to target's target to try to find a valid target.
|
||||
if (RuleB(Spells,UseSpellImpliedTargeting) && IsClient()) {
|
||||
if (RuleB(Spells,UseSpellImpliedTargeting) && IsClient()) {
|
||||
Mob* spell_target = entity_list.GetMobID(target_id);
|
||||
if (spell_target) {
|
||||
Mob* targets_target = spell_target->GetTarget();
|
||||
@ -202,7 +202,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!DoCastingChecksOnCaster(spell_id, slot) ||
|
||||
!DoCastingChecksZoneRestrictions(true, spell_id) ||
|
||||
@ -2278,7 +2278,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
|
||||
}
|
||||
}
|
||||
|
||||
//Guard Assist Code
|
||||
//Guard Assist Code
|
||||
if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) {
|
||||
if (IsClient() && spell_target->IsClient()|| (HasOwner() && GetOwner()->IsClient() && spell_target->IsClient())) {
|
||||
auto& mob_list = entity_list.GetCloseMobList(spell_target);
|
||||
@ -2591,8 +2591,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
|
||||
case Beam:
|
||||
{
|
||||
BeamDirectional(spell_id, resist_adjust);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TargetRing:
|
||||
{
|
||||
@ -2885,7 +2885,7 @@ int CalcBuffDuration_formula(int level, int formula, int duration)
|
||||
temp = 10 * (level + 10);
|
||||
break;
|
||||
case 50: // Permanent. Cancelled by casting/combat for perm invis, non-lev zones for lev, curing poison/curse
|
||||
// counters, etc.
|
||||
// counters, etc.
|
||||
return -1;
|
||||
case 51: // Permanent. Cancelled when out of range of aura.
|
||||
return -4;
|
||||
@ -3108,7 +3108,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
continue;
|
||||
|
||||
if (IsBardOnlyStackEffect(effect1) && GetSpellLevel(spellid1, BARD) != 255 &&
|
||||
GetSpellLevel(spellid2, BARD) != 255)
|
||||
GetSpellLevel(spellid2, BARD) != 255)
|
||||
continue;
|
||||
|
||||
// big ol' list according to the client, wasn't that nice!
|
||||
@ -3541,7 +3541,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
|
||||
// other AE spells this is redundant, oh well
|
||||
// 1 = PCs, 2 = NPCs
|
||||
if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList &&
|
||||
spells[spell_id].target_type != ST_HateList) {
|
||||
spells[spell_id].target_type != ST_HateList) {
|
||||
if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc() && !spelltar->IsBot())
|
||||
return false;
|
||||
else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc() || spelltar->IsBot()))
|
||||
@ -5520,6 +5520,33 @@ uint32 Client::GetHighestScribedSpellinSpellGroup(uint32 spell_group)
|
||||
return highest_spell_id;
|
||||
}
|
||||
|
||||
std::unordered_map<uint32, std::vector<uint16>> Client::LoadSpellGroupCache(uint8 min_level, uint8 max_level) {
|
||||
std::unordered_map<uint32, std::vector<uint16>> spell_group_cache;
|
||||
|
||||
const auto query = fmt::format(
|
||||
"SELECT a.spellgroup, a.id, a.rank "
|
||||
"FROM spells_new a "
|
||||
"INNER JOIN ("
|
||||
"SELECT spellgroup, MAX(rank) rank "
|
||||
"FROM spells_new "
|
||||
"GROUP BY spellgroup) "
|
||||
"b ON a.spellgroup = b.spellgroup AND a.rank = b.rank "
|
||||
"WHERE a.spellgroup IN (SELECT DISTINCT spellgroup FROM spells_new WHERE spellgroup != 0 and classes{} BETWEEN {} AND {}) ORDER BY rank DESC",
|
||||
m_pp.class_, min_level, max_level
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
return spell_group_cache;
|
||||
}
|
||||
|
||||
for (auto row : results) {
|
||||
spell_group_cache[std::stoul(row[0])].push_back(static_cast<uint16>(std::stoul(row[1])));
|
||||
}
|
||||
|
||||
return spell_group_cache;
|
||||
}
|
||||
|
||||
bool Client::SpellGlobalCheck(uint16 spell_id, uint32 character_id) {
|
||||
std::string query = fmt::format(
|
||||
"SELECT qglobal, value FROM spell_globals WHERE spellid = {}",
|
||||
@ -6340,7 +6367,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
|
||||
while (iter != targets_in_range.end()) {
|
||||
if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient())) ||
|
||||
(*iter)->BehindMob(this, (*iter)->GetX(), (*iter)->GetY())) {
|
||||
(*iter)->BehindMob(this, (*iter)->GetX(), (*iter)->GetY())) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
@ -6429,7 +6456,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
}
|
||||
|
||||
float heading_to_target =
|
||||
(CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 512.0f);
|
||||
(CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 512.0f);
|
||||
|
||||
while (heading_to_target < 0.0f)
|
||||
heading_to_target += 360.0f;
|
||||
@ -6473,7 +6500,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
|
||||
if (angle_start > angle_end) {
|
||||
if ((heading_to_target >= angle_start && heading_to_target <= 360.0f) ||
|
||||
(heading_to_target >= 0.0f && heading_to_target <= angle_end)) {
|
||||
(heading_to_target >= 0.0f && heading_to_target <= angle_end)) {
|
||||
if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) {
|
||||
(*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this);
|
||||
SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user