Fix #38 address issues with food/drink cross zone

Custom spells now survive cross zone with their modified spell stats saved to the database for reloading later.  The design is such that a spell can be custom defined for its spell stats, but a character/player cannot have more than one of that spell (eg. food can share a spell id since only one food can be applied to you, but you can't have many spells apply using the same custom spell id).

CREATE TABLE character_custom_spell_dataindex (
    charid INT UNSIGNED NOT NULL,
    spell_id INT UNSIGNED NOT NULL,
    idx INT UNSIGNED NOT NULL,
    type ENUM('int', 'float', 'bool', 'string') NOT NULL,
    value1 TEXT,
    value2 TEXT,
    PRIMARY KEY (charid, spell_id, idx)
);

CREATE TABLE character_custom_spell_display (
    charid INT UNSIGNED NOT NULL,
    spell_id INT UNSIGNED NOT NULL,
    idx INT UNSIGNED NOT NULL,
    field VARCHAR(64) NOT NULL,
    value TEXT,
    PRIMARY KEY (charid, spell_id, idx, field)
);

CREATE TABLE character_custom_spell_data (
    charid INT UNSIGNED NOT NULL,
    spell_id INT UNSIGNED NOT NULL,
    field VARCHAR(64) NOT NULL,
    type ENUM('int', 'float', 'bool', 'string') NOT NULL,
    value TEXT NOT NULL,
    PRIMARY KEY (charid, spell_id, field)
);
This commit is contained in:
Emagi 2025-06-26 18:27:35 -04:00
parent 11a6a80647
commit 260a48be1e
11 changed files with 551 additions and 33 deletions

View File

@ -127,6 +127,7 @@ void Entity::DeleteSpellEffects(bool removeClient)
if (IsPlayer())
GetInfoStruct()->maintained_effects[i].icon = 0xFFFF;
GetInfoStruct()->maintained_effects[i].spell_id = 0xFFFFFFFF;
GetInfoStruct()->maintained_effects[i].inherited_spell_id = 0;
GetInfoStruct()->maintained_effects[i].spell = nullptr;
}
}
@ -139,6 +140,7 @@ void Entity::DeleteSpellEffects(bool removeClient)
}
}
GetInfoStruct()->spell_effects[i].spell_id = 0xFFFFFFFF;
GetInfoStruct()->spell_effects[i].inherited_spell_id = 0;
GetInfoStruct()->spell_effects[i].icon = 0;
GetInfoStruct()->spell_effects[i].icon_backdrop = 0;
GetInfoStruct()->spell_effects[i].tier = 0;
@ -1177,6 +1179,7 @@ void Entity::RemoveMaintainedSpell(LuaSpell* luaspell){
if (found) {
memset(&GetInfoStruct()->maintained_effects[29], 0, sizeof(MaintainedEffects));
GetInfoStruct()->maintained_effects[29].spell_id = 0xFFFFFFFF;
GetInfoStruct()->maintained_effects[29].inherited_spell_id = 0;
GetInfoStruct()->maintained_effects[29].icon = 0xFFFF;
GetInfoStruct()->maintained_effects[29].spell = nullptr;
}
@ -1198,6 +1201,7 @@ void Entity::RemoveSpellEffect(LuaSpell* spell) {
GetZone()->GetSpellProcess()->RemoveTargetFromSpell(spell, this);
memset(&GetInfoStruct()->spell_effects[44], 0, sizeof(SpellEffects));
GetInfoStruct()->spell_effects[44].spell_id = 0xFFFFFFFF;
GetInfoStruct()->spell_effects[44].inherited_spell_id = 0;
GetInfoStruct()->spell_effects[44].spell = nullptr;
changed = true;
info_changed = true;
@ -1227,12 +1231,12 @@ MaintainedEffects* Entity::GetFreeMaintainedSpellSlot(){
return ret;
}
MaintainedEffects* Entity::GetMaintainedSpell(int32 spell_id){
MaintainedEffects* Entity::GetMaintainedSpell(int32 spell_id, bool on_char_load){
MaintainedEffects* ret = 0;
InfoStruct* info = GetInfoStruct();
MMaintainedSpells.readlock(__FUNCTION__, __LINE__);
for (int i = 0; i<NUM_MAINTAINED_EFFECTS; i++){
if (info->maintained_effects[i].spell_id == spell_id){
if (info->maintained_effects[i].spell_id == spell_id || (on_char_load && info->maintained_effects[i].inherited_spell_id == id)){
ret = &info->maintained_effects[i];
break;
}
@ -1256,12 +1260,12 @@ SpellEffects* Entity::GetFreeSpellEffectSlot(){
return ret;
}
SpellEffects* Entity::GetSpellEffect(int32 id, Entity* caster) {
SpellEffects* Entity::GetSpellEffect(int32 id, Entity* caster, bool on_char_load) {
SpellEffects* ret = 0;
InfoStruct* info = GetInfoStruct();
MSpellEffects.readlock(__FUNCTION__, __LINE__);
for(int i = 0; i < 45; i++) {
if(info->spell_effects[i].spell_id == id) {
if(info->spell_effects[i].spell_id == id || (on_char_load && info->maintained_effects[i].inherited_spell_id == id)) {
if (!caster || info->spell_effects[i].caster == caster){
ret = &info->spell_effects[i];
break;
@ -2743,6 +2747,7 @@ void Entity::AddDetrimentalSpell(LuaSpell* luaspell, int32 override_expire_times
new_det.det_type = data->det_type;
new_det.incurable = data->incurable;
new_det.spell_id = spell->GetSpellID();
new_det.inherited_spell_id = data->inherited_spell_id;
new_det.control_effect = data->control_effect_type;
new_det.total_time = spell->GetSpellDuration()/10;

View File

@ -55,6 +55,7 @@ struct MaintainedEffects{
int32 target;
int8 target_type;
int32 spell_id;
int32 inherited_spell_id;
int32 slot_pos;
int16 icon;
int16 icon_backdrop;
@ -67,6 +68,7 @@ struct MaintainedEffects{
struct SpellEffects{
int32 spell_id;
int32 inherited_spell_id;
Entity* caster;
float total_time;
int32 expire_timestamp;
@ -78,6 +80,7 @@ struct SpellEffects{
struct DetrimentalEffects {
int32 spell_id;
int32 inherited_spell_id;
Entity* caster;
int32 expire_timestamp;
int16 icon;
@ -1067,6 +1070,7 @@ struct InfoStruct{
for(int i=0;i<45;i++){
if(i<30){
maintained_effects[i].spell_id = 0xFFFFFFFF;
maintained_effects[i].inherited_spell_id = 0;
if (spawn->IsPlayer())
maintained_effects[i].icon = 0xFFFF;
@ -1074,6 +1078,7 @@ struct InfoStruct{
}
spell_effects[i].icon = 0;
spell_effects[i].spell_id = 0xFFFFFFFF;
spell_effects[i].inherited_spell_id = 0;
spell_effects[i].icon_backdrop = 0;
spell_effects[i].tier = 0;
spell_effects[i].total_time = 0.0f;
@ -1425,7 +1430,7 @@ public:
virtual void AddSkillBonus(int32 spell_id, int32 skill_id, float value);
void AddDetrimentalSpell(LuaSpell* spell, int32 override_expire_timestamp = 0);
DetrimentalEffects* GetDetrimentalEffect(int32 spell_id, Entity* caster);
virtual MaintainedEffects* GetMaintainedSpell(int32 spell_id);
virtual MaintainedEffects* GetMaintainedSpell(int32 spell_id, bool on_char_load = false);
void RemoveDetrimentalSpell(LuaSpell* spell);
void SetDeity(int8 new_deity){
deity = new_deity;
@ -1489,7 +1494,7 @@ public:
void DoRegenUpdate();
MaintainedEffects* GetFreeMaintainedSpellSlot();
SpellEffects* GetFreeSpellEffectSlot();
SpellEffects* GetSpellEffect(int32 id, Entity* caster = 0);
SpellEffects* GetSpellEffect(int32 id, Entity* caster = 0, bool on_char_load = false);
SpellEffects* GetSpellEffectBySpellType(int8 spell_type);
SpellEffects* GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer = 0, sint32 type_group_spell_id = 0, Entity* caster = 0);
LuaSpell* HasLinkedTimerID(LuaSpell* spell, Spawn* target = nullptr, bool stackWithOtherPlayers = true);

View File

@ -12436,12 +12436,16 @@ int EQ2Emu_lua_SetSpellData(lua_State* state) {
boost::to_lower(field);
bool valSet = false;
bool setVal = false;
spell->spell->SetSpellData(state, field, fieldArg);
setVal = spell->spell->SetSpellData(state, field, fieldArg);
lua_interface->ResetFunctionStack(state);
if(setVal) {
spell->MarkFieldModified(field);
}
lua_interface->SetBooleanValue(state, setVal);
return valSet;
return 1;
}
int EQ2Emu_lua_SetSpellDataIndex(lua_State* state) {
@ -12510,7 +12514,13 @@ int EQ2Emu_lua_SetSpellDataIndex(lua_State* state) {
lua_interface->ResetFunctionStack(state);
return setVal;
lua_interface->SetBooleanValue(state, setVal);
if(setVal) {
data->needs_db_save = true;
}
return 1;
}
@ -12607,22 +12617,30 @@ int EQ2Emu_lua_SetSpellDisplayEffect(lua_State* state) {
return 0;
}
bool setVal = true;
// do we need to lock? eh probably not this should only be used before use of the custom spell
SpellDisplayEffect* effect = spell->spell->effects[idx];
if (field == "description")
if (field == "description") {
effect->description = string(lua_interface->GetStringValue(state, 4));
else if (field == "bullet")
effect->needs_db_save = true;
}
else if (field == "bullet") {
effect->subbullet = lua_interface->GetInt8Value(state, 4);
else if (field == "percentage")
effect->needs_db_save = true;
}
else if (field == "percentage") {
effect->percentage = lua_interface->GetInt8Value(state, 4);
else { // no match
lua_interface->ResetFunctionStack(state);
return 0;
effect->needs_db_save = true;
}
else {
setVal = false;
}
lua_interface->ResetFunctionStack(state);
lua_interface->SetBooleanValue(state, setVal);
return 1;
}

View File

@ -39,6 +39,149 @@ extern WorldDatabase database;
extern ZoneList zone_list;
const std::unordered_map<std::string, std::pair<SpellFieldType, SpellFieldGetter>> SpellDataFieldAccessors = {
{"spell_book_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_book_type); }}},
{"icon", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->icon); }}},
{"icon_heroic_op", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->icon_heroic_op); }}},
{"icon_backdrop", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->icon_backdrop); }}},
{"type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->type); }}},
{"class_skill", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->class_skill); }}},
{"min_class_skill_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->min_class_skill_req); }}},
{"mastery_skill", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->mastery_skill); }}},
{"ts_loc_index", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->ts_loc_index); }}},
{"num_levels", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->num_levels); }}},
{"tier", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->tier); }}},
{"hp_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->hp_req); }}},
{"hp_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->hp_upkeep); }}},
{"power_req", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->power_req); }}},
{"power_by_level", {SpellFieldType::Boolean, [](SpellData* d) { return d->power_by_level ? "1" : "0"; }}},
{"power_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->power_upkeep); }}},
{"savagery_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savagery_req); }}},
{"savagery_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savagery_upkeep); }}},
{"dissonance_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->dissonance_req); }}},
{"dissonance_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->dissonance_upkeep); }}},
{"target_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->target_type); }}},
{"cast_time", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->cast_time); }}},
{"recovery", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->recovery); }}},
{"recast", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->recast); }}},
{"linked_timer", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->linked_timer); }}},
{"radius", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->radius); }}},
{"max_aoe_targets", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->max_aoe_targets); }}},
{"friendly_spell", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->friendly_spell); }}},
{"req_concentration", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->req_concentration); }}},
{"range", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->range); }}},
{"duration1", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->duration1); }}},
{"duration2", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->duration2); }}},
{"resistibility", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->resistibility); }}},
{"duration_until_cancel", {SpellFieldType::Boolean, [](SpellData* d) { return d->duration_until_cancel ? "1" : "0"; }}},
{"power_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->power_req_percent); }}},
{"hp_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->hp_req_percent); }}},
{"savagery_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savagery_req_percent); }}},
{"dissonance_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->dissonance_req_percent); }}},
{"name", {SpellFieldType::String, [](SpellData* d) { return d->name.data; }}},
{"description", {SpellFieldType::String, [](SpellData* d) { return d->description.data; }}},
{"success_message", {SpellFieldType::String, [](SpellData* d) { return d->success_message; }}},
{"fade_message", {SpellFieldType::String, [](SpellData* d) { return d->fade_message; }}},
{"fade_message_others", {SpellFieldType::String, [](SpellData* d) { return d->fade_message_others; }}},
{"cast_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->cast_type).c_str(); }}},
{"lua_script", {SpellFieldType::String, [](SpellData* d) { return d->lua_script; }}},
{"interruptable", {SpellFieldType::Boolean, [](SpellData* d) { return d->interruptable ? "1" : "0"; }}},
{"spell_visual", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_visual); }}},
{"effect_message", {SpellFieldType::String, [](SpellData* d) { return d->effect_message; }}},
{"min_range", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->min_range); }}},
{"can_effect_raid", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->can_effect_raid); }}},
{"affect_only_group_members", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->affect_only_group_members); }}},
{"group_spell", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->group_spell); }}},
{"hit_bonus", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->hit_bonus); }}},
{"display_spell_tier", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->display_spell_tier); }}},
{"is_active", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->is_active); }}},
{"det_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->det_type); }}},
{"incurable", {SpellFieldType::Boolean, [](SpellData* d) { return d->incurable ? "1" : "0"; }}},
{"control_effect_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->control_effect_type); }}},
{"casting_flags", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->casting_flags); }}},
{"cast_while_moving", {SpellFieldType::Boolean, [](SpellData* d) { return d->cast_while_moving ? "1" : "0"; }}},
{"persist_through_death", {SpellFieldType::Boolean, [](SpellData* d) { return d->persist_through_death ? "1" : "0"; }}},
{"not_maintained", {SpellFieldType::Boolean, [](SpellData* d) { return d->not_maintained ? "1" : "0"; }}},
{"is_aa", {SpellFieldType::Boolean, [](SpellData* d) { return d->is_aa ? "1" : "0"; }}},
{"savage_bar", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savage_bar); }}},
{"savage_bar_slot", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savage_bar_slot); }}},
{"soe_spell_crc", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->soe_spell_crc); }}},
{"spell_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_type); }}},
{"spell_name_crc", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_name_crc); }}},
{"type_group_spell_id", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->type_group_spell_id); }}},
{"can_fizzle", {SpellFieldType::Boolean, [](SpellData* d) { return d->can_fizzle ? "1" : "0"; }}}
};
const std::unordered_map<std::string, std::function<void(Spell*, const std::string&)>> SpellFieldGenericSetters = {
{ "spell_book_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->spell_book_type = static_cast<int32>(std::stoi(val)); } },
{ "icon", [](Spell* spell, const std::string& val) { spell->GetSpellData()->icon = static_cast<sint16>(std::stoi(val)); } },
{ "icon_heroic_op", [](Spell* spell, const std::string& val) { spell->GetSpellData()->icon_heroic_op = static_cast<int16>(std::stoi(val)); } },
{ "icon_backdrop", [](Spell* spell, const std::string& val) { spell->GetSpellData()->icon_backdrop = static_cast<int16>(std::stoi(val)); } },
{ "type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->type = static_cast<int16>(std::stoi(val)); } },
{ "class_skill", [](Spell* spell, const std::string& val) { spell->GetSpellData()->class_skill = static_cast<int32>(std::stoi(val)); } },
{ "min_class_skill_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->min_class_skill_req = static_cast<int16>(std::stoi(val)); } },
{ "mastery_skill", [](Spell* spell, const std::string& val) { spell->GetSpellData()->mastery_skill = static_cast<int32>(std::stoi(val)); } },
{ "ts_loc_index", [](Spell* spell, const std::string& val) { spell->GetSpellData()->ts_loc_index = static_cast<int8>(std::stoi(val)); } },
{ "num_levels", [](Spell* spell, const std::string& val) { spell->GetSpellData()->num_levels = static_cast<int8>(std::stoi(val)); } },
{ "tier", [](Spell* spell, const std::string& val) { spell->GetSpellData()->tier = static_cast<int8>(std::stoi(val)); } },
{ "hp_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hp_req = static_cast<int16>(std::stoi(val)); } },
{ "hp_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hp_upkeep = static_cast<int16>(std::stoi(val)); } },
{ "power_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_req = std::stof(val); } },
{ "power_by_level", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_by_level = (val == "1" || val == "true"); } },
{ "power_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_upkeep = static_cast<int16>(std::stoi(val)); } },
{ "savagery_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savagery_req = static_cast<int16>(std::stoi(val)); } },
{ "savagery_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savagery_upkeep = static_cast<int16>(std::stoi(val)); } },
{ "dissonance_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->dissonance_req = static_cast<int16>(std::stoi(val)); } },
{ "dissonance_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->dissonance_upkeep = static_cast<int16>(std::stoi(val)); } },
{ "target_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->target_type = static_cast<int16>(std::stoi(val)); } },
{ "cast_time", [](Spell* spell, const std::string& val) { spell->GetSpellData()->cast_time = static_cast<int16>(std::stoi(val)); } },
{ "recovery", [](Spell* spell, const std::string& val) { spell->GetSpellData()->recovery = std::stof(val); } },
{ "recast", [](Spell* spell, const std::string& val) { spell->GetSpellData()->recast = std::stof(val); } },
{ "linked_timer", [](Spell* spell, const std::string& val) { spell->GetSpellData()->linked_timer = static_cast<int32>(std::stoi(val)); } },
{ "radius", [](Spell* spell, const std::string& val) { spell->GetSpellData()->radius = std::stof(val); } },
{ "max_aoe_targets", [](Spell* spell, const std::string& val) { spell->GetSpellData()->max_aoe_targets = static_cast<int16>(std::stoi(val)); } },
{ "friendly_spell", [](Spell* spell, const std::string& val) { spell->GetSpellData()->friendly_spell = static_cast<int8>(std::stoi(val)); } },
{ "req_concentration", [](Spell* spell, const std::string& val) { spell->GetSpellData()->req_concentration = static_cast<int16>(std::stoi(val)); } },
{ "range", [](Spell* spell, const std::string& val) { spell->GetSpellData()->range = std::stof(val); } },
{ "duration1", [](Spell* spell, const std::string& val) { spell->GetSpellData()->duration1 = static_cast<sint32>(std::stoi(val)); } },
{ "duration2", [](Spell* spell, const std::string& val) { spell->GetSpellData()->duration2 = static_cast<sint32>(std::stoi(val)); } },
{ "resistibility", [](Spell* spell, const std::string& val) { spell->GetSpellData()->resistibility = std::stof(val); } },
{ "duration_until_cancel", [](Spell* spell, const std::string& val) { spell->GetSpellData()->duration_until_cancel = (val == "1" || val == "true"); } },
{ "power_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_req_percent = static_cast<int8>(std::stoi(val)); } },
{ "hp_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hp_req_percent = static_cast<int8>(std::stoi(val)); } },
{ "savagery_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savagery_req_percent = static_cast<int8>(std::stoi(val)); } },
{ "dissonance_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->dissonance_req_percent = static_cast<int8>(std::stoi(val)); } },
{ "name", [](Spell* spell, const std::string& val) { spell->GetSpellData()->name.data = val; } },
{ "description", [](Spell* spell, const std::string& val) { spell->GetSpellData()->description.data = val; } },
{ "success_message", [](Spell* spell, const std::string& val) { spell->GetSpellData()->success_message = val; } },
{ "fade_message", [](Spell* spell, const std::string& val) { spell->GetSpellData()->fade_message = val; } },
{ "fade_message_others", [](Spell* spell, const std::string& val) { spell->GetSpellData()->fade_message_others = val; } },
{ "cast_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->cast_type = static_cast<int8>(std::stoi(val)); } },
{ "call_frequency", [](Spell* spell, const std::string& val) { spell->GetSpellData()->call_frequency = static_cast<int32>(std::stoi(val)); } },
{ "interruptable", [](Spell* spell, const std::string& val) { spell->GetSpellData()->interruptable = (val == "1" || val == "true"); } },
{ "spell_visual", [](Spell* spell, const std::string& val) { spell->GetSpellData()->spell_visual = static_cast<int32>(std::stoi(val)); } },
{ "effect_message", [](Spell* spell, const std::string& val) { spell->GetSpellData()->effect_message = val; } },
{ "min_range", [](Spell* spell, const std::string& val) { spell->GetSpellData()->min_range = std::stof(val); } },
{ "can_effect_raid", [](Spell* spell, const std::string& val) { spell->GetSpellData()->can_effect_raid = static_cast<int8>(std::stoi(val)); } },
{ "affect_only_group_members", [](Spell* spell, const std::string& val) { spell->GetSpellData()->affect_only_group_members = static_cast<int8>(std::stoi(val)); } },
{ "group_spell", [](Spell* spell, const std::string& val) { spell->GetSpellData()->group_spell = static_cast<int8>(std::stoi(val)); } },
{ "hit_bonus", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hit_bonus = std::stof(val); } },
{ "display_spell_tier", [](Spell* spell, const std::string& val) { spell->GetSpellData()->display_spell_tier = static_cast<int8>(std::stoi(val)); } },
{ "is_active", [](Spell* spell, const std::string& val) { spell->GetSpellData()->is_active = static_cast<int8>(std::stoi(val)); } },
{ "det_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->det_type = static_cast<int8>(std::stoi(val)); } },
{ "incurable", [](Spell* spell, const std::string& val) { spell->GetSpellData()->incurable = (val == "1" || val == "true"); } },
{ "control_effect_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->control_effect_type = static_cast<int8>(std::stoi(val)); } },
{ "casting_flags", [](Spell* spell, const std::string& val) { spell->GetSpellData()->casting_flags = static_cast<int32>(std::stoi(val)); } },
{ "cast_while_moving", [](Spell* spell, const std::string& val) { spell->GetSpellData()->cast_while_moving = (val == "1" || val == "true"); } },
{ "persist_through_death", [](Spell* spell, const std::string& val) { spell->GetSpellData()->persist_through_death = (val == "1" || val == "true"); } },
{ "not_maintained", [](Spell* spell, const std::string& val) { spell->GetSpellData()->not_maintained = (val == "1" || val == "true"); } },
{ "is_aa", [](Spell* spell, const std::string& val) { spell->GetSpellData()->is_aa = (val == "1" || val == "true"); } },
{ "savage_bar", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savage_bar = static_cast<int8>(std::stoi(val)); } },
{ "spell_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->spell_type = static_cast<int8>(std::stoi(val)); } },
{ "type_group_spell_id", [](Spell* spell, const std::string& val) { spell->GetSpellData()->type_group_spell_id = static_cast<sint32>(std::stoi(val)); } },
{ "can_fizzle", [](Spell* spell, const std::string& val) { spell->GetSpellData()->can_fizzle = (val == "1" || val == "true"); } },
};
LuaInterface::LuaInterface() {
shutting_down = false;
lua_system_reloading = false;
@ -3067,3 +3210,40 @@ LUASpellWrapper::LUASpellWrapper() {
bool LUASpellWrapper::IsSpell() {
return true;
}
bool LuaSpell::SetSpellDataIndex(int idx, const std::string& value, const std::string& value2) {
if (!spell || spell->lua_data.size() <= idx)
return false;
LUAData* data = spell->lua_data[idx];
if (!data)
return false;
bool setVal = true;
switch (data->type) {
case 0: // int + int
data->int_value = std::stoi(value);
data->int_value2 = std::stoi(value2);
break;
case 1: // float + float
data->float_value = std::stof(value);
data->float_value2 = std::stof(value2);
break;
case 2: // bool
data->bool_value = (value == "1" || value == "true");
break;
case 3: // string + string
data->string_value = value;
data->string_value2 = value2;
break;
default:
setVal = false;
break;
}
if (setVal)
data->needs_db_save = true;
return setVal;
}

View File

@ -22,6 +22,7 @@
#include <mutex>
#include <shared_mutex>
#include <unordered_set>
#include "Spawn.h"
#include "Spells.h"
@ -73,6 +74,18 @@ struct OptionWindowOption {
#define EFFECT_FLAG_FEAR_IMMUNE 2097152
#define EFFECT_FLAG_SAFEFALL 4194304
enum class SpellFieldType {
Integer,
Float,
Boolean,
String
};
using SpellFieldGetter = std::function<std::string(SpellData*)>;
extern const std::unordered_map<std::string, std::function<void(Spell*, const std::string&)>> SpellFieldGenericSetters;
extern const std::unordered_map<std::string, std::pair<SpellFieldType, SpellFieldGetter>> SpellDataFieldAccessors;
extern std::unordered_map<std::string, std::function<void(Spell*, const std::string&)>> SpellDataFieldSetters;
struct LuaSpell{
Entity* caster;
int32 initial_caster_char_id;
@ -108,6 +121,9 @@ struct LuaSpell{
ZoneServer* zone;
int16 initial_caster_level;
std::unordered_set<std::string> modified_fields;
mutable std::shared_mutex spell_modify_mutex;
void AddTarget(int32 target_id) {
std::unique_lock lock(targets_mutex);
targets.push_back(target_id);
@ -212,6 +228,63 @@ struct LuaSpell{
char_id_targets.clear();
}
void MarkFieldModified(const std::string& field) {
std::unique_lock lock(spell_modify_mutex);
modified_fields.insert(field);
}
bool IsFieldModified(const std::string& field) const {
std::shared_lock lock(spell_modify_mutex);
return modified_fields.find(field) != modified_fields.end();
}
void ClearFieldModifications() {
std::unique_lock lock(spell_modify_mutex);
modified_fields.clear();
}
std::unordered_set<std::string> GetModifiedFieldsCopy() const {
std::shared_lock lock(spell_modify_mutex);
return modified_fields; // safe shallow copy
}
bool SetSpellDataGeneric(const std::string& field, int value) {
return SetSpellDataGeneric(field, std::to_string(value));
}
bool SetSpellDataGeneric(const std::string& field, float value) {
return SetSpellDataGeneric(field, std::to_string(value));
}
bool SetSpellDataGeneric(const std::string& field, bool value) {
return SetSpellDataGeneric(field, value ? "1" : "0");
}
bool SetSpellDataGeneric(const std::string& field, const std::string& value) {
auto it = SpellFieldGenericSetters.find(field);
if (it == SpellFieldGenericSetters.end())
return false;
if (!spell)
return false;
it->second(spell, value);
return true;
}
bool SetSpellDataIndex(int idx, const std::string& value, const std::string& value2 = "");
bool SetSpellDataIndex(int idx, int value, int value2) {
return SetSpellDataIndex(idx, std::to_string(value), std::to_string(value2));
}
bool SetSpellDataIndex(int idx, float value, float value2) {
return SetSpellDataIndex(idx, std::to_string(value), std::to_string(value2));
}
bool SetSpellDataIndex(int idx, bool value) {
return SetSpellDataIndex(idx, value ? "1" : "0");
}
};
enum class LuaArgType { SINT64, INT64, SINT, INT, FLOAT, STRING, BOOL, SPAWN, ZONE, SKILL, ITEM, QUEST, SPELL /* etc */ };

View File

@ -3284,10 +3284,12 @@ PlayerInfo::PlayerInfo(Player* in_player){
for(int i=0;i<45;i++){
if(i<30){
info_struct->maintained_effects[i].spell_id = 0xFFFFFFFF;
info_struct->maintained_effects[i].inherited_spell_id = 0;
info_struct->maintained_effects[i].icon = 0xFFFF;
info_struct->maintained_effects[i].spell = nullptr;
}
info_struct->spell_effects[i].spell_id = 0xFFFFFFFF;
info_struct->spell_effects[i].inherited_spell_id = 0;
info_struct->spell_effects[i].icon = 0;
info_struct->spell_effects[i].icon_backdrop = 0;
info_struct->spell_effects[i].tier = 0;
@ -3324,12 +3326,12 @@ MaintainedEffects* Player::GetFreeMaintainedSpellSlot(){
return ret;
}
MaintainedEffects* Player::GetMaintainedSpell(int32 id){
MaintainedEffects* Player::GetMaintainedSpell(int32 id, bool on_char_load){
MaintainedEffects* ret = 0;
InfoStruct* info = GetInfoStruct();
GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__);
for(int i=0;i<NUM_MAINTAINED_EFFECTS;i++){
if(info->maintained_effects[i].spell_id == id){
if(info->maintained_effects[i].spell_id == id || (on_char_load && info->maintained_effects[i].inherited_spell_id == id)){
ret = &info->maintained_effects[i];
break;
}
@ -3560,6 +3562,7 @@ void Player::RemoveMaintainedSpell(LuaSpell* luaspell){
if (found) {
memset(&GetInfoStruct()->maintained_effects[29], 0, sizeof(MaintainedEffects));
GetInfoStruct()->maintained_effects[29].spell_id = 0xFFFFFFFF;
GetInfoStruct()->maintained_effects[29].inherited_spell_id = 0;
GetInfoStruct()->maintained_effects[29].icon = 0xFFFF;
GetInfoStruct()->maintained_effects[29].spell = nullptr;
charsheet_changed = true;
@ -3580,6 +3583,7 @@ void Player::RemoveSpellEffect(LuaSpell* spell){
if (found) {
memset(&GetInfoStruct()->spell_effects[44], 0, sizeof(SpellEffects));
GetInfoStruct()->spell_effects[44].spell_id = 0xFFFFFFFF;
GetInfoStruct()->spell_effects[44].inherited_spell_id = 0;
GetInfoStruct()->spell_effects[44].spell = nullptr;
changed = true;
info_changed = true;
@ -7331,6 +7335,110 @@ NPC* Player::InstantiateSpiritShard(float origX, float origY, float origZ, float
return npc;
}
void Player::SaveCustomSpellFields(LuaSpell* luaspell) {
if (!luaspell || !luaspell->spell || !luaspell->spell->IsCopiedSpell())
return;
auto spell_data = luaspell->spell->GetSpellData();
std::unordered_set<std::string> modified_fields = luaspell->GetModifiedFieldsCopy();
Query savedEffects;
for (const std::string& field : modified_fields) {
auto it = SpellDataFieldAccessors.find(field);
if (it == SpellDataFieldAccessors.end())
continue;
const auto& [type, getter] = it->second;
std::string value = getter(spell_data);
std::string type_str;
switch (type) {
case SpellFieldType::Integer: type_str = "int"; break;
case SpellFieldType::Float: type_str = "float"; break;
case SpellFieldType::Boolean: type_str = "bool"; break;
case SpellFieldType::String: type_str = "string"; break;
default: continue;
}
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT INTO character_custom_spell_data (charid, spell_id, field, type, value) VALUES (%u, %u, '%s', '%s', '%s')",
GetCharacterID(),
luaspell->spell->GetSpellData()->inherited_spell_id,
database.getSafeEscapeString(field.c_str()).c_str(),
type_str.c_str(),
database.getSafeEscapeString(value.c_str()).c_str());
}
}
void Player::SaveCustomSpellDataIndex(LuaSpell* luaspell) {
if (!luaspell || !luaspell->spell || !luaspell->spell->IsCopiedSpell())
return;
auto& vec = luaspell->spell->lua_data;
Query savedEffects;
for (int i = 0; i < vec.size(); ++i) {
LUAData* data = vec[i];
if (!data || !data->needs_db_save)
continue;
std::string value1, value2, type;
switch (data->type) {
case 0:
value1 = std::to_string(data->int_value);
value2 = std::to_string(data->int_value2);
type = "int";
break;
case 1:
value1 = std::to_string(data->float_value);
value2 = std::to_string(data->float_value2);
type = "float";
break;
case 2:
value1 = data->bool_value ? "1" : "0";
type = "bool";
break;
case 3:
value1 = database.getSafeEscapeString(data->string_value.c_str());
value2 = database.getSafeEscapeString(data->string_value2.c_str());
type = "string";
break;
default:
continue;
}
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT INTO character_custom_spell_dataindex (charid, spell_id, idx, type, value1, value2) VALUES (%u, %u, %d, '%s', '%s', '%s')", GetCharacterID(),
luaspell->spell->GetSpellData()->inherited_spell_id,
i,
type.c_str(), value1.c_str(), value2.c_str());
}
}
void Player::SaveCustomSpellEffectsDisplay(LuaSpell* luaspell) {
if (!luaspell || !luaspell->spell || !luaspell->spell->IsCopiedSpell())
return;
auto& vec = luaspell->spell->effects;
Query savedEffects;
for (int i = 0; i < vec.size(); ++i) {
SpellDisplayEffect* eff = vec[i];
if (!eff || !eff->needs_db_save)
continue;
std::string charid = std::to_string(GetCharacterID());
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT INTO character_custom_spell_display (charid, spell_id, idx, field, value) VALUES (%u, %u, %d, 'description', '%s')",
GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id, i,
database.getSafeEscapeString(eff->description.c_str()).c_str());
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT INTO character_custom_spell_display (charid, spell_id, idx, field, value) VALUES (%u, %u, %d, 'bullet', '%d')",
GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id, i, eff->subbullet);
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT INTO character_custom_spell_display (charid, spell_id, idx, field, value) VALUES (%u, %u, %d, 'percentage', '%d')",
GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id, i, eff->percentage);
}
}
void Player::SaveSpellEffects()
{
if(stop_save_spell_effects)
@ -7346,6 +7454,9 @@ void Player::SaveSpellEffects()
Query savedEffects;
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_spell_effects where charid=%u", GetCharacterID());
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_spell_effect_targets where caster_char_id=%u", GetCharacterID());
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_dataindex where charid=%u", GetCharacterID());
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_display where charid=%u", GetCharacterID());
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_data where charid=%u", GetCharacterID());
InfoStruct* info = GetInfoStruct();
MSpellEffects.readlock(__FUNCTION__, __LINE__);
MMaintainedSpells.readlock(__FUNCTION__, __LINE__);
@ -7376,6 +7487,10 @@ void Player::SaveSpellEffects()
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->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);
SaveCustomSpellDataIndex(info->spell_effects[i].spell);
SaveCustomSpellEffectsDisplay(info->spell_effects[i].spell);
}
if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell_id != 0xFFFFFFFF){
Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->initial_target);
@ -7404,6 +7519,10 @@ void Player::SaveSpellEffects()
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->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);
SaveCustomSpellDataIndex(info->maintained_effects[i].spell);
SaveCustomSpellEffectsDisplay(info->maintained_effects[i].spell);
std::string insertTargets = string("insert into character_spell_effect_targets (caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos) values ");
bool firstTarget = true;
map<Spawn*, int8> targetsInserted;

View File

@ -589,7 +589,7 @@ public:
EQ2Packet* MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, bool* item_deleted, int16 version = 1);
bool IsPlayer(){ return true; }
MaintainedEffects* GetFreeMaintainedSpellSlot();
MaintainedEffects* GetMaintainedSpell(int32 id);
MaintainedEffects* GetMaintainedSpell(int32 id, bool on_char_load = false);
MaintainedEffects* GetMaintainedSpellBySlot(int8 slot);
MaintainedEffects* GetMaintainedSpells();
SpellEffects* GetFreeSpellEffectSlot();
@ -1050,6 +1050,9 @@ public:
void DismissAllPets();
void SaveSpellEffects();
void SaveCustomSpellFields(LuaSpell* luaspell);
void SaveCustomSpellDataIndex(LuaSpell* luaspell);
void SaveCustomSpellEffectsDisplay(LuaSpell* luaspell);
void SetSaveSpellEffects(bool val) { stop_save_spell_effects = val; }
AppearanceData SavedApp;

View File

@ -222,7 +222,7 @@ void Spell::AddSpellLuaData(int8 type, int int_value, int int_value2, float floa
data->string_value = string_value;
data->string_value2 = string_value2;
data->string_helper = helper;
data->needs_db_save = false;
lua_data.push_back(data);
}
@ -237,6 +237,7 @@ void Spell::AddSpellLuaDataInt(int value, int value2, string helper) {
data->float_value2 = 0;
data->bool_value = false;
data->string_helper = helper;
data->needs_db_save = false;
lua_data.push_back(data);
}
@ -252,6 +253,7 @@ void Spell::AddSpellLuaDataFloat(float value, float value2, string helper) {
data->float_value2 = value2;
data->bool_value = false;
data->string_helper = helper;
data->needs_db_save = false;
lua_data.push_back(data);
}
@ -265,6 +267,7 @@ void Spell::AddSpellLuaDataBool(bool value, string helper) {
data->float_value = 0;
data->bool_value = value;
data->string_helper = helper;
data->needs_db_save = false;
lua_data.push_back(data);
}
@ -282,6 +285,7 @@ void Spell::AddSpellLuaDataString(string value, string value2,string helper) {
data->string_value = value;
data->string_value2 = value2;
data->string_helper = helper;
data->needs_db_save = false;
lua_data.push_back(data);
}
@ -1154,6 +1158,7 @@ void Spell::AddSpellEffect(int8 percentage, int8 subbullet, string description){
effect->description = description;
effect->subbullet = subbullet;
effect->percentage = percentage;
effect->needs_db_save = false;
effects.push_back(effect);
}

View File

@ -205,6 +205,7 @@ struct LUAData{
sint32 int_value2;
float float_value2;
string string_helper;
bool needs_db_save;
};
struct SpellScriptTimer {
LuaSpell* spell;
@ -224,6 +225,7 @@ struct SpellDisplayEffect{
int8 percentage;
int8 subbullet;
string description;
bool needs_db_save;
};
enum GivenByType {
@ -352,6 +354,12 @@ public:
int16 GetSavageryRequired(Spawn* spawn);
int16 GetDissonanceRequired(Spawn* spawn);
SpellData* GetSpellData();
SpellDisplayEffect* GetSpellDisplayEffectSafe(int index) const {
if (index < 0 || index >= effects.size())
return nullptr;
return effects[index];
}
bool GetSpellData(lua_State* state, std::string field);
bool SetSpellData(lua_State* state, std::string field, int8 fieldArg);
bool ScribeAllowed(Player* player);

View File

@ -5283,6 +5283,11 @@ bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_titles where char_id = %u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from char_colors where char_id = %u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from statistics where char_id = %u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_spell_effects where charid=%u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_spell_effect_targets where caster_char_id=%u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_dataindex where charid=%u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_display where charid=%u", character_id);
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_data where charid=%u", character_id);
return true;
}
@ -7970,6 +7975,94 @@ int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race,
return query.GetLastInsertedID();
}
void WorldDatabase::LoadCustomSpellData(Client* client, LuaSpell* luaspell) {
if (!luaspell || !luaspell->spell || !luaspell->spell->GetSpellData() || !luaspell->spell->IsCopiedSpell())
return;
auto spell_data = luaspell->spell->GetSpellData();
DatabaseResult result;
if (!database_new.Select(&result, "SELECT field, type, value FROM character_custom_spell_data WHERE charid = %u AND spell_id = %u",
client->GetPlayer()->GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id))
return;
while (result.Next()) {
std::string field = result.GetStringStr("field");
std::string type = result.GetStringStr("type");
std::string value = result.GetStringStr("value");
if (type == "int")
luaspell->SetSpellDataGeneric(field, atoi(value.c_str()));
else if (type == "float")
luaspell->SetSpellDataGeneric(field, static_cast<float>(atof(value.c_str())));
else if (type == "bool")
luaspell->SetSpellDataGeneric(field, value == "1");
else if (type == "string")
luaspell->SetSpellDataGeneric(field, value);
luaspell->MarkFieldModified(field);
}
}
void WorldDatabase::LoadCustomSpellDataIndex(Client* client, LuaSpell* luaspell) {
if (!luaspell || !luaspell->spell || !luaspell->spell->GetSpellData() || !luaspell->spell->IsCopiedSpell())
return;
DatabaseResult result;
if (!database_new.Select(&result, "SELECT idx, type, value1, value2 FROM character_custom_spell_dataindex WHERE charid = %u AND spell_id = %u",
client->GetPlayer()->GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id))
return;
while (result.Next()) {
int idx = result.GetInt32Str("idx");
std::string type = result.GetStringStr("type");
std::string v1 = result.GetStringStr("value1");
std::string v2 = result.GetStringStr("value2");
if (type == "int")
luaspell->SetSpellDataIndex(idx, atoi(v1.c_str()), atoi(v2.c_str()));
else if (type == "float")
luaspell->SetSpellDataIndex(idx, static_cast<float>(atof(v1.c_str())), static_cast<float>(atof(v2.c_str())));
else if (type == "bool")
luaspell->SetSpellDataIndex(idx, v1 == "1");
else if (type == "string")
luaspell->SetSpellDataIndex(idx, v1, v2);
}
}
void WorldDatabase::LoadCustomSpellDisplayEffects(Client* client, LuaSpell* luaspell) {
if (!luaspell || !luaspell->spell || !luaspell->spell->GetSpellData() || !luaspell->spell->IsCopiedSpell())
return;
DatabaseResult result;
if (!database_new.Select(&result, "SELECT idx, field, value FROM character_custom_spell_display WHERE charid = %u AND spell_id = %u",
client->GetPlayer()->GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id))
return;
while (result.Next()) {
int idx = result.GetInt32Str("idx");
std::string field = result.GetStringStr("field");
std::string value = result.GetStringStr("value");
SpellDisplayEffect* effect = luaspell->spell->GetSpellDisplayEffectSafe(idx);
if (!effect)
continue;
if (field == "description") {
effect->description = value;
effect->needs_db_save = true;
}
else if (field == "bullet") {
effect->subbullet = atoi(value.c_str());
effect->needs_db_save = true;
}
else if (field == "percentage") {
effect->percentage = atoi(value.c_str());
effect->needs_db_save = true;
}
}
}
void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int8 db_spell_type)
{
SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess();
@ -8049,7 +8142,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
bool isExistingLuaSpell = false;
MaintainedEffects* effect = nullptr;
Client* tmpCaster = nullptr;
if(caster_char_id == player->GetCharacterID() && (target_char_id == 0xFFFFFFFF || target_char_id == player->GetCharacterID()) && (effect = player->GetMaintainedSpell(spell_id)) != nullptr)
if(caster_char_id == player->GetCharacterID() && (target_char_id == 0xFFFFFFFF || target_char_id == player->GetCharacterID()) && (effect = player->GetMaintainedSpell(spell_id, true)) != nullptr)
{
safe_delete(lua_spell);
lua_spell = effect->spell;
@ -8059,9 +8152,9 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
isExistingLuaSpell = true;
}
else if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr
&& tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id)) != nullptr)
&& tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id, true)) != nullptr)
{
if(effect->spell && effect->spell_id == spell_id)
if(effect->spell && (effect->spell_id == spell_id || effect->inherited_spell_id == spell_id))
{
safe_delete(lua_spell);
if(tmpCaster->GetCurrentZone() == player->GetZone())
@ -8155,6 +8248,10 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
//lua_spell->num_calls ??
//if(target_char_id == player->GetCharacterID())
// lua_spell->targets.push_back(player->GetID());
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCustomSpell: %s (%u) lua_spell caster %s (%u), caster char id: %u. IsCopiedSpell: %u", lua_spell->spell->GetName(), lua_spell->spell->GetSpellID(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, lua_spell->spell->IsCopiedSpell());
LoadCustomSpellData(client, lua_spell);
LoadCustomSpellDataIndex(client, lua_spell);
LoadCustomSpellDisplayEffects(client, lua_spell);
if(db_spell_type == DB_TYPE_SPELLEFFECTS)
{
@ -8212,7 +8309,8 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
info->spell_effects[effect_slot].icon = icon;
info->spell_effects[effect_slot].icon_backdrop = icon_backdrop;
info->spell_effects[effect_slot].spell_id = spell_id;
info->spell_effects[effect_slot].spell_id = lua_spell->spell->GetSpellID();
info->spell_effects[effect_slot].inherited_spell_id = lua_spell->spell->GetSpellData()->inherited_spell_id;
info->spell_effects[effect_slot].tier = tier;
info->spell_effects[effect_slot].total_time = total_time;
info->spell_effects[effect_slot].spell = lua_spell;
@ -8315,7 +8413,8 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
info->maintained_effects[effect_slot].icon = icon;
info->maintained_effects[effect_slot].icon_backdrop = icon_backdrop;
info->maintained_effects[effect_slot].spell_id = spell_id;
info->maintained_effects[effect_slot].spell_id = lua_spell->spell->GetSpellID();
info->maintained_effects[effect_slot].inherited_spell_id = lua_spell->spell->GetSpellData()->inherited_spell_id;
info->maintained_effects[effect_slot].tier = tier;
info->maintained_effects[effect_slot].total_time = total_time;
info->maintained_effects[effect_slot].spell = lua_spell;
@ -8387,7 +8486,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
Client* tmpCaster = nullptr;
MaintainedEffects* effect = nullptr;
if (caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr && (cross_zone_target_buff ||
tmpCaster->GetCurrentZone() == player->GetZone()) && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id)) != nullptr)
tmpCaster->GetCurrentZone() == player->GetZone()) && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id, true)) != nullptr)
{
if(prev_target_type > 0)
{
@ -8399,7 +8498,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
restoreSpells.insert(make_pair(effect->spell, player->GetCharmedPet()));
}
}
else if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer()))
else if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer(), true))
{
if(effect->spell->initial_target_char_id == player->GetCharacterID())
effect->spell->initial_target = player->GetID();

View File

@ -656,6 +656,9 @@ public:
float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid);
bool DeleteSpiritShard(int32 id);
void LoadCustomSpellData(Client* client, LuaSpell* luaspell);
void LoadCustomSpellDataIndex(Client* client, LuaSpell* luaspell);
void LoadCustomSpellDisplayEffects(Client* client, LuaSpell* luaspell);
void LoadCharacterSpellEffects(int32 char_id, Client *client, int8 db_spell_type);
int32 GetMysqlExpCurve(int level);