LUA RemoveSpell(Spawn, spell_id, ..) added, Illusion is a control effect, Redesign of control effects, immunities, effect flags away from mutex lists

RemoveSpell(Spawn, SpellID, MaintainedSpell, ReasonString) - maintainedspell is a boolean, default false it will remove an effect (per target) otherwise setting to true will remove the entire spell, maintained, if that is the caster.  Reason String is "canceled" by default unless overwritten

Illusions made to a control effect so we can enforce an existing illusion
This commit is contained in:
Emagi 2025-08-25 09:18:34 -04:00
parent 765a110732
commit 1bc87d6617
9 changed files with 774 additions and 519 deletions

View File

@ -0,0 +1,442 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <shared_mutex>
#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
class LuaSpell;
// ----------------------------- Core bitflags --------------------------------
template <std::size_t NBits>
class BitFlags {
public:
static constexpr std::size_t kWordBits = 64;
static constexpr std::size_t kWords = (NBits + kWordBits - 1) / kWordBits;
constexpr BitFlags() noexcept : words_{} {}
// Single-bit ops
inline void set(std::size_t bit) noexcept { words_[word_index(bit)] |= word_mask(bit); }
inline void reset(std::size_t bit) noexcept { words_[word_index(bit)] &= ~word_mask(bit); }
inline void toggle(std::size_t bit) noexcept { words_[word_index(bit)] ^= word_mask(bit); }
inline bool test(std::size_t bit) const noexcept { return (words_[word_index(bit)] & word_mask(bit)) != 0ULL; }
// Bulk ops
inline void clear() noexcept {
for (std::size_t i = 0; i < kWords; ++i) words_[i] = 0ULL;
}
inline bool any() const noexcept {
for (std::size_t i = 0; i < kWords; ++i) if (words_[i]) return true;
return false;
}
inline bool none() const noexcept { return !any(); }
inline void import_u32(std::uint32_t legacy) noexcept {
if (!legacy) return;
const std::size_t lim = (NBits < 32 ? NBits : 32);
for (std::size_t i = 0; i < lim; ++i)
if ((legacy >> i) & 1u) set(i);
}
inline std::uint32_t export_u32() const noexcept {
std::uint32_t out = 0;
const std::size_t lim = (NBits < 32 ? NBits : 32);
for (std::size_t i = 0; i < lim; ++i)
if (test(i)) out |= (1u << i);
return out;
}
// Import a 64-bit legacy mask (low 64 bits only)
inline void import_u64(std::uint64_t legacy) noexcept {
if (!legacy) return;
const std::size_t lim = (NBits < 64 ? NBits : 64);
for (std::size_t i = 0; i < lim; ++i)
if ((legacy >> i) & 1ULL) set(i);
}
// Export low 64 bits into a uint64_t
inline std::uint64_t export_u64() const noexcept {
std::uint64_t out = 0;
const std::size_t lim = (NBits < 64 ? NBits : 64);
for (std::size_t i = 0; i < lim; ++i)
if (test(i)) out |= (1ULL << i);
return out;
}
// Set by legacy power-of-two value (find index of the single set bit)
inline void set_legacy_flag_value(std::uint64_t pow2) noexcept {
if (!pow2) return;
#if defined(__GNUC__) || defined(__clang__)
std::size_t bit = static_cast<std::size_t>(__builtin_ctzll(pow2));
#else
std::size_t bit = 0; while (bit < 64 && ((pow2 >> bit) & 1ULL) == 0ULL) ++bit;
#endif
if (bit < NBits) set(bit);
}
// Direct word access (used by TS wrapper)
static constexpr std::size_t words_count() noexcept { return kWords; }
inline std::uint64_t& word_ref(std::size_t i) noexcept { return words_[i]; }
inline const std::uint64_t& word_ref(std::size_t i) const noexcept { return words_[i]; }
private:
static constexpr std::size_t word_index(std::size_t bit) noexcept { return bit / kWordBits; }
static constexpr std::uint64_t word_mask (std::size_t bit) noexcept { return 1ULL << (bit % kWordBits); }
std::array<std::uint64_t, kWords> words_;
};
// --------------------------- Thread-safe wrapper ----------------------------
template <std::size_t NBits>
class TSBitFlags {
public:
using Snapshot = BitFlags<NBits>;
// Single-bit ops
inline void set(std::size_t bit) { std::unique_lock lk(m_); bits_.set(bit); }
inline void reset(std::size_t bit) { std::unique_lock lk(m_); bits_.reset(bit); }
inline void toggle(std::size_t bit) { std::unique_lock lk(m_); bits_.toggle(bit); }
inline bool test(std::size_t bit) const { std::shared_lock lk(m_); return bits_.test(bit); }
// Convenience
inline void add(std::size_t bit) { set(bit); }
inline void remove(std::size_t bit) { reset(bit); }
inline bool has(std::size_t bit) const { return test(bit); }
// Batch ops
inline void set_many(std::initializer_list<std::size_t> bits) {
std::unique_lock lk(m_); for (auto b : bits) bits_.set(b);
}
inline void reset_many(std::initializer_list<std::size_t> bits) {
std::unique_lock lk(m_); for (auto b : bits) bits_.reset(b);
}
// Clear / any / none
inline void clear() { std::unique_lock lk(m_); bits_.clear(); }
inline bool any() const { std::shared_lock lk(m_); return bits_.any(); }
inline bool none() const { std::shared_lock lk(m_); return bits_.none(); }
// Legacy helpers
inline void import_u32(std::uint32_t legacy) { std::unique_lock lk(m_); bits_.import_u32(legacy); }
inline std::uint32_t export_u32() const { std::shared_lock lk(m_); return bits_.export_u32(); }
inline void import_u64(std::uint64_t legacy) { std::unique_lock lk(m_); bits_.import_u64(legacy); }
inline std::uint64_t export_u64() const { std::shared_lock lk(m_); return bits_.export_u64(); }
inline void set_legacy_flag_value(std::uint64_t v){ std::unique_lock lk(m_); bits_.set_legacy_flag_value(v); }
// Snapshots (copy out/in without exposing lock)
inline Snapshot snapshot() const { std::shared_lock lk(m_); return bits_; }
inline void assign_from(const Snapshot& snap) {
std::unique_lock lk(m_);
for (std::size_t i = 0; i < Snapshot::words_count(); ++i)
bits_.word_ref(i) = snap.word_ref(i);
}
private:
mutable std::shared_mutex m_;
Snapshot bits_{};
};
// ------------------------- Project-specific typedefs ------------------------
inline constexpr std::size_t kEffectBits = 64; // limited due to DB restrictions at this time
using EffectFlags = TSBitFlags<kEffectBits>;
enum : std::size_t {
EFFECT_IDX_STUN = 0,
EFFECT_IDX_ROOT = 1,
EFFECT_IDX_MEZ = 2,
EFFECT_IDX_STIFLE = 3,
EFFECT_IDX_DAZE = 4,
EFFECT_IDX_FEAR = 5,
EFFECT_IDX_SPELLBONUS = 6,
EFFECT_IDX_SKILLBONUS = 7,
EFFECT_IDX_STEALTH = 8,
EFFECT_IDX_INVIS = 9,
EFFECT_IDX_SNARE = 10,
EFFECT_IDX_WATERWALK = 11,
EFFECT_IDX_WATERJUMP = 12,
EFFECT_IDX_FLIGHT = 13,
EFFECT_IDX_GLIDE = 14,
EFFECT_IDX_AOE_IMMUNE = 15,
EFFECT_IDX_STUN_IMMUNE = 16,
EFFECT_IDX_MEZ_IMMUNE = 17,
EFFECT_IDX_DAZE_IMMUNE = 18,
EFFECT_IDX_ROOT_IMMUNE = 19,
EFFECT_IDX_STIFLE_IMMUNE = 20,
EFFECT_IDX_FEAR_IMMUNE = 21,
EFFECT_IDX_SAFEFALL = 22,
EFFECT_IDX_ILLUSION = 23,
};
class EffectRegistryPerSpell {
public:
using Code = uint8_t; // your int8 codes
using Value = std::variant<std::monostate, int64_t, double, std::string>;
explicit EffectRegistryPerSpell(Code max_code_inclusive)
: max_code_(max_code_inclusive),
slots_(static_cast<size_t>(max_code_inclusive) + 1) {}
// --- Membership (writers) -------------------------------------------------
// Add a spell to a type. Returns true if inserted (false if already present).
bool Add(Code code, LuaSpell* spell) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
return s.members.emplace(spell, Entry{spell, std::monostate{}}).second;
}
// Add or update (set/replace the per-spell value atomically).
// Returns true if inserted new, false if updated existing.
template <typename T>
bool AddOrUpdate(Code code, LuaSpell* spell, T v) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
auto [it, inserted] = s.members.emplace(spell, Entry{spell, std::monostate{}});
it->second.val = to_variant(std::move(v));
return inserted;
}
// Remove a spell from a type. Returns true if erased.
bool Remove(Code code, LuaSpell* spell) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
return s.members.erase(spell) > 0;
}
// Clear all spells at a type
void Clear(Code code) {
if (!in_range(code)) return;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
s.members.clear();
}
// --- Queries (readers) ----------------------------------------------------
// Is there any spell registered at this type?
bool Has(Code code) const {
if (!in_range(code)) return false;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
return !s.members.empty();
}
// Is this specific spell present under this type?
bool Contains(Code code, const LuaSpell* spell) const {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
return s.members.find(const_cast<LuaSpell*>(spell)) != s.members.end();
}
// How many spells are registered at this type?
size_t Count(Code code) const {
if (!in_range(code)) return 0;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
return s.members.size();
}
// Snapshot list of spells under a type (copy out while holding a shared lock).
std::vector<LuaSpell*> Snapshot(Code code) const {
std::vector<LuaSpell*> out;
if (!in_range(code)) return out;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
out.reserve(s.members.size());
for (auto& kv : s.members) out.push_back(kv.first);
return out;
}
// Snapshot of (spell, value) pairs under a type.
struct SpellWithValue {
LuaSpell* spell{};
Value value{};
};
std::vector<SpellWithValue> SnapshotWithValues(Code code) const {
std::vector<SpellWithValue> out;
if (!in_range(code)) return out;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
out.reserve(s.members.size());
for (auto& kv : s.members) out.push_back({kv.second.ptr, kv.second.val});
return out;
}
// --- Per-spell value API --------------------------------------------------
// Set/replace the value for a specific spell under a type.
template <typename T>
bool SetValue(Code code, LuaSpell* spell, T v) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
auto it = s.members.find(spell);
if (it == s.members.end()) return false;
it->second.val = to_variant(std::move(v));
return true;
}
// Clear value for a specific spell under a type (keeps membership).
bool ClearValue(Code code, LuaSpell* spell) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
auto it = s.members.find(spell);
if (it == s.members.end()) return false;
it->second.val = std::monostate{};
return true;
}
// Read value as requested type. nullopt if not set or wrong type.
template <typename T>
std::optional<T> GetValue(Code code, const LuaSpell* spell) const {
if (!in_range(code) || !spell) return std::nullopt;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
auto it = s.members.find(const_cast<LuaSpell*>(spell));
if (it == s.members.end()) return std::nullopt;
if (auto p = std::get_if<T>(&it->second.val)) return *p;
return std::nullopt;
}
// Convenience typed getters
std::optional<int64_t> GetInt (Code code, const LuaSpell* s) const { return GetValue<int64_t>(code, s); }
std::optional<double> GetFloat (Code code, const LuaSpell* s) const { return GetValue<double> (code, s); }
std::optional<std::string> GetString(Code code, const LuaSpell* s) const { return GetValue<std::string>(code, s); }
Code MaxCode() const { return max_code_; }
private:
struct Entry {
LuaSpell* ptr{};
Value val{};
};
struct Slot {
mutable std::shared_mutex mtx;
std::unordered_map<LuaSpell*, Entry> members; // key = spell*, value = (spell*, variant)
};
bool in_range(Code code) const { return code <= max_code_; }
// normalize to variant
static Value to_variant(int64_t v) { return Value{v}; }
static Value to_variant(int v) { return Value{static_cast<int64_t>(v)}; }
static Value to_variant(uint32_t v) { return Value{static_cast<int64_t>(v)}; }
static Value to_variant(float v) { return Value{static_cast<double>(v)}; }
static Value to_variant(double v) { return Value{v}; }
static Value to_variant(const char* s) { return Value{std::string(s ? s : "")}; }
static Value to_variant(std::string s) { return Value{std::move(s)}; }
Code max_code_;
std::vector<Slot> slots_;
};
#define CONTROL_EFFECT_TYPE_MEZ 1
#define CONTROL_EFFECT_TYPE_STIFLE 2
#define CONTROL_EFFECT_TYPE_DAZE 3
#define CONTROL_EFFECT_TYPE_STUN 4
#define CONTROL_EFFECT_TYPE_ROOT 5
#define CONTROL_EFFECT_TYPE_FEAR 6
#define CONTROL_EFFECT_TYPE_WALKUNDERWATER 7
#define CONTROL_EFFECT_TYPE_JUMPUNDERWATER 8
#define CONTROL_EFFECT_TYPE_INVIS 9
#define CONTROL_EFFECT_TYPE_STEALTH 10
#define CONTROL_EFFECT_TYPE_SNARE 11
#define CONTROL_EFFECT_TYPE_FLIGHT 12
#define CONTROL_EFFECT_TYPE_GLIDE 13
#define CONTROL_EFFECT_TYPE_SAFEFALL 14
#define CONTROL_EFFECT_TYPE_ILLUSION 15
#define CONTROL_MAX_EFFECTS 16
#define IMMUNITY_TYPE_MEZ 1
#define IMMUNITY_TYPE_STIFLE 2
#define IMMUNITY_TYPE_DAZE 3
#define IMMUNITY_TYPE_STUN 4
#define IMMUNITY_TYPE_ROOT 5
#define IMMUNITY_TYPE_FEAR 6
#define IMMUNITY_TYPE_AOE 7
#define IMMUNITY_TYPE_TAUNT 8
#define IMMUNITY_TYPE_RIPOSTE 9
#define IMMUNITY_TYPE_STRIKETHROUGH 10
#define IMMUNITY_MAX_TYPES 11
class ControlEffects {
public:
ControlEffects() : reg_(CONTROL_MAX_EFFECTS - 1) {} // max code = 14
bool Add(uint8_t type, LuaSpell* s) { return reg_.Add(type, s); }
template <typename T> bool AddOrUpdate(uint8_t t, LuaSpell* s, T v){ return reg_.AddOrUpdate(t, s, std::move(v)); }
bool Remove(uint8_t type, LuaSpell* s) { return reg_.Remove(type, s); }
void Clear(uint8_t type) { reg_.Clear(type); }
bool Has(uint8_t type) const { return reg_.Has(type); }
bool Contains(uint8_t type, const LuaSpell* s) const { return reg_.Contains(type, s); }
size_t Count(uint8_t type) const { return reg_.Count(type); }
std::vector<LuaSpell*> Snapshot(uint8_t type) const { return reg_.Snapshot(type); }
auto SnapshotWithValues(uint8_t type) const { return reg_.SnapshotWithValues(type); }
template <typename T> bool SetValue(uint8_t t, LuaSpell* s, T v) { return reg_.SetValue(t, s, std::move(v)); }
bool ClearValue(uint8_t t, LuaSpell* s) { return reg_.ClearValue(t, s); }
template <typename T> std::optional<T> GetValue(uint8_t t, const LuaSpell* s) const { return reg_.GetValue<T>(t, s); }
private:
EffectRegistryPerSpell reg_;
};
class Immunities {
public:
Immunities() : reg_(IMMUNITY_MAX_TYPES) {} // max code = 10
bool Add(uint8_t type, LuaSpell* s) { return reg_.Add(type, s); }
template <typename T> bool AddOrUpdate(uint8_t t, LuaSpell* s, T v){ return reg_.AddOrUpdate(t, s, std::move(v)); }
bool Remove(uint8_t type, LuaSpell* s) { return reg_.Remove(type, s); }
void Clear(uint8_t type) { reg_.Clear(type); }
bool Has(uint8_t type) const { return reg_.Has(type); }
bool Contains(uint8_t type, const LuaSpell* s) const { return reg_.Contains(type, s); }
size_t Count(uint8_t type) const { return reg_.Count(type); }
std::vector<LuaSpell*> Snapshot(uint8_t type) const { return reg_.Snapshot(type); }
auto SnapshotWithValues(uint8_t type) const { return reg_.SnapshotWithValues(type); }
template <typename T> bool SetValue(uint8_t t, LuaSpell* s, T v) { return reg_.SetValue(t, s, std::move(v)); }
bool ClearValue(uint8_t t, LuaSpell* s) { return reg_.ClearValue(t, s); }
template <typename T> std::optional<T> GetValue(uint8_t t, const LuaSpell* s) const { return reg_.GetValue<T>(t, s); }
private:
EffectRegistryPerSpell reg_;
};

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@
#include "MutexList.h" #include "MutexList.h"
#include "MutexVector.h" #include "MutexVector.h"
#include "Trade.h" #include "Trade.h"
#include "EffectFlags.h"
#include <set> #include <set>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
@ -1115,6 +1116,7 @@ struct InfoStruct{
// when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock
std::mutex classMutex; std::mutex classMutex;
std::unordered_map<std::string, std::string> props; std::unordered_map<std::string, std::string> props;
EffectFlags spell_flags;
private: private:
std::string name_; std::string name_;
int8 class1_; int8 class1_;
@ -1413,33 +1415,6 @@ struct ThreatTransfer {
#define DISPELL_TYPE_CURE 0 #define DISPELL_TYPE_CURE 0
#define DISPELL_TYPE_DISPELL 1 #define DISPELL_TYPE_DISPELL 1
#define CONTROL_EFFECT_TYPE_MEZ 1
#define CONTROL_EFFECT_TYPE_STIFLE 2
#define CONTROL_EFFECT_TYPE_DAZE 3
#define CONTROL_EFFECT_TYPE_STUN 4
#define CONTROL_EFFECT_TYPE_ROOT 5
#define CONTROL_EFFECT_TYPE_FEAR 6
#define CONTROL_EFFECT_TYPE_WALKUNDERWATER 7
#define CONTROL_EFFECT_TYPE_JUMPUNDERWATER 8
#define CONTROL_EFFECT_TYPE_INVIS 9
#define CONTROL_EFFECT_TYPE_STEALTH 10
#define CONTROL_EFFECT_TYPE_SNARE 11
#define CONTROL_EFFECT_TYPE_FLIGHT 12
#define CONTROL_EFFECT_TYPE_GLIDE 13
#define CONTROL_EFFECT_TYPE_SAFEFALL 14
#define CONTROL_MAX_EFFECTS 15 // always +1 to highest control effect
#define IMMUNITY_TYPE_MEZ 1
#define IMMUNITY_TYPE_STIFLE 2
#define IMMUNITY_TYPE_DAZE 3
#define IMMUNITY_TYPE_STUN 4
#define IMMUNITY_TYPE_ROOT 5
#define IMMUNITY_TYPE_FEAR 6
#define IMMUNITY_TYPE_AOE 7
#define IMMUNITY_TYPE_TAUNT 8
#define IMMUNITY_TYPE_RIPOSTE 9
#define IMMUNITY_TYPE_STRIKETHROUGH 10
//class Spell; //class Spell;
//class ZoneServer; //class ZoneServer;
@ -1985,6 +1960,8 @@ public:
void RemoveSafefallSpell(LuaSpell* spell); void RemoveSafefallSpell(LuaSpell* spell);
void AddGlideSpell(LuaSpell* spell); void AddGlideSpell(LuaSpell* spell);
void RemoveGlideSpell(LuaSpell* spell); void RemoveGlideSpell(LuaSpell* spell);
void AddIllusionSpell(LuaSpell* spell, int16 model_id);
void RemoveIllusionSpell(LuaSpell* spell);
GroupMemberInfo* GetGroupMemberInfo() { return group_member_info; } GroupMemberInfo* GetGroupMemberInfo() { return group_member_info; }
void SetGroupMemberInfo(GroupMemberInfo* info) { group_member_info = info; } void SetGroupMemberInfo(GroupMemberInfo* info) { group_member_info = info; }
@ -2110,6 +2087,10 @@ public:
return "SafeFall"; return "SafeFall";
break; break;
} }
case CONTROL_EFFECT_TYPE_ILLUSION:{
return "Illusion";
break;
}
default: { default: {
return "Undefined"; return "Undefined";
break; break;
@ -2136,8 +2117,8 @@ protected:
bool m_petDismissing; bool m_petDismissing;
private: private:
MutexList<BonusValues*> bonus_list; MutexList<BonusValues*> bonus_list;
map<int8, MutexList<LuaSpell*>*> control_effects; ControlEffects control_effects;
map<int8, MutexList<LuaSpell*>*> immunities; Immunities immunities;
float max_speed; float max_speed;
int8 deity; int8 deity;
sint16 regen_hp_rate; sint16 regen_hp_rate;
@ -2176,6 +2157,7 @@ private:
float speed_multiplier; float speed_multiplier;
map<LuaSpell*, float> snare_values; map<LuaSpell*, float> snare_values;
mutable std::shared_mutex MSnareValues;
ThreatTransfer* m_threatTransfer; ThreatTransfer* m_threatTransfer;

View File

@ -2465,8 +2465,7 @@ int EQ2Emu_lua_AddSpellBonus(lua_State* state) {
lua_interface->LogError("%s: Error applying spell bonus on non entity.", lua_interface->GetScriptName(state)); lua_interface->LogError("%s: Error applying spell bonus on non entity.", lua_interface->GetScriptName(state));
} }
} }
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SPELLBONUS)) luaspell->effect_flags.add(EFFECT_IDX_SPELLBONUS);
luaspell->effect_bitmask += EFFECT_FLAG_SPELLBONUS;
} }
else if (spawn && spawn->IsEntity()) { else if (spawn && spawn->IsEntity()) {
((Entity*)spawn)->AddSpellBonus(luaspell, type, value, class_req, race_req, faction_req); ((Entity*)spawn)->AddSpellBonus(luaspell, type, value, class_req, race_req, faction_req);
@ -2540,8 +2539,7 @@ int EQ2Emu_lua_AddSpawnSpellBonus(lua_State* state) {
((Entity*)spawn)->AddSpellBonus(luaspell, type, value, class_req, race_req, faction_req); ((Entity*)spawn)->AddSpellBonus(luaspell, type, value, class_req, race_req, faction_req);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SPELLBONUS)) luaspell->effect_flags.add(EFFECT_IDX_SPELLBONUS);
luaspell->effect_bitmask += EFFECT_FLAG_SPELLBONUS;
if (spawn->IsPlayer()) if (spawn->IsPlayer())
((Player*)spawn)->SetCharSheetChanged(true); ((Player*)spawn)->SetCharSheetChanged(true);
@ -2640,13 +2638,11 @@ int EQ2Emu_lua_AddSkillBonus(lua_State* state) {
if (packet) if (packet)
client->QueuePacket(packet); client->QueuePacket(packet);
} }
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SKILLBONUS)) luaspell->effect_flags.add(EFFECT_IDX_SKILLBONUS);
luaspell->effect_bitmask += EFFECT_FLAG_SKILLBONUS;
} }
else if (target->IsNPC()) { else if (target->IsNPC()) {
((NPC*)target)->AddSkillBonus(spell_id, skill_id, value); ((NPC*)target)->AddSkillBonus(spell_id, skill_id, value);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SKILLBONUS)) luaspell->effect_flags.add(EFFECT_IDX_SKILLBONUS);
luaspell->effect_bitmask += EFFECT_FLAG_SKILLBONUS;
} }
else else
LogWrite(LUA__ERROR, 0, "LUA", "Error applying bonus buff on '%s'. Not a NPC or player.", target->GetName()); LogWrite(LUA__ERROR, 0, "LUA", "Error applying bonus buff on '%s'. Not a NPC or player.", target->GetName());
@ -2746,77 +2742,65 @@ int EQ2Emu_lua_AddControlEffect(lua_State* state) {
if (target && target->IsEntity()) { if (target && target->IsEntity()) {
if (type == CONTROL_EFFECT_TYPE_MEZ) { if (type == CONTROL_EFFECT_TYPE_MEZ) {
((Entity*)target)->AddMezSpell(luaspell); ((Entity*)target)->AddMezSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_MEZ)) luaspell->effect_flags.add(EFFECT_IDX_MEZ);
luaspell->effect_bitmask += EFFECT_FLAG_MEZ;
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_STIFLE) { else if (type == CONTROL_EFFECT_TYPE_STIFLE) {
((Entity*)target)->AddStifleSpell(luaspell); ((Entity*)target)->AddStifleSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_STIFLE)) luaspell->effect_flags.add(EFFECT_IDX_STIFLE);
luaspell->effect_bitmask += EFFECT_FLAG_STIFLE;
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_DAZE) { else if (type == CONTROL_EFFECT_TYPE_DAZE) {
((Entity*)target)->AddDazeSpell(luaspell); ((Entity*)target)->AddDazeSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_DAZE)) luaspell->effect_flags.add(EFFECT_IDX_DAZE);
luaspell->effect_bitmask += EFFECT_FLAG_DAZE;
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_STUN) { else if (type == CONTROL_EFFECT_TYPE_STUN) {
if (!(luaspell->effect_bitmask & EFFECT_FLAG_STUN)) luaspell->effect_flags.add(EFFECT_IDX_STUN);
luaspell->effect_bitmask += EFFECT_FLAG_STUN;
((Entity*)target)->AddStunSpell(luaspell); ((Entity*)target)->AddStunSpell(luaspell);
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_ROOT) { else if (type == CONTROL_EFFECT_TYPE_ROOT) {
if (!(luaspell->effect_bitmask & EFFECT_FLAG_ROOT))
luaspell->effect_bitmask += EFFECT_FLAG_ROOT;
((Entity*)target)->AddRootSpell(luaspell); ((Entity*)target)->AddRootSpell(luaspell);
luaspell->effect_flags.add(EFFECT_IDX_ROOT);
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_FEAR) { else if (type == CONTROL_EFFECT_TYPE_FEAR) {
if (!(luaspell->effect_bitmask & EFFECT_FLAG_FEAR))
luaspell->effect_bitmask += EFFECT_FLAG_FEAR;
((Entity*)target)->AddFearSpell(luaspell); ((Entity*)target)->AddFearSpell(luaspell);
luaspell->effect_flags.add(EFFECT_IDX_FEAR);
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_WALKUNDERWATER) { else if (type == CONTROL_EFFECT_TYPE_WALKUNDERWATER) {
((Entity*)target)->AddWaterwalkSpell(luaspell); ((Entity*)target)->AddWaterwalkSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_WATERWALK)) luaspell->effect_flags.add(EFFECT_IDX_WATERWALK);
luaspell->effect_bitmask += EFFECT_FLAG_WATERWALK;
} }
else if (type == CONTROL_EFFECT_TYPE_JUMPUNDERWATER) { else if (type == CONTROL_EFFECT_TYPE_JUMPUNDERWATER) {
((Entity*)target)->AddWaterjumpSpell(luaspell); ((Entity*)target)->AddWaterjumpSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_WATERJUMP)) luaspell->effect_flags.add(EFFECT_IDX_WATERJUMP);
luaspell->effect_bitmask += EFFECT_FLAG_WATERJUMP;
} }
else if (type == CONTROL_EFFECT_TYPE_SNARE) { else if (type == CONTROL_EFFECT_TYPE_SNARE) {
((Entity*)target)->AddSnareSpell(luaspell); ((Entity*)target)->AddSnareSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SNARE)) luaspell->effect_flags.add(EFFECT_IDX_SNARE);
luaspell->effect_bitmask += EFFECT_FLAG_SNARE;
if (target->IsNPC()) if (target->IsNPC())
((NPC*)target)->Brain()->AddHate(luaspell->caster, 5); ((NPC*)target)->Brain()->AddHate(luaspell->caster, 5);
} }
else if (type == CONTROL_EFFECT_TYPE_FLIGHT) { else if (type == CONTROL_EFFECT_TYPE_FLIGHT) {
((Entity*)target)->AddFlightSpell(luaspell); ((Entity*)target)->AddFlightSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_FLIGHT)) luaspell->effect_flags.add(EFFECT_IDX_FLIGHT);
luaspell->effect_bitmask += EFFECT_FLAG_FLIGHT;
} }
else if (type == CONTROL_EFFECT_TYPE_GLIDE) { else if (type == CONTROL_EFFECT_TYPE_GLIDE) {
((Entity*)target)->AddGlideSpell(luaspell); ((Entity*)target)->AddGlideSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_GLIDE)) luaspell->effect_flags.add(EFFECT_IDX_GLIDE);
luaspell->effect_bitmask += EFFECT_FLAG_GLIDE;
} }
else if (type == CONTROL_EFFECT_TYPE_SAFEFALL) { else if (type == CONTROL_EFFECT_TYPE_SAFEFALL) {
((Entity*)target)->AddSafefallSpell(luaspell); ((Entity*)target)->AddSafefallSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SAFEFALL)) luaspell->effect_flags.add(EFFECT_IDX_SAFEFALL);
luaspell->effect_bitmask += EFFECT_FLAG_SAFEFALL;
} }
else else
lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type); lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type);
@ -2828,63 +2812,51 @@ int EQ2Emu_lua_AddControlEffect(lua_State* state) {
else if (only_add_spawn && spawn && spawn->IsEntity()) { else if (only_add_spawn && spawn && spawn->IsEntity()) {
if (type == CONTROL_EFFECT_TYPE_MEZ) { if (type == CONTROL_EFFECT_TYPE_MEZ) {
((Entity*)spawn)->AddMezSpell(luaspell); ((Entity*)spawn)->AddMezSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_MEZ)) luaspell->effect_flags.add(EFFECT_IDX_MEZ);
luaspell->effect_bitmask += EFFECT_FLAG_MEZ;
} }
else if (type == CONTROL_EFFECT_TYPE_STIFLE) { else if (type == CONTROL_EFFECT_TYPE_STIFLE) {
((Entity*)spawn)->AddStifleSpell(luaspell); ((Entity*)spawn)->AddStifleSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_STIFLE)) luaspell->effect_flags.add(EFFECT_IDX_STIFLE);
luaspell->effect_bitmask += EFFECT_FLAG_STIFLE;
} }
else if (type == CONTROL_EFFECT_TYPE_DAZE) { else if (type == CONTROL_EFFECT_TYPE_DAZE) {
((Entity*)spawn)->AddDazeSpell(luaspell); ((Entity*)spawn)->AddDazeSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_DAZE)) luaspell->effect_flags.add(EFFECT_IDX_DAZE);
luaspell->effect_bitmask += EFFECT_FLAG_DAZE;
} }
else if (type == CONTROL_EFFECT_TYPE_STUN) { else if (type == CONTROL_EFFECT_TYPE_STUN) {
((Entity*)spawn)->AddStunSpell(luaspell); ((Entity*)spawn)->AddStunSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_STUN)) luaspell->effect_flags.add(EFFECT_IDX_STUN);
luaspell->effect_bitmask += EFFECT_FLAG_STUN;
} }
else if (type == CONTROL_EFFECT_TYPE_ROOT) { else if (type == CONTROL_EFFECT_TYPE_ROOT) {
((Entity*)spawn)->AddRootSpell(luaspell); ((Entity*)spawn)->AddRootSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_ROOT)) luaspell->effect_flags.add(EFFECT_IDX_ROOT);
luaspell->effect_bitmask += EFFECT_FLAG_ROOT;
} }
else if (type == CONTROL_EFFECT_TYPE_FEAR) { else if (type == CONTROL_EFFECT_TYPE_FEAR) {
((Entity*)spawn)->AddFearSpell(luaspell); ((Entity*)spawn)->AddFearSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_FEAR)) luaspell->effect_flags.add(EFFECT_IDX_FEAR);
luaspell->effect_bitmask += EFFECT_FLAG_FEAR;
} }
else if (type == CONTROL_EFFECT_TYPE_WALKUNDERWATER) { else if (type == CONTROL_EFFECT_TYPE_WALKUNDERWATER) {
((Entity*)spawn)->AddWaterwalkSpell(luaspell); ((Entity*)spawn)->AddWaterwalkSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_WATERWALK)) luaspell->effect_flags.add(EFFECT_IDX_WATERWALK);
luaspell->effect_bitmask += EFFECT_FLAG_WATERWALK;
} }
else if (type == CONTROL_EFFECT_TYPE_JUMPUNDERWATER) { else if (type == CONTROL_EFFECT_TYPE_JUMPUNDERWATER) {
((Entity*)spawn)->AddWaterjumpSpell(luaspell); ((Entity*)spawn)->AddWaterjumpSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_WATERJUMP)) luaspell->effect_flags.add(EFFECT_IDX_WATERJUMP);
luaspell->effect_bitmask += EFFECT_FLAG_WATERJUMP;
} }
else if (type == CONTROL_EFFECT_TYPE_SNARE) { else if (type == CONTROL_EFFECT_TYPE_SNARE) {
((Entity*)spawn)->AddSnareSpell(luaspell); ((Entity*)spawn)->AddSnareSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SNARE)) luaspell->effect_flags.add(EFFECT_IDX_SNARE);
luaspell->effect_bitmask += EFFECT_FLAG_SNARE;
} }
else if (type == CONTROL_EFFECT_TYPE_FLIGHT) { else if (type == CONTROL_EFFECT_TYPE_FLIGHT) {
((Entity*)spawn)->AddFlightSpell(luaspell); ((Entity*)spawn)->AddFlightSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_FLIGHT)) luaspell->effect_flags.add(EFFECT_IDX_FLIGHT);
luaspell->effect_bitmask += EFFECT_FLAG_FLIGHT;
} }
else if (type == CONTROL_EFFECT_TYPE_GLIDE) { else if (type == CONTROL_EFFECT_TYPE_GLIDE) {
((Entity*)spawn)->AddGlideSpell(luaspell); ((Entity*)spawn)->AddGlideSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_GLIDE)) luaspell->effect_flags.add(EFFECT_IDX_GLIDE);
luaspell->effect_bitmask += EFFECT_FLAG_GLIDE;
} }
else if (type == CONTROL_EFFECT_TYPE_SAFEFALL) { else if (type == CONTROL_EFFECT_TYPE_SAFEFALL) {
((Entity*)spawn)->AddSafefallSpell(luaspell); ((Entity*)spawn)->AddSafefallSpell(luaspell);
if (!(luaspell->effect_bitmask & EFFECT_FLAG_SAFEFALL)) luaspell->effect_flags.add(EFFECT_IDX_SAFEFALL);
luaspell->effect_bitmask += EFFECT_FLAG_SAFEFALL;
} }
else else
lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type); lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type);
@ -2933,6 +2905,8 @@ int EQ2Emu_lua_RemoveControlEffect(lua_State* state) {
((Entity*)target)->RemoveGlideSpell(luaspell); ((Entity*)target)->RemoveGlideSpell(luaspell);
else if (type == CONTROL_EFFECT_TYPE_SAFEFALL) else if (type == CONTROL_EFFECT_TYPE_SAFEFALL)
((Entity*)target)->RemoveGlideSpell(luaspell); ((Entity*)target)->RemoveGlideSpell(luaspell);
else if (type == CONTROL_EFFECT_TYPE_ILLUSION)
((Entity*)target)->RemoveIllusionSpell(luaspell);
else else
lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type); lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type);
} }
@ -2963,6 +2937,8 @@ int EQ2Emu_lua_RemoveControlEffect(lua_State* state) {
((Entity*)spawn)->RemoveGlideSpell(luaspell); ((Entity*)spawn)->RemoveGlideSpell(luaspell);
else if (type == CONTROL_EFFECT_TYPE_SAFEFALL) else if (type == CONTROL_EFFECT_TYPE_SAFEFALL)
((Entity*)spawn)->RemoveSafefallSpell(luaspell); ((Entity*)spawn)->RemoveSafefallSpell(luaspell);
else if (type == CONTROL_EFFECT_TYPE_ILLUSION)
((Entity*)spawn)->RemoveIllusionSpell(luaspell);
else else
lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type); lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type);
} }
@ -6836,13 +6812,11 @@ int EQ2Emu_lua_Stealth(lua_State* state) {
if (spawn->IsEntity()) { if (spawn->IsEntity()) {
if (type == 1) { if (type == 1) {
((Entity*)spawn)->AddStealthSpell(spell); ((Entity*)spawn)->AddStealthSpell(spell);
if (!(spell->effect_bitmask & EFFECT_FLAG_STEALTH)) spell->effect_flags.add(EFFECT_IDX_STEALTH);
spell->effect_bitmask += EFFECT_FLAG_STEALTH;
} }
else if (type == 2) { else if (type == 2) {
((Entity*)spawn)->AddInvisSpell(spell); ((Entity*)spawn)->AddInvisSpell(spell);
if (!(spell->effect_bitmask & EFFECT_FLAG_INVIS)) spell->effect_flags.add(EFFECT_IDX_INVIS);
spell->effect_bitmask += EFFECT_FLAG_INVIS;
} }
return 0; return 0;
} }
@ -6859,13 +6833,11 @@ int EQ2Emu_lua_Stealth(lua_State* state) {
if (type == 1) { if (type == 1) {
((Entity*)spawn)->AddStealthSpell(spell); ((Entity*)spawn)->AddStealthSpell(spell);
if (!(spell->effect_bitmask & EFFECT_FLAG_STEALTH)) spell->effect_flags.add(EFFECT_IDX_STEALTH);
spell->effect_bitmask += EFFECT_FLAG_STEALTH;
} }
else if (type == 2) { else if (type == 2) {
((Entity*)spawn)->AddInvisSpell(spell); ((Entity*)spawn)->AddInvisSpell(spell);
if (!(spell->effect_bitmask & EFFECT_FLAG_INVIS)) spell->effect_flags.add(EFFECT_IDX_INVIS);
spell->effect_bitmask += EFFECT_FLAG_INVIS;
} }
else { else {
lua_interface->LogError("%s: LUA Stealth command error: invalid stealth type given", lua_interface->GetScriptName(state)); lua_interface->LogError("%s: LUA Stealth command error: invalid stealth type given", lua_interface->GetScriptName(state));
@ -9480,11 +9452,21 @@ int EQ2Emu_lua_SetIllusion(lua_State* state) {
lua_interface->ResetFunctionStack(state); lua_interface->ResetFunctionStack(state);
if (spell && spell->spell && spell->GetTargetCount() > 0) { if (spell && spell->spell && spell->GetTargetCount() > 0) {
spell->effect_flags.add(EFFECT_IDX_ILLUSION);
ZoneServer* zone = spell->zone; ZoneServer* zone = spell->zone;
for (int32 id : spell->GetTargets()) { for (int32 id : spell->GetTargets()) {
Spawn* target = zone->GetSpawnByID(id); Spawn* target = zone->GetSpawnByID(id);
if (target) if (target) {
target->SetIllusionModel(model); if(!target->IsEntity()) {
target->SetIllusionModel(model);
}
else if(model) {
((Entity*)target)->AddIllusionSpell(spell, model);
}
else {
((Entity*)target)->RemoveIllusionSpell(spell);
}
}
} }
} }
else { else {
@ -9492,8 +9474,15 @@ int EQ2Emu_lua_SetIllusion(lua_State* state) {
lua_interface->LogError("%s: LUA SetIllusion command error: spawn is not valid", lua_interface->GetScriptName(state)); lua_interface->LogError("%s: LUA SetIllusion command error: spawn is not valid", lua_interface->GetScriptName(state));
return 0; return 0;
} }
if(!spawn->IsEntity()) {
spawn->SetIllusionModel(model); spawn->SetIllusionModel(model);
}
else if(model) {
((Entity*)spawn)->AddIllusionSpell(nullptr, model);
}
else {
((Entity*)spawn)->RemoveIllusionSpell(spell);
}
} }
return 0; return 0;
@ -9511,8 +9500,12 @@ int EQ2Emu_lua_ResetIllusion(lua_State* state) {
ZoneServer* zone = spell->zone; ZoneServer* zone = spell->zone;
for (int32 id : spell->GetTargets()) { for (int32 id : spell->GetTargets()) {
Spawn* target = zone->GetSpawnByID(id); Spawn* target = zone->GetSpawnByID(id);
if (target) if (target) {
target->SetIllusionModel(0); if(target->IsEntity())
((Entity*)target)->RemoveIllusionSpell(spell);
else
target->SetIllusionModel(0);
}
} }
} }
else { else {
@ -9520,8 +9513,10 @@ int EQ2Emu_lua_ResetIllusion(lua_State* state) {
lua_interface->LogError("%s: LUA ResetIllusion command error: spawn is not valid", lua_interface->GetScriptName(state)); lua_interface->LogError("%s: LUA ResetIllusion command error: spawn is not valid", lua_interface->GetScriptName(state));
return 0; return 0;
} }
if(spawn->IsEntity())
spawn->SetIllusionModel(0); ((Entity*)spawn)->RemoveIllusionSpell(spell);
else
spawn->SetIllusionModel(0);
} }
return 0; return 0;
@ -14914,3 +14909,44 @@ int EQ2Emu_lua_RemoveCharacterProperty(lua_State* state) {
query.AddQueryAsync(((Player*)player)->GetCharacterID(), &database, Q_DELETE, "delete from character_properties where charid = %u and propname='%s'", ((Player*)player)->GetCharacterID(), propname.c_str()); query.AddQueryAsync(((Player*)player)->GetCharacterID(), &database, Q_DELETE, "delete from character_properties where charid = %u and propname='%s'", ((Player*)player)->GetCharacterID(), propname.c_str());
return 0; return 0;
} }
int EQ2Emu_lua_RemoveSpell(lua_State* state) {
Spawn* spawn = lua_interface->GetSpawn(state);
int32 spell_id = lua_interface->GetInt32Value(state, 2);
bool maintained_spell = lua_interface->GetBooleanValue(state, 3);
string reason = lua_interface->GetStringValue(state, 4);
lua_interface->ResetFunctionStack(state);
if(!spawn || !spawn->IsEntity()) {
lua_interface->LogError("%s: LUA RemoveSpell command error: entity is not valid", lua_interface->GetScriptName(state));
return 0;
}
if(!spawn->GetZone()) {
lua_interface->LogError("%s: LUA RemoveSpell command error: no valid zone", lua_interface->GetScriptName(state));
return 0;
}
if(!spell_id) {
lua_interface->LogError("%s: LUA RemoveSpell command error: spell id is not valid", lua_interface->GetScriptName(state));
return 0;
}
Entity* ent = (Entity*)spawn;
if(reason.length() < 1)
reason = "canceled";
if(maintained_spell) {
MaintainedEffects* effect = ent->GetMaintainedSpell(spell_id);
if(effect && effect->spell) {
ent->GetZone()->GetSpellProcess()->DeleteCasterSpell(effect->spell, reason, false);
}
}
else {
SpellEffects* effect = ent->GetSpellEffect(spell_id);
if(effect && effect->spell) {
ent->GetZone()->GetSpellProcess()->DeleteCasterSpell(effect->spell, reason, false, spawn);
}
}
return 0;
}

View File

@ -699,4 +699,5 @@ int EQ2Emu_lua_InSameRaid(lua_State* state);
int EQ2Emu_lua_GetRaid(lua_State* state); int EQ2Emu_lua_GetRaid(lua_State* state);
int EQ2Emu_lua_AdjustHatePosition(lua_State* state); int EQ2Emu_lua_AdjustHatePosition(lua_State* state);
int EQ2Emu_lua_RemoveCharacterProperty(lua_State* state); int EQ2Emu_lua_RemoveCharacterProperty(lua_State* state);
int EQ2Emu_lua_RemoveSpell(lua_State* state);
#endif #endif

View File

@ -1839,6 +1839,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state,"GetRaid", EQ2Emu_lua_GetRaid); lua_register(state,"GetRaid", EQ2Emu_lua_GetRaid);
lua_register(state,"AdjustHatePosition", EQ2Emu_lua_AdjustHatePosition); lua_register(state,"AdjustHatePosition", EQ2Emu_lua_AdjustHatePosition);
lua_register(state,"RemoveCharacterProperty", EQ2Emu_lua_RemoveCharacterProperty); lua_register(state,"RemoveCharacterProperty", EQ2Emu_lua_RemoveCharacterProperty);
lua_register(state,"RemoveSpell", EQ2Emu_lua_RemoveSpell);
} }
void LuaInterface::LogError(const char* error, ...) { void LuaInterface::LogError(const char* error, ...) {
@ -2320,7 +2321,6 @@ LuaSpell* LuaInterface::LoadSpellScript(const char* name) {
spell->had_dmg_remaining = false; spell->had_dmg_remaining = false;
spell->slot_pos = 0; spell->slot_pos = 0;
spell->damage_remaining = 0; spell->damage_remaining = 0;
spell->effect_bitmask = 0;
spell->restored = false; spell->restored = false;
spell->has_proc = false; spell->has_proc = false;
spell->initial_caster_char_id = 0; spell->initial_caster_char_id = 0;
@ -2670,7 +2670,6 @@ LuaSpell* LuaInterface::CreateSpellScript(const char* name, lua_State* existStat
new_spell->had_dmg_remaining = false; new_spell->had_dmg_remaining = false;
new_spell->slot_pos = 0; new_spell->slot_pos = 0;
new_spell->damage_remaining = 0; new_spell->damage_remaining = 0;
new_spell->effect_bitmask = 0;
new_spell->caster = 0; new_spell->caster = 0;
new_spell->initial_target = 0; new_spell->initial_target = 0;
new_spell->spell = 0; new_spell->spell = 0;

View File

@ -31,6 +31,7 @@
#include "Quests.h" #include "Quests.h"
#include "zoneserver.h" #include "zoneserver.h"
#include "client.h" #include "client.h"
#include "EffectFlags.h"
#include <lua.hpp> #include <lua.hpp>
@ -116,7 +117,7 @@ struct LuaSpell{
bool had_triggers; bool had_triggers;
bool had_dmg_remaining; bool had_dmg_remaining;
Mutex MScriptMutex; Mutex MScriptMutex;
int32 effect_bitmask; EffectFlags effect_flags;
bool restored; // restored spell cross zone bool restored; // restored spell cross zone
std::atomic<bool> has_proc; std::atomic<bool> has_proc;
ZoneServer* zone; ZoneServer* zone;

View File

@ -7509,12 +7509,12 @@ void Player::SaveSpellEffects()
continue; continue;
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT,
"insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, has_damaged, custom_function, caster_level) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s', %u)", "insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, has_damaged, custom_function, caster_level) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %llu, %u, %u, %u, %u, %u, %u, %u, %u, '%s', %u)",
database.getSafeEscapeString(info->spell_effects[i].spell->spell->GetName()).c_str(), caster_char_id, database.getSafeEscapeString(info->spell_effects[i].spell->spell->GetName()).c_str(), caster_char_id,
target_char_id, 0 /*no target_type for spell_effects*/, DB_TYPE_SPELLEFFECTS /* db_effect_type for spell_effects */, info->spell_effects[i].spell->spell->IsCopiedSpell() ? info->spell_effects[i].spell->spell->GetSpellData()->inherited_spell_id : info->spell_effects[i].spell_id, i, info->spell_effects[i].spell->slot_pos, target_char_id, 0 /*no target_type for spell_effects*/, DB_TYPE_SPELLEFFECTS /* db_effect_type for spell_effects */, info->spell_effects[i].spell->spell->IsCopiedSpell() ? info->spell_effects[i].spell->spell->GetSpellData()->inherited_spell_id : info->spell_effects[i].spell_id, i, info->spell_effects[i].spell->slot_pos,
info->spell_effects[i].icon, info->spell_effects[i].icon_backdrop, 0 /* no conc_used for spell_effects */, info->spell_effects[i].tier, info->spell_effects[i].icon, info->spell_effects[i].icon_backdrop, 0 /* no conc_used for spell_effects */, info->spell_effects[i].tier,
info->spell_effects[i].total_time, timestamp, database.getSafeEscapeString(info->spell_effects[i].spell->file_name.c_str()).c_str(), info->spell_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(), info->spell_effects[i].total_time, timestamp, database.getSafeEscapeString(info->spell_effects[i].spell->file_name.c_str()).c_str(), info->spell_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(),
info->spell_effects[i].spell->damage_remaining, info->spell_effects[i].spell->effect_bitmask, info->spell_effects[i].spell->num_triggers, info->spell_effects[i].spell->had_triggers, info->spell_effects[i].spell->cancel_after_all_triggers, info->spell_effects[i].spell->damage_remaining, info->spell_effects[i].spell->effect_flags.export_u64(), info->spell_effects[i].spell->num_triggers, info->spell_effects[i].spell->had_triggers, info->spell_effects[i].spell->cancel_after_all_triggers,
info->spell_effects[i].spell->crit, info->spell_effects[i].spell->last_spellattack_hit, info->spell_effects[i].spell->interrupted, info->spell_effects[i].spell->resisted, info->spell_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->spell_effects[i].spell).c_str()).c_str(), info->spell_effects[i].spell->initial_caster_level); info->spell_effects[i].spell->crit, info->spell_effects[i].spell->last_spellattack_hit, info->spell_effects[i].spell->interrupted, info->spell_effects[i].spell->resisted, info->spell_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->spell_effects[i].spell).c_str()).c_str(), info->spell_effects[i].spell->initial_caster_level);
SaveCustomSpellFields(info->spell_effects[i].spell); SaveCustomSpellFields(info->spell_effects[i].spell);
@ -7542,11 +7542,11 @@ void Player::SaveSpellEffects()
if(info->maintained_effects[i].spell->spell->GetSpellData() && !info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel) if(info->maintained_effects[i].spell->spell->GetSpellData() && !info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
timestamp = info->maintained_effects[i].expire_timestamp - Timer::GetCurrentTime2(); timestamp = info->maintained_effects[i].expire_timestamp - Timer::GetCurrentTime2();
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT,
"insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, has_damaged, custom_function, caster_level) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s', %u)", "insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, has_damaged, custom_function, caster_level) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %llu, %u, %u, %u, %u, %u, %u, %u, %u, '%s', %u)",
database.getSafeEscapeString(info->maintained_effects[i].name).c_str(), caster_char_id, target_char_id, info->maintained_effects[i].target_type, DB_TYPE_MAINTAINEDEFFECTS /* db_effect_type for maintained_effects */, info->maintained_effects[i].spell->spell->IsCopiedSpell() ? info->maintained_effects[i].spell->spell->GetSpellData()->inherited_spell_id : info->maintained_effects[i].spell_id, i, info->maintained_effects[i].slot_pos, database.getSafeEscapeString(info->maintained_effects[i].name).c_str(), caster_char_id, target_char_id, info->maintained_effects[i].target_type, DB_TYPE_MAINTAINEDEFFECTS /* db_effect_type for maintained_effects */, info->maintained_effects[i].spell->spell->IsCopiedSpell() ? info->maintained_effects[i].spell->spell->GetSpellData()->inherited_spell_id : info->maintained_effects[i].spell_id, i, info->maintained_effects[i].slot_pos,
info->maintained_effects[i].icon, info->maintained_effects[i].icon_backdrop, info->maintained_effects[i].conc_used, info->maintained_effects[i].tier, info->maintained_effects[i].icon, info->maintained_effects[i].icon_backdrop, info->maintained_effects[i].conc_used, info->maintained_effects[i].tier,
info->maintained_effects[i].total_time, timestamp, database.getSafeEscapeString(info->maintained_effects[i].spell->file_name.c_str()).c_str(), info->maintained_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(), info->maintained_effects[i].total_time, timestamp, database.getSafeEscapeString(info->maintained_effects[i].spell->file_name.c_str()).c_str(), info->maintained_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(),
info->maintained_effects[i].spell->damage_remaining, info->maintained_effects[i].spell->effect_bitmask, info->maintained_effects[i].spell->num_triggers, info->maintained_effects[i].spell->had_triggers, info->maintained_effects[i].spell->cancel_after_all_triggers, info->maintained_effects[i].spell->damage_remaining, info->maintained_effects[i].spell->effect_flags.export_u64(), info->maintained_effects[i].spell->num_triggers, info->maintained_effects[i].spell->had_triggers, info->maintained_effects[i].spell->cancel_after_all_triggers,
info->maintained_effects[i].spell->crit, info->maintained_effects[i].spell->last_spellattack_hit, info->maintained_effects[i].spell->interrupted, info->maintained_effects[i].spell->resisted, info->maintained_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->maintained_effects[i].spell).c_str()).c_str(), info->maintained_effects[i].spell->initial_caster_level); info->maintained_effects[i].spell->crit, info->maintained_effects[i].spell->last_spellattack_hit, info->maintained_effects[i].spell->interrupted, info->maintained_effects[i].spell->resisted, info->maintained_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->maintained_effects[i].spell).c_str()).c_str(), info->maintained_effects[i].spell->initial_caster_level);
SaveCustomSpellFields(info->maintained_effects[i].spell); SaveCustomSpellFields(info->maintained_effects[i].spell);

View File

@ -8270,7 +8270,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
int8 custom_spell = result.GetInt32Str("custom_spell"); int8 custom_spell = result.GetInt32Str("custom_spell");
int32 damage_remaining = result.GetInt32Str("damage_remaining"); int32 damage_remaining = result.GetInt32Str("damage_remaining");
int32 effect_bitmask = result.GetInt32Str("effect_bitmask"); int64 effect_bitmask = result.GetInt64Str("effect_bitmask");
int16 num_triggers = result.GetInt32Str("num_triggers"); int16 num_triggers = result.GetInt32Str("num_triggers");
int8 had_triggers = result.GetInt32Str("had_triggers"); int8 had_triggers = result.GetInt32Str("had_triggers");
int8 cancel_after_triggers = result.GetInt32Str("cancel_after_triggers"); int8 cancel_after_triggers = result.GetInt32Str("cancel_after_triggers");
@ -8394,7 +8394,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
{ {
lua_spell->crit = crit; lua_spell->crit = crit;
lua_spell->damage_remaining = damage_remaining; lua_spell->damage_remaining = damage_remaining;
lua_spell->effect_bitmask = effect_bitmask; lua_spell->effect_flags.import_u64(effect_bitmask);
lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false; lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false;
lua_spell->had_triggers = had_triggers; lua_spell->had_triggers = had_triggers;
lua_spell->initial_caster_char_id = caster_char_id; lua_spell->initial_caster_char_id = caster_char_id;